From 4239d5d5910f9c54521fd114a304362bf3af18a9 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 29 May 2015 22:37:52 -0700 Subject: [PATCH 01/96] sync with stable --- flash.bat | 4 ++-- open_evse.ino | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/flash.bat b/flash.bat index c8b4b7db..584b2315 100644 --- a/flash.bat +++ b/flash.bat @@ -1,3 +1,3 @@ -avrdude -c USBasp -p m328p -U lfuse:w:0xFF:m -U hfuse:w:0xDE:m -U efuse:w:0x05:m -avrdude -c USBasp -p m328p -U flash:w:open_evse-378.hex +avrdude -c USBasp -p m328p -U lfuse:w:0xFF:m -U hfuse:w:0xDF:m -U efuse:w:0x05:m +avrdude -c USBasp -p m328p -U flash:w:open_evse-384.hex pause diff --git a/open_evse.ino b/open_evse.ino index 1bf5cabe..a076a449 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -1135,6 +1135,7 @@ void J1772EVSEController::ShowDisabledTests() g_OBD.LcdMsg_P(g_psDisabledTests,g_psVentReqChk); delay(SHOW_DISABLED_DELAY); } +#ifdef ADVPWR if (!GndChkEnabled()) { g_OBD.LcdMsg_P(g_psDisabledTests,g_psGndChk); delay(SHOW_DISABLED_DELAY); @@ -1143,10 +1144,13 @@ void J1772EVSEController::ShowDisabledTests() g_OBD.LcdMsg_P(g_psDisabledTests,g_psRlyChk); delay(SHOW_DISABLED_DELAY); } +#endif // ADVPWR +#ifdef GFI_SELFTEST if (!GfiSelfTestEnabled()) { g_OBD.LcdMsg_P(g_psDisabledTests,g_psGfiTest); delay(SHOW_DISABLED_DELAY); } +#endif // GFI_SELFTEST g_OBD.LcdSetBacklightColor(WHITE); } From c6bc9eb3a4979503f9935cf2901e0946a6dc99f4 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 2 Jun 2015 15:39:47 -0700 Subject: [PATCH 02/96] setup for changing I2C bus freq --- Adafruit_MCP9808.cpp | 2 +- Adafruit_TMP007.cpp | 2 +- LiquidTWI2.cpp | 2 ++ open_evse.h | 2 ++ open_evse.ino | 6 +++++- 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Adafruit_MCP9808.cpp b/Adafruit_MCP9808.cpp index bba5a087..f8449777 100644 --- a/Adafruit_MCP9808.cpp +++ b/Adafruit_MCP9808.cpp @@ -64,7 +64,7 @@ Adafruit_MCP9808::Adafruit_MCP9808() { /**************************************************************************/ boolean Adafruit_MCP9808::begin(uint8_t addr) { _i2caddr = addr; - Wire.begin(); + //don't need - open_evse.ino does it Wire.begin(); if (read16(MCP9808_REG_MANUF_ID) != 0x0054) return false; if (read16(MCP9808_REG_DEVICE_ID) != 0x0400) return false; diff --git a/Adafruit_TMP007.cpp b/Adafruit_TMP007.cpp index 5f3c06d5..17eb9cbb 100644 --- a/Adafruit_TMP007.cpp +++ b/Adafruit_TMP007.cpp @@ -26,7 +26,7 @@ Adafruit_TMP007::Adafruit_TMP007(uint8_t i2caddr) { boolean Adafruit_TMP007::begin(uint8_t samplerate) { - Wire.begin(); +//don't need - open_evse.ino does it Wire.begin(); write16(TMP007_CONFIG, TMP007_CFG_MODEON | TMP007_CFG_ALERTEN | TMP007_CFG_TRANSC | samplerate); diff --git a/LiquidTWI2.cpp b/LiquidTWI2.cpp index 5698fc4d..ce646304 100644 --- a/LiquidTWI2.cpp +++ b/LiquidTWI2.cpp @@ -111,9 +111,11 @@ void LiquidTWI2::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! // according to datasheet, we need at least 40ms after power rises above 2.7V // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 + /* don't need - open_evse.ino does it delay(50); Wire.begin(); + */ uint8_t result; #if defined(MCP23017)&&defined(MCP23008) diff --git a/open_evse.h b/open_evse.h index 80f47b64..05c48b5f 100644 --- a/open_evse.h +++ b/open_evse.h @@ -269,6 +269,8 @@ //-- begin configuration +#define I2C_FREQ 100000UL // Hz + // WARNING: ALL DELAYS *MUST* BE SHORTER THAN THIS TIMER OR WE WILL GET INTO // AN INFINITE RESET LOOP #define WATCHDOG_TIMEOUT WDTO_2S diff --git a/open_evse.ino b/open_evse.ino index a076a449..a58c0a20 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -3927,8 +3927,9 @@ void DelayTimer::PrintTimerIcon(){ void EvseReset() { -#ifdef RTC Wire.begin(); + +#ifdef RTC g_RTC.begin(); #ifdef DELAYTIMER g_DelayTimer.Init(); @@ -3972,6 +3973,9 @@ void setup() g_WattHours_accumulated = eeprom_read_dword((uint32_t*)EOFS_KWH_ACCUMULATED); // get the stored value for the kWh from eeprom #endif // KWH_RECORDING + // need I2C speed last because Wire.begin(), which might be called by + // libraries, sets it to TWI_FREQ + Wire.setClock(I2C_FREQ); } // setup() From fb3af599d7745136211ccf095b1d09f1d05704ec Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 2 Jun 2015 15:40:03 -0700 Subject: [PATCH 03/96] change to usbasp --- fuse.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuse.bat b/fuse.bat index 3ba834f1..1a17902c 100644 --- a/fuse.bat +++ b/fuse.bat @@ -1,2 +1,2 @@ -avrdude -c usbtiny -p m328p -U lfuse:w:0xFF:m -U hfuse:w:0xDF:m -U efuse:w:0x05:m +avrdude -c usbasp -p m328p -U lfuse:w:0xFF:m -U hfuse:w:0xDF:m -U efuse:w:0x05:m pause From 770488e7fdb19d8ff47b13a5cfd157d9969b41e1 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 2 Jun 2015 15:41:31 -0700 Subject: [PATCH 04/96] split out Gfi/J1772PIlot/J1772EvseController into separate files --- CHANGELOG | 3 + Gfi.cpp | 93 +++ J1772EvseController.cpp | 1497 ++++++++++++++++++++++++++++++++++ J1772EvseController.h | 336 ++++++++ J1772Pilot.cpp | 145 ++++ J1772Pilot.h | 38 + open_evse.h | 415 ++-------- open_evse.ino | 1683 +-------------------------------------- 8 files changed, 2202 insertions(+), 2008 deletions(-) create mode 100644 Gfi.cpp create mode 100644 J1772EvseController.cpp create mode 100644 J1772EvseController.h create mode 100644 J1772Pilot.cpp create mode 100644 J1772Pilot.h diff --git a/CHANGELOG b/CHANGELOG index 042f7106..710a483c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Change Log +vD3.8.5 SCL 20150602 +- split out Gfi/J1772Pilot/J1772EvseController into separate files +- add support for changing I2C frequency vD3.8.4 SCL 20150526 - display "Disabled" when EVSE_STATE_DISABLED instead of "Stopped" diff --git a/Gfi.cpp b/Gfi.cpp new file mode 100644 index 00000000..0240cf05 --- /dev/null +++ b/Gfi.cpp @@ -0,0 +1,93 @@ +/* + * This file is part of Open EVSE. + + * Open EVSE 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 3, or (at your option) + * any later version. + + * Open EVSE 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 Open EVSE; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "open_evse.h" + +#ifdef GFI +// interrupt service routing +void gfi_isr() +{ + g_EvseController.SetGfiTripped(); +} + + +void Gfi::Init() +{ + pin.init(GFI_REG,GFI_IDX,DigitalPin::INP); + // GFI triggers on rising edge + attachInterrupt(GFI_INTERRUPT,gfi_isr,RISING); + +#ifdef GFI_SELFTEST + pinTest.init(GFITEST_REG,GFITEST_IDX,DigitalPin::OUT); +#endif + + Reset(); +} + + +//RESET GFI LOGIC +void Gfi::Reset() +{ + WDT_RESET(); + +#ifdef GFI_SELFTEST + testInProgress = 0; + testSuccess = 0; +#endif // GFI_SELFTEST + + if (pin.read()) m_GfiFault = 1; // if interrupt pin is high, set fault + else m_GfiFault = 0; +} + +#ifdef GFI_SELFTEST + +uint8_t Gfi::SelfTest() +{ + testInProgress = 1; + testSuccess = 0; + for(int i=0; i < GFI_TEST_CYCLES; i++) { + pinTest.write(1); + delayMicroseconds(GFI_PULSE_DURATION_US); + pinTest.write(0); + delayMicroseconds(GFI_PULSE_DURATION_US); + if (testSuccess) break; // no need to keep trying. + } + + // wait for GFI pin to clear + do { + delay(50); + WDT_RESET(); + } + while(pin.read()); + +#ifndef OPENEVSE_2 + // sometimes getting spurious GFI faults when testing just before closing + // relay. + // wait a little more for everything to settle down + // this delay is needed only if 10uF cap is in the circuit, which makes the circuit + // temporarily overly sensitive to trips until it discharges + delay(1000); +#endif // OPENEVSE_2 + + m_GfiFault = 0; + testInProgress = 0; + + return !testSuccess; +} +#endif // GFI_SELFTEST +#endif // GFI diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp new file mode 100644 index 00000000..4e9a4cf2 --- /dev/null +++ b/J1772EvseController.cpp @@ -0,0 +1,1497 @@ +/* + * This file is part of Open EVSE. + + * Open EVSE 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 3, or (at your option) + * any later version. + + * Open EVSE 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 Open EVSE; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "open_evse.h" + + +void J1772EVSEController::SaveSettings() +{ + // n.b. should we add dirty bits so we only write the changed values? or should we just write them on the fly when necessary? + eeprom_write_byte((uint8_t *)((GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),(byte)GetCurrentCapacity()); + SaveEvseFlags(); +} + + +THRESH_DATA g_DefaultThreshData = {875,780,690,0,260}; + +J1772EVSEController g_EvseController; + +#ifdef AMMETER +static inline unsigned long ulong_sqrt(unsigned long in) +{ + unsigned long out; + // find the last int whose square is not too big + // Yes, it's wasteful, but we only theoretically ever have to go to 512. + // Removing floating point saves us almost 1K of flash. + for(out = 1; out*out <= in; out++) ; + return out - 1; +} + +void J1772EVSEController::readAmmeter() +{ + WDT_RESET(); + + unsigned long sum = 0; + unsigned int zero_crossings = 0; + unsigned long last_zero_crossing_time = 0, now_ms; + long last_sample = -1; // should be impossible - the A/d is 0 to 1023. + unsigned int sample_count = 0; + for(unsigned long start = millis(); ((now_ms = millis()) - start) < CURRENT_SAMPLE_INTERVAL; ) { + long sample = (long) adcCurrent.read(); + // If this isn't the first sample, and if the sign of the value differs from the + // sign of the previous value, then count that as a zero crossing. + if (last_sample != -1 && ((last_sample > 512) != (sample > 512))) { + // Once we've seen a zero crossing, don't look for one for a little bit. + // It's possible that a little noise near zero could cause a two-sample + // inversion. + if ((now_ms - last_zero_crossing_time) > CURRENT_ZERO_DEBOUNCE_INTERVAL) { + zero_crossings++; + last_zero_crossing_time = now_ms; + } + } + last_sample = sample; + switch(zero_crossings) { + case 0: + continue; // Still waiting to start sampling + case 1: + case 2: + // Gather the sum-of-the-squares and count how many samples we've collected. + sum += (unsigned long)((sample - 512) * (sample - 512)); + sample_count++; + continue; + case 3: + // The answer is the square root of the mean of the squares. + // But additionally, that value must be scaled to a real current value. + // we will do that elsewhere + m_AmmeterReading = ulong_sqrt(sum / sample_count); + return; + } + } + // ran out of time. Assume that it's simply not oscillating any. + m_AmmeterReading = 0; + + WDT_RESET(); +} + +#define MA_PTS 32 // # points in moving average MUST BE power of 2 +#define MA_BITS 5 // log2(MA_PTS) +/* +uint32_t MovingAverage(uint32_t samp) +{ + static uint32_t samps[MA_PTS] = {0}; + uint32_t tot = samp; + samps[0] = samp; + + for (int8_t c=MA_PTS-1;c > 0;c--) { + samps[c] = samps[c-1]; + tot += samps[c]; + } + + return tot >> MA_BITS; +} +*/ + +// to save memory +// instead of doing a real moving average we just do a non-overlapping +// sliding window and output a value every MA_PTS +uint32_t MovingAverage(uint32_t samp) +{ + static uint32_t tot = 0; + static int8_t curidx = 0; + + if (curidx == 0) { + tot = 0; + } + + tot += samp; + + if (++curidx == MA_PTS) { + curidx = 0; + return tot >> MA_BITS; // tot / MA_PTS + } + return 0xffffffff; +} + +#endif // AMMETER + +J1772EVSEController::J1772EVSEController() : + adcPilot(VOLT_PIN) +#ifdef CURRENT_PIN + , adcCurrent(CURRENT_PIN) +#endif +#ifdef VOLTMETER_PIN + , adcVoltMeter(VOLTMETER_PIN) +#endif +{ +} + +// use watchdog to perform a reset +void J1772EVSEController::Reboot() +{ + m_Pilot.SetState(PILOT_STATE_P12); + +#ifdef LCD16X2 + g_OBD.LcdPrint_P(1,PSTR("Resetting...")); +#endif + + if (chargingIsOn()) { + // give the EV some time to open its contactor in response to P12 + delay(3000); + } + + // hardware reset by forcing watchdog to timeout + wdt_enable(WDTO_1S); // enable watchdog timer + delay(1500); +} + + + +#ifdef SHOW_DISABLED_TESTS +void J1772EVSEController::ShowDisabledTests() +{ + if (m_wFlags & (ECF_DIODE_CHK_DISABLED| + ECF_VENT_REQ_DISABLED| + ECF_GND_CHK_DISABLED| + ECF_STUCK_RELAY_CHK_DISABLED| + ECF_GFI_TEST_DISABLED)) { + g_OBD.LcdSetBacklightColor(YELLOW); + + if (!DiodeCheckEnabled()) { + g_OBD.LcdMsg_P(g_psDisabledTests,g_psDiodeCheck); + delay(SHOW_DISABLED_DELAY); + } + if (!VentReqEnabled()) { + g_OBD.LcdMsg_P(g_psDisabledTests,g_psVentReqChk); + delay(SHOW_DISABLED_DELAY); + } +#ifdef ADVPWR + if (!GndChkEnabled()) { + g_OBD.LcdMsg_P(g_psDisabledTests,g_psGndChk); + delay(SHOW_DISABLED_DELAY); + } + if (!StuckRelayChkEnabled()) { + g_OBD.LcdMsg_P(g_psDisabledTests,g_psRlyChk); + delay(SHOW_DISABLED_DELAY); + } +#endif // ADVPWR +#ifdef GFI_SELFTEST + if (!GfiSelfTestEnabled()) { + g_OBD.LcdMsg_P(g_psDisabledTests,g_psGfiTest); + delay(SHOW_DISABLED_DELAY); + } +#endif // GFI_SELFTEST + + g_OBD.LcdSetBacklightColor(WHITE); + } +} +#endif //SHOW_DISABLED_TESTS + +void J1772EVSEController::chargingOn() +{ // turn on charging current + pinCharging.write(1); +#ifdef CHARGING2_REG + pinCharging2.write(1); +#endif +#ifdef CHARGINGAC_REG + pinChargingAC.write(1); +#endif + m_bVFlags |= ECVF_CHARGING_ON; + + m_ChargeOnTime = now(); + m_ChargeOnTimeMS = millis(); +} + +void J1772EVSEController::chargingOff() +{ // turn off charging current + pinCharging.write(0); +#ifdef CHARGING2_REG + pinCharging2.write(0); +#endif +#ifdef CHARGINGAC_REG + pinChargingAC.write(0); +#endif + m_bVFlags &= ~ECVF_CHARGING_ON; + + m_ChargeOffTime = now(); + m_ChargeOffTimeMS = millis(); + +#ifdef AMMETER + m_ChargingCurrent = 0; +#endif +} + +void J1772EVSEController::HardFault() +{ +#ifdef BTN_MENU + g_SettingsMenu.EnableExitItem(0); +#endif + g_OBD.Update(OBD_UPD_HARDFAULT); + while (1) ProcessInputs(1); // spin forever or until user resets via menu +} + +#ifdef GFI +void J1772EVSEController::SetGfiTripped() +{ +#ifdef GFI_SELFTEST + if (m_Gfi.SelfTestInProgress()) { + m_Gfi.SetTestSuccess(); + return; + } +#endif + m_bVFlags |= ECVF_GFI_TRIPPED; + + // this is repeated Update(), but we want to keep latency as low as possible + // for safety so we do it here first anyway + chargingOff(); // turn off charging current + // turn off the pilot + m_Pilot.SetState(PILOT_STATE_N12); + + m_Gfi.SetFault(); + // the rest of the logic will be handled in Update() +} +#endif // GFI + +void J1772EVSEController::EnableDiodeCheck(uint8_t tf) +{ + if (tf) { + m_wFlags &= ~ECF_DIODE_CHK_DISABLED; + } + else { + m_wFlags |= ECF_DIODE_CHK_DISABLED; + } + SaveEvseFlags(); +} + +#ifdef GFI_SELFTEST +void J1772EVSEController::EnableGfiSelfTest(uint8_t tf) +{ + if (tf) { + m_wFlags &= ~ECF_GFI_TEST_DISABLED; + } + else { + m_wFlags |= ECF_GFI_TEST_DISABLED; + } + SaveEvseFlags(); +} +#endif + +void J1772EVSEController::EnableVentReq(uint8_t tf) +{ + if (tf) { + m_wFlags &= ~ECF_VENT_REQ_DISABLED; + } + else { + m_wFlags |= ECF_VENT_REQ_DISABLED; + } + SaveEvseFlags(); +} + +#ifdef ADVPWR +void J1772EVSEController::EnableGndChk(uint8_t tf) +{ + if (tf) { + m_wFlags &= ~ECF_GND_CHK_DISABLED; + } + else { + m_NoGndRetryCnt = 0; + m_NoGndStart = 0; + m_wFlags |= ECF_GND_CHK_DISABLED; + } + SaveEvseFlags(); +} + +void J1772EVSEController::EnableStuckRelayChk(uint8_t tf) +{ + if (tf) { + m_wFlags &= ~ECF_STUCK_RELAY_CHK_DISABLED; + } + else { + m_wFlags |= ECF_STUCK_RELAY_CHK_DISABLED; + } + SaveEvseFlags(); +} + +void J1772EVSEController::EnableAutoSvcLevel(uint8_t tf) +{ + if (tf) { + m_wFlags &= ~ECF_AUTO_SVC_LEVEL_DISABLED; + } + else { + m_wFlags |= ECF_AUTO_SVC_LEVEL_DISABLED; + } + SaveEvseFlags(); +} + + +#endif // ADVPWR + +// Functions to support Auto Start feature - GoldServe +#ifdef MANUALSTART +void J1772EVSEController::EnableAutoStart(uint8_t tf) +{ + if (tf) { + m_wFlags &= ~ECF_AUTO_START_DISABLED; + } + else { + m_wFlags |= ECF_AUTO_START_DISABLED; + } + SaveEvseFlags(); +} +#endif //#ifdef MANUALSTART +void J1772EVSEController::EnableSerDbg(uint8_t tf) +{ + if (tf) { + m_wFlags |= ECF_SERIAL_DBG; + } + else { + m_wFlags &= ~ECF_SERIAL_DBG; + } + SaveEvseFlags(); +} + +#ifdef RGBLCD +int J1772EVSEController::SetBacklightType(uint8_t t,uint8_t update) +{ +#ifdef RGBLCD + g_OBD.LcdSetBacklightType(t,update); + if (t == BKL_TYPE_MONO) m_wFlags |= ECF_MONO_LCD; + else m_wFlags &= ~ECF_MONO_LCD; + SaveEvseFlags(); +#endif // RGBLCD + return 0; +} +#endif // RGBLCD +void J1772EVSEController::Enable() +{ + if ((m_EvseState == EVSE_STATE_DISABLED)|| + (m_EvseState == EVSE_STATE_SLEEPING)) { +#ifdef SLEEP_STATUS_REG + if (m_EvseState == EVSE_STATE_SLEEPING) { + pinSleepStatus.write(0); + } +#endif // SLEEP_STATUS_REG + + m_PrevEvseState = EVSE_STATE_DISABLED; + m_EvseState = EVSE_STATE_UNKNOWN; + m_Pilot.SetState(PILOT_STATE_P12); + } +} + +void J1772EVSEController::Disable() +{ + if (m_EvseState != EVSE_STATE_DISABLED) { + m_Pilot.SetState(PILOT_STATE_N12); + m_EvseState = EVSE_STATE_DISABLED; + // panic stop so we won't wait for EV to open its contacts first + chargingOff(); + g_OBD.Update(OBD_UPD_FORCE); +#ifdef RAPI + g_ERP.sendEvseState(); +#endif // RAPI + } +} + + +void J1772EVSEController::Sleep() +{ + if (m_EvseState != EVSE_STATE_SLEEPING) { + m_Pilot.SetState(PILOT_STATE_P12); + m_EvseState = EVSE_STATE_SLEEPING; +#ifdef SLEEP_STATUS_REG + pinSleepStatus.write(1); +#endif // SLEEP_STATUS_REG + + g_OBD.Update(OBD_UPD_FORCE); +#ifdef RAPI + g_ERP.sendEvseState(); +#endif // RAPI + // try to prevent arcing of our relay by waiting for EV to open its contacts first + // use the charge end time variable temporarily to count down + // when to open the contacts in Update() + m_ChargeOffTimeMS = millis(); + } +} + +void J1772EVSEController::LoadThresholds() +{ + memcpy(&m_ThreshData,&g_DefaultThreshData,sizeof(m_ThreshData)); +} + +void J1772EVSEController::SetSvcLevel(uint8_t svclvl,uint8_t updatelcd) +{ +#ifdef SERIALCLI + if (SerDbgEnabled()) { + g_CLI.printlnn(); + g_CLI.print_P(PSTR("SetSvcLevel: "));Serial.println((int)svclvl); + } +#endif //#ifdef SERIALCLI + if (svclvl == 2) { + m_wFlags |= ECF_L2; // set to Level 2 + } + else { + svclvl = 1; + m_wFlags &= ~ECF_L2; // set to Level 1 + } + + SaveEvseFlags(); + + uint8_t ampacity = eeprom_read_byte((uint8_t*)((svclvl == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2)); + + if ((ampacity == 0xff) || (ampacity == 0)) { + ampacity = (svclvl == 1) ? DEFAULT_CURRENT_CAPACITY_L1 : DEFAULT_CURRENT_CAPACITY_L2; + } + + if (ampacity < MIN_CURRENT_CAPACITY) { + ampacity = MIN_CURRENT_CAPACITY; + } + else { + if (svclvl == 1) { // L1 + if (ampacity > MAX_CURRENT_CAPACITY_L1) { + ampacity = MAX_CURRENT_CAPACITY_L1; + } + } + else { + if (ampacity > MAX_CURRENT_CAPACITY_L2) { + ampacity = MAX_CURRENT_CAPACITY_L2; + } + } + } + + + LoadThresholds(); + + SetCurrentCapacity(ampacity); + + if (updatelcd) { + g_OBD.Update(OBD_UPD_FORCE); + } +} + +#ifdef ADVPWR + +// acpinstate : bit 1 = AC pin 1, bit0 = AC pin 2 +uint8_t J1772EVSEController::ReadACPins() +{ +#ifndef OPENEVSE_2 +#ifdef SAMPLE_ACPINS + // + // AC pins are active low, so we set them high + // and then if voltage is detected on a pin, it will go low + // + uint8_t ac1 = 2; + uint8_t ac2 = 1; + unsigned long startms = millis(); + + do { + if (ac1 && !pinAC1.read()) { + ac1 = 0; + } + if (ac2 && !pinAC2.read()) { + ac2 = 0; + } + } while ((ac1 || ac2) && ((millis() - startms) < AC_SAMPLE_MS)); + return ac1 | ac2; +#else // !SAMPLE_ACPINS + return (pinAC1.read() ? 2 : 0) | (pinAC2.read() ? 1 : 0); +#endif // SAMPLE_ACPINS +#else + // For OpenEVSE II, there is only ACLINE1_PIN, and it is + // active *high*. '3' is the value for "both AC lines dead" + // and '0' is the value for "both AC lines live". There is + // no need to sample, as the hardware does a peak-hold. + return (pinAC1.read() ? 0 : 3); +#endif // OPENEVSE_2 +} + + + +uint8_t J1772EVSEController::doPost() +{ + WDT_RESET(); + + uint8_t RelayOff, Relay1, Relay2; //Relay Power status + uint8_t svcState = UD; // service state = undefined + +#ifdef SERIALCLI + if (SerDbgEnabled()) { + g_CLI.print_P(PSTR("POST start...")); + } +#endif //#ifdef SERIALCLI + + + m_Pilot.SetState(PILOT_STATE_P12); //check to see if EV is plugged in + + g_OBD.SetRedLed(1); +#ifdef LCD16X2 //Adafruit RGB LCD + g_OBD.LcdMsg_P(g_psPwrOn,g_psSelfTest); +#endif //Adafruit RGB LCD + + if (AutoSvcLevelEnabled()) { +#ifdef OPENEVSE_2 + // For OpenEVSE II, there is a voltmeter for auto L1/L2. + uint32_t long ac_volts = ReadVoltmeter(); + if (ac_volts > L2_VOLTAGE_THRESHOLD) { + svcState = L2; + } else { + svcState = L1; + } +#ifdef SERIALCLI + if (SerDbgEnabled()) { + g_CLI.print_P(PSTR("AC millivolts: "));Serial.println(ac_volts); + g_CLI.print_P(PSTR("SvcState: "));Serial.println((int)svcState); + } +#endif //#ifdef SERIALCLI +#ifdef LCD16X2 + g_OBD.LcdMsg_P(g_psAutoDetect,(svcState == L2) ? g_psLevel2 : g_psLevel1); +#endif //LCD16x2 + +#else //!OPENEVSE_2 + + delay(150); // delay reading for stable pilot before reading + int reading = adcPilot.read(); //read pilot +#ifdef SERIALCLI + if (SerDbgEnabled()) { + g_CLI.printlnn(); + g_CLI.print_P(PSTR("Pilot: "));Serial.println((int)reading); + } +#endif //#ifdef SERIALCLI + + m_Pilot.SetState(PILOT_STATE_N12); + if (reading > 900) { // IF EV is not connected its Okay to open the relay the do the L1/L2 and ground Check + + // save state with both relays off - for stuck relay state + RelayOff = ReadACPins(); + + // save state with Relay 1 on + pinCharging.write(1); +#ifdef CHARGINGAC_REG + pinChargingAC.write(1); +#endif + delay(RelaySettlingTime); + Relay1 = ReadACPins(); + pinCharging.write(0); +#ifdef CHARGINGAC_REG + pinChargingAC.write(0); +#endif + delay(RelaySettlingTime); //allow relay to fully open before running other tests + + // save state for Relay 2 on +#ifdef CHARGING2_REG + pinCharging2.write(1); +#endif + delay(RelaySettlingTime); + Relay2 = ReadACPins(); +#ifdef CHARGING2_REG + pinCharging2.write(0); +#endif + delay(RelaySettlingTime); //allow relay to fully open before running other tests + + // decide input power state based on the status read on L1 and L2 + // either 2 SPST or 1 DPST relays can be configured + // valid svcState is L1 - one hot, L2 both hot, OG - open ground both off, SR - stuck relay when shld be off + // + if (RelayOff == none) { // relay not stuck on when off + switch ( Relay1 ) { + case ( both ): // + if ( Relay2 == none ) svcState = L2; + if (StuckRelayChkEnabled()) { + if ( Relay2 != none ) svcState = SR; + } + break; + case ( none ): // + if (GndChkEnabled()) { + if ( Relay2 == none ) svcState = OG; + } + if ( Relay2 == both ) svcState = L2; + if ( Relay2 == L1 || Relay2 == L2 ) svcState = L1; + break; + case ( L1on ): // L1 or L2 + case ( L2on ): + if (StuckRelayChkEnabled()) { + if ( Relay2 != none ) svcState = SR; + } + if ( Relay2 == none ) svcState = L1; + if ( (Relay1 == L1on) && (Relay2 == L2on)) svcState = L2; + if ( (Relay1 == L2on) && (Relay2 == L1on)) svcState = L2; + break; + } // end switch + } + else { // Relay stuck on + if (StuckRelayChkEnabled()) { + svcState = SR; + } + } +#ifdef SERIALCLI + if (SerDbgEnabled()) { + g_CLI.print_P(PSTR("RelayOff: "));Serial.println((int)RelayOff); + g_CLI.print_P(PSTR("Relay1: "));Serial.println((int)Relay1); + g_CLI.print_P(PSTR("Relay2: "));Serial.println((int)Relay2); + g_CLI.print_P(PSTR("SvcState: "));Serial.println((int)svcState); + } +#endif //#ifdef SERIALCLI + + // update LCD +#ifdef LCD16X2 + if (svcState == L1) g_OBD.LcdMsg_P(g_psAutoDetect,g_psLevel1); + if (svcState == L2) g_OBD.LcdMsg_P(g_psAutoDetect,g_psLevel2); + if ((svcState == OG) || (svcState == SR)) { + g_OBD.LcdSetBacklightColor(RED); + } + if (svcState == OG) g_OBD.LcdMsg_P(g_psTestFailed,g_psNoGround); + if (svcState == SR) g_OBD.LcdMsg_P(g_psTestFailed,g_psStuckRelay); +#endif // LCD16X2 + } // endif test, no EV is plugged in + else { + // since we can't auto detect, for safety's sake, we must set to L1 + svcState = L1; + SetAutoSvcLvlSkipped(1); + // EV connected.. do stuck relay check + goto stuckrelaychk; + } +#endif //#else OPENEVSE_2 + } + else { // ! AutoSvcLevelEnabled + stuckrelaychk: + if (StuckRelayChkEnabled()) { + RelayOff = ReadACPins(); + if ((RelayOff & 3) != 3) { + svcState = SR; +#ifdef LCD16X2 + g_OBD.LcdMsg_P(g_psTestFailed,g_psStuckRelay); +#endif // LCD16X2 + } + } + } // endif AutoSvcLevelEnabled + +#ifdef GFI_SELFTEST + // only run GFI test if no fault detected above + if (((svcState == UD)||(svcState == L1)||(svcState == L2)) && + GfiSelfTestEnabled()) { + if (m_Gfi.SelfTest()) { +#ifdef LCD16X2 + g_OBD.LcdMsg_P(g_psTestFailed,g_psGfci); +#endif // LCD16X2 + svcState = FG; + } + } +#endif + + if ((svcState == OG)||(svcState == SR)||(svcState == FG)) { + g_OBD.LcdSetBacklightColor(RED); + g_OBD.SetGreenLed(0); + g_OBD.SetRedLed(1); + } + else { + g_OBD.SetRedLed(0); + } + m_Pilot.SetState(PILOT_STATE_P12); + +#ifdef SERIALCLI + if (SerDbgEnabled()) { + g_CLI.print_P(PSTR("POST result: ")); + Serial.println((int)svcState); + } +#endif //#ifdef SERIALCLI + + WDT_RESET(); + + return svcState; +} +#endif // ADVPWR + +void J1772EVSEController::ProcessInputs(uint8_t nosleeptoggle) +{ +#ifdef RAPI + g_ERP.doCmd(); +#endif +#ifdef SERIALCLI + g_CLI.getInput(); +#endif // SERIALCLI +#ifdef BTN_MENU + g_BtnHandler.ChkBtn(nosleeptoggle); +#endif +} + +void J1772EVSEController::Init() +{ + // read settings from EEPROM + uint16_t rflgs = eeprom_read_word((uint16_t*)EOFS_FLAGS); + +#ifdef RGBLCD + if ((rflgs != 0xffff) && (rflgs & ECF_MONO_LCD)) { + g_OBD.LcdSetBacklightType(BKL_TYPE_MONO); + } +#endif // RGBLCD + + pinCharging.init(CHARGING_REG,CHARGING_IDX,DigitalPin::OUT); +#ifdef CHARGING2_REG + pinCharging2.init(CHARGING2_REG,CHARGING2_IDX,DigitalPin::OUT); +#endif +#ifdef CHARGINGAC_REG + pinChargingAC.init(CHARGINGAC_REG,CHARGINGAC_IDX,DigitalPin::OUT); +#endif +#ifdef ACLINE1_REG + pinAC1.init(ACLINE1_REG,ACLINE1_IDX,DigitalPin::INP_PU); +#endif +#ifdef ACLINE2_REG + pinAC2.init(ACLINE2_REG,ACLINE2_IDX,DigitalPin::INP_PU); +#endif +#ifdef SLEEP_STATUS_REG + pinSleepStatus.init(SLEEP_STATUS_REG,SLEEP_STATUS_IDX,DigitalPin::OUT); +#endif + +#ifdef GFI + m_Gfi.Init(); +#endif // GFI + + chargingOff(); + + m_Pilot.Init(); // init the pilot + + uint8_t svclvl = (uint8_t)DEFAULT_SERVICE_LEVEL; + + if (rflgs == 0xffff) { // uninitialized EEPROM + m_wFlags = ECF_DEFAULT; +#ifdef RGBLCD + if (DEFAULT_LCD_BKL_TYPE == BKL_TYPE_MONO) { + m_wFlags |= ECF_MONO_LCD; + } +#endif // RGBLCD + } + else { + m_wFlags = rflgs; + svclvl = GetCurSvcLevel(); + + } + +#ifdef NOCHECKS + m_wFlags |= ECF_DIODE_CHK_DISABLED|ECF_VENT_REQ_DISABLED|ECF_GND_CHK_DISABLED|ECF_STUCK_RELAY_CHK_DISABLED|ECF_GFI_TEST_DISABLED; +#endif + +#ifdef AMMETER + m_AmmeterCurrentOffset = eeprom_read_word((uint16_t*)EOFS_AMMETER_CURR_OFFSET); + m_CurrentScaleFactor = eeprom_read_word((uint16_t*)EOFS_CURRENT_SCALE_FACTOR); + + if (m_AmmeterCurrentOffset == 0x0000ffff) { + m_AmmeterCurrentOffset = DEFAULT_AMMETER_CURRENT_OFFSET; + } + if (m_CurrentScaleFactor == 0x0000ffff) { + m_CurrentScaleFactor = DEFAULT_CURRENT_SCALE_FACTOR; + } + + m_AmmeterReading = 0; + m_ChargingCurrent = 0; + // m_LastAmmeterReadMs = 0; +#endif // AMMETER + +#ifdef VOLTMETER + m_VoltOffset = eeprom_read_dword((uint32_t*)EOFS_VOLT_OFFSET); + m_VoltScaleFactor = eeprom_read_word((uint16_t*)EOFS_VOLT_SCALE_FACTOR); + + if (m_VoltOffset == 0xffffffff) { + m_VoltOffset = DEFAULT_VOLT_OFFSET; + } + if (m_VoltScaleFactor == 0xffff) { + m_VoltScaleFactor = DEFAULT_VOLT_SCALE_FACTOR; + } +#endif // VOLTMETER + +#ifndef RGBLCD + m_wFlags |= ECF_MONO_LCD; +#endif + + m_bVFlags = ECVF_DEFAULT; +#ifdef GFI + m_GfiRetryCnt = 0; + m_GfiTripCnt = eeprom_read_byte((uint8_t*)EOFS_GFI_TRIP_CNT); + if (m_GfiTripCnt == 255) { + m_GfiTripCnt = 0; + } +#endif // GFI +#ifdef ADVPWR + m_NoGndRetryCnt = 0; + m_NoGndTripCnt = eeprom_read_byte((uint8_t*)EOFS_NOGND_TRIP_CNT); + if (m_NoGndTripCnt != 255) { + m_NoGndTripCnt = 0; + } + + m_StuckRelayStartTimeMS = 0; + m_StuckRelayTripCnt = eeprom_read_byte((uint8_t*)EOFS_STUCK_RELAY_TRIP_CNT); + if (m_StuckRelayTripCnt != 255) { + m_StuckRelayTripCnt = 0; + } + + m_NoGndRetryCnt = 0; + m_NoGndStart = 0; +#endif // ADVPWR + + m_EvseState = EVSE_STATE_UNKNOWN; + m_PrevEvseState = EVSE_STATE_UNKNOWN; + + +#ifdef ADVPWR + +#ifdef FT_READ_AC_PINS + while (1) { + WDT_RESET(); + sprintf(g_sTmp,"%d",(int)ReadACPins()); + g_OBD.LcdMsg("AC Pins",g_sTmp); + } +#endif // FT_READ_AC_PINS + +#ifdef SHOW_DISABLED_TESTS + ShowDisabledTests(); +#endif + +#ifdef BTN_MENU + g_SettingsMenu.EnableExitItem(0); +#endif // BTN_MENU + uint8_t fault; + do { + fault = 0; // reset post fault + uint8_t psvclvl = doPost(); // auto detect service level overrides any saved values + + if ((AutoSvcLevelEnabled()) && ((psvclvl == L1) || (psvclvl == L2))) svclvl = psvclvl; //set service level + if ((GndChkEnabled()) && (psvclvl == OG)) { m_EvseState = EVSE_STATE_NO_GROUND; fault = 1;} // set No Ground error + if ((StuckRelayChkEnabled()) && (psvclvl == SR)) { m_EvseState = EVSE_STATE_STUCK_RELAY; fault = 1; } // set Stuck Relay error +#ifdef GFI_SELFTEST + if ((GfiSelfTestEnabled()) && (psvclvl == FG)) { m_EvseState = EVSE_STATE_GFI_TEST_FAILED; fault = 1; } // set GFI test fail error +#endif + if (fault) { +#ifdef UL_COMPLIANT + // UL wants EVSE to hard fault until power cycle if POST fails + while (1) { // spin forever +#endif + unsigned long faultms = millis(); + // wait for GFI_TIMEOUT before retrying POST + while ((millis() - faultms) < GFI_TIMEOUT) { + ProcessInputs(1); + } +#ifdef UL_COMPLIANT + } +#endif + } + } while ( fault && ( m_EvseState == EVSE_STATE_GFI_TEST_FAILED || m_EvseState == EVSE_STATE_NO_GROUND || m_EvseState == EVSE_STATE_STUCK_RELAY )); +#endif // ADVPWR + +#ifdef BTN_MENU + g_SettingsMenu.EnableExitItem(1); +#endif // BTN_MENU + + SetSvcLevel(svclvl); + + // Start Manual Start Feature - GoldServe +#ifdef MANUALSTART + if (AutoStartEnabled()){ + Enable(); + } else { + Sleep(); + } +#endif //#ifdef MANUALSTART + // End Manual Start Feature - GoldServe + + g_OBD.SetGreenLed(0); +} + +void J1772EVSEController::ReadPilot(int *plow,int *phigh,int loopcnt) +{ + int pl = 1023; + int ph = 0; + + // 1x = 114us 20x = 2.3ms 100x = 11.3ms + for (int i=0;i < 100;i++) { + int reading = adcPilot.read(); // measures pilot voltage + + if (reading > ph) { + ph = reading; + } + else if (reading < pl) { + pl = reading; + } + } + + *plow = pl; + *phigh = ph; +} + + +//TABLE A1 - PILOT LINE VOLTAGE RANGES (recommended.. adjust as necessary +// Minimum Nominal Maximum +//Positive Voltage, State A 11.40 12.00 12.60 +//Positive Voltage, State B 8.36 9.00 9.56 +//Positive Voltage, State C 5.48 6.00 6.49 +//Positive Voltage, State D 2.62 3.00 3.25 +//Negative Voltage - States B, C, D, and F -11.40 -12.00 -12.60 +void J1772EVSEController::Update() +{ + int plow; + int phigh; + + unsigned long curms = millis(); + + if (m_EvseState == EVSE_STATE_DISABLED) { + m_PrevEvseState = m_EvseState; // cancel state transition + return; + } + else if (m_EvseState == EVSE_STATE_SLEEPING) { + if (chargingIsOn()) { + ReadPilot(&plow,&phigh); + // wait for pilot voltage to go > STATE C. This will happen if + // a) EV reacts and goes back to state B (opens its contacts) + // b) user pulls out the charge connector + // if it doesn't happen within 3 sec, we'll just open our relay anyway + if ((phigh >= m_ThreshData.m_ThreshBC) + || ((curms - m_ChargeOffTimeMS) >= 3000)) { + chargingOff(); +#ifdef FT_SLEEP_DELAY + sprintf(g_sTmp,"SLEEP OPEN %d",(int)phigh); + g_OBD.LcdMsg(g_sTmp,(phigh >= m_ThreshData.m_ThreshBC) ? "THRESH" : "TIMEOUT"); + delay(2000); +#endif + } + } + m_PrevEvseState = m_EvseState; // cancel state transition + return; + } + + uint8_t prevevsestate = m_EvseState; + uint8_t tmpevsestate = EVSE_STATE_UNKNOWN; + uint8_t nofault = 1; + +#ifdef ADVPWR + uint8_t acpinstate = ReadACPins(); + + if (chargingIsOn()) { // relay closed + if ((curms - m_ChargeOnTimeMS) > GROUND_CHK_DELAY) { + // ground check - can only test when relay closed + if (GndChkEnabled() && ((acpinstate & 3) == 3)) { + // bad ground + tmpevsestate = EVSE_STATE_NO_GROUND; + m_EvseState = EVSE_STATE_NO_GROUND; + + chargingOff(); // open the relay + + if ((prevevsestate != EVSE_STATE_NO_GROUND) && (m_NoGndTripCnt < 254)) { + m_NoGndTripCnt++; + eeprom_write_byte((uint8_t*)EOFS_NOGND_TRIP_CNT,m_NoGndTripCnt); + } + m_NoGndStart = curms; + + nofault = 0; + } + + // if EV was plugged in during POST, we couldn't do AutoSvcLevel detection, + // so we had to hardcode L1. During first charge session, we can probe and set to L2 if necessary + if (AutoSvcLvlSkipped() && (m_EvseState == EVSE_STATE_C)) { + if (!acpinstate) { + // set to L2 + SetSvcLevel(2,1); + } + SetAutoSvcLvlSkipped(0); + } + } + } + else { // !chargingIsOn() - relay open + if (prevevsestate == EVSE_STATE_NO_GROUND) { + if (((m_NoGndRetryCnt < GFI_RETRY_COUNT) || (GFI_RETRY_COUNT == 255)) && + ((curms - m_NoGndStart) > GFI_TIMEOUT)) { + m_NoGndRetryCnt++; + } + else { + tmpevsestate = EVSE_STATE_NO_GROUND; + m_EvseState = EVSE_STATE_NO_GROUND; + + nofault = 0; + } + } + else if (StuckRelayChkEnabled()) { // stuck relay check - can test only when relay open + if (((acpinstate & 3) != 3)) { // Stuck Relay reading + if ((prevevsestate != EVSE_STATE_STUCK_RELAY) && !m_StuckRelayStartTimeMS) { //check for first occurence + m_StuckRelayStartTimeMS = curms; // mark start state + } + if ( ( ((curms - m_ChargeOffTimeMS) > STUCK_RELAY_DELAY) && // charge off de-bounce + ((curms - m_StuckRelayStartTimeMS) > STUCK_RELAY_DELAY) ) || // start delay de-bounce + (prevevsestate == EVSE_STATE_STUCK_RELAY) ) { // already in error state + // stuck relay + if ((prevevsestate != EVSE_STATE_STUCK_RELAY) && (m_StuckRelayTripCnt < 254)) { + m_StuckRelayTripCnt++; + eeprom_write_byte((uint8_t*)EOFS_STUCK_RELAY_TRIP_CNT,m_StuckRelayTripCnt); + } + tmpevsestate = EVSE_STATE_STUCK_RELAY; + m_EvseState = EVSE_STATE_STUCK_RELAY; + nofault = 0; + } + } // end of stuck relay reading + else m_StuckRelayStartTimeMS = 0; // not stuck - reset + } // end of StuckRelayChkEnabled + } // end of !chargingIsOn() - relay open +#endif // ADVPWR + +#ifdef GFI + if (m_Gfi.Fault()) { + tmpevsestate = EVSE_STATE_GFCI_FAULT; + m_EvseState = EVSE_STATE_GFCI_FAULT; + + if (prevevsestate != EVSE_STATE_GFCI_FAULT) { + if (m_GfiTripCnt < 254) { + m_GfiTripCnt++; + eeprom_write_byte((uint8_t*)EOFS_GFI_TRIP_CNT,m_GfiTripCnt); + } + m_GfiRetryCnt = 0; + m_GfiTimeout = curms + GFI_TIMEOUT; + } + else if (curms >= m_GfiTimeout) { +#ifdef FT_GFI_RETRY + g_OBD.LcdMsg("Reset","GFI"); + delay(250); +#endif // FT_GFI_RETRY + m_GfiRetryCnt++; + + if ((GFI_RETRY_COUNT != 255) && (m_GfiRetryCnt > GFI_RETRY_COUNT)) { + HardFault(); + } + else { + m_Gfi.Reset(); + m_GfiTimeout = curms + GFI_TIMEOUT; + } + } + + nofault = 0; + } +#endif // GFI + + +#ifdef TEMPERATURE_MONITORING // A state for OverTemp fault + if ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_PANIC) || + (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_PANIC) || + (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_PANIC)) { + tmpevsestate = EVSE_STATE_OVER_TEMPERATURE; + m_EvseState = EVSE_STATE_OVER_TEMPERATURE; + nofault = 0; + } +#endif // TEMPERATURE_MONITORING + + if (nofault) { + if ((prevevsestate == EVSE_STATE_GFCI_FAULT) || + (prevevsestate == EVSE_STATE_OVER_TEMPERATURE) || + (prevevsestate == EVSE_STATE_NO_GROUND) || + (prevevsestate == EVSE_STATE_STUCK_RELAY)) { + // just got out of fault state - pilot back on + m_Pilot.SetState(PILOT_STATE_P12); + prevevsestate = EVSE_STATE_UNKNOWN; + m_EvseState = EVSE_STATE_UNKNOWN; + } + + ReadPilot(&plow,&phigh); + + if (DiodeCheckEnabled() && (m_Pilot.GetState() == PILOT_STATE_PWM) && (plow >= m_ThreshData.m_ThreshDS)) { + // diode check failed + tmpevsestate = EVSE_STATE_DIODE_CHK_FAILED; + } + else if (phigh >= m_ThreshData.m_ThreshAB) { + // 12V EV not connected + tmpevsestate = EVSE_STATE_A; + } + else if (phigh >= m_ThreshData.m_ThreshBC) { + // 9V EV connected, waiting for ready to charge + tmpevsestate = EVSE_STATE_B; + } + else if ((phigh >= m_ThreshData.m_ThreshCD) || + (!VentReqEnabled() && (phigh > m_ThreshData.m_ThreshD))) { + // 6V ready to charge + tmpevsestate = EVSE_STATE_C; + } + else if (phigh > m_ThreshData.m_ThreshD) { + // 3V ready to charge vent required + tmpevsestate = EVSE_STATE_D; + } + else { + tmpevsestate = EVSE_STATE_UNKNOWN; + } + +#ifdef FT_ENDURANCE + if (nofault) { + if (((tmpevsestate == EVSE_STATE_A)||(tmpevsestate == EVSE_STATE_B)) && (g_CycleCnt < 0)) { + g_CycleCnt = 0; + g_CycleHalfStart = curms; + g_CycleState = EVSE_STATE_B; + } + + if (g_CycleCnt >= 0) { + if (g_CycleState == EVSE_STATE_B) { + if ((curms - g_CycleHalfStart) >= 9000) { + g_CycleCnt++; + g_CycleHalfStart = curms; + tmpevsestate = EVSE_STATE_C; + g_CycleState = EVSE_STATE_C; + } + else tmpevsestate = EVSE_STATE_B; + } + else if (g_CycleState == EVSE_STATE_C) { + if ((curms - g_CycleHalfStart) >= 1000) { + g_CycleHalfStart = curms; + tmpevsestate = EVSE_STATE_B; + g_CycleState = EVSE_STATE_B; + } + else tmpevsestate = EVSE_STATE_C; + } + } + } +#endif // FT_ENDURANCE + + // debounce state transitions + if (tmpevsestate != prevevsestate) { + if (tmpevsestate != m_TmpEvseState) { + m_TmpEvseStateStart = curms; + } + else if ((curms - m_TmpEvseStateStart) >= ((tmpevsestate == EVSE_STATE_A) ? DELAY_STATE_TRANSITION_A : DELAY_STATE_TRANSITION)) { + m_EvseState = tmpevsestate; + } + } + } // nofault + + m_TmpEvseState = tmpevsestate; + +#ifdef FT_GFI_RETRY + if (nofault && (prevevsestate == EVSE_STATE_C) && + ((curms - m_ChargeOnTimeMS) > 10000)) { + g_OBD.LcdMsg("Induce","Fault"); + for(int i = 0; i < GFI_TEST_CYCLES; i++) { + pinTest.write(1); + delayMicroseconds(GFI_PULSE_DURATION_US); + pinTest.write(0); + delayMicroseconds(GFI_PULSE_DURATION_US); + } + } +#endif // FT_GFI_RETRY + + + // state transition + if (m_EvseState != prevevsestate) { + if (m_EvseState == EVSE_STATE_A) { // EV not connected + chargingOff(); // turn off charging current + m_Pilot.SetState(PILOT_STATE_P12); + #ifdef KWH_RECORDING + g_WattHours_accumulated = g_WattHours_accumulated + (g_WattSeconds / 3600); + eeprom_write_dword((uint32_t*)EOFS_KWH_ACCUMULATED,g_WattHours_accumulated); + #endif // KWH_RECORDING +#ifdef CHARGE_LIMIT + SetChargeLimit(0); +#endif // CHARGE_LIMIT +#ifdef TIME_LIMIT + SetTimeLimit(0); +#endif // TIME_LIMIT + } + else if (m_EvseState == EVSE_STATE_B) { // connected + chargingOff(); // turn off charging current + m_Pilot.SetPWM(m_CurrentCapacity); + #ifdef KWH_RECORDING + if (prevevsestate == EVSE_STATE_A) { + g_WattSeconds = 0; + } + #endif + } + else if (m_EvseState == EVSE_STATE_C) { + m_Pilot.SetPWM(m_CurrentCapacity); +#ifdef FT_GFI_LOCKOUT + for(int i = 0; i < GFI_TEST_CYCLES; i++) { + pinTest.write(1); + delayMicroseconds(GFI_PULSE_DURATION_US); + pinTest.write(0); + delayMicroseconds(GFI_PULSE_DURATION_US); + } + g_OBD.LcdMsg("Closing","Relay"); + delay(150); +#endif // FT_GFI_LOCKOUT + +#ifdef UL_GFI_SELFTEST + // test GFI before closing relay + if (GfiSelfTestEnabled() && m_Gfi.SelfTest()) { + // GFI test failed - hard fault + m_EvseState = EVSE_STATE_GFI_TEST_FAILED; + HardFault(); + } +#endif // UL_GFI_SELFTEST + + chargingOn(); // turn on charging current + #ifdef KWH_RECORDING + if (prevevsestate == EVSE_STATE_A) { + g_WattSeconds = 0; + } + #endif + } + else if (m_EvseState == EVSE_STATE_D) { + // vent required not supported + chargingOff(); // turn off charging current + m_Pilot.SetState(PILOT_STATE_P12); + } + else if (m_EvseState == EVSE_STATE_GFCI_FAULT) { + // vehicle state F + chargingOff(); // turn off charging current + m_Pilot.SetState(PILOT_STATE_N12); + } +#ifdef TEMPERATURE_MONITORING + else if (m_EvseState == EVSE_STATE_OVER_TEMPERATURE) { + // vehicle state Over Teperature within the EVSE + m_Pilot.SetState(PILOT_STATE_P12); // Signal the EV to pause, high current should cease within five seconds + + while ((millis()-curms) < 5000) { + wdt_reset(); + } + chargingOff(); // open the EVSE relays hopefully the EV has already disconnected by now by the J1772 specification + m_Pilot.SetState(PILOT_STATE_N12); // This will tell the EV that the EVSE has major problems requiring disconnecting from the EV + HardFault(); + } +#endif //TEMPERATURE_MONITORING + else if (m_EvseState == EVSE_STATE_DIODE_CHK_FAILED) { + chargingOff(); // turn off charging current + // must leave pilot on so we can keep checking + // N.B. J1772 specifies to go to State F (-12V) but we can't do that + // and keep checking + m_Pilot.SetPWM(m_CurrentCapacity); + } + else if (m_EvseState == EVSE_STATE_NO_GROUND) { + // Ground not detected + chargingOff(); // turn off charging current + m_Pilot.SetState(PILOT_STATE_N12); + } + else if (m_EvseState == EVSE_STATE_STUCK_RELAY) { + // Stuck relay detected + chargingOff(); // turn off charging current + m_Pilot.SetState(PILOT_STATE_N12); +#ifdef UL_COMPLIANT + // per discussion w/ UL Fred Reyes 20150217 + // always hard fault stuck relay + HardFault(); +#endif // UL_COMPLIANT + } + else { + m_Pilot.SetState(PILOT_STATE_P12); + chargingOff(); // turn off charging current + } + +#ifdef RAPI + g_ERP.sendEvseState(); +#endif // RAPI +#ifdef SERIALCLI + if (SerDbgEnabled()) { + g_CLI.print_P(PSTR("state: ")); + Serial.print((int)prevevsestate); + g_CLI.print_P(PSTR("->")); + Serial.print((int)m_EvseState); + g_CLI.print_P(PSTR(" p ")); + Serial.print(plow); + g_CLI.print_P(PSTR(" ")); + Serial.println(phigh); + } +#endif //#ifdef SERIALCLI + + } // state transition + +#ifdef UL_COMPLIANT + if (!nofault && (prevevsestate == EVSE_STATE_C)) { + // if fault happens immediately (within 2 sec) after charging starts, hard fault + if ((curms - m_ChargeOnTimeMS) <= 2000) { + HardFault(); + } + } +#endif // UL_COMPLIANT + + m_PrevEvseState = prevevsestate; + +#ifdef VOLTMETER + ReadVoltmeter(); +#endif // VOLTMETER +#ifdef AMMETER + if (((m_EvseState == EVSE_STATE_C) && (m_CurrentScaleFactor > 0)) || AmmeterCalEnabled()) { + + readAmmeter(); + uint32_t ma = MovingAverage(m_AmmeterReading); + if (ma != 0xffffffff) { + m_ChargingCurrent = ma * m_CurrentScaleFactor - m_AmmeterCurrentOffset; // subtract it + if (m_ChargingCurrent < 0) { + m_ChargingCurrent = 0; + } + g_OBD.SetAmmeterDirty(1); + } + } +#endif // AMMETER + if (m_EvseState == EVSE_STATE_C) { + m_ElapsedChargeTimePrev = m_ElapsedChargeTime; + m_ElapsedChargeTime = now() - m_ChargeOnTime; + +#ifdef TEMPERATURE_MONITORING + if (m_ElapsedChargeTime != m_ElapsedChargeTimePrev) { + g_TempMonitor.Read(); + if (!g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_THROTTLE_DOWN ) || // any sensor reaching threshold trips action + (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) || + (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // Throttle back the L2 current advice to the EV + g_TempMonitor.SetOverTemperature(1); + SetCurrentCapacity(g_TempMonitor.m_ampacity / 2,0,1); // set L2 to the throttled back level + } + + if (g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_RESTORE_AMPERAGE ) && // all sensors need to show return to lower levels + (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ) && + (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ))) { // restore the original L2 current advice to the EV + g_TempMonitor.SetOverTemperature(0); + SetCurrentCapacity(g_TempMonitor.m_ampacity,0,1); // set L2 to the user's original setting for L2 current + } + + + if (!g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_SHUTDOWN ) || // any sensor reaching threshold trips action + (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ) || + (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ))) { // Throttle back the L2 current advice to the EV + g_TempMonitor.SetOverTemperatureShutdown(1); + SetCurrentCapacity(g_TempMonitor.m_ampacity / 4,0,1); + } + + if (g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_THROTTLE_DOWN ) && // all sensors need to show return to lower levels + (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) && + (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // restore the throttled down current advice to the EV since things have cooled down again + g_TempMonitor.SetOverTemperatureShutdown(0); + SetCurrentCapacity(g_TempMonitor.m_ampacity / 2,0,1); // set L2 to the throttled back level + m_Pilot.SetPWM(m_CurrentCapacity); + } + } +#endif // TEMPERATURE_MONITORING +#ifdef CHARGE_LIMIT + if (m_chargeLimit && (g_WattSeconds >= 3600000 * (uint32_t)m_chargeLimit)) { + SetChargeLimit(0); // reset charge limit + Sleep(); + } +#endif +#ifdef TIME_LIMIT + if (m_timeLimit) { + // must call millis() below because curms is sampled before transition to + // to State C, so m_ChargeOnTimeMS will be > curms from the start + if ((millis() - m_ChargeOnTimeMS) >= (15lu*60000lu * (unsigned long)m_timeLimit)) { + SetTimeLimit(0); // reset time limit + Sleep(); + } + } +#endif + } +} + +#ifdef CALIBRATE +// read ADC values and get min/max/avg for pilot steady high/low states +void J1772EVSEController::Calibrate(PCALIB_DATA pcd) +{ + uint16_t pmax,pmin,pavg,nmax,nmin,navg; + + for (int l=0;l < 2;l++) { + int reading; + uint32_t tot = 0; + uint16_t plow = 1023; + uint16_t phigh = 0; + uint16_t avg = 0; + m_Pilot.SetState(l ? PILOT_STATE_N12 : PILOT_STATE_P12); + + delay(250); // wait for stabilization + + // 1x = 114us 20x = 2.3ms 100x = 11.3ms + int i; + for (i=0;i < 1000;i++) { + reading = adcPilot.read(); // measures pilot voltage + + if (reading > phigh) { + phigh = reading; + } + else if (reading < plow) { + plow = reading; + } + + tot += reading; + } + avg = tot / i; + + if (l) { + nmax = phigh; + nmin = plow; + navg = avg; + } + else { + pmax = phigh; + pmin = plow; + pavg = avg; + } + } + pcd->m_pMax = pmax; + pcd->m_pAvg = pavg; + pcd->m_pMin = pmin; + pcd->m_nMax = nmax; + pcd->m_nAvg = navg; + pcd->m_nMin = nmin; +} +#endif // CALIBRATE + +int J1772EVSEController::SetCurrentCapacity(uint8_t amps,uint8_t updatelcd,uint8_t nosave) +{ + int rc = 0; + uint8_t maxcurrentcap = (GetCurSvcLevel() == 1) ? MAX_CURRENT_CAPACITY_L1 : MAX_CURRENT_CAPACITY_L2; + + if ((amps >= MIN_CURRENT_CAPACITY) && (amps <= maxcurrentcap)) { + m_CurrentCapacity = amps; + } + else if (amps < MIN_CURRENT_CAPACITY) { + m_CurrentCapacity = MIN_CURRENT_CAPACITY; + rc = 1; + } + else { + m_CurrentCapacity = maxcurrentcap; + rc = 2; + } + + if (!nosave) { + eeprom_write_byte((uint8_t*)((GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),(byte)m_CurrentCapacity); + } + + if (m_Pilot.GetState() == PILOT_STATE_PWM) { + m_Pilot.SetPWM(m_CurrentCapacity); + } + + if (updatelcd) { + g_OBD.Update(OBD_UPD_FORCE); + } + + return rc; +} + +#ifdef VOLTMETER +void J1772EVSEController::SetVoltmeter(uint16_t scale,uint32_t offset) +{ + m_VoltScaleFactor = scale; + eeprom_write_word((uint16_t*)EOFS_VOLT_SCALE_FACTOR,scale); + m_VoltOffset = offset; + eeprom_write_dword((uint32_t*)EOFS_VOLT_OFFSET,offset); +} + +uint32_t J1772EVSEController::ReadVoltmeter() +{ + unsigned int peak = 0; + for(uint32_t start_time = millis(); (millis() - start_time) < VOLTMETER_POLL_INTERVAL; ) { + unsigned int val = adcVoltMeter.read(); + if (val > peak) peak = val; + } + m_Voltage = ((uint32_t)peak) * ((uint32_t)m_VoltScaleFactor) + m_VoltOffset; + return m_Voltage; +} +#endif // VOLTMETER + + +//-- end J1772EVSEController diff --git a/J1772EvseController.h b/J1772EvseController.h new file mode 100644 index 00000000..123348ef --- /dev/null +++ b/J1772EvseController.h @@ -0,0 +1,336 @@ +#pragma once +/* + * This file is part of Open EVSE. + + * Open EVSE 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 3, or (at your option) + * any later version. + + * Open EVSE 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 Open EVSE; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +// EVSE states for m_EvseState +#define EVSE_STATE_UNKNOWN 0x00 +#define EVSE_STATE_A 0x01 // vehicle state A 12V - not connected +#define EVSE_STATE_B 0x02 // vehicle state B 9V - connected, ready +#define EVSE_STATE_C 0x03 // vehicle state C 6V - charging +#define EVSE_STATE_D 0x04 // vehicle state D 3V - vent required +#define EVSE_STATE_DIODE_CHK_FAILED 0x05 // diode check failed +#define EVSE_STATE_GFCI_FAULT 0x06 // GFCI fault +#define EVSE_STATE_NO_GROUND 0x07 //bad ground +#define EVSE_STATE_STUCK_RELAY 0x08 //stuck relay +#define EVSE_STATE_GFI_TEST_FAILED 0x09 // GFI self-test failure +#define EVSE_STATE_OVER_TEMPERATURE 0x0A // over temperature error shutdown +#define EVSE_STATE_SLEEPING 0xfe // waiting for timer +#define EVSE_STATE_DISABLED 0xff // disabled + +typedef struct threshdata { + uint16_t m_ThreshAB; // state A -> B + uint16_t m_ThreshBC; // state B -> C + uint16_t m_ThreshCD; // state C -> D + uint16_t m_ThreshD; // state D + uint16_t m_ThreshDS; // diode short +} THRESH_DATA,*PTHRESH_DATA; + +typedef struct calibdata { + uint16_t m_pMax; + uint16_t m_pAvg; + uint16_t m_pMin; + uint16_t m_nMax; + uint16_t m_nAvg; + uint16_t m_nMin; +} CALIB_DATA,*PCALIB_DATA; + + + +// J1772EVSEController m_wFlags bits - saved to EEPROM +#define ECF_L2 0x0001 // service level 2 +#define ECF_DIODE_CHK_DISABLED 0x0002 // no diode check +#define ECF_VENT_REQ_DISABLED 0x0004 // no vent required state +#define ECF_GND_CHK_DISABLED 0x0008 // no chk for ground fault +#define ECF_STUCK_RELAY_CHK_DISABLED 0x0010 // no chk for stuck relay +#define ECF_AUTO_SVC_LEVEL_DISABLED 0x0020 // auto detect svc level - requires ADVPWR +// Ability set the EVSE for manual button press to start charging - GoldServe +#define ECF_AUTO_START_DISABLED 0x0040 // no auto start charging +#define ECF_SERIAL_DBG 0x0080 // enable debugging messages via serial +#define ECF_MONO_LCD 0x0100 // monochrome LCD backlight +#define ECF_GFI_TEST_DISABLED 0x0200 // no GFI self test +#define ECF_DEFAULT 0x0000 + +// J1772EVSEController volatile m_bVFlags bits - not saved to EEPROM +#define ECVF_AUTOSVCLVL_SKIPPED 0x01 // auto svc level test skipped during post +#define ECVF_AMMETER_CAL 0x10 // ammeter calibration mode +#define ECVF_NOGND_TRIPPED 0x20 // no ground has tripped at least once +#define ECVF_CHARGING_ON 0x40 // charging relay is closed +#define ECVF_GFI_TRIPPED 0x80 // gfi has tripped at least once +#define ECVF_DEFAULT 0x00 + +class J1772EVSEController { + J1772Pilot m_Pilot; +#ifdef GFI + Gfi m_Gfi; + unsigned long m_GfiTimeout; + unsigned long m_GfiRetryCnt; + uint8_t m_GfiTripCnt; +#endif // GFI + AdcPin adcPilot; +#ifdef CURRENT_PIN + AdcPin adcCurrent; +#endif +#ifdef VOLTMETER_PIN + AdcPin adcVoltMeter; +#endif + + DigitalPin pinCharging; +#ifdef CHARGING2_REG + DigitalPin pinCharging2; +#endif +#ifdef CHARGINGAC_REG + DigitalPin pinChargingAC; +#endif +#ifdef ACLINE1_REG + DigitalPin pinAC1; +#endif +#ifdef ACLINE2_REG + DigitalPin pinAC2; +#endif +#ifdef SLEEP_STATUS_REG + DigitalPin pinSleepStatus; +#endif +#ifdef ADVPWR + unsigned long m_NoGndStart; + unsigned long m_NoGndRetryCnt; + uint8_t m_NoGndTripCnt; + unsigned long m_StuckRelayStartTimeMS; + uint8_t m_StuckRelayTripCnt; +#endif // ADVPWR + uint16_t m_wFlags; // ECF_xxx + uint8_t m_bVFlags; // ECVF_xxx + THRESH_DATA m_ThreshData; + uint8_t m_EvseState; + uint8_t m_PrevEvseState; + uint8_t m_TmpEvseState; + unsigned long m_TmpEvseStateStart; + uint8_t m_CurrentCapacity; // max amps we can output + unsigned long m_ChargeOnTimeMS; // millis() when relay last closed + unsigned long m_ChargeOffTimeMS; // millis() when relay last opened + time_t m_ChargeOnTime; // unixtime when relay last closed + time_t m_ChargeOffTime; // unixtime when relay last opened + time_t m_ElapsedChargeTime; + time_t m_ElapsedChargeTimePrev; + +#ifdef ADVPWR +// power states for doPost() (active low) +#define both 0 +#define L1on 1 +#define L2on 2 +#define none 3 +// service states for doPost() +#define UD 0 // undefined +#define L1 1 // L1 +#define L2 2 // L2 +#define OG 3 // open ground +#define SR 4 // stuck relay +#define FG 5 // GFI fault + + uint8_t doPost(); +#endif // ADVPWR + void chargingOn(); + void chargingOff(); + uint8_t chargingIsOn() { return m_bVFlags & ECVF_CHARGING_ON; } + void setFlags(uint16_t flags) { + m_wFlags |= flags; + } + void clrFlags(uint16_t flags) { + m_wFlags &= ~flags; + } + +#ifdef AMMETER + unsigned long m_AmmeterReading; + int32_t m_ChargingCurrent; + int16_t m_AmmeterCurrentOffset; + int16_t m_CurrentScaleFactor; +#ifdef CHARGE_LIMIT + uint8_t m_chargeLimit; // kWh +#endif +#ifdef TIME_LIMIT + uint8_t m_timeLimit; // minutes * 15 +#endif + + void readAmmeter(); +#endif // AMMETER +#ifdef VOLTMETER + uint16_t m_VoltScaleFactor; + uint32_t m_VoltOffset; + uint32_t m_Voltage; // mV +#endif // VOLTMETER + +public: + J1772EVSEController(); + void Init(); + void Update(); // read sensors + void Enable(); + void Disable(); // panic stop - open relays abruptly + void Sleep(); // graceful stop - e.g. waiting for timer to fire- give the EV time to stop charging first + void LoadThresholds(); + + uint16_t GetFlags() { return m_wFlags; } + uint8_t GetState() { + return m_EvseState; + } + uint8_t GetPrevState() { + return m_PrevEvseState; + } + int StateTransition() { + return (m_EvseState != m_PrevEvseState) ? 1 : 0; + } + + void SaveEvseFlags() { + eeprom_write_word((uint16_t *)EOFS_FLAGS,m_wFlags); + } + + uint8_t GetCurrentCapacity() { + return m_CurrentCapacity; + } + int SetCurrentCapacity(uint8_t amps,uint8_t updatelcd=0,uint8_t nosave=0); + //int GetCurrentReading() { return m_CurrentReading; } + //float GetCurrentAmps(); + time_t GetElapsedChargeTime() { + return m_ElapsedChargeTime; + } + time_t GetElapsedChargeTimePrev() { + return m_ElapsedChargeTimePrev; + } + time_t GetChargeOffTime() { + return m_ChargeOffTime; + } + void Calibrate(PCALIB_DATA pcd); + uint8_t GetCurSvcLevel() { + return (m_wFlags & ECF_L2) ? 2 : 1; + } + void SetSvcLevel(uint8_t svclvl,uint8_t updatelcd=0); + PTHRESH_DATA GetThreshData() { + return &m_ThreshData; + } + uint8_t DiodeCheckEnabled() { + return (m_wFlags & ECF_DIODE_CHK_DISABLED) ? 0 : 1; + } + void EnableDiodeCheck(uint8_t tf); + uint8_t VentReqEnabled() { + return (m_wFlags & ECF_VENT_REQ_DISABLED) ? 0 : 1; + } + void EnableVentReq(uint8_t tf); + void SaveSettings(); +#ifdef ADVPWR + uint8_t GndChkEnabled() { + return (m_wFlags & ECF_GND_CHK_DISABLED) ? 0 : 1; + } + void EnableGndChk(uint8_t tf); + void EnableStuckRelayChk(uint8_t tf); + uint8_t StuckRelayChkEnabled() { + return (m_wFlags & ECF_STUCK_RELAY_CHK_DISABLED) ? 0 : 1; + } + uint8_t AutoSvcLevelEnabled() { return (m_wFlags & ECF_AUTO_SVC_LEVEL_DISABLED) ? 0 : 1; } + void EnableAutoSvcLevel(uint8_t tf); + void SetNoGndTripped(); + uint8_t NoGndTripped() { return m_bVFlags & ECVF_NOGND_TRIPPED; } + + void SetAutoSvcLvlSkipped(uint8_t tf) { + if (tf) m_bVFlags |= ECVF_AUTOSVCLVL_SKIPPED; + else m_bVFlags &= ~ECVF_AUTOSVCLVL_SKIPPED; + } + uint8_t AutoSvcLvlSkipped() { return m_bVFlags & ECVF_AUTOSVCLVL_SKIPPED; } + + + uint8_t ReadACPins(); +#endif // ADVPWR + + void HardFault(); + +#ifdef GFI + void SetGfiTripped(); + uint8_t GfiTripped() { return m_bVFlags & ECVF_GFI_TRIPPED; } +#ifdef GFI_SELFTEST + uint8_t GfiSelfTestEnabled() { + return (m_wFlags & ECF_GFI_TEST_DISABLED) ? 0 : 1; + } + void EnableGfiSelfTest(uint8_t tf); +#endif +#endif // GFI + uint8_t SerDbgEnabled() { + return (m_wFlags & ECF_SERIAL_DBG) ? 1 : 0; + } + // Function to suppport Auto Start feature - GoldServe +#ifdef MANUALSTART + void EnableAutoStart(uint8_t tf); + uint8_t AutoStartEnabled() { + return (m_wFlags & ECF_AUTO_START_DISABLED) ? 0 : 1; + } +#endif //ifdef MANUALSTART + void EnableSerDbg(uint8_t tf); +#ifdef RGBLCD + int SetBacklightType(uint8_t t,uint8_t update=1); // BKL_TYPE_XXX +#endif // RGBLCD + +#ifdef VOLTMETER + uint16_t GetVoltScaleFactor() { return m_VoltScaleFactor; } + uint32_t GetVoltOffset() { return m_VoltOffset; } + void SetVoltmeter(uint16_t scale,uint32_t offset); + uint32_t ReadVoltmeter(); + int32_t GetVoltage() { return m_Voltage; } +#else + uint32_t GetVoltage() { return (uint32_t)-1; } +#endif // VOLTMETER +#ifdef AMMETER + int32_t GetChargingCurrent() { return m_ChargingCurrent; } + int16_t GetAmmeterCurrentOffset() { return m_AmmeterCurrentOffset; } + int16_t GetCurrentScaleFactor() { return m_CurrentScaleFactor; } + void SetAmmeterCurrentOffset(int16_t offset) { + m_AmmeterCurrentOffset = offset; + eeprom_write_word((uint16_t*)EOFS_AMMETER_CURR_OFFSET,offset); + } + void SetCurrentScaleFactor(int16_t scale) { + m_CurrentScaleFactor = scale; + eeprom_write_word((uint16_t*)EOFS_CURRENT_SCALE_FACTOR,scale); + } + uint8_t AmmeterCalEnabled() { + return (m_bVFlags & ECVF_AMMETER_CAL) ? 1 : 0; + } + void EnableAmmeterCal(uint8_t tf) { + if (tf) { + m_bVFlags |= ECVF_AMMETER_CAL; + } + else { + m_bVFlags &= ~ECVF_AMMETER_CAL; + } + } +#ifdef CHARGE_LIMIT + void SetChargeLimit(uint8_t kwh) { m_chargeLimit = kwh; } + uint8_t GetChargeLimit() { return m_chargeLimit; } +#endif // CHARGE_LIMIT +#ifdef TIME_LIMIT + uint8_t SetTimeLimit(uint8_t minutes) { m_timeLimit = minutes; } + uint8_t GetTimeLimit() { return m_timeLimit; } +#endif // TIME_LIMIT +#else // !AMMETER + int32_t GetChargingCurrent() { return -1; } +#endif + void ReadPilot(int *plow,int *phigh,int loopcnt=PILOT_LOOP_CNT); + void ProcessInputs(uint8_t nosleeptoggle); + void Reboot(); +#ifdef SHOW_DISABLED_TESTS + void ShowDisabledTests(); +#endif +}; + +extern J1772EVSEController g_EvseController; diff --git a/J1772Pilot.cpp b/J1772Pilot.cpp new file mode 100644 index 00000000..ff8a3330 --- /dev/null +++ b/J1772Pilot.cpp @@ -0,0 +1,145 @@ +/* + * This file is part of Open EVSE. + + * Open EVSE 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 3, or (at your option) + * any later version. + + * Open EVSE 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 Open EVSE; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "open_evse.h" + + +#define TOP ((F_CPU / 2000000) * 1000) // for 1KHz (=1000us period) + +void J1772Pilot::Init() +{ +#ifdef PAFC_PWM + // set up Timer for phase & frequency correct PWM + TCCR1A = 0; // set up Control Register A + ICR1 = TOP; + // WGM13 -> select P&F mode CS10 -> prescaler = 1 + TCCR1B = _BV(WGM13) | _BV(CS10); + +#if (PILOT_IDX == 1) // PB1 + DDRB |= _BV(PORTB1); + TCCR1A |= _BV(COM1A1); +#else // PB2 + DDRB |= _BV(PORTB2); + TCCR1A |= _BV(COM1B1); +#endif // PILOT_IDX +#else // fast PWM + pin.init(PILOT_REG,PILOT_IDX,DigitalPin::OUT); +#endif + + SetState(PILOT_STATE_P12); // turns the pilot on 12V steady state +} + + +// no PWM pilot signal - steady state +// PILOT_STATE_P12 = steady +12V (EVSE_STATE_A - VEHICLE NOT CONNECTED) +// PILOT_STATE_N12 = steady -12V (EVSE_STATE_F - FAULT) +void J1772Pilot::SetState(PILOT_STATE state) +{ + AutoCriticalSection asc; + +#ifdef PAFC_PWM + if (state == PILOT_STATE_P12) { +#if (PILOT_IDX == 1) + OCR1A = TOP; +#else + OCR1B = TOP; +#endif + } + else { +#if (PILOT_IDX == 1) // PB1 + OCR1A = 0; +#else // PB2 + OCR1B = 0; +#endif + } +#else // fast PWM + TCCR1A = 0; //disable pwm by turning off COM1A1,COM1A0,COM1B1,COM1B0 + pin.write((state == PILOT_STATE_P12) ? 1 : 0); +#endif // PAFC_PWM + + m_State = state; +} + + +// set EVSE current capacity in Amperes +// duty cycle +// outputting a 1KHz square wave to digital pin 10 via Timer 1 +// +int J1772Pilot::SetPWM(int amps) +{ + +#ifdef PAFC_PWM + // duty cycle = OCR1A(B) / ICR1 * 100 % + + unsigned cnt; + if ((amps >= 6) && (amps <= 51)) { + // amps = (duty cycle %) X 0.6 + cnt = amps * (TOP/60); + } else if ((amps > 51) && (amps <= 80)) { + // amps = (duty cycle % - 64) X 2.5 + cnt = (amps * (TOP/250)) + (64*(TOP/100)); + } + else { + return 1; + } + + +#if (PILOT_IDX == 1) // PB1 + OCR1A = cnt; +#else // PB2 + OCR1B = cnt; +#endif + + m_State = PILOT_STATE_PWM; + + return 0; +#else // fast PWM + uint8_t ocr1b = 0; + if ((amps >= 6) && (amps <= 51)) { + ocr1b = 25 * amps / 6 - 1; // J1772 states "Available current = (duty cycle %) X 0.6" + } else if ((amps > 51) && (amps <= 80)) { + ocr1b = amps + 159; // J1772 states "Available current = (duty cycle % - 64) X 2.5" + } + else { + return 1; // error + } + + if (ocr1b) { + AutoCriticalSection asc; + // Timer1 initialization: + // 16MHz / 64 / (OCR1A+1) / 2 on digital 9 + // 16MHz / 64 / (OCR1A+1) on digital 10 + // 1KHz variable duty cycle on digital 10, 500Hz fixed 50% on digital 9 + // pin 10 duty cycle = (OCR1B+1)/(OCR1A+1) + + TCCR1A = _BV(COM1A0) | _BV(COM1B1) | _BV(WGM11) | _BV(WGM10); + TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11) | _BV(CS10); + OCR1A = 249; + + // 10% = 24 , 96% = 239 + OCR1B = ocr1b; + + m_State = PILOT_STATE_PWM; + return 0; + } + else { // !duty + // invalid amps + return 1; + } +#endif // PAFC_PWM +} diff --git a/J1772Pilot.h b/J1772Pilot.h new file mode 100644 index 00000000..4e78b2cc --- /dev/null +++ b/J1772Pilot.h @@ -0,0 +1,38 @@ +#pragma once +/* + * This file is part of Open EVSE. + + * Open EVSE 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 3, or (at your option) + * any later version. + + * Open EVSE 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 Open EVSE; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +typedef enum { + PILOT_STATE_P12,PILOT_STATE_PWM,PILOT_STATE_N12} +PILOT_STATE; +class J1772Pilot { + PILOT_STATE m_State; +#ifndef PAFC_PWM + DigitalPin pin; +#endif // !PAFC_PWM +public: + J1772Pilot() { + } + void Init(); + void SetState(PILOT_STATE pstate); // P12/N12 + PILOT_STATE GetState() { + return m_State; + } + int SetPWM(int amps); // 12V 1KHz PWM +}; diff --git a/open_evse.h b/open_evse.h index 05c48b5f..e4f6acf1 100644 --- a/open_evse.h +++ b/open_evse.h @@ -800,332 +800,8 @@ class TempMonitor { }; #endif // TEMPERATURE_MONITORING -typedef enum { - PILOT_STATE_P12,PILOT_STATE_PWM,PILOT_STATE_N12} -PILOT_STATE; -class J1772Pilot { - PILOT_STATE m_State; -#ifndef PAFC_PWM - DigitalPin pin; -#endif // !PAFC_PWM -public: - J1772Pilot() { - } - void Init(); - void SetState(PILOT_STATE pstate); // P12/N12 - PILOT_STATE GetState() { - return m_State; - } - int SetPWM(int amps); // 12V 1KHz PWM -}; - -// EVSE states for m_EvseState -#define EVSE_STATE_UNKNOWN 0x00 -#define EVSE_STATE_A 0x01 // vehicle state A 12V - not connected -#define EVSE_STATE_B 0x02 // vehicle state B 9V - connected, ready -#define EVSE_STATE_C 0x03 // vehicle state C 6V - charging -#define EVSE_STATE_D 0x04 // vehicle state D 3V - vent required -#define EVSE_STATE_DIODE_CHK_FAILED 0x05 // diode check failed -#define EVSE_STATE_GFCI_FAULT 0x06 // GFCI fault -#define EVSE_STATE_NO_GROUND 0x07 //bad ground -#define EVSE_STATE_STUCK_RELAY 0x08 //stuck relay -#define EVSE_STATE_GFI_TEST_FAILED 0x09 // GFI self-test failure -#define EVSE_STATE_OVER_TEMPERATURE 0x0A // over temperature error shutdown -#define EVSE_STATE_SLEEPING 0xfe // waiting for timer -#define EVSE_STATE_DISABLED 0xff // disabled - -typedef struct threshdata { - uint16_t m_ThreshAB; // state A -> B - uint16_t m_ThreshBC; // state B -> C - uint16_t m_ThreshCD; // state C -> D - uint16_t m_ThreshD; // state D - uint16_t m_ThreshDS; // diode short -} THRESH_DATA,*PTHRESH_DATA; - -typedef struct calibdata { - uint16_t m_pMax; - uint16_t m_pAvg; - uint16_t m_pMin; - uint16_t m_nMax; - uint16_t m_nAvg; - uint16_t m_nMin; -} CALIB_DATA,*PCALIB_DATA; - - - -// J1772EVSEController m_wFlags bits - saved to EEPROM -#define ECF_L2 0x0001 // service level 2 -#define ECF_DIODE_CHK_DISABLED 0x0002 // no diode check -#define ECF_VENT_REQ_DISABLED 0x0004 // no vent required state -#define ECF_GND_CHK_DISABLED 0x0008 // no chk for ground fault -#define ECF_STUCK_RELAY_CHK_DISABLED 0x0010 // no chk for stuck relay -#define ECF_AUTO_SVC_LEVEL_DISABLED 0x0020 // auto detect svc level - requires ADVPWR -// Ability set the EVSE for manual button press to start charging - GoldServe -#define ECF_AUTO_START_DISABLED 0x0040 // no auto start charging -#define ECF_SERIAL_DBG 0x0080 // enable debugging messages via serial -#define ECF_MONO_LCD 0x0100 // monochrome LCD backlight -#define ECF_GFI_TEST_DISABLED 0x0200 // no GFI self test -#define ECF_DEFAULT 0x0000 - -// J1772EVSEController volatile m_bVFlags bits - not saved to EEPROM -#define ECVF_AUTOSVCLVL_SKIPPED 0x01 // auto svc level test skipped during post -#define ECVF_AMMETER_CAL 0x10 // ammeter calibration mode -#define ECVF_NOGND_TRIPPED 0x20 // no ground has tripped at least once -#define ECVF_CHARGING_ON 0x40 // charging relay is closed -#define ECVF_GFI_TRIPPED 0x80 // gfi has tripped at least once -#define ECVF_DEFAULT 0x00 - -class J1772EVSEController { - J1772Pilot m_Pilot; -#ifdef GFI - Gfi m_Gfi; - unsigned long m_GfiTimeout; - unsigned long m_GfiRetryCnt; - uint8_t m_GfiTripCnt; -#endif // GFI - AdcPin adcPilot; -#ifdef CURRENT_PIN - AdcPin adcCurrent; -#endif -#ifdef VOLTMETER_PIN - AdcPin adcVoltMeter; -#endif - - DigitalPin pinCharging; -#ifdef CHARGING2_REG - DigitalPin pinCharging2; -#endif -#ifdef CHARGINGAC_REG - DigitalPin pinChargingAC; -#endif -#ifdef ACLINE1_REG - DigitalPin pinAC1; -#endif -#ifdef ACLINE2_REG - DigitalPin pinAC2; -#endif -#ifdef SLEEP_STATUS_REG - DigitalPin pinSleepStatus; -#endif -#ifdef ADVPWR - unsigned long m_NoGndStart; - unsigned long m_NoGndRetryCnt; - uint8_t m_NoGndTripCnt; - unsigned long m_StuckRelayStartTimeMS; - uint8_t m_StuckRelayTripCnt; -#endif // ADVPWR - uint16_t m_wFlags; // ECF_xxx - uint8_t m_bVFlags; // ECVF_xxx - THRESH_DATA m_ThreshData; - uint8_t m_EvseState; - uint8_t m_PrevEvseState; - uint8_t m_TmpEvseState; - unsigned long m_TmpEvseStateStart; - uint8_t m_CurrentCapacity; // max amps we can output - unsigned long m_ChargeOnTimeMS; // millis() when relay last closed - unsigned long m_ChargeOffTimeMS; // millis() when relay last opened - time_t m_ChargeOnTime; // unixtime when relay last closed - time_t m_ChargeOffTime; // unixtime when relay last opened - time_t m_ElapsedChargeTime; - time_t m_ElapsedChargeTimePrev; - -#ifdef ADVPWR -// power states for doPost() (active low) -#define both 0 -#define L1on 1 -#define L2on 2 -#define none 3 -// service states for doPost() -#define UD 0 // undefined -#define L1 1 // L1 -#define L2 2 // L2 -#define OG 3 // open ground -#define SR 4 // stuck relay -#define FG 5 // GFI fault - - uint8_t doPost(); -#endif // ADVPWR - void chargingOn(); - void chargingOff(); - uint8_t chargingIsOn() { return m_bVFlags & ECVF_CHARGING_ON; } - void setFlags(uint16_t flags) { - m_wFlags |= flags; - } - void clrFlags(uint16_t flags) { - m_wFlags &= ~flags; - } - -#ifdef AMMETER - unsigned long m_AmmeterReading; - int32_t m_ChargingCurrent; - int16_t m_AmmeterCurrentOffset; - int16_t m_CurrentScaleFactor; -#ifdef CHARGE_LIMIT - uint8_t m_chargeLimit; // kWh -#endif -#ifdef TIME_LIMIT - uint8_t m_timeLimit; // minutes * 15 -#endif - - void readAmmeter(); -#endif // AMMETER -#ifdef VOLTMETER - uint16_t m_VoltScaleFactor; - uint32_t m_VoltOffset; - uint32_t m_Voltage; // mV -#endif // VOLTMETER - -public: - J1772EVSEController(); - void Init(); - void Update(); // read sensors - void Enable(); - void Disable(); // panic stop - open relays abruptly - void Sleep(); // graceful stop - e.g. waiting for timer to fire- give the EV time to stop charging first - void LoadThresholds(); - - uint16_t GetFlags() { return m_wFlags; } - uint8_t GetState() { - return m_EvseState; - } - uint8_t GetPrevState() { - return m_PrevEvseState; - } - int StateTransition() { - return (m_EvseState != m_PrevEvseState) ? 1 : 0; - } - uint8_t GetCurrentCapacity() { - return m_CurrentCapacity; - } - int SetCurrentCapacity(uint8_t amps,uint8_t updatelcd=0,uint8_t nosave=0); - //int GetCurrentReading() { return m_CurrentReading; } - //float GetCurrentAmps(); - time_t GetElapsedChargeTime() { - return m_ElapsedChargeTime; - } - time_t GetElapsedChargeTimePrev() { - return m_ElapsedChargeTimePrev; - } - time_t GetChargeOffTime() { - return m_ChargeOffTime; - } - void Calibrate(PCALIB_DATA pcd); - uint8_t GetCurSvcLevel() { - return (m_wFlags & ECF_L2) ? 2 : 1; - } - void SetSvcLevel(uint8_t svclvl,uint8_t updatelcd=0); - PTHRESH_DATA GetThreshData() { - return &m_ThreshData; - } - uint8_t DiodeCheckEnabled() { - return (m_wFlags & ECF_DIODE_CHK_DISABLED) ? 0 : 1; - } - void EnableDiodeCheck(uint8_t tf); - uint8_t VentReqEnabled() { - return (m_wFlags & ECF_VENT_REQ_DISABLED) ? 0 : 1; - } - void EnableVentReq(uint8_t tf); -#ifdef ADVPWR - uint8_t GndChkEnabled() { - return (m_wFlags & ECF_GND_CHK_DISABLED) ? 0 : 1; - } - void EnableGndChk(uint8_t tf); - void EnableStuckRelayChk(uint8_t tf); - uint8_t StuckRelayChkEnabled() { - return (m_wFlags & ECF_STUCK_RELAY_CHK_DISABLED) ? 0 : 1; - } - uint8_t AutoSvcLevelEnabled() { return (m_wFlags & ECF_AUTO_SVC_LEVEL_DISABLED) ? 0 : 1; } - void EnableAutoSvcLevel(uint8_t tf); - void SetNoGndTripped(); - uint8_t NoGndTripped() { return m_bVFlags & ECVF_NOGND_TRIPPED; } - - void SetAutoSvcLvlSkipped(uint8_t tf) { - if (tf) m_bVFlags |= ECVF_AUTOSVCLVL_SKIPPED; - else m_bVFlags &= ~ECVF_AUTOSVCLVL_SKIPPED; - } - uint8_t AutoSvcLvlSkipped() { return m_bVFlags & ECVF_AUTOSVCLVL_SKIPPED; } - - - uint8_t ReadACPins(); -#endif // ADVPWR - - void HardFault(); - -#ifdef GFI - void SetGfiTripped(); - uint8_t GfiTripped() { return m_bVFlags & ECVF_GFI_TRIPPED; } -#ifdef GFI_SELFTEST - uint8_t GfiSelfTestEnabled() { - return (m_wFlags & ECF_GFI_TEST_DISABLED) ? 0 : 1; - } - void EnableGfiSelfTest(uint8_t tf); -#endif -#endif // GFI - uint8_t SerDbgEnabled() { - return (m_wFlags & ECF_SERIAL_DBG) ? 1 : 0; - } - // Function to suppport Auto Start feature - GoldServe -#ifdef MANUALSTART - void EnableAutoStart(uint8_t tf); - uint8_t AutoStartEnabled() { - return (m_wFlags & ECF_AUTO_START_DISABLED) ? 0 : 1; - } -#endif //ifdef MANUALSTART - void EnableSerDbg(uint8_t tf); -#ifdef RGBLCD - int SetBacklightType(uint8_t t,uint8_t update=1); // BKL_TYPE_XXX -#endif // RGBLCD - -#ifdef VOLTMETER - uint16_t GetVoltScaleFactor() { return m_VoltScaleFactor; } - uint32_t GetVoltOffset() { return m_VoltOffset; } - void SetVoltmeter(uint16_t scale,uint32_t offset); - uint32_t ReadVoltmeter(); - int32_t GetVoltage() { return m_Voltage; } -#else - uint32_t GetVoltage() { return (uint32_t)-1; } -#endif // VOLTMETER -#ifdef AMMETER - int32_t GetChargingCurrent() { return m_ChargingCurrent; } - int16_t GetAmmeterCurrentOffset() { return m_AmmeterCurrentOffset; } - int16_t GetCurrentScaleFactor() { return m_CurrentScaleFactor; } - void SetAmmeterCurrentOffset(int16_t offset) { - m_AmmeterCurrentOffset = offset; - eeprom_write_word((uint16_t*)EOFS_AMMETER_CURR_OFFSET,offset); - } - void SetCurrentScaleFactor(int16_t scale) { - m_CurrentScaleFactor = scale; - eeprom_write_word((uint16_t*)EOFS_CURRENT_SCALE_FACTOR,scale); - } - uint8_t AmmeterCalEnabled() { - return (m_bVFlags & ECVF_AMMETER_CAL) ? 1 : 0; - } - void EnableAmmeterCal(uint8_t tf) { - if (tf) { - m_bVFlags |= ECVF_AMMETER_CAL; - } - else { - m_bVFlags &= ~ECVF_AMMETER_CAL; - } - } -#ifdef CHARGE_LIMIT - void SetChargeLimit(uint8_t kwh) { m_chargeLimit = kwh; } - uint8_t GetChargeLimit() { return m_chargeLimit; } -#endif // CHARGE_LIMIT -#ifdef TIME_LIMIT - uint8_t SetTimeLimit(uint8_t minutes) { m_timeLimit = minutes; } - uint8_t GetTimeLimit() { return m_timeLimit; } -#endif // TIME_LIMIT -#else // !AMMETER - int32_t GetChargingCurrent() { return -1; } -#endif - void ReadPilot(int *plow,int *phigh,int loopcnt=PILOT_LOOP_CNT); - void ProcessInputs(uint8_t nosleeptoggle); - void Reboot(); -#ifdef SHOW_DISABLED_TESTS - void ShowDisabledTests(); -#endif -}; +#include "J1772Pilot.h" +#include "J1772EvseController.h" #ifdef BTN_MENU #define BTN_STATE_OFF 0 @@ -1416,7 +1092,6 @@ class BtnHandler { #ifdef DELAYTIMER // Start Delay Timer class definition - GoldServe -void SaveSettings(); class DelayTimer { uint8_t m_DelayTimerEnabled; uint8_t m_StartTimerHour; @@ -1454,14 +1129,14 @@ class DelayTimer { m_StartTimerMin = min; eeprom_write_byte((uint8_t*)EOFS_TIMER_START_HOUR, m_StartTimerHour); eeprom_write_byte((uint8_t*)EOFS_TIMER_START_MIN, m_StartTimerMin); - SaveSettings(); + g_EvseController.SaveSettings(); }; void SetStopTimer(uint8_t hour, uint8_t min){ m_StopTimerHour = hour; m_StopTimerMin = min; eeprom_write_byte((uint8_t*)EOFS_TIMER_STOP_HOUR, m_StopTimerHour); eeprom_write_byte((uint8_t*)EOFS_TIMER_STOP_MIN, m_StopTimerMin); - SaveSettings(); + g_EvseController.SaveSettings(); }; uint8_t IsTimerValid(){ if (m_StartTimerHour || m_StartTimerMin || m_StopTimerHour || m_StopTimerMin){ // Check not all equal 0 @@ -1494,7 +1169,10 @@ extern char g_sSpace[]; #define g_sSpace " " -extern J1772EVSEController g_EvseController; +#ifdef BTN_MENU +extern BtnHandler g_BtnHandler; +extern SettingsMenu g_SettingsMenu; +#endif // BTN_MENU extern OnboardDisplay g_OBD; extern char g_sTmp[TMP_BUF_SIZE]; @@ -1511,4 +1189,81 @@ extern TempMonitor g_TempMonitor; extern const char VERSTR[] PROGMEM; inline void GetVerStr(char *buf) { strcpy_P(buf,VERSTR); } + +#if defined(BTN_MENU) || defined(SHOW_DISABLED_TESTS) +extern const char g_psSettings[] PROGMEM; +extern const char g_psSetup[] PROGMEM; +extern const char g_psSvcLevel[] PROGMEM; +extern const char g_psMaxCurrent[] PROGMEM; +extern const char g_psDiodeCheck[] PROGMEM; +extern const char g_psVentReqChk[] PROGMEM; +#ifdef RGBLCD +extern const char g_psBklType[] PROGMEM; +#endif +#ifdef ADVPWR +extern const char g_psGndChk[] PROGMEM; +extern const char g_psRlyChk[] PROGMEM; +#endif // ADVPWR +#ifdef GFI_SELFTEST +extern const char g_psGfiTest[] PROGMEM; +#endif +#endif // BTN_MENU || SHOW_DISABLED_TEST +#ifdef BTN_MENU +extern const char g_psReset[] PROGMEM; +extern const char g_psExit[] PROGMEM; +// Add additional strings - GoldServe +#ifdef AUTOSTART_MENU +extern const char g_psAutoStart[] PROGMEM; +#endif //#ifdef AUTOSTART_MENU +#ifdef DELAYTIMER_MENU +extern const char g_psRTC[] PROGMEM; +extern const char g_psRTC_Month[] PROGMEM; +extern const char g_psRTC_Day[] PROGMEM; +extern const char g_psRTC_Year[] PROGMEM; +extern const char g_psRTC_Hour[] PROGMEM; +extern const char g_psRTC_Minute[] PROGMEM; +extern const char g_psDelayTimer[] PROGMEM; +extern const char g_psDelayTimerStartHour[] PROGMEM; +extern const char g_psDelayTimerStartMin[] PROGMEM; +extern const char g_psDelayTimerStopHour[] PROGMEM; +extern const char g_psDelayTimerStopMin[] PROGMEM; +#endif // DELAYTIMER_MENU +#ifdef CHARGE_LIMIT +extern const char g_psChargeLimit[] PROGMEM; +#endif // CHARGE_LIMIT +#ifdef TIME_LIMIT +extern const char g_psTimeLimit[] PROGMEM; +#endif // TIME_LIMIT +#endif // BTN_MENU + +#ifdef LCD16X2 +#ifdef ADVPWR +extern const char g_psPwrOn[] PROGMEM; +extern const char g_psSelfTest[] PROGMEM; +extern const char g_psAutoDetect[] PROGMEM; +extern const char g_psLevel1[] PROGMEM; +extern const char g_psLevel2[] PROGMEM; +extern const char g_psTestFailed[] PROGMEM; +#endif // ADVPWR +extern const char g_psEvseError[] PROGMEM; +extern const char g_psSvcReq[] PROGMEM; +extern const char g_psVentReq[] PROGMEM; +extern const char g_psDiodeChkFailed[] PROGMEM; +extern const char g_psGfciFault[] PROGMEM; +extern const char g_psGfci[] PROGMEM; +#ifdef TEMPERATURE_MONITORING +extern const char g_psTemperatureFault[] PROGMEM; +#endif +extern const char g_psNoGround[] PROGMEM; +extern const char g_psStuckRelay[] PROGMEM; +extern const char g_psDisabled[] PROGMEM; +extern const char g_psWaiting[] PROGMEM; +extern const char g_psSleeping[] PROGMEM; +extern const char g_psEvConnected[] PROGMEM; +#ifdef SHOW_DISABLED_TESTS +extern const char g_psDisabledTests[] PROGMEM; +#endif +#endif // LCD16X2 + + #include "rapi_proc.h" diff --git a/open_evse.ino b/open_evse.ino index a58c0a20..3f7dec39 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -147,6 +147,7 @@ ChargeLimitMenu g_ChargeLimitMenu; TimeLimitMenu g_TimeLimitMenu; #endif // TIME_LIMIT + Menu *g_SettingsMenuList[] = { #ifdef TIME_LIMIT &g_TimeLimitMenu, @@ -196,8 +197,6 @@ BtnHandler g_BtnHandler; char g_sTmp[TMP_BUF_SIZE]; -THRESH_DATA g_DefaultThreshData = {875,780,690,0,260}; -J1772EVSEController g_EvseController; OnboardDisplay g_OBD; // Instantiate RTC and Delay Timer - GoldServe @@ -327,18 +326,6 @@ void wdt_init(void) return; } -void SaveEvseFlags() -{ - uint16_t flags = g_EvseController.GetFlags(); - eeprom_write_word((uint16_t *)EOFS_FLAGS,flags); -} - -void SaveSettings() -{ - // n.b. should we add dirty bits so we only write the changed values? or should we just write them on the fly when necessary? - eeprom_write_byte((uint8_t *)((g_EvseController.GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),(byte)g_EvseController.GetCurrentCapacity()); - SaveEvseFlags(); -} #ifdef TEMPERATURE_MONITORING void TempMonitor::Init() @@ -882,1667 +869,7 @@ void OnboardDisplay::Update(int8_t updmode) } -#ifdef GFI -// interrupt service routing -void gfi_isr() -{ - g_EvseController.SetGfiTripped(); -} - - -void Gfi::Init() -{ - pin.init(GFI_REG,GFI_IDX,DigitalPin::INP); - // GFI triggers on rising edge - attachInterrupt(GFI_INTERRUPT,gfi_isr,RISING); - -#ifdef GFI_SELFTEST - pinTest.init(GFITEST_REG,GFITEST_IDX,DigitalPin::OUT); -#endif - - Reset(); -} - - -//RESET GFI LOGIC -void Gfi::Reset() -{ - WDT_RESET(); - -#ifdef GFI_SELFTEST - testInProgress = 0; - testSuccess = 0; -#endif // GFI_SELFTEST - - if (pin.read()) m_GfiFault = 1; // if interrupt pin is high, set fault - else m_GfiFault = 0; -} - -#ifdef GFI_SELFTEST - -uint8_t Gfi::SelfTest() -{ - testInProgress = 1; - testSuccess = 0; - for(int i=0; i < GFI_TEST_CYCLES; i++) { - pinTest.write(1); - delayMicroseconds(GFI_PULSE_DURATION_US); - pinTest.write(0); - delayMicroseconds(GFI_PULSE_DURATION_US); - if (testSuccess) break; // no need to keep trying. - } - - // wait for GFI pin to clear - do { - delay(50); - WDT_RESET(); - } - while(pin.read()); - -#ifndef OPENEVSE_2 - // sometimes getting spurious GFI faults when testing just before closing - // relay. - // wait a little more for everything to settle down - // this delay is needed only if 10uF cap is in the circuit, which makes the circuit - // temporarily overly sensitive to trips until it discharges - delay(1000); -#endif // OPENEVSE_2 - - m_GfiFault = 0; - testInProgress = 0; - - return !testSuccess; -} -#endif // GFI_SELFTEST -#endif // GFI -//-- begin J1772Pilot - -#define TOP ((F_CPU / 2000000) * 1000) // for 1KHz (=1000us period) -void J1772Pilot::Init() -{ -#ifdef PAFC_PWM - // set up Timer for phase & frequency correct PWM - TCCR1A = 0; // set up Control Register A - ICR1 = TOP; - // WGM13 -> select P&F mode CS10 -> prescaler = 1 - TCCR1B = _BV(WGM13) | _BV(CS10); - -#if (PILOT_IDX == 1) // PB1 - DDRB |= _BV(PORTB1); - TCCR1A |= _BV(COM1A1); -#else // PB2 - DDRB |= _BV(PORTB2); - TCCR1A |= _BV(COM1B1); -#endif // PILOT_IDX -#else // fast PWM - pin.init(PILOT_REG,PILOT_IDX,DigitalPin::OUT); -#endif - - SetState(PILOT_STATE_P12); // turns the pilot on 12V steady state -} - - -// no PWM pilot signal - steady state -// PILOT_STATE_P12 = steady +12V (EVSE_STATE_A - VEHICLE NOT CONNECTED) -// PILOT_STATE_N12 = steady -12V (EVSE_STATE_F - FAULT) -void J1772Pilot::SetState(PILOT_STATE state) -{ - AutoCriticalSection asc; - -#ifdef PAFC_PWM - if (state == PILOT_STATE_P12) { -#if (PILOT_IDX == 1) - OCR1A = TOP; -#else - OCR1B = TOP; -#endif - } - else { -#if (PILOT_IDX == 1) // PB1 - OCR1A = 0; -#else // PB2 - OCR1B = 0; -#endif - } -#else // fast PWM - TCCR1A = 0; //disable pwm by turning off COM1A1,COM1A0,COM1B1,COM1B0 - pin.write((state == PILOT_STATE_P12) ? 1 : 0); -#endif // PAFC_PWM - - m_State = state; -} - - -// set EVSE current capacity in Amperes -// duty cycle -// outputting a 1KHz square wave to digital pin 10 via Timer 1 -// -int J1772Pilot::SetPWM(int amps) -{ - -#ifdef PAFC_PWM - // duty cycle = OCR1A(B) / ICR1 * 100 % - - unsigned cnt; - if ((amps >= 6) && (amps <= 51)) { - // amps = (duty cycle %) X 0.6 - cnt = amps * (TOP/60); - } else if ((amps > 51) && (amps <= 80)) { - // amps = (duty cycle % - 64) X 2.5 - cnt = (amps * (TOP/250)) + (64*(TOP/100)); - } - else { - return 1; - } - - -#if (PILOT_IDX == 1) // PB1 - OCR1A = cnt; -#else // PB2 - OCR1B = cnt; -#endif - - m_State = PILOT_STATE_PWM; - - return 0; -#else // fast PWM - uint8_t ocr1b = 0; - if ((amps >= 6) && (amps <= 51)) { - ocr1b = 25 * amps / 6 - 1; // J1772 states "Available current = (duty cycle %) X 0.6" - } else if ((amps > 51) && (amps <= 80)) { - ocr1b = amps + 159; // J1772 states "Available current = (duty cycle % - 64) X 2.5" - } - else { - return 1; // error - } - - if (ocr1b) { - AutoCriticalSection asc; - // Timer1 initialization: - // 16MHz / 64 / (OCR1A+1) / 2 on digital 9 - // 16MHz / 64 / (OCR1A+1) on digital 10 - // 1KHz variable duty cycle on digital 10, 500Hz fixed 50% on digital 9 - // pin 10 duty cycle = (OCR1B+1)/(OCR1A+1) - - TCCR1A = _BV(COM1A0) | _BV(COM1B1) | _BV(WGM11) | _BV(WGM10); - TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11) | _BV(CS10); - OCR1A = 249; - - // 10% = 24 , 96% = 239 - OCR1B = ocr1b; - - m_State = PILOT_STATE_PWM; - return 0; - } - else { // !duty - // invalid amps - return 1; - } -#endif // PAFC_PWM -} - -//-- end J1772Pilot - -//-- begin J1772EVSEController - -J1772EVSEController::J1772EVSEController() : - adcPilot(VOLT_PIN) -#ifdef CURRENT_PIN - , adcCurrent(CURRENT_PIN) -#endif -#ifdef VOLTMETER_PIN - , adcVoltMeter(VOLTMETER_PIN) -#endif -{ -} - -// use watchdog to perform a reset -void J1772EVSEController::Reboot() -{ - m_Pilot.SetState(PILOT_STATE_P12); - -#ifdef LCD16X2 - g_OBD.LcdPrint_P(1,PSTR("Resetting...")); -#endif - - if (chargingIsOn()) { - // give the EV some time to open its contactor in response to P12 - delay(3000); - } - - // hardware reset by forcing watchdog to timeout - wdt_enable(WDTO_1S); // enable watchdog timer - delay(1500); -} - - - -#ifdef SHOW_DISABLED_TESTS -void J1772EVSEController::ShowDisabledTests() -{ - if (m_wFlags & (ECF_DIODE_CHK_DISABLED| - ECF_VENT_REQ_DISABLED| - ECF_GND_CHK_DISABLED| - ECF_STUCK_RELAY_CHK_DISABLED| - ECF_GFI_TEST_DISABLED)) { - g_OBD.LcdSetBacklightColor(YELLOW); - - if (!DiodeCheckEnabled()) { - g_OBD.LcdMsg_P(g_psDisabledTests,g_psDiodeCheck); - delay(SHOW_DISABLED_DELAY); - } - if (!VentReqEnabled()) { - g_OBD.LcdMsg_P(g_psDisabledTests,g_psVentReqChk); - delay(SHOW_DISABLED_DELAY); - } -#ifdef ADVPWR - if (!GndChkEnabled()) { - g_OBD.LcdMsg_P(g_psDisabledTests,g_psGndChk); - delay(SHOW_DISABLED_DELAY); - } - if (!StuckRelayChkEnabled()) { - g_OBD.LcdMsg_P(g_psDisabledTests,g_psRlyChk); - delay(SHOW_DISABLED_DELAY); - } -#endif // ADVPWR -#ifdef GFI_SELFTEST - if (!GfiSelfTestEnabled()) { - g_OBD.LcdMsg_P(g_psDisabledTests,g_psGfiTest); - delay(SHOW_DISABLED_DELAY); - } -#endif // GFI_SELFTEST - - g_OBD.LcdSetBacklightColor(WHITE); - } -} -#endif //SHOW_DISABLED_TESTS - -void J1772EVSEController::chargingOn() -{ // turn on charging current - pinCharging.write(1); -#ifdef CHARGING2_REG - pinCharging2.write(1); -#endif -#ifdef CHARGINGAC_REG - pinChargingAC.write(1); -#endif - m_bVFlags |= ECVF_CHARGING_ON; - - m_ChargeOnTime = now(); - m_ChargeOnTimeMS = millis(); -} - -void J1772EVSEController::chargingOff() -{ // turn off charging current - pinCharging.write(0); -#ifdef CHARGING2_REG - pinCharging2.write(0); -#endif -#ifdef CHARGINGAC_REG - pinChargingAC.write(0); -#endif - m_bVFlags &= ~ECVF_CHARGING_ON; - - m_ChargeOffTime = now(); - m_ChargeOffTimeMS = millis(); - -#ifdef AMMETER - m_ChargingCurrent = 0; -#endif -} - -void J1772EVSEController::HardFault() -{ - g_SettingsMenu.EnableExitItem(0); - g_OBD.Update(OBD_UPD_HARDFAULT); - while (1) ProcessInputs(1); // spin forever or until user resets via menu -} - -#ifdef GFI -inline void J1772EVSEController::SetGfiTripped() -{ -#ifdef GFI_SELFTEST - if (m_Gfi.SelfTestInProgress()) { - m_Gfi.SetTestSuccess(); - return; - } -#endif - m_bVFlags |= ECVF_GFI_TRIPPED; - - // this is repeated Update(), but we want to keep latency as low as possible - // for safety so we do it here first anyway - chargingOff(); // turn off charging current - // turn off the pilot - m_Pilot.SetState(PILOT_STATE_N12); - - m_Gfi.SetFault(); - // the rest of the logic will be handled in Update() -} -#endif // GFI - -void J1772EVSEController::EnableDiodeCheck(uint8_t tf) -{ - if (tf) { - m_wFlags &= ~ECF_DIODE_CHK_DISABLED; - } - else { - m_wFlags |= ECF_DIODE_CHK_DISABLED; - } - SaveEvseFlags(); -} - -#ifdef GFI_SELFTEST -void J1772EVSEController::EnableGfiSelfTest(uint8_t tf) -{ - if (tf) { - m_wFlags &= ~ECF_GFI_TEST_DISABLED; - } - else { - m_wFlags |= ECF_GFI_TEST_DISABLED; - } - SaveEvseFlags(); -} -#endif - -void J1772EVSEController::EnableVentReq(uint8_t tf) -{ - if (tf) { - m_wFlags &= ~ECF_VENT_REQ_DISABLED; - } - else { - m_wFlags |= ECF_VENT_REQ_DISABLED; - } - SaveEvseFlags(); -} - -#ifdef ADVPWR -void J1772EVSEController::EnableGndChk(uint8_t tf) -{ - if (tf) { - m_wFlags &= ~ECF_GND_CHK_DISABLED; - } - else { - m_NoGndRetryCnt = 0; - m_NoGndStart = 0; - m_wFlags |= ECF_GND_CHK_DISABLED; - } - SaveEvseFlags(); -} - -void J1772EVSEController::EnableStuckRelayChk(uint8_t tf) -{ - if (tf) { - m_wFlags &= ~ECF_STUCK_RELAY_CHK_DISABLED; - } - else { - m_wFlags |= ECF_STUCK_RELAY_CHK_DISABLED; - } - SaveEvseFlags(); -} - -void J1772EVSEController::EnableAutoSvcLevel(uint8_t tf) -{ - if (tf) { - m_wFlags &= ~ECF_AUTO_SVC_LEVEL_DISABLED; - } - else { - m_wFlags |= ECF_AUTO_SVC_LEVEL_DISABLED; - } - SaveEvseFlags(); -} - - -#endif // ADVPWR - -// Functions to support Auto Start feature - GoldServe -#ifdef MANUALSTART -void J1772EVSEController::EnableAutoStart(uint8_t tf) -{ - if (tf) { - m_wFlags &= ~ECF_AUTO_START_DISABLED; - } - else { - m_wFlags |= ECF_AUTO_START_DISABLED; - } - SaveEvseFlags(); -} -#endif //#ifdef MANUALSTART -void J1772EVSEController::EnableSerDbg(uint8_t tf) -{ - if (tf) { - m_wFlags |= ECF_SERIAL_DBG; - } - else { - m_wFlags &= ~ECF_SERIAL_DBG; - } - SaveEvseFlags(); -} - -#ifdef RGBLCD -int J1772EVSEController::SetBacklightType(uint8_t t,uint8_t update) -{ -#ifdef RGBLCD - g_OBD.LcdSetBacklightType(t,update); - if (t == BKL_TYPE_MONO) m_wFlags |= ECF_MONO_LCD; - else m_wFlags &= ~ECF_MONO_LCD; - SaveEvseFlags(); -#endif // RGBLCD - return 0; -} -#endif // RGBLCD -void J1772EVSEController::Enable() -{ - if ((m_EvseState == EVSE_STATE_DISABLED)|| - (m_EvseState == EVSE_STATE_SLEEPING)) { -#ifdef SLEEP_STATUS_REG - if (m_EvseState == EVSE_STATE_SLEEPING) { - pinSleepStatus.write(0); - } -#endif // SLEEP_STATUS_REG - - m_PrevEvseState = EVSE_STATE_DISABLED; - m_EvseState = EVSE_STATE_UNKNOWN; - m_Pilot.SetState(PILOT_STATE_P12); - } -} - -void J1772EVSEController::Disable() -{ - if (m_EvseState != EVSE_STATE_DISABLED) { - m_Pilot.SetState(PILOT_STATE_N12); - m_EvseState = EVSE_STATE_DISABLED; - // panic stop so we won't wait for EV to open its contacts first - chargingOff(); - g_OBD.Update(OBD_UPD_FORCE); -#ifdef RAPI - g_ERP.sendEvseState(); -#endif // RAPI - } -} - - -void J1772EVSEController::Sleep() -{ - if (m_EvseState != EVSE_STATE_SLEEPING) { - m_Pilot.SetState(PILOT_STATE_P12); - m_EvseState = EVSE_STATE_SLEEPING; -#ifdef SLEEP_STATUS_REG - pinSleepStatus.write(1); -#endif // SLEEP_STATUS_REG - - g_OBD.Update(OBD_UPD_FORCE); -#ifdef RAPI - g_ERP.sendEvseState(); -#endif // RAPI - // try to prevent arcing of our relay by waiting for EV to open its contacts first - // use the charge end time variable temporarily to count down - // when to open the contacts in Update() - m_ChargeOffTimeMS = millis(); - } -} - -void J1772EVSEController::LoadThresholds() -{ - memcpy(&m_ThreshData,&g_DefaultThreshData,sizeof(m_ThreshData)); -} - -void J1772EVSEController::SetSvcLevel(uint8_t svclvl,uint8_t updatelcd) -{ -#ifdef SERIALCLI - if (SerDbgEnabled()) { - g_CLI.printlnn(); - g_CLI.print_P(PSTR("SetSvcLevel: "));Serial.println((int)svclvl); - } -#endif //#ifdef SERIALCLI - if (svclvl == 2) { - m_wFlags |= ECF_L2; // set to Level 2 - } - else { - svclvl = 1; - m_wFlags &= ~ECF_L2; // set to Level 1 - } - - SaveEvseFlags(); - - uint8_t ampacity = eeprom_read_byte((uint8_t*)((svclvl == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2)); - - if ((ampacity == 0xff) || (ampacity == 0)) { - ampacity = (svclvl == 1) ? DEFAULT_CURRENT_CAPACITY_L1 : DEFAULT_CURRENT_CAPACITY_L2; - } - - if (ampacity < MIN_CURRENT_CAPACITY) { - ampacity = MIN_CURRENT_CAPACITY; - } - else { - if (svclvl == 1) { // L1 - if (ampacity > MAX_CURRENT_CAPACITY_L1) { - ampacity = MAX_CURRENT_CAPACITY_L1; - } - } - else { - if (ampacity > MAX_CURRENT_CAPACITY_L2) { - ampacity = MAX_CURRENT_CAPACITY_L2; - } - } - } - - - LoadThresholds(); - - SetCurrentCapacity(ampacity); - - if (updatelcd) { - g_OBD.Update(OBD_UPD_FORCE); - } -} - -#ifdef ADVPWR - -// acpinstate : bit 1 = AC pin 1, bit0 = AC pin 2 -uint8_t J1772EVSEController::ReadACPins() -{ -#ifndef OPENEVSE_2 -#ifdef SAMPLE_ACPINS - // - // AC pins are active low, so we set them high - // and then if voltage is detected on a pin, it will go low - // - uint8_t ac1 = 2; - uint8_t ac2 = 1; - unsigned long startms = millis(); - - do { - if (ac1 && !pinAC1.read()) { - ac1 = 0; - } - if (ac2 && !pinAC2.read()) { - ac2 = 0; - } - } while ((ac1 || ac2) && ((millis() - startms) < AC_SAMPLE_MS)); - return ac1 | ac2; -#else // !SAMPLE_ACPINS - return (pinAC1.read() ? 2 : 0) | (pinAC2.read() ? 1 : 0); -#endif // SAMPLE_ACPINS -#else - // For OpenEVSE II, there is only ACLINE1_PIN, and it is - // active *high*. '3' is the value for "both AC lines dead" - // and '0' is the value for "both AC lines live". There is - // no need to sample, as the hardware does a peak-hold. - return (pinAC1.read() ? 0 : 3); -#endif // OPENEVSE_2 -} - - - -uint8_t J1772EVSEController::doPost() -{ - WDT_RESET(); - - uint8_t RelayOff, Relay1, Relay2; //Relay Power status - uint8_t svcState = UD; // service state = undefined - -#ifdef SERIALCLI - if (SerDbgEnabled()) { - g_CLI.print_P(PSTR("POST start...")); - } -#endif //#ifdef SERIALCLI - - - m_Pilot.SetState(PILOT_STATE_P12); //check to see if EV is plugged in - - g_OBD.SetRedLed(1); -#ifdef LCD16X2 //Adafruit RGB LCD - g_OBD.LcdMsg_P(g_psPwrOn,g_psSelfTest); -#endif //Adafruit RGB LCD - - if (AutoSvcLevelEnabled()) { -#ifdef OPENEVSE_2 - // For OpenEVSE II, there is a voltmeter for auto L1/L2. - uint32_t long ac_volts = ReadVoltmeter(); - if (ac_volts > L2_VOLTAGE_THRESHOLD) { - svcState = L2; - } else { - svcState = L1; - } -#ifdef SERIALCLI - if (SerDbgEnabled()) { - g_CLI.print_P(PSTR("AC millivolts: "));Serial.println(ac_volts); - g_CLI.print_P(PSTR("SvcState: "));Serial.println((int)svcState); - } -#endif //#ifdef SERIALCLI -#ifdef LCD16X2 - g_OBD.LcdMsg_P(g_psAutoDetect,(svcState == L2) ? g_psLevel2 : g_psLevel1); -#endif //LCD16x2 - -#else //!OPENEVSE_2 - - delay(150); // delay reading for stable pilot before reading - int reading = adcPilot.read(); //read pilot -#ifdef SERIALCLI - if (SerDbgEnabled()) { - g_CLI.printlnn(); - g_CLI.print_P(PSTR("Pilot: "));Serial.println((int)reading); - } -#endif //#ifdef SERIALCLI - - m_Pilot.SetState(PILOT_STATE_N12); - if (reading > 900) { // IF EV is not connected its Okay to open the relay the do the L1/L2 and ground Check - - // save state with both relays off - for stuck relay state - RelayOff = ReadACPins(); - - // save state with Relay 1 on - pinCharging.write(1); -#ifdef CHARGINGAC_REG - pinChargingAC.write(1); -#endif - delay(RelaySettlingTime); - Relay1 = ReadACPins(); - pinCharging.write(0); -#ifdef CHARGINGAC_REG - pinChargingAC.write(0); -#endif - delay(RelaySettlingTime); //allow relay to fully open before running other tests - - // save state for Relay 2 on -#ifdef CHARGING2_REG - pinCharging2.write(1); -#endif - delay(RelaySettlingTime); - Relay2 = ReadACPins(); -#ifdef CHARGING2_REG - pinCharging2.write(0); -#endif - delay(RelaySettlingTime); //allow relay to fully open before running other tests - - // decide input power state based on the status read on L1 and L2 - // either 2 SPST or 1 DPST relays can be configured - // valid svcState is L1 - one hot, L2 both hot, OG - open ground both off, SR - stuck relay when shld be off - // - if (RelayOff == none) { // relay not stuck on when off - switch ( Relay1 ) { - case ( both ): // - if ( Relay2 == none ) svcState = L2; - if (StuckRelayChkEnabled()) { - if ( Relay2 != none ) svcState = SR; - } - break; - case ( none ): // - if (GndChkEnabled()) { - if ( Relay2 == none ) svcState = OG; - } - if ( Relay2 == both ) svcState = L2; - if ( Relay2 == L1 || Relay2 == L2 ) svcState = L1; - break; - case ( L1on ): // L1 or L2 - case ( L2on ): - if (StuckRelayChkEnabled()) { - if ( Relay2 != none ) svcState = SR; - } - if ( Relay2 == none ) svcState = L1; - if ( (Relay1 == L1on) && (Relay2 == L2on)) svcState = L2; - if ( (Relay1 == L2on) && (Relay2 == L1on)) svcState = L2; - break; - } // end switch - } - else { // Relay stuck on - if (StuckRelayChkEnabled()) { - svcState = SR; - } - } -#ifdef SERIALCLI - if (SerDbgEnabled()) { - g_CLI.print_P(PSTR("RelayOff: "));Serial.println((int)RelayOff); - g_CLI.print_P(PSTR("Relay1: "));Serial.println((int)Relay1); - g_CLI.print_P(PSTR("Relay2: "));Serial.println((int)Relay2); - g_CLI.print_P(PSTR("SvcState: "));Serial.println((int)svcState); - } -#endif //#ifdef SERIALCLI - - // update LCD -#ifdef LCD16X2 - if (svcState == L1) g_OBD.LcdMsg_P(g_psAutoDetect,g_psLevel1); - if (svcState == L2) g_OBD.LcdMsg_P(g_psAutoDetect,g_psLevel2); - if ((svcState == OG) || (svcState == SR)) { - g_OBD.LcdSetBacklightColor(RED); - } - if (svcState == OG) g_OBD.LcdMsg_P(g_psTestFailed,g_psNoGround); - if (svcState == SR) g_OBD.LcdMsg_P(g_psTestFailed,g_psStuckRelay); -#endif // LCD16X2 - } // endif test, no EV is plugged in - else { - // since we can't auto detect, for safety's sake, we must set to L1 - svcState = L1; - SetAutoSvcLvlSkipped(1); - // EV connected.. do stuck relay check - goto stuckrelaychk; - } -#endif //#else OPENEVSE_2 - } - else { // ! AutoSvcLevelEnabled - stuckrelaychk: - if (StuckRelayChkEnabled()) { - RelayOff = ReadACPins(); - if ((RelayOff & 3) != 3) { - svcState = SR; -#ifdef LCD16X2 - g_OBD.LcdMsg_P(g_psTestFailed,g_psStuckRelay); -#endif // LCD16X2 - } - } - } // endif AutoSvcLevelEnabled - -#ifdef GFI_SELFTEST - // only run GFI test if no fault detected above - if (((svcState == UD)||(svcState == L1)||(svcState == L2)) && - GfiSelfTestEnabled()) { - if (m_Gfi.SelfTest()) { -#ifdef LCD16X2 - g_OBD.LcdMsg_P(g_psTestFailed,g_psGfci); -#endif // LCD16X2 - svcState = FG; - } - } -#endif - - if ((svcState == OG)||(svcState == SR)||(svcState == FG)) { - g_OBD.LcdSetBacklightColor(RED); - g_OBD.SetGreenLed(0); - g_OBD.SetRedLed(1); - } - else { - g_OBD.SetRedLed(0); - } - m_Pilot.SetState(PILOT_STATE_P12); - -#ifdef SERIALCLI - if (SerDbgEnabled()) { - g_CLI.print_P(PSTR("POST result: ")); - Serial.println((int)svcState); - } -#endif //#ifdef SERIALCLI - - WDT_RESET(); - - return svcState; -} -#endif // ADVPWR - -void J1772EVSEController::ProcessInputs(uint8_t nosleeptoggle) -{ -#ifdef RAPI - g_ERP.doCmd(); -#endif -#ifdef SERIALCLI - g_CLI.getInput(); -#endif // SERIALCLI -#ifdef BTN_MENU - g_BtnHandler.ChkBtn(nosleeptoggle); -#endif -} - -void J1772EVSEController::Init() -{ - // read settings from EEPROM - uint16_t rflgs = eeprom_read_word((uint16_t*)EOFS_FLAGS); - -#ifdef RGBLCD - if ((rflgs != 0xffff) && (rflgs & ECF_MONO_LCD)) { - g_OBD.LcdSetBacklightType(BKL_TYPE_MONO); - } -#endif // RGBLCD - - pinCharging.init(CHARGING_REG,CHARGING_IDX,DigitalPin::OUT); -#ifdef CHARGING2_REG - pinCharging2.init(CHARGING2_REG,CHARGING2_IDX,DigitalPin::OUT); -#endif -#ifdef CHARGINGAC_REG - pinChargingAC.init(CHARGINGAC_REG,CHARGINGAC_IDX,DigitalPin::OUT); -#endif -#ifdef ACLINE1_REG - pinAC1.init(ACLINE1_REG,ACLINE1_IDX,DigitalPin::INP_PU); -#endif -#ifdef ACLINE2_REG - pinAC2.init(ACLINE2_REG,ACLINE2_IDX,DigitalPin::INP_PU); -#endif -#ifdef SLEEP_STATUS_REG - pinSleepStatus.init(SLEEP_STATUS_REG,SLEEP_STATUS_IDX,DigitalPin::OUT); -#endif - -#ifdef GFI - m_Gfi.Init(); -#endif // GFI - - chargingOff(); - - m_Pilot.Init(); // init the pilot - - uint8_t svclvl = (uint8_t)DEFAULT_SERVICE_LEVEL; - - if (rflgs == 0xffff) { // uninitialized EEPROM - m_wFlags = ECF_DEFAULT; -#ifdef RGBLCD - if (DEFAULT_LCD_BKL_TYPE == BKL_TYPE_MONO) { - m_wFlags |= ECF_MONO_LCD; - } -#endif // RGBLCD - } - else { - m_wFlags = rflgs; - svclvl = GetCurSvcLevel(); - - } - -#ifdef NOCHECKS - m_wFlags |= ECF_DIODE_CHK_DISABLED|ECF_VENT_REQ_DISABLED|ECF_GND_CHK_DISABLED|ECF_STUCK_RELAY_CHK_DISABLED|ECF_GFI_TEST_DISABLED; -#endif - -#ifdef AMMETER - m_AmmeterCurrentOffset = eeprom_read_word((uint16_t*)EOFS_AMMETER_CURR_OFFSET); - m_CurrentScaleFactor = eeprom_read_word((uint16_t*)EOFS_CURRENT_SCALE_FACTOR); - - if (m_AmmeterCurrentOffset == 0x0000ffff) { - m_AmmeterCurrentOffset = DEFAULT_AMMETER_CURRENT_OFFSET; - } - if (m_CurrentScaleFactor == 0x0000ffff) { - m_CurrentScaleFactor = DEFAULT_CURRENT_SCALE_FACTOR; - } - - m_AmmeterReading = 0; - m_ChargingCurrent = 0; - // m_LastAmmeterReadMs = 0; -#endif // AMMETER - -#ifdef VOLTMETER - m_VoltOffset = eeprom_read_dword((uint32_t*)EOFS_VOLT_OFFSET); - m_VoltScaleFactor = eeprom_read_word((uint16_t*)EOFS_VOLT_SCALE_FACTOR); - - if (m_VoltOffset == 0xffffffff) { - m_VoltOffset = DEFAULT_VOLT_OFFSET; - } - if (m_VoltScaleFactor == 0xffff) { - m_VoltScaleFactor = DEFAULT_VOLT_SCALE_FACTOR; - } -#endif // VOLTMETER - -#ifndef RGBLCD - m_wFlags |= ECF_MONO_LCD; -#endif - - m_bVFlags = ECVF_DEFAULT; -#ifdef GFI - m_GfiRetryCnt = 0; - m_GfiTripCnt = eeprom_read_byte((uint8_t*)EOFS_GFI_TRIP_CNT); - if (m_GfiTripCnt == 255) { - m_GfiTripCnt = 0; - } -#endif // GFI -#ifdef ADVPWR - m_NoGndRetryCnt = 0; - m_NoGndTripCnt = eeprom_read_byte((uint8_t*)EOFS_NOGND_TRIP_CNT); - if (m_NoGndTripCnt != 255) { - m_NoGndTripCnt = 0; - } - - m_StuckRelayStartTimeMS = 0; - m_StuckRelayTripCnt = eeprom_read_byte((uint8_t*)EOFS_STUCK_RELAY_TRIP_CNT); - if (m_StuckRelayTripCnt != 255) { - m_StuckRelayTripCnt = 0; - } - - m_NoGndRetryCnt = 0; - m_NoGndStart = 0; -#endif // ADVPWR - - m_EvseState = EVSE_STATE_UNKNOWN; - m_PrevEvseState = EVSE_STATE_UNKNOWN; - - -#ifdef ADVPWR - -#ifdef FT_READ_AC_PINS - while (1) { - WDT_RESET(); - sprintf(g_sTmp,"%d",(int)ReadACPins()); - g_OBD.LcdMsg("AC Pins",g_sTmp); - } -#endif // FT_READ_AC_PINS - -#ifdef SHOW_DISABLED_TESTS - ShowDisabledTests(); -#endif - - g_SettingsMenu.EnableExitItem(0); - uint8_t fault; - do { - fault = 0; // reset post fault - uint8_t psvclvl = doPost(); // auto detect service level overrides any saved values - - if ((AutoSvcLevelEnabled()) && ((psvclvl == L1) || (psvclvl == L2))) svclvl = psvclvl; //set service level - if ((GndChkEnabled()) && (psvclvl == OG)) { m_EvseState = EVSE_STATE_NO_GROUND; fault = 1;} // set No Ground error - if ((StuckRelayChkEnabled()) && (psvclvl == SR)) { m_EvseState = EVSE_STATE_STUCK_RELAY; fault = 1; } // set Stuck Relay error -#ifdef GFI_SELFTEST - if ((GfiSelfTestEnabled()) && (psvclvl == FG)) { m_EvseState = EVSE_STATE_GFI_TEST_FAILED; fault = 1; } // set GFI test fail error -#endif - if (fault) { -#ifdef UL_COMPLIANT - // UL wants EVSE to hard fault until power cycle if POST fails - while (1) { // spin forever -#endif - unsigned long faultms = millis(); - // wait for GFI_TIMEOUT before retrying POST - while ((millis() - faultms) < GFI_TIMEOUT) { - ProcessInputs(1); - } -#ifdef UL_COMPLIANT - } -#endif - } - } while ( fault && ( m_EvseState == EVSE_STATE_GFI_TEST_FAILED || m_EvseState == EVSE_STATE_NO_GROUND || m_EvseState == EVSE_STATE_STUCK_RELAY )); -#endif // ADVPWR - - g_SettingsMenu.EnableExitItem(1); - - SetSvcLevel(svclvl); - - // Start Manual Start Feature - GoldServe -#ifdef MANUALSTART - if (AutoStartEnabled()){ - Enable(); - } else { - Sleep(); - } -#endif //#ifdef MANUALSTART - // End Manual Start Feature - GoldServe - - g_OBD.SetGreenLed(0); -} - -void J1772EVSEController::ReadPilot(int *plow,int *phigh,int loopcnt) -{ - int pl = 1023; - int ph = 0; - - // 1x = 114us 20x = 2.3ms 100x = 11.3ms - for (int i=0;i < 100;i++) { - int reading = adcPilot.read(); // measures pilot voltage - - if (reading > ph) { - ph = reading; - } - else if (reading < pl) { - pl = reading; - } - } - - *plow = pl; - *phigh = ph; -} - - -//TABLE A1 - PILOT LINE VOLTAGE RANGES (recommended.. adjust as necessary -// Minimum Nominal Maximum -//Positive Voltage, State A 11.40 12.00 12.60 -//Positive Voltage, State B 8.36 9.00 9.56 -//Positive Voltage, State C 5.48 6.00 6.49 -//Positive Voltage, State D 2.62 3.00 3.25 -//Negative Voltage - States B, C, D, and F -11.40 -12.00 -12.60 -void J1772EVSEController::Update() -{ - int plow; - int phigh; - - unsigned long curms = millis(); - - if (m_EvseState == EVSE_STATE_DISABLED) { - m_PrevEvseState = m_EvseState; // cancel state transition - return; - } - else if (m_EvseState == EVSE_STATE_SLEEPING) { - if (chargingIsOn()) { - ReadPilot(&plow,&phigh); - // wait for pilot voltage to go > STATE C. This will happen if - // a) EV reacts and goes back to state B (opens its contacts) - // b) user pulls out the charge connector - // if it doesn't happen within 3 sec, we'll just open our relay anyway - if ((phigh >= m_ThreshData.m_ThreshBC) - || ((curms - m_ChargeOffTimeMS) >= 3000)) { - chargingOff(); -#ifdef FT_SLEEP_DELAY - sprintf(g_sTmp,"SLEEP OPEN %d",(int)phigh); - g_OBD.LcdMsg(g_sTmp,(phigh >= m_ThreshData.m_ThreshBC) ? "THRESH" : "TIMEOUT"); - delay(2000); -#endif - } - } - m_PrevEvseState = m_EvseState; // cancel state transition - return; - } - - uint8_t prevevsestate = m_EvseState; - uint8_t tmpevsestate = EVSE_STATE_UNKNOWN; - uint8_t nofault = 1; - -#ifdef ADVPWR - uint8_t acpinstate = ReadACPins(); - - if (chargingIsOn()) { // relay closed - if ((curms - m_ChargeOnTimeMS) > GROUND_CHK_DELAY) { - // ground check - can only test when relay closed - if (GndChkEnabled() && ((acpinstate & 3) == 3)) { - // bad ground - tmpevsestate = EVSE_STATE_NO_GROUND; - m_EvseState = EVSE_STATE_NO_GROUND; - - chargingOff(); // open the relay - - if ((prevevsestate != EVSE_STATE_NO_GROUND) && (m_NoGndTripCnt < 254)) { - m_NoGndTripCnt++; - eeprom_write_byte((uint8_t*)EOFS_NOGND_TRIP_CNT,m_NoGndTripCnt); - } - m_NoGndStart = curms; - - nofault = 0; - } - - // if EV was plugged in during POST, we couldn't do AutoSvcLevel detection, - // so we had to hardcode L1. During first charge session, we can probe and set to L2 if necessary - if (AutoSvcLvlSkipped() && (m_EvseState == EVSE_STATE_C)) { - if (!acpinstate) { - // set to L2 - SetSvcLevel(2,1); - } - SetAutoSvcLvlSkipped(0); - } - } - } - else { // !chargingIsOn() - relay open - if (prevevsestate == EVSE_STATE_NO_GROUND) { - if (((m_NoGndRetryCnt < GFI_RETRY_COUNT) || (GFI_RETRY_COUNT == 255)) && - ((curms - m_NoGndStart) > GFI_TIMEOUT)) { - m_NoGndRetryCnt++; - } - else { - tmpevsestate = EVSE_STATE_NO_GROUND; - m_EvseState = EVSE_STATE_NO_GROUND; - - nofault = 0; - } - } - else if (StuckRelayChkEnabled()) { // stuck relay check - can test only when relay open - if (((acpinstate & 3) != 3)) { // Stuck Relay reading - if ((prevevsestate != EVSE_STATE_STUCK_RELAY) && !m_StuckRelayStartTimeMS) { //check for first occurence - m_StuckRelayStartTimeMS = curms; // mark start state - } - if ( ( ((curms - m_ChargeOffTimeMS) > STUCK_RELAY_DELAY) && // charge off de-bounce - ((curms - m_StuckRelayStartTimeMS) > STUCK_RELAY_DELAY) ) || // start delay de-bounce - (prevevsestate == EVSE_STATE_STUCK_RELAY) ) { // already in error state - // stuck relay - if ((prevevsestate != EVSE_STATE_STUCK_RELAY) && (m_StuckRelayTripCnt < 254)) { - m_StuckRelayTripCnt++; - eeprom_write_byte((uint8_t*)EOFS_STUCK_RELAY_TRIP_CNT,m_StuckRelayTripCnt); - } - tmpevsestate = EVSE_STATE_STUCK_RELAY; - m_EvseState = EVSE_STATE_STUCK_RELAY; - nofault = 0; - } - } // end of stuck relay reading - else m_StuckRelayStartTimeMS = 0; // not stuck - reset - } // end of StuckRelayChkEnabled - } // end of !chargingIsOn() - relay open -#endif // ADVPWR - -#ifdef GFI - if (m_Gfi.Fault()) { - tmpevsestate = EVSE_STATE_GFCI_FAULT; - m_EvseState = EVSE_STATE_GFCI_FAULT; - - if (prevevsestate != EVSE_STATE_GFCI_FAULT) { - if (m_GfiTripCnt < 254) { - m_GfiTripCnt++; - eeprom_write_byte((uint8_t*)EOFS_GFI_TRIP_CNT,m_GfiTripCnt); - } - m_GfiRetryCnt = 0; - m_GfiTimeout = curms + GFI_TIMEOUT; - } - else if (curms >= m_GfiTimeout) { -#ifdef FT_GFI_RETRY - g_OBD.LcdMsg("Reset","GFI"); - delay(250); -#endif // FT_GFI_RETRY - m_GfiRetryCnt++; - - if ((GFI_RETRY_COUNT != 255) && (m_GfiRetryCnt > GFI_RETRY_COUNT)) { - HardFault(); - } - else { - m_Gfi.Reset(); - m_GfiTimeout = curms + GFI_TIMEOUT; - } - } - - nofault = 0; - } -#endif // GFI - - -#ifdef TEMPERATURE_MONITORING // A state for OverTemp fault - if ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_PANIC) || - (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_PANIC) || - (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_PANIC)) { - tmpevsestate = EVSE_STATE_OVER_TEMPERATURE; - m_EvseState = EVSE_STATE_OVER_TEMPERATURE; - nofault = 0; - } -#endif // TEMPERATURE_MONITORING - - if (nofault) { - if ((prevevsestate == EVSE_STATE_GFCI_FAULT) || - (prevevsestate == EVSE_STATE_OVER_TEMPERATURE) || - (prevevsestate == EVSE_STATE_NO_GROUND) || - (prevevsestate == EVSE_STATE_STUCK_RELAY)) { - // just got out of fault state - pilot back on - m_Pilot.SetState(PILOT_STATE_P12); - prevevsestate = EVSE_STATE_UNKNOWN; - m_EvseState = EVSE_STATE_UNKNOWN; - } - - ReadPilot(&plow,&phigh); - - if (DiodeCheckEnabled() && (m_Pilot.GetState() == PILOT_STATE_PWM) && (plow >= m_ThreshData.m_ThreshDS)) { - // diode check failed - tmpevsestate = EVSE_STATE_DIODE_CHK_FAILED; - } - else if (phigh >= m_ThreshData.m_ThreshAB) { - // 12V EV not connected - tmpevsestate = EVSE_STATE_A; - } - else if (phigh >= m_ThreshData.m_ThreshBC) { - // 9V EV connected, waiting for ready to charge - tmpevsestate = EVSE_STATE_B; - } - else if ((phigh >= m_ThreshData.m_ThreshCD) || - (!VentReqEnabled() && (phigh > m_ThreshData.m_ThreshD))) { - // 6V ready to charge - tmpevsestate = EVSE_STATE_C; - } - else if (phigh > m_ThreshData.m_ThreshD) { - // 3V ready to charge vent required - tmpevsestate = EVSE_STATE_D; - } - else { - tmpevsestate = EVSE_STATE_UNKNOWN; - } - -#ifdef FT_ENDURANCE - if (nofault) { - if (((tmpevsestate == EVSE_STATE_A)||(tmpevsestate == EVSE_STATE_B)) && (g_CycleCnt < 0)) { - g_CycleCnt = 0; - g_CycleHalfStart = curms; - g_CycleState = EVSE_STATE_B; - } - - if (g_CycleCnt >= 0) { - if (g_CycleState == EVSE_STATE_B) { - if ((curms - g_CycleHalfStart) >= 9000) { - g_CycleCnt++; - g_CycleHalfStart = curms; - tmpevsestate = EVSE_STATE_C; - g_CycleState = EVSE_STATE_C; - } - else tmpevsestate = EVSE_STATE_B; - } - else if (g_CycleState == EVSE_STATE_C) { - if ((curms - g_CycleHalfStart) >= 1000) { - g_CycleHalfStart = curms; - tmpevsestate = EVSE_STATE_B; - g_CycleState = EVSE_STATE_B; - } - else tmpevsestate = EVSE_STATE_C; - } - } - } -#endif // FT_ENDURANCE - - // debounce state transitions - if (tmpevsestate != prevevsestate) { - if (tmpevsestate != m_TmpEvseState) { - m_TmpEvseStateStart = curms; - } - else if ((curms - m_TmpEvseStateStart) >= ((tmpevsestate == EVSE_STATE_A) ? DELAY_STATE_TRANSITION_A : DELAY_STATE_TRANSITION)) { - m_EvseState = tmpevsestate; - } - } - } // nofault - - m_TmpEvseState = tmpevsestate; - -#ifdef FT_GFI_RETRY - if (nofault && (prevevsestate == EVSE_STATE_C) && - ((curms - m_ChargeOnTimeMS) > 10000)) { - g_OBD.LcdMsg("Induce","Fault"); - for(int i = 0; i < GFI_TEST_CYCLES; i++) { - pinTest.write(1); - delayMicroseconds(GFI_PULSE_DURATION_US); - pinTest.write(0); - delayMicroseconds(GFI_PULSE_DURATION_US); - } - } -#endif // FT_GFI_RETRY - - - // state transition - if (m_EvseState != prevevsestate) { - if (m_EvseState == EVSE_STATE_A) { // EV not connected - chargingOff(); // turn off charging current - m_Pilot.SetState(PILOT_STATE_P12); - #ifdef KWH_RECORDING - g_WattHours_accumulated = g_WattHours_accumulated + (g_WattSeconds / 3600); - eeprom_write_dword((uint32_t*)EOFS_KWH_ACCUMULATED,g_WattHours_accumulated); - #endif // KWH_RECORDING -#ifdef CHARGE_LIMIT - SetChargeLimit(0); -#endif // CHARGE_LIMIT -#ifdef TIME_LIMIT - SetTimeLimit(0); -#endif // TIME_LIMIT - } - else if (m_EvseState == EVSE_STATE_B) { // connected - chargingOff(); // turn off charging current - m_Pilot.SetPWM(m_CurrentCapacity); - #ifdef KWH_RECORDING - if (prevevsestate == EVSE_STATE_A) { - g_WattSeconds = 0; - } - #endif - } - else if (m_EvseState == EVSE_STATE_C) { - m_Pilot.SetPWM(m_CurrentCapacity); -#ifdef FT_GFI_LOCKOUT - for(int i = 0; i < GFI_TEST_CYCLES; i++) { - pinTest.write(1); - delayMicroseconds(GFI_PULSE_DURATION_US); - pinTest.write(0); - delayMicroseconds(GFI_PULSE_DURATION_US); - } - g_OBD.LcdMsg("Closing","Relay"); - delay(150); -#endif // FT_GFI_LOCKOUT - -#ifdef UL_GFI_SELFTEST - // test GFI before closing relay - if (GfiSelfTestEnabled() && m_Gfi.SelfTest()) { - // GFI test failed - hard fault - m_EvseState = EVSE_STATE_GFI_TEST_FAILED; - HardFault(); - } -#endif // UL_GFI_SELFTEST - - chargingOn(); // turn on charging current - #ifdef KWH_RECORDING - if (prevevsestate == EVSE_STATE_A) { - g_WattSeconds = 0; - } - #endif - } - else if (m_EvseState == EVSE_STATE_D) { - // vent required not supported - chargingOff(); // turn off charging current - m_Pilot.SetState(PILOT_STATE_P12); - } - else if (m_EvseState == EVSE_STATE_GFCI_FAULT) { - // vehicle state F - chargingOff(); // turn off charging current - m_Pilot.SetState(PILOT_STATE_N12); - } -#ifdef TEMPERATURE_MONITORING - else if (m_EvseState == EVSE_STATE_OVER_TEMPERATURE) { - // vehicle state Over Teperature within the EVSE - m_Pilot.SetState(PILOT_STATE_P12); // Signal the EV to pause, high current should cease within five seconds - - while ((millis()-curms) < 5000) { - wdt_reset(); - } - chargingOff(); // open the EVSE relays hopefully the EV has already disconnected by now by the J1772 specification - m_Pilot.SetState(PILOT_STATE_N12); // This will tell the EV that the EVSE has major problems requiring disconnecting from the EV - HardFault(); - } -#endif //TEMPERATURE_MONITORING - else if (m_EvseState == EVSE_STATE_DIODE_CHK_FAILED) { - chargingOff(); // turn off charging current - // must leave pilot on so we can keep checking - // N.B. J1772 specifies to go to State F (-12V) but we can't do that - // and keep checking - m_Pilot.SetPWM(m_CurrentCapacity); - } - else if (m_EvseState == EVSE_STATE_NO_GROUND) { - // Ground not detected - chargingOff(); // turn off charging current - m_Pilot.SetState(PILOT_STATE_N12); - } - else if (m_EvseState == EVSE_STATE_STUCK_RELAY) { - // Stuck relay detected - chargingOff(); // turn off charging current - m_Pilot.SetState(PILOT_STATE_N12); -#ifdef UL_COMPLIANT - // per discussion w/ UL Fred Reyes 20150217 - // always hard fault stuck relay - HardFault(); -#endif // UL_COMPLIANT - } - else { - m_Pilot.SetState(PILOT_STATE_P12); - chargingOff(); // turn off charging current - } - -#ifdef RAPI - g_ERP.sendEvseState(); -#endif // RAPI -#ifdef SERIALCLI - if (SerDbgEnabled()) { - g_CLI.print_P(PSTR("state: ")); - Serial.print((int)prevevsestate); - g_CLI.print_P(PSTR("->")); - Serial.print((int)m_EvseState); - g_CLI.print_P(PSTR(" p ")); - Serial.print(plow); - g_CLI.print_P(PSTR(" ")); - Serial.println(phigh); - } -#endif //#ifdef SERIALCLI - - } // state transition - -#ifdef UL_COMPLIANT - if (!nofault && (prevevsestate == EVSE_STATE_C)) { - // if fault happens immediately (within 2 sec) after charging starts, hard fault - if ((curms - m_ChargeOnTimeMS) <= 2000) { - HardFault(); - } - } -#endif // UL_COMPLIANT - - m_PrevEvseState = prevevsestate; - -#ifdef VOLTMETER - ReadVoltmeter(); -#endif // VOLTMETER -#ifdef AMMETER - if (((m_EvseState == EVSE_STATE_C) && (m_CurrentScaleFactor > 0)) || AmmeterCalEnabled()) { - - readAmmeter(); - uint32_t ma = MovingAverage(m_AmmeterReading); - if (ma != 0xffffffff) { - m_ChargingCurrent = ma * m_CurrentScaleFactor - m_AmmeterCurrentOffset; // subtract it - if (m_ChargingCurrent < 0) { - m_ChargingCurrent = 0; - } - g_OBD.SetAmmeterDirty(1); - } - } -#endif // AMMETER - if (m_EvseState == EVSE_STATE_C) { - m_ElapsedChargeTimePrev = m_ElapsedChargeTime; - m_ElapsedChargeTime = now() - m_ChargeOnTime; - -#ifdef TEMPERATURE_MONITORING - if (m_ElapsedChargeTime != m_ElapsedChargeTimePrev) { - g_TempMonitor.Read(); - if (!g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_THROTTLE_DOWN ) || // any sensor reaching threshold trips action - (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) || - (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // Throttle back the L2 current advice to the EV - g_TempMonitor.SetOverTemperature(1); - SetCurrentCapacity(g_TempMonitor.m_ampacity / 2,0,1); // set L2 to the throttled back level - } - - if (g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_RESTORE_AMPERAGE ) && // all sensors need to show return to lower levels - (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ) && - (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ))) { // restore the original L2 current advice to the EV - g_TempMonitor.SetOverTemperature(0); - SetCurrentCapacity(g_TempMonitor.m_ampacity,0,1); // set L2 to the user's original setting for L2 current - } - - - if (!g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_SHUTDOWN ) || // any sensor reaching threshold trips action - (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ) || - (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ))) { // Throttle back the L2 current advice to the EV - g_TempMonitor.SetOverTemperatureShutdown(1); - SetCurrentCapacity(g_TempMonitor.m_ampacity / 4,0,1); - } - - if (g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_THROTTLE_DOWN ) && // all sensors need to show return to lower levels - (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) && - (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // restore the throttled down current advice to the EV since things have cooled down again - g_TempMonitor.SetOverTemperatureShutdown(0); - SetCurrentCapacity(g_TempMonitor.m_ampacity / 2,0,1); // set L2 to the throttled back level - m_Pilot.SetPWM(m_CurrentCapacity); - } - } -#endif // TEMPERATURE_MONITORING -#ifdef CHARGE_LIMIT - if (m_chargeLimit && (g_WattSeconds >= 3600000 * (uint32_t)m_chargeLimit)) { - SetChargeLimit(0); // reset charge limit - Sleep(); - } -#endif -#ifdef TIME_LIMIT - if (m_timeLimit) { - // must call millis() below because curms is sampled before transition to - // to State C, so m_ChargeOnTimeMS will be > curms from the start - if ((millis() - m_ChargeOnTimeMS) >= (15lu*60000lu * (unsigned long)m_timeLimit)) { - SetTimeLimit(0); // reset time limit - Sleep(); - } - } -#endif - } -} - -#ifdef CALIBRATE -// read ADC values and get min/max/avg for pilot steady high/low states -void J1772EVSEController::Calibrate(PCALIB_DATA pcd) -{ - uint16_t pmax,pmin,pavg,nmax,nmin,navg; - - for (int l=0;l < 2;l++) { - int reading; - uint32_t tot = 0; - uint16_t plow = 1023; - uint16_t phigh = 0; - uint16_t avg = 0; - m_Pilot.SetState(l ? PILOT_STATE_N12 : PILOT_STATE_P12); - - delay(250); // wait for stabilization - - // 1x = 114us 20x = 2.3ms 100x = 11.3ms - int i; - for (i=0;i < 1000;i++) { - reading = adcPilot.read(); // measures pilot voltage - - if (reading > phigh) { - phigh = reading; - } - else if (reading < plow) { - plow = reading; - } - - tot += reading; - } - avg = tot / i; - - if (l) { - nmax = phigh; - nmin = plow; - navg = avg; - } - else { - pmax = phigh; - pmin = plow; - pavg = avg; - } - } - pcd->m_pMax = pmax; - pcd->m_pAvg = pavg; - pcd->m_pMin = pmin; - pcd->m_nMax = nmax; - pcd->m_nAvg = navg; - pcd->m_nMin = nmin; -} -#endif // CALIBRATE - -int J1772EVSEController::SetCurrentCapacity(uint8_t amps,uint8_t updatelcd,uint8_t nosave) -{ - int rc = 0; - uint8_t maxcurrentcap = (GetCurSvcLevel() == 1) ? MAX_CURRENT_CAPACITY_L1 : MAX_CURRENT_CAPACITY_L2; - - if ((amps >= MIN_CURRENT_CAPACITY) && (amps <= maxcurrentcap)) { - m_CurrentCapacity = amps; - } - else if (amps < MIN_CURRENT_CAPACITY) { - m_CurrentCapacity = MIN_CURRENT_CAPACITY; - rc = 1; - } - else { - m_CurrentCapacity = maxcurrentcap; - rc = 2; - } - - if (!nosave) { - eeprom_write_byte((uint8_t*)((GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),(byte)m_CurrentCapacity); - } - - if (m_Pilot.GetState() == PILOT_STATE_PWM) { - m_Pilot.SetPWM(m_CurrentCapacity); - } - - if (updatelcd) { - g_OBD.Update(OBD_UPD_FORCE); - } - - return rc; -} - -#ifdef VOLTMETER -void J1772EVSEController::SetVoltmeter(uint16_t scale,uint32_t offset) -{ - m_VoltScaleFactor = scale; - eeprom_write_word((uint16_t*)EOFS_VOLT_SCALE_FACTOR,scale); - m_VoltOffset = offset; - eeprom_write_dword((uint32_t*)EOFS_VOLT_OFFSET,offset); -} - -uint32_t J1772EVSEController::ReadVoltmeter() -{ - unsigned int peak = 0; - for(uint32_t start_time = millis(); (millis() - start_time) < VOLTMETER_POLL_INTERVAL; ) { - unsigned int val = adcVoltMeter.read(); - if (val > peak) peak = val; - } - m_Voltage = ((uint32_t)peak) * ((uint32_t)m_VoltScaleFactor) + m_VoltOffset; - return m_Voltage; -} -#endif - -#ifdef AMMETER -static inline unsigned long ulong_sqrt(unsigned long in) -{ - unsigned long out; - // find the last int whose square is not too big - // Yes, it's wasteful, but we only theoretically ever have to go to 512. - // Removing floating point saves us almost 1K of flash. - for(out = 1; out*out <= in; out++) ; - return out - 1; -} - -void J1772EVSEController::readAmmeter() -{ - WDT_RESET(); - - unsigned long sum = 0; - unsigned int zero_crossings = 0; - unsigned long last_zero_crossing_time = 0, now_ms; - long last_sample = -1; // should be impossible - the A/d is 0 to 1023. - unsigned int sample_count = 0; - for(unsigned long start = millis(); ((now_ms = millis()) - start) < CURRENT_SAMPLE_INTERVAL; ) { - long sample = (long) adcCurrent.read(); - // If this isn't the first sample, and if the sign of the value differs from the - // sign of the previous value, then count that as a zero crossing. - if (last_sample != -1 && ((last_sample > 512) != (sample > 512))) { - // Once we've seen a zero crossing, don't look for one for a little bit. - // It's possible that a little noise near zero could cause a two-sample - // inversion. - if ((now_ms - last_zero_crossing_time) > CURRENT_ZERO_DEBOUNCE_INTERVAL) { - zero_crossings++; - last_zero_crossing_time = now_ms; - } - } - last_sample = sample; - switch(zero_crossings) { - case 0: - continue; // Still waiting to start sampling - case 1: - case 2: - // Gather the sum-of-the-squares and count how many samples we've collected. - sum += (unsigned long)((sample - 512) * (sample - 512)); - sample_count++; - continue; - case 3: - // The answer is the square root of the mean of the squares. - // But additionally, that value must be scaled to a real current value. - // we will do that elsewhere - m_AmmeterReading = ulong_sqrt(sum / sample_count); - return; - } - } - // ran out of time. Assume that it's simply not oscillating any. - m_AmmeterReading = 0; - - WDT_RESET(); -} - -#define MA_PTS 32 // # points in moving average MUST BE power of 2 -#define MA_BITS 5 // log2(MA_PTS) -/* -uint32_t MovingAverage(uint32_t samp) -{ - static uint32_t samps[MA_PTS] = {0}; - uint32_t tot = samp; - samps[0] = samp; - - for (int8_t c=MA_PTS-1;c > 0;c--) { - samps[c] = samps[c-1]; - tot += samps[c]; - } - - return tot >> MA_BITS; -} -*/ - -// to save memory -// instead of doing a real moving average we just do a non-overlapping -// sliding window and output a value every MA_PTS -uint32_t MovingAverage(uint32_t samp) -{ - static uint32_t tot = 0; - static int8_t curidx = 0; - - if (curidx == 0) { - tot = 0; - } - - tot += samp; - - if (++curidx == MA_PTS) { - curidx = 0; - return tot >> MA_BITS; // tot / MA_PTS - } - return 0xffffffff; -} - -#endif // AMMETER - -//-- end J1772EVSEController #ifdef BTN_MENU Btn::Btn() @@ -2875,7 +1202,7 @@ Menu *SvcLevelMenu::Select() g_OBD.LcdPrint(0,1,g_sPlus); g_OBD.LcdPrint(g_SvcLevelMenuItems[m_CurIdx]); - SaveEvseFlags(); + g_EvseController.SaveEvseFlags(); delay(500); @@ -3562,7 +1889,7 @@ Menu *AutoStartMenu::Select() g_OBD.LcdPrint(0,1,g_sPlus); g_OBD.LcdPrint(g_YesNoMenuItems[m_CurIdx]); g_EvseController.EnableAutoStart((m_CurIdx == 0) ? 1 : 0); - SaveEvseFlags(); + g_EvseController.SaveEvseFlags(); delay(500); return &g_SettingsMenu; } @@ -3902,7 +2229,7 @@ void DelayTimer::Enable(){ m_DelayTimerEnabled = 0x01; eeprom_write_byte((uint8_t*)EOFS_TIMER_FLAGS, m_DelayTimerEnabled); g_EvseController.EnableAutoStart(0); - SaveSettings(); + g_EvseController.SaveSettings(); CheckTime(); g_OBD.Update(OBD_UPD_FORCE); } @@ -3910,7 +2237,7 @@ void DelayTimer::Disable(){ m_DelayTimerEnabled = 0x00; eeprom_write_byte((uint8_t*)EOFS_TIMER_FLAGS, m_DelayTimerEnabled); g_EvseController.EnableAutoStart(1); - SaveSettings(); + g_EvseController.SaveSettings(); g_OBD.Update(OBD_UPD_FORCE); } void DelayTimer::PrintTimerIcon(){ From ac9d9a13b218a1fa79f185c91ee9cc3fdd70f642 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 2 Jun 2015 16:09:00 -0700 Subject: [PATCH 05/96] rearrange --- J1772EvseController.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 4e9a4cf2..0e24a043 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -19,13 +19,6 @@ #include "open_evse.h" -void J1772EVSEController::SaveSettings() -{ - // n.b. should we add dirty bits so we only write the changed values? or should we just write them on the fly when necessary? - eeprom_write_byte((uint8_t *)((GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),(byte)GetCurrentCapacity()); - SaveEvseFlags(); -} - THRESH_DATA g_DefaultThreshData = {875,780,690,0,260}; @@ -140,6 +133,15 @@ J1772EVSEController::J1772EVSEController() : { } +void J1772EVSEController::SaveSettings() +{ + // n.b. should we add dirty bits so we only write the changed values? or should we just write them on the fly when necessary? + eeprom_write_byte((uint8_t *)((GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),(byte)GetCurrentCapacity()); + SaveEvseFlags(); +} + + + // use watchdog to perform a reset void J1772EVSEController::Reboot() { From b7f7a35f868ba058e8e590dce086f78c94ef3262 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 2 Jun 2015 17:09:28 -0700 Subject: [PATCH 06/96] use local Wire/twi files --- twi.h | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 twi.h diff --git a/twi.h b/twi.h new file mode 100644 index 00000000..65265933 --- /dev/null +++ b/twi.h @@ -0,0 +1,53 @@ +/* + twi.h - TWI/I2C library for Wiring & Arduino + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef twi_h +#define twi_h + + #include + + //#define ATMEGA8 + + #ifndef TWI_FREQ + #define TWI_FREQ 100000L + #endif + + #ifndef TWI_BUFFER_LENGTH + #define TWI_BUFFER_LENGTH 32 + #endif + + #define TWI_READY 0 + #define TWI_MRX 1 + #define TWI_MTX 2 + #define TWI_SRX 3 + #define TWI_STX 4 + + void twi_init(void); + void twi_setAddress(uint8_t); + uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t, uint8_t); + uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t, uint8_t); + uint8_t twi_transmit(const uint8_t*, uint8_t); + void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) ); + void twi_attachSlaveTxEvent( void (*)(void) ); + void twi_reply(uint8_t); + void twi_stop(void); + void twi_releaseBus(void); + +#endif + From 92dbccdbb8b027b77042501f94c95f5e1dd00bb8 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 2 Jun 2015 17:58:59 -0700 Subject: [PATCH 07/96] use local Wire/twi - missed commits --- Adafruit_MCP9808.cpp | 2 +- Adafruit_MCP9808.h | 2 +- Adafruit_TMP007.h | 2 +- CHANGELOG | 3 +- LiquidTWI2.cpp | 2 +- RTClib.cpp | 2 +- Wire.cpp | 303 +++++++++++++++++++++++++ Wire.h | 80 +++++++ avrstuff.h | 6 + open_evse.h | 6 +- open_evse.ino | 7 +- twi.c | 530 +++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 930 insertions(+), 15 deletions(-) create mode 100644 Wire.cpp create mode 100644 Wire.h create mode 100644 twi.c diff --git a/Adafruit_MCP9808.cpp b/Adafruit_MCP9808.cpp index f8449777..8e0480d1 100644 --- a/Adafruit_MCP9808.cpp +++ b/Adafruit_MCP9808.cpp @@ -28,7 +28,7 @@ #include "TinyWireM.h" #define Wire TinyWireM #else - #include + #include "./Wire.h" #endif static inline void wiresend(uint8_t x) { diff --git a/Adafruit_MCP9808.h b/Adafruit_MCP9808.h index dad9cc72..f4242f8e 100644 --- a/Adafruit_MCP9808.h +++ b/Adafruit_MCP9808.h @@ -27,7 +27,7 @@ #include "TinyWireM.h" #define Wire TinyWireM #else - #include + #include "./Wire.h" #endif #define MCP9808_I2CADDR_DEFAULT 0x18 diff --git a/Adafruit_TMP007.h b/Adafruit_TMP007.h index 6f3aede5..0c60ccb7 100644 --- a/Adafruit_TMP007.h +++ b/Adafruit_TMP007.h @@ -20,7 +20,7 @@ #else #include "WProgram.h" #endif -#include "Wire.h" +#include "./Wire.h" // uncomment for debugging! //#define TMP007_DEBUG 1 diff --git a/CHANGELOG b/CHANGELOG index 710a483c..3211d85b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,9 @@ Change Log -vD3.8.5 SCL 20150602 +vD3.9.0 SCL 20150602 - split out Gfi/J1772Pilot/J1772EvseController into separate files - add support for changing I2C frequency +- use modified local Wire/twi to get rid of digitalWrite() vD3.8.4 SCL 20150526 - display "Disabled" when EVSE_STATE_DISABLED instead of "Stopped" diff --git a/LiquidTWI2.cpp b/LiquidTWI2.cpp index ce646304..bc1cbcf5 100644 --- a/LiquidTWI2.cpp +++ b/LiquidTWI2.cpp @@ -19,7 +19,7 @@ #include "TinyWireM.h" #define Wire TinyWireM #else -#include +#include "./Wire.h" #endif #if defined(ARDUINO) && (ARDUINO >= 100) //scl #include "Arduino.h" diff --git a/RTClib.cpp b/RTClib.cpp index 4516a570..d1f5d801 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -1,7 +1,7 @@ // Code by JeeLabs http://news.jeelabs.org/code/ // Released to the public domain! Enjoy! -#include +#include "./Wire.h" #include #include "RTClib.h" diff --git a/Wire.cpp b/Wire.cpp new file mode 100644 index 00000000..f97531b9 --- /dev/null +++ b/Wire.cpp @@ -0,0 +1,303 @@ +/* + TwoWire.cpp - TWI/I2C library for Wiring & Arduino + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts +*/ + +extern "C" { + #include + #include + #include + #include "./twi.h" +} + +#include "./Wire.h" + +// Initialize Class Variables ////////////////////////////////////////////////// + +uint8_t TwoWire::rxBuffer[BUFFER_LENGTH]; +uint8_t TwoWire::rxBufferIndex = 0; +uint8_t TwoWire::rxBufferLength = 0; + +uint8_t TwoWire::txAddress = 0; +uint8_t TwoWire::txBuffer[BUFFER_LENGTH]; +uint8_t TwoWire::txBufferIndex = 0; +uint8_t TwoWire::txBufferLength = 0; + +uint8_t TwoWire::transmitting = 0; +void (*TwoWire::user_onRequest)(void); +void (*TwoWire::user_onReceive)(int); + +// Constructors //////////////////////////////////////////////////////////////// + +TwoWire::TwoWire() +{ +} + +// Public Methods ////////////////////////////////////////////////////////////// + +void TwoWire::begin(void) +{ + rxBufferIndex = 0; + rxBufferLength = 0; + + txBufferIndex = 0; + txBufferLength = 0; + + twi_init(); +} + +void TwoWire::begin(uint8_t address) +{ + twi_setAddress(address); + twi_attachSlaveTxEvent(onRequestService); + twi_attachSlaveRxEvent(onReceiveService); + begin(); +} + +void TwoWire::begin(int address) +{ + begin((uint8_t)address); +} + +void TwoWire::setClock(uint32_t frequency) +{ + TWBR = ((F_CPU / frequency) - 16) / 2; +} + +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) +{ + // clamp to buffer length + if(quantity > BUFFER_LENGTH){ + quantity = BUFFER_LENGTH; + } + // perform blocking read into buffer + uint8_t read = twi_readFrom(address, rxBuffer, quantity, sendStop); + // set rx buffer iterator vars + rxBufferIndex = 0; + rxBufferLength = read; + + return read; +} + +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) +{ + return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); +} + +uint8_t TwoWire::requestFrom(int address, int quantity) +{ + return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); +} + +uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) +{ + return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop); +} + +void TwoWire::beginTransmission(uint8_t address) +{ + // indicate that we are transmitting + transmitting = 1; + // set address of targeted slave + txAddress = address; + // reset tx buffer iterator vars + txBufferIndex = 0; + txBufferLength = 0; +} + +void TwoWire::beginTransmission(int address) +{ + beginTransmission((uint8_t)address); +} + +// +// Originally, 'endTransmission' was an f(void) function. +// It has been modified to take one parameter indicating +// whether or not a STOP should be performed on the bus. +// Calling endTransmission(false) allows a sketch to +// perform a repeated start. +// +// WARNING: Nothing in the library keeps track of whether +// the bus tenure has been properly ended with a STOP. It +// is very possible to leave the bus in a hung state if +// no call to endTransmission(true) is made. Some I2C +// devices will behave oddly if they do not see a STOP. +// +uint8_t TwoWire::endTransmission(uint8_t sendStop) +{ + // transmit buffer (blocking) + int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1, sendStop); + // reset tx buffer iterator vars + txBufferIndex = 0; + txBufferLength = 0; + // indicate that we are done transmitting + transmitting = 0; + return ret; +} + +// This provides backwards compatibility with the original +// definition, and expected behaviour, of endTransmission +// +uint8_t TwoWire::endTransmission(void) +{ + return endTransmission(true); +} + +// must be called in: +// slave tx event callback +// or after beginTransmission(address) +size_t TwoWire::write(uint8_t data) +{ + if(transmitting){ + // in master transmitter mode + // don't bother if buffer is full + if(txBufferLength >= BUFFER_LENGTH){ + setWriteError(); + return 0; + } + // put byte in tx buffer + txBuffer[txBufferIndex] = data; + ++txBufferIndex; + // update amount in buffer + txBufferLength = txBufferIndex; + }else{ + // in slave send mode + // reply to master + twi_transmit(&data, 1); + } + return 1; +} + +// must be called in: +// slave tx event callback +// or after beginTransmission(address) +size_t TwoWire::write(const uint8_t *data, size_t quantity) +{ + if(transmitting){ + // in master transmitter mode + for(size_t i = 0; i < quantity; ++i){ + write(data[i]); + } + }else{ + // in slave send mode + // reply to master + twi_transmit(data, quantity); + } + return quantity; +} + +// must be called in: +// slave rx event callback +// or after requestFrom(address, numBytes) +int TwoWire::available(void) +{ + return rxBufferLength - rxBufferIndex; +} + +// must be called in: +// slave rx event callback +// or after requestFrom(address, numBytes) +int TwoWire::read(void) +{ + int value = -1; + + // get each successive byte on each call + if(rxBufferIndex < rxBufferLength){ + value = rxBuffer[rxBufferIndex]; + ++rxBufferIndex; + } + + return value; +} + +// must be called in: +// slave rx event callback +// or after requestFrom(address, numBytes) +int TwoWire::peek(void) +{ + int value = -1; + + if(rxBufferIndex < rxBufferLength){ + value = rxBuffer[rxBufferIndex]; + } + + return value; +} + +void TwoWire::flush(void) +{ + // XXX: to be implemented. +} + +// behind the scenes function that is called when data is received +void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes) +{ + // don't bother if user hasn't registered a callback + if(!user_onReceive){ + return; + } + // don't bother if rx buffer is in use by a master requestFrom() op + // i know this drops data, but it allows for slight stupidity + // meaning, they may not have read all the master requestFrom() data yet + if(rxBufferIndex < rxBufferLength){ + return; + } + // copy twi rx buffer into local read buffer + // this enables new reads to happen in parallel + for(uint8_t i = 0; i < numBytes; ++i){ + rxBuffer[i] = inBytes[i]; + } + // set rx iterator vars + rxBufferIndex = 0; + rxBufferLength = numBytes; + // alert user program + user_onReceive(numBytes); +} + +// behind the scenes function that is called when data is requested +void TwoWire::onRequestService(void) +{ + // don't bother if user hasn't registered a callback + if(!user_onRequest){ + return; + } + // reset tx buffer iterator vars + // !!! this will kill any pending pre-master sendTo() activity + txBufferIndex = 0; + txBufferLength = 0; + // alert user program + user_onRequest(); +} + +// sets function called on slave write +void TwoWire::onReceive( void (*function)(int) ) +{ + user_onReceive = function; +} + +// sets function called on slave read +void TwoWire::onRequest( void (*function)(void) ) +{ + user_onRequest = function; +} + +// Preinstantiate Objects ////////////////////////////////////////////////////// + +TwoWire Wire = TwoWire(); + diff --git a/Wire.h b/Wire.h new file mode 100644 index 00000000..732bdc31 --- /dev/null +++ b/Wire.h @@ -0,0 +1,80 @@ +/* + TwoWire.h - TWI/I2C library for Arduino & Wiring + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts +*/ + +#ifndef TwoWire_h +#define TwoWire_h + +#include +#include "Stream.h" + +#define BUFFER_LENGTH 32 + +class TwoWire : public Stream +{ + private: + static uint8_t rxBuffer[]; + static uint8_t rxBufferIndex; + static uint8_t rxBufferLength; + + static uint8_t txAddress; + static uint8_t txBuffer[]; + static uint8_t txBufferIndex; + static uint8_t txBufferLength; + + static uint8_t transmitting; + static void (*user_onRequest)(void); + static void (*user_onReceive)(int); + static void onRequestService(void); + static void onReceiveService(uint8_t*, int); + public: + TwoWire(); + void begin(); + void begin(uint8_t); + void begin(int); + void setClock(uint32_t); + void beginTransmission(uint8_t); + void beginTransmission(int); + uint8_t endTransmission(void); + uint8_t endTransmission(uint8_t); + uint8_t requestFrom(uint8_t, uint8_t); + uint8_t requestFrom(uint8_t, uint8_t, uint8_t); + uint8_t requestFrom(int, int); + uint8_t requestFrom(int, int, int); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *, size_t); + virtual int available(void); + virtual int read(void); + virtual int peek(void); + virtual void flush(void); + void onReceive( void (*)(int) ); + void onRequest( void (*)(void) ); + + inline size_t write(unsigned long n) { return write((uint8_t)n); } + inline size_t write(long n) { return write((uint8_t)n); } + inline size_t write(unsigned int n) { return write((uint8_t)n); } + inline size_t write(int n) { return write((uint8_t)n); } + using Print::write; +}; + +extern TwoWire Wire; + +#endif + diff --git a/avrstuff.h b/avrstuff.h index 942ff4df..6e3590c6 100644 --- a/avrstuff.h +++ b/avrstuff.h @@ -23,6 +23,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO +#ifdef __cplusplus class CriticalSection { uint8_t sreg; public: @@ -38,6 +39,8 @@ class AutoCriticalSection { ~AutoCriticalSection() { SREG = sreg; } }; +#endif // __cplusplus + // n.b. NONE OF THE PIN FUNCTIONS BELOW HANDLE PORTS > 0x100.. must be wrapped // in critical section.. see Marlin fastio.h for details @@ -84,6 +87,7 @@ example // pin macros .. ugly, but no RAM usage // +#ifdef __cplusplus // // begin digitalPin class @@ -176,3 +180,5 @@ class Led { // // end digital pin class // + +#endif // __cplusplus diff --git a/open_evse.h b/open_evse.h index e4f6acf1..163417b9 100644 --- a/open_evse.h +++ b/open_evse.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include "./Wire.h" #include "./Time.h" #include "avrstuff.h" #if defined(ARDUINO) && (ARDUINO >= 100) @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.8.4" +#define VERSION "D3.9.0" //-- begin features @@ -425,7 +425,7 @@ #if defined(RGBLCD) || defined(I2CLCD) // Using LiquidTWI2 for both types of I2C LCD's // see http://blog.lincomatic.com/?p=956 for installation instructions -#include +#include "./Wire.h" #ifdef I2CLCD_PCF8574 #include #define LCD_I2C_ADDR 0x27 diff --git a/open_evse.ino b/open_evse.ino index 3f7dec39..098bb162 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -43,7 +43,7 @@ #include #include #include -#include +#include "./Wire.h" #include "./RTClib.h" #include "open_evse.h" // if using I2CLCD_PCF8574 uncomment below line and comment out LiquidTWI2.h above @@ -987,7 +987,6 @@ void SettingsMenu::CheckSkipLimits() void SettingsMenu::Init() { m_CurIdx = 0; - ; #if defined(CHARGE_LIMIT)||defined(TIME_LIMIT) while (m_skipLimits && ( @@ -2299,10 +2298,6 @@ void setup() g_WattHours_accumulated = eeprom_read_dword((uint32_t*)EOFS_KWH_ACCUMULATED); // get the stored value for the kWh from eeprom #endif // KWH_RECORDING - - // need I2C speed last because Wire.begin(), which might be called by - // libraries, sets it to TWI_FREQ - Wire.setClock(I2C_FREQ); } // setup() diff --git a/twi.c b/twi.c new file mode 100644 index 00000000..a914eb43 --- /dev/null +++ b/twi.c @@ -0,0 +1,530 @@ +/* + twi.c - TWI/I2C library for Wiring & Arduino + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts +*/ + +#include +#include +#include +#include +#include +#include + +#ifndef cbi +#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) +#endif + +#ifndef sbi +#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) +#endif + +#include "./twi.h" + +#define false 0 +#define true (!false) + +static volatile uint8_t twi_state; +static volatile uint8_t twi_slarw; +static volatile uint8_t twi_sendStop; // should the transaction end with a stop +static volatile uint8_t twi_inRepStart; // in the middle of a repeated start + +static void (*twi_onSlaveTransmit)(void); +static void (*twi_onSlaveReceive)(uint8_t*, int); + +static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH]; +static volatile uint8_t twi_masterBufferIndex; +static volatile uint8_t twi_masterBufferLength; + +static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH]; +static volatile uint8_t twi_txBufferIndex; +static volatile uint8_t twi_txBufferLength; + +static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; +static volatile uint8_t twi_rxBufferIndex; + +static volatile uint8_t twi_error; + +/* + * Function twi_init + * Desc readys twi pins and sets twi bitrate + * Input none + * Output none + */ +void twi_init(void) +{ + // initialize state + twi_state = TWI_READY; + twi_sendStop = true; // default value + twi_inRepStart = false; + + // activate internal pullups for twi. + // scl + // digitalWrite(SDA, 1); + // digitalWrite(SCL, 1); + PORTC |= (1 << PC4) | (1 << PC5); + + // initialize twi prescaler and bit rate + cbi(TWSR, TWPS0); + cbi(TWSR, TWPS1); + TWBR = ((F_CPU / TWI_FREQ) - 16) / 2; + + /* twi bit rate formula from atmega128 manual pg 204 + SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR)) + note: TWBR should be 10 or higher for master mode + It is 72 for a 16mhz Wiring board with 100kHz TWI */ + + // enable twi module, acks, and twi interrupt + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA); +} + +/* + * Function twi_slaveInit + * Desc sets slave address and enables interrupt + * Input none + * Output none + */ +void twi_setAddress(uint8_t address) +{ + // set twi slave address (skip over TWGCE bit) + TWAR = address << 1; +} + +/* + * Function twi_readFrom + * Desc attempts to become twi bus master and read a + * series of bytes from a device on the bus + * Input address: 7bit i2c device address + * data: pointer to byte array + * length: number of bytes to read into array + * sendStop: Boolean indicating whether to send a stop at the end + * Output number of bytes read + */ +uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop) +{ + uint8_t i; + + // ensure data will fit into buffer + if(TWI_BUFFER_LENGTH < length){ + return 0; + } + + // wait until twi is ready, become master receiver + while(TWI_READY != twi_state){ + continue; + } + twi_state = TWI_MRX; + twi_sendStop = sendStop; + // reset error state (0xFF.. no error occured) + twi_error = 0xFF; + + // initialize buffer iteration vars + twi_masterBufferIndex = 0; + twi_masterBufferLength = length-1; // This is not intuitive, read on... + // On receive, the previously configured ACK/NACK setting is transmitted in + // response to the received byte before the interrupt is signalled. + // Therefor we must actually set NACK when the _next_ to last byte is + // received, causing that NACK to be sent in response to receiving the last + // expected byte of data. + + // build sla+w, slave device address + w bit + twi_slarw = TW_READ; + twi_slarw |= address << 1; + + if (true == twi_inRepStart) { + // if we're in the repeated start state, then we've already sent the start, + // (@@@ we hope), and the TWI statemachine is just waiting for the address byte. + // We need to remove ourselves from the repeated start state before we enable interrupts, + // since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning + // up. Also, don't enable the START interrupt. There may be one pending from the + // repeated start that we sent outselves, and that would really confuse things. + twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR + TWDR = twi_slarw; + TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START + } + else + // send start condition + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); + + // wait for read operation to complete + while(TWI_MRX == twi_state){ + continue; + } + + if (twi_masterBufferIndex < length) + length = twi_masterBufferIndex; + + // copy twi buffer to data + for(i = 0; i < length; ++i){ + data[i] = twi_masterBuffer[i]; + } + + return length; +} + +/* + * Function twi_writeTo + * Desc attempts to become twi bus master and write a + * series of bytes to a device on the bus + * Input address: 7bit i2c device address + * data: pointer to byte array + * length: number of bytes in array + * wait: boolean indicating to wait for write or not + * sendStop: boolean indicating whether or not to send a stop at the end + * Output 0 .. success + * 1 .. length to long for buffer + * 2 .. address send, NACK received + * 3 .. data send, NACK received + * 4 .. other twi error (lost bus arbitration, bus error, ..) + */ +uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop) +{ + uint8_t i; + + // ensure data will fit into buffer + if(TWI_BUFFER_LENGTH < length){ + return 1; + } + + // wait until twi is ready, become master transmitter + while(TWI_READY != twi_state){ + continue; + } + twi_state = TWI_MTX; + twi_sendStop = sendStop; + // reset error state (0xFF.. no error occured) + twi_error = 0xFF; + + // initialize buffer iteration vars + twi_masterBufferIndex = 0; + twi_masterBufferLength = length; + + // copy data to twi buffer + for(i = 0; i < length; ++i){ + twi_masterBuffer[i] = data[i]; + } + + // build sla+w, slave device address + w bit + twi_slarw = TW_WRITE; + twi_slarw |= address << 1; + + // if we're in a repeated start, then we've already sent the START + // in the ISR. Don't do it again. + // + if (true == twi_inRepStart) { + // if we're in the repeated start state, then we've already sent the start, + // (@@@ we hope), and the TWI statemachine is just waiting for the address byte. + // We need to remove ourselves from the repeated start state before we enable interrupts, + // since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning + // up. Also, don't enable the START interrupt. There may be one pending from the + // repeated start that we sent outselves, and that would really confuse things. + twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR + TWDR = twi_slarw; + TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START + } + else + // send start condition + TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs + + // wait for write operation to complete + while(wait && (TWI_MTX == twi_state)){ + continue; + } + + if (twi_error == 0xFF) + return 0; // success + else if (twi_error == TW_MT_SLA_NACK) + return 2; // error: address send, nack received + else if (twi_error == TW_MT_DATA_NACK) + return 3; // error: data send, nack received + else + return 4; // other twi error +} + +/* + * Function twi_transmit + * Desc fills slave tx buffer with data + * must be called in slave tx event callback + * Input data: pointer to byte array + * length: number of bytes in array + * Output 1 length too long for buffer + * 2 not slave transmitter + * 0 ok + */ +uint8_t twi_transmit(const uint8_t* data, uint8_t length) +{ + uint8_t i; + + // ensure data will fit into buffer + if(TWI_BUFFER_LENGTH < length){ + return 1; + } + + // ensure we are currently a slave transmitter + if(TWI_STX != twi_state){ + return 2; + } + + // set length and copy data into tx buffer + twi_txBufferLength = length; + for(i = 0; i < length; ++i){ + twi_txBuffer[i] = data[i]; + } + + return 0; +} + +/* + * Function twi_attachSlaveRxEvent + * Desc sets function called before a slave read operation + * Input function: callback function to use + * Output none + */ +void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) ) +{ + twi_onSlaveReceive = function; +} + +/* + * Function twi_attachSlaveTxEvent + * Desc sets function called before a slave write operation + * Input function: callback function to use + * Output none + */ +void twi_attachSlaveTxEvent( void (*function)(void) ) +{ + twi_onSlaveTransmit = function; +} + +/* + * Function twi_reply + * Desc sends byte or readys receive line + * Input ack: byte indicating to ack or to nack + * Output none + */ +void twi_reply(uint8_t ack) +{ + // transmit master read ready signal, with or without ack + if(ack){ + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); + }else{ + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); + } +} + +/* + * Function twi_stop + * Desc relinquishes bus master status + * Input none + * Output none + */ +void twi_stop(void) +{ + // send stop condition + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); + + // wait for stop condition to be exectued on bus + // TWINT is not set after a stop condition! + while(TWCR & _BV(TWSTO)){ + continue; + } + + // update twi state + twi_state = TWI_READY; +} + +/* + * Function twi_releaseBus + * Desc releases bus control + * Input none + * Output none + */ +void twi_releaseBus(void) +{ + // release bus + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT); + + // update twi state + twi_state = TWI_READY; +} + +ISR(TWI_vect) +{ + switch(TW_STATUS){ + // All Master + case TW_START: // sent start condition + case TW_REP_START: // sent repeated start condition + // copy device address and r/w bit to output register and ack + TWDR = twi_slarw; + twi_reply(1); + break; + + // Master Transmitter + case TW_MT_SLA_ACK: // slave receiver acked address + case TW_MT_DATA_ACK: // slave receiver acked data + // if there is data to send, send it, otherwise stop + if(twi_masterBufferIndex < twi_masterBufferLength){ + // copy data to output register and ack + TWDR = twi_masterBuffer[twi_masterBufferIndex++]; + twi_reply(1); + }else{ + if (twi_sendStop) + twi_stop(); + else { + twi_inRepStart = true; // we're gonna send the START + // don't enable the interrupt. We'll generate the start, but we + // avoid handling the interrupt until we're in the next transaction, + // at the point where we would normally issue the start. + TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ; + twi_state = TWI_READY; + } + } + break; + case TW_MT_SLA_NACK: // address sent, nack received + twi_error = TW_MT_SLA_NACK; + twi_stop(); + break; + case TW_MT_DATA_NACK: // data sent, nack received + twi_error = TW_MT_DATA_NACK; + twi_stop(); + break; + case TW_MT_ARB_LOST: // lost bus arbitration + twi_error = TW_MT_ARB_LOST; + twi_releaseBus(); + break; + + // Master Receiver + case TW_MR_DATA_ACK: // data received, ack sent + // put byte into buffer + twi_masterBuffer[twi_masterBufferIndex++] = TWDR; + case TW_MR_SLA_ACK: // address sent, ack received + // ack if more bytes are expected, otherwise nack + if(twi_masterBufferIndex < twi_masterBufferLength){ + twi_reply(1); + }else{ + twi_reply(0); + } + break; + case TW_MR_DATA_NACK: // data received, nack sent + // put final byte into buffer + twi_masterBuffer[twi_masterBufferIndex++] = TWDR; + if (twi_sendStop) + twi_stop(); + else { + twi_inRepStart = true; // we're gonna send the START + // don't enable the interrupt. We'll generate the start, but we + // avoid handling the interrupt until we're in the next transaction, + // at the point where we would normally issue the start. + TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ; + twi_state = TWI_READY; + } + break; + case TW_MR_SLA_NACK: // address sent, nack received + twi_stop(); + break; + // TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case + + // Slave Receiver + case TW_SR_SLA_ACK: // addressed, returned ack + case TW_SR_GCALL_ACK: // addressed generally, returned ack + case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack + case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack + // enter slave receiver mode + twi_state = TWI_SRX; + // indicate that rx buffer can be overwritten and ack + twi_rxBufferIndex = 0; + twi_reply(1); + break; + case TW_SR_DATA_ACK: // data received, returned ack + case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack + // if there is still room in the rx buffer + if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ + // put byte in buffer and ack + twi_rxBuffer[twi_rxBufferIndex++] = TWDR; + twi_reply(1); + }else{ + // otherwise nack + twi_reply(0); + } + break; + case TW_SR_STOP: // stop or repeated start condition received + // put a null char after data if there's room + if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){ + twi_rxBuffer[twi_rxBufferIndex] = '\0'; + } + // sends ack and stops interface for clock stretching + twi_stop(); + // callback to user defined callback + twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex); + // since we submit rx buffer to "wire" library, we can reset it + twi_rxBufferIndex = 0; + // ack future responses and leave slave receiver state + twi_releaseBus(); + break; + case TW_SR_DATA_NACK: // data received, returned nack + case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack + // nack back at master + twi_reply(0); + break; + + // Slave Transmitter + case TW_ST_SLA_ACK: // addressed, returned ack + case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack + // enter slave transmitter mode + twi_state = TWI_STX; + // ready the tx buffer index for iteration + twi_txBufferIndex = 0; + // set tx buffer length to be zero, to verify if user changes it + twi_txBufferLength = 0; + // request for txBuffer to be filled and length to be set + // note: user must call twi_transmit(bytes, length) to do this + twi_onSlaveTransmit(); + // if they didn't change buffer & length, initialize it + if(0 == twi_txBufferLength){ + twi_txBufferLength = 1; + twi_txBuffer[0] = 0x00; + } + // transmit first byte from buffer, fall + case TW_ST_DATA_ACK: // byte sent, ack returned + // copy data to output register + TWDR = twi_txBuffer[twi_txBufferIndex++]; + // if there is more to send, ack, otherwise nack + if(twi_txBufferIndex < twi_txBufferLength){ + twi_reply(1); + }else{ + twi_reply(0); + } + break; + case TW_ST_DATA_NACK: // received nack, we are done + case TW_ST_LAST_DATA: // received ack, but we are done already! + // ack future responses + twi_reply(1); + // leave slave receiver state + twi_state = TWI_READY; + break; + + // All + case TW_NO_INFO: // no state information + break; + case TW_BUS_ERROR: // bus error, illegal stop/start + twi_error = TW_BUS_ERROR; + twi_stop(); + break; + } +} + From c91a9062fcf5f1adf45dd1c3de22d3255271696d Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 2 Jun 2015 18:06:22 -0700 Subject: [PATCH 08/96] go directly to Setup menu on POST failure, reboot on exit menu --- CHANGELOG | 1 + open_evse.ino | 35 +++++++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3211d85b..d099680e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ vD3.9.0 SCL 20150602 - split out Gfi/J1772Pilot/J1772EvseController into separate files - add support for changing I2C frequency - use modified local Wire/twi to get rid of digitalWrite() +- on POST failure, go directly to Setup menu. Setup menu exits to reboot vD3.8.4 SCL 20150526 - display "Disabled" when EVSE_STATE_DISABLED instead of "Stopped" diff --git a/open_evse.ino b/open_evse.ino index 098bb162..48b32c7b 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -1091,6 +1091,7 @@ Menu *SetupMenu::Select() return g_SetupMenuList[m_CurIdx]; } else { + m_CurIdx = 0; return NULL; } } @@ -2096,25 +2097,38 @@ void BtnHandler::ChkBtn(int8_t nosleeptoggle) } m_CurMenu->Init(); if ((m_CurMenu == &g_SettingsMenu)||(m_CurMenu == &g_SetupMenu)) { - // restore prev menu item - m_CurMenu->m_CurIdx = curidx-1; - m_CurMenu->Next(); + if (curidx > 0) { + // restore prev menu item + m_CurMenu->m_CurIdx = curidx-1; + m_CurMenu->Next(); + } } + } else { // exit + if (nosleeptoggle) { + g_EvseController.Reboot(); + } + else { #if defined(DELAYTIMER) - if (!g_DelayTimer.IsTimerEnabled()){ - g_EvseController.Enable(); - } + if (!g_DelayTimer.IsTimerEnabled()){ + g_EvseController.Enable(); + } #else - g_EvseController.Enable(); + g_EvseController.Enable(); #endif - g_OBD.DisableUpdate(0); - g_OBD.LcdSetBacklightType(m_SavedLcdMode); // exiting menus - restore LCD mode - g_OBD.Update(OBD_UPD_FORCE); + g_OBD.DisableUpdate(0); + g_OBD.LcdSetBacklightType(m_SavedLcdMode); // exiting menus - restore LCD mode + g_OBD.Update(OBD_UPD_FORCE); + } } } else { + if (nosleeptoggle) { + g_SetupMenu.Init(); + m_CurMenu = &g_SetupMenu; + } + else { #if defined(CHARGE_LIMIT) || defined(TIME_LIMIT) g_SettingsMenu.CheckSkipLimits(); #endif // CHARGE_LIMIT @@ -2124,6 +2138,7 @@ void BtnHandler::ChkBtn(int8_t nosleeptoggle) g_OBD.LcdSetBacklightColor(WHITE); g_SettingsMenu.Init(); m_CurMenu = &g_SettingsMenu; + } } } } From 0b50a67911d6a0f3ed1eceaa31c670b4ea49508b Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 2 Jun 2015 18:12:04 -0700 Subject: [PATCH 09/96] get rid of newly superfluous EnableExitItem() code --- J1772EvseController.cpp | 10 ---------- open_evse.ino | 3 +-- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 0e24a043..665646a5 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -239,9 +239,6 @@ void J1772EVSEController::chargingOff() void J1772EVSEController::HardFault() { -#ifdef BTN_MENU - g_SettingsMenu.EnableExitItem(0); -#endif g_OBD.Update(OBD_UPD_HARDFAULT); while (1) ProcessInputs(1); // spin forever or until user resets via menu } @@ -860,9 +857,6 @@ void J1772EVSEController::Init() ShowDisabledTests(); #endif -#ifdef BTN_MENU - g_SettingsMenu.EnableExitItem(0); -#endif // BTN_MENU uint8_t fault; do { fault = 0; // reset post fault @@ -891,10 +885,6 @@ void J1772EVSEController::Init() } while ( fault && ( m_EvseState == EVSE_STATE_GFI_TEST_FAILED || m_EvseState == EVSE_STATE_NO_GROUND || m_EvseState == EVSE_STATE_STUCK_RELAY )); #endif // ADVPWR -#ifdef BTN_MENU - g_SettingsMenu.EnableExitItem(1); -#endif // BTN_MENU - SetSvcLevel(svclvl); // Start Manual Start Feature - GoldServe diff --git a/open_evse.ino b/open_evse.ino index 48b32c7b..d33bcaa2 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -1009,8 +1009,7 @@ void SettingsMenu::Init() void SettingsMenu::Next() { - if ((++m_CurIdx > m_menuCnt) || - (m_noExit && (m_CurIdx == m_menuCnt))) { // skip exit item + if (++m_CurIdx > m_menuCnt) { m_CurIdx = 0; } From 880e10bdac0c1f443aa618ed9aaa0e98657a24e5 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 2 Jun 2015 18:12:31 -0700 Subject: [PATCH 10/96] get rid of newly superfluous EnableExitItem() code --- open_evse.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/open_evse.h b/open_evse.h index 163417b9..de9e69f4 100644 --- a/open_evse.h +++ b/open_evse.h @@ -841,7 +841,6 @@ class Menu { class SettingsMenu : public Menu { uint8_t m_menuCnt; - uint8_t m_noExit; #if defined(CHARGE_LIMIT)||defined(TIME_LIMIT) uint8_t m_skipLimits; #endif @@ -850,9 +849,6 @@ class SettingsMenu : public Menu { void Init(); void Next(); Menu *Select(); - void EnableExitItem(uint8_t tf) { - m_noExit = !tf; - } #if defined(CHARGE_LIMIT) || defined(TIME_LIMIT) void CheckSkipLimits(); #endif From eb3a406bd699c457976982b8f788f5e9ee372313 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Wed, 3 Jun 2015 12:22:31 -0700 Subject: [PATCH 11/96] increase I2C from 100KHz to 200KHz --- twi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/twi.h b/twi.h index 65265933..93578c8f 100644 --- a/twi.h +++ b/twi.h @@ -25,7 +25,7 @@ //#define ATMEGA8 #ifndef TWI_FREQ - #define TWI_FREQ 100000L + #define TWI_FREQ 200000L #endif #ifndef TWI_BUFFER_LENGTH From c88721ea646dc4170f00a4493e679f6b853aa66c Mon Sep 17 00:00:00 2001 From: lincomatic Date: Wed, 3 Jun 2015 14:22:20 -0700 Subject: [PATCH 12/96] take out ifdefs --- twi.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/twi.h b/twi.h index 93578c8f..b8eba69b 100644 --- a/twi.h +++ b/twi.h @@ -24,13 +24,9 @@ //#define ATMEGA8 - #ifndef TWI_FREQ #define TWI_FREQ 200000L - #endif - #ifndef TWI_BUFFER_LENGTH #define TWI_BUFFER_LENGTH 32 - #endif #define TWI_READY 0 #define TWI_MRX 1 From 4020dd6c9c79199ee09a1be2114374f0445e6d74 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Wed, 3 Jun 2015 14:22:34 -0700 Subject: [PATCH 13/96] split strings to separate file --- strings.cpp | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++ strings.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 strings.cpp create mode 100644 strings.h diff --git a/strings.cpp b/strings.cpp new file mode 100644 index 00000000..97cdac2d --- /dev/null +++ b/strings.cpp @@ -0,0 +1,116 @@ +// -*- C++ -*- +/* + * Open EVSE Firmware + * + * This file is part of Open EVSE. + + * Open EVSE 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 3, or (at your option) + * any later version. + + * Open EVSE 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 Open EVSE; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "open_evse.h" + +const char VERSTR[] PROGMEM = VERSION; + + +#if defined(BTN_MENU) || defined(SHOW_DISABLED_TESTS) +const char g_psSettings[] PROGMEM = "Settings"; +const char g_psSetup[] PROGMEM = "Setup"; +const char g_psSvcLevel[] PROGMEM = "Service Level"; +const char g_psMaxCurrent[] PROGMEM = "Max Current"; +const char g_psDiodeCheck[] PROGMEM = "Diode Check"; +const char g_psVentReqChk[] PROGMEM = "Vent Req'd Check"; +#ifdef RGBLCD +const char g_psBklType[] PROGMEM = "Backlight Type"; +#endif +#ifdef ADVPWR +const char g_psGndChk[] PROGMEM = "Ground Check"; +const char g_psRlyChk[] PROGMEM = "Stuck Relay Chk"; +#endif // ADVPWR +#ifdef GFI_SELFTEST +const char g_psGfiTest[] PROGMEM = "GFI Self Test"; +#endif +#endif // BTN_MENU || SHOW_DISABLED_TEST + +#ifdef BTN_MENU +const char g_psReset[] PROGMEM = "Restart"; +const char g_psExit[] PROGMEM = "Exit"; +// Add additional strings - GoldServe +#ifdef AUTOSTART_MENU +const char g_psAutoStart[] PROGMEM = "Auto Start"; +#endif //#ifdef AUTOSTART_MENU +#ifdef DELAYTIMER_MENU +const char g_psRTC[] PROGMEM = "Date/Time"; +const char g_psRTC_Month[] PROGMEM = "Month"; +const char g_psRTC_Day[] PROGMEM = "Day"; +const char g_psRTC_Year[] PROGMEM = "Year"; +const char g_psRTC_Hour[] PROGMEM = "Hour"; +const char g_psRTC_Minute[] PROGMEM = "Minute"; +const char g_psDelayTimer[] PROGMEM = "Delay Timer"; +const char g_psDelayTimerStartHour[] PROGMEM = "Start Hour"; +const char g_psDelayTimerStartMin[] PROGMEM = "Start Min"; +const char g_psDelayTimerStopHour[] PROGMEM = "Stop Hour"; +const char g_psDelayTimerStopMin[] PROGMEM = "Stop Min"; +#endif // DELAYTIMER_MENU +#ifdef CHARGE_LIMIT +const char g_psChargeLimit[] PROGMEM = "Charge Limit"; +#endif // CHARGE_LIMIT +#ifdef TIME_LIMIT +const char g_psTimeLimit[] PROGMEM = "Time Limit"; +#endif // TIME_LIMIT +#ifdef RGBLCD +char *g_BklMenuItems[] = {"RGB","Monochrome"}; +#endif // RGBLCD +#endif // BTN_MENU + +#ifdef LCD16X2 +#ifdef ADVPWR +const char g_psPwrOn[] PROGMEM = "Power On"; +const char g_psSelfTest[] PROGMEM = "Self Test"; +const char g_psAutoDetect[] PROGMEM = "Auto Detect"; +const char g_psLevel1[] PROGMEM = "Svc Level: L1"; +const char g_psLevel2[] PROGMEM = "Svc Level: L2"; +const char g_psTestFailed[] PROGMEM = "TEST FAILED"; +#endif // ADVPWR +const char g_psEvseError[] PROGMEM = "EVSE ERROR"; +const char g_psSvcReq[] PROGMEM = "SERVICE REQUIRED"; +const char g_psVentReq[] PROGMEM = "VENT REQUIRED"; +const char g_psDiodeChkFailed[] PROGMEM = "DIODE CHECK"; +const char g_psGfciFault[] PROGMEM = "GFCI FAULT"; +const char g_psGfci[] PROGMEM = "GFCI"; +#ifdef TEMPERATURE_MONITORING +const char g_psTemperatureFault[] PROGMEM = "OVER TEMPERATURE"; +#endif +const char g_psNoGround[] PROGMEM = "NO GROUND"; +const char g_psStuckRelay[] PROGMEM = "STUCK RELAY"; +const char g_psDisabled[] PROGMEM = "Disabled"; +const char g_psWaiting[] PROGMEM = "Waiting"; +const char g_psSleeping[] PROGMEM = "Sleeping"; +const char g_psEvConnected[] PROGMEM = "Connected"; +#ifdef SHOW_DISABLED_TESTS +const char g_psDisabledTests[] PROGMEM = "TEST DISABLED"; +#endif + +char g_sRdyLAstr[] = "L%d:%dA"; +const char g_psReady[] PROGMEM = "Ready"; +const char g_psCharging[] PROGMEM = "Charging"; +char *g_sMaxCurrentFmt = "%s Max Current"; +#endif // LCD16X2 + +#ifdef DELAYTIMER_MENU +char *g_YesNoMenuItems[] = {"Yes","No"}; +const char g_psResetNow[] PROGMEM = "Restart Now?"; +const char g_psSetDateTime[] PROGMEM = "Set Date/Time?"; +char *g_DelayMenuItems[] = {"Yes/No","Set Start","Set Stop"}; +#endif // DELAYTIMER_MENU diff --git a/strings.h b/strings.h new file mode 100644 index 00000000..89387277 --- /dev/null +++ b/strings.h @@ -0,0 +1,118 @@ +// -*- C++ -*- +/* + * Open EVSE Firmware + * + * This file is part of Open EVSE. + + * Open EVSE 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 3, or (at your option) + * any later version. + + * Open EVSE 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 Open EVSE; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +extern const char VERSTR[] PROGMEM; +inline void GetVerStr(char *buf) { strcpy_P(buf,VERSTR); } + + +#if defined(BTN_MENU) || defined(SHOW_DISABLED_TESTS) +extern const char g_psSettings[] PROGMEM; +extern const char g_psSetup[] PROGMEM; +extern const char g_psSvcLevel[] PROGMEM; +extern const char g_psMaxCurrent[] PROGMEM; +extern const char g_psDiodeCheck[] PROGMEM; +extern const char g_psVentReqChk[] PROGMEM; +#ifdef RGBLCD +extern const char g_psBklType[] PROGMEM; +#endif +#ifdef ADVPWR +extern const char g_psGndChk[] PROGMEM; +extern const char g_psRlyChk[] PROGMEM; +#endif // ADVPWR +#ifdef GFI_SELFTEST +extern const char g_psGfiTest[] PROGMEM; +#endif +#endif // BTN_MENU || SHOW_DISABLED_TEST + +#ifdef BTN_MENU +extern const char g_psReset[] PROGMEM; +extern const char g_psExit[] PROGMEM; +// Add additional strings - GoldServe +#ifdef AUTOSTART_MENU +extern const char g_psAutoStart[] PROGMEM; +#endif //#ifdef AUTOSTART_MENU +#ifdef DELAYTIMER_MENU +extern const char g_psRTC[] PROGMEM; +extern const char g_psRTC_Month[] PROGMEM; +extern const char g_psRTC_Day[] PROGMEM; +extern const char g_psRTC_Year[] PROGMEM; +extern const char g_psRTC_Hour[] PROGMEM; +extern const char g_psRTC_Minute[] PROGMEM; +extern const char g_psDelayTimer[] PROGMEM; +extern const char g_psDelayTimerStartHour[] PROGMEM; +extern const char g_psDelayTimerStartMin[] PROGMEM; +extern const char g_psDelayTimerStopHour[] PROGMEM; +extern const char g_psDelayTimerStopMin[] PROGMEM; +#endif // DELAYTIMER_MENU +#ifdef CHARGE_LIMIT +extern const char g_psChargeLimit[] PROGMEM; +#endif // CHARGE_LIMIT +#ifdef TIME_LIMIT +extern const char g_psTimeLimit[] PROGMEM; +#endif // TIME_LIMIT +#ifdef RGBLCD +extern char *g_BklMenuItems[]; +#endif // RGBLCD +#endif // BTN_MENU + +#ifdef LCD16X2 +#ifdef ADVPWR +extern const char g_psPwrOn[] PROGMEM; +extern const char g_psSelfTest[] PROGMEM; +extern const char g_psAutoDetect[] PROGMEM; +extern const char g_psLevel1[] PROGMEM; +extern const char g_psLevel2[] PROGMEM; +extern const char g_psTestFailed[] PROGMEM; +#endif // ADVPWR +extern const char g_psEvseError[] PROGMEM; +extern const char g_psSvcReq[] PROGMEM; +extern const char g_psVentReq[] PROGMEM; +extern const char g_psDiodeChkFailed[] PROGMEM; +extern const char g_psGfciFault[] PROGMEM; +extern const char g_psGfci[] PROGMEM; +#ifdef TEMPERATURE_MONITORING +extern const char g_psTemperatureFault[] PROGMEM; +#endif +extern const char g_psNoGround[] PROGMEM; +extern const char g_psStuckRelay[] PROGMEM; +extern const char g_psDisabled[] PROGMEM; +extern const char g_psWaiting[] PROGMEM; +extern const char g_psSleeping[] PROGMEM; +extern const char g_psEvConnected[] PROGMEM; +#ifdef SHOW_DISABLED_TESTS +extern const char g_psDisabledTests[] PROGMEM; +#endif +extern char g_sRdyLAstr[]; +extern const char g_psReady[] PROGMEM; +extern const char g_psCharging[] PROGMEM; +extern char *g_sMaxCurrentFmt; +#endif // LCD16X2 + +#ifdef DELAYTIMER_MENU +extern char *g_YesNoMenuItems[]; +extern const char g_psResetNow[] PROGMEM; +extern const char g_psSetDateTime[] PROGMEM; +extern char *g_DelayMenuItems[]; +#endif // DELAYTIMER_MENU + From e144329e2965ca85369f9825ea6332839ab9e0bf Mon Sep 17 00:00:00 2001 From: lincomatic Date: Wed, 3 Jun 2015 15:19:27 -0700 Subject: [PATCH 14/96] strings, shrink --- open_evse.h | 91 +--------------------- open_evse.ino | 206 +++++++++++--------------------------------------- rapi_proc.cpp | 10 ++- strings.cpp | 4 +- strings.h | 4 +- 5 files changed, 62 insertions(+), 253 deletions(-) diff --git a/open_evse.h b/open_evse.h index de9e69f4..4d7f5ab6 100644 --- a/open_evse.h +++ b/open_evse.h @@ -293,7 +293,8 @@ #define DEFAULT_CURRENT_CAPACITY_L2 16 // minimum allowable current in amps -#define MIN_CURRENT_CAPACITY 6 +#define MIN_CURRENT_CAPACITY_L1 6 +#define MIN_CURRENT_CAPACITY_L2 10 // maximum allowable current in amps #define MAX_CURRENT_CAPACITY_L1 16 // J1772 Max for L1 on a 20A circuit @@ -872,9 +873,8 @@ class SvcLevelMenu : public Menu { }; class MaxCurrentMenu : public Menu { + uint8_t m_MinCurrent; uint8_t m_MaxCurrent; - uint8_t m_MaxIdx; - uint8_t *m_MaxAmpsList; public: MaxCurrentMenu(); void Init(); @@ -1046,8 +1046,6 @@ class DelayMenuStopMin : public Menu { #ifdef CHARGE_LIMIT class ChargeLimitMenu : public Menu { - uint8_t m_kwhLimit; - uint8_t m_MaxIdx; void showCurSel(uint8_t plus=0); public: ChargeLimitMenu(); @@ -1059,8 +1057,6 @@ class ChargeLimitMenu : public Menu { #ifdef TIME_LIMIT class TimeLimitMenu : public Menu { - uint8_t m_timeLimit; - uint8_t m_MaxIdx; void showCurSel(uint8_t plus=0); public: TimeLimitMenu(); @@ -1182,84 +1178,5 @@ extern unsigned long g_WattSeconds; extern TempMonitor g_TempMonitor; #endif // TEMPERATURE_MONITORING -extern const char VERSTR[] PROGMEM; -inline void GetVerStr(char *buf) { strcpy_P(buf,VERSTR); } - - -#if defined(BTN_MENU) || defined(SHOW_DISABLED_TESTS) -extern const char g_psSettings[] PROGMEM; -extern const char g_psSetup[] PROGMEM; -extern const char g_psSvcLevel[] PROGMEM; -extern const char g_psMaxCurrent[] PROGMEM; -extern const char g_psDiodeCheck[] PROGMEM; -extern const char g_psVentReqChk[] PROGMEM; -#ifdef RGBLCD -extern const char g_psBklType[] PROGMEM; -#endif -#ifdef ADVPWR -extern const char g_psGndChk[] PROGMEM; -extern const char g_psRlyChk[] PROGMEM; -#endif // ADVPWR -#ifdef GFI_SELFTEST -extern const char g_psGfiTest[] PROGMEM; -#endif -#endif // BTN_MENU || SHOW_DISABLED_TEST -#ifdef BTN_MENU -extern const char g_psReset[] PROGMEM; -extern const char g_psExit[] PROGMEM; -// Add additional strings - GoldServe -#ifdef AUTOSTART_MENU -extern const char g_psAutoStart[] PROGMEM; -#endif //#ifdef AUTOSTART_MENU -#ifdef DELAYTIMER_MENU -extern const char g_psRTC[] PROGMEM; -extern const char g_psRTC_Month[] PROGMEM; -extern const char g_psRTC_Day[] PROGMEM; -extern const char g_psRTC_Year[] PROGMEM; -extern const char g_psRTC_Hour[] PROGMEM; -extern const char g_psRTC_Minute[] PROGMEM; -extern const char g_psDelayTimer[] PROGMEM; -extern const char g_psDelayTimerStartHour[] PROGMEM; -extern const char g_psDelayTimerStartMin[] PROGMEM; -extern const char g_psDelayTimerStopHour[] PROGMEM; -extern const char g_psDelayTimerStopMin[] PROGMEM; -#endif // DELAYTIMER_MENU -#ifdef CHARGE_LIMIT -extern const char g_psChargeLimit[] PROGMEM; -#endif // CHARGE_LIMIT -#ifdef TIME_LIMIT -extern const char g_psTimeLimit[] PROGMEM; -#endif // TIME_LIMIT -#endif // BTN_MENU - -#ifdef LCD16X2 -#ifdef ADVPWR -extern const char g_psPwrOn[] PROGMEM; -extern const char g_psSelfTest[] PROGMEM; -extern const char g_psAutoDetect[] PROGMEM; -extern const char g_psLevel1[] PROGMEM; -extern const char g_psLevel2[] PROGMEM; -extern const char g_psTestFailed[] PROGMEM; -#endif // ADVPWR -extern const char g_psEvseError[] PROGMEM; -extern const char g_psSvcReq[] PROGMEM; -extern const char g_psVentReq[] PROGMEM; -extern const char g_psDiodeChkFailed[] PROGMEM; -extern const char g_psGfciFault[] PROGMEM; -extern const char g_psGfci[] PROGMEM; -#ifdef TEMPERATURE_MONITORING -extern const char g_psTemperatureFault[] PROGMEM; -#endif -extern const char g_psNoGround[] PROGMEM; -extern const char g_psStuckRelay[] PROGMEM; -extern const char g_psDisabled[] PROGMEM; -extern const char g_psWaiting[] PROGMEM; -extern const char g_psSleeping[] PROGMEM; -extern const char g_psEvConnected[] PROGMEM; -#ifdef SHOW_DISABLED_TESTS -extern const char g_psDisabledTests[] PROGMEM; -#endif -#endif // LCD16X2 - - +#include "strings.h" #include "rapi_proc.h" diff --git a/open_evse.ino b/open_evse.ino index d33bcaa2..20aca923 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -57,53 +57,6 @@ #endif #endif // TEMPERATURE_MONITORING -const char VERSTR[] PROGMEM = VERSION; - - -#if defined(BTN_MENU) || defined(SHOW_DISABLED_TESTS) -const char g_psSettings[] PROGMEM = "Settings"; -const char g_psSetup[] PROGMEM = "Setup"; -const char g_psSvcLevel[] PROGMEM = "Service Level"; -const char g_psMaxCurrent[] PROGMEM = "Max Current"; -const char g_psDiodeCheck[] PROGMEM = "Diode Check"; -const char g_psVentReqChk[] PROGMEM = "Vent Req'd Check"; -#ifdef RGBLCD -const char g_psBklType[] PROGMEM = "Backlight Type"; -#endif -#ifdef ADVPWR -const char g_psGndChk[] PROGMEM = "Ground Check"; -const char g_psRlyChk[] PROGMEM = "Stuck Relay Chk"; -#endif // ADVPWR -#ifdef GFI_SELFTEST -const char g_psGfiTest[] PROGMEM = "GFI Self Test"; -#endif -#endif // BTN_MENU || SHOW_DISABLED_TEST -#ifdef BTN_MENU -const char g_psReset[] PROGMEM = "Restart"; -const char g_psExit[] PROGMEM = "Exit"; -// Add additional strings - GoldServe -#ifdef AUTOSTART_MENU -const char g_psAutoStart[] PROGMEM = "Auto Start"; -#endif //#ifdef AUTOSTART_MENU -#ifdef DELAYTIMER_MENU -const char g_psRTC[] PROGMEM = "Date/Time"; -const char g_psRTC_Month[] PROGMEM = "Month"; -const char g_psRTC_Day[] PROGMEM = "Day"; -const char g_psRTC_Year[] PROGMEM = "Year"; -const char g_psRTC_Hour[] PROGMEM = "Hour"; -const char g_psRTC_Minute[] PROGMEM = "Minute"; -const char g_psDelayTimer[] PROGMEM = "Delay Timer"; -const char g_psDelayTimerStartHour[] PROGMEM = "Start Hour"; -const char g_psDelayTimerStartMin[] PROGMEM = "Start Min"; -const char g_psDelayTimerStopHour[] PROGMEM = "Stop Hour"; -const char g_psDelayTimerStopMin[] PROGMEM = "Stop Min"; -#endif // DELAYTIMER_MENU -#ifdef CHARGE_LIMIT -const char g_psChargeLimit[] PROGMEM = "Charge Limit"; -#endif // CHARGE_LIMIT -#ifdef TIME_LIMIT -const char g_psTimeLimit[] PROGMEM = "Time Limit"; -#endif // TIME_LIMIT SettingsMenu g_SettingsMenu; SetupMenu g_SetupMenu; @@ -187,6 +140,7 @@ Menu *g_SetupMenuList[] = { NULL }; +#ifdef BTN_MENU BtnHandler g_BtnHandler; #endif // BTN_MENU #ifdef DELAYTIMER @@ -227,34 +181,6 @@ uint8_t sec = 0; #endif // DELAYTIMER_MENU #endif // DELAYTIMER -#ifdef LCD16X2 -#ifdef ADVPWR -const char g_psPwrOn[] PROGMEM = "Power On"; -const char g_psSelfTest[] PROGMEM = "Self Test"; -const char g_psAutoDetect[] PROGMEM = "Auto Detect"; -const char g_psLevel1[] PROGMEM = "Svc Level: L1"; -const char g_psLevel2[] PROGMEM = "Svc Level: L2"; -const char g_psTestFailed[] PROGMEM = "TEST FAILED"; -#endif // ADVPWR -const char g_psEvseError[] PROGMEM = "EVSE ERROR"; -const char g_psSvcReq[] PROGMEM = "SERVICE REQUIRED"; -const char g_psVentReq[] PROGMEM = "VENT REQUIRED"; -const char g_psDiodeChkFailed[] PROGMEM = "DIODE CHECK"; -const char g_psGfciFault[] PROGMEM = "GFCI FAULT"; -const char g_psGfci[] PROGMEM = "GFCI"; -#ifdef TEMPERATURE_MONITORING -const char g_psTemperatureFault[] PROGMEM = "OVER TEMPERATURE"; -#endif -const char g_psNoGround[] PROGMEM = "NO GROUND"; -const char g_psStuckRelay[] PROGMEM = "STUCK RELAY"; -const char g_psDisabled[] PROGMEM = "Disabled"; -const char g_psWaiting[] PROGMEM = "Waiting"; -const char g_psSleeping[] PROGMEM = "Sleeping"; -const char g_psEvConnected[] PROGMEM = "Connected"; -#ifdef SHOW_DISABLED_TESTS -const char g_psDisabledTests[] PROGMEM = "TEST DISABLED"; -#endif -#endif // LCD16X2 #ifdef TEMPERATURE_MONITORING TempMonitor g_TempMonitor; @@ -516,9 +442,6 @@ void OnboardDisplay::LcdMsg(const char *l1,const char *l2) #endif // LCD16X2 -char g_sRdyLAstr[] = "L%d:%dA"; -const char g_psReady[] PROGMEM = "Ready"; -const char g_psCharging[] PROGMEM = "Charging"; void OnboardDisplay::Update(int8_t updmode) { if (updateDisabled()) return; @@ -1109,7 +1032,6 @@ const char *g_SvcLevelMenuItems[] = { }; #ifdef RGBLCD -char *g_BklMenuItems[] = {"RGB","Monochrome"}; BklTypeMenu::BklTypeMenu() { m_Title = g_psBklType; @@ -1208,8 +1130,6 @@ Menu *SvcLevelMenu::Select() return &g_SetupMenu; } -uint8_t g_L1MaxAmps[] = {6,10,12,14,15,16,0}; -uint8_t g_L2MaxAmps[] = {10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,0}; MaxCurrentMenu::MaxCurrentMenu() { m_Title = g_psMaxCurrent; @@ -1218,54 +1138,50 @@ MaxCurrentMenu::MaxCurrentMenu() void MaxCurrentMenu::Init() { - m_CurIdx = 0; uint8_t cursvclvl = g_EvseController.GetCurSvcLevel(); - m_MaxAmpsList = (cursvclvl == 1) ? g_L1MaxAmps : g_L2MaxAmps; - m_MaxCurrent = 0; - uint8_t currentlimit = (cursvclvl == 1) ? MAX_CURRENT_CAPACITY_L1 : MAX_CURRENT_CAPACITY_L2; - - for (m_MaxIdx=0;m_MaxAmpsList[m_MaxIdx] != 0;m_MaxIdx++); - m_MaxCurrent = m_MaxAmpsList[--m_MaxIdx]; - - for (uint8_t i=0;i <= m_MaxIdx;i++) { - if (m_MaxAmpsList[i] == g_EvseController.GetCurrentCapacity()) { - m_CurIdx = i; - break; - } + if (cursvclvl == 1) { + m_MinCurrent = MIN_CURRENT_CAPACITY_L1; + m_MaxCurrent = MAX_CURRENT_CAPACITY_L1; + } + else { + m_MinCurrent = MIN_CURRENT_CAPACITY_L2; + m_MaxCurrent = MAX_CURRENT_CAPACITY_L2; } - sprintf(g_sTmp,"%s Max Current",(cursvclvl == 1) ? "L1" : "L2"); + sprintf(g_sTmp,g_sMaxCurrentFmt,(cursvclvl == 1) ? "L1" : "L2"); g_OBD.LcdPrint(0,g_sTmp); - sprintf(g_sTmp,"+%dA",m_MaxAmpsList[m_CurIdx]); + m_CurIdx = g_EvseController.GetCurrentCapacity(); + sprintf(g_sTmp,"+%dA",m_CurIdx); g_OBD.LcdPrint(1,g_sTmp); } void MaxCurrentMenu::Next() { - if (++m_CurIdx > m_MaxIdx) { - m_CurIdx = 0; + m_CurIdx += 2; + if (m_CurIdx > m_MaxCurrent) { + m_CurIdx = m_MinCurrent; } g_OBD.LcdClearLine(1); g_OBD.LcdSetCursor(0,1); - if (g_EvseController.GetCurrentCapacity() == m_MaxAmpsList[m_CurIdx]) { + if (g_EvseController.GetCurrentCapacity() == m_CurIdx) { g_OBD.LcdPrint(g_sPlus); } - g_OBD.LcdPrint(m_MaxAmpsList[m_CurIdx]); + g_OBD.LcdPrint(m_CurIdx); g_OBD.LcdPrint("A"); } Menu *MaxCurrentMenu::Select() { g_OBD.LcdPrint(0,1,g_sPlus); - g_OBD.LcdPrint(m_MaxAmpsList[m_CurIdx]); + g_OBD.LcdPrint(m_CurIdx); g_OBD.LcdPrint("A"); delay(500); - eeprom_write_byte((uint8_t*)((g_EvseController.GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),m_MaxAmpsList[m_CurIdx]); - g_EvseController.SetCurrentCapacity(m_MaxAmpsList[m_CurIdx]); + eeprom_write_byte((uint8_t*)((g_EvseController.GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),m_CurIdx); + g_EvseController.SetCurrentCapacity(m_CurIdx); return &g_SetupMenu; } -char *g_YesNoMenuItems[] = {"Yes","No"}; + DiodeChkMenu::DiodeChkMenu() { m_Title = g_psDiodeCheck; @@ -1469,7 +1385,7 @@ ResetMenu::ResetMenu() m_Title = g_psReset; } -const char g_psResetNow[] PROGMEM = "Restart Now?"; + void ResetMenu::Init() { m_CurIdx = 0; @@ -1514,7 +1430,7 @@ RTCMenu::RTCMenu() { m_Title = g_psRTC; } -const char g_psSetDateTime[] PROGMEM = "Set Date/Time?"; + void RTCMenu::Init() { m_CurIdx = 0; @@ -1673,7 +1589,6 @@ Menu *RTCMenuMinute::Select() delay(500); return &g_SetupMenu; } -char *g_DelayMenuItems[] = {"Yes/No","Set Start","Set Stop"}; void HsStrPrint1(uint8_t h,uint8_t m,int8_t pluspos=-1) { @@ -1895,7 +1810,7 @@ Menu *AutoStartMenu::Select() #endif //#ifdef AUTOSTART_MENU #ifdef CHARGE_LIMIT -uint8_t g_ChargeLimitList[] = {0,1,2,3,4,5,10,15,20,30,0}; +#define MAX_CHARGE_LIMIT 40 ChargeLimitMenu::ChargeLimitMenu() { @@ -1906,11 +1821,11 @@ void ChargeLimitMenu::showCurSel(uint8_t plus) { *g_sTmp = 0; if (plus) strcpy(g_sTmp,g_sPlus); - if (g_ChargeLimitList[m_CurIdx] == 0) { + if (m_CurIdx == 0) { strcat(g_sTmp,"off"); } else { - strcat(g_sTmp,u2a(g_ChargeLimitList[m_CurIdx])); + strcat(g_sTmp,u2a(m_CurIdx)); strcat(g_sTmp," kWh"); } g_OBD.LcdPrint(1,g_sTmp); @@ -1919,57 +1834,41 @@ void ChargeLimitMenu::showCurSel(uint8_t plus) void ChargeLimitMenu::Init() { - m_CurIdx = 0; - m_kwhLimit = g_EvseController.GetChargeLimit(); - - for (m_MaxIdx=1;g_ChargeLimitList[m_MaxIdx] != 0;m_MaxIdx++); + m_CurIdx = g_EvseController.GetChargeLimit(); - if (m_kwhLimit) { - for (uint8_t i=0;i <= m_MaxIdx;i++) { - if (g_ChargeLimitList[i] == m_kwhLimit) { - m_CurIdx = i; - break; - } - } - } - else { - m_CurIdx = 0; - m_kwhLimit = 0; - } - g_OBD.LcdPrint_P(0,g_psChargeLimit); showCurSel(1); } void ChargeLimitMenu::Next() { - if (++m_CurIdx > m_MaxIdx) { + if (m_CurIdx < 5) m_CurIdx++; + else m_CurIdx += 5; + if (m_CurIdx > MAX_CHARGE_LIMIT) { m_CurIdx = 0; } - showCurSel((m_kwhLimit == g_ChargeLimitList[m_CurIdx]) ? 1 : 0); + showCurSel((g_EvseController.GetChargeLimit() == m_CurIdx) ? 1 : 0); } Menu *ChargeLimitMenu::Select() { - uint8_t limit = g_ChargeLimitList[m_CurIdx]; showCurSel(1); - g_EvseController.SetChargeLimit(limit); + g_EvseController.SetChargeLimit(m_CurIdx); #ifdef TIME_LIMIT - if (limit) { + if (m_CurIdx) { g_EvseController.SetTimeLimit(0); } #endif // TIME_LIMIT delay(500); - return limit ? NULL : &g_SettingsMenu; + return m_CurIdx ? NULL : &g_SettingsMenu; } #endif // CHARGE_LIMIT #ifdef TIME_LIMIT -// above 60min must be in half hour increments < 256min -// uint8_t g_TimeLimitList[] = {0,15,30,60,90,120,180,240,300,360,420,480,0}; // minutes -uint8_t g_TimeLimitList[] = {0,1,2,4,6,8,10,12,16,20,24,28,32,0}; // 15 min increments +// max time limit = MAX_TIME_LIMIT_D15 * 15 minutes +#define MAX_TIME_LIMIT_D15 32 TimeLimitMenu::TimeLimitMenu() { @@ -1978,7 +1877,7 @@ TimeLimitMenu::TimeLimitMenu() void TimeLimitMenu::showCurSel(uint8_t plus) { - uint16_t limit = g_TimeLimitList[m_CurIdx] * 15; + uint16_t limit = m_CurIdx * 15; *g_sTmp = 0; if (plus) strcpy(g_sTmp,g_sPlus); if (limit == 0) { @@ -2003,50 +1902,35 @@ void TimeLimitMenu::showCurSel(uint8_t plus) void TimeLimitMenu::Init() { - m_CurIdx = 0; - m_timeLimit = g_EvseController.GetTimeLimit(); - - for (m_MaxIdx=1;g_TimeLimitList[m_MaxIdx] != 0;m_MaxIdx++); + m_CurIdx = g_EvseController.GetTimeLimit(); - if (m_timeLimit) { - for (uint8_t i=0;i <= m_MaxIdx;i++) { - if (g_TimeLimitList[i] == m_timeLimit) { - m_CurIdx = i; - break; - } - } - } - else { - m_CurIdx = 0; - m_timeLimit = 0; - } - g_OBD.LcdPrint_P(0,g_psTimeLimit); showCurSel(1); } void TimeLimitMenu::Next() { - if (++m_CurIdx > m_MaxIdx) { + if (m_CurIdx < 4) m_CurIdx++; + else if (m_CurIdx < 12) m_CurIdx += 2; + else m_CurIdx += 4; + if (m_CurIdx > MAX_TIME_LIMIT_D15) { m_CurIdx = 0; } - showCurSel((m_timeLimit == g_TimeLimitList[m_CurIdx]) ? 1 : 0); + showCurSel((g_EvseController.GetTimeLimit() == m_CurIdx) ? 1 : 0); } Menu *TimeLimitMenu::Select() { - uint8_t limit = g_TimeLimitList[m_CurIdx]; showCurSel(1); - g_EvseController.SetTimeLimit(limit); + g_EvseController.SetTimeLimit(m_CurIdx); #ifdef CHARGE_LIMIT - if (limit) { + if (m_CurIdx) { g_EvseController.SetChargeLimit(0); } #endif // CHARGE_LIMIT delay(500); - return limit ? NULL : &g_SettingsMenu; + return m_CurIdx ? NULL : &g_SettingsMenu; } - #endif // TIME_LIMIT Menu *ResetMenu::Select() diff --git a/rapi_proc.cpp b/rapi_proc.cpp index 346c3f79..e1187d95 100644 --- a/rapi_proc.cpp +++ b/rapi_proc.cpp @@ -365,7 +365,15 @@ int EvseRapiProcessor::processCmd() break; #endif // AMMETER case 'C': // get current capacity range - sprintf(buffer,"%d %d",MIN_CURRENT_CAPACITY,(g_EvseController.GetCurSvcLevel() == 2) ? MAX_CURRENT_CAPACITY_L2 : MAX_CURRENT_CAPACITY_L1); + if (g_EvseController.GetCurSvcLevel() == 2) { + u1.i = MIN_CURRENT_CAPACITY_L2; + u2.i = MAX_CURRENT_CAPACITY_L2; + } + else { + u1.i = MIN_CURRENT_CAPACITY_L1; + u2.i = MAX_CURRENT_CAPACITY_L1; + } + sprintf(buffer,"%d %d",u1.i,u2.i); bufCnt = 1; // flag response text output break; case 'E': // get settings diff --git a/strings.cpp b/strings.cpp index 97cdac2d..66d45ecf 100644 --- a/strings.cpp +++ b/strings.cpp @@ -44,6 +44,8 @@ const char g_psGfiTest[] PROGMEM = "GFI Self Test"; #endif // BTN_MENU || SHOW_DISABLED_TEST #ifdef BTN_MENU +char *g_YesNoMenuItems[] = {"Yes","No"}; +const char g_psResetNow[] PROGMEM = "Restart Now?"; const char g_psReset[] PROGMEM = "Restart"; const char g_psExit[] PROGMEM = "Exit"; // Add additional strings - GoldServe @@ -109,8 +111,6 @@ char *g_sMaxCurrentFmt = "%s Max Current"; #endif // LCD16X2 #ifdef DELAYTIMER_MENU -char *g_YesNoMenuItems[] = {"Yes","No"}; -const char g_psResetNow[] PROGMEM = "Restart Now?"; const char g_psSetDateTime[] PROGMEM = "Set Date/Time?"; char *g_DelayMenuItems[] = {"Yes/No","Set Start","Set Stop"}; #endif // DELAYTIMER_MENU diff --git a/strings.h b/strings.h index 89387277..e20651ac 100644 --- a/strings.h +++ b/strings.h @@ -46,6 +46,8 @@ extern const char g_psGfiTest[] PROGMEM; #endif // BTN_MENU || SHOW_DISABLED_TEST #ifdef BTN_MENU +extern char *g_YesNoMenuItems[]; +extern const char g_psResetNow[] PROGMEM; extern const char g_psReset[] PROGMEM; extern const char g_psExit[] PROGMEM; // Add additional strings - GoldServe @@ -110,8 +112,6 @@ extern char *g_sMaxCurrentFmt; #endif // LCD16X2 #ifdef DELAYTIMER_MENU -extern char *g_YesNoMenuItems[]; -extern const char g_psResetNow[] PROGMEM; extern const char g_psSetDateTime[] PROGMEM; extern char *g_DelayMenuItems[]; #endif // DELAYTIMER_MENU From dfa8c0361e3e57db3df4449f9768b6110ff70bb7 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 09:27:36 -0700 Subject: [PATCH 15/96] add file --- Gfi.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Gfi.h diff --git a/Gfi.h b/Gfi.h new file mode 100644 index 00000000..2d1ff875 --- /dev/null +++ b/Gfi.h @@ -0,0 +1,44 @@ +/* + * Open EVSE Firmware + * + * This file is part of Open EVSE. + + * Open EVSE 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 3, or (at your option) + * any later version. + + * Open EVSE 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 Open EVSE; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#pragma once + +class Gfi { + DigitalPin pin; + uint8_t m_GfiFault; +#ifdef GFI_SELFTEST + DigitalPin pinTest; + uint8_t testSuccess; + uint8_t testInProgress; +#endif // GFI_SELFTEST +public: + Gfi() {} + + void Init(); + void Reset(); + void SetFault() { m_GfiFault = 1; } + uint8_t Fault() { return m_GfiFault; } +#ifdef GFI_SELFTEST + uint8_t SelfTest(); + void SetTestSuccess() { testSuccess = 1; } + uint8_t SelfTestSuccess() { return testSuccess; } + uint8_t SelfTestInProgress() { return testInProgress; } +#endif +}; From d660f83641a521e4ea27f0c20c8e4b982f4fe56d Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 11:38:38 -0700 Subject: [PATCH 16/96] OnboardDisplay::Update() - run non-state transition updates only once/sec --- open_evse.ino | 374 +++++++++++++++++++++++++------------------------- 1 file changed, 188 insertions(+), 186 deletions(-) diff --git a/open_evse.ino b/open_evse.ino index 20aca923..898da1bb 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -449,11 +449,10 @@ void OnboardDisplay::Update(int8_t updmode) int i; uint8_t curstate = g_EvseController.GetState(); uint8_t svclvl = g_EvseController.GetCurSvcLevel(); + unsigned long curms = millis(); if (g_EvseController.StateTransition() || (updmode != OBD_UPD_NORMAL)) { - // Optimize function call - GoldServe sprintf(g_sTmp,g_sRdyLAstr,(int)svclvl,(int)g_EvseController.GetCurrentCapacity()); - switch(curstate) { case EVSE_STATE_A: // not connected SetGreenLed(1); @@ -540,249 +539,252 @@ void OnboardDisplay::Update(int8_t updmode) #endif //Adafruit RGB LCD // n.b. blue LED is on break; - case EVSE_STATE_D: // vent required - SetGreenLed(0); - SetRedLed(1); + case EVSE_STATE_D: // vent required + SetGreenLed(0); + SetRedLed(1); #ifdef LCD16X2 //Adafruit RGB LCD - LcdSetBacklightColor(RED); - LcdMsg_P(g_psEvseError,g_psVentReq); + LcdSetBacklightColor(RED); + LcdMsg_P(g_psEvseError,g_psVentReq); #endif //Adafruit RGB LCD - // n.b. blue LED is off - break; - case EVSE_STATE_DIODE_CHK_FAILED: - SetGreenLed(0); - SetRedLed(1); + // n.b. blue LED is off + break; + case EVSE_STATE_DIODE_CHK_FAILED: + SetGreenLed(0); + SetRedLed(1); #ifdef LCD16X2 //Adafruit RGB LCD - LcdSetBacklightColor(RED); - LcdMsg_P(g_psEvseError,g_psDiodeChkFailed); + LcdSetBacklightColor(RED); + LcdMsg_P(g_psEvseError,g_psDiodeChkFailed); #endif //Adafruit RGB LCD - // n.b. blue LED is off - break; - case EVSE_STATE_GFCI_FAULT: - SetGreenLed(0); - SetRedLed(1); + // n.b. blue LED is off + break; + case EVSE_STATE_GFCI_FAULT: + SetGreenLed(0); + SetRedLed(1); #ifdef LCD16X2 //Adafruit RGB LCD - LcdSetBacklightColor(RED); - LcdMsg_P(updmode == OBD_UPD_HARDFAULT ? g_psSvcReq : g_psEvseError,g_psGfciFault); + LcdSetBacklightColor(RED); + LcdMsg_P(updmode == OBD_UPD_HARDFAULT ? g_psSvcReq : g_psEvseError,g_psGfciFault); #endif //Adafruit RGB LCD - // n.b. blue LED is off - break; + // n.b. blue LED is off + break; #ifdef TEMPERATURE_MONITORING - case EVSE_STATE_OVER_TEMPERATURE: // overtemp message in Red on the RGB LCD - SetGreenLed(0); - SetRedLed(1); + case EVSE_STATE_OVER_TEMPERATURE: // overtemp message in Red on the RGB LCD + SetGreenLed(0); + SetRedLed(1); #ifdef LCD16X2 //Adafruit RGB LCD - LcdSetBacklightColor(RED); - LcdMsg_P(g_psSvcReq,g_psTemperatureFault); // SERVICE REQUIRED OVER TEMPERATURE + LcdSetBacklightColor(RED); + LcdMsg_P(g_psSvcReq,g_psTemperatureFault); // SERVICE REQUIRED OVER TEMPERATURE #endif - break; + break; #endif //TEMPERATURE_MONITORING - case EVSE_STATE_NO_GROUND: - SetGreenLed(0); - SetRedLed(1); + case EVSE_STATE_NO_GROUND: + SetGreenLed(0); + SetRedLed(1); #ifdef LCD16X2 //Adafruit RGB LCD - LcdSetBacklightColor(RED); - LcdMsg_P(updmode == OBD_UPD_HARDFAULT ? g_psSvcReq : g_psEvseError,g_psNoGround); + LcdSetBacklightColor(RED); + LcdMsg_P(updmode == OBD_UPD_HARDFAULT ? g_psSvcReq : g_psEvseError,g_psNoGround); #endif //Adafruit RGB LCD - // n.b. blue LED is off - break; - case EVSE_STATE_STUCK_RELAY: - SetGreenLed(0); - SetRedLed(1); + // n.b. blue LED is off + break; + case EVSE_STATE_STUCK_RELAY: + SetGreenLed(0); + SetRedLed(1); #ifdef LCD16X2 //Adafruit RGB LCD - LcdSetBacklightColor(RED); - LcdMsg_P(updmode == OBD_UPD_HARDFAULT ? g_psSvcReq : g_psEvseError,g_psStuckRelay); + LcdSetBacklightColor(RED); + LcdMsg_P(updmode == OBD_UPD_HARDFAULT ? g_psSvcReq : g_psEvseError,g_psStuckRelay); #endif //Adafruit RGB LCD - // n.b. blue LED is off - break; - case EVSE_STATE_DISABLED: - SetGreenLed(0); - SetRedLed(1); + // n.b. blue LED is off + break; + case EVSE_STATE_DISABLED: + SetGreenLed(0); + SetRedLed(1); #ifdef LCD16X2 - LcdSetBacklightColor(VIOLET); - LcdClear(); - LcdSetCursor(0,0); - LcdPrint_P(g_psDisabled); - LcdPrint(10,0,g_sTmp); + LcdSetBacklightColor(VIOLET); + LcdClear(); + LcdSetCursor(0,0); + LcdPrint_P(g_psDisabled); + LcdPrint(10,0,g_sTmp); #endif // LCD16X2 - break; - case EVSE_STATE_GFI_TEST_FAILED: - SetGreenLed(0); - SetRedLed(1); - LcdSetBacklightColor(RED); - LcdMsg_P(g_psTestFailed,g_psGfci); - break; - case EVSE_STATE_SLEEPING: - SetGreenLed(1); - SetRedLed(1); + break; + case EVSE_STATE_GFI_TEST_FAILED: + SetGreenLed(0); + SetRedLed(1); + LcdSetBacklightColor(RED); + LcdMsg_P(g_psTestFailed,g_psGfci); + break; + case EVSE_STATE_SLEEPING: + SetGreenLed(1); + SetRedLed(1); #ifdef LCD16X2 - LcdSetBacklightColor(VIOLET); - LcdClear(); - LcdSetCursor(0,0); - LcdPrint_P(g_psSleeping); - LcdPrint(10,0,g_sTmp); + LcdSetBacklightColor(VIOLET); + LcdClear(); + LcdSetCursor(0,0); + LcdPrint_P(g_psSleeping); + LcdPrint(10,0,g_sTmp); #endif // LCD16X2 - break; - default: - SetGreenLed(0); - SetRedLed(1); - // n.b. blue LED is off - } + break; + default: + SetGreenLed(0); + SetRedLed(1); + // n.b. blue LED is off + } } + // + // put anything that needs to be updated periodically here + // the code below will only run once per second + // + if ((curms-m_LastUpdateMs) >= 1000) { +#ifdef RTC + g_CurrTime = g_RTC.now(); +#endif + + #if defined(AMMETER) && defined(LCD16X2) if (((curstate == EVSE_STATE_C) || g_EvseController.AmmeterCalEnabled()) && AmmeterIsDirty()) { - SetAmmeterDirty(0); + SetAmmeterDirty(0); - uint32_t current = g_EvseController.GetChargingCurrent(); + uint32_t current = g_EvseController.GetChargingCurrent(); - if (current >= 1000) { // display only if > 1000 - int a = current / 1000; - int ma = (current % 1000) / 100; - if (ma > 9) { - ma = 0; - a++; + if (current >= 1000) { // display only if > 1000 + int a = current / 1000; + int ma = (current % 1000) / 100; + if (ma > 9) { + ma = 0; + a++; + } + sprintf(g_sTmp,"%3d.%dA",a,ma); + } + else { + strcpy_P(g_sTmp,PSTR(" 0A")); + } + LcdPrint(10,0,g_sTmp); } - sprintf(g_sTmp,"%3d.%dA",a,ma); - } - else { - strcpy_P(g_sTmp,PSTR(" 0A")); - } - LcdPrint(10,0,g_sTmp); - } #endif // AMMETER -#if defined(DELAYTIMER) && defined(LCD16X2) - DateTime curtime = g_RTC.now(); -#endif //#ifdef DELAYTIMER - #ifdef LCD16X2 - if (curstate == EVSE_STATE_C) { - time_t elapsedTime = g_EvseController.GetElapsedChargeTime(); - if (elapsedTime != g_EvseController.GetElapsedChargeTimePrev()) { + if (curstate == EVSE_STATE_C) { + time_t elapsedTime = g_EvseController.GetElapsedChargeTime(); + if (elapsedTime != g_EvseController.GetElapsedChargeTimePrev()) { - #ifdef KWH_RECORDING - uint32_t current = g_EvseController.GetChargingCurrent(); - #ifdef VOLTMETER - g_WattSeconds = g_WattSeconds + (g_EvseController.GetVoltage() / 1000 * current / 1000); - #else - if (g_EvseController.GetCurSvcLevel() == 2) { // first verify L2 or L1 - g_WattSeconds = g_WattSeconds + (VOLTS_FOR_L2 * current / 1000); // accumulate Watt Seconds for Level2 charging - } - else { - g_WattSeconds = g_WattSeconds + (VOLTS_FOR_L1 * current / 1000); // accumulate Watt Seconds for Level1 charging - } - #endif // VOLTMETER - sprintf(g_sTmp,"%5luWh",(g_WattSeconds / 3600) ); - LcdPrint(0,1,g_sTmp); - - #ifdef VOLTMETER - sprintf(g_sTmp," %3luV",(g_EvseController.GetVoltage() / 1000)); // Display voltage from OpenEVSE II - LcdPrint(11,1,g_sTmp); - #else - sprintf(g_sTmp,"%6lukWh",(g_WattHours_accumulated / 1000)); // display accumulated kWh - LcdPrint(7,1,g_sTmp); - #endif // VOLTMETER - #else // ! KWH_RECORDING - LcdClearLine(0); - #endif // KWH_RECORDING +#ifdef KWH_RECORDING + uint32_t current = g_EvseController.GetChargingCurrent(); +#ifdef VOLTMETER + g_WattSeconds = g_WattSeconds + (g_EvseController.GetVoltage() / 1000 * current / 1000); +#else + if (g_EvseController.GetCurSvcLevel() == 2) { // first verify L2 or L1 + g_WattSeconds = g_WattSeconds + (VOLTS_FOR_L2 * current / 1000); // accumulate Watt Seconds for Level2 charging + } + else { + g_WattSeconds = g_WattSeconds + (VOLTS_FOR_L1 * current / 1000); // accumulate Watt Seconds for Level1 charging + } +#endif // VOLTMETER + sprintf(g_sTmp,"%5luWh",(g_WattSeconds / 3600) ); + LcdPrint(0,1,g_sTmp); + +#ifdef VOLTMETER + sprintf(g_sTmp," %3luV",(g_EvseController.GetVoltage() / 1000)); // Display voltage from OpenEVSE II + LcdPrint(11,1,g_sTmp); +#else + sprintf(g_sTmp,"%6lukWh",(g_WattHours_accumulated / 1000)); // display accumulated kWh + LcdPrint(7,1,g_sTmp); +#endif // VOLTMETER +#else // ! KWH_RECORDING + LcdClearLine(0); +#endif // KWH_RECORDING #ifdef TEMPERATURE_MONITORING - if ((g_TempMonitor.OverTemperature()) || TEMPERATURE_DISPLAY_ALWAYS) { - g_OBD.LcdClearLine(1); - static const char *tempfmt = "%2d.%1dC"; + if ((g_TempMonitor.OverTemperature()) || TEMPERATURE_DISPLAY_ALWAYS) { + g_OBD.LcdClearLine(1); + const char *tempfmt = "%2d.%1dC"; #ifdef MCP9808_IS_ON_I2C - if ( g_TempMonitor.m_MCP9808_temperature != 0 ) { // it returns 0 if it is not present - sprintf(g_sTmp,tempfmt,g_TempMonitor.m_MCP9808_temperature/10, g_TempMonitor.m_MCP9808_temperature % 10); // Ambient sensor near or on the LCD - LcdPrint(0,1,g_sTmp); - } + if ( g_TempMonitor.m_MCP9808_temperature != 0 ) { // it returns 0 if it is not present + sprintf(g_sTmp,tempfmt,g_TempMonitor.m_MCP9808_temperature/10, g_TempMonitor.m_MCP9808_temperature % 10); // Ambient sensor near or on the LCD + LcdPrint(0,1,g_sTmp); + } #endif #ifdef RTC - if ( g_TempMonitor.m_DS3231_temperature != 0) { // it returns 0 if it is not present - sprintf(g_sTmp,tempfmt,g_TempMonitor.m_DS3231_temperature/10, g_TempMonitor.m_DS3231_temperature % 10); // sensor built into the DS3231 RTC Chip - LcdPrint(5,1,g_sTmp); - } + if ( g_TempMonitor.m_DS3231_temperature != 0) { // it returns 0 if it is not present + sprintf(g_sTmp,tempfmt,g_TempMonitor.m_DS3231_temperature/10, g_TempMonitor.m_DS3231_temperature % 10); // sensor built into the DS3231 RTC Chip + LcdPrint(5,1,g_sTmp); + } #endif #ifdef TMP007_IS_ON_I2C - if ( g_TempMonitor.m_TMP007_temperature != 0 ) { // it returns 0 if it is not present - sprintf(g_sTmp,tempfmt,g_TempMonitor.m_TMP007_temperature/10, g_TempMonitor.m_TMP007_temperature % 10); // Infrared sensor probably looking at 30A fuses - LcdPrint(11,1,g_sTmp); - } + if ( g_TempMonitor.m_TMP007_temperature != 0 ) { // it returns 0 if it is not present + sprintf(g_sTmp,tempfmt,g_TempMonitor.m_TMP007_temperature/10, g_TempMonitor.m_TMP007_temperature % 10); // Infrared sensor probably looking at 30A fuses + LcdPrint(11,1,g_sTmp); + } #endif - if (g_TempMonitor.BlinkAlarm() && g_TempMonitor.OverTemperatureShutdown()) { // Blink Red off-and-on while over the temperature shutdown limit, zero current should flow to the EV - g_TempMonitor.SetBlinkAlarm(0); // toggle the alarm flag so we can blink - SetRedLed(1); + if (g_TempMonitor.BlinkAlarm() && g_TempMonitor.OverTemperatureShutdown()) { // Blink Red off-and-on while over the temperature shutdown limit, zero current should flow to the EV + g_TempMonitor.SetBlinkAlarm(0); // toggle the alarm flag so we can blink + SetRedLed(1); #ifdef LCD16X2 //Adafruit RGB LCD - LcdSetBacklightColor(RED); + LcdSetBacklightColor(RED); #endif //Adafruit RGB LCD - } - else { - g_TempMonitor.SetBlinkAlarm(1); // toggle the alarm flag so we can blink - SetRedLed(0); + } + else { + g_TempMonitor.SetBlinkAlarm(1); // toggle the alarm flag so we can blink + SetRedLed(0); #ifdef LCD16X2 //Adafruit RGB LCD - LcdSetBacklightColor(TEAL); + LcdSetBacklightColor(TEAL); #endif - } - } // (g_TempMonitor.OverTemperature()) || TEMPERATURE_DISPLAY_ALWAYS) - else { - SetRedLed(0); // restore the normal TEAL backlight in case it was left RED while last blinking + } + } // (g_TempMonitor.OverTemperature()) || TEMPERATURE_DISPLAY_ALWAYS) + else { + SetRedLed(0); // restore the normal TEAL backlight in case it was left RED while last blinking #ifdef LCD16X2 //Adafruit RGB LCD - LcdSetBacklightColor(TEAL); + LcdSetBacklightColor(TEAL); #endif - } + } #else // !TEMPERATURE_MONITORING - #ifndef KWH_RECORDING +#ifndef KWH_RECORDING int h = hour(elapsedTime); // display the elapsed charge time int m = minute(elapsedTime); int s = second(elapsedTime); - #ifdef DELAYTIMER - sprintf(g_sTmp,"%02d:%02d:%02d %02d:%02d",h,m,s,(int)((curtime.hour() <= 23) ? curtime.hour() : 0),(int)((curtime.hour() <= 23) ? curtime.minute() : 0)); - #else +#ifdef DELAYTIMER + sprintf(g_sTmp,"%02d:%02d:%02d %02d:%02d",h,m,s,(int)((g_CurrTime.hour() <= 23) ? g_CurrTime.hour() : 0),(int)((g_CurrTime.hour() <= 23) ? g_CurrTime.minute() : 0)); +#else sprintf(g_sTmp,"%02d:%02d:%02d",h,m,s); - #endif //#ifdef DELAYTIMER +#endif //#ifdef DELAYTIMER LcdPrint(1,g_sTmp); - #endif // KWH_RECORDING +#endif // KWH_RECORDING #endif // TEMPERATURE_MONITORING - } - } + } + } -// Display a new stopped LCD screen with Delay Timers enabled - GoldServe + // Display a new stopped LCD screen with Delay Timers enabled - GoldServe #ifdef DELAYTIMER - else if (curstate == EVSE_STATE_SLEEPING) { - if (memcmp(&curtime,&g_CurrTime,sizeof(curtime))) { - LcdSetCursor(0,0); - g_DelayTimer.PrintTimerIcon(); - LcdPrint_P(g_DelayTimer.IsTimerEnabled() ? g_psWaiting : g_psSleeping); - // sprintf(g_sTmp,"%02d:%02d \0\1",curtime.hour(),curtime.minute()); - sprintf(g_sTmp,"%02d:%02d:%02d",curtime.hour(),curtime.minute(),curtime.second()); - LcdPrint(0,1,g_sTmp); - if (g_DelayTimer.IsTimerEnabled()){ - LcdSetCursor(9,0); - LcdWrite(0x2); - LcdWrite(0x0); - sprintf(g_sTmp,g_sHHMMfmt,g_DelayTimer.GetStartTimerHour(),g_DelayTimer.GetStartTimerMin()); - LcdPrint(11,0,g_sTmp); - LcdSetCursor(9,1); - LcdWrite(0x1); - LcdWrite(0x0); - sprintf(g_sTmp,g_sHHMMfmt,g_DelayTimer.GetStopTimerHour(),g_DelayTimer.GetStopTimerMin()); - LcdPrint(11,1,g_sTmp); - } else { - sprintf(g_sTmp,g_sRdyLAstr,(int)svclvl,(int)g_EvseController.GetCurrentCapacity()); - LcdPrint(10,0,g_sTmp); - } - } - } + else if (curstate == EVSE_STATE_SLEEPING) { + LcdSetCursor(0,0); + g_DelayTimer.PrintTimerIcon(); + // LcdPrint_P(g_DelayTimer.IsTimerEnabled() ? g_psWaiting : g_psSleeping); + LcdPrint_P(g_psSleeping); + sprintf(g_sTmp,"%02d:%02d:%02d",g_CurrTime.hour(),g_CurrTime.minute(),g_CurrTime.second()); + LcdPrint(0,1,g_sTmp); + if (g_DelayTimer.IsTimerEnabled()){ + LcdSetCursor(9,0); + LcdWrite(0x2); + LcdWrite(0x0); + sprintf(g_sTmp,g_sHHMMfmt,g_DelayTimer.GetStartTimerHour(),g_DelayTimer.GetStartTimerMin()); + LcdPrint(11,0,g_sTmp); + LcdSetCursor(9,1); + LcdWrite(0x1); + LcdWrite(0x0); + sprintf(g_sTmp,g_sHHMMfmt,g_DelayTimer.GetStopTimerHour(),g_DelayTimer.GetStopTimerMin()); + LcdPrint(11,1,g_sTmp); + } else { + sprintf(g_sTmp,g_sRdyLAstr,(int)svclvl,(int)g_EvseController.GetCurrentCapacity()); + LcdPrint(10,0,g_sTmp); + } + } #endif // DELAYTIMER #endif // LCD16X2 -#if defined(DELAYTIMER) && defined(LCD16X2) - g_CurrTime = curtime; -#endif //#ifdef DELAYTIMER + m_LastUpdateMs = curms; + } #ifdef FT_ENDURANCE LcdSetCursor(0,0); From 653ac131808853e1a11569dd9438e2c8f32d5b7a Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 11:45:24 -0700 Subject: [PATCH 17/96] DelayTimer::CheckTime() only when not in fault state --- open_evse.ino | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/open_evse.ino b/open_evse.ino index 898da1bb..8b6bc9d0 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -2077,13 +2077,15 @@ void DelayTimer::Init() { void DelayTimer::CheckTime() { - unsigned long curms = millis(); - if ((curms - m_LastCheck) > 1000ul) { - if (IsTimerEnabled() && IsTimerValid()){ + if (!g_EvseController.InFaultState() && + IsTimerEnabled() && + IsTimerValid()) { + unsigned long curms = millis(); + if ((curms - m_LastCheck) > 1000ul) { g_CurrTime = g_RTC.now(); m_CurrHour = g_CurrTime.hour(); m_CurrMin = g_CurrTime.minute(); - + uint16_t startTimerMinutes = m_StartTimerHour * 60 + m_StartTimerMin; uint16_t stopTimerMinutes = m_StopTimerHour * 60 + m_StopTimerMin; uint16_t currTimeMinutes = m_CurrHour * 60 + m_CurrMin; From 1ee42a9cc963b4dc100ce2e336b2c790fb918995 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 16:47:15 -0700 Subject: [PATCH 18/96] auto reset GFI/NoGND faults when EV disconnected, retry countdown --- CHANGELOG | 15 ++- Gfi.h | 5 +- J1772EvseController.cpp | 144 +++++++++++++++++++---------- J1772EvseController.h | 21 ++++- open_evse.h | 26 +----- open_evse.ino | 197 ++++++++++++++++++++++------------------ strings.cpp | 4 +- strings.h | 4 +- 8 files changed, 249 insertions(+), 167 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d099680e..8a4cdf32 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,8 +3,21 @@ Change Log vD3.9.0 SCL 20150602 - split out Gfi/J1772Pilot/J1772EvseController into separate files - add support for changing I2C frequency +- increase I2C bus from 100KHz -> 200KHz - use modified local Wire/twi to get rid of digitalWrite() -- on POST failure, go directly to Setup menu. Setup menu exits to reboot +- on POST failure, go directly to Setup menu. Setup menu exits to reboot on POST failure +- shrink MaxCurrent/ChargeLimit/TimeLimit menu memory/code by calculating allowed + values rather than using RAM lookup tables +- put strings in strings.cpp +- fix bugs from 3.8.4: 1) Setup menu Exit sometimes didn't work 2) Setup menu would + start on Exit item after first use +- GFCI and No Ground faults auto reset when EV is unplugged, even if hard fault +- GFCI and No Ground faults display auto reset countdown when applicable + GFCI FAULT + RETRY IN 04:55 +- when waiting for timer, just display Sleeping instead of Waiting +- during a fault, short press goes directly to Setup menu instead of putting + EVSE in Sleep state vD3.8.4 SCL 20150526 - display "Disabled" when EVSE_STATE_DISABLED instead of "Stopped" diff --git a/Gfi.h b/Gfi.h index 2d1ff875..922ca396 100644 --- a/Gfi.h +++ b/Gfi.h @@ -24,11 +24,14 @@ class Gfi { DigitalPin pin; uint8_t m_GfiFault; #ifdef GFI_SELFTEST - DigitalPin pinTest; uint8_t testSuccess; uint8_t testInProgress; #endif // GFI_SELFTEST public: +#ifdef GFI_SELFTEST + DigitalPin pinTest; +#endif + Gfi() {} void Init(); diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 665646a5..ed50be7a 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -239,8 +239,23 @@ void J1772EVSEController::chargingOff() void J1772EVSEController::HardFault() { + SetHardFault(); g_OBD.Update(OBD_UPD_HARDFAULT); - while (1) ProcessInputs(1); // spin forever or until user resets via menu + while (1) { + ProcessInputs(); // spin forever or until user resets via menu + // if we're in P12 state, we can recover from the hard fault when EV + // is unplugged + if (m_Pilot.GetState() == PILOT_STATE_P12) { + int plow,phigh; + ReadPilot(&plow,&phigh); + if (phigh >= m_ThreshData.m_ThreshAB) { + // EV disconnected - cancel fault + m_EvseState = EVSE_STATE_UNKNOWN; + break; + } + } + } + ClrHardFault(); } #ifdef GFI @@ -254,11 +269,11 @@ void J1772EVSEController::SetGfiTripped() #endif m_bVFlags |= ECVF_GFI_TRIPPED; - // this is repeated Update(), but we want to keep latency as low as possible + // this is repeated in Update(), but we want to keep latency as low as possible // for safety so we do it here first anyway chargingOff(); // turn off charging current - // turn off the pilot - m_Pilot.SetState(PILOT_STATE_N12); + // turn off the PWM + m_Pilot.SetState(PILOT_STATE_P12); m_Gfi.SetFault(); // the rest of the logic will be handled in Update() @@ -455,8 +470,8 @@ void J1772EVSEController::SetSvcLevel(uint8_t svclvl,uint8_t updatelcd) ampacity = (svclvl == 1) ? DEFAULT_CURRENT_CAPACITY_L1 : DEFAULT_CURRENT_CAPACITY_L2; } - if (ampacity < MIN_CURRENT_CAPACITY) { - ampacity = MIN_CURRENT_CAPACITY; + if (ampacity < MIN_CURRENT_CAPACITY_L1) { + ampacity = MIN_CURRENT_CAPACITY_L1; } else { if (svclvl == 1) { // L1 @@ -713,7 +728,7 @@ uint8_t J1772EVSEController::doPost() } #endif // ADVPWR -void J1772EVSEController::ProcessInputs(uint8_t nosleeptoggle) +void J1772EVSEController::ProcessInputs() { #ifdef RAPI g_ERP.doCmd(); @@ -722,7 +737,7 @@ void J1772EVSEController::ProcessInputs(uint8_t nosleeptoggle) g_CLI.getInput(); #endif // SERIALCLI #ifdef BTN_MENU - g_BtnHandler.ChkBtn(nosleeptoggle); + g_BtnHandler.ChkBtn(); #endif } @@ -872,13 +887,13 @@ void J1772EVSEController::Init() #ifdef UL_COMPLIANT // UL wants EVSE to hard fault until power cycle if POST fails while (1) { // spin forever -#endif - unsigned long faultms = millis(); - // wait for GFI_TIMEOUT before retrying POST - while ((millis() - faultms) < GFI_TIMEOUT) { - ProcessInputs(1); - } -#ifdef UL_COMPLIANT + ProcessInputs(); + } +#else // !UL_COMPLIANT + unsigned long faultms = millis(); + // keep retrying POST every 2 minutes + while ((millis() - faultms) < 2*60000ul) { + ProcessInputs(); } #endif } @@ -1000,6 +1015,14 @@ void J1772EVSEController::Update() } else { // !chargingIsOn() - relay open if (prevevsestate == EVSE_STATE_NO_GROUND) { + // check to see if EV disconnected + ReadPilot(&plow,&phigh); + if (phigh >= m_ThreshData.m_ThreshAB) { + // EV disconnected - cancel fault + m_EvseState = EVSE_STATE_UNKNOWN; + return; + } + if (((m_NoGndRetryCnt < GFI_RETRY_COUNT) || (GFI_RETRY_COUNT == 255)) && ((curms - m_NoGndStart) > GFI_TIMEOUT)) { m_NoGndRetryCnt++; @@ -1039,27 +1062,39 @@ void J1772EVSEController::Update() tmpevsestate = EVSE_STATE_GFCI_FAULT; m_EvseState = EVSE_STATE_GFCI_FAULT; - if (prevevsestate != EVSE_STATE_GFCI_FAULT) { + if (prevevsestate != EVSE_STATE_GFCI_FAULT) { // state transition if (m_GfiTripCnt < 254) { m_GfiTripCnt++; eeprom_write_byte((uint8_t*)EOFS_GFI_TRIP_CNT,m_GfiTripCnt); } m_GfiRetryCnt = 0; - m_GfiTimeout = curms + GFI_TIMEOUT; + m_GfiFaultStartMs = curms; } - else if (curms >= m_GfiTimeout) { + else { // was already in GFI fault + // check to see if EV disconnected + ReadPilot(&plow,&phigh); + if (phigh >= m_ThreshData.m_ThreshAB) { + // EV disconnected - cancel fault + m_EvseState = EVSE_STATE_UNKNOWN; + m_Gfi.Reset(); + return; + } + + if ((curms - m_GfiFaultStartMs) >= GFI_TIMEOUT) { #ifdef FT_GFI_RETRY - g_OBD.LcdMsg("Reset","GFI"); - delay(250); + g_OBD.LcdMsg("Reset","GFI"); + delay(250); #endif // FT_GFI_RETRY - m_GfiRetryCnt++; - - if ((GFI_RETRY_COUNT != 255) && (m_GfiRetryCnt > GFI_RETRY_COUNT)) { - HardFault(); - } - else { - m_Gfi.Reset(); - m_GfiTimeout = curms + GFI_TIMEOUT; + m_GfiRetryCnt++; + + if ((GFI_RETRY_COUNT != 255) && (m_GfiRetryCnt > GFI_RETRY_COUNT)) { + HardFault(); + return; + } + else { + m_Gfi.Reset(); + m_GfiFaultStartMs = 0; + } } } @@ -1079,10 +1114,8 @@ void J1772EVSEController::Update() #endif // TEMPERATURE_MONITORING if (nofault) { - if ((prevevsestate == EVSE_STATE_GFCI_FAULT) || - (prevevsestate == EVSE_STATE_OVER_TEMPERATURE) || - (prevevsestate == EVSE_STATE_NO_GROUND) || - (prevevsestate == EVSE_STATE_STUCK_RELAY)) { + if ((prevevsestate >= EVSE_FAULT_STATE_BEGIN) && + (prevevsestate <= EVSE_FAULT_STATE_END)) { // just got out of fault state - pilot back on m_Pilot.SetState(PILOT_STATE_P12); prevevsestate = EVSE_STATE_UNKNOWN; @@ -1164,10 +1197,11 @@ void J1772EVSEController::Update() ((curms - m_ChargeOnTimeMS) > 10000)) { g_OBD.LcdMsg("Induce","Fault"); for(int i = 0; i < GFI_TEST_CYCLES; i++) { - pinTest.write(1); + m_Gfi.pinTest.write(1); delayMicroseconds(GFI_PULSE_DURATION_US); - pinTest.write(0); + m_Gfi.pinTest.write(0); delayMicroseconds(GFI_PULSE_DURATION_US); + if (m_Gfi.Fault()) break; } } #endif // FT_GFI_RETRY @@ -1200,26 +1234,29 @@ void J1772EVSEController::Update() } else if (m_EvseState == EVSE_STATE_C) { m_Pilot.SetPWM(m_CurrentCapacity); -#ifdef FT_GFI_LOCKOUT - for(int i = 0; i < GFI_TEST_CYCLES; i++) { - pinTest.write(1); - delayMicroseconds(GFI_PULSE_DURATION_US); - pinTest.write(0); - delayMicroseconds(GFI_PULSE_DURATION_US); - } - g_OBD.LcdMsg("Closing","Relay"); - delay(150); -#endif // FT_GFI_LOCKOUT - #ifdef UL_GFI_SELFTEST // test GFI before closing relay if (GfiSelfTestEnabled() && m_Gfi.SelfTest()) { // GFI test failed - hard fault m_EvseState = EVSE_STATE_GFI_TEST_FAILED; + m_Pilot.SetState(PILOT_STATE_P12); HardFault(); + return; } #endif // UL_GFI_SELFTEST +#ifdef FT_GFI_LOCKOUT + for(int i = 0; i < GFI_TEST_CYCLES; i++) { + m_Gfi.pinTest.write(1); + delayMicroseconds(GFI_PULSE_DURATION_US); + m_Gfi.pinTest.write(0); + delayMicroseconds(GFI_PULSE_DURATION_US); + if (m_Gfi.Fault()) break; + } + g_OBD.LcdMsg("Closing","Relay"); + delay(150); +#endif // FT_GFI_LOCKOUT + chargingOn(); // turn on charging current #ifdef KWH_RECORDING if (prevevsestate == EVSE_STATE_A) { @@ -1235,7 +1272,7 @@ void J1772EVSEController::Update() else if (m_EvseState == EVSE_STATE_GFCI_FAULT) { // vehicle state F chargingOff(); // turn off charging current - m_Pilot.SetState(PILOT_STATE_N12); + m_Pilot.SetState(PILOT_STATE_P12); } #ifdef TEMPERATURE_MONITORING else if (m_EvseState == EVSE_STATE_OVER_TEMPERATURE) { @@ -1260,7 +1297,7 @@ void J1772EVSEController::Update() else if (m_EvseState == EVSE_STATE_NO_GROUND) { // Ground not detected chargingOff(); // turn off charging current - m_Pilot.SetState(PILOT_STATE_N12); + m_Pilot.SetState(PILOT_STATE_P12); } else if (m_EvseState == EVSE_STATE_STUCK_RELAY) { // Stuck relay detected @@ -1300,6 +1337,7 @@ void J1772EVSEController::Update() // if fault happens immediately (within 2 sec) after charging starts, hard fault if ((curms - m_ChargeOnTimeMS) <= 2000) { HardFault(); + return; } } #endif // UL_COMPLIANT @@ -1437,11 +1475,11 @@ int J1772EVSEController::SetCurrentCapacity(uint8_t amps,uint8_t updatelcd,uint8 int rc = 0; uint8_t maxcurrentcap = (GetCurSvcLevel() == 1) ? MAX_CURRENT_CAPACITY_L1 : MAX_CURRENT_CAPACITY_L2; - if ((amps >= MIN_CURRENT_CAPACITY) && (amps <= maxcurrentcap)) { + if ((amps >= MIN_CURRENT_CAPACITY_L1) && (amps <= maxcurrentcap)) { m_CurrentCapacity = amps; } - else if (amps < MIN_CURRENT_CAPACITY) { - m_CurrentCapacity = MIN_CURRENT_CAPACITY; + else if (amps < MIN_CURRENT_CAPACITY_L1) { + m_CurrentCapacity = MIN_CURRENT_CAPACITY_L1; rc = 1; } else { @@ -1464,6 +1502,12 @@ int J1772EVSEController::SetCurrentCapacity(uint8_t amps,uint8_t updatelcd,uint8 return rc; } +unsigned long J1772EVSEController::GetResetMs() +{ + return GFI_TIMEOUT - (millis() - ((m_EvseState == EVSE_STATE_GFCI_FAULT) ? m_GfiFaultStartMs : m_NoGndStart)); +} + + #ifdef VOLTMETER void J1772EVSEController::SetVoltmeter(uint16_t scale,uint32_t offset) { diff --git a/J1772EvseController.h b/J1772EvseController.h index 123348ef..7ace3f50 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -25,12 +25,15 @@ #define EVSE_STATE_B 0x02 // vehicle state B 9V - connected, ready #define EVSE_STATE_C 0x03 // vehicle state C 6V - charging #define EVSE_STATE_D 0x04 // vehicle state D 3V - vent required +#define EVSE_FAULT_STATE_BEGIN EVSE_STATE_D #define EVSE_STATE_DIODE_CHK_FAILED 0x05 // diode check failed #define EVSE_STATE_GFCI_FAULT 0x06 // GFCI fault #define EVSE_STATE_NO_GROUND 0x07 //bad ground #define EVSE_STATE_STUCK_RELAY 0x08 //stuck relay #define EVSE_STATE_GFI_TEST_FAILED 0x09 // GFI self-test failure -#define EVSE_STATE_OVER_TEMPERATURE 0x0A // over temperature error shutdown +#define EVSE_STATE_OVER_TEMPERATURE 0x0A // over temperature error shutdown +#define EVSE_FAULT_STATE_END EVSE_STATE_OVER_TEMPERATURE + #define EVSE_STATE_SLEEPING 0xfe // waiting for timer #define EVSE_STATE_DISABLED 0xff // disabled @@ -69,6 +72,7 @@ typedef struct calibdata { // J1772EVSEController volatile m_bVFlags bits - not saved to EEPROM #define ECVF_AUTOSVCLVL_SKIPPED 0x01 // auto svc level test skipped during post +#define ECVF_HARD_FAULT 0x02 // in non-autoresettable fault #define ECVF_AMMETER_CAL 0x10 // ammeter calibration mode #define ECVF_NOGND_TRIPPED 0x20 // no ground has tripped at least once #define ECVF_CHARGING_ON 0x40 // charging relay is closed @@ -79,7 +83,7 @@ class J1772EVSEController { J1772Pilot m_Pilot; #ifdef GFI Gfi m_Gfi; - unsigned long m_GfiTimeout; + unsigned long m_GfiFaultStartMs; unsigned long m_GfiRetryCnt; uint8_t m_GfiTripCnt; #endif // GFI @@ -199,6 +203,15 @@ class J1772EVSEController { eeprom_write_word((uint16_t *)EOFS_FLAGS,m_wFlags); } + int8_t InFaultState() { + return ((m_EvseState >= EVSE_FAULT_STATE_BEGIN) && (m_EvseState <= EVSE_FAULT_STATE_END)); + } + + void SetHardFault() { m_bVFlags |= ECVF_HARD_FAULT; } + void ClrHardFault() { m_bVFlags &= ~ECVF_HARD_FAULT; } + int8_t InHardFault() { return (m_bVFlags & ECVF_HARD_FAULT) ? 1 : 0; } + unsigned long GetResetMs(); + uint8_t GetCurrentCapacity() { return m_CurrentCapacity; } @@ -319,14 +332,14 @@ class J1772EVSEController { uint8_t GetChargeLimit() { return m_chargeLimit; } #endif // CHARGE_LIMIT #ifdef TIME_LIMIT - uint8_t SetTimeLimit(uint8_t minutes) { m_timeLimit = minutes; } + uint8_t SetTimeLimit(uint8_t mind15) { m_timeLimit = mind15; } uint8_t GetTimeLimit() { return m_timeLimit; } #endif // TIME_LIMIT #else // !AMMETER int32_t GetChargingCurrent() { return -1; } #endif void ReadPilot(int *plow,int *phigh,int loopcnt=PILOT_LOOP_CNT); - void ProcessInputs(uint8_t nosleeptoggle); + void ProcessInputs(); void Reboot(); #ifdef SHOW_DISABLED_TESTS void ShowDisabledTests(); diff --git a/open_evse.h b/open_evse.h index 4d7f5ab6..f7b7ca57 100644 --- a/open_evse.h +++ b/open_evse.h @@ -627,6 +627,7 @@ class OnboardDisplay #endif // defined(RGBLCD) || defined(I2CLCD) uint8_t m_bFlags; char m_strBuf[LCD_MAX_CHARS_PER_LINE+1]; + unsigned long m_LastUpdateMs; int8_t updateDisabled() { return m_bFlags & OBDF_UPDATE_DISABLED; } @@ -731,28 +732,7 @@ class OnboardDisplay }; #ifdef GFI -class Gfi { - DigitalPin pin; - uint8_t m_GfiFault; -#ifdef GFI_SELFTEST - DigitalPin pinTest; - uint8_t testSuccess; - uint8_t testInProgress; -#endif // GFI_SELFTEST -public: - Gfi() {} - - void Init(); - void Reset(); - void SetFault() { m_GfiFault = 1; } - uint8_t Fault() { return m_GfiFault; } -#ifdef GFI_SELFTEST - uint8_t SelfTest(); - void SetTestSuccess() { testSuccess = 1; } - uint8_t SelfTestSuccess() { return testSuccess; } - uint8_t SelfTestInProgress() { return testInProgress; } -#endif -}; +#include "Gfi.h" #endif // GFI #ifdef TEMPERATURE_MONITORING @@ -1074,7 +1054,7 @@ class BtnHandler { public: BtnHandler(); void init() { m_Btn.init(); } - void ChkBtn(int8_t nosleeptoggle); + void ChkBtn(); uint8_t InMenu() { return (m_CurMenu == NULL) ? 0 : 1; } uint8_t GetSavedLcdMode() { return m_SavedLcdMode; } void SetSavedLcdMode(uint8_t mode ) { m_SavedLcdMode = mode; } diff --git a/open_evse.ino b/open_evse.ino index 8b6bc9d0..8738c19d 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -449,10 +449,13 @@ void OnboardDisplay::Update(int8_t updmode) int i; uint8_t curstate = g_EvseController.GetState(); uint8_t svclvl = g_EvseController.GetCurSvcLevel(); + int currentcap = g_EvseController.GetCurrentCapacity(); unsigned long curms = millis(); if (g_EvseController.StateTransition() || (updmode != OBD_UPD_NORMAL)) { - sprintf(g_sTmp,g_sRdyLAstr,(int)svclvl,(int)g_EvseController.GetCurrentCapacity()); + curms += 1000; // trigger periodic update code below + + sprintf(g_sTmp,g_sRdyLAstr,(int)svclvl,currentcap); switch(curstate) { case EVSE_STATE_A: // not connected SetGreenLed(1); @@ -468,14 +471,14 @@ void OnboardDisplay::Update(int8_t updmode) LcdPrint_P(g_psReady); LcdPrint(10,0,g_sTmp); - #ifdef KWH_RECORDING +#ifdef KWH_RECORDING sprintf(g_sTmp,"%5luWh",(g_WattSeconds / 3600) ); LcdPrint(0,1,g_sTmp); - + sprintf(g_sTmp,"%6lukWh",(g_WattHours_accumulated / 1000)); // display accumulated kWh LcdPrint(7,1,g_sTmp); - #endif // KWH_RECORDING - +#endif // KWH_RECORDING + #endif //Adafruit RGB LCD // n.b. blue LED is off break; @@ -502,17 +505,17 @@ void OnboardDisplay::Update(int8_t updmode) #endif //#ifdef DELAYTIMER LcdPrint_P(g_psEvConnected); LcdPrint(10,0,g_sTmp); - - #ifdef KWH_RECORDING + +#ifdef KWH_RECORDING sprintf(g_sTmp,"%5luWh",(g_WattSeconds / 3600) ); LcdPrint(0,1,g_sTmp); - + sprintf(g_sTmp,"%6lukWh",(g_WattHours_accumulated / 1000)); // Display accumulated kWh LcdPrint(7,1,g_sTmp); - #endif // KWH_RECORDING - +#endif // KWH_RECORDING + #endif //Adafruit RGB LCD - // n.b. blue LED is off + // n.b. blue LED is off break; case EVSE_STATE_C: // charging SetGreenLed(0); @@ -562,7 +565,13 @@ void OnboardDisplay::Update(int8_t updmode) SetRedLed(1); #ifdef LCD16X2 //Adafruit RGB LCD LcdSetBacklightColor(RED); - LcdMsg_P(updmode == OBD_UPD_HARDFAULT ? g_psSvcReq : g_psEvseError,g_psGfciFault); + if (updmode == OBD_UPD_HARDFAULT) { + LcdMsg_P(g_psEvseError,g_psGfciFault); + } + else { + // 2nd line will be updated below with auto retry count + LcdPrint_P(0,g_psGfciFault); + } #endif //Adafruit RGB LCD // n.b. blue LED is off break; @@ -581,7 +590,13 @@ void OnboardDisplay::Update(int8_t updmode) SetRedLed(1); #ifdef LCD16X2 //Adafruit RGB LCD LcdSetBacklightColor(RED); - LcdMsg_P(updmode == OBD_UPD_HARDFAULT ? g_psSvcReq : g_psEvseError,g_psNoGround); + if (updmode == OBD_UPD_HARDFAULT) { + LcdMsg_P(g_psEvseError,g_psNoGround); + } + else { + // 2nd line will be updated below with auto retry count + LcdPrint_P(0,g_psNoGround); + } #endif //Adafruit RGB LCD // n.b. blue LED is off break; @@ -634,6 +649,20 @@ void OnboardDisplay::Update(int8_t updmode) // the code below will only run once per second // if ((curms-m_LastUpdateMs) >= 1000) { + m_LastUpdateMs = curms; + + if (!g_EvseController.InHardFault() && + ((curstate == EVSE_STATE_GFCI_FAULT) || (curstate == EVSE_STATE_NO_GROUND))) { + strcpy(g_sTmp,g_sRetryIn); + int resetsec = (int)(g_EvseController.GetResetMs() / 1000ul); + if (resetsec >= 0) { + sprintf(g_sTmp+sizeof(g_sTmp)-6,g_sHHMMfmt,resetsec / 60,resetsec % 60); + strcat(g_sTmp,g_sTmp+sizeof(g_sTmp)-6); + LcdPrint(1,g_sTmp); + } + return; + } + #ifdef RTC g_CurrTime = g_RTC.now(); #endif @@ -664,95 +693,93 @@ void OnboardDisplay::Update(int8_t updmode) #ifdef LCD16X2 if (curstate == EVSE_STATE_C) { time_t elapsedTime = g_EvseController.GetElapsedChargeTime(); - if (elapsedTime != g_EvseController.GetElapsedChargeTimePrev()) { #ifdef KWH_RECORDING - uint32_t current = g_EvseController.GetChargingCurrent(); + uint32_t current = g_EvseController.GetChargingCurrent(); #ifdef VOLTMETER - g_WattSeconds = g_WattSeconds + (g_EvseController.GetVoltage() / 1000 * current / 1000); + g_WattSeconds = g_WattSeconds + (g_EvseController.GetVoltage() / 1000 * current / 1000); #else - if (g_EvseController.GetCurSvcLevel() == 2) { // first verify L2 or L1 - g_WattSeconds = g_WattSeconds + (VOLTS_FOR_L2 * current / 1000); // accumulate Watt Seconds for Level2 charging - } - else { - g_WattSeconds = g_WattSeconds + (VOLTS_FOR_L1 * current / 1000); // accumulate Watt Seconds for Level1 charging - } + if (g_EvseController.GetCurSvcLevel() == 2) { // first verify L2 or L1 + g_WattSeconds = g_WattSeconds + (VOLTS_FOR_L2 * current / 1000); // accumulate Watt Seconds for Level2 charging + } + else { + g_WattSeconds = g_WattSeconds + (VOLTS_FOR_L1 * current / 1000); // accumulate Watt Seconds for Level1 charging + } #endif // VOLTMETER - sprintf(g_sTmp,"%5luWh",(g_WattSeconds / 3600) ); - LcdPrint(0,1,g_sTmp); + sprintf(g_sTmp,"%5luWh",(g_WattSeconds / 3600) ); + LcdPrint(0,1,g_sTmp); #ifdef VOLTMETER - sprintf(g_sTmp," %3luV",(g_EvseController.GetVoltage() / 1000)); // Display voltage from OpenEVSE II - LcdPrint(11,1,g_sTmp); + sprintf(g_sTmp," %3luV",(g_EvseController.GetVoltage() / 1000)); // Display voltage from OpenEVSE II + LcdPrint(11,1,g_sTmp); #else - sprintf(g_sTmp,"%6lukWh",(g_WattHours_accumulated / 1000)); // display accumulated kWh - LcdPrint(7,1,g_sTmp); + sprintf(g_sTmp,"%6lukWh",(g_WattHours_accumulated / 1000)); // display accumulated kWh + LcdPrint(7,1,g_sTmp); #endif // VOLTMETER #else // ! KWH_RECORDING - LcdClearLine(0); + LcdClearLine(0); #endif // KWH_RECORDING #ifdef TEMPERATURE_MONITORING - if ((g_TempMonitor.OverTemperature()) || TEMPERATURE_DISPLAY_ALWAYS) { - g_OBD.LcdClearLine(1); - const char *tempfmt = "%2d.%1dC"; + if ((g_TempMonitor.OverTemperature()) || TEMPERATURE_DISPLAY_ALWAYS) { + g_OBD.LcdClearLine(1); + const char *tempfmt = "%2d.%1dC"; #ifdef MCP9808_IS_ON_I2C - if ( g_TempMonitor.m_MCP9808_temperature != 0 ) { // it returns 0 if it is not present - sprintf(g_sTmp,tempfmt,g_TempMonitor.m_MCP9808_temperature/10, g_TempMonitor.m_MCP9808_temperature % 10); // Ambient sensor near or on the LCD - LcdPrint(0,1,g_sTmp); - } + if ( g_TempMonitor.m_MCP9808_temperature != 0 ) { // it returns 0 if it is not present + sprintf(g_sTmp,tempfmt,g_TempMonitor.m_MCP9808_temperature/10, g_TempMonitor.m_MCP9808_temperature % 10); // Ambient sensor near or on the LCD + LcdPrint(0,1,g_sTmp); + } #endif #ifdef RTC - if ( g_TempMonitor.m_DS3231_temperature != 0) { // it returns 0 if it is not present - sprintf(g_sTmp,tempfmt,g_TempMonitor.m_DS3231_temperature/10, g_TempMonitor.m_DS3231_temperature % 10); // sensor built into the DS3231 RTC Chip - LcdPrint(5,1,g_sTmp); - } + if ( g_TempMonitor.m_DS3231_temperature != 0) { // it returns 0 if it is not present + sprintf(g_sTmp,tempfmt,g_TempMonitor.m_DS3231_temperature/10, g_TempMonitor.m_DS3231_temperature % 10); // sensor built into the DS3231 RTC Chip + LcdPrint(5,1,g_sTmp); + } #endif #ifdef TMP007_IS_ON_I2C - if ( g_TempMonitor.m_TMP007_temperature != 0 ) { // it returns 0 if it is not present - sprintf(g_sTmp,tempfmt,g_TempMonitor.m_TMP007_temperature/10, g_TempMonitor.m_TMP007_temperature % 10); // Infrared sensor probably looking at 30A fuses - LcdPrint(11,1,g_sTmp); - } + if ( g_TempMonitor.m_TMP007_temperature != 0 ) { // it returns 0 if it is not present + sprintf(g_sTmp,tempfmt,g_TempMonitor.m_TMP007_temperature/10, g_TempMonitor.m_TMP007_temperature % 10); // Infrared sensor probably looking at 30A fuses + LcdPrint(11,1,g_sTmp); + } #endif - if (g_TempMonitor.BlinkAlarm() && g_TempMonitor.OverTemperatureShutdown()) { // Blink Red off-and-on while over the temperature shutdown limit, zero current should flow to the EV - g_TempMonitor.SetBlinkAlarm(0); // toggle the alarm flag so we can blink - SetRedLed(1); + if (g_TempMonitor.BlinkAlarm() && g_TempMonitor.OverTemperatureShutdown()) { // Blink Red off-and-on while over the temperature shutdown limit, zero current should flow to the EV + g_TempMonitor.SetBlinkAlarm(0); // toggle the alarm flag so we can blink + SetRedLed(1); #ifdef LCD16X2 //Adafruit RGB LCD - LcdSetBacklightColor(RED); + LcdSetBacklightColor(RED); #endif //Adafruit RGB LCD - } - else { - g_TempMonitor.SetBlinkAlarm(1); // toggle the alarm flag so we can blink - SetRedLed(0); -#ifdef LCD16X2 //Adafruit RGB LCD - LcdSetBacklightColor(TEAL); -#endif - } - } // (g_TempMonitor.OverTemperature()) || TEMPERATURE_DISPLAY_ALWAYS) + } else { - SetRedLed(0); // restore the normal TEAL backlight in case it was left RED while last blinking + g_TempMonitor.SetBlinkAlarm(1); // toggle the alarm flag so we can blink + SetRedLed(0); #ifdef LCD16X2 //Adafruit RGB LCD LcdSetBacklightColor(TEAL); #endif - } + } + } // (g_TempMonitor.OverTemperature()) || TEMPERATURE_DISPLAY_ALWAYS) + else { + SetRedLed(0); // restore the normal TEAL backlight in case it was left RED while last blinking +#ifdef LCD16X2 //Adafruit RGB LCD + LcdSetBacklightColor(TEAL); +#endif + } #else // !TEMPERATURE_MONITORING #ifndef KWH_RECORDING - int h = hour(elapsedTime); // display the elapsed charge time - int m = minute(elapsedTime); - int s = second(elapsedTime); + int h = hour(elapsedTime); // display the elapsed charge time + int m = minute(elapsedTime); + int s = second(elapsedTime); #ifdef DELAYTIMER - sprintf(g_sTmp,"%02d:%02d:%02d %02d:%02d",h,m,s,(int)((g_CurrTime.hour() <= 23) ? g_CurrTime.hour() : 0),(int)((g_CurrTime.hour() <= 23) ? g_CurrTime.minute() : 0)); + sprintf(g_sTmp,"%02d:%02d:%02d %02d:%02d",h,m,s,(int)((g_CurrTime.hour() <= 23) ? g_CurrTime.hour() : 0),(int)((g_CurrTime.hour() <= 23) ? g_CurrTime.minute() : 0)); #else - sprintf(g_sTmp,"%02d:%02d:%02d",h,m,s); + sprintf(g_sTmp,"%02d:%02d:%02d",h,m,s); #endif //#ifdef DELAYTIMER - LcdPrint(1,g_sTmp); + LcdPrint(1,g_sTmp); #endif // KWH_RECORDING #endif // TEMPERATURE_MONITORING - } } // Display a new stopped LCD screen with Delay Timers enabled - GoldServe @@ -776,14 +803,12 @@ void OnboardDisplay::Update(int8_t updmode) sprintf(g_sTmp,g_sHHMMfmt,g_DelayTimer.GetStopTimerHour(),g_DelayTimer.GetStopTimerMin()); LcdPrint(11,1,g_sTmp); } else { - sprintf(g_sTmp,g_sRdyLAstr,(int)svclvl,(int)g_EvseController.GetCurrentCapacity()); + sprintf(g_sTmp,g_sRdyLAstr,(int)svclvl,currentcap); LcdPrint(10,0,g_sTmp); } } #endif // DELAYTIMER #endif // LCD16X2 - - m_LastUpdateMs = curms; } #ifdef FT_ENDURANCE @@ -1951,17 +1976,21 @@ BtnHandler::BtnHandler() m_CurMenu = NULL; } -void BtnHandler::ChkBtn(int8_t nosleeptoggle) +void BtnHandler::ChkBtn() { WDT_RESET(); + int8_t infaultstate = g_EvseController.InFaultState(); + m_Btn.read(); if (m_Btn.shortPress()) { if (m_CurMenu) { m_CurMenu->Next(); } else { - if (!nosleeptoggle) { + // force into setup menu when in fault + if (infaultstate) goto longpress; + else { if ((g_EvseController.GetState() == EVSE_STATE_DISABLED) || (g_EvseController.GetState() == EVSE_STATE_SLEEPING)) { g_EvseController.Enable(); @@ -1973,6 +2002,7 @@ void BtnHandler::ChkBtn(int8_t nosleeptoggle) } } else if (m_Btn.longPress()) { + longpress: if (m_CurMenu) { m_CurMenu = m_CurMenu->Select(); if (m_CurMenu) { @@ -1991,7 +2021,7 @@ void BtnHandler::ChkBtn(int8_t nosleeptoggle) } else { // exit - if (nosleeptoggle) { + if (infaultstate) { g_EvseController.Reboot(); } else { @@ -2009,21 +2039,20 @@ void BtnHandler::ChkBtn(int8_t nosleeptoggle) } } else { - if (nosleeptoggle) { - g_SetupMenu.Init(); +#if defined(CHARGE_LIMIT) || defined(TIME_LIMIT) + g_SettingsMenu.CheckSkipLimits(); +#endif // CHARGE_LIMIT + m_SavedLcdMode = g_OBD.IsLcdBacklightMono() ? BKL_TYPE_MONO : BKL_TYPE_RGB; + g_OBD.LcdSetBacklightColor(WHITE); + if (infaultstate) { m_CurMenu = &g_SetupMenu; } else { -#if defined(CHARGE_LIMIT) || defined(TIME_LIMIT) - g_SettingsMenu.CheckSkipLimits(); -#endif // CHARGE_LIMIT - g_EvseController.Sleep(); g_OBD.DisableUpdate(1); - m_SavedLcdMode = g_OBD.IsLcdBacklightMono() ? BKL_TYPE_MONO : BKL_TYPE_RGB; - g_OBD.LcdSetBacklightColor(WHITE); - g_SettingsMenu.Init(); + g_EvseController.Sleep(); m_CurMenu = &g_SettingsMenu; } + m_CurMenu->Init(); } } } @@ -2142,12 +2171,8 @@ void DelayTimer::Disable(){ g_OBD.Update(OBD_UPD_FORCE); } void DelayTimer::PrintTimerIcon(){ - //g_OBD.LcdClear(); - //g_OBD.LcdSetCursor(0,0); if (IsTimerEnabled() && IsTimerValid()){ -#ifdef BTN_MENU g_OBD.LcdWrite(0x0); -#endif //#ifdef BTN_MENU } } // End Delay Timer Functions - GoldServe @@ -2211,7 +2236,7 @@ void loop() g_OBD.Update(); - g_EvseController.ProcessInputs(0); + g_EvseController.ProcessInputs(); // Delay Timer Handler - GoldServe #ifdef DELAYTIMER diff --git a/strings.cpp b/strings.cpp index 66d45ecf..fb5069c4 100644 --- a/strings.cpp +++ b/strings.cpp @@ -91,13 +91,15 @@ const char g_psVentReq[] PROGMEM = "VENT REQUIRED"; const char g_psDiodeChkFailed[] PROGMEM = "DIODE CHECK"; const char g_psGfciFault[] PROGMEM = "GFCI FAULT"; const char g_psGfci[] PROGMEM = "GFCI"; +const char g_sRetryIn[] = "RETRY IN "; + #ifdef TEMPERATURE_MONITORING const char g_psTemperatureFault[] PROGMEM = "OVER TEMPERATURE"; #endif const char g_psNoGround[] PROGMEM = "NO GROUND"; const char g_psStuckRelay[] PROGMEM = "STUCK RELAY"; const char g_psDisabled[] PROGMEM = "Disabled"; -const char g_psWaiting[] PROGMEM = "Waiting"; +//const char g_psWaiting[] PROGMEM = "Waiting"; const char g_psSleeping[] PROGMEM = "Sleeping"; const char g_psEvConnected[] PROGMEM = "Connected"; #ifdef SHOW_DISABLED_TESTS diff --git a/strings.h b/strings.h index e20651ac..33fc94b0 100644 --- a/strings.h +++ b/strings.h @@ -93,13 +93,15 @@ extern const char g_psVentReq[] PROGMEM; extern const char g_psDiodeChkFailed[] PROGMEM; extern const char g_psGfciFault[] PROGMEM; extern const char g_psGfci[] PROGMEM; +extern const char g_sRetryIn[]; + #ifdef TEMPERATURE_MONITORING extern const char g_psTemperatureFault[] PROGMEM; #endif extern const char g_psNoGround[] PROGMEM; extern const char g_psStuckRelay[] PROGMEM; extern const char g_psDisabled[] PROGMEM; -extern const char g_psWaiting[] PROGMEM; +//extern const char g_psWaiting[] PROGMEM; extern const char g_psSleeping[] PROGMEM; extern const char g_psEvConnected[] PROGMEM; #ifdef SHOW_DISABLED_TESTS From ea1da45baf4848aec765050e6bd365f19b70b5c7 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 17:00:49 -0700 Subject: [PATCH 19/96] update date --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8a4cdf32..296b5cb6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,6 @@ Change Log -vD3.9.0 SCL 20150602 +vD3.9.0 SCL 20150604 - split out Gfi/J1772Pilot/J1772EvseController into separate files - add support for changing I2C frequency - increase I2C bus from 100KHz -> 200KHz From 75ebc79fab5cb511577614bd6e218ff89909b43a Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 17:14:42 -0700 Subject: [PATCH 20/96] send WIFI_MODE_AP_DEFAULT on very long press --- CHANGELOG | 2 ++ open_evse.ino | 2 +- rapi_proc.h | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 296b5cb6..0d03e019 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,8 @@ vD3.9.0 SCL 20150604 - when waiting for timer, just display Sleeping instead of Waiting - during a fault, short press goes directly to Setup menu instead of putting EVSE in Sleep state +- send $WF WIFI_MODE_AP_DEFAULT instead of WIFI_MODE_AP to client on very long + button press vD3.8.4 SCL 20150526 - display "Disabled" when EVSE_STATE_DISABLED instead of "Stopped" diff --git a/open_evse.ino b/open_evse.ino index 8738c19d..61c9d5a0 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -875,7 +875,7 @@ void Btn::read() else if (sample && vlongDebounceTime && (buttonState == BTN_STATE_LONG)) { if ((millis() - vlongDebounceTime) >= BTN_PRESS_VERYLONG) { vlongDebounceTime = 0; - g_ERP.setWifiMode(WIFI_MODE_AP); + g_ERP.setWifiMode(WIFI_MODE_AP_DEFAULT); } } #endif // RAPI diff --git a/rapi_proc.h b/rapi_proc.h index 7553d89f..11c8d2e3 100644 --- a/rapi_proc.h +++ b/rapi_proc.h @@ -51,6 +51,9 @@ response format asynchronous messages $ST state\r - EVSE state transition - sent whenever EVSE state changes state: EVSE_STATE_xxx +$WF mode\r - Request client WiFi mode + mode: WIFI_MODE_XXX + (currently very long press (10 sec) of menu btn on OpenEVSE will send WIFI_MODE_AP_DEFAULT commands From fc1bcf7c5a83206b3c1426a3885c365766585e57 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 17:57:57 -0700 Subject: [PATCH 21/96] correction --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 41020e77..2a448504 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -This is the stable branch +This is the development branch - use for testing only Firmware for Open EVSE: Open Source Hardware J1772 Electric Vehicle Supply Equipment From 2f5e3112d915ed9e06293af0515911c53c969846 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 18:00:39 -0700 Subject: [PATCH 22/96] delete superfluous --- open_evse.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/open_evse.h b/open_evse.h index f7b7ca57..dad15944 100644 --- a/open_evse.h +++ b/open_evse.h @@ -269,8 +269,6 @@ //-- begin configuration -#define I2C_FREQ 100000UL // Hz - // WARNING: ALL DELAYS *MUST* BE SHORTER THAN THIS TIMER OR WE WILL GET INTO // AN INFINITE RESET LOOP #define WATCHDOG_TIMEOUT WDTO_2S From c16ecd1d75652b77bbc1eba2d5aec8ad6da25d80 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 21:12:52 -0700 Subject: [PATCH 23/96] allow 79A, L1 in increments of 1 instead of 2 --- open_evse.ino | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/open_evse.ino b/open_evse.ino index 61c9d5a0..06f157f7 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -1184,7 +1184,13 @@ void MaxCurrentMenu::Init() void MaxCurrentMenu::Next() { - m_CurIdx += 2; + if ((g_EvseController.GetCurSvlLevel() == 1) || + (m_CurIdx == 78)) { // n.b. some cars can't do 80A, so allow 79A + m_CurIdx ++; + } + else { + else m_CurIdx += 2; + } if (m_CurIdx > m_MaxCurrent) { m_CurIdx = m_MinCurrent; } From 7ed7c5dae38977ba8b9da6f0dcbd5309fcba89a4 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 21:23:14 -0700 Subject: [PATCH 24/96] fix typo --- open_evse.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/open_evse.ino b/open_evse.ino index 06f157f7..15b62249 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -1184,7 +1184,7 @@ void MaxCurrentMenu::Init() void MaxCurrentMenu::Next() { - if ((g_EvseController.GetCurSvlLevel() == 1) || + if ((g_EvseController.GetCurSvcLevel() == 1) || (m_CurIdx == 78)) { // n.b. some cars can't do 80A, so allow 79A m_CurIdx ++; } From c5a216a487b444171e9dd6c6cc446d6e316caa8b Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 23:02:30 -0700 Subject: [PATCH 25/96] rapi fixes --- J1772EvseController.h | 5 +++++ rapi_proc.cpp | 6 +++--- rapi_proc.h | 7 +++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/J1772EvseController.h b/J1772EvseController.h index 7ace3f50..0621128d 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -273,6 +273,7 @@ class J1772EVSEController { #ifdef GFI void SetGfiTripped(); uint8_t GfiTripped() { return m_bVFlags & ECVF_GFI_TRIPPED; } + uint8_t GetGfiTripCnt() { return m_GfiTripCnt; } #ifdef GFI_SELFTEST uint8_t GfiSelfTestEnabled() { return (m_wFlags & ECF_GFI_TEST_DISABLED) ? 0 : 1; @@ -344,6 +345,10 @@ class J1772EVSEController { #ifdef SHOW_DISABLED_TESTS void ShowDisabledTests(); #endif +#ifdef ADVPWR + uint8_t GetNoGndTripCnt() { return m_NoGndTripCnt; } + uint8_t GetStuckRelayTripCnt() { return m_StuckRelayTripCnt; } +#endif // ADVPWR }; extern J1772EVSEController g_EvseController; diff --git a/rapi_proc.cpp b/rapi_proc.cpp index e1187d95..7b34d0d5 100644 --- a/rapi_proc.cpp +++ b/rapi_proc.cpp @@ -383,9 +383,9 @@ int EvseRapiProcessor::processCmd() bufCnt = 1; // flag response text output break; case 'F': // get fault counters - u1.u = eeprom_read_byte((uint8_t*)EOFS_GFI_TRIP_CNT); - u2.u = eeprom_read_byte((uint8_t*)EOFS_NOGND_TRIP_CNT); - u3.u = eeprom_read_byte((uint8_t*)EOFS_STUCK_RELAY_TRIP_CNT); + u1.u = g_EvseController.GetGfiTripCnt(); + u2.u = g_EvseController.GetNoGndTripCnt(); + u3.u = g_EvseController.GetStuckRelayTripCnt(); sprintf(buffer,"%x %x %x",u1.u,u2.u,u3.u); bufCnt = 1; // flag response text output break; diff --git a/rapi_proc.h b/rapi_proc.h index 11c8d2e3..9720db9d 100644 --- a/rapi_proc.h +++ b/rapi_proc.h @@ -89,6 +89,8 @@ S2 0|1 - disable/enable ammeter calibration mode - ammeter is read even when not S3 cnt - set charge time limit to cnt*15 minutes (0=disable, max=255) SA currentscalefactor currentoffset - set ammeter settings SC amps - set current capacity + if amps < minimum current capacity, will set to minimum and return $NK + if amps > maximum current capacity, will set to maximum and return $NK SD 0|1 - disable/enable diode check $SD 0*0B $SD 1*0C @@ -139,11 +141,12 @@ GE - get settings $GE*B0 GF - get fault counters response: OK gfitripcnt nogndtripcnt stuckrelaytripcnt (all values hex) + maximum trip count = 0xFE for any counter $GF*B1 GG - get charging current and voltage response: OK milliamps millivolts - AMMETER must be defined in order to get amps, otherwise returns 0 amps - VOLTMETER must be defined in order to get voltage, otherwise returns 0 volts + AMMETER must be defined in order to get amps, otherwise returns -1 amps + VOLTMETER must be defined in order to get voltage, otherwise returns -1 volts $GG*B2 GH - get cHarge limit response: OK kWh From 9cfcd44fec0d9348e6ed62e20e7b113460782db3 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 23:02:58 -0700 Subject: [PATCH 26/96] minor tweaks --- CHANGELOG | 3 +++ J1772EvseController.cpp | 6 +++--- open_evse.ino | 7 ++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0d03e019..976c1725 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,9 @@ vD3.9.0 SCL 20150604 EVSE in Sleep state - send $WF WIFI_MODE_AP_DEFAULT instead of WIFI_MODE_AP to client on very long button press +- fix RAPI bug: $GF was returning FF as trip counts if zero. New max value for + any trip count = FE +- fix documentation for $SC & $GG vD3.8.4 SCL 20150526 - display "Disabled" when EVSE_STATE_DISABLED instead of "Stopped" diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index ed50be7a..4234fbfe 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -993,7 +993,7 @@ void J1772EVSEController::Update() chargingOff(); // open the relay - if ((prevevsestate != EVSE_STATE_NO_GROUND) && (m_NoGndTripCnt < 254)) { + if ((prevevsestate != EVSE_STATE_NO_GROUND) && (m_NoGndTripCnt < 253)) { m_NoGndTripCnt++; eeprom_write_byte((uint8_t*)EOFS_NOGND_TRIP_CNT,m_NoGndTripCnt); } @@ -1043,7 +1043,7 @@ void J1772EVSEController::Update() ((curms - m_StuckRelayStartTimeMS) > STUCK_RELAY_DELAY) ) || // start delay de-bounce (prevevsestate == EVSE_STATE_STUCK_RELAY) ) { // already in error state // stuck relay - if ((prevevsestate != EVSE_STATE_STUCK_RELAY) && (m_StuckRelayTripCnt < 254)) { + if ((prevevsestate != EVSE_STATE_STUCK_RELAY) && (m_StuckRelayTripCnt < 253)) { m_StuckRelayTripCnt++; eeprom_write_byte((uint8_t*)EOFS_STUCK_RELAY_TRIP_CNT,m_StuckRelayTripCnt); } @@ -1063,7 +1063,7 @@ void J1772EVSEController::Update() m_EvseState = EVSE_STATE_GFCI_FAULT; if (prevevsestate != EVSE_STATE_GFCI_FAULT) { // state transition - if (m_GfiTripCnt < 254) { + if (m_GfiTripCnt < 253) { m_GfiTripCnt++; eeprom_write_byte((uint8_t*)EOFS_GFI_TRIP_CNT,m_GfiTripCnt); } diff --git a/open_evse.ino b/open_evse.ino index 15b62249..bd5b4e6f 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -376,7 +376,7 @@ void OnboardDisplay::Init() #else LcdPrint_P(0,PSTR("Open EVSE")); #endif - LcdPrint_P(0,1,PSTR("Version ")); + LcdPrint_P(0,1,PSTR("Ver. ")); LcdPrint_P(VERSTR); delay(1500); WDT_RESET(); @@ -1178,6 +1178,7 @@ void MaxCurrentMenu::Init() sprintf(g_sTmp,g_sMaxCurrentFmt,(cursvclvl == 1) ? "L1" : "L2"); g_OBD.LcdPrint(0,g_sTmp); m_CurIdx = g_EvseController.GetCurrentCapacity(); + if (m_CurIdx < m_MinCurrent) m_CurIdx = m_MinCurrent; sprintf(g_sTmp,"+%dA",m_CurIdx); g_OBD.LcdPrint(1,g_sTmp); } @@ -1185,11 +1186,11 @@ void MaxCurrentMenu::Init() void MaxCurrentMenu::Next() { if ((g_EvseController.GetCurSvcLevel() == 1) || - (m_CurIdx == 78)) { // n.b. some cars can't do 80A, so allow 79A + (m_CurIdx >= 78)) { // n.b. some cars can't do 80A, so allow 79A m_CurIdx ++; } else { - else m_CurIdx += 2; + m_CurIdx += 2; } if (m_CurIdx > m_MaxCurrent) { m_CurIdx = m_MinCurrent; From 0f1a1c37fd88e3581313a3de690aba62efc51665 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 23:44:16 -0700 Subject: [PATCH 27/96] remove MANUALSTART/AUTOSTART_MENU --- CHANGELOG | 1 + J1772EvseController.cpp | 23 +++------------------ J1772EvseController.h | 7 ------- open_evse.h | 22 +++----------------- open_evse.ino | 46 ----------------------------------------- serialcli.cpp | 26 ----------------------- strings.cpp | 3 --- strings.h | 3 --- 8 files changed, 7 insertions(+), 124 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 976c1725..71c5afcc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -23,6 +23,7 @@ vD3.9.0 SCL 20150604 - fix RAPI bug: $GF was returning FF as trip counts if zero. New max value for any trip count = FE - fix documentation for $SC & $GG +- got rid of superfluous AUTOSTART_MENU and MANUALSTART code vD3.8.4 SCL 20150526 - display "Disabled" when EVSE_STATE_DISABLED instead of "Stopped" diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 4234fbfe..8b4d903d 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -354,19 +354,6 @@ void J1772EVSEController::EnableAutoSvcLevel(uint8_t tf) #endif // ADVPWR -// Functions to support Auto Start feature - GoldServe -#ifdef MANUALSTART -void J1772EVSEController::EnableAutoStart(uint8_t tf) -{ - if (tf) { - m_wFlags &= ~ECF_AUTO_START_DISABLED; - } - else { - m_wFlags |= ECF_AUTO_START_DISABLED; - } - SaveEvseFlags(); -} -#endif //#ifdef MANUALSTART void J1772EVSEController::EnableSerDbg(uint8_t tf) { if (tf) { @@ -902,15 +889,11 @@ void J1772EVSEController::Init() SetSvcLevel(svclvl); - // Start Manual Start Feature - GoldServe -#ifdef MANUALSTART - if (AutoStartEnabled()){ - Enable(); - } else { +#ifdef DELAYTIMER + if (g_DelayTimer.IsTimerEnabled()) { Sleep(); } -#endif //#ifdef MANUALSTART - // End Manual Start Feature - GoldServe +#endif g_OBD.SetGreenLed(0); } diff --git a/J1772EvseController.h b/J1772EvseController.h index 0621128d..877d0dbc 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -284,13 +284,6 @@ class J1772EVSEController { uint8_t SerDbgEnabled() { return (m_wFlags & ECF_SERIAL_DBG) ? 1 : 0; } - // Function to suppport Auto Start feature - GoldServe -#ifdef MANUALSTART - void EnableAutoStart(uint8_t tf); - uint8_t AutoStartEnabled() { - return (m_wFlags & ECF_AUTO_START_DISABLED) ? 0 : 1; - } -#endif //ifdef MANUALSTART void EnableSerDbg(uint8_t tf); #ifdef RGBLCD int SetBacklightType(uint8_t t,uint8_t update=1); // BKL_TYPE_XXX diff --git a/open_evse.h b/open_evse.h index dad15944..db76a6b7 100644 --- a/open_evse.h +++ b/open_evse.h @@ -162,19 +162,10 @@ // Option for Delay Timer - GoldServe #define DELAYTIMER -// Option for AutoStart Menu. If defined, ManualStart feature is also defined by default - GoldServe -//#define AUTOSTART_MENU - #if defined(DELAYTIMER) && defined(BTN_MENU) #define DELAYTIMER_MENU #endif -// AutoStart feature must be defined if Delay Timers are used - GoldServe -#if defined(DELAYTIMER)||defined(AUTOSTART_MENU) -// Option for AutoStart Enable/Disable - GoldServe -#define MANUALSTART -#endif - #endif // RTC // if defined, this pin goes HIGH when the EVSE is sleeping, and LOW otherwise @@ -923,16 +914,6 @@ class BklTypeMenu : public Menu { }; #endif // RGBLCD -#ifdef AUTOSTART_MENU -class AutoStartMenu : public Menu { -public: - AutoStartMenu(); - void Init(); - void Next(); - Menu *Select(); -}; -#endif //#ifdef AUTOSTART_MENU - #if defined(DELAYTIMER) class RTCMenu : public Menu { public: @@ -1139,6 +1120,9 @@ extern char g_sSpace[]; #define g_sSpace " " +#ifdef DELAYTIMER +extern DelayTimer g_DelayTimer; +#endif #ifdef BTN_MENU extern BtnHandler g_BtnHandler; extern SettingsMenu g_SettingsMenu; diff --git a/open_evse.ino b/open_evse.ino index bd5b4e6f..b0628c1a 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -76,9 +76,6 @@ RlyChkMenu g_RlyChkMenu; #endif // ADVPWR ResetMenu g_ResetMenu; // Instantiate additional Menus - GoldServe -#ifdef AUTOSTART_MENU -AutoStartMenu g_AutoStartMenu; -#endif //#ifdef AUTOSTART_MENU #if defined(DELAYTIMER_MENU) RTCMenu g_RTCMenu; RTCMenuMonth g_RTCMenuMonth; @@ -111,9 +108,6 @@ Menu *g_SettingsMenuList[] = { #ifdef DELAYTIMER_MENU &g_DelayMenu, #endif // DELAYTIMER_MENU -#ifdef AUTOSTART_MENU - &g_AutoStartMenu, -#endif // AUTOSTART_MENU &g_SetupMenu, &g_ResetMenu, NULL @@ -1804,44 +1798,6 @@ Menu *DelayMenuStopMin::Select() return &g_SettingsMenu; } #endif // DELAYTIMER_MENU -#ifdef AUTOSTART_MENU -// Menus for Auto Start feature - GoldServe -AutoStartMenu::AutoStartMenu() -{ - m_Title = g_psAutoStart; -} -void AutoStartMenu::Init() -{ - g_OBD.LcdPrint_P(0,g_psAutoStart); - m_CurIdx = !g_EvseController.AutoStartEnabled(); - sprintf(g_sTmp,"+%s",g_YesNoMenuItems[m_CurIdx]); - g_OBD.LcdPrint(1,g_sTmp); -} -void AutoStartMenu::Next() -{ - - if (++m_CurIdx >= 2) { - m_CurIdx = 0; - } - g_OBD.LcdClearLine(1); - g_OBD.LcdSetCursor(0,1); - uint8_t dce = g_EvseController.AutoStartEnabled(); - if ((dce && !m_CurIdx) || (!dce && m_CurIdx)) { - g_OBD.LcdPrint(g_sPlus); - } - g_OBD.LcdPrint(g_YesNoMenuItems[m_CurIdx]); - -} -Menu *AutoStartMenu::Select() -{ - g_OBD.LcdPrint(0,1,g_sPlus); - g_OBD.LcdPrint(g_YesNoMenuItems[m_CurIdx]); - g_EvseController.EnableAutoStart((m_CurIdx == 0) ? 1 : 0); - g_EvseController.SaveEvseFlags(); - delay(500); - return &g_SettingsMenu; -} -#endif //#ifdef AUTOSTART_MENU #ifdef CHARGE_LIMIT #define MAX_CHARGE_LIMIT 40 @@ -2165,7 +2121,6 @@ void DelayTimer::CheckTime() void DelayTimer::Enable(){ m_DelayTimerEnabled = 0x01; eeprom_write_byte((uint8_t*)EOFS_TIMER_FLAGS, m_DelayTimerEnabled); - g_EvseController.EnableAutoStart(0); g_EvseController.SaveSettings(); CheckTime(); g_OBD.Update(OBD_UPD_FORCE); @@ -2173,7 +2128,6 @@ void DelayTimer::Enable(){ void DelayTimer::Disable(){ m_DelayTimerEnabled = 0x00; eeprom_write_byte((uint8_t*)EOFS_TIMER_FLAGS, m_DelayTimerEnabled); - g_EvseController.EnableAutoStart(1); g_EvseController.SaveSettings(); g_OBD.Update(OBD_UPD_FORCE); } diff --git a/serialcli.cpp b/serialcli.cpp index e59ba82c..09b982fe 100644 --- a/serialcli.cpp +++ b/serialcli.cpp @@ -94,11 +94,6 @@ void CLI::getInput() print_P(PSTR("Stuck Relay Check: ")); println_P(g_EvseController.StuckRelayChkEnabled() ? g_psEnabled : g_psDisabled); #endif // ADVPWR - // Option to disable auto start - GoldServe -#ifdef MANUALSTART - print_P(PSTR("Auto Start: ")); - println_P(g_EvseController.AutoStartEnabled() ? g_psEnabled : g_psDisabled); -#endif //#ifdef MANUALSTART // Start Delay Timer feature - GoldServe #ifdef DELAYTIMER print_P(PSTR("Delay Timer: ")); @@ -157,11 +152,6 @@ void CLI::getInput() println_P(PSTR("gndchk on/off - turn ground check on/off")); println_P(PSTR("rlychk on/off - turn stuck relay check on/off")); #endif // ADVPWR - // Start Delay Timer feature - GoldServe -#ifdef MANUALSTART - println_P(PSTR("autostart on/off - enable/disable autostart")); -#endif //#ifdef MANUALSTART - // End Delay Timer feature - GoldServe println_P(PSTR("sdbg on/off - turn serial debugging on/off")); } else if (strncmp_P(m_CLIinstr, PSTR("set "),4) == 0) { @@ -202,22 +192,6 @@ void CLI::getInput() println_P(g_psDisabled); } } - // Start Delay Timer feature - GoldServe -#ifdef MANUALSTART - else if (!strncmp_P(p,PSTR("autostart "),10)) { - p += 10; - print_P(PSTR("autostart ")); - if (!strcmp_P(p,g_pson)) { - g_EvseController.EnableAutoStart(1); - println_P(g_psEnabled); - } - else { - g_EvseController.EnableAutoStart(0); - println_P(g_psDisabled); - } - } -#endif //#ifdef MANUALSTART - // End Delay Timer feature - GoldServe #ifdef ADVPWR else if (!strncmp_P(p,PSTR("gndchk "),7)) { p += 7; diff --git a/strings.cpp b/strings.cpp index fb5069c4..f3f0c24f 100644 --- a/strings.cpp +++ b/strings.cpp @@ -49,9 +49,6 @@ const char g_psResetNow[] PROGMEM = "Restart Now?"; const char g_psReset[] PROGMEM = "Restart"; const char g_psExit[] PROGMEM = "Exit"; // Add additional strings - GoldServe -#ifdef AUTOSTART_MENU -const char g_psAutoStart[] PROGMEM = "Auto Start"; -#endif //#ifdef AUTOSTART_MENU #ifdef DELAYTIMER_MENU const char g_psRTC[] PROGMEM = "Date/Time"; const char g_psRTC_Month[] PROGMEM = "Month"; diff --git a/strings.h b/strings.h index 33fc94b0..95f21152 100644 --- a/strings.h +++ b/strings.h @@ -51,9 +51,6 @@ extern const char g_psResetNow[] PROGMEM; extern const char g_psReset[] PROGMEM; extern const char g_psExit[] PROGMEM; // Add additional strings - GoldServe -#ifdef AUTOSTART_MENU -extern const char g_psAutoStart[] PROGMEM; -#endif //#ifdef AUTOSTART_MENU #ifdef DELAYTIMER_MENU extern const char g_psRTC[] PROGMEM; extern const char g_psRTC_Month[] PROGMEM; From e816feb598a4462372aab3fab368165d592527ae Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 4 Jun 2015 23:59:28 -0700 Subject: [PATCH 28/96] add dependency --- open_evse.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/open_evse.h b/open_evse.h index db76a6b7..d7a359e0 100644 --- a/open_evse.h +++ b/open_evse.h @@ -106,8 +106,10 @@ #ifdef AMMETER // kWh Recording feature depends upon #AMMETER support #define KWH_RECORDING +#ifdef KWH_RECORDING // stop charging after a certain kWh reached #define CHARGE_LIMIT +#endif // KWH_RECORDING #endif //AMMETER //Adafruit RGBLCD (MCP23017) - can have RGB or monochrome backlight From b332e7275c42210b4899c67f894b05b21cae0472 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 5 Jun 2015 08:44:17 -0700 Subject: [PATCH 29/96] fix fault countdown displaying during menu --- open_evse.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/open_evse.ino b/open_evse.ino index b0628c1a..a6bd3e89 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -2007,11 +2007,11 @@ void BtnHandler::ChkBtn() #endif // CHARGE_LIMIT m_SavedLcdMode = g_OBD.IsLcdBacklightMono() ? BKL_TYPE_MONO : BKL_TYPE_RGB; g_OBD.LcdSetBacklightColor(WHITE); + g_OBD.DisableUpdate(1); if (infaultstate) { m_CurMenu = &g_SetupMenu; } else { - g_OBD.DisableUpdate(1); g_EvseController.Sleep(); m_CurMenu = &g_SettingsMenu; } From b6b504f4cc0b050c1bfbf7298a681999926aade3 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 5 Jun 2015 09:21:43 -0700 Subject: [PATCH 30/96] fix for blanked out charge screen when KWH_RECORDING and/or TEMPERATURE_MONITORING disabled --- open_evse.ino | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/open_evse.ino b/open_evse.ino index a6bd3e89..fb0ad665 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -662,7 +662,8 @@ void OnboardDisplay::Update(int8_t updmode) #endif -#if defined(AMMETER) && defined(LCD16X2) +#ifdef LCD16X2 +#if defined(AMMETER) if (((curstate == EVSE_STATE_C) || g_EvseController.AmmeterCalEnabled()) && AmmeterIsDirty()) { SetAmmeterDirty(0); @@ -684,7 +685,6 @@ void OnboardDisplay::Update(int8_t updmode) } #endif // AMMETER -#ifdef LCD16X2 if (curstate == EVSE_STATE_C) { time_t elapsedTime = g_EvseController.GetElapsedChargeTime(); @@ -710,8 +710,6 @@ void OnboardDisplay::Update(int8_t updmode) sprintf(g_sTmp,"%6lukWh",(g_WattHours_accumulated / 1000)); // display accumulated kWh LcdPrint(7,1,g_sTmp); #endif // VOLTMETER -#else // ! KWH_RECORDING - LcdClearLine(0); #endif // KWH_RECORDING #ifdef TEMPERATURE_MONITORING @@ -760,7 +758,7 @@ void OnboardDisplay::Update(int8_t updmode) LcdSetBacklightColor(TEAL); #endif } -#else // !TEMPERATURE_MONITORING + #ifndef KWH_RECORDING int h = hour(elapsedTime); // display the elapsed charge time int m = minute(elapsedTime); From 631348f6d9919cd58116b24e815f00ab62688eff Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 5 Jun 2015 10:38:37 -0700 Subject: [PATCH 31/96] delete --- open_evse-378.hex | 1916 --------------------------------------------- 1 file changed, 1916 deletions(-) delete mode 100644 open_evse-378.hex diff --git a/open_evse-378.hex b/open_evse-378.hex deleted file mode 100644 index d3511ed9..00000000 --- a/open_evse-378.hex +++ /dev/null @@ -1,1916 +0,0 @@ -:100000000C94C9010C94E4310C9411320C94FA0153 -:100010000C94FA010C94FA010C94FA010C94FA0174 -:100020000C94FA010C94FA010C94FA010C94FA0164 -:100030000C94FA010C94FA010C94FA010C94FA0154 -:100040000C943E320C94FA010C94C8340C94FA349B -:100050000C94FA010C94FA010C94FA010C94FA0134 -:100060000C9497300C94FA018B299E29D629112CD7 -:10007000112C112C112C112C112C112C112C112C98 -:10008000112C112C112C112C112CE929112C0C2ABA -:10009000202A352A462A582A112C112C112C6A2A7A -:1000A000802A112C112C112C112C112C9F2A462A3C -:1000B000B42A112C172B3A2B112C462B112C552B13 -:1000C000682B7F2B112C112C112C112C112C112C85 -:1000D000112C112C992B112C112CAE2BCB2BCF2B9F -:1000E000F82B526573657474696E672E2E2E005658 -:1000F000657273696F6E20004F70656E20455653B0 -:10010000450053657420446174652F54696D653FE3 -:100110000052657374617274204E6F773F004368BC -:10012000617267696E670052656164790000080C4E -:100130000E0C08000000000E0E0E000000000E1550 -:1001400017110E0000544553542044495341424C6A -:10015000454400436F6E6E656374656400536C655F -:100160006570696E670057616974696E67005374E2 -:100170006F7070656400535455434B2052454C4199 -:1001800059004E4F2047524F554E44004F5645524E -:100190002054454D50455241545552450047464321 -:1001A000490047464349204641554C540044494F75 -:1001B000444520434845434B0056454E5420524544 -:1001C00051554952454400534552564943452052E2 -:1001D00045515549524544004556534520455252D4 -:1001E0004F520054455354204641494C4544005316 -:1001F0007663204C6576656C3A204C32005376630A -:10020000204C6576656C3A204C31004175746F2046 -:100210004465746563740053656C6620546573743B -:1002200000506F776572204F6E00536574205374D1 -:100230006F70204D696E005365742053746F702089 -:10024000486F757200536574205374617274204D49 -:10025000696E0053657420537461727420486F7521 -:10026000720044656C61792054696D657200536554 -:1002700074204D696E7574650053657420486F7500 -:10028000720053657420596561720053657420448F -:10029000617900536574204D6F6E74680044617419 -:1002A000652F54696D6500457869740052657374F3 -:1002B000617274004746492053656C66205465732B -:1002C0007400537475636B2052656C6179204368C8 -:1002D0006B0047726F756E6420436865636B004204 -:1002E00061636B6C6967687420547970650056654A -:1002F0006E7420526571276420436865636B004407 -:10030000696F646520436865636B004D61782043C5 -:10031000757272656E740053657276696365204C00 -:100320006576656C00536574757000332E372E3812 -:1003300000312E302E33001F1C1F1E1F1E1F1F1EBC -:100340001F1E1F0000000800020100000304070038 -:10035000000000000000000102040810204080019D -:1003600002040810200102040810200404040404FC -:100370000404040202020202020303030303030053 -:10038000000000250028002B0000550FBC28B52FC9 -:100390002A3511241FBECFEFD8E0DEBFCDBF14BE7B -:1003A00088E10FB6F89480936000109260000FBE51 -:1003B00013E0A0E0B1E0E4E2F5E702C005900D92A1 -:1003C000AE37B107D9F726E0AEE7B3E001C01D9222 -:1003D000AD3AB207E1F713E0C2E9D3E004C02297D7 -:1003E000FE010E94EB36CA38D107C9F70E946E356C -:1003F0000C94903A0C94000008951F93CF93DF93D0 -:10040000EC01162F688183E495E00E94932F612F01 -:1004100083E495E00E94372F83E495E00E94B22F99 -:1004200042E0688183E495E00E948D2F83E495E0AB -:100430000E94E72ED82FCC2783E495E00E94E72E78 -:100440009E01282BC901DF91CF911F910895CF9371 -:10045000DF93EC01688383E495E00E94762F66E0E9 -:10046000CE010E94FD018435910559F467E0CE016B -:100470000E94FD0121E08115944009F020E0822FC7 -:1004800001C080E0DF91CF91089565E00E94FD01F9 -:100490009C01AC015F706AE0649FC001659F900D94 -:1004A000112444E0969587954A95E1F7A0E0B0E0E5 -:1004B00034FF03C09150A109B1090895DC01ED9109 -:1004C000FC910190F081E02D0994FC016083089576 -:1004D0000F931F93CF93DF931F92CDB7DEB7062FF5 -:1004E000152FFC01608183E495E049830E94932FDE -:1004F000602F83E495E00E94372F612F70E083E442 -:1005000095E00E945E024981642F712F83E495E09B -:100510000E945E0283E495E00F90DF91CF911F91DE -:100520000F910C94B22F1F93CF93DF93EC01162FF2 -:10053000688183E495E00E94932F612F83E495E026 -:100540000E94372F83E495E00E94B22F688183E4F4 -:1005500095E00E94932F42E0688183E495E00E9439 -:100560008D2F83E495E00E94E72ED82F80E0C82FDE -:1005700083E495E00E94E72EC82BD92B83E495E015 -:100580000E94B22FCE01DF91CF911F9108950F935A -:100590001F93CF93DF931F92CDB7DEB78C0183E417 -:1005A00095E069830E94762F6981462F50E0406470 -:1005B000516162E0C8010E94680240E050EC65E0D1 -:1005C000C8010E9468026FE1C8010E94930221E005 -:1005D0008837910509F020E0822F0F90DF91CF91AD -:1005E0001F910F91089563E00E94930280FD0FC058 -:1005F000DC012DE231E341E050E00E94093720E4C4 -:1006000032E44FE050E00E94C736C901089580E00F -:1006100090E00895FC016115710561F0A081B18140 -:1006200092816130710579F412968C911297892B21 -:1006300012968C93A081B18111968C911197928121 -:100640009095892304C011968C911197892B11964E -:100650008C930895FC017183608351E001C0550FB4 -:100660004A95EAF75283B9010C940A036E3008F0F8 -:100670006E50FC01608308952091000140E4249FA6 -:1006800090011124FC0180818770822B80937C0073 -:1006900080917A00806480937A0080917A0086FD50 -:1006A000FCCF809178002091790090E0922B089502 -:1006B000FC011382128280E791E0918380834423BE -:1006C00011F082E001C081E0838722876187148274 -:1006D0000895682F83E495E00C94372FCF93DF9330 -:1006E000D62FC72FFC0191859062692F70E083E4BB -:1006F00095E00E949D2F82E10E9469038D2F0E9448 -:1007000069038C2F0E94690383E495E00E94B22F55 -:100710008111FACFDF91CF910895FC018385882361 -:1007200009F18285882311F087E0682760FD03C006 -:1007300080EC91E002C080E891E09587848761FFBA -:1007400005C0848595858F779587848762FF05C06E -:10075000848595859E7F9587848764857585CF017F -:100760000C946E030895CF93C62FFC01918590627F -:10077000692F70E083E495E00E949D2F83E10E9441 -:1007800069038C2F0E94690383E495E00E94B22FD5 -:100790008111FACFCF910895EF92FF920F931F939B -:1007A000CF93DF931F92CDB7DEB77C01162FFC01EC -:1007B000858564FD806115FD886016FD846017FDE8 -:1007C0008260082F442311F0006A01C00062602F8C -:1007D000C70149830E94B303602F6F7DC7010E9448 -:1007E000B303F7018585498110FD806111FD8860A3 -:1007F00012FD846013FD8260182F442311F0106AEB -:1008000001C01062612FC7010E94B303612F6F7D89 -:10081000C7010F90DF91CF911F910F91FF90EF9043 -:100820000C94B30340E00C94CC03FC0123852223F9 -:1008300039F061E00E94120480ED97E00C94DF3201 -:100840000895CF93DF93CDB7DEB728970FB6F8940E -:10085000DEBF0FBECDBFFC012385222319F128E0A6 -:10086000E4E6F1E0DE01119601900D922A95E1F7A0 -:10087000FC012781241710F44FEF420FFE0131963F -:10088000E40FF11DE40FF11D2081260F2068622F77 -:1008900028960FB6F894DEBF0FBECDBFDF91CF9183 -:1008A0000C94120428960FB6F894DEBF0FBECDBF8D -:1008B000DF91CF910895FC012385222339F06581D2 -:1008C000262F246025836C600C9412040895CF9227 -:1008D000DF92EF92FF920F931F93CF93DF9300D09D -:1008E000CDB7DEB76C01DC011B968C91882319F122 -:1008F0006770B8E06B9FB00111246064C601498342 -:100900005A830E9412044981042F5A81152FE12C29 -:10091000F12CF80161918F01D601ED91FC910190CC -:10092000F081E02DC6010995BFEFEB1AFB0AE8E064 -:10093000EE16F10471F70F900F90DF91CF911F9198 -:100940000F91FF90EF90DF90CF900895FC012385E9 -:10095000222319F041E00E94CC0381E090E0089549 -:100960000F931F93CF93DF93EC01142F022F62E3B9 -:1009700070E080E090E00E94B73283E495E00E944E -:10098000762F6985606270E083E495E00E949D2F78 -:1009900080E00E9469038FE10E94690383E495E08F -:1009A0000E94B22F882319F08B85823061F169850E -:1009B000606270E083E495E00E949D2F8CE00E94CD -:1009C00069038FE10E94690383E495E00E94B22FDE -:1009D000882319F08B858230B1F06985606270E000 -:1009E00083E495E00E949D2F81E00E94690380E0EE -:1009F0000E94690383E495E00E94B22F9B858823BF -:100A000041F0923031F41B86DF91CF911F910F910D -:100A10000895923011F481E08B87123018F08C81A8 -:100A200088608C831F831886002329F0113019F405 -:100A30008C8184608C831D861C8613E068E3CE0164 -:100A40000E94B30368E1CE010E94B3031150B1F7D5 -:100A500068E2CE010E94B30368E0CE010E94B303B6 -:100A600065E070E080E090E00E94B7326C816062E7 -:100A7000CE010E94120465E070E080E090E00E94E8 -:100A8000B7326C816062CE010E94120465E070E0B2 -:100A900080E090E00E94B7328CE08D83CE010E940E -:100AA0005B04CE010E94150482E08E8366E0CE01D5 -:100AB000DF91CF911F910F910C941204CF93DF938C -:100AC000EC018B858823F1F06985606270E083E436 -:100AD00095E00E949D2F82E10E94690383E495E0E6 -:100AE0000E94B22F6985606270E041E050E083E4CB -:100AF00095E00E94902F83E495E00E94E72E809578 -:100B00008F7101C080E0DF91CF910895CF93DF9383 -:100B1000EC0164EF71E080E090E00E94B7328C81DC -:100B2000882329F0813031F487E994E005C08CE90D -:100B300094E002C082E994E0DF91CF910895DC0156 -:100B4000ED91FC911197228112968C91282B228392 -:100B50000895DC01ED91FC911197208112968C9102 -:100B6000282381E009F480E0089561110C949F0529 -:100B7000DC01ED91FC911197228112968C91809568 -:100B8000282322830895FC01238521110C94BB3571 -:100B900008951F93CF93DF93EC01162F462F60E04B -:100BA0000E9421048B85882339F060915E01709149 -:100BB0005F01CE010E94BB35412F60E0CE01DF9185 -:100BC000CF911F910C942104CF93DF93EC018C8182 -:100BD0008F5F823010F48C8301C01C8261E070E072 -:100BE0008CEA93E00E94C90541E060E08CEA93E062 -:100BF0000E94210464E771E08CEA93E00E94C3053F -:100C0000EC81F0E0EE0FFF1FE85DFE4F6081718127 -:100C10008CEA93E0DF91CF910C94C305CF93DF93DF -:100C2000EC018C818F5F8C839E81981708F41C8265 -:100C300061E070E08CEA93E00E94C90541E060E069 -:100C40008CEA93E00E9421048C81EF81F885E80F03 -:100C5000F11D909114048081981306C064E771E03F -:100C60008CEA93E00E94C3058C81EF81F885E80F40 -:100C7000F11D60818091B703882339F070E04AE06C -:100C800050E08CEA93E00E94673666EC71E08CEAF3 -:100C900093E0DF91CF910C94C305CF93DF93EC01E8 -:100CA0008C818F5F833010F48C8301C01C8261E0E3 -:100CB00070E08CEA93E00E94C90541E060E08CEAB4 -:100CC00093E00E9421048091000480FF02C022E092 -:100CD00001C021E030E08C8190E0019628173907AF -:100CE00031F464E771E08CEA93E00E94C305EC8183 -:100CF000F0E0EE0FFF1FE45DFE4F608171818CEA32 -:100D000093E0DF91CF910C94C305CF93DF93EC0177 -:100D10008C818F5F823010F48C8301C01C8261E073 -:100D200070E08CEA93E00E94C90541E060E08CEA43 -:100D300093E00E942104909100048C8191FD03C0F6 -:100D4000882319F008C0882331F064E771E08CEA49 -:100D500093E00E94C305EC81F0E0EE0FFF1FE95F16 -:100D6000FE4F608171818CEA93E0DF91CF910C940A -:100D7000C305CF93DF93EC018C818F5F823010F439 -:100D80008C8301C01C8261E070E08CEA93E00E94D9 -:100D9000C90541E060E08CEA93E00E942104909153 -:100DA00000048C8192FD03C0882319F008C08823B9 -:100DB00031F064E771E08CEA93E00E94C305EC81B6 -:100DC000F0E0EE0FFF1FE95FFE4F608171818CEA5A -:100DD00093E0DF91CF910C94C305CF93DF93EC01A7 -:100DE0008C818F5F823010F48C8301C01C8261E0A3 -:100DF00070E08CEA93E00E94C90541E060E08CEA73 -:100E000093E00E942104909100048C8193FD03C023 -:100E1000882319F008C0882331F064E771E08CEA78 -:100E200093E00E94C305EC81F0E0EE0FFF1FE95F45 -:100E3000FE4F608171818CEA93E0DF91CF910C9439 -:100E4000C305CF93DF93EC018C818F5F823010F468 -:100E50008C8301C01C8261E070E08CEA93E00E9408 -:100E6000C90541E060E08CEA93E00E942104909182 -:100E700000048C8194FD03C0882319F008C08823E6 -:100E800031F064E771E08CEA93E00E94C305EC81E5 -:100E9000F0E0EE0FFF1FE95FFE4F608171818CEA89 -:100EA00093E0DF91CF910C94C305CF93DF93EC01D6 -:100EB0008C818F5F823010F48C8301C01C8261E0D2 -:100EC00070E08CEA93E00E94C90541E060E08CEAA2 -:100ED00093E00E94210480910004909101042C81F0 -:100EE00091FD03C0222319F008C0222331F064E7EA -:100EF00071E08CEA93E00E94C305EC81F0E0EE0F14 -:100F0000FF1FE95FFE4F608171818CEA93E0DF9102 -:100F1000CF910C94C305FC01238521110C94150479 -:100F20000895DC011496EC91149715968C91E817AE -:100F300040F4F0E0EE0FFF1FEA5BFE4F80819181ED -:100F400008958CEA93E00E948B0780E090E008957A -:100F5000FC01268520FF02C0611167E00C948D031F -:100F6000FC0191818381911108C02281211105C06A -:100F7000811103C02481222341F0981711F081E0F0 -:100F80000895928184819813FACF80E008956BE2EE -:100F900073E00C944337CF92EF920F93CF93DF938C -:100FA00000D000D000D0CDB7DEB790E0CE2CE02E40 -:100FB000022F242F462FBC01CE0101960E94112D35 -:100FC000CE0101960E94BE2D26960FB6F894DEBF84 -:100FD0000FBECDBFDF91CF910F91EF90CF900895CD -:100FE000CF93DF93EC010E94272D2093A60330932B -:100FF000A7034093A8035093A9036093AA03709397 -:10100000AB03E6EAF3E01F927F9384811F928F93F4 -:1010100083811F928F9382811F928F9381811F9270 -:101020008F9380811F928F9326E731E03F932F9318 -:10103000DF93CF930E9480378DB79EB740960FB64F -:10104000F8949EBF0FBE8DBFDF91CF910895CF92D0 -:10105000DF92EF92FF920F931F93CF93DF938A015A -:1010600010927804611571058105910561F480E3A2 -:1010700080937704FA013197C7E7D4E0CE018E1B45 -:101080009F0B20E31AC0C8E7D4E0EAE0CE2ED12CB3 -:10109000E12CF12CA70196010E94C736605D6A938E -:1010A000FE01E857F440E00FF11FB901CA016115D4 -:1010B00071058105910571F7E1CFC817D90711F0C6 -:1010C0002A93FBCFCE01DF91CF911F910F91FF901B -:1010D000EF90DF90CF90089560910004709101042B -:1010E00082E090E00C949E39609114048091000499 -:1010F00080FF02C022E001C021E081E090E02130C9 -:1011000011F480E090E00E9488390C946C08CF9331 -:10111000DF93EC01188286EE90E09D838C839F83A1 -:101120008E839987888768E1CE0101960E94270205 -:1011300060E0CE0102960E94C702809114048B8366 -:10114000DF91CF910895CF93DF93EC0120E041E050 -:1011500060E20E94580381E188A3DF91CF91089556 -:101160000F931F93CF93DF93EC0189018B85882325 -:1011700059F0CE010E942104B801CE01DF91CF9138 -:101180001F910F910C94BB35DF91CF911F910F915F -:101190000895CF93DF93EC018C818F5F823010F440 -:1011A0008C8301C01C8261E070E08CEA93E00E94B5 -:1011B000C905EC81F0E0EE0FFF1FE95FFE4F2081D3 -:1011C000318141E050E060E070E08CEA93E0DF9133 -:1011D000CF910C94B008CF93DF93EC018C818F5F9B -:1011E000823010F48C8301C01C8261E070E08CEAD4 -:1011F00093E00E94C905EC81F0E0EE0FFF1FE95F6C -:10120000FE4F2081318141E050E060E070E08CEAE7 -:1012100093E0DF91CF910C94B008CF93DF93EC0172 -:1012200024E731E041E050E060E070E08CEA93E0D8 -:101230000E94B008EC81F0E0EE0FFF1FE95FFE4F67 -:10124000608171818CEA93E00E94C30564EF71E0D4 -:1012500080E090E00E94B7328C81811103C08AEB5C -:1012600094E002C085EF94E0DF91CF910895CF9391 -:10127000DF93EC018C818F5F833010F48C8301C08D -:101280001C8261E070E08CEA93E00E94C905EC8169 -:10129000F0E0EE0FFF1FEF5FFE4F2081318141E054 -:1012A00050E060E070E08CEA93E0DF91CF910C9425 -:1012B000B008CF93DF93EC018C818F5F823010F404 -:1012C0008C8301C01C8261E070E08CEA93E00E9494 -:1012D000C905EC81F0E081E090E020919B0322239E -:1012E00011F080E090E0E817F907C1F424E731E05D -:1012F00041E050E060E070E08CEA93E00E94B008CA -:10130000EC81F0E0EE0FFF1FE95FFE4F608171811D -:101310008CEA93E0DF91CF910C94C305EE0FFF1F91 -:10132000E95FFE4F2081318141E050E060E070E0F4 -:101330008CEA93E0DF91CF910C94B0080F931F9348 -:10134000CF93DF93EC018B858823A9F08E01015F99 -:101350001F4F48A150E0C8010E944A3788A1FE01F2 -:10136000E80FF11D1686B801CE01DF91CF911F91D4 -:101370000F910C94BB35DF91CF911F910F91089580 -:10138000EF92FF920F931F93CF93DF93EC01F62E12 -:10139000E42E8C01015F1F4F88A1482F50E0B90156 -:1013A000C8010E944A3788A1FE01E80FF11D168688 -:1013B0004E2D6F2DCE010E942104B801CE01DF9188 -:1013C000CF911F910F91FF90EF900C94BB35CF936D -:1013D000DF93EC01A89518861F820E94A905882337 -:1013E00019F081E08B8301C01B82DF91CF910895BA -:1013F000CF93DF93EC0120E030E042E069E270E05F -:101400000E942A0343E050E069E17BE080E00E9413 -:10141000C43122E030E046E069E270E0CE0104969B -:101420000E942A03CE01DF91CF910C94E709EF923D -:10143000FF920F931F93CF93DF93EC0181E0888796 -:101440001F820CE310E07E0184E0E80EF11CC7016E -:101450000E949F058DE890E20E94DF3260E0C701A4 -:101460000E94B5058DE890E20E94DF328F818823CB -:1014700069F062E370E080E090E00E94B732A895E6 -:10148000CE010E94A9058111F4CF04C001501109B9 -:10149000F1F6EFCF68EE73E080E090E00E94B732A3 -:1014A0001B82188681E09F81911180E0DF91CF91AE -:1014B0001F910F91FF90EF9008954FB7F894611529 -:1014C000710539F420E43FE130938B0020938A00CA -:1014D00004C010928B0010928A00FC01718360831B -:1014E0004FBF0895E0E8F0E0108220E43FE1309340 -:1014F00087002093860021E120938100229A208199 -:101500002062208360E070E00C945D0AAB019B01D7 -:10151000265031092E32310538F465E8649F900178 -:10152000659F300D112414C09B01245331092D31C6 -:101530003105D0F4662757FD6095762F40565F4FF2 -:101540006F4F7F4F9A0145E0220F331F4A95E1F715 -:1015500030938B0020938A0021E030E0FC0131833E -:10156000208380E090E0089581E090E00895CF939B -:10157000DF93EC0161E044960E94360360E0CE0107 -:101580004596DF91CF910C943603CF93DF93EC0116 -:1015900046960E949F05CE0149960E949F05CE0166 -:1015A0004C960E949F058DA980648DAB0E946A2E87 -:1015B000FE01E05BFF4F60837183828393830E940F -:1015C0008832C85BDF4F688379838A839B83DF918E -:1015D000CF910895CF93DF93EC0160E046960E948F -:1015E000B50560E0CE0149960E94B50560E0CE01E8 -:1015F0004C960E94B5058DA98F7B8DAB0E946A2EFB -:10160000FE01EC5AFF4F60837183828393830E94B3 -:101610008832FE01E45BFF4F608371838283938392 -:10162000CC59DF4F188219821A821B82DF91CF9129 -:1016300008958091D703882321F081E08093D60319 -:101640000895809102048068809302048DEC93E0F9 -:101650000E94EA0A62E070E08DEC93E00E945D0A6D -:1016600081E08093D2030895FC0183A994A96623A5 -:1016700011F08D7F01C0826094AB83AB0C946C0839 -:10168000CF93DF93EC0124E731E041E050E060E0EC -:1016900070E08CEA93E00E94B008EC81F0E0EE0F7D -:1016A000FF1FE95FFE4F608171818CEA93E00E9429 -:1016B000C30561E08C81811160E08DEC93E00E94B4 -:1016C000340B64EF71E080E090E00E94B73285EF68 -:1016D00094E0DF91CF910895FC0183A994A966233A -:1016E00011F09D7F01C0926094AB83AB0C946C08A9 -:1016F000CF93DF93EC0124E731E041E050E060E07C -:1017000070E08CEA93E00E94B008EC81F0E0EE0F0C -:10171000FF1FE95FFE4F608171818CEA93E00E94B8 -:10172000C30561E08C81811160E08DEC93E00E9443 -:101730006C0B64EF71E080E090E00E94B73285EFBF -:1017400094E0DF91CF910895FC0183A994A96623C9 -:1017500011F08B7F01C0846094AB83AB0C946C0858 -:10176000CF93DF93EC0124E731E041E050E060E00B -:1017700070E08CEA93E00E94B008EC81F0E0EE0F9C -:10178000FF1FE95FFE4F608171818CEA93E00E9448 -:10179000C30561E08C81811160E08DEC93E00E94D3 -:1017A000A40B64EF71E080E090E00E94B73285EF17 -:1017B00094E0DF91CF910895FC0183A994A9662359 -:1017C00011F0877F09C011A612A613A614A615A2B0 -:1017D00016A217A210A6886094AB83AB0C946C0879 -:1017E000CF93DF93EC0124E731E041E050E060E08B -:1017F00070E08CEA93E00E94B008EC81F0E0EE0F1C -:10180000FF1FE95FFE4F608171818CEA93E00E94C7 -:10181000C30561E08C81811160E08DEC93E00E9452 -:10182000DC0B64EF71E080E090E00E94B73285EF5E -:1018300094E0DF91CF910895FC0183A994A96623D8 -:1018400011F08F7E01C0806194AB83AB0C946C0867 -:10185000CF93DF93EC0124E731E041E050E060E01A -:1018600070E08CEA93E00E94B008EC81F0E0EE0FAB -:10187000FF1FE95FFE4F608171818CEA93E00E9457 -:10188000C30561E08C81811160E08DEC93E00E94E2 -:101890001C0C64EF71E080E090E00E94B73285EFAD -:1018A00094E0DF91CF910895FC0183A994A9662368 -:1018B00011F08F7D01C0806294AB83AB0C946C08F7 -:1018C000FC01EF5BFF4F2FEF20833197108260E028 -:1018D00070E00C945D0A2AE0EAE3F1E0DC01D696C0 -:1018E00001900D922A95E1F70895AF92BF92CF92A1 -:1018F000DF92EF92FF920F931F93CF93DF938C01B0 -:101900000E9488326B017C01AA24A394C2E0BC2E01 -:10191000E8016F960E5D1F4FBB2031F0CE010E9493 -:10192000A905811101C0B12CAA20B1F0C8010E9403 -:10193000A905882381F0B11002C0AA24A3940E94B3 -:1019400088326C197D098E099F0964317105810502 -:10195000910520F4E1CFA12CB110F1CF8B2D8A2974 -:10196000DF91CF911F910F91FF90EF90DF90CF907B -:10197000BF90AF9008958F929F92AF92BF92CF92F7 -:10198000DF92EF92FF920F931F93CF93DF935B0150 -:101990006A01D4E6ED2EF12CC0E0D0E00FEF13E0A9 -:1019A00044964C01C4010E943C03382F292FC817CC -:1019B000D90734F0801791070CF48C013C2F2D2FA0 -:1019C00081E0E81AF10819F0C32FD22FEBCFF5010F -:1019D00011830083F60130832183DF91CF911F9122 -:1019E0000F91FF90EF90DF90CF90BF90AF909F90BE -:1019F0008F9008952F923F924F925F926F927F92B5 -:101A00008F929F92AF92BF92CF92DF92EF92FF920E -:101A10000F931F93CF93DF93CDB7DEB760970FB6C9 -:101A2000F894DEBF0FBECDBF8C01A8950E9488320E -:101A30006D837E838F839887212C312C88248A9410 -:101A4000982C540119821A821B821C82188A1F86C4 -:101A5000C12CD12C7601C80145969A8789870E94AE -:101A60008832AB01BC01DB01CA014D805E806F8012 -:101A7000788484199509A609B7098397A105B1054A -:101A800008F08BC089859A854B875C876D877E8738 -:101A90000E943C032C01612C712C4B855C856D856B -:101AA0007E859FEF89169906A906B90659F191E03E -:101AB000E1E04E16E2E05E06610471040CF490E091 -:101AC00081E0F1E08F16F2E09F06A104B1040CF46E -:101AD00080E09817B9F0DB01CA0189809A80AB8059 -:101AE000BC8088199909AA09BB090697A105B10507 -:101AF00048F08F8598890196988B8F8749835A8300 -:101B00006B837C83EF85F889E130F10508F442C0EE -:101B1000E330F10580F03397E9F5910140E050E0C2 -:101B2000C701B6010E94C73649015A01C12CD12C08 -:101B30007601C39411C0C301B201725081099109A9 -:101B40009B01AC010E948336C60ED71EE81EF91E0B -:101B5000FFEF2F1A3F0A1EC0A7019601C701B60169 -:101B60000E94833686169706A806B90630F02FEF36 -:101B7000C21AD20AE20AF20AEFCFF801E05AFF4F86 -:101B800081E0C81AD108E108F108C082D182E2825E -:101B9000F3820BC05301420162CFF801E05AFF4FBC -:101BA0001082118212821382A89560960FB6F89463 -:101BB000DEBF0FBECDBFDF91CF911F910F91FF9080 -:101BC000EF90DF90CF90BF90AF909F908F907F90DD -:101BD0006F905F904F903F902F900895CF92DF923B -:101BE000EF92FF9220918203211108C010927E0390 -:101BF00010927F031092800310928103C0907E03A5 -:101C0000D0907F03E0908003F0908103C60ED71E32 -:101C1000E81EF91EC0927E03D0927F03E0928003FB -:101C2000F09281032F5F20938203203261F410929F -:101C30008203C701B60125E096958795779567954C -:101C40002A95D1F703C06FEF7FEFCB01FF90EF90A4 -:101C5000DF90CF90089521E030E043E066E270E04D -:101C60000C942A030F931F93CF93DF93EC018CEA1C -:101C700093E00E945E05982F917080FD0CC08B81CF -:101C8000823049F44C815D816E817F81452B462BEA -:101C9000472B09F41B828B81882351F08130E9F5B1 -:101CA0004C815D816E817F81452B462B472BA9F1AD -:101CB000992371F14C815D816E817F81452B462B8B -:101CC000472B41F4811106C00E9488326C837D83CA -:101CD0008E839F830E9488320C811D812E813F81DB -:101CE000601B710B820B930B2B81211107C0623398 -:101CF000710581059105C8F181E008C02130A9F581 -:101D0000643F71408105910580F182E08B832DC095 -:101D10001C821D821E821F8228C0992331F14885B2 -:101D200059856A857B85452B462B472BF1F0823000 -:101D3000E1F40E948832088519852A853B85601B5D -:101D4000710B820B930B603177428105910568F02E -:101D5000188619861A861B8660E08CEF94E0DF9166 -:101D6000CF911F910F910C945628DF91CF911F9125 -:101D70000F910895FC018381813051F44481558194 -:101D800066817781452B462B472B11F413820895EA -:101D900080E00895FC018381823099F4448155816B -:101DA00066817781411551056105710551F04087C4 -:101DB000518762877387148215821682178281E0A9 -:101DC000089580E00895DC018EE392E011969C93E3 -:101DD0008E9385E293E013969C938E9312971596BB -:101DE0001C92159715962C911597E22FF0E0EE0FA7 -:101DF000FF1FEA5BFE4F80819181892B29F02F5FC5 -:101E000015962C931597EECF0895CF93DF93EC01A1 -:101E100084E090E00E9474398F3F39F4188260E0CA -:101E200084E090E00E94883901C0888385E090E0DA -:101E30000E9474398F3F39F4198260E085E090E0A8 -:101E40000E94883901C0898386E090E00E9474393D -:101E50008F3F41F485E08A8365E086E090E00E9450 -:101E6000883901C08A8387E090E00E9474398F3FEF -:101E700041F486E08B8366E087E090E00E94883939 -:101E800001C08B8388E090E00E9474398F3F49F451 -:101E900087E38C8367E388E090E0DF91CF910C9437 -:101EA00088398C83DF91CF91089585EF94E00E946B -:101EB000E30E82E592E09093F1048093F00487E1D1 -:101EC00093E09093F3048093F2048CE592E0909376 -:101ED000E8048093E7048BE093E09093EA04809316 -:101EE000E90486E692E09093E3048093E2048FEFA6 -:101EF00092E09093E5048093E40488E492E0909368 -:101F0000DE048093DD048FED92E09093E0048093F3 -:101F1000DF0480E792E09093D9048093D80484EBA7 -:101F200092E09093DB048093DA048AE792E0909346 -:101F3000D4048093D3048EEE92E09093D6048093E1 -:101F4000D50484E892E09093CF048093CE0482ED90 -:101F500092E09093D1048093D0048EE892E0909325 -:101F6000CA048093C90482EC92E09093CC048093DD -:101F7000CB0488E992E09093C5048093C4048CEA72 -:101F800092E09093C7048093C60482EA92E0909313 -:101F9000C0048093BF048DE992E09093C2048093C3 -:101FA000C1042CEA32E03093BB042093BA0426EB40 -:101FB00032E03093B6042093B5049093B804809334 -:101FC000B70480EC92E09093B1048093B0048AEC63 -:101FD00092E09093AC048093AB0484ED92E09093F4 -:101FE000A7048093A6048EED92E09093A2048093C0 -:101FF000A10482E692E09093A4048093A30428EEC7 -:1020000032E030939D0420939C0490939F0480932E -:102010009E0482EF92E090939804809397048CEF53 -:1020200092E0909393048093920486E093E09093DF -:102030008E0480938D0480E193E0909389048093D3 -:10204000880410927C0410927D0410927E041092F9 -:102050007F041092800410928104109282041092E6 -:1020600083041092840410928604109285048DECEF -:1020700093E00E94B70A8CEA93E00E94A30840E034 -:1020800050E0BA0186EA93E00E94B32C80EA95E121 -:10209000AFEFBFEF8093A2039093A303A093A40399 -:1020A000B093A5038CE893E00E94FC0160E48DE806 -:1020B00093E00C9465020F931F93CF93DF93EC0191 -:1020C00002960E94F30299878887CE0101960E94AA -:1020D00045028F3F28E0920719F09D838C8302C050 -:1020E0001D821C8268E670E083E495E00E949D2FCB -:1020F0006EE083E495E00E94372F60E283E495E090 -:102100000E94372F83E495E00E94B22F68E670E0CA -:1021100083E495E00E949D2F61E183E495E00E94B5 -:10212000372F83E495E00E94B22F42E050E068E64A -:1021300070E083E495E00E94902F83E495E00E9494 -:10214000E72E9AE0899F800111241F830E8383E488 -:1021500095E00E94E72E992746E0959587954A9548 -:10216000E1F745E0489F9001499F300D11243595D6 -:102170002795200F311F2D3F89E0380719F03F8345 -:102180002E8302C01F821E82DF91CF911F910F917B -:102190000895CF92DF92EF92FF920F931F93CF9308 -:1021A000DF937C018A01FC018385882331F1462F6E -:1021B00060E0C7010E942104F80101900020E9F7C6 -:1021C0003197DE2ED01AF0E1FD1510F460E1D62E25 -:1021D000C02EC02FD12F8C2F8C198D1528F469910A -:1021E000C7010E94A604F7CFF0E1DF1631F060E2EC -:1021F000C7010E94A604D394F7CFDF91CF911F911E -:102200000F91FF90EF90DF90CF900895EF92FF92A3 -:102210000F931F93CF93DF93EC017B01BA018C01E5 -:10222000015F1F4F48A150E0C8010E944A3788A1B2 -:10223000FE01E80FF11D1686A801B701CE01DF915E -:10224000CF911F910F91FF90EF900C94C910CF93F5 -:10225000DF93EC01A8951E8622E042E060E10E9437 -:10226000B00467E0CE010E948D0367E0CE010E94BA -:10227000A80748E050E06DE371E089E394E00E9434 -:102280002F3749E354E060E0CE010E94670448E044 -:1022900050E065E371E089E394E00E942F3749E361 -:1022A00054E061E0CE010E94670448E050E06DE236 -:1022B00071E089E394E00E942F3749E354E062E043 -:1022C000CE010E946704CE010E94150448EF50E041 -:1022D00060E070E0CE010E9406112FEE30E041E098 -:1022E00050E060E070E0CE010E94C0096BE273E054 -:1022F000CE010E949E096CED75E080E090E00E94A6 -:10230000B732A895DF91CF9108950F931F93CF9384 -:10231000DF93EC018A01AB0160E070E00E940611DE -:10232000A80161E070E0CE01DF91CF911F910F9184 -:102330000C940611CF93DF93EC018BA99CA98E71AD -:102340009270892B09F462C063E08CEA93E00E94EA -:10235000A8078BA981FF0EC04FEF52E065E471E042 -:102360008CEA93E00E9485116CED75E080E090E0CE -:102370000E94B7328BA982FF0EC04EEE52E065E498 -:1023800071E08CEA93E00E9485116CED75E080E0CD -:1023900090E00E94B7328BA983FF0EC042ED52E05D -:1023A00065E471E08CEA93E00E9485116CED75E0C4 -:1023B00080E090E00E94B7328BA984FF0EC042EC0F -:1023C00052E065E471E08CEA93E00E9485116CEDC7 -:1023D00075E080E090E00E94B7328BA99CA991FF44 -:1023E0000EC044EB52E065E471E08CEA93E00E9499 -:1023F00085116CED75E080E090E00E94B73267E0F7 -:102400008CEA93E0DF91CF910C94A807DF91CF91F4 -:102410000895AF92BF92DF92EF92FF920F931F93B6 -:10242000CF93DF9300D0CDB7DEB75C01A89560E015 -:1024300070E00E945D0A47E152E061E272E08CEADE -:1024400093E00E948511F50183A985FDD5C066E959 -:1024500070E080E090E00E94B732C50144960E948F -:102460003C038C0162E070E0C5010E945D0A053802 -:1024700013400CF4BBC0C5010E94750CD82E750129 -:1024800086E1E80EF11CC7010E949F058501045EEC -:102490001F4FC8010E949F056AEF70E080E090E046 -:1024A0000E94B732C5010E94750C382F60E0C70149 -:1024B0003A830E94B50560E0C8010E94B5056AEF45 -:1024C00070E080E090E00E94B73203501109C8012B -:1024D0000E949F056AEF70E080E090E00E94B732B2 -:1024E000C5010E94750C282F60E0C80129830E9455 -:1024F000B5056AEF70E080E090E00E94B732298174 -:102500003A81E3E0DE1234C0333028F4F50183A9C8 -:10251000313020F01FC0333061F09CC0233011F007 -:1025200010E001C012E084FD2BC0233009F094C0FC -:1025300027C0F50183A983FD02C0233021F02223A7 -:1025400021F010E003C013E001C012E0215022305E -:10255000B8F418C084FD04C02330A1F014E003C017 -:10256000233081F010E0313041F4223049F470C062 -:10257000F50183A984FF70C06DC0213009F468C0E3 -:10258000113051F44DEF51E06BE072E08CEA93E0D2 -:102590000E94851111E00AC0123041F44FEE51E063 -:1025A0006BE072E08CEA93E00E9485118DEF810F61 -:1025B000823070F561E08CEA93E00E94A807133046 -:1025C00049F442E851E063EE71E08CEA93E00E9446 -:1025D000851134C01430F1F446E751E063EE71E048 -:1025E0008CEA93E00E94851114E028C0F50185A9CA -:1025F000816085AB11E001C010E0F50183A984FD85 -:1026000009C0C5010E94750C8370833029F702C090 -:102610001330A1F0F50123A934A931FD0FC0C50184 -:1026200002960E94170A882349F04DE951E063EEB3 -:1026300071E08CEA93E00E94851115E08DEF810F27 -:10264000833060F461E08CEA93E00E94A80706C042 -:1026500012E0A4CF10E0AACF14E0A8CF60E070E0B1 -:10266000C5010E945D0AA895812F0F900F90DF9100 -:10267000CF911F910F91FF90EF90DF90BF90AF909F -:102680000895CF93DF93EC0160E070E00E945D0A53 -:1026900042EE50E061E070E08CEA93E00E940611A7 -:1026A0008DA986FF06C068EB7BE080E090E00E9489 -:1026B000B7322EE088E190E00FB6F894A8958093A9 -:1026C00060000FBE209360006CED75E080E090E04C -:1026D000DF91CF910C94B732CF93DF93EC0124E7D5 -:1026E00031E041E050E060E070E08CEA93E00E946D -:1026F000B008EC81F0E0EE0FFF1FE95FFE4F608154 -:1027000071818CEA93E00E94C30564EF71E080E080 -:1027100090E00E94B7328C81811104C08DEC93E06F -:102720000E94411380E090E0DF91CF910895FC0179 -:1027300014824281538160E070E08CEA93E00E9451 -:102740000611E0914601F09147014281538161E019 -:1027500070E08CEA93E00C940611FC0184818F5F99 -:10276000848395819817D8F02681222311F0891748 -:10277000B1F0E481E91758F4F0E0EE0FFF1FEA5BD7 -:10278000FE4F0190F081E02D4281538102C047EA63 -:1027900052E061E070E08CEA93E00C940611148240 -:1027A000E8CFCF93DF93EC014A815B8160E070E07A -:1027B0008CEA93E00E9406118091000485FD02C01E -:1027C0001C8206C080FF02C082E001C081E08C83D1 -:1027D00061E070E08CEA93E00E94C90524E731E0F3 -:1027E00041E050E060E070E08CEA93E00E94B008C5 -:1027F000EC81F0E0EE0FFF1FE45DFE4F6081718120 -:102800008CEA93E0DF91CF910C94C305CF93DF93D3 -:10281000EC0181E090919B03911180E08C8342E672 -:1028200052E060E070E08CEA93E00E94061161E003 -:1028300070E08CEA93E00E94C90524E731E041E0B2 -:1028400050E060E070E08CEA93E00E94B008EC8118 -:10285000F0E0EE0FFF1FE95FFE4F608171818CEAAF -:1028600093E0DF91CF910C94C305CF93DF93EC01FC -:102870004A815B8160E070E08CEA93E00E9406117F -:10288000E1E080918704813009F4E0E0EC83F0E03E -:10289000EE0FFF1FE85DFE4F81818F9380818F9344 -:1028A00088E891E09F938F9389E394E09F938F93BF -:1028B0000E9480370F900F900F900F900F900F9005 -:1028C00049E354E061E070E08CEA93E0DF91CF915E -:1028D0000C94C9100F931F93CF93DF93EC011C82CC -:1028E0008091000480FD04C041E081E291E003C0DA -:1028F00042E08BE091E098878F831D821E823E81AB -:10290000FC01E30FF11D2081222319F03F5F3E837C -:10291000F6CF31503E83FC01E30FF11D20812D8362 -:102920005091140420E0FC01E20FF11D6081651359 -:1029300002C02C8303C02F5F3217A8F7413019F46F -:102940008CE891E002C08FE891E09F938F9382E939 -:1029500091E09F938F9309E314E01F930F930E94DC -:102960008037A80160E070E08CEA93E00E94C91013 -:102970008C81EF81F885E80FF11D80811F928F9384 -:1029800081EA91E09F938F931F930F930E9480376A -:102990008DB79EB70C960FB6F8949EBF0FBE8DBF35 -:1029A000A80161E070E08CEA93E0DF91CF911F9184 -:1029B0000F910C94C910CF93DF93EC014A815B8196 -:1029C00060E070E08CEA93E00E940611E091000460 -:1029D000F0910104F695E795E170EC83F0E0EE0FDD -:1029E000FF1FE95FFE4F81818F9380818F9388E87D -:1029F00091E09F938F9389E394E09F938F930E943C -:102A000080370F900F900F900F900F900F9049E329 -:102A100054E061E070E08CEA93E0DF91CF910C9498 -:102A2000C910CF93DF93EC014A815B8160E070E0D5 -:102A30008CEA93E00E940611E0910104E695E170B2 -:102A4000EC83F0E0EE0FFF1FE95FFE4F81818F9373 -:102A500080818F9388E891E09F938F9389E394E03E -:102A60009F938F930E9480370F900F900F900F903D -:102A70000F900F9049E354E061E070E08CEA93E03E -:102A8000DF91CF910C94C910CF93DF93EC014A8171 -:102A90005B8160E070E08CEA93E00E940611E091B7 -:102AA0000004F0910104F695E795F695E795E1703D -:102AB000EC83F0E0EE0FFF1FE95FFE4F81818F9303 -:102AC00080818F9388E891E09F938F9389E394E0CE -:102AD0009F938F930E9480370F900F900F900F90CD -:102AE0000F900F9049E354E061E070E08CEA93E0CE -:102AF000DF91CF910C94C910CF93DF93EC014A8101 -:102B00005B8160E070E08CEA93E00E940611E09146 -:102B10000004F091010453E0F695E7955A95E1F72A -:102B2000E170EC83F0E0EE0FFF1FE95FFE4F818163 -:102B30008F9380818F9388E891E09F938F9389E3AF -:102B400094E09F938F930E9480370F900F900F9087 -:102B50000F900F900F9049E354E061E070E08CEA31 -:102B600093E0DF91CF910C94C910CF93DF93EC01E8 -:102B70004A815B8160E070E08CEA93E00E9406117C -:102B8000E0910004F091010474E0F695E7957A95E0 -:102B9000E1F7E170EC83F0E0EE0FFF1FE95FFE4F1D -:102BA00081818F9380818F9388E891E09F938F93A9 -:102BB00089E394E09F938F930E9480370F900F904A -:102BC0000F900F900F900F9049E354E061E070E098 -:102BD0008CEA93E0DF91CF910C94C910FC01148230 -:102BE00041E151E060E070E08CEA93E00E94061160 -:102BF000409107015091080161E070E08CEA93E098 -:102C00000C94C910CF92DF92EF92FF920F931F9313 -:102C1000CF93DF93EC01C62ED42EF22E1E2D1092F0 -:102C20003904E11006C064E771E089E394E00E9492 -:102C300066376C2D70E080E090E042E050E00E944A -:102C40002708BC0189E394E00E94663766EA71E0D8 -:102C500089E394E00E946637113031F464E771E053 -:102C600089E394E00E9466376D2D70E080E090E08B -:102C700042E050E00E942708BC0189E394E00E94F2 -:102C8000663766EA71E089E394E00E9466371230A5 -:102C900031F464E771E089E394E00E946637BE0195 -:102CA00080E090E042E050E00E942708BC0189E308 -:102CB00094E00E94663768E372E089E394E00E9442 -:102CC0006637133031F464E771E089E394E00E94E1 -:102CD00066376F2D70E080E090E042E050E00E94A7 -:102CE0002708BC0189E394E00E94663768EA71E036 -:102CF00089E394E00E946637143031F464E771E0B0 -:102D000089E394E00E946637602F70E080E090E0F5 -:102D100042E050E00E942708BC0189E394E00E9451 -:102D2000663749E354E061E070E08CEA93E0DF91BC -:102D3000CF911F910F91FF90EF90DF90CF900C9467 -:102D4000C910EF920F93CF93DF93EC0143E952E068 -:102D500060E070E08CEA93E00E9406110E94272D4B -:102D60002093A6033093A7034093A8035093A9038D -:102D70006093AA037093AB03309398034093970337 -:102D8000822F90E090939A0380939903252F50937C -:102D90009603062F609395033C83E12C632F0E94DA -:102DA0000216DF91CF910F91EF900895EF920F935C -:102DB000CF93DF93EC018C818F5F8D3008F081E041 -:102DC0008C8361E070E08CEA93E00E94C905E12CFD -:102DD0000091950320919603409197036C81809117 -:102DE000990390919A030E940216DF91CF910F915F -:102DF000EF900895EF920F93FC0164816093980324 -:102E0000E12C0091950320919603409197038091C6 -:102E1000990390919A030E94021664EF71E080E09A -:102E200090E00E94B73285EB94E00F91EF90089507 -:102E3000EF920F93CF93DF93EC014BE852E060E009 -:102E400070E08CEA93E00E940611409197034C8356 -:102E5000EE24E394009195032091960360919803EA -:102E60008091990390919A030E940216DF91CF916D -:102E70000F91EF900895EF920F93CF93DF93EC01B2 -:102E80008C818F5F803208F081E08C8361E070E09C -:102E90008CEA93E00E94C905EE24E3940091950327 -:102EA000209196034C8160919803809199039091B1 -:102EB0009A030E940216DF91CF910F91EF9008952F -:102EC000EF920F93FC01448140939703EE24E39427 -:102ED0000091950320919603609198038091990346 -:102EE00090919A030E94021664EF71E080E090E0F6 -:102EF0000E94B73280EB94E00F91EF900895EF922B -:102F00000F93CF93DF93EC0142E852E060E070E072 -:102F10008CEA93E00E9406118091990391EF980F3B -:102F20009B3010F48C8308C08FE08C838FE090E09E -:102F300090939A03809399038C81B2E0EB2E0091D9 -:102F4000950320919603409197036091980390E038 -:102F50000E940216DF91CF910F91EF900895EF92AA -:102F60000F93FC0184818F5F8A3108F08FE08483A6 -:102F7000848102E0E02E0091950320919603409118 -:102F800097036091980390E00E9402160F91EF90D2 -:102F90000895EF920F93FC01848190E090939A033F -:102FA0008093990322E0E22E0091950320919603ED -:102FB00040919703609198030E94021664EF71E0BC -:102FC00080E090E00E94B7328BEA94E00F91EF909E -:102FD0000895EF920F93CF93DF93EC0149E752E00E -:102FE00060E070E08CEA93E00E9406112091960365 -:102FF0002C8333E0E32E0091950340919703609179 -:1030000098038091990390919A030E940216DF9190 -:10301000CF910F91EF900895EF920F93CF93DF939D -:10302000EC018C818F5F883110F48C8301C01C828D -:1030300061E070E08CEA93E00E94C90543E0E42E71 -:10304000009195032C8140919703609198038091A2 -:10305000990390919A030E940216DF91CF910F91EC -:10306000EF900895EF920F93FC0124812093960333 -:1030700053E0E52E009195034091970360919803EA -:103080008091990390919A030E94021664EF71E077 -:1030900080E090E00E94B73286EA94E00F91EF90D2 -:1030A0000895EF920F93CF93DF93EC014EE652E039 -:1030B00060E070E08CEA93E00E94061100919503B5 -:1030C0000C8364E0E62E2091960340919703609173 -:1030D00098038091990390919A030E940216DF91C0 -:1030E000CF910F91EF900895EF920F93CF93DF93CD -:1030F000EC018C818F5F8C3310F48C8301C01C82B7 -:1031000061E070E08CEA93E00E94C90574E0E72E6C -:103110000C812091960340919703609198038091D0 -:10312000990390919A030E940216DF91CF910F911B -:10313000EF900895CF92EF920F93CF93DF9300D04B -:1031400000D000D0CDB7DEB7FC0104810093950319 -:10315000E4E0EE2E2091960340919703609198034E -:103160008091990390919A030E94021660919903AD -:1031700070919A03C12CE0909503009196032091E1 -:10318000970340919803CE0101960E94112DCE0124 -:1031900001960E94BE2D64EF71E080E090E00E94F5 -:1031A000B73285EF94E026960FB6F894DEBF0FBED7 -:1031B000CDBFDF91CF910F91EF90CF900895FC019B -:1031C000148242E051E060E070E08CEA93E00E94FB -:1031D0000611409107015091080161E070E08CEA0E -:1031E00093E00C94C9101F93CF93DF93182FD62F21 -:1031F000C42F10923904411106C064E771E089E3DD -:1032000094E00E946637612F70E080E090E042E039 -:1032100050E00E942708BC0189E394E00E946637D1 -:1032200068EA71E089E394E00E946637C13031F4C6 -:1032300064E771E089E394E00E9466376D2F70E0E7 -:1032400080E090E042E050E00E942708BC0189E362 -:1032500094E00E94663749E354E061E070E08CEA54 -:1032600093E0DF91CF911F910C94C910CF93DF931E -:10327000EC0143E552E060E070E08CEA93E00E94EC -:103280000611EBE9F3E081818093960362816093FC -:1032900095038C8340E0DF91CF910C94F318FC01EF -:1032A00084818F5F883110F4848301C0148240E0F0 -:1032B0006091950384810C94F318FC0184818093C0 -:1032C000960340E0609195030E94F31864EF71E06B -:1032D00080E090E00E94B7328DE894E00895CF93AB -:1032E000DF93EC0147E352E060E070E08CEA93E0AA -:1032F0000E940611EBE9F3E08381809396036481D9 -:10330000609395038C8340E0DF91CF910C94F31888 -:10331000FC0184818F5F883110F4848301C01482A2 -:1033200040E06091950384810C94F318FC01848142 -:103330008093960340E0609195030E94F31864EF38 -:1033400071E080E090E00E94B73288E894E0089550 -:10335000CF93DF93EC0145E452E060E070E08CEA4B -:1033600093E00E940611609195036C8341E0809187 -:103370009603DF91CF910C94F318FC0184818F5F49 -:103380008C3310F4848301C0148241E06481809105 -:1033900096030C94F318CF93DF93FC016481609340 -:1033A000950341E0809196030E94F3188091950364 -:1033B0006091960360939C03CDE9D3E0888385E018 -:1033C00090E00E948839688186E090E00E94883908 -:1033D0000E94740864EF71E080E090E00E94B732D0 -:1033E00085EF94E0DF91CF910895CF93DF93EC01C7 -:1033F0004AE252E060E070E08CEA93E00E9406113D -:10340000609195036C8341E080919603DF91CF91A9 -:103410000C94F318FC0184818F5F8C3310F4848347 -:1034200001C0148241E06481809196030C94F318EA -:10343000CF93DF93FC0164816093950341E0809119 -:1034400096030E94F3188091950360919603609310 -:103450009E03CFE9D3E0888387E090E00E9488391B -:10346000688188E090E00E9488390E94740864EFC7 -:1034700071E080E090E00E94B73285EF94E0DF9148 -:10348000CF910895FC01148242E652E060E070E0C2 -:103490008CEA93E00E9406114091010150910201D3 -:1034A00061E070E08CEA93E00C94C910FC01238584 -:1034B00021110C94A6040895FC012081222349F0D7 -:1034C0000E94B007882329F060E08CEA93E00C9416 -:1034D000561A0895BF92CF92DF92EF92FF920F9308 -:1034E0001F93CF93DF9300D000D000D0CDB7DEB7CD -:1034F0008C01B62EFC01868586FD92C3D0900D040A -:103500008091000480FF03C082E0C82E02C0CC245A -:10351000C39480910E04D8123DC1B1103BC123E089 -:10352000D21621F08091020484FF40C0F801868504 -:1035300087FF3CC08F77F8018687609131047091D6 -:1035400032048091330490913404683EF3E07F07A5 -:103550008105910508F46EC228EE33E040E050E0AA -:103560000E94C736F22EE32E24E630E040E050E021 -:103570000E94C7363F932F93EF92FF9289EB91E021 -:103580009F938F9389E394E09F938F930E9480375A -:103590000FB6F894DEBF0FBECDBF29E334E040E0A4 -:1035A00050E06AE070E0C8010E94B0080E94272D38 -:1035B00029833A834B835C836D837E8333E0D3120C -:1035C0002DC04091250450912604609127047091EC -:1035D00028048091290490912A04A0912B04B09191 -:1035E0002C04481759076A077B0709F4C9C02091C2 -:1035F000310430913204409133045091340480916D -:103600000004C0908303D0908403E0908503F09081 -:10361000860380FF16C2A0EFB0E015C2FEEFDF12F6 -:10362000AFC046E050E066EA73E0CE0101960E942A -:103630005937892B09F4A4C040E060E0C8010E941A -:1036400021048BE993E00E945C1A80919B038823FC -:1036500019F066E671E002C06DE571E0C8010E94F4 -:103660009E098E811F928F938D811F928F938C81E3 -:103670001F928F9381ED91E09F938F93A9E3EA2EA0 -:10368000A4E0FA2EFF92EF920E948037970141E06A -:1036900050E060E070E0C8010E94B0080FB6F894F6 -:1036A000DEBF0FBECDBF80919B03882309F44CC0C1 -:1036B00040E069E0C8010E94210462E0C8010E9464 -:1036C000561A60E0C8010E94561A80919D031F920D -:1036D0008F9380919C031F928F93809145018F93CC -:1036E000809144018F93FF92EF920E94803797015F -:1036F00040E050E06BE070E0C8010E94B00841E09B -:1037000069E0C8010E94210461E0C8010E94561AC4 -:1037100060E0C8010E94561A80919F031F928F9308 -:1037200080919E031F928F93809145018F9380918A -:1037300044018F93FF92EF920E948037970141E0FE -:1037400050E06BE070E014C0809114041F928F93DE -:103750001F92CF9282E391E09F938F93FF92EF921B -:103760000E94803729E334E040E050E06AE070E0F6 -:10377000C8010E94B0080FB6F894DEBF0FBECDBFDF -:1037800086E0FE013196A6EAB3E001900D928A959B -:10379000E1F746C2809114041F928F931F92CF923B -:1037A00082E391E09F938F93F9E3EF2EF4E0FF2EF5 -:1037B000FF92EF920E9480370FB6F894DEBF0FBEE3 -:1037C000CDBF36E0D31220C061E0C8010E94A80737 -:1037D00092E0B91203C067EC71E002C068ED71E0DD -:1037E00042EA51E00DC08D1558F5F4E0DF12ABC090 -:1037F00061E0C8010E94A80749EB51E068ED71E063 -:10380000C8010E9485118ECE3D1508F4A6C083E044 -:10381000D812E9CF66E0C8010E94A807C8010E943B -:103820008B0740E060E0C8010E9421048BE993E02F -:103830000E945C1A6EE171E0C8010E949E0976CE7A -:1038400091E0D91213C062E0C8010E94A807C80124 -:103850000E948B0740E060E0C8010E9421048BE9D0 -:1038600093E00E945C1A67E271E015C0E2E0DE12AC -:1038700056CE63E0C8010E94A807C8010E948B07CA -:1038800040E060E0C8010E9421048BE993E00E94BF -:103890005C1A63E571E0C8010E949E0929E334E0E7 -:1038A00040E050E06AE070E0C8010E94B00860911A -:1038B000830370918403809185039091860320E1B6 -:1038C0003EE040E050E00E94C7365F934F933F9345 -:1038D0002F938AEA91E09F938F93FF92EF920E9439 -:1038E000803729E334E041E050E060E070E0C80157 -:1038F0000E94B008609187037091880380918903CA -:1039000090918A0328EE33E040E050E00E94C736F1 -:103910005F934F933F932F9381EB91E09F938F930E -:10392000FF92EF920E94803729E334E041E050E0BB -:1039300067E070E0C8010E94B0080FB6F894DEBFDF -:103940000FBECDBFEFCD25E0D212E9CD61E0C801B9 -:103950000E94A8074DEA51E051CF39E0D31209C0C7 -:1039600061E0C8010E94A8074DE951E063EE71E0F3 -:1039700047CF3D1520F187E0D8120FC061E0C801A4 -:103980000E94A807E2E0BE1203C067EC71E002C02B -:1039900068ED71E042E851E033CF98E0D912BFCD35 -:1039A00061E0C8010E94A807F2E0BF1203C067EC03 -:1039B00071E002C068ED71E046E751E021CFEEEF23 -:1039C000DE121BC065E0C8010E94A807C8010E9462 -:1039D0008B0740E060E0C8010E9421046DE571E0C2 -:1039E000C8010E949E0929E334E040E050E06AE00B -:1039F00070E0C8010E94B00895CDED1578F465E03F -:103A0000C8010E94A807C8010E948B0740E060E03F -:103A1000C8010E9421046EE671E0E2CFFAE0DF12F5 -:103A20007ECD61E0C8010E94A8074CE851E067EC38 -:103A300071E0E6CE61EC71E089E394E00E947137B9 -:103A4000ACCDA8E7B0E00E94FE3628EE33E040E0BF -:103A500050E00E94C7362C0D3D1D4E1D5F1D20936A -:103A600083033093840340938503509386036091CE -:103A7000830370918403809185039091860320E1F4 -:103A80003EE040E050E00E94C7365F934F933F9383 -:103A90002F938AEA91E09F938F93B9E3EB2EB4E0E2 -:103AA000FB2EFF92EF920E948037970141E050E099 -:103AB00060E070E0C8010E94B008609187037091D7 -:103AC00088038091890390918A0328EE33E040E0D7 -:103AD00050E00E94C7365F934F933F932F9381EB43 -:103AE00091E09F938F93FF92EF920E94803797010E -:103AF00041E050E067E070E0C8010E94B0088091AA -:103B00008B030FB6F894DEBF0FBECDBF80FF83C01E -:103B100061E070E08CEA93E00E94C90580918F0318 -:103B2000909190030097E9F06AE070E00E94B3364C -:103B30009F938F937F936F9388EC91E09F938F93E4 -:103B4000FF92EF920E948037970141E050E060E0E1 -:103B500070E0C8010E94B0080FB6F894DEBF0FBE37 -:103B6000CDBF8091910390919203009701F16AE09B -:103B700070E00E94B3369F938F937F936F9388EC8E -:103B800091E09F938F9389E394E09F938F930E949A -:103B9000803729E334E041E050E065E070E0C8019F -:103BA0000E94B0080FB6F894DEBF0FBECDBF809163 -:103BB000930390919403009701F16AE070E00E94F2 -:103BC000B3369F938F937F936F9388EC91E09F938D -:103BD0008F9389E394E09F938F930E94803729E32A -:103BE00034E041E050E06BE070E0C8010E94B008B2 -:103BF0000FB6F894DEBF0FBECDBF80918B03982F18 -:103C00009670963029F48B7F80938B0361E004C01B -:103C1000846080938B0366E0C8010E94A807B0CD42 -:103C200026960FB6F894DEBF0FBECDBFDF91CF91C1 -:103C30001F910F91FF90EF90DF90CF90BF9008956C -:103C40000F931F93CF93DF93EC018C01005C1F4F08 -:103C5000F80180818F3FF9F062E070E0CE010E94B0 -:103C60005D0A8FEFF8018083CE010E94EA0A61E0CD -:103C70008CEA93E00E946A1ACF5BDF4FF8019081D3 -:103C80008881981303C08FEF888305C08CEF94E080 -:103C90000E943628F8CFDF91CF911F910F910895A0 -:103CA000EF92FF920F931F93CF93DF93EC018C0160 -:103CB000005C1F4FF80180818E3F41F160E070E0B1 -:103CC000CE010E945D0A8EEFF801808361E08CEAEC -:103CD00093E00E946A1A7E01F1E4EF0EF11CF801F4 -:103CE0009081F701808198130CC08EEFF7018083DB -:103CF0000E948832C45BDF4F688379838A839B8309 -:103D000005C08CEF94E00E943628EFCFDF91CF9171 -:103D10001F910F91FF90EF900895CF92DF92EF9255 -:103D2000FF92CF93DF93EC010E9488326B017C01FC -:103D30008F819885A985BA85A7019601281B390B23 -:103D40004A0B5B0BDA01C901893E9340A105B1051D -:103D500008F45FC08881882309F43FC0CE010E9427 -:103D6000B0078823D1F10E94272DE52F2093A603C9 -:103D70003093A7034093A8035093A9036093AA0329 -:103D80007093AB035D838091AA038E8399814A81EE -:103D900050E06CE3969F400D511D11249B812C81B6 -:103DA00030E06CE3969F200D311D112490E06CE310 -:103DB000E69F800D911D112424173507D0F4841738 -:103DC000950798F02817390768F480910D048E3F05 -:103DD00021F48DEC93E00E94600CCF82D886E986B6 -:103DE000FA8617C04817590738F08217930738F436 -:103DF000ECCF84179507C8F78217930770F32F5FEE -:103E00003F4F2817390748F38DEC93E00E94501E6E -:103E1000E4CFDF91CF91FF90EF90DF90CF900895A6 -:103E2000DF92EF92FF920F931F93CF93DF938C015A -:103E3000D42EFC0183A994A9FC01E170FF2780FD29 -:103E400002C080E101C080E5E801C95BDF4F663058 -:103E500030F0861750F06883E12CF12C0AC086E020 -:103E60008883EE24E394F12C04C0888392E0E92E49 -:103E7000F12C21110EC06881EF2B11F022E001C05E -:103E800021E081E090E0213011F480E090E00E9498 -:103E90008839F80180819181019729F4688170E067 -:103EA000C8010E94860ADD2029F061E08CEA93E0D7 -:103EB0000E946A1AC701DF91CF911F910F91FF9065 -:103EC000EF90DF900895CF93DF93EC0124E731E08A -:103ED00041E050E060E070E08CEA93E00E94B008BE -:103EE0008C81EF81F885E80FF11D60818091B70327 -:103EF000882339F070E04AE050E08CEA93E00E94B9 -:103F0000673666EC71E08CEA93E00E94C30564EFCB -:103F100071E080E090E00E94B7328C81EF81F885FB -:103F2000E80FF11D60818091000480FF02C022E053 -:103F300001C021E081E090E0213011F480E090E0C8 -:103F40000E9488398C81EF81F885E80FF11D20E00F -:103F500040E060818DEC93E00E94101F85EF94E0BB -:103F6000DF91CF910895EF92FF920F931F93CF931C -:103F7000DF931F92CDB7DEB77C01162F042FFC0113 -:103F800083A994A9623021F4816094AB83AB05C00E -:103F90008E7FF70194AB83AB11E00E946C0881E047 -:103FA00090E0113011F480E090E00E9474399FEFAE -:103FB000980F9E3F20F0113049F58CE004C0863008 -:103FC00060F0113029F4682F813140F060E106C0C3 -:103FD000682F813518F060E501C066E0C70169838C -:103FE0000E946B0C20E040E06981C7010E94101F15 -:103FF000002371F061E08CEA93E00F90DF91CF91A4 -:104000001F910F91FF90EF900C946A1A80E1D9CF25 -:104010000F90DF91CF911F910F91FF90EF90089536 -:10402000CF93DF93EC016C81611106C020910004F5 -:10403000309101042F7D0AC040E08DEC93E00E9496 -:10404000B31F2091000430910104206230930104D9 -:10405000209300040E946C0824E731E041E050E026 -:1040600060E070E08CEA93E00E94B008EC81F0E040 -:10407000EE0FFF1FE45DFE4F608171818CEA93E0DB -:104080000E94C3050E946C0864EF71E080E090E03C -:104090000E94B73285EF94E0DF91CF910895EF92BF -:1040A000FF921F93CF93DF931F92CDB7DEB77C01B2 -:1040B000A89569830E94320EC7010E94BA0E6981D9 -:1040C000882331F1D7011C968D919C911D97009703 -:1040D00069F0DC01ED91FC910280F381E02D0F90FD -:1040E000DF91CF911F91FF90EF90099461119CC0D7 -:1040F00080910D048E5F823008F08CC08DEC93E0CF -:104100000F90DF91CF911F91FF90EF900C94600C76 -:10411000C7010E94CA0E882309F486C0F70184856E -:104120009585009709F457C0DC01ED91FC9104805E -:10413000F581E02D0995F70195878487009709F1AE -:10414000F4E0853F9F0711F41091F904DC01ED9133 -:10415000FC910190F081E02D0995F701848595850A -:10416000855F944009F060C011501093F90485EF09 -:1041700094E00F90DF91CF911F91FF90EF900C94FE -:10418000AD1380919B03811104C08DEC93E00E94DC -:10419000600C9091BA03892F8F7B8093BA03D7016B -:1041A0001E962C91213021F49E7B9093BA0303C07C -:1041B00081608093BA0361E08CEA93E00E946A1AFE -:1041C00061E08CEA93E00F90DF91CF911F91FF9017 -:1041D000EF900C946A1A8DEC93E00E94501E80912F -:1041E000BA03982F90649093BA0380958170F70179 -:1041F000868767E08CEA93E00E94A80785EF94E049 -:104200000E94971385EF94E0D7011D969C938E939F -:104210001C970AC08DEC93E00F90DF91CF911F9116 -:10422000FF90EF900C94501E0F90DF91CF911F9153 -:10423000FF90EF900895CF93C62F60E08CEF94E04D -:104240000E94282C6C2F89E794E0CF910C944F208A -:10425000EC0181E08093FB0462E08CEA93E00E9431 -:104260006A1A61E0CE010E941B21FBCF2F923F9280 -:104270004F925F926F927F928F929F92AF92BF9276 -:10428000CF92DF92EF92FF920F931F93CF93DF9322 -:1042900000D000D01F92CDB7DEB78C010E948832CB -:1042A0006B017C01580120E4A20EB11CD5017C9069 -:1042B000BFEF7B1207C065E070E080E090E00E94F5 -:1042C000B73210C4EEEF7E122EC0D801D5968C9175 -:1042D00086FF08C424E630E0AE014F5F5F4FBE01A9 -:1042E0006D5F7F4FC8010E94BB0C29813A81F801A4 -:1042F00080AD91AD2817390790F4E45BFF4F8081C2 -:104300009181A281B381C81AD90AEA0AFB0AF8EBA3 -:10431000CF16FBE0DF06E104F10408F4E3C3C801B3 -:104320000E94EA0ADFC3C8010E94750C982ED801CA -:10433000D5968C9186FF2CC0F801E85BFF4F408139 -:1043400051816281738116012701241A350A460AB8 -:10435000570A29EE221623E032064104510408F4DC -:104360009DC0D801D3968C9183FD05C0892D8370A3 -:10437000833009F411C36624639420E0F80185A911 -:1043800080FF8FC0D5018C91833009F08AC021C392 -:10439000F7E07F122AC0F80181A592A5A3A5B4A5D4 -:1043A00086309105A105B105D0F445A156A167A1BC -:1043B00070A516012701241A350A460A570A21EE6C -:1043C000221623E9320624E04206510440F0019609 -:1043D000A11DB11D81A792A7A3A7B4A75FC087E0C5 -:1043E000D5018C93612C27E05CC0F80183A984FD82 -:1043F00055C0892D8370833009F44CC0F8E07F16D6 -:1044000069F0F80186A597A5A0A9B1A9892B8A2BE7 -:104410008B2B21F4C6A6D7A6E0AAF1AAF801E45B8B -:10442000FF4F80819181A281B38116012701281A53 -:10443000390A4A0A5B0A29EE221623E032064104B1 -:10444000510490F0F80186A597A5A0A9B1A916017D -:104450002701281A390A4A0A5B0A29EE221623E0A4 -:1044600032064104510420F438E0731217C00CC026 -:1044700048E0741649F062A96E3F30F46F5F62AB9A -:1044800083E190E00E94883988E0F5018083612C07 -:1044900028E007C016A617A610AA11AA66246394DE -:1044A00020E0F8018581882309F445C086E0D50124 -:1044B0008C937816A9F063896E3F30F46F5F638B3D -:1044C00081E190E00E948839F8011786108A118AEC -:1044D000128AD701C60180529C46AB4FBF4F25C000 -:1044E000F80183859485A585B685C816D906EA06A0 -:1044F000FB06F8F087859089A189B2890196A11DF4 -:10450000B11D8787908BA18BB28B0797A105B10551 -:1045100008F039C1C80102960E94E709D701C60117 -:1045200080529C46AB4FBF4FF80183879487A58785 -:10453000B687612C26E08091930390919403A801A3 -:104540004E5B5F4F4A018E3E924074F480918F0320 -:1045500090919003843492403CF480919103909127 -:1045600092038434924034F08AE0F5018083612C18 -:104570002AE06DC0662009F46AC08AEF870D833097 -:1045800018F0FAE07F1208C060E070E0C8010E94F5 -:104590005D0AD5011C92712C24E630E0AE014F5F1C -:1045A0005F4FBE016D5F7F4FC8010E94BB0CF801D9 -:1045B00043A941FD0DC080819181019749F42B8170 -:1045C0003C8186AD97AD2817390710F025E013C060 -:1045D00089819A81F80126A937A98217930708F0E3 -:1045E00018C2D801D8962D913C91D997821793077C -:1045F00008F4FDC122E0271541F1F4018081F801A2 -:10460000ED5BFF4F281729F0C082D182E282F3824E -:104610001CC080819181A281B381B701A601481B92 -:10462000590B6A0B7B0B213029F489E190E0A0E063 -:10463000B0E004C08AEF90E0A0E0B0E0481759076E -:104640006A077B0710F0D5012C9366246394F4016C -:104650002083D5018C91871509F4B2C0813089F58A -:10466000C8010E94EA0A60E070E0C8010E945D0A89 -:1046700060918303709184038091850390918603F8 -:1046800020E13EE040E050E00E94C736BA01A901B7 -:104690008091870390918803A0918903B0918A0348 -:1046A000480F591F6A1F7B1F40938703509388034D -:1046B0006093890370938A038DE090E00E9496399D -:1046C0007FC08230B9F4C8010E94EA0AF801E95BB0 -:1046D000FF4F608170E0C8010E94860AB1E07B1242 -:1046E0006FC010928303109284031092850310927E -:1046F00086037EC08330C1F4F801E95BFF4F60811F -:1047000070E0C8010E94860AF80183A994A991FD6E -:1047100089C1C80102960E94170A882309F482C140 -:1047200089E0D5018C932FC0843031F4C8010E94F8 -:10473000EA0A60E070E007C0863049F4C8010E94D0 -:10474000EA0A62E070E0C8010E945D0A39C08A305E -:10475000E9F460E070E0C8010E945D0A0E948832BE -:104760006C197D098E099F0968387341810591058F -:1047700010F4A895F3CFC8010E94EA0A62E070E045 -:10478000C8010E945D0AC8010E942821853061F499 -:10479000C8010E94EA0AF801E95BFF4F608170E0FE -:1047A000C8010E94860A0CC0873041F2883019F394 -:1047B00060E070E0C8010E945D0AC8010E94EA0A38 -:1047C000611016C053E0252E721012C0F801E85B8C -:1047D000FF4F80819181A281B381C81AD90AEA0A68 -:1047E000FB0A21EDC21627E0D206E104F10458F2DB -:1047F000F801EF5BFF4F7082D5018C91833031F46B -:10480000B996808191811816190624F0F80185A9BE -:1048100084FF32C0C8010E94FA0CF801E05AFF4F31 -:1048200060817181828193810E94EE0D9B01AC01B8 -:104830006F3FEFEF7E078E079E07F1F0F801E65914 -:10484000FF4FA081B1810E9409373297C080D1808B -:10485000EE24D7FCE094FE2C6C197D098E099F098B -:10486000349797FDEEC06083718382839383809138 -:10487000BA0380688093BA03D5018C91833009F024 -:1048800031C16801B8E5CB0ED11CF6018081918160 -:10489000A281B3817801FCE5EF0EF11CF701808362 -:1048A0009183A283B3830E946A2EF801E05BFF4FDD -:1048B00080809180A280B380681979098A099B0958 -:1048C000D6016D937D938D939C931397F701C080D0 -:1048D000D180E280F3806C157D058E059F0509F47B -:1048E00001C18BE893E00E945B1020918B0320FDB7 -:1048F00018C080919303909194038A3892400CF0F1 -:10490000A5C080918F0390919003883092400CF065 -:104910009DC08091910390919203883092400CF059 -:1049200095C020918B0320FF16C080919303909136 -:104930009403893592407CF480918F039091900389 -:104940008B3E914044F480919103909192038B3E71 -:1049500091400CF487C020918B0321FD18C08091F9 -:104960009303909194038C3B92400CF086C080910D -:104970008F0390919003863292400CF07EC080911C -:10498000910390919203863292400CF076C0209170 -:104990008B0321FD7FC0A6C087E0D5018C93C801A1 -:1049A0000E94EA0AB7E07B1651F0F80165A56E3F58 -:1049B00030F46F5F65A782E190E00E948839D801EA -:1049C0009596CD92DD92ED92FC929897612C27E01E -:1049D000D5CC911007C041E062E0C8012D830E9450 -:1049E000B31F2D81F80185A98E7F85AB5ACDDA964C -:1049F0002D913C91DB978217930748F4DC962D911B -:104A00003C91DD9742FF07C02817390720F423E0C7 -:104A1000F2CD21E0F0CD2817390710F020E0EBCDE2 -:104A200024E0E9CDB1E07B1208C01092830310921C -:104A300084031092850310928603C8010E94C50A60 -:104A4000BFCE108211821282138211CF2160209377 -:104A50008B0360918E03669521E040E0C8010E94BF -:104A6000101F5FCF2E7F20938B0321E040E06091E9 -:104A70008E03C8010E94101F6ECF226020938B030B -:104A800060918E036695669521E040E0C8010E9422 -:104A9000101F7DCF80919303909194038B389240A7 -:104AA0000CF580918F039091900389309240D4F45B -:104AB0008091910390919203893092409CF42D7FD4 -:104AC00020938B0360918E03669521E040E0C8013E -:104AD0000E94101FF801E95BFF4F608170E0C80180 -:104AE0000E94860A0F900F900F900F900F90DF9109 -:104AF000CF911F910F91FF90EF90DF90CF90BF90DB -:104B0000AF909F908F907F906F905F904F903F906D -:104B10002F900895CF93DF93EC0181E0888361E0CB -:104B200084E090E00E948839E0E0F4E02081318167 -:104B30002064318320830E946C080E947408CE0197 -:104B40000E948D1E61E08CEA93E0DF91CF910C947E -:104B50006A1AFC01108260E084E090E00E948839CB -:104B6000E0E0F4E0808191818F7B918380830E94DB -:104B70006C080E94740861E08CEA93E00C946A1A55 -:104B8000CF93DF93EC0124E731E041E050E060E0B7 -:104B900070E08CEA93E00E94B008EC81F0E0EE0F48 -:104BA000FF1FE95FFE4F608171818CEA93E00E94F4 -:104BB000C30564EF71E080E090E00E94B7328C8121 -:104BC000811105C08BE993E00E948A2504C08BE91E -:104BD00093E00E94A92585EF94E0DF91CF9108959D -:104BE000A8958DEC93E00E94362160E08CEA93E07A -:104BF0000E946A1A60E08DEC93E00E941B218BE911 -:104C000093E00C948D1E1F93CF93DF93EC01162F2E -:104C10008091BA03613011F48E7F01C0816080936E -:104C2000BA03642F8CEA93E00E946A1A8BA99CA9AC -:104C3000111102C0916001C09E7F9CAB8BAB0E94A2 -:104C40006C0860EE71E080E196E00E94BB356BA9D4 -:104C50007CA940E150E080E196E00E947C3680E053 -:104C600090E0DF91CF911F910895CF93DF93EC01F6 -:104C700024E731E041E050E060E070E08CEA93E04E -:104C80000E94B008EC81F0E0EE0FFF1FE85DFE4FE0 -:104C9000608171818CEA93E00E94C30561E08C81A0 -:104CA000811160E041E08DEC93E00E94032681E0F9 -:104CB0009C81911180E08093870485EF94E0DF91DF -:104CC000CF910895CF92DF92EF92FF921F93CF93EF -:104CD000DF93EC0182E090E00E9482397C018F3FFB -:104CE0002FEF920761F090FF0AC08091BA038160B4 -:104CF0008093BA0361E08CEA93E00E946A1A22E092 -:104D000030E040E063E270E0CE0146960E942A0364 -:104D100022E030E047E069E270E0CE0149960E946F -:104D20002A0322E030E041E063E270E0CE014C96DD -:104D30000E942A0321E030E043E069E270E0CE0106 -:104D40004F960E942A0321E030E044E069E270E0DF -:104D5000CE0182960E942A03CE0102960E94F80993 -:104D6000CE010E94EA0ACE010E94720A8FEFE81675 -:104D7000F80619F41CAA1BAA04C0FCAAEBAAE0FEC0 -:104D800002C012E001C011E08BE090E00E94823985 -:104D90006C017E01A8E6EA0EF11CF7019183808385 -:104DA00089E090E00E948239FE01E659FF4F91832D -:104DB0008083CD20C09419F4D7011D921C920196D6 -:104DC00021F48CED90E091838083FE01E05AFF4F47 -:104DD0001082118212821382349610821182128202 -:104DE00013821DAA1F86188A198A1A8A81E190E007 -:104DF0000E9474398B8B8F3F09F41B8A19A61AA65F -:104E00001BA61CA682E190E00E9474398F3F11F42A -:104E10008DA701C01DA61EA61FA618AA19AA83E168 -:104E200090E00E9474398F3F11F48AAB01C01AAA36 -:104E30001DA21EA21FA218A67E01E0E4EE0EF11C28 -:104E4000D7011C92FE01EF5BFF4F1082CE010E9442 -:104E50009A1181E08093FB04CE010E9409122BA9D4 -:104E60003CA925FD05C09FEF980F923008F4182F3C -:104E700023FD02C08330E9F190E024FD02C08430BC -:104E8000E9F131FD02C08530F1F19923A1F00E94D2 -:104E900088326B017C010E9488326C197D098E0971 -:104EA0009F09603E73498440910588F761E0CE0117 -:104EB0000E941B21F0CF1092FB0440E0612FCE0135 -:104EC0000E94B31F8BA986FD0AC0CE01DF91CF914E -:104ED0001F91FF90EF90DF90CF900C94600CCE016B -:104EE000DF91CF911F91FF90EF90DF90CF900C94C6 -:104EF000501E97E0F701908391E0BFCF98E0D70173 -:104F00009C9391E0BECF89E0F7018083C0CF83E41A -:104F100095E00E94762F0E94252D8BE993E00E9458 -:104F2000050F8CEA93E00E9427118DEC93E00C941E -:104F3000622688E10FB6F8948093600010926000BA -:104F40000FBE68EC70E080E090E00E94B73226E08F -:104F500040E052EC61E070E080E196E00E9466344F -:104F600089E794E00E942B0E0E9487278BE893E04C -:104F70000E9487082FE088E190E00FB6F894A8958A -:104F8000809360000FBE209360008DE090E00E944F -:104F90007C396F3F7F4F8F4F9F4F39F440E050E097 -:104FA000BA018DE090E00E9496398DE090E00E9479 -:104FB0007C3960938703709388038093890390936F -:104FC0008A030895BC01009799F0FC01019000202C -:104FD000E9F7AF0141505109481B590BE091100608 -:104FE000F09111060280F381E02D80E196E00994B2 -:104FF0000895FC01908180ED890F8A3040F08FEB9D -:10500000890F863018F489EC890F01C080E0829501 -:10501000807F918120ED290F2A3038F02FEB290F66 -:10502000263020F49753890F0895820F0895CF9367 -:10503000FC0120E030E0A901C191CC2399F0AAE065 -:10504000B0E00E94FE362C2F332727FD30952053E9 -:105050003109442737FD4095542F260F371F481F2D -:10506000591FEACFCA01B901CF9108951F928DE06F -:105070008F9380910D041F928F931F9284E28F93E0 -:1050800086E193E09F938F9389E394E09F938F93BE -:105090000E9480378DB79EB70A960FB6F8949EBFD0 -:1050A0000FBE8DBF89E394E00C94E2271F928DE040 -:1050B0008F931F926F931F9284E28F9382E293E00B -:1050C0009F938F9389E394E09F938F930E948037FF -:1050D0008DB79EB70A960FB6F8949EBF0FBE8DBFD0 -:1050E00089E394E00C94E2270F931F93CF93DF930F -:1050F000FC01019690A3878F81E083AB818104E25C -:10510000080F14E21827DF0112963A2F2B2F8D91EA -:10511000882321F1803281F4005E1827ED01219768 -:105120001882C3A981E08C0F83ABCC0FDD0BCE0FAF -:10513000DF1FB8A3AF8FE9CF8A3221F08E3581F41B -:10514000C2E001C0C1E0832F922FFC011192CF0178 -:105150000E94F927C13039F4801307C080E006C0EF -:10516000080F1827D2CF8117C9F381E090E0DF91B3 -:10517000CF911F910F910895109230051092FC0469 -:1051800010921A0508951F93CF93DF93EC01162F09 -:1051900064E280E196E00E940134112319F08EE26E -:1051A00093E002C082E393E00E94E2278E8D882381 -:1051B00019F0CE010E94E2276DE080E196E00E94A6 -:1051C00001348CA9882341F06AE080E196E0DF9108 -:1051D000CF911F910C940134DF91CF911F910895CD -:1051E000AF92BF92CF92DF92EF92FF920F931F93F5 -:1051F000CF93DF9300D0CDB7DEB75C01DC015E96C4 -:105200001C925E975F96ED91FC9190978081873418 -:1052100009F41FC2833509F46EC0863409F001C356 -:1052200081818534B1F154F4823419F1843409F068 -:10523000F8C28DEC93E00E94201EEFC2823509F483 -:1052400050C0833509F452C0803509F0EAC2D50157 -:1052500091968D919C9192970E9417286B017C01E9 -:10526000F50183A194A10E94172824E030E010E208 -:1052700015C0F50181A192A10E9417288091BA035F -:1052800080FF02C0611167E08CEA93E00E948D0309 -:10529000C4C28DEC93E00E94600CBFC2F9012F5F85 -:1052A0003F4FD501D3964C91D397552747FD509545 -:1052B000E417F5075CF4F901EE0FFF1FEA0DFB1D83 -:1052C000058CF68DE02D31971083E8CF95962D91C2 -:1052D0003C919697AB01B6018CEA93E00E94B0082E -:1052E0009CC28DEC93E00E94411397C28DEC93E039 -:1052F0000E94501E92C28181992787FD9095A92F07 -:10530000B92FFC01F097E732F10508F08AC2EC5C96 -:10531000FF4F0C94E936F50183A9823009F081C270 -:1053200001A0F2A1E02D61E08081803309F460E00A -:1053300041E08DEC93E00E9403267DC0D501D39619 -:105340008C91D397873009F06CC29B968D919C917C -:105350009C970E941728E62EF50181A592A50E9430 -:105360001728062FD50197968D919C9198970E94AA -:105370001728262FF50185A196A12A830E941728B8 -:10538000462FD50193968D919C91949749830E94C5 -:105390001728162FF50181A192A10E941728862FA8 -:1053A0002A814981612F0E94CB0737C2D501D3964C -:1053B0008C91D397823009F034C29196ED91FC9193 -:1053C0009297908180910204913309F023C2806109 -:1053D00022C2F50183A9833009F023C281A192A1E1 -:1053E0000E9411379093380480933704BC0189E000 -:1053F00090E00E949E39D50193968D919C9194974F -:105400000E9411379093360480933504BC018BE0E1 -:1054100090E00E949E3901C2F50183A9823009F013 -:1054200000C281A192A10E94172820E041E08DECEA -:1054300093E00E94101F8C01009709F0F4C1EDC1A8 -:10544000D501D3968C91D397823009F0EAC1919619 -:10545000ED91FC91929761E08081803309F460E0E6 -:105460008DEC93E00E94340BD8C1F50183A9823002 -:1054700009F0D7C101A0F2A1E02D81E09081903325 -:1054800009F480E0D501D4968C93C7C1F50183A9B6 -:10549000823009F0C6C101A0F2A1E02D61E0808157 -:1054A000803309F460E08DEC93E00E946C0BB5C191 -:1054B000F50183A9823009F0B4C101A0F2A1E02D69 -:1054C00061E08081803309F460E08DEC93E00E941C -:1054D000DC0BA3C1D50191968D919C9192970E946E -:1054E0001728AB01BC014093870350938803609356 -:1054F000890370938A038DE090E00E9496398DC1F4 -:10550000F50183A9823009F08CC101A0F2A1E02D40 -:10551000608161330CF485C163332CF0613409F090 -:1055200080C161E007C0605341E08DEC93E00E94D0 -:10553000B31F60E08DEC93E00E94540C6EC1D50166 -:10554000D3968C91D397823009F06BC19196ED91EF -:10555000FC91929761E08081803309F460E08DECEA -:1055600093E00E941C0C59C1D501D3968C91D3971E -:10557000853009F056C191960D911C919297F801D2 -:10558000208193968D919C9194972033C1F4DC01F6 -:105590002C912033A1F4D5019596ED91FC9196972D -:1055A0002081203361F49796ED91FC9198972081AA -:1055B000203329F48BE993E00E94A9252EC10E9493 -:1055C0001728F62EC8010E94172860939C03F092BA -:1055D0009D0385E090E00E94883960919D0386E0FC -:1055E00090E00E9488390E947408F50187A190A577 -:1055F0000E941728162FD50195968D919C9196970C -:105600000E94172860939E0310939F0387E090E009 -:105610000E94883960919F0388E090E00E94883959 -:105620000E9474088BE993E00E948A25F6C0F50178 -:1056300083A9823009F0F5C001A0F2A1E02D61E05C -:105640008081803309F460E08DEC93E00E94A40B2C -:10565000E4C08181992787FD9095A92FB92FFC017E -:10566000E154F109E631F10508F0DBC0E55AFF4FDE -:105670000C94E936809136048F93809135048F9392 -:10568000809138048F93809137040BC0809100047F -:1056900080FF02C080E501C080E11F928F931F92BE -:1056A00086E08F9382E891E00CC0809101048F9393 -:1056B000809100048F93809114041F928F9386E34E -:1056C00093E09F938F93BF92AF920E9480375EC00A -:1056D00081E190E00E947439182F82E190E00E94ED -:1056E0007439082F83E190E00E9474391F928F93E0 -:1056F0001F920F931F921F938EE393E041C08FEF91 -:105700008F938F938F938F93809134048F93809195 -:1057100033048F93809132048F93809131048F935F -:1057200087E493E09F938F93BF92AF920E9480375C -:105730002DC0809194038F93809193038F938091D8 -:1057400090038F9380918F038F93809192038F9317 -:10575000809191038F938FE791E012C0809128048C -:105760008F93809127048F93809126048F9380914B -:1057700025048F9380910D041F928F938FE493E003 -:105780009F938F93BF92AF920E94803781E0D501A3 -:105790005E968C9327C0C5010E94F00736C08091A9 -:1057A0008A038F93809189038F93809188038F93CD -:1057B000809187038F93809186038F9380918503D7 -:1057C0008F93809184038F93809183038F9386E5D9 -:1057D00093E09F938F93BF92AF920E94803781E0B6 -:1057E000F501868F0FB6F894DEBF0FBECDBF15C092 -:1057F000C5010E94C70768E372E0C5010E946637D1 -:1058000061E373E0C5010E94383781E0D5015E96FF -:105810008C9303C08F7E8093020400E010E061E06F -:1058200003C00FEF1FEF60E0C5010E94C328F50120 -:105830001082168EC8010F900F90DF91CF911F91AB -:105840000F91FF90EF90DF90CF90BF90AF900895B1 -:105850008F929F92BF92CF92DF92EF92FF920F931F -:105860001F93CF93DF931F92CDB7DEB74C01662312 -:1058700031F090910D0480910E0498135BC080E18B -:1058800096E00E9483336C01009709F450C0E12C2C -:10589000F12C01E010E0BB24B394EC14FD040CF0F7 -:1058A0004DC080E196E00E949C33482FF40184A90A -:1058B000882339F0642F80E196E049830E94013407 -:1058C0004981F401443219F44083B68E2CC08081A2 -:1058D000843249F5268D2E311CF581E0820F332765 -:1058E00027FD30954D30B1F4868FE20FF31F108203 -:1058F000C4010E947428892B29F4C4010E94F02855 -:105900008C0111C0F4011082168E60E0C4010E9467 -:10591000C32809C0F401868FE20FF31F408303C040 -:10592000F4011082168EFFEFEF1AFF0AB6CF01E0E6 -:1059300010E004C0C4010E943628A1CFC8010F9016 -:10594000DF91CF911F910F91FF90EF90DF90CF905B -:10595000BF909F908F900895DC01ED91FC91019094 -:10596000F081E02D0994CF92DF92EF92FF92CF93D6 -:10597000DF93EC01405853446D467843ECE3CE2E60 -:10598000D12CE12CF12CCB01BA01A70196010E9488 -:10599000C7366D83CA01B901A70196010E94C736B7 -:1059A0006C83CA01B90128E130E040E050E00E9478 -:1059B000C7366B83188243E95EEFE8818E2F8370D0 -:1059C000A1E009F0A0E08A2F90E0BC0163597E4F6E -:1059D0002617370740F0BA01681B790B260F371FCF -:1059E000EF5FE883EACF81E089838981E82FF0E0E7 -:1059F000EA5CFC4FE491AA2319F0823009F4EF5FCE -:105A0000F0E02E173F0720F02E1B3F0B8F5FECCFEF -:105A10002F5F2A83DF91CF91FF90EF90DF90CF909F -:105A20000895CF92EF920F93FC01603D87E07807D5 -:105A300010F0605D77406083418322830383E482BA -:105A4000C5820F91EF90CF90089581E008951F9344 -:105A5000CF93DF93CDB7DEB72B970FB6F894DEBFA9 -:105A60000FBECDBF68E670E083E495E00E949D2FF5 -:105A7000609131057091320583E495E00E94AC2C71 -:105A800083E495E00E94B22F47E050E068E670E0C2 -:105A900083E495E00E94902F83E495E00E94E72E36 -:105AA0008F77982F92959F701AEF782F919F700D96 -:105AB000112483E495E07B870E94E72E9C01332725 -:105AC000F4E035952795FA95E1F7682F129F600D60 -:105AD000112483E495E06A870E94E72E9C01332716 -:105AE000A4E035952795AA95E1F7582F129F500D00 -:105AF000112483E495E059870E94E72E83E495E022 -:105B00000E94E72E9C013327B4E035952795BA957E -:105B1000E1F7482F129F400D112483E495E0488758 -:105B20000E94E72E9C01332794E0359527959A959E -:105B3000E1F7382F129F300D112483E495E03F8365 -:105B40000E94E72EFC01FF2724E0F595E7952A95B2 -:105B5000E1F7282F1E9F200D11243F81488559858C -:105B60006A857B8580E090E02B960FB6F894DEBFC7 -:105B70000FBECDBFDF91CF911F9108951F93CF939B -:105B8000DF93EC0168E670E083E495E00E949D2FCE -:105B9000609131057091320583E495E00E94AC2C50 -:105BA0002D811AE0822F612F0E949336682F660F95 -:105BB000680F660F620F83E495E00E94372F2C81F7 -:105BC000822F612F0E949336682F660F680F660F31 -:105BD000620F83E495E00E94372F2B81822F612F83 -:105BE0000E949336682F660F680F660F620F83E47A -:105BF00095E00E94372F60E083E495E00E94372F04 -:105C00002A81822F612F0E949336682F660F680FBA -:105C1000660F620F83E495E00E94372F2981822F5F -:105C2000612F0E949336682F660F680F660F620F10 -:105C300083E495E00E94372F2881822F612F0E94F4 -:105C40009336682F660F680F660F620F83E495E046 -:105C50000E94372F609131057091320583E495E001 -:105C60000E94AC2C83E495E0DF91CF911F910C94BE -:105C7000B22F0F931F9360933F057093400580935D -:105C800041059093420500916001109161012091BE -:105C9000620130916301060F171F281F391F0093FF -:105CA0003705109338052093390530933A0582E083 -:105CB00090E090933605809335050E94883260937A -:105CC0003B0570933C0580933D0590933E051F91E5 -:105CD0000F910895CF92DF92EF92FF920F931F934F -:105CE0000E948832C0903B05D0903C05E0903D0575 -:105CF000F0903E056C197D098E099F0900913F05C2 -:105D0000109140052091410530914205683E734055 -:105D100081059105D8F00F5F1F4F2F4F3F4F009324 -:105D20003F0510934005209341053093420548EE0E -:105D3000C40E43E0D41EE11CF11CC0923B05D0927E -:105D40003C05E0923D05F0923E05CACF80913705B3 -:105D500090913805A0913905B0913A0508171907B7 -:105D60002A073B07E8F0E0913305F09134053097BE -:105D7000B9F00995611571058105910519F00E9429 -:105D8000392E0EC081E090E0209135053091360526 -:105D9000232B11F480E090E0909336058093350535 -:105DA00060913F05709140058091410590914205B9 -:105DB0001F910F91FF90EF90DF90CF900895809109 -:105DC000730590E020917405821B9109089520913C -:105DD000740580917305281750F4E22FF0E0EB581A -:105DE000FA4F808190E02F5F2093740508958FEF24 -:105DF0009FEF0895E091740580917305E81730F4E2 -:105E0000F0E0EB58FA4F808190E008958FEF9FEF1C -:105E100008950895CF92DF92EF92FF920F931F9310 -:105E2000CF93DF937C01CB018A0120914F05222380 -:105E300089F0EB016B01C40ED51ECC15DD0569F0B0 -:105E40006991D701ED91FC910190F081E02DC7019E -:105E50000995F3CF642F0E946E30C801DF91CF9176 -:105E60001F910F91FF90EF90DF90CF900895CF9307 -:105E7000DF931F92CDB7DEB7698320914F052223B0 -:105E8000D1F020915005203240F021E030E0FC01BB -:105E90003383228380E090E015C080915105E82F84 -:105EA000F0E0EE5AFA4F998190838F5F809351050D -:105EB0008093500505C061E0CE0101960E946E30CE -:105EC00081E090E00F90DF91CF910895FC01138263 -:105ED000128248EE53E060E070E0448355836683AD -:105EE000778382E693E0918380830895109274050E -:105EF0001092730510925105109250050C94B92F11 -:105F0000862F413208F040E265E775E00E94D72F06 -:105F10001092740580937305089521E00C94802FEE -:105F200021E00C94802F81E080934F0560937205EF -:105F3000109251051092500508950C94932F0F93D1 -:105F4000062F21E04091500562E575E080917205D1 -:105F50000E941B30109251051092500510924F056F -:105F60000F91089561E00C949F2F83E495E00C94C9 -:105F7000662F1092020681E0809300061092FF05C2 -:105F800061E082E10E944D3361E083E10E944D3384 -:105F9000E9EBF0E080818E7F808380818D7F80833C -:105FA00088E48093B80085E48093BC000895413272 -:105FB00008F03FC0909102069111FCCF91E09093C0 -:105FC0000206209300062FEF209395051092DA0524 -:105FD000240F2093D9059093010690910106880F14 -:105FE000892B809301068091FF05813041F4109246 -:105FF000FF05809101068093BB0085EC01C085EE12 -:106000008093BC00809102068130E1F38091DA0533 -:10601000841710F44091DA052BED35E0FB01D9012E -:106020008A2F821B841718F48D918193F9CF842FC6 -:10603000089580E008950F93413208F046C0909192 -:1060400002069111FCCF92E09093020600930006A5 -:106050009FEF909395051092DA054093D905FB01C7 -:106060006BED75E0DB019A2F961B941718F4919154 -:106070009D93F9CF1092010690910106880F892B0C -:10608000809301068091FF05813041F41092FF0555 -:10609000809101068093BB0085EC01C085EE809362 -:1060A000BC00222321F0809102068230E1F380912E -:1060B00095058F3F61F080919505803251F0809178 -:1060C0009505803341F483E007C081E005C080E09E -:1060D00003C082E001C084E00F910895613298F41A -:1060E00020910206243089F46093B705FC0189EB06 -:1060F00095E0DC012A2F281B261718F421912D93F7 -:10610000F9CF80E0089581E0089582E0089585ED5B -:106110008093BC008091BC0084FDFCCF10920206ED -:10612000089585EC8093BC001092020608951F929A -:106130000F920FB60F9211242F933F934F935F93BB -:106140006F937F938F939F93AF93BF93EF93FF933F -:106150008091B900887F803609F49CC068F5883248 -:1061600009F45BC090F4803109F454C038F48823FA -:1061700009F4F5C0883009F44DC0F5C0883109F440 -:106180004CC0803209F45DC0EEC0803409F468C0B0 -:1061900048F4803309F455C0883309F0E4C0809393 -:1061A0009505A8C0803509F44FC0883509F45DC055 -:1061B000883409F0D8C0D5C0883909F4C6C0A8F41D -:1061C000883709F467C038F4883609F463C080372B -:1061D00009F460C0C8C0883809F4B7C0803909F430 -:1061E0005FC0803809F0BFC05BC0803B09F485C048 -:1061F00038F4803A09F466C0883A09F47EC0B3C026 -:10620000803C09F4A6C0883C09F4A3C0883B09F48B -:1062100089C0A9C08091010610C09091DA058091D3 -:10622000D905981770F5E091DA0581E08E0F80931B -:10623000DA05F0E0E552FA4F80818093BB0085ECEF -:1062400085C0809395058DC0E091DA0581E08E0FC1 -:106250008093DA058091BB00F0E0E552FA4F80832D -:106260009091DA058091D9056DC0E091DA0581E061 -:106270008E0F8093DA058091BB00F0E0E552FA4F73 -:1062800080838091000681116CC081E08093FF05BE -:1062900084EA60C083E08093020610929605CFCF17 -:1062A00080919605803208F050C0E091960581E01B -:1062B0008E0F809396058091BB00F0E0E956FA4F6F -:1062C0008083BDCF80919605803230F4E0919605B1 -:1062D000F0E0E956FA4F10820E94873060919605EF -:1062E00070E0E091FB05F091FC0587E995E00995E8 -:1062F000109296050E94913036C084E08093020689 -:106300001092B8051092B705E091FD05F091FE05D9 -:1063100009958091B705811105C081E08093B7058B -:106320001092B905E091B80581E08E0F8093B80511 -:10633000F0E0E754FA4F80818093BB009091B8055C -:106340008091B705981708F47ACF85E88093BC0050 -:106350000AC085EC8093BC001092020604C0109223 -:1063600095050E948730FF91EF91BF91AF919F916A -:106370008F917F916F915F914F913F912F910F90EE -:106380000FBE0F901F9018958230E8F4E82FF0E0D0 -:10639000EE0FFF1FED5FF94F71836083813041F095 -:1063A000809169008C7F842B80936900E89A08951E -:1063B00080916900440F551F440F551F837F842B24 -:1063C00080936900E99A08951F920F920FB60F9279 -:1063D00011242F933F934F935F936F937F938F93EA -:1063E0009F93AF93BF93EF93FF9380910306909198 -:1063F0000406892B29F0E0910306F0910406099523 -:10640000FF91EF91BF91AF919F918F917F916F918C -:106410005F914F913F912F910F900FBE0F901F9062 -:1064200018951F920F920FB60F9211242F933F933E -:106430004F935F936F937F938F939F93AF93BF938C -:10644000EF93FF938091050690910606892B29F022 -:10645000E0910506F09106060995FF91EF91BF9135 -:10646000AF919F918F917F916F915F914F913F91EC -:106470002F910F900FBE0F901F9018951F920F92A3 -:106480000FB60F9211242F933F938F939F93AF9347 -:10649000BF938091080690910906A0910A06B091D9 -:1064A0000B063091070623E0230F2D3720F40196C9 -:1064B000A11DB11D05C026E8230F0296A11DB11D27 -:1064C000209307068093080690930906A0930A0676 -:1064D000B0930B0680910C0690910D06A0910E06CC -:1064E000B0910F060196A11DB11D80930C069093EB -:1064F0000D06A0930E06B0930F06BF91AF919F912A -:106500008F913F912F910F900FBE0F901F90189574 -:106510002FB7F894609108067091090680910A06D9 -:1065200090910B062FBF08953FB7F89480910C0609 -:1065300090910D06A0910E06B0910F0626B5A89B6E -:1065400005C02F3F19F00196A11DB11D3FBF662761 -:10655000782F892F9A2F620F711D811D911D42E0A6 -:10656000660F771F881F991F4A95D1F70895CF921C -:10657000DF92EF92FF92CF93DF936B017C010E9439 -:106580009432EB01C114D104E104F10489F00E94BA -:1065900082360E9494326C1B7D0B683E734090F3F0 -:1065A00081E0C81AD108E108F108C851DC4FEACFF0 -:1065B000DF91CF91FF90EF90DF90CF9008950197FA -:1065C00039F0880F991F880F991F02970197F1F7EB -:1065D0000895789484B5826084BD84B5816084BD5B -:1065E00085B5826085BD85B5816085BDEEE6F0E04C -:1065F000808181608083E1E8F0E0108280818260A8 -:106600008083808181608083E0E8F0E08081816028 -:106610008083E1EBF0E0808184608083E0EBF0E058 -:10662000808181608083EAE7F0E0808184608083FC -:1066300080818260808380818160808380818068A6 -:1066400080831092C1000895833081F028F4813056 -:1066500099F08230A1F008958730A9F08830B9F020 -:106660008430D1F4809180008F7D03C080918000C0 -:106670008F7780938000089584B58F7702C084B5AA -:106680008F7D84BD08958091B0008F7703C0809185 -:10669000B0008F7D8093B00008950F931F93CF9328 -:1066A000DF931F92CDB7DEB7282F30E0F901ED5B05 -:1066B000FC4F8491F901E95AFC4F1491F901E55915 -:1066C000FC4F04910023C9F0882321F069830E94C4 -:1066D00024336981E02FF0E0EE0FFF1FE158FC4FFB -:1066E000A591B4919FB7F8948C91611103C0109556 -:1066F000812301C0812B8C939FBF0F90DF91CF919D -:106700001F910F910895FC01818D228D90E0805C96 -:106710009F4F821B91098F7399270895FC01918DDA -:10672000828D981731F0828DE80FF11D858D90E0F4 -:1067300008958FEF9FEF0895FC01918D828D98173A -:1067400061F0828DDF01A80FB11D5D968C91928D55 -:106750009F5F9F73928F90E008958FEF9FEF089552 -:1067600080E295E3892B49F080E090E0892B29F0C5 -:106770000E94203581110C9400000895FC01848D45 -:10678000DF01A80FB11DA35ABF4F2C91848D90E05B -:1067900001968F739927848FA689B7892C93A08936 -:1067A000B1898C9180648C93938D848D981306C0ED -:1067B0000288F389E02D80818F7D80830895CF93B7 -:1067C000DF93EC01888D8823C9F0EA89FB898081F9 -:1067D00085FD05C0A889B9898C9186FD0FC00FB6CB -:1067E00007FCF5CF808185FFF2CFA889B9898C910C -:1067F00085FFEDCFCE010E94BE33E7CFDF91CF9171 -:106800000895CF92DF92FF920F931F93CF93DF9360 -:106810001F92CDB7DEB76C01DC015B969C915B9754 -:106820005C968C915C97981307C05096ED91FC9103 -:106830005197808185FD32C0F601038D10E00F5F16 -:106840001F4F0F731127F02EF601848DF81211C01F -:106850000FB607FCF9CFD6015096ED91FC915197F8 -:10686000808185FFF1CFC60169830E94BE336981B3 -:10687000EBCF838DE80FF11DE35AFF4F6083D60104 -:106880005B960C935B975296ED91FC9153978081A8 -:106890008062808381E058968C930DC0D601569615 -:1068A000ED91FC91579760835096ED91FC91519733 -:1068B00080818064808381E090E00F90DF91CF91B0 -:1068C0001F910F91FF90DF90CF900895BF92CF92CC -:1068D000DF92EF92FF92CF93DF93EC016A017B018D -:1068E000B22EE889F98982E08083411581EE58074C -:1068F00061057105A1F060E079E08DE390E0A7010A -:1069000096010E94C7362150310941095109569517 -:10691000479537952795211580E1380798F0E88944 -:10692000F989108260E874E88EE190E0A701960191 -:106930000E94C736215031094109510956954795A2 -:1069400037952795EC85FD853083EE85FF8520837F -:10695000188EEC89FD89B082EA89FB89808180618B -:106960008083EA89FB89808188608083EA89FB894A -:10697000808180688083EA89FB8980818F7D808324 -:10698000DF91CF91FF90EF90DF90CF90BF9008956F -:106990001F920F920FB60F9211242F938F939F93F4 -:1069A000EF93FF93E0912006F09121068081E09122 -:1069B0002606F091270682FD12C09081809129065B -:1069C0008F5F8F7320912A06821751F0E09129067C -:1069D000F0E0E05FF94F958F8093290601C0808138 -:1069E000FF91EF919F918F912F910F900FBE0F907C -:1069F0001F9018951F920F920FB60F9211242F938C -:106A00003F934F935F936F937F938F939F93AF9336 -:106A1000BF93EF93FF9380E196E00E94BE33FF9116 -:106A2000EF91BF91AF919F918F917F916F915F9106 -:106A30004F913F912F910F900FBE0F901F9018957F -:106A400080E196E00E94833321E0892B09F420E065 -:106A5000822F0895109213061092120688EE93E08A -:106A6000A0E0B0E08093140690931506A09316065C -:106A7000B093170682E793E0909311068093100677 -:106A800085EC90E090931D0680931C0684EC90E0CA -:106A900090931F0680931E0680EC90E09093210651 -:106AA0008093200681EC90E0909323068093220649 -:106AB00082EC90E0909325068093240686EC90E08B -:106AC00090932706809326061092290610922A0694 -:106AD00010922B0610922C06089508950E94E93218 -:106AE0000E946D350E949927C0EBD3E30E94F025E8 -:106AF0002097E1F30E94B033F9CFCF92DF92EF926B -:106B0000FF920F931F93CF93DF936C017A01EB01F8 -:106B1000E60EF71E00E010E0CE15DF0561F069918A -:106B2000D601ED91FC910190F081E02DC60109950F -:106B3000080F191FF1CFC801DF91CF911F910F915D -:106B4000FF90EF90DF90CF9008956115710581F06F -:106B5000DB010D900020E9F7AD0141505109461BC2 -:106B6000570BDC01ED91FC910280F381E02D09943B -:106B700080E090E008950C94A535DC01ED91FC9146 -:106B80000190F081E02D09948F929F92AF92BF9275 -:106B9000CF92DF92EF92FF920F931F93CF93DF93E9 -:106BA000CDB7DEB7A1970FB6F894DEBF0FBECDBF4D -:106BB0007C01C42EE52FCB01D22E19A221E02D1588 -:106BC00010F02AE0D22E8E010F5D1F4F8D2C912CDC -:106BD000A12CB12C6C2D7E2FA50194010E94C736EB -:106BE0008C2DD29E80191124015011098A3014F481 -:106BF000805D01C0895CF801808321153105410564 -:106C0000510521F0C22EE32FCA01E4CFB801C7011C -:106C10000E94A535A1960FB6F894DEBF0FBECDBF7A -:106C2000DF91CF911F910F91FF90EF90DF90CF9068 -:106C3000BF90AF909F908F900895CF92DF92EF9288 -:106C4000FF920F931F93CF93DF93EC016A017B01B7 -:106C50002115310579F4E881F9810190F081E02D69 -:106C6000642FDF91CF911F910F91FF90EF90DF90F4 -:106C7000CF9009942A303105E9F477FF1AC06DE20C -:106C80000E94BD358C0144275527BA014C195D0976 -:106C90006E097F092AE0CE010E94C435800F911F42 -:106CA000DF91CF911F910F91FF90EF90DF90CF90E8 -:106CB00008952AE0B701A601CE01DF91CF911F917F -:106CC0000F91FF90EF90DF90CF900C94C4359A0114 -:106CD000AB01662757FD6095762F0C941D36211564 -:106CE000310541F4DC01ED91FC910190F081E02D42 -:106CF000642F09940C94C4359A01AB0160E070E0F4 -:106D00000C946F360895DB018F939F930E94FE369B -:106D1000BF91AF91A29F800D911DA39F900DB29F37 -:106D2000900D11240895991B79E004C0991F9617BE -:106D300008F0961B881F7A95C9F780950895AA1BBD -:106D4000BB1B51E107C0AA1FBB1FA617B70710F056 -:106D5000A61BB70B881F991F5A95A9F78095909588 -:106D6000BC01CD01089597FB072E16F4009407D0BF -:106D700077FD09D00E949F3607FC05D03EF4909520 -:106D800081959F4F0895709561957F4F0895A1E279 -:106D90001A2EAA1BBB1BFD010DC0AA1FBB1FEE1F95 -:106DA000FF1FA217B307E407F50720F0A21BB30BE0 -:106DB000E40BF50B661F771F881F991F1A9469F75C -:106DC00060957095809590959B01AC01BD01CF01B8 -:106DD0000895EE0FFF1F0590F491E02D0994A29FF6 -:106DE000B001B39FC001A39F700D811D1124911D9F -:106DF000B29F700D811D1124911D08950E94EF36E0 -:106E0000A59F900DB49F900DA49F800D911D1124FE -:106E10000895B7FF0C94FE360E94FE36821B930B3A -:106E20000895FC0188279927E89421912032E9F3FD -:106E3000293010F02E30C8F32B3241F02D3239F4C6 -:106E4000689404C00E947837820F911D21912053CD -:106E50002A30C0F31EF4909581959F4F0895FB0151 -:106E6000DC0102C005900D9241505040D8F70895C2 -:106E7000FB01DC010D900020E9F7119705900D92C0 -:106E80000020E1F70895FB01DC0105900D92002040 -:106E9000E1F70895FB01DC014150504048F00590B6 -:106EA0000D920020C9F701C01D9241505040E0F7FB -:106EB0000895FB01DC0104C08D910190801921F43B -:106EC00041505040C8F7881B990B0895FB01DC0125 -:106ED0000D900020E9F7119701900D920020E1F745 -:106EE0000895FB01DC0101900D920020E1F7089567 -:106EF0007AE0979F902D879F802D910D1124089502 -:106F0000AEE0B0E0E6E8F7E30C94673A0D891E893D -:106F100086E08C831A8309838FEF9FE79E838D839E -:106F2000AE01475E5F4F6F89788DCE0101960E945A -:106F3000A237EF81F885E00FF11F10822E96E4E072 -:106F40000C94833AACE0B0E0E8EAF7E30C94593AE9 -:106F50007C016B018A01FC0117821682838181FF0B -:106F6000BDC1CE0101964C01F7019381F60193FD5D -:106F7000859193FF81916F01882309F4ABC185321C -:106F800039F493FD859193FF81916F01853229F446 -:106F9000B70190E00E94B939E7CF512C312C20E0A5 -:106FA0002032A0F48B3269F030F4803259F0833211 -:106FB00069F420612CC08D3239F0803339F42160BE -:106FC00026C02260246023C0286021C027FD27C07E -:106FD00030ED380F3A3078F426FF06C0FAE05F9EB5 -:106FE000300D1124532E13C08AE0389E300D112429 -:106FF000332E20620CC08E3221F426FD6BC120643A -:1070000006C08C3611F4206802C0883641F4F601BF -:1070100093FD859193FF81916F018111C1CF982FCD -:107020009F7D9554933028F40C5F1F4FFFE3F98345 -:107030000DC0833631F0833771F0833509F05BC0C2 -:1070400022C0F801808189830E5F1F4F442443943E -:10705000512C540115C03801F2E06F0E711CF8017B -:10706000A080B18026FF03C0652D70E002C06FEFE5 -:107070007FEFC5012C870E94AE392C0183012C853E -:107080002F77222E17C03801F2E06F0E711CF80125 -:10709000A080B18026FF03C0652D70E002C06FEFB5 -:1070A0007FEFC5012C870E94A3392C012C85206815 -:1070B000222E830123FC1BC0832D90E04816590625 -:1070C000B0F4B70180E290E00E94B9393A94F4CF6D -:1070D000F50127FC859127FE81915F01B70190E0C2 -:1070E0000E94B93931103A94F1E04F1A5108411415 -:1070F000510471F7E5C0843611F0893639F5F8018D -:1071000027FF07C060817181828193810C5F1F4FCF -:1071100008C060817181882777FD8095982F0E5F68 -:107120001F4F2F76B22E97FF09C0909580957095CE -:1071300061957F4F8F4F9F4F2068B22E2AE030E03D -:10714000A4010E94EB39A82EA81844C0853729F461 -:107150002F7EB22E2AE030E025C0F22FF97FBF2E1D -:107160008F36C1F018F4883579F0B4C0803719F043 -:10717000883721F0AFC02F2F2061B22EB4FE0DC092 -:107180008B2D8460B82E09C024FF0AC09F2F966003 -:10719000B92E06C028E030E005C020E130E002C092 -:1071A00020E132E0F801B7FE07C060817181828181 -:1071B00093810C5F1F4F06C06081718180E090E079 -:1071C0000E5F1F4FA4010E94EB39A82EA818FB2DBB -:1071D000FF77BF2EB6FE0BC02B2D2E7FA51450F4CB -:1071E000B4FE0AC0B2FC08C02B2D2E7E05C07A2C3E -:1071F0002B2D03C07A2C01C0752C24FF0DC0FE017D -:10720000EA0DF11D8081803311F4297E09C022FF2F -:1072100006C07394739404C0822F867809F0739427 -:1072200023FD13C020FF06C05A2C731418F4530C0E -:107230005718732C731468F4B70180E290E02C8720 -:107240000E94B93973942C85F5CF731410F4371854 -:1072500001C0312C24FF12C0B70180E390E02C87DD -:107260000E94B9392C8522FF17C021FF03C088E591 -:1072700090E002C088E790E0B7010CC0822F8678CA -:1072800059F021FD02C080E201C08BE227FD8DE2B2 -:10729000B70190E00E94B939A51438F4B70180E332 -:1072A00090E00E94B9395A94F7CFAA94F401EA0DFC -:1072B000F11D8081B70190E00E94B939A110F5CF8E -:1072C000332009F451CEB70180E290E00E94B93931 -:1072D0003A94F6CFF7018681978102C08FEF9FEF36 -:1072E0002C96E2E10C94753AF999FECF92BD81BDDE -:1072F000F89A992780B50895A6E1B0E044E050E0FF -:107300000C944B3AA8E1B0E042E050E00C944B3AC8 -:10731000262FF999FECF1FBA92BD81BD20BD0FB6B1 -:10732000F894FA9AF99A0FBE01960895242F0E94B4 -:107330008939252F0E9489390C949E390E948839F9 -:10734000272F0C948939FC01059061507040011081 -:10735000D8F7809590958E0F9F1F0895FC0161507E -:10736000704001900110D8F7809590958E0F9F1F67 -:1073700008950F931F93CF93DF93182F092FEB01DD -:107380008B8181FD03C08FEF9FEF20C082FF10C073 -:107390004E815F812C813D81421753077CF4E88147 -:1073A000F9819F012F5F3F4F39832883108306C0E7 -:1073B000E885F985812F0995892B29F72E813F8151 -:1073C0002F5F3F4F3F832E83812F902FDF91CF91EF -:1073D0001F910F910895FA01AA27283051F1203109 -:1073E00081F1E8946F936E7F6E5F7F4F8F4F9F4F59 -:1073F000AF4FB1E03ED0B4E03CD0670F781F891F9B -:107400009A1FA11D680F791F8A1F911DA11D6A0F68 -:10741000711D811D911DA11D20D009F468943F911B -:107420002AE0269F11243019305D3193DEF6CF011A -:107430000895462F4770405D4193B3E00FD0C9F7E0 -:10744000F6CF462F4F70405D4A3318F0495D31FD4D -:107450004052419302D0A9F7EACFB4E0A6959795A0 -:10746000879577956795BA95C9F700976105710576 -:1074700008959B01AC010A2E0694579547953795C0 -:107480002795BA95C9F7620F731F841F951FA01D1A -:107490000895DC01CB01FC01F999FECF06C0F2BDD5 -:1074A000E1BDF89A319600B40D9241505040B8F7C2 -:1074B00008952F923F924F925F926F927F928F9298 -:1074C0009F92AF92BF92CF92DF92EF92FF920F9373 -:1074D0001F93CF93DF93CDB7DEB7CA1BDB0B0FB67D -:1074E000F894DEBF0FBECDBF09942A88398848883A -:1074F0005F846E847D848C849B84AA84B984C884D0 -:10750000DF80EE80FD800C811B81AA81B981CE0FC6 -:10751000D11D0FB6F894DEBF0FBECDBFED010895AB -:04752000F894FFCF0D -:1075240001E701EE01F8010102EB010A0C0E101251 -:107534001416181A1C1E20222426282A2C2E303217 -:1075440000060A0C0E0F10000502090214021902AB -:1075540021024C25643A256441006B030C03B202FA -:1075640000000401D601DD04F004E704E204D304BE -:10757400CE04C904D804BF04A104C4040000290231 -:107584002C01000000004000140054000000000022 -:10759400A6047D352B002564202564202564202540 -:1075A40064202564202564002B2573004C31004C95 -:1075B40032002573204D61782043757272656E74B4 -:1075C400002B256441002F003A0025356C7557685F -:1075D4000025366C756B5768002533642E2564418D -:1075E40000202020203041002532642E25316443C0 -:1075F40000253032643A253032643A253032640052 -:1076040077666C616773005965732F4E6F0053651D -:1076140074205374617274005365742053746F70D2 -:107624000059657300524742004D6F6E6F63687274 -:107634006F6D65004175746F004C6576656C203123 -:10764400004C6576656C203200202020202020200C -:10765400202020202020202020000000000097135C -:10766400AD139107000000003514E4053526000031 -:107674000000D1134D061020000000006A140E060D -:10768400631F00000000DB148506400B00000000AF -:1076940011155507780B000000004415B906B00B0E -:1076A400000000007C15ED06F00B00000000B5158D -:1076B4002107280C00000000EE15C9086C13000017 -:1076C4000000DF18EB080D0900000000A116D61613 -:1076D400FA160000000018173B176017000000009E -:1076E4007F17AF17C91700000000E9170C183218EC -:1076F40000000000511874189A1800000000421A83 -:10770400370986050000000006145909C025000049 -:10771400000036194F195D19000000006F1988190F -:10772400961900000000A819BD19CB19000000002B -:10773400F5190A1A181A256353542025303278256E -:1077440063002563574620253032782563004F4B6C -:1077540020004E4B20002564202530347800257805 -:1077640020257820257800256C6420256C6400256C -:107774006420256C6400256C7520256C7500000060 -:107784000000372F0A2FDF2EE72EFA2E092F0000D4 -:0E779400000001347D3583339C338E33DF33A8 -:00000001FF From dc5a402472061cc4789745dfdf45dde967bb56df Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Fri, 5 Jun 2015 14:03:18 -0700 Subject: [PATCH 32/96] maintainance of kWh recording code --- open_evse.ino | 2 -- 1 file changed, 2 deletions(-) diff --git a/open_evse.ino b/open_evse.ino index fb0ad665..a20d97c0 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -262,8 +262,6 @@ void TempMonitor::Init() #ifdef TMP007_IS_ON_I2C m_tmp007.begin(); #endif // TMP007_IS_ON_I2C - - m_ampacity = g_EvseController.GetCurrentCapacity(); // Need to keep track of the user's original ampacity setting since temperature monitoring will throttle it back and need to later restore it } void TempMonitor::Read() From dd31da2b2d1b1ebd5909cf2218c261f8a07bbf2c Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Fri, 5 Jun 2015 14:04:56 -0700 Subject: [PATCH 33/96] maintainance of kWh recording code --- open_evse.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/open_evse.h b/open_evse.h index d7a359e0..cf5554bf 100644 --- a/open_evse.h +++ b/open_evse.h @@ -99,12 +99,11 @@ #define GFI_SELFTEST #endif //UL_GFI_SELFTEST - -// Temperature monitoring support // comment out both TEMPERATURE_MONITORING and KWH_RECORDING to have the elapsed time and time of day displayed on the second line of the LCD -#define TEMPERATURE_MONITORING +#define TEMPERATURE_MONITORING // Temperature monitoring support #ifdef AMMETER // kWh Recording feature depends upon #AMMETER support +// comment out KWH_RECORDING to have the elapsed time and time of day displayed on the second line of the LCD #define KWH_RECORDING #ifdef KWH_RECORDING // stop charging after a certain kWh reached @@ -743,8 +742,6 @@ class TempMonitor { #ifdef TMP007_IS_ON_I2C Adafruit_TMP007 m_tmp007; #endif //TMP007_IS_ON_I2C - - uint8_t m_ampacity; // using this to keep track of the user's original ampacity to restore after temperature monitoring has throttled it to a lower value // these three temperatures need to be signed integers int16_t m_MCP9808_temperature; // 230 means 23.0C Using an integer to save on floating point library use int16_t m_DS3231_temperature; // the DS3231 RTC has a built in temperature sensor From 90efba17e7e28634546e7ef59ddc1608b2770fe3 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Fri, 5 Jun 2015 14:05:47 -0700 Subject: [PATCH 34/96] maintainance of kWh recording code --- J1772EvseController.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 8b4d903d..6bc27274 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -1355,14 +1355,14 @@ void J1772EVSEController::Update() (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // Throttle back the L2 current advice to the EV g_TempMonitor.SetOverTemperature(1); - SetCurrentCapacity(g_TempMonitor.m_ampacity / 2,0,1); // set L2 to the throttled back level + SetCurrentCapacity((eeprom_read_byte((uint8_t*) EOFS_CURRENT_CAPACITY_L2)) / 2,0,1); // set L2 to the throttled back level } if (g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_RESTORE_AMPERAGE ) && // all sensors need to show return to lower levels (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ) && (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ))) { // restore the original L2 current advice to the EV g_TempMonitor.SetOverTemperature(0); - SetCurrentCapacity(g_TempMonitor.m_ampacity,0,1); // set L2 to the user's original setting for L2 current + SetCurrentCapacity((eeprom_read_byte((uint8_t*) EOFS_CURRENT_CAPACITY_L2)),0,1); // set L2 to the user's original setting for L2 current } @@ -1370,14 +1370,14 @@ void J1772EVSEController::Update() (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ))) { // Throttle back the L2 current advice to the EV g_TempMonitor.SetOverTemperatureShutdown(1); - SetCurrentCapacity(g_TempMonitor.m_ampacity / 4,0,1); + SetCurrentCapacity((eeprom_read_byte((uint8_t*) EOFS_CURRENT_CAPACITY_L2)) / 4,0,1); } if (g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_THROTTLE_DOWN ) && // all sensors need to show return to lower levels (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) && (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // restore the throttled down current advice to the EV since things have cooled down again g_TempMonitor.SetOverTemperatureShutdown(0); - SetCurrentCapacity(g_TempMonitor.m_ampacity / 2,0,1); // set L2 to the throttled back level + SetCurrentCapacity((eeprom_read_byte((uint8_t*) EOFS_CURRENT_CAPACITY_L2)) / 2,0,1); // set L2 to the throttled back level m_Pilot.SetPWM(m_CurrentCapacity); } } From 3c3934cfeba06bbba5df34609fa6e8b91fb237ac Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Fri, 5 Jun 2015 14:06:39 -0700 Subject: [PATCH 35/96] maintainance of kWh recording code From 0c232191dc6feb17e405c8365a1d7e8950b8bbdc Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 5 Jun 2015 14:28:54 -0700 Subject: [PATCH 36/96] show charge time only when not overtemp --- open_evse.ino | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/open_evse.ino b/open_evse.ino index fb0ad665..e1ca05b6 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -661,7 +661,6 @@ void OnboardDisplay::Update(int8_t updmode) g_CurrTime = g_RTC.now(); #endif - #ifdef LCD16X2 #if defined(AMMETER) if (((curstate == EVSE_STATE_C) || g_EvseController.AmmeterCalEnabled()) && AmmeterIsDirty()) { @@ -757,23 +756,26 @@ void OnboardDisplay::Update(int8_t updmode) #ifdef LCD16X2 //Adafruit RGB LCD LcdSetBacklightColor(TEAL); #endif - } - + } + if (!(g_TempMonitor.OverTemperature() || TEMPERATURE_DISPLAY_ALWAYS)) { +#endif // TEMPERATURE_MONITORING #ifndef KWH_RECORDING int h = hour(elapsedTime); // display the elapsed charge time int m = minute(elapsedTime); int s = second(elapsedTime); -#ifdef DELAYTIMER - sprintf(g_sTmp,"%02d:%02d:%02d %02d:%02d",h,m,s,(int)((g_CurrTime.hour() <= 23) ? g_CurrTime.hour() : 0),(int)((g_CurrTime.hour() <= 23) ? g_CurrTime.minute() : 0)); -#else sprintf(g_sTmp,"%02d:%02d:%02d",h,m,s); -#endif //#ifdef DELAYTIMER - +#ifdef RTC + g_sTmp[8]=' '; + g_sTmp[9]=' '; + g_sTmp[10]=' '; + sprintf(g_sTmp+11,g_sHHMMfmt,(int)g_CurrTime.hour(),(int)g_CurrTime.minute()); +#endif //RTC LcdPrint(1,g_sTmp); #endif // KWH_RECORDING +#ifdef TEMPERATURE_MONITORING + } #endif // TEMPERATURE_MONITORING - } - + } // curstate == EVSE_STATE_C // Display a new stopped LCD screen with Delay Timers enabled - GoldServe #ifdef DELAYTIMER else if (curstate == EVSE_STATE_SLEEPING) { From 54b7774eddb0dd74abfdac767ae59fb1d698f731 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 5 Jun 2015 14:52:07 -0700 Subject: [PATCH 37/96] support L1, save bytes --- J1772EvseController.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 6bc27274..5ed015d3 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -1350,19 +1350,23 @@ void J1772EVSEController::Update() #ifdef TEMPERATURE_MONITORING if (m_ElapsedChargeTime != m_ElapsedChargeTimePrev) { + uint8_t currcap = eeprom_read_byte((uint8_t*) ((GetCurSvcLevel() == 2) ? EOFS_CURRENT_CAPACITY_L2 : EOFS_CURRENT_CAPACITY_L1)); + uint8_t setit = 0; g_TempMonitor.Read(); if (!g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_THROTTLE_DOWN ) || // any sensor reaching threshold trips action (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // Throttle back the L2 current advice to the EV g_TempMonitor.SetOverTemperature(1); - SetCurrentCapacity((eeprom_read_byte((uint8_t*) EOFS_CURRENT_CAPACITY_L2)) / 2,0,1); // set L2 to the throttled back level + currcap /= 2; // set to the throttled back level + setit = 1; } if (g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_RESTORE_AMPERAGE ) && // all sensors need to show return to lower levels (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ) && (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ))) { // restore the original L2 current advice to the EV g_TempMonitor.SetOverTemperature(0); - SetCurrentCapacity((eeprom_read_byte((uint8_t*) EOFS_CURRENT_CAPACITY_L2)),0,1); // set L2 to the user's original setting for L2 current + + setit = 1; // set to the user's original setting for current } @@ -1370,16 +1374,23 @@ void J1772EVSEController::Update() (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ))) { // Throttle back the L2 current advice to the EV g_TempMonitor.SetOverTemperatureShutdown(1); - SetCurrentCapacity((eeprom_read_byte((uint8_t*) EOFS_CURRENT_CAPACITY_L2)) / 4,0,1); + currcap /= 4; + setit = 1; } if (g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_THROTTLE_DOWN ) && // all sensors need to show return to lower levels (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) && (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // restore the throttled down current advice to the EV since things have cooled down again g_TempMonitor.SetOverTemperatureShutdown(0); - SetCurrentCapacity((eeprom_read_byte((uint8_t*) EOFS_CURRENT_CAPACITY_L2)) / 2,0,1); // set L2 to the throttled back level - m_Pilot.SetPWM(m_CurrentCapacity); + + currcap /= 2; // set to the throttled back level } + if (setit) { + SetCurrentCapacity(currcap,0,1); + if (m_Pilot.GetState() != PILOT_STATE_PWM) { + m_Pilot.SetPWM(m_CurrentCapacity); + } + } } #endif // TEMPERATURE_MONITORING #ifdef CHARGE_LIMIT From b684b3a160c14fed1c25937e19b774ceca02b461 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 5 Jun 2015 15:47:04 -0700 Subject: [PATCH 38/96] fix bug --- J1772EvseController.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 5ed015d3..37c51f66 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -1384,6 +1384,7 @@ void J1772EVSEController::Update() g_TempMonitor.SetOverTemperatureShutdown(0); currcap /= 2; // set to the throttled back level + setit = 1; } if (setit) { SetCurrentCapacity(currcap,0,1); From d9d48459749050953e701a50c84925048c104891 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 5 Jun 2015 17:29:42 -0700 Subject: [PATCH 39/96] fix FT_ENDURANCE compile problems --- J1772EvseController.cpp | 6 +++++- J1772EvseController.h | 6 ++++++ open_evse.ino | 5 ----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 37c51f66..324ef9aa 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -18,7 +18,11 @@ */ #include "open_evse.h" - +#ifdef FT_ENDURANCE +int g_CycleCnt = -1; +long g_CycleHalfStart; +uint8_t g_CycleState; +#endif THRESH_DATA g_DefaultThreshData = {875,780,690,0,260}; diff --git a/J1772EvseController.h b/J1772EvseController.h index 877d0dbc..25ae741d 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -344,4 +344,10 @@ class J1772EVSEController { #endif // ADVPWR }; +#ifdef FT_ENDURANCE +extern int g_CycleCnt; +extern long g_CycleHalfStart; +extern uint8_t g_CycleState; +#endif + extern J1772EVSEController g_EvseController; diff --git a/open_evse.ino b/open_evse.ino index 3249d841..53023151 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -185,11 +185,6 @@ unsigned long g_WattHours_accumulated; unsigned long g_WattSeconds; #endif // KWH_RECORDING -#ifdef FT_ENDURANCE -int g_CycleCnt = -1; -long g_CycleHalfStart; -uint8_t g_CycleState; -#endif //-- end global variables From 1c389f4e85363c5b83a9d21576ba2b3ffbea053e Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 5 Jun 2015 18:05:35 -0700 Subject: [PATCH 40/96] update --- CHANGELOG | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 71c5afcc..3ba78f43 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,6 @@ Change Log -vD3.9.0 SCL 20150604 +vD3.9.0 SCL 20150604-5 - split out Gfi/J1772Pilot/J1772EvseController into separate files - add support for changing I2C frequency - increase I2C bus from 100KHz -> 200KHz @@ -24,6 +24,8 @@ vD3.9.0 SCL 20150604 any trip count = FE - fix documentation for $SC & $GG - got rid of superfluous AUTOSTART_MENU and MANUALSTART code +- fixes for TEMPERATURE_MONITORING from CraigK +- fix bugs in Charging screen when KWH_RECORDING or TEMPERATURE_MONITORING not defined vD3.8.4 SCL 20150526 - display "Disabled" when EVSE_STATE_DISABLED instead of "Stopped" From 72eae1ee17e759188fff1acbe50f2752ffde091b Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 5 Jun 2015 18:15:17 -0700 Subject: [PATCH 41/96] boost I2C bus from 200 -> 400KHz --- CHANGELOG | 3 +++ open_evse.h | 2 +- twi.h | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3ba78f43..cff6c5c0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Change Log +vD3.9.1 SCL 20150605 +- boost I2C bus to 400KHz + vD3.9.0 SCL 20150604-5 - split out Gfi/J1772Pilot/J1772EvseController into separate files - add support for changing I2C frequency diff --git a/open_evse.h b/open_evse.h index cf5554bf..3fd3d2af 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.0" +#define VERSION "D3.9.1" //-- begin features diff --git a/twi.h b/twi.h index b8eba69b..866e3a35 100644 --- a/twi.h +++ b/twi.h @@ -24,7 +24,7 @@ //#define ATMEGA8 - #define TWI_FREQ 200000L + #define TWI_FREQ 400000L #define TWI_BUFFER_LENGTH 32 From 706e467b87c4b6e35bc8d3343fb374346e98d21e Mon Sep 17 00:00:00 2001 From: lincomatic Date: Sat, 6 Jun 2015 11:49:37 -0700 Subject: [PATCH 42/96] fix $GF bug --- CHANGELOG | 3 +++ J1772EvseController.cpp | 9 --------- J1772EvseController.h | 12 ++++++------ open_evse.h | 2 +- rapi_proc.h | 2 +- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index cff6c5c0..ef289eaa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Change Log +vD3.9.2 SCL 20150606 +- fix $GF bug + vD3.9.1 SCL 20150605 - boost I2C bus to 400KHz diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 324ef9aa..4a16554b 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -824,22 +824,13 @@ void J1772EVSEController::Init() #ifdef GFI m_GfiRetryCnt = 0; m_GfiTripCnt = eeprom_read_byte((uint8_t*)EOFS_GFI_TRIP_CNT); - if (m_GfiTripCnt == 255) { - m_GfiTripCnt = 0; - } #endif // GFI #ifdef ADVPWR m_NoGndRetryCnt = 0; m_NoGndTripCnt = eeprom_read_byte((uint8_t*)EOFS_NOGND_TRIP_CNT); - if (m_NoGndTripCnt != 255) { - m_NoGndTripCnt = 0; - } m_StuckRelayStartTimeMS = 0; m_StuckRelayTripCnt = eeprom_read_byte((uint8_t*)EOFS_STUCK_RELAY_TRIP_CNT); - if (m_StuckRelayTripCnt != 255) { - m_StuckRelayTripCnt = 0; - } m_NoGndRetryCnt = 0; m_NoGndStart = 0; diff --git a/J1772EvseController.h b/J1772EvseController.h index 25ae741d..8e74427a 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -85,7 +85,7 @@ class J1772EVSEController { Gfi m_Gfi; unsigned long m_GfiFaultStartMs; unsigned long m_GfiRetryCnt; - uint8_t m_GfiTripCnt; + uint8_t m_GfiTripCnt; // contains tripcnt-1 #endif // GFI AdcPin adcPilot; #ifdef CURRENT_PIN @@ -114,9 +114,9 @@ class J1772EVSEController { #ifdef ADVPWR unsigned long m_NoGndStart; unsigned long m_NoGndRetryCnt; - uint8_t m_NoGndTripCnt; + uint8_t m_NoGndTripCnt; // contains tripcnt-1 unsigned long m_StuckRelayStartTimeMS; - uint8_t m_StuckRelayTripCnt; + uint8_t m_StuckRelayTripCnt; // contains tripcnt-1 #endif // ADVPWR uint16_t m_wFlags; // ECF_xxx uint8_t m_bVFlags; // ECVF_xxx @@ -273,7 +273,7 @@ class J1772EVSEController { #ifdef GFI void SetGfiTripped(); uint8_t GfiTripped() { return m_bVFlags & ECVF_GFI_TRIPPED; } - uint8_t GetGfiTripCnt() { return m_GfiTripCnt; } + uint8_t GetGfiTripCnt() { return m_GfiTripCnt+1; } #ifdef GFI_SELFTEST uint8_t GfiSelfTestEnabled() { return (m_wFlags & ECF_GFI_TEST_DISABLED) ? 0 : 1; @@ -339,8 +339,8 @@ class J1772EVSEController { void ShowDisabledTests(); #endif #ifdef ADVPWR - uint8_t GetNoGndTripCnt() { return m_NoGndTripCnt; } - uint8_t GetStuckRelayTripCnt() { return m_StuckRelayTripCnt; } + uint8_t GetNoGndTripCnt() { return m_NoGndTripCnt+1; } + uint8_t GetStuckRelayTripCnt() { return m_StuckRelayTripCnt+1; } #endif // ADVPWR }; diff --git a/open_evse.h b/open_evse.h index 3fd3d2af..d71ccb49 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.1" +#define VERSION "D3.9.2" //-- begin features diff --git a/rapi_proc.h b/rapi_proc.h index 9720db9d..68814227 100644 --- a/rapi_proc.h +++ b/rapi_proc.h @@ -141,7 +141,7 @@ GE - get settings $GE*B0 GF - get fault counters response: OK gfitripcnt nogndtripcnt stuckrelaytripcnt (all values hex) - maximum trip count = 0xFE for any counter + maximum trip count = 0xFF for any counter $GF*B1 GG - get charging current and voltage response: OK milliamps millivolts From 722c024433c88001d57e53728da0dfe7324de189 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Sat, 6 Jun 2015 20:47:09 -0700 Subject: [PATCH 43/96] fix trip counters not incrementing --- J1772EvseController.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 4a16554b..75368c16 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -971,7 +971,7 @@ void J1772EVSEController::Update() chargingOff(); // open the relay - if ((prevevsestate != EVSE_STATE_NO_GROUND) && (m_NoGndTripCnt < 253)) { + if ((prevevsestate != EVSE_STATE_NO_GROUND) && (m_NoGndTripCnt+1 < 254)) { m_NoGndTripCnt++; eeprom_write_byte((uint8_t*)EOFS_NOGND_TRIP_CNT,m_NoGndTripCnt); } @@ -1021,7 +1021,7 @@ void J1772EVSEController::Update() ((curms - m_StuckRelayStartTimeMS) > STUCK_RELAY_DELAY) ) || // start delay de-bounce (prevevsestate == EVSE_STATE_STUCK_RELAY) ) { // already in error state // stuck relay - if ((prevevsestate != EVSE_STATE_STUCK_RELAY) && (m_StuckRelayTripCnt < 253)) { + if ((prevevsestate != EVSE_STATE_STUCK_RELAY) && (m_StuckRelayTripCnt+1 < 254)) { m_StuckRelayTripCnt++; eeprom_write_byte((uint8_t*)EOFS_STUCK_RELAY_TRIP_CNT,m_StuckRelayTripCnt); } @@ -1041,7 +1041,7 @@ void J1772EVSEController::Update() m_EvseState = EVSE_STATE_GFCI_FAULT; if (prevevsestate != EVSE_STATE_GFCI_FAULT) { // state transition - if (m_GfiTripCnt < 253) { + if (m_GfiTripCnt+1 < 254) { m_GfiTripCnt++; eeprom_write_byte((uint8_t*)EOFS_GFI_TRIP_CNT,m_GfiTripCnt); } From b2c27979775fe8daea353e6452777aecba3bf4de Mon Sep 17 00:00:00 2001 From: lincomatic Date: Sun, 7 Jun 2015 13:24:35 -0700 Subject: [PATCH 44/96] tripcnt not incrementing fix #2 --- J1772EvseController.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 75368c16..cbd45788 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -970,8 +970,7 @@ void J1772EVSEController::Update() m_EvseState = EVSE_STATE_NO_GROUND; chargingOff(); // open the relay - - if ((prevevsestate != EVSE_STATE_NO_GROUND) && (m_NoGndTripCnt+1 < 254)) { + if ((prevevsestate != EVSE_STATE_NO_GROUND) && (((uint8_t)(m_NoGndTripCnt+1)) < 254)) { m_NoGndTripCnt++; eeprom_write_byte((uint8_t*)EOFS_NOGND_TRIP_CNT,m_NoGndTripCnt); } @@ -1021,7 +1020,7 @@ void J1772EVSEController::Update() ((curms - m_StuckRelayStartTimeMS) > STUCK_RELAY_DELAY) ) || // start delay de-bounce (prevevsestate == EVSE_STATE_STUCK_RELAY) ) { // already in error state // stuck relay - if ((prevevsestate != EVSE_STATE_STUCK_RELAY) && (m_StuckRelayTripCnt+1 < 254)) { + if ((prevevsestate != EVSE_STATE_STUCK_RELAY) && (((uint8_t)(m_StuckRelayTripCnt+1)) < 254)) { m_StuckRelayTripCnt++; eeprom_write_byte((uint8_t*)EOFS_STUCK_RELAY_TRIP_CNT,m_StuckRelayTripCnt); } @@ -1041,7 +1040,7 @@ void J1772EVSEController::Update() m_EvseState = EVSE_STATE_GFCI_FAULT; if (prevevsestate != EVSE_STATE_GFCI_FAULT) { // state transition - if (m_GfiTripCnt+1 < 254) { + if (((uint8_t)(m_GfiTripCnt+1)) < 254) { m_GfiTripCnt++; eeprom_write_byte((uint8_t*)EOFS_GFI_TRIP_CNT,m_GfiTripCnt); } From 1166a34e6d35bac0802e98ab8578d9b7dd210b47 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Mon, 8 Jun 2015 10:28:36 -0700 Subject: [PATCH 45/96] Reading temperatures in all EVSE States now. --- J1772EvseController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index cbd45788..3899502b 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -1346,7 +1346,7 @@ void J1772EVSEController::Update() if (m_ElapsedChargeTime != m_ElapsedChargeTimePrev) { uint8_t currcap = eeprom_read_byte((uint8_t*) ((GetCurSvcLevel() == 2) ? EOFS_CURRENT_CAPACITY_L2 : EOFS_CURRENT_CAPACITY_L1)); uint8_t setit = 0; - g_TempMonitor.Read(); + // g_TempMonitor.Read(); // moved this to main update loop so it reads temperatures in all EVSE states if (!g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_THROTTLE_DOWN ) || // any sensor reaching threshold trips action (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // Throttle back the L2 current advice to the EV From 946627dfde33e512e8a7dae8ff461e5f09d5c479 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Mon, 8 Jun 2015 10:29:55 -0700 Subject: [PATCH 46/96] Reading temperatures in all EVSE States now. --- open_evse.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/open_evse.ino b/open_evse.ino index 53023151..ea686c1f 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -637,6 +637,8 @@ void OnboardDisplay::Update(int8_t updmode) // if ((curms-m_LastUpdateMs) >= 1000) { m_LastUpdateMs = curms; + + g_TempMonitor.Read(); // update temperatures once per second if (!g_EvseController.InHardFault() && ((curstate == EVSE_STATE_GFCI_FAULT) || (curstate == EVSE_STATE_NO_GROUND))) { From 26db43996d7e92dd2f15db3c751660f81900b183 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Mon, 8 Jun 2015 16:30:57 -0700 Subject: [PATCH 47/96] restore I2CLCD_PCF8574 build capability --- I2CIO.cpp | 198 ++++++++++++++++ I2CIO.h | 148 ++++++++++++ LCD.cpp | 347 +++++++++++++++++++++++++++ LCD.h | 536 ++++++++++++++++++++++++++++++++++++++++++ LiquidCrystal_I2C.cpp | 291 +++++++++++++++++++++++ LiquidCrystal_I2C.h | 204 ++++++++++++++++ LiquidTWI2.cpp | 3 + open_evse.h | 7 +- open_evse.ino | 2 +- 9 files changed, 1729 insertions(+), 7 deletions(-) create mode 100644 I2CIO.cpp create mode 100644 I2CIO.h create mode 100644 LCD.cpp create mode 100644 LCD.h create mode 100644 LiquidCrystal_I2C.cpp create mode 100644 LiquidCrystal_I2C.h diff --git a/I2CIO.cpp b/I2CIO.cpp new file mode 100644 index 00000000..99061552 --- /dev/null +++ b/I2CIO.cpp @@ -0,0 +1,198 @@ +// --------------------------------------------------------------------------- +// Created by Francisco Malpartida on 20/08/11. +// Copyright 2011 - Under creative commons license 3.0: +// Attribution-ShareAlike CC BY-SA +// +// This software is furnished "as is", without technical support, and with no +// warranty, express or implied, as to its usefulness for any purpose. +// +// Thread Safe: No +// Extendable: Yes +// +// @file I2CIO.h +// This file implements a basic IO library using the PCF8574 I2C IO Expander +// chip. +// +// @brief +// Implement a basic IO library to drive the PCF8574* I2C IO Expander ASIC. +// The library implements basic IO general methods to configure IO pin direction +// read and write uint8_t operations and basic pin level routines to set or read +// a particular IO port. +// +// +// @version API 1.0.0 +// +// @author F. Malpartida - fmalpartida@gmail.com +// --------------------------------------------------------------------------- +#if (ARDUINO < 100) +#include +#else +#include +#endif + +#include + +#include "./Wire.h" +#include "./I2CIO.h" + +// CLASS VARIABLES +// --------------------------------------------------------------------------- + + +// CONSTRUCTOR +// --------------------------------------------------------------------------- +I2CIO::I2CIO ( ) +{ + _i2cAddr = 0x0; + _dirMask = 0xFF; // mark all as INPUTs + _shadow = 0x0; // no values set + _initialised = false; +} + +// PUBLIC METHODS +// --------------------------------------------------------------------------- + +// +// begin +int I2CIO::begin ( uint8_t i2cAddr ) +{ + _i2cAddr = i2cAddr; + + Wire.begin ( ); + + _initialised = Wire.requestFrom ( _i2cAddr, (uint8_t)1 ); + +#if (ARDUINO < 100) + _shadow = Wire.receive (); +#else + _shadow = Wire.read (); // Remove the byte read don't need it. +#endif + + return ( _initialised ); +} + +// +// pinMode +void I2CIO::pinMode ( uint8_t pin, uint8_t dir ) +{ + if ( _initialised ) + { + if ( OUTPUT == dir ) + { + _dirMask &= ~( 1 << pin ); + } + else + { + _dirMask |= ( 1 << pin ); + } + } +} + +// +// portMode +void I2CIO::portMode ( uint8_t dir ) +{ + + if ( _initialised ) + { + if ( dir == INPUT ) + { + _dirMask = 0xFF; + } + else + { + _dirMask = 0x00; + } + } +} + +// +// read +uint8_t I2CIO::read ( void ) +{ + uint8_t retVal = 0; + + if ( _initialised ) + { + Wire.requestFrom ( _i2cAddr, (uint8_t)1 ); +#if (ARDUINO < 100) + retVal = ( _dirMask & Wire.receive ( ) ); +#else + retVal = ( _dirMask & Wire.read ( ) ); +#endif + + } + return ( retVal ); +} + +// +// write +int I2CIO::write ( uint8_t value ) +{ + int status = 0; + + if ( _initialised ) + { + // Only write HIGH the values of the ports that have been initialised as + // outputs updating the output shadow of the device + _shadow = ( value & ~(_dirMask) ); + + Wire.beginTransmission ( _i2cAddr ); +#if (ARDUINO < 100) + Wire.send ( _shadow ); +#else + Wire.write ( _shadow ); +#endif + status = Wire.endTransmission (); + } + return ( (status == 0) ); +} + +// +// digitalRead +uint8_t I2CIO::digitalRead ( uint8_t pin ) +{ + uint8_t pinVal = 0; + + // Check if initialised and that the pin is within range of the device + // ------------------------------------------------------------------- + if ( ( _initialised ) && ( pin <= 7 ) ) + { + // Remove the values which are not inputs and get the value of the pin + pinVal = this->read() & _dirMask; + pinVal = ( pinVal >> pin ) & 0x01; // Get the pin value + } + return (pinVal); +} + +// +// digitalWrite +int I2CIO::digitalWrite ( uint8_t pin, uint8_t level ) +{ + uint8_t writeVal; + int status = 0; + + // Check if initialised and that the pin is within range of the device + // ------------------------------------------------------------------- + if ( ( _initialised ) && ( pin <= 7 ) ) + { + // Only write to HIGH the port if the port has been configured as + // an OUTPUT pin. Add the new state of the pin to the shadow + writeVal = ( 1 << pin ) & ~_dirMask; + if ( level == HIGH ) + { + _shadow |= writeVal; + + } + else + { + _shadow &= ~writeVal; + } + status = this->write ( _shadow ); + } + return ( status ); +} + +// +// PRIVATE METHODS +// --------------------------------------------------------------------------- diff --git a/I2CIO.h b/I2CIO.h new file mode 100644 index 00000000..e05f16b1 --- /dev/null +++ b/I2CIO.h @@ -0,0 +1,148 @@ +// --------------------------------------------------------------------------- +// Created by Francisco Malpartida on 20/08/11. +// Copyright 2011 - Under creative commons license 3.0: +// Attribution-ShareAlike CC BY-SA +// +// This software is furnished "as is", without technical support, and with no +// warranty, express or implied, as to its usefulness for any purpose. +// +// Thread Safe: No +// Extendable: Yes +// +// @file I2CIO.h +// This file implements a basic IO library using the PCF8574 I2C IO Expander +// chip. +// +// @brief +// Implement a basic IO library to drive the PCF8574* I2C IO Expander ASIC. +// The library implements basic IO general methods to configure IO pin direction +// read and write uint8_t operations and basic pin level routines to set or read +// a particular IO port. +// +// @version API 1.0.0 +// +// @author F. Malpartida - fmalpartida@gmail.com +// --------------------------------------------------------------------------- + +#ifndef _I2CIO_H_ +#define _I2CIO_H_ + +#include + +#define _I2CIO_VERSION "1.0.0" + +/*! + @class + @abstract I2CIO + @discussion Library driver to control PCF8574 based ASICs. Implementing + library calls to set/get port through I2C bus. + */ + +class I2CIO +{ +public: + /*! + @method + @abstract Constructor method + @discussion Class constructor constructor. + */ + I2CIO ( ); + + /*! + @method + @abstract Initializes the device. + @discussion This method initializes the device allocating an I2C address. + This method is the first method that should be call prior to calling any + other method form this class. On initialization all pins are configured + as INPUT on the device. + + @param i2cAddr: I2C Address where the device is located. + @result 1 if the device was initialized correctly, 0 otherwise + */ + int begin ( uint8_t i2cAddr ); + + /*! + @method + @abstract Sets the mode of a particular pin. + @discussion Sets the mode of a particular pin to INPUT, OUTPUT. digitalWrite + has no effect on pins which are not declared as output. + + @param pin[in] Pin from the I2C IO expander to be configured. Range 0..7 + @param dir[in] Pin direction (INPUT, OUTPUT). + */ + void pinMode ( uint8_t pin, uint8_t dir ); + + /*! + @method + @abstract Sets all the pins of the device in a particular direction. + @discussion This method sets all the pins of the device in a particular + direction. This method is useful to set all the pins of the device to be + either inputs or outputs. + @param dir[in] Direction of all the pins of the device (INPUT, OUTPUT). + */ + void portMode ( uint8_t dir ); + + /*! + @method + @abstract Reads all the pins of the device that are configured as INPUT. + @discussion Reads from the device the status of the pins that are configured + as INPUT. During initialization all pins are configured as INPUTs by default. + Please refer to pinMode or portMode. + + @param none + */ + uint8_t read ( void ); + + /*! + @method + @abstract Read a pin from the device. + @discussion Reads a particular pin from the device. To read a particular + pin it has to be configured as INPUT. During initialization all pins are + configured as INPUTs by default. Please refer to pinMode or portMode. + + @param pin[in] Pin from the port to read its status. Range (0..7) + @result Returns the pin status (HIGH, LOW) if the pin is configured + as an output, reading its value will always return LOW regardless of its + real state. + */ + uint8_t digitalRead ( uint8_t pin ); + + /*! + @method + @abstract Write a value to the device. + @discussion Writes to a set of pins in the device. The value is the binary + representation of all the pins in device. The value written is masked with + the configuration of the direction of the pins; to change the state of + a particular pin with this method, such pin has to be configured as OUTPUT + using the portMode or pinMode methods. If no pins have been configured as + OUTPUTs this method will have no effect. + + @param value[in] value to be written to the device. + @result 1 on success, 0 otherwise + */ + int write ( uint8_t value ); + + /*! + @method + @abstract Writes a digital level to a particular pin. + @discussion Write a level to the indicated pin of the device. For this + method to have effect, the pin has to be configured as OUTPUT using the + pinMode or portMode methods. + + @param pin[in] device pin to change level. Range (0..7). + @para level[in] logic level to set the pin at (HIGH, LOW). + @result 1 on success, 0 otherwise. + */ + int digitalWrite ( uint8_t pin, uint8_t level ); + + + +private: + uint8_t _shadow; // Shadow output + uint8_t _dirMask; // Direction mask + uint8_t _i2cAddr; // I2C address + bool _initialised; // Initialised object + +}; + +#endif \ No newline at end of file diff --git a/LCD.cpp b/LCD.cpp new file mode 100644 index 00000000..0c86d972 --- /dev/null +++ b/LCD.cpp @@ -0,0 +1,347 @@ +// --------------------------------------------------------------------------- +// Created by Francisco Malpartida on 20/08/11. +// Copyright 2011 - Under creative commons license 3.0: +// Attribution-ShareAlike CC BY-SA +// +// This software is furnished "as is", without technical support, and with no +// warranty, express or implied, as to its usefulness for any purpose. +// +// Thread Safe: No +// Extendable: Yes +// +// @file LCD.cpp +// This file implements a basic liquid crystal library that comes as standard +// in the Arduino SDK. +// +// @brief +// This is a basic implementation of the HD44780 library of the +// Arduino SDK. This library is a refactored version of the one supplied +// in the Arduino SDK in such a way that it simplifies its extension +// to support other mechanism to communicate to LCDs such as I2C, Serial, SR, ... +// The original library has been reworked in such a way that this will be +// the base class implementing all generic methods to command an LCD based +// on the Hitachi HD44780 and compatible chipsets. +// +// This base class is a pure abstract class and needs to be extended. As reference, +// it has been extended to drive 4 and 8 bit mode control, LCDs and I2C extension +// backpacks such as the I2CLCDextraIO using the PCF8574* I2C IO Expander ASIC. +// +// +// @version API 1.1.0 +// +// 2012.03.29 bperrybap - changed comparision to use LCD_5x8DOTS rather than 0 +// @author F. Malpartida - fmalpartida@gmail.com +// --------------------------------------------------------------------------- +#include +#include +#include + +#if (ARDUINO < 100) +#include +#else +#include +#endif +#include "./LCD.h" + +// CLASS CONSTRUCTORS +// --------------------------------------------------------------------------- +// Constructor +LCD::LCD () +{ + +} + +// PUBLIC METHODS +// --------------------------------------------------------------------------- +// When the display powers up, it is configured as follows: +// +// 1. Display clear +// 2. Function set: +// DL = 1; 8-bit interface data +// N = 0; 1-line display +// F = 0; 5x8 dot character font +// 3. Display on/off control: +// D = 0; Display off +// C = 0; Cursor off +// B = 0; Blinking off +// 4. Entry mode set: +// I/D = 1; Increment by 1 +// S = 0; No shift +// +// Note, however, that resetting the Arduino doesn't reset the LCD, so we +// can't assume that its in that state when a sketch starts (and the +// LiquidCrystal constructor is called). +// A call to begin() will reinitialize the LCD. +// +void LCD::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) +{ + if (lines > 1) + { + _displayfunction |= LCD_2LINE; + } + _numlines = lines; + _cols = cols; + + // for some 1 line displays you can select a 10 pixel high font + // ------------------------------------------------------------ + if ((dotsize != LCD_5x8DOTS) && (lines == 1)) + { + _displayfunction |= LCD_5x10DOTS; + } + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way before 4.5V so we'll wait + // 50 + // --------------------------------------------------------------------------- + delay (100); // 100ms delay + + //put the LCD into 4 bit or 8 bit mode + // ------------------------------------- + if (! (_displayfunction & LCD_8BITMODE)) + { + // this is according to the hitachi HD44780 datasheet + // figure 24, pg 46 + + // we start in 8bit mode, try to set 4 bit mode + send(0x03, FOUR_BITS); + delayMicroseconds(4500); // wait min 4.1ms + + // second try + send ( 0x03, FOUR_BITS ); + delayMicroseconds(4500); // wait min 4.1ms + + // third go! + send( 0x03, FOUR_BITS ); + delayMicroseconds(150); + + // finally, set to 4-bit interface + send ( 0x02, FOUR_BITS ); + } + else + { + // this is according to the hitachi HD44780 datasheet + // page 45 figure 23 + + // Send function set command sequence + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(4500); // wait more than 4.1ms + + // second try + command(LCD_FUNCTIONSET | _displayfunction); + delayMicroseconds(150); + + // third go + command(LCD_FUNCTIONSET | _displayfunction); + } + + // finally, set # lines, font size, etc. + command(LCD_FUNCTIONSET | _displayfunction); + + // turn the display on with no cursor or blinking default + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; + display(); + + // clear the LCD + clear(); + + // Initialize to default text direction (for romance languages) + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; + // set the entry mode + command(LCD_ENTRYMODESET | _displaymode); + + backlight(); + +} + +// Common LCD Commands +// --------------------------------------------------------------------------- +void LCD::clear() +{ + command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero + delayMicroseconds(HOME_CLEAR_EXEC); // this command is time consuming +} + +void LCD::home() +{ + command(LCD_RETURNHOME); // set cursor position to zero + delayMicroseconds(HOME_CLEAR_EXEC); // This command is time consuming +} + +void LCD::setCursor(uint8_t col, uint8_t row) +{ + const byte row_offsetsDef[] = { 0x00, 0x40, 0x14, 0x54 }; // For regular LCDs + const byte row_offsetsLarge[] = { 0x00, 0x40, 0x10, 0x50 }; // For 16x4 LCDs + + if ( row >= _numlines ) + { + row = _numlines-1; // rows start at 0 + } + + // 16x4 LCDs have special memory map layout + // ---------------------------------------- + if ( _cols == 16 && _numlines == 4 ) + { + command(LCD_SETDDRAMADDR | (col + row_offsetsLarge[row])); + } + else + { + command(LCD_SETDDRAMADDR | (col + row_offsetsDef[row])); + } + +} + +// Turn the display on/off +void LCD::noDisplay() +{ + _displaycontrol &= ~LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +void LCD::display() +{ + _displaycontrol |= LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turns the underline cursor on/off +void LCD::noCursor() +{ + _displaycontrol &= ~LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LCD::cursor() +{ + _displaycontrol |= LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turns on/off the blinking cursor +void LCD::noBlink() +{ + _displaycontrol &= ~LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +void LCD::blink() +{ + _displaycontrol |= LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// These commands scroll the display without changing the RAM +void LCD::scrollDisplayLeft(void) +{ + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); +} + +void LCD::scrollDisplayRight(void) +{ + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); +} + +// This is for text that flows Left to Right +void LCD::leftToRight(void) +{ + _displaymode |= LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This is for text that flows Right to Left +void LCD::rightToLeft(void) +{ + _displaymode &= ~LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This method moves the cursor one space to the right +void LCD::moveCursorRight(void) +{ + command(LCD_CURSORSHIFT | LCD_CURSORMOVE | LCD_MOVERIGHT); +} + +// This method moves the cursor one space to the left +void LCD::moveCursorLeft(void) +{ + command(LCD_CURSORSHIFT | LCD_CURSORMOVE | LCD_MOVELEFT); +} + + +// This will 'right justify' text from the cursor +void LCD::autoscroll(void) +{ + _displaymode |= LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'left justify' text from the cursor +void LCD::noAutoscroll(void) +{ + _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// Write to CGRAM of new characters +void LCD::createChar(uint8_t location, uint8_t charmap[]) +{ + location &= 0x7; // we only have 8 locations 0-7 + + command(LCD_SETCGRAMADDR | (location << 3)); + delayMicroseconds(30); + + for (int i=0; i<8; i++) + { + write(charmap[i]); // call the virtual write method + delayMicroseconds(40); + } +} + +// +// Switch on the backlight +void LCD::backlight ( void ) +{ + setBacklight(255); +} + +// +// Switch off the backlight +void LCD::noBacklight ( void ) +{ + setBacklight(0); +} + +// +// Switch fully on the LCD (backlight and LCD) +void LCD::on ( void ) +{ + display(); + backlight(); +} + +// +// Switch fully off the LCD (backlight and LCD) +void LCD::off ( void ) +{ + noBacklight(); + noDisplay(); +} + +// General LCD commands - generic methods used by the rest of the commands +// --------------------------------------------------------------------------- +void LCD::command(uint8_t value) +{ + send(value, COMMAND); +} + +#if (ARDUINO < 100) +void LCD::write(uint8_t value) +{ + send(value, DATA); +} +#else +size_t LCD::write(uint8_t value) +{ + send(value, DATA); + return 1; // assume OK +} +#endif diff --git a/LCD.h b/LCD.h new file mode 100644 index 00000000..88a58ad1 --- /dev/null +++ b/LCD.h @@ -0,0 +1,536 @@ +// --------------------------------------------------------------------------- +// Created by Francisco Malpartida on 20/08/11. +// Copyright 2011 - Under creative commons license 3.0: +// Attribution-ShareAlike CC BY-SA +// +// This software is furnished "as is", without technical support, and with no +// warranty, express or implied, as to its usefulness for any purpose. +// +// Thread Safe: No +// Extendable: Yes +// +// @file LCD.h +// This file implements a basic liquid crystal library that comes as standard +// in the Arduino SDK. +// +// @brief +// This is a basic implementation of the LiquidCrystal library of the +// Arduino SDK. This library is a refactored version of the one supplied +// in the Arduino SDK in such a way that it simplifies its extension +// to support other mechanism to communicate to LCDs such as I2C, Serial, SR, +// The original library has been reworked in such a way that this will be +// the base class implementing all generic methods to command an LCD based +// on the Hitachi HD44780 and compatible chipsets. +// +// This base class is a pure abstract class and needs to be extended. As reference, +// it has been extended to drive 4 and 8 bit mode control, LCDs and I2C extension +// backpacks such as the I2CLCDextraIO using the PCF8574* I2C IO Expander ASIC. +// +// The functionality provided by this class and its base class is identical +// to the original functionality of the Arduino LiquidCrystal library. +// +// @version API 1.1.0 +// +// +// @author F. Malpartida - fmalpartida@gmail.com +// --------------------------------------------------------------------------- +#ifndef _LCD_H_ +#define _LCD_H_ + +#if (ARDUINO < 100) +#include +#else +#include +#endif + +#include +#include + + +/*! + @defined + @abstract Enables disables fast waits for write operations for LCD + @discussion If defined, the library will avoid doing un-necessary waits. + this can be done, because the time taken by Arduino's slow digitalWrite + operations. If fast digitalIO operations, comment this line out or undefine + the mode. + */ +#ifdef __AVR__ +#define FAST_MODE +#endif + +/*! + @function + @abstract waits for a given time in microseconds (compilation dependent). + @discussion Waits for a given time defined in microseconds depending on + the FAST_MODE define. If the FAST_MODE is defined the call will return + inmediatelly. + @param uSec[in] time in microseconds. + @result None + */ +inline static void waitUsec ( uint16_t uSec ) +{ +#ifndef FAST_MODE + delayMicroseconds ( uSec ); +#endif // FAST_MODE +} + + +/*! + @defined + @abstract All these definitions shouldn't be used unless you are writing + a driver. + @discussion All these definitions are for driver implementation only and + shouldn't be used by applications. + */ +// LCD Commands +// --------------------------------------------------------------------------- +#define LCD_CLEARDISPLAY 0x01 +#define LCD_RETURNHOME 0x02 +#define LCD_ENTRYMODESET 0x04 +#define LCD_DISPLAYCONTROL 0x08 +#define LCD_CURSORSHIFT 0x10 +#define LCD_FUNCTIONSET 0x20 +#define LCD_SETCGRAMADDR 0x40 +#define LCD_SETDDRAMADDR 0x80 + +// flags for display entry mode +// --------------------------------------------------------------------------- +#define LCD_ENTRYRIGHT 0x00 +#define LCD_ENTRYLEFT 0x02 +#define LCD_ENTRYSHIFTINCREMENT 0x01 +#define LCD_ENTRYSHIFTDECREMENT 0x00 + +// flags for display on/off and cursor control +// --------------------------------------------------------------------------- +#define LCD_DISPLAYON 0x04 +#define LCD_DISPLAYOFF 0x00 +#define LCD_CURSORON 0x02 +#define LCD_CURSOROFF 0x00 +#define LCD_BLINKON 0x01 +#define LCD_BLINKOFF 0x00 + +// flags for display/cursor shift +// --------------------------------------------------------------------------- +#define LCD_DISPLAYMOVE 0x08 +#define LCD_CURSORMOVE 0x00 +#define LCD_MOVERIGHT 0x04 +#define LCD_MOVELEFT 0x00 + +// flags for function set +// --------------------------------------------------------------------------- +#define LCD_8BITMODE 0x10 +#define LCD_4BITMODE 0x00 +#define LCD_2LINE 0x08 +#define LCD_1LINE 0x00 +#define LCD_5x10DOTS 0x04 +#define LCD_5x8DOTS 0x00 + + +// Define COMMAND and DATA LCD Rs (used by send method). +// --------------------------------------------------------------------------- +#define COMMAND 0 +#define DATA 1 +#define FOUR_BITS 2 + + +/*! + @defined + @abstract Defines the duration of the home and clear commands + @discussion This constant defines the time it takes for the home and clear + commands in the LCD - Time in microseconds. + */ +#define HOME_CLEAR_EXEC 2000 + +/*! + @defined + @abstract Backlight off constant declaration + @discussion Used in combination with the setBacklight to swith off the + LCD backlight. @set setBacklight +*/ +#define BACKLIGHT_OFF 0 + +/*! + @defined + @abstract Backlight on constant declaration + @discussion Used in combination with the setBacklight to swith on the + LCD backlight. @set setBacklight + */ +#define BACKLIGHT_ON 255 + + +/*! + @typedef + @abstract Define backlight control polarity + @discussion Backlight control polarity. @see setBacklightPin. + */ +typedef enum { POSITIVE, NEGATIVE } t_backlighPol; + +class LCD : public Print +{ +public: + + /*! + @method + @abstract LiquidCrystal abstract constructor. + @discussion LiquidCrystal class abstract constructor needed to create + the base abstract class. + */ + LCD ( ); + + /*! + @function + @abstract LCD initialization. + @discussion Initializes the LCD to a given size (col, row). This methods + initializes the LCD, therefore, it MUST be called prior to using any other + method from this class. + + This method is abstract, a base implementation is available common to all LCD + drivers. Should it not be compatible with some other LCD driver, a derived + implementation should be done on the driver specif class. + + @param cols[in] the number of columns that the display has + @param rows[in] the number of rows that the display has + @param charsize[in] character size, default==LCD_5x8DOTS + */ + virtual void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS); + + /*! + @function + @abstract Clears the LCD. + @discussion Clears the LCD screen and positions the cursor in the upper-left + corner. + + This operation is time consuming for the LCD. + + @param none + */ + void clear(); + + /*! + @function + @abstract Sets the cursor to the upper-left corner. + @discussion Positions the cursor in the upper-left of the LCD. + That is, use that location in outputting subsequent text to the display. + To also clear the display, use the clear() function instead. + + This operation is time consuming for the LCD. + + @param none + */ + void home(); + + /*! + @function + @abstract Turns off the LCD display. + @discussion Turns off the LCD display, without losing the text currently + being displayed on it. + + @param none + */ + void noDisplay(); + + /*! + @function + @abstract Turns on the LCD display. + @discussion Turns on the LCD display, after it's been turned off with + noDisplay(). This will restore the text (and cursor location) that was on + the display prior to calling noDisplay(). + + @param none + */ + void display(); + + /*! + @function + @abstract Turns off the blinking of the LCD cursor. + + @param none + */ + void noBlink(); + + /*! + @function + @abstract Display the cursor of the LCD. + @discussion Display the blinking LCD cursor. If used in combination with + the cursor() function, the result will depend on the particular display. + + @param none + */ + void blink(); + + /*! + @function + @abstract Hides the LCD cursor. + + @param none + */ + void noCursor(); + + /*! + @function + @abstract Display the LCD cursor. + @discussion Display the LCD cursor: an underscore (line) at the location + where the next character will be written. + + @param none + */ + void cursor(); + + /*! + @function + @abstract Scrolls the contents of the display (text and cursor) one space + to the left. + + @param none + */ + void scrollDisplayLeft(); + + /*! + @function + @abstract Scrolls the contents of the display (text and cursor) one space + to the right. + + @param none + */ + void scrollDisplayRight(); + + /*! + @function + @abstract Set the direction for text written to the LCD to left-to-right. + @discussion Set the direction for text written to the LCD to left-to-right. + All subsequent characters written to the display will go from left to right, + but does not affect previously-output text. + + This is the default configuration. + + @param none + */ + void leftToRight(); + + /*! + @function + @abstract Set the direction for text written to the LCD to right-to-left. + @discussion Set the direction for text written to the LCD to right-to-left. + All subsequent characters written to the display will go from right to left, + but does not affect previously-output text. + + left-to-right is the default configuration. + + @param none + */ + void rightToLeft(); + + /*! + @function + @abstract Moves the cursor one space to the left. + @discussion + @param none + */ + void moveCursorLeft(); + + + /*! + @function + @abstract Moves the cursor one space to the right. + + @param none + */ + void moveCursorRight(); + + /*! + @function + @abstract Turns on automatic scrolling of the LCD. + @discussion Turns on automatic scrolling of the LCD. This causes each + character output to the display to push previous characters over by one + space. If the current text direction is left-to-right (the default), + the display scrolls to the left; if the current direction is right-to-left, + the display scrolls to the right. + This has the effect of outputting each new character to the same location on + the LCD. + + @param none + */ + void autoscroll(); + + /*! + @function + @abstract Turns off automatic scrolling of the LCD. + @discussion Turns off automatic scrolling of the LCD, this is the default + configuration of the LCD. + + @param none + */ + void noAutoscroll(); + + /*! + @function + @abstract Creates a custom character for use on the LCD. + @discussion Create a custom character (glyph) for use on the LCD. + Most chipsets only support up to eight characters of 5x8 pixels. Therefore, + this methods has been limited to locations (numbered 0 to 7). + + The appearance of each custom character is specified by an array of eight + bytes, one for each row. The five least significant bits of each byte + determine the pixels in that row. To display a custom character on screen, + write()/print() its number, i.e. lcd.print (char(x)); // Where x is 0..7. + + @param location[in] LCD memory location of the character to create + (0 to 7) + @param charmap[in] the bitmap array representing each row of the character. + */ + void createChar(uint8_t location, uint8_t charmap[]); + + /*! + @function + @abstract Position the LCD cursor. + @discussion Sets the position of the LCD cursor. Set the location at which + subsequent text written to the LCD will be displayed. + + @param col[in] LCD column + @param row[in] LCD row - line. + */ + void setCursor(uint8_t col, uint8_t row); + + /*! + @function + @abstract Switch-on the LCD backlight. + @discussion Switch-on the LCD backlight. + The setBacklightPin has to be called before setting the backlight for + this method to work. @see setBacklightPin. + */ + void backlight ( void ); + + /*! + @function + @abstract Switch-off the LCD backlight. + @discussion Switch-off the LCD backlight. + The setBacklightPin has to be called before setting the backlight for + this method to work. @see setBacklightPin. + */ + void noBacklight ( void ); + + /*! + @function + @abstract Switch on the LCD module. + @discussion Switch on the LCD module, it will switch on the LCD controller + and the backlight. This method has the same effect of calling display and + backlight. @see display, @see backlight + */ + void on ( void ); + + /*! + @function + @abstract Switch off the LCD module. + @discussion Switch off the LCD module, it will switch off the LCD controller + and the backlight. This method has the same effect of calling noDisplay and + noBacklight. @see display, @see backlight + */ + void off ( void ); + + // + // virtual class methods + // -------------------------------------------------------------------------- + /*! + @function + @abstract Sets the pin to control the backlight. + @discussion Sets the pin in the device to control the backlight. + This method is device dependent and can be programmed on each subclass. An + empty function call is provided that does nothing. + + @param value: pin associated to backlight control. + @param pol: backlight polarity control (POSITIVE, NEGATIVE) + */ + virtual void setBacklightPin ( uint8_t value, t_backlighPol pol ) { }; + + /*! + @function + @abstract Sets the pin to control the backlight. + @discussion Sets the pin in the device to control the backlight. The behaviour + of this method is very dependent on the device. Some controllers support + dimming some don't. Please read the actual header file for each individual + device. The setBacklightPin method has to be called before setting the backlight + or the adequate backlight control constructor. + @see setBacklightPin. + + NOTE: The prefered methods to control the backlight are "backlight" and + "noBacklight". + + @param 0..255 - the value is very dependent on the LCD. However, + BACKLIGHT_OFF will be interpreted as off and BACKLIGHT_ON will drive the + backlight on. + */ + virtual void setBacklight ( uint8_t value ) { }; + + /*! + @function + @abstract Writes to the LCD. + @discussion This method writes character to the LCD in the current cursor + position. + + This is the virtual write method, implemented in the Print class, therefore + all Print class methods will end up calling this method. + + @param value[in] Value to write to the LCD. + */ +#if (ARDUINO < 100) + virtual void write(uint8_t value); +#else + virtual size_t write(uint8_t value); +#endif + +#if (ARDUINO < 100) + using Print::write; +#else + using Print::write; +#endif + +protected: + // Internal LCD variables to control the LCD shared between all derived + // classes. + uint8_t _displayfunction; // LCD_5x10DOTS or LCD_5x8DOTS, LCD_4BITMODE or + // LCD_8BITMODE, LCD_1LINE or LCD_2LINE + uint8_t _displaycontrol; // LCD base control command LCD on/off, blink, cursor + // all commands are "ored" to its contents. + uint8_t _displaymode; // Text entry mode to the LCD + uint8_t _numlines; // Number of lines of the LCD, initialized with begin() + uint8_t _cols; // Number of columns in the LCD + t_backlighPol _polarity; // Backlight polarity + +private: + /*! + @function + @abstract Send a command to the LCD. + @discussion This method sends a command to the LCD by setting the Register + select line of the LCD. + + This command shouldn't be used to drive the LCD, only to implement any other + feature that is not available on this library. + + @param value[in] Command value to send to the LCD (COMMAND, DATA or + FOUR_BITS). + */ + void command(uint8_t value); + + /*! + @function + @abstract Send a particular value to the LCD. + @discussion Sends a particular value to the LCD. This is a pure abstract + method, therefore, it is implementation dependent of each derived class how + to physically write to the LCD. + + Users should never call this method. + + @param value[in] Value to send to the LCD. + @result mode LOW - write to the LCD CGRAM, HIGH - write a command to + the LCD. + */ +#if (ARDUINO < 100) + virtual void send(uint8_t value, uint8_t mode) { }; +#else + virtual void send(uint8_t value, uint8_t mode) = 0; +#endif + +}; + +#endif diff --git a/LiquidCrystal_I2C.cpp b/LiquidCrystal_I2C.cpp new file mode 100644 index 00000000..65269c2c --- /dev/null +++ b/LiquidCrystal_I2C.cpp @@ -0,0 +1,291 @@ +// --------------------------------------------------------------------------- +// Created by Francisco Malpartida on 20/08/11. +// Copyright 2011 - Under creative commons license 3.0: +// Attribution-ShareAlike CC BY-SA +// +// This software is furnished "as is", without technical support, and with no +// warranty, express or implied, as to its usefulness for any purpose. +// +// Thread Safe: No +// Extendable: Yes +// +// @file LiquidCrystal_I2C.c +// This file implements a basic liquid crystal library that comes as standard +// in the Arduino SDK but using an I2C IO extension board. +// +// @brief +// This is a basic implementation of the LiquidCrystal library of the +// Arduino SDK. The original library has been reworked in such a way that +// this class implements the all methods to command an LCD based +// on the Hitachi HD44780 and compatible chipsets using I2C extension +// backpacks such as the I2CLCDextraIO with the PCF8574* I2C IO Expander ASIC. +// +// The functionality provided by this class and its base class is identical +// to the original functionality of the Arduino LiquidCrystal library. +// +// +// +// @author F. Malpartida - fmalpartida@gmail.com +// --------------------------------------------------------------------------- +#if (ARDUINO < 100) +#include +#else +#include +#endif +#include +#include "./I2CIO.h" +#include "./LiquidCrystal_I2C.h" + +// CONSTANT definitions +// --------------------------------------------------------------------------- + +// flags for backlight control +/*! + @defined + @abstract LCD_NOBACKLIGHT + @discussion NO BACKLIGHT MASK + */ +#define LCD_NOBACKLIGHT 0x00 + +/*! + @defined + @abstract LCD_BACKLIGHT + @discussion BACKLIGHT MASK used when backlight is on + */ +#define LCD_BACKLIGHT 0xFF + + +// Default library configuration parameters used by class constructor with +// only the I2C address field. +// --------------------------------------------------------------------------- +/*! + @defined + @abstract Enable bit of the LCD + @discussion Defines the IO of the expander connected to the LCD Enable + */ +#define EN 6 // Enable bit + +/*! + @defined + @abstract Read/Write bit of the LCD + @discussion Defines the IO of the expander connected to the LCD Rw pin + */ +#define RW 5 // Read/Write bit + +/*! + @defined + @abstract Register bit of the LCD + @discussion Defines the IO of the expander connected to the LCD Register select pin + */ +#define RS 4 // Register select bit + +/*! + @defined + @abstract LCD dataline allocation this library only supports 4 bit LCD control + mode. + @discussion D4, D5, D6, D7 LCD data lines pin mapping of the extender module + */ +#define D4 0 +#define D5 1 +#define D6 2 +#define D7 3 + + +// CONSTRUCTORS +// --------------------------------------------------------------------------- +LiquidCrystal_I2C::LiquidCrystal_I2C( uint8_t lcd_Addr ) +{ + config(lcd_Addr, EN, RW, RS, D4, D5, D6, D7); +} + + +LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t backlighPin, + t_backlighPol pol = POSITIVE) +{ + config(lcd_Addr, EN, RW, RS, D4, D5, D6, D7); + setBacklightPin(backlighPin, pol); +} + +LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, + uint8_t Rs) +{ + config(lcd_Addr, En, Rw, Rs, D4, D5, D6, D7); +} + +LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, + uint8_t Rs, uint8_t backlighPin, + t_backlighPol pol = POSITIVE) +{ + config(lcd_Addr, En, Rw, Rs, D4, D5, D6, D7); + setBacklightPin(backlighPin, pol); +} + +LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, + uint8_t Rs, uint8_t d4, uint8_t d5, + uint8_t d6, uint8_t d7 ) +{ + config(lcd_Addr, En, Rw, Rs, d4, d5, d6, d7); +} + +LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, + uint8_t Rs, uint8_t d4, uint8_t d5, + uint8_t d6, uint8_t d7, uint8_t backlighPin, + t_backlighPol pol = POSITIVE ) +{ + config(lcd_Addr, En, Rw, Rs, d4, d5, d6, d7); + setBacklightPin(backlighPin, pol); +} + +// PUBLIC METHODS +// --------------------------------------------------------------------------- + +// +// begin +void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) +{ + + init(); // Initialise the I2C expander interface + LCD::begin ( cols, lines, dotsize ); +} + + +// User commands - users can expand this section +//---------------------------------------------------------------------------- +// Turn the (optional) backlight off/on + +// +// setBacklightPin +void LiquidCrystal_I2C::setBacklightPin ( uint8_t value, t_backlighPol pol = POSITIVE ) +{ + _backlightPinMask = ( 1 << value ); + _polarity = pol; + setBacklight(BACKLIGHT_OFF); +} + +// +// setBacklight +void LiquidCrystal_I2C::setBacklight( uint8_t value ) +{ + // Check if backlight is available + // ---------------------------------------------------- + if ( _backlightPinMask != 0x0 ) + { + // Check for polarity to configure mask accordingly + // ---------------------------------------------------------- + if (((_polarity == POSITIVE) && (value > 0)) || + ((_polarity == NEGATIVE ) && ( value == 0 ))) + { + _backlightStsMask = _backlightPinMask & LCD_BACKLIGHT; + } + else + { + _backlightStsMask = _backlightPinMask & LCD_NOBACKLIGHT; + } + _i2cio.write( _backlightStsMask ); + } +} + + +// PRIVATE METHODS +// --------------------------------------------------------------------------- + +// +// init +int LiquidCrystal_I2C::init() +{ + int status = 0; + + // initialize the backpack IO expander + // and display functions. + // ------------------------------------------------------------------------ + if ( _i2cio.begin ( _Addr ) == 1 ) + { + _i2cio.portMode ( OUTPUT ); // Set the entire IO extender to OUTPUT + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + status = 1; + _i2cio.write(0); // Set the entire port to LOW + } + return ( status ); +} + +// +// config +void LiquidCrystal_I2C::config (uint8_t lcd_Addr, uint8_t En, uint8_t Rw, uint8_t Rs, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7 ) +{ + _Addr = lcd_Addr; + + _backlightPinMask = 0; + _backlightStsMask = LCD_NOBACKLIGHT; + _polarity = POSITIVE; + + _En = ( 1 << En ); + _Rw = ( 1 << Rw ); + _Rs = ( 1 << Rs ); + + // Initialise pin mapping + _data_pins[0] = ( 1 << d4 ); + _data_pins[1] = ( 1 << d5 ); + _data_pins[2] = ( 1 << d6 ); + _data_pins[3] = ( 1 << d7 ); +} + + + +// low level data pushing commands +//---------------------------------------------------------------------------- + +// +// send - write either command or data +void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) +{ + // No need to use the delay routines since the time taken to write takes + // longer that what is needed both for toggling and enable pin an to execute + // the command. + + if ( mode == FOUR_BITS ) + { + write4bits( (value & 0x0F), COMMAND ); + } + else + { + write4bits( (value >> 4), mode ); + write4bits( (value & 0x0F), mode); + } +} + +// +// write4bits +void LiquidCrystal_I2C::write4bits ( uint8_t value, uint8_t mode ) +{ + uint8_t pinMapValue = 0; + + // Map the value to LCD pin mapping + // -------------------------------- + for ( uint8_t i = 0; i < 4; i++ ) + { + if ( ( value & 0x1 ) == 1 ) + { + pinMapValue |= _data_pins[i]; + } + value = ( value >> 1 ); + } + + // Is it a command or data + // ----------------------- + if ( mode == DATA ) + { + mode = _Rs; + } + + pinMapValue |= mode | _backlightStsMask; + pulseEnable ( pinMapValue ); +} + +// +// pulseEnable +void LiquidCrystal_I2C::pulseEnable (uint8_t data) +{ + _i2cio.write (data | _En); // En HIGH + _i2cio.write (data & ~_En); // En LOW +} diff --git a/LiquidCrystal_I2C.h b/LiquidCrystal_I2C.h new file mode 100644 index 00000000..ea6d87f7 --- /dev/null +++ b/LiquidCrystal_I2C.h @@ -0,0 +1,204 @@ +// --------------------------------------------------------------------------- +// Created by Francisco Malpartida on 20/08/11. +// Copyright 2011 - Under creative commons license 3.0: +// Attribution-ShareAlike CC BY-SA +// +// This software is furnished "as is", without technical support, and with no +// warranty, express or implied, as to its usefulness for any purpose. +// +// Thread Safe: No +// Extendable: Yes +// +// @file LiquidCrystal_I2C.h +// This file implements a basic liquid crystal library that comes as standard +// in the Arduino SDK but using an I2C IO extension board. +// +// @brief +// This is a basic implementation of the LiquidCrystal library of the +// Arduino SDK. The original library has been reworked in such a way that +// this class implements the all methods to command an LCD based +// on the Hitachi HD44780 and compatible chipsets using I2C extension +// backpacks such as the I2CLCDextraIO with the PCF8574* I2C IO Expander ASIC. +// +// The functionality provided by this class and its base class is identical +// to the original functionality of the Arduino LiquidCrystal library. +// +// +// @author F. Malpartida - fmalpartida@gmail.com +// --------------------------------------------------------------------------- +#ifndef LiquidCrystal_I2C_h +#define LiquidCrystal_I2C_h +#include +#include + +#include "I2CIO.h" +#include "LCD.h" + + +class LiquidCrystal_I2C : public LCD +{ +public: + + /*! + @method + @abstract Class constructor. + @discussion Initializes class variables and defines the I2C address of the + LCD. The constructor does not initialize the LCD. + + @param lcd_Addr[in] I2C address of the IO expansion module. For I2CLCDextraIO, + the address can be configured using the on board jumpers. + */ + LiquidCrystal_I2C (uint8_t lcd_Addr); + // Constructor with backlight control + LiquidCrystal_I2C (uint8_t lcd_Addr, uint8_t backlighPin, t_backlighPol pol); + + /*! + @method + @abstract Class constructor. + @discussion Initializes class variables and defines the I2C address of the + LCD. The constructor does not initialize the LCD. + + @param lcd_Addr[in] I2C address of the IO expansion module. For I2CLCDextraIO, + the address can be configured using the on board jumpers. + @param En[in] LCD En (Enable) pin connected to the IO extender module + @param Rw[in] LCD Rw (Read/write) pin connected to the IO extender module + @param Rs[in] LCD Rs (Reset) pin connected to the IO extender module + */ + LiquidCrystal_I2C( uint8_t lcd_Addr, uint8_t En, uint8_t Rw, uint8_t Rs); + // Constructor with backlight control + LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, uint8_t Rs, + uint8_t backlighPin, t_backlighPol pol); + + /*! + @method + @abstract Class constructor. + @discussion Initializes class variables and defines the I2C address of the + LCD. The constructor does not initialize the LCD. + + @param lcd_Addr[in] I2C address of the IO expansion module. For I2CLCDextraIO, + the address can be configured using the on board jumpers. + @param En[in] LCD En (Enable) pin connected to the IO extender module + @param Rw[in] LCD Rw (Read/write) pin connected to the IO extender module + @param Rs[in] LCD Rs (Reset) pin connected to the IO extender module + @param d4[in] LCD data 0 pin map on IO extender module + @param d5[in] LCD data 1 pin map on IO extender module + @param d6[in] LCD data 2 pin map on IO extender module + @param d7[in] LCD data 3 pin map on IO extender module + */ + LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, uint8_t Rs, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7 ); + // Constructor with backlight control + LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, uint8_t Rs, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7, + uint8_t backlighPin, t_backlighPol pol); + /*! + @function + @abstract LCD initialization and associated HW. + @discussion Initializes the LCD to a given size (col, row). This methods + initializes the LCD, therefore, it MUST be called prior to using any other + method from this class or parent class. + + The begin method can be overloaded if necessary to initialize any HW that + is implemented by a library and can't be done during construction, here + we use the Wire class. + + @param cols[in] the number of columns that the display has + @param rows[in] the number of rows that the display has + @param charsize[in] size of the characters of the LCD: LCD_5x8DOTS or + LCD_5x10DOTS. + */ + virtual void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS); + + /*! + @function + @abstract Send a particular value to the LCD. + @discussion Sends a particular value to the LCD for writing to the LCD or + as an LCD command. + + Users should never call this method. + + @param value[in] Value to send to the LCD. + @param mode[in] DATA - write to the LCD CGRAM, COMMAND - write a + command to the LCD. + */ + virtual void send(uint8_t value, uint8_t mode); + + /*! + @function + @abstract Sets the pin to control the backlight. + @discussion Sets the pin in the device to control the backlight. This device + doesn't support dimming backlight capability. + + @param 0: backlight off, 1..255: backlight on. + */ + void setBacklightPin ( uint8_t value, t_backlighPol pol ); + + /*! + @function + @abstract Switch-on/off the LCD backlight. + @discussion Switch-on/off the LCD backlight. + The setBacklightPin has to be called before setting the backlight for + this method to work. @see setBacklightPin. + + @param value: backlight mode (HIGH|LOW) + */ + void setBacklight ( uint8_t value ); + +private: + + /*! + @method + @abstract Initializes the LCD class + @discussion Initializes the LCD class and IO expansion module. + */ + int init(); + + /*! + @function + @abstract Initialises class private variables + @discussion This is the class single point for initialising private variables. + + @param lcd_Addr[in] I2C address of the IO expansion module. For I2CLCDextraIO, + the address can be configured using the on board jumpers. + @param En[in] LCD En (Enable) pin connected to the IO extender module + @param Rw[in] LCD Rw (Read/write) pin connected to the IO extender module + @param Rs[in] LCD Rs (Reset) pin connected to the IO extender module + @param d4[in] LCD data 0 pin map on IO extender module + @param d5[in] LCD data 1 pin map on IO extender module + @param d6[in] LCD data 2 pin map on IO extender module + @param d7[in] LCD data 3 pin map on IO extender module + */ + void config (uint8_t lcd_Addr, uint8_t En, uint8_t Rw, uint8_t Rs, + uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7 ); + + /*! + @method + @abstract Writes an 4 bit value to the LCD. + @discussion Writes 4 bits (the least significant) to the LCD control data lines. + @param value[in] Value to write to the LCD + @param more[in] Value to distinguish between command and data. + COMMAND == command, DATA == data. + */ + void write4bits(uint8_t value, uint8_t mode); + + /*! + @method + @abstract Pulse the LCD enable line (En). + @discussion Sends a pulse of 1 uS to the Enable pin to execute an command + or write operation. + */ + void pulseEnable(uint8_t); + + + uint8_t _Addr; // I2C Address of the IO expander + uint8_t _backlightPinMask; // Backlight IO pin mask + uint8_t _backlightStsMask; // Backlight status mask + I2CIO _i2cio; // I2CIO PCF8574* expansion module driver I2CLCDextraIO + uint8_t _En; // LCD expander word for enable pin + uint8_t _Rw; // LCD expander word for R/W pin + uint8_t _Rs; // LCD expander word for Register Select pin + uint8_t _data_pins[4]; // LCD data lines + +}; + +#endif diff --git a/LiquidTWI2.cpp b/LiquidTWI2.cpp index bc1cbcf5..d0e547e1 100644 --- a/LiquidTWI2.cpp +++ b/LiquidTWI2.cpp @@ -12,6 +12,7 @@ Compatible with Adafruit I2C LCD backpack (MCP23008) and Adafruit RGB LCD Shield */ +#if defined(MCP23017) || defined(MCP23008) #include #include #include @@ -701,3 +702,5 @@ void LiquidTWI2::buzz(long duration, uint16_t freq) { } } #endif //MCP23017 + +#endif // defined(MCP23017) || defined(MCP23008) diff --git a/open_evse.h b/open_evse.h index d71ccb49..6cacf580 100644 --- a/open_evse.h +++ b/open_evse.h @@ -124,10 +124,6 @@ //#define I2CLCD // Support PCF8574* based I2C backpack using F. Malpartida's library // https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads -// note: When enabling I2CLCD_PCF8754, due to stupidity of Arduino, at -// top of open_evse.pde must -// uncomment #include and -// comment out #include //#define I2CLCD_PCF8574 // Advanced Powersupply... Ground check, stuck relay, L1/L2 detection. @@ -418,7 +414,7 @@ // see http://blog.lincomatic.com/?p=956 for installation instructions #include "./Wire.h" #ifdef I2CLCD_PCF8574 -#include +#include "./LiquidCrystal_I2C.h" #define LCD_I2C_ADDR 0x27 #else #ifdef RGBLCD @@ -609,7 +605,6 @@ class OnboardDisplay #endif #if defined(RGBLCD) || defined(I2CLCD) #ifdef I2CLCD_PCF8574 -#include LiquidCrystal_I2C m_Lcd; #else LiquidTWI2 m_Lcd; diff --git a/open_evse.ino b/open_evse.ino index ea686c1f..bbd673cf 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -832,7 +832,7 @@ void Btn::read() #ifdef ADAFRUIT_BTN sample = (g_OBD.readButtons() & BUTTON_SELECT) ? 1 : 0; #else //!ADAFRUIT_BTN - sample = btnPin.read() ? 0 : 1; + sample = pinBtn.read() ? 0 : 1; #endif // ADAFRUIT_BTN if (!sample && (buttonState == BTN_STATE_LONG) && !lastDebounceTime) { buttonState = BTN_STATE_OFF; From d7be47b81382871d78c9ba65deb4dd4c776561d3 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Mon, 8 Jun 2015 21:38:07 -0700 Subject: [PATCH 48/96] fix broken delay timer --- CHANGELOG | 4 ++++ open_evse.h | 2 +- open_evse.ino | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ef289eaa..c43cce90 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ Change Log +vD3.9.3 SCL 20150608 +- add files and edits to restore I2CLCD_PCF8574 build capability +- broken DelayTimer() introduced in 3.9.0 + vD3.9.2 SCL 20150606 - fix $GF bug diff --git a/open_evse.h b/open_evse.h index 6cacf580..0ad2ee8e 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.2" +#define VERSION "D3.9.3" //-- begin features diff --git a/open_evse.ino b/open_evse.ino index bbd673cf..6cb427a8 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -2109,8 +2109,8 @@ void DelayTimer::CheckTime() } } } + m_LastCheck = curms; } - m_LastCheck = curms; } } void DelayTimer::Enable(){ From 8212bb113d6456df07bd1ddcae5e4810b4cf53c6 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 9 Jun 2015 07:48:27 -0700 Subject: [PATCH 49/96] fix compile error when DELAYTIMER turned off --- open_evse.ino | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/open_evse.ino b/open_evse.ino index 6cb427a8..f73fe730 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -137,9 +137,8 @@ Menu *g_SetupMenuList[] = { #ifdef BTN_MENU BtnHandler g_BtnHandler; #endif // BTN_MENU -#ifdef DELAYTIMER + #define g_sHHMMfmt "%02d:%02d" -#endif// DELAYTIMER //-- begin global variables From f1d8c6e591f41d6ac8e23a378665d33b5ccad8c8 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Wed, 10 Jun 2015 12:23:17 -0700 Subject: [PATCH 50/96] Menu allows disabling temperature monitoring also bumped temperature thresholds up by 3C --- J1772EvseController.cpp | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 3899502b..6fe147ee 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -174,7 +174,8 @@ void J1772EVSEController::ShowDisabledTests() ECF_VENT_REQ_DISABLED| ECF_GND_CHK_DISABLED| ECF_STUCK_RELAY_CHK_DISABLED| - ECF_GFI_TEST_DISABLED)) { + ECF_GFI_TEST_DISABLED| + ECF_TEMP_CHK_DISABLED)) { g_OBD.LcdSetBacklightColor(YELLOW); if (!DiodeCheckEnabled()) { @@ -201,6 +202,12 @@ void J1772EVSEController::ShowDisabledTests() delay(SHOW_DISABLED_DELAY); } #endif // GFI_SELFTEST +#ifdef TEMPERATURE_MONITORING + if (!TempChkEnabled()) { + g_OBD.LcdMsg_P(g_psDisabledTests,g_psTempChk); + delay(SHOW_DISABLED_DELAY); + } +#endif // TEMPERATURE_MONITORING g_OBD.LcdSetBacklightColor(WHITE); } @@ -308,6 +315,19 @@ void J1772EVSEController::EnableGfiSelfTest(uint8_t tf) } #endif +#ifdef TEMPERATURE_MONITORING +void J1772EVSEController::EnableTempChk(uint8_t tf) +{ + if (tf) { + m_wFlags &= ~ECF_TEMP_CHK_DISABLED; + } + else { + m_wFlags |= ECF_TEMP_CHK_DISABLED; + } + SaveEvseFlags(); +} +#endif TEMPERATURE_MONITORING + void J1772EVSEController::EnableVentReq(uint8_t tf) { if (tf) { @@ -785,7 +805,7 @@ void J1772EVSEController::Init() } #ifdef NOCHECKS - m_wFlags |= ECF_DIODE_CHK_DISABLED|ECF_VENT_REQ_DISABLED|ECF_GND_CHK_DISABLED|ECF_STUCK_RELAY_CHK_DISABLED|ECF_GFI_TEST_DISABLED; + m_wFlags |= ECF_DIODE_CHK_DISABLED|ECF_VENT_REQ_DISABLED|ECF_GND_CHK_DISABLED|ECF_STUCK_RELAY_CHK_DISABLED|ECF_GFI_TEST_DISABLED|ECF_TEMP_CHK_DISABLED; #endif #ifdef AMMETER @@ -1081,6 +1101,7 @@ void J1772EVSEController::Update() #ifdef TEMPERATURE_MONITORING // A state for OverTemp fault +if (TempChkEnabled()) { if ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_PANIC) || (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_PANIC) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_PANIC)) { @@ -1088,6 +1109,7 @@ void J1772EVSEController::Update() m_EvseState = EVSE_STATE_OVER_TEMPERATURE; nofault = 0; } +} #endif // TEMPERATURE_MONITORING if (nofault) { @@ -1343,6 +1365,7 @@ void J1772EVSEController::Update() m_ElapsedChargeTime = now() - m_ChargeOnTime; #ifdef TEMPERATURE_MONITORING + if(TempChkEnabled()) { if (m_ElapsedChargeTime != m_ElapsedChargeTimePrev) { uint8_t currcap = eeprom_read_byte((uint8_t*) ((GetCurSvcLevel() == 2) ? EOFS_CURRENT_CAPACITY_L2 : EOFS_CURRENT_CAPACITY_L1)); uint8_t setit = 0; @@ -1387,6 +1410,7 @@ void J1772EVSEController::Update() } } } + } #endif // TEMPERATURE_MONITORING #ifdef CHARGE_LIMIT if (m_chargeLimit && (g_WattSeconds >= 3600000 * (uint32_t)m_chargeLimit)) { From a3cb26d4ead30ff1ee02ee99e0b191342d5eb8c7 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Wed, 10 Jun 2015 12:24:52 -0700 Subject: [PATCH 51/96] Menu allows disabling temperature monitoring --- J1772EvseController.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/J1772EvseController.h b/J1772EvseController.h index 8e74427a..e6d19a54 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -68,6 +68,7 @@ typedef struct calibdata { #define ECF_SERIAL_DBG 0x0080 // enable debugging messages via serial #define ECF_MONO_LCD 0x0100 // monochrome LCD backlight #define ECF_GFI_TEST_DISABLED 0x0200 // no GFI self test +#define ECF_TEMP_CHK_DISABLED 0x0400 // no Temperature Monitoring #define ECF_DEFAULT 0x0000 // J1772EVSEController volatile m_bVFlags bits - not saved to EEPROM @@ -281,6 +282,14 @@ class J1772EVSEController { void EnableGfiSelfTest(uint8_t tf); #endif #endif // GFI + +#ifdef TEMPERATURE_MONITORING + uint8_t TempChkEnabled() { + return (m_wFlags & ECF_TEMP_CHK_DISABLED) ? 0 : 1; + } + void EnableTempChk(uint8_t tf); +#endif TEMPERATURE_MONITORING + uint8_t SerDbgEnabled() { return (m_wFlags & ECF_SERIAL_DBG) ? 1 : 0; } From 3f6ef79b9fe7f4b1e7f7316f6e1910b7483ede09 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Wed, 10 Jun 2015 12:26:25 -0700 Subject: [PATCH 52/96] Menu allows disabling temperature monitoring also increased the temperature thresholds by 3C --- open_evse.h | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/open_evse.h b/open_evse.h index 0ad2ee8e..c4da3188 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.3" +#define VERSION "D3.9.3.1" //-- begin features @@ -515,12 +515,12 @@ // The SHUTDOWN value must be lower than the PANIC value #ifndef TESTING_TEMPERATURE_OPERATION // normal oerational thresholds just below -#define TEMPERATURE_AMBIENT_THROTTLE_DOWN 520 // This is the temperature in the enclosure where we tell the car to draw 1/2 amperage. -#define TEMPERATURE_AMBIENT_RESTORE_AMPERAGE 490 // If the OpenEVSE responds nicely to the lower current drawn and temperatures in the enclosure +#define TEMPERATURE_AMBIENT_THROTTLE_DOWN 550 // This is the temperature in the enclosure where we tell the car to draw 1/2 amperage. +#define TEMPERATURE_AMBIENT_RESTORE_AMPERAGE 520 // If the OpenEVSE responds nicely to the lower current drawn and temperatures in the enclosure // recover to this level we can kick the current back up to the user's original amperage setting. -#define TEMPERATURE_AMBIENT_SHUTDOWN 550 // This is the temperature in the enclosure where we tell the car to draw 1/4 amperage or 6A is minimum. +#define TEMPERATURE_AMBIENT_SHUTDOWN 580 // This is the temperature in the enclosure where we tell the car to draw 1/4 amperage or 6A is minimum. -#define TEMPERATURE_AMBIENT_PANIC 580 // At this temperature gracefully tell the EV to quit drawing any current, and leave the EVSE in +#define TEMPERATURE_AMBIENT_PANIC 610 // At this temperature gracefully tell the EV to quit drawing any current, and leave the EVSE in // an over temperature error state. The EVSE can be restart from the button or unplugged. // If temperatures get to this level it is advised to open the enclosure to look for trouble. @@ -863,6 +863,16 @@ class GfiTestMenu : public Menu { }; #endif +#ifdef TEMPERATURE_MONITORING +class TempOnOffMenu : public Menu { +public: + TempOnOffMenu(); + void Init(); + void Next(); + Menu *Select(); +}; +#endif // TEMPERATURE_MONITORING + class VentReqMenu : public Menu { public: VentReqMenu(); From b8899966f253c1f2c8b36455956bea8feeb2fbae Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Wed, 10 Jun 2015 12:28:14 -0700 Subject: [PATCH 53/96] Menu allows disabling temperature monitoring --- open_evse.ino | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/open_evse.ino b/open_evse.ino index f73fe730..d8349cb1 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -69,6 +69,9 @@ BklTypeMenu g_BklTypeMenu; #ifdef GFI_SELFTEST GfiTestMenu g_GfiTestMenu; #endif +#ifdef TEMPERATURE_MONITORING +TempOnOffMenu g_TempOnOffMenu; +#endif // TEMPERATURE_MONITORING VentReqMenu g_VentReqMenu; #ifdef ADVPWR GndChkMenu g_GndChkMenu; @@ -131,6 +134,9 @@ Menu *g_SetupMenuList[] = { #ifdef GFI_SELFTEST &g_GfiTestMenu, #endif // GFI_SELFTEST +#ifdef TEMPERATURE_MONITORING + &g_TempOnOffMenu, +#endif // TEMPERATURE_MONITORING NULL }; @@ -1284,6 +1290,47 @@ Menu *GfiTestMenu::Select() } #endif // GFI_SELFTEST +#ifdef TEMPERATURE_MONITORING +TempOnOffMenu::TempOnOffMenu() +{ + m_Title = g_psTempChk; +} + +void TempOnOffMenu::Init() +{ + g_OBD.LcdPrint_P(0,m_Title); + m_CurIdx = g_EvseController.TempChkEnabled() ? 0 : 1; + sprintf(g_sTmp,"+%s",g_YesNoMenuItems[m_CurIdx]); + g_OBD.LcdPrint(1,g_sTmp); +} + +void TempOnOffMenu::Next() +{ + if (++m_CurIdx >= 2) { + m_CurIdx = 0; + } + g_OBD.LcdClearLine(1); + g_OBD.LcdSetCursor(0,1); + uint8_t dce = g_EvseController.TempChkEnabled(); + if ((dce && !m_CurIdx) || (!dce && m_CurIdx)) { + g_OBD.LcdPrint(g_sPlus); + } + g_OBD.LcdPrint(g_YesNoMenuItems[m_CurIdx]); +} + +Menu *TempOnOffMenu::Select() +{ + g_OBD.LcdPrint(0,1,g_sPlus); + g_OBD.LcdPrint(g_YesNoMenuItems[m_CurIdx]); + + g_EvseController.EnableTempChk((m_CurIdx == 0) ? 1 : 0); + + delay(500); + + return &g_SetupMenu; +} +#endif // TEMPERATURE_MONITORING + VentReqMenu::VentReqMenu() { m_Title = g_psVentReqChk; From 19b9566f5ce2c0c97a2e2dbf1b94ab78f43c88a2 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Wed, 10 Jun 2015 12:29:15 -0700 Subject: [PATCH 54/96] Menu allows disabling temperature monitoring --- strings.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/strings.cpp b/strings.cpp index f3f0c24f..5b716a9b 100644 --- a/strings.cpp +++ b/strings.cpp @@ -41,6 +41,9 @@ const char g_psRlyChk[] PROGMEM = "Stuck Relay Chk"; #ifdef GFI_SELFTEST const char g_psGfiTest[] PROGMEM = "GFI Self Test"; #endif +#ifdef TEMPERATURE_MONITORING +const char g_psTempChk[] PROGMEM = "Temperature Chk"; +#endif #endif // BTN_MENU || SHOW_DISABLED_TEST #ifdef BTN_MENU From c93e8d48472569e5af28e8ae8d59497bfc75deae Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Wed, 10 Jun 2015 12:30:30 -0700 Subject: [PATCH 55/96] Menu allows disabling temperature monitoring --- strings.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/strings.h b/strings.h index 95f21152..04822788 100644 --- a/strings.h +++ b/strings.h @@ -43,6 +43,9 @@ extern const char g_psRlyChk[] PROGMEM; #ifdef GFI_SELFTEST extern const char g_psGfiTest[] PROGMEM; #endif +#ifdef TEMPERATURE_MONITORING +extern const char g_psTempChk[] PROGMEM; +#endif // TEMPERATURE_MONITORING #endif // BTN_MENU || SHOW_DISABLED_TEST #ifdef BTN_MENU @@ -94,6 +97,7 @@ extern const char g_sRetryIn[]; #ifdef TEMPERATURE_MONITORING extern const char g_psTemperatureFault[] PROGMEM; +extern const char g_psTempOnOff[] PROGMEM; #endif extern const char g_psNoGround[] PROGMEM; extern const char g_psStuckRelay[] PROGMEM; @@ -114,4 +118,3 @@ extern char *g_sMaxCurrentFmt; extern const char g_psSetDateTime[] PROGMEM; extern char *g_DelayMenuItems[]; #endif // DELAYTIMER_MENU - From 712eb9a2f4f66e6ccff863fef41e88bbf89a36fa Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 11 Jun 2015 09:25:17 -0700 Subject: [PATCH 56/96] add amgient/ir_thresh support --- open_evse.h | 14 +++++++-- open_evse.ino | 83 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 74 insertions(+), 23 deletions(-) diff --git a/open_evse.h b/open_evse.h index c4da3188..35be21b5 100644 --- a/open_evse.h +++ b/open_evse.h @@ -358,6 +358,8 @@ #define EOFS_VOLT_OFFSET 20 // 4 bytes #define EOFS_VOLT_SCALE_FACTOR 24 // 2 bytes +#define EOFS_THRESH_AMBIENT 26 // 2 bytes +#define EOFS_THRESH_IR 28 // 2 bytes // must stay within thresh for this time in ms before switching states @@ -724,12 +726,14 @@ class OnboardDisplay #include "./Adafruit_MCP9808.h" // adding the ambient temp sensor to I2C #include "./Adafruit_TMP007.h" // adding the TMP007 IR I2C sensor +#define TEMPMONITOR_UPDATE_INTERVAL 1000ul // TempMonitor.m_Flags #define TMF_OVERTEMPERATURE 0x01 #define TMF_OVERTEMPERATURE_SHUTDOWN 0x02 #define TMF_BLINK_ALARM 0x04 class TempMonitor { uint8_t m_Flags; + unsigned long m_LastUpdate; public: #ifdef MCP9808_IS_ON_I2C Adafruit_MCP9808 m_tempSensor; @@ -737,6 +741,9 @@ class TempMonitor { #ifdef TMP007_IS_ON_I2C Adafruit_TMP007 m_tmp007; #endif //TMP007_IS_ON_I2C + int16_t m_ambient_thresh; + int16_t m_ir_thresh; + int16_t m_TMP007_thresh; // these three temperatures need to be signed integers int16_t m_MCP9808_temperature; // 230 means 23.0C Using an integer to save on floating point library use int16_t m_DS3231_temperature; // the DS3231 RTC has a built in temperature sensor @@ -761,6 +768,8 @@ class TempMonitor { else m_Flags &= ~TMF_OVERTEMPERATURE_SHUTDOWN; } int8_t OverTemperatureShutdown() { return (m_Flags & TMF_OVERTEMPERATURE_SHUTDOWN) ? 1 : 0; } + void LoadThresh(); + void SaveThresh(); }; #endif // TEMPERATURE_MONITORING @@ -1112,6 +1121,9 @@ class DelayTimer { // -- end class definitions +char *u2a(unsigned long x,int8_t digits=0); +void ProcessInputs(); + /* extern char g_sPlus[]; extern char g_sSlash[]; @@ -1134,8 +1146,6 @@ extern SettingsMenu g_SettingsMenu; extern OnboardDisplay g_OBD; extern char g_sTmp[TMP_BUF_SIZE]; -char *u2a(unsigned long x,int8_t digits=0); - #ifdef KWH_RECORDING extern unsigned long g_WattHours_accumulated; extern unsigned long g_WattSeconds; diff --git a/open_evse.ino b/open_evse.ino index d8349cb1..b1011893 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -248,6 +248,25 @@ void wdt_init(void) #ifdef TEMPERATURE_MONITORING +void TempMonitor::LoadThresh() +{ + m_ambient_thresh = eeprom_read_word((uint16_t *)EOFS_THRESH_AMBIENT); + if (m_ambient_thresh == 0xffff) { + m_ambient_thresh = TEMPERATURE_AMBIENT_THROTTLE_DOWN; + } + m_ir_thresh = eeprom_read_word((uint16_t *)EOFS_THRESH_IR); + if (m_ir_thresh == 0xffff) { + m_ir_thresh = TEMPERATURE_INFRARED_THROTTLE_DOWN; + } +} + +void TempMonitor::SaveThresh() +{ + eeprom_write_word((uint16_t *)EOFS_THRESH_AMBIENT,m_ambient_thresh); + eeprom_write_word((uint16_t *)EOFS_THRESH_IR,m_ir_thresh); +} + + void TempMonitor::Init() { m_Flags = 0; @@ -255,6 +274,8 @@ void TempMonitor::Init() m_DS3231_temperature = 230; // the DS3231 RTC has a built in temperature sensor m_TMP007_temperature = 230; + LoadThresh(); + #ifdef MCP9808_IS_ON_I2C m_tempSensor.begin(); #endif // MCP9808_IS_ON_I2C @@ -266,35 +287,40 @@ void TempMonitor::Init() void TempMonitor::Read() { + unsigned long curms = millis(); + if ((curms - m_LastUpdate) >= TEMPMONITOR_UPDATE_INTERVAL) { #ifdef TMP007_IS_ON_I2C - m_TMP007_temperature = m_tmp007.readObjTempC10(); // using the TI TMP007 IR sensor + m_TMP007_temperature = m_tmp007.readObjTempC10(); // using the TI TMP007 IR sensor #endif #ifdef MCP9808_IS_ON_I2C - m_MCP9808_temperature = m_tempSensor.readTempC10(); // for the MCP9808 - if (m_MCP9808_temperature == 2303) { - m_MCP9808_temperature = 0; } // return 0 if the sensor is not present on the I2C bus + m_MCP9808_temperature = m_tempSensor.readTempC10(); // for the MCP9808 + if (m_MCP9808_temperature == 2303) { + m_MCP9808_temperature = 0; } // return 0 if the sensor is not present on the I2C bus #endif #ifdef RTC - // This code chunck below reads the DS3231 RTC's internal temperature sensor - Wire.beginTransmission(0x68); - wiresend(uint8_t(0x0e)); - wiresend( 0x20 ); // write bit 5 to initiate conversion of temperature - Wire.endTransmission(); + // This code chunck below reads the DS3231 RTC's internal temperature sensor + Wire.beginTransmission(0x68); + wiresend(uint8_t(0x0e)); + wiresend( 0x20 ); // write bit 5 to initiate conversion of temperature + Wire.endTransmission(); - Wire.beginTransmission(0x68); - wiresend(uint8_t(0x11)); - Wire.endTransmission(); + Wire.beginTransmission(0x68); + wiresend(uint8_t(0x11)); + Wire.endTransmission(); - Wire.requestFrom(0x68, 2); - m_DS3231_temperature = 10 * wirerecv(); // Here's the MSB - m_DS3231_temperature = m_DS3231_temperature + (5*(wirerecv()>>6))/2; // keep the reading like 235 meaning 23.5C - if (m_DS3231_temperature == 0x09FD) m_DS3231_temperature = 0; // If the DS3231 is not present then return 0 - #ifdef OPENEVSE_2 + Wire.requestFrom(0x68, 2); + m_DS3231_temperature = 10 * wirerecv(); // Here's the MSB + m_DS3231_temperature = m_DS3231_temperature + (5*(wirerecv()>>6))/2; // keep the reading like 235 meaning 23.5C + if (m_DS3231_temperature == 0x09FD) m_DS3231_temperature = 0; // If the DS3231 is not present then return 0 +#ifdef OPENEVSE_2 m_DS3231_temperature = 0; // If the DS3231 is not present then return 0, OpenEVSE II does not use the DS3231 - #endif +#endif #endif // RTC + + m_LastUpdate = curms; + } } #endif // TEMPERATURE_MONITORING @@ -643,8 +669,6 @@ void OnboardDisplay::Update(int8_t updmode) if ((curms-m_LastUpdateMs) >= 1000) { m_LastUpdateMs = curms; - g_TempMonitor.Read(); // update temperatures once per second - if (!g_EvseController.InHardFault() && ((curstate == EVSE_STATE_GFCI_FAULT) || (curstate == EVSE_STATE_NO_GROUND))) { strcpy(g_sTmp,g_sRetryIn); @@ -2180,6 +2204,23 @@ void DelayTimer::PrintTimerIcon(){ // End Delay Timer Functions - GoldServe #endif //#ifdef DELAYTIMER +void ProcessInputs() +{ +#ifdef RAPI + g_ERP.doCmd(); +#endif +#ifdef SERIALCLI + g_CLI.getInput(); +#endif // SERIALCLI +#ifdef BTN_MENU + g_BtnHandler.ChkBtn(); +#endif +#ifdef TEMPERATURE_MONITORING + g_TempMonitor.Read(); // update temperatures once per second +#endif +} + + void EvseReset() { Wire.begin(); @@ -2238,7 +2279,7 @@ void loop() g_OBD.Update(); - g_EvseController.ProcessInputs(); + ProcessInputs(); // Delay Timer Handler - GoldServe #ifdef DELAYTIMER From 02d9990573a2d0e3e9b97cdc93c9de99dd4361e7 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 11 Jun 2015 20:30:09 -0700 Subject: [PATCH 57/96] delete J1772EVSEController::ProcessInputs() --- J1772EvseController.cpp | 13 ------------- J1772EvseController.h | 1 - 2 files changed, 14 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 6fe147ee..9f971001 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -739,19 +739,6 @@ uint8_t J1772EVSEController::doPost() } #endif // ADVPWR -void J1772EVSEController::ProcessInputs() -{ -#ifdef RAPI - g_ERP.doCmd(); -#endif -#ifdef SERIALCLI - g_CLI.getInput(); -#endif // SERIALCLI -#ifdef BTN_MENU - g_BtnHandler.ChkBtn(); -#endif -} - void J1772EVSEController::Init() { // read settings from EEPROM diff --git a/J1772EvseController.h b/J1772EvseController.h index e6d19a54..42713ade 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -342,7 +342,6 @@ class J1772EVSEController { int32_t GetChargingCurrent() { return -1; } #endif void ReadPilot(int *plow,int *phigh,int loopcnt=PILOT_LOOP_CNT); - void ProcessInputs(); void Reboot(); #ifdef SHOW_DISABLED_TESTS void ShowDisabledTests(); From 56c12599272ec328c69f1f18e7a57f803f5b86e1 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 11 Jun 2015 20:35:29 -0700 Subject: [PATCH 58/96] reduce GFI test pulse from 50% to 33% --- CHANGELOG | 6 ++++++ Gfi.cpp | 4 ++-- J1772EvseController.cpp | 8 ++++---- open_evse.h | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c43cce90..c61feba5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,11 @@ Change Log +vD3.9.4 SCL 20150611 +- reduce GFI self test pulse duration from 50% to 33% + +vD3.9.3.1 SCL 20150611 +- craigK PR #46 + vD3.9.3 SCL 20150608 - add files and edits to restore I2CLCD_PCF8574 build capability - broken DelayTimer() introduced in 3.9.0 diff --git a/Gfi.cpp b/Gfi.cpp index 0240cf05..33381c20 100644 --- a/Gfi.cpp +++ b/Gfi.cpp @@ -62,9 +62,9 @@ uint8_t Gfi::SelfTest() testSuccess = 0; for(int i=0; i < GFI_TEST_CYCLES; i++) { pinTest.write(1); - delayMicroseconds(GFI_PULSE_DURATION_US); + delayMicroseconds(GFI_PULSE_ON_US); pinTest.write(0); - delayMicroseconds(GFI_PULSE_DURATION_US); + delayMicroseconds(GFI_PULSE_OFF_US); if (testSuccess) break; // no need to keep trying. } diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 6fe147ee..29aa1120 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -1197,9 +1197,9 @@ if (TempChkEnabled()) { g_OBD.LcdMsg("Induce","Fault"); for(int i = 0; i < GFI_TEST_CYCLES; i++) { m_Gfi.pinTest.write(1); - delayMicroseconds(GFI_PULSE_DURATION_US); + delayMicroseconds(GFI_PULSE_ON_US); m_Gfi.pinTest.write(0); - delayMicroseconds(GFI_PULSE_DURATION_US); + delayMicroseconds(GFI_PULSE_OFF_US); if (m_Gfi.Fault()) break; } } @@ -1247,9 +1247,9 @@ if (TempChkEnabled()) { #ifdef FT_GFI_LOCKOUT for(int i = 0; i < GFI_TEST_CYCLES; i++) { m_Gfi.pinTest.write(1); - delayMicroseconds(GFI_PULSE_DURATION_US); + delayMicroseconds(GFI_PULSE_ON_US); m_Gfi.pinTest.write(0); - delayMicroseconds(GFI_PULSE_DURATION_US); + delayMicroseconds(GFI_PULSE_OFF_US); if (m_Gfi.Fault()) break; } g_OBD.LcdMsg("Closing","Relay"); diff --git a/open_evse.h b/open_evse.h index c4da3188..05b58bf5 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.3.1" +#define VERSION "D3.9.4" //-- begin features @@ -386,7 +386,9 @@ #define GFITEST_IDX 6 #define GFI_TEST_CYCLES 60 -#define GFI_PULSE_DURATION_US 8333 // of roughly 60 Hz. - 8333us as a half-cycle +// GFI pulse should be 33% duty cycle +#define GFI_PULSE_ON_US 5556 // 1/3 of roughly 60 Hz. +#define GFI_PULSE_OFF_US 11111 // 2/3 of roughly 60 Hz. #endif From 4f260c1b162b719b6b42cacf1e8972b774156340 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 12 Jun 2015 15:06:01 -0700 Subject: [PATCH 59/96] minor code shrink --- Gfi.cpp | 3 +-- J1772EvseController.cpp | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Gfi.cpp b/Gfi.cpp index 33381c20..a083ae5d 100644 --- a/Gfi.cpp +++ b/Gfi.cpp @@ -60,12 +60,11 @@ uint8_t Gfi::SelfTest() { testInProgress = 1; testSuccess = 0; - for(int i=0; i < GFI_TEST_CYCLES; i++) { + for(int i=0; !testSuccess && (i < GFI_TEST_CYCLES); i++) { pinTest.write(1); delayMicroseconds(GFI_PULSE_ON_US); pinTest.write(0); delayMicroseconds(GFI_PULSE_OFF_US); - if (testSuccess) break; // no need to keep trying. } // wait for GFI pin to clear diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 29aa1120..d3f74030 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -140,7 +140,13 @@ J1772EVSEController::J1772EVSEController() : void J1772EVSEController::SaveSettings() { // n.b. should we add dirty bits so we only write the changed values? or should we just write them on the fly when necessary? - eeprom_write_byte((uint8_t *)((GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),(byte)GetCurrentCapacity()); + // ugly code below is smaller than this: eeprom_write_byte((uint8_t *)((GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),(byte)GetCurrentCapacity()); + if (GetCurSvcLevel() == 1) { + eeprom_write_byte((uint8_t *)EOFS_CURRENT_CAPACITY_L1,(byte)GetCurrentCapacity()); + } + else { + eeprom_write_byte((uint8_t *)EOFS_CURRENT_CAPACITY_L2,(byte)GetCurrentCapacity()); + } SaveEvseFlags(); } @@ -1373,16 +1379,13 @@ if (TempChkEnabled()) { if (!g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_THROTTLE_DOWN ) || // any sensor reaching threshold trips action (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // Throttle back the L2 current advice to the EV - g_TempMonitor.SetOverTemperature(1); currcap /= 2; // set to the throttled back level - setit = 1; + setit = 2; } if (g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_RESTORE_AMPERAGE ) && // all sensors need to show return to lower levels (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ) && (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ))) { // restore the original L2 current advice to the EV - g_TempMonitor.SetOverTemperature(0); - setit = 1; // set to the user's original setting for current } @@ -1390,20 +1393,18 @@ if (TempChkEnabled()) { if (!g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_SHUTDOWN ) || // any sensor reaching threshold trips action (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ))) { // Throttle back the L2 current advice to the EV - g_TempMonitor.SetOverTemperatureShutdown(1); currcap /= 4; - setit = 1; + setit = 2; } if (g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_THROTTLE_DOWN ) && // all sensors need to show return to lower levels (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) && (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // restore the throttled down current advice to the EV since things have cooled down again - g_TempMonitor.SetOverTemperatureShutdown(0); - currcap /= 2; // set to the throttled back level setit = 1; } if (setit) { + g_TempMonitor.SetOverTemperatureShutdown(setit-1); SetCurrentCapacity(currcap,0,1); if (m_Pilot.GetState() != PILOT_STATE_PWM) { m_Pilot.SetPWM(m_CurrentCapacity); From 805a559145cf887a2732e3d8bd3ca13675ef0257 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Fri, 12 Jun 2015 16:03:04 -0700 Subject: [PATCH 60/96] add $SO & $GO --- rapi_proc.cpp | 14 ++++++++++++++ rapi_proc.h | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/rapi_proc.cpp b/rapi_proc.cpp index 7b34d0d5..a547a8d2 100644 --- a/rapi_proc.cpp +++ b/rapi_proc.cpp @@ -311,6 +311,14 @@ int EvseRapiProcessor::processCmd() } break; #endif // VOLTMETER +#ifdef TEMPERATURE_MONITORING + case 'O': + if (tokenCnt == 3) { + g_TempMonitor.m_ambient_thresh = dtou32(tokens[1]); + g_TempMonitor.m_ir_thresh = dtou32(tokens[2]); + } + break; +#endif // TEMPERATURE_MONITORING #ifdef ADVPWR case 'R': // stuck relay check if (tokenCnt == 2) { @@ -412,6 +420,12 @@ int EvseRapiProcessor::processCmd() break; #endif // VOLTMETER #ifdef TEMPERATURE_MONITORING + case 'O': + u1.i = g_TempMonitor.m_ambient_thresh; + u2.i = g_TempMonitor.m_ir_thresh; + sprintf(buffer,"%d %d",u1.i,u2.i); + bufCnt = 1; // flag response text output + break; case 'P': sprintf(buffer,"%d %d %d",(int)g_TempMonitor.m_DS3231_temperature, (int)g_TempMonitor.m_MCP9808_temperature, diff --git a/rapi_proc.h b/rapi_proc.h index 68814227..6bbc1c77 100644 --- a/rapi_proc.h +++ b/rapi_proc.h @@ -114,6 +114,8 @@ SL 1|2|A - set service level L1/L2/Auto $SL 2*15 $SL A*24 SM voltscalefactor voltoffset - set voltMeter settings +SO ambientthresh irthresh - set Overtemperature thresholds + thresholds are in 10ths of a degree Celcius SR 0|1 - disable/enable stuck relay check $SR 0*19 $SR 1*1A @@ -154,6 +156,10 @@ GH - get cHarge limit GM - get voltMeter settings response: OK voltcalefactor voltoffset $GM^2E +GO get Overtemperature thresholds + response: OK ambientthresh irthresh + thresholds are in 10ths of a degree Celcius + $GO^2C GP - get temPerature (v1.0.3+) $GP*BB response: OK ds3231temp mcp9808temp tmp007temp From 5d210e217ff9ef3a6919488e22d26ba724d974ee Mon Sep 17 00:00:00 2001 From: lincomatic Date: Mon, 20 Jul 2015 15:27:15 -0700 Subject: [PATCH 61/96] automatically exit sleep if EV disconnected if sleep induced by charge/time limit expiration --- J1772EvseController.cpp | 29 +++++++++++++++++++++++++---- J1772EvseController.h | 7 +++++++ open_evse.h | 2 +- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 9f971001..3da6ffe0 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -410,6 +410,9 @@ void J1772EVSEController::Enable() pinSleepStatus.write(0); } #endif // SLEEP_STATUS_REG +#if defined(TIME_LIMIT) || defined(CHARGE_LIMIT) + SetLimitSleep(0); +#endif //defined(TIME_LIMIT) || defined(CHARGE_LIMIT) m_PrevEvseState = EVSE_STATE_DISABLED; m_EvseState = EVSE_STATE_UNKNOWN; @@ -932,7 +935,7 @@ void J1772EVSEController::ReadPilot(int *plow,int *phigh,int loopcnt) void J1772EVSEController::Update() { int plow; - int phigh; + int phigh = -127; unsigned long curms = millis(); @@ -941,6 +944,7 @@ void J1772EVSEController::Update() return; } else if (m_EvseState == EVSE_STATE_SLEEPING) { + int8_t cancelTransition = 1; if (chargingIsOn()) { ReadPilot(&plow,&phigh); // wait for pilot voltage to go > STATE C. This will happen if @@ -957,8 +961,21 @@ void J1772EVSEController::Update() #endif } } - m_PrevEvseState = m_EvseState; // cancel state transition - return; +#if defined(TIME_LIMIT) || defined(CHARGE_LIMIT) + else if (LimitSleepIsSet()) { + ReadPilot(&plow,&phigh); + if (phigh >= m_ThreshData.m_ThreshAB) { + // if we went into sleep due to time/charge limit met, then + // automatically cancel the sleep when the car is unplugged + cancelTransition = 0; + SetLimitSleep(0); + } + } +#endif //defined(TIME_LIMIT) || defined(CHARGE_LIMIT) + if (cancelTransition) { + m_PrevEvseState = m_EvseState; // cancel state transition + return; + } } uint8_t prevevsestate = m_EvseState; @@ -1000,7 +1017,9 @@ void J1772EVSEController::Update() else { // !chargingIsOn() - relay open if (prevevsestate == EVSE_STATE_NO_GROUND) { // check to see if EV disconnected - ReadPilot(&plow,&phigh); + if (phigh == -127) { + ReadPilot(&plow,&phigh); + } if (phigh >= m_ThreshData.m_ThreshAB) { // EV disconnected - cancel fault m_EvseState = EVSE_STATE_UNKNOWN; @@ -1402,6 +1421,7 @@ if (TempChkEnabled()) { #ifdef CHARGE_LIMIT if (m_chargeLimit && (g_WattSeconds >= 3600000 * (uint32_t)m_chargeLimit)) { SetChargeLimit(0); // reset charge limit + SetLimitSleep(1); Sleep(); } #endif @@ -1411,6 +1431,7 @@ if (TempChkEnabled()) { // to State C, so m_ChargeOnTimeMS will be > curms from the start if ((millis() - m_ChargeOnTimeMS) >= (15lu*60000lu * (unsigned long)m_timeLimit)) { SetTimeLimit(0); // reset time limit + SetLimitSleep(1); Sleep(); } } diff --git a/J1772EvseController.h b/J1772EvseController.h index 42713ade..4c57a135 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -72,6 +72,7 @@ typedef struct calibdata { #define ECF_DEFAULT 0x0000 // J1772EVSEController volatile m_bVFlags bits - not saved to EEPROM +#define ECVF_LIMIT_SLEEP 0x04 // currently sleeping after reaching time/charge limit #define ECVF_AUTOSVCLVL_SKIPPED 0x01 // auto svc level test skipped during post #define ECVF_HARD_FAULT 0x02 // in non-autoresettable fault #define ECVF_AMMETER_CAL 0x10 // ammeter calibration mode @@ -271,6 +272,12 @@ class J1772EVSEController { void HardFault(); + void SetLimitSleep(int8_t tf) { + if (tf) m_bVFlags |= ECVF_LIMIT_SLEEP; + else m_bVFlags &= ~ECVF_LIMIT_SLEEP; + } + int8_t LimitSleepIsSet() { return (int8_t)(m_bVFlags & ECVF_LIMIT_SLEEP); } + #ifdef GFI void SetGfiTripped(); uint8_t GfiTripped() { return m_bVFlags & ECVF_GFI_TRIPPED; } diff --git a/open_evse.h b/open_evse.h index 35be21b5..ea7cbde0 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.3.1" +#define VERSION "D3.9.3.2T" //-- begin features From 45845844e0f92e9a508d58cd02c1069eac385728 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Wed, 22 Jul 2015 13:58:41 -0700 Subject: [PATCH 62/96] D3.7.4 --- CHANGELOG | 12 ++++++++++++ open_evse.h | 13 ++++++++++--- open_evse.ino | 5 ++++- rapi_proc.cpp | 5 ++++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c61feba5..022cdee0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,17 @@ Change Log +vD3.9.5 SCL 20150722 +- merge changes from temp branch: + 1. EVSE goes to sleep after time/charge limit met.. would stay in sleep + -> automatically cancel sleep mode when car disconnected *only if* the + sleep was induced by time/charge limit + 2. decouple temperature monitoring from J1772EVSEController::Update() + -> so that temperature is still monitored when EVSE disabled/sleeping + 3. ProcessInputs() removed from J1772EVSEController + 4. added TEMPERATURE_MONITORING_NY (not yet) code .. currently disabled + -> $GO/$SO RAPI + -> added ambient/ir_thresh support + vD3.9.4 SCL 20150611 - reduce GFI self test pulse duration from 50% to 33% diff --git a/open_evse.h b/open_evse.h index ea7cbde0..985885d1 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.3.2T" +#define VERSION "D3.9.5" //-- begin features @@ -99,7 +99,8 @@ #define GFI_SELFTEST #endif //UL_GFI_SELFTEST -#define TEMPERATURE_MONITORING // Temperature monitoring support +#define TEMPERATURE_MONITORING // Temperature monitoring support +// not yet #define TEMPERATURE_MONITORING_NY #ifdef AMMETER // kWh Recording feature depends upon #AMMETER support @@ -388,7 +389,9 @@ #define GFITEST_IDX 6 #define GFI_TEST_CYCLES 60 -#define GFI_PULSE_DURATION_US 8333 // of roughly 60 Hz. - 8333us as a half-cycle +// GFI pulse should be 33% duty cycle +#define GFI_PULSE_ON_US 5556 // 1/3 of roughly 60 Hz. +#define GFI_PULSE_OFF_US 11111 // 2/3 of roughly 60 Hz. #endif @@ -741,9 +744,11 @@ class TempMonitor { #ifdef TMP007_IS_ON_I2C Adafruit_TMP007 m_tmp007; #endif //TMP007_IS_ON_I2C +#ifdef TEMPERATURE_MONITORING_NY int16_t m_ambient_thresh; int16_t m_ir_thresh; int16_t m_TMP007_thresh; +#endif //TEMPERATURE_MONITORING_NY // these three temperatures need to be signed integers int16_t m_MCP9808_temperature; // 230 means 23.0C Using an integer to save on floating point library use int16_t m_DS3231_temperature; // the DS3231 RTC has a built in temperature sensor @@ -768,8 +773,10 @@ class TempMonitor { else m_Flags &= ~TMF_OVERTEMPERATURE_SHUTDOWN; } int8_t OverTemperatureShutdown() { return (m_Flags & TMF_OVERTEMPERATURE_SHUTDOWN) ? 1 : 0; } +#ifdef TEMPERATURE_MONITORING_NY void LoadThresh(); void SaveThresh(); +#endif //TEMPERATURE_MONITORING_NY }; #endif // TEMPERATURE_MONITORING diff --git a/open_evse.ino b/open_evse.ino index b1011893..9c63b47a 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -248,6 +248,7 @@ void wdt_init(void) #ifdef TEMPERATURE_MONITORING +#ifdef TEMPERATURE_MONITORING_NY void TempMonitor::LoadThresh() { m_ambient_thresh = eeprom_read_word((uint16_t *)EOFS_THRESH_AMBIENT); @@ -265,7 +266,7 @@ void TempMonitor::SaveThresh() eeprom_write_word((uint16_t *)EOFS_THRESH_AMBIENT,m_ambient_thresh); eeprom_write_word((uint16_t *)EOFS_THRESH_IR,m_ir_thresh); } - +#endif // TEMPERATURE_MONITORING_NY void TempMonitor::Init() { @@ -274,7 +275,9 @@ void TempMonitor::Init() m_DS3231_temperature = 230; // the DS3231 RTC has a built in temperature sensor m_TMP007_temperature = 230; +#ifdef TEMPERATURE_MONITORING_NY LoadThresh(); +#endif #ifdef MCP9808_IS_ON_I2C m_tempSensor.begin(); diff --git a/rapi_proc.cpp b/rapi_proc.cpp index a547a8d2..5a194b66 100644 --- a/rapi_proc.cpp +++ b/rapi_proc.cpp @@ -311,11 +311,12 @@ int EvseRapiProcessor::processCmd() } break; #endif // VOLTMETER -#ifdef TEMPERATURE_MONITORING +#ifdef TEMPERATURE_MONITORING_NY case 'O': if (tokenCnt == 3) { g_TempMonitor.m_ambient_thresh = dtou32(tokens[1]); g_TempMonitor.m_ir_thresh = dtou32(tokens[2]); + g_TempMonitor.SaveThresh(); } break; #endif // TEMPERATURE_MONITORING @@ -420,12 +421,14 @@ int EvseRapiProcessor::processCmd() break; #endif // VOLTMETER #ifdef TEMPERATURE_MONITORING +#ifdef TEMPERATURE_MONITORING_NY case 'O': u1.i = g_TempMonitor.m_ambient_thresh; u2.i = g_TempMonitor.m_ir_thresh; sprintf(buffer,"%d %d",u1.i,u2.i); bufCnt = 1; // flag response text output break; +#endif // TEMPERATURE_MONITORING_NY case 'P': sprintf(buffer,"%d %d %d",(int)g_TempMonitor.m_DS3231_temperature, (int)g_TempMonitor.m_MCP9808_temperature, From 56ffc43e198f04ad1063e2605be67246d985e6c8 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Wed, 22 Jul 2015 16:45:39 -0700 Subject: [PATCH 63/96] Fix kWh inadvertently accumulating exiting sleep --- J1772EvseController.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 22115599..1554381e 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -1224,8 +1224,10 @@ if (TempChkEnabled()) { chargingOff(); // turn off charging current m_Pilot.SetState(PILOT_STATE_P12); #ifdef KWH_RECORDING + if ((prevevsestate == EVSE_STATE_C) || (prevevsestate == EVSE_STATE_B)) { g_WattHours_accumulated = g_WattHours_accumulated + (g_WattSeconds / 3600); eeprom_write_dword((uint32_t*)EOFS_KWH_ACCUMULATED,g_WattHours_accumulated); + } #endif // KWH_RECORDING #ifdef CHARGE_LIMIT SetChargeLimit(0); From 2d887d48c415c57a4d477aab348ba6ce9cf37b80 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 23 Jul 2015 22:04:31 -0700 Subject: [PATCH 64/96] re-enable bootloader --- boards.txt.arduino1.6.x | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards.txt.arduino1.6.x b/boards.txt.arduino1.6.x index 2fa36133..edaa2155 100644 --- a/boards.txt.arduino1.6.x +++ b/boards.txt.arduino1.6.x @@ -16,7 +16,7 @@ openevse.bootloader.high_fuses=0xDF openevse.bootloader.extended_fuses=0x05 openevse.bootloader.unlock_bits=0x3F openevse.bootloader.lock_bits=0x0F -#openevse.bootloader.file=optiboot/optiboot_atmega328.hex +openevse.bootloader.file=optiboot/optiboot_atmega328.hex openevse.build.mcu=atmega328p openevse.build.f_cpu=16000000L From df6bbc50c445dbc77d11a761c58da5e85ec5ec6f Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 18 Aug 2015 16:49:17 -0700 Subject: [PATCH 65/96] bump up OEII ambient temps by 10C --- CHANGELOG | 3 +++ open_evse.h | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 022cdee0..05d482c3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Change Log +vD3.9.6 SCL 20150818 +- for OpenEVSE II, bump up TEMPERATURE_AMBIENT_xxx by 10 due to extra headroom of CUI VSK-S3-12U power supply + vD3.9.5 SCL 20150722 - merge changes from temp branch: 1. EVSE goes to sleep after time/charge limit met.. would stay in sleep diff --git a/open_evse.h b/open_evse.h index 985885d1..8e392ac0 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.5" +#define VERSION "D3.9.6" //-- begin features @@ -519,15 +519,37 @@ // The THROTTLE_DOWN value must be lower than the SHUTDOWN value // The SHUTDOWN value must be lower than the PANIC value #ifndef TESTING_TEMPERATURE_OPERATION - // normal oerational thresholds just below -#define TEMPERATURE_AMBIENT_THROTTLE_DOWN 550 // This is the temperature in the enclosure where we tell the car to draw 1/2 amperage. -#define TEMPERATURE_AMBIENT_RESTORE_AMPERAGE 520 // If the OpenEVSE responds nicely to the lower current drawn and temperatures in the enclosure - // recover to this level we can kick the current back up to the user's original amperage setting. -#define TEMPERATURE_AMBIENT_SHUTDOWN 580 // This is the temperature in the enclosure where we tell the car to draw 1/4 amperage or 6A is minimum. +// normal oerational thresholds just below +// This is the temperature in the enclosure where we tell the car to draw 1/2 amperage. +#ifdef OPENEVSE_2 +#define TEMPERATURE_AMBIENT_THROTTLE_DOWN 650 +#else +#define TEMPERATURE_AMBIENT_THROTTLE_DOWN 550 +#endif + +// If the OpenEVSE responds nicely to the lower current drawn and temperatures in the enclosure +// recover to this level we can kick the current back up to the user's original amperage setting. +#ifdef OPENEVSE_2 +#define TEMPERATURE_AMBIENT_RESTORE_AMPERAGE 620 +#else +#define TEMPERATURE_AMBIENT_RESTORE_AMPERAGE 520 +#endif + +// This is the temperature in the enclosure where we tell the car to draw 1/4 amperage or 6A is minimum. +#ifdef OPENEVSE_2 +#define TEMPERATURE_AMBIENT_SHUTDOWN 680 +#else +#define TEMPERATURE_AMBIENT_SHUTDOWN 580 +#endif -#define TEMPERATURE_AMBIENT_PANIC 610 // At this temperature gracefully tell the EV to quit drawing any current, and leave the EVSE in - // an over temperature error state. The EVSE can be restart from the button or unplugged. - // If temperatures get to this level it is advised to open the enclosure to look for trouble. +// At this temperature gracefully tell the EV to quit drawing any current, and leave the EVSE in +// an over temperature error state. The EVSE can be restart from the button or unplugged. +// If temperatures get to this level it is advised to open the enclosure to look for trouble. +#ifdef OPENEVSE_2 +#define TEMPERATURE_AMBIENT_PANIC 710 +#else +#define TEMPERATURE_AMBIENT_PANIC 610 +#endif #define TEMPERATURE_INFRARED_THROTTLE_DOWN 650 // This is the temperature seen by the IR sensor where we tell the car to draw 1/2 amperage. #define TEMPERATURE_INFRARED_RESTORE_AMPERAGE 600 // If the OpenEVSE responds nicely to the lower current drawn and temperatures in the enclosure From 39362e90afff7eac89c800c7c4b6ab752f1f9d80 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Sat, 5 Sep 2015 11:53:52 -0700 Subject: [PATCH 66/96] fix compile error when AMMETER disabled --- CHANGELOG | 3 +++ J1772EvseController.h | 13 +++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 05d482c3..f357eead 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Change Log +SCL 20150905 +- fix compile error when AMMETER disabled + vD3.9.6 SCL 20150818 - for OpenEVSE II, bump up TEMPERATURE_AMBIENT_xxx by 10 due to extra headroom of CUI VSK-S3-12U power supply diff --git a/J1772EvseController.h b/J1772EvseController.h index 4c57a135..065df9b4 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -161,6 +161,10 @@ class J1772EVSEController { m_wFlags &= ~flags; } +#ifdef TIME_LIMIT + uint8_t m_timeLimit; // minutes * 15 +#endif + #ifdef AMMETER unsigned long m_AmmeterReading; int32_t m_ChargingCurrent; @@ -169,9 +173,6 @@ class J1772EVSEController { #ifdef CHARGE_LIMIT uint8_t m_chargeLimit; // kWh #endif -#ifdef TIME_LIMIT - uint8_t m_timeLimit; // minutes * 15 -#endif void readAmmeter(); #endif // AMMETER @@ -341,13 +342,13 @@ class J1772EVSEController { void SetChargeLimit(uint8_t kwh) { m_chargeLimit = kwh; } uint8_t GetChargeLimit() { return m_chargeLimit; } #endif // CHARGE_LIMIT +#else // !AMMETER + int32_t GetChargingCurrent() { return -1; } +#endif // AMMETER #ifdef TIME_LIMIT uint8_t SetTimeLimit(uint8_t mind15) { m_timeLimit = mind15; } uint8_t GetTimeLimit() { return m_timeLimit; } #endif // TIME_LIMIT -#else // !AMMETER - int32_t GetChargingCurrent() { return -1; } -#endif void ReadPilot(int *plow,int *phigh,int loopcnt=PILOT_LOOP_CNT); void Reboot(); #ifdef SHOW_DISABLED_TESTS From 40373cabebd0d6a4fb421f56b05893a434b76ba4 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Sat, 5 Sep 2015 12:00:07 -0700 Subject: [PATCH 67/96] +10C temp thresholds for OE --- CHANGELOG | 3 +++ open_evse.h | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f357eead..842dcc35 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Change Log +vD3.9.7 SCL 20150905 +- raise OpenEVSE temperature thresholds +10C to match OE-II + SCL 20150905 - fix compile error when AMMETER disabled diff --git a/open_evse.h b/open_evse.h index 8e392ac0..90e3af10 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.6" +#define VERSION "D3.9.7" //-- begin features @@ -524,7 +524,7 @@ #ifdef OPENEVSE_2 #define TEMPERATURE_AMBIENT_THROTTLE_DOWN 650 #else -#define TEMPERATURE_AMBIENT_THROTTLE_DOWN 550 +#define TEMPERATURE_AMBIENT_THROTTLE_DOWN 650 #endif // If the OpenEVSE responds nicely to the lower current drawn and temperatures in the enclosure @@ -532,14 +532,14 @@ #ifdef OPENEVSE_2 #define TEMPERATURE_AMBIENT_RESTORE_AMPERAGE 620 #else -#define TEMPERATURE_AMBIENT_RESTORE_AMPERAGE 520 +#define TEMPERATURE_AMBIENT_RESTORE_AMPERAGE 620 #endif // This is the temperature in the enclosure where we tell the car to draw 1/4 amperage or 6A is minimum. #ifdef OPENEVSE_2 #define TEMPERATURE_AMBIENT_SHUTDOWN 680 #else -#define TEMPERATURE_AMBIENT_SHUTDOWN 580 +#define TEMPERATURE_AMBIENT_SHUTDOWN 680 #endif // At this temperature gracefully tell the EV to quit drawing any current, and leave the EVSE in @@ -548,7 +548,7 @@ #ifdef OPENEVSE_2 #define TEMPERATURE_AMBIENT_PANIC 710 #else -#define TEMPERATURE_AMBIENT_PANIC 610 +#define TEMPERATURE_AMBIENT_PANIC 710 #endif #define TEMPERATURE_INFRARED_THROTTLE_DOWN 650 // This is the temperature seen by the IR sensor where we tell the car to draw 1/2 amperage. From 939a983bac8408201145670ebd34a89e3f7b6eee Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Sat, 12 Sep 2015 17:32:03 -0700 Subject: [PATCH 68/96] Fixing Temperature Monitoring broken since D3.9.5 --- J1772EvseController.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 1554381e..bce44f35 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -1387,13 +1387,16 @@ if (TempChkEnabled()) { if (!g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_THROTTLE_DOWN ) || // any sensor reaching threshold trips action (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // Throttle back the L2 current advice to the EV + g_TempMonitor.SetOverTemperature(1); currcap /= 2; // set to the throttled back level - setit = 2; + setit = 1; } if (g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_RESTORE_AMPERAGE ) && // all sensors need to show return to lower levels (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ) && (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ))) { // restore the original L2 current advice to the EV + g_TempMonitor.SetOverTemperature(0); + setit = 1; // set to the user's original setting for current } @@ -1401,18 +1404,20 @@ if (TempChkEnabled()) { if (!g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_SHUTDOWN ) || // any sensor reaching threshold trips action (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ))) { // Throttle back the L2 current advice to the EV + g_TempMonitor.SetOverTemperatureShutdown(1); currcap /= 4; - setit = 2; + setit = 1; } if (g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_THROTTLE_DOWN ) && // all sensors need to show return to lower levels (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) && (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // restore the throttled down current advice to the EV since things have cooled down again + g_TempMonitor.SetOverTemperatureShutdown(0); + currcap /= 2; // set to the throttled back level setit = 1; } if (setit) { - g_TempMonitor.SetOverTemperatureShutdown(setit-1); SetCurrentCapacity(currcap,0,1); if (m_Pilot.GetState() != PILOT_STATE_PWM) { m_Pilot.SetPWM(m_CurrentCapacity); From b1b28635bb309894917853a60ccd4d3ddb6ad6c2 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Sat, 12 Sep 2015 17:35:07 -0700 Subject: [PATCH 69/96] Fixing Temperature Monitoring broken since D3.9.5 --- open_evse.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/open_evse.h b/open_evse.h index 90e3af10..325d5251 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.7" +#define VERSION "D3.9.8" //-- begin features From e09ce09d5fc721f4363f71de1d8d849deaae0c26 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Sat, 12 Sep 2015 17:36:24 -0700 Subject: [PATCH 70/96] Fixing Temperature Monitoring broken since D3.9.5 --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 842dcc35..387ba728 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ Change Log +vD3.9.8 CWK 20150912 +- fix Temperature Monitoring, broken by accident in D3.9.5 + The original users amperage level is restored properly after an event. + vD3.9.7 SCL 20150905 - raise OpenEVSE temperature thresholds +10C to match OE-II From c141cd08ff0de99fa03e52a77cf582f7275e8a75 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Mon, 14 Sep 2015 21:15:08 -0700 Subject: [PATCH 71/96] fix diode check oscillation bug (cwk) --- J1772EvseController.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index bce44f35..4a0d6b39 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -1306,6 +1306,8 @@ if (TempChkEnabled()) { // N.B. J1772 specifies to go to State F (-12V) but we can't do that // and keep checking m_Pilot.SetPWM(m_CurrentCapacity); + m_Pilot.SetState(PILOT_STATE_P12); + HardFault(); } else if (m_EvseState == EVSE_STATE_NO_GROUND) { // Ground not detected From dbeae3a3c8c1d83255c34b6615594f13e0604b93 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Mon, 14 Sep 2015 21:16:04 -0700 Subject: [PATCH 72/96] fix diode check oscillation bug (scl) --- J1772EvseController.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 4a0d6b39..0fe9725b 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -1147,8 +1147,8 @@ if (TempChkEnabled()) { // 9V EV connected, waiting for ready to charge tmpevsestate = EVSE_STATE_B; } - else if ((phigh >= m_ThreshData.m_ThreshCD) || - (!VentReqEnabled() && (phigh > m_ThreshData.m_ThreshD))) { + else if ((m_Pilot.GetState() == PILOT_STATE_PWM) && + ((phigh >= m_ThreshData.m_ThreshCD) ||(!VentReqEnabled() && (phigh > m_ThreshData.m_ThreshD)))) { // 6V ready to charge tmpevsestate = EVSE_STATE_C; } From bad307a721e45e6a9d77aaaabbcf5533bfcba3df Mon Sep 17 00:00:00 2001 From: lincomatic Date: Mon, 14 Sep 2015 21:16:27 -0700 Subject: [PATCH 73/96] hard fault on vent req --- J1772EvseController.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 0fe9725b..d8be9074 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -1281,6 +1281,7 @@ if (TempChkEnabled()) { // vent required not supported chargingOff(); // turn off charging current m_Pilot.SetState(PILOT_STATE_P12); + HardFault(); } else if (m_EvseState == EVSE_STATE_GFCI_FAULT) { // vehicle state F From 131386a63fc04a7bdecb63a588efa20c5660a62d Mon Sep 17 00:00:00 2001 From: lincomatic Date: Mon, 14 Sep 2015 21:19:26 -0700 Subject: [PATCH 74/96] increase I2C delay to 400ms (cwk) --- open_evse.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/open_evse.ino b/open_evse.ino index 9c63b47a..a63dc376 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -2248,7 +2248,7 @@ void setup() { wdt_disable(); - delay(200); // give I2C devices time to be ready before running code that wants to initialize I2C devices. Otherwise a hang can occur upon powerup. + delay(400); // give I2C devices time to be ready before running code that wants to initialize I2C devices. Otherwise a hang can occur upon powerup. Serial.begin(SERIAL_BAUD); From 2eeb234dd993da347953282bb77446112624726b Mon Sep 17 00:00:00 2001 From: lincomatic Date: Mon, 14 Sep 2015 21:19:54 -0700 Subject: [PATCH 75/96] D3.9.9 version tag --- CHANGELOG | 5 +++++ open_evse.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 387ba728..7be4be93 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,10 @@ Change Log +vD3.9.9 CWK/SCL 20150914 +- fix diode check state oscillation bug (cwk/scl) +- hard fault on vent required (scl) +- setup() - increase I2C device delay from 200 -> 400ms (cwk) for Danny ter Haars board + vD3.9.8 CWK 20150912 - fix Temperature Monitoring, broken by accident in D3.9.5 The original users amperage level is restored properly after an event. diff --git a/open_evse.h b/open_evse.h index 325d5251..50440185 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.8" +#define VERSION "D3.9.9" //-- begin features From be53848c9539e7c272ef9c1915bf23dde5318f8f Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Tue, 15 Sep 2015 12:22:50 -0700 Subject: [PATCH 76/96] compacting temperature monitoring, saving 62 bytes --- J1772EvseController.cpp | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index d8be9074..4fec77bb 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -1390,41 +1390,39 @@ if (TempChkEnabled()) { if (!g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_THROTTLE_DOWN ) || // any sensor reaching threshold trips action (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // Throttle back the L2 current advice to the EV - g_TempMonitor.SetOverTemperature(1); currcap /= 2; // set to the throttled back level - setit = 1; + setit = 2; } - - if (g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_RESTORE_AMPERAGE ) && // all sensors need to show return to lower levels + + else if (g_TempMonitor.OverTemperature() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_RESTORE_AMPERAGE ) && // all sensors need to show return to lower levels (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ) && (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_RESTORE_AMPERAGE ))) { // restore the original L2 current advice to the EV - g_TempMonitor.SetOverTemperature(0); - setit = 1; // set to the user's original setting for current } - if (!g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_SHUTDOWN ) || // any sensor reaching threshold trips action + else if (!g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature >= TEMPERATURE_INFRARED_SHUTDOWN ) || // any sensor reaching threshold trips action (g_TempMonitor.m_MCP9808_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ) || (g_TempMonitor.m_DS3231_temperature >= TEMPERATURE_AMBIENT_SHUTDOWN ))) { // Throttle back the L2 current advice to the EV - g_TempMonitor.SetOverTemperatureShutdown(1); - currcap /= 4; - setit = 1; + currcap /= 4; + setit = 4; } - if (g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_THROTTLE_DOWN ) && // all sensors need to show return to lower levels + else if (g_TempMonitor.OverTemperatureShutdown() && ((g_TempMonitor.m_TMP007_temperature <= TEMPERATURE_INFRARED_THROTTLE_DOWN ) && // all sensors need to show return to lower levels (g_TempMonitor.m_MCP9808_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ) && (g_TempMonitor.m_DS3231_temperature <= TEMPERATURE_AMBIENT_THROTTLE_DOWN ))) { // restore the throttled down current advice to the EV since things have cooled down again - g_TempMonitor.SetOverTemperatureShutdown(0); - currcap /= 2; // set to the throttled back level - setit = 1; + setit = 3; } if (setit) { - SetCurrentCapacity(currcap,0,1); - if (m_Pilot.GetState() != PILOT_STATE_PWM) { - m_Pilot.SetPWM(m_CurrentCapacity); - } + if (setit <= 2) + g_TempMonitor.SetOverTemperature(setit-1); + else + g_TempMonitor.SetOverTemperatureShutdown(setit-3); + SetCurrentCapacity(currcap,0,1); + if (m_Pilot.GetState() != PILOT_STATE_PWM) { + m_Pilot.SetPWM(m_CurrentCapacity); + } } } } From 24b8fb897e5d273511abfbfc121a64edcf6eb7d6 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Tue, 15 Sep 2015 12:27:57 -0700 Subject: [PATCH 77/96] compacting temperature monitoring, saving 62 bytes --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 7be4be93..c9cba6a9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ vD3.9.9 CWK/SCL 20150914 - fix diode check state oscillation bug (cwk/scl) - hard fault on vent required (scl) - setup() - increase I2C device delay from 200 -> 400ms (cwk) for Danny ter Haars board +- code cleanup of temperature monitoring to save 62 bytes vD3.9.8 CWK 20150912 - fix Temperature Monitoring, broken by accident in D3.9.5 From abe684952ec0872b01c18839b58d10b5569a4959 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Sun, 20 Sep 2015 16:43:57 -0700 Subject: [PATCH 78/96] GFI Self Test duty cycle restored to 50% Long story explained in this debug history: https://groups.google.com/forum/#!category-topic/openevse/joOssmouqa8 --- open_evse.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/open_evse.h b/open_evse.h index 50440185..b13e7fa3 100644 --- a/open_evse.h +++ b/open_evse.h @@ -389,9 +389,9 @@ #define GFITEST_IDX 6 #define GFI_TEST_CYCLES 60 -// GFI pulse should be 33% duty cycle -#define GFI_PULSE_ON_US 5556 // 1/3 of roughly 60 Hz. -#define GFI_PULSE_OFF_US 11111 // 2/3 of roughly 60 Hz. +// GFI pulse should be 50% duty cycle +#define GFI_PULSE_ON_US 8333 // 1/2 of roughly 60 Hz. +#define GFI_PULSE_OFF_US 8334 // 1/2 of roughly 60 Hz. #endif From e2e0dfc2d66308c7a7135fb01af4de6d2348db92 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Sun, 20 Sep 2015 16:45:21 -0700 Subject: [PATCH 79/96] Updating to D3.9.10 Version --- open_evse.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/open_evse.h b/open_evse.h index b13e7fa3..d5a01509 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.9" +#define VERSION "D3.9.10" //-- begin features From 4b68fa09f07e69fb6894e6088326b240de01572a Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Sun, 20 Sep 2015 16:48:30 -0700 Subject: [PATCH 80/96] GFI Self Test Duty Cycle restored to 50% --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index c9cba6a9..df1ea187 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Change Log +vD3.9.10 CWK 20150920 +- GFI Self Test Duty Cylce restored to 50% + vD3.9.9 CWK/SCL 20150914 - fix diode check state oscillation bug (cwk/scl) - hard fault on vent required (scl) From 558de57a44dac0b4c6170c12ab46111ea536b75d Mon Sep 17 00:00:00 2001 From: lincomatic Date: Mon, 12 Oct 2015 23:27:02 -0700 Subject: [PATCH 81/96] D3.10.0 --- CHANGELOG | 9 +++++++++ J1772EvseController.cpp | 19 +++++++++++++++---- J1772EvseController.h | 2 +- open_evse.h | 2 +- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index df1ea187..5bcdcb1f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,14 @@ Change Log +vD3.10.0 SCL 20151012 +- fix bug introduced by diode check state oscillation fix in D3.9.9 - if + transition to sleep while charging, on sleep exit would go into VENT REQ error + -> if pilot voltage in State C range, but PWM off, go to State B + this is an added safety feature, because State C should never be allowed + when PWM is off +- fix bug: if enter sleep mode due to time/charge limit met, sleep should + automatically be cancelled when car disconnected but it was staying in sleep + vD3.9.10 CWK 20150920 - GFI Self Test Duty Cylce restored to 50% diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 4fec77bb..4a037f21 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -975,6 +975,7 @@ void J1772EVSEController::Update() // automatically cancel the sleep when the car is unplugged cancelTransition = 0; SetLimitSleep(0); + m_EvseState = EVSE_STATE_UNKNOWN; } } #endif //defined(TIME_LIMIT) || defined(CHARGE_LIMIT) @@ -1147,14 +1148,24 @@ if (TempChkEnabled()) { // 9V EV connected, waiting for ready to charge tmpevsestate = EVSE_STATE_B; } - else if ((m_Pilot.GetState() == PILOT_STATE_PWM) && - ((phigh >= m_ThreshData.m_ThreshCD) ||(!VentReqEnabled() && (phigh > m_ThreshData.m_ThreshD)))) { + else if (phigh >= m_ThreshData.m_ThreshCD) { // 6V ready to charge - tmpevsestate = EVSE_STATE_C; + if (m_Pilot.GetState() == PILOT_STATE_PWM) { + tmpevsestate = EVSE_STATE_C; + } + else { + // PWM is off so we can't charge.. force to State B + tmpevsestate = EVSE_STATE_B; + } } else if (phigh > m_ThreshData.m_ThreshD) { // 3V ready to charge vent required - tmpevsestate = EVSE_STATE_D; + if (VentReqEnabled()) { + tmpevsestate = EVSE_STATE_D; + } + else { + tmpevsestate = EVSE_STATE_C; + } } else { tmpevsestate = EVSE_STATE_UNKNOWN; diff --git a/J1772EvseController.h b/J1772EvseController.h index 065df9b4..7e772745 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -72,9 +72,9 @@ typedef struct calibdata { #define ECF_DEFAULT 0x0000 // J1772EVSEController volatile m_bVFlags bits - not saved to EEPROM -#define ECVF_LIMIT_SLEEP 0x04 // currently sleeping after reaching time/charge limit #define ECVF_AUTOSVCLVL_SKIPPED 0x01 // auto svc level test skipped during post #define ECVF_HARD_FAULT 0x02 // in non-autoresettable fault +#define ECVF_LIMIT_SLEEP 0x04 // currently sleeping after reaching time/charge limit #define ECVF_AMMETER_CAL 0x10 // ammeter calibration mode #define ECVF_NOGND_TRIPPED 0x20 // no ground has tripped at least once #define ECVF_CHARGING_ON 0x40 // charging relay is closed diff --git a/open_evse.h b/open_evse.h index d5a01509..ad691cf7 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.9.10" +#define VERSION "D3.10.0" //-- begin features From c6ddbe1ed526a010fa9537f0e531976686078ccb Mon Sep 17 00:00:00 2001 From: William McBrine Date: Sat, 17 Oct 2015 15:00:51 -0400 Subject: [PATCH 82/96] Improved square root function -- much, much faster; same results; and it saves 196 bytes of compiled program space, despite longer source code. --- J1772EvseController.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 4a037f21..eafa1039 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -31,12 +31,25 @@ J1772EVSEController g_EvseController; #ifdef AMMETER static inline unsigned long ulong_sqrt(unsigned long in) { - unsigned long out; - // find the last int whose square is not too big - // Yes, it's wasteful, but we only theoretically ever have to go to 512. - // Removing floating point saves us almost 1K of flash. - for(out = 1; out*out <= in; out++) ; - return out - 1; + unsigned long out = 0; + unsigned long bit = 1 << 30; + + // "bit" starts at the highest power of four <= the argument. + while (bit > in) + bit >>= 2; + + while (bit) { + unsigned long sum = out + bit; + if (in >= sum) { + in -= sum; + out = (out >> 1) + bit; + } + else + out >>= 1; + bit >>= 2; + } + + return out; } void J1772EVSEController::readAmmeter() From 9bddf13a0522fef944e35c091cd0fcaac3507545 Mon Sep 17 00:00:00 2001 From: William McBrine Date: Sat, 17 Oct 2015 15:10:50 -0400 Subject: [PATCH 83/96] Saves 12 bytes, less ugly. :) --- J1772EvseController.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index eafa1039..cf7bfb78 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -152,14 +152,15 @@ J1772EVSEController::J1772EVSEController() : void J1772EVSEController::SaveSettings() { + uint8_t *dest; // n.b. should we add dirty bits so we only write the changed values? or should we just write them on the fly when necessary? - // ugly code below is smaller than this: eeprom_write_byte((uint8_t *)((GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),(byte)GetCurrentCapacity()); if (GetCurSvcLevel() == 1) { - eeprom_write_byte((uint8_t *)EOFS_CURRENT_CAPACITY_L1,(byte)GetCurrentCapacity()); + dest = (uint8_t *)EOFS_CURRENT_CAPACITY_L1; } else { - eeprom_write_byte((uint8_t *)EOFS_CURRENT_CAPACITY_L2,(byte)GetCurrentCapacity()); + dest = (uint8_t *)EOFS_CURRENT_CAPACITY_L2; } + eeprom_write_byte(dest, GetCurrentCapacity()); SaveEvseFlags(); } From cc7234536cc744df063ff334aac48e6ff392d64b Mon Sep 17 00:00:00 2001 From: William McBrine Date: Sat, 17 Oct 2015 15:59:09 -0400 Subject: [PATCH 84/96] Save 52 bytes in u2a(). --- open_evse.ino | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/open_evse.ino b/open_evse.ino index a63dc376..4cabfa6e 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -213,23 +213,16 @@ static inline uint8_t wirerecv(void) { // WARNING: This function uses the *end* of g_sTmp as its buffer char *u2a(unsigned long x,int8_t digits) { - int8_t d = digits; char *s = g_sTmp + sizeof(g_sTmp); + *--s = 0; - if (!x) { - *--s = '0'; - d--; - } - else { - for (; x; x/=10) { - *--s = '0'+ x%10; - d--; - } - } - for (;d > 0;d--) { - *--s = '0'; - } - + + do { + *--s = '0'+ x % 10; + x /= 10; + --digits; + } while (x || digits > 0); + return s; } From 7cf7d0c8e37f005bc9abc002b2aedc3c6cbc4f21 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Sat, 17 Oct 2015 15:06:21 -0700 Subject: [PATCH 85/96] compile error fix --- CHANGELOG | 3 +++ open_evse.ino | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 5bcdcb1f..80f737fa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Change Log +vD3.10.0 SCL 20151017 +- fix compile error caused by include for LiquidCrystal_I2C.h + vD3.10.0 SCL 20151012 - fix bug introduced by diode check state oscillation fix in D3.9.9 - if transition to sleep while charging, on sleep exit would go into VENT REQ error diff --git a/open_evse.ino b/open_evse.ino index 4cabfa6e..34c79074 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -47,7 +47,7 @@ #include "./RTClib.h" #include "open_evse.h" // if using I2CLCD_PCF8574 uncomment below line and comment out LiquidTWI2.h above -//#include +//#include "./LiquidCrystal_I2C.h" #ifdef TEMPERATURE_MONITORING #ifdef MCP9808_IS_ON_I2C #include "./Adafruit_MCP9808.h" // adding the ambient temp sensor to I2C From f5f147970db8ce11109ea2388f324d546aabf021 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Sat, 17 Oct 2015 15:44:13 -0700 Subject: [PATCH 86/96] update version & changelog from PR47 --- CHANGELOG | 5 +++++ open_evse.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 80f737fa..d824e7c8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,10 @@ Change Log +vD3.10.1 SCL 20151017 +- merge PR 47 from wmcbrine + - faster ulong_sqrt() + - code saving tweaks to u2a() & SaveSettings() + vD3.10.0 SCL 20151017 - fix compile error caused by include for LiquidCrystal_I2C.h diff --git a/open_evse.h b/open_evse.h index ad691cf7..2d0bb0cc 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.10.0" +#define VERSION "D3.10.1" //-- begin features From 090debcc9c11bfa8989d8e45034e649211b58452 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Sat, 17 Oct 2015 22:18:42 -0700 Subject: [PATCH 87/96] compiler warning fixes --- Adafruit_TMP007.cpp | 2 +- Adafruit_TMP007.h | 2 +- J1772EvseController.cpp | 24 ++++++++++++------------ J1772EvseController.h | 6 +++--- RTClib.cpp | 4 ++-- open_evse.h | 12 ++++++------ open_evse.ino | 17 +++++++++-------- strings.cpp | 10 +++++----- strings.h | 10 +++++----- 9 files changed, 44 insertions(+), 43 deletions(-) diff --git a/Adafruit_TMP007.cpp b/Adafruit_TMP007.cpp index 17eb9cbb..3933a069 100644 --- a/Adafruit_TMP007.cpp +++ b/Adafruit_TMP007.cpp @@ -25,7 +25,7 @@ Adafruit_TMP007::Adafruit_TMP007(uint8_t i2caddr) { } -boolean Adafruit_TMP007::begin(uint8_t samplerate) { +boolean Adafruit_TMP007::begin(uint16_t samplerate) { //don't need - open_evse.ino does it Wire.begin(); write16(TMP007_CONFIG, TMP007_CFG_MODEON | TMP007_CFG_ALERTEN | diff --git a/Adafruit_TMP007.h b/Adafruit_TMP007.h index 0c60ccb7..8ca19483 100644 --- a/Adafruit_TMP007.h +++ b/Adafruit_TMP007.h @@ -54,7 +54,7 @@ class Adafruit_TMP007 { public: Adafruit_TMP007(uint8_t addr = TMP007_I2CADDR); - boolean begin(uint8_t samplerate = TMP007_CFG_4SAMPLE); // by default go to once per second + boolean begin(uint16_t samplerate = TMP007_CFG_4SAMPLE); // by default go to once per second int16_t readRawDieTemperature(void); int16_t readRawVoltage(void); diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index cf7bfb78..7f36415a 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -32,7 +32,7 @@ J1772EVSEController g_EvseController; static inline unsigned long ulong_sqrt(unsigned long in) { unsigned long out = 0; - unsigned long bit = 1 << 30; + unsigned long bit = 0x40000000ul; // "bit" starts at the highest power of four <= the argument. while (bit > in) @@ -277,7 +277,7 @@ void J1772EVSEController::HardFault() // if we're in P12 state, we can recover from the hard fault when EV // is unplugged if (m_Pilot.GetState() == PILOT_STATE_P12) { - int plow,phigh; + uint16_t plow,phigh; ReadPilot(&plow,&phigh); if (phigh >= m_ThreshData.m_ThreshAB) { // EV disconnected - cancel fault @@ -346,7 +346,7 @@ void J1772EVSEController::EnableTempChk(uint8_t tf) } SaveEvseFlags(); } -#endif TEMPERATURE_MONITORING +#endif // TEMPERATURE_MONITORING void J1772EVSEController::EnableVentReq(uint8_t tf) { @@ -822,10 +822,10 @@ void J1772EVSEController::Init() m_AmmeterCurrentOffset = eeprom_read_word((uint16_t*)EOFS_AMMETER_CURR_OFFSET); m_CurrentScaleFactor = eeprom_read_word((uint16_t*)EOFS_CURRENT_SCALE_FACTOR); - if (m_AmmeterCurrentOffset == 0x0000ffff) { + if (m_AmmeterCurrentOffset == (int16_t)0xffff) { m_AmmeterCurrentOffset = DEFAULT_AMMETER_CURRENT_OFFSET; } - if (m_CurrentScaleFactor == 0x0000ffff) { + if (m_CurrentScaleFactor == (int16_t)0xffff) { m_CurrentScaleFactor = DEFAULT_CURRENT_SCALE_FACTOR; } @@ -923,14 +923,14 @@ void J1772EVSEController::Init() g_OBD.SetGreenLed(0); } -void J1772EVSEController::ReadPilot(int *plow,int *phigh,int loopcnt) +void J1772EVSEController::ReadPilot(uint16_t *plow,uint16_t *phigh,int loopcnt) { - int pl = 1023; - int ph = 0; + uint16_t pl = 1023; + uint16_t ph = 0; // 1x = 114us 20x = 2.3ms 100x = 11.3ms for (int i=0;i < 100;i++) { - int reading = adcPilot.read(); // measures pilot voltage + uint16_t reading = adcPilot.read(); // measures pilot voltage if (reading > ph) { ph = reading; @@ -954,8 +954,8 @@ void J1772EVSEController::ReadPilot(int *plow,int *phigh,int loopcnt) //Negative Voltage - States B, C, D, and F -11.40 -12.00 -12.60 void J1772EVSEController::Update() { - int plow; - int phigh = -127; + uint16_t plow; + uint16_t phigh = 0xffff; unsigned long curms = millis(); @@ -1038,7 +1038,7 @@ void J1772EVSEController::Update() else { // !chargingIsOn() - relay open if (prevevsestate == EVSE_STATE_NO_GROUND) { // check to see if EV disconnected - if (phigh == -127) { + if (phigh == 0xffff) { ReadPilot(&plow,&phigh); } if (phigh >= m_ThreshData.m_ThreshAB) { diff --git a/J1772EvseController.h b/J1772EvseController.h index 7e772745..58ef1485 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -296,7 +296,7 @@ class J1772EVSEController { return (m_wFlags & ECF_TEMP_CHK_DISABLED) ? 0 : 1; } void EnableTempChk(uint8_t tf); -#endif TEMPERATURE_MONITORING +#endif //TEMPERATURE_MONITORING uint8_t SerDbgEnabled() { return (m_wFlags & ECF_SERIAL_DBG) ? 1 : 0; @@ -346,10 +346,10 @@ class J1772EVSEController { int32_t GetChargingCurrent() { return -1; } #endif // AMMETER #ifdef TIME_LIMIT - uint8_t SetTimeLimit(uint8_t mind15) { m_timeLimit = mind15; } + void SetTimeLimit(uint8_t mind15) { m_timeLimit = mind15; } uint8_t GetTimeLimit() { return m_timeLimit; } #endif // TIME_LIMIT - void ReadPilot(int *plow,int *phigh,int loopcnt=PILOT_LOOP_CNT); + void ReadPilot(uint16_t *plow,uint16_t *phigh,int loopcnt=PILOT_LOOP_CNT); void Reboot(); #ifdef SHOW_DISABLED_TESTS void ShowDisabledTests(); diff --git a/RTClib.cpp b/RTClib.cpp index d1f5d801..090ada24 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -54,7 +54,7 @@ DateTime::DateTime (uint32_t t) { uint8_t leap; for (yOff = 0; ; ++yOff) { leap = yOff % 4 == 0; - if (days < 365 + leap) + if (days < (uint16_t)(365 + leap)) break; days -= 365 + leap; } @@ -95,7 +95,7 @@ DateTime::DateTime (const char* date, const char* time) { yOff = conv2d(date + 9); // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec switch (date[0]) { - case 'J': m = date[1] == 'a' ? 1 : m = date[2] == 'n' ? 6 : 7; break; + case 'J': m = (date[1] == 'a') ? 1 : ((date[2] == 'n') ? 6 : 7); break; case 'F': m = 2; break; case 'A': m = date[2] == 'r' ? 4 : 8; break; case 'M': m = date[2] == 'r' ? 3 : 5; break; diff --git a/open_evse.h b/open_evse.h index 2d0bb0cc..4ec588d0 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.10.1" +#define VERSION "D3.10.2" //-- begin features @@ -675,11 +675,11 @@ class OnboardDisplay void LcdPrint(const char *s) { m_Lcd.print(s); } - void LcdPrint_P(const char PROGMEM *s); + void LcdPrint_P(PGM_P s); void LcdPrint(int y,const char *s); - void LcdPrint_P(int y,const char PROGMEM *s); + void LcdPrint_P(int y,PGM_P s); void LcdPrint(int x,int y,const char *s); - void LcdPrint_P(int x,int y,const char PROGMEM *s); + void LcdPrint_P(int x,int y,PGM_P s); void LcdPrint(int i) { m_Lcd.print(i); } @@ -700,7 +700,7 @@ class OnboardDisplay m_Lcd.write(data); } void LcdMsg(const char *l1,const char *l2); - void LcdMsg_P(const char PROGMEM *l1,const char PROGMEM *l2); + void LcdMsg_P(PGM_P l1,PGM_P l2); void LcdSetBacklightType(uint8_t t,uint8_t update=OBD_UPD_FORCE) { // BKL_TYPE_XXX #ifdef RGBLCD if (t == BKL_TYPE_RGB) m_bFlags &= ~OBDF_MONO_BACKLIGHT; @@ -829,7 +829,7 @@ class Btn { class Menu { public: - const char PROGMEM *m_Title; + PGM_P m_Title; uint8_t m_CurIdx; void init(const char *firstitem); diff --git a/open_evse.ino b/open_evse.ino index 34c79074..5b75649f 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -404,21 +404,21 @@ void OnboardDisplay::LcdPrint(int x,int y,const char *s) m_Lcd.print(s); } -void OnboardDisplay::LcdPrint_P(const char PROGMEM *s) +void OnboardDisplay::LcdPrint_P(PGM_P s) { strncpy_P(m_strBuf,s,LCD_MAX_CHARS_PER_LINE); m_strBuf[LCD_MAX_CHARS_PER_LINE] = 0; m_Lcd.print(m_strBuf); } -void OnboardDisplay::LcdPrint_P(int y,const char PROGMEM *s) +void OnboardDisplay::LcdPrint_P(int y,PGM_P s) { strncpy_P(m_strBuf,s,LCD_MAX_CHARS_PER_LINE); m_strBuf[LCD_MAX_CHARS_PER_LINE] = 0; LcdPrint(y,m_strBuf); } -void OnboardDisplay::LcdPrint_P(int x,int y,const char PROGMEM *s) +void OnboardDisplay::LcdPrint_P(int x,int y,PGM_P s) { strncpy_P(m_strBuf,s,LCD_MAX_CHARS_PER_LINE); m_strBuf[LCD_MAX_CHARS_PER_LINE] = 0; @@ -426,7 +426,7 @@ void OnboardDisplay::LcdPrint_P(int x,int y,const char PROGMEM *s) m_Lcd.print(m_strBuf); } -void OnboardDisplay::LcdMsg_P(const char PROGMEM *l1,const char PROGMEM *l2) +void OnboardDisplay::LcdMsg_P(PGM_P l1,PGM_P l2) { LcdPrint_P(0,l1); LcdPrint_P(1,l2); @@ -460,7 +460,6 @@ void OnboardDisplay::Update(int8_t updmode) { if (updateDisabled()) return; - int i; uint8_t curstate = g_EvseController.GetState(); uint8_t svclvl = g_EvseController.GetCurSvcLevel(); int currentcap = g_EvseController.GetCurrentCapacity(); @@ -705,7 +704,9 @@ void OnboardDisplay::Update(int8_t updmode) #endif // AMMETER if (curstate == EVSE_STATE_C) { +#ifndef KWH_RECORDING time_t elapsedTime = g_EvseController.GetElapsedChargeTime(); +#endif #ifdef KWH_RECORDING uint32_t current = g_EvseController.GetChargingCurrent(); @@ -992,7 +993,7 @@ void SettingsMenu::Next() } #endif // CHARGE_LIMIT || TIME_LIMIT - const char PROGMEM *title; + PGM_P title; if (m_CurIdx < m_menuCnt) { title = g_SettingsMenuList[m_CurIdx]->m_Title; } @@ -1037,7 +1038,7 @@ void SetupMenu::Next() m_CurIdx = 0; } - const char PROGMEM *title; + PGM_P title; if (m_CurIdx < m_menuCnt) { title = g_SetupMenuList[m_CurIdx]->m_Title; } @@ -2030,7 +2031,7 @@ void BtnHandler::ChkBtn() if (m_CurMenu) { m_CurMenu = m_CurMenu->Select(); if (m_CurMenu) { - uint8_t curidx; + uint8_t curidx = 0; if ((m_CurMenu == &g_SettingsMenu)||(m_CurMenu == &g_SetupMenu)) { curidx = m_CurMenu->m_CurIdx; } diff --git a/strings.cpp b/strings.cpp index 5b716a9b..e2bd5e70 100644 --- a/strings.cpp +++ b/strings.cpp @@ -47,7 +47,7 @@ const char g_psTempChk[] PROGMEM = "Temperature Chk"; #endif // BTN_MENU || SHOW_DISABLED_TEST #ifdef BTN_MENU -char *g_YesNoMenuItems[] = {"Yes","No"}; +const char *g_YesNoMenuItems[] = {"Yes","No"}; const char g_psResetNow[] PROGMEM = "Restart Now?"; const char g_psReset[] PROGMEM = "Restart"; const char g_psExit[] PROGMEM = "Exit"; @@ -72,7 +72,7 @@ const char g_psChargeLimit[] PROGMEM = "Charge Limit"; const char g_psTimeLimit[] PROGMEM = "Time Limit"; #endif // TIME_LIMIT #ifdef RGBLCD -char *g_BklMenuItems[] = {"RGB","Monochrome"}; +const char *g_BklMenuItems[] = {"RGB","Monochrome"}; #endif // RGBLCD #endif // BTN_MENU @@ -106,13 +106,13 @@ const char g_psEvConnected[] PROGMEM = "Connected"; const char g_psDisabledTests[] PROGMEM = "TEST DISABLED"; #endif -char g_sRdyLAstr[] = "L%d:%dA"; +const char g_sRdyLAstr[] = "L%d:%dA"; const char g_psReady[] PROGMEM = "Ready"; const char g_psCharging[] PROGMEM = "Charging"; -char *g_sMaxCurrentFmt = "%s Max Current"; +const char *g_sMaxCurrentFmt = "%s Max Current"; #endif // LCD16X2 #ifdef DELAYTIMER_MENU const char g_psSetDateTime[] PROGMEM = "Set Date/Time?"; -char *g_DelayMenuItems[] = {"Yes/No","Set Start","Set Stop"}; +const char *g_DelayMenuItems[] = {"Yes/No","Set Start","Set Stop"}; #endif // DELAYTIMER_MENU diff --git a/strings.h b/strings.h index 04822788..a92d39cb 100644 --- a/strings.h +++ b/strings.h @@ -49,7 +49,7 @@ extern const char g_psTempChk[] PROGMEM; #endif // BTN_MENU || SHOW_DISABLED_TEST #ifdef BTN_MENU -extern char *g_YesNoMenuItems[]; +extern const char *g_YesNoMenuItems[]; extern const char g_psResetNow[] PROGMEM; extern const char g_psReset[] PROGMEM; extern const char g_psExit[] PROGMEM; @@ -74,7 +74,7 @@ extern const char g_psChargeLimit[] PROGMEM; extern const char g_psTimeLimit[] PROGMEM; #endif // TIME_LIMIT #ifdef RGBLCD -extern char *g_BklMenuItems[]; +extern const char *g_BklMenuItems[]; #endif // RGBLCD #endif // BTN_MENU @@ -108,13 +108,13 @@ extern const char g_psEvConnected[] PROGMEM; #ifdef SHOW_DISABLED_TESTS extern const char g_psDisabledTests[] PROGMEM; #endif -extern char g_sRdyLAstr[]; +extern const char g_sRdyLAstr[]; extern const char g_psReady[] PROGMEM; extern const char g_psCharging[] PROGMEM; -extern char *g_sMaxCurrentFmt; +extern const char *g_sMaxCurrentFmt; #endif // LCD16X2 #ifdef DELAYTIMER_MENU extern const char g_psSetDateTime[] PROGMEM; -extern char *g_DelayMenuItems[]; +extern const char *g_DelayMenuItems[]; #endif // DELAYTIMER_MENU From 1bb64702ab61a145f64d189d812e1e07a561a8d3 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Sat, 17 Oct 2015 22:37:13 -0700 Subject: [PATCH 88/96] manual merge PR48 --- CHANGELOG | 4 ++++ J1772EvseController.cpp | 24 ++++++++++++------------ J1772EvseController.h | 1 + open_evse.h | 1 + open_evse.ino | 19 +++++++++++-------- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d824e7c8..596d8414 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ Change Log +vD3.10.2 SCL 20151017 +- lots of changes to get rid of 1.6.5 compiler warnings(More) +- wmcbrine PR 48: more code shrinking from - manually merged + vD3.10.1 SCL 20151017 - merge PR 47 from wmcbrine - faster ulong_sqrt() diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index 7f36415a..c8c103d4 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -188,6 +188,12 @@ void J1772EVSEController::Reboot() #ifdef SHOW_DISABLED_TESTS +void J1772EVSEController::DisabledTest_P(PGM_P message) +{ + g_OBD.LcdMsg_P(g_psDisabledTests, message); + delay(SHOW_DISABLED_DELAY); +} + void J1772EVSEController::ShowDisabledTests() { if (m_wFlags & (ECF_DIODE_CHK_DISABLED| @@ -199,33 +205,27 @@ void J1772EVSEController::ShowDisabledTests() g_OBD.LcdSetBacklightColor(YELLOW); if (!DiodeCheckEnabled()) { - g_OBD.LcdMsg_P(g_psDisabledTests,g_psDiodeCheck); - delay(SHOW_DISABLED_DELAY); + DisabledTest_P(g_psDiodeCheck); } if (!VentReqEnabled()) { - g_OBD.LcdMsg_P(g_psDisabledTests,g_psVentReqChk); - delay(SHOW_DISABLED_DELAY); + DisabledTest_P(g_psVentReqChk); } #ifdef ADVPWR if (!GndChkEnabled()) { - g_OBD.LcdMsg_P(g_psDisabledTests,g_psGndChk); - delay(SHOW_DISABLED_DELAY); + DisabledTest_P(g_psGndChk); } if (!StuckRelayChkEnabled()) { - g_OBD.LcdMsg_P(g_psDisabledTests,g_psRlyChk); - delay(SHOW_DISABLED_DELAY); + DisabledTest_P(g_psRlyChk); } #endif // ADVPWR #ifdef GFI_SELFTEST if (!GfiSelfTestEnabled()) { - g_OBD.LcdMsg_P(g_psDisabledTests,g_psGfiTest); - delay(SHOW_DISABLED_DELAY); + DisabledTest_P(g_psGfiTest); } #endif // GFI_SELFTEST #ifdef TEMPERATURE_MONITORING if (!TempChkEnabled()) { - g_OBD.LcdMsg_P(g_psDisabledTests,g_psTempChk); - delay(SHOW_DISABLED_DELAY); + DisabledTest_P(g_psTempChk); } #endif // TEMPERATURE_MONITORING diff --git a/J1772EvseController.h b/J1772EvseController.h index 58ef1485..c85ae241 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -352,6 +352,7 @@ class J1772EVSEController { void ReadPilot(uint16_t *plow,uint16_t *phigh,int loopcnt=PILOT_LOOP_CNT); void Reboot(); #ifdef SHOW_DISABLED_TESTS + void DisabledTest_P(PGM_P message); void ShowDisabledTests(); #endif #ifdef ADVPWR diff --git a/open_evse.h b/open_evse.h index 4ec588d0..20e0266f 100644 --- a/open_evse.h +++ b/open_evse.h @@ -643,6 +643,7 @@ class OnboardDisplay int8_t updateDisabled() { return m_bFlags & OBDF_UPDATE_DISABLED; } + void MakeChar(uint8_t n, PGM_P bytes); public: OnboardDisplay(); void Init(); diff --git a/open_evse.ino b/open_evse.ino index 5b75649f..c3b279e0 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -7,6 +7,7 @@ * timer code Copyright (c) 2013 Kevin L * portions Copyright (c) 2014-2015 Nick Sayer * portions Copyright (c) 2015 Craig Kirkpatrick + * portions Copyright (c) 2015 wmcbrine Revised Ver By Reason 6/21/13 20b3 Scott Rubin fixed LCD display bugs with RTC enabled @@ -346,6 +347,12 @@ const char CustomChar_2[8] PROGMEM = {0x0,0x8,0xc,0xe,0xc,0x8,0x0,0x0}; // play const char CustomChar_3[8] PROGMEM = {0x0,0xe,0xc,0x1f,0x3,0x6,0xc,0x8}; // lightning #endif +void OnboardDisplay::MakeChar(uint8_t n, PGM_P bytes) +{ + memcpy_P(g_sTmp, bytes, 8); + m_Lcd.createChar(n, (uint8_t*)g_sTmp); +} + void OnboardDisplay::Init() { WDT_RESET(); @@ -370,18 +377,14 @@ void OnboardDisplay::Init() LcdSetBacklightColor(WHITE); #if defined(DELAYTIMER)||defined(TIME_LIMIT) - memcpy_P(g_sTmp,CustomChar_0,8); - m_Lcd.createChar(0, (uint8_t*)g_sTmp); + MakeChar(0,CustomChar_0); #endif #ifdef DELAYTIMER - memcpy_P(g_sTmp,CustomChar_1,8); - m_Lcd.createChar(1, (uint8_t*)g_sTmp); - memcpy_P(g_sTmp,CustomChar_2,8); - m_Lcd.createChar(2, (uint8_t*)g_sTmp); + MakeChar(1,CustomChar_1); + MakeChar(2,CustomChar_2); #endif //#ifdef DELAYTIMER #if defined(DELAYTIMER)||defined(CHARGE_LIMIT) - memcpy_P(g_sTmp,CustomChar_3,8); - m_Lcd.createChar(3, (uint8_t*)g_sTmp); + MakeChar(3,CustomChar_3); #endif m_Lcd.clear(); From 082e4c4b7ad58925e53b51ca70efcf70e6430999 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Sat, 17 Oct 2015 22:56:00 -0700 Subject: [PATCH 89/96] fix compiler warnings for OEII --- J1772EvseController.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index c8c103d4..a37b4b0d 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -572,7 +572,10 @@ uint8_t J1772EVSEController::doPost() { WDT_RESET(); - uint8_t RelayOff, Relay1, Relay2; //Relay Power status + uint8_t RelayOff; +#ifndef OPENEVSE_2 + uint8_t Relay1, Relay2; //Relay Power status +#endif uint8_t svcState = UD; // service state = undefined #ifdef SERIALCLI @@ -714,7 +717,9 @@ uint8_t J1772EVSEController::doPost() #endif //#else OPENEVSE_2 } else { // ! AutoSvcLevelEnabled +#ifndef OPENEVSE_2 stuckrelaychk: +#endif if (StuckRelayChkEnabled()) { RelayOff = ReadACPins(); if ((RelayOff & 3) != 3) { From 427f8c946e1056254ab1c130820fd4a13b717699 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Sun, 18 Oct 2015 12:49:29 -0700 Subject: [PATCH 90/96] Correct accumulate of kWh and reset Wh in an out of sleep state --- J1772EvseController.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index a37b4b0d..ab8cd88a 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -457,6 +457,12 @@ void J1772EVSEController::Disable() void J1772EVSEController::Sleep() { +#ifdef KWH_RECORDING // Reset the Wh when exiting State A for any reason + if (m_EvseState == EVSE_STATE_A) { + g_WattSeconds = 0; + } +#endif + if (m_EvseState != EVSE_STATE_SLEEPING) { m_Pilot.SetState(PILOT_STATE_P12); m_EvseState = EVSE_STATE_SLEEPING; @@ -1254,10 +1260,8 @@ if (TempChkEnabled()) { chargingOff(); // turn off charging current m_Pilot.SetState(PILOT_STATE_P12); #ifdef KWH_RECORDING - if ((prevevsestate == EVSE_STATE_C) || (prevevsestate == EVSE_STATE_B)) { g_WattHours_accumulated = g_WattHours_accumulated + (g_WattSeconds / 3600); eeprom_write_dword((uint32_t*)EOFS_KWH_ACCUMULATED,g_WattHours_accumulated); - } #endif // KWH_RECORDING #ifdef CHARGE_LIMIT SetChargeLimit(0); @@ -1269,11 +1273,6 @@ if (TempChkEnabled()) { else if (m_EvseState == EVSE_STATE_B) { // connected chargingOff(); // turn off charging current m_Pilot.SetPWM(m_CurrentCapacity); - #ifdef KWH_RECORDING - if (prevevsestate == EVSE_STATE_A) { - g_WattSeconds = 0; - } - #endif } else if (m_EvseState == EVSE_STATE_C) { m_Pilot.SetPWM(m_CurrentCapacity); @@ -1301,11 +1300,6 @@ if (TempChkEnabled()) { #endif // FT_GFI_LOCKOUT chargingOn(); // turn on charging current - #ifdef KWH_RECORDING - if (prevevsestate == EVSE_STATE_A) { - g_WattSeconds = 0; - } - #endif } else if (m_EvseState == EVSE_STATE_D) { // vent required not supported @@ -1375,7 +1369,11 @@ if (TempChkEnabled()) { Serial.println(phigh); } #endif //#ifdef SERIALCLI - + #ifdef KWH_RECORDING // Reset the Wh when exiting State A for any reason + if (prevevsestate == EVSE_STATE_A) { + g_WattSeconds = 0; + } + #endif } // state transition #ifdef UL_COMPLIANT From 1cfad5c75d204d2f80c345892a6b299a6f48a765 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Sun, 18 Oct 2015 12:50:58 -0700 Subject: [PATCH 91/96] Update open_evse.h --- open_evse.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/open_evse.h b/open_evse.h index 20e0266f..abce9593 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.10.2" +#define VERSION "D3.10.3" //-- begin features From 425983216bca3fa0d665e99b6653d438ad0e30e1 Mon Sep 17 00:00:00 2001 From: Craig Kirkpatrick Date: Sun, 18 Oct 2015 12:53:09 -0700 Subject: [PATCH 92/96] Correct accumulate of kWh and reset Wh in an out of sleep state --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 596d8414..54f61bb1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Change Log +vD3.10.3 CWK 20151018 +- Correcting accumulating kWh and resetting Wh going in and out of sleep state + vD3.10.2 SCL 20151017 - lots of changes to get rid of 1.6.5 compiler warnings(More) - wmcbrine PR 48: more code shrinking from - manually merged From 5e9b67a1ec3a20f7ccca83a3eab1fcaa94e6c528 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Sun, 18 Oct 2015 23:09:54 -0700 Subject: [PATCH 93/96] update copyright message --- open_evse.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/open_evse.ino b/open_evse.ino index c3b279e0..df42dfa7 100644 --- a/open_evse.ino +++ b/open_evse.ino @@ -7,7 +7,7 @@ * timer code Copyright (c) 2013 Kevin L * portions Copyright (c) 2014-2015 Nick Sayer * portions Copyright (c) 2015 Craig Kirkpatrick - * portions Copyright (c) 2015 wmcbrine + * portions Copyright (c) 2015 William McBrine Revised Ver By Reason 6/21/13 20b3 Scott Rubin fixed LCD display bugs with RTC enabled From 3cf98e1a2b8e9ce3b2e79ac69f68b25365485bce Mon Sep 17 00:00:00 2001 From: lincomatic Date: Thu, 22 Oct 2015 22:30:06 -0700 Subject: [PATCH 94/96] SERDBG + RTClib tweak --- CHANGELOG | 6 ++++ J1772EvseController.cpp | 68 +++++++++++++++++++++++------------------ RTClib.cpp | 2 +- open_evse.h | 5 ++- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 54f61bb1..4b89923a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,11 @@ Change Log +SCL 20151022 +- added SERDBG - decouple serial diags from SERIALCLI + +SCL 20151019 +- slight tweak to RTClib.cpp compiler warning fix in 3.10.2 + vD3.10.3 CWK 20151018 - Correcting accumulating kWh and resetting Wh going in and out of sleep state diff --git a/J1772EvseController.cpp b/J1772EvseController.cpp index ab8cd88a..0db508a5 100644 --- a/J1772EvseController.cpp +++ b/J1772EvseController.cpp @@ -488,12 +488,11 @@ void J1772EVSEController::LoadThresholds() void J1772EVSEController::SetSvcLevel(uint8_t svclvl,uint8_t updatelcd) { -#ifdef SERIALCLI +#ifdef SERDBG if (SerDbgEnabled()) { - g_CLI.printlnn(); - g_CLI.print_P(PSTR("SetSvcLevel: "));Serial.println((int)svclvl); + Serial.print("SetSvcLevel: ");Serial.println((int)svclvl); } -#endif //#ifdef SERIALCLI +#endif //#ifdef SERDBG if (svclvl == 2) { m_wFlags |= ECF_L2; // set to Level 2 } @@ -584,11 +583,11 @@ uint8_t J1772EVSEController::doPost() #endif uint8_t svcState = UD; // service state = undefined -#ifdef SERIALCLI +#ifdef SERDBG if (SerDbgEnabled()) { - g_CLI.print_P(PSTR("POST start...")); + Serial.print("POST start..."); } -#endif //#ifdef SERIALCLI +#endif //#ifdef SERDBG m_Pilot.SetState(PILOT_STATE_P12); //check to see if EV is plugged in @@ -607,12 +606,12 @@ uint8_t J1772EVSEController::doPost() } else { svcState = L1; } -#ifdef SERIALCLI +#ifdef SERDBG if (SerDbgEnabled()) { - g_CLI.print_P(PSTR("AC millivolts: "));Serial.println(ac_volts); - g_CLI.print_P(PSTR("SvcState: "));Serial.println((int)svcState); + Serial.print("AC millivolts: ");Serial.println(ac_volts); + Serial.print("SvcState: ");Serial.println((int)svcState); } -#endif //#ifdef SERIALCLI +#endif //#ifdef SERDBG #ifdef LCD16X2 g_OBD.LcdMsg_P(g_psAutoDetect,(svcState == L2) ? g_psLevel2 : g_psLevel1); #endif //LCD16x2 @@ -621,12 +620,11 @@ uint8_t J1772EVSEController::doPost() delay(150); // delay reading for stable pilot before reading int reading = adcPilot.read(); //read pilot -#ifdef SERIALCLI +#ifdef SERDBG if (SerDbgEnabled()) { - g_CLI.printlnn(); - g_CLI.print_P(PSTR("Pilot: "));Serial.println((int)reading); + Serial.print("Pilot: ");Serial.println((int)reading); } -#endif //#ifdef SERIALCLI +#endif //#ifdef SERDBG m_Pilot.SetState(PILOT_STATE_N12); if (reading > 900) { // IF EV is not connected its Okay to open the relay the do the L1/L2 and ground Check @@ -693,14 +691,14 @@ uint8_t J1772EVSEController::doPost() svcState = SR; } } -#ifdef SERIALCLI +#ifdef SERDBG if (SerDbgEnabled()) { - g_CLI.print_P(PSTR("RelayOff: "));Serial.println((int)RelayOff); - g_CLI.print_P(PSTR("Relay1: "));Serial.println((int)Relay1); - g_CLI.print_P(PSTR("Relay2: "));Serial.println((int)Relay2); - g_CLI.print_P(PSTR("SvcState: "));Serial.println((int)svcState); + Serial.print("RelayOff: ");Serial.println((int)RelayOff); + Serial.print("Relay1: ");Serial.println((int)Relay1); + Serial.print("Relay2: ");Serial.println((int)Relay2); + Serial.print("SvcState: ");Serial.println((int)svcState); } -#endif //#ifdef SERIALCLI +#endif //#ifdef SERDBG // update LCD #ifdef LCD16X2 @@ -760,12 +758,12 @@ uint8_t J1772EVSEController::doPost() } m_Pilot.SetState(PILOT_STATE_P12); -#ifdef SERIALCLI +#ifdef SERDBG if (SerDbgEnabled()) { - g_CLI.print_P(PSTR("POST result: ")); + Serial.print("POST result: "); Serial.println((int)svcState); } -#endif //#ifdef SERIALCLI +#endif //#ifdef SERDBG WDT_RESET(); @@ -829,6 +827,10 @@ void J1772EVSEController::Init() m_wFlags |= ECF_DIODE_CHK_DISABLED|ECF_VENT_REQ_DISABLED|ECF_GND_CHK_DISABLED|ECF_STUCK_RELAY_CHK_DISABLED|ECF_GFI_TEST_DISABLED|ECF_TEMP_CHK_DISABLED; #endif +#ifdef SERDBG + EnableSerDbg(1); +#endif + #ifdef AMMETER m_AmmeterCurrentOffset = eeprom_read_word((uint16_t*)EOFS_AMMETER_CURR_OFFSET); m_CurrentScaleFactor = eeprom_read_word((uint16_t*)EOFS_CURRENT_SCALE_FACTOR); @@ -1357,18 +1359,24 @@ if (TempChkEnabled()) { #ifdef RAPI g_ERP.sendEvseState(); #endif // RAPI -#ifdef SERIALCLI +#ifdef SERDBG if (SerDbgEnabled()) { - g_CLI.print_P(PSTR("state: ")); + Serial.print("state: "); + switch (m_Pilot.GetState()) { + case PILOT_STATE_P12: Serial.print("P12"); break; + case PILOT_STATE_PWM: Serial.print("PWM"); break; + case PILOT_STATE_N12: Serial.print("N12"); break; + } + Serial.print(" "); Serial.print((int)prevevsestate); - g_CLI.print_P(PSTR("->")); + Serial.print("->"); Serial.print((int)m_EvseState); - g_CLI.print_P(PSTR(" p ")); + Serial.print(" p "); Serial.print(plow); - g_CLI.print_P(PSTR(" ")); + Serial.print(" "); Serial.println(phigh); } -#endif //#ifdef SERIALCLI +#endif //#ifdef SERDBG #ifdef KWH_RECORDING // Reset the Wh when exiting State A for any reason if (prevevsestate == EVSE_STATE_A) { g_WattSeconds = 0; diff --git a/RTClib.cpp b/RTClib.cpp index 090ada24..32aa526a 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -54,7 +54,7 @@ DateTime::DateTime (uint32_t t) { uint8_t leap; for (yOff = 0; ; ++yOff) { leap = yOff % 4 == 0; - if (days < (uint16_t)(365 + leap)) + if (days < 365u + leap) break; days -= 365 + leap; } diff --git a/open_evse.h b/open_evse.h index abce9593..8291028d 100644 --- a/open_evse.h +++ b/open_evse.h @@ -196,7 +196,7 @@ #endif //If LCD and RTC is defined, un-define CLI so we can save ram space. -#if defined(SERIALCLI) && defined(DELAYTIMER) +#if defined(SERIALCLI) && defined(DELAYTIMER_MENU) #error INVALID CONFIG - CANNOT enable SERIALCLI with DELAYTIMER_MENU together - too big #endif @@ -212,6 +212,9 @@ #error INVALID CONFIG - GFI SELF TEST NEEDED FOR UL COMPLIANCE #endif +// for testing print various diagnostic messages to the UART +//#define SERDBG + // // begin functional tests // From b6ba83c075e576fc19777c1b8f0abaeaae987cb3 Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 10 Nov 2015 13:22:03 -0800 Subject: [PATCH 95/96] update version number --- CHANGELOG | 3 +++ open_evse.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 4b89923a..2d34b4e4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Change Log +vD3.10.4 CWK 20151110 +- update version number + SCL 20151022 - added SERDBG - decouple serial diags from SERIALCLI diff --git a/open_evse.h b/open_evse.h index 8291028d..861a59a2 100644 --- a/open_evse.h +++ b/open_evse.h @@ -36,7 +36,7 @@ #include "WProgram.h" // shouldn't need this but arduino sometimes messes up and puts inside an #ifdef #endif // ARDUINO -#define VERSION "D3.10.3" +#define VERSION "D3.10.4" //-- begin features From d59c333d2c0c05c38dbf790832e65244e16fb95d Mon Sep 17 00:00:00 2001 From: lincomatic Date: Tue, 10 Nov 2015 13:27:08 -0800 Subject: [PATCH 96/96] correction --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 2d34b4e4..58cb1d7b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,6 @@ Change Log -vD3.10.4 CWK 20151110 +vD3.10.4 SCL 20151110 - update version number SCL 20151022