Power Meter
Project Description
I made this power meter during my university group project for DESN2000 where I needed a way to validate the amount of energy generated by our backpack.
Technologies Used
#ESP-32#C#Electronics#Soldering
Process
I only wanted to use what I already had on hand at the time which was:
- ESP32
- LCD display
- 2KΩ resistors
- ACS712 5A current sensor
After accounting for the required resources, this was the schematic that I ended up with.
And after assembly, this is what it looks like.
I then wrote the following code for the ESP-32 to interpert the sensor readings and output it to the LCD display.
#include <LiquidCrystal_I2C.h>
#define READINGS_LEN 64
#define DELTA_TIME 6
double CURRENT_CALIBRATION_OFFSET = 0.0;
double VOLTAGE_CALIBRATION_OFFSET = 0.0;
double* currentReadings = new double[READINGS_LEN]();
double* voltageReadings = new double[READINGS_LEN]();
double joules = 0;
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C address 0x27, 16 column and 2 rows
unsigned long lastLcdRefresh = 100000000;
// Average of an array
double average(double array[], int len) {
double sum = 0.0;
for (int i = 0; i < len; i++)
sum += array[i];
return sum / len;
}
// Shifts the array right
void shift_right(double array[], int len) {
for (int i = len - 1; i > 0; i--) {
array[i] = array[i - 1];
}
array[0] = 0.0;
}
void calibrate() {
const int totalCalibrationCycles = 120;
double totalVoltageDiff = 0.0;
double totalCurrentDiff = 0.0;
lcd.clear();
lcd.setCursor(0, 0);
for (int i = 0; i < totalCalibrationCycles; i++) {
int adc = analogRead(34);
double loadVoltage = analogRead(35) * 3.3 / 4095.0 * 2.0;
double sensorVoltage = adc * 3.3 / 4095.0;
double current = (sensorVoltage - 4.92 / 2.0) / 0.185;
// No voltage and current at the start.
double voltageDiff = 0 - loadVoltage;
double currentDiff = 0 - current;
// Add difference to the calibration results
totalVoltageDiff += voltageDiff;
totalCurrentDiff += currentDiff;
// Display calibration status to lcd
lcd.setCursor(0, 0);
lcd.print("Calib..");
lcd.setCursor(8, 0);
lcd.print("i: ");
lcd.print(i);
lcd.setCursor(0, 1);
lcd.print("V: ");
lcd.print(totalVoltageDiff / (double)i);
lcd.setCursor(8, 1);
lcd.print("A: ");
lcd.print(totalCurrentDiff / (double)i);
delay(6);
}
// Update calibration offsets
VOLTAGE_CALIBRATION_OFFSET = totalVoltageDiff / totalCalibrationCycles;
CURRENT_CALIBRATION_OFFSET = totalCurrentDiff / totalCalibrationCycles;
}
void setup() {
lcd.init(); // initialize the lcd
lcd.backlight();
calibrate();
}
void loop() {
// Obtain input
int adc = analogRead(34);
double loadVoltage = analogRead(35) * 3.3 / 4095.0 * 2.0 + VOLTAGE_CALIBRATION_OFFSET;
if (loadVoltage < VOLTAGE_CALIBRATION_OFFSET + 0.05) loadVoltage = 0;
double sensorVoltage = adc * 3.3 / 4095.0;
double current = (sensorVoltage - 4.92 / 2.0) / 0.185 + CURRENT_CALIBRATION_OFFSET;
if (current < 0.05) current = 0;
// Calculate average current
double averagedCurrent = average(currentReadings, READINGS_LEN);
shift_right(currentReadings, READINGS_LEN);
currentReadings[0] = current;
if (averagedCurrent < 0.035) averagedCurrent = 0;
// Calculate average voltage
double averagedVoltage = average(voltageReadings, READINGS_LEN);
shift_right(voltageReadings, READINGS_LEN);
voltageReadings[0] = loadVoltage;
// Calculate total energy generated
joules += averagedVoltage * averagedCurrent * DELTA_TIME / 1000.0;
// Display to LCD
if (lastLcdRefresh > 500) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(averagedVoltage);
lcd.print(" V");
lcd.setCursor(8, 0);
lcd.print(averagedCurrent);
lcd.print(" A");
lcd.setCursor(0, 1);
lcd.print(averagedCurrent * averagedVoltage);
lcd.print(" W");
lcd.setCursor(8, 1);
lcd.print(joules);
lcd.print(" J");
lastLcdRefresh = 0;
}
lastLcdRefresh += DELTA_TIME;
delay(DELTA_TIME);
}
Results
The result was a fully functioning power meter that was suitable for the degree of accuracy that I required. Below is an image of the meter in action when charging an iPhone.