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 Genauigkeit 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 Genauigkeit erhöhen. Die Wurzel der Zahl der Messungen gibt den Faktor der Erhöhung der Genauigkeit 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.

27 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 Artikelautor

      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 Artikelautor

      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 Artikelautor

      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 Artikelautor

      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
  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 Artikelautor

      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 Artikelautor

      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 Artikelautor

          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 Artikelautor

      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 Artikelautor

      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 Artikelautor

        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 Artikelautor

      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

Schreibe einen Kommentar

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