9
9
#include " hepa-uv/core/messages.hpp"
10
10
#include " hepa-uv/firmware/gpio_drive_hardware.hpp"
11
11
#include " hepa-uv/firmware/uv_control_hardware.hpp"
12
+ #include " ot_utils/freertos/freertos_sleep.hpp"
12
13
#include " ot_utils/freertos/freertos_timer.hpp"
13
14
14
15
namespace uv_task {
15
16
16
17
// How long to keep the UV light on in seconds.
17
18
static constexpr uint32_t DELAY_S = 60 * 15 ; // 15 minutes
18
19
static constexpr uint32_t MAX_DELAY_S = 60 * 60 ; // 1hr max timeout
20
+ static constexpr uint32_t DEBOUNCE_MS = 250 ; // button debounce
19
21
20
22
using TaskMessage = uv_task_messages::TaskMessage;
21
23
@@ -33,11 +35,15 @@ class UVMessageHandler {
33
35
can_client{can_client},
34
36
_timer (
35
37
" UVTask" , [ThisPtr = this ] { ThisPtr->timer_callback (); },
36
- DELAY_S * 1000 ) {
38
+ DELAY_S * 1000 ),
39
+ debounce_timer (
40
+ " UVTaskDebounce" , [ThisPtr = this ] { ThisPtr->debounce_cb (); },
41
+ DEBOUNCE_MS) {
37
42
// get current state
38
43
uv_push_button = gpio::is_set (drive_pins.uv_push_button );
39
44
door_closed = gpio::is_set (drive_pins.door_open );
40
45
reed_switch_set = gpio::is_set (drive_pins.reed_switch );
46
+ update_safety_relay_state ();
41
47
// turn off UV Ballast
42
48
gpio::reset (drive_pins.uv_on_off );
43
49
}
@@ -58,6 +64,19 @@ class UVMessageHandler {
58
64
uv_push_button = false ;
59
65
}
60
66
67
+ // callback to debounce the irq signals
68
+ void debounce_cb () {
69
+ debounce_timer.stop ();
70
+ set_uv_light_state (uv_push_button, uv_off_timeout_s);
71
+ }
72
+
73
+ // Helper to update safety relay state
74
+ void update_safety_relay_state () {
75
+ if (drive_pins.safety_relay_active .has_value ())
76
+ safety_relay_active =
77
+ gpio::is_set (drive_pins.safety_relay_active .value ());
78
+ }
79
+
61
80
void visit (const std::monostate &) {}
62
81
63
82
// Handle GPIO EXTI Interrupts here
@@ -67,17 +86,16 @@ class UVMessageHandler {
67
86
return ;
68
87
}
69
88
89
+ // debounce
90
+ if (debounce_timer.is_running ()) return ;
91
+ debounce_timer.start ();
92
+
70
93
// update states
71
- if (m.pin == drive_pins.uv_push_button .pin ) {
94
+ door_closed = gpio::is_set (drive_pins.door_open );
95
+ reed_switch_set = gpio::is_set (drive_pins.reed_switch );
96
+ update_safety_relay_state ();
97
+ if (m.pin == drive_pins.uv_push_button .pin )
72
98
uv_push_button = !uv_push_button;
73
- } else if (m.pin == drive_pins.door_open .pin ) {
74
- door_closed = gpio::is_set (drive_pins.door_open );
75
- } else if (m.pin == drive_pins.reed_switch .pin ) {
76
- reed_switch_set = gpio::is_set (drive_pins.reed_switch );
77
- }
78
-
79
- // Drive the UV light
80
- set_uv_light_state (uv_push_button, uv_off_timeout_s);
81
99
}
82
100
83
101
void visit (const can::messages::SetHepaUVStateRequest &m) {
@@ -89,13 +107,15 @@ class UVMessageHandler {
89
107
}
90
108
91
109
void visit (const can::messages::GetHepaUVStateRequest &m) {
110
+ update_safety_relay_state ();
92
111
uv_current_ma = uv_hardware.get_uv_light_current ();
93
112
auto resp = can::messages::GetHepaUVStateResponse{
94
113
.message_index = m.message_index ,
95
114
.timeout_s = uv_off_timeout_s,
96
115
.uv_light_on = uv_light_on,
97
116
.remaining_time_s = (_timer.get_remaining_time () / 1000 ),
98
- .uv_current_ma = uv_current_ma};
117
+ .uv_current_ma = uv_current_ma,
118
+ .safety_relay_active = safety_relay_active};
99
119
can_client.send_can_message (can::ids::NodeId::host, resp);
100
120
}
101
121
@@ -153,15 +173,40 @@ class UVMessageHandler {
153
173
if (_timer.is_running ()) _timer.stop ();
154
174
}
155
175
156
- // Update the voltage usage of the uv light
176
+ // wait 10ms for safety relay, then update the states
177
+ ot_utils::freertos_sleep::sleep (100 );
157
178
uv_current_ma = uv_hardware.get_uv_light_current ();
179
+ update_safety_relay_state ();
180
+ if (uv_light_on && !safety_relay_active) {
181
+ // we tried to set the uv light, but the relay is not active
182
+ if (_timer.is_running ()) {
183
+ gpio::reset (drive_pins.uv_on_off );
184
+ _timer.stop ();
185
+ led_control_client.send_led_control_message (
186
+ led_control_task_messages::PushButtonLED (UV_BUTTON, 0 , 0 ,
187
+ 50 , 0 ));
188
+ }
189
+ // send error
190
+ auto msg = can::messages::ErrorMessage{
191
+ .message_index = 0 ,
192
+ .severity = can::ids::ErrorSeverity::warning,
193
+ .error_code = can::ids::ErrorCode::safety_relay_inactive,
194
+ };
195
+ can_client.send_can_message (can::ids::NodeId::host, msg);
196
+
197
+ uv_push_button = false ;
198
+ uv_light_on = false ;
199
+ uv_current_ma = 0 ;
200
+ return ;
201
+ }
158
202
159
203
// TODO: send state change CAN message to host
160
204
}
161
205
162
206
// state tracking variables
163
207
bool door_closed = false ;
164
208
bool reed_switch_set = false ;
209
+ bool safety_relay_active = false ;
165
210
bool uv_push_button = false ;
166
211
bool uv_light_on = false ;
167
212
uint32_t uv_off_timeout_s = DELAY_S;
@@ -172,6 +217,7 @@ class UVMessageHandler {
172
217
LEDControlClient &led_control_client;
173
218
CanClient &can_client;
174
219
ot_utils::freertos_timer::FreeRTOSTimer _timer;
220
+ ot_utils::freertos_timer::FreeRTOSTimer debounce_timer;
175
221
};
176
222
177
223
/* *
0 commit comments