Stromzähler (S0) mit ESP32 auslesen

Gast #6832596
Lesenswert?

Hallo,
ich würde gerne die S0-Schnittstelle meines Stromzählers (ein Eltako) 
mit einem ESP32 (Sparkfun ESP32Thing) auslesen.

Der Code ist denkbar einfach:
1
#include <Arduino.h>
2

3
constexpr uint8_t InterruptPin = 25;
4

5
uint32_t TotalNumEdges = 0;
6

7

8
// S0 interrupt handler
9
void IRAM_ATTR ImpulseDetected()
10
{
11
  TotalNumEdges++;
12
}
13

14
// Arduino setup function (called once on startup)
15
void setup()
16
{
17
  Serial.begin(115200);
18

19
  // Attach interrupt to pin
20
  pinMode(InterruptPin, INPUT_PULLDOWN);
21
  attachInterrupt(InterruptPin, ImpulseDetected, RISING);
22
}
23

24
// Arduino loop function (called repeatedly after startup)
25
void loop()
26
{
27
  delay(3000);
28
  Serial.println(TotalNumEdges);
29
}

Hardwareseitig ist das ganze auch sehr simpel: ich versorge S0+ mit den 
5V vom ESP32-Board (kommt vom USB). S0- hängt am Eingangspin des ESP und 
ist mit einem internen Pulldown versehen (siehe Code).

Leider zählt der ESP zu viele Pulse, d.h. pro Puls springt der Zähler um 
1, 2 oder auch 3 nach oben.

Mein erster Gedanke war natürlich, dass der Puls nicht sauber ist. Daher 
habe ich zuerst meinen Logic Analyzer mit dran gehängt, danach noch mein 
Oszi. Beide sind sich darin einig, dass der Puls bildhübsch ist. Da 
schwingt nichts, sowohl die steigende als auch die fallende Flanke sehen 
sehr ordentlich aus.

Also sollte der ESP eigentlich nicht mehrere Flanken pro Puls sehen 
können...

Hat noch Jemand Ideen/Erfahrungswerte? Woran könnte das liegen?
Danke!
#6832605
Lesenswert?

Frinch schrieb:
> Daher habe ich zuerst meinen Logic Analyzer mit dran gehängt, danach
> noch mein Oszi. Beide sind sich darin einig, dass der Puls bildhübsch
> ist.

Dann gönne dem Eingang doch eine Kapazität im Bereich 
Logikanalysator-C_in + Oszi-C_in oder größer. Welche Grenzfrequenz muss 
denn erreicht werden?

Zusätzlich würde ich noch eine Softwareentprellung draufgeben. Dazu 
wertest du die Zeit zwischen den negativen(?) Flanken und den aktuellen 
Pegel aus. Die Zeit muss größer als die Periodendauer der maximalen 
Pulsfrequenz sein. Der Pegel muss dem erwarteten Pegel nach 
Flankenerkennung sein, z.B. Low für High-Low-Interrupt.

mfg mf

PS, bevor hier wieder ein Herr Dannegger seinen Kaffe verschüttet, man 
kann es auch mit Polling probieren, leider wird der Espressif-Stack noch 
bedient. Man kann also mit fast garnix rechnen.
#6832717
Lesenswert?

Hallo,

man kann direkt die Interruptnummer angeben oder die Pinnummer über eine 
zusätzliche Funktion.
https://www.arduino.cc/reference/de/language/functions/external-interrupts/attachinterrupt/
Bleibt man bei den typischen Pinnummern, dann lieber so.
1
attachInterrupt(digitalPinToInterrupt(InterruptPin), ImpulseDetected, RISING);

Derzeit fängt sich der offene Interruptpin irgendwelche Signale ein.
Gast #6832729
Lesenswert?

Veit D. schrieb:
> man kann direkt die Interruptnummer angeben oder die Pinnummer über eine
> zusätzliche Funktion.

Echt jetzt!? faceplam

Danke für den Tipp, das erklärt einiges. Ich war irrtümlich der Meinung, 
man müsste direkt die GPIO-Pin-Nummer übergeben... Klar, dass ich so nur 
Mist gemessen habe.

Ich werd's dann heute Abend mit digitalPinToInterrupt probieren.
Moderator Persönliche Seite #6832736
Lesenswert?

Frinch schrieb:
1
uint32_t TotalNumEdges = 0;
2
// S0 interrupt handler
3
void IRAM_ATTR ImpulseDetected()
4
{
5
  TotalNumEdges++;
6
}
Da hier TotalNumEdges in einer ISR verändert wird, sollte man dies dem 
Compiler kundtun:
1
volatile uint32_t TotalNumEdges = 0;
Ohne dieses "volatile" wird es ein Glücksspiel, ob man korrekte Werte 
bekommt, wenn man in loop() auf TotalNumEdges zugreift.
#6832777
Lesenswert?

Hallo,

upps, das fehlende volatile habe ich übersehen. Zum Schluss noch 
TotalNumEdges vor Ausgabe Interrupt geschützt (atomic block) auslesen 
und es wird funktionieren.

Damit es übersichtlicher wird vielleicht den Pinnamen umbenennen in 
"messPin" oder ähnliches. Mehr der Aufgabe entsprechend weniger der 
Hardwarefunktion.
1
attachInterrupt(digitalPinToInterrupt(messPin), ImpulseDetected, RISING);

Maximale Erfolge.
#6832930
Lesenswert?

Veit D. schrieb:
> attachInterrupt(digitalPinToInterrupt(messPin), ImpulseDetected,
> RISING);

Ja man sollte es so benutzen, wie es in der Arduino-Referenz angegeben 
ist. Dazu ist sie ja da.

Trotzdem können sich aber auch leicht Störungen einkoppeln.
Der ESP32 taktet ja mit 240MHz, d.h. eine Störnadel von >4,2ns löst 
garantiert auch einen Interrupt aus.

S0-Pulse müssen ja >30ms einen Pegel beibehalten. Eine Entstörung per 
Polling im Timerinterrupt ist also deutlich störfester.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren