|
| 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