mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-03 22:08:28 +00:00 
			
		
		
		
	freemodbus: allow address gaps in master data dictionary (support of UID field in MBAP) (backport v4.3)
This commit is contained in:
		
				
					committed by
					
						
						Jiang Jiang Jian
					
				
			
			
				
	
			
			
			
						parent
						
							5bad27d0d5
						
					
				
				
					commit
					073da59d27
				
			@@ -19,6 +19,7 @@
 | 
			
		||||
#include <sys/time.h>               // for calculation of time stamp in milliseconds
 | 
			
		||||
#include "esp_log.h"                // for log_write
 | 
			
		||||
#include <string.h>                 // for memcpy
 | 
			
		||||
#include <sys/queue.h>              // for list
 | 
			
		||||
#include "freertos/FreeRTOS.h"      // for task creation and queue access
 | 
			
		||||
#include "freertos/task.h"          // for task api access
 | 
			
		||||
#include "freertos/event_groups.h"  // for event groups
 | 
			
		||||
@@ -42,6 +43,59 @@
 | 
			
		||||
#define MB_TCP_CONNECTION_TOUT pdMS_TO_TICKS(CONFIG_FMB_TCP_CONNECTION_TOUT_SEC * 1000)
 | 
			
		||||
 | 
			
		||||
static mb_master_interface_t* mbm_interface_ptr = NULL;
 | 
			
		||||
static const char *TAG = "MB_CONTROLLER_MASTER";
 | 
			
		||||
 | 
			
		||||
// Searches the slave address in the address info list and returns address info if found, else NULL
 | 
			
		||||
static mb_slave_addr_entry_t* mbc_tcp_master_find_slave_addr(uint8_t slave_addr)
 | 
			
		||||
{
 | 
			
		||||
    mb_slave_addr_entry_t* it;
 | 
			
		||||
    mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
 | 
			
		||||
 | 
			
		||||
    if (LIST_EMPTY(&mbm_opts->mbm_slave_list)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    LIST_FOREACH(it, &mbm_opts->mbm_slave_list, entries) {
 | 
			
		||||
        if (slave_addr == it->slave_addr) {
 | 
			
		||||
            return it;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static esp_err_t mbc_tcp_master_add_slave(uint16_t index, uint8_t slave_addr, const char* ip_addr)
 | 
			
		||||
{
 | 
			
		||||
    MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
 | 
			
		||||
    // Initialize interface properties
 | 
			
		||||
    mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
 | 
			
		||||
 | 
			
		||||
    mb_slave_addr_entry_t* new_slave_entry = (mb_slave_addr_entry_t*) heap_caps_malloc(sizeof(mb_slave_addr_entry_t),
 | 
			
		||||
                                               MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
 | 
			
		||||
    MB_MASTER_CHECK((new_slave_entry != NULL), ESP_ERR_NO_MEM, "mb can not allocate memory for slave entry.");
 | 
			
		||||
    new_slave_entry->index = index;
 | 
			
		||||
    new_slave_entry->ip_address = ip_addr;
 | 
			
		||||
    new_slave_entry->slave_addr = slave_addr;
 | 
			
		||||
    new_slave_entry->p_data = NULL;
 | 
			
		||||
    LIST_INSERT_HEAD(&mbm_opts->mbm_slave_list, new_slave_entry, entries);
 | 
			
		||||
    MB_MASTER_CHECK((mbm_opts->mbm_slave_list_count < (MB_TCP_PORT_MAX_CONN - 1)),
 | 
			
		||||
                        ESP_ERR_INVALID_STATE, "mb max number of slaves < %d.", MB_TCP_PORT_MAX_CONN);
 | 
			
		||||
    mbm_opts->mbm_slave_list_count++;
 | 
			
		||||
    return ESP_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbc_tcp_master_free_slave_list(void)
 | 
			
		||||
{
 | 
			
		||||
    mb_slave_addr_entry_t* it;
 | 
			
		||||
    MB_MASTER_ASSERT(mbm_interface_ptr != NULL);
 | 
			
		||||
 | 
			
		||||
    // Initialize interface properties
 | 
			
		||||
    mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts;
 | 
			
		||||
 | 
			
		||||
    LIST_FOREACH(it, &mbm_opts->mbm_slave_list, entries) {
 | 
			
		||||
        LIST_REMOVE(it, entries);
 | 
			
		||||
        mbm_opts->mbm_slave_list_count--;
 | 
			
		||||
        free(it);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Modbus event processing task
 | 
			
		||||
static void modbus_tcp_master_task(void *pvParameters)
 | 
			
		||||
@@ -113,21 +167,21 @@ static esp_err_t mbc_tcp_master_start(void)
 | 
			
		||||
    vMBTCPPortMasterSetNetOpt(comm_info->ip_netif_ptr, ip_ver, proto);
 | 
			
		||||
    vMBTCPPortMasterTaskStart();
 | 
			
		||||
 | 
			
		||||
    // Add slave IP address for each slave to initialise connection
 | 
			
		||||
    for (int idx = 0; *comm_ip_table != NULL; idx++, comm_ip_table++)
 | 
			
		||||
    {
 | 
			
		||||
        result = (BOOL)xMBTCPPortMasterAddSlaveIp(*comm_ip_table);
 | 
			
		||||
    // Add slave IP address for each slave to initialize connection
 | 
			
		||||
    mb_slave_addr_entry_t *p_slave_info;
 | 
			
		||||
 | 
			
		||||
    LIST_FOREACH(p_slave_info, &mbm_opts->mbm_slave_list, entries) {
 | 
			
		||||
        result = (BOOL)xMBTCPPortMasterAddSlaveIp(p_slave_info->index, p_slave_info->ip_address, p_slave_info->slave_addr);
 | 
			
		||||
        MB_MASTER_CHECK(result, ESP_ERR_INVALID_STATE, "mb stack add slave IP failed: %s.", *comm_ip_table);
 | 
			
		||||
    }
 | 
			
		||||
    // Init polling event handlers and wait before start polling
 | 
			
		||||
    xMBTCPPortMasterWaitEvent(mbm_opts->mbm_event_group, (EventBits_t)MB_EVENT_STACK_STARTED, 1);
 | 
			
		||||
 | 
			
		||||
    // Add end of list condition
 | 
			
		||||
    (void)xMBTCPPortMasterAddSlaveIp(0xFF, NULL, 0xFF);
 | 
			
		||||
 | 
			
		||||
    status = eMBMasterEnable();
 | 
			
		||||
    MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE,
 | 
			
		||||
            "mb stack set slave ID failure, eMBMasterEnable() returned (0x%x).", (uint32_t)status);
 | 
			
		||||
 | 
			
		||||
    // Send end of list condition to start connection phase
 | 
			
		||||
    (void)xMBTCPPortMasterAddSlaveIp(NULL);
 | 
			
		||||
 | 
			
		||||
    // Wait for connection done event
 | 
			
		||||
    bool start = (bool)xMBTCPPortMasterWaitEvent(mbm_opts->mbm_event_group,
 | 
			
		||||
@@ -160,6 +214,7 @@ static esp_err_t mbc_tcp_master_destroy(void)
 | 
			
		||||
    mbm_opts->mbm_task_handle = NULL;
 | 
			
		||||
    (void)vEventGroupDelete(mbm_opts->mbm_event_group);
 | 
			
		||||
    mbm_opts->mbm_event_group = NULL;
 | 
			
		||||
    mbc_tcp_master_free_slave_list();
 | 
			
		||||
    free(mbm_interface_ptr); // free the memory allocated for options
 | 
			
		||||
    vMBPortSetMode((UCHAR)MB_PORT_INACTIVE);
 | 
			
		||||
    mbm_interface_ptr = NULL;
 | 
			
		||||
@@ -179,14 +234,25 @@ static esp_err_t mbc_tcp_master_set_descriptor(const mb_parameter_descriptor_t*
 | 
			
		||||
    MB_MASTER_CHECK((comm_ip_table != NULL), ESP_ERR_INVALID_ARG, "mb ip table address is incorrect.");
 | 
			
		||||
 | 
			
		||||
    const mb_parameter_descriptor_t *reg_ptr = descriptor;
 | 
			
		||||
    uint16_t slave_cnt = 0;
 | 
			
		||||
    mb_slave_addr_entry_t* p_slave = NULL;
 | 
			
		||||
 | 
			
		||||
    // Go through all items in the table to check all Modbus registers
 | 
			
		||||
    for (uint16_t counter = 0; counter < (num_elements); counter++, reg_ptr++)
 | 
			
		||||
    for (int idx = 0; idx < (num_elements); idx++, reg_ptr++)
 | 
			
		||||
    {
 | 
			
		||||
        MB_MASTER_CHECK((comm_ip_table[reg_ptr->mb_slave_addr - 1] != NULL), ESP_ERR_INVALID_ARG, "mb ip table address is incorrect.");
 | 
			
		||||
        // Below is the code to check consistency of the table format and required fields.
 | 
			
		||||
        MB_MASTER_CHECK((reg_ptr->cid == counter), ESP_ERR_INVALID_ARG, "mb descriptor cid field is incorrect.");
 | 
			
		||||
        MB_MASTER_CHECK((reg_ptr->cid == idx), ESP_ERR_INVALID_ARG, "mb descriptor cid field is incorrect.");
 | 
			
		||||
        MB_MASTER_CHECK((reg_ptr->param_key != NULL), ESP_ERR_INVALID_ARG, "mb descriptor param key is incorrect.");
 | 
			
		||||
        MB_MASTER_CHECK((reg_ptr->mb_size > 0), ESP_ERR_INVALID_ARG, "mb descriptor param size is incorrect.");
 | 
			
		||||
        // Is the slave already in the list?
 | 
			
		||||
        p_slave = mbc_tcp_master_find_slave_addr(reg_ptr->mb_slave_addr);
 | 
			
		||||
        // Add it to slave list if not there.
 | 
			
		||||
        if (!p_slave) {
 | 
			
		||||
            // Is the IP address correctly defined for the slave?
 | 
			
		||||
            MB_MASTER_CHECK((comm_ip_table[slave_cnt]), ESP_ERR_INVALID_STATE, "mb missing IP address for cid #%d.", reg_ptr->cid);
 | 
			
		||||
            // Add slave to the list
 | 
			
		||||
            MB_MASTER_ASSERT(mbc_tcp_master_add_slave(idx, reg_ptr->mb_slave_addr, comm_ip_table[slave_cnt++]) == ESP_OK);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    mbm_opts->mbm_param_descriptor_table = descriptor;
 | 
			
		||||
    mbm_opts->mbm_param_descriptor_size = num_elements;
 | 
			
		||||
@@ -258,7 +324,7 @@ static esp_err_t mbc_tcp_master_send_request(mb_param_request_t* request, void*
 | 
			
		||||
                                                        (USHORT)mb_size, (LONG) MB_RESPONSE_TIMEOUT );
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect function in request (%u) ",
 | 
			
		||||
            ESP_LOGE(TAG, "%s: Incorrect function in request (%u) ",
 | 
			
		||||
                                                    __FUNCTION__, mb_command);
 | 
			
		||||
            mb_error = MB_MRE_NO_REG;
 | 
			
		||||
            break;
 | 
			
		||||
@@ -289,7 +355,7 @@ static esp_err_t mbc_tcp_master_send_request(mb_param_request_t* request, void*
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect return code (%x) ", __FUNCTION__, mb_error);
 | 
			
		||||
            ESP_LOGE(TAG, "%s: Incorrect return code (%x) ", __FUNCTION__, mb_error);
 | 
			
		||||
            error = ESP_FAIL;
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -333,11 +399,11 @@ static uint8_t mbc_tcp_master_get_command(mb_param_type_t param_type, mb_param_m
 | 
			
		||||
            if (mode != MB_PARAM_WRITE) {
 | 
			
		||||
                command = MB_FUNC_READ_DISCRETE_INPUTS;
 | 
			
		||||
            } else {
 | 
			
		||||
                ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect mode (%u)", __FUNCTION__, (uint8_t)mode);
 | 
			
		||||
                ESP_LOGE(TAG, "%s: Incorrect mode (%u)", __FUNCTION__, (uint8_t)mode);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect param type (%u)", __FUNCTION__, param_type);
 | 
			
		||||
            ESP_LOGE(TAG, "%s: Incorrect param type (%u)", __FUNCTION__, param_type);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
    return command;
 | 
			
		||||
@@ -368,7 +434,7 @@ static esp_err_t mbc_tcp_master_set_param_data(void* dest, void* src, mb_descr_t
 | 
			
		||||
            memcpy((void*)dest, (void*)src, (size_t)param_size);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect param type (%u).",
 | 
			
		||||
            ESP_LOGE(TAG, "%s: Incorrect param type (%u).",
 | 
			
		||||
                        __FUNCTION__, (uint16_t)param_type);
 | 
			
		||||
            err = ESP_ERR_NOT_SUPPORTED;
 | 
			
		||||
            break;
 | 
			
		||||
@@ -420,31 +486,43 @@ static esp_err_t mbc_tcp_master_get_parameter(uint16_t cid, char* name, uint8_t*
 | 
			
		||||
{
 | 
			
		||||
    MB_MASTER_CHECK((name != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor.");
 | 
			
		||||
    MB_MASTER_CHECK((type != NULL), ESP_ERR_INVALID_ARG, "type pointer is incorrect.");
 | 
			
		||||
    MB_MASTER_CHECK((value != NULL), ESP_ERR_INVALID_ARG, "value pointer is incorrect.");
 | 
			
		||||
    esp_err_t error = ESP_ERR_INVALID_RESPONSE;
 | 
			
		||||
    mb_param_request_t request ;
 | 
			
		||||
    mb_parameter_descriptor_t reg_info = { 0 };
 | 
			
		||||
    uint8_t param_buffer[PARAM_MAX_SIZE] = { 0 };
 | 
			
		||||
    uint8_t* pdata = NULL;
 | 
			
		||||
 | 
			
		||||
    error = mbc_tcp_master_set_request(name, MB_PARAM_READ, &request, ®_info);
 | 
			
		||||
    if ((error == ESP_OK) && (cid == reg_info.cid)) {
 | 
			
		||||
        error = mbc_tcp_master_send_request(&request, ¶m_buffer[0]);
 | 
			
		||||
        // alloc buffer to store parameter data
 | 
			
		||||
        pdata = calloc(1, (reg_info.mb_size << 1));
 | 
			
		||||
        if (!pdata) {
 | 
			
		||||
            return ESP_ERR_INVALID_STATE;
 | 
			
		||||
        }
 | 
			
		||||
        error = mbc_tcp_master_send_request(&request, pdata);
 | 
			
		||||
        if (error == ESP_OK) {
 | 
			
		||||
            // If data pointer is NULL then we don't need to set value (it is still in the cache of cid)
 | 
			
		||||
            if (value != NULL) {
 | 
			
		||||
                error = mbc_tcp_master_set_param_data((void*)value, (void*)¶m_buffer[0],
 | 
			
		||||
                error = mbc_tcp_master_set_param_data((void*)value, (void*)pdata,
 | 
			
		||||
                                                    reg_info.param_type, reg_info.param_size);
 | 
			
		||||
                MB_MASTER_CHECK((error == ESP_OK), ESP_ERR_INVALID_STATE, "fail to set parameter data.");
 | 
			
		||||
                if (error != ESP_OK) {
 | 
			
		||||
                    ESP_LOGE(TAG, "fail to set parameter data.");
 | 
			
		||||
                    error = ESP_ERR_INVALID_STATE;
 | 
			
		||||
                } else {
 | 
			
		||||
                    ESP_LOGD(TAG, "%s: Good response for get cid(%u) = %s",
 | 
			
		||||
                                                        __FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            ESP_LOGD(MB_MASTER_TAG, "%s: Good response for get cid(%u) = %s",
 | 
			
		||||
                                    __FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error));
 | 
			
		||||
        } else {
 | 
			
		||||
            ESP_LOGD(MB_MASTER_TAG, "%s: Bad response to get cid(%u) = %s",
 | 
			
		||||
            ESP_LOGD(TAG, "%s: Bad response to get cid(%u) = %s",
 | 
			
		||||
                                            __FUNCTION__, reg_info.cid, (char*)esp_err_to_name(error));
 | 
			
		||||
            error = ESP_ERR_INVALID_RESPONSE;
 | 
			
		||||
        }
 | 
			
		||||
        free(pdata);
 | 
			
		||||
        // Set the type of parameter found in the table
 | 
			
		||||
        *type = reg_info.param_type;
 | 
			
		||||
    } else {
 | 
			
		||||
        ESP_LOGE(MB_MASTER_TAG, "%s: The cid(%u) not found in the data dictionary.",
 | 
			
		||||
        ESP_LOGE(TAG, "%s: The cid(%u) not found in the data dictionary.",
 | 
			
		||||
                                                    __FUNCTION__, reg_info.cid);
 | 
			
		||||
        error = ESP_ERR_INVALID_ARG;
 | 
			
		||||
    }
 | 
			
		||||
@@ -461,27 +539,36 @@ static esp_err_t mbc_tcp_master_set_parameter(uint16_t cid, char* name, uint8_t*
 | 
			
		||||
    esp_err_t error = ESP_ERR_INVALID_RESPONSE;
 | 
			
		||||
    mb_param_request_t request ;
 | 
			
		||||
    mb_parameter_descriptor_t reg_info = { 0 };
 | 
			
		||||
    uint8_t param_buffer[PARAM_MAX_SIZE] = { 0 };
 | 
			
		||||
    uint8_t* pdata = NULL;
 | 
			
		||||
 | 
			
		||||
    error = mbc_tcp_master_set_request(name, MB_PARAM_WRITE, &request, ®_info);
 | 
			
		||||
    if ((error == ESP_OK) && (cid == reg_info.cid)) {
 | 
			
		||||
        pdata = calloc(1, (reg_info.mb_size << 1)); // alloc parameter buffer
 | 
			
		||||
        if (!pdata) {
 | 
			
		||||
            return ESP_ERR_INVALID_STATE;
 | 
			
		||||
        }
 | 
			
		||||
        // Transfer value of characteristic into parameter buffer
 | 
			
		||||
        error = mbc_tcp_master_set_param_data((void*)¶m_buffer[0], (void*)value,
 | 
			
		||||
        error = mbc_tcp_master_set_param_data((void*)pdata, (void*)value,
 | 
			
		||||
                                                reg_info.param_type, reg_info.param_size);
 | 
			
		||||
        MB_MASTER_CHECK((error == ESP_OK), ESP_ERR_INVALID_STATE, "failure to set parameter data.");
 | 
			
		||||
        if (error != ESP_OK) {
 | 
			
		||||
            ESP_LOGE(TAG, "fail to set parameter data.");
 | 
			
		||||
            free(pdata);
 | 
			
		||||
            return ESP_ERR_INVALID_STATE;
 | 
			
		||||
        }
 | 
			
		||||
        // Send request to write characteristic data
 | 
			
		||||
        error = mbc_tcp_master_send_request(&request, ¶m_buffer[0]);
 | 
			
		||||
        error = mbc_tcp_master_send_request(&request, pdata);
 | 
			
		||||
        if (error == ESP_OK) {
 | 
			
		||||
            ESP_LOGD(MB_MASTER_TAG, "%s: Good response for set cid(%u) = %s",
 | 
			
		||||
                                    __FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error));
 | 
			
		||||
            ESP_LOGD(TAG, "%s: Good response for set cid(%u) = %s",
 | 
			
		||||
                                    __FUNCTION__, (unsigned)reg_info.cid, (char*)esp_err_to_name(error));
 | 
			
		||||
        } else {
 | 
			
		||||
            ESP_LOGD(MB_MASTER_TAG, "%s: Bad response to set cid(%u) = %s",
 | 
			
		||||
            ESP_LOGD(TAG, "%s: Bad response to set cid(%u) = %s",
 | 
			
		||||
                                    __FUNCTION__, reg_info.cid, (char*)esp_err_to_name(error));
 | 
			
		||||
        }
 | 
			
		||||
        free(pdata);
 | 
			
		||||
        // Set the type of parameter found in the table
 | 
			
		||||
        *type = reg_info.param_type;
 | 
			
		||||
    } else {
 | 
			
		||||
        ESP_LOGE(MB_MASTER_TAG, "%s: The requested cid(%u) not found in the data dictionary.",
 | 
			
		||||
        ESP_LOGE(TAG, "%s: The requested cid(%u) not found in the data dictionary.",
 | 
			
		||||
                                    __FUNCTION__, reg_info.cid);
 | 
			
		||||
        error = ESP_ERR_INVALID_ARG;
 | 
			
		||||
    }
 | 
			
		||||
@@ -709,6 +796,9 @@ esp_err_t mbc_tcp_master_create(void** handler)
 | 
			
		||||
    }
 | 
			
		||||
    MB_MASTER_ASSERT(mbm_opts->mbm_task_handle != NULL); // The task is created but handle is incorrect
 | 
			
		||||
 | 
			
		||||
    LIST_INIT(&mbm_opts->mbm_slave_list); // Init slave address list
 | 
			
		||||
    mbm_opts->mbm_slave_list_count = 0;
 | 
			
		||||
 | 
			
		||||
    // Initialize public interface methods of the interface
 | 
			
		||||
    mbm_interface_ptr->init = mbc_tcp_master_create;
 | 
			
		||||
    mbm_interface_ptr->destroy = mbc_tcp_master_destroy;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user