mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 02:09:30 +02:00
Merge branch 'main' into feature/mqtt-on-main
This commit is contained in:
commit
2cea5bd578
9 changed files with 565 additions and 11 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
8
Software/src/charger/CHARGERS.h
Normal file
8
Software/src/charger/CHARGERS.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef CHARGERS_H
|
||||
#define CHARGERS_H
|
||||
|
||||
#ifdef CHEVYVOLT_CHARGER
|
||||
#include "chevyvolt.h"
|
||||
#endif
|
||||
|
||||
#endif
|
198
Software/src/charger/chevyvolt.cpp
Normal file
198
Software/src/charger/chevyvolt.cpp
Normal 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
|
||||
}
|
21
Software/src/charger/chevyvolt.h
Normal file
21
Software/src/charger/chevyvolt.h
Normal 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
|
|
@ -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;'>✕</span></h4>";
|
||||
}
|
||||
|
||||
#ifdef CHEVYVOLT_CHARGER
|
||||
content += "<h4>Charger HV Enabled: ";
|
||||
if (charger_HV_enabled) {
|
||||
content += "<span>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
content += "</h4>";
|
||||
|
||||
content += "<h4>Charger Aux12v Enabled: ";
|
||||
if (charger_aux12V_enabled) {
|
||||
content += "<span>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</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>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
content += " <button onclick='editChargerHVDCEnabled()'>Edit</button></h4>";
|
||||
|
||||
content += "<h4 style='color: white;'>Charger Aux12VDC Enabled: ";
|
||||
if (charger_aux12V_enabled) {
|
||||
content += "<span>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue