#ifndef CONFIG_H
#define CONFIG_H
// debug
#define _dumpCan true
/* aiguille */
#define DELAI_AIGUILLE 150
#define DIRECT 0
#define DEVIE 1
#define MOVEDIRECT 2
#define MOVEDEVIE 4
#define ACTIF HIGH
#define INACTIF LOW
/* deteleur */
#define DELAI_DETELEUR 200
#define DETACTIF HIGH
#define DETINACTIF LOW
/* interface CAN */
static const uint8_t MCP2515_CS = 10;
static const uint8_t MCP2515_INT = 2;
#define ID_MASTER 0x7a0
#define RELAI_MASK 0x7ef
#define RELAI_FILTER 0x7e1
#define CAN_ORDRE 0x10
#define CAN_LIT 0x20
#define CAN_LIT_TOUT 0x30
#endif
#ifndef PARAMETRES_H
#define PARAMETRES_H
#include "config.h"
#include "aiguille.h"
#include "deteleur.h"
/* 8 sorties donc 4 aiguilles possibles ou 8 dételeurs ou une combinaison des 2 */
// 2 aiguilles
aiguille aig0 = aiguille(3,4,DIRECT);
aiguille aig1 = aiguille(5,6,DIRECT);
aiguille tabAiguille[2] = {aig0,aig1};
byte nombreAiguille = 2 ;
// 3 deteleurs
deteleur det0 = deteleur(7);
deteleur det1 = deteleur(8);
deteleur det2 = deteleur(9);
deteleur tabDeteleur[3] = {det0,det1,det2};
byte nombreDeteleur = 3;
#endif
#ifndef AIGUILLE_H
#define AIGUILLE_H
#include "config.h"
class aiguille {
private:
byte ports[2];
byte posAiguille;
byte posAttendue;
boolean enCours;
unsigned long limite;
public:
aiguille(byte p0, byte p1, byte posDepart);
boolean etat();
void positionne(byte attendu);
byte pos_aiguille();
void loop();
};
aiguille::aiguille(byte p0, byte p1, byte posDepart) {
ports[DIRECT] = p0;
ports[DEVIE] = p1;
pinMode(ports[DIRECT], OUTPUT);
pinMode(ports[DEVIE], OUTPUT);
digitalWrite(ports[DIRECT],INACTIF);
digitalWrite(ports[DEVIE],INACTIF);
digitalWrite(ports[posDepart],ACTIF);
delay(DELAI_AIGUILLE);
digitalWrite(ports[posDepart],INACTIF);
posAiguille = posDepart;
posAttendue = posDepart;
enCours = false;
limite = 0;
}
boolean aiguille::etat() {
return enCours;
}
byte aiguille::pos_aiguille() {
if (enCours) {
if (posAttendue == DIRECT) {
return MOVEDIRECT;
}
else {
return MOVEDEVIE;
}
}
else {
return posAiguille;
}
}
void aiguille::positionne(byte attendu) {
posAttendue = attendu;
}
void aiguille::loop() {
if (enCours) {
if (millis() > limite) {
digitalWrite(ports[posAttendue],INACTIF);
posAiguille = posAttendue;
enCours = false;
moveEnCours = false;
}
}
else {
if (posAttendue != posAiguille) {
digitalWrite(ports[posAttendue],ACTIF);
enCours = true;
moveEnCours = true;
limite = millis() + DELAI_AIGUILLE;
}
}
}
#endif
#ifndef DETELEUR_H
#define DETELEUR_H
#include "config.h"
class deteleur {
private:
byte port;
boolean enCours;
unsigned long limite;
public:
deteleur(byte p0);
byte etat();
void detelle();
void loop();
};
deteleur::deteleur(byte p0) {
port = p0;
pinMode(port, OUTPUT);
digitalWrite(port,DETINACTIF);
enCours = false;
limite = 0;
}
byte deteleur::etat() {
return enCours?1:0;
}
void deteleur::detelle() {
enCours = true;
moveEnCours = true;
digitalWrite(port,DETACTIF);
limite = millis() + DELAI_DETELEUR;
}
void deteleur::loop() {
if (enCours) {
if (millis() > limite) {
digitalWrite(port,DETINACTIF);
enCours = false;
moveEnCours = false;
}
}
}
#endif
#ifndef GESTATTENTE_H
#define GESTATTENTE_H
#include "config.h"
#include "aiguille.h"
#include "deteleur.h"
#include "parametres.h"
#define MAXLISTE 20
struct ordre {
byte index;
byte action;
};
class gestAttente {
private:
ordre liste[MAXLISTE];
int indexListe;
public:
gestAttente();
boolean ajouteOrdre(byte index, byte action);
void loop();
};
gestAttente::gestAttente() {
indexListe = -1;
}
boolean gestAttente::ajouteOrdre(byte index, byte action) {
ordre o;
if (indexListe < MAXLISTE -1) {
indexListe++;
o.index = index;
o.action = action;
liste[indexListe] = o;
return true;
}
return false;
}
void gestAttente::loop() {
ordre o;
if (! moveEnCours) {
if (indexListe > -1) {
o = liste[indexListe];
if (o.index < 10) {
if (o.index < nombreAiguille) {
tabAiguille[o.index].positionne(o.action);
}
}
else {
o.index = o.index - 10;
if (o.index < nombreDeteleur) {
tabDeteleur[o.index].detelle();
}
}
indexListe--;
}
}
}
#endif
#include <SPI.h>
// Déclaration de la bubliothèque CAN pour ATmega328
#include <mcp_can.h>
boolean moveEnCours;
#include "config.h"
#include "aiguille.h"
#include "deteleur.h"
#include "parametres.h"
#include "gestAttente.h"
gestAttente gest = gestAttente();
// variables globales pour l'interface CAN
MCP_CAN CAN(MCP2515_CS); // Definition du CS (chip select) pin 53 (SS du bus SPI)
volatile byte Flag_Recv = 0; // variable d'échange avec l'interruption IRQ
byte IdR; // Id pour la routine CAN_recup()
unsigned char lenR = 0; // Longueur " " "
unsigned char bufR[8]; // tampon de reception "
unsigned char bufS[8]; // tampon d'emission
// Variable globale Mémoire circulaire pour le stockage des messages reçus
unsigned char _Circule[256]; // récepteur circulaire des messages CAN sous IT
int _indexW, _indexR, _Ncan; // index d'écriture et lecture, nb d'octets a lire
byte _CANoverflow = 0; // flag overflow (buffer _Circule plein)
/*
* ISR CAN (Routine de Service d'Interruption)
* le flag IRQ monte quand au moins un message est reçu
* le flag IRQ ne retombe QUE si tous les messages sont lus
*/
void MCP2515_ISR() {
Flag_Recv = 1;
}
void setup() {
while(true) {
if (CAN_OK == CAN.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ)) {
// initialisation du can bus : baudrate = 500k
Serial.println(F("CAN BUS init ok!"));
break; // on sort du while.
}
else {
Serial.println(F("CAN BUS init echec !"));
Serial.println(F("Init CAN BUS a nouveau"));
}
delay(200);
}
attachInterrupt(digitalPinToInterrupt(MCP2515_INT), MCP2515_ISR, FALLING); // interrupt 0 (pin 2)
/*
* set mask & filter
*/
CAN.init_Mask(0, 0, RELAI_MASK);
CAN.init_Mask(1, 0, RELAI_MASK);
CAN.init_Filt(0, 0, RELAI_FILTER);
CAN.init_Filt(1, 0, RELAI_FILTER);
CAN.init_Filt(2, 0, RELAI_FILTER);
CAN.init_Filt(3, 0, RELAI_FILTER);
CAN.init_Filt(4, 0, RELAI_FILTER);
CAN.init_Filt(5, 0, RELAI_FILTER);
}
void loop() {
if (Flag_Recv) {
Flag_Recv = 0; // Flag MCP2515 prêt pour un nouvel IRQ
CAN_recup(); // récupération du ou des messages CAN reçus
CAN_depile();
}
gest.loop();
for (int i = 0; i < nombreAiguille; i++) {
tabAiguille[i].loop();
}
for (int i = 0; i < nombreDeteleur; i++) {
tabDeteleur[i].loop();
}
}
void CAN_recup() {
unsigned char len = 0; // nombre d'octets du message
unsigned char buf[8]; // message
unsigned long Id; // Id (on devrait plutôt utiliser un int car il y a 11 bits)
while (CAN_MSGAVAIL == CAN.checkReceive()) {
CAN.readMsgBuf(&Id, &len, buf); // read data, len: data length, buf: data buf
if ((_Ncan+len+2) < sizeof(_Circule)) { // il reste de la place dans _Circule
_Circule[_indexW] = Id; // enregistrement de Id
_indexW++;
_Ncan++;
if (_indexW == sizeof(_Circule)) {_indexW = 0;}
_Circule[_indexW] = len; // enregistrement de len
_indexW++;
_Ncan++;
if (_indexW == sizeof(_Circule)) {_indexW = 0;}
for (byte z = 0; z<len; z++) {
_Circule[_indexW] = buf[z]; // enregistrement du message
_indexW++;
_Ncan++;
if (_indexW == sizeof(_Circule)) {_indexW = 0;}
}
}
else {
_CANoverflow = 1; // dépassement de la capacite de Circule
// le message est perdu
}
}
}
void CAN_depile() {
byte RId; // variables pour le traitement des messages lus dans _Circule
byte Rlen;
byte Rbuf[8];
byte Ebuf[8];
byte element;
byte action;
byte i;
// traitement des messages stockés dans la mémoire circulaire _Circule
while (_Ncan > 2) { // chaque message dans _Circule occupe au moins 3 octets
_Ncan--;
RId = _Circule[_indexR]; // recup Id
_indexR++;
if (_indexR == sizeof(_Circule)) {_indexR = 0;}
_Ncan--;
Rlen = _Circule[_indexR]; // recup longueur
_indexR++;
if (_indexR == sizeof(_Circule)) {_indexR = 0;}
if (_dumpCan) { // _dumpCan est un boolean a déclarer en globale
Serial.print("CAN id "); // si on veut conditionner l'affichage des message
Serial.print(RId);
Serial.print(", data ");
}
for (int k = 0; k < Rlen; k++) {
_Ncan--;
Rbuf[k] = _Circule[_indexR]; // recup octets message
_indexR++;
if (_indexR == sizeof(_Circule)) {_indexR = 0;}
if (_dumpCan) {
Serial.print("0x");
Serial.print(Rbuf[k], HEX);
}
}
if (_dumpCan) Serial.println();
// le message est maintenant dans les globales RId, Rlen et Rbuf[..]
switch (Rbuf[0]) {
case CAN_ORDRE:
if (Rlen > 1) {
element = Rbuf[1];
if (element < nombreAiguille && Rlen > 2) {
action = Rbuf[2];
gest.ajouteOrdre(element,action);
}
else {
if (element >= 10 && element < nombreDeteleur) {
gest.ajouteOrdre(element,0);
}
}
}
break;
case CAN_LIT:
if (Rlen > 1) {
element = Rbuf[1];
if (element < nombreAiguille) {
Ebuf[0] = CAN_LIT;
Ebuf[1] = element;
Ebuf[2] = tabAiguille[element].pos_aiguille();
CAN.sendMsgBuf(RId, 0, 3, Ebuf);
}
}
break;
case CAN_LIT_TOUT:
Ebuf[0] = CAN_LIT_TOUT;
Ebuf[1] = 0;
Ebuf[2] = 0;
for(i = 0 ; i < nombreAiguille ; i++) {
Ebuf[1] |= tabAiguille[i].pos_aiguille()<<(i*2);
}
for (i = 0 ; i < nombreDeteleur ; i++) {
Ebuf[2] |= tabDeteleur[i].etat() << i;
}
CAN.sendMsgBuf(RId, 0, 3, Ebuf);
break;
default:
break;
}
}
}
type de contrôleur | Masque | Filtre mini | Filtre maxi | Nombre ids | Notes |
MASTER | 0x6fe | 0x6fe | 0x6fe | 1 | |
TCO | 0x6ff | 0x6ff | 0x6ff | 1 | |
spéciaux | 0x7f0 | 0x7f0 | 0x7ff | 16 | par exemple plaque tournante |
aiguilles | 0x7c0 | 0x7c0 | 0x7df | 32 | commandes par relais (aiguilles et dételeurs) |
pwm | 0x7a0 | 0x7a0 | 0x7bf | 32 | Alimentation des sections de voies |
capteurs | Ox780 | 0x780 | 0x79f | 32 | Capteurs "courant" + barrières IR |
accessoires | 0x760 | 0x760 | 0x77f | 32 | sorties diverses (leds, servos, ...) |
J'ai tout compris ? :)
Composant | Nombre maxi | utilisation |
MASTER | 1 | centre névralgique: possède la typologie du réseau |
TCO | 1 | Affichage état + prise de commandes manuelles (aiguilles/dételeurs/vitesse des trains,…) |
SECTION | 32 | Alimentation des sections + géstion des zones d'arrêt + détections présence |
SOLENOIDE | 16 | Commandes appareils de voie (aiguilles/dételeur) (4 aiguilles/8 dételeurs par composant) |
PLAQUE | 2 | Plaque tournante (rotation + alimentation + détection présence) |
COULISSE | 2 | Coulisse du réseau (position sortie + alimentation + détection présence) |
ACCESSOIRE | 16 | Pilotage des accessoires (éclairage, animation, …) |
ID | Nom | Source | Cible | utilisation |
0x01 | MASTER_ALERTE | tous | MASTER | réception des alertes provenant des autres composants |
0x03 | MASTER_ERREUR | tous | MASTER | réception des erreurs provenant des autres composants |
0x05 | MASTER_ORDRE | TCO | MASTER | réception des ordres provenant du TCO |
0x07 | MASTER_ACK | tous | MASTER | réception acquittement des ordres transmis |
0x09 | MASTER_ACCESSOIRE | TCO | MASTER | réception ordres accessoires provenant du TCO |
0x0A | MASTER_FINI | tous | MASTER | réception signal de fin d'action |
0x0F | MASTER_INFO | tous | MASTER | réception infos provenant des autres composants |
0x10-0x2F | SECTION_ARRET | MASTER | SECTION | Commandes zones d’arrêt des sections |
0x30-0x31 | PLAQUE_VITESSE | MASTER | PLAQUE | commande vitesse/sens sur plaque |
0x32-0x33 | PLAQUE_POSITION | MASTER | PLAQUE | commande position plaque |
0x34-0x35 | COULISSE_VITESSE | MASTER | COULISSE | Commande vitesse/sens dans coulisse |
0x36-0x37 | COULISSE_POSITON | MASTER | COULISSE | commande position coulisse |
0x40-0x4F | SOLENOIDE_ORDRE | MASTER | SOLENOIDE | Commandes aiguilles + dételeurs |
0x50-0x6F | SECTION_VITESSE | MASTER | SECTION | commandes vitesse/sens des sections |
0x70-0x7F | SOLENOIDE_INFO | MASTER | SOLENOIDE | demandes états aiguilles/dételeurs |
0x80-0x9F | SECTION_INFO | MASTER | SECTION | demandes état des sections |
0xA0-0xAf | ACCESSOIRE_ACTION | MASTER | ACCESSOIRE | modification accessoire |
0xD0-0xD1 | PLAQUE_INFO | MASTER | PLAQUE | demandes état des plaques tournantes |
0xD2-0xD3 | COULISSE_INFO | MASTER | COULISSE | demande état de la coulisse |
0xE0-0xEF | ACCESSOIRE_INFO | MASTER ACCESSOIRE | demande d'info accessoire | |
0xF0 | TCO_AFFICHAGE | MASTER | TCO | Affichage sur le TCO (position aiguille, occupation,vitesse…) |
Un échange type sera le suivant :
<action> TCO MASTER_ORDRE -> MASTER demande d'action (aiguille, dételeur,vitesse) <vérification possible> SOLENOIDE_ORDRE-> SOLENOIDE <verification possible et mise en pile commande> MASTER_ACK -> MASTER TCO_AFFICHAGE-> TCO (clignotement)
Une fois l'aiguille positionnée SOLENOIDE MASTER_FINI-> MASTER TCO_AFFICHAGE-> TCO (nouvelle position)
autre exemple
SECTION détection en fin de section MASTER_ALERTE-> MASTER (vérification suivant libre ou occupé) si occupé SECTION_ARRET-> SECTION vitesse nulle ou coupure section d'arrêt (arrêt) MASTER_ACK-> MASTER TCO_AFFICHAGE-> TCO affichage vitesse
utilisateur | TCO | message | MASTER | message | SOLENOIDE |
modifie position aiguille n° 7 | envoi de l'information au MASTER | ==> MASTER_ORDRE | convertit le numéro de l'aiguille en couple contrôleur/sortie et envoie l'info au contrôleur de solénoide | ==> SOLENOIDE_ORDRE | réception du message |
Affichage clignotant de l'aiguille | <== TCO_AFFICHAGE | réception du ACK et demande d'affichage (aiguille en attente) | <== MASTER_ACK | Mise en liste d'attente de la commande (pas d'activation simultanée de solénoïdes) | |
Affichage fixe de l'aiguille | <== TCO_AFFICHAGE | modification aiguille terminée. On affiche le résultat final sur le TCO | <== MASTER_FINI | L'aiguille a été actionnée |
utilisateur | TCO | message | MASTER | message | SECTION |
Vérification de l'état de la section suivante | <== MASTER_ALERTE | (section courante) Un train a atteint l'extrémité de la section et va sortir de la section | |||
premier cas: La section suivante est libre et accessible | |||||
Alimentation section suivante (pas la même)et attente sortie de la section courante | ==> SECTION_VITESSE | (section suivante) Alimentation des voies | |||
<== MASTER_ACK | (section suivante) acquittement | ||||
Affichage état des sections | <== TCO_AFFICHAGE | Affichage sur TCO: section courante libérée, section suivante occupée | <== MASTER_FINI | (section courante) Le train n'est plus dans la section | |
second cas: La section suivante est occupée ou inaccessible | |||||
Affichage état du train | <== TCO_AFFICHAGE | Arrêt !!! et indication TCO que le train est arrêté | ==> SECTION_STOP | (section courante) coupure alimentation des voies | |
<== MASTER_ACK | (section courante) acquittement | ||||
un ange passe ... la section suivante devient accessible ... | ==> SECTION_VITESSE | (section courante) réalimentation des voies | |||
<== MASTER_ACK | (section courante) acquittement | ||||
A partir de là, on repart depuis le premier cas |
void messageCmd (const CANMessage & inMessage) {
uint32_t mId = inMessage.id;
uint8_t cmdType = inMessage.data[0];
if ((mId & 0x1F) == EstouOuest) { // test 1A (EST) ou 1B (OUEST)
}
}
const ACAN2515Mask masque = extended2515Mask (0x1FFFFFFF) ; // For filter #0 and #1
const ACAN2515AcceptanceFilter filtres [] = {
{extended2515Filter (0x1FFFFF7A), messageCmd},
{extended2515Filter (0x1FFFFF7B), messageCmd},
} ;
// réception des messages CAN : voir messageCmd
if (controleurCAN.receive(messageCANReception)) {
Serial.print("->recu : ");
Serial.print(messageCANReception.len);
Serial.print(", data : ");
Serial.print(messageCANReception.data[0], HEX);
Serial.print(", ");
Serial.print(messageCANReception.data[1], HEX);
Serial.print(", ");
Serial.println(messageCANReception.data[2], HEX);
messageCmd (messageCANReception);
}
Est ce que le driver CAN ne va pas l'appeler lui même ?const ACAN2515Mask masque = extended2515Mask (0x1FFFFFFF) ; // For filter #0 and #1
const ACAN2515AcceptanceFilter filtres [] = {
{extended2515Filter (0x1FFFFF7A), messageAlert},
{extended2515Filter (0x1FFFFF7B), messageTCO},
} ;
ou traiter tous les filtrages dans la même fonction (comme dans votre programme) ?controleurCAN.dispatchReceivedMessage()
qui passe la main à la fonction messageCmd
dans les filtres, mais j'explique seulement ce qu'on peut faire. const ACAN2515Mask masque = extended2515Mask (0x1FFFFFFF) ; // For filter #0 and #1
const ACAN2515AcceptanceFilter filtres [] = {
{extended2515Filter (0x1FFFFF7A), messageCmd},
{extended25l15Filter (0x1FFFFF7B), messageCmd},
} ;
les filtres refletent les ID 0x7A et 0x7Bconst ACAN2515Mask masque = 0x7FF);
const ACAN2515AcceptanceFilter filtres [] = {
{0x701, messageAlert}, // Réception alertes depuis les contrôleurs
{0x703, messageErr}, // notification d'erreurs depuis les contrôleurs
{0x705, messageOrdre}, // réception des ordres depuis le TCO
{0x707, messageAck}, // notifications d’acquittement depuis les controleurs
{0x709, messageAccessoire}, // réception des ordres pour les accessoires
{0x70A, messageFini}, // notifications de fin d'opérations pour les ordres "longs" (par exemple déplacement d'aiguille)
{0x70F, messageInfo} // messages d'informations depuis les contrôleurs
} ;
const ACAN2515Mask masque = 0x7FF);
const ACAN2515AcceptanceFilter filtres [] = {
{0x7F0, messageAffichage} // Réception commandes d'affichage
} ;
const ACAN2515Mask masque = 0x7FF);
const ACAN2515AcceptanceFilter filtres [] = {
{0x742, messageOrdre}, // Réception commandes d'aiguille (SOLENOIDE_ORDRE)
{0x772, messageInfo}, // Réception demande d'état (SOLENOIDE_INFO)
} ;
et pour le controleur d'aiguille n° 4 const ACAN2515Mask masque = 0x7FF);
const ACAN2515AcceptanceFilter filtres [] = {
{0x744, messageOrdre}, // Réception commandes d'aiguille (SOLENOIDE_ORDRE)
{0x774, messageInfo}, // Réception demande d'état (SOLENOIDE_INFO)
} ;
CAN.sendMsgBuf(0x05,8,buff)
ou CAN.sendMsgBuf(0x705,8,buff)
Bonjour DominiqueOUI
J'ai vraiment des lacunes concernant le CAN entre le filtre et l'id
Pour moi, le filtre est utilisé pour inhiber la réception de messages inutiles pour le controleur.OUI
Par exemple Si j'ai un masque 0x7FF et un filtre à 0x7A0 , je ne recevrai que les messages dont l'id sera 0xA0, toute autre valeur d'id sera ignoréeOUI mais ausi 7A0
Dans votre exempleNON : il n'acceptent que les Ids de messages longs 1FFFFF7A et 1FFFFF7B mais n'émettent surtout pas de messages avec ces IdsCode: [Sélectionner]const ACAN2515Mask masque = extended2515Mask (0x1FFFFFFF) ; // For filter #0 and #1
les filtres refletent les ID 0x7A et 0x7B
const ACAN2515AcceptanceFilter filtres [] = {
{extended2515Filter (0x1FFFFF7A), messageCmd},
{extended25l15Filter (0x1FFFFF7B), messageCmd},
} ;
Si je reprend mon tableauNON: le MASTER, comme le TCO ne reçoit jamais de messages avec les Ids des messages qu'il envoie : 2 entités (noeuds) ne peuvent émettre avec le même Id.
Le master a pour IDs 01/03/05/07/09/0A/0F (les MASTER_XXXX)
Le tableau des filtres implanté dans le master sera le suivantCode: [Sélectionner]const ACAN2515Mask masque = 0x7FF);
const ACAN2515AcceptanceFilter filtres [] = {
{0x701, messageAlert}, // Réception alertes depuis les contrôleurs
{0x703, messageErr}, // notification d'erreurs depuis les contrôleurs
{0x705, messageOrdre}, // réception des ordres depuis le TCO
{0x707, messageAck}, // notifications d’acquittement depuis les controleurs
{0x709, messageAccessoire}, // réception des ordres pour les accessoires
{0x70A, messageFini}, // notifications de fin d'opérations pour les ordres "longs" (par exemple déplacement d'aiguille)
{0x70F, messageInfo} // messages d'informations depuis les contrôleurs
} ;
Le TCO a pour Ids 0xF0 (TCO_AFFICHAGE)
Le tableau des filtres implanté dans le TCO sera le suivantCode: [Sélectionner]const ACAN2515Mask masque = 0x7FF);
const ACAN2515AcceptanceFilter filtres [] = {
{0x7F0, messageAffichage} // Réception commandes d'affichage
} ;
Le meme principe est appliqué pour les autres types de controleurs :C'est bien ça qui est à revoir !
les filtres adaptés en fonction des ID pouvant être reçus par le controleur implantéOUI ça c'est vrai
Maintenant, la ou je ne suis pas sur c'est comment doit etre effectué le dialogue dans le programme:l'Id uniquement (le masque c'est en réception seulement)
Par exemple le TCO pour transmettre un ordre au master va utiliser la commande CAN.sendMsgBuf en envoyant le message au Master sur l'id MASTER_ORDRE
Mais doit il utilisé l'ID (0x05) ou la valeur de masque (0x705) pour envoyer le message ?
Premier exemple, action sur aiguillage depuis le TCOet mettez les valeurs des Ids dans les messages
utilisateur TCO message MASTER message SOLENOIDE
modifie position aiguille n° 7 envoi de l'information au MASTER ==> MASTER_ORDRE convertit le numéro de l'aiguille en couple contrôleur/sortie et envoie l'info au contrôleur de solénoide ==> SOLENOIDE_ORDRE réception du message
Affichage clignotant de l'aiguille <== TCO_AFFICHAGE réception du ACK et demande d'affichage (aiguille en attente) <== MASTER_ACK Mise en liste d'attente de la commande (pas d'activation simultanée de solénoïdes)
Affichage fixe de l'aiguille <== TCO_AFFICHAGE modification aiguille terminée. On affiche le résultat final sur le TCO <== MASTER_FINI L'aiguille a été actionnée