651 lines
16 KiB
C++
651 lines
16 KiB
C++
/*Power Analyzer
|
|
*22.08.2018
|
|
*/
|
|
|
|
//Libraries
|
|
#include <Wire.h>
|
|
#include <TaskScheduler.h>
|
|
#include <LiquidCrystal_I2C.h>
|
|
#include <INA219.h>
|
|
#include <EEPROM.h>
|
|
#include <ESPAsyncWebServer.h>
|
|
#include <ESPAsyncWiFiManager.h>
|
|
#include <SPIFFS.h>
|
|
#include <ESP8266FtpServer.h>
|
|
#include <ArduinoOTA.h>
|
|
|
|
//Library Config
|
|
LiquidCrystal_I2C lcd(0x27,20,4);
|
|
INA219 ina219;
|
|
AsyncWebServer server(80);
|
|
DNSServer dns;
|
|
FtpServer ftpSrv;
|
|
Scheduler task;
|
|
|
|
//Konstanten
|
|
#define R_SHUNT 0.001
|
|
#define V_SHUNT_MAX 0.060
|
|
#define V_BUS_MAX 26
|
|
#define I_MAX_EXPECTED 60
|
|
#define i2c_address 1
|
|
#define led_lowbat_pin 25
|
|
#define led_run_pin 26
|
|
#define button1_pin 14
|
|
#define button2_pin 17
|
|
#define button3_pin 16
|
|
#define adc_battery_pin A0
|
|
|
|
//Funktionsdeklarationen
|
|
void lcd_print();
|
|
void lcd_init();
|
|
void lcd_header();
|
|
void eeprom_save();
|
|
void i2c_receive();
|
|
void i2c_respond();
|
|
void readCurrent();
|
|
void read_bat();
|
|
void read_button();
|
|
void web_measuring_stop(AsyncWebServerRequest *request);
|
|
void web_measuring_run(AsyncWebServerRequest *request);
|
|
void web_measuring_reset(AsyncWebServerRequest *request);
|
|
void web_get_values(AsyncWebServerRequest *request);
|
|
void data_logging();
|
|
|
|
//Task Scheduler
|
|
Task t1(500, TASK_FOREVER, &lcd_print);
|
|
Task t2(1750, TASK_FOREVER, &lcd_header);
|
|
Task t3(10000, TASK_FOREVER, &read_bat);
|
|
Task t4(300, TASK_FOREVER, &read_button);
|
|
Task t5(30000, TASK_FOREVER, &data_logging);
|
|
//Task t3(60000, TASK_FOREVER, &eeprom_save);
|
|
|
|
//Globale Variablen
|
|
float shuntvoltage_V = 0, shuntvoltage_mV = 0, busvoltage_V = 0, busvoltage_mV = 0, current_A = 0, current_mA = 0, power_W = 0, power_mW = 0;
|
|
float shuntvoltage_V_max = 0, current_A_max = 0, power_W_max = 0, shuntvoltage_V_min = 0, current_A_min = 0, power_W_min = 0;
|
|
float Ah = 0, mAh = 0, Wh = 0, mWh = 0;
|
|
float battery_voltage, battery_average;
|
|
int i_header = 0;
|
|
bool button1, button2, button3;
|
|
bool lcd_light = true, wifi_enabled = true, test_mode = false;
|
|
bool measuring_run = false, measuring_init = false, reset_actual = false, reset_minmax =false, battery_low = false;
|
|
unsigned long lastread = 0;
|
|
unsigned long tick;
|
|
unsigned long previousMillisReadData = 0;
|
|
unsigned long previousMillisDisplay = 0;
|
|
unsigned long data_timestamp = 0;
|
|
byte i2c_MasterCommand = 0;
|
|
|
|
//Custom LCD Characters
|
|
byte grad[8] = {
|
|
0b01100,
|
|
0b10010,
|
|
0b10010,
|
|
0b01100,
|
|
0b00000,
|
|
0b00000,
|
|
0b00000,
|
|
0b00000
|
|
};
|
|
|
|
byte sollwert[8] = {
|
|
0b00000,
|
|
0b00000,
|
|
0b01111,
|
|
0b00011,
|
|
0b00101,
|
|
0b01001,
|
|
0b10000,
|
|
0b00000
|
|
};
|
|
|
|
byte batterie[8] = {
|
|
0b01110,
|
|
0b11011,
|
|
0b10001,
|
|
0b10001,
|
|
0b10001,
|
|
0b10001,
|
|
0b10001,
|
|
0b11111
|
|
};
|
|
|
|
void setup() {
|
|
//Setup GPIO
|
|
pinMode(led_lowbat_pin, OUTPUT);
|
|
pinMode(led_run_pin, OUTPUT);
|
|
pinMode(button1_pin, INPUT_PULLUP);
|
|
pinMode(button2_pin, INPUT_PULLUP);
|
|
pinMode(button3_pin, INPUT_PULLUP);
|
|
battery_average = analogRead(A0);
|
|
|
|
//Setup Debug Serial
|
|
Serial.begin(115200);
|
|
Serial.println("Power Analyzer by Carsten Schmiemann (C) 2018");
|
|
|
|
//Setup Over The Air - Update
|
|
ArduinoOTA
|
|
.onStart([]() {
|
|
//Disable Tasks
|
|
t1.disable();
|
|
t2.disable();
|
|
t3.disable();
|
|
t4.disable();
|
|
t5.disable();
|
|
SPIFFS.end();
|
|
String type;
|
|
lcd.clear();
|
|
lcd.setCursor(0,0);
|
|
lcd.print("OTA Firmware Update");
|
|
lcd.setCursor(7,1);
|
|
lcd.print("Init");
|
|
})
|
|
.onEnd([]() {
|
|
lcd.setCursor(7,2);
|
|
lcd.print("Done.");
|
|
delay(5000);
|
|
ESP.restart();
|
|
})
|
|
.onProgress([](unsigned int progress, unsigned int total) {
|
|
lcd.setCursor(3,1);
|
|
lcd.print(progress / (total / 100)); lcd.print("% Complete");
|
|
})
|
|
.onError([](ota_error_t error) {
|
|
lcd.setCursor(3,3);
|
|
lcd.print("Done.");
|
|
if (error == OTA_AUTH_ERROR) lcd.print("Auth Failed");
|
|
else if (error == OTA_BEGIN_ERROR) lcd.print("Begin Failed");
|
|
else if (error == OTA_CONNECT_ERROR) lcd.print("Connect Failed");
|
|
else if (error == OTA_RECEIVE_ERROR) lcd.print("Receive Failed");
|
|
else if (error == OTA_END_ERROR) lcd.print("End Failed");
|
|
});
|
|
ArduinoOTA.setPort(1337);
|
|
ArduinoOTA.setPassword("powerlyzer");
|
|
ArduinoOTA.begin();
|
|
|
|
//Init LCD and Custom Chars
|
|
lcd.init();
|
|
lcd.backlight();
|
|
lcd.createChar(0, grad);
|
|
lcd.createChar(1, sollwert);
|
|
lcd.createChar(2, batterie);
|
|
digitalWrite(led_lowbat_pin, 255);
|
|
digitalWrite(led_run_pin, 255);
|
|
delay(1000);
|
|
|
|
//Bootscreen
|
|
lcd.clear();
|
|
lcd.setCursor(4,0);
|
|
lcd.print("Power Analyzer");
|
|
lcd.setCursor(7,1);
|
|
//lcd.print("Tracker");
|
|
lcd.setCursor(6,2);
|
|
lcd.print("V0.29 Beta");
|
|
lcd.setCursor(7,3);
|
|
lcd.print("CS,2018");
|
|
lcd.write(1);
|
|
|
|
//End Test Output PWM
|
|
digitalWrite(led_lowbat_pin, 0);
|
|
digitalWrite(led_run_pin, 0);
|
|
delay(5000);
|
|
lcd.clear();
|
|
|
|
/*
|
|
//Load Setpoint from EEPROM
|
|
lcd.setCursor(0,0);
|
|
lcd.print("Loading Setup from");
|
|
lcd.setCursor(0,1);
|
|
lcd.print("EEPROM...");
|
|
EEPROM.get(0, ee_setpoint);
|
|
setpoint = ee_setpoint;
|
|
delay(800);
|
|
lcd.setCursor(16,1);
|
|
lcd.print("Done");
|
|
delay(200);
|
|
*/
|
|
|
|
//Init Wi-Fi
|
|
lcd.setCursor(0,0);
|
|
if (digitalRead(button3_pin)) {
|
|
lcd.print("Setup Wi-Fi...");
|
|
delay(300);
|
|
AsyncWiFiManager wifiManager(&server,&dns);
|
|
wifiManager.autoConnect("PowerAnalyzer WiFi");
|
|
lcd.setCursor(16,0);
|
|
lcd.print("Done");
|
|
} else {
|
|
lcd.print("Wi-Fi disabled");
|
|
wifi_enabled = false;
|
|
}
|
|
|
|
//Setup Over The Air - Update
|
|
ArduinoOTA
|
|
.onStart([]() {
|
|
//Disable Tasks
|
|
t1.disable();
|
|
t2.disable();
|
|
t3.disable();
|
|
t4.disable();
|
|
t5.disable();
|
|
SPIFFS.end();
|
|
String type;
|
|
lcd.clear();
|
|
lcd.setCursor(0,0);
|
|
lcd.print("OTA Firmware Update");
|
|
lcd.setCursor(7,1);
|
|
lcd.print("Init");
|
|
})
|
|
.onEnd([]() {
|
|
lcd.setCursor(7,2);
|
|
lcd.print("Done.");
|
|
delay(5000);
|
|
ESP.restart();
|
|
})
|
|
.onProgress([](unsigned int progress, unsigned int total) {
|
|
lcd.setCursor(3,1);
|
|
lcd.print(progress / (total / 100)); lcd.print("% Complete");
|
|
})
|
|
.onError([](ota_error_t error) {
|
|
lcd.setCursor(3,3);
|
|
lcd.print("Done.");
|
|
if (error == OTA_AUTH_ERROR) lcd.print("Auth Failed");
|
|
else if (error == OTA_BEGIN_ERROR) lcd.print("Begin Failed");
|
|
else if (error == OTA_CONNECT_ERROR) lcd.print("Connect Failed");
|
|
else if (error == OTA_RECEIVE_ERROR) lcd.print("Receive Failed");
|
|
else if (error == OTA_END_ERROR) lcd.print("End Failed");
|
|
});
|
|
if (wifi_enabled) {
|
|
ArduinoOTA.setPort(1337);
|
|
ArduinoOTA.setPassword("powerlyzer");
|
|
ArduinoOTA.begin();
|
|
}
|
|
|
|
//Init ina219s
|
|
lcd.setCursor(0,1);
|
|
lcd.print("Init Sensors...");
|
|
if (digitalRead(button1_pin)) {
|
|
ina219.begin();
|
|
ina219.configure(INA219::RANGE_16V, INA219::GAIN_2_80MV, INA219::ADC_16SAMP, INA219::ADC_16SAMP, INA219::CONT_SH_BUS);
|
|
lastread = millis();
|
|
ina219.calibrate(R_SHUNT, V_SHUNT_MAX, V_BUS_MAX, I_MAX_EXPECTED);
|
|
delay(300);
|
|
lcd.setCursor(16,1);
|
|
lcd.print("Done");
|
|
delay(200);
|
|
} else {
|
|
lcd.setCursor(16,1);
|
|
lcd.print("Test");
|
|
test_mode = true;
|
|
delay(500);
|
|
}
|
|
|
|
//Init Task Scheduler
|
|
lcd.setCursor(0,2);
|
|
lcd.print("Init Threads...");
|
|
task.init();
|
|
task.addTask(t1);
|
|
task.addTask(t2);
|
|
task.addTask(t3);
|
|
task.addTask(t4);
|
|
task.addTask(t5);
|
|
//Init Filesystem and Webserver
|
|
SPIFFS.begin();
|
|
server.on("/meas/run", HTTP_GET, web_measuring_run);
|
|
server.on("/meas/stop", HTTP_GET, web_measuring_stop);
|
|
server.on("/meas/reset", HTTP_GET, web_measuring_reset);
|
|
server.on("/meas/values", HTTP_GET, web_get_values);
|
|
server.on("/datalog.csv", HTTP_ANY, [](AsyncWebServerRequest *request){
|
|
request->send(SPIFFS, "/datalog.csv");
|
|
});
|
|
server.serveStatic("/img", SPIFFS, "/img");
|
|
server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");
|
|
server.begin();
|
|
ftpSrv.begin("power","analyzer");
|
|
delay(500);
|
|
lcd.setCursor(16,2);
|
|
lcd.print("Done");
|
|
delay(500);
|
|
|
|
lcd.setCursor(0,3);
|
|
lcd.print("Done. Starting up...");
|
|
delay(1500);
|
|
//Display IP address
|
|
if (wifi_enabled) {
|
|
lcd.clear();
|
|
lcd.setCursor(6,1);
|
|
lcd.print("IP address");
|
|
lcd.setCursor(6,2);
|
|
lcd.print(WiFi.localIP());
|
|
delay(1500);
|
|
}
|
|
//Load Default Screen
|
|
lcd_init();
|
|
|
|
//Enable Tasks
|
|
t1.enable();
|
|
t2.enable();
|
|
t3.enable();
|
|
t4.enable();
|
|
t5.enable();
|
|
}
|
|
|
|
void loop() {
|
|
//Exec TaskScheduler
|
|
task.execute();
|
|
ftpSrv.handleFTP();
|
|
ArduinoOTA.handle();
|
|
|
|
unsigned long currentMillis = millis();
|
|
|
|
if ((unsigned long)(currentMillis - previousMillisReadData) >= 250) {
|
|
previousMillisReadData = millis();
|
|
if (test_mode) {
|
|
if (digitalRead(button1_pin)) {
|
|
shuntvoltage_V += 1;
|
|
power_W += 1;
|
|
current_A += 1;
|
|
Ah += 1;
|
|
Wh += 1;
|
|
}
|
|
if (digitalRead(button2_pin)) {
|
|
shuntvoltage_V -= 1;
|
|
power_W -= 1;
|
|
current_A -= 1;
|
|
Ah -= 1;
|
|
Wh -= 1;
|
|
}
|
|
} else {
|
|
readCurrent();
|
|
}
|
|
}
|
|
}
|
|
|
|
void lcd_print() {
|
|
lcd.setCursor(0,1);
|
|
if (current_A < 100 && current_A >= 10) {lcd.print(" ");} else if (current_A < 10 && current_A >= 0) {lcd.print(" ");};
|
|
if (current_A > -100 && current_A <= -10) {lcd.print(" ");} else if (current_A > -10 && current_A < 0) {lcd.print(" ");};
|
|
lcd.print(current_A);
|
|
|
|
lcd.setCursor(0,2);
|
|
if (power_W < 1000 && power_W > 100) {lcd.print(" ");} else if (power_W < 100 && power_W >= 10) {lcd.print(" ");} else if (power_W < 10) {lcd.print(" ");};
|
|
lcd.print(power_W);
|
|
|
|
lcd.setCursor(0,3);
|
|
if (busvoltage_V < 100 && busvoltage_V >= 10) {lcd.print(" ");} else if (busvoltage_V < 10) {lcd.print(" ");};
|
|
lcd.print(busvoltage_V);
|
|
|
|
lcd.setCursor(11,1);
|
|
if (Ah < 1000 && Ah > 100) {lcd.print(" ");} else if (Ah < 100 && Ah >= 10) {lcd.print(" ");} else if (Ah < 10) {lcd.print(" ");};
|
|
lcd.print(Ah);
|
|
|
|
lcd.setCursor(11,2);
|
|
if (Wh < 1000 && Wh > 100) {lcd.print(" ");} else if (Wh < 100 && Wh >= 10) {lcd.print(" ");} else if (Wh < 10) {lcd.print(" ");};
|
|
lcd.print(Wh);
|
|
|
|
lcd.setCursor(14,3);
|
|
if (battery_voltage > 3.4) {lcd.print(battery_voltage);} else {lcd.print("LOW ");}
|
|
}
|
|
|
|
void lcd_init() {
|
|
lcd.clear();
|
|
lcd.setCursor(0,0);
|
|
lcd.print("===--->Power<---===");
|
|
lcd.setCursor(7,1);
|
|
lcd.print("A");
|
|
lcd.setCursor(7,2);
|
|
lcd.print("W");
|
|
lcd.setCursor(7,3);
|
|
lcd.print("V");
|
|
lcd.setCursor(18,1);
|
|
lcd.print("Ah");
|
|
lcd.setCursor(18,2);
|
|
lcd.print("Wh");
|
|
lcd.setCursor(13,3);
|
|
lcd.write((uint8_t)2);
|
|
lcd.setCursor(18,3);
|
|
lcd.print("V");
|
|
delay(300);
|
|
}
|
|
|
|
void lcd_header() {
|
|
if (measuring_run) {
|
|
i_header++;
|
|
if (i_header == 1) {
|
|
lcd.setCursor(0,0);
|
|
lcd.print("-===-->Power<--===-");
|
|
}
|
|
if (i_header == 2) {
|
|
lcd.setCursor(0,0);
|
|
lcd.print("--===->Power<-===--");
|
|
}
|
|
if (i_header == 3) {
|
|
lcd.setCursor(0,0);
|
|
lcd.print("---===>Power<===---");
|
|
}
|
|
if (i_header == 4) {
|
|
lcd.setCursor(0,0);
|
|
lcd.print("=---==>Power<==---=");
|
|
}
|
|
if (i_header == 5) {
|
|
lcd.setCursor(0,0);
|
|
lcd.print("==---=>Power<=---==");
|
|
}
|
|
if (i_header == 6) {
|
|
lcd.setCursor(0,0);
|
|
lcd.print("===--->Power<---===");
|
|
i_header = 0;
|
|
}
|
|
}
|
|
//Flash Battery Low LED
|
|
if (battery_low && digitalRead(led_lowbat_pin)) {
|
|
digitalWrite(led_lowbat_pin,LOW);
|
|
} else if (battery_low && digitalRead(led_lowbat_pin) == false) { digitalWrite(led_lowbat_pin,HIGH); }
|
|
}
|
|
|
|
/*void eeprom_save() {
|
|
if (ee_setpoint != setpoint) {
|
|
EEPROM.put(0, setpoint);
|
|
ee_setpoint = setpoint;
|
|
lcd.setCursor(0,2);
|
|
lcd.print(" ");
|
|
lcd.setCursor(2,2);
|
|
lcd.print("EEProm written.");
|
|
}
|
|
}*/
|
|
|
|
void i2c_receive(int howMany){
|
|
i2c_MasterCommand = Wire.read();
|
|
}
|
|
|
|
void i2c_respond(){
|
|
|
|
int returnValue = 0;
|
|
|
|
switch(i2c_MasterCommand){
|
|
case 0: // No new command was received
|
|
Wire.write("Invalid Request");
|
|
break;
|
|
|
|
case 10:
|
|
returnValue = busvoltage_V;
|
|
break;
|
|
|
|
case 20:
|
|
returnValue = current_A;
|
|
break;
|
|
|
|
case 30:
|
|
returnValue = power_W;
|
|
|
|
}
|
|
|
|
byte buffer[2];
|
|
buffer[0] = returnValue >> 8;
|
|
buffer[1] = returnValue & 255;
|
|
Wire.write(buffer, 2);
|
|
i2c_MasterCommand = 0;
|
|
}
|
|
|
|
void readCurrent() {
|
|
uint32_t count = 0;
|
|
unsigned long newtime;
|
|
busvoltage_V = ina219.busVoltage();
|
|
while(!ina219.ready() && count < 500) {
|
|
count++;
|
|
delay(1);
|
|
busvoltage_V = ina219.busVoltage();
|
|
busvoltage_mV = ina219.busVoltage() * 1000;
|
|
}
|
|
shuntvoltage_V = ina219.shuntVoltage() * 1000;
|
|
shuntvoltage_mV = ina219.shuntVoltage();
|
|
current_A = ina219.shuntCurrent();
|
|
current_mA = current_A * 1000;
|
|
power_W = ina219.busPower();
|
|
power_mW = power_W * 1000;
|
|
newtime = millis();
|
|
tick = newtime - lastread;
|
|
if (measuring_run) {
|
|
Ah += (current_A * tick)/3600000.0;
|
|
mAh = Ah * 1000;
|
|
Wh += (power_W * tick)/3600000.0;
|
|
mWh = Wh * 1000;
|
|
|
|
if (shuntvoltage_V_max < shuntvoltage_V) {
|
|
shuntvoltage_V_max = shuntvoltage_V;
|
|
}
|
|
if (current_A_max < current_A) {
|
|
current_A_max = current_A;
|
|
}
|
|
if (power_W_max < power_W) {
|
|
power_W_max = power_W;
|
|
}
|
|
if (shuntvoltage_V_min > shuntvoltage_V) {
|
|
shuntvoltage_V_min = shuntvoltage_V;
|
|
}
|
|
if (current_A_min > current_A) {
|
|
current_A_min = current_A;
|
|
}
|
|
if (power_W_min > power_W) {
|
|
power_W_min = power_W;
|
|
}
|
|
}
|
|
if (reset_actual) {
|
|
Ah = 0;
|
|
Wh = 0;
|
|
data_timestamp = 0;
|
|
reset_actual = false;
|
|
shuntvoltage_V_max = 0;
|
|
current_A_max = 0;
|
|
power_W_max = 0;
|
|
shuntvoltage_V_min = 0;
|
|
current_A_min = 0;
|
|
power_W_min = 0;
|
|
}
|
|
if (measuring_run && measuring_init == false) {
|
|
shuntvoltage_V_max = 0;
|
|
current_A_max = 0;
|
|
power_W_max = 0;
|
|
shuntvoltage_V_min = shuntvoltage_V;
|
|
current_A_min = current_A;
|
|
power_W_min = power_W;
|
|
measuring_init = true;
|
|
}
|
|
lastread = newtime;
|
|
ina219.recalibrate();
|
|
ina219.reconfig();
|
|
}
|
|
|
|
void read_bat() {
|
|
battery_average += (analogRead(adc_battery_pin) - battery_average) * 0.3;
|
|
battery_voltage = map(battery_average,0,4096,0,450)/100.0;
|
|
if (battery_voltage < 3.3 && battery_low == false) {
|
|
battery_low = true;
|
|
lcd.noBacklight();
|
|
}
|
|
if (battery_voltage > 3.6 && battery_low) {
|
|
battery_low = false;
|
|
digitalWrite(led_lowbat_pin,LOW);
|
|
lcd.backlight();
|
|
}
|
|
}
|
|
|
|
void read_button() {
|
|
if (digitalRead(button1_pin)) {
|
|
button1 = false;
|
|
}
|
|
else {
|
|
button1 = true;
|
|
}
|
|
if (digitalRead(button2_pin)) {
|
|
button2 = false;
|
|
}
|
|
else {
|
|
button2 = true;
|
|
}
|
|
if (digitalRead(button3_pin)) {
|
|
button3 = false;
|
|
}
|
|
else {
|
|
button3 = true;
|
|
}
|
|
if (button1 && test_mode == false) {
|
|
reset_actual = true;
|
|
}
|
|
if (button2 && measuring_run && test_mode == false) {
|
|
measuring_run = false;
|
|
} else if (button2 && measuring_run == false) {
|
|
measuring_run = true;
|
|
}
|
|
if (measuring_run) {
|
|
digitalWrite(led_run_pin,true);
|
|
} else {
|
|
digitalWrite(led_run_pin,false);
|
|
}
|
|
if (button3 && lcd_light) {
|
|
lcd_light = false;
|
|
lcd.noBacklight();
|
|
} else if (button3 && lcd_light == false) {
|
|
lcd_light = true;
|
|
lcd.backlight();
|
|
}
|
|
}
|
|
|
|
void web_measuring_stop(AsyncWebServerRequest *request) {
|
|
measuring_run = false;
|
|
request->send(200);
|
|
}
|
|
|
|
void web_measuring_run(AsyncWebServerRequest *request) {
|
|
measuring_run = true;
|
|
request->send(200);
|
|
}
|
|
|
|
void web_measuring_reset(AsyncWebServerRequest *request) {
|
|
reset_actual = true;
|
|
request->send(200);
|
|
}
|
|
|
|
void web_get_values(AsyncWebServerRequest *request) {
|
|
String web_status;
|
|
if (measuring_run) {
|
|
web_status = "Running";
|
|
}
|
|
else {
|
|
web_status = "Stopped";
|
|
}
|
|
request->send( 200, "application/json", "{\"voltage\":" + String(busvoltage_V) + ", \"current\":" + String(current_A) + ", \"power\":" + String(power_W) + ", \"Ah\":" + String(Ah) + ", \"Wh\":" + String(Wh) + ", \"battery\": " + String(battery_voltage) + ", \"run\":\"" + String(web_status) + "\", \"shuntvoltage_V_max\":" + String(shuntvoltage_V_max) + ", \"shuntvoltage_V_min\":" + String(shuntvoltage_V_min) + ", \"current_A_max\":" + String(current_A_max) + ", \"current_A_min\":" + String(current_A_min) + ", \"power_W_max\":" + String(power_W_max) + ", \"power_W_min\":" + String(power_W_min) + "}");
|
|
}
|
|
|
|
void data_logging() {
|
|
if (measuring_run && test_mode == false) {
|
|
File datalog = SPIFFS.open("/datalog.csv", "a");
|
|
datalog.print(data_timestamp); datalog.print(',');
|
|
datalog.print(busvoltage_V); datalog.print(',');
|
|
datalog.print(current_A); datalog.print(',');
|
|
datalog.print(power_W); datalog.print(',');
|
|
datalog.print(Ah); datalog.print(',');
|
|
datalog.println(Wh);
|
|
datalog.close();
|
|
data_timestamp++;
|
|
}
|
|
} |