Skip to content

Commit

Permalink
Merge branch 'main' into feature/double-lilygo
Browse files Browse the repository at this point in the history
  • Loading branch information
dalathegreat authored Dec 18, 2023
2 parents 90bb3fe + 4801eda commit 2ab1970
Show file tree
Hide file tree
Showing 98 changed files with 14,611 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# Ignore any .vscode folder
*.vscode/

# Ignore any files in the build folder
Software/build/
4 changes: 3 additions & 1 deletion Software/Software.ino
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ void setup() {
// Perform main program functions
void loop() {

runSerialDataLink();
#ifdef WEBSERVER
// Over-the-air updates by ElegantOTA
ElegantOTA.loop();
Expand All @@ -145,6 +144,9 @@ void loop() {
#ifdef DUAL_CAN
receive_can2();
#endif
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
runSerialDataLink();
#endif

// Process
if (millis() - previousMillis10ms >= interval10) // Every 10ms
Expand Down
8 changes: 8 additions & 0 deletions Software/USER_SETTINGS.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "USER_SETTINGS.h"

#ifdef WEBSERVER
const char* ssid = "REPLACE_WITH_YOUR_SSID"; // maximum of 63 characters;
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
#endif
1 change: 1 addition & 0 deletions Software/USER_SETTINGS.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@
//#define DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 controller (Needed for FoxESS inverters)
//#define SERIAL_LINK_RECEIVER //Enable this line to receive battery data over RS485 pins from another Lilygo (This LilyGo interfaces with inverter)
//#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

#endif
1 change: 0 additions & 1 deletion Software/src/battery/TESLA-MODEL-3-BATTERY.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ static uint8_t packContactorSetState = 0;
static uint8_t packCtrsClosingAllowed = 0;
static uint8_t pyroTestInProgress = 0;
static uint8_t send221still = 10;
static bool LFP_Chemistry = false;
//Fault codes
static uint8_t WatchdogReset = 0; //Warns if the processor has experienced a reset due to watchdog reset.
static uint8_t PowerLossReset = 0; //Warns if the processor has experienced a reset due to power loss.
Expand Down
2 changes: 2 additions & 0 deletions Software/src/battery/TESLA-MODEL-3-BATTERY.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ extern uint16_t cell_min_voltage; //mV, 0-4350
extern uint8_t LEDcolor; //Enum, 0-10
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
extern bool LFP_Chemistry;

// Definitions for BMS status
#define STANDBY 0
#define INACTIVE 1
Expand Down
39 changes: 39 additions & 0 deletions Software/src/devboard/webserver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Battery Emulator Webserver
This webserver creates a WiFi access point. It also connects ot an existing network.
The webserver intends to display useful information to the user of the battery emulator
development board, without the need to physically connect to the board via USB.
The webserver implementation also provides the option to update the firmware of the board over the air.

To use the webserver, follow the following steps:
- Connect to the board via Serial, and boot up the board.
- The IP address of the WiFi access point is printed to Serial when the board boots up. Note this down.
- Connect to the access point created by board via a WiFi-capable device
- On that device, open a webbrowser and type the IP address of the WiFi access point.
- If the ssid and password of an existing WiFi network are provided, the board will also connect to this network. The IP address obtained on the existing network is shown in the webserver. Note this down.
- From this point onwards, any device connected to the existing WiFi network can access the webserver via a webbrowser. To do this:
- Connect your WiFi-capable device to the existing nwetork.
- Open a webbrowser and type the IP address obtained on the existing WiFi network.

To update the software over the air:
- In Arduino, go to `Sketch` > `Export Compiled Binary`. This will create the `.bin` file that you need to update the firmware. It is found in the folder `Software/build/`
- In your webbrowser, go to the url consisting of the IP address, followed by `/update`, for instance `http://192.168.0.224/update`.
- In the webbrowser, follow the steps to select the `.bin` file and to upload the file to the board.

## Future work
This section lists a number of features that can be implemented as part of the webserver in the future.

- TODO: display state of charge
- TODO: display battery hardware selected
- TODO: display emulated inverter communication protocol selected
- TODO: list all available ssids: scan WiFi Networks https://randomnerdtutorials.com/esp32-useful-wi-fi-functions-arduino/
- TODO: add option to add/change ssid and password and save, connect to the new ssid (Option: save ssid and password using Preferences.h library https://randomnerdtutorials.com/esp32-save-data-permanently-preferences/)
- TODO: display WiFi connection strength (https://randomnerdtutorials.com/esp32-useful-wi-fi-functions-arduino/)
- TODO: display CAN state (indicate if there is a communication error)
- TODO: display battery errors in battery diagnosis tab
- TODO: display inverter errors in battery diagnosis tab
- TODO: add functionality to turn WiFi AP off
- TODO: fix IP address on home network (https://randomnerdtutorials.com/esp32-static-fixed-ip-address-arduino-ide/)
- TODO: set hostname (https://randomnerdtutorials.com/esp32-set-custom-hostname-arduino/)

# References
The code for this webserver is based on code provided by Rui Santos at https://randomnerdtutorials.com.
177 changes: 177 additions & 0 deletions Software/src/devboard/webserver/webserver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#include "webserver.h"

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

// Measure OTA progress
unsigned long ota_progress_millis = 0;

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<title>Battery Emulator Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>
html {font-family: Arial; display: inline-block; text-align: center;}
h2 {font-size: 3.0rem;}
p {font-size: 3.0rem;}
body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
.switch {position: relative; display: inline-block; width: 120px; height: 68px}
.switch input {display: none}
.slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 6px}
.slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 3px}
input:checked+.slider {background-color: #b30000}
input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
</style>
</head>
<body>
<h2>Battery Emulator Web Server</h2>
%PLACEHOLDER%
</script>
</body>
</html>
)rawliteral";

String wifi_state;
bool wifi_connected;

// Wifi connect time declarations and definition
unsigned long wifi_connect_start_time;
unsigned long wifi_connect_current_time;
const long wifi_connect_timeout = 5000; // Timeout for WiFi connect in milliseconds

void init_webserver() {
// Configure WiFi
WiFi.mode(WIFI_AP_STA); // Simultaneous WiFi Access Point and WiFi STAtion
init_WiFi_AP();
init_WiFi_STA(ssid, password);

// Route for root / web page
server.on("/", HTTP_GET,
[](AsyncWebServerRequest* request) { request->send_P(200, "text/html", index_html, processor); });

// Send a GET request to <ESP_IP>/update
server.on("/debug", HTTP_GET,
[](AsyncWebServerRequest* request) { request->send(200, "text/plain", "Debug: all OK."); });

// Initialize ElegantOTA
init_ElegantOTA();

// Start server
server.begin();
}

void init_WiFi_AP() {
Serial.print("Creating Access Point: ");
Serial.println(ssidAP);
Serial.print("With password: ");
Serial.println(passwordAP);

WiFi.softAP(ssidAP, passwordAP);

IPAddress IP = WiFi.softAPIP();
Serial.println("Access Point created.");
Serial.print("IP address: ");
Serial.println(IP);
}

void init_WiFi_STA(const char* ssid, const char* password) {
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);

wifi_connect_start_time = millis();
wifi_connect_current_time = wifi_connect_start_time;
while ((wifi_connect_current_time - wifi_connect_start_time) <= wifi_connect_timeout &&
WiFi.status() != WL_CONNECTED) { // do this loop for up to 5000ms
// to break the loop when the connection is not established (wrong ssid or password).
delay(500);
Serial.print(".");
wifi_connect_current_time = millis();
}
if (WiFi.status() == WL_CONNECTED) { // WL_CONNECTED is assigned when connected to a WiFi network
wifi_connected = true;
wifi_state = "connected";
// Print local IP address and start web server
Serial.println("");
Serial.print("Connected to WiFi network: ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
} else {
wifi_connected = false;
wifi_state = "not connected";
Serial.print("Not connected to WiFi network: ");
Serial.println(ssid);
Serial.println("Please check WiFi network name and password, and if WiFi network is available.");
}
}

void init_ElegantOTA() {
ElegantOTA.begin(&server); // Start ElegantOTA
// ElegantOTA callbacks
ElegantOTA.onStart(onOTAStart);
ElegantOTA.onProgress(onOTAProgress);
ElegantOTA.onEnd(onOTAEnd);
}

String processor(const String& var) {
if (var == "PLACEHOLDER") {
String content = "";
// Display LED color
content += "<h4>LED color: ";
switch (LEDcolor) {
case GREEN:
content += "GREEN</h4>";
break;
case YELLOW:
content += "YELLOW</h4>";
break;
case BLUE:
content += "BLUE</h4>";
break;
case RED:
content += "RED</h4>";
break;
case TEST_ALL_COLORS:
content += "RAINBOW</h4>";
break;
default:
break;
}
// Display ssid of network connected to and, if connected to the WiFi, its own IP
content += "<h4>SSID: " + String(ssid) + "</h4>";
content += "<h4>status: " + wifi_state + "</h4>";
if (wifi_connected == true) {
content += "<h4>IP: " + WiFi.localIP().toString() + "</h4>";
}
return content;
}
return String();
}

void onOTAStart() {
// Log when OTA has started
Serial.println("OTA update started!");
// <Add your own code here>
}

void onOTAProgress(size_t current, size_t final) {
// Log every 1 second
if (millis() - ota_progress_millis > 1000) {
ota_progress_millis = millis();
Serial.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
}
}

void onOTAEnd(bool success) {
// Log when OTA has finished
if (success) {
Serial.println("OTA update finished successfully!");
} else {
Serial.println("There was an error during OTA update!");
}
// <Add your own code here>
}
92 changes: 92 additions & 0 deletions Software/src/devboard/webserver/webserver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#ifndef WEBSERVER_H
#define WEBSERVER_H

// Load Wi-Fi library
#include <WiFi.h>
#include "../../../USER_SETTINGS.h" // Needed for WiFi ssid and password
#include "../../lib/ayushsharma82-ElegantOTA/src/ElegantOTA.h"
#include "../../lib/me-no-dev-AsyncTCP/src/AsyncTCP.h"
#include "../../lib/me-no-dev-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
#include "../config.h" // Needed for LED defines

extern uint8_t LEDcolor; // Enum, 0-10
extern const char* ssid;
extern const char* password;
extern const char* ssidAP;
extern const char* passwordAP;

/**
* @brief Initialization function for the webserver.
*
* @param[in] void
*
* @return void
*/
void init_webserver();

/**
* @brief Initialization function that creates a WiFi Access Point.
*
* @param[in] void
*
* @return void
*/
void init_WiFi_AP();

/**
* @brief Initialization function that connects to an existing network.
*
* @param[in] ssid WiFi network name
* @param[in] password WiFi network password
*
* @return void
*/
void init_WiFi_STA(const char* ssid, const char* password);

/**
* @brief Initialization function for ElegantOTA.
*
* @param[in] void
*
* @return void
*/
void init_ElegantOTA();

/**
* @brief Replaces placeholder with content section in web page
*
* @param[in] var
*
* @return String
*/
String processor(const String& var);

/**
* @brief Executes on OTA start
*
* @param[in] void
*
* @return void
*/
void onOTAStart();

/**
* @brief Executes on OTA progress
*
* @param[in] current Current bytes
* @param[in] final Final bytes
*
* @return void
*/
void onOTAProgress(size_t current, size_t final);

/**
* @brief Executes on OTA end
*
* @param[in] void
*
* @return bool success: success = true, failed = false
*/
void onOTAEnd(bool success);

#endif
21 changes: 21 additions & 0 deletions Software/src/inverter/BYD-MODBUS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

void update_modbus_registers_byd() {
//Updata for ModbusRTU Server for BYD
verify_temperature_modbus();
handle_update_data_modbusp201_byd();
handle_update_data_modbusp301_byd();
}
Expand Down Expand Up @@ -106,3 +107,23 @@ void handle_update_data_modbusp301_byd() {
static uint16_t i = 300;
memcpy(&mbPV[i], battery_data, sizeof(battery_data));
}

void verify_temperature_modbus() {
if (LFP_Chemistry) {
return; // Skip the following section
}
// This section checks if the battery temperature is negative, and incase it falls between -9.0 and -20.0C degrees
// The Fronius Gen24 (and other Fronius inverters also affected), will stop charge/discharge if the battery gets colder than -10°C.
// This is due to the original battery pack (BYD HVM), is a lithium iron phosphate battery, that cannot be charged in cold weather.
// When using EV packs with NCM/LMO/NCA chemsitry, this is not a problem, since these chemistries are OK for outdoor cold use.
if (temperature_min > 32768) { // Signed value on negative side
if (temperature_min < 65445 && temperature_min > 65335) { // Between -9.0 and -20.0C degrees
temperature_min = 65445; //Cap value to -9.0C
}
}
if (temperature_max > 32768) { // Signed value on negative side
if (temperature_max < 65445 && temperature_max > 65335) { // Between -9.0 and -20.0C degrees
temperature_max = 65445; //Cap value to -9.0C
}
}
}
Loading

0 comments on commit 2ab1970

Please sign in to comment.