Page 1 of 1

ESP32 Modbus RS485 sensor not responding

Posted: Thu Nov 09, 2023 6:30 pm
by yasmineK
Hello,

I want to work with a sensor that uses Modbus as a communication protocol, to be able to use ESP32, I am using a TTL MAX485 converter.
Unfortunately the sensor is not responsing, sometimes it responds with the same request I send, sometimes it displaying nothing in the serial monitor.
Please can anyone help ?

Code: Select all

#include <SoftwareSerial.h>

uint8_t TX_PIN = 27, RX_PIN = 26;
uint8_t  LED_PIN = 2;
#define RE 32  
#define DE 33 

SoftwareSerial Soft_Serial(RX_PIN, TX_PIN);
#define Soft_Serial Serial2 

byte no_ByteB, no_ByteV, incomingByteB[128], incomingByteV[128] = {0};
float temperature, dissolvedOxygen;
int doDec, tempDec, doDecp, tempDecp;
float doValue, tempValue; 

void setup() {
  Serial.begin(115200);
  Soft_Serial.begin(9600);

  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);  // DE/RE Control pin of RS-485
  pinMode(LED_PIN, OUTPUT);
  Serial.println(" end setup ");
}

void disableEchoMode() {
  byte command[] = {0x06, 0x06, 0x11, 0x00, 0x00, 0x01, 0x4C, 0x81};
 Serial.println(" ** inside disableEchoMode function ** ");
   digitalWrite(DE,HIGH);  // Receive Enabled
  digitalWrite(RE,HIGH);  
 Soft_Serial.write(command, sizeof(command));
 delay(100);
    digitalWrite(DE,LOW);  // Transmit Enabled
    digitalWrite(RE,LOW); 
 while (Soft_Serial.available() > 0) {
    byte temp = Soft_Serial.read();
    incomingByteB[no_ByteB] = temp;
    no_ByteB++;
 }
 Serial.println("Response:");
 for (int i = 0; i < no_ByteB; i++) {
   Serial.print(String(incomingByteB[i], HEX) + " ");
 }
 no_ByteB = 0;
 Serial.println(" \n ** End Bootcommand ** ");
}


void readModbus() {
  byte sendBuffer[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x04, 0x45, 0xBE}; 
    Serial.println(" ** inside readModbus function ** ");
    digitalWrite(DE,HIGH);  // Transmit Enabled
    digitalWrite(RE,HIGH);  
    Soft_Serial.write(sendBuffer, sizeof(sendBuffer));
    digitalWrite(DE,LOW);   // Receive Enabled
    digitalWrite(RE,LOW);  
    delay(100);
    Serial.println("  Response inside readmodbus function ");
    while (Soft_Serial.available() > 0) {
    byte temp = Soft_Serial.read();
    Serial.println("Response: " + String(temp, DEC) + " " + String(temp, HEX));
    incomingByteV[no_ByteV] = temp;
    no_ByteV++;
  }
    Serial.println(" \n ** End readModbus ** ");
}

//________________________________________LOOP_________________________________________________________________

void loop() {
  disableEchoMode(); 
  Serial.println("        ***         ");
  delay(8000);
  readModbus();
//digitalWrite(LED_PIN, HIGH);

/*
  Serial.println(" ** here response should be displayed ");
 if (no_ByteV >= 12) {
    // Check if there are at least 12 bytes in the response

    // Extract the dissolved oxygen and temperature values
    byte doValueH = incomingByteV[3];
    byte doValueL = incomingByteV[4];
    byte tempValueH = incomingByteV[7];
    byte tempValueL = incomingByteV[8];

    // Combine the high and low bytes to form 16-bit values
    uint16_t doValueHex = (doValueH << 8) | doValueL;
    uint16_t tempValueHex = (tempValueH << 8) | tempValueL;

    // Extract the decimal points
    byte doDecimals = incomingByteV[5];
    byte tempDecimals = incomingByteV[9];

    // Calculate the actual values
    float doValue = static_cast<float>(doValueHex) / pow(10, doDecimals);
    float tempValue = static_cast<float>(tempValueHex) / pow(10, tempDecimals);

    // Display the results
    Serial.print("Dissolved Oxygen: ");
    Serial.print(doValue, 2);  // Display with 2 decimal places
    Serial.println(" mg/L");
    Serial.print("Temperature: ");
    Serial.print(tempValue, 1);  // Display with 1 decimal place
    Serial.println(" °C");

    no_ByteV = 0;  // Reset the byte count for the next reading
  } */
  delay(5000);
}


int hexToDec(String hexString) {
 int decValue = 0, nextInt;
 for (int i = 0; i < hexString.length(); i++) {
    nextInt = (int)hexString.charAt(i) - 48; // Subtract 48 from ASCII value
    decValue = (decValue << 4) + nextInt;
 }
 return decValue;
}

Re: ESP32 Modbus RS485 sensor not responding

Posted: Mon Nov 13, 2023 1:53 pm
by ESP_alisitsyn
Hello,

Do you have the name of sensor and the Modbus protocol mapping table or the manual for this sensor?
If not, it will be very difficult to get the information. Please provide more information and I will try to help you to configure the device with esp-modbus. Also please describe you interface connection. Thanks.

Re: ESP32 Modbus RS485 sensor not responding

Posted: Mon Nov 13, 2023 3:15 pm
by yasmineK
Hello,
Thanks for your response,
Here is the datasheet of the sensor: https://files.seeedstudio.com/wiki/Diss ... Sensor.pdf

Here is a simulation of the circuit connection;
I am using a TTL converter MAX 485, noting that the converter needs 5V and the sensor needs 12V, I used two Buck-Boost converters to make sure they get the needed voltage.


Thanks again

Re: ESP32 Modbus RS485 sensor not responding

Posted: Mon Nov 13, 2023 3:28 pm
by yasmineK
Hello,

Thanks for your response,
Here is the datasheet of the sensor: https://files.seeedstudio.com/wiki/Diss ... Sensor.pdf

Here is the circuit of the esp32 and the TTL converter MAX485
Noting that the converter needs 5V and the sensor 12V, I used Buck-Boost Converters to make sure they get the needed voltage.

Thanks again

Re: ESP32 Modbus RS485 sensor not responding

Posted: Thu Nov 23, 2023 10:02 am
by ESP_alisitsyn
Hello yasmineK,

Thank you for the information. You can use the modbus master example here: https://github.com/espressif/esp-idf/tr ... /mb_master to communicate with your device using RTU mode.

https://docs.espressif.com/projects/esp ... t-started/

https://docs.espressif.com/projects/esp ... odbus.html

You can use more simple code for the example like below: (you can simply change the source of `master.c` original example as below)

Code: Select all

#include "mbcontroller.h"   // for common Modbus defines
#include "string.h"
#include "esp_log.h"

#define MASTER_MAX_CIDS 2
#define MASTER_MAX_RETRY 100
#define MASTER_PORT_NUM 2
#define MASTER_SPEED 9600
#define MASTER_TAG "MODBUS_MASTER"
#define MB_UART_RXD_PIN 22 // Connect to RO pin on your RS485 adapter
#define MB_UART_TXD_PIN 23 // DI pin on RS485 adapter
#define MB_UART_RTS_PIN 18 // DE-RE pins connected together

#define MASTER_CHECK(a, ret_val, str, ...) \
    if (!(a)) { \
        ESP_LOGE(MASTER_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
        return (ret_val); \
    }

#define STR(fieldname) ((const char*)( fieldname ))
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }

// Enumeration of modbus slave addresses accessed by master device
enum {
    MB_DEVICE_ADDR = 55
};

// Enumeration of all supported CIDs for device (used in parameter definition table)
enum {
    CID_DEV_REG0 = 0,
    CID_DEV_REG1
};

// Example Data (Object) Dictionary for Modbus parameters
const mb_parameter_descriptor_t device_parameters[] = {
    // CID, Name, Units, Modbus addr, register type, Modbus Reg Start Addr, Modbus Reg read length, 
    // Instance offset (NA), Instance type, Instance length (bytes), Options (NA), Permissions
    { CID_DEV_REG0, STR("Measurements"), STR("mg/L value x100"), MB_DEVICE_ADDR, MB_PARAM_HOLDING, 0, 1,
                    0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER },
    { CID_DEV_REG1, STR("Temperature value"), STR("Deg C"), MB_DEVICE_ADDR, MB_PARAM_HOLDING, 2, 1,
                    0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }
};

// Calculate number of parameters in the table
const uint16_t num_device_parameters = (sizeof(device_parameters)/sizeof(device_parameters[0]));

static esp_err_t read_modbus_parameter(uint16_t cid, uint16_t *par_data)
{
    const mb_parameter_descriptor_t* param_descriptor = NULL;
    
    esp_err_t err = mbc_master_get_cid_info(cid, &param_descriptor);
    if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
        uint8_t type = 0;
        err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
                                                        (uint8_t*)par_data, &type);
        if (err == ESP_OK) {
            ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = (0x%04x) parameter read successful.",
                                     param_descriptor->cid,
                                     (char*)param_descriptor->param_key,
                                     (char*)param_descriptor->param_units,
                                     *(uint16_t*)par_data);
        }
        else 
        {
            ESP_LOGE(MASTER_TAG, "Characteristic #%d %s (%s), parameter read fail.", 
                                    param_descriptor->cid,
                                    (char*)param_descriptor->param_key,
                                    (char*)param_descriptor->param_units);
        }
    }
    return err;  
}

static esp_err_t write_modbus_parameter(uint16_t cid, uint16_t *par_data)
{
    const mb_parameter_descriptor_t* param_descriptor = NULL;
    
    esp_err_t err = mbc_master_get_cid_info(cid, &param_descriptor);
    if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
        uint8_t type = 0; // type of parameter from dictionary
        err = mbc_master_set_parameter(cid, (char*)param_descriptor->param_key,
                                                        (uint8_t*)par_data, &type);
        if (err == ESP_OK) {
            ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = (0x%04x), write successful.",
                                        param_descriptor->cid,
                                        (char*)param_descriptor->param_key,
                                        (char*)param_descriptor->param_units,
                                        *(uint16_t*)par_data);
        } else {
            ESP_LOGE(MASTER_TAG, "Characteristic #%d (%s) write fail, err = 0x%x (%s).",
                                    param_descriptor->cid,
                                    (char*)param_descriptor->param_key,
                                    (int)err,
                                    (char*)esp_err_to_name(err));
        }
    }
    return err;  
}

// This is user function to read and write modbus holding registers
static void master_read_write_func(void *arg)
{
    esp_err_t err = ESP_OK;
    uint16_t register_data = 0;
    
    ESP_LOGI(MASTER_TAG, "Start modbus test...");
    
    for(uint16_t retry = 0; retry <= MASTER_MAX_RETRY; retry++) {
        // Simply read your register here CID_DEV_REG0 - one register from address 0 (see device_parameters)
        err = read_modbus_parameter(CID_DEV_REG0, &register_data);   
        if (err == ESP_OK) {
            // Read ok the readings are already printed in read function, you can use them here in register_data
        }
        err = read_modbus_parameter(CID_DEV_REG1, &register_data);   
        if (err == ESP_OK) {
            // Read Ok
        }
    }
    ESP_LOGI(MASTER_TAG, "Modbus test is completed.");
}

// Modbus master initialization
static esp_err_t master_init(void)
{
    // Initialize and start Modbus controller
    mb_communication_info_t comm = {
            .port = MASTER_PORT_NUM,
            .mode = MB_MODE_RTU,
            .baudrate = MASTER_SPEED,
            .parity = MB_PARITY_NONE
    };
    void* master_handler = NULL;

    esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler);
    MASTER_CHECK((master_handler != NULL), ESP_ERR_INVALID_STATE,
                                "mb controller initialization fail.");
    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
                            "mb controller initialization fail, returns(0x%x).",
                            (uint32_t)err);
    err = mbc_master_setup((void*)&comm);
    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
                            "mb controller setup fail, returns(0x%x).",
                            (uint32_t)err);

    // Set UART pin numbers
    err = uart_set_pin(MASTER_PORT_NUM, MB_UART_TXD_PIN, MB_UART_RXD_PIN,
                            MB_UART_RTS_PIN, UART_PIN_NO_CHANGE);

    err = mbc_master_start();
    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
                            "mb controller start fail, returns(0x%x).",
                            (uint32_t)err);

    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
            "mb serial set pin failure, uart_set_pin() returned (0x%x).", (uint32_t)err);
    // Set driver mode to Half Duplex
    err = uart_set_mode(MASTER_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
            "mb serial set mode failure, uart_set_mode() returned (0x%x).", (uint32_t)err);

    vTaskDelay(5);
    err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
                                "mb controller set descriptor fail, returns(0x%x).",
                                (uint32_t)err);
    ESP_LOGI(MASTER_TAG, "Modbus master stack initialized...");
    return err;
}

void app_main(void)
{
    // Initialization of device peripheral and objects
    ESP_ERROR_CHECK(master_init());
    vTaskDelay(10);
    
    // Write registers to predefined state
    master_read_write_func(NULL);   
}
Please connect your adapter as below (recommended but you can change it to your pins):
#define MB_UART_RXD_PIN 22 // Connect to RO pin on your RS485 adapter
#define MB_UART_TXD_PIN 23 // DI pin on RS485 adapter
#define MB_UART_RTS_PIN 18 // DE-RE pins connected together

You can connect the VCC pin of your adapter to +5V (or 3.3V depends on adapter IC) pin on the ESP32 board. A-B pins of adapter connected to the A-B pins of your sensor. It is better to use the adapter with max483 IC with 3.3V output but for investigation purpose the 5V max or AD485 IC can also work normally with ESP32.

In this example you should get the measurements from your device.
See the communication log and post it here.
Thanks.

Re: ESP32 Modbus RS485 sensor not responding

Posted: Thu Nov 23, 2023 12:42 pm
by ESP_alisitsyn
Hello yasmineK,

Thank you for the information. You can use the modbus master example here: https://github.com/espressif/esp-idf/tr ... /mb_master to communicate with your device using RTU mode.

https://docs.espressif.com/projects/esp ... t-started/

https://docs.espressif.com/projects/esp ... odbus.html

You can use more simple code for the example like below: (you can simply change the source of `master.c` original example as below)

Code: Select all

#include "mbcontroller.h"   // for common Modbus defines
#include "string.h"
#include "esp_log.h"

#define MASTER_MAX_CIDS 2
#define MASTER_MAX_RETRY 100
#define MASTER_PORT_NUM 2
#define MASTER_SPEED 9600
#define MASTER_TAG "MODBUS_MASTER"
#define MB_UART_RXD_PIN 22 // Connect to RO pin on your RS485 adapter
#define MB_UART_TXD_PIN 23 // DI pin on RS485 adapter
#define MB_UART_RTS_PIN 18 // DE-RE pins connected together

#define MASTER_CHECK(a, ret_val, str, ...) \
    if (!(a)) { \
        ESP_LOGE(MASTER_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
        return (ret_val); \
    }

#define STR(fieldname) ((const char*)( fieldname ))
#define OPTS(min_val, max_val, step_val) { .opt1 = min_val, .opt2 = max_val, .opt3 = step_val }

// Enumeration of modbus slave addresses accessed by master device
enum {
    MB_DEVICE_ADDR = 55
};

// Enumeration of all supported CIDs for device (used in parameter definition table)
enum {
    CID_DEV_REG0 = 0,
    CID_DEV_REG1
};

// Example Data (Object) Dictionary for Modbus parameters
const mb_parameter_descriptor_t device_parameters[] = {
    // CID, Name, Units, Modbus addr, register type, Modbus Reg Start Addr, Modbus Reg read length,
    // Instance offset (NA), Instance type, Instance length (bytes), Options (NA), Permissions
    { CID_DEV_REG0, STR("Measurements"), STR("mg/L value x100"), MB_DEVICE_ADDR, MB_PARAM_HOLDING, 0, 1,
                    0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER },
    { CID_DEV_REG1, STR("Temperature value"), STR("Deg C"), MB_DEVICE_ADDR, MB_PARAM_HOLDING, 2, 1,
                    0, PARAM_TYPE_U16, 2, OPTS( 0,0,0 ), PAR_PERMS_READ_WRITE_TRIGGER }
};

// Calculate number of parameters in the table
const uint16_t num_device_parameters = (sizeof(device_parameters)/sizeof(device_parameters[0]));

static esp_err_t read_modbus_parameter(uint16_t cid, uint16_t *par_data)
{
    const mb_parameter_descriptor_t* param_descriptor = NULL;

    esp_err_t err = mbc_master_get_cid_info(cid, &param_descriptor);
    if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
        uint8_t type = 0;
        err = mbc_master_get_parameter(cid, (char*)param_descriptor->param_key,
                                                        (uint8_t*)par_data, &type);
        if (err == ESP_OK) {
            ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = (0x%04x) parameter read successful.",
                                     param_descriptor->cid,
                                     (char*)param_descriptor->param_key,
                                     (char*)param_descriptor->param_units,
                                     *(uint16_t*)par_data);
        }
        else
        {
            ESP_LOGE(MASTER_TAG, "Characteristic #%d %s (%s), parameter read fail.",
                                    param_descriptor->cid,
                                    (char*)param_descriptor->param_key,
                                    (char*)param_descriptor->param_units);
        }
    }
    return err;
}

static esp_err_t write_modbus_parameter(uint16_t cid, uint16_t *par_data)
{
    const mb_parameter_descriptor_t* param_descriptor = NULL;

    esp_err_t err = mbc_master_get_cid_info(cid, &param_descriptor);
    if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) {
        uint8_t type = 0; // type of parameter from dictionary
        err = mbc_master_set_parameter(cid, (char*)param_descriptor->param_key,
                                                        (uint8_t*)par_data, &type);
        if (err == ESP_OK) {
            ESP_LOGI(MASTER_TAG, "Characteristic #%d %s (%s) value = (0x%04x), write successful.",
                                        param_descriptor->cid,
                                        (char*)param_descriptor->param_key,
                                        (char*)param_descriptor->param_units,
                                        *(uint16_t*)par_data);
        } else {
            ESP_LOGE(MASTER_TAG, "Characteristic #%d (%s) write fail, err = 0x%x (%s).",
                                    param_descriptor->cid,
                                    (char*)param_descriptor->param_key,
                                    (int)err,
                                    (char*)esp_err_to_name(err));
        }
    }
    return err;
}

// This is user function to read and write modbus holding registers
static void master_read_write_func(void *arg)
{
    esp_err_t err = ESP_OK;
    uint16_t register_data = 0;

    ESP_LOGI(MASTER_TAG, "Start modbus test...");

    for(uint16_t retry = 0; retry <= MASTER_MAX_RETRY; retry++) {
        // Simply read your register here CID_DEV_REG0 - one register from address 0 (see device_parameters)
        err = read_modbus_parameter(CID_DEV_REG0, &register_data);
        if (err == ESP_OK) {
            // Read ok the readings are already printed in read function, you can use them here in register_data
        }
        err = read_modbus_parameter(CID_DEV_REG1, &register_data);
        if (err == ESP_OK) {
            // Read Ok
        }
    }
    ESP_LOGI(MASTER_TAG, "Modbus test is completed.");
}

// Modbus master initialization
static esp_err_t master_init(void)
{
    // Initialize and start Modbus controller
    mb_communication_info_t comm = {
            .port = MASTER_PORT_NUM,
            .mode = MB_MODE_RTU,
            .baudrate = MASTER_SPEED,
            .parity = MB_PARITY_NONE
    };
    void* master_handler = NULL;

    esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler);
    MASTER_CHECK((master_handler != NULL), ESP_ERR_INVALID_STATE,
                                "mb controller initialization fail.");
    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
                            "mb controller initialization fail, returns(0x%x).", (int)err);
    err = mbc_master_setup((void*)&comm);
    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
                            "mb controller setup fail, returns(0x%x).", (int)err);

    // Set UART pin numbers
    err = uart_set_pin(MASTER_PORT_NUM, MB_UART_TXD_PIN, MB_UART_RXD_PIN,
                            MB_UART_RTS_PIN, UART_PIN_NO_CHANGE);

    err = mbc_master_start();
    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
                            "mb controller start fail, returns(0x%x).", (int)err);

    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
            "mb serial set pin failure, uart_set_pin() returned (0x%x).", (int)err);
    // Set driver mode to Half Duplex
    err = uart_set_mode(MASTER_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
            "mb serial set mode failure, uart_set_mode() returned (0x%x).", (int)err);

    vTaskDelay(5);
    err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters);
    MASTER_CHECK((err == ESP_OK), ESP_ERR_INVALID_STATE,
                                "mb controller set descriptor fail, returns(0x%x).",
                                (int)err);
    ESP_LOGI(MASTER_TAG, "Modbus master stack initialized...");
    return err;
}

void app_main(void)
{
    // Initialization of device peripheral and objects
    ESP_ERROR_CHECK(master_init());
    vTaskDelay(10);

    // Write registers to predefined state
    master_read_write_func(NULL);
}
Please connect your adapter as below (recommended but you can change it to your pins):
#define MB_UART_RXD_PIN 22 // Connect to RO pin on your RS485 adapter
#define MB_UART_TXD_PIN 23 // DI pin on RS485 adapter
#define MB_UART_RTS_PIN 18 // DE-RE pins connected together

You can connect the VCC pin of your adapter to +5V (or 3.3V depends on adapter IC) pin on the ESP32 board. A-B pins of adapter connected to the A-B pins of your sensor. It is better to use the adapter with max483 IC with 3.3V output but for investigation purpose the 5V max or AD485 IC can also work normally with ESP32.

In this example you should get the measurements from your device.
See the communication log and post it here.
Thanks.