From 6a9e4773ea5ca1112924ea8a01efdfebef897170 Mon Sep 17 00:00:00 2001 From: Eduard Iten Date: Tue, 1 Jul 2025 15:27:57 +0200 Subject: [PATCH] feat(shell): Add commands to configure Modbus - Implement a new 'modbus' command in the shell. - Add sub-commands 'set_baud', 'set_id', and 'show'. - Add validation for baud rate and slave ID inputs. - The new parameters are applied to the Modbus server at runtime, allowing for live reconfiguration of the communication settings. - The shell backend is set to RTT. --- software/apps/slave_node/CMakeLists.txt | 2 +- .../slave_node/boards/bluepill_f103rb.overlay | 2 +- software/apps/slave_node/prj.conf | 4 + software/apps/slave_node/src/main.c | 77 +++++++++++++----- software/apps/slave_node/src/modbus_bridge.h | 27 +++++++ software/apps/slave_node/src/shell_modbus.c | 79 +++++++++++++++++++ 6 files changed, 170 insertions(+), 21 deletions(-) create mode 100644 software/apps/slave_node/src/modbus_bridge.h create mode 100644 software/apps/slave_node/src/shell_modbus.c diff --git a/software/apps/slave_node/CMakeLists.txt b/software/apps/slave_node/CMakeLists.txt index 85a4f8f..cfe9623 100644 --- a/software/apps/slave_node/CMakeLists.txt +++ b/software/apps/slave_node/CMakeLists.txt @@ -6,4 +6,4 @@ list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(slave_node) -target_sources(app PRIVATE src/main.c) +target_sources(app PRIVATE src/main.c src/shell_modbus.c) diff --git a/software/apps/slave_node/boards/bluepill_f103rb.overlay b/software/apps/slave_node/boards/bluepill_f103rb.overlay index 0816af0..1cbdfca 100644 --- a/software/apps/slave_node/boards/bluepill_f103rb.overlay +++ b/software/apps/slave_node/boards/bluepill_f103rb.overlay @@ -1,7 +1,7 @@ / { chosen { zephyr,console = &rtt; - zephyr,shell-uart = &rtt; // If using shell + zephyr,shell = &rtt; }; rtt: rtt { diff --git a/software/apps/slave_node/prj.conf b/software/apps/slave_node/prj.conf index 8ba4116..64b0c5e 100644 --- a/software/apps/slave_node/prj.conf +++ b/software/apps/slave_node/prj.conf @@ -9,6 +9,10 @@ CONFIG_UART_CONSOLE=n CONFIG_RTT_CONSOLE=y CONFIG_USE_SEGGER_RTT=y +# Enable Shell +CONFIG_SHELL=y +CONFIG_SHELL_BACKEND_RTT=y + # Config modbus CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_MODBUS=y diff --git a/software/apps/slave_node/src/main.c b/software/apps/slave_node/src/main.c index cef1530..3f524a5 100644 --- a/software/apps/slave_node/src/main.c +++ b/software/apps/slave_node/src/main.c @@ -12,6 +12,8 @@ #include #include +#include "modbus_bridge.h" + LOG_MODULE_REGISTER(mbs_sample, LOG_LEVEL_INF); #define APP_VERSION_MAJOR 1 @@ -32,6 +34,20 @@ enum { static uint16_t watchdog_timeout_s; +static int modbus_iface; + +static struct modbus_iface_param server_param = { + .mode = MODBUS_MODE_RTU, + .server = { + .user_cb = NULL, // Will be set later + .unit_id = 1, + }, + .serial = { + .baud = 19200, + .parity = UART_CFG_PARITY_NONE, + }, +}; + static int coil_rd(uint16_t addr, bool *state) { @@ -112,33 +128,56 @@ static struct modbus_user_callbacks mbs_cbs = { .input_reg_rd = input_reg_rd, }; -const static struct modbus_iface_param server_param = { - .mode = MODBUS_MODE_RTU, - .server = { - .user_cb = &mbs_cbs, - .unit_id = 1, - }, - .serial = { - .baud = 19200, - .parity = UART_CFG_PARITY_NONE, - }, -}; - #define MODBUS_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(zephyr_modbus_serial) +int modbus_reconfigure(uint32_t baudrate, uint8_t unit_id) +{ + int err; + + LOG_INF("Reconfiguring Modbus: baudrate=%u, id=%u", baudrate, unit_id); + + err = modbus_disable(modbus_iface); + if (err) { + LOG_ERR("Failed to disable Modbus: %d", err); + return err; + } + + server_param.serial.baud = baudrate; + server_param.server.unit_id = unit_id; + + err = modbus_init_server(modbus_iface, server_param); + if (err) { + LOG_ERR("Failed to re-init Modbus server: %d", err); + return err; + } + + return 0; +} + +uint32_t modbus_get_baudrate(void) +{ + return server_param.serial.baud; +} + +uint8_t modbus_get_unit_id(void) +{ + return server_param.server.unit_id; +} + + static int init_modbus_server(void) { const char iface_name[] = {DEVICE_DT_NAME(MODBUS_NODE)}; - int iface; - iface = modbus_iface_get_by_name(iface_name); - - if (iface < 0) { + modbus_iface = modbus_iface_get_by_name(iface_name); + if (modbus_iface < 0) { LOG_ERR("Failed to get iface index for %s", iface_name); - return iface; + return modbus_iface; } - return modbus_init_server(iface, server_param); + server_param.server.user_cb = &mbs_cbs; + + return modbus_init_server(modbus_iface, server_param); } int main(void) @@ -154,4 +193,4 @@ int main(void) } return 0; -} \ No newline at end of file +} diff --git a/software/apps/slave_node/src/modbus_bridge.h b/software/apps/slave_node/src/modbus_bridge.h new file mode 100644 index 0000000..f88b67c --- /dev/null +++ b/software/apps/slave_node/src/modbus_bridge.h @@ -0,0 +1,27 @@ +#ifndef MODBUS_BRIDGE_H +#define MODBUS_BRIDGE_H + +#include + +/** + * @brief Reconfigures the Modbus server with new parameters. + * + * @param baudrate New baudrate. + * @param unit_id New slave unit ID. + * @return 0 on success, negative error code on failure. + */ +int modbus_reconfigure(uint32_t baudrate, uint8_t unit_id); + +/** + * @brief Gets the currently active Modbus baudrate. + * @return The current baudrate. + */ +uint32_t modbus_get_baudrate(void); + +/** + * @brief Gets the currently active Modbus slave unit ID. + * @return The current slave unit ID. + */ +uint8_t modbus_get_unit_id(void); + +#endif // MODBUS_BRIDGE_H diff --git a/software/apps/slave_node/src/shell_modbus.c b/software/apps/slave_node/src/shell_modbus.c new file mode 100644 index 0000000..7397f9d --- /dev/null +++ b/software/apps/slave_node/src/shell_modbus.c @@ -0,0 +1,79 @@ +#include +#include +#include "modbus_bridge.h" + +static int cmd_modbus_set_baud(const struct shell *sh, size_t argc, char **argv) +{ + if (argc != 2) { + shell_error(sh, "Usage: set_baud "); + return -EINVAL; + } + + uint32_t new_baud = (uint32_t)strtoul(argv[1], NULL, 10); + const uint32_t valid_baud_rates[] = {1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200}; + bool is_valid = false; + + for (int i = 0; i < ARRAY_SIZE(valid_baud_rates); i++) { + if (new_baud == valid_baud_rates[i]) { + is_valid = true; + break; + } + } + + if (!is_valid) { + char error_msg[128]; + int offset = snprintf(error_msg, sizeof(error_msg), "Invalid baudrate. Valid rates are: "); + for (int i = 0; i < ARRAY_SIZE(valid_baud_rates); i++) { + offset += snprintf(error_msg + offset, sizeof(error_msg) - offset, "%u ", valid_baud_rates[i]); + } + shell_error(sh, "%s", error_msg); + return -EINVAL; + } + + if (modbus_reconfigure(new_baud, modbus_get_unit_id()) != 0) { + shell_error(sh, "Failed to apply new baudrate"); + } else { + shell_print(sh, "Modbus baudrate set to: %u", new_baud); + } + + return 0; +} + +static int cmd_modbus_set_id(const struct shell *sh, size_t argc, char **argv) +{ + if (argc != 2) { + shell_error(sh, "Usage: set_id "); + return -EINVAL; + } + + uint32_t new_id = (uint32_t)strtoul(argv[1], NULL, 10); + if (new_id == 0 || new_id > 247) { + shell_error(sh, "Invalid slave ID: %s. Must be between 1 and 247.", argv[1]); + return -EINVAL; + } + + if (modbus_reconfigure(modbus_get_baudrate(), (uint8_t)new_id) != 0) { + shell_error(sh, "Failed to apply new slave ID"); + } else { + shell_print(sh, "Modbus slave ID set to: %u", (uint8_t)new_id); + } + + return 0; +} + +static int cmd_modbus_show(const struct shell *sh, size_t argc, char **argv) +{ + shell_print(sh, "Current Modbus Configuration:"); + shell_print(sh, " Baudrate: %u", modbus_get_baudrate()); + shell_print(sh, " Slave ID: %u", modbus_get_unit_id()); + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(sub_modbus_cmds, + SHELL_CMD(set_baud, NULL, "Set Modbus baudrate", cmd_modbus_set_baud), + SHELL_CMD(set_id, NULL, "Set Modbus slave ID", cmd_modbus_set_id), + SHELL_CMD(show, NULL, "Show current Modbus configuration", cmd_modbus_show), + SHELL_SUBCMD_SET_END +); + +SHELL_CMD_REGISTER(modbus, &sub_modbus_cmds, "Modbus configuration", NULL); \ No newline at end of file