From 9ec420fc4a09677d18f3fe0f295ded7ebf391b6b Mon Sep 17 00:00:00 2001 From: Manush Date: Thu, 23 Oct 2025 22:02:54 -0400 Subject: [PATCH 01/26] setup of lvbms system --- projects/lvcontroller/src/lvbms/lvbms.cc | 7 +++++++ projects/lvcontroller/src/lvbms/lvbms.hpp | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 projects/lvcontroller/src/lvbms/lvbms.cc create mode 100644 projects/lvcontroller/src/lvbms/lvbms.hpp diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc new file mode 100644 index 000000000..e76e184b7 --- /dev/null +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -0,0 +1,7 @@ +#pragma once + +#include "lvbms.hpp" + +#include "periph/spi.hpp" + +namespace macfe::lv {} diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp new file mode 100644 index 000000000..ecb5a1cd7 --- /dev/null +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace macfe::periph { +class SpiMaster; +} + +namespace macfe::lv { + +class LvBms { +public: + LvBms(macfe::periph::SpiMaster&); + +private: + macfe::periph::SpiMaster& spi_; +}; + +} // namespace macfe::lv \ No newline at end of file From 6ed41a8a63381136aed24c51b46b47417ce05b46 Mon Sep 17 00:00:00 2001 From: Manush Date: Wed, 29 Oct 2025 23:57:31 -0400 Subject: [PATCH 02/26] compile error --- projects/lvcontroller/src/lvbms/lvbms.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index e76e184b7..00e54630e 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -1,4 +1,4 @@ -#pragma once + #include "lvbms.hpp" From 7bb0b753ba6102e7702d2fb549feaa7a26a7a5e8 Mon Sep 17 00:00:00 2001 From: Nevin Speakman <150295918+SpeakBoy@users.noreply.github.com> Date: Sun, 9 Nov 2025 14:46:59 -0500 Subject: [PATCH 03/26] Improve static code analysis time for PRs (#542) * Check for changes only in certain directories * Fix static code analysis workflow * Fix build-test-projects.yml * Add test files for PR analysis * Remove test files from repo * Reduce number of pre-commit checks * Reduce number of PR analysis checks --- .github/workflows/build-test-projects.yml | 16 ++++++++++++++-- scripts/static_analysis/static_analysis.sh | 10 +++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-test-projects.yml b/.github/workflows/build-test-projects.yml index 884d24560..0f77c96a8 100644 --- a/.github/workflows/build-test-projects.yml +++ b/.github/workflows/build-test-projects.yml @@ -7,6 +7,8 @@ jobs: steps: - name: Checkout Repository uses: actions/checkout@v4 + with: + fetch-depth: 0 - uses: actions/cache@v4 with: @@ -52,10 +54,20 @@ jobs: - name: Perform Static Code Analysis on PlatformIO Projects shell: bash run: | - git ls-files -z 'projects/**/platformio.ini' | while IFS= read -r -d '' ini; do + git ls-files -z 'projects/**/platformio.ini' | grep -zv 'projects/demo/' | while IFS= read -r -d '' ini; do dir=$(dirname "$ini") echo "============================================================" echo "==== Performing Static Code Analysis on $dir" echo "============================================================" - pio check --project-dir "$dir" --fail-on-defect=high || exit 1 + + SUBDIRS=("$dir/src" "$dir/include") + EXCLUDE_PREFIX="stm*" + + # Check for changes in $dir/src and $dir/include, excluding files within $dir/src/platforms/stm* + if git diff --name-only origin/main...HEAD -- "${SUBDIRS[@]}" ":!$dir/src/platforms/$EXCLUDE_PREFIX" | grep -q .; then + echo "Changes detected in $dir → running pio check" + pio check --project-dir "$dir" --flags "--checks=bugprone-*,clang-analyzer-*,google-*,performance-*" --fail-on-defect=high || exit 1 + else + echo "No changes in $dir → skipping" + fi done diff --git a/scripts/static_analysis/static_analysis.sh b/scripts/static_analysis/static_analysis.sh index cce99cc3d..0a85e0bd0 100644 --- a/scripts/static_analysis/static_analysis.sh +++ b/scripts/static_analysis/static_analysis.sh @@ -1,13 +1,17 @@ #!/bin/bash -git ls-files -z 'projects/**/platformio.ini' | while IFS= read -r -d '' ini; do +git ls-files -z 'projects/**/platformio.ini' | grep -zv 'projects/demo/' | while IFS= read -r -d '' ini; do dir=$(dirname "$ini") echo "============================================================" echo "==== Performing Static Code Analysis on $dir" echo "============================================================" - if ! git diff --quiet --staged --exit-code "$dir"; then + SUBDIRS=("$dir/src" "$dir/include") + EXCLUDE_PREFIX="stm*" + + # Check for changes in $dir/src and $dir/include, excluding files within $dir/src/platforms/stm* + if git diff --name-only --staged -- "${SUBDIRS[@]}" ":!$dir/src/platforms/$EXCLUDE_PREFIX" | grep -q .; then echo "Changes detected in $dir → running pio check" - pio check --project-dir "$dir" --fail-on-defect=high || exit 1 + pio check --project-dir "$dir" --flags "--checks=bugprone-*,clang-analyzer-*,google-*,performance-*" --fail-on-defect=high || exit 1 else echo "No changes in $dir → skipping" fi From fc1437078226ccc2271e2e564b5436fe77484165 Mon Sep 17 00:00:00 2001 From: Manush Date: Sun, 9 Nov 2025 16:15:46 -0500 Subject: [PATCH 04/26] pec15 & sendcmd function --- lib/periph/periph/spi.hpp | 7 ++-- projects/lvcontroller/src/lvbms/lvbms.cc | 39 ++++++++++++++++++++++- projects/lvcontroller/src/lvbms/lvbms.hpp | 7 ++++ projects/lvcontroller/src/main.cc | 6 ++++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/lib/periph/periph/spi.hpp b/lib/periph/periph/spi.hpp index da02bd731..07428db6f 100644 --- a/lib/periph/periph/spi.hpp +++ b/lib/periph/periph/spi.hpp @@ -7,16 +7,17 @@ namespace macfe::periph { class SpiMaster { public: + virtual ~SpiMaster() = default; /// Transmit `length` bytes from `tx_data` - virtual void Transmit(uint8_t* tx_data, size_t length); + virtual void Transmit(uint8_t* tx_data, size_t length) {} /// Receive `length` bytes and place them into `rx_data` - virtual void Receive(uint8_t* rx_data, size_t length); + virtual void Receive(uint8_t* rx_data, size_t length) {} /// Transmit `length` bytes from `tx_data` and receive `length` bytes, /// placing them into `rx_data` virtual void TransmitReceive(uint8_t* tx_data, uint8_t* rx_data, - size_t length); + size_t length) {} }; } // namespace macfe::periph \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index 00e54630e..76430b1e2 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -2,6 +2,43 @@ #include "lvbms.hpp" +#include + #include "periph/spi.hpp" -namespace macfe::lv {} +namespace macfe::lv { +macfe::lv::LvBms::LvBms(macfe::periph::SpiMaster& spi) : spi_(spi) {} + +bool LvBms::resetCommandCounter() { + uint8_t hi = (0x052E >> 8) & 0xFF; // RSTCC command + uint8_t lo = 0x052E & 0xFF; + return sendCmd(hi, lo); +} + +bool LvBms::sendCmd(uint8_t hi, uint8_t lo) { + uint8_t tx[4] = {hi, lo, 0, 0}; + uint16_t pec = pec15(tx, 2); + tx[2] = (pec >> 8) & 0xFF; + tx[3] = pec & 0xFF; + + spi_.Transmit(tx, 4); + return true; +} + +uint16_t LvBms::pec15(const uint8_t* data, int len) const { + // Packet Error Checker (CRC) + uint16_t rem = 0x0010; + constexpr uint16_t poly = 0x4599; + for (int i = 0; i < len; i++) { + rem ^= static_cast(data[i]) << 7; + for (int b = 0; b < 8; b++) { + bool top = rem & 0x4000; // checks for leftmost bit = 1 + rem <<= 1; + if (top) rem ^= poly; + rem &= 0x7FFF; + } + } + + return rem; +} +} // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index ecb5a1cd7..40b6c5044 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -1,4 +1,5 @@ #pragma once +#include namespace macfe::periph { class SpiMaster; @@ -10,8 +11,14 @@ class LvBms { public: LvBms(macfe::periph::SpiMaster&); + bool init(); + bool reset(); + bool resetCommandCounter(); // RSTCC + private: macfe::periph::SpiMaster& spi_; + bool sendCmd(uint8_t hi, uint8_t lo); + uint16_t pec15(const uint8_t* data, int len) const; }; } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/main.cc b/projects/lvcontroller/src/main.cc index 75905c745..36c9e835e 100644 --- a/projects/lvcontroller/src/main.cc +++ b/projects/lvcontroller/src/main.cc @@ -14,7 +14,9 @@ #include "brakelight/brakelight.hpp" #include "dcdc/dcdc.hpp" #include "fans/fans.hpp" +#include "lvbms/lvbms.hpp" #include "motor_controller/motor_controller.hpp" +#include "periph/spi.hpp" #include "scheduler/scheduler.hpp" #include "suspension/suspension.hpp" #include "tssi/tssi.hpp" @@ -247,6 +249,10 @@ int main(void) { fsm::Init(); motor_controller::Init(); + macfe::periph::SpiMaster spi; + macfe::lv::LvBms bms(spi); + bms.resetCommandCounter(); + scheduler::register_task(task_100hz, 10); scheduler::register_task(task_10hz, 100); scheduler::register_task(task_1hz, 1000); From 556ee51ae27f85e10ed6c2f17090bc6a616e1914 Mon Sep 17 00:00:00 2001 From: Manush Date: Sun, 16 Nov 2025 17:16:28 -0500 Subject: [PATCH 05/26] added enum codes and read/write command --- projects/lvcontroller/src/lvbms/lvbms.cc | 155 ++++++++++++++++++++-- projects/lvcontroller/src/lvbms/lvbms.hpp | 7 +- 2 files changed, 148 insertions(+), 14 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index 76430b1e2..4d4eebfa4 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -2,6 +2,7 @@ #include "lvbms.hpp" +#include #include #include "periph/spi.hpp" @@ -9,36 +10,166 @@ namespace macfe::lv { macfe::lv::LvBms::LvBms(macfe::periph::SpiMaster& spi) : spi_(spi) {} +/** + * @note The ADI codes are a little more intricate, and need to be looked into + * @note ADI1, ADI2, ADV have dummy values, so this will need to change + */ + +enum class commandCode : uint16_t { + SRST = 0x27, + RSTCC = 0x2E, + SNAP = 0x2D, + UNSNAP = 0x2F, + ADI1 = 0x00, + ADI2 = 0x00, + ADV = 0x00, + ADX = 0x530, + CLRI = 0x711, + CLRA = 0x714, + CLRVX = 0x712, + CLRO = 0x713, + CLRFLAG = 0x717, + RDFLAG = 0x72, // Includes ERR code, set to 1 + RDSTAT = 0x34, + RDI = 0x12, + RDVB = 0x13, + RDIACC = 0x44, + RDVBACC = 0x46, + RDIVB1 = 0x14, + RDIVB1ACC = 0x48, + RDV1A = 0xA, + RDV1B = 0x9, + RDV1C = 0x3, + RDV1D = 0xB, + RDV2A = 0x7, + RDV2B = 0xD, + RDV2C = 0x5, + RDV2D = 0x1F, + RDV2E = 0x25, + RDXA = 0x30, + RDXB = 0x31, + RDXC = 0x33, + RDOC = 0xB, + RDSID = 0x2C, + RDCFGA = 0x2, + RDCFGB = 0x26, + RDCOMM = 0x722, + WRCFGA = 0x1, + WRCFGB = 0x24, + WRCOMM = 0x721, + STCOMM = 0x723, + PLADC = 0x718, + PLI1 = 0x71C, + PLI2 = 0x71D, + PLV = 0x71E, + PLX = 0x71F, + RDALLI = 0xC, + RDALLA = 0x4C, + RDALLV = 0x35, + RDALLR = 0x11, + RDALLX = 0x51, + RDALLC = 0x10, +}; + bool LvBms::resetCommandCounter() { - uint8_t hi = (0x052E >> 8) & 0xFF; // RSTCC command - uint8_t lo = 0x052E & 0xFF; - return sendCmd(hi, lo); + uint16_t cmd = static_cast(commandCode::RSTCC); + uint8_t hi = (cmd >> 8) & 0xFF; // RSTCC command (top half) + uint8_t lo = cmd & 0xFF; // low half + uint8_t data[6]; + return sendReadCmd(hi, lo, data); +} + +bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { + // Read Command: Command Bytes + Command PEC + + // Frame 1: Sends Read Command + uint8_t tx[12] = {0}; + uint8_t rx[12] = {0}; + + tx[0] = hi; + tx[1] = lo; + + // Compute PEC15 for command + uint16_t pec = commandPec15(tx, 2); + tx[2] = (pec >> 8) & 0xFF; + tx[3] = pec & 0xFF; + + for (int i = 4; i < 12; i++) { + tx[i] = 0xFF; + } + + spi_.TransmitReceive(tx, rx, 12); + + // Copy recieved data bytes + std::memcpy(out, &rx[4], 6); + + // Check returned PEC10 + uint16_t rxPec = (rx[10] << 8) | rx[11]; + uint16_t calcPec = dataPec10(&rx[4], 6); + + // return true if dpec matches + return (rxPec == calcPec); } -bool LvBms::sendCmd(uint8_t hi, uint8_t lo) { - uint8_t tx[4] = {hi, lo, 0, 0}; - uint16_t pec = pec15(tx, 2); +bool LvBms::sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]) { + // Write Command: 2 Command + 2 PEC15 + 6 Data + 2 PEC10 = 12 bytes + + // Command bytes + uint8_t tx[12]; + tx[0] = hi; + tx[1] = lo; + + // Command PEC15 bytes + uint16_t pec = commandPec15(tx, 2); tx[2] = (pec >> 8) & 0xFF; tx[3] = pec & 0xFF; - spi_.Transmit(tx, 4); + // Copy Data (6 bytes) + std::memcpy(&tx[4], data, 6); + + // Data PEC10 + uint16_t dataPec = dataPec10(&tx[4], 6); + tx[10] = (dataPec >> 8) & 0xFF; + tx[11] = dataPec & 0xFF; + + // Sends all 12 bytes + spi_.Transmit(tx, 12); return true; } -uint16_t LvBms::pec15(const uint8_t* data, int len) const { +uint16_t LvBms::commandPec15(const uint8_t* data, int len) const { // Packet Error Checker (CRC) uint16_t rem = 0x0010; - constexpr uint16_t poly = 0x4599; + uint16_t msbMask = 0x4000; // bit 14 + constexpr uint16_t poly = + 0x4599; // x^15 +x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1 for (int i = 0; i < len; i++) { - rem ^= static_cast(data[i]) << 7; + rem ^= static_cast(data[i]) << 7; // shifts to 15 bits, so + // XOR for (int b = 0; b < 8; b++) { - bool top = rem & 0x4000; // checks for leftmost bit = 1 + bool top = rem & msbMask; // checks for leftmost bit = 1 rem <<= 1; if (top) rem ^= poly; - rem &= 0x7FFF; + rem &= 0x7FFF; // forces remainder to be 15 bits } } return rem; } + +uint16_t LvBms::dataPec10(const uint8_t* data, int len) const { + uint16_t rem = 0x20; // 00000010000 (10-bit initial value) + constexpr uint16_t msbMask = 0x0200; // bit9 + constexpr uint16_t poly = 0x2D1; // x^10 + x^7 + x^5 + x^4 + x^3 + 1 + for (int i = 0; i < len; i++) { + rem ^= static_cast(data[i]) << 2; + for (int b = 0; b < 8; b++) { + bool top = rem & msbMask; + rem <<= 1; + if (top) rem ^= poly; + rem &= 0x3FF; // keep CRC to 10 bits + } + } + return rem; +} } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index 40b6c5044..0af291651 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -17,8 +17,11 @@ class LvBms { private: macfe::periph::SpiMaster& spi_; - bool sendCmd(uint8_t hi, uint8_t lo); - uint16_t pec15(const uint8_t* data, int len) const; + bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]); + bool sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]); + + uint16_t commandPec15(const uint8_t* data, int len) const; + uint16_t dataPec10(const uint8_t* data, int len) const; }; } // namespace macfe::lv \ No newline at end of file From 13b7a4ba5c3bd63d1a99d553e2338d55be0a9e8e Mon Sep 17 00:00:00 2001 From: Luai Bashar <122651391+luaibash@users.noreply.github.com> Date: Wed, 19 Nov 2025 21:35:07 -0500 Subject: [PATCH 06/26] Transmit Git Hash over CAN (#540) * Added generate git hash script * Added platformio configs to use generate_git_hash.py as a new pre script file * Added sending of git hash in lvcontroller * Added 1hz task and git hash send to front controller * Added 1hz task and git hash send to tms * Used env variable for finding include directory of project --- projects/dashboard/platformio.ini | 6 ++++ projects/front_controller/platformio.ini | 3 ++ projects/front_controller/src/main.cc | 13 +++++++ projects/lvcontroller/platformio.ini | 7 +++- projects/lvcontroller/src/main.cc | 5 +++ projects/tms/platformio.ini | 3 ++ projects/tms/src/main.cc | 26 +++++++++++++- projects/veh.dbc | 2 +- scripts/build/generate_git_hash.py | 46 ++++++++++++++++++++++++ 9 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 scripts/build/generate_git_hash.py diff --git a/projects/dashboard/platformio.ini b/projects/dashboard/platformio.ini index af8d23496..0c76b4fb2 100644 --- a/projects/dashboard/platformio.ini +++ b/projects/dashboard/platformio.ini @@ -35,6 +35,8 @@ flags = -Wall -Wno-unused-function ; having problems with unused MPU_Config() -D LV_BUILD_TEST=0 +extra_scripts = + pre:../../scripts/build/generate_git_hash.py [env:vehicle] platform = ststm32 @@ -47,6 +49,7 @@ lib_deps = ${common.lib_deps} extra_scripts = + ${common.extra_scripts} pre:../../scripts/build/add_hardfloat.py build_src_filter = @@ -79,3 +82,6 @@ build_flags = -l SDL2 lib_deps = ${common.lib_deps} + +extra_scripts = + ${common.extra_scripts} diff --git a/projects/front_controller/platformio.ini b/projects/front_controller/platformio.ini index b66343bcd..3cf3510ed 100644 --- a/projects/front_controller/platformio.ini +++ b/projects/front_controller/platformio.ini @@ -30,6 +30,8 @@ flags = -Werror -Wall -Wno-unused-function ; having problems with unused MPU_Config() +extra_scripts = + pre:../../scripts/build/generate_git_hash.py [env:vehicle] platform = ststm32 @@ -45,6 +47,7 @@ custom_freertos_heap_impl = "" custom_freertos_config_location = src/platforms/stm32-ev6/Inc/FreeRTOSConfig.h extra_scripts = + ${common.extra_scripts} pre:../../scripts/build/add_hardfloat.py build_src_filter = diff --git a/projects/front_controller/src/main.cc b/projects/front_controller/src/main.cc index 96d68686f..ef2ea1981 100644 --- a/projects/front_controller/src/main.cc +++ b/projects/front_controller/src/main.cc @@ -10,6 +10,7 @@ #include "generated/can/pt_messages.hpp" #include "generated/can/veh_bus.hpp" #include "generated/can/veh_messages.hpp" +#include "generated/githash.hpp" #include "motors/motors.hpp" #include "physical.hpp" #include "sensors/driver/driver.hpp" @@ -28,6 +29,7 @@ static const size_t STACK_SIZE_WORDS = // higher number = higher priority static const uint32_t PRIORITY_100HZ = 3; static const uint32_t PRIORITY_10HZ = 2; +static const uint32_t PRIORITY_1HZ = 1; StaticTask_t t100hz_control_block; StackType_t t100hz_buffer[STACK_SIZE_WORDS]; @@ -35,6 +37,9 @@ StackType_t t100hz_buffer[STACK_SIZE_WORDS]; StaticTask_t t10hz_control_block; StackType_t t10hz_buffer[STACK_SIZE_WORDS]; +StaticTask_t t1hz_control_block; +StackType_t t1hz_buffer[STACK_SIZE_WORDS]; + using namespace generated::can; VehBus veh_can_bus{bindings::veh_can_base}; @@ -263,6 +268,11 @@ void task_1hz(void* argument) { TickType_t wake_time = xTaskGetTickCount(); while (true) { + veh_can_bus.Send(TxFcGitHash{ + .commit = macfe::generated::GIT_HASH, + .dirty = macfe::generated::GIT_DIRTY, + }); + vTaskDelayUntil(&wake_time, pdMS_TO_TICKS(1000)); } } @@ -371,6 +381,9 @@ int main(void) { xTaskCreateStatic(task_10hz, "10HZ", STACK_SIZE_WORDS, NULL, PRIORITY_10HZ, t10hz_buffer, &t10hz_control_block); + xTaskCreateStatic(task_1hz, "1HZ", STACK_SIZE_WORDS, NULL, PRIORITY_1HZ, + t1hz_buffer, &t1hz_control_block); + vTaskStartScheduler(); while (true) continue; diff --git a/projects/lvcontroller/platformio.ini b/projects/lvcontroller/platformio.ini index 75d4f7ac3..078c43e40 100644 --- a/projects/lvcontroller/platformio.ini +++ b/projects/lvcontroller/platformio.ini @@ -29,6 +29,8 @@ flags = -Werror -Wall -Wno-unused-function ; having problems with unused MPU_Config() +extra_scripts = + pre:../../scripts/build/generate_git_hash.py [env:vehicle] platform = ststm32 @@ -47,6 +49,7 @@ build_flags = board_build.ldscript = src/platforms/stm32-ev6/STM32F767ZITX_FLASH.ld extra_scripts = + ${common.extra_scripts} pre:../../scripts/build/add_hardfloat.py [env:cli] @@ -56,4 +59,6 @@ build_src_filter = + + build_flags = - ${common.flags} \ No newline at end of file + ${common.flags} +extra_scripts = + ${common.extra_scripts} \ No newline at end of file diff --git a/projects/lvcontroller/src/main.cc b/projects/lvcontroller/src/main.cc index 75905c745..9951dd7f0 100644 --- a/projects/lvcontroller/src/main.cc +++ b/projects/lvcontroller/src/main.cc @@ -7,6 +7,7 @@ #include "bindings.hpp" #include "generated/can/veh_bus.hpp" #include "generated/can/veh_messages.hpp" +#include "generated/githash.hpp" #include "periph/gpio.hpp" // LV Modules @@ -210,6 +211,10 @@ void check_can_flash(void) { void task_1hz(void) { veh_can.Send(TxLvDbcHash(generated::can::kVehDbcHash)); + veh_can.Send(TxLvGitHash{ + .commit = macfe::generated::GIT_HASH, + .dirty = macfe::generated::GIT_DIRTY, + }); } void task_10hz(void) { diff --git a/projects/tms/platformio.ini b/projects/tms/platformio.ini index ab65d4105..81728ead2 100644 --- a/projects/tms/platformio.ini +++ b/projects/tms/platformio.ini @@ -29,6 +29,8 @@ flags = -Werror -Wall -Wno-unused-function ; having problems with unused MPU_Config() +extra_scripts = + pre:../../scripts/build/generate_git_hash.py [env:vehicle] platform = ststm32 @@ -52,4 +54,5 @@ custom_freertos_heap_impl = "" custom_freertos_config_location = src/platforms/stm32f767/Inc/FreeRTOSConfig.h extra_scripts = + ${common.extra_scripts} pre:../../scripts/build/add_hardfloat.py diff --git a/projects/tms/src/main.cc b/projects/tms/src/main.cc index 888a8a1b0..312b51755 100644 --- a/projects/tms/src/main.cc +++ b/projects/tms/src/main.cc @@ -8,6 +8,7 @@ #include "fan_controller/fan_controller.hpp" #include "generated/can/veh_bus.hpp" #include "generated/can/veh_messages.hpp" +#include "generated/githash.hpp" #include "periph/gpio.hpp" #include "temp_sensor/temp_sensor.hpp" @@ -16,11 +17,15 @@ #include "task.h" static const size_t STACK_SIZE_WORDS = 2048 * 16; -static const uint32_t PRIORITY_10HZ = 1; +static const uint32_t PRIORITY_10HZ = 2; +static const uint32_t PRIORITY_1HZ = 1; StaticTask_t t10hz_control_block; StackType_t t10hz_buffer[STACK_SIZE_WORDS]; +StaticTask_t t1hz_control_block; +StackType_t t1hz_buffer[STACK_SIZE_WORDS]; + using namespace generated::can; etl::array temp_sensors{ @@ -108,6 +113,22 @@ void Update(float update_period_ms) { toggle = !toggle; } +void task_1hz(void* argument) { + (void)argument; + + const uint32_t kUpdatePeriodMs = 1000; + TickType_t wake_time = xTaskGetTickCount(); + + while (true) { + veh_can_bus.Send(TxTmsGitHash{ + .commit = macfe::generated::GIT_HASH, + .dirty = macfe::generated::GIT_DIRTY, + }); + + vTaskDelayUntil(&wake_time, pdMS_TO_TICKS(kUpdatePeriodMs)); + } +} + void task_10hz(void* argument) { (void)argument; @@ -133,6 +154,9 @@ int main(void) { xTaskCreateStatic(task_10hz, "10HZ", STACK_SIZE_WORDS, NULL, PRIORITY_10HZ, t10hz_buffer, &t10hz_control_block); + xTaskCreateStatic(task_1hz, "1HZ", STACK_SIZE_WORDS, NULL, PRIORITY_1HZ, + t1hz_buffer, &t1hz_control_block); + vTaskStartScheduler(); while (true) continue; diff --git a/projects/veh.dbc b/projects/veh.dbc index 42969db86..d958c54df 100644 --- a/projects/veh.dbc +++ b/projects/veh.dbc @@ -167,7 +167,7 @@ BO_ 410 LvGitHash: 5 LVC SG_ Commit : 0|32@1+ (1,0) [0|0] "" RPI SG_ Dirty : 32|1@1+ (1,0) [0|0] "" RPI -BO_ 420 TmsGitHash: 5 LVC +BO_ 420 TmsGitHash: 5 TMS SG_ Commit : 0|32@1+ (1,0) [0|0] "" RPI SG_ Dirty : 32|1@1+ (1,0) [0|0] "" RPI diff --git a/scripts/build/generate_git_hash.py b/scripts/build/generate_git_hash.py new file mode 100644 index 000000000..16caef778 --- /dev/null +++ b/scripts/build/generate_git_hash.py @@ -0,0 +1,46 @@ +Import("env") + +import subprocess +from pathlib import Path + +include_path = Path(env.subst("$PROJECT_INCLUDE_DIR")) / "generated" + +def get_git_hash(): + # Executed command line command to retrieve a 7 character git hash + git_hash = subprocess.check_output( + ["git", "rev-parse", "--short=7", "HEAD"], + cwd=include_path + ).decode().strip() + + # Create the is_dirty variable to check if the repository has uncommitted changes + dirty_output = subprocess.check_output( + ["git", "status", "--porcelain"], + cwd=include_path + ).decode().strip() + is_dirty = bool(dirty_output) + + git_hash_int = int(git_hash, 16) & 0x0FFFFFFF + return git_hash_int, is_dirty + +def generate_git_hash_file(): + # Create the output directory path if it doesn't exist + include_path.mkdir(parents=True, exist_ok=True) + header_path = include_path / "githash.hpp" + + git_hash, is_dirty = get_git_hash() + + header_content = f"""#pragma once + +#include + +namespace macfe::generated {{ + +const uint32_t GIT_HASH = 0x{git_hash:08x}; +const bool GIT_DIRTY = {'true' if is_dirty else 'false'}; + +}} // namespace macfe::generated +""" + + header_path.write_text(header_content) + +generate_git_hash_file() \ No newline at end of file From 97696f848a58a6fb893599df4b4ae9809967d235 Mon Sep 17 00:00:00 2001 From: Manush Date: Sun, 23 Nov 2025 00:56:11 -0500 Subject: [PATCH 07/26] CCNT for data integrity and helper functions --- projects/lvcontroller/src/lvbms/lvbms.cc | 77 ++++++++++++++++++++--- projects/lvcontroller/src/lvbms/lvbms.hpp | 10 ++- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index 4d4eebfa4..a8e1fe538 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -2,6 +2,7 @@ #include "lvbms.hpp" +// #include #include #include @@ -15,6 +16,13 @@ macfe::lv::LvBms::LvBms(macfe::periph::SpiMaster& spi) : spi_(spi) {} * @note ADI1, ADI2, ADV have dummy values, so this will need to change */ +/** + * @note everytime we utilize a read command, we should check for the CCNT + * frames, and check them with our count. This will tell us if any commands may + * have been ignored. Increment expected CCNT each time a write function is + * called + **/ + enum class commandCode : uint16_t { SRST = 0x27, RSTCC = 0x2E, @@ -71,12 +79,27 @@ enum class commandCode : uint16_t { RDALLC = 0x10, }; -bool LvBms::resetCommandCounter() { - uint16_t cmd = static_cast(commandCode::RSTCC); - uint8_t hi = (cmd >> 8) & 0xFF; // RSTCC command (top half) - uint8_t lo = cmd & 0xFF; // low half - uint8_t data[6]; - return sendReadCmd(hi, lo, data); +// Helper Functions ----------------------------------------------------- +std::array LvBms::splitMessage(uint16_t cmd) { + uint8_t hi = (cmd >> 8) & 0xFF; + uint8_t lo = cmd & 0xFF; + + std::array split = {hi, lo}; + return split; +} + +void LvBms::sendControlCmd(std::array cmd) { + uint8_t tx[4] = {0}; + tx[0] = cmd[0]; // HI + tx[1] = cmd[1]; // LO + // Command PEC for transmit + uint16_t pec = commandPec15(tx, 2); + tx[2] = (pec >> 8) & 0xFF; + tx[3] = pec & 0xFF; + + spi_.Transmit(tx, 4); + + expectedCCNT = (expectedCCNT % 63) + 1; } bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { @@ -100,13 +123,19 @@ bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { spi_.TransmitReceive(tx, rx, 12); - // Copy recieved data bytes - std::memcpy(out, &rx[4], 6); + expectedCCNT = (expectedCCNT % 63) + 1; // update after every transmit // Check returned PEC10 uint16_t rxPec = (rx[10] << 8) | rx[11]; + + // CCNT and PEC must be identical + actualCCNT = (rx[10] >> 2) & 0x3F; + uint16_t calcPec = dataPec10(&rx[4], 6); + if (actualCCNT != expectedCCNT) { + // maybe define a special message back to lv controller + } // return true if dpec matches return (rxPec == calcPec); } @@ -172,4 +201,36 @@ uint16_t LvBms::dataPec10(const uint8_t* data, int len) const { } return rem; } + +// ------------------------------------------------------------------------------------ + +void LvBms::resetCommandCounter() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RSTCC)); + expectedCCNT = 0; // reset + sendControlCmd(cmd); +} + +void LvBms::freezeResultRegisters() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::SNAP)); + + sendControlCmd(cmd); +} + +void LvBms::unfreezeResultRegisters() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::UNSNAP)); + + uint8_t tx[4] = {0}; + tx[0] = cmd[0]; // HI + tx[1] = cmd[1]; // LO + // Command PEC for transmit + uint16_t pec = commandPec15(tx, 2); + tx[2] = (pec >> 8) & 0xFF; + tx[3] = pec & 0xFF; + + spi_.Transmit(tx, 4); +} + } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index 0af291651..014f5a85e 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include namespace macfe::periph { @@ -13,12 +14,19 @@ class LvBms { bool init(); bool reset(); - bool resetCommandCounter(); // RSTCC + void resetCommandCounter(); // RSTCC + void freezeResultRegisters(); // SNAP + void unfreezeResultRegisters(); // UNSNAP private: macfe::periph::SpiMaster& spi_; + uint8_t expectedCCNT = 0; // tracks expected command counter + uint8_t actualCCNT = 0; + bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]); bool sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]); + void sendControlCmd(std::array cmd); + std::array splitMessage(uint16_t cmd); uint16_t commandPec15(const uint8_t* data, int len) const; uint16_t dataPec10(const uint8_t* data, int len) const; From 29ffb03f83228c5640980dd661b3bc4b6f84cf13 Mon Sep 17 00:00:00 2001 From: Blake Freer <59676067+BlakeFreer@users.noreply.github.com> Date: Fri, 5 Dec 2025 13:59:54 -0500 Subject: [PATCH 08/26] cobs library (#547) --- lib/cobs/.gitignore | 5 ++ lib/cobs/cobs.cpp | 69 ++++++++++++++++ lib/cobs/cobs.hpp | 25 ++++++ lib/cobs/platformio.ini | 9 +++ lib/cobs/test/cobs_test.cpp | 157 ++++++++++++++++++++++++++++++++++++ 5 files changed, 265 insertions(+) create mode 100644 lib/cobs/.gitignore create mode 100644 lib/cobs/cobs.cpp create mode 100644 lib/cobs/cobs.hpp create mode 100644 lib/cobs/platformio.ini create mode 100644 lib/cobs/test/cobs_test.cpp diff --git a/lib/cobs/.gitignore b/lib/cobs/.gitignore new file mode 100644 index 000000000..89cc49cbd --- /dev/null +++ b/lib/cobs/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/lib/cobs/cobs.cpp b/lib/cobs/cobs.cpp new file mode 100644 index 000000000..adcc47d3c --- /dev/null +++ b/lib/cobs/cobs.cpp @@ -0,0 +1,69 @@ +#include "cobs.hpp" + +#include +#include + +namespace amber::cobs { + +Decoder::Decoder(uint8_t* buffer) + : buffer(buffer), length(0), block_remaining_(0), code_(0xff) {} + +bool Decoder::Decode(const uint8_t* encoded, size_t encoded_length) { + // adapted from + // https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing + // differences: supports procedural decoding / does not require `encoded` to + // contain the entire message + + const uint8_t* input_cursor = encoded; + + for (; input_cursor < encoded + encoded_length; --block_remaining_) { + if (block_remaining_ > 0) { + buffer[length++] = *input_cursor++; + } else { + block_remaining_ = *input_cursor++; + if (block_remaining_ == 0) { + return true; + } + if (code_ != 0xff) { + buffer[length++] = 0; + } + code_ = block_remaining_; + } + } + return false; +} + +constexpr size_t MaxEncodedLength(size_t raw_length) { + constexpr size_t LEADING_ZERO = 1; + constexpr size_t TERMINATING_ZERO = 1; + size_t MAX_STUFF_BYTES = (raw_length + 253) / 254; + return LEADING_ZERO + raw_length + MAX_STUFF_BYTES + TERMINATING_ZERO; +} + +size_t Encode(const uint8_t* raw, size_t length, uint8_t* output) { + uint8_t* encode_cursor = output; + + uint8_t zero_offset = 1; + uint8_t* zero_offset_p = encode_cursor++; + + for (const uint8_t* byte = raw; length--; ++byte) { + if (*byte != 0) { + *encode_cursor++ = *byte; + ++zero_offset; + } + if ((*byte == 0) || (zero_offset == 0xff)) { + *zero_offset_p = zero_offset; + zero_offset = 1; + zero_offset_p = encode_cursor; + if ((*byte == 0) || (length > 0)) { + ++encode_cursor; + } + } + } + *zero_offset_p = zero_offset; // write the final zero_offset value + *encode_cursor++ = 0; // write delimiter + + return size_t(encode_cursor - output); +} + +} // namespace amber::cobs diff --git a/lib/cobs/cobs.hpp b/lib/cobs/cobs.hpp new file mode 100644 index 000000000..4b51afcde --- /dev/null +++ b/lib/cobs/cobs.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace amber::cobs { + +class Decoder { +public: + Decoder(uint8_t* buffer); + bool Decode(const uint8_t* encoded, size_t encoded_length); + + uint8_t* buffer; + size_t length; + +private: + uint8_t block_remaining_; + uint8_t code_; +}; + +constexpr size_t MaxEncodedLength(size_t raw_length); + +size_t Encode(const uint8_t* raw, size_t length, uint8_t* output); + +} // namespace amber::cobs \ No newline at end of file diff --git a/lib/cobs/platformio.ini b/lib/cobs/platformio.ini new file mode 100644 index 000000000..fc09d0803 --- /dev/null +++ b/lib/cobs/platformio.ini @@ -0,0 +1,9 @@ +[platformio] +include_dir = . +src_dir = . + +[env:native] +test_framework = googletest +platform = native +test_build_src = true +build_src_filter = + \ No newline at end of file diff --git a/lib/cobs/test/cobs_test.cpp b/lib/cobs/test/cobs_test.cpp new file mode 100644 index 000000000..bcc0bc859 --- /dev/null +++ b/lib/cobs/test/cobs_test.cpp @@ -0,0 +1,157 @@ +#include "cobs.hpp" + +#include "gtest/gtest.h" + +using namespace amber::cobs; + +void EXPECT_ARRAY_EQUAL(uint8_t* expected, size_t expected_len, uint8_t* actual, + size_t actual_len) { + ASSERT_EQ(expected_len, actual_len); + for (size_t i = 0; i < expected_len; i++) { + EXPECT_EQ(expected[i], actual[i]) << "Differ at index " << i; + } +} + +TEST(COBS, ZeroString) { + uint8_t decoded[] = {0x00}; + size_t dec_length = sizeof(decoded) / sizeof(decoded[0]); + + uint8_t encoded[] = {0x01, 0x01, 0x00}; + size_t enc_length = sizeof(encoded) / sizeof(encoded[0]); + + uint8_t enc_output[256]; + size_t enc_length_actual = Encode(decoded, dec_length, enc_output); + EXPECT_ARRAY_EQUAL(encoded, enc_length, enc_output, enc_length_actual); + + uint8_t dec_output[256]; + Decoder decoder(dec_output); + EXPECT_TRUE(decoder.Decode(encoded, enc_length)); + EXPECT_ARRAY_EQUAL(decoded, dec_length, decoder.buffer, decoder.length); +} + +TEST(COBS, EmptyString) { + uint8_t decoded[] = {}; + size_t dec_length = sizeof(decoded) / sizeof(decoded[0]); + + uint8_t encoded[] = {0x01, 0x00}; + size_t enc_length = sizeof(encoded) / sizeof(encoded[0]); + + uint8_t enc_output[256]; + size_t enc_length_actual = Encode(decoded, dec_length, enc_output); + EXPECT_ARRAY_EQUAL(encoded, enc_length, enc_output, enc_length_actual); + + uint8_t dec_output[256]; + Decoder decoder(dec_output); + EXPECT_TRUE(decoder.Decode(encoded, enc_length)); + EXPECT_ARRAY_EQUAL(decoded, dec_length, decoder.buffer, decoder.length); +} + +TEST(COBS, Short) { + uint8_t decoded[] = {0x11, 0x22, 0x00, 0x33}; + size_t dec_length = sizeof(decoded) / sizeof(decoded[0]); + + uint8_t encoded[] = {0x03, 0x11, 0x22, 0x02, 0x33, 0x00}; + size_t enc_length = sizeof(encoded) / sizeof(encoded[0]); + + uint8_t enc_output[256]; + size_t enc_length_actual = Encode(decoded, dec_length, enc_output); + EXPECT_ARRAY_EQUAL(encoded, enc_length, enc_output, enc_length_actual); + + uint8_t dec_output[256]; + Decoder decoder(dec_output); + EXPECT_TRUE(decoder.Decode(encoded, enc_length)); + EXPECT_ARRAY_EQUAL(decoded, dec_length, decoder.buffer, decoder.length); +} + +TEST(COBS, ManyZeros) { + uint8_t decoded[] = {0x11, 0x00, 0x00, 0x00}; + size_t dec_length = sizeof(decoded) / sizeof(decoded[0]); + + uint8_t encoded[] = {0x02, 0x11, 0x01, 0x01, 0x01, 0x00}; + size_t enc_length = sizeof(encoded) / sizeof(encoded[0]); + + uint8_t enc_output[256]; + size_t enc_length_actual = Encode(decoded, dec_length, enc_output); + EXPECT_ARRAY_EQUAL(encoded, enc_length, enc_output, enc_length_actual); + + uint8_t dec_output[256]; + Decoder decoder(dec_output); + EXPECT_TRUE(decoder.Decode(encoded, enc_length)); + EXPECT_ARRAY_EQUAL(decoded, dec_length, decoder.buffer, decoder.length); +} + +TEST(COBS, FFBlock) { + uint8_t decoded[] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, + 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, + 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, + 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, + 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, + 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, + 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, + 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, + 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, + 0xfd, 0xfe, 0xff}; + size_t dec_length = sizeof(decoded) / sizeof(decoded[0]); + + uint8_t encoded[] = { + 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, + 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, + 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0x02, 0xff, 0x00}; + size_t enc_length = sizeof(encoded) / sizeof(encoded[0]); + + uint8_t enc_output[512]; + size_t enc_length_actual = Encode(decoded, dec_length, enc_output); + EXPECT_ARRAY_EQUAL(encoded, enc_length, enc_output, enc_length_actual); + + uint8_t dec_output[512] = {0}; + Decoder decoder(dec_output); + EXPECT_TRUE(decoder.Decode(encoded, enc_length)); + EXPECT_ARRAY_EQUAL(decoded, dec_length, decoder.buffer, decoder.length); + + // Test partial decoding + uint8_t dec_output2[512] = {0}; + decoder = Decoder(dec_output2); + size_t i; + for (i = 0; i < enc_length - 50; i += 10) { + EXPECT_FALSE(decoder.Decode(encoded + i, 10)); + } + EXPECT_TRUE(decoder.Decode(encoded + i, enc_length - i)); + EXPECT_ARRAY_EQUAL(decoded, dec_length, decoder.buffer, decoder.length); +} + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file From d99628415c00d4e2509d38323b0ce2e3799d2aca Mon Sep 17 00:00:00 2001 From: Blake Freer <59676067+BlakeFreer@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:18:59 -0500 Subject: [PATCH 09/26] rename cobs library (#548) --- lib/cobs/cobs.cpp | 4 ++-- lib/cobs/cobs.hpp | 4 ++-- lib/cobs/test/cobs_test.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/cobs/cobs.cpp b/lib/cobs/cobs.cpp index adcc47d3c..4f00e194a 100644 --- a/lib/cobs/cobs.cpp +++ b/lib/cobs/cobs.cpp @@ -3,7 +3,7 @@ #include #include -namespace amber::cobs { +namespace macfe::cobs { Decoder::Decoder(uint8_t* buffer) : buffer(buffer), length(0), block_remaining_(0), code_(0xff) {} @@ -66,4 +66,4 @@ size_t Encode(const uint8_t* raw, size_t length, uint8_t* output) { return size_t(encode_cursor - output); } -} // namespace amber::cobs +} // namespace macfe::cobs diff --git a/lib/cobs/cobs.hpp b/lib/cobs/cobs.hpp index 4b51afcde..6efe27ef1 100644 --- a/lib/cobs/cobs.hpp +++ b/lib/cobs/cobs.hpp @@ -3,7 +3,7 @@ #include #include -namespace amber::cobs { +namespace macfe::cobs { class Decoder { public: @@ -22,4 +22,4 @@ constexpr size_t MaxEncodedLength(size_t raw_length); size_t Encode(const uint8_t* raw, size_t length, uint8_t* output); -} // namespace amber::cobs \ No newline at end of file +} // namespace macfe::cobs \ No newline at end of file diff --git a/lib/cobs/test/cobs_test.cpp b/lib/cobs/test/cobs_test.cpp index bcc0bc859..a18232848 100644 --- a/lib/cobs/test/cobs_test.cpp +++ b/lib/cobs/test/cobs_test.cpp @@ -2,7 +2,7 @@ #include "gtest/gtest.h" -using namespace amber::cobs; +using namespace macfe::cobs; void EXPECT_ARRAY_EQUAL(uint8_t* expected, size_t expected_len, uint8_t* actual, size_t actual_len) { From 0181eaac5184af6c4fdab5377440ba07a12771ed Mon Sep 17 00:00:00 2001 From: Blake Freer <59676067+BlakeFreer@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:37:56 -0500 Subject: [PATCH 10/26] Disable pre-commit static analysis (#549) Takes way to long. We should not be checking static analysis in precommit since it takes several minutes. This should only be a github check --- .pre-commit-config.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7fb6ed2d0..6571a0f72 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,11 +16,3 @@ repos: - id: clang-format require_serial: true stages: [pre-commit] - - - repo: local - hooks: - - id: pio-check - name: Platform IO Static Analysis - entry: bash scripts/static_analysis/static_analysis.sh - language: system - stages: [pre-commit] \ No newline at end of file From 6953bc0d319f7f55af9b216a5210891b0d46047e Mon Sep 17 00:00:00 2001 From: Manush Date: Thu, 18 Dec 2025 23:11:02 -0500 Subject: [PATCH 11/26] more read functions --- projects/lvcontroller/src/lvbms/lvbms.cc | 101 +++++++++++++++++++--- projects/lvcontroller/src/lvbms/lvbms.hpp | 47 +++++++++- 2 files changed, 136 insertions(+), 12 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index a8e1fe538..7db1139a3 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -102,7 +102,8 @@ void LvBms::sendControlCmd(std::array cmd) { expectedCCNT = (expectedCCNT % 63) + 1; } -bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { +bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6], + bool measurementRead) { // Read Command: Command Bytes + Command PEC // Frame 1: Sends Read Command @@ -123,8 +124,6 @@ bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { spi_.TransmitReceive(tx, rx, 12); - expectedCCNT = (expectedCCNT % 63) + 1; // update after every transmit - // Check returned PEC10 uint16_t rxPec = (rx[10] << 8) | rx[11]; @@ -136,6 +135,17 @@ bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { if (actualCCNT != expectedCCNT) { // maybe define a special message back to lv controller } + + if (measurementRead) { // CCNT check + if (expectedCCNT != actualCCNT) { + return false; + } + if (rxPec != calcPec) { + return false; + } + expectedCCNT = (expectedCCNT % 63) + 1; + } + // return true if dpec matches return (rxPec == calcPec); } @@ -204,6 +214,14 @@ uint16_t LvBms::dataPec10(const uint8_t* data, int len) const { // ------------------------------------------------------------------------------------ +// Command Codes +void LvBms::softwareReset() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::SRST)); + // Look for expected CCNT Reset + sendControlCmd(cmd); +} + void LvBms::resetCommandCounter() { std::array cmd = LvBms::splitMessage(uint16_t(commandCode::RSTCC)); @@ -221,16 +239,77 @@ void LvBms::freezeResultRegisters() { void LvBms::unfreezeResultRegisters() { std::array cmd = LvBms::splitMessage(uint16_t(commandCode::UNSNAP)); + sendControlCmd(cmd); +} - uint8_t tx[4] = {0}; - tx[0] = cmd[0]; // HI - tx[1] = cmd[1]; // LO - // Command PEC for transmit - uint16_t pec = commandPec15(tx, 2); - tx[2] = (pec >> 8) & 0xFF; - tx[3] = pec & 0xFF; +void LvBms::clearIxVBxADCResults() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRI)); - spi_.Transmit(tx, 4); + sendControlCmd(cmd); +} + +void LvBms::clearAccumalators() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRA)); + + sendControlCmd(cmd); +} + +void LvBms::clearVxADCResults() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRVX)); + + sendControlCmd(cmd); +} + +void LvBms::clearOCxADCResults() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRO)); + + sendControlCmd(cmd); +} +// -------------------------------------------------------- + +// Write Functions +void LvBms::CLRFLAG() { + /* + Used to reset individual bits in the flag register. This write function, + like all, require 6 bytes sent back. Any bit of the data that is set to 1 + clears the related flag + */ + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRFLAG)); + uint8_t output[6]; + sendWriteCmd(cmd[0], cmd[1], output); +} + +// Read Functions + +// non measurement read will pass in false -> will not increment CCNT + +void LvBms::RDFLAG() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRFLAG)); + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output, false); + + // Fault and Status Information is read from this command +} + +void LvBms::RDSTAT() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDSTAT)); + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output, false); +} + +void LvBms::RDI() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDI)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output, false); } } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index 014f5a85e..0cc5b55ce 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -14,16 +14,61 @@ class LvBms { bool init(); bool reset(); + + // Command Codes + void softwareReset(); void resetCommandCounter(); // RSTCC void freezeResultRegisters(); // SNAP void unfreezeResultRegisters(); // UNSNAP + void clearIxVBxADCResults(); // CLRI + void clearAccumalators(); // CLRA + void clearVxADCResults(); // CLRVX + void clearOCxADCResults(); // CLRO + + // Read Commands + // ----------------------- + void RDFLAG(); + void RDSTAT(); + void RDI(); + void RDV8(); + void RDIACC(); + void RDVBACC(); + void RDIVB1(); + void RDIB1ACC(); + + // ADCV1 + void RDV1A(); + void RDV1B(); + void RDV1C(); + void RDV1D(); + + // ADCV2 + void RDV2A(); + void RDV2B(); + void RDV2C(); + void RDV2D(); + void RDV2E(); + + // AUX ADC + void RDXA(); + void RDXB(); + void RDXC(); + + void RDSID(); + void RDCFGA(); + void RDCFGB(); + + // Write Commands + // ----------------------- + void CLRFLAG(); private: macfe::periph::SpiMaster& spi_; uint8_t expectedCCNT = 0; // tracks expected command counter uint8_t actualCCNT = 0; - bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]); + bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6], + bool measurementRead); bool sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]); void sendControlCmd(std::array cmd); std::array splitMessage(uint16_t cmd); From a2b4b60041c156ed139f681c8ad10974a52b85dc Mon Sep 17 00:00:00 2001 From: Manush Date: Sat, 20 Dec 2025 22:01:19 -0500 Subject: [PATCH 12/26] fix CCNT misunderstanding --- projects/lvcontroller/src/lvbms/lvbms.cc | 45 +++++++++++++---------- projects/lvcontroller/src/lvbms/lvbms.hpp | 7 +++- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index 7db1139a3..ff50fee17 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -19,8 +19,8 @@ macfe::lv::LvBms::LvBms(macfe::periph::SpiMaster& spi) : spi_(spi) {} /** * @note everytime we utilize a read command, we should check for the CCNT * frames, and check them with our count. This will tell us if any commands may - * have been ignored. Increment expected CCNT each time a write function is - * called + * have been ignored. Increment expected CCNT each time a write or command + * function has been called **/ enum class commandCode : uint16_t { @@ -98,12 +98,11 @@ void LvBms::sendControlCmd(std::array cmd) { tx[3] = pec & 0xFF; spi_.Transmit(tx, 4); - - expectedCCNT = (expectedCCNT % 63) + 1; + expectedCCNT = + (expectedCCNT % 63) + 1; // Increment CCNT for Command Functions } -bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6], - bool measurementRead) { +bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { // Read Command: Command Bytes + Command PEC // Frame 1: Sends Read Command @@ -134,16 +133,12 @@ bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6], if (actualCCNT != expectedCCNT) { // maybe define a special message back to lv controller + // From now on, these values won't be the same... this could flood our + // can bus } - - if (measurementRead) { // CCNT check - if (expectedCCNT != actualCCNT) { - return false; - } - if (rxPec != calcPec) { - return false; - } - expectedCCNT = (expectedCCNT % 63) + 1; + if (rxPec != calcPec) { + // send back a specific message that the message was not sent through, + // this would be a rare instance since PEC is pretty good } // return true if dpec matches @@ -173,6 +168,10 @@ bool LvBms::sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]) { // Sends all 12 bytes spi_.Transmit(tx, 12); + + expectedCCNT = (expectedCCNT % 63) + 1; // Increment CCNT for Write + // Commands + return true; } @@ -290,9 +289,9 @@ void LvBms::CLRFLAG() { void LvBms::RDFLAG() { std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::CLRFLAG)); + LvBms::splitMessage(uint16_t(commandCode::RDFLAG)); uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output, false); + sendReadCmd(cmd[0], cmd[1], output); // Fault and Status Information is read from this command } @@ -301,7 +300,7 @@ void LvBms::RDSTAT() { std::array cmd = LvBms::splitMessage(uint16_t(commandCode::RDSTAT)); uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output, false); + sendReadCmd(cmd[0], cmd[1], output); } void LvBms::RDI() { @@ -309,7 +308,15 @@ void LvBms::RDI() { LvBms::splitMessage(uint16_t(commandCode::RDI)); uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output, false); + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV8() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDI)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); } } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index 0cc5b55ce..23f07ddbe 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -57,18 +57,21 @@ class LvBms { void RDSID(); void RDCFGA(); void RDCFGB(); + void RDCOMM(); // Write Commands // ----------------------- void CLRFLAG(); + void WRCFGA(); + void WRCFGB(); + void WRCOMM(); private: macfe::periph::SpiMaster& spi_; uint8_t expectedCCNT = 0; // tracks expected command counter uint8_t actualCCNT = 0; - bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6], - bool measurementRead); + bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]); bool sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]); void sendControlCmd(std::array cmd); std::array splitMessage(uint16_t cmd); From e4bee93d9ee7764adfd644286edf8e77feeeb075 Mon Sep 17 00:00:00 2001 From: Manush Date: Sat, 20 Dec 2025 22:22:30 -0500 Subject: [PATCH 13/26] idk gang more commands --- projects/lvcontroller/src/lvbms/lvbms.cc | 172 +++++++++++++++++++++- projects/lvcontroller/src/lvbms/lvbms.hpp | 5 +- 2 files changed, 173 insertions(+), 4 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index ff50fee17..fbdfba78d 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -311,9 +311,177 @@ void LvBms::RDI() { sendReadCmd(cmd[0], cmd[1], output); } -void LvBms::RDV8() { +void LvBms::RDVB() { std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDI)); + LvBms::splitMessage(uint16_t(commandCode::RDVB)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDIACC() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDIACC)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDVBACC() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDIACC)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDIVB1() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDIVB1)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDIVB1ACC() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDIVB1ACC)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV1A() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV1A)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV1B() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV1B)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV1C() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV1C)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV1D() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV1D)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV2A() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV2A)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV2B() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV2B)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV2C() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV2C)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV2D() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV2D)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV2E() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV2E)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDXA() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDXA)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDXB() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDXB)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDXC() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDXC)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDOC() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDOC)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDSID() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDSID)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDCFGA() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDCFGA)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDCFGB() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDCFGB)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDCOMM() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDCOMM)); uint8_t output[6]; sendReadCmd(cmd[0], cmd[1], output); diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index 23f07ddbe..c6248b555 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -30,11 +30,11 @@ class LvBms { void RDFLAG(); void RDSTAT(); void RDI(); - void RDV8(); + void RDVB(); void RDIACC(); void RDVBACC(); void RDIVB1(); - void RDIB1ACC(); + void RDIVB1ACC(); // ADCV1 void RDV1A(); @@ -53,6 +53,7 @@ class LvBms { void RDXA(); void RDXB(); void RDXC(); + void RDOC(); void RDSID(); void RDCFGA(); From 0eba844e398bd6a3479f8013d8ddd87c77052f3e Mon Sep 17 00:00:00 2001 From: Manush Date: Fri, 26 Dec 2025 01:10:24 -0500 Subject: [PATCH 14/26] removed unneccessary functions, simplified implementation --- projects/lvcontroller/src/lvbms/lvbms.cc | 353 +--------------------- projects/lvcontroller/src/lvbms/lvbms.hpp | 117 +++---- projects/lvcontroller/src/main.cc | 1 - 3 files changed, 70 insertions(+), 401 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index fbdfba78d..a9201bb15 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -23,62 +23,6 @@ macfe::lv::LvBms::LvBms(macfe::periph::SpiMaster& spi) : spi_(spi) {} * function has been called **/ -enum class commandCode : uint16_t { - SRST = 0x27, - RSTCC = 0x2E, - SNAP = 0x2D, - UNSNAP = 0x2F, - ADI1 = 0x00, - ADI2 = 0x00, - ADV = 0x00, - ADX = 0x530, - CLRI = 0x711, - CLRA = 0x714, - CLRVX = 0x712, - CLRO = 0x713, - CLRFLAG = 0x717, - RDFLAG = 0x72, // Includes ERR code, set to 1 - RDSTAT = 0x34, - RDI = 0x12, - RDVB = 0x13, - RDIACC = 0x44, - RDVBACC = 0x46, - RDIVB1 = 0x14, - RDIVB1ACC = 0x48, - RDV1A = 0xA, - RDV1B = 0x9, - RDV1C = 0x3, - RDV1D = 0xB, - RDV2A = 0x7, - RDV2B = 0xD, - RDV2C = 0x5, - RDV2D = 0x1F, - RDV2E = 0x25, - RDXA = 0x30, - RDXB = 0x31, - RDXC = 0x33, - RDOC = 0xB, - RDSID = 0x2C, - RDCFGA = 0x2, - RDCFGB = 0x26, - RDCOMM = 0x722, - WRCFGA = 0x1, - WRCFGB = 0x24, - WRCOMM = 0x721, - STCOMM = 0x723, - PLADC = 0x718, - PLI1 = 0x71C, - PLI2 = 0x71D, - PLV = 0x71E, - PLX = 0x71F, - RDALLI = 0xC, - RDALLA = 0x4C, - RDALLV = 0x35, - RDALLR = 0x11, - RDALLX = 0x51, - RDALLC = 0x10, -}; - // Helper Functions ----------------------------------------------------- std::array LvBms::splitMessage(uint16_t cmd) { uint8_t hi = (cmd >> 8) & 0xFF; @@ -88,7 +32,8 @@ std::array LvBms::splitMessage(uint16_t cmd) { return split; } -void LvBms::sendControlCmd(std::array cmd) { +void LvBms::sendControlCmd(LvBms::commandCode cmdCode) { + std::array cmd = LvBms::splitMessage(uint16_t(cmdCode)); uint8_t tx[4] = {0}; tx[0] = cmd[0]; // HI tx[1] = cmd[1]; // LO @@ -102,15 +47,15 @@ void LvBms::sendControlCmd(std::array cmd) { (expectedCCNT % 63) + 1; // Increment CCNT for Command Functions } -bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { +bool LvBms::sendReadCmd(LvBms::commandCode cmdCode) { // Read Command: Command Bytes + Command PEC - + std::array cmd = LvBms::splitMessage(uint16_t(cmdCode)); // Frame 1: Sends Read Command uint8_t tx[12] = {0}; uint8_t rx[12] = {0}; - tx[0] = hi; - tx[1] = lo; + tx[0] = cmd[0]; + tx[1] = cmd[1]; // Compute PEC15 for command uint16_t pec = commandPec15(tx, 2); @@ -145,13 +90,13 @@ bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { return (rxPec == calcPec); } -bool LvBms::sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]) { +bool LvBms::sendWriteCmd(LvBms::commandCode cmdCode, const uint8_t data[6]) { // Write Command: 2 Command + 2 PEC15 + 6 Data + 2 PEC10 = 12 bytes - + std::array cmd = LvBms::splitMessage(uint16_t(cmdCode)); // Command bytes uint8_t tx[12]; - tx[0] = hi; - tx[1] = lo; + tx[0] = cmd[0]; + tx[1] = cmd[1]; // Command PEC15 bytes uint16_t pec = commandPec15(tx, 2); @@ -198,7 +143,7 @@ uint16_t LvBms::commandPec15(const uint8_t* data, int len) const { uint16_t LvBms::dataPec10(const uint8_t* data, int len) const { uint16_t rem = 0x20; // 00000010000 (10-bit initial value) constexpr uint16_t msbMask = 0x0200; // bit9 - constexpr uint16_t poly = 0x2D1; // x^10 + x^7 + x^5 + x^4 + x^3 + 1 + constexpr uint16_t poly = 0x8F; // x^10 + x^7 + x^3 + x^2 + x^1 + 1 for (int i = 0; i < len; i++) { rem ^= static_cast(data[i]) << 2; for (int b = 0; b < 8; b++) { @@ -211,280 +156,4 @@ uint16_t LvBms::dataPec10(const uint8_t* data, int len) const { return rem; } -// ------------------------------------------------------------------------------------ - -// Command Codes -void LvBms::softwareReset() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::SRST)); - // Look for expected CCNT Reset - sendControlCmd(cmd); -} - -void LvBms::resetCommandCounter() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RSTCC)); - expectedCCNT = 0; // reset - sendControlCmd(cmd); -} - -void LvBms::freezeResultRegisters() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::SNAP)); - - sendControlCmd(cmd); -} - -void LvBms::unfreezeResultRegisters() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::UNSNAP)); - sendControlCmd(cmd); -} - -void LvBms::clearIxVBxADCResults() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::CLRI)); - - sendControlCmd(cmd); -} - -void LvBms::clearAccumalators() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::CLRA)); - - sendControlCmd(cmd); -} - -void LvBms::clearVxADCResults() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::CLRVX)); - - sendControlCmd(cmd); -} - -void LvBms::clearOCxADCResults() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::CLRO)); - - sendControlCmd(cmd); -} -// -------------------------------------------------------- - -// Write Functions -void LvBms::CLRFLAG() { - /* - Used to reset individual bits in the flag register. This write function, - like all, require 6 bytes sent back. Any bit of the data that is set to 1 - clears the related flag - */ - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::CLRFLAG)); - uint8_t output[6]; - sendWriteCmd(cmd[0], cmd[1], output); -} - -// Read Functions - -// non measurement read will pass in false -> will not increment CCNT - -void LvBms::RDFLAG() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDFLAG)); - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); - - // Fault and Status Information is read from this command -} - -void LvBms::RDSTAT() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDSTAT)); - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDI() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDI)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDVB() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDVB)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDIACC() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDIACC)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDVBACC() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDIACC)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDIVB1() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDIVB1)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDIVB1ACC() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDIVB1ACC)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDV1A() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDV1A)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDV1B() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDV1B)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDV1C() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDV1C)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDV1D() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDV1D)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDV2A() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDV2A)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDV2B() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDV2B)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDV2C() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDV2C)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDV2D() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDV2D)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDV2E() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDV2E)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDXA() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDXA)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDXB() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDXB)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDXC() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDXC)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDOC() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDOC)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDSID() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDSID)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDCFGA() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDCFGA)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDCFGB() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDCFGB)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - -void LvBms::RDCOMM() { - std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDCOMM)); - - uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output); -} - } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index c6248b555..dcdfeb3f8 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -9,72 +9,73 @@ class SpiMaster; namespace macfe::lv { class LvBms { + enum class commandCode : uint16_t { + SRST = 0x27, + RSTCC = 0x2E, + SNAP = 0x2D, + UNSNAP = 0x2F, + ADI1 = 0x00, + ADI2 = 0x00, + ADV = 0x00, + ADX = 0x530, + CLRI = 0x711, + CLRA = 0x714, + CLRVX = 0x712, + CLRO = 0x713, + CLRFLAG = 0x717, + RDFLAG = 0x72, // Includes ERR code, set to 1 + RDSTAT = 0x34, + RDI = 0x12, + RDVB = 0x13, + RDIACC = 0x44, + RDVBACC = 0x46, + RDIVB1 = 0x14, + RDIVB1ACC = 0x48, + RDV1A = 0xA, + RDV1B = 0x9, + RDV1C = 0x3, + RDV1D = 0xB, + RDV2A = 0x7, + RDV2B = 0xD, + RDV2C = 0x5, + RDV2D = 0x1F, + RDV2E = 0x25, + RDXA = 0x30, + RDXB = 0x31, + RDXC = 0x33, + RDOC = 0xB, + RDSID = 0x2C, + RDCFGA = 0x2, + RDCFGB = 0x26, + RDCOMM = 0x722, + WRCFGA = 0x1, + WRCFGB = 0x24, + WRCOMM = 0x721, + STCOMM = 0x723, + PLADC = 0x718, + PLI1 = 0x71C, + PLI2 = 0x71D, + PLV = 0x71E, + PLX = 0x71F, + RDALLI = 0xC, + RDALLA = 0x4C, + RDALLV = 0x35, + RDALLR = 0x11, + RDALLX = 0x51, + RDALLC = 0x10, + }; + public: LvBms(macfe::periph::SpiMaster&); - bool init(); - bool reset(); - - // Command Codes - void softwareReset(); - void resetCommandCounter(); // RSTCC - void freezeResultRegisters(); // SNAP - void unfreezeResultRegisters(); // UNSNAP - void clearIxVBxADCResults(); // CLRI - void clearAccumalators(); // CLRA - void clearVxADCResults(); // CLRVX - void clearOCxADCResults(); // CLRO - - // Read Commands - // ----------------------- - void RDFLAG(); - void RDSTAT(); - void RDI(); - void RDVB(); - void RDIACC(); - void RDVBACC(); - void RDIVB1(); - void RDIVB1ACC(); - - // ADCV1 - void RDV1A(); - void RDV1B(); - void RDV1C(); - void RDV1D(); - - // ADCV2 - void RDV2A(); - void RDV2B(); - void RDV2C(); - void RDV2D(); - void RDV2E(); - - // AUX ADC - void RDXA(); - void RDXB(); - void RDXC(); - void RDOC(); - - void RDSID(); - void RDCFGA(); - void RDCFGB(); - void RDCOMM(); - - // Write Commands - // ----------------------- - void CLRFLAG(); - void WRCFGA(); - void WRCFGB(); - void WRCOMM(); - private: macfe::periph::SpiMaster& spi_; uint8_t expectedCCNT = 0; // tracks expected command counter uint8_t actualCCNT = 0; - bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]); - bool sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]); - void sendControlCmd(std::array cmd); + bool sendReadCmd(commandCode cmdCode); + bool sendWriteCmd(commandCode cmdCode, const uint8_t data[6]); + void sendControlCmd(commandCode cmdCode); std::array splitMessage(uint16_t cmd); uint16_t commandPec15(const uint8_t* data, int len) const; diff --git a/projects/lvcontroller/src/main.cc b/projects/lvcontroller/src/main.cc index 36c9e835e..dd57dad9d 100644 --- a/projects/lvcontroller/src/main.cc +++ b/projects/lvcontroller/src/main.cc @@ -251,7 +251,6 @@ int main(void) { macfe::periph::SpiMaster spi; macfe::lv::LvBms bms(spi); - bms.resetCommandCounter(); scheduler::register_task(task_100hz, 10); scheduler::register_task(task_10hz, 100); From 5384a4704a61f3632dea265e8cb440db8230d384 Mon Sep 17 00:00:00 2001 From: Manush Date: Fri, 9 Jan 2026 18:00:42 -0500 Subject: [PATCH 15/26] failure cases for PEC and CCNT --- projects/lvcontroller/src/lvbms/lvbms.cc | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index a9201bb15..231777c66 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -77,13 +77,24 @@ bool LvBms::sendReadCmd(LvBms::commandCode cmdCode) { uint16_t calcPec = dataPec10(&rx[4], 6); if (actualCCNT != expectedCCNT) { - // maybe define a special message back to lv controller - // From now on, these values won't be the same... this could flood our - // can bus + // resend the message 5 times, but each time check if CCNT has + // increased, indicating a message sent + uint8_t prevCCNT = actualCCNT; + for (int i = 0; i < 5; i++) { + spi_.TransmitReceive(tx, rx, 12); + rxPec = (rx[10] << 8) | rx[11]; + actualCCNT = (rx[10] >> 2) & 0x3F; + calcPec = dataPec10(&rx[4], 6); + if (actualCCNT > prevCCNT) { // this means the bms successfully + // received atleast one message + break; + } + } } if (rxPec != calcPec) { - // send back a specific message that the message was not sent through, - // this would be a rare instance since PEC is pretty good + // resend the message once, this error should not happen often and very + // rarely twice + spi_.TransmitReceive(tx, rx, 12); } // return true if dpec matches From e3c453c0726fb577ea5531014b4b16bc198006da Mon Sep 17 00:00:00 2001 From: Samuel Parent <108418256+samparent97@users.noreply.github.com> Date: Fri, 9 Jan 2026 16:44:32 -0800 Subject: [PATCH 16/26] Remove samparent97 from CODEOWNERS (#554) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1348238f7..564f7db7f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # All Pr's must be reviewed by at least one of the following members -* @samparent97 @BlakeFreer @TylerStAmour @langei @CameronBeneteau +* @BlakeFreer @TylerStAmour @langei @CameronBeneteau From 5058c52f22d2970a07ab269e4c04fd980a2fcf82 Mon Sep 17 00:00:00 2001 From: Blake Freer <59676067+BlakeFreer@users.noreply.github.com> Date: Tue, 13 Jan 2026 21:23:57 -0500 Subject: [PATCH 17/26] Remove the CODEOWNERs file (#556) PRs can now be approved by anyone in the organization. --- .github/CODEOWNERS | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 564f7db7f..000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# All Pr's must be reviewed by at least one of the following members -* @BlakeFreer @TylerStAmour @langei @CameronBeneteau From 177690b40c9d305ddc68d2d9e0e44e51131dd07e Mon Sep 17 00:00:00 2001 From: Manush Date: Thu, 23 Oct 2025 22:02:54 -0400 Subject: [PATCH 18/26] setup of lvbms system --- projects/lvcontroller/src/lvbms/lvbms.cc | 7 +++++++ projects/lvcontroller/src/lvbms/lvbms.hpp | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 projects/lvcontroller/src/lvbms/lvbms.cc create mode 100644 projects/lvcontroller/src/lvbms/lvbms.hpp diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc new file mode 100644 index 000000000..e76e184b7 --- /dev/null +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -0,0 +1,7 @@ +#pragma once + +#include "lvbms.hpp" + +#include "periph/spi.hpp" + +namespace macfe::lv {} diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp new file mode 100644 index 000000000..ecb5a1cd7 --- /dev/null +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace macfe::periph { +class SpiMaster; +} + +namespace macfe::lv { + +class LvBms { +public: + LvBms(macfe::periph::SpiMaster&); + +private: + macfe::periph::SpiMaster& spi_; +}; + +} // namespace macfe::lv \ No newline at end of file From 640a64fbd4ff2c539d3cc3c2083a83618e6f3d81 Mon Sep 17 00:00:00 2001 From: Manush Date: Wed, 29 Oct 2025 23:57:31 -0400 Subject: [PATCH 19/26] compile error --- projects/lvcontroller/src/lvbms/lvbms.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index e76e184b7..00e54630e 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -1,4 +1,4 @@ -#pragma once + #include "lvbms.hpp" From 52f79576c0514b3a5132e62ec6bdfd016038c17a Mon Sep 17 00:00:00 2001 From: Manush Date: Sun, 9 Nov 2025 16:15:46 -0500 Subject: [PATCH 20/26] pec15 & sendcmd function --- lib/periph/periph/spi.hpp | 7 ++-- projects/lvcontroller/src/lvbms/lvbms.cc | 39 ++++++++++++++++++++++- projects/lvcontroller/src/lvbms/lvbms.hpp | 7 ++++ projects/lvcontroller/src/main.cc | 6 ++++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/lib/periph/periph/spi.hpp b/lib/periph/periph/spi.hpp index da02bd731..07428db6f 100644 --- a/lib/periph/periph/spi.hpp +++ b/lib/periph/periph/spi.hpp @@ -7,16 +7,17 @@ namespace macfe::periph { class SpiMaster { public: + virtual ~SpiMaster() = default; /// Transmit `length` bytes from `tx_data` - virtual void Transmit(uint8_t* tx_data, size_t length); + virtual void Transmit(uint8_t* tx_data, size_t length) {} /// Receive `length` bytes and place them into `rx_data` - virtual void Receive(uint8_t* rx_data, size_t length); + virtual void Receive(uint8_t* rx_data, size_t length) {} /// Transmit `length` bytes from `tx_data` and receive `length` bytes, /// placing them into `rx_data` virtual void TransmitReceive(uint8_t* tx_data, uint8_t* rx_data, - size_t length); + size_t length) {} }; } // namespace macfe::periph \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index 00e54630e..76430b1e2 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -2,6 +2,43 @@ #include "lvbms.hpp" +#include + #include "periph/spi.hpp" -namespace macfe::lv {} +namespace macfe::lv { +macfe::lv::LvBms::LvBms(macfe::periph::SpiMaster& spi) : spi_(spi) {} + +bool LvBms::resetCommandCounter() { + uint8_t hi = (0x052E >> 8) & 0xFF; // RSTCC command + uint8_t lo = 0x052E & 0xFF; + return sendCmd(hi, lo); +} + +bool LvBms::sendCmd(uint8_t hi, uint8_t lo) { + uint8_t tx[4] = {hi, lo, 0, 0}; + uint16_t pec = pec15(tx, 2); + tx[2] = (pec >> 8) & 0xFF; + tx[3] = pec & 0xFF; + + spi_.Transmit(tx, 4); + return true; +} + +uint16_t LvBms::pec15(const uint8_t* data, int len) const { + // Packet Error Checker (CRC) + uint16_t rem = 0x0010; + constexpr uint16_t poly = 0x4599; + for (int i = 0; i < len; i++) { + rem ^= static_cast(data[i]) << 7; + for (int b = 0; b < 8; b++) { + bool top = rem & 0x4000; // checks for leftmost bit = 1 + rem <<= 1; + if (top) rem ^= poly; + rem &= 0x7FFF; + } + } + + return rem; +} +} // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index ecb5a1cd7..40b6c5044 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -1,4 +1,5 @@ #pragma once +#include namespace macfe::periph { class SpiMaster; @@ -10,8 +11,14 @@ class LvBms { public: LvBms(macfe::periph::SpiMaster&); + bool init(); + bool reset(); + bool resetCommandCounter(); // RSTCC + private: macfe::periph::SpiMaster& spi_; + bool sendCmd(uint8_t hi, uint8_t lo); + uint16_t pec15(const uint8_t* data, int len) const; }; } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/main.cc b/projects/lvcontroller/src/main.cc index 9951dd7f0..2010a3f63 100644 --- a/projects/lvcontroller/src/main.cc +++ b/projects/lvcontroller/src/main.cc @@ -15,7 +15,9 @@ #include "brakelight/brakelight.hpp" #include "dcdc/dcdc.hpp" #include "fans/fans.hpp" +#include "lvbms/lvbms.hpp" #include "motor_controller/motor_controller.hpp" +#include "periph/spi.hpp" #include "scheduler/scheduler.hpp" #include "suspension/suspension.hpp" #include "tssi/tssi.hpp" @@ -252,6 +254,10 @@ int main(void) { fsm::Init(); motor_controller::Init(); + macfe::periph::SpiMaster spi; + macfe::lv::LvBms bms(spi); + bms.resetCommandCounter(); + scheduler::register_task(task_100hz, 10); scheduler::register_task(task_10hz, 100); scheduler::register_task(task_1hz, 1000); From 76de297b358fa7c667451aff37e410515629b81b Mon Sep 17 00:00:00 2001 From: Manush Date: Sun, 16 Nov 2025 17:16:28 -0500 Subject: [PATCH 21/26] added enum codes and read/write command --- projects/lvcontroller/src/lvbms/lvbms.cc | 155 ++++++++++++++++++++-- projects/lvcontroller/src/lvbms/lvbms.hpp | 7 +- 2 files changed, 148 insertions(+), 14 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index 76430b1e2..4d4eebfa4 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -2,6 +2,7 @@ #include "lvbms.hpp" +#include #include #include "periph/spi.hpp" @@ -9,36 +10,166 @@ namespace macfe::lv { macfe::lv::LvBms::LvBms(macfe::periph::SpiMaster& spi) : spi_(spi) {} +/** + * @note The ADI codes are a little more intricate, and need to be looked into + * @note ADI1, ADI2, ADV have dummy values, so this will need to change + */ + +enum class commandCode : uint16_t { + SRST = 0x27, + RSTCC = 0x2E, + SNAP = 0x2D, + UNSNAP = 0x2F, + ADI1 = 0x00, + ADI2 = 0x00, + ADV = 0x00, + ADX = 0x530, + CLRI = 0x711, + CLRA = 0x714, + CLRVX = 0x712, + CLRO = 0x713, + CLRFLAG = 0x717, + RDFLAG = 0x72, // Includes ERR code, set to 1 + RDSTAT = 0x34, + RDI = 0x12, + RDVB = 0x13, + RDIACC = 0x44, + RDVBACC = 0x46, + RDIVB1 = 0x14, + RDIVB1ACC = 0x48, + RDV1A = 0xA, + RDV1B = 0x9, + RDV1C = 0x3, + RDV1D = 0xB, + RDV2A = 0x7, + RDV2B = 0xD, + RDV2C = 0x5, + RDV2D = 0x1F, + RDV2E = 0x25, + RDXA = 0x30, + RDXB = 0x31, + RDXC = 0x33, + RDOC = 0xB, + RDSID = 0x2C, + RDCFGA = 0x2, + RDCFGB = 0x26, + RDCOMM = 0x722, + WRCFGA = 0x1, + WRCFGB = 0x24, + WRCOMM = 0x721, + STCOMM = 0x723, + PLADC = 0x718, + PLI1 = 0x71C, + PLI2 = 0x71D, + PLV = 0x71E, + PLX = 0x71F, + RDALLI = 0xC, + RDALLA = 0x4C, + RDALLV = 0x35, + RDALLR = 0x11, + RDALLX = 0x51, + RDALLC = 0x10, +}; + bool LvBms::resetCommandCounter() { - uint8_t hi = (0x052E >> 8) & 0xFF; // RSTCC command - uint8_t lo = 0x052E & 0xFF; - return sendCmd(hi, lo); + uint16_t cmd = static_cast(commandCode::RSTCC); + uint8_t hi = (cmd >> 8) & 0xFF; // RSTCC command (top half) + uint8_t lo = cmd & 0xFF; // low half + uint8_t data[6]; + return sendReadCmd(hi, lo, data); +} + +bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { + // Read Command: Command Bytes + Command PEC + + // Frame 1: Sends Read Command + uint8_t tx[12] = {0}; + uint8_t rx[12] = {0}; + + tx[0] = hi; + tx[1] = lo; + + // Compute PEC15 for command + uint16_t pec = commandPec15(tx, 2); + tx[2] = (pec >> 8) & 0xFF; + tx[3] = pec & 0xFF; + + for (int i = 4; i < 12; i++) { + tx[i] = 0xFF; + } + + spi_.TransmitReceive(tx, rx, 12); + + // Copy recieved data bytes + std::memcpy(out, &rx[4], 6); + + // Check returned PEC10 + uint16_t rxPec = (rx[10] << 8) | rx[11]; + uint16_t calcPec = dataPec10(&rx[4], 6); + + // return true if dpec matches + return (rxPec == calcPec); } -bool LvBms::sendCmd(uint8_t hi, uint8_t lo) { - uint8_t tx[4] = {hi, lo, 0, 0}; - uint16_t pec = pec15(tx, 2); +bool LvBms::sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]) { + // Write Command: 2 Command + 2 PEC15 + 6 Data + 2 PEC10 = 12 bytes + + // Command bytes + uint8_t tx[12]; + tx[0] = hi; + tx[1] = lo; + + // Command PEC15 bytes + uint16_t pec = commandPec15(tx, 2); tx[2] = (pec >> 8) & 0xFF; tx[3] = pec & 0xFF; - spi_.Transmit(tx, 4); + // Copy Data (6 bytes) + std::memcpy(&tx[4], data, 6); + + // Data PEC10 + uint16_t dataPec = dataPec10(&tx[4], 6); + tx[10] = (dataPec >> 8) & 0xFF; + tx[11] = dataPec & 0xFF; + + // Sends all 12 bytes + spi_.Transmit(tx, 12); return true; } -uint16_t LvBms::pec15(const uint8_t* data, int len) const { +uint16_t LvBms::commandPec15(const uint8_t* data, int len) const { // Packet Error Checker (CRC) uint16_t rem = 0x0010; - constexpr uint16_t poly = 0x4599; + uint16_t msbMask = 0x4000; // bit 14 + constexpr uint16_t poly = + 0x4599; // x^15 +x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1 for (int i = 0; i < len; i++) { - rem ^= static_cast(data[i]) << 7; + rem ^= static_cast(data[i]) << 7; // shifts to 15 bits, so + // XOR for (int b = 0; b < 8; b++) { - bool top = rem & 0x4000; // checks for leftmost bit = 1 + bool top = rem & msbMask; // checks for leftmost bit = 1 rem <<= 1; if (top) rem ^= poly; - rem &= 0x7FFF; + rem &= 0x7FFF; // forces remainder to be 15 bits } } return rem; } + +uint16_t LvBms::dataPec10(const uint8_t* data, int len) const { + uint16_t rem = 0x20; // 00000010000 (10-bit initial value) + constexpr uint16_t msbMask = 0x0200; // bit9 + constexpr uint16_t poly = 0x2D1; // x^10 + x^7 + x^5 + x^4 + x^3 + 1 + for (int i = 0; i < len; i++) { + rem ^= static_cast(data[i]) << 2; + for (int b = 0; b < 8; b++) { + bool top = rem & msbMask; + rem <<= 1; + if (top) rem ^= poly; + rem &= 0x3FF; // keep CRC to 10 bits + } + } + return rem; +} } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index 40b6c5044..0af291651 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -17,8 +17,11 @@ class LvBms { private: macfe::periph::SpiMaster& spi_; - bool sendCmd(uint8_t hi, uint8_t lo); - uint16_t pec15(const uint8_t* data, int len) const; + bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]); + bool sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]); + + uint16_t commandPec15(const uint8_t* data, int len) const; + uint16_t dataPec10(const uint8_t* data, int len) const; }; } // namespace macfe::lv \ No newline at end of file From 0a0a0e9624ff17d571ffcbd60dd8590fa5f01212 Mon Sep 17 00:00:00 2001 From: Manush Date: Sun, 23 Nov 2025 00:56:11 -0500 Subject: [PATCH 22/26] CCNT for data integrity and helper functions --- projects/lvcontroller/src/lvbms/lvbms.cc | 77 ++++++++++++++++++++--- projects/lvcontroller/src/lvbms/lvbms.hpp | 10 ++- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index 4d4eebfa4..a8e1fe538 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -2,6 +2,7 @@ #include "lvbms.hpp" +// #include #include #include @@ -15,6 +16,13 @@ macfe::lv::LvBms::LvBms(macfe::periph::SpiMaster& spi) : spi_(spi) {} * @note ADI1, ADI2, ADV have dummy values, so this will need to change */ +/** + * @note everytime we utilize a read command, we should check for the CCNT + * frames, and check them with our count. This will tell us if any commands may + * have been ignored. Increment expected CCNT each time a write function is + * called + **/ + enum class commandCode : uint16_t { SRST = 0x27, RSTCC = 0x2E, @@ -71,12 +79,27 @@ enum class commandCode : uint16_t { RDALLC = 0x10, }; -bool LvBms::resetCommandCounter() { - uint16_t cmd = static_cast(commandCode::RSTCC); - uint8_t hi = (cmd >> 8) & 0xFF; // RSTCC command (top half) - uint8_t lo = cmd & 0xFF; // low half - uint8_t data[6]; - return sendReadCmd(hi, lo, data); +// Helper Functions ----------------------------------------------------- +std::array LvBms::splitMessage(uint16_t cmd) { + uint8_t hi = (cmd >> 8) & 0xFF; + uint8_t lo = cmd & 0xFF; + + std::array split = {hi, lo}; + return split; +} + +void LvBms::sendControlCmd(std::array cmd) { + uint8_t tx[4] = {0}; + tx[0] = cmd[0]; // HI + tx[1] = cmd[1]; // LO + // Command PEC for transmit + uint16_t pec = commandPec15(tx, 2); + tx[2] = (pec >> 8) & 0xFF; + tx[3] = pec & 0xFF; + + spi_.Transmit(tx, 4); + + expectedCCNT = (expectedCCNT % 63) + 1; } bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { @@ -100,13 +123,19 @@ bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { spi_.TransmitReceive(tx, rx, 12); - // Copy recieved data bytes - std::memcpy(out, &rx[4], 6); + expectedCCNT = (expectedCCNT % 63) + 1; // update after every transmit // Check returned PEC10 uint16_t rxPec = (rx[10] << 8) | rx[11]; + + // CCNT and PEC must be identical + actualCCNT = (rx[10] >> 2) & 0x3F; + uint16_t calcPec = dataPec10(&rx[4], 6); + if (actualCCNT != expectedCCNT) { + // maybe define a special message back to lv controller + } // return true if dpec matches return (rxPec == calcPec); } @@ -172,4 +201,36 @@ uint16_t LvBms::dataPec10(const uint8_t* data, int len) const { } return rem; } + +// ------------------------------------------------------------------------------------ + +void LvBms::resetCommandCounter() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RSTCC)); + expectedCCNT = 0; // reset + sendControlCmd(cmd); +} + +void LvBms::freezeResultRegisters() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::SNAP)); + + sendControlCmd(cmd); +} + +void LvBms::unfreezeResultRegisters() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::UNSNAP)); + + uint8_t tx[4] = {0}; + tx[0] = cmd[0]; // HI + tx[1] = cmd[1]; // LO + // Command PEC for transmit + uint16_t pec = commandPec15(tx, 2); + tx[2] = (pec >> 8) & 0xFF; + tx[3] = pec & 0xFF; + + spi_.Transmit(tx, 4); +} + } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index 0af291651..014f5a85e 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include namespace macfe::periph { @@ -13,12 +14,19 @@ class LvBms { bool init(); bool reset(); - bool resetCommandCounter(); // RSTCC + void resetCommandCounter(); // RSTCC + void freezeResultRegisters(); // SNAP + void unfreezeResultRegisters(); // UNSNAP private: macfe::periph::SpiMaster& spi_; + uint8_t expectedCCNT = 0; // tracks expected command counter + uint8_t actualCCNT = 0; + bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]); bool sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]); + void sendControlCmd(std::array cmd); + std::array splitMessage(uint16_t cmd); uint16_t commandPec15(const uint8_t* data, int len) const; uint16_t dataPec10(const uint8_t* data, int len) const; From 016511c2c6f0d83b4c8e24e5f4c5b928bc11d660 Mon Sep 17 00:00:00 2001 From: Manush Date: Thu, 18 Dec 2025 23:11:02 -0500 Subject: [PATCH 23/26] more read functions --- projects/lvcontroller/src/lvbms/lvbms.cc | 101 +++++++++++++++++++--- projects/lvcontroller/src/lvbms/lvbms.hpp | 47 +++++++++- 2 files changed, 136 insertions(+), 12 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index a8e1fe538..7db1139a3 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -102,7 +102,8 @@ void LvBms::sendControlCmd(std::array cmd) { expectedCCNT = (expectedCCNT % 63) + 1; } -bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { +bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6], + bool measurementRead) { // Read Command: Command Bytes + Command PEC // Frame 1: Sends Read Command @@ -123,8 +124,6 @@ bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { spi_.TransmitReceive(tx, rx, 12); - expectedCCNT = (expectedCCNT % 63) + 1; // update after every transmit - // Check returned PEC10 uint16_t rxPec = (rx[10] << 8) | rx[11]; @@ -136,6 +135,17 @@ bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { if (actualCCNT != expectedCCNT) { // maybe define a special message back to lv controller } + + if (measurementRead) { // CCNT check + if (expectedCCNT != actualCCNT) { + return false; + } + if (rxPec != calcPec) { + return false; + } + expectedCCNT = (expectedCCNT % 63) + 1; + } + // return true if dpec matches return (rxPec == calcPec); } @@ -204,6 +214,14 @@ uint16_t LvBms::dataPec10(const uint8_t* data, int len) const { // ------------------------------------------------------------------------------------ +// Command Codes +void LvBms::softwareReset() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::SRST)); + // Look for expected CCNT Reset + sendControlCmd(cmd); +} + void LvBms::resetCommandCounter() { std::array cmd = LvBms::splitMessage(uint16_t(commandCode::RSTCC)); @@ -221,16 +239,77 @@ void LvBms::freezeResultRegisters() { void LvBms::unfreezeResultRegisters() { std::array cmd = LvBms::splitMessage(uint16_t(commandCode::UNSNAP)); + sendControlCmd(cmd); +} - uint8_t tx[4] = {0}; - tx[0] = cmd[0]; // HI - tx[1] = cmd[1]; // LO - // Command PEC for transmit - uint16_t pec = commandPec15(tx, 2); - tx[2] = (pec >> 8) & 0xFF; - tx[3] = pec & 0xFF; +void LvBms::clearIxVBxADCResults() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRI)); - spi_.Transmit(tx, 4); + sendControlCmd(cmd); +} + +void LvBms::clearAccumalators() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRA)); + + sendControlCmd(cmd); +} + +void LvBms::clearVxADCResults() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRVX)); + + sendControlCmd(cmd); +} + +void LvBms::clearOCxADCResults() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRO)); + + sendControlCmd(cmd); +} +// -------------------------------------------------------- + +// Write Functions +void LvBms::CLRFLAG() { + /* + Used to reset individual bits in the flag register. This write function, + like all, require 6 bytes sent back. Any bit of the data that is set to 1 + clears the related flag + */ + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRFLAG)); + uint8_t output[6]; + sendWriteCmd(cmd[0], cmd[1], output); +} + +// Read Functions + +// non measurement read will pass in false -> will not increment CCNT + +void LvBms::RDFLAG() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::CLRFLAG)); + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output, false); + + // Fault and Status Information is read from this command +} + +void LvBms::RDSTAT() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDSTAT)); + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output, false); +} + +void LvBms::RDI() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDI)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output, false); } } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index 014f5a85e..0cc5b55ce 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -14,16 +14,61 @@ class LvBms { bool init(); bool reset(); + + // Command Codes + void softwareReset(); void resetCommandCounter(); // RSTCC void freezeResultRegisters(); // SNAP void unfreezeResultRegisters(); // UNSNAP + void clearIxVBxADCResults(); // CLRI + void clearAccumalators(); // CLRA + void clearVxADCResults(); // CLRVX + void clearOCxADCResults(); // CLRO + + // Read Commands + // ----------------------- + void RDFLAG(); + void RDSTAT(); + void RDI(); + void RDV8(); + void RDIACC(); + void RDVBACC(); + void RDIVB1(); + void RDIB1ACC(); + + // ADCV1 + void RDV1A(); + void RDV1B(); + void RDV1C(); + void RDV1D(); + + // ADCV2 + void RDV2A(); + void RDV2B(); + void RDV2C(); + void RDV2D(); + void RDV2E(); + + // AUX ADC + void RDXA(); + void RDXB(); + void RDXC(); + + void RDSID(); + void RDCFGA(); + void RDCFGB(); + + // Write Commands + // ----------------------- + void CLRFLAG(); private: macfe::periph::SpiMaster& spi_; uint8_t expectedCCNT = 0; // tracks expected command counter uint8_t actualCCNT = 0; - bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]); + bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6], + bool measurementRead); bool sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]); void sendControlCmd(std::array cmd); std::array splitMessage(uint16_t cmd); From 05e7c9b86765df54e0bc10512807e0432c9fe799 Mon Sep 17 00:00:00 2001 From: Manush Date: Sat, 20 Dec 2025 22:01:19 -0500 Subject: [PATCH 24/26] fix CCNT misunderstanding --- projects/lvcontroller/src/lvbms/lvbms.cc | 45 +++++++++++++---------- projects/lvcontroller/src/lvbms/lvbms.hpp | 7 +++- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index 7db1139a3..ff50fee17 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -19,8 +19,8 @@ macfe::lv::LvBms::LvBms(macfe::periph::SpiMaster& spi) : spi_(spi) {} /** * @note everytime we utilize a read command, we should check for the CCNT * frames, and check them with our count. This will tell us if any commands may - * have been ignored. Increment expected CCNT each time a write function is - * called + * have been ignored. Increment expected CCNT each time a write or command + * function has been called **/ enum class commandCode : uint16_t { @@ -98,12 +98,11 @@ void LvBms::sendControlCmd(std::array cmd) { tx[3] = pec & 0xFF; spi_.Transmit(tx, 4); - - expectedCCNT = (expectedCCNT % 63) + 1; + expectedCCNT = + (expectedCCNT % 63) + 1; // Increment CCNT for Command Functions } -bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6], - bool measurementRead) { +bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { // Read Command: Command Bytes + Command PEC // Frame 1: Sends Read Command @@ -134,16 +133,12 @@ bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6], if (actualCCNT != expectedCCNT) { // maybe define a special message back to lv controller + // From now on, these values won't be the same... this could flood our + // can bus } - - if (measurementRead) { // CCNT check - if (expectedCCNT != actualCCNT) { - return false; - } - if (rxPec != calcPec) { - return false; - } - expectedCCNT = (expectedCCNT % 63) + 1; + if (rxPec != calcPec) { + // send back a specific message that the message was not sent through, + // this would be a rare instance since PEC is pretty good } // return true if dpec matches @@ -173,6 +168,10 @@ bool LvBms::sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]) { // Sends all 12 bytes spi_.Transmit(tx, 12); + + expectedCCNT = (expectedCCNT % 63) + 1; // Increment CCNT for Write + // Commands + return true; } @@ -290,9 +289,9 @@ void LvBms::CLRFLAG() { void LvBms::RDFLAG() { std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::CLRFLAG)); + LvBms::splitMessage(uint16_t(commandCode::RDFLAG)); uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output, false); + sendReadCmd(cmd[0], cmd[1], output); // Fault and Status Information is read from this command } @@ -301,7 +300,7 @@ void LvBms::RDSTAT() { std::array cmd = LvBms::splitMessage(uint16_t(commandCode::RDSTAT)); uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output, false); + sendReadCmd(cmd[0], cmd[1], output); } void LvBms::RDI() { @@ -309,7 +308,15 @@ void LvBms::RDI() { LvBms::splitMessage(uint16_t(commandCode::RDI)); uint8_t output[6]; - sendReadCmd(cmd[0], cmd[1], output, false); + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV8() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDI)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); } } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index 0cc5b55ce..23f07ddbe 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -57,18 +57,21 @@ class LvBms { void RDSID(); void RDCFGA(); void RDCFGB(); + void RDCOMM(); // Write Commands // ----------------------- void CLRFLAG(); + void WRCFGA(); + void WRCFGB(); + void WRCOMM(); private: macfe::periph::SpiMaster& spi_; uint8_t expectedCCNT = 0; // tracks expected command counter uint8_t actualCCNT = 0; - bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6], - bool measurementRead); + bool sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]); bool sendWriteCmd(uint8_t hi, uint8_t lo, const uint8_t data[6]); void sendControlCmd(std::array cmd); std::array splitMessage(uint16_t cmd); From 423da01768fb81e06574a935df49b333b7003107 Mon Sep 17 00:00:00 2001 From: Manush Date: Sat, 20 Dec 2025 22:22:30 -0500 Subject: [PATCH 25/26] idk gang more commands --- projects/lvcontroller/src/lvbms/lvbms.cc | 172 +++++++++++++++++++++- projects/lvcontroller/src/lvbms/lvbms.hpp | 5 +- 2 files changed, 173 insertions(+), 4 deletions(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index ff50fee17..fbdfba78d 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -311,9 +311,177 @@ void LvBms::RDI() { sendReadCmd(cmd[0], cmd[1], output); } -void LvBms::RDV8() { +void LvBms::RDVB() { std::array cmd = - LvBms::splitMessage(uint16_t(commandCode::RDI)); + LvBms::splitMessage(uint16_t(commandCode::RDVB)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDIACC() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDIACC)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDVBACC() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDIACC)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDIVB1() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDIVB1)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDIVB1ACC() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDIVB1ACC)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV1A() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV1A)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV1B() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV1B)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV1C() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV1C)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV1D() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV1D)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV2A() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV2A)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV2B() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV2B)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV2C() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV2C)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV2D() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV2D)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDV2E() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDV2E)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDXA() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDXA)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDXB() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDXB)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDXC() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDXC)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDOC() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDOC)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDSID() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDSID)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDCFGA() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDCFGA)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDCFGB() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDCFGB)); + + uint8_t output[6]; + sendReadCmd(cmd[0], cmd[1], output); +} + +void LvBms::RDCOMM() { + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDCOMM)); uint8_t output[6]; sendReadCmd(cmd[0], cmd[1], output); diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index 23f07ddbe..c6248b555 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -30,11 +30,11 @@ class LvBms { void RDFLAG(); void RDSTAT(); void RDI(); - void RDV8(); + void RDVB(); void RDIACC(); void RDVBACC(); void RDIVB1(); - void RDIB1ACC(); + void RDIVB1ACC(); // ADCV1 void RDV1A(); @@ -53,6 +53,7 @@ class LvBms { void RDXA(); void RDXB(); void RDXC(); + void RDOC(); void RDSID(); void RDCFGA(); From c9ee8d218b5fccb2406ba61ef6b8c60bf435a06f Mon Sep 17 00:00:00 2001 From: HarshPatel08 Date: Sun, 21 Dec 2025 20:33:52 -0500 Subject: [PATCH 26/26] Added Read All Commands --- projects/lvcontroller/src/lvbms/lvbms.cc | 67 ++++++++++++++++++++++- projects/lvcontroller/src/lvbms/lvbms.hpp | 13 +++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/projects/lvcontroller/src/lvbms/lvbms.cc b/projects/lvcontroller/src/lvbms/lvbms.cc index fbdfba78d..e47fcc893 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.cc +++ b/projects/lvcontroller/src/lvbms/lvbms.cc @@ -103,7 +103,7 @@ void LvBms::sendControlCmd(std::array cmd) { } bool LvBms::sendReadCmd(uint8_t hi, uint8_t lo, uint8_t out[6]) { - // Read Command: Command Bytes + Command PEC + // Read Command: Comand Bytes + Command PEC // Frame 1: Sends Read Command uint8_t tx[12] = {0}; @@ -487,4 +487,69 @@ void LvBms::RDCOMM() { sendReadCmd(cmd[0], cmd[1], output); } +void LvBms::readAllConfigFlags(uint8_t input[6]) { + + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDALLC)); + + sendReadCmd(cmd[0], cmd[1], input); + + +} + +void LvBms::readAllAUXADC(uint8_t input[6]) { + + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDALLX)); + + sendReadCmd(cmd[0], cmd[1], input); + +} + +void LvBms::readAllExternalinputV2ADCResults(uint8_t input[6]) { + + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDALLR)); + + sendReadCmd(cmd[0], cmd[1], input); + +} + + +void LvBms::readAllExternalinputV1ADCResults(uint8_t input[6]) { + + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDALLV)); + + sendReadCmd(cmd[0], cmd[1], input); + +} + + +void LvBms::readIxADCandVbADCaccumulators(uint8_t input[6]) { + freezeResultRegisters(); + + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDALLA)); + + sendReadCmd(cmd[0], cmd[1], input); + + unfreezeResultRegisters(); + +} + +void LvBms::readIxADCandVbADCresults(uint8_t input[6]) { + freezeResultRegisters(); + + std::array cmd = + LvBms::splitMessage(uint16_t(commandCode::RDALLI)); + + sendReadCmd(cmd[0], cmd[1], input); + + unfreezeResultRegisters(); + +} + + + } // namespace macfe::lv \ No newline at end of file diff --git a/projects/lvcontroller/src/lvbms/lvbms.hpp b/projects/lvcontroller/src/lvbms/lvbms.hpp index c6248b555..9d3fa18b4 100644 --- a/projects/lvcontroller/src/lvbms/lvbms.hpp +++ b/projects/lvcontroller/src/lvbms/lvbms.hpp @@ -20,6 +20,7 @@ class LvBms { void resetCommandCounter(); // RSTCC void freezeResultRegisters(); // SNAP void unfreezeResultRegisters(); // UNSNAP + void clearIxVBxADCResults(); // CLRI void clearAccumalators(); // CLRA void clearVxADCResults(); // CLRVX @@ -60,6 +61,15 @@ class LvBms { void RDCFGB(); void RDCOMM(); + void readAllConfigFlags(uint8_t input[6]); // RDALLC + void readAllAUXADC(uint8_t input[6]); // RDALLX + void readAllExternalinputV2ADCResults(uint8_t input[6]); // RDALLR + void readAllExternalinputV1ADCResults(uint8_t input[6]); // RDALLV + void readIxADCandVbADCaccumulators(uint8_t input[6]); // RDALLA + void readIxADCandVbADCresults(uint8_t input[6]); // RDALLI + + + // Write Commands // ----------------------- void CLRFLAG(); @@ -67,6 +77,9 @@ class LvBms { void WRCFGB(); void WRCOMM(); + + + private: macfe::periph::SpiMaster& spi_; uint8_t expectedCCNT = 0; // tracks expected command counter