Tacho mit Reed-Schalter

Hallo! Mein erstes Projekt auf einem Arduino Nano ist ein Tachometer mit Hilfe eines alten Fahrrad-Reed-Schalter. Ich habe teilweise mit Hilfe eines Tutorials folgenden Code geschrieben. Leider hat aber meine Freude daran, dass alles einwandfrei funktioniert nur kurz gedauert. Was mir aufgefallen ist, wenn ich in der vorletzten Zeile mit "Serial.println(impuls);" die Zwischenschritte von der impuls-variable ausgeben lasse, stimmt das km/h Ergebnis recht genau, wenn ich diese Zeile weg lasse, stimmt sie nicht mehr. (km/h-Ergebnis ist zu hoch) Ich gehe davon aus, dass das daran liegt, dass der Prozessor dann einfach schneller in die loop() gelangt und somit die millis() einen geringeren Wert hat und somit der Anschein entsteht, dass das Fahrrad schneller fährt. Könnt ihr mir da weiterhelfen, wie man so etwas besser löst? Darf man davon ausgehen, dass der Prozessor diesen Code immer gleich schnell durchläuft? Mit einem Delay in der trigger-funktion wäre das dann ja viell. unschön aber funktionierend gelöst :blush:

vielen Dank!

lg cleee

// Digitaltacho mit Reed-Schalter

double umfang=2.0;                // in Meter
int umdrehungen;
int impuls;      
int pinReed = 2;                  // Digital Pin am Arduino A2
double kmh;

double rpmilli;                   // Geschwindigkeit in Umdrehungen pro Millisekunde
double rpm;
unsigned long timeold;        
unsigned long zeitpU;             // Zeit pro Umdrehung

void setup() 
{
  Serial.begin(9600);
  
  // Pinbelegung für Reed-Schalter
  pinMode(pinReed, INPUT);
  digitalWrite(pinReed, HIGH);
  attachInterrupt(0, trigger, FALLING);
   
  rpmilli=0;
  timeold=0;
  zeitpU=0;
  impuls=0;
}

void loop() 
{
   if (impuls >= 6) 
   {
     umdrehungen=3;                         // 6 Impulse bedeutzet 3 Umdrehungen (2 Impulse pro Umdrehung)        
     zeitpU=((millis()-timeold))/3;         // Zeit für eine Umdrehung
     timeold=millis();                      // Bereits vergangene Zeit
     rpmilli=1/(double)zeitpU;              // Umdrehungen pro Millisekunde
     rpm=rpmilli*1000*60;                   // Umdrehungen pro Minute
     kmh=(rpm*60*umfang)/1000;
     Serial.println(kmh);
     impuls=0; 
   }
}

void trigger() 
{
  impuls++;
  Serial.println(impuls);
}

1.) Serial hat in Interrupts nichts verloren. Das geht einfach nicht 2.) Variablen die in Interrupts und außerhalb verwendet werden müssen volatile sein! http://arduino.cc/en/Reference/Volatile Das verhindert außerdem dass der Compiler eine Variable wegoptimiert. Der hat nämlich keine Möglichkeit festzustellen ob und wie die Interrupt Routine aufgerufen wird.

3.) Delay geht in Interrupts nicht und hat in Interrupts auch vom Prinzip her nichts verloren. ISRs müssen so kurz wie möglich sein.

Wenn du nach einem Interrupt was auf Serial ausgeben willst mach es so:

volatile int impuls;
volatile boolean triggered;

void trigger() 
{
  impuls++;
  triggered = true;
}

void loop()
{
     if(triggered)
     {
          Serial.println(impuls);
          triggered = false;
     }
}

Vielen Dank für die Tipps, das ging ja schnell um diese Uhrzeit :astonished: So müsste es in dem Fall besser sein? Ich werde morgen noch mal mit einem originalen Fahrradtacho die Geschwindigkeit vergleichen :)

// Digitaltacho mit Reed-Schalter

double umfang=2.0; // in Meter

volatile int impuls; volatile boolean triggered;

int umdrehungen; int pinReed = 2; // Digital Pin am Arduino A2 double kmh;

double rpmilli; // Geschwindigkeit in Umdrehungen pro Millisekunde double rpm; unsigned long timeold; unsigned long zeitpU; // Zeit pro Umdrehung

void setup() { Serial.begin(9600);

// Pinbelegung für Reed-Schalter pinMode(pinReed, INPUT); digitalWrite(pinReed, HIGH); attachInterrupt(0, trigger, FALLING); rpmilli=0; timeold=0; zeitpU=0; impuls=0; }

void loop() { if (impuls >= 6) { umdrehungen=3; // 6 Impulse bedeutzet 3 Umdrehungen (2 Impulse pro Umdrehung) zeitpU=((millis()-timeold))/3; // Zeit für eine Umdrehung timeold=millis(); // Bereits vergangene Zeit rpmilli=1/(double)zeitpU; // Umdrehungen pro Millisekunde rpm=rpmilli*1000*60; // Umdrehungen pro Minute kmh=(rpm*60*umfang)/1000;

if(triggered) { Serial.println(kmh); triggered=false; }

impuls=0; } }

void trigger() { impuls++; triggered=true;

}

Rein vom Code her ist das besser ja. Ob es geht und das korrekte Ergebnis ausspuckt kann ich nicht sagen

Würde es aber eher so machen:

if (triggered && impuls >= 6)
{
    triggered=false;
    impuls=0; 


     ....
}

Die Variable triggered ist in diesem Fall genaugenommen nicht zwingend nötig, da dir impuls schon anzeigt ob sich was geändert hat. Es schadet aber auch nichts.

cleee: Ich werde morgen noch mal mit einem originalen Fahrradtacho die Geschwindigkeit vergleichen :)

Ich kann Dir auch so sagen, was passiert: Wenn Du nach dem Fahren anhältst und die Geschwindigkeit null ist, wird der Fahrradtacho 0.0 km/h anzeigen und Dein Tacho wird die zuletzt getriggerte Geschwindigkeit anzeigen:

  if(triggered)
     {
       Serial.println(kmh);
       triggered=false;
     }

Wenn kein Impuls getriggert wird, wird von Deinem Tachon auch keine Geschwindigkeit ausgegeben, sondern es bleibt immer die zuletzt getriggerte Geschwindigkeit die zuletzt angezeigte.

Deine Programmlogik kann Stillstand mit 0.0 km/h nicht anzeigen. Übliche Fahrradtachos geben nach dem Anhalten aber 0.0 aus.

Hallo,

ein kleiner Tipp am Rande. Falls das Programm stimmt aber die km/h Anzeige noch nicht, messe nochmal den Reifenumfang nach. 2m sind eigentlich unüblich. Mein MTB 26" Reifen hat 2135mm Umfang. Mit Maßband ausgemessen. Jede kleine Abweichung macht sich bemerkbar wenn es genau sein soll.

Danke für eure tipps! das mit der 0.00km/h angabe muss ich mir noch anschauen :-) die 2m umfang sind nur gewählt dass ich mit 2m/s leicht mit dem sensor in der hand probieren konnte ob das ergebnis in etwa stimmt. Tendenziell ist das ergebnis aber noch zu hoch. :)

War auch eher als Prinzip gedacht wie man loop() mitteilt, dass etwas in einem Interrupt gemacht wurde :)

Du kannst immer den letzten Wert in extra Variable abspeichern. Dann vergleichst du den aktuellen Wert mit dem letzten Wert (dabei aufpassen, dass man bei Gleitkommazahlen wegen der Rundungsfehler auf einen Bereich abfragen sollte und nicht auf exakte Gleichheit!). Wenn der Wert gleich ist machst du nichts. Wenn der Wert unterschiedlich ist, aktualisierst du die Anzeige und setzt den alten Wert auf den neuen Wert.

cleee:
Tendenziell ist das ergebnis aber noch zu hoch. :slight_smile:

Dir ist schon klar, dass Reedkontakte zwar eine nur geringe Neigung zum Kontaktprellen haben, aber dass es trotzdem eben nur mechanische Kontakte sind, bei denen eine mechanisches Kontaktprellen nicht vollkommen auszuschließen ist?

Dein Code enthält keine softwaremäßige Entprellung, d.h.: Falls die Kontakte prellen, besteht die Möglichkeit, dass Du einen Schließvorgang der Kontakte mehrmals zählen könntest, wenn ein Kontaktprellen auftritt. Das würde ich lieber vermeiden und das Signal softwaremäßig entprellen.

Außerdem enthält Dein Code kleine Schlampigkeiten, die zu geringen Fehlern führen, z.B.

zeitpU=((millis()-timeold))/3;

Damit machst Du eine Integer-Division, der Divisionsrest nach dem Komma fällt unter den Tisch.

Beträgt die Differenz z.B. 500, dann ergibt 500/3 = 166
Das würde allerdings nur zu einem Fehler von weniger als 1% führen.

Halloo! Nach längerer Zeit bin ich wieder am Basteln... Ich habe ein kleines OLED-Display besorgt (standard 0.96" China-Ding), am Arduino nano gab es für die Spannungsversorgung einen 5V-Ausgang... Derzeit hab ich einen Arduino mini im Einsatz, da gibt es diesen Ausgang nicht... Darf man an einem High-Pin die 5V abzapfen solange der maximal-Strom nicht überstiegen wird oder ist das ganz einfach unschön gelöst?

danke,

cleee

Les die Produktseite http://arduino.cc/en/Main/ArduinoBoardProMini

VCC = 5V

cleee: Darf man an einem High-Pin die 5V abzapfen solange der maximal-Strom nicht überstiegen wird oder ist das ganz einfach unschön gelöst?

danke,

cleee

Dürfen darf man alles ... nach dieser Elektro-Philosofischen Betrachtung: Nein, soll man nicht tun.

Grüße Uwe

Dankeschön, funktioniert einwandfrei :)