From 852c5c72beca11e7fecc4527cf19ae8907ef3824 Mon Sep 17 00:00:00 2001 From: Eduard Iten Date: Tue, 17 Jun 2025 16:43:13 +0200 Subject: [PATCH] Added shell functions --- software/.vscode/settings.json | 4 +- .../boards/iten/valve_node/valve_node.dts | 10 +- .../iten/valve_node/valve_node_defconfig | 1 + software/lib/canbus.c | 91 ++++++++++++--- software/lib/canbus.h | 9 +- software/lib/canbus_registers.h | 18 +++ software/lib/config.c | 71 ++++++++++++ software/lib/modbus.h | 10 ++ software/test_canbus/prj.conf | 13 ++- software/test_canbus/src/main.c | 108 +++++++++++++++--- 10 files changed, 296 insertions(+), 39 deletions(-) create mode 100644 software/lib/canbus_registers.h diff --git a/software/.vscode/settings.json b/software/.vscode/settings.json index c595456..144ab03 100644 --- a/software/.vscode/settings.json +++ b/software/.vscode/settings.json @@ -8,7 +8,9 @@ "canbus.h": "c", "kernel.h": "c", "settings.h": "c", - "can.h": "c" + "can.h": "c", + "stdlib.h": "c", + "reboot.h": "c" }, "C_Cpp.default.compileCommands": [ "build/compile_commands.json", diff --git a/software/boards/iten/valve_node/valve_node.dts b/software/boards/iten/valve_node/valve_node.dts index 76ace88..471fe91 100644 --- a/software/boards/iten/valve_node/valve_node.dts +++ b/software/boards/iten/valve_node/valve_node.dts @@ -14,7 +14,7 @@ compatible = "iten,valve-node", "st,stm32f103rb"; can_loopback0: can_loopback0 { - status = "okay"; + status = "disabled"; compatible = "zephyr,can-loopback"; }; @@ -23,8 +23,8 @@ zephyr,shell-uart = &usart1; zephyr,sram = &sram0; zephyr,flash = &flash0; - //zephyr,canbus = &can1; - zephyr,canbus = &can_loopback0; + zephyr,canbus = &can1; + //zephyr,canbus = &can_loopback0; }; leds: leds { @@ -79,6 +79,10 @@ gpios = <&gpiob 13 0>; label = "Motor Open"; }; + fake: fake { + gpios = <&gpiob 11 GPIO_PULL_UP>; + label = "CAN RX pullup"; + }; }; }; diff --git a/software/boards/iten/valve_node/valve_node_defconfig b/software/boards/iten/valve_node/valve_node_defconfig index 8eb026e..5e3d242 100644 --- a/software/boards/iten/valve_node/valve_node_defconfig +++ b/software/boards/iten/valve_node/valve_node_defconfig @@ -18,6 +18,7 @@ CONFIG_MODBUS_ROLE_CLIENT=y CONFIG_CAN=y CONFIG_CAN_INIT_PRIORITY=80 #CONFIG_CAN_MAX_FILTER=5 +CONFIG_CAN_ACCEPT_RTR=y # settings CONFIG_FLASH=y diff --git a/software/lib/canbus.c b/software/lib/canbus.c index 23d12c1..dba7108 100644 --- a/software/lib/canbus.c +++ b/software/lib/canbus.c @@ -3,6 +3,7 @@ #include #include #include "config.h" +#include "modbus.h" int canbus_node_id = 1; // Default node ID for CAN bus @@ -11,7 +12,7 @@ const struct device *const can_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_canbus)); struct k_thread rx_thread_data; K_THREAD_STACK_DEFINE(rx_thread_stack, CANBUS_RX_THREAD_STACK_SIZE); -CAN_MSGQ_DEFINE(commands_msq, 2); +CAN_MSGQ_DEFINE(commands_msq, CANBUS_RX_MSGQ_SIZE); LOG_MODULE_REGISTER(canbus, CONFIG_LOG_CAN_LEVEL); @@ -20,14 +21,16 @@ static void rx_thread(void *arg1, void *arg2, void *arg3) ARG_UNUSED(arg1); ARG_UNUSED(arg2); ARG_UNUSED(arg3); + const struct can_filter filter = { - .flags = CAN_FILTER_IDE, + .flags = 0, // No special flags, .id = (canbus_node_id << 8) | 0x00, // Standard ID with node ID in the first byte .mask = CAN_STD_ID_MASK & 0xFFFFFF00U // Mask for standard ID, ignoring the last byte }; struct can_frame frame; int filter_id; + extern struct k_msgq modbus_msgq; filter_id = can_add_rx_filter_msgq(can_dev, &commands_msq, &filter); LOG_DBG("RX thread started. Filter ID: %d, flags: 0x%02x, id: 0x%08x, mask: 0x%08x", filter_id, filter.flags, filter.id, filter.mask); @@ -37,10 +40,6 @@ static void rx_thread(void *arg1, void *arg2, void *arg3) k_msgq_get(&commands_msq, &frame, K_FOREVER); LOG_DBG("Received CAN frame: ID=0x%08x, DLC=%u, Flags=0x%02x", frame.id, frame.dlc, frame.flags); - if (IS_ENABLED(CONFIG_CAN_ACCEPT_RTR) && (frame.flags & CAN_FRAME_RTR) != 0U) - { - continue; - } uint8_t target_node = (frame.id >> 8) & 0xFF; // Extract the target node from the ID if (target_node != canbus_node_id) @@ -50,9 +49,49 @@ static void rx_thread(void *arg1, void *arg2, void *arg3) } uint8_t target_register = frame.id & 0xFF; // Extract the register address from the ID + if (IS_ENABLED(CONFIG_CAN_ACCEPT_RTR) && (frame.flags & CAN_FRAME_RTR) != 0U) + { + LOG_DBG("Received RTR frame for register address: 0x%02x", target_register); + switch (target_register) + { + case CANBUS_REGISTER_WATER_LEVEL_MM: + case CANBUS_REGISTER_WATER_MINIMUM: + case CANBUS_REGISTER_WATER_MAXIMUM: + LOG_DBG("Handling water sensor request for node %d", target_node); + modbus_register_t reg_max; + reg_max.reg = target_register; + reg_max.command = CANBUS_REGISTER_COMMAND_GET; // Set command to GET + k_msgq_put(&modbus_msgq, ®_max, K_NO_WAIT); + break; + default: + LOG_WRN("Received RTR frame for unknown register address: 0x%02x", target_register); + } + continue; + } // Log the received frame details - LOG_DBG("Received CAN frame: ID=0x%08x, DLC=%u, Flags=0x%02x, Target Node=%d, Register Address=%d", + LOG_DBG("Received CAN frame: ID=0x%08x, DLC=%u, Flags=0x%02x, Target Node=%d, Register Address=0x%02x", frame.id, frame.dlc, frame.flags, target_node, target_register); + switch (target_register) + { + case CANBUS_REGISTER_WATER_MINIMUM: + case CANBUS_REGISTER_WATER_MAXIMUM: + LOG_DBG("Handling water sensor data for node %d", target_node); + modbus_register_t reg; + reg.reg = target_register; + reg.command = CANBUS_REGISTER_COMMAND_SET; // Set command to SET + if (frame.dlc >= sizeof(reg.value)) // Ensure enough data length for value + { + reg.value = sys_get_be16(frame.data); // Convert received data to big-endian format + LOG_DBG("Received value: %d, 0x%04x", reg.value, reg.value); + } + else + { + LOG_ERR("Received frame with insufficient data length: %u", frame.dlc); + continue; // Skip processing if data length is invalid + } + k_msgq_put(&modbus_msgq, ®, K_NO_WAIT); + break; + } LOG_HEXDUMP_DBG(frame.data, sizeof(frame.data) * sizeof(frame.data[0]), "Frame data:"); } } @@ -112,11 +151,12 @@ int canbus_init(void) return rc; } #endif - rc = can_start(can_dev); - if (rc != 0) { - printf("Error starting CAN controller [%d]", rc); - return 0; - } + rc = can_start(can_dev); + if (rc != 0) + { + printf("Error starting CAN controller [%d]", rc); + return 0; + } LOG_DBG("CAN device %s is ready", can_dev->name); LOG_DBG("Trying to start rx thread"); @@ -137,7 +177,7 @@ int canbus_init(void) return 0; // Return 0 on success } -int canbus_send_message(uint8_t destination_node, uint8_t register_address, uint8_t *data, size_t data_length) +int canbus_send_register(uint8_t destination_node, uint8_t register_address, uint8_t *data, size_t data_length) { struct can_frame frame; if (data_length > sizeof(frame.data)) @@ -155,7 +195,8 @@ int canbus_send_message(uint8_t destination_node, uint8_t register_address, uint // Send the CAN frame int rc = can_send(can_dev, &frame, K_MSEC(100), canbus_tx_callback, NULL); - if (rc < 0) { + if (rc < 0) + { LOG_ERR("Failed to send CAN message: %d", rc); return rc; // Return error code } @@ -163,3 +204,25 @@ int canbus_send_message(uint8_t destination_node, uint8_t register_address, uint LOG_DBG("CAN message sent: ID=0x%08x, DLC=%u", frame.id, frame.dlc); return 0; } + +int canbus_request_register(uint8_t node_id, uint8_t register_address) +{ + int rc; + struct can_frame frame; + + // Prepare the CAN frame for the request + frame.id = (node_id << 8) | register_address; // Standard ID with node ID in the first byte + frame.dlc = 0; // No data for request + frame.flags = CAN_FRAME_RTR; // Set RTR flag for request + + // Send the CAN frame + rc = can_send(can_dev, &frame, K_MSEC(100), canbus_tx_callback, NULL); + if (rc < 0) + { + LOG_ERR("Failed to send CAN request: %d", rc); + return rc; // Return error code + } + + LOG_DBG("CAN request sent: ID=0x%08x", frame.id); + return 0; +} diff --git a/software/lib/canbus.h b/software/lib/canbus.h index 0d4266d..0cb353d 100644 --- a/software/lib/canbus.h +++ b/software/lib/canbus.h @@ -3,11 +3,13 @@ #include #include +#include "canbus_registers.h" #define CANBUS_RX_THREAD_STACK_SIZE (512) -#define CANBUS_RX_THREAD_PRIORITY (-2) +#define CANBUS_RX_THREAD_PRIORITY (5) +#define CANBUS_RX_MSGQ_SIZE (5) -typedef struct { +typedef struct can_frame_t{ /** Standard (11-bit) or extended (29-bit) CAN identifier. */ uint32_t id; /** Data Length Code (DLC) indicating data length in bytes. */ @@ -24,6 +26,7 @@ typedef struct { } can_frame_t; int canbus_init(void); -int canbus_send_message(uint8_t destination_node, uint8_t register_address, uint8_t *data, size_t data_length); +int canbus_send_register(uint8_t node_id, uint8_t register_address, uint8_t *data, size_t data_length); +int canbus_request_register(uint8_t node_id, uint8_t register_address); #endif // __CANBUS_H__ \ No newline at end of file diff --git a/software/lib/canbus_registers.h b/software/lib/canbus_registers.h new file mode 100644 index 0000000..b915c89 --- /dev/null +++ b/software/lib/canbus_registers.h @@ -0,0 +1,18 @@ +#ifndef __CANBUS_REGISTERS_H__ +#define __CANBUS_REGISTERS_H__ + +typedef enum { + CANBUS_REGISTER_VALVE_COMMAND = 0x00, + CANBUS_REGISTER_VALVE_STATE = 0x01, + + CANBUS_REGISTER_WATER_LEVEL_MM = 0x10, + CANBUS_REGISTER_WATER_MINIMUM = 0x11, + CANBUS_REGISTER_WATER_MAXIMUM = 0x12, +} canbus_registers_t; + +typedef enum { + CANBUS_REGISTER_COMMAND_SET = 0x00, + CANBUS_REGISTER_COMMAND_GET = 0x01, +} canbus_register_command_t; + +#endif // __CANBUS_REGISTERS_H__ \ No newline at end of file diff --git a/software/lib/config.c b/software/lib/config.c index 54155aa..0c9632a 100644 --- a/software/lib/config.c +++ b/software/lib/config.c @@ -6,6 +6,77 @@ LOG_MODULE_REGISTER(config, CONFIG_LOG_SETTINGS_LEVEL); extern int canbus_node_id; // Default node ID for CAN bus +// Only compile shell commands if CONFIG_SHELL is enabled +// and CONFIG_REBOOT is enabled, as rebooting is required to apply changes +#ifdef CONFIG_SHELL +#ifndef CONFIG_REBOOT +#error You must enable CONFIG_REBOOT to use the shell commands +#endif // CONFIG_REBOOT + +#include +#include +#include + +int reboot_system(const struct shell *shell, size_t argc, char **argv) +{ + // Reboot the system + shell_print(shell, "Rebooting node in 1 second..."); + k_sleep(K_MSEC(1000)); // Wait for 1 second before rebooting + sys_reboot(SYS_REBOOT_COLD); // Perform a cold reboot + return 0; // Return 0 on success +} + +int shell_print_config(const struct shell *shell, size_t argc, char **argv) +{ + // Print the current settings for the CAN bus node ID + shell_print(shell, "Current configuration settings:"); + shell_print(shell, "%26s <%d>", "CANBUS node ID:", canbus_node_id); + return 0; +} + +int shell_set_canbus_node_id(const struct shell *shell, size_t argc, char **argv) +{ + shell_print(shell, "argument count: %zu", argc); + if (argc != 2) + { + shell_error(shell, "Usage: config set_nodeid "); + return -EINVAL; // Invalid argument + } + + int new_node_id = atoi(argv[1]); + if (new_node_id < 1 || new_node_id > 7) + { + shell_error(shell, "Invalid CAN bus node ID: <%d>. Must be between 1 and 7.", new_node_id); + return -EINVAL; // Invalid argument + } + + canbus_node_id = new_node_id; + LOG_INF("Set CAN bus node ID to <%d>", canbus_node_id); + + // Save the new node ID to settings + int rc = settings_save_one("canbus/node_id", &canbus_node_id, sizeof(canbus_node_id)); + if (rc < 0) + { + shell_error(shell, "Failed to save CAN bus node ID: %d", rc); + return rc; // Save error + } + + shell_print(shell, "CAN bus node ID set to <%d> and saved to settings", canbus_node_id); + shell_warn(shell, "Reboot the node (command: 'reboot') to apply changes."); + return 0; // Return 0 on success +} + +SHELL_STATIC_SUBCMD_SET_CREATE( + config_cmds, + SHELL_CMD_ARG(print, NULL, "Print current configuration settings", shell_print_config, 0, 0), + SHELL_CMD_ARG(set_nodeid, NULL, "Set canbus node id", shell_set_canbus_node_id, 2, 0), + SHELL_SUBCMD_SET_END +); + +SHELL_CMD_REGISTER(config, &config_cmds, "Configuration commands", NULL); +SHELL_CMD_REGISTER(reboot, NULL, "Reboot the node", reboot_system); +#endif // CONFIG_SHELL + static int settings_canbus(const char *key, size_t len, settings_read_cb read_cb, void *cb_arg) { const char *next; diff --git a/software/lib/modbus.h b/software/lib/modbus.h index 3d43bac..5959b7c 100644 --- a/software/lib/modbus.h +++ b/software/lib/modbus.h @@ -3,6 +3,10 @@ #include +#define MODBUS_THREAD_STACK_SIZE (512) +#define MODBUS_THREAD_PRIORITY (6) +#define MODBUS_MSGQ_SIZE (5) + const static struct modbus_iface_param client_param = { .mode = MODBUS_MODE_RTU, .rx_timeout = 50000, @@ -12,6 +16,12 @@ const static struct modbus_iface_param client_param = { }, }; +typedef struct { + uint8_t reg; + uint8_t command; // 0 for set, 1 for get + int16_t value; +} modbus_register_t; + int mb_init_client(void); int mb_read_holding_registers(int node, uint16_t reg_addr, uint16_t *data, size_t len); int mb_read_water_level(double *mb_read_water_level); diff --git a/software/test_canbus/prj.conf b/software/test_canbus/prj.conf index ae29397..6f7c90c 100644 --- a/software/test_canbus/prj.conf +++ b/software/test_canbus/prj.conf @@ -1,11 +1,20 @@ CONFIG_LOG=y CONFIG_LOG_DEFAULT_LEVEL=3 -CONFIG_LOG_SETTINGS_LEVEL=4 -CONFIG_LOG_CAN_LEVEL=4 +# CONFIG_LOG_SETTINGS_LEVEL=4 +# CONFIG_LOG_CAN_LEVEL=4 CONFIG_CBPRINTF_FP_SUPPORT=y CONFIG_UART_CONSOLE=y # Console on USART1 #CONFIG_RTT_CONSOLE=y #CONFIG_USE_SEGGER_RTT=y +# CAN loopback mode for testing CONFIG_LOOPBACK_MODE=y +CONFIG_SHELL=y +CONFIG_CAN_SHELL=y +CONFIG_GPIO_SHELL=y +CONFIG_REBOOT=y + +# CONFIG_USE_SEGGER_RTT=y +# CONFIG_SHELL_BACKEND_RTT=y +# CONFIG_SHELL_BACKEND_SERIAL=n \ No newline at end of file diff --git a/software/test_canbus/src/main.c b/software/test_canbus/src/main.c index 1c8b85c..a95d4f5 100644 --- a/software/test_canbus/src/main.c +++ b/software/test_canbus/src/main.c @@ -7,17 +7,80 @@ #include #include #include +#include #include "canbus.h" +#include "modbus.h" #include "config.h" LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL); +K_THREAD_STACK_DEFINE(fake_modbus_stack, MODBUS_THREAD_STACK_SIZE); +K_MSGQ_DEFINE(modbus_msgq, sizeof(modbus_register_t), 10, 4); + +int water_min = 0; +int water_max = 2000; // Maximum water level in mm + +void fake_modbus_thread(void *arg1, void *arg2, void *arg3) +{ + modbus_register_t reg; + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + LOG_INF("Fake Modbus thread started"); + + while (1) + { + k_msgq_get(&modbus_msgq, ®, K_FOREVER); + LOG_INF("Received Modbus register: 0x%02x, command: %s, value: %d", reg.reg, reg.command ? "GET" : "SET", reg.value); + switch (reg.command) + { + case CANBUS_REGISTER_COMMAND_SET: + switch (reg.reg) + { + case CANBUS_REGISTER_WATER_MINIMUM: + water_min = reg.value; + LOG_INF("Set water minimum to %d mm", water_min); + break; + case CANBUS_REGISTER_WATER_MAXIMUM: + water_max = reg.value; + LOG_INF("Set water maximum to %d mm", water_max); + break; + default: + LOG_INF("Received unknown command: 0x%02x for register: 0x%02x", reg.command, reg.reg); + break; + } + case CANBUS_REGISTER_COMMAND_GET: + switch (reg.reg) + { + case CANBUS_REGISTER_WATER_LEVEL_MM: + // Simulate reading water level in mm + reg.value = (water_min + water_max) / 2; // Example: average of min and max + LOG_INF("Read water level: %d mm", reg.value); + break; + case CANBUS_REGISTER_WATER_MINIMUM: + reg.value = water_min; + LOG_INF("Read water minimum: %d mm", reg.value); + break; + case CANBUS_REGISTER_WATER_MAXIMUM: + reg.value = water_max; + LOG_INF("Read water maximum: %d mm", reg.value); + break; + default: + LOG_INF("Received unknown command: 0x%02x for register: 0x%02x", reg.command, reg.reg); + break; + } + break; + } + } +} + int main(void) { int rc; LOG_INF("Starting CAN bus initialization..."); - + // Initialize the configuration and CAN bus rc = config_init(); if (rc != 0) @@ -32,25 +95,38 @@ int main(void) return rc; } LOG_INF("CAN bus initialized successfully"); - uint16_t counter = 0; - k_sleep(K_SECONDS(1)); // Sleep for 1 second before starting the loop + struct k_thread fake_modbus_thread_data; + k_tid_t fake_modbus_tid = k_thread_create(&fake_modbus_thread_data, fake_modbus_stack, + K_THREAD_STACK_SIZEOF(fake_modbus_stack), fake_modbus_thread, NULL, NULL, NULL, + MODBUS_THREAD_PRIORITY, 0, K_NO_WAIT); + if (fake_modbus_tid == NULL) + { + LOG_ERR("Failed to create fake Modbus thread"); + return -ENOMEM; // Not enough memory to create thread + } + + LOG_INF("Fake Modbus thread created successfully"); while (1) { - uint8_t data[2] = {0x00}; - UNALIGNED_PUT(sys_cpu_to_be16(counter), (uint16_t *)&data[0]); - canbus_send_message(1, 0x01, data, sizeof(data)); // Example message sending - LOG_DBG("Sent message with counter: %d", counter); - canbus_send_message(2, 0x01, data, sizeof(data)); // Example message sending - LOG_DBG("Sent message with counter: %d", counter); - counter++; - if (counter > 1000) // Reset counter after 1000 - { - counter = 0; - } + canbus_request_register(0x01, CANBUS_REGISTER_WATER_LEVEL_MM); // Request water level in mm + k_sleep(K_MSEC(150)); // Wait for 150 ms to allow the request to be processed + canbus_request_register(0x01, CANBUS_REGISTER_WATER_MINIMUM); // Request water minimum + k_sleep(K_MSEC(150)); // Wait for 150 ms to allow the request to be processed + + int water_tmp = -100; + uint8_t data[2]; + sys_put_be16(water_tmp, data); // Convert water_tmp to big-endian format + rc = canbus_send_register(0x01, CANBUS_REGISTER_WATER_MINIMUM, data, sizeof(data)); + k_sleep(K_MSEC(150)); // Wait for 150 ms to allow the request to be processed + canbus_request_register(0x01, CANBUS_REGISTER_WATER_LEVEL_MM); // Request water level in mm + k_sleep(K_MSEC(150)); // Wait for 150 ms to allow the request to be processed + canbus_request_register(0x01, CANBUS_REGISTER_WATER_MINIMUM); // Request water minimum + k_sleep(K_SECONDS(5)); // Sleep for 5 second before next iteration - return 0; // Exit the loop after one iteration for testing purposes - // In a real application, you would likely not return here and continue the loop indefinitely + return 0; // Exit the loop after one iteration for testing purposes + // In a real application, you would remove this return statement + // to keep the loop running indefinitely. } return 0; }