Skip to content

Initial new firmware commit#80

Closed
Mandeson wants to merge 38 commits intomasterfrom
new-board-firmware
Closed

Initial new firmware commit#80
Mandeson wants to merge 38 commits intomasterfrom
new-board-firmware

Conversation

@Mandeson
Copy link
Collaborator

No description provided.

…ld BMP280 driver, add possibility to wait for the serial monitor to connect
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This is a major firmware rewrite that replaces the rocket flight computer implementation with a new sensor data acquisition system. The PR removes the old flight control logic, LoRa communication, camera control, SD card storage, and flight state management, replacing them with a simplified system focused on reading data from barometer (BME280), accelerometer (LSM6DSO), and GPS sensors, then buffering the data using lock-free ring buffers.

Key Changes:

  • Removed MPU6050-based IMU and replaced with LSM6DSO32 accelerometer/gyroscope
  • Replaced BMP280 barometer with BME280 (adds humidity sensing)
  • Completely rewrote GPS handling to use raw UART with NMEA decoding instead of TinyGPS++ library
  • Implemented custom I2C driver using ESP-IDF APIs directly
  • Added lock-free SPSC ring buffer library for sensor data buffering
  • Removed all flight control logic, LoRa telemetry, camera control, ignition system, and persistent storage

Reviewed changes

Copilot reviewed 50 out of 52 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
boards/src/storage.cpp New buffering system using lock-free ring buffers, periodically flushes sensor data to serial output
boards/src/i2c.cpp New custom I2C driver implementation using ESP-IDF low-level APIs
boards/src/gps.cpp Rewritten GPS module using UART event-driven processing with custom NMEA decoder
boards/src/barometer.cpp New BME280 barometer driver with pressure, humidity, and temperature sensing
boards/src/accelerometer.cpp New LSM6DSO accelerometer/gyroscope driver with FIFO buffering at 833Hz
boards/src/main.cpp Simplified main loop that periodically reads sensors and posts to storage buffers
boards/lib/ultra-low-latency-ring-buffer/ Added lock-free SPSC ring buffer library for thread-safe sensor data buffering
boards/lib/nmea-decoder/ Added custom NMEA sentence parser for GPS data extraction
boards/platformio.ini Updated build configuration for new hardware target (CosmoChip ESP32-S3) and dependencies
boards/include/board_config.h Simplified board configuration with new I2C and GPS pin definitions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

namespace storage {

// 200ms delay between data flushes
static constexpr inline int DATA_FLUSH_INTERVAL = 1000;
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "200ms delay" but the constant is set to 1000ms (1 second). Either update the comment to say "1000ms delay" or "1 second delay", or change the constant value to 200 if 200ms was intended.

Copilot uses AI. Check for mistakes.
{
.clk_speed = FREQUENCY,
},
.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL,
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The typo in the constant name I2C_SCLK_SRC_FLAG_FOR_NOMAL appears to be from the ESP-IDF library itself (missing 'R' in NORMAL). However, if this constant doesn't exist in the actual library, this will cause a compilation error. Verify that this constant is correctly spelled according to the ESP-IDF version being used, or change it to the correct constant name like I2C_SCLK_SRC_FLAG_FOR_NOMAL (if that's the actual library constant) or potentially just 0 if the constant is unavailable.

Copilot uses AI. Check for mistakes.
#else
#include "lora-uart.h"
#endif
// In miliseconds
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: "miliseconds" should be "milliseconds".

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +60
if (current_head == current_tail) return true;

return false;
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The empty() function can be simplified. The explicit if statement and boolean return is redundant. Replace lines 58-60 with: return current_head == current_tail;

Copilot uses AI. Check for mistakes.
return true;
}

size_t Size() {
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Size() function uses PascalCase naming, which is inconsistent with the empty() and pop() functions that use lowercase. For consistency with C++ standard library conventions and the other methods in this class, rename Size() to size().

Copilot uses AI. Check for mistakes.
TinyGPSPlus tiny_gps;
SemaphoreHandle_t gps_mutex;
static void readLine() {
// Get posision of detected '\n' character
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: "posision" should be "position".

Copilot uses AI. Check for mistakes.
uart_set_pin(UART_BUS_NUMBER, board_config::GPS_UART_TX_PIN, board_config::GPS_UART_RX_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

// Setup pattern recognition to detect '\n' as the and of NMEA line
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: "and" should be "end" (referring to "the end of NMEA line").

Copilot uses AI. Check for mistakes.
constexpr inline int GPS_BAUDRATE = 115200;

#endif
constexpr inline uint8_t LSM6DSO_ADDR = LSM6DSO_I2C_ADD_H; // Alternetively LSM6DSO_I2C_ADD_L
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: "Alternetively" should be "Alternatively".

Suggested change
constexpr inline uint8_t LSM6DSO_ADDR = LSM6DSO_I2C_ADD_H; // Alternetively LSM6DSO_I2C_ADD_L
constexpr inline uint8_t LSM6DSO_ADDR = LSM6DSO_I2C_ADD_H; // Alternatively LSM6DSO_I2C_ADD_L

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +86
static bool performWriteTransaction(uint8_t addr, uint8_t reg, const uint8_t *buffer, uint16_t size,
i2c_cmd_handle_t command) {
if (i2c_master_start(command) != ESP_OK) return FAILURE;
if (i2c_master_write_byte(command, addr << 1, true) != ESP_OK) return FAILURE;
if (i2c_master_write_byte(command, reg, true) != ESP_OK) return FAILURE;
if (i2c_master_write(command, buffer, size, true) != ESP_OK) return FAILURE;
if (i2c_master_stop(command) != ESP_OK) return FAILURE;
if (i2c_master_cmd_begin(BUS_NUMBER, command, TIMEOUT) != ESP_OK) return FAILURE;
return SUCCESS;
}

Result write(uint8_t addr, uint8_t reg, const uint8_t *buffer, uint16_t size) {
if (size > MAX_SUPPORTED_TRANSFER_SIZE) {
Serial.println("I2C: Error: unsupported write size");
return FAILURE;
}

uint8_t command_buffer[I2C_LINK_RECOMMENDED_SIZE(2)] = {0};
i2c_cmd_handle_t command =
i2c_cmd_link_create_static(command_buffer, I2C_LINK_RECOMMENDED_SIZE(2));

if (performWriteTransaction(addr, reg, buffer, size, command) != SUCCESS) {
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function performWriteTransaction returns a bool but is being compared to SUCCESS (which is an enum value 0). While this works, it's inconsistent. The function should either return Result type to match the calling code, or the comparison should be changed to check the boolean return value directly (e.g., if (!performWriteTransaction(...))). Recommend changing the return type to Result for consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +98
if (part[1][0] != ',') {
*outPtr++ = part[1][0];
*outPtr++ = part[1][1];
*outPtr++ = ':';
*outPtr++ = part[1][2];
*outPtr++ = part[1][3];
*outPtr++ = ':';
*outPtr++ = part[1][4];
*outPtr++ = part[1][5];
*outPtr++ = '.';
*outPtr++ = part[1][7];
*outPtr++ = part[1][8];
}

*outPtr++ = ',';

int frac;

if (part[2][0] != ',') {
// Latitude format: DDMM.mmmmm -> DD.ddddddd
frac = ((part[2][2] - '0') * 100'000'000LL + (part[2][3] - '0') * 10'000'000LL +
(part[2][5] - '0') * 1'000'000LL + (part[2][6] - '0') * 100'000LL +
(part[2][7] - '0') * 10'000LL + (part[2][8] - '0') * 1'000LL +
(part[2][9] - '0') * 100LL) /
60;
if (part[2][0] != '0') *outPtr++ = part[2][0];
*outPtr++ = part[2][1];
*outPtr++ = '.';
minutesToString(outPtr, frac);
*outPtr++ = part[3][0];
}

*outPtr++ = ',';

if (part[4][0] != ',') {
// Longitude format: DDDMM.mmmmm -> DDD.ddddddd
frac = ((part[4][3] - '0') * 100'000'000LL + (part[4][4] - '0') * 10'000'000LL +
(part[4][6] - '0') * 1'000'000LL + (part[4][7] - '0') * 100'000LL +
(part[4][8] - '0') * 10'000LL + (part[4][9] - '0') * 1'000LL +
(part[4][10] - '0') * 100LL) /
60;
if (part[4][0] != '0') *outPtr++ = part[4][0];
if (part[4][0] != '0' || part[4][1] != '0') *outPtr++ = part[4][1];
*outPtr++ = part[4][2];
*outPtr++ = '.';
minutesToString(outPtr, frac);
*outPtr++ = part[5][0];
}
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NMEA decoder assumes specific character positions are valid without bounds checking. For example, accessing part[1][7], part[1][8], part[2][9], etc. These accesses could read beyond the actual string data if the NMEA sentence is malformed or truncated. Add validation to ensure each part has sufficient length before accessing specific indices, or the parser may access invalid memory.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this part will be removed too

Copy link
Member

@PatrykPaluch PatrykPaluch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those comments are for discussion at Friday meeting. I wrote them very sparingly

Comment on lines +7 to +9
using Pressure = float;
using Humidity = float;
using Temperature = float;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not a bing fan of aliasing simple types

@@ -0,0 +1,3 @@
#pragma once

enum Result { SUCCESS = 0, FAILURE };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i mean, it just bool, and if i remember correctly, we discussed to use single Result with all error codes defined in it


constexpr inline size_t SENTENCE_PARTS = 15;

static void minutesToString(char *&outPtr, int frac) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why *&, not just ** or std::string or std::span or anything

Comment on lines +23 to +24
Result decode(std::array<char, MAX_OUTPUT_SIZE> &out,
const std::array<char, MAX_SENTENCE_SIZE> &nmea_sentence) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is for longer discussion

static constexpr inline int MAX_SENTENCE_SIZE = 196;
static constexpr inline int MAX_OUTPUT_SIZE = 50;

enum Result { SUCCESS = 0, WRONG_HEADER, WRONG_DATA_FORMAT, BUFFER_OVERFLOW };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not common.h Result

lsm6dso_fifo_tag_t tag;
uint16_t data_count;

if (g_init_error) return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

silent crash (comment in init())

init();
return FAILURE;
}
pressure = static_cast<float>(g_data.pressure); // We don't need double precision
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"We don't need double precision"

why not?

constexpr inline size_t UART_PATTERN_QUEUE_SIZE = 20;

Data gps_data;
QueueHandle_t g_uart_queue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are using many different queue implementation. Here QueueHandle_t, somewhere else is SPSCQueue and i think i saw yet another one different queue type

Comment on lines +30 to +34
gps::init();
storage::init();
i2c::init();
barometer::init();
accelerometer::init();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, those function can fail and there is no error handing

if (last_time == 0) last_time = esp_timer_get_time();

// Wait for the timer to measure given interval
if (xSemaphoreTake(g_main_loop_semaphore, 500) == pdTRUE) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to double check every semaphore operation to make sure there is no race condition or we can try to spend some time on some other safer solution.

Im not sure what happen here with those semaphores in loop() and timers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants