Here is my solar power monitor based on an INA226 and an ESP8266.
the device monitors a 12 V battery charged with a solar panel and has got an integrated OLED display.
Additionally it transmits the values to thinger.io, where it can be graphically monitored.
The bill of material is specially unexpensive:
1 Wemos D1 Mini
1 Wemos OLED 64x48 OLED Display (optional)
1 INA 226 breakboard
1 HW384 low power 12V-5V converter (Fine 6-24V 12V/24V to 5V 3A CAR USB Charger Module DC Buck step down Converter | eBay)
1 Case
2 pairs od Banana connectors
altogether should cost less than 10€
Program description:
The sketch connects to WiFi and displays the connection data to the OLED screen
Then it gets the time over SNTP
Then it gets the information from the INA226 and display the information together with the time on the OLED screen
Then it forwards the information to Thinger, where the information is graphically presented.
The current sketch does not yet contain power optimizations, the whole thing consumes about 10 mA.
The OLED display requires additional 2mA
The HW384 12V-5V converter is a specially low quiescent power model
The code will come in the next message (why that stupid length limit?)
Next steps: power optimization, shut down WiFi between transmissions, averaging over 1 min. UDP transmission for LAN usage
Enjoy!
Here the code:
//***Libraries***
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <FS.h>
#include <string.h>
#include <INA.h> // Zanshin INA Library
#include <TimeLib.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <ThingerESP8266.h>
//***Hardware Definitions***
#define SCL 5
#define SDA 4
#define MOSI 13 // GPIO for SPI Master Out
#define MISO 12 // GPIO for SPI Master In
#define SCLK 14 // GPIO for SPI System Clock
#define OLED_RESET 0 // GPIO0
#define LED 13
//***Network Definitions***
int Ser2NetSendPort = 3051;
int Ser2NetRecvPort = 3051;
int SerCommSendPort = 6001;
int SerCommRecvPort = 6001;
byte mac[6]; // the mac address of your Wifi shield
unsigned int localPort = 8888; // local port to listen for UDP packets
IPAddress ip;
IPAddress Laptop(192, 168, 178, 24) ;
IPAddress DFLD_004_027 (192, 168, 178, 34) ;
IPAddress DFLD_BARE (192, 168, 178, 41) ;
IPAddress ESP_361913(92, 168, 178, 44);
IPAddress broadcast(255, 255, 255, 255);
// NTP Servers:
static const char ntpServerName[] = "nl.pool.ntp.org";
const int timeZone = 1; // Central European Time
//***Start instances***
ThingerESP8266 thing("Name", "Device", "ID");
Adafruit_SSD1306 display(OLED_RESET);
WiFiUDP Udp;
time_t getNtpTime();
INA_Class INA;
//***Variables***
unsigned long address;
time_t prevDisplay = 0; // when the digital clock was displayed
const char* filename = "/samplefile.txt";
const char ssid[] = "SSID"; // Network SSID (name)
const char pass[] = "Password"; // Network password
#define SERIAL_SPEED 9600
#define SAMPLING_INTERVAL 1000
#define SAMPLING_COUNT 100
byte devicesFound = 0; ///< Number of INAs found
float ina_current;
float ina_voltage;
float ina_shunt;
float ina_power;
int ind = 0;
float currents[SAMPLING_COUNT];
float voltages[SAMPLING_COUNT];
void setup() {
devicesFound = INA.begin(1, 100000); // Set to an expected 1 Amp maximum and a 100000 microOhm resistor
INA.setBusConversion(8500); // Maximum conversion time 8.244ms
INA.setShuntConversion(8500); // Maximum conversion time 8.244ms
INA.setAveraging(128); // Average each reading n-times
INA.setMode(INA_MODE_CONTINUOUS_BOTH); // Bus/shunt measured continuously
INA.AlertOnBusOverVoltage(true, 13900); // Trigger alert if over 13,8V on bus
Serial.begin(SERIAL_SPEED);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
delay(500);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println("Power Mon");
display.print("Found: ");
display.println(devicesFound);
display.println("Type: ");
display.println(INA.getDeviceName());
display.print("Addr: ");
display.println(INA.getDeviceAddress());
display.display();
delay(2000);
display.clearDisplay();
display.setCursor(0, 0);
Serial.println("Power Monitor ");
Serial.print("Connecting to ");
Serial.println(ssid);
display.println("Con 2 WiFi");
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
display.print(".");
display.display();
}
ip = WiFi.localIP();
display.println();
display.println("ONLINE");
display.println(ssid);
display.print("IP=");
display.println(ip);
display.display();
Serial.print("IP number assigned by DHCP is ");
Serial.println(WiFi.localIP());
Serial.println("Starting UDP");
Udp.begin(localPort);
Serial.print("Local port:");
Serial.println(Udp.localPort());
Serial.println("waiting for sync");
delay(10000);
display.clearDisplay();
setSyncProvider(getNtpTime);
setSyncInterval(300);
thing["BattVolt"] >> outputValue(ina_voltage);
thing["BattmA"] >> outputValue(ina_current);
thing["BattmW"] >> outputValue(ina_power);
}
void loop()
{
if (timeStatus() == timeNotSet)
{
display.setCursor(0, 0);
display.println("Waiting for NTP");
display.display();
} else {
delay(SAMPLING_INTERVAL);
ina_voltage = INA.getBusMilliVolts(0) / 1000.0;
ina_shunt = INA.getShuntMicroVolts(0) / 1000.0;
ina_current = (INA.getBusMicroAmps(0) / -1000.0) -0.06;
ina_power = INA.getBusMicroWatts(0) / 1000.0;
display.clearDisplay();
display.setCursor(0, 0);
display.println("Power Mon");
display.print(ina_voltage); display.println(" V");
display.print(ina_current); display.println(" mA");
display.print(ina_power); display.println(" mW");
digitalClockDisplay();
display.display();
Serial.print(" V: "); Serial.print(ina_voltage);
Serial.print(" mA: "); Serial.print(ina_current);
Serial.print(" mW: "); Serial.println(ina_power);
// communication to thinger.io
thing.handle();
delay(2000);
}
}
void digitalClockDisplay()
{
// digital clock display of the time
display.print(hour());
displayDigits(minute());
displayDigits(second());
display.println(" ");
display.print(day());
display.print(".");
display.print(month());
display.print(".");
display.println(year());
}
void digitalClockPrint()
{
// digital clock print of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
// Serial.print(" ");
// Serial.print(day());
// Serial.print(".");
// Serial.print(month());
// Serial.print(".");
// Serial.print(year());
}
void printDigits(int digits)
{
// utility for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if (digits < 10)
Serial.print('0');
Serial.print(digits);
}
void displayDigits(int digits)
{
// utility for digital clock display: prints preceding colon and leading 0
display.print(":");
if (digits < 10)
display.print('0');
display.print(digits);
}
/*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
time_t getNtpTime()
{
IPAddress ntpServerIP; // NTP server's ip address
while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.println("Transmit NTP Request");
// get a random server from the pool
WiFi.hostByName(ntpServerName, ntpServerIP);
Serial.print(ntpServerName);
Serial.print(":");
Serial.println(ntpServerIP);
sendNTPpacket(ntpServerIP);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Receive NTP Response");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response : -(");
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
/*
float mean(float* array)
{
float sum = 0.0;
for (int i = 0; i < SAMPLING_COUNT; i++) {
sum += abs(array[i]);
}
return sum / (float)SAMPLING_COUNT;
}
*/
2 Likes