Pagine

lunedì 11 luglio 2011

bikeComputer con Arduino

Premessa

Finalmente è arrivata l’estate e con essa il caldo; la voglia di uscire e stare all’aria aperta si fa sentire ogni giorno con maggiore forza e, talvolta, nei pigri pomeriggi cittadini, nelle assolate domeniche fuori porta o sulla passeggiata del lungo mare decidiamo di abbandonare la tanto amata automobile per tornare a mezzi di locomozione più tradizionali: le nostre gambe ma, soprattutto, la bicicletta.


I più curiosi, in queste occasioni, vorrebbero sapere quanti chilometri riescono a percorrere, quale sia la velocità di punta toccata e quante pedalate riescono a fare in un minuto; magari per vantarsi con gli amici :-). E’ per far fronte a queste esigenze che io ed Enkel abbiamo pensato di progettare il bikeComputer con Arduino. In questo articolo vi racconterò come abbiamo fatto.

L'immagine che vedete qui sopra è il prototipo funzionante che abbiamo avvolto nello scotch sul manubrio della bici di Enkel per fare il test "su strada" :-)
Potete trovare tutte le immagini del progetto in questa pagina.


L'idea

L’idea è semplice: tramite arduino si visualizza la velocità di punta e la velocità media su un display 16X2.
Per contare i giri della ruota abbiamo utilizzato un reed switch. Questo sensore si comporta come un interruttore: esso rimane normalmente aperto (e quindi non passa corrente); arduino lo vede come un segnale digitale LOW.
Quando passa un magnete vicino (posizionato sui raggi della ruota), il sensore si chiude facendo passare corrente; in questo modo restituisce un HIGH digitale ad arduino riconosce il passaggio del magnete; il tempo trascorso tra un passaggio e l’altro ci permette di calcolare la velocità istantanea (e successivamente quella media) e, conoscendo il raggio della ruota, si può calcolare il numero di metri percorsi.
Analogo discorso si può fare per contare le pedalate piazzando il magnete sulla corona della bicicletta ed un secondo reed switch sul telaio della bicicletta.
Per ragioni di compattezza abbiamo utilizzato uno schermo LCD 16x2 ma abbiamo meditato l’uso di un 20x4 in modo da vedere tutte le informazioni contemporaneamente.
Ciò che ci interessava molto, però, era anche la possibilità di poter fruire dei dati dopo aver terminato la scampagnata (si pensi ad una gita: vado in un luogo in bici, mangio, gioco a freesbie e riprendo la bici fino a casa dove voglio analizzare le mie performance); per questo motivo abbiamo usato uno shield microSD con memoria da 1Gb.
La memoria ha un duplice scopo: l'utente dovrà inserire un file da denominare config.txt (qualsiasi operativo può creare un file di testo semplice) dove inserisce un numero di 4 cifre che esprime il diametro della ruota della propria bicicletta in mm (serve per il calcolo della distanza percorsa); dal canto suo arduino aggiorna il file log.txt.
Tale file sarà creato ed alimentato da arduino con tante righe quanti sono i passaggi del magnete; ogni riga contiene il numero di millisecondi passati dall’accensione di arduino (tramite la funzione millis()) nel momento in cui si rileva il passaggio del magnete vicino al reed (da questi è poi semplice ricavarsi tutto il resto magari effettuando un grafico con Processing).
L’alimentazione del sistema è stata affidata ad un pacco batterie composto da 4 pile in serie. Conteggiando circa 1.2-1.5V per ciascuna si arriva ad una tensione di alimentazione di 4.8-6V che abbiamo inserito direttamente nel pin Vin per evitare inutili dispersioni di energia.
Per quanto riguarda il “case” ci siamo appoggiati al FabLab di Torino (nelle immagini non è ancora presente ma è "coming soon") grazie all’aiuto di Davide G. ed Enrico C. che ci hanno aiutato con il laser-cutter a progettare un adeguato alloggiamento per il sistema.
Una esigenza con la quale ci siamo scontrati, inoltre, era il desiderio di rendere indipendente il reed sensor e il “case” con arduino dentro: l’idea è quella di arrivare a destinazione (magari davanti un supermarket per fare la spesa) e poter staccare tutto il sistema (per non lasciarlo in balia di occhi indiscreti e mani disoneste); a questo proposito è stata ingegnosa l’intuizione di Enkel che ha pensato di riutilizzare delle vecchie cuffie stereo per walkman: abbiamo tagliato gli auricolari dai quali prelevare due fili (massa e segnale); dall’altra estremità il jack da 3.5mm era già pronto per essere inserito durante l’utilizzo ed estratto a destinazione.

Schema

Seguono lo schema di connessione sulla breadboard e lo schema elettrico. Entrambi realizzati con fritzing.
Schema sulla breadboard:
Schema elettrico:

Codice

Di seguito il codice utilizzato
/*
////////////////////////////////////////////////

[ Arduino Cycle Computer ] Version A0.2

>> Original written by Adam O'Hern
>> Modified by Alexdlp for Instructables 2011
>> Modified by Enkel Bici
>> and Vittorio Zuccala' for Wired.it 2011

////////////////////////////////////////////////
*/

#include <LiquidCrystal.h>
//#include <LcdBarGraph.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd( 7,6, 5, 4, 3, 2);

#include <SD.h>
File myFile;


//byte lcdNumCols = 15; // -- number of columns in the LCD
//LcdBarGraph lbg(&lcd, lcdNumCols); // -- creating

int LED = 13; //pin for the LED
int SensorPin = 15; //input for REED SensorPin (analog 0 as digital)
int rim = 2035; //circumference of wheel in mm (28" rim)

int val = 0; // used to store input value
int previousVal = 0; // lets not be too repetitious

int debounce = 10; // the debounce time, increase if the output flickers
int cycles = 1; // total number of revolutions
float currentSpeed = 0; // current speed in MPH
float averageSpeed = 0; // average speed since "newRide" was true

unsigned long revTimer; // create a timer that tells us how long we go between pulses,
unsigned long serialTimer; // and one for how long it's been since we sent anything over serial
unsigned long rideTimer; // total time since "newRide" was true

boolean activeRiding = false; // is the bike moving?
boolean activityChange = true; // just a way of keeping track of how long we're inactive
long inactivityTimer; // millis() on which we began inactivity
long activeRidingOffset = 0; // time subtracted from total riding time when calculating average speed
boolean newRide = true; // true if we haven't moved in half an hour or more
float rideDistance = 0; // total distance traveled since "newRide" was true

void setup() {
lcd.begin(16, 2);
pinMode(LED, OUTPUT); // tell arduino LED port is an output,
pinMode(SensorPin, INPUT); // and SensorPin port is input

lcd.setCursor(0,0);
lcd.print("Init SD...");
lcd.setCursor(0,1);
if (!SD.begin(8)) {
lcd.print("Init failed!");
delay(20000);
return;
}
else{
delay(2000);
lcd.print("Init OK!!");
delay(10000);
lcd.clear();
myFile = SD.open("setup.txt");
if (myFile) {
lcd.setCursor(0,0);
lcd.print("wheel:");
lcd.setCursor(0,1);
char c[5];
int count=0;
// read from the file until there's nothing else in it:
while (myFile.available()) {
c[count]=myFile.read();
count++;
}
// close the file:
myFile.close();
delay(2000);
rim=0; //reset the wheel diameter
for(count=0;count<4;count++){ //it supposes the diameter is expressed in mm and composed of 4 characters written in a text file on the sd card
rim=rim+(c[count]-48)*pow(10,(3-count)); //each character reappresents a number. 0 is equal 48 in ascii code. From then on you add 1 to get the next numbers.
}
rim=rim+1; //magic number
lcd.print(rim);
delay(2000);
}
else {
// if the file didn't open, print an error:
lcd.print("error reading...");
delay(20000);
}
}

Serial.begin(9600); // start a serial session
revTimer = millis(); // start pulse timer
serialTimer = millis(); // start serial timer
rideTimer = millis(); // start ride timer
}

void loop(){

if(!activeRiding) {
if(activityChange) {
inactivityTimer = millis();
activityChange = false;
}
}
else {
if(activityChange) {
activeRidingOffset += millis() - inactivityTimer;
activityChange = false;
}
}


val = digitalRead(SensorPin);
if (val==LOW) { // check whether input is LOW (magnet is NOT in range of reed SensorPin)
digitalWrite(LED, LOW); // turn LED off
previousVal = LOW; // allow the next "pulse" to be counted
}
else{
digitalWrite(LED, HIGH); // turn LED off
lcd.setCursor(1, 0); // Set cursor to the top right of LCD
lcd.print("*"); // Print a blank character or 'space'
//delay(100); // debounce timer
lcd.setCursor(1, 0); // Set cursor to the top right of LCD
lcd.print(" "); // Print a blank character or 'space'
if (previousVal != HIGH && millis() - revTimer > debounce) { // we've got a pulse!
pulse();
}
previousVal = HIGH; // (in case the magnet is in range of the SensorPin while sitting still)
}

// if it's been too long since the last pulse, assume we're not moving.
if(millis()-revTimer > 2000) {
currentSpeed = 0;
sendStats();
if(activeRiding) {
activityChange = true;
activeRiding = false;
}
}

// if it's been more than fifteen minutes...
if (millis() - revTimer > 15*60*1000) {
// we'll assume it's a new riding session & zero everything out at next pulse.
newRide = true;
}
}


void pulse() {
if(newRide) {
lcd.clear();
cycles = 0;
averageSpeed = 0;
rideTimer = millis();
rideDistance = 0;

//log data to file
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("log.txt", FILE_WRITE);

// if the file is available, write to it:
if (dataFile) {
dataFile.print("newride");
dataFile.println(",");
dataFile.close();
// print to the serial port too:
//Serial.println("newride");
}
// if the file isn't open, pop up an error:
else {
lcd.setCursor(1, 0);
lcd.println("can't write");
}
}

cycles++; // The wheel has obviously turned another revolution

rideDistance = (float) rim*cycles/1000; // distance in meters,
//rideDistance = rideDistance * 0.000621371192; // converted to miles

currentSpeed = (float) (millis() - revTimer)*0.001; // Convert time elapsed to milliseconds to seconds
currentSpeed = rim/currentSpeed; // S = d/t: Rim circumference divided by time elapsed
//currentSpeed = currentSpeed*0.002237; // MPH Conversion: 1 mm/s = 0.001 m/s * 3600 s/hr * 1 mile / 1609 m = 0.002237
i/hr
currentSpeed = currentSpeed*0.0036; // 1Km/hr Conversion: 1 mm/s = 0.001 m/s * 3600 s/hr * 1Km/1000m = 0,0036 Km/hr

// time ridden since "newRide", in hours, not including inactive time
unsigned long activeRidingMillis = millis() - rideTimer - activeRidingOffset;
//float activeRidingHours = (float) activeRidingMillis/1000/60/60; // convert to hours
float activeRidingSeconds = (float) activeRidingMillis*0.001; // convert to hours
averageSpeed = rideDistance / activeRidingSeconds;
averageSpeed = averageSpeed*3,6;

revTimer = millis(); // remember the current moment for speed calculations next time around
sendStats(); // Tell Processing what's going on
newRide = false;



if(!activeRiding) { //if you start from standin still
activityChange = true;
activeRiding = true;
}

//log data to file
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("log.txt", FILE_WRITE);

// if the file is available, write to it:
if (dataFile) {
dataFile.print(revTimer);
dataFile.println(",");
dataFile.close();
// print to the serial port too:
Serial.println(revTimer);
}
// if the file isn't open, pop up an error:
else {
lcd.setCursor(1, 0);
lcd.println("can't write");
}
}

void sendStats() {
/*
Serial.print("rideDistance=");
Serial.print(rideDistance,2);
Serial.print("&currentSpeed=");
Serial.print(currentSpeed,1);
*/
lcd.setCursor(0, 1);
lcd.print("Speed = ");
lcd.setCursor(9, 1);
lcd.print(currentSpeed,1);
lcd.setCursor(13, 1);
lcd.print("kmh");

//lbg.drawValue(currentSpeed, 35);
/*
Serial.print("&averageSpeed=");
Serial.print(averageSpeed,2);

// send a linefeed character, telling Processing that we're done transmitting.
Serial.print(10,BYTE);
*/
serialTimer = millis();
}

22 commenti:

  1. molto interessante questo progetto. volevo segnalarti un progetto che riassume in pieno l’essenza di Facebook e la porta nel mondo reale grazie ad Arduino: si tratta di un Facebook Notifier, praticamente il notificatore di messaggi Facebook inversione hardware, reale, che comunica con il PC tramite collegamento USB. La presenza di notifiche si segnala con la bandiera gialla alzata, che si abbassa quando queste vengono lette.
    http://it.emcelettronica.com/arduino-sbarca-su-facebook

    RispondiElimina
  2. Ciao, Puoi fare un post con la lista dei componenti (e relativi costi) utilizzata?
    Sinceramente non saprei dove comprare un Reed switch.

    Ciao e grazie

    RispondiElimina
  3. Ciao, per quanto riguarda la componentistica abbiamo usato: oltre a fili vari, un arduino, uno schermo LCD ed uno shield SD (con una SD da 1Gb non SHDC): tutti questi componenti puoi prenderli dallo store di arduino.
    Per il reed switch puoi comprarlo in qualche negozio fornito di elettronica; noi lo abbiamo ordinato da Pinto a Torino facendolo arrivare da Farnell. Il modello che abbiamo preso è questo: http://is.gd/qJapKN
    Spero di averti risposto in maniera esaustiva.

    RispondiElimina
  4. Grazie per la risposta, un ultima curiosità: il magnete è al neodimio? L'avete incollato ai raggi della bici?

    RispondiElimina
  5. Ciao 'Anonimo' non so cosa sia il magnete al neodimio ad ogni modo abbiamo preso un magnete di quelli che si attaccano ai frigo e che compri in vacanza per intenderci e lo abbiamo fissato con un po' di scotch (anti estetico ma funzionale) e una fascetta da elettricista... :)

    RispondiElimina
  6. Visto che i negozi di componenti elettronici scarseggiano, segnalo che un interruttore reed si trova anche nei sensori di allarme per le finestre, quei "cosetti" che si attaccano col baidesivo alle serrande e ne segnalano l'apertura alle centraline di allarme. Costano dai 2 ai 20 euro, secondo se li comprate dai cinesi o dal ferramenta....

    RispondiElimina
  7. jumpjack grazie per la segnalazione.
    Non ci avevo pensato ma ottimo spunto!

    RispondiElimina
  8. ciao Vittorio,
    complimenti per il lavoro.
    Sono una vecchio frequentatore del forum di Arduino. Vorrei sapere cosa fare per scrivere un post sul blog ufficiale di Arduino sul progetto che ho realizzato insieme ad un collega.
    Hai la mail di qualche redattore?

    Se vuoi dare un occhio www.floweather.com

    RispondiElimina
  9. Ciao Stefano, ti ringrazio.
    Hai provato a sentire direttamente Massimo Banzi?
    Prova a sentire anche l'Administrator (magari con un messaggio privato) del Forum. So che si occupa anche di alcune "relazioni pubbliche" e forse può darti una mano.

    RispondiElimina
  10. E da un po' che leggo il tuo blog e lo trovo davvero molto interessante e pieno di spunti, volevo chiederti un consiglio su un problema con arduino :
    possiedo un portatile della HP Pavilion dv6 fin dalla prima volta che ho installato l'IDE di arduino qunando lo apro dopo alcuni minuti mi esce LAUNCH4j AN ERROR OCCURRED WHILE STARTING THE APPLICATION ma poi si apre lo sketch normalmente e tutto funziona l'unica cosa e che le scritte spesso si sfuocano ma basta scorrere un po' e tutto torna normale uso l'utima versione di arduino la 22 e su internet non c'e niente in proposito o io non l'ho trovato, spero che tu ci capisca qualcosa perche' e' un po' fastidioso ps. sul portatile di mia figlia non ho il minimo problema. Grazie

    RispondiElimina
    Risposte
    1. sotituisci il file

      C:\Program Files (x86)\arduino-1.0\lib\preference.txt
      con quello ceh trovi in :
      Documents and Settings -> [username] -> Application Data ->ARDUINO/Processing -> preferences.txt (on Windows XP)
      ed hai risolto ;)
      ;)
      fL

      Elimina
  11. Ciao Roberto e grazie x i complimenti.
    In questo momento sono in ferie e non mi riesce di fare molte ricerche dal cellulare ma sembra sia un problema di macchina virtuale in Java legato al sistema operativo.
    Utilizzi xp per caso?
    Hai provato a lanciare il .jar anziché il .exe?
    Sembra che risolva il problema. Prova anche a scaricare nuovamente l'IDE perché potrebbe essere un problema di file corrotto.
    Facci sapere ok? :)

    RispondiElimina
    Risposte
    1. ciao Vittorio in questi mesi non sono riuscito a risolvere il problema
      io uso vista , cosa intendi per lanciare il jar anzichè exe ?
      ho aggiornato piu volte java e di arduino ho tutte le versioni dalla 22 alla 1 ma tutte hanno lo stesso problema se puoi aiutami prima che prenda a calci questo pc grazie

      Elimina
  12. ciao vittorio ho necessità di una tua consulenza per un progetto che mi sono impuntato a fare.
    Vorrei istallare delle fotocellule in una pista di BMX per una rilevazione cronometrica, ho acquistato ARDUINO UNO, le fotocellule, ho scaricato un programma free come cronometro (che lavora in excel).
    Ora mi trovo a non sapere come incominciare e come programmare l'arduino e dove collegare le fotocellule (sapendo che ho solo il contatto pulito C-NC/NO, ti ringrazio FabioG

    RispondiElimina
  13. Ciao, Fabio, linkami le fotocellule ed il programma free sull'uso del cronometro e vediamo cosa si può fare :-)
    Ad occhio non sembra difficilissimo ma bisogna capire come collegare le fotocellule ad arduino e come farle lavorare...

    RispondiElimina
  14. Ciao, ti ringrazio per la tua disponibilità, di seguito ti riiporto i vari programmi e marche emodello delle foto, se mi dai una tua mail ti giro tutti i file non molto grandi.
    il prog per cronometro è "xntimer", mentre le fotocellule sono le " sick sensick W280" alimentazione 24/240v ( per praticità verranno alimentate a 230V.
    per ogni tua altra richiesta puoi conattarmi a cliffhanger27@libero.it

    RispondiElimina
  15. Voglio spiegarti per esteso qual'è la mia idea per creare la rilevazione cronometrica nella pista di BMX.
    Vorrei mettere un sensore meccanico collegato al cancello di partenza, che dia il consenso di "start", a 4 mt una fotocel per intertempo ( per misurare il tempo di reazione) e poi una foto all'arrivo.
    Ora vorrei prima crearlo a casa per tetstarlo e capire se Arduino a bisogno di un impulso o del contatto continuo. Siccome lo si vuole utilizzare in gara (quindi con max 8 corridori)per misurare il più veloce, vorrei capire se ad ogni transito sulla foto l'Arduino lo segnala come itertempo o va in tilt.
    Sono un pò contorto!!!! grazie ancora FG

    RispondiElimina
  16. Ciao Vittorio, sto costruendo il cycle computer con arduino, ma vorrei misurare i singoli metri o giri della ruota. Dove devo cambiare il comando nel codice? Grazie.
    Tiberio

    RispondiElimina
    Risposte
    1. Ciao Tiberio devi effettuare il calcolo dove si calcola currentspeed...

      Elimina
    2. Ciao molto bello questo progetto complimenti.... volevo chiederti con questo sistema si può utilizzare per contare dei giri di un modellino a scoppio?

      Elimina
  17. Certamente anche se forse per dimensioni meglio usare un Arduino mini

    RispondiElimina