Motorradtacho mit Arduino nano // Hilfe benötigt

Hallo Kollegen,
ich hab ein komisches Problem mit einem Motorradtacho-Sketch, welches ich nicht recht inWorte fassen kann.. daher hab ich auch keinen aussagekräftigeren Titel gewählt (Falls jemand eine gute Idee hat - bitte melden ):

Also... Ich programmiere mir gerade einen kleinen Tacho für mein Motorrad. Vereinfacht gesagt wird per Reedschalter eine Interruptroutine ausgelöst, in der ich die Geschwindigkeit berechne. Das ganze wird dann auf einem 0.96" oled dargestellt und der Kilometerstand (Gesamt & Trip) zyklisch ins Eeprom weggeschrieben.
Bis gestern funktionierte auch alles, dann bin ich aber über ein (für mich) unerklärliches Phänomen gestolpert.
Sobald ich in void setup() die drei zeilen ab // if (GesamtMeter > 3000 ) ..... einkommentiere, gibt mir die RunningAverage Funktion (zur "Glättung" der Geschwindkeit) nur noch "nan" (not a number) aus und der Tacho funktioniert nicht mehr. Kommentiert man diese drei Zeilen wieder raus, klappt alles.

Ich konnte das Problem auf die Variable "Gesamtmeter" eingrenzen. Sobald man damit irgendwas in void-setup anstellt, funktinoiert RunningAverage nicht mehr... (Serial.println(GesamtMeter); führt zum selben Problem)

Wieso tangiert diese Variable die RunningAverage Funktion?
Für mich als Anfänger ist das sehr befremdlich :slight_smile:

Unten findet ihr den aufs wesentliche gekürzten Code. Als Attachement habe ich die ungekürzte Version angehängt.

danke im Voraus

mfg
Basti

#include <EEPROM.h>
#include <RunningAverage.h>
#include <Wire.h>
#include "OneButton.h"
#include <U8g2lib.h>
#include "RTClib.h"

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17);   // ESP32 Thing, HW I2C with pin remapping
RTC_DS3231 rtc;


RunningAverage myRA(5);
volatile unsigned long dauer = 0; 
volatile unsigned long last = 0;              // Zählerwert beim letzten Interrupt
short geschwindigkeit;                         
short VAnzeige;
const int buttonPin = 4;
const int reedPin = 3;
int screen = 0;
volatile int RotationCounter = 0;
OneButton button(buttonPin, true);
char Minute[3];
char Stunde[3];
const int RadUmfang = 1862;
char Uhrzeit[10];
char Temperatur[15];
char Speed[10];
float dummyInterval = (RadUmfang/1000.0 * 160.0);

unsigned long GesamtMeter;
unsigned long TripMillimeter;
int GesamtMeter_ADDR = 0;
int TripMillimeter_ADDR = GesamtMeter_ADDR + 4;
char TripKm[10];
char GesamtKm[10];
int testMeter = 0;
boolean reset = 0;


   
void setup()   {
  Serial.begin(9600);      // open the serial port at 9600 bps:

  if (reset) { resetEEPROM(); }     //alles auf 0 setzen
  
  pinMode(reedPin, INPUT_PULLUP); // Reed-Sensor an Pin 3
  pinMode(buttonPin, INPUT_PULLUP); // Taster an Pin 2
  attachInterrupt(digitalPinToInterrupt(reedPin), readmillis, RISING);
  GesamtMeter = eepromReadLong(GesamtMeter_ADDR);
  TripMillimeter = eepromReadLong(TripMillimeter_ADDR);

//  if (GesamtMeter > 3000 ) {
//    Serial.println("EEPROMschonen()");
//    }

  myRA.clear();
  button.attachClick(klick);
  button.attachLongPressStop(langDruck);
  u8g2.begin();
  delay(500);
  
}



void loop() {  

      if (RotationCounter >= 160) {    //ungefähr alle 300m Tachostand ins EEprom wegschreiben und GesamtMeter hochzählen 
         RotationCounter = 0;
         GesamtMeter += dummyInterval;  //alle 160 Umdrehungen Radumfang (mm) auf Gesamtlaufleistung (Meter) addieren
         eepromWriteLong(GesamtMeter, GesamtMeter_ADDR);
         eepromWriteLong(TripMillimeter, TripMillimeter_ADDR);
       }

      button.tick();
            
      if (millis()-last >= 2000) {
        myRA.clear();                       //wenn keine Radumdrehung innerhalb von 2 Sek stattgefunden hat --> Geschwindigkeit nullen
      }
      
      VAnzeige = myRA.getAverage();        // Mittelwert errechnen 
      u8g2.clearBuffer();
      draw();      
      u8g2.sendBuffer();
      delay(100);
}


void draw() {
  u8g2_prepare();
  DateTime now = rtc.now();
  sprintf(Uhrzeit, "%02d:%02d", now.hour(), now.minute());
  int hunderter = (TripMillimeter / 100000) % 10;
  int tausender = TripMillimeter / 1000000;
  sprintf(GesamtKm, "%6dkm", GesamtMeter/1000);
  sprintf(TripKm, "%5d.%dkm", tausender, hunderter); 
  sprintf(Speed, "%3d", VAnzeige); 
  
  switch (screen) {
    case 0: 
      u8g2.setFont(u8g2_font_logisoso38_tn);
      u8g2.drawStr( 5, 15, Uhrzeit);    
    break;
    case 1:
      u8g2.drawXBMP( 80, 0, tachoSymbol_width, tachoSymbol_height, tachoSymbol);  
      u8g2.setFont(u8g2_font_logisoso38_tn);   
      u8g2.drawStr( 0, 15, Speed);    
      u8g2.setFont(u8g2_font_logisoso20_tr);
      u8g2.drawStr( 75, 32, "kmh");
    break;  
    case 2:
      u8g2.setFont(u8g2_font_logisoso20_tr);
      u8g2.drawStr( 0, 10, "Trip:");
      u8g2.drawStr( 0, 40, TripKm);
      u8g2.drawXBMP( 80, 0, route_width, route_height, route_bits);  
    break; 
    case 3:
      u8g2.drawStr( 0, 40, TripKm);
      u8g2.drawStr( 5, 10, GesamtKm);
    break; 
    case 4:
      u8g2.setFont(u8g2_font_logisoso38_tn);
      u8g2.setCursor(0, 15);
      u8g2.print(tempDS3231(), 1); 
      u8g2.drawCircle(98, 20, 5, U8G2_DRAW_ALL);  //nicht besonders schön, aber speicher-optimalste Veriante ??
      u8g2.drawCircle(98, 20, 4, U8G2_DRAW_ALL);
      u8g2.drawXBMP( 105, 0, thermometer_width, thermometer_height, thermometer_Symbol);  
    break;               
    }
}


void u8g2_prepare(void) {
  u8g2.setFontRefHeightExtendedText();
  u8g2.setDrawColor(1);
  u8g2.setFontPosTop();
  u8g2.setFontDirection(0);
}


void readmillis() {                           // Interrupt-Routine
  
  unsigned long m = millis();                 // Microsekundenzähler auslesen
  unsigned long v = m - last;                 // Differenz zum letzten Durchlauf berechnen
  if (v > 20) {                               // ignorieren wenn <= 10ms (Kontaktpreller)
    dauer = v;                                // Dauer einer Radumdrehung in ms
    last = m;                                 // und wieder den neuen Wert merken
    geschwindigkeit = RadUmfang*3.6/dauer;             // Geschwindigkeit: 1 Interrupt alle 1861mm (Radumfang)
    myRA.addValue(geschwindigkeit);           // Wert zur Mittelwerberechnung hinzufügen
    TripMillimeter += RadUmfang;                    //pro Umdrehung 1861mm gefahren
    RotationCounter += 1; 
    Serial.println(myRA.getAverage());
    }
}

void klick() {   //durchtoggeln der Screens
  screen++;  
  if (screen > 4) { screen= 0; } 
 }

void langDruck() {    
  if (screen == 2)  { 
    TripMillimeter = 0;   //Trip zurücksetzen
    eepromWriteLong(TripMillimeter, TripMillimeter_ADDR);
    } 
}


float tempDS3231() {    // Die Temperatur des DS3231 auslesen  
  float temp;
  int msb, lsb;
  Wire.beginTransmission(0x68);
  Wire.write(0x11); // DS3231 Register zu 11h
  Wire.endTransmission();
  Wire.requestFrom(0x68, 2); // 2 Byte Daten vom DS3231 holen
  msb = Wire.read();
  lsb = Wire.read();
  temp=((msb << 2) + (lsb >> 6) ) /4.0;
  return temp;
}


void eepromWriteLong(long lo, int adr) {
    byte by;
    for(int i=0;i< 4;i++) {
      by = (lo >> ((3-i)*8)) & 0x000000ff; 
      EEPROM.write(adr+i, by);
    }
} 

long eepromReadLong(int adr) {
  long lo=0;
  for(int i=0;i< 3;i++){
    lo += EEPROM.read(adr+i);
    lo = lo << 8;
  }
  lo += EEPROM.read(adr+3);
  return lo;
}

tacho.txt (10.1 KB)

Ich sehe einen Typkonflikt:

unsigned long GesamtMeter;
...
GesamtMeter = eepromReadLong(GesamtMeter_ADDR);
...
long eepromReadLong(int adr) {
...

Ob das mit Deinem Problem zu tun hat, weiß ich aber nicht.

Also das unsigned entfernen?

Edit:
mit "long GesamtMeter;" klappt es auch nicht (falls du das gemeint hast)...

mfg
basti

BwieBertha:
Also das unsigned entfernen?

Eher umgekehrt, weil der Wert nicht negativ werden kann.

Ok, aber ohne unsigned gibt es doch keinen Konflikt mehr... Sinnhaftigkeit hin oder her...

Somit dürfte mein Problem woanders liegen...

Kleines Update:
Ich habe rausgefunden, dass das Problem verschwindet, sobald man diese Line auskommentiert (auch wenn sie viel später im code steht):

sprintf(Uhrzeit, "%02d:%02d", now.hour(), now.minute());

Mache ich da irgendwas falsch?

Bin gerade etwas angefressen O_o...

mfg
basti

BwieBertha:
Ich habe rausgefunden, dass das Problem verschwindet, sobald man diese Line auskommentiert (auch wenn sie viel später im code steht):

Wo sich die Ursache für ein Fehlverhalten versteckt, ist eines der vielen großen und kleinen Adventures, die man im Leben spielt. Manchmal helfen nur gute Flüche und ein Backup. Glücklich ist, wer versioniert :slight_smile:

Gruß

Gregor

Danke......

Mittlerweile hab ich noch zwei weitere Ungereimtheiten gefunden, die sich jedoch alle in Luft auflösen, wenn ich sämtliche sprintf() Anweisungen rausnehme.

Kann mir jemand sagen ob ich da was falsch mache?
Oder ist diese Funktion generell mit Vorsicht zu genießen?

Hilfe...

BwieBertha:
Danke......

Mittlerweile hab ich noch zwei weitere Ungereimtheiten gefunden, die sich jedoch alle in Luft auflösen, wenn ich sämtliche sprintf() Anweisungen rausnehme.

Kann mir jemand sagen ob ich da was falsch mache?
Oder ist diese Funktion generell mit Vorsicht zu genießen?

Ja ist doch super.
Funktioniert dann dein Sketch noch ?

Im übrigen sind alle Funktionen und Anweisungen mit Vorsicht zu genießen, wenn man nicht damit umgehen kann.

Einfach mal die Dokumentation dazu lesen.

Nein, mein sketch funktioniert nicht mehr ohne sprintf.
Des weiteren bin ich mir sicher, sprintf richtig zu verwenden.
Das Problem muss wo anders liegen.... Evtl ein Speicherschmierer?

(deleted)

Das kann ich eigentlich ausschließen....

Es sei denn, die RunnigAverage Lib hat einen bug, aber das kann ich mir nicht vorstellen.

Edit: (Um das nochmal zu präzisieren:)
RunningAverage liest hier immer 5 (Geschwindigkeits-)Werte ein und bildet dann den Mittelwert.
Ich habe mir jeden dieser Werte vorher ausgeben lassen - alle waren ok (zwischen 0 und 210).
Allerdings bringt die Abfrage von RA.getElement(0) schon NaN (obwohl die Input-Größe wie besagt nachweislich ok war).

(deleted)

Ja, das weiß ich. Aber ich füge ja laufend neue Werte hinzu....

Wenn ich die sprintf auskommentiere funktioniert auch alles...

Mir ist einfach dieser Zusammenhang unklar.

(deleted)

Habs eben probiert - bringt leider keine Änderung meines Problems ....

Danke dennoch!

Hallo Kollegen,
sorry aber ich muss den Thread nochmal aufwärmen, da ich hier einfach nicht weiterkomme.
Ich habe meinen Sketch auf ein (kompilierendes) Minimum runtergestrickt; den Fehler "provoziere" ich in der Setup() Routine.
Hier übergebe ich 5 Werte an Runningaverage und lese anschließend den Mittelwert aus. Nach wievor bekomme ich "nan" als Rückgabewert.
Allerdings wird das Problem gelöst, wenn ich:

  • getKilometerstand() auskommentiere ODER
  • draw() auskommentiere ODER
  • OneButton.h inkl. aller Aufrufe entferne ODER
  • das Display inkl. u8g2 entferne ODER
  • die RTC entferne

Eine dieser Aktionen reicht!!! Das macht doch alles keinen Sinn.
Wie kann eine Funktion in void setup() von später aufgerufenen Funktionen beeinflusst werden? Zumal diese Funktionen nichts mit RunningAverage zu tun haben?

ARGH!!!! :o

#include <EEPROM.h>
#include <RunningAverage.h>
#include "OneButton.h"
#include <U8g2lib.h>
#include "RTClib.h"

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17);   // ESP32 Thing, HW I2C with pin remapping
RTC_DS3231 rtc;

RunningAverage myRA(5);
short VAnzeige;
const int buttonPin = 4;
OneButton button(buttonPin, true);
char Speed[10];

unsigned long GesamtMeter;
unsigned long TripMillimeter;
int GesamtMeter_ADDR = 0;
int TripMillimeter_ADDR = GesamtMeter_ADDR + 4;
char TripKm[10];
char GesamtKm[10];

void setup()   {
  Serial.begin(9600);      // open the serial port at 9600 bps:
   
  myRA.clear();
  Serial.println("5 Werte werden übergeben");
  myRA.addValue(5);
  myRA.addValue(100);
  myRA.addValue(20);
  myRA.addValue(30);
  myRA.addValue(77);
  Serial.println(myRA.getAverage());  

  getKilometerstand();                                   //gespeicherten Tachostand abrufen (eeprom-adresse mit höchstem Zählerstand suchen) 

}

void loop() {                   
  u8g2.clearBuffer();
  draw();      
  u8g2.sendBuffer();
}


void draw() {  
  DateTime now = rtc.now();
  int hunderter = (TripMillimeter / 100000) % 10;
  int tausender = TripMillimeter / 1000000;
  sprintf(GesamtKm, "%6ldkm", GesamtMeter/1000); 
  sprintf(TripKm, "%5d.%dkm", tausender, hunderter); 
  sprintf(Speed, "%3d", VAnzeige); 
  }

void getKilometerstand(){      
  EEPROM.get(GesamtMeter_ADDR, GesamtMeter);    
  EEPROM.get(TripMillimeter_ADDR, TripMillimeter);
  Serial.println("########");
  Serial.println("gefundener Kilometerstand:");      
  Serial.println(GesamtMeter);
}

(deleted)

öh.. ist das Verhalten also "geräteabhängig"?????

Es beruhigt mich aber ungemein, dass du das Problem nachstellen kannst.

Hi

Hast Du einen Link zur RunningAverage?
Die fehlt mir und in der Arduino-eigenen-Bibliotheks-Auswahl ist Diese nicht enthalten.
-> bei mir kompiliert der Sketch so nicht (bisher das Einzige, was Er anmeckert)

MfG

PS:
Wenig Arbeitsspeicher verfügbar, es können Stabilitätsprobleme auftreten.
Das soll Dir sagen, daß beim Anlegen diverser lokalen Variablen der Speicher ausgehen wird und der Arduino Mist baut.
Du hast sehr sehr viele globale Variablen - überdenke, Welche Du davon wirklich global brauchst und Welche Du nur innerhalb irgend welcher Abläufe benötigst.
Wobei in diesem Sketch DEINE Variablen echt übersichtlich sind - die ganzen eingebundenen Libs werden sich dort großzügig bedient haben - aber z.B. das Display wird den Puffer wohl brauchen.