Merge branch 'main' into feature/mqtt-on-main

This commit is contained in:
Cabooman 2024-02-02 18:23:46 +01:00
commit 2cea5bd578
9 changed files with 565 additions and 11 deletions

View file

@ -2,9 +2,11 @@
/* Only change battery specific settings in "USER_SETTINGS.h" */
#include <Arduino.h>
#include <Preferences.h>
#include "HardwareSerial.h"
#include "USER_SETTINGS.h"
#include "src/battery/BATTERIES.h"
#include "src/charger/CHARGERS.h"
#include "src/devboard/config.h"
#include "src/inverter/INVERTERS.h"
#include "src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h"
@ -18,6 +20,8 @@
#include "src/devboard/webserver/webserver.h"
#endif
Preferences settings; // Store user settings
// Interval settings
int intervalUpdateValues = 4800; // Interval at which to update inverter values / Modbus registers
const int interval10 = 10; // Interval for 10ms tasks
@ -68,6 +72,21 @@ uint16_t cell_max_voltage = 3700; // Stores the highest cell voltage value
uint16_t cell_min_voltage = 3700; // Stores the minimum cell voltage value in the system
bool LFP_Chemistry = false;
// Common charger parameters
volatile float charger_setpoint_HV_VDC = 0.0f;
volatile float charger_setpoint_HV_IDC = 0.0f;
volatile float charger_setpoint_HV_IDC_END = 0.0f;
bool charger_HV_enabled = false;
bool charger_aux12V_enabled = false;
// Common charger statistics, instantaneous values
float charger_stat_HVcur = 0;
float charger_stat_HVvol = 0;
float charger_stat_ACcur = 0;
float charger_stat_ACvol = 0;
float charger_stat_LVcur = 0;
float charger_stat_LVvol = 0;
// LED parameters
Adafruit_NeoPixel pixels(1, WS2812_PIN, NEO_GRB + NEO_KHZ800);
static uint8_t brightness = 0;
@ -102,6 +121,8 @@ bool inverterAllowsContactorClosing = true;
void setup() {
init_serial();
init_stored_settings();
#ifdef WEBSERVER
init_webserver();
#endif
@ -172,6 +193,37 @@ void init_serial() {
Serial.println("__ OK __");
}
void init_stored_settings() {
settings.begin("batterySettings", false);
#ifndef LOAD_SAVED_SETTINGS_ON_BOOT
settings.clear(); // If this clear function is executed, no settings will be read from storage
#endif
static uint16_t temp = 0;
temp = settings.getUInt("BATTERY_WH_MAX", false);
if (temp != 0) {
BATTERY_WH_MAX = temp;
}
temp = settings.getUInt("MAXPERCENTAGE", false);
if (temp != 0) {
MAXPERCENTAGE = temp;
}
temp = settings.getUInt("MINPERCENTAGE", false);
if (temp != 0) {
MINPERCENTAGE = temp;
}
temp = settings.getUInt("MAXCHARGEAMP", false);
if (temp != 0) {
MAXCHARGEAMP = temp;
}
temp = settings.getUInt("MAXDISCHARGEAMP", false);
if (temp != 0) {
MAXDISCHARGEAMP = temp;
}
settings.end();
}
void init_CAN() {
// CAN pins
pinMode(CAN_SE_PIN, OUTPUT);
@ -363,6 +415,9 @@ void receive_can() { // This section checks if we have a complete CAN message i
#endif
#ifdef SMA_CAN
receive_can_sma(rx_frame);
#endif
#ifdef CHEVYVOLT_CHARGER
receive_can_chevyvolt_charger(rx_frame);
#endif
} else {
//printf("New extended frame");
@ -422,6 +477,9 @@ void send_can() {
#ifdef TEST_FAKE_BATTERY
send_can_test_battery();
#endif
#ifdef CHEVYVOLT_CHARGER
send_can_chevyvolt_charger();
#endif
}
#ifdef DUAL_CAN
@ -672,3 +730,13 @@ void init_serialDataLink() {
Serial2.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
#endif
}
void storeSettings() {
settings.begin("batterySettings", false);
settings.putUInt("BATTERY_WH_MAX", BATTERY_WH_MAX);
settings.putUInt("MAXPERCENTAGE", MAXPERCENTAGE);
settings.putUInt("MINPERCENTAGE", MINPERCENTAGE);
settings.putUInt("MAXCHARGEAMP", MAXCHARGEAMP);
settings.putUInt("MAXDISCHARGEAMP", MAXDISCHARGEAMP);
settings.end();
}

View file

@ -21,6 +21,13 @@ volatile uint16_t MAXDISCHARGEAMP =
const char* mqtt_user = "REDACTED";
const char* mqtt_password = "REDACTED";
#endif // USE_MQTT
/* Charger settings */
volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack
volatile float CHARGER_MAX_HV = 420; // Max permissible output (VDC) of charger
volatile float CHARGER_MIN_HV = 200; // Min permissible output (VDC) of charger
volatile float CHARGER_MAX_POWER = 3300; // Max power capable of charger, as a ceiling for validating config
volatile float CHARGER_MAX_A = 11.5; // Max current output (amps) of charger
volatile float CHARGER_END_A = 1.0; // Current at which charging is considered complete
#ifdef WEBSERVER
volatile uint8_t AccessPointEnabled =
@ -29,5 +36,5 @@ const char* ssid = "REPLACE_WITH_YOUR_SSID"; // Maximum of 63 character
const char* password = "REPLACE_WITH_YOUR_PASSWORD"; // Minimum of 8 characters;
const char* ssidAP = "Battery Emulator"; // Maximum of 63 characters;
const char* passwordAP = "123456789"; // Minimum of 8 characters; set to NULL if you want the access point to be open
const char* versionNumber = "4.4.0"; // The current software version, shown on webserver
const char* versionNumber = "4.5.0"; // The current software version, shown on webserver
#endif

View file

@ -38,6 +38,10 @@
//#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery)
//#define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.
//#define MQTT // Enable this line to enable MQTT
//#define LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot
/* Select charger used (Optional) */
//#define CHEVYVOLT_CHARGER //Enable this line to control a Chevrolet Volt charger connected to battery - for example, when generator charging or using an inverter without a charging function.
/* Battery limits: These are set in the USER_SETTINGS.cpp file, or later on via the Webserver */
extern volatile uint16_t BATTERY_WH_MAX;
@ -46,4 +50,18 @@ extern volatile uint16_t MINPERCENTAGE;
extern volatile uint16_t MAXCHARGEAMP;
extern volatile uint16_t MAXDISCHARGEAMP;
extern volatile uint8_t AccessPointEnabled;
/* Charger limits: Set in the USER_SETTINGS.cpp or later in the webserver */
extern volatile float charger_setpoint_HV_VDC;
extern volatile float charger_setpoint_HV_IDC;
extern volatile float charger_setpoint_HV_IDC_END;
extern volatile float CHARGER_SET_HV;
extern volatile float CHARGER_MAX_HV;
extern volatile float CHARGER_MIN_HV;
extern volatile float CHARGER_MAX_POWER;
extern volatile float CHARGER_MAX_A;
extern volatile float CHARGER_END_A;
extern bool charger_HV_enabled;
extern bool charger_aux12V_enabled;
#endif

View file

@ -32,8 +32,8 @@ static uint16_t volts = 0; // V
static int16_t amps = 0; // A
static int16_t power = 0; // W
static uint16_t raw_amps = 0; // A
static int16_t max_temp = 6; // C*
static int16_t min_temp = 5; // C*
static int16_t max_temp = 0; // C*
static int16_t min_temp = 0; // C*
static uint16_t energy_buffer = 0;
static uint16_t energy_to_charge_complete = 0;
static uint16_t expected_energy_remaining = 0;
@ -209,10 +209,8 @@ void update_values_tesla_model_3_battery() { //This function maps all the value
power = ((volts / 10) * amps);
stat_batt_power = convert2unsignedInt16(power);
min_temp = (min_temp * 10);
temperature_min = convert2unsignedInt16(min_temp);
max_temp = (max_temp * 10);
temperature_max = convert2unsignedInt16(max_temp);
cell_max_voltage = cell_max_v;
@ -443,11 +441,8 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame) {
}
if (mux == 0) //Temperature sensors
{
temp = rx_frame.data.u8[2];
max_temp = (temp * 0.5) - 40; //in celcius, Example 24
temp = rx_frame.data.u8[3];
min_temp = (temp * 0.5) - 40; //in celcius , Example 24
max_temp = (rx_frame.data.u8[2] * 5) - 400; //Temperature values have 40.0*C offset, 0.5*C /bit
min_temp = (rx_frame.data.u8[3] * 5) - 400; //Multiply by 5 and remove offset to get C+1 (0x61*5=485-400=8.5*C)
}
break;
case 0x2d2:

View file

@ -0,0 +1,8 @@
#ifndef CHARGERS_H
#define CHARGERS_H
#ifdef CHEVYVOLT_CHARGER
#include "chevyvolt.h"
#endif
#endif

View file

@ -0,0 +1,198 @@
#include "chevyvolt.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
/* This implements Chevy Volt / Ampera charger support (2011-2015 model years).
*
* This code is intended to facilitate battery charging while repurposing inverters
* that lack embedded charging features, to facilitate standalone charging, etc.
*
* Key influence and inspiration informed by prior work by those such as:
* Damien Maguire (evmbw.org, github.com/damienmaguire/AmperaCharger)
* Tom deBree, Arber Kramer, Colin Kidder, EVTV, etc
* Various implementation details aided by openinverter forum discussion/CAN logs
*
* It is very likely that Lear charger support could be added to this with minimal effort
* (similar hardware, different firmware and CAN messages).
*
* 2024 smaresca
*/
/* CAN cycles and timers */
static const int interval30ms = 30; // 30ms cycle for keepalive frames
static const int interval200ms = 200; // 200ms cycle for commanding I/V targets
static const int interval5000ms = 5000; // 5s status printout to serial
static unsigned long previousMillis30ms = 0;
static unsigned long previousMillis200ms = 0;
static unsigned long previousMillis5000ms = 0;
/* voltage and current settings. Validation performed to set ceiling of 3300w vol*cur */
extern volatile float charger_setpoint_HV_VDC;
extern volatile float charger_setpoint_HV_IDC;
extern volatile float charger_setpoint_HV_IDC_END;
extern bool charger_HV_enabled;
extern bool charger_aux12V_enabled;
extern float charger_stat_HVcur;
extern float charger_stat_HVvol;
extern float charger_stat_ACcur;
extern float charger_stat_ACvol;
extern float charger_stat_LVcur;
extern float charger_stat_LVvol;
enum CHARGER_MODES : uint8_t { MODE_DISABLED = 0, MODE_LV, MODE_HV, MODE_HVLV };
//Actual content messages
static CAN_frame_t charger_keepalive_frame = {.FIR = {.B =
{
//one byte only, indicating enabled or disabled
.DLC = 1,
.FF = CAN_frame_std,
}},
.MsgID = 0x30E,
.data = {MODE_DISABLED}};
static CAN_frame_t charger_set_targets = {.FIR = {.B =
{
.DLC = 4,
.FF = CAN_frame_std,
}},
.MsgID = 0x304,
// data[0] is a static value, meaning unknown
.data = {0x40, 0x00, 0x00, 0x00}};
/* We are mostly sending out not receiving */
void receive_can_chevyvolt_charger(CAN_frame_t rx_frame) {
uint16_t charger_stat_HVcur_temp = 0;
uint16_t charger_stat_HVvol_temp = 0;
uint16_t charger_stat_LVcur_temp = 0;
uint16_t charger_stat_LVvol_temp = 0;
uint16_t charger_stat_ACcur_temp = 0;
uint16_t charger_stat_ACvol_temp = 0;
switch (rx_frame.MsgID) {
//ID 0x212 conveys instantaneous DC charger stats
case 0x212:
charger_stat_HVcur_temp = (uint16_t)(rx_frame.data.u8[0] << 8 | rx_frame.data.u8[1]);
charger_stat_HVcur = (float)(charger_stat_HVcur_temp >> 3) * 0.05;
charger_stat_HVvol_temp = (uint16_t)((((rx_frame.data.u8[1] << 8 | rx_frame.data.u8[2])) >> 1) & 0x3ff);
charger_stat_HVvol = (float)(charger_stat_HVvol_temp) * .5;
charger_stat_LVcur_temp = (uint16_t)(((rx_frame.data.u8[2] << 8 | rx_frame.data.u8[3]) >> 1) & 0x00ff);
charger_stat_LVcur = (float)(charger_stat_LVcur_temp) * .2;
charger_stat_LVvol_temp = (uint16_t)(((rx_frame.data.u8[3] << 8 | rx_frame.data.u8[4]) >> 1) & 0x00ff);
charger_stat_LVvol = (float)(charger_stat_LVvol_temp) * .1;
break;
//ID 0x30A conveys instantaneous AC charger stats
case 0x30A:
charger_stat_ACcur_temp = (uint16_t)((rx_frame.data.u8[0] << 8 | rx_frame.data.u8[1]) >> 4);
charger_stat_ACcur = (float)(charger_stat_ACcur_temp) * 0.2;
charger_stat_ACvol_temp = (uint16_t)(((rx_frame.data.u8[1] << 8 | rx_frame.data.u8[2]) >> 4) & 0x00ff);
charger_stat_ACvol = (float)(charger_stat_ACvol_temp) * 2;
break;
//ID 0x266, 0x268, and 0x308 are regularly emitted by the charger but content is unknown
// 0x266 and 0x308 are len 5
// 0x268 may be temperature data (len 8). Could resemble the Lear charger equivalent TODO
case 0x266:
break;
case 0x268:
break;
case 0x308:
break;
default:
#ifdef DEBUG_VIA_USB
Serial.printf("CAN Rcv unknown frame MsgID=%x\n", rx_frame.MsgID);
#endif
break;
}
}
void send_can_chevyvolt_charger() {
unsigned long currentMillis = millis();
uint16_t Vol_temp = 0;
uint16_t setpoint_HV_VDC = floor(charger_setpoint_HV_VDC);
uint16_t setpoint_HV_IDC = floor(charger_setpoint_HV_IDC);
uint16_t setpoint_HV_IDC_END = floor(charger_setpoint_HV_IDC_END);
uint8_t charger_mode = MODE_DISABLED;
/* Send keepalive with mode every 30ms */
if (currentMillis - previousMillis30ms >= interval30ms) {
previousMillis30ms = currentMillis;
if (charger_HV_enabled) {
charger_mode += MODE_HV;
/* disable HV if end amperage reached
* TODO - integration opportunity with battery/inverter code
if (setpoint_HV_IDC_END > 0 && charger_stat_HVcur > setpoint_HV_IDC_END) {
charger_mode -= MODE_HV;
}
*/
}
if (charger_aux12V_enabled)
charger_mode += MODE_LV;
charger_keepalive_frame.data.u8[0] = charger_mode;
ESP32Can.CANWriteFrame(&charger_keepalive_frame);
}
/* Send current targets every 200ms */
if (currentMillis - previousMillis200ms >= interval200ms) {
previousMillis200ms = currentMillis;
/* These values should be and are validated elsewhere, but adjust if needed
* to stay within limits of hardware and user-supplied settings
*/
if (setpoint_HV_VDC > CHARGER_MAX_HV) {
setpoint_HV_VDC = CHEVYVOLT_MAX_HVDC;
}
if (setpoint_HV_VDC < CHARGER_MIN_HV && setpoint_HV_VDC > 0) {
setpoint_HV_VDC = CHEVYVOLT_MIN_HVDC;
}
if (setpoint_HV_IDC > CHARGER_MAX_A) {
setpoint_HV_VDC = CHEVYVOLT_MAX_AMP;
}
/* if power overcommitted, back down to just below while maintaining voltage target */
if (setpoint_HV_IDC * setpoint_HV_VDC > CHARGER_MAX_POWER) {
setpoint_HV_IDC = floor(CHARGER_MAX_POWER / setpoint_HV_VDC);
}
/* current setting */
charger_set_targets.data.u8[1] = setpoint_HV_IDC * 20;
Vol_temp = setpoint_HV_VDC * 2;
/* first 2 bits are MSB of the voltage command */
charger_set_targets.data.u8[2] = highByte(Vol_temp);
/* LSB of the voltage command. Then MSB LSB is divided by 2 */
charger_set_targets.data.u8[3] = lowByte(Vol_temp);
ESP32Can.CANWriteFrame(&charger_set_targets);
}
#ifdef DEBUG_VIA_USB
/* Serial echo every 5s of charger stats */
if (currentMillis - previousMillis5000ms >= interval5000ms) {
previousMillis5000ms = currentMillis;
Serial.printf("Charger AC in IAC=%fA VAC=%fV\n", charger_stat_ACcur, charger_stat_ACvol);
Serial.printf("Charger HV out IDC=%fA VDC=%fV\n", charger_stat_HVcur, charger_stat_HVvol);
Serial.printf("Charger LV out IDC=%fA VDC=%fV\n", charger_stat_LVcur, charger_stat_LVvol);
Serial.printf("Charger mode=%s\n", (charger_mode > MODE_DISABLED) ? "Enabled" : "Disabled");
Serial.printf("Charger HVset=%uV,%uA finishCurrent=%uA\n", setpoint_HV_VDC, setpoint_HV_IDC, setpoint_HV_IDC_END);
}
#endif
}

View file

@ -0,0 +1,21 @@
#ifndef CHEVYVOLT_H
#define CHEVYVOLT_H
#include <Arduino.h>
#include "../../USER_SETTINGS.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
/* Charger hardware limits
*
* Relative to runtime settings, expectations are:
* hw minimum <= setting minimum <= setting maximum <= hw max
*/
#define CHEVYVOLT_MAX_HVDC 420.0
#define CHEVYVOLT_MIN_HVDC 200.0
#define CHEVYVOLT_MAX_AMP 11.5
#define CHEVYVOLT_MAX_POWER 3300
void update_values_can_chevyvolt_charger();
void send_can_chevyvolt_charger();
void receive_can_chevyvolt_charger(CAN_frame_t rx_frame);
#endif

View file

@ -1,4 +1,7 @@
#include "webserver.h"
#include <Preferences.h>
Preferences preferences3;
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
@ -65,6 +68,7 @@ void init_webserver() {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
BATTERY_WH_MAX = value.toInt();
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
@ -76,6 +80,7 @@ void init_webserver() {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
MAXPERCENTAGE = value.toInt() * 10;
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
@ -87,6 +92,7 @@ void init_webserver() {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
MINPERCENTAGE = value.toInt() * 10;
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
@ -98,6 +104,7 @@ void init_webserver() {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
MAXCHARGEAMP = value.toInt() * 10;
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
@ -109,12 +116,92 @@ void init_webserver() {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
MAXDISCHARGEAMP = value.toInt() * 10;
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
}
});
#ifdef CHEVYVOLT_CHARGER
// Route for editing ChargerTargetV
server.on("/updateChargeSetpointV", HTTP_GET, [](AsyncWebServerRequest* request) {
if (!request->hasParam("value")) {
request->send(400, "text/plain", "Bad Request");
}
String value = request->getParam("value")->value();
float val = value.toFloat();
if (!(val <= CHARGER_MAX_HV && val >= CHARGER_MIN_HV)) {
request->send(400, "text/plain", "Bad Request");
}
if (!(val * charger_setpoint_HV_IDC <= CHARGER_MAX_POWER)) {
request->send(400, "text/plain", "Bad Request");
}
charger_setpoint_HV_VDC = val;
request->send(200, "text/plain", "Updated successfully");
});
// Route for editing ChargerTargetA
server.on("/updateChargeSetpointA", HTTP_GET, [](AsyncWebServerRequest* request) {
if (!request->hasParam("value")) {
request->send(400, "text/plain", "Bad Request");
}
String value = request->getParam("value")->value();
float val = value.toFloat();
if (!(val <= MAXCHARGEAMP && val <= CHARGER_MAX_A)) {
request->send(400, "text/plain", "Bad Request");
}
if (!(val * charger_setpoint_HV_VDC <= CHARGER_MAX_POWER)) {
request->send(400, "text/plain", "Bad Request");
}
charger_setpoint_HV_IDC = value.toFloat();
request->send(200, "text/plain", "Updated successfully");
});
// Route for editing ChargerEndA
server.on("/updateChargeEndA", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
charger_setpoint_HV_IDC_END = value.toFloat();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
}
});
// Route for enabling/disabling HV charger
server.on("/updateChargerHvEnabled", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
charger_HV_enabled = (bool)value.toInt();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
}
});
// Route for enabling/disabling aux12v charger
server.on("/updateChargerAux12vEnabled", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
charger_aux12V_enabled = (bool)value.toInt();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
}
});
#endif
// Send a GET request to <ESP_IP>/update
server.on("/debug", HTTP_GET,
[](AsyncWebServerRequest* request) { request->send(200, "text/plain", "Debug: all OK."); });
@ -303,6 +390,15 @@ String processor(const String& var) {
content += "Fake battery for testing purposes";
#endif
content += "</h4>";
#ifdef CHEVYVOLT_CHARGER
content += "<h4 style='color: white;'>Charger protocol: ";
#ifdef CHEVYVOLT_CHARGER
content += "Chevy Volt Gen1 Charger";
#endif
content += "</h4>";
#endif
// Close the block
content += "</div>";
@ -396,6 +492,42 @@ String processor(const String& var) {
content += "<span style='color: red;'>&#10005;</span></h4>";
}
#ifdef CHEVYVOLT_CHARGER
content += "<h4>Charger HV Enabled: ";
if (charger_HV_enabled) {
content += "<span>&#10003;</span>";
} else {
content += "<span style='color: red;'>&#10005;</span>";
}
content += "</h4>";
content += "<h4>Charger Aux12v Enabled: ";
if (charger_aux12V_enabled) {
content += "<span>&#10003;</span>";
} else {
content += "<span style='color: red;'>&#10005;</span>";
}
content += "</h4>";
float chgPwrDC = static_cast<float>(charger_stat_HVcur * charger_stat_HVvol);
float chgPwrAC = static_cast<float>(charger_stat_ACcur * charger_stat_ACvol);
float chgEff = chgPwrDC / chgPwrAC * 100;
float ACcur = charger_stat_ACcur;
float ACvol = charger_stat_ACvol;
float HVvol = charger_stat_HVvol;
float HVcur = charger_stat_HVcur;
float LVvol = charger_stat_LVvol;
float LVcur = charger_stat_LVcur;
content += formatPowerValue("Charger Output Power", chgPwrDC, "", 1);
content += "<h4 style='color: white;'>Charger Efficiency: " + String(chgEff) + "%</h4>";
content += "<h4 style='color: white;'>Charger HVDC Output V: " + String(HVvol, 2) + "</h4>";
content += "<h4 style='color: white;'>Charger HVDC Output I: " + String(HVcur, 2) + "</h4>";
content += "<h4 style='color: white;'>Charger LVDC Output I: " + String(LVcur, 2) + "</h4>";
content += "<h4 style='color: white;'>Charger LVDC Output V: " + String(LVvol, 2) + "</h4>";
content += "<h4 style='color: white;'>Charger AC Input V: " + String(ACvol, 2) + "VAC</h4>";
content += "<h4 style='color: white;'>Charger AC Input I: " + String(ACvol, 2) + "VAC</h4>";
#endif
// Close the block
content += "</div>";
@ -450,6 +582,28 @@ String settings_processor(const String& var) {
" A </span> <button onclick='editMaxChargeA()'>Edit</button></h4>";
content += "<h4 style='color: white;'>Max discharge speed: " + String(MAXDISCHARGEAMP / 10.0, 1) +
" A </span> <button onclick='editMaxDischargeA()'>Edit</button></h4>";
#ifdef CHEVYVOLT_CHARGER
content += "<h4 style='color: white;'>Charger HVDC Enabled: ";
if (charger_HV_enabled) {
content += "<span>&#10003;</span>";
} else {
content += "<span style='color: red;'>&#10005;</span>";
}
content += " <button onclick='editChargerHVDCEnabled()'>Edit</button></h4>";
content += "<h4 style='color: white;'>Charger Aux12VDC Enabled: ";
if (charger_aux12V_enabled) {
content += "<span>&#10003;</span>";
} else {
content += "<span style='color: red;'>&#10005;</span>";
}
content += " <button onclick='editChargerAux12vEnabled()'>Edit</button></h4>";
content += "<h4 style='color: white;'>Charger Voltage Setpoint: " + String(charger_setpoint_HV_VDC, 1) +
" V </span> <button onclick='editChargerSetpointVDC()'>Edit</button></h4>";
content += "<h4 style='color: white;'>Charger Current Setpoint: " + String(charger_setpoint_HV_IDC, 1) +
" A </span> <button onclick='editChargerSetpointIDC()'>Edit</button></h4>";
#endif
content += "<script>";
content += "function editWh() {";
@ -520,6 +674,81 @@ String settings_processor(const String& var) {
content += " }";
content += "}";
content += "}";
#ifdef CHEVYVOLT_CHARGER
content += "function editChargerHVDCEnabled() {";
content += " var value = prompt('Enable or disable HV DC output. Enter 1 for enabled, 0 for disabled');";
content += " if (value !== null) {";
content += " if (value == 0 || value == 1) {";
content += " var xhr = new XMLHttpRequest();";
content += " xhr.open('GET', '/updateChargerHvEnabled?value=' + value, true);";
content += " xhr.send();";
content += " }";
content += " } else {";
content += " alert('Invalid value. Please enter 1 or 0');";
content += " }";
content += "}";
content += "function editChargerAux12vEnabled() {";
content +=
"var value = prompt('Enable or disable low voltage 12v auxiliary DC output. Enter 1 for enabled, 0 for "
"disabled');";
content += "if (value !== null) {";
content += " if (value == 0 || value == 1) {";
content += " var xhr = new XMLHttpRequest();";
content += " xhr.open('GET', '/updateChargerAux12vEnabled?value=' + value, true);";
content += " xhr.send();";
content += " } else {";
content += " alert('Invalid value. Please enter 1 or 0');";
content += " }";
content += "}";
content += "}";
content += "function editChargerSetpointVDC() {";
content +=
"var value = prompt('Set charging voltage. Input will be validated against inverter and/or charger "
"configuration parameters, but use sensible values like 200 to 420.');";
content += "if (value !== null) {";
content += " if (value >= 0 && value <= 1000) {";
content += " var xhr = new XMLHttpRequest();";
content += " xhr.open('GET', '/updateChargeSetpointV?value=' + value, true);";
content += " xhr.send();";
content += " } else {";
content += " alert('Invalid value. Please enter a value between 0 and 1000');";
content += " }";
content += "}";
content += "}";
content += "function editChargerSetpointIDC() {";
content +=
"var value = prompt('Set charging amperage. Input will be validated against inverter and/or charger "
"configuration parameters, but use sensible values like 6 to 48.');";
content += "if (value !== null) {";
content += " if (value >= 0 && value <= 1000) {";
content += " var xhr = new XMLHttpRequest();";
content += " xhr.open('GET', '/updateChargeSetpointA?value=' + value, true);";
content += " xhr.send();";
content += " } else {";
content += " alert('Invalid value. Please enter a value between 0 and 100');";
content += " }";
content += "}";
content += "}";
content += "function editChargerSetpointEndI() {";
content +=
"var value = prompt('Set amperage that terminates charge as being sufficiently complete. Input will be "
"validated against inverter and/or charger configuration parameters, but use sensible values like 1-5.');";
content += "if (value !== null) {";
content += " if (value >= 0 && value <= 1000) {";
content += " var xhr = new XMLHttpRequest();";
content += " xhr.open('GET', '/updateChargeEndA?value=' + value, true);";
content += " xhr.send();";
content += " } else {";
content += " alert('Invalid value. Please enter a value between 0 and 100');";
content += " }";
content += "}";
content += "}";
#endif
content += "</script>";
// Close the block

View file

@ -1,7 +1,7 @@
#ifndef WEBSERVER_H
#define WEBSERVER_H
// Load Wi-Fi library
#include <Preferences.h>
#include <WiFi.h>
#include "../../../USER_SETTINGS.h" // Needed for WiFi ssid and password
#include "../../lib/ayushsharma82-ElegantOTA/src/ElegantOTA.h"
@ -41,6 +41,14 @@ extern const char* ssidAP;
extern const char* passwordAP;
extern const char* versionNumber;
// Common charger parameters
extern float charger_stat_HVcur;
extern float charger_stat_HVvol;
extern float charger_stat_ACcur;
extern float charger_stat_ACvol;
extern float charger_stat_LVcur;
extern float charger_stat_LVvol;
/**
* @brief Initialization function for the webserver.
*
@ -134,4 +142,6 @@ void onOTAEnd(bool success);
template <typename T>
String formatPowerValue(String label, T value, String unit, int precision);
extern void storeSettings();
#endif