Indicatori di direzione Arduino + MCP2515 + 2Module Relay

Ciao a tutti.

Sto provando ad implementare un codice che piloti le frecce direzionali dal blocchetto a manubrio della moto leggendolo dal CANBUS sull'interfaccia OBD2.
L'utilizzo dei materiali è il seguente:
Arduino UNO;
MCP2515;
2 RELAY MODULE
Come si evince dallo sketch in allegato, nessun problema a leggere i tasti destra, sinistra, centrale e down.
Il problema nasce nel momento in cui devo far azionare i rele dopo la pressione dei tasti.
Su comando 4 Frecce, sinistra, funziona (ma hanno il delay bloccante e premendo il centrale non disattiva nulla, quindi è inutile);
Su comando destro (impostato con millis) blinka il rele solo quando tengo premuto il pulsante destro.

Il codice è il seguente:

#include <mcp_can.h>
#include <SPI.h>

//buffer
long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];

// button
const byte btndx[] = {0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}; //comando freccia destra
const byte btnsx[] = {0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}; //comando freccia sinistra
const byte btnce[] = {0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}; //comando centro disattiva frecce
const byte btndn[] = {0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00}; //comando 4 frecce

// tempo
unsigned long previousMillis = 0;
unsigned long currentMillis;
const long intervalon = 500;
const long intervaloff = 500;
const long durata = 10000;
//const long intervalbtn = 5000;
//boolean RELDXSTATE = digitalRead(7);
//boolean RELSXSTATE = digitalRead(8);

int RELDX = 7;
int RELSX = 8;

MCP_CAN CAN0(10);                          // Set CS to pin 10

void setup()
{
  Serial.begin(115200);
  if(CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_8MHZ) == CAN_OK) Serial.print("MCP2515 Init Okay!!\r\n");
  else Serial.print("MCP2515 Init Failed!!\r\n");
 
  pinMode(2, INPUT);                       // Setting pin 2 for /INT input
  pinMode(RELDX, OUTPUT);
  pinMode(RELSX, OUTPUT);
  digitalWrite(RELDX, HIGH);               //imposto lo stato del rele su spento
  digitalWrite(RELSX, HIGH);               //imposto lo stato del rele su spento
  CAN0.setMode(MCP_NORMAL);   // Change to normal mode to allow messages to be transmitted

}
// richiamo tasto centro per disattivare gli indicatori

void ce() {
 if (digitalRead(RELDX) == LOW) digitalWrite(RELDX, HIGH); //disattivo le frecce di destra
 if (digitalRead(RELSX) == LOW) digitalWrite(RELSX, HIGH); //disattivo le frecce di sinistra
}

// richiamo tasto destro

void dx()   {  //testata funzione millis
  
  Serial.println("DX");
  
  if (digitalRead(RELSX) == LOW) digitalWrite(RELSX, HIGH);
 // if (currentMillis - previousMillis > durata) { 
  if (digitalRead(RELDX) == LOW){                        
    if (currentMillis - previousMillis > intervaloff){  
      digitalWrite(RELDX, HIGH);                        
                                                        
     previousMillis = currentMillis;                    
    }
  } else {                                                  
   if (currentMillis - previousMillis > intervalon){   
      digitalWrite(RELDX, LOW);                            
    
     previousMillis = currentMillis; 
   }
  }   
  }

void sx()   {

  Serial.println("SX");
  if (digitalRead(RELDX) == LOW) digitalWrite(RELDX, HIGH);
  digitalWrite(RELSX, LOW);
  delay(600);
  digitalWrite(RELSX, HIGH);
  delay(600);
}

void hazard() {
 
  Serial.println("HAZARD");

  digitalWrite(RELSX, LOW);
  digitalWrite(RELDX, LOW);
  delay(600);
  digitalWrite(RELSX, HIGH);
  digitalWrite(RELDX, HIGH);
  delay(600);
}


void loop()
{
currentMillis = millis();
  
  if(!digitalRead(2))                                                        // If pin 2 is low, read receive buffer
  CAN0.readMsgBuf(&rxId, &len, rxBuf);                                       // Read data: len = data length, buf = data byte(s)
  if (len == sizeof btnsx && memcmp(btnsx, rxBuf, len) == 0){                // pulsante sinistra
  
  if (digitalRead(RELDX) == LOW) digitalWrite(RELDX, HIGH);
  digitalWrite(RELSX, LOW);
  delay(600);
  digitalWrite(RELSX, HIGH);
  delay(600);
  }

 
  if(!digitalRead(2))                                                        // If pin 2 is low, read receive buffer
  CAN0.readMsgBuf(&rxId, &len, rxBuf);                                       // Read data: len = data length, buf = data byte(s)
   if (len == sizeof btndx && memcmp(btndx, rxBuf, len) == 0)                // pulsante destra
  
dx();

  if(!digitalRead(2))                                                        // If pin 2 is low, read receive buffer
  CAN0.readMsgBuf(&rxId, &len, rxBuf);                                       // Read data: len = data length, buf = data byte(s)
  if (len == sizeof btnce && memcmp(btnce, rxBuf, len) == 0)                 // pulsante disattivazione frecce

ce();

  if(!digitalRead(2))                                                        // If pin 2 is low, read receive buffer
  CAN0.readMsgBuf(&rxId, &len, rxBuf);                                       // Read data: len = data length, buf = data byte(s)
  if (len == sizeof btndn && memcmp(btndn, rxBuf, len) == 0)                 // pulsante 4 frecce

hazard();
}  

La funzione che dovrei implementare è la seguente:
Tasto DX; disattiva lo stato del RELESX, accende RELEDX ad intermittenza di mezzo secondo per un tempo di 15 secondi (simulazione spegnimento automatico frecce)
Tasto SX; come DX al contrario;
Tasto CENTRO; disattiva le frecce, quindi manda in off RELEDX e RELESX;
Tasto DOWN; se premuto per 3 secondi attiva tutti e due i rele ad intermittenza di mezzo secondo fino a quando o si ripreme DOWN per altri 3 secondi oppure premendo il tasto CENTRO.

C'è qualcuno che protrebbe illuminarmi sulla realizzazione di questo progetto?
Ne approfitto anche per chiedere questo. Perché i rele si disattivano quando è impostato lo stato dei pin 7 e 8 su "HIGH"?
Vi ringrazio anticipatamente per l'attenzione

Buona sera e benvenuto nella sezione Italiana del forum,

cortesemente, come prima cosa, leggi attentamente il REGOLAMENTO di detta sezione, (... e, per evitare future possibili discussioni/incomprensioni, prestando molta attenzione al punto 15), dopo di che, come da suddetto regolamento (punto 16.7), fai la tua presentazione NELL'APPOSITA DISCUSSIONE spiegando bene quali esperienze hai in elettronica e programmazione, affinché noi possiamo conoscere la tua esperienza ed esprimerci con termini adeguati.

Grazie,

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposita discussione, nel rispetto del succitato regolamento nessuno ti risponderà (eventuali risposte o tuoi ulteriori post, verrebbero temporaneamente nascosti), quindi ti consiglio di farla al più presto. :wink:

Grazie mille per l'avviso Guglielmo, appena fatto :grinning:

Per gestire correttamente cose di questo tipo (ma sono molti i casi, visto che parliamo di un microcontrollore di "qualcosa") evitando cicli bloccanti è quello di iniziare disegnando una macchina a stati finiti, o FSM, ed una volta fatto si deve implementare nel codice usando almeno una variabile che conservi lo stato corrente del sistema ed in base a questo operare.

Un elenco di stati per delle frecce potrebbe essere questo:

S_OFF: tutto spento
S_LEFT: lampeggia solo il sinistro
S_RIGHT: lampeggia solo il destro
S_HAZARD: lampeggiano tutte

Quindi in genere o si fa un disegno o almeno uno schema di transizione di stato ossia righe nel formato:

stato corrente: SE condizione ALLORA fai questa azione PASSA a questo stato

Per gestire le normali frecce (solo con tre tasti più uno per le 4 frecce), potrebbe essere questo:

S_OFF:   SE tasto sx premuto ALLORA attiva lampeggiamento sx PASSA a S_LEFT
S_LEFT:  SE tasto centro premuto ALLORA disattiva ogni lampeggiamento  PASSA a S_OFF
S_LEFT:  SE tasto dx premuto ALLORA attiva lampeggiamento dx PASSA a S_RIGHT
S_RIGHT: SE tasto centro premuto ALLORA disattiva ogni lampeggiamento  PASSA a S_OFF
S_RIGHT: SE tasto sx premuto ALLORA attiva lampeggiamento dx PASSA a S_LEFT

dove "attiva lampeggiamento" viene gestito con millis ed una variabile che indica se quella specifica luce debba lampeggiare o meno, una cosa del tipo (ovviamente questo è solo uno spunto):

#define P_LUCE_SX 2
#define P_LUCE_DX 3
#define FLASH_TIME 1000
bool FlashL = false;
bool FlashR = false;
unsigned long tmrFlashL = 0;
unsigned long tmrFlashR = 0;
unsigned long curMillis = 0;

...
void loop () {
  // Gestione lampeggianti
  curMillis = millis();
  if (FlashL)  {
    if (curMillis - tmrFlashL > FLASH_TIME) {
      tmrFlashL = curMillis;
      // Inverte lo stato
      digitalWrite(P_LUCE_SX, !digitalRead(P_LUCE_SX));
    }
  } else {
    // Spegne
    digitalWrite(P_LUCE_SX, LOW);
  }
  ...
    // Attiva il lampeggiamento sx
    FlashSX = true;
  ...
    // Disattiva il lampeggiamento sx
    FlashSX = false;
...
}

Per la gestione dello stato lo schema in genere è questo, ossia almeno uno "swtich" nel quale riporti le descrizioni delle condizioni all'interno di ogni singolo stato (ed ovviamente poi dovrai "tradurle" in codice reale):

#define S_OFF 0
#define S_LEFT 1
#define S_RIGHT 2
#define S_HAZARD 3
byte Stato = S_OFF; // stato iniziale
...
void loop()
{
  switch(Stato) {
    case S_OFF:
      // SE tasto sx premuto ALLORA attiva lampeggiamento sx PASSA a S_LEFT
      // TODO
      break;

    case S_LEFT:
      // SE tasto centro premuto ALLORA disattiva ogni lampeggiamento  PASSA a S_OFF
      // TODO
      // SE tasto dx premuto ALLORA attiva lampeggiamento dx PASSA a S_RIGHT
      // TODO
      break;

... eccetera ...
  }
}

NB: il cambio di stato implica semplicemente assegnare un nuovo valore alla variabile "Stato"...

Dopo che avrai disegnato la tua FSM, prova a modificare il tuo codice per implementarla.

2 Likes