Custom serial com on TRB246

Good day!

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

1/ 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:
image
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.

2/ Write my own program from scratch: a fail (read() is not working properly)
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?

3/ 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 1/ script work, or on how to solve the read() issue!

Cheers,

Francis

Could you clarify how this is related to OpenWrt?

I am wondering if there are conditions for read usage linked to openwrt for serial com. TRB246 is running a rutOS, based on openwrt.

That device is running a heavily modified (vendor) fork of OpenWrt. It is not supported by the official OpenWrt project. You need to ask the maintainers of the rutOS for help, as it is not supportable here.