Problema ContaRpm ContaKm

Salve,
sono nuovo da queste parti! Ho da poco costruito un Contagiri e Contakm/h usando una scheda arduino uno e due sensori costruiti da un led IR e un ricevitore IR.
Dato che sono un novellino ho preso spunto da vari codici trovati in giro per la rete in particolare uno sketch adatto a creare un contagiri.
Solo il contagiri funzionava bene… Oggi pero mi sono deciso a “clonare” la funzione di conteggio dei giri al minuto in modo da poter calcolare la velocità della ruota e quindi i km/h.
Il problema adesso è che conta soltanto gli rpm e non precisi…
I sensori sono collegati rispettivamente ai pin 2-13 e 3-6.
Di seguito il codice, grazie a chi sarà disponibile da dare un’occhiata…

int ledPin = 13;                
volatile byte rpmcount;
unsigned int rpm;
unsigned long timeold;

int ledPin2 = 6;
volatile byte rpmcount_tyre;
unsigned int rpm_tyre;
unsigned long timeold_tyre;
unsigned long kmh;


#include <LiquidCrystal.h>

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

void rpm_fun()
{
 
     rpmcount++; 
}

void rpm_tyre_func ()
{
rpmcount_tyre++; 
}

void setup()
{
  lcd.begin(16, 2);  
  attachInterrupt(0, rpm_fun, FALLING);

attachInterrupt(1,rpm_tyre_func, FALLING);

  
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);

pinMode(ledPin2, OUTPUT); 
digitalWrite(ledPin2, HIGH);



  rpmcount = 0;
  rpm = 0;
  timeold = 0;
  
  rpmcount_tyre=0;
  rpm_tyre = 0;
  timeold_tyre = 0;

}
void loop()
{
  
  delay(1000);
  
  detachInterrupt(0);
  detachInterrupt (1) ;
  
  rpm = 60*1000/(millis() - timeold)*rpmcount;
  timeold = millis();
  rpmcount = 0;


rpm_tyre= 60*1000/(millis() - timeold_tyre)*rpmcount_tyre;
timeold_tyre = millis();
rpmcount_tyre = 0;

kmh = ((rpm_tyre*1,307)/1000000)*60;







  lcd.clear();
 
  lcd.setCursor(0,0);
  lcd.print("RPM:");
  lcd.print(rpm);
  lcd.setCursor(9,0);
  lcd.print("KMH:");
  lcd.print(kmh);

  
  attachInterrupt(0, rpm_fun, FALLING);
  attachInterrupt(1, rpm_tyre_func, FALLING); 
  

 }

lo sketch va fra i tag

Grazie, sistemato! :blush:

perché hai fatto i
volatile byte rpmcount; byte??

Dove hai la circonferenza della ruota?

Ciao Uwe

Mi sembra quasi brutto rispondere in questo modo ma rpm_count l'ho dichiarato byte perchè sullo sketch trovato sul web era dichiarato cosi e il solo conta rpm funzionava ... :slightly_frowning_face:

La circonferenza della ruota (1,307 mt) è espressa direttamente nella riga in cui calcolo kmh.

Prova così:

#define ledPin   13
#define ledPin2  6

#include <LiquidCrystal.h>

volatile unsigned int rpmcount = 0;
unsigned long rpm = 0;
unsigned long timeold = 0;

volatile unsigned int rpmcount_tyre = 0;
unsigned long rpm_tyre = 0;
unsigned long timeold_tyre = 0;

unsigned long kmh = 0;

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

void rpm_fun() {
  rpmcount ++; 
}

void rpm_tyre_func() {
  rpmcount_tyre ++; 
}

void setup() {

  pinMode(ledPin, OUTPUT);
  pinMode(ledPin2, OUTPUT); 

  digitalWrite(ledPin, HIGH);
  digitalWrite(ledPin2, HIGH);

  lcd.begin(16, 2);  

  attachInterrupt(0, rpm_fun, FALLING);
  attachInterrupt(1, rpm_tyre_func, FALLING);
}

void loop() {

  rpm = 60 * 1000 / (millis() - timeold) * rpmcount;
  timeold = millis();
  rpmcount = 0;

  rpm_tyre = 60 * 1000 / (millis() - timeold_tyre) * rpmcount_tyre;
  timeold_tyre = millis();
  rpmcount_tyre = 0;

  kmh = ((rpm_tyre * 1,307) / 1000000) * 60;

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("RPM:");
  lcd.print(rpm);
  lcd.setCursor(9,0);
  lcd.print("KMH:");
  lcd.print(kmh);

  delay(1000);
}

Con questo sketch rimane tutto sullo 0...

ciao prova a mettere anche queste due istruzioni

  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);

inoltre non ti servono due sensori quando hai i RPM calcoli la velocità noto il diametro ruota

stefa24: inoltre non ti servono due sensori quando hai i RPM calcoli la velocità noto il diametro ruota

Solo se sai che marcia stai usando e i rapporti delle stesse. Su tutti i veicoli il contakm è un sensore posto dopo il cambio mentre gli rpm sono presi prima del cambio.

Il calcolo della velocità lo cambierei eliminando la virgola

kmh = ((rpm_tyre * 1307UL * 60UL) / 1000000000UL);

Comunque secondo me c'è qualche errore nella conversione da metri a chilometri orari. Forse una divisione per mille di troppo.

kmh = ((rpm_tyre * 1307UL * 60UL) / 1000000UL);

astrobeed: Solo se sai che marcia stai usando e i rapporti delle stesse. Su tutti i veicoli il contakm è un sensore posto dopo il cambio mentre gli rpm sono presi prima del cambio.

si giusto, pensavo a una cosa semplice

Benissimo! :) intanto grazie mille a tutti In questi giorni vedo di cablare per bene i due sensori in modo tale da poter testare comodamente il tutto e cerco di fare delle prove con i vostri consigli...

Ciao, premetto che mi sono un pò arrugginito causa lungo periodo di abbandono dei miei progetti Arduino, ma visto che stavo lavorando sulla possibilità di creare una centralina iniezione con arduino, ho ben lavorato sul calcolo dei giri motore, anzi, nel mio caso, deve contare gli impulsi di una ruota fonica e da simulatore ed oscilloscopio, mi funzionava alla grande, io per il mio progetto ho fatto:

Giri = ((1000000UL / Tooth_Period)/Numero_Denti)*60 ;

nel mio caso:

-uso i microsecondi con la funzione micros(). -uso il Input Capture del Timer1, ma ho fatto anche una prima versione con attachinterrupt che funzionava bene comunque. -misuro il tempo trascorso tra due "eventi", nel tuo caso quanti sono? uno a giro? nel mio caso sono 58 a giro (ruota fonica da 60 denti meno 2 per essere preciso).

Quindi, cosa fa il mio calcolo? Dividi 1 secondo in microsecondi (appunto 1000000 microsecondi) per il periodo tra eventi, nel mio caso tra due denti, e quindi, considerando che la mia ruota fonica ha 60 denti di cui due mancanti, questo "tooth_Period" è solo 1/60 di giro e quindi devo trasformarlo ad una circonferenza completa, per poi, moltiplicare per 60 per ottenere i giri al "minuto" e non al "secondo".

Nel tuo caso, se hai un singolo impulso a giro, potresti fare semplicemente:

Giri = ( (1000000UL / Tooth_Period) * 60 );

Ovviamente, devi sostituire i miei nomi variabili ai tuoi.

Secondo me, l'uso di "rpmcount++" è superfluo, gi fa incrementare un contatore che poi viene azzerato dopo il calcolo rpm. Unico vantaggio di questo è che all'interno della funzione non vi sono altri calcoli e quindi la ISR è veloce da eseguire.

Per il calcolo della velocità, dovresti avere i dati di demoltiplicazione del cambio/trasmissione dal motore alla(e) ruota(e), solo se la ruota fosse saldamente legata al motori senza nessuna demoltiplicazione, potresti usare il metodo da te indicato. Altrimenti, se hai più di un rapporto marcia, devi misurare la velocità alla ruota tramite il secondo attachinterrupt.

eliminare i float è sempre una buona idea per velocizzare i calcoli, io ne so qualcosa!!

LucaLeo ha sviluppato una centralina per motori marini --> http://forum.arduino.cc/index.php?topic=157511.0 Forse c'era anche un altro topic sull'argomento.

uwefed: perché hai fatto i volatile byte rpmcount; byte??

Le variabili usate sia all'interno di ISR che in altre parti del codice vanno dichiarate di tipo "volatile" in modo che il compilatore non ottimizzi il codice che ne fa uso, causando quindi l'alterazione dei valori.

Si tratta di un banale sketch che simula la funzione di frequenzimetro.

Visto che un contagiri e un contakm sono di fatto dei frequenzimetri, vedi se la mia soluzione ti può essere di aiuto. Ovvio ci sarà da modificare la base dei tempi per adottarlo alle varie funzioni.

#define pinInput 2                         // pin input impulsi
#define Interrupt 0                        // interrupt associato

volatile word impulsi = 0;                 // contatore impulsi
word impulsiOld =0;
word freq =0;                              // valore frequenza

const unsigned long periodo = 1000;        // costante unità di tempo = 1" ritardo
unsigned long millisOld;                   // variabile supporto per calcolo periodo 

  
// -----------------------------------------------------------------------------------------------

void setup() {

  pinMode (pinInput,INPUT_PULLUP);           // imposta porta ingresso (opzionale PULLUP)
  Serial.begin(57600);                       // attiva comunicazione
   
  attachInterrupt(Interrupt,somma,RISING);   // iniziailizza interrupt  
  impulsiOld = impulsi;                     // allinea variabile
  
  millisOld = millis();                     // inizializza timer
 }


void somma() {
  impulsi ++;                                            // incrementa contatore
}


void loop() {
  if ((millis() - millisOld) >= periodo){                 // trascorso 1"
    millisOld += periodo;                                 // aggiorna variabile
    freq = impulsi - impulsiOld;                          // calcola frequenza
    impulsiOld = impulsi;                                 // allinea variabile

    Serial.println (freq);
  }//if
}//void loop

lelebum: Si tratta di un banale sketch che simula la funzione di frequenzimetro.

Visto che un contagiri e un contakm sono di fatto dei frequenzimetri, vedi se la mia soluzione ti può essere di aiuto. Ovvio ci sarà da modificare la base dei tempi per adottarlo alle varie funzioni.

#define pinInput 2                         // pin input impulsi
#define Interrupt 0                        // interrupt associato

volatile word impulsi = 0;                // contatore impulsi word impulsiOld =0; word freq =0;                              // valore frequenza

const unsigned long periodo = 1000;        // costante unità di tempo = 1" ritardo unsigned long millisOld;                  // variabile supporto per calcolo periodo

  // -----------------------------------------------------------------------------------------------

void setup() {

  pinMode (pinInput,INPUT_PULLUP);          // imposta porta ingresso (opzionale PULLUP)   Serial.begin(57600);                      // attiva comunicazione  
  attachInterrupt(Interrupt,somma,RISING);  // iniziailizza interrupt    impulsiOld = impulsi;                    // allinea variabile     millisOld = millis();                    // inizializza timer }

void somma() {   impulsi ++;                                            // incrementa contatore }

void loop() {   if ((millis() - millisOld) >= periodo){                // trascorso 1"     millisOld += periodo;                                // aggiorna variabile     freq = impulsi - impulsiOld;                          // calcola frequenza     impulsiOld = impulsi;                                // allinea variabile

    Serial.println (freq);   }//if }//void loop

Anche questo metodo è buono, ha come unica pecca (secondo la mia umile opinione ed esperienza) che il risultato di aggiorna ogni 1000 ms ovvero ogni secondo. Per molte applicazioni andrebbe anche bene, ma per altre no. Esempio, motore lento, giri a minimo e cambio in folle, rapida accellerata, i giri salgono più o meno velocemente, il nostro conteggio ogni secondo ci da un valore abbastanza attendibile ma comunque sballato, molto indietro con i veri giri. Stessa operazione, ma con un motore moto, di quelle super sportive, in un secondo, a folle, questi motori possono passare da 1000 rpm a 12000 rpm! Sono davvero tanti per avere un'aggiornamento ad ogni secondo.

Meglio quindi, ridurre il tempo di campionamento a 100 ms, si renderebbe più preciso di 10 volte circa, e sarebbe più veritiero per tante applicazioni.

Ma se invece si vuole un' aggiornamento istantaneo, del tipo utilizzato dalle centraline motore per il conteggio ruota fonica (e quindi della posizione angolare del motore), il metodo indicato nel mio sketch di esempio è da preferire, consentirebbe aggiornamenti "quasi in real time" (tempi di calcolo e invio a display a parte).

Anche se sono molti mesi che ho messo in "naftalina" il mio progetto di ECU motore, posso assicurare che la UNO è in grado di "leggere" correttamente una ruota fonica da 60-2 denti fino ad almeno 6000 rpm senza il minimo problema con comunicazione seriale a serialmonitor dell'IDE. Ho ottenuto risultati anche migliori, ma con diversi stratagemmi per ottimizzare i tempi di esecuzione come usare molto codice C puro per esempio, è sono soddisfatto di quello che sono riuscito a fare con la UNO, niente male per essere una "entry level" (ma dopotutto, le prime ECU erano tutte a 8 bit, avevano pochi kb di RAM ed una frequenza di clock di 8 MHz al massimo, come per esempio la IAW della mitica Lancia Delta!!)

Nella mia soluzione ho cercato di ottimizzare il codice al massimo, e la scelta di una base dei tempi di 1" è puramente formale, giusto per fare delle verifiche strumentali.

Sono arrivato a leggere frequenza in modo molto preciso fino a 65kHz. e non escludo che utilizzando per "impulsi" una variabile Long si possa aumentare le prestazioni.

L'importante non utilizzare il comando delay() che ruba risorse alla CPU per l'esecuzione di altre funzioni.

Una ruota fonica (58 denti) a 6000rpm genera una frequenza 5800hz (più o meno simmetrica). Se al mio sketch assegni "periodo = 100" ottieni un aggiornamento ogni 100mS per valori che variano tra 0-580, che io definirei accettabile

@Lelebum, hai ragione, scusami se il mio post è stato mal esposto, non intendevo assolutamente sminuire o altro il tuo approccio, semplicemente ho esposto il metodo da me utilizzato per il mio progetto che è ripreso pari pari da come viene fatto sulle ECU delle auto. Ovviamente avevo visto che avevi previsto la possibilità di modificare il periodo di "sampling" quindi è sufficiente abbassare il tempo di campionamento per migliorare la risposta e/o aumentare la precisione.

Credo che per un'uso come contagiri, usare un periodo di campionamento di 100ms sia più che sufficiente, all'occorenza, se dovesse essere troppo lento, potrebbe mettere 50ms in modo da avere un'aggiornamento di 20 Hz quindi al limite quasi della persistenza visiva dell'occhio umano, resta da verificare se al display LCD va bene questo refresh rate.