Projet d'analyseur de réseau BUSCAN

Bonjour à tous,

Je suis actuellement sur un projet professionnel qui concerne les réseaux BUSCAN embarqués dans des véhicules du domaine agricole. L’idée à terme va être de piloter des fonctions d’un véhicule et de réceptionner des données pour de l’affichage distant bref, manipuler des données qui transitent sur le BUSCAN.

J’ai démarré ce projet par l’étude la plus complète possible sur le réseau BUSCAN, ça fait deux / trois mois que j’y suis. J’ai dans un premier temps étudié à l’oscilloscope le réseau qui transite sur les différents véhicules à ma disposition en concession afin de maitriser la détection de la vitesse du BUS, voltage des CAN H / CAN L, types de trames, etc.

J’ai eu au départ de mon projet beaucoup de mal à faire fonctionné des analyseurs de trame ( chinois ) sur les véhicules afin de déceler les trames qui m’intéressent, je me suis du coup penché sur l’idée de créer moi-même mon propre analyseur BUS CAN et je pense que c’est une bonne solution pour progresser dans ce domaine.

Voici la présentation de l’analyseur que j’aimerais créer :

  • Une carte microcontrôleur Arduino Méga 2560
  • Deux relais de sélection de BUS
  • Un module MCP2515
  • Bibliothèque ACAN2515 de PierreMOLINARO
  • 3 contacteurs : 1 pour la sélection du BaudsRate / 1 pour la sélection du BUS et 1 pour la sélection des masques.
  • Un encodeur rotatif analogique ( fonctionnement par ocpto-coupleur )
  • Un DAC 4725 pour l’alimentation analogique de mon encodeur
  • Des leds néopixel pour l’affichage de la sélection du BaudRate / état du système
  • Un afficheur LCD 4 lignes 16x4 I2C
  • Un boitier aluminium BOLPLA
  • Brochage faisceau électrique / boitier par broche D-SUB

Voici en image le boitier et son schéma de câblage :

Voici le fonctionnement souhaité :

Lors du démarrage du programme, le voyant « Power » clignote en bleu le temps de l’initialisation du programme et passe en vert lorsque le démarrage du MCP2515 est validé.

Le programme sélectionne le BaudsRate de 100 KB/s ainsi que la valeur de Serial.begin(valeur) ; par défaut puis peut être modifié via un des contacteurs jaunes et ainsi caler la vitesse du réseau BUSCAN avec le débit USB du port série.

Le programme propose de sélectionner ensuite le réseau BUS CAN à écouter ( sur les véhicules agricoles, plusieurs BUS s’interconnectent via un calculateur passerelle). Dans mon cas, il s’agit soit d’un réseau BUSCAN Véhicule, soit un réseau BUS CAN Equipement qui ont tous deux différentes vitesses de fonctionnement ( 500Kbits/s pour l’un et 250Kbits/s pour l’autre ). Le voyant concerné s’allume en fonction de la sélection.

Le programme récupère ensuite les données via le moniteur série lorsque des messages sont réceptionnés et seront enregistrés dans un fichier via le logiciel PUTTY.

Dans le cadre de la recherche des identifiants de trame qui concerne les données qui m’intéressent, je souhaite pouvoir faire évoluer les masques et les filtres en actionnant le contacteur orange et grâce à la molette de l’encodeur faire avancer et reculer les valeurs des filtres ou masques dans des tableaux ce qui va me permettre de faire un focus sur des portions de d’identifiants car en une seconde sur le véhicule, il doit y avoir plus de 100 trames / seconde rendant illisible la trame qui nous intéresse.

L’arrivée des messages est représentée par le voyant interruption avec un flash led rapide qui représente le flux des messages en réception.
Le cavalier de la résistance 120 ohm présente ou non selon le réseau sur lequel on souhaite se connecter doit être présente ou non suivant le cas.
Voila pour le projet, pour le moment, le programme est fonctionnel sur sa partie démarrage et arrivé des messages sans sélection d’identifiants ou de masque. Voici le programme provisoire qui n’est pas terminé, j’ai toute la partie sélection des filtres et masques qui n’est pas faite et j’ai des soucis sur certains points du programme qui ne marche pas comme je le souhaite donc je compte un peu sur vous pour m’aider à comprendre certaines choses et trouver les axes d’amélioration à apporter.

Voici le code réalisé pour le moment :

//  _______________  Déclaration de la bibliothèque MCP2515  ____________________________________

#include <ACAN2515_Buffer16.h>
#include <ACANBuffer.h>
#include <ACAN2515Settings.h>
#include <ACAN2515.h>
#include <ACAN2515_CANMessage.h>

// ________________  Déclaration des filtres  ____________________________

volatile int compteur = 0;  // Position (en nombre de pas) du codeur
bool etatArriverMessages = false;

int valeurFiltreUn;
int valeurFiltreDeux;
int valeurFiltreTrois;
int valeurFiltreQuatre;
int valeurFiltreCinq;
int valeurFiltreSix;

static const uint8_t MCP2515_CS = 53;
static const uint8_t MCP2515_INT = 19;

ACAN2515 controleurCAN(MCP2515_CS, SPI, MCP2515_INT);

// ________________  Déclaration du Baud Rate  ___________________________

unsigned long tableauValeurBaudRate[5]{ 125ul, 250ul, 500ul, 800ul, 1000ul };
unsigned long tableauValeurSerialBegin[5]{ 125000, 250000, 500000, 800000, 1000000 };

unsigned long valeurBaudRate = tableauValeurBaudRate[0];
unsigned long valeurSerialBegin = tableauValeurSerialBegin[0];

int commutateurBaudRate = 0;
int commutateurFitresEtMasques = 0;
int commutateurAffichageEncodeur = 0;

static const uint32_t FREQUENCE_DU_QUARTZ = 16ul * 1000ul * 1000ul;
static const uint32_t FREQUENCE_DU_BUS_CAN = valeurBaudRate * 1000ul;

// ____________________________________  Fonction de lecture des trames BUSCAN  _______________________________________________

void messagesDesFiltres(const CANMessage & inMessage, String nomDuFiltre){

 etatArriverMessages = true;

  if(etatArriverMessages == true) {
    clignotementLedInterruption();
  }

 Serial.print("Reception messages des filtres : "); 
 Serial.print(nomDuFiltre);
 Serial.print(" : ");
 Serial.println("Identifiant : " + String(inMessage.id) + "\t" + "Std ou ext : " + String(inMessage.ext) + "\t" + "Taille : " + String(inMessage.len) + "\t" + "Data[32] : " + String(inMessage.data32[0]));

}

void messageValFiltreUn(const CANMessage & inMessage){
 messagesDesFiltres(inMessage, "Filtre un");
}
void messageValFiltreDeux(const CANMessage & inMessage){
 messagesDesFiltres(inMessage, "Filtre deux");
}
void messageValFiltreTrois(const CANMessage & inMessage){
 messagesDesFiltres(inMessage, "Filtre trois");
}
void messageValFiltreQuatre(const CANMessage & inMessage){
 messagesDesFiltres(inMessage, "Filtre quatre");
}
void messageValFiltreCinq(const CANMessage & inMessage){
 messagesDesFiltres(inMessage, "Filtre cinq");
}
void messageValFiltreSix(const CANMessage & inMessage){
 messagesDesFiltres(inMessage, "Filtre six");
}

 const ACAN2515Mask typeDeMasqueStandardUn = standard2515Mask(0x7FF, 0, 0); /* Que des 1 sur 11 bits */
 const ACAN2515Mask typeDeMasqueStandardDeux = standard2515Mask(0x7FF, 0, 0); /* Que des 1 sur 11 bits */

 const ACAN2515AcceptanceFilter filtres[] = {
     { standard2515Filter(valeurFiltreUn, 0, 0), messageValFiltreUn },
     { standard2515Filter(valeurFiltreDeux, 0, 0), messageValFiltreDeux },
     { standard2515Filter(valeurFiltreTrois, 0, 0), messageValFiltreTrois },
     { standard2515Filter(valeurFiltreQuatre, 0, 0), messageValFiltreQuatre },
     { standard2515Filter(valeurFiltreCinq, 0, 0), messageValFiltreCinq },
     { standard2515Filter(valeurFiltreSix, 0, 0), messageValFiltreSix }
 };

void changementDesFiltresEtMasquesEtendu(){

 ACAN2515Settings reglagesEtendu(FREQUENCE_DU_QUARTZ, FREQUENCE_DU_BUS_CAN);

 reglagesEtendu.mReceiveBufferSize = 100 ;
 reglagesEtendu.mTransmitBuffer0Size = 0; 

 const ACAN2515Mask typeDeMasqueEtenduUn = extended2515Mask(0x1FFFFFFF); 
 const ACAN2515Mask typeDeMasqueEtenduDeux = extended2515Mask(0x1FFFFFFF); 

 const ACAN2515AcceptanceFilter filtresEtenduApresDemarrage[] = {
     { extended2515Filter(valeurFiltreUn), messageValFiltreUn },
     { extended2515Filter(valeurFiltreDeux), messageValFiltreDeux },
     { extended2515Filter(valeurFiltreTrois), messageValFiltreTrois },
     { extended2515Filter(valeurFiltreQuatre), messageValFiltreQuatre },
     { extended2515Filter(valeurFiltreCinq), messageValFiltreCinq },
     { extended2515Filter(valeurFiltreSix), messageValFiltreSix }
 };

 const uint16_t codeErreurEtendu = controleurCAN.begin(reglagesEtendu, [] { controleurCAN.isr(); }, typeDeMasqueEtenduUn, typeDeMasqueEtenduDeux, filtresEtenduApresDemarrage, 6 ); 

  if (codeErreurEtendu == 0) {

    Serial.print("CAN controleur etendu apres demarrage configurer / ");
    Serial.print("Etat codeErreurEtendu (begin) : ");
    Serial.println(codeErreurEtendu);
    delay(1000);
  } else {
    Serial.print("CAN controleur etendu apres demarrage non configurer / ");
    Serial.print("Etat codeErreurEtendu (begin) : ");
    Serial.println(codeErreurEtendu, HEX);
    delay(1000);
    while (1);
  }
}

void changementDesFiltresEtMasquesStandard(){

 ACAN2515Settings reglagesStandard(FREQUENCE_DU_QUARTZ, FREQUENCE_DU_BUS_CAN);

 reglagesStandard.mReceiveBufferSize = 100 ;
 reglagesStandard.mTransmitBuffer0Size = 0; 

 const ACAN2515Mask typeDeMasqueStandardUnApresDemarrage = standard2515Mask(0x7FF, 0, 0); 
 const ACAN2515Mask typeDeMasqueStandardDeuxApresDemarrage = standard2515Mask(0x7FF, 0, 0); 

 const ACAN2515AcceptanceFilter filtresStandardApresDemarrage[] = {
     { standard2515Filter(valeurFiltreUn, 0, 0), messageValFiltreUn },
     { standard2515Filter(valeurFiltreDeux, 0, 0), messageValFiltreDeux },
     { standard2515Filter(valeurFiltreTrois, 0, 0), messageValFiltreTrois },
     { standard2515Filter(valeurFiltreQuatre, 0, 0), messageValFiltreQuatre },
     { standard2515Filter(valeurFiltreCinq, 0, 0), messageValFiltreCinq },
     { standard2515Filter(valeurFiltreSix, 0, 0), messageValFiltreSix }
 };

 const uint16_t codeErreurStandard = controleurCAN.begin(reglagesStandard, [] { controleurCAN.isr(); }, typeDeMasqueStandardUnApresDemarrage, typeDeMasqueStandardDeuxApresDemarrage, filtresStandardApresDemarrage, 6 ); 

  if (codeErreurStandard == 0) {
    allumagePowerLed();     
    Serial.print("CAN controleur standard apres demarrage configurer / ");
    Serial.print("Etat codeErreurStandard (begin): ");
    Serial.println(codeErreurStandard);  
    delay(1000);
  } else {
    problemeAllumagePowerLed();
    Serial.print("CAN controleur standard apres demarrage non configurer / ");
    Serial.print("Etat  codeErreurStandard (begin) : 0x");
    Serial.println(codeErreurStandard, HEX);
    delay(1000);
    while (1);
  }
}

// __________________________  Bibliothèque Adafruit DAC MCP4725 et encodeur JD _________________________________


#define adresseI2CduMCP4725 0x60
#define memorisationTensionDeSortieUn false
#include <Adafruit_MCP4725.h>
Adafruit_MCP4725 dacEncodeur;

int etatPrecedentCanalUn;
int etatPrecedentCanalDeux;

int pinCanalUn = A0;
int pinCanalDeux = A1;

int etatAnalogiqueCanalUn = 0;
int etatAnalogiqueCanalDeux = 0;

int etatNumeriqueCanalUn = 0;
int etatNumeriqueCanalDeux = 0;

int etatActuelCanalUn = 0;
int etatActuelCanalDeux = 0;

// __________________________  Bibliothèque Néopixel _________________________________

const unsigned long tempoClignotementLed = 25;
unsigned long tempsEcouler = millis();

#include <Adafruit_NeoPixel.h>

#define ledPinDeux 9
#define ledCountDeux 6

#define ledPinUn 8
#define ledCountUn 4

Adafruit_NeoPixel stripUn(ledCountUn, ledPinUn, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel stripDeux(ledCountDeux, ledPinDeux, NEO_GRB + NEO_KHZ800);

void allumageProgramme() {
  for (int t = 0; t <= 30; t++) {
    delay(100);
    for (int i = 0; i <= 0; i++) {
      stripUn.setPixelColor(i, 0, 0, 255);
      stripUn.show();
      delay(45);
      stripUn.setPixelColor(i, 0, 0, 0);
      stripUn.show();
    }
  }
}

void allumagePowerLed() {
  for (int i = 0; i <= 0; i++) {
    stripUn.setPixelColor(i, 0, 140, 0);
    stripUn.show();
  }
}

void problemeAllumagePowerLed(){
  for (int i = 0; i <= 0; i++) {
    stripUn.setPixelColor(i, 255, 0, 0);
    stripUn.show();
  } 
}

// _______________________________________ Fonction de réception des messages standards _______________________________________________

int relaisSelectionDesBus = 12;
int etatDuRelaisDeSelectionDesBus = 0;

// ________________  Déclaration des boutons  ____________________________

#include "OneButton.h"

OneButton boutonSelectionBaudsRate;
OneButton boutonSelectionDesBus;
OneButton boutonSelectionFitresEtMasques;

//  _______________  Déclaration de la bibliothèque afficheur  ____________________________________

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x20, 20, 4);

//___________________________________________________________________________________  SETUP  ________________________________________________________________________________

void setup() {

 SPI.begin();

 pinMode(relaisSelectionDesBus, OUTPUT);
 digitalWrite(relaisSelectionDesBus, LOW);

 Serial.begin(115200);

 stripUn.begin();
 stripUn.show();
 stripUn.setBrightness(15);

 stripDeux.begin();
 stripDeux.show();
 stripDeux.setBrightness(15);

 lcd.init();
 lcd.backlight();

// __________________ Initialisation du DAC  _________________________

 dacEncodeur.begin(0x60);
 dacEncodeur.setVoltage(4095, true);

 etatPrecedentCanalUn = etatNumeriqueCanalUn;
 etatPrecedentCanalDeux = etatNumeriqueCanalDeux;

 delay(200);

// __________________  Initialisation SETUP des boutons  _____________________ //

 boutonSelectionBaudsRate.setup(6, INPUT, true);
 boutonSelectionBaudsRate.attachClick(selectionBaudsRate);

 boutonSelectionDesBus.setup(4, INPUT, true);
 boutonSelectionDesBus.attachClick(selectionDuBus);

 boutonSelectionFitresEtMasques.setup(7, INPUT, true);
 boutonSelectionFitresEtMasques.attachClick(appuiDuBoutonDesMasquesEtFiltres);

// ______________________   Initialisation SETUP du MCP2515  _________________________

 lcd.setCursor(4, 1);
 lcd.print("Analyseur de");
 lcd.setCursor(7, 2);
 lcd.print("BUS CAN");
 lcd.setCursor(16, 3);
 lcd.print("Ludo");
 delay(2000);

 allumageProgramme();

  for (int i = 1; i <= 1; i++) {  // Sélection Baud Rate par défaut
    stripDeux.setPixelColor(i, 0, 0, 255);
    stripDeux.show();
  }

 ACAN2515Settings reglages(FREQUENCE_DU_QUARTZ, FREQUENCE_DU_BUS_CAN);

 reglages.mReceiveBufferSize = 100 ;
 reglages.mTransmitBuffer0Size = 0; 

 lcd.clear();

 const uint16_t codeErreur = controleurCAN.begin(reglages, [] { controleurCAN.isr(); }, typeDeMasqueStandardUn, typeDeMasqueStandardDeux, filtres, 6 );
   
  if (codeErreur == 0) {
    allumagePowerLed();
    lcd.setCursor(3, 1);
    lcd.print("Demarrage ok !");      
    Serial.print("CAN controleur au demarrage configurer / ");
    Serial.print("Etat codeErreur (begin) : ");
    Serial.println(codeErreur);  
    delay(1500);
    lcd.clear();
  } else {
    problemeAllumagePowerLed();
    Serial.print("Etat codeErreur (begin) : 0x");
    Serial.println(codeErreur, HEX);
    delay(1500);
    lcd.clear();
    while (1);
  }

 lcd.setCursor(2, 1);
 lcd.print("Selectionner un");
 lcd.setCursor(3, 2);
 lcd.print("reseau BUS CAN");

}

//____________________________________________________________  LOOP  _____________________________________________________________

void loop() {

 boutonSelectionBaudsRate.tick();
 boutonSelectionDesBus.tick();
 boutonSelectionFitresEtMasques.tick();

 gestionEncodeur();
 calculDesFiltres();

  if ( commutateurAffichageEncodeur == 1 ){
    lcd.setCursor(16, 1);
    lcd.print(compteur);
    lcd.print(" ");  
    lcd.setCursor(3, 3);
    lcd.print("De : ");  
    lcd.setCursor(6, 3);
    lcd.print(valeurFiltreUn);
    lcd.print("   ");
    lcd.setCursor(11, 3);
    lcd.print("a");
    lcd.setCursor(14, 3);
    lcd.print(valeurFiltreSix);  
    lcd.print("   ");

  }
 
 controleurCAN.dispatchReceivedMessage() ;

}                                              

// _________________________________________________________________  FONCTION(S) _____________________________________________________________

// ______________________________________________________________  Fonction sélection du BUS CAN Véhicule ou équipement  ______________________________________________________

void selectionDuBus() {

  switch (etatDuRelaisDeSelectionDesBus) {

    case 0:
      
      Serial.print("Action sur bouton de selection des BUS CAN : ");
      Serial.println(boutonSelectionDesBus.debouncedValue());
      lcd.clear();
      digitalWrite(relaisSelectionDesBus, HIGH);

      Serial.println(" Sélection du BUS Vehicule ");

      for (int i = 1; i <= 1; i++) {
        stripUn.setPixelColor(i, 0, 0, 0);
        stripUn.show();
      }

      for (int i = 2; i <= 2; i++) {
        stripUn.setPixelColor(i, 0, 0, 255);
        stripUn.show();
      }

      lcd.setCursor(4, 0);
      lcd.print("Selection du ");
      lcd.setCursor(7, 1);
      lcd.print("BUS CAN");
      lcd.setCursor(6, 2);
      lcd.print("Vehicule");

      delay(1500);
      lcd.clear();

      lcd.setCursor(1, 1);
      lcd.print("Selectionner votre ");
      lcd.setCursor(5, 2);
      lcd.print("BaudsRate");
      
      Serial.println(" Sortie du Case 0 sélection des circuits CAN ");
      delay(750);

      etatDuRelaisDeSelectionDesBus = 1;
      commutateurAffichageEncodeur = 0;

      break;

    case 1:
      
      Serial.print("Action sur bouton de selection des BUS CAN : ");
      Serial.println(boutonSelectionDesBus.debouncedValue());
      lcd.clear();
      digitalWrite(relaisSelectionDesBus, LOW);

      Serial.println(" Sélection du BUS Equipement ");

      for (int i = 2; i <= 2; i++) {
        stripUn.setPixelColor(i, 0, 0, 0);
        stripUn.show();
      }

      for (int i = 1; i <= 1; i++) {
        stripUn.setPixelColor(i, 0, 0, 255);
        stripUn.show();
      }

      lcd.clear();
      lcd.setCursor(4, 0);
      lcd.print("Selection du ");
      lcd.setCursor(7, 1);
      lcd.print("BUS CAN");
      lcd.setCursor(5, 2);
      lcd.print("Equipement");

      delay(1500);
      lcd.clear();

      lcd.setCursor(1, 1);
      lcd.print("Selectionner votre ");
      lcd.setCursor(5, 2);
      lcd.print("BaudsRate");

      Serial.println(" Sortie du Case 1 sélection des circuits CAN ");
      delay(750);

      etatDuRelaisDeSelectionDesBus = 0;
      commutateurAffichageEncodeur = 0;

      break;
  }
}

// ______________________________________________________________  Fonction sélection du Baud Rate  ______________________________________________________

void selectionBaudsRate() {

  switch (commutateurBaudRate) {

    case 0:  // baud 250

      lcd.clear();
      valeurBaudRate = tableauValeurBaudRate[1];
      Serial.begin(tableauValeurSerialBegin[1]);

      for (int i = 1; i <= 1; i++) {
        stripDeux.setPixelColor(i, 0, 0, 0);
        stripDeux.show();
      }
      for (int i = 2; i <= 2; i++) {
        stripDeux.setPixelColor(i, 0, 0, 255);
        stripDeux.show();
      }


      lcd.setCursor(0, 1);
      lcd.print("Baud Rate = ");
      lcd.setCursor(12, 1);
      lcd.print(tableauValeurBaudRate[1]);
      lcd.setCursor(16, 1);
      lcd.print("Kb/s");

      lcd.setCursor(0, 2);
      lcd.print("Serial = ");
      lcd.setCursor(9, 2);
      lcd.print(tableauValeurSerialBegin[1]);
      lcd.setCursor(17, 2);
      lcd.print("Ba");


      commutateurBaudRate = 1;

      delay(1000);
      lcd.clear();

      lcd.setCursor(2, 0);
      lcd.print("selectionner les");
      lcd.setCursor(0, 2);
      lcd.print("masques identifiants");
      lcd.setCursor(0, 3);
      lcd.print("filtres identifiants");
      
      commutateurAffichageEncodeur = 0;

      break;

    case 1:  // baud 500

      valeurBaudRate = tableauValeurBaudRate[2];
      Serial.begin(tableauValeurSerialBegin[2]);

      stripDeux.Color(0, 0, 0);
      stripDeux.show();

      for (int i = 1; i <= 2; i++) {
        stripDeux.setPixelColor(i, 0, 0, 0);
        stripDeux.show();
      }
      for (int i = 3; i <= 3; i++) {
        stripDeux.setPixelColor(i, 0, 0, 255);
        stripDeux.show();
      }
      lcd.clear();
      lcd.setCursor(0, 1);
      lcd.print("Baud Rate = ");
      lcd.setCursor(12, 1);
      lcd.print(tableauValeurBaudRate[2]);
      lcd.setCursor(16, 1);
      lcd.print("Kb/s");

      lcd.setCursor(0, 2);
      lcd.print("Serial = ");
      lcd.setCursor(9, 2);
      lcd.print(tableauValeurSerialBegin[2]);
      lcd.setCursor(17, 2);
      lcd.print("Ba");

      commutateurBaudRate = 2;

      delay(1000);
      lcd.clear();

      lcd.setCursor(2, 0);
      lcd.print("selectionner les");
      lcd.setCursor(0, 2);
      lcd.print("masques identifiants");
      lcd.setCursor(0, 3);
      lcd.print("filtres identifiants");

      commutateurAffichageEncodeur = 0;

      break;

    case 2:  // baud 800

      valeurBaudRate = tableauValeurBaudRate[3];
      Serial.begin(tableauValeurSerialBegin[3]);

      for (int i = 1; i <= 3; i++) {
        stripDeux.setPixelColor(i, 0, 0, 0);
        stripDeux.show();
      }
      for (int i = 4; i <= 4; i++) {
        stripDeux.setPixelColor(i, 0, 0, 255);
        stripDeux.show();
      }
      lcd.clear();
      lcd.setCursor(0, 1);
      lcd.print("Baud Rate = ");
      lcd.setCursor(12, 1);
      lcd.print(tableauValeurBaudRate[3]);
      lcd.setCursor(16, 1);
      lcd.print("Kb/s");

      lcd.setCursor(0, 2);
      lcd.print("Serial = ");
      lcd.setCursor(9, 2);
      lcd.print(tableauValeurSerialBegin[3]);
      lcd.setCursor(17, 2);
      lcd.print("Ba");

      commutateurBaudRate = 3;

      delay(1000);
      lcd.clear();

      lcd.setCursor(2, 0);
      lcd.print("selectionner les");
      lcd.setCursor(0, 2);
      lcd.print("masques identifiants");
      lcd.setCursor(0, 3);
      lcd.print("filtres identifiants");

      commutateurAffichageEncodeur = 0;

      break;

    case 3:  // baud 1000

      valeurBaudRate = tableauValeurBaudRate[4];
      Serial.begin(tableauValeurSerialBegin[4]);

      for (int i = 1; i <= 4; i++) {
        stripDeux.setPixelColor(i, 0, 0, 0);
        stripDeux.show();
      }
      for (int i = 5; i <= 5; i++) {
        stripDeux.setPixelColor(i, 0, 0, 255);
        stripDeux.show();
      }
      lcd.clear();
      lcd.setCursor(0, 1);
      lcd.print("Baud Rate = ");
      lcd.setCursor(12, 1);
      lcd.print(tableauValeurBaudRate[4]);
      lcd.setCursor(16, 1);
      lcd.print("Kb/s");

      lcd.setCursor(0, 2);
      lcd.print("Serial = ");
      lcd.setCursor(9, 2);
      lcd.print(tableauValeurSerialBegin[4]);
      lcd.setCursor(17, 2);
      lcd.print("Ba");

      commutateurBaudRate = 4;

      delay(1000);
      lcd.clear();

      lcd.setCursor(2, 0);
      lcd.print("selectionner les");
      lcd.setCursor(0, 2);
      lcd.print("masques identifiants");
      lcd.setCursor(0, 3);
      lcd.print("filtres identifiants");

      commutateurAffichageEncodeur = 0;

      break;

    case 4:

      valeurBaudRate = tableauValeurBaudRate[0];
      Serial.begin(tableauValeurSerialBegin[0]);

      for (int i = 1; i <= 5; i++) {
        stripDeux.setPixelColor(i, 0, 0, 0);
        stripDeux.show();
      }
      for (int i = 1; i <= 1; i++) {
        stripDeux.setPixelColor(i, 0, 0, 255);
        stripDeux.show();
      }

      lcd.clear();
      lcd.setCursor(0, 1);
      lcd.print("Baud Rate = ");
      lcd.setCursor(12, 1);
      lcd.print(tableauValeurBaudRate[0]);
      lcd.setCursor(16, 1);
      lcd.print("Kb/s");

      lcd.setCursor(0, 2);
      lcd.print("Serial = ");  // 9
      lcd.setCursor(9, 2);
      lcd.print(tableauValeurSerialBegin[0]);
      lcd.setCursor(17, 2);
      lcd.print("Ba");

      commutateurBaudRate = 0;

      delay(1000);
      lcd.clear();

      lcd.setCursor(2, 0);
      lcd.print("selectionner les");
      lcd.setCursor(0, 2);
      lcd.print("masques identifiants");
      lcd.setCursor(0, 3);
      lcd.print("filtres identifiants");
      
      commutateurAffichageEncodeur = 0;

      break;

    default:
      valeurBaudRate = tableauValeurBaudRate[0];
      Serial.begin(tableauValeurSerialBegin[0]);
      for (int i = 0; i <= 0; i++) {
        stripDeux.setPixelColor(i, 0, 0, 255);
        stripDeux.show();
      }
      lcd.clear();
      lcd.setCursor(0, 1);
      lcd.print("Baud Rate = ");
      lcd.setCursor(12, 1);
      lcd.print(tableauValeurBaudRate[0]);
      lcd.setCursor(16, 1);
      lcd.print("Kb/s");

      lcd.setCursor(0, 2);
      lcd.print("Serial = ");
      lcd.setCursor(9, 2);
      lcd.print(tableauValeurSerialBegin[0]);
      lcd.setCursor(17, 2);
      lcd.print("Ba");

      delay(1000);
      lcd.clear();

      lcd.setCursor(2, 0);
      lcd.print("selectionner les");
      lcd.setCursor(0, 2);
      lcd.print("masques identifiants");
      lcd.setCursor(0, 3);
      lcd.print("filtres identifiants");
      
      commutateurAffichageEncodeur = 0;

      break;
  }
}

// ______________________________________________________________  Fonction sélection des filtres et masques  ______________________________________________________

void appuiDuBoutonDesMasquesEtFiltres() {

  switch (commutateurFitresEtMasques) {

    case 0:   
        
      Serial.print("Action sur bouton de selection des masques et filtres : ");
      Serial.println(boutonSelectionFitresEtMasques.debouncedValue());
      lcd.clear();

      for (int i = 0; i <= 0; i++) {
          stripDeux.setPixelColor(i, 0, 0, 0);
          stripDeux.show();
      }

      for (int i = 0; i <= 0; i++) {
          stripDeux.setPixelColor(i, 0, 255, 0);
          stripDeux.show();
      }
          
      changementDesFiltresEtMasquesEtendu();

      lcd.setCursor(2, 0);
      lcd.print("Masque etendu");
      lcd.setCursor(1, 1);
      lcd.print("ID de 6 en 6 : ");
      lcd.setCursor(2, 2);
      lcd.print("Filtres etendu");
        
      commutateurAffichageEncodeur = 1;
      commutateurFitresEtMasques = 1;
      compteur = 0;

      Serial.println("Sortie ok de case 0");
      delay(1000);

    break;

    case 1:
        
      Serial.print("Action sur bouton de selection des masques et filtres : ");
      Serial.println(boutonSelectionFitresEtMasques.debouncedValue());
      lcd.clear();

      for (int i = 0; i <= 0; i++) {
          stripDeux.setPixelColor(i, 0, 0, 0);
          stripDeux.show();
      }

      for (int i = 0; i <= 0; i++) {
          stripDeux.setPixelColor(i, 255, 255, 0);
          stripDeux.show();
      }

      changementDesFiltresEtMasquesStandard();

      lcd.setCursor(2, 0);
      lcd.print("Masque standard");
      lcd.setCursor(1, 1);
      lcd.print("ID de 6 en 6 : ");
      lcd.setCursor(2, 2);
      lcd.print("Filtres standard");

      commutateurFitresEtMasques = 0;
      compteur = 0;
        
      Serial.println(" Sortie ok de case 1 ");
        
      delay(1000);

    break;
  }  
}

// ______________________________________________________________  Fonction allumage Led lors d'un interruption du MCP2515  ______________________________________________________

void clignotementLedInterruption() {

 static boolean ledOn;  // Pour garder l'état de la LED

  if (millis() - tempsEcouler >= tempoClignotementLed) {

    ledOn = !ledOn;  // Inverser l'état de la LED

    if (ledOn) {
      stripUn.setPixelColor(3, 255, 255, 255);
      stripUn.show();
    } else {
      stripUn.setPixelColor(3, 0, 0, 0);
      stripUn.show();
    }
    tempsEcouler = millis();
    etatArriverMessages = false;
  }
}

// ______________________________________________________________  Fonction gestion encodeur  ______________________________________________________

void gestionEncodeur() {

 etatAnalogiqueCanalUn = analogRead(pinCanalUn);
 etatAnalogiqueCanalDeux = analogRead(pinCanalDeux);

  if (etatAnalogiqueCanalUn < 300) {
    etatNumeriqueCanalUn = LOW;
  } else {
    etatNumeriqueCanalUn = HIGH;
  }

  if (etatAnalogiqueCanalDeux > 500) {
    etatNumeriqueCanalDeux = HIGH;
  } else {
    etatNumeriqueCanalDeux = LOW;
  }

 etatActuelCanalUn = etatNumeriqueCanalUn;
 etatActuelCanalDeux = etatNumeriqueCanalDeux;

  if (etatActuelCanalUn != etatPrecedentCanalUn) {
    if (etatPrecedentCanalDeux == 1 && etatActuelCanalUn == 1) {
      compteur++;
    }
    if (etatPrecedentCanalUn == 1 && etatActuelCanalDeux == 0) {
      compteur++;
    }
    if (etatPrecedentCanalDeux == 0 && etatActuelCanalUn == 1) {
      compteur--;
    }
    if (etatPrecedentCanalDeux == 1 && etatActuelCanalUn == 0) {
      compteur--;
    }
  }

  if (etatActuelCanalDeux != etatPrecedentCanalDeux) {
    if (etatPrecedentCanalUn == 1 && etatActuelCanalDeux == 0) {
      compteur++;
    }
    if (etatPrecedentCanalUn == 0 && etatActuelCanalDeux == 1) {
      compteur++;
    }
    if (etatPrecedentCanalUn == 1 && etatActuelCanalDeux == 1) {
      compteur--;
    }
    if (etatPrecedentCanalUn == 0 && etatActuelCanalDeux == 0) {
      compteur--;
    }
  }

 etatPrecedentCanalUn = etatActuelCanalUn;
 etatPrecedentCanalDeux = etatActuelCanalDeux;

  if (compteur <= 0) {
    compteur = 0;
  }

 //Serial.println("Etat actuel canal un : " + String(etatActuelCanalUn) + "\t" + "Etat precedent canal un : " + String(etatPrecedentCanalUn) + "\t" + "Etat actuel canal deux : " + String(etatActuelCanalDeux) + "\t" + "Etat precedent canal deux : " + String(etatPrecedentCanalDeux) + "\t" + "Compteur : " + String(compteur));
 //Serial.println(" Etat numerique canal un : " + String(etatNumeriqueCanalUn) + "\t" + " Etat numerique canal deux : " + String(etatNumeriqueCanalDeux));

}

// ______________________________________________________________  Calcul des six filtres  ___________________________________________________

void calculDesFiltres(){
  
 valeurFiltreUn = 1 + 6 * (compteur);
 valeurFiltreDeux = valeurFiltreUn + 1;
 valeurFiltreTrois = valeurFiltreUn + 2;
 valeurFiltreQuatre = valeurFiltreUn + 3;
 valeurFiltreCinq = valeurFiltreUn + 4;
 valeurFiltreSix = valeurFiltreUn + 5;

 /*Serial.print("compteur : ");
 Serial.print(compteur);
 Serial.print("  ");
 Serial.print("Filtre un : ");
 Serial.print(valeurFiltreUn);
 Serial.print("  ");
 Serial.print("Filtre deux : ");
 Serial.print(valeurFiltreDeux);
 Serial.print("  ");
 Serial.print("Filtre trois : ");
 Serial.print(valeurFiltreTrois);
 Serial.print("  ");
 Serial.print("Filtre Quatre : ");
 Serial.print(valeurFiltreQuatre);
 Serial.print("  ");
 Serial.print("Filtre cinq : ");
 Serial.print(valeurFiltreCinq);
 Serial.print("  ");
 Serial.print("Filtre six : ");
 Serial.println(valeurFiltreSix);*/
}


  

Pour plus de clarté sur ce sujet, je poserais une question qui me bloque step by step afin d’avancer au mieux et pour commencer, je rencontre une difficulté sur le voyant d’interruption qui me ralenti l’arrivé des messages, j’ai fait deux petites vidéos démontrant la différence de vitesse ( j'ai décommenté pour le moment la fonction dans la loop ) :

Moniteur série avec voyant d'interruption actif

Moniteur série sans voyant d'interruption actif

Merci par avance à tous pour l'entraide.

Ludo

1 Like

Bonjour udina

Tout d'abord, superbe réalisation, l'usage de LED adressables pour la signalisation, top, ça simplifie nettement le câblage :+1:

Petite remarque concernant ton encodeur, tu te simplifierai nettement la tâche, en employant une bibliothèque comme Encoder.h,:
image

son auteur est une référence dans ce domaine.

Maintenant, pour ta LED, il te faut te tourner vers les millis() pour la faire clignoter.

Si tu veux un exemple ...

A+
Cordialement
jpbbricole

Beau travail.

Si je peux me permettre, ce cavalier ce n'est pas une bonne idée.

  1. c'est fragile, là où il est placé il risque d'être accroché et le broches seront cassées tôt ou tard
  2. c'est petits cavaliers se perdent avec une facilité dont tu n'as pas idée.

Pour un environnement de travail, je suggérerais un interrupteur. Pour éviter qu'il dépasse trop, éventuellement, un modèle à glissière, mais de bonne qualité.
Je suggérerais même un inter 2 circuits afin d'avoir une recopie de l'état de celui-ci sur la face avant car là où il est placé on risque de l'oublier dans la mauvaise position de temps en temps.

Merci pour cette observation, il est vrai que le cavalier translate facilement, je me note de faire évoluer ce point sur un contacteur :+1:

Je vais refaire la fonction clignotementLedInterruption(); avec millis(); et je te la représenterais :+1:

Merci pour vos retours positifs, au travail :sweat_smile:

Voici ce que je viens de modifier, niveau code, est ce fonctionnel ?

unsigned long interval = 0;
unsigned long tempsCourant = 0;
 
void clignotementLedInterruption() {

tempsCourant = millis();
   
  for (int i = 1; i <= 1; i++) {
    stripUn.setPixelColor(i, 255, 255, 255);
    stripUn.show();
      if((tempsCourant - interval) > 25) {     
        stripUn.setPixelColor(i, 0, 0, 0);
        stripUn.show();
      }
    interval = tempsCourant;
  }
}

Quel rythme de temps doit on mettre en place dans la condition afin d'être le plus précis possible ?

Bonsoir udina

Si tu veux que ton clignotement n'influence pas ou très peu le déroulement de l'affichage de tes données, c'est plus compliqué que ça, délay est à bannir, je t'ai fait une démo complète, le clignotement démarre avec un bouton sur la pin 3, l'autre côté du bouton à GND et la console à 115200.

Le programme:

/*
    Name:       DEMO_ClignotementLedWoDelay.ino
    Created:	16.02.2025
    Author:     jpbbricole
	Remarque:	La LED clignote un nombre clignNombre de fois
				Au rythme de clignTempo millisecondes
				Déclenchement avec bouton sur pin 3, 
				l'autre côté du bouton à GND.
*/

const int clignBtnPin = 3; // Bouton de démarrage
const int clignLedPin = 13;
const int clignNombre = 30; // Nombre de clignotement
int clignCompteur = 3; // Comptage des clignotements

const unsigned long clignTempo = 25; // Rythme de clignotement en millisesondes
unsigned long clignMillis = millis(); // Rythme de clignotement en millisesondes, chrono

boolean clignOn = false; // Etat du clignotement

const unsigned long displayTempo = 250; // Tempo d'affichage
unsigned long displayMillis = 250; // Tempo d'affichage, chrono
int displCompteur = 0; // Pour avoir quelque chose à afficher

void setup()
{
	Serial.begin(115200);
	pinMode(clignLedPin, OUTPUT);
	digitalWrite(clignLedPin, LOW);
	
	pinMode(clignBtnPin, INPUT_PULLUP);
}

void loop()
{
	if (clignOn) // Si clignotement en fonction
	{
		clignotementLedInterruption();
	}
	
	if (digitalRead(clignBtnPin) == LOW && !clignOn) // Si bouton pressé et clignotement off
	{
		clignCompteur = 0;
		clignMillis = millis(); // Démarrage du chrono
		clignOn = true;
	}
	
	if (millis() - displayMillis >= displayTempo) // Si c'est le moment d'afficher
	{
		Serial.println("Donnees recues: " + String(displCompteur));
		displCompteur ++;
		displayMillis = millis(); // Redémarrage du chrono d'affichage
	}
}
// ______________________________________________________________  Fonction allumage Led lors d'un interruption du MCP2515  ______________________________________________________

void clignotementLedInterruption() {
	static boolean ledOn; // Pour garder l'état de la LED
	
	if (millis() - clignMillis >= clignTempo) // Si période
	{
		ledOn = !ledOn; // Inverser l'état de la LED
		
		if (ledOn) // Allumer la LED
		{
			digitalWrite(clignLedPin,HIGH);
		}
		else // Eteindre la LED
		{
			digitalWrite(clignLedPin,LOW);
			clignCompteur ++;
		}
		clignMillis = millis(); // Redémarrer le chrono
	}

	if (clignCompteur >= clignNombre) // Fin du clignotement
	{
		digitalWrite(clignLedPin,LOW);
		clignOn = false; // Arrêt
	}
}

A+
jpbbricole

Bonjour,

Ton schéma ne parle ni du connecteur Sub D (que j'aurais mit en femelle pour éviter les contact malencontreux) ni de la résistance 120R ni du cavalier ni du port USB ni clairement de la maniére dont le tout est alimenté.

Ces éléments mit sur le schéma auraient éte instructifs pour mieux comprendre le fonctionnement.

Super boulot tout de même.

Bonjour,

C'est un schéma un peu simplifié, c'est une bonne objection, je ferais une update pour plus de clarté.

Merci

Bonjour à tous,

Pour la fonction de voyant led avec millis(); je suis partie sur ce modèle :

unsigned long interval = 0;

void clignotementLedInterruption() {

static boolean etatLedInterruption;
tempsCourant = millis();

if((tempsCourant - interval) >= 25){
 etatLedInterruption  = !etatLedInterruption;
  if(etatLedInterruption){    
    stripUn.setPixelColor(1, 255, 255, 255);
    stripUn.show();   
  } else {    
    stripUn.setPixelColor(1, 0, 0, 0);
    stripUn.show();          
  }     
interval = tempsCourant;
  }
}

Une zone d'ombre persiste dans ma tête, voici comment je décompose les choses :

  1. Un message arrive se qui fait rentrer le programme dans la fonction ci-dessus.
  2. On initialise un booléen sur l'état de la led mais quel état prend il ?
  3. j'indique que tempsCourant prend l'état actuel de millis.
  4. si le tempsCourant - interval est supérieur ou égal à 25 millisecondes
  5. j'inverse l'état de l'état de la led
  6. si état de la led ( je ne comprend quand cette comparaison est vrai ) ?
  7. j'allume la led 1 de mon ruban
  8. je ne comprend pas non plus la condition qui me fait rentrer dans le sinon
  9. je remets mon interval à la valeur de millis.

Merci par avance pour vos précisions.

Bonjour udina

As tu essayé ton programme?
A mon avis il ne va rien se passer.
Tu reçois un message et va dans clignotementLedInterruption()
tempsCourant = millis();
et tout de suite derrière:
if((tempsCourant - interval) >= 25)
Donc condition pas vraie et:

  } else {    
    stripUn.setPixelColor(1, 0, 0, 0);
    stripUn.show();

Extinction de la LED.

Dans le post#6, je t'ai mis un exemple (il fait clignoter 30 fois la LED selon ton programme mais peut être changé), mais c'est surtout la "mécanique" qu'il faut y voire. Ainsi, lors de l'arrivée d'un message (qui est remplacé par la pression d'un bouton) c'est uniquement la variable clignOn qui est mise à true et tout le reste découle automatiquement jusqu'à l'arrêt du processus.

Cordialement
jpbbricole

Oui, j'ai testé ton programme et j'y ai intégré en plus mon ruban néopixel, ça marche niquel !
J'ai joué avec le tempo de la led d'ailleurs, en dessous de 15 millisecondes, l’œil ne distingue plus trop le changement d'état.
Je vais reprendre tout ça à tête reposer pour que cet mécanique me semble logique.

Bonjour à tous,

Après amélioration de la partie gestion de clignotement de la led de détection des arrivées des messages BUSCAN ( partie du code Updaté, post#1), je poursuits sur le point suivant qui me bloque.
Lorsque je rentre dans la fonction selectionBaudsRate() je change la valeur BaudsRate du circuit BUSCAN et je change également la valeur de Serial.begin();

unsigned long tableauValeurSerialBegin[6]{ 100000, 125000, 250000, 500000, 800000, 1000000 };

Dans le moniteur série de l'IDE, on n'a pas les mêmes vitesses dans le menu déroulant, y a t il une manière de sélectionner nous même une vitesse " personnalisée " ?

J'ai du coup modifié la structuration des vitesses baud rate en supprimant la première vitesse ( 100Kb/s ) que je ne rencontrerais surement jamais. La led Néopixel qui était dédié à cette vitesse va du coup servir à distinguer le type de trame :

  • Vert pour les trames standards
  • Jaune pour les trames étendues

Ces deux types de trame seront sélectionné par le contacteur orange et entreront dans un switch case à quatres groupes :

Case 0 : qui contiendra un masque standard qui triera la plage d'identifiants qui peuvent passer.
( j'aimerais grâce à la molette d'encodeur passer le masque de 10 en 10 et avoir le retour visuel par le lcd )

Case 1 : qui contiendra un masque étendu qui triera la plage d'identifiants qui peuvent passer.
( j'aimerais grâce à la molette d'encodeur passer le masque de 10 en 10 et avoir le retour visuel par le lcd )

Ces deux premières fonctions me permettront de voir plus facilement passer l'identifiant de trame qui m'intéresse lorsque je sollicite la fonction du véhicule qui m'intéresse.

Case 2 : qui contiendra une fois avoir trouver les trames à étudier six filtres d'identifiants standards de trame. ( visuel des valeurs des filtres sur l'écran lcd ).

Case 3 : idem au Case 2 mais avec des filtres étendus.

Les identifiants de trame standard ont une taille de 11 bits et exprimés en HEX dans le programme de la bibliothèque ACAN donc par logique, si je souhaite aller de 10 en 10 :

  • 00000001010 ( Bin ) ==> 0xA ( Hex ) ==> 10 ( Dec )
  • 00000010100 ( Bin ) ==> 0x14 ( Hex ) ==> 20 ( Dec )
  • ect ...

Par contre, par cette méthode je ne compare pas les valeurs du masque de 10 en 10, je ne vois pas dans cette bibliothèque comment exprimer une fonction qui compare les masques de 10 valeurs en dix valeurs.

Autrement, faudrait il accepter un masque qui accepte tous les identifiants et plutôt travailler sur les six filtres possibles dans la bibliothèque ACAN ?

J'ai traduit un PDF en français de cette bibliothèque pour un support d'aide à ceux qui veulent bien m'aider :
Bibliothèque ACAN2515

Si quelqu'un peut m'aider à y voir plus clair dans une méthode à mettre en place ou des pistes à explorer, ça serait super !

Merci par avance

Bonsoir,

Je rencontre une difficulté d'affichage de ma variable compteur; sur mon afficheur lcd.
Cette variable est gérée par mon encodeur et peut varier de 0 à 9000.
Lorsque j'entre dans la loop et que j'ai une lecture sur mon afficheur, ma variable démarre bien à 0 et à chaque rotation positive, elle s'incrémente sans soucis par contre, lorsque je souhaite la descendre, je rencontre un souci entre les centaines / les dizaines ect ...
Avez vous des exemples ou des conseils de mise en oeuvre pour régler se souci de décrémentation ?

Oui mais comme on a pas ton matériel on ignore quel est le soucis

Il faut que tu nous montres ton code.

Bonsoir udina

C'est un classique de l'affichage sur LCD, il faut ajouter un ou 2 espace(s) après l'affichage de la variable, ainsi, lorsque ça descend d'une ou 2 décade(s), l'espace efface le ou les chiffres devenus inutiles.

Un cas ressemblant ici.

A+
Cordialement
jpbbricole

Merci beaucoup, c'est réglé par ton astuce.
Vous trouverez le code mis à jour dans mon premier post.

Voici le morceau de code :

lcd.setCursor(16, 1);
lcd.print(compteur);
lcd.print(" ");

Un espace derrière la variable ramène le curseur automatiquement au bon endroit lorsque la variable passe de 1000 à 999 et de 100 à 99 et de 10 à 9.

Merci à vous

Bonjour à tous,

Mon projet avance tranquillement, je commence à rencontrer un problème de mémoire dynamique suite à l’intégration de six tableaux de filtre BUSCAN qui incluent des valeurs jusqu’à 15000 nombres.

le tableau un démarre à la valeur de 1 à pas de 6

le tableau deux démarre à la valeur de 2 à pas de 6

le tableau trois démarre à la valeur de 3 à pas de 6 ect…

Vous trouverez le programme mis à jour sur mon premier post.

Comment puise je réaliser les tableaux sans ittérer tous les nombres ?

Un mappage est il possible dans un tableau ?

Si tu as des gros tableaux, PROGMEM est ton ami
https://docs.arduino.cc/language-reference/en/variables/utilities/PROGMEM/


Dans ton cas, tu n'as pas besoin de tableaux il suffit de calculer les valeurs

  1. pour avoir la n-ième valeur: val = 1 + 6 * n
  2. pour avoir la n-ième valeur: val = 2 + 6 * n
  3. pour avoir la n-ième valeur: val = 3 + 6 * n

Donc pour avoir la n-ième valeur du i-ième tableau: val = i + 6 * n

D'ailleurs pourquoi avoir fait plusieurs tableaux alors qu'au final ils contiennent la même progression à l'offset prêt
Le second tableau c'est le premier tableau + 1
Le troisième tableau c'est le premier tableau + 2
et ainsi de suite