Compare commits

...

5 Commits

Author SHA1 Message Date
Eduard Iten d6fb501594 docs: Add Doxygen comment for movement_timeout_handler
feat: Configure valve logging via Kconfig

This commit adds a Kconfig option  to control the log level of the valve library.
- : Added the new Kconfig option.
- : Updated  to use  and adjusted log levels for debug messages.
- : Enabled debug logging for the valve library by setting .

refactor: Adjust k-vcc calibration value for VND7050AJ

Updated the  calibration value in  from 4139 to 3816 for the VND7050AJ driver.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-11 08:07:41 +02:00
Your Name 76d0d0647c feat: Implement obstacle detection for valve movement
Implement obstacle detection for valve movement that stops the motor if the current exceeds a predefined threshold during opening or closing.

- :
    - Added new defines  and  with a default value of 500 mA.
- :
    - Modified  function to compare the measured current with the new obstacle thresholds.
    - If the threshold is exceeded, the valve movement is stopped and the status is set to .

Signed-off-by: Your Name <your.email@example.com>
2025-07-11 01:21:41 +02:00
Eduard Iten 3de42a46c2 refactor: Entferne ungenutzte Funktion
Die Funktion  wurde aus  und  entfernt, da sie im Code nicht mehr verwendet wird. Die Stromwerte werden stattdessen über  und  abgerufen.

Diese Änderung entfernt ungenutzten Code und verbessert die Code-Sauberkeit.
2025-07-11 01:09:46 +02:00
Eduard Iten ddaaa8988d feat: Modbus-Register für Endstromschwellenwerte korrigiert
Behebt ein Problem, bei dem das Python-Tool 0 mA für die Endstromschwellenwerte anzeigte.

Die Zephyr-Anwendung definierte zuvor nur ein einzelnes Modbus-Register für den Endstromschwellenwert, während das Python-Tool separate Register für das Öffnen und Schließen erwartete.

Änderungen:
- :
    -  wurde in  umbenannt.
    -  wurde als neues Register hinzugefügt.
- :
    - Implementierung der Lese- und Schreib-Callbacks für  und  unter Verwendung der entsprechenden -Bibliotheksfunktionen.

Diese Änderungen stellen sicher, dass die Zephyr-Anwendung die Endstromschwellenwerte korrekt über Modbus bereitstellt und das Python-Tool diese Werte nun richtig lesen und schreiben kann.
2025-07-11 01:01:45 +02:00
Eduard Iten b937c52bcc Revert "feat(valve): Implement obstacle detection with configurable thresholds"
This reverts commit 3c2235733b.
2025-07-11 00:35:19 +02:00
10 changed files with 77 additions and 169 deletions

View File

@ -51,8 +51,6 @@ 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. |

View File

@ -15,7 +15,7 @@
fault-reset-gpios = <&gpiob 3 GPIO_ACTIVE_LOW>;
io-channels = <&adc1 1>;
r-sense-ohms = <1500>;
k-vcc = <4139>;
k-vcc = <3816>;
};
};

View File

@ -26,4 +26,4 @@ CONFIG_MODBUS_BUFFER_SIZE=256
# Enable VND7050AJ
CONFIG_VND7050AJ=y
CONFIG_LOG_VALVE_LEVEL=4

View File

@ -89,21 +89,14 @@ enum {
*/
REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002,
/**
* @brief Minimum current threshold in mA for end-position detection during opening.
* @brief Minimum current threshold in mA for end-position detection.
*/
REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA = 0x0003,
/**
* @brief Minimum current threshold in mA for end-position detection during closing.
* @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.

View File

@ -17,6 +17,9 @@
#define VALVE_CHANNEL_CLOSE 1
#define VALVE_ENDPOSITION_CHECK_INTERVAL K_MSEC(100)
#define VALVE_OBSTACLE_THRESHOLD_OPEN_MA 500
#define VALVE_OBSTACLE_THRESHOLD_CLOSE_MA 500
/**
* @brief Represents the static state of the valve (open or closed).
*/
@ -32,8 +35,7 @@ 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_OBSTACLE /**< An obstacle was detected during movement. */
VALVE_MOVEMENT_ERROR /**< An error occurred during movement. */
};
/**
@ -79,13 +81,6 @@ enum valve_state valve_get_state(void);
*/
enum valve_movement valve_get_movement(void);
/**
* @brief Gets the motor current.
*
* @return The motor current in milliamps (currently simulated).
*/
uint16_t valve_get_motor_current(void);
/**
* @brief Sets the maximum time for the valve to open.
*
@ -114,20 +109,6 @@ 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.
*
@ -156,20 +137,6 @@ 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.
*

View File

@ -81,6 +81,12 @@ static int holding_reg_rd(uint16_t addr, uint16_t *reg)
case REG_HOLDING_WATCHDOG_TIMEOUT_S:
*reg = watchdog_timeout_s;
break;
case REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA:
*reg = valve_get_end_current_threshold_open();
break;
case REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA:
*reg = valve_get_end_current_threshold_close();
break;
default:
*reg = 0;
break;
@ -114,6 +120,12 @@ static int holding_reg_wr(uint16_t addr, uint16_t reg)
case REG_HOLDING_MAX_CLOSING_TIME_S:
valve_set_max_close_time(reg);
break;
case REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA:
valve_set_end_current_threshold_open(reg);
break;
case REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA:
valve_set_end_current_threshold_close(reg);
break;
case REG_HOLDING_WATCHDOG_TIMEOUT_S:
watchdog_timeout_s = reg;
if (watchdog_timeout_s > 0) {
@ -176,6 +188,12 @@ static int input_reg_rd(uint16_t addr, uint16_t *reg)
case REG_INPUT_FIRMWARE_VERSION_PATCH:
*reg = APP_PATCHLEVEL;
break;
case REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA:
*reg = valve_get_end_current_threshold_open();
break;
case REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA:
*reg = valve_get_end_current_threshold_close();
break;
default:
*reg = 0;
break;

View File

@ -55,32 +55,6 @@ 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 <milliamps>");
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 <milliamps>");
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;
@ -98,16 +72,6 @@ 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;
}
@ -122,14 +86,6 @@ 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);

View File

@ -3,3 +3,12 @@ config LIB_VALVE
default y
help
Enable the Valve Library.
if LIB_VALVE
config LOG_VALVE_LEVEL
int "Valve Log Level"
default 3
help
Set the log level for the Valve Library.
0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug
endif # LIB_VALVE

View File

@ -21,17 +21,15 @@
#endif
const struct device *vnd7050aj_dev = DEVICE_DT_GET(VND_NODE);
LOG_MODULE_REGISTER(valve, LOG_LEVEL_INF);
LOG_MODULE_REGISTER(valve, CONFIG_LOG_VALVE_LEVEL);
static enum valve_state current_state = VALVE_STATE_OPEN;
static enum valve_movement current_movement = VALVE_MOVEMENT_IDLE;
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 uint16_t end_current_threshold_open_ma = 10;
static uint16_t end_current_threshold_close_ma = 10;
static struct k_work_delayable valve_work;
static struct k_timer movement_timer;
/**
@ -49,33 +47,39 @@ static void valve_work_handler(struct k_work *work)
if (current_movement == VALVE_MOVEMENT_OPENING) {
vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_OPEN, &current_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) {
LOG_DBG("Current load during opening: %d mA", current_ma);
if (current_ma > VALVE_OBSTACLE_THRESHOLD_OPEN_MA) {
LOG_ERR(
"Obstacle detected during opening (current: %d mA), stopping motor.",
current_ma);
current_movement = VALVE_MOVEMENT_ERROR;
valve_stop();
goto work_handler_cleanup;
} else if (current_ma > end_current_threshold_open_ma) {
k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL);
return;
}
LOG_INF("Valve finished opening");
LOG_DBG("Valve finished opening");
} else if (current_movement == VALVE_MOVEMENT_CLOSING) {
vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, &current_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) {
LOG_DBG("Current load during closing: %d mA", current_ma);
if (current_ma > VALVE_OBSTACLE_THRESHOLD_CLOSE_MA) {
LOG_ERR(
"Obstacle detected during closing (current: %d mA), stopping motor.",
current_ma);
current_movement = VALVE_MOVEMENT_ERROR;
valve_stop();
goto work_handler_cleanup;
} else if (current_ma > end_current_threshold_close_ma) {
k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL);
return;
}
current_state = VALVE_STATE_CLOSED;
LOG_INF("Valve finished closing");
LOG_DBG("Valve finished closing");
}
current_movement = VALVE_MOVEMENT_IDLE;
work_handler_finish:
work_handler_cleanup:
// Reset the movement timer
k_timer_stop(&movement_timer);
@ -83,6 +87,15 @@ work_handler_finish:
vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false);
}
/**
* @brief Timer handler for valve movement timeouts.
*
* This function is called when the maximum allowed time for valve movement
* (opening or closing) has been reached. It stops the valve motor, cancels
* any pending end-position checks, and sets the movement status to error.
*
* @param timer Pointer to the k_timer instance that expired.
*/
void movement_timeout_handler(struct k_timer *timer)
{
// Stop the end position check if the timer expires
@ -117,27 +130,20 @@ 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, obs_curr_open=%umA, obs_curr_close=%umA",
"end_curr_close=%umA",
max_opening_time_s,
max_closing_time_s,
end_current_threshold_open_ma,
end_current_threshold_close_ma,
obstacle_current_threshold_open_ma,
obstacle_current_threshold_close_ma);
end_current_threshold_close_ma);
valve_close();
return 0;
}
void valve_open(void)
{
LOG_DBG("Opening valve");
vnd7050aj_reset_fault(vnd7050aj_dev);
vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false);
vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, true);
@ -152,6 +158,7 @@ void valve_open(void)
void valve_close(void)
{
LOG_DBG("Closing valve");
vnd7050aj_reset_fault(vnd7050aj_dev);
vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, false);
vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, true);
@ -179,10 +186,6 @@ 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)
{
@ -211,22 +214,6 @@ 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;
@ -246,16 +233,6 @@ 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;

View File

@ -27,8 +27,6 @@ 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
@ -92,7 +90,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=6, slave=slave_id)
hr_valve = client.read_holding_registers(REG_HOLDING_MAX_OPENING_TIME_S, count=4, 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)
@ -101,7 +99,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", 4: "Obstacle"}
movement_map = {0: "Idle", 1: "Opening", 2: "Closing", 3: "Error"}
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')
@ -111,8 +109,6 @@ 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}"
@ -238,7 +234,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 Obstacle Current Open", "Set Obstacle Current Close", "Set Watchdog", "Back"]
settings_menu = ["Set Max Open Time", "Set Max Close Time", "Set End Current Open", "Set End Current Close", "Set Watchdog", "Back"]
current_menu = menu
current_row_idx = 0
message, message_time = "", 0
@ -289,10 +285,6 @@ 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":
@ -332,8 +324,6 @@ 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)