Use base class for Chademo battery

This commit is contained in:
Jaakko Haakana 2025-05-19 20:43:54 +03:00
parent 6b21cc3b07
commit 764fc8cca9
4 changed files with 362 additions and 349 deletions

View file

@ -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

View file

@ -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);

View file

@ -2,8 +2,7 @@
#define CHADEMO_BATTERY_H
#include <Arduino.h>
#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

View file

@ -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"