Dirt Simple Solar Charge Monitor using INA219 (formerly ACS712)

EDIT - Latest Updates start at link below (Now with INA219, DHT22, Wemos and LED Strip lights… kitchen sink coming soon).

Including sketch and QR

The weather is improving and I needed to do something outside, even if only for a short while :slight_smile: It is not much of a project, but it beats sitting at the workbench, blinking RGB lights and fixing unformated code in the forums all day :stuck_out_tongue:

So I whipped together a super simple Solar Charge Monitor, running on a Arduino Leonardo/Pro Micro and two ACS712 based current monitors (of course I only had the 20A versions, but ya works with the tools ya gots). And to save me running long USB cables to a PC, I opted for a good old HC-05 Bluetooth module. Poor range, but I can read it from the front half of the motorhome.

I presently have two small panels (max 2.5amp @ 12vdc), each hooked up to it’s own cheap controller and salvaged battery. At first I was happy to see, displayed on the App, a small current on both batteries… but then everything dropped to nothing… turns out both batteries are fully charged :wink: But the older battery that I salvaged last winter does dip occasionally to show an approximate 0.86amp trickle charge once in awhile.


Awesome! I’m gonna read up on your code later :smiley:

Thanks. It is fairly well commented, but if you get lost on some complexity, please let me know and I will gladly help you out :stuck_out_tongue_winking_eye:

Seriously though… I miss having overall energy/concentration/hardware. I really love mucking around with this stuff.

I don’t have the proper sensors to track solar output even without charging the batteries, so I may need to add in a relay, LDR sensor and a light or something, so that power is used during the night and recharges in the day… just to see something happening on the app :wink: Ah, simple pleasures!

1 Like

Another post hail, sunnyish afternoon had me outside again… this might be a trend :stuck_out_tongue:

Tweaked the sketch to add in a DHT11 sensor and accommodate a basic BT friendly graph. Updated the code above and here is the App:

And yes, this is all a rather simple and basic Blynk / Hardware application, but until I get my energy back (personal, not App :wink: ), it is meaningful accomplishments for me :smiley:

Keep the updates coming!

Well, bluetooth is fine for basic short term connections, but no good for this… it kept shutting off the app after a few minutes.

So I ran a USB cable to it from my media PC and using USB-link have it running 24/7 and collecting actual data :wink:

I also added in some bridge functionality to send the graph data to my primary test app as well.

Here is the new USB/bridge code:

#define BLYNK_USE_128_VPINS // to allow Leonardo to compile with the higher range vPins needed for the bridge to the Mega
#include <BlynkSimpleStream.h>
#include <DHT.h>
BlynkTimer timer;
#define DHTPIN 15
#define DHTTYPE DHT11
char auth[] = "c48a5c383892447c84eeaafe5820485d";
int mVperAmp = 100; // Use 100 for 20A module of ACS712 current sensor
int ACSoffset = 2450;
const int Solar1 = A0; // Sensor1
const int Solar2 = A1; // Sensor2
int RawValue1 = 0;
int RawValue2 = 0;
double Voltage1 = 0;
double Voltage2 = 0;
double Amps1 = 0;
double Amps2 = 0;
WidgetBridge bridge1(V37); //Initiating Bridge Widget on V50 of MEGA
WidgetBridge bridge2(V38); //Initiating Bridge Widget on V51 of MEGA

void setup()
  Serial.begin(19200);  // To server (via USB Link - batch file on PC).
  Blynk.begin(Serial, auth);  // Connect to Blynk - Local Server.
  bridge1.setAuthToken("d1e516ca1d8b4d57a172dac786907a90"); // Token of the Mega
  bridge2.setAuthToken("d1e516ca1d8b4d57a172dac786907a90"); // Token of the Mega
  timer.setInterval(1000L, []() {  // UpTime display
    Blynk.virtualWrite(V0, millis() / 1000);
  timer.setInterval(4000L, Current);  // Current sensors function
  timer.setInterval(11000L, DHTSensor);  // Temp & Humidity sensor function
}  // END Setup Loop

void DHTSensor()  // DHT11
  float h = dht.readHumidity();
  float t = dht.readTemperature(); // or dht.readTemperature(true) for Fahrenheit
  Blynk.virtualWrite(V7, h);
  Blynk.virtualWrite(V8, t);
}  // End loop

void Current()
  RawValue1 = analogRead(Solar1); // Sensor1
  RawValue2 = analogRead(Solar2); // Sensor2
  Voltage1 = (RawValue1 / 1024.0) * 4900; // Gets you mV
  Voltage2 = (RawValue2 / 1024.0) * 4900;
  Amps1 = ((Voltage1 - ACSoffset) / mVperAmp); // Gets you AMPs
  Amps2 = ((Voltage2 - ACSoffset) / mVperAmp);
  if (Amps1 < 0.10) {
    Amps1 = 0;
  }  // End If
  Blynk.virtualWrite(V1, Amps1); // Send to display widgets
  Blynk.virtualWrite(V3, Amps1); // Send to Graph widgets
  bridge1.virtualWrite(V37, Amps1);  // For bridge data to Mega
  if (Amps2 < 0.10) {   // this keeps small current leaks from displaying
    Amps2 = 0;
  }  // End If
  Blynk.virtualWrite(V2, Amps2);
  Blynk.virtualWrite(V4, Amps2);
  bridge2.virtualWrite(V38, Amps2); 
}  // End loop

void loop()
}  // END Void loop

Awhile ago I updated this whole thing with a Wemos D1 Mini, DHT22, and a relay shield (for a remote controlled accessory, powered from the batteries). But I forgot to update this topic… sorry if it is not a very interesting update, but it is what it is :wink:

Relay controlled 12v accessory power ports

DHT22 for outdoor temp and humidity.


I just can’t leave this thing alone :smiley: I ran out of available Wemos D1s, so I merged my Solar Monitor with my LED Strip light into one MCU… which sorta makes sense, since the LEDs are powered by the batteries charged by the solar monitoring system.

It is a bit of a mess right now, but I will add in a picture or two along with some hookup details in awhile.

1 Like

Why did you switch to INA219 ?

It is more accurate for DC use and provides voltage measurement as well as current… ACS712 is more meant for AC current only.

In that case im gonna look if there isn’t one in my stock :stuck_out_tongue:

1 Like

Time to update this topic with current status and code.

This system basically monitors a simple charge controller that is fed from a couple of solar panels wired in parallel. The App sits on my treadmill and “runs” :stuck_out_tongue_winking_eye: 24/7 Giving me updates on charge rate, battery voltage, outside temperature & humidity (with a bridge feed to another bench-top project with indoor temp & humidity monitoring) and a history graph of the whole thing so i can look back a recollect how sunny but cold that there day was :sunny: :thermometer:

And since I also mounted a couple of 1.5 meter RGB strips on the solar panels - One facing outward and the other, wired in parallel, facing upwards to reflect on the windshield and thus inside. I figured I can have a single Wemos D1 Miini, doing multi-duty by controlling them as as well. It is running a FastLED scrolling demo, also 24/7.

I used to have the batteries power the LED strips, but since I doubled them up and let them run all night… these little batterys don’t have the holding power… so for now the LEDs are powered via as AC adapter.

#define BLYNK_MSG_LIMIT 500
#define BLYNK_NO_BUILTIN  // Disable built-in analog & digital pin operations

#include <ESP8266WiFi.h>  // For Blynk
#include <BlynkSimpleEsp8266.h>  // For Blynk

#include <DHT.h>  // For DHT22

#include <Wire.h>  // For INA219
#include <Adafruit_INA219.h>  // For INA219

#include <ESP8266mDNS.h>  // For OTA
#include <WiFiUdp.h>  // For OTA
#include <ArduinoOTA.h>  // For OTA

// ---------- For RGB Strip ----------
#include "FastLED.h"
#if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000)
#warning "Requires FastLED 3.1 or later; check github for latest code."
#define DATA_PIN 13
#define LED_TYPE WS2812
#define NUM_LEDS 90
#define BRIGHTNESS 100
#define LED_LIMIT_MILLIAMPS 3000 // Limit current in mA (Must be using FastLED v3.1.1+)
// -----------------------------------

BlynkTimer timer;
#define DHTPIN 0   // For DHT - GPIO0(D3) on Wemos D1 Mini - DHT22
#define DHTTYPE DHT22  // For DHT
float h, t;  // For DHT

Adafruit_INA219 ina219;  // For INA219 Current Sensor
float shuntvoltage = 0;  // For INA219 Current Sensor
float busvoltage = 0;  // For INA219 Current Sensor
float current_mA = 0;  // For INA219 Current Sensor
float loadvoltage = 0;  // For INA219 Current Sensor

char auth[] = "xxxxxxxxxx";
char ssid[] = "xxxxxxxxxx";
char pass[] = "xxxxxxxxxx";
char server[] = "xxx.xxx.xxx.xxx";
int port = 8080;

int CLK = 0;  // void loop() count variable

WidgetBridge bridge1(V43); //Initiating Bridge Widget - For Temp output to Mega

void setup() {
  dht.begin();  // For DHT

  uint32_t currentFrequency;  // For INA219 Current Sensor
  ina219.begin();  // For INA219 Current Sensor

  Blynk.connectWiFi(ssid, pass);
  Blynk.config(auth, server, port);

  // Timed Lambda Function - UpTime & void loop() cycle display
  timer.setInterval(1000L, []() {
    Blynk.virtualWrite(V0, millis() / 60000);
    Blynk.virtualWrite(V10, CLK);  // Display the void loop() cycles per second
    CLK = 0;  // Reset the void loop() cycles per second
  });  // END Timer Function

  // Timed Lambda Function - INA219 Current Sensor function
  timer.setInterval(1200L, []() {
    busvoltage = ina219.getBusVoltage_V();
    shuntvoltage = ina219.getShuntVoltage_mV();
    loadvoltage = busvoltage + (shuntvoltage / 1000);
    current_mA = ina219.getCurrent_mA();
    Blynk.virtualWrite(V4, current_mA);
    Blynk.virtualWrite(V3, busvoltage);
  });  // END Timer Function

  // Timed Lambda Function - RSSI display
  timer.setInterval(4500L, []() {
    Blynk.virtualWrite(V9, WiFi.RSSI());
  });  // END Timer Function

  // Timed Lambda Function - DHT22 Temp & Humidity sensor
  timer.setInterval(62200L, []() {
    h = dht.readHumidity();
    t = dht.readTemperature(); // or dht.readTemperature(true) for Fahrenheit
    // Check if any reads failed and try again.
    if (isnan(h) || isnan(t)) {
      h = dht.readHumidity();
      t = dht.readTemperature(); // or dht.readTemperature(true) for Fahrenheit
    Blynk.virtualWrite(V7, h);
    Blynk.virtualWrite(V8, t);
  });  // END Timer Function

  // Timer for MEGA Bridge Data Dump Function
  timer.setInterval(63000L, MEGAupdate);  // Sending bridge data to MEGA

  ArduinoOTA.setHostname("Wemos D1 Mini Solar Monitor - Fast LED RGB");  // For OTA
  ArduinoOTA.begin();  // For OTA

  // FastLED LED strip configuration
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);  // set master brightness control
}  // END Setup Loop

  bridge1.setAuthToken("d1e516ca1d8b4d57a172dac786907a90"); // Token for the Mega
  Blynk.virtualWrite(V0, "REBOOT");
  Blynk.virtualWrite(V5, WiFi.macAddress());
  Blynk.virtualWrite(V11, ESP.getCoreVersion());
  Blynk.virtualWrite(V12, BLYNK_VERSION);

  if (param.asInt()) {

void MEGAupdate() { //  Sending data to MEGA
  bridge1.virtualWrite(V43, t);
  bridge1.virtualWrite(V44, h);
  bridge1.virtualWrite(V47, current_mA);
  bridge1.virtualWrite(V48, busvoltage);

// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm };
uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 0; // rotating "base color" used by many of the patterns
#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))

void loop() {
  CLK++;  // Count the void loop() cycles per second
  ArduinoOTA.handle();  // For OTA

  gPatterns[gCurrentPatternNumber](); // Call the current pattern function once, updating the 'leds' array
  FastLED.show();  // send the 'leds' array out to the actual LED strip
  FastLED.delay(500 / FRAMES_PER_SECOND);  // insert a delay to keep the framerate modest

  // do some periodic updates
    gHue++;  // slowly cycle the "base color" through the rainbow

    nextPattern();  // change patterns periodically
}  // END Void loop

void nextPattern() {
  // add one to the current pattern number, and wrap around at the end
  gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns);

void rainbow() {
  // FastLED's built-in rainbow generator
  fill_rainbow( leds, NUM_LEDS, gHue, 7);

void rainbowWithGlitter() {
  // built-in FastLED rainbow, plus some random sparkly glitter

void addGlitter( fract8 chanceOfGlitter) {
  if ( random8() < chanceOfGlitter) {
    leds[ random16(NUM_LEDS) ] += CRGB::White;

void confetti() {
  // random colored speckles that blink in and fade smoothly
  fadeToBlackBy( leds, NUM_LEDS, 10);
  int pos = random16(NUM_LEDS);
  leds[pos] += CHSV( gHue + random8(64), 200, 255);

void sinelon() {
  // a colored dot sweeping back and forth, with fading trails
  fadeToBlackBy( leds, NUM_LEDS, 20);
  int pos = beatsin16( 13, 0, NUM_LEDS - 1 );
  leds[pos] += CHSV( gHue, 255, 192);

void bpm() {
  // colored stripes pulsing at a defined Beats-Per-Minute (BPM)
  uint8_t BeatsPerMinute = 62;
  CRGBPalette16 palette = PartyColors_p;
  uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
  for ( int i = 0; i < NUM_LEDS; i++) { //9948
    leds[i] = ColorFromPalette(palette, gHue + (i * 2), beat - gHue + (i * 10));

void juggle() {
  // eight colored dots, weaving in and out of sync with each other
  fadeToBlackBy( leds, NUM_LEDS, 20);
  byte dothue = 0;
  for ( int i = 0; i < 8; i++) {
    leds[beatsin16( i + 7, 0, NUM_LEDS - 1 )] |= CHSV(dothue, 200, 255);
    dothue += 32;



hi, have you finished with this project already? I study at the university and your project would be a good practice for me on such a scale. please tell me where you connected in219. or if you have a scheme of the whole project will be wonderful. Sincerely

Sorry, I never make detailed schemes… that would be too organised for my needs :stuck_out_tongue: But I have already included all the relevant pin details in the code… FYI, the INA219 is an i2c device so you can easily Google how that hooks up to any ESP8266… should be good practice for them skool lernins :wink: