Feature: Add initial support for Atto 3 (#325)

* Add initial files for Atto 3
This commit is contained in:
Daniel Öster 2024-06-10 23:35:30 +03:00 committed by GitHub
parent 6f541ace92
commit 218ed254be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 342 additions and 0 deletions

View file

@ -34,6 +34,7 @@ jobs:
# These are the batteries for which the code will be compiled.
battery:
- BMW_I3_BATTERY
- BYD_ATTO_3_BATTERY
- CHADEMO_BATTERY
- IMIEV_CZERO_ION_BATTERY
- KIA_HYUNDAI_64_BATTERY

View file

@ -37,6 +37,7 @@ jobs:
# These are the batteries for which the code will be compiled.
battery:
- BMW_I3_BATTERY
- BYD_ATTO_3_BATTERY
- CHADEMO_BATTERY
- IMIEV_CZERO_ION_BATTERY
- KIA_HYUNDAI_64_BATTERY

View file

@ -9,6 +9,7 @@
/* Select battery used */
//#define BMW_I3_BATTERY
//#define BYD_ATTO_3_BATTERY
//#define CHADEMO_BATTERY //NOTE: inherently enables CONTACTOR_CONTROL below
//#define IMIEV_CZERO_ION_BATTERY
//#define KIA_HYUNDAI_64_BATTERY

View file

@ -7,6 +7,10 @@
#include "BMW-I3-BATTERY.h"
#endif
#ifdef BYD_ATTO_3_BATTERY
#include "BYD-ATTO-3-BATTERY.h"
#endif
#ifdef CHADEMO_BATTERY
#include "CHADEMO-BATTERY.h"
#endif

View file

@ -0,0 +1,319 @@
#include "../include.h"
#ifdef BYD_ATTO_3_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 "BYD-ATTO-3-BATTERY.h"
/* TODO:
- Get contactor closing working
- NOTE: Some packs can be locked hard? after a crash has occured. Bypassing contactors manually might be required
- Figure out which CAN messages need to be sent towards the battery to keep it alive
-Maybe already enough with 0x12D and 0x411?
- Map all values from battery CAN messages
-SOC% still not found (Lets take it from PID poll)
-Voltage still not found (Lets take it from PID poll)
-Rest is optional and can be added later
*/
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis50 = 0; // will store last time a 50ms CAN Message was send
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
static uint8_t counter_50ms = 0;
static uint8_t counter_100ms = 0;
static uint8_t frame6_counter = 0xB;
static uint8_t frame7_counter = 0x5;
static int16_t temperature_ambient = 0;
static int16_t daughterboard_temperatures[10];
static int16_t lowest_temperature = 0;
static int16_t highest_temperature = 0;
static int16_t calc_min_temperature = 0;
static int16_t calc_max_temperature = 0;
static uint16_t BMS_SOC = 0;
static uint16_t BMS_voltage = 0;
static int16_t BMS_current = 0;
static int16_t BMS_lowest_cell_temperature = 0;
static int16_t BMS_highest_cell_temperature = 0;
static int16_t BMS_average_cell_temperature = 0;
#define POLL_FOR_BATTERY_SOC 0x05
#define POLL_FOR_BATTERY_VOLTAGE 0x08
#define POLL_FOR_BATTERY_CURRENT 0x09
#define POLL_FOR_LOWEST_TEMP_CELL 0x2f
#define POLL_FOR_HIGHEST_TEMP_CELL 0x31
#define POLL_FOR_BATTERY_PACK_AVG_TEMP 0x32
#define UNKNOWN_POLL_1 0xFC
CAN_frame_t ATTO_3_12D = {.FIR = {.B =
{
.DLC = 8,
.FF = CAN_frame_std,
}},
.MsgID = 0x12D,
.data = {0xA0, 0x28, 0x02, 0xA0, 0x0C, 0x71, 0xCF, 0x49}};
CAN_frame_t ATTO_3_411 = {.FIR = {.B =
{
.DLC = 8,
.FF = CAN_frame_std,
}},
.MsgID = 0x411,
.data = {0x98, 0x3A, 0x88, 0x13, 0x9D, 0x00, 0xFF, 0x8C}};
CAN_frame_t ATTO_3_7E7_POLL = {
.FIR = {.B =
{
.DLC = 8,
.FF = CAN_frame_std,
}},
.MsgID = 0x7E7,
.data = {0x03, 0x22, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00}}; //Poll PID 03 22 00 05 (POLL_FOR_BATTERY_SOC)
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
datalayer.battery.status.real_soc = BMS_SOC * 100;
datalayer.battery.status.voltage_dV = BMS_voltage * 100;
datalayer.battery.status.current_dA = BMS_current; //TODO: Signed right way?
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
datalayer.battery.status.max_discharge_power_W = 5000;
datalayer.battery.status.max_charge_power_W = 5000;
datalayer.battery.status.active_power_W; //TODO: Map!
// Initialize min and max variables
calc_min_temperature = daughterboard_temperatures[0];
calc_max_temperature = daughterboard_temperatures[0];
// Loop through the array of daughterboard temps to find the smallest and largest values
for (int i = 1; i < 10; i++) {
if (daughterboard_temperatures[i] < calc_min_temperature) {
calc_min_temperature = daughterboard_temperatures[i];
}
if (daughterboard_temperatures[i] > calc_max_temperature) {
calc_max_temperature = daughterboard_temperatures[i];
}
}
datalayer.battery.status.temperature_min_dC = calc_min_temperature * 10; // Add decimals
datalayer.battery.status.temperature_max_dC = calc_max_temperature * 10;
#ifdef DEBUG_VIA_USB
#endif
}
void receive_can_battery(CAN_frame_t rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.MsgID) { //Log values taken with 422V from battery
case 0x244: //00,00,00,04,41,0F,20,8B - Static, values never changes between logs
break;
case 0x245: //01,00,02,19,3A,25,90,F4 Seems to have a mux in frame0
//02,00,90,01,79,79,90,EA // Point of interest, went from 7E,75 to 7B,7C when discharging
//03,C6,88,12,FD,48,90,5C
//04,00,FF,FF,00,00,90,6D
if (rx_frame.data.u8[0] == 0x01) {
temperature_ambient = (rx_frame.data.u8[4] - 40); // TODO, check if this is actually temperature_ambient
}
break;
case 0x286: //01,FF,FF,FF,FF,FF,FF,04 - Static, values never changes between logs
break;
case 0x334: //FF,FF,FF,FC,3F,00,F0,D7 - Static, values never changes between logs
break;
case 0x338: //01,52,02,00,88,13,00,0F
//01,51,02,00,88,13,00,10 407.5V 18deg
//01,4F,02,00,88,13,00,12 408.5V 14deg
break;
case 0x344: //00,52,02,CC,1F,FF,04,BD
break;
case 0x345: //27,0B,00,00,00,E0,01,EC - Static, values never changes between logs
break;
case 0x347: //FF,00,00,F9,FF,FF,FF,0A - Static, values never changes between logs
break;
case 0x34A: //00,52,02,CC,1F,FF,04,BD
//00,51,02,CC,1F,FF,04,BE //407.5V 18deg
//00,4F,02,CC,1F,FF,04,C0 //408.5V 14deg
break;
case 0x35E: //01,00,C8,32,00,63,00,A1 - Flickering between A0 and A1, Could be temperature?
//01,00,64,01,10,63,00,26 //407.5V 18deg
//01,00,64,1C,10,63,00,0B //408.5V 14deg
break;
case 0x360: //30,19,DE,D1,0B,C3,4B,EE - Static, values never changes between logs, Last and first byte has F-0 counters
break;
case 0x36C: //01,57,13,DC,08,70,17,29 Seems to have a mux in frame0 , first message is static, never changes between logs
//02,03,DC,05,C0,0F,0F,3B - Static, values never changes between logs
//03,86,01,40,06,5C,02,D1 - Static, values never changes between logs
//04,57,13,73,04,01,FF,1A - Static, values never changes between logs
//05,FF,FF,FF,FF,FF,FF,00 - Static, values never changes between logs
break;
case 0x438: //55,55,01,F6,47,2E,10,D9 - 0x10D9 = 4313
//55,55,01,F6,47,FD,0F,0B //407.5V 18deg
//55,55,01,F6,47,15,10,F2 //408.5V 14deg
break;
case 0x43A: //7E,0A,B0,1C,63,E1,03,64
//7E,0A,E0,1E,63,E1,03,32 //407.5V 18deg
//7E,0A,66,1C,63,E1,03,AE //408.5V 14deg
break;
case 0x43B: //01,3B,06,39,FF,64,64,BD
//01,3B,06,38,FF,64,64,BE
break;
case 0x43C: // Daughterboard temperatures reside in this CAN message
if (rx_frame.data.u8[0] == 0x00) {
daughterboard_temperatures[0] = (rx_frame.data.u8[1] - 40);
daughterboard_temperatures[1] = (rx_frame.data.u8[2] - 40);
daughterboard_temperatures[2] = (rx_frame.data.u8[3] - 40);
daughterboard_temperatures[3] = (rx_frame.data.u8[4] - 40);
daughterboard_temperatures[4] = (rx_frame.data.u8[5] - 40);
daughterboard_temperatures[5] = (rx_frame.data.u8[6] - 40);
}
if (rx_frame.data.u8[0] == 0x01) {
daughterboard_temperatures[6] = (rx_frame.data.u8[1] - 40);
daughterboard_temperatures[7] = (rx_frame.data.u8[2] - 40);
daughterboard_temperatures[8] = (rx_frame.data.u8[3] - 40);
daughterboard_temperatures[9] = (rx_frame.data.u8[4] - 40);
}
break;
case 0x43D: //Varies a lot
break;
case 0x444: //9E,01,88,13,64,64,98,65
//9A,01,B6,13,64,64,98,3B //407.5V 18deg
//9B,01,B8,13,64,64,98,38 //408.5V 14deg
break;
case 0x445: //00,98,FF,FF,63,20,4E,98 - Static, values never changes between logs
break;
case 0x446: //2C,D4,0C,4D,21,DC,0C,9D - 0,1,7th frame varies a lot
break;
case 0x447: // Seems to contain more temperatures, highest and lowest?
//06,38,01,3B,E0,03,39,69
//06,36,02,36,E0,03,36,72,
lowest_temperature = (rx_frame.data.u8[1] - 40); //Best guess for now
highest_temperature = (rx_frame.data.u8[3] - 40); //Best guess for now
break;
case 0x47B: //01,FF,FF,FF,FF,FF,FF,FF - Static, values never changes between logs
break;
case 0x524: //24,40,00,00,00,00,00,9B - Static, values never changes between logs
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; // Let system know battery is sending CAN
//This message transmits every 5?seconds. Seems like suitable place to poll for a PID
ESP32Can.CANWriteFrame(&ATTO_3_7E7_POLL);
switch (ATTO_3_7E7_POLL.data.u8[3]) {
case POLL_FOR_BATTERY_SOC:
ATTO_3_7E7_POLL.data.u8[3] = POLL_FOR_BATTERY_VOLTAGE;
break;
case POLL_FOR_BATTERY_VOLTAGE:
ATTO_3_7E7_POLL.data.u8[3] = POLL_FOR_BATTERY_CURRENT;
break;
case POLL_FOR_BATTERY_CURRENT:
ATTO_3_7E7_POLL.data.u8[3] = POLL_FOR_LOWEST_TEMP_CELL;
break;
case POLL_FOR_LOWEST_TEMP_CELL:
ATTO_3_7E7_POLL.data.u8[3] = POLL_FOR_HIGHEST_TEMP_CELL;
break;
case POLL_FOR_HIGHEST_TEMP_CELL:
ATTO_3_7E7_POLL.data.u8[3] = POLL_FOR_BATTERY_PACK_AVG_TEMP;
break;
case POLL_FOR_BATTERY_PACK_AVG_TEMP:
ATTO_3_7E7_POLL.data.u8[3] = POLL_FOR_BATTERY_SOC;
break;
default: //Something went wrong with logic, request SOC
ATTO_3_7E7_POLL.data.u8[3] = POLL_FOR_BATTERY_SOC;
break;
}
break;
case 0x7EF: //OBD2 PID reply from battery
switch (rx_frame.data.u8[3]) {
case POLL_FOR_BATTERY_SOC:
BMS_SOC = rx_frame.data.u8[4];
break;
case POLL_FOR_BATTERY_VOLTAGE:
BMS_voltage = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
break;
case POLL_FOR_BATTERY_CURRENT:
BMS_current = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) - 5000;
break;
case POLL_FOR_LOWEST_TEMP_CELL:
BMS_lowest_cell_temperature = (rx_frame.data.u8[4] - 40);
break;
case POLL_FOR_HIGHEST_TEMP_CELL:
BMS_highest_cell_temperature = (rx_frame.data.u8[4] - 40);
break;
case POLL_FOR_BATTERY_PACK_AVG_TEMP:
BMS_average_cell_temperature = (rx_frame.data.u8[4] - 40);
break;
default: //Unrecognized reply
break;
}
break;
default:
break;
}
}
void send_can_battery() {
unsigned long currentMillis = millis();
//Send 50ms message
if (currentMillis - previousMillis50 >= INTERVAL_50_MS) {
// Check if sending of CAN messages has been delayed too much.
if ((currentMillis - previousMillis50 >= INTERVAL_50_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) {
set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis50));
}
previousMillis50 = currentMillis;
counter_50ms++;
if (counter_50ms > 23) {
ATTO_3_12D.data.u8[2] = 0x00; // Goes from 02->00
ATTO_3_12D.data.u8[3] = 0x22; // Goes from A0->22
ATTO_3_12D.data.u8[5] = 0x31; // Goes from 71->31
// TODO: handle more variations after more seconds have passed if needed
}
// Update the counters in frame 6 & 7 (they are not in sync)
if (frame6_counter == 0x0) {
frame6_counter = 0xF; // Reset to 0xF after reaching 0x0
} else {
frame6_counter--; // Decrement the counter
}
if (frame7_counter == 0x0) {
frame7_counter = 0xF; // Reset to 0xF after reaching 0x0
} else {
frame7_counter--; // Decrement the counter
}
ATTO_3_12D.data.u8[6] = (0x0F | (frame6_counter << 4));
ATTO_3_12D.data.u8[7] = (0x09 | (frame7_counter << 4));
ESP32Can.CANWriteFrame(&ATTO_3_12D);
}
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
counter_100ms++;
if (counter_100ms > 3) {
ATTO_3_411.data.u8[5] = 0x01;
ATTO_3_411.data.u8[7] = 0xF5;
}
ESP32Can.CANWriteFrame(&ATTO_3_411);
}
}
void setup_battery(void) { // Performs one time setup at startup
#ifdef DEBUG_VIA_USB
Serial.println("BYD Atto 3 battery selected");
#endif
datalayer.battery.info.max_design_voltage_dV = 4400; // Over this charging is not possible
datalayer.battery.info.min_design_voltage_dV = 3700; // Under this discharging is disabled
}
#endif

View file

@ -0,0 +1,12 @@
#ifndef ATTO_3_BATTERY_H
#define ATTO_3_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#define BATTERY_SELECTED
#define MAX_CELL_DEVIATION_MV 150
void setup_battery(void);
#endif

View file

@ -25,6 +25,7 @@ enum led_color { GREEN, YELLOW, RED, BLUE, RGB };
#define INTERVAL_10_MS_DELAYED 15
#define INTERVAL_20_MS_DELAYED 30
#define INTERVAL_30_MS_DELAYED 40
#define INTERVAL_50_MS_DELAYED 65
#define INTERVAL_100_MS_DELAYED 120
#define INTERVAL_500_MS_DELAYED 550

View file

@ -445,6 +445,9 @@ String processor(const String& var) {
#ifdef BMW_I3_BATTERY
content += "BMW i3";
#endif
#ifdef BYD_ATTO_3_BATTERY
content += "BYD Atto 3";
#endif
#ifdef CHADEMO_BATTERY
content += "Chademo V2X mode";
#endif