mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 10:19:29 +02:00
Use base class for Chademo battery
This commit is contained in:
parent
6b21cc3b07
commit
764fc8cca9
4 changed files with 362 additions and 349 deletions
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue