Skip to content

Commit

Permalink
Wireless stuff on its separate task, separate modules for general wif…
Browse files Browse the repository at this point in the history
…i+OTA, webserver and MQTT, some messing around with the settings
  • Loading branch information
Cabooman committed Jan 8, 2024
1 parent 543deff commit 600cda9
Show file tree
Hide file tree
Showing 11 changed files with 1,328 additions and 153 deletions.
24 changes: 10 additions & 14 deletions Software/Software.ino
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "USER_SETTINGS.h"
#include "src/battery/BATTERIES.h"
#include "src/devboard/config.h"
#include "src/devboard/webserver/webserver.h"
#include "src/devboard/wifi/wifi.h"
#include "src/inverter/INVERTERS.h"
#include "src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h"
#include "src/lib/eModbus-eModbus/Logging.h"
Expand All @@ -14,10 +16,6 @@
#include "src/lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"

#ifdef WEBSERVER
#include "src/devboard/webserver/webserver.h"
#endif

// Interval settings
int intervalUpdateValues = 4800; // Interval at which to update inverter values / Modbus registers
const int interval10 = 10; // Interval for 10ms tasks
Expand Down Expand Up @@ -113,10 +111,6 @@ bool inverterAllowsContactorClosing = true;
void setup() {
init_serial();

#ifdef WEBSERVER
init_webserver();
#endif

init_CAN();

init_LED();
Expand All @@ -130,16 +124,12 @@ void setup() {
inform_user_on_inverter();

inform_user_on_battery();

init_wireless();
}

// Perform main program functions
void loop() {

#ifdef WEBSERVER
// Over-the-air updates by ElegantOTA
ElegantOTA.loop();
#endif

// Input
receive_can(); // Receive CAN messages. Runs as fast as possible
#ifdef DUAL_CAN
Expand Down Expand Up @@ -170,6 +160,12 @@ void loop() {
#ifdef DUAL_CAN
send_can2();
#endif
if (webserver_ota_started()) {
Serial.println("OTA started, stopping CAN traffic");
ESP32Can.CANStop();
bms_status = 5; //Inform inverter that we are updating
LEDcolor = BLUE;
}
}

// Initialization functions
Expand Down
19 changes: 14 additions & 5 deletions Software/USER_SETTINGS.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
#include <Arduino.h>
#include "USER_SETTINGS.h"

#ifdef WEBSERVER
#define ENABLE_AP //Comment out this line to turn off the broadcasted AP
const char* ssid = "REPLACE_WITH_YOUR_SSID"; // maximum of 63 characters;
const char* password = "REPLACE_WITH_YOUR_PASSWORD"; // minimum of 8 characters;
#ifdef USE_WIFI

// MQTT
// For more detailed settings, see mqtt.h
#ifdef USE_MQTT
const char* mqtt_user = "REDACTED";
const char* mqtt_password = "REDACTED";
#endif // USE_MQTT

const char* ssid = "REDACTED"; // maximum of 63 characters;
const char* password = "REDACTED"; // 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

#endif
47 changes: 38 additions & 9 deletions Software/USER_SETTINGS.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
//#define CHADEMO_BATTERY
//#define IMIEV_CZERO_ION_BATTERY
//#define KIA_HYUNDAI_64_BATTERY
//#define NISSAN_LEAF_BATTERY
#define NISSAN_LEAF_BATTERY
//#define RENAULT_ZOE_BATTERY
//#define TESLA_MODEL_3_BATTERY
//#define TEST_FAKE_BATTERY

/* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki */
//#define BYD_CAN //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus
//#define BYD_MODBUS //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU
#define BYD_MODBUS //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU
//#define LUNA2000_MODBUS //Enable this line to emulate a "Luna2000 battery" over Modbus RTU
//#define PYLON_CAN //Enable this line to emulate a "Pylontech battery" over CAN bus
//#define SMA_CAN //Enable this line to emulate a "BYD Battery-Box H 8.9kWh, 7 mod" over CAN bus
Expand All @@ -38,12 +38,41 @@
//define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting

/* Other options */
#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs
//#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence
//#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM logic for contactors, which lower power consumption and heat generation
//#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. See USER_SETTINGS.cpp for the Wifi settings.
//#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs
//#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence
//#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM logic for contactors, which lower power consumption and heat generation
//#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)

// High level connectivity settings below. See USER_SETTINGS.cpp for detailed settings.

// Enabling any define below enables Wifi functionality. See USER_SETTINGS.cpp for the Wifi settings.
#define USE_WEBSERVER //Enable this line to run the webserver and enable OTA updates.
#define USE_MQTT //Enable this line to enable MQTT functionality. See USER_SETTINGS.cpp for the MQTT settings.

// Various Wifi specific defines, placed here to remove the need for messier code and issues with optimizations/linking.
//#define ENABLE_AP //Enable this line to run Wifi in AP mode (only broadcasting the LilyGO access point SSID).
#define ENABLE_STA //Enable this line to run Wifi in STA mode (connected to your home network).

#define MQTT_SUBSCRIPTIONS {"my/topic/abc", "my/other/topic"}
#define MQTT_SERVER "192.168.xxx.yyy"
#define MQTT_PORT 1883

// ---- Derived defines below ----

#if defined(USE_WEBSERVER) || defined(USE_MQTT)
#define USE_WIFI
#endif

#if defined(ENABLE_AP) && defined(ENABLE_STA)
#define WIFI_MODE WIFI_AP_STA
#elif defined(ENABLE_AP)
#define WIFI_MODE WIFI_AP
#elif defined(ENABLE_STA)
#define WIFI_MODE WIFI_STA
#else
#define WIFI_MODE WIFI_OFF // Just in case
#endif

#endif
97 changes: 97 additions & 0 deletions Software/src/devboard/mqtt/mqtt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#include "mqtt.h"
#include <Arduino.h>
#include <WiFi.h>
#include <freertos/FreeRTOS.h>
#include "../../../USER_SETTINGS.h"
#include "../../lib/knolleary-pubsubclient/PubSubClient.h"
#include "../wifi/wifi.h"

const char* mqtt_subscriptions[] = MQTT_SUBSCRIPTIONS;
const size_t mqtt_nof_subscriptions = sizeof(mqtt_subscriptions) / sizeof(mqtt_subscriptions[0]);

WiFiClient espClient;
PubSubClient client(espClient);
char msg[MSG_BUFFER_SIZE];
int value = 0;
static unsigned long previousMillisUpdateVal;

#ifdef USE_MQTT
/** Publish global values and call callbacks for specific modules */
static void publish_values(void) {

snprintf(msg, sizeof(msg),
"{\n"
" \"SOC\": %.3f,\n"
" \"StateOfHealth\": %.3f,\n"
" \"temperature_min\": %.3f,\n"
" \"temperature_max\": %.3f,\n"
" \"cell_max_voltage\": %d,\n"
" \"cell_min_voltage\": %d\n"
"}\n",
((float)SOC) / 100.0, ((float)StateOfHealth) / 100.0, ((float)((int16_t)temperature_min)) / 10.0,
((float)((int16_t)temperature_max)) / 10.0, cell_max_voltage, cell_min_voltage);
client.publish("battery/info", msg);
// Serial.println(msg); // Uncomment to print the payload on serial
}

/* This is called whenever a subscribed topic changes (hopefully) */
static void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (unsigned int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
}

/* If we lose the connection, get it back and re-sub */
static void reconnect() {
// attempt one reconnection
Serial.print("Attempting MQTT connection... ");
// Create a random client ID
String clientId = "LilyGoClient-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
Serial.println("connected");

for (int i = 0; i < mqtt_nof_subscriptions; i++) {
client.subscribe(mqtt_subscriptions[i]);
Serial.print("Subscribed to: ");
Serial.println(mqtt_subscriptions[i]);
}
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
}
}
#endif //

void init_mqtt(void) {
client.setServer(MQTT_SERVER, MQTT_PORT);
client.setCallback(callback);

previousMillisUpdateVal = millis();
reconnect();
Serial.println("MQTT initialized");
}

void mqtt_loop(void) {
if (client.connected()) {
client.loop();
if (millis() - previousMillisUpdateVal >= 5000) // Every 5s
{
previousMillisUpdateVal = millis();
publish_values(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus.
}
} else {
if (millis() - previousMillisUpdateVal >= 5000) // Every 5s
{
previousMillisUpdateVal = millis();
reconnect(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus.
}
}
}
55 changes: 55 additions & 0 deletions Software/src/devboard/mqtt/mqtt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* MQTT add-on for the battery emulator
*
* Usage:
*
* Subscription - Add topics to MQTT_SUBSCRIPTIONS in USER_SETTINGS.h and handle the messages in mqtt.cpp:callback()
*
* Publishing - See example in mqtt.cpp:publish_values() for constructing the payload
*
* Home assistant - See below for an example, and the official documentation is quite good (https://www.home-assistant.io/integrations/sensor.mqtt/)
* in configuration.yaml:
* mqtt: !include mqtt.yaml
*
* in mqtt.yaml:
* sensor:
* - name: "Cell max"
* state_topic: "battery/info"
* unit_of_measurement: "mV"
* value_template: "{{ value_json.cell_max_voltage | int }}"
* - name: "Cell min"
* state_topic: "battery/info"
* unit_of_measurement: "mV"
* value_template: "{{ value_json.cell_min_voltage | int }}"
* - name: "Temperature max"
* state_topic: "battery/info"
* unit_of_measurement: "C"
* value_template: "{{ value_json.temperature_max | float }}"
* - name: "Temperature min"
* state_topic: "battery/info"
* unit_of_measurement: "C"
* value_template: "{{ value_json.temperature_min | float }}"
*/

#ifndef __MQTT_H__
#define __MQTT_H__

#include <Arduino.h>
#include "../../../USER_SETTINGS.h"

#define MSG_BUFFER_SIZE (256)

extern uint16_t SOC;
extern uint16_t StateOfHealth;
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385)
extern uint16_t cell_max_voltage; //mV, 0-4350
extern uint16_t cell_min_voltage; //mV, 0-4350

extern const char* mqtt_user;
extern const char* mqtt_password;

void init_mqtt(void);
void mqtt_loop(void);

#endif
Loading

0 comments on commit 600cda9

Please sign in to comment.