I am trying to read a register from an RS485 adapter using a UART connection on an ARM board with no success
Machines
- ARM board: CM-T335 - TI AM335x Computer-on-Module http://www.compulab.co.il/products/computer-on-modules/cm-t335/
- PC: x86 machine running Ubuntu
- Slave device: Elnet Pico (Energy power meter) - works as a modbus slave and sends energy statuses to master when requested using the modbus protocol http://elnet.feniks-pro.com/documents/User_manual/Elnet%20Pico%20-%20User%20Manual.pdf
Connectors
- RX/TX cable
- SENA RS485 adapter: http://www.senanetworks.com/download/datasheet/ds_ltc100.pdf
- Male - Male COM adapter: http://i.ebayimg.com/images/i/300758549547-0-1/s-l1000.jpg
- ultra mini serial to DB9 cable: https://cdn3.yawarra.com.au/wp-content/uploads/Ultra-mini-serial-DB9-adapter_600x600.jpg
Slave device connected to ARM board
Energy power meter <--rx/tx cable--> SENA RS485 Adapter <--Serial Male/Male adapter + Serial to mini serial cable --> ARM board mini-serial port
see images of the setup:
- https://ibin.co/3CCvTVPPIUWQ.jpg
- https://ibin.co/3CCvhrr3CQjr.jpg
- https://ibin.co/3CCw5uNqTW8B.jpg
- https://ibin.co/3CCvuys1tUgt.jpg
Device tree used: am335x-sbc-t335.dts
Device tree resources: http://get-album.com/dts/
Slave device connected to PC
Energy power meter <--2 wire rx/tx--> SENA RS485 Adapter <--> PC
note: The mini serial port on the board is the same port that is used for serial-console communication (on baud 115200) - and this works with no problems
Below is a c code that reads the first register from the connected slave device with the use of libmodbus library
libmodbus_test.c - reading and printing the first reigster from the device:
#include <sys/types.h>
#include <string.h>
#include <modbus.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define MODBUS_DEVICE "/dev/ttyS2"
#define MODBUS_STOP_BIT 1
#define MODBUS_DATA_BIT 8
#define MODBUS_START_BIT 1
#define MODBUS_PARITY 'N'
int main(int argc, char *argv[]) {
const char *dev_path = MODBUS_DEVICE;
uint16_t result[2];
int slave_nr = 31, baud = 9600;
modbus_t *mb;
if (argc == 2) {
dev_path=argv[1];
}
mb = modbus_new_rtu(dev_path, baud, MODBUS_PARITY, MODBUS_DATA_BIT, MODBUS_STOP_BIT);
if (mb == NULL) {
printf("error creating libmodbus rtu\n");
return -1;
}
printf("created new rtu...\n");
modbus_set_debug(mb, 1);
if (modbus_connect(mb) < 0 ){
printf("modbus error: %s\n", modbus_strerror(errno));
modbus_close(mb);
modbus_free(mb);
return -1;
}
modbus_set_slave(mb, slave_nr);
printf("ModBus connected !\n");
if (modbus_read_registers(mb, 0x1, 2, result) < 0) {
printf("error reading bits: %s\n", modbus_strerror(errno));
modbus_close(mb);
modbus_free(mb);
return -1;
}
printf("successfully red integer: %d: %X (hex)\n", result[0], result[0]);
modbus_free(mb);
return 0;
}
[output from running libmodbus_test on PC]
root@cm-debian:~/new# modbus gcc -I /usr/local/include/modbus libmodbus_test.c -o libmodbus_test -lmodbus
root@cm-debian:~/new# ./libmodbus_test /dev/ttyS2
created new rtu...
Opening /dev/ttyS2 at 9600 bauds (N, 8, 1)
ModBus connected !
[1F][03][00][01][00][02][96][75]
Waiting for a confirmation...
<1F><03><04><00><DD><00><DD><54><51>
successfully red integer: 221: DD (hex)
[output from running libmodbus_test on the ARM board]
root@cm-debian:~/new# gcc -I /usr/include/modbus/ libmodbus_test.c -o libmodbus_test -lmodbus
root@cm-debian:~/new# ./libmodbus_test /dev/ttyO0
created new rtu...
Opening /dev/ttyO0 at 9600 bauds (N, 8, 1)
ModBus connected !
[1F][03][00][01][00][02][96][75]
Waiting for a confirmation...
ERROR Connection timed out: select
when executing libmodbus_test from the ARM board, select() always returns 0 while running the same program on PC, it works just fine => the slave device returns data.
An attempt using termios also failed with similar results
termios_test.c
#include <sys/select.h>
#include <termios.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <string.h>
#include <stdint.h>
#include <linux/serial.h>
#include <sys/ioctl.h>
static const uint8_t table_crc_hi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};
/* Table of CRC values for low-order byte */
static const uint8_t table_crc_lo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};
void calc_crc(uint8_t *buffer, ssize_t length, uint8_t *crc_hi_arg, uint8_t *crc_lo_arg) {
uint8_t crc_hi = 0xff;
uint8_t crc_lo = 0xff;
unsigned int i;
while (length--) {
i = crc_hi ^ *buffer++;
crc_hi = crc_lo ^ table_crc_hi[i];
crc_lo = table_crc_lo[i];
}
*crc_hi_arg = crc_hi;
*crc_lo_arg = crc_lo;
}
int main(int argc, char **argv){
char *dev_path = "/dev/ttyS2";
if (argc == 2) {
dev_path = argv[1];
}
uint8_t write_data[8];
int fd,write_len,select_ret, bytes_avail, status;
struct termios config;
char c;
uint8_t crc_hi, crc_lo;
fd_set activefs, tmpfs;;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec= 500000;
fd = open(dev_path, O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL);
if (fd == -1){
perror("open");
return 1;
}
FD_ZERO(&tmpfs);
FD_SET(fd, &tmpfs);
printf("opened device\n");
if (tcgetattr(fd, &config) < 0) { close(fd); return -1 }
if (cfsetispeed(&config, B9600) < 0 || cfsetospeed(&config, B9600) < 0) {
printf("cant setting speed!\n");
close(fd);
return 1;
}
config.c_cflag |= (CREAD | CLOCAL);
config.c_cflag &=~ CSIZE;
config.c_cflag &=~ CSTOPB;
config.c_cflag &=~ PARENB;
config.c_cflag |= CS8;
config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
config.c_iflag &= ~INPCK;
config.c_iflag &= ~(IXON | IXOFF| IXANY);
config.c_oflag &= ~OPOST;
config.c_cc[VMIN] = 0;
config.c_cc[VTIME] = 0;
if (tcsetattr(fd, TCSANOW, &config) < 0) {
printf("cant apply config!\n");
close(fd);
return 1;
}
write_data[0] = 0x1f; // slave addr
write_data[1] = 0x03; // function
write_data[2] = 0x00; // data address of first coil (8b)
write_data[3] = 0x01; // data address of first coil (8b)
write_data[4] = 0x00; // num of coils requested
write_data[5] = 0x01; // num of coils requested
calc_crc(write_data, 6, &crc_hi, &crc_lo);
write_data[6] = crc_hi;
write_data[7] = crc_lo;
printf("data: [0x%x][0x%x][0x%x][0x%x][0x%x][0x%x][0x%x][0x%x]", write_data[0], write_data[1], write_data[2], write_data[3], write_data[4], write_data[5], write_data[6], write_data[7]);
while (1) {
sleep(1);
write_len= write(fd, write_data, 8);
activefs = tmpfs;
select_ret = select(1, &activefs, NULL, NULL, &timeout);
if (select_ret < 0) {
perror("select");
return 1;
}
if (select_ret > 0) {
printf("select returned %d\n", select_ret);
if (read(fd, &c, 1) < 0) {
perror("read");
} else {
printf("received: %d\n", c);
}
}
}
}
[output from termios.c on the ARM board]
root@cm-debian:~/new# ./termios /dev/ttyO0
opened device
... select keeps returning 0
[output from termios.c on PC]
$ gcc -o termios_test termios_test.c
$ ./termios_test /dev/ttyS2
opened device
data: [0x1f][0x3][0x0][0x1][0x0][0x1][0xd6][0x74]select returned 1
received: 31
select returned 1
Never mind the values, there IS a data exchange and that's what I want to achieve using the board
I tried passing RS485 attributes via pcntl, but the same results happened termios_rs485_test.c: http://pastebin.com/RWtHtjLF
The connection between the board and the PC is done through the ultra mini serial to DB9 cable and I can successfully read/write data from and to the board. How come reading data from the RS485 adapter never succeeds? where should I be looking in order to get closer a solution?
Here are some info about the board / drivers / etc
root@cm-debian:~/modbus# uname -a
Linux cm-debian 4.4.0-cm-t335-5.1 #98 SMP Thu Sep 1 15:12:31 IDT 2016 armv7l GNU/Linux
root@cm-debian:~/new# dmesg | grep -i --color '\(serial\|tty\|uart\)'
[ 0.000000] Kernel command line: console=ttyO0,115200n8 root=ubi0:rootfs rw rootfstype=ubifs ubi.mtd=rootfs
[ 0.771007] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
[ 0.780286] omap_uart 44e09000.serial: no wakeirq for uart0
[ 0.780329] of_get_named_gpiod_flags: can't parse 'rts-gpio' property of node '/ocp/serial@44e09000[0]'
[ 0.780960] 44e09000.serial: ttyO0 at MMIO 0x44e09000 (irq = 155, base_baud = 3000000) is a OMAP UART0
[ 1.543031] console [ttyO0] enabled
[ 1.550036] omap_uart 48022000.serial: no wakeirq for uart1
[ 1.556099] of_get_named_gpiod_flags: can't parse 'rts-gpio' property of node '/ocp/serial@48022000[0]'
[ 1.556764] 48022000.serial: ttyO1 at MMIO 0x48022000 (irq = 156, base_baud = 3000000) is a OMAP UART1
[ 2.953486] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[ 2.973176] usb usb1: SerialNumber: musb-hdrc.0.auto
[ 3.572722] usb 1-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[ 6.689030] systemd[1]: Expecting device dev-ttyO0.device...
[ 7.210727] systemd[1]: Starting system-getty.slice.
[ 7.235407] systemd[1]: Created slice system-getty.slice.
[ 7.241302] systemd[1]: Starting system-serial\x2dgetty.slice.
[ 7.265277] systemd[1]: Created slice system-serial\x2dgetty.slice.
[ 7.925632] systemd[1]: Starting LSB: controls configuration of serial ports...
[ 8.485680] systemd[1]: Started LSB: controls configuration of serial ports.
[ 14.840532] pinctrl-single 44e10800.pinmux: pin 44e10978.0 already requested by 48022000.serial; cannot claim for 481cc000.can
[ 14.895866] pinctrl-single 44e10800.pinmux: pin 44e10980.0 already requested by 48022000.serial; cannot claim for 481d0000.can
root@cm-debian:~/modbus# setserial -a /dev/ttyO0
/dev/ttyO0, Line 0, UART: undefined, Port: 0x0000, IRQ: 155
Baud_base: 3000000, close_delay: 50, divisor: 0
closing_wait: 3000
Flags: spd_normal
root@cm-debian:~/new# setserial -a /dev/ttyS2
/dev/ttyS2, Line 2, UART: unknown, Port: 0x0000, IRQ: 0
Baud_base: 0, close_delay: 50, divisor: 0
closing_wait: 3000
Flags: spd_normal
root@cm-debian:~/new# setserial -a /dev/ttyS1
/dev/ttyS1, Line 1, UART: unknown, Port: 0x0000, IRQ: 0
Baud_base: 0, close_delay: 50, divisor: 0
closing_wait: 3000
Flags: spd_normal
root@cm-debian:~/new# setserial -a /dev/ttyS0
/dev/ttyS0, Line 0, UART: unknown, Port: 0x0000, IRQ: 0
Baud_base: 0, close_delay: 50, divisor: 0
closing_wait: 3000
Flags: spd_normal
root@cm-debian:~/modbus# lsmod
Module Size Used by
sha256_generic 9731 1
hmac 2866 1
drbg 13731 1
ctr 3673 2
ccm 7928 2
arc4 2000 2
wl12xx 57190 0
wlcore 180594 1 wl12xx
mac80211 605465 2 wl12xx,wlcore
cfg80211 492985 2 mac80211,wlcore
snd_soc_davinci_mcasp 15953 2
snd_soc_tlv320aic23_i2c 2092 1
snd_soc_simple_card 7474 0
snd_soc_tlv320aic23 10191 1 snd_soc_tlv320aic23_i2c
snd_soc_edma 1309 1 snd_soc_davinci_mcasp
snd_soc_core 158330 5 snd_soc_davinci_mcasp,snd_soc_edma,snd_soc_tlv320aic23_i2c,snd_soc_tlv320aic23,snd_soc_simple_card
snd_pcm_dmaengine 5548 1 snd_soc_core
snd_pcm 92743 4 snd_soc_davinci_mcasp,snd_soc_core,snd_soc_tlv320aic23,snd_pcm_dmaengine
c_can_platform 6650 0
c_can 9638 1 c_can_platform
wlcore_spi 5086 0
can_dev 12315 1 c_can
ti_am335x_adc 5635 0
snd_timer 21413 1 snd_pcm
kfifo_buf 3452 1 ti_am335x_adc
snd 55936 3 snd_soc_core,snd_timer,snd_pcm
industrialio 40286 2 ti_am335x_adc,kfifo_buf
evdev 13187 0
omap_wdt 5293 0
soundcore 1339 1 snd
root@cm-debian:~/new# cat /proc/cpuinfo
processor : 0
model name : ARMv7 Processor rev 2 (v7l)
BogoMIPS : 597.60
Features : half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x3
CPU part : 0xc08
CPU revision : 2
Hardware : Generic AM33XX (Flattened Device Tree)
Revision : 0000
Serial : 0000000000000000
root@cm-debian:~/new# cat /proc/consoles
ttyO0 -W- (EC p ) 250:0
root@cm-debian:/etc# cat debian_version
8.2
Any help is well appreciated Thanks
UPDATE
While the ultra mini serial to DB9 cable is connected to the board, I ran termios_test.c and while running, I inserted a metal piece in the middle pin of the cable (the TX) and I could see garbage data showing on the screen (sometimes!). I plugged the male-male adapter to the cable and touched the middle pin again, and I could see garbage data again.
Another thing I've done is connecting the slave device to to the SENA RS485 adapter, and use the metal piece on the pins, the middle pin of the adapter, when touching the metal, makes the TX led turn on. no pins would turn on the RX led. When connecting that RS485 adapter to the PC's COM port, sending data to that port would make the RX led flash. I am beginning to suspect that the board is not writing to the RS485 adapter correctly, or that pins are not mapped correctly. Where should I go from here? might this be really a software issue?
for i in /proc/[0-9][0-9*]; do ls -l $i/fd | grep tty; done;
and see if a user process is using the port. A module (console) might also claim it. Linux can get the console device from several places (including command line, built-in and device tree). – Vinositymodbus_test.c
in the question and alibmodbus_test.c
in the compilation command... Are these the same files? – Hermaphroditusfor i in /proc/[0-9][0-9*]; do ls -l $i/fd | grep tty; done;
did not list any files.lsof | grep ttyO0
also did not produce any result. added device tree resource to question. – Gimlet