Raspberry PI python the most simple MODBUS library

Thread Starter

zazas321

Joined Nov 29, 2015
936
Hey. I have multiple ESP32 boards daisy chained. I initially configured one of the ESP32 as a master device and tested the communications.
Now that I have confirmed that communications are working properly and I can write/read data thorugh MODBUS, I want to configure my raspberry PI as a master device and send a very simple messages to the selected slave devices.

This is my master code for ESP32:
Code:
/*
* //masteris su lipduku
  ModbusRTU ESP8266/ESP32
  Read multiple coils from slave device example

  (c)2019 Alexander Emelianov (a.m.emelianov@gmail.com)
  https://github.com/emelianov/modbus-esp8266

  modified 13 May 2020
  by brainelectronics

  This code is licensed under the BSD New License. See LICENSE.txt for more info.
*/
#include <ModbusRTU.h>
#define RXD2 16
#define TXD2 17
#define DIR 15
#define REG1 10
#define BUTTON1   36 //pin D5
#define BUTTON2   39 //pin D5


ModbusRTU mb;

int buttonState1 = 0;         // current state of the button
int lastButtonState1 = 0;     // previous state of the button
int buttonState2 = 0;         // current state of the button
int lastButtonState2 = 0;     // previous state of the button
uint16_t send_data = 5;
uint16_t storing_data = 0; // place holder to store data
unsigned long lastMsg = 0;

char myChar = 'A';
bool cbWrite(Modbus::ResultCode event, uint16_t transactionId, void* data) {
  Serial.printf_P("Request result: 0x%02X, Mem: %d\n", event, ESP.getFreeHeap());
  return true;
}
//bool cbRead(Modbus::ResultCode event, uint16_t transactionId, void* data) {
  //Serial.printf_P("Request result: 0x%02X, Mem: %d\n", event, ESP.getFreeHeap());
  //return true;
//}

void setup() {
  pinMode(BUTTON1,INPUT);
  pinMode(BUTTON2,INPUT);
  Serial.begin(9600);
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
  mb.begin(&Serial2,DIR,true);
  mb.master();
}

void loop() {

  mb.task();
  //yield();
  check_button_state(BUTTON1,BUTTON2);

    unsigned long now = millis();

  // no need to update when the device is in pildymas mode
  if (now - lastMsg > 1000 ) {
    if (!mb.slave()) {
      mb.readHreg(1, REG1, &storing_data,1);
      Serial.print("Value inside storing data=");
      Serial.println(storing_data);
      }
    lastMsg=now;
 
  }
}

void check_button_state(int pin1,int pin2){

  buttonState1 = digitalRead(pin1);
  buttonState2 = digitalRead(pin2);

  if (buttonState1 != lastButtonState1) {
    if (buttonState1 == HIGH){
      Serial.println("button1 pressed");
      if (!mb.slave()) {
        mb.writeHreg(1, REG1, 99,  cbWrite);
        Serial.println("Writing 99 to REG1");
      }
      // if the current state is HIGH then the button went from off to on:
      Serial.println("on");
    }
      else {
      // if the current state is LOW then the button went from on to off:
      Serial.println("off");
    }
  }
  if (buttonState2 != lastButtonState2) {
    if (buttonState2 == HIGH){
      Serial.println("button2 pressed");
      if (!mb.slave()) {
        Serial.println("Writing char to REG1");
        mb.writeHreg(1, REG1, myChar,  cbWrite);
      }
      // if the current state is HIGH then the button went from off to on:
      Serial.println("on");
    }
      else {
      // if the current state is LOW then the button went from on to off:
      Serial.println("off");

      }
  }

  lastButtonState1 = buttonState1;
  lastButtonState2 = buttonState2;
}
I have wired 2 buttons to ESP32, and all it is doing is sending a slave with ID = 1 a message according to which button is pressed.

I have connected my raspberry PI TX/RX and direction pin to a RS485 driver module:
https://www.ti.com/lit/ds/symlink/sn75176b.pdf?ts=1603871763734&ref_url=https%3A%2F%2Fwww.google.com%2F

and connecting A/B to the ESP32.


The first library I have looked at is modbus tk. I have tried the following code with no luck :

Code:
#!/usr/bin/env python
# -*- coding: utf_8 -*-
"""
Modbus TestKit: Implementation of Modbus protocol in python
(C)2009 - Luc Jean - luc.jean@gmail.com
(C)2009 - Apidev - http://www.apidev.fr
This is distributed under GNU LGPL license, see license.txt
"""

import serial
import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu

#PORT = 1
PORT = '/dev/ttyAMA0'

def main():
    """main"""
    logger = modbus_tk.utils.create_logger("console")

    try:
        #Connect to the slave
        master = modbus_rtu.RtuMaster(
            serial.Serial(port=PORT, baudrate=9600, bytesize=8, parity='N', stopbits=1, xonxoff=0)
        )
        master.set_timeout(5.0)
        master.set_verbose(True)
        logger.info("connected")




        #logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 1,5 ))

        #send some queries
        #logger.info(master.execute(1, cst.READ_COILS, 0, 10))
        #logger.info(master.execute(1, cst.READ_DISCRETE_INPUTS, 0, 8))
        #logger.info(master.execute(1, cst.READ_INPUT_REGISTERS, 100, 3))
        #logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 100, 12))
        #logger.info(master.execute(1, cst.WRITE_SINGLE_COIL, 7, output_value=1))
        logger.info(master.execute(1, cst.WRITE_SINGLE_REGISTER, 10, output_value=99))
        #logger.info(master.execute(1, cst.WRITE_MULTIPLE_COILS, 0, output_value=[1, 1, 0, 1, 1, 0, 1, 1]))
        #logger.info(master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 100, output_value=xrange(12)))

    except modbus_tk.modbus.ModbusError as exc:
        logger.error("%s- Code=%d", exc, exc.get_exception_code())

if __name__ == "__main__":
    main()

How does the library above know what DIR pin I have connected to since I do not see any declaration in the RTU example code? How is direction pin controller by this library?

and my Slave code is :
Code:
/*
  ModbusRTU ESP8266/ESP32
  Simple slave example

  (c)2019 Alexander Emelianov (a.m.emelianov@gmail.com)
  https://github.com/emelianov/modbus-esp8266

  modified 13 May 2020
  by brainelectronics

  This code is licensed under the BSD New License. See LICENSE.txt for more info.
*/

#include <ModbusRTU.h>

#define RXD2 16
#define TXD2 17
#define DIR 15
#define REG1 10
#define SLAVE_ID 1
#define BUTTON1   36 //pin D5

ModbusRTU mb;

uint16_t send_data = 100;


void setup() {

  Serial.begin(9600, SERIAL_8N1);
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
  mb.begin(&Serial2,DIR,true);
  mb.setBaudrate(9600);

  mb.slave(SLAVE_ID);//Initializng modbus slave device with ID 1
  mb.addHreg(REG1); // add the register with address 1
  //mb.Hreg(REG1, 0); 

  //mb.writeHreg(1, 100, &send_data, 1, cbWrite);
}
void loop() {
  mb.task();


  switch(mb.Hreg(REG1)){
    case 99:
      Serial.println(mb.Hreg(REG1));
      mb.Hreg(REG1, 55);    //PUT 55 IN REG1
      break;
    case 65:
      Serial.println(mb.Hreg(REG1));
      mb.Hreg(REG1, 155);    //PUT 55 IN REG1
      break;
  }



}

Could someone suggest the most simple modbus library for python? I just need to be able to write/read registers to and from slave devices
 
Last edited:

Thread Starter

zazas321

Joined Nov 29, 2015
936
My latest findings:
Just trying to write some serial data and capture it using logic analyzer. I have connected RX/TX And GND to my signal analyzer and this is my python script:
Code:
import serial
import time
sendSerial = serial.Serial ("/dev/ttyAMA0", 9600)

for i in range(0,10):
    sendSerial.write (str.encode("HELLO"))
    time.sleep(0.2)
    print("done")

sendSerial.close()
print("done")

Issue 1:
After initialzing serial as port /dev/ttyAMA0, I can no longer execute any print statements. Also, my logic analyzer does not pick up any data either.

Could it be that I am initializing a wrong port? is it not supposed to be AMA0?
 

Thread Starter

zazas321

Joined Nov 29, 2015
936
I have been reading about the Raspberry PI and modbus. All the libraries I have managed to find for the raspberry PI on MODBUS does not ever mention the flow control direction pin. I assume all libraries are making use of RS485 driver chips that use automatic flow control. That is not good for me since I have a RS485 module that requires me to toggle DIR pin. I literally could not find a single library that would control direction pin for me for the Raspberry PI.
 

nsaspook

Joined Aug 27, 2009
13,273
Yes, the modbus library should handle the flow-control inside the standard Linux device driver on a compatible device.
http://www.gikfun.com/belong-to-you...rs485-ttl-serial-converter-adapter-p-212.html
https://techsparx.com/energy-system/modbus/linux-modbus-usb-rs485.html

If you have your own external RS485 driver then the switching is up to you with a gpio pin.
https://github.com/nsaspook/ihc_mon/blob/re20a/ihc_mons.pdf
https://forum.allaboutcircuits.com/...c-controlled-battery-array.32879/post-1484053

In the language you use define gpio pins for DE and RE_ and then toggle as needed.
C code fragments for a PIC18 modbus master using this transceiver.
https://www.mouser.com/datasheet/2/609/ADM3095E-EP-1517523.pdf

C:
#define DE          LATAbits.LATA0
#define RE_        LATAbits.LATA1


    case INIT:
        if (get_2hz(FALSE) > QDELAY) {
#ifdef LOCAL_ECHO
            RE_ = 0; // keep receiver active
#else
            RE_ = 1; // shutdown receiver
#endif
            DE = 1;
            V.send_count = 0;
            V.recv_count = 0;
            cstate = SEND;
            clear_500hz();
        }
        break;
    case SEND:
        if (get_500hz(FALSE) > TDELAY) {
            do {
                while (BusyUSART()); // wait for each byte
                TXREG = cc_buffer[V.send_count];
            } while (++V.send_count < req_length);
            while (BusyUSART()); // wait for the last byte
            cstate = RECV;
            clear_500hz();
        }
        break;
    case RECV:
        if (get_500hz(FALSE) > TDELAY) {
            uint16_t c_crc, c_crc_rec;

            DE = 0;
            RE_ = 0;

            /*
             * check received response data for size and format for each command sent
             */
            switch (modbus_command) {
            case G_ERROR: // check for controller error codes
                req_length = sizeof(re20a_error);
                if ((V.recv_count >= req_length) && (cc_buffer[0] == 0x01) && (cc_buffer[1] == 0x03)) {
                    uint16_t temp;
                    c_crc = crc16(cc_buffer, req_length - 2);
                    c_crc_rec = (uint16_t) ((uint16_t) cc_buffer[req_length - 2] << (uint16_t) 8) | ((uint16_t) cc_buffer[req_length - 1] & 0x00ff);
                    if (c_crc == c_crc_rec) {
                        if ((temp = (cc_buffer[3] << 8) +(cc_buffer[4]&0xff))) {
                            NOP();
                            RE20A_ERROR = ON;
                            set_led_blink(ERROR_BLINKS);
                        } else {
                            RE20A_ERROR = OFF;
                        }
                    }
                    cstate = CLEAR;
                } else {
                    if (get_500hz(FALSE) > RDELAY) {
                        cstate = CLEAR;
                        RE20A_ERROR = OFF;
                        mcmd = G_MODE;
                    }
                }
                break;
 

Thread Starter

zazas321

Joined Nov 29, 2015
936
I am getting so much problems with this Raspberry PI and modbus. I cannot figure out how to properly implement the direction pin control. I have connection the DIR to the GPIO 17 ( which has an alternate GPIO mode of usart0 RTS).

I have then found this article:
https://ethertubes.com/raspberry-pi-rts
https://github.com/mholling/rpirtscts

After executing the suggested commands, I have checked the raspberry GPIO tree with command:
gpio readall

It appears that it did not change to mode of GPIO16/GPIO17
https://ibb.co/FbXRkB9


Also, running python script does not work well with handling the direction pin:
Code:
#!/usr/bin/env python
# -*- coding: utf_8 -*-
"""
Modbus TestKit: Implementation of Modbus protocol in python
(C)2009 - Luc Jean - luc.jean@gmail.com
(C)2009 - Apidev - http://www.apidev.fr
This is distributed under GNU LGPL license, see license.txt
"""

import serial
import RPi.GPIO as GPIO
import modbus_tk
import modbus_tk.defines as cst
import time
from modbus_tk import modbus_rtu
GPIO.setmode(GPIO.BOARD) # Use physical pin numbering
GPIO.setup(11, GPIO.OUT) # Set pin 10 to be an input pie to be pulled low (off)


PORT = '/dev/ttyS0'
time.sleep(1)
master = modbus_rtu.RtuMaster(serial.Serial(port=PORT, baudrate=9600, bytesize=8, parity='N', stopbits=1, xonxoff=0))
master.set_timeout(5.0)
master.set_verbose(True)


GPIO.output(11,1)
time.sleep(1)
master.execute(1, cst.WRITE_SINGLE_REGISTER, 10, output_value=99)
time.sleep(1)
GPIO.output(11,0)
As you can see I am manually toggling the direction pin HIGH and then LOW after I execute modbus command. The pin goes HIGH but never goes LOW. I think the library intends to control this pin by software automatically but could not find any signs of that from the github page of this library:
https://github.com/ljean/modbus-tk

Channel 0 is usart TX
Channel 2 is DIR pin

1604471892511.png
 
Last edited:
Top