diff --git a/software/.vscode/settings.json b/software/.vscode/settings.json new file mode 100644 index 0000000..ff68cde --- /dev/null +++ b/software/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "nrf-connect.applications": [ + "${workspaceFolder}\\modbus_test" + ], + "files.associations": { + "log.h": "c", + "modbus.h": "c", + "array": "c", + "string": "c", + "string_view": "c" + } +} \ 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 7662552..bd39f4a 100644 --- a/software/boards/iten/valve_node/valve_node.dts +++ b/software/boards/iten/valve_node/valve_node.dts @@ -10,12 +10,12 @@ #include / { - model = "STMicroelectronics STM32F103RB-NUCLEO board"; - compatible = "st,stm32f103rb-nucleo"; + model = "Iten engineering Valve Node"; + compatible = "iten,valve-node", "st,stm32f103rb"; chosen { - zephyr,console = &usart2; - zephyr,shell-uart = &usart2; + zephyr,console = &usart1; + zephyr,shell-uart = &usart1; zephyr,sram = &sram0; zephyr,flash = &flash0; zephyr,canbus = &can1; @@ -25,7 +25,7 @@ compatible = "gpio-leds"; green_led_2: led_2 { - gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>; + gpios = <&gpiob 2 GPIO_ACTIVE_HIGH>; label = "User LD2"; }; }; @@ -81,8 +81,7 @@ }; &clk_hse { - hse-bypass; - clock-frequency = ; /* STLink 8MHz clock */ + clock-frequency = ; status = "okay"; }; @@ -104,15 +103,26 @@ &usart1 { pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>; pinctrl-names = "default"; - current-speed = <115200>; status = "okay"; + current-speed = <115200>; }; &usart2 { pinctrl-0 = <&usart2_tx_pa2 &usart2_rx_pa3>; + current-speed = <9600>; pinctrl-names = "default"; - current-speed = <115200>; status = "okay"; + modbus0 { + compatible = "zephyr,modbus-serial"; + status = "okay"; + //de-gpios = <&gpioa 15 GPIO_ACTIVE_LOW>; + }; +}; + +&usart3 { + pinctrl-0 = <&usart3_tx_pb10 &usart3_rx_pb11>; + current-speed = <115200>; + pinctrl-names = "default"; }; &i2c1 { diff --git a/software/boards/iten/valve_node/valve_node_defconfig b/software/boards/iten/valve_node/valve_node_defconfig index 5568aa3..bf33e2b 100644 --- a/software/boards/iten/valve_node/valve_node_defconfig +++ b/software/boards/iten/valve_node/valve_node_defconfig @@ -4,7 +4,6 @@ CONFIG_SERIAL=y # enable console CONFIG_CONSOLE=y -CONFIG_UART_CONSOLE=y # enable GPIO CONFIG_GPIO=y diff --git a/software/lib/modbus.c b/software/lib/modbus.c new file mode 100644 index 0000000..7ad2703 --- /dev/null +++ b/software/lib/modbus.c @@ -0,0 +1,164 @@ +#include "modbus.h" +#include +#include +#include +#include + +// LOG_MODULE_REGISTER(mbc, CONFIG_LOG_DEFAULT_LEVEL); +LOG_MODULE_REGISTER(mbc, 4); // Set log level to 4 (Debug) + +#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 new file mode 100644 index 0000000..3d43bac --- /dev/null +++ b/software/lib/modbus.h @@ -0,0 +1,24 @@ +#ifndef __waterlevel_h +#define __waterlevel_h + +#include + +const static struct modbus_iface_param client_param = { + .mode = MODBUS_MODE_RTU, + .rx_timeout = 50000, + .serial = { + .baud = 9600, + .parity = UART_CFG_PARITY_NONE, + }, +}; + +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/modbus_test/.gitignore b/software/modbus_test/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/software/modbus_test/.gitignore @@ -0,0 +1 @@ +build diff --git a/software/modbus_test/.vscode/c_cpp_properties.json b/software/modbus_test/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..b1f27e8 --- /dev/null +++ b/software/modbus_test/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/software/modbus_test/CMakeLists.txt b/software/modbus_test/CMakeLists.txt new file mode 100644 index 0000000..5d2c3bf --- /dev/null +++ b/software/modbus_test/CMakeLists.txt @@ -0,0 +1,14 @@ +# 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/modbus_test/boards/nucleo_f103rb.overlay b/software/modbus_test/boards/nucleo_f103rb.overlay new file mode 100644 index 0000000..a3e38dc --- /dev/null +++ b/software/modbus_test/boards/nucleo_f103rb.overlay @@ -0,0 +1,9 @@ +&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/modbus_test/prj.conf b/software/modbus_test/prj.conf new file mode 100644 index 0000000..69162ba --- /dev/null +++ b/software/modbus_test/prj.conf @@ -0,0 +1,13 @@ +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 + +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=n + +CONFIG_MODBUS=y +CONFIG_MODBUS_ROLE_CLIENT=y \ No newline at end of file diff --git a/software/modbus_test/src/main.c b/software/modbus_test/src/main.c new file mode 100644 index 0000000..dd57a08 --- /dev/null +++ b/software/modbus_test/src/main.c @@ -0,0 +1,81 @@ +/* Testing MODBUS functionality + This code initializes a Modbus client, sets minimum and maximum water levels, + reads the current water level, and logs the results. + You can set the zero point and the 2m point for water level in lines 32 and 38. +*/ +#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); + + while (1) + { /* 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; + k_sleep(K_SECONDS(1)); + } + + return 0; +}