I'm communicating with a Automatic Land Navigation System via serial wherein I'll be issuing commands to it and will get in return the required response from it. Since the messages are terminated by \r\n just like in GPS (NMEA standard) I'm opening the serial port in Line Mode. The problem I'm facing is if I try to send data at a faster rate the program is getting hanged. For time being I'm flushing both the input and output buffers as a workaround of this issue. Does anybody have a better solution. This is how I'm opening my serial port:
#include <iostream>
#include <chrono>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include <sioLib.h>
#include <tyLib.h>
int fd = -1;
void recvthread() {
fd = open("/tyCo/2", O_RDWR | O_NOCTTY, 0);
if (fd != -1) {
int nRes = ioctl(fd, FIOBAUDRATE, 9600);
if (nRes == -1)
perror("Error in setting Baudrate for /tyCo/2");
int options = ioctl(fd, FIOGETOPTIONS, 0);
if (options == -1)
options = 0;
options |= CREAD | CLOCAL;
options &= ~CSIZE;
options |= CS8;
options &= ~STOPB;
options &= ~PARENB;
options |= OPT_LINE;
nRes = ioctl(fd, FIOSETOPTIONS, options);
if (nRes == -1)
perror("Error in setting options for /tyCo/2");
char buff[512];
while (true) {
// receive data
int len = read(fd, buff, 10);
// process the received data
}
}
}
int main() {
std::thread t(recvthread);
sleep(5);
char buff[512];
while (true) {
int len = write(fd, buff, 56);
std::this_thread::sleep_for(std::chrono::milliseconds(250LL));
}
}
I use Curtiss Wright 185 board which has xmc 371 serial card as an accessory for which I use tews serial driver.
TL;DR - I am attempting serial communication with Arduino with code that I found here and nothing gets sent over (Arduino programmed to respond, and I checked that it does with its serial monitor)
Hi there,
I was looking for a way to send information over to an Arduino Mega (2560) unit over linux serial port by C++.
I came across the following solution: Solution
I'm using this guy's code for write (I'm able to read data from the arduino) and use the same parameters (they work, as I'm able to receive data from the Ardunio).
I programmed my Arduino to send "Hi" over serial whenever it sees at least 1 bit of information, and checked it worked through the Arduino IDE Serial Monitor.
Yet when running the C++ code, the arduino doesn't respond. Do anyone might have idea why?
Full disclosure - I inserted #Lunatic999's code to a class so I can make an instance of it for my needs of the code.
fd = open(portNameC, O_RDWR | O_NOCTTY | O_SYNC); //open port ("opens file")
Serial Parameters:
struct termios tty;
struct termios tty_old;
memset (&tty, 0, sizeof tty);
/* Error Handling */
if ( tcgetattr ( fd, &tty ) != 0 ) {
std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl;
}
/* Save old tty parameters */
tty_old = tty;
/* Set Baud Rate */
cfsetospeed (&tty, (speed_t)B19200);
cfsetispeed (&tty, (speed_t)B19200);
/* Setting other Port Stuff */
tty.c_cflag &= ~PARENB; // Make 8n1
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS; // no flow control
tty.c_cc[VMIN] = 1; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
/* Make raw */
cfmakeraw(&tty);
/* Flush Port, then applies attributes */
tcflush( fd, TCIFLUSH );
if ( tcsetattr ( fd, TCSANOW, &tty ) != 0) {
std::cout << "Error " << errno << " from tcsetattr" << std::endl;
}
Write (this code I put inside a function which I call)
unsigned char cmd[] = "INIT \r";
int n_written = 0,
spot = 0;
do {
n_written = write( fd, &cmd[spot], 1 );
spot += n_written;
} while (cmd[spot-1] != '\r' && n_written > 0);
Arduino code:
bool dataRecieved = false;
int ledpin = 13;
void setup() {
pinMode(ledpin, OUTPUT);
digitalWrite(ledpin, HIGH);
Serial.begin(19200);
}
void loop() {
while(!dataRecieved)
{
digitalWrite(ledpin,HIGH);
if (Serial.available() > 0)
{
dataRecieved = true;
}
}
digitalWrite(ledpin,LOW);
delay(1000);
digitalWrite(ledpin,HIGH);
delay(1000);
Serial.println("hi");
}
Turns out it was an arduino problem all along. I needed to apply some usleep to let arduino bootload
I'm reading information from a serial port using this code:
struct termios tio;
memset(&tio, 0, sizeof(tio));
// Open serial port in mode `8N1', non-blocking
tio.c_cflag = CS8 | CREAD | CLOCAL;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 10;
int fd = open("/dev/ttyACM1", O_RDONLY);
cfsetospeed(&tio, B9600);
cfsetispeed(&tio, B9600);
tcsetattr(fd, TCSANOW, &tio);
unsigned char byte = '0';
// check for input from arduino
while (!quit)
{
keyboardInput(quit);
read(fd, &byte, 1);
if ((byte == '1' || quit)
{
oldByteDoor = '1';
break;
}
}
where keyboardInput(quit) sets quit to true when the close button of the window is pressed.
If nothing is in the serial port it gets stuck at read(fd, &byte, 1) forever.
How can I prevent this?
Thanks
I have been tasked with the implementation of the ModBus protocol over a RS485 2-wire system. (Actually it's three wires, A/B and GND).
ModBus is not the point though, but the step before that...simple I/O over the interface.
I am using the FTDI USB-RS485 converter to connect a Linux host (not interchangeable) to a Windows host (interchangeable with another Linux host, though I'd like to avoid that)
The encoding is supposed to be 19200, 8, n, 1.
But it just doesn't seem to work.
I don't have the exact code handy, but on Linux I am doing this:
int fd = open("/dev/ttyS3", O_RDWR | O_CTTY);
if(fd == -1) return "Error while opening the port";
Next, I configure the port.
struct termios tty;
tcgetattr(fd, &tty);
cfsetispeed(&tty, B19200);
cfsetospeed(&tty, B19200);
tty.c_cflag = CS8; //Empties the cflags and sets the character width.
tty.c_cflag |= (CLOCAL | CREAD); //Sets 'recommended' options.
tty.c_lflag = 0;
tty.c_iflag = 0;
tty.c_oflag = 0;
tcgetattr(fd, TCSANOW, &tty);
Parity and Flow Control are currently not planned, since the final result will connect to a low level board, where I need to take care of the signals myself. Furthermore there aren't any wires, which would allow 'unfettered communication'. (After all I don't want an XON/XOFF character to limit the byte range I can transmit)
All of these function go through properly and the data is set.
On Windows, I open the serial port like this:
DCB SP;
HANDLE hSerial = CreateFile("COM6", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if(hSerial == INVALID_HANDLE_VALUE) return "Error while opening the port";
GetCommState(hSerial, &SP);
Parity is disabled, as well as flow control. Byte size is set to 8.
Edit:
Since it has been asked, here is my code for the baudrate on Windows (from memory)
SP.DCBlength= sizeof(SP);
SP.BaudRate = 19200;
SP.Parity = NOPARITY;
SP.StopBits = ONESTOPBIT;
SetCommState(hSerial, &SP);
Again, all of these functions run flawlessly.
Now, for the test case that's giving me a major headache.
On the Linux host, I create a byte buffer of 256 bytes size.
This buffer is filled with the character values from 0-255...and then sent over the wire with write.
At the same time, the other side is waiting with 'ReadFile' for data to arrive.
With this configuration, for both the 'other Linux host', as well as for the Windows host, 256 Bytes arrive...however it's NOT the numbers from 0-255, but something 00 06 etc.
I can get the Linuxhost to work, when I'm setting all members of the termios structure to 0 before setting the options I actually want. I'm guessing, it's because of the control characters...however if I do that, the Windows host either receives only 4 of 256 bytes.
As I said, unfortunately I don't have the code handy. If anyone has any idea from what point I could tackle this, I'd be very grateful. I will post more code, once I have access to it again.
How I'm implementing the read operation:
DWORD nBytes = 0;
char Buffer[256], *ptr = Buffer;
int Rem = 256;
while(Rem) {
ReadFile(hSerial, ptr, Rem, &nBytes, 0);
Rem -= nBytes;
ptr += nBytes;
}
//Evaluate Buffer
To be noted, I did set the timeouts, but can't remember the exact values.
Edit: Since I now have access to my work place again, here's the actual (current) code.
const char *InitCOM(const char *TTY) {
struct termios tty;
hSerial = open(TTY, O_RDWR | O_NOCTTY | O_NDELAY);
if(hSerial == -1) return "Opening of the port failed";
fcntl(hSerial, F_SETFL, 0);
if(tcgetattr(hSerial, &tty) != 0) return "Getting the parameters failed.";
if(cfsetispeed(&tty, B19200) != 0 || cfsetospeed(&tty, B19200) != 0) return "Setting the baud rate failed.";
//CFlags
//Note: I am full aware, that there's an '=', and that it makes the '&=' obsolete, but they're in there for the sake of completeness.
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; //8-bit characters
tty.c_cflag |= (CLOCAL | CREAD);und erlaubt 'Lesen'.
tty.c_cflag &= ~(PARENB | PARODD);
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
//Input Flags
tty.c_iflag &= ~IGNBRK;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
//Local Flags
tty.c_lflag = 0;
//Output Flags
tty.c_oflag = 0;
//Control-Characters
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 5;
if(tcsetattr(hSerial, TCSAFLUSH, &tty) != 0) return "Setting the new parameters failed";
return NULL;
}
As for the actual sending/receiving code:
int main(int argc, char* argv[]) {
#if defined FOR_PC
const char *err = InitCOM("/dev/ttyUSB0");
#else
const char *err = InitCOM("/dev/ttyS3");
#endif
if(err) printf("Error while initalizing: %s ErrNum: %d\n", err, errno);
else {
/*unsigned char C[256]; //Original code with the array
int nBytes;
#ifdef FOR_PC
int Rem = 256, ReqCount = 0;
unsigned char *ptr = C;
while(Rem > 0) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(hSerial, &fds);
select(hSerial+1, &fds, NULL, NULL, NULL);
nBytes = read(hSerial, ptr, Rem);
if(nBytes > 0) {
Rem -= nBytes;
ptr += nBytes;
++ReqCount;
}
}
printf("Number of received Bytes: %d in %d sends.\n\n", 256 - Rem, ReqCount);
for(int i = 0; i < 256; ++i) {
printf("%02X ", C[i]);
if((i%32) == 31) printf("\n");
}
#else
for(int i = 0; i < 256; ++i) C[i] = i;
nBytes = write(hSerial, C, 256);
printf("\nWritten Bytes: %d\n", nBytes);
#endif*/
//Single-Byte Code
unsigned char C = 0x55;
#ifdef FOR_PC
while(true) { //Keeps listening
fd_set fds;
FD_ZERO(&fds);
FD_SET(hSerial, &fds);
select(hSerial+1, &fds, NULL, NULL, NULL);
read(hSerial, &C, 1);
printf("Received value 0x%02X\n", C);
}
#else
write(hSerial, &C, 1); //Sends one byte
#endif
close(hSerial);
}
return 0;
}
As for the Oscilloscope: I've tested both directions with sending. Both did their job quite admirably.
The signal of 0x55 is a constant Up/Down at the length of 50 microseconds (as it should, so setting the baud rate is no problem either).
So is there anything in my 'receive' code I'm doing wrong? Is the 'select' wrong?
Are you also setting the proper baud rate on the Windows side?
Use an oscilloscope to check the actual data present on the wire(s). Debugging serial communications is what oscilloscopes were invented for. Almost. :)
Your read function could easily explode. If you are near the end of the buffer, and you read more than the amount to fill it, you will copy past the end of the buffer, overwriting the stack.
On the Linux sending side, you should look into "raw mode", e.g., cfmakeraw(). This way you will not be bothered by the system "helping" you (like adding CR when you send a newline -- really screws up the binary data...). There's a way to do that in microsoft, but I forget how.
on windows side, did you set the DCBLength field in your DCB struct?
dcb.DCBlength = sizeof(dcb);
I'm running Ubuntu 9.10 and I seem to be having trouble with termios.
So I can start minicom open the serial port at 57600 Baud, 8N1, no hardware or software flow control and it works great. I type in #17 5 and my device responds. When I try to set up my serial port in my C++ code I don't get a response. I know that the software is communicating to the port because an led turns on.
Here is my main:
int main(void)
{
int fd; /* File descriptor for the port */
fd = open("/dev/keyspan1", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
/*
* Could not open the port.
*/
perror("open_port: Unable to open /dev/ttyS0 - ");
}
else
fcntl(fd, F_SETFL, 0);
/*****************************CHANGE PORT OPTIONS***************************/
struct termios options;
/*
* Get the current options for the port...
*/
tcgetattr(fd, &options);
/*
* Set the baud rates to 57600...
*/
cfsetispeed(&options, B57600);
cfsetospeed(&options, B57600);
/*
* Enable the receiver and set local mode...
*/
options.c_cflag |= (CLOCAL | CREAD);
/*
* Set the new options for the port...
*/
tcsetattr(fd, TCSANOW, &options);
/***********************************END PORT OPTIONS***********************/
int n;
n = write(fd, "#17 5 \r", 7);
if (n < 0)
fputs("write() of 8 bytes failed!\n", stderr);
char buff[20];
sleep(1);
n = read(fd, buff, 10);
printf("Returned = %d\n", n);
close(fd);
return(0);
}
Any suggestions would be appreciated. Thanks.
You probably need to initialize your terminal to raw mode. I suggest you use cfmakeraw() to initialize the term options structure. Among others, cfmakeraw will make sure that flow control is disabled, parity checks are disabled, and input is available character by character.
cfmakeraw is non-Posix. If you're concerned with portability, look up the cfmakeraw manpage for the settings it is making.