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.
This commit is contained in:
Eduard Iten 2025-07-01 15:27:57 +02:00
parent b836f9a2f4
commit 6a9e4773ea
6 changed files with 170 additions and 21 deletions

View File

@ -6,4 +6,4 @@ list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(slave_node) project(slave_node)
target_sources(app PRIVATE src/main.c) target_sources(app PRIVATE src/main.c src/shell_modbus.c)

View File

@ -1,7 +1,7 @@
/ { / {
chosen { chosen {
zephyr,console = &rtt; zephyr,console = &rtt;
zephyr,shell-uart = &rtt; // If using shell zephyr,shell = &rtt;
}; };
rtt: rtt { rtt: rtt {

View File

@ -9,6 +9,10 @@ CONFIG_UART_CONSOLE=n
CONFIG_RTT_CONSOLE=y CONFIG_RTT_CONSOLE=y
CONFIG_USE_SEGGER_RTT=y CONFIG_USE_SEGGER_RTT=y
# Enable Shell
CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_RTT=y
# Config modbus # Config modbus
CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_MODBUS=y CONFIG_MODBUS=y

View File

@ -12,6 +12,8 @@
#include <zephyr/usb/usb_device.h> #include <zephyr/usb/usb_device.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include "modbus_bridge.h"
LOG_MODULE_REGISTER(mbs_sample, LOG_LEVEL_INF); LOG_MODULE_REGISTER(mbs_sample, LOG_LEVEL_INF);
#define APP_VERSION_MAJOR 1 #define APP_VERSION_MAJOR 1
@ -32,6 +34,20 @@ enum {
static uint16_t watchdog_timeout_s; 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) 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, .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) #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) static int init_modbus_server(void)
{ {
const char iface_name[] = {DEVICE_DT_NAME(MODBUS_NODE)}; const char iface_name[] = {DEVICE_DT_NAME(MODBUS_NODE)};
int iface;
iface = modbus_iface_get_by_name(iface_name); modbus_iface = modbus_iface_get_by_name(iface_name);
if (modbus_iface < 0) {
if (iface < 0) {
LOG_ERR("Failed to get iface index for %s", iface_name); 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) int main(void)

View File

@ -0,0 +1,27 @@
#ifndef MODBUS_BRIDGE_H
#define MODBUS_BRIDGE_H
#include <stdint.h>
/**
* @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

View File

@ -0,0 +1,79 @@
#include <zephyr/shell/shell.h>
#include <stdlib.h>
#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 <baudrate>");
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 <slave_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);