Connecting to slave with TCP Modbus on ESP32

cslehel
Posts: 9
Joined: Tue Nov 09, 2021 5:25 pm

Connecting to slave with TCP Modbus on ESP32

Postby cslehel » Tue Nov 09, 2021 5:31 pm

Hi,
i would like a little help.

I have the following code:

Code: Select all

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

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

// Enumeration of modbus slave addresses accessed by master device
enum {
    MB_DEVICE_ADDR1 = 1,
    MB_SLAVE_COUNT
};

// Enumeration of all supported CIDs for device
enum {
    CID_STATUS = 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 error_holder = mbc_master_get_cid_info( cid, &param_descriptor );

    if ( ( error_holder != ESP_ERR_NOT_FOUND ) && ( param_descriptor != NULL ) ) {

        uint8_t type = 0;

        error_holder = mbc_master_get_parameter( cid, (char*)param_descriptor->param_key, (uint8_t*)par_data, &type );

        if ( error_holder == ESP_OK ) {

            ESP_LOGI( 
                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( 
                TAG, 
                "Characteristic #%d %s (%s), parameter read fail.", 
                param_descriptor->cid,
                (char*)param_descriptor->param_key,
                (char*)param_descriptor->param_units
            );

        }

    }

    return error_holder;  
}



static void master_read( void *parameters ) {
    esp_err_t error_holder = ESP_OK;
    uint16_t register_data = 0;
    
    ESP_LOGI( TAG, "Start modbus test..." );
    
    error_holder = read_modbus_parameter( CID_STATUS, &register_data );   

    if ( error_holder != ESP_OK ) {

        ESP_LOGE( TAG, "mb read holder fail, err=%x.", error_holder );

    }

    ESP_LOGI( TAG, "Modbus test is completed." );
}



// Modbus master initialization
static esp_err_t master_init( void ) {
    esp_err_t error_holder = ESP_OK;

    // Example Data Dictionary for Modbus parameters in 1 slave in the segment
    mb_parameter_descriptor_t device_parameters[] = {
        {
            CID_STATUS, /* CID */
            STR( "Status" ), /* Name */
            STR( "x" ), /* Units */
            MB_DEVICE_ADDR1, /* Modbus Address */
            MB_PARAM_HOLDING, /* Register Type */
            32089, /* Modbus Reg Start Address */
            1, /* Modbus Reg Read Length */
            0, /* Instance Offset (NA) */
            PARAM_TYPE_U16, /* Instance Type */
            1, /* Instance Length (bytes) */
            OPTS( 0, 0, 0 ), /* Options (NA) */
            PAR_PERMS_READ_TRIGGER /* Permissions */
        }
    };

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

    void* master_handler = NULL; // Pointer to allocate interface structure

    // Initialization of Modbus slave for TCP
    error_holder = mbc_master_init_tcp( &master_handler );

    if ( master_handler == NULL || error_holder != ESP_OK ) {
        ESP_LOGE( TAG, "mb controller initialization fail." );
    }

    char* slave_ip_address_table[ 2 ] = { "192.168.1.186", NULL };

     mb_communication_info_t comm_info = {
        .ip_port = 502,
        .ip_addr_type = MB_IPV4,
        .ip_mode = MB_MODE_TCP,
        .ip_addr = slave_ip_address_table,
        .ip_netif_ptr = s_esp_netif
    };

    ESP_ERROR_CHECK( mbc_master_setup( (void*)&comm_info ) );

    ESP_ERROR_CHECK( mbc_master_set_descriptor( &device_parameters[ 0 ], num_device_parameters ) );

    ESP_LOGI( TAG, "Set Descriptor" );

    vTaskDelay( 1000 / portTICK_PERIOD_MS );

    error_holder = mbc_master_start();
    if ( error_holder != ESP_OK ) {
        ESP_LOGE( TAG, "mb controller start fail, err=%x.", error_holder );
    }

    ESP_LOGI( TAG, "Start Modbus Master" );

    vTaskDelay( 1000 / portTICK_PERIOD_MS );

    error_holder = mbc_master_set_descriptor( &device_parameters[ 0 ], num_device_parameters );
    MASTER_CHECK( ( error_holder == ESP_OK ), ESP_ERR_INVALID_STATE, "mb controller set descriptor fail, returns(0x%x).", (uint32_t)error_holder );

    vTaskDelay( 1000 / portTICK_PERIOD_MS );

    ESP_LOGI( TAG, "Modbus master stack initialized..." );

    return error_holder;

}
The modbus connection doeas not get past of MB_TCP_MASTER_PORT: Connecting to slaves...

0;32mI (2107) Modbus Power: Initializing Modbus Master
0;32mI (2107) Modbus Power: Set Descriptor
0;32mI (3107) MB_TCP_MASTER_PORT: TCP master stack initialized.
0;32mI (3107) MB_TCP_MASTER_PORT: Host[IP]: "192.168.1.186"[192.168.1.186]
0;32mI (3107) MB_TCP_MASTER_PORT: Add slave IP: 192.168.1.186
0;32mI (3107) MB_TCP_MASTER_PORT: Connecting to slaves...
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.


I made a Python script which is working fine:

Code: Select all

client = ModbusClient( '192.168.101.186', port = 502, unit_id = 0 )

client_connection = client.connect()
time.sleep( 1 )

if client_connection:
    try:
        device_status_register = client.read_holding_registers( 0x7D59, 0x01, unit = 1 )
    except Exception:
        print( 'Exception' )
    else:
        print( 'Device Status:', type_status( device_status_register.registers[ 0 ] ) )
    finally:
        client.close()
Can somebody help me to connect to the modbus slave ?

Thank you for your time.

cslehel
Posts: 9
Joined: Tue Nov 09, 2021 5:25 pm

Re: Connecting to slave with TCP Modbus on ESP32

Postby cslehel » Wed Nov 10, 2021 11:08 am

The problem was the ip address.

But now i have another problem.

Made a simple request to read a register after the master_init.

Code: Select all

    uint16_t value = 0;

    mb_param_request_t request;
    request.slave_addr = 1;
    request.command = 3;
    request.reg_start = 32089;
    request.reg_size = 1;

    esp_err_t err = mbc_master_send_request( &request, &value );
With the Python script i am getting the value below 1 second but with the ESP32 i am getting the following error:

(6750) MB_CONTROLLER_MASTER: mbc_master_send_request(103): Master send request failure error=(0x107) (ESP_ERR_TIMEOUT)

I changed CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000 in the sdkconfig.

ESP_alisitsyn
Posts: 203
Joined: Fri Feb 01, 2019 4:02 pm
Contact:

Re: Connecting to slave with TCP Modbus on ESP32

Postby ESP_alisitsyn » Wed Nov 10, 2021 11:54 am

Hi @cslehel,

This line means that the master has been configured to connect to slave `192.168.1.186` but can not connect to this modbus slave because it is not registered in the network correctly.

Code: Select all

0;32mI (3107) MB_TCP_MASTER_PORT: Add slave IP: 192.168.1.186
0;32mI (3107) MB_TCP_MASTER_PORT: Connecting to slaves...
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
So, what I can see in your code above, the ESP32 Modbus master is trying to connect to slave with IP = '192.168.1.186' but your script is connecting to slave '192.168.101.186' which is different IP. Please check the IP address of the Modbus Slave example which is showed in its connection log and set it in the IP table of Modbus master example.

Code: Select all

I (4465) example_connect: Connected to example_connect: sta
I (4475) example_connect: - IPv4 address: 192.168.1.21 <<<<<<<< The IP address of the slave
I (4475) example_connect: - IPv6 address: fe80:0000:0000:0000:7edf:a1ff:fe00:4039, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (4495) MB_TCP_SLAVE_PORT: Socket (#54), listener  on port: 502, errno=0
I (4495) MB_TCP_SLAVE_PORT: Protocol stack initialized.
I (4505) SLAVE_TEST: Modbus slave stack initialized.
Also please check the SSID and password in the kconfig options of Modbus slave example. You can use the

Code: Select all

CONFIG_MB_MDNS_IP_RESOLVER
kconfig option to allow master to find the IP address of the connected slaves.

Code: Select all

client = ModbusClient( '192.168.101.186', port = 502, unit_id = 0 )
Let me know if you still have issues with this.

cslehel
Posts: 9
Joined: Tue Nov 09, 2021 5:25 pm

Re: Connecting to slave with TCP Modbus on ESP32

Postby cslehel » Wed Nov 10, 2021 1:02 pm

Thank You very much for your help.
I corrected the IP address, but now i am getting the following error:

Code: Select all

(2889) Modbus Power: Reading Holding Register
(5889) MB_CONTROLLER_MASTER: mbc_master_send_request(103): Master send request failure error=(0x107) (ESP_ERR_TIMEOUT)
(5889) Modbus Power: Holding Register Read Fail, error: 107
I simplified the code:

Code: Select all

    uint16_t register_value = 0;

    ESP_LOGI( TAG, "Reading Holding Register" );

    mb_param_request_t request;
    request.slave_addr = 1;
    request.command = 3;
    request.reg_start = 32089;
    request.reg_size = 1;

     esp_err_t err = mbc_master_send_request( &request, &register_value );
I modified CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000

ESP_alisitsyn
Posts: 203
Joined: Fri Feb 01, 2019 4:02 pm
Contact:

Re: Connecting to slave with TCP Modbus on ESP32

Postby ESP_alisitsyn » Sun Nov 14, 2021 9:35 pm

Hi @cslehel,

The timeout means that the slave did not get the request and does not respond to your request.
Could you give me more information about your slave? The register map of the slave would help to find the reason for this fail.
According to the address of the register if it follows the Modbus address notation https://www.fernhillsoftware.com/help/d ... ormat.html it can be the INPUT register instead (3x register area).

I recommend to change the address of the register in master to

Code: Select all

mb_parameter_descriptor_t device_parameters[] = {
        {
            CID_STATUS, /* CID */
            STR( "Status" ), /* Name */
            STR( "x" ), /* Units */
            MB_DEVICE_ADDR1, /* Modbus Address */
            MB_PARAM_INPUT, /* Register Type */ <<<<<<<<<<<<<<<<<< change to INPUT register area
            2089, /* Modbus Reg Start Address */ <<<<<<<<<<<<<<<<< 2089
            1, /* Modbus Reg Read Length */
            0, /* Instance Offset (NA) */
            PARAM_TYPE_U16, /* Instance Type */
            1, /* Instance Length (bytes) */
            OPTS( 0, 0, 0 ), /* Options (NA) */
            PAR_PERMS_READ_TRIGGER /* Permissions */
        }
    };
Also, I would recommend to check your master code with https://modbustools.com/modbus_slave.html.

Thank you.

cslehel
Posts: 9
Joined: Tue Nov 09, 2021 5:25 pm

Re: Connecting to slave with TCP Modbus on ESP32

Postby cslehel » Tue Nov 16, 2021 8:14 am

Hello alisitsyn,

i appreciate your help.

I will try your suggestion in the following days.

I tried with an arduino library eModbus (https://emodbus.github.io/).
The following code is working most of the time.

Code: Select all

IPAddress ip = { 192, 168, 101, 186 };
uint16_t port = 502;

ModbusClientTCPasync MB( ip, port );

MB.onDataHandler( &handleData );
MB.onErrorHandler( &handleError );
MB.setTimeout( 10000 );
MB.setIdleTimeout( 60000 );

MB.addRequest( 32080, 1, READ_HOLD_REGISTER, 32080, 2 )
The device i am polling is a Huawei inverter.
You can find the documentation here:
https://javierin.com/wp-content/uploads ... itions.pdf

ESP_alisitsyn
Posts: 203
Joined: Fri Feb 01, 2019 4:02 pm
Contact:

Re: Connecting to slave with TCP Modbus on ESP32

Postby ESP_alisitsyn » Tue Nov 16, 2021 9:22 am

@cslehel,

Thank you for information. This confirms what I stated before. The device uses Modbus address notation and addresses

Code: Select all

30001 - 39999
are Input registers instead of holding. The library you mentioned uses the Modbus notation for addresses but Esp-Modbus library uses zero based address offset in each area. So, all addresses in the manual starting from 3xxxx are Modbus input registers (read only), Addresses started from 4xxxx - are holding registers. The examples of setting in data dictionary for your device are below:

Code: Select all

mb_parameter_descriptor_t device_parameters[] = {
        {
            CID_SYSTIME, /* CID */
            STR( "Time" ), /* Name */
            STR( "x" ), /* Units */
            MB_DEVICE_ADDR1, /* Modbus Address */
            MB_PARAM_HOLDING, /* Register Type */ <<<<<<<<<<<<<<<<<< Holding register area for register `System time`
            0, /* Modbus Reg Start Address */ <<<<<<<<<<<<<<<<< 0- corresponds to address 40000 - System time
            2, /* Modbus Reg Read Length */ <<<<<<<<<<<< 2 registers for U32 type
            0, /* Instance Offset (NA) */
            PARAM_TYPE_U32, /* Instance Type */
            4, /* Instance Length (bytes) */ <<<<<<<<<<<<<<<<<<<<<<<<<< Instance length in bytes = 4, because of U32 type
            OPTS( 0, 0, 0 ), /* Options (NA) */
            PAR_PERMS_READ_TRIGGER /* Permissions */
        }
    }; 

Code: Select all

mb_parameter_descriptor_t device_parameters[] = {
        {
            CID_STATUS, /* CID */
            STR( "Status" ), /* Name */
            STR( "x" ), /* Units */
            MB_DEVICE_ADDR1, /* Modbus Address */
            MB_PARAM_INPUT, /* Register Type */ <<<<<<<<<<<<<<<<<< change to INPUT register area
            2089, /* Modbus Reg Start Address */ <<<<<<<<<<<<<<<<< 2089 - corresponds to address 32089 - Device status
            1, /* Modbus Reg Read Length */  <<<<<<<<<<<<<<<<<< Corresponds to Quantity field in the register map
            0, /* Instance Offset (NA) */
            PARAM_TYPE_U16, /* Instance Type */ <<<<<<<<<< one register
            2, /* Instance Length (bytes) */ <<<<<<<<<<<< The value is U16 = 1 register = 2 bytes
            OPTS( 0, 0, 0 ), /* Options (NA) */
            PAR_PERMS_READ_TRIGGER /* Permissions */
        }
    }; 

Using the approach you can add all required parameters into data dictionary and use your functions with corresponded CID as input parameter

Code: Select all

read(write)_modbus_parameter( uint16_t cid, uint16_t *par_data )
to read/write data accordingly.

Let me know the results. Thanks.

cslehel
Posts: 9
Joined: Tue Nov 09, 2021 5:25 pm

Re: Connecting to slave with TCP Modbus on ESP32

Postby cslehel » Sat Nov 20, 2021 12:41 pm

I tried your suggestion but without success. Can you take a look on more time please ?

Code: Select all

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


enum {
    MB_DEVICE_ADDR1 = 1,
    MB_SLAVE_COUNT
};

enum {
    CID_STATUS = 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 error_holder = mbc_master_get_cid_info( cid, &param_descriptor );

    if ( ( error_holder != ESP_ERR_NOT_FOUND ) && ( param_descriptor != NULL ) ) {

        uint8_t type = 0;

        error_holder = mbc_master_get_parameter( cid, (char*)param_descriptor->param_key, (uint8_t*)par_data, &type );

        if ( error_holder == ESP_OK ) {

            ESP_LOGI( 
                TAG, 
                "Characteristic #%d %s (%s) Value = (0x%04x) Parameter Read Success.",
                param_descriptor->cid,
                (char*)param_descriptor->param_key,
                (char*)param_descriptor->param_units,
                *(uint16_t*)par_data
            );

        } else {

            ESP_LOGE( 
                TAG, 
                "Characteristic #%d %s (%s), Parameter Read Fail.", 
                param_descriptor->cid,
                (char*)param_descriptor->param_key,
                (char*)param_descriptor->param_units
            );

        }

    }

    return error_holder;  
}


static void master_read( void *parameters ) {
    esp_err_t error_holder = ESP_OK;
    uint16_t register_data = 0;

    ESP_LOGI( TAG, "MB Test Starting" );

    error_holder = read_modbus_parameter( CID_STATUS, &register_data );   

    if ( error_holder != ESP_OK ) {

        ESP_LOGE( TAG, "MB Read Fail, ERROR = %x.", error_holder );

    }

    ESP_LOGI( TAG, "MB Test Completed." );
}


static esp_err_t master_init( void ) {
    esp_err_t error_holder = ESP_OK;

    s_modbus_event_group = xEventGroupCreate();

    char* slave_ip_address_table[ 2 ] = { 
        "192.168.101.186", 
        NULL 
    };

    void* master_handler = NULL;

     mb_communication_info_t comm_info = {
        .ip_port = 502,
        .ip_addr_type = MB_IPV4,
        .ip_mode = MB_MODE_TCP,
        .ip_addr = (void*)slave_ip_address_table,
        .ip_netif_ptr = s_esp_netif
    };


    // Data Dictionary
    mb_parameter_descriptor_t device_parameters[] = {
        {
            CID_STATUS,
            STR( "Status" ),
            STR( "x" ),
            MB_DEVICE_ADDR1,
            MB_PARAM_INPUT,
            2089,
            1,
            0,
            PARAM_TYPE_U16,
            2,
            OPTS( 0, 0, 0 ),
            PAR_PERMS_READ_TRIGGER
        }
    };

    const uint16_t num_device_parameters = ( sizeof( device_parameters ) / sizeof( device_parameters[ 0 ] ) );

    error_holder = mbc_master_init_tcp( &master_handler );

    if ( master_handler == NULL || error_holder != ESP_OK ) {
        ESP_LOGE( TAG, "MB Controller Initialization Fail." );
    }

    ESP_ERROR_CHECK( mbc_master_setup( (void*)&comm_info ) );

    ESP_ERROR_CHECK( mbc_master_set_descriptor( &device_parameters[ 0 ], num_device_parameters ) );

    ESP_LOGI( TAG, "Set Descriptor" );

    vTaskDelay( 1000 / portTICK_PERIOD_MS );

    error_holder = mbc_master_start();
    if ( error_holder != ESP_OK ) {
        ESP_LOGE( TAG, "MB Controller Start Fail, ERROR = %x.", error_holder );
    }

    ESP_LOGI( TAG, "Start MB Master" );

    vTaskDelay( 1000 / portTICK_PERIOD_MS );

    error_holder = mbc_master_set_descriptor( &device_parameters[ 0 ], num_device_parameters );
    MASTER_CHECK( ( error_holder == ESP_OK ), ESP_ERR_INVALID_STATE, "MB Controller Set Descriptor Fail, returns(0x%x).", (uint32_t)error_holder );

    vTaskDelay( 1000 / portTICK_PERIOD_MS );

    ESP_LOGI( TAG, "MB Master Stack Initialized..." );

    master_read( NULL );
    vTaskDelay( 1000 / portTICK_PERIOD_MS );
    master_read( NULL );
    vTaskDelay( 1000 / portTICK_PERIOD_MS );
    master_read( NULL );

    return error_holder;

}
The results are:

Code: Select all

(1570) esp_netif_handlers: sta ip: 192.168.1.245, mask: 255.255.255.0, gw: 192.168.1.1
(1570) Modbus Power: got ip:192.168.1.245
(1570) Modbus Power: connected to ap SSID:x
(1680) Modbus Power: Initializing Modbus Master
(1680) Modbus Power: Set Descriptor
(2680) MB_TCP_MASTER_PORT: TCP master stack initialized.
(2680) MB_TCP_MASTER_PORT: Host[IP]: "192.168.101.186"[192.168.101.186]
(2680) MB_TCP_MASTER_PORT: Add slave IP: 192.168.101.186
(2680) MB_TCP_MASTER_PORT: Connecting to slaves...
(2730) MB_TCP_MASTER_PORT: Connected 1 slaves, start polling...
(2730) Modbus Power: Start MB Master
(4730) Modbus Power: MB Master Stack Initialized...
(4730) Modbus Power: MB Test Starting
(7730) MB_CONTROLLER_MASTER: mbc_master_get_parameter(83): Master get parameter failure, error=(0x107) (ESP_ERR_TIMEOUT).
(7730) Modbus Power: Characteristic #0 Status (x), Parameter Read Fail.
(7740) Modbus Power: MB Read Fail, ERROR = 107.
(7740) Modbus Power: MB Test Completed.
(8750) Modbus Power: MB Test Starting
(11750) MB_CONTROLLER_MASTER: mbc_master_get_parameter(83): Master get parameter failure, error=(0x107) (ESP_ERR_TIMEOUT).
(11750) Modbus Power: Characteristic #0 Status (x), Parameter Read Fail.
(11760) Modbus Power: MB Read Fail, ERROR = 107.
(11760) Modbus Power: MB Test Completed.
(12770) Modbus Power: MB Test Starting
(15770) MB_CONTROLLER_MASTER: mbc_master_get_parameter(83): Master get parameter failure, error=(0x107) (ESP_ERR_TIMEOUT).
(15770) Modbus Power: Characteristic #0 Status (x), Parameter Read Fail.
(15780) Modbus Power: MB Read Fail, ERROR = 107.
(15780) Modbus Power: MB Test Completed.

ESP_alisitsyn
Posts: 203
Joined: Fri Feb 01, 2019 4:02 pm
Contact:

Re: Connecting to slave with TCP Modbus on ESP32

Postby ESP_alisitsyn » Mon Nov 22, 2021 7:57 pm

@cslehel,

Thank you for the update.
The data dictionary looks good now but it can not be a local variable of the function but should be global or static variable instead. Please change it as below. Please check the master example for more information: https://github.com/espressif/esp-idf/bl ... ster.c#L99

The documentation that describes the data dictionary: https://docs.espressif.com/projects/esp ... ata-access

Code: Select all


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


enum {
    MB_DEVICE_ADDR1 = 1,
    MB_SLAVE_COUNT
};

enum {
    CID_STATUS = 0
};

// Data Dictionary
static mb_parameter_descriptor_t device_parameters[] = {
    {
        CID_STATUS,
        STR( "Status" ),
        STR( "x" ),
        MB_DEVICE_ADDR1,
        MB_PARAM_INPUT,
        2089,
        1,
        0,
        PARAM_TYPE_U16,
        2,
        OPTS( 0, 0, 0 ),
        PAR_PERMS_READ_TRIGGER
    }
};

const uint16_t num_device_parameters = ( sizeof( device_parameters ) / sizeof( device_parameters[ 0 ] ) );

char* slave_ip_address_table[ 2 ] = { 
    "192.168.101.186", 
    NULL 
};

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 error_holder = mbc_master_get_cid_info( cid, &param_descriptor );

    if ( ( error_holder != ESP_ERR_NOT_FOUND ) && ( param_descriptor != NULL ) ) {

        uint8_t type = 0;

        error_holder = mbc_master_get_parameter( cid, (char*)param_descriptor->param_key, (uint8_t*)par_data, &type );

        if ( error_holder == ESP_OK ) {

            ESP_LOGI( 
                TAG, 
                "Characteristic #%d %s (%s) Value = (0x%04x) Parameter Read Success.",
                param_descriptor->cid,
                (char*)param_descriptor->param_key,
                (char*)param_descriptor->param_units,
                *(uint16_t*)par_data
            );

        } else {

            ESP_LOGE( 
                TAG, 
                "Characteristic #%d %s (%s), Parameter Read Fail.", 
                param_descriptor->cid,
                (char*)param_descriptor->param_key,
                (char*)param_descriptor->param_units
            );

        }

    }

    return error_holder;  
}

static void master_read( void *parameters ) {
    esp_err_t error_holder = ESP_OK;
    uint16_t register_data = 0;

    ESP_LOGI( TAG, "MB Test Starting" );

    error_holder = read_modbus_parameter( CID_STATUS, &register_data );   

    if ( error_holder != ESP_OK ) {

        ESP_LOGE( TAG, "MB Read Fail, ERROR = %x.", error_holder );

    }

    ESP_LOGI( TAG, "MB Test Completed." );
}

static esp_err_t master_init( void ) {
    esp_err_t error_holder = ESP_OK;

    s_modbus_event_group = xEventGroupCreate();

    void* master_handler = NULL;

     mb_communication_info_t comm_info = {
        .ip_port = 502,
        .ip_addr_type = MB_IPV4,
        .ip_mode = MB_MODE_TCP,
        .ip_addr = (void*)slave_ip_address_table,
        .ip_netif_ptr = s_esp_netif
    };

    error_holder = mbc_master_init_tcp( &master_handler );

    if ( master_handler == NULL || error_holder != ESP_OK ) {
        ESP_LOGE( TAG, "MB Controller Initialization Fail." );
    }

    ESP_ERROR_CHECK( mbc_master_setup( (void*)&comm_info ) );

    ESP_ERROR_CHECK( mbc_master_set_descriptor( &device_parameters[ 0 ], num_device_parameters ) );

    ESP_LOGI( TAG, "Set Descriptor" );

    vTaskDelay( 1000 / portTICK_PERIOD_MS );

    error_holder = mbc_master_start();
    if ( error_holder != ESP_OK ) {
        ESP_LOGE( TAG, "MB Controller Start Fail, ERROR = %x.", error_holder );
    }

    ESP_LOGI( TAG, "Start MB Master" );

    vTaskDelay( 1000 / portTICK_PERIOD_MS );

    error_holder = mbc_master_set_descriptor( &device_parameters[ 0 ], num_device_parameters );
    MASTER_CHECK( ( error_holder == ESP_OK ), ESP_ERR_INVALID_STATE, "MB Controller Set Descriptor Fail, returns(0x%x).", (uint32_t)error_holder );

    vTaskDelay( 1000 / portTICK_PERIOD_MS );

    ESP_LOGI( TAG, "MB Master Stack Initialized..." );

    master_read( NULL );
    vTaskDelay( 1000 / portTICK_PERIOD_MS );
    master_read( NULL );
    vTaskDelay( 1000 / portTICK_PERIOD_MS );
    master_read( NULL );

    return error_holder;

}
The code should work just fine finally. I overviwed the code and do not see any other critical issues for now. Please consult the documentation and the tcp_master example (links above). Let me know if you still have issues.

Update: Your device according to manual is not standard or contain misleading information.
1. All values with starting address from 3xxxxx (RO) in the table 3.1 represent the Modbus Input registers and addresses starting from 4xxxx represent Holding registers. This is clear and corresponds to spec.
2. In spite of using the Modbus addressing notation the manual does not describe the command 0x04 (Read Input Registers) in Table 6.3, but specifies only the command 0x03 (Read Holding Registers) which looks very strange in this case. The example on page 79 confirms this variant of addressing (your original variant).
3. The device contain vendor specific functions (example 0x41).

Code: Select all

static mb_parameter_descriptor_t device_parameters[] = {
    {
        CID_STATUS,
        STR( "Status" ),
        STR( "x" ),
        MB_DEVICE_ADDR1,          
        MB_PARAM_HOLDING,
        32089 ,                     
        1,
        0,
        PARAM_TYPE_U16,
        2,
        OPTS( 0, 0, 0 ),
        PAR_PERMS_READ_TRIGGER
    }
};
Please try again the original variant of addressing in data dictionary (as per example on page 79) again after modification of code. Good luck.

cslehel
Posts: 9
Joined: Tue Nov 09, 2021 5:25 pm

Re: Connecting to slave with TCP Modbus on ESP32

Postby cslehel » Sat Nov 27, 2021 11:51 am

Hi alisitsyn,

thank you very much for your help.
I made the modifications but i am still getting the ESP_ERR_TIMEOUT error.

Tried with the mbc_master_send_request command too but without success:

Code: Select all

    mb_param_request_t request;
    request.slave_addr = 1;
    request.command = 3;
    request.reg_start = 32089;
    request.reg_size = 1;
My current parameter descriptor looks like:

Code: Select all

static mb_parameter_descriptor_t device_parameters[] = {
    {
        CID_STATUS,
        STR( "Status" ),
        STR( "x" ),
        MB_DEVICE_ADDR1,
        MB_PARAM_HOLDING,
        32089,
        1,
        0,
        PARAM_TYPE_U16,
        2,
        OPTS( 0, 0, 0 ),
        PAR_PERMS_READ
    }
};
const uint16_t num_device_parameters = ( sizeof( device_parameters ) / sizeof( device_parameters[ 0 ] ) );
I have the following settings in menuconfig:

Code: Select all

	[*] Enable Modbus stack support for TCP communication mode       
	(502)   Modbus TCP port number
	(5)     Maximum allowed connections for TCP stack
	(20)    Modbus TCP connection timeout
	[*] Enable Modbus stack support for RTU mode
	[*] Enable Modbus stack support for ASCII mode
	(3000) Slave respond timeout (Milliseconds)
	(200) Slave conversion delay (Milliseconds)
	(20) Modbus serial task queue length
	(4096) Modbus port task stack size
	(256) Modbus serial task RX/TX buffer size
	(8) Number of data bits per ASCII character
	(1000) Response timeout for ASCII communication mode (ms)        
	(10) Modbus port task priority
	[*] Modbus controller slave ID support
	(0x00)  Modbus controller slave ID
	(20) Modbus controller notification timeout (ms)
	(20) Modbus controller notification queue size
	(4096) Modbus controller stack size
	(500) Modbus stack event queue timeout (ms)
	[*] Modbus slave stack use timer for 3.5T symbol time measurement
	(0) Modbus Timer group number
	(0) Modbus Timer index in the group
	[ ] Place timer interrupt handler into IRAM
I am using PlatformIO.

Do you have any other suggestions ?
I can understand if this is taking too much time to make it work, i will use the arduino version.
Thank you again for your time helping me.

Who is online

Users browsing this forum: Bing [Bot], cdollar, Google [Bot] and 151 guests