Cronometro lap RC car

Ciao a tutti.
Sto realizzando una barriera cronometrica (scusate il termine) per una pista da macchine radiocomandate.
Le gare si svolgono uno per volta cronometrando i tempi su 8 giri.
Ho quindi realizzato un sistema così composto:
La barriera è realizzata con un piccolo puntatore laser che illumina una fotoresistenza.
8 led inizialmente fanno da semaforo per la partenza poi si spengono ad indicare il passaggio dei giri (questi sono comandati tramite uno shift register).
Un buzzer passivo blippa per fare inizialmente da semaforo e poi da avviso del passaggio.
Su uno schermo LCD vengono mostrati il tempo totale e il tempo per giro.
Infine un pulsante permette di riavviare il ciclo.

Il tutto pare funzionare ma sono sicuro che il programma contenga molti errori (oltre ad essere probabilmente rozzo).
Fondamentalmente non capisco perché non riesco a sincronizzare lo shift register con il suono e il trascorrere dei lap. Per farlo funzionare come volevo ho dovuto ogni volta ri-inviare l'input.

Questo è il codice che sto usando:

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 7, 8, 3, 2);

int latchPin = 5;              //shift register
int clockPin = 6;              //shift register
int dataPin = 4;               //shift register
const int Buzzer = 10;
const int Button = 9;
const int LuceMin = 600;       //soglia limite luce
int StatoRicevitore = 0;       //Record luce
int START = 0;                 //Blocco sequenza iniziale
int Lap = 0;                   //Contatore giri
int LapLED = 0;                //LED accesi
unsigned long TimeSTART = 0;   //Tempo iniziale
unsigned long TimeNOW = 0;     //Tempo corrente
unsigned long TimeSHOW = 0;    //Tempo da mostrare
unsigned long TimeLAP = 0;     //Tempo giro
unsigned long TimeLAST = 0;    //Tempo giro precedente
unsigned long TimeEND = 0;     //Tempo finale
unsigned long Minuti = 0;
unsigned long Secondi = 0;
unsigned long MilliSec = 0;
unsigned long MinutiL = 0;
unsigned long SecondiL = 0;
unsigned long MilliSecL = 0;

void setup()
{
  Serial.begin(9600);
  lcd.begin(16, 2);                       // set up the LCD's number of columns and rows
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(Buzzer, OUTPUT);
  pinMode(Button, INPUT);
}

void loop()
{
  // START inizio gara
  if (START == 0) {
    START = START + 1;
    lcd.display();                                  // Turn on the display:
    lcd.clear();
    lcd.setCursor(0, 0);                            //Display position riga colonna
    lcd.print("START    ");
    lcd.setCursor(0, 1);
    lcd.print("        By ALEK");
    digitalWrite(latchPin, LOW);

    for (int i = 1; i < 3; i++) {                  //sequenza semaforo
      digitalWrite(latchPin, HIGH);                //accesi tutti no red
      shiftOut(dataPin, clockPin, LSBFIRST, 127);
      digitalWrite(latchPin, LOW);
      delay (1000);
      tone (Buzzer, 600, 500);
      digitalWrite(latchPin, HIGH);                //spenti tutti
      shiftOut(dataPin, clockPin, LSBFIRST, 0);
      digitalWrite(latchPin, LOW);
      delay (2000);
    }

    digitalWrite(latchPin, HIGH);                 //accesi tutti LAP 0
    shiftOut(dataPin, clockPin, LSBFIRST, 255);
    digitalWrite(latchPin, LOW);
    digitalWrite(latchPin, HIGH);                 //accesi tutti LAP 0
    shiftOut(dataPin, clockPin, LSBFIRST, 255);
    digitalWrite(latchPin, LOW);
    lcd.setCursor(0, 0); //Display position
    lcd.print("GO     ");
    tone (Buzzer, 700, 700);
  }

  // CONTEGGIO GIRI
  // GIRO INIZIALE 0
  StatoRicevitore = analogRead(A0);
  if (StatoRicevitore < LuceMin && START == 1) {
    //PRIMO PASSAGGIO AVVIO TIMER
    START = START + 1;
    digitalWrite(latchPin, HIGH);                 //accesi tutti LAP 0
    shiftOut(dataPin, clockPin, LSBFIRST, 255);
    digitalWrite(latchPin, LOW);
    TimeSTART = millis();                         //Record tempo inizio
    TimeLAST = TimeSTART;
    lcd.clear();
    lcd.setCursor(0, 0);                          //Display position colonna riga
    lcd.print("LAP: 0  ");
    lcd.setCursor(0, 1);                          //Display position colonna riga
    lcd.print("Time:");
    tone (Buzzer, 700, 250);
    delay (200);
  }

  if (START == 2) {
    //GIRI SUCCESSIVI 1-7
    do {
      //CONTEGGIO TEMPO
      TimeNOW = millis();                           //Registrazione tempo atuale
      TimeSHOW = TimeNOW - TimeSTART;               //Tempo trascorso da inizio
      MilliSec = TimeSHOW;
      MilliSec = MilliSec % 1000;
      Secondi = TimeSHOW / 1000;
      Minuti = Secondi / 60;
      Secondi = Secondi % 60;
      Minuti = Minuti % 60;
      if (Minuti < 10) {
        lcd.setCursor(9, 1);                        //Display position colonna riga
      } else {
        lcd.setCursor(8, 1);                        //Display position colonna riga
      }
      lcd.print(Minuti);
      lcd.setCursor(10, 1);                         //Display position colonna riga
      lcd.print(":");
      lcd.setCursor(11, 1);                         //Display position colonna riga
      if (Secondi < 10) {
        lcd.print("0");
        lcd.setCursor(12, 1);                       //Display position colonna riga
      }
      lcd.print(Secondi);
      lcd.setCursor(13, 1);                         //Display position colonna riga
      lcd.print(":");
      lcd.setCursor(14, 1);                         //Display position colonna riga
      lcd.print(MilliSec);
      //CONTROLLO GIRO
      StatoRicevitore = analogRead(A0);
      if (StatoRicevitore < LuceMin) {
        Lap = Lap + 1;
        switch (Lap) {
          case 0: LapLED = 255;
            tone (Buzzer, 700, 250);
            break;
          case 1: LapLED = 254;
            tone (Buzzer, 700, 250);
            break;
          case 2: LapLED = 252;
            tone (Buzzer, 700, 250);
            break;
          case 3: LapLED = 248;
            tone (Buzzer, 700, 250);
            break;
          case 4: LapLED = 240;
            tone (Buzzer, 700, 250);
            break;
          case 5: LapLED = 224;
            tone (Buzzer, 700, 250);
            break;
          case 6: LapLED = 192;
            tone (Buzzer, 700, 250);
            break;
          case 7: LapLED = 128;
            tone (Buzzer, 700, 250);
            break;
          case 8: LapLED = 0;
            START = START + 1;
            tone (Buzzer, 700, 700);
            break;
        }
        lcd.setCursor(5, 0);                            //Display position colonna riga
        lcd.print(Lap);
        digitalWrite(latchPin, HIGH);                   //lap X
        shiftOut(dataPin, clockPin, LSBFIRST, LapLED);
        digitalWrite(latchPin, LOW);
        digitalWrite(latchPin, HIGH);                   //lap X
        shiftOut(dataPin, clockPin, LSBFIRST, LapLED);
        digitalWrite(latchPin, LOW);

        TimeLAP = TimeNOW - TimeLAST;                 //Tempo trascorso da inizio giro
        MilliSecL = TimeLAP;
        MilliSecL = MilliSecL % 1000;
        SecondiL = TimeLAP / 1000;
        MinutiL = SecondiL / 60;
        SecondiL = SecondiL % 60;
        MinutiL = MinutiL % 60;
        lcd.setCursor(9, 0);                          //Display position colonna riga
        lcd.print(MinutiL);
        lcd.setCursor(10, 0);                         //Display position colonna riga
        lcd.print(":");
        lcd.setCursor(11, 0);                         //Display position colonna riga
        if (SecondiL < 10) {
          lcd.print("0");
          lcd.setCursor(12, 0);                       //Display position colonna riga
        }
        lcd.print(SecondiL);
        lcd.setCursor(13, 0);                         //Display position colonna riga
        lcd.print(":");
        lcd.setCursor(14, 0);                         //Display position colonna riga
        lcd.print(MilliSecL);
        TimeLAST = TimeNOW;
        delay (300);
      }
    } while (Lap < 8);
  }

  //RESTART
  if (digitalRead(Button)) {
    Lap = 0;
    START = 0;
  }
}

Grazie mille per la pazienza e per l'aiuto.

Avrei poi anche una seconda domanda ma visto che è più per la parte hardware non so se è il caso di porla qui o se conviene aprire una nuova discussione.

Non mi tornava la sequenza di start delle luci del semaforo, cosi ho cercato con google e trovato questo test:
https://f1-start.glitch.me/

Si tratti di un test di riflessi.

201 ms era il mio miglior risultato, quando casualmente ho premuto con le luci ancora accese ed ho ottenuto 71ms.

Tornando alla sequenza di accensione e spegnimento del tuo sketch. Tu hai 8 led se li vuoi fare accendere uno ogni secondo ci vorranno 8 secondi, oppure 2 led ogni secondo e ci vorranno 4 secondi. Quando sono tutti accesi salvo il tempo di millis() e dovrei spegnerli in un tempo casuale nel range 1÷3 secondi. Per essere realistico ci vorrebbe un generatore di numeri casuali tra 1000ms e 3000ms a questo ci penso dopo.

Ciao.

1 Like

Ciao.
Grazie per la risposta ma ti chiedo scusa non ho capito cosa intendi. Come mai mi parli di generare dei numeri casuali?

Aggiungo eventualmente questa spiegazione che magari può aiutare.

Io ho 8 led in totale di cui 7 verdi e uno rosso.
Nella sequenza di partenza i 7 verdi si accendono mentre il buzzer fa un beep.
Poi si spengono, si riaccendono e si spengono nuovamente.
Con un beep un po' più lungo si riaccendono tutti e 8 i led, sullo schermo Start passa a GO e Arduino si mette in attesa di una diminuzione della luce sulla fotoresistenza.

Al primo passaggio della macchina si sente un beep corto e comincia a contare il LAP 0 (riga superiore) e il tempo che trascorre (riga inferiore). tutti i led sono accesi.
Ad ogni passaggio si spegne un led, il contatore dei lap sul monitor si incrementa di uno e viene segnato anche il tempo impiegato per fare il giro.
Questo si ripete fino a che non si spegne anche il led rosso (che indica l'ultimo giro) e con un beep un po' più lungo si blocca anche il tempo totale.

Premendo il pulsante si può riavviare il ciclo.

Chiedo scusa... nello sketch mi sono scordato l'avvio del serial monitor che usavo all'inizio per settare i parametri (ma che potrei eventualmente usare per visionare tutti i tempi giro su PC).

Ora molte cose sono più chiare. Ecco perché il for (int i= 1; i < 3..., ecco perché lo shitfout di 127, perché i primi 7 led verdi sono connessi Q0÷Q6, su Q7 c'è il led rosso.

Di una cosa non sono certo e chiedo conferma: Si tratta di partenza su giro lanciato?
Se fosse così in effetti non serve il generatore random che serve invece nel caso in cui l'auto fosse ferma in attesa della notifica visiva dell'istante di inizio gara.

const byte PIN_LATCH = 5;
const byte PIN_CLOCK = 6;
const byte PIN_DATA = 4;

void writeShiftRegister(byte data)
{
   digitalWrite(PIN_LATCH, LOW);
   shiftOut(PIN_DATA, PIN_CLOCK, MSBFIRST, data);
   digitalWrite(PIN_LATCH, HIGH);
}

Sopra una piccola e comoda funzione per scrivere data nello shift register.

Il tuo for diveterebbe così:

for (int i = 1; i < 3; i++) {                 
    writeShiftRegister(127);
    delay (1000);
    tone (Buzzer, 600, 500);
    writeShiftRegister(0);
    delay (2000);
}

Ciao.

Toglierei lo switch/case:
LapLED = 255-((1<<Lap)-1);
ovvero:
LapLED = (byte)~((1<<Lap)-1);
mettendo poi un
if (Lap==8)
per il suono più lungo.

Poi:

      lcd.print(Minuti);
      lcd.print(':');
      if (Secondi < 10) {
        lcd.print('0');
      }
      lcd.print(Secondi);
      lcd.print(':');
      lcd.print(MilliSec)

Considera che, dopo aver scritto un carattere, il carattere del print successivo verrà messo naturalmente alla posizione successiva.

Per scrivere singoli caratteri è più conveniente usare 'x' anziché "x", perché vengono trattati come singoli caratteri anziché come stringhe con il terminatore, quindi occupano un byte in meno.

Anche per MilliSec devi fare il posizionamento corretto e la cancellazione delle cifre nulle, altrimenti se dopo 123ms scrive 4ms si leggerà 423ms!

Grazie mille per il tempo che mi dedicate.
Cercherò di mettere a frutto le vostre indicazioni.
@Maurotec: si esatto, la gara è sul giro lanciato quindi va bene che il tempo parta dal primo passaggio sul traguardo. Non consideriamo la prontezza di riflessi per scattare al fischio.
Anche perché poi sarà da provare sulla pista (all'aperto) se si vede e si sente dal palco di guida.

@Datman: capito. Correggo l'uso delle virgolette e il posizionamento dei millisecondi grazie. Non capisco però la sintassi e il significato della tua risposta precedente.
Dovrei eliminare completamente lo switch per sostituirlo con quello che indichi tu?
LapLED = 255-((1<<Lap)-1);

Ciao

Esatto. Sicuramente la sintassi incompresa è (1 << Lap).
Si tratta di un bit shift a sinistra. Il risultato sostituendo i numeri alla variabile Lap è il seguente:

(1  << 0) risultato 1
(1  << 1) risultato 2
(1  << 2) risultato 4
(1  << 3) risultato 8
(1  << 4) risultato 16
(1  << 5) risultato 32
(1  << 6) risultato 64
(1  << 7) risultato 128

255 ((1) - 1) = 255
255 - ((128) - 1) = 128

Attenzione Lap non avrà mai valore zero:

if (StatoRicevitore < LuceMin) {
        Lap = Lap + 1;
        switch (Lap) {  // case 0 mai eseguito
          case 0: LapLED = 255;
            tone (Buzzer, 700, 250);
            break;
          case 1: LapLED = 254;

Ciao.

Sì, come dice Maurotec << fa scorrere i bit a sinistra un numero di volte pari al valore che lo segue.
Considerando che 1 in binario possiamo scriverlo 0b00000001:
1<<0 = 0b00000001, cioè 1; sottraendo 1: 0; 255-0 = 255;
1<<1 = 0b00000010, cioè 2; sottraendo 1: 1; 255-1 = 254;
1<<2 = 0b00000100, cioè 4; sottraendo 1: 3; 255-3 = 252;
1<<3 = 0b00001000, cioè 8; sottraendo 1: 7; 255-7 = 248;
1<<4 = 0b00010000, cioè 16; sottraendo 1: 15; 255-15 = 240;
I valori che si ottengono sono quelli che hai scritto nei case
-che, "chissà perché" :smile:, mi fanno venire in mente le maschere di sottorete...:

L'altra forma, che è equivalente:
LapLED = (byte)~((1<<Lap)-1);
fa ugualmente (1<<Lap)-1, ma poi, anziché sottrarre il risultato a 255, fa la negazione dei bit (operatore ~), perciò:

0b00000001-1 = 0b00000000; negazione = 0b11111111=255;
0b00000010-1 = 0b00000001; negazione = 0b11111110=254;
0b00000100-1 = 0b00000011; negazione = 0b11111100=252;
0b00001000-1 = 0b00000111; negazione = 0b11111000=248;
0b00010000-1 = 0b00001111; negazione = 0b11110000=240.

Il casting a byte (byte) è indispensabile perchè per default i valori sono considerati a 16 bit! Se non ci fosse il casting, non verrebbe 255-x ma 65535-x.

Grazie mille ad entrambi. Era proprio quello che non capivo.
Purtroppo essendo il mio livello di programmazione molto basico mi mancano molti di questi trick di sintassi.

Grazie ancora e buona giornata

Sì. Lap+=1 (cioè Lap=Lap+1) va messo dopo tutti i case.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.