Genaue Strommessung mit dem Arduino und dem ACS712 Hall-Sensor mittels Oversampling

Für ein Projekt habe ich einen ACS712 Hall-Effekt Stromsensor an einen Arduino angeschlossen, um den Strom und die Scheinleistung an einer Steckdose zu messen. Der ACS712 sorgt für eine Isolation zwischen 230V Netz und Arduino. Die Schaltung an sich ist trivial. Der ACS712 wird vom Arduino mit 5V versorgt. Der Ausgang des ACS712 geht an den A0 Pin des Arduino. Ich habe die 20A Version des ACS712 genommen, um den kompletten Bereich einer Steckdose abdecken zu können. Das Ganze habe ich zum Testen in das Gehäuse einer ausgeschlachteten Funksteckdose gepackt:
ACS712-Aufbau
Da der ACS712 an Wechselstrom angeschlossen ist, wird der Strom in eine Wechselspannung umgesetzt. Der Mittelwert der Wechselspannung ist die halbe Versorgungsspannung, also 2.5V. Die Amplitude der Wechselspannnung ist linear zum Strom durch den ACS712; bei der 20A Version 100mV/A. Bei Wechselstrom muss man das Ausgangssignal des ACS712 abtasten und den effektiven Strom berechnen:
Ieff^2 = Sum(I(t)^2)
Zunächst habe ich diese Abtastung per analogread realisiert. Die Abtastdauer habe ich auf 100ms gelegt. Das ist die kürzeste Dauer, die ein ganzahliges Vielfaches der Periodendauer für 50Hz und 60Hz Wechselstrom darstellt. Das Ganze hat funktioniert, war aber nicht wirklich genau.

Die Gründe:
1. Der ADC des ATmega 328P hat 10Bit Auflösung. Bei der 20A Version des ACS712 ergibt sich bei 100mV/A eine Genauigkeit der Einzelmessung von 49mA was bei 235V ca. 11 Watt entspricht.
2. Der Nullpunkt der Spannung liegt nicht exakt auf 512. Er liegt auch nicht exakt auf einem der Diskretisierungswerte.

Die Lösung:
1. Oversampling
Man kann durch eine geeignete Mittelung von Messwerten des ADC die Auflösung deutlich über die 10Bit erhöhen. Die Details dazu hat Atmel in der Application Note AVR121 beschrieben:
http://www.atmel.com/Images/doc8003.pdf
Kurz gesagt steht da: wenn man einen Wert mehrfach misst, kann man die Auflösung erhöhen. Die Wurzel der Zahl der Messungen gibt den Faktor der Erhöhung der Auflösung an. Das funktioniert zunächst einmal nur bei einem konstanten analogen Signal. Das liegt hier nicht vor. Wir wissen aber, wir wir die Einzelwerte zu summieren haben. Und wir wissen, dass die Summe der Einzelwerte über eine oder mehrere komplette Perioden Null ist.
Um dies Ausnutzen zu können bin ich von der Benutzung von analogread() weggegangen und habe das Sampling durch eine direkte Programmierung der ADC Register realisiert. Damit ist die Programmierung für den ATmega 328P bei 16MHz CPU Takt ausgelegt (Arduino Nano, Uno, MiniPro etc). Für andere Mikrocontroller sind ggf Codeänderungen nötig.
Im Datenblatt des ATmega328 steht, dass der ADC-Takt für optimale Genauigkeit auf 100-200kHz eingestellt werden soll. Deshalb wird im Code über das ADCSRA Register ein Faktor 1/128 zwischen CPU und ADC Takt eingestellt. Weiterhin braucht eine ADC Wandlung 13 ADC-Takte. Theoretisch ergibt sich so eine Samplerate von 9600 Abtastungen pro Sekunde. Praktisch erreicht der Code 812 Abtastungen in 100ms; es wird ja auch noch ein wenig gerechnet und der Loop ist auch nicht umsonst. Theoretisch ergibt sich damit eine Genauigkeit zwischen 14 und 15 Bit. 14 Bit entspricht ca. 0,6mA oder 0,15W. In der Realität ist aber die Diskretisierung nicht ideal und Rauschen spielt auch eine Rolle.

2. Behandlung des ADC-Offset / Nullpunkts
Der Code versucht den Nullpunkt des gesampleten Signals zu schätzen. Das geht, weil die Summe der Einzelwerte über eine oder mehrere komplette Perioden hinweg Null sein sollte. Dies kann man zweimal Ausnutzen:
a) Nach jeder Sampleperiode wird der Mittelwert bestimmt und als ADC-Offset für die nächste Sampleperiode eingestellt. Damit werden unnötig große Zahlen in der quadratischen Summe vermieden. Allerdings wirkt diese Korrekur nur auf die nächste Sampleperiode und die Korrektur wirkt nur „ganzzahlig“.
b) Der ADC-Offset liegt typischerweise nicht exakt auf einem Diskretisierungswert sondern z.B. bei 509,24. Damit bleibt der Effekt von 0,24 unberücksichtigt; in diesem Beispiel 2,6W. Da wir aber wissen, dass die Summe der Abtastwerte Null ist, können wir den ADC-Offset berechnen und nachträglich korrigieren. Dazu wird in der Sampleschleife neben der Quadratsumme auch die Summe der Messwerte berechnet. Es gilt dann:
Sum((I(t)+offset)^2) = Sum(I(t)^2) + 2*offset*Sum(I(t)) + offset^2*NumSamples

Mit diesen Ideen habe ich folgenden Code programmiert. Die Code gibt die Scheinleistung in Watt aus.
ACS712_AC_20A

Praktische Anwendung – Test 1 : Lastfrei
Ohne angeschlossene Last sollten wir 0 Watt messen. Das klappt auch prima – Mittelwertbildung und Offset-Korrektur funktionieren:
Precise AC Current Measurement with ACS712 - Stefan Thesen 09/2016
0.00
0.00
0.00
0.00
0.00
0.00

Praktische Anwendung – Test 2 : 40W Glühbirne
Mit einer 40Watt Glühbirne habe ich folgende Messwerte erhalten:
38,83
38,60
38,65
38,06
38,17
38,53
38,42
38,64
38,59
38,26
38,86
38,64
38,25
38,71
38,27
38,60
38,68

Anhand der Messwerte bestätigt sich eine Reproduzierbarkeit, die grob in die Richtung der theoretisch vorausgesagte Genauigkeit geht. Der Mittelwert dieser Werte ist 38,52W. Mein Energiemessgerät zeigt 38,6W an:
Energiemessgeraet
Ich möchte aber klar darauf hinweisen, dass ich hier keine ernsthafte Eichung oder Validierung der Messwerte durchgeführt habe. Der ACS712 führt auch noch eine Ungenauigkeit ein. Auf 2-3 Watt sollte es stimmen. Bitte auch beachten, dass wir hier die Scheinleistung sehen; eine Phasenverschiebung zwischen Strom und Spannung wird nicht gemessen.

Ergänzung (02.10.15):
Die Stromversorgung des Arduino muss sehr stabil sein, damit die o.g. Genauigkeiten erreicht werden. Wenn das Netzteil „brummt“, wirkt sich das spürbar aus.

63 Gedanken zu „Genaue Strommessung mit dem Arduino und dem ACS712 Hall-Sensor mittels Oversampling

  1. andreas

    hallo stefan, tolle realisierung – kompliment! ich beschäftige mich auch gerade mit dem thema, da ich mir eine applikaltion überlege, wo ich via webserver abfragen kann ob ich zu hause das licht o.ä. angerschaltet habe lassen. wenn ich nun einen verbraucher angeschaltet habe, zeigt dein programm ziemlich genau die leistung, zum beispiel bügeleisen 2000w, allerdings ohne last sehe ich 7w und nicht null … gruss aus wien

    Antworten
    1. ST Beitragsautor

      Hallo Andreas,

      versuche es bitte mal mit sehr kurzen Kabeln und gelöteten Verbindungen und einer Batterie als Spannungsquelle. So wie wir hier samplen, können Brummen oder sonstige Dreckeffekte deutlich merkbar sein.

      Gruss Stefan

      Antworten
  2. Christian

    Hallo,

    vielen Dank für den Sketch. Lange im Netz danach gesucht!

    Er funktioniert gut bei mir, allerdings ebenfalls mit Schwankungen ohne das am ACS712 eine Last anliegt. Ich hatte zusätzlich noch ein kleines OLED Display an meinem Arduino Nano laufen, was die Schwankungen noch verstärkt hat.

    Eine Frage, was passiert wenn ich die Zeit in deinem Sketch hochsetze, also z.B. auf 15 Sekunden?

    unsigned long gulSamplePeriod_us = 15000000;

    Hintergrund ist das ich eine Funkübertragung mit einem RFM12B machen möchte, sobald eine Last anliegt. Muss ich noch andere Teile aus dem Skript anpassen oder reicht die Anpassung der Zeit?

    Gruß
    Christian

    Antworten
    1. ST Beitragsautor

      Hallo Christian,
      wenn das OLED Display das Problem verschärft, probier mal mit Stützkondensatoren und einem besser stabilisierten Netzgerät zu arbeiten. Ich vermute stark, dass auch hier die scheinbare Leistung aus Spannungsschwankungen entsteht. Ganz weg habe ich sie bei mir nur mit einer Batterie als Spannungsquelle bekommen.

      Die Sample-Dauer kannst Du natürlich hoch setzen. Die Dauer sollte ein ganzzahliges Vielfaches von 20ms (=Periodendauer bei 50Hz). Wenn Du viele Sekunden lang samplest, dann könnten die long Datentypen überlaufen. Grob überschlagen sollten 10s gehen. Ich vermute aber, dass das alles nichts ändert. Wenn die Spannungsquelle periodische Schwankungen einführt, dann mitteln diese sich nicht heraus.

      Ansonsten musst Du halt eine Schwelle definieren unterhalb derer Du die Last ignorierst. Nach meiner Beobachtung sind die Messungen überhalb dieser Schwelle ohne Abzug korrekt. Konkret habe ich bei einem recht instabilen Netzgerät eine scheinbare Last von 18W beobachet. Die oben schon benutzte 40W Glühbirne erzeugt hier aber auch 39W auf dem seriellen Monitor.

      Viel Erfolg
      Stefan

      Antworten
  3. Christian

    Hallo Stefan,

    danke für die Info! Ich werde es heute Abend mal ausprobieren.

    Mir ist auch aufgefallen, dass der ACS712 im Ruhezustand etwas „nervös“ ist, unter Last aber über 0 verlässliche Werte liefert. In meinem Sketch habe ich einen Schwellwert definiert. Unterhalb des Wertes wird nicht gefunkt (ich sende die Daten mittels RFM12B auf einen RaspberryPI mit Emoncms).

    Sehr schön. So lässt sich mit wenig Geld ein verlässlicher Sensor bauen.

    Ich frage mich nur ob der ACS712 auch schon mit 3.6 Volt funktioniert? Hast du da irgendwelche Erfahrungen? In den Specs lese ich nur 5V.

    Ansonsten toller Blog! Viele interessante Themen.

    Gruß
    Christian

    Antworten
    1. ST Beitragsautor

      Hallo Christian,
      Danke für die Rückmeldung. Ich habe keine Erfahrungen mit dem ACS712 bei 3,x Volt. Das Datenblatt sagt 4.5V min. Der Sensor an sich ist komplett analog. Der Hall Effekt wird auch bei 3V da sein 🙂 ; die Frage ist, was der Vorverstärker dazu sagt. Da bleibt wohl nur der experimentelle Ansatz.

      Gruss Stefan

      Antworten
  4. Marcus

    Hallo Stefan,

    erstmal ein Lob für die tolle Beschreibung und Umsetzung ! Ich habe deinen Code „ACS712_AC_20A“ verwendet und die Zeile “ float gfACS712_Factor = 27.03f; “ angepasst da ich die 5A Version im Einsatz habe. Im Leerlauf bekomme ich dauerhaft Werte zwischen ca. 25-28 Watt angezeigt. Wenn ich eine 40 Watt Glühlampe anschließe geht die Anzeige auf 39-40 Watt, wenn ich kleinere Lasten wie z.B. 8 Watt (Energiesparlampe) anschließe bekomme ich diese nicht angezeigt.

    Hast Du eventuell einen Tipp für mich ?

    Gruß marCus

    Antworten
    1. ST Beitragsautor

      Hi Marcus,

      versuch mal den Arduino und des ACS mit einer Batterie zu versorgen, so dass Du eine perfekt stabile Versorgungsspannung hast. Wenn die Versorgungsspannung nicht perfekt stabil ist, geht das in das Sampling des AC Signals rein. Ebenso bitte schauen, dass Du kurze Kabel zwischen Arduino und ACS hast und da kein „Dreck“ einkoppeln kann. Ggf kannst Du mal mit einem Oszi schauen, ob die Signale sauber sind.

      Viel Erfolg,
      Stefan

      Antworten
    2. fritz

      Hallo Marcus,

      lege den Ausgang des ACS oder den Analog-Eingang mit einen Widerstand von 10k gegen Masse.
      Somit werden „wilde“ Einkopplungen unterbunden!
      Gruß

      Antworten
  5. Markus T

    Hallo Stefen,
    Bin durch zufall hier gelandet und fand dein Projekt Beitrag recht interessant. Ein paar verbesserungsvorschläge um deine und eure Probleme zu beheben.

    Erstens. ADC Spannungskalibrierung über zwei punkte um einen konkreten Adc offset, sowie den Verstärkungsfehler ermitteln zu können. APPNOTE

    Zweitens. Dein Atmel verfügt, wenn ich mich recht entsinne, über einen modus um adc messungen über eine interne referenzspannungsquelle mit 1,7V zu machen 😉 häng an den VCC einen hochohmigen Spannungsteiler um eine spannung kleiner 1.7V zu erzielen und mess diese spannung. Nun kannst du vor jeder messung dein VCC überprüfen, um das ratiometrische verhalten des Hall sensors auszugleichen.

    Zu guter letzt, vielleicht hilft noch ein ferritkern um dein zu messendes kabel, um emv-technisch das ganze noch zu stabilisieren

    Liebe Grüße aus dem Schwarzwald
    Markus

    Antworten
    1. ST Beitragsautor

      Hallo Markus,

      vielen Dank für Deine Anmerkungen und Gedanken. Ein paar Kommentare dazu:

      Stichwork Ferritkern: Da stimme ich völlig mit Dir überein. Wenn man lange Kabel nehmen muss, dann sollte man abschirmen bzw mit Ferritkernen hochfrequenze Störungen wegfangen. Falls es die Schaltung zuläßt empfehle ich die Kabellänge zwischen ACS712 und 328P zu minimieren. Auch Steckverbindungen auf einem Breadboard sind ein beliebtes Problem. Besser löten und durch kurze Kabel erstmal möglichst nichts einfangen.

      Stichwort 1.7V: Mit ist nicht bekannt, dass der 328P eine interne 1.7V Referenz hat. Es gibt eine 1.1V Referenz. Man kann Genauigkeit gewinnen, wenn man den Messbereich einschränkt. Im Codevorschlag wird der ADV mit einer 5V Referenz betrieben. Wenn wir die max. 16A einer typ Steckdose abbilden wollen, dann brauchen wir bei dem verwendeten ACS712-20A (100mV / A) einen Bereich von 0 – 3.2Volt (oder +/- 1.6V) am ADC. Geht man auf 1.1V herunter, dann schränkt man sich auf 1/3 des meßbaren Strombereichs ein und landet bei 6,6A. In diesem Fall würde ich dann eher zum ACS712 in der 5A Variante greifen. Diese hat eine Sensitivität von 185mV/A. Es dürfte genauer sein, einen größeren analogen Wert zu sampeln.

      Stichwort Zweipunkt Kalibrierung: Das könnte man in der Tat tun, um die Auflösung des ADC zu verbessern und Fehler zu minimieren. Wenn ich die Schaltung mit einer Batterie (= nahezu ideal stabile Spannungsquelle) betreibe, so langt die Messgenauigkeit mehr als aus. Ich vermute, dass diese verbesserte Kalibrierung keine reale Verbesserung bringt.

      Woher kommen dann aber die Fehler? – Die Fehler kommen nicht aus konstanten Störungen, Offsets oder Verstärkungsthemen im 328P oder ACS. Das Kernproblem sind Schwankungen der Versorgungsspannung, die während der Sampling Periode von 100ms auftreten und höherfrequent sind. Letztlich kommen diese Probleme typischerweise aus der Spannungsversorgung selbst: Die Schaltung hängt am Rechner, der mit einem Schaltnetzteil läuft, an einem Handynetzteil (auch Schaltnetzteil) etc. Die Abweichungen lassen sich auch sehr gut verändern, wenn man die Stabilität der Versorgungsspannung verändert. Die Messwerte werden nahezu ideal, wenn man einen Linearen Festspannungsregler (LM7805 o.ä.) nimmt und dahinter einen sehr dicken Elko (2000 Mikrofarad) setzt oder aber die Spannnungsversorgung mit einer Batterie erledigt. Wir versuchen hier mit einem 20A Sensor Ströme im 10mA Bereich bei einer Sensitivität von 100mV/A zu messen –> 1mV Änderung im Signal. Das ist schon sehr wenig. Und damit wären wir wieder bei einem 5A Sensor, wenn man den Messbereich einschränken kann. Oder man nimmt einen deutlich größeren HW-Aufwand in Kauf und misst mit anderen bzw adaptiven Verfahren.

      Gruss Stefan

      Antworten
  6. Stefan Beier

    Hallo Stefan,
    schönes Projekt!
    Was müsste ich machen, wenn ich den Stromwert (nicht den Leistungswert) mobil auf einen Display (16×2) angezeigt haben möchte?.
    Wäre so etwas möglich?
    Schön wäre auch noch, wenn der Max. Wert angezeigt werden könnte.

    Gruß Stefan

    Antworten
    1. ST Beitragsautor

      Hallo Stefan,

      lass uns das in 3 Fragen zerlegen:
      Strom: Wenn Du den Strom anzeigen möchtest, musst Du im Code nur den Teil am Ende entfernen, der die 235V dranmultipliziert werden (gfLineVoltage).

      Maximal Wert: Lege Dir eine globale Variable gfMaxCurrent an, setze sie in setup auf 0.0 und schreibe den aktuellen Stromwert hinein, wenn er größer ist als der bisherige Wert von gfMaxCurrent.

      Display 16×2:
      Vermutlich meinst Du eines dieser Hitachi HD44780-kompatiblen Displays. Nimm einfach die Schaltung und den Code des entsprechenden Arduino Beispiels:
      https://www.arduino.cc/en/Tutorial/HelloWorld
      Die Schaltung baust Du einfach zu dem Vorschlag hier dazu. Hier wird der A0 Pin verwendet. Dieser bleibt im Beispiel mit dem Display frei. Mit lcd.print kannst Du dann den Wert ins Display schreiben. Du musst eigentlich nur beide Codes mergen.

      Gruss Stefan

      Antworten
      1. Stefan Beier

        Hallo Stefan,
        danke für deine Tipps. Ich werde die mal ausprobieren.
        Wo in deinem Code wird der analoge Eingang A0 als Eingang gesetzt? Irgendwie kann ich es nicht finden. Kann man den auch auf einen anderen Pin umschreiben?

        Mein Vorhaben ist es vier Ströme (AC) gleichzeitig zu messen und anzuzeigen. Konkret handelt es sich um 4 Motortreiber, die miteinander verglichen werden sollen, um eine erhöhte Stromaufnahme (z.B. durch schwergängige Lager) festzustellen. Wäre das überhaupt mit einem Arduino Pro Mini und 4 Stromsensoren zu realisieren, oder wäre der Arduino damit überlastete? Da es ja bei der AC Strommessung auf zeitlich abhängige Messungen ankommt.
        Es gibt auch noch andere Projekte, bei denen wird das Signal (AC Spannung) der Stromsensoren mittels einer Schaltung in eine Gleichspannung umgewandelt und dann im Arduino ausgewertet. Wäre das in meinen Fall sinnvoller?

        Gruß Stefan

        Antworten
        1. ST Beitragsautor

          Hallo Stefan,

          wenn Du die anderen Pins (A1..A8) nutzen willst, musst Du das in dem ADMUX Register einstellen. Hier ist eine schöne Beschreibung der Register:
          http://www.mikrocontroller.net/attachment/279315/adwandler.pdf
          Auf Seite 2 steht ca. in der Mitte, welche Werte Du zu ADMUX dazuaddieren musst, um die anderen Pins zu nutzen: Einfach die Nummer des Pins. ADMUX = 0x41 würde somit A1 nutzen.

          Zu den anderen Fragen kann ich nur mal mit Halbwissen kommentieren:
          Du kannst mit einem MiniPro (also 328p) nicht wirklich parallel messen. Du könntest aber in den ersten 100ms Motor 1 ausmessen, danach 100ms lang Motor 2 usw. Wenn Deine Problemstellung also im Bereich von mehreren Sekunden konstante Lasten/Ströme an den Motoren hergibt, dann geht es mit einem 328p. Wenn Du wirklich parallel messen musst, dann brauchst Du mehrere MiniPros oder alternativ einen anderen Microcontroller der mehrere ADCs hat.
          Intuitiv würde bei einer MiniPro Lösung einen Master MiniPro an die anderen MiniPros per Signalleitung und Interrupt das Kommando zum Messstart geben. Die Messwerte müsstest Du dann auf einer Art Bus einsammeln. Man könnte auch eine kaskadierte serielle Kommunikation machen.

          Andere Spromsensoren kannst Du natürlich auch nehmen und dann ggf daraus ein Gleichstrom Signal erzeugen. Ob das sinnvoller ist, hängt vermutlich von den Zeitkonstanten des Problems ab. Wenn Du Gleichstrom erzeugst, ist da wohl im analogen Teil eine Integration drin (Kondensator?). Keine Ahnung, ob das wesentlich schneller ist als die 100ms.

          Und noch eine Anmerkung: Falls die Motoren mit einer Phasenschnittsteuerung oder per PWM angesteuert werden, muss ggf auch das Sampling angepasst werden. Aktuell ist das für 50 bzw 60Hz AC ausgelegt.

          Gruss Stefan

          Antworten
  7. Markus T

    Hallo Stefan,
    Bei meinem Vorschlag mit der Referenzspannung meinte ich nicht, dass du den Hall Sensor damit misst, sondern deine VBat bzw VCC.
    Der Hall hat ein ratiometrischen verhalten; Vcc =5V, B=0H, Uhall=2,5V bei Vcc=4,8V, B=0H, Uhall=2,4V

    Da die Interne Referenzspannung relativ stabil ist, also 500mV keine Veränderung hervorrufen lässt sich damit Vcc bzw Vbat ganz einfach über einen Spannungsteiler überwachen.

    Die Adc kalibrierung dient wiederum dem ganzen um die interne referenzspannung zu messen, da die 1,1V +/- 0,1V betragen kann.

    Ich werd den Code mal dafür schreiben, allerdings erst im Sommer, wegen Umzug und Familienzuwachs.

    Lg aus dem Schwarzwald

    Antworten
  8. Franz

    Hallo Stefan,

    Danke für Deinen Beitrag!

    Ich hatte mein Projekt mit getrennter Messung von 3 Phasenspannungen und – Strömen schon fertig, als ich Deinen Beitrag hier fand. Ich will aber gerne ein paar gemachte Erfahrungen weitergeben:

    1) Hardware

    Ich verwende insgesamt 6 Arduino mini pro und zwei Arduino Mega, jeweils einen für Strom und Spannung x 3 Phasen und die Megas für die Weiterverarbeitung. Experimentiert habe ich auch mit den ACS-Teilen. Dabei waren mir dann aber die Offset-Werte im Wege, die immer eine Abweichung von ein paar zig Millivolts hatten. Außerdem wollte bzw. konnte ich die Stromleitungen nicht auftrennen.

    Also habe ich Stromtrafos verwendet. Das eine Bein lege ich (zwischen 0 und 5Volt) an einen genauen Spannungsteiler 500/500 Ohm und dazwischen einen 50-Ohm Cermettrimmer mit 20 Gängen. Das andere Bein kommt an A0. So kann ich dann am Ende mit Strom I=0 genau auf Null Ampere Messwert abgleichen.

    Die 230V-Spannungen messe ich über einen Spannungsteiler 1020 kOhm / 7,2 kOhm und einen 500 Ohm Cermettrimmer dazwischen. Das eine Bein hängt an A0. Den Schutzleiter (!) nutze ich als Bezugspunkt und hänge ihn ebenfalls an einen Spannungsteiler wie oben.

    Selbstverständlich ist die Spannungsabteilung über Optokoppler und getrennte Stromversorgungen galvanisch getrennt. Die Messwerte übertrage ich über Optokoppler an einen Arduino Mega, der später die Berechnungen vornimmt.

    In der Spannungsmess-Abteilung wird vom ersten Arduino Mini der Takt für übrigen Arduinos erzeugt, die diesen über Polling abfragen.

    Apropos Stromversorgung: Strom- und Spannungsabteilungen werden über je ein 9 Volt Schaltnetzteil und einen nachgeschalteten LT1068 mit 5 Volt versorgt, der sehr gut temperaturstabil ist. Ein LT1068 versorgt jeweils 3 Arduinos.

    In dem den 6 Arduinos nachgeschalteten ersten Mega (wegen der seriellen Schnittstellen und des Speichers) werden Ströme und Spannungen miteinander verrechnet sowie die umgesetzte Energie der einzelnen Phasen aufaddiert.

    Der achte Arduino (Mega) sorgt für die Darstellung der Messwerte über ein LCD-Display (oder auch TFT) sowie die Übertragung zur Datenbank.

    2.) Software

    Ich taste jeweils im Abstand von 500µs jede Periode 40-mal ab und bilde daraus die Mittelwerte über bis zu 20 Perioden.

    Alles zusammen funktioniert sehr gut. Bisher messe ich noch mit maximal 25 Volt aus einem Regel-Trenntrafo und bis zu 10 Ampere. Die Messwerte stimmen sehr gut mit Digital-Volt und Wattmeter überein.

    Allerdings sind die Arduinos mini hierbei schon fast am Ende ihrer Speichrfähigkeit, insbesondere was meinen Plan betrifft, nicht nur 3 Phasenströme sondern insgesamt 8×3 Phasenströme gleichzeitig zu messen. Vielleicht hast Du oder ein Leser Deines Blogs dazu eine gute Idee?

    Gruß aus dem Vor-Harz in den Schwarzwald

    Franz

    Antworten
    1. ST Beitragsautor

      Hallo Franz,

      das ist eine interessante Lösung. Deutlich aufwändiger aber damit vermutlich auch stabiler. Wenn ich das richtig verstehe, dann laufen Dir Minis über, weil Du zuviele Daten speichern musst (40 Abtastwerte x 20 Perioden). Kannst Du die Mittelwerte nicht inkrementell errechnen? – Mache ich in dem oben publizierten Beispielcode auch. Wenn Du das geschickt anstellst, brauchst Du Arrays vermeiden / minimieren.

      Gruss Stefan

      Antworten
    1. ST Beitragsautor

      It is certainly a good idea to incorporate the internal voltmeter. Thanks for posting that. In my code, you should set the voltage. The code you published uses analogread. You might want to try the sampling code used in the example posted here, which is more stable. Would combine both advantags.
      Moreover one key problem is not solved by both codes: If the power supply is instable e.g. by USB, this usually does not give a constant voltage. Thus the sampling happens under unstable conditions and that creates noise, which leads to wrong results. In my experience a well stabilized power supply can be more key to accuracy than the exact voltage.

      P.S.: your page currently seems to work only for desktop browsers.

      Antworten
  9. Danny Zenger

    Da ich kein Arduino verwende, sondern einen ESP8266 habe ich das Problem, das ADCSRA und ADMUX nicht benutzbar sind:

    was not declared in this scope

    Hat einer einen Tipp was ich wie umschreiben muss ?

    Antworten
    1. Günther

      Hallo zusammen,

      die Frage stelle ich mir auch. Wäre klasse wenn man den gemessen Wert direkt über den ESP8266 und Wifi an eine Hausautomatisierung z.B. übertragen könnte.
      Sonst eine echt super Lösung und sehr genau von der Messung her!

      Antworten
  10. Michael Lange

    Hallo,
    sehr interessante Anleitung, ich würde gerne mal einen Blick in den Code werfen, nur kann ich diesen nicht finden, wahrscheinlich sehe ich den Wald vor Bäumen nicht.
    Wäre sehr nett, wenn ich den Code haben dürfte, danke
    Michael

    Antworten
  11. Paul Rengier

    Hallo,

    ich habe das Programm so übernommen und mit einem ACS712 funktioniert es einwandfrei. Jetzt habe ich die Loop() unbenannt in Sample() und versuche in der Loop() mit einer For-Schleife 4 Ports anzusprechen:
    for (i=0; i<4; i++) {
    sample(i); }
    in sample(byte port) wird dann der Port mit der Anweisung " ADMUX = 0x40 | port;" auf A0-A3 gesetzt.
    Jetzt kommt das Unerwartete: sample() liefert keine Werte (0,0)! Wenn ich die For-Schleife durch eine Konstante (z.B. i=2;sample(i);) ersetze, wird auf die Konstante der korrekte Port (aber eben leider nur dieser) gelesen und ein plausibles Ergebnis ausgegeben.

    Was mache ich falsch?
    Paul

    Antworten
    1. Paul Rengier

      Hallo,

      mittlerweile bin ich der Lösung auf der Spur. Die Sample() Funktion bringt als erstes Ergebnis nach der Umstellung von ADMUX auf einen anderen Port immer das Ergebnis 0,0. Man muss die Funktion mit dem gleichen Portwert ein 2. Mal durchlaufen, um einen Messwert zu bekommen. Irgendwie verstehe ich das Timing aus dem Datenblatt nicht ganz. Immerhin werden in der while-Schleife ca. 800 Messungen gemacht.

      Meine Idee, die Messungen 2x zu stoppen und wieder zu starten halfen auch nichts:
      ADMUX = 0x40 | port;
      ADCSRA = 0x87;
      // 1st sample is slower due to datasheet – so we spoil it
      ADCSRA |= (1 << ADSC);
      while (!(ADCSRA & 0x10));
      ADCSRA = 0x00;
      ADCSRA |= (1 << ADSC);
      while (!(ADCSRA & 0x10));

      Ich habe jetzt einen Workaround, indem ich einen DummySample (inklusive der FP Berechnungen, ein delay() an der stelle bringt nichts) einführe, aber verstehen wäre besser….

      Paul

      Antworten
      1. ST Beitragsautor

        Hallo Paul,

        ich vermute, dass Du die ADC Eingänge auf dieser Samplefrequenz nicht sauber multiplexen kannst. Der 328p hat ja nur einen ADC. Wenn Du die Eingänge so schnell wechselst, dann wird der Eingangskondensator des ADV permanent an eine andere Spannung angeschlossen. Kommt dann noch etwas Sinnvolles raus?
        Vermutlich ist es besser, erst 100ms auf Eingang 1 zu samplen und auszuwerten, dann EIngang 2 usw.

        Gruss Stefan

        Antworten
  12. Yannic

    Hallo,
    habe den Sketch auch dankend übernommen. Ich nutze einen Arduino Mega 2560.
    Der Sketch wurde in ein anderes Programm übernommen, welches allerdings an A10-A12 noch weitere Analogwerte erfassen soll.
    Mein Problem ist nun, dass alle Werte verfälscht werden, sobald ich über A10-A12 auch noch was einlesen möchte. Die Wattangabe ist zu hoch und bei den anderen Eingängen werden auch Werte angezeigt obwohl die Eingangsspannung 0V ist. Diese anderen Werte ändern sich parallel zu der falschen Leistungsangabe, welche immer etwas im Kommabereich schwankt.Hätten Sie da einen Tip für mich, was ich an dem Sketch ändern muss, damit A10-A12 nicht auch von der Leistungsmessung beeinflusst werden?

    Grüße

    Yannic

    Antworten
    1. ST Beitragsautor

      Hallo Yannic,

      Als Schuss ins Blaue : schau mal ob Dein Code etwas an der Referenz Spannung ändert und ob sich die beide Codes sich dabei ggf in die Quere kommen. Zum Zweiten sollte dein zweiter Code möglichst keine Interrupts anlegen o.ä. Du musst ggf dafür sorgen, dass der Sample Code nicht von Interrupts unterbrochen wird.

      Gruss Stefan

      Antworten
  13. apollo2mond

    hallo,
    wieder ein schöner beitrag, wie so viele im internet, der“sinnfrei“ ist! warum? weil die genauigkeit- sprich die toleranz durch oversampling NICHT verbessert werden kann, sondern NUR die Auflösung der messungen. beispiel: eine waage mit 3,5 ist nicht genauer als eine mit 4,5 anzeigestellen, wenn das messprinzip bzw. die fehlertoleranzen unveränder sind!
    hier mal was zum thema toleranzen der strommessung mit ACS712 und attiny85, die 20A version schaltet die adc referenz um, daher zwei angaben.
    //tolerance estimation dc/@25C => sensor 20A: +-0.3W/0-10A, +-0.5W/10-20A, sensor 5A: +- 0.2W/0-5A
    // Uref 2.56: 2.3-2.8V, 2.53V@25C, Uref 1.1: 1.0-1.2V, 1.09V@25C
    // sensor sensitivity +-1.5% 20A: 96-104mV/A, 5A: 180-190mV
    // adc/current value: +- 3lsb
    // vcc ldo regulator +-0.4%, Uoffset 2.5V: 2.49-2.51V

    Antworten
    1. ST Beitragsautor

      Apollo,

      die Qualität des Kommentars Inhalts und der Stils erinnert mich irgendwie an ein Dieter Nuhr Zitat.
      Ich zitiere: „Wenn man keine Ahnung hat: Einfach mal Fresse halten“ aus „Nuhr nach Vorn“, 4. Mai 1999

      Die notwendig Verbesserung der Auflösung kommt aus der Art und Weise, wie der ADC implementiert ist (Oversamling; 10 Bit langen nicht). Siehe auch das oben referenzierte Dokument von Atmel. Wenn man die Referenzspannung genau kennt, ergibt sich auch eine eine recht gute Genauigkeit. Das wird in den vorherigen Kommentaren auch diskutiert.
      Der ACS selbst wird im Datenblatt im wesentlichen durch einen total Output Error von 1,5% beschrieben. Das widerspricht den gezeigten Werten nicht. Und last but not least ist die Theorie oben auch durch ein Experiment belegt. Es funktioniert. Das Gegenteil zu behaupten ändert die Tatsachen nicht.
      Da Du ohne Quelle zitierst, ist mit dem Kommentar auch sonst wenig anzufangen.

      Sorry für die klare Antwort.

      Gruss Stefan

      P.S.: Im Code ist tatsächlich ein kleiner Rechenfehler, der aber kaum etwas ausmacht.
      P.P.S: Weitere Kommentare von apollo2mond mit sinkendem Inhalt und wachsender Despekierlichkeit habe ich geblockt.

      Antworten
  14. pi

    Hallo,

    ich setzte den Code in einer eigenen Schaltung ein. Es funktioniert. Ich bekomme bei Glühbirnen (30W-1000) bis hin zu Heizungen (3000W) gute &stabile Werte, die auch zu meinem Energiekostenmeßgerät passen. Die Abweichung sind nur ein paar Prozent. Bei Motoren sind die angezeigten Watt Werte zu hoch. Was ist da los?
    Wichtig ist eine stabile Versorgungsspannung. Hatte die Schaltung erst am USB Port. Da waren die Ergebnisse nicht stabil.

    Antworten
    1. ST Beitragsautor

      Hallo pi,

      die Messung hier mißt nur den Strom. Die Leistung wir errechnet. Dabei wird eine ohmsche Last angenommen und eine Wechselspannung mit 235V. Beides ist eine Annahme. Bei einem Motor ist insbesondere die Annahme einer ohmschen Last falsch (Stichwort Scheinleistung und Wirkleistung). Auch bei einer LED Lampe werden zu hoche Werte für die Leistung herauskommen.
      Schau mal, ob Dein Energiemessgerät die Messung der Scheinleistung anbietet. Hier sollten die Werte zusammenpassen.
      Und ja, Du hast Recht: Die Versorgungsspannung muss stabil sein und möglicht genau 5V betragen.

      Gruss Stefan

      Antworten
  15. Georg Nüssgens

    Frage: Wo kann man den ACS712 auf der In Deinem Beitrag abgebildeten Platine aufgelötet beziehen? Ich würde gerne die Strommessung an mehreren Rohrmotoren ausprobieren, wobei mich nicht der absolute Stromverbrauch interessiert,sondern die Differenz der Messwerte. Sprich, wenn einer der Motoren ausfällt, dann sollen die anderen sofort stehenbleiben. Wie muss in diesem Fall der elektrische Anschluss aussehen? Gruß Georg

    Antworten
    1. ST Beitragsautor

      Hallo Georg,

      such einfach bei ebay, Amazon oder aliexpress nach „ACS712“. Bei Ali geht es ab 1,10€ inkl. Versand los. Aus Deutschland kosten die Dinge ca. das Dreifache.
      Ich weiss nicht genau, was Du vor hast. Es klingt nach mehreren Rohrmotoren an einer Rollade. Ein typischer Rohrmotor hat zwei Anschlüsse für Phase (hoch/runter) und einen für den Neutralleiter. Den ACS kannst Du in den Neutralleiter einschleifen. Dann die verschiedenen Rohrmotoren an A0, A1, … Den Programmcode ändern, so dass er abwechselnd A0, A1 … nutzt. Ob das wirklich robust klappt, musst Du ausprobieren.

      Gruss Stefan

      Antworten
  16. Lou

    Die meisten der auf Amazon, Aliexpress oder eBay angebotenen ACS712 Sensoren sollte man KEINESFALLS am 230V Netz betreiben. Die Isolationsstrecke der Leiterplatte ist völlig unzureichend, und der Betrieb deshalb lebensgefährlich. Der Chip ist OK. Das Problem ist die Leiterplatte des Moduls, welche ohne Not die Masseflächen der Sekundärseite unmittelbar an die Primärseite heranführt, so auch beim abgebildeten Modell. Da reicht dann schon ein bisschen Dreck oder Feuchtigkeit für einen Überschlag. Mal ganz abgesehen davon, dass die Anschlussklemmen nicht für den zugesagten Strom taugen. Es gibt brauchbare Layouts, welche dann ein bisschen teurer sind.

    Antworten
    1. ST Beitragsautor

      Hallo Lou,

      es gibt durchaus fragwürdige Platinen, die da angeboten werden. Wenn man die komplette Platine / Steuerung (sollte man eh) sicher in einem Gehäuse montiert hat, ist das Risiko allerdings sehr klein. Der ACS712 hat laut Datenblatt einen Innenwiderstand von 1.2 MilliOhm. Bei einem maximalen Strom von 16A ergibt sich eine Potentialdifferenz von 20 Millivolt zwischen den Eingängen des ACS712. Das Risiko eines Überschlags ist nicht da.

      Gruss Stefan

      Antworten
  17. Frank

    Das ist mal wieder ein tolles Projekt, wie viele andere in diesem Blog.

    Ich habe einen Trenntrafo, hinter den ich einen Stelltrafo (nicht galvanisch getrennt) schalten möchte. An dessen Ausgang möchte ich nun die Spannung 0-230V und den Strom 0-4A messen und als deren Produkt die Leistung anzeigen. Das Ganze soll auf einem LCD ausgegeben werden. Das sollte soweit klappen.

    Aber ich habe keine Idee, wie ich die Spannung von 230V AC auf Arduino taugliche Werte bekommen soll. Wie könnte man das machen?

    Antworten
    1. Heiko

      Hallo Frank,

      dazu fällt mir ein kleiner Übertrager / Trafo ein, das Ausgangssignal dann um den maximalen negativen Betrag anheben oder verlustarm gleichrichten. Das ist zwar nicht besonders elektronisch, hilft aber voran

      Heiko

      Antworten
  18. AndiSh

    Hallo!

    Ähja, Überschlag unter’m Chip (Funken bei Überspannung): habe das so verstanden, dass manche Platinen zu wenig Isolationsstrecke zwischen den beiden Seiten des ACS haben, unterm Hintern des Chips durch die Längsachse darf einfach nix sein, höchstens eine extra Schicht gutes Epoxy-Harz.
    Das ist nötig, um die 230V-Seite (oder was auch immer) von der analog out – Seite zu trennen.
    Denn nur dann hat man auch die ?2,5kV? Isolation. Das ist schon fast Überspannungsschutz, wie er sinnvoll wird, wenn man bei einem nahen Blitzeinschlag nicht alles kaputt haben will. Man muss nur so weiter machen, auf der Signalleitung, etc.. Am Besten Polymerfasern verlegen zur Datenübertragung.

    Meine eb* Platinchen taugen eigentlich ganz gut, beidseitige Führung des Stroms mit 3 Durchkontaktierungen, 1,7mm Isolationsabstand unterm Chip, extra Isolationslack beidseitig. Stabile Klemme. 16A, naja. Siehe Foto da:
    https://image.pushauction.com/0/0/f37477f9-6aaa-4217-9548-c36e3e397539/2712c8e4-79ed-4bad-a2c0-0872b8bc2669.jpg
    Es gibt aber mehr untaugliche Platinen als solche mit beidseitiger gut durchkontaktierter Stromführung zur Klemme und ordentlich Abstand zwischen „primär und sekundär“.

    Aber für Wechselstrom gibt es ja Clamp-On-Trafos, habe ich mir gebraucht mal 30 Stück bei eBay geholt. Wobei das Design mit Hall schon verführerisch klein ist.

    Andererseits stört mich an den Dingern immer, dass sie zu geizig waren, ihre interne Referenz-Masse herauszuführen. Wie kann so ein Chip solche Erfolge feiern;)? Wer einen findet, bei dem das besser gelöst ist, mit 1-2 Pins mehr am Gehäuse? Her damit!
    Ich muss also das, was der Chip intern fehlerbehaftet macht, herausfinden, und extern nachbilden. Sonst habe ich immer Offset. Daher mal einen alternativen Weg:

    Ganz ohne Trafo und Hall:
    Auf der anderen Seite habe ich mir von Analog Devices Gratismuster von A/D-Wandlern mit recht hoher Auflösung und eingebauter potentialfreier „Stromversorgung“ geholt, nennt sich iCoupler.

    Die werden von der Schnittstellenseite mit Strom für die „Primärseite“, also, wo gemessen wird, versehen. Intern haben sie Spulen, wo ns – Pulse als Datenstrom drüberflitzen, aber eben auch in einer 3. Spule die Leistung für die Mess-Seite.

    Chipintern ist alles fertig für den DC-DC-Wandler, außer 3 Kapazitäten pro Seite 😉 !

    Man muss gute Masse- und VSS-Flächen vorsehen und sehr kurze Wege für die 3 relevanten SMD-Kondensatoren, die die iCoupler-Technik für die Stromversorgung nutzt, einhalten, siehe Referenz-Design von AD. Dann klappt das super. Das Ergebnis ist eine potentialfreie Messung, und man kann sogar bei vielen Chips 5V/5mA abzwacken, um z.B. noch einen OpAmp mit Strom zu versorgen, ermöglicht sehr niederohmige Shunts.

    Gibt es für solche Standardsachen nicht eine fertige Lösung, die gleich den Phasenwinkel ausspuckt, und die sowohl für AC und auch DC funktioniert? Ich meine, nicht umsonst kann man Messtechnik ein paar Semester beackern in einem eTechnik-Studium…

    Ich frage mich gerade, ob ein eBike-Akku rein ohmsch belastet wird, oder ob da auch der Bär tobt, also der Akku nicht phasengleich zum Strom in die Knie geht. Mischstrom heißt das dann, muss ich mich noch belesen. Ich müsste also den Gleichanteil messen und dazu noch den Wechselstromanteil ohne Bildleistung addieren. Macht man das auch messtechnisch so?
    Eigentlich mag ich nur einen einfachen Klotz zum Überwachen in die Leitung Batterie-Motor hängen, der nachweisen soll, ob die Batterie ihr Geld wert war, vor Ende der ersten 6 Monate, möglichst ;)) Wenn er aber gleich Wirkleistung von Mischstrom ordentlich messen kann, ist es auch recht.
    PSoC von Cypress hat ne nette AN, AN2159 war’s, http://www.cypress.com/file/102826/download , dazu, die nehmen einen Analogschalter, der chopped das erste Analogsignal mit einem zweiten analogen Eingang. Macht PWM mit DutyCycle nach Analog2 von Analog1. Das gefiltert ergibt eine Multiplikation, ohne mit Nichtlinearitäten wie im Hallchip oder analogen Multis leben zu müssen. Und ohne kleine arme Prozessoren mit floating point zu quälen. Clever. Andere brauchen dafür einen cpld.

    Aber die Anwendung mit den Rohrmotoren habe ich auch, ich dachte aber eher an ein Program, wie es mal TI in einer AN für Fensterheber in KFZ gezeigt hat, sehr ausgefuchst, wo in Lernkurven mit Sicherheitsband der vorgesehene Strom gespeichert ist und beim Verlassen nach unten oder oben Fehler ausgelöst wird. Sei es, dass der Rollo festgefroren ist, oder auch mal eine Lamelle reißt, also der Motor zu leicht geht.

    Viel Spaß beim Kreativen Stromern!
    & Allzeit Strom-Schlag-Freie Fahrt!

    Andi

    Antworten
  19. Michael

    Für alle, die sehr ungenaue Messwerte erhalten: Ich habe zwei gleiche Module (beide 30A) von verschiedenen Verkäufern bei eBay bezogen; eines davon zeigt ohne Last ~15 Watt an, das andere bleibt hübsch ruhig bei 0W. Mit 40 Watt Verbraucher zeigt das „gute“ Modul ~39,5 W, das hibbelige irgendwas zwischen 20W und 25W. Äußerlich gibt es keine Auffälligkeiten. Entweder ist der Chip im Teich, oder die Platine schlecht verarbeitet. Wenn’s also gar nicht klappt, erstmal ein anderes Modul probieren.

    Vielen Dank für den Code!

    Micha

    Antworten
  20. Wolfgang

    Hallo Stefan,
    danke für den Code. Er funktioniert prima. Ich habe nur ein Problem: Die Strom-(bzw. Leistungs-)Messung verändert dauerhaft eine andere Spannungsmessung auch dann, wenn diese wiederholt wird. Konkret:
    Ich habe im Loop eine if-Abfrage der Spannung an A4 des Arduino und nur, wenn diese Spannung größer Schwellwert ist, soll der Strom gemessen (und weiter ausgewertet) werden.
    Bei Start des Loop wird die Spannung an A4 korrekt gemessen, aber nachdem auch nur einer einzigen Strommessung ist die Messung an A4 reproduzierbar falsch (3,3V).
    Hast due eine Idee, was die Ursache sein kann?

    Vielen Dank

    Antworten
  21. Karl-Heinz

    Hallo,
    Danke für den Code, der gut funktioniert. Da ich das Teil (ACS712 5A) direkt in die Hausverteilung einbauen möchte, sind mir die Kriechstrecken nicht so ganz nach VDE. So habe ich mir einen Stromwandler gewickelt. Einfach einen Ferritring mit Zwillingslitze, so viel wie rein passt, bewickelt. Es sollten schon 20 Windungen sein. Natürlich geht NYA (H07V-U 1,5 mm² ) auch, dann muss man aber die Windungen für Sekundär- und Primärspule genau zählen. Der fliegende Aufbau funktioniert schon und schickt die momentane Leistung einer Mikro-Solaranlage (305 Wp) ins Hausnetz.
    Gruß
    Karl-Heinz

    Antworten
  22. Roman Trapp

    Hallo,
    erstmal danke für den Code.
    Ich habe die Application 3 laut Datenblatt nachgebaut. Mit dieser Schaltung erhalte ich mit einem ACS712 5A eine Ausgangsspannung von 610mV pro Ampere.

    Wie muss ich deinen Code anpassen, damit die richtigen Werte ausgegeben werden?
    Ich denke es muss der gfACS712_Factor angepasst werden…
    Aber wie wird dieser berechnet, wenn der ACS712-5A 612mV / Ampere ausgibt?

    Vielen Danke u. LG
    Roman

    Antworten
    1. ST Beitragsautor

      Hallo Roman,
      in der Codezeile habe ich notiert, dass für die 5A Version 27.03f ist. Dieser Wert bezieht sich auf das Datenblatt des ACS712. Dort ist die 20A Version 100mV/A angegeben und die 5A Version mit 185mV/A. Ich weiß nicht, wie Du auf die 612mV/A kommst; falls das stimmt wäre 8.17f der richtige Wert. Berechnung des Werts: 1/( : 1000 )*5 . Also einfach in A/V umrechnen und die 5V Versorgungsspannung bedenken.
      Gruss Stefan

      Antworten
      1. Roman Trapp

        Hallo Stefan,
        danke für die schnelle Antwort.

        Im Datenblatt des ACS712 gibt es eine Schaltung, bei der am Ausgang noch eine OP-Schaltung nachgeschalten ist. Diese Schaltung verstärkt das Ausgangssignal des ACS um den Faktor 3.3. Das ergibt dann einen Spannungshub von 610mV / A, (185mV * 3.3 = 610,5V).

        1 / (0.6105V / 1000) * 5V = 8.19f
        Mit dem neu errechneten Wert funktioniert es jetzt perfekt.

        Danke noch einmal für den tollen Code!

        Vielen Danke u. LG
        Roman

        Antworten
  23. Jonas Weber

    Hallo Stefan,

    vielen Dank für diesen auführlichen und guten Beitrag.
    Dein Projekt enspricht genau einem Teil meines aktuellen Projekts.

    Bin nur mit „Analog Read“ einfach auf keine gut genugen Ergebnisse gekommen.
    Habe die Auswertung nun von dir übernommen und klappt einwanfrei.

    Danke 😀

    Antworten
  24. Bernhard G.

    Hallo Stefan,
    wirklich schöne Idee und danke für den Code.

    Meines Erachtens kann die Anzahl der Samples pro 5 Perioden von 831 auf 892 gesteigert werden (+7%):

    Dazu muss man die Berechnung durchführen während auf das Ergebnis der Konvertierung gewartet wird. Details in den Kommentaren vom angehängten Sketch.

    (Außerdem habe ich noch ein paar Vorschläge wie man die Behandlung von „micros did run over“ vermeiden kann, und andere kleine Vorschläge der bereits diskutierten Probleme, mehr dazu in einer späteren Nachricht.)

    Bitte lass mich wissen ob ich etwas dabei übersehen habe, und was du davon denkst.

    Hier das abgeänderte Sketch:

    /******************************************************/
    float gfLineVoltage = 235.0f;               // typical effective Voltage in Germany
    float gfACS712_Factor = 50.0f;              // use 50.0f for 20A version, 75.76f for 30A version; 27.03f for 5A version
    unsigned long gulSamplePeriod_us = 100000;  // 100ms is 5 cycles at 50Hz and 6 cycles at 60Hz
    int giADCOffset = 512;                      // initial digital zero of the arduino input from ACS712
    
    void setup()
    {
      Serial.begin(38400);
      while (!Serial) ; // wait for serial port to connect. Needed for native USB
      Serial.println("Precise AC Current Measurement with ACS712 - Stefan Thesen 09/2016");
    }
    
    void loop()
    {
      float strom;
      strom = getACS712CurrentRMS();
      Serial.print(strom);
      Serial.println(" mA RMS");
    }
    
    
    // #############################
    float getACS712CurrentRMS(void) {
     long lNoSamples=0;
     long lCurrentSumSQ = 0;
     long lCurrentSum=0;
     long lValue=0;
     static float fCurrentRMS;
     byte bADCL = 0;
     byte bADCH = 0;
     
      // set-up ADC
      ADCSRA = 0x87;  // turn on adc, adc-freq = 1/128 of CPU ; keep in min: adc converseion takes 13 ADC clock cycles
      ADMUX = 0x40;   // internal 5V reference
    
      // 1st sample is slower due to datasheet - so we spoil it
      ADCSRA |= (1 << ADSC);
      while (!(ADCSRA & 0x10));
      
      // sample loop - with inital parameters, we will get approx 892 values in 100ms
      unsigned long ulEndMicros = micros()+gulSamplePeriod_us;
    
      //preparation of sampling for while-loop, do not insert any extra commands until start of loop
        // start sampling and wait for first result (to make sure ADCL and ADCH has "fresh" value)
        ADCSRA |= (1 << ADSC);
        while (!(ADCSRA & 0x10)); // now ADCL and ADCH has new "fresh" value
        
        // start sampling again (we will read ADCL and ADCH before conversion is finished
        ADCSRA |= (1 << ADSC);
      
      while(micros()<ulEndMicros)
      {
        /*
        // make sure that we read ADCL 1st
        lValue = ADCL; 
        lValue += (ADCH << 8);
        lValue -= giADCOffset;
        */
        
        // read old value quickly before conversion is finished
        bADCL = ADCL; // make sure that we read ADCL 1st
        bADCH = ADCH; // reading finished before registers were updated due to running conversion.
        
        // Now there is plenty of time to calculate before conversion is finished 
        lValue = (bADCL + (bADCH << 8)) - giADCOffset; // faster calculation (just one memory access)?
        lCurrentSum += lValue;
        lCurrentSumSQ += lValue*lValue;
        lNoSamples++;
        
        // make sure conversion is finished (wait for result) 
        while (!(ADCSRA & 0x10)); // now ADCL and ADCH has new "fresh" value
        
        // start sampling again (we will read ADCL and ADCH before conversion is finished
        ADCSRA |= (1 <0)  // if no samples, micros did run over
               // in this case fCurrentRMS is not recalculated, and old value is returned (variable is defined as static).
      {  
        // correct quadradic current sum for offset: Sum((i(t)+o)^2) = Sum(i(t)^2) + 2*o*Sum(i(t)) + o^2*NoSamples
        // sum should be zero as we have sampled 5 cycles at 50Hz (or 6 at 60Hz)
        float fOffset = (float)lCurrentSum/lNoSamples;
        lCurrentSumSQ -= 2*fOffset*lCurrentSum + fOffset*fOffset*lNoSamples;
        if (lCurrentSumSQ<0) {lCurrentSumSQ=0;} // avoid NaN due to round-off effects
        
        fCurrentRMS = sqrtf((float)lCurrentSumSQ/(float)lNoSamples) * gfACS712_Factor;// * gfLineVoltage / 1024;
          
        // correct offset for next round
        giADCOffset=(int)(giADCOffset+fOffset+0.5f);
      }
      return fCurrentRMS;
    }
    
    Antworten
    1. ST Beitragsautor

      Danke für die Info und den Code. Ich bin etwas erstaunt, dass das etwas bringt. Hätte gedacht, dass der Compiler das identisch optimiert. Wieder was gelernt. Ich habe in den Post noch Formatierung eingebaut, da WordPress sonst den Code „entformatiert“ darstellt (deshalb hat es auch ein paar Tage mit der Freigabe gedauert).

      Gruss Stefan

      Antworten
  25. Bernhard G.

    Hallo Stefan,

    hier die versprochene Änderung zur etwas eleganteren Behandlung des Überlaufs von micros(), bekanntlich vom Typ „unsigned long“.
    Also der Trick liegt darin, dass man in der Subtraktion von unsigned Variablen jene Differenz erhält, die der „Vorwärtszählung (mit Überlauf) entspricht. Einfach mal ausprobieren (z.B. mit „byte“ als unsigned 8 bit integer statt „unsigned long“)…

    CHANGE FROM:

    unsigned long ulEndMicros = micros()+gulSamplePeriod_us;
    while(micros()0) { // if no samples, micros did run over
    // do current calculation here
    }

    CHANGE TO:

    unsigned long ulStartMicros = micros();
    while ( (unsigned long)( micros() – ulStartMicros) <= gulSamplePeriod_us ) {
    //do sampling stuff here
    }
    // do current calculation here (no if needed)

    Schöne Grüße
    Bernhard

    Antworten
  26. Bernhard G.

    ups, die Autoformatierung hat da was zerstört, hier nochmals:

    CHANGE FROM:

    unsigned long ulEndMicros = micros()+gulSamplePeriod_us;
    while( micros() 0) { // if no samples, micros did run over
    // do current calculation here
    }

    CHANGE TO:

    unsigned long ulStartMicros = micros();
    while ( (unsigned long)( micros() – ulStartMicros ) <= gulSamplePeriod_us ) {
    //do sampling stuff here
    }
    // do current calculation here, … no if needed

    Antworten
    1. Bernhard G.

      Im „change from“ werden anscheinend Zeichen/Zeilen automatisch ersetzt. Kann den orignial-Code nicht posten.
      Das „change to“ beinhaltet aber den richtigen und gewünschten Code.

      Antworten
  27. Bernhard G.

    Hallo Stefan,

    und ein für heute letzter Kommentar zur Berechnung von gfACS712_Factor. Meines Erachtens müsste z.B. für die 5A Version mit 185mV/A (typ) lauten:

    ( 5V / 1024bit ) / (0.185 V/A) = 0.0264 A/bit = 26.39 mA/bit

    Also:
    5A-Version (typ 185 mV/A)
    26,39 = gfACS712_Factor

    20A-Version (typ 100 mV/A)
    48,83 = gfACS712_Factor

    30A-Version (typ 66 mV/A)
    73,98200758 = gfACS712_Factor

    Bei Datenblättern von Halbleitern ist allerdings immer nur der Min und Max Wert richtig aussagekräftig. Deshalb können die tatsächlich benötigten Werte aufgrund Toleranzen im Fertigungsprozess jeweils von o.g. Werten abweichen, und für die 20A Version ist 50.0 noch im Min-Max Bereich.

    Schöne Grüße
    Bernhard

    Antworten
  28. Jimy147

    Hallo meine Freunde,

    ich bin neu auf dem Gebiet hier und wollte das ganze hier mal nutzen, um entsprechend 3 Phasen zu überwachen, Hierzu habe ich von Wago Stromwandler bestellt 75/5A. Alles soweit so gut.

    Da ich überhaupt keine Ahnung bzgl. ADC Register habe, hätte ich Fog. Frage.

    Hier im Beispiel wird der Eingang A0 genutzt, wie kann ich das hier umstellen auf den A1 Eingang?

    Vielen dank für eure Geduld bei mir im Vorraum 🙂

    Antworten
    1. ST Beitragsautor

      Hallo Jimy,
      wenn Du noch am Anfang stehst, ist die hier vorgestellte Programmierung mit Registern vielleicht nicht der ideale Einstieg. Man kann den Pin mit dem Mux Register einstellen. Für den Anfang vielleicht mit analogRead starten.
      Dann hast Du einen Stromsensor gekauft, der auf dem Trafo Prinzip beruht. Der hier benutze Stromsensor nutzt den Hall-Effekt. Dieser Sensor funktioniert physikalisch komplett anders und wir auch anders beschaltet. Der Arduino dürfte abfackeln, wenn Du Deinen Stromsensor einfach an den Arduino anschließt. Vielleicht suchst Du nach einer Referenzschaltung für diesen Sensor. Und Vorsicht bitte mit 230/400V.
      Gruss Stefan

      Antworten
      1. Jimy 147

        Hallo Stefan,

        Vielen Dank für die Info, sowie die Wahrung bzgl. Spannungen.
        Ich meiner Seite bin Elektro Meister in Energie und Gebäudetechnik, habe jedoch geringe bis keine Erfahrung mit dem Arduino.

        Die Wandler werde ich wie hier auch Vorgestellt an ein ACS712 mit 5A anschliessen. Dies habe ich soweit auch getestet. und es funktioniert auch super.

        Mit dem analogRead(A0)… habe ich das Problem, dass die Messung extrem Schwank und nicht aussagekräftig genug ist. Das hier zur verfügung gestellte Programm ist dahingehend überragend genau.

        Mein Problem bei dem ganzen ist, dass ich hier jedoch nur „einen“ eigang sprich A0 nutzen kann. Mein Projekt braucht jedoch den A0 für L1, A1 – L2 und den A2 – L3 Eingang, damit ich hier alle drei Phasen überwachen und auf eine z.b. SD Karte Speichern kann. Evtl. auch ein Display…
        Diese Daten auf dem SD werden dann in eine Excel Tabelle eingetragen und per Grafik Diagramm ausgewertet.

        Grund unter anderem für diese Projekt ist, bei Kunden eine Langzeitmessung (ca. 1-2 Wochen) am Hausanschlusskasten durchzuführen, um den ist Leistungbedarf zu ermitteln.

        Unter anderem ist hier auch die 10 Bit Beschrieben, macht es sinn einen ADS1115 und Esp 8266 zu kaufen und „normal“ mit dem analogRead zu arbeiten, damit mann hier auf 16Bit kommen würde?

        Das ganze würde ich in einer Abzweigdose montieren mit einer Magnetbefestigung.

        Gibt es eine Anleitung / Bibliothek in dem ich das mit dem Register lernen könnte? Oder besteht die Möglichkeit dass du mir das umschreibst? Gerne entlohne ich dich dafür.

        Beste Grüße Jimy 147

        Antworten
        1. ST Beitragsautor

          Hallo Jimy,

          alles klar – dann weißt Du was Du am Stromnetz treibst und nimmst die richtigen Sensoren. Wenn Du einen anderen analogen Eingang nehmen willst, musst Du die Nummer nur zum ADMUX Wert addieren.
          ADMUX = 0x40; // –> A0; A1 waere also 41
          Du kannst mit dem Arduino nicht parallel sampeln; also musst Du die Phasen in einer Schleife durchlaufen.
          Zum Schreiben auf SD gibt es vermutlich genug Code Beispiele. Die Genauigkeit sollte reichen.
          Register Beschreibungen gibt es einige; zB hier https://www-user.tu-chemnitz.de/~heha/viewchm.php/hs/ATmegaX8.chm/24.htm

          Good luck
          Stefan

          Antworten
  29. Jimy147

    Hallo Steffen,

    ist das so Richtig wie ich das aufgebaut habe, beim Versuchsaufbau habe ich bei A0 sowie bei A1 den gleichen wert, muss ich hier evtl. eine einmalige Pause mit einbauen? da die Phasen 120 Grad versetzt sind?

    Vielen Dank für die Hilfe im Voraus, habe mir das wenn ich ehrlich bin einfacher vorstellt wie es wirklich ist. Respekt an die die es können.

    Antworten
    1. ST Beitragsautor

      Hallo Jimy,
      bitte keinen kompletten Sourcecode in ein Kommentarfeld posten. Das packt WordPress nicht – das wird monsterlang und alle Formatierungen fliegen raus. Ich habe deshalb den Code in Deinem Kommentar gelöscht.
      Du kannst in C/C++ nicht einfach den Code zweimal hinter einander packen. Der Compiler wird z.B. meckern, dass Du Variablen doppelt deklarierst. Ich kann hier jetzt aber offen gesagt keinen Basis Programmierkurs dazutexten.
      Sinnvoll wäre es zB eine for() Schleife zu bauen, die den Code 3 Mal mit unterschiedlichen Mux Werten durchläuft.

      Antworten
  30. Andreas Dittmar

    Hallo Stefan,
    ich habe folgende Frage: Die Schaltung arbeitet doch mit einer AC-Spannung. Der Arduino verträgt meines Wissens nach nur DC von 0…5V. Was passiert mit der negativen Halbwelle?

    Viele Grüße

    Andreas

    Antworten
      1. Andreas Dittmar

        Danke für den Hinweis. Somit bekommt der Arduino nur positive Spannungen.
        Der Hintergrund meiner Frage war, dass ich einen ARCELI 5-A Sensor einsetzen wollte, der über eine Spule mit Verstärker eine AC-Spannung ausgibt. Dieser Sensor ist sehr empfindlich (3000 mV/A). Ich möchte damit geringe Ströme einer modernen Heizungspumpe messen.
        Dann müsste wohl der Arduino über eine Diode nur die pos. Spannung bekommen und der Programmcode etwas angepasst werden?

        Gruß Andreas

        Antworten
        1. ST Beitragsautor

          Hallo Andreas,
          ich kenne das konkrete Board/Sensor nicht. Es gibt fertige Boards mit einer solchen Induktionsspule. Da ist dann eine Vorverstärker Schaltung vorhanden. Diese wird vermutlich auch den Nullpunkt verschieben. Entweder in die Doku schauen oder die Schaltung mal an ein Oszi anschließen. Ohne entsprechende Schaltung musst Du selbst was konzipieren.
          Viel Erfolg
          Stefan

          Antworten
          1. Andreas

            Hallo Stefan,
            ich habe den Ausgang des Operationsverstärkers (Sensorboard mit Spule) über einen Spannungsteiler auf die halbe Betriebsspannung gezogen. Damit kommt der Arduino nun zurecht. Dein Script funktioniert jetzt bestens. Eine 60 W-Glühlampe ergibt 60 W (daruf habe ich kalibriert) und nahezu 0, wenn sie nicht angeschlossen ist. Bei einer Ergiesparlampe passt das wegen dem nicht mehr sinusförmigen Strom bzw. der Blindleistung nicht so gut.
            Vielen Dank für den schönen Blog.
            Gruß Andreas

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert