From 9d017f9f8da4f61219eb0d55ac394d8d639e076a Mon Sep 17 00:00:00 2001 From: Eduard Iten Date: Mon, 16 Jun 2025 13:09:27 +0200 Subject: [PATCH 1/3] Zwischenspeichern --- software/.vscode/settings.json | 16 +- .../boards/iten/valve_node/Kconfig.valve_node | 8 + .../boards/iten/valve_node/valve_node.dts | 10 +- software/lib/canbus.c | 140 +++++++++++++----- software/lib/canbus.h | 22 ++- software/lib/config.c | 62 ++++++++ software/lib/config.h | 11 ++ software/lib/settings.h | 17 --- software/test_canbus/CMakeLists.txt | 1 + software/test_canbus/prj.conf | 3 +- software/test_canbus/src/main.c | 32 +++- 11 files changed, 258 insertions(+), 64 deletions(-) create mode 100644 software/lib/config.c create mode 100644 software/lib/config.h delete mode 100644 software/lib/settings.h diff --git a/software/.vscode/settings.json b/software/.vscode/settings.json index 15b66f3..c595456 100644 --- a/software/.vscode/settings.json +++ b/software/.vscode/settings.json @@ -1,7 +1,4 @@ { - "nrf-connect.applications": [ - "${workspaceFolder}\\modbus_test" - ], "files.associations": { "log.h": "c", "modbus.h": "c", @@ -10,6 +7,15 @@ "string_view": "c", "canbus.h": "c", "kernel.h": "c", - "settings.h": "c" - } + "settings.h": "c", + "can.h": "c" + }, + "C_Cpp.default.compileCommands": [ + "build/compile_commands.json", + "../build/compile_commands.json" + ], + "cmake.sourceDirectory": "/home/edi/zephyrproject/projects/irrigation_system/software/test_canbus", + "nrf-connect.applications": [ + "${workspaceFolder}/test_canbus" + ], } \ No newline at end of file diff --git a/software/boards/iten/valve_node/Kconfig.valve_node b/software/boards/iten/valve_node/Kconfig.valve_node index a52f9b8..e155e80 100644 --- a/software/boards/iten/valve_node/Kconfig.valve_node +++ b/software/boards/iten/valve_node/Kconfig.valve_node @@ -13,6 +13,14 @@ config LOG_CAN_LEVEL int "Log level for CAN" default 3 range 0 7 + help + Set the log level for CAN messages. + 0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Trace, 6 = Debug2, 7 = Debug3 + +config LOG_SETTINGS_LEVEL + int "Log level for settings" + default 3 + range 0 7 help Set the log level for CAN messages. 0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Trace, 6 = Debug2, 7 = Debug3 \ No newline at end of file diff --git a/software/boards/iten/valve_node/valve_node.dts b/software/boards/iten/valve_node/valve_node.dts index bd39f4a..76ace88 100644 --- a/software/boards/iten/valve_node/valve_node.dts +++ b/software/boards/iten/valve_node/valve_node.dts @@ -13,12 +13,18 @@ model = "Iten engineering Valve Node"; compatible = "iten,valve-node", "st,stm32f103rb"; + can_loopback0: can_loopback0 { + status = "okay"; + compatible = "zephyr,can-loopback"; + }; + chosen { zephyr,console = &usart1; zephyr,shell-uart = &usart1; zephyr,sram = &sram0; zephyr,flash = &flash0; - zephyr,canbus = &can1; + //zephyr,canbus = &can1; + zephyr,canbus = &can_loopback0; }; leds: leds { @@ -174,7 +180,7 @@ pinctrl-0 = <&can_rx_pa11 &can_tx_pa12>; pinctrl-names = "default"; status= "okay"; - bus-speed = < 125000 >; + bus-speed = <500000>; }; &exti { diff --git a/software/lib/canbus.c b/software/lib/canbus.c index 55442a3..23d12c1 100644 --- a/software/lib/canbus.c +++ b/software/lib/canbus.c @@ -1,53 +1,80 @@ #include "canbus.h" #include #include -#include "settings.h" +#include +#include "config.h" + +int canbus_node_id = 1; // Default node ID for CAN bus const struct device *const can_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_canbus)); -K_THREAD_STACK_DEFINE(rx_thread_stack, RX_THREAD_STACK_SIZE); +struct k_thread rx_thread_data; -static uint8_t node_id = 12; // Default node ID +K_THREAD_STACK_DEFINE(rx_thread_stack, CANBUS_RX_THREAD_STACK_SIZE); +CAN_MSGQ_DEFINE(commands_msq, 2); LOG_MODULE_REGISTER(canbus, CONFIG_LOG_CAN_LEVEL); -static int canbus_set(const char *key, size_t len, settings_read_cb read_cb, void *cb_arg) +static void rx_thread(void *arg1, void *arg2, void *arg3) { - const char *next; - int rc; + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + const struct can_filter filter = { + .flags = CAN_FILTER_IDE, + .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 + }; - // Handle setting values for CAN bus configuration - LOG_DBG("Setting CAN bus configuration: key=%s, len=%zu", key, len); - if (settings_name_steq(key, "node_id", &next) && next != NULL) + struct can_frame frame; + int filter_id; + + 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); + + while (1) { - if (len != sizeof(node_id)) + 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) { - LOG_ERR("Invalid length for node_id setting: %zu", len); - return -EINVAL; // Invalid argument + continue; } - int rc = read_cb(cb_arg, &node_id, sizeof(node_id)); - if (rc < 0) + uint8_t target_node = (frame.id >> 8) & 0xFF; // Extract the target node from the ID + if (target_node != canbus_node_id) { - LOG_ERR("Failed to read node_id setting: %d", rc); - return rc; // Read error + LOG_WRN("Received frame for different node ID: %d, expected: %d", target_node, canbus_node_id); + continue; // Ignore frames not meant for this node } - LOG_DBG("Set CAN bus node ID to: %d", node_id); + uint8_t target_register = frame.id & 0xFF; // Extract the register address from the ID + + // Log the received frame details + LOG_DBG("Received CAN frame: ID=0x%08x, DLC=%u, Flags=0x%02x, Target Node=%d, Register Address=%d", + frame.id, frame.dlc, frame.flags, target_node, target_register); + LOG_HEXDUMP_DBG(frame.data, sizeof(frame.data) * sizeof(frame.data[0]), "Frame data:"); } - return 0; // Return 0 on success } -struct settings_handler canbus_settings_handler = { - .name = "canbus", - .h_set = canbus_set, // No settings set handler -}; +static void canbus_tx_callback(const struct device *dev, int error, void *user_data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(user_data); + if (error != 0) + { + LOG_ERR("CAN transmission error: %d", error); + } + else + { + LOG_DBG("CAN message sent successfully"); + } +} int canbus_init(void) { + k_tid_t rx_tid; int rc; - settings_subsys_init(); - settings_register(&canbus_settings_handler); - settings_load(); rc = settings_get_val_len("canbus/node_id"); if (rc < 0) @@ -58,8 +85,8 @@ int canbus_init(void) else if (rc == 0) { - LOG_WRN("No CAN bus node id found in settings, using default default (%d)", node_id); - settings_save_one("canbus/node_id", &node_id, sizeof(node_id)); + LOG_WRN("No CAN bus node id found in settings, using default (%d)", canbus_node_id); + settings_save_one("canbus/node_id", &canbus_node_id, sizeof(canbus_node_id)); if (rc < 0) { LOG_ERR("Failed to save default CAN bus node id: %d", rc); @@ -68,13 +95,7 @@ int canbus_init(void) } else { - rc = settings_load_one("canbus/node_id", &node_id, sizeof(node_id)); - if (rc < 0) - { - LOG_ERR("Failed to load CAN bus node id from settings: %d", rc); - return rc; - } - LOG_DBG("Loaded CAN bus node id: %d", node_id); + LOG_DBG("CAN bus node ID found in settings: %d", canbus_node_id); } if (!device_is_ready(can_dev)) @@ -91,5 +112,54 @@ int canbus_init(void) return rc; } #endif + 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"); + + // Create the RX thread for handling incoming CAN messages + rx_tid = k_thread_create(&rx_thread_data, rx_thread_stack, + K_THREAD_STACK_SIZEOF(rx_thread_stack), + rx_thread, NULL, NULL, NULL, + CANBUS_RX_THREAD_PRIORITY, 0, K_NO_WAIT); + + if (rx_tid == NULL) + { + LOG_ERR("Failed to create CAN RX thread"); + return -ENOMEM; // Not enough memory to create thread + } + + LOG_INF("CAN bus initialized with node ID: %d,", canbus_node_id); + 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) +{ + struct can_frame frame; + if (data_length > sizeof(frame.data)) + { + LOG_ERR("Data length exceeds maximum CAN frame size"); + return -EINVAL; // Invalid argument + } + + frame.id = (destination_node << 8) | register_address; // Standard ID with node ID in the first byte + frame.dlc = data_length; + frame.flags = 0; // No special flags + + // Copy data into the frame + memcpy(frame.data, data, data_length); + + // Send the CAN frame + int rc = can_send(can_dev, &frame, K_MSEC(100), canbus_tx_callback, NULL); + if (rc < 0) { + LOG_ERR("Failed to send CAN message: %d", rc); + return rc; // Return error code + } + + LOG_DBG("CAN message sent: ID=0x%08x, DLC=%u", frame.id, frame.dlc); return 0; -} \ No newline at end of file +} diff --git a/software/lib/canbus.h b/software/lib/canbus.h index c58a404..0d4266d 100644 --- a/software/lib/canbus.h +++ b/software/lib/canbus.h @@ -4,10 +4,26 @@ #include #include -#define RX_THREAD_STACK_SIZE 512 -#define RX_THREAD_PRIORITY 2 +#define CANBUS_RX_THREAD_STACK_SIZE (512) +#define CANBUS_RX_THREAD_PRIORITY (-2) + +typedef struct { + /** Standard (11-bit) or extended (29-bit) CAN identifier. */ + uint32_t id; + /** Data Length Code (DLC) indicating data length in bytes. */ + uint8_t dlc; + /** Flags. */ + uint8_t flags; + /** The frame payload data. */ + union { + /** Payload data accessed as unsigned 8 bit values. */ + uint8_t data[8]; + uint16_t data_16[4]; + uint32_t data_32[2]; + } data; +} can_frame_t; int canbus_init(void); -int canbus_send_message(const struct can_frame *frame); +int canbus_send_message(uint8_t destination_node, uint8_t register_address, uint8_t *data, size_t data_length); #endif // __CANBUS_H__ \ No newline at end of file diff --git a/software/lib/config.c b/software/lib/config.c new file mode 100644 index 0000000..54155aa --- /dev/null +++ b/software/lib/config.c @@ -0,0 +1,62 @@ +#include "config.h" +#include + +LOG_MODULE_REGISTER(config, CONFIG_LOG_SETTINGS_LEVEL); + + +extern int canbus_node_id; // Default node ID for CAN bus + +static int settings_canbus(const char *key, size_t len, settings_read_cb read_cb, void *cb_arg) +{ + const char *next; + int rc; + + // Handle setting values for CAN bus configuration + LOG_DBG("Trying to read CAN bus configuration: key=%s, len=%zu", key, len); + if (settings_name_steq(key, "node_id", &next) && next == NULL) + { + if (len != sizeof(canbus_node_id)) + { + LOG_ERR("Invalid length for node_id setting: %zu", len); + return -EINVAL; // Invalid argument + } + + rc = read_cb(cb_arg, &canbus_node_id, sizeof(canbus_node_id)); + if (rc < 0) + { + LOG_ERR("Failed to read node_id setting: %d", rc); + return rc; // Read error + } + LOG_INF("Set CAN bus node ID to <%d> from settings", canbus_node_id); + } + return 0; // Return 0 on success +} + +static struct settings_handler settings_handler_canbus = { + .name = "canbus", + .h_set = settings_canbus, // No settings set handler +}; + +int config_init(void) +{ + int rc; + rc=settings_subsys_init(); + if (rc < 0) + { + LOG_ERR("Failed to initialize settings subsystem: %d", rc); + return rc; // Initialization error + } + rc = settings_register(&settings_handler_canbus); + if (rc < 0) + { + LOG_ERR("Failed to register settings handler: %d", rc); + return rc; // Registration error + } + rc = settings_load(); + if (rc < 0) + { + LOG_ERR("Failed to load settings: %d", rc); + return rc; // Load error + } + return 0; // Return 0 on success +} \ No newline at end of file diff --git a/software/lib/config.h b/software/lib/config.h new file mode 100644 index 0000000..78ab7dc --- /dev/null +++ b/software/lib/config.h @@ -0,0 +1,11 @@ +#ifndef __SETTINGS_H__ +#define __SETTINGS_H__ + +#include + +#include +#include + +int config_init(void); + +#endif // __SETTINGS_H__ \ No newline at end of file diff --git a/software/lib/settings.h b/software/lib/settings.h deleted file mode 100644 index 523df6f..0000000 --- a/software/lib/settings.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __SETTINGS_H__ -#define __SETTINGS_H__ - -#include - -#include -#include - -#if defined(CONFIG_SETTINGS_FILE) -#include -#include -#endif - -#define STORAGE_PARTITION storage_partition -#define STORAGE_PARTITION_ID FIXED_PARTITION_ID(STORAGE_PARTITION) - -#endif // __SETTINGS_H__ \ No newline at end of file diff --git a/software/test_canbus/CMakeLists.txt b/software/test_canbus/CMakeLists.txt index 3989c64..17330ae 100644 --- a/software/test_canbus/CMakeLists.txt +++ b/software/test_canbus/CMakeLists.txt @@ -9,6 +9,7 @@ project(hello_world) target_sources(app PRIVATE src/main.c) target_sources(app PRIVATE ../lib/canbus.c) +target_sources(app PRIVATE ../lib/config.c) target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../lib ) diff --git a/software/test_canbus/prj.conf b/software/test_canbus/prj.conf index b95fd27..ae29397 100644 --- a/software/test_canbus/prj.conf +++ b/software/test_canbus/prj.conf @@ -1,5 +1,7 @@ CONFIG_LOG=y CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_SETTINGS_LEVEL=4 +CONFIG_LOG_CAN_LEVEL=4 CONFIG_CBPRINTF_FP_SUPPORT=y CONFIG_UART_CONSOLE=y # Console on USART1 @@ -7,4 +9,3 @@ CONFIG_UART_CONSOLE=y # Console on USART1 #CONFIG_USE_SEGGER_RTT=y CONFIG_LOOPBACK_MODE=y -CONFIG_LOG_CAN_LEVEL=4 \ No newline at end of file diff --git a/software/test_canbus/src/main.c b/software/test_canbus/src/main.c index eec6388..1c8b85c 100644 --- a/software/test_canbus/src/main.c +++ b/software/test_canbus/src/main.c @@ -6,15 +6,25 @@ #include #include -#include +#include #include "canbus.h" +#include "config.h" LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL); int main(void) { int rc; + LOG_INF("Starting CAN bus initialization..."); + + // Initialize the configuration and CAN bus + rc = config_init(); + if (rc != 0) + { + LOG_ERR("Failed to initialize configuration: %d", rc); + return rc; + } rc = canbus_init(); if (rc != 0) { @@ -22,5 +32,25 @@ 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 + + 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; + } + 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; } From 852c5c72beca11e7fecc4527cf19ae8907ef3824 Mon Sep 17 00:00:00 2001 From: Eduard Iten Date: Tue, 17 Jun 2025 16:43:13 +0200 Subject: [PATCH 2/3] 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; } From e9fc3700942fd2abb8feaea1fcf38468fda61668 Mon Sep 17 00:00:00 2001 From: Eduard Iten Date: Thu, 19 Jun 2025 16:56:21 +0200 Subject: [PATCH 3/3] canbus, modbus working, valve has to be implemented in real --- software/.vscode/extensions.json | 10 + software/.vscode/launch.json | 28 ++ software/.vscode/settings.json | 31 +- software/.vscode/tasks.json | 96 +++++ software/CMakeLists.txt | 25 ++ .../boards/iten/valve_node/Kconfig.valve_node | 36 +- software/boards/iten/valve_node/board.cmake | 1 + .../boards/iten/valve_node/valve_node.dts | 2 +- software/lib/canbus.c | 382 +++++++++-------- software/lib/canbus.h | 30 +- software/lib/canbus_registers.h | 48 ++- software/lib/config.c | 133 ------ software/lib/config.h | 11 - software/lib/modbus.c | 163 -------- software/lib/modbus.h | 34 -- software/lib/valve.c | 163 ++++++++ software/lib/valve.h | 27 ++ software/lib/waterlevel_sensor.c | 384 ++++++++++++++++++ software/lib/waterlevel_sensor.h | 27 ++ software/{test_canbus => }/prj.conf | 17 +- software/src/main.c | 53 +++ software/test_canbus/.gitignore | 1 - .../test_canbus/.vscode/c_cpp_properties.json | 16 - software/test_canbus/CMakeLists.txt | 15 - .../test_canbus/boards/nucleo_f103rb.overlay | 9 - software/test_canbus/src/main.c | 132 ------ software/test_modbus/.gitignore | 1 - .../test_modbus/.vscode/c_cpp_properties.json | 16 - software/test_modbus/CMakeLists.txt | 14 - .../test_modbus/boards/nucleo_f103rb.overlay | 9 - software/test_modbus/prj.conf | 7 - software/test_modbus/src/main.c | 78 ---- software/worspace.code-workspace | 132 ++++++ 33 files changed, 1261 insertions(+), 870 deletions(-) create mode 100644 software/.vscode/extensions.json create mode 100644 software/.vscode/launch.json create mode 100644 software/.vscode/tasks.json create mode 100644 software/CMakeLists.txt delete mode 100644 software/lib/config.c delete mode 100644 software/lib/config.h delete mode 100644 software/lib/modbus.c delete mode 100644 software/lib/modbus.h create mode 100644 software/lib/valve.c create mode 100644 software/lib/valve.h create mode 100644 software/lib/waterlevel_sensor.c create mode 100644 software/lib/waterlevel_sensor.h rename software/{test_canbus => }/prj.conf (52%) create mode 100644 software/src/main.c delete mode 100644 software/test_canbus/.gitignore delete mode 100644 software/test_canbus/.vscode/c_cpp_properties.json delete mode 100644 software/test_canbus/CMakeLists.txt delete mode 100644 software/test_canbus/boards/nucleo_f103rb.overlay delete mode 100644 software/test_canbus/src/main.c delete mode 100644 software/test_modbus/.gitignore delete mode 100644 software/test_modbus/.vscode/c_cpp_properties.json delete mode 100644 software/test_modbus/CMakeLists.txt delete mode 100644 software/test_modbus/boards/nucleo_f103rb.overlay delete mode 100644 software/test_modbus/prj.conf delete mode 100644 software/test_modbus/src/main.c create mode 100644 software/worspace.code-workspace diff --git a/software/.vscode/extensions.json b/software/.vscode/extensions.json new file mode 100644 index 0000000..2b32164 --- /dev/null +++ b/software/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + "recommendations": [ + "ms-vscode.cpptools-extension-pack", + "ms-python.python", + "ms-vscode.vscode-embedded-tools", + "ms-vscode.vscode-serial-monitor", + "marus25.cortex-debug", + "donjayamanne.python-environment-manager" + ] +} \ No newline at end of file diff --git a/software/.vscode/launch.json b/software/.vscode/launch.json new file mode 100644 index 0000000..a386ed2 --- /dev/null +++ b/software/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "device": "STM32F103RB", + "cwd": "${workspaceFolder}", + "executable": "build/zephyr/zephyr.elf", + "request": "launch", + "type": "cortex-debug", + //"runToEntryPoint": "main", + "servertype": "jlink", + "gdbPath": "${userHome}/zephyr-sdk-0.17.1/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb", + "preLaunchTask": "West Build" + }, + { + "name": "Attach", + "device": "nRF52840_xxAA", + "cwd": "${workspaceFolder}", + "executable": "build/zephyr/zephyr.elf", + "request": "attach", + "type": "cortex-debug", + // "runToEntryPoint": "main", + "servertype": "jlink", + "gdbPath": "${userHome}/zephyr-sdk-0.17.1/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb" + }, + ] +} \ No newline at end of file diff --git a/software/.vscode/settings.json b/software/.vscode/settings.json index 144ab03..4f59a24 100644 --- a/software/.vscode/settings.json +++ b/software/.vscode/settings.json @@ -1,23 +1,12 @@ { - "files.associations": { - "log.h": "c", - "modbus.h": "c", - "array": "c", - "string": "c", - "string_view": "c", - "canbus.h": "c", - "kernel.h": "c", - "settings.h": "c", - "can.h": "c", - "stdlib.h": "c", - "reboot.h": "c" - }, - "C_Cpp.default.compileCommands": [ - "build/compile_commands.json", - "../build/compile_commands.json" - ], - "cmake.sourceDirectory": "/home/edi/zephyrproject/projects/irrigation_system/software/test_canbus", - "nrf-connect.applications": [ - "${workspaceFolder}/test_canbus" - ], + // Hush CMake + "cmake.configureOnOpen": false, + + // IntelliSense + "C_Cpp.default.compilerPath": "${userHome}/zephyr-sdk-0.17.1/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc.exe", + "C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json", + + // File Associations + "files.associations": { + } } \ No newline at end of file diff --git a/software/.vscode/tasks.json b/software/.vscode/tasks.json new file mode 100644 index 0000000..89ed6b2 --- /dev/null +++ b/software/.vscode/tasks.json @@ -0,0 +1,96 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "West Build", + "type": "shell", + "group": { + "kind": "build", + "isDefault": true + }, + "linux": { + "command": "${userHome}/zephyrproject/.venv/bin/west" + }, + "windows": { + "command": "${userHome}/zephyrproject/.venv/Scripts/west.exe" + }, + "osx": { + "command": "${userHome}/zephyrproject/.venv/bin/west" + }, + "args": [ + "build", + "-p", + "auto", + "-b", + "valve_node" + ], + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "West Configurable Build", + "type": "shell", + "group": { + "kind": "build", + }, + "linux": { + "command": "${userHome}/zephyrproject/.venv/bin/west" + }, + "windows": { + "command": "${userHome}/zephyrproject/.venv/Scripts/west.exe" + }, + "osx": { + "command": "${userHome}/zephyrproject/.venv/bin/west" + }, + "args": [ + "build", + "-p", + "${input:pristine}", + "-b", + "${input:board}" + ], + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "West Flash", + "type": "shell", + "linux": { + "command": "${userHome}/zephyrproject/.venv/bin/west" + }, + "windows": { + "command": "${userHome}/zephyrproject/.venv/Scripts/west.exe" + }, + "osx": { + "command": "${userHome}/zephyrproject/.venv/bin/west" + }, + "args": [ + "flash" + ], + "problemMatcher": [ + "$gcc" + ] + } + ], + "inputs": [ + { + "id": "board", + "type": "promptString", + "default": "valve-node", + "description": "See https://docs.zephyrproject.org/latest/boards/index.html" + }, + { + "id": "pristine", + "type": "pickString", + "description": "Choose when to run a pristine build", + "default": "auto", + "options": [ + "auto", + "always", + "never" + ] + } + ] +} \ No newline at end of file diff --git a/software/CMakeLists.txt b/software/CMakeLists.txt new file mode 100644 index 0000000..5472451 --- /dev/null +++ b/software/CMakeLists.txt @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(valve_node) + +target_sources(app PRIVATE src/main.c) +target_sources(app PRIVATE lib/canbus.c) + +# source files for modbus waterlevel sensor +zephyr_library_sources_ifdef(CONFIG_HAS_MODBUS_WATERLEVEL_SENSOR + lib/waterlevel_sensor.c +) + +#source files for valve +zephyr_library_sources_ifdef(CONFIG_HAS_VALVE + lib/valve.c +) + +zephyr_include_directories( + lib +) diff --git a/software/boards/iten/valve_node/Kconfig.valve_node b/software/boards/iten/valve_node/Kconfig.valve_node index e155e80..1525006 100644 --- a/software/boards/iten/valve_node/Kconfig.valve_node +++ b/software/boards/iten/valve_node/Kconfig.valve_node @@ -1,7 +1,7 @@ config BOARD_VALVE_NODE select SOC_STM32F103XB -mainmenu "Controller Area Network sample application" +mainmenu "APP CAN Settings" config LOOPBACK_MODE bool "Loopback LOOPBACK_MODE" default n @@ -9,6 +9,7 @@ config LOOPBACK_MODE Set the can controller to loopback mode. This allows testing without a second board. +mainmenu "APP Logging Settings" config LOG_CAN_LEVEL int "Log level for CAN" default 3 @@ -23,4 +24,35 @@ config LOG_SETTINGS_LEVEL range 0 7 help Set the log level for CAN messages. - 0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Trace, 6 = Debug2, 7 = Debug3 \ No newline at end of file + 0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Trace, 6 = Debug2, 7 = Debug3 + +config LOG_WATERLEVELSENSOR_LEVEL + int "Log level for waterlevel sensor" + default 3 + range 0 7 + help + Set the log level for CAN messages. + 0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Trace, 6 = Debug2, 7 = Debug3 + +config LOG_VALVE_LEVEL + int "Log level for valve control" + default 3 + range 0 7 + help + Set the log level for valve control messages. + 0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Trace, 6 = Debug2, 7 = Debug3 + +mainmenu "Irrigation controller node configuration" +config HAS_MODBUS_WATERLEVEL_SENSOR + bool "Has modbus water level sensor" + default n + help + Enable modbus water level sensor support. + This allows reading the water level from a modbus device. + +config HAS_VALVE + bool "Has valve control" + default n + help + Enable valve control support. + This allows controlling valves via CAN messages. \ No newline at end of file diff --git a/software/boards/iten/valve_node/board.cmake b/software/boards/iten/valve_node/board.cmake index 9b122cd..ebd92d6 100644 --- a/software/boards/iten/valve_node/board.cmake +++ b/software/boards/iten/valve_node/board.cmake @@ -8,3 +8,4 @@ board_runner_args(jlink "--device=STM32F103RB" "--speed=4000") include(${ZEPHYR_BASE}/boards/common/stm32cubeprogrammer.board.cmake) include(${ZEPHYR_BASE}/boards/common/openocd-stm32.board.cmake) include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) +include(${ZEPHYR_BASE}/boards/common/blackmagicprobe.board.cmake) diff --git a/software/boards/iten/valve_node/valve_node.dts b/software/boards/iten/valve_node/valve_node.dts index 471fe91..f1f38ab 100644 --- a/software/boards/iten/valve_node/valve_node.dts +++ b/software/boards/iten/valve_node/valve_node.dts @@ -184,7 +184,7 @@ pinctrl-0 = <&can_rx_pa11 &can_tx_pa12>; pinctrl-names = "default"; status= "okay"; - bus-speed = <500000>; + bus-speed = <125000>; }; &exti { diff --git a/software/lib/canbus.c b/software/lib/canbus.c index dba7108..e2f78ab 100644 --- a/software/lib/canbus.c +++ b/software/lib/canbus.c @@ -2,140 +2,151 @@ #include #include #include -#include "config.h" -#include "modbus.h" +#include +#include -int canbus_node_id = 1; // Default node ID for CAN bus +#ifdef CONFIG_HAS_MODBUS_WATERLEVEL_SENSOR +#include "waterlevel_sensor.h" +#endif // CONFIG_HAS_MODBUS_WATERLEVEL_SENSOR + +#ifdef CONFIG_HAS_VALVE +#include "valve.h" +#endif // CONFIG_HAS_VALVE 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, CANBUS_RX_MSGQ_SIZE); - LOG_MODULE_REGISTER(canbus, CONFIG_LOG_CAN_LEVEL); -static void rx_thread(void *arg1, void *arg2, void *arg3) +K_MSGQ_DEFINE(canbus_msgq, sizeof(struct can_frame), CANBUS_RX_MSGQ_SIZE, 4); +K_THREAD_STACK_DEFINE(canbus_rx_stack, CANBUS_RX_THREAD_STACK_SIZE); + +uint8_t node_id = 0; // Default node ID, can be set later +uint8_t can_msg_id = 0; + +k_tid_t canbus_rx_thread_id; +struct k_thread canbus_rx_thread_data; + +int canbus_rx_filter_id = -1; + +void canbus_rx_callback(const struct device *dev, struct can_frame *frame, void *user_data) +{ + int rc; + ARG_UNUSED(dev); + ARG_UNUSED(user_data); + + if (frame->id >> 8 != node_id) // Check if the frame ID matches the node ID + { + LOG_DBG("Received CAN frame with ID %d, but it does not match node ID %d", frame->id >> 8, node_id); + return; // Ignore frames that do not match the node ID + } + + // Process the received CAN frame + rc = k_msgq_put(&canbus_msgq, frame, K_NO_WAIT); + if (rc != 0) + { + LOG_ERR("Failed to put CAN frame into message queue: %d", rc); + } +} + +void canbus_thread(void *arg1, void *arg2, void *arg3) { ARG_UNUSED(arg1); ARG_UNUSED(arg2); ARG_UNUSED(arg3); - const struct can_filter filter = { - .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); + LOG_INF("CAN bus thread started"); + // Main loop for CAN bus operations while (1) { - 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); - - uint8_t target_node = (frame.id >> 8) & 0xFF; // Extract the target node from the ID - if (target_node != canbus_node_id) + int rc; + struct can_frame frame; + k_msgq_get(&canbus_msgq, &frame, K_FOREVER); // Wait for a message from the queue + LOG_DBG("Received CAN frame with ID: 0x%02x, DLC: %d, RTR: %d", + frame.id, frame.dlc, (uint8_t)(frame.flags & CAN_FRAME_RTR)); + LOG_HEXDUMP_DBG(frame.data, frame.dlc, "CAN frame data"); + uint8_t reg = frame.id & 0xFF; // Extract register from the frame ID + bool is_rtr = (frame.flags & CAN_FRAME_RTR) != 0; // Check if it's a remote transmission request + switch (reg) { - LOG_WRN("Received frame for different node ID: %d, expected: %d", target_node, canbus_node_id); - continue; // Ignore frames not meant for this node - } - 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) +#ifdef CONFIG_HAS_MODBUS_WATERLEVEL_SENSOR + case CANBUS_REG_WATERLEVEL_LEVEL: + case CANBUS_REG_WATERLEVEL_ZERO_POINT: + case CANBUS_REG_WATERLEVEL_MAX_POINT: + waterlevel_command_t command; + command.cmd = is_rtr ? WATERLEVEL_CMD_GET : WATERLEVEL_CMD_SET; // Determine command type based on RTR + command.reg = reg; // Set the register ID + int16_t value = 0; // Initialize value to 0 + if (!is_rtr) // If it's not a remote request, extract the value from the frame data { - 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); + if (frame.dlc < sizeof(command.data)) + { LOG_ERR("Received frame with insufficient data length: %d", frame.dlc); + continue; // Skip processing if data length is insufficient + } + value = sys_be16_to_cpu(*(uint16_t *)frame.data); // Convert data from big-endian to host byte order + command.data = value; // Set the data field + LOG_DBG("Setting command data to: %d", value); } - continue; - } - // Log the received frame details - 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 + extern struct k_msgq waterlevel_sensor_msgq; // Declare the water level sensor message queue + k_msgq_put(&waterlevel_sensor_msgq, &command, K_NO_WAIT); + break; +#endif // CONFIG_HAS_MODBUS_WATERLEVEL_SENSOR +#ifdef CONFIG_HAS_VALVE + case CANBUS_REG_VALVE_STATUS: + case CANBUS_REG_VALVE_OPERATION: + if (is_rtr) { - 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); + LOG_DBG("Received remote request for valve status or operation"); + if (reg == CANBUS_REG_VALVE_STATUS) + { + valve_send_status(); // Send the current valve status + } + else if (reg == CANBUS_REG_VALVE_OPERATION) + { + valve_send_operation(); // Send the current valve operation state + } else { + LOG_ERR("Unknown valve register: 0x%02x", reg); + continue; // Skip processing if the register is unknown + } } else { - LOG_ERR("Received frame with insufficient data length: %u", frame.dlc); - continue; // Skip processing if data length is invalid + LOG_ERR("Received CAN frame with data for valve status or operation, but RTR is not set"); + continue; // Skip processing if RTR is not set for valve status or operation + } + break; + case CANBUS_REG_VALVE_COMMAND: + { + if (is_rtr) { + LOG_ERR("Received remote request for valve command, but this is not supported"); + continue; // Skip processing if RTR is set for valve command + } else { + // Extract the command from the frame data + if (frame.dlc < sizeof(uint8_t)) { + LOG_ERR("Received frame with insufficient data length for valve command: %d", frame.dlc); + continue; // Skip processing if data length is insufficient + } + uint8_t command = frame.data[0]; // Get the command from the first byte of data + LOG_DBG("Received valve command: 0x%02x", command); + rc = valve_cmd(command); // Send the command to the valve + if (rc < 0) { + LOG_ERR("Failed to send valve command: %d", rc); + continue; // Skip processing if sending the command failed + } } - k_msgq_put(&modbus_msgq, ®, K_NO_WAIT); break; } - LOG_HEXDUMP_DBG(frame.data, sizeof(frame.data) * sizeof(frame.data[0]), "Frame data:"); + default: + LOG_DBG("Received CAN frame with unknown register: 0x%02x", reg); + break; + } + #endif // CONFIG_HAS_VALVE } } -static void canbus_tx_callback(const struct device *dev, int error, void *user_data) -{ - ARG_UNUSED(dev); - ARG_UNUSED(user_data); - - if (error != 0) - { - LOG_ERR("CAN transmission error: %d", error); - } - else - { - LOG_DBG("CAN message sent successfully"); - } -} int canbus_init(void) { - k_tid_t rx_tid; - int rc; - - rc = settings_get_val_len("canbus/node_id"); - if (rc < 0) - { - LOG_ERR("Failed to check CAN bus settings: %d", rc); - return rc; - } - - else if (rc == 0) - { - LOG_WRN("No CAN bus node id found in settings, using default (%d)", canbus_node_id); - settings_save_one("canbus/node_id", &canbus_node_id, sizeof(canbus_node_id)); - if (rc < 0) - { - LOG_ERR("Failed to save default CAN bus node id: %d", rc); - return rc; - } - } - else - { - LOG_DBG("CAN bus node ID found in settings: %d", canbus_node_id); - } + int rc = 0; if (!device_is_ready(can_dev)) { @@ -151,6 +162,7 @@ int canbus_init(void) return rc; } #endif + rc = can_start(can_dev); if (rc != 0) { @@ -158,71 +170,117 @@ int canbus_init(void) return 0; } LOG_DBG("CAN device %s is ready", can_dev->name); + // Initialize the CAN RX thread + canbus_rx_thread_id = k_thread_create(&canbus_rx_thread_data, canbus_rx_stack, + K_THREAD_STACK_SIZEOF(canbus_rx_stack), canbus_thread, + NULL, NULL, NULL, + CANBUS_RX_THREAD_PRIORITY, 0, K_NO_WAIT); + k_thread_name_set(canbus_rx_thread_id, "canbus_rx"); - LOG_DBG("Trying to start rx thread"); - - // Create the RX thread for handling incoming CAN messages - rx_tid = k_thread_create(&rx_thread_data, rx_thread_stack, - K_THREAD_STACK_SIZEOF(rx_thread_stack), - rx_thread, NULL, NULL, NULL, - CANBUS_RX_THREAD_PRIORITY, 0, K_NO_WAIT); - - if (rx_tid == NULL) - { - LOG_ERR("Failed to create CAN RX thread"); - return -ENOMEM; // Not enough memory to create thread - } - - LOG_INF("CAN bus initialized with node ID: %d,", canbus_node_id); - return 0; // Return 0 on success -} - -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)) - { - LOG_ERR("Data length exceeds maximum CAN frame size"); - return -EINVAL; // Invalid argument - } - - frame.id = (destination_node << 8) | register_address; // Standard ID with node ID in the first byte - frame.dlc = data_length; - frame.flags = 0; // No special flags - - // Copy data into the frame - memcpy(frame.data, data, data_length); - - // Send the CAN frame - int rc = can_send(can_dev, &frame, K_MSEC(100), canbus_tx_callback, NULL); - if (rc < 0) - { - LOG_ERR("Failed to send CAN message: %d", rc); - return rc; // Return error code - } - - LOG_DBG("CAN message sent: ID=0x%08x, DLC=%u", frame.id, frame.dlc); + const struct can_filter filter = { + .id = node_id << 8, // Standard ID with node ID in the first byte + .mask = 0x700, // Mask to match the first byte of the ID + .flags = 0, // No special flags + }; + canbus_rx_filter_id = can_add_rx_filter(can_dev, canbus_rx_callback, NULL, &filter); + LOG_DBG("CAN RX filter added for node ID %d", canbus_rx_filter_id); return 0; } -int canbus_request_register(uint8_t node_id, uint8_t register_address) +void canbus_tx8_callback(const struct device *dev, int error, void *user_data) { - int rc; - struct can_frame frame; + ARG_UNUSED(dev); + uint8_t frame_id = *(uint8_t *)user_data; // Retrieve the frame ID from user data - // 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) + if (error != 0) { - LOG_ERR("Failed to send CAN request: %d", rc); - return rc; // Return error code + LOG_ERR("CAN transmission error. Error code: %d, Frame ID: %d", error, frame_id); } + else + { + LOG_DBG("CAN message with Frame ID %d sent successfully", frame_id); + } + free(user_data); // Free the allocated memory for frame ID +} - LOG_DBG("CAN request sent: ID=0x%08x", frame.id); +int canbus_send8(uint16_t reg, uint8_t value) +{ + int rc = 0; + struct can_frame frame = { + .id = (node_id << 8) | reg, // Standard ID with node ID in the first byte + .dlc = sizeof(value), // Data Length Code (DLC) + }; + frame.data[0] = value; // Set the value in the first byte of the data + uint8_t *frame_id = malloc(sizeof(uint8_t)); // Allocate memory for frame ID + LOG_DBG("Preparing to send 8-bit value 0x%02x to register 0x%02x on node %d", value, reg, node_id); + if (frame_id == NULL) + { + LOG_ERR("Failed to allocate memory for frame ID"); + return -ENOMEM; // Not enough memory + } + *frame_id = can_msg_id++; // Increment message ID for uniqueness + LOG_DBG("Using frame ID: %d", *frame_id); + rc = can_send(can_dev, &frame, CANBUS_TX_TIMEOUT, canbus_tx8_callback, frame_id); + // Send the CAN frame with a timeout and callback + if (rc != 0) + { + LOG_ERR("Failed to queue CAN frame: %d", rc); + free(frame_id); // Free the allocated memory for frame ID + return rc; + } return 0; } + +void canbus_tx16_callback(const struct device *dev, int error, void *user_data) +{ + ARG_UNUSED(dev); + uint8_t frame_id = *(uint8_t *)user_data; // Retrieve the frame ID from user data + + if (error != 0) + { + LOG_ERR("CAN transmission error. Error code: %d, Frame ID: %d", error, frame_id); + } + else + { + LOG_DBG("CAN message with Frame ID %d sent successfully", frame_id); + } + free(user_data); // Free the allocated memory for frame ID +} + +int canbus_send16(uint16_t reg, uint16_t value) +{ + int rc = 0; + union data_type + { + int16_t value; + uint8_t bytes[2]; + } data; + data.value = sys_cpu_to_be16(value); // Convert value to big-endian format + + struct can_frame frame = { + .id = (node_id << 8) | reg, // Standard ID with node ID in the first byte + .dlc = sizeof(data), // Data Length Code (DLC) + }; + memcpy(frame.data, data.bytes, sizeof(data)); // Copy data into the frame + uint8_t *frame_id = malloc(sizeof(uint8_t)); // Allocate memory for frame ID + LOG_DBG("Preparing to send 16-bit value 0x%04x to register 0x%02x on node %d", value, reg, node_id); + if (frame_id == NULL) + { + LOG_ERR("Failed to allocate memory for frame ID"); + return -ENOMEM; // Not enough memory + } + *frame_id = can_msg_id++; // Increment message ID for uniqueness + LOG_DBG("Using frame ID: %d", *frame_id); + rc = can_send(can_dev, &frame, CANBUS_TX_TIMEOUT, canbus_tx16_callback, frame_id); + // Send the CAN frame with a timeout and callback + if (rc != 0) + { + LOG_ERR("Failed to queue CAN frame: %d", rc); + free(frame_id); // Free the allocated memory for frame ID + return rc; + } + + LOG_DBG("Queued 16-bit value 0x%04x to register 0x%02x on node %d, frame ID: %d", + value, reg, node_id, *frame_id); + return 0; +} \ No newline at end of file diff --git a/software/lib/canbus.h b/software/lib/canbus.h index 0cb353d..51e6ea2 100644 --- a/software/lib/canbus.h +++ b/software/lib/canbus.h @@ -1,32 +1,16 @@ #ifndef __CANBUS_H__ #define __CANBUS_H__ -#include -#include +#include #include "canbus_registers.h" -#define CANBUS_RX_THREAD_STACK_SIZE (512) -#define CANBUS_RX_THREAD_PRIORITY (5) -#define CANBUS_RX_MSGQ_SIZE (5) - -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. */ - uint8_t dlc; - /** Flags. */ - uint8_t flags; - /** The frame payload data. */ - union { - /** Payload data accessed as unsigned 8 bit values. */ - uint8_t data[8]; - uint16_t data_16[4]; - uint32_t data_32[2]; - } data; -} can_frame_t; +#define CANBUS_RX_THREAD_STACK_SIZE (512) // Stack size for the CAN RX thread +#define CANBUS_RX_THREAD_PRIORITY (5) // Priority for the CAN RX thread +#define CANBUS_RX_MSGQ_SIZE (5) // Size of the message queue for CAN RX thread +#define CANBUS_TX_TIMEOUT K_MSEC(100) // Timeout for sending CAN messages in milliseconds int canbus_init(void); -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); +int canbus_send8(uint16_t reg, uint8_t value); +int canbus_send16(uint16_t reg, uint16_t value); #endif // __CANBUS_H__ \ No newline at end of file diff --git a/software/lib/canbus_registers.h b/software/lib/canbus_registers.h index b915c89..5463b53 100644 --- a/software/lib/canbus_registers.h +++ b/software/lib/canbus_registers.h @@ -1,18 +1,42 @@ #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; +enum canbus_registers { + CANBUS_REG_REBOOT = 0x00, + CANBUS_REG_STATE = 0x01, + CANBUS_REG_ERROR = 0x02, -typedef enum { - CANBUS_REGISTER_COMMAND_SET = 0x00, - CANBUS_REGISTER_COMMAND_GET = 0x01, -} canbus_register_command_t; + CANBUS_REG_VALVE_STATUS = 0x10, + CANBUS_REG_VALVE_OPERATION = 0x11, + CANBUS_REG_VALVE_COMMAND = 0x12, + CANBUS_REG_WATERLEVEL_STATE = 0x20, + CANBUS_REG_WATERLEVEL_LEVEL = 0x21, + CANBUS_REG_WATERLEVEL_ZERO_POINT = 0x22, + CANBUS_REG_WATERLEVEL_MAX_POINT = 0x23, +}; + +enum valve_status { + VALVE_STATE_CLOSED = 0x00, + VALVE_STATE_OPEN = 0x01, + VALVE_STATE_ERROR = 0x02, + VALVE_STATE_UNKNOWN = 0x03, +}; + +enum valve_operation_state { + VALVE_OPERATION_IDLE = 0x00, + VALVE_OPERATION_OPENING = 0x01, + VALVE_OPERATION_CLOSING = 0x02, +}; + +enum valve_command { + VALVE_COMMAND_STOP = 0x00, + VALVE_COMMAND_OPEN = 0x01, + VALVE_COMMAND_CLOSE = 0x02, +}; + +enum waterlevel_state { + WATERLEVEL_STATE_OK = 0x00, + WATERLEVEL_STATE_MODBUS_ERROR = 0x02, +}; #endif // __CANBUS_REGISTERS_H__ \ No newline at end of file diff --git a/software/lib/config.c b/software/lib/config.c deleted file mode 100644 index 0c9632a..0000000 --- a/software/lib/config.c +++ /dev/null @@ -1,133 +0,0 @@ -#include "config.h" -#include - -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; - int rc; - - // Handle setting values for CAN bus configuration - LOG_DBG("Trying to read CAN bus configuration: key=%s, len=%zu", key, len); - if (settings_name_steq(key, "node_id", &next) && next == NULL) - { - if (len != sizeof(canbus_node_id)) - { - LOG_ERR("Invalid length for node_id setting: %zu", len); - return -EINVAL; // Invalid argument - } - - rc = read_cb(cb_arg, &canbus_node_id, sizeof(canbus_node_id)); - if (rc < 0) - { - LOG_ERR("Failed to read node_id setting: %d", rc); - return rc; // Read error - } - LOG_INF("Set CAN bus node ID to <%d> from settings", canbus_node_id); - } - return 0; // Return 0 on success -} - -static struct settings_handler settings_handler_canbus = { - .name = "canbus", - .h_set = settings_canbus, // No settings set handler -}; - -int config_init(void) -{ - int rc; - rc=settings_subsys_init(); - if (rc < 0) - { - LOG_ERR("Failed to initialize settings subsystem: %d", rc); - return rc; // Initialization error - } - rc = settings_register(&settings_handler_canbus); - if (rc < 0) - { - LOG_ERR("Failed to register settings handler: %d", rc); - return rc; // Registration error - } - rc = settings_load(); - if (rc < 0) - { - LOG_ERR("Failed to load settings: %d", rc); - return rc; // Load error - } - return 0; // Return 0 on success -} \ No newline at end of file diff --git a/software/lib/config.h b/software/lib/config.h deleted file mode 100644 index 78ab7dc..0000000 --- a/software/lib/config.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __SETTINGS_H__ -#define __SETTINGS_H__ - -#include - -#include -#include - -int config_init(void); - -#endif // __SETTINGS_H__ \ No newline at end of file diff --git a/software/lib/modbus.c b/software/lib/modbus.c deleted file mode 100644 index ed118cf..0000000 --- a/software/lib/modbus.c +++ /dev/null @@ -1,163 +0,0 @@ -#include "modbus.h" -#include -#include -#include -#include - -LOG_MODULE_REGISTER(mbc, CONFIG_LOG_DEFAULT_LEVEL); - -#define MODBUS_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(zephyr_modbus_serial) - -static int client_iface; - -static struct -{ - int level; // Water level value - int minimum; // Minimum value - int maximum; // Maximum value - int factor; // Factor for unit conversion -} measurement; - -int mb_init_client(void) -{ - const char iface_name[] = {DEVICE_DT_NAME(MODBUS_NODE)}; - client_iface = modbus_iface_get_by_name(iface_name); - LOG_DBG("Modbus client interface: %d", client_iface); - if (client_iface < 0) - { - LOG_ERR("Failed to get Modbus interface by name: %s", iface_name); - return client_iface; - } - return modbus_init_client(client_iface, client_param); -} - -int mb_read_holding_registers(int node, uint16_t reg_addr, uint16_t *data, size_t len) -{ - return modbus_read_holding_regs(client_iface, node, reg_addr, data, len); -} - -int mb_read() -{ - int rc; - int16_t data[5] = {0}; - rc = mb_read_holding_registers(1, 0x0002, data, sizeof(data) / sizeof(data[0])); - if (rc < 0) - { - LOG_ERR("Failed to read holding registers: %d", rc); - return rc; - } - LOG_HEXDUMP_DBG(data, sizeof(data), "Holding Registers Data"); - - int unit, decimals; - unit = data[0]; - decimals = data[1]; - int factor; - switch (unit) - { - case 1: // cm - factor = 10; - break; - case 2: // mm - factor = 1; - break; - default: - LOG_ERR("Unknown unit: %d", unit); - return -EINVAL; - } - switch(decimals) - { - case 0: // no decimals - factor /= 1; - break; - case 1: // one decimal - factor /= 10; - break; - case 2: // two decimals - factor /= 100; - break; - default: - LOG_ERR("Unknown decimals: %d", decimals); - return -EINVAL; - } - measurement.factor = factor; - measurement.level = data[2] * factor; - measurement.minimum = data[3] * factor; - measurement.maximum = data[4] * factor; - LOG_DBG("Water level: %dmm, Minimum: %dmm, Maximum: %dmm", - measurement.level, measurement.minimum, measurement.maximum); - return 0; -} - -int mb_read_water_level(double *mb_read_water_level) -{ - int rc = mb_read(); - if (rc < 0) - { - LOG_ERR("Failed to read water level: %d", rc); - return rc; - } - *mb_read_water_level = (double)measurement.level / 1000.0; // Convert to meters - - return 0; -} - -int mb_read_water_level_mm(int *mb_read_water_level) -{ - int rc = mb_read(); - if (rc < 0) - { - LOG_ERR("Failed to read water level: %d", rc); - return rc; - } - *mb_read_water_level = measurement.level; - - return 0; -} - -int mb_read_minimum_mm(int *mb_read_minimum) -{ - int rc = mb_read(); - if (rc < 0) - { - LOG_ERR("Failed to read water level: %d", rc); - return rc; - } - *mb_read_minimum = measurement.minimum; - return 0; -} - -int mb_read_maximum_mm(int *mb_read_maximum) -{ - int rc = mb_read(); - if (rc < 0) - { - LOG_ERR("Failed to read water level: %d", rc); - return rc; - } - *mb_read_maximum = measurement.maximum; - return 0; -} - -int mb_write_minimum_mm(int minimum) -{ - int rc = mb_read(); - if (rc < 0) - { - LOG_ERR("Failed to read water level: %d", rc); - return rc; - } - modbus_write_holding_reg(client_iface, 1, 0x0005, minimum / measurement.factor); - return 0; -} - -int mb_write_maximum_mm(int maximum) -{ - int rc = mb_read(); - if (rc < 0) - { - LOG_ERR("Failed to read water level: %d", rc); - return rc; - } - modbus_write_holding_reg(client_iface, 1, 0x0006, maximum / measurement.factor); - return 0; -} diff --git a/software/lib/modbus.h b/software/lib/modbus.h deleted file mode 100644 index 5959b7c..0000000 --- a/software/lib/modbus.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef __waterlevel_h -#define __waterlevel_h - -#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, - .serial = { - .baud = 9600, - .parity = UART_CFG_PARITY_NONE, - }, -}; - -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); -int mb_read_water_level_mm(int *mb_read_water_level_mm); -int mb_read_minimum_mm(int *mb_read_minimum); -int mb_read_maximum_mm(int *mb_read_maximum); -int mb_write_minimum_mm(int minimum); -int mb_write_maximum_mm(int maximum); - -#endif \ No newline at end of file diff --git a/software/lib/valve.c b/software/lib/valve.c new file mode 100644 index 0000000..4966540 --- /dev/null +++ b/software/lib/valve.c @@ -0,0 +1,163 @@ +#include +#include + +#include "canbus.h" +#include "canbus_registers.h" +#include "valve.h" + +LOG_MODULE_REGISTER(valve, CONFIG_LOG_VALVE_LEVEL); + +K_THREAD_STACK_DEFINE(valve_thread_stack, VALVE_THREAD_STACK_SIZE); +K_MSGQ_DEFINE(valve_msgq, sizeof(int), VALVE_MSGQ_SIZE, 4); + +k_tid_t valve_thread_id; +struct k_thread valve_thread_data; + +valve_status_t valve_status_data = { + .valve_state = VALVE_STATE_UNKNOWN, + .valve_operation = VALVE_OPERATION_IDLE, +}; + +int valve_start_thread(void) +{ + int rc; + + // Initialize the valve + rc = valve_init(); + if (rc < 0) + { + LOG_ERR("Failed to initialize valve: %d", rc); + return rc; + } + + // Create the valve thread + valve_thread_id = k_thread_create(&valve_thread_data, valve_thread_stack, + K_THREAD_STACK_SIZEOF(valve_thread_stack), + (k_thread_entry_t)valve_cmd, NULL, NULL, NULL, + VALVE_THREAD_PRIORITY, 0, K_NO_WAIT); + k_thread_name_set(valve_thread_id, "valve"); + + LOG_INF("Valve thread started successfully"); +while (1) + { + // Wait for commands from the message queue + int cmd; + rc = k_msgq_get(&valve_msgq, &cmd, VALVE_STATE_INTERVAL); + if (rc == 0) + { + // Process the command + rc = valve_cmd(cmd); + if (rc < 0) + { + LOG_ERR("Failed to process valve command: %d", rc); + } + } + else + { + valve_send_status(); // Send current valve status periodically + } + } + return 0; +} +int valve_init(void) +{ + return 0; +} + +int valve_cmd(int cmd) +{ + switch (cmd) + { + case VALVE_COMMAND_OPEN: + if (valve_status_data.valve_state != VALVE_STATE_OPEN) + { + valve_status_data.valve_state = VALVE_STATE_OPEN; + valve_status_data.valve_operation = VALVE_OPERATION_OPENING; + valve_send_status(); // Send updated status before opening + valve_send_operation(); // Send updated operation state before opening + k_sleep(VALVE_OPENING_TIME); // Simulate opening time + valve_status_data.valve_operation = VALVE_OPERATION_IDLE; // Set operation to idle after opening + valve_send_status(); // Send updated status after opening + valve_send_operation(); // Send updated operation state after opening + } + break; + case VALVE_COMMAND_CLOSE: + if (valve_status_data.valve_state != VALVE_STATE_CLOSED) + { + valve_status_data.valve_operation = VALVE_OPERATION_CLOSING; + valve_send_operation(); // Send updated operation state before closing + k_sleep(VALVE_CLOSING_TIME); // Simulate closing time + valve_status_data.valve_state = VALVE_STATE_CLOSED; // Set valve state to closed after closing + valve_status_data.valve_operation = VALVE_OPERATION_IDLE; // Set operation to idle after closing + valve_send_status(); // Send updated status after closing + valve_send_operation(); // Send updated operation state after closing + } + break; + case VALVE_COMMAND_STOP: + valve_status_data.valve_operation = VALVE_OPERATION_IDLE; + break; + default: + LOG_ERR("Unknown valve command: %d", cmd); + return -EINVAL; // Invalid command + } + return 0; +} + +int valve_send_status(void) +{ + int rc = canbus_send8(CANBUS_REG_VALVE_STATUS, valve_status_data.valve_state); + if (rc != 0) + { + LOG_ERR("Failed to send valve status: %d", rc); + return rc; + } + char *state_str; + switch (valve_status_data.valve_state) + { + case VALVE_STATE_CLOSED: + state_str = "CLOSED"; + break; + case VALVE_STATE_OPEN: + state_str = "OPEN"; + break; + case VALVE_STATE_ERROR: + state_str = "ERROR"; + break; + case VALVE_STATE_UNKNOWN: + state_str = "UNKNOWN"; + break; + default: + state_str = "INVALID"; + break; + } + LOG_INF("Valve status sent: %s", state_str); + return 0; +} + +int valve_send_operation(void) +{ + int rc = canbus_send8(CANBUS_REG_VALVE_OPERATION, valve_status_data.valve_operation); + if (rc != 0) + { + LOG_ERR("Failed to send valve operation: %d", rc); + return rc; + } + char *operation_str; + switch (valve_status_data.valve_operation) + { + case VALVE_OPERATION_IDLE: + operation_str = "IDLE"; + break; + case VALVE_OPERATION_OPENING: + operation_str = "OPENING"; + break; + case VALVE_OPERATION_CLOSING: + operation_str = "CLOSING"; + break; + default: + operation_str = "UNKNOWN"; + break; + } + LOG_INF("Valve operation sent: %s", operation_str); + return 0; +} \ No newline at end of file diff --git a/software/lib/valve.h b/software/lib/valve.h new file mode 100644 index 0000000..94f4538 --- /dev/null +++ b/software/lib/valve.h @@ -0,0 +1,27 @@ +#ifndef __VALVE_H__ +#define __VALVE_H__ + +#define VALVE_OPENING_TIME K_MSEC(4500) // Time to open the valve +#define VALVE_CLOSING_TIME K_MSEC(4500) // Time to close the valve +#define VALVE_MAX_OPENING_TIME K_MSEC(5000) // Maximum time to open the valve +#define VALVE_MAX_CLOSING_TIME K_MSEC(5000) // Maximum time to close the valve +#define VALVE_STATE_INTERVAL K_SECONDS(5 * 60) // Interval to check the valve state +#define VALVE_THREAD_STACK_SIZE (512) // Stack size for the valve thread +#define VALVE_THREAD_PRIORITY (2) // Priority for the valve thread +#define VALVE_MSGQ_SIZE (5) // Size of the message queue for valve operations + +#include +#include "canbus_registers.h" + +typedef struct { + uint8_t valve_state; + uint8_t valve_operation; +} valve_status_t; + +int valve_init(void); +int valve_cmd(int cmd); +int valve_send_status(void); +int valve_send_operation(void); + + +#endif // __VALVE_H__ \ No newline at end of file diff --git a/software/lib/waterlevel_sensor.c b/software/lib/waterlevel_sensor.c new file mode 100644 index 0000000..3d441f8 --- /dev/null +++ b/software/lib/waterlevel_sensor.c @@ -0,0 +1,384 @@ +#include "waterlevel_sensor.h" +#include +#include +#include +#include +#include "canbus.h" + +LOG_MODULE_REGISTER(wls, CONFIG_LOG_WATERLEVELSENSOR_LEVEL); + +#define MODBUS_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(zephyr_modbus_serial) + +struct k_thread waterlevel_sensor_thread_data; +K_THREAD_STACK_DEFINE(waterlevel_sensor_stack, WATERLEVEL_SENSOR_STACK_SIZE); +K_MSGQ_DEFINE(waterlevel_sensor_msgq, sizeof(waterlevel_command_t), WATERLEVEL_MESSAGE_QUEUE_SIZE, 4); + +static int modbus_client_iface; + +volatile static struct +{ + int level; // Water level value + int zeropoint; // Minimum value + int maxpoint; // Maximum value + int factor; // Factor for unit conversion + bool factor_set; // Flag to indicate if factor is set +} waterlevel_measurement = { + .factor_set = false, +}; + +struct modbus_iface_param client_param = { + .mode = MODBUS_MODE_RTU, + .rx_timeout = 50000, // Timeout for receiving data in milliseconds + .serial = { + .baud = 9600, + .parity = UART_CFG_PARITY_NONE, + .stop_bits_client = UART_CFG_STOP_BITS_1, // 1 stop bit for Modbus RTU + }, +}; + +static int waterlevel_modbus_init() { + const char iface_name[] = {DEVICE_DT_NAME(MODBUS_NODE)}; + modbus_client_iface = modbus_iface_get_by_name(iface_name); + if (modbus_client_iface < 0) + { + LOG_ERR("Failed to get Modbus interface by name: %s", iface_name); + return modbus_client_iface; + } + LOG_DBG("Initializing modbus client interface: %s", iface_name); + return modbus_init_client(modbus_client_iface, client_param); +} + +static int waterlevel_modbus_read(void) { + int rc; + union + { + struct + { + int16_t unit; // Unit of measurement (e.g., cm, mm) + int16_t decimals; // Number of decimal places for the measurement + int16_t level; // Water level value + int16_t zeropoint; // Zero point for the measurement + int16_t maxpoint; // Maximum point for the measurement + }; + int16_t data[5]; // Data array for holding registers + } waterlevel_modbus_data; + rc = modbus_read_holding_regs(modbus_client_iface, WATERLEVEL_SENSOR_MODBUS_NODE_ID, 0x0002, waterlevel_modbus_data.data, sizeof(waterlevel_modbus_data.data) / sizeof(waterlevel_modbus_data.data[0])); + if (rc < 0) + { + LOG_ERR("Failed to read holding registers, node <%d>, returncode: %d", WATERLEVEL_SENSOR_MODBUS_NODE_ID, rc); + return rc; + } + + LOG_DBG("Got values. Unit: %d, Decimals: %d, Level: %d, Zero Point: %d, Max Point: %d", + waterlevel_modbus_data.unit, + waterlevel_modbus_data.decimals, + waterlevel_modbus_data.level, + waterlevel_modbus_data.zeropoint, + waterlevel_modbus_data.maxpoint); + + LOG_HEXDUMP_DBG(waterlevel_modbus_data.data, sizeof(waterlevel_modbus_data.data), "Waterlevel Sensor Holding Registers Data"); + + switch (waterlevel_modbus_data.unit) + { + case 1: // cm + waterlevel_measurement.factor = 10; + break; + case 2: // mm + waterlevel_measurement.factor = 1; + break; + default: + LOG_ERR("Unknown unit: %d", waterlevel_modbus_data.unit); + waterlevel_measurement.factor_set = false; + return -EINVAL; + } + switch (waterlevel_modbus_data.decimals) + { + case 0: // no decimals + waterlevel_measurement.factor /= 1; + break; + case 1: // one decimal + waterlevel_measurement.factor /= 10; + break; + case 2: // two decimals + waterlevel_measurement.factor /= 100; + break; + default: + LOG_ERR("Unknown decimals: %d", waterlevel_modbus_data.decimals); + waterlevel_measurement.factor_set = false; + return -EINVAL; + } + waterlevel_measurement.factor_set = true; + waterlevel_measurement.level = waterlevel_modbus_data.level * waterlevel_measurement.factor; + waterlevel_measurement.zeropoint = waterlevel_modbus_data.zeropoint * waterlevel_measurement.factor; + waterlevel_measurement.maxpoint = waterlevel_modbus_data.maxpoint * waterlevel_measurement.factor; + + LOG_DBG("Water level: %dmm, zero point: %dmm, maximum point: %dmm", + waterlevel_measurement.level, + waterlevel_measurement.zeropoint, + waterlevel_measurement.maxpoint); + LOG_HEXDUMP_DBG(waterlevel_modbus_data.data, sizeof(waterlevel_modbus_data.data), "Waterlevel Sensor Holding Registers Data"); + return 0; +} + +static int waterlevel_send_level(void) { + if (!waterlevel_measurement.factor_set) { + LOG_ERR("Factor not set, cannot send water level"); + return -EINVAL; + } + + LOG_INF("Sending water level: %dmm", waterlevel_measurement.level); + canbus_send16(CANBUS_REG_WATERLEVEL_LEVEL, waterlevel_measurement.level); + return 0; +} + +static int waterlevel_send_zero_point(void) { + if (!waterlevel_measurement.factor_set) { + LOG_ERR("Factor not set, cannot send zero point"); + return -EINVAL; + } + + LOG_INF("Sending water zero point: %dmm", waterlevel_measurement.zeropoint); + canbus_send16(CANBUS_REG_WATERLEVEL_ZERO_POINT, waterlevel_measurement.zeropoint); + return 0; +} + +static int waterlevel_send_max_point(void) { + if (!waterlevel_measurement.factor_set) { + LOG_ERR("Factor not set, cannot send maximum point"); + return -EINVAL; + } + + LOG_INF("Sending water maximum point: %dmm", waterlevel_measurement.maxpoint); + canbus_send16(CANBUS_REG_WATERLEVEL_MAX_POINT, waterlevel_measurement.maxpoint); + return 0; +} + +static int waterlevel_set_zero_point(int zeropoint) { + if (!waterlevel_measurement.factor_set) { + LOG_ERR("Factor not set, cannot set zero point"); + return -EINVAL; + } + + int16_t zeropoint_modbus = zeropoint / waterlevel_measurement.factor; + int rc = modbus_write_holding_regs(modbus_client_iface, WATERLEVEL_SENSOR_MODBUS_NODE_ID, 0x0005, &zeropoint_modbus, 1); + if (rc < 0) { + LOG_ERR("Failed to write zero point: %d", rc); + return rc; + } + + waterlevel_measurement.zeropoint = zeropoint; // Update the local measurement structure + LOG_INF("Zero point set to: %dmm", waterlevel_measurement.zeropoint); + rc = waterlevel_send_zero_point(); + if (rc < 0) { + LOG_ERR("Failed to send zero point: %d", rc); + return rc; + } + return 0; +} + +static int waterlevel_set_max_point(int maxpoint) { + if (!waterlevel_measurement.factor_set) { + LOG_ERR("Factor not set, cannot set maximum point"); + return -EINVAL; + } + + int16_t maxpoint_modbus = maxpoint / waterlevel_measurement.factor; + int rc = modbus_write_holding_regs(modbus_client_iface, WATERLEVEL_SENSOR_MODBUS_NODE_ID, 0x0006, &maxpoint_modbus, 1); + if (rc < 0) { + LOG_ERR("Failed to write maximum point: %d", rc); + return rc; + } + + waterlevel_measurement.maxpoint = maxpoint; // Update the local measurement structure + LOG_INF("Maximum point set to: %dmm", waterlevel_measurement.maxpoint); + rc = waterlevel_send_max_point(); + if (rc < 0) { + LOG_ERR("Failed to send maximum point: %d", rc); + return rc; + } + return 0; +} + +void waterlevel_sensor_thread(void *arg1, void *arg2, void *arg3) +{ + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + // Initialize the Modbus client + int rc = waterlevel_modbus_init(); + if (rc < 0) + { + LOG_ERR("Failed to initialize Modbus client: %d", rc); + return; + } + + rc = waterlevel_modbus_read(); + if (rc < 0) { + LOG_ERR("Failed to read initial water level: %d", rc); + return; + } + waterlevel_send_level(); + waterlevel_send_zero_point(); + waterlevel_send_max_point(); + + // Initialize the last transmission time and level + // Use k_uptime_get_32() to get the current uptime in milliseconds + // and store the initial water level measurement. + // This will be used to determine when to send updates. + + uint32_t last_transmission_time_ms = k_uptime_get_32(); + int32_t last_transmission_level = waterlevel_measurement.level; + + while (1) + { + uint32_t current_time_ms = k_uptime_get_32(); + uint32_t delta_time = current_time_ms-last_transmission_time_ms; + waterlevel_command_t command; + + rc = waterlevel_modbus_read(); + if (rc < 0) + { + LOG_ERR("Failed to read water level: %d", rc); + continue; + } + + if (delta_time >= WATERLEVEL_SENSOR_MAX_UPDATE_INTERVAL_MS || + abs(waterlevel_measurement.level - last_transmission_level) >= WATERLEVEL_SENSOR_MIN_DELTA) { + rc = waterlevel_send_level(); + if (rc < 0) { + LOG_ERR("Failed to send water level: %d", rc); + } else { + last_transmission_time_ms = current_time_ms; + last_transmission_level = waterlevel_measurement.level; + } + } + + while (k_msgq_get(&waterlevel_sensor_msgq, &command, K_NO_WAIT) == 0) + { + switch(command.cmd) + { + case WATERLEVEL_CMD_SET: + switch (command.reg) + { + case CANBUS_REG_WATERLEVEL_ZERO_POINT: // Set zero point + rc = waterlevel_set_zero_point(command.data); + if (rc < 0) { + LOG_ERR("Failed to set zero point: %d", rc); + } + break; + case CANBUS_REG_WATERLEVEL_MAX_POINT: // Set maximum point + rc = waterlevel_set_max_point(command.data); + if (rc < 0) { + LOG_ERR("Failed to set maximum point: %d", rc); + } + break; + default: + LOG_ERR("Unknown register for SET command: 0x%02X", command.reg); + break; + } + break; + case WATERLEVEL_CMD_GET: + switch (command.reg) + { + case CANBUS_REG_WATERLEVEL_LEVEL: // Get water level + waterlevel_send_level(); + break; + case CANBUS_REG_WATERLEVEL_ZERO_POINT: // Get zero point + waterlevel_send_zero_point(); + break; + case CANBUS_REG_WATERLEVEL_MAX_POINT: // Get maximum point + waterlevel_send_max_point(); + break; + default: + LOG_ERR("Unknown register for GET command: 0x%02X", command.reg); + break; + } + break; + default: + LOG_ERR("Unknown command type: %d", command.cmd); + break; + } + } + } +} + +int waterlevel_sensor_start_thread(void) +{ + k_tid_t waterlevel_sensor_thread_id; + + // Start the thread + waterlevel_sensor_thread_id = k_thread_create(&waterlevel_sensor_thread_data, waterlevel_sensor_stack, + K_THREAD_STACK_SIZEOF(waterlevel_sensor_stack), waterlevel_sensor_thread, + NULL, NULL, NULL, + WATERLEVEL_SENSOR_THREAD_PRIORITY, 0, K_NO_WAIT); + + if (waterlevel_sensor_thread_id == NULL) + { + LOG_ERR("Failed to create water level sensor thread"); + return -ENOMEM; + } + k_thread_name_set(waterlevel_sensor_thread_id, "waterlevel_sensor"); + + LOG_INF("Water level sensor thread started successfully"); + return 0; +} + +#ifdef CONFIG_SHELL +#include + +void waterlevel_set_zero_point_shell(const struct shell *shell, size_t argc, char **argv) { + if (argc != 2) { + shell_error(shell, "Usage: waterlevel_sensor set_zero_point "); + return; + } + + int zeropoint = atoi(argv[1]); + int rc = waterlevel_set_zero_point(zeropoint); + if (rc < 0) { + shell_error(shell, "Failed to set zero point: %d", rc); + } else { + shell_print(shell, "Zero point set to: %dmm", zeropoint); + } +} + +void waterlevel_set_max_point_shell(const struct shell *shell, size_t argc, char **argv) { + if (argc != 2) { + shell_error(shell, "Usage: waterlevel_sensor set_max_point "); + return; + } + + int maxpoint = atoi(argv[1]); + int rc = waterlevel_set_max_point(maxpoint); + if (rc < 0) { + shell_error(shell, "Failed to set maximum point: %d", rc); + } else { + shell_print(shell, "Maximum point set to: %dmm", maxpoint); + } +} + +void waterlevel_sensor_print_shell(const struct shell *shell, size_t argc, char **argv) { + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + waterlevel_modbus_read(); + if (!waterlevel_measurement.factor_set) { + shell_error(shell, "Factor not set, cannot print water level"); + return; + } + + shell_print(shell, "Current water level: %4dmm", waterlevel_measurement.level); + shell_print(shell, "Zero point: %4dmm", waterlevel_measurement.zeropoint); + shell_print(shell, "Maximum point: %4dmm", waterlevel_measurement.maxpoint); +} + +// Define the shell commands for the water level sensor +SHELL_STATIC_SUBCMD_SET_CREATE( + waterlevel_sensor_cmds, + SHELL_CMD(print, NULL, "Print the current water level, zero point, and maximum point", waterlevel_sensor_print_shell), + SHELL_CMD(setzero, NULL, "Set the zero point for the water level sensor", waterlevel_set_zero_point_shell), + SHELL_CMD(setmax, NULL, "Set the maximum point for the water level sensor", waterlevel_set_max_point_shell), + SHELL_SUBCMD_SET_END); + +SHELL_CMD_REGISTER(wls, &waterlevel_sensor_cmds, "Water level sensor commands", NULL); +#endif // CONFIG_SHELL diff --git a/software/lib/waterlevel_sensor.h b/software/lib/waterlevel_sensor.h new file mode 100644 index 0000000..e8ab7e5 --- /dev/null +++ b/software/lib/waterlevel_sensor.h @@ -0,0 +1,27 @@ +#ifndef __WATERLEVEL_SENSOR_H__ +#define __WATERLEVEL_SENSOR_H__ + +#define WATERLEVEL_SENSOR_STACK_SIZE (512) +#define WATERLEVEL_SENSOR_THREAD_PRIORITY (2) +#define WATERLEVEL_MESSAGE_QUEUE_SIZE (5) // Size of the message queue for water level sensor thread +#define WATERLEVEL_SENSOR_READ_INTERVAL_MS (5000) // Interval for reading the water level sensor in milliseconds +#define WATERLEVEL_SENSOR_MIN_DELTA (2) // Minimum change in water level to trigger an update +#define WATERLEVEL_SENSOR_MAX_UPDATE_INTERVAL_MS (600000) // Maximum interval for updating the water level in milliseconds + +#define WATERLEVEL_SENSOR_MODBUS_NODE_ID (0x01) // Modbus node ID for the water level sensor +#define WATERLEVEL_SENSOR_MODBUS_BAUD_RATE (9600) // Baud rate for Modbus communication + +#include + +int waterlevel_sensor_start_thread(void); + +typedef struct { + uint8_t reg; + enum { + WATERLEVEL_CMD_SET, + WATERLEVEL_CMD_GET, + } cmd; + int16_t data; // Data to be set +} waterlevel_command_t; + +#endif // __WATERLEVEL_SENSOR_H__ \ No newline at end of file diff --git a/software/test_canbus/prj.conf b/software/prj.conf similarity index 52% rename from software/test_canbus/prj.conf rename to software/prj.conf index 6f7c90c..f297171 100644 --- a/software/test_canbus/prj.conf +++ b/software/prj.conf @@ -1,20 +1,17 @@ +CONFIG_HAS_MODBUS_WATERLEVEL_SENSOR=y +CONFIG_HAS_VALVE=y + CONFIG_LOG=y CONFIG_LOG_DEFAULT_LEVEL=3 -# CONFIG_LOG_SETTINGS_LEVEL=4 # CONFIG_LOG_CAN_LEVEL=4 +# CONFIG_LOG_WATERLEVELSENSOR_LEVEL=4 +# CONFIG_LOG_VALVE_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_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 +CONFIG_REBOOT=y \ No newline at end of file diff --git a/software/src/main.c b/software/src/main.c new file mode 100644 index 0000000..8cb48fb --- /dev/null +++ b/software/src/main.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include "canbus.h" +#include "canbus_registers.h" + +#ifdef CONFIG_HAS_MODBUS_WATERLEVEL_SENSOR +// Include the water level sensor header file when the feature is enabled +#include "waterlevel_sensor.h" +#endif // CONFIG_HAS_MODBUS_WATERLEVEL_SENSOR + +#ifdef CONFIG_HAS_VALVE +#include "valve.h" +#endif // CONFIG_HAS_VALVE + +LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL); + +int main(void) +{ + LOG_INF("Starting main application..."); + canbus_init(); + k_sleep(K_MSEC(3000)); // Allow some time for CAN initialization +#ifdef CONFIG_HAS_MODBUS_WATERLEVEL_SENSOR + int rc = waterlevel_sensor_start_thread(); + if (rc < 0) + { + LOG_ERR("Failed to start water level sensor thread: %d", rc); + return rc; + } +#endif // CONFIG_HAS_MODBUS_WATERLEVEL_SENSOR + + valve_cmd(VALVE_COMMAND_CLOSE); // Ensure the valve is closed at startup + + LOG_INF("Main application started successfully."); + return 0; // Return 0 on success +} + +#ifdef CONFIG_SHELL +#include +#include + +static int reboot_shell_cmd(const struct shell *shell, size_t argc, char **argv) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + shell_print(shell, "Rebooting the node in 1 second..."); + k_sleep(K_SECONDS(1)); + sys_reboot(SYS_REBOOT_COLD); + return 0; +} +SHELL_CMD_REGISTER(reboot, NULL, "Reboot the node", reboot_shell_cmd); +#endif // CONFIG_SHELL diff --git a/software/test_canbus/.gitignore b/software/test_canbus/.gitignore deleted file mode 100644 index 378eac2..0000000 --- a/software/test_canbus/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/software/test_canbus/.vscode/c_cpp_properties.json b/software/test_canbus/.vscode/c_cpp_properties.json deleted file mode 100644 index b1f27e8..0000000 --- a/software/test_canbus/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "configurations": [ - { - "name": "Win32", - "includePath": [ - "${workspaceFolder}/**" - ], - "defines": [ - "_DEBUG", - "UNICODE", - "_UNICODE" - ] - } - ], - "version": 4 -} \ No newline at end of file diff --git a/software/test_canbus/CMakeLists.txt b/software/test_canbus/CMakeLists.txt deleted file mode 100644 index 17330ae..0000000 --- a/software/test_canbus/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 - -cmake_minimum_required(VERSION 3.20.0) - -list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..) - -find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) -project(hello_world) - -target_sources(app PRIVATE src/main.c) -target_sources(app PRIVATE ../lib/canbus.c) -target_sources(app PRIVATE ../lib/config.c) -target_include_directories(app PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../lib -) diff --git a/software/test_canbus/boards/nucleo_f103rb.overlay b/software/test_canbus/boards/nucleo_f103rb.overlay deleted file mode 100644 index a3e38dc..0000000 --- a/software/test_canbus/boards/nucleo_f103rb.overlay +++ /dev/null @@ -1,9 +0,0 @@ -&usart1 { - status = "okay"; - current-speed = <9600>; - modbus0 { - compatible = "zephyr,modbus-serial"; - status = "okay"; - // de-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */ - }; -}; \ No newline at end of file diff --git a/software/test_canbus/src/main.c b/software/test_canbus/src/main.c deleted file mode 100644 index a95d4f5..0000000 --- a/software/test_canbus/src/main.c +++ /dev/null @@ -1,132 +0,0 @@ -/* Testing MODBUS functionality - This code initializes a Modbus client, sets the zero point and the 2m point for water level, - and reads the water level in both meters and millimeters. - It uses the Zephyr RTOS and its Modbus library to communicate with a Modbus server. -*/ - -#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) - { - LOG_ERR("Failed to initialize configuration: %d", rc); - return rc; - } - rc = canbus_init(); - if (rc != 0) - { - LOG_ERR("Failed to initialize CAN bus: %d", rc); - return rc; - } - LOG_INF("CAN bus initialized successfully"); - - 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) - { - 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 remove this return statement - // to keep the loop running indefinitely. - } - return 0; -} diff --git a/software/test_modbus/.gitignore b/software/test_modbus/.gitignore deleted file mode 100644 index 378eac2..0000000 --- a/software/test_modbus/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/software/test_modbus/.vscode/c_cpp_properties.json b/software/test_modbus/.vscode/c_cpp_properties.json deleted file mode 100644 index b1f27e8..0000000 --- a/software/test_modbus/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "configurations": [ - { - "name": "Win32", - "includePath": [ - "${workspaceFolder}/**" - ], - "defines": [ - "_DEBUG", - "UNICODE", - "_UNICODE" - ] - } - ], - "version": 4 -} \ No newline at end of file diff --git a/software/test_modbus/CMakeLists.txt b/software/test_modbus/CMakeLists.txt deleted file mode 100644 index 5d2c3bf..0000000 --- a/software/test_modbus/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 - -cmake_minimum_required(VERSION 3.20.0) - -list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..) - -find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) -project(hello_world) - -target_sources(app PRIVATE src/main.c) -target_sources(app PRIVATE ../lib/modbus.c) -target_include_directories(app PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../lib -) diff --git a/software/test_modbus/boards/nucleo_f103rb.overlay b/software/test_modbus/boards/nucleo_f103rb.overlay deleted file mode 100644 index a3e38dc..0000000 --- a/software/test_modbus/boards/nucleo_f103rb.overlay +++ /dev/null @@ -1,9 +0,0 @@ -&usart1 { - status = "okay"; - current-speed = <9600>; - modbus0 { - compatible = "zephyr,modbus-serial"; - status = "okay"; - // de-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */ - }; -}; \ No newline at end of file diff --git a/software/test_modbus/prj.conf b/software/test_modbus/prj.conf deleted file mode 100644 index 502f667..0000000 --- a/software/test_modbus/prj.conf +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG_LOG=y -CONFIG_LOG_DEFAULT_LEVEL=3 -CONFIG_CBPRINTF_FP_SUPPORT=y - -CONFIG_UART_CONSOLE=y # Console on USART1 -#CONFIG_RTT_CONSOLE=y -#CONFIG_USE_SEGGER_RTT=y \ No newline at end of file diff --git a/software/test_modbus/src/main.c b/software/test_modbus/src/main.c deleted file mode 100644 index 7a79b40..0000000 --- a/software/test_modbus/src/main.c +++ /dev/null @@ -1,78 +0,0 @@ -/* Testing MODBUS functionality - This code initializes a Modbus client, sets the zero point and the 2m point for water level, - and reads the water level in both meters and millimeters. - It uses the Zephyr RTOS and its Modbus library to communicate with a Modbus server. -*/ - -#include -#include -#include - -#include "modbus.h" - -LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL); - -/* 1000 msec = 1 sec */ -#define SLEEP_TIME_MS 1000 - -int main(void) -{ - int rc; - - rc = mb_init_client(); - if (rc != 0) - { - LOG_ERR("Failed to initialize Modbus client: %d", rc); - return rc; - } - LOG_INF("Modbus client initialized successfully"); - - double water_level = 0.0; - int water_level_mm, water_level_min_mm, water_level_max_mm = 0; - - rc = mb_write_minimum_mm(42); // Set the zero point for water level - if (rc < 0) - { - LOG_ERR("Failed to write minimum water level: %d", rc); - return rc; - } - rc = mb_write_maximum_mm(2000); // Set the 2m point for water level - if (rc < 0) - { - LOG_ERR("Failed to write maximum water level: %d", rc); - return rc; - } - rc = mb_read_minimum_mm(&water_level_min_mm); - if (rc < 0) - { - LOG_ERR("Failed to read minimum water level: %d", rc); - return rc; - } - rc = mb_read_maximum_mm(&water_level_max_mm); - if (rc < 0) - { - LOG_ERR("Failed to read maximum water level: %d", rc); - return rc; - } - LOG_INF("Water zero point is set to: %dmm", water_level_min_mm); - LOG_INF("Water 2m point is set to: %dmm", water_level_max_mm); - - /* Read water level */ - rc = mb_read_water_level(&water_level); - if (rc < 0) - { - LOG_ERR("Failed to read water level: %d", rc); - return rc; - } - rc = mb_read_water_level_mm(&water_level_mm); - if (rc < 0) - { - LOG_ERR("Failed to read water level: %d", rc); - return rc; - } - LOG_INF("Water level: %.3fm", water_level); - LOG_INF("Water level: %dmm", water_level_mm); - LOG_INF("Modbus test completed successfully"); - - return 0; -} diff --git a/software/worspace.code-workspace b/software/worspace.code-workspace new file mode 100644 index 0000000..47b5bb9 --- /dev/null +++ b/software/worspace.code-workspace @@ -0,0 +1,132 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + // Hush CMake + "cmake.configureOnOpen": false, + + // IntelliSense + "C_Cpp.default.compilerPath": "${userHome}/zephyr-sdk-0.17.1/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc", + "C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json", + + // File Associations + "files.associations": { + "waterlevel_sensor.h": "c", + "shell.h": "c", + "can.h": "c" + } + }, + "tasks": { + "version": "2.0.0", + "tasks": [ + { + "label": "West Build", + "type": "shell", + "group": { + "kind": "build", + "isDefault": true + }, + "command": "${userHome}/zephyrproject/.venv/bin/west", + "args": [ + "build", + "-p", + "auto", + "-b", + "valve_node" + ], + "problemMatcher": [ + "$gcc" + ], + }, + { + "label": "West Configurable Build", + "type": "shell", + "group": { + "kind": "build", + }, + "command": "${userHome}/zephyrproject/.venv/bin/west", + "args": [ + "build", + "-p", + "${input:pristine}", + "-b", + "${input:board}" + ], + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "West Flash", + "type": "shell", + "command": "${userHome}/zephyrproject/.venv/bin/west", + "args": [ + "flash" + ], + "problemMatcher": [ + "$gcc" + ] + } + ], + "inputs": [ + { + "id": "board", + "type": "promptString", + "default": "vave_node", + "description": "See https://docs.zephyrproject.org/latest/boards/index.html" + }, + { + "id": "pristine", + "type": "pickString", + "description": "Choose when to run a pristine build", + "default": "auto", + "options": [ + "auto", + "always", + "never" + ] + } + ] + }, + "launch": { + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "device": "STM32F103RB", + "cwd": "${workspaceFolder}", + "executable": "build/zephyr/zephyr.elf", + "request": "launch", + "type": "cortex-debug", + //"runToEntryPoint": "main", + "servertype": "jlink", + "gdbPath": "${userHome}/zephyr-sdk-0.17.1/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb", + "preLaunchTask": "West Build" + }, + { + "name": "Attach", + "device": "STM32F103RB", + "cwd": "${workspaceFolder}", + "executable": "build/zephyr/zephyr.elf", + "request": "attach", + "type": "cortex-debug", + //"runToEntryPoint": "main", + "servertype": "jlink", + "gdbPath": "${userHome}/zephyr-sdk-0.17.1/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb" + }, + ] + }, + "extensions": { + "recommendations": [ + "ms-vscode.cpptools-extension-pack", + "ms-python.python", + "ms-vscode.vscode-embedded-tools", + "ms-vscode.vscode-serial-monitor", + "marus25.cortex-debug", + "donjayamanne.python-environment-manager" + ] + } +}