/*
Forum: https://forum.arduino.cc/t/pulse-zahlen-in-einer-definierten-zeit/1413218
Wokwi: https://wokwi.com/projects/447595962731835393
Vorläufer vom 2025/11/11
Wokwi: https://wokwi.com/projects/447332250347170817
OLED-Display ergänzt
2025/11/14
ec2021
In der Annahme, dass ein Schalen-Anemometer verwendet wird ergibt sich gemäß
https://de.wikipedia.org/wiki/Anemometer die Berechnung der Windgeschwindigkeit
aus der "Schnelllaufzahl" des Anemometers, die nach Wikipedia zwischen
0,3 und 0,4 liegen soll.
Die Schnelllaufzahl (https://de.wikipedia.org/wiki/Schnelllaufzahl) ist als das
Verhältnis von Umfangsgeschwindigkeit des Rotors zur Windgeschwindigkeit definiert:
Schnelllaufzahl: Lambda
Rotorradius: Radius [m]
Rotordrehzahl: Drehzahl [U/s]
Windgeschwindigkeit: WindGeschw [m/s]
Lambda = (2*Pi*Radius*Drehzahl)/WindGeschw
daraus abgeleitet:
Windgeschw = (2*Pi*Radius*Drehzahl)/Lambda
Mit
Faktor = (2*Pi*Radius)/Lambda [m]
ergibt sich
Windgeschw = Faktor*Drehzahl [m/s]
Die Drehzahlerfassung wird mittels an der Rotorachse angebrachten Magneten umgesetzt.
Pro Magnet wird ein Impuls (fallende Flanke) ermittelt. Bei M an der Achse montierten
Magneten ergeben sich damit ebenfalls M fallende Impulsflanken für eine Umdrehung:
Umdrehungen = (Anzahl fallender Flanken)/M
Drehzahl = Umdrehungen/(Beobachtungszeitraum) [U/s]
Alternativ lässt sich die Drehzahl auch aus der Zeit zwischen zwei fallenden Flanken
ermitteln:
Delta = Zeit zwischen zwei fallenden Flanken [s]
M = Anzahl der Magnete
Drehzahl = 1/(M*Delta)
Beispiele: M = 4, Delta = 1,00 s ---> Drehzahl = 1/(4*1,00 s) = 0,25 U/s
M = 4, Delta = 0,25 s ---> Drehzahl = 1/(4*0,25 s) = 1,00 U/s
*/
#include <Arduino.h>
#include <util/atomic.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
constexpr int SCREEN_WIDTH {128}; // OLED display width, in pixels
constexpr int SCREEN_HEIGHT {64}; // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
constexpr byte OLED_RESET { 4}; // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
//Pins
constexpr byte pinImpuls {3}; // Pin für Drehzahlimpuls
// Drehzahlmessung
constexpr uint16_t PulseProRotation {4}; // Impulse pro Umdrehung
constexpr float Rotorradius {0.04}; // 4 cm Radius in [m]
constexpr float Lambda {0.4}; // Schnelllaufzahl siehe Datenblatt
constexpr float Faktor {2 * 3.14159 * Rotorradius / Lambda}; //
constexpr unsigned long displayInterval {2000}; // Zeitintervall für Anzeige
unsigned long lastDisplayTime = 0; // Zeit der letzten Anzeige
uint16_t _impulseCount = 0; // Impulszähler
unsigned long _timeBetweenInterrupts = 0; // Zeit zwischen zwei Interrupts in [µS]
void setup()
{
//Pin In Output
pinMode(pinImpuls, INPUT); //Pin Impuls
attachInterrupt(digitalPinToInterrupt(pinImpuls), ImpulsZaehlen, FALLING);
Serial.begin(115200);
OledSetup();
starteSimulation();
}
void loop()
{
anzeige();
checkSerial();
}
void OledSetup() {
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Falls nicht gefunden, stoppen
}
display.cp437(true); // Benutze 'Code Page 437'
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE); // Textfarbe
printToOled("--.-- m/s");
}
void printToOled(char * txt) {
display.clearDisplay();
display.setCursor(5, 0);
display.write("Anemometer");
display.setCursor(10, 40);
display.write(txt);
display.display();
}
void anzeige() {
if (millis() - lastDisplayTime >= displayInterval) {
float windGeschwindigkeit = berechneWindGeschwindigkeit();
lastDisplayTime = millis();
Serial.print("\tWindgeschwindigkeit: ");
Serial.println(windGeschwindigkeit);
char metersec[16];
char txt[16];
dtostrf(windGeschwindigkeit, 5, 2, metersec);
snprintf(txt, sizeof(txt), "%s m/s", metersec);
printToOled(txt);
}
}
float getWindGeschwindigkeitAusPulsAnzahl(uint16_t impulse, unsigned long interval) {
float Umdrehungen = float(impulse) / PulseProRotation;
float Drehzahl = Umdrehungen * 1000.0 / interval;
return Faktor * Drehzahl;
}
float getWindGeschwindigkeitAusPulsAbstand(unsigned long timeBetweenInterrupts) {
if (timeBetweenInterrupts > 0) {
float Drehzahl = 1 / (PulseProRotation * timeBetweenInterrupts / 1000000.0);
return Faktor * Drehzahl;
} else {
return 0.0;
}
}
float berechneWindGeschwindigkeit() {
uint16_t impulseCount;
unsigned long timeBetweenInterrupts;
ATOMIC_BLOCK(ATOMIC_FORCEON)
{
impulseCount = _impulseCount;
_impulseCount = 0;
timeBetweenInterrupts = _timeBetweenInterrupts;
}
float pulseProSek = impulseCount * 1000.0 / displayInterval;
Serial.print("Impulse pro Sekunde: ");
Serial.print(pulseProSek);
// Falls im Display-Intervall kein Impuls verzeichnet wurde, haben wir vermutlich
// Windstille ...
if (impulseCount == 0) {
timeBetweenInterrupts = 0;
}
// Geschwindigkeit aus Puls Anzahl im Display-Intervall
//return getWindGeschwindigkeitAusPulsAnzahl(impulseCount, displayInterval);
//
// Oder alternativ aus dem Abstand der Pulse (über die fallende Flanke ermittelt)
return getWindGeschwindigkeitAusPulsAbstand(timeBetweenInterrupts);
}
void ImpulsZaehlen() {
static unsigned long _lastInterrupt = 0;
_impulseCount++;
if (_lastInterrupt > 0) {
_timeBetweenInterrupts = micros() - _lastInterrupt;
}
_lastInterrupt = micros();
}
/*************************************************************************************/
/* Dieser Teil ist für das Simulieren der Pulse für die Drehzahlmessung erforderlich */
#include <TimerOne.h>
constexpr byte outPin {4};
unsigned long interval;
float zielDrehzahl = 200;
byte togglePin = 0;
void starteSimulation() {
pinMode(outPin, OUTPUT);
togglePin = 1 << outPin;
setDrehzahl(1200);
delay(500);
Serial.println("Drehzahl eingeben: ");
}
void setDrehzahl(float ziel) {
Serial.print("Neue Drehzahl ");
Serial.print(ziel);
if (ziel > 0) {
zielDrehzahl = ziel;
float pulseProSekunde = (zielDrehzahl / 60.0) * PulseProRotation;
interval = round(1000000.0 / pulseProSekunde);
Serial.print("\tPulse pro Sekunde: ");
Serial.print(pulseProSekunde);
Serial.print("\tWindgeschwindigkeit: ");
Serial.print(getWindGeschwindigkeitAusPulsAnzahl(pulseProSekunde * 10, 10000));
Timer1.initialize(interval);
Timer1.attachInterrupt(timer_isr);
}
Serial.println();
}
void checkSerial() {
constexpr int maxC {20};
static char inp[maxC];
static int index = 0;
boolean changed = false;
while (Serial.available()) {
char c = Serial.read();
switch (c) {
case '0' ... '9':
if (index < maxC) {
inp[index++] = c;
inp[index] = '\0';
}
break;
case '\n':
int zahl = atoi(inp);
index = 0;
if (zahl >= 0)
zielDrehzahl = zahl;
changed = true;
break;
}
}
if (changed) {
Timer1.detachInterrupt();
setDrehzahl(zielDrehzahl);
}
}
void timer_isr() {
PIND = togglePin;
delayMicroseconds(3);
PIND = togglePin;
}
/*************************************************************************************/