I have written the C program that should read UART's RxD port and display the results as soon as there is any information. To achieve this I'm using signal_handler SIGIO signal
Read program c code
#include <iostream>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <poll.h>
#include <time.h>
#include <string.h>
#define BAUDRATE B19200
#define PORT "/dev/ttyO4"
#define _POSIX_SOURCE 1
int fd;
void signal_handler_IO(int status);
void set_port_settings();
char buff[255];
sig_atomic_t flag=0;
using namespace std;
int main ()
{
set_port_settings();
for(;;){
if(flag !=0)
{
//printf( "sync : 0x%X\n", buff[1]);
//printf ( "PID: 0x%X\n", buff[2]);
printf ( "D0: 0x%X\n", buff[4]);
printf ( "D1: 0x%X\n", buff[5]);
printf ( "D2: 0x%X\n", buff[6]);
printf ( "D3: 0x%X\n", buff[7]);
printf ( "D4: 0x%X\n", buff[8]);
printf ( "D5: 0x%X\n", buff[9]);
printf ( "D6: 0x%X\n", buff[10]);
printf ( "D7: 0x%X\n", buff[11]);
printf ( "CHK: 0x%X\n", buff[12]);
flag = 0;
}
}
}
void signal_handler_IO(int status)
{
if(flag !=1)
{
read(fd, &buff, sizeof(buff));
flag = 1;
}
}
void set_port_settings()
{
struct termios oldtio, newtio;
struct sigaction saio;
fd = open(PORT, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd<0) {perror(PORT); exit(-1);}
saio.sa_handler=signal_handler_IO;
sigemptyset(&saio.sa_mask);
saio.sa_flags=SA_RESTART;
sigaction(SIGIO, &saio,NULL);
fcntl (fd, F_SETOWN, getgid());
fcntl(fd, F_SETFL, FASYNC);
tcgetattr(fd, &oldtio); perror("tsgetattr");
newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD ; perror("c_cflag");
newtio.c_iflag = IGNPAR | IXON ; perror("c_iflag");
newtio.c_oflag = 0; perror("c_oflag");
newtio.c_lflag = ICANON | ISIG ; perror("c_lflag");
newtio.c_cc[VMIN]=8;perror("c_cc[VMIN]");
newtio.c_cc[VTIME]=1; perror("c_cc[VTIME]");
newtio.c_cc[VSTART]=0x55;
tcflush(fd, TCIFLUSH); perror("TCFLUSH");
tcsetattr(fd, TCSANOW, &newtio); perror("tcsetattr");
}
Problem that I have, is that when the program reads data and starts printing out the results, the information printed out is somehow correct just printed (or read in) in a wrong place.
I'm writing to the port using another C program. I've tried to do it from the same C program but was unsuccessful to write and read from the same C program. So I keep 2 shells open: on one shell I'm running write program, on another shell I'm running read program to display what it was able to read in.
Write program C code
#include <iostream>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#define BAUDRATE9600 B19200
#define PORT "/dev/ttyO4"
#define _POSIX_SOURCE 1
using namespace std;
int main() {
int fd;
char buffer[255];
struct termios oldtio, newtio;
struct sigaction saio;
fd = open(PORT, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd<0) {perror(PORT); exit(-1);}
tcgetattr(fd, &oldtio);
newtio.c_cflag = BAUDRATE9600 | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
newtio.c_lflag = 0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &newtio);
char SYNC [] = {0x55};
char PID [] = {0x97};
char data0 [] = {0x25};
char data1 [] = {0xFF};
char data2 [] = {0x00};
char data3 [] = {0x64};
char data4 [] = {0x01};
char data5 [] = {0xFF};
char data6 [] = {0xFF};
char data7 [] = {0xFC};
char checksum [] ={0xE0};
for (;;) {
ioctl(fd, TIOCSBRK);
usleep(676); // 13 bits, 1 bit = 52us
ioctl(fd,TIOCCBRK);
usleep(260); // 5 bits
write(fd, SYNC, sizeof(SYNC));
write(fd, PID, sizeof(PID));
write(fd, data0, sizeof(data0));
write(fd, data1, sizeof(data1));
write(fd, data2, sizeof(data2));
write(fd, data3, sizeof(data3));
write(fd, data4, sizeof(data4));
write(fd, data5, sizeof(data5));
write(fd, data6, sizeof(data6));
write(fd, data7, sizeof(data7));
write(fd, checksum, sizeof(checksum));
usleep(10000);
close (fd); }
When I run both programs to check if the READ programs is working as it should, I can see that the data is read in, but is not exactly as it it's written to the port.
example of the data read in
d0: 0x7C
d1: 0x66
d2: 0x1
d3: 0xE0
d4: 0x4C
d5: 0x7C
d6: 0x8
d7: 0x60
CHK: 0x60
d0: 0x1
d1: 0xE0
d2: 0x4C
d3: 0x7C
d4: 0x8
d5: 0x60
d6: 0xFC
d7: 0x60
CHK: 060
I hope that somebody will be able to point where I have made a mistake and what should I do to be able to read from the UART port without the problem.
Problem that I have, is that when the program reads data and starts printing out the results, the information printed out is somehow correct just printed (or read in) in a wrong place.
Unfortunately, because your read program does output something, you mistakenly think there is only one problem relating to data data alignment. Data or message alignment is just one of many problems in your programs.
The read and write programs improperly initialize the serial port to an incomplete (and therefore unknown) state. As commented by #Swanand the write program prematurely closes its file descriptor.
The read program is needlessly using async I/O events to perform read() syscalls and not checking the return code. The read program then unconditionally prints out the buffer regardless of whether there is actual read data.
Using a break condition on the serial link is an unorthodox method of message separation. Since the message seems to be framed with a starting "sync" byte and a terminating "checksum" byte, this message frame should be validated by the read program to ensure that message alignment is intact.
Some of the specific errors in your code:
Lack of proper and consistent formatting.
void set_port_settings()
{
struct termios oldtio, newtio;
newtio is an automatic variable, and therefore is not initialized.
The program selectively assigns values to a few structure elements, and leaves others undefined.
These undefined elements in the termios structure passed on through the tcsetattr() syscall can lead to unpredictable and/or unreliable operation of the serial port.
You should study Setting Terminal Modes Properly and Serial Programming Guide for POSIX Operating Systems.
newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD ; perror("c_cflag");
newtio.c_iflag = IGNPAR | IXON ; perror("c_iflag");
This is an improper method of setting termios attributes.
Refer to the mentioned guides for proper technique.
You claimed you corrected these mistakes in this question, yet here they are still.
The unconditional perror() after each assignment is wrong since this isn't a syscall.
Enabling IXON seems irrelevant since this is the read program that isn't doing any output. (IXON enables soft flow-control for output.)
newtio.c_lflag = ICANON | ISIG ; perror("c_lflag");
Enabling ICANON seems incorrect since the data from the write program is not line-formatted text but binary data. This program should be setting up raw mode instead of canonical mode.
Enabling ISIG seems incorrect since the triggering character values in VINTR, VQUIT, VSUSP, or VDSUSP are all undefined in this uninitialized termios structure.
Since the write program is creating break conditions, a signal from a break, could be generated but has unwanted side effects, i.e. flushing the queues.
The unconditional perror() after each assignment is wrong since this isn't a syscall.
tcsetattr(fd, TCSANOW, &newtio); perror("tcsetattr");
The return code from this (and all other) system call needs to be checked.
The unconditional perror() call is wrong.
I have written the C program that should read UART's RxD port and display the results as soon as there is any information. To achieve this I'm using signal_handler SIGIO signal
Your program has no direct access to the UART's RxD port.
It's the serial port driver that reads the actual data from the UART RxD register.
Since you've setup the serial port for canonical input, the data is stored in the line discipline buffer, and then copied to a system buffer.
The read() syscall in your program retrieves data from the system buffer.
The SIGIO is not speeding up the read by your program.
void signal_handler_IO(int status)
{
if(flag !=1)
{
read(fd, &buff, sizeof(buff));
flag = 1;
}
}
This signal handler is not necessary if your program performed an ordinary blocking read(). Your premise for using nonblocking I/O and SIGIO seem to be incorrect. The program is not utilizing asynchronous I/O effectively, so you might as well simplify the program.
The return code from the read() needs to be checked. Besides checking for any error condition, the return code will indicate the number of bytes that have been read.
Your read program incorrectly assumes that a complete message of at least 11 bytes is received every time, and could cause display of stale values in the main loop.
Since the read program enables neither IGNBRK or BRKINT, a break condition is received by your program as a null byte, 0x00, along with the actual message data.
The read program needs to search for and maintain message alignment using the "sync" and "checksum" bytes. Your program currently has no method at all to test for or to achieve message alignment.
A basic algorithm for performing this are in this answer
If you read/write non ascii data to files you should open with 'O_BINARY'.
As next hint:
Use your console to read or write data to see which program is faulty. Simply use 'echo' for that.
To see what your program internally reads or writes use 'strace -xfo dump your_prog' and look into the file 'dump' to see which characters was send/read from your uart.
The following instructions seems to be really senseless for me! There is no chance to get a sleep exactly to any kind of bit rates until you have a hard real time kernel and hardware. On x86 you will have deviations of more then 100ms! for a usleep if an other process is running. Every disk io will kill your timing for example.
ioctl(fd, TIOCSBRK);
usleep(676); // 13 bits, 1 bit = 52us
ioctl(fd,TIOCCBRK);
usleep(260); // 5 bits
For sending a break use
tcsendbreak()
Related
I am using a BeagleBone Black to read data coming from a microcontroller(s) via a UART port(s). I need the reading of the UART port to be a blocking call. Additionally, for the usage of this software there will be some non-standard baud rates in use (aka not provided by termios). Additionally, the UART should follow 8-N-1 (8 data bits, no parity, 1 stop bit).
The code I have for the opening the UART port is as follows:
int UART::UART_open(unsigned int baudRate)
{
mFd = open(mPath.c_str(), O_RDWR | O_NOCTTY);
if(mFd < 0)
{
return -1;
}
struct termios2 tty;
if(ioctl(mFd, TCGETS2, &tty) == -1)
{
return -1;
}
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 5;
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_cflag &= ~CBAUD;
tty.c_cflag |= (BOTHER | CREAD | CLOCAL);
tty.c_ispeed = baudRate;
tty.c_ospeed = baudRate;
if(ioctl(mFd, TCSETS2, &tty) == -1)
{
return -1;
}
return 0;
}
The code I have for reading the UART port is as follows:
int UART::UART_read(unsigned char* buf, int length)
{
if(mFd < 0)
{
return -1;
}
if(read(mFd, buf, length) != length)
{
return -1;
}
return length;
}
There is some odd behavior going on. What happens is, the reading is inconsistent. Sometimes when I test it with an Mbed microcontroller sending data continuously (with small delays in between) via UART to the right port, and a test program to continuously read the UART port on the BeagleBone Black, and print out the data it gets, it works fine, and I am able to print out the data sent and everything works as expected. However, what happens often is the very first read simply blocks forever. No errors occur from the functions, the UART_read function simply hangs. So, to debug the error the first thing I do is I use 'screen' to monitor the /dev/ttyO* port I am trying to read from. What I find is that data is being sent to that port just fine. Then, the odd thing is, after I use screen, if I run my test program to continuously read the UART port, it works fine. This happens consistently too, if I do a quick 'screen' of the port when it is not working, I see data being sent, then my test program works. I have tried changing some of the opening termios2 struct options, to no avail. Any help would be appreciated!
Your termios initialization is obviously incomplete. There's a partial configuration for raw mode, e.g.
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 5;
yet you never actually enable noncanonical mode.
The "blocks forever" behavior of read() is symptomatic of a serial terminal (mis)configured for canonical mode when only binary data is received (i.e. there is no line terminator to "complete" the request).
Another possible cause of "blocks forever" behavior is failure to disable hardware flow-control when it is not used.
The insertion of the following statements would enable noncanonical mode and disable the HW handshake:
cfmakeraw(&tty);
tty.c_cflag &= ~CRTSCTS;
Refer to this answer for a complete example.
if(read(mFd, buf, length) != length)
{
return -1;
}
There is no requirement that a read() of a serial terminal will fill the buffer. Depending on the configuration, a "successful" read can return zero or more bytes up to the number requested.
Therefore a short read is not actually an error condition.
I am trying to do a serial communication application. I have a device which is running angstrom qt linux image. I need to write the serial code application for that device. Because the cross compiler is set for QT4.8.7, so it do not include the QSerialPort. So I am following this link for serial communication and I have also found many good examples on it.
Below is the code:
void MainWindow::SerialOpen(QString PORT)
{
fd = open(PORT.toStdString().c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyLP0\n");
exit(1);
}
saio.sa_handler = signal_handler_IO;
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,&termAttr);
cfsetispeed(&termAttr,B9600);
cfsetospeed(&termAttr,B9600);
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;
tcsetattr(fd,TCSANOW,&termAttr);
qDebug("Serial Port configured....\n");
}
and for reading I am using:
void signal_handler_IO (int status)
{
char buf [100];
int n = read (fd, buf, sizeof buf);
if(n > 0)
{
buf[n] = '\0';
printf("Receive OK %s\n",buf);
}
}
I am using event based serial read. I am wiriting it in QT creator. The problem I am facing is that whenever I am sending characters like A or B or any other single char. It receives it. But when I send the complete string, it breaks automatically. Have a look at the below image:
As you can see in the image, it receives char like p l h but then I sent hello world. It breaks it and first received hello and then world. I again sent the hello world it receives the he then llo then worl and then d. Why is this showing this behaviour. Please help. Thanks.
You are receiving your data in small chunks. Try running the read function in a while loop to append all chunks into one message like so:
#include <cstring> // needed for strcat.
#include <errno.h> // needed for strerror and errno
void signal_handler_IO (int status)
{
char buf [100];
char msg[1024];
int n;
int length = 0;
while((n = read (fd, buf, (sizeof buf)-1)) > 0)
{
buf[n] = '\0';
// you can add a check here to see if (length+n) < 1024
strcat(msg, buf);
length += n;
}
if(n<0)
{
//error handling
//EDIT: adding error handling
fprintf(stderr, "Error while receiving message: %s\n", strerror(errno));
return;
}
printf("Receive OK: '%s'\n", msg);
}
The signal handling needs to be turned off for the time of reading data so that the signal handler doesn't get called for each new chunk of data. After we're done reading the handler needs to be restored.
EDIT: Thanks to #MarkPlotnick for giving a better example on masking the signal instead of turning it off (could cause race condition). Add these two lines before calling sigaction to MainWindow::SerialOpen to mask the signal so that the handler doesn't get called again while the signal is being handled:
sigemptyset(&saio.sa_mask);
sigaddset(&saio.sa_mask, SIGIO);
The size of msg is just an example, you can set it to any value that will fit the message. Also, if you're using c++ then it would be better to use std::string instead of an array for msg.
Note the (sizeof buf)-1, if you read all characters there would be no room for the '\0' at the end of the array.
ANOTHER EDIT: After our chat conversation it turned out that read is blocking when there are no more characters to read (even though open was called with O_NDELAY flag for non-blocking reading). To fix this issue one can first check how many bytes are available for reading:
while(true)
{
size_t bytes_avail;
ioctl(fd, FIONREAD, &bytes_avail);
if(bytes_avail == 0)
break;
int n = read (fd, buf, (sizeof buf)-1);
if(n<0)
{
//error handling
fprintf(stderr, "Error while receiving message: %s\n", strerror(errno));
return;
}
buf[n] = '\0';
// you can add a check here to see if (length+n) < 1024
strcat(msg, buf);
length += n;
}
QT4.8.7, so it do not include the QSerialPort
You can build QSerialPort from sources and to use it. Please read Wiki: https://wiki.qt.io/Qt_Serial_Port
I am having an issue reading from a serial port on a Beaglebone Black using a C++ script I've written. The script sends commands to and receives responses from an Adafruit FONA GSM/GPS device and works, except that there is a long delay between sending the commands and actually receiving any bytes back from the device (I have to put a 1 sec delay between the write and read commands in order to get the response from the device). However, when I use the minicom serial terminal emulator there is no perceptible delay between sending the command and receiving the response. I'm guessing it has to do with how I am opening the serial port, but I don't know what else I can change. Right now I have the settings set to raw input and output modes with no line control or echoing, but still unable to reduce the response time. Any help or ideas are more than welcome and appreciated! Thanks!
CPP file:
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <linux/serial.h>
#include <termios.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include "Fona_control.h"
Fona_control::Fona_control(void)
{
begin();
}
void Fona_control::get_gps(void)
{
printf("Reading GPS\n");
unsigned char gpsbuff[250];
memset(gpsbuff,'\0',250);
sleep(0.1);
int bytes_a = 0;
int n_write = write(fona_fd,GPS_GET_DATA,sizeof(GPS_GET_DATA)-1);
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_a);
printf("Bytes avail: %i\n",bytes_a);
int n_read = read(fona_fd,gpsbuff,bytes_a);
printf("Buffer: %s\n",gpsbuff);
printf("Bytes read: %i\n",n_read);
return;
}
void Fona_control::begin(void)
{
printf("FONA Beginning\n");
struct termios oldtio, newtio;
struct serial_struct serinfo;
// Load the pin configuration
/* 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. */
fona_fd = open(FONA_DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
if (fona_fd < 0) { perror(FONA_DEVICE); exit(-1); }
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 */
cfsetspeed(&newtio,BAUDRATE_Fona);
newtio.c_cflag |= ( CLOCAL | CREAD );
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8;
newtio.c_cflag &= ~PARENB;
newtio.c_cflag &= ~CRTSCTS;
newtio.c_cflag &= ~CSTOPB;
newtio.c_cc[VMIN] = 1;
newtio.c_cc[VTIME] = 1;
ioctl (fona_fd, TIOCGSERIAL, &serinfo);
serinfo.flags |= 0x4000;
ioctl (fona_fd, TIOCSSERIAL, &serinfo);
/* setup for non-canonical mode */
//newtio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | ISTRIP | INLCR | INPCK | ICRNL | IXON | IGNCR);
newtio.c_iflag = 0;
/* Set line flags */
//newtio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
newtio.c_lflag = 0;
/* Raw output
newtio.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | ONOEOT| OFILL | OLCUC | OPOST); */
newtio.c_oflag = 0;
/* now clean the modem line and activate the settings for the port */
tcflush(fona_fd, TCIOFLUSH);
if(tcsetattr(fona_fd,TCSANOW,&newtio) < 0)
{
printf("Error Setting FONA Serial Port Attributes!");
exit(0);
};
/* terminal settings done, now send FONA initialization commands*/
sleep(1);
unsigned char buffer[50];
int bytes_avail = 0;
int n_write = 0;
int n_read = 0;
int cnt = 0;
memset(buffer,'\0',50);
tcflush(fona_fd, TCIOFLUSH);
while(strstr((char *)buffer,"OK") == NULL && cnt < 5)
{
memset(buffer,'\0',50);
n_write = write(fona_fd,FONA_AT,sizeof(FONA_AT)-1);
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_avail);
printf("BA: %i\n",bytes_avail);
if(bytes_avail > 0)
{
n_read = read(fona_fd,buffer,bytes_avail);
printf("%s\n",buffer);
}
sleep(1);
cnt++;
}
sleep(1);
n_write = write(fona_fd,"+++",3);
bytes_avail = 0;
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_avail);
printf("BA2: %i\n",bytes_avail);
n_read = read(fona_fd,buffer,bytes_avail);
printf("%s",buffer);
printf("AT Accepted\n");
sleep(1);
tcflush(fona_fd, TCIOFLUSH);
unsigned char buffer1[50];
memset(buffer1,'\0',50);
int n = write(fona_fd,FONA_ECHO_OFF,sizeof(FONA_ECHO_OFF)-1);
printf("Writ: %i\n",n);
bytes_avail = 0;
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_avail);
printf("BA2: %i\n",bytes_avail);
n = read(fona_fd,buffer1,bytes_avail);
printf("%s",buffer1);
memset(buffer1,'\0',50);
sleep(1);
n = write(fona_fd,GPS_POWER_ON,sizeof(GPS_POWER_ON)-1);
printf("Writ: %i\n",n);
bytes_avail = 0;
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_avail);
printf("BA2: %i\n",bytes_avail);
n = read(fona_fd,buffer1,bytes_avail);
printf("%s\n",buffer1);
memset(buffer1,'\0',50);
sleep(1);
n = write(fona_fd,FONA_SMS_TYPE,sizeof(FONA_SMS_TYPE)-1);
printf("Writ: %i\n",n);
bytes_avail = 0;
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_avail);
printf("BA2: %i\n",bytes_avail);
n = read(fona_fd,buffer1,bytes_avail);
printf("%s\n",buffer1);
sleep(1);
}
H File:
#ifndef _fona_control_H
#define _fona_control_H
#include <stdint.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#define FONA_DEVICE "/dev/ttyO5" //Beaglebone Black serial port
//#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define BAUDRATE_Fona B115200 // Change as needed, keep B
/* Define FONA AT Commands */
#define FONA_AT "AT\r\n"
#define FONA_ECHO_OFF "ATE0\r\n"
#define FONA_CMD_REPEAT "A/\r\n"
#define FONA_NO_ECHO "ATE0\r\n"
#define FONA_PIN_CHECK "AT+CPIN?\r\n"
#define FONA_PIN_SEND "AT+CPIN=1234\r\n"
#define FONA_SMS_TYPE "AT+CMGF=1\r\n"
/* Define FONA GPS AT Commands */
#define GPS_POWER_ON "AT+CGNSPWR=1\r\n"
#define GPS_POWER_OFF "AT+CGNSPWR=0\r\n"
#define GPS_GET_DATA "AT+CGNSINF\r\n"
/* Define FONA GPS NMEA Commands */
#define PMTK_CMD_HOT_START "AT+CGNSCMD=0,"$PMTK101*32"\r\n"
#define PMTK_CMD_WARM_START "AT+CGNSCMD=0,"$PMTK102*31"\r\n"
#define PMTK_CMD_COLD_START "AT+CGNSCMD=0,"$PMTK103*30"\r\n"
#define PMTK_SET_NMEA_5HZ "AT+CGNSCMD=0,"$PMTK220,200*2C"\r\n"
#define PMTK_SET_BAUD_38400 "AT+CGNSCMD=0,"$PMTK251,38400*27"\r\n"
#define PMTK_SET_WAAS "AT+CGNSCMD=0,"$PMTK301,2*2E"\r\n"
#define PMTK_SET_SBAS "AT+CGNSCMD=0,"$PMTK313,1*2E"\r\n"
#define PMTK_NMEA_TYPES "AT+CGNSCMD=0,"$PMTK314,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0*29"\r\n"
#define PMTK_STANDY_MODE "AT+CGNSCMD=0,"$PMTK161,0*28"\r\n" //Send any byte to exit standby mode
class Fona_control {
public:
void begin(void);
void get_gps(void);
Fona_control(void); // Constructor when using HardwareSerial
uint8_t fix_status, fix_mode, sats, sats_used, glo_sats, cn0;
uint8_t month, day, minute;
uint32_t year;
double seconds, latitude, longitude, speed, course, hdop, vdop, pdop, hpa, vpa;
int fona_fd;
private:
};
#endif
1 second sleeps are murderous and I can assure you minicom is not doing them. It will be waiting for data to come in, rather than polling, then displaying it. Serial data is slow. Every character sent at 9600 baud takes about a millisecond to arrive. at 115.2k baud, you're moving a character in a little less than 85 microseconds. Your Beaglebone, on the other hand, is working in nanoseconds, so if you don't wait before reading, the data won't be there yet. That said, you should have that "OK" in less than 1 ms and waiting a whole second is vast overkill.
Consider blocking and waiting for the three bytes needed for "OK"
while(strstr((char *)buffer,"OK") == NULL && cnt < 5)
{
memset(buffer,'\0',50);
n_write = write(fona_fd,FONA_AT,sizeof(FONA_AT)-1);
n_read = read(fona_fd,buffer,3);
cnt++;
}
This can block forever if the device never responds so you need a timeout
The easiest catch-all timeout mechanism I can think of off the top of my head is something like this:
int retry= 5;
while (retry)
{
fd_set readfs; // file descriptor set used by select
struct timeval timeout;
FD_ZERO(&readfs); // clear file descriptor set
FD_SET(fona_fd, &readfs); // set our port as the one item in the set
timeout.tv_sec = 1;
timeout.tv_usec = 0;
if (select(fona_fd+1, &readfs, NULL, NULL, &timeout) !=0)
{
rval = read(fona_fd, buffer, sizeof(buffer)-1);
// will stop reading after configurable gap between bytes read
buffer[rval] = '\0'; // NULL terminate the buffer or it ain't a string
// If not a string, results of strstr are undefined
if (strstr((char *)buffer,"OK") != NULL)
{
break;
}
}
retry--;
}
Documentation on select. It's very very cool function. Not as cool as epoll, but easier to find tutorials for. If you do a lot of select give epoll a good look over.
Select in this case will wait for data for 1 second before giving up. If it finds data, the program will enter the if body and read will read until some user configurable number of character-times have passed since the last character was received or the buffer is full.
We then null terminate the data in the buffer so we can use string handling routines on it. This also eliminates the need to memset the buffer. If the buffer contains "OK", we exit the loop.
Otherwise the we decrement the retry counter and loop around to see if there are more retries.
Hello I need advice of how correctly to change the baud rate of the UART port in the middle of the program.
To establish communication with my desired slave I need to send the so called "break field" which consists of 13-26 (MIN 11) LOW BITS, on UART as I think it's not achievable as before and after every 8 bits UART adds start & stop bit. I had an idea of achieving it by manipulating the baud-rates send break field at 9600Bauds followed by the 19200Baud speed.
But I'm unable to see difference between baudrates on the oscilloscope. So I suppose I do it in the wrong way.
#include <iostream>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <time.h>
#include <stdlib.h>
#define BAUDRATE9600 B9600
#define BAUDRATE19200 B19200
#define PORT "/dev/ttyO4"
#define _POSIX_SOURCE 1
main()
{
int fd, c;
struct termios oldtio, newtio, midtio;
fd = open(PORT, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd <0) { perror(PORT); exit(-1); }
tcgetattr(fd, &oldtio); /* save current port settings */
/* set new port settings for canonical input processing */
midtio.c_cflag = BAUDRATE9600 | CS8 | CLOCAL | CSTOPB ;
midtio.c_iflag = IGNPAR | ICRNL;
midtio.c_oflag = 0;
midtio.c_lflag = ICANON;
newtio.c_cflag = BAUDRATE19200 | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ICANON;
tcsetattr(fd, TCSANOW, &midtio);
char break[] = {0x00}; // break field (0000 0000)
c = write (fd, break, sizeof(break));
tcsetattr(fd, TCSANOW, &newtio);
char sync [] = {0x55};
char PID [] = {0xE2};
char data [] = {0xAA};
char data1 [] = {0xA5};
char check [] = {0xAF};
c = write (fd, sync, sizeof(sync));
c = write (fd, PID, sizeof(PID));
c = write (fd, data, sizeof(data));
c = write (fd, data1, sizeof(data1));
c = write (fd, check, sizeof(check));
/* restore old port settings */
tcsetattr(fd,TCSANOW,&oldtio);
}
I have tried it also by setting baudrates in different way as well:
cfsetispeed(&midtio, B9600)
cfset0speed(&midtio, B9600)
cfsetispeed(&newtio, B19200)
cfset0speed(&newtio, B19200)
I need to send the so called "break field"
If the goal is to send a "break", then you've asked an XY question with "How to change the baudrate in c/c++". You cannot generate a break condition by manipulating the baud rate. You would need a baud rate of less than 32 baud to achieve a quarter-second break.
A break condition can be sent on the serial link by using the TCSBRK command in an ioctl().
BTW your serial port initialization code does not conform to best programming practices. You should always be testing the return codes from system calls. The elements of the termios structure should not be directly assigned, but rather just the individual bits should be modified. Refer to a guide such as this.
I bought board like this now i want to connect temperature sensor to this board.
How to read temperature from sensor in c or c++?
I tried to write some code but it won't work. I connect DS18B20 data cabel directly to TXD and RXD pins.
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <cstring>
#include <inttypes.h>
#include <errno.h>
int
set_interface_attribs (int fd, int speed, int parity)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
std::cout<<"error "<<errno<<" from tcgetattr";
return -1;
}
cfsetospeed (&tty, speed);
cfsetispeed (&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // ignore break signal
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
std::cout<<"error "<<errno<<" from tcsetattr";
return -1;
}
return 0;
}
void
set_blocking (int fd, int should_block)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
std::cout<<"error "<<errno<<" from tggetattr";
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
std::cout<<"error "<<errno<<" setting term attributes";
}
int main()
{
char *portname = "/dev/ttyUSB0";
int tty_fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (tty_fd < 0)
{
std::cout<<"error "<<errno<<" opening "<<portname<<": "<< strerror (errno);
return -1;
}
set_interface_attribs (tty_fd, B9600, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (tty_fd, true);
unsigned char c = 0xCC;
if(!write(tty_fd, &c, sizeof(c)))
std::cout<<"Write error";
sleep(2);
unsigned char buffer[8];
int size;
if((size = read(tty_fd, &buffer, 8)) < 0)
std::cout<<"Error";
else
std::cout<<"CC("<<size<<")='"<<buffer<<"'";
std::cout<<"\n";
c = 0x44;
if(!write(tty_fd, &c, sizeof(c)))
std::cout<<"Write error2";
c = 0xBE;
if(!write(tty_fd, &c, sizeof(c)))
std::cout<<"Write error2";
sleep(2);
if((size = read(tty_fd, &buffer, 8)) < 0)
std::cout<<"Error";
else
std::cout<<"BE("<<size<<")='"<<buffer<<"'";
std::cout<<"\n######################\n";
close(tty_fd);
}
I got:
CC(1)='Č#'
BE(2)='#ž#'
######################
CC(1)='Č#'
BE(2)='#ž#'
######################
CC(1)='Č#'
BE(2)='#ž#'
######################
Can you help me?
You can't do this with any software. The DS18B20 is electrically incompatible with the board you have. The sensor uses a 1-wire, open-collector communication scheme that is completely different from the serial protocol normally used with this board. With great difficulty you might be able to do some bit-banging of the RTS/CTS signals but you need circuitry to combine them into a bidirectional open-collector signal.
You can hack the UART to communicate with 1 wire protocol.
Connect Rx to Tx and add 4.7 pull-up resistor
See the application note from maxim:
http://www.maximintegrated.com/en/app-notes/index.mvp/id/214
As pointed out by user3804701, it's indeed possible to communicate with a 1-Wire device using a UART interface, and the application note at https://www.maximintegrated.com/en/app-notes/index.mvp/id/214 contains all the information needed to make it work.
But the OP's code needs several fixes:
Each transaction with DS18B20 is made of 3 steps: initialization (also called reset), ROM command and function command, optionally followed by a data exchange
The initialization or reset step is performed by configuring the UART with 9600 bps baud rate, transmitting 0x0F, and receiving a dummy byte; then, the baud rate must be set to 115200 bps to perform the next steps
Following the reset step, data is sent to DS18B20 by writing to the UART a 0xFF byte for each data bit set to 1, and a 0x00 byte for each bit set to 0, starting from the least significant bit; for example, to send 0xAB (i.e. 10101011), one would write to the UART the sequence (FF FF 00 FF 00 FF 00 FF); for each byte written to the UART, there is a "return byte" that needs to be read from the UART and discarded
Data is received from DS18B20 by sending the 0xFF byte following the rules in the previous bullet point, but instead of discarding the "return bytes" reading from the UART a byte for each data bit, starting from the least significant bit: a 0xFF value means that the bit value is 1, otherwise the bit value is 0; for example, the sequence (00 FF FF 00 FF 00 00 FF) read from the UART means that DS18B20 sent 0x96 (i.e. 10010110)
If there is just one DS18B20 connected to the 1-Wire bus, all transactions can use "skip ROM" (byte value 0xCC) as ROM command
The first transaction to be performed is the one that triggers a temperature conversion from DS18B20, with function command 0x44
After waiting for the conversion to complete (which can take up to 750 ms), the host can perform a second transaction to read the DS18B20 scratchpad memory (function command 0xBE); the scratchpad memory is 9 bytes long and contains among other things the temperature value
So in summary the steps needed to get a temperature sample from DS18B20 are: reset, write 0xCC, write 0x44, wait for conversion, reset, write 0xCC, write 0xBE, read 9 bytes.
Example code that implements this stuff is available at https://github.com/dword1511/onewire-over-uart.