diff --git a/lib/AT24Cx/AT24CX.cpp b/lib/AT24Cx/AT24CX.cpp new file mode 100644 index 0000000000..bca40187cd --- /dev/null +++ b/lib/AT24Cx/AT24CX.cpp @@ -0,0 +1,345 @@ +/** + +AT24CX.cpp +Library for using the EEPROM AT24C32/64 + +Copyright (c) 2014 Christian Paul + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + */ +#include "AT24CX.h" +#include + +/** + * Constructor with AT24Cx EEPROM at index 0 + */ +AT24CX::AT24CX() { + init(0, 32, 4096); +} + +/** + * Constructor with AT24Cx EEPROM at given index, size of page and max size in bytes + */ +AT24CX::AT24CX(uint8_t index, uint8_t pageSize, uint32_t maxSize) { + init(index, pageSize, maxSize); +} + +/** + * Constructor with AT24C32 EEPROM at index 0 + */ +AT24C32::AT24C32() { + init(0, 32, 4096); +} +/** + * Constructor with AT24Cx EEPROM at given index + */ +AT24C32::AT24C32(uint8_t index) { + init(index, 32, 4096); +} + +/** + * Constructor with AT24C64 EEPROM at index 0 + */ +AT24C64::AT24C64() { + init(0, 32, 8192); +} +/** + * Constructor with AT24C64 EEPROM at given index + */ +AT24C64::AT24C64(uint8_t index) { + init(index, 32, 8192); +} + +/** + * Constructor with AT24C128 EEPROM at index 0 + */ +AT24C128::AT24C128() { + init(0, 64, 16384); +} +/** + * Constructor with AT24C128 EEPROM at given index + */ +AT24C128::AT24C128(uint8_t index) { + init(index, 64, 16384); +} + +/** + * Constructor with AT24C256 EEPROM at index 0 + */ +AT24C256::AT24C256() { + init(0, 64, 32768); +} +/** + * Constructor with AT24C128 EEPROM at given index + */ +AT24C256::AT24C256(uint8_t index) { + init(index, 64, 32768); +} + +/** + * Constructor with AT24C512 EEPROM at index 0 + */ +AT24C512::AT24C512() { + init(0, 128, 65536); +} +/** + * Constructor with AT24C512 EEPROM at given index + */ +AT24C512::AT24C512(uint8_t index) { + init(index, 128, 65536); +} + +/** + * Init + */ +void AT24CX::init(uint8_t index, uint8_t pageSize, uint32_t maxSize) { + _id = AT24CX_ID | (index & 0x7); + _pageSize = pageSize; + _maxSize = maxSize; + // Wire.begin(); +} + +/** + * Check address not reached + */ +bool AT24CX::checkSize(uint32_t address, uint32_t size) { + return (address + size - 1) <= _maxSize; +} + +/** + * Write byte + */ +void AT24CX::write(uint32_t address, uint8_t data) { + if (!checkSize(address, 1)) { + return; + } + Wire.beginTransmission(_id); + if(Wire.endTransmission()==0) { + Wire.beginTransmission(_id); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + Wire.write(data); + Wire.endTransmission(); + delay(20); + } +} + +/** + * Write integer + */ +void AT24CX::writeInt(uint32_t address, uint16_t data) { + write(address, (uint8_t*)&data, 2); +} + +/** + * Write long + */ +void AT24CX::writeLong(uint32_t address, uint32_t data) { + write(address, (uint8_t*)&data, 4); +} + +/** + * Write float + */ +void AT24CX::writeFloat(uint32_t address, float data) { + write(address, (uint8_t*)&data, 4); +} + +/** + * Write double + */ +void AT24CX::writeDouble(uint32_t address, double data) { + write(address, (uint8_t*)&data, 8); +} + +/** + * Write chars + */ +void AT24CX::writeChars(uint32_t address, char *data, int length) { + write(address, (uint8_t*)data, length); +} + +/** + * Write bytes + */ +void AT24CX::writeBytes(uint32_t address, uint8_t *data, int length) { + write(address, data, length); +} + +/** + * Read integer + */ +uint16_t AT24CX::readInt(uint32_t address) { + memset(_b, 0, sizeof(_b)); + read(address, _b, 2); + return *(uint32_t*)&_b[0]; +} + +/** + * Read long + */ +uint32_t AT24CX::readLong(uint32_t address) { + read(address, _b, 4); + return *(unsigned long*)&_b[0]; +} + +/** + * Read float + */ +float AT24CX::readFloat(uint32_t address) { + read(address, _b, 4); + return *(float*)&_b[0]; +} + +/** + * Read double + */ +double AT24CX::readDouble(uint32_t address) { + read(address, _b, 8); + return *(double*)&_b[0]; +} + +/** + * Read chars + */ +void AT24CX::readChars(uint32_t address, char *data, int n) { + read(address, (uint8_t*)data, n); +} + +/** + * Read bytes + */ +void AT24CX::readBytes(uint32_t address, uint8_t *data, int n) { + read(address, data, n); +} + +/** + * Write sequence of n bytes + */ +void AT24CX::write(uint32_t address, uint8_t *data, int n) { + if (!checkSize(address, n)) { + return; + } + // status quo + int c = n; // bytes left to write + int offD = 0; // current offset in data pointer + int offP; // current offset in page + int nc = 0; // next n bytes to write + + // write alle bytes in multiple steps + while (c > 0) { + // calc offset in page + offP = address % _pageSize; + // maximal 30 bytes to write + nc = min(min(c, 30), _pageSize - offP); + write(address, data, offD, nc); + c-=nc; + offD+=nc; + address+=nc; + } +} + +/** + * Write sequence of n bytes from offset + */ +void AT24CX::write(uint32_t address, uint8_t *data, int offset, int n) { + if (!checkSize(address, n)) { + return; + } + Wire.beginTransmission(_id); + if (Wire.endTransmission()==0) { + Wire.beginTransmission(_id); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + uint8_t *adr = data+offset; + Wire.write(adr, n); + Wire.endTransmission(); + delay(20); + } +} + +/** + * Read byte + */ +uint8_t AT24CX::read(uint32_t address) { + if (!checkSize(address, 1)) { + return 0; + } + uint8_t b = 0; + int r = 0; + Wire.beginTransmission(_id); + if (Wire.endTransmission()==0) { + Wire.beginTransmission(_id); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + if (Wire.endTransmission()==0) { + Wire.requestFrom(_id, 1); + while (Wire.available() > 0 && r<1) { + b = (uint8_t)Wire.read(); + r++; + } + } + } + return b; +} + +/** + * Read sequence of n bytes + */ +void AT24CX::read(uint32_t address, uint8_t *data, int n) { + if (!checkSize(address, n)) { + return; + } + int c = n; + int offD = 0; + // read until are n bytes read + while (c > 0) { + // read maximal 32 bytes + int nc = c; + if (nc > 32) + nc = 32; + read(address, data, offD, nc); + address+=nc; + offD+=nc; + c-=nc; + } +} + + +/** + * Read sequence of n bytes to offset + */ +void AT24CX::read(uint32_t address, uint8_t *data, int offset, int n) { + Wire.beginTransmission(_id); + if (Wire.endTransmission()==0) { + Wire.beginTransmission(_id); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + if (Wire.endTransmission()==0) { + int r = 0; + Wire.requestFrom(_id, n); + while (Wire.available() > 0 && r +#include + +// // byte +// typedef uint8_t byte; + +// AT24Cx I2C adress +// 80 +// 0x50 +#define AT24CX_ID 0b01010000 + +// general class definition +class AT24CX { +public: + AT24CX(); + AT24CX(uint8_t index, uint8_t pageSize, uint32_t maxSize); + void write(uint32_t address, uint8_t data); + void write(uint32_t address, uint8_t *data, int n); + void writeInt(uint32_t address, uint16_t data); + void writeLong(uint32_t address, uint32_t data); + void writeFloat(uint32_t address, float data); + void writeDouble(uint32_t address, double data); + void writeChars(uint32_t address, char *data, int length); + void writeBytes(uint32_t address, uint8_t *data, int length); + uint8_t read(uint32_t address); + void read(uint32_t address, uint8_t *data, int n); + uint16_t readInt(uint32_t address); + uint32_t readLong(uint32_t address); + float readFloat(uint32_t address); + double readDouble(uint32_t address); + void readChars(uint32_t address, char *data, int n); + void readBytes(uint32_t address, uint8_t *data, int n); +protected: + void init(uint8_t index, uint8_t pageSize, uint32_t maxSize); +private: + void read(uint32_t address, uint8_t *data, int offset, int n); + void write(uint32_t address, uint8_t *data, int offset, int n); + bool checkSize(uint32_t address, uint32_t size); + int _id; + uint8_t _b[8]; + uint8_t _pageSize; + uint32_t _maxSize{}; +}; + +// AT24C32 class definiton +class AT24C32 : public AT24CX { +public: + AT24C32(); + AT24C32(uint8_t index); +}; + +// AT24C64 class definiton +class AT24C64 : public AT24CX { +public: + AT24C64(); + AT24C64(uint8_t index); +}; + +// AT24C128 class definiton +class AT24C128 : public AT24CX { +public: + AT24C128(); + AT24C128(uint8_t index); +}; + +// AT24C256 class definiton +class AT24C256 : public AT24CX { +public: + AT24C256(); + AT24C256(uint8_t index); +}; + +// AT24C512 class definiton +class AT24C512 : public AT24CX { +public: + AT24C512(); + AT24C512(uint8_t index); +}; + + + +#endif diff --git a/lib/AT24Cx/AT24CX_demo/AT24CX_demo.ino b/lib/AT24Cx/AT24CX_demo/AT24CX_demo.ino new file mode 100644 index 0000000000..b49cadab5c --- /dev/null +++ b/lib/AT24Cx/AT24CX_demo/AT24CX_demo.ino @@ -0,0 +1,128 @@ +/* +* +* Read and write demo of the AT24CX library +* Written by Christian Paul, 2014-11-24 +* +* +*/ + +// include libraries +#include +#include + +// EEPROM object +AT24CX mem; + +// setup +void setup() { + // serial init + Serial.begin(115200); + Serial.println("AT24CX read/write demo"); + Serial.println("----------------------"); +} + +// main loop +void loop() { + // read and write byte + Serial.println("Write 42 to address 12"); + mem.write(12, 42); + Serial.println("Read byte from address 12 ..."); + byte b = mem.read(12); + Serial.print("... read: "); + Serial.println(b, DEC); + Serial.println(); + + // read and write integer + Serial.println("Write 65000 to address 15"); + mem.writeInt(15, 65000); + Serial.println("Read integer from address 15 ..."); + unsigned int i = mem.readInt(15); + Serial.print("... read: "); + Serial.println(i, DEC); + Serial.println(); + + // read and write long + Serial.println("Write 3293732729 to address 20"); + mem.writeLong(20, 3293732729UL); + Serial.println("Read long from address 20 ..."); + unsigned long l = mem.readLong(20); + Serial.print("... read: "); + Serial.println(l, DEC); + Serial.println(); + + // read and write long + Serial.println("Write 1111111111 to address 31"); + mem.writeLong(31, 1111111111); + Serial.println("Read long from address 31 ..."); + unsigned long l2 = mem.readLong(31); + Serial.print("... read: "); + Serial.println(l2, DEC); + Serial.println(); + + // read and write float + Serial.println("Write 3.14 to address 40"); + mem.writeFloat(40, 3.14); + Serial.println("Read float from address 40 ..."); + float f = mem.readFloat(40); + Serial.print("... read: "); + Serial.println(f, DEC); + Serial.println(); + + // read and write double + Serial.println("Write 3.14159265359 to address 50"); + mem.writeDouble(50, 3.14159265359); + Serial.println("Read double from address 50 ..."); + double d = mem.readDouble(50); + Serial.print("... read: "); + Serial.println(d, DEC); + Serial.println(); + + // read and write char + Serial.print("Write chars: '"); + char msg[] = "This is a message"; + Serial.print(msg); + Serial.println("' to address 200"); + mem.writeChars(200, msg, sizeof(msg)); + Serial.println("Read chars from address 200 ..."); + char msg2[30]; + mem.readChars(200, msg2, sizeof(msg2)); + Serial.print("... read: '"); + Serial.print(msg2); + Serial.println("'"); + Serial.println(); + + // write array of bytes + Serial.println("Write array of 80 bytes at address 1000"); + byte xy[] = {0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9, // 10 x 3 = 30 + 10,11,12,13,14,15,16,17,18,19, // 10 + 120,121,122,123,124,125,126,127,128,129, // 10 + 130,131,132,133,134,135,136,137,138,139, // 10 + 200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219}; // 20 + mem.write(1000, (byte*)xy, sizeof(xy)); + + // read bytes with multiple steps + Serial.println("Read 80 single bytes starting at address 1000"); + for (int i=0; i for definitons and differences. + +Written by Christian Paul, 2014 - 2015. +This software is released under the terms of the MIT license. +See the file LICENSE or LIZENZ for details, please. + +You can use any of the eight possibles EEPROM devices on the I2C bus. + +Constructor + + AT24CX(byte pageSize); + +uses the device with index 0 and given page size. You can select a device with given index between 0 and 8 with constructor + + AT24CX(byte index, byte pageSize); + +Than, you can single write or read single bytes from the EEPROM with + + void write(unsigned int address, byte data); + byte read(unsigned int address); + +or write and read an array of bytes with + + void write(unsigned int address, byte *data, int n); + void read(unsigned int address, byte *data, int n); + +For writing integers, long, float, double or sequences of chars you can use the comfort functions + + void writeInt(unsigned int address, unsigned int data); + void writeLong(unsigned int address, unsigned long data); + void writeFloat(unsigned int address, float data); + void writeDouble(unsigned int address, double data); + void writeChars(unsigned int address, char *data, int length); + +Reading the values is done by using + + unsigned int readInt(unsigned int address); + unsigned long readLong(unsigned int address); + float readFloat(unsigned int address); + double readDouble(unsigned int address); + void readChars(unsigned int address, char *data, int n); + +Alternative you can use the individual classes with predefined page sizes: + + AT24C32(); + AT24C64(); + AT24C128(); + AT24C256(); + AT24C512(); + +or with different index than 0: + + AT24C32(byte index); + AT24C64(byte index); + AT24C128(byte index); + AT24C256(byte index); + AT24C512(byte index); + diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp new file mode 100644 index 0000000000..0e6d06ce5d --- /dev/null +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp @@ -0,0 +1,415 @@ +#include "../Helpers/EEPROMExternal.h" +#include "../../../src/Globals/Settings.h" +#include "../../../src/Helpers/I2C_access.h" +#include "../../../ESPEasy_common.h" +#include "../../../src/Helpers/StringConverter.h" + +#if FEATURE_EEPROM_EXTERNAL + +namespace ESPEasy { +namespace eeprom { +AT24CX *EEPROMExternal = nullptr; +EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::Undefined; +bool EEPROMParamsOkState{}; +LongTermTimer EEPROMParamsOkTimer; + +constexpr uint32_t sizeof_eeprom_slot = sizeof(double); + +/** + * Initialize the external EEPROM device and variables + */ +void initializeEEPROMExternal() { + const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); + + if ((nullptr != ESPEasy::eeprom::EEPROMExternal) || (eepromAddress == 0)) { // Cleanup when turning off EEPROM + delete ESPEasy::eeprom::EEPROMExternal; + ESPEasy::eeprom::EEPROMExternal = nullptr; + ESPEasy::eeprom::EEPROMExternalWriteProtect = ESPEasy::eeprom::EEPROMExternal_WriteProtect_e::Undefined; + } + + if ((nullptr == ESPEasy::eeprom::EEPROMExternal) && (eepromAddress > 0)) { + const ESPEasy::eeprom::EEPROMExternal_Type_e eepromType = + static_cast(Settings.EEPROMExternalType()); + + if (0 != ESPEasy::eeprom::selectEEPROMI2CBusAndMultiplexer()) { // Switch to I2C Bus and multiplexer channel of External EEPROM + // We have an I2C device at this address, let's assume it's an EEPROM... + uint8_t pageSize = 0; + const uint32_t eepromSize = ESPEasy::eeprom::getEEPROMSize(eepromType, pageSize); + ESPEasy::eeprom::EEPROMExternal = new (std::nothrow) AT24CX(eepromAddress, pageSize, eepromSize); + + if (nullptr != ESPEasy::eeprom::EEPROMExternal) { + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("EEPROM: %s initialized at address 0x%02x"), + FsP(ESPEasy::eeprom::getEEPROMName(eepromType)), + eepromAddress)); + } + + ESPEasy::eeprom::checkEEPROMExternalWriteProtected(); + + if (ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { + addLog(LOG_LEVEL_INFO, concat(F("EEPROM: Write-protected! Status: "), + static_cast(ESPEasy::eeprom::checkEEPROMExternalWriteProtected()))); + } + } else { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, strformat(F("EEPROM: Initialization of %s failed"), + FsP(ESPEasy::eeprom::getEEPROMName(eepromType)))); + } + } + } else { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, strformat(F("EEPROM: No %s found at address 0x%02x"), + FsP(ESPEasy::eeprom::getEEPROMName(eepromType)), + eepromAddress)); + } + } + + # if FEATURE_I2CMULTIPLEXER + I2CMultiplexerOff( + # if FEATURE_I2C_MULTIPLE + Settings.getI2CInterfaceEEPROM() + # else // if FEATURE_I2C_MULTIPLE + 0 + # endif // if FEATURE_I2C_MULTIPLE + ); // Restore the Multiplexer channel + # endif // if FEATURE_I2CMULTIPLEXER + } +} + +/** + * Check the stored parameters in the EEPROM with current data and settings + * - Version + * - Max. tasks + * - Vars per tasks + * - RTC Cache address + * - Pinstate address + */ +bool validateEEPROMExternalParameters(bool force) { + if (!force && (EEPROMParamsOkTimer.millisPassedSince() < EEPROM_PARAMSOK_STATE_TIMEOUT)) { // When called within timeout return cached + // result + return EEPROMParamsOkState; + } + const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); + + EEPROMParamsOkTimer.setNow(); + EEPROMParamsOkState = false; + + if (EEPROM_PARAMS_CURRENT_VERSION == eepromVersionParam) { + EEPROMParamsOkState = true; + } + + return EEPROMParamsOkState; +} + +/** + * Update the stored parameters in EEPROM + * - Version + */ +void updateEEPROMExternalParameters() { + const uint16_t eepromVersionParam = EEPROMExternal->readInt(EEPROM_PARAMS_VERSION_ADDRESS); + + if (EEPROM_PARAMS_CURRENT_VERSION != eepromVersionParam) { + EEPROMExternal->writeInt(EEPROM_PARAMS_VERSION_ADDRESS, EEPROM_PARAMS_CURRENT_VERSION); + } +} + +/** + * Check if the EEPROM is properly initialized and enabled. + * Returns the I2C address if all is OK + */ +uint8_t checkEEPROMEnabled() { + const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); + + if ((nullptr != EEPROMExternal) && (eepromAddress > 0)) { // EEPROM Configured? + return eepromAddress; + } + return 0; +} + +/** + * Check if the EEPROM is write-protected + * when forced = false only detect if current state is Undefined + * - read a random byte in the first half of the address space (some chips ony WP the first half of the space!) + * - write 0xAA and read back -> if unequal: read-only + * - write 0x55 and read back -> if unequal: read-only + * - Still OK: + * - restore original byte + * - Set status read-write + */ +EEPROMExternal_WriteProtect_e checkEEPROMExternalWriteProtected(bool forced) { + if ((nullptr != EEPROMExternal) && ((EEPROMExternal_WriteProtect_e::Undefined == EEPROMExternalWriteProtect) || forced)) { + const uint32_t addr = random(0, getEEPROMSize(static_cast(Settings.EEPROMExternalType())) / 2); + const uint8_t original = EEPROMExternal->read(addr); + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("EEPROM: Writeable check, addr: 0x%04x data: 0x%02X"), addr, original)); + } + # endif // ifndef BUILD_NO_DEBUG + EEPROMExternal->write(addr, 0xAA); + uint8_t newdata = EEPROMExternal->read(addr); + + if (0xAA != newdata) { // write failed + EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::ReadOnly; + } else { + EEPROMExternal->write(addr, 0x55); + newdata = EEPROMExternal->read(addr); + + if (0x55 != newdata) { // write failed + EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::ReadOnly; + } else { + EEPROMExternal->write(addr, original); + EEPROMExternalWriteProtect = EEPROMExternal_WriteProtect_e::ReadWrite; + } + } + } + return EEPROMExternalWriteProtect; +} + +/** + * Is the EEPROM WriteProtected? + */ +bool isEEPROMExternalWriteProtected() { return EEPROMExternal_WriteProtect_e::ReadWrite != checkEEPROMExternalWriteProtected(); } + +/** + * Switch to I2C Bus and multiplexer channel of External EEPROM + */ +uint8_t selectEEPROMI2CBusAndMultiplexer() { + const uint8_t eepromAddress = Settings.EEPROMExternalI2CAddress(); + + if (eepromAddress > 0) { // EEPROM Configured? + # if FEATURE_I2C_MULTIPLE + const uint8_t i2cBus = Settings.getI2CInterfaceEEPROM(); + # else // if FEATURE_I2C_MULTIPLE + constexpr uint8_t i2cBus = 0; + # endif // if FEATURE_I2C_MULTIPLE + + I2CSelectHighClockSpeed(i2cBus); + + # if FEATURE_I2CMULTIPLEXER + const uint16_t eepromFlags = Settings.EEPROMExternalI2CMultiplexerFlags(); + const int eepromMuxPort = get8BitFromUL(eepromFlags, EEPROM_MUX_FLAGS_PORT); + const bool eepromMulti = bitRead(eepromFlags, EEPROM_MUX_FLAGS_MULTI); + I2CMultiplexerSelectByBusAndMux(i2cBus, eepromMulti, eepromMuxPort); + # endif // if FEATURE_I2CMULTIPLEXER + + if (0 == I2C_wakeup(eepromAddress)) { + return eepromAddress; + } + } + return 0; +} + +/** + * EEPROM size in bytes + */ +uint32_t getEEPROMSize(EEPROMExternal_Type_e type) { + switch (type) + { + case EEPROMExternal_Type_e::AT24C256: + case EEPROMExternal_Type_e::MB85RC256: + return 32768ul; + case EEPROMExternal_Type_e::AT24C512: + case EEPROMExternal_Type_e::MB85RC512: + return 65536ul; + # if EEPROM_SUPPORT_AT24C1024 + case EEPROMExternal_Type_e::AT24C1024: + case EEPROMExternal_Type_e::MB85RC1M: + return 131072ul; + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C2048: + case EEPROMExternal_Type_e::MB85RC2M: + return 262144ul; + # endif // if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C32: + case EEPROMExternal_Type_e::MB85RC32: + return 4096ul; + case EEPROMExternal_Type_e::AT24C64: + case EEPROMExternal_Type_e::MB85RC64: + return 8192ul; + case EEPROMExternal_Type_e::AT24C128: + case EEPROMExternal_Type_e::MB85RC128: + return 16384ul; + } + return 0; +} + +/** + * EEPROM pagesize in bytes + */ +uint32_t getEEPROMSize(EEPROMExternal_Type_e type, + uint8_t & pageSize) { + pageSize = 0; + + switch (type) + { + case EEPROMExternal_Type_e::AT24C256: + case EEPROMExternal_Type_e::MB85RC256: + pageSize = 64u; + case EEPROMExternal_Type_e::AT24C512: + case EEPROMExternal_Type_e::MB85RC512: + pageSize = 128u; + # if EEPROM_SUPPORT_AT24C1024 + case EEPROMExternal_Type_e::AT24C1024: + case EEPROMExternal_Type_e::MB85RC1M: + pageSize = 128u; + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C2048: + case EEPROMExternal_Type_e::MB85RC2M: + pageSize = 128u; + # endif // if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C32: + case EEPROMExternal_Type_e::MB85RC32: + pageSize = 32u; + case EEPROMExternal_Type_e::AT24C64: + case EEPROMExternal_Type_e::MB85RC64: + pageSize = 32u; + case EEPROMExternal_Type_e::AT24C128: + case EEPROMExternal_Type_e::MB85RC128: + pageSize = 64u; + } + return getEEPROMSize(type); +} + +/** + * EEPROM/FRAM name + */ +const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type) { + switch (type) + { + case EEPROMExternal_Type_e::AT24C256: + return F("AT24C256"); + case EEPROMExternal_Type_e::AT24C512: + return F("AT24C512"); + # if EEPROM_SUPPORT_AT24C1024 + case EEPROMExternal_Type_e::AT24C1024: + return F("AT24C1024"); + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C2048: + return F("AT24C2048"); + # endif // if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C32: + return F("AT24C32"); + case EEPROMExternal_Type_e::AT24C64: + return F("AT24C64"); + case EEPROMExternal_Type_e::AT24C128: + return F("AT24C128"); + case EEPROMExternal_Type_e::MB85RC256: + return F("MB85RC256"); + case EEPROMExternal_Type_e::MB85RC512: + return F("MB85RC512"); + # if EEPROM_SUPPORT_AT24C1024 + case EEPROMExternal_Type_e::MB85RC1M: + return F("MB85RC1M"); + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::MB85RC2M: + return F("MB85RC2M"); + # endif // if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::MB85RC32: + return F("MB85RC32"); + case EEPROMExternal_Type_e::MB85RC64: + return F("MB85RC64"); + case EEPROMExternal_Type_e::MB85RC128: + return F("MB85RC128"); + } + return F(""); +} + +/** + * EEPROM address for slot or 0xFFFF when error + */ +uint32_t getEEPROMAddressForSlot(uint32_t slot) { + if (checkEEPROMEnabled() > 0) { + const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); + + if ((eepromSize > 0) && (slot < getEEPROMMaxSlots())) { + const uint32_t slotAddr = EEPROM_CUSTOM_START_OFFSET + (slot * sizeof_eeprom_slot); + + if (slotAddr < eepromSize) { + return slotAddr; + } + } + } + return std::numeric_limits::max(); +} + +/** + * EEPROM available number of slots, max use all available space minus some administrative bytes + */ +uint32_t getEEPROMMaxSlots() { + if (checkEEPROMEnabled() > 0) { + const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); + + if (eepromSize > 0) { + const uint32_t slotMax = (unsigned long)(((eepromSize - EEPROM_CUSTOM_START_OFFSET) / EEPROM_CUSTOM_DIVISOR) / sizeof_eeprom_slot); + + return slotMax; + } + } + return 0; +} + +/** + * EEPROM write value to slot if the slot is valid + */ +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + +bool writeEEPROMSlot(uint32_t slot, + double data) +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + +bool writeEEPROMSlot(uint32_t slot, + float data) +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +{ + const uint32_t addr = getEEPROMAddressForSlot(slot); + + if ((addr != std::numeric_limits::max()) && !isEEPROMExternalWriteProtected()) { + # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + const double oldData = EEPROMExternal->readDouble(addr); + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + const float oldData = EEPROMExternal->readDouble(addr); + # endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + + if (!essentiallyEqual(oldData, data)) { + EEPROMExternal->writeDouble(addr, data); // Always write double size! + } + return true; + } + return false; +} + +/** + * EEPROM read value from slot or 0 when invalid + */ +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + +double readEEPROMSlot(uint32_t slot) { + const uint32_t addr = getEEPROMAddressForSlot(slot); + + if (addr != std::numeric_limits::max()) { + return EEPROMExternal->readDouble(addr); + } + return 0.0; +} + +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + +float readEEPROMSlot(uint32_t slot) { + const uint32_t addr = getEEPROMAddressForSlot(slot); + + if (addr != std::numeric_limits::max()) { + return EEPROMExternal->readDouble(addr); // Always read double size! + } + return 0.0f; +} + +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + +} // namespace eeprom +} // namespace ESPEasy +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h new file mode 100644 index 0000000000..634f0bc34d --- /dev/null +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h @@ -0,0 +1,101 @@ +#pragma once +#include "../../../ESPEasy_common.h" + +#if FEATURE_EEPROM_EXTERNAL + +# include "../../../src/DataTypes/TaskIndex.h" +# include "../../../src/Helpers/LongTermTimer.h" + +# include + +namespace ESPEasy { +namespace eeprom { +enum class EEPROMExternal_WriteProtect_e : uint8_t { + Undefined = 0, + ReadWrite = 1, + ReadOnly = 2, + +}; + +extern AT24CX *EEPROMExternal; +extern EEPROMExternal_WriteProtect_e EEPROMExternalWriteProtect; +extern bool EEPROMParamsOkState; +extern LongTermTimer EEPROMParamsOkTimer; + +# define EEPROM_PARAMS_CURRENT_VERSION (1) // Let's start with version 1 + +// Start from this offset +# define EEPROM_BASERTC_START_OFFSET (0) + +// Some system parameters to check before restoring anything +# define EEPROM_PARAMS_VERSION_ADDRESS (32) + +// Start writing the Custom slot values from this offset so we have some room for settings, if needed +# define EEPROM_CUSTOM_START_OFFSET (EEPROM_BASERTC_START_OFFSET + 128) +# define EEPROM_CUSTOM_DIVISOR (1u) // Use all for slots + +// Enable/disable some models +# define EEPROM_SUPPORT_AT24C1024 1 +# define EEPROM_SUPPORT_AT24C2048 0 + +# define EEPROM_PARAMSOK_STATE_TIMEOUT (180000) // 3 minutes + +// Supported AT24Cxxx and MB85RCxxx devices +enum class EEPROMExternal_Type_e : uint8_t { + AT24C256 = 0, // Default, 32 kB + AT24C512 = 1, // 64 kB + # if EEPROM_SUPPORT_AT24C1024 + AT24C1024 = 2, // 128 kB + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + AT24C2048 = 3, // 256 kB (not supported yet) + # endif // if EEPROM_SUPPORT_AT24C2048 + AT24C32 = 4, // 4 kB, not endorsed, but widely available + AT24C64 = 5, // 8 kB + AT24C128 = 6, // 16 kB + MB85RC256 = 7, // 32 kB + MB85RC512 = 8, // 64 kB + # if EEPROM_SUPPORT_AT24C1024 + MB85RC1M = 9, // 128 kB + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + MB85RC2M = 10, // 256 kB (not supported yet) + # endif // if EEPROM_SUPPORT_AT24C2048 + MB85RC32 = 11, // 4 kB, not endorsed, possibly not available + MB85RC64 = 12, // 8 kB + MB85RC128 = 13, // 16 kB + +}; + +void initializeEEPROMExternal(); + +bool validateEEPROMExternalParameters(bool force = false); +void updateEEPROMExternalParameters(); + +uint8_t checkEEPROMEnabled(); +EEPROMExternal_WriteProtect_e checkEEPROMExternalWriteProtected(bool forced = false); +bool isEEPROMExternalWriteProtected(); + +uint8_t selectEEPROMI2CBusAndMultiplexer(); + +uint32_t getEEPROMSize(EEPROMExternal_Type_e type); +uint32_t getEEPROMSize(EEPROMExternal_Type_e type, + uint8_t & pageSize); +const __FlashStringHelper* getEEPROMName(EEPROMExternal_Type_e type); + +uint32_t getEEPROMAddressForSlot(uint32_t slot); + +uint32_t getEEPROMMaxSlots(); +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +bool writeEEPROMSlot(uint32_t slot, + double data); +double readEEPROMSlot(uint32_t slot); +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +bool writeEEPROMSlot(uint32_t slot, + float data); +float readEEPROMSlot(uint32_t slot); +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + +} // namespace eeprom +} // namespace ESPEasy +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Commands/EEPROMExternal.cpp b/src/src/Commands/EEPROMExternal.cpp new file mode 100644 index 0000000000..da332d51df --- /dev/null +++ b/src/src/Commands/EEPROMExternal.cpp @@ -0,0 +1,57 @@ +#include "../Commands/EEPROMExternal.h" +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" + +#include "../../ESPEasy_common.h" + +#include "../Commands/Common.h" + +#include "../DataStructs/ESPEasy_EventStruct.h" + +#include "../Helpers/Misc.h" +#include "../Helpers/Numerical.h" +#include "../Helpers/StringConverter.h" + +#if FEATURE_EEPROM_EXTERNAL + +// Command: WriteEE,, : set a slot value. 0 is 'erased' +// Command: WriteEE,erase,erase : reset all slots to 0 +const __FlashStringHelper* Command_writeEE(struct EventStruct *event, const char *Line) +{ + uint32_t slot{}; + + # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + double value{}; + bool validValue = validDoubleFromString(parseString(Line, 3), value); + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + float value{}; + bool validValue = validFloatFromString(parseString(Line, 3), value); + # endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + + if (validUIntFromString(parseString(Line, 2), slot) && validValue) { + return return_command_boolean_result_flashstr(ESPEasy::eeprom::writeEEPROMSlot(slot, value)); + } else if (equals(parseString(Line, 2), F("erase")) && equals(parseString(Line, 3), F("erase"))) { + for (uint32_t slot = 0; slot < ESPEasy::eeprom::getEEPROMMaxSlots(); ++slot) { + # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + ESPEasy::eeprom::writeEEPROMSlot(slot, 0.0); + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + ESPEasy::eeprom::writeEEPROMSlot(slot, 0.0f); + # endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + + if (slot % 50 == 0) { delay(0); } + } + addLog(LOG_LEVEL_INFO, F("EEPROM: All slot-values erased.")); + return return_command_success_flashstr(); + } else if (equals(parseString(Line, 2), F("check")) && equals(parseString(Line, 3), F("wp"))) { + addLog(LOG_LEVEL_INFO, F("EEPROM: Check write-protect.")); + ESPEasy::eeprom::checkEEPROMExternalWriteProtected(true); + + if (ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { + addLog(LOG_LEVEL_INFO, + concat(F("EEPROM: Write-protected! Status: "), static_cast(ESPEasy::eeprom::checkEEPROMExternalWriteProtected()))); + } + return return_command_success_flashstr(); + } + return return_command_failed_flashstr(); +} + +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Commands/EEPROMExternal.h b/src/src/Commands/EEPROMExternal.h new file mode 100644 index 0000000000..c42ac560a4 --- /dev/null +++ b/src/src/Commands/EEPROMExternal.h @@ -0,0 +1,8 @@ +#pragma once + +#include "../../ESPEasy_common.h" + +#if FEATURE_EEPROM_EXTERNAL +const __FlashStringHelper* Command_writeEE(struct EventStruct *event, + const char *Line); +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Commands/InternalCommands.cpp b/src/src/Commands/InternalCommands.cpp index b76b20248d..ed80947744 100644 --- a/src/src/Commands/InternalCommands.cpp +++ b/src/src/Commands/InternalCommands.cpp @@ -11,6 +11,9 @@ #include "../Commands/Common.h" #include "../Commands/Controller.h" #include "../Commands/Diagnostic.h" +#if FEATURE_EEPROM_EXTERNAL +#include "../Commands/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL #include "../Commands/GPIO.h" #include "../Commands/HTTP.h" #include "../Commands/InternalCommands_decoder.h" @@ -511,7 +514,11 @@ bool InternalCommands::executeInternalCommand() case ESPEasy_cmd_e::wifissid: COMMAND_CASE_R(Command_Wifi_SSID, 1); // WiFi.h case ESPEasy_cmd_e::wifissid2: COMMAND_CASE_R(Command_Wifi_SSID2, 1); // WiFi.h case ESPEasy_cmd_e::wifistamode: COMMAND_CASE_R(Command_Wifi_STAMode, 0); // WiFi.h -#endif +#endif // if FEATURE_WIFI +#if FEATURE_EEPROM_EXTERNAL + case ESPEasy_cmd_e::writeee: COMMAND_CASE_R(Command_writeEE, 2); // EEPROMExternal.h +#endif // if FEATURE_EEPROM_EXTERNAL + case ESPEasy_cmd_e::NotMatched: return false; diff --git a/src/src/Commands/InternalCommands_decoder.cpp b/src/src/Commands/InternalCommands_decoder.cpp index 77849e31c3..52613a6b70 100644 --- a/src/src/Commands/InternalCommands_decoder.cpp +++ b/src/src/Commands/InternalCommands_decoder.cpp @@ -317,6 +317,9 @@ const char Internal_commands_w[] PROGMEM = "wdconfig|" "wdread|" #endif // ifndef LIMIT_BUILD_SIZE +#if FEATURE_EEPROM_EXTERNAL + "writeee|" +#endif // if FEATURE_EEPROM_EXTERNAL ; #endif diff --git a/src/src/Commands/InternalCommands_decoder.h b/src/src/Commands/InternalCommands_decoder.h index 4815d2c59e..b908d1dc7e 100644 --- a/src/src/Commands/InternalCommands_decoder.h +++ b/src/src/Commands/InternalCommands_decoder.h @@ -258,6 +258,9 @@ enum class ESPEasy_cmd_e : uint8_t { wdconfig, wdread, #endif // ifndef LIMIT_BUILD_SIZE +#if FEATURE_EEPROM_EXTERNAL + writeee, +#endif // if FEATURE_EEPROM_EXTERNAL NotMatched // Keep as last one diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 04748d003d..7cc8f2eae0 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -4323,6 +4323,19 @@ To create/register a plugin, you have to : #endif #endif // if FEATURE_TASKVALUE_ATTRIBUTES +#ifndef FEATURE_EEPROM_EXTERNAL + #ifdef ESP32 + #define FEATURE_EEPROM_EXTERNAL 1 + #endif + #ifdef ESP8266 + #ifdef LIMIT_BUILD_SIZE + #define FEATURE_EEPROM_EXTERNAL 0 // Disabled for limited builds on ESP8266 + #else + #define FEATURE_EEPROM_EXTERNAL 1 // Enabled by default on ESP8266 + #endif + #endif +#endif // ifndef FEATURE_EEPROM_EXTERNAL + #ifndef FEATURE_PLUGIN_LIST #ifdef ESP32 #define FEATURE_PLUGIN_LIST 1 diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index 7433096f57..5bf20e3aa4 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -48,9 +48,21 @@ #define I2C_PERIPHERAL_BUS_CLOCK 0 // bit-offset for I2C bus used for the RTC clock device #define I2C_PERIPHERAL_BUS_WDT 3 // bit-offset for I2C bus used for the watchdog timer #define I2C_PERIPHERAL_BUS_PCFMCP 6 // bit-offset for I2C bus used for PCF & MCP direct access -// #define I2C_PERIPHERAL_BUS_??? 9 // bit-offset for I2C bus used for the ??? +#if FEATURE_EEPROM_EXTERNAL +#define I2C_PERIPHERAL_BUS_EEPROM 9 // bit-offset for I2C bus used for an external EEPROM +#endif // if FEATURE_EEPROM_EXTERNAL +// #define I2C_PERIPHERAL_BUS_??? 12 // bit-offset for I2C bus used for the ??? #endif // if FEATURE_I2C_MULTIPLE +#if FEATURE_EEPROM_EXTERNAL +#define EEPROM_EXTERNAL_FLAGS_ADDRESS 0 // bit-offset for the I2C Address (8 bits) +#define EEPROM_EXTERNAL_FLAGS_SIZE 8 // bit-offset for the size-id of the EEPROM (4 bits) +#define EEPROM_EXTERNAL_FLAGS_MUX 16 // bit-offset for the multiplexer flags of the EEPROM (16 bits) + +#define EEPROM_MUX_FLAGS_PORT 0 // bit-offset within multiplexerflags for the portnr/bits (8 bits) +#define EEPROM_MUX_FLAGS_MULTI 8 // bit-offset within multiplexerflags for bits or port (1 bit) + +#endif // if FEATURE_EEPROM_EXTERNAL // Stored in Settings.I2C_SPI_bus_Flags !!! #define SPI_FLAGS_TASK_BUS_NUMBER 0 // 2 bit, stores the configured bus for a task // Stored in Settings.I2C_SPI_bus_Flags for Task 1 settings diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.h b/src/src/DataStructs/ExtraTaskSettingsStruct.h index 95c41cedb0..dc5ad44614 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.h +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.h @@ -108,6 +108,12 @@ struct ExtraTaskSettingsStruct float TaskDeviceMaxValue[VARS_PER_TASK]; float TaskDeviceErrorValue[VARS_PER_TASK]; uint32_t VariousBits[VARS_PER_TASK]; + /** Mapping of VariousBits: + * - 0..7 : PluginStats config (8 bits) + * - 8..15 : UnitOfMeasure index (8 bits) + * - 16..23 : CustomValueType index (8 bits) + * - 24 : Store value in EEPROM (1 bit) + */ }; diff --git a/src/src/DataStructs/RTC_cache_handler_struct.cpp b/src/src/DataStructs/RTC_cache_handler_struct.cpp index 60f3e0962e..e6f1815844 100644 --- a/src/src/DataStructs/RTC_cache_handler_struct.cpp +++ b/src/src/DataStructs/RTC_cache_handler_struct.cpp @@ -23,7 +23,6 @@ ESPEasy_RTC_ATTR RTC_cache_struct RTC_cache; ESPEasy_RTC_ATTR uint8_t RTC_cache_data[RTC_CACHE_DATA_SIZE]; #endif // ifdef ESP32 - /********************************************************************************************\ RTC located cache \*********************************************************************************************/ @@ -501,6 +500,7 @@ bool RTC_cache_handler_struct::saveRTCcache(unsigned int startOffset, size_t nrB { RTC_cache.checksumData = getDataChecksum(); RTC_cache.checksumMetadata = calc_CRC32(reinterpret_cast(&RTC_cache), sizeof(RTC_cache) - sizeof(uint32_t)); + #ifdef ESP32 return true; #endif // ifdef ESP32 diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 6be8e31fc4..ec61f88e52 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -428,8 +428,20 @@ class SettingsStruct_tmpl uint8_t getI2CInterfaceRTC() const; uint8_t getI2CInterfaceWDT() const; uint8_t getI2CInterfacePCFMCP() const; + #if FEATURE_EEPROM_EXTERNAL + uint8_t getI2CInterfaceEEPROM() const; + #endif // if FEATURE_EEPROM_EXTERNAL #endif // if FEATURE_I2C_MULTIPLE + #if FEATURE_EEPROM_EXTERNAL + uint8_t EEPROMExternalI2CAddress() const; + void EEPROMExternalI2CAddress(uint8_t address); + uint16_t EEPROMExternalI2CMultiplexerFlags() const; + void EEPROMExternalI2CMultiplexerFlags(uint16_t muxFlags); + uint8_t EEPROMExternalType() const; + void EEPROMExternalType(uint8_t type); + #endif // if FEATURE_EEPROM_EXTERNAL + #if FEATURE_I2CMULTIPLEXER int8_t getI2CMultiplexerType(uint8_t i2cBus) const; int8_t getI2CMultiplexerAddr(uint8_t i2cBus) const; @@ -531,7 +543,7 @@ class SettingsStruct_tmpl uint8_t WebLogLevel = 0; uint8_t SDLogLevel = 0; uint32_t BaudRate = 115200; - uint32_t MessageDelay_unused = 0; // MQTT settings now moved to the controller settings. + uint32_t EEPROMExternalFlags = 0; uint8_t deepSleep_wakeTime = 0; // 0 = Sleep Disabled, else time awake from sleep in seconds boolean CustomCSS = false; boolean DST = false; @@ -607,7 +619,8 @@ class SettingsStruct_tmpl int8_t SPI1_SCLK_pin = -1; int8_t SPI1_MISO_pin = -1; int8_t SPI1_MOSI_pin = -1; - unsigned int OLD_TaskDeviceID[N_TASKS - 8] = {0}; // UNUSED: this can be reused + uint32_t EEPROMSaveOptions = 0; + uint32_t OLD_TaskDeviceID[N_TASKS - 9] = {0}; // UNUSED: this can be reused // FIXME TD-er: When used on ESP8266, this conversion union may not work // It might work as it is 32-bit in size. @@ -755,8 +768,8 @@ class SettingsStruct_tmpl uint32_t ShowUnitOfMeasureOnDevicesPage : 1; // Bit 07 // inverted uint32_t WiFi_band_mode : 2; // Bit 08 & 09 uint32_t WiFi_AP_enable_NAPT : 1; // Bit 10 // inverted - uint32_t RestoreUserVarsFromEEPROMOnColdBoot : 1; // Bit 11 - uint32_t RestoreUserVarsFromEEPROMOnWarmBoot : 1; // Bit 12 + uint32_t unused_11 : 1; // Bit 11 + uint32_t unused_12 : 1; // Bit 12 uint32_t MQTTConnectInBackground : 1; // Bit 13 // inverted uint32_t StartAPfallback_NoCredentials : 1; // Bit 14 // inverted diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index a071f76d4f..b119bb8602 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -734,7 +734,7 @@ void SettingsStruct_tmpl::clearMisc() { # endif // ifdef ESP32 } BaudRate = DEFAULT_SERIAL_BAUD; - MessageDelay_unused = 0; + EEPROMExternalFlags = 0; deepSleep_wakeTime = 0; CustomCSS = false; WDI2CAddress = 0; @@ -1350,8 +1350,42 @@ template uint8_t SettingsStruct_tmpl::getI2CInterfacePCFMCP() const { return get3BitFromUL(I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP); } + +#if FEATURE_EEPROM_EXTERNAL +template +uint8_t SettingsStruct_tmpl::getI2CInterfaceEEPROM() const { + return get3BitFromUL(I2C_peripheral_bus, I2C_PERIPHERAL_BUS_EEPROM); +} +#endif // if FEATURE_EEPROM_EXTERNAL #endif // if FEATURE_I2C_MULTIPLE +#if FEATURE_EEPROM_EXTERNAL +template +uint8_t SettingsStruct_tmpl::EEPROMExternalI2CAddress() const { + return get8BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_ADDRESS); +} +template +void SettingsStruct_tmpl::EEPROMExternalI2CAddress(uint8_t address) { + set8BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_ADDRESS, address); +} +template +uint16_t SettingsStruct_tmpl::EEPROMExternalI2CMultiplexerFlags() const { + return get16BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_MUX); +} +template +void SettingsStruct_tmpl::EEPROMExternalI2CMultiplexerFlags(uint16_t muxFlags) { + set16BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_MUX, muxFlags); +} +template +uint8_t SettingsStruct_tmpl::EEPROMExternalType() const { + return static_cast(get4BitFromUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE)); +} +template +void SettingsStruct_tmpl::EEPROMExternalType(uint8_t type) { + set4BitToUL(EEPROMExternalFlags, EEPROM_EXTERNAL_FLAGS_SIZE, type); +} +#endif // if FEATURE_EEPROM_EXTERNAL + #if FEATURE_I2CMULTIPLEXER template int8_t SettingsStruct_tmpl::getI2CMultiplexerType(uint8_t i2cBus) const { diff --git a/src/src/ESPEasyCore/ESPEasy_setup.cpp b/src/src/ESPEasyCore/ESPEasy_setup.cpp index 5fdbec68f7..6a4d9288dc 100644 --- a/src/src/ESPEasyCore/ESPEasy_setup.cpp +++ b/src/src/ESPEasyCore/ESPEasy_setup.cpp @@ -35,7 +35,6 @@ #include "../Helpers/StringGenerator_System.h" #include "../WebServer/ESPEasy_WebServer.h" - #ifdef USE_RTOS_MULTITASKING # include "../Helpers/Networking.h" # include "../Helpers/PeriodicalActions.h" diff --git a/src/src/Helpers/CRC_functions.cpp b/src/src/Helpers/CRC_functions.cpp index ba76121624..d18e93fa21 100644 --- a/src/src/Helpers/CRC_functions.cpp +++ b/src/src/Helpers/CRC_functions.cpp @@ -57,6 +57,32 @@ uint32_t calc_CRC32(const uint8_t *data, size_t length) { return crc; } +uint32_t calc_CRC32(tDataReader dataReader, size_t length) { + uint32_t crc = 0xffffffff; + size_t index{}; + + if (dataReader) { + while (length--) { + uint8_t c = dataReader(index); + ++index; + + for (uint32_t i = 0x80; i > 0; i >>= 1) { + bool bit = crc & 0x80000000; + + if (c & i) { + bit = !bit; + } + crc <<= 1; + + if (bit) { + crc ^= 0x04c11db7; + } + } + } + } + return crc; +} + uint8_t calc_CRC8(const uint8_t *data, size_t length) { /* diff --git a/src/src/Helpers/CRC_functions.h b/src/src/Helpers/CRC_functions.h index dfbf589d8a..d418112b02 100644 --- a/src/src/Helpers/CRC_functions.h +++ b/src/src/Helpers/CRC_functions.h @@ -3,6 +3,10 @@ #include "../../ESPEasy_common.h" +#include + +typedef std::function tDataReader; + int calc_CRC16(const String& text); int IRAM_ATTR calc_CRC16(const char *ptr, @@ -11,6 +15,9 @@ int IRAM_ATTR calc_CRC16(const char *ptr, uint32_t calc_CRC32(const uint8_t *data, size_t length); +uint32_t calc_CRC32(tDataReader dataReader, + size_t length); + uint8_t calc_CRC8(const uint8_t *data, size_t length); diff --git a/src/src/Helpers/ESPEasyRTC.cpp b/src/src/Helpers/ESPEasyRTC.cpp index 324e2af43a..b91cb45dc2 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -81,6 +81,11 @@ // - RTCStruct to keep information on reboot reason, last used WiFi, etc. // - UserVar to keep task values persistent just like on ESP8266 +/** + * With EEPROMExternal (AT24cxxx) enabled and configured: + * - UserVar will be stored in external EEPROM + * - + */ @@ -88,6 +93,7 @@ //#define RTC_STRUCT_DEBUG +constexpr uint32_t sizeof_uint32_t = sizeof(uint32_t); #ifdef ESP32 constexpr size_t UserVar_nrelements = VARS_PER_TASK * TASKS_MAX; @@ -134,7 +140,7 @@ void initRTC() saveToRTC(); UserVar.clear(); - saveUserVarToRTC(); + saveUserVarToRTC(true); } /********************************************************************************************\ @@ -158,7 +164,11 @@ bool readFromRTC() /********************************************************************************************\ Save values to RTC memory \*********************************************************************************************/ -bool saveUserVarToRTC() +bool saveUserVarToRTC() { + return saveUserVarToRTC(false); +} + +bool saveUserVarToRTC(bool initial) { // ESP8266 has the RTC struct stored in memory which we must actively fetch // ESP32 Uses a temp structure which is mapped to the RTC address range. @@ -168,7 +178,6 @@ bool saveUserVarToRTC() if (taskValues != nullptr) { for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) { const size_t index = (task * VARS_PER_TASK) + varNr; - constexpr bool raw = true; UserVar_RTC[index] = taskValues->getUint32(varNr); } } diff --git a/src/src/Helpers/ESPEasyRTC.h b/src/src/Helpers/ESPEasyRTC.h index d98182f83d..58bdf5269c 100644 --- a/src/src/Helpers/ESPEasyRTC.h +++ b/src/src/Helpers/ESPEasyRTC.h @@ -17,6 +17,7 @@ bool readFromRTC(); Save values to RTC memory \*********************************************************************************************/ bool saveUserVarToRTC(); +bool saveUserVarToRTC(bool initial); /********************************************************************************************\ Read RTC struct from RTC memory diff --git a/src/src/Helpers/ESPEasy_FactoryDefault.cpp b/src/src/Helpers/ESPEasy_FactoryDefault.cpp index aac872006c..e0e6cfa772 100644 --- a/src/src/Helpers/ESPEasy_FactoryDefault.cpp +++ b/src/src/Helpers/ESPEasy_FactoryDefault.cpp @@ -296,7 +296,7 @@ void ResetFactory(bool formatFS) // Settings.UseRules = DEFAULT_USE_RULES; Settings.ControllerEnabled[0] = DEFAULT_CONTROLLER_ENABLED; Settings.MQTTRetainFlag_unused = DEFAULT_MQTT_RETAIN; - Settings.MessageDelay_unused = DEFAULT_MQTT_DELAY; + // Settings.MessageDelay_unused = DEFAULT_MQTT_DELAY; Settings.MQTTUseUnitNameAsClientId_unused = DEFAULT_MQTT_USE_UNITNAME_AS_CLIENTID; // allow to set default latitude and longitude diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index 07a7e6fe5d..12c4659766 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -47,7 +47,6 @@ #include "../DataStructs/NotificationSettingsStruct.h" #endif // if FEATURE_NOTIFIER - // ******************************************************************************** // Check struct sizes at compile time // Usage: @@ -178,7 +177,7 @@ void run_compiletime_checks() { static_assert(198u == offsetof(SettingsStruct, TaskDeviceNumber), "NOTIFICATION_MAX has changed?"); // All settings related to N_TASKS - static_assert((232 + TASKS_MAX) == offsetof(SettingsStruct, OLD_TaskDeviceID), ""); // 32-bit alignment, so offset of 2 bytes. + static_assert((236 + TASKS_MAX) == offsetof(SettingsStruct, OLD_TaskDeviceID), ""); // 32-bit alignment, so offset of 2 bytes. static_assert((200 + (67 * TASKS_MAX)) == offsetof(SettingsStruct, ControllerEnabled), ""); // Used to compute true offset. @@ -190,7 +189,10 @@ void run_compiletime_checks() { // to determine nr of bits in a struct. static_assert(GPIO_DIRECTION_NR_BITS== NR_BITS(static_cast(gpio_direction::gpio_direction_MAX)), "Correct GPIO_DIRECTION_NR_BITS"); + #endif // ifndef LIMIT_BUILD_SIZE } + +#ifndef LIMIT_BUILD_SIZE String ReportOffsetErrorInStruct(const String& structname, size_t offset) { String error; if (error.reserve(48 + structname.length())) { diff --git a/src/src/Helpers/Hardware_I2C.cpp b/src/src/Helpers/Hardware_I2C.cpp index 6743da5180..2f53a18576 100644 --- a/src/src/Helpers/Hardware_I2C.cpp +++ b/src/src/Helpers/Hardware_I2C.cpp @@ -7,6 +7,9 @@ #include "../Helpers/I2C_access.h" #include "../Helpers/StringConverter.h" +#if FEATURE_EEPROM_EXTERNAL +# include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL #if FEATURE_I2C #include @@ -27,12 +30,12 @@ void initI2C() { { if (Settings.isI2CEnabled(i2cBus)) { #ifndef LIMIT_BUILD_SIZE - #if !FEATURE_I2C_MULTIPLE + # if !FEATURE_I2C_MULTIPLE addLog(LOG_LEVEL_INFO, F("INIT : I2C Bus")); - #else // if !FEATURE_I2C_MULTIPLE + # else // if !FEATURE_I2C_MULTIPLE addLog(LOG_LEVEL_INFO, concat(F("INIT : I2C Bus "), i2cBus)); - #endif // if !FEATURE_I2C_MULTIPLE - #endif + # endif // if !FEATURE_I2C_MULTIPLE + #endif // ifndef BUILD_MINIMAL_OTA I2CSelectHighClockSpeed(i2cBus); // Set normal clock speed, on I2C Bus 1 (index 0) } } @@ -75,6 +78,11 @@ void initI2C() { } } } + + #if FEATURE_EEPROM_EXTERNAL + ESPEasy::eeprom::initializeEEPROMExternal(); + #endif // if FEATURE_EEPROM_EXTERNAL + I2CSelectHighClockSpeed(0); // Select first interface by default } @@ -144,10 +152,11 @@ void I2CBegin(int8_t sda, int8_t scl, uint32_t clockFreq, uint32_t clockStretch) // No need to change the clock speed. return; } - if (sda == -1 || scl == -1) { + + if ((sda == -1) || (scl == -1)) { #ifdef ESP32 Wire.end(); -#endif +#endif // ifdef ESP32 last_sda = sda; last_scl = scl; return; @@ -241,6 +250,25 @@ uint8_t I2CMultiplexerShiftBit(uint8_t i2cBus, uint8_t i) { return toWrite; } +void I2CMultiplexerSelectByBusAndMux(uint8_t i2cBus, bool singleMulti, int muxPort) { + uint8_t toWrite{}; + + if ((singleMulti && (muxPort > 0)) || + (!singleMulti && (muxPort > -1))) { + if (!singleMulti) { + uint8_t i = muxPort; + + if (i < 8) { + toWrite = I2CMultiplexerShiftBit(i2cBus, i); + } + } else { + toWrite = muxPort; // Bitpattern is already correctly stored + } + } + + SetI2CMultiplexer(i2cBus, toWrite); +} + // As initially constructed by krikk in PR#254, quite adapted // utility method for the I2C multiplexer // select the multiplexer port given as parameter, if taskIndex < 0 then take that abs value as the port to select (to allow I2C scanner) diff --git a/src/src/Helpers/Hardware_I2C.h b/src/src/Helpers/Hardware_I2C.h index fc546350a5..a6e31a7036 100644 --- a/src/src/Helpers/Hardware_I2C.h +++ b/src/src/Helpers/Hardware_I2C.h @@ -24,6 +24,11 @@ void I2CBegin(int8_t sda, #if FEATURE_I2CMULTIPLEXER bool isI2CMultiplexerEnabled(uint8_t i2cBus); +uint8_t I2CMultiplexerShiftBit(uint8_t i2cBus, uint8_t i); + +void I2CMultiplexerSelectByBusAndMux(uint8_t i2cBus, + bool singleMulti, + int muxPort); void I2CMultiplexerSelectByTaskIndex(taskIndex_t taskIndex); void I2CMultiplexerSelect(uint8_t i2cBus, uint8_t i); diff --git a/src/src/Helpers/PeriodicalActions.cpp b/src/src/Helpers/PeriodicalActions.cpp index ec05a25ee8..99ab148abb 100644 --- a/src/src/Helpers/PeriodicalActions.cpp +++ b/src/src/Helpers/PeriodicalActions.cpp @@ -49,7 +49,6 @@ #include "../Helpers/MDNS_Helper.h" #endif - #define PLUGIN_ID_MQTT_IMPORT 37 diff --git a/src/src/Helpers/StringParser.cpp b/src/src/Helpers/StringParser.cpp index d0e487d4d3..36fc84d552 100644 --- a/src/src/Helpers/StringParser.cpp +++ b/src/src/Helpers/StringParser.cpp @@ -24,7 +24,9 @@ #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" - +#if FEATURE_EEPROM_EXTERNAL +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL /********************************************************************************************\ Parse string template @@ -167,10 +169,16 @@ String parseTemplate_padded(String& tmpString, uint8_t minimal_lineSize, bool us const bool devNameEqStr = equals(deviceName, F("str")); const bool devNameEqLength = equals(deviceName, F("length")); #endif // if FEATURE_STRING_VARIABLES + #if FEATURE_EEPROM_EXTERNAL + const bool devNameEqReadEE = equals(deviceName, F("readee")); + #endif // if FEATURE_EEPROM_EXTERNAL if (devNameEqInt || equals(deviceName, F("var")) #if FEATURE_STRING_VARIABLES || devNameEqStr || devNameEqLength #endif // if FEATURE_STRING_VARIABLES + #if FEATURE_EEPROM_EXTERNAL + || devNameEqReadEE + #endif // if FEATURE_EEPROM_EXTERNAL ) { // Address an internal variable either as float or as int @@ -198,6 +206,31 @@ String parseTemplate_padded(String& tmpString, uint8_t minimal_lineSize, bool us tmpString); } else #endif + #if FEATURE_EEPROM_EXTERNAL + if (devNameEqReadEE) { + uint32_t slot{}; + String value; + if (validUIntFromString(valueName, slot)) { + #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + value = doubleToString(ESPEasy::eeprom::readEEPROMSlot(slot)); + #else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + value = toString(ESPEasy::eeprom::readEEPROMSlot(slot)); + #endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + } else if (valueName.equalsIgnoreCase(F("max"))) { + value = ESPEasy::eeprom::getEEPROMMaxSlots(); + } else if (valueName.equalsIgnoreCase(F("wp"))) { + value = ESPEasy::eeprom::isEEPROMExternalWriteProtected() ? 1 : 0; + } + if (!value.isEmpty()) { + transformValue( + newString, + minimal_lineSize, + std::move(value), + format, + tmpString); + } + } else + #endif // if FEATURE_EEPROM_EXTERNAL { const ESPEASY_RULES_FLOAT_TYPE floatvalue = getCustomFloatVar(valueName); unsigned char nr_decimals = maxNrDecimals_fpType(floatvalue); diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 2db6c29b2c..467e3595d6 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -314,22 +314,11 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task # endif // if FEATURE_I2C_MULTIPLE # if FEATURE_I2CMULTIPLEXER - - if (isI2CMultiplexerEnabled(i2cBus)) { - int multipleMuxPortsOption = getFormItemInt(F("taskdeviceflags1"), 0); - bitWrite(flags, I2C_FLAGS_MUX_MULTICHANNEL, multipleMuxPortsOption == 1); - - if (multipleMuxPortsOption == 1) { - uint8_t selectedPorts = 0; - - for (int x = 0; x < I2CMultiplexerMaxChannels(i2cBus); ++x) { - bitWrite(selectedPorts, x, isFormItemChecked(concat(F("taskdeviceflag1ch"), x))); - } - Settings.I2C_Multiplexer_Channel[taskIndex] = selectedPorts; - } else { - Settings.I2C_Multiplexer_Channel[taskIndex] = getFormItemInt(F("taskdevicei2cmuxport"), 0); - } - } + bool muxPortsOption{}; + int selectedPorts{}; + GetI2CMultiplexerFromPage(i2cBus, muxPortsOption, selectedPorts); + bitWrite(flags, I2C_FLAGS_MUX_MULTICHANNEL, muxPortsOption); + Settings.I2C_Multiplexer_Channel[taskIndex] = selectedPorts; # endif // if FEATURE_I2CMULTIPLEXER @@ -464,6 +453,7 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task # if FEATURE_PLUGIN_FILTER ExtraTaskSettings.enablePluginFilter(varNr, isFormItemChecked(getPluginCustomArgName(F("TDFIL"), varNr))); # endif // if FEATURE_PLUGIN_FILTER + # if FEATURE_PLUGIN_STATS PluginStats_Config_t pluginStats_Config; pluginStats_Config.setEnabled(isFormItemChecked(getPluginCustomArgName(F("TDS"), varNr))); @@ -1496,10 +1486,16 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex } # endif // if FEATURE_I2C_MULTIPLE # if FEATURE_I2CMULTIPLEXER + ShowI2CMultiplexerUI(i2cBus, + bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL), + Settings.I2C_Multiplexer_Channel[taskIndex]); + # endif // if FEATURE_I2CMULTIPLEXER +} +# if FEATURE_I2CMULTIPLEXER +void ShowI2CMultiplexerUI(uint8_t i2cBus, bool muxPortsOption, int taskDeviceI2CMuxPort) { // Show selector for an I2C multiplexer port if a multiplexer is configured if (isI2CMultiplexerEnabled(i2cBus)) { - bool multipleMuxPorts = bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL); { const __FlashStringHelper *i2c_mux_channels[] = { F("Single channel"), @@ -1507,8 +1503,8 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex constexpr int i2c_mux_channelOptions[] = { 0, 1 }; int i2c_mux_channelCount = 1; - if (Settings.I2C_Multiplexer_Type == I2C_MULTIPLEXER_PCA9540) { - multipleMuxPorts = false; // force off + if (Settings.getI2CMultiplexerType(i2cBus) == I2C_MULTIPLEXER_PCA9540) { + muxPortsOption = false; // force off } else { i2c_mux_channelCount++; } @@ -1520,10 +1516,10 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex selector.addFormSelector( F("Multiplexer channels"), F("taskdeviceflags1"), - multipleMuxPorts ? 1 : 0); + muxPortsOption ? 1 : 0); } - if (multipleMuxPorts) { + if (muxPortsOption) { addRowLabel(F("Select connections"), EMPTY_STRING); html_table(EMPTY_STRING, false); // Sub-table html_table_header(F("Channel"), 100); @@ -1536,11 +1532,10 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex html_TD(); addHtml(concat(F("Channel "), x)); html_TD(); - addCheckBox(concat(F("taskdeviceflag1ch"), x), bitRead(Settings.I2C_Multiplexer_Channel[taskIndex], x), false); + addCheckBox(concat(F("taskdeviceflag1ch"), x), bitRead(taskDeviceI2CMuxPort, x), false); } html_end_table(); } else { - int taskDeviceI2CMuxPort = Settings.I2C_Multiplexer_Channel[taskIndex]; const uint32_t mux_max = I2CMultiplexerMaxChannels(i2cBus); String i2c_mux_portoptions[mux_max + 1]; int i2c_mux_portchoices[mux_max + 1]; @@ -1564,10 +1559,26 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex taskDeviceI2CMuxPort); } } - # endif // if FEATURE_I2CMULTIPLEXER } #endif +void GetI2CMultiplexerFromPage(uint8_t i2cBus, bool &muxPortsOption, int &selectedPorts) { + if (isI2CMultiplexerEnabled(i2cBus)) { + muxPortsOption = getFormItemInt(F("taskdeviceflags1"), 0) == 1; + + if (muxPortsOption) { + selectedPorts = 0; + + for (int x = 0; x < I2CMultiplexerMaxChannels(i2cBus); ++x) { + bitWrite(selectedPorts, x, isFormItemChecked(concat(F("taskdeviceflag1ch"), x))); + } + } else { + selectedPorts = getFormItemInt(F("taskdevicei2cmuxport"), -1); + } + } +} +# endif // if FEATURE_I2CMULTIPLEXER + void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { struct EventStruct TempEvent(taskIndex); diff --git a/src/src/WebServer/DevicesPage.h b/src/src/WebServer/DevicesPage.h index 9ccfe1cf15..cffc3f12da 100644 --- a/src/src/WebServer/DevicesPage.h +++ b/src/src/WebServer/DevicesPage.h @@ -69,6 +69,15 @@ void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); #endif +# if FEATURE_I2CMULTIPLEXER +void ShowI2CMultiplexerUI(uint8_t i2cBus, + bool muxPortsOption, + int taskDeviceI2CMuxPort); +void GetI2CMultiplexerFromPage(uint8_t i2cBus, + bool &muxPortsOption, + int &selectedPorts); +#endif // if FEATURE_I2CMULTIPLEXER + void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); #if FEATURE_PLUGIN_STATS diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index 35224d8041..38bfa5ec7f 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -12,6 +12,7 @@ //#include "../WebServer/CustomPage.h" #include "../WebServer/DevicesPage.h" #include "../WebServer/DownloadPage.h" +#include "../WebServer/EepromVarPage.h" #include "../WebServer/FactoryResetPage.h" #include "../WebServer/FileList.h" #include "../WebServer/HTML_wrappers.h" @@ -340,6 +341,9 @@ void WebServerInit() #ifdef WEBSERVER_SYSVARS web_server.on(F("/sysvars"), handle_sysvars); #endif // WEBSERVER_SYSVARS +#if FEATURE_EEPROM_EXTERNAL + web_server.on(F("/eepromvars"), handle_eepromvars); +#endif // if FEATURE_EEPROM_EXTERNAL #if FEATURE_PLUGIN_LIST web_server.on(F("/pluginlist"), handle_pluginlist); #endif // if FEATURE_PLUGIN_LIST diff --git a/src/src/WebServer/EepromVarPage.h b/src/src/WebServer/EepromVarPage.h new file mode 100644 index 0000000000..ed4edb5620 --- /dev/null +++ b/src/src/WebServer/EepromVarPage.h @@ -0,0 +1,8 @@ +#pragma once + +#if FEATURE_EEPROM_EXTERNAL +# include "../WebServer/common.h" + +void handle_eepromvars(); + +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/WebServer/EpromVarPage.cpp b/src/src/WebServer/EpromVarPage.cpp new file mode 100644 index 0000000000..cd3d3ead3c --- /dev/null +++ b/src/src/WebServer/EpromVarPage.cpp @@ -0,0 +1,97 @@ +#include "../WebServer/EepromVarPage.h" + + +#include "../WebServer/ESPEasy_WebServer.h" +#include "../WebServer/AccessControl.h" +#include "../WebServer/Markup.h" +#include "../WebServer/Markup_Forms.h" +#include "../WebServer/HTML_wrappers.h" + +#include "../Globals/Settings.h" +#include "../Globals/ExtraTaskSettings.h" + +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" +#include "../Helpers/ESPEasy_Storage.h" + +#include "../Helpers/StringConverter.h" + +#if FEATURE_EEPROM_EXTERNAL + +void handle_eepromvars() { + if (!isLoggedIn()) { return; } + + TXBuffer.startStream(); + sendHeadandTail_stdtemplate(_HEAD); + + if (ESPEasy::eeprom::checkEEPROMEnabled() > 0) { + // the table header + html_table_class_normal(); + html_TR(); + html_table_header(F("External EEPROM"), + 300); + html_table_header(ESPEasy::eeprom::getEEPROMName(static_cast(Settings.EEPROMExternalType())), + 500); + html_table_header(ESPEasy::eeprom::isEEPROMExternalWriteProtected() ? F("Write-protected!") : F(""), + 400); + html_table_header(F("")); + + html_TR(); + + // sub-table header + html_table_header(F("Slot"), 300); + html_table_header(F("Value (only non-zero values)"), 500); + html_table_header(F("")); + html_table_header(F("")); + + const uint32_t maxSlots = ESPEasy::eeprom::getEEPROMMaxSlots(); + uint32_t count{}; + + for (uint32_t slot = 0; slot < maxSlots; ++slot) { + const float value = ESPEasy::eeprom::readEEPROMSlot(slot); + + if (slot % 50 == 0) { delay(0); } + + if (!isnan(value) && !essentiallyZero(value)) { + ++count; + html_TR_TD(); + addHtmlInt(slot); + html_TD(); + # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + addHtml(doubleToString(value)); + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + addHtml(toString(value)); + # endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + html_TD(2); + } + } + + addTableSeparator(F("Summary"), 3, 2); + + html_TR_TD(); + addHtml(F("Slots occupied: ")); + + if (0 == count) { + addHtml(F("none")); + } else { + addHtmlInt(count); + } + addHtml(F("")); + addHtml(F("Slots available: ")); + addHtmlInt(maxSlots - count); + + if (count > 0) { + addHtml(F(" of ")); + addHtmlInt(maxSlots); + } + html_TD(); + + html_end_table(); + } else { + addHtml(F("External EEPROM not enabled.")); + } + html_end_form(); + sendHeadandTail_stdtemplate(_TAIL); + TXBuffer.endStream(); +} + +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index cc7ce1f0a9..b2faf6bb6c 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -22,6 +22,11 @@ #include "../Helpers/StringGenerator_GPIO.h" +#if FEATURE_EEPROM_EXTERNAL +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" +#include "../WebServer/DevicesPage.h" // For using ShowI2CMultiplexerUI() and GetI2CMultiplexerFromPage() +#endif // if FEATURE_EEPROM_EXTERNAL + // ******************************************************************************** // Web Interface hardware page // ******************************************************************************** @@ -35,6 +40,8 @@ void handle_hardware() { TXBuffer.startStream(); sendHeadandTail_stdtemplate(_HEAD); + html_add_form(); + if (isFormItem(F("pled"))) { String error; Settings.Pin_status_led = getFormItemInt(F("pled")); @@ -44,6 +51,34 @@ void handle_hardware() { if (isFormItem(F("pi2cbuspcf"))) { set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf"))); } + // EEPROM settings + # if FEATURE_EEPROM_EXTERNAL + const uint8_t i2cBus = getFormItemInt(F("pi2cbuseeprom"), 0); + set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_EEPROM, i2cBus); + # endif // if FEATURE_EEPROM_EXTERNAL + + #if FEATURE_I2CMULTIPLEXER && !FEATURE_I2C_MULTIPLE && FEATURE_EEPROM_EXTERNAL + constexpr uint8_t i2cBus = 0; + #endif // if FEATURE_I2CMULTIPLEXER && !FEATURE_I2C_MULTIPLE && FEATURE_EEPROM_EXTERNAL + + #if FEATURE_EEPROM_EXTERNAL + Settings.EEPROMExternalType(getFormItemInt(F("eepromtype"), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C256))); + Settings.EEPROMExternalI2CAddress(getFormItemInt(F("i2c_eeprom"), 0)); + + # if FEATURE_I2CMULTIPLEXER + + bool muxPortsOption{}; + int selectedPorts{}; + GetI2CMultiplexerFromPage(i2cBus, muxPortsOption, selectedPorts); + uint16_t muxFlags{}; + bitWrite(muxFlags, EEPROM_MUX_FLAGS_MULTI, muxPortsOption); + set8BitToUL(muxFlags, EEPROM_MUX_FLAGS_PORT, selectedPorts); + Settings.EEPROMExternalI2CMultiplexerFlags(muxFlags); + # endif // if FEATURE_I2CMULTIPLEXER + + #endif // if FEATURE_EEPROM_EXTERNAL + #endif // if FEATURE_I2C_MULTIPLE #if defined(ESP32) && FEATURE_SD Settings.setSPIBusForSDCard(getFormItemInt(F("sdspibus"), 0)); @@ -98,6 +133,101 @@ void handle_hardware() { } # endif // if FEATURE_I2C_MULTIPLE + #if FEATURE_EEPROM_EXTERNAL + { + addFormSubHeader(F("External I2C EEPROM")); + const __FlashStringHelper*eepromOptions[] = { + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C256), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C512), + #if EEPROM_SUPPORT_AT24C1024 + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C1024), + #endif // if EEPROM_SUPPORT_AT24C1024 + #if EEPROM_SUPPORT_AT24C2048 + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C2048), + #endif // if EEPROM_SUPPORT_AT24C2048 + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C32), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C64), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C128), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC256), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC512), + #if EEPROM_SUPPORT_AT24C1024 + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC1M), + #endif // if EEPROM_SUPPORT_AT24C1024 + #if EEPROM_SUPPORT_AT24C2048 + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC2M), + #endif // if EEPROM_SUPPORT_AT24C2048 + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC32), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC64), + getEEPROMName(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC128), + }; + const int eepromTypes[] = { + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C256), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C512), + #if EEPROM_SUPPORT_AT24C1024 + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C1024), + #endif // if EEPROM_SUPPORT_AT24C1024 + #if EEPROM_SUPPORT_AT24C2048 + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C2048), + #endif // if EEPROM_SUPPORT_AT24C2048 + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C32), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C64), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::AT24C128), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC256), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC512), + #if EEPROM_SUPPORT_AT24C1024 + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC1M), + #endif // if EEPROM_SUPPORT_AT24C1024 + #if EEPROM_SUPPORT_AT24C2048 + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC2M), + #endif // if EEPROM_SUPPORT_AT24C2048 + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC32), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC64), + static_cast(ESPEasy::eeprom::EEPROMExternal_Type_e::MB85RC128), + }; + constexpr uint8_t eepromSizeCount = NR_ELEMENTS(eepromTypes); + FormSelectorOptions eepromSizeSelector(eepromSizeCount, eepromOptions, eepromTypes); + eepromSizeSelector.addFormSelector(F("EEPROM Model/size"), F("eepromtype"), Settings.EEPROMExternalType()); + + const uint8_t i2cAddressValues[] = { 0, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57 }; + constexpr int nrAddressOptions = NR_ELEMENTS(i2cAddressValues); + + addFormSelectorI2C(F("i2c_eeprom"), nrAddressOptions, i2cAddressValues, Settings.EEPROMExternalI2CAddress()); + + #if FEATURE_I2C_MULTIPLE + const uint8_t i2cBus = Settings.getI2CInterfaceEEPROM(); + if (i2cMaxBusCount > 1) { + I2CInterfaceSelector(F("I2C Bus"), + F("pi2cbuseeprom"), + i2cBus, + false); + + } + #endif // if FEATURE_I2C_MULTIPLE + + #if FEATURE_I2CMULTIPLEXER && !FEATURE_I2C_MULTIPLE && FEATURE_EEPROM_EXTERNAL + constexpr uint8_t i2cBus = 0; + #endif // if FEATURE_I2CMULTIPLEXER && !FEATURE_I2C_MULTIPLE && FEATURE_EEPROM_EXTERNAL + + #if FEATURE_I2CMULTIPLEXER + const uint16_t eepromMux = Settings.EEPROMExternalI2CMultiplexerFlags(); + ShowI2CMultiplexerUI(i2cBus, + bitRead(eepromMux, EEPROM_MUX_FLAGS_MULTI), + get8BitFromUL(eepromMux, EEPROM_MUX_FLAGS_PORT)); // Re-used from DevicesPage + #endif // if FEATURE_I2CMULTIPLEXER + + const bool eepromChecked = ESPEasy::eeprom::checkEEPROMEnabled() > 0; + addRowLabel(F("EEPROM Enabled")); + addEnabled(eepromChecked); + if (eepromChecked && ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { + addHtml(F(" Write-protected!")); + } + if (eepromChecked && !ESPEasy::eeprom::isEEPROMExternalWriteProtected()) { + addRowLabel(F("'WriteEE' slots available")); + addHtmlInt(ESPEasy::eeprom::getEEPROMMaxSlots()); + } + } + #endif // if FEATURE_EEPROM_EXTERNAL + #if FEATURE_SD addFormSubHeader(F("SD Card")); #ifdef ESP32 diff --git a/src/src/WebServer/I2C_Scanner.cpp b/src/src/WebServer/I2C_Scanner.cpp index f210220ad7..fb968f9d23 100644 --- a/src/src/WebServer/I2C_Scanner.cpp +++ b/src/src/WebServer/I2C_Scanner.cpp @@ -313,19 +313,23 @@ String getKnownI2Cdevice(uint8_t address) { case 0x4D: result += F("PCF8591,MCP3221,LM75A,INA219"); break; + case 0x50: + case 0x52: + result += F("AT24Cxx,MB85RCxx"); + break; case 0x51: - result += F("PCF8563"); + result += F("PCF8563,AT24Cxx,MB85RCxx"); break; case 0x53: - result += F("ADXL345,LTR390"); + result += F("ADXL345,LTR390,AT24Cxx,MB85RCxx"); break; case 0x55: - result += F("DFRobot Rotary enc,BeFlE Moisture"); + result += F("DFRobot Rotary enc,BeFlE Moisture,AT24Cxx,MB85RCxx"); break; case 0x54: case 0x56: case 0x57: - result += F("DFRobot Rotary enc"); + result += F("DFRobot Rotary enc,AT24Cxx,MB85RCxx"); break; case 0x58: result += F("SGP30,GP8403"); @@ -398,6 +402,9 @@ String getKnownI2Cdevice(uint8_t address) { case 0x78: result += F("LiquidLevel"); break; + case 0x7C: + result += F("MB85RCxx"); + break; case 0x7f: result += F("Arduino PME,XDB401"); break; diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 524085b3dc..37e0ac7091 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -528,6 +528,9 @@ void addFormSelectorI2C(const String& id, { String option = formatToHex_decimal(addresses[x]); + if (0 == addresses[x]) { + option = F("Disabled"); + } else if (((x == 0) && (defaultAddress == 0)) || (defaultAddress == addresses[x])) { option += F(" (default)"); } diff --git a/src/src/WebServer/ToolsPage.cpp b/src/src/WebServer/ToolsPage.cpp index c4fa74bcc6..26992cbbdb 100644 --- a/src/src/WebServer/ToolsPage.cpp +++ b/src/src/WebServer/ToolsPage.cpp @@ -97,6 +97,10 @@ void handle_tools() { addWideButtonPlusDescription(F("sysvars"), F("System Variables"), F("Show all system variables and conversions")); # endif // ifdef WEBSERVER_SYSVARS + #if FEATURE_EEPROM_EXTERNAL + addWideButtonPlusDescription(F("eepromvars"), F("External EEPROM values"), F("Show all values stored in the external EEPROM")); + #endif // if FEATURE_EEPROM_EXTERNAL + #if FEATURE_PLUGIN_LIST addWideButtonPlusDescription(F("pluginlist"), F("Included Plugins"), F("Show all plugins that are included in this build")); #endif // if FEATURE_PLUGIN_LIST