Writing STRINGS to serial port in C++ linux - c++

I know this question is scattered all over the internet, but still, nothing is getting me completely there yet. I want to write data to a serial port in C++ (linux) for a a Propeller board. Program works fine when taking input from the console, but when I write strings to it always return: ERROR - Invalid command from the device. I tried creating array of char with Hex values then it worked. here's a working code, below. But how will i be able to just provide a string variable of command and send it to the serial port? perhaps, how do you I convert it to hex values if it's the only way? Thanks everyone
note: the loop is to use user input from console. What i need is a way to send a string variable to the serial port.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
int main(int argc,char** argv){
struct termios tio;
struct termios stdio;
int tty_fd;
fd_set rdset;
unsigned char c='D';
printf("Please start with %s /dev/ttyS1 (for example)\n",argv[0]);
memset(&stdio,0,sizeof(stdio));
stdio.c_iflag=0;
stdio.c_oflag=0;
stdio.c_cflag=0;
stdio.c_lflag=0;
stdio.c_cc[VMIN]=1;
stdio.c_cc[VTIME]=0;
tcsetattr(STDOUT_FILENO,TCSANOW,&stdio);
tcsetattr(STDOUT_FILENO,TCSAFLUSH,&stdio);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // make the reads non-blocking
memset(&tio,0,sizeof(tio));
tio.c_iflag=0;
tio.c_oflag=0;
tio.c_cflag=CS8|CREAD|CLOCAL; // 8n1, see termios.h for more information
tio.c_lflag=0;
tio.c_cc[VMIN]=1;
tio.c_cc[VTIME]=5;
tty_fd=open(argv[1], O_RDWR | O_NONBLOCK);
cfsetospeed(&tio,B115200); // 115200 baud
cfsetispeed(&tio,B115200); // 115200 baud
tcsetattr(tty_fd,TCSANOW,&tio);
//char str[] = {'V','E','R','\r'};
//the above str[] doesn't work although it's exactly the same as the following
char str[] = {0x56, 0x45, 0x52, 0x0D};
write(tty_fd,str,strlen(str));
if (read(tty_fd,&c,1)>0)
write(STDOUT_FILENO,&c,1);
while (c!='q')
{
if (read(tty_fd,&c,1)>0) write(STDOUT_FILENO,&c,1); // if new data is available on the serial port, print it out
if (read(STDIN_FILENO,&c,1)>0)
if(c!='q')
write(tty_fd,&c,1); // if new data is available on the console, send it to the serial port
}
close(tty_fd);
}

I'm happy to solve my own solution but yet disappointed to not have seen the trivial matter much sooner. char by default are signed in c++, which makes it holding the range -128 to 127. However, we are expecting the ASCII values which are 0 to 255. Hence it's as simple as declaring it to be unsigned char str[] and everything else should work. Silly me, Silly me.
Still, Thank you everyone for helping me!!!

Are you sure you should end with '\r'? When entering text from console the return key will result in a '\n' character (on Linux) and not '\r'
Also error checking is missing on most functions (open(), fcntl(), etc.). Maybe one of these functions fail. To find out how to check for errors read the man page (for example man 2 open for the open() command. In case of open() the man page explains it returns -1 when it could not open the file/port.
After your edit you wrote:
char str[] = {0x56, 0x45, 0x52, 0x0D};
write(tty_fd,str,strlen(str));
which is wrong. strlen expects a '\0' terminated string which str is obviously not so now it sends your data and whatever there is in memory until it sees a '\0'. You need to add 0x00 to your str array.

Related

I want to receive multiple data from arduino to raspberry pi using I2C

Thank you to whoever is kind enough to look into this question.
I want to receive multiple data from arduino to raspberry pi using I2C.
I can obtain 1 data from arduino, but once I move to more than one data, it fails to do so.
I have tried multiple methods so far, and I found this method to work the best to obtain data from Arduino.
My previous attempt in obtaining data from arduino is as follows:
I want to read from Arduino using I2C using Raspberry Pi
Raspberry Pi's terminal response has weird font that cannot be recognized
Which are all solved by now.
Got Massive Help from link below
https://area-51.blog/2014/02/15/connecting-an-arduino-to-a-raspberry-pi-using-i2c/
Arduino Code
#include <Wire.h>
#define echoPin 7
#define trigPin 8
int number=0;
long duration;
long distance;
void setup()
{
//Join I2C bus as slave with address 8
Wire.begin(8);
//Call SendData & Receive Data
Wire.onRequest(SendData);
//Setup pins as output and input to operate ultrasonic sensor
Serial.begin(9600);
pinMode(echoPin,INPUT);
pinMode(trigPin,OUTPUT);
}
void loop ()
{
digitalWrite(trigPin,LOW);
delayMicroseconds(2);
digitalWrite(trigPin,HIGH);
delayMicroseconds(2);
digitalWrite(trigPin,LOW);
duration=pulseIn(echoPin,HIGH);
distance=duration/58.2;
Serial.print(distance);
Serial.println(" cm");
}
void SendData()
{
Wire.write(distance);
Wire.write("Why No Work?");
Wire.write(distance);
}
C++ Code
//Declare and Include the necessary header files
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
//Define Address of Slave Address
#define ADDRESS 0x08
//Eliminate the Used of std in the future
using namespace std;
static const char *devName="/dev/i2c-1";
int main(int argc, char **argv)
{
//Check to see if C++ works
cout<<"Hello, World!\n";
cout<<"I2C: Connecting"<<endl;
int file;
if ((file = open(devName, O_RDWR))<0)
{
fprintf(stderr, "I2C: Failed to access");
exit(1);
}
if (ioctl(file, I2C_SLAVE, ADDRESS)<0)
{
cout<<"Failed to Access"<<endl;
}
char buf[0];
char dd;
for (int i=0; i<100;i++)
{
read(file,buf, 3);
float distance= (int) buf[0];
dd= buf[1];
float dist=(int) buf[2];
cout<<distance<<endl;
usleep(10000);
cout<<"doh"<<endl;
cout<<dd<<endl;
cout<<dist<<endl;
}
return 0;
}
What I would expect from the c++ code would be as follows
15
doh
Why No Work?
15
But I get
15
doh
weird font can't be recognized
255
Wire.write(distance);
wants to write a long onto the I2C bus. For an Arduino This is 32 bits, 4 bytes, of data. I'm not sure exactly what wire.write does because the documentation I can find is substandard to the point of being garbage, but the documentation looks like it's going to send exactly 1 of the 4 bytes you wanted to send. In order to send more than one byte, it looks like you need to use the array version: Wire.write(&distance, sizeof (distance));, but even this may not be sufficient. I'll get back into that later.
Wire.write("Why No Work?");
writes a null-terminated string (specifically a const char[13]) onto the I2C bus. I don't know arduino well enough to know if this also sends the terminating null.
so
Wire.write(distance);
Wire.write("Why No Work?");
Wire.write(distance);
needed to write at least 4 + 12 + 4 bytes onto the I2C bus. and probably only wrote 1 + 12 + 1.
On the Pi side,
read(file,buf, 3);
read out 3 bytes. This isn't enough to get the whole of distance, let alone the array of characters and second write of distance. You need to read all of the data you wrote, at least 20 bytes.
In addition,
char buf[0];
defines an array of 0 length. There isn't much you can do with it as there is no space to store anything here. It cannot hold 3 characters, let alone the 20 or 21 necessary. read of 3 bytes wrote into invalid memory and the program can no longer be counted on for sane results.
This means that at best
float distance= (int) buf[0];
dd= buf[1];
float dist=(int) buf[2];
got only one byte of the four bytes of distance and it's dumb luck that the result was the same as expected. dd got exactly one character, not the whole string, and this is turning out to be nonsense because of one of the preceding mistakes. dist is similarly garbage.
To successfully move data from one machine to another, you need to establish a communication protocol. You can't just write a long onto a wire. long doesn't have the same size on all platforms, nor does it always have the same encoding. You have to make absolutely certain that both sides agree on how the long is to be written (size and byte order) and read.
Exactly how you are going to do this is up to you, but here are some pointers and a search term, serialization, to assist you in further research.

Dealing with null characters in stdin in C++

I am writing an extension for Chrome that uses native host messaging. The goal is to have Chrome open links in the OS default browser when running in app mode. Chrome implements the native host messaging over pipes to stdin and stdout of the native application. This is all well and good and I've got the extension talking to the native application. The problem I'm having is that the first 4 bytes of data contain the length of the following string, which for my purposes will always contain null characters. An example strace is shown below. What is the best way to deal with this? I'd like to use something like cin or getline, that will stall the program until input is received if possible.
Process 27964 attached
read(0, "~\0\0\0\"http://stackoverflow.com/qu"..., 4096) = 130
read(0,
This is the current C++ code. I've tried variations using cin.get and fgets, but they don't wait on input and Chrome kills the program after the loop runs amok.
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
for(;;) {
string message;
cin >> message;
if(!message.length()) break;
string cmd(string("xdg-open ") + message);
system(cmd.c_str());
}
return 0;
}
As far as I understand here, the length should be in native byte order, so the same endianness that your compiler uses for the same CPU architecture:
each message is serialized using JSON, UTF-8 encoded and is preceded
with 32-bit message length in native byte order.
This means that you could read first the length:
uint32_t len;
while (cin.read(reinterpret_cast<char*>(&len), sizeof (len))) // process the messages
{
// you know the number of bytes in the message: just read them
string msg (len, ' '); // string filled with blanks
if (!cin.read(&msg[0], len) )
/* process unexpected error of missing bytes */;
else /* process the message normally */
}

Serial communication - I'm having trouble turning an incoming char array into an int

I'm trying to receive a number from an Arduino as an integer in C++. The full code is below:
#define STRICT
#include <tchar.h>
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "Serial.h"
#include <boost\lexical_cast.hpp>
enum { EOF_Char = 27 };
int __cdecl _tmain(int /*argc*/, char** /*argv*/)
{
CSerial serial;
LONG lLastError = ERROR_SUCCESS;
// Attempt to open the serial port (COM4)
lLastError = serial.Open(_T("COM4"), 0, 0, false);
// Setup the serial port (9600,8N1, which is the default setting)
lLastError = serial.Setup(CSerial::EBaud9600, CSerial::EData8, CSerial::EParNone, CSerial::EStop1);
// Register only for the receive event
lLastError = serial.SetMask(CSerial::EEventBreak |
CSerial::EEventCTS |
CSerial::EEventDSR |
CSerial::EEventError |
CSerial::EEventRing |
CSerial::EEventRLSD |
CSerial::EEventRecv);
// Use 'non-blocking' reads, because we don't know how many bytes
// will be received. This is normally the most convenient mode
// (and also the default mode for reading data).
lLastError = serial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking);
// Keep reading data, until an EOF (CTRL-Z) has been received
bool fContinue = true;
do
{
// Wait for an event
lLastError = serial.WaitEvent();
// Save event
const CSerial::EEvent eEvent = serial.GetEventType();
// Handle data receive event
if (eEvent & CSerial::EEventRecv)
{
// Read data, until there is nothing left
DWORD dwBytesRead = 0;
char szBuffer[101];
do
{
// Read data from the COM-port
lLastError = serial.Read(szBuffer, sizeof(szBuffer) - 1, &dwBytesRead);
if (dwBytesRead > 0)
{
// Finalize the data, so it is a valid string
szBuffer[dwBytesRead] = '\0';
// Display the data
printf("%s", szBuffer);
// Check if EOF (CTRL+'[') has been specified
if (strchr(szBuffer, EOF_Char))
fContinue = false;
}
} while (dwBytesRead == sizeof(szBuffer) - 1);
}
} while (fContinue);
// Close the port again
serial.Close();
return 0;
}
I have my Arduino constantly sending out the number 51. This code works fine and consistently displays "51". However, I want an int to manipulate in C++.
First I added
std::stringstream str(szBuffer);
int tester;
str >> tester;
printf("My number is: %d\n", tester+1);
right after
printf("%s", szBuffer);
A typical result looks like:
51My number is: 52
51My number is: 52
51My number is: 52
51My number is: 52
51My number is: 52
5My number is: 6
1My number is: 2
After doing it perfectly 5 or 6 times, the output always separates the incoming digits once or twice in a row (I haven't been able to find a specific pattern yet, but it's always 5-6 and 1-2).
My other attempt was to use the boost library:
int tester = boost::lexical_cast<int>(szBuffer);
printf("My number is: %d\n", tester);
right after
printf("%s", szBuffer);
and I get the same result (1-2 errors after 5-6 correct ones). I don't think the Arduino is sending bad data, since just a
printf("%s", szBuffer);
will never deviate from the number it's supposed to be. Could the conversion be messing up the receiving of data? Thanks.
EDIT: The Arduino code is:
void setup() {
Serial.begin(9600); // same as in your c++ script
}
void loop() {
Serial.print(51);
delay(1000);
}
With serial ports, there is no mechanism where a transmitter can inform a receiver how many bytes were transmitted as a block. I.e. there's no "hidden" marker where Serial.print(51); tells the receiver that it sent two characters as one number. You have to add some kind of indication (spaces, commas, line ends, initial byte counts, whatever) to your serial protocol.
Because of this, the number of characters you get from serial.Read depends on the number of characters you asked it to read (the second parameter) and how many characters are in the serial port's receive buffer, whichever is smaller. Most of the time, it seems the Arduino sends both digits before you call serial.Read, but sometimes it only gets one out in time... and the second is read the next time through the loop.
So let's assume you decided to use line ends to separate your numbers. All you have to do on the Arduino end is change to Serial.println(51);. The receive end is a little more complex.
I don't know what your serial library has in it. Most have some kind of "read line" function, and you would just replace the serial.Read call with something like:
serial.Readline(szBuffer, sizeof(szBuffer) - 1);
and it will take care of null-terminating the output. If it doesn't take care of null-termination, you'll need to find the line end and change it to a \0 yourself. From this point on, your code will work fine, because the serial.Readline function will block until it gets the whole line.
If you don't have a "read line" or at least a "read until this character" function, it's a bit harder. You have to repeatedly call serial.Read, moving through your buffer, until you see the line end character. Further, you run the risk of reading part or all of the next line, so you can't just discard all the data you read when you're done reading the number; you have to move teh data in the buffer so the next line's data (and further) is at the start of the buffer.
If you're using Boost (are you? it has no CSerial that I see), it looks like it has a read_until function. This takes three parameters: the stream you're reading from, a stream buffer to store the data in, and something to stop on. In this case, the stream buffer for storage is the one in your std::stringstream:
std::stringstream buffer;
size_t chars = boost::asio::read_until(serial, buffer.rdbuf(), '\n');
if(chars == 0) return;
int tester;
buffer >> tester;
printf("My number is: %d\n", tester+1);

How to fix serial-port transmission error?

I am trying to send some chars via serial port. The problem is that if I send A (ASCII 65) I receive something else (ASCII 225). Any letter or string that I send I receive something else.
Here is my code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
int open_port(void)
{
int port;
port = open("/dev/ttySAC3", O_RDWR | O_NOCTTY | O_NDELAY);
if (port == -1){
perror("open_port: Unable to open /dev/ttySAC3 - ");
}else{
fcntl(port, F_SETFL, 0);
}
return (port);
}
int main()
{
int port,n;
char str = 'A';
struct termios specs;
port = open_port();
tcgetattr(port, &specs);
specs.c_cflag = (CLOCAL | CREAD );
specs.c_oflag = (OPOST | CR3| CS8);
cfsetospeed(&specs,B9600);
tcsetattr(port,TCSANOW,&specs);
n = write(port, &str, 1);
if (n<0) {
printf("\nError");
}
close(port);
return(0);
}
I measured using an oscilloscope and that is the data leaving the device, so it is not a reading issue on my PC.
I searched the web for the last 2 days and can't figure out what I am doing wrong. Some help would be appreciated.
In case you get 226 for a "'B'" then I'd say the uP sends a 'b' and uses 7 data bits and uses parity.
From N Alex's comment below I conclude:
My above assumption was wrong.
He could have simply added a proper initialisation to the termios structure:
struct termios specs = {0};
Baud rate is one parameter, there are also number of data bits, stop bits, parity and flow control (RTS/CTS, none, etc). These parameters must be identical on both sides, provided the hardware on both sides support them. If this is the case, then the cable Rx, Tx must connect Rx(side A) to Tx(side B) and Tx(side A) to Rx(side B).
This is the minimum requirement if we ignore ground and grid for interference, and flow control pins.
The next consideration is distance. RS232 cannot go very far on standard cables due to interference from nearby static emitters (cellphones, motors, etc.)
The final, and imho hardest is that RS232 has no error correction. If some bits are changed during transmission, then the other side can only check if the packet is correct up to a very low degree, but it cannot correct it. For this, you'll need on both sides an error correction protocol, or at the very least a ACK/NAK mechanism in place.
Hope this helps.

Invalid conversion from char to const char*

Sorry in advance for what I feel is going to be a really simple problem, but I've been stuck on it for hours and I haven't been able to work out how to fix it based on the stuff I've found here or on google.
I've got an arduino hooked up to a GPS and a radio and am trying to broadcast the GPS signal to my radio. What I am trying to do now is get the NMEA sentence from the GPS into the variable 'text', but am getting confused by this error, which I think is due to arrays.
My error is occurring on this line:
sprintf(text, char(c));
I've tried a few different things but this is where I'm stuck at the moment. Any help would be really appreciated.
#define RADIOPIN 13
#include <string.h>
#include <util/crc16.h>
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 2);
#define GPSECHO true
Adafruit_GPS GPS(&mySerial);
char datastring[80];
char text[80];
void setup() {
Serial.begin(115200);
GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_5HZ);
delay(3000);
pinMode(RADIOPIN,OUTPUT);
}
void loop(){
gpscheck();
}
void gpscheck(){
char c = GPS.read();
if (c) {
// Serial.print(c);
sprintf(text, char*(c));
Serial.print(text);
}
}
You might want to read a reference of sprintf. Then you will see that the second argument is a string.
So the following would be fine:
sprintf(text, "%c", c);
To be on the safe side, you might want to use snprintf instead, to lessesn the risk of buffer overflows.
Of course, for a single character, you might as well do e.g.
text[0] = c;
text[1] = '\0'; /* Terminate string */
sprintf(text, char*(c));
If you want to print character
simply use printf("%c",c);
if you want to copy that into text
sprintf(text,"%c", c);