Skip to content

Commit

Permalink
feat(hepa-uv): Add a task to handle the UV Ballast state based on ext…
Browse files Browse the repository at this point in the history
…ernal 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
  • Loading branch information
vegano1 authored Jan 18, 2024
1 parent d0b3683 commit 2809beb
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 7 deletions.
12 changes: 11 additions & 1 deletion hepa-uv/core/tasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ static auto queues = hepauv_tasks::QueueClient{can::ids::NodeId::hepa_uv};
static auto hepa_task_builder =
freertos_task::TaskStarter<512, hepa_task::HepaTask>{};

static auto uv_task_builder =
freertos_task::TaskStarter<512, uv_task::UVTask>{};

/**
* Start hepa_uv tasks.
*/
Expand All @@ -27,22 +30,29 @@ void hepauv_tasks::start_tasks(
can_task::start_reader(can_bus);

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

tasks.hepa_task_handler = &hepa_task;
tasks.uv_task_handler = &uv_task;
tasks.can_writer = &can_writer;

queues.set_queue(&can_writer.get_queue());
queues.hepa_queue = &hepa_task.get_queue();
queues.uv_queue = &uv_task.get_queue();
}

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

void hepauv_tasks::QueueClient::send_interrupt_message(
void hepauv_tasks::QueueClient::send_hepa_message(
const hepa_task::TaskMessage& m) {
hepa_queue->try_write(m);
}

void hepauv_tasks::QueueClient::send_uv_message(const uv_task::TaskMessage& m) {
uv_queue->try_write(m);
}

/**
* Access to the tasks singleton
* @return
Expand Down
12 changes: 8 additions & 4 deletions hepa-uv/firmware/main_rev1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,24 @@ static auto gpio_drive_pins = gpio_drive_hardware::GpioDrivePins{
.pin = UV_ON_OFF_MCU_PIN,
.active_setting = UV_ON_OFF_AS}};

static auto& hepa_queue_client = hepauv_tasks::get_main_queues();
static auto& hepauv_queues = hepauv_tasks::get_main_queues();

extern "C" void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
switch (GPIO_Pin) {
case DOOR_OPEN_MCU_PIN:
case REED_SW_MCU_PIN:
case HEPA_NO_MCU_PIN:
case UV_NO_MCU_PIN:
if (hepa_queue_client.hepa_queue != nullptr) {
static_cast<void>(hepa_queue_client.hepa_queue->try_write_isr(
if (hepauv_queues.hepa_queue != nullptr) {
static_cast<void>(hepauv_queues.hepa_queue->try_write_isr(
interrupt_task_messages::GPIOInterruptChanged{
.pin = GPIO_Pin}));
}
if (hepauv_queues.uv_queue != nullptr) {
static_cast<void>(hepauv_queues.uv_queue->try_write_isr(
interrupt_task_messages::GPIOInterruptChanged{
.pin = GPIO_Pin}));
}
// send to uv queue here
break;
default:
break;
Expand Down
7 changes: 6 additions & 1 deletion include/hepa-uv/core/hepa_task.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ using TaskMessage = interrupt_task_messages::TaskMessage;
class HepaMessageHandler {
public:
explicit HepaMessageHandler(gpio_drive_hardware::GpioDrivePins &drive_pins)
: drive_pins{drive_pins} {}
: drive_pins{drive_pins} {
// get current state
hepa_push_button = gpio::is_set(drive_pins.hepa_push_button);
// turn off the HEPA fan
gpio::reset(drive_pins.hepa_on_off);
}
HepaMessageHandler(const HepaMessageHandler &) = delete;
HepaMessageHandler(const HepaMessageHandler &&) = delete;
auto operator=(const HepaMessageHandler &) -> HepaMessageHandler & = delete;
Expand Down
10 changes: 9 additions & 1 deletion include/hepa-uv/core/tasks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "can/core/message_writer.hpp"
#include "common/core/freertos_timer.hpp"
#include "hepa-uv/core/hepa_task.hpp"
#include "hepa-uv/core/uv_task.hpp"
#include "hepa-uv/firmware/gpio_drive_hardware.hpp"

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

void send_interrupt_message(const hepa_task::TaskMessage& m);
void send_hepa_message(const hepa_task::TaskMessage& m);
void send_uv_message(const hepa_task::TaskMessage& m);

freertos_message_queue::FreeRTOSMessageQueue<hepa_task::TaskMessage>*
hepa_queue{nullptr};

freertos_message_queue::FreeRTOSMessageQueue<uv_task::TaskMessage>*
uv_queue{nullptr};
};

/**
Expand All @@ -33,6 +38,9 @@ struct AllTask {

hepa_task::HepaTask<freertos_message_queue::FreeRTOSMessageQueue>*
hepa_task_handler{nullptr};

uv_task::UVTask<freertos_message_queue::FreeRTOSMessageQueue>*
uv_task_handler{nullptr};
};

/**
Expand Down
133 changes: 133 additions & 0 deletions include/hepa-uv/core/uv_task.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#pragma once

#include "can/core/ids.hpp"
#include "common/core/bit_utils.hpp"
#include "common/core/logging.h"
#include "common/core/message_queue.hpp"
#include "hepa-uv/firmware/gpio_drive_hardware.hpp"
#include "messages.hpp"
#include "ot_utils/freertos/freertos_timer.hpp"

namespace uv_task {

// How long to keep the UV light on in ms.
static constexpr uint32_t DELAY_MS = 1000 * 60 * 15; // 15 minutes

using TaskMessage = interrupt_task_messages::TaskMessage;

class UVMessageHandler {
public:
explicit UVMessageHandler(gpio_drive_hardware::GpioDrivePins &drive_pins)
: drive_pins{drive_pins},
_timer(
"UVTask", [ThisPtr = this] { ThisPtr->timer_callback(); },
DELAY_MS) {
// get current state
uv_push_button = gpio::is_set(drive_pins.uv_push_button);
door_closed = gpio::is_set(drive_pins.door_open);
reed_switch_set = gpio::is_set(drive_pins.reed_switch);
// turn off UV Ballast
gpio::reset(drive_pins.uv_on_off);
}
UVMessageHandler(const UVMessageHandler &) = delete;
UVMessageHandler(const UVMessageHandler &&) = delete;
auto operator=(const UVMessageHandler &) -> UVMessageHandler & = delete;
auto operator=(const UVMessageHandler &&) -> UVMessageHandler && = delete;
~UVMessageHandler() {}

void handle_message(const TaskMessage &m) {
std::visit([this](auto o) { this->visit(o); }, m);
}

private:
// call back to turn off the UV ballast
auto timer_callback() -> void {
gpio::reset(drive_pins.uv_on_off);
uv_push_button = false;
uv_fan_on = false;
}

void visit(const std::monostate &) {}

// Handle GPIO EXTI Interrupts here
void visit(const interrupt_task_messages::GPIOInterruptChanged &m) {
if (m.pin == drive_pins.hepa_push_button.pin) {
// ignore hepa push button presses
return;
}

// update states
if (m.pin == drive_pins.uv_push_button.pin) {
uv_push_button = !uv_push_button;
} else if (m.pin == drive_pins.door_open.pin) {
door_closed = gpio::is_set(drive_pins.door_open);
} else if (m.pin == drive_pins.reed_switch.pin) {
reed_switch_set = gpio::is_set(drive_pins.reed_switch);
}

// reset push button state if the door is opened or the reed switch is
// not set
if (!door_closed || !reed_switch_set) uv_push_button = false;

// set the UV Ballast
if (door_closed && reed_switch_set && uv_push_button) {
gpio::set(drive_pins.uv_on_off);
// start the turn off timer
if (_timer.is_running()) _timer.stop();
_timer.start();
uv_fan_on = true;
} else {
gpio::reset(drive_pins.uv_on_off);
if (_timer.is_running()) _timer.stop();
uv_fan_on = false;
}

// TODO: send CAN message to host
}

// state tracking variables
bool door_closed = false;
bool reed_switch_set = false;
bool uv_push_button = false;
bool uv_fan_on = false;

gpio_drive_hardware::GpioDrivePins &drive_pins;
ot_utils::freertos_timer::FreeRTOSTimer _timer;
};

/**
* The task type.
*/
template <template <class> class QueueImpl>
requires MessageQueue<QueueImpl<TaskMessage>, TaskMessage>
class UVTask {
public:
using Messages = TaskMessage;
using QueueType = QueueImpl<TaskMessage>;
UVTask(QueueType &queue) : queue{queue} {}
UVTask(const UVTask &c) = delete;
UVTask(const UVTask &&c) = delete;
auto operator=(const UVTask &c) = delete;
auto operator=(const UVTask &&c) = delete;
~UVTask() = default;

/**
* Task entry point.
*/
[[noreturn]] void operator()(
gpio_drive_hardware::GpioDrivePins *drive_pins) {
auto handler = UVMessageHandler{*drive_pins};
TaskMessage message{};
for (;;) {
if (queue.try_read(&message, queue.max_delay)) {
handler.handle_message(message);
}
}
}

[[nodiscard]] auto get_queue() const -> QueueType & { return queue; }

private:
QueueType &queue;
};
}; // namespace uv_task

0 comments on commit 2809beb

Please sign in to comment.