Tachometer mit ESP32, OLED und Hallsensor

Mein erstes Projekt, bin Anfänger: Für mein Fahrrad möchte ich ein Digitaldisplay realisieren. Den Code habe ich aus einem Video. Der Aufgebaut ist mit einem ESP32 C3 mini, Oled SSD1306 128*64 Display und Hallsensor der am Pin4 vom ESP32 hängt. Ich hab das Problem dass auf dem Oled Display alle Texte und Werte angezeigt werden, aber leider werden die Werte nicht aktualisiert diese bleiben immer 0 stehen. Der Hallsensor soll über 2 Magnete angesprochen werden. Danke für die Hilfe, Gruß Joerg

/*
 * Titel: Geschwindigkeit
 */
#include <Interval.h>  // Zeitbibiliothek zum ständigen Wiederholen der Funktion

#include <SPI.h>               // Display: Benötigte Bibliothekebn
#include <Wire.h>              // Serial Peripheral Interface: Kommunikation zwischen den Geräten
#include <Adafruit_GFX.h>      // Kommunikation zu 12c (inter Intergrated Circuit) device (OLED-Display)
#include <Adafruit_SSD1306.h>  // Grafik-Bibliothek

// Dimension des Displays
#define SCREEN_WIDTH 128  // OLED Displa Beite in (Pixel)
#define SCREEN_HEIGHT 64  // OLED Display Höhe in (Pixel)

#define OLED_RESET 4

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define signalHallsensor 4  // Signal des Hallsensors geht an den Pin4 des ESP32

// Dimension des Rades (26x1, 90 Zoll)
// Rad: 26 Zoll (1 Zoll = 25,4 mm) --> 660,4 mm / 1000 = 0,6604m (Durchmesser in Meter)
// Umpfang = Pi * 0,6604m                              = 2,0747m (Umpfang in Meter)

float radumpfang = 2.0747;


void setup() 
{
  
  // Display Grundeinstellungen
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Adresse 0x3c für 128x64
  display.clearDisplay();                     // Leerer Bildschirm
  display.setTextSize(1);                     // Display: Schriftgröße ("1"/"Normal")
  display.setTextColor(WHITE);                // Display: Schriftfarbe (Weiß)


  pinMode(signalHallsensor, INPUT);  // Interruproutine bei steigender Flanke
  attachInterrupt(signalHallsensor, ISR, RISING);
}

volatile byte summeMagnete;
volatile long summeMillis;

unsigned long vorherigeMillis;

void ISR() 
{
  if (millis() - vorherigeMillis >= 25)  // 25ms enspricht maximal 40 Radumdrehungen pro Sekunde
  {
    summeMagnete++;                             // Magnete zählen und Aufsummieren
    summeMillis += millis() - vorherigeMillis;  // Zeit zwischen den Signalen zählen und Aufsummieren
    vorherigeMillis = millis();                 // Zeit merken
  }
}

void loop() 
{
  Interval(2500); {
    tachometer();
  }
}

unsigned long gesamtUmdrehungen;

void tachometer() 
{
  byte einzelUmdrehungen;
  unsigned long zeit;
  float drehzahl, geschwindigkeit, strecke;

  noInterrupts();                          // Interrupts werden gesperrt
    einzelUmdrehungen = (summeMagnete) /2;  // Zahlvariable Magnete umkopieren
    summeMagnete = 0;                        // Zahlvariable Magnete auf 0 zurücksetzen
    zeit = summeMillis;                      // Zahlvariable Zeit umkopieren
    summeMillis = 0;                         // Zahlvariable Zeit auf 0 zurücksetzen
  interrupts();                            // Interrupts werden wieder zugelassen

  gesamtUmdrehungen += einzelUmdrehungen;  // Aufsummieren aller Radumdehungen

  strecke = (float)gesamtUmdrehungen * (radumpfang/1000.0);  // Fahrtkilometerzähler

  if (einzelUmdrehungen > 0) {
    drehzahl = 60.0 * 1000.0 * ((float)einzelUmdrehungen) / ((float)zeit);                    // Raddrehzal
    geschwindigkeit = 3.6 * 1000.0 * radumpfang * ((float)einzelUmdrehungen)/((float)zeit);  // Geschwindigkeit
  } else {
    drehzahl = 0.0;
    geschwindigkeit = 0.0;
  }
  

  display.clearDisplay();
  display.setCursor(5, 0);
  display.println("Tachometer");

  display.setCursor(0, 26);
  display.println("o km/h:");
  display.setCursor(0, 27);
  display.println("/");
  display.setCursor(60, 26);
  display.println(durchschnittsgeschwindigkeit);
  display.setCursor(98, 26);
  display.println("km/h");

  display.setCursor(0, 16);
  display.println("Dauer:");
  display.setCursor(60, 16);
  display.println(dauerM);
  display.setCursor(64, 16);
  display.println(":");
  display.setCursor(68, 16);
  display.println(dauerM);
  display.setCursor(77, 16);
  display.println(":");
  display.setCursor(81, 16);
  display.println(dauerS);
  display.display();

  display.setCursor(0, 36);
  display.println("Geschw:");
  display.setCursor(10, 36);
  display.println(geschwindigkeit);
  display.setCursor(68, 36);
  display.println("km/h");
  

  display.display();
}

Dann zeig mal ein Schaltbild, wie du das verdrahtet hast, denn so kann es nicht funktionieren.

Beides auf Pin 4 funktioniert nicht.
Auch vermute ich, dein Oled hat keinen Resetpin.

So wie @HotSystems, es gibt selten ein I²C OLED mit Resetpin Schreib mal

#define OLED_RESET -1

Einmal den Schaltplan bitte.
Welchen Hal Sensor hast du?
Ist der in Reichweite der Magnete?

Die Doppelbelegung von Pin 4 ist schon mal ein Problem.

Da würde ich auch was umstellen: Die Routine verarbeitet genau ein Ereignis; da würde ich - auch wenn es wenig ausmacht - lieber mit einem Zeitpunkt arbeiten als die Funktion dreimal aufzurufen:

void ISR() 
{
  uint32_t  aktuelleMillis = millis();
  if (aktuelleMillis - vorherigeMillis >= 25)         // 25ms entspricht maximal 40 Radumdrehungen pro Sekunde
  {
    summeMagnete++;                                   // Magnete zählen und Aufsummieren
    summeMillis += aktuelleMillis - vorherigeMillis;  // Zeit zwischen den Signalen zählen und Aufsummieren
    vorherigeMillis = aktuelleMillis ;                // Zeit merken
  }
}

Vielen Dank für Eure schnellen Antworten.

Der Hallsensor befindet sich jetzt am Pin5 und wird mit 3,3v com ESP versorgt.
Das OLED ist an Pin 8 SDA und Pin9 SCL und wird ebenfalls mit 3,3v vom ESP versorgt.
Den Hallsensor den ich beutze ist der A3144.
Den Schaltplan habe ich von diesem Video übernommen.
Fahradtachometer

Ich habe mal den Code nach Euren Vorschlägen geändert und das was ich aktuell ersteinmal nicht benötige auskommentiert. Es wird trozdem keine Aktualisierung des kmh Wertes gemacht.

/*
 * Titel: Geschwindigkeit
 */
#include <Interval.h>  // Zeitbibiliothek zum ständigen Wiederholen der Funktion

#include <SPI.h>               // Display: Benötigte Bibliothekebn
#include <Wire.h>              // Serial Peripheral Interface: Kommunikation zwischen den Geräten
#include <Adafruit_GFX.h>      // Kommunikation zu 12c (inter Intergrated Circuit) device (OLED-Display)
#include <Adafruit_SSD1306.h>  // Grafik-Bibliothek

// Dimension des Displays
#define SCREEN_WIDTH 128  // OLED Displa Beite in (Pixel)
#define SCREEN_HEIGHT 64  // OLED Display Höhe in (Pixel)

#define OLED_RESET -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define signalHallsensor 5  // Signal des Hallsensors geht an den Pin4 des ESP32

// Dimension des Rades (26x1, 90 Zoll)
// Rad: 26 Zoll (1 Zoll = 25,4 mm) --> 660,4 mm / 1000 = 0,6604m (Durchmesser in Meter)
// Umpfang = Pi * 0,6604m                              = 2,0747m (Umpfang in Meter)

float radumpfang = 2.0747;

// Dimension des Kugellagers 5mm
// Lager: 0,19685 Zoll (1 Zoll = 25,4 mm) --> 4,99999 mm / 1000 = 0,00499999m (Durchmesser in Meter)
// Umpfang = Pi * 0,00499999m                              = 0.01571m (Umpfang in Meter)

// float radumpfang = 0.01571;      // für 5mm Kugellager 0.01571m (Umpfang in Meter)

void setup() 
{
  
  // Display Grundeinstellungen
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Adresse 0x3c für 128x64
  display.clearDisplay();                     // Leerer Bildschirm
  display.setTextSize(1);                     // Display: Schriftgröße ("1"/"Normal")
  display.setTextColor(WHITE);                // Display: Schriftfarbe (Weiß)
  display.println("Willkommen");
  display.display();

  pinMode(signalHallsensor, INPUT);  // Interruproutine bei steigender Flanke
  attachInterrupt(signalHallsensor, ISR, RISING);
}

volatile byte summeMagnete;
volatile long summeMillis;

unsigned long vorherigeMillis;

void ISR() 
{
  uint32_t  aktuelleMillis = millis();
  if (aktuelleMillis - vorherigeMillis >= 25)         // 25ms entspricht maximal 40 Radumdrehungen pro Sekunde
  {
    summeMagnete++;                                   // Magnete zählen und Aufsummieren
    summeMillis += aktuelleMillis - vorherigeMillis;  // Zeit zwischen den Signalen zählen und Aufsummieren
    vorherigeMillis = aktuelleMillis ;                // Zeit merken
  }
}

/*void ISR() 
{
  if (millis() - vorherigeMillis >= 25)  // 25ms enspricht maximal 40 Radumdrehungen pro Sekunde
  {
    summeMagnete++;                             // Magnete zählen und Aufsummieren
    summeMillis += millis() - vorherigeMillis;  // Zeit zwischen den Signalen zählen und Aufsummieren
    vorherigeMillis = millis();                 // Zeit merken
  }
}
*/
void loop() 
{
  Interval(2500); {
    tachometer();
  }
}

unsigned long gesamtUmdrehungen;

void tachometer() 
{
  byte einzelUmdrehungen;
  unsigned long zeit;
  float drehzahl, geschwindigkeit, strecke;

  noInterrupts();                          // Interrupts werden gesperrt
    einzelUmdrehungen = (summeMagnete) /2;  // Zahlvariable Magnete umkopieren
    summeMagnete = 0;                        // Zahlvariable Magnete auf 0 zurücksetzen
    zeit = summeMillis;                      // Zahlvariable Zeit umkopieren
    summeMillis = 0;                         // Zahlvariable Zeit auf 0 zurücksetzen
  interrupts();                            // Interrupts werden wieder zugelassen

  gesamtUmdrehungen += einzelUmdrehungen;  // Aufsummieren aller Radumdehungen

  strecke = (float)gesamtUmdrehungen * (radumpfang/1000.0);  // Fahrtkilometerzähler

  if (einzelUmdrehungen > 0) {
    drehzahl = 60.0 * 1000.0 * ((float)einzelUmdrehungen) / ((float)zeit);                    // Raddrehzal
    geschwindigkeit = 3.6 * 1000.0 * radumpfang * ((float)einzelUmdrehungen)/((float)zeit);  // Geschwindigkeit
  } else {
    drehzahl = 0.0;
    geschwindigkeit = 0.0;
  }
  

  display.clearDisplay();
  display.setCursor(5, 0);
  display.println("Tachometer");

//  display.setCursor(0, 26);
//  display.println("o km/h:");
//  display.setCursor(0, 27);
//  display.println("/");
//  display.setCursor(60, 26);
//  display.println(durchschnittsgeschwindigkeit);
//  display.setCursor(98, 26);
//  display.println("km/h");

//  display.setCursor(0, 16);
//  display.println("Dauer:");
//  display.setCursor(60, 16);
//  display.println(dauerM);
//  display.setCursor(64, 16);
//  display.println(":");
//  display.setCursor(68, 16);
//  display.println(dauerM);
//  display.setCursor(77, 16);
// display.println(":");
//  display.setCursor(81, 16);
//  display.println(dauerS);
//  display.display();

//  display.setCursor(0, 36);
//  display.println("Geschw:");
  display.setCursor(10, 36);
  display.println(geschwindigkeit);
  display.setCursor(68, 36);
  display.println("km/h");
  

  display.display();
}

Was soll dieser Code eigentlich machen?

Er soll mir die gefahrenen Kilometer und die kmh anzeigen.

Dann entferne mal das Semikolon in der Zeile.
Mit Semikolon wird tachometer nie aufgerufen.

Wie schon geschrieben habe ich den Code aus einem Video und bei diesem funktioniert dies.
Ich bin Neuling in der Arduino Programmierung und versuche gerade zu lernen. Deswegen kann ich auch noch nicht sagen was bei diesem loop genau passieren soll.

Dann hast du wohl tippfehler fabriziert oder der Sketch war total falsch.
Das können wir so nur raten.
Da musst du schon einen Link zum Original liefern.

Deinen 2. geänderten Sketch mag ich mir überhaupt nicht ansehen, der scheint mir total vermurkst.

Yep.

@floddie Aus dem Video kopiert:

Den Fehler habe ich auch erkannt und das Simikolon in meinem Code ergänzt.

INTERVAL != Interval...

Ich finde gerade keine Doku zu dem Befehl.
Ersetze das mal mit delay.

Was für einen Fehler willst du erkannt haben ?
Bitte lies unsere Anmerkungen genau.

Das Semikolon muss gelöscht werde.

Moin @floddie ,

ohne den Sketch weiter im Detail geprüft zu haben:

  • Interval.h gelöscht
  • Dafür eine kleine eigene Funktion eingeführt
  • Zusätzlich eine Simulation für die Pulserzeugung eingebaut, damit der Rest auch ohne die Hardware getestet werden kann
/*
   Titel: Geschwindigkeit

   Forum: https://forum.arduino.cc/t/tachometer-mit-esp32-oled-und-hallsensor/1442427
   Wokwi: https://wokwi.com/projects/463002589818292225

  Geänderter Sketch
    Puls-Simulation ergänzt
    Interval.h entfernt, dafür eine eigene kleine millis()-Funktion eingebaut

   2026/05/03
   ec2021
*/


#include <SPI.h>               // Display: Benötigte Bibliothekebn
#include <Wire.h>              // Serial Peripheral Interface: Kommunikation zwischen den Geräten
#include <Adafruit_GFX.h>      // Kommunikation zu 12c (inter Intergrated Circuit) device (OLED-Display)
#include <Adafruit_SSD1306.h>  // Grafik-Bibliothek

// Dimension des Displays
#define SCREEN_WIDTH 128  // OLED Displa Beite in (Pixel)
#define SCREEN_HEIGHT 64  // OLED Display Höhe in (Pixel)

#define OLED_RESET -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define signalHallsensor 5  // Signal des Hallsensors geht an den Pin4 des ESP32

// Dimension des Rades (26x1, 90 Zoll)
// Rad: 26 Zoll (1 Zoll = 25,4 mm) --> 660,4 mm / 1000 = 0,6604m (Durchmesser in Meter)
// Umfang = Pi * 0,6604m                              = 2,0747m (Umfang in Meter)

float radumfang = 2.0747;

// Dimension des Kugellagers 5mm
// Lager: 0,19685 Zoll (1 Zoll = 25,4 mm) --> 4,99999 mm / 1000 = 0,00499999m (Durchmesser in Meter)
// Umfang = Pi * 0,00499999m                              = 0.01571m (Umfang in Meter)

// float radumfang = 0.01571;      // für 5mm Kugellager 0.01571m (Umfang in Meter)



void setup()
{
  Serial.begin(115200);
  Serial.println("Start");
  // Display Grundeinstellungen
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // Adresse 0x3c für 128x64
  display.clearDisplay();                     // Leerer Bildschirm
  display.setTextSize(1);                     // Display: Schriftgröße ("1"/"Normal")
  display.setTextColor(WHITE);                // Display: Schriftfarbe (Weiß)
  display.println("Willkommen");
  display.display();

  pinMode(signalHallsensor, INPUT);  // Interruproutine bei steigender Flanke
  attachInterrupt(signalHallsensor, ISR, RISING);
}

volatile byte summeMagnete;
volatile long summeMillis;

unsigned long vorherigeMillis;
unsigned long gesamtUmdrehungen;

void ISR()
{
  uint32_t  aktuelleMillis = millis();
  if (aktuelleMillis - vorherigeMillis >= 25)         // 25ms entspricht maximal 40 Radumdrehungen pro Sekunde
  {
    summeMagnete++;                                   // Magnete zählen und Aufsummieren
    summeMillis += aktuelleMillis - vorherigeMillis;  // Zeit zwischen den Signalen zählen und Aufsummieren
    vorherigeMillis = aktuelleMillis ;                // Zeit merken
  }
}

void loop()
{
  /****************************/
  simulatePPM(300);
  /****************************/
  if (interval(2500)) {
    tachometer();
  }
}

bool interval(unsigned long time) {
  static unsigned long lastTime = 0;
  if (millis() - lastTime >= time) {
    lastTime = millis();
    return true;
  }
  return false;
}


void tachometer()
{
  byte einzelUmdrehungen;
  unsigned long zeit;
  float drehzahl, geschwindigkeit, strecke;

  noInterrupts();                          // Interrupts werden gesperrt
  einzelUmdrehungen = (summeMagnete) / 2; // Zahlvariable Magnete umkopieren
  summeMagnete = 0;                        // Zahlvariable Magnete auf 0 zurücksetzen
  zeit = summeMillis;                      // Zahlvariable Zeit umkopieren
  summeMillis = 0;                         // Zahlvariable Zeit auf 0 zurücksetzen
  interrupts();                            // Interrupts werden wieder zugelassen

  Serial.println(einzelUmdrehungen);
  gesamtUmdrehungen += einzelUmdrehungen;  // Aufsummieren aller Radumdehungen

  strecke = (float)gesamtUmdrehungen * (radumfang / 1000.0); // Fahrtkilometerzähler

  if (einzelUmdrehungen > 0) {
    drehzahl = 60.0 * 1000.0 * ((float)einzelUmdrehungen) / ((float)zeit);                    // Raddrehzal
    geschwindigkeit = 3.6 * 1000.0 * radumfang * ((float)einzelUmdrehungen) / ((float)zeit); // Geschwindigkeit
  } else {
    drehzahl = 0.0;
    geschwindigkeit = 0.0;
  }


  display.clearDisplay();
  display.setCursor(5, 0);
  display.println("Tachometer");
  display.setCursor(10, 36);
  display.println(geschwindigkeit);
  display.setCursor(68, 36);
  display.println("km/h");


  display.display();
}

/* The following function creates pulses in pulseOutPin that */
/* simulate pulses from a given sensor                       */

void simulatePPM(uint16_t RPM) {
  constexpr byte pulseOutPin {5};
  static unsigned long lastPulse = 0;
  if (lastPulse == 0) {
    pinMode(pulseOutPin, OUTPUT);
    digitalWrite(pulseOutPin, HIGH);
  }
  unsigned long noOfPulses = RPM / 60;
  unsigned long microsInterval = 1000000UL / noOfPulses;
  if (micros() - lastPulse >= microsInterval) {
    lastPulse = micros();
    digitalWrite(pulseOutPin, LOW);
    delayMicroseconds(5);
    digitalWrite(pulseOutPin, HIGH);
  }
}

Zum Ausprobieren auf Wokwi: https://wokwi.com/projects/463002589818292225

Ob neben der Library-Funktion noch weitere Probleme lauern, habe ich - wie o.a. - nicht geprüft ... Habe nur noch das p aus dem "Umfang" herausoperiert ... :wink:

Viel Erfolg!
ec2021

P.S.: Um den Sketch mit dem Board zu testen, muss man nur Pin 4 mit Pin 5 verbinden. Dann kann man in der loop() bei simulatePPM(x); anstelle von x einen Wert >= 60 eingeben. Darunter ist (bisher) nicht vorgesehen, da ich die Funktion für eine Wokwi-Simulation eines Drehzahlmessers geschrieben und hier 1:1 eingebaut habe. Wenn weniger als 1 Puls/s simuliert werden soll, muss man noch ein wenig Hand anlegen ...