Skip to content

Commit

Permalink
feat(hepa-uv): add support for the safety relay in the new revision o…
Browse files Browse the repository at this point in the history
…f the Hepa/UV module. (#782)
  • Loading branch information
vegano1 authored Jun 17, 2024
1 parent 1788c1f commit 0e8fb3d
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 35 deletions.
4 changes: 2 additions & 2 deletions bootloader/firmware/stm32G4/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ endmacro()
foreach_revision(
PROJECT_NAME bootloader-hepa-uv
CALL_FOREACH_REV hepauv_bootloader_loop
REVISIONS a1 b1
SOURCES hepauv_sources hepauv_sources
REVISIONS a1 b1 c1
SOURCES hepauv_sources hepauv_sources hepauv_sources
NO_CREATE_IMAGE_HEX
NO_CREATE_INSTALL_RULES
)
Expand Down
12 changes: 12 additions & 0 deletions cpp-utils/include/ot_utils/freertos/freertos_sleep.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include "FreeRTOS.h"
#include "task.h"

namespace ot_utils {
namespace freertos_sleep {

static void sleep(uint32_t time_ms) { if (time_ms > 0) vTaskDelay(pdMS_TO_TICKS(time_ms)); }

} // namespace freertos_sleep
} // namespace ot_utils
7 changes: 3 additions & 4 deletions hepa-uv/firmware/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ set(HEPAUV_SRCS_A1
)
set(HEPAUV_SRCS_B1 ${HEPAUV_SRCS_A1})
set(HEPAUV_SRCS_C1 ${HEPAUV_SRCS_B1})
set(HEPAUV_SRCS_C2 ${HEPAUV_SRCS_C1})

macro(hepa_uv_loop)
set(_driver_suffix ${PROJECT_NAME}_${REVISION})
Expand Down Expand Up @@ -84,12 +83,12 @@ endmacro()

foreach_revision(
PROJECT_NAME hepa-uv
REVISIONS a1 b1
SOURCES HEPAUV_SRCS_A1 HEPAUV_SRCS_B1
REVISIONS a1 b1 c1
SOURCES HEPAUV_SRCS_A1 HEPAUV_SRCS_B1 HEPAUV_SRCS_C1
CALL_FOREACH_REV hepa_uv_loop)

alias_for_revision(PROJECT_NAME hepa-uv REVISION a1 REVISION_ALIAS proto)
alias_for_revision(PROJECT_NAME hepa-uv REVISION b1 REVISION_ALIAS rev1)
alias_for_revision(PROJECT_NAME hepa-uv REVISION c1 REVISION_ALIAS rev1)

add_clang_tidy_target(
TARGET_NAME hepa-uv-lint
Expand Down
31 changes: 23 additions & 8 deletions hepa-uv/firmware/main_rev1.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <array>
#include <optional>

// clang-format off
#include "FreeRTOS.h"
Expand Down Expand Up @@ -92,6 +93,17 @@ class EEPromHardwareInterface
};
static auto eeprom_hw_iface = EEPromHardwareInterface();

#if PCB_PRIMARY_REVISION == 'a' || PCB_PRIMARY_REVISION == 'b'
static constexpr std::optional<gpio::PinConfig> safety_relay_active =
std::nullopt;
#else
static std::optional<gpio::PinConfig> safety_relay_active =
gpio::PinConfig{// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
.port = nSAFETY_ACTIVE_MCU_PORT,
.pin = nSAFETY_ACTIVE_MCU_PIN,
.active_setting = nSAFETY_ACTIVE_AS};
#endif

static auto gpio_drive_pins = gpio_drive_hardware::GpioDrivePins{
.door_open =
gpio::PinConfig{
Expand All @@ -114,20 +126,23 @@ static auto gpio_drive_pins = gpio_drive_hardware::GpioDrivePins{
.uv_push_button =
gpio::PinConfig{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
.port = UV_NO_MCU_PORT,
.pin = UV_NO_MCU_PIN,
.port = nUV_PRESSED_PORT,
.pin = nUV_PRESSED_PIN,
},
.hepa_on_off =
gpio::PinConfig{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
.port = HEPA_ON_OFF_PORT,
.pin = HEPA_ON_OFF_PIN,
.active_setting = HEPA_ON_OFF_AS},
.uv_on_off = gpio::PinConfig{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
.port = UV_ON_OFF_MCU_PORT,
.pin = UV_ON_OFF_MCU_PIN,
.active_setting = UV_ON_OFF_AS}};
.uv_on_off =
gpio::PinConfig{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
.port = UV_ON_OFF_MCU_PORT,
.pin = UV_ON_OFF_MCU_PIN,
.active_setting = UV_ON_OFF_AS},
.safety_relay_active = safety_relay_active,
};

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

Expand All @@ -140,7 +155,7 @@ extern "C" void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
case DOOR_OPEN_MCU_PIN:
case REED_SW_MCU_PIN:
case HEPA_NO_MCU_PIN:
case UV_NO_MCU_PIN:
case nUV_PRESSED_PIN:
if (hepauv_queues.hepa_queue != nullptr) {
static_cast<void>(hepauv_queues.hepa_queue->try_write_isr(
GPIOInterruptChanged{.pin = GPIO_Pin}));
Expand Down
21 changes: 19 additions & 2 deletions hepa-uv/firmware/utility_gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ void uv_push_button_input_gpio_init(void) {
__HAL_RCC_GPIOC_CLK_ENABLE();
/*Configure GPIO pin UV_NO_MCU : PC2 */
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = UV_NO_MCU_PIN;
GPIO_InitStruct.Pin = nUV_PRESSED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(UV_NO_MCU_PORT, &GPIO_InitStruct);
HAL_GPIO_Init(nUV_PRESSED_PORT, &GPIO_InitStruct);
}

/**
Expand Down Expand Up @@ -134,6 +134,22 @@ void uv_on_off_output_init() {
HAL_GPIO_Init(UV_ON_OFF_MCU_PORT, &GPIO_InitStruct);
}

/**
* @brief nSAFETY_ACTIVE_MCU GPIO Initialization Function
* @param None
* @retval None
*/
void safety_relay_active_input_init() {
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin nSAFETY_ACTIVE_MCU: PB5 */
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = nSAFETY_ACTIVE_MCU_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(nSAFETY_ACTIVE_MCU_PORT, &GPIO_InitStruct);
}

/**
* @brief NVIC EXTI interrupt priority Initialization
* @param None
Expand Down Expand Up @@ -168,4 +184,5 @@ void utility_gpio_init(void) {
hepa_on_off_output_init();
uv_push_button_input_gpio_init();
uv_on_off_output_init();
safety_relay_active_input_init();
}
1 change: 1 addition & 0 deletions include/bootloader/core/ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ typedef enum {
can_errorcode_over_pressure = 0xd,
can_errorcode_door_open = 0xe,
can_errorcode_reed_open = 0xf,
can_errorcode_safety_relay_inactive = 0x11,
} CANErrorCode;

/** Tool types detected on Head. */
Expand Down
1 change: 1 addition & 0 deletions include/can/core/ids.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ enum class ErrorCode {
over_pressure = 0xd,
door_open = 0xe,
reed_open = 0xf,
safety_relay_inactive = 0x11,
};

/** Error Severity levels. */
Expand Down
2 changes: 2 additions & 0 deletions include/can/core/messages.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,7 @@ struct GetHepaUVStateResponse
uint8_t uv_light_on;
uint32_t remaining_time_s;
uint16_t uv_current_ma;
uint8_t safety_relay_active;

template <bit_utils::ByteIterator Output, typename Limit>
auto serialize(Output body, Limit limit) const -> uint8_t {
Expand All @@ -1724,6 +1725,7 @@ struct GetHepaUVStateResponse
iter = bit_utils::int_to_bytes(uv_light_on, iter, limit);
iter = bit_utils::int_to_bytes(remaining_time_s, iter, limit);
iter = bit_utils::int_to_bytes(uv_current_ma, iter, limit);
iter = bit_utils::int_to_bytes(safety_relay_active, iter, limit);
return iter - body;
}

Expand Down
70 changes: 58 additions & 12 deletions include/hepa-uv/core/uv_task.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
#include "hepa-uv/core/messages.hpp"
#include "hepa-uv/firmware/gpio_drive_hardware.hpp"
#include "hepa-uv/firmware/uv_control_hardware.hpp"
#include "ot_utils/freertos/freertos_sleep.hpp"
#include "ot_utils/freertos/freertos_timer.hpp"

namespace uv_task {

// How long to keep the UV light on in seconds.
static constexpr uint32_t DELAY_S = 60 * 15; // 15 minutes
static constexpr uint32_t MAX_DELAY_S = 60 * 60; // 1hr max timeout
static constexpr uint32_t DEBOUNCE_MS = 250; // button debounce

using TaskMessage = uv_task_messages::TaskMessage;

Expand All @@ -33,11 +35,15 @@ class UVMessageHandler {
can_client{can_client},
_timer(
"UVTask", [ThisPtr = this] { ThisPtr->timer_callback(); },
DELAY_S * 1000) {
DELAY_S * 1000),
debounce_timer(
"UVTaskDebounce", [ThisPtr = this] { ThisPtr->debounce_cb(); },
DEBOUNCE_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);
update_safety_relay_state();
// turn off UV Ballast
gpio::reset(drive_pins.uv_on_off);
}
Expand All @@ -58,6 +64,19 @@ class UVMessageHandler {
uv_push_button = false;
}

// callback to debounce the irq signals
void debounce_cb() {
debounce_timer.stop();
set_uv_light_state(uv_push_button, uv_off_timeout_s);
}

// Helper to update safety relay state
void update_safety_relay_state() {
if (drive_pins.safety_relay_active.has_value())
safety_relay_active =
gpio::is_set(drive_pins.safety_relay_active.value());
}

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

// Handle GPIO EXTI Interrupts here
Expand All @@ -67,17 +86,16 @@ class UVMessageHandler {
return;
}

// debounce
if (debounce_timer.is_running()) return;
debounce_timer.start();

// update states
if (m.pin == drive_pins.uv_push_button.pin) {
door_closed = gpio::is_set(drive_pins.door_open);
reed_switch_set = gpio::is_set(drive_pins.reed_switch);
update_safety_relay_state();
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);
}

// Drive the UV light
set_uv_light_state(uv_push_button, uv_off_timeout_s);
}

void visit(const can::messages::SetHepaUVStateRequest &m) {
Expand All @@ -89,13 +107,15 @@ class UVMessageHandler {
}

void visit(const can::messages::GetHepaUVStateRequest &m) {
update_safety_relay_state();
uv_current_ma = uv_hardware.get_uv_light_current();
auto resp = can::messages::GetHepaUVStateResponse{
.message_index = m.message_index,
.timeout_s = uv_off_timeout_s,
.uv_light_on = uv_light_on,
.remaining_time_s = (_timer.get_remaining_time() / 1000),
.uv_current_ma = uv_current_ma};
.uv_current_ma = uv_current_ma,
.safety_relay_active = safety_relay_active};
can_client.send_can_message(can::ids::NodeId::host, resp);
}

Expand Down Expand Up @@ -153,15 +173,40 @@ class UVMessageHandler {
if (_timer.is_running()) _timer.stop();
}

// Update the voltage usage of the uv light
// wait 10ms for safety relay, then update the states
ot_utils::freertos_sleep::sleep(100);
uv_current_ma = uv_hardware.get_uv_light_current();
update_safety_relay_state();
if (uv_light_on && !safety_relay_active) {
// we tried to set the uv light, but the relay is not active
if (_timer.is_running()) {
gpio::reset(drive_pins.uv_on_off);
_timer.stop();
led_control_client.send_led_control_message(
led_control_task_messages::PushButtonLED(UV_BUTTON, 0, 0,
50, 0));
}
// send error
auto msg = can::messages::ErrorMessage{
.message_index = 0,
.severity = can::ids::ErrorSeverity::warning,
.error_code = can::ids::ErrorCode::safety_relay_inactive,
};
can_client.send_can_message(can::ids::NodeId::host, msg);

uv_push_button = false;
uv_light_on = false;
uv_current_ma = 0;
return;
}

// TODO: send state change CAN message to host
}

// state tracking variables
bool door_closed = false;
bool reed_switch_set = false;
bool safety_relay_active = false;
bool uv_push_button = false;
bool uv_light_on = false;
uint32_t uv_off_timeout_s = DELAY_S;
Expand All @@ -172,6 +217,7 @@ class UVMessageHandler {
LEDControlClient &led_control_client;
CanClient &can_client;
ot_utils::freertos_timer::FreeRTOSTimer _timer;
ot_utils::freertos_timer::FreeRTOSTimer debounce_timer;
};

/**
Expand Down
4 changes: 4 additions & 0 deletions include/hepa-uv/firmware/gpio_drive_hardware.hpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
#pragma once

#include <optional>

#include "common/firmware/gpio.hpp"

namespace gpio_drive_hardware {

// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
struct GpioDrivePins {
gpio::PinConfig door_open;
gpio::PinConfig reed_switch;
gpio::PinConfig hepa_push_button;
gpio::PinConfig uv_push_button;
gpio::PinConfig hepa_on_off;
gpio::PinConfig uv_on_off;
std::optional<gpio::PinConfig> safety_relay_active = std::nullopt;
};

} // namespace gpio_drive_hardware
15 changes: 8 additions & 7 deletions include/hepa-uv/firmware/utility_gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,9 @@ void utility_gpio_init();
#define UV_ON_OFF_MCU_PORT GPIOA
#define UV_ON_OFF_MCU_PIN GPIO_PIN_4
#define UV_ON_OFF_AS GPIO_PIN_RESET
// UV_NO_MCU PC2
#define UV_NO_MCU_PORT GPIOC
#define UV_NO_MCU_PIN GPIO_PIN_2
// UV_ADC PA3
#define UV_ADC_PORT GPIOC
#define UV_ADC_PIN GPIO_PIN_3
// nUV_PRESSED PC2
#define nUV_PRESSED_PORT GPIOC
#define nUV_PRESSED_PIN GPIO_PIN_2
// UV_B_CTRL PC5
#define UV_B_CTRL_PORT GPIOC
#define UV_B_CTRL_PIN GPIO_PIN_5
Expand All @@ -105,4 +102,8 @@ void utility_gpio_init();
#define UV_R_CTRL_PIN GPIO_PIN_1
// UV_W_CTRL PB2
#define UV_W_CTRL_PORT GPIOB
#define UV_W_CTRL_PIN GPIO_PIN_2
#define UV_W_CTRL_PIN GPIO_PIN_2
// nSAFETY_ACTIVE_MCU PB5
#define nSAFETY_ACTIVE_MCU_PORT GPIOB
#define nSAFETY_ACTIVE_MCU_PIN GPIO_PIN_5
#define nSAFETY_ACTIVE_AS GPIO_PIN_RESET

0 comments on commit 0e8fb3d

Please sign in to comment.