ModbusTCP - wrong ID in generated frame - c

I have weird problem. I try to communicate with ifm AY1020 via modbusTCP using libmodbus from PC.
My code looks as follow:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <modbus/modbus.h>
int main()
{
modbus_t *ctx;
uint16_t *tab_reg;
int rc;
int i;
ctx = modbus_new_tcp("192.168.1.250", 502);
modbus_set_debug(ctx, TRUE);
tab_reg = (uint16_t *) malloc(5 * sizeof(uint16_t));
memset(tab_reg, 0, 5 * sizeof(uint16_t));
if (modbus_connect(ctx) == -1)
{
fprintf(stderr, "Connection failed: %s\n",modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
rc = modbus_read_registers(ctx, 3002, 2, tab_reg);
if (rc == -1)
{
fprintf(stderr, "%s\n", modbus_strerror(errno));
return -1;
}
for (i=0; i < rc; i++) {
printf("reg[%d]=%d (0x%X)\n", i, tab_reg[i], tab_reg[i]);
}
modbus_close(ctx);
modbus_free(ctx);
}
Thanks to debug I was able to get the frame that is generated in modbus_read_registers function:
[00][01][00][00][00][06][FF][03][0B][BA][00][02]
And I get this
ERROR Gateway path unavailable
Gateway path unavailable
After analysis you can find that device id in that frame is FF, but according to this error PLC expects 1.
Going further if during debugging I force change this value from FF to 01 everything works fine. It looks like it assign wrong ID.
I would be grateful for any help, advice, solution.
Best,
Paweł

Looking at the Man
You should call modbus_set_slave to set a specific destination device.
TCP
The slave number is only required in TCP if the message must reach a device on a serial network. The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to restore the default value.
Emphasis mine
Your code should be
modbus_set_slave(ctx, 1);
rc = modbus_read_registers(ctx, 3002, 2, tab_reg);

Related

Listening for sysfs battery events

I'm writing a daemon which checks the battery capacity. This is for a solar powered embedded device running Linux. I've read that it's a bad idea to use sleep() in daemons, thus I'm trying to use events. So I wrote some PoCs, but I'm not getting any events! My first implementation, as they recommended to me, uses libudev and poll():
#include <fcntl.h>
#include <libudev.h>
#include <poll.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void)
{
struct udev *udev;
struct udev_monitor *mon;
struct pollfd fds[1];
int fd;
udev = udev_new();
if (udev == NULL)
return 1;
mon = udev_monitor_new_from_netlink(udev, "udev");
udev_monitor_filter_add_match_subsystem_devtype(mon, "power_supply", NULL);
udev_monitor_enable_receiving(mon);
fd = udev_monitor_get_fd(mon);
fds[0].fd = fd;
fds[0].events = POLLIN;
fds[0].revents = 0;
if (poll(fds, 1, -1) > 0) {
/* Never gets here! */
struct udev_device *const dev = udev_monitor_receive_device(mon);
if (dev != NULL) {
puts(udev_device_get_sysname(dev));
udev_device_unref(dev);
}
else
fputs("udev_monitor_receive_device() failed\n", stderr);
}
udev_unref(udev);
return 0;
}
They only event I get, is when I plug/unplug the charger! Then I thought that the status bar I use in my laptop's installation does show the battery capacity. I looked at the source and they're using inotify to monitor the battery's uevent. But I've read everywhere that I shouldn't use inotify for sysfs! I tried nonetheless:
#include <stdio.h>
#include <sys/inotify.h>
#include <unistd.h>
#define BAT_PATH "/sys/class/power_supply/BAT0"
int main(void)
{
struct inotify_event ev = {0};
int wd, ret = 1;
ssize_t len;
const int fd = inotify_init1(IN_CLOEXEC);
if (fd < 0) {
perror("inotify_init() failed");
return ret;
}
/* else */
wd = inotify_add_watch(fd, BAT_PATH "/uevent", IN_ACCESS);
if (wd < 0)
goto end;
/* else */
len = read(fd, &ev, sizeof(ev));
/* Again... never gets here. */
if (len > 0 && (ev.mask & IN_ACCESS))
puts("It worked!");
inotify_rm_watch(fd, wd);
ret = 0;
end:
close(fd);
return ret;
}
Turns out that doesn't work either! How can it work for my status bar but not work when I try it? Am I doing something horribly wrong? Thank you.
Regarding your first implementation (would comment but not enough rep. as i know nothing about libudev): the guide i followed to successfully use sysfs to poll() a GPIO for interrupt suggests to look for a POLLPRI event, instead of POLLIN as you show in the first implementation (see man poll for event types).
More importantly, you say you get a single event when you connect/disconnect charger, do you mean a single event per software execution? If this is the case, it might be due to the fact that you don't clear the interrupt flag: after poll() hits, in sysfs one needs to int len = read(fds[0].fd, *buf, SIZE); to mark the interrupt as served, and also lseek(fds[0].fd, 0, 0); in order for the next read() to be succesful (see my other answer here for a code example).
i dont know whether this answer will help you or not, but i am writing answer because if any other users face same issue they can get it resolved.
the solution is:
you need to monitor kernel Events, so you need to change line
form
mon = udev_monitor_new_from_netlink(udev, "udev");
to
mon = udev_monitor_new_from_netlink(udev, "kernel");
then you will get events.

`ioctl` returns -1 when trying to TUNSETIFF a TUN device

I've been following the official documentation on creating a TUN device. There's very little documentation, but what even is there does not seem to work. Here's what I have so far:
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
int tun_alloc(char *dev)
{
struct ifreq ifr;
int fd, err;
if( (fd = open("/dev/net/tun", O_RDWR)) < 0 )
return 7; // tun_alloc_old(dev); // this does not happen, linux docs don't say what tun_alloc_old is anyway
memset(&ifr, 0, sizeof(ifr));
/* Flags: IFF_TUN - TUN device (no Ethernet headers)
* IFF_TAP - TAP device
*
* IFF_NO_PI - Do not provide packet information
*/
ifr.ifr_flags = IFF_TUN;
if( *dev ) {
printf("setting ifr.ifr_name to \"%s\"\n", dev);
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
printf("ifr.ifr_name is \"%s\"\n", ifr.ifr_name);
if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
perror("ERR");
close(fd);
printf("GOT ERROR: %d\n", err);
return err;
}
printf("past TUNSETIFF error checking\n");
strcpy(dev, ifr.ifr_name);
return fd;
}
int main() {
char name[128];
strcpy(&name, "tun23");
int tun = tun_alloc(name);
printf("tun_alloc: %s id: %d\n", name, tun);
if(tun == -1) return -1;
while(1) {
printf("in loop\n");
char buf[128];
ssize_t readAmount = read(tun, buf, 128);
printf("finished read\n");
if(readAmount == -1) {
printf("read 0 bytes\n");
continue;
}
printf("Read %d: ", readAmount);
for(int i = 0; i < 128; i++) {
printf("%hhx", buf[i]);
}
printf("\n");
}
return tun;
}
Compiler step is gcc -g3 test.c && a.out.
When ran as non-root, I get this output
setting ifr.ifr_name to "tun23"
ifr.ifr_name is "tun23"
ERR: Operation not permitted
GOT ERROR: -1
tun_alloc: tun23 id: -1
And as root, it successfully gets into the loop, but then the call to read seems to block.
setting ifr.ifr_name to "tun23"
ifr.ifr_name is "tun23"
past TUNSETIFF error checking
tun_alloc: tun23 id: 3
in loop
(In the event of an XY problem, the reason I'm doing this is to try and make a very simple vpn-like application)
The issue is that while it's blocking in this loop, no TUN devices are created. I am looking in /dev/. I'm not sure how I would give it any bytes to read.
Edit: added a proper perror call and the respective output Operation not permitted, valgrind output, and a couple print statements. Going to try and debug the strcpy error.
Edit: fixed the strcpy error, was pretty simple. Seems to fail in non-root due to a lack of permission to create TUN devices.

How to transfer data using RS485 to BeagleBone Black

I need to transfer data to BeagleBone Black via Modbus RTU using Rs485. To work with Modbus RTU,but I don't know how to toggle rts in rs-485.HELP
Modbus RTU data transmission code
how to add RS485 code to this,to use the Modbus library libmodbus
#include "modbus-rtu.h"
#include <stdio.h>
#include <errno.h>
int main(){
int connected;
modbus_t *ctx;
uint16_t tab_reg[64];
int rc;
int i;
ctx = modbus_new_rtu("/dev/ttyS4", 9600, 'N', 8, 1);
if(ctx == NULL) {
fprintf(stderr, "Unable to create the libmodbus context\n");
}
else {
modbus_set_slave(ctx, 1);
modbus_set_debug(ctx, TRUE);
connected = modbus_connect(ctx);
printf("modbus_set_slave return: %d\n", rc);
if (rc != 0)
{
printf("modbus_set_slave: %s \n"modbus_strerror(errno));
}
rc = modbus_read_registers(ctx, 0, 3, tab_reg);
for (i = 0; i < rc; i++) {
printf("reg[%d]=%d (0x%X)\n", i, tab_reg[i], tab_reg[i]);
}
if(rc == -1)
{
fprintf(stderr, "%s\n", modbus_strerror(errno));
}
modbus_close(ctx);
modbus_free(ctx);
}
return 0;
}
First I would check if your hardware support automatic (hardware) half-duplex direction control.
If not, then you can take a look at this answer. In there you have all the details you need to compile and install libmodbus with support for toggling the line with software.
I'm using this solution in my RPi and CHIP computers but you should be able to use it right away on your BBB. You might find a small caveat if the GPIO file name has a 3 or 4-digit number (take a look in /sys/class/gpio). If that's the case, take a look here, I had to modify the code to correct that problem.

Multiple RS485 slaves with libmodbus

I have multiple slaves on a RS485 bus. I have been using pymodbus so far but I'm not quite happy with it's performance and other issues. So I wanted to to test libmodus and use that instead.
I wrote a minimal program that reads the model number of my slaves
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <modbus.h>
#include <errno.h>
char *bigendian_vec_to_str(uint16_t *vec, size_t vec_size, char *buff, size_t buff_len)
{
memset(buff, 0, sizeof *buff * buff_len);
int i;
for(i = 0; i < vec_size; ++i)
{
uint16_t fl = vec[i] >> 8;
uint16_t sl = vec[i] & 0xff;
if(2*i >= buff_len - 1)
return buff;
if(fl == 0)
return buff;
buff[2 * i] = fl;
if(2*i + 1 >= buff_len - 1)
return buff;
if(sl == 0)
return buff;
buff[2 * i + 1] = sl;
}
return buff;
}
char *get_model_name_of(modbus_t *modbus, int slave, char *buff, size_t buff_len)
{
modbus_flush(modbus);
modbus_set_slave(modbus, slave);
int rc;
uint16_t reg[9];
memset(reg, 0, sizeof reg);
rc = modbus_read_registers(modbus, 0xe, 8, reg);
if (rc == -1) {
fprintf(stderr, "Error %d while reading: %s\n", errno, modbus_strerror(errno));
return NULL;
}
return bigendian_vec_to_str(reg, 8, buff, buff_len);
}
int main(void)
{
modbus_t *modbus = modbus_new_rtu("/dev/ttyUSB0", 9600, 'N', 8, 1);
modbus_rtu_set_serial_mode(modbus, MODBUS_RTU_RS485);
if (modbus_connect(modbus) == -1) {
fprintf(stderr, "Connexion failed: %s\n", modbus_strerror(errno));
modbus_free(modbus);
return -1;
}
char buff[1024];
int i;
for(i = 2; i < 5; ++i)
{
printf("Model of slave %d: %s\n", i, get_model_name_of(modbus, i, buff, sizeof buff));
}
modbus_free(modbus);
return 0;
}
When I ran this code I got
Model of slave 2: LEFS25B-600
Error 110 while reading: Connection timed out
Model of slave 3: (null)
Model of slave 4: LEHF10K2-16
and it seemed strange that the 2nd module was not responding. So I looped get_model_name_of through 2,3,4,2,3,4,2,3,4.... and every second read attempt ended with Error 110 while reading: Connection timed out. After the iine modbus_set_slave(modbus, slave); I added
usleep(0.005 * 1000000);
and then I didn't get timeouts anymore. I read the man pages twice and I didn't find anything warning me about this. I also searched google but none of the "similar" threads I found were of any help.
What is the best way to deal with multiple slaves? Why does adding a sleep of half of milisecond help here? I mean the code on libmodus does
static int _modbus_set_slave(modbus_t *ctx, int slave)
{
/* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
if (slave >= 0 && slave <= 247) {
ctx->slave = slave;
} else {
errno = EINVAL;
return -1;
}
return 0;
}
is setting an internal value in the context. Are there any time constraints between the change of an internal value in the context and reading/writing to the bus? If so, how long should I wait after a set_slave? Why does libmodbus set the slave id globally instead of having it as a parameter in the read/write method as other libraries (like pymodbus) do?
Or am I using this API just incorrectly?
Thanks
I may be wrong.. but.. as I understand it. The modbus master sends out a request, targeted at a specfic slave number. The intention is to recieve a reply from the targeted slave and then send a request to the next slave and await a reply from second slave. If the requests are sent out without waiting for reply from the first slave.. then there is a possibility to miss the reply from the second slave(or third or whatever number slave) , while the first slave reply is being sent and recieved by the master.
I am not good in C programming.. but I recommend you check this..as I think that may be why you adding a delay seems to help... ( Also.. part of Modbus protocol does require a pause in signal transmission to define start and end of transmission.)
If I am correct , then the use of a delay will only work well if you know the size of data being sent and the time to calculate a response..For other situations a handshake of some kind would be safe.. Such as read a coil.. that indicates whether data is refreshed and ready to be read from the slave as a possible traffic light . to control timing of requests going to other slave and to avoid collision of responses.
Again.. I am not good in C and if I have misinterpreted the program.. please ignore what I have said.. If it helps.. I would be happy hear.
Peter

How to get network adapter stats in linux/Mac OSX?

I'm looking for a way to get hold of network stats in C on Linux and MacOSX. Specifically, I need to monitor the number of bytes uploaded and downloaded from each network adapter on the system - I don't need to do packet inspection, or differentiate between protocols, just a 'total bytes' counter which I can poll at intervals would be fine. In Windows I can do this using the iphlpapi.dll library via GetIfTable (to list the network adapters) and GetIfEntry (to read the stats), but I can't find the Linux/OSX equivalents. My knowledge of C is fairly basic so I would appreciate a solution that isn't too involved. Any help would be much appreciated!
The Darwin netstat source code uses sysctl.
Here's some code that prints the number of bytes in and out on OSX:
#import <Foundation/Foundation.h>
#include <sys/sysctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/route.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int mib[] = {
CTL_NET,
PF_ROUTE,
0,
0,
NET_RT_IFLIST2,
0
};
size_t len;
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
fprintf(stderr, "sysctl: %s\n", strerror(errno));
exit(1);
}
char *buf = (char *)malloc(len);
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
fprintf(stderr, "sysctl: %s\n", strerror(errno));
exit(1);
}
char *lim = buf + len;
char *next = NULL;
u_int64_t totalibytes = 0;
u_int64_t totalobytes = 0;
for (next = buf; next < lim; ) {
struct if_msghdr *ifm = (struct if_msghdr *)next;
next += ifm->ifm_msglen;
if (ifm->ifm_type == RTM_IFINFO2) {
struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm;
totalibytes += if2m->ifm_data.ifi_ibytes;
totalobytes += if2m->ifm_data.ifi_obytes;
}
}
printf("total ibytes %qu\tobytes %qu\n", totalibytes, totalobytes);
[pool drain];
return 0;
}
I can't speak to OSX but on linux take a look at /proc/net/dev.
If you do 'cat /proc/net/dev' you should see statistics including 'bytes' - the total number of bytes of data transmitted or received by the interface. You can read the file within your own program.
EDIT:
I didn't read your whole question. This article should help you get started with /proc and has a section on /proc/net/dev.
Also, to list the interfaces you can call ioctl with the SIOCGIFCONF option. You can Google for a decent code example on how to loop through the returned data. Or you can simply pull it out of the /proc.net/dev data mentioned above, which should be easier.
on Linux:
low level: check /sys/class/net/eth0/statistics/
slightly higher level: ip -s link show eth0
graphical: iftop
interactive: iptraf

Resources