From 9e956fd86c7569b0da6ba6b2469c9636a1b2e329 Mon Sep 17 00:00:00 2001 From: Louis Brennan Date: Tue, 14 Apr 2026 23:53:46 +0100 Subject: [PATCH 1/4] Put missing #pragma once to some headers and added serial debug --- .gitignore | 1 + include/CircularBuffer.h | 4 ++-- include/MpuController.h | 1 + include/Nextion.h | 1 + include/config.h | 1 + src/Logger.cpp | 6 +++++- src/Nextion.cpp | 4 ++-- src/main.cpp | 8 ++++++++ 8 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 89cc49c..5203348 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .vscode/c_cpp_properties.json .vscode/launch.json .vscode/ipch +/logs diff --git a/include/CircularBuffer.h b/include/CircularBuffer.h index 371108b..9412026 100644 --- a/include/CircularBuffer.h +++ b/include/CircularBuffer.h @@ -4,7 +4,7 @@ template class CircularBuffer { private: - T buffer[size]; + T _buffer[size]; size_t _head = 0; size_t _tail = 0; bool _full = false; @@ -19,7 +19,7 @@ class CircularBuffer { _full = _head == _tail; } - bool pop (&T item) { + bool pop (T &item) { if (isEmpty()) { return false; } diff --git a/include/MpuController.h b/include/MpuController.h index beaed81..acb0ad5 100644 --- a/include/MpuController.h +++ b/include/MpuController.h @@ -1,3 +1,4 @@ +#pragma once #include "config.h" #include "Logger.h" #include "Nextion.h" diff --git a/include/Nextion.h b/include/Nextion.h index 4230395..671ea51 100644 --- a/include/Nextion.h +++ b/include/Nextion.h @@ -1,3 +1,4 @@ +#pragma once #include "config.h" // Nextion connected to Serial7 (TX7/RX7 on Teensy 4.1) diff --git a/include/config.h b/include/config.h index b68a5bb..d934800 100644 --- a/include/config.h +++ b/include/config.h @@ -13,6 +13,7 @@ #define MAX_BUF 16 #define MAX_LOG_LEN 128 extern FsFile logFile; +#define SERIAL_DEBUG true // --------- BUTTON ---------- #define DEBOUNCE_MS 50 diff --git a/src/Logger.cpp b/src/Logger.cpp index bb7ac0e..17d14cc 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -17,6 +17,10 @@ void Logger::log(LogLevel level, const char* module, const char* msg) { LogEntry entry; snprintf(entry.data, MAX_LOG_LEN, "[%s] %s: %s", levelToStr(level), module, msg); + if (SERIAL_DEBUG) { + Serial.println(entry.data); + } + _logBuffer.push(entry); //TODO: consider having mode for nextion to display logs in real time // serial writing is better done in smaller bursts, so use different buffer @@ -43,7 +47,7 @@ void Logger::process() { uint8_t count = 0; // Pop and write in small bursts - while (_logBuffer.pop(&entry) && count < 5) { + while (_logBuffer.pop(entry) && count < 5) { logFile.println(entry.data); count++; } diff --git a/src/Nextion.cpp b/src/Nextion.cpp index 3824925..d7bdd3a 100644 --- a/src/Nextion.cpp +++ b/src/Nextion.cpp @@ -10,14 +10,14 @@ void Nextion::sendCmd(const char *cmd) { void Nextion::begin() { NEXTION_SERIAL.begin(NEXTION_BAUD); - delay(100); + delay(100); sendCmd(""); // clear RX buffer sendCmd("bkcmd=1"); // enable Nextion command feedback sendCmd("page 0"); } void Nextion::page(uint8_t pageNumber) { - char cmd[8]; + char cmd[16]; snprintf(cmd, sizeof(cmd), "page %d", pageNumber); sendCmd(cmd); } diff --git a/src/main.cpp b/src/main.cpp index 4d3cea6..cfb0dde 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,12 @@ Button driveButton(BUTTON_PIN); DashStatus dashStatus; void setup() { + if (SERIAL_DEBUG) { + Serial.begin(115200); + while (!Serial); + Serial.println("System Ready!"); + } + Nextion::begin(); Nextion::bootStatus("INITIALISATION", "System booting up, initialising components"); @@ -21,6 +27,8 @@ void setup() { Nextion::bootStatus("INITIALISATION", "Initialisation complete, starting main loop"); Logger::log(LogLevel::INFO, "Main", "System initialised, starting main loop"); + + Nextion::page(NX_PAGE_DRIVE); } void loop() { From 381e3a4a67bd315dc9f533fc4fdca6ee5a2b25d0 Mon Sep 17 00:00:00 2001 From: louisbrennan Date: Sun, 3 May 2026 17:42:00 +0100 Subject: [PATCH 2/4] updated nextion code --- include/Nextion.h | 8 +++++--- include/config.h | 4 ++-- src/Nextion.cpp | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/include/Nextion.h b/include/Nextion.h index 671ea51..3d55506 100644 --- a/include/Nextion.h +++ b/include/Nextion.h @@ -17,11 +17,12 @@ #define NX_DRIVE_SPEED "n_speed" // number: vehicle speed, integer km/h #define NX_DRIVE_RPM "n_rpm" // number: motor RPM #define NX_DRIVE_TORQUE "n_torque" // number: torque command, 0-100% -#define NX_DRIVE_DCBUS "n_dcbus" // number: DC bus voltage, whole volts +#define NX_DRIVE_DCBUS "n_dc_bus" // number: DC bus voltage, whole volts #define NX_DRIVE_FAULT "t_fault" // text: "OK" or "FAULT" #define NX_DRIVE_STATE "t_drive" // text: "DRIVE: ON" or "DRIVE: OFF" -#define NX_DRIVE_MOTOR_TEMP "n_mtemp" // number: motor temperature, °C -#define NX_DRIVE_INVERTER_TEMP "n_itemp" // number: inverter temperature, °C +#define NX_DRIVE_MOTOR_TEMP "n_motor_temp" // number: motor temperature, °C +#define NX_DRIVE_INVERTER_TEMP "n_inv_temp" // number: inverter temperature, °C +#define NX_DRIVE_SPEED_BAR "j_throttle" // progress bar: throttle command, 0-100% struct DashStatus { int16_t speed; // km/h @@ -44,4 +45,5 @@ class Nextion { static void sendNumber(const char *component, int16_t value); static void bootStatus(const char *phase, const char *detail); static void updateDash(DashStatus dashStatus); + static void sendNumberValue(const char *component, int16_t value); }; diff --git a/include/config.h b/include/config.h index d934800..91191ec 100644 --- a/include/config.h +++ b/include/config.h @@ -8,12 +8,12 @@ // --------- LOGGING ---------- -#define LOG_SD true +#define LOG_SD false #define LOG_SERIAL false #define MAX_BUF 16 #define MAX_LOG_LEN 128 extern FsFile logFile; -#define SERIAL_DEBUG true +#define SERIAL_DEBUG false // --------- BUTTON ---------- #define DEBOUNCE_MS 50 diff --git a/src/Nextion.cpp b/src/Nextion.cpp index d7bdd3a..8abb855 100644 --- a/src/Nextion.cpp +++ b/src/Nextion.cpp @@ -29,6 +29,14 @@ void Nextion::sendText(const char *component, const char *text) { } void Nextion::sendNumber(const char *component, int16_t value) { + char cmd[32]; + char buf[8]; + itoa(value, buf, 10); + snprintf(cmd, sizeof(cmd), "%s.txt=\"%s\"", component, buf); + sendCmd(cmd); +} + +void Nextion::sendNumberValue(const char *component, int16_t value) { char cmd[32]; snprintf(cmd, sizeof(cmd), "%s.val=%d", component, value); sendCmd(cmd); @@ -44,8 +52,10 @@ void Nextion::updateDash(DashStatus dashStatus) { sendNumber(NX_DRIVE_RPM, dashStatus.rpm); sendNumber(NX_DRIVE_TORQUE, dashStatus.torque); sendNumber(NX_DRIVE_DCBUS, dashStatus.dcBusV); - sendText(NX_DRIVE_FAULT, dashStatus.fault ? "FAULT" : "OK"); - sendText(NX_DRIVE_STATE, dashStatus.driveOn ? "DRIVE: ON" : "DRIVE: OFF"); + //sendText(NX_DRIVE_FAULT, dashStatus.fault ? "FAULT" : "OK"); + //sendText(NX_DRIVE_STATE, dashStatus.driveOn ? "DRIVE: ON" : "DRIVE: OFF"); + // TODO: update fault and drive state when we have that data from CAN sendNumber(NX_DRIVE_MOTOR_TEMP, dashStatus.motorTemp); sendNumber(NX_DRIVE_INVERTER_TEMP, dashStatus.inverterTemp); + sendNumberValue(NX_DRIVE_SPEED_BAR, dashStatus.torque); } \ No newline at end of file From 5ac3e394a57f609d9823cb0e693512f21a3f34b4 Mon Sep 17 00:00:00 2001 From: louisbrennan Date: Mon, 4 May 2026 20:52:58 +0100 Subject: [PATCH 3/4] Updated logging and pedal code --- include/Button.h | 21 ++++++++++----------- include/Logger.h | 11 ++++++----- include/Pedal.h | 13 +++++++++++++ include/config.h | 16 +++++++++++++++- src/Logger.cpp | 7 ++++--- src/Pedal.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 11 ++++++----- 7 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 include/Pedal.h create mode 100644 src/Pedal.cpp diff --git a/include/Button.h b/include/Button.h index d4db6b3..f0e57ee 100644 --- a/include/Button.h +++ b/include/Button.h @@ -2,15 +2,14 @@ #include "config.h" class Button { - public: - Button(int pin); - bool isPressed(); - bool isReleased(); - bool heldFor(uint16_t duration_ms); - - private: - uint8_t _pin; - bool _lastState; - uint64_t _lastEdgeTime; - uint64_t _holdStartTime; +private: + uint8_t _pin; + bool _lastState; + uint64_t _lastEdgeTime; + uint64_t _holdStartTime; +public: + Button(int pin); + bool isPressed(); + bool isReleased(); + bool heldFor(uint16_t duration_ms); }; \ No newline at end of file diff --git a/include/Logger.h b/include/Logger.h index 527c620..92fd613 100644 --- a/include/Logger.h +++ b/include/Logger.h @@ -2,21 +2,22 @@ #include "config.h" #include "CircularBuffer.h" -enum LogLevel { DEBUG, ERROR, INFO, WARNING }; +enum LogLevel { NONE, ERROR, WARNING, INFO, DEBUG }; + -// In C++, this is the cleaner way to define a fixed-size char array type struct LogEntry { char data[MAX_LOG_LEN]; }; class Logger { private: - static SdFs sd; // The SD filesystem object - static FsFile logFile; // The log file object + static SdFs sd; + static FsFile logFile; + static LogLevel serialDebug; public: static CircularBuffer _logBuffer; - static bool begin(); + static bool begin(LogLevel debugLevel); static void sync(); static void log(LogLevel level, const char* module, const char* msg); static void process(); diff --git a/include/Pedal.h b/include/Pedal.h new file mode 100644 index 0000000..c2e544d --- /dev/null +++ b/include/Pedal.h @@ -0,0 +1,13 @@ +#include "config.h" + +class Pedal { +private: + float appsPercent(int raw, int rest, int full); + bool fault = false; + uint8_t plausibilityCount = 0; +public: + Pedal(); + int16_t apps1Raw = 0; + int16_t apps2Raw = 0; + int16_t read(); +}; \ No newline at end of file diff --git a/include/config.h b/include/config.h index 91191ec..9a927a9 100644 --- a/include/config.h +++ b/include/config.h @@ -13,12 +13,26 @@ #define MAX_BUF 16 #define MAX_LOG_LEN 128 extern FsFile logFile; -#define SERIAL_DEBUG false // --------- BUTTON ---------- #define DEBOUNCE_MS 50 #define BUTTON_PIN 2 +// ---------- APPS (pedal sensor) config ---------- +// Set REST to ADC reading with pedal physically released. +// Set FULL to ADC reading at maximum pedal travel. +// Formula handles both rising and falling sensor directions. +#define APPS1_PIN A0 +#define APPS2_PIN A1 +#define APPS1_REST 2884 // calibrate: ADC at physical zero +#define APPS1_FULL 1835 // calibrate: ADC at full pedal +#define APPS2_REST 2910 // calibrate: ADC at physical zero +#define APPS2_FULL 1845 // calibrate: ADC at full pedal +#define PEDAL_DEADBAND_PERCENT 3 +#define PEDAL_PLAUSIBILITY_PERCENT 10 +#define MAX_ACCEL_PERCENT 100 +#define TORQUE_MAX 32767 + // ---------- Adafruit MPU ----------- #define MPU_ACCEL_RANGE MPU6050_RANGE_8_G #define MPU_GYRO_RANGE MPU6050_RANGE_500_DEG diff --git a/src/Logger.cpp b/src/Logger.cpp index 17d14cc..3128e4c 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -17,8 +17,8 @@ void Logger::log(LogLevel level, const char* module, const char* msg) { LogEntry entry; snprintf(entry.data, MAX_LOG_LEN, "[%s] %s: %s", levelToStr(level), module, msg); - if (SERIAL_DEBUG) { - Serial.println(entry.data); + if (serialDebug >= level) { + Serial.println(entry.data); } _logBuffer.push(entry); @@ -26,7 +26,8 @@ void Logger::log(LogLevel level, const char* module, const char* msg) { // serial writing is better done in smaller bursts, so use different buffer } -bool Logger::begin() { +bool Logger::begin(LogLevel serialDebugLevel) { + serialDebug = serialDebugLevel; // SdioConfig(FIFO_SDIO) tells the Teensy to use the fast onboard slot if (!sd.begin(SdioConfig(FIFO_SDIO))) { return false; diff --git a/src/Pedal.cpp b/src/Pedal.cpp new file mode 100644 index 0000000..3b3bed3 --- /dev/null +++ b/src/Pedal.cpp @@ -0,0 +1,47 @@ +#include "pedal.h" + +float Pedal::appsPercent(int raw, int rest, int full) { + float pct = (float)(rest - raw) * 100.0f / (float)(rest - full); + if (pct < 0.0f) pct = 0.0f; + if (pct > 100.0f) pct = 100.0f; + return pct; +} + +int16_t Pedal::read() { + apps1Raw = analogRead(APPS1_PIN); + apps2Raw = analogRead(APPS2_PIN); + + float pct1 = appsPercent(apps1Raw, APPS1_REST, APPS1_FULL); + float pct2 = appsPercent(apps2Raw, APPS2_REST, APPS2_FULL); + + // Plausibility: sensors must agree within PEDAL_PLAUSIBILITY_PERCENT. + // Fault only latches after 3 consecutive bad readings (~60ms) to reject + // single noisy samples. Zero torque immediately on any bad reading. + if (fabsf(pct1 - pct2) > PEDAL_PLAUSIBILITY_PERCENT) { + plausibilityCount++; + if (plausibilityCount >= 3) fault = true; + return 0; + } + + plausibilityCount = 0; + + // reset fault when pedal is released + if (fault) { + if (pct1 < PEDAL_DEADBAND_PERCENT && pct2 < PEDAL_DEADBAND_PERCENT) { + fault = false; + } else { + return 0; + } + } + + float pct = (pct1 + pct2) * 0.5f; + + if (pct < PEDAL_DEADBAND_PERCENT) { + pct = 0.0f; + } + if (pct > MAX_ACCEL_PERCENT) { + pct = (float)MAX_ACCEL_PERCENT; + } + + return (int16_t)(TORQUE_MAX * (pct / 100.0f)); +} diff --git a/src/main.cpp b/src/main.cpp index cfb0dde..5f0b403 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,16 +11,17 @@ Button driveButton(BUTTON_PIN); DashStatus dashStatus; void setup() { - if (SERIAL_DEBUG) { + Nextion::begin(); + Nextion::bootStatus("INITIALISATION", "System booting up, initialising components"); + + LogLevel serialDebug = LogLevel::NONE; + Logger::begin(serialDebug); + if (serialDebug != LogLevel::NONE) { Serial.begin(115200); while (!Serial); Serial.println("System Ready!"); } - Nextion::begin(); - Nextion::bootStatus("INITIALISATION", "System booting up, initialising components"); - - Logger::begin(); Logger::log(LogLevel::INFO, "Main", "System booting up, initialising components"); mpuController.begin(); From efbbd2068bb4b977fa7f51e8ee8e9f1b9425f477 Mon Sep 17 00:00:00 2001 From: louisbrennan Date: Mon, 4 May 2026 22:01:06 +0100 Subject: [PATCH 4/4] Fix changes and add todos --- include/Logger.h | 3 +-- include/Pedal.h | 1 + include/config.h | 8 +++++++- src/Logger.cpp | 5 ++--- src/Pedal.cpp | 9 +++------ src/main.cpp | 16 +++++++++++++--- 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/include/Logger.h b/include/Logger.h index 92fd613..c342e18 100644 --- a/include/Logger.h +++ b/include/Logger.h @@ -13,11 +13,10 @@ class Logger { private: static SdFs sd; static FsFile logFile; - static LogLevel serialDebug; public: static CircularBuffer _logBuffer; - static bool begin(LogLevel debugLevel); + static bool begin(); static void sync(); static void log(LogLevel level, const char* module, const char* msg); static void process(); diff --git a/include/Pedal.h b/include/Pedal.h index c2e544d..86a8c32 100644 --- a/include/Pedal.h +++ b/include/Pedal.h @@ -1,3 +1,4 @@ +#pragma once #include "config.h" class Pedal { diff --git a/include/config.h b/include/config.h index 9a927a9..44ccd3a 100644 --- a/include/config.h +++ b/include/config.h @@ -8,6 +8,7 @@ // --------- LOGGING ---------- +#define SERIAL_LOG_LEVEL LogLevel::NONE // threshold for logging to serial (NONE to disable) #define LOG_SD false #define LOG_SERIAL false #define MAX_BUF 16 @@ -36,4 +37,9 @@ extern FsFile logFile; // ---------- Adafruit MPU ----------- #define MPU_ACCEL_RANGE MPU6050_RANGE_8_G #define MPU_GYRO_RANGE MPU6050_RANGE_500_DEG -#define MPU_FILTER_BW MPU6050_BAND_21_HZ \ No newline at end of file +#define MPU_FILTER_BW MPU6050_BAND_21_HZ + +// ---------- BAMOCAR ---------- +#define BAMOCAR_RX_ID 0x201 // Teensy → Bamocar +#define BAMOCAR_TX_ID 0x181 // Bamocar → Teensy +extern FlexCAN_T4 Can1; \ No newline at end of file diff --git a/src/Logger.cpp b/src/Logger.cpp index 3128e4c..58f914c 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -17,7 +17,7 @@ void Logger::log(LogLevel level, const char* module, const char* msg) { LogEntry entry; snprintf(entry.data, MAX_LOG_LEN, "[%s] %s: %s", levelToStr(level), module, msg); - if (serialDebug >= level) { + if (SERIAL_LOG_LEVEL >= level) { Serial.println(entry.data); } @@ -26,8 +26,7 @@ void Logger::log(LogLevel level, const char* module, const char* msg) { // serial writing is better done in smaller bursts, so use different buffer } -bool Logger::begin(LogLevel serialDebugLevel) { - serialDebug = serialDebugLevel; +bool Logger::begin() { // SdioConfig(FIFO_SDIO) tells the Teensy to use the fast onboard slot if (!sd.begin(SdioConfig(FIFO_SDIO))) { return false; diff --git a/src/Pedal.cpp b/src/Pedal.cpp index 3b3bed3..a174cae 100644 --- a/src/Pedal.cpp +++ b/src/Pedal.cpp @@ -2,8 +2,7 @@ float Pedal::appsPercent(int raw, int rest, int full) { float pct = (float)(rest - raw) * 100.0f / (float)(rest - full); - if (pct < 0.0f) pct = 0.0f; - if (pct > 100.0f) pct = 100.0f; + pct = std::clamp(pct, 0.0f, 100.0f); return pct; } @@ -33,15 +32,13 @@ int16_t Pedal::read() { return 0; } } + // TODO: cut power if fault persistent for 100ms float pct = (pct1 + pct2) * 0.5f; - if (pct < PEDAL_DEADBAND_PERCENT) { pct = 0.0f; } - if (pct > MAX_ACCEL_PERCENT) { - pct = (float)MAX_ACCEL_PERCENT; - } + pct = std::min(pct, (float)MAX_ACCEL_PERCENT); return (int16_t)(TORQUE_MAX * (pct / 100.0f)); } diff --git a/src/main.cpp b/src/main.cpp index 5f0b403..0f150ac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,14 +9,14 @@ Adafruit_MPU6050 MPU; MpuController mpuController(MPU); Button driveButton(BUTTON_PIN); DashStatus dashStatus; +FlexCAN_T4 Can1; void setup() { Nextion::begin(); Nextion::bootStatus("INITIALISATION", "System booting up, initialising components"); - LogLevel serialDebug = LogLevel::NONE; - Logger::begin(serialDebug); - if (serialDebug != LogLevel::NONE) { + Logger::begin(); + if (SERIAL_LOG_LEVEL != LogLevel::NONE) { Serial.begin(115200); while (!Serial); Serial.println("System Ready!"); @@ -35,4 +35,14 @@ void setup() { void loop() { mpuController.logTelemetry(); //TODO: update dashStatus from CAN + + + + //TODO: Implement brake and APPS signal checking + /* EV2.3.1 — if brakes are mechanically actuated AND APPS signals >25% pedal travel + (or >5kW, whichever is lower) simultaneously for more than 500ms, commanded torque + must be 0 Nm. + EV2.3.2 — once that condition triggers, torque must stay at 0 Nm until APPS drops + below 5% and 0 Nm is commanded — regardless of whether brakes are still on. + */ }