Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add files via upload #109

Closed
wants to merge 10 commits into from
Closed
135 changes: 135 additions & 0 deletions firmware/open_evse/J1772EvseController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,19 @@ void J1772EVSEController::Init()
#ifdef RAPI
RapiSendBootNotification();
#endif

#ifdef HEARTBEAT_SUPERVISION
//Grab the EEPROM setpoints and see if they are legitimate
m_HsInterval = eeprom_read_word((uint16_t*)EOFS_HEARTBEAT_SUPERVISION_INTERVAL);
m_IFallback = eeprom_read_byte((uint8_t*)EOFS_HEARTBEAT_SUPERVISION_CURRENT);
//Legit check:
if (m_HsInterval == 0xffff) { //EEPROM not initialized, let's just load the default values eh? <== CANADA
m_HsInterval = HS_INTERVAL_DEFAULT;
m_IFallback = HS_IFALLBACK_DEFAULT;
}
m_HsLastPulse = millis(); //Set up the HS pulse interval start time as "now"
#endif //HEARTBEAT_SUPERVISION

}

void J1772EVSEController::ReadPilot(uint16_t *plow,uint16_t *phigh)
Expand Down Expand Up @@ -1751,6 +1764,11 @@ if (TempChkEnabled()) {
}
}
#endif // TEMPERATURE_MONITORING

#ifdef HEARTBEAT_SUPERVISION
HsExpirationCheck(); //Check to see if HS is engaged, and if so whether we missed a pulse
#endif //HEARTBEAT_SUPERVISION

#ifdef CHARGE_LIMIT
if (m_chargeLimitTotWs && (g_EnergyMeter.GetSessionWs() >= m_chargeLimitTotWs)) {
ClrChargeLimit(); // clear charge limit
Expand Down Expand Up @@ -1878,6 +1896,123 @@ int J1772EVSEController::SetCurrentCapacity(uint8_t amps,uint8_t updatelcd,uint8
return rc;
}

#ifdef HEARTBEAT_SUPERVISION
int J1772EVSEController::HeartbeatSupervision(uint16_t interval, uint8_t amps)
{
m_HsInterval = interval;
m_IFallback = amps;
m_HsTriggered = 0; //We clear the "Triggered" flag whenever we initiate HEARTBEAT_SUPERVISION
m_HsLastPulse = millis(); //RESET m_HsLastPulse if it was set to 0 as a flag of previous HEARTBEAT_SUPERVISION enforcement
if (eeprom_read_word((uint16_t*)EOFS_HEARTBEAT_SUPERVISION_INTERVAL) != m_HsInterval) { //only write EEPROM if it is needful!
eeprom_write_word((uint16_t*)EOFS_HEARTBEAT_SUPERVISION_INTERVAL, m_HsInterval);
}
if (eeprom_read_byte((uint8_t*)EOFS_HEARTBEAT_SUPERVISION_CURRENT) != m_IFallback) { //only write EEPROM if it is needful!
eeprom_write_byte((uint8_t*)EOFS_HEARTBEAT_SUPERVISION_CURRENT, amps);
}
return 0; // No error codes yet
}

int J1772EVSEController::HsPulse()
{
int rc = 1;
if ((m_HsTriggered = HS_MISSEDPULSE_NOACK)) { //We were in a state of missed pulse therefore we need to restore the current capacity since we now see a Pulse
rc = 1; //We have been triggered but have not been acknowledged responce with NK
}
else { // If we have been triggered it has been dealt with (m_HsTriggered = HS_MISSEDPULSE or 0)
rc = 0;
}
m_HsLastPulse = millis(); //We just had a heartbeat so reset the HEARTBEAT SUPERVISION timeout interval;
return rc;
}

int J1772EVSEController::HsRestoreAmpacity()
{
UNION4B u1,u2,u3,u4;
int rc=1;
if(m_HsTriggered) {//At some point while active, HEARTBEAT_SUPERVISION was triggered, so we will have to perturb the ampacity
u3.u8 = g_EvseController.GetCurrentCapacity(); //We get the ceiling for how high we can set ampacity
#ifdef TEMPERATURE_MONITORING
if (!g_TempMonitor.OverTemperature()) { //We need to ensure that OverTemperature is not active before we raise current capacity
rc = g_EvseController.SetCurrentCapacity(u3.u8,1,0); //We are not writing EEPROM, but we are setting current to maximum capacity
}
else {
rc = 1; //Fail. Cannnot restore ampacity, as TEMPERATURE_MONITORING OverTemperature() is still in force
}
#else // !TEMPERATURE_MONITORING
rc = g_EvseController.SetCurrentCapacity(u3.u8,1,0);
#endif // TEMPERATURE_MONITORING
}
return rc;
}

int J1772EVSEController::HsExpirationCheck()
{
unsigned long sinceLastPulse = (millis() - m_HsLastPulse);
int rc=1;
if (m_HsInterval != 0) { //HEARTBEAT_SUPERVISION is currently active
if((m_HsTriggered = HS_MISSEDPULSE_NOACK)) { //There has been a pulse miss that has not been acknowledged
if(!(m_IFallback > GetCurrentCapacity())) { //We are still in HEARTBEAT_SUPERVISION ampacity limiting but the current capacity is not OK
rc=SetCurrentCapacity(m_IFallback,0,0); //Drop the current, but do not update the display and do not write it to EEPROM
}
}
else if (sinceLastPulse > m_HsInterval) {//Whups, we didn't get a heartbeat within the specified time interval, and HS is in active state.
//Something went wrong, and as a result we may have to perturb the system ampacity setting.
//We flag that here by setting m_HsTriggered = HS_MISSEDPULSE_NOACK
m_HsTriggered = HS_MISSEDPULSE_NOACK; //Flag the fact that HEARTBEAT_SUPERVISION had a missed pulse and report same when pulsed, until acknowledged
if (m_IFallback < GetCurrentCapacity()) { //Check to see if we need to drop ampacity
rc=SetCurrentCapacity(m_IFallback,0,0); //Drop the current, but do not update the display and do not write it to EEPROM
}
}
else { //Do nothing
}

m_HsLastPulse = millis(); //In all cases, reset the timer so we don't check again until the next interval
}
else {
rc = 0; //HEARTBEAT_SUPERVISION inactive, return normally
}
return rc;
}

int J1772EVSEController::HsAckMissedPulse(uint8_t ack)
{
int rc = 1;
if (ack == HS_ACK_COOKIE) { //Is this a legitimate acknowlegement of missed HEARTBEAT_SUPERVISION pulse?
if(m_HsTriggered == HS_MISSEDPULSE_NOACK) {// Has HEARTBEAT_SUPERVISION missed a pulse with no subsequenr acknowledgement?
rc = HsRestoreAmpacity(); // Let's make an attempt to restore ampacity to original conditions
if (rc == 0) {
m_HsTriggered = HS_MISSEDPULSE; // Ampacity restoration was successful, leave a semi-permanent record that a pulse was missed and ampacity was perturbed
} //else: Ampacity could not be restored at this time. HsAckMissedPulse() unsuccessful.
}
}
return rc;
}

int J1772EVSEController::HsDeactivate()
{
int rc=1;
m_HsInterval = 0;
rc=(int)m_HsInterval;
return rc;
}

int J1772EVSEController::GetHearbeatInterval()
{
return (int)m_HsInterval;
}

int J1772EVSEController::GetHearbeatCurrent()
{
return (int)m_IFallback;
}

int J1772EVSEController::GetHearbeatTrigger()
{
return (int)m_HsTriggered;
}

#endif //HEARTBEAT_SUPERVISION

#if defined(GFI) || defined(ADVPWR)
unsigned long J1772EVSEController::GetResetMs()
{
Expand Down
26 changes: 26 additions & 0 deletions firmware/open_evse/J1772EvseController.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ typedef uint8_t (*EvseStateTransitionReqFunc)(uint8_t prevPilotState,uint8_t cur
#define ECVF_DEFAULT ECVF_SESSION_ENDED
#endif

#define HS_INTERVAL_DEFAULT 0x0000 //By default, on an unformatted EEPROM, Heartbeat Supervision is not activated
#define HS_IFALLBACK_DEFAULT 0x00 //By default, on an unformatted EEPROM, HS fallback current is 0 Amperes
#define HS_ACK_COOKIE 0XA5 //ACK will not work unless it contin this cookie
#define HS_MISSEDPULSE_NOACK 0x02 //HEARTBEAT_SUPERVISION missed a pulse and this has not been acknowleged
#define HS_MISSEDPULSE 0x01 //HEARTBEAT_SUPERVISION missed a pulse and this is the semi-permanent record flag


class J1772EVSEController {
J1772Pilot m_Pilot;
Expand Down Expand Up @@ -239,6 +245,13 @@ class J1772EVSEController {
uint32_t m_Voltage; // mV
#endif // VOLTMETER

#ifdef HEARTBEAT_SUPERVISION
uint16_t m_HsInterval; // Number of seconds HS will wait for a heartbeat before reducing ampacity to m_IFallback. If 0 disable.
uint8_t m_IFallback; // HEARTBEAT_SUPERVISION fallback current in Amperes.
uint8_t m_HsTriggered; // Will be 0 if HEARTBEAT_SUPERVISION has never had a missed pulse
unsigned long m_HsLastPulse; // The last time we saw a HS pulse or the last time m_HsInterval elpased without seeing one. Set to 0 if HEARTBEAT_SUPERVISION triggered
#endif //HEARTBEAT_SUPERVISION

public:
J1772EVSEController();
void Init();
Expand Down Expand Up @@ -374,6 +387,19 @@ class J1772EVSEController {
void EnableTempChk(uint8_t tf);
#endif //TEMPERATURE_MONITORING

#ifdef HEARTBEAT_SUPERVISION
int HeartbeatSupervision(uint16_t interval, uint8_t amps);
int HsPulse();
int HsRestoreAmpacity();
int HsExpirationCheck();
int HsAckMissedPulse(uint8_t ack);
int HsDeactivate();
int GetHearbeatInterval();
int GetHearbeatCurrent();
int GetHearbeatTrigger();
#endif //HEARTBEAT_SUPERVISION


uint8_t SerDbgEnabled() {
return (m_wFlags & ECF_SERIAL_DBG) ? 1 : 0;
}
Expand Down
10 changes: 9 additions & 1 deletion firmware/open_evse/open_evse.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ extern AutoCurrentCapacityController g_ACCController;
#define TEMPERATURE_MONITORING // Temperature monitoring support
// not yet #define TEMPERATURE_MONITORING_NY

#define HEARTBEAT_SUPERVISION // Heartbeat Supervision support

#ifdef AMMETER

// if OVERCURRENT_THRESHOLD is defined, then EVSE will hard fault in
Expand Down Expand Up @@ -537,6 +539,13 @@ extern AutoCurrentCapacityController g_ACCController;
// non-volatile flags
#define EOFS_DUO_NVFLAGS 32 // 1 byte
#define EOFS_DUO_SHARED_AMPS 33 // 1 byte
//
// Reserved for HEARTBEAT_SUPERVISION (3 Bytes)
//
// Duration in seconds:
#define EOFS_HEARTBEAT_SUPERVISION_INTERVAL 34 // 2 bytes (zero if infinite)
// Fallback Current in quarter Amperes:
#define EOFS_HEARTBEAT_SUPERVISION_CURRENT 36 // 1 byte

//- start TESTING ONLY
#ifdef RELAY_AUTO_PWM_PIN_TESTING
Expand Down Expand Up @@ -1394,4 +1403,3 @@ void wdt_delay(uint32_t ms);

#include "strings.h"
#include "rapi_proc.h"

2 changes: 2 additions & 0 deletions firmware/open_evse/open_evse.ino
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* portions Copyright (c) 2014-2015 Nick Sayer <[email protected]>
* portions Copyright (c) 2015 Craig Kirkpatrick
* portions Copyright (c) 2015 William McBrine
* portions Copyright (c) 2019 Tim Kuechler BowElectric at gmail

Revised Ver By Reason
6/21/13 20b3 Scott Rubin fixed LCD display bugs with RTC enabled
Expand All @@ -21,6 +22,7 @@
10/25/14 Craig K add smoothing to the Amperage readout
3/1/15 Craig K add TEMPERATURE_MONITORING
3/7/15 Craig K add KWH_RECORDING
10/28/2019 Tim Kuechler add HEARTBEAT_SUPERVISION

* This file is part of Open EVSE.

Expand Down
36 changes: 36 additions & 0 deletions firmware/open_evse/rapi_proc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,34 @@ int EvseRapiProcessor::processCmd()
}
break;
#endif // DELAYTIMER

#ifdef HEARTBEAT_SUPERVISION
case 'Y': // HEARTBEAT SUPERVISION
if (tokenCnt == 1) { //This is a heartbeat
rc = g_EvseController.HsPulse(); //pet the dog
}
else if (tokenCnt == 3) { //This is a full HEARTBEAT_SUPERVISION setpoint command with both parameters
u2.u16 = (uint16_t)dtou32(tokens[2]); // HS Interval in seconds. 0 = disabled
u1.u8 = (uint8_t)dtou32(tokens[1]); // HS fallback current, in amperes
if (u1.u16 != 0) { //Set-up new heartbeat supervision parameters
rc = g_EvseController.HeartbeatSupervision(u1.u16, u2.u8);
}
else { // This is the case where we are disabling HEARTBEAT_SUPERVISION
rc = (g_EvseController.HsDeactivate() || g_EvseController.HsRestoreAmpacity());
}
}
else if (tokenCnt == 2) { //This is a command to ack a hearbtbeat supervison miss
u1.u8 = (uint8_t)dtou32(tokens[1]); //Magic cookie
g_EvseController.HsAckMissedPulse(u1.u8);
}
else { //Invalid number of tokens, return 1
rc = 1; //Invalid number of tokens
}
sprintf(buffer,"%d %d %d", g_EvseController.GetHearbeatInterval(), g_EvseController.GetHearbeatCurrent(), g_EvseController.GetHearbeatCurrent());
bufCnt = 1;
break;
#endif //HEARTBEAT_SUPERVISION

}
break;

Expand Down Expand Up @@ -665,6 +693,14 @@ int EvseRapiProcessor::processCmd()
bufCnt = 1; // flag response text output
rc = 0;
break;

#ifdef HEARTBEAT_SUPERVISION
case 'Y': // HEARTBEAT SUPERVISION
sprintf(buffer,"%d %d %d", g_EvseController.GetHearbeatInterval(), g_EvseController.GetHearbeatCurrent(), g_EvseController.GetHearbeatCurrent());
bufCnt = 1;
break;
#endif //HEARTBEAT_SUPERVISION

}
break;

Expand Down
16 changes: 16 additions & 0 deletions firmware/open_evse/rapi_proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,15 @@ SL 1|2|A - set service level L1/L2/Auto
SM voltscalefactor voltoffset - set voltMeter settings
ST starthr startmin endhr endmin - set timer
$ST 0 0 0 0^23 - cancel timer
SY heartbeatinterval hearbeatcurrentlimit
Response includes heartbeatinterval hearbeatcurrentlimit hearbeattrigger
hearbeattrigger: 0 - There has never been a missed pulse,
2 - there is a missed pulse, and HS is still in current limit
1 - There was a missed pulse once, but it has since been acknkoledged. Ampacity has been successfully restored to max permitted
$SY 100 6 //If no pulse for 100 seconds, set EVE ampacity limit to 6A until missed pulse is acknowledged
$SY //This is a heartbeat supervision pulse. Need one every heartbeatinterval seconds.
$SY 165 //This is an acknowledgemnt of a missed pulse. Magic Cookie = 165 (=0XA5)
When you send a pulse, an NK response indicates that a previous pulse was missed and has not yet been acked

G0 - get EV connect state
response: $OK connectstate
Expand Down Expand Up @@ -286,6 +295,13 @@ T commands for debugging only #define RAPI_T_COMMMANDS
T0 amps - set fake charging current
response: $OK
$T0 75

GY - Get Hearbeat Supervision Status
Response includes heartbeatinterval hearbeatcurrentlimit hearbeattrigger
hearbeattrigger: 0 - There has never been a missed pulse,
2 - there is a missed pulse, and HS is still in current limit
1 - There was a missed pulse once, but it has since been acknkoledged. Ampacity has been successfully restored to max permitted
See SY above for worked expamples.

Z0 FOR TESTING RELAY_AUTO_PWM_PIN ONLY
Z0 closems holdpwm
Expand Down
4 changes: 2 additions & 2 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
; http://docs.platformio.org/en/stable/projectconf.html

[platformio]
env_default = openevse
default_envs = openevse
src_dir = firmware/open_evse

[common]
lib_deps =

[env:openevse]
platform = atmelavr
board = OpenEVSE
board = openevse
framework = arduino
lib_deps = ${common.lib_deps}
upload_protocol = usbasp
Expand Down