#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <cerrno>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstdlib>
#include <ctime>
using namespace std;
typedef struct termios ComSet;
int main()
{
int acm=-1;
acm=open("/dev/ttyACM0",O_RDWR | O_NOCTTY);
if(acm == -1)
{
cout<<"Error Opening ttyACM0"<<endl;
exit(1);
}
else
{
cout<<"Preparing ttyACM0..."<<endl;
ComSet SerialSettings;
tcgetattr(acm, &SerialSettings);
cfsetispeed(&SerialSettings,B115200);
cfsetospeed(&SerialSettings,B115200);
/* 8N1 Mode */
SerialSettings.c_cflag &= ~PARENB;
SerialSettings.c_cflag &= ~CSTOPB;
SerialSettings.c_cflag &= ~CSIZE;
SerialSettings.c_cflag |= CS8;
SerialSettings.c_cflag &= ~CRTSCTS;
SerialSettings.c_cflag |= CREAD | CLOCAL;
SerialSettings.c_iflag &= ~(IXON | IXOFF | IXANY);
SerialSettings.c_iflag &=ICANON;
SerialSettings.c_iflag &= ~(ECHO | ECHOE | ISIG);
SerialSettings.c_oflag &= ~OPOST;
/* Setting Time outs */
SerialSettings.c_cc[VMIN] = 1;
SerialSettings.c_cc[VTIME] = 0;
if((tcsetattr(acm,TCSANOW,&SerialSettings)) != 0)
{
cout<< " ERROR ! in Setting attributes"<<endl;
}
else
{
cout<< "=======ttyACM0 Setting====="<<endl;
cout<<"BaudRate = 115200 StopBits = 1 Parity = none"<<endl;
cout<<"Reading ttyACM0... "<<endl;
char read_buffer[1024];
bzero(read_buffer,1024);
int read_bytes=0;
while(1)
{
write(acm,"Hello World !",13);
read_bytes=read(acm,&read_buffer,1024);
if(read_bytes>0)
{
cout<<read_buffer;
bzero(read_buffer,1024);
}
else
{
cout<<"No-Data"<<endl;
}
tcflush(acm, TCOFLUSH);
tcflush(acm, TCIFLUSH);
}
}
close(acm); /* Close the serial port */
}
return 0;
}
Why is this code not working ? i m trying to receive same message from the arduino but it will not work.it completely ignores write.i have also found that it works if i use cin.get() before write.
it works if i want to read from arduino only.
Here is my arduino code
String Input;
boolean done=false;
void setup()
{
Serial.begin(115200);
}
void loop()
{
Serial.println(GetSerial());
Input="";
}
String GetSerial()
{
done=false;
Input="";
while(!done)
{
while(Serial.available()>0)
{
Input=Serial.readStringUntil('\n'); //Block Reading
done=true;
}
}
return Input;
}
i want to receive whatever is sent on serial port as it is with as much less delay as possible
Related
I am trying to read serial data from an Arduino Uno. I am using the Termios struct and followed this tutorial. I am not receiving the Arduino serial data in return, can anyone help me? First time using c++ to read serial. Following is the c++ code (i'm using ROS), and the arduino code.
c++ code
#include <ros.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#include <csignal>
#include <sstream>
#include <algorithm>
#include <iterator>
const char *arduino_path = "/dev/ttyACM0";
int serial_port;
struct termios tty;
void init_serial(){
//do cereal things https://blog.mbedded.ninja/programming/operating-systems/linux/linux-serial-ports-using-c-cpp/
serial_port = open(arduino_path, O_RDWR | O_NOCTTY | O_NDELAY);
if (serial_port < 0) {
ROS_ERROR("Error %i from open: %s", errno, strerror(errno));
}
memset(&tty, 0, sizeof tty);
if(tcgetattr(serial_port, &tty) != 0) {
printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
}
//set flags
tty.c_cflag &= ~PARENB;
tty.c_cflag |= CS8; // 8 bits per byte (most common)
tty.c_cflag &= ~CRTSCTS;
tty.c_cflag |= CREAD | CLOCAL;
tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO; // Disable echo
tty.c_lflag &= ~ECHOE; // Disable erasure
tty.c_lflag &= ~ECHONL; // Disable new-line echo
tty.c_lflag &= ~ISIG;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
tty.c_cc[VTIME] = 10; // Wait for up to 1s (10 deciseconds), returning as soon as any data is received.
tty.c_cc[VMIN] = 0;
cfsetispeed(&tty, B9600);
cfsetospeed(&tty, B9600);
if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
}
}
std::string read_port(){
// Allocate memory for read buffer, set size according to your needs
char read_buf [256];
memset(&read_buf, '\0', sizeof(read_buf));
int n = read(serial_port, &read_buf, sizeof(read_buf));
std::string ret(read_buf);
return ret;
}
int main(int argc, char **argv){
ros::init(argc, argv, "sensor_handler");
ros::NodeHandle n;
init_serial();
while(ros::ok()){
cout << read_port() << std::endl;
}
}
Arduino code:
bool startup;
//Soil pins
const int soil_sensor_A0_AI = 0;
const int soil_sensor_A0_P = 0;
const int soil_sensor_A1_AI = 1;
const int soil_sensor_A1_P = 1;
const int soil_sensor_B0_AI = 2;
const int soil_sensor_B0_P = 2;
const int soil_sensor_B1_AI = 3;
const int soil_sensor_B1_P = 3;
int no_soil_sensors = 4;
//timer for the water
double water_timer;
int hours_delay = 4;
//water level vars
int water_level_sensor_pin = 5; //must be a PWM pin
int water_level; //in mm^3
double min_water = 20;
void setup() {
Serial.begin(9600);
//Setup soil moisture power pins as outputs
pinMode(soil_sensor_A0_P, OUTPUT);
pinMode(soil_sensor_A1_P, OUTPUT);
pinMode(soil_sensor_B0_P, OUTPUT);
pinMode(soil_sensor_B1_P, OUTPUT);
pinMode(water_level_sensor_pin, INPUT);
//init timer
water_timer = micros();
startup = true;
}
void loop() {
//check if starting up
if(startup){
check_water();
check_soil();
startup = false;
water_timer = micros();
}
/*
* if last check+delay in micros is less than or equal to current time then it means it is time to run a soil check.
*/
if(water_timer + (3600000000*hours_delay) <= micros() ){
check_water();
check_soil();
water_timer = micros();
}
//keep checking water, good way to monitor for leaks
check_water();
}
void check_soil() {
Serial.print("$SOIL:");
if(water_level < min_water){
Serial.println("E, Water level is too low to water the plants");
return;
}
for(int i = 0; i<no_soil_sensors; i++){
switch(i){
case soil_sensor_A0_AI:
digitalWrite(soil_sensor_A0_P, HIGH);
Serial.print("A0,");
Serial.print(analogRead(soil_sensor_A0_AI));
Serial.print(";");
digitalWrite(soil_sensor_A0_P, LOW);
break;
case soil_sensor_A1_AI:
digitalWrite(soil_sensor_A1_P, HIGH);
Serial.print("A1,");
Serial.print(analogRead(soil_sensor_A1_AI));
Serial.print(";");
digitalWrite(soil_sensor_A1_P, LOW);
break;
case soil_sensor_B0_AI:
digitalWrite(soil_sensor_B0_P, HIGH);
Serial.print("B0,");
Serial.print(analogRead(soil_sensor_B0_AI));
Serial.print(";");
digitalWrite(soil_sensor_B0_P, LOW);
break;
case soil_sensor_B1_AI:
digitalWrite(soil_sensor_B1_P, HIGH);
Serial.print("B1,");
Serial.print(analogRead(soil_sensor_B1_AI));
Serial.print(";");
digitalWrite(soil_sensor_B1_P, LOW);
break;
default:
Serial.println("E, SOIL_SENSORS");
break;
}
}
Serial.println("");
}
void check_water(){
//I don't know the math behind this yet but it will be in here
Serial.print("$WATER:");
double water_voltage = digitalRead(water_level_sensor_pin);
//math
double water_level = 0;
Serial.print(water_level);
if(water_level <= min_water){
Serial.print(";E, Water Level Low");
}
Serial.println("");
}
I got a problem with a posix signal, that I use for reading serial data. First here is a small example code how I initialize my serial port, my signal and a shared memory.
#include <unistd.h>
#include <termios.h>
#include <sys/signal.h>
#include <stdint.h>
#include <cassert>
#include <cstdio>
#include <fcntl.h>
#include <iostream>
#include <QSharedMemory>
int fd;
struct termios termAttr;
struct sigaction saio;
char buff[255];
QSharedMemory QShare("IDSTRINGOFTHISMEMORY");
void signal_handler_IO (int status)
{
std::cout << "Got Serial Data!" << std::endl;
int si = read(fd, &buff[0], 255);
if(true) // Works with false here, but then the shared memory ist not used!
{
char TWrite = 't';
QShare.lock();
char *to = (char*)QShare.data();
const char *from = reinterpret_cast<char*>(&TWrite);
memcpy(to, from, 1);
QShare.unlock();
}
}
int main()
{
// Create SharedMemory:
QShare.create(1);
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
return -1;
}
saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigemptyset(&saio.sa_mask);
sigaddset(&saio.sa_mask, SIGINT);
sigaction(SIGIO,&saio,NULL);
fcntl(fd, F_SETFL, FNDELAY|FASYNC);
fcntl(fd, F_SETOWN, getpid());
tcgetattr(fd,&termAttr);
cfsetospeed(&termAttr,B57600);
cfsetispeed(&termAttr,B57600);
termAttr.c_cflag &= ~PARENB;
termAttr.c_cflag &= ~CSTOPB;
termAttr.c_cflag &= ~CSIZE;
termAttr.c_cflag |= CS8;
termAttr.c_cflag |= (CLOCAL | CREAD);
termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
termAttr.c_oflag &= ~OPOST;
termAttr.c_cc[VMIN] = 5;
tcsetattr(fd,TCSANOW,&termAttr);
while(true)
{
usleep(1000000);
}
return 0;
}
I had this code in use for a while and it did work quite well. Now for a different project I want to store the serial data in shared memory block, so I can read the current datapoint from multiple processes. In the signal_handler_IO I try to write the data to a shared memory that is created by sys/shm.h. The moment I started using shared memory the posix signal stopped working. Since I was unsure about the use of the shared memory I also tried to use the QSharedMemory class from qt, which created the same result.
Can you tell me, what the shared memory does to my posix signal?
I wrote a code to connect, throught a serial port, mi computer to arduino.
This is arduino's code:
#include <Servo.h>
Servo servo;
const int pinServo = 2;
unsigned int angle;
void setup()
{
Serial.begin(9600);
servo.attach(pinServo);
servo.write(0);
}
void loop()
{
if(Serial.available()>0)
{
angle = Serial.read();
if(angle <= 179)
{
servo.write(angle);
}
}
}
And this is c++'s:
#include <iostream>
#include <unistd.h>
#include <fstream>
#include <termios.h>
using namespace std;
int main()
{
unsigned int angle;
ofstream arduino;
struct termios ttable;
cout<<"\n\ntest1\n\n";
arduino.open("/dev/tty.usbmodem3a21");
cout<<"\n\ntest2\n\n";
if(!arduino)
{
cout<<"\n\nERR: could not open port\n\n";
}
else
{
if(tcgetattr(arduino,&ttable)<0)
{
cout<<"\n\nERR: could not get terminal options\n\n";
}
else
{
ttable.c_cflag &= ~PARENB;
ttable.c_cflag &= ~CSTOPB;
ttable.c_cflag &= ~CSIZE;
ttable.c_cflag |= CS8;
ttable.c_cflag &= ~CRTSCTS;
ttable.c_cflag |= CREAD | CLOCAL;
ttable.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
ttable.c_oflag &= ~OPOST;
ttable.c_cc[VMIN] = 0;
ttable.c_cc[VTIME] = 0;
cfsetospeed(&ttable,9600);
if(tcsetattr(arduino,TCSANOW,&ttable)<0)
{
cout<<"\n\nERR: could not set new terminal options\n\n";
}
else
{
do
{
cout<<"\n\ninsert a number between 0 and 179";
cin>>angle;
arduino<<angle;
}while(angle<=179);
arduino.close();
}
}
}
}
It should connect to arduino , then ask me a number between 0 an 179 and the send that number to arduino which aplly that number as an angle to a servo motor;
But it stops at arduino.open("/dev/tty.usbmodem3a21") .What can i do ?
I think your problems appear in these lines of code
if(tcgetattr(arduino,&ttable)<0) {
// ...
}
else {
// ...
if(tcsetattr(arduino,TCSANOW,&ttable)<0) {
// ...
}
}
The arduino variable is of type ofstream, where tcgetattr() and tcsetattr() expect a file descriptor obtained with open() at this point.
ofstream provides an automatic conversion to bool and thus int implicitely. Supposed the
arduino.open("/dev/tty.usbmodem3a21");
went OK, you are effectively passing 1 to tcgetattr() and tcsetattr(), which is the standard input file descriptor.
Solution is not to use a ofstream for arduino, but an ordinary file descriptor
int arduino = open("/dev/tty.usbmodem3a21",O_WRONLY);
I want to control the RTS line on the serial port to wake up a device. Here is some rough test code to open the serial port. I need to set the RTS line low for 500ms. I'm having trouble googleing/understanding how to control the lines. Any Hints?
EDIT: OR controlling the CTS/DTR, any other line besides the Tx/Rx lines to connect to a pin of the robot.
#include <iostream>
#include <string.h>
#include <cstdio>
#include <fstream>
#include <sys/types.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <stdint.h> /* Standard types */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/ioctl.h>
#include <getopt.h>
using namespace std;
int fd_global;
FILE* fp_global;
int serialport_init(const char* serialport, int baud);
int serialport_read_until(int fd);
int fpeek(FILE* stream);
bool fexists(const char *filename);
void* screen_thread(void* arg) {
while(1) {
}
}
int main(void) {
pthread_t screen_thread_id;
int flags;
int baudrate = B9600; // default
fd_global = serialport_init((char*)"/dev/ttyUSB0", baudrate);
if(fd_global==-1) {
cout << "Open port: error\n";
return -1;
}
fp_global = fdopen(fd_global, "r");
if (-1 == (flags = fcntl(fd_global, F_GETFL, 0))) flags = 0;
fcntl(fd_global, F_SETFL, flags | O_NONBLOCK);
//pthread_create(&screen_thread_id, NULL, screen_thread, (void*)NULL);
serialport_read_until(fd_global);
return 0;
}
int serialport_read_until(int fd)
{
char b[1];
while(1) {
int n = read(fd, b, 1); //Read byte at a time
if(n==-1) {
printf("N");fflush(stdout);
perror("READ: ");
}
else if( n==0 ) {
printf("Z");fflush(stdout);
} else if(n>0) {
printf(":%d:",n);fflush(stdout);
}
usleep(100*1000);
if (fexists("/dev/ttyUSB0"))
printf("CONNECTED\n");
else
printf("NOT CONNECTED\n");
}
return 0;
}
bool fexists(const char *filename)
{
ifstream ifile(filename);
return ifile;
}
int serialport_init(const char* serialport, int baud)
{
struct termios toptions;
int fd;
// Open port
fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("init_serialport: Unable to open port ");
return -1;
}
// Read current termios settings
if (tcgetattr(fd, &toptions) < 0) {
perror("init_serialport: Couldn't get term attributes");
return -1;
}
// Set baud rate variable
speed_t brate = baud;
switch(baud) {
case 2400: brate=B2400; break;
case 4800: brate=B4800; break;
case 9600: brate=B9600; break;
#ifdef B14400
case 14400: brate=B14400; break;
#endif
case 19200: brate=B19200; break;
#ifdef B28800
case 28800: brate=B28800; break;
#endif
case 38400: brate=B38400; break;
case 57600: brate=B57600; break;
case 115200: brate=B115200; break;
}
cfsetispeed(&toptions, brate);
cfsetospeed(&toptions, brate);
// Setup termios for 8N1
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
// Reccomended settings
toptions.c_cflag &= ~CRTSCTS; // no flow control
toptions.c_cflag |= CREAD | CLOCAL; // turn on read & ignore ctrl lines
toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
toptions.c_oflag &= ~OPOST; // make raw
// Setting when read() releases
// see: http://unixwiz.net/techtips/termios-vmin-vtime.html (Still a little confusing)
toptions.c_cc[VMIN] = 0;
toptions.c_cc[VTIME] = 20;
// Apply settings
if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
perror("init_serialport: Couldn't set term attributes");
return -1;
}
return fd;
}
Would this work? I'll test it tomorrow.
int status;
status |= TIOCM_RTS;
ioctl(fd, TIOCMSET, &status);
usleep(500*1000);
status &= ~TIOCM_RTS;
ioctl(fd, TIOCMSET, &status);
I want a infinit loop to break when I press "q" key on my keyboard.
Problems I did'nt realize: standard getchar waits for the user to make an
input and press enter, which halt the execution of the loop there.
I got around the "enter" problem, but the loop still halts and waits for the input.
here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <termios.h>
int getch(void); // Declare of new function
int main (void) { char x;
do
{
if (x = getch())
printf ("Got It! \n!);
else
{
delay(2000);
printf ("Not yet\n!);
}
}while x != 'q');
return 0;
}
int getch(void)
{
int ch;
struct termios oldt;
struct termios newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return ch;
}
You could read them from the device:
#define INPUT_QUEUE "/dev/input/event0"
#define EVENT_LEN 16
void readEventLine(FILE * in, char * data) { //read input key stream
int i;
for(i = 0; i <= 15; i++) { //each key press will trigger 16 characters of data, describing the event
data[i] = (char) fgetc(in);
}
}
int readKeyPress() {
FILE * input;
char data[EVENT_LEN];
input = fopen(INPUT_QUEUE, "r+");
readEventLine(input, data);
}
Just call something like this instead of your getch.
Adapted from http://www.cplusplus.com/forum/unices/8206/
I had to do following to make it work correctly, thanks for input!!
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
int getch(void); // Declare of new function
int main (void)
{
char x;
do
{
x = getch();
if (x != EOF)
{
printf ("\r%s\n", "Got something:");
printf ("it's %c!",x); // %c - for character %d - for ascii number
}else
{
delay(2000);
printf ("Not yet\n!);
}
}while x != 'q');
return 0;
}
int getch(void)
{
int ch;
struct termios oldt;
struct termios newt;
long oldf;
long newf;
tcgetattr(STDIN_FILENO, &oldt); /* Store old settings */
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO); /* Make one change to old settings in new settings */
tcsetattr(STDIN_FILENO, TCSANOW, &newt); /* Apply the changes immediatly */
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
newf = oldf | O_NONBLOCK;
fcntl(STDIN_FILENO, F_SETFL, newf);
ch = getchar();
fcntl(STDIN_FILENO, F_SETFL, oldf);
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); /* Reapply the old settings */
return ch;
}