Skip to content

Commit 2809beb

Browse files
authored
feat(hepa-uv): Add a task to handle the UV Ballast state based on external input (#744)
- Added a UVTask that listens for irq interrupts and turns on/off the UV Ballast accordingly - Turn OFF the HEPA fan and UV Ballast on startup
1 parent d0b3683 commit 2809beb

File tree

5 files changed

+167
-7
lines changed

5 files changed

+167
-7
lines changed

hepa-uv/core/tasks.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ static auto queues = hepauv_tasks::QueueClient{can::ids::NodeId::hepa_uv};
1717
static auto hepa_task_builder =
1818
freertos_task::TaskStarter<512, hepa_task::HepaTask>{};
1919

20+
static auto uv_task_builder =
21+
freertos_task::TaskStarter<512, uv_task::UVTask>{};
22+
2023
/**
2124
* Start hepa_uv tasks.
2225
*/
@@ -27,22 +30,29 @@ void hepauv_tasks::start_tasks(
2730
can_task::start_reader(can_bus);
2831

2932
auto& hepa_task = hepa_task_builder.start(5, "hepa_fan", gpio_drive_pins);
33+
auto& uv_task = uv_task_builder.start(5, "uv_ballast", gpio_drive_pins);
3034

3135
tasks.hepa_task_handler = &hepa_task;
36+
tasks.uv_task_handler = &uv_task;
3237
tasks.can_writer = &can_writer;
3338

3439
queues.set_queue(&can_writer.get_queue());
3540
queues.hepa_queue = &hepa_task.get_queue();
41+
queues.uv_queue = &uv_task.get_queue();
3642
}
3743

3844
hepauv_tasks::QueueClient::QueueClient(can::ids::NodeId this_fw)
3945
: can::message_writer::MessageWriter{this_fw} {}
4046

41-
void hepauv_tasks::QueueClient::send_interrupt_message(
47+
void hepauv_tasks::QueueClient::send_hepa_message(
4248
const hepa_task::TaskMessage& m) {
4349
hepa_queue->try_write(m);
4450
}
4551

52+
void hepauv_tasks::QueueClient::send_uv_message(const uv_task::TaskMessage& m) {
53+
uv_queue->try_write(m);
54+
}
55+
4656
/**
4757
* Access to the tasks singleton
4858
* @return

hepa-uv/firmware/main_rev1.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,20 +91,24 @@ static auto gpio_drive_pins = gpio_drive_hardware::GpioDrivePins{
9191
.pin = UV_ON_OFF_MCU_PIN,
9292
.active_setting = UV_ON_OFF_AS}};
9393

94-
static auto& hepa_queue_client = hepauv_tasks::get_main_queues();
94+
static auto& hepauv_queues = hepauv_tasks::get_main_queues();
9595

9696
extern "C" void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
9797
switch (GPIO_Pin) {
9898
case DOOR_OPEN_MCU_PIN:
9999
case REED_SW_MCU_PIN:
100100
case HEPA_NO_MCU_PIN:
101101
case UV_NO_MCU_PIN:
102-
if (hepa_queue_client.hepa_queue != nullptr) {
103-
static_cast<void>(hepa_queue_client.hepa_queue->try_write_isr(
102+
if (hepauv_queues.hepa_queue != nullptr) {
103+
static_cast<void>(hepauv_queues.hepa_queue->try_write_isr(
104+
interrupt_task_messages::GPIOInterruptChanged{
105+
.pin = GPIO_Pin}));
106+
}
107+
if (hepauv_queues.uv_queue != nullptr) {
108+
static_cast<void>(hepauv_queues.uv_queue->try_write_isr(
104109
interrupt_task_messages::GPIOInterruptChanged{
105110
.pin = GPIO_Pin}));
106111
}
107-
// send to uv queue here
108112
break;
109113
default:
110114
break;

include/hepa-uv/core/hepa_task.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ using TaskMessage = interrupt_task_messages::TaskMessage;
1515
class HepaMessageHandler {
1616
public:
1717
explicit HepaMessageHandler(gpio_drive_hardware::GpioDrivePins &drive_pins)
18-
: drive_pins{drive_pins} {}
18+
: drive_pins{drive_pins} {
19+
// get current state
20+
hepa_push_button = gpio::is_set(drive_pins.hepa_push_button);
21+
// turn off the HEPA fan
22+
gpio::reset(drive_pins.hepa_on_off);
23+
}
1924
HepaMessageHandler(const HepaMessageHandler &) = delete;
2025
HepaMessageHandler(const HepaMessageHandler &&) = delete;
2126
auto operator=(const HepaMessageHandler &) -> HepaMessageHandler & = delete;

include/hepa-uv/core/tasks.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "can/core/message_writer.hpp"
33
#include "common/core/freertos_timer.hpp"
44
#include "hepa-uv/core/hepa_task.hpp"
5+
#include "hepa-uv/core/uv_task.hpp"
56
#include "hepa-uv/firmware/gpio_drive_hardware.hpp"
67

78
namespace hepauv_tasks {
@@ -18,10 +19,14 @@ void start_tasks(can::bus::CanBus& can_bus,
1819
struct QueueClient : can::message_writer::MessageWriter {
1920
QueueClient(can::ids::NodeId this_fw);
2021

21-
void send_interrupt_message(const hepa_task::TaskMessage& m);
22+
void send_hepa_message(const hepa_task::TaskMessage& m);
23+
void send_uv_message(const hepa_task::TaskMessage& m);
2224

2325
freertos_message_queue::FreeRTOSMessageQueue<hepa_task::TaskMessage>*
2426
hepa_queue{nullptr};
27+
28+
freertos_message_queue::FreeRTOSMessageQueue<uv_task::TaskMessage>*
29+
uv_queue{nullptr};
2530
};
2631

2732
/**
@@ -33,6 +38,9 @@ struct AllTask {
3338

3439
hepa_task::HepaTask<freertos_message_queue::FreeRTOSMessageQueue>*
3540
hepa_task_handler{nullptr};
41+
42+
uv_task::UVTask<freertos_message_queue::FreeRTOSMessageQueue>*
43+
uv_task_handler{nullptr};
3644
};
3745

3846
/**

include/hepa-uv/core/uv_task.hpp

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#pragma once
2+
3+
#include "can/core/ids.hpp"
4+
#include "common/core/bit_utils.hpp"
5+
#include "common/core/logging.h"
6+
#include "common/core/message_queue.hpp"
7+
#include "hepa-uv/firmware/gpio_drive_hardware.hpp"
8+
#include "messages.hpp"
9+
#include "ot_utils/freertos/freertos_timer.hpp"
10+
11+
namespace uv_task {
12+
13+
// How long to keep the UV light on in ms.
14+
static constexpr uint32_t DELAY_MS = 1000 * 60 * 15; // 15 minutes
15+
16+
using TaskMessage = interrupt_task_messages::TaskMessage;
17+
18+
class UVMessageHandler {
19+
public:
20+
explicit UVMessageHandler(gpio_drive_hardware::GpioDrivePins &drive_pins)
21+
: drive_pins{drive_pins},
22+
_timer(
23+
"UVTask", [ThisPtr = this] { ThisPtr->timer_callback(); },
24+
DELAY_MS) {
25+
// get current state
26+
uv_push_button = gpio::is_set(drive_pins.uv_push_button);
27+
door_closed = gpio::is_set(drive_pins.door_open);
28+
reed_switch_set = gpio::is_set(drive_pins.reed_switch);
29+
// turn off UV Ballast
30+
gpio::reset(drive_pins.uv_on_off);
31+
}
32+
UVMessageHandler(const UVMessageHandler &) = delete;
33+
UVMessageHandler(const UVMessageHandler &&) = delete;
34+
auto operator=(const UVMessageHandler &) -> UVMessageHandler & = delete;
35+
auto operator=(const UVMessageHandler &&) -> UVMessageHandler && = delete;
36+
~UVMessageHandler() {}
37+
38+
void handle_message(const TaskMessage &m) {
39+
std::visit([this](auto o) { this->visit(o); }, m);
40+
}
41+
42+
private:
43+
// call back to turn off the UV ballast
44+
auto timer_callback() -> void {
45+
gpio::reset(drive_pins.uv_on_off);
46+
uv_push_button = false;
47+
uv_fan_on = false;
48+
}
49+
50+
void visit(const std::monostate &) {}
51+
52+
// Handle GPIO EXTI Interrupts here
53+
void visit(const interrupt_task_messages::GPIOInterruptChanged &m) {
54+
if (m.pin == drive_pins.hepa_push_button.pin) {
55+
// ignore hepa push button presses
56+
return;
57+
}
58+
59+
// update states
60+
if (m.pin == drive_pins.uv_push_button.pin) {
61+
uv_push_button = !uv_push_button;
62+
} else if (m.pin == drive_pins.door_open.pin) {
63+
door_closed = gpio::is_set(drive_pins.door_open);
64+
} else if (m.pin == drive_pins.reed_switch.pin) {
65+
reed_switch_set = gpio::is_set(drive_pins.reed_switch);
66+
}
67+
68+
// reset push button state if the door is opened or the reed switch is
69+
// not set
70+
if (!door_closed || !reed_switch_set) uv_push_button = false;
71+
72+
// set the UV Ballast
73+
if (door_closed && reed_switch_set && uv_push_button) {
74+
gpio::set(drive_pins.uv_on_off);
75+
// start the turn off timer
76+
if (_timer.is_running()) _timer.stop();
77+
_timer.start();
78+
uv_fan_on = true;
79+
} else {
80+
gpio::reset(drive_pins.uv_on_off);
81+
if (_timer.is_running()) _timer.stop();
82+
uv_fan_on = false;
83+
}
84+
85+
// TODO: send CAN message to host
86+
}
87+
88+
// state tracking variables
89+
bool door_closed = false;
90+
bool reed_switch_set = false;
91+
bool uv_push_button = false;
92+
bool uv_fan_on = false;
93+
94+
gpio_drive_hardware::GpioDrivePins &drive_pins;
95+
ot_utils::freertos_timer::FreeRTOSTimer _timer;
96+
};
97+
98+
/**
99+
* The task type.
100+
*/
101+
template <template <class> class QueueImpl>
102+
requires MessageQueue<QueueImpl<TaskMessage>, TaskMessage>
103+
class UVTask {
104+
public:
105+
using Messages = TaskMessage;
106+
using QueueType = QueueImpl<TaskMessage>;
107+
UVTask(QueueType &queue) : queue{queue} {}
108+
UVTask(const UVTask &c) = delete;
109+
UVTask(const UVTask &&c) = delete;
110+
auto operator=(const UVTask &c) = delete;
111+
auto operator=(const UVTask &&c) = delete;
112+
~UVTask() = default;
113+
114+
/**
115+
* Task entry point.
116+
*/
117+
[[noreturn]] void operator()(
118+
gpio_drive_hardware::GpioDrivePins *drive_pins) {
119+
auto handler = UVMessageHandler{*drive_pins};
120+
TaskMessage message{};
121+
for (;;) {
122+
if (queue.try_read(&message, queue.max_delay)) {
123+
handler.handle_message(message);
124+
}
125+
}
126+
}
127+
128+
[[nodiscard]] auto get_queue() const -> QueueType & { return queue; }
129+
130+
private:
131+
QueueType &queue;
132+
};
133+
}; // namespace uv_task

0 commit comments

Comments
 (0)