From 764fc8cca96870beb89438988b51f4041f0360e4 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Mon, 19 May 2025 20:43:54 +0300 Subject: [PATCH] Use base class for Chademo battery --- .../src/battery/CHADEMO-BATTERY-INTERNAL.h | 233 ------------ Software/src/battery/CHADEMO-BATTERY.cpp | 128 +------ Software/src/battery/CHADEMO-BATTERY.h | 349 +++++++++++++++++- Software/src/battery/CHADEMO-SHUNTS.cpp | 1 - 4 files changed, 362 insertions(+), 349 deletions(-) delete mode 100644 Software/src/battery/CHADEMO-BATTERY-INTERNAL.h diff --git a/Software/src/battery/CHADEMO-BATTERY-INTERNAL.h b/Software/src/battery/CHADEMO-BATTERY-INTERNAL.h deleted file mode 100644 index ba761260..00000000 --- a/Software/src/battery/CHADEMO-BATTERY-INTERNAL.h +++ /dev/null @@ -1,233 +0,0 @@ -#ifndef CHADEMO_BATTERY_TYPES_H -#define CHADEMO_BATTERY_TYPES_H - -#define MAX_EVSE_POWER_CHARGING 3300 -#define MAX_EVSE_OUTPUT_VOLTAGE 410 -#define MAX_EVSE_OUTPUT_CURRENT 11 - -enum CHADEMO_STATE { - CHADEMO_FAULT, - CHADEMO_STOP, - CHADEMO_IDLE, - CHADEMO_CONNECTED, - CHADEMO_INIT, // intermediate state indicating CAN from Vehicle not yet received after connection - CHADEMO_NEGOTIATE, - CHADEMO_EV_ALLOWED, - CHADEMO_EVSE_PREPARE, - CHADEMO_EVSE_START, - CHADEMO_EVSE_CONTACTORS_ENABLED, - CHADEMO_POWERFLOW, -}; - -enum Mode { CHADEMO_CHARGE, CHADEMO_DISCHARGE, CHADEMO_BIDIRECTIONAL }; - -/* Charge/discharge sequence, indicating applicable V2H guideline - * If sequence number is not agreed upon via H201/H209 between EVSE and Vehicle, - * V2H 1.1 is assumed, which..is somehow between 0x0 and 0x1 ? TODO: better understanding here - * Use CHADEMO_seq to decide whether emitting 209 is necessary - * 0x0 1.0 and earlier - * 0x1 2.0 appendix A - * 0x2 2.0 appendix B - * TODO: is this influenced by x109->CHADEMO_protocol_number, or x102->ControlProtocolNumberEV ?? - * Unused for now. -uint8_t CHADEMO_seq = 0x0; - */ - -/*----------- CHARGING SUPPORT V2X --------------------------------------------------------------*/ -/* ---------- VEHICLE Data structures */ - -//H100 - Vehicle - Minimum charging expectations -//TODO decide whether default values for vehicle-origin frames is even appropriate -struct x100_Vehicle_Charging_Limits { - uint8_t MinimumChargeCurrent = 0; - uint16_t MinimumBatteryVoltage = 300; - uint16_t MaximumBatteryVoltage = 402; - uint8_t ConstantOfChargingRateIndication = 0; -}; -//H101 - Vehicle - Maximum charging expectations -struct x101_Vehicle_Charging_Estimate { - uint8_t MaxChargingTime10sBit = 0; - uint8_t MaxChargingTime1minBit = 0; - uint8_t EstimatedChargingTime = 0; - uint16_t RatedBatteryCapacity = 0; -}; - -//H102 - Vehicle - Charging targets and Status -// peer to x109 from EVSE -// termination triggers in both -// TODO see also Table A.26—Charge control termination command patterns -struct x102_Vehicle_Charging_Session { //Frame byte - uint8_t ControlProtocolNumberEV = 0; // 0 - uint16_t TargetBatteryVoltage = 0; // 1-2 - uint8_t ChargingCurrentRequest = 0; // 3 Note: per spec, units for this changed from kWh --> % - - union { - uint8_t faults; - struct { - bool unused_3 : 1; - bool unused_2 : 1; - bool unused_1 : 1; - bool FaultBatteryVoltageDeviation : 1; // 4 - bool FaultHighBatteryTemperature : 1; // 3 - bool FaultBatteryCurrentDeviation : 1; // 2 - bool FaultBatteryUnderVoltage : 1; // 1 - bool FaultBatteryOverVoltage : 1; // 0 - } fault; - } f; - - union { - uint8_t packed; - struct { - bool StatusVehicleDischargeCompatible : 1; //5.7 - bool unused_2 : 1; //5.6 - bool unused_1 : 1; //5.5 - bool StatusNormalStopRequest : 1; //5.4 - bool StatusVehicle : 1; //5.3 - bool StatusChargingError : 1; //5.2 - bool StatusVehicleShifterPosition : 1; //5.1 - bool StatusVehicleChargingEnabled : 1; //5.0 - bit zero is TODO. Vehicle charging enabled ==1 *AND* charge - // permission signal k needs to be active for charging to be - // permitted -- TODO document bits per byte for these flags - // and update variables to be more appropriate - } status; - } s; - - uint8_t StateOfCharge = 0; //6 state of charge? -}; - -/* ---------- CHARGING: EVSE Data structures */ -struct x108_EVSE_Capabilities { // Frame byte - bool contactor_weld_detection = 1; // 0 - uint16_t available_output_voltage = MAX_EVSE_OUTPUT_VOLTAGE; // 1,2 - uint8_t available_output_current = MAX_EVSE_OUTPUT_CURRENT; // 3 - uint16_t threshold_voltage = 297; // 4,5 voltage that EVSE will stop if car fails to - // perhaps vehicle minus 3%, hardcoded initially to 96*2.95 - // 6,7 = unused -}; - -/* Does double duty for charging and discharging */ -struct x109_EVSE_Status { // Frame byte - uint8_t CHADEMO_protocol_number = 0x02; // 0 - uint16_t setpoint_HV_VDC = - 0; // 1,2 NOTE: charger_setpoint_HV_VDC elsewhere is a float. THIS is protocol-defined as an int. cast float->int and lose some precision for protocol adherence - uint8_t setpoint_HV_IDC = 0; // 3 - // - bool discharge_compatible = true; // 4, bit 0. bits - // 4, bit 7-6 (?) unused. spec typo? maybe 1-7 unused - union { - uint8_t packed; - struct { - bool EVSE_status : 1; // 5, bit 0 - bool EVSE_error : 1; // 5, bit 1 - bool connector_locked : 1; // 5, bit 2 //NOTE: treated as connector_lock during discharge, but - // seen as 'energizing' during charging mode - - bool battery_incompatible : 1; // 5, bit 3 - bool ChgDischError : 1; // 5, bit 4 - - bool ChgDischStopControl : 1; // 5, bit 5 - set to false for initialization to indicate 'preparing to charge' - // set to false when ready to charge/discharge - - } status; - } s; - - // Either, or; not both. - // seconds field set to 0xFF by default - // indicating only the minutes field is used instead - // BOTH observed initially set to 0xFF in logs, so use - // that as the initialzed value - uint8_t remaining_time_10s = 0xFF; // 6 - uint8_t remaining_time_1m = 0xFF; // 7 -}; - -/*----------- DISCHARGING SUPPORT V2X --------------------------------------------------------------*/ -/* ---------- VEHICLE Data structures */ -//H200 - Vehicle - Discharge limits -struct x200_Vehicle_Discharge_Limits { - uint8_t MaximumDischargeCurrent = 0xFF; - uint16_t MinimumDischargeVoltage = 0; - uint16_t MinimumBatteryDischargeLevel = 0; - uint16_t MaxRemainingCapacityForCharging = 0; -}; - -/* TODO When charge/discharge sequence control number (ID201/209) is not received, the vehicle or the EVSE -should determine that the other is the EVSE or the vehicle of the model before the V2H guideline 1.1. */ -//H201 - Vehicle - Estimated capacity available -// Intended primarily for display purposes. -// Peer to H209 -// NOTE: in available CAN logs from a Leaf, 209 is sent with no 201 reply, so < 1.1 must be the inferred version -struct x201_Vehicle_Discharge_Estimate { - uint8_t V2HchargeDischargeSequenceNum = 0; - uint16_t ApproxDischargeCompletionTime = 0; - uint16_t AvailableVehicleEnergy = 0; -}; - -/* ---------- EVSE Data structures */ -struct x208_EVSE_Discharge_Capability { // Frame byte - uint8_t present_discharge_current = 0xFF; // 0 - uint16_t available_input_voltage = 500; // 1,2 -- poorly named as both 'available' and minimum input voltage - uint16_t available_input_current = 250; // 3 -- poorly named as both 'available' and maximum input current - // spec idiosyncracy in naming/description - // 4,5 = unused - uint16_t lower_threshold_voltage = 0; // 6,7 -}; - -// H209 - EVSE - Estimated Discharge Duration -// peer to Vehicle's 201 event (Note: 209 seen -// in CAN logs even when 201 is not) -struct x209_EVSE_Discharge_Estimate { // Frame byte - uint8_t sequence_control_number = 0x2; // 0 - uint16_t remaining_discharge_time = 0x0000; // 0x0000 == unused -}; - -/*----------- DYNAMIC CONTROL SUPPORT --------------------------------------------------------------*/ -/* ---------- VEHICLE Data structures */ -struct x110_Vehicle_Dynamic_Control { //Frame byte - union { - uint8_t packed; - struct { - bool PermissionResetMaxChgTime : 1; // bit 5 or 6? is this only x118 not x110? - bool unused_3 : 1; - bool unused_2 : 1; - bool unused_1 : 1; - bool HighVoltageControlStatus : 1; // bit 2 = High voltage control support - bool HighCurrentControlStatus : 1; // bit 1 = High current control support - // rate of change is -20A/s to 20A/s relative to 102.3 - bool DynamicControlStatus : 1; // bit 0 = Dynamic Control support - } status; - } u; -}; - -/* ---------- EVSE Data structures */ -// TODO 118 -//H118 -//see also table a.59 page 104 IEEE -struct x118_EVSE_Dynamic_Control { // Frame byte - union { - uint8_t packed; - struct { - bool PermissionResetMaxChgTime : 1; // bit 5 or 6? - bool unused_3 : 1; - bool unused_2 : 1; - bool unused_1 : 1; - bool HighVoltageControlStatus : 1; // bit 2 = High voltage control support - bool HighCurrentControlStatus : 1; // bit 1 = High current control support - // rate of change is -20A/s to 20A/s relative to 102.3 - bool DynamicControlStatus : 1; // bit 0 = Dynamic Control support - } status; - } u; -}; - -/*----------- MANUFACTURER ID SUPPORT --------------------------------------------------------------*/ -/* ---------- VEHICLE Data structures */ -//H700 - Vehicle - Manufacturer identification -//Peer to H708 -//Used to adapt to manufacturer-prescribed optional specification -struct x700_Vehicle_Vendor_ID { - uint8_t AutomakerCode = 0; // 0 = set to 0x0 to indicate incompatibility. Best as a starting place - uint8_t OptionalContent = 0; // 1-7, variable per vendor spec -}; - -void handle_chademo_sequence(); - -#endif diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index e2ba0095..75c64071 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -2,7 +2,6 @@ #ifdef CHADEMO_BATTERY #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "CHADEMO-BATTERY-INTERNAL.h" #include "CHADEMO-BATTERY.h" #include "CHADEMO-SHUNTS.h" @@ -14,101 +13,8 @@ #define CAN_STILL_ALIVE 75 //#define CH_CAN_DEBUG -static unsigned long setupMillis = 0; -static unsigned long handlerBeforeMillis = 0; -static unsigned long handlerAfterMillis = 0; - -/* Do not change code below unless you are sure what you are doing */ -static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send -static unsigned long previousMillis5000 = - 0; // will store last time a 5s threshold was reached for display during debug - -bool plug_inserted = false; -bool vehicle_can_initialized = false; -bool vehicle_can_received = false; -bool vehicle_permission = false; -bool evse_permission = false; - -bool precharge_low = false; -bool positive_high = false; -bool contactors_ready = false; - -uint8_t framecount = 0; - -uint8_t max_discharge_current = 0; //TODO not sure on this one, but really influenced by inverter capability - -bool high_current_control_enabled = false; // set to true when high current control is operating - // if true, values from 110.1 and 110.2 should be used instead of 102.3 - // and 118 should be used for evse responses - // permissible rate of change is -20A/s to 20A/s relative to 102.3 - -Mode EVSE_mode = CHADEMO_DISCHARGE; -CHADEMO_STATE CHADEMO_Status = CHADEMO_IDLE; - -/* Charge/discharge sequence, indicating applicable V2H guideline - * If sequence number is not agreed upon via H201/H209 between EVSE and Vehicle, - * V2H 1.1 is assumed - * Use CHADEMO_seq to decide whether emitting 209 is necessary - * 0x0 1.0 and earlier - * 0x1 2.0 appendix A - * 0x2 2.0 appendix B - * Unused for now. -uint8_t CHADEMO_seq = 0x0; - */ - -bool x201_received = false; -bool x209_sent = false; - -struct x100_Vehicle_Charging_Limits x100_chg_lim = {}; -struct x101_Vehicle_Charging_Estimate x101_chg_est = {}; -struct x102_Vehicle_Charging_Session x102_chg_session = {}; -struct x110_Vehicle_Dynamic_Control x110_vehicle_dyn = {}; -struct x200_Vehicle_Discharge_Limits x200_discharge_limits = {}; -struct x201_Vehicle_Discharge_Estimate x201_discharge_estimate = {}; -struct x700_Vehicle_Vendor_ID x700_vendor_id = {}; - -struct x209_EVSE_Discharge_Estimate x209_evse_dischg_est; -struct x108_EVSE_Capabilities x108_evse_cap; -struct x109_EVSE_Status x109_evse_state; -struct x118_EVSE_Dynamic_Control x118_evse_dyn; -struct x208_EVSE_Discharge_Capability x208_evse_dischg_cap; - -CAN_frame CHADEMO_108 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x108, - .data = {0x01, 0xF4, 0x01, 0x0F, 0xB3, 0x01, 0x00, 0x00}}; -CAN_frame CHADEMO_109 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x109, - .data = {0x02, 0x00, 0x00, 0x00, 0x01, 0x20, 0xFF, 0xFF}}; -//For chademo v2.0 only -CAN_frame CHADEMO_118 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x118, - .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - -// OLD value from skeleton implementation, indicates dynamic control is possible. -// Hardcode above as being incompatible for simplicity in current incarnation. -// .data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}}; - -// 0x200 : From vehicle-side. A V2X-ready vehicle will send this message to broadcast its “Maximum discharger current”. (It is a similar logic to the limits set in 0x100 or 0x102 during a DC charging session) -// 0x208 : From EVSE-side. A V2X EVSE will use this to send the “present discharger current” during the session, and the “available input current”. (uses similar logic to 0x108 and 0x109 during a DC charging session) -CAN_frame CHADEMO_208 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x208, - .data = {0xFF, 0xF4, 0x01, 0xF0, 0x00, 0x00, 0xFA, 0x00}}; -CAN_frame CHADEMO_209 = {.FD = false, - .ext_ID = false, - .DLC = 8, - .ID = 0x209, - .data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; - //This function maps all the values fetched via CAN to the correct parameters used for the inverter -void update_values_battery() { +void ChademoBattery::update_values() { datalayer.battery.status.real_soc = x102_chg_session.StateOfCharge; @@ -147,21 +53,21 @@ void update_values_battery() { //see IEEE Table A.26—Charge control termination command pattern on pg58 //for stop conditions -inline void process_vehicle_charging_minimums(CAN_frame rx_frame) { +void ChademoBattery::process_vehicle_charging_minimums(CAN_frame rx_frame) { x100_chg_lim.MinimumChargeCurrent = rx_frame.data.u8[0]; x100_chg_lim.MinimumBatteryVoltage = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); x100_chg_lim.MaximumBatteryVoltage = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]); x100_chg_lim.ConstantOfChargingRateIndication = rx_frame.data.u8[6]; } -inline void process_vehicle_charging_maximums(CAN_frame rx_frame) { +void ChademoBattery::process_vehicle_charging_maximums(CAN_frame rx_frame) { x101_chg_est.MaxChargingTime10sBit = rx_frame.data.u8[1]; x101_chg_est.MaxChargingTime1minBit = rx_frame.data.u8[2]; x101_chg_est.EstimatedChargingTime = rx_frame.data.u8[3]; x101_chg_est.RatedBatteryCapacity = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[5]); } -inline void process_vehicle_charging_session(CAN_frame rx_frame) { +void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) { uint16_t newTargetBatteryVoltage = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[1]); uint16_t priorTargetBatteryVoltage = x102_chg_session.TargetBatteryVoltage; uint8_t newChargingCurrentRequest = rx_frame.data.u8[3]; @@ -303,7 +209,7 @@ inline void process_vehicle_charging_session(CAN_frame rx_frame) { } /* x200 Vehicle, peer to x208 EVSE */ -inline void process_vehicle_charging_limits(CAN_frame rx_frame) { +void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) { x200_discharge_limits.MaximumDischargeCurrent = rx_frame.data.u8[0]; x200_discharge_limits.MinimumDischargeVoltage = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]); @@ -332,7 +238,7 @@ inline void process_vehicle_charging_limits(CAN_frame rx_frame) { /* Vehicle 0x201, peer to EVSE 0x209 * HOWEVER, 201 isn't even emitted in any of the v2x canlogs available */ -inline void process_vehicle_discharge_estimate(CAN_frame rx_frame) { +void ChademoBattery::process_vehicle_discharge_estimate(CAN_frame rx_frame) { unsigned long currentMillis = millis(); x201_discharge_estimate.V2HchargeDischargeSequenceNum = rx_frame.data.u8[0]; @@ -350,7 +256,7 @@ inline void process_vehicle_discharge_estimate(CAN_frame rx_frame) { #endif } -inline void process_vehicle_dynamic_control(CAN_frame rx_frame) { +void ChademoBattery::process_vehicle_dynamic_control(CAN_frame rx_frame) { //SM Dynamic Control = Charging station can increase of decrease "available output current" during charging. //If you set 0x110 byte 0, bit 0 to 1 you say you can do dynamic control. //Charging station communicates this in 0x118 byte 0, bit 0 @@ -359,13 +265,13 @@ inline void process_vehicle_dynamic_control(CAN_frame rx_frame) { x110_vehicle_dyn.u.status.DynamicControlStatus = bitRead(rx_frame.data.u8[0], 0); } -inline void process_vehicle_vendor_ID(CAN_frame rx_frame) { +void ChademoBattery::process_vehicle_vendor_ID(CAN_frame rx_frame) { x700_vendor_id.AutomakerCode = rx_frame.data.u8[0]; x700_vendor_id.OptionalContent = ((rx_frame.data.u8[2] << 8) | rx_frame.data.u8[1]); //Actually more bytes, but not needed for our purpose } -void handle_incoming_can_frame_battery(CAN_frame rx_frame) { +void ChademoBattery::handle_incoming_can_frame(CAN_frame rx_frame) { #ifdef CH_CAN_DEBUG logging.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D logging.print(" "); @@ -438,7 +344,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { } /* (re)initialize evse structures to pre-charge/discharge states */ -void evse_init() { +void ChademoBattery::evse_init() { // Held at 1 until start of charge when set to 0 // returns to 1 when ceasing power flow // mutually exclusive values @@ -457,7 +363,7 @@ void evse_init() { } /* updates for x108 */ -void update_evse_capabilities(CAN_frame& f) { +void ChademoBattery::update_evse_capabilities(CAN_frame& f) { /* TODO use charger defines/runtime config? * for now..leave as a future tweak. @@ -495,7 +401,7 @@ void update_evse_capabilities(CAN_frame& f) { } /* updates for x109 */ -void update_evse_status(CAN_frame& f) { +void ChademoBattery::update_evse_status(CAN_frame& f) { x109_evse_state.s.status.EVSE_status = 1; x109_evse_state.s.status.EVSE_error = 0; @@ -586,7 +492,7 @@ void update_evse_status(CAN_frame& f) { * NOTE: x209 is emitted in CAN logs when x201 isn't even present * it may not be understood by leaf (or ignored unless >= a certain protocol version or v2h sequence number */ -void update_evse_discharge_estimate(CAN_frame& f) { +void ChademoBattery::update_evse_discharge_estimate(CAN_frame& f) { //x209_evse_dischg_est.remaining_discharge_time_1m = x201_discharge_estimate.ApproxDischargeCompletionTime; @@ -605,7 +511,7 @@ void update_evse_discharge_estimate(CAN_frame& f) { } /* x208 EVSE, peer to 0x200 Vehicle */ -void update_evse_discharge_capabilities(CAN_frame& f) { +void ChademoBattery::update_evse_discharge_capabilities(CAN_frame& f) { //present discharge current is a measured value x208_evse_dischg_cap.present_discharge_current = 0xFF - get_measured_current(); @@ -655,7 +561,7 @@ void update_evse_discharge_capabilities(CAN_frame& f) { CHADEMO_208.data.u8[7] = highByte(x208_evse_dischg_cap.lower_threshold_voltage); } -void transmit_can_battery(unsigned long currentMillis) { +void ChademoBattery::transmit_can(unsigned long currentMillis) { handlerBeforeMillis = currentMillis; handle_chademo_sequence(); @@ -733,7 +639,7 @@ void transmit_can_battery(unsigned long currentMillis) { * 5) Emergency stop stage * CHADEMO_FAULT */ -void handle_chademo_sequence() { +void ChademoBattery::handle_chademo_sequence() { precharge_low = digitalRead(PRECHARGE_PIN) == LOW; positive_high = digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH; @@ -1020,7 +926,7 @@ void handle_chademo_sequence() { return; } -void setup_battery(void) { // Performs one time setup at startup +void ChademoBattery::setup(void) { // Performs one time setup at startup pinMode(CHADEMO_PIN_2, OUTPUT); digitalWrite(CHADEMO_PIN_2, LOW); diff --git a/Software/src/battery/CHADEMO-BATTERY.h b/Software/src/battery/CHADEMO-BATTERY.h index eacc2fd1..7e50841f 100644 --- a/Software/src/battery/CHADEMO-BATTERY.h +++ b/Software/src/battery/CHADEMO-BATTERY.h @@ -2,8 +2,7 @@ #define CHADEMO_BATTERY_H #include #include "../include.h" - -#define BATTERY_SELECTED +#include "CanBattery.h" //Contactor control is required for CHADEMO support #define CONTACTOR_CONTROL @@ -12,7 +11,349 @@ // other measurement sources may be added in the future #define ISA_SHUNT -void setup_battery(void); -void transmit_can_frame(CAN_frame* tx_frame, int interface); +#define BATTERY_SELECTED +#define SELECTED_BATTERY_CLASS ChademoBattery + +class ChademoBattery : public CanBattery { + public: + virtual void setup(void); + virtual void handle_incoming_can_frame(CAN_frame rx_frame); + virtual void update_values(); + virtual void transmit_can(unsigned long currentMillis); + + private: + void process_vehicle_charging_minimums(CAN_frame rx_frame); + void process_vehicle_charging_maximums(CAN_frame rx_frame); + void process_vehicle_charging_session(CAN_frame rx_frame); + void process_vehicle_charging_limits(CAN_frame rx_frame); + void process_vehicle_discharge_estimate(CAN_frame rx_frame); + void process_vehicle_dynamic_control(CAN_frame rx_frame); + void process_vehicle_vendor_ID(CAN_frame rx_frame); + void evse_init(); + void update_evse_capabilities(CAN_frame& f); + void update_evse_status(CAN_frame& f); + void update_evse_discharge_estimate(CAN_frame& f); + void update_evse_discharge_capabilities(CAN_frame& f); + void handle_chademo_sequence(); + + static const int MAX_EVSE_POWER_CHARGING = 3300; + static const int MAX_EVSE_OUTPUT_VOLTAGE = 410; + static const int MAX_EVSE_OUTPUT_CURRENT = 11; + + enum CHADEMO_STATE { + CHADEMO_FAULT, + CHADEMO_STOP, + CHADEMO_IDLE, + CHADEMO_CONNECTED, + CHADEMO_INIT, // intermediate state indicating CAN from Vehicle not yet received after connection + CHADEMO_NEGOTIATE, + CHADEMO_EV_ALLOWED, + CHADEMO_EVSE_PREPARE, + CHADEMO_EVSE_START, + CHADEMO_EVSE_CONTACTORS_ENABLED, + CHADEMO_POWERFLOW, + }; + + enum Mode { CHADEMO_CHARGE, CHADEMO_DISCHARGE, CHADEMO_BIDIRECTIONAL }; + + /* Charge/discharge sequence, indicating applicable V2H guideline + * If sequence number is not agreed upon via H201/H209 between EVSE and Vehicle, + * V2H 1.1 is assumed, which..is somehow between 0x0 and 0x1 ? TODO: better understanding here + * Use CHADEMO_seq to decide whether emitting 209 is necessary + * 0x0 1.0 and earlier + * 0x1 2.0 appendix A + * 0x2 2.0 appendix B + * TODO: is this influenced by x109->CHADEMO_protocol_number, or x102->ControlProtocolNumberEV ?? + * Unused for now. +uint8_t CHADEMO_seq = 0x0; + */ + + /*----------- CHARGING SUPPORT V2X --------------------------------------------------------------*/ + /* ---------- VEHICLE Data structures */ + + //H100 - Vehicle - Minimum charging expectations + //TODO decide whether default values for vehicle-origin frames is even appropriate + struct x100_Vehicle_Charging_Limits { + uint8_t MinimumChargeCurrent = 0; + uint16_t MinimumBatteryVoltage = 300; + uint16_t MaximumBatteryVoltage = 402; + uint8_t ConstantOfChargingRateIndication = 0; + }; + //H101 - Vehicle - Maximum charging expectations + struct x101_Vehicle_Charging_Estimate { + uint8_t MaxChargingTime10sBit = 0; + uint8_t MaxChargingTime1minBit = 0; + uint8_t EstimatedChargingTime = 0; + uint16_t RatedBatteryCapacity = 0; + }; + + //H102 - Vehicle - Charging targets and Status + // peer to x109 from EVSE + // termination triggers in both + // TODO see also Table A.26—Charge control termination command patterns + struct x102_Vehicle_Charging_Session { //Frame byte + uint8_t ControlProtocolNumberEV = 0; // 0 + uint16_t TargetBatteryVoltage = 0; // 1-2 + uint8_t ChargingCurrentRequest = 0; // 3 Note: per spec, units for this changed from kWh --> % + + union { + uint8_t faults; + struct { + bool unused_3 : 1; + bool unused_2 : 1; + bool unused_1 : 1; + bool FaultBatteryVoltageDeviation : 1; // 4 + bool FaultHighBatteryTemperature : 1; // 3 + bool FaultBatteryCurrentDeviation : 1; // 2 + bool FaultBatteryUnderVoltage : 1; // 1 + bool FaultBatteryOverVoltage : 1; // 0 + } fault; + } f; + + union { + uint8_t packed; + struct { + bool StatusVehicleDischargeCompatible : 1; //5.7 + bool unused_2 : 1; //5.6 + bool unused_1 : 1; //5.5 + bool StatusNormalStopRequest : 1; //5.4 + bool StatusVehicle : 1; //5.3 + bool StatusChargingError : 1; //5.2 + bool StatusVehicleShifterPosition : 1; //5.1 + bool StatusVehicleChargingEnabled : 1; //5.0 - bit zero is TODO. Vehicle charging enabled ==1 *AND* charge + // permission signal k needs to be active for charging to be + // permitted -- TODO document bits per byte for these flags + // and update variables to be more appropriate + } status; + } s; + + uint8_t StateOfCharge = 0; //6 state of charge? + }; + + /* ---------- CHARGING: EVSE Data structures */ + struct x108_EVSE_Capabilities { // Frame byte + bool contactor_weld_detection = 1; // 0 + uint16_t available_output_voltage = MAX_EVSE_OUTPUT_VOLTAGE; // 1,2 + uint8_t available_output_current = MAX_EVSE_OUTPUT_CURRENT; // 3 + uint16_t threshold_voltage = 297; // 4,5 voltage that EVSE will stop if car fails to + // perhaps vehicle minus 3%, hardcoded initially to 96*2.95 + // 6,7 = unused + }; + + /* Does double duty for charging and discharging */ + struct x109_EVSE_Status { // Frame byte + uint8_t CHADEMO_protocol_number = 0x02; // 0 + uint16_t setpoint_HV_VDC = + 0; // 1,2 NOTE: charger_setpoint_HV_VDC elsewhere is a float. THIS is protocol-defined as an int. cast float->int and lose some precision for protocol adherence + uint8_t setpoint_HV_IDC = 0; // 3 + // + bool discharge_compatible = true; // 4, bit 0. bits + // 4, bit 7-6 (?) unused. spec typo? maybe 1-7 unused + union { + uint8_t packed; + struct { + bool EVSE_status : 1; // 5, bit 0 + bool EVSE_error : 1; // 5, bit 1 + bool connector_locked : 1; // 5, bit 2 //NOTE: treated as connector_lock during discharge, but + // seen as 'energizing' during charging mode + + bool battery_incompatible : 1; // 5, bit 3 + bool ChgDischError : 1; // 5, bit 4 + + bool ChgDischStopControl : 1; // 5, bit 5 - set to false for initialization to indicate 'preparing to charge' + // set to false when ready to charge/discharge + + } status; + } s; + + // Either, or; not both. + // seconds field set to 0xFF by default + // indicating only the minutes field is used instead + // BOTH observed initially set to 0xFF in logs, so use + // that as the initialzed value + uint8_t remaining_time_10s = 0xFF; // 6 + uint8_t remaining_time_1m = 0xFF; // 7 + }; + + /*----------- DISCHARGING SUPPORT V2X --------------------------------------------------------------*/ + /* ---------- VEHICLE Data structures */ + //H200 - Vehicle - Discharge limits + struct x200_Vehicle_Discharge_Limits { + uint8_t MaximumDischargeCurrent = 0xFF; + uint16_t MinimumDischargeVoltage = 0; + uint16_t MinimumBatteryDischargeLevel = 0; + uint16_t MaxRemainingCapacityForCharging = 0; + }; + + /* TODO When charge/discharge sequence control number (ID201/209) is not received, the vehicle or the EVSE +should determine that the other is the EVSE or the vehicle of the model before the V2H guideline 1.1. */ + //H201 - Vehicle - Estimated capacity available + // Intended primarily for display purposes. + // Peer to H209 + // NOTE: in available CAN logs from a Leaf, 209 is sent with no 201 reply, so < 1.1 must be the inferred version + struct x201_Vehicle_Discharge_Estimate { + uint8_t V2HchargeDischargeSequenceNum = 0; + uint16_t ApproxDischargeCompletionTime = 0; + uint16_t AvailableVehicleEnergy = 0; + }; + + /* ---------- EVSE Data structures */ + struct x208_EVSE_Discharge_Capability { // Frame byte + uint8_t present_discharge_current = 0xFF; // 0 + uint16_t available_input_voltage = 500; // 1,2 -- poorly named as both 'available' and minimum input voltage + uint16_t available_input_current = 250; // 3 -- poorly named as both 'available' and maximum input current + // spec idiosyncracy in naming/description + // 4,5 = unused + uint16_t lower_threshold_voltage = 0; // 6,7 + }; + + // H209 - EVSE - Estimated Discharge Duration + // peer to Vehicle's 201 event (Note: 209 seen + // in CAN logs even when 201 is not) + struct x209_EVSE_Discharge_Estimate { // Frame byte + uint8_t sequence_control_number = 0x2; // 0 + uint16_t remaining_discharge_time = 0x0000; // 0x0000 == unused + }; + + /*----------- DYNAMIC CONTROL SUPPORT --------------------------------------------------------------*/ + /* ---------- VEHICLE Data structures */ + struct x110_Vehicle_Dynamic_Control { //Frame byte + union { + uint8_t packed; + struct { + bool PermissionResetMaxChgTime : 1; // bit 5 or 6? is this only x118 not x110? + bool unused_3 : 1; + bool unused_2 : 1; + bool unused_1 : 1; + bool HighVoltageControlStatus : 1; // bit 2 = High voltage control support + bool HighCurrentControlStatus : 1; // bit 1 = High current control support + // rate of change is -20A/s to 20A/s relative to 102.3 + bool DynamicControlStatus : 1; // bit 0 = Dynamic Control support + } status; + } u; + }; + + /* ---------- EVSE Data structures */ + // TODO 118 + //H118 + //see also table a.59 page 104 IEEE + struct x118_EVSE_Dynamic_Control { // Frame byte + union { + uint8_t packed; + struct { + bool PermissionResetMaxChgTime : 1; // bit 5 or 6? + bool unused_3 : 1; + bool unused_2 : 1; + bool unused_1 : 1; + bool HighVoltageControlStatus : 1; // bit 2 = High voltage control support + bool HighCurrentControlStatus : 1; // bit 1 = High current control support + // rate of change is -20A/s to 20A/s relative to 102.3 + bool DynamicControlStatus : 1; // bit 0 = Dynamic Control support + } status; + } u; + }; + + /*----------- MANUFACTURER ID SUPPORT --------------------------------------------------------------*/ + /* ---------- VEHICLE Data structures */ + //H700 - Vehicle - Manufacturer identification + //Peer to H708 + //Used to adapt to manufacturer-prescribed optional specification + struct x700_Vehicle_Vendor_ID { + uint8_t AutomakerCode = 0; // 0 = set to 0x0 to indicate incompatibility. Best as a starting place + uint8_t OptionalContent = 0; // 1-7, variable per vendor spec + }; + + unsigned long setupMillis = 0; + unsigned long handlerBeforeMillis = 0; + unsigned long handlerAfterMillis = 0; + + /* Do not change code below unless you are sure what you are doing */ + unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send + unsigned long previousMillis5000 = 0; // will store last time a 5s threshold was reached for display during debug + + bool plug_inserted = false; + bool vehicle_can_initialized = false; + bool vehicle_can_received = false; + bool vehicle_permission = false; + bool evse_permission = false; + + bool precharge_low = false; + bool positive_high = false; + bool contactors_ready = false; + + uint8_t framecount = 0; + + uint8_t max_discharge_current = 0; //TODO not sure on this one, but really influenced by inverter capability + + bool high_current_control_enabled = false; // set to true when high current control is operating + // if true, values from 110.1 and 110.2 should be used instead of 102.3 + // and 118 should be used for evse responses + // permissible rate of change is -20A/s to 20A/s relative to 102.3 + + Mode EVSE_mode = CHADEMO_DISCHARGE; + CHADEMO_STATE CHADEMO_Status = CHADEMO_IDLE; + + /* Charge/discharge sequence, indicating applicable V2H guideline + * If sequence number is not agreed upon via H201/H209 between EVSE and Vehicle, + * V2H 1.1 is assumed + * Use CHADEMO_seq to decide whether emitting 209 is necessary + * 0x0 1.0 and earlier + * 0x1 2.0 appendix A + * 0x2 2.0 appendix B + * Unused for now. +uint8_t CHADEMO_seq = 0x0; + */ + + bool x201_received = false; + bool x209_sent = false; + + struct x100_Vehicle_Charging_Limits x100_chg_lim = {}; + struct x101_Vehicle_Charging_Estimate x101_chg_est = {}; + struct x102_Vehicle_Charging_Session x102_chg_session = {}; + struct x110_Vehicle_Dynamic_Control x110_vehicle_dyn = {}; + struct x200_Vehicle_Discharge_Limits x200_discharge_limits = {}; + struct x201_Vehicle_Discharge_Estimate x201_discharge_estimate = {}; + struct x700_Vehicle_Vendor_ID x700_vendor_id = {}; + + struct x209_EVSE_Discharge_Estimate x209_evse_dischg_est; + struct x108_EVSE_Capabilities x108_evse_cap; + struct x109_EVSE_Status x109_evse_state; + struct x118_EVSE_Dynamic_Control x118_evse_dyn; + struct x208_EVSE_Discharge_Capability x208_evse_dischg_cap; + + CAN_frame CHADEMO_108 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x108, + .data = {0x01, 0xF4, 0x01, 0x0F, 0xB3, 0x01, 0x00, 0x00}}; + CAN_frame CHADEMO_109 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x109, + .data = {0x02, 0x00, 0x00, 0x00, 0x01, 0x20, 0xFF, 0xFF}}; + //For chademo v2.0 only + CAN_frame CHADEMO_118 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x118, + .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + + // OLD value from skeleton implementation, indicates dynamic control is possible. + // Hardcode above as being incompatible for simplicity in current incarnation. + // .data = {0x10, 0x64, 0x00, 0xB0, 0x00, 0x1E, 0x00, 0x8F}}; + + // 0x200 : From vehicle-side. A V2X-ready vehicle will send this message to broadcast its “Maximum discharger current”. (It is a similar logic to the limits set in 0x100 or 0x102 during a DC charging session) + // 0x208 : From EVSE-side. A V2X EVSE will use this to send the “present discharger current” during the session, and the “available input current”. (uses similar logic to 0x108 and 0x109 during a DC charging session) + CAN_frame CHADEMO_208 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x208, + .data = {0xFF, 0xF4, 0x01, 0xF0, 0x00, 0x00, 0xFA, 0x00}}; + CAN_frame CHADEMO_209 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x209, + .data = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +}; #endif diff --git a/Software/src/battery/CHADEMO-SHUNTS.cpp b/Software/src/battery/CHADEMO-SHUNTS.cpp index 7b8bbb74..1f336e4c 100644 --- a/Software/src/battery/CHADEMO-SHUNTS.cpp +++ b/Software/src/battery/CHADEMO-SHUNTS.cpp @@ -22,7 +22,6 @@ #ifdef CHADEMO_BATTERY #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "CHADEMO-BATTERY-INTERNAL.h" #include "CHADEMO-BATTERY.h" #include "CHADEMO-SHUNTS.h"