Thank you for the reply. I am using the following BSP: Seed nRF52 Boards, version 1.1.13, non-mbed. I don’t think I inserted the while (serial) statement in my code, but maybe I am missing something. Here is the code: #include <LSM6DS3.h>
#include <Wire.h>
#include <bluefruit.h>
#include <InternalFileSystem.h>
using namespace Adafruit_LittleFS_Namespace;
LSM6DS3 myIMU(I2C_MODE, 0x6A);
// --- PIN BATTERIA INTERNI ALLA XIAO ---
const int PIN_CHARGE_CURRENT = 13; // P0.13
const int PIN_BAT_ENABLE = 14; // P0.14
// --- CONFIGURAZIONE BLUETOOTH ---
uint8_t const SVC_UUID[] = { 0x10, 0x12, 0x8A, 0x76, 0x04, 0xD1, 0x6C, 0x4F, 0x7E, 0x53, 0xF2, 0xE8, 0x01, 0x00, 0xB1, 0x19 };
uint8_t const CHR_ANGLE_UUID[] = { 0x14, 0x12, 0x8A, 0x76, 0x04, 0xD1, 0x6C, 0x4F, 0x7E, 0x53, 0xF2, 0xE8, 0x01, 0x00, 0xB1, 0x19 };
uint8_t const CHR_CMD_UUID[] = { 0x15, 0x12, 0x8A, 0x76, 0x04, 0xD1, 0x6C, 0x4F, 0x7E, 0x53, 0xF2, 0xE8, 0x01, 0x00, 0xB1, 0x19 };
BLEService skateService(SVC_UUID);
BLECharacteristic liveAngleChar(CHR_ANGLE_UUID);
BLECharacteristic commandChar(CHR_CMD_UUID);
bool isRecording = false;
const char* filename = "skate.csv";
const uint32_t MAX_FILE_SIZE = 150000; // Limite massimo 150 KB (~4 minuti continui)
const uint32_t WARNING_SIZE = 120000; // Soglia LED rosso all'80% (120 KB)
File file(InternalFS);
void setup() {
// --- CONFIGURAZIONE BATTERIA (Sicurezza e Ricarica Rapida 100mA) ---
pinMode(PIN_BAT_ENABLE, OUTPUT);
digitalWrite(PIN_BAT_ENABLE, LOW);
pinMode(PIN_CHARGE_CURRENT, OUTPUT);
digitalWrite(PIN_CHARGE_CURRENT, LOW);
// --- CONFIGURAZIONE LED ROSSO DI AVVISO MEMORIA ---
pinMode(LED_RED, OUTPUT);
digitalWrite(LED_RED, HIGH); // HIGH significa SPENTO
myIMU.begin();
// INIZIALIZZA LA MEMORIA INTERNA
InternalFS.begin();
// --- CONFIGURAZIONE BLUETOOTH E MODALITA' STEALTH ---
Bluefruit.begin();
Bluefruit.autoConnLed(false); // Spegne il lampeggio del LED Blu
Bluefruit.setName("XIAO_SKATE");
skateService.begin();
liveAngleChar.setProperties(CHR_PROPS_READ | CHR_PROPS_NOTIFY);
liveAngleChar.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
liveAngleChar.setMaxLen(20);
liveAngleChar.begin();
commandChar.setProperties(CHR_PROPS_WRITE | CHR_PROPS_NOTIFY);
commandChar.setPermission(SECMODE_OPEN, SECMODE_OPEN);
commandChar.setWriteCallback(onCommandWrite);
commandChar.setMaxLen(20);
commandChar.begin();
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addService(skateService);
Bluefruit.Advertising.addName();
Bluefruit.Advertising.start(0);
}
// --- CERVELLO DELLA SCATOLA NERA (Ascolta Python) ---
void onCommandWrite(uint16_t conn_hdl, BLECharacteristic* chr, uint8_t* data, uint16_t len) {
char buf[21] = {0};
uint16_t safe_len = len < 20 ? len : 20;
memcpy(buf, data, safe_len);
String cmd = String(buf);
cmd.trim();
if (cmd == "RECORD") {
// Gestione Dashcam: svuota lo spazio se il file precedente ha riempito la memoria
if (InternalFS.exists(filename)) {
File checkFile = InternalFS.open(filename, FILE_O_READ);
if (checkFile) {
uint32_t f_size = checkFile.size();
checkFile.close();
if (f_size > MAX_FILE_SIZE) {
InternalFS.remove(filename);
}
}
}
file.open(filename, FILE_O_WRITE);
if (file) isRecording = true;
}
else if (cmd == "STOP") {
isRecording = false;
if (file) {
file.close();
digitalWrite(LED_RED, HIGH); // Spegne il LED rosso per sicurezza
}
}
else if (cmd == "DUMP") {
File srcFile = InternalFS.open(filename, FILE_O_READ);
if (srcFile) {
while (srcFile.available()) {
String line = srcFile.readStringUntil('\n');
liveAngleChar.notify(line.c_str(), line.length());
delay(4);
}
srcFile.close();
InternalFS.remove(filename); // Svuota la memoria dopo il download
}
commandChar.notify("DUMP_DONE", 9);
}
// --- COMANDO DI SPEGNIMENTO TOTALE (DEEP SLEEP) ---
else if (cmd == "SHUTDOWN") {
isRecording = false;
if (file) {
file.close(); // Salva eventuali dati in sospeso
digitalWrite(LED_RED, HIGH);
}
// Disconnette il Bluetooth dolcemente
Bluefruit.disconnect(conn_hdl);
delay(500);
// Spegne completamente il processore (Per riaccenderlo basta collegare il cavo USB-C per 1 secondo)
sd_power_system_off();
}
}
void loop() {
if (Bluefruit.connected()) {
// --- LETTURA SULL'ASSE Y (Per montaggio longitudinale) ---
float rollAngle = myIMU.readFloatAccelY() * 90.0;
String angleStr = String(rollAngle, 2);
liveAngleChar.notify(angleStr.c_str(), angleStr.length());
// --- SALVATAGGIO IN MEMORIA (Senza freno a mano) ---
if (isRecording && file) {
uint32_t timestamp = millis();
String dataRow = String(timestamp) + "," + angleStr + "\n";
file.print(dataRow);
// NESSUN FLUSH QUI: Usiamo la RAM per viaggiare fluidi a 100Hz
// Controllo capienza memoria per accendere il LED di allarme
if (file.size() > WARNING_SIZE) {
digitalWrite(LED_RED, LOW); // Accende il LED rosso
} else {
digitalWrite(LED_RED, HIGH); // Mantiene il LED rosso spento
}
}
delay(10); // Velocissimo a 100 cicli al secondo
}
}