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/CHANGELOG b/CHANGELOG index fdd945cd..f71b2644 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,93 @@ Change Log +SC: 20151011 +v3.10.4 +- vD3.10.4 promoted to stable + +vD3.10.4 SCL 20151110 +- update version number + +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 + +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() + - code saving tweaks to u2a() & SaveSettings() + +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 + -> 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% + +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 + 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 + +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 + +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% + +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 + +vD3.9.2 SCL 20150606 +- fix $GF bug + +vD3.9.1 SCL 20150605 +- boost I2C bus to 400KHz + + SCL20150605 -> promote to v3.9.0 in stable branch Many thanks to CraigK for his numerous contributions and top notch testing. diff --git a/Gfi.cpp b/Gfi.cpp index 0240cf05..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_DURATION_US); + delayMicroseconds(GFI_PULSE_ON_US); pinTest.write(0); - delayMicroseconds(GFI_PULSE_DURATION_US); - if (testSuccess) break; // no need to keep trying. + delayMicroseconds(GFI_PULSE_OFF_US); } // wait for GFI pin to clear 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/J1772EvseController.cpp b/J1772EvseController.cpp index 324ef9aa..0db508a5 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 = 0x40000000ul; + + // "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() @@ -139,8 +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? - eeprom_write_byte((uint8_t *)((GetCurSvcLevel() == 1) ? EOFS_CURRENT_CAPACITY_L1 : EOFS_CURRENT_CAPACITY_L2),(byte)GetCurrentCapacity()); + if (GetCurSvcLevel() == 1) { + dest = (uint8_t *)EOFS_CURRENT_CAPACITY_L1; + } + else { + dest = (uint8_t *)EOFS_CURRENT_CAPACITY_L2; + } + eeprom_write_byte(dest, GetCurrentCapacity()); SaveEvseFlags(); } @@ -168,39 +188,46 @@ 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| 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()) { - 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()) { + DisabledTest_P(g_psTempChk); + } +#endif // TEMPERATURE_MONITORING g_OBD.LcdSetBacklightColor(WHITE); } @@ -250,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 @@ -308,6 +335,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) { @@ -390,6 +430,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; @@ -414,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; @@ -439,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 } @@ -529,14 +577,17 @@ 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 +#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 @@ -555,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 @@ -569,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 @@ -641,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 @@ -671,7 +721,9 @@ uint8_t J1772EVSEController::doPost() #endif //#else OPENEVSE_2 } else { // ! AutoSvcLevelEnabled +#ifndef OPENEVSE_2 stuckrelaychk: +#endif if (StuckRelayChkEnabled()) { RelayOff = ReadACPins(); if ((RelayOff & 3) != 3) { @@ -706,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(); @@ -719,19 +771,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 @@ -785,17 +824,21 @@ 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 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); - 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; } @@ -824,22 +867,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; @@ -902,14 +936,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; @@ -933,8 +967,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; + uint16_t plow; + uint16_t phigh = 0xffff; unsigned long curms = millis(); @@ -943,6 +977,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 @@ -959,8 +994,22 @@ 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); + m_EvseState = EVSE_STATE_UNKNOWN; + } + } +#endif //defined(TIME_LIMIT) || defined(CHARGE_LIMIT) + if (cancelTransition) { + m_PrevEvseState = m_EvseState; // cancel state transition + return; + } } uint8_t prevevsestate = m_EvseState; @@ -979,8 +1028,7 @@ void J1772EVSEController::Update() m_EvseState = EVSE_STATE_NO_GROUND; chargingOff(); // open the relay - - if ((prevevsestate != EVSE_STATE_NO_GROUND) && (m_NoGndTripCnt < 253)) { + 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); } @@ -1003,7 +1051,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 == 0xffff) { + ReadPilot(&plow,&phigh); + } if (phigh >= m_ThreshData.m_ThreshAB) { // EV disconnected - cancel fault m_EvseState = EVSE_STATE_UNKNOWN; @@ -1030,7 +1080,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) && (((uint8_t)(m_StuckRelayTripCnt+1)) < 254)) { m_StuckRelayTripCnt++; eeprom_write_byte((uint8_t*)EOFS_STUCK_RELAY_TRIP_CNT,m_StuckRelayTripCnt); } @@ -1050,7 +1100,7 @@ void J1772EVSEController::Update() m_EvseState = EVSE_STATE_GFCI_FAULT; if (prevevsestate != EVSE_STATE_GFCI_FAULT) { // state transition - if (m_GfiTripCnt < 253) { + if (((uint8_t)(m_GfiTripCnt+1)) < 254) { m_GfiTripCnt++; eeprom_write_byte((uint8_t*)EOFS_GFI_TRIP_CNT,m_GfiTripCnt); } @@ -1091,6 +1141,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)) { @@ -1098,6 +1149,7 @@ void J1772EVSEController::Update() m_EvseState = EVSE_STATE_OVER_TEMPERATURE; nofault = 0; } +} #endif // TEMPERATURE_MONITORING if (nofault) { @@ -1123,14 +1175,24 @@ void J1772EVSEController::Update() // 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 (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; @@ -1185,9 +1247,9 @@ void J1772EVSEController::Update() 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; } } @@ -1213,11 +1275,6 @@ void J1772EVSEController::Update() 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); @@ -1235,9 +1292,9 @@ void J1772EVSEController::Update() #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"); @@ -1245,16 +1302,12 @@ void J1772EVSEController::Update() #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 chargingOff(); // turn off charging current m_Pilot.SetState(PILOT_STATE_P12); + HardFault(); } else if (m_EvseState == EVSE_STATE_GFCI_FAULT) { // vehicle state F @@ -1280,6 +1333,8 @@ void J1772EVSEController::Update() // 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 @@ -1304,19 +1359,29 @@ void J1772EVSEController::Update() #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; + } + #endif } // state transition #ifdef UL_COMPLIANT @@ -1353,54 +1418,55 @@ 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; - 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 - 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); + } } } + } #endif // TEMPERATURE_MONITORING #ifdef CHARGE_LIMIT if (m_chargeLimit && (g_WattSeconds >= 3600000 * (uint32_t)m_chargeLimit)) { SetChargeLimit(0); // reset charge limit + SetLimitSleep(1); Sleep(); } #endif @@ -1410,6 +1476,7 @@ void J1772EVSEController::Update() // 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 25ae741d..c85ae241 100644 --- a/J1772EvseController.h +++ b/J1772EvseController.h @@ -68,11 +68,13 @@ 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 #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 @@ -85,7 +87,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 +116,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 @@ -159,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; @@ -167,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 @@ -270,10 +273,16 @@ 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; } - 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; @@ -281,6 +290,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; } @@ -325,22 +342,22 @@ 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; } + void 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(); + 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 - 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/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/RTClib.cpp b/RTClib.cpp index d1f5d801..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 < 365 + leap) + if (days < 365u + 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/flash.bat b/flash.bat index 6b6a2517..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:0xDF:m -U efuse:w:0x05:m -avrdude -c USBasp -p m328p -U flash:w:open_evse-390.hex +avrdude -c USBasp -p m328p -U flash:w:open_evse-384.hex pause diff --git a/open_evse.h b/open_evse.h index 7c0e7f33..d661f2c4 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 "3.9.0" +#define VERSION "3.10.4" //-- 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 @@ -124,10 +125,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. @@ -199,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 @@ -215,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 // @@ -362,6 +362,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 @@ -390,7 +392,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 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 @@ -418,7 +422,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 @@ -518,15 +522,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 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 - // 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. +// 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 650 +#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 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 680 +#endif -#define TEMPERATURE_AMBIENT_PANIC 580 // 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 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. #define TEMPERATURE_INFRARED_RESTORE_AMPERAGE 600 // If the OpenEVSE responds nicely to the lower current drawn and temperatures in the enclosure @@ -609,7 +635,6 @@ class OnboardDisplay #endif #if defined(RGBLCD) || defined(I2CLCD) #ifdef I2CLCD_PCF8574 -#include LiquidCrystal_I2C m_Lcd; #else LiquidTWI2 m_Lcd; @@ -621,6 +646,7 @@ class OnboardDisplay int8_t updateDisabled() { return m_bFlags & OBDF_UPDATE_DISABLED; } + void MakeChar(uint8_t n, PGM_P bytes); public: OnboardDisplay(); void Init(); @@ -653,11 +679,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); } @@ -678,7 +704,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; @@ -729,12 +755,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; @@ -742,6 +770,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 @@ -766,6 +799,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 @@ -796,7 +833,7 @@ class Btn { class Menu { public: - const char PROGMEM *m_Title; + PGM_P m_Title; uint8_t m_CurIdx; void init(const char *firstitem); @@ -868,6 +905,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(); @@ -1107,6 +1154,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[]; @@ -1129,8 +1179,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 53023151..df42dfa7 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 William McBrine Revised Ver By Reason 6/21/13 20b3 Scott Rubin fixed LCD display bugs with RTC enabled @@ -47,7 +48,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 @@ -69,6 +70,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,15 +135,17 @@ Menu *g_SetupMenuList[] = { #ifdef GFI_SELFTEST &g_GfiTestMenu, #endif // GFI_SELFTEST +#ifdef TEMPERATURE_MONITORING + &g_TempOnOffMenu, +#endif // TEMPERATURE_MONITORING NULL }; #ifdef BTN_MENU BtnHandler g_BtnHandler; #endif // BTN_MENU -#ifdef DELAYTIMER + #define g_sHHMMfmt "%02d:%02d" -#endif// DELAYTIMER //-- begin global variables @@ -208,23 +214,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; } @@ -243,6 +242,26 @@ 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); + 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); +} +#endif // TEMPERATURE_MONITORING_NY + void TempMonitor::Init() { m_Flags = 0; @@ -250,6 +269,10 @@ 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(); #endif // MCP9808_IS_ON_I2C @@ -261,35 +284,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 @@ -319,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(); @@ -343,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(); @@ -377,21 +407,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; @@ -399,7 +429,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); @@ -433,7 +463,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(); @@ -637,7 +666,7 @@ void OnboardDisplay::Update(int8_t updmode) // 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); @@ -678,7 +707,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(); @@ -830,7 +861,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; @@ -965,7 +996,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; } @@ -1010,7 +1041,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; } @@ -1283,6 +1314,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; @@ -1962,7 +2034,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; } @@ -2107,8 +2179,8 @@ void DelayTimer::CheckTime() } } } + m_LastCheck = curms; } - m_LastCheck = curms; } } void DelayTimer::Enable(){ @@ -2132,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(); @@ -2156,7 +2245,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); @@ -2190,7 +2279,7 @@ void loop() g_OBD.Update(); - g_EvseController.ProcessInputs(); + ProcessInputs(); // Delay Timer Handler - GoldServe #ifdef DELAYTIMER diff --git a/rapi_proc.cpp b/rapi_proc.cpp index 7b34d0d5..5a194b66 100644 --- a/rapi_proc.cpp +++ b/rapi_proc.cpp @@ -311,6 +311,15 @@ int EvseRapiProcessor::processCmd() } break; #endif // VOLTMETER +#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 #ifdef ADVPWR case 'R': // stuck relay check if (tokenCnt == 2) { @@ -412,6 +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, diff --git a/rapi_proc.h b/rapi_proc.h index 9720db9d..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 @@ -141,7 +143,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 @@ -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 diff --git a/strings.cpp b/strings.cpp index f3f0c24f..e2bd5e70 100644 --- a/strings.cpp +++ b/strings.cpp @@ -41,10 +41,13 @@ 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 -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"; @@ -69,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 @@ -103,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 95f21152..a92d39cb 100644 --- a/strings.h +++ b/strings.h @@ -43,10 +43,13 @@ 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 -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; @@ -71,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 @@ -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; @@ -104,14 +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 - 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