From 314744987588121c3987b4f81df5cf0d87f786cd Mon Sep 17 00:00:00 2001 From: Hibyehello <36666883+Hibyehello@users.noreply.github.com> Date: Tue, 10 Feb 2026 14:42:35 -0700 Subject: [PATCH 1/3] Add Inverter Watchdog --- lib/can/types.h | 24 +++++++++++- lib/inverter/DTIX50/heartbeat.cpp | 23 +++++++++-- lib/inverter/DTIX50/heartbeat.h | 5 ++- lib/inverter/DTIX50/watchdog.cpp | 38 +++++++++++++++++++ lib/inverter/DTIX50/watchdog.h | 27 +++++++++++++ .../test_inverter_heartbeat_controller.cpp | 2 +- 6 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 lib/inverter/DTIX50/watchdog.cpp create mode 100644 lib/inverter/DTIX50/watchdog.h diff --git a/lib/can/types.h b/lib/can/types.h index d220cb9..69cee46 100644 --- a/lib/can/types.h +++ b/lib/can/types.h @@ -1,5 +1,6 @@ #pragma once +#include #include namespace CAN { @@ -7,6 +8,27 @@ namespace CAN { typedef uint32_t Tick; typedef uint32_t Alert; + +/** + * @brief A generic enum for the status of CAN devices + * @note Device specific errors should be converted into a status error + * + * @note Less than 0 is Fatal Errors + * @note Greater than 0 is non-fatal errors/warnings + */ +enum class Status { + OK = 0, + // Warnings/Non-Fatal Errors + WARN = 1, + COMM_WARN = 2, + // Fatal Errors + FATAL = -1, + COMM_ERR = -2, // TODO: Make a list of errors that should kill the car and not + +}; + +typedef std::function ErrorCallback; + enum class Result { OK = 0, /*!< esp_err_t value indicating success (no error) */ FAIL = -1, /*!< Generic esp_err_t code indicating failure */ @@ -144,7 +166,7 @@ struct Frame { } template - T* decode() { + T* decode() const { return (T*) data; } }; diff --git a/lib/inverter/DTIX50/heartbeat.cpp b/lib/inverter/DTIX50/heartbeat.cpp index 99c26f9..6a755c1 100644 --- a/lib/inverter/DTIX50/heartbeat.cpp +++ b/lib/inverter/DTIX50/heartbeat.cpp @@ -5,7 +5,9 @@ using namespace CAN; namespace Inverter { namespace DTIX50 { -Heartbeat::Heartbeat(std::shared_ptr canProvider, std::unique_ptr lock_strategy, std::unique_ptr thread_strategy) { +uint8_t MAX_FAILED_TRANSMITS = 3; + +Heartbeat::Heartbeat(std::shared_ptr canProvider, std::unique_ptr lock_strategy, std::unique_ptr thread_strategy, ErrorCallback callback) : onErrorCallback(callback) { m_canProvider = canProvider; m_shouldStop_mut = std::move(lock_strategy); m_thread = std::move(thread_strategy); @@ -57,7 +59,12 @@ void Heartbeat::heartbeat(void* s) { self->m_shouldStop_mut->unlock(); // Send drive disable Frame frame(0x0C52, &self->disable); - self->m_canProvider->transmit(frame, 1000); + bool success = self->m_canProvider->transmit(frame, 1000); + + if(!success) { + self->onErrorCallback(Status::COMM_ERR); + } + return; } self->m_shouldStop_mut->unlock(); @@ -65,7 +72,17 @@ void Heartbeat::heartbeat(void* s) { // Send drive enable Frame frame(0x0C52, &self->enable); - self->m_canProvider->transmit(frame, 1000); + bool success = self->m_canProvider->transmit(frame, 1000); + if(!success) { + if(self->failed_transmits > MAX_FAILED_TRANSMITS) { + self->onErrorCallback(Status::COMM_ERR); + } else { + self->onErrorCallback(Status::COMM_WARN); + self->failed_transmits++; + } + } else { + self->failed_transmits -= self->failed_transmits > 0 ? 1: 0; + } self->m_thread->sleep(250U); } diff --git a/lib/inverter/DTIX50/heartbeat.h b/lib/inverter/DTIX50/heartbeat.h index ffef29b..c2d3add 100644 --- a/lib/inverter/DTIX50/heartbeat.h +++ b/lib/inverter/DTIX50/heartbeat.h @@ -24,8 +24,11 @@ class Heartbeat { Command::SetDriveEnable enable; Command::SetDriveEnable disable; + + uint8_t failed_transmits; // Counts consecutive failed transmits + ErrorCallback onErrorCallback; public: - Heartbeat(std::shared_ptr canProvider, std::unique_ptr lock_strategy, std::unique_ptr thread_strategy); + Heartbeat(std::shared_ptr canProvider, std::unique_ptr lock_strategy, std::unique_ptr thread_strategy, ErrorCallback callback); void start(); void stop(); diff --git a/lib/inverter/DTIX50/watchdog.cpp b/lib/inverter/DTIX50/watchdog.cpp new file mode 100644 index 0000000..8a8b566 --- /dev/null +++ b/lib/inverter/DTIX50/watchdog.cpp @@ -0,0 +1,38 @@ +#include "watchdog.h" + +using namespace CAN; + +namespace Inverter { +namespace DTIX50 { + +Watchdog::Watchdog(ErrorCallback callback) : onErrorCallback(callback) {} + +Status Watchdog::readFrame(const Frame& frame) { + uint32_t id = frame.identifier >> 5; + + switch(id) { + case 0x22: + return handleMessage22(frame); + default: + break; + } + return Status::OK; +} + +Status Watchdog::handleMessage22(const Frame& frame) { + auto message = frame.decode(); + + // We can expand this to cover all errors, status just doesn't have much support yet + if(message->fault_code != FaultCodes::NONE) { + return Status::FATAL; + } + + return Status::OK; +} + +void Watchdog::onError(const Status& status) { + onErrorCallback(status); +} + +}; +}; \ No newline at end of file diff --git a/lib/inverter/DTIX50/watchdog.h b/lib/inverter/DTIX50/watchdog.h new file mode 100644 index 0000000..1a23368 --- /dev/null +++ b/lib/inverter/DTIX50/watchdog.h @@ -0,0 +1,27 @@ +#pragma once + +#include "../../can/types.h" + +#include "messages.h" + +using namespace CAN; + +namespace Inverter { +namespace DTIX50 { + +class Watchdog { +public: + ErrorCallback onErrorCallback; +public: + Watchdog(ErrorCallback callback); + + Status readFrame(const Frame& frame); + + void onError(const Status& error); +private: + + Status handleMessage22(const Frame& frame); +}; + +}; +}; \ No newline at end of file diff --git a/test/test_inverter/DTIX50/test_inverter_heartbeat_controller.cpp b/test/test_inverter/DTIX50/test_inverter_heartbeat_controller.cpp index e464a10..f19fbdb 100644 --- a/test/test_inverter/DTIX50/test_inverter_heartbeat_controller.cpp +++ b/test/test_inverter/DTIX50/test_inverter_heartbeat_controller.cpp @@ -29,7 +29,7 @@ void test_Heartbeat() { std::unique_ptr lockStrategy(new NativeLockStrategy()); // We'll want the class to recieve ownership std::unique_ptr threadStrategy(new NativeThreadStrategy()); // We'll want the class to recieve ownership - DTIX50::Heartbeat heartbeat(canProvider, std::move(lockStrategy), std::move(threadStrategy)); + DTIX50::Heartbeat heartbeat(canProvider, std::move(lockStrategy), std::move(threadStrategy), nullptr); TEST_ASSERT(!heartbeat.started()); heartbeat.start(); From 503b9b11516fff3894de640842e77746940cf859 Mon Sep 17 00:00:00 2001 From: Hibyehello <36666883+Hibyehello@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:52:28 -0700 Subject: [PATCH 2/3] Create abstract receiver class --- lib/can/receiver.h | 37 +++++++++++++++++++++++++++++++ lib/can/types.h | 21 ------------------ lib/inverter/DTIX50/heartbeat.cpp | 5 ++--- lib/inverter/DTIX50/heartbeat.h | 2 +- lib/inverter/DTIX50/watchdog.cpp | 4 ++-- lib/inverter/DTIX50/watchdog.h | 8 +++---- 6 files changed, 46 insertions(+), 31 deletions(-) create mode 100644 lib/can/receiver.h diff --git a/lib/can/receiver.h b/lib/can/receiver.h new file mode 100644 index 0000000..2ac3d18 --- /dev/null +++ b/lib/can/receiver.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include "types.h" + +namespace CAN { + +/** + * @brief A generic enum for the status of CAN devices + * @note Device specific errors should be converted into a status error + * + * @note Less than 0 is Fatal Errors + * @note Greater than 0 is non-fatal errors/warnings + */ +enum class Status { + OK = 0, + // Warnings/Non-Fatal Errors + WARN = 1, + COMM_WARN = 2, + // Fatal Errors + FATAL = -1, + COMM_ERR = -2, // TODO: Make a list of errors that should kill the car and not + +}; +typedef std::function ErrorCallback; + +class Receiver { +private: + ErrorCallback onErrorCallback; +public: + virtual Status digestFrame(const Frame&) const = 0; + virtual void onError(const Status&) = 0; +}; + +}; \ No newline at end of file diff --git a/lib/can/types.h b/lib/can/types.h index 69cee46..a17bc91 100644 --- a/lib/can/types.h +++ b/lib/can/types.h @@ -8,27 +8,6 @@ namespace CAN { typedef uint32_t Tick; typedef uint32_t Alert; - -/** - * @brief A generic enum for the status of CAN devices - * @note Device specific errors should be converted into a status error - * - * @note Less than 0 is Fatal Errors - * @note Greater than 0 is non-fatal errors/warnings - */ -enum class Status { - OK = 0, - // Warnings/Non-Fatal Errors - WARN = 1, - COMM_WARN = 2, - // Fatal Errors - FATAL = -1, - COMM_ERR = -2, // TODO: Make a list of errors that should kill the car and not - -}; - -typedef std::function ErrorCallback; - enum class Result { OK = 0, /*!< esp_err_t value indicating success (no error) */ FAIL = -1, /*!< Generic esp_err_t code indicating failure */ diff --git a/lib/inverter/DTIX50/heartbeat.cpp b/lib/inverter/DTIX50/heartbeat.cpp index 6a755c1..a559489 100644 --- a/lib/inverter/DTIX50/heartbeat.cpp +++ b/lib/inverter/DTIX50/heartbeat.cpp @@ -72,8 +72,7 @@ void Heartbeat::heartbeat(void* s) { // Send drive enable Frame frame(0x0C52, &self->enable); - bool success = self->m_canProvider->transmit(frame, 1000); - if(!success) { + if(!self->m_canProvider->transmit(frame, 100)) { if(self->failed_transmits > MAX_FAILED_TRANSMITS) { self->onErrorCallback(Status::COMM_ERR); } else { @@ -81,7 +80,7 @@ void Heartbeat::heartbeat(void* s) { self->failed_transmits++; } } else { - self->failed_transmits -= self->failed_transmits > 0 ? 1: 0; + self->failed_transmits = 0; } self->m_thread->sleep(250U); diff --git a/lib/inverter/DTIX50/heartbeat.h b/lib/inverter/DTIX50/heartbeat.h index c2d3add..6d34b0c 100644 --- a/lib/inverter/DTIX50/heartbeat.h +++ b/lib/inverter/DTIX50/heartbeat.h @@ -7,7 +7,7 @@ #include "../core/lock/i_lock_strategy.h" #include "../core/thread/i_thread_strategy.h" #include "../can/provider.h" -#include "../can/types.h" +#include "../can/receiver.h" using namespace CAN; diff --git a/lib/inverter/DTIX50/watchdog.cpp b/lib/inverter/DTIX50/watchdog.cpp index 8a8b566..28340bd 100644 --- a/lib/inverter/DTIX50/watchdog.cpp +++ b/lib/inverter/DTIX50/watchdog.cpp @@ -7,7 +7,7 @@ namespace DTIX50 { Watchdog::Watchdog(ErrorCallback callback) : onErrorCallback(callback) {} -Status Watchdog::readFrame(const Frame& frame) { +Status Watchdog::digestFrame(const Frame& frame) const { uint32_t id = frame.identifier >> 5; switch(id) { @@ -19,7 +19,7 @@ Status Watchdog::readFrame(const Frame& frame) { return Status::OK; } -Status Watchdog::handleMessage22(const Frame& frame) { +Status Watchdog::handleMessage22(const Frame& frame) const { auto message = frame.decode(); // We can expand this to cover all errors, status just doesn't have much support yet diff --git a/lib/inverter/DTIX50/watchdog.h b/lib/inverter/DTIX50/watchdog.h index 1a23368..0568ab3 100644 --- a/lib/inverter/DTIX50/watchdog.h +++ b/lib/inverter/DTIX50/watchdog.h @@ -1,6 +1,6 @@ #pragma once -#include "../../can/types.h" +#include "../../can/receiver.h" #include "messages.h" @@ -9,18 +9,18 @@ using namespace CAN; namespace Inverter { namespace DTIX50 { -class Watchdog { +class Watchdog : public Receiver { public: ErrorCallback onErrorCallback; public: Watchdog(ErrorCallback callback); - Status readFrame(const Frame& frame); + Status digestFrame(const Frame& frame) const override; void onError(const Status& error); private: - Status handleMessage22(const Frame& frame); + Status handleMessage22(const Frame& frame) const; }; }; From a24d6f9e422b270b4081b4f4fae7ea73303c1ab2 Mon Sep 17 00:00:00 2001 From: Hibyehello <36666883+Hibyehello@users.noreply.github.com> Date: Tue, 10 Feb 2026 21:27:54 -0700 Subject: [PATCH 3/3] Add getIDs function with a helper getIDCount function --- lib/can/receiver.h | 2 ++ lib/inverter/DTIX50/watchdog.cpp | 9 +++++++++ lib/inverter/DTIX50/watchdog.h | 5 ++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/can/receiver.h b/lib/can/receiver.h index 2ac3d18..8f66e0b 100644 --- a/lib/can/receiver.h +++ b/lib/can/receiver.h @@ -32,6 +32,8 @@ class Receiver { public: virtual Status digestFrame(const Frame&) const = 0; virtual void onError(const Status&) = 0; + virtual uint32_t* getIDs() = 0; + virtual uint32_t idCount() = 0; }; }; \ No newline at end of file diff --git a/lib/inverter/DTIX50/watchdog.cpp b/lib/inverter/DTIX50/watchdog.cpp index 28340bd..3fc9a85 100644 --- a/lib/inverter/DTIX50/watchdog.cpp +++ b/lib/inverter/DTIX50/watchdog.cpp @@ -34,5 +34,14 @@ void Watchdog::onError(const Status& status) { onErrorCallback(status); } +uint32_t* Watchdog::getIDs() { + uint32_t ids[] = { 0x52 } ; + return ids; +} + +uint32_t Watchdog::idCount() { + return 1; +} + }; }; \ No newline at end of file diff --git a/lib/inverter/DTIX50/watchdog.h b/lib/inverter/DTIX50/watchdog.h index 0568ab3..2549a01 100644 --- a/lib/inverter/DTIX50/watchdog.h +++ b/lib/inverter/DTIX50/watchdog.h @@ -9,7 +9,7 @@ using namespace CAN; namespace Inverter { namespace DTIX50 { -class Watchdog : public Receiver { + class Watchdog final : public Receiver { public: ErrorCallback onErrorCallback; public: @@ -18,6 +18,9 @@ class Watchdog : public Receiver { Status digestFrame(const Frame& frame) const override; void onError(const Status& error); + + uint32_t* getIDs() override; + uint32_t idCount() override; private: Status handleMessage22(const Frame& frame) const;