writing a modbus program for a Open-WRT router using libmodbus C (rewrite Python app to C)

6.6k views Asked by At

I am trying to write a program that will run on my Open-wrt router, to reads some registers from a modbus device. The only way I have found to do this is to write the program in C. I have written a simple working Python app for communicating with the Modbus RTU slave device from my PC:

#!/usr/bin/env python

import minimalmodbus
import serial

m = minimalmodbus.Instrument('/dev/ttyUSB0', 2) # port name, slave address (in decimal)
m.serial.baudrate = 19200
m.serial.bytesize = 8
m.serial.stopbits = 2
m.serial.parity   = serial.PARITY_NONE

data = m.read_registers(0, 2, 3) # 3 = Read holding register

print "Value A = ", data[0]
print "Value B = ", data[1]

if (data[0] < 20):
    send = 1
else:
    send = 0

m.write_register(2, send, 0, 16) # 16 = Write multiple registers

Now I need to rewrite my code to C using libmodbus or some other C modbus library. I cannot install python to the device, as it only has 4MB of space, so my only option is to use C/C++.

I found this example code for Raspberry Pi, but code is for RPi only:

// Access from ARM Running Linux
#define BCM2708_PERI_BASE        0x20000000
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */
void *gpio_map;
// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
#define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
// Note: Revision2.0 RaspberryPis, GPIO Pins are: 2, 3, 4, 7, 8, 9, 10, 11, 14, 15, 17, 18, 22, 23, 24, 25, 27, with 28, 29, 30, 31 additionally available on the P5 header)
// for pinout info see http://elinux.org/RPi_Low-level_peripherals#GPIO_Driving_Example_.28C.29
// define GPIO number for each pin (using rev2 of pi)
// T= Top row of pins

My device is not RPi.

Do you know any examples? I do not know C except beyond a simple Hello World program. Sorry for stupid question.

1

There are 1 answers

4
bigjake On

I feel for you... as far as what programming languages it supports, it looks like your stuck with C/C++ or maybe lua. Because your trying to compile code for your router with your PC your going to have to look into cross-compiling. The reason for this (in case you don't know) being that your PC runs on a (most likely) x86_64 or 32 bit architecture(arch) CPU and when you compile code normally, it will compile to machine code that will only run on this arch. Your router likely runs on a ARM or MIPS32 or similar arch, (you should be able to find this in the DD-wrt or Open-wrt databases) so you have to make your computers compiler compile the code to machine code that will run on a different architecture which can be tricky. I'm no expert on cross-compiling, or on Open-wrt, or modding routers in general(PITA) but if you Google around you should be able to find some good tutorials.

There is a decent tutorial on cross-compiling for embedded Linux here.

And here is a good tutorial that looks like it walks you through the process of cross-compiling specifically for Open-wrt C projects:

Also, just checking but i'm assuming your router either has a serial port, or you plan on using a ModbusTCP to RTU gateway, in which case your going to have to set your program up in TCP mode.

From my experience working with modbus in any language can be a bit intimidating if you don't have a working understanding of the protocol itself as well as the device you are trying to communicate with.(Oftentimes the devices are tricky, double and triple check their documentation) Additionally every implementation is a little bit different, some can take a lot of reading, tweaking and debugging to even get a working test, others work right out of the box (such as the minimalmodbus lib for python).

here is a link(not enough rep so and copy and paste and add an 'h') to the docs for libmodbus: ttp://libmodbus.org/docs/v3.1.1/

I haven't written C code for a while but I have cut and pasted this snippet together from the documentation, and tried to add some comments. It handles your basic RTU connection as well as reading from a register.

#include <modbus.h>
modbus_t *ctx;
uint16_t tab_reg[64];
int rc;
int i;
// create your modbus device object
/*modbus_t *modbus_new_rtu(
    const char *device, int baud, char parity, int data_bit, int stop_bit);*/
ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1);
// check if object was created successfully
if (ctx == NULL) {
    fprintf(stderr, "Unable to create the libmodbus context\n");
    return -1;
}
//int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
// read a register value from modbus object into value 'rc'
rc = modbus_read_registers(ctx, 2, 3, tab_reg);
// check to make sure read was successful
if (rc == -1) {
    fprintf(stderr, "%s\n", modbus_strerror(errno));
    return -1;
}
// i'm guessing on this one, but convert a bytes vale into an integer... probably?
for (i=0; i < rc; i++) {
    printf("reg[%d]=%d (0x%X)\n", i, tab_reg[i], tab_reg[i]);
}
// close port and free memory
modbus_close(ctx);
modbus_free(ctx);

I would advise you to find a good tutorial or book such as Practical C++ from O'Reilly(that's what I used at first), or Google search 'c programming tutorials' (I don't have enough rep to post more links and I don't know enough about C to post good ones anyway) and learn a little of the language, before anything else.

If you need more help with the libmodbus package after you have read through the documentation you can try Stephanes IRC channel on freenode #libmodbus or you can always try back here. But if your having a problem be specific, it is well worth investing a little time in making sure your question is as informative as need be.

Good Luck! :-)