Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hepa-uv): add support for the safety relay in the new revisions of the Hepa/UV module. #782

Merged
merged 7 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1712,6 +1712,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 @@ -1720,6 +1721,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
3 changes: 3 additions & 0 deletions include/hepa-uv/firmware/gpio_drive_hardware.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <optional>

#include "common/firmware/gpio.hpp"

namespace gpio_drive_hardware {
Expand All @@ -11,6 +13,7 @@ struct GpioDrivePins {
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
Loading