diff --git a/.github/workflows/compile-all-batteries.yml b/.github/workflows/compile-all-batteries.yml index 2ae5a558..31f506a7 100644 --- a/.github/workflows/compile-all-batteries.yml +++ b/.github/workflows/compile-all-batteries.yml @@ -38,6 +38,7 @@ jobs: - IMIEV_CZERO_ION_BATTERY - KIA_HYUNDAI_64_BATTERY - NISSAN_LEAF_BATTERY + - PYLON_BATTERY - RENAULT_KANGOO_BATTERY - RENAULT_ZOE_BATTERY - TESLA_MODEL_3_BATTERY diff --git a/.github/workflows/compile-all-combinations.yml b/.github/workflows/compile-all-combinations.yml index dfa15484..492bc132 100644 --- a/.github/workflows/compile-all-combinations.yml +++ b/.github/workflows/compile-all-combinations.yml @@ -41,6 +41,7 @@ jobs: - IMIEV_CZERO_ION_BATTERY - KIA_HYUNDAI_64_BATTERY - NISSAN_LEAF_BATTERY + - PYLON_BATTERY - RENAULT_KANGOO_BATTERY - RENAULT_ZOE_BATTERY - TESLA_MODEL_3_BATTERY diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 5537a05c..8d3161c4 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -14,6 +14,7 @@ //#define KIA_HYUNDAI_64_BATTERY //#define KIA_E_GMP_BATTERY //#define NISSAN_LEAF_BATTERY +//#define PYLON_BATTERY //#define RENAULT_KANGOO_BATTERY //#define RENAULT_ZOE_BATTERY //#define SANTA_FE_PHEV_BATTERY diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index ef35110c..be9cd616 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -4,55 +4,59 @@ #include "../../USER_SETTINGS.h" #ifdef BMW_I3_BATTERY -#include "BMW-I3-BATTERY.h" //See this file for more i3 battery settings +#include "BMW-I3-BATTERY.h" #endif #ifdef CHADEMO_BATTERY -#include "CHADEMO-BATTERY.h" //See this file for more Chademo settings +#include "CHADEMO-BATTERY.h" #endif #ifdef IMIEV_CZERO_ION_BATTERY -#include "IMIEV-CZERO-ION-BATTERY.h" //See this file for more triplet battery settings +#include "IMIEV-CZERO-ION-BATTERY.h" #endif #ifdef KIA_E_GMP_BATTERY -#include "KIA-E-GMP-BATTERY.h" //See this file for more GMP battery settings +#include "KIA-E-GMP-BATTERY.h" #endif #ifdef KIA_HYUNDAI_64_BATTERY -#include "KIA-HYUNDAI-64-BATTERY.h" //See this file for more 64kWh battery settings +#include "KIA-HYUNDAI-64-BATTERY.h" #endif #ifdef NISSAN_LEAF_BATTERY -#include "NISSAN-LEAF-BATTERY.h" //See this file for more LEAF battery settings +#include "NISSAN-LEAF-BATTERY.h" +#endif + +#ifdef PYLON_BATTERY +#include "PYLON-BATTERY.h" #endif #ifdef RENAULT_KANGOO_BATTERY -#include "RENAULT-KANGOO-BATTERY.h" //See this file for more Kangoo battery settings +#include "RENAULT-KANGOO-BATTERY.h" #endif #ifdef RENAULT_ZOE_BATTERY -#include "RENAULT-ZOE-BATTERY.h" //See this file for more Zoe battery settings +#include "RENAULT-ZOE-BATTERY.h" #endif #ifdef SANTA_FE_PHEV_BATTERY -#include "SANTA-FE-PHEV-BATTERY.h" //See this file for more Santa Fe PHEV battery settings +#include "SANTA-FE-PHEV-BATTERY.h" #endif #ifdef TESLA_MODEL_3_BATTERY -#include "TESLA-MODEL-3-BATTERY.h" //See this file for more Tesla battery settings +#include "TESLA-MODEL-3-BATTERY.h" #endif #ifdef TEST_FAKE_BATTERY -#include "TEST-FAKE-BATTERY.h" //See this file for more Fake battery settings +#include "TEST-FAKE-BATTERY.h" #endif #ifdef VOLVO_SPA_BATTERY -#include "VOLVO-SPA-BATTERY.h" //See this file for more XC40 Recharge/Polestar2 settings +#include "VOLVO-SPA-BATTERY.h" #endif #ifdef SERIAL_LINK_RECEIVER -#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h" //See this file for more Serial-battery settings +#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h" #endif #ifdef SERIAL_LINK_RECEIVER // The serial thing does its thing diff --git a/Software/src/battery/PYLON-BATTERY.cpp b/Software/src/battery/PYLON-BATTERY.cpp new file mode 100644 index 00000000..f9c3db33 --- /dev/null +++ b/Software/src/battery/PYLON-BATTERY.cpp @@ -0,0 +1,212 @@ +#include "../include.h" +#ifdef PYLON_BATTERY +#include "../datalayer/datalayer.h" +#include "../devboard/utils/events.h" +#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h" +#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" +#include "PYLON-BATTERY.h" + +/* TODO: +- Test the protocol with a battery +- Check if values are scaled correctly in the Webserver +- Check if CAN sending towards the battery works OK. Tweak values from log file if needed +- If all looks good, try adding an inverter into the mix! +*/ + +/* Do not change code below unless you are sure what you are doing */ +static unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent +static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive + +//Actual content messages +CAN_frame_t PYLON_3010 = {.FIR = {.B = + { + .DLC = 8, + .FF = CAN_frame_ext, + }}, + .MsgID = 0x3010, + .data = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame_t PYLON_8200 = {.FIR = {.B = + { + .DLC = 8, + .FF = CAN_frame_ext, + }}, + .MsgID = 0x8200, + .data = {0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame_t PYLON_8210 = {.FIR = {.B = + { + .DLC = 8, + .FF = CAN_frame_ext, + }}, + .MsgID = 0x8210, + .data = {0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame_t PYLON_4200 = {.FIR = {.B = + { + .DLC = 8, + .FF = CAN_frame_ext, + }}, + .MsgID = 0x4200, + .data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + +static int16_t celltemperature_max_dC = 0; +static int16_t celltemperature_min_dC = 0; +static int16_t current_dA = 0; +static uint16_t voltage_dV = 0; +static uint16_t cellvoltage_max_mV = 0; +static uint16_t cellvoltage_min_mV = 0; +static uint16_t charge_cutoff_voltage = 0; +static uint16_t discharge_cutoff_voltage = 0; +static int16_t max_charge_current = 0; +static int16_t max_discharge_current = 0; +static uint8_t ensemble_info_ack = 0; +static uint8_t battery_module_quantity = 0; +static uint8_t battery_modules_in_series = 0; +static uint8_t cell_quantity_in_module = 0; +static uint8_t voltage_level = 0; +static uint8_t ah_number = 0; +static uint8_t SOC = 0; +static uint8_t SOH = 0; +static uint8_t charge_forbidden = 0; +static uint8_t discharge_forbidden = 0; + +void update_values_battery() { + + datalayer.battery.status.real_soc = (SOC * 100); //increase SOC range from 0-100 -> 100.00 + + datalayer.battery.status.soh_pptt = (SOH * 100); //Increase decimals from 100% -> 100.00% + + datalayer.battery.status.voltage_dV = voltage_dV; //value is *10 (3700 = 370.0) + + datalayer.battery.status.current_dA = current_dA; //value is *10 (150 = 15.0) , invert the sign + + datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt + ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); + + datalayer.battery.status.max_charge_power_W = (max_charge_current * (voltage_dV / 10)); + + datalayer.battery.status.max_discharge_power_W = (max_discharge_current * (voltage_dV / 10)); + + datalayer.battery.status.cell_max_voltage_mV = cellvoltage_max_mV; + + datalayer.battery.status.cell_min_voltage_mV = cellvoltage_min_mV; + + datalayer.battery.status.temperature_min_dC = celltemperature_min_dC; + + datalayer.battery.status.temperature_max_dC = celltemperature_max_dC; + + datalayer.battery.info.max_design_voltage_dV = charge_cutoff_voltage; + + datalayer.battery.info.min_design_voltage_dV = discharge_cutoff_voltage; + + /* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/ + if (!CANstillAlive) { + set_event(EVENT_CAN_RX_FAILURE, 0); + } else { + CANstillAlive--; + clear_event(EVENT_CAN_RX_FAILURE); + } +} + +void receive_can_battery(CAN_frame_t rx_frame) { + CANstillAlive = 12; + switch (rx_frame.MsgID) { + case 0x7310: + case 0x7311: + ensemble_info_ack = true; + // This message contains software/hardware version info. No interest to us + break; + case 0x7320: + case 0x7321: + ensemble_info_ack = true; + battery_module_quantity = rx_frame.data.u8[0]; + battery_modules_in_series = rx_frame.data.u8[2]; + cell_quantity_in_module = rx_frame.data.u8[3]; + voltage_level = rx_frame.data.u8[4]; + ah_number = rx_frame.data.u8[6]; + break; + case 0x4210: + case 0x4211: + voltage_dV = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); + current_dA = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); + SOC = rx_frame.data.u8[6]; + SOH = rx_frame.data.u8[7]; + break; + case 0x4220: + case 0x4221: + charge_cutoff_voltage = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); + discharge_cutoff_voltage = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); + max_charge_current = (((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) * 0.1) - 3000; + max_discharge_current = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) * 0.1) - 3000; + break; + case 0x4230: + case 0x4231: + cellvoltage_max_mV = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); + cellvoltage_min_mV = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); + break; + case 0x4240: + case 0x4241: + celltemperature_max_dC = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) - 1000; + celltemperature_min_dC = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 1000; + break; + case 0x4250: + case 0x4251: + //Byte0 Basic Status + //Byte1-2 Cycle Period + //Byte3 Error + //Byte4-5 Alarm + //Byte6-7 Protection + break; + case 0x4260: + case 0x4261: + //Byte0-1 Module Max Voltage + //Byte2-3 Module Min Voltage + //Byte4-5 Module Max. Voltage Number + //Byte6-7 Module Min. Voltage Number + break; + case 0x4270: + case 0x4271: + //Byte0-1 Module Max. Temperature + //Byte2-3 Module Min. Temperature + //Byte4-5 Module Max. Temperature Number + //Byte6-7 Module Min. Temperature Number + break; + case 0x4280: + case 0x4281: + charge_forbidden = rx_frame.data.u8[0]; + discharge_forbidden = rx_frame.data.u8[1]; + break; + case 0x4290: + case 0x4291: + break; + default: + break; + } +} + +void send_can_battery() { + unsigned long currentMillis = millis(); + // Send 1s CAN Message + if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { + + previousMillis1000 = currentMillis; + + ESP32Can.CANWriteFrame(&PYLON_3010); // Heartbeat + ESP32Can.CANWriteFrame(&PYLON_4200); // Ensemble OR System equipment info, depends on frame0 + ESP32Can.CANWriteFrame(&PYLON_8200); // Control device quit sleep status + ESP32Can.CANWriteFrame(&PYLON_8210); // Charge command + + if (ensemble_info_ack) { + PYLON_4200.data.u8[0] = 0x00; //Request system equipment info + } + } +} + +void setup_battery(void) { // Performs one time setup at startup +#ifdef DEBUG_VIA_USB + Serial.println("Pylon battery selected"); +#endif + + datalayer.battery.info.max_design_voltage_dV = 4040; // 404.0V, charging over this is not possible + datalayer.battery.info.min_design_voltage_dV = 3100; // 310.0V, under this, discharging further is disabled +} + +#endif diff --git a/Software/src/battery/PYLON-BATTERY.h b/Software/src/battery/PYLON-BATTERY.h new file mode 100644 index 00000000..0233c649 --- /dev/null +++ b/Software/src/battery/PYLON-BATTERY.h @@ -0,0 +1,11 @@ +#ifndef PYLON_BATTERY_H +#define PYLON_BATTERY_H +#include +#include "../include.h" +#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" + +#define BATTERY_SELECTED + +void setup_battery(void); + +#endif