Advertisement
bipping

FreehareV4.cpp

Mar 19th, 2025
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 18.43 KB | None | 0 0
  1. /**
  2.  * Système d'Acquisition Haute Fréquence pour Accéléromètre sur Arduino
  3.  *
  4.  * Ce programme implémente une architecture d'acquisition optimisée pour maximiser
  5.  * la fréquence d'échantillonnage d'un accéléromètre ADXL335 sur Arduino Uno
  6.  *
  7.  * Principes architecturaux:
  8.  * - Séparation stricte des phases d'acquisition et de sauvegarde
  9.  * - Compression différentielle adaptative avec multiple formats d'encodage
  10.  * - Accès direct aux registres matériels pour maximiser les performances
  11.  *
  12.  * Le programme utilise cinq formats de stockage différents selon les caractéristiques
  13.  * du signal, avec un ratio de compression moyen attendu d'environ 35-40%.
  14.  */
  15. #include <SPI.h>
  16. #include <SD.h>
  17. #include <avr/wdt.h>
  18.  
  19. // Constantes système
  20. #define PIN_CAPTEUR A2
  21. #define PIN_CS_SD 4
  22. #define DUREE_ACQUISITION_S 7
  23. #define BAUD_RATE 115200
  24.  
  25. // Constantes pour l'algorithme de compression
  26. #define MARQUEUR_VALEUR_COMPLETE 0xFF  // Valeur complète avec temps (5 octets)
  27. #define MARQUEUR_DELTA_TEMPS_STD 0x7F  // Delta avec temps fixe 1ms (2 octets)
  28. #define MARQUEUR_DELTA_TEMPS_VAR 0x7E  // Delta avec temps variable (3 octets)
  29. #define MARQUEUR_RLE_DELTA       0x7D  // Séquence répétitive (3 octets)
  30. #define MARQUEUR_BLOC_TEMPOREL   0x7C  // Bloc multi-échantillons (7+ octets)
  31.  
  32. // Paramètres de compression
  33. #define COMPTEUR_RLE_MIN         3     // Seuil minimal de rentabilité RLE
  34. #define COMPTEUR_RLE_MAX         255   // Valeur maximale encodable sur 1 octet
  35. #define TAILLE_BLOC_MAX          8     // Nombre max d'échantillons par bloc
  36. #define DELTA_MAX                126   // Valeur maximale encodable sur 8 bits signé
  37. #define DELTA_MIN                -126  // Valeur minimale encodable (réserve -127,-128)
  38. #define INTERVALLE_TEMPS_STD     1     // Intervalle standard (ms)
  39. #define INTERVALLE_TEMPS_MAX     20    // Limite pour accumuler les échantillons
  40.  
  41. // Macro de vérification pour uniformiser les contrôles
  42. #define VERIFIER_ESPACE_BUFFER(taille, actionEchec) \
  43.     if (indexBuffer + (taille) > BUFFER_SIZE) { \
  44.         Serial.println(F("Buffer plein")); \
  45.         acquisitionTerminee = true; \
  46.         actionEchec; \
  47.     }
  48.  
  49. // Constantes de conversion et calibration
  50. #define ZERO_G_VALUE 261  // Valeur ADC représentant 0g (à calibrer)
  51. #define SCALE_FACTOR 21   // Facteur de conversion ADC → mg (millièmes de g)
  52.  
  53. // Optimisation mémoire critique
  54. #define BUFFER_SIZE 1024  // Buffer principal (50% de la RAM)
  55.  
  56. // Buffer principal de stockage des données compressées
  57. uint8_t buffer[BUFFER_SIZE];
  58. uint16_t indexBuffer = 0;        // Position courante dans le buffer
  59. uint16_t nbEchantillons = 0;     // Compteur d'échantillons acquis
  60. int16_t dernierAccel = 0;        // Dernière valeur d'accélération stockée
  61. uint16_t dernierTemps = 0;       // Dernier horodatage relatif stocké
  62.  
  63. // Variables de contrôle du timing
  64. volatile bool acquisitionTerminee = false;
  65. volatile uint8_t compteurSecondes = 0;
  66. uint16_t tempsDebutAcquisition = 0;
  67.  
  68. // Interface d'accès à la carte SD
  69. File fichierSD;
  70.  
  71. // Structure pour l'état de compression RLE
  72. typedef struct {
  73.     int8_t  dernierDeltaAccel;   // Dernier delta d'accélération encodé
  74.     uint8_t compteurRLE;         // Compteur de répétitions en cours
  75.     bool    modeRLE;             // Indicateur de séquence RLE active
  76. } EtatCompressionRLE;
  77.  
  78. // Instance globale pour la compression RLE
  79. EtatCompressionRLE etatRLE = {0, 0, false};
  80.  
  81. // Buffer temporaire pour compression multi-échantillons
  82. int16_t bufferAcceleration[TAILLE_BLOC_MAX];
  83. uint16_t bufferTemps[TAILLE_BLOC_MAX];
  84. uint8_t nbEchantillonsBuffer = 0;
  85.  
  86. /**
  87.  * Routine d'interruption du Watchdog Timer
  88.  */
  89. ISR(WDT_vect) {
  90.     if (++compteurSecondes >= DUREE_ACQUISITION_S) {
  91.         acquisitionTerminee = true;
  92.         wdt_disable();
  93.     }
  94. }
  95.  
  96. /**
  97.  * Lecture ADC avec accès direct aux registres
  98.  */
  99. inline uint16_t lectureADC(uint8_t pin) {
  100.     ADMUX = (ADMUX & 0xF0) | (pin & 0x07);
  101.     ADCSRA |= (1 << ADSC);
  102.     while (ADCSRA & (1 << ADSC));
  103.     uint8_t low = ADCL;
  104.     uint8_t high = ADCH;
  105.     return (high << 8) | low;
  106. }
  107.  
  108. /**
  109.  * Configuration du Watchdog comme base de temps précise
  110.  */
  111. void configurerWatchdog() {
  112.     cli();
  113.     MCUSR &= ~(1 << WDRF);
  114.     WDTCSR |= (1 << WDCE) | (1 << WDE);
  115.     WDTCSR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1);  // ~1s
  116.     sei();
  117. }
  118.  
  119. /**
  120.  * Calcul optimisé de la conversion ADC vers accélération
  121.  */
  122. inline int16_t calcule_optimise(int32_t x) {
  123.     int32_t tmp = ZERO_G_VALUE - x;
  124.     int32_t result = (SCALE_FACTOR * tmp) >> 10;
  125.     return (int16_t)result;
  126. }
  127.  
  128. /**
  129.  * Stockage d'une valeur complète avec horodatage
  130.  */
  131. inline void stockerValeurComplete(int16_t valeur, uint16_t temps) {  
  132.     buffer[indexBuffer++] = MARQUEUR_VALEUR_COMPLETE;
  133.     buffer[indexBuffer++] = (valeur >> 8) & 0xFF;
  134.     buffer[indexBuffer++] = valeur & 0xFF;
  135.     buffer[indexBuffer++] = (temps >> 8) & 0xFF;
  136.     buffer[indexBuffer++] = temps & 0xFF;
  137.    
  138.     dernierAccel = valeur;
  139.     dernierTemps = temps;
  140.     nbEchantillons++;
  141. }
  142.  
  143. /**
  144.  * Finalisation d'une séquence RLE active
  145.  */
  146. inline bool finaliserSequenceRLE() {
  147.     if (!etatRLE.modeRLE || etatRLE.compteurRLE < COMPTEUR_RLE_MIN) {
  148.         return false;
  149.     }
  150.    
  151.     VERIFIER_ESPACE_BUFFER(3, return false)
  152.    
  153.     buffer[indexBuffer++] = MARQUEUR_RLE_DELTA;
  154.     buffer[indexBuffer++] = etatRLE.compteurRLE;
  155.     buffer[indexBuffer++] = etatRLE.dernierDeltaAccel;
  156.     etatRLE.compteurRLE = 0;
  157.     etatRLE.modeRLE = false;
  158.     return true;
  159. }
  160.  
  161. /**
  162.  * Compression différentielle avec Run-Length Encoding
  163.  */
  164. bool stockerDeltaValeurRLE(int16_t valeur, uint16_t temps) {
  165.     int16_t deltaAccel = valeur - dernierAccel;
  166.     uint16_t deltaTemps = temps - dernierTemps;
  167.    
  168.     if (deltaAccel < DELTA_MIN || deltaAccel > DELTA_MAX || deltaTemps > 255) {
  169.         finaliserSequenceRLE();
  170.         etatRLE.modeRLE = false;
  171.         etatRLE.compteurRLE = 0;
  172.         return false;
  173.     }
  174.    
  175.     if (deltaAccel == etatRLE.dernierDeltaAccel && deltaTemps == INTERVALLE_TEMPS_STD) {
  176.         if (!etatRLE.modeRLE) {
  177.             etatRLE.modeRLE = true;
  178.             etatRLE.compteurRLE = 1;
  179.         } else {
  180.             etatRLE.compteurRLE++;
  181.            
  182.             if (etatRLE.compteurRLE == COMPTEUR_RLE_MAX) {
  183.                 VERIFIER_ESPACE_BUFFER(3, return false)
  184.                 buffer[indexBuffer++] = MARQUEUR_RLE_DELTA;
  185.                 buffer[indexBuffer++] = etatRLE.compteurRLE;
  186.                 buffer[indexBuffer++] = etatRLE.dernierDeltaAccel;
  187.                 etatRLE.compteurRLE = 0;
  188.                 etatRLE.modeRLE = false;
  189.             }
  190.         }
  191.     } else {
  192.         if (etatRLE.modeRLE) {
  193.             if (etatRLE.compteurRLE >= COMPTEUR_RLE_MIN) {
  194.                 VERIFIER_ESPACE_BUFFER(3, return false)
  195.                 buffer[indexBuffer++] = MARQUEUR_RLE_DELTA;
  196.                 buffer[indexBuffer++] = etatRLE.compteurRLE;
  197.                 buffer[indexBuffer++] = etatRLE.dernierDeltaAccel;
  198.             } else {
  199.                 for (uint8_t i = 0; i < etatRLE.compteurRLE; i++) {
  200.                     VERIFIER_ESPACE_BUFFER(2, return false)
  201.                     buffer[indexBuffer++] = MARQUEUR_DELTA_TEMPS_STD;
  202.                     buffer[indexBuffer++] = etatRLE.dernierDeltaAccel;
  203.                 }
  204.             }
  205.             etatRLE.modeRLE = false;
  206.             etatRLE.compteurRLE = 0;
  207.         }
  208.        
  209.         if (deltaTemps == INTERVALLE_TEMPS_STD) {
  210.             VERIFIER_ESPACE_BUFFER(2, return false)
  211.             buffer[indexBuffer++] = MARQUEUR_DELTA_TEMPS_STD;
  212.             buffer[indexBuffer++] = deltaAccel;
  213.             etatRLE.dernierDeltaAccel = deltaAccel;
  214.         } else {
  215.             VERIFIER_ESPACE_BUFFER(3, return false)
  216.             buffer[indexBuffer++] = MARQUEUR_DELTA_TEMPS_VAR;
  217.             buffer[indexBuffer++] = deltaAccel;
  218.             buffer[indexBuffer++] = deltaTemps;
  219.             etatRLE.dernierDeltaAccel = deltaAccel;
  220.         }
  221.     }
  222.    
  223.     dernierAccel = valeur;
  224.     dernierTemps = temps;
  225.     nbEchantillons++;
  226.     return true;
  227. }
  228.  
  229. /**
  230.  * Compression multi-échantillons avec alignement temporel
  231.  *
  232.  * Format: [MARQUEUR][TEMPS_MSB][TEMPS_LSB][NB_ECHANT][INTERVALLE][VAL_REF_MSB][VAL_REF_LSB][DELTA1]...[DELTAn]
  233.  */
  234. bool stockerBlocTemporel(int16_t* valeurs, uint16_t temps, uint8_t nbEchantillons, uint8_t intervalle) {
  235.     // Validation des paramètres
  236.     if (nbEchantillons < 2 || nbEchantillons > TAILLE_BLOC_MAX) {
  237.         return false;
  238.     }
  239.    
  240.     // Calcul de l'espace requis: marqueur + temps(2) + nbEchant + intervalle + valRef(2) + deltas
  241.     uint8_t espaceRequis = 1 + 2 + 1 + 1 + 2 + (nbEchantillons - 1);
  242.     VERIFIER_ESPACE_BUFFER(espaceRequis, return false)
  243.    
  244.     // Écriture de l'en-tête du bloc
  245.     buffer[indexBuffer++] = MARQUEUR_BLOC_TEMPOREL;
  246.     buffer[indexBuffer++] = (temps >> 8) & 0xFF;
  247.     buffer[indexBuffer++] = temps & 0xFF;
  248.     buffer[indexBuffer++] = nbEchantillons;
  249.     buffer[indexBuffer++] = intervalle;
  250.    
  251.     // Stockage de la valeur de référence
  252.     buffer[indexBuffer++] = (valeurs[0] >> 8) & 0xFF;
  253.     buffer[indexBuffer++] = valeurs[0] & 0xFF;
  254.    
  255.     // Calcul et stockage des deltas
  256.     int16_t valeurPrecedente = valeurs[0];
  257.     for (uint8_t i = 1; i < nbEchantillons; i++) {
  258.         int16_t delta = valeurs[i] - valeurPrecedente;
  259.        
  260.         // Vérification des limites encodables
  261.         if (delta < DELTA_MIN || delta > DELTA_MAX) {
  262.             return false;  // Delta hors limites, échec de compression
  263.         }
  264.        
  265.         buffer[indexBuffer++] = (int8_t)delta;
  266.         valeurPrecedente = valeurs[i];
  267.     }
  268.    
  269.     // Mise à jour des références globales
  270.     dernierAccel = valeurs[nbEchantillons - 1];
  271.     dernierTemps = temps + (nbEchantillons - 1) * intervalle;
  272.    
  273.     // Incrémenter le compteur d'échantillons global
  274.     ::nbEchantillons += nbEchantillons;
  275.    
  276.     return true;
  277. }
  278.  
  279. /**
  280.  * Formatage et écriture d'une mesure dans le fichier
  281.  */
  282. inline void ecrireMesure(int16_t accel, uint16_t temps, char* buffer, File& fichier) {
  283.     float accel_g = accel / 1000.0;
  284.     dtostrf(accel_g, 6, 2, buffer);
  285.     fichier.print(buffer);
  286.     fichier.print(F(", "));
  287.     fichier.println(temps);
  288. }
  289.  
  290. /**
  291.  * Décodage d'un bloc temporel compressé
  292.  */
  293. void decoderBlocTemporel(uint16_t& i, int16_t& accel, uint16_t& temps, char* bufferFormatage, File& fichier) {
  294.     uint16_t tempsBase = (buffer[i] << 8) | buffer[i+1];
  295.     i += 2;
  296.     uint8_t nbEchant = buffer[i++];
  297.     uint8_t intervalle = buffer[i++];
  298.    
  299.     int16_t valeurReference = (buffer[i] << 8) | buffer[i+1];
  300.     i += 2;
  301.    
  302.     accel = valeurReference;
  303.     temps = tempsBase;
  304.     ecrireMesure(accel, temps, bufferFormatage, fichier);
  305.    
  306.     int16_t valeurPrecedente = valeurReference;
  307.     for (uint8_t j = 1; j < nbEchant; j++) {
  308.         int8_t delta = buffer[i++];
  309.         accel = valeurPrecedente + delta;
  310.         temps = tempsBase + (j * intervalle);
  311.         ecrireMesure(accel, temps, bufferFormatage, fichier);
  312.         valeurPrecedente = accel;
  313.     }
  314. }
  315.  
  316. /**
  317.  * Décompression et sauvegarde des données sur carte SD
  318.  */
  319. void sauvegarderDonneesMesureRLE() {
  320.     fichierSD = SD.open(F("MESURE.txt"), FILE_WRITE);
  321.     if (!fichierSD) {
  322.         Serial.println(F("Erreur création fichier MESURE.txt"));
  323.         return;
  324.     }
  325.    
  326.     fichierSD.println(F("Accel Z (g), Temps (ms)"));
  327.    
  328.     uint16_t i = 0;
  329.     int16_t accel = 0;
  330.     uint16_t temps = 0;
  331.     char bufferFormatage[20];
  332.    
  333.     while (i < indexBuffer) {
  334.         uint8_t marqueur = buffer[i++];
  335.        
  336.         if (marqueur == MARQUEUR_VALEUR_COMPLETE) {
  337.             accel = (buffer[i] << 8) | buffer[i+1];
  338.             i += 2;
  339.             temps = (buffer[i] << 8) | buffer[i+1];
  340.             i += 2;
  341.             ecrireMesure(accel, temps, bufferFormatage, fichierSD);
  342.         }
  343.         else if (marqueur == MARQUEUR_DELTA_TEMPS_STD) {
  344.             int8_t deltaAccel = buffer[i++];
  345.             accel += deltaAccel;
  346.             temps += INTERVALLE_TEMPS_STD;
  347.             ecrireMesure(accel, temps, bufferFormatage, fichierSD);
  348.         }
  349.         else if (marqueur == MARQUEUR_DELTA_TEMPS_VAR) {
  350.             int8_t deltaAccel = buffer[i++];
  351.             uint8_t deltaTemps = buffer[i++];
  352.             accel += deltaAccel;
  353.             temps += deltaTemps;
  354.             ecrireMesure(accel, temps, bufferFormatage, fichierSD);
  355.         }
  356.         else if (marqueur == MARQUEUR_RLE_DELTA) {
  357.             uint8_t compteur = buffer[i++];
  358.             int8_t deltaAccel = buffer[i++];
  359.            
  360.             for (uint8_t j = 0; j < compteur; j++) {
  361.                 accel += deltaAccel;
  362.                 temps += INTERVALLE_TEMPS_STD;
  363.                 ecrireMesure(accel, temps, bufferFormatage, fichierSD);
  364.             }
  365.         }
  366.         else if (marqueur == MARQUEUR_BLOC_TEMPOREL) {
  367.             decoderBlocTemporel(i, accel, temps, bufferFormatage, fichierSD);
  368.         }
  369.     }
  370.    
  371.     fichierSD.close();
  372.     Serial.println(F("Fichier MESURE.txt généré"));
  373.    
  374.     // Génération du fichier de statistiques
  375.     fichierSD = SD.open(F("INFO.txt"), FILE_WRITE);
  376.     if (fichierSD) {
  377.         fichierSD.println(F("STATISTIQUES ACQUISITION:"));
  378.         fichierSD.println(F("------------------------"));
  379.         fichierSD.print(F("Échantillons: "));
  380.         fichierSD.println(nbEchantillons);
  381.        
  382.         fichierSD.print(F("Compression: "));
  383.         uint8_t ratioCompression = (uint8_t)((float)indexBuffer / (nbEchantillons * 4.0) * 100.0);
  384.         fichierSD.print(ratioCompression);
  385.         fichierSD.println(F("%"));
  386.        
  387.         fichierSD.print(F("Durée: "));
  388.         fichierSD.print(compteurSecondes);
  389.         fichierSD.println(F(" sec"));
  390.        
  391.         fichierSD.print(F("Fréquence moyenne: "));
  392.         uint16_t freqMoy = nbEchantillons / compteurSecondes;
  393.         fichierSD.print(freqMoy);
  394.         fichierSD.println(F(" Hz"));
  395.        
  396.         fichierSD.close();
  397.     }
  398. }
  399.  
  400. /**
  401.  * Vérification de l'uniformité des intervalles temporels
  402.  */
  403. bool verifierIntervallesUniformes(uint16_t* temps, uint8_t taille, uint8_t& intervalle) {
  404.     if (taille < 2) return false;
  405.    
  406.     intervalle = temps[1] - temps[0];
  407.    
  408.     // Vérifier que tous les intervalles sont identiques
  409.     for (uint8_t i = 2; i < taille; i++) {
  410.         if (temps[i] - temps[i-1] != intervalle) {
  411.             return false;
  412.         }
  413.     }
  414.    
  415.     return true;
  416. }
  417.  
  418. /**
  419.  * Initialisation du système
  420.  */
  421. void setup() {
  422.     Serial.begin(BAUD_RATE);
  423.     Serial.println(F("Initialisation système"));
  424.    
  425.     // Configuration ADC optimisée (prescaler 128)
  426.     ADCSRA = (ADCSRA & 0xF8) | 0x07;
  427.    
  428.     // Initialisation carte SD
  429.     if (!SD.begin(PIN_CS_SD)) {
  430.         Serial.println(F("Échec initialisation carte SD"));
  431.         while (1);
  432.     }
  433.    
  434.     // Configuration du Watchdog
  435.     configurerWatchdog();
  436.    
  437.     // Initialisation des variables
  438.     etatRLE.dernierDeltaAccel = 0;
  439.     etatRLE.compteurRLE = 0;
  440.     etatRLE.modeRLE = false;
  441.    
  442.     indexBuffer = 0;
  443.     nbEchantillons = 0;
  444.     nbEchantillonsBuffer = 0;
  445.     tempsDebutAcquisition = millis();
  446.    
  447.     Serial.println(F("Début acquisition"));
  448.     Serial.print(F("Capacité buffer: "));
  449.     Serial.print(BUFFER_SIZE);
  450.     Serial.println(F(" octets"));
  451. }
  452.  
  453. /**
  454.  * Boucle principale
  455.  */
  456. void loop() {
  457.     if (!acquisitionTerminee) {
  458.         // Phase d'acquisition
  459.         uint16_t valeurADC = lectureADC(PIN_CAPTEUR - A0);
  460.         int16_t acceleration = calcule_optimise((int32_t)valeurADC);
  461.         uint16_t tempsRelatif = millis() - tempsDebutAcquisition;
  462.        
  463.         // Ajout au buffer d'accumulation pour compression multi-échantillons
  464.         bufferAcceleration[nbEchantillonsBuffer] = acceleration;
  465.         bufferTemps[nbEchantillonsBuffer] = tempsRelatif;
  466.         nbEchantillonsBuffer++;
  467.        
  468.         // Traitement du buffer quand il est plein ou après un délai maximum
  469.         if (nbEchantillonsBuffer == TAILLE_BLOC_MAX ||
  470.             (nbEchantillonsBuffer > 1 && (tempsRelatif - bufferTemps[0]) > INTERVALLE_TEMPS_MAX)) {
  471.            
  472.             // Vérifier si les intervalles sont uniformes
  473.             uint8_t intervalleMesure;
  474.             bool intervallesUniformes = verifierIntervallesUniformes(
  475.                 bufferTemps, nbEchantillonsBuffer, intervalleMesure);
  476.            
  477.             // Compression multi-échantillons si possible
  478.             if (nbEchantillonsBuffer > 1 && intervallesUniformes && intervalleMesure <= 255) {
  479.                 if (stockerBlocTemporel(bufferAcceleration, bufferTemps[0],
  480.                                         nbEchantillonsBuffer, intervalleMesure)) {
  481.                     // Compression réussie, réinitialisation du buffer
  482.                     nbEchantillonsBuffer = 0;
  483.                     return;  // Continue to next sample acquisition
  484.                 }
  485.             }
  486.            
  487.             // Fallback: stockage traditionnel
  488.             for (uint8_t i = 0; i < nbEchantillonsBuffer; i++) {
  489.                 if (i == 0 || !stockerDeltaValeurRLE(bufferAcceleration[i], bufferTemps[i])) {
  490.                     VERIFIER_ESPACE_BUFFER(5, return)
  491.                     stockerValeurComplete(bufferAcceleration[i], bufferTemps[i]);
  492.                 }
  493.             }
  494.            
  495.             nbEchantillonsBuffer = 0;
  496.         }
  497.     }
  498.     else {
  499.         // Phase de sauvegarde
  500.         static bool sauvegardeFaite = false;
  501.        
  502.         if (!sauvegardeFaite) {
  503.             // Traiter les échantillons restants dans le buffer
  504.             if (nbEchantillonsBuffer > 0) {
  505.                 for (uint8_t i = 0; i < nbEchantillonsBuffer; i++) {
  506.                     if (i == 0 || !stockerDeltaValeurRLE(bufferAcceleration[i], bufferTemps[i])) {
  507.                         VERIFIER_ESPACE_BUFFER(5, break)
  508.                         stockerValeurComplete(bufferAcceleration[i], bufferTemps[i]);
  509.                     }
  510.                 }
  511.                 nbEchantillonsBuffer = 0;
  512.             }
  513.            
  514.             // Finaliser toute séquence RLE en cours
  515.             finaliserSequenceRLE();
  516.            
  517.             Serial.println(F("Acquisition terminée"));
  518.             Serial.print(F("Échantillons acquis: "));
  519.             Serial.println(nbEchantillons);
  520.            
  521.             // Sauvegarde des données
  522.             sauvegarderDonneesMesureRLE();
  523.            
  524.             sauvegardeFaite = true;
  525.             Serial.println(F("Traitement terminé"));
  526.         }
  527.     }
  528. }
Tags: Arduino
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement