0

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:

  1. Use microcom in a shell script (swift option, should be sufficient)
  2. Write my own program from scratch
  3. 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!

0andriy
4,8242 gold badges27 silver badges42 bronze badges
asked Jun 7, 2024 at 14:46
7
  • "read() does not wait at all" - Your code is accessing the serial terminal in non-blocking mode because it was opened with O_NDELAY. Study stackoverflow.com/questions/25996171/… Commented Jun 7, 2024 at 21:22
  • Opening with O_NDELAY is useful to prevent open() blocking until carrier detect is asserted. If you want to set it to blocking mode for data after opening, that can be done using fcntl(). Commented Jun 10, 2024 at 16:47
  • 1
    One thing I noticed about the script is that it does not set the baud rate before echoing TX data to the device. Commented Jun 10, 2024 at 16:50
  • Hello! Thank you for the comments. Removing the O_NDELAY resolves the problem! However, I got : 4feff0123456789abcd instead of: ff0123456789abcd4fe... Do you have an idea why the last two bytes 0x04 and 0xfe are read fisrt? Commented Jun 13, 2024 at 14:32
  • @SFrancis "Do you have an idea why the last two bytes 0x04 and 0xfe are read fisrt?" - That appears to be a message alignment issue. What you call "the last two bytes" probably belong to the previous message. Your program has no logic to confirm that the first byte of the read buffer is actually the first byte of a message/packet. You've configured termios for raw reads using only a byte count. There's nothing to assure that the count will start on the first byte of a message. Commented Jun 14, 2024 at 3:50

1 Answer 1

0

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!

answered Jun 13, 2024 at 15:35
Sign up to request clarification or add additional context in comments.

1 Comment

The read syscall is not returning the data in "the wrong order" as you claim. More likely is that you're looking at the end bytes of one message combined with the start of the next message. When you have "VTIME =0, VMIN = the exact number of bytes to read, you got lucky, and your code only seemed to work. But your code is not reliable because there's nothing to enforce the byte alignment of messages. According to Murphy's Law, your code will only fail when you try to demo it to others.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.