Hulp bij het maken van een snelheidsmeter, kilometerteller.

Hallo forumers,

Een poosje geleden ben ik begonnen met de Arduino UNO starterset te experimenteren.
Na het doorworstelen van een aantal experimenten vond ik dat het wel aardig zou zijn om iets te maken wat voor mij wel handig is. Hieruit is het snelheidsmeter idee ontstaan.
Deze meter gaat als het gaat lukken gemonteerd worden op een kart.
Als het allemaal mogelijk is zou ik als uitbreiding in de toekomst ook graag de koelwater temperatuur, rondetijd en de omwentelingen van de tweetakt motor meten…
Maar eerst maar eens de snelheidsmeter met kilometerteller en dit levert al genoeg problemen voor mij op…
Het idee is om met een HALL effect sensor en een magneet ergens op draaiend onderdeel van mijn kart een puls op te wekken. Deze pulsen dan tellen en aan de hand van de wielomtrek de snelheid en kilometers uit te rekenen.
De HALL sensor heb ik nog niet, dus heb ik in plaats daarvan een drukknopje gebruikt om toch wat te kunnen proberen.

De volgende code heb ik met behulp van diverse voorbeelden in elkaar gezet.
Achter de code heb ik in NL geschreven wat ik begrepen heb van wat welke functie doet.

// Opent de bibliotheek voor de display.
#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,7,6,5,4);      // was 12,11,5,4,3,2
  
int snelheid=0;
int maxsnelheid=0;
int puls=0;
int rpm=0;
unsigned long lastmillis=0;           // unsigned long kan geen negatieve getallen bevatten.
long wielomtrek=0;                    // long is een variabele waar grote getallen in kunnen ook negatieve getallen.
long afstand=0;
long maxafstand=0;


void setup(){                         // Start het setup gedeelte, wordt slechts 1 x gelezen met het aanzetten.
//Serial.begin(9600);                   // Opent een seriële verbindingspoort.
lcd.begin(16, 2);                     // Stelt de LCD in 16 kolommen en 2 rijen.
attachInterrupt(0, rpm_as, FALLING);
}                                     // Stopt het setup gedeelte.


void loop(){                              // Start het loop gedeelte.
if (millis() - lastmillis == 1000){       // Update every one second, this will be equal to reading frecuency (Hz).
detachInterrupt(0);                       // Disable interrupt when calculating.
       rpm = puls * 60;                   // Zet frequentie om naar RPM,dit werkt voor 1 puls per rotatie.
                                          // Voor 2 pulsen per rotatie doe puls * 30.
wielomtrek=(1000);                        // Vul de variabele "wielomtrek" met een waarde in mm.
snelheid=((wielomtrek*rpm*60)/(1000000)); // Reken de snelheid uit.
afstand=(wielomtrek*puls);                // Reken de afgelegde afstand uit.
maxafstand=(afstand+maxafstand);



//Hieronder wordt de hoogste waarde van de variabele "maxsnelheid" onthouden.
if(snelheid > maxsnelheid)maxsnelheid = snelheid;
if(snelheid < maxsnelheid)maxsnelheid = maxsnelheid;

lcd.clear();                   // Maak de display leeg.
lcd.setCursor(4, 0);           // Zet de cursor op de gewenste plaats, kolom4(5) en rij1(0).
lcd.print("Km/h,    Rpm");     // Schrijf de tekst tussen de "".
lcd.setCursor(0, 0);
lcd.print(snelheid);           // Schrijf de variabele "snelheid".
lcd.setCursor(10, 0);
lcd.print(rpm);        

lcd.setCursor(0, 1);
lcd.print ("Snelste     Km/h");
lcd.setCursor(8, 1); 
lcd.print(maxsnelheid);
/*

    Serial.print("RPM = ");          // print het woord "RPM".
    Serial.print(rpm);               // print de rpm waarde.
    Serial.println();
    Serial.print("Hz = ");           // print het woord "Hz".
    Serial.print(puls);              // print omwentelingen per seconde of Hz.
    Serial.println();
    Serial.print("Snelheid = ");
    Serial.print(snelheid);
    Serial.println();                // print een nieuwe lege () regel.
    Serial.print("Maxsnelheid = ");
    Serial.print(maxsnelheid);
    Serial.println();
    Serial.print("Afstand = ");
    Serial.println(maxafstand/1000000);
    Serial.println();
    Serial.println();
*/
 puls = 0;               // Herstart de RPM teller, zet op 0.
 lastmillis = millis();  // Update de variabele "lastmillis".
 attachInterrupt(0, rpm_as, FALLING); //Enable interrupt.
  }
 }
 //Deze code wordt gestart iedere keer als de interupt 0 (pin2) laag wordt.
 
void rpm_as(){
puls++;
//afstand=0;

}

Deze code lijkt redelijk te werken alleen is het maar voor even… de UNO loopt helaas vast, soms doet ie het maar kort en soms wat langer. Heeft iemand een idee waarom dat zo is en waar ik dat dan eventueel in kan zoeken?
Ik dacht eerst dat het zat in de seriële verbindingspoort,dat bleek niet het geval.
Het sluiten en het opnieuw starten van dat seriële scherm doet de UNO wel weer starten.
Als ik Serial.begin(9600); echter d.m.v. // ervoor uitzet dan loopt de UNO na verloop tijd vast en is ie alleen maar met de resetknop aan te zetten.
Ook lijkt het er op dat als er met de drukknop gepulst wordt de UNO later vastloopt dan wanneer er niet gepulst wordt.
De volgende setup heb in nu, zie attachement voor een plaatje. De afbeelding in dit topic toevoegen werd wel heel groot.
Directe link naar de afbeelding is http://nl.tinypic.com/r/2zizbd1/8

Met vriendelijk groet,
Roel

Roel
a) Als je knopjes gebruikt, heb je grote kans dat er een heleboel interrupts worden gegenereert bij 1x indrukken. Dat komt omdat de contacten vaak mechanisch een paar keer stuiteren voordat het contact echt gemaakt is. Dat verstoord je proces.
b) Ik weet nog niet welke HALL sensor je wilt gebruiken, maar ik zou zeker voor zorgen dat je een strakke blokgolf krijgt uit dat ding. Eventueel een transisor die gelijk in de verzadiging gaat of een Schmitt trigger gebruiken.
c) puls wordt zowel in de interrupt routine als het programma gebruikt. Die variabelen moet je altijd als volatile beschrijven. Dus **volatile unsigned int puls;**Ik heb er tevens unsigned van gemaakt. Immers de pulsen zijn altijd positief.
d) Ik zou zelf eenmalig de interupt attachen. Doordat je volatile gebruikt kun je hem gewoon resetten in het programma.
e) Je definieert variabelen als int. Dus max 32768. Volgens mij kunnen sommige tussenresultaten groter worden. Dat gaat fout. Ik zou alle variabelen die in jouw geval worden gebruikt gewoon als unsigned long definiëren.
Zo even een eerste paar indrukken.

Hallo Nico,

Bedankt voor jouw (snelle) antwoord!

Dat van die knopjes had ik idd ergens gelezen, dat neem ik nu even voor lief kan ik in ieder geval wel wat proberen.
Welke HALL sensor ik ga gebruiken weet ik nog niet.
De plaatselijke elctronica onderdelen leverancier heeft er voor mij 1 besteld welke volgens hem wel zou kunnen…
Ik ben daar zelf helemaal niet in thuis.
De rest van jouw opmerkingen heb ik geprobeerd toe te passen.
De volgende code is daaruit voort gekomen.

// Opent de bibliotheek voor de display.
#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,7,6,5,4);      // was 12,11,5,4,3,2
  
unsigned long snelheid=0;
unsigned long maxsnelheid=0;
volatile unsigned int puls=0;         //volatile wordt altijd gebruikt in combi met een interupt, het vertelt de compiler 
                                      // om gegevens direct uit RAM te halen en niet uit een tijdelijk register
unsigned long rpm=0;
unsigned long lastmillis=0;           // unsigned long kan geen negatieve getallen bevatten.
unsigned long wielomtrek=0;          // long is een variabele waar grote getallen in kunnen ook negatieve getallen.
unsigned long afstand=0;
unsigned long maxafstand=0;


void setup(){                         // Start het setup gedeelte, wordt slechts 1 x gelezen met het aanzetten.
//Serial.begin(9600);                 // Opent een seriële verbindingspoort.
lcd.begin(16, 2);                     // Stelt de LCD in 16 kolommen en 2 rijen.
attachInterrupt(0, rpm_as, FALLING);
}                                     // Stopt het setup gedeelte.


void loop(){                              // Start het loop gedeelte.
if (millis() - lastmillis == 1000){       // Update every one second, this will be equal to reading frecuency (Hz).
detachInterrupt(0);                       // Disable interrupt when calculating.
       rpm = puls * 60;                   // Zet frequentie om naar RPM,dit werkt voor 1 puls per rotatie.
                                          // Voor 2 pulsen per rotatie doe puls * 30.
wielomtrek=(1000);                        // Vul de variabele "wielomtrek" met een waarde in mm.
snelheid=((wielomtrek*rpm*60)/(1000000)); // Reken de snelheid uit.
afstand=(wielomtrek*puls);                // Reken de afgelegde afstand uit.
maxafstand=(afstand+maxafstand);



//Hieronder wordt de hoogste waarde van de variabele "maxsnelheid" onthouden.
if(snelheid > maxsnelheid)maxsnelheid = snelheid;
if(snelheid < maxsnelheid)maxsnelheid = maxsnelheid;

lcd.clear();                   // Maak de display leeg.
lcd.setCursor(4, 0);           // Zet de cursor op de gewenste plaats, kolom4(5) en rij1(0).
lcd.print("Km/h,    Rpm");     // Schrijf de tekst tussen de "".
lcd.setCursor(0, 0);
lcd.print(snelheid);           // Schrijf de variabele "snelheid".
lcd.setCursor(10, 0);
lcd.print(rpm);        

lcd.setCursor(0, 1);
lcd.print ("Snelste     Km/h");
lcd.setCursor(8, 1); 
lcd.print(maxsnelheid);
/*

    Serial.print("RPM = ");          // print het woord "RPM".
    Serial.print(rpm);               // print de rpm waarde.
    Serial.println();
    Serial.print("Hz = ");           // print het woord "Hz".
    Serial.print(puls);              // print omwentelingen per seconde of Hz.
    Serial.println();
    Serial.print("Snelheid = ");
    Serial.print(snelheid);
    Serial.println();                // print een nieuwe lege () regel.
    Serial.print("Maxsnelheid = ");
    Serial.print(maxsnelheid);
    Serial.println();
    Serial.print("Afstand = ");
    Serial.println(maxafstand/1000000);
    Serial.println();
    Serial.println();
*/
 puls = 0;               // Herstart de RPM teller, zet op 0.
 lastmillis = millis();  // Update de variabele "lastmillis".

attachInterrupt(0, rpm_as, FALLING); //Enable interrupt.
  }
 }
 //Deze code wordt gestart iedere keer als de interupt 0 (pin2) laag wordt.
 
void rpm_as(){
puls++;
//afstand=0;

}

De attachInterrupt(0, rpm_as, FALLING); heb ik wel twee keer laten staan, haal ik 1 van beide weg dan loopt de UNO gelijk vast.
De UNO loopt nog steeds vast, al lijkt het wel langer te duren voordat ie vast loopt.

Edit a1121lua-t is de HALL sensor welke besteld is

Met vriendelijke groet,
Roel

Die sensor is inderdaad een logic hall. Dus die wordt hog of laag. Je zal er nog wel een weerstandje bij nodig hebben of de interne weerstand van de Arduino kunnen gebruiken.
Ik zal je code vanmiddag wel ff laden. Kijk wel ff.

Dat je mee wilt kijken wordt erg gewaardeerd!
Dat weerstandje waar je over praat, waar heb ik deze bij nodig dan?
Ik had begrepen dat deze sensor werkt met een spanning tussen 3 en 24 volt.

Die sensor heeft een de drain (de data pin) open hangen zoals dat heet. Door die pin aan de voedinsspanning te hangen gaat er stroom lopen en gaat ie dus schakelen. Bij de Arduino kun je opgeven of een pin INPUT, OUTPUT of INPUT_PULLUP is. Bij dat laatste wordt er intern een weerstandje aan de data pin verbonden (intern). En is daarmee het probleem opgelost.

Dit lijkt te werken:

// Opent de bibliotheek voor de display.
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);      // was 12,11,5,4,3,2

#define MEASURE_TIME	1000		// measure every second
#define WIEL_OMTREK		1000		// wiel omtrek in millimeter

unsigned long triggerTijd;		// testen voor elke MEASURE_TIME
unsigned long snelheid = 0;
unsigned long maxsnelheid = 0;
volatile unsigned int puls = 0; //volatile wordt altijd gebruikt in combi met een interupt, het vertelt de compiler
// om gegevens direct uit RAM te halen en niet uit een tijdelijk register
unsigned long rpm = 0L;
unsigned long wielomtrek = 0L; // long is een variabele waar grote getallen in kunnen ook negatieve getallen.
unsigned long afstand = 0L;
unsigned long maxafstand = 0L;
/**
 * @name setup()
 */
void setup() {
	//
	// we have no LCD so do serial
	//
	Serial.begin(115200);                 	// Opent een seriële verbindingspoort.
//	lcd.begin(16, 2);                   // Stelt de LCD in 16 kolommen en 2 rijen.
	attachInterrupt(0, rpm_as, FALLING);	// vallende puls als trigger
	triggerTijd = millis() + MEASURE_TIME;// start de timer
}
/**
 * @name loop()
 */
void loop() {

	if (millis() > triggerTijd) {
		//
		// bereken het aaltal RPM per minuut
		//
		rpm 			= puls * 60;
		snelheid 	= ((WIEL_OMTREK * rpm * 60) / (1000000)); // Reken de snelheid uit.
		afstand 	= (WIEL_OMTREK * puls);            				// Reken de afgelegde afstand uit.
		maxafstand= (afstand + maxafstand);

		//Hieronder wordt de hoogste waarde van de variabele "maxsnelheid" onthouden.
		if (snelheid > maxsnelheid)
			maxsnelheid = snelheid;
		if (snelheid < maxsnelheid)
			maxsnelheid = maxsnelheid;

//		lcd.clear();                   // Maak de display leeg.
//		lcd.setCursor(4, 0); // Zet de cursor op de gewenste plaats, kolom4(5) en rij1(0).
//		lcd.print("Km/h,    Rpm");     // Schrijf de tekst tussen de "".
//		lcd.setCursor(0, 0);
//		lcd.print(snelheid);           // Schrijf de variabele "snelheid".
//		lcd.setCursor(10, 0);
//		lcd.print(rpm);
//
//		lcd.setCursor(0, 1);
//		lcd.print("Snelste     Km/h");
//		lcd.setCursor(8, 1);
//		lcd.print(maxsnelheid);

			Serial.print("RPM = ");          // print het woord "RPM".
			Serial.print(rpm);               // print de rpm waarde.
			Serial.print(" Hz = ");           // print het woord "Hz".
			Serial.print(puls);              // print omwentelingen per seconde of Hz.
			Serial.print(" Snelheid = ");
			Serial.print(snelheid);
			Serial.print(" Maxsnelheid = ");
			Serial.print(maxsnelheid);
			Serial.print(" Afstand = ");
			Serial.println(maxafstand / 1000000);
			//
			// reset de pulsteller
			//
			puls = 0;
			triggerTijd = millis() + MEASURE_TIME;// start de timer
	}
}
/**
 * @name rpm_as
 * wordt aangeroepen bij elke trigger van de hall sensor
 */
void rpm_as() {
	puls++;															// verhoog de teller
}

Je moet alleen ff omzetten naar de LCD. Die heb ik niet. En let wel. De interrupt 0 zit op PIN2.

Klopt, dit werkt!
Maar wat was nou de reden dat de vorige sketch de Arduino liet hangen, de hele mannier van schrijven?
Die pullup weerstand kan die ook tussen +5 en de data pin van de HALL sensor worden gezet?

Ik weet niet of je de correcte pin voor de interrupt hebt gebruikt?
mogelijk dat gedonder met hat detachen en attachen en ik heb de serial op een hogere baudrate gezet.
Als je een digitalWrite (2,HIGH); dan heeft interrupt0 automatisch een weerstand. Anders moet je inderdaad iets van een 4K7 tot 10K weerstand zetten tussen de datapin (pin 2 in dit geval) en Vcc.

Ja ik had idd pin 2 in gebruik.
Ik heb die baudrate weer teruggezet naar 9600 omdat er op mijn seriële scherm alleen maar onzin kwam te staan.
Met de baudrate weer op 9600 was dat weer goed.
Is het slimmer om die baudrate dus weer hoger te zetten, het seriële scherm wordt in de praktijk(op de baan) toch niet gebruikt.

Nou wat je moet weten is dat Serial en interrupts niet echt lekker willen samenwerken. Dus hoe sneller de data weg is, hoe minder kans op problemen je hebt. Overigens kan je de baudrate bij de Arduino in de terminal opgeven (dacht iets van rechts onder op het scherm).

Oke dat zal ik onthouden!
Hartelijk dank voor de medewerking tot zover!!

Ik ben zelf ook al enige tijd bezig met een soortgelijke km-teller, maar het is mij nog steeds niet gelukt.

Om mijn schakeling te testen heb ik op internet heb ik een schema gevonden om een blokgolf te maken met een ic ne555, maar als ik de uitgang aansluit op pin 2 van de arduino en onderstaand schema gebruik knippert de led op pin13 2x zo langszaam. Hoe dat kan is me een raadsel, ik zal wel weer iets fout doen, maar wat.

const int ledPin =  13;

volatile boolean puls = false; //volatile wordt altijd gebruikt in combi met een interupt, het vertelt de compiler

void setup() {
	attachInterrupt(0, rpm_as, RISING);	// vallende puls als trigger
        pinMode(ledPin,OUTPUT);
}

void loop() {
digitalWrite(ledPin,puls);
}

void rpm_as() {
	puls = !puls;															// verhoog de teller
}

klopt als een bus wat je zegt:
pulse in: puls = ! puls stel hij was 0 is ie nu HIGH
pulse in: puls = !puls hij was HIGH dus nu LOW
pulse in: puls = !puls hij was LOW dus nu HIGH
pulse in: puls = !puls hij was HIGH dus nu LOW
etc
Dus een mooie divide by 2 :grin:

:blush: Tuurlijk, ik moet nog veel leren zie ik wel

Ik ben nieuw op dit forum en sinds kort bezig met Arduino te gebruiken.

Ik kwam bovenstaande snelheidsmeter tegen en dat lijkt me erg geschikte toepassing om te gaan gebruiken.
Alvast hartelijk dank voor de info en de gedane oplossingen.

Ik wil bovenstaande snelheidsmeter gaan gebruiken op een hometrainer en dan dit uitbreiden met een instelbare wisselende belasting.
Ik heb een schuifpotmeter van 10 K.ohm op de verstelling van de belasting gemonteerd en bij minimale belasting is de weerstand 2 K.Ohm .bij maximale belasting is deze 8 K.Ohm.
In combinatie met extra weerstand krijg ik waarde uitlezing op de monitor van de arduino minimaal 380 en max 750.

Samen met de snelheid wil ik hier een vermogen aan koppelen, wat er gegenereerd wordt.
Dus bv Snelheid van 20 KM/uur en minimale belasting geeft 25 watt
Snelheid van 20 Km/uur en maximale belasting geeft 150 watt.

En dus ook dit in display laten zien. Dan kun je met meerderen een competitie element hiervan maken.

Ik ben erover aan het denken, hoe ik dit kan realiseren. Wie kan me hiermee op weg helpen.

Hoi Lambert51, welkom.

Ik vraag me af of de conversie die je maakt, lineair loopt met de ingestelde belasting die je uitleest met je potmeter.
Volgens mij word het dan een kwestie van uitrekenen.
De waarde uit een potmeter kun je heel eenvoudig omzetten naar een gewenste waarde middels de map (klik !) functie.
De uitkomst daarvan kun je dan weer gebruiken in een formule om tot het weer te geven resultaat te komen.

Hartelijk dank MAS3 voor uw aanvulling

Ik ga me hier verder in verdiepen en kijken of ik hier verder uitkom.
Eerst het schema opbouwen display bestellen.

Mogelijk loopt de belasting niet lineair op , maar je krijgt wel verschillen in de verschillende instellingen.
En voor het competitie element geeft het toch al duidelijke verschillen tov alleen naar toerental te kijken.
Als dit gedeelte werkend is kan ik in latere fase trachten om de waarden zo reeel mogelijk te krijgen.