I'm trying to write a program for serial communication on TRB246 from Teltonika (BusyBox v1.34.1).
Incoming data is raw, in hex bytes, coming every seconds.
Three solutions:
- Use
microcomin a shell script (swift option, should be sufficient) - Write my own program from scratch
- Try to customize
microcom
Use microcom: a fail
Script:
#!/bin/sh
ask_dataA='\xFF\x12\xB0\x80\x28\x00\x01\x01\x00\xEA\x00\x00\x00\x00\x77\xA8\x04\xFF'
echo "Asking data A..."
echo -en $ask_dataA > /dev/rs485 # send query
nice -20 microcom -t 1100 -s 9600 /dev/rs485 > tmpsf # listen
if [ -s tmpsf ]; then # checks if file is empty
hexdump -ve '/1 "%02x"' -n 36 tmpsf # convert to ascii
echo ""
else
echo "nothing"
fi
Output: enter image description here
Sometimes if gives the hex answer as expected.
I am spying the communication using Docklight, every messages travel correctly and on time. It is just the TRB246 that does not catch the answer.
I think I come too late with microcom, since answer comes a few tens of milliseconds after the echo.
Write my own program from scratch: a fail (read() is not working properly), the code:
#include <fcntl.h>
#include <termios.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#define RS485_DEV "/dev/ttyS1"
#define RS485_BAUDRATE B9600
#define RS485_TIMEOUT 0 // in tenths of sec; or use poll() https://man7.org/linux/man-pages/man2/poll.2.html
#define RS485_MINBRD 16 // min bytes to read see http://manpagesfr.free.fr/man/man3/termios.3.html
#define RS485_IBUFFSIZE 32
int main()
{
printf("Hello guys!\n");
uint8_t ibuff[RS485_IBUFFSIZE];
int fd = open(RS485_DEV, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) { printf("Failed to open rs485 port: %s\n", strerror(errno)); exit(1); }
struct termios term_conf;
struct termios new_term_conf;
if (tcgetattr(fd, &term_conf) == -1) { printf("Failed to get term config: %s\n", strerror(errno)); } // get current settings
printf("Former terminal configuration:\n");
printf("term_conf:\n\tc_iflag = %u\n\tc_oflag = %u\n\tc_cflag = %u\n\tc_lflag = %u\n\n",
term_conf.c_iflag, term_conf.c_oflag, term_conf.c_cflag, term_conf.c_lflag);
for (int i = 0; i < NCCS; i++) { printf("\tc_cc[%d] = %u\n", i, term_conf.c_cc[i]); }
printf("\n");
// Configure terminal settings
// cfmakeraw(&term_conf);
// Control flags
// Input flags
// convert break to null byte, no CR to NL translation,
// no NL to CR translation, don't mark parity errors or breaks
// no input parity check, don't strip high bit off,
// no XON/XOFF software flow control
term_conf.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON); //cfmakeraw adds IGNCR and removes INPCK
// Output flags
// no CR to NL translation, no NL to CR-NL translation,
// no NL to CR translation, no column 0 CR suppression,
// no Ctrl-D suppression, no fill characters, no case mapping,
// no local output processing
term_conf.c_oflag = 0;
// Local flags
// echo off, echo newline off, canonical mode off,
// extended input processing off, signal chars off
term_conf.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
// Chars processing
term_conf.c_cflag &= ~(CSIZE | PARENB); //clear current char size mask, no parity checking
term_conf.c_cflag |= CS8; //force 8 bit input
// Others
printf("term_conf.c_cc[VTIME] = %d, RS485_TIMEOUT = %d, VTIME = %d\n\n", term_conf.c_cc[VTIME], RS485_TIMEOUT, VTIME);
printf("term_conf.c_cc[VMIN] = %d, RS485_MINBRD = %d, VMIN = %d\n\n", term_conf.c_cc[VMIN], RS485_MINBRD, VMIN);
term_conf.c_cc[VTIME] = RS485_TIMEOUT;
printf("term_conf.c_cc[VTIME] = %d, RS485_TIMEOUT = %d, VTIME = %d\n\n", term_conf.c_cc[VTIME], RS485_TIMEOUT, VTIME);
term_conf.c_cc[VMIN] = RS485_MINBRD;
printf("term_conf.c_cc[VMIN] = %d, RS485_MINBRD = %d, VMIN = %d\n\n", term_conf.c_cc[VMIN], RS485_MINBRD, VMIN);
if (cfsetispeed(&term_conf, RS485_BAUDRATE) == -1) { printf("Failed to set term ispeed: %s\n", strerror(errno)); }
if (cfsetospeed(&term_conf, RS485_BAUDRATE) == -1) { printf("Failed to set term ospeed: %s\n", strerror(errno)); }
tcsetattr(fd, TCSANOW, &term_conf); // apply new conf now
tcgetattr(fd, &new_term_conf); // get conf again
if (memcmp(&term_conf, &new_term_conf, sizeof(term_conf)) != 0) { // compare to check if changes applied
printf("Terminal configuration failed\n");
printf("term_conf:\n\tc_iflag = %u\n\tc_oflag = %u\n\tc_cflag = %u\n\tc_lflag = %u\n\n",\
term_conf.c_iflag, term_conf.c_oflag, term_conf.c_cflag, term_conf.c_lflag);
for (int i = 0; i < NCCS; i++) { printf("\tc_cc[%d] = %u\n", i, term_conf.c_cc[i]); }
printf("\n");
printf("new_term_conf:\n\tc_iflag = %u\n\tc_oflag = %u\n\tc_cflag = %u\n\tc_lflag = %u\n\n",\
new_term_conf.c_iflag, new_term_conf.c_oflag, new_term_conf.c_cflag, new_term_conf.c_lflag);
for (int i = 0; i < NCCS; i++) { printf("\tc_cc[%d] = %u\n", i, new_term_conf.c_cc[i]); }
printf("\n");
printf("new_term_conf.c_cc[VTIME] = %d, RS485_TIMEOUT = %d, VTIME = %d\n\n", new_term_conf.c_cc[VTIME], RS485_TIMEOUT, VTIME);
printf("new_term_conf.c_cc[VMIN] = %d, RS485_MINBRD = %d, VMIN = %d\n\n", new_term_conf.c_cc[VMIN], RS485_MINBRD, VMIN);
}
// if (ioctl(fd, TIOCEXCL, NULL) == -1) { printf("Failed to get exclusivity: %s", strerror(errno)); }
// if (tcflush(fd, TCIOFLUSH) == -1) { printf("Failed to flush: %s", strerror(errno)); }
memset(ibuff, 0, RS485_IBUFFSIZE);
int i = 20000;
while (i) {
i--;
if (read(fd, ibuff, 1) != -1) { printf("%x\n", ibuff[0]); }
usleep(200);
}
close(fd);
// printf("EAGAIN = %d\n", EAGAIN);
return 0;
}
Output:
Hello guys!
Former terminal configuration:
term_conf:
c_iflag = 0
c_oflag = 0
c_cflag = 3261
c_lflag = 0
c_cc[0] = 3
c_cc[1] = 28
c_cc[2] = 127
c_cc[3] = 21
c_cc[4] = 16
c_cc[5] = 0
c_cc[6] = 0
c_cc[7] = 0
c_cc[8] = 17
c_cc[9] = 19
c_cc[10] = 26
c_cc[11] = 0
c_cc[12] = 18
c_cc[13] = 15
c_cc[14] = 23
c_cc[15] = 22
c_cc[16] = 4
c_cc[17] = 0
c_cc[18] = 0
c_cc[19] = 0
c_cc[20] = 0
c_cc[21] = 0
c_cc[22] = 0
c_cc[23] = 0
c_cc[24] = 64
c_cc[25] = 235
c_cc[26] = 119
c_cc[27] = 0
c_cc[28] = 0
c_cc[29] = 0
c_cc[30] = 0
c_cc[31] = 100
term_conf.c_cc[VTIME] = 0, RS485_TIMEOUT = 0, VTIME = 5
term_conf.c_cc[VMIN] = 16, RS485_MINBRD = 16, VMIN = 4
term_conf.c_cc[VTIME] = 0, RS485_TIMEOUT = 0, VTIME = 5
term_conf.c_cc[VMIN] = 16, RS485_MINBRD = 16, VMIN = 4
Terminal configuration failed
term_conf:
c_iflag = 0
c_oflag = 0
c_cflag = 3261
c_lflag = 0
c_cc[0] = 3
c_cc[1] = 28
c_cc[2] = 127
c_cc[3] = 21
c_cc[4] = 16
c_cc[5] = 0
c_cc[6] = 0
c_cc[7] = 0
c_cc[8] = 17
c_cc[9] = 19
c_cc[10] = 26
c_cc[11] = 0
c_cc[12] = 18
c_cc[13] = 15
c_cc[14] = 23
c_cc[15] = 22
c_cc[16] = 4
c_cc[17] = 0
c_cc[18] = 0
c_cc[19] = 0
c_cc[20] = 0
c_cc[21] = 0
c_cc[22] = 0
c_cc[23] = 0
c_cc[24] = 64
c_cc[25] = 235
c_cc[26] = 119
c_cc[27] = 0
c_cc[28] = 0
c_cc[29] = 0
c_cc[30] = 0
c_cc[31] = 100
new_term_conf:
c_iflag = 0
c_oflag = 0
c_cflag = 3261
c_lflag = 0
c_cc[0] = 3
c_cc[1] = 28
c_cc[2] = 127
c_cc[3] = 21
c_cc[4] = 16
c_cc[5] = 0
c_cc[6] = 0
c_cc[7] = 0
c_cc[8] = 17
c_cc[9] = 19
c_cc[10] = 26
c_cc[11] = 0
c_cc[12] = 18
c_cc[13] = 15
c_cc[14] = 23
c_cc[15] = 22
c_cc[16] = 4
c_cc[17] = 0
c_cc[18] = 0
c_cc[19] = 0
c_cc[20] = 0
c_cc[21] = 0
c_cc[22] = 0
c_cc[23] = 161
c_cc[24] = 7
c_cc[25] = 64
c_cc[26] = 0
c_cc[27] = 168
c_cc[28] = 152
c_cc[29] = 232
c_cc[30] = 119
c_cc[31] = 104
new_term_conf.c_cc[VTIME] = 0, RS485_TIMEOUT = 0, VTIME = 5
new_term_conf.c_cc[VMIN] = 16, RS485_MINBRD = 16, VMIN = 4
4
fe
4
fe
4
fe
4
fe
4
fe
4
fe
4 and fe are the standard final bytes of my messages. Even if I change the nbytes to read in the function call, I never get the other bytes.
Note: over the 10k reads, it returns only a few bytes.
When nothing is returned, read() fails and returns -1, with errno = 11 = EAGAIN, and strerror(errno) gives "Resource temporarily unavailable".
I have tried to setup VMIN = 0 and VTIME = 12 (1.2 sec), but read() does not wait at all, just as if VTIME = 0. Why?
Try to customize microcom
This is now the solution I am looking for in parallel, trying to make a new program inside BusyBox, but that's not proper...
I do thank you very much if you can give an advise on how to make the script work, or on how to solve the read() issue!
1 Answer 1
Finally, removing O_NDELAY enabled me to read my data, but in the wrong order.
Setting a timeout did not help (VTIME = 10, VMIN =0). And I had data in the wrong order.
Setting the exact number of bytes to read in read() as well as having VTIME =0, VMIN = the exact number of bytes to read, solved completely the problem!
Thank you for your help!
1 Comment
Explore related questions
See similar questions with these tags.
O_NDELAYis useful to preventopen()blocking until carrier detect is asserted. If you want to set it to blocking mode for data after opening, that can be done usingfcntl().O_NDELAYresolves the problem! However, I got :4feff0123456789abcdinstead of:ff0123456789abcd4fe... Do you have an idea why the last two bytes0x04and0xfeare read fisrt?