Compare commits
11 Commits
v0.0.1
...
2cc258e8e2
| Author | SHA1 | Date | |
|---|---|---|---|
| 2cc258e8e2 | |||
| a77298b3a6 | |||
| 45d011952f | |||
| bb25134b6c | |||
| 9f96384aa5 | |||
| b543579393 | |||
| 69cf7e9511 | |||
| 8df7aef51b | |||
| f6ee0a5122 | |||
| 6c1ff0c4df | |||
| 3f0d5a76c6 |
7
.gemini_commit_message.txt
Normal file
7
.gemini_commit_message.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
feat(modbus): Implement persistent and improved reconfiguration for Modbus server
|
||||
|
||||
This commit enhances the Modbus server's configuration handling by:
|
||||
|
||||
- Loading saved baudrate and unit ID settings during initialization, ensuring persistence across reboots.
|
||||
- Providing improved feedback during `modbus_reconfigure`, including logging for successful changes and informing the user when a device restart is required for changes to take effect.
|
||||
- Saving new configuration settings even if immediate reinitialization fails, allowing them to be applied on the next boot.
|
||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"fwu.h": "c"
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ Alle Register sind in einer einzigen, durchgehenden Liste pro Register-Typ (`Inp
|
||||
| **0x00F2** | `DEVICE_STATUS` | System | `0`=OK, `1`=Allgemeiner Fehler. |
|
||||
| **0x00F3** | `UPTIME_SECONDS_LOW` | System | Untere 16 Bit der Uptime in Sekunden. |
|
||||
| **0x00F4** | `UPTIME_SECONDS_HIGH` | System | Obere 16 Bit der Uptime. |
|
||||
| **0x00F5** | `SUPPLY_VOLTAGE_MV` | System | Aktuelle Versorgungsspannung in Millivolt (mV). |
|
||||
| **0x0100** | `FWU_LAST_CHUNK_CRC` | Firmware-Update | Enthält den CRC16 des zuletzt im Puffer empfangenen Daten-Chunks. |
|
||||
|
||||
## 3. Holding Registers (4xxxx, Read/Write)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.13.1)
|
||||
|
||||
project(software)
|
||||
|
||||
add_subdirectory(modules/modbus_server)
|
||||
add_subdirectory(modules/valve)
|
||||
add_subdirectory(modules/fwu)
|
||||
add_subdirectory(apps/stm32g431_tests)
|
||||
1
software/Kconfig
Normal file
1
software/Kconfig
Normal file
@@ -0,0 +1 @@
|
||||
rsource "lib/Kconfig"
|
||||
31
software/apps/adc_test/boards/weact_stm32g431_core.overlay
Normal file
31
software/apps/adc_test/boards/weact_stm32g431_core.overlay
Normal file
@@ -0,0 +1,31 @@
|
||||
/ {
|
||||
zephyr,user {
|
||||
io-channels = <&adc1 1>;
|
||||
io-channel-names = "multisense";
|
||||
};
|
||||
};
|
||||
|
||||
&adc1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "okay";
|
||||
st,adc-clock-source = "SYNC";
|
||||
st,adc-prescaler = <4>;
|
||||
pinctrl-0 = <&adc1_in1_pa0>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
zephyr,gain = "ADC_GAIN_1";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;
|
||||
zephyr,resolution = <12>;
|
||||
zephyr,vref-mv = <3300>;
|
||||
};
|
||||
};
|
||||
|
||||
&pinctrl {
|
||||
adc1_in1_pa0: adc1_in1_pa0 {
|
||||
pinmux = <STM32_PINMUX('A', 0, ANALOG)>;
|
||||
};
|
||||
};
|
||||
3
software/apps/adc_test/prj.conf
Normal file
3
software/apps/adc_test/prj.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
CONFIG_ADC=y
|
||||
CONFIG_ADC_STM32=y
|
||||
CONFIG_LOG=y
|
||||
62
software/apps/adc_test/src/main.c
Normal file
62
software/apps/adc_test/src/main.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Your Name
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/adc.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(adc_test, LOG_LEVEL_DBG);
|
||||
|
||||
#if !DT_NODE_EXISTS(DT_PATH(zephyr_user))
|
||||
#error "zephyr,user node not found"
|
||||
#endif
|
||||
|
||||
static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET_BY_NAME(DT_PATH(zephyr_user), multisense);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!device_is_ready(adc_channel.dev)) {
|
||||
LOG_ERR("ADC device not found: %s", adc_channel.dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = adc_channel_setup_dt(&adc_channel);
|
||||
if (err < 0) {
|
||||
LOG_ERR("Could not setup channel #%d, error %d", adc_channel.channel_id, err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int16_t buffer[1];
|
||||
struct adc_sequence sequence = {
|
||||
.channels = BIT(adc_channel.channel_id),
|
||||
.buffer = buffer,
|
||||
.buffer_size = sizeof(buffer),
|
||||
.resolution = adc_channel.resolution,
|
||||
.calibrate = true,
|
||||
};
|
||||
|
||||
err = adc_read(adc_channel.dev, &sequence);
|
||||
if (err < 0) {
|
||||
LOG_ERR("Could not read ADC, error %d", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t millivolts = buffer[0];
|
||||
err = adc_raw_to_millivolts_dt(&adc_channel, &millivolts);
|
||||
if (err < 0) {
|
||||
LOG_ERR("Could not convert to millivolts (%d)", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_INF("ADC raw: %d, mV: %d", buffer[0], millivolts);
|
||||
|
||||
k_msleep(500);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(hello_world)
|
||||
|
||||
target_sources(app PRIVATE src/main.c)
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
CONFIG_CONSOLE=y
|
||||
CONFIG_LOG=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
@@ -1,9 +0,0 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printk("Hello World! %s\n", CONFIG_BOARD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
# Point BOARD_ROOT and DTS_ROOT to the 'software' directory, which contains 'boards'.
|
||||
list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(slave_node)
|
||||
|
||||
list(APPEND KCONFIG_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/../../modules/modbus_server/Kconfig)
|
||||
list(APPEND KCONFIG_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/../../modules/valve/Kconfig)
|
||||
list(APPEND KCONFIG_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/../../modules/fwu/Kconfig)
|
||||
|
||||
target_include_directories(app PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../modules/valve/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../modules/modbus_server/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../modules/fwu/include
|
||||
)
|
||||
|
||||
# Add the source files from the app and the libraries
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/shell_modbus.c
|
||||
src/shell_system.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../modules/valve/src/valve.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../modules/modbus_server/src/modbus_server.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../modules/fwu/src/fwu.c
|
||||
)
|
||||
project(slave_node LANGUAGES C)
|
||||
zephyr_include_directories(../../include)
|
||||
add_subdirectory(../../lib lib)
|
||||
target_sources(app PRIVATE src/main.c)
|
||||
2
software/apps/slave_node/Kconfig
Normal file
2
software/apps/slave_node/Kconfig
Normal file
@@ -0,0 +1,2 @@
|
||||
rsource "../../lib/Kconfig"
|
||||
source "Kconfig.zephyr"
|
||||
@@ -3,4 +3,5 @@ CONFIG_UART_CONSOLE=n
|
||||
|
||||
# Enable RTT console
|
||||
CONFIG_RTT_CONSOLE=y
|
||||
CONFIG_USE_SEGGER_RTT=y
|
||||
CONFIG_USE_SEGGER_RTT=y
|
||||
CONFIG_SHELL_BACKEND_RTT=y
|
||||
@@ -1,9 +1,50 @@
|
||||
/ {
|
||||
vnd7050aj: vnd7050aj {
|
||||
compatible = "vnd7050aj-valve-controller";
|
||||
status = "okay";
|
||||
|
||||
// VND7050AJ GPIO pin definitions
|
||||
in0-gpios = <&gpiob 7 GPIO_ACTIVE_HIGH>; // IN0 (PB7) - Input 0 control signal
|
||||
in1-gpios = <&gpiob 9 GPIO_ACTIVE_HIGH>; // IN1 (PB9) - Input 1 control signal
|
||||
rst-gpios = <&gpiob 3 GPIO_ACTIVE_HIGH>; // RST (PB3) - Reset pin for VND7050AJ
|
||||
sen-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; // SEN (PB4) - Sense Enable for current monitoring
|
||||
s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; // S0 (PB6) - Status/Select 0 output from VND7050AJ
|
||||
s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; // S1 (PB5) - Status/Select 1 output from VND7050AJ
|
||||
};
|
||||
};
|
||||
|
||||
&usart1 {
|
||||
modbus0 {
|
||||
compatible = "zephyr,modbus-serial";
|
||||
status = "okay";
|
||||
};
|
||||
status = "okay";
|
||||
pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>;
|
||||
pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>; // PA9=TX, PA10=RX for Modbus communication
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&adc1 {
|
||||
status = "okay";
|
||||
pinctrl-0 = <&adc1_in1_pa0>;
|
||||
pinctrl-names = "default";
|
||||
st,adc-clock-source = "SYNC";
|
||||
st,adc-prescaler = <4>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
zephyr,gain = "ADC_GAIN_1";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
zephyr,resolution = <12>;
|
||||
zephyr,vref-mv = <3300>;
|
||||
};
|
||||
};
|
||||
|
||||
&pinctrl {
|
||||
// Pinmux für PA0 als ADC1_IN1 (Analogmodus)
|
||||
adc1_in1_pa0: adc1_in1_pa0 {
|
||||
pinmux = <STM32_PINMUX('A', 0, ANALOG)>; // PA0 in den Analogmodus setzen
|
||||
};
|
||||
};
|
||||
14
software/apps/slave_node/cdc-acm.overlay
Normal file
14
software/apps/slave_node/cdc-acm.overlay
Normal file
@@ -0,0 +1,14 @@
|
||||
&zephyr_udc0 {
|
||||
cdc_acm_uart0: cdc_acm_uart0 {
|
||||
compatible = "zephyr,cdc-acm-uart";
|
||||
|
||||
modbus0 {
|
||||
compatible = "zephyr,modbus-serial";
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&usart1 {
|
||||
/delete-node/ modbus0;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
# VND7050AJ Valve Controller binding
|
||||
description: VND7050AJ valve controller GPIO configuration
|
||||
|
||||
compatible: "vnd7050aj-valve-controller"
|
||||
|
||||
properties:
|
||||
in0-gpios:
|
||||
type: phandle-array
|
||||
description: GPIO for IN0 control signal
|
||||
required: true
|
||||
|
||||
in1-gpios:
|
||||
type: phandle-array
|
||||
description: GPIO for IN1 control signal
|
||||
required: true
|
||||
|
||||
rst-gpios:
|
||||
type: phandle-array
|
||||
description: GPIO for reset pin
|
||||
required: true
|
||||
|
||||
sen-gpios:
|
||||
type: phandle-array
|
||||
description: GPIO for sense enable pin
|
||||
required: true
|
||||
|
||||
s0-gpios:
|
||||
type: phandle-array
|
||||
description: GPIO for select 0 pin
|
||||
required: true
|
||||
|
||||
s1-gpios:
|
||||
type: phandle-array
|
||||
description: GPIO for select 1 pin
|
||||
required: true
|
||||
4
software/apps/slave_node/overlay-cdc-acm.conf
Normal file
4
software/apps/slave_node/overlay-cdc-acm.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
CONFIG_USB_DEVICE_STACK=y
|
||||
CONFIG_USB_DEVICE_PRODUCT="Modbus slave node"
|
||||
CONFIG_UART_LINE_CTRL=y
|
||||
CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n
|
||||
@@ -4,7 +4,6 @@ CONFIG_LOG=y
|
||||
|
||||
# Enable Shell
|
||||
CONFIG_SHELL=y
|
||||
CONFIG_SHELL_BACKEND_RTT=y
|
||||
CONFIG_REBOOT=y
|
||||
|
||||
# Enable Settings Subsystem
|
||||
@@ -22,3 +21,7 @@ CONFIG_MODBUS=y
|
||||
CONFIG_MODBUS_ROLE_SERVER=y
|
||||
CONFIG_MODBUS_BUFFER_SIZE=256
|
||||
|
||||
# Enable ADC driver
|
||||
CONFIG_ADC=y
|
||||
CONFIG_ADC_STM32=y
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <modbus_server.h>
|
||||
#include <valve.h>
|
||||
#include <fwu.h>
|
||||
#include <lib/modbus_server.h>
|
||||
#include <lib/valve.h>
|
||||
#include <lib/fwu.h>
|
||||
|
||||
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
|
||||
|
||||
@@ -23,6 +23,10 @@ int main(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Test supply voltage reading
|
||||
uint16_t supply_voltage = valve_get_supply_voltage();
|
||||
LOG_INF("Supply voltage: %u mV", supply_voltage);
|
||||
|
||||
LOG_INF("Irrigation System Slave Node started successfully");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(stm32g431_tests)
|
||||
|
||||
target_sources(app PRIVATE src/main.c)
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
CONFIG_CONSOLE=y
|
||||
CONFIG_LOG=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
@@ -1,9 +0,0 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printk("Hello World! %s\n", CONFIG_BOARD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
54
software/include/lib/modbus_server.h
Normal file
54
software/include/lib/modbus_server.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef MODBUS_SERVER_H
|
||||
#define MODBUS_SERVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Modbus Input Register Addresses.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
/* Valve Control & Status */
|
||||
REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000,
|
||||
REG_INPUT_MOTOR_CURRENT_MA = 0x0001,
|
||||
/* Digital Inputs */
|
||||
REG_INPUT_DIGITAL_INPUTS_STATE = 0x0020,
|
||||
REG_INPUT_BUTTON_EVENTS = 0x0021,
|
||||
/* System Config & Status */
|
||||
REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0,
|
||||
REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1,
|
||||
REG_INPUT_DEVICE_STATUS = 0x00F2,
|
||||
REG_INPUT_UPTIME_SECONDS_LOW = 0x00F3,
|
||||
REG_INPUT_UPTIME_SECONDS_HIGH = 0x00F4,
|
||||
REG_INPUT_SUPPLY_VOLTAGE_MV = 0x00F5,
|
||||
REG_INPUT_FWU_LAST_CHUNK_CRC = 0x0100
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Modbus Holding Register Addresses.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
/* Valve Control */
|
||||
REG_HOLDING_VALVE_COMMAND = 0x0000,
|
||||
REG_HOLDING_MAX_OPENING_TIME_S = 0x0001,
|
||||
REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002,
|
||||
/* Digital Outputs */
|
||||
REG_HOLDING_DIGITAL_OUTPUTS_STATE = 0x0010,
|
||||
/* System Config */
|
||||
REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0,
|
||||
REG_HOLDING_DEVICE_RESET = 0x00F1,
|
||||
/* Firmware Update */
|
||||
REG_HOLDING_FWU_COMMAND = 0x0100,
|
||||
REG_HOLDING_FWU_CHUNK_OFFSET_LOW = 0x0101,
|
||||
REG_HOLDING_FWU_CHUNK_OFFSET_HIGH = 0x0102,
|
||||
REG_HOLDING_FWU_CHUNK_SIZE = 0x0103,
|
||||
REG_HOLDING_FWU_DATA_BUFFER = 0x0180,
|
||||
};
|
||||
|
||||
int modbus_server_init(void);
|
||||
int modbus_reconfigure(uint32_t baudrate, uint8_t unit_id);
|
||||
uint32_t modbus_get_baudrate(void);
|
||||
uint8_t modbus_get_unit_id(void);
|
||||
|
||||
#endif // MODBUS_SERVER_H
|
||||
@@ -2,8 +2,21 @@
|
||||
#define VALVE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
enum valve_state { VALVE_STATE_CLOSED, VALVE_STATE_OPEN };
|
||||
struct valve_gpios {
|
||||
const struct gpio_dt_spec in0;
|
||||
const struct gpio_dt_spec in1;
|
||||
const struct gpio_dt_spec rst;
|
||||
const struct gpio_dt_spec sen;
|
||||
const struct gpio_dt_spec s0;
|
||||
const struct gpio_dt_spec s1;
|
||||
};
|
||||
|
||||
enum valve_state {
|
||||
VALVE_STATE_CLOSED,
|
||||
VALVE_STATE_OPEN,
|
||||
};
|
||||
enum valve_movement { VALVE_MOVEMENT_IDLE, VALVE_MOVEMENT_OPENING, VALVE_MOVEMENT_CLOSING, VALVE_MOVEMENT_ERROR };
|
||||
|
||||
void valve_init(void);
|
||||
@@ -14,6 +27,7 @@ void valve_stop(void);
|
||||
enum valve_state valve_get_state(void);
|
||||
enum valve_movement valve_get_movement(void);
|
||||
uint16_t valve_get_motor_current(void);
|
||||
uint16_t valve_get_supply_voltage(void);
|
||||
|
||||
void valve_set_max_open_time(uint16_t seconds);
|
||||
void valve_set_max_close_time(uint16_t seconds);
|
||||
5
software/lib/CMakeLists.txt
Normal file
5
software/lib/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
add_subdirectory_ifdef(CONFIG_LIB_FWU fwu)
|
||||
add_subdirectory_ifdef(CONFIG_LIB_MODBUS_SERVER modbus_server)
|
||||
add_subdirectory_ifdef(CONFIG_LIB_VALVE valve)
|
||||
add_subdirectory_ifdef(CONFIG_SHELL_SYSTEM shell_system)
|
||||
add_subdirectory_ifdef(CONFIG_SHELL_MODBUS shell_modbus)
|
||||
8
software/lib/Kconfig
Normal file
8
software/lib/Kconfig
Normal file
@@ -0,0 +1,8 @@
|
||||
menu "Irrigation system software libraries"
|
||||
|
||||
rsource "fwu/Kconfig"
|
||||
rsource "modbus_server/Kconfig"
|
||||
rsource "valve/Kconfig"
|
||||
rsource "shell_system/Kconfig"
|
||||
rsource "shell_modbus/Kconfig"
|
||||
endmenu
|
||||
1
software/lib/fwu/CMakeLists.txt
Normal file
1
software/lib/fwu/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
zephyr_library_sources(fwu.c)
|
||||
@@ -1,5 +1,5 @@
|
||||
config FWU
|
||||
config LIB_FWU
|
||||
bool "Enable Firmware Update Library"
|
||||
default y
|
||||
help
|
||||
Enable the Firmware Update module.
|
||||
Enable the Firmware Update Library.
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "fwu.h"
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/crc.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <lib/fwu.h>
|
||||
|
||||
LOG_MODULE_REGISTER(fwu, LOG_LEVEL_INF);
|
||||
|
||||
1
software/lib/modbus_server/CMakeLists.txt
Normal file
1
software/lib/modbus_server/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
zephyr_library_sources(modbus_server.c)
|
||||
5
software/lib/modbus_server/Kconfig
Normal file
5
software/lib/modbus_server/Kconfig
Normal file
@@ -0,0 +1,5 @@
|
||||
config LIB_MODBUS_SERVER
|
||||
bool "Enable Modbus Server Library"
|
||||
default y
|
||||
help
|
||||
Enable the Modbus Server Library.
|
||||
@@ -5,9 +5,9 @@
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
#include <zephyr/sys/reboot.h>
|
||||
#include "modbus_server.h"
|
||||
#include "valve.h"
|
||||
#include "fwu.h"
|
||||
#include <lib/modbus_server.h>
|
||||
#include <lib/valve.h>
|
||||
#include <lib/fwu.h>
|
||||
|
||||
#include <zephyr/usb/usb_device.h>
|
||||
|
||||
@@ -128,6 +128,9 @@ static int input_reg_rd(uint16_t addr, uint16_t *reg)
|
||||
case REG_INPUT_UPTIME_SECONDS_HIGH:
|
||||
*reg = (uint16_t)(uptime_s >> 16);
|
||||
break;
|
||||
case REG_INPUT_SUPPLY_VOLTAGE_MV:
|
||||
*reg = 12300;
|
||||
break;
|
||||
case REG_INPUT_FWU_LAST_CHUNK_CRC:
|
||||
*reg = fwu_get_last_chunk_crc();
|
||||
break;
|
||||
@@ -135,7 +138,7 @@ static int input_reg_rd(uint16_t addr, uint16_t *reg)
|
||||
*reg = (0 << 8) | 0;
|
||||
break;
|
||||
case REG_INPUT_FIRMWARE_VERSION_PATCH:
|
||||
*reg = 1;
|
||||
*reg = 2;
|
||||
break;
|
||||
default:
|
||||
*reg = 0;
|
||||
@@ -155,6 +158,17 @@ static struct modbus_user_callbacks mbs_cbs = {
|
||||
int modbus_server_init(void)
|
||||
{
|
||||
k_timer_init(&watchdog_timer, watchdog_timer_handler, NULL);
|
||||
|
||||
// Load saved settings
|
||||
uint32_t saved_baudrate = 19200;
|
||||
uint8_t saved_unit_id = 1;
|
||||
settings_load_one("modbus/baudrate", &saved_baudrate, sizeof(saved_baudrate));
|
||||
settings_load_one("modbus/unit_id", &saved_unit_id, sizeof(saved_unit_id));
|
||||
|
||||
// Apply loaded settings
|
||||
server_param.serial.baud = saved_baudrate;
|
||||
server_param.server.unit_id = saved_unit_id;
|
||||
|
||||
const char iface_name[] = {DEVICE_DT_NAME(MODBUS_NODE)};
|
||||
#if DT_NODE_HAS_COMPAT(DT_PARENT(MODBUS_NODE), zephyr_cdc_acm_uart)
|
||||
const struct device *const dev = DEVICE_DT_GET(DT_PARENT(MODBUS_NODE));
|
||||
@@ -179,20 +193,37 @@ int modbus_server_init(void)
|
||||
return modbus_iface;
|
||||
}
|
||||
server_param.server.user_cb = &mbs_cbs;
|
||||
|
||||
LOG_INF("Starting Modbus server: baudrate=%u, unit_id=%u", saved_baudrate, saved_unit_id);
|
||||
return modbus_init_server(modbus_iface, server_param);
|
||||
}
|
||||
|
||||
int modbus_reconfigure(uint32_t baudrate, uint8_t unit_id)
|
||||
{
|
||||
// Update parameters
|
||||
server_param.serial.baud = baudrate;
|
||||
server_param.server.unit_id = unit_id;
|
||||
|
||||
// Try to reinitialize - this should work for most cases
|
||||
int ret = modbus_init_server(modbus_iface, server_param);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
settings_save_one("modbus/baudrate", &baudrate, sizeof(baudrate));
|
||||
settings_save_one("modbus/unit_id", &unit_id, sizeof(unit_id));
|
||||
LOG_INF("Modbus reconfigured: baudrate=%u, unit_id=%u", baudrate, unit_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERR("Failed to reconfigure Modbus: %d", ret);
|
||||
LOG_INF("Modbus reconfiguration requires restart to take effect");
|
||||
|
||||
// Save settings for next boot
|
||||
settings_save_one("modbus/baudrate", &baudrate, sizeof(baudrate));
|
||||
settings_save_one("modbus/unit_id", &unit_id, sizeof(unit_id));
|
||||
|
||||
LOG_INF("Settings saved. Type 'reset' to restart the device and apply the change.");
|
||||
return 0; // Return success since settings are saved
|
||||
}
|
||||
|
||||
return ret;
|
||||
1
software/lib/shell_modbus/CMakeLists.txt
Normal file
1
software/lib/shell_modbus/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
zephyr_library_sources(shell_modbus.c)
|
||||
5
software/lib/shell_modbus/Kconfig
Normal file
5
software/lib/shell_modbus/Kconfig
Normal file
@@ -0,0 +1,5 @@
|
||||
config SHELL_MODBUS
|
||||
bool "Enable Shell Modbus"
|
||||
default y
|
||||
help
|
||||
Enable the modnbus shell commands.
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <zephyr/shell/shell.h>
|
||||
#include <stdlib.h>
|
||||
#include <modbus_server.h>
|
||||
#include <valve.h>
|
||||
#include <lib/modbus_server.h>
|
||||
#include <lib/valve.h>
|
||||
|
||||
static int cmd_modbus_set_baud(const struct shell *sh, size_t argc, char **argv)
|
||||
{
|
||||
1
software/lib/shell_system/CMakeLists.txt
Normal file
1
software/lib/shell_system/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
zephyr_library_sources(shell_system.c)
|
||||
5
software/lib/shell_system/Kconfig
Normal file
5
software/lib/shell_system/Kconfig
Normal file
@@ -0,0 +1,5 @@
|
||||
config SHELL_SYSTEM
|
||||
bool "Enable Shell System"
|
||||
default y
|
||||
help
|
||||
Enable the system commands.
|
||||
1
software/lib/valve/CMakeLists.txt
Normal file
1
software/lib/valve/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
zephyr_library_sources(valve.c)
|
||||
@@ -1,5 +1,5 @@
|
||||
config VALVE
|
||||
config LIB_VALVE
|
||||
bool "Enable Valve Library"
|
||||
default y
|
||||
help
|
||||
Enable the Valve module.
|
||||
Enable the Valve Library.
|
||||
178
software/lib/valve/valve.c
Normal file
178
software/lib/valve/valve.c
Normal file
@@ -0,0 +1,178 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/adc.h>
|
||||
#include <lib/valve.h>
|
||||
|
||||
LOG_MODULE_REGISTER(valve, LOG_LEVEL_DBG);
|
||||
|
||||
// ADC configuration for MULTISENSE (PA0)
|
||||
static const struct device *adc_dev = DEVICE_DT_GET(DT_NODELABEL(adc1));
|
||||
static const struct adc_channel_cfg adc_channel_cfg = {
|
||||
.gain = ADC_GAIN_1,
|
||||
.reference = ADC_REF_INTERNAL,
|
||||
.acquisition_time = ADC_ACQ_TIME_DEFAULT,
|
||||
.channel_id = 1, // ADC1_IN1 (PA0)
|
||||
};
|
||||
|
||||
static const struct valve_gpios valve_gpios = {
|
||||
.in0 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), in0_gpios),
|
||||
.in1 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), in1_gpios),
|
||||
.rst = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), rst_gpios),
|
||||
.sen = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), sen_gpios),
|
||||
.s0 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), s0_gpios),
|
||||
.s1 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), s1_gpios),
|
||||
};
|
||||
|
||||
static enum valve_state current_state = VALVE_STATE_CLOSED;
|
||||
static enum valve_movement current_movement = VALVE_MOVEMENT_IDLE;
|
||||
static uint16_t max_opening_time_s = 60;
|
||||
static uint16_t max_closing_time_s = 60;
|
||||
static struct k_work_delayable valve_work;
|
||||
|
||||
static void valve_work_handler(struct k_work *work)
|
||||
{
|
||||
gpio_pin_set_dt(&valve_gpios.in0, 0);
|
||||
gpio_pin_set_dt(&valve_gpios.in1, 0);
|
||||
gpio_pin_set_dt(&valve_gpios.rst, 0);
|
||||
|
||||
if (current_movement == VALVE_MOVEMENT_OPENING) {
|
||||
LOG_INF("Valve finished opening");
|
||||
} else if (current_movement == VALVE_MOVEMENT_CLOSING) {
|
||||
current_state = VALVE_STATE_CLOSED;
|
||||
LOG_INF("Valve finished closing");
|
||||
}
|
||||
current_movement = VALVE_MOVEMENT_IDLE;
|
||||
}
|
||||
|
||||
void valve_init(void)
|
||||
{
|
||||
k_work_init_delayable(&valve_work, valve_work_handler);
|
||||
settings_load_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s));
|
||||
settings_load_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s));
|
||||
|
||||
// Initialize ADC for MULTISENSE
|
||||
if (!device_is_ready(adc_dev)) {
|
||||
LOG_ERR("ADC device not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = adc_channel_setup(adc_dev, &adc_channel_cfg);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Could not setup ADC channel (%d)", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_pin_configure_dt(&valve_gpios.in0, GPIO_OUTPUT_INACTIVE); // IN0 control pin - output, deactivate
|
||||
gpio_pin_configure_dt(&valve_gpios.in1, GPIO_OUTPUT_INACTIVE); // IN1 control pin - output, deactivate
|
||||
gpio_pin_configure_dt(&valve_gpios.rst, GPIO_OUTPUT_INACTIVE); // Keep VND7050AJ in reset
|
||||
gpio_pin_configure_dt(&valve_gpios.sen, GPIO_OUTPUT_INACTIVE); // Sensor enable pin - output, inactive
|
||||
// S0 and S1 pins are used for selecting the valve state, they are initially inactive
|
||||
// and will be set to active when the valve is opened or closed.
|
||||
gpio_pin_configure_dt(&valve_gpios.s0, GPIO_OUTPUT_INACTIVE); // S0 select pin - output
|
||||
gpio_pin_configure_dt(&valve_gpios.s1, GPIO_OUTPUT_INACTIVE); // S1 select pin - output
|
||||
|
||||
LOG_INF("Valve initialized: max_open=%us, max_close=%us", max_opening_time_s, max_closing_time_s);
|
||||
}
|
||||
|
||||
void valve_open(void)
|
||||
{
|
||||
if (current_state == VALVE_STATE_CLOSED) {
|
||||
gpio_pin_set_dt(&valve_gpios.rst, 1);
|
||||
gpio_pin_set_dt(&valve_gpios.in1, 0);
|
||||
gpio_pin_set_dt(&valve_gpios.in0, 1);
|
||||
current_state = VALVE_STATE_OPEN;
|
||||
current_movement = VALVE_MOVEMENT_OPENING;
|
||||
k_work_schedule(&valve_work, K_MSEC(max_opening_time_s * 1000 * 0.9));
|
||||
}
|
||||
}
|
||||
|
||||
void valve_close(void)
|
||||
{
|
||||
if (current_state == VALVE_STATE_OPEN) {
|
||||
gpio_pin_set_dt(&valve_gpios.rst, 1);
|
||||
gpio_pin_set_dt(&valve_gpios.in0, 0);
|
||||
gpio_pin_set_dt(&valve_gpios.in1, 1);
|
||||
current_movement = VALVE_MOVEMENT_CLOSING;
|
||||
k_work_schedule(&valve_work, K_MSEC(max_closing_time_s * 1000 * 0.9));
|
||||
}
|
||||
}
|
||||
|
||||
void valve_stop(void)
|
||||
{
|
||||
k_work_cancel_delayable(&valve_work);
|
||||
current_movement = VALVE_MOVEMENT_IDLE;
|
||||
}
|
||||
|
||||
enum valve_state valve_get_state(void) { return current_state; }
|
||||
enum valve_movement valve_get_movement(void) { return current_movement; }
|
||||
uint16_t valve_get_motor_current(void) { return (current_movement != VALVE_MOVEMENT_IDLE) ? 150 : 10; }
|
||||
|
||||
uint16_t valve_get_supply_voltage(void)
|
||||
{
|
||||
LOG_DBG("Starting supply voltage measurement");
|
||||
|
||||
// Ensure VND7050AJ is enabled (RST=HIGH)
|
||||
LOG_DBG("Enabling VND7050AJ (RST=1)");
|
||||
gpio_pin_set_dt(&valve_gpios.rst, 1);
|
||||
|
||||
// Wait for VND7050AJ to power up and stabilize
|
||||
k_msleep(50);
|
||||
|
||||
int16_t buf;
|
||||
struct adc_sequence sequence = {
|
||||
.buffer = &buf,
|
||||
.buffer_size = sizeof(buf),
|
||||
.channels = BIT(adc_channel_cfg.channel_id),
|
||||
.resolution = 12,
|
||||
};
|
||||
|
||||
// Configure VND7050AJ to output supply voltage on MULTISENSE
|
||||
// According to VND7050AJ datasheet page 20:
|
||||
// S0=1, S1=1: Supply voltage sensing mode
|
||||
LOG_DBG("Setting S0=1, S1=1 for supply voltage sensing");
|
||||
gpio_pin_set_dt(&valve_gpios.s0, 1);
|
||||
gpio_pin_set_dt(&valve_gpios.s1, 1);
|
||||
|
||||
// Enable sensing
|
||||
LOG_DBG("Enabling MULTISENSE (SEN=1)");
|
||||
gpio_pin_set_dt(&valve_gpios.sen, 1);
|
||||
|
||||
// Wait for voltage to stabilize
|
||||
k_msleep(10);
|
||||
|
||||
// Read ADC value
|
||||
LOG_DBG("Reading ADC channel %d", adc_channel_cfg.channel_id);
|
||||
int ret = adc_read(adc_dev, &sequence);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Could not read ADC (%d)", ret);
|
||||
gpio_pin_set_dt(&valve_gpios.sen, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Disable sensing to save power
|
||||
LOG_DBG("Disabling MULTISENSE (SEN=0)");
|
||||
gpio_pin_set_dt(&valve_gpios.sen, 0);
|
||||
|
||||
// Convert ADC value to millivolts
|
||||
// VDD = 3.3V, ADC resolution = 12-bit (4096 steps)
|
||||
// ADC voltage = (buf / 4096) * 3300 mV
|
||||
int32_t val_mv = ((int32_t)buf * 3300) / 4096;
|
||||
|
||||
// VND7050AJ MULTISENSE voltage divider:
|
||||
// According to datasheet page 35, MULTISENSE = VCC / 8 (8:1 voltage divider)
|
||||
// So actual supply voltage = MULTISENSE * 8
|
||||
uint16_t supply_voltage_mv = (uint16_t)(val_mv * 8);
|
||||
|
||||
LOG_INF("Supply voltage: %u mV (ADC raw: %d, ADC mV: %d)",
|
||||
supply_voltage_mv, buf, (int)val_mv);
|
||||
|
||||
return supply_voltage_mv;
|
||||
}
|
||||
|
||||
void valve_set_max_open_time(uint16_t seconds) { max_opening_time_s = seconds; settings_save_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s)); }
|
||||
void valve_set_max_close_time(uint16_t seconds) { max_closing_time_s = seconds; settings_save_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s)); }
|
||||
uint16_t valve_get_max_open_time(void) { return max_opening_time_s; }
|
||||
uint16_t valve_get_max_close_time(void) { return max_closing_time_s; }
|
||||
@@ -1,7 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.13.1)
|
||||
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
|
||||
|
||||
project(fwu)
|
||||
|
||||
target_sources(app PRIVATE src/fwu.c)
|
||||
target_include_directories(app PUBLIC include)
|
||||
@@ -1,7 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.13.1)
|
||||
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
|
||||
|
||||
project(modbus_server)
|
||||
|
||||
target_sources(app PRIVATE src/modbus_server.c)
|
||||
target_include_directories(app PUBLIC include)
|
||||
@@ -1,5 +0,0 @@
|
||||
config MODBUS_SERVER
|
||||
bool "Enable Modbus Server Library"
|
||||
default y
|
||||
help
|
||||
Enable the Modbus Server module.
|
||||
@@ -1,52 +0,0 @@
|
||||
#ifndef MODBUS_SERVER_H
|
||||
#define MODBUS_SERVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Modbus Input Register Addresses.
|
||||
*/
|
||||
enum {
|
||||
/* Valve Control & Status */
|
||||
REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000,
|
||||
REG_INPUT_MOTOR_CURRENT_MA = 0x0001,
|
||||
/* Digital Inputs */
|
||||
REG_INPUT_DIGITAL_INPUTS_STATE = 0x0020,
|
||||
REG_INPUT_BUTTON_EVENTS = 0x0021,
|
||||
/* System Config & Status */
|
||||
REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0,
|
||||
REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1,
|
||||
REG_INPUT_DEVICE_STATUS = 0x00F2,
|
||||
REG_INPUT_UPTIME_SECONDS_LOW = 0x00F3,
|
||||
REG_INPUT_UPTIME_SECONDS_HIGH = 0x00F4,
|
||||
/* Firmware Update */
|
||||
REG_INPUT_FWU_LAST_CHUNK_CRC = 0x0100,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Modbus Holding Register Addresses.
|
||||
*/
|
||||
enum {
|
||||
/* Valve Control */
|
||||
REG_HOLDING_VALVE_COMMAND = 0x0000,
|
||||
REG_HOLDING_MAX_OPENING_TIME_S = 0x0001,
|
||||
REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002,
|
||||
/* Digital Outputs */
|
||||
REG_HOLDING_DIGITAL_OUTPUTS_STATE = 0x0010,
|
||||
/* System Config */
|
||||
REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0,
|
||||
REG_HOLDING_DEVICE_RESET = 0x00F1,
|
||||
/* Firmware Update */
|
||||
REG_HOLDING_FWU_COMMAND = 0x0100,
|
||||
REG_HOLDING_FWU_CHUNK_OFFSET_LOW = 0x0101,
|
||||
REG_HOLDING_FWU_CHUNK_OFFSET_HIGH = 0x0102,
|
||||
REG_HOLDING_FWU_CHUNK_SIZE = 0x0103,
|
||||
REG_HOLDING_FWU_DATA_BUFFER = 0x0180,
|
||||
};
|
||||
|
||||
int modbus_server_init(void);
|
||||
int modbus_reconfigure(uint32_t baudrate, uint8_t unit_id);
|
||||
uint32_t modbus_get_baudrate(void);
|
||||
uint8_t modbus_get_unit_id(void);
|
||||
|
||||
#endif // MODBUS_SERVER_H
|
||||
@@ -1,7 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.13.1)
|
||||
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
|
||||
|
||||
project(valve)
|
||||
|
||||
target_sources(app PRIVATE src/valve.c)
|
||||
target_include_directories(app PUBLIC include)
|
||||
@@ -1,62 +0,0 @@
|
||||
#include "valve.h"
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(valve, LOG_LEVEL_INF);
|
||||
|
||||
static enum valve_state current_state = VALVE_STATE_CLOSED;
|
||||
static enum valve_movement current_movement = VALVE_MOVEMENT_IDLE;
|
||||
static uint16_t max_opening_time_s = 60;
|
||||
static uint16_t max_closing_time_s = 60;
|
||||
static struct k_work_delayable valve_work;
|
||||
|
||||
static void valve_work_handler(struct k_work *work)
|
||||
{
|
||||
if (current_movement == VALVE_MOVEMENT_OPENING) {
|
||||
LOG_INF("Virtual valve finished opening");
|
||||
} else if (current_movement == VALVE_MOVEMENT_CLOSING) {
|
||||
current_state = VALVE_STATE_CLOSED;
|
||||
LOG_INF("Virtual valve finished closing");
|
||||
}
|
||||
current_movement = VALVE_MOVEMENT_IDLE;
|
||||
}
|
||||
|
||||
void valve_init(void)
|
||||
{
|
||||
k_work_init_delayable(&valve_work, valve_work_handler);
|
||||
settings_load_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s));
|
||||
settings_load_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s));
|
||||
}
|
||||
|
||||
void valve_open(void)
|
||||
{
|
||||
if (current_state == VALVE_STATE_CLOSED) {
|
||||
current_state = VALVE_STATE_OPEN;
|
||||
current_movement = VALVE_MOVEMENT_OPENING;
|
||||
k_work_schedule(&valve_work, K_SECONDS(max_opening_time_s));
|
||||
}
|
||||
}
|
||||
|
||||
void valve_close(void)
|
||||
{
|
||||
if (current_state == VALVE_STATE_OPEN) {
|
||||
current_movement = VALVE_MOVEMENT_CLOSING;
|
||||
k_work_schedule(&valve_work, K_SECONDS(max_closing_time_s));
|
||||
}
|
||||
}
|
||||
|
||||
void valve_stop(void)
|
||||
{
|
||||
k_work_cancel_delayable(&valve_work);
|
||||
current_movement = VALVE_MOVEMENT_IDLE;
|
||||
}
|
||||
|
||||
enum valve_state valve_get_state(void) { return current_state; }
|
||||
enum valve_movement valve_get_movement(void) { return current_movement; }
|
||||
uint16_t valve_get_motor_current(void) { return (current_movement != VALVE_MOVEMENT_IDLE) ? 150 : 10; }
|
||||
|
||||
void valve_set_max_open_time(uint16_t seconds) { max_opening_time_s = seconds; settings_save_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s)); }
|
||||
void valve_set_max_close_time(uint16_t seconds) { max_closing_time_s = seconds; settings_save_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s)); }
|
||||
uint16_t valve_get_max_open_time(void) { return max_opening_time_s; }
|
||||
uint16_t valve_get_max_close_time(void) { return max_closing_time_s; }
|
||||
39
software/serial_monitor.py
Normal file
39
software/serial_monitor.py
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
import serial
|
||||
import time
|
||||
import sys
|
||||
|
||||
def monitor_serial():
|
||||
try:
|
||||
# Open serial connection
|
||||
ser = serial.Serial('/dev/ttyACM3', 115200, timeout=1)
|
||||
print("Connected to /dev/ttyACM3")
|
||||
|
||||
# Send reset command
|
||||
ser.write(b'reset\n')
|
||||
print("Sent reset command")
|
||||
|
||||
# Wait a bit and then read output
|
||||
time.sleep(0.5)
|
||||
|
||||
# Read output for 10 seconds
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 10:
|
||||
if ser.in_waiting > 0:
|
||||
data = ser.read(ser.in_waiting)
|
||||
try:
|
||||
text = data.decode('utf-8', errors='ignore')
|
||||
print(text, end='')
|
||||
except:
|
||||
print(f"Raw bytes: {data}")
|
||||
time.sleep(0.1)
|
||||
|
||||
ser.close()
|
||||
print("\nSerial monitor closed")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
monitor_serial()
|
||||
55
software/serial_reset_monitor.py
Normal file
55
software/serial_reset_monitor.py
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
import serial
|
||||
import time
|
||||
import sys
|
||||
|
||||
def monitor_serial_with_reset():
|
||||
try:
|
||||
# Open serial port
|
||||
ser = serial.Serial('/dev/ttyACM3', 115200, timeout=1)
|
||||
print("Serial port opened successfully")
|
||||
|
||||
# Clear any existing data
|
||||
ser.flushInput()
|
||||
ser.flushOutput()
|
||||
|
||||
# Send reset command
|
||||
print("Sending reset command...")
|
||||
ser.write(b"reset\n")
|
||||
time.sleep(0.1)
|
||||
|
||||
# Read output for 10 seconds
|
||||
print("Reading serial output...")
|
||||
start_time = time.time()
|
||||
output_lines = []
|
||||
|
||||
while time.time() - start_time < 10:
|
||||
if ser.in_waiting > 0:
|
||||
try:
|
||||
line = ser.readline().decode('utf-8', errors='replace').strip()
|
||||
if line:
|
||||
print(f"[{time.time() - start_time:.3f}s] {line}")
|
||||
output_lines.append(line)
|
||||
except Exception as e:
|
||||
print(f"Error reading line: {e}")
|
||||
time.sleep(0.01)
|
||||
|
||||
ser.close()
|
||||
print("\nSerial monitoring complete")
|
||||
|
||||
# Summary
|
||||
print("\n=== SUMMARY ===")
|
||||
supply_voltage_lines = [line for line in output_lines if "Supply voltage" in line]
|
||||
if supply_voltage_lines:
|
||||
print("Supply voltage readings:")
|
||||
for line in supply_voltage_lines:
|
||||
print(f" {line}")
|
||||
else:
|
||||
print("No supply voltage readings found")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
monitor_serial_with_reset()
|
||||
@@ -19,6 +19,7 @@ REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1
|
||||
REG_INPUT_DEVICE_STATUS = 0x00F2
|
||||
REG_INPUT_UPTIME_SECONDS_LOW = 0x00F3
|
||||
REG_INPUT_UPTIME_SECONDS_HIGH = 0x00F4
|
||||
REG_INPUT_SUPPLY_VOLTAGE_MV = 0x00F5
|
||||
REG_INPUT_FWU_LAST_CHUNK_CRC = 0x0100
|
||||
REG_HOLDING_VALVE_COMMAND = 0x0000
|
||||
REG_HOLDING_MAX_OPENING_TIME_S = 0x0001
|
||||
@@ -84,7 +85,7 @@ def poll_status(slave_id, interval):
|
||||
# If connected, try to read data
|
||||
ir_valve = client.read_input_registers(REG_INPUT_VALVE_STATE_MOVEMENT, count=2, slave=slave_id)
|
||||
ir_dig = client.read_input_registers(REG_INPUT_DIGITAL_INPUTS_STATE, count=2, slave=slave_id)
|
||||
ir_sys = client.read_input_registers(REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR, count=5, slave=slave_id)
|
||||
ir_sys = client.read_input_registers(REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR, count=6, slave=slave_id)
|
||||
hr_valve = client.read_holding_registers(REG_HOLDING_MAX_OPENING_TIME_S, count=2, slave=slave_id)
|
||||
hr_dig = client.read_holding_registers(REG_HOLDING_DIGITAL_OUTPUTS_STATE, count=1, slave=slave_id)
|
||||
hr_sys = client.read_holding_registers(REG_HOLDING_WATCHDOG_TIMEOUT_S, count=1, slave=slave_id)
|
||||
@@ -109,9 +110,11 @@ def poll_status(slave_id, interval):
|
||||
fw_minor = ir_sys.registers[0] & 0xFF
|
||||
fw_patch = ir_sys.registers[1]
|
||||
uptime_seconds = (ir_sys.registers[4] << 16) | ir_sys.registers[3]
|
||||
supply_voltage_mv = ir_sys.registers[5]
|
||||
new_data["firmware"] = f"v{fw_major}.{fw_minor}.{fw_patch}"
|
||||
new_data["device_status"] = "OK" if ir_sys.registers[2] == 0 else "ERROR"
|
||||
new_data["uptime"] = format_uptime(uptime_seconds)
|
||||
new_data["supply_voltage"] = f"{supply_voltage_mv / 1000.0:.2f} V"
|
||||
new_data["watchdog"] = f"{hr_sys.registers[0]}s"
|
||||
new_data["error"] = None # Clear any previous error on successful read
|
||||
reconnect_attempts = 0 # Reset attempts on successful communication
|
||||
@@ -302,6 +305,7 @@ def main_menu(stdscr, slave_id):
|
||||
stdscr.addstr(1, col4, "Firmware:", bold); stdscr.addstr(1, col4 + 14, str(current_data.get('firmware', 'N/A')), normal)
|
||||
stdscr.addstr(2, col4, "Uptime:", bold); stdscr.addstr(2, col4 + 14, str(current_data.get('uptime', 'N/A')), normal)
|
||||
stdscr.addstr(3, col4, "Dev. Status:", bold); stdscr.addstr(3, col4 + 14, str(current_data.get('device_status', 'N/A')), normal)
|
||||
stdscr.addstr(4, col4, "Supply V:", bold); stdscr.addstr(4, col4 + 14, str(current_data.get('supply_voltage', 'N/A')), normal)
|
||||
stdscr.addstr(5, 0, "─" * (w - 1), normal)
|
||||
for idx, row in enumerate(menu):
|
||||
draw_button(stdscr, h // 2 - len(menu) + (idx * 2), w // 2 - len(row) // 2, row, idx == current_row_idx)
|
||||
|
||||
Reference in New Issue
Block a user