contatore rpm tramite il sensore tcrt5000

salve a tutti. come da titolo, vorrei realizzare un contatore rpm tramite il sensore tcrt5000 visibile in allegato. https://www.ebay.it/itm/2PCS-TCRT5000-IR-Infrared-Line-Track-Follower-Sensor-Obstacle-Avoidanc-Arduino/122678201533?hash=item1c9030ccbd:g:1fEAAOSwi8xZ6FVW quello che vorrei che facesse è contare le volte che il sensore incontra l'ostacolo posto nell'albero motore e mi desse istante per istante il valore di giri al minuto. non riesco a inquadrare la funzione matematica che mi potrebbe permettere di farlo. il sensore è digitale, quindi rileva o HIGH o LOW. ho pensato di dichiarare fuori dal setup una variabile, ad esempio "sensore_impulsi=50;" che va sul pin 50 di un arduino mega. inoltre dichiaro un altra variabile ad esempio "int rpm;" Al setup inizializzo la comunicazione al monitor seriale. al loop non so proprio come trattare questo impulso.. Ho pensato a un contatore di impulsi magari, che raccolga quanti passaggi effettua l'ostacolo nel sensore in 1 secondo, e che moltiplichi questa variabile per 60 secondi.. gradirei spunti di riflessione grazie

Quel sensore "restituisce" high ogni volta che vede un ostacolo. Dovresti contare le volte che passa da high a low e poi fare i calcoli che ti servono. Prova a buttar giù qualcosa e postalo, così proviamo a sistemarlo. Magari parti da un esempio semplice, tipo accendere un led ogni volta che passi col dito. Poi, una volta capito il funzionamento, si complica quanto si vuole.

Ecco, un codice di questo genere dovrebbe accedere il led quando il sensore rileva l'ostacolo, e spegnerlo quando non lo rileva. È corretto?

int sensore=52;
int led=50;
void setup() ;
pinMode (sensore, INPUT) ;
pinMode (led, OUTPUT) ;
Serial. Begin(9600);

void loop() ;
if (digitalRead(sensore ==HIGH)) ;
digitalWrite (led, HIGH) ;
else
digitalWrite(led, LOW) ;
delay(100) ;

L'hai provato? Credo proprio che non venga nemmeno compilato...

Ce l'hai un arduino, un sensore e un led per fare delle prove o vuoi fare tutto solo in teoria?

Comunque, a parte portare quel codice in una forma almeno compilabile, diciamo che in teoria fa quello che vuoi: accende un led quando vede un ostacolo. Se però al posto del led metti un contatore che si incrementa hai qualche problema perché continui ad incrementare finché l'ostacolo rimane lì invece di incrementare una sola volta per ogni ostacolo.

PS: ma ti interessa capire come funziona la cosa o stai cercando solo uno sketch che funziona? giusto per evitare di scrivere roba che non ti interessa...

Non l ho provato, era giusto per capire se era il concetto che intendevi quando mi hai proposto di scrivere qualcosa che accende un led quando vede l'ostacolo. A parte gli errori di sintassi, la logica è quella, credo. Si comunque ho tutto l'occorrente. Hai introdotto il concetto di contatore, e non so cosa sia, né come funziona. Mi pare di capire che bisogna lavorare sul cambio di stato, da HIGH a LOW.. Si, certo che mi interessa capire come funziona la cosa, non sto cercando pappa pronta o cose simili, solo che il mio livello di conoscenza è quello che è...

Per contatore intendo semplicemente una variabile in cui memorizzi il numero di volte che il sensore vede un ostacolo (quella che all'inizio hai chiamato rpm).

Sì, nel tuo caso, secondo me, dovresti contare ogni volta che va a low dopo essere stato high.

Curiosità... che velocità può raggiungere il motore? Quanti impulsi al secondo dovrebbe leggere il sensore?

Una variabile che memorizzi il numero di volte che passa da HIGH a LOW.. Non ho idea di come si possa fare.. C è una funzione che lo fa? Credo che allora bisogna altre due variabili, una tipo SENSORE_HIGH e l'altra SENSORE_LOW.. può essere? Per il numero di giri, mi sembra difficile che possa superare i 10 giri al secondo

A e' una variabile ... A++ significa aggiungi uno al valore della variabile ... ecco fatto un contatore ... ;)

Scherzi a parte, il "contatore" non e' altro che una variabile (a cui dai il nome che vuoi tu, mica deve essere A per forza ;)), ed al cui valore aggiungi (o togli) uno secondo le condizioni che leggi e/o quello che deve poi fare nello sketch ... se parti con il valore zero, ogni volta che aggiungi (o sottrai) uno, "conti" un'evento, da li il nome "contatore" ... tutto qui ...

Per usarla per sapere quanti giri fai, la incrementi ogni volta che c'e' l'evento che ti interessa (nel tuo caso il passaggio dell'ostacolo), e poi ogni secondo ad esempio la leggi, e dal valore che ha, calcoli gli RPM, se in un secondo leggi 10, ovviamente sai che hai 600RPM, e cosi via ... ovviamente la devi anche rimettere a zero dopo ogni lettura ;)

Piu che altro, sarebbe meglio che usassi un'interrupt che legga solo il cambio di stato, per non avere falsi incrementi ... oppure devi scriverti una parte che legge ed incrementa solo ogni volta che passi da alto a basso (o viceversa), e poi ignora lo stato finche' non torna da basso ad alto (o viceversa), altrimenti per tutto il tempo che il sensore legge, il contatore incrementera' di uno ad ogni giro del loop ...

>washe850: ... andiamo per gradi, per fare una cosa fatta bene dovrai usare gli "interrupt", quindi ... comincia con studiarti gli interrupt. Un buon inizio è studiare QUI ... assimila bene il concetto e poi si va avanti con il tuo programma.

Guglielmo

P.S: Etem mi ha anticipato sul suggerimento ... ;)

… e dai una letta anche l’allegato documento (… è meno Arduino like e più tecnico) :wink:

Guglielmo

Interrupts.pdf (168 KB)

Etemenanki: poi ogni secondo ad esempio la leggi, e dal valore che ha, calcoli gli RPM, se in un secondo leggi 10, ovviamente sai che hai 600RPM

Il principio è corretto... per un frequenzimetro, ma per frequenze così basse bisognerebbe aspettare diversi secondi per ridurre l'errore (con una misura di un solo secondo si potrebbero avere 40rpm di errore su 600).

In questo caso meglio misurare il tempo tra due fronti di salita o tra due fronti di discesa (periodimetro). Anche con un polling "becero" leggendo micros si dovrebbe stare sotto i 40µs di errore, pari allo 0.04% a 600rpm (0.24rpm di errore).

La cattura dei fronti tramite interrupt dovrebbe migliorare ancora la precisione.

Dopo di che gli rpm si ottengono direttamente con: 60000000/µs

Ho cercato di capire il funzionamento degli interrupt. Non mi sono molto chiare alcune cose. La prima è: Quando ,fuori dal setup, perché si dichiara una variabile di tipo "volatile", e perché per esempio è di tipo "byte"?

La seconda è : Mi è chiaro il motivo per cui le funzioni delay () e millis () non vanno d'accordo con l'interrupt, ma vuol dire che in nessun caso potranno comparire nel programma oppure in certe parti, ossia fuori dai casi in cui venga attivato l'interrupt, ci possono essere?

La terza è : In che modo, utilizzando gli interrupt, si arriva a un contatore?

Ora, per esempio, nel mio progetto, decido che l'interrupt verrà attivato in modalità RISING , credo quindi che lo sketch possa essere qualcosa di simile.

int sensore=2; // al pin 2 per via del fatto che è un pin abilitato alla funzione interrupt
int led=13; // pin dedicato all'accensione del led per ogni passaggio dell'ostacolo 
volatile byte impulso=0; // variabile interna all'interrup

Void Setup ()
pinMode (2,INPUT); //inizializzazione del pin2 sul quale è collegato il sensore in modalità INPUT
pinMode (13,OUTPUT); //inizializzazione del pin 13 come OUTPUT
attachInterrupt (digitalPinToInterrupt(2),segnale,RISING); // dichiarazione dell'interrupt con modalità RISING, cioè solo quando si passa da 0 logico a 1 logico

void loop ()
{
digitalWrite (13,impulso); // lo stato del led sul pin13 è comandato da valore di impulso
}

void segnale()
{
if (impulso == HIGH) //quando questa variabile  (che parte da livello logico 0), diventa HIGH, attivata dall'interrupt solo quando il sensore passa da 0 a 1
    impulso = HIGH;    // diventerà HIGH anche per il digitalWrite del led
else
   Impulso = LOW;     // altrimenti mantiene il led spento
}

È corretto qualcosa del genere? Ho preso spunto da un tutorial su youtube. Tuttavia da qua non riesco a vedere in che modo si possa costruire un contatore ....

Non sono molto ferrato su questi argomenti, provo a spiegarteli come li ho capiti io. Se passa uno di quelli bravi con la teoria sarà sicuramente più preciso di me...

1) volatile è una direttiva per il compilatore, così le variabili che dichiari in questo modo vengono trattate in maniera diversa. Per usare una variabile con gli interrupt devi dichiararla così. Il reference è abbastanza chiaro sull'argomento. Per il tipo (byte) dipende da cosa ti serve, puoi dichiararla anche di un altro tipo.

2) non sono sicuro, ma credo di possa usare millis() con i dovuti accorgimenti... Aspetta quello bravo di prima...

3) puoi fare un contatore con una variabile che viene incrementata dall'interrupt. Il tuo codice dovrebbe (credo) far lampeggiare un led... Ma in realtà non fa niente perchè l'if ha sempre lo stesso esito... In più usi la variabile una volta con la maiuscola e una volta no... Nel tuo caso io vedrei meglio il falling come interrupt. Poi potresti semplicemente dichiarare una variabile int (o long dipende dal massimo valore che ti serve) e incrementarla dentro l'interrupt (un banale variabile++) poi nel loop ti preoccupi di fare calcoli e visualizzarla.

Ps: è evidente che il codice che posti non lo stai provando... Credo sarebbe più istruttivo cominciare a fare delle prove pratiche per vedere l'effetto di quello che ti viene suggerito.

perché si dichiara una variabile di tipo "volatile", e perché per esempio è di tipo "byte"?

Si dichiara una variabile di tipo “volatile” quando essa è usata SIA all'interno di un Interrupt che fuori. Non serve se detta variabile presente in uno solo dei due casi

Di tipo “byte” perchè si vede che nell'esempio che hai visto il suo valore poteva essere al massimo di 255. byte = 8bit = Valori da 0 a 255 int = 16bit = Valori da -32768 a 32767 unsigned int = 16bit = Valori da 0 a 65535 E' chiaro che meno bit vengono usati, minore ' lo spazio che occuperanno in memoria

Mi è chiaro il motivo per cui le funzioni delay () e millis () non vanno d'accordo con l'interrupt....

Non mi pare che ti sia chiaro, infatti dette funzioni non hanno nessun effetto sugli interrupt. L'interrupt è un evento che si scatena indipendentemente da quello che sta eseguendo il Micro ( a meno che tu non dia istruzioni per evitare che ciò accada ) e anche se stai eseguendo un delay() ( con il Micro che sembra bloccato ) gli interrupt vengono eseguiti lo stesso , altrimenti come credi che possa misurare il tempo . Idem nel tuo caso, un interrupt che viene attivato da un evento esterno

In che modo, utilizzando gli interrupt, si arriva a un contatore?

Te l'hanno già detto, rileggiti il post #7, la risposta di Etemenanki

Vuoi far lampeggiare il Led ?

void segnale() {
  if (impulso == LOW)
    impulso = HIGH;
  else
    Impulso = LOW;
}

Vuoi fare un contatore / contagiri ?

void segnale() {
    impulso ++;
 }

 void loop()  {
      impulso = 0 ; // Resetta il valore
      delay(1000);
      Serial.println( impulso * 60 );
    }

Vuoi fare un contagiri misurando il tempo tra due impulsi ?

volatile unsigned long Tempo1 = 0;
volatile unsigned long Tempo2 = 0;

void segnale() {
  Tempo1 = Tempo2;
  Tempo2 = micros();
}

void loop() {
  if (Tempo2 > Tempo1) {
  unsigned long Rpm = 60000000UL / (Tempo2 - Tempo1);  // si applica la formula 60000000/µs
  }
  else 
    Rpm = 0;
  Tempo1 = Tempo2;
  Serial.print( etc etc

Ah ecco, non avevo capito che la costruzione del contatore, come sintassi, è così semplice.. Pensavo fosse più complicato. Ho realizzato il circuito e però mi accorgo che non è molto accurato.. Se con il dito simulo il passaggio dell'ostacolo mi da valori di rpm che vanno a intervalli di 60, e con uno scarto temporale di 1 secondo. Ho provato ad abbassare il delay da 1000 a 500, e ovviamente ho raddoppiato il fattore prodotto da 60 a 120 ma addirittura peggiora la precisione. Per ora la precisione migliore che sono riuscito a ottenere è quella che considera i giri/secondo, con la quale posso sicuramente lavorare per i miei scopi, però mi piacerebbe capire come avere una lettura precisa in giri/minuto, magari con una lettura ogni 200ms

in più non capisco l’ultimo sketch proposto, non ci arrivo

Eppure è semplice, ragiona :

Quando il sensore va in LOW ( e allora è meglio se lo dichiari FALLING e non RISING ) provoca un interrupt e allora viene eseguita la funzione “segnale”, dove c’è

void segnale() {
  Tempo1 = Tempo2;
  Tempo2 = micros();
}

un Tempo1 che diventa uguale a Tempo2. Tempo2 inizialmente è zero
poi, Tempo2 prende il valore che in quel momento ha micros() ( che è come la millis() solo che invece dei millisecondi conteggia i microsecondi ) che appunto indica il tempo trascorso da quando accendi Arduino.

Quando poi il sensore scatta di nuovo, Tempo1 diventa il tempo in cui ha rilevato il passaggio precedente del sensore ( Tempo1 = Tempo2 ) e Tempo2 memorizza il valore attuale della micros()

Nel loop() non fai altro che controllare che Tempo2 sia maggiore di Tempo1 , sottrai il tempo precedente all’attuale (Tempo2 - Tempo1 ) e sai quanto è trascorso dal primo al secondo impulso
formuletta 60 / tempo(in secondi ) che avendo un valore in µS devi moltiplicare per 1.000.000

volatile unsigned long Tempo1 = 0;
volatile unsigned long Tempo2 = 0;
unsigned long Rpm = 0 ;
int sensore = 2;
int led = 13;

void segnale() {
  Tempo1 = Tempo2;
  Tempo2 = micros();
}

void setup () {
  Serial.begin(9600);
  pinMode (2, INPUT);
  pinMode (13, OUTPUT);
  attachInterrupt (digitalPinToInterrupt(2), segnale, FALLING);
}

void loop() {
  if (Tempo2 > Tempo1) {
    Rpm = 60000000UL / (Tempo2 - Tempo1); 
  }
  else
    Rpm = 0;
  Tempo1 = Tempo2;
  if (Rpm != 0 )
  Serial.println( Rpm );
}