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

Release 2.4.1. #4

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
560 changes: 560 additions & 0 deletions Hydra_EVSE/Hydra_EVSE.h

Large diffs are not rendered by default.

2,533 changes: 1,201 additions & 1,332 deletions Hydra_EVSE/Hydra_EVSE.ino

Large diffs are not rendered by default.

3,448 changes: 1,643 additions & 1,805 deletions Hydra_EVSE/Hydra_EVSE.ino.standard.hex

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions Hydra_EVSE/KUMAN.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Display and button compatibility layer for Hydra for KUMAN shield display
*/
#ifndef KUMAN_H_INCLUDED
#define KUMAN_H_INCLUDED

#include <LiquidCrystal.h>

#define BUTTON_RIGHT 0x04
#define BUTTON_UP 0x08
#define BUTTON_DOWN 0x10
#define BUTTON_LEFT 0x02
#define BUTTON_SELECT 0x01
#define BUTTON_PIN 0

#define KUMAN_ADDR 8,9,4,5,6,7

// To retain compatibility with LiquidTWI2 library.
class KUMAN: public LiquidCrystal {

public :

KUMAN () : LiquidCrystal(KUMAN_ADDR) { }

void setBacklight(uint8_t status) { }

uint8_t readButtons() {
int r = analogRead(BUTTON_PIN);

if ( r > 1000 ) return 0; // no button

// otherwise, work with ranges which roughly follow powers of 2.

if ( (r >>= 6) == 0) return BUTTON_RIGHT;
if ( (r >>= 1) == 0) return BUTTON_UP;
if ( (r >>= 1) == 0) return BUTTON_DOWN;
if ( (r >>= 1) == 0) return BUTTON_LEFT;
return BUTTON_SELECT;
}


void setMCPType(uint8_t mcptype) {}



};


#endif // KUMAN_H_INCLUDED
114 changes: 82 additions & 32 deletions Hydra_EVSE/ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,51 @@
Hydra EVSE version 2.3.5
Hydra EVSE version 2.4.3
========================

This release focuses on fixes and additions for the sequential mode.
* Bug fix: GFI fault count limit does not during pause mode
* GFI_TEST_DEBOUNCE_TIME 250 (down from 400ms)

* Lack of sequential mode tiebreaking when both cars plugged during paused period is fixed.
When both cars are plugged during paused period, and EVSE is then unpaused either manually or by
an event, then both cars stay in "wait" state.
Hydra EVSE version 2.4.2
========================

* Tie break is selected by either:
* last car charging before pause (if it wasn't unplugged during pause period) (same as before)
* last car plugged during paused period (new)
* there is no undetermined tiebreak state any more (new). On startup, default tiebreak is selected as car A.
* Added the fifth event. If we turn the EVSE manually during partial peak times in PGE, fifth event is needed/
configured to switch the station off again at partial peak to full peak transition.

* In the sequential mode, when cars are paused and both off, the current tiebreak status is indicated by an
'*' (asterisk) symbol next to the car status (off). The car marked by the asterisk will be first in
the charging sequence once hydra enters the unpaused mode.
* Bug fix: incorrect error F (CGM) registration on car A (sends to car B instead)

* Flipping the charging status.
* Bug fix: constant overflow for ERR G restart timeout causing restart not to work for timeouts > ~1 min

Previously, once one vehicle had transitioned from C or D state (charging) into B
(plugged but not charging), the pilot was immediately withdrawn from that vehicle indicating no allowed amperage
(pause) and then another car was given a full pilot, thus allowing it entering C or D immediately.
Hydra EVSE version 2.4.1
========================

That created problems when both cars are fully charged, since both of my cars would still keep starting short
charging sessions intermittently even that they are already charged, thus flipping contactors (which is pretty
loud), generating events, sending emails etc. every few seconds.
* WARNING: EEPROM format is incompatible with previous releases, this release will
reset all the eeprom settings

The version 2.3.1 firmware actually has a notion the flipping timeout (5 minutes) but that would only apply if the
fully charged car did not initiate charging session when offered again (not the case for both of my two cars).
* Unit test mode in a standard uno 3 board

To fix the high frequency sequential charging sessions with 2 cars, the implementation now tracks "done"
vs. "wait" statuses (also displayed during 2-car sequential charging). The "done" status indicates that the car
has completed at least one full charging session since it had been unpaused or plugged in.
* Display and pin assignments hardware abstractions

Thus, when a car is done charging, transition to another car in the "wait" status is still done immediately, whereas
transition to a car in the "done" status is subject to the same 5 minute flipping interval regardless of whether
the current car did a charging session or not.
* A lot of redundant code and constants eliminated
* display duplication
* symmetrical code duplication
* timezone library is gone. Funcationality rewritten in more compact way
* the production versuib removes serial logging and its related strings completely

* eprom persitence and validation:
* save/load via one operation as the whole struct
* added eeprom "magic" signature to validation logic

* exponentially weighed average online symmarizer with irregular sampling to smooth out the current displays
(display only; internal analytic is seeing original raw values)

* experimental: learn and apply RTC calibration with a fast convergent model with embedded Bayesian treatment

* up to 4 attemtps to restart EVSE charging after a GFI fault separated by 15 minute pause. The
attempt count is reset by one of the following:
* EVSE restart
* EVSE enter into paused mode by short-pushing the button
* Unplugging _both_ cars thus clearing _both_ states into state A

This gives cars a chance to still commence longer charging sessions after they are completely charged (e.g., if
they are scheduled to condition in the morning), while eliminating overly frequent sequential flipping in case they
do initiate charging sessions even after having been fully charged.

* Firmware version is tracked separately from the hardware version. Current hardware release is stil 2.3.1 (as found
on the EVSE logic board) and is tested again such. Both versions are shown at the startup.

Hydra EVSE version 2.4.0
========================
Expand Down Expand Up @@ -78,3 +81,50 @@ Important:
----------

When flashing firmware 2.4.0 for the first time over previous versions **without erasing the EEPROM** make sure to check calibration menu settings to show all 0s after the first flash.

Hydra EVSE version 2.3.5
========================

This release focuses on fixes and additions for the sequential mode.

* Lack of sequential mode tiebreaking when both cars plugged during paused period is fixed.
When both cars are plugged during paused period, and EVSE is then unpaused either manually or by
an event, then both cars stay in "wait" state.

* Tie break is selected by either:
* last car charging before pause (if it wasn't unplugged during pause period) (same as before)
* last car plugged during paused period (new)
* there is no undetermined tiebreak state any more (new). On startup, default tiebreak is selected as car A.

* In the sequential mode, when cars are paused and both off, the current tiebreak status is indicated by an
'*' (asterisk) symbol next to the car status (off). The car marked by the asterisk will be first in
the charging sequence once hydra enters the unpaused mode.

* Flipping the charging status.

Previously, once one vehicle had transitioned from C or D state (charging) into B
(plugged but not charging), the pilot was immediately withdrawn from that vehicle indicating no allowed amperage
(pause) and then another car was given a full pilot, thus allowing it entering C or D immediately.

That created problems when both cars are fully charged, since both of my cars would still keep starting short
charging sessions intermittently even that they are already charged, thus flipping contactors (which is pretty
loud), generating events, sending emails etc. every few seconds.

The version 2.3.1 firmware actually has a notion the flipping timeout (5 minutes) but that would only apply if the
fully charged car did not initiate charging session when offered again (not the case for both of my two cars).

To fix the high frequency sequential charging sessions with 2 cars, the implementation now tracks "done"
vs. "wait" statuses (also displayed during 2-car sequential charging). The "done" status indicates that the car
has completed at least one full charging session since it had been unpaused or plugged in.

Thus, when a car is done charging, transition to another car in the "wait" status is still done immediately, whereas
transition to a car in the "done" status is subject to the same 5 minute flipping interval regardless of whether
the current car did a charging session or not.

This gives cars a chance to still commence longer charging sessions after they are completely charged (e.g., if
they are scheduled to condition in the morning), while eliminating overly frequent sequential flipping in case they
do initiate charging sessions even after having been fully charged.

* Firmware version is tracked separately from the hardware version. Current hardware release is stil 2.3.1 (as found
on the EVSE logic board) and is tested again such. Both versions are shown at the startup.

67 changes: 67 additions & 0 deletions Hydra_EVSE/dst.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*

J1772 Hydra (EVSE variant) for Arduino
Copyright 2014 Nicholas W. Sayer, Dmitriy Lyubimov

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "dst.h"


boolean isSummer(DSTRule* rules , time_t t) {

// The algorithm here is simple. in he current year,
// if we are past the second rule, then we take that rule.
// otherwise, if we are past the first rule, we take that rule.
// otherwise, we take second rule.
for (int i = 1; i >= 0; i--) {
if ( rules[i] <= t) return rules[i].dst == summer;
}
// if we got here, we are before the first rule, which means we have to assume
// it is a continuation of the 2nd rule ( last rule in calendar order) from the
// previous year.
return rules[1].dst == summer;
}

time_t toDST(DSTRule *rules, time_t t) {
return isSummer(rules, t) ? t + 3600 : t;

}

boolean DSTRule::operator<=(time_t that) {

uint8_t thatMo = month(that);

// try to break based on the month.
if ( mo < thatMo) return true;
else if ( mo > thatMo) return false;

// same month -- we will have to figure the day of week of the rule.
time_t ruleBound;
if ( week != Last)
// our week enum starts with 0, so we will just multiply that by the
// week duration, and then find the next closest day of week w.r.t. that:
ruleBound = nextDow (monthBegin(that) + SECS_PER_WEEK * week, dow) + hr * SECS_PER_HOUR;
else
// handle last day of week in a month: take the last day of the month
// (which is the first day of the next month), and
// find the previous closest day of week.
ruleBound = previousDow(nextMonthBegin(that) - SECS_PER_DAY, dow) + hr * SECS_PER_HOUR;

return ruleBound <= that;
}


92 changes: 92 additions & 0 deletions Hydra_EVSE/dst.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*

J1772 Hydra (EVSE variant) for Arduino
Copyright 2014 Nicholas W. Sayer, Dmitriy Lyubimov

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef ___DST_H___
#define ___DST_H___

// Standard Arduino types like boolean
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

#include <Time.h>

enum week_t {First, Second, Third, Fourth, Last};
enum dow_t {Sun = 1, Mon, Tue, Wed, Thu, Fri, Sat};
enum month_t {Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec};
enum dst_t {summer, winter};

//structure to describe rules for when daylight/summer time begins,
//or when standard time begins.
struct DSTRule
{
dst_t dst; // Whether the rule switches to summer or winter time
uint8_t week; // First, Second, Third, Fourth, or Last week of the month
uint8_t dow; // day of week, here and on per Time.h
uint8_t mo;
uint8_t hr;

boolean operator<=(time_t that);
boolean operator>(time_t that) { return ! (*this <= that); }
};
// Exactly 2 rules are expected for the rules argument, in calendar succession.
extern boolean isSummer(DSTRule* rules , time_t);
extern time_t toDST(DSTRule *rules, time_t);

#define sameWeekDow(_time_, _dow_) ((_time_ / SECS_PER_DAY + _dow_ - dayOfWeek(_time_)) * SECS_PER_DAY)

#define monthBegin(_time_) ((_time_ / SECS_PER_DAY + 1 - day(_time_)) * SECS_PER_DAY)

#define nextMonthBegin(_time_) monthBegin((_time_ / SECS_PER_DAY + 32 - day(_time_)) * SECS_PER_DAY)

// This returns next day of week w.r.t. current time. if today is the same day of week
// as the one requested, returns today's midnight.
inline time_t nextDow (time_t _time_, uint8_t _dow_) {
time_t sameWDow = sameWeekDow( _time_, _dow_);
return sameWDow >= previousMidnight(_time_) ? sameWDow : sameWDow + SECS_PER_WEEK;
}

// returns previous closest day of week to the _time_. If today is the same as _dow_, returns today's midnight.
inline time_t previousDow (time_t _time_, uint8_t _dow_) {
time_t sameWDow = sameWeekDow( _time_, _dow_);
return sameWDow <= previousMidnight(_time_) ? sameWDow : sameWDow - SECS_PER_WEEK;
}



// We don't really need to support timezones. We just want to perform automatic DST switching.
// The following are the U.S. DST rules. If you live elsewhere, the customize these. The params
// are a descriptive string (not used, but must be 5 chars or less), a "descriptor" for which
// weekday in the active month is in the rule (first, second... last), the day of the week,
// the month, and the hour of the day. The last parameter is the offset in minutes. You should
// have the "winter" rule have a 0 offset so that turning off DST returns you to winter time.
// The summer offset should be relative to winter time (probably +60 minutes).

// US
#define US_DST_RULES(name) DSTRule name[] = {{ summer, Second, Sun, Mar, 2 }, { winter, First, Sun, Nov, 2 }}
// Europe
#define EU_DST_RULES(name) DSTRule name[] = {{ summer, Last, Sun, Mar, 1 }, { winter, Last, Sun, Oct, 1 }}
// Australia - note the reversal due to the Southern hemisphere
#define AU_DST_RULES(name) DSTRule name[] = {{ winter, First, Sun, Apr, 2 }, { summer, First, Sun, Oct, 2 }}


#endif // ___DST_H___
Loading