Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b6e0b41
Add OTA BLE service and device settings support
zjwhitehead Jan 26, 2026
2bf9acf
Update BLE OTA service to Espressif standard
zjwhitehead Jan 26, 2026
dd8cc21
Enable OTA rollback and confirm firmware validity
zjwhitehead Jan 26, 2026
4833b13
Refactor BLE OTA service to Espressif sector/CRC protocol
zjwhitehead Jan 26, 2026
a9a2b10
Update ota_service.cpp
zjwhitehead Jan 26, 2026
cd1304f
Enhance OTA service with image size tracking and validation
zjwhitehead Jan 26, 2026
faeb2ca
Update BLE OTA service UUIDs and protocol
zjwhitehead Jan 26, 2026
756d6fb
linting
zjwhitehead Jan 26, 2026
17f3d00
Remove extra internal logging
zjwhitehead Jan 27, 2026
d2fbb5b
Switch OTA CRC to CRC16-CCITT for compatibility
zjwhitehead Jan 28, 2026
8dcd3f5
Improve OTA BLE service and LVGL display handling
zjwhitehead Jan 28, 2026
266f5ec
Update main.cpp
zjwhitehead Jan 28, 2026
fa65b02
Remove unused alt_wire parameter from setupAltimeter
zjwhitehead Jan 28, 2026
f0b0b5a
Defer BLE advertising until after splash screen
zjwhitehead Jan 28, 2026
e77322e
Refactor boot sequence and RTOS setup for clarity
zjwhitehead Jan 28, 2026
5638fc0
linting and cleanup
zjwhitehead Jan 29, 2026
3e8a503
Enable app rollback and improve OTA validation logic
zjwhitehead Jan 29, 2026
eb8ab17
tweak LED ram usage
zjwhitehead Jan 29, 2026
5de7963
Refactor altimeter sensor handling to dedicated task
zjwhitehead Jan 29, 2026
fd40868
Refactor altimeter data sharing using FreeRTOS queue
zjwhitehead Jan 30, 2026
0e70ac1
Improve BLE OTA timeout and conn params
zjwhitehead Feb 17, 2026
b5cd41e
Merge branch 'master' into esp-idf
zjwhitehead Feb 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
with:
python-version: 3.x
- run: pip install cpplint
- run: cpplint --linelength 140 --filter=-legal/copyright,-runtime/int,-build/include_subdir,-readability/casting,-readability/todo,-build/include_order,-build/include_what_you_use --recursive ./inc/ ./lib/ ./src/
- run: cpplint --linelength 140 --filter=-legal/copyright,-runtime/int,-build/include_subdir,-readability/casting,-readability/todo,-build/include_order,-build/include_what_you_use,-whitespace/newline --recursive ./inc/ ./lib/ ./src/

pio-test:
name: PlatformIO Tests
Expand Down
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(openppg_controller)
62 changes: 31 additions & 31 deletions inc/sp140/altimeter.h
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
#ifndef INC_SP140_ALTIMETER_H_
#define INC_SP140_ALTIMETER_H_
#include <Arduino.h>
#include <CircularBuffer.hpp>
#include "sp140/structs.h"
#include "sp140/shared-config.h"
// Constants
#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);
// Get the altitude (in meters)
float getAltitude(const STR_DEVICE_DATA_140_V1& deviceData);
// Get the vertical speed in meters per second
float getVerticalSpeed();
// Set the ground altitude to the current altitude to compute AGL
void setGroundAltitude(const STR_DEVICE_DATA_140_V1& deviceData);
// Get the temperature in degrees Celsius
float getBaroTemperature();
// Get the pressure in hPa
float getBaroPressure();
#endif // INC_SP140_ALTIMETER_H_
#ifndef INC_SP140_ALTIMETER_H_
#define INC_SP140_ALTIMETER_H_

#include <Arduino.h>
#include <CircularBuffer.hpp>
#include "sp140/structs.h"
#include "sp140/shared-config.h"

// Constants
#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();

// Get the altitude (in meters)
float getAltitude(const STR_DEVICE_DATA_140_V1& deviceData);

// Get the vertical speed in meters per second
float getVerticalSpeed();

// Set the ground altitude to the current altitude to compute AGL
void setGroundAltitude(const STR_DEVICE_DATA_140_V1& deviceData);

// Get the temperature in degrees Celsius
float getBaroTemperature();

// Get the pressure in hPa
float getBaroPressure();

#endif // INC_SP140_ALTIMETER_H_
1 change: 0 additions & 1 deletion inc/sp140/ble.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,5 @@ extern NimBLECharacteristic* pThrottleCharacteristic;
extern NimBLECharacteristic* pDeviceStateCharacteristic;
extern NimBLEServer* pServer;
extern volatile bool deviceConnected;
extern volatile bool oldDeviceConnected;

#endif // INC_SP140_BLE_H_
6 changes: 6 additions & 0 deletions inc/sp140/ble/ble_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@ void setupBLE();
// Allow modules to trigger advertising after disconnects.
void restartBLEAdvertising();

// Request fast connection parameters (15ms interval) for OTA transfers.
void requestFastConnParams();

// Restore normal connection parameters (~36ms interval) after OTA.
void requestNormalConnParams();

#endif // INC_SP140_BLE_BLE_CORE_H_
7 changes: 7 additions & 0 deletions inc/sp140/ble/ble_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,11 @@
#define CONTROLLER_SERVICE_UUID "01C63B60-0891-4655-BBCA-8E745C48A175"
#define CONTROLLER_TELEMETRY_UUID "01C63B61-0891-4655-BBCA-8E745C48A176"

// Espressif Standard OTA Service UUIDs (Android esp-ble-ota-android app)
static const NimBLEUUID OTA_SERVICE_UUID("00008018-0000-1000-8000-00805f9b34fb");
static const NimBLEUUID OTA_RECV_FW_UUID("00008020-0000-1000-8000-00805f9b34fb"); // Firmware data (Write/Indicate)
static const NimBLEUUID OTA_PROGRESS_UUID("00008021-0000-1000-8000-00805f9b34fb"); // Progress (Indicate)
static const NimBLEUUID OTA_COMMAND_UUID("00008022-0000-1000-8000-00805f9b34fb"); // Command (Write/Indicate)
static const NimBLEUUID OTA_CUSTOMER_UUID("00008023-0000-1000-8000-00805f9b34fb"); // Customer (Indicate)

#endif // INC_SP140_BLE_BLE_IDS_H_
30 changes: 30 additions & 0 deletions inc/sp140/ble/ota_service.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef INC_SP140_BLE_OTA_SERVICE_H_
#define INC_SP140_BLE_OTA_SERVICE_H_

#include <NimBLEDevice.h>

/**
* Initialize the OTA BLE service.
* @param pServer Pointer to the NimBLEServer instance.
*/
void initOtaBleService(NimBLEServer* pServer);

/**
* Check if an OTA update is currently in progress.
* @return true if OTA is active (should pause other tasks)
*/
bool isOtaInProgress();

/**
* Abort any in-progress OTA update and reset state.
* Safe to call even if no OTA is in progress.
*/
void abortOta();

/**
* Check if OTA has been idle too long and abort if so.
* Call periodically (e.g. from a monitoring task).
*/
void checkOtaTimeout();

#endif // INC_SP140_BLE_OTA_SERVICE_H_
1 change: 1 addition & 0 deletions inc/sp140/bms.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ extern STR_BMS_TELEMETRY_140 bmsTelemetryData;
extern BMS_CAN* bms_can;

// BMS functions
bool initBMSCAN(SPIClass* spi);
void updateBMSData();
void printBMSData();
17 changes: 17 additions & 0 deletions inc/sp140/device_settings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef INC_SP140_DEVICE_SETTINGS_H_
#define INC_SP140_DEVICE_SETTINGS_H_

#include "structs.h"
#include "esp32s3-config.h"

// Constants
extern const unsigned int DEFAULT_SCREEN_ROTATION;
void refreshDeviceData();
void writeDeviceData();
void resetDeviceData();
void parse_serial_commands();
void send_device_data();
bool sanitizeDeviceData();
void debugHardwareConfig(const HardwareConfig& config);

#endif // INC_SP140_DEVICE_SETTINGS_H_
28 changes: 10 additions & 18 deletions inc/sp140/esc.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +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>

// Temperature thresholds
#define ESC_MOS_WARN 90
#define ESC_MOS_CRIT 110
#define ESC_MCU_WARN 80
#define ESC_MCU_CRIT 95
#define ESC_CAP_WARN 85
#define ESC_CAP_CRIT 100
#define MOTOR_WARN 105
#define MOTOR_CRIT 150

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);

// Function declarations
TempState checkTempState(float temp, TempComponent component);

// ESC Error Decoding Functions
String decodeRunningError(uint16_t errorCode);
// ESC Error Decoding Functions
String decodeRunningError(uint16_t errorCode);
String decodeSelfCheckError(uint16_t errorCode);
bool hasRunningError(uint16_t errorCode);
bool hasSelfCheckError(uint16_t errorCode);
Expand Down
2 changes: 2 additions & 0 deletions inc/sp140/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ extern UnifiedBatteryData unifiedBatteryData; // Instance to hold battery data

extern STR_BMS_TELEMETRY_140 bmsTelemetryData;

extern AltimeterTelemetry altimeterData;

// Hardware/Connection Status Flags
extern bool bmsCanInitialized;
extern bool escTwaiInitialized;
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
32 changes: 11 additions & 21 deletions inc/sp140/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,6 @@

#pragma pack(push, 1)

// Temperature component enum
enum TempComponent {
COMP_ESC_MOS = 0,
COMP_ESC_MCU = 1,
COMP_ESC_CAP = 2,
COMP_MOTOR = 3
};

enum TempState {
TEMP_INVALID = -1, // Sensor reading is invalid
TEMP_NORMAL = 0,
TEMP_WARNING = 1,
TEMP_CRITICAL = 2
};

// Telemetry connection state
enum class TelemetryState : uint8_t {
NOT_CONNECTED, // Never received data since boot
Expand All @@ -42,11 +27,6 @@ typedef struct {
uint8_t statusFlag;
word checksum;
unsigned long lastUpdateMs; // Timestamp of last telemetry update
TempState mos_state; // MOS temperature state
TempState mcu_state; // MCU temperature state
TempState cap_state; // CAP temperature state
TempState motor_state; // Motor temperature state
bool temp_sensor_error; // True if any sensor is invalid
TelemetryState escState; // Current connection state
uint16_t running_error; // Runtime error bitmask
uint16_t selfcheck_error; // Self-check error bitmask
Expand Down Expand Up @@ -119,6 +99,16 @@ typedef struct {
} STR_BMS_TELEMETRY_140;
#pragma pack(pop)

// Altimeter telemetry - read by multiple tasks, written by altimeter task only
struct AltimeterTelemetry {
float altitude; // Relative altitude AGL (meters)
float temperature; // Barometer temperature (°C)
float pressure; // Barometric pressure (hPa)
float verticalSpeed; // Vertical speed (m/s)
unsigned long lastUpdate; // Timestamp of last reading (millis)
bool connected; // Sensor available and responding
};

// Add this struct definition near other structs
struct MelodyRequest {
uint16_t* notes;
Expand Down Expand Up @@ -167,7 +157,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
Loading
Loading