Firmware v3.3 + calibration tool v3 : debug BLE, timeline tirs, minSensors

Firmware (xiao_airsoft_pro.ino) :
- Persistance config en flash (InternalFileSystem / LittleFS)
- Mode debug activable via BLE : octet fixe offset 28 du payload config
- minSensors par défaut : 2 → 3 (exige les 3 capteurs simultanément)
- Toutes les fenêtres trigger à 60ms (> DEBUG_RATE 50ms)

Calibration tool (xiao_calibration_tool.py) :
- Scan BLE par nom automatique (30s), connexion directe si adresse fournie
- Config + debug FULL envoyés automatiquement à la connexion
- NUM0 : cycle debug OFF/RAW/TRIGGERS/FULL
- NUM6/4 : ajustement minSensors 1-3 en temps réel
- 4ème graphique : timeline des tirs détectés (barres oranges)
- Layout 4 sous-graphiques avec height_ratios=[3,3,3,1]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 12:18:02 +01:00
parent d30569e6a1
commit 6e1241f6bb
2 changed files with 209 additions and 48 deletions

View File

@@ -16,7 +16,9 @@
#define LED_BLUE 13
// ====== FLASH ======
#define FLASH_STORAGE_START 0x7F000
#define FLASH_STORAGE_START 0x7F000 // Pairing MAC
#define FLASH_CONFIG_START 0x7E000 // ShotConfig persistée
#define CONFIG_MAGIC_NUMBER 0xDEADBEEF
// ====== ENUMS (déclarés en premier) ======
enum LedState {
@@ -168,10 +170,10 @@ void initDefaultConfig() {
shotConfig.accelThreshold = 2.5f;
shotConfig.gyroThreshold = 200.0f;
shotConfig.audioThreshold = 3000; // Seuil PDM (0-32767)
shotConfig.accelWindow = 20;
shotConfig.gyroWindow = 20;
shotConfig.audioWindow = 15;
shotConfig.minSensors = 2;
shotConfig.accelWindow = 60; // augmenté pour visibilité debug (>DEBUG_RATE 50ms)
shotConfig.gyroWindow = 60; // augmenté pour visibilité debug (>DEBUG_RATE 50ms)
shotConfig.audioWindow = 60; // augmenté pour visibilité debug (>DEBUG_RATE 50ms)
shotConfig.minSensors = 3;
shotConfig.shotCooldown = 80;
shotConfig.useAccel = true;
shotConfig.useGyro = true;
@@ -235,6 +237,44 @@ bool isAuthorized(const char* mac) {
return (strcmp(pairing.authorizedMAC, mac) == 0);
}
// ════════════════════════════════════════════════
// CONFIG FLASH (page 0x7E000)
// ════════════════════════════════════════════════
struct StoredConfig {
uint32_t magic;
ShotConfig config;
uint8_t reserved[28]; // padding pour aligner sur 4 bytes
};
void saveConfigToFlash() {
StoredConfig sc;
sc.magic = CONFIG_MAGIC_NUMBER;
memcpy(&sc.config, &shotConfig, sizeof(ShotConfig));
memset(sc.reserved, 0xFF, sizeof(sc.reserved));
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een; while(NRF_NVMC->READY==NVMC_READY_READY_Busy){}
NRF_NVMC->ERASEPAGE = FLASH_CONFIG_START; while(NRF_NVMC->READY==NVMC_READY_READY_Busy){}
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen; while(NRF_NVMC->READY==NVMC_READY_READY_Busy){}
uint32_t* src = (uint32_t*)&sc;
uint32_t* dst = (uint32_t*)FLASH_CONFIG_START;
for (int i = 0; i < (int)(sizeof(StoredConfig)/4); i++) {
dst[i] = src[i]; while(NRF_NVMC->READY==NVMC_READY_READY_Busy){}
}
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren; while(NRF_NVMC->READY==NVMC_READY_READY_Busy){}
Serial.println("💾 Config sauvegardée en flash");
}
void loadConfigFromFlash() {
StoredConfig* sc = (StoredConfig*)FLASH_CONFIG_START;
if (sc->magic == CONFIG_MAGIC_NUMBER) {
memcpy(&shotConfig, &sc->config, sizeof(ShotConfig));
Serial.println("📂 Config chargée depuis flash");
} else {
Serial.println("📂 Pas de config flash → valeurs par défaut");
initDefaultConfig();
}
}
// ════════════════════════════════════════════════
// CALLBACKS BLE
// ════════════════════════════════════════════════
@@ -263,6 +303,21 @@ void onConfigWrite(BLEDevice central, BLECharacteristic c) {
if (len >= (int)sizeof(ShotConfig)) {
memcpy(&shotConfig, data, sizeof(ShotConfig));
Serial.println("⚙️ Config reçue"); printConfig();
saveConfigToFlash();
}
// Octet optionnel : debug mode à l'offset fixe 28 (si != 0xFF)
// Offset fixe 28 = après toute ShotConfig + padding, dans le zone de padding 32 bytes
const int debugByteIdx = 28;
if (len > debugByteIdx && data[debugByteIdx] != 0xFF) {
uint8_t dm = data[debugByteIdx] & 0x03; // 0-3
debugMode = (DebugMode)dm;
Serial.print("🐛 Debug BLE → ");
switch(debugMode){
case DEBUG_OFF: Serial.println("OFF"); break;
case DEBUG_RAW: Serial.println("RAW"); break;
case DEBUG_TRIGGERS: Serial.println("TRIGGERS"); break;
case DEBUG_FULL: Serial.println("FULL"); break;
}
}
}
@@ -405,7 +460,7 @@ void setup() {
pinMode(LED_BLUE, OUTPUT);
changeLedState(LED_BOOT);
initDefaultConfig();
loadConfigFromFlash(); // charge depuis flash ou valeurs par défaut
loadPairingData();
// IMU