I have written some code to find any modems on a unix system using the regex /dev/tty* basically and then for any matches see if can open the port and if so send an AT command and check if the response message contains the characters 'OK'.
The code does find a modem but unfortunately it messes up the terminal display. See below. I notice that it also prints the AT command - see output below. Why is my terminal display altered and how can I fix that?
After running the program, if you enter a command and enter, eg ls, the command is not shown but when you press enter you do see the output.
Here is the code:
#include <iostream>
#include <string>
#include <unordered_map>
#include <iomanip>
#include <memory>
#include <sstream>
#include <thread>
#include <iostream>
#include <filesystem>
#include <regex>
#include <unistd.h> // close
#include <fcntl.h> // open, O_RDWR, etc
#include <termios.h>
#include <string.h>
#include <sys/select.h> // timeouts for read
#include <sys/timeb.h> // measure time taken
int set_interface_attribs(int fd, int speed)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
// Error from tcgetattr - can use strerror(errno)
return -1;
}
cfsetospeed(&tty, (speed_t)speed);
cfsetispeed(&tty, (speed_t)speed);
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag &= ~PARENB; /* no parity bit */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
/* setup for non-canonical mode */
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 1;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
// Error from tcsetattr- use strerror(errno)
return -1;
}
return 0;
}
long enumerate_ports(std::unordered_map <std::string, std::string>& ports) {
// ls /dev | grep ^tty.*
const std::regex my_filter( "^tty.*" );
std::string path = "/dev/";
for (const auto & entry : std::filesystem::directory_iterator(path)) {
std::smatch sm;
std::string tmp = entry.path().filename().string();
// if we have a regex match attempt to open port and send AT command
if (std::regex_match(tmp, sm, my_filter)) {
std::string portname = entry.path().string();
int fd = ::open(portname.c_str(), O_RDWR | O_NOCTTY);
if (fd < 0) {
// Error opening port
continue;
} else {
// port was opened successfully
// try to write AT command and do we get an OK response
// baudrate 9600, 8 bits, no parity, 1 stop bit
if(set_interface_attribs(fd, B9600) != 0) {
::close(fd);
continue;
}
int wlen = ::write(fd, "AT\r\n", 4);
if (wlen != 4) {
// Error from write
::close(fd);
continue;
}
// tcdrain() waits until all output written to the object referred
// to by fd has been transmitted.
tcdrain(fd);
fd_set set;
struct timeval timeout;
FD_ZERO(&set); /* clear the set */
FD_SET(fd, &set); /* add our file descriptor to the set */
timeout.tv_sec = 0;
timeout.tv_usec = 100000; // 100 milliseconds
// wait for data to be read or timeout
int rv = select(fd + 1, &set, NULL, NULL, &timeout);
if(rv > 0) { // no timeout or error
unsigned char buf[80];
const int bytes_read = ::read(fd, buf, sizeof(buf) - 1);
if (bytes_read > 0) {
buf[bytes_read] = 0;
unsigned char* p = buf;
// scan for "OK"
for (int i = 0; i < bytes_read; ++i) {
if (*p == 'O' && i < bytes_read - 1 && *(p+1) == 'K') {
// we have a positive response from device so add to ports
ports[portname] = "";
break;
}
p++;
}
}
}
::close(fd);
}
}
}
return ports.size();
}
int main() {
struct timeb start, end;
int diff;
ftime(&start);
// get list of ports available on system
std::unordered_map <std::string, std::string> ports;
long result = enumerate_ports(ports);
std::cout << "No. found modems: " << result << std::endl;
for (const auto& item : ports) {
std::cout << item.first << "->" << item.second << std::endl;
}
ftime(&end);
diff = (int) (1000.0 * (end.time - start.time)
+ (end.millitm - start.millitm));
printf("Operation took %u milliseconds\n", diff);
}
And the output:
acomber#mail:~/Documents/projects/modem/serial/gdbplay$ ls
main.cpp main.o Makefile serial
acomber#mail:~/Documents/projects/modem/serial/gdbplay$ make serial
g++ -Wall -Werror -ggdb3 -std=c++17 -pedantic -c main.cpp
g++ -o serial -Wall -Werror -ggdb3 -std=c++17 -pedantic main.o -L/usr/lib -lstdc++fs
acomber#mail:~/Documents/projects/modem/serial/gdbplay$ sudo ./serial
[sudo] password for acomber:
AT
No. found modems: 1
/dev/ttyACM0->
Operation took 8643 milliseconds
acomber#mail:~/Documents/projects/modem/serial/gdbplay$
Why does this serial/modem code mess up my terminal display?
A precise answer requires you to post the terminal's settings prior to executing your code, i.e. stty -a.
The code does find a modem but unfortunately it messes up the terminal display.
The simplest (i.e. straightforward) workaround/solution is to adhere to the old (but rarely followed) advice of saving and then restoring the terminal's termios settings, as in this example.
The simple changes needed in your code would be something like (please overlook mixing of C and C++; I only know C) the following patch.
struct termios savetty;
int set_interface_attribs(int fd, int speed)
{
+ struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
// Error from tcgetattr - can use strerror(errno)
return -1;
}
+ savetty = tty; /* preserve original settings for restoration */
cfsetospeed(&tty, (speed_t)speed);
cfsetispeed(&tty, (speed_t)speed);
Then in enumerate_ports(), the last two instances of ::close(fd); need to be replaced with the sequence that will perform the restoration:
+ if (tcsetattr(fd, &savetty) < 0) {
+ // report cannot restore attributes
+ }
::close(fd);
After running the program, if you enter a command and enter, eg ls, the command is not shown ...
That is obviously the result of leaving the ECHO attribute cleared.
The "missing" carriage returns are probably due to a cleared OPOST.
Other salient attributes that were cleared by your program but probably are expected to be set by the shell are ICANON, ICRNL, and IEXTEN.
But rather than try to determine what exactly needs to be undone, the proper and guaranteed fix is to simply restore the termios settings back to its original state.
An alternative (lazy) approach would to use the stty sane command after you execute your program.
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 am trying to write to the serial port (sending a handshake) and then subsequently I try to read the serial port. When reading the port, I notice I am getting garbage reads (even if there is nothing connected to the RX line) or part of the write string I am sending to the TX line. Why am I getting part of that string? I am not supposed to be seeing that!
Here is my code:
class UART{
public:
UART();
~UART();
int open_port();
int configure_port(); // All port configurations such as parity, baud rate, hardware flow, etc
int uart_write(std::string); // Send characters to the serial port
int uart_read(std::string*, int); // Read from serial port
// Close
void close_port();
private:
int fd;
uart.cpp:
UART::UART(){
open_port();
configure_port();
}
UART::~UART(){
close_port();
}
int UART::open_port()
{
// Open ttys4
fd = open("/dev/ttyS4", O_RDWR | O_NOCTTY | O_NDELAY);
if(fd == -1) // if open is unsucessful
{
//perror("open_port: Unable to open /dev/ttyS0 - ");
printf("open_port: Unable to open /dev/ttyS4. \n");
}
else
{
fcntl(fd, F_SETFL, 0);
printf("port is open.\n");
}
return(fd);
} //open_port
// configure the port
int UART::configure_port()
{
struct termios port_settings; // structure to store the port settings in
cfsetispeed(&port_settings, B9600); // set baud rates
cfsetospeed(&port_settings, B9600);
port_settings.c_cflag &= ~PARENB; // set no parity, stop bits, data bits
port_settings.c_cflag &= ~CSTOPB;
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8;
port_settings.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
port_settings.c_cc[VTIME] = 10; // n seconds read timeout
port_settings.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
port_settings.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
port_settings.c_oflag &= ~OPOST; // make raw
tcsetattr(fd, TCSANOW, &port_settings); // apply the settings to the port
return(fd);
}
// Write to serial port
int UART::uart_write(string data)
{
int buffer_size = data.length();
char * data_write = new char[data.length()+1];
strcpy (data_write, data.c_str());
int n = write(fd, data_write, buffer_size); //Send data
usleep(1000);
tcdrain(fd);
printf("Wrote the bytes. \n");
/* Error Handling */
int status = 0;
if (n < 0)
{
cout << "Error Writing: " << strerror(errno) << endl;
status = 0;
}else{
status = 1;
}
delete[] data_write;
return status;
}
int UART::uart_read(string *data,int buffer_size)
{
// Buffer
char * buf = new char[buffer_size+1];
usleep(1000);
tcflush(fd, TCIOFLUSH);
// Read
/*I NEED THIS PART TO BE BLOCKING*/
int n = read( fd, buf , buffer_size );
/* Error Handling */
if (n < 0)
{
cout << "Error reading: " << strerror(errno) << endl;
}
// String received
string data_received(buf,buffer_size);
*data = data_received;
delete[] buf;
cout << "data_received: " << *data << endl;
// Did we get blank data?
if( data_received.length() == 0 )
return 0;
else
return 1;
}
main
int main()
{
UART uart_connection;
string handshake = "handshake!";
uart_connection.uart_write(handshake);
string data;
string *data_ptr = &data;
uart_connection.uart_read(data_ptr );
cout << data << endl;
}
When printing the received data, I usually get part of the sent data. So on cout << data << endl I am getting the following:
dshake
along with some weird characters after it, or if I don't write anything to the serial port then I just get random characters.
Specifically I want int n = read( fd, buf , buffer_size ); to be a blocking function, which apparently it's not happening... It just goes through and it returns a bunch of weird characters or it reads part of the string sent with write.
Please note that the code works and when I do actually send something to the RX line, I can read it just fine. However, I am finding it difficult to send large chunks of data without getting bad reads.
I believe this could all be solved if I could make the read() function a blocking function, and avoid it reading those weird characters.
Read is always allowed to read less than what you asked for. To make it "block" until you have read enough characters, you need to wrap it in a loop and call it until you've read however many bytes you wanted.
In your code, n is the number of bytes that were read successfully. You only ever check that it is non-negative.
The loop would probably look like this:
size_t read_count = 0;
while (read_count < buffer_size)
{
ssize_t read_result = read(fd, buf + read_count, buffer_size - read_count);
if (read_result < 0)
{
cout << "Error reading: " << strerror(errno) << endl;
break;
}
read_count += read_result;
}
Note that generally speaking, read is a low-level interface with lots of easy-to-miss subtleties. For instance, on error, it's worth checking for EINTR and maybe a few others.
Off the top of my head, FILE* functions don't have these issues, and you may be able to use fdopen and fread to consistently get what you want.
Although it appears that you haven't had that problem yet, write has the same set of issues. It is also allowed to write fewer bytes than you gave it, and it can be interrupted too. However, this rarely happens with small writes.
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);