diff --git a/docs/modbus-registers.de.md b/docs/modbus-registers.de.md index 9349d37..40aec7d 100644 --- a/docs/modbus-registers.de.md +++ b/docs/modbus-registers.de.md @@ -51,6 +51,8 @@ Alle Register sind in einer einzigen, durchgehenden Liste pro Register-Typ (`Inp | **0x0002** | `MAX_CLOSING_TIME_S` | Ventil | Sicherheits-Timeout in Sekunden für den Schliessen-Vorgang. | | **0x0003** | `END_CURRENT_THRESHOLD_OPEN_MA` | Ventil | Minimaler Stromschwellenwert in mA zur Endlagenerkennung beim Öffnen. | | **0x0004** | `END_CURRENT_THRESHOLD_CLOSE_MA` | Ventil | Minimaler Stromschwellenwert in mA zur Endlagenerkennung beim Schliessen. | +| **0x0005** | `OBSTACLE_CURRENT_THRESHOLD_OPEN_MA` | Ventil | Stromschwellenwert in mA zur Hinderniserkennung beim Öffnen. | +| **0x0006** | `OBSTACLE_CURRENT_THRESHOLD_CLOSE_MA` | Ventil | Stromschwellenwert in mA zur Hinderniserkennung beim Schliessen. | | **0x0010** | `DIGITAL_OUTPUTS_STATE` | Ausgänge | Bitmaske zum Lesen und Schreiben der Ausgänge. Bit 0: Ausgang 1, Bit 1: Ausgang 2. `1`=AN, `0`=AUS. | | **0x00F0** | `WATCHDOG_TIMEOUT_S` | System | Timeout des Fail-Safe-Watchdogs in Sekunden. `0`=Deaktiviert. | | **0x00F1** | `DEVICE_RESET` | System | Schreibt `1` um das Gerät neu zu starten. | diff --git a/software/include/lib/modbus_server.h b/software/include/lib/modbus_server.h index 2bd2504..485a3f9 100644 --- a/software/include/lib/modbus_server.h +++ b/software/include/lib/modbus_server.h @@ -89,9 +89,21 @@ enum { */ REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002, /** - * @brief Minimum current threshold in mA for end-position detection. + * @brief Minimum current threshold in mA for end-position detection during opening. */ - REG_HOLDING_VALVE_END_CURRENT_THRESHOLD_MA = 0x0003, + REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA = 0x0003, + /** + * @brief Minimum current threshold in mA for end-position detection during closing. + */ + REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA = 0x0004, + /** + * @brief Obstacle current threshold in mA for opening. + */ + REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_OPEN_MA = 0x0005, + /** + * @brief Obstacle current threshold in mA for closing. + */ + REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_CLOSE_MA = 0x0006, /** * @brief Bitmask for reading and writing digital outputs. Bit 0: Output 1, * Bit 1: Output 2. 1=ON, 0=OFF. diff --git a/software/include/lib/valve.h b/software/include/lib/valve.h index d7ce6df..10eb4b8 100644 --- a/software/include/lib/valve.h +++ b/software/include/lib/valve.h @@ -32,7 +32,8 @@ enum valve_movement { VALVE_MOVEMENT_IDLE, /**< The valve is not moving. */ VALVE_MOVEMENT_OPENING, /**< The valve is currently opening. */ VALVE_MOVEMENT_CLOSING, /**< The valve is currently closing. */ - VALVE_MOVEMENT_ERROR /**< An error occurred during movement. */ + VALVE_MOVEMENT_ERROR, /**< An error occurred during movement. */ + VALVE_MOVEMENT_OBSTACLE /**< An obstacle was detected during movement. */ }; /** @@ -113,6 +114,20 @@ void valve_set_end_current_threshold_open(uint16_t current_ma); */ void valve_set_end_current_threshold_close(uint16_t current_ma); +/** + * @brief Sets the current threshold for obstacle detection during opening. + * + * @param current_ma The current threshold in milliamps. + */ +void valve_set_obstacle_current_threshold_open(uint16_t current_ma); + +/** + * @brief Sets the current threshold for obstacle detection during closing. + * + * @param current_ma The current threshold in milliamps. + */ +void valve_set_obstacle_current_threshold_close(uint16_t current_ma); + /** * @brief Gets the current threshold for end-position detection during opening. * @@ -141,6 +156,20 @@ uint16_t valve_get_max_open_time(void); */ uint16_t valve_get_max_close_time(void); +/** + * @brief Gets the current threshold for obstacle detection during opening. + * + * @return The current threshold in milliamps. + */ +uint16_t valve_get_obstacle_current_threshold_open(void); + +/** + * @brief Gets the current threshold for obstacle detection during closing. + * + * @return The current threshold in milliamps. + */ +uint16_t valve_get_obstacle_current_threshold_close(void); + /** * @brief Gets the current drawn by the valve motor during opening. * diff --git a/software/lib/shell_valve/shell_valve.c b/software/lib/shell_valve/shell_valve.c index 24d5d90..12e16ad 100644 --- a/software/lib/shell_valve/shell_valve.c +++ b/software/lib/shell_valve/shell_valve.c @@ -55,6 +55,32 @@ static int cmd_valve_set_end_curr_close(const struct shell *sh, size_t argc, cha return 0; } +static int cmd_valve_set_obs_curr_open(const struct shell *sh, size_t argc, char **argv) +{ + if (argc != 2) { + shell_print(sh, "Usage: valve set_obs_curr_open "); + return -EINVAL; + } + + uint16_t current_ma = (uint16_t)atoi(argv[1]); + valve_set_obstacle_current_threshold_open(current_ma); + shell_print(sh, "Obstacle current threshold (open) set to %u mA.", current_ma); + return 0; +} + +static int cmd_valve_set_obs_curr_close(const struct shell *sh, size_t argc, char **argv) +{ + if (argc != 2) { + shell_print(sh, "Usage: valve set_obs_curr_close "); + return -EINVAL; + } + + uint16_t current_ma = (uint16_t)atoi(argv[1]); + valve_set_obstacle_current_threshold_close(current_ma); + shell_print(sh, "Obstacle current threshold (close) set to %u mA.", current_ma); + return 0; +} + static int cmd_valve_show(const struct shell *sh, size_t argc, char **argv) { const int label_width = 30; @@ -72,6 +98,16 @@ static int cmd_valve_show(const struct shell *sh, size_t argc, char **argv) label_width, "End Current Threshold (Close):", valve_get_end_current_threshold_close()); + shell_print(sh, + "%*s %u mA", + label_width, + "Obstacle Current Threshold (Open):", + valve_get_obstacle_current_threshold_open()); + shell_print(sh, + "%*s %u mA", + label_width, + "Obstacle Current Threshold (Close):", + valve_get_obstacle_current_threshold_close()); return 0; } @@ -86,6 +122,14 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_valve_settings, NULL, "Set end current threshold for closing (mA)", cmd_valve_set_end_curr_close), + SHELL_CMD(set_obs_curr_open, + NULL, + "Set obstacle current threshold for opening (mA)", + cmd_valve_set_obs_curr_open), + SHELL_CMD(set_obs_curr_close, + NULL, + "Set obstacle current threshold for closing (mA)", + cmd_valve_set_obs_curr_close), SHELL_CMD(show, NULL, "Show valve configuration", cmd_valve_show), SHELL_SUBCMD_SET_END); diff --git a/software/lib/valve/valve.c b/software/lib/valve/valve.c index 0ea21ae..e6cba8f 100644 --- a/software/lib/valve/valve.c +++ b/software/lib/valve/valve.c @@ -29,6 +29,8 @@ static uint16_t max_opening_time_s = 10; static uint16_t max_closing_time_s = 10; static uint16_t end_current_threshold_open_ma = 10; // Default value for open static uint16_t end_current_threshold_close_ma = 10; // Default value for close +static uint16_t obstacle_current_threshold_open_ma = 600; // Default value for open +static uint16_t obstacle_current_threshold_close_ma = 600; // Default value for close static struct k_work_delayable valve_work; // Work item for scheduling valve movement timeouts static struct k_timer movement_timer; @@ -47,6 +49,11 @@ static void valve_work_handler(struct k_work *work) if (current_movement == VALVE_MOVEMENT_OPENING) { vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_OPEN, ¤t_ma); + if (current_ma > obstacle_current_threshold_open_ma) { + LOG_ERR("Obstacle detected during opening! Current: %d mA", current_ma); + current_movement = VALVE_MOVEMENT_OBSTACLE; + goto work_handler_finish; + } if (current_ma > end_current_threshold_open_ma) { k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); return; @@ -54,6 +61,11 @@ static void valve_work_handler(struct k_work *work) LOG_INF("Valve finished opening"); } else if (current_movement == VALVE_MOVEMENT_CLOSING) { vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, ¤t_ma); + if (current_ma > obstacle_current_threshold_close_ma) { + LOG_ERR("Obstacle detected during closing! Current: %d mA", current_ma); + current_movement = VALVE_MOVEMENT_OBSTACLE; + goto work_handler_finish; + } if (current_ma > end_current_threshold_close_ma) { k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); return; @@ -63,6 +75,7 @@ static void valve_work_handler(struct k_work *work) } current_movement = VALVE_MOVEMENT_IDLE; +work_handler_finish: // Reset the movement timer k_timer_stop(&movement_timer); @@ -104,13 +117,21 @@ int valve_init(void) settings_load_one("valve/end_current_threshold_close", &end_current_threshold_close_ma, sizeof(end_current_threshold_close_ma)); + settings_load_one("valve/obstacle_current_threshold_open", + &obstacle_current_threshold_open_ma, + sizeof(obstacle_current_threshold_open_ma)); + settings_load_one("valve/obstacle_current_threshold_close", + &obstacle_current_threshold_close_ma, + sizeof(obstacle_current_threshold_close_ma)); LOG_INF("Valve initialized: max_open=%us, max_close=%us, end_curr_open=%umA, " - "end_curr_close=%umA", + "end_curr_close=%umA, obs_curr_open=%umA, obs_curr_close=%umA", max_opening_time_s, max_closing_time_s, end_current_threshold_open_ma, - end_current_threshold_close_ma); + end_current_threshold_close_ma, + obstacle_current_threshold_open_ma, + obstacle_current_threshold_close_ma); valve_close(); return 0; } @@ -190,6 +211,22 @@ void valve_set_end_current_threshold_close(uint16_t current_ma) sizeof(end_current_threshold_close_ma)); } +void valve_set_obstacle_current_threshold_open(uint16_t current_ma) +{ + obstacle_current_threshold_open_ma = current_ma; + settings_save_one("valve/obstacle_current_threshold_open", + &obstacle_current_threshold_open_ma, + sizeof(obstacle_current_threshold_open_ma)); +} + +void valve_set_obstacle_current_threshold_close(uint16_t current_ma) +{ + obstacle_current_threshold_close_ma = current_ma; + settings_save_one("valve/obstacle_current_threshold_close", + &obstacle_current_threshold_close_ma, + sizeof(obstacle_current_threshold_close_ma)); +} + uint16_t valve_get_max_open_time(void) { return max_opening_time_s; @@ -209,6 +246,16 @@ uint16_t valve_get_end_current_threshold_close(void) return end_current_threshold_close_ma; } +uint16_t valve_get_obstacle_current_threshold_open(void) +{ + return obstacle_current_threshold_open_ma; +} + +uint16_t valve_get_obstacle_current_threshold_close(void) +{ + return obstacle_current_threshold_close_ma; +} + int32_t valve_get_opening_current(void) { int32_t current; diff --git a/software/tools/modbus_tool/modbus_tool.py b/software/tools/modbus_tool/modbus_tool.py index e0ffe6c..8693331 100755 --- a/software/tools/modbus_tool/modbus_tool.py +++ b/software/tools/modbus_tool/modbus_tool.py @@ -27,6 +27,8 @@ REG_HOLDING_MAX_OPENING_TIME_S = 0x0001 REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002 REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA = 0x0003 REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA = 0x0004 +REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_OPEN_MA = 0x0005 +REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_CLOSE_MA = 0x0006 REG_HOLDING_DIGITAL_OUTPUTS_STATE = 0x0010 REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0 REG_HOLDING_DEVICE_RESET = 0x00F1 @@ -90,7 +92,7 @@ def poll_status(slave_id, interval): ir_current = client.read_input_registers(REG_INPUT_MOTOR_OPEN_CURRENT_MA, 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=6, slave=slave_id) - hr_valve = client.read_holding_registers(REG_HOLDING_MAX_OPENING_TIME_S, count=4, slave=slave_id) + hr_valve = client.read_holding_registers(REG_HOLDING_MAX_OPENING_TIME_S, count=6, 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) @@ -99,7 +101,7 @@ def poll_status(slave_id, interval): raise ModbusException(str(res)) valve_state_raw = ir_valve.registers[0] - movement_map = {0: "Idle", 1: "Opening", 2: "Closing", 3: "Error"} + movement_map = {0: "Idle", 1: "Opening", 2: "Closing", 3: "Error", 4: "Obstacle"} state_map = {0: "Closed", 1: "Open"} new_data["movement"] = movement_map.get(valve_state_raw >> 8, 'Unknown') new_data["state"] = state_map.get(valve_state_raw & 0xFF, 'Unknown') @@ -109,6 +111,8 @@ def poll_status(slave_id, interval): new_data["close_time"] = f"{hr_valve.registers[1]}s" new_data["end_curr_open"] = f"{hr_valve.registers[2]}mA" new_data["end_curr_close"] = f"{hr_valve.registers[3]}mA" + new_data["obs_curr_open"] = f"{hr_valve.registers[4]}mA" + new_data["obs_curr_close"] = f"{hr_valve.registers[5]}mA" new_data["digital_inputs"] = f"0x{ir_dig.registers[0]:04X}" new_data["button_events"] = f"0x{ir_dig.registers[1]:04X}" new_data["digital_outputs"] = f"0x{hr_dig.registers[0]:04X}" @@ -234,7 +238,7 @@ def main_menu(stdscr, slave_id): stdscr.bkgd(' ', curses.color_pair(1)) menu = ["Open Valve", "Close Valve", "Stop Valve", "Toggle Output 1", "Toggle Output 2", "Settings", "Reset Node", "Firmware Update", "Exit"] - settings_menu = ["Set Max Open Time", "Set Max Close Time", "Set End Current Open", "Set End Current Close", "Set Watchdog", "Back"] + settings_menu = ["Set Max Open Time", "Set Max Close Time", "Set End Current Open", "Set End Current Close", "Set Obstacle Current Open", "Set Obstacle Current Close", "Set Watchdog", "Back"] current_menu = menu current_row_idx = 0 message, message_time = "", 0 @@ -285,6 +289,10 @@ def main_menu(stdscr, slave_id): input_mode, input_prompt, input_target_reg = True, "Enter End Current Threshold Open (mA): ", REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA elif selected_option == "Set End Current Close": input_mode, input_prompt, input_target_reg = True, "Enter End Current Threshold Close (mA): ", REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA + elif selected_option == "Set Obstacle Current Open": + input_mode, input_prompt, input_target_reg = True, "Enter Obstacle Current Threshold Open (mA): ", REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_OPEN_MA + elif selected_option == "Set Obstacle Current Close": + input_mode, input_prompt, input_target_reg = True, "Enter Obstacle Current Threshold Close (mA): ", REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_CLOSE_MA elif selected_option == "Set Watchdog": input_mode, input_prompt, input_target_reg = True, "Enter Watchdog Timeout (s): ", REG_HOLDING_WATCHDOG_TIMEOUT_S elif selected_option == "Reset Node": @@ -324,6 +332,8 @@ def main_menu(stdscr, slave_id): stdscr.addstr(3, col3, "Watchdog:", bold); stdscr.addstr(3, col3 + 16, str(current_data.get('watchdog', 'N/A')), normal) stdscr.addstr(4, col3, "End Curr Open:", bold); stdscr.addstr(4, col3 + 16, str(current_data.get('end_curr_open', 'N/A')), normal) stdscr.addstr(5, col3, "End Curr Close:", bold); stdscr.addstr(5, col3 + 16, str(current_data.get('end_curr_close', 'N/A')), normal) + stdscr.addstr(6, col3, "Obs Curr Open:", bold); stdscr.addstr(6, col3 + 16, str(current_data.get('obs_curr_open', 'N/A')), normal) + stdscr.addstr(7, col3, "Obs Curr Close:", bold); stdscr.addstr(7, col3 + 16, str(current_data.get('obs_curr_close', 'N/A')), normal) 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)