Primo progetto: Cronometro a fotocellula

Ciao… da circa una settimana mi sto cimentando nella programmazione per realizzare un cronometro con due fotocellule che vorrei utilizzare per cronometrare auto radiocomandate su una distanza nota. Al momento utilizzo due fotocellule E18-D80NK che, bene o male, ritengo adeguate. Ho due problemi ancora irrisolti:

  1. la fotocellula 1 (quella dello start per capirci) attiva il conteggio del tempo SOLO quando l’oggetto è transitato e NON appena il segnale cambia lo stato da LOW ad HIGH (per spiegarmi meglio…se l’oggetto fosse lungo due m, il cronometro parte quando sono passati tutti i due m…);
  2. con il codice attuale, riesco a calcolare il tempo (anche se con l’errore del punto 1) ma, se provo a calcolare la velocità con distanza nota (inserita nel codice) il risultato è sempre zero (sia con variabili INT, FLOAT, DOUBLE).

Vi posto il codice attuale in cui compaiono anche variabili che non utilizzo ma che pensavo di inserire nel caso volessi implementare il sistema con una futura terza fotocellula:

const int Pinstart = 2;
const int Pinstop = 3;
int sensorValue;
int sensorValue1;

unsigned long t0;
unsigned long t1;
unsigned long t2;
unsigned long t3;
float v;  // spazio = 2m, V= m/s
float v1; // spazio = 2m, V= Km/h
int a = 0;
int b = 0;
int c = 0;
int d;
int flag = 1;

void setup() {
  delay(1500);
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(2, 0);
  lcd.print("Chrono");
  delay(4000);
  lcd.clear();
  lcd.setCursor(4, 1);
  lcd.print("READY !");
  pinMode(Pinstart, INPUT);
  pinMode(Pinstop, INPUT);
  Serial.begin(9600);
}
void loop()
{
  sensorValue = digitalRead (Pinstart);
  sensorValue1 = digitalRead (Pinstop);
  if ((sensorValue == HIGH) && (flag < 2))
  {
    t0 = millis();
    flag = 0;
  
  }
  if (flag != 1)
  {
    lcd.setCursor(0, 0);
    lcd.print("TEMPO  (secondi)");
    lcd.setCursor(0, 1);
    lcd.print("PARTITO!    ");
    t1 = millis() - t0;
    t2 = t1 / 1000;
    if (t2 <= 9)
    {
      lcd.setCursor(12, 1);
      lcd.print("   ");
      lcd.print(t2);
    }
    if ((t2 > 9) && (t2 <= 99))
    {
      lcd.setCursor(12, 1);
      lcd.print("  ");
      lcd.print(t2);
    }
    if ((t2 > 99) && (t2 <= 999))
    {
      lcd.setCursor(12, 1);
      lcd.print(" ");
      lcd.print(t2);
    }
    if ((t2 > 999) && (t2 <= 9999))
    {
      lcd.setCursor(12, 1);
      lcd.print(t2);
    }
 }

if ((sensorValue1 == HIGH) && (flag == 0))
  {
    lcd.setCursor(0, 0);
    lcd.print("TEMPO (millisec)");
    lcd.setCursor(0, 1);
    lcd.print("STOP!    ");
    if ((t1 > 9) && (t1 <= 99))
    {
      lcd.setCursor(9, 1);
      lcd.print("     ");
      lcd.print(t1);
    }
    if ((t1 > 99) && (t1 <= 999))
    {
      lcd.setCursor(9, 1);
      lcd.print("    ");
      lcd.print(t1);
    }
    if ((t1 > 999) && (t1 <= 9999))
    {
      lcd.setCursor(9, 1);
      lcd.print("   ");
      lcd.print(t1);
    }
    if ((t1 > 9999) && (t1 <= 99999))
    {
      lcd.setCursor(9, 1);
      lcd.print("  ");
      lcd.print(t1);
    }
    if ((t1 > 99999) && (t1 <= 999999))
    {
      lcd.setCursor(9, 1);
      lcd.print(" ");
      lcd.print(t1);
    }
    if ((t1 > 999999) && (t1 <= 9999999))
    {
      lcd.setCursor(9, 1);
      lcd.print(t1);
    }
    flag = 1;

        c = t1 / 1000;    // elimino le ultime tre cifre per avere la parte intera
        d = t1 % 1000;   // estraggo solo le ultime tre cifre 
        float v = 1000 / t1; // calcolo velocità con distanza nota di 1000 mm
        float v1 = (v * 36) / 10;
        delay(2000);   // Risultati, tempo e velocità
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("T (s)"); // tempo impiegato in ms
        lcd.setCursor(10, 0);
        lcd.print(c);
        lcd.print(".");
        lcd.println(d);
        lcd.setCursor(0, 1);
        lcd.print("V (m/s)"); // velocità in m/s
        lcd.setCursor(10, 1);
        lcd.print (v);
        delay(2000);           // velocità in Km/h
        lcd.setCursor(0, 1);
        lcd.print("V (Km/h)"); 
        lcd.setCursor(10, 1);
        lcd.print(v1);
 }
 }

@wbirillo : in conformità al REGOLAMENTO , punto 7, cortesemente edita il tuo post qui sopra (quindi NON scrivendo un nuovo post, ma utilizzando il bottone a forma di piccola matita :pencil2: che si trova in basso del tuo post), seleziona TUTTA la parte di codice e premi l’icona </> nella barra degli strumenti per contrassegnarla come codice. Inoltre, così com’è non è molto leggibile … assicurati di averlo correttamente indentato nell’IDE prima di inserirlo (questo lo si fa premendo ctrlT su un PC o cmdT su un Mac all’intero del IDE). Per maggiori informazioni … punto 17.2 del succitato regolamento. Grazie.

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà sistemato il codice, nel rispetto del suddetto regolamento nessuno ti risponderà, quindi ti consiglio di fare il tutto al più presto. :wink:

Grazie mille… spero di aver fatto bene :slight_smile:

Ciao! Il conteggio del tempo con la funzione millis() è impreciso, quindi il tempo che puoi ricavare è poco preciso rispetto a quello di un vero cronometro, bisognerebbe usare un rtc per avere conteggio esatto.

Detto questo spieghiamo il tuo problema.

if ((sensorValue == HIGH) && (flag < 2))
  {
    t0 = millis();
    flag = 0;
  
  }

il sensore segna HIGH, e prende il tempo, ma il sensore continua a segnare HIGH finchè la fotocellula non si disattiva, finchè l’oggetto non è transitato, per cui questo pezzo di codice si ripete fino a che l’oggetto è transitato, bloccando il cronometro.
Come puoi risolvere, una variabile che passa dallo stato 0 a 1, inizialmente è allo stato 0, e abilita la condizione, esegue il codice e passa allo stato 1, in questo modo il codice viene eseguito un unica volta al primo segnale della fotocellula.
Quando si attiva la fotocellula stop la variabile torna a 0, permettendo un nuovo conteggio.

if ((sensorValue == HIGH) && (flag < 2) && attivo==0)
  {
    t0 = millis();
    flag = 0;
    attivo=1; // Esegue il codice un unica volta, rendendo la condizione falsa
  
  }

// All'attivazione della fotocellula stop attivo=0; permettendo un nuovo conteggio

Ma scusate, non si fa prima a gestire il passaggio con gli interrupt invece di fare tutti sti check? Tanto più che già usa i pin “giusti” (il 2 e 3)… :wink:

Poi darei anche una leggera ristrutturata al codice, per renderlo più leggibile. Ecco un esempio, dove ho solo omesso i calcoli e gli output sull’LCD:

#define P_START 2
#define P_STOP 3

volatile unsigned long t0 = 0;
volatile unsigned long t1 = 0;
bool isRunning = false;

void setup() 
{
  Serial.begin(9600);
  Serial.println("CRONO 1.0");
  delay(1500);
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(2, 0);
  lcd.print("Chrono");
  delay(4000);
  lcd.clear();
  lcd.setCursor(4, 1);
  lcd.print("READY !");
  pinMode(P_START, INPUT);
  pinMode(P_STOP, INPUT);
  attachInterrupt(digitalPinToInterrupt(P_START), timeStart, RISING);
  attachInterrupt(digitalPinToInterrupt(P_STOP), timeEnd, RISING);
  Serial.println("Ready.");
}

void timeStart() {
  t0 = millis();
  t1 = 0;
}
void timeEnd() {
  if (t0 > 0)
    t1 = millis();
}

void loop()
{
  // Rilevo quando inizia il cronometraggio
  // Metti nella funzione "started()" quello che deve fare quando parte 
  // (es. scrivi "PARTITO!" e pulisci i dati)
  if (t0 > 0 && t1 == 0 && !isRunning) {
    isRunning = true;
    started();
  }
  // Cronometro in azione
  // Metti in "running()" quello che deve fare per mostrare il tempo attuale 
  if (t0 > 0 && t1 == 0 && isRunning) {
    running();
  }
  // Arrivo!
  // Metti in "stopped()" quello che deve fare per la fine, come
  // calcolare e mostrare il tempo e la velocità media rilevata
  if (t1 > 0 && isRunning) {
    stopped();
    isRunning = false;
    t0 = 0;
    t1 = 0;
  }
  // Mettici un delay pari alla risoluzione minima che vuoi avere nel diplay,
  // inutile aggiornare il display più spesso di qualche decimo di secondo
  // perché i cristalli liquidi non possono essere così rapidi...
  delay(300); 
}

void started() {
  // ...
}

void running() {
  // ...
}

void stopped() {
  // ...
}

Entrambe le idee fantastiche… come dicevo nella presentazione sto imparando solo ora il codice. Tolto Access, l’ultima volta che mi misi a programmare fu circa 30 anni fa con Basic, Logo, Turbo Pascal e Lotus123…eree geologiche. Proverò entrambi i metodi in modo da apprendere cose nuove. Vi aggiorno :slight_smile:

 float v = 1000 / t1;
v = 1000.0 / t1;

uhhhh il DAT:… quanti bei ricordi :slight_smile: Ero più innamorato del MiniDisc che ebbi per diversi anni, però… bei tempi! Grazie per l’info sul calcolo. Stasera studiando un po’ il codice sono riuscito a farlo funzionare e sto affinando il tutto. Una cosa non riesco a capire: se io sostituisco il valore 1000 (altro non è che una distanza) con una variabile x (in modo che possa variarla facilmente), il sistema mi va in tilt. Ho cercato nella sintassi ma non capisco dove sbaglio. Chiedo venia.

Riconosco che all’inizio mi ci è voluto un po’ di tempo per entrare nella logica… ma ora che ho capito, mi ci trovo bene. In effetti è più ordinato. Piano piano affino tutto ma già adesso funziona benone cavandomela con 6502 byte.

Grazie al vs aiuto e ad un po' di insistenza, sono riuscito ad affinare il codice e farlo funzionare alla perfezione. Prima di chiudere la scheda dentro una bella scatoletta di plastica, mi sono accertato che i cablaggi e il sistema funzionassero bene e tutto è andato liscio fino a quando... non ho iniziato a fare le prove serie di cronometraggio. Premetto che ho collegato il sistema a due fotocellule E18 - D80NK...dopo alcune prove una ha iniziato a dare i numeri...cavo che non faceva bene il suo mestiere. L'ho sostituita ma poi è partita l'altra... alla fine ne ho prese 4 e, nonostante sulle prime sembrava funzionare a dovere... mi danno problemi nella rilevazione a distanze superiori a 5 cm (a me servirebbe che avessero un tiro di un buon 30 cm). Regolando il trim in un verso o nell'altro non cambiava la situazione e molte volte le fotocellule sembravano non inviare il segnale alla scheda... qualcuno conosce delle fotocellule serie a buon mercato oppure sa dirmi quale macuba bisogna fare per far funzionare queste E18?

Funzionano per la rilevazione di ostacoli, ma non hanno una buona velocita' di risposta, sarebbe meglio usare due fototransistor e due led , con fototransistor e led posti uno di fronte all'altro sui due lati della pista ... i fotodiodi sono ancora piu veloci, ma poi serve un po di lavoro in piu per la circuiteria che gli va intorno (comunque ormai sono disponibili fototransistor abbastanza veloci, ad esempio LTR 3208, LTR4206, o ancora meglio W53P3C o SFH309).

Se proprio non e' possibile mettere uno da un lato ed uno dall'altro, si possono mettere affiancati in due tubetti di metallo, con un pezzo di catarifrangente dall'altra parte :wink:

più che la velocità di risposta mi interessava l'affidabilità anche perché, avendo due punti in sequenza (start e stop) i ritardi sono uguali , sia in start che in stop. I fototransistor li ho visti ma non ho molta voglia di sbattermi tra saldature, modifiche ecc... in quanto non sono molto bravo a saldare e preferivo una soluzione un pochino più plug-in, perché poi il tutto lo devo anche portare in giro. Immagino che le fotocellule da garage me le bocci nonostante il principio si simile... vedo di valutare un attimo il tutto...per ora grazie mille!

forse ho trovato un buon compromesso ... che ne pensi? foto sensore laser

Quel sensore usa un fototransistor, non posso dare giudizi sulle specifiche perche' non ci sono dati sul sito, ma dovrebbe essere piu preciso e ripetitivo della fotocellula usando un raggio laser invece che un led con la lente, pero' il giudizio finale puoi darlo solo tu facendo delle prove.