I am receiving sensor datas through the serial port using Xbee module. The sensors are connected to Arduino and they are sending datas to the zigbee connected to my laptop. here, is the code i am using to show the received datas.
#include <fcntl.h>
#include <stdio.h>
#include <termios.h>
#include <stdlib.h>
#include <string.h>
/* Change to the baud rate of the port B2400, B9600, B19200 etc as per Arduino board is sending */
#define SPEED B9600
/* Change to the serial port you want to use /dev/ttyUSB0, /dev/ttyS0, etc. */
#define PORT "/dev/ttyS1"
/* Sensor raw datas */
int accLowX, accLowY, accLowZ;
int accHighX, accHighY, accHighZ;
int main( ){
int fd = open( PORT, O_RDONLY | O_NOCTTY );
if (fd <0) {perror(PORT); exit(-1); }
struct termios options;
bzero(&options, sizeof(options));
options.c_cflag = SPEED | CS8 | CLOCAL | CREAD | IGNPAR;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &options);
int r;
char buf[255];
char sensorDatas[255];
while( 1 ){
r = read( fd, buf, 255 );
buf[r]=0;
memcpy(sensorDatas, buf, strlen(buf)+1);
printf( "%s", sensorDatas);
// separate the strings for sensor datas
//
}
}
My Output of received datas are:
56-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:341 AHZ:421
57-ALX:350 ALY:349 ALZ:351 AHX:354 AHY:341 AHZ:422
58-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
59-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
60-ALX:350 ALY:349 ALZ:351 AHX:352 AHY:342 AHZ:422
61-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:421
62-ALX:350 ALY:349 ALZ:351 AHX:354 AHY:342 AHZ:422
63-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
64-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
65-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:421
66-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:421
67-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
68-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
69-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:341 AHZ:421
70-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
71-ALX:350 ALY:349 ALZ:351 AHX:352 AHY:342 AHZ:422
72-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
73-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
74-ALX:350 ALY:349 ALZ:351 AHX:352 AHY:342 AHZ:422
75-ALX:350 ALY:349 ALZ:351 AHX:352 AHY:342 AHZ:422
76-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
77-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
78-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:421
79-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
80-ALX:350 ALY:350 ALZ:351 AHX:353 AHY:343 AHZ:422
81-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
82-ALX:350 ALY:349 ALZ:351 AHX:353 AHY:342 AHZ:422
Here i cannot use strlen function as it gives me the output 0 as r is set zo zero. without knowing the string length, i cannot proceed to segment it to get the following datas.
Consider for received string 56, I want to store the following value in each iteration in the following the variables
accLowX = 350, accLowY = 349, accLowZ=351
accHighX =353, accHighY = 341, accHighZ =421;
How can i do it?
I would start by creating a string array and put each line in it (with getline maybe?) Then, you can use split function of boost and use a space as a delimiter. Then, put all of these in a temporary array. http://www.cplusplus.com/faq/sequences/strings/split/ -> Boost String Algorithms: Split
After, just use the find (http://www.cplusplus.com/reference/string/string/find/) to identify each parameter and use a switch case for each one. Or, take the array and use it directly, since all position will be the same (ex: [0] = ALX:(Number), [1] = ALY:(Number), etc). Hope this help.
You can use sscanf , something like this -
if(sscanf(s,"%*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d"
,&accLowX,&accLowY,&accLowZ,&accHighX,&accHighY,&accHighZ))==6){
// do something
}
After your valuable suggestions, I found this way to solve my problem. This code is inspired by this CodeSource
Modified Code:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BAUDRATE B9600 /* baudrate settings are defined in <asm/termbits.h>, which is included by <termios.h> */
#define MODEMDEVICE "/dev/ttyS1" /* change this definition for the correct port */
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
int main()
{
int fd,c, res;
struct termios oldtio,newtio;
char buf[255];
char sensorDatas[255];
/* Sensor raw datas */
int accLowX, accLowY, accLowZ;
int accHighX, accHighY, accHighZ;
/*
Open modem device for reading and writing and not as controlling tty
because we don't want to get killed if linenoise sends CTRL-C.
*/
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if(fd <0) {
perror(MODEMDEVICE);
exit(-1);
}
tcgetattr(fd,&oldtio); /* save current serial port settings */
//bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */
/*
BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
CRTSCTS : output hardware flow control (only used if the cable has
all necessary lines. See sect. 7 of Serial-HOWTO)
CS8 : 8n1 (8bit,no parity,1 stopbit)
CLOCAL : local connection, no modem contol
CREAD : enable receiving characters
*/
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
/*
IGNPAR : ignore bytes with parity errors
ICRNL : map CR to NL (otherwise a CR input on the other computer
will not terminate input)
otherwise make device raw (no other input processing)
*/
newtio.c_iflag = IGNPAR | ICRNL;
/*
Raw output.
*/
newtio.c_oflag = 0;
/*
ICANON : enable canonical input
disable all echo functionality, and don't send signals to calling program
*/
newtio.c_lflag = ICANON;
/*
initialize all control characters
default values can be found in /usr/include/termios.h, and are given
in the comments, but we don't need them here
*/
newtio.c_cc[VINTR] = 0; /* Ctrl-c */
newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */
newtio.c_cc[VERASE] = 0; /* del */
newtio.c_cc[VKILL] = 0; /* # */
newtio.c_cc[VEOF] = 4; /* Ctrl-d */
newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
newtio.c_cc[VMIN] = 1; /* blocking read until 1 character arrives */
newtio.c_cc[VSWTC] = 0; /* '\0' */
newtio.c_cc[VSTART] = 0; /* Ctrl-q */
newtio.c_cc[VSTOP] = 0; /* Ctrl-s */
newtio.c_cc[VSUSP] = 0; /* Ctrl-z */
newtio.c_cc[VEOL] = 0; /* '\0' */
newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */
newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */
newtio.c_cc[VWERASE] = 0; /* Ctrl-w */
newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */
newtio.c_cc[VEOL2] = 0; /* '\0' */
/*
now clean the modem line and activate the settings for the port
*/
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
/*
terminal settings done, now handle input
In this example, inputting a 'z' at the beginning of a line will
exit the program.
*/
while (STOP==FALSE) { /* loop until we have a terminating condition */
/* read blocks program execution until a line terminating character is
input, even if more than 255 chars are input. If the number
of characters read is smaller than the number of chars available,
subsequent reads will return the remaining chars. res will be set
to the actual number of characters actually read */
res = read(fd,buf,255);
memcpy(sensorDatas, buf, strlen(buf)+1);
buf[res]=0; /* set end of string, so we can printf */
sensorDatas[res]=0;
printf("%s", sensorDatas);
/* Segmentation of Datas received from Arduino */
if(sscanf(sensorDatas,"%*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d %*[^:]:%d"
,&accLowX,&accLowY,&accLowZ,&accHighX,&accHighY,&accHighZ)==6) {
// do something
printf( "accLowX : %d\n", accLowX);
printf( "accLowY : %d\n", accLowY);
printf( "accLowZ : %d\n", accLowZ);
}
}
/* restore the old port settings */
tcsetattr(fd,TCSANOW,&oldtio);
}
Related
I am trying to get data from a gyro using I2C interface and I can't figure out how can I set a event handler to check for IO instead of reading the FIFO buffer in an infinite loop.
I had done a similar thing using UART.
struct sigaction saio;
struct termios options;
uartFilestream = open(UART_DEV, O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
saio.sa_handler = HandleIO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
fcntl(uartFilestream, F_SETFL, FNDELAY);
fcntl(uartFilestream, F_SETOWN, getpid());
fcntl(uartFilestream, F_SETFL, O_ASYNC );
tcgetattr(uartFilestream, &options);
options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; //<Set baud rate
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(uartFilestream, TCIFLUSH);
tcsetattr(uartFilestream, TCSANOW, &options);
HandleIO is called whenever IO happens on the the serial interface. Please tell me how can I do the same with I2C.
Here is what I did with I2C but it doesn't work.
fd = open("/dev/i2c-1", O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
saio.sa_handler = test_func;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
fcntl(fd, F_SETFL, FNDELAY);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, O_ASYNC );
tcgetattr(fd, &options);
options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; //<Set baud rate
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &options);
I'm trying to write bytes to an serialport, where I connect a relay.
Everything works fine, if I use C++ for this: (comments are german, sorry)
#include <fcntl.h>
#include <termios.h>
RelayProtocol::RelayProtocol()
{
// Create structure
struct termios options;
// Open Connection
_handle = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
// clear all flags
fcntl(_handle, F_SETFL, 0);
// Get current options
tcgetattr(_handle, &options);
// Set Boundrate (19200)
cfsetispeed(&options, B19200); // Input
cfsetospeed(&options, B19200); // Output
// Set serial-flags
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag |= (CLOCAL | CREAD);
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_oflag &= ~OPOST;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
// Flush settings
tcflush(_handle,TCIOFLUSH);
tcsetattr(_handle, TCSAFLUSH, &options);
fcntl(_handle, F_SETFL, FNDELAY);
fcntl(_handle, F_SETFL, 0);
// Initialize relay
SendCommand(1, 1, 0);
}
Now I tried the same using vala:
public static void main (string[] args) {
int handle = Posix.open ("/dev/ttyUSB0", Posix.O_RDWR | Posix.O_NOCTTY | O_NDELAY);
Posix.termios termios;
Posix.fcntl (handle, Posix.F_SETFL, 0);
Posix.tcgetattr (handle, out termios);
termios.c_ispeed = 19200;
termios.c_ospeed = 19200;
/* Configure serialport */
termios.c_cflag &= ~Posix.PARENB;
termios.c_cflag &= ~Posix.CSTOPB;
termios.c_cflag &= ~Posix.CSIZE;
termios.c_cflag |= Posix.CS8;
termios.c_cflag |= (Posix.CLOCAL | Posix.CREAD);
termios.c_lflag &= ~(Posix.ICANON | Posix.ECHO | Posix.ECHOE | Posix.ISIG);
termios.c_oflag &= ~Posix.OPOST;
termios.c_cc[Posix.VMIN] = 0;
termios.c_cc[Posix.VTIME] = 10;
Posix.tcflush (handle, Posix.TCIOFLUSH);
Posix.tcsetattr (handle, Posix.TCSAFLUSH, termios);
Posix.fcntl (handle, Posix.F_SETFL, FNDELAY);
Posix.fcntl (handle, Posix.F_SETFL, 0);
/* Everything works fine, if I open it using tail -f /dev/ttyUSB0*/
uint8[] data = new uint8[4];
data[0] = 1;
data[1] = 1;
data[2] = 0;
data[3] = data[0] ^ data[1] ^ data[2];
int l = (int)Posix.write (handle, data, data.length);
Thread.usleep (1000 * 1000);
data[0] = make_array (2, true)[0];
data[1] = make_array (2, true)[1];
data[2] = make_array (2, true)[2];
data[3] = data[0] ^ data[1] ^ data[2];
l = (int)Posix.write (handle, data, data.length);
}
I have created two variables on the top, which doesn't exist in the posix namespace. I simply print the value in C.
This code in vala doesn't work, the relay doesn't receive anything, although it uses the same flags and handles.
The strange thing is, that the vala code works fine, if I open a terminal and run "tail -f /dev/ttyUSB0".
It seems that the valacode isn't able to initialize the serial port, so relay will never receive them.
Anybody know a fix for this, or find my mistake in the vala code?
Normally the librarys should be the same in both programming languages.
I can open serial port, but I can't correctly configure this port for write (/dev/ttyUSB0).
Piece of code C++:
int
Platform::initConnection( const char* devicePath, int baudRate )
{
int fd = 0;
int ret = 0;
struct termios terminalOptions; // POSIX structure for configurating terminal devices
fd = open( devicePath, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
//fd = open( devicePath, O_RDWR | O_NOCTTY );
if (fd == -1)
{
this->setFail();
this->setErrorStr( "Failed to open: " + (std::string)devicePath + ". " + (std::string)strerror(errno) );
return -1;
}
memset( &terminalOptions, 0, sizeof( struct termios ) ); // Cleaning up the structure
cfmakeraw(&terminalOptions); //
cfsetspeed(&terminalOptions, baudRate);
/*terminalOptions.c_cflag = CLOCAL; // If CLOCAL is set, the line behaves as if DCD is always asserted.
// It is used when your device is local
terminalOptions.c_cflag |= CS8; // Character size mask
terminalOptions.c_cc[VMIN] = 24; // 1 second timeout
terminalOptions.c_cc[VTIME] = 0; // */
terminalOptions.c_cflag &= ~CRTSCTS;
terminalOptions.c_cflag |= (CLOCAL | CREAD);
terminalOptions.c_iflag |= (IGNPAR | IGNCR);
terminalOptions.c_iflag &= ~(IXON | IXOFF | IXANY);
terminalOptions.c_oflag &= ~OPOST;
terminalOptions.c_cflag &= ~CSIZE;
terminalOptions.c_cflag |= CS8;
terminalOptions.c_cflag &= ~PARENB;
terminalOptions.c_iflag &= ~INPCK;
terminalOptions.c_iflag &= ~(ICRNL|IGNCR);
terminalOptions.c_cflag &= ~CSTOPB;
terminalOptions.c_iflag |= INPCK;
terminalOptions.c_cc[VTIME] = 0.001; // 1s=10 0.1s=1 *
terminalOptions.c_cc[VMIN] = 0;
ret = ioctl( fd, TIOCSETA, &terminalOptions ); // Configuring the device
if (ret == -1)
{
this->setFail();
this->setErrorStr( "Failed to configure device: " + (std::string)devicePath + ". " + (std::string)strerror(errno) );
return -1;
}
return fd;
}
Error:
Failed to configure device: /dev/ttyUSB0. Inappropriate ioctl for device
Arduino UNO uses chipset CH340.
I have no idea about the resolve this problem. I'm hope for your help. Thanks!
Update:
Log from dmesg
[11840.346071] usb 2-1.2: new full-speed USB device number 5 using ehci-pci
[11840.439832] usb 2-1.2: New USB device found, idVendor=1a86, idProduct=7523
[11840.439844] usb 2-1.2: New USB device strings: Mfr=0, Product=2, SerialNumber=0
[11840.439850] usb 2-1.2: Product: USB2.0-Serial
[11840.440472] ch341 2-1.2:1.0: ch341-uart converter detected
[11840.442452] usb 2-1.2: ch341-uart converter now attached to ttyUSB0
Thanks to all. I found the solution on their own:
As autoreset on serial connection is activated by default on most boards, you need to disable this feature if you want to communicate directly with your board with the last command instead of a terminal emulator (arduino IDE, screen, picocom...). If you have a Leonardo board, you are not concerned by this, because it does not autoreset. If you have a Uno board, connect a 10 µF capacitor between the RESET and GND pins. If you have another board, connect a 120 ohms resistor between the RESET and 5V pins. See http://playground.arduino.cc/Main/DisablingAutoResetOnSerialConnection for more details.
Сhanged code
memset( &terminalOptions, 0, sizeof( struct termios ) );
tcgetattr(fd, &terminalOptions); //change
cfmakeraw(&terminalOptions);
cfsetspeed(&terminalOptions, baudRate);
terminalOptions.c_cflag = CLOCAL;
terminalOptions.c_cflag |= CS8;
terminalOptions.c_cc[VMIN] = 0;
terminalOptions.c_cc[VTIME] = 10;
terminalOptions.c_cflag = CLOCAL;
terminalOptions.c_cflag &= ~HUPCL; //change (disable hang-up-on-close to avoid reset)
ret = tcsetattr(fd, TCSANOW, &terminalOptions); //change
if (ret == -1)
{
this->setFail();
this->setErrorStr( "Failed to configure device: " + (std::string)devicePath + ". " + (std::string)strerror(errno) );
return -1;
}
return fd;
I also have arduino UNO and when I plug it via usb port it connects to /dev/ttyACM0 not ttyUSB0 you should also check ttyACM0 when you plug and unplug your arduino UNO.
It is also the case If you haven't installed arduino port driver
I'd like a little clarification on what socat does to the serial communication, because for some reason I can only get the communication to my Arduino to work when using socat.
This is the command I use to set up socat:
socat -d -v -x PTY,link=/tmp/serial,wait-slave,raw /dev/ttyACM0,raw
The code I used in C++ is:
int file;
file = open("/tmp/serial", O_RDWR | O_NOCTTY | O_NDELAY);
if (file == -1)
{
perror("open_port: Unable to open \n");
}
else
fcntl(file, F_SETFL, 0);
struct termios options;
//* Get the current options for the port...
tcgetattr(file, &options);
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
options.c_oflag = 0;
//options.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | OFILL | OLCUC | OPOST);
options.c_cflag |= (CLOCAL | CREAD);
cfmakeraw(&options);
tcsetattr(file, TCSANOW, &options);
sprintf(sendpack,"$C000C000C000C000\r");
num = write(file,sendpack,18);
tcflush(file, TCIOFLUSH); // clear buffer
So when I run this through socat the arduino reacts as it is supposed to, but if I change the open() line from /tmp/serial to /dev/ttyACM0 the arduino no longer performs at all. Can someone exlplain to me what socat is doing to the data to make things work, and how I can change my C code to do the same thing? I cannot use socat in my final implementation because it causes a delay that would make driving my robot unpredictable.
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.