mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-06 03:50:13 +02:00
Centralizations and mods for it
This commit is contained in:
parent
838c2f0dcb
commit
13bfdcc33b
6 changed files with 107 additions and 73 deletions
|
@ -71,6 +71,7 @@ uint16_t stat_batt_power = 0; // Power going in/out of battery
|
||||||
uint16_t cell_max_voltage = 3700; // Stores the highest cell voltage value in the system
|
uint16_t cell_max_voltage = 3700; // Stores the highest cell voltage value in the system
|
||||||
uint16_t cell_min_voltage = 3700; // Stores the minimum cell voltage value in the system
|
uint16_t cell_min_voltage = 3700; // Stores the minimum cell voltage value in the system
|
||||||
uint16_t cellvoltages[120]; // Stores all cell voltages
|
uint16_t cellvoltages[120]; // Stores all cell voltages
|
||||||
|
uint8_t nof_cellvoltages = 0; // Total number of cell voltages, set by each battery.
|
||||||
bool LFP_Chemistry = false;
|
bool LFP_Chemistry = false;
|
||||||
|
|
||||||
// Common charger parameters
|
// Common charger parameters
|
||||||
|
@ -141,6 +142,8 @@ void setup() {
|
||||||
inform_user_on_inverter();
|
inform_user_on_inverter();
|
||||||
|
|
||||||
inform_user_on_battery();
|
inform_user_on_battery();
|
||||||
|
|
||||||
|
init_battery();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform main program functions
|
// Perform main program functions
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef BATTERIES_H
|
#ifndef BATTERIES_H
|
||||||
#define BATTERIES_H
|
#define BATTERIES_H
|
||||||
|
|
||||||
|
#include "../../USER_SETTINGS.h"
|
||||||
|
|
||||||
#ifdef BMW_I3_BATTERY
|
#ifdef BMW_I3_BATTERY
|
||||||
#include "BMW-I3-BATTERY.h" //See this file for more i3 battery settings
|
#include "BMW-I3-BATTERY.h" //See this file for more i3 battery settings
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,10 +20,6 @@ static uint8_t mprun10r = 0; //counter 0-20 for 0x1F2 message
|
||||||
static uint8_t mprun10 = 0; //counter 0-3
|
static uint8_t mprun10 = 0; //counter 0-3
|
||||||
static uint8_t mprun100 = 0; //counter 0-3
|
static uint8_t mprun100 = 0; //counter 0-3
|
||||||
|
|
||||||
#ifdef MQTT
|
|
||||||
bool mqtt_first_transmission = true;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CAN_frame_t LEAF_1F2 = {.FIR = {.B =
|
CAN_frame_t LEAF_1F2 = {.FIR = {.B =
|
||||||
{
|
{
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
|
@ -162,10 +158,6 @@ static uint16_t temp_raw_min = 0;
|
||||||
static int16_t temp_polled_max = 0;
|
static int16_t temp_polled_max = 0;
|
||||||
static int16_t temp_polled_min = 0;
|
static int16_t temp_polled_min = 0;
|
||||||
|
|
||||||
#ifdef MQTT
|
|
||||||
void publish_data(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void print_with_units(char* header, int value, char* units) {
|
void print_with_units(char* header, int value, char* units) {
|
||||||
Serial.print(header);
|
Serial.print(header);
|
||||||
Serial.print(value);
|
Serial.print(value);
|
||||||
|
@ -442,9 +434,6 @@ void update_values_leaf_battery() { /* This function maps all the values fetched
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef MQTT
|
|
||||||
publish_data();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_can_leaf_battery(CAN_frame_t rx_frame) {
|
void receive_can_leaf_battery(CAN_frame_t rx_frame) {
|
||||||
|
@ -951,69 +940,19 @@ uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrib
|
||||||
return static_cast<uint16_t>(1094 + (309 - temperature) * 2.5714285714285715);
|
return static_cast<uint16_t>(1094 + (309 - temperature) * 2.5714285714285715);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void init_battery(void) {
|
||||||
|
nof_cellvoltages = 96;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MQTT
|
#ifdef MQTT
|
||||||
void publish_data(void) {
|
void publish_battery_specifics(void) {
|
||||||
|
static bool first_execution = true;
|
||||||
// At startup, re-post the discovery message for home assistant
|
if(first_execution == true) {
|
||||||
if (mqtt_first_transmission == true) {
|
first_execution = false;
|
||||||
mqtt_first_transmission = false;
|
// Discovery stuff
|
||||||
|
|
||||||
// Base topic for any cell voltage "sensor"
|
|
||||||
// TODO: make the discovery topic configurable centrally... but this is fine
|
|
||||||
String topic = "homeassistant/sensor/battery-emulator/cell_voltage";
|
|
||||||
for (int i = 0; i < 96; i++) {
|
|
||||||
// Build JSON message with device configuration for each cell voltage
|
|
||||||
// Probably shouldn't be BatteryEmulator here, instead "LeafBattery"
|
|
||||||
// or similar but hey, it works.
|
|
||||||
// mqtt_msg is a global buffer, should be fine since we run too much
|
|
||||||
// in a single thread :)
|
|
||||||
snprintf(mqtt_msg, sizeof(mqtt_msg),
|
|
||||||
"{"
|
|
||||||
"\"device\": {"
|
|
||||||
"\"identifiers\": ["
|
|
||||||
"\"battery-emulator\""
|
|
||||||
"],"
|
|
||||||
"\"manufacturer\": \"DalaTech\","
|
|
||||||
"\"model\": \"BatteryEmulator\","
|
|
||||||
"\"name\": \"BatteryEmulator\""
|
|
||||||
"},"
|
|
||||||
"\"device_class\": \"voltage\","
|
|
||||||
"\"enabled_by_default\": true,"
|
|
||||||
"\"object_id\": \"sensor_battery_voltage_cell%d\","
|
|
||||||
"\"origin\": {"
|
|
||||||
"\"name\": \"BatteryEmulator\","
|
|
||||||
"\"sw\": \"4.4.0-mqtt\","
|
|
||||||
"\"url\": \"https://github.com/dalathegreat/Battery-Emulator\""
|
|
||||||
"},"
|
|
||||||
"\"state_class\": \"measurement\","
|
|
||||||
"\"name\": \"Battery Cell Voltage %d\","
|
|
||||||
"\"state_topic\": \"battery/spec_data\","
|
|
||||||
"\"unique_id\": \"battery-emulator_battery_voltage_cell%d\","
|
|
||||||
"\"unit_of_measurement\": \"V\","
|
|
||||||
"\"value_template\": \"{{ value_json.cell_voltages[%d] }}\""
|
|
||||||
"}",
|
|
||||||
i + 1, i + 1, i + 1, i);
|
|
||||||
// End each discovery topic with cell number and '/config'
|
|
||||||
String cell_topic = topic + String(i + 1) + "/config";
|
|
||||||
mqtt_publish_retain(cell_topic.c_str());
|
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
|
// Publishing stuff
|
||||||
// Every 5-ish seconds, build the JSON payload for the state topic. This requires
|
|
||||||
// some annoying formatting due to C++ not having nice Python-like string formatting.
|
|
||||||
// msg_length is a cumulative variable to track start position (param 1) and for
|
|
||||||
// modifying the maxiumum amount of characters to write (param 2). The third parameter
|
|
||||||
// is the string content
|
|
||||||
size_t msg_length = snprintf(mqtt_msg, sizeof(mqtt_msg), "{\n\"cell_voltages\":[");
|
|
||||||
for (size_t i = 0; i < 97; ++i) {
|
|
||||||
msg_length +=
|
|
||||||
snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "%s%d", (i == 0) ? "" : ", ", cell_voltages[i]);
|
|
||||||
}
|
|
||||||
snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "]\n}\n");
|
|
||||||
|
|
||||||
// Publish and print error if not OK
|
|
||||||
if (mqtt_publish_retain("battery/spec_data") == false) {
|
|
||||||
Serial.println("Nissan MQTT msg could not be sent");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -36,4 +36,7 @@ uint16_t convert2unsignedint16(int16_t signed_value);
|
||||||
uint16_t Temp_fromRAW_to_F(uint16_t temperature);
|
uint16_t Temp_fromRAW_to_F(uint16_t temperature);
|
||||||
bool is_message_corrupt(CAN_frame_t rx_frame);
|
bool is_message_corrupt(CAN_frame_t rx_frame);
|
||||||
|
|
||||||
|
void publish_battery_specifics(void);
|
||||||
|
void init_battery(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include "../../../USER_SETTINGS.h"
|
#include "../../../USER_SETTINGS.h"
|
||||||
|
#include "../../battery/BATTERIES.h"
|
||||||
#include "../../lib/knolleary-pubsubclient/PubSubClient.h"
|
#include "../../lib/knolleary-pubsubclient/PubSubClient.h"
|
||||||
#include "../utils/timer.h"
|
#include "../utils/timer.h"
|
||||||
|
|
||||||
|
@ -16,9 +17,17 @@ int value = 0;
|
||||||
static unsigned long previousMillisUpdateVal;
|
static unsigned long previousMillisUpdateVal;
|
||||||
MyTimer publish_global_timer(5000);
|
MyTimer publish_global_timer(5000);
|
||||||
|
|
||||||
|
static void publish_common_info(void);
|
||||||
|
static void publish_cell_voltages(void);
|
||||||
|
|
||||||
/** Publish global values and call callbacks for specific modules */
|
/** Publish global values and call callbacks for specific modules */
|
||||||
static void publish_values(void) {
|
static void publish_values(void) {
|
||||||
|
publish_common_info();
|
||||||
|
publish_cell_voltages();
|
||||||
|
publish_battery_specifics();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void publish_common_info(void) {
|
||||||
snprintf(mqtt_msg, sizeof(mqtt_msg),
|
snprintf(mqtt_msg, sizeof(mqtt_msg),
|
||||||
"{\n"
|
"{\n"
|
||||||
" \"SOC\": %.3f,\n"
|
" \"SOC\": %.3f,\n"
|
||||||
|
@ -31,7 +40,83 @@ static void publish_values(void) {
|
||||||
((float)SOC) / 100.0, ((float)StateOfHealth) / 100.0, ((float)((int16_t)temperature_min)) / 10.0,
|
((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);
|
((float)((int16_t)temperature_max)) / 10.0, cell_max_voltage, cell_min_voltage);
|
||||||
bool result = client.publish("battery/info", mqtt_msg, true);
|
bool result = client.publish("battery/info", mqtt_msg, true);
|
||||||
Serial.println(mqtt_msg); // Uncomment to print the payload on serial
|
//Serial.println(mqtt_msg); // Uncomment to print the payload on serial
|
||||||
|
}
|
||||||
|
|
||||||
|
static void publish_cell_voltages(void) {
|
||||||
|
static bool mqtt_first_transmission = true;
|
||||||
|
|
||||||
|
// If the cell voltage number isn't initialized...
|
||||||
|
if (nof_cellvoltages == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// At startup, re-post the discovery message for home assistant
|
||||||
|
if (mqtt_first_transmission == true) {
|
||||||
|
mqtt_first_transmission = false;
|
||||||
|
|
||||||
|
// Base topic for any cell voltage "sensor"
|
||||||
|
String topic = "homeassistant/sensor/battery-emulator/cell_voltage";
|
||||||
|
for (int i = 0; i < nof_cellvoltages; i++) {
|
||||||
|
// Build JSON message with device configuration for each cell voltage
|
||||||
|
// Probably shouldn't be BatteryEmulator here, instead "LeafBattery"
|
||||||
|
// or similar but hey, it works.
|
||||||
|
// mqtt_msg is a global buffer, should be fine since we run too much
|
||||||
|
// in a single thread :)
|
||||||
|
snprintf(mqtt_msg, sizeof(mqtt_msg),
|
||||||
|
"{"
|
||||||
|
"\"device\": {"
|
||||||
|
"\"identifiers\": ["
|
||||||
|
"\"battery-emulator\""
|
||||||
|
"],"
|
||||||
|
"\"manufacturer\": \"DalaTech\","
|
||||||
|
"\"model\": \"BatteryEmulator\","
|
||||||
|
"\"name\": \"BatteryEmulator\""
|
||||||
|
"},"
|
||||||
|
"\"device_class\": \"voltage\","
|
||||||
|
"\"enabled_by_default\": true,"
|
||||||
|
"\"object_id\": \"sensor_battery_voltage_cell%d\","
|
||||||
|
"\"origin\": {"
|
||||||
|
"\"name\": \"BatteryEmulator\","
|
||||||
|
"\"sw\": \"4.4.0-mqtt\","
|
||||||
|
"\"url\": \"https://github.com/dalathegreat/Battery-Emulator\""
|
||||||
|
"},"
|
||||||
|
"\"state_class\": \"measurement\","
|
||||||
|
"\"name\": \"Battery Cell Voltage %d\","
|
||||||
|
"\"state_topic\": \"battery/spec_data\","
|
||||||
|
"\"unique_id\": \"battery-emulator_battery_voltage_cell%d\","
|
||||||
|
"\"unit_of_measurement\": \"V\","
|
||||||
|
"\"value_template\": \"{{ value_json.cell_voltages[%d] }}\""
|
||||||
|
"}",
|
||||||
|
i + 1, i + 1, i + 1, i);
|
||||||
|
// End each discovery topic with cell number and '/config'
|
||||||
|
String cell_topic = topic + String(i + 1) + "/config";
|
||||||
|
mqtt_publish_retain(cell_topic.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Every 5-ish seconds, build the JSON payload for the state topic. This requires
|
||||||
|
// some annoying formatting due to C++ not having nice Python-like string formatting.
|
||||||
|
// msg_length is a cumulative variable to track start position (param 1) and for
|
||||||
|
// modifying the maxiumum amount of characters to write (param 2). The third parameter
|
||||||
|
// is the string content
|
||||||
|
|
||||||
|
// If cell voltages haven't been populated...
|
||||||
|
if (cellvoltages[0] == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t msg_length = snprintf(mqtt_msg, sizeof(mqtt_msg), "{\n\"cell_voltages\":[");
|
||||||
|
for (size_t i = 0; i < nof_cellvoltages; ++i) {
|
||||||
|
msg_length +=
|
||||||
|
snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "%s%d", (i == 0) ? "" : ", ", cellvoltages[i]);
|
||||||
|
}
|
||||||
|
snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "]\n}\n");
|
||||||
|
|
||||||
|
// Publish and print error if not OK
|
||||||
|
if (mqtt_publish_retain("battery/spec_data") == false) {
|
||||||
|
Serial.println("Cell voltage MQTT msg could not be sent");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is called whenever a subscribed topic changes (hopefully) */
|
/* This is called whenever a subscribed topic changes (hopefully) */
|
||||||
|
|
|
@ -45,6 +45,8 @@ extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 funct
|
||||||
extern uint16_t temperature_max; //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_max_voltage; //mV, 0-4350
|
||||||
extern uint16_t cell_min_voltage; //mV, 0-4350
|
extern uint16_t cell_min_voltage; //mV, 0-4350
|
||||||
|
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell
|
||||||
|
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery.
|
||||||
|
|
||||||
extern const char* mqtt_user;
|
extern const char* mqtt_user;
|
||||||
extern const char* mqtt_password;
|
extern const char* mqtt_password;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue