Forum: Mikrocontroller und Digitale Elektronik Stromzähler (S0) mit ESP32 auslesen


von Frinch (Gast)


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!

von Achim M. (minifloat)


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.

: Bearbeitet durch User
von Veit D. (devil-elec)


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.

von Frinch (Gast)


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.

von Frank M. (ukw) (Moderator) Benutzerseite


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.

von Veit D. (devil-elec)


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.

von Mike R. (thesealion)


Lesenswert?

Frinch schrieb:
> 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).

Welche Spannung kommt denn an deinem S0- an?

Mehr als 3.3V mag der ESP nämlich überhaupt nicht.

von Peter D. (peda)


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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

stimmt auch wieder.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.