-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
esp-modbus as a slave data validation before writing (IDFGH-14101) #83
Comments
#82 is in the same boat as me I think. I am also unable to see the data being written by the master. |
Sorry for the delay with answer. This stack was designed to hide some Modbus internals from user and it is why this is not documented. This actually can be done by defining user handler functions for commands. There are several tricks to accomplish this. The one trick to override the callbacks in user code is described below: Please apply this code above the app_main() in the slave example: // The exception structure codes are defined in private file, let just override them here
typedef enum
{
MB_EX_NONE = 0x00,
MB_EX_ILLEGAL_FUNCTION = 0x01,
MB_EX_ILLEGAL_DATA_ADDRESS = 0x02,
MB_EX_ILLEGAL_DATA_VALUE = 0x03,
MB_EX_SLAVE_DEVICE_FAILURE = 0x04,
MB_EX_ACKNOWLEDGE = 0x05,
MB_EX_SLAVE_BUSY = 0x06,
MB_EX_MEMORY_PARITY_ERROR = 0x08,
MB_EX_GATEWAY_PATH_FAILED = 0x0A,
MB_EX_GATEWAY_TGT_FAILED = 0x0B
} e_mb_exception;
typedef e_mb_exception( *pfunc_handler_t ) ( uint8_t * pframe, uint16_t * plength );
// Will call the handler registration function from stack core
extern int eMBRegisterCB( uint8_t func_code, pfunc_handler_t pfunc_handler );
// Define your custom function handler to process the command received.
// Can return the error code if we need to inform master about specific failure
e_mb_exception my_input_read_registers_handler_func( uint8_t *pframe, uint16_t *plen )
{
// This error handler will be executed to check the request for the command 0x04
// See the `esp-modbus/freemodbus/modbus/functions/mbfuncinput.c` for more information
// pframe is pointer to command buffer, plen - is pointer to length
ESP_LOGE("TEST_HANDLER", "Overridden handler for command 0x04 is called.");
return MB_EX_ILLEGAL_DATA_VALUE;
}
// 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(void)
{
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
uint8_t override_command = 0x04;
// <<<<<<<<<<<<<<<<<<<<<< Register the custom function handler here
int err = eMBRegisterCB(override_command, NULL);
MB_RETURN_ON_FALSE(!err, ;, TAG,
"could not override func handler, returned (0x%x).", err);
err = eMBRegisterCB(override_command, my_input_read_registers_handler_func);
MB_RETURN_ON_FALSE(!err, ;, TAG,
"could not override func handler, returned (0x%x).", err);
ESP_LOGI("TEST_HANDLER", "Overridden function handler for the command 0x%x ", (int)override_command);
// Setup communication parameters and start stack
#if CONFIG_MB_COMM_MODE_ASCII
comm_info.mode = MB_MODE_ASCII;
#elif CONFIG_MB_COMM_MODE_RTU
comm_info.mode = MB_MODE_RTU;
#endif
comm_info.slave_addr = MB_SLAVE_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)); Note: the handler will be executed by stack from the polling task, so keep the handler as short as possible and safe. |
Hey @alisitsyn. Thank you for replying. Your code looks good to me and it should work. I shall test it in my application and then get back to you. |
Hey @kabirwbt, Any update on this? |
Hi @alisitsyn sorry for not updating sooner. I was using esp-modbus in the arduino framework. Even though your code is great, it wasn't compiling for me because arduino framework pre-compiles the .c files of esp-idf libraries and only exposes the .h files to end-users. They only include the statically linked .a files in their code. I am obviously unable to call the I am porting my project to esp-idf soon and can only test properly then. The second option is to try and compile with arduino framework for esp32 separately including esp-modbus, which i am not looking forward too 😄. I am using master just fine. I will finish porting over my project to esp-idf in the coming time and will properly update then. |
Thank you for your feedback. Yes, this trick is probably for esp-idf only and works (verified). There are some other possible ways to accomplish this for arduino making changes in the internal component folder. This approach may also help to patch the official component in platform io (will not work for arduino). Once you have some results, please report here. Thanks. |
Checklist
Issue or Suggestion Description
I had a question regrading the code for the modbus slave. I have gone through the example for modbus rtu slave (https://github.com/espressif/esp-idf/blob/master/examples/protocols/modbus/serial/mb_slave/main/slave.c). It is working well. However, I have a possible issue with the way it is implemented that I need help with. Let's say that I write to the slave using fc 06 (write holding reg), via a modbus master. The library automatically writes the value to the specified register. However, I would like to see the incoming data, check whether it is valid (in range) and only then allow it to be written, otherwise send a modbus exception (Illegal Data Value 03). Please help me how to do this with esp-modbus.
An example case is let's say I am creating a thermostat. I would want my set-temperature to be allowed within range (let's say 16-40 C) . If a user enters a number out of range, I want to set a modbus exception, or at least prevent it being written. I am not able to understand how to do this with esp-modbus. I have read the slave API documentation and was unable to understand how to achieve this. Currently it writes the value instantly to specified register.
The text was updated successfully, but these errors were encountered: