From 873937133cb9576bbddfd4a802c9e71577018bdc Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 28 Jul 2023 00:16:56 +0300 Subject: [PATCH] Add initial imiev support --- Software/BATTERIES.h | 4 + Software/IMIEV-CZERO-ION-BATTERY.cpp | 154 +++++++++++++++++++++++++++ Software/IMIEV-CZERO-ION-BATTERY.h | 40 +++++++ Software/Software.ino | 20 +++- 4 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 Software/IMIEV-CZERO-ION-BATTERY.cpp create mode 100644 Software/IMIEV-CZERO-ION-BATTERY.h diff --git a/Software/BATTERIES.h b/Software/BATTERIES.h index ae9d4ef3..96e9b111 100644 --- a/Software/BATTERIES.h +++ b/Software/BATTERIES.h @@ -13,6 +13,10 @@ #include "RENAULT-ZOE-BATTERY.h" //See this file for more Zoe battery settings #endif +#ifdef IMIEV_ION_CZERO_BATTERY + #include "IMIEV-CZERO-ION-BATTERY.h" //See this file for more triplet battery settings +#endif + #ifdef CHADEMO #include "CHADEMO-BATTERY.h" //See this file for more Chademo settings #endif diff --git a/Software/IMIEV-CZERO-ION-BATTERY.cpp b/Software/IMIEV-CZERO-ION-BATTERY.cpp new file mode 100644 index 00000000..ce50f388 --- /dev/null +++ b/Software/IMIEV-CZERO-ION-BATTERY.cpp @@ -0,0 +1,154 @@ +#include "IMIEV-CZERO-ION-BATTERY.h" +#include "ESP32CAN.h" +#include "CAN_config.h" + +//Code still very WIP +//TODO: Add CMU warning incase we detect a direct connection to the CMUs. It wont be safe to use the battery in this mode +//Map the missing values +//Check scaling of all values +//Generate messages towards BMU to keep it happy? + +/* Do not change code below unless you are sure what you are doing */ +#define BMU_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to inverter +#define BMU_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to inverter +static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive +static uint8_t errorCode = 0; //stores if we have an error code active from battery control logic +static uint8_t BMU_Detected = 0; +static uint8_t CMU_Detected = 0; + +static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was sent +static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent +static const int interval10 = 10; // interval (ms) at which send CAN Messages +static const int interval100 = 100; // interval (ms) at which send CAN Messages + +static int pid_index = 0; +static int cmu_id = 0; +static int voltage_index = 0; +static int temp_index = 0; +static int BMU_SOC = 0; +static double temp1 = 0; +static double temp2 = 0; +static double temp3 = 0; +static double voltage1 = 0; +static double voltage2 = 0; +static double BMU_Current = 0; +static double BMU_PackVoltage = 0; +static double BMU_Power = 0; +static uint16_t cell_voltages[89]; //array with all the cellvoltages //Todo, what is max array size? 80/88 cells? +static uint16_t cell_temperatures[89]; //array with all the celltemperatures //Todo, what is max array size? 80/88cells? + + +void update_values_imiev_battery() +{ //This function maps all the values fetched via CAN to the correct parameters used for modbus + bms_status = ACTIVE; //Startout in active mode + + SOC = BMU_SOC; //Todo, scaling? + + battery_voltage = (BMU_PackVoltage*10); //Todo, scaling? + + battery_current = (BMU_Current*10); //Todo, scaling? + + capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value + + remaining_capacity_Wh = (SOC/100)*capacity_Wh; + + max_target_charge_power; //TODO, map + + max_target_discharge_power; //TODO, map + + stat_batt_power = BMU_Power; //TODO, Scaling? + + temperature_min; //TODO, map + + temperature_max; //TODO, map + + /* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/ + if(!CANstillAlive) + { + bms_status = FAULT; + Serial.println("No CAN communication detected for 60s. Shutting down battery control."); + } + else + { + CANstillAlive--; + } + + if(printValues) + { //values heading towards the modbus registers + Serial.print("BMU SOC: "); + Serial.println(BMU_SOC); + Serial.print("BMU Current: "); + Serial.println(BMU_Current); + Serial.print("BMU Battery Voltage: "); + Serial.println(BMU_PackVoltage); + } +} + +void receive_can_imiev_battery(CAN_frame_t rx_frame) +{ + switch (rx_frame.MsgID) + { + CANstillAlive = 12; //Todo, move this inside a known message ID to prevent CAN inverter from keeping battery alive detection going + case 0x374: //BMU message, 10ms - SOC + BMU_SOC = ((rx_frame.data.u8[1] - 10) / 2); + break; + case 0x373: //BMU message, 100ms - Pack Voltage and current + BMU_Current = ((((((rx_frame.data.u8[2] * 256.0) + rx_frame.data.u8[3])) - 32768)) * 0.01); + BMU_PackVoltage = ((rx_frame.data.u8[4] * 256.0 + rx_frame.data.u8[5]) * 0.1); + BMU_Power = (BMU_Current * BMU_PackVoltage); + break; + case 0x6e1: //BMU message, 25ms - Battery temperatures and voltages + case 0x6e2: + case 0x6e3: + case 0x6e4: + BMU_Detected = 1; + //Pid index 0-3 + pid_index = (rx_frame.MsgID) - 1761; + //cmu index 1-12: ignore high order nibble which appears to sometimes contain other status bits + cmu_id = (rx_frame.data.u8[0] & 0x0f); + // + temp1 = rx_frame.data.u8[1] - 50.0; + temp2 = rx_frame.data.u8[2] - 50.0; + temp3 = rx_frame.data.u8[3] - 50.0; + + voltage1 = (((rx_frame.data.u8[4] * 256.0 + rx_frame.data.u8[5]) * 0.005) + 2.1); + voltage2 = (((rx_frame.data.u8[6] * 256.0 + rx_frame.data.u8[7]) * 0.005) + 2.1); + + voltage_index = ((cmu_id - 1) * 8 + (2 * pid_index)); + temp_index = ((cmu_id - 1) * 6 + (2 * pid_index)); + if (cmu_id >= 7) + { + voltage_index -= 4; + temp_index -= 3; + } + cell_voltages[voltage_index] = voltage1; + cell_voltages[voltage_index + 1] = voltage2; + + if (pid_index == 0) + { + cell_temperatures[temp_index] = temp2; + cell_temperatures[temp_index + 1] = temp3; + } + else + { + cell_temperatures[temp_index] = temp1; + if (cmu_id != 6 && cmu_id != 12) + { + cell_temperatures[temp_index + 1] = temp2; + } + } + break; + default: + break; + } +} + +void send_can_imiev_battery() +{ + unsigned long currentMillis = millis(); + // Send 100ms CAN Message + if (currentMillis - previousMillis100 >= interval100) + { + previousMillis100 = currentMillis; + } +} diff --git a/Software/IMIEV-CZERO-ION-BATTERY.h b/Software/IMIEV-CZERO-ION-BATTERY.h new file mode 100644 index 00000000..ca386b45 --- /dev/null +++ b/Software/IMIEV-CZERO-ION-BATTERY.h @@ -0,0 +1,40 @@ +#ifndef IMIEV_CZERO_ION_BATTERY_H +#define IMIEV_CZERO_ION_BATTERY_H +#include +#include "ESP32CAN.h" + +#define BATTERY_WH_MAX 22000 //Battery size in Wh (Maximum value Fronius accepts is 60000 [60kWh] you can use larger batteries but do set value over 60000 +#define ABSOLUTE_MAX_VOLTAGE 4040 // 404.4V,if battery voltage goes over this, charging is not possible (goes into forced discharge) +#define ABSOLUTE_MIN_VOLTAGE 3100 // 310.0V if battery voltage goes under this, discharging further is disabled +#define MAXPERCENTAGE_ZOE 800 //80.0% , Max percentage the battery will charge to (App will show 100% once this value is reached) +#define MINPERCENTAGE_ZOE 200 //20.0% , Min percentage the battery will discharge to (App will show 0% once this value is reached) +static byte printValues = 1; //Should modbus values be printed to serial output? + +// These parameters need to be mapped for the Gen24 +extern uint16_t SOC; +extern uint16_t StateOfHealth; +extern uint16_t battery_voltage; +extern uint16_t battery_current; +extern uint16_t capacity_Wh; +extern uint16_t remaining_capacity_Wh; +extern uint16_t max_target_discharge_power; +extern uint16_t max_target_charge_power; +extern uint16_t bms_status; +extern uint16_t bms_char_dis_status; +extern uint16_t stat_batt_power; +extern uint16_t temperature_min; +extern uint16_t temperature_max; +extern uint16_t CANerror; +// Definitions for BMS status +#define STANDBY 0 +#define INACTIVE 1 +#define DARKSTART 2 +#define ACTIVE 3 +#define FAULT 4 +#define UPDATING 5 + +void update_values_imiev_battery(); +void receive_can_imiev_battery(CAN_frame_t rx_frame); +void send_can_imiev_battery(); + +#endif \ No newline at end of file diff --git a/Software/Software.ino b/Software/Software.ino index dcb2a3fd..e4e4193d 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -2,6 +2,7 @@ #define BATTERY_TYPE_LEAF // See NISSAN-LEAF-BATTERY.h for more LEAF battery settings //#define TESLA_MODEL_3_BATTERY // See TESLA-MODEL-3-BATTERY.h for more Tesla battery settings //#define RENAULT_ZOE_BATTERY // See RENAULT-ZOE-BATTERY.h for more Zoe battery settings +//#define IMIEV_ION_CZERO_BATTERY // See IMIEV-CZERO-ION-BATTERY.h for more triplet battery settings //#define CHADEMO // See CHADEMO.h for more Chademo related settings /* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki */ @@ -167,8 +168,11 @@ void setup() Serial.println("Tesla Model 3 battery selected"); #endif #ifdef RENAULT_ZOE_BATTERY - Serial.println("Renault Zoe battery selected"); + Serial.println("Renault Zoe / Kangoo battery selected"); #endif + #ifdef IMIEV_ION_CZERO_BATTERY + Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected"); + #endif } // perform main program functions @@ -208,12 +212,16 @@ void handle_can() #ifdef RENAULT_ZOE_BATTERY receive_can_zoe_battery(rx_frame); #endif + #ifdef IMIEV_ION_CZERO_BATTERY + receive_can_imiev_battery(rx_frame); + #endif #ifdef CAN_BYD receive_can_byd(rx_frame); #endif - #ifdef CHADEMO + #ifdef CHADEMO receive_can_chademo(rx_frame); #endif + } else { @@ -244,6 +252,9 @@ void handle_can() #ifdef RENAULT_ZOE_BATTERY send_can_zoe_battery(); #endif + #ifdef IMIEV_ION_CZERO_BATTERY + send_can_imiev_battery(); + #endif #ifdef CHADEMO send_can_chademo_battery(); #endif @@ -260,6 +271,9 @@ void handle_inverter() #ifdef RENAULT_ZOE_BATTERY update_values_zoe_battery(); //Map the values to the correct registers #endif + #ifdef IMIEV_ION_CZERO_BATTERY + update_values_imiev_battery(); //Map the values to the correct registers + #endif #ifdef SOLAX_CAN update_values_can_solax(); #endif @@ -269,7 +283,7 @@ void handle_inverter() #ifdef PYLON_CAN update_values_can_pylon(); #endif - #ifdef CHADEMO + #ifdef CHADEMO update_values_can_chademo(); #endif