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.