Creating custom service on Linux to communicate with modem over serial UART - c

I have written a program in C to communicate with a 3G modem connected to my raspberry pi over serial uart. After it's open, I do not break the connection unless the program crashes. It runs fine when I run it myself, but when I create a service to run it, it fails right after it starts.
My main question, is this an issue with my program, or is this an issue with how I configured my linux service to run? And what is the best practices to fix this.
my service file
[Unit]
Description=Firmware for sensor IOT module
Documentation=m.thompson.239#gmail.com
After=network-online.target
[Service]
Type=simple
ExecStart=/hdd/firmware
StandardOutput=syslog
RestartSec=30
Restart=always
[Install]
WantedBy=multi-user.target
Alias=firmware.service
When I check the status of the service
Loaded: loaded (/lib/systemd/system/firmware.service; enabled; vendor preset:
Active: activating (auto-restart) (Result: signal) since Thu 2018-07-12 14:44
Process: 539 ExecStart=/hdd/firmware (code=killed, signal=SEGV)
Main PID: 539 (code=killed, signal=SEGV)
Jul 12 14:44:34 raspberrypi systemd[1]: firmware.service: Failed with result 'si
lines 1-7/7 (END)...skipping...
● firmware.service - Firmware for sensor IOT module
Loaded: loaded (/lib/systemd/system/firmware.service; enabled; vendor preset: enabled)
Active: activating (auto-restart) (Result: signal) since Thu 2018-07-12 14:44:34 UTC; 28s ago
Process: 539 ExecStart=/hdd/firmware (code=killed, signal=SEGV)
Main PID: 539 (code=killed, signal=SEGV)
Jul 12 14:44:34 raspberrypi systemd[1]: firmware.service: Failed with result 'signal'.
These are my setting when I open the serial port.
int set_interface_attribs(int fd, int speed)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", 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) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}

Related

Data from serial port only displayed when cable is unplugged

I am trying to read data from serial port sent by RS232 module using :
A USB - SERIAL converter
A C program and the lib TERMIOS (Which worked perfectly ; I can read and send data in real time).
A Linux environment
Now, I'm trying to do the exact same thing but using directly the serial port of my device and not the USB port.
With the same configuration (except I change the device from tty USB0 to tty mxc2 and using a RS232 cable only not the USB - SERIAL converter). I can't get the data but when I unplug the RS232 cable, all the data I try to read is received and printed to the terminal.
I also tried to use stty directly to read (thinking it was a C problem) but I get the same result (I unplug the RS232 cable & the data is read)
I can not explain why there is a delta between using USB - RS232 converter and using only RS232 cable.
//configuration of TERMIOS :
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 | CRTSCTS); /* only need 1 stop bit & 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;
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
//trying to read with termios :
retlen = read(fd,&tmp_buff,1);
if (retlen > 0)
printf("%02x\n",tmp_buff);
here are some result and output from terminal that may help :
When the cable is plugged the data are not displayed
root#2021ufv6zgzg:~# ./main.exe
When the cable is unplugged the data are displayed
root#2021ufv6zgzg:~# ./main.exe
00
fe
00
fe
00
fe
00
fe
Same goes with only stty, data displayed when I unplug the cable :
od -x < /dev/ttymxc2
0003260 fe00 fe00 fe00 fe00 fe00 fe00 fe00 fe00
Thank you very much for your help. Even though you can not find the solution, i'd really like to know the differences between using tty USB0 and tty mxc2.

Sending AT commands to Telit modem with C

I am trying to send AT commands to a Telit modem from C. Simply interfacing with the modem through system() calls works fine, but there are some issues that arise when I try to transfer a python script with AT#WSCRIPT. The ">>>" portion also gets transferred into the chip, corrupting the script.
Anyway, so I am now trying to do it within C using termios library. The problem is, I can only send the chip one "AT" command, it responds with an OK and every attempt to send another AT commands results in an ERROR response. Further, after runnning my C code, I can't interface with the chip even through the terminal.
I suspect something is off about the termios configuration, which is listed below.
Any ideas? Thanks
char command[] = "AT\r\n";
fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY);
termios_config.c_cflag &= ~PARENB;
termios_config.c_cflag &= ~CSTOPB;
termios_config.c_cflag &= ~CSIZE;
termios_config.c_cflag |= CS8;
termios_config.c_cflag &= ~CRTSCTS;
termios_config.c_cflag |= CREAD | CLOCAL;
termios_config.c_cflag &= ~(IXON | IXOFF | IXANY);
termios_config.c_cflag &= ~(ICANON | ECHO | ECHOE | ISIG);
termios_config.c_cflag &= ~OPOST;
tcsetattr(fd, TCANOW, &termios_config);
write(fd, send_str, strlen(send_str));

Linux Reading Data from UART

I want to read data from UART, i followed this tutorial, the write function works as expected, however i'am getting problem with the read function :
This is the uart_init function:
void uart_init()
{
printf("\n +----------------------------------+");
printf("\n | Serial Port Write |");
printf("\n +----------------------------------+");
/*------------------------------- Opening the Serial Port -------------------------------*/
fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY| O_SYNC); /* !!blocks the read */
/* O_RDWR Read/Write access to serial port */
/* O_NOCTTY - No terminal will control the process */
/* O_NDELAY -Non Blocking Mode,Does not care about- */
/* -the status of DCD line,Open() returns immediatly */
if(fd == -1) /* Error Checking */
printf("\n Error! in Opening ttyUSB0 ");
else
printf("\n ttyUSB0 Opened Successfully ");
/*---------- Setting the Attributes of the serial port using termios structure --------- */
struct termios SerialPortSettings; /* Create the structure */
tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */
cfsetispeed(&SerialPortSettings,B19200); /* Set Read Speed as 19200 */
cfsetospeed(&SerialPortSettings,B19200); /* Set Write Speed as 19200 */
SerialPortSettings.c_cflag &= ~PARENB; /* Disables the Parity Enable bit(PARENB),So No Parity */
SerialPortSettings.c_cflag &= ~CSTOPB; /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
SerialPortSettings.c_cflag &= ~CSIZE; /* Clears the mask for setting the data size */
SerialPortSettings.c_cflag |= CS8; /* Set the data bits = 8 */
SerialPortSettings.c_cflag &= ~CRTSCTS; /* No Hardware flow Control */
SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines */
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable XON/XOFF flow control both i/p and o/p */
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Non Cannonical mode */
SerialPortSettings.c_oflag &= ~OPOST;/*No Output Processing*/
/* Setting Time outs */
SerialPortSettings.c_cc[VMIN] = 10; /* Read at least 10 characters */
SerialPortSettings.c_cc[VTIME] = 0; /* Wait indefinetly */
if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) /* Set the attributes to the termios structure*/
printf("\n ERROR ! in Setting attributes");
else
printf("\n BaudRate = 19200 \n StopBits = 1 \n Parity = none");
}
the receive function :
void uart_receive()
{
char read_buffer[32]; /* Buffer to store the data received */
int bytes_read = 0; /* Number of bytes read by the read() system call */
int i = 0;
bytes_read = read(fd,&read_buffer,10); /* Read the data */
printf("\n\n Bytes Rxed %d", bytes_read); /* Print the number of bytes read */
printf("\n\n ");
for(i=0;i<bytes_read;i++) /*printing only the received characters*/
printf("%c",read_buffer[i]);
printf("\n +----------------------------------+\n\n\n");
}
the main function :
void main(void)
{
uart_init();
/*------------------------------- Write data to serial port -----------------------------*/
//uart_write_commande(write_buffer); //Write function works well
uart_receive();
close(fd);/* Close the Serial port */
}
I execute the program and wait for data bytes to be received in UART, i send data using UART but the read function keeps blocked.
I'am using a Virtual machine with Ubunutu 14.04 on it, and i'am not sure that using an emulated UART can cause problems during reception.
Your program is hanging in the read() syscall because it is blocked waiting for a line-termination character.
You tried to configure the port for non-canonical mode with the statement
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Non Cannonical mode
but that operation is on the wrong termios element.
The ICANON attribute is part of the lflag element (and not the iflag). (This error originates from the tutorial you referenced!)
Therefore your program is performing blocking canonical reads.
There's a convenient termios function for configuring non-canonical mode:
cfmakeraw() sets the terminal to something like the "raw" mode of the old
Version 7 terminal driver: input is available character by
character, echoing is disabled, and all special processing of
terminal input and output characters is disabled. The terminal
attributes are set as follows:
termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios_p->c_cflag &= ~(CSIZE | PARENB);
termios_p->c_cflag |= CS8;
There's an error in the read function
bytes_read = read(fd,&read_buffer,10); /* Read the data
should be
bytes_read = read(fd,read_buffer,10); /* Read the data
Your read() function may be blocked, for whatever reason. Here is a discussion on reading from a serial port, including code for blocked/unblocked settings.
It may also be possible that there is no data being transferred, leading to nothing being read. Without having access to the hardware setup, it is difficult to go further without very specific information about what you are seeing.
Also, in addition to passing the read_buffer correctly (another answer), there are at least two additional things that may improve:
1) check the return of read before using it:
bytes_read = read(fd,&read_buffer,10); /* Read the data*/
if(bytes_read > 0)
{
...
}
2) Change:
for(i=0;i<bytes_read;i++) /*printing only the received characters*/
printf("%c",read_buffer[i]);
To:
//after successful read:
read_buffer[bytes_read]=0;//place null termination after last character read.
printf("%s",read_buffer);//note format specifier
This will print the number of characters read, without the loop.

Program hangs when setting serial port parameters

In my program a connection is opened with serial port that recieves 10-byte packages with set frequency (usually 100 Hz). However i have run into the following problem: sometimes (but not always) when i am closing connection, the program hangs.
In the following code i do not specify constants; at runtime connection parameters (baud rate, parity...) are determined from command line. In practice, connection is usually opened with baud rate 230400, no parity, 8-bit character size and 1 stop bit.
This is how i am opening serial port:
struct termios config;
LOGPRINT("Will operate on device \'%s\'\n", pconfig->device);
LOGPRINT("Opening serial device\n");
int flag = O_RDWR | O_NOCTTY | ASYNC_LOW_LATENCY;
// pconfig contains some necessary information to set connection parameters
// async == false for connectin that causes problems
int fd = open(pconfig->device, async? flag | O_NONBLOCK: flag);
if(fd == -1)
{ /* skipped error handling */ }
LOGPRINT("Retrieving terminal attributes\n");
if(!!tcgetattr(fd, &config))
{ /* skipped error handling */ }
else
{
// backup old configuration for restoring it on exit
if(poldconfig != NULL)
memcpy(poldconfig, &config, sizeof(struct termios));
LOGPRINT("Setting terminal attributes\n");
set_attribs(fd, &config, pconfig);
}
...
int set_attribs(const int fd, struct termios* pterm, const struct serial_config* pconfig)
{
// set baud rate
// baud_rates contains integers of actual rates
LOGPRINT("Setting baud rate to %d.\n", baud_rates[pconfig->baud_rate_index]);
// change to raw mode
LOGPRINT("Setting terminal to raw mode\n");
pterm->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
| IGNCR | ICRNL | IXON);
pterm->c_oflag &= OPOST;
pterm->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
// baud_bits contains actual pre-defined baud rate flags
cfsetispeed(pterm, baud_bits[pconfig->baud_rate_index]);
cfsetospeed(pterm, baud_bits[pconfig->baud_rate_index]);
LOGPRINT("Set character size, parity and stop bit flags.\n");
// set character size
pterm->c_cflag &= ~CSIZE;
pterm->c_cflag |= pconfig->csize_flag;
// set parity
pterm->c_cflag &= ~(PARENB | PARODD);
pterm->c_cflag |= pconfig->parity_flag;
// set stopbits
pterm->c_cflag &= ~CSTOPB;
pterm->c_cflag |= pconfig->stopbits_flag;
// enable reading; ignore control lines
pterm->c_cflag |= CREAD | CLOCAL;
LOGPRINT("Flush terminal.\n");
// flush terminal
tcflush(fd, TCIOFLUSH);
LOGPRINT("Apply parameters.\n");
return tcsetattr(fd, TCSANOW, pterm);
}
For valid reasons, i needed to force serial device driver to use 1-byte buffer for incoming/outgoing data, which i have set using this function:
int set_port_type(int fd, int ptype)
{
struct serial_struct temp;
int res = ioctl(fd, TIOCGSERIAL, &temp);
if(!!res)
{ /* stipped error handling */ }
temp.type = ptype;
res = ioctl(fd, TIOCSSERIAL, &temp);
if(!!res)
{ /* stipped error handling */ }
return res;
}
with ptype = PORT_UNKNOWN or ptype = PORT_16450.
When finished with serial port, i perform the following operations:
Flush it
Set its type back to 16550A
Restore its settigns to saved ones
Close it
From time to time it causes the program to hang and stop responding to any external stimulus. It forces me to suspend the program and then reboot system, as it never releases file descriptors it holds.
What can i do to ensure that does not happen? Even if settings are not restored, the program needs to close on its own - maybe with error code.
Operating system i am using is TinyCore Linux. No other user program is being run an the same time as my program.
IMportant clarification. Serial device i am working with is actually NPort 5232 - asyncronous RS-422/485 - Ethernet communicator (description from vendor. Consequently, serial devices i am working with are virtual, which enables me to try and change their type / size of internal buffer.

Linux - serial port read returning EAGAIN

I am having some trouble reading some data from a serial port I opened the following way. I've used this instance of code plenty of times and all worked fine, but now, for some reason that I cant figure out, I am completely unable to read anything from the serial port.
I am able to write and all is correctly received on the other end, but the replies (which are correctly sent) are never received (No, the cables are all ok ;) )
The code I used to open the serial port is the following:
fd = open("/dev/ttyUSB0", O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd == -1)
{
Aviso("Unable to open port");
return (fd);
}
else
{
//Get the current options for the port...
bzero(&options, sizeof(options)); /* clear struct for new port settings */
tcgetattr(fd, &options);
/*-- Set baud rate -------------------------------------------------------*/
if (cfsetispeed(&options, SerialBaudInterp(BaudRate))==-1)
perror("On cfsetispeed:");
if (cfsetospeed(&options, SerialBaudInterp(BaudRate))==-1)
perror("On cfsetospeed:");
//Enable the receiver and set local mode...
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB; /* Parity disabled */
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag |= SerialDataBitsInterp(8); /* CS8 - Selects 8 data bits */
options.c_cflag &= ~CRTSCTS; // disable hardware flow control
options.c_iflag &= ~(IXON | IXOFF | IXANY); // disable XON XOFF (for transmit and receive)
options.c_cflag |= CRTSCTS; /* enable hardware flow control */
options.c_cc[VMIN] = 0; //min carachters to be read
options.c_cc[VTIME] = 0; //Time to wait for data (tenths of seconds)
//Set the new options for the port...
tcflush(fd, TCIFLUSH);
if (tcsetattr(fd, TCSANOW, &options)==-1)
{
perror("On tcsetattr:");
}
PortOpen[ComPort] = fd;
}
return PortOpen[ComPort];
After the port is initializeed I write some stuff to it through simple write command...
int nc = write(hCom, txchar, n);
where hCom is the file descriptor (and it's ok), and (as I said) this works. But... when I do a read afterwards, I get a "Resource Temporarily Unavailable" error from errno.
I tested select to see when the file descriptor had something t read... but it always times out!
I read data like this:
ret = read(hCom, rxchar, n);
and I always get an EAGAIN and I have no idea why.
Update:
The HW is working fine! I can see that there is inbound data on the serial port because I've made a debug cable to read whats going on on another terminal. So...
I know what nonblocking should do. My question is... why isn't anything getting read!. The same setup works fine on windows, so all hardware is working fine...
This is driving me nuts! I'm sure it's something simple as hell! I even tried getting rid of O_NONBLOCK to see when I would receive something... but nothing...
Read this.
EAGAIN Non-blocking I/O has been
selected using O_NONBLOCK and no data
was immediately available for reading.
You need to check the serial terminal settings first.
use command - stty -F /dev/ttyUSB0 -a
Check that ctsrts is selected as -ctsrts
and do the other required settings with stty utility and you are done.
EAGAIN with O_NONBLOCK means there's been no data received on the port. Check that the port and cable are working properly (using minicom or some other known-good program), and that the remote really is sending some data.
see my code samples, if EAGAIN, you'd try to read again:
...
options.c_cflag &= ~PARENB;
options.c_iflag &= ~INPCK;
...
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // input
options.c_oflag &= ~OPOST; // output
...
fd = open("/dev/ttyUSB0", O_RDWR | O_NDELY | O_NOCTTY);
fcntl(fd, F_SETFL, 0);
...
int nc = write(hCom, txchar, n);
msleep(500); // wait 500ms
fcntl(hCom, F_SETFL, FNDELAY); // don't block serial read
ret = read(hCom, rxchar, n);
if (ret > 0) {
here had read n bytes or just partial data, read again if partial.
}
if (ret < 0) {
if (EAGAIN == errno) {
not a real error, just read again.
} else {
oops, errors.
}
}
...

Resources