Contains two different component folders per each implementation (serial_master and serial_slave) with concrete ports. Added common public api for master and slave and common interface for master and slave implementation. Add support of cmake system (added cmake files). Added sdkconfig.defaults files for slave and master modbus examples. Updated make file and KConfig for freemodbus component Update according to review and fix doxygen warnings Fix Doxyfile to pass documentation build Update headers and change interface file names as per review comments Merge branch feature/freemodbus_move_rs485_mode_control Update after review: The stack modbus folder updated to support master and slave ports together and moved into freemodbus/modbus Stack and port files updated to remove duplicated simbols Make file, KConfig and CMakeLists.txt updated to compile master and slave stacks, common interface and concrete implementations of ports Stack callback functions execute callbacks using interface pointer from concrete port implementation User can instantiate any of concrete port using common API (only one concrete port at a time) and it does not require to select port by KConfig Port pins and mode configuration moved into example files from port files to allow user select pins and port mode (customer request) Changes tested using pymodbus, ModbusPoll and communication between two boards Updated DoxyFile according to public include path Fix maximum instance size for slave (merge from master of customer issue) Fix critical section issue TW#28622 (change spin lock based critical section to semaphore) Move serial port files into component port folder for master and slave accordingly Fix example issue showed in the log when IO slave is not configured correctly Fix conflicts while merging from origin/master Fix errors handling in modbus controller interface + some final corrections according to review Update maximum allowed number of slaves in the network segment Fix bug with incorrect coils read mask Closes https://github.com/espressif/esp-idf/issues/858
184 lines
8.7 KiB
C
184 lines
8.7 KiB
C
/* FreeModbus Slave Example ESP32
|
|
|
|
Unless required by applicable law or agreed to in writing, this
|
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
CONDITIONS OF ANY KIND, either express or implied.
|
|
*/
|
|
#include <stdio.h>
|
|
#include "esp_err.h"
|
|
#include "sdkconfig.h"
|
|
#include "mbcontroller.h" // for mbcontroller defines and api
|
|
#include "deviceparams.h" // for device parameters structures
|
|
#include "esp_log.h" // for log_write
|
|
|
|
#define MB_PORT_NUM (2) // Number of UART port used for Modbus connection
|
|
#define MB_DEV_ADDR (1) // The address of device in Modbus network
|
|
#define MB_DEV_SPEED (115200) // The communication speed of the UART
|
|
|
|
// Defines below are used to define register start address for each type of Modbus registers
|
|
#define MB_REG_DISCRETE_INPUT_START (0x0000)
|
|
#define MB_REG_INPUT_START (0x0000)
|
|
#define MB_REG_HOLDING_START (0x0000)
|
|
#define MB_REG_COILS_START (0x0000)
|
|
|
|
#define MB_PAR_INFO_GET_TOUT (10) // Timeout for get parameter info
|
|
#define MB_CHAN_DATA_MAX_VAL (10)
|
|
#define MB_CHAN_DATA_OFFSET (0.01f)
|
|
#define MB_READ_MASK (MB_EVENT_INPUT_REG_RD \
|
|
| MB_EVENT_HOLDING_REG_RD \
|
|
| MB_EVENT_DISCRETE_RD \
|
|
| MB_EVENT_COILS_RD)
|
|
#define MB_WRITE_MASK (MB_EVENT_HOLDING_REG_WR \
|
|
| MB_EVENT_COILS_WR)
|
|
#define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK)
|
|
|
|
static const char *TAG = "MODBUS_SLAVE_APP";
|
|
|
|
// Set register values into known state
|
|
static void setup_reg_data()
|
|
{
|
|
// Define initial state of parameters
|
|
discrete_reg_params.discrete_input1 = 1;
|
|
discrete_reg_params.discrete_input3 = 1;
|
|
discrete_reg_params.discrete_input5 = 1;
|
|
discrete_reg_params.discrete_input7 = 1;
|
|
|
|
holding_reg_params.data_chan0 = 1.34;
|
|
holding_reg_params.data_chan1 = 2.56;
|
|
holding_reg_params.data_chan2 = 3.78;
|
|
holding_reg_params.data_chan3 = 4.90;
|
|
|
|
coil_reg_params.coil0 = 1;
|
|
coil_reg_params.coil2 = 1;
|
|
coil_reg_params.coil4 = 1;
|
|
coil_reg_params.coil6 = 1;
|
|
coil_reg_params.coil7 = 1;
|
|
|
|
input_reg_params.data_chan0 = 1.34;
|
|
input_reg_params.data_chan1 = 2.56;
|
|
input_reg_params.data_chan2 = 3.78;
|
|
input_reg_params.data_chan3 = 4.90;
|
|
}
|
|
|
|
// An example application of Modbus slave. It is based on freemodbus stack.
|
|
// See deviceparams.h file for more information about assigned Modbus parameters.
|
|
// These parameters can be accessed from main application and also can be changed
|
|
// by external Modbus master host.
|
|
void app_main()
|
|
{
|
|
mb_param_info_t reg_info; // keeps the Modbus registers access information
|
|
mb_communication_info_t comm_info; // Modbus communication parameters
|
|
mb_register_area_descriptor_t reg_area; // Modbus register area descriptor structure
|
|
|
|
// Set UART log level
|
|
esp_log_level_set(TAG, ESP_LOG_INFO);
|
|
void* mbc_slave_handler = NULL;
|
|
|
|
ESP_ERROR_CHECK(mbc_slave_init(MB_PORT_SERIAL_SLAVE, &mbc_slave_handler)); // Initialization of Modbus controller
|
|
|
|
// Setup communication parameters and start stack
|
|
comm_info.mode = MB_MODE_RTU;
|
|
comm_info.slave_addr = MB_DEV_ADDR;
|
|
comm_info.port = MB_PORT_NUM;
|
|
comm_info.baudrate = MB_DEV_SPEED;
|
|
comm_info.parity = MB_PARITY_NONE;
|
|
ESP_ERROR_CHECK(mbc_slave_setup((void*)&comm_info));
|
|
|
|
// The code below initializes Modbus register area descriptors
|
|
// for Modbus Holding Registers, Input Registers, Coils and Discrete Inputs
|
|
// Initialization should be done for each supported Modbus register area according to register map.
|
|
// When external master trying to access the register in the area that is not initialized
|
|
// by mbc_slave_set_descriptor() API call then Modbus stack
|
|
// will send exception response for this register area.
|
|
reg_area.type = MB_PARAM_HOLDING; // Set type of register area
|
|
reg_area.start_offset = MB_REG_HOLDING_START; // Offset of register area in Modbus protocol
|
|
reg_area.address = (void*)&holding_reg_params; // Set pointer to storage instance
|
|
reg_area.size = sizeof(holding_reg_params); // Set the size of register storage instance
|
|
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
|
|
|
// Initialization of Input Registers area
|
|
reg_area.type = MB_PARAM_INPUT;
|
|
reg_area.start_offset = MB_REG_INPUT_START;
|
|
reg_area.address = (void*)&input_reg_params;
|
|
reg_area.size = sizeof(input_reg_params);
|
|
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
|
|
|
// Initialization of Coils register area
|
|
reg_area.type = MB_PARAM_COIL;
|
|
reg_area.start_offset = MB_REG_COILS_START;
|
|
reg_area.address = (void*)&coil_reg_params;
|
|
reg_area.size = sizeof(coil_reg_params);
|
|
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
|
|
|
// Initialization of Discrete Inputs register area
|
|
reg_area.type = MB_PARAM_DISCRETE;
|
|
reg_area.start_offset = MB_REG_DISCRETE_INPUT_START;
|
|
reg_area.address = (void*)&discrete_reg_params;
|
|
reg_area.size = sizeof(discrete_reg_params);
|
|
ESP_ERROR_CHECK(mbc_slave_set_descriptor(reg_area));
|
|
|
|
setup_reg_data(); // Set values into known state
|
|
|
|
// Starts of modbus controller and stack
|
|
ESP_ERROR_CHECK(mbc_slave_start());
|
|
|
|
// Set UART driver mode to Half Duplex
|
|
ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX));
|
|
|
|
// Set UART pin numbers
|
|
ESP_ERROR_CHECK(uart_set_pin(MB_PORT_NUM, CONFIG_MB_UART_TXD,
|
|
CONFIG_MB_UART_RXD, CONFIG_MB_UART_RTS,
|
|
UART_PIN_NO_CHANGE));
|
|
|
|
// The cycle below will be terminated when parameter holdingRegParams.dataChan0
|
|
// incremented each access cycle reaches the CHAN_DATA_MAX_VAL value.
|
|
for(;holding_reg_params.data_chan0 < MB_CHAN_DATA_MAX_VAL;) {
|
|
// Check for read/write events of Modbus master for certain events
|
|
mb_event_group_t event = mbc_slave_check_event(MB_READ_WRITE_MASK);
|
|
const char* rw_str = (event & MB_READ_MASK) ? "READ" : "WRITE";
|
|
// Filter events and process them accordingly
|
|
if(event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD)) {
|
|
// Get parameter information from parameter queue
|
|
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
|
printf("HOLDING %s: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
|
rw_str,
|
|
(uint32_t)reg_info.time_stamp,
|
|
(uint32_t)reg_info.mb_offset,
|
|
(uint32_t)reg_info.type,
|
|
(uint32_t)reg_info.address,
|
|
(uint32_t)reg_info.size);
|
|
if (reg_info.address == (uint8_t*)&holding_reg_params.data_chan0)
|
|
{
|
|
holding_reg_params.data_chan0 += MB_CHAN_DATA_OFFSET;
|
|
}
|
|
} else if (event & MB_EVENT_INPUT_REG_RD) {
|
|
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
|
printf("INPUT READ: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
|
(uint32_t)reg_info.time_stamp,
|
|
(uint32_t)reg_info.mb_offset,
|
|
(uint32_t)reg_info.type,
|
|
(uint32_t)reg_info.address,
|
|
(uint32_t)reg_info.size);
|
|
} else if (event & MB_EVENT_DISCRETE_RD) {
|
|
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
|
printf("DISCRETE READ: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
|
(uint32_t)reg_info.time_stamp,
|
|
(uint32_t)reg_info.mb_offset,
|
|
(uint32_t)reg_info.type,
|
|
(uint32_t)reg_info.address,
|
|
(uint32_t)reg_info.size);
|
|
} else if (event & (MB_EVENT_COILS_RD | MB_EVENT_COILS_WR)) {
|
|
ESP_ERROR_CHECK(mbc_slave_get_param_info(®_info, MB_PAR_INFO_GET_TOUT));
|
|
printf("COILS %s: time_stamp(us):%u, mb_addr:%u, type:%u, st_address:0x%.4x, size:%u\r\n",
|
|
rw_str,
|
|
(uint32_t)reg_info.time_stamp,
|
|
(uint32_t)reg_info.mb_offset,
|
|
(uint32_t)reg_info.type,
|
|
(uint32_t)reg_info.address,
|
|
(uint32_t)reg_info.size);
|
|
}
|
|
}
|
|
// Destroy of Modbus controller once get maximum value of data_chan0
|
|
printf("Modbus controller destroyed.");
|
|
ESP_ERROR_CHECK(mbc_slave_destroy());
|
|
}
|