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/lib/AT24Cx/library.properties b/lib/AT24Cx/library.properties new file mode 100644 index 0000000000..24d9622016 --- /dev/null +++ b/lib/AT24Cx/library.properties @@ -0,0 +1,11 @@ +name=AT24CX +version=0.0.1 +author=Christian Paul +maintainer=Christian Paul +sentence=Arduino library for AT24Cx EEPROM storage devices. +paragraph= +category=Sensors +url=https://github.com/cyberp/AT24Cx +architectures=* +includes=AT24CX.h +depends= diff --git a/lib/Adafruit_RTClib/src/RTClib.cpp b/lib/Adafruit_RTClib/src/RTClib.cpp index 22a83ced95..1c51a59718 100644 --- a/lib/Adafruit_RTClib/src/RTClib.cpp +++ b/lib/Adafruit_RTClib/src/RTClib.cpp @@ -1546,6 +1546,187 @@ void RTC_PCF8563::writeSqwPinMode(Pcf8563SqwPinMode mode) { } // END RTC_PCF8563 implementation +// START RTC_PCF8583 implementation + +// Code inspired by https://github.com/xoseperez/pcf8583 + +/**************************************************************************/ +/*! + @brief Start I2C for the PCF8583 and test succesful connection + @details Use altAddress() before calling begin() to use the alternate address (0x51) + @return True if Wire can find PCF8583 or false otherwise. +*/ +/**************************************************************************/ + +boolean RTC_PCF8583::begin(TwoWire *wireInstance) { + RTCWireBus = wireInstance; + RTCWireBus->beginTransmission(_addr); + if (RTCWireBus->endTransmission() == 0) { + return true; + } + return false; +} + +void RTC_PCF8583::altAddress() { + _addr = PCF8583_ADDRESS_1; +} + +/**************************************************************************/ +/*! + @brief Check the status of the VL bit in the VL_SECONDS register. + @details The PCF8583 does not support voltage-low detector. + @return False +*/ +/**************************************************************************/ + +boolean RTC_PCF8583::lostPower(void) { + return false; // Not supported +} + +/**************************************************************************/ +/*! + @brief Set the date and time + @param dt DateTime to set +*/ +/**************************************************************************/ +void RTC_PCF8583::adjust(const DateTime &dt) { + + uint16_t year = dt.year() - PCF8583_BASE_YEAR; + const uint8_t offset = year & 0xFC; + year -= offset; + RTCWireBus->beginTransmission(_addr); + RTCWireBus->_I2C_WRITE(PCF8583_VL_SECONDS); // start at location 2, VL_SECONDS + RTCWireBus->_I2C_WRITE(bin2bcd(dt.second())); + RTCWireBus->_I2C_WRITE(bin2bcd(dt.minute())); + RTCWireBus->_I2C_WRITE(bin2bcd(dt.hour())); + RTCWireBus->_I2C_WRITE(bin2bcd(dt.day()) | (year < 6)); + RTCWireBus->_I2C_WRITE(bin2bcd(0)); // skip weekdays + RTCWireBus->_I2C_WRITE(bin2bcd(dt.month())); + RTCWireBus->_I2C_WRITE(bin2bcd(dt.year() - 2000)); + RTCWireBus->endTransmission(); + + RTCWireBus->beginTransmission(_addr); + RTCWireBus->_I2C_WRITE(PCF8583_OFFSET_YEAR); // Update offset and year + RTCWireBus->_I2C_WRITE(offset); + RTCWireBus->_I2C_WRITE(year); + RTCWireBus->endTransmission(); +} + +/**************************************************************************/ +/*! + @brief Get the current date/time + @return DateTime object containing the current date/time +*/ +/**************************************************************************/ + +DateTime RTC_PCF8583::now() { + RTCWireBus->beginTransmission(_addr); + RTCWireBus->_I2C_WRITE((byte)PCF8583_VL_SECONDS); + RTCWireBus->endTransmission(); + + RTCWireBus->requestFrom(_addr, (size_t)6); + const uint8_t ss = bcd2bin(RTCWireBus->_I2C_READ() & 0x7F); + const uint8_t mm = bcd2bin(RTCWireBus->_I2C_READ() & 0x7F); + const uint8_t hh = bcd2bin(RTCWireBus->_I2C_READ() & 0x3F); + const uint8_t d_r = RTCWireBus->_I2C_READ(); + const uint8_t d = bcd2bin(d_r & 0x3F); + const uint16_t year = d_r >> 6; + RTCWireBus->_I2C_READ(); // skip 'weekdays' + const uint8_t m = bcd2bin(RTCWireBus->_I2C_READ() & 0x1F); + + const uint8_t last = read_i2c_register(_addr, PCF8583_LAST_YEAR, RTCWireBus); + uint8_t offset = read_i2c_register(_addr, PCF8583_OFFSET_YEAR, RTCWireBus); + + // we have changed the year: happy new year!!! + if (last != year) { + + // we have overflowed the year value: update the offset + if (last > year) { + offset += 4; + write_i2c_register(_addr, PCF8583_OFFSET_YEAR, offset, RTCWireBus); + } + + write_i2c_register(_addr, PCF8583_LAST_YEAR, year, RTCWireBus); + } + const uint16_t y = PCF8583_BASE_YEAR + offset + year; + + return DateTime(y, m, d, hh, mm, ss); +} + +/**************************************************************************/ +/*! + @brief Resets the STOP bit in register Control_1 +*/ +/**************************************************************************/ +void RTC_PCF8583::start(void) { + const uint8_t ctlreg = read_i2c_register(_addr, PCF8583_CONTROL_1, RTCWireBus); + write_i2c_register(_addr, PCF8583_CONTROL_1, ctlreg & 0x7F, + RTCWireBus); +} + +/**************************************************************************/ +/*! + @brief Sets the STOP bit in register Control_1 +*/ +/**************************************************************************/ +void RTC_PCF8583::stop(void) { + const uint8_t ctlreg = read_i2c_register(_addr, PCF8583_CONTROL_1, RTCWireBus); + write_i2c_register(PCF8523_ADDRESS, PCF8583_CONTROL_1, ctlreg | 0x80, + RTCWireBus); +} + +/**************************************************************************/ +/*! + @brief Is the PCF8583 running? Check the STOP bit in register Control_1 + @return 1 if the RTC is running, 0 if not +*/ +/**************************************************************************/ +uint8_t RTC_PCF8583::isrunning() { + const uint8_t ctlreg = read_i2c_register(_addr, PCF8583_CONTROL_1, RTCWireBus); + return !(ctlreg >> 7); // bit 7 = 0 => running +} + +/**************************************************************************/ +/*! + @brief Read data from the PCF8583's NVRAM + @param buf Pointer to a buffer to store the data - make sure it's large + enough to hold size bytes + @param size Number of bytes to read + @param address Starting NVRAM address, from 0 to 235 +*/ +/**************************************************************************/ +void RTC_PCF8583::readnvram(uint8_t *buf, uint8_t size, uint8_t address) { + const int addrByte = PCF8583_NVRAM + address; + RTCWireBus->beginTransmission(_addr); + RTCWireBus->_I2C_WRITE(addrByte); + RTCWireBus->endTransmission(); + + RTCWireBus->requestFrom(_addr, size); + for (uint8_t pos = 0; pos < size; ++pos) { + buf[pos] = RTCWireBus->_I2C_READ(); + } +} + +/**************************************************************************/ +/*! + @brief Write data to the PCF8583 NVRAM + @param address Starting NVRAM address, from 0 to 248 + @param buf Pointer to buffer containing the data to write + @param size Number of bytes in buf to write to NVRAM +*/ +/**************************************************************************/ +void RTC_PCF8583::writenvram(uint8_t address, uint8_t *buf, uint8_t size) { + const int addrByte = PCF8583_NVRAM + address; + RTCWireBus->beginTransmission(_addr); + RTCWireBus->_I2C_WRITE(addrByte); + for (uint8_t pos = 0; pos < size; ++pos) { + RTCWireBus->_I2C_WRITE(buf[pos]); + } + RTCWireBus->endTransmission(); +} + +// END RTC_PCF8583 implementation + /**************************************************************************/ /*! @brief Convert the day of the week to a representation suitable for @@ -1864,3 +2045,43 @@ bool RTC_DS3231::isEnabled32K(void) { read_i2c_register(DS3231_ADDRESS, DS3231_STATUSREG, RTCWireBus); return (status >> 0x03) & 0x1; } + +/**************************************************************************/ +/*! + @brief Read data from the DS3232's NVRAM + @param buf Pointer to a buffer to store the data - make sure it's large + enough to hold size bytes + @param size Number of bytes to read + @param address Starting NVRAM address, from 0 to 235 +*/ +/**************************************************************************/ +void RTC_DS3231::readnvram(uint8_t *buf, uint8_t size, uint8_t address) { + int addrByte = DS3232_NVRAM + address; + RTCWireBus->beginTransmission(DS3231_ADDRESS); + RTCWireBus->_I2C_WRITE(addrByte); + RTCWireBus->endTransmission(); + + RTCWireBus->requestFrom((uint8_t)DS3231_ADDRESS, size); + for (uint8_t pos = 0; pos < size; ++pos) { + buf[pos] = RTCWireBus->_I2C_READ(); + } +} + +/**************************************************************************/ +/*! + @brief Write data to the DS3232 NVRAM + @param address Starting NVRAM address, from 0 to 235 + @param buf Pointer to buffer containing the data to write + @param size Number of bytes in buf to write to NVRAM +*/ +/**************************************************************************/ +void RTC_DS3231::writenvram(uint8_t address, uint8_t *buf, uint8_t size) { + int addrByte = DS3232_NVRAM + address; + RTCWireBus->beginTransmission(DS3231_ADDRESS); + RTCWireBus->_I2C_WRITE(addrByte); + for (uint8_t pos = 0; pos < size; ++pos) { + RTCWireBus->_I2C_WRITE(buf[pos]); + } + RTCWireBus->endTransmission(); +} + diff --git a/lib/Adafruit_RTClib/src/RTClib.h b/lib/Adafruit_RTClib/src/RTClib.h index cf696ad90e..fb7d781036 100644 --- a/lib/Adafruit_RTClib/src/RTClib.h +++ b/lib/Adafruit_RTClib/src/RTClib.h @@ -57,6 +57,17 @@ class TimeSpan; #define DS3231_TEMPERATUREREG \ 0x11 ///< Temperature register (high byte - low byte is at 0x12), 10-bit ///< temperature value +#define DS3232_NVRAM 0x14 ///< Start of RAM registers - 236 bytes, 0x14 to 0xFF + +#define PCF8583_ADDRESS 0x50 ///< I2C address for PCF8583 +#define PCF8583_ADDRESS_1 0x51 ///< Alternate I2C address for PCF8583 (A0 = high) +#define PCF8583_CONTROL_1 0x00 ///< Control and status register 1 +#define PCF8583_VL_SECONDS 0x02 ///< register address for VL_SECONDS +#define PCF8583_NVRAM 0x10 ///< Start of RAM registers - 240 bytes, 0x10 to 0xFF + +#define PCF8583_OFFSET_YEAR 0x08 +#define PCF8583_LAST_YEAR 0x09 +#define PCF8583_BASE_YEAR 2012 /** Constants */ #define SECONDS_PER_DAY 86400L ///< 60 * 60 * 24 @@ -347,6 +358,8 @@ class RTC_DS3231 { void disable32K(void); bool isEnabled32K(void); float getTemperature(); // in Celsius degree + void readnvram(uint8_t *buf, uint8_t size, uint8_t address); + void writenvram(uint8_t address, uint8_t *buf, uint8_t size); protected: TwoWire *RTCWireBus; @@ -454,6 +467,30 @@ class RTC_PCF8563 { TwoWire *RTCWireBus; }; +/**************************************************************************/ +/*! + @brief RTC based on the PCF8583 chip connected via I2C and the Wire library +*/ +/**************************************************************************/ + +class RTC_PCF8583 { +public: + boolean begin(TwoWire *wireInstance = &Wire); + void altAddress(); + boolean lostPower(void); + void adjust(const DateTime &dt); + DateTime now(); + void start(void); + void stop(void); + uint8_t isrunning(); + void readnvram(uint8_t *buf, uint8_t size, uint8_t address); + void writenvram(uint8_t address, uint8_t *buf, uint8_t size); + +protected: + TwoWire *RTCWireBus; + uint8_t _addr = PCF8583_ADDRESS; +}; + /**************************************************************************/ /*! @brief RTC using the internal millis() clock, has to be initialized before diff --git a/src/Custom-sample.h b/src/Custom-sample.h index 97e6daad1f..0247d3b210 100644 --- a/src/Custom-sample.h +++ b/src/Custom-sample.h @@ -30,6 +30,8 @@ #define FEATURE_JSON_EVENT 0 // Generates an event with the values of a JSON repsonse of an HTTP call. Keys are stored in json.keys one key per line (e.g.: Body.Data.DAY_ENERGY.Values.1) // #define FEATURE_SD 1 // Enable SD card support // #define FEATURE_DOWNLOAD 1 // Enable downloading a file from an url +// #define FEATURE_EEPROM_EXTERNAL 1 // Enable support for AT24Cxxx EEPROM chips AT24C32(4kB)..AT24C1024(128kB), optional: AT24C2048(256kB) and FRAM MB85RC32(4kB)..MB85RC1M(128kB), optional: MB85RC2M(256kB) +// #define FEATURE_RTC_SRAM_STORAGE 1 // Enable storing values in supported RTC Clock chip SRAM (DS1307, DS3232, PCF8583) #ifdef BUILD_GIT # undef BUILD_GIT @@ -227,6 +229,7 @@ #define FEATURE_SSDP 1 #define FEATURE_EXT_RTC 1 // Support for external RTC clock modules like PCF8563/PCF8523/DS3231/DS1307 +#define FEATURE_EXT_RTC_PCF8583 1 // As we enable External RTC clock modules, we can also include PCF8583 #define FEATURE_PLUGIN_STATS 1 // Support collecting historic data + computing stats on historic data #ifdef ESP8266 diff --git a/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp new file mode 100644 index 0000000000..3339a43cfb --- /dev/null +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.cpp @@ -0,0 +1,393 @@ +#include "../Helpers/EEPROMExternal.h" +#if FEATURE_EEPROM_EXTERNAL + +# include "../../../src/Globals/Settings.h" +# include "../../../src/Helpers/I2C_access.h" +# include "../../../ESPEasy_common.h" +# include "../../../src/Helpers/StringConverter.h" + + +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 (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) { // EEPROM Configured? + return eepromAddress; + } + return (uint8_t)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) { // 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 (uint8_t)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 0ul; +} + +/** + * EEPROM pagesize in bytes + */ +uint32_t getEEPROMSize(EEPROMExternal_Type_e type, + uint8_t & pageSize) { + pageSize = (uint8_t)0; + + switch (type) + { + case EEPROMExternal_Type_e::AT24C256: + case EEPROMExternal_Type_e::MB85RC256: + case EEPROMExternal_Type_e::AT24C128: + case EEPROMExternal_Type_e::MB85RC128: + pageSize = (uint8_t)64; + break; + # if EEPROM_SUPPORT_AT24C1024 + case EEPROMExternal_Type_e::AT24C1024: + case EEPROMExternal_Type_e::MB85RC1M: + # endif // if EEPROM_SUPPORT_AT24C1024 + # if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C2048: + case EEPROMExternal_Type_e::MB85RC2M: + # endif // if EEPROM_SUPPORT_AT24C2048 + case EEPROMExternal_Type_e::AT24C512: + case EEPROMExternal_Type_e::MB85RC512: + pageSize = (uint8_t)128; + break; + case EEPROMExternal_Type_e::AT24C32: + case EEPROMExternal_Type_e::MB85RC32: + case EEPROMExternal_Type_e::AT24C64: + case EEPROMExternal_Type_e::MB85RC64: + pageSize = (uint8_t)32; + break; + } + 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()) { + const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); + + if (eepromSize && (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()) { + const uint32_t eepromSize = getEEPROMSize(static_cast(Settings.EEPROMExternalType())); + + if (eepromSize) { + const uint32_t slotMax = (unsigned long)(((eepromSize - EEPROM_CUSTOM_START_OFFSET) / EEPROM_CUSTOM_DIVISOR) / sizeof_eeprom_slot); + + return slotMax; + } + } + return 0ul; +} + +/** + * EEPROM write value to slot if the slot is valid + */ +bool writeEEPROMSlot(uint32_t slot, + ESPEASY_RULES_FLOAT_TYPE data) +{ + const uint32_t addr = getEEPROMAddressForSlot(slot); + + if ((addr != std::numeric_limits::max()) && !isEEPROMExternalWriteProtected()) { + const ESPEASY_RULES_FLOAT_TYPE oldData = EEPROMExternal->readDouble(addr); + + 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 + */ +ESPEASY_RULES_FLOAT_TYPE readEEPROMSlot(uint32_t slot) { + const uint32_t addr = getEEPROMAddressForSlot(slot); + + if (addr != std::numeric_limits::max()) { + return EEPROMExternal->readDouble(addr); + } + # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + return 0.0; + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + 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..7c8dd371e2 --- /dev/null +++ b/src/ESPEasy/eeprom/Helpers/EEPROMExternal.h @@ -0,0 +1,96 @@ +#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(); + +bool writeEEPROMSlot(uint32_t slot, + ESPEASY_RULES_FLOAT_TYPE data); +ESPEASY_RULES_FLOAT_TYPE readEEPROMSlot(uint32_t slot); + +} // namespace eeprom +} // namespace ESPEasy +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/ESPEasy/eeprom/Helpers/RTCSRAMStorage.cpp b/src/ESPEasy/eeprom/Helpers/RTCSRAMStorage.cpp new file mode 100644 index 0000000000..7c6fd8a5d7 --- /dev/null +++ b/src/ESPEasy/eeprom/Helpers/RTCSRAMStorage.cpp @@ -0,0 +1,271 @@ +#include "../Helpers/RTCSRAMStorage.h" +#if FEATURE_RTC_SRAM_STORAGE +# include "../../../src/Globals/Settings.h" +# include "../../../src/Helpers/I2C_access.h" +# include "../../../ESPEasy_common.h" +# include "../../../src/Helpers/StringConverter.h" +# include "../../../src/DataTypes/TimeSource.h" +# include + + +namespace ESPEasy { +namespace eeprom { + +constexpr uint32_t sizeof_rtcsram_slot = sizeof(SRAM_STORAGE_FLOAT_TYPE); + +/** + * Check if the RTC is properly initialized and enabled. + * Returns the external rtc type if SRAM is available + */ +uint8_t checkRTCSRAMEnabled() { + const ExtTimeSource_e extRtcType = Settings.ExtTimeSource(); + + if ((ExtTimeSource_e::DS1307 == extRtcType) || + # if FEATURE_EXT_RTC_PCF8583 + (ExtTimeSource_e::PCF8583 == extRtcType) || + (ExtTimeSource_e::PCF8583a == extRtcType) || + # endif // if FEATURE_EXT_RTC_PCF8583 + (ExtTimeSource_e::DS3232 == extRtcType)) { // RTC with SRAM Configured? + return static_cast(Settings.ExtTimeSource()); + } + return (uint8_t)0; +} + +/** + * Get the RTC I2C Address + */ +uint8_t getRTCI2CAddress() { + const ExtTimeSource_e type = Settings.ExtTimeSource(); + + switch (type) + { + case ExtTimeSource_e::DS1307: + return DS1307_ADDRESS; + case ExtTimeSource_e::DS3232: + return DS3231_ADDRESS; + # if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::PCF8583: + case ExtTimeSource_e::PCF8583a: + return PCF8583_ADDRESS; + # endif // if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::DS3231: + case ExtTimeSource_e::PCF8523: + case ExtTimeSource_e::PCF8563: + case ExtTimeSource_e::None: + break; + } + return (uint8_t)0; + +} + +/** + * Switch to I2C Bus and multiplexer channel of RTC SRAM + */ +uint8_t selectRTCSRAMI2CBus() { + const ExtTimeSource_e type = Settings.ExtTimeSource(); + + if (checkRTCSRAMEnabled()) { // RTC Module Configured? + # if FEATURE_I2C_MULTIPLE + const uint8_t i2cBus = Settings.getI2CInterfaceRTC(); + # else // if FEATURE_I2C_MULTIPLE + constexpr uint8_t i2cBus = 0; + # endif // if FEATURE_I2C_MULTIPLE + + switch (type) + { + case ExtTimeSource_e::DS1307: + # if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::PCF8583: + case ExtTimeSource_e::PCF8583a: + # endif // if FEATURE_EXT_RTC_PCF8583 + I2CSelect_Max100kHz_ClockSpeed(i2cBus); + break; + case ExtTimeSource_e::DS3232: + I2CSelectHighClockSpeed(i2cBus); + break; + case ExtTimeSource_e::DS3231: + case ExtTimeSource_e::PCF8523: + case ExtTimeSource_e::PCF8563: + case ExtTimeSource_e::None: + break; + } + + if (0 == I2C_wakeup(getRTCI2CAddress())) { + return static_cast(Settings.ExtTimeSource()); + } + } + return (uint8_t)0; +} + +/** + * RTC SRAM size in bytes + */ +uint32_t getRTCSRAMSize() { + const ExtTimeSource_e type = Settings.ExtTimeSource(); + + switch (type) + { + case ExtTimeSource_e::DS1307: + return 56ul; + case ExtTimeSource_e::DS3232: + return 240ul; + # if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::PCF8583: + case ExtTimeSource_e::PCF8583a: + return 240ul; + # endif // if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::DS3231: + case ExtTimeSource_e::PCF8523: + case ExtTimeSource_e::PCF8563: + case ExtTimeSource_e::None: + break; + } + return 0ul; +} + +/** + * RTC SRAM _relative_ address for slot or 0xFFFF when error + */ +uint32_t getRTCSRAMAddressForSlot(uint32_t slot) { + if (checkRTCSRAMEnabled()) { + const uint32_t rtcSramSize = getRTCSRAMSize(); + + if (rtcSramSize && (slot < getRTCSRAMMaxSlots())) { + const uint32_t slotAddr = slot * sizeof_rtcsram_slot; + + if (slotAddr < rtcSramSize) { + return slotAddr; + } + } + } + return std::numeric_limits::max(); +} + +/** + * RTC SRAM available number of slots, max use all available space + */ +uint32_t getRTCSRAMMaxSlots() { + if (checkRTCSRAMEnabled()) { + const uint32_t rtcSramSize = getRTCSRAMSize(); + + if (rtcSramSize) { + const uint32_t slotMax = (uint32_t)(rtcSramSize / sizeof_rtcsram_slot); + + return slotMax; + } + } + return 0ul; +} + +/** + * RTC SRAM write value to slot if the slot is valid + */ +bool writeRTCSRAMSlot(uint32_t slot, + SRAM_STORAGE_FLOAT_TYPE data) +{ + const uint32_t addr = getRTCSRAMAddressForSlot(slot); + + if ((addr != std::numeric_limits::max()) && (selectRTCSRAMI2CBus() > 0)) { + const ExtTimeSource_e type = Settings.ExtTimeSource(); + uint8_t _b[sizeof_rtcsram_slot]{}; + + switch (type) + { + case ExtTimeSource_e::DS1307: + { + RTC_DS1307 rtc; + rtc.readnvram(_b, sizeof_rtcsram_slot, addr); + const SRAM_STORAGE_FLOAT_TYPE oldData = *(SRAM_STORAGE_FLOAT_TYPE *)&_b[0]; + + if (!essentiallyEqual(oldData, data)) { + rtc.writenvram(addr, (uint8_t *)&data, sizeof_rtcsram_slot); + } + return true; + } + case ExtTimeSource_e::DS3232: + { + RTC_DS3231 rtc; + rtc.readnvram(_b, sizeof_rtcsram_slot, addr); + const SRAM_STORAGE_FLOAT_TYPE oldData = *(SRAM_STORAGE_FLOAT_TYPE *)&_b[0]; + + if (!essentiallyEqual(oldData, data)) { + rtc.writenvram(addr, (uint8_t *)&data, sizeof_rtcsram_slot); + } + return true; + } + # if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::PCF8583: + case ExtTimeSource_e::PCF8583a: + { + RTC_PCF8583 rtc; + rtc.readnvram(_b, sizeof_rtcsram_slot, addr); + const SRAM_STORAGE_FLOAT_TYPE oldData = *(SRAM_STORAGE_FLOAT_TYPE *)&_b[0]; + + if (!essentiallyEqual(oldData, data)) { + rtc.writenvram(addr, (uint8_t *)&data, sizeof_rtcsram_slot); + } + return true; + } + # endif // if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::DS3231: + case ExtTimeSource_e::PCF8523: + case ExtTimeSource_e::PCF8563: + case ExtTimeSource_e::None: + break; + } + + } + return false; +} + +/** + * RTC SRAM read value from slot or 0 when invalid + */ +SRAM_STORAGE_FLOAT_TYPE readRTCSRAMSlot(uint32_t slot) { + const uint32_t addr = getRTCSRAMAddressForSlot(slot); + + if ((addr != std::numeric_limits::max()) && (selectRTCSRAMI2CBus() > 0)) { + const ExtTimeSource_e type = Settings.ExtTimeSource(); + uint8_t _b[sizeof_rtcsram_slot]{}; + + switch (type) + { + case ExtTimeSource_e::DS1307: + { + RTC_DS1307 rtc; + rtc.readnvram(_b, sizeof_rtcsram_slot, addr); + return *(SRAM_STORAGE_FLOAT_TYPE *)&_b[0]; + } + case ExtTimeSource_e::DS3232: + { + RTC_DS3231 rtc; + rtc.readnvram(_b, sizeof_rtcsram_slot, addr); + return *(SRAM_STORAGE_FLOAT_TYPE *)&_b[0]; + } + # if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::PCF8583: + case ExtTimeSource_e::PCF8583a: + { + RTC_PCF8583 rtc; + rtc.readnvram(_b, sizeof_rtcsram_slot, addr); + return *(SRAM_STORAGE_FLOAT_TYPE *)&_b[0]; + } + # endif // if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::DS3231: + case ExtTimeSource_e::PCF8523: + case ExtTimeSource_e::PCF8563: + case ExtTimeSource_e::None: + break; + } + + } + # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + return 0.0; + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + return 0.0f; + # endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +} + +} // namespace eeprom +} // namespace ESPEasy +#endif // if FEATURE_RTC_SRAM_STORAGE diff --git a/src/ESPEasy/eeprom/Helpers/RTCSRAMStorage.h b/src/ESPEasy/eeprom/Helpers/RTCSRAMStorage.h new file mode 100644 index 0000000000..58ced1e4e8 --- /dev/null +++ b/src/ESPEasy/eeprom/Helpers/RTCSRAMStorage.h @@ -0,0 +1,30 @@ +#pragma once +#include "../../../ESPEasy_common.h" + +#if FEATURE_RTC_SRAM_STORAGE + +# include "../../../src/DataTypes/TaskIndex.h" + +namespace ESPEasy { +namespace eeprom { + +# if FEATURE_SRAM_STORAGE_DOUBLE + # define SRAM_STORAGE_FLOAT_TYPE double +# else + # define SRAM_STORAGE_FLOAT_TYPE float +# endif // if FEATURE_SRAM_STORAGE_DOUBLE + +uint8_t checkRTCSRAMEnabled(); +uint8_t selectRTCSRAMI2CBus(); + +uint32_t getRTCSRAMSize(); +uint32_t getRTCSRAMAddressForSlot(uint32_t slot); +uint32_t getRTCSRAMMaxSlots(); + +bool writeRTCSRAMSlot(uint32_t slot, + SRAM_STORAGE_FLOAT_TYPE data); +SRAM_STORAGE_FLOAT_TYPE readRTCSRAMSlot(uint32_t slot); + +} // namespace eeprom +} // namespace ESPEasy +#endif // if FEATURE_RTC_SRAM_STORAGE diff --git a/src/_C005.cpp b/src/_C005.cpp index 4b8485a790..9dd17d1839 100644 --- a/src/_C005.cpp +++ b/src/_C005.cpp @@ -93,9 +93,14 @@ bool CPlugin_005(CPlugin::Function function, struct EventStruct *event, String& mqttDiscoveryTimeout = random(10, MQTT_DISCOVERY_MAX_DELAY_0_1_SECONDS); if (loglevelActiveFor(LOG_LEVEL_INFO)) { + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, strformat(F("C005 : INIT: AutoDiscovery for Controller %d in %.1f sec."), event->ControllerIndex + 1, mqttDiscoveryTimeout / 10.0f)); + # else // ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, strformat(F("C005 : Discovery in %.1f sec."), + mqttDiscoveryTimeout / 10.0f)); + # endif // ifndef BUILD_NO_DEBUG } } } @@ -112,7 +117,11 @@ bool CPlugin_005(CPlugin::Function function, struct EventStruct *event, String& if (0 == mqttDiscoveryTimeout) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("C005 : AutoDiscovery delay expired, starting now...")); + # else // ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, F("C005 : Discovery starting...")); + # endif // ifndef BUILD_NO_DEBUG } success = MQTT_SendAutoDiscovery(event->ControllerIndex, CPLUGIN_ID_005); } @@ -175,8 +184,13 @@ bool CPlugin_005(CPlugin::Function function, struct EventStruct *event, String& mqttDiscoveryTimeout = random(10, MQTT_DISCOVERY_MAX_DELAY_0_1_SECONDS); if (loglevelActiveFor(LOG_LEVEL_INFO)) { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, strformat(F("C005 : Request for AutoDiscovery received. %.1f sec."), mqttDiscoveryTimeout / 10.0f)); + #else // ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, strformat(F("C005 : Request Discovery. %.1f sec."), + mqttDiscoveryTimeout / 10.0f)); + #endif // ifndef BUILD_NO_DEBUG } // FIXME Generate event when request received? diff --git a/src/_C014.cpp b/src/_C014.cpp index 852af381fd..d9bc10e85e 100644 --- a/src/_C014.cpp +++ b/src/_C014.cpp @@ -287,10 +287,10 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String& // $localip Device → Controller IP of the device on the local network Yes Yes # ifdef CPLUGIN_014_V3 - CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$localip"), formatIP(NetworkLocalIP()), errorCounter); + CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$localip"), formatIP(NetworkLocalIP()), errorCounter); // $mac Device → Controller Mac address of the device network interface. The format MUST be of the type A1:B2:C3:D4:E5:F6 Yes Yes - CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$mac"), ESPEasy::net::NetworkMacAddress(), errorCounter); + CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$mac"), ESPEasy::net::NetworkMacAddress(), errorCounter); // $implementation Device → Controller An identifier for the Homie implementation (example esp8266) Yes Yes CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$implementation"), @@ -436,7 +436,8 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String& // $datatype The data type. See Payloads. Enum: [integer, float, boolean,string, enum, color] unitName.clear(); - switch (Settings.TaskDevicePluginConfig[x][varNr]) { + switch (Settings.TaskDevicePluginConfig[x][varNr]) + { case 0: valueName = F("integer"); @@ -457,8 +458,10 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String& ExtraTaskSettings.TaskDevicePluginConfig[varNr + valueCount]); } break; - case 2: valueName = F("boolean"); break; - case 3: valueName = F("string"); break; + case 2: valueName = F("boolean"); + break; + case 3: valueName = F("string"); + break; case 4: valueName = F("enum"); unitName = ExtraTaskSettings.TaskDeviceFormula[varNr]; @@ -687,7 +690,8 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String& CPlugin_014_sendMQTTdevice(pubname, event->TaskIndex, F("$state"), F("ready"), errorCounter); success = true; } -#ifndef BUILD_NO_DEBUG +# ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("C014 : autodiscover information of %d Devices and %d Nodes sent with %s errors! (%d messages)"), @@ -697,7 +701,7 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String& msgCounter) ); } -#endif +# endif // ifndef BUILD_NO_DEBUG msgCounter = 0; errorCounter = 0; break; @@ -721,7 +725,7 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String& if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = strformat(F("C014 : Device: %s got invalid (disconnect%s"), - Settings.getHostname().c_str(), String(success ? F("ed).") : F(") failed!")).c_str()); + Settings.getHostname().c_str(), FsP(success ? F("ed).") : F(") failed!"))); addLogMove(LOG_LEVEL_INFO, log); } break; @@ -919,7 +923,8 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String& addLogMove(LOG_LEVEL_ERROR, log); } } else if (equals(commandName, F(CPLUGIN_014_HOMIEVALUESET_COMMAND))) { // acknowledges value form P086 Homie Receiver - switch (Settings.TaskDevicePluginConfig[deviceIndex - 1][taskVarIndex]) { + switch (Settings.TaskDevicePluginConfig[deviceIndex - 1][taskVarIndex]) + { case 0: // PLUGIN_085_VALUE_INTEGER valueInt = static_cast(UserVar[userVarIndex]); valueStr = toString(UserVar[userVarIndex], 0); diff --git a/src/_P004_Dallas.ino b/src/_P004_Dallas.ino index a2c144e256..d5113b34cb 100644 --- a/src/_P004_Dallas.ino +++ b/src/_P004_Dallas.ino @@ -360,6 +360,7 @@ boolean Plugin_004(uint8_t function, struct EventStruct *event, String& string) } } + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("DS : Temperature: "); @@ -371,6 +372,7 @@ boolean Plugin_004(uint8_t function, struct EventStruct *event, String& string) log += strformat(F(" (%s)"), P004_data->get_formatted_address(i).c_str()); addLogMove(LOG_LEVEL_INFO, log); } + #endif // ifndef BUILD_NO_DEBUG } P004_data->set_measurement_inactive(); } diff --git a/src/_P011_PME.ino b/src/_P011_PME.ino index b13ffd5c6f..fb8c2580df 100644 --- a/src/_P011_PME.ino +++ b/src/_P011_PME.ino @@ -120,9 +120,11 @@ boolean Plugin_011(uint8_t function, struct EventStruct *event, String& string) if (P011_TYPE_SWITCH != P011_PORT_TYPE) { // Not for Switch type UserVar.setFloat(event->TaskIndex, 0, Plugin_011_Read(P011_PORT_TYPE, CONFIG_PORT)); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("PME : PortValue: "), formatUserVarNoCheck(event, 0))); } + #endif // ifndef BUILD_NO_DEBUG success = true; } break; @@ -138,9 +140,11 @@ boolean Plugin_011(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 0, newValue); sendData(event); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, strformat(F("PME : Switch state: %d"), newValue)); } + #endif // ifndef BUILD_NO_DEBUG success = true; } } diff --git a/src/_P017_PN532.ino b/src/_P017_PN532.ino index 9da5e31b0e..f8782799dc 100644 --- a/src/_P017_PN532.ino +++ b/src/_P017_PN532.ino @@ -245,7 +245,9 @@ bool P017_handle_timer_in(struct EventStruct *event) // Reset card id on timeout if (P017_AUTO_TAG_REMOVAL == 0) { UserVar.setSensorTypeLong(event->TaskIndex, P017_NO_TAG_DETECTED_VALUE); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("RFID : Removed Tag")); + #endif // ifndef BUILD_NO_DEBUG if (P017_EVENT_ON_TAG_REMOVAL == 1) { sendData(event); @@ -396,10 +398,12 @@ boolean Plugin_017_Init(int8_t resetPin) if (validGpio(resetPin)) { + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("PN532: Reset on pin: "), resetPin)); } + #endif // ifndef BUILD_NO_DEBUG pinMode(resetPin, OUTPUT); digitalWrite(resetPin, LOW); delay(100); diff --git a/src/_P018_Dust.ino b/src/_P018_Dust.ino index d1e3afb01b..4d8a262a22 100644 --- a/src/_P018_Dust.ino +++ b/src/_P018_Dust.ino @@ -100,9 +100,11 @@ boolean Plugin_018(uint8_t function, struct EventStruct *event, String& string) ISR_interrupts(); UserVar.setFloat(event->TaskIndex, 0, value); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("GPY : Dust value: "), value)); } + #endif // ifndef BUILD_NO_DEBUG success = true; break; diff --git a/src/_P034_DHT12.ino b/src/_P034_DHT12.ino index 3c12f5138a..0a52be13b5 100644 --- a/src/_P034_DHT12.ino +++ b/src/_P034_DHT12.ino @@ -100,6 +100,7 @@ boolean Plugin_034(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 0, temperature); UserVar.setFloat(event->TaskIndex, 1, humidity); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("DHT12: Temperature: "), @@ -118,13 +119,16 @@ boolean Plugin_034(uint8_t function, struct EventStruct *event, String& string) addLog(LOG_LEVEL_INFO, log); */ } + #endif // ifndef BUILD_NO_DEBUG success = true; } // checksum } // error if (!success) { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("DHT12: No reading!")); + #endif // ifndef BUILD_NO_DEBUG UserVar.setFloat(event->TaskIndex, 0, NAN); UserVar.setFloat(event->TaskIndex, 1, NAN); } diff --git a/src/_P040_ID12.ino b/src/_P040_ID12.ino index 6d1af2bfd3..a9faaae08a 100644 --- a/src/_P040_ID12.ino +++ b/src/_P040_ID12.ino @@ -77,7 +77,9 @@ boolean Plugin_040(uint8_t function, struct EventStruct *event, String& string) if (Plugin_040_init) { // Reset card id on timeout UserVar.setSensorTypeLong(event->TaskIndex, 0); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("RFID : Removed Tag")); + #endif // ifndef BUILD_NO_DEBUG sendData(event); success = true; } diff --git a/src/_P051_AM2320.ino b/src/_P051_AM2320.ino index 7718a8943f..67d9cc49bd 100644 --- a/src/_P051_AM2320.ino +++ b/src/_P051_AM2320.ino @@ -100,10 +100,12 @@ boolean Plugin_051(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 0, th.t); UserVar.setFloat(event->TaskIndex, 1, th.h); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("AM2320: Temperature: "), formatUserVarNoCheck(event, 0))); addLogMove(LOG_LEVEL_INFO, concat(F("AM2320: Humidity: "), formatUserVarNoCheck(event, 1))); } + #endif // ifndef BUILD_NO_DEBUG success = true; break; } diff --git a/src/_P060_MCP3221.ino b/src/_P060_MCP3221.ino index d670edc89e..7b9e8d0492 100644 --- a/src/_P060_MCP3221.ino +++ b/src/_P060_MCP3221.ino @@ -157,7 +157,9 @@ boolean Plugin_060(uint8_t function, struct EventStruct *event, String& string) if (nullptr != P060_data) { UserVar.setFloat(event->TaskIndex, 0, P060_data->getValue()); + #ifndef BUILD_NO_DEBUG String log = concat(F("ADMCP: Analog value: "), formatUserVarNoCheck(event, 0)); + #endif // ifndef BUILD_NO_DEBUG if (PCONFIG(3)) // Calibration? { @@ -171,11 +173,15 @@ boolean Plugin_060(uint8_t function, struct EventStruct *event, String& string) const float normalized = (UserVar[event->BaseVarIndex] - adc1) / static_cast(adc2 - adc1); UserVar.setFloat(event->TaskIndex, 0, normalized * (out2 - out1) + out1); + #ifndef BUILD_NO_DEBUG log += concat(F(" = "), formatUserVarNoCheck(event, 0)); + #endif // ifndef BUILD_NO_DEBUG } } + #ifndef BUILD_NO_DEBUG addLogMove(LOG_LEVEL_INFO, log); + #endif // ifndef BUILD_NO_DEBUG success = true; } break; diff --git a/src/_P065_DRF0299_MP3.ino b/src/_P065_DRF0299_MP3.ino index ec0a70f255..ff38ba0557 100644 --- a/src/_P065_DRF0299_MP3.ino +++ b/src/_P065_DRF0299_MP3.ino @@ -221,6 +221,7 @@ boolean Plugin_065(uint8_t function, struct EventStruct *event, String& string) success = true; } + #ifndef BUILD_NO_DEBUG if (success && loglevelActiveFor(LOG_LEVEL_INFO)) { String log; log.reserve(20); @@ -232,6 +233,7 @@ boolean Plugin_065(uint8_t function, struct EventStruct *event, String& string) } addLogMove(LOG_LEVEL_INFO, log); } + #endif // ifndef BUILD_NO_DEBUG break; } } diff --git a/src/_P068_SHT3x.ino b/src/_P068_SHT3x.ino index e0912aec76..562b83e850 100644 --- a/src/_P068_SHT3x.ino +++ b/src/_P068_SHT3x.ino @@ -132,10 +132,12 @@ boolean Plugin_068(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 0, sht3x->tmp); UserVar.setFloat(event->TaskIndex, 1, sht3x->hum); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("SHT3x: Temperature: "), formatUserVarNoCheck(event, 0))); addLogMove(LOG_LEVEL_INFO, concat(F("SHT3x: Humidity: "), formatUserVarNoCheck(event, 1))); } + #endif // ifndef BUILD_NO_DEBUG success = true; break; } diff --git a/src/_P071_Kamstrup401.ino b/src/_P071_Kamstrup401.ino index c9c493649d..17f66ee065 100644 --- a/src/_P071_Kamstrup401.ino +++ b/src/_P071_Kamstrup401.ino @@ -263,10 +263,12 @@ boolean Plugin_071(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 0, m_energy); // gives energy in Wh UserVar.setFloat(event->TaskIndex, 1, m_volume); // gives volume in liters + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, strformat(F("Kamstrup : Heat value: %.3f kWh"), m_energy / 1000)); addLogMove(LOG_LEVEL_INFO, strformat(F("Kamstrup : Volume value: %d Liter"), m_volume)); } + #endif // ifndef BUILD_NO_DEBUG } else { diff --git a/src/_P072_HDC1080.ino b/src/_P072_HDC1080.ino index cbc9eab859..978ebebefc 100644 --- a/src/_P072_HDC1080.ino +++ b/src/_P072_HDC1080.ino @@ -108,10 +108,12 @@ boolean Plugin_072(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 0, hdc1080_temp); UserVar.setFloat(event->TaskIndex, 1, hdc1080_hum); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("HDC10xx: Temperature: "), formatUserVarNoCheck(event, 0))); addLogMove(LOG_LEVEL_INFO, concat(F("HDC10xx: Humidity: "), formatUserVarNoCheck(event, 1))); } + #endif // ifndef BUILD_NO_DEBUG success = true; break; } diff --git a/src/_P084_VEML6070.ino b/src/_P084_VEML6070.ino index 4c9d90c2a6..7f266256df 100644 --- a/src/_P084_VEML6070.ino +++ b/src/_P084_VEML6070.ino @@ -148,9 +148,11 @@ boolean Plugin_084(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 1, uv_risk); UserVar.setFloat(event->TaskIndex, 2, uv_power); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("VEML6070: UV: "), formatUserVarNoCheck(event, 0))); } + #endif // ifndef BUILD_NO_DEBUG success = true; } diff --git a/src/_P088_HeatpumpIR.ino b/src/_P088_HeatpumpIR.ino index cb669b3d00..6ba4324b54 100644 --- a/src/_P088_HeatpumpIR.ino +++ b/src/_P088_HeatpumpIR.ino @@ -216,7 +216,9 @@ boolean Plugin_088(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_EXIT: { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("P088: Heatpump IR transmitter deactivated")); + #endif // ifndef BUILD_NO_DEBUG if (Plugin_088_irSender != nullptr) { @@ -243,7 +245,9 @@ boolean Plugin_088(uint8_t function, struct EventStruct *event, String& string) enableIR_RX(true); delete panasonicHeatpumpIR; + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("P088: The TIMER led on Panasonic CKP should now be OFF")); + #endif // ifndef BUILD_NO_DEBUG } } } diff --git a/src/_P090_CCS811.ino b/src/_P090_CCS811.ino index ba8d8d39e0..b298fa33e2 100644 --- a/src/_P090_CCS811.ino +++ b/src/_P090_CCS811.ino @@ -281,10 +281,12 @@ boolean Plugin_090(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 1, P090_data->myCCS811.getCO2()); P090_data->newReadingAvailable = true; + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, strformat(F("CCS811 : tVOC: %d, eCO2: %d"), P090_data->myCCS811.getTVOC(), P090_data->myCCS811.getCO2())); } + #endif // ifndef BUILD_NO_DEBUG } } } diff --git a/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino b/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino index 8ab8238b8b..809cf56d2b 100644 --- a/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino +++ b/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino @@ -605,7 +605,9 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) if (P103_send_I2C_command(P103_I2C_ADDRESS, readCommand, boarddata)) { const String sensorString(boarddata); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, concat(F("P103: READ result: "), sensorString)); + #endif // ifndef BUILD_NO_DEBUG float sensor_f{}; diff --git a/src/_P105_AHT.ino b/src/_P105_AHT.ino index 2e38d1860e..a349b8afec 100644 --- a/src/_P105_AHT.ino +++ b/src/_P105_AHT.ino @@ -224,6 +224,7 @@ boolean Plugin_105(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 0, P105_data->getTemperature() + (P105_TEMPERATURE_OFFSET / 10.0f)); UserVar.setFloat(event->TaskIndex, 1, min(P105_data->getHumidity() * (1 - 0.005f * P105_TEMPERATURE_OFFSET), 100.0f)); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, strformat(F("%s : Addr: 0x%02x"), P105_data->getDeviceName().c_str(), P105_I2C_ADRESS)); addLogMove(LOG_LEVEL_INFO, @@ -232,6 +233,7 @@ boolean Plugin_105(uint8_t function, struct EventStruct *event, String& string) formatUserVarNoCheck(event, 0).c_str(), formatUserVarNoCheck(event, 1).c_str())); } + #endif // ifndef BUILD_NO_DEBUG success = true; } break; diff --git a/src/_P107_SI1145.ino b/src/_P107_SI1145.ino index 6cb8cb4e76..e7dd834fc5 100644 --- a/src/_P107_SI1145.ino +++ b/src/_P107_SI1145.ino @@ -108,11 +108,13 @@ boolean Plugin_107(uint8_t function, struct EventStruct *event, String& string) P107_data->uv.reset(); // Stop the sensor reading + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("SI1145: Visible: "), formatUserVarNoCheck(event, 0))); addLogMove(LOG_LEVEL_INFO, concat(F("SI1145: Infrared: "), formatUserVarNoCheck(event, 1))); addLogMove(LOG_LEVEL_INFO, concat(F("SI1145: UV index: "), formatUserVarNoCheck(event, 2))); } + #endif // ifndef BUILD_NO_DEBUG success = true; break; } diff --git a/src/_P112_AS7265x.ino b/src/_P112_AS7265x.ino index 9d3ccea0ee..064535a8a3 100644 --- a/src/_P112_AS7265x.ino +++ b/src/_P112_AS7265x.ino @@ -304,7 +304,9 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) P112_data->initialized = false; // Force re-init just in case the address changed. if (P112_data->begin()) { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("AS7265X: Found sensor")); + #endif // ifndef BUILD_NO_DEBUG success = P112_data->initialized; P112_data->sensor.setGain(PCONFIG_LONG(0)); @@ -337,7 +339,7 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) success = true; } else { - addLog(LOG_LEVEL_INFO, F("AS7265X: No sensor found")); + addLog(LOG_LEVEL_ERROR, F("AS7265X: No sensor found")); success = false; } } diff --git a/src/_P114_VEML6075.ino b/src/_P114_VEML6075.ino index 8a6445a5b8..f8a8b83b80 100644 --- a/src/_P114_VEML6075.ino +++ b/src/_P114_VEML6075.ino @@ -161,6 +161,7 @@ boolean Plugin_114(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 1, UVB); UserVar.setFloat(event->TaskIndex, 2, UVIndex); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, strformat(F("VEML6075: Address: 0x%02x / Integration Time: %d / " "Dynamic Mode: %d / divisor: %d / UVA: %.2f / UVB: %.2f / UVIndex: %.2f"), @@ -169,6 +170,7 @@ boolean Plugin_114(uint8_t function, struct EventStruct *event, String& string) UserVar[event->BaseVarIndex + 1], UserVar[event->BaseVarIndex + 2])); } + #endif // ifndef BUILD_NO_DEBUG success = true; } diff --git a/src/_P115_MAX1704x_v2.ino b/src/_P115_MAX1704x_v2.ino index ddbedabdfc..699fea1665 100644 --- a/src/_P115_MAX1704x_v2.ino +++ b/src/_P115_MAX1704x_v2.ino @@ -161,10 +161,12 @@ boolean Plugin_115(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 2, P115_data->alert); UserVar.setFloat(event->TaskIndex, 3, P115_data->changeRate); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, strformat(F("MAX1704x : Voltage: %.2f SoC: %.2f Alert: %d Rate: %.2f"), P115_data->voltage, P115_data->soc, P115_data->alert, P115_data->changeRate)); } + #endif // ifndef BUILD_NO_DEBUG success = true; } break; diff --git a/src/_P117_SCD30.ino b/src/_P117_SCD30.ino index 80294e1972..94f5885cf6 100644 --- a/src/_P117_SCD30.ino +++ b/src/_P117_SCD30.ino @@ -181,7 +181,7 @@ boolean Plugin_117(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 3, scd30_CO2); if (scd30_CO2EAvg > 5000) { - addLog(LOG_LEVEL_INFO, F("SCD30: Sensor saturated! > 5000 ppm")); + addLog(LOG_LEVEL_ERROR, F("SCD30: Sensor saturated! > 5000 ppm")); } break; case ERROR_SCD30_NO_DATA: diff --git a/src/_P122_SHT2x.ino b/src/_P122_SHT2x.ino index e842e20fe4..f29c27a2b1 100644 --- a/src/_P122_SHT2x.ino +++ b/src/_P122_SHT2x.ino @@ -221,6 +221,7 @@ boolean Plugin_122(uint8_t function, struct EventStruct *event, String& string) } } + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, @@ -229,6 +230,7 @@ boolean Plugin_122(uint8_t function, struct EventStruct *event, String& string) formatUserVarNoCheck(event, 1).c_str() )); } + #endif // ifndef BUILD_NO_DEBUG success = true; break; } diff --git a/src/_P127_CDM7160.ino b/src/_P127_CDM7160.ino index 323a1466cf..6c19279e95 100644 --- a/src/_P127_CDM7160.ino +++ b/src/_P127_CDM7160.ino @@ -160,6 +160,7 @@ boolean Plugin_127(uint8_t function, struct EventStruct *event, String& string) addLog(LOG_LEVEL_ERROR, F("CDM7160: Sensor saturated! > 10000 ppm")); } + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, strformat(F("CDM7160: Address: 0x%02x: CO2 ppm: %d, alt: %d, comp: %d"), P127_CONFIG_I2C_ADDRESS, @@ -167,6 +168,7 @@ boolean Plugin_127(uint8_t function, struct EventStruct *event, String& string) P127_data->getAltitude(), P127_data->getCompensation())); } + #endif // ifndef BUILD_NO_DEBUG break; } case PLUGIN_WRITE: diff --git a/src/src/Commands/EEPROMExternal.cpp b/src/src/Commands/EEPROMExternal.cpp new file mode 100644 index 0000000000..8a7af26976 --- /dev/null +++ b/src/src/Commands/EEPROMExternal.cpp @@ -0,0 +1,57 @@ +#include "../Commands/EEPROMExternal.h" +#if FEATURE_EEPROM_EXTERNAL +# 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" + +// Command: WriteEE,, : set a slot value. 0 is 'erased' +// Command: WriteEE,erase,erase : reset all slots to 0 +// Command: WriteEE,check,wp : check if external EEPROM is write-protected +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 f0426f1fa2..a23a711184 100644 --- a/src/src/Commands/InternalCommands.cpp +++ b/src/src/Commands/InternalCommands.cpp @@ -11,6 +11,12 @@ #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 +#if FEATURE_RTC_SRAM_STORAGE +#include "../Commands/RTCSRAMStorage.h" +#endif // if FEATURE_RTC_SRAM_STORAGE #include "../Commands/GPIO.h" #include "../Commands/HTTP.h" #include "../Commands/InternalCommands_decoder.h" @@ -511,7 +517,14 @@ 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 +#if FEATURE_RTC_SRAM_STORAGE + case ESPEasy_cmd_e::writertc: COMMAND_CASE_R(Command_writeRTC, 2); // RTCSRAMStorage.h +#endif // if FEATURE_RTC_SRAM_STORAGE + 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..59a8d9ee9c 100644 --- a/src/src/Commands/InternalCommands_decoder.cpp +++ b/src/src/Commands/InternalCommands_decoder.cpp @@ -317,6 +317,12 @@ const char Internal_commands_w[] PROGMEM = "wdconfig|" "wdread|" #endif // ifndef LIMIT_BUILD_SIZE +#if FEATURE_EEPROM_EXTERNAL + "writeee|" +#endif // if FEATURE_EEPROM_EXTERNAL +#if FEATURE_RTC_SRAM_STORAGE + "writertc|" +#endif // if FEATURE_RTC_SRAM_STORAGE ; #endif diff --git a/src/src/Commands/InternalCommands_decoder.h b/src/src/Commands/InternalCommands_decoder.h index 4815d2c59e..0130dcb2f8 100644 --- a/src/src/Commands/InternalCommands_decoder.h +++ b/src/src/Commands/InternalCommands_decoder.h @@ -258,6 +258,12 @@ enum class ESPEasy_cmd_e : uint8_t { wdconfig, wdread, #endif // ifndef LIMIT_BUILD_SIZE +#if FEATURE_EEPROM_EXTERNAL + writeee, +#endif // if FEATURE_EEPROM_EXTERNAL +#if FEATURE_RTC_SRAM_STORAGE + writertc, +#endif // if FEATURE_RTC_SRAM_STORAGE NotMatched // Keep as last one diff --git a/src/src/Commands/RTCSRAMStorage.cpp b/src/src/Commands/RTCSRAMStorage.cpp new file mode 100644 index 0000000000..3924aa80a7 --- /dev/null +++ b/src/src/Commands/RTCSRAMStorage.cpp @@ -0,0 +1,47 @@ +#include "../Commands/RTCSRAMStorage.h" +#if FEATURE_RTC_SRAM_STORAGE +# include "../../ESPEasy/eeprom/Helpers/RTCSRAMStorage.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" + +// Command: WriteRTC,, : set a slot value. 0 is 'erased' +// Command: WriteRTC,erase,erase : reset all slots to 0 +const __FlashStringHelper* Command_writeRTC(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::writeRTCSRAMSlot(slot, value)); + } else if (equals(parseString(Line, 2), F("erase")) && equals(parseString(Line, 3), F("erase"))) { + for (uint32_t slot = 0; slot < ESPEasy::eeprom::getRTCSRAMMaxSlots(); ++slot) { + # if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + ESPEasy::eeprom::writeRTCSRAMSlot(slot, 0.0); + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + ESPEasy::eeprom::writeRTCSRAMSlot(slot, 0.0f); + # endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + + if (slot % 50 == 0) { delay(0); } + } + addLog(LOG_LEVEL_INFO, F("RTC SRAM: All slot-values erased.")); + return return_command_success_flashstr(); + } + return return_command_failed_flashstr(); +} + +#endif // if FEATURE_EEPROM_EXTERNAL diff --git a/src/src/Commands/RTCSRAMStorage.h b/src/src/Commands/RTCSRAMStorage.h new file mode 100644 index 0000000000..6c0593e952 --- /dev/null +++ b/src/src/Commands/RTCSRAMStorage.h @@ -0,0 +1,8 @@ +#pragma once + +#include "../../ESPEasy_common.h" + +#if FEATURE_RTC_SRAM_STORAGE +const __FlashStringHelper* Command_writeRTC(struct EventStruct *event, + const char *Line); +#endif // if FEATURE_RTC_SRAM_STORAGE diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 0ddd3935bd..73a6c853fa 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -711,6 +711,10 @@ To create/register a plugin, you have to : #undef FEATURE_EXT_RTC #endif #define FEATURE_EXT_RTC 0 + #ifdef FEATURE_EXT_RTC_PCF8583 + #undef FEATURE_EXT_RTC_PCF8583 + #endif + #define FEATURE_EXT_RTC_PCF8583 0 #ifdef FEATURE_SYSLOG #undef FEATURE_SYSLOG #endif @@ -3209,6 +3213,10 @@ To create/register a plugin, you have to : #undef FEATURE_EXT_RTC #endif #define FEATURE_EXT_RTC 0 + #ifdef FEATURE_EXT_RTC_PCF8583 + #undef FEATURE_EXT_RTC_PCF8583 + #endif + #define FEATURE_EXT_RTC_PCF8583 0 #ifndef BUILD_NO_DEBUG #define BUILD_NO_DEBUG #endif @@ -4331,6 +4339,48 @@ 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 0 // Disabled by default on ESP8266 + #endif + #endif +#endif // ifndef FEATURE_EEPROM_EXTERNAL + +#if FEATURE_EXT_RTC +#ifndef FEATURE_RTC_SRAM_STORAGE + #ifdef ESP32 + #define FEATURE_RTC_SRAM_STORAGE 1 + #endif + #ifdef ESP8266 + #ifdef LIMIT_BUILD_SIZE + #define FEATURE_RTC_SRAM_STORAGE 0 // Disabled for limited builds on ESP8266 + #else + #define FEATURE_RTC_SRAM_STORAGE 0 // Disabled by default on ESP8266 + #endif + #endif +#endif // ifndef FEATURE_RTC_SRAM_STORAGE +#ifndef FEATURE_EXT_RTC_PCF8583 + #if defined(PLUGIN_BUILD_MAX_ESP32) + #define FEATURE_EXT_RTC_PCF8583 1 // Also include PCF8583, that has 240 bytes of SRAM + #else // if defined(PLUGIN_BUILD_MAX_ESP32) + #define FEATURE_EXT_RTC_PCF8583 0 // Also include PCF8583, that has 240 bytes of SRAM + #endif // if defined(PLUGIN_BUILD_MAX_ESP32) +#endif +#else // if FEATURE_EXT_RTC + #define FEATURE_RTC_SRAM_STORAGE 0 // Not available +#endif // if FEATURE_EXT_RTC +#if FEATURE_RTC_SRAM_STORAGE + #define FEATURE_SRAM_STORAGE_DOUBLE 1 // 1 = double-size SRAM storage = 8 bytes per slot +#else // if FEATURE_RTC_SRAM_STORAGE + #define FEATURE_SRAM_STORAGE_DOUBLE 0 // 0 = float-size SRAM storage = 4 bytes per slot +#endif // if FEATURE_RTC_SRAM_STORAGE + #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..b4e3c4077e 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.h +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.h @@ -108,6 +108,11 @@ 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) + */ }; 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 38c2dfceb8..dc0b079f45 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; @@ -635,7 +647,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. @@ -783,8 +796,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 @@ -816,6 +829,8 @@ class SettingsStruct_tmpl // TODO TD-er: For ESP8266 we may likely ever use upto 2 or 3 network interfaces, so maybe re-use the rest later? uint16_t NetworkInterfaceStartupDelay[NETWORK_MAX]{}; + uint32_t EEPROMExternalFlags{}; + // Try to extend settings to make the checksum 4-uint8_t aligned. diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index fd1c206659..d12ad749f7 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -734,6 +734,7 @@ void SettingsStruct_tmpl::clearMisc() { # endif // ifdef ESP32 } BaudRate = DEFAULT_SERIAL_BAUD; + EEPROMExternalFlags = 0; NetworkFlags._all_bits = 0; deepSleep_wakeTime = 0; CustomCSS = false; @@ -1352,8 +1353,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/DataTypes/TimeSource.cpp b/src/src/DataTypes/TimeSource.cpp index 3542a864a8..45aca6e65d 100644 --- a/src/src/DataTypes/TimeSource.cpp +++ b/src/src/DataTypes/TimeSource.cpp @@ -2,12 +2,18 @@ const __FlashStringHelper* toString(ExtTimeSource_e timeSource) { - switch (timeSource) { + switch (timeSource) + { case ExtTimeSource_e::None: break; case ExtTimeSource_e::DS1307: return F("DS1307"); case ExtTimeSource_e::DS3231: return F("DS3231"); + case ExtTimeSource_e::DS3232: return F("DS3232"); case ExtTimeSource_e::PCF8523: return F("PCF8523"); case ExtTimeSource_e::PCF8563: return F("PCF8563"); + #if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::PCF8583: return F("PCF8583 (0x50)"); + case ExtTimeSource_e::PCF8583a: return F("PCF8583 (0x51)"); + #endif // if FEATURE_EXT_RTC_PCF8583 } return F("-"); } diff --git a/src/src/DataTypes/TimeSource.h b/src/src/DataTypes/TimeSource.h index 37ea3b1ed6..461226f2d8 100644 --- a/src/src/DataTypes/TimeSource.h +++ b/src/src/DataTypes/TimeSource.h @@ -9,7 +9,13 @@ enum class ExtTimeSource_e { DS1307, DS3231, PCF8523, - PCF8563 + PCF8563, + DS3232, + #if FEATURE_EXT_RTC_PCF8583 + PCF8583, + PCF8583a, + #endif // if FEATURE_EXT_RTC_PCF8583 + }; 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/AdafruitGFX_helper.cpp b/src/src/Helpers/AdafruitGFX_helper.cpp index ed616452ef..f44bcc8851 100644 --- a/src/src/Helpers/AdafruitGFX_helper.cpp +++ b/src/src/Helpers/AdafruitGFX_helper.cpp @@ -764,7 +764,9 @@ AdafruitGFX_helper::AdafruitGFX_helper(Adafruit_GFX *display, _textPrintMode(textPrintMode), _fontscaling(fontscaling), _fgcolor(fgcolor), _bgcolor(bgcolor), _useValidation(useValidation), _textBackFill(textBackFill), _defaultFontId(defaultFontId) { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("AdaGFX_helper: GFX Init.")); + #endif // ifndef BUILD_NO_DEBUG } # if ADAGFX_ENABLE_BMP_DISPLAY @@ -785,7 +787,9 @@ AdafruitGFX_helper::AdafruitGFX_helper(Adafruit_SPITFT *display, _useValidation(useValidation), _textBackFill(textBackFill), _defaultFontId(defaultFontId) { _display = _tft; + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("AdaGFX_helper: TFT Init.")); + #endif // ifndef BUILD_NO_DEBUG } # endif // if ADAGFX_ENABLE_BMP_DISPLAY diff --git a/src/src/Helpers/Audio.cpp b/src/src/Helpers/Audio.cpp index 1ee71cda51..b334d9802a 100644 --- a/src/src/Helpers/Audio.cpp +++ b/src/src/Helpers/Audio.cpp @@ -43,7 +43,9 @@ void clear_rtttl_melody() { // The non-blocking play will read from a char pointer. // So we must stop the playing before changing the string as it could otherwise lead to a crash. if (anyrtttl::nonblocking::isPlaying()) { // If currently playing, cancel that + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("RTTTL: Cancelling running song...")); + #endif // ifndef BUILD_NO_DEBUG anyrtttl::nonblocking::stop(); # if FEATURE_RTTTL_EVENTS 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..aa94852514 100644 --- a/src/src/Helpers/ESPEasyRTC.cpp +++ b/src/src/Helpers/ESPEasyRTC.cpp @@ -168,7 +168,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/ESPEasy_TouchHandler.cpp b/src/src/Helpers/ESPEasy_TouchHandler.cpp index eea5fd5262..5fcb0af03d 100644 --- a/src/src/Helpers/ESPEasy_TouchHandler.cpp +++ b/src/src/Helpers/ESPEasy_TouchHandler.cpp @@ -2222,7 +2222,9 @@ void ESPEasy_TouchHandler::generateObjectEvent(struct EventStruct *event, eventCommand += ','; eventCommand += wrapWithQuotesIfContainsParameterSeparatorChar(TouchObjects[objectIndex].objectName); ExecuteCommand_all({ EventValueSource::Enum::VALUE_SOURCE_RULES, eventCommand }, true); // Simulate like from rules + #ifndef BUILD_NO_DEBUG addLogMove(LOG_LEVEL_INFO, eventCommand); + #endif // ifndef BUILD_NO_DEBUG delay(0); // Handle group actions @@ -2251,7 +2253,9 @@ void ESPEasy_TouchHandler::generateObjectEvent(struct EventStruct *event, case Touch_action_e::Default: break; } + #ifndef BUILD_NO_DEBUG addLogMove(LOG_LEVEL_INFO, concat(F("TOUCH event: "), toString(action))); + #endif // ifndef BUILD_NO_DEBUG } } } diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index a7d1c6d2a2..940df67801 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: @@ -83,10 +82,10 @@ void run_compiletime_checks() { check_size(); check_max_size(); #ifdef ESP32 - constexpr unsigned int SettingsStructSize = (376 + 84 * TASKS_MAX); + constexpr unsigned int SettingsStructSize = (380 + 84 * TASKS_MAX); #endif #ifdef ESP8266 - constexpr unsigned int SettingsStructSize = (344 + 84 * TASKS_MAX); + constexpr unsigned int SettingsStructSize = (348 + 84 * TASKS_MAX); #endif #if FEATURE_CUSTOM_PROVISIONING check_size(); @@ -179,7 +178,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. @@ -192,6 +191,7 @@ void run_compiletime_checks() { static_assert(GPIO_DIRECTION_NR_BITS== NR_BITS(static_cast(gpio_direction::gpio_direction_MAX)), "Correct GPIO_DIRECTION_NR_BITS"); } + String ReportOffsetErrorInStruct(const String& structname, size_t offset) { String error; if (error.reserve(48 + structname.length())) { @@ -269,4 +269,4 @@ String checkTaskSettings(taskIndex_t taskIndex) { #endif return err; } -#endif \ No newline at end of file +#endif // ifndef LIMIT_BUILD_SIZE diff --git a/src/src/Helpers/ESPEasy_time.cpp b/src/src/Helpers/ESPEasy_time.cpp index 387ea8b7a5..dbf4c0c7f6 100644 --- a/src/src/Helpers/ESPEasy_time.cpp +++ b/src/src/Helpers/ESPEasy_time.cpp @@ -978,6 +978,7 @@ bool ESPEasy_time::ExtRTC_get(uint32_t& unixtime) break; } case ExtTimeSource_e::DS3231: + case ExtTimeSource_e::DS3232: { I2CSelectHighClockSpeed(i2cBus); RTC_DS3231 rtc; @@ -1029,6 +1030,30 @@ bool ESPEasy_time::ExtRTC_get(uint32_t& unixtime) unixtime = rtc.now().unixtime(); break; } + #if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::PCF8583: + case ExtTimeSource_e::PCF8583a: + { + I2CSelect_Max100kHz_ClockSpeed(i2cBus); + RTC_PCF8583 rtc; + + if (ExtTimeSource_e::PCF8583a == Settings.ExtTimeSource()) { + rtc.altAddress(); // Set alternative address (0x51) + } + + if (!rtc.begin()) { + // Not found + break; + } + + if (rtc.lostPower() || !rtc.isrunning()) { + // Cannot get the time from the module + break; + } + unixtime = rtc.now().unixtime(); + break; + } + #endif // if FEATURE_EXT_RTC_PCF8583 } if (unixtime != 0) { @@ -1073,6 +1098,7 @@ bool ESPEasy_time::ExtRTC_set(uint32_t unixtime) break; } case ExtTimeSource_e::DS3231: + case ExtTimeSource_e::DS3232: { I2CSelectHighClockSpeed(i2cBus); RTC_DS3231 rtc; @@ -1108,6 +1134,25 @@ bool ESPEasy_time::ExtRTC_set(uint32_t unixtime) } break; } + #if FEATURE_EXT_RTC_PCF8583 + case ExtTimeSource_e::PCF8583: + case ExtTimeSource_e::PCF8583a: + { + I2CSelect_Max100kHz_ClockSpeed(i2cBus); + RTC_PCF8583 rtc; + + if (ExtTimeSource_e::PCF8583a == Settings.ExtTimeSource()) { + rtc.altAddress(); // Set alternative address (0x51) + } + + if (rtc.begin()) { + rtc.adjust(DateTime(unixtime)); + rtc.start(); + timeAdjusted = true; + } + break; + } + #endif // if FEATURE_EXT_RTC_PCF8583 } if (timeAdjusted) { 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/OTA.cpp b/src/src/Helpers/OTA.cpp index 77f1d4d907..3aa0c3e8bf 100644 --- a/src/src/Helpers/OTA.cpp +++ b/src/src/Helpers/OTA.cpp @@ -124,9 +124,7 @@ void ArduinoOTAInit() #endif if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = F("OTA : Arduino OTA enabled on port "); - log += ARDUINO_OTA_PORT; - addLogMove(LOG_LEVEL_INFO, log); + addLogMove(LOG_LEVEL_INFO, concat(F("OTA : Arduino OTA enabled on port "), ARDUINO_OTA_PORT)); } } } diff --git a/src/src/Helpers/PeriodicalActions.cpp b/src/src/Helpers/PeriodicalActions.cpp index c2c829222e..c931d85032 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 ccf985cf7d..07b2971ca0 100644 --- a/src/src/Helpers/StringParser.cpp +++ b/src/src/Helpers/StringParser.cpp @@ -24,7 +24,12 @@ #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" - +#if FEATURE_EEPROM_EXTERNAL +#include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" +#endif // if FEATURE_EEPROM_EXTERNAL +#if FEATURE_RTC_SRAM_STORAGE +#include "../../ESPEasy/eeprom/Helpers/RTCSRAMStorage.h" +#endif // if FEATURE_RTC_SRAM_STORAGE /********************************************************************************************\ Parse string template @@ -178,10 +183,22 @@ 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 FEATURE_RTC_SRAM_STORAGE + const bool devNameEqReadRTC = equals(deviceName, F("readrtc")); + #endif // if FEATURE_RTC_SRAM_STORAGE 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 + #if FEATURE_RTC_SRAM_STORAGE + || devNameEqReadRTC + #endif // if FEATURE_RTC_SRAM_STORAGE ) { // Address an internal variable either as float or as int @@ -209,6 +226,54 @@ 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 + #if FEATURE_RTC_SRAM_STORAGE + if (devNameEqReadRTC) { + uint32_t slot{}; + String value; + if (validUIntFromString(valueName, slot)) { + #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + value = doubleToString(ESPEasy::eeprom::readRTCSRAMSlot(slot)); + #else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + value = toString(ESPEasy::eeprom::readRTCSRAMSlot(slot)); + #endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + } else if (valueName.equalsIgnoreCase(F("max"))) { + value = ESPEasy::eeprom::getRTCSRAMMaxSlots(); + } + if (!value.isEmpty()) { + transformValue( + newString, + minimal_lineSize, + std::move(value), + format, + tmpString); + } + } else + #endif // if FEATURE_RTC_SRAM_STORAGE { const ESPEASY_RULES_FLOAT_TYPE floatvalue = getCustomFloatVar(valueName); unsigned char nr_decimals = maxNrDecimals_fpType(floatvalue); diff --git a/src/src/PluginStructs/P005_data_struct.cpp b/src/src/PluginStructs/P005_data_struct.cpp index f5caadc6d3..96b68f3f6b 100644 --- a/src/src/PluginStructs/P005_data_struct.cpp +++ b/src/src/PluginStructs/P005_data_struct.cpp @@ -38,12 +38,16 @@ const __FlashStringHelper* P005_logString(P005_logNr logNr) { \*********************************************************************************************/ void P005_log(struct EventStruct *event, P005_logNr logNr) { + #ifndef BUILD_NO_DEBUG bool isError = true; + #endif // ifndef BUILD_NO_DEBUG switch (logNr) { case P005_logNr::P005_info_temperature: case P005_logNr::P005_info_humidity: + #ifndef BUILD_NO_DEBUG isError = false; + #endif // ifndef BUILD_NO_DEBUG break; default: @@ -52,6 +56,7 @@ void P005_log(struct EventStruct *event, P005_logNr logNr) break; } + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(isError ? LOG_LEVEL_ERROR : LOG_LEVEL_INFO)) { String text = concat(F("DHT : "), P005_logString(logNr)); @@ -64,6 +69,7 @@ void P005_log(struct EventStruct *event, P005_logNr logNr) } addLogMove(isError ? LOG_LEVEL_ERROR : LOG_LEVEL_INFO, text); } + #endif // ifndef BUILD_NO_DEBUG } P005_data_struct::P005_data_struct(struct EventStruct *event) { diff --git a/src/src/PluginStructs/P014_data_struct.cpp b/src/src/PluginStructs/P014_data_struct.cpp index 9091caeb57..bd45b013d8 100644 --- a/src/src/PluginStructs/P014_data_struct.cpp +++ b/src/src/PluginStructs/P014_data_struct.cpp @@ -50,10 +50,7 @@ bool P014_data_struct::finalizeInit(uint8_t i2caddr, uint8_t resolution) addLogMove(LOG_LEVEL_INFO, strformat(F("P014: chip_id=%d"), chip_id)); } if (chip_id == CHIP_ID_SI7013){ - if (!I2C_write8_reg(i2caddr,SI7013_WRITE_REG2, SI7013_REG2_DEFAULT )){ - return false; - } - if (!enablePowerForADC(i2caddr)){ + if (!I2C_write8_reg(i2caddr,SI7013_WRITE_REG2, SI7013_REG2_DEFAULT ) || !enablePowerForADC(i2caddr)){ return false; } } diff --git a/src/src/PluginStructs/P020_data_struct.cpp b/src/src/PluginStructs/P020_data_struct.cpp index becf5a6d88..890616a573 100644 --- a/src/src/PluginStructs/P020_data_struct.cpp +++ b/src/src/PluginStructs/P020_data_struct.cpp @@ -75,7 +75,7 @@ void P020_Task::startServer(uint16_t portnumber) { ser2netServer->begin(); if (serverActive(ser2netServer)) { - addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: WiFi server started at port %d"), portnumber)); + addLog(LOG_LEVEL_INFO, concat(F("Ser2Net: WiFi server started at port "), portnumber)); } else { addLog(LOG_LEVEL_ERROR, strformat(F("Ser2Net: WiFi server start FAILED at port %d, retrying..."), portnumber)); } @@ -105,11 +105,11 @@ void P020_Task::checkServer() { if (ser2netUdp->begin(_udpport) == 0) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - addLog(LOG_LEVEL_ERROR, strformat(F("Ser2Net: Cannot bind UDP at port %d"), _udpport)); + addLog(LOG_LEVEL_ERROR, concat(F("Ser2Net: Cannot bind UDP at port "), _udpport)); } } else { if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: UDP receiver started at port %d"), _udpport)); + addLog(LOG_LEVEL_INFO, concat(F("Ser2Net: UDP receiver started at port "), _udpport)); } } } @@ -378,9 +378,11 @@ void P020_Task::discardSerialIn() { // We can also use the rules engine for local control! void P020_Task::rulesEngine(const String& message, struct EventStruct *event) { if (!Settings.UseRules || message.isEmpty() || (P020_Events::None == serial_processing)) { return; } - int NewLinePos = 0; - uint16_t StartPos = 0; - bool eventSent = false; + int NewLinePos = 0; + uint16_t StartPos = 0; + # if FEATURE_STRING_VARIABLES + bool eventSent = false; + # endif // if FEATURE_STRING_VARIABLES NewLinePos = handleMultiLine ? message.indexOf('\n', StartPos) : message.length(); @@ -449,7 +451,9 @@ void P020_Task::rulesEngine(const String& message, struct EventStruct *event) { eventString += message.substring(StartPos, NewLinePos); } eventQueue.addMove(std::move(eventString)); + # if FEATURE_STRING_VARIABLES eventSent = true; + # endif // if FEATURE_STRING_VARIABLES break; } case P020_Events::P1WiFiGateway: // P1 WiFi Gateway @@ -474,7 +478,9 @@ void P020_Task::rulesEngine(const String& message, struct EventStruct *event) { if (!eventString.isEmpty()) { eventQueue.add(eventString); + # if FEATURE_STRING_VARIABLES eventSent = true; + # endif // if FEATURE_STRING_VARIABLES } NewLinePos = message.indexOf('\n', StartPos); diff --git a/src/src/PluginStructs/P035_data_struct.cpp b/src/src/PluginStructs/P035_data_struct.cpp index eb3bdd8ded..7a67ddd0cc 100644 --- a/src/src/PluginStructs/P035_data_struct.cpp +++ b/src/src/PluginStructs/P035_data_struct.cpp @@ -30,8 +30,7 @@ bool P035_data_struct::plugin_init(struct EventStruct *event) { if ((Plugin_035_irSender == nullptr) && validGpio(_gpioPin)) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, F("INIT: IR TX")); - addLog(LOG_LEVEL_INFO, F("IR lib Version: " _IRREMOTEESP8266_VERSION_STR)); + addLog(LOG_LEVEL_INFO, F("INIT: IR TX, lib Version: " _IRREMOTEESP8266_VERSION_STR)); # ifdef P035_DEBUG_LOG addLog(LOG_LEVEL_INFO, concat(F("Supported Protocols by IRSEND: "), listProtocols())); # endif // ifdef P035_DEBUG_LOG diff --git a/src/src/PluginStructs/P037_data_struct.cpp b/src/src/PluginStructs/P037_data_struct.cpp index 269216848f..f222ff5e7b 100644 --- a/src/src/PluginStructs/P037_data_struct.cpp +++ b/src/src/PluginStructs/P037_data_struct.cpp @@ -33,8 +33,9 @@ bool P037_data_struct::loadSettings() { if (_taskIndex < TASKS_MAX) { # ifdef USE_SECOND_HEAP -// HeapSelectIram ephemeral; - #endif + + // HeapSelectIram ephemeral; + # endif // ifdef USE_SECOND_HEAP size_t offset = 0; LoadCustomTaskSettings(_taskIndex, mqttTopics, VARS_PER_TASK, 41, offset); @@ -50,7 +51,7 @@ bool P037_data_struct::loadSettings() { LoadCustomTaskSettings(_taskIndex, tmp, 1, 41, offset); move_special(globalTopicPrefix, std::move(tmp[0])); - offset += 41; + offset += 41; } @@ -292,7 +293,7 @@ bool P037_data_struct::webform_load( html_TD(); addTextBox(getPluginCustomArgName(idx + 100 + 0), parseStringKeepCase(valueArray[filterOffset], 1, P037_VALUE_SEPARATOR), - 32, + 32, F("xwide")); } { @@ -302,7 +303,7 @@ bool P037_data_struct::webform_load( html_TD(); addTextBox(getPluginCustomArgName(idx + 100 + 2), parseStringKeepCase(valueArray[filterOffset], 3, P037_VALUE_SEPARATOR), - 32, + 32, F("")); addUnit(F("Range/List: separate values with ; ")); html_TD(); @@ -314,17 +315,13 @@ bool P037_data_struct::webform_load( # ifdef PLUGIN_037_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String info; - info.reserve(25); - info += concat(F("P037 maxFilter: "), (int)_maxFilter); - info += concat(F(" idx: "), (int)idx); - addLogMove(LOG_LEVEL_INFO, info); + addLogMove(LOG_LEVEL_INFO, strformat(F("P037 maxFilter: %d idx: %d"), _maxFilter, idx)); } # endif // ifdef PLUGIN_037_DEBUG # ifndef P037_FILTER_PER_TOPIC filterIndex = 0; uint8_t extraFilters = 0; - + while (extraFilters < P037_EXTRA_VALUES && idx < P037_MAX_FILTERS * 3) { { html_TR_TD(); @@ -332,7 +329,7 @@ bool P037_data_struct::webform_load( addHtmlInt(filterNr); html_TD(); addTextBox(getPluginCustomArgName(idx + 100 + 0), EMPTY_STRING, - 32, + 32, F("xwide")); } { @@ -340,7 +337,7 @@ bool P037_data_struct::webform_load( selector.addSelector(getPluginCustomArgName(idx + 100 + 1), filterIndex); html_TD(); addTextBox(getPluginCustomArgName(idx + 100 + 2), EMPTY_STRING, - 32, + 32, F("")); addUnit(F("Range/List: separate values with ; ")); html_TD(); @@ -355,9 +352,7 @@ bool P037_data_struct::webform_load( # ifdef PLUGIN_037_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { - info = concat(F("P037 extraFilters: "), (int)extraFilters); - info += concat(F(" idx: "), (int)idx); - addLogMove(LOG_LEVEL_INFO, info); + addLogMove(LOG_LEVEL_INFO, strformat(F("P037 extraFilters: %d idx: %d"), extraFilters, idx)); } # endif // ifdef PLUGIN_037_DEBUG # endif // ifndef P037_FILTER_PER_TOPIC @@ -388,8 +383,8 @@ bool P037_data_struct::webform_load( html_table_header(F("Value"), 300); const __FlashStringHelper *operandOptions[] = { - F("map"), // map name to int - F("percentage") }; // map attribute value to percentage of provided value + F("map"), // map name to int + F("percentage") }; // map attribute value to percentage of provided value const int operandIndices[] = { 0, 1 }; const FormSelectorOptions selector(P037_OPERAND_COUNT, operandOptions, operandIndices); @@ -408,7 +403,7 @@ bool P037_data_struct::webform_load( html_TD(); addTextBox(getPluginCustomArgName(idx + 0), parseStringKeepCase(valueArray[mappingOffset], 1, P037_VALUE_SEPARATOR), - 32, + 32, F("")); } { @@ -419,7 +414,7 @@ bool P037_data_struct::webform_load( html_TD(); addTextBox(getPluginCustomArgName(idx + 2), parseStringKeepCase(valueArray[mappingOffset], 3, P037_VALUE_SEPARATOR), - 32, + 32, F("")); html_TD(); } @@ -429,11 +424,7 @@ bool P037_data_struct::webform_load( # ifdef PLUGIN_037_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String info; - info.reserve(25); - info += concat(F("P037 maxIdx: "), (int)_maxIdx); - info += concat(F(" idx: "), (int)idx); - addLogMove(LOG_LEVEL_INFO, info); + addLogMove(LOG_LEVEL_INFO, strformat(F("P037 maxIdx: %d idx: %d"), _maxIdx, idx)); } # endif // ifdef PLUGIN_037_DEBUG operandIndex = 0; @@ -445,18 +436,18 @@ bool P037_data_struct::webform_load( addHtml(F(" ")); addHtmlInt(mapNr); html_TD(); - addTextBox(getPluginCustomArgName(idx + 0), + addTextBox(getPluginCustomArgName(idx + 0), EMPTY_STRING, - 32, + 32, F("")); } { html_TD(); selector.addSelector(getPluginCustomArgName(idx + 1), operandIndex); html_TD(); - addTextBox(getPluginCustomArgName(idx + 2), + addTextBox(getPluginCustomArgName(idx + 2), EMPTY_STRING, - 32, + 32, F("")); html_TD(); } @@ -468,19 +459,15 @@ bool P037_data_struct::webform_load( # ifdef PLUGIN_037_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String info; - info.reserve(35); - info += concat(F("P037 extraMappings: "), (int)extraMappings); - info += concat(F(" idx: "), (int)idx); - addLogMove(LOG_LEVEL_INFO, info); + addLogMove(LOG_LEVEL_INFO, strformat(F("P037 extraMappings: %d idx: %d"), extraMappings, idx)); } # endif // ifdef PLUGIN_037_DEBUG addFormNote(F("Both Name and Value must be filled for a valid mapping. Mappings are case-sensitive.")); if (extraMappings == P037_EXTRA_VALUES) { addFormNote(strformat( - F("After filling all mappings, submitting this page will make extra mappings available (up to %d)."), - P037_MAX_MAPPINGS)); + F("After filling all mappings, submitting this page will make extra mappings available (up to %d)."), + P037_MAX_MAPPINGS)); } } # endif // if P037_MAPPING_SUPPORT @@ -638,14 +625,10 @@ bool P037_data_struct::webform_save( # if P037_MAPPING_SUPPORT # ifdef PLUGIN_037_DEBUG + void P037_data_struct::logMapValue(const String& input, const String& result) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String info; - info.reserve(45); - info += concat(F("IMPT : MQTT mapped value '"), input); - info += concat(F("' to '"), result); - info += '\''; - addLogMove(LOG_LEVEL_INFO, info); + addLogMove(LOG_LEVEL_INFO, strformat(F("IMPT : MQTT mapped value '%s' to '%s'"), input.c_str(), result.c_str())); } } // logMapValue @@ -671,7 +654,8 @@ String P037_data_struct::mapValue(const String& input, const String& attribute) if ((name == input) || ((!attribute.isEmpty()) && (name == attribute))) { int8_t operandIndex = operands.indexOf(oper); - switch (operandIndex) { + switch (operandIndex) + { case 0: // = => 1:1 mapping { if (!valu.isEmpty()) { @@ -690,9 +674,12 @@ String P037_data_struct::mapValue(const String& input, const String& attribute) if (validDoubleFromString(input, inputDouble) && validDoubleFromString(valu, mappingDouble)) { if (compareDoubleValues('>', mappingDouble, 0.0)) { - ESPEASY_RULES_FLOAT_TYPE resultDouble = (static_cast(100) / mappingDouble) * inputDouble; // Simple calculation to percentage - int8_t decimals = 0; - int8_t dotPos = input.indexOf('.'); + ESPEASY_RULES_FLOAT_TYPE resultDouble = (static_cast(100) / mappingDouble) * inputDouble; // Simple + // calculation + // to + // percentage + int8_t decimals = 0; + int8_t dotPos = input.indexOf('.'); if (dotPos > -1) { String decPart = input.substring(dotPos + 1); @@ -733,6 +720,7 @@ bool P037_data_struct::hasFilters() { } // hasFilters # ifdef P037_FILTER_PER_TOPIC + String P037_data_struct::getFilterAsTopic(uint8_t topicId) { String result; @@ -763,15 +751,10 @@ String P037_data_struct::getFilterAsTopic(uint8_t topicId) { # endif // P037_FILTER_PER_TOPIC # ifdef PLUGIN_037_DEBUG + void P037_data_struct::logFilterValue(const String& text, const String& key, const String& value, const String& match) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log; - log.reserve(text.length() + key.length() + value.length() + match.length() + 16); - log += text; - log += key; - log += concat(F(" value: "), value); - log += concat(F(" match: "), match); - addLogMove(LOG_LEVEL_INFO, log); + addLogMove(LOG_LEVEL_INFO, strformat(F("%s %s value: %s match: %s"), text.c_str(), key.c_str(), value.c_str(), match.c_str())); } } // logFilterValue @@ -790,10 +773,10 @@ bool P037_data_struct::checkFilters(const String& key, const String& value, int8 if ((!key.isEmpty()) && (!value.isEmpty())) { // Ignore empty input(s) - String filters = P037_FILTER_LIST; - String valueData = value; - String fltKey, fltIndex, filterData, fltOper; - ESPEASY_RULES_FLOAT_TYPE from, to, doubleValue; + String filters = P037_FILTER_LIST; + String valueData = value; + String fltKey, fltIndex, filterData, fltOper; + ESPEASY_RULES_FLOAT_TYPE from, to, doubleValue; int8_t rangeSeparator; bool accept = true; bool matchTopicId = true; @@ -825,8 +808,9 @@ bool P037_data_struct::checkFilters(const String& key, const String& value, int8 filterData = parseStringKeepCase(valueArray[flt], 3, P037_VALUE_SEPARATOR); parseSystemVariables(filterData, false); // Replace system variables - switch (filterIndex) { - case 0: // = => equals + switch (filterIndex) + { + case 0: // = => equals { _filterListItem = EMPTY_STRING; @@ -903,7 +887,8 @@ bool P037_data_struct::checkFilters(const String& key, const String& value, int8 validDoubleFromString(valueData, doubleValue)) { accept = false; - do { + do + { item = filterData.substring(0, rangeSeparator); item.trim(); filterData = filterData.substring(rangeSeparator + 1); @@ -980,7 +965,8 @@ bool P037_data_struct::parseJSONMessage(const String& message) { // Try to allocate in PSRAM or 2nd heap if possible constexpr unsigned size = sizeof(DynamicJsonDocument); void *ptr = special_calloc(1, size); - if (ptr) { + + if (ptr) { root = new (ptr) DynamicJsonDocument(lastJsonMessageLength); // Dynamic allocation } } @@ -989,9 +975,9 @@ bool P037_data_struct::parseJSONMessage(const String& message) { deserializeJson(*root, message); if (!root->isNull()) { - # ifdef USE_SECOND_HEAP + # ifdef USE_SECOND_HEAP HeapSelectIram ephemeral; - # endif // ifdef USE_SECOND_HEAP + # endif // ifdef USE_SECOND_HEAP result = true; doc = root->as(); diff --git a/src/src/PluginStructs/P038_data_struct.cpp b/src/src/PluginStructs/P038_data_struct.cpp index fec73913f9..d549bbe617 100644 --- a/src/src/PluginStructs/P038_data_struct.cpp +++ b/src/src/PluginStructs/P038_data_struct.cpp @@ -70,6 +70,7 @@ enum class p038_commands_e : int8_t { neopixelfor, neopixelforhsv, # endif // if P038_FEATURE_NEOPIXELFOR + }; bool P038_data_struct::plugin_write(struct EventStruct *event, const String& string) { @@ -82,14 +83,18 @@ bool P038_data_struct::plugin_write(struct EventStruct *event, const String& str if (cmd_i < 0) { return false; } // Fail fast + # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("P038 : write - "), string)); } + # endif // ifndef BUILD_NO_DEBUG const p038_commands_e cmde = static_cast(cmd_i); success = true; - switch (cmde) { + switch (cmde) + { case p038_commands_e::invalid: break; case p038_commands_e::neopixel: diff --git a/src/src/PluginStructs/P047_data_struct.cpp b/src/src/PluginStructs/P047_data_struct.cpp index 6ffe0d16dc..7ede2682ec 100644 --- a/src/src/PluginStructs/P047_data_struct.cpp +++ b/src/src/PluginStructs/P047_data_struct.cpp @@ -11,7 +11,7 @@ P047_data_struct::P047_data_struct(uint8_t address, _address(address), _model(static_cast(model)) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, - strformat(F("SoilMoisture: Initializing sensor: %s, version: 0x%x"), String(toString(_model)).c_str(), getVersion())); + strformat(F("SoilMoisture: Initializing sensor: %s, version: 0x%x"), FsP(toString(_model)), getVersion())); } } @@ -120,6 +120,7 @@ bool P047_data_struct::plugin_read(struct EventStruct *event) { UserVar.setFloat(event->TaskIndex, 1, moisture); UserVar.setFloat(event->TaskIndex, 2, light); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = strformat(F("SoilMoisture: Address: 0x%02x"), P047_I2C_ADDR); @@ -134,6 +135,7 @@ bool P047_data_struct::plugin_read(struct EventStruct *event) { addLogMove(LOG_LEVEL_INFO, concat(F("SoilMoisture: Light: "), formatUserVarNoCheck(event, 2))); } } + #endif // ifndef BUILD_NO_DEBUG if (P047_SENSOR_SLEEP) { // send sensor to sleep diff --git a/src/src/PluginStructs/P049_data_struct.cpp b/src/src/PluginStructs/P049_data_struct.cpp index 287f12a3ee..139a8fec7d 100644 --- a/src/src/PluginStructs/P049_data_struct.cpp +++ b/src/src/PluginStructs/P049_data_struct.cpp @@ -103,25 +103,33 @@ bool P049_data_struct::plugin_write(struct EventStruct *event, const String& str if (equals(command, F("mhzcalibratezero"))) { send_mhzCmd(mhzCmdCalibrateZero); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("MHZ19: Calibrated zero point!")); + #endif // ifndef BUILD_NO_DEBUG return true; } else if (equals(command, F("mhzreset"))) { send_mhzCmd(mhzCmdReset); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("MHZ19: Sent sensor reset!")); + #endif // ifndef BUILD_NO_DEBUG return true; } else if (equals(command, F("mhzabcenable"))) { send_mhzCmd(mhzCmdABCEnable); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("MHZ19: Sent sensor ABC Enable!")); + #endif // ifndef BUILD_NO_DEBUG return true; } else if (equals(command, F("mhzabcdisable"))) { send_mhzCmd(mhzCmdABCDisable); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("MHZ19: Sent sensor ABC Disable!")); + #endif // ifndef BUILD_NO_DEBUG return true; } @@ -130,25 +138,33 @@ bool P049_data_struct::plugin_write(struct EventStruct *event, const String& str if (equals(command, F("mhzmeasurementrange1000"))) { send_mhzCmd(mhzCmdMeasurementRange1000); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("MHZ19: Sent measurement range 0-1000PPM!")); + #endif // ifndef BUILD_NO_DEBUG return true; } else if (equals(command, F("mhzmeasurementrange2000"))) { send_mhzCmd(mhzCmdMeasurementRange2000); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("MHZ19: Sent measurement range 0-2000PPM!")); + #endif // ifndef BUILD_NO_DEBUG return true; } else if (equals(command, F("mhzmeasurementrange3000"))) { send_mhzCmd(mhzCmdMeasurementRange3000); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("MHZ19: Sent measurement range 0-3000PPM!")); + #endif // ifndef BUILD_NO_DEBUG return true; } else if (equals(command, F("mhzmeasurementrange5000"))) { send_mhzCmd(mhzCmdMeasurementRange5000); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("MHZ19: Sent measurement range 0-5000PPM!")); + #endif // ifndef BUILD_NO_DEBUG return true; } } diff --git a/src/src/PluginStructs/P053_data_struct.cpp b/src/src/PluginStructs/P053_data_struct.cpp index 4b63bf011f..cde6ccc43f 100644 --- a/src/src/PluginStructs/P053_data_struct.cpp +++ b/src/src/PluginStructs/P053_data_struct.cpp @@ -545,7 +545,9 @@ bool P053_data_struct::checkAndClearValuesReceived(struct EventStruct *event) { bool P053_data_struct::resetSensor() { if (validGpio(_resetPin)) { // Reset if pin is configured // Toggle 'reset' to assure we start reading header + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("PMSx003: resetting module")); + #endif // ifndef BUILD_NO_DEBUG pinMode(_resetPin, OUTPUT); digitalWrite(_resetPin, LOW); delay(250); @@ -560,7 +562,9 @@ bool P053_data_struct::wakeSensor() { if (!initialized()) { return false; } + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("PMSx003: Wake sensor")); + #endif // ifndef BUILD_NO_DEBUG if (validGpio(_pwrPin)) { // Make sure the sensor is "on" @@ -594,7 +598,9 @@ bool P053_data_struct::sleepSensor() { } // Put the sensor to sleep + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("PMSx003: Sleep sensor")); + #endif // ifndef BUILD_NO_DEBUG if (_pwrPin >= 0) { pinMode(_pwrPin, OUTPUT); diff --git a/src/src/PluginStructs/P067_data_struct.cpp b/src/src/PluginStructs/P067_data_struct.cpp index a2de8e1b66..ffc7c064f2 100644 --- a/src/src/PluginStructs/P067_data_struct.cpp +++ b/src/src/PluginStructs/P067_data_struct.cpp @@ -203,14 +203,18 @@ bool P067_data_struct::plugin_write(struct EventStruct *event, P067_int2float(P067_OFFSET_CHANNEL_A_1, P067_OFFSET_CHANNEL_A_2, &_offsetChanA); OversamplingChanA.reset(); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("HX711: tare channel A")); + #endif // ifndef BUILD_NO_DEBUG success = true; } else if (equals(command, F("tarechanb"))) { P067_float2int(-UserVar[event->BaseVarIndex + 3], &P067_OFFSET_CHANNEL_B_1, &P067_OFFSET_CHANNEL_B_2); P067_int2float(P067_OFFSET_CHANNEL_B_1, P067_OFFSET_CHANNEL_B_2, &_offsetChanB); OversamplingChanB.reset(); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("HX711: tare channel B")); + #endif // ifndef BUILD_NO_DEBUG success = true; } diff --git a/src/src/PluginStructs/P095_data_struct.cpp b/src/src/PluginStructs/P095_data_struct.cpp index c22586713d..20557c6831 100644 --- a/src/src/PluginStructs/P095_data_struct.cpp +++ b/src/src/PluginStructs/P095_data_struct.cpp @@ -210,7 +210,7 @@ bool P095_data_struct::plugin_init(struct EventStruct *event) { } # endif // ifndef BUILD_NO_DEBUG } else { - addLog(LOG_LEVEL_INFO, F("ILI9341: No init?")); + addLog(LOG_LEVEL_ERROR, F("ILI9341: No init?")); } if (isInitialized()) { diff --git a/src/src/PluginStructs/P096_data_struct.cpp b/src/src/PluginStructs/P096_data_struct.cpp index 972a6b2063..97c2ac2e12 100644 --- a/src/src/PluginStructs/P096_data_struct.cpp +++ b/src/src/PluginStructs/P096_data_struct.cpp @@ -245,7 +245,7 @@ bool P096_data_struct::plugin_init(struct EventStruct *event) { success = true; } else { - addLog(LOG_LEVEL_INFO, F("EPD : No init?")); + addLog(LOG_LEVEL_ERROR, F("EPD : No init?")); } return success; @@ -269,7 +269,9 @@ void P096_data_struct::updateFontMetrics() { * plugin_exit: De-initialize before destruction ***************************************************************************/ bool P096_data_struct::plugin_exit(struct EventStruct *event) { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("EPD : Exit.")); + #endif // ifndef BUILD_NO_DEBUG # if P096_USE_EXTENDED_SETTINGS diff --git a/src/src/PluginStructs/P105_data_struct.cpp b/src/src/PluginStructs/P105_data_struct.cpp index b653ea4d11..6104a8c4fb 100644 --- a/src/src/PluginStructs/P105_data_struct.cpp +++ b/src/src/PluginStructs/P105_data_struct.cpp @@ -119,7 +119,9 @@ bool P105_data_struct::updateMeasurements(taskIndex_t task_index) { addLogMove(LOG_LEVEL_ERROR, strformat(F("%s : unable to initialize"), getDeviceName().c_str())); return false; } + #ifndef BUILD_NO_DEBUG addLogMove(LOG_LEVEL_INFO, strformat(F("%s : initialized"), getDeviceName().c_str())); + #endif // ifndef BUILD_NO_DEBUG trigger_time = current_time; state = AHTx_state::AHTx_Trigger_measurement; diff --git a/src/src/PluginStructs/P110_data_struct.cpp b/src/src/PluginStructs/P110_data_struct.cpp index 0a9b3ffaa6..1ceb903f68 100644 --- a/src/src/PluginStructs/P110_data_struct.cpp +++ b/src/src/PluginStructs/P110_data_struct.cpp @@ -16,8 +16,8 @@ bool P110_data_struct::begin(uint32_t interval_ms) { sensor.setAddress(_i2cAddress); // Initialize for configured address if (!sensor.init()) { + addLogMove(LOG_LEVEL_ERROR, strformat(F("VL53L0X: Sensor not found, init failed for 0x%02x"), _i2cAddress)); if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, strformat(F("VL53L0X: Sensor not found, init failed for 0x%02x"), _i2cAddress)); addLog(LOG_LEVEL_INFO, sensor.getInitResult()); } return false; @@ -97,12 +97,12 @@ bool P110_data_struct::plugin_read(struct EventStruct *event) { // direction_changed || (std::abs(displacement) > P110_DELTA); - # ifdef P110_INFO_LOG + # ifdef P110_INFO_LOG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("VL53L0x: Perform read: trig: %d, prev: %d, dist: %d"), triggered, p_dist, dist)); } - # endif // ifdef P110_INFO_LOG + # endif // ifdef P110_INFO_LOG // Value is classified as invalid when > 8190, so no conversion or 'split' needed UserVar.setFloat(event->TaskIndex, 0, _filtered); UserVar.setFloat(event->TaskIndex, 1, disp_dir); // Trend of value diff --git a/src/src/PluginStructs/P111_data_struct.cpp b/src/src/PluginStructs/P111_data_struct.cpp index 479638024d..c00763c5e0 100644 --- a/src/src/PluginStructs/P111_data_struct.cpp +++ b/src/src/PluginStructs/P111_data_struct.cpp @@ -145,11 +145,13 @@ bool P111_data_struct::reset(int8_t csPin, int8_t resetPin) { if ((resetPin != -1) && (initPhase == P111_initPhases::Ready)) { + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove( LOG_LEVEL_INFO, concat(F("MFRC522: Reset on pin: "), resetPin)); } + #endif // ifndef BUILD_NO_DEBUG init(); return true; diff --git a/src/src/PluginStructs/P114_data_struct.cpp b/src/src/PluginStructs/P114_data_struct.cpp index 6aa6b5a749..d468f64636 100644 --- a/src/src/PluginStructs/P114_data_struct.cpp +++ b/src/src/PluginStructs/P114_data_struct.cpp @@ -102,10 +102,7 @@ bool P114_data_struct::init_sensor() { } return false; } else if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log; - log = F("VEML6075: sensor initialised / CONF: "); - log += String((uint16_t)(IT << 4) | (HD << 3), BIN); - addLogMove(LOG_LEVEL_INFO, log); + addLogMove(LOG_LEVEL_INFO, concat(F("VEML6075: sensor initialised / CONF: "), String((uint16_t)(IT << 4) | (HD << 3), BIN))); } } return true; diff --git a/src/src/PluginStructs/P116_data_struct.cpp b/src/src/PluginStructs/P116_data_struct.cpp index b4f0dfe1ba..203a16c76d 100644 --- a/src/src/PluginStructs/P116_data_struct.cpp +++ b/src/src/PluginStructs/P116_data_struct.cpp @@ -169,7 +169,9 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { DebounceCounter = 0; // debounce counter if (nullptr == st77xx) { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("ST77xx: Init start.")); + #endif // ifndef BUILD_NO_DEBUG uint8_t initRoptions = 0xFF; #ifdef ESP32 @@ -320,7 +322,7 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { } # endif // ifndef BUILD_NO_DEBUG } else { - addLog(LOG_LEVEL_INFO, F("ST77xx: No init?")); + addLog(LOG_LEVEL_ERROR, F("ST77xx: No init?")); } if (nullptr != st77xx) { diff --git a/src/src/PluginStructs/P119_data_struct.cpp b/src/src/PluginStructs/P119_data_struct.cpp index 06ca401316..f5c187fe53 100644 --- a/src/src/PluginStructs/P119_data_struct.cpp +++ b/src/src/PluginStructs/P119_data_struct.cpp @@ -115,11 +115,19 @@ bool P119_data_struct::init_sensor() { itg3205 = new (std::nothrow) ITG3205(_i2cAddress); if (initialized()) { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("ITG3205: Initializing Gyro...")); + #endif // ifndef BUILD_NO_DEBUG itg3205->initGyro(); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("ITG3205: Calibrating Gyro...")); + #endif // ifndef BUILD_NO_DEBUG itg3205->calibrate(); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("ITG3205: Calibration done.")); + #else // ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, F("ITG3205: Init done.")); + #endif // ifndef BUILD_NO_DEBUG } else { addLog(LOG_LEVEL_ERROR, F("ITG3205: Initialization of Gyro failed.")); return false; diff --git a/src/src/PluginStructs/P120_data_struct.cpp b/src/src/PluginStructs/P120_data_struct.cpp index 26509fe336..93b0d0e697 100644 --- a/src/src/PluginStructs/P120_data_struct.cpp +++ b/src/src/PluginStructs/P120_data_struct.cpp @@ -71,7 +71,9 @@ bool P120_data_struct::read_sensor(struct EventStruct *event) { } if (initialized()) { - _x = 0; _y = 0; _z = 0; + _x = 0; + _y = 0; + _z = 0; adxl345->readAccel(&_x, &_y, &_z); _XA[_aUsed] = _x; _YA[_aUsed] = _y; @@ -134,7 +136,8 @@ bool P120_data_struct::read_data(struct EventStruct *event) const const uint8_t pconfigIndex = i + P120_QUERY1_CONFIG_POS; float value = 0.0f; - switch (static_cast(PCONFIG(pconfigIndex))) { + switch (static_cast(PCONFIG(pconfigIndex))) + { case valueType::Empty: break; case valueType::X_RAW: @@ -238,6 +241,7 @@ bool P120_data_struct::init_sensor(struct EventStruct *event) { } else { # ifdef ESP32 auto spi_ptr = getSPIBusForTask(event->TaskIndex); + if (!spi_ptr) { return false; } @@ -252,6 +256,8 @@ bool P120_data_struct::init_sensor(struct EventStruct *event) { if (initialized()) { uint8_t act = 0, freeFall = 0, singleTap = 0, doubleTap = 0; + # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("ADXL345: Initializing sensor for "); @@ -263,6 +269,7 @@ bool P120_data_struct::init_sensor(struct EventStruct *event) { log += F("..."); addLogMove(LOG_LEVEL_INFO, log); } + # endif // ifndef BUILD_NO_DEBUG adxl345->powerOn(); adxl345->setRangeSetting(2 ^ (get2BitFromUL(P120_CONFIG_FLAGS1, P120_FLAGS1_RANGE) + 1)); // Range is stored in 2 bits, only 4 possible // options @@ -373,12 +380,13 @@ void P120_data_struct::sensor_check_interrupt(struct EventStruct *event) { // Do not call again until you need to recheck for triggered actions uint8_t interrupts = adxl345->getInterruptSource(); String payload; + const bool doLog = bitRead(P120_CONFIG_FLAGS1, P120_FLAGS1_LOG_ACTIVITY); payload.reserve(30); // Free Fall Detection if (adxl345->triggered(interrupts, ADXL345_FREE_FALL)) { - if (bitRead(P120_CONFIG_FLAGS1, P120_FLAGS1_LOG_ACTIVITY)) { + if (doLog) { addLog(LOG_LEVEL_INFO, F("ADXL345: *** FREE FALL ***")); } payload = F("FreeFall="); @@ -390,7 +398,7 @@ void P120_data_struct::sensor_check_interrupt(struct EventStruct *event) { if (adxl345->triggered(interrupts, ADXL345_INACTIVITY) && bitRead(P120_CONFIG_FLAGS1, P120_FLAGS1_SEND_ACTIVITY)) { if (!inactivityTriggered) { - if (bitRead(P120_CONFIG_FLAGS1, P120_FLAGS1_LOG_ACTIVITY)) { + if (doLog) { addLog(LOG_LEVEL_INFO, F("ADXL345: *** INACTIVITY ***")); } payload = F("Inactivity="); @@ -408,7 +416,7 @@ void P120_data_struct::sensor_check_interrupt(struct EventStruct *event) { if (adxl345->triggered(interrupts, ADXL345_ACTIVITY) && bitRead(P120_CONFIG_FLAGS1, P120_FLAGS1_SEND_ACTIVITY)) { if (!activityTriggered) { - if (bitRead(P120_CONFIG_FLAGS1, P120_FLAGS1_LOG_ACTIVITY)) { + if (doLog) { addLog(LOG_LEVEL_INFO, F("ADXL345: *** ACTIVITY ***")); } payload = F("Activity="); @@ -427,14 +435,14 @@ void P120_data_struct::sensor_check_interrupt(struct EventStruct *event) { if (adxl345->triggered(interrupts, ADXL345_DOUBLE_TAP) || (adxl345->triggered(interrupts, ADXL345_SINGLE_TAP))) { if (adxl345->triggered(interrupts, ADXL345_SINGLE_TAP)) { - if (bitRead(P120_CONFIG_FLAGS1, P120_FLAGS1_LOG_ACTIVITY)) { + if (doLog) { addLog(LOG_LEVEL_INFO, F("ADXL345: *** TAP ***")); } payload = F("Tapped"); } - if (adxl345->triggered(interrupts, ADXL345_DOUBLE_TAP)) { // tonhuisman: Double-tap overrides single-tap event - if (bitRead(P120_CONFIG_FLAGS1, P120_FLAGS1_LOG_ACTIVITY)) { // This is on purpose and as intended! + if (adxl345->triggered(interrupts, ADXL345_DOUBLE_TAP)) { // tonhuisman: Double-tap overrides single-tap event + if (doLog) { // This is on purpose and as intended! addLog(LOG_LEVEL_INFO, F("ADXL345: *** DOUBLE TAP ***")); } payload = F("DoubleTapped"); @@ -792,7 +800,8 @@ void P120_data_struct::plugin_get_device_value_names(struct EventStruct *event) } const __FlashStringHelper * P120_data_struct::valuename(uint8_t value_nr, bool displayString) { - switch (static_cast(value_nr)) { + switch (static_cast(value_nr)) + { case valueType::Empty: return displayString ? F("Empty") : F(""); case valueType::X_RAW: return displayString ? F("X RAW") : F("X"); case valueType::Y_RAW: return displayString ? F("Y RAW") : F("Y"); @@ -808,9 +817,6 @@ const __FlashStringHelper * P120_data_struct::valuename(uint8_t value_nr, bool d return F(""); } -bool P120_data_struct::isXYZ(valueType vtype) -{ - return vtype >= valueType::X_RAW && vtype <= valueType::Z_g; -} +bool P120_data_struct::isXYZ(valueType vtype) { return vtype >= valueType::X_RAW && vtype <= valueType::Z_g; } #endif // if defined(USES_P120) || defined(USES_P125) diff --git a/src/src/PluginStructs/P123_data_struct.cpp b/src/src/PluginStructs/P123_data_struct.cpp index f4501d51a2..bf810a3bfa 100644 --- a/src/src/PluginStructs/P123_data_struct.cpp +++ b/src/src/PluginStructs/P123_data_struct.cpp @@ -144,7 +144,7 @@ bool P123_data_struct::init(struct EventStruct *event) { addLogMove(LOG_LEVEL_INFO, concat(concat(F("P123 DEBUG Plugin"), nullptr != touchscreen ? F(" & touchscreen") : F("")), F(" initialized."))); } else { - addLog(LOG_LEVEL_INFO, F("P123 DEBUG Touchscreen initialization FAILED.")); + addLog(LOG_LEVEL_ERROR, F("P123 DEBUG Touchscreen initialization FAILED.")); # endif // PLUGIN_123_DEBUG } return isInitialized(); diff --git a/src/src/PluginStructs/P128_data_struct.cpp b/src/src/PluginStructs/P128_data_struct.cpp index 5a0a5b0d29..68c3360517 100644 --- a/src/src/PluginStructs/P128_data_struct.cpp +++ b/src/src/PluginStructs/P128_data_struct.cpp @@ -46,8 +46,8 @@ bool P128_data_struct::plugin_read(struct EventStruct *event) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, strformat( F("Lights: mode: %s lastmode: %s fadetime: %d fadedelay: %d"), - String(P128_modeType_toString(mode)).c_str(), - String(P128_modeType_toString(savemode)).c_str(), + FsP(P128_modeType_toString(mode)), + FsP(P128_modeType_toString(savemode)), (int)UserVar[event->BaseVarIndex + 2], (int)UserVar[event->BaseVarIndex + 3])); } diff --git a/src/src/PluginStructs/P131_data_struct.cpp b/src/src/PluginStructs/P131_data_struct.cpp index 6dc8944649..584ad0952d 100644 --- a/src/src/PluginStructs/P131_data_struct.cpp +++ b/src/src/PluginStructs/P131_data_struct.cpp @@ -99,7 +99,7 @@ bool P131_data_struct::plugin_init(struct EventStruct *event) { addLogMove(LOG_LEVEL_INFO, log); } } else { - addLog(LOG_LEVEL_INFO, F("NEOMATRIX: Init failed.")); + addLog(LOG_LEVEL_ERROR, F("NEOMATRIX: Init failed.")); # endif // ifndef BUILD_NO_DEBUG } @@ -453,11 +453,7 @@ bool P131_data_struct::plugin_write(struct EventStruct *event, const String& str # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = F("NEOMATRIX: set line "); - log += event->Par2; - log += F(": "); - log += strings[x]; - addLogMove(LOG_LEVEL_INFO, log); + addLogMove(LOG_LEVEL_INFO, strformat(F("NEOMATRIX: set line %d: %s"), event->Par2, strings[x].c_str())); } # endif // ifndef BUILD_NO_DEBUG } diff --git a/src/src/PluginStructs/P134_data_struct.cpp b/src/src/PluginStructs/P134_data_struct.cpp index 10ddf2cddf..74a99a1a88 100644 --- a/src/src/PluginStructs/P134_data_struct.cpp +++ b/src/src/PluginStructs/P134_data_struct.cpp @@ -99,9 +99,11 @@ bool P134_data_struct::plugin_read(struct EventStruct *event) { if (measurementStatus == A02YYUW_status_e::STATUS_OK) { UserVar.setFloat(event->TaskIndex, 0, static_cast(measuredDistance)); + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("A02YYUW: Distance value = "), static_cast(measuredDistance))); } + #endif // ifndef BUILD_NO_DEBUG success = true; } else { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { diff --git a/src/src/PluginStructs/P135_data_struct.cpp b/src/src/PluginStructs/P135_data_struct.cpp index b4e37c57cb..ddc65e0f97 100644 --- a/src/src/PluginStructs/P135_data_struct.cpp +++ b/src/src/PluginStructs/P135_data_struct.cpp @@ -95,7 +95,9 @@ bool P135_data_struct::plugin_read(struct EventStruct *event) { getMeasure = false; singleShotStarted = true; + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("SCD4x: SingleShot measurement started.")); + # endif // ifndef BUILD_NO_DEBUG } if (getMeasure && scd4x->readMeasurement()) { @@ -140,63 +142,69 @@ bool P135_data_struct::plugin_read(struct EventStruct *event) { # if P135_FEATURE_RESET_COMMANDS if (operation != SCD4x_Operations_e::None) { - switch (operation) { + switch (operation) + { # if P135_FEATURE_FACTORYRESET - case SCD4x_Operations_e::RunFactoryReset: { // May take up to 1200 mSec + case SCD4x_Operations_e::RunFactoryReset: // May take up to 1200 mSec + { success = scd4x->performFactoryReset(); - String log = F("SCD4x: Factory reset "); uint8_t lvl = LOG_LEVEL_INFO; if (success) { initialized = startPeriodicMeasurements(); // Select the correct periodic measurement, and start a READ Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + P135_STOP_MEASUREMENT_DELAY); - log += F("success."); } else { - lvl = LOG_LEVEL_ERROR; - log += F("failed!"); + lvl = LOG_LEVEL_ERROR; + } + + if (loglevelActiveFor(lvl)) { + addLog(lvl, concat(F("SCD4x: Factory reset "), success ? F("success.") : F("failed!"))); } - addLog(lvl, log); break; } # endif // if P135_FEATURE_FACTORYRESET - case SCD4x_Operations_e::RunSelfTest: { // May take up to 10 seconds! + case SCD4x_Operations_e::RunSelfTest: // May take up to 10 seconds! + { success = scd4x->performSelfTest(); - String log = F("SCD4x: Sensor self-test "); uint8_t lvl = LOG_LEVEL_INFO; if (success) { initialized = startPeriodicMeasurements(); // Select the correct periodic measurement, and start a READ Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + P135_STOP_MEASUREMENT_DELAY); - log += F("success."); } else { - lvl = LOG_LEVEL_ERROR; - log += F("failed!"); + lvl = LOG_LEVEL_ERROR; + } + + if (loglevelActiveFor(lvl)) { + addLog(lvl, concat(F("SCD4x: Sensor self-test "), success ? F("success.") : F("failed!"))); } - addLog(lvl, log); break; } - case SCD4x_Operations_e::RunForcedRecalibration: { // May take up to 400 mSec + case SCD4x_Operations_e::RunForcedRecalibration: // May take up to 400 mSec + { float frcCorrection = 0.0f; success = scd4x->performForcedRecalibration(frcValue, &frcCorrection); frcValue = 0; - String log = F("SCD4x: Forced Recalibration "); uint8_t lvl = LOG_LEVEL_INFO; if (success) { initialized = startPeriodicMeasurements(); // Select the correct periodic measurement, and start a READ Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + P135_STOP_MEASUREMENT_DELAY); - log += strformat(F("success. New setting: %d, correction: %.2f"), frcValue, frcCorrection); } else { - lvl = LOG_LEVEL_ERROR; - log += F("failed!"); + lvl = LOG_LEVEL_ERROR; + } + + if (loglevelActiveFor(lvl)) { + addLog(lvl, concat(F("SCD4x: Forced Recalibration "), + success ? strformat(F("success. New setting: %d, correction: %.2f"), frcValue, frcCorrection) : F("failed!"))); } - addLog(lvl, log); break; } - case SCD4x_Operations_e::None: { // To keep the compiler and developer happy :-) + case SCD4x_Operations_e::None: // To keep the compiler and developer happy :-) + { break; } } diff --git a/src/src/PluginStructs/P138_data_struct.cpp b/src/src/PluginStructs/P138_data_struct.cpp index f4513afa03..cdf0b2c4c6 100644 --- a/src/src/PluginStructs/P138_data_struct.cpp +++ b/src/src/PluginStructs/P138_data_struct.cpp @@ -43,7 +43,9 @@ int Plugin_138_QueryVType(uint8_t value_nr) { // Constructor // **************************************************************************/ P138_data_struct::P138_data_struct(struct EventStruct *event) { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("IP5306: Init.")); + #endif // ifndef BUILD_NO_DEBUG _ip5306 = new (std::nothrow) arduino::ip5306(); // Default address and I2C Wire object } diff --git a/src/src/PluginStructs/P141_data_struct.cpp b/src/src/PluginStructs/P141_data_struct.cpp index bb3a1f6843..8e62db171d 100644 --- a/src/src/PluginStructs/P141_data_struct.cpp +++ b/src/src/PluginStructs/P141_data_struct.cpp @@ -73,7 +73,9 @@ bool P141_data_struct::plugin_init(struct EventStruct *event) { if (nullptr == pcd8544) { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("PCD8544: Init start.")); + #endif // ifndef BUILD_NO_DEBUG pcd8544 = new (std::nothrow) Adafruit_PCD8544(P141_DC_PIN, P141_CS_PIN, P141_RST_PIN # ifdef ESP32 @@ -95,7 +97,7 @@ bool P141_data_struct::plugin_init(struct EventStruct *event) { } # endif // ifndef BUILD_NO_DEBUG } else { - addLog(LOG_LEVEL_INFO, F("PCD8544: No init?")); + addLog(LOG_LEVEL_ERROR, F("PCD8544: No init?")); } if (nullptr != pcd8544) { diff --git a/src/src/PluginStructs/P142_data_struct.cpp b/src/src/PluginStructs/P142_data_struct.cpp index 1bf8b56bad..bfef8d99b2 100644 --- a/src/src/PluginStructs/P142_data_struct.cpp +++ b/src/src/PluginStructs/P142_data_struct.cpp @@ -55,9 +55,11 @@ bool P142_data_struct::init(struct EventStruct *event) { as5600->setOffset(_angleOffset); } + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, concat(F("AS5600: Initialization "), isInitialized() ? F("succeeded") : F("failed"))); } + #endif // ifndef BUILD_NO_DEBUG return isInitialized(); } diff --git a/src/src/PluginStructs/P150_data_struct.cpp b/src/src/PluginStructs/P150_data_struct.cpp index 97fd42d9fe..0ef5141de0 100644 --- a/src/src/PluginStructs/P150_data_struct.cpp +++ b/src/src/PluginStructs/P150_data_struct.cpp @@ -64,6 +64,7 @@ bool P150_data_struct::plugin_read(struct EventStruct *event) { UserVar.setFloat(event->TaskIndex, 0, _finalTempC); UserVar.setFloat(event->TaskIndex, 1, _digitalTempC); + #ifndef BUILD_NO_DEBUG if (_logEnabled && loglevelActiveFor(LOG_LEVEL_INFO)) { String log = strformat(F("TMP117: Temperature: %sC"), formatUserVarNoCheck(event, 0).c_str()); @@ -72,6 +73,7 @@ bool P150_data_struct::plugin_read(struct EventStruct *event) { } addLogMove(LOG_LEVEL_INFO, log); } + #endif // ifndef BUILD_NO_DEBUG if (P150_GET_CONF_CONVERSION_MODE == P150_CONVERSION_ONE_SHOT) { setConfig(); // Start the next one-shot measurement diff --git a/src/src/PluginStructs/P153_data_struct.cpp b/src/src/PluginStructs/P153_data_struct.cpp index 25418dddae..b80a475f8a 100644 --- a/src/src/PluginStructs/P153_data_struct.cpp +++ b/src/src/PluginStructs/P153_data_struct.cpp @@ -170,6 +170,7 @@ bool P153_data_struct::plugin_read(struct EventStruct *event) { } } + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { const String taskName = getTaskDeviceName(event->TaskIndex); addLogMove(LOG_LEVEL_INFO, strformat(F("%s: Temperature: %s"), @@ -180,6 +181,7 @@ bool P153_data_struct::plugin_read(struct EventStruct *event) { taskName.c_str(), formatUserVarNoCheck(event, 1).c_str())); } + #endif // ifndef BUILD_NO_DEBUG } else { UserVar.setFloat(event->TaskIndex, 0, NAN); UserVar.setFloat(event->TaskIndex, 1, NAN); diff --git a/src/src/PluginStructs/P159_data_struct.cpp b/src/src/PluginStructs/P159_data_struct.cpp index d267582941..bf33ded3c9 100644 --- a/src/src/PluginStructs/P159_data_struct.cpp +++ b/src/src/PluginStructs/P159_data_struct.cpp @@ -158,9 +158,11 @@ bool P159_data_struct::processSensor(struct EventStruct *event) { break; } - if (P159_state_e::Running != sState) { // FIXME Remove log + if (P159_state_e::Running != sState) { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, strformat(F("LD2410: Starting state: %d duration: %d msec."), static_cast(sState), timePassedSince(iStart))); + #endif // ifndef BUILD_NO_DEBUG } } // isValid() @@ -322,7 +324,9 @@ bool P159_data_struct::plugin_webform_save(struct EventStruct *event) { const uint16_t idle = getFormItemIntCustomArgName(idx++); const uint8_t gMove = getFormItemIntCustomArgName(idx++); const uint8_t gStat = getFormItemIntCustomArgName(idx++); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("LD2410: Save sensitivity settings to sensor, start...")); + #endif // ifndef BUILD_NO_DEBUG radar->requestConfigurationModeBegin(); radar->setMaxValues(gMove, gStat, idle); const uint16_t maxGate = radar->cfgMaxGate(); diff --git a/src/src/PluginStructs/P162_data_struct.cpp b/src/src/PluginStructs/P162_data_struct.cpp index 9939a63460..6294c35d61 100644 --- a/src/src/PluginStructs/P162_data_struct.cpp +++ b/src/src/PluginStructs/P162_data_struct.cpp @@ -78,7 +78,9 @@ bool P162_data_struct::hw_reset() { digitalWrite(_rstPin, LOW); delayMicroseconds(1); // Reset requires low signal for at least 150 nsec, so 1 microsecond should suffice digitalWrite(_rstPin, HIGH); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("Digipot: Hardware reset applied.")); + #endif // ifndef BUILD_NO_DEBUG return true; } return false; @@ -106,7 +108,9 @@ bool P162_data_struct::plugin_write(struct EventStruct *event, updateUserVars(event); write_pot(event, P162_BOTH_POT_SEL, _pot0_value); // Single command success = true; + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("Digipot: Software reset applied.")); + #endif // ifndef BUILD_NO_DEBUG } } else diff --git a/src/src/PluginStructs/P167_data_struct.cpp b/src/src/PluginStructs/P167_data_struct.cpp index e8e8cdb69f..02ec3bf48d 100644 --- a/src/src/PluginStructs/P167_data_struct.cpp +++ b/src/src/PluginStructs/P167_data_struct.cpp @@ -150,9 +150,11 @@ P167_data_struct::~P167_data_struct() { bool P167_data_struct::setupDevice(uint8_t i2caddr) { _i2caddr = i2caddr; + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("SEN5x: Setup with address= 0x%02x"), _i2caddr)); } + #endif // ifndef BUILD_NO_DEBUG return true; } diff --git a/src/src/PluginStructs/P168_data_struct.cpp b/src/src/PluginStructs/P168_data_struct.cpp index 5d71cf803a..5d22c711ad 100644 --- a/src/src/PluginStructs/P168_data_struct.cpp +++ b/src/src/PluginStructs/P168_data_struct.cpp @@ -38,7 +38,9 @@ bool P168_data_struct::init(struct EventStruct *event) { veml->setPowerSaveMode(_psm_mode); veml->enable(true); + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("VEML : 6030/7700 Initialized.")); + #endif // ifndef BUILD_NO_DEBUG initialized = true; } else { diff --git a/src/src/PluginStructs/P173_data_struct.cpp b/src/src/PluginStructs/P173_data_struct.cpp index f102f6fdd1..b6d911f2a6 100644 --- a/src/src/PluginStructs/P173_data_struct.cpp +++ b/src/src/PluginStructs/P173_data_struct.cpp @@ -62,12 +62,14 @@ bool P173_data_struct::plugin_read(struct EventStruct *event) { success = true; errorCount = 0; + #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("%s: Temperature: %s, Humidity: %s"), getTaskDeviceName(event->TaskIndex).c_str(), formatUserVarNoCheck(event, 0).c_str(), formatUserVarNoCheck(event, 1).c_str())); } + #endif // ifndef BUILD_NO_DEBUG } else { addLog(LOG_LEVEL_ERROR, concat(F("SHTC3: READ CRC Error, data: 0x"), formatToHex_array(data, 6))); errorCount++; diff --git a/src/src/WebServer/AdvancedConfigPage.cpp b/src/src/WebServer/AdvancedConfigPage.cpp index cf5ffa115a..47641f2325 100644 --- a/src/src/WebServer/AdvancedConfigPage.cpp +++ b/src/src/WebServer/AdvancedConfigPage.cpp @@ -10,6 +10,9 @@ #include "../../ESPEasy/net/wifi/ESPEasyWifi.h" +#if FEATURE_RTC_SRAM_STORAGE +#include "../../ESPEasy/eeprom/Helpers/RTCSRAMStorage.h" +#endif // if FEATURE_RTC_SRAM_STORAGE #include "../Globals/ESPEasy_time.h" #include "../Globals/Settings.h" @@ -172,7 +175,8 @@ void handle_advanced() { } } - addHtml(F("
")); + html_add_form(); + html_table_class_normal(); addFormHeader(F("Advanced Settings"), F("RTDTools/Tools.html#advanced")); @@ -200,6 +204,12 @@ void handle_advanced() { if (Settings.ExtTimeSource() != ExtTimeSource_e::None) { addFormNote(concat(getLabel(LabelType::EXT_RTC_UTC_TIME), F(": ")) + getValue(LabelType::EXT_RTC_UTC_TIME)); } + #if FEATURE_RTC_SRAM_STORAGE + if (ESPEasy::eeprom::checkRTCSRAMEnabled()) { + addRowLabel(F("'WriteRTC' slots available")); + addHtmlInt(ESPEasy::eeprom::getRTCSRAMMaxSlots()); + } + #endif // if FEATURE_RTC_SRAM_STORAGE #if FEATURE_I2C_MULTIPLE { const uint8_t i2cBus = Settings.getI2CInterfaceRTC(); @@ -455,14 +465,29 @@ void addFormDstSelect(bool isStart, uint16_t choice) { void addFormExtTimeSourceSelect(const __FlashStringHelper * label, const __FlashStringHelper * id, ExtTimeSource_e choice) { addRowLabel(label); - const __FlashStringHelper * options[] = - { F("None"), F("DS1307"), F("DS3231"), F("PCF8523"), F("PCF8563")}; + const __FlashStringHelper * options[] = { + F("None"), + toString(ExtTimeSource_e::DS1307), + toString(ExtTimeSource_e::DS3231), + toString(ExtTimeSource_e::DS3232), + toString(ExtTimeSource_e::PCF8523), + toString(ExtTimeSource_e::PCF8563), + #if FEATURE_EXT_RTC_PCF8583 + toString(ExtTimeSource_e::PCF8583), + toString(ExtTimeSource_e::PCF8583a), + #endif // if FEATURE_EXT_RTC_PCF8583 + }; constexpr int optionValues[] = { static_cast(ExtTimeSource_e::None), static_cast(ExtTimeSource_e::DS1307), static_cast(ExtTimeSource_e::DS3231), + static_cast(ExtTimeSource_e::DS3232), static_cast(ExtTimeSource_e::PCF8523), - static_cast(ExtTimeSource_e::PCF8563) + static_cast(ExtTimeSource_e::PCF8563), + #if FEATURE_EXT_RTC_PCF8583 + static_cast(ExtTimeSource_e::PCF8583), + static_cast(ExtTimeSource_e::PCF8583a), + #endif // if FEATURE_EXT_RTC_PCF8583 }; const FormSelectorOptions selector(NR_ELEMENTS(optionValues), options, optionValues); diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index c6e737f7d3..10ec077452 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -100,7 +100,11 @@ void handle_controllers() { mqttDiscoveryTimeout = random(10, MQTT_DISCOVERY_MAX_DELAY_0_1_SECONDS); if (loglevelActiveFor(LOG_LEVEL_INFO)) { + #ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, strformat(F("MQTT : Start AutoDiscovery on Save. Starting in %.1f sec."), mqttDiscoveryTimeout / 10)); + #else // ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, strformat(F("MQTT : Discovery starting in %.1f sec."), mqttDiscoveryTimeout / 10)); + #endif // ifndef BUILD_NO_DEBUG } } # endif // if FEATURE_MQTT_DISCOVER diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index d1386ae5dc..c23acb1f02 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -309,22 +309,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 @@ -459,6 +448,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))); @@ -597,11 +587,12 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) for (taskIndex_t x = (page - 1) * TASKS_PER_PAGE; x < ((page) * TASKS_PER_PAGE) && validTaskIndex(x); x++) { const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(x); - const bool pluginID_set = INVALID_PLUGIN_ID != Settings.getPluginID_for_task(x); + const pluginID_t pid = Settings.getPluginID_for_task(x); + const bool pluginID_set = INVALID_PLUGIN_ID != pid; html_TR_TD(); - if (pluginID_set && !supportedPluginID(Settings.getPluginID_for_task(x))) { + if (pluginID_set && !supportedPluginID(pid)) { html_add_button_prefix(F("red"), true); } else { html_add_button_prefix(); @@ -631,7 +622,7 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) addEnabled(Settings.TaskDeviceEnabled[x] && validDeviceIndex(DeviceIndex)); html_TD(); - addHtml(getPluginNameFromPluginID(Settings.getPluginID_for_task(x))); + addHtml(getPluginNameFromPluginID(pid)); html_TD(); addHtml(getTaskDeviceName(x)); html_TD(); @@ -867,7 +858,7 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) for (uint8_t varNr = 0; varNr < valueCount; varNr++) { - if (validPluginID_fullcheck(Settings.getPluginID_for_task(x))) + if (validPluginID_fullcheck(pid)) { # if FEATURE_TASKVALUE_UNIT_OF_MEASURE const uint8_t uomIndex = Cache.getTaskVarUnitOfMeasure(x, varNr); @@ -1112,11 +1103,13 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) addHtml(F("Device:")); + const pluginID_t pid = Settings.getPluginID_for_task(taskIndex); + // no (supported) device selected, this effectively checks for validDeviceIndex - if (!supportedPluginID(Settings.getPluginID_for_task(taskIndex))) + if (!supportedPluginID(pid)) { // takes lots of memory/time so call this only when needed. - addDeviceSelect(F("TDNUM"), Settings.getPluginID_for_task(taskIndex)); // ="taskdevicenumber" + addDeviceSelect(F("TDNUM"), pid); // ="taskdevicenumber" addFormSeparator(4); } @@ -1129,18 +1122,16 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) addHtml(F("'); // show selected device name and delete button addHtml(getPluginNameFromDeviceIndex(DeviceIndex)); - const uint8_t pid = Settings.getPluginID_for_task(taskIndex).value; - - if (pid <= 79) { // Up to P079 seem to be listed in the old Wiki (and a few incomplete pages), so lets keep pointing there too - addHelpButton(concat(F("Plugin"), Settings.getPluginID_for_task(taskIndex).value)); + if (pid.value <= 79) { // Up to P079 seem to be listed in the old Wiki (and a few incomplete pages), so lets keep pointing there too + addHelpButton(concat(F("Plugin"), pid.value)); } - addRTDPluginButton(Settings.getPluginID_for_task(taskIndex)); + addRTDPluginButton(pid); addFormTextBox(F("Name"), F("TDN"), getTaskDeviceName(taskIndex), NAME_FORMULA_LENGTH_MAX); // ="taskdevicename" @@ -1491,10 +1482,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"), @@ -1502,8 +1499,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++; } @@ -1515,10 +1512,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); @@ -1531,11 +1528,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]; @@ -1559,9 +1555,25 @@ 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 +#endif // if FEATURE_I2C void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { 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 8f20a3d743..f01a075bbf 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" @@ -363,6 +364,9 @@ void WebServerInit() #ifdef WEBSERVER_SYSVARS web_server.on(F("/sysvars"), handle_sysvars); #endif // WEBSERVER_SYSVARS +#if FEATURE_EEPROM_EXTERNAL || FEATURE_RTC_SRAM_STORAGE + web_server.on(F("/eepromvars"), handle_eepromvars); +#endif // if FEATURE_EEPROM_EXTERNAL || FEATURE_RTC_SRAM_STORAGE #if FEATURE_PLUGIN_LIST web_server.on(F("/pluginlist"), handle_pluginlist); #endif // if FEATURE_PLUGIN_LIST diff --git a/src/src/WebServer/EepromVarPage.cpp b/src/src/WebServer/EepromVarPage.cpp new file mode 100644 index 0000000000..059570c121 --- /dev/null +++ b/src/src/WebServer/EepromVarPage.cpp @@ -0,0 +1,171 @@ +#include "../WebServer/EepromVarPage.h" + +#if FEATURE_EEPROM_EXTERNAL || FEATURE_RTC_SRAM_STORAGE + +# 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" + +# if FEATURE_EEPROM_EXTERNAL +# include "../../ESPEasy/eeprom/Helpers/EEPROMExternal.h" +# endif // if FEATURE_EEPROM_EXTERNAL +# if FEATURE_RTC_SRAM_STORAGE +# include "../../ESPEasy/eeprom/Helpers/RTCSRAMStorage.h" +# endif // if FEATURE_RTC_SRAM_STORAGE +# include "../Helpers/ESPEasy_Storage.h" + +# include "../Helpers/StringConverter.h" + +void handle_eepromvars() { + if (!isLoggedIn()) { return; } + + if (!startStream_send_stdTemplate(MENU_INDEX_TOOLS)) { return; } + + # if FEATURE_EEPROM_EXTERNAL + + 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 ESPEASY_RULES_FLOAT_TYPE 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, ESPEASY_DOUBLE_NR_DECIMALS, true)); + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + addHtml(toString(value, ESPEASY_FLOAT_NR_DECIMALS, true)); + # 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.
")); + } + # endif // if FEATURE_EEPROM_EXTERNAL + # if FEATURE_RTC_SRAM_STORAGE + + if (ESPEasy::eeprom::checkRTCSRAMEnabled() > 0) { + // the table header + html_table_class_normal(); + html_TR(); + html_table_header(F("External RTC SRAM"), + 300); + html_table_header(toString(Settings.ExtTimeSource()), + 500); + html_table_header(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::getRTCSRAMMaxSlots(); + uint32_t count{}; + + for (uint32_t slot = 0; slot < maxSlots; ++slot) { + const ESPEASY_RULES_FLOAT_TYPE value = ESPEasy::eeprom::readRTCSRAMSlot(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, ESPEASY_DOUBLE_NR_DECIMALS, true)); + # else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + addHtml(toString(value, ESPEASY_FLOAT_NR_DECIMALS, true)); + # 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 RTC SRAM not available.")); + } + # endif // if FEATURE_RTC_SRAM_STORAGE + html_end_form(); + sendTail_stdtemplate(); +} + +#endif // if FEATURE_EEPROM_EXTERNAL || FEATURE_RTC_SRAM_STORAGE diff --git a/src/src/WebServer/EepromVarPage.h b/src/src/WebServer/EepromVarPage.h new file mode 100644 index 0000000000..366b3a5bac --- /dev/null +++ b/src/src/WebServer/EepromVarPage.h @@ -0,0 +1,8 @@ +#pragma once + +# include "../WebServer/common.h" +#if FEATURE_EEPROM_EXTERNAL || FEATURE_RTC_SRAM_STORAGE + +void handle_eepromvars(); + +#endif // if FEATURE_EEPROM_EXTERNAL || FEATURE_RTC_SRAM_STORAGE diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index df42a0c372..dc6e93ac83 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 // ******************************************************************************** @@ -41,6 +46,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)); @@ -64,9 +97,10 @@ void handle_hardware() { addHtmlError(error); } - addHtml(F("")); + html_add_form(); + html_table_class_normal(); - addFormHeader(F("Hardware Settings"), F(""), F("Hardware/Hardware.html")); + addFormHeader(F("Hardware Settings"), F("RTDHardware/Hardware.html")); addFormSubHeader(F("Wifi Status LED")); addFormPinSelect(PinSelectPurpose::Status_led, formatGpioName_output(F("LED")), F("pled"), Settings.Pin_status_led); @@ -95,6 +129,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 9238823df3..c9e9f10577 100644 --- a/src/src/WebServer/I2C_Scanner.cpp +++ b/src/src/WebServer/I2C_Scanner.cpp @@ -312,19 +312,25 @@ String getKnownI2Cdevice(uint8_t address) { case 0x4D: result += F("PCF8591,MCP3221,LM75A,INA219"); break; + case 0x50: + result += F("PCF8583,AT24Cxx,MB85RCxx"); + break; + case 0x52: + result += F("AT24Cxx,MB85RCxx"); + break; case 0x51: - result += F("PCF8563"); + result += F("PCF8563,PCF8583,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"); @@ -397,6 +403,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/InterfacesPage.cpp b/src/src/WebServer/InterfacesPage.cpp index abdc7c9277..25c0afc287 100644 --- a/src/src/WebServer/InterfacesPage.cpp +++ b/src/src/WebServer/InterfacesPage.cpp @@ -99,7 +99,8 @@ void handle_interfaces() { save_interfaces(); - addHtml(F("")); + html_add_form(); + html_table_class_normal(); addFormFixedFirstColumn(); // This must be added as the first element in a table definition addFormHeader(strformat(F("%s Interfaces Settings"), FsP(getGpMenuIcon(navMenuIndex))), F(""), F("Interfaces/Interfaces.html")); diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 1cbb01bb5a..7cd1546e99 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -558,6 +558,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/Rules.cpp b/src/src/WebServer/Rules.cpp index ac30ce099a..97f18b1a3f 100644 --- a/src/src/WebServer/Rules.cpp +++ b/src/src/WebServer/Rules.cpp @@ -461,7 +461,7 @@ bool handle_rules_edit(String originalUri, bool isAddNew) { if (f) { - addLog(LOG_LEVEL_INFO, String(F(" Write to file: ")) + fileName); + addLog(LOG_LEVEL_INFO, concat(F(" Write to file: "), fileName)); f.print(rules); f.close(); } diff --git a/src/src/WebServer/SetupPage.cpp b/src/src/WebServer/SetupPage.cpp index 9d9a4316f4..a4a1b1bbb7 100644 --- a/src/src/WebServer/SetupPage.cpp +++ b/src/src/WebServer/SetupPage.cpp @@ -145,9 +145,7 @@ void handle_setup() { # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String reconnectlog = F("WIFI : Credentials Changed, retry connection. SSID: "); - reconnectlog += ssid; - addLogMove(LOG_LEVEL_INFO, reconnectlog); + addLogMove(LOG_LEVEL_INFO, concat(F("WIFI : Credentials Changed, retry connection. SSID: "), ssid)); } # endif // ifndef BUILD_NO_DEBUG status = HANDLE_SETUP_CONNECTING_STAGE; diff --git a/src/src/WebServer/ToolsPage.cpp b/src/src/WebServer/ToolsPage.cpp index 5bab88ce5d..aec05350bb 100644 --- a/src/src/WebServer/ToolsPage.cpp +++ b/src/src/WebServer/ToolsPage.cpp @@ -86,6 +86,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 || FEATURE_RTC_SRAM_STORAGE + addWideButtonPlusDescription(F("eepromvars"), F("External EEPROM/RTC values"), F("Show all values stored in the external EEPROM or RTC SRAM")); + #endif // if FEATURE_EEPROM_EXTERNAL || FEATURE_RTC_SRAM_STORAGE + #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 diff --git a/static/espeasy.js b/static/espeasy.js index 3bf7a53d44..4994711e84 100644 --- a/static/espeasy.js +++ b/static/espeasy.js @@ -11,10 +11,12 @@ var commonCommands = ["AccessInfo", "Background", "Build", "ClearAccessBlock", " "TaskDisable", "TaskEnable", "TaskRun", "TaskValueSet", "TaskValueSetAndRun", "TaskValueSetDerived", "TaskValueSetPresentation", "TimerPause", "TimerResume", "TimerSet", "TimerSet_ms", "TimeZone", "UdpPort", "UdpTest", "Unit", "UseNTP", "WdConfig", "WdRead", "WiFi", "WiFiAllowAP", "WiFiAPMode", "WiFiConnect", "WiFiDisconnect", "WiFiKey", "WiFiKey2", "WiFiMode", "WiFiScan", "WiFiSSID", "WiFiSSID2", "WiFiSTAMode", + "WriteEE", "WriteRTC", "Event", "AsyncEvent", "GPIO", "GPIOToggle", "LongPulse", "LongPulse_mS", "Monitor", "Pulse", "PWM", "Servo", "Status", "Tone", "RTTTL", "UnMonitor", "Provision", "Provision,Config", "Provision,Security", "Provision,Notification", "Provision,Provision", "Provision,Rules", "Provision,CustomCdnUrl", "Provision,Firmware"]; var commonEvents = ["Clock#Time", "JsonReply", "JsonReply#", "Login#Failed", "MQTT#Connected", "MQTT#Disconnected", "MQTTimport#Connected", "MQTTimport#Disconnected","OpenMeteo#current","OpenMeteo#daily", "OpenMeteo#hourly", "Rules#Timer", "System#Boot", + "ReadEE#", "ReadRTC#", "System#BootMode", "System#Sleep", "System#Wake", "TaskExit#", "TaskInit#", "ThingspeakReply", "Time#Initialized", "Time#Set", "WiFi#APmodeDisabled", "WiFi#APmodeEnabled", "WiFi#ChangedAccesspoint", "WiFi#ChangedWiFichannel", "WiFi#Connected", "WiFi#Disconnected"]; var commonPlugins = [ diff --git a/static/espeasy.min.js b/static/espeasy.min.js index 63a16731ae..dc0e22f27c 100644 --- a/static/espeasy.min.js +++ b/static/espeasy.min.js @@ -1 +1 @@ -var commonAtoms=["And","Or"],commonKeywords=["If","Else","Elseif","Endif"],commonCommands=["AccessInfo","Background","Build","ClearAccessBlock","ClearRTCam","Config","ControllerDisable","ControllerEnable","DateTime","Debug","Dec","DeepSleep","DisablePriorityTask","DNS","DST","EraseSDKWiFi","ExecuteRules","FactoryReset","Gateway","I2Cscanner","Inc","IP","Latitude","Let","LetStr","Load","LogEntry","LogPortStatus","Longitude","LoopTimerSet","LoopTimerSet_ms","LoopTimerSetAndRun","LoopTimerSetAndRun_ms","MemInfo","MemInfoDetail","Name","NetworkDisable","NetworkEnable","Password","PostToHTTP","PostToHTTPS","Publish","PublishR","PutToHTTP","PutToHTTPS","Reboot","Save","SendTo","SendToHTTP","SendToHTTPS","SendToUDP","SendToUDPMix","Settings","Subnet","Subscribe","TaskClear","TaskClearAll","TaskDisable","TaskEnable","TaskRun","TaskValueSet","TaskValueSetAndRun","TaskValueSetDerived","TaskValueSetPresentation","TimerPause","TimerResume","TimerSet","TimerSet_ms","TimeZone","UdpPort","UdpTest","Unit","UseNTP","WdConfig","WdRead","WiFi","WiFiAllowAP","WiFiAPMode","WiFiConnect","WiFiDisconnect","WiFiKey","WiFiKey2","WiFiMode","WiFiScan","WiFiSSID","WiFiSSID2","WiFiSTAMode","Event","AsyncEvent","GPIO","GPIOToggle","LongPulse","LongPulse_mS","Monitor","Pulse","PWM","Servo","Status","Tone","RTTTL","UnMonitor","Provision","Provision,Config","Provision,Security","Provision,Notification","Provision,Provision","Provision,Rules","Provision,CustomCdnUrl","Provision,Firmware"],commonEvents=["Clock#Time","JsonReply","JsonReply#","Login#Failed","MQTT#Connected","MQTT#Disconnected","MQTTimport#Connected","MQTTimport#Disconnected","OpenMeteo#current","OpenMeteo#daily","OpenMeteo#hourly","Rules#Timer","System#Boot","System#BootMode","System#Sleep","System#Wake","TaskExit#","TaskInit#","ThingspeakReply","Time#Initialized","Time#Set","WiFi#APmodeDisabled","WiFi#APmodeEnabled","WiFi#ChangedAccesspoint","WiFi#ChangedWiFichannel","WiFi#Connected","WiFi#Disconnected"],commonPlugins=["ResetPulseCounter","SetPulseCounterTotal","LogPulseStatistic","analogout","MCPGPIO","MCPGPIOToggle","MCPLongPulse","MCPLongPulse_ms","MCPPulse","Status,MCP","Monitor,MCP","MonitorRange,MCP","UnMonitorRange,MCP","UnMonitor,MCP","MCPGPIORange","MCPGPIOPattern","MCPMode","MCPModeRange","ExtGpio","ExtPwm","ExtPulse","ExtLongPulse","Status,EXT,","LCDCmd","LCD","PCFGPIO","PCFGPIOToggle","PCFLongPulse","PCFLongPulse_ms","PCFPulse","Status,PCF","Monitor,PCF","MonitorRange,PCF","UnMonitorRange,PCF","UnMonitor,PCF","PCFGPIORange","PCFGPIOpattern","PCFMode","PCFmodeRange","SerialSend","SerialSendMix","Ser2NetClientSend","SerialSend_test","pcapwm","pcafrq","mode2","OLED","OLEDCMD","OLEDCMD,on","OLEDCMD,off","OLEDCMD,clear","IRSEND","IRSENDAC","OledFramedCmd","OledFramedCmd,Display","OledFramedCmd,low","OledFramedCmd,med","OledFramedCmd,high","OledFramedCmd,Frame","OledFramedCmd,linecount","OledFramedCmd,leftalign","OledFramedCmd,align","OledFramedCmd,userDef1","OledFramedCmd,userDef2","NeoPixel","NeoPixelAll","NeoPixelLine","NeoPixelHSV","NeoPixelAllHSV","NeoPixelLineHSV","NeoPixelBright","MotorShieldCmd,DCMotor","MotorShieldCmd,Stepper","MHZCalibrateZero","MHZReset","MHZABCEnable","MHZABCDisable","Sensair_SetRelay","PMSX003","PMSX003,Wake","PMSX003,Sleep","PMSX003,Reset","encwrite","Play","Vol","Eq","Mode","Repeat","tareChanA","tareChanB","7dn","7dst","7dsd","7dtext","7ddt","7dt","7dtfont","7dtbin","7don","7doff","7output","HLWCalibrate","HLWReset","csecalibrate","cseclearpulses","csereset","WemosMotorShieldCMD","LolinMotorShieldCMD","GPS","GPS,Sleep","GPS,Wake","GPS#GotFix","GPS#LostFix","GPS#Travelled","homieValueSet","SerialProxy_Write","SerialProxy_WriteMix","SerialProxy_Test","HeatPumpir","MitsubishiHP","MitsubishiHP,temperature","MitsubishiHP,power","MitsubishiHP,mode","MitsubishiHP,fan","MitsubishiHP,vane","MitsubishiHP,widevane","Culreader_Write","Touch","Touch,Rot","Touch,Flip","Touch,Enable","Touch,Disable","Touch,On","Touch,Off","Touch,Toggle","Touch,Setgrp","Touch,Incgrp","Touch,Decgrp","Touch,Incpage","Touch,Decpage","Touch,Updatebutton","WakeOnLan","DotMatrix","DotMatrix,clear","DotMatrix,update","DotMatrix,size","DotMatrix,txt","DotMatrix,settxt","DotMatrix,content","DotMatrix,alignment","DotMatrix,anim.in","DotMatrix,anim.out","DotMatrix,speed","DotMatrix,pause","DotMatrix,font","DotMatrix,layout","DotMatrix,inverted","DotMatrix,specialeffect","DotMatrix,offset","DotMatrix,brightness","DotMatrix,repeat","DotMatrix,setbar","DotMatrix,bar","Thermo","Thermo,Up","Thermo,Down","Thermo,Mode","Thermo,ModeBtn","Thermo,Setpoint","Max1704xclearalert","scdgetabc","scdgetalt","scdgettmp","scdsetcalibration","scdsetfrc","scdgetinterval","multirelay","multirelay,on","multirelay,off","multirelay,set","multirelay,get","multirelay,loop","ShiftOut","ShiftOut,Set","ShiftOut,SetNoUpdate","ShiftOut,Update","ShiftOut,SetAll","ShiftOut,SetAllNoUpdate","ShiftOut,SetAllLow","ShiftOut,SetAllHigh","ShiftOut,SetChipCount","ShiftOut,SetHexBin","cdmrst","nfx","nfx,off","nfx,on","nfx,dim","nfx,line,","nfx,hsvline,","nfx,one,","nfx,hsvone,","nfx,all,","nfx,rgb,","nfx,fade,","nfx,hsv,","nfx,colorfade,","nfx,rainbow","nfx,kitt,","nfx,comet,","nfx,theatre,","nfx,scan,","nfx,dualscan,","nfx,twinkle,","nfx,twinklefade,","nfx,sparkle,","nfx,wipe,","nfx,dualwipe","nfx,fire","nfx,fireflicker","nfx,faketv","nfx,simpleclock","nfx,stop","nfx,statusrequest","nfx,fadetime,","nfx,fadedelay,","nfx,speed,","nfx,count,","nfx,bgcolor","ShiftIn","ShiftIn,PinEvent","ShiftIn,ChipEvent","ShiftIn,SetChipCount","ShiftIn,SampleFrequency","ShiftIn,EventPerPin","scd4x","scd4x,storesettings","scd4x,facoryreset","scd4x,selftest","scd4x,setfrc,","axp","axp,ldo2","axp,ldo3","axp,ldoio","axp,gpio0","axp,gpio1","axp,gpio2","axp,gpio3","axp,gpio4","axp,dcdc2","axp,dcdc3","axp,ldo2map","axp,ldo3map","axp,ldoiomap","axp,dcdc2map","axp,dcdc3map","axp,ldo2perc","axp,ldo3perc","axp,ldoioperc","axp,dcdc2perc","axp,dcdc3perc","I2CEncoder","I2CEncoder,bright","I2CEncoder,led1","I2CEncoder,led2","I2CEncoder,gain","I2CEncoder,set","cachereader","cachereader,readpos","cachereader,sendtaskinfo","cachereader,flush","tm1621","tm1621,write,","tm1621,writerow,","tm1621,voltamp,","tm1621,energy,","tm1621,celcius,","tm1621,fahrenheit,","tm1621,humidity,","tm1621,raw,","dac","dac,1","dac,2","sht4x","sht4x,startup","ld2410","ld2410,factoryreset","ld2410,logall","digipot","digipot,reset","digipot,shutdown","digipot,","7dextra","7dbefore","7dgroup","7digit","7color","7digitcolor","7groupcolor","gp8403","gp8403,volt,","gp8403,mvolt,","gp8403,range,","gp8403,preset,","gp8403,init,","sen5x","sen5x,startclean","sen5x,techlog,","as3935","as3935,clearstats","as3935,calibrate","as3935,setgain,","as3935,setnf,","as3935,setwd,","as3925,setsrej,","lu9685","lu9685,servo,","lu9685,enable,","lu9685,disable,","lu9685,setrange,","geni2c","geni2c,cmd,","geni2c,exec,","geni2c,log,"],pluginDispKind=["tft","ili9341","ili9342","ili9481","ili9486","ili9488","epd","eink","epaper","il3897","uc8151d","ssd1680","ws2in7","ws1in54","st77xx","st7735","st7789","st7796","neomatrix","neo","pcd8544"],pluginDispCmd=["cmd,on","cmd,off","cmd,clear","cmd,backlight","cmd,bright","cmd,deepsleep","cmd,seq_start","cmd,seq_end","cmd,inv","cmd,rot",",clear",",rot",",tpm",",txt",",txp",",txz",",txc",",txs",",txtfull",",asciitable",",font",",l",",lh",",lv",",lm",",lmr",",r",",rf",",c",",cf",",rf",",t",",tf",",rr",",rrf",",px",",pxh",",pxv",",bmp",",btn",",win",",defwin",",delwin"],commonTag=["On","Do","Endon"],commonNumber=["toBin","toHex","Constrain","XOR","AND:","OR:","Ord","bitRead","bitSet","bitClear","bitWrite","urlencode"],commonMath=["Log","Ln","Abs","Exp","Sqrt","Sq","Round","Sin","Cos","Tan","aSin","aCos","aTan","aTan2","Sin_d","Cos_d","Tan_d","aSin_d","aCos_d","aTan_d","aTan2_d","map","mapc","fmod"],commonWarning=["delay","Delay","ResetFlashWriteCounter"],taskSpecifics=["settings.Enabled","settings.Interval","settings.ValueCount","settings.Controller1.Enabled","settings.Controller2.Enabled","settings.Controller3.Enabled","settings.Controller1.Idx","settings.Controller2.Idx","settings.Controller3.Idx"],AnythingElse=["%eventvalue%","%eventpar%","%eventname%","%sysname%","%bootcause%","%systime%","%systm_hm%","%systm_hm_0%","%systm_hm_sp%","%systime_am%","%systime_am_0%","%systime_am_sp%","%systm_hm_am%","%systm_hm_am_0%","%systm_hm_am_sp%","%lcltime%","%sunrise%","%s_sunrise%","%m_sunrise%","%sunset%","%s_sunset%","%m_sunset%","%lcltime_am%","%latitude%","%longitude%","%syshour%","%syshour_0%","%sysmin%","%sysmin_0%","%syssec%","%syssec_0%","%sysday%","%sysday_0%","%sysmonth%","%sysmonth_0%","%systzoffset%","%systzoffset_s%","%sysyear%","%sysyear_0%","%sysyears%","%sysweekday%","%sysweekday_s%","%unixtime%","%unixtime_lcl%","%uptime%","%uptime_ms%","%rssi%","%ip%","%unit%","%unit_0%","%ssid%","%bssid%","%wi_ch%","%iswifi%","%vcc%","%mac%","%mac_int%","%isntp%","%ismqtt%","%dns%","%dns1%","%dns2%","%flash_freq%","%flash_size%","%flash_chip_vendor%","%flash_chip_model%","%fs_free%","%fs_size%","%cpu_id%","%cpu_freq%","%cpu_model%","%cpu_rev%","%cpu_cores%","%board_name%","%inttemp%","%islimited_build%","%isvar_double%","substring","lookup","indexOf","indexOf_ci","equals","equals_ci","strtol","timeToMin","timeToSec","%ethwifimode%","%ethconnected%","%ethduplex%","%ethspeed%","%ethstate%","%ethspeedstate%","%c_w_dir%","%c_c2f%","%c_ms2Bft%","%c_dew_th%","%c_alt_pres_sea%","%c_sea_pres_alt%","%c_cm2imp%","%c_isnum%","%c_mm2imp%","%c_m2day%","%c_m2dh%","%c_m2dhm%","%c_s2dhms%","%c_ts2date%","%c_ts2isodate%","%c_ts2wday%","%c_random%","%c_2hex%","%c_u2ip%","%c_uname%","%c_uage%","%c_ubuild%","%c_ubuildstr%","%c_uload%","%c_utype%","%c_utypestr%","%c_strf%","%c_d2r%","%c_r2d%","%SP%","%CR%","%LF%","%N%","%R%","%e%","%pi%","var","int","str","length"];for(const e of pluginDispKind)commonPlugins=commonPlugins.concat(e);for(const e of pluginDispKind)for(const t of pluginDispCmd){let n=e+t;commonPlugins=commonPlugins.concat(n)}var rEdit,EXTRAWORDS=commonAtoms.concat(commonPlugins,commonKeywords,commonCommands,commonEvents,commonTag,commonNumber,commonMath,commonWarning,taskSpecifics,AnythingElse),confirmR=!0,android=/Android/.test(navigator.userAgent);function initCM(){function e(e){}android&&(confirmR=!!confirm("Do you want to enable colored rules on your Android device?\nThis feature hasn't been fully tested yet and may still have some issues.\nIt is currently expected to work with Chrome, Firefox, and Vivaldi.\nPlease report any problems you encounter.")),confirmR&&(CodeMirror.commands.autocomplete=function(e){e.showHint({hint:CodeMirror.hint.anyword})},(rEdit=CodeMirror.fromTextArea(document.getElementById("rules"),{tabSize:2,indentWithTabs:!1,lineNumbers:!0,autoCloseBrackets:!0,extraKeys:{"Ctrl-Space":"autocomplete",Tab:e=>{"null"===e.getMode().name?e.execCommand("insertTab"):e.somethingSelected()?e.execCommand("indentMore"):e.execCommand("insertSoftTab")},"Shift-Tab":e=>e.execCommand("indentLess")}})).on("change",(function(){rEdit.save()})),android||rEdit.on("inputRead",(function(e,t){var n=e.getCursor(),o=e.getTokenAt(n);/[\w%,.]/.test(t.text)&&"comment"!=o.type&&e.showHint({completeSingle:!1})})),CodeMirror.keyMap.default["Ctrl-F"]=function(e){openFind()},CodeMirror.keyMap.default["Cmd-F"]=function(e){openFind()},CodeMirror.keyMap.default["Ctrl-G"]=e,CodeMirror.keyMap.default["Cmd-G"]=e,CodeMirror.keyMap.default["Shift-Ctrl-G"]=e,CodeMirror.keyMap.default["Shift-Cmd-G"]=e,CodeMirror.keyMap.default["Ctrl-H"]=e,CodeMirror.keyMap.default["Cmd-H"]=e,CodeMirror.keyMap.default["Shift-Ctrl-F"]=e,CodeMirror.keyMap.default["Shift-Cmd-F"]=e,CodeMirror.keyMap.default["Ctrl-Shift-R"]=e,CodeMirror.keyMap.default["Cmd-Shift-R"]=e)}function closeSearchDialog(){const e=document.querySelectorAll(".CodeMirror-dialog");e.length>0&&(e.forEach((e=>e.remove())),document.body.classList.remove("dialog-opened")),rEdit.execCommand("clearSearch")}function removeHighlight(){requestAnimationFrame((()=>{document.querySelectorAll(".search-next-highlight").forEach((e=>e.classList.remove("search-next-highlight")))}))}let findDialogObserver=null;function openFind(){findDialogObserver&&(document.querySelectorAll(".CodeMirror-dialog").forEach((e=>e.remove())),findDialogObserver.disconnect(),findDialogObserver=null),findDialogObserver=new MutationObserver((()=>{document.querySelector(".CodeMirror-dialog")||(removeHighlight(),findDialogObserver.disconnect(),findDialogObserver=null)})),findDialogObserver.observe(document.body,{childList:!0,subtree:!0}),clearSearchNextHighlight(rEdit),rEdit.execCommand("findPersistent"),addFindButtons()}function clearSearchNextHighlight(e){removeHighlight(),e.__searchNextHighlight&&(e.__searchNextHighlight.clear(),e.__searchNextHighlight=null)}function addFindButtons(){document.querySelector(".CodeMirror-selected");const e=document.querySelector(".CodeMirror-dialog");if(!e||e.querySelector(".search-button-group"))return;[{title:"Find Previous",symbol:"▲",action:()=>rEdit.execCommand("findPersistentPrev")},{title:"Find Next",symbol:"▼",action:()=>rEdit.execCommand("findPersistentNext")},{title:"Replace",symbol:"Replace",action:()=>{closeSearchDialog(),rEdit.execCommand("replace"),addFindButtons()}},{title:"Close",symbol:"❌",action:closeSearchDialog},{title:"Help",symbol:"?",action:()=>{alert("Available shortcuts:\n• Ctrl+F / Cmd+F: Open search\n• Enter: Find next\n• Shift+Enter: Find previous\n• Use /re/ syntax for regex search")}}].forEach((({title:t,symbol:n,action:o})=>{const i=document.createElement("span");i.title=t,i.className="help"===t.toLowerCase()?"button help":"button",i.innerHTML=n,i.style.cssText="\n cursor: pointer;\n user-select: none;\n ",i.addEventListener("click",(e=>{e.preventDefault(),o()})),e.appendChild(i)}))}function triggerFormatting(){let e,t,n,o,i;if(confirmR){const r=rEdit.getDoc();e=rEdit.getScrollInfo(),t=r.getCursor(),n=0===t.ch?t.line-1:t.line,o=rEdit.getLine(n)||"",i=rEdit.getValue()}else i=document.getElementById("rules").value;if(i=initalAutocorrection(i),i=formatLogic(i),confirmR){rEdit.setValue(i);const r=n,s=0===t.ch&&o.length>0?o.length:t.ch;rEdit.setCursor({line:r,ch:s}),setTimeout((()=>{rEdit.scrollTo(e.left,e.top),rEdit.focus()}),0),rEdit.save()}else document.getElementById("rules").value=i}function initalAutocorrection(e){for(const t of EXTRAWORDS)if("Do"===t){const t=/(^|\s)(do)(\s*)(\/\/.*)?$/gim;e=e.replace(t,((e,t,n,o,i)=>`${t}Do${o}${i??""}`))}else{const n=new RegExp(`^\\s*\\b${t}\\b`,"gmi");e=e.replace(n,(e=>e.replace(new RegExp(t,"i"),t)))}return e}function formatLogic(e){const t=" ",n=e.split("\n").map((e=>{const t=e.trimStart();return t.startsWith("//")?e:t})),o=[],i=[];let r=!1,s=null,a=[],l=[];function c(e){return e.trim().startsWith("//")}function d(e){return""===e.trim()}function m(e){return e.trim().toLowerCase().startsWith("on")}function u(e){return e.trim().toLowerCase().endsWith("do")}function f(e){return"endon"===e.trim().toLowerCase()}function h(e){return e.trim().toLowerCase().startsWith("if")}function p(e){return"else"===e.trim().toLowerCase()}function g(e){return e.trim().toLowerCase().startsWith("elseif")}function C(e){return"endif"===e.trim().toLowerCase()}let x=0;function S(){a.length>0&&(i.push(`• Missing ${a.length} Endif(s):`),i.push(` - Unclosed If block(s) starting at line(s): ${l.join(", ")}`)),a=[],l=[]}for(let e=0;e0){const e=extractFirstErrorLine(i);if(alert("Errors found:\n"+i.join("\n")),!isNaN(e))if(confirmR)setTimeout((()=>{jumpToLine(e)}),50);else{const t=document.getElementById("rules");setTimeout((()=>{jumpToLineInTextarea(t,e)}),50)}}return o.join("\n")}function jumpToLine(e){const t=Math.max(0,e-1);rEdit.setCursor({line:t,ch:0}),rEdit.focus(),rEdit.scrollIntoView({line:t,ch:0},100)}function extractFirstErrorLine(e){for(const t of e){let e=t.match(/• Line (\d+)/);if(e)return parseInt(e[1]);if(e=t.match(/starting at line (\d+)/),e)return parseInt(e[1]);if(e=t.match(/starting at line\(s\):\s*(\d+)/),e)return parseInt(e[1])}return null}function jumpToLineInTextarea(e,t){const n=e.value.split("\n"),o=Math.max(1,Math.min(t,n.length));let i=0;for(let e=0;e{const e=document.getElementById("rulesselect");if(e){if(confirmR){const t=document.createElement("button");t.type="button",t.id="searchBtn",t.innerHTML="🔎︎",t.style.padding="2px 5px",t.className="button help",e.appendChild(t),t.addEventListener("click",(()=>{void 0!==rEdit&&openFind()}))}const t=document.createElement("button");t.type="button",t.id="formatBtn",t.textContent="Format",t.className="button",e.appendChild(t),t.addEventListener("click",(()=>{triggerFormatting()}))}let t="";if(document.addEventListener("keydown",(function(e){const n=e.key;(["Backspace","Delete","ArrowLeft","ArrowRight","ArrowUp","ArrowDown","Enter","Tab","Escape","Shift","Control","Alt","Meta"].includes(n)||1!==n.length)&&(t="")})),android){var n=!1;rEdit.on("keydown",((e,o)=>{["Enter","Backspace"," "].includes(o.key)&&(t=""),n=!0}));let e="";const i=rEdit.getInputField();function o(o,i=!1){if(!(rEdit.hasFocus()&&rEdit&&o.data&&n))return;n=!1;const r=o.data,s=rEdit.getDoc(),a=s.getCursor(),l=rEdit.getTokenAt(a);if(" "===r)return t="",void(e="");if(!(r===e&&t.length>0))if(/[\w%,.]/.test(r)&&"comment"!==l.type){const n=a.ch<=1?r.slice(-1):r;t+=n,e=t,t.startsWith(String(a.line+1))&&0===a.ch&&(t=t.slice(String(a.line).length));const o={line:a.line,ch:a.ch-t.length+1},l=()=>{s.replaceRange(t,o,a),rEdit.setCursor({line:o.line,ch:o.ch+t.length}),rEdit.showHint({completeSingle:!1})};i?l():setTimeout(l,0)}else t=""}const r=navigator.userAgent.toLowerCase(),s=/firefox/.test(r),a=/chrome/.test(r)&&!s;s?i.addEventListener("beforeinput",(e=>{e.preventDefault(),o(e,!0)})):a&&document.addEventListener("input",(e=>{o(e,!1)})),rEdit.on("endCompletion",(function(){setTimeout((()=>{!function(){const e=document.createElement("input");e.type="text",e.style.position="absolute",e.style.opacity="0",e.style.height="0",e.style.width="0",e.style.border="none",e.style.top="0",e.style.left="-9999",e.style.padding="0",e.style.zIndex="-1",e.style.fontSize="16px",document.body.appendChild(e),e.focus(),setTimeout((()=>{e.remove(),rEdit.focus()}),10)}()}),100)}))}})),function(e){"object"==typeof exports&&"object"==typeof module?e(require("codemirror")):"function"==typeof define&&define.amd?define(["codemirror"],e):e(CodeMirror)}((function(e){"use strict";e.defineMode("espeasy",(function(){var e={};function t(t,n){for(var o=0;oe.toLowerCase()));commonCommands=commonCommands.concat(n);var o=commonEvents.map((e=>e.toLowerCase()));commonEvents=commonEvents.concat(o);var i=commonPlugins.map((e=>e.toLowerCase()));commonPlugins=commonPlugins.concat(i);var r=commonAtoms.map((e=>e.toLowerCase()));commonAtoms=commonAtoms.concat(r);var s=commonKeywords.map((e=>e.toLowerCase()));commonKeywords=commonKeywords.concat(s);var a=commonTag.map((e=>e.toLowerCase()));commonTag=commonTag.concat(a);var l=commonNumber.map((e=>e.toLowerCase()));commonNumber=commonNumber.concat(l);var c=commonMath.map((e=>e.toLowerCase()));commonMath=commonMath.concat(c);var d=AnythingElse.map((e=>e.toLowerCase()));AnythingElse=AnythingElse.concat(d);var m=taskSpecifics.map((e=>e.toLowerCase()));function u(t,n){if(t.eatSpace())return null;t.sol();var o=t.next();if(/\d/.test(o)){if("0"==o)return"x"===t.next()?(t.eatWhile(/\w/),"number"):(t.eatWhile(/\d|\./),"number");if(t.eatWhile(/\d|\./),!t.match("d")&&!t.match("output")&&(t.eol()||/\D/.test(t.peek())))return"number"}if(/\w/.test(o))for(const e of EXTRAWORDS){let n=e.substring(1);(e.includes(":")||e.includes(",")||e.includes("."))&&t.match(n)}if(/\w/.test(o)&&(t.eatWhile(/[\w]/),t.match(".gpio")||t.match(".pulse")||t.match(".frq")||t.match(".pwm")))return"def";if("\\"===o)return t.next(),null;if("("===o||")"===o)return"bracket";if("{"===o||"}"===o||":"===o)return"number";if("/"==o)return/\//.test(t.peek())?(t.skipToEnd(),"comment"):"operator";if("'"==o&&(t.eatWhile(/[^']/),t.match("'")))return"attribute";if("+"===o||"="===o||"<"===o||">"===o||"-"===o||","===o||"*"===o||"!"===o)return"operator";if("%"==o){if(/\d/.test(t.next()))return"number";if(t.eatWhile(/[^\s\%]/),t.match("%"))return"hr"}if("["==o&&(t.eatWhile(/[^\s\]]/),t.eat("]")))return"hr";t.eatWhile(/\w/);var i=t.current();return/\w/.test(o)&&t.match("#")?(t.eatWhile(/[\w.#]/),"events"):"#"===o?(t.eatWhile(/\w/),"number"):e.hasOwnProperty(i)?e[i]:null}function f(e,t){return(t.tokens[0]||u)(e,t)}return taskSpecifics=taskSpecifics.concat(m),t("atom",commonAtoms),t("keyword",commonKeywords),t("builtin",commonCommands),t("events",commonEvents),t("def",commonPlugins),t("tag",commonTag),t("number",commonNumber),t("bracket",commonMath),t("warning",commonWarning),t("hr",AnythingElse),t("comment",taskSpecifics),{startState:function(){return{tokens:[]}},token:function(e,t){return f(e,t)},closeBrackets:"[]{}''\"\"``()",lineComment:"//",fold:"brace"}}))})),function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],mod):e(CodeMirror)}((function(e){var t={pairs:"()[]{}''\"\"",closeBefore:")]}'\":;>",triples:"",explode:"[]{}"},n=e.Pos;function o(e,n){return"pairs"==n&&"string"==typeof e?e:"object"==typeof e&&null!=e[n]?e[n]:t[n]}e.defineOption("autoCloseBrackets",!1,(function(t,n,s){s&&s!=e.Init&&(t.removeKeyMap(i),t.state.closeBrackets=null),n&&(r(o(n,"pairs")),t.state.closeBrackets=n,t.addKeyMap(i))}));var i={Backspace:function(t){var i=a(t);if(!i||t.getOption("disableInput"))return e.Pass;for(var r=o(i,"pairs"),s=t.listSelections(),l=0;l=0;l--){var m=s[l].head;t.replaceRange("",n(m.line,m.ch-1),n(m.line,m.ch+1),"+delete")}},Enter:function(t){var n=a(t),i=n&&o(n,"explode");if(!i||t.getOption("disableInput"))return e.Pass;for(var r=t.listSelections(),s=0;s1&&h.indexOf(i)>=0&&t.getRange(n(M.line,M.ch-2),M)==i+i){if(M.ch>2&&/\bstring/.test(t.getTokenTypeAt(n(M.line,M.ch-2))))return e.Pass;S="addFour"}else if(p){var b=0==M.ch?" ":t.getRange(n(M.line,M.ch-1),M);if(e.isWordChar(P)||b==i||e.isWordChar(b))return e.Pass;S="both"}else{if(!C||!(0===P.length||/\s/.test(P)||f.indexOf(P)>-1))return e.Pass;S="both"}else S=p&&m(t,M)?"both":h.indexOf(i)>=0&&t.getRange(M,n(M.line,M.ch+3))==i+i+i?"skipThree":"skip";if(u){if(u!=S)return e.Pass}else u=S}var T=d%2?s.charAt(d-1):i,v=d%2?i:s.charAt(d+1);t.operation((function(){if("skip"==u)l(t,1);else if("skipThree"==u)l(t,3);else if("surround"==u){for(var e=t.getSelections(),n=0;n0?{line:s.head.line,ch:s.head.ch+t}:{line:s.head.line-1};n.push({anchor:a,head:a})}e.setSelections(n,i)}function c(t){var o=e.cmpPos(t.anchor,t.head)>0;return{anchor:new n(t.anchor.line,t.anchor.ch+(o?-1:1)),head:new n(t.head.line,t.head.ch+(o?1:-1))}}function d(e,t){var o=e.getRange(n(t.line,t.ch-1),n(t.line,t.ch+1));return 2==o.length?o:null}function m(e,t){var o=e.getTokenAt(n(t.line,t.ch+1));return/\bstring/.test(o.type)&&o.start==t.ch&&(0==t.ch||!/\bstring/.test(e.getTokenTypeAt(t)))}r(t.pairs+"`")})); \ No newline at end of file +var commonAtoms=["And","Or"],commonKeywords=["If","Else","Elseif","Endif"],commonCommands=["AccessInfo","Background","Build","ClearAccessBlock","ClearRTCam","Config","ControllerDisable","ControllerEnable","DateTime","Debug","Dec","DeepSleep","DisablePriorityTask","DNS","DST","EraseSDKWiFi","ExecuteRules","FactoryReset","Gateway","I2Cscanner","Inc","IP","Latitude","Let","LetStr","Load","LogEntry","LogPortStatus","Longitude","LoopTimerSet","LoopTimerSet_ms","LoopTimerSetAndRun","LoopTimerSetAndRun_ms","MemInfo","MemInfoDetail","Name","NetworkDisable","NetworkEnable","Password","PostToHTTP","PostToHTTPS","Publish","PublishR","PutToHTTP","PutToHTTPS","Reboot","Save","SendTo","SendToHTTP","SendToHTTPS","SendToUDP","SendToUDPMix","Settings","Subnet","Subscribe","TaskClear","TaskClearAll","TaskDisable","TaskEnable","TaskRun","TaskValueSet","TaskValueSetAndRun","TaskValueSetDerived","TaskValueSetPresentation","TimerPause","TimerResume","TimerSet","TimerSet_ms","TimeZone","UdpPort","UdpTest","Unit","UseNTP","WdConfig","WdRead","WiFi","WiFiAllowAP","WiFiAPMode","WiFiConnect","WiFiDisconnect","WiFiKey","WiFiKey2","WiFiMode","WiFiScan","WiFiSSID","WiFiSSID2","WiFiSTAMode","WriteEE","WriteRTC","Event","AsyncEvent","GPIO","GPIOToggle","LongPulse","LongPulse_mS","Monitor","Pulse","PWM","Servo","Status","Tone","RTTTL","UnMonitor","Provision","Provision,Config","Provision,Security","Provision,Notification","Provision,Provision","Provision,Rules","Provision,CustomCdnUrl","Provision,Firmware"],commonEvents=["Clock#Time","JsonReply","JsonReply#","Login#Failed","MQTT#Connected","MQTT#Disconnected","MQTTimport#Connected","MQTTimport#Disconnected","OpenMeteo#current","OpenMeteo#daily","OpenMeteo#hourly","Rules#Timer","System#Boot","ReadEE#","ReadRTC#","System#BootMode","System#Sleep","System#Wake","TaskExit#","TaskInit#","ThingspeakReply","Time#Initialized","Time#Set","WiFi#APmodeDisabled","WiFi#APmodeEnabled","WiFi#ChangedAccesspoint","WiFi#ChangedWiFichannel","WiFi#Connected","WiFi#Disconnected"],commonPlugins=["ResetPulseCounter","SetPulseCounterTotal","LogPulseStatistic","analogout","MCPGPIO","MCPGPIOToggle","MCPLongPulse","MCPLongPulse_ms","MCPPulse","Status,MCP","Monitor,MCP","MonitorRange,MCP","UnMonitorRange,MCP","UnMonitor,MCP","MCPGPIORange","MCPGPIOPattern","MCPMode","MCPModeRange","ExtGpio","ExtPwm","ExtPulse","ExtLongPulse","Status,EXT,","LCDCmd","LCD","PCFGPIO","PCFGPIOToggle","PCFLongPulse","PCFLongPulse_ms","PCFPulse","Status,PCF","Monitor,PCF","MonitorRange,PCF","UnMonitorRange,PCF","UnMonitor,PCF","PCFGPIORange","PCFGPIOpattern","PCFMode","PCFmodeRange","SerialSend","SerialSendMix","Ser2NetClientSend","SerialSend_test","pcapwm","pcafrq","mode2","OLED","OLEDCMD","OLEDCMD,on","OLEDCMD,off","OLEDCMD,clear","IRSEND","IRSENDAC","OledFramedCmd","OledFramedCmd,Display","OledFramedCmd,low","OledFramedCmd,med","OledFramedCmd,high","OledFramedCmd,Frame","OledFramedCmd,linecount","OledFramedCmd,leftalign","OledFramedCmd,align","OledFramedCmd,userDef1","OledFramedCmd,userDef2","NeoPixel","NeoPixelAll","NeoPixelLine","NeoPixelHSV","NeoPixelAllHSV","NeoPixelLineHSV","NeoPixelBright","MotorShieldCmd,DCMotor","MotorShieldCmd,Stepper","MHZCalibrateZero","MHZReset","MHZABCEnable","MHZABCDisable","Sensair_SetRelay","PMSX003","PMSX003,Wake","PMSX003,Sleep","PMSX003,Reset","encwrite","Play","Vol","Eq","Mode","Repeat","tareChanA","tareChanB","7dn","7dst","7dsd","7dtext","7ddt","7dt","7dtfont","7dtbin","7don","7doff","7output","HLWCalibrate","HLWReset","csecalibrate","cseclearpulses","csereset","WemosMotorShieldCMD","LolinMotorShieldCMD","GPS","GPS,Sleep","GPS,Wake","GPS#GotFix","GPS#LostFix","GPS#Travelled","homieValueSet","SerialProxy_Write","SerialProxy_WriteMix","SerialProxy_Test","HeatPumpir","MitsubishiHP","MitsubishiHP,temperature","MitsubishiHP,power","MitsubishiHP,mode","MitsubishiHP,fan","MitsubishiHP,vane","MitsubishiHP,widevane","Culreader_Write","Touch","Touch,Rot","Touch,Flip","Touch,Enable","Touch,Disable","Touch,On","Touch,Off","Touch,Toggle","Touch,Setgrp","Touch,Incgrp","Touch,Decgrp","Touch,Incpage","Touch,Decpage","Touch,Updatebutton","WakeOnLan","DotMatrix","DotMatrix,clear","DotMatrix,update","DotMatrix,size","DotMatrix,txt","DotMatrix,settxt","DotMatrix,content","DotMatrix,alignment","DotMatrix,anim.in","DotMatrix,anim.out","DotMatrix,speed","DotMatrix,pause","DotMatrix,font","DotMatrix,layout","DotMatrix,inverted","DotMatrix,specialeffect","DotMatrix,offset","DotMatrix,brightness","DotMatrix,repeat","DotMatrix,setbar","DotMatrix,bar","Thermo","Thermo,Up","Thermo,Down","Thermo,Mode","Thermo,ModeBtn","Thermo,Setpoint","Max1704xclearalert","scdgetabc","scdgetalt","scdgettmp","scdsetcalibration","scdsetfrc","scdgetinterval","multirelay","multirelay,on","multirelay,off","multirelay,set","multirelay,get","multirelay,loop","ShiftOut","ShiftOut,Set","ShiftOut,SetNoUpdate","ShiftOut,Update","ShiftOut,SetAll","ShiftOut,SetAllNoUpdate","ShiftOut,SetAllLow","ShiftOut,SetAllHigh","ShiftOut,SetChipCount","ShiftOut,SetHexBin","cdmrst","nfx","nfx,off","nfx,on","nfx,dim","nfx,line,","nfx,hsvline,","nfx,one,","nfx,hsvone,","nfx,all,","nfx,rgb,","nfx,fade,","nfx,hsv,","nfx,colorfade,","nfx,rainbow","nfx,kitt,","nfx,comet,","nfx,theatre,","nfx,scan,","nfx,dualscan,","nfx,twinkle,","nfx,twinklefade,","nfx,sparkle,","nfx,wipe,","nfx,dualwipe","nfx,fire","nfx,fireflicker","nfx,faketv","nfx,simpleclock","nfx,stop","nfx,statusrequest","nfx,fadetime,","nfx,fadedelay,","nfx,speed,","nfx,count,","nfx,bgcolor","ShiftIn","ShiftIn,PinEvent","ShiftIn,ChipEvent","ShiftIn,SetChipCount","ShiftIn,SampleFrequency","ShiftIn,EventPerPin","scd4x","scd4x,storesettings","scd4x,facoryreset","scd4x,selftest","scd4x,setfrc,","axp","axp,ldo2","axp,ldo3","axp,ldoio","axp,gpio0","axp,gpio1","axp,gpio2","axp,gpio3","axp,gpio4","axp,dcdc2","axp,dcdc3","axp,ldo2map","axp,ldo3map","axp,ldoiomap","axp,dcdc2map","axp,dcdc3map","axp,ldo2perc","axp,ldo3perc","axp,ldoioperc","axp,dcdc2perc","axp,dcdc3perc","I2CEncoder","I2CEncoder,bright","I2CEncoder,led1","I2CEncoder,led2","I2CEncoder,gain","I2CEncoder,set","cachereader","cachereader,readpos","cachereader,sendtaskinfo","cachereader,flush","tm1621","tm1621,write,","tm1621,writerow,","tm1621,voltamp,","tm1621,energy,","tm1621,celcius,","tm1621,fahrenheit,","tm1621,humidity,","tm1621,raw,","dac","dac,1","dac,2","sht4x","sht4x,startup","ld2410","ld2410,factoryreset","ld2410,logall","digipot","digipot,reset","digipot,shutdown","digipot,","7dextra","7dbefore","7dgroup","7digit","7color","7digitcolor","7groupcolor","gp8403","gp8403,volt,","gp8403,mvolt,","gp8403,range,","gp8403,preset,","gp8403,init,","sen5x","sen5x,startclean","sen5x,techlog,","as3935","as3935,clearstats","as3935,calibrate","as3935,setgain,","as3935,setnf,","as3935,setwd,","as3925,setsrej,","lu9685","lu9685,servo,","lu9685,enable,","lu9685,disable,","lu9685,setrange,","geni2c","geni2c,cmd,","geni2c,exec,","geni2c,log,"],pluginDispKind=["tft","ili9341","ili9342","ili9481","ili9486","ili9488","epd","eink","epaper","il3897","uc8151d","ssd1680","ws2in7","ws1in54","st77xx","st7735","st7789","st7796","neomatrix","neo","pcd8544"],pluginDispCmd=["cmd,on","cmd,off","cmd,clear","cmd,backlight","cmd,bright","cmd,deepsleep","cmd,seq_start","cmd,seq_end","cmd,inv","cmd,rot",",clear",",rot",",tpm",",txt",",txp",",txz",",txc",",txs",",txtfull",",asciitable",",font",",l",",lh",",lv",",lm",",lmr",",r",",rf",",c",",cf",",rf",",t",",tf",",rr",",rrf",",px",",pxh",",pxv",",bmp",",btn",",win",",defwin",",delwin"],commonTag=["On","Do","Endon"],commonNumber=["toBin","toHex","Constrain","XOR","AND:","OR:","Ord","bitRead","bitSet","bitClear","bitWrite","urlencode"],commonMath=["Log","Ln","Abs","Exp","Sqrt","Sq","Round","Sin","Cos","Tan","aSin","aCos","aTan","aTan2","Sin_d","Cos_d","Tan_d","aSin_d","aCos_d","aTan_d","aTan2_d","map","mapc","fmod"],commonWarning=["delay","Delay","ResetFlashWriteCounter"],taskSpecifics=["settings.Enabled","settings.Interval","settings.ValueCount","settings.Controller1.Enabled","settings.Controller2.Enabled","settings.Controller3.Enabled","settings.Controller1.Idx","settings.Controller2.Idx","settings.Controller3.Idx"],AnythingElse=["%eventvalue%","%eventpar%","%eventname%","%sysname%","%bootcause%","%systime%","%systm_hm%","%systm_hm_0%","%systm_hm_sp%","%systime_am%","%systime_am_0%","%systime_am_sp%","%systm_hm_am%","%systm_hm_am_0%","%systm_hm_am_sp%","%lcltime%","%sunrise%","%s_sunrise%","%m_sunrise%","%sunset%","%s_sunset%","%m_sunset%","%lcltime_am%","%latitude%","%longitude%","%syshour%","%syshour_0%","%sysmin%","%sysmin_0%","%syssec%","%syssec_0%","%sysday%","%sysday_0%","%sysmonth%","%sysmonth_0%","%systzoffset%","%systzoffset_s%","%sysyear%","%sysyear_0%","%sysyears%","%sysweekday%","%sysweekday_s%","%unixtime%","%unixtime_lcl%","%uptime%","%uptime_ms%","%rssi%","%ip%","%unit%","%unit_0%","%ssid%","%bssid%","%wi_ch%","%iswifi%","%vcc%","%mac%","%mac_int%","%isntp%","%ismqtt%","%dns%","%dns1%","%dns2%","%flash_freq%","%flash_size%","%flash_chip_vendor%","%flash_chip_model%","%fs_free%","%fs_size%","%cpu_id%","%cpu_freq%","%cpu_model%","%cpu_rev%","%cpu_cores%","%board_name%","%inttemp%","%islimited_build%","%isvar_double%","substring","lookup","indexOf","indexOf_ci","equals","equals_ci","strtol","timeToMin","timeToSec","%ethwifimode%","%ethconnected%","%ethduplex%","%ethspeed%","%ethstate%","%ethspeedstate%","%c_w_dir%","%c_c2f%","%c_ms2Bft%","%c_dew_th%","%c_alt_pres_sea%","%c_sea_pres_alt%","%c_cm2imp%","%c_isnum%","%c_mm2imp%","%c_m2day%","%c_m2dh%","%c_m2dhm%","%c_s2dhms%","%c_ts2date%","%c_ts2isodate%","%c_ts2wday%","%c_random%","%c_2hex%","%c_u2ip%","%c_uname%","%c_uage%","%c_ubuild%","%c_ubuildstr%","%c_uload%","%c_utype%","%c_utypestr%","%c_strf%","%c_d2r%","%c_r2d%","%SP%","%CR%","%LF%","%N%","%R%","%e%","%pi%","var","int","str","length"];for(const e of pluginDispKind)commonPlugins=commonPlugins.concat(e);for(const e of pluginDispKind)for(const t of pluginDispCmd){let n=e+t;commonPlugins=commonPlugins.concat(n)}var rEdit,EXTRAWORDS=commonAtoms.concat(commonPlugins,commonKeywords,commonCommands,commonEvents,commonTag,commonNumber,commonMath,commonWarning,taskSpecifics,AnythingElse),confirmR=!0,android=/Android/.test(navigator.userAgent);function initCM(){function e(e){}android&&(confirmR=!!confirm("Do you want to enable colored rules on your Android device?\nThis feature hasn't been fully tested yet and may still have some issues.\nIt is currently expected to work with Chrome, Firefox, and Vivaldi.\nPlease report any problems you encounter.")),confirmR&&(CodeMirror.commands.autocomplete=function(e){e.showHint({hint:CodeMirror.hint.anyword})},(rEdit=CodeMirror.fromTextArea(document.getElementById("rules"),{tabSize:2,indentWithTabs:!1,lineNumbers:!0,autoCloseBrackets:!0,extraKeys:{"Ctrl-Space":"autocomplete",Tab:e=>{"null"===e.getMode().name?e.execCommand("insertTab"):e.somethingSelected()?e.execCommand("indentMore"):e.execCommand("insertSoftTab")},"Shift-Tab":e=>e.execCommand("indentLess")}})).on("change",(function(){rEdit.save()})),android||rEdit.on("inputRead",(function(e,t){var n=e.getCursor(),o=e.getTokenAt(n);/[\w%,.]/.test(t.text)&&"comment"!=o.type&&e.showHint({completeSingle:!1})})),CodeMirror.keyMap.default["Ctrl-F"]=function(e){openFind()},CodeMirror.keyMap.default["Cmd-F"]=function(e){openFind()},CodeMirror.keyMap.default["Ctrl-G"]=e,CodeMirror.keyMap.default["Cmd-G"]=e,CodeMirror.keyMap.default["Shift-Ctrl-G"]=e,CodeMirror.keyMap.default["Shift-Cmd-G"]=e,CodeMirror.keyMap.default["Ctrl-H"]=e,CodeMirror.keyMap.default["Cmd-H"]=e,CodeMirror.keyMap.default["Shift-Ctrl-F"]=e,CodeMirror.keyMap.default["Shift-Cmd-F"]=e,CodeMirror.keyMap.default["Ctrl-Shift-R"]=e,CodeMirror.keyMap.default["Cmd-Shift-R"]=e)}function closeSearchDialog(){const e=document.querySelectorAll(".CodeMirror-dialog");e.length>0&&(e.forEach((e=>e.remove())),document.body.classList.remove("dialog-opened")),rEdit.execCommand("clearSearch")}function removeHighlight(){requestAnimationFrame((()=>{document.querySelectorAll(".search-next-highlight").forEach((e=>e.classList.remove("search-next-highlight")))}))}let findDialogObserver=null;function openFind(){findDialogObserver&&(document.querySelectorAll(".CodeMirror-dialog").forEach((e=>e.remove())),findDialogObserver.disconnect(),findDialogObserver=null),findDialogObserver=new MutationObserver((()=>{document.querySelector(".CodeMirror-dialog")||(removeHighlight(),findDialogObserver.disconnect(),findDialogObserver=null)})),findDialogObserver.observe(document.body,{childList:!0,subtree:!0}),clearSearchNextHighlight(rEdit),rEdit.execCommand("findPersistent"),addFindButtons()}function clearSearchNextHighlight(e){removeHighlight(),e.__searchNextHighlight&&(e.__searchNextHighlight.clear(),e.__searchNextHighlight=null)}function addFindButtons(){document.querySelector(".CodeMirror-selected");const e=document.querySelector(".CodeMirror-dialog");if(!e||e.querySelector(".search-button-group"))return;[{title:"Find Previous",symbol:"▲",action:()=>rEdit.execCommand("findPersistentPrev")},{title:"Find Next",symbol:"▼",action:()=>rEdit.execCommand("findPersistentNext")},{title:"Replace",symbol:"Replace",action:()=>{closeSearchDialog(),rEdit.execCommand("replace"),addFindButtons()}},{title:"Close",symbol:"❌",action:closeSearchDialog},{title:"Help",symbol:"?",action:()=>{alert("Available shortcuts:\n• Ctrl+F / Cmd+F: Open search\n• Enter: Find next\n• Shift+Enter: Find previous\n• Use /re/ syntax for regex search")}}].forEach((({title:t,symbol:n,action:o})=>{const i=document.createElement("span");i.title=t,i.className="help"===t.toLowerCase()?"button help":"button",i.innerHTML=n,i.style.cssText="\n cursor: pointer;\n user-select: none;\n ",i.addEventListener("click",(e=>{e.preventDefault(),o()})),e.appendChild(i)}))}function triggerFormatting(){let e,t,n,o,i;if(confirmR){const r=rEdit.getDoc();e=rEdit.getScrollInfo(),t=r.getCursor(),n=0===t.ch?t.line-1:t.line,o=rEdit.getLine(n)||"",i=rEdit.getValue()}else i=document.getElementById("rules").value;if(i=initalAutocorrection(i),i=formatLogic(i),confirmR){rEdit.setValue(i);const r=n,s=0===t.ch&&o.length>0?o.length:t.ch;rEdit.setCursor({line:r,ch:s}),setTimeout((()=>{rEdit.scrollTo(e.left,e.top),rEdit.focus()}),0),rEdit.save()}else document.getElementById("rules").value=i}function initalAutocorrection(e){for(const t of EXTRAWORDS)if("Do"===t){const t=/(^|\s)(do)(\s*)(\/\/.*)?$/gim;e=e.replace(t,((e,t,n,o,i)=>`${t}Do${o}${i??""}`))}else{const n=new RegExp(`^\\s*\\b${t}\\b`,"gmi");e=e.replace(n,(e=>e.replace(new RegExp(t,"i"),t)))}return e}function formatLogic(e){const t=" ",n=e.split("\n").map((e=>{const t=e.trimStart();return t.startsWith("//")?e:t})),o=[],i=[];let r=!1,s=null,a=[],l=[];function c(e){return e.trim().startsWith("//")}function d(e){return""===e.trim()}function m(e){return e.trim().toLowerCase().startsWith("on")}function u(e){return e.trim().toLowerCase().endsWith("do")}function f(e){return"endon"===e.trim().toLowerCase()}function h(e){return e.trim().toLowerCase().startsWith("if")}function p(e){return"else"===e.trim().toLowerCase()}function g(e){return e.trim().toLowerCase().startsWith("elseif")}function C(e){return"endif"===e.trim().toLowerCase()}let x=0;function S(){a.length>0&&(i.push(`• Missing ${a.length} Endif(s):`),i.push(` - Unclosed If block(s) starting at line(s): ${l.join(", ")}`)),a=[],l=[]}for(let e=0;e0){const e=extractFirstErrorLine(i);if(alert("Errors found:\n"+i.join("\n")),!isNaN(e))if(confirmR)setTimeout((()=>{jumpToLine(e)}),50);else{const t=document.getElementById("rules");setTimeout((()=>{jumpToLineInTextarea(t,e)}),50)}}return o.join("\n")}function jumpToLine(e){const t=Math.max(0,e-1);rEdit.setCursor({line:t,ch:0}),rEdit.focus(),rEdit.scrollIntoView({line:t,ch:0},100)}function extractFirstErrorLine(e){for(const t of e){let e=t.match(/• Line (\d+)/);if(e)return parseInt(e[1]);if(e=t.match(/starting at line (\d+)/),e)return parseInt(e[1]);if(e=t.match(/starting at line\(s\):\s*(\d+)/),e)return parseInt(e[1])}return null}function jumpToLineInTextarea(e,t){const n=e.value.split("\n"),o=Math.max(1,Math.min(t,n.length));let i=0;for(let e=0;e{const e=document.getElementById("rulesselect");if(e){if(confirmR){const t=document.createElement("button");t.type="button",t.id="searchBtn",t.innerHTML="🔎︎",t.style.padding="2px 5px",t.className="button help",e.appendChild(t),t.addEventListener("click",(()=>{void 0!==rEdit&&openFind()}))}const t=document.createElement("button");t.type="button",t.id="formatBtn",t.textContent="Format",t.className="button",e.appendChild(t),t.addEventListener("click",(()=>{triggerFormatting()}))}let t="";if(document.addEventListener("keydown",(function(e){const n=e.key;(["Backspace","Delete","ArrowLeft","ArrowRight","ArrowUp","ArrowDown","Enter","Tab","Escape","Shift","Control","Alt","Meta"].includes(n)||1!==n.length)&&(t="")})),android){var n=!1;rEdit.on("keydown",((e,o)=>{["Enter","Backspace"," "].includes(o.key)&&(t=""),n=!0}));let e="";const i=rEdit.getInputField();function o(o,i=!1){if(!(rEdit.hasFocus()&&rEdit&&o.data&&n))return;n=!1;const r=o.data,s=rEdit.getDoc(),a=s.getCursor(),l=rEdit.getTokenAt(a);if(" "===r)return t="",void(e="");if(!(r===e&&t.length>0))if(/[\w%,.]/.test(r)&&"comment"!==l.type){const n=a.ch<=1?r.slice(-1):r;t+=n,e=t,t.startsWith(String(a.line+1))&&0===a.ch&&(t=t.slice(String(a.line).length));const o={line:a.line,ch:a.ch-t.length+1},l=()=>{s.replaceRange(t,o,a),rEdit.setCursor({line:o.line,ch:o.ch+t.length}),rEdit.showHint({completeSingle:!1})};i?l():setTimeout(l,0)}else t=""}const r=navigator.userAgent.toLowerCase(),s=/firefox/.test(r),a=/chrome/.test(r)&&!s;s?i.addEventListener("beforeinput",(e=>{e.preventDefault(),o(e,!0)})):a&&document.addEventListener("input",(e=>{o(e,!1)})),rEdit.on("endCompletion",(function(){setTimeout((()=>{!function(){const e=document.createElement("input");e.type="text",e.style.position="absolute",e.style.opacity="0",e.style.height="0",e.style.width="0",e.style.border="none",e.style.top="0",e.style.left="-9999",e.style.padding="0",e.style.zIndex="-1",e.style.fontSize="16px",document.body.appendChild(e),e.focus(),setTimeout((()=>{e.remove(),rEdit.focus()}),10)}()}),100)}))}})),function(e){"object"==typeof exports&&"object"==typeof module?e(require("codemirror")):"function"==typeof define&&define.amd?define(["codemirror"],e):e(CodeMirror)}((function(e){"use strict";e.defineMode("espeasy",(function(){var e={};function t(t,n){for(var o=0;oe.toLowerCase()));commonCommands=commonCommands.concat(n);var o=commonEvents.map((e=>e.toLowerCase()));commonEvents=commonEvents.concat(o);var i=commonPlugins.map((e=>e.toLowerCase()));commonPlugins=commonPlugins.concat(i);var r=commonAtoms.map((e=>e.toLowerCase()));commonAtoms=commonAtoms.concat(r);var s=commonKeywords.map((e=>e.toLowerCase()));commonKeywords=commonKeywords.concat(s);var a=commonTag.map((e=>e.toLowerCase()));commonTag=commonTag.concat(a);var l=commonNumber.map((e=>e.toLowerCase()));commonNumber=commonNumber.concat(l);var c=commonMath.map((e=>e.toLowerCase()));commonMath=commonMath.concat(c);var d=AnythingElse.map((e=>e.toLowerCase()));AnythingElse=AnythingElse.concat(d);var m=taskSpecifics.map((e=>e.toLowerCase()));function u(t,n){if(t.eatSpace())return null;t.sol();var o=t.next();if(/\d/.test(o)){if("0"==o)return"x"===t.next()?(t.eatWhile(/\w/),"number"):(t.eatWhile(/\d|\./),"number");if(t.eatWhile(/\d|\./),!t.match("d")&&!t.match("output")&&(t.eol()||/\D/.test(t.peek())))return"number"}if(/\w/.test(o))for(const e of EXTRAWORDS){let n=e.substring(1);(e.includes(":")||e.includes(",")||e.includes("."))&&t.match(n)}if(/\w/.test(o)&&(t.eatWhile(/[\w]/),t.match(".gpio")||t.match(".pulse")||t.match(".frq")||t.match(".pwm")))return"def";if("\\"===o)return t.next(),null;if("("===o||")"===o)return"bracket";if("{"===o||"}"===o||":"===o)return"number";if("/"==o)return/\//.test(t.peek())?(t.skipToEnd(),"comment"):"operator";if("'"==o&&(t.eatWhile(/[^']/),t.match("'")))return"attribute";if("+"===o||"="===o||"<"===o||">"===o||"-"===o||","===o||"*"===o||"!"===o)return"operator";if("%"==o){if(/\d/.test(t.next()))return"number";if(t.eatWhile(/[^\s\%]/),t.match("%"))return"hr"}if("["==o&&(t.eatWhile(/[^\s\]]/),t.eat("]")))return"hr";t.eatWhile(/\w/);var i=t.current();return/\w/.test(o)&&t.match("#")?(t.eatWhile(/[\w.#]/),"events"):"#"===o?(t.eatWhile(/\w/),"number"):e.hasOwnProperty(i)?e[i]:null}function f(e,t){return(t.tokens[0]||u)(e,t)}return taskSpecifics=taskSpecifics.concat(m),t("atom",commonAtoms),t("keyword",commonKeywords),t("builtin",commonCommands),t("events",commonEvents),t("def",commonPlugins),t("tag",commonTag),t("number",commonNumber),t("bracket",commonMath),t("warning",commonWarning),t("hr",AnythingElse),t("comment",taskSpecifics),{startState:function(){return{tokens:[]}},token:function(e,t){return f(e,t)},closeBrackets:"[]{}''\"\"``()",lineComment:"//",fold:"brace"}}))})),function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],mod):e(CodeMirror)}((function(e){var t={pairs:"()[]{}''\"\"",closeBefore:")]}'\":;>",triples:"",explode:"[]{}"},n=e.Pos;function o(e,n){return"pairs"==n&&"string"==typeof e?e:"object"==typeof e&&null!=e[n]?e[n]:t[n]}e.defineOption("autoCloseBrackets",!1,(function(t,n,s){s&&s!=e.Init&&(t.removeKeyMap(i),t.state.closeBrackets=null),n&&(r(o(n,"pairs")),t.state.closeBrackets=n,t.addKeyMap(i))}));var i={Backspace:function(t){var i=a(t);if(!i||t.getOption("disableInput"))return e.Pass;for(var r=o(i,"pairs"),s=t.listSelections(),l=0;l=0;l--){var m=s[l].head;t.replaceRange("",n(m.line,m.ch-1),n(m.line,m.ch+1),"+delete")}},Enter:function(t){var n=a(t),i=n&&o(n,"explode");if(!i||t.getOption("disableInput"))return e.Pass;for(var r=t.listSelections(),s=0;s1&&h.indexOf(i)>=0&&t.getRange(n(M.line,M.ch-2),M)==i+i){if(M.ch>2&&/\bstring/.test(t.getTokenTypeAt(n(M.line,M.ch-2))))return e.Pass;S="addFour"}else if(p){var b=0==M.ch?" ":t.getRange(n(M.line,M.ch-1),M);if(e.isWordChar(P)||b==i||e.isWordChar(b))return e.Pass;S="both"}else{if(!C||!(0===P.length||/\s/.test(P)||f.indexOf(P)>-1))return e.Pass;S="both"}else S=p&&m(t,M)?"both":h.indexOf(i)>=0&&t.getRange(M,n(M.line,M.ch+3))==i+i+i?"skipThree":"skip";if(u){if(u!=S)return e.Pass}else u=S}var T=d%2?s.charAt(d-1):i,v=d%2?i:s.charAt(d+1);t.operation((function(){if("skip"==u)l(t,1);else if("skipThree"==u)l(t,3);else if("surround"==u){for(var e=t.getSelections(),n=0;n0?{line:s.head.line,ch:s.head.ch+t}:{line:s.head.line-1};n.push({anchor:a,head:a})}e.setSelections(n,i)}function c(t){var o=e.cmpPos(t.anchor,t.head)>0;return{anchor:new n(t.anchor.line,t.anchor.ch+(o?-1:1)),head:new n(t.head.line,t.head.ch+(o?1:-1))}}function d(e,t){var o=e.getRange(n(t.line,t.ch-1),n(t.line,t.ch+1));return 2==o.length?o:null}function m(e,t){var o=e.getTokenAt(n(t.line,t.ch+1));return/\bstring/.test(o.type)&&o.start==t.ch&&(0==t.ch||!/\bstring/.test(e.getTokenTypeAt(t)))}r(t.pairs+"`")})); \ No newline at end of file