Création d'une pile de stockage

Bonsoir,
J'utilise un ARDUINO pour récupérer des données DCC (ADDRESS et DATA) pour commander des accessoires sur mon train (aiguillages et signaux).
La récupération des données se fait à l'aide de la bibliothèque DCC_COMMANDER qui utilise une interruption.
Cette (ces) fonction(s) me renvoi 3 valeurs dont 2 utiles (adresse et action).
Problème: quand je traite ces données ensuite, même avec des instructions courtes, je perds les données qui suivent.
Il faudrait que je puisse les mettre dans une pile, puis les travailler ensuite en les dépilant.
La pile s'incrémenterait à chaque nouvelle données.

Oui, mais voilà, je n'ai aucune idée comment faire et je cherche un peu d'aide.

Ca dépend de quelques paramètres : le taux d'acquisition des données, le volume à stocker et la mémoire dont tu disposes. Il existe des shields "data logger" pour Arduino (jamais testé de mon côté) qui pourraient t'aider.
1.43€ chez Ali :

https://www.aliexpress.com/item/New-Data-Module-Logging-Shield-Data-Recorder-Shield-for-for-Arduino-UNO-SD-Card/32647454509.html

Tu trouveras facilement des tutos pour l'utilisation qui te donneront des idées pour mettre en oeuvre ta pile.

http://tiptopboards.free.fr/arduino_forum/viewtopic.php?f=5&t=68

Salut,

Je n'ai pas trouvé cet lib dcc_commander, donc je ne sais pas du tout comment ça fonctionne.

Cherches-tu à avoir une pile ou un buffer?

La différence est assez simple :

le pile (STACK) est un stockage à taille variable (donc dangereux si ça déborde : le "stack overflow") et fonctionne en FILO (First In, Last Out). donc pour atteindre la première donnée, il faut avoir dépilé toutes les autres... Sur un tout petit µC comme l'arduino, le proc en utilise déjà une, et ça devient plus compliqué d'en créer une seconde sans composants externes...

le buffer (ou ring buf), a une taille fixe et tourne en FIFO (First In, First Out), ce qui permet de traiter les données dans l'ordre... Lorsque le buffer est plein, les données arrivantes écrasent les plus anciennes. La réception série utilise de tels buffers.

Il faut aussi bien réfléchir à la façon de traiter les données : ne jamais utiliser de delay par exemple, et aller au plus vite. Mais dans ton cas, je ne sais pas comment tu gères ça...

Bonsoir,

j'ai fait une erreur dans le nom de la librairie: c'est DCC_Decoder que j'utilise.

Ci-dessous, copie de mon programme, pour la partie acquisition des données DCC:


#include <DCC_Decoder.h>
#define kDCC_INTERRUPT 0 // pin 2 for UNO

int old_address = 0;
int old_action = 0;

//Handler DCC

void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data){
// Conversion de l'adresse NMRA en adresse decodeur d'accessoire
address -= 1;
address *= 4;
address += 1;
address += (data & 0x06) >> 1;
int action = (data & 0x01);

if ((address != old_address) || (action != old_action)){

//
//
// ici se situe le programme pour activer les sorties
// en fonction des données reçues
//
//
}
}
void setup() {

Serial.begin(115200);

DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true);
DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT );

}

void loop() {
DCC.loop();
}


Le décodage des données DCC, qui fonctionne sous une interruption (pin 2 pour moi), me renvoi 3 variables: activate (non utilisé), address et data qui après passage dans une formule "magique" me donne l'adresse de l'accessoire et une action (0 ou 1).
Quand je me limite à ce programme, je vois bien passer toutes les données DCC. Dès que je fais un peu de traitement, sans utiliser de fonction delay, je perds des données, surtout pour les signaux qui envoient 2 adresses et comme un ROUGE sur n, déclenche un JAUNE sur n-1 et un VERT sur n-2, cela peut faire 6 données à la suite.
Mon idées est donc d'enregistrer ces données dans un buffer, dès qu'elles sortent de la fonction DECODER, puis de les traiter ensuite. Il faudrait que ce buffer puisse enregistrer 2 x 10 variables (10 adresses et 10 actions) et qu'il se remplisse lors de l'interruption.
Je pense que c'est possible mais je n'ai aucune idée sur la manière de faire, même après avoir étudié la biblothèque CircularBuffer par exemple

Salut

Je traiterai le problème de cette façon...

Il faut partir sur un ou plusieurs tableaux de 10 éléments (ou une structure suivant tes connaissances) ... et un index qui parcoure les tableaux...
Il te faut un tableau d’adresse, un tableau d’action, un tableau (flag) pour savoir si l’element doit être traité

Quand ta fonction DÉCODER reçois un packet, tu stockes dans les tableaux l’adresse, l’action et tu mets le flag a 1 ... puis tu incrémentes l’index pour le prochain ordre que tu recevras (quand tu arrives à 9 tu repasses à 0 (buffer circulaire de 10 elements numérotés de 0 a 9))

Exemple tu reçois un ordre ... tu fais
//mémorisation d’un nouvel ordre
Tableau_adresse[index]= adresse
Tableau_action[index]= action
Tableau_flag[index]=1 // cette action devra être exécutée plus tard
index++; // prépare l’index pour le prochain ordre reçu
Si index=10 alors index=0; // ne garde que 10 ordres ...

Plus loin dans ton programme tu parcoures les tableaux à la recherche d’un ordre qui n’aurait pas été exécuté :
Pour i= 0 jusqu’a 9
Si Tableau_flag =1 alors
Exécuter l’ordre a l’adresse Tableau_adresse et l’action Tableau_action
Et tu libères le flag : Tableau_flag*=0; // ne pas répéter cet ordre*
* Sinon rien*
C’est assez basique et il n’y a pas de contrôles dans ce traitement (par ex si les Tableaux sont pleins d’ordres à exécuter et que tu en ajoutes un nouveau, ça écrasera un ordre qui ne sera donc jamais exécuté ... si il y a une chronologie à respecter, il faudra lire les tableaux différemment ...)
Bon courage !

Merci, c'est une approche à laquelle je vais réfléchir.

Par contre, il faudrait que le remplissage des tableaux se fasse dans le cadre de la routine d'interruption (et le traitement hors routine) et je ne vois pas à quelle place mettre les deux groupes d'instructions (remplissage et traitement) dans le programme actuel

Et bien dans la fonction BasicAccDecoderPacket() tu fais le traitement des tableaux et dans le loop tu gères la mémorisation... à chaque fois que DCCloop() a fini le traitement des Tableau sera réalisé...

C'est à dire que si je suis en train de faire le traitement des tableaux dans la "loop", dès qu'une donnée DCC arrive sur la pin 2, le traitement s'interrompra, DCC.loop ira récupérer la donnée qui sera stockée dans la fonction BasicAccDecoderPacket() et le traitement des tableaux reprendra?

Oui les interruptions fonctionnent comme tu le dis ... elles interrompent le programme qui reprend une fois le traitement de l’interruption terminé.

Il faudra bien vérifier ce qui se passe dans le tableau car il me semble que le DCC est un flux continu d’ordres pour maintenir éveillés les décodeurs ... peut être qu’il faudra filtrer les ordres pour les accessoires et ceux pour les locos ...

Non, j'ai déjà vérifié en lisant les "ordres bruts", il ne décode que les accessoires.
Il y a quand même un inconvénient, à savoir que les commandes sont répétées plusieurs fois ce qui fait qu'il va falloir que je sur-dimensionne mes fichiers de stockage. Je ferais une comparaison, pour ne garder qu'une valeur à chaque fois, au moment du traitement des données.
Je vais prévoir 2 fichiers de stockage: un pour les adresses, l'autre pour les actions en espérant qu'ils soient bien incrémentés et décrémentés en même temps.
Est-ce qu'il y a moyens de faire différemment avec, par exemple, un fichier à "2 lignes"?

Bonsoir,

Avec l'aide de beaucoup d'entre vous, je pense avoir résolu mon problème en stockant les données brutes DCC dans 2 fichiers, lors de la routine d'interruption.
Le traitement des données se fera ensuite dans la "loop", à l'emplacement des instructions Serial.print.
Seul inquiétude: je stock dans 2 fichiers différents (storadresse et storaction) qui, théoriquement sont remplis et vidés en même temps mais je préfèrerais des opérations plus synchrones.
Est-il possible de faire un tableau à "2 cases"?

Ci-dessous mon code:

#include <DCC_Decoder.h>
#define kDCC_INTERRUPT    0    // pin 2 for UNO
#include <CircularBuffer.h>

CircularBuffer <int, 100> storadresse;
CircularBuffer <byte, 100> storaction;

int add = 0;
int act = 0;
int old_add = 0;
int old_act = 0;
int action = 0;

//Handler DCC
void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data){
  //Serial.print(address);
  //Serial.print("    ");
  //Serial.println(data);
 storadresse.push (address);
 storaction.push (data); 
  } 
void setup() {      
  DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true); 
  DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT );
  Serial.begin (115200);  
  Serial.println("Debut test");
}
void loop() {
  DCC.loop();

  while(!storadresse.isEmpty()) {
   add = storadresse.shift();
   act = storaction.shift();

 if ((add != old_add) || (act != old_act)){
    old_add = add;
    old_act = act;
// Conversion de l'adresse NMRA en adresse decodeur d'accessoire 
   add -= 1;
   add *= 4;
   add += 1;
   add += (act & 0x06) >> 1;
   action =(act & 0x01);
   Serial.print(add);
   Serial.print("   ");
   Serial.println(action);
   
   } //fin nouvelle donnée
  } //fin while   
} //fin loop

Oui ça s’appelle une structure qui se définit comme suit

typedef struct {
int adresse;
byte action;
boolean atraiter;
} mastructure;

Ensuite tu déclares un tableau de 10 éléments de cette structure

mastructure tableau[10];

Et dans ton programme tu accèdes aux éléments de ta structure en faisant ...

tableau[index].adresse = ...
tableau[index].action= ...
tableau[index].atraiter=...

A voir si tu peux intégrer une structure dans ton circularbuffer ou s’il faut que tu fasses ta propre gestion (qui reste relativement simple)

Depuis le début je m’interroge sur les actions que tu dois faire ... et le fait que tu rates des ordres parfois ...
Tes actions c’est quoi ? Des digitalWrite ? Un gros traitement mathématique ?
Il me semble que les serial.print sont assez longs pour l’arduino ... est-ce que ce n’est pas ça dans ton interruption qui fait que des packets DCC sont ratés ? (Si les serial.print sont en dehors de l’interruption alors ton interruption n’est pas bloquée pendant le serial.print ) Ton programme ne marche pas si tu enlèves les serial.print dans la fonction de ton interruption ?

Le traitement des données est assez simple:

Si je vais directement vers une pin configurée en sortie

  • un test pour savoir si "action" vaut 0 ou 1
  • si 1, un digitalwrite HIGH vers la sortie. N° de la sortie = adresse - constante
  • si 0, idem avec un LOW

Si je vais vers un registre à décalage,

  • idem test 0 ou 1
  • calcul d'une valeur que je mets dans un tableau: je met BYTE(adresse - constante) ou 0 dans l'emplacement "adresse - constante" du tableau
    *j'additionne tous les termes du tableau
  • j'envoi la valeur par la fonction SHIFTOUT

Donc, rien de bien long.
Je pense que le vrai problème vient du fait que je faisais ce traitement dans le void qui réagit à l'interruption.
En mettant le traitement dans la "loop", il s'interrompt à chaque fois qu'une donnée arrive et reprends ensuite.
Je vais quand même vérifier tout ça car, pour l'instant, je n'ai fait que visualiser les résultats avec Serial.print.