Skip to content

Commit

Permalink
Merge branch 'june-2.4.1-gfi-retry' into june-2.4.1-rtc-autocalib
Browse files Browse the repository at this point in the history
  • Loading branch information
dlyubimov committed Jul 10, 2017
2 parents 6882bc8 + 3d93c7c commit b65e52b
Show file tree
Hide file tree
Showing 4 changed files with 1,455 additions and 1,446 deletions.
16 changes: 13 additions & 3 deletions Hydra_EVSE/Hydra_EVSE.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@

#endif


#ifdef QUICK_CYCLING_WORKAROUND

// How many minutes do we wait after one car finishes before raising the other pilot?
Expand Down Expand Up @@ -130,6 +131,7 @@
#define STATUS_ERR_MASK ( -1u >> UINT_BITS - 3 << 6)
#define STATUS_ERR_F ( 0x0u << 6 )
#define STATUS_ERR_O ( 0x1u << 6 )
// gfi
#define STATUS_ERR_G ( 0x2u << 6 )
#define STATUS_ERR_T ( 0x3u << 6 )
#define STATUS_ERR_R ( 0x4u << 6 )
Expand All @@ -151,6 +153,14 @@
#define GFI_TEST_CLEAR_TIME 100 // Takes the GFCI this long to clear
#define GFI_TEST_DEBOUNCE_TIME 400 // Delay extra time after GFCI clears to make sure it stays.

//After each GFCI event we will retry charging up to 4 times after a 15 minute
// delay per event. (UL 2231)
#define GFI_CLEAR_MS (15 * 60)
// debug
//#define GFI_CLEAR_MS (1 * 60)
#define GFI_CLEAR_ATTEMPTS 4


// These are the expected analogRead() ranges for pilot read-back from the cars.
// These are calculated from the expected voltages seen through the dividor network,
// then scaling those voltages for 0-1024.
Expand Down Expand Up @@ -344,16 +354,15 @@ extern char p_buffer[];
struct timeouts_struct {
unsigned long sequential_pilot_timeout;
unsigned long button_press_time, button_debounce_time;
volatile unsigned long relay_change_time;
// last gfi time
unsigned long gfi_time;

timeout_struct() {
clear();
}

void clear() {
memset(this, 0, sizeof(*this));
// clearing volatile again to make sure proper value write
relay_change_time = 0;
}
};

Expand Down Expand Up @@ -415,6 +424,7 @@ struct car_struct {
};

#define EVENT_COUNT 4

typedef struct event_struct {
unsigned char hour;
unsigned char minute;
Expand Down
113 changes: 51 additions & 62 deletions Hydra_EVSE/Hydra_EVSE.ino
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ unsigned char currentMenuChoices[] = { 12, 16, 20, 24, 28, 30, 32, 36, 40, 44, 5
#define LAST_YEAR 2020


// Time zone rules.
// Time zone rules.
// Use US_DST_RULES macro for US, EU_... for EU, and AU_... for Australia. Uncomment only one of
// those as applicabble.
US_DST_RULES(dstRules);
Expand Down Expand Up @@ -97,6 +97,13 @@ car_struct cars[] =
car_struct &car_a(cars[0]), &car_b(cars[1]);
timeouts_struct timeouts;

// This is special -- we cannot frivolously clear it with the other timeouts, or we may see spurious err R.
volatile unsigned long relay_change_time;

// We cannot make it part of timeouts since errors clear all pending timeouts.
unsigned char gfi_count;


unsigned long last_state_log;
unsigned char &operatingMode(persisted.operatingMode), sequential_mode_tiebreak;
#ifdef GROUND_TEST
Expand Down Expand Up @@ -429,8 +436,17 @@ void error(unsigned int status)
// (that is, turn off the oscillator) whenever we want.
// Stop flipping, one way or another
unsigned int car = status & CAR_MASK;
timeouts.sequential_pilot_timeout = 0;
if (car == BOTH || car == CAR_A)

// Cancel all pending events except for the relay test protection
// timeouts.sequential_pilot_timeout = 0;
timeouts.clear();

// kick off gfi retry timer (if under the allowed number of attempts).
if ( (status & STATUS_MASK) == STATUS_ERR_G && gfi_count++ < GFI_CLEAR_ATTEMPTS) {
timeouts.gfi_time = millis();
}

if ( car == BOTH || car == CAR_A)
{
car_a.setPilot(HIGH);
if (car_a.last_state != STATE_E)
Expand Down Expand Up @@ -462,7 +478,7 @@ void gfi_trigger()
digitalWrite(CAR_B_RELAY, LOW);
// Now make the data consistent. Make sure that anything you touch here is declared "volatile"
car_a.relay_state = car_b.relay_state = LOW;
timeouts.relay_change_time = millis();
relay_change_time = millis();
// We don't have time in an IRQ to do more than that.
gfiTriggered = true;
}
Expand All @@ -478,20 +494,8 @@ void car_struct::setRelay(unsigned int state)
if (relay_state == state ) return;
digitalWrite(relay_pin, state);
relay_state = state;
// switch (us) {
// case CAR_A:
// if (relay_state_a == state) return; // did nothing.
// digitalWrite(CAR_A_RELAY, state);
// relay_state_a = state;
// break;
// case CAR_B:
// if (relay_state_b == state) return; // did nothing.
// digitalWrite(CAR_B_RELAY, state);
// relay_state_b = state;
// break;
// }
// This only counts if we actually changed anything.
timeouts.relay_change_time = millis();
relay_change_time = millis();
}

// If it's in an error state, it's not charging (the relay may still be on during error delay).
Expand All @@ -500,20 +504,6 @@ void car_struct::setRelay(unsigned int state)
/*static inline*/ boolean car_struct::isCarCharging()
{
if (paused) return false;
// switch (car) {
// case CAR_A:
// if (last_car_a_state == STATE_E) return LOW;
// if (car_a_request_time != 0) return HIGH;
// return relay_state_a;
// break;
// case CAR_B:
// if (last_car_b_state == STATE_E) return LOW;
// if (car_b_request_time != 0) return HIGH;
// return relay_state_b;
// break;
// default:
// return LOW; // This should not be possible
// }
if (last_state == STATE_E) return LOW;
if (request_time != 0) return HIGH;
return relay_state;
Expand All @@ -530,19 +520,6 @@ void car_struct::setPilot(unsigned int which)
// set the outgoing pilot for the given car to either HALF state, FULL state, or HIGH.
// int pin;
char pilot_derate = car == CAR_A ? persisted.calib.pilot_a : persisted.calib.pilot_b;
// switch (car) {
// case CAR_A:
// pin = CAR_A_PILOT_OUT_PIN;
// pilot_state_a = which;
// pilot_derate = persisted.calib.pilot_a;
// break;
// case CAR_B:
// pin = CAR_B_PILOT_OUT_PIN;
// pilot_state_b = which;
// pilot_derate = persisted.calib.pilot_b;
// break;
// default: return;
// }
if (which == LOW || which == HIGH)
{
// This is what the pwm library does anyway.
Expand All @@ -569,11 +546,6 @@ void car_struct::setPilot(unsigned int which)
pilot_state = which;
}

//static inline unsigned int pilotState(unsigned int car)
//{
// return (car == CAR_A) ? car_a.pilot_state : car_b.pilot_state;
//}

int car_struct::checkState()
{
// poll the pilot state pin for 10 ms (should be 10 pilot cycles), looking for the low and high.
Expand Down Expand Up @@ -1762,6 +1734,8 @@ void setup()
car_b.setRelay(LOW);

timeouts.clear();
relay_change_time = 0;
gfi_count = 0;
last_minute = 99;
#ifdef QUICK_CYCLING_WORKAROUND
pilot_release_holdoff_time = 0;
Expand Down Expand Up @@ -1846,6 +1820,11 @@ void car_struct::loopCheckPilot(unsigned int car_state) {
// will take us back to state A.
last_state = DUNNO;
logInfo(P("Car %c disconnected, clearing error"), carLetter());
// timeouts.clear();

// clear gfi counts only if both cars are disconnected.
if (them.last_state == STATE_A) gfi_count = 0;

}
else
{
Expand Down Expand Up @@ -1890,6 +1869,10 @@ void car_struct::loopCheckPilot(unsigned int car_state) {
sequential_mode_transition(car_state);
break;
}
} else if ( ! paused && timeouts.gfi_time > 0 && timeouts.gfi_time + GFI_CLEAR_MS < millis()) {
// timeouts.clear();
// enable transition next iteration
for (int i = 0; i < 2; i++ ) cars[i].last_state = DUNNO;
}

}
Expand Down Expand Up @@ -1993,18 +1976,18 @@ void car_struct::loopCheckDelayedTransition() {
}
}

// This switches offer, sequential mode only, from a current car in mode B to the other car currently
// This switches offer, sequential mode only, from a current car in mode B to the other car currently
// also in mode B, based on offer timeout.
void car_struct::loopSeqHandover(unsigned long nowMs) {
{
logInfo(P("Sequential mode offer timeout, moving offer to %s"), car_str(CAR_B));
// move the pilot offer.
setPilot(HIGH);
them.setPilot(FULL);
timeouts.sequential_pilot_timeout = nowMs;
displayStatus(car | (seq_done ? STATUS_DONE : STATUS_WAIT));
displayStatus(them.car | STATUS_OFF );
}
{
logInfo(P("Sequential mode offer timeout, moving offer to %s"), car_str(CAR_B));
// move the pilot offer.
setPilot(HIGH);
them.setPilot(FULL);
timeouts.sequential_pilot_timeout = nowMs;
displayStatus(car | (seq_done ? STATUS_DONE : STATUS_WAIT));
displayStatus(them.car | STATUS_OFF );
}
}

///////////////////////////////////////////////////////////
Expand All @@ -2027,6 +2010,7 @@ void loop()
if (gfiTriggered)
{
logInfo(P("GFI fault detected"));
// timeouts.clear();
error(BOTH | STATUS_ERR | STATUS_ERR_G);
gfiTriggered = false;
}
Expand All @@ -2053,7 +2037,7 @@ void loop()
#endif

#ifdef RELAY_TEST
if (timeouts.relay_change_time == 0)
if (relay_change_time == 0)
{
// If the power's off but there's still a voltage, that's a stuck relay
if ((digitalRead(CAR_A_RELAY_TEST) == HIGH) && (car_a.relay_state == LOW))
Expand Down Expand Up @@ -2085,8 +2069,8 @@ void loop()
unsigned long incomingPilotMilliamps = 1000ul * persisted.max_amps;

// in this hardware version (2.3.1) we don't even have to do that, right? We don't induce the relay tests anymore.
if (timeouts.relay_change_time != 0 && millis() > timeouts.relay_change_time + RELAY_TEST_GRACE_TIME)
timeouts.relay_change_time = 0;
if (relay_change_time != 0 && millis() > relay_change_time + RELAY_TEST_GRACE_TIME)
relay_change_time = 0;

// Update the display
if (car_a.last_state == STATE_E || car_b.last_state == STATE_E)
Expand All @@ -2111,6 +2095,9 @@ void loop()
{
if (!paused)
{
// cancel all events except for relay check guarding period
timeouts.clear();

if (operatingMode == MODE_SEQUENTIAL)
{
// remember which car was active
Expand Down Expand Up @@ -2140,6 +2127,8 @@ void loop()
{
car_a.last_state = DUNNO;
car_b.last_state = DUNNO;
// clear all pending events.
timeouts.clear();
}
paused = false;
}
Expand Down
Loading

0 comments on commit b65e52b

Please sign in to comment.