Skip to content
Merged
4 changes: 2 additions & 2 deletions inc/sp140/altimeter.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
#define VARIO_BUFFER_SIZE 25 // Number of samples to average for vertical speed
#define MAX_VERTICAL_SPEED 250.0f // Maximum vertical speed to display (m/s)

// Set up the barometer
bool setupAltimeter(bool alt_wire = false);
// Set up the barometer
bool setupAltimeter();

// Get the altitude (in meters)
float getAltitude(const STR_DEVICE_DATA_140_V1& deviceData);
Expand Down
15 changes: 10 additions & 5 deletions inc/sp140/esc.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@
#define INC_SP140_ESC_H_

#include <Arduino.h>

// Motor temp validity range (disconnected/invalid readings are represented as NaN)
constexpr float MOTOR_TEMP_VALID_MIN_C = -20.0f;
constexpr float MOTOR_TEMP_VALID_MAX_C = 140.0f;

inline bool isMotorTempValidC(float tempC) {
return tempC > MOTOR_TEMP_VALID_MIN_C && tempC <= MOTOR_TEMP_VALID_MAX_C;
}
#include "sp140/structs.h"
#include "../../inc/sp140/esp32s3-config.h"
#include <SineEsc.h>
#include <CanardAdapter.h>

void initESC();
void initESC();
void setESCThrottle(int throttlePWM);
void readESCTelemetry();
bool setupTWAI();

// Get the highest temperature from all ESC sensors
float getHighestTemp(const STR_ESC_TELEMETRY_140& telemetry);

// ESC Error Decoding Functions
String decodeRunningError(uint16_t errorCode);
String decodeRunningError(uint16_t errorCode);
String decodeSelfCheckError(uint16_t errorCode);
bool hasRunningError(uint16_t errorCode);
bool hasSelfCheckError(uint16_t errorCode);
Expand Down
17 changes: 17 additions & 0 deletions inc/sp140/simple_monitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <vector>
#include <functional>
#include <math.h>
#include <Arduino.h>
#include "sp140/structs.h"

Expand Down Expand Up @@ -148,6 +149,14 @@ struct SensorMonitor : public IMonitor {

void check() override {
float v = read();
if (isnan(v)) {
if (last != AlertLevel::OK) {
logger->log(id, AlertLevel::OK, v);
last = AlertLevel::OK;
}
return;
}

AlertLevel now = AlertLevel::OK;

if (v <= thr.critLow) now = AlertLevel::CRIT_LOW;
Expand Down Expand Up @@ -182,6 +191,14 @@ struct HysteresisSensorMonitor : public SensorMonitor {

void check() override {
float v = read();
if (isnan(v)) {
if (last != AlertLevel::OK) {
logger->log(id, AlertLevel::OK, v);
last = AlertLevel::OK;
}
return;
}

AlertLevel now = last; // Start with current state

// Hysteresis logic to prevent bouncing
Expand Down
2 changes: 1 addition & 1 deletion inc/sp140/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ typedef struct {
float mos_temp; // MOSFET temperature (°C)
float cap_temp; // Capacitor temperature (°C)
float mcu_temp; // MCU temperature (°C)
float motor_temp; // Motor temperature (°C)
float motor_temp; // Motor temperature (°C), NaN if sensor invalid/disconnected
int32_t eRPM; // Electrical RPM
uint16_t inPWM; // Input PWM value
uint16_t running_error; // Runtime error bitmask
Expand Down
24 changes: 20 additions & 4 deletions inc/sp140/throttle.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,22 @@ uint16_t readThrottleRaw();
/** Get the most recently sampled raw throttle value (0..4095). */
uint16_t getLastThrottleRaw();

/** Convert raw pot reading (0..4095) to PWM microseconds. */
/** Convert raw pot reading (0..4095) to PWM microseconds (full range). */
int potRawToPwm(uint16_t raw);

/**
* Convert raw pot reading (0..4095) to PWM microseconds using the
* mode-specific maximum. In chill mode the full physical range maps to
* ESC_MIN_PWM..CHILL_MODE_MAX_PWM; in sport mode it maps to the full
* ESC_MIN_PWM..ESC_MAX_PWM range. This gives chill mode full granular
* control over its limited output range instead of a hard clamp.
*
* @param raw Raw ADC value (0..4095)
* @param performance_mode 0 = CHILL, 1 = SPORT
* @return PWM microseconds in the mode's range
*/
int potRawToModePwm(uint16_t raw, uint8_t performance_mode);

/**
* Apply mode-based ramp (us/tick) and clamp to the mode's max PWM.
* Updates `prevPwm` with the final value.
Expand Down Expand Up @@ -86,10 +99,13 @@ bool throttleEngaged();
/**
* Read throttle input and return smoothed PWM value.
* This is the core throttle processing pipeline without any state logic.
* Uses mode-aware mapping so the full physical range covers the mode's
* PWM output range.
*
* @param performance_mode 0 = CHILL, 1 = SPORT
* @return Smoothed PWM value from throttle input
*/
int getSmoothedThrottlePwm();
int getSmoothedThrottlePwm(uint8_t performance_mode);

/**
* Reset throttle state for clean startup/disarm.
Expand All @@ -107,8 +123,8 @@ void handleThrottle();

/**
* Calculate the cruise control PWM value from a raw pot reading.
* Uses the same mapping as normal throttle (full range then clamp to mode max).
* Also applies the absolute cruise max cap.
* Uses the same mode-aware mapping as normal throttle, then applies
* the absolute cruise max cap.
*
* @param potVal Raw potentiometer value (0..4095)
* @param performance_mode 0 = CHILL, 1 = SPORT
Expand Down
19 changes: 7 additions & 12 deletions inc/sp140/utilities.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
#ifndef INC_SP140_UTILITIES_H_
#define INC_SP140_UTILITIES_H_

#include <Arduino.h>

double mapd(double x, double in_min, double in_max, double out_min, double out_max);

// Function for digital time display - adds leading zeros
String convertToDigits(byte digits);

// Function to get unique chip ID
String chipId();
#ifndef INC_SP140_UTILITIES_H_
#define INC_SP140_UTILITIES_H_

#include <Arduino.h>

// Function to get unique chip ID
String chipId();

// Definitions for main rainbow colors in WRGB format for NeoPixel.
// The 32-bit color value is WRGB. W (White) is ignored for RGB pixels.
Expand Down
31 changes: 0 additions & 31 deletions inc/sp140/vibration_pwm.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,35 +74,4 @@ void pulseVibration(uint16_t duration_ms, uint8_t intensity);
*/
void stopVibration();

/**
* @brief Initializes the critical alert service.
*
* This function sets up the necessary resources for handling synchronized critical alerts.
* It should be called once during system initialization.
*/
void initCriticalAlertService();

/**
* @brief Starts the critical alert notifications.
*
* Activates the synchronized vibration and border flashing. If alerts are
* already running, this function has no effect.
*/
void startCriticalAlerts();

/**
* @brief Stops the critical alert notifications.
*
* Deactivates the vibration and border flashing. If alerts are not currently
* running, this function has no effect.
*/
void stopCriticalAlerts();

/**
* @brief Checks if the critical alert system is currently active.
*
* @return true if critical alerts are active, false otherwise.
*/
bool isCriticalAlertActive();

#endif // INC_SP140_VIBRATION_PWM_H_
3 changes: 0 additions & 3 deletions src/sp140/alert_display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ void initAlertDisplay() {
return;
}

// Init the new alert service
initCriticalAlertService();

// Create aggregation task (low priority)
xTaskCreate(alertAggregationTask, "AlertAgg", 4096, NULL, 1, &alertAggregationTaskHandle);
USBSerial.println("[AlertDisplay] Init complete");
Expand Down
12 changes: 5 additions & 7 deletions src/sp140/altimeter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,11 @@ float getBaroPressure() {
return __FLT_MIN__; // Return a very small number if BMP is not present
}

// Start the bmp3XX sensor
bool setupAltimeter(bool altWire) {
TwoWire* wire = &Wire;

// pull down pin 40 to high to set the address
pinMode(40, OUTPUT);
digitalWrite(40, HIGH);
// Start the bmp3XX sensor
bool setupAltimeter() {
// pull down pin 40 to high to set the address
pinMode(40, OUTPUT);
digitalWrite(40, HIGH);
if (!bmp.begin_I2C(BMP3XX_DEFAULT_ADDRESS, &Wire)) return false;

bmp.setOutputDataRate(BMP3_ODR_25_HZ);
Expand Down
2 changes: 2 additions & 0 deletions src/sp140/ble/esc_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ void updateESCPackedTelemetry(const STR_ESC_TELEMETRY_140& telemetry) {
packet.mos_temp = telemetry.mos_temp;
packet.cap_temp = telemetry.cap_temp;
packet.mcu_temp = telemetry.mcu_temp;
// motor_temp is NaN when sensor is disconnected/invalid.
packet.motor_temp = telemetry.motor_temp;
packet.eRPM = static_cast<int32_t>(telemetry.eRPM);
packet.inPWM = static_cast<uint16_t>(telemetry.inPWM);
Expand All @@ -118,6 +119,7 @@ void updateESCTelemetryBLE(const STR_ESC_TELEMETRY_140& telemetry) {
float voltage = telemetry.volts;
float current = telemetry.amps;
int32_t rpm = static_cast<int32_t>(telemetry.eRPM);
// motor_temp is NaN when sensor is disconnected/invalid.
EscTempsPacket temps = {
telemetry.mos_temp,
telemetry.cap_temp,
Expand Down
31 changes: 12 additions & 19 deletions src/sp140/esc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,23 +94,25 @@ void readESCTelemetry() {
escTelemetryData.amps = res->bus_current / 10.0f;
escTelemetryData.mos_temp = res->mos_temp / 10.0f;
escTelemetryData.cap_temp = res->cap_temp / 10.0f;
escTelemetryData.mcu_temp = res->mcu_temp / 10.0f;
escTelemetryData.mcu_temp = res->mcu_temp / 10.0f;
// Filter motor temp - only update if sensor is connected (valid range: -20°C to 140°C)
// Disconnected sensor reads ~149°C (thermistor pulled high)
float rawMotorTemp = res->motor_temp / 10.0f;
if (rawMotorTemp > -20.0f && rawMotorTemp <= 140.0f) {
if (isMotorTempValidC(rawMotorTemp)) {
escTelemetryData.motor_temp = rawMotorTemp;
} else {
// Store invalid motor temp as NaN. Downstream consumers can skip on isnan().
escTelemetryData.motor_temp = NAN;
}
// else: keep previous value (sensor disconnected or invalid)
escTelemetryData.eRPM = res->speed;
escTelemetryData.inPWM = res->recv_pwm / 10.0f;
watts = escTelemetryData.amps * escTelemetryData.volts;

// Store error bitmasks
escTelemetryData.running_error = res->running_error;
escTelemetryData.selfcheck_error = res->selfcheck_error;

// Record the time of this successful communication using the local clock
// Store error bitmasks
escTelemetryData.running_error = res->running_error;
escTelemetryData.selfcheck_error = res->selfcheck_error;
// Record the time of this successful communication using the local clock
lastSuccessfulCommTimeMs = millis();
} // else: Timestamp hasn't changed, treat as stale data, don't update local timer or telemetry

Expand Down Expand Up @@ -299,19 +301,10 @@ double mapDouble(double x, double in_min, double in_max, double out_min, double
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

/**
* Get the highest temperature reading from all ESC sensors
* @param telemetry ESC telemetry data structure
* @return The highest temperature value among motor, MOSFET, and capacitor temps
*/
float getHighestTemp(const STR_ESC_TELEMETRY_140& telemetry) {
return max(telemetry.motor_temp, max(telemetry.mos_temp, telemetry.cap_temp));
}

/**
* Decode running error bitmask into human-readable string
* @param errorCode 16-bit running error code from ESC
* @return String containing decoded error messages
* @param errorCode 16-bit running error code from ESC
* @return String containing decoded error messages
*/
String decodeRunningError(uint16_t errorCode) {
if (errorCode == 0) {
Expand Down
2 changes: 1 addition & 1 deletion src/sp140/esc_monitors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ void addESCMonitors() {
monitors.push_back(escCapTemp);

// Motor Temperature Monitor - with hysteresis
// Note: Invalid readings (>140°C = disconnected) are filtered in esc.cpp
// Return NaN for disconnected/invalid sensor so monitor logic skips it.
static HysteresisSensorMonitor* motorTemp = new HysteresisSensorMonitor(
SensorID::Motor_Temp,
SensorCategory::ESC,
Expand Down
6 changes: 3 additions & 3 deletions src/sp140/lvgl/lvgl_updates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "../../../inc/sp140/globals.h"
#include "../../../inc/sp140/vibration_pwm.h"
#include "../../../inc/sp140/shared-config.h"
#include <math.h>

// Flash timer globals - definitions
lv_timer_t* cruise_flash_timer = NULL;
Expand Down Expand Up @@ -704,9 +705,8 @@ void updateLvglMainScreen(
lv_obj_remove_style(motor_temp_bg, &style_warning, 0);
lv_obj_remove_style(motor_temp_bg, &style_critical, 0);

// Show motor temp if ESC connected and reading is valid (not disconnected)
// Disconnected sensor reads ~149°C, so treat > 140°C as invalid
if (escTelemetry.escState == TelemetryState::CONNECTED && motorTemp > -20.0f && motorTemp <= 140.0f) {
// Show motor temp only for a valid numeric reading while ESC is connected.
if (escTelemetry.escState == TelemetryState::CONNECTED && !isnan(motorTemp)) {
lv_label_set_text_fmt(motor_temp_label, "%d", static_cast<int>(motorTemp));

if (motorTemp >= motorTempThresholds.critHigh) {
Expand Down
6 changes: 3 additions & 3 deletions src/sp140/sp140.ino
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ void setup140() {
const int SDA_PIN = 44;
const int SCL_PIN = 41;
Wire.setPins(SDA_PIN, SCL_PIN);
if (!setupAltimeter(board_config.alt_wire)) {
if (!setupAltimeter()) {
USBSerial.println("Error initializing BMP3xx barometer");
}
if (ENABLE_VIBE) {
Expand Down Expand Up @@ -1102,7 +1102,7 @@ void handleThrottle() {
break;

case ARMED:
int smoothedPwm = getSmoothedThrottlePwm();
int smoothedPwm = getSmoothedThrottlePwm(deviceData.performance_mode);
finalPwm = applyModeRampClamp(smoothedPwm, prevPwm, deviceData.performance_mode);
break;
}
Expand Down Expand Up @@ -1178,7 +1178,7 @@ void afterCruiseEnd() {
// Instead of clearing the buffer which causes throttle to drop to 0,
// pre-populate it with the current throttle position to ensure smooth transition
int currentPotVal = readThrottleRaw();
int currentPwmVal = potRawToPwm(currentPotVal);
int currentPwmVal = potRawToModePwm(currentPotVal, deviceData.performance_mode);

// Pre-fill the buffer with current pot value for smooth transition
throttleFilterReset(currentPwmVal);
Expand Down
Loading