01 ianuarie 2015

Arduino Energy-meter

A few days ago I was contemplating a little device from a hobby store. It was a smart power gauge meter for tracking the charge and discharge of a battery.
I was wondering if I cannot do it by myself with a Arduino Uno board and some current sensor.
As I'm not a code-freak dude, I did a little research and found a nice start for my project, right here.
I do have a ACS-712 for 5 Amps and also for 30 Amps.

I decided to start with the little one as my project is designated for my portable HF backpack based on ICOM IC-703.

As the Arduino ADC input cannot accept more than 5V, a voltage divider has to be used.
Is the classic one, in wich I used a 10 kOhm from A4 to ground and a 100 kOhm from A4 to the probe. I used SMD resistors but after I measured the ratio I found that the real ratio is not 10 to 1 but somewhere around 10.93:1.
It is important as this ratio will be used to calculate the voltage into the Arduino code.
I modified the code from Instructables because it had some major measurement errors inside as it was written for ACS715 and I do have some problems here with the libraries since I tried to do some tests with I2C LCDs... Also, I found a lot of discussions about how that code is not working properly.

So, below  is my version of the code of the Energy meter with ACS-712-05T.

The code is heavily commented so, I believe is easy to understand what is about.

#include <Arduino.h>
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal.h>

/* This sketch describes how to connect a ACS712 - Bidirectional Current Sense Carrier
to the Arduino, and read current flowing through the sensor.


LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Easy to connect the LCD Shield


Vcc on carrier board to Arduino +5v
GND on carrier board to Arduino GND
OUT on carrier board to Arduino A0

Insert the power lugs into the loads positive lead circuit,
arrow on carrier board points to load, other lug connects to
power supply positive


  int batMonPin = A4;           // input pin for the voltage divider
  int batVal = 0;                     // variable for the A/D value
  float pinVoltage = 0;           // variable to hold the calculated voltage
  float batteryVoltage = 0;

  int analogInPin = A0;          // Analog input pin that the carrier board OUT is connected to
  int sensorValue = 0;            // value read from the carrier board
  int outputValue = 0;            // output in milliamps
  unsigned long msec = 0;
  float time = 0.0;
  int sample = 0;
  float totalCharge = 0.0;
  float averageAmps = 0.0;
  float ampSeconds = 0.0;
  float ampHours = 0.0;
  float wattHours = 0.0;
  float amps = 0.0;

void setup()
                                               // initialize serial communications at 9600 bps:
  lcd.begin(20, 4);

void loop()

  int sampleBVal = 0;
  int avgBVal = 0; 
  int sampleAmpVal = 0;
  int avgSAV = 0;
      for (int x = 0; x < 20; x++)                 // run through loop 20x

                                                                                        // read the analog in value:
        sensorValue = analogRead(analogInPin);  
        sampleAmpVal = sampleAmpVal + sensorValue;       // add samples together

        batVal = analogRead(batMonPin);                           // read the voltage on the divider
        sampleBVal = sampleBVal + batVal;                        // add samples together
      delay (10);                                                                // let ADC settle before next sample


   avgSAV = sampleAmpVal / 20;

                                                                                      // convert to milli amps
   outputValue = (((long)avgSAV * 4980 / 1024) - 2485 ) * 1000 / 130;  
Modified by Adrian YO3HJV for real life ACS712-05A

Sensor outputs about 2.485 V at rest.
Analog read produces a value of 0-1023, equating to 0v to 5v.
"((long)sensorValue * 5000 / 1024)" is the voltage on the sensor's output in millivolts.
"5000"mV is ideal value, my board has 4.985 V measured with a precision Voltmeter
Therefore, we have a 2485mv offset to subtract.
The unit produces 185 mv per amp of current, so divide by 0.185 to convert mV to mA.
The documentation said that the ACS has 185mV/Amp but I measured 130 mV/ Amp.


  avgBVal = sampleBVal / 20;                       //divide by 20 (number of samples) to get a steady reading

  pinVoltage = (avgBVal * 5.0) / 1024;      
                                //  Calculate the voltage on the A/D pin
                                /*  A reading of 1 for the A/D = 0.0048mV
                                    if we multiply the A/D reading by 0.00488 then
                                    we get the voltage on the pin.                                 
                                   It is a good practice to measure the Vcc with a good voltmeter and
                                   to adjust the 5.0 V to the measured value.                                  
                                    Also, depending on wiring and where voltage is being read, under
                                    heavy loads voltage displayed can be  well under voltage at supply. monitor
                                    at load or supply and decide.

  batteryVoltage = pinVoltage * 10.93;    /* 10.93 is the voltage divider ratio
                                          measured with a voltmeter.
                                          First, measure the input voltage (ex. 12V)
                                          Then, measure the voltage at pin A4 (V input).
                                          Then make the ratio between and write it here.
  amps = (float) outputValue / 1000;
  float watts = amps * batteryVoltage;
                                          //Here we print the data output to serial port.
                                          //Usefull for some data logging onto PC
  Serial.print("Volts = " );                      
  Serial.print("\t Current (amps) = ");     
  Serial.print("\t Power (Watts) = ");  
  sample = sample + 1; 
  msec = millis();
   time = (float) msec / 1000.0;
   totalCharge = totalCharge + amps; 
   averageAmps = totalCharge / sample; 
   ampSeconds = averageAmps*time;
   ampHours = ampSeconds/3600; 
   wattHours = batteryVoltage * ampHours;

  Serial.print("\t Time (hours) = ");
  Serial.print("\t Amp Hours (ah) = ");
  Serial.print("\t Watt Hours (wh) = ");

  lcd.setCursor(0, 0);
    lcd.print(batteryVoltage, 2);
    lcd.print(" V ");
  lcd.setCursor(11, 0);
    lcd.print(amps, 2);
  lcd.setCursor(16, 0);
    lcd.print(" A ");
  lcd.setCursor(0, 1);
    lcd.print(watts, 2 );
  lcd.setCursor(7, 1);
     lcd.print(" W ");
  lcd.setCursor(11, 1);
  lcd.setCursor(16, 1);
    lcd.print(" H ");
  lcd.setCursor(0, 2);
    lcd.print(ampHours, 2);
    lcd.print(" Ah ");
  lcd.setCursor(11, 2);
    lcd.print(wattHours, 2);
    lcd.print(" Wh ");

  lcd.setCursor(0, 3);
  lcd.print("Ch/Dsc: ");
  lcd.print(totalCharge, 0);
 // lcd.print(avgBVal);

  // wait 10 milliseconds before the next loop
  // for the analog-to-digital converter to settle
  // after the last reading:
//END of void loop ()

 I have some plans to develop even further this project... I think is suitable for a smart monitor for my holiday house...

73 de Adrian

7 comentarii:

YO3HJV, Adrian spunea...

Cum sa iti faci un dispozitiv care sa masoare tensiunea, curentul si energia consumata sau incarcata din si inr-o baterie, cu un Arduino UNO. Se mai poate utiliza si la monitorizarea consumului de energie.

YO9IRF spunea...

La multi ani :).

Misto jucaria. Vezi ca scala ADC-ului e de la 0 la 1023, deci trebuie sa imparti la 1023 nu la 1024. Daca vrei sa nu mai depinzi de valoarea reala a tensiunii de alimentare pentru corectitudinea citirilor, vezi ca AVR-urile au o referinta interna de precizie, pe care o poti folosi pentru a determina in software care e tensiunea exacta de alimentare. Cauta ceva gen "Arduino secret voltmeter", gasesti imediat cum se face.

Am folosit si eu ACS71x cu Arduino, era versiunea de 30A - ACS715 parca, tin minte ca gama reala de output era intre 0.5V pentru 0A si 4.5V pentru 30A - in consecinta corespondenta era de 133mV/A.

YO9IRF spunea...

Ah, si cand scrii pe LCD baga " mA " cu un spatiu inainte si dupa, ca sa acopere A-ul ala care ramane acolo de la scrierile anterioare ;). Sau da-i un lcd.setCursor(14,3); inainte.

YO3HJV, Adrian spunea...

Pai, si "0"? Probabil de aici apare o abatere variabila. O sa modific sa vad ce si cum.
Am si referinta TL431, am facut voltmetru de precizie si am si varianta cu referinta externa dar scopul era un device cat mai simplu, fara interventii HW "complexe". M-am jucat nitel...

dragos Radiofarafiltru spunea...

o sa incerc si eu. asa ca si proiect "didactic" :)

Adrian spunea...

Asa ceva m-am apucat sa fac si eu, parca tot de sarbatori, anul trecut hi hi cu acest cod de pe Instructables, doar ca am folosit un display grafic de la Nokia 3310 si doi senzori de curent de 5A luati de pe ebay. M-am jucat un pic cu el doar "in gol" nu am bagat nici un consumator. Ideea e sa-mi fac un mic device de QRP portabil sa-mi masoare pentru o baterie de 3.5-7Ah cat imi consuma statia si eventual ce imi mai baga si niste mici panouri solare (vreo 350mA), astfel sa stiu cat mai am de trancanit. Mai vroiam sa-i fac un fel de meniu cu doua butonase unde sa introduc, aproximativ, Wh bateriei pe care o folosesc, ca sa-mi calculeze cat % mai am din ea.

73s La multi ani! YO4HHP

YO3HJV, Adrian spunea...

Pai, de ce doi senzori? Unul e suficient, pus pe baterie. Daca bilantul este pozitiv, va arata incarcarea, daca e negatic, e evident... Acuma, ca stau si ma gandesc, la SLA-uri, tensiunea bateriei este un bun indicator al gradului de incarcare si ar putea fi introdus in ecuatie.

Postări populare

OnLine logbook YO3HJV