From a14254a6b4270725a119b2c759120c16f4b497ea Mon Sep 17 00:00:00 2001 From: Bruno Castro Date: Tue, 10 Sep 2024 21:37:40 -0300 Subject: [PATCH 01/15] CAN Hardware Implementation --- lib/arduino-CAN/.travis.yml | 31 + lib/arduino-CAN/API.md | 272 +++ lib/arduino-CAN/LICENSE | 21 + lib/arduino-CAN/README.md | 71 + .../examples/CANReceiver/CANReceiver.ino | 56 + .../CANReceiverCallback.ino | 59 + .../examples/CANSender/CANSender.ino | 50 + lib/arduino-CAN/keywords.txt | 49 + lib/arduino-CAN/library.properties | 10 + lib/arduino-CAN/src/CAN.h | 13 + lib/arduino-CAN/src/CANController.cpp | 216 ++ lib/arduino-CAN/src/CANController.h | 78 + lib/arduino-CAN/src/ESP32SJA1000.cpp | 422 ++++ lib/arduino-CAN/src/ESP32SJA1000.h | 65 + lib/arduino-CAN/src/MCP2515.cpp | 495 ++++ lib/arduino-CAN/src/MCP2515.h | 77 + src/src/CustomBuild/ESPEasyDefaults.h | 4 + src/src/DataStructs/SettingsStruct.h | 8 + src/src/DataStructs_templ/SettingsStruct.cpp | 12 + src/src/Helpers/ESPEasy_checks.cpp | 530 ++-- src/src/Helpers/Hardware.cpp | 2095 ++++++++-------- src/src/Helpers/Hardware.h | 3 + src/src/Helpers/StringGenerator_GPIO.cpp | 16 +- src/src/Helpers/StringGenerator_GPIO.h | 4 +- src/src/WebServer/HardwarePage.cpp | 21 + src/src/WebServer/Markup.cpp | 2176 +++++++++-------- 26 files changed, 4473 insertions(+), 2381 deletions(-) create mode 100644 lib/arduino-CAN/.travis.yml create mode 100644 lib/arduino-CAN/API.md create mode 100644 lib/arduino-CAN/LICENSE create mode 100644 lib/arduino-CAN/README.md create mode 100644 lib/arduino-CAN/examples/CANReceiver/CANReceiver.ino create mode 100644 lib/arduino-CAN/examples/CANReceiverCallback/CANReceiverCallback.ino create mode 100644 lib/arduino-CAN/examples/CANSender/CANSender.ino create mode 100644 lib/arduino-CAN/keywords.txt create mode 100644 lib/arduino-CAN/library.properties create mode 100644 lib/arduino-CAN/src/CAN.h create mode 100644 lib/arduino-CAN/src/CANController.cpp create mode 100644 lib/arduino-CAN/src/CANController.h create mode 100644 lib/arduino-CAN/src/ESP32SJA1000.cpp create mode 100644 lib/arduino-CAN/src/ESP32SJA1000.h create mode 100644 lib/arduino-CAN/src/MCP2515.cpp create mode 100644 lib/arduino-CAN/src/MCP2515.h diff --git a/lib/arduino-CAN/.travis.yml b/lib/arduino-CAN/.travis.yml new file mode 100644 index 0000000000..418faf1b29 --- /dev/null +++ b/lib/arduino-CAN/.travis.yml @@ -0,0 +1,31 @@ +language: generic +env: + global: + - IDE_VERSION=1.8.5 + matrix: + - BOARD="arduino:avr:uno" + - BOARD="arduino:samd:mkrzero" + - BOARD="espressif:esp32:esp32" +before_install: + - wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz + - tar xf arduino-$IDE_VERSION-linux64.tar.xz + - mv arduino-$IDE_VERSION $HOME/arduino-ide + - export PATH=$PATH:$HOME/arduino-ide + - if [[ "$BOARD" =~ "arduino:samd:" ]]; then + arduino --install-boards arduino:samd; + fi + - if [[ "$BOARD" =~ "espressif:esp32:" ]]; then + mkdir -p $HOME/Arduino/hardware/espressif; + git clone https://github.com/espressif/arduino-esp32.git $HOME/Arduino/hardware/espressif/esp32; + pushd $HOME/Arduino/hardware/espressif/esp32/tools/; + python get.py; + popd; + fi + - buildExampleSketch() { arduino --verbose-build --verify --board $BOARD $PWD/examples/$1/$1.ino; } +install: + - mkdir -p $HOME/Arduino/libraries + - ln -s $PWD $HOME/Arduino/libraries/CAN +script: + - buildExampleSketch CANReceiver + - buildExampleSketch CANReceiverCallback + - buildExampleSketch CANSender diff --git a/lib/arduino-CAN/API.md b/lib/arduino-CAN/API.md new file mode 100644 index 0000000000..cbe9cac6c9 --- /dev/null +++ b/lib/arduino-CAN/API.md @@ -0,0 +1,272 @@ +# CAN API + +## Include Library + +```arduino +#include +``` + +## Setup + +### Begin + +Initialize the library with the specified bit rate. + +```arduino +CAN.begin(bitrate); +``` + * `bitrate` - bit rate in bits per seconds (bps) (`1000E3`, `500E3`, `250E3`, `200E3`, `125E3`, `100E3`, `80E3`, `50E3`, `40E3`, `20E3`, `10E3`, `5E3`) + +Returns `1` on success, `0` on failure. + +### Set pins + +#### MCP2515 + +Override the default `CS` and `INT` pins used by the library. **Must** be called before `CAN.begin(...)`. + +```arduino +CAN.setPins(cs, irq); +``` + * `cs` - new chip select pin to use, defaults to `10` + * `irq` - new INT pin to use, defaults to `2`. **Must** be interrupt capable via [attachInterrupt(...)](https://www.arduino.cc/en/Reference/AttachInterrupt). + +This call is optional and only needs to be used if you need to change the default pins used. + +#### ESP32 + +Override the default `CTX` and `CRX` pins used by the library. **Must** be called before `CAN.begin(...)`. + +```arduino +CAN.setPins(rx, tx); +``` + * `rx` - new CRX pin to use, defaults to `4` + * `tx` - new CTX pin to use, defaults to `5`. + +This call is optional and only needs to be used if you need to change the default pins used. + +### Set SPI Frequency + +**MCP2515 only** + +Override the default SPI frequency of 10 MHz used by the library. **Must** be called before `CAN.begin(...)`. + +```arduino +CAN.setSPIFrequency(frequency); +``` + * `frequency` - new SPI frequency to use, defaults to `10E6` + +This call is optional and only needs to be used if you need to change the default SPI frequency used. Some logic level converters cannot support high speeds such as 10 MHz, so a lower SPI frequency can be selected with `CAN.setSPIFrequency(frequency)`. + +### Set Clock Frequency + +**MCP2515 only** + +Override the default clock source frequency that is connected to the MCP2515. **Must** be called before `CAN.begin(...)`. + +```arduino +CAN.setClockFrequency(clockFrequency); +``` + * `clockFrequency` - new clock frequency to use (`8E6`, `16E6`) connected to MCP2515, defaults to `16 Mhz` + +This call is optional and only needs to be used if you need to change the clock source frequency connected to the MCP2515. Most shields have a 16 MHz clock source on board, some breakout boards have a 8 MHz source. + +### End + +Stop the library + +```arduino +CAN.end() +``` + +## Sending data + +### Begin packet + +Start the sequence of sending a packet. + +```arduino +CAN.beginPacket(id); +CAN.beginPacket(id, dlc); +CAN.beginPacket(id, dlc, rtr); + +CAN.beginExtendedPacket(id); +CAN.beginExtendedPacket(id, dlc); +CAN.beginExtendedPacket(id, dlc, rtr); +``` + + * `id` - 11-bit id (standard packet) or 29-bit packet id (extended packet) + * `dlc` - (optional) value of Data Length Code (DLC) field of packet, default is size of data written in packet + * `rtr` - (optional) value of Remote Transmission Request (RTR) field of packet (`false` or `true`), defaults to `false`. RTR packets contain no data, the DLC field of the packet represents the requested length. + +Returns `1` on success, `0` on failure. + +### Writing + +Write data to the packet. Each packet can contain up to 8 bytes. + +```arduino +CAN.write(byte); + +CAN.write(buffer, length); +``` +* `byte` - single byte to write to packet + +or + +* `buffer` - data to write to packet +* `length` - size of data to write + +Returns the number of bytes written. + +**Note:** Other Arduino `Print` API's can also be used to write data into the packet + +### End packet + +End the sequence of sending a packet. + +```arduino +CAN.endPacket() +``` + +Returns `1` on success, `0` on failure. + +## Receiving data + +### Parsing packet + +Check if a packet has been received. + +```arduino +int packetSize = CAN.parsePacket(); +``` + +Returns the packet size in bytes or `0` if no packet was received. For RTR packets the size reflects the DLC field of the packet. + +### Register callback + +Register a callback function for when a packet is received. + +```arduino +CAN.onReceive(onReceive); + +void onReceive(int packetSize) { + // ... +} +``` + + * `onReceive` - function to call when a packet is received. + +### Packet ID + +```arduino +long id = CAN.packetId(); +``` + +Returns the id (11-bit or 29 bit) of the received packet. Standard packets have an 11-bit id, extended packets have an 29-bit id. + +### Packet Extended + +```arduino +bool extended = CAN.packetExtended(); +``` + +Returns `true` if the received packet is extended, `false` otherwise. + +### Packet RTR + +```arduino +bool rtr = CAN.packetRtr(); +``` + +Returns the value of the Remote Transmission Request (RTR) field of the packet `true`/`false`. RTR packets contain no data, the DLC field is the requested data length. + +### Packet DLC + +```arduino +int DLC = CAN.packetDlc(); +``` + +Returns the value of the Data Length Code (DLC) field of the packet. + + +### Available + +```arduino +int availableBytes = CAN.available() +``` + +Returns number of bytes available for reading. + +### Peeking + +Peek at the next byte in the packet. + +```arduino +int b = CAN.peek(); +``` + +Returns the next byte in the packet or `-1` if no bytes are available. + +### Reading + +Read the next byte from the packet. + +```arduino +int b = CAN.read(); +``` + +Returns the next byte in the packet or `-1` if no bytes are available. + +**Note:** Other Arduino [`Stream` API's](https://www.arduino.cc/en/Reference/Stream) can also be used to read data from the packet + +### Filtering + +Filter packets that meet the desired criteria. + +``` +CAN.filter(id); +CAN.filter(id, mask); + +CAN.filterExtended(id); +CAN.filterExtended(id, mask); +``` + + * `id` - 11-bit id (standard packet) or 29-bit packet id (extended packet) + * `mask` - (optional) 11-bit mask (standard packet) or 29-bit mask (extended packet), defaults to `0x7ff` or `0x1fffffff` (extended) + +Only packets that meet the following criteria are acknowleged and received, other packets are ignored: + +``` +if ((packetId & mask) == id) { + // acknowleged and received +} else { + // ignored +} +``` + +Returns `1` on success, `0` on failure. + +## Other modes + +### Loopback mode + +Put the CAN controller in loopback mode, any outgoing packets will also be received. + +```arduino +CAN.loopback(); +``` + +### Sleep mode + +Put the CAN contoller in sleep mode. + +```arduino +CAN.sleep(); +``` + +Wake up the CAN contoller if it was previously in sleep mode. + +```arduino +CAN.wakeup(); +``` diff --git a/lib/arduino-CAN/LICENSE b/lib/arduino-CAN/LICENSE new file mode 100644 index 0000000000..97a0d8be20 --- /dev/null +++ b/lib/arduino-CAN/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Sandeep Mistry + +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. diff --git a/lib/arduino-CAN/README.md b/lib/arduino-CAN/README.md new file mode 100644 index 0000000000..cf4b7985b8 --- /dev/null +++ b/lib/arduino-CAN/README.md @@ -0,0 +1,71 @@ +# Arduino CAN + +[![Build Status](https://travis-ci.org/sandeepmistry/arduino-CAN.svg?branch=master)](https://travis-ci.org/sandeepmistry/arduino-CAN) + +An Arduino library for sending and receiving data using CAN bus. + +## Compatible Hardware + +* [Microchip MCP2515](http://www.microchip.com/wwwproducts/en/en010406) based boards/shields + * [Arduino MKR CAN shield](https://store.arduino.cc/arduino-mkr-can-shield) +* [Espressif ESP32](http://espressif.com/en/products/hardware/esp32/overview)'s built-in [SJA1000](https://www.nxp.com/products/analog/interfaces/in-vehicle-network/can-transceiver-and-controllers/stand-alone-can-controller:SJA1000T) compatible CAN controller with an external 3.3V CAN transceiver + +### Microchip MCP2515 wiring + +| Microchip MCP2515 | Arduino | +| :---------------: | :-----: | +| VCC | 5V | +| GND | GND | +| SCK | SCK | +| SO | MISO | +| SI | MOSI | +| CS | 10 | +| INT | 2 | + + +`CS` and `INT` pins can be changed by using `CAN.setPins(cs, irq)`. `INT` pin is optional, it is only needed for receive callback mode. If `INT` pin is used, it **must** be interrupt capable via [`attachInterrupt(...)`](https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/). + +**NOTE**: Logic level converters must be used for boards which operate at 3.3V. + +### Espressif ESP32 wiring + +Requires an external 3.3V CAN transceiver, such as a [TI SN65HVD230](http://www.ti.com/product/SN65HVD230). + +| CAN transceiver | ESP32 | +| :-------------: | :---: | +| 3V3 | 3V3 | +| GND | GND | +| CTX | GPIO_5 | +| CRX | GPIO_4 | + +`CTX` and `CRX` pins can be changed by using `CAN.setPins(rx, tx)`. + +## Installation + +### Using the Arduino IDE Library Manager + +1. Choose `Sketch` -> `Include Library` -> `Manage Libraries...` +2. Type `CAN` into the search box. +3. Click the row to select the library. +4. Click the `Install` button to install the library. + +### Using Git + +```sh +cd ~/Documents/Arduino/libraries/ +git clone https://github.com/sandeepmistry/arduino-CAN CAN +``` + +## API + +See [API.md](API.md). + +## Examples + +See [examples](examples) folder. + +For OBD-II examples, checkout the [arduino-OBD2](https://github.com/sandeepmistry/arduino-OBD2) library's [examples](https://github.com/sandeepmistry/arduino-OBD2/examples). + +## License + +This library is [licensed](LICENSE) under the [MIT Licence](http://en.wikipedia.org/wiki/MIT_License). diff --git a/lib/arduino-CAN/examples/CANReceiver/CANReceiver.ino b/lib/arduino-CAN/examples/CANReceiver/CANReceiver.ino new file mode 100644 index 0000000000..d2f5db80c2 --- /dev/null +++ b/lib/arduino-CAN/examples/CANReceiver/CANReceiver.ino @@ -0,0 +1,56 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("CAN Receiver"); + + // start the CAN bus at 500 kbps + if (!CAN.begin(500E3)) { + Serial.println("Starting CAN failed!"); + while (1); + } +} + +void loop() { + // try to parse packet + int packetSize = CAN.parsePacket(); + + if (packetSize || CAN.packetId() != -1) { + // received a packet + Serial.print("Received "); + + if (CAN.packetExtended()) { + Serial.print("extended "); + } + + if (CAN.packetRtr()) { + // Remote transmission request, packet contains no data + Serial.print("RTR "); + } + + Serial.print("packet with id 0x"); + Serial.print(CAN.packetId(), HEX); + + if (CAN.packetRtr()) { + Serial.print(" and requested length "); + Serial.println(CAN.packetDlc()); + } else { + Serial.print(" and length "); + Serial.println(packetSize); + + // only print packet data for non-RTR packets + while (CAN.available()) { + Serial.print((char)CAN.read()); + } + Serial.println(); + } + + Serial.println(); + } +} + diff --git a/lib/arduino-CAN/examples/CANReceiverCallback/CANReceiverCallback.ino b/lib/arduino-CAN/examples/CANReceiverCallback/CANReceiverCallback.ino new file mode 100644 index 0000000000..cf2fa01bf5 --- /dev/null +++ b/lib/arduino-CAN/examples/CANReceiverCallback/CANReceiverCallback.ino @@ -0,0 +1,59 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("CAN Receiver Callback"); + + // start the CAN bus at 500 kbps + if (!CAN.begin(500E3)) { + Serial.println("Starting CAN failed!"); + while (1); + } + + // register the receive callback + CAN.onReceive(onReceive); +} + +void loop() { + // do nothing +} + +void onReceive(int packetSize) { + // received a packet + Serial.print("Received "); + + if (CAN.packetExtended()) { + Serial.print("extended "); + } + + if (CAN.packetRtr()) { + // Remote transmission request, packet contains no data + Serial.print("RTR "); + } + + Serial.print("packet with id 0x"); + Serial.print(CAN.packetId(), HEX); + + if (CAN.packetRtr()) { + Serial.print(" and requested length "); + Serial.println(CAN.packetDlc()); + } else { + Serial.print(" and length "); + Serial.println(packetSize); + + // only print packet data for non-RTR packets + while (CAN.available()) { + Serial.print((char)CAN.read()); + } + Serial.println(); + } + + Serial.println(); +} + + diff --git a/lib/arduino-CAN/examples/CANSender/CANSender.ino b/lib/arduino-CAN/examples/CANSender/CANSender.ino new file mode 100644 index 0000000000..92772cccf9 --- /dev/null +++ b/lib/arduino-CAN/examples/CANSender/CANSender.ino @@ -0,0 +1,50 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +void setup() { + Serial.begin(9600); + while (!Serial); + + Serial.println("CAN Sender"); + + // start the CAN bus at 500 kbps + if (!CAN.begin(500E3)) { + Serial.println("Starting CAN failed!"); + while (1); + } +} + +void loop() { + // send packet: id is 11 bits, packet can contain up to 8 bytes of data + Serial.print("Sending packet ... "); + + CAN.beginPacket(0x12); + CAN.write('h'); + CAN.write('e'); + CAN.write('l'); + CAN.write('l'); + CAN.write('o'); + CAN.endPacket(); + + Serial.println("done"); + + delay(1000); + + // send extended packet: id is 29 bits, packet can contain up to 8 bytes of data + Serial.print("Sending extended packet ... "); + + CAN.beginExtendedPacket(0xabcdef); + CAN.write('w'); + CAN.write('o'); + CAN.write('r'); + CAN.write('l'); + CAN.write('d'); + CAN.endPacket(); + + Serial.println("done"); + + delay(1000); +} + diff --git a/lib/arduino-CAN/keywords.txt b/lib/arduino-CAN/keywords.txt new file mode 100644 index 0000000000..985ce39f13 --- /dev/null +++ b/lib/arduino-CAN/keywords.txt @@ -0,0 +1,49 @@ +####################################### +# Syntax Coloring Map For CAN +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +CAN KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +end KEYWORD2 + +beginPacket KEYWORD2 +beginExtendedPacket KEYWORD2 +endPacket KEYWORD2 + +parsePacket KEYWORD2 +packetId KEYWORD2 +packetExtended KEYWORD2 +packetRtr KEYWORD2 +packetDlc KEYWORD2 + +write KEYWORD2 + +available KEYWORD2 +read KEYWORD2 +peek KEYWORD2 +flush KEYWORD2 + +onReceive KEYWORD2 +filter KEYWORD2 +filterExtended KEYWORD2 +loopback KEYWORD2 +sleep KEYWORD2 +wakeup KEYWORD2 + +setPins KEYWORD2 +setSPIFrequency KEYWORD2 +setClockFrequency KEYWORD2 +dumpRegisters KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/lib/arduino-CAN/library.properties b/lib/arduino-CAN/library.properties new file mode 100644 index 0000000000..acf321fe86 --- /dev/null +++ b/lib/arduino-CAN/library.properties @@ -0,0 +1,10 @@ +name=CAN +version=0.3.1 +author=Sandeep Mistry +maintainer=Sandeep Mistry +sentence=An Arduino library for sending and receiving data using CAN bus. +paragraph=Supports Microchip MCP2515 based boards/shields and the Espressif ESP32's built-in SJA1000 compatible CAN controller. +category=Communication +url=https://github.com/sandeepmistry/arduino-CAN +architectures=* +includes=CAN.h diff --git a/lib/arduino-CAN/src/CAN.h b/lib/arduino-CAN/src/CAN.h new file mode 100644 index 0000000000..d8edc06baa --- /dev/null +++ b/lib/arduino-CAN/src/CAN.h @@ -0,0 +1,13 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CAN_H +#define CAN_H + +#ifdef ARDUINO_ARCH_ESP32 +#include "ESP32SJA1000.h" +#else +#include "MCP2515.h" +#endif + +#endif diff --git a/lib/arduino-CAN/src/CANController.cpp b/lib/arduino-CAN/src/CANController.cpp new file mode 100644 index 0000000000..8bd34ed920 --- /dev/null +++ b/lib/arduino-CAN/src/CANController.cpp @@ -0,0 +1,216 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "CANController.h" + +CANControllerClass::CANControllerClass() : + _onReceive(NULL), + + _packetBegun(false), + _txId(-1), + _txExtended(-1), + _txRtr(false), + _txDlc(0), + _txLength(0), + + _rxId(-1), + _rxExtended(false), + _rxRtr(false), + _rxDlc(0), + _rxLength(0), + _rxIndex(0) +{ + // overide Stream timeout value + setTimeout(0); +} + +CANControllerClass::~CANControllerClass() +{ +} + +int CANControllerClass::begin(long /*baudRate*/) +{ + _packetBegun = false; + _txId = -1; + _txRtr =false; + _txDlc = 0; + _txLength = 0; + + _rxId = -1; + _rxRtr = false; + _rxDlc = 0; + _rxLength = 0; + _rxIndex = 0; + + return 1; +} + +void CANControllerClass::end() +{ +} + +int CANControllerClass::beginPacket(int id, int dlc, bool rtr) +{ + if (id < 0 || id > 0x7FF) { + return 0; + } + + if (dlc > 8) { + return 0; + } + + _packetBegun = true; + _txId = id; + _txExtended = false; + _txRtr = rtr; + _txDlc = dlc; + _txLength = 0; + + memset(_txData, 0x00, sizeof(_txData)); + + return 1; +} + +int CANControllerClass::beginExtendedPacket(long id, int dlc, bool rtr) +{ + if (id < 0 || id > 0x1FFFFFFF) { + return 0; + } + + if (dlc > 8) { + return 0; + } + + _packetBegun = true; + _txId = id; + _txExtended = true; + _txRtr = rtr; + _txDlc = dlc; + _txLength = 0; + + memset(_txData, 0x00, sizeof(_txData)); + + return 1; +} + +int CANControllerClass::endPacket(unsigned long timeoutMs) +{ + if (!_packetBegun) { + return 0; + } + _packetBegun = false; + + if (_txDlc >= 0) { + _txLength = _txDlc; + } + + return 1; +} + +int CANControllerClass::parsePacket() +{ + return 0; +} + +long CANControllerClass::packetId() +{ + return _rxId; +} + +bool CANControllerClass::packetExtended() +{ + return _rxExtended; +} + +bool CANControllerClass::packetRtr() +{ + return _rxRtr; +} + +int CANControllerClass::packetDlc() +{ + return _rxDlc; +} + +size_t CANControllerClass::write(uint8_t byte) +{ + return write(&byte, sizeof(byte)); +} + +size_t CANControllerClass::write(const uint8_t *buffer, size_t size) +{ + if (!_packetBegun) { + return 0; + } + + if (size > (sizeof(_txData) - _txLength)) { + size = sizeof(_txData) - _txLength; + } + + memcpy(&_txData[_txLength], buffer, size); + _txLength += size; + + return size; +} + +int CANControllerClass::available() +{ + return (_rxLength - _rxIndex); +} + +int CANControllerClass::read() +{ + if (!available()) { + return -1; + } + + return _rxData[_rxIndex++]; +} + +int CANControllerClass::peek() +{ + if (!available()) { + return -1; + } + + return _rxData[_rxIndex]; +} + +void CANControllerClass::flush() +{ +} + +void CANControllerClass::onReceive(void(*callback)(int)) +{ + _onReceive = callback; +} + +int CANControllerClass::filter(int /*id*/, int /*mask*/) +{ + return 0; +} + +int CANControllerClass::filterExtended(long /*id*/, long /*mask*/) +{ + return 0; +} + +int CANControllerClass::observe() +{ + return 0; +} + +int CANControllerClass::loopback() +{ + return 0; +} + +int CANControllerClass::sleep() +{ + return 0; +} + +int CANControllerClass::wakeup() +{ + return 0; +} diff --git a/lib/arduino-CAN/src/CANController.h b/lib/arduino-CAN/src/CANController.h new file mode 100644 index 0000000000..311c2f8be5 --- /dev/null +++ b/lib/arduino-CAN/src/CANController.h @@ -0,0 +1,78 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CAN_CONTROLLER_H +#define CAN_CONTROLLER_H + +#include + +class CANControllerClass : public Stream { + +public: + enum { + SEND_OK, + SEND_BEGIN, + SEND_TIMEOUT, + SEND_ACK, + }; + + virtual int begin(long baudRate); + virtual void end(); + + int beginPacket(int id, int dlc = -1, bool rtr = false); + int beginExtendedPacket(long id, int dlc = -1, bool rtr = false); + virtual int endPacket(unsigned long timeoutMs = 0); + + virtual int parsePacket(); + long packetId(); + bool packetExtended(); + bool packetRtr(); + int packetDlc(); + + // from Print + virtual size_t write(uint8_t byte); + virtual size_t write(const uint8_t *buffer, size_t size); + + // from Stream + virtual int available(); + virtual int read(); + virtual int peek(); + virtual void flush(); + + virtual void onReceive(void(*callback)(int)); + + virtual int filter(int id) { return filter(id, 0x7ff); } + virtual int filter(int id, int mask); + virtual int filterExtended(long id) { return filterExtended(id, 0x1fffffff); } + virtual int filterExtended(long id, long mask); + + virtual int observe(); + virtual int loopback(); + virtual int sleep(); + virtual int wakeup(); + +protected: + CANControllerClass(); + virtual ~CANControllerClass(); + +protected: + void (*_onReceive)(int); + + bool _packetBegun; + long _txId; + bool _txExtended; + bool _txRtr; + int _txDlc; + int _txLength; + uint8_t _txData[8]; + + long _rxId; + bool _rxExtended; + bool _rxRtr; + int _rxDlc; + int _rxLength; + int _rxIndex; + uint8_t _rxData[8]; +}; + +#endif diff --git a/lib/arduino-CAN/src/ESP32SJA1000.cpp b/lib/arduino-CAN/src/ESP32SJA1000.cpp new file mode 100644 index 0000000000..30e39e984a --- /dev/null +++ b/lib/arduino-CAN/src/ESP32SJA1000.cpp @@ -0,0 +1,422 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifdef ARDUINO_ARCH_ESP32 + +#include "esp_intr.h" +#include "soc/dport_reg.h" +#include "driver/gpio.h" + +#include "ESP32SJA1000.h" + +#define REG_BASE 0x3ff6b000 + +#define REG_MOD 0x00 +#define REG_CMR 0x01 +#define REG_SR 0x02 +#define REG_IR 0x03 +#define REG_IER 0x04 + +#define REG_BTR0 0x06 +#define REG_BTR1 0x07 +#define REG_OCR 0x08 + +#define REG_ALC 0x0b +#define REG_ECC 0x0c +#define REG_EWLR 0x0d +#define REG_RXERR 0x0e +#define REG_TXERR 0x0f +#define REG_SFF 0x10 +#define REG_EFF 0x10 +#define REG_ACRn(n) (0x10 + n) +#define REG_AMRn(n) (0x14 + n) + +#define REG_CDR 0x1F + + +ESP32SJA1000Class::ESP32SJA1000Class() : + CANControllerClass(), + _rxPin(DEFAULT_CAN_RX_PIN), + _txPin(DEFAULT_CAN_TX_PIN), + _loopback(false), + _intrHandle(NULL) +{ +} + +ESP32SJA1000Class::~ESP32SJA1000Class() +{ +} + +int ESP32SJA1000Class::begin(long baudRate) +{ + CANControllerClass::begin(baudRate); + + _loopback = false; + + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST); + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN); + + // RX pin + gpio_set_direction(_rxPin, GPIO_MODE_INPUT); + gpio_matrix_in(_rxPin, CAN_RX_IDX, 0); + gpio_pad_select_gpio(_rxPin); + + // TX pin + gpio_set_direction(_txPin, GPIO_MODE_OUTPUT); + gpio_matrix_out(_txPin, CAN_TX_IDX, 0, 0); + gpio_pad_select_gpio(_txPin); + + modifyRegister(REG_CDR, 0x80, 0x80); // pelican mode + modifyRegister(REG_BTR0, 0xc0, 0x40); // SJW = 1 + modifyRegister(REG_BTR1, 0x70, 0x10); // TSEG2 = 1 + + switch (baudRate) { + case (long)1000E3: + modifyRegister(REG_BTR1, 0x0f, 0x04); + modifyRegister(REG_BTR0, 0x3f, 4); + break; + + case (long)500E3: + modifyRegister(REG_BTR1, 0x0f, 0x0c); + modifyRegister(REG_BTR0, 0x3f, 4); + break; + + case (long)250E3: + modifyRegister(REG_BTR1, 0x0f, 0x0c); + modifyRegister(REG_BTR0, 0x3f, 9); + break; + + case (long)200E3: + modifyRegister(REG_BTR1, 0x0f, 0x0c); + modifyRegister(REG_BTR0, 0x3f, 12); + break; + + case (long)125E3: + modifyRegister(REG_BTR1, 0x0f, 0x0c); + modifyRegister(REG_BTR0, 0x3f, 19); + break; + + case (long)100E3: + modifyRegister(REG_BTR1, 0x0f, 0x0c); + modifyRegister(REG_BTR0, 0x3f, 24); + break; + + case (long)80E3: + modifyRegister(REG_BTR1, 0x0f, 0x0c); + modifyRegister(REG_BTR0, 0x3f, 30); + break; + + case (long)50E3: + modifyRegister(REG_BTR1, 0x0f, 0x0c); + modifyRegister(REG_BTR0, 0x3f, 49); + break; + +/* + Due to limitations in ESP32 hardware and/or RTOS software, baudrate can't be lower than 50kbps. + See https://esp32.com/viewtopic.php?t=2142 +*/ + default: + return 0; + break; + } + + modifyRegister(REG_BTR1, 0x80, 0x80); // SAM = 1 + writeRegister(REG_IER, 0xff); // enable all interrupts + + // set filter to allow anything + writeRegister(REG_ACRn(0), 0x00); + writeRegister(REG_ACRn(1), 0x00); + writeRegister(REG_ACRn(2), 0x00); + writeRegister(REG_ACRn(3), 0x00); + writeRegister(REG_AMRn(0), 0xff); + writeRegister(REG_AMRn(1), 0xff); + writeRegister(REG_AMRn(2), 0xff); + writeRegister(REG_AMRn(3), 0xff); + + modifyRegister(REG_OCR, 0x03, 0x02); // normal output mode + // reset error counters + writeRegister(REG_TXERR, 0x00); + writeRegister(REG_RXERR, 0x00); + + // clear errors and interrupts + readRegister(REG_ECC); + readRegister(REG_IR); + + // normal mode + modifyRegister(REG_MOD, 0x08, 0x08); + modifyRegister(REG_MOD, 0x17, 0x00); + + return 1; +} + +void ESP32SJA1000Class::end() +{ + if (_intrHandle) { + esp_intr_free(_intrHandle); + _intrHandle = NULL; + } + + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN); + + CANControllerClass::end(); +} + +int ESP32SJA1000Class::endPacket(unsigned long timeoutMs) +{ + unsigned long maxTime = millis() + timeoutMs; + + if (!CANControllerClass::endPacket(timeoutMs)) { + return SEND_BEGIN; + } + + // wait for TX buffer to free + while ((readRegister(REG_SR) & 0x04) != 0x04) { + if (timeoutMs && (millis() > maxTime)) { + return SEND_TIMEOUT; + } + yield(); + } + + int dataReg; + + if (_txExtended) { + writeRegister(REG_EFF, 0x80 | (_txRtr ? 0x40 : 0x00) | (0x0f & _txLength)); + writeRegister(REG_EFF + 1, _txId >> 21); + writeRegister(REG_EFF + 2, _txId >> 13); + writeRegister(REG_EFF + 3, _txId >> 5); + writeRegister(REG_EFF + 4, _txId << 3); + + dataReg = REG_EFF + 5; + } else { + writeRegister(REG_SFF, (_txRtr ? 0x40 : 0x00) | (0x0f & _txLength)); + writeRegister(REG_SFF + 1, _txId >> 3); + writeRegister(REG_SFF + 2, _txId << 5); + + dataReg = REG_SFF + 3; + } + + for (int i = 0; i < _txLength; i++) { + writeRegister(dataReg + i, _txData[i]); + } + + if ( _loopback) { + // self reception request + modifyRegister(REG_CMR, 0x1f, 0x10); + } else { + // transmit request + modifyRegister(REG_CMR, 0x1f, 0x01); + } + + // wait for TX complete + while ((readRegister(REG_SR) & 0x08) != 0x08) { + if (readRegister(REG_ECC) == 0xd9) { + modifyRegister(REG_CMR, 0x1f, 0x02); // error, abort + return SEND_ACK; + } else if (timeoutMs && (millis() > maxTime)) { + return SEND_TIMEOUT; + } + yield(); + } + + return SEND_OK; +} + +int ESP32SJA1000Class::parsePacket() +{ + if ((readRegister(REG_SR) & 0x01) != 0x01) { + // no packet + return 0; + } + + _rxExtended = (readRegister(REG_SFF) & 0x80) ? true : false; + _rxRtr = (readRegister(REG_SFF) & 0x40) ? true : false; + _rxDlc = (readRegister(REG_SFF) & 0x0f); + _rxIndex = 0; + + int dataReg; + + if (_rxExtended) { + _rxId = (readRegister(REG_EFF + 1) << 21) | + (readRegister(REG_EFF + 2) << 13) | + (readRegister(REG_EFF + 3) << 5) | + (readRegister(REG_EFF + 4) >> 3); + + dataReg = REG_EFF + 5; + } else { + _rxId = (readRegister(REG_SFF + 1) << 3) | ((readRegister(REG_SFF + 2) >> 5) & 0x07); + + dataReg = REG_SFF + 3; + } + + if (_rxRtr) { + _rxLength = 0; + } else { + _rxLength = _rxDlc; + + for (int i = 0; i < _rxLength; i++) { + _rxData[i] = readRegister(dataReg + i); + } + } + + // release RX buffer + modifyRegister(REG_CMR, 0x04, 0x04); + + return _rxDlc; +} + +void ESP32SJA1000Class::onReceive(void(*callback)(int)) +{ + CANControllerClass::onReceive(callback); + + if (_intrHandle) { + esp_intr_free(_intrHandle); + _intrHandle = NULL; + } + + if (callback) { + esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, ESP32SJA1000Class::onInterrupt, this, &_intrHandle); + } +} + +int ESP32SJA1000Class::filter(int id, int mask) +{ + id &= 0x7ff; + mask = ~(mask & 0x7ff); + + modifyRegister(REG_MOD, 0x17, 0x01); // reset + + writeRegister(REG_ACRn(0), id >> 3); + writeRegister(REG_ACRn(1), id << 5); + writeRegister(REG_ACRn(2), 0x00); + writeRegister(REG_ACRn(3), 0x00); + + writeRegister(REG_AMRn(0), mask >> 3); + writeRegister(REG_AMRn(1), (mask << 5) | 0x1f); + writeRegister(REG_AMRn(2), 0xff); + writeRegister(REG_AMRn(3), 0xff); + + modifyRegister(REG_MOD, 0x17, 0x00); // normal + + return 1; +} + +int ESP32SJA1000Class::filterExtended(long id, long mask) +{ + id &= 0x1FFFFFFF; + mask &= ~(mask & 0x1FFFFFFF); + + modifyRegister(REG_MOD, 0x17, 0x01); // reset + + writeRegister(REG_ACRn(0), id >> 21); + writeRegister(REG_ACRn(1), id >> 13); + writeRegister(REG_ACRn(2), id >> 5); + writeRegister(REG_ACRn(3), id << 3); + + writeRegister(REG_AMRn(0), mask >> 21); + writeRegister(REG_AMRn(1), mask >> 13); + writeRegister(REG_AMRn(2), mask >> 5); + writeRegister(REG_AMRn(3), (mask << 3) | 0x1f); + + modifyRegister(REG_MOD, 0x17, 0x00); // normal + + return 1; +} + +int ESP32SJA1000Class::observe() +{ + modifyRegister(REG_MOD, 0x17, 0x01); // reset + modifyRegister(REG_MOD, 0x17, 0x02); // observe + + return 1; +} + +int ESP32SJA1000Class::loopback() +{ + _loopback = true; + + modifyRegister(REG_MOD, 0x17, 0x01); // reset + modifyRegister(REG_MOD, 0x17, 0x04); // self test mode + + return 1; +} + +int ESP32SJA1000Class::sleep() +{ + modifyRegister(REG_MOD, 0x1f, 0x10); + + return 1; +} + +int ESP32SJA1000Class::wakeup() +{ + modifyRegister(REG_MOD, 0x1f, 0x00); + + return 1; +} + +void ESP32SJA1000Class::setPins(int rx, int tx) +{ + _rxPin = (gpio_num_t)rx; + _txPin = (gpio_num_t)tx; +} + +void ESP32SJA1000Class::dumpRegisters(Stream& out) +{ + for (int i = 0; i < 32; i++) { + byte b = readRegister(i); + + out.print("0x"); + if (i < 16) { + out.print('0'); + } + out.print(i, HEX); + out.print(": 0x"); + if (b < 16) { + out.print('0'); + } + out.println(b, HEX); + } +} + +void ESP32SJA1000Class::handleInterrupt() +{ + uint8_t ir = readRegister(REG_IR); + + if (ir & 0x01) { + // received packet, parse and call callback + parsePacket(); + + _onReceive(available()); + } +} + +uint8_t ESP32SJA1000Class::readRegister(uint8_t address) +{ + volatile uint32_t* reg = (volatile uint32_t*)(REG_BASE + address * 4); + + return *reg; +} + +void ESP32SJA1000Class::modifyRegister(uint8_t address, uint8_t mask, uint8_t value) +{ + volatile uint32_t* reg = (volatile uint32_t*)(REG_BASE + address * 4); + + *reg = (*reg & ~mask) | value; +} + +void ESP32SJA1000Class::writeRegister(uint8_t address, uint8_t value) +{ + volatile uint32_t* reg = (volatile uint32_t*)(REG_BASE + address * 4); + + *reg = value; +} + +void ESP32SJA1000Class::onInterrupt(void* arg) +{ + ((ESP32SJA1000Class*)arg)->handleInterrupt(); +} + +ESP32SJA1000Class CAN; + +#endif diff --git a/lib/arduino-CAN/src/ESP32SJA1000.h b/lib/arduino-CAN/src/ESP32SJA1000.h new file mode 100644 index 0000000000..b1a0d306ce --- /dev/null +++ b/lib/arduino-CAN/src/ESP32SJA1000.h @@ -0,0 +1,65 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifdef ARDUINO_ARCH_ESP32 + +#ifndef ESP32_SJA1000_H +#define ESP32_SJA1000_H + +#include "CANController.h" + +#define DEFAULT_CAN_RX_PIN GPIO_NUM_4 +#define DEFAULT_CAN_TX_PIN GPIO_NUM_5 + +class ESP32SJA1000Class : public CANControllerClass { + +public: + ESP32SJA1000Class(); + virtual ~ESP32SJA1000Class(); + + virtual int begin(long baudRate); + virtual void end(); + + virtual int endPacket(unsigned long timeoutMs = 0); + + virtual int parsePacket(); + + virtual void onReceive(void(*callback)(int)); + + using CANControllerClass::filter; + virtual int filter(int id, int mask); + using CANControllerClass::filterExtended; + virtual int filterExtended(long id, long mask); + + virtual int observe(); + virtual int loopback(); + virtual int sleep(); + virtual int wakeup(); + + void setPins(int rx, int tx); + + void dumpRegisters(Stream& out); + +private: + void reset(); + + void handleInterrupt(); + + uint8_t readRegister(uint8_t address); + void modifyRegister(uint8_t address, uint8_t mask, uint8_t value); + void writeRegister(uint8_t address, uint8_t value); + + static void onInterrupt(void* arg); + +private: + gpio_num_t _rxPin; + gpio_num_t _txPin; + bool _loopback; + intr_handle_t _intrHandle; +}; + +extern ESP32SJA1000Class CAN; + +#endif + +#endif diff --git a/lib/arduino-CAN/src/MCP2515.cpp b/lib/arduino-CAN/src/MCP2515.cpp new file mode 100644 index 0000000000..a153a7693b --- /dev/null +++ b/lib/arduino-CAN/src/MCP2515.cpp @@ -0,0 +1,495 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef ARDUINO_ARCH_ESP32 + +#include "MCP2515.h" + +#define REG_BFPCTRL 0x0c +#define REG_TXRTSCTRL 0x0d + +#define REG_CANCTRL 0x0f + +#define REG_CNF3 0x28 +#define REG_CNF2 0x29 +#define REG_CNF1 0x2a + +#define REG_CANINTE 0x2b +#define REG_CANINTF 0x2c + +#define FLAG_RXnIE(n) (0x01 << n) +#define FLAG_RXnIF(n) (0x01 << n) +#define FLAG_TXnIF(n) (0x04 << n) + +#define REG_RXFnSIDH(n) (0x00 + (n * 4)) +#define REG_RXFnSIDL(n) (0x01 + (n * 4)) +#define REG_RXFnEID8(n) (0x02 + (n * 4)) +#define REG_RXFnEID0(n) (0x03 + (n * 4)) + +#define REG_RXMnSIDH(n) (0x20 + (n * 0x04)) +#define REG_RXMnSIDL(n) (0x21 + (n * 0x04)) +#define REG_RXMnEID8(n) (0x22 + (n * 0x04)) +#define REG_RXMnEID0(n) (0x23 + (n * 0x04)) + +#define REG_TXBnCTRL(n) (0x30 + (n * 0x10)) +#define REG_TXBnSIDH(n) (0x31 + (n * 0x10)) +#define REG_TXBnSIDL(n) (0x32 + (n * 0x10)) +#define REG_TXBnEID8(n) (0x33 + (n * 0x10)) +#define REG_TXBnEID0(n) (0x34 + (n * 0x10)) +#define REG_TXBnDLC(n) (0x35 + (n * 0x10)) +#define REG_TXBnD0(n) (0x36 + (n * 0x10)) + +#define REG_RXBnCTRL(n) (0x60 + (n * 0x10)) +#define REG_RXBnSIDH(n) (0x61 + (n * 0x10)) +#define REG_RXBnSIDL(n) (0x62 + (n * 0x10)) +#define REG_RXBnEID8(n) (0x63 + (n * 0x10)) +#define REG_RXBnEID0(n) (0x64 + (n * 0x10)) +#define REG_RXBnDLC(n) (0x65 + (n * 0x10)) +#define REG_RXBnD0(n) (0x66 + (n * 0x10)) + +#define FLAG_IDE 0x08 +#define FLAG_SRR 0x10 +#define FLAG_RTR 0x40 +#define FLAG_EXIDE 0x08 + +#define FLAG_RXM0 0x20 +#define FLAG_RXM1 0x40 + + +MCP2515Class::MCP2515Class() : + CANControllerClass(), + _spiSettings(10E6, MSBFIRST, SPI_MODE0), + _csPin(MCP2515_DEFAULT_CS_PIN), + _intPin(MCP2515_DEFAULT_INT_PIN), + _clockFrequency(MCP2515_DEFAULT_CLOCK_FREQUENCY) +{ +} + +MCP2515Class::~MCP2515Class() +{ +} + +int MCP2515Class::begin(long baudRate) +{ + CANControllerClass::begin(baudRate); + + pinMode(_csPin, OUTPUT); + + // start SPI + SPI.begin(); + + reset(); + + writeRegister(REG_CANCTRL, 0x80); + if (readRegister(REG_CANCTRL) != 0x80) { + return 0; + } + + const struct { + long clockFrequency; + long baudRate; + uint8_t cnf[3]; + } CNF_MAPPER[] = { + { (long)8E6, (long)1000E3, { 0x00, 0x80, 0x00 } }, + { (long)8E6, (long)500E3, { 0x00, 0x90, 0x02 } }, + { (long)8E6, (long)250E3, { 0x00, 0xb1, 0x05 } }, + { (long)8E6, (long)200E3, { 0x00, 0xb4, 0x06 } }, + { (long)8E6, (long)125E3, { 0x01, 0xb1, 0x05 } }, + { (long)8E6, (long)100E3, { 0x01, 0xb4, 0x06 } }, + { (long)8E6, (long)80E3, { 0x01, 0xbf, 0x07 } }, + { (long)8E6, (long)50E3, { 0x03, 0xb4, 0x06 } }, + { (long)8E6, (long)40E3, { 0x03, 0xbf, 0x07 } }, + { (long)8E6, (long)20E3, { 0x07, 0xbf, 0x07 } }, + { (long)8E6, (long)10E3, { 0x0f, 0xbf, 0x07 } }, + { (long)8E6, (long)5E3, { 0x1f, 0xbf, 0x07 } }, + + { (long)16E6, (long)1000E3, { 0x00, 0xd0, 0x82 } }, + { (long)16E6, (long)500E3, { 0x00, 0xf0, 0x86 } }, + { (long)16E6, (long)250E3, { 0x41, 0xf1, 0x85 } }, + { (long)16E6, (long)200E3, { 0x01, 0xfa, 0x87 } }, + { (long)16E6, (long)125E3, { 0x03, 0xf0, 0x86 } }, + { (long)16E6, (long)100E3, { 0x03, 0xfa, 0x87 } }, + { (long)16E6, (long)80E3, { 0x03, 0xff, 0x87 } }, + { (long)16E6, (long)50E3, { 0x07, 0xfa, 0x87 } }, + { (long)16E6, (long)40E3, { 0x07, 0xff, 0x87 } }, + { (long)16E6, (long)20E3, { 0x0f, 0xff, 0x87 } }, + { (long)16E6, (long)10E3, { 0x1f, 0xff, 0x87 } }, + { (long)16E6, (long)5E3, { 0x3f, 0xff, 0x87 } }, + }; + + const uint8_t* cnf = NULL; + + for (unsigned int i = 0; i < (sizeof(CNF_MAPPER) / sizeof(CNF_MAPPER[0])); i++) { + if (CNF_MAPPER[i].clockFrequency == _clockFrequency && CNF_MAPPER[i].baudRate == baudRate) { + cnf = CNF_MAPPER[i].cnf; + break; + } + } + + if (cnf == NULL) { + return 0; + } + + writeRegister(REG_CNF1, cnf[0]); + writeRegister(REG_CNF2, cnf[1]); + writeRegister(REG_CNF3, cnf[2]); + + writeRegister(REG_CANINTE, FLAG_RXnIE(1) | FLAG_RXnIE(0)); + writeRegister(REG_BFPCTRL, 0x00); + writeRegister(REG_TXRTSCTRL, 0x00); + writeRegister(REG_RXBnCTRL(0), FLAG_RXM1 | FLAG_RXM0); + writeRegister(REG_RXBnCTRL(1), FLAG_RXM1 | FLAG_RXM0); + + writeRegister(REG_CANCTRL, 0x00); + if (readRegister(REG_CANCTRL) != 0x00) { + return 0; + } + + return 1; +} + +void MCP2515Class::end() +{ + SPI.end(); + + CANControllerClass::end(); +} + +int MCP2515Class::endPacket() +{ + if (!CANControllerClass::endPacket()) { + return 0; + } + + int n = 0; + + if (_txExtended) { + writeRegister(REG_TXBnSIDH(n), _txId >> 21); + writeRegister(REG_TXBnSIDL(n), (((_txId >> 18) & 0x07) << 5) | FLAG_EXIDE | ((_txId >> 16) & 0x03)); + writeRegister(REG_TXBnEID8(n), (_txId >> 8) & 0xff); + writeRegister(REG_TXBnEID0(n), _txId & 0xff); + } else { + writeRegister(REG_TXBnSIDH(n), _txId >> 3); + writeRegister(REG_TXBnSIDL(n), _txId << 5); + writeRegister(REG_TXBnEID8(n), 0x00); + writeRegister(REG_TXBnEID0(n), 0x00); + } + + if (_txRtr) { + writeRegister(REG_TXBnDLC(n), 0x40 | _txLength); + } else { + writeRegister(REG_TXBnDLC(n), _txLength); + + for (int i = 0; i < _txLength; i++) { + writeRegister(REG_TXBnD0(n) + i, _txData[i]); + } + } + + writeRegister(REG_TXBnCTRL(n), 0x08); + + bool aborted = false; + + while (readRegister(REG_TXBnCTRL(n)) & 0x08) { + if (readRegister(REG_TXBnCTRL(n)) & 0x10) { + // abort + aborted = true; + + modifyRegister(REG_CANCTRL, 0x10, 0x10); + } + + yield(); + } + + if (aborted) { + // clear abort command + modifyRegister(REG_CANCTRL, 0x10, 0x00); + } + + modifyRegister(REG_CANINTF, FLAG_TXnIF(n), 0x00); + + return (readRegister(REG_TXBnCTRL(n)) & 0x70) ? 0 : 1; +} + +int MCP2515Class::parsePacket() +{ + int n; + + uint8_t intf = readRegister(REG_CANINTF); + + if (intf & FLAG_RXnIF(0)) { + n = 0; + } else if (intf & FLAG_RXnIF(1)) { + n = 1; + } else { + _rxId = -1; + _rxExtended = false; + _rxRtr = false; + _rxLength = 0; + return 0; + } + + _rxExtended = (readRegister(REG_RXBnSIDL(n)) & FLAG_IDE) ? true : false; + + uint32_t idA = ((readRegister(REG_RXBnSIDH(n)) << 3) & 0x07f8) | ((readRegister(REG_RXBnSIDL(n)) >> 5) & 0x07); + if (_rxExtended) { + uint32_t idB = (((uint32_t)(readRegister(REG_RXBnSIDL(n)) & 0x03) << 16) & 0x30000) | ((readRegister(REG_RXBnEID8(n)) << 8) & 0xff00) | readRegister(REG_RXBnEID0(n)); + + _rxId = (idA << 18) | idB; + _rxRtr = (readRegister(REG_RXBnDLC(n)) & FLAG_RTR) ? true : false; + } else { + _rxId = idA; + _rxRtr = (readRegister(REG_RXBnSIDL(n)) & FLAG_SRR) ? true : false; + } + _rxDlc = readRegister(REG_RXBnDLC(n)) & 0x0f; + _rxIndex = 0; + + if (_rxRtr) { + _rxLength = 0; + } else { + _rxLength = _rxDlc; + + for (int i = 0; i < _rxLength; i++) { + _rxData[i] = readRegister(REG_RXBnD0(n) + i); + } + } + + modifyRegister(REG_CANINTF, FLAG_RXnIF(n), 0x00); + + return _rxDlc; +} + +void MCP2515Class::onReceive(void(*callback)(int)) +{ + CANControllerClass::onReceive(callback); + + pinMode(_intPin, INPUT); + + if (callback) { + SPI.usingInterrupt(digitalPinToInterrupt(_intPin)); + attachInterrupt(digitalPinToInterrupt(_intPin), MCP2515Class::onInterrupt, LOW); + } else { + detachInterrupt(digitalPinToInterrupt(_intPin)); +#ifdef SPI_HAS_NOTUSINGINTERRUPT + SPI.notUsingInterrupt(digitalPinToInterrupt(_intPin)); +#endif + } +} + +int MCP2515Class::filter(int id, int mask) +{ + id &= 0x7ff; + mask &= 0x7ff; + + // config mode + writeRegister(REG_CANCTRL, 0x80); + if (readRegister(REG_CANCTRL) != 0x80) { + return 0; + } + + for (int n = 0; n < 2; n++) { + // standard only + writeRegister(REG_RXBnCTRL(n), FLAG_RXM0); + writeRegister(REG_RXBnCTRL(n), FLAG_RXM0); + + writeRegister(REG_RXMnSIDH(n), mask >> 3); + writeRegister(REG_RXMnSIDL(n), mask << 5); + writeRegister(REG_RXMnEID8(n), 0); + writeRegister(REG_RXMnEID0(n), 0); + } + + for (int n = 0; n < 6; n++) { + writeRegister(REG_RXFnSIDH(n), id >> 3); + writeRegister(REG_RXFnSIDL(n), id << 5); + writeRegister(REG_RXFnEID8(n), 0); + writeRegister(REG_RXFnEID0(n), 0); + } + + // normal mode + writeRegister(REG_CANCTRL, 0x00); + if (readRegister(REG_CANCTRL) != 0x00) { + return 0; + } + + return 1; +} + +int MCP2515Class::filterExtended(long id, long mask) +{ + id &= 0x1FFFFFFF; + mask &= 0x1FFFFFFF; + + // config mode + writeRegister(REG_CANCTRL, 0x80); + if (readRegister(REG_CANCTRL) != 0x80) { + return 0; + } + + for (int n = 0; n < 2; n++) { + // extended only + writeRegister(REG_RXBnCTRL(n), FLAG_RXM1); + writeRegister(REG_RXBnCTRL(n), FLAG_RXM1); + + writeRegister(REG_RXMnSIDH(n), mask >> 21); + writeRegister(REG_RXMnSIDL(n), (((mask >> 18) & 0x03) << 5) | FLAG_EXIDE | ((mask >> 16) & 0x03)); + writeRegister(REG_RXMnEID8(n), (mask >> 8) & 0xff); + writeRegister(REG_RXMnEID0(n), mask & 0xff); + } + + for (int n = 0; n < 6; n++) { + writeRegister(REG_RXFnSIDH(n), id >> 21); + writeRegister(REG_RXFnSIDL(n), (((id >> 18) & 0x03) << 5) | FLAG_EXIDE | ((id >> 16) & 0x03)); + writeRegister(REG_RXFnEID8(n), (id >> 8) & 0xff); + writeRegister(REG_RXFnEID0(n), id & 0xff); + } + + // normal mode + writeRegister(REG_CANCTRL, 0x00); + if (readRegister(REG_CANCTRL) != 0x00) { + return 0; + } + + return 1; +} + +int MCP2515Class::observe() +{ + writeRegister(REG_CANCTRL, 0x60); + if (readRegister(REG_CANCTRL) != 0x60) { + return 0; + } + + return 1; +} + +int MCP2515Class::loopback() +{ + writeRegister(REG_CANCTRL, 0x40); + if (readRegister(REG_CANCTRL) != 0x40) { + return 0; + } + + return 1; +} + +int MCP2515Class::sleep() +{ + writeRegister(REG_CANCTRL, 0x01); + if (readRegister(REG_CANCTRL) != 0x01) { + return 0; + } + + return 1; +} + +int MCP2515Class::wakeup() +{ + writeRegister(REG_CANCTRL, 0x00); + if (readRegister(REG_CANCTRL) != 0x00) { + return 0; + } + + return 1; +} + +void MCP2515Class::setPins(int cs, int irq) +{ + _csPin = cs; + _intPin = irq; +} + +void MCP2515Class::setSPIFrequency(uint32_t frequency) +{ + _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); +} + +void MCP2515Class::setClockFrequency(long clockFrequency) +{ + _clockFrequency = clockFrequency; +} + +void MCP2515Class::dumpRegisters(Stream& out) +{ + for (int i = 0; i < 128; i++) { + byte b = readRegister(i); + + out.print("0x"); + if (i < 16) { + out.print('0'); + } + out.print(i, HEX); + out.print(": 0x"); + if (b < 16) { + out.print('0'); + } + out.println(b, HEX); + } +} + +void MCP2515Class::reset() +{ + SPI.beginTransaction(_spiSettings); + digitalWrite(_csPin, LOW); + SPI.transfer(0xc0); + digitalWrite(_csPin, HIGH); + SPI.endTransaction(); + + delayMicroseconds(10); +} + +void MCP2515Class::handleInterrupt() +{ + if (readRegister(REG_CANINTF) == 0) { + return; + } + + while (parsePacket() || _rxId != -1) { + _onReceive(available()); + } +} + +uint8_t MCP2515Class::readRegister(uint8_t address) +{ + uint8_t value; + + SPI.beginTransaction(_spiSettings); + digitalWrite(_csPin, LOW); + SPI.transfer(0x03); + SPI.transfer(address); + value = SPI.transfer(0x00); + digitalWrite(_csPin, HIGH); + SPI.endTransaction(); + + return value; +} + +void MCP2515Class::modifyRegister(uint8_t address, uint8_t mask, uint8_t value) +{ + SPI.beginTransaction(_spiSettings); + digitalWrite(_csPin, LOW); + SPI.transfer(0x05); + SPI.transfer(address); + SPI.transfer(mask); + SPI.transfer(value); + digitalWrite(_csPin, HIGH); + SPI.endTransaction(); +} + +void MCP2515Class::writeRegister(uint8_t address, uint8_t value) +{ + SPI.beginTransaction(_spiSettings); + digitalWrite(_csPin, LOW); + SPI.transfer(0x02); + SPI.transfer(address); + SPI.transfer(value); + digitalWrite(_csPin, HIGH); + SPI.endTransaction(); +} + +void MCP2515Class::onInterrupt() +{ + CAN.handleInterrupt(); +} + +MCP2515Class CAN; + +#endif diff --git a/lib/arduino-CAN/src/MCP2515.h b/lib/arduino-CAN/src/MCP2515.h new file mode 100644 index 0000000000..2f0444f90f --- /dev/null +++ b/lib/arduino-CAN/src/MCP2515.h @@ -0,0 +1,77 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef ARDUINO_ARCH_ESP32 + +#ifndef MCP2515_H +#define MCP2515_H + +#include + +#include "CANController.h" + +#define MCP2515_DEFAULT_CLOCK_FREQUENCY 16e6 + +#if defined(ARDUINO_ARCH_SAMD) && defined(PIN_SPI_MISO) && defined(PIN_SPI_MOSI) && defined(PIN_SPI_SCK) && (PIN_SPI_MISO == 10) && (PIN_SPI_MOSI == 8) && (PIN_SPI_SCK == 9) +// Arduino MKR board: MKR CAN shield CS is pin 3, INT is pin 7 +#define MCP2515_DEFAULT_CS_PIN 3 +#define MCP2515_DEFAULT_INT_PIN 7 +#else +#define MCP2515_DEFAULT_CS_PIN 10 +#define MCP2515_DEFAULT_INT_PIN 2 +#endif + +class MCP2515Class : public CANControllerClass { + +public: + MCP2515Class(); + virtual ~MCP2515Class(); + + virtual int begin(long baudRate); + virtual void end(); + + virtual int endPacket(); + + virtual int parsePacket(); + + virtual void onReceive(void(*callback)(int)); + + using CANControllerClass::filter; + virtual int filter(int id, int mask); + using CANControllerClass::filterExtended; + virtual int filterExtended(long id, long mask); + + virtual int observe(); + virtual int loopback(); + virtual int sleep(); + virtual int wakeup(); + + void setPins(int cs = MCP2515_DEFAULT_CS_PIN, int irq = MCP2515_DEFAULT_INT_PIN); + void setSPIFrequency(uint32_t frequency); + void setClockFrequency(long clockFrequency); + + void dumpRegisters(Stream& out); + +private: + void reset(); + + void handleInterrupt(); + + uint8_t readRegister(uint8_t address); + void modifyRegister(uint8_t address, uint8_t mask, uint8_t value); + void writeRegister(uint8_t address, uint8_t value); + + static void onInterrupt(); + +private: + SPISettings _spiSettings; + int _csPin; + int _intPin; + long _clockFrequency; +}; + +extern MCP2515Class CAN; + +#endif + +#endif diff --git a/src/src/CustomBuild/ESPEasyDefaults.h b/src/src/CustomBuild/ESPEasyDefaults.h index 389011813b..3ecfc35363 100644 --- a/src/src/CustomBuild/ESPEasyDefaults.h +++ b/src/src/CustomBuild/ESPEasyDefaults.h @@ -389,6 +389,10 @@ #endif +#ifndef DEFAULT_CAN_BAUDRATE +#define DEFAULT_CAN_BAUDRATE 500e3 +#endif + // --- Defaults to be used for custom automatic provisioning builds ------------------------------------ #if FEATURE_CUSTOM_PROVISIONING diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 45fade5f86..2d8716ff3d 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -354,6 +354,9 @@ class SettingsStruct_tmpl memcpy(&VariousBits_2, &value, sizeof(VariousBits_2)); } + bool isCAN_valid() const; + + bool isCAN_pin(int8_t pin) const; unsigned long PID = 0; int Version = 0; @@ -567,6 +570,11 @@ class SettingsStruct_tmpl int8_t console_serial_rxpin = DEFAULT_CONSOLE_PORT_RXPIN; int8_t console_serial_txpin = DEFAULT_CONSOLE_PORT_TXPIN; uint8_t console_serial0_fallback = DEFAULT_CONSOLE_SER0_FALLBACK; + + uint8_t CAN_Tx_pin = -1; + uint8_t CAN_Rx_pin = -1; + long CAN_baudrate = DEFAULT_CAN_BAUDRATE; + int CAN_node_id = 0; // 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 8c2732e1f5..b222fda893 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -974,6 +974,18 @@ bool SettingsStruct_tmpl::isI2CEnabled() const { (I2C_clockSpeed_Slow > 0); } +template +bool SettingsStruct_tmpl::isCAN_valid() const { + return CAN_Rx_pin != -1 && CAN_Tx_pin != -1 && CAN_baudrate == 500e3; +} + + +template +bool SettingsStruct_tmpl::isCAN_pin(int8_t pin) const { + if (pin < 0) { return false; } + return CAN_Rx_pin == pin || CAN_Tx_pin == pin; +} + template bool SettingsStruct_tmpl::isEthernetPin(int8_t pin) const { #if FEATURE_ETHERNET diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index b56db5c670..e3e59ab14d 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -1,266 +1,266 @@ -#include "../Helpers/ESPEasy_checks.h" - - -#include "../../ESPEasy_common.h" -#ifndef BUILD_MINIMAL_OTA - -#include "../DataStructs/CRCStruct.h" -#include "../DataStructs/ControllerSettingsStruct.h" -#include "../DataStructs/DeviceStruct.h" -#include "../DataStructs/ESPEasy_EventStruct.h" -#include "../DataStructs/ExtraTaskSettingsStruct.h" -#include "../DataStructs/FactoryDefaultPref.h" -#include "../DataStructs/GpioFactorySettingsStruct.h" -#include "../DataStructs/LogStruct.h" -#if FEATURE_ESPEASY_P2P -#include "../DataStructs/NodeStruct.h" -#endif -#include "../DataStructs/PortStatusStruct.h" -#include "../DataStructs/ProtocolStruct.h" -#if FEATURE_CUSTOM_PROVISIONING -#include "../DataStructs/ProvisioningStruct.h" -#endif -#include "../DataStructs/RTCStruct.h" -#include "../DataStructs/SecurityStruct.h" -#include "../DataStructs/SettingsStruct.h" -#include "../DataStructs/SystemTimerStruct.h" - -#include "../Globals/ExtraTaskSettings.h" -#include "../Globals/Settings.h" - -#include "../Helpers/ESPEasy_Storage.h" -#include "../Helpers/StringConverter.h" - -#include - -#ifdef USES_C013 -#include "../DataStructs/C013_p2p_SensorDataStruct.h" -#include "../DataStructs/C013_p2p_SensorInfoStruct.h" -#endif - -#ifdef USES_C016 -#include "../ControllerQueue/C016_queue_element.h" -#endif - -#if FEATURE_NOTIFIER -#include "../DataStructs/NotificationStruct.h" -#include "../DataStructs/NotificationSettingsStruct.h" -#endif // if FEATURE_NOTIFIER - - -// ******************************************************************************** -// Check struct sizes at compile time -// Usage: -// struct foo -// { -// char bla[16]; -// }; -// -// check_size(); -// ******************************************************************************** -template -void check_size() { - static_assert(ExpectedSize == RealSize, ""); -} - - - -// ******************************************************************************** -// Check struct sizes at compile time -// Usage: -// struct X { int a, b, c, d; } -// static_assert(ExpectedSize == offsetof(X, c), ""); -// ******************************************************************************** - -void run_compiletime_checks() { - #ifndef LIMIT_BUILD_SIZE - check_size(); - check_size(); - #ifdef ESP32 - constexpr unsigned int SettingsStructSize = (340 + 84 * TASKS_MAX); - #endif - #ifdef ESP8266 - constexpr unsigned int SettingsStructSize = (316 + 84 * TASKS_MAX); - #endif - #if FEATURE_CUSTOM_PROVISIONING - check_size(); - #endif - check_size(); - check_size(); - #if FEATURE_NOTIFIER - check_size(); - #endif // if FEATURE_NOTIFIER - check_size(); - #if ESP_IDF_VERSION_MAJOR > 3 - // String class has increased with 4 bytes - check_size(); // Is not stored - #else - check_size(); // Is not stored - #endif - - - // LogStruct is mainly dependent on the number of lines. - // Has to be round up to multiple of 4. - #if ESP_IDF_VERSION_MAJOR > 3 - // String class has increased with 4 bytes - const unsigned int LogStructSize = ((13u + 24 * LOG_STRUCT_MESSAGE_LINES) + 3) & ~3; - #else - const unsigned int LogStructSize = ((13u + 20 * LOG_STRUCT_MESSAGE_LINES) + 3) & ~3; - #endif - check_size(); // Is not stored - check_size(); // Is not stored - check_size(); - #if FEATURE_NOTIFIER - check_size(); - #endif // if FEATURE_NOTIFIER - #if FEATURE_ESPEASY_P2P - check_size(); - #endif - #if FEATURE_CUSTOM_PROVISIONING - check_size(); - #endif - check_size(); - check_size(); - check_size(); - check_size(); - check_size(); - #ifdef USES_C013 - check_size(); - check_size(); - #endif - #ifdef USES_C016 - check_size(); - #endif - - - #if FEATURE_NON_STANDARD_24_TASKS && defined(ESP8266) - static_assert(TASKS_MAX == 24, "TASKS_MAX invalid size"); - #endif - - // Check for alignment issues at compile time - { - const unsigned int ControllerUser_offset = 256u; - static_assert(ControllerUser_offset == offsetof(SecurityStruct, ControllerUser), ""); - - const unsigned int ControllerPassword_offset = 256u + (CONTROLLER_MAX * 26); - static_assert(ControllerPassword_offset == offsetof(SecurityStruct, ControllerPassword), ""); - - const unsigned int Password_offset = ControllerPassword_offset + (CONTROLLER_MAX * 64); - static_assert(Password_offset == offsetof(SecurityStruct, Password), ""); - - const unsigned int AllowedIPrangeLow_offset = Password_offset + 26; - static_assert(AllowedIPrangeLow_offset == offsetof(SecurityStruct, AllowedIPrangeLow), ""); - - const unsigned int IPblockLevel_offset = AllowedIPrangeLow_offset + 8; - static_assert(IPblockLevel_offset == offsetof(SecurityStruct, IPblockLevel), ""); - - const unsigned int ProgmemMd5_offset = IPblockLevel_offset + 1; - static_assert(ProgmemMd5_offset == offsetof(SecurityStruct, ProgmemMd5), ""); - - const unsigned int md5_offset = ProgmemMd5_offset + 16; - static_assert(md5_offset == offsetof(SecurityStruct, md5), ""); - - #if FEATURE_CUSTOM_PROVISIONING - const unsigned int prov_pass_offset = 62u; - static_assert(prov_pass_offset == offsetof(ProvisioningStruct, pass), ""); - - - #endif - } - - - static_assert(192u == offsetof(SettingsStruct, Protocol), ""); - static_assert(195u == offsetof(SettingsStruct, Notification), "CONTROLLER_MAX has changed?"); - static_assert(198u == offsetof(SettingsStruct, TaskDeviceNumber), "NOTIFICATION_MAX has changed?"); - - // All settings related to N_TASKS - static_assert((200 + 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. - //const size_t offset = offsetof(SettingsStruct, ControllerEnabled); - //check_size(); - - #endif -} - -#ifndef LIMIT_BUILD_SIZE -String ReportOffsetErrorInStruct(const String& structname, size_t offset) { - String error; - if (error.reserve(48 + structname.length())) { - error = F("Error: Incorrect offset in struct: "); - error += structname; - error += wrap_braces(String(offset)); - } - return error; -} -#endif - -/*********************************************************************************************\ -* Analyze SettingsStruct and report inconsistencies -* Not a member function to be able to use the F-macro -\*********************************************************************************************/ -bool SettingsCheck(String& error) { - error = String(); - #ifndef LIMIT_BUILD_SIZE -#ifdef esp8266 - size_t offset = offsetof(SettingsStruct, ResetFactoryDefaultPreference); - - if (offset != 1224) { - error = ReportOffsetErrorInStruct(F("SettingsStruct"), offset); - } -#endif // ifdef esp8266 - - if (!Settings.networkSettingsEmpty()) { - if ((Settings.IP[0] == 0) || (Settings.Gateway[0] == 0) || (Settings.Subnet[0] == 0) || (Settings.DNS[0] == 0)) { - error += F("Error: Either fill all IP settings fields or leave all empty"); - } - } - - #endif - - return error.isEmpty(); -} - -#include "../Helpers/Numerical.h" - -String checkTaskSettings(taskIndex_t taskIndex) { - String err = LoadTaskSettings(taskIndex); - #if !defined(PLUGIN_BUILD_MINIMAL_OTA) && !defined(ESP8266_1M) - if (err.length() > 0) return err; - if (!ExtraTaskSettings.checkUniqueValueNames()) { - return F("Use unique value names"); - } - if (!ExtraTaskSettings.checkInvalidCharInNames()) { - return concat(F("Invalid character in name. Do not use space or '"), ExtraTaskSettingsStruct::getInvalidCharsForNames()) + '\''; - } - String deviceName = ExtraTaskSettings.TaskDeviceName; - NumericalType detectedType; - if (isNumerical(deviceName, detectedType)) { - return F("Invalid name. Should not be numeric."); - } - if (deviceName.isEmpty()) { - if (Settings.TaskDeviceEnabled[taskIndex]) { - // Decide what to do here, for now give a warning when task is enabled. - return F("Warning: Task Device Name is empty. It is adviced to give tasks an unique name"); - } - } - // Do not use the cached function findTaskIndexByName since that one does rely on the fact names should be unique. - for (taskIndex_t i = 0; i < TASKS_MAX; ++i) { - if (i != taskIndex && Settings.TaskDeviceEnabled[i]) { - LoadTaskSettings(i); - if (ExtraTaskSettings.TaskDeviceName[0] != 0) { - if (strcasecmp(ExtraTaskSettings.TaskDeviceName, deviceName.c_str()) == 0) { - err = F("Task Device Name is not unique, conflicts with task ID #"); - err += (i+1); -// return err; - } - } - } - } - - err += LoadTaskSettings(taskIndex); - #endif - return err; -} +#include "../Helpers/ESPEasy_checks.h" + + +#include "../../ESPEasy_common.h" +#ifndef BUILD_MINIMAL_OTA + +#include "../DataStructs/CRCStruct.h" +#include "../DataStructs/ControllerSettingsStruct.h" +#include "../DataStructs/DeviceStruct.h" +#include "../DataStructs/ESPEasy_EventStruct.h" +#include "../DataStructs/ExtraTaskSettingsStruct.h" +#include "../DataStructs/FactoryDefaultPref.h" +#include "../DataStructs/GpioFactorySettingsStruct.h" +#include "../DataStructs/LogStruct.h" +#if FEATURE_ESPEASY_P2P +#include "../DataStructs/NodeStruct.h" +#endif +#include "../DataStructs/PortStatusStruct.h" +#include "../DataStructs/ProtocolStruct.h" +#if FEATURE_CUSTOM_PROVISIONING +#include "../DataStructs/ProvisioningStruct.h" +#endif +#include "../DataStructs/RTCStruct.h" +#include "../DataStructs/SecurityStruct.h" +#include "../DataStructs/SettingsStruct.h" +#include "../DataStructs/SystemTimerStruct.h" + +#include "../Globals/ExtraTaskSettings.h" +#include "../Globals/Settings.h" + +#include "../Helpers/ESPEasy_Storage.h" +#include "../Helpers/StringConverter.h" + +#include + +#ifdef USES_C013 +#include "../DataStructs/C013_p2p_SensorDataStruct.h" +#include "../DataStructs/C013_p2p_SensorInfoStruct.h" +#endif + +#ifdef USES_C016 +#include "../ControllerQueue/C016_queue_element.h" +#endif + +#if FEATURE_NOTIFIER +#include "../DataStructs/NotificationStruct.h" +#include "../DataStructs/NotificationSettingsStruct.h" +#endif // if FEATURE_NOTIFIER + + +// ******************************************************************************** +// Check struct sizes at compile time +// Usage: +// struct foo +// { +// char bla[16]; +// }; +// +// check_size(); +// ******************************************************************************** +template +void check_size() { + static_assert(ExpectedSize == RealSize, ""); +} + + + +// ******************************************************************************** +// Check struct sizes at compile time +// Usage: +// struct X { int a, b, c, d; } +// static_assert(ExpectedSize == offsetof(X, c), ""); +// ******************************************************************************** + +void run_compiletime_checks() { + #ifndef LIMIT_BUILD_SIZE + check_size(); + check_size(); + #ifdef ESP32 + constexpr unsigned int SettingsStructSize = (352 + 84 * TASKS_MAX); + #endif + #ifdef ESP8266 + constexpr unsigned int SettingsStructSize = (316 + 84 * TASKS_MAX); + #endif + #if FEATURE_CUSTOM_PROVISIONING + check_size(); + #endif + check_size(); + check_size(); + #if FEATURE_NOTIFIER + check_size(); + #endif // if FEATURE_NOTIFIER + check_size(); + #if ESP_IDF_VERSION_MAJOR > 3 + // String class has increased with 4 bytes + check_size(); // Is not stored + #else + check_size(); // Is not stored + #endif + + + // LogStruct is mainly dependent on the number of lines. + // Has to be round up to multiple of 4. + #if ESP_IDF_VERSION_MAJOR > 3 + // String class has increased with 4 bytes + const unsigned int LogStructSize = ((13u + 24 * LOG_STRUCT_MESSAGE_LINES) + 3) & ~3; + #else + const unsigned int LogStructSize = ((13u + 20 * LOG_STRUCT_MESSAGE_LINES) + 3) & ~3; + #endif + check_size(); // Is not stored + check_size(); // Is not stored + check_size(); + #if FEATURE_NOTIFIER + check_size(); + #endif // if FEATURE_NOTIFIER + #if FEATURE_ESPEASY_P2P + check_size(); + #endif + #if FEATURE_CUSTOM_PROVISIONING + check_size(); + #endif + check_size(); + check_size(); + check_size(); + check_size(); + check_size(); + #ifdef USES_C013 + check_size(); + check_size(); + #endif + #ifdef USES_C016 + check_size(); + #endif + + + #if FEATURE_NON_STANDARD_24_TASKS && defined(ESP8266) + static_assert(TASKS_MAX == 24, "TASKS_MAX invalid size"); + #endif + + // Check for alignment issues at compile time + { + const unsigned int ControllerUser_offset = 256u; + static_assert(ControllerUser_offset == offsetof(SecurityStruct, ControllerUser), ""); + + const unsigned int ControllerPassword_offset = 256u + (CONTROLLER_MAX * 26); + static_assert(ControllerPassword_offset == offsetof(SecurityStruct, ControllerPassword), ""); + + const unsigned int Password_offset = ControllerPassword_offset + (CONTROLLER_MAX * 64); + static_assert(Password_offset == offsetof(SecurityStruct, Password), ""); + + const unsigned int AllowedIPrangeLow_offset = Password_offset + 26; + static_assert(AllowedIPrangeLow_offset == offsetof(SecurityStruct, AllowedIPrangeLow), ""); + + const unsigned int IPblockLevel_offset = AllowedIPrangeLow_offset + 8; + static_assert(IPblockLevel_offset == offsetof(SecurityStruct, IPblockLevel), ""); + + const unsigned int ProgmemMd5_offset = IPblockLevel_offset + 1; + static_assert(ProgmemMd5_offset == offsetof(SecurityStruct, ProgmemMd5), ""); + + const unsigned int md5_offset = ProgmemMd5_offset + 16; + static_assert(md5_offset == offsetof(SecurityStruct, md5), ""); + + #if FEATURE_CUSTOM_PROVISIONING + const unsigned int prov_pass_offset = 62u; + static_assert(prov_pass_offset == offsetof(ProvisioningStruct, pass), ""); + + + #endif + } + + + static_assert(192u == offsetof(SettingsStruct, Protocol), ""); + static_assert(195u == offsetof(SettingsStruct, Notification), "CONTROLLER_MAX has changed?"); + static_assert(198u == offsetof(SettingsStruct, TaskDeviceNumber), "NOTIFICATION_MAX has changed?"); + + // All settings related to N_TASKS + static_assert((200 + 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. + //const size_t offset = offsetof(SettingsStruct, ControllerEnabled); + //check_size(); + + #endif +} + +#ifndef LIMIT_BUILD_SIZE +String ReportOffsetErrorInStruct(const String& structname, size_t offset) { + String error; + if (error.reserve(48 + structname.length())) { + error = F("Error: Incorrect offset in struct: "); + error += structname; + error += wrap_braces(String(offset)); + } + return error; +} +#endif + +/*********************************************************************************************\ +* Analyze SettingsStruct and report inconsistencies +* Not a member function to be able to use the F-macro +\*********************************************************************************************/ +bool SettingsCheck(String& error) { + error = String(); + #ifndef LIMIT_BUILD_SIZE +#ifdef esp8266 + size_t offset = offsetof(SettingsStruct, ResetFactoryDefaultPreference); + + if (offset != 1224) { + error = ReportOffsetErrorInStruct(F("SettingsStruct"), offset); + } +#endif // ifdef esp8266 + + if (!Settings.networkSettingsEmpty()) { + if ((Settings.IP[0] == 0) || (Settings.Gateway[0] == 0) || (Settings.Subnet[0] == 0) || (Settings.DNS[0] == 0)) { + error += F("Error: Either fill all IP settings fields or leave all empty"); + } + } + + #endif + + return error.isEmpty(); +} + +#include "../Helpers/Numerical.h" + +String checkTaskSettings(taskIndex_t taskIndex) { + String err = LoadTaskSettings(taskIndex); + #if !defined(PLUGIN_BUILD_MINIMAL_OTA) && !defined(ESP8266_1M) + if (err.length() > 0) return err; + if (!ExtraTaskSettings.checkUniqueValueNames()) { + return F("Use unique value names"); + } + if (!ExtraTaskSettings.checkInvalidCharInNames()) { + return concat(F("Invalid character in name. Do not use space or '"), ExtraTaskSettingsStruct::getInvalidCharsForNames()) + '\''; + } + String deviceName = ExtraTaskSettings.TaskDeviceName; + NumericalType detectedType; + if (isNumerical(deviceName, detectedType)) { + return F("Invalid name. Should not be numeric."); + } + if (deviceName.isEmpty()) { + if (Settings.TaskDeviceEnabled[taskIndex]) { + // Decide what to do here, for now give a warning when task is enabled. + return F("Warning: Task Device Name is empty. It is adviced to give tasks an unique name"); + } + } + // Do not use the cached function findTaskIndexByName since that one does rely on the fact names should be unique. + for (taskIndex_t i = 0; i < TASKS_MAX; ++i) { + if (i != taskIndex && Settings.TaskDeviceEnabled[i]) { + LoadTaskSettings(i); + if (ExtraTaskSettings.TaskDeviceName[0] != 0) { + if (strcasecmp(ExtraTaskSettings.TaskDeviceName, deviceName.c_str()) == 0) { + err = F("Task Device Name is not unique, conflicts with task ID #"); + err += (i+1); +// return err; + } + } + } + } + + err += LoadTaskSettings(taskIndex); + #endif + return err; +} #endif \ No newline at end of file diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index 47862a9638..7c50c8eac3 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -1,1033 +1,1062 @@ -#include "../Helpers/Hardware.h" - -#include "../Commands/GPIO.h" -#include "../CustomBuild/ESPEasyLimits.h" -#include "../DataTypes/SPI_options.h" -#include "../ESPEasyCore/ESPEasyGPIO.h" -#include "../ESPEasyCore/ESPEasy_Log.h" - -#include "../Globals/Device.h" -#include "../Globals/ESPEasyWiFiEvent.h" -#include "../Globals/ExtraTaskSettings.h" -#include "../Globals/Settings.h" -#include "../Globals/Statistics.h" -#include "../Globals/GlobalMapPortStatus.h" - -#include "../Helpers/ESPEasy_FactoryDefault.h" -#include "../Helpers/ESPEasy_Storage.h" -#include "../Helpers/FS_Helper.h" -#include "../Helpers/Hardware_device_info.h" -#include "../Helpers/Hardware_GPIO.h" -#include "../Helpers/Hardware_I2C.h" -#include "../Helpers/I2C_access.h" -#include "../Helpers/Misc.h" -#include "../Helpers/PortStatus.h" -#include "../Helpers/StringConverter.h" - - -#if defined(ESP8266) - # include -#endif // if defined(ESP8266) -#if defined(ESP32) - # include -#endif // if defined(ESP32) - -// #include "../../ESPEasy-Globals.h" - -#ifdef ESP32 - # include - # include - # include - # include - # include - - # if ESP_IDF_VERSION_MAJOR == 4 - # if CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3 - # include - # include - # include - # elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2 - # include - # include - # include - # elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 - # include - # include - # elif CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 - # include - # include - # include - # else // if CONFIG_IDF_TARGET_ESP32S3 - # error Target CONFIG_IDF_TARGET is not supported - # endif // if CONFIG_IDF_TARGET_ESP32S3 - # else // ESP32 IDF 5.x and later - # include - # include - # include - # endif // if ESP_IDF_VERSION_MAJOR == 4 - - -# if CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3 - # define HAS_HALL_EFFECT_SENSOR 0 - # define HAS_TOUCH_GPIO 1 -# elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2 - # define HAS_HALL_EFFECT_SENSOR 0 - # define HAS_TOUCH_GPIO 1 -# elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6 - # define HAS_HALL_EFFECT_SENSOR 0 - # define HAS_TOUCH_GPIO 0 -# elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 - # define HAS_HALL_EFFECT_SENSOR 0 - # define HAS_TOUCH_GPIO 0 -# elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2 - # define HAS_HALL_EFFECT_SENSOR 0 - # define HAS_TOUCH_GPIO 0 -# elif CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 - # if ESP_IDF_VERSION_MAJOR < 5 - # define HAS_HALL_EFFECT_SENSOR 1 - # else // if ESP_IDF_VERSION_MAJOR < 5 - -// Support for Hall Effect sensor was removed in ESP_IDF 5.x - # define HAS_HALL_EFFECT_SENSOR 0 - # endif // if ESP_IDF_VERSION_MAJOR < 5 - # define HAS_TOUCH_GPIO 1 -# else // if CONFIG_IDF_TARGET_ESP32S3 - # error Target CONFIG_IDF_TARGET is not supported -# endif // if CONFIG_IDF_TARGET_ESP32S3 - - -# ifndef HAS_TOUCH_GPIO -# define HAS_TOUCH_GPIO 0 -# endif // ifndef HAS_TOUCH_GPIO - - -# if ESP_IDF_VERSION_MAJOR >= 5 - -# include -# include -# include -# include - -// #include - -# endif // if ESP_IDF_VERSION_MAJOR >= 5 - -# include "../Helpers/Hardware_ADC_cali.h" - -#if FEATURE_ETHERNET -#include -#endif - -#endif // ifdef ESP32 - - -#if FEATURE_SD -# include -#endif // if FEATURE_SD - - -#include - - -# define GPIO_PLUGIN_ID 1 - -/********************************************************************************************\ - * Initialize specific hardware settings (only global ones, others are set through devices) - \*********************************************************************************************/ -void hardwareInit() -{ - // set GPIO pins state if not set to default - bool hasPullUp, hasPullDown; - - for (int gpio = 0; gpio <= MAX_GPIO; ++gpio) { - const bool serialPinConflict = isSerialConsolePin(gpio); - - if (!serialPinConflict) { - const uint32_t key = createKey(PLUGIN_GPIO, gpio); - #ifdef ESP32 - checkAndClearPWM(key); - #endif // ifdef ESP32 - - if (getGpioPullResistor(gpio, hasPullUp, hasPullDown)) { - PinBootState bootState = Settings.getPinBootState(gpio); - #if FEATURE_ETHERNET -/* - if (Settings.ETH_Pin_power_rst == gpio) - { - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = F("ETH : Reset ETH module on pin "); - log += Settings.ETH_Pin_power_rst; - addLog(LOG_LEVEL_INFO, log); - } - bootState = PinBootState::Output_low; - } - */ - #endif // if FEATURE_ETHERNET - - #ifdef ESP32 - if (bootState != PinBootState::Default_state) { - gpio_reset_pin(static_cast(gpio)); - } - #endif - - switch (bootState) - { - case PinBootState::Default_state: - // At startup, pins are configured as INPUT - break; - case PinBootState::Output_low: - createAndSetPortStatus_Mode_State(key, PIN_MODE_OUTPUT, 0); - GPIO_Write(PLUGIN_GPIO, gpio, LOW, PIN_MODE_OUTPUT); - - // setPinState(1, gpio, PIN_MODE_OUTPUT, LOW); - break; - case PinBootState::Output_high: - createAndSetPortStatus_Mode_State(key, PIN_MODE_OUTPUT, 0); - GPIO_Write(PLUGIN_GPIO, gpio, HIGH, PIN_MODE_OUTPUT); - - // setPinState(1, gpio, PIN_MODE_OUTPUT, HIGH); - break; - case PinBootState::Input_pullup: - - if (hasPullUp) { - createAndSetPortStatus_Mode_State(key, PIN_MODE_INPUT_PULLUP, 0); - pinMode(gpio, INPUT_PULLUP); - } - break; - case PinBootState::Input_pulldown: - - if (hasPullDown) { - createAndSetPortStatus_Mode_State(key, PIN_MODE_INPUT_PULLDOWN, 0); - - #ifdef ESP8266 - - if (gpio == 16) { - pinMode(gpio, INPUT_PULLDOWN_16); - } - #endif // ifdef ESP8266 - #ifdef ESP32 - pinMode(gpio, INPUT_PULLDOWN); - #endif // ifdef ESP32 - } - break; - case PinBootState::Input: - createAndSetPortStatus_Mode_State(key, PIN_MODE_INPUT, 0); - pinMode(gpio, INPUT); - break; - } - } - } - } - - if (getGpioPullResistor(Settings.Pin_Reset, hasPullUp, hasPullDown)) { - if (hasPullUp) { - pinMode(Settings.Pin_Reset, INPUT_PULLUP); - } - } - - initI2C(); - - #if FEATURE_PLUGIN_PRIORITY - String dummy; - PluginCall(PLUGIN_PRIORITY_INIT_ALL, nullptr, dummy); - #endif // if FEATURE_PLUGIN_PRIORITY - - bool tryInitSPI = true; -#if FEATURE_ETHERNET - if ((Settings.NetworkMedium == NetworkMedium_t::Ethernet) && - isValid(Settings.ETH_Phy_Type) && - isSPI_EthernetType(Settings.ETH_Phy_Type)) - { -#if !ETH_SPI_SUPPORTS_CUSTOM - tryInitSPI = false; -#endif - } -#endif - - - // SPI Init - bool SPI_initialized = false; - if (tryInitSPI && Settings.isSPI_valid()) - { - SPI.setHwCs(false); - - // MFD: for ESP32 enable the SPI on HSPI as the default is VSPI - #ifdef ESP32 - - const SPI_Options_e SPI_selection = static_cast(Settings.InitSPI); - int8_t spi_gpios[3] = {}; - - if (Settings.getSPI_pins(spi_gpios)) { - if (SPI_selection == SPI_Options_e::Vspi_Fspi) { - SPI.begin(); // Default SPI bus - } else { - SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); - } - SPI_initialized = true; - } - #else // ifdef ESP32 - SPI.begin(); - SPI_initialized = true; - #endif // ifdef ESP32 - } - - if (SPI_initialized) - { - addLog(LOG_LEVEL_INFO, F("INIT : SPI Init (without CS)")); - #if FEATURE_SD - - if (Settings.Pin_sd_cs >= 0) - { - if (SD.begin(Settings.Pin_sd_cs)) - { - addLog(LOG_LEVEL_INFO, F("SD : Init OK")); - } - else - { - SD.end(); - addLog(LOG_LEVEL_ERROR, F("SD : Init failed")); - } - } -#endif // if FEATURE_SD - } else { - addLog(LOG_LEVEL_INFO, F("INIT : SPI not enabled")); - } -} - - -void checkResetFactoryPin() { - static uint8_t factoryResetCounter = 0; - - if (Settings.Pin_Reset == -1) { - return; - } - - if (digitalRead(Settings.Pin_Reset) == 0) { // active low reset pin - factoryResetCounter++; // just count every second - } - else - { // reset pin released - if (factoryResetCounter > 9) { - // factory reset and reboot - ResetFactory(); - } - - if (factoryResetCounter > 3) { - // normal reboot - reboot(IntendedRebootReason_e::ResetFactoryPinActive); - } - factoryResetCounter = 0; // count was < 3, reset counter - } -} - -#ifdef ESP8266 -int lastADCvalue = 0; - -int espeasy_analogRead(int pin) { - if (!WiFiEventData.wifiConnectInProgress) { - #if FEATURE_ADC_VCC - lastADCvalue = ESP.getVcc(); - #else - lastADCvalue = analogRead(A0); - #endif // if FEATURE_ADC_VCC - } - return lastADCvalue; -} - -#endif // ifdef ESP8266 - -float mapADCtoFloat(float float_value, - float adc1, - float adc2, - float out1, - float out2) -{ - if (!approximatelyEqual(adc1, adc2)) - { - const float normalized = (float_value - adc1) / (adc2 - adc1); - float_value = normalized * (out2 - out1) + out1; - } - return float_value; -} - - -#ifdef ESP32 - -// ESP32 ADC calibration datatypes. - - -// FIXME TD-er: For now keep a local array of the adc calibration -#if ESP_IDF_VERSION_MAJOR < 5 -Hardware_ADC_cali_t ESP32_ADC_cali[ADC_ATTEN_MAX]{}; -#else -Hardware_ADC_cali_t ESP32_ADC_cali[ADC_ATTENDB_MAX]{}; -#endif - - -void initADC() { - for (size_t atten = 0; atten < NR_ELEMENTS(ESP32_ADC_cali); ++atten) { - if (!ESP32_ADC_cali[atten].initialized()) { - // FIXME TD-er: For now fake some pin which is connected to ADC1 - #ifdef ESP32_CLASSIC - const int pin = 36; - #else - const int pin = 1; - #endif - ESP32_ADC_cali[atten].init(pin, static_cast(atten)); - } - } -} - -float applyADCFactoryCalibration(float raw_value, adc_atten_t attenuation) -{ - if (attenuation < NR_ELEMENTS(ESP32_ADC_cali)) { - return ESP32_ADC_cali[attenuation].applyFactoryCalibration(raw_value); - } - return raw_value; -} - -bool hasADC_factory_calibration() { - return ESP32_ADC_cali[0].useFactoryCalibration(); -} - -const __FlashStringHelper* getADC_factory_calibration_type() -{ - return ESP32_ADC_cali[0].getADC_factory_calibration_type(); -} - -float getADC_factory_calibrated_min(adc_atten_t attenuation) -{ - if (attenuation < NR_ELEMENTS(ESP32_ADC_cali)) { - return ESP32_ADC_cali[attenuation].getMinOut(); - } - return 0.0f; -} - -float getADC_factory_calibrated_max(adc_atten_t attenuation) -{ - if (attenuation < NR_ELEMENTS(ESP32_ADC_cali)) { - return ESP32_ADC_cali[attenuation].getMaxOut(); - } - return MAX_ADC_VALUE; -} - -int getADC_num_for_gpio(int pin) { - int ch; - - return getADC_num_for_gpio(pin, ch); -} - -int getADC_num_for_gpio(int pin, int& channel) -{ - int adc, t; - - if (getADC_gpio_info(pin, adc, channel, t)) { - return adc; - } - return -1; -} - -int espeasy_analogRead(int pin, bool readAsTouch) { - int value = 0; - int adc, ch, t; - - if (getADC_gpio_info(pin, adc, ch, t)) { - bool canread = false; - - switch (adc) { - case 0: - # if HAS_HALL_EFFECT_SENSOR - value = hallRead(); - # endif // if HAS_HALL_EFFECT_SENSOR - break; - case 1: - canread = true; - break; - case 2: -#if ESP_IDF_VERSION_MAJOR < 5 - if (WiFi.getMode() == WIFI_OFF) { - // See: - // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html#configuration-and-reading-adc - // ADC2 is shared with WiFi, so don't read ADC2 when WiFi is on. - canread = true; - } -#else - canread = true; -#endif - break; - } - - if (canread) { - if (readAsTouch && (t >= 0)) { - # if HAS_TOUCH_GPIO - value = touchRead(pin); - # endif // if HAS_TOUCH_GPIO - } else { - value = analogRead(pin); - } - } - } - return value; -} - - -int getCPU_MaxFreqMHz() -{ -#if CONFIG_IDF_TARGET_ESP32 - return static_cast(efuse_hal_get_rated_freq_mhz()); -#elif CONFIG_IDF_TARGET_ESP32C2 - return 120; -#elif CONFIG_IDF_TARGET_ESP32C3 - return 160; -#elif CONFIG_IDF_TARGET_ESP32C6 - return 160; -#elif CONFIG_IDF_TARGET_ESP32H2 - //IDF-6570 - return 96; -#elif CONFIG_IDF_TARGET_ESP32P4 - return 400; -#elif CONFIG_IDF_TARGET_ESP32S2 - return 240; -#elif CONFIG_IDF_TARGET_ESP32S3 - return 240; - -# else - # error Target CONFIG_IDF_TARGET is not supported - return 160; -# endif -} - -int getCPU_MinFreqMHz() -{ - // TODO TD-er: May differ on some ESPs and also some allow less but only without WiFi - return 80; -} - - -#endif // ifdef ESP32 - - - -/*********************************************************************************************\ -* High entropy hardware random generator -* Thanks to DigitalAlchemist -\*********************************************************************************************/ - -#if ESP_IDF_VERSION_MAJOR >= 5 -#include -#endif - -uint32_t HwRandom() { -#if ESP_IDF_VERSION_MAJOR >= 5 - // See for more info on the HW RNG: - // https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/system/random.html - return esp_random(); -#else - -// Based on code from https://raw.githubusercontent.com/espressif/esp-idf/master/components/esp32/hw_random.c -// https://github.com/arendst/Tasmota/blob/1e6b78a957be538cf494f0e2dc49060d1cb0fe8b/tasmota/support_esp.ino#L805 -#if ESP8266 - - // https://web.archive.org/web/20160922031242/http://esp8266-re.foogod.com/wiki/Random_Number_Generator - # define _RAND_ADDR 0x3FF20E44UL -#endif // ESP8266 -#ifdef ESP32 - # define _RAND_ADDR 0x3FF75144UL -#endif // ESP32 - static uint32_t last_ccount = 0; - uint32_t ccount; - uint32_t result = 0; - - do { - ccount = ESP.getCycleCount(); - result ^= *(volatile uint32_t *)_RAND_ADDR; // -V566 - } while (ccount - last_ccount < 64); - last_ccount = ccount; - return result ^ *(volatile uint32_t *)_RAND_ADDR; // -V566 -#undef _RAND_ADDR -#endif -} - -long HwRandom(long howbig) { - if(howbig == 0) { - return 0; - } - return HwRandom() % howbig; -} - -long HwRandom(long howsmall, long howbig) { - if(howsmall >= howbig) { - return howsmall; - } - long diff = howbig - howsmall; - return HwRandom(diff) + howsmall; -} - -#ifdef ESP8266 -void readBootCause() { - lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; - const rst_info *resetInfo = ESP.getResetInfoPtr(); - - if (resetInfo != nullptr) { - switch (resetInfo->reason) { - // normal startup by power on - case REASON_DEFAULT_RST: lastBootCause = BOOT_CAUSE_COLD_BOOT; break; - - // hardware watch dog reset - case REASON_WDT_RST: lastBootCause = BOOT_CAUSE_EXT_WD; break; - - // exception reset, GPIO status won’t change - case REASON_EXCEPTION_RST: lastBootCause = BOOT_CAUSE_EXCEPTION; break; - - // software watch dog reset, GPIO status won’t change - case REASON_SOFT_WDT_RST: lastBootCause = BOOT_CAUSE_SW_WATCHDOG; break; - - // software restart ,system_restart , GPIO status won’t change - case REASON_SOFT_RESTART: lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; - - // wake up from deep-sleep - case REASON_DEEP_SLEEP_AWAKE: lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; - - // external system reset - case REASON_EXT_SYS_RST: lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; - default: - break; - } - } -} - -#endif // ifdef ESP8266 - -#ifdef ESP32 -void readBootCause() { - lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; - - #ifdef ESP32S2 - - switch (rtc_get_reset_reason(0)) { - case NO_MEAN : break; - case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ - case RTC_SW_SYS_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ - case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<5, Deep Sleep reset digital core*/ - case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ - case TG1WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<8, Timer Group1 Watch dog reset digital core*/ - case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ - case INTRUSION_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<10, Instrusion tested to reset CPU*/ - case TG0WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group0 reset CPU*/ - case RTC_SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ - case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ - case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ - case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ - case TG1WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<17, Time Group1 reset CPU*/ - case SUPER_WDT_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<18, super watchdog reset digital core and rtc module*/ - case GLITCH_RTC_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<19, glitch reset digital core and rtc module*/ - case EFUSE_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<20, efuse reset digital core*/ - } - - - -#elif defined(ESP32S3) - switch (rtc_get_reset_reason(0)) { - case NO_MEAN : break; - case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ - case RTC_SW_SYS_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ - case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<5, Deep Sleep reset digital core*/ - case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ - case TG1WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<8, Timer Group1 Watch dog reset digital core*/ - case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ - case INTRUSION_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<10, Instrusion tested to reset CPU*/ - case TG0WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group0 reset CPU*/ - case RTC_SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ - case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ - case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ - case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ - case TG1WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<17, Time Group1 reset CPU*/ - case SUPER_WDT_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<18, super watchdog reset digital core and rtc module*/ - case GLITCH_RTC_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<19, glitch reset digital core and rtc module*/ - case EFUSE_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<20, efuse reset digital core*/ - case USB_UART_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<21, usb uart reset digital core */ - case USB_JTAG_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<22, usb jtag reset digital core */ - case POWER_GLITCH_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<23, power glitch reset digital core and rtc module*/ - } - - -#elif defined(ESP32C2) - switch (rtc_get_reset_reason(0)) { - case NO_MEAN : break; - case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ - case RTC_SW_SYS_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ - case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<3, Deep Sleep reset digital core*/ - case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ - case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ - case INTRUSION_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<10, Instrusion tested to reset CPU*/ - case TG0WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group0 reset CPU*/ - case RTC_SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ - case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ - case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ - case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ - case SUPER_WDT_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, super watchdog reset digital core and rtc module*/ - case GLITCH_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<19, glitch reset digital core and rtc module*/ - case EFUSE_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<20, efuse reset digital core*/ - case JTAG_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<24, jtag reset CPU*/ - } - - -#elif defined(ESP32C3) - switch (rtc_get_reset_reason(0)) { - case NO_MEAN : break; - case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ - case RTC_SW_SYS_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ - case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<5, Deep Sleep reset digital core*/ - case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ - case TG1WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<8, Timer Group1 Watch dog reset digital core*/ - case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ - case INTRUSION_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<10, Instrusion tested to reset CPU*/ - case TG0WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group0 reset CPU*/ - case RTC_SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ - case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ - case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ - case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ - case TG1WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<17, Time Group1 reset CPU*/ - case SUPER_WDT_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<18, super watchdog reset digital core and rtc module*/ - case GLITCH_RTC_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<19, glitch reset digital core and rtc module*/ - case EFUSE_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<20, efuse reset digital core*/ - case USB_UART_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<21, usb uart reset digital core */ - case USB_JTAG_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<22, usb jtag reset digital core */ - case POWER_GLITCH_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<23, power glitch reset digital core and rtc module*/ - } - -#elif defined(ESP32C6) - switch (rtc_get_reset_reason(0)) { - case NO_MEAN : break; - case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ - case RTC_SW_SYS_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ - case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<5, Deep Sleep reset digital core*/ - case SDIO_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<6, Reset by SLC module, reset digital core (hp system)*/ - case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ - case TG1WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<8, Timer Group1 Watch dog reset digital core*/ - case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ - case TG0WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group0 reset CPU*/ - case RTC_SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ - case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ - case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ - case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ - case TG1WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<17, Time Group1 reset CPU*/ - case SUPER_WDT_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<18, super watchdog reset digital core and rtc module*/ - case EFUSE_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<20, efuse reset digital core*/ - case USB_UART_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<21, usb uart reset digital core */ - case USB_JTAG_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<22, usb jtag reset digital core */ - case JTAG_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<24, jtag reset CPU*/ - } - -# elif defined(ESP32_CLASSIC) - switch (rtc_get_reset_reason(0)) { - case NO_MEAN : break; - case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ - case SW_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ - case OWDT_RESET : lastBootCause = BOOT_CAUSE_SW_WATCHDOG; break; /**<4, Legacy watch dog reset digital core*/ - case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<3, Deep Sleep reset digital core*/ - case SDIO_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<6, Reset by SLC module, reset digital core*/ - case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ - case TG1WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<8, Timer Group1 Watch dog reset digital core*/ - case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ - case INTRUSION_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<10, Instrusion tested to reset CPU*/ - case TGWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group reset CPU*/ - case SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ - case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ - case EXT_CPU_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<14, for APP CPU, reseted by PRO CPU*/ - case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ - case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ - } - - # else - - static_assert(false, "Implement processor architecture"); - - #endif -} - -#endif // ifdef ESP32 - - -/********************************************************************************************\ - Hardware specific configurations - \*********************************************************************************************/ -const __FlashStringHelper* getDeviceModelBrandString(DeviceModel model) { - switch (model) { - case DeviceModel::DeviceModel_Sonoff_Basic: - case DeviceModel::DeviceModel_Sonoff_TH1x: - case DeviceModel::DeviceModel_Sonoff_S2x: - case DeviceModel::DeviceModel_Sonoff_TouchT1: - case DeviceModel::DeviceModel_Sonoff_TouchT2: - case DeviceModel::DeviceModel_Sonoff_TouchT3: - case DeviceModel::DeviceModel_Sonoff_4ch: - case DeviceModel::DeviceModel_Sonoff_POW: - case DeviceModel::DeviceModel_Sonoff_POWr2: return F("Sonoff"); - case DeviceModel::DeviceModel_Shelly1: - case DeviceModel::DeviceModel_ShellyPLUG_S: return F("Shelly"); -# if CONFIG_ETH_USE_ESP32_EMAC - case DeviceModel::DeviceModel_Olimex_ESP32_PoE: - case DeviceModel::DeviceModel_Olimex_ESP32_EVB: - case DeviceModel::DeviceModel_Olimex_ESP32_GATEWAY: - #ifdef ESP32_CLASSIC - return F("Olimex"); - #endif // ifdef ESP32_CLASSIC - case DeviceModel::DeviceModel_wESP32: - #ifdef ESP32_CLASSIC - return F("wESP32"); - #endif // ifdef ESP32_CLASSIC - case DeviceModel::DeviceModel_WT32_ETH01: - #ifdef ESP32_CLASSIC - return F("WT32-ETH01"); - #endif // ifdef ESP32_CLASSIC -#endif - case DeviceModel::DeviceModel_default: - case DeviceModel::DeviceModel_MAX: break; - - // Do not use default: as this allows the compiler to detect any missing cases. - } - return F(""); -} - -const __FlashStringHelper* getDeviceModelTypeString(DeviceModel model) -{ - switch (model) { -#if defined(ESP8266) && !defined(LIMIT_BUILD_SIZE) - case DeviceModel::DeviceModel_Sonoff_Basic: return F(" Basic"); - case DeviceModel::DeviceModel_Sonoff_TH1x: return F(" TH1x"); - case DeviceModel::DeviceModel_Sonoff_S2x: return F(" S2x"); - case DeviceModel::DeviceModel_Sonoff_TouchT1: return F(" TouchT1"); - case DeviceModel::DeviceModel_Sonoff_TouchT2: return F(" TouchT2"); - case DeviceModel::DeviceModel_Sonoff_TouchT3: return F(" TouchT3"); - case DeviceModel::DeviceModel_Sonoff_4ch: return F(" 4ch"); - case DeviceModel::DeviceModel_Sonoff_POW: return F(" POW"); - case DeviceModel::DeviceModel_Sonoff_POWr2: return F(" POW-r2"); - case DeviceModel::DeviceModel_Shelly1: return F("1"); - case DeviceModel::DeviceModel_ShellyPLUG_S: return F(" PLUG S"); -#else // if defined(ESP8266) && !defined(LIMIT_BUILD_SIZE) - case DeviceModel::DeviceModel_Sonoff_Basic: - case DeviceModel::DeviceModel_Sonoff_TH1x: - case DeviceModel::DeviceModel_Sonoff_S2x: - case DeviceModel::DeviceModel_Sonoff_TouchT1: - case DeviceModel::DeviceModel_Sonoff_TouchT2: - case DeviceModel::DeviceModel_Sonoff_TouchT3: - case DeviceModel::DeviceModel_Sonoff_4ch: - case DeviceModel::DeviceModel_Sonoff_POW: - case DeviceModel::DeviceModel_Sonoff_POWr2: - case DeviceModel::DeviceModel_Shelly1: - case DeviceModel::DeviceModel_ShellyPLUG_S: - return F("default"); -#endif // if defined(ESP8266) && !defined(LIMIT_BUILD_SIZE) -#if CONFIG_ETH_USE_ESP32_EMAC - case DeviceModel::DeviceModel_Olimex_ESP32_PoE: return F(" ESP32-PoE"); - case DeviceModel::DeviceModel_Olimex_ESP32_EVB: return F(" ESP32-EVB"); - case DeviceModel::DeviceModel_Olimex_ESP32_GATEWAY: return F(" ESP32-GATEWAY"); - case DeviceModel::DeviceModel_wESP32: break; - case DeviceModel::DeviceModel_WT32_ETH01: return F(" add-on"); -#endif // if CONFIG_ETH_USE_ESP32_EMAC - - case DeviceModel::DeviceModel_default: - case DeviceModel::DeviceModel_MAX: return F("default"); - - // Do not use default: as this allows the compiler to detect any missing cases. - } - return F(""); -} - -String getDeviceModelString(DeviceModel model) { - return concat( - getDeviceModelBrandString(model), - getDeviceModelTypeString(model)); -} - -bool modelMatchingFlashSize(DeviceModel model) { -#if defined(ESP8266) || (defined(ESP32_CLASSIC) && FEATURE_ETHERNET) - const uint32_t size_MB = getFlashRealSizeInBytes() >> 20; -#endif // if defined(ESP8266) || (defined(ESP32_CLASSIC) && FEATURE_ETHERNET) - - // TD-er: This also checks for ESP8266/ESP8285/ESP32_CLASSIC - switch (model) { - case DeviceModel::DeviceModel_Sonoff_Basic: - case DeviceModel::DeviceModel_Sonoff_TH1x: - case DeviceModel::DeviceModel_Sonoff_S2x: - case DeviceModel::DeviceModel_Sonoff_TouchT1: - case DeviceModel::DeviceModel_Sonoff_TouchT2: - case DeviceModel::DeviceModel_Sonoff_TouchT3: - case DeviceModel::DeviceModel_Sonoff_4ch: -#ifdef ESP8266 - return size_MB == 1; -#else // ifdef ESP8266 - return false; -#endif // ifdef ESP8266 - - case DeviceModel::DeviceModel_Sonoff_POW: - case DeviceModel::DeviceModel_Sonoff_POWr2: -#ifdef ESP8266 - return size_MB == 4; -#else // ifdef ESP8266 - return false; -#endif // ifdef ESP8266 - - case DeviceModel::DeviceModel_Shelly1: - case DeviceModel::DeviceModel_ShellyPLUG_S: -#ifdef ESP8266 - return size_MB == 2; -#else // ifdef ESP8266 - return false; -#endif // ifdef ESP8266 - - // These Olimex boards all have Ethernet -#if CONFIG_ETH_USE_ESP32_EMAC - case DeviceModel::DeviceModel_Olimex_ESP32_PoE: - case DeviceModel::DeviceModel_Olimex_ESP32_EVB: - case DeviceModel::DeviceModel_Olimex_ESP32_GATEWAY: - case DeviceModel::DeviceModel_wESP32: - case DeviceModel::DeviceModel_WT32_ETH01: -# if defined(ESP32_CLASSIC) && FEATURE_ETHERNET - return size_MB == 4; -# else // if defined(ESP32_CLASSIC) && FEATURE_ETHERNET - return false; -# endif // if defined(ESP32_CLASSIC) && FEATURE_ETHERNET -#endif // if CONFIG_ETH_USE_ESP32_EMAC - case DeviceModel::DeviceModel_default: - case DeviceModel::DeviceModel_MAX: - return true; - - // Do not use default: as this allows the compiler to detect any missing cases. - } - return true; -} - -void setFactoryDefault(DeviceModel model) { - ResetFactoryDefaultPreference.setDeviceModel(model); -} - -/********************************************************************************************\ - Add pre defined plugins and rules. - \*********************************************************************************************/ -void addSwitchPlugin(taskIndex_t taskIndex, int gpio, const String& name, bool activeLow) { - setTaskDevice_to_TaskIndex(PLUGIN_GPIO, taskIndex); - const int pins[] = { gpio, -1, -1 }; - - setBasicTaskValues( - taskIndex, - 0, // taskdevicetimer - true, // enabled - name, // name - pins); - Settings.TaskDevicePin1PullUp[taskIndex] = true; - - if (activeLow) { - Settings.TaskDevicePluginConfig[taskIndex][2] = 1; // SWITCH_TYPE_PUSH_ACTIVE_LOW; - } - Settings.TaskDevicePluginConfig[taskIndex][3] = 1; // "Send Boot state" checked. -} - -void addPredefinedPlugins(const GpioFactorySettingsStruct& gpio_settings) { - taskIndex_t taskIndex = 0; - - for (int i = 0; i < 4; ++i) { - if (gpio_settings.button[i] >= 0) { - String label = F("Button"); - label += (i + 1); - addSwitchPlugin(taskIndex, gpio_settings.button[i], label, true); - ++taskIndex; - } - - if (gpio_settings.relais[i] >= 0) { - String label = F("Relay"); - label += (i + 1); - addSwitchPlugin(taskIndex, gpio_settings.relais[i], label, false); - ++taskIndex; - } - } -} - -void addButtonRelayRule(uint8_t buttonNumber, int relay_gpio) { - Settings.UseRules = true; - String fileName; - - #if defined(ESP32) - fileName += '/'; - #endif // if defined(ESP32) - fileName += F("rules1.txt"); - String rule = F("on ButtonBNR#state do\n if [RelayBNR#state]=0\n gpio,GNR,1\n else\n gpio,GNR,0\n endif\nendon\n"); - rule.replace(F("BNR"), String(buttonNumber)); - rule.replace(F("GNR"), String(relay_gpio)); - String result = appendLineToFile(fileName, rule); - - if (result.length() > 0) { - addLogMove(LOG_LEVEL_ERROR, result); - } -} - -void addPredefinedRules(const GpioFactorySettingsStruct& gpio_settings) { - for (int i = 0; i < 4; ++i) { - if ((gpio_settings.button[i] >= 0) && (gpio_settings.relais[i] >= 0)) { - addButtonRelayRule((i + 1), gpio_settings.relais[i]); - } - } -} - -// ******************************************************************************** -// change of device: cleanup old device and reset default settings -// ******************************************************************************** -void setTaskDevice_to_TaskIndex(pluginID_t taskdevicenumber, taskIndex_t taskIndex) { - struct EventStruct TempEvent(taskIndex); - String dummy; - - // let the plugin do its cleanup by calling PLUGIN_EXIT with this TaskIndex - PluginCall(PLUGIN_EXIT, &TempEvent, dummy); - taskClear(taskIndex, false); // clear settings, but do not save - ClearCustomTaskSettings(taskIndex); - - Settings.TaskDeviceNumber[taskIndex] = taskdevicenumber.value; - - // Settings.getPluginID_for_task(taskIndex) = taskdevicenumber; - - if (validPluginID_fullcheck(taskdevicenumber)) // set default values if a new device has been selected - { - // FIXME TD-er: Must check if this is working (e.g. need to set nr. decimals?) - ExtraTaskSettings.clear(); - ExtraTaskSettings.TaskIndex = taskIndex; - - // NOTE: do not enable task by default. allow user to enter sensible valus first and let him enable it when ready. - PluginCall(PLUGIN_SET_DEFAULTS, &TempEvent, dummy); - PluginCall(PLUGIN_GET_DEVICEVALUENAMES, &TempEvent, dummy); // the plugin should populate ExtraTaskSettings with its default values. - } else { - // New task is empty task, thus save config now. - taskClear(taskIndex, true); // clear settings, and save - } -} - -// ******************************************************************************** -// Initialize task with some default values applicable for almost all tasks -// ******************************************************************************** -void setBasicTaskValues(taskIndex_t taskIndex, unsigned long taskdevicetimer, - bool enabled, const String& name, const int pins[3]) { - if (!validTaskIndex(taskIndex)) { return; } - const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(taskIndex); - - if (!validDeviceIndex(DeviceIndex)) { return; } - - LoadTaskSettings(taskIndex); // Make sure ExtraTaskSettings are up-to-date - - if (taskdevicetimer > 0) { - Settings.TaskDeviceTimer[taskIndex] = taskdevicetimer; - } else { - if (!Device[DeviceIndex].TimerOptional) { // Set default delay, unless it's optional... - Settings.TaskDeviceTimer[taskIndex] = Settings.Delay; - } - else { - Settings.TaskDeviceTimer[taskIndex] = 0; - } - } - Settings.TaskDeviceEnabled[taskIndex] = enabled; - //Settings.TaskDeviceEnabled[taskIndex].enabled = enabled; - safe_strncpy(ExtraTaskSettings.TaskDeviceName, name.c_str(), sizeof(ExtraTaskSettings.TaskDeviceName)); - - // FIXME TD-er: Check for valid GPIO pin (and -1 for "not set") - Settings.TaskDevicePin1[taskIndex] = pins[0]; - Settings.TaskDevicePin2[taskIndex] = pins[1]; - Settings.TaskDevicePin3[taskIndex] = pins[2]; -} +#include "../Helpers/Hardware.h" + +#include "../Commands/GPIO.h" +#include "../CustomBuild/ESPEasyLimits.h" +#include "../DataTypes/SPI_options.h" +#include "../ESPEasyCore/ESPEasyGPIO.h" +#include "../ESPEasyCore/ESPEasy_Log.h" + +#include "../Globals/Device.h" +#include "../Globals/ESPEasyWiFiEvent.h" +#include "../Globals/ExtraTaskSettings.h" +#include "../Globals/Settings.h" +#include "../Globals/Statistics.h" +#include "../Globals/GlobalMapPortStatus.h" + +#include "../Helpers/ESPEasy_FactoryDefault.h" +#include "../Helpers/ESPEasy_Storage.h" +#include "../Helpers/FS_Helper.h" +#include "../Helpers/Hardware_device_info.h" +#include "../Helpers/Hardware_GPIO.h" +#include "../Helpers/Hardware_I2C.h" +#include "../Helpers/I2C_access.h" +#include "../Helpers/Misc.h" +#include "../Helpers/PortStatus.h" +#include "../Helpers/StringConverter.h" + + +#if defined(ESP8266) + # include +#endif // if defined(ESP8266) +#if defined(ESP32) + # include +#endif // if defined(ESP32) + +// #include "../../ESPEasy-Globals.h" + +#ifdef ESP32 + # include + # include + # include + # include + # include + + # if ESP_IDF_VERSION_MAJOR == 4 + # if CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3 + # include + # include + # include + # elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2 + # include + # include + # include + # elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 + # include + # include + # elif CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 + # include + # include + # include + # else // if CONFIG_IDF_TARGET_ESP32S3 + # error Target CONFIG_IDF_TARGET is not supported + # endif // if CONFIG_IDF_TARGET_ESP32S3 + # else // ESP32 IDF 5.x and later + # include + # include + # include + # endif // if ESP_IDF_VERSION_MAJOR == 4 + + +# if CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3 + # define HAS_HALL_EFFECT_SENSOR 0 + # define HAS_TOUCH_GPIO 1 +# elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2 + # define HAS_HALL_EFFECT_SENSOR 0 + # define HAS_TOUCH_GPIO 1 +# elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6 + # define HAS_HALL_EFFECT_SENSOR 0 + # define HAS_TOUCH_GPIO 0 +# elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 + # define HAS_HALL_EFFECT_SENSOR 0 + # define HAS_TOUCH_GPIO 0 +# elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2 + # define HAS_HALL_EFFECT_SENSOR 0 + # define HAS_TOUCH_GPIO 0 +# elif CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 + # if ESP_IDF_VERSION_MAJOR < 5 + # define HAS_HALL_EFFECT_SENSOR 1 + # else // if ESP_IDF_VERSION_MAJOR < 5 + +// Support for Hall Effect sensor was removed in ESP_IDF 5.x + # define HAS_HALL_EFFECT_SENSOR 0 + # endif // if ESP_IDF_VERSION_MAJOR < 5 + # define HAS_TOUCH_GPIO 1 +# else // if CONFIG_IDF_TARGET_ESP32S3 + # error Target CONFIG_IDF_TARGET is not supported +# endif // if CONFIG_IDF_TARGET_ESP32S3 + + +# ifndef HAS_TOUCH_GPIO +# define HAS_TOUCH_GPIO 0 +# endif // ifndef HAS_TOUCH_GPIO + + +# if ESP_IDF_VERSION_MAJOR >= 5 + +# include +# include +# include +# include + +// #include + +# endif // if ESP_IDF_VERSION_MAJOR >= 5 + +# include "../Helpers/Hardware_ADC_cali.h" + +#if FEATURE_ETHERNET +#include +#endif + +#endif // ifdef ESP32 + + +#if FEATURE_SD +# include +#endif // if FEATURE_SD + +#if FEATURE_CAN +#include +#endif +#include + + +# define GPIO_PLUGIN_ID 1 + +/********************************************************************************************\ + * Initialize specific hardware settings (only global ones, others are set through devices) + \*********************************************************************************************/ +void hardwareInit() +{ + // set GPIO pins state if not set to default + bool hasPullUp, hasPullDown; + + for (int gpio = 0; gpio <= MAX_GPIO; ++gpio) { + const bool serialPinConflict = isSerialConsolePin(gpio); + + if (!serialPinConflict) { + const uint32_t key = createKey(PLUGIN_GPIO, gpio); + #ifdef ESP32 + checkAndClearPWM(key); + #endif // ifdef ESP32 + + if (getGpioPullResistor(gpio, hasPullUp, hasPullDown)) { + PinBootState bootState = Settings.getPinBootState(gpio); + #if FEATURE_ETHERNET +/* + if (Settings.ETH_Pin_power_rst == gpio) + { + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + String log = F("ETH : Reset ETH module on pin "); + log += Settings.ETH_Pin_power_rst; + addLog(LOG_LEVEL_INFO, log); + } + bootState = PinBootState::Output_low; + } + */ + #endif // if FEATURE_ETHERNET + + #ifdef ESP32 + if (bootState != PinBootState::Default_state) { + gpio_reset_pin(static_cast(gpio)); + } + #endif + + switch (bootState) + { + case PinBootState::Default_state: + // At startup, pins are configured as INPUT + break; + case PinBootState::Output_low: + createAndSetPortStatus_Mode_State(key, PIN_MODE_OUTPUT, 0); + GPIO_Write(PLUGIN_GPIO, gpio, LOW, PIN_MODE_OUTPUT); + + // setPinState(1, gpio, PIN_MODE_OUTPUT, LOW); + break; + case PinBootState::Output_high: + createAndSetPortStatus_Mode_State(key, PIN_MODE_OUTPUT, 0); + GPIO_Write(PLUGIN_GPIO, gpio, HIGH, PIN_MODE_OUTPUT); + + // setPinState(1, gpio, PIN_MODE_OUTPUT, HIGH); + break; + case PinBootState::Input_pullup: + + if (hasPullUp) { + createAndSetPortStatus_Mode_State(key, PIN_MODE_INPUT_PULLUP, 0); + pinMode(gpio, INPUT_PULLUP); + } + break; + case PinBootState::Input_pulldown: + + if (hasPullDown) { + createAndSetPortStatus_Mode_State(key, PIN_MODE_INPUT_PULLDOWN, 0); + + #ifdef ESP8266 + + if (gpio == 16) { + pinMode(gpio, INPUT_PULLDOWN_16); + } + #endif // ifdef ESP8266 + #ifdef ESP32 + pinMode(gpio, INPUT_PULLDOWN); + #endif // ifdef ESP32 + } + break; + case PinBootState::Input: + createAndSetPortStatus_Mode_State(key, PIN_MODE_INPUT, 0); + pinMode(gpio, INPUT); + break; + } + } + } + } + + if (getGpioPullResistor(Settings.Pin_Reset, hasPullUp, hasPullDown)) { + if (hasPullUp) { + pinMode(Settings.Pin_Reset, INPUT_PULLUP); + } + } + + initI2C(); + + #if FEATURE_PLUGIN_PRIORITY + String dummy; + PluginCall(PLUGIN_PRIORITY_INIT_ALL, nullptr, dummy); + #endif // if FEATURE_PLUGIN_PRIORITY + + bool tryInitSPI = true; +#if FEATURE_ETHERNET + if ((Settings.NetworkMedium == NetworkMedium_t::Ethernet) && + isValid(Settings.ETH_Phy_Type) && + isSPI_EthernetType(Settings.ETH_Phy_Type)) + { +#if !ETH_SPI_SUPPORTS_CUSTOM + tryInitSPI = false; +#endif + } +#endif + + + // SPI Init + bool SPI_initialized = false; + if (tryInitSPI && Settings.isSPI_valid()) + { + SPI.setHwCs(false); + + // MFD: for ESP32 enable the SPI on HSPI as the default is VSPI + #ifdef ESP32 + + const SPI_Options_e SPI_selection = static_cast(Settings.InitSPI); + int8_t spi_gpios[3] = {}; + + if (Settings.getSPI_pins(spi_gpios)) { + if (SPI_selection == SPI_Options_e::Vspi_Fspi) { + SPI.begin(); // Default SPI bus + } else { + SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); + } + SPI_initialized = true; + } + #else // ifdef ESP32 + SPI.begin(); + SPI_initialized = true; + #endif // ifdef ESP32 + } + + if (SPI_initialized) + { + addLog(LOG_LEVEL_INFO, F("INIT : SPI Init (without CS)")); + #if FEATURE_SD + + if (Settings.Pin_sd_cs >= 0) + { + if (SD.begin(Settings.Pin_sd_cs)) + { + addLog(LOG_LEVEL_INFO, F("SD : Init OK")); + } + else + { + SD.end(); + addLog(LOG_LEVEL_ERROR, F("SD : Init failed")); + } + } +#endif // if FEATURE_SD + } else { + addLog(LOG_LEVEL_INFO, F("INIT : SPI not enabled")); + } + + #if FEATURE_CAN + initCAN(); + #endif +} + + +void checkResetFactoryPin() { + static uint8_t factoryResetCounter = 0; + + if (Settings.Pin_Reset == -1) { + return; + } + + if (digitalRead(Settings.Pin_Reset) == 0) { // active low reset pin + factoryResetCounter++; // just count every second + } + else + { // reset pin released + if (factoryResetCounter > 9) { + // factory reset and reboot + ResetFactory(); + } + + if (factoryResetCounter > 3) { + // normal reboot + reboot(IntendedRebootReason_e::ResetFactoryPinActive); + } + factoryResetCounter = 0; // count was < 3, reset counter + } +} + +#ifdef ESP8266 +int lastADCvalue = 0; + +int espeasy_analogRead(int pin) { + if (!WiFiEventData.wifiConnectInProgress) { + #if FEATURE_ADC_VCC + lastADCvalue = ESP.getVcc(); + #else + lastADCvalue = analogRead(A0); + #endif // if FEATURE_ADC_VCC + } + return lastADCvalue; +} + +#endif // ifdef ESP8266 + +float mapADCtoFloat(float float_value, + float adc1, + float adc2, + float out1, + float out2) +{ + if (!approximatelyEqual(adc1, adc2)) + { + const float normalized = (float_value - adc1) / (adc2 - adc1); + float_value = normalized * (out2 - out1) + out1; + } + return float_value; +} + + +#ifdef ESP32 + +// ESP32 ADC calibration datatypes. + + +// FIXME TD-er: For now keep a local array of the adc calibration +#if ESP_IDF_VERSION_MAJOR < 5 +Hardware_ADC_cali_t ESP32_ADC_cali[ADC_ATTEN_MAX]{}; +#else +Hardware_ADC_cali_t ESP32_ADC_cali[ADC_ATTENDB_MAX]{}; +#endif + + +void initADC() { + for (size_t atten = 0; atten < NR_ELEMENTS(ESP32_ADC_cali); ++atten) { + if (!ESP32_ADC_cali[atten].initialized()) { + // FIXME TD-er: For now fake some pin which is connected to ADC1 + #ifdef ESP32_CLASSIC + const int pin = 36; + #else + const int pin = 1; + #endif + ESP32_ADC_cali[atten].init(pin, static_cast(atten)); + } + } +} + +float applyADCFactoryCalibration(float raw_value, adc_atten_t attenuation) +{ + if (attenuation < NR_ELEMENTS(ESP32_ADC_cali)) { + return ESP32_ADC_cali[attenuation].applyFactoryCalibration(raw_value); + } + return raw_value; +} + +bool hasADC_factory_calibration() { + return ESP32_ADC_cali[0].useFactoryCalibration(); +} + +const __FlashStringHelper* getADC_factory_calibration_type() +{ + return ESP32_ADC_cali[0].getADC_factory_calibration_type(); +} + +float getADC_factory_calibrated_min(adc_atten_t attenuation) +{ + if (attenuation < NR_ELEMENTS(ESP32_ADC_cali)) { + return ESP32_ADC_cali[attenuation].getMinOut(); + } + return 0.0f; +} + +float getADC_factory_calibrated_max(adc_atten_t attenuation) +{ + if (attenuation < NR_ELEMENTS(ESP32_ADC_cali)) { + return ESP32_ADC_cali[attenuation].getMaxOut(); + } + return MAX_ADC_VALUE; +} + +int getADC_num_for_gpio(int pin) { + int ch; + + return getADC_num_for_gpio(pin, ch); +} + +int getADC_num_for_gpio(int pin, int& channel) +{ + int adc, t; + + if (getADC_gpio_info(pin, adc, channel, t)) { + return adc; + } + return -1; +} + +int espeasy_analogRead(int pin, bool readAsTouch) { + int value = 0; + int adc, ch, t; + + if (getADC_gpio_info(pin, adc, ch, t)) { + bool canread = false; + + switch (adc) { + case 0: + # if HAS_HALL_EFFECT_SENSOR + value = hallRead(); + # endif // if HAS_HALL_EFFECT_SENSOR + break; + case 1: + canread = true; + break; + case 2: +#if ESP_IDF_VERSION_MAJOR < 5 + if (WiFi.getMode() == WIFI_OFF) { + // See: + // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html#configuration-and-reading-adc + // ADC2 is shared with WiFi, so don't read ADC2 when WiFi is on. + canread = true; + } +#else + canread = true; +#endif + break; + } + + if (canread) { + if (readAsTouch && (t >= 0)) { + # if HAS_TOUCH_GPIO + value = touchRead(pin); + # endif // if HAS_TOUCH_GPIO + } else { + value = analogRead(pin); + } + } + } + return value; +} + + +int getCPU_MaxFreqMHz() +{ +#if CONFIG_IDF_TARGET_ESP32 + return static_cast(efuse_hal_get_rated_freq_mhz()); +#elif CONFIG_IDF_TARGET_ESP32C2 + return 120; +#elif CONFIG_IDF_TARGET_ESP32C3 + return 160; +#elif CONFIG_IDF_TARGET_ESP32C6 + return 160; +#elif CONFIG_IDF_TARGET_ESP32H2 + //IDF-6570 + return 96; +#elif CONFIG_IDF_TARGET_ESP32P4 + return 400; +#elif CONFIG_IDF_TARGET_ESP32S2 + return 240; +#elif CONFIG_IDF_TARGET_ESP32S3 + return 240; + +# else + # error Target CONFIG_IDF_TARGET is not supported + return 160; +# endif +} + +int getCPU_MinFreqMHz() +{ + // TODO TD-er: May differ on some ESPs and also some allow less but only without WiFi + return 80; +} + + +#endif // ifdef ESP32 + + + +/*********************************************************************************************\ +* High entropy hardware random generator +* Thanks to DigitalAlchemist +\*********************************************************************************************/ + +#if ESP_IDF_VERSION_MAJOR >= 5 +#include +#endif + +uint32_t HwRandom() { +#if ESP_IDF_VERSION_MAJOR >= 5 + // See for more info on the HW RNG: + // https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/system/random.html + return esp_random(); +#else + +// Based on code from https://raw.githubusercontent.com/espressif/esp-idf/master/components/esp32/hw_random.c +// https://github.com/arendst/Tasmota/blob/1e6b78a957be538cf494f0e2dc49060d1cb0fe8b/tasmota/support_esp.ino#L805 +#if ESP8266 + + // https://web.archive.org/web/20160922031242/http://esp8266-re.foogod.com/wiki/Random_Number_Generator + # define _RAND_ADDR 0x3FF20E44UL +#endif // ESP8266 +#ifdef ESP32 + # define _RAND_ADDR 0x3FF75144UL +#endif // ESP32 + static uint32_t last_ccount = 0; + uint32_t ccount; + uint32_t result = 0; + + do { + ccount = ESP.getCycleCount(); + result ^= *(volatile uint32_t *)_RAND_ADDR; // -V566 + } while (ccount - last_ccount < 64); + last_ccount = ccount; + return result ^ *(volatile uint32_t *)_RAND_ADDR; // -V566 +#undef _RAND_ADDR +#endif +} + +long HwRandom(long howbig) { + if(howbig == 0) { + return 0; + } + return HwRandom() % howbig; +} + +long HwRandom(long howsmall, long howbig) { + if(howsmall >= howbig) { + return howsmall; + } + long diff = howbig - howsmall; + return HwRandom(diff) + howsmall; +} + +#ifdef ESP8266 +void readBootCause() { + lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; + const rst_info *resetInfo = ESP.getResetInfoPtr(); + + if (resetInfo != nullptr) { + switch (resetInfo->reason) { + // normal startup by power on + case REASON_DEFAULT_RST: lastBootCause = BOOT_CAUSE_COLD_BOOT; break; + + // hardware watch dog reset + case REASON_WDT_RST: lastBootCause = BOOT_CAUSE_EXT_WD; break; + + // exception reset, GPIO status won’t change + case REASON_EXCEPTION_RST: lastBootCause = BOOT_CAUSE_EXCEPTION; break; + + // software watch dog reset, GPIO status won’t change + case REASON_SOFT_WDT_RST: lastBootCause = BOOT_CAUSE_SW_WATCHDOG; break; + + // software restart ,system_restart , GPIO status won’t change + case REASON_SOFT_RESTART: lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; + + // wake up from deep-sleep + case REASON_DEEP_SLEEP_AWAKE: lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; + + // external system reset + case REASON_EXT_SYS_RST: lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; + default: + break; + } + } +} + +#endif // ifdef ESP8266 + +#ifdef ESP32 +void readBootCause() { + lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; + + #ifdef ESP32S2 + + switch (rtc_get_reset_reason(0)) { + case NO_MEAN : break; + case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ + case RTC_SW_SYS_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ + case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<5, Deep Sleep reset digital core*/ + case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ + case TG1WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<8, Timer Group1 Watch dog reset digital core*/ + case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ + case INTRUSION_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<10, Instrusion tested to reset CPU*/ + case TG0WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group0 reset CPU*/ + case RTC_SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ + case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ + case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ + case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ + case TG1WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<17, Time Group1 reset CPU*/ + case SUPER_WDT_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<18, super watchdog reset digital core and rtc module*/ + case GLITCH_RTC_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<19, glitch reset digital core and rtc module*/ + case EFUSE_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<20, efuse reset digital core*/ + } + + + +#elif defined(ESP32S3) + switch (rtc_get_reset_reason(0)) { + case NO_MEAN : break; + case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ + case RTC_SW_SYS_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ + case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<5, Deep Sleep reset digital core*/ + case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ + case TG1WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<8, Timer Group1 Watch dog reset digital core*/ + case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ + case INTRUSION_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<10, Instrusion tested to reset CPU*/ + case TG0WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group0 reset CPU*/ + case RTC_SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ + case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ + case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ + case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ + case TG1WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<17, Time Group1 reset CPU*/ + case SUPER_WDT_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<18, super watchdog reset digital core and rtc module*/ + case GLITCH_RTC_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<19, glitch reset digital core and rtc module*/ + case EFUSE_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<20, efuse reset digital core*/ + case USB_UART_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<21, usb uart reset digital core */ + case USB_JTAG_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<22, usb jtag reset digital core */ + case POWER_GLITCH_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<23, power glitch reset digital core and rtc module*/ + } + + +#elif defined(ESP32C2) + switch (rtc_get_reset_reason(0)) { + case NO_MEAN : break; + case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ + case RTC_SW_SYS_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ + case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<3, Deep Sleep reset digital core*/ + case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ + case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ + case INTRUSION_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<10, Instrusion tested to reset CPU*/ + case TG0WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group0 reset CPU*/ + case RTC_SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ + case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ + case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ + case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ + case SUPER_WDT_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, super watchdog reset digital core and rtc module*/ + case GLITCH_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<19, glitch reset digital core and rtc module*/ + case EFUSE_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<20, efuse reset digital core*/ + case JTAG_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<24, jtag reset CPU*/ + } + + +#elif defined(ESP32C3) + switch (rtc_get_reset_reason(0)) { + case NO_MEAN : break; + case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ + case RTC_SW_SYS_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ + case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<5, Deep Sleep reset digital core*/ + case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ + case TG1WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<8, Timer Group1 Watch dog reset digital core*/ + case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ + case INTRUSION_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<10, Instrusion tested to reset CPU*/ + case TG0WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group0 reset CPU*/ + case RTC_SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ + case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ + case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ + case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ + case TG1WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<17, Time Group1 reset CPU*/ + case SUPER_WDT_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<18, super watchdog reset digital core and rtc module*/ + case GLITCH_RTC_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<19, glitch reset digital core and rtc module*/ + case EFUSE_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<20, efuse reset digital core*/ + case USB_UART_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<21, usb uart reset digital core */ + case USB_JTAG_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<22, usb jtag reset digital core */ + case POWER_GLITCH_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<23, power glitch reset digital core and rtc module*/ + } + +#elif defined(ESP32C6) + switch (rtc_get_reset_reason(0)) { + case NO_MEAN : break; + case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ + case RTC_SW_SYS_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ + case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<5, Deep Sleep reset digital core*/ + case SDIO_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<6, Reset by SLC module, reset digital core (hp system)*/ + case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ + case TG1WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<8, Timer Group1 Watch dog reset digital core*/ + case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ + case TG0WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group0 reset CPU*/ + case RTC_SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ + case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ + case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ + case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ + case TG1WDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<17, Time Group1 reset CPU*/ + case SUPER_WDT_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<18, super watchdog reset digital core and rtc module*/ + case EFUSE_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<20, efuse reset digital core*/ + case USB_UART_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<21, usb uart reset digital core */ + case USB_JTAG_CHIP_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<22, usb jtag reset digital core */ + case JTAG_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<24, jtag reset CPU*/ + } + +# elif defined(ESP32_CLASSIC) + switch (rtc_get_reset_reason(0)) { + case NO_MEAN : break; + case POWERON_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<1, Vbat power on reset*/ + case SW_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<3, Software reset digital core*/ + case OWDT_RESET : lastBootCause = BOOT_CAUSE_SW_WATCHDOG; break; /**<4, Legacy watch dog reset digital core*/ + case DEEPSLEEP_RESET : lastBootCause = BOOT_CAUSE_DEEP_SLEEP; break; /**<3, Deep Sleep reset digital core*/ + case SDIO_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<6, Reset by SLC module, reset digital core*/ + case TG0WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<7, Timer Group0 Watch dog reset digital core*/ + case TG1WDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<8, Timer Group1 Watch dog reset digital core*/ + case RTCWDT_SYS_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<9, RTC Watch dog Reset digital core*/ + case INTRUSION_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<10, Instrusion tested to reset CPU*/ + case TGWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<11, Time Group reset CPU*/ + case SW_CPU_RESET : lastBootCause = BOOT_CAUSE_SOFT_RESTART; break; /**<12, Software reset CPU*/ + case RTCWDT_CPU_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<13, RTC Watch dog Reset CPU*/ + case EXT_CPU_RESET : lastBootCause = BOOT_CAUSE_MANUAL_REBOOT; break; /**<14, for APP CPU, reseted by PRO CPU*/ + case RTCWDT_BROWN_OUT_RESET : lastBootCause = BOOT_CAUSE_POWER_UNSTABLE; break; /**<15, Reset when the vdd voltage is not stable*/ + case RTCWDT_RTC_RESET : lastBootCause = BOOT_CAUSE_EXT_WD; break; /**<16, RTC Watch dog reset digital core and rtc module*/ + } + + # else + + static_assert(false, "Implement processor architecture"); + + #endif +} + +#endif // ifdef ESP32 + + +/********************************************************************************************\ + Hardware specific configurations + \*********************************************************************************************/ +const __FlashStringHelper* getDeviceModelBrandString(DeviceModel model) { + switch (model) { + case DeviceModel::DeviceModel_Sonoff_Basic: + case DeviceModel::DeviceModel_Sonoff_TH1x: + case DeviceModel::DeviceModel_Sonoff_S2x: + case DeviceModel::DeviceModel_Sonoff_TouchT1: + case DeviceModel::DeviceModel_Sonoff_TouchT2: + case DeviceModel::DeviceModel_Sonoff_TouchT3: + case DeviceModel::DeviceModel_Sonoff_4ch: + case DeviceModel::DeviceModel_Sonoff_POW: + case DeviceModel::DeviceModel_Sonoff_POWr2: return F("Sonoff"); + case DeviceModel::DeviceModel_Shelly1: + case DeviceModel::DeviceModel_ShellyPLUG_S: return F("Shelly"); +# if CONFIG_ETH_USE_ESP32_EMAC + case DeviceModel::DeviceModel_Olimex_ESP32_PoE: + case DeviceModel::DeviceModel_Olimex_ESP32_EVB: + case DeviceModel::DeviceModel_Olimex_ESP32_GATEWAY: + #ifdef ESP32_CLASSIC + return F("Olimex"); + #endif // ifdef ESP32_CLASSIC + case DeviceModel::DeviceModel_wESP32: + #ifdef ESP32_CLASSIC + return F("wESP32"); + #endif // ifdef ESP32_CLASSIC + case DeviceModel::DeviceModel_WT32_ETH01: + #ifdef ESP32_CLASSIC + return F("WT32-ETH01"); + #endif // ifdef ESP32_CLASSIC +#endif + case DeviceModel::DeviceModel_default: + case DeviceModel::DeviceModel_MAX: break; + + // Do not use default: as this allows the compiler to detect any missing cases. + } + return F(""); +} + +const __FlashStringHelper* getDeviceModelTypeString(DeviceModel model) +{ + switch (model) { +#if defined(ESP8266) && !defined(LIMIT_BUILD_SIZE) + case DeviceModel::DeviceModel_Sonoff_Basic: return F(" Basic"); + case DeviceModel::DeviceModel_Sonoff_TH1x: return F(" TH1x"); + case DeviceModel::DeviceModel_Sonoff_S2x: return F(" S2x"); + case DeviceModel::DeviceModel_Sonoff_TouchT1: return F(" TouchT1"); + case DeviceModel::DeviceModel_Sonoff_TouchT2: return F(" TouchT2"); + case DeviceModel::DeviceModel_Sonoff_TouchT3: return F(" TouchT3"); + case DeviceModel::DeviceModel_Sonoff_4ch: return F(" 4ch"); + case DeviceModel::DeviceModel_Sonoff_POW: return F(" POW"); + case DeviceModel::DeviceModel_Sonoff_POWr2: return F(" POW-r2"); + case DeviceModel::DeviceModel_Shelly1: return F("1"); + case DeviceModel::DeviceModel_ShellyPLUG_S: return F(" PLUG S"); +#else // if defined(ESP8266) && !defined(LIMIT_BUILD_SIZE) + case DeviceModel::DeviceModel_Sonoff_Basic: + case DeviceModel::DeviceModel_Sonoff_TH1x: + case DeviceModel::DeviceModel_Sonoff_S2x: + case DeviceModel::DeviceModel_Sonoff_TouchT1: + case DeviceModel::DeviceModel_Sonoff_TouchT2: + case DeviceModel::DeviceModel_Sonoff_TouchT3: + case DeviceModel::DeviceModel_Sonoff_4ch: + case DeviceModel::DeviceModel_Sonoff_POW: + case DeviceModel::DeviceModel_Sonoff_POWr2: + case DeviceModel::DeviceModel_Shelly1: + case DeviceModel::DeviceModel_ShellyPLUG_S: + return F("default"); +#endif // if defined(ESP8266) && !defined(LIMIT_BUILD_SIZE) +#if CONFIG_ETH_USE_ESP32_EMAC + case DeviceModel::DeviceModel_Olimex_ESP32_PoE: return F(" ESP32-PoE"); + case DeviceModel::DeviceModel_Olimex_ESP32_EVB: return F(" ESP32-EVB"); + case DeviceModel::DeviceModel_Olimex_ESP32_GATEWAY: return F(" ESP32-GATEWAY"); + case DeviceModel::DeviceModel_wESP32: break; + case DeviceModel::DeviceModel_WT32_ETH01: return F(" add-on"); +#endif // if CONFIG_ETH_USE_ESP32_EMAC + + case DeviceModel::DeviceModel_default: + case DeviceModel::DeviceModel_MAX: return F("default"); + + // Do not use default: as this allows the compiler to detect any missing cases. + } + return F(""); +} + +String getDeviceModelString(DeviceModel model) { + return concat( + getDeviceModelBrandString(model), + getDeviceModelTypeString(model)); +} + +bool modelMatchingFlashSize(DeviceModel model) { +#if defined(ESP8266) || (defined(ESP32_CLASSIC) && FEATURE_ETHERNET) + const uint32_t size_MB = getFlashRealSizeInBytes() >> 20; +#endif // if defined(ESP8266) || (defined(ESP32_CLASSIC) && FEATURE_ETHERNET) + + // TD-er: This also checks for ESP8266/ESP8285/ESP32_CLASSIC + switch (model) { + case DeviceModel::DeviceModel_Sonoff_Basic: + case DeviceModel::DeviceModel_Sonoff_TH1x: + case DeviceModel::DeviceModel_Sonoff_S2x: + case DeviceModel::DeviceModel_Sonoff_TouchT1: + case DeviceModel::DeviceModel_Sonoff_TouchT2: + case DeviceModel::DeviceModel_Sonoff_TouchT3: + case DeviceModel::DeviceModel_Sonoff_4ch: +#ifdef ESP8266 + return size_MB == 1; +#else // ifdef ESP8266 + return false; +#endif // ifdef ESP8266 + + case DeviceModel::DeviceModel_Sonoff_POW: + case DeviceModel::DeviceModel_Sonoff_POWr2: +#ifdef ESP8266 + return size_MB == 4; +#else // ifdef ESP8266 + return false; +#endif // ifdef ESP8266 + + case DeviceModel::DeviceModel_Shelly1: + case DeviceModel::DeviceModel_ShellyPLUG_S: +#ifdef ESP8266 + return size_MB == 2; +#else // ifdef ESP8266 + return false; +#endif // ifdef ESP8266 + + // These Olimex boards all have Ethernet +#if CONFIG_ETH_USE_ESP32_EMAC + case DeviceModel::DeviceModel_Olimex_ESP32_PoE: + case DeviceModel::DeviceModel_Olimex_ESP32_EVB: + case DeviceModel::DeviceModel_Olimex_ESP32_GATEWAY: + case DeviceModel::DeviceModel_wESP32: + case DeviceModel::DeviceModel_WT32_ETH01: +# if defined(ESP32_CLASSIC) && FEATURE_ETHERNET + return size_MB == 4; +# else // if defined(ESP32_CLASSIC) && FEATURE_ETHERNET + return false; +# endif // if defined(ESP32_CLASSIC) && FEATURE_ETHERNET +#endif // if CONFIG_ETH_USE_ESP32_EMAC + case DeviceModel::DeviceModel_default: + case DeviceModel::DeviceModel_MAX: + return true; + + // Do not use default: as this allows the compiler to detect any missing cases. + } + return true; +} + +void setFactoryDefault(DeviceModel model) { + ResetFactoryDefaultPreference.setDeviceModel(model); +} + +/********************************************************************************************\ + Add pre defined plugins and rules. + \*********************************************************************************************/ +void addSwitchPlugin(taskIndex_t taskIndex, int gpio, const String& name, bool activeLow) { + setTaskDevice_to_TaskIndex(PLUGIN_GPIO, taskIndex); + const int pins[] = { gpio, -1, -1 }; + + setBasicTaskValues( + taskIndex, + 0, // taskdevicetimer + true, // enabled + name, // name + pins); + Settings.TaskDevicePin1PullUp[taskIndex] = true; + + if (activeLow) { + Settings.TaskDevicePluginConfig[taskIndex][2] = 1; // SWITCH_TYPE_PUSH_ACTIVE_LOW; + } + Settings.TaskDevicePluginConfig[taskIndex][3] = 1; // "Send Boot state" checked. +} + +void addPredefinedPlugins(const GpioFactorySettingsStruct& gpio_settings) { + taskIndex_t taskIndex = 0; + + for (int i = 0; i < 4; ++i) { + if (gpio_settings.button[i] >= 0) { + String label = F("Button"); + label += (i + 1); + addSwitchPlugin(taskIndex, gpio_settings.button[i], label, true); + ++taskIndex; + } + + if (gpio_settings.relais[i] >= 0) { + String label = F("Relay"); + label += (i + 1); + addSwitchPlugin(taskIndex, gpio_settings.relais[i], label, false); + ++taskIndex; + } + } +} + +void addButtonRelayRule(uint8_t buttonNumber, int relay_gpio) { + Settings.UseRules = true; + String fileName; + + #if defined(ESP32) + fileName += '/'; + #endif // if defined(ESP32) + fileName += F("rules1.txt"); + String rule = F("on ButtonBNR#state do\n if [RelayBNR#state]=0\n gpio,GNR,1\n else\n gpio,GNR,0\n endif\nendon\n"); + rule.replace(F("BNR"), String(buttonNumber)); + rule.replace(F("GNR"), String(relay_gpio)); + String result = appendLineToFile(fileName, rule); + + if (result.length() > 0) { + addLogMove(LOG_LEVEL_ERROR, result); + } +} + +void addPredefinedRules(const GpioFactorySettingsStruct& gpio_settings) { + for (int i = 0; i < 4; ++i) { + if ((gpio_settings.button[i] >= 0) && (gpio_settings.relais[i] >= 0)) { + addButtonRelayRule((i + 1), gpio_settings.relais[i]); + } + } +} + +// ******************************************************************************** +// change of device: cleanup old device and reset default settings +// ******************************************************************************** +void setTaskDevice_to_TaskIndex(pluginID_t taskdevicenumber, taskIndex_t taskIndex) { + struct EventStruct TempEvent(taskIndex); + String dummy; + + // let the plugin do its cleanup by calling PLUGIN_EXIT with this TaskIndex + PluginCall(PLUGIN_EXIT, &TempEvent, dummy); + taskClear(taskIndex, false); // clear settings, but do not save + ClearCustomTaskSettings(taskIndex); + + Settings.TaskDeviceNumber[taskIndex] = taskdevicenumber.value; + + // Settings.getPluginID_for_task(taskIndex) = taskdevicenumber; + + if (validPluginID_fullcheck(taskdevicenumber)) // set default values if a new device has been selected + { + // FIXME TD-er: Must check if this is working (e.g. need to set nr. decimals?) + ExtraTaskSettings.clear(); + ExtraTaskSettings.TaskIndex = taskIndex; + + // NOTE: do not enable task by default. allow user to enter sensible valus first and let him enable it when ready. + PluginCall(PLUGIN_SET_DEFAULTS, &TempEvent, dummy); + PluginCall(PLUGIN_GET_DEVICEVALUENAMES, &TempEvent, dummy); // the plugin should populate ExtraTaskSettings with its default values. + } else { + // New task is empty task, thus save config now. + taskClear(taskIndex, true); // clear settings, and save + } +} + +// ******************************************************************************** +// Initialize task with some default values applicable for almost all tasks +// ******************************************************************************** +void setBasicTaskValues(taskIndex_t taskIndex, unsigned long taskdevicetimer, + bool enabled, const String& name, const int pins[3]) { + if (!validTaskIndex(taskIndex)) { return; } + const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(taskIndex); + + if (!validDeviceIndex(DeviceIndex)) { return; } + + LoadTaskSettings(taskIndex); // Make sure ExtraTaskSettings are up-to-date + + if (taskdevicetimer > 0) { + Settings.TaskDeviceTimer[taskIndex] = taskdevicetimer; + } else { + if (!Device[DeviceIndex].TimerOptional) { // Set default delay, unless it's optional... + Settings.TaskDeviceTimer[taskIndex] = Settings.Delay; + } + else { + Settings.TaskDeviceTimer[taskIndex] = 0; + } + } + Settings.TaskDeviceEnabled[taskIndex] = enabled; + //Settings.TaskDeviceEnabled[taskIndex].enabled = enabled; + safe_strncpy(ExtraTaskSettings.TaskDeviceName, name.c_str(), sizeof(ExtraTaskSettings.TaskDeviceName)); + + // FIXME TD-er: Check for valid GPIO pin (and -1 for "not set") + Settings.TaskDevicePin1[taskIndex] = pins[0]; + Settings.TaskDevicePin2[taskIndex] = pins[1]; + Settings.TaskDevicePin3[taskIndex] = pins[2]; +} + +#if FEATURE_CAN +void initCAN() +{ + if (Settings.isCAN_valid()) + { + CAN.end(); //Make sure we clear any previous configuration + CAN.setPins(Settings.CAN_Rx_pin, Settings.CAN_Tx_pin); + if (!CAN.begin(Settings.CAN_baudrate)) + { + addLog(LOG_LEVEL_ERROR, F("CAN : Unable to initialize CAN interface")); + } + else + { + addLog(LOG_LEVEL_INFO, F("CAN : CAN interface initialized")); + } + } + else + { + addLog(LOG_LEVEL_INFO, F("CAN : CAN not enabled")); + } +} +#endif \ No newline at end of file diff --git a/src/src/Helpers/Hardware.h b/src/src/Helpers/Hardware.h index bfff165a25..d028f9796d 100644 --- a/src/src/Helpers/Hardware.h +++ b/src/src/Helpers/Hardware.h @@ -65,6 +65,9 @@ int getCPU_MinFreqMHz(); #endif // ifdef ESP32 +#if FEATURE_CAN +void initCAN(); +#endif /*********************************************************************************************\ * High entropy hardware random generator diff --git a/src/src/Helpers/StringGenerator_GPIO.cpp b/src/src/Helpers/StringGenerator_GPIO.cpp index 3c0450930d..3e9c3780fa 100644 --- a/src/src/Helpers/StringGenerator_GPIO.cpp +++ b/src/src/Helpers/StringGenerator_GPIO.cpp @@ -224,6 +224,10 @@ const __FlashStringHelper* getConflictingUse(int gpio, PinSelectPurpose purpose) bool includeEthernet = true; #endif // if FEATURE_ETHERNET + #if FEATURE_CAN + bool includeCAN = true; + #endif + switch (purpose) { case PinSelectPurpose::I2C: includeI2C = false; @@ -252,6 +256,11 @@ const __FlashStringHelper* getConflictingUse(int gpio, PinSelectPurpose purpose) includeSDCard = false; break; #endif + #if FEATURE_CAN + case PinSelectPurpose::CAN: + includeCAN = false; + break; + #endif } if (includeI2C && Settings.isI2C_pin(gpio)) { @@ -313,7 +322,12 @@ const __FlashStringHelper* getConflictingUse(int gpio, PinSelectPurpose purpose) } #endif // if FEATURE_ETHERNET - + #if FEATURE_CAN + if (includeCAN && Settings.isCAN_pin(gpio)) + { + return (Settings.CAN_Rx_pin == gpio) ? F("CAN RX") : F("CAN TX"); + } + #endif return F(""); } diff --git a/src/src/Helpers/StringGenerator_GPIO.h b/src/src/Helpers/StringGenerator_GPIO.h index 2b9271ff14..cafbd785f8 100644 --- a/src/src/Helpers/StringGenerator_GPIO.h +++ b/src/src/Helpers/StringGenerator_GPIO.h @@ -31,7 +31,9 @@ enum class PinSelectPurpose : uint8_t { #if FEATURE_SD SD_Card, #endif - + #if FEATURE_CAN + CAN, + #endif }; diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index c91e854462..49fc1be5c9 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -81,6 +81,15 @@ void handle_hardware() { #endif Settings.NetworkMedium = static_cast(getFormItemInt(F("ethwifi"))); #endif // if FEATURE_ETHERNET + #if FEATURE_CAN + Settings.CAN_Rx_pin = getFormItemInt(F("canrxpin"), -1); + Settings.CAN_Tx_pin = getFormItemInt(F("cantxpin"), -1); + Settings.CAN_baudrate = getFormItemInt(F("canbaudrate"), DEFAULT_CAN_BAUDRATE); + Settings.CAN_node_id = getFormItemInt(F("cannodeid"), 0); + if (!Settings.isCAN_valid()) { + error += F("User-defined CAN is not configured correctly!\n"); + } + #endif int gpio = 0; while (gpio <= MAX_GPIO) { @@ -100,6 +109,9 @@ void handle_hardware() { if (error.isEmpty()) { // Apply I2C settings. initI2C(); + #if FEATURE_CAN + initCAN(); + #endif } } @@ -227,6 +239,15 @@ void handle_hardware() { addFormNote(F("CLK=GPIO-14 (D5), MISO=GPIO-12 (D6), MOSI=GPIO-13 (D7)")); #endif addFormNote(F("Chip Select (CS) config must be done in the plugin")); + + #if FEATURE_CAN + addFormSubHeader(F("CAN Interface")); + addFormPinSelect(PinSelectPurpose::CAN, F("TX"),F("cantxpin"), Settings.CAN_Tx_pin); + addFormPinSelect(PinSelectPurpose::CAN, F("RX"),F("canrxpin"), Settings.CAN_Rx_pin); + addFormNumericBox(F("Baudrate"), F("canbaudrate"), Settings.CAN_baudrate, 50E3, 1000E3); + addUnit(F("Hz")); + addFormNumericBox(F("Node ID"), F("cannodeid"), Settings.CAN_node_id, 0, 0x7FF); + #endif #if FEATURE_SD addFormSubHeader(F("SD Card")); diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index 965d8dde35..0df35bb830 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -1,1081 +1,1095 @@ - -#include "../WebServer/Markup.h" - -#include "../WebServer/HTML_wrappers.h" - -#include "../CustomBuild/ESPEasyLimits.h" - -#include "../Globals/Settings.h" - -#include "../Helpers/Convert.h" -#include "../Helpers/Hardware_GPIO.h" -#include "../Helpers/StringConverter_Numerical.h" -#include "../Helpers/StringConverter.h" -#include "../Helpers/StringGenerator_GPIO.h" - -#include "../../ESPEasy_common.h" - -// ******************************************************************************** -// Add Selector -// ******************************************************************************** -void addSelector(const __FlashStringHelper *id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled) -{ - addSelector(String(id), optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); -} - -void addSelector(const String & id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled) -{ - addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); -} - -void addSelector(const String& id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled) -{ - addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); -} - -void addSelector(const String & id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , const String & tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - // FIXME TD-er Change bool to disabled - if (reloadonchange) - { - addSelector_Head_reloadOnChange(id, classname, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } else { - do_addSelector_Head(id, classname, EMPTY_STRING, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } - addSelector_options(optionCount, options, indices, attr, selectedIndex); - addSelector_Foot(); -} - -void addSelector_reloadOnChange( - const String& id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - const String& onChangeCall, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , - const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - // FIXME TD-er Change bool to disabled - do_addSelector_Head(id, classname, onChangeCall, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - addSelector_options(optionCount, options, indices, attr, selectedIndex); - addSelector_Foot(); -} - - -void addSelector(const String & id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - // FIXME TD-er Change bool to disabled - if (reloadonchange) - { - addSelector_Head_reloadOnChange(id, classname, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } else { - do_addSelector_Head(id, classname, EMPTY_STRING, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } - addSelector_options(optionCount, options, indices, attr, selectedIndex); - addSelector_Foot(); -} - -void addSelector_options(int optionCount, const __FlashStringHelper *options[], const int indices[], const String attr[], int selectedIndex) -{ - for (uint8_t x = 0; x < optionCount; ++x) - { - const int index = indices ? indices[x] : x; - addSelector_Item( - options[x], - index, - selectedIndex == index, - false, - attr ? attr[x] : EMPTY_STRING); - if ((x & 0x07) == 0) delay(0); - } -} - -void addSelector_options(int optionCount, const String options[], const int indices[], const String attr[], int selectedIndex) -{ - for (uint8_t x = 0; x < optionCount; ++x) - { - const int index = indices ? indices[x] : x; - addSelector_Item( - options[x], - index, - selectedIndex == index, - false, - attr ? attr[x] : EMPTY_STRING); - if ((x & 0x07) == 0) delay(0); - } -} - -void addSelector_Head(const String& id) { - do_addSelector_Head(id, F("wide"), EMPTY_STRING, false - #if FEATURE_TOOLTIPS - , F("") - #endif // if FEATURE_TOOLTIPS - ); -} - -void addSelector_Head_reloadOnChange(const __FlashStringHelper * id) { - addSelector_Head_reloadOnChange(String(id), F("wide"), false); -} - -/* -void addSelector_Head_reloadOnChange(const String& id) { - addSelector_Head_reloadOnChange(id, F("wide"), false); -} -*/ - -void addSelector_Head_reloadOnChange(const String& id, - const __FlashStringHelper * classname, - bool disabled - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) { - do_addSelector_Head(id, classname, F("return dept_onchange(frmselect)"), disabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); -} - -void addSelector_Head_reloadOnChange(const String& id, const __FlashStringHelper * classname, const String& onChangeCall, bool disabled - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) { - do_addSelector_Head(id, classname, onChangeCall, disabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); -} - - -void do_addSelector_Head(const String& id, const __FlashStringHelper * classname, const String& onChangeCall, const bool& disabled - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - addHtml(F("")); -} - -void addUnit(const __FlashStringHelper *unit) -{ - addHtml(F(" [")); - addHtml(unit); - addHtml(']'); -} - -void addUnit(const String& unit) -{ - if (unit.isEmpty()) return; - addHtml(F(" [")); - addHtml(unit); - addHtml(']'); -} - -void addUnit(char unit) -{ - addHtml(F(" [")); - addHtml(unit); - addHtml(']'); -} - -void addRowLabel_tr_id(const __FlashStringHelper *label, const __FlashStringHelper *id) -{ - addRowLabel_tr_id(label, String(id)); -} - -void addRowLabel_tr_id(const __FlashStringHelper *label, const String& id) -{ - if (id.isEmpty()) { - addRowLabel(label); - } else { - addRowLabel_tr_id(String(label), id); - } -} - -void addRowLabel_tr_id(const String& label, const String& id) -{ - if (id.isEmpty()) { - addRowLabel(label); - } else { - addRowLabel(label, concat(F("tr_"), id)); - } -} - -void addRowLabel(const __FlashStringHelper *label) -{ - html_TR_TD(); - addHtml(concat(label, F(":"))); - html_TD(); -} - -void addRowLabel(const String& label, const String& id) -{ - if (id.length() > 0) { - addHtml(F("")); - } else { - html_TR_TD(); - } - - if (!label.isEmpty()) { - addHtml(label); - addHtml(':'); - } - addHtml(F("")); - html_TD(); -} - -// Add a row label and mark it with copy markers to copy it to clipboard. -void addRowLabel_copy(const __FlashStringHelper *label) { - addHtml(F("")); - html_copyText_TD(); - addHtml(label); - addHtml(':'); - html_copyText_marker(); - html_copyText_TD(); -} - -void addRowLabel_copy(const String& label) { - addHtml(F("")); - html_copyText_TD(); - addHtml(label); - addHtml(':'); - html_copyText_marker(); - html_copyText_TD(); -} - -void addRowLabel(LabelType::Enum label) { - addRowLabel(getLabel(label)); -} - -void addRowLabelValue(LabelType::Enum label) { - addRowLabel(getLabel(label)); - addHtml(getValue(label)); - addUnit(getFormUnit(label)); -} - -void addRowLabelValues(const LabelType::Enum labels[]) { - size_t i = 0; - LabelType::Enum cur = static_cast(pgm_read_byte(labels + i)); - - while (true) { - const LabelType::Enum next = static_cast(pgm_read_byte(labels + i + 1)); - addRowLabelValue(cur); - if (next == LabelType::MAX_LABEL) { - return; - } - ++i; - cur = next; - } -} - -void addRowLabelValue_copy(LabelType::Enum label) { - addRowLabel_copy(getLabel(label)); - addHtml(getValue(label)); - addUnit(getFormUnit(label)); -} - -// ******************************************************************************** -// Add a header -// ******************************************************************************** -void addTableSeparator(const __FlashStringHelper *label, int colspan, int h_size) -{ - addHtml(strformat( - F(""), - colspan, h_size)); - addHtml(label); - addHtml(strformat( - F(""), - h_size)); -} - -void addTableSeparator(const __FlashStringHelper *label, int colspan, int h_size, const __FlashStringHelper *helpButton) -{ - addTableSeparator(String(label), colspan, h_size, String(helpButton)); -} - -void addTableSeparator(const String& label, int colspan, int h_size, const String& helpButton) { - addHtml(strformat( - F(""), - colspan, h_size)); - addHtml(label); - - if (!helpButton.isEmpty()) { - addHelpButton(helpButton); - } - addHtml(strformat( - F(""), - h_size)); -} - -void addFormHeader(const __FlashStringHelper *header) { - addFormHeader(header, F(""), F("")); -} - -void addFormHeader(const __FlashStringHelper *header, - const __FlashStringHelper *helpButton) -{ - addFormHeader(header, helpButton, F("")); -} - -void addFormHeader(const __FlashStringHelper *header, - const __FlashStringHelper *helpButton, - const __FlashStringHelper *rtdHelpButton) -{ - html_TR(); - html_table_header(header, helpButton, rtdHelpButton, 300); - html_table_header(F("")); -} - -/* -void addFormHeader(const String& header, const String& helpButton) { - addFormHeader(header, helpButton, EMPTY_STRING); -} - -void addFormHeader(const String& header, const String& helpButton, const String& rtdHelpButton) -{ - html_TR(); - html_table_header(header, helpButton, rtdHelpButton, 225); - html_table_header(F("")); -} -*/ - -// ******************************************************************************** -// Add a sub header -// ******************************************************************************** -void addFormSubHeader(const __FlashStringHelper *header) { - addTableSeparator(header, 2, 3); -} - -void addFormSubHeader(const String& header) -{ - addTableSeparator(header, 2, 3); -} - -// ******************************************************************************** -// Add a checkbox -// ******************************************************************************** -void addCheckBox(const __FlashStringHelper *id, bool checked, bool disabled) -{ - addCheckBox(String(id), checked, disabled); -} - -void addCheckBox(const String& id, bool checked, bool disabled - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - addHtml(F("")); -} - -// ******************************************************************************** -// Add a numeric box -// ******************************************************************************** -void addNumericBox(const __FlashStringHelper *id, int value, int min, int max, bool disabled) -{ - addNumericBox(String(id), value, min, max, disabled); -} - -void addNumericBox(const String& id, int value, int min, int max - #if FEATURE_TOOLTIPS - , const __FlashStringHelper * classname, const String& tooltip - #endif // if FEATURE_TOOLTIPS - , bool disabled - ) -{ - addHtml(F(" 0) { - addHtmlAttribute(F("title"), tooltip); - } - #endif // if FEATURE_TOOLTIPS - - if (disabled) { - addDisabled(); - } - - if (value < min) { - value = min; - } - - if (value > max) { - value = max; - } - - if (min != INT_MIN) - { - addHtmlAttribute(F("min"), min); - } - - if (max != INT_MAX) - { - addHtmlAttribute(F("max"), max); - } - addHtmlAttribute(F("value"), value); - addHtml('>'); -} - -#if FEATURE_TOOLTIPS -void addNumericBox(const String& id, int value, int min, int max, bool disabled) -{ - addNumericBox(id, value, min, max, F("widenumber"), EMPTY_STRING, disabled); -} - -#endif // if FEATURE_TOOLTIPS - -void addFloatNumberBox(const String& id, float value, float min, float max, unsigned int nrDecimals, float stepsize - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - addHtml(strformat( - F("'); -} - -// ******************************************************************************** -// Add Textbox -// ******************************************************************************** -void addTextBox(const __FlashStringHelper * id, const String& value, int maxlength, bool readonly, bool required, const String& pattern) { - addTextBox(id, value, maxlength, readonly, required, pattern, F("wide")); -} - -void addTextBox(const String& id, const String& value, int maxlength, bool readonly, bool required, const String& pattern) { - addTextBox(id, value, maxlength, readonly, required, pattern, F("wide")); -} - -void addTextBox(const String & id, - const String & value, - int maxlength, - bool readonly, - bool required, - const String & pattern, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - , - const String& datalist - ) -{ - addHtml(F(" 0) { - addHtmlAttribute(F("maxlength"), maxlength); - } - if (!datalist.isEmpty()) { - addHtmlAttribute(F("list"), datalist); - } - addHtmlAttribute(F("value"), value); - - if (readonly) { - addHtml(F(" readonly ")); - } - - if (required) { - addHtml(F(" required ")); - } - - if (pattern.length() > 0) { - addHtmlAttribute(F("pattern"), pattern); - } - - #if FEATURE_TOOLTIPS - - if (tooltip.length() > 0) { - addHtmlAttribute(F("title"), tooltip); - } - #endif // if FEATURE_TOOLTIPS - addHtml('>'); -} - - - -// ******************************************************************************** -// Add Textarea -// ******************************************************************************** -void addTextArea(const String & id, - const String & value, - int maxlength, - int rows, - int columns, - bool readonly, - bool required - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - addHtml(F("")); -} - -// ******************************************************************************** -// Add Help Buttons -// ******************************************************************************** - -// adds a Help Button with points to the the given Wiki Subpage -// If url starts with "RTD", it will be considered as a Read-the-docs link -void addHelpButton(const __FlashStringHelper *url) { - addHelpButton(String(url)); -} - -void addHelpButton(const String& url) { -#ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON - - if (url.startsWith("RTD")) { - addRTDHelpButton(url.substring(3)); - } else { - addHelpButton(url, false); - } -#endif // ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON -} - -void addRTDHelpButton(const String& url) -{ - addHelpButton(url, true); -} - -void addHelpButton(const String& url, bool isRTD) -{ - #ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON - addHtmlLink( - F("button help"), - makeDocLink(url, isRTD), - isRTD ? F("i") : F("?")); - #endif // ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON -} - -void addRTDPluginButton(pluginID_t pluginID) { - addRTDHelpButton( - strformat( - F("Plugin/%s.html"), - get_formatted_Plugin_number(pluginID).c_str())); - - constexpr pluginID_t PLUGIN_ID_P076_HLW8012(76); - constexpr pluginID_t PLUGIN_ID_P077_CSE7766(77); - - if ((pluginID == PLUGIN_ID_P076_HLW8012) || - (pluginID == PLUGIN_ID_P077_CSE7766)) { - addHtmlLink( - F("button help"), - makeDocLink(F("Reference/Safety.html"), true), - F("⚡")); // High voltage sign - } -} - -# ifndef LIMIT_BUILD_SIZE -void addRTDControllerButton(cpluginID_t cpluginID) { - addRTDHelpButton( - strformat( - F("Controller/%s.html"), - get_formatted_Controller_number(cpluginID).c_str())); -} -# endif // ifndef LIMIT_BUILD_SIZE - -String makeDocLink(const String& url, bool isRTD) { - String result; - - if (!url.startsWith(F("http"))) { - if (isRTD) { - result += F("https://espeasy.readthedocs.io/en/latest/"); - } else { - result += F("http://www.letscontrolit.com/wiki/index.php/"); - } - } - result += url; - return result; -} - -void addPinSelect(PinSelectPurpose purpose, const __FlashStringHelper *id, int choice) -{ - addPinSelect(purpose, String(id), choice); -} - -void addPinSelect(PinSelectPurpose purpose, const String& id, int choice) -{ - addSelector_Head(id); - - // At i == 0 && gpio == -1, add the "- None -" option first - int i = 0; - int gpio = -1; - - while (gpio <= MAX_GPIO) { - int pinnr = -1; - bool input, output, warning = false; - - // Make sure getGpioInfo is called (compiler may optimize it away if (i == 0)) - const bool UsableGPIO = getGpioInfo(gpio, pinnr, input, output, warning); - - if (UsableGPIO || (i == 0)) { - addPinSelector_Item( - purpose, - concat( - createGPIO_label(gpio, pinnr, input, output, warning), - getConflictingUse_wrapped(gpio, purpose)), - gpio, - choice == gpio); - - ++i; - } - ++gpio; - } - addSelector_Foot(); -} - -#ifdef ESP32 -void addADC_PinSelect(AdcPinSelectPurpose purpose, const String& id, int choice) -{ - addSelector_Head(id); - - // At i == 0 && gpio == -1, add the "Hall Effect" option first - int i = 0; - int gpio = -1; - - if ( -#if HAS_HALL_EFFECT_SENSOR - (purpose == AdcPinSelectPurpose::ADC_Touch_HallEffect) || -#endif - (purpose == AdcPinSelectPurpose::ADC_Touch_Optional)) { - addPinSelector_Item( - PinSelectPurpose::Generic, - purpose == AdcPinSelectPurpose::ADC_Touch_Optional ? F("- None -") : formatGpioName_ADC(gpio), - gpio, - choice == gpio); - } - - while (i <= MAX_GPIO && gpio <= MAX_GPIO) { - int pinnr = -1; - bool input, output, warning; - - if (purpose == AdcPinSelectPurpose::TouchOnly) { - // For touch only list, sort based on touch number - // Default sort is on GPIO number. - gpio = touchPinToGpio(i); - } else { - ++gpio; - } - - if (getGpioInfo(gpio, pinnr, input, output, warning)) { - int adc, ch, t; - - if (getADC_gpio_info(gpio, adc, ch, t)) { - if ((purpose != AdcPinSelectPurpose::TouchOnly) || (t >= 0)) { - String gpio_label; - gpio_label = formatGpioName_ADC(gpio); - - if (adc != 0) { - gpio_label += F(" / "); - gpio_label += createGPIO_label(gpio, pinnr, input, output, warning); - gpio_label += getConflictingUse_wrapped(gpio); - } - addPinSelector_Item( - PinSelectPurpose::Generic, - gpio_label, - gpio, - choice == gpio); - } - } - } - ++i; - } - addSelector_Foot(); -} - -void addDAC_PinSelect(const String& id, int choice) -{ - addSelector_Head(id); - - // At i == 0 && gpio == -1, add the "- None -" option first - int i = 0; - int gpio = -1; - - while (gpio <= MAX_GPIO) { - int pinnr = -1; - bool input = false; - bool output = false; - bool warning = false; - int dac = 0; - - // Make sure getGpioInfo is called (compiler may optimize it away if (i == 0)) - const bool UsableGPIO = getDAC_gpio_info(gpio, dac); // getGpioInfo(gpio, pinnr, input, output, warning); - - if (UsableGPIO || (i == 0)) { - if (getGpioInfo(gpio, pinnr, input, output, warning) || (i == 0)) { - String gpio_label = formatGpioName_DAC(gpio); - - if (dac != 0) { - gpio_label += F(" / "); - gpio_label += createGPIO_label(gpio, pinnr, input, output, warning); - gpio_label += getConflictingUse_wrapped(gpio, PinSelectPurpose::DAC); - } - addPinSelector_Item( - PinSelectPurpose::DAC, - gpio_label, - gpio, - choice == gpio); - } - ++i; - } - ++gpio; - } - addSelector_Foot(); -} - -#endif // ifdef ESP32 + +#include "../WebServer/Markup.h" + +#include "../WebServer/HTML_wrappers.h" + +#include "../CustomBuild/ESPEasyLimits.h" + +#include "../Globals/Settings.h" + +#include "../Helpers/Convert.h" +#include "../Helpers/Hardware_GPIO.h" +#include "../Helpers/StringConverter_Numerical.h" +#include "../Helpers/StringConverter.h" +#include "../Helpers/StringGenerator_GPIO.h" + +#include "../../ESPEasy_common.h" + +// ******************************************************************************** +// Add Selector +// ******************************************************************************** +void addSelector(const __FlashStringHelper *id, + int optionCount, + const __FlashStringHelper *options[], + const int indices[], + const String attr[], + int selectedIndex, + bool reloadonchange, + bool enabled) +{ + addSelector(String(id), optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); +} + +void addSelector(const String & id, + int optionCount, + const __FlashStringHelper *options[], + const int indices[], + const String attr[], + int selectedIndex, + bool reloadonchange, + bool enabled) +{ + addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); +} + +void addSelector(const String& id, + int optionCount, + const String options[], + const int indices[], + const String attr[], + int selectedIndex, + bool reloadonchange, + bool enabled) +{ + addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); +} + +void addSelector(const String & id, + int optionCount, + const __FlashStringHelper *options[], + const int indices[], + const String attr[], + int selectedIndex, + bool reloadonchange, + bool enabled, + const __FlashStringHelper * classname + #if FEATURE_TOOLTIPS + , const String & tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + // FIXME TD-er Change bool to disabled + if (reloadonchange) + { + addSelector_Head_reloadOnChange(id, classname, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + } else { + do_addSelector_Head(id, classname, EMPTY_STRING, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + } + addSelector_options(optionCount, options, indices, attr, selectedIndex); + addSelector_Foot(); +} + +void addSelector_reloadOnChange( + const String& id, + int optionCount, + const String options[], + const int indices[], + const String attr[], + int selectedIndex, + const String& onChangeCall, + bool enabled, + const __FlashStringHelper * classname + #if FEATURE_TOOLTIPS + , + const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + // FIXME TD-er Change bool to disabled + do_addSelector_Head(id, classname, onChangeCall, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + addSelector_options(optionCount, options, indices, attr, selectedIndex); + addSelector_Foot(); +} + + +void addSelector(const String & id, + int optionCount, + const String options[], + const int indices[], + const String attr[], + int selectedIndex, + bool reloadonchange, + bool enabled, + const __FlashStringHelper * classname + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + // FIXME TD-er Change bool to disabled + if (reloadonchange) + { + addSelector_Head_reloadOnChange(id, classname, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + } else { + do_addSelector_Head(id, classname, EMPTY_STRING, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + } + addSelector_options(optionCount, options, indices, attr, selectedIndex); + addSelector_Foot(); +} + +void addSelector_options(int optionCount, const __FlashStringHelper *options[], const int indices[], const String attr[], int selectedIndex) +{ + for (uint8_t x = 0; x < optionCount; ++x) + { + const int index = indices ? indices[x] : x; + addSelector_Item( + options[x], + index, + selectedIndex == index, + false, + attr ? attr[x] : EMPTY_STRING); + if ((x & 0x07) == 0) delay(0); + } +} + +void addSelector_options(int optionCount, const String options[], const int indices[], const String attr[], int selectedIndex) +{ + for (uint8_t x = 0; x < optionCount; ++x) + { + const int index = indices ? indices[x] : x; + addSelector_Item( + options[x], + index, + selectedIndex == index, + false, + attr ? attr[x] : EMPTY_STRING); + if ((x & 0x07) == 0) delay(0); + } +} + +void addSelector_Head(const String& id) { + do_addSelector_Head(id, F("wide"), EMPTY_STRING, false + #if FEATURE_TOOLTIPS + , F("") + #endif // if FEATURE_TOOLTIPS + ); +} + +void addSelector_Head_reloadOnChange(const __FlashStringHelper * id) { + addSelector_Head_reloadOnChange(String(id), F("wide"), false); +} + +/* +void addSelector_Head_reloadOnChange(const String& id) { + addSelector_Head_reloadOnChange(id, F("wide"), false); +} +*/ + +void addSelector_Head_reloadOnChange(const String& id, + const __FlashStringHelper * classname, + bool disabled + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) { + do_addSelector_Head(id, classname, F("return dept_onchange(frmselect)"), disabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); +} + +void addSelector_Head_reloadOnChange(const String& id, const __FlashStringHelper * classname, const String& onChangeCall, bool disabled + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) { + do_addSelector_Head(id, classname, onChangeCall, disabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); +} + + +void do_addSelector_Head(const String& id, const __FlashStringHelper * classname, const String& onChangeCall, const bool& disabled + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + addHtml(F("")); +} + +void addUnit(const __FlashStringHelper *unit) +{ + addHtml(F(" [")); + addHtml(unit); + addHtml(']'); +} + +void addUnit(const String& unit) +{ + if (unit.isEmpty()) return; + addHtml(F(" [")); + addHtml(unit); + addHtml(']'); +} + +void addUnit(char unit) +{ + addHtml(F(" [")); + addHtml(unit); + addHtml(']'); +} + +void addRowLabel_tr_id(const __FlashStringHelper *label, const __FlashStringHelper *id) +{ + addRowLabel_tr_id(label, String(id)); +} + +void addRowLabel_tr_id(const __FlashStringHelper *label, const String& id) +{ + if (id.isEmpty()) { + addRowLabel(label); + } else { + addRowLabel_tr_id(String(label), id); + } +} + +void addRowLabel_tr_id(const String& label, const String& id) +{ + if (id.isEmpty()) { + addRowLabel(label); + } else { + addRowLabel(label, concat(F("tr_"), id)); + } +} + +void addRowLabel(const __FlashStringHelper *label) +{ + html_TR_TD(); + addHtml(concat(label, F(":"))); + html_TD(); +} + +void addRowLabel(const String& label, const String& id) +{ + if (id.length() > 0) { + addHtml(F("")); + } else { + html_TR_TD(); + } + + if (!label.isEmpty()) { + addHtml(label); + addHtml(':'); + } + addHtml(F("")); + html_TD(); +} + +// Add a row label and mark it with copy markers to copy it to clipboard. +void addRowLabel_copy(const __FlashStringHelper *label) { + addHtml(F("")); + html_copyText_TD(); + addHtml(label); + addHtml(':'); + html_copyText_marker(); + html_copyText_TD(); +} + +void addRowLabel_copy(const String& label) { + addHtml(F("")); + html_copyText_TD(); + addHtml(label); + addHtml(':'); + html_copyText_marker(); + html_copyText_TD(); +} + +void addRowLabel(LabelType::Enum label) { + addRowLabel(getLabel(label)); +} + +void addRowLabelValue(LabelType::Enum label) { + addRowLabel(getLabel(label)); + addHtml(getValue(label)); + addUnit(getFormUnit(label)); +} + +void addRowLabelValues(const LabelType::Enum labels[]) { + size_t i = 0; + LabelType::Enum cur = static_cast(pgm_read_byte(labels + i)); + + while (true) { + const LabelType::Enum next = static_cast(pgm_read_byte(labels + i + 1)); + addRowLabelValue(cur); + if (next == LabelType::MAX_LABEL) { + return; + } + ++i; + cur = next; + } +} + +void addRowLabelValue_copy(LabelType::Enum label) { + addRowLabel_copy(getLabel(label)); + addHtml(getValue(label)); + addUnit(getFormUnit(label)); +} + +// ******************************************************************************** +// Add a header +// ******************************************************************************** +void addTableSeparator(const __FlashStringHelper *label, int colspan, int h_size) +{ + addHtml(strformat( + F(""), + colspan, h_size)); + addHtml(label); + addHtml(strformat( + F(""), + h_size)); +} + +void addTableSeparator(const __FlashStringHelper *label, int colspan, int h_size, const __FlashStringHelper *helpButton) +{ + addTableSeparator(String(label), colspan, h_size, String(helpButton)); +} + +void addTableSeparator(const String& label, int colspan, int h_size, const String& helpButton) { + addHtml(strformat( + F(""), + colspan, h_size)); + addHtml(label); + + if (!helpButton.isEmpty()) { + addHelpButton(helpButton); + } + addHtml(strformat( + F(""), + h_size)); +} + +void addFormHeader(const __FlashStringHelper *header) { + addFormHeader(header, F(""), F("")); +} + +void addFormHeader(const __FlashStringHelper *header, + const __FlashStringHelper *helpButton) +{ + addFormHeader(header, helpButton, F("")); +} + +void addFormHeader(const __FlashStringHelper *header, + const __FlashStringHelper *helpButton, + const __FlashStringHelper *rtdHelpButton) +{ + html_TR(); + html_table_header(header, helpButton, rtdHelpButton, 300); + html_table_header(F("")); +} + +/* +void addFormHeader(const String& header, const String& helpButton) { + addFormHeader(header, helpButton, EMPTY_STRING); +} + +void addFormHeader(const String& header, const String& helpButton, const String& rtdHelpButton) +{ + html_TR(); + html_table_header(header, helpButton, rtdHelpButton, 225); + html_table_header(F("")); +} +*/ + +// ******************************************************************************** +// Add a sub header +// ******************************************************************************** +void addFormSubHeader(const __FlashStringHelper *header) { + addTableSeparator(header, 2, 3); +} + +void addFormSubHeader(const String& header) +{ + addTableSeparator(header, 2, 3); +} + +// ******************************************************************************** +// Add a checkbox +// ******************************************************************************** +void addCheckBox(const __FlashStringHelper *id, bool checked, bool disabled) +{ + addCheckBox(String(id), checked, disabled); +} + +void addCheckBox(const String& id, bool checked, bool disabled + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + addHtml(F("")); +} + +// ******************************************************************************** +// Add a numeric box +// ******************************************************************************** +void addNumericBox(const __FlashStringHelper *id, int value, int min, int max, bool disabled) +{ + addNumericBox(String(id), value, min, max, disabled); +} + +void addNumericBox(const String& id, int value, int min, int max + #if FEATURE_TOOLTIPS + , const __FlashStringHelper * classname, const String& tooltip + #endif // if FEATURE_TOOLTIPS + , bool disabled + ) +{ + addHtml(F(" 0) { + addHtmlAttribute(F("title"), tooltip); + } + #endif // if FEATURE_TOOLTIPS + + if (disabled) { + addDisabled(); + } + + if (value < min) { + value = min; + } + + if (value > max) { + value = max; + } + + if (min != INT_MIN) + { + addHtmlAttribute(F("min"), min); + } + + if (max != INT_MAX) + { + addHtmlAttribute(F("max"), max); + } + addHtmlAttribute(F("value"), value); + addHtml('>'); +} + +#if FEATURE_TOOLTIPS +void addNumericBox(const String& id, int value, int min, int max, bool disabled) +{ + addNumericBox(id, value, min, max, F("widenumber"), EMPTY_STRING, disabled); +} + +#endif // if FEATURE_TOOLTIPS + +void addFloatNumberBox(const String& id, float value, float min, float max, unsigned int nrDecimals, float stepsize + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + addHtml(strformat( + F("'); +} + +// ******************************************************************************** +// Add Textbox +// ******************************************************************************** +void addTextBox(const __FlashStringHelper * id, const String& value, int maxlength, bool readonly, bool required, const String& pattern) { + addTextBox(id, value, maxlength, readonly, required, pattern, F("wide")); +} + +void addTextBox(const String& id, const String& value, int maxlength, bool readonly, bool required, const String& pattern) { + addTextBox(id, value, maxlength, readonly, required, pattern, F("wide")); +} + +void addTextBox(const String & id, + const String & value, + int maxlength, + bool readonly, + bool required, + const String & pattern, + const __FlashStringHelper * classname + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + , + const String& datalist + ) +{ + addHtml(F(" 0) { + addHtmlAttribute(F("maxlength"), maxlength); + } + if (!datalist.isEmpty()) { + addHtmlAttribute(F("list"), datalist); + } + addHtmlAttribute(F("value"), value); + + if (readonly) { + addHtml(F(" readonly ")); + } + + if (required) { + addHtml(F(" required ")); + } + + if (pattern.length() > 0) { + addHtmlAttribute(F("pattern"), pattern); + } + + #if FEATURE_TOOLTIPS + + if (tooltip.length() > 0) { + addHtmlAttribute(F("title"), tooltip); + } + #endif // if FEATURE_TOOLTIPS + addHtml('>'); +} + + + +// ******************************************************************************** +// Add Textarea +// ******************************************************************************** +void addTextArea(const String & id, + const String & value, + int maxlength, + int rows, + int columns, + bool readonly, + bool required + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + addHtml(F("")); +} + +// ******************************************************************************** +// Add Help Buttons +// ******************************************************************************** + +// adds a Help Button with points to the the given Wiki Subpage +// If url starts with "RTD", it will be considered as a Read-the-docs link +void addHelpButton(const __FlashStringHelper *url) { + addHelpButton(String(url)); +} + +void addHelpButton(const String& url) { +#ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON + + if (url.startsWith("RTD")) { + addRTDHelpButton(url.substring(3)); + } else { + addHelpButton(url, false); + } +#endif // ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON +} + +void addRTDHelpButton(const String& url) +{ + addHelpButton(url, true); +} + +void addHelpButton(const String& url, bool isRTD) +{ + #ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON + addHtmlLink( + F("button help"), + makeDocLink(url, isRTD), + isRTD ? F("i") : F("?")); + #endif // ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON +} + +void addRTDPluginButton(pluginID_t pluginID) { + addRTDHelpButton( + strformat( + F("Plugin/%s.html"), + get_formatted_Plugin_number(pluginID).c_str())); + + constexpr pluginID_t PLUGIN_ID_P076_HLW8012(76); + constexpr pluginID_t PLUGIN_ID_P077_CSE7766(77); + + if ((pluginID == PLUGIN_ID_P076_HLW8012) || + (pluginID == PLUGIN_ID_P077_CSE7766)) { + addHtmlLink( + F("button help"), + makeDocLink(F("Reference/Safety.html"), true), + F("⚡")); // High voltage sign + } +} + +# ifndef LIMIT_BUILD_SIZE +void addRTDControllerButton(cpluginID_t cpluginID) { + addRTDHelpButton( + strformat( + F("Controller/%s.html"), + get_formatted_Controller_number(cpluginID).c_str())); +} +# endif // ifndef LIMIT_BUILD_SIZE + +String makeDocLink(const String& url, bool isRTD) { + String result; + + if (!url.startsWith(F("http"))) { + if (isRTD) { + result += F("https://espeasy.readthedocs.io/en/latest/"); + } else { + result += F("http://www.letscontrolit.com/wiki/index.php/"); + } + } + result += url; + return result; +} + +void addPinSelect(PinSelectPurpose purpose, const __FlashStringHelper *id, int choice) +{ + addPinSelect(purpose, String(id), choice); +} + +void addPinSelect(PinSelectPurpose purpose, const String& id, int choice) +{ + addSelector_Head(id); + + // At i == 0 && gpio == -1, add the "- None -" option first + int i = 0; + int gpio = -1; + + while (gpio <= MAX_GPIO) { + int pinnr = -1; + bool input, output, warning = false; + + // Make sure getGpioInfo is called (compiler may optimize it away if (i == 0)) + const bool UsableGPIO = getGpioInfo(gpio, pinnr, input, output, warning); + + if (UsableGPIO || (i == 0)) { + addPinSelector_Item( + purpose, + concat( + createGPIO_label(gpio, pinnr, input, output, warning), + getConflictingUse_wrapped(gpio, purpose)), + gpio, + choice == gpio); + + ++i; + } + ++gpio; + } + addSelector_Foot(); +} + +#ifdef ESP32 +void addADC_PinSelect(AdcPinSelectPurpose purpose, const String& id, int choice) +{ + addSelector_Head(id); + + // At i == 0 && gpio == -1, add the "Hall Effect" option first + int i = 0; + int gpio = -1; + + if ( +#if HAS_HALL_EFFECT_SENSOR + (purpose == AdcPinSelectPurpose::ADC_Touch_HallEffect) || +#endif + (purpose == AdcPinSelectPurpose::ADC_Touch_Optional)) { + addPinSelector_Item( + PinSelectPurpose::Generic, + purpose == AdcPinSelectPurpose::ADC_Touch_Optional ? F("- None -") : formatGpioName_ADC(gpio), + gpio, + choice == gpio); + } + + while (i <= MAX_GPIO && gpio <= MAX_GPIO) { + int pinnr = -1; + bool input, output, warning; + + if (purpose == AdcPinSelectPurpose::TouchOnly) { + // For touch only list, sort based on touch number + // Default sort is on GPIO number. + gpio = touchPinToGpio(i); + } else { + ++gpio; + } + + if (getGpioInfo(gpio, pinnr, input, output, warning)) { + int adc, ch, t; + + if (getADC_gpio_info(gpio, adc, ch, t)) { + if ((purpose != AdcPinSelectPurpose::TouchOnly) || (t >= 0)) { + String gpio_label; + gpio_label = formatGpioName_ADC(gpio); + + if (adc != 0) { + gpio_label += F(" / "); + gpio_label += createGPIO_label(gpio, pinnr, input, output, warning); + gpio_label += getConflictingUse_wrapped(gpio); + } + addPinSelector_Item( + PinSelectPurpose::Generic, + gpio_label, + gpio, + choice == gpio); + } + } + } + ++i; + } + addSelector_Foot(); +} + +void addDAC_PinSelect(const String& id, int choice) +{ + addSelector_Head(id); + + // At i == 0 && gpio == -1, add the "- None -" option first + int i = 0; + int gpio = -1; + + while (gpio <= MAX_GPIO) { + int pinnr = -1; + bool input = false; + bool output = false; + bool warning = false; + int dac = 0; + + // Make sure getGpioInfo is called (compiler may optimize it away if (i == 0)) + const bool UsableGPIO = getDAC_gpio_info(gpio, dac); // getGpioInfo(gpio, pinnr, input, output, warning); + + if (UsableGPIO || (i == 0)) { + if (getGpioInfo(gpio, pinnr, input, output, warning) || (i == 0)) { + String gpio_label = formatGpioName_DAC(gpio); + + if (dac != 0) { + gpio_label += F(" / "); + gpio_label += createGPIO_label(gpio, pinnr, input, output, warning); + gpio_label += getConflictingUse_wrapped(gpio, PinSelectPurpose::DAC); + } + addPinSelector_Item( + PinSelectPurpose::DAC, + gpio_label, + gpio, + choice == gpio); + } + ++i; + } + ++gpio; + } + addSelector_Foot(); +} + +#endif // ifdef ESP32 From 66680c6f9afecdb6ac30385b8925677ccefe064e Mon Sep 17 00:00:00 2001 From: Bruno Castro Date: Tue, 10 Sep 2024 21:38:43 -0300 Subject: [PATCH 02/15] CAN controller implementation --- src/_C020.cpp | 90 ++ src/src/Commands/CAN.cpp | 21 + src/src/Commands/CAN.h | 10 + src/src/Commands/InternalCommands.cpp | 982 +++++++++--------- src/src/Commands/InternalCommands_decoder.cpp | 4 + src/src/Commands/InternalCommands_decoder.h | 5 + src/src/CustomBuild/define_plugin_sets.h | 5 + src/src/Helpers/_Plugin_Helper_CAN.cpp | 209 ++++ src/src/Helpers/_Plugin_Helper_CAN.h | 14 + 9 files changed, 852 insertions(+), 488 deletions(-) create mode 100644 src/_C020.cpp create mode 100644 src/src/Commands/CAN.cpp create mode 100644 src/src/Commands/CAN.h create mode 100644 src/src/Helpers/_Plugin_Helper_CAN.cpp create mode 100644 src/src/Helpers/_Plugin_Helper_CAN.h diff --git a/src/_C020.cpp b/src/_C020.cpp new file mode 100644 index 0000000000..9d4a24f9ad --- /dev/null +++ b/src/_C020.cpp @@ -0,0 +1,90 @@ +#include "src/Helpers/_CPlugin_Helper.h" + +#ifdef USES_C020 + +// ####################################################################################################### +// ########################### Controller Plugin 020: CAN - SJA1000 ###################################### +// ####################################################################################################### + +# define CPLUGIN_020 +# define CPLUGIN_ID_020 20 +# define CPLUGIN_NAME_020 "CAN 2.0 - TWAI" + +# include "src/DataTypes/ESPEasy_plugin_functions.h" +# include "src/Globals/CPlugins.h" +# include "src/Helpers/_Plugin_Helper_CAN.h" + +bool CPlugin_020(CPlugin::Function function, struct EventStruct *event, String& string) +{ + bool success = false; + + switch (function) + { + case CPlugin::Function::CPLUGIN_PROTOCOL_ADD: + { + ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_020; + proto.bits = 0; + break; + } + + case CPlugin::Function::CPLUGIN_GET_DEVICENAME: + { + string = F(CPLUGIN_NAME_020); + break; + } + + case CPlugin::Function::CPLUGIN_INIT: + { + break; + } + + case CPlugin::Function::CPLUGIN_EXIT: + { + break; + } + + case CPlugin::Function::CPLUGIN_PROTOCOL_SEND: + { + canHelper_sendTaskData(event); + break; + } + + case CPlugin::Function::CPLUGIN_FIFTY_PER_SECOND: + { + canHelper_recv(); + break; + } + + // case CPlugin::Function::CPLUGIN_INTERVAL: + // { + // for (taskIndex_t x = 0; x < TASKS_MAX; x++) { + // constexpr pluginID_t PLUGIN_ID_CAN_HELPER(155); + // if (Settings.TaskDeviceEnabled[x] && (Settings.getPluginID_for_task(x) == PLUGIN_ID_CAN_HELPER)) + // { + // EventStruct tmp; + // String dummy; + // unsigned int val = 0; + + // tmp.Par1 = 0; + // tmp.idx = 2; + // tmp.TaskIndex = x; + // PluginCall(PLUGIN_READ, &tmp, dummy); + // } + // } + + // break; + // } + + // case CPlugin::Function::CPLUGIN_FLUSH: + // { + // delay(0); + // break; + // } + + default: + break; + } + return success; +} + +#endif // ifdef USES_C020 diff --git a/src/src/Commands/CAN.cpp b/src/src/Commands/CAN.cpp new file mode 100644 index 0000000000..92df7e2897 --- /dev/null +++ b/src/src/Commands/CAN.cpp @@ -0,0 +1,21 @@ +#include "CAN.h" +#include "../DataStructs/ESPEasy_EventStruct.h" +#include "../Helpers/_Plugin_Helper_CAN.h" + +const __FlashStringHelper* Command_CAN_SendToCAN(struct EventStruct *event, const char *line) +{ + if (canHelper_sendCmd(event)) { + return F("OK"); + } + + return F("ERROR"); +} + +const __FlashStringHelper* Command_CAN_SendCAN(struct EventStruct *event, const char * line) +{ + if (canHelper_sendData(event->Par2, event->Par1, static_cast(Sensor_VType::SENSOR_TYPE_SINGLE), event->Par3)) { + return F("OK"); + } + + return F("ERROR"); +} \ No newline at end of file diff --git a/src/src/Commands/CAN.h b/src/src/Commands/CAN.h new file mode 100644 index 0000000000..20b94befcb --- /dev/null +++ b/src/src/Commands/CAN.h @@ -0,0 +1,10 @@ +#ifndef COMMAND_CAN_H +#define COMMAND_CAN_H + +#include "../../ESPEasy_common.h" + +const __FlashStringHelper* Command_CAN_SendToCAN(struct EventStruct *event, const char * line); + +const __FlashStringHelper* Command_CAN_SendCAN(struct EventStruct *event, const char * line); + +#endif diff --git a/src/src/Commands/InternalCommands.cpp b/src/src/Commands/InternalCommands.cpp index a1ae0fa895..6a0930dd37 100644 --- a/src/src/Commands/InternalCommands.cpp +++ b/src/src/Commands/InternalCommands.cpp @@ -1,488 +1,494 @@ -#include "../Commands/InternalCommands.h" - -#include "../../ESPEasy_common.h" - -#include "../../_Plugin_Helper.h" -#include "../Globals/Settings.h" - -#if FEATURE_BLYNK -# include "../Commands/Blynk.h" -# include "../Commands/Blynk_c015.h" -#endif // if FEATURE_BLYNK - -#include "../Commands/Common.h" -#include "../Commands/Controller.h" -#include "../Commands/Diagnostic.h" -#include "../Commands/GPIO.h" -#include "../Commands/HTTP.h" -#include "../Commands/InternalCommands_decoder.h" -#include "../Commands/i2c.h" - -#if FEATURE_MQTT -# include "../Commands/MQTT.h" -#endif // if FEATURE_MQTT - -#include "../Commands/Networks.h" -#if FEATURE_NOTIFIER -# include "../Commands/Notifications.h" -#endif // if FEATURE_NOTIFIER -#if FEATURE_DALLAS_HELPER && FEATURE_COMMAND_OWSCAN -#include "../Commands/OneWire.h" -#endif // if FEATURE_DALLAS_HELPER && FEATURE_COMMAND_OWSCAN -#include "../Commands/Provisioning.h" -#include "../Commands/RTC.h" -#include "../Commands/Rules.h" -#include "../Commands/SDCARD.h" -#include "../Commands/Settings.h" -#if FEATURE_SERVO -# include "../Commands/Servo.h" -#endif // if FEATURE_SERVO -#include "../Commands/System.h" -#include "../Commands/Tasks.h" -#include "../Commands/Time.h" -#include "../Commands/Timer.h" -#include "../Commands/UPD.h" -#include "../Commands/wd.h" -#include "../Commands/WiFi.h" - -#include "../DataStructs/TimingStats.h" - -#include "../ESPEasyCore/ESPEasy_Log.h" - -#include "../Helpers/Misc.h" -#include "../Helpers/StringConverter.h" -#include "../Helpers/StringParser.h" - - -bool checkNrArguments(const char *cmd, const String& Line, int nrArguments) { - if (nrArguments < 0) { return true; } - - // 0 arguments means argument on pos1 is valid (the command) and argpos 2 should not be there. - if (HasArgv(Line.c_str(), nrArguments + 2)) { - #ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - String log; - - if (log.reserve(128)) { - log += F("Too many arguments: cmd="); - log += cmd; - - if (nrArguments < 1) { - log += Line; - } else { - // Check for one more argument than allowed, since we apparently have one. - bool done = false; - int i = 1; - - while (!done) { - String parameter; - - if (i == nrArguments) { - parameter = tolerantParseStringKeepCase(Line, i + 1); - } else { - parameter = parseStringKeepCase(Line, i + 1); - } - done = parameter.isEmpty(); - - if (!done) { - if (i <= nrArguments) { - if (Settings.TolerantLastArgParse() && (i == nrArguments)) { - log += F(" (fixed)"); - } - log += F(" Arg"); - } else { - log += F(" ExtraArg"); - } - log += i; - log += '='; - log += parameter; - } - ++i; - } - } - log += F(" lineLength="); - log += Line.length(); - addLogMove(LOG_LEVEL_ERROR, log); - } - addLogMove(LOG_LEVEL_ERROR, strformat(F("Line: _%s_"), Line.c_str())); - - addLogMove(LOG_LEVEL_ERROR, concat(Settings.TolerantLastArgParse() ? - F("Command executed, but may fail.") : F("Command not executed!"), - F(" See: https://github.com/letscontrolit/ESPEasy/issues/2724"))); - } - #endif // ifndef BUILD_NO_DEBUG - - if (Settings.TolerantLastArgParse()) { - return true; - } - return false; - } - return true; -} - -bool checkSourceFlags(EventValueSource::Enum source, EventValueSourceGroup::Enum group) { - if (EventValueSource::partOfGroup(source, group)) { - return true; - } - addLog(LOG_LEVEL_ERROR, return_incorrect_source()); - return false; -} - -command_case_data::command_case_data(const char *cmd, struct EventStruct *event, const char *line) : - cmd(cmd), event(event), line(line) -{ - cmd_lc = cmd; - cmd_lc.toLowerCase(); -} - -InternalCommands::InternalCommands(const char *cmd, struct EventStruct *event, const char *line) - : _data(cmd, event, line) {} - - -// Wrapper to reduce generated code by macro -bool InternalCommands::do_command_case_all(command_function_fs pFunc, - int nrArguments) -{ - return do_command_case(_data, pFunc, nrArguments, EventValueSourceGroup::Enum::ALL); -} - -bool InternalCommands::do_command_case_all(command_function pFunc, - int nrArguments) -{ - return do_command_case(_data, pFunc, nrArguments, EventValueSourceGroup::Enum::ALL); -} - -// Wrapper to reduce generated code by macro -bool InternalCommands::do_command_case_all_restricted(command_function_fs pFunc, - int nrArguments) -{ - return do_command_case(_data, pFunc, nrArguments, EventValueSourceGroup::Enum::RESTRICTED); -} - -bool InternalCommands::do_command_case_all_restricted(command_function pFunc, - int nrArguments) -{ - return do_command_case(_data, pFunc, nrArguments, EventValueSourceGroup::Enum::RESTRICTED); -} - -bool do_command_case_check(command_case_data & data, - int nrArguments, - EventValueSourceGroup::Enum group) -{ - // The data struct is re-used on each attempt to process an internal command. - // Re-initialize the only two members that may have been altered by a previous call. - data.retval = false; - data.status = String(); - - if (!checkSourceFlags(data.event->Source, group)) { - data.status = return_incorrect_source(); - return false; - } - - // FIXME TD-er: Do not check nr arguments from MQTT source. - // See https://github.com/letscontrolit/ESPEasy/issues/3344 - // C005 does recreate command partly from topic and published message - // e.g. ESP_Easy/Bathroom_pir_env/GPIO/14 with data 0 or 1 - // This only allows for 2 parameters, but some commands need more arguments (default to "0") - const bool mustCheckNrArguments = data.event->Source != EventValueSource::Enum::VALUE_SOURCE_MQTT; - - if (mustCheckNrArguments) { - if (!checkNrArguments(data.cmd, data.line, nrArguments)) { - data.status = return_incorrect_nr_arguments(); - - // data.retval = false; - return true; // Command is handled - } - } - data.retval = true; // Mark the command should be executed. - return true; // Command is handled -} - -bool InternalCommands::do_command_case(command_case_data & data, - command_function_fs pFunc, - int nrArguments, - EventValueSourceGroup::Enum group) -{ - if (do_command_case_check(data, nrArguments, group)) { - // It has been handled, check if we need to execute it. - // FIXME TD-er: Must change command function signature to use const String& - START_TIMER; - data.status = pFunc(data.event, data.line.c_str()); - STOP_TIMER(COMMAND_EXEC_INTERNAL); - return true; - } - return false; -} - -bool InternalCommands::do_command_case(command_case_data & data, - command_function pFunc, - int nrArguments, - EventValueSourceGroup::Enum group) -{ - if (do_command_case_check(data, nrArguments, group)) { - // It has been handled, check if we need to execute it. - // FIXME TD-er: Must change command function signature to use const String& - START_TIMER; - data.status = pFunc(data.event, data.line.c_str()); - STOP_TIMER(COMMAND_EXEC_INTERNAL); - return true; - } - return false; -} - -bool InternalCommands::executeInternalCommand() -{ - // Simple macro to match command to function call. - - // EventValueSourceGroup::Enum::ALL - #define COMMAND_CASE_A(C, NARGS) \ - do_command_case_all(&C, NARGS); break; - - // EventValueSourceGroup::Enum::RESTRICTED - #define COMMAND_CASE_R(C, NARGS) \ - do_command_case_all_restricted(&C, NARGS); break; - - - const ESPEasy_cmd_e cmd = match_ESPEasy_internal_command(_data.cmd_lc); - - _data.retval = false; - - if (cmd == ESPEasy_cmd_e::NotMatched) { - return false; - } - - // FIXME TD-er: Should we execute command when number of arguments is wrong? - - // FIXME TD-er: must determine nr arguments where NARGS is set to -1 - switch (cmd) { - case ESPEasy_cmd_e::accessinfo: COMMAND_CASE_A(Command_AccessInfo_Ls, 0); // Network Command - case ESPEasy_cmd_e::asyncevent: COMMAND_CASE_A(Command_Rules_Async_Events, -1); // Rule.h -#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - case ESPEasy_cmd_e::background: COMMAND_CASE_R(Command_Background, 1); // Diagnostic.h -#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS -#ifdef USES_C012 - case ESPEasy_cmd_e::blynkget: COMMAND_CASE_A(Command_Blynk_Get, -1); -#endif // ifdef USES_C012 -#ifdef USES_C015 - case ESPEasy_cmd_e::blynkset: COMMAND_CASE_R(Command_Blynk_Set, -1); -#endif // ifdef USES_C015 - case ESPEasy_cmd_e::build: COMMAND_CASE_A(Command_Settings_Build, 1); // Settings.h - case ESPEasy_cmd_e::clearaccessblock: COMMAND_CASE_R(Command_AccessInfo_Clear, 0); // Network Command - case ESPEasy_cmd_e::clearpassword: COMMAND_CASE_R(Command_Settings_Password_Clear, 1); // Settings.h - case ESPEasy_cmd_e::clearrtcram: COMMAND_CASE_R(Command_RTC_Clear, 0); // RTC.h -#ifdef ESP8266 - case ESPEasy_cmd_e::clearsdkwifi: COMMAND_CASE_R(Command_System_Erase_SDK_WiFiconfig, 0); // System.h - case ESPEasy_cmd_e::clearwifirfcal: COMMAND_CASE_R(Command_System_Erase_RFcal, 0); // System.h -#endif // ifdef ESP8266 - case ESPEasy_cmd_e::config: COMMAND_CASE_R(Command_Task_RemoteConfig, -1); // Tasks.h - case ESPEasy_cmd_e::controllerdisable: COMMAND_CASE_R(Command_Controller_Disable, 1); // Controller.h - case ESPEasy_cmd_e::controllerenable: COMMAND_CASE_R(Command_Controller_Enable, 1); // Controller.h - case ESPEasy_cmd_e::datetime: COMMAND_CASE_R(Command_DateTime, 2); // Time.h - case ESPEasy_cmd_e::debug: COMMAND_CASE_R(Command_Debug, 1); // Diagnostic.h - case ESPEasy_cmd_e::dec: COMMAND_CASE_A(Command_Rules_Dec, -1); // Rules.h - case ESPEasy_cmd_e::deepsleep: COMMAND_CASE_R(Command_System_deepSleep, 1); // System.h - case ESPEasy_cmd_e::delay: COMMAND_CASE_R(Command_Delay, 1); // Timers.h -#if FEATURE_PLUGIN_PRIORITY - case ESPEasy_cmd_e::disableprioritytask: COMMAND_CASE_R(Command_PriorityTask_Disable, 1); // Tasks.h -#endif // if FEATURE_PLUGIN_PRIORITY - case ESPEasy_cmd_e::dns: COMMAND_CASE_R(Command_DNS, 1); // Network Command - case ESPEasy_cmd_e::dst: COMMAND_CASE_R(Command_DST, 1); // Time.h -#if FEATURE_ETHERNET - case ESPEasy_cmd_e::ethphyadr: COMMAND_CASE_R(Command_ETH_Phy_Addr, 1); // Network Command - case ESPEasy_cmd_e::ethpinmdc: COMMAND_CASE_R(Command_ETH_Pin_mdc, 1); // Network Command - case ESPEasy_cmd_e::ethpinmdio: COMMAND_CASE_R(Command_ETH_Pin_mdio, 1); // Network Command - case ESPEasy_cmd_e::ethpinpower: COMMAND_CASE_R(Command_ETH_Pin_power, 1); // Network Command - case ESPEasy_cmd_e::ethphytype: COMMAND_CASE_R(Command_ETH_Phy_Type, 1); // Network Command - case ESPEasy_cmd_e::ethclockmode: COMMAND_CASE_R(Command_ETH_Clock_Mode, 1); // Network Command - case ESPEasy_cmd_e::ethip: COMMAND_CASE_R(Command_ETH_IP, 1); // Network Command - case ESPEasy_cmd_e::ethgateway: COMMAND_CASE_R(Command_ETH_Gateway, 1); // Network Command - case ESPEasy_cmd_e::ethsubnet: COMMAND_CASE_R(Command_ETH_Subnet, 1); // Network Command - case ESPEasy_cmd_e::ethdns: COMMAND_CASE_R(Command_ETH_DNS, 1); // Network Command - case ESPEasy_cmd_e::ethdisconnect: COMMAND_CASE_A(Command_ETH_Disconnect, 0); // Network Command - case ESPEasy_cmd_e::ethwifimode: COMMAND_CASE_R(Command_ETH_Wifi_Mode, 1); // Network Command -#endif // FEATURE_ETHERNET - case ESPEasy_cmd_e::erasesdkwifi: COMMAND_CASE_R(Command_WiFi_Erase, 0); // WiFi.h - case ESPEasy_cmd_e::event: COMMAND_CASE_A(Command_Rules_Events, -1); // Rule.h - case ESPEasy_cmd_e::executerules: COMMAND_CASE_A(Command_Rules_Execute, -1); // Rule.h - case ESPEasy_cmd_e::factoryreset: COMMAND_CASE_R(Command_Settings_FactoryReset, 0); // Settings.h - case ESPEasy_cmd_e::gateway: COMMAND_CASE_R(Command_Gateway, 1); // Network Command - case ESPEasy_cmd_e::gpio: COMMAND_CASE_A(Command_GPIO, 2); // Gpio.h - case ESPEasy_cmd_e::gpiotoggle: COMMAND_CASE_A(Command_GPIO_Toggle, 1); // Gpio.h - case ESPEasy_cmd_e::hiddenssid: COMMAND_CASE_R(Command_Wifi_HiddenSSID, 1); // wifi.h - case ESPEasy_cmd_e::i2cscanner: COMMAND_CASE_R(Command_i2c_Scanner, -1); // i2c.h - case ESPEasy_cmd_e::inc: COMMAND_CASE_A(Command_Rules_Inc, -1); // Rules.h - case ESPEasy_cmd_e::ip: COMMAND_CASE_R(Command_IP, 1); // Network Command -#if FEATURE_USE_IPV6 - case ESPEasy_cmd_e::ip6: COMMAND_CASE_A(Command_show_all_IP6, 0); // Network Command -#endif -#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - case ESPEasy_cmd_e::jsonportstatus: COMMAND_CASE_A(Command_JSONPortStatus, -1); // Diagnostic.h -#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - case ESPEasy_cmd_e::let: COMMAND_CASE_A(Command_Rules_Let, 2); // Rules.h - case ESPEasy_cmd_e::load: COMMAND_CASE_A(Command_Settings_Load, 0); // Settings.h - case ESPEasy_cmd_e::logentry: COMMAND_CASE_A(Command_logentry, -1); // Diagnostic.h - case ESPEasy_cmd_e::looptimerset: COMMAND_CASE_A(Command_Loop_Timer_Set, 3); // Timers.h - case ESPEasy_cmd_e::looptimerset_ms: COMMAND_CASE_A(Command_Loop_Timer_Set_ms, 3); // Timers.h - case ESPEasy_cmd_e::longpulse: COMMAND_CASE_A(Command_GPIO_LongPulse, 5); // GPIO.h - case ESPEasy_cmd_e::longpulse_ms: COMMAND_CASE_A(Command_GPIO_LongPulse_Ms, 5); // GPIO.h -#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - case ESPEasy_cmd_e::logportstatus: COMMAND_CASE_A(Command_logPortStatus, 0); // Diagnostic.h - case ESPEasy_cmd_e::lowmem: COMMAND_CASE_A(Command_Lowmem, 0); // Diagnostic.h -#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS -#ifdef USES_P009 - case ESPEasy_cmd_e::mcpgpio: COMMAND_CASE_A(Command_GPIO, 2); // Gpio.h - case ESPEasy_cmd_e::mcpgpiorange: COMMAND_CASE_A(Command_GPIO_McpGPIORange, -1); // Gpio.h - case ESPEasy_cmd_e::mcpgpiopattern: COMMAND_CASE_A(Command_GPIO_McpGPIOPattern, -1); // Gpio.h - case ESPEasy_cmd_e::mcpgpiotoggle: COMMAND_CASE_A(Command_GPIO_Toggle, 1); // Gpio.h - case ESPEasy_cmd_e::mcplongpulse: COMMAND_CASE_A(Command_GPIO_LongPulse, 3); // GPIO.h - case ESPEasy_cmd_e::mcplongpulse_ms: COMMAND_CASE_A(Command_GPIO_LongPulse_Ms, 3); // GPIO.h - case ESPEasy_cmd_e::mcpmode: COMMAND_CASE_A(Command_GPIO_Mode, 2); // Gpio.h - case ESPEasy_cmd_e::mcpmoderange: COMMAND_CASE_A(Command_GPIO_ModeRange, 3); // Gpio.h - case ESPEasy_cmd_e::mcppulse: COMMAND_CASE_A(Command_GPIO_Pulse, 3); // GPIO.h -#endif // ifdef USES_P009 - case ESPEasy_cmd_e::monitor: COMMAND_CASE_A(Command_GPIO_Monitor, 2); // GPIO.h - case ESPEasy_cmd_e::monitorrange: COMMAND_CASE_A(Command_GPIO_MonitorRange, 3); // GPIO.h -#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - case ESPEasy_cmd_e::malloc: COMMAND_CASE_A(Command_Malloc, 1); // Diagnostic.h - case ESPEasy_cmd_e::meminfo: COMMAND_CASE_A(Command_MemInfo, 0); // Diagnostic.h - case ESPEasy_cmd_e::meminfodetail: COMMAND_CASE_A(Command_MemInfo_detail, 0); // Diagnostic.h -#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - case ESPEasy_cmd_e::name: COMMAND_CASE_R(Command_Settings_Name, 1); // Settings.h - case ESPEasy_cmd_e::nosleep: COMMAND_CASE_R(Command_System_NoSleep, 1); // System.h -#if FEATURE_NOTIFIER - case ESPEasy_cmd_e::notify: COMMAND_CASE_R(Command_Notifications_Notify, -1); // Notifications.h -#endif // if FEATURE_NOTIFIER - case ESPEasy_cmd_e::ntphost: COMMAND_CASE_R(Command_NTPHost, 1); // Time.h -#if FEATURE_DALLAS_HELPER && FEATURE_COMMAND_OWSCAN - case ESPEasy_cmd_e::owscan: COMMAND_CASE_R(Command_OneWire_Owscan, -1); // OneWire.h -#endif // if FEATURE_DALLAS_HELPER && FEATURE_COMMAND_OWSCAN -#ifdef USES_P019 - case ESPEasy_cmd_e::pcfgpio: COMMAND_CASE_A(Command_GPIO, 2); // Gpio.h - case ESPEasy_cmd_e::pcfgpiorange: COMMAND_CASE_A(Command_GPIO_PcfGPIORange, -1); // Gpio.h - case ESPEasy_cmd_e::pcfgpiopattern: COMMAND_CASE_A(Command_GPIO_PcfGPIOPattern, -1); // Gpio.h - case ESPEasy_cmd_e::pcfgpiotoggle: COMMAND_CASE_A(Command_GPIO_Toggle, 1); // Gpio.h - case ESPEasy_cmd_e::pcflongpulse: COMMAND_CASE_A(Command_GPIO_LongPulse, 3); // GPIO.h - case ESPEasy_cmd_e::pcflongpulse_ms: COMMAND_CASE_A(Command_GPIO_LongPulse_Ms, 3); // GPIO.h - case ESPEasy_cmd_e::pcfmode: COMMAND_CASE_A(Command_GPIO_Mode, 2); // Gpio.h - case ESPEasy_cmd_e::pcfmoderange: COMMAND_CASE_A(Command_GPIO_ModeRange, 3); // Gpio.h ************ - case ESPEasy_cmd_e::pcfpulse: COMMAND_CASE_A(Command_GPIO_Pulse, 3); // GPIO.h -#endif // ifdef USES_P019 - case ESPEasy_cmd_e::password: COMMAND_CASE_R(Command_Settings_Password, 1); // Settings.h -#if FEATURE_POST_TO_HTTP - case ESPEasy_cmd_e::posttohttp: COMMAND_CASE_A(Command_HTTP_PostToHTTP, -1); // HTTP.h -#endif // if FEATURE_POST_TO_HTTP -#if FEATURE_CUSTOM_PROVISIONING - case ESPEasy_cmd_e::provision: COMMAND_CASE_A(Command_Provisioning_Dispatcher, -1); // Provisioning.h -# ifdef PLUGIN_BUILD_MAX_ESP32 - - // FIXME DEPRECATED: Fallback for temporary backward compatibility - case ESPEasy_cmd_e::provisionconfig: COMMAND_CASE_A(Command_Provisioning_ConfigFallback, 0); // Provisioning.h - case ESPEasy_cmd_e::provisionsecurity: COMMAND_CASE_A(Command_Provisioning_SecurityFallback, 0); // Provisioning.h -# if FEATURE_NOTIFIER - case ESPEasy_cmd_e::provisionnotification: COMMAND_CASE_A(Command_Provisioning_NotificationFallback, 0); // Provisioning.h -# endif // if FEATURE_NOTIFIER - case ESPEasy_cmd_e::provisionprovision: COMMAND_CASE_A(Command_Provisioning_ProvisionFallback, 0); // Provisioning.h - case ESPEasy_cmd_e::provisionrules: COMMAND_CASE_A(Command_Provisioning_RulesFallback, 1); // Provisioning.h - case ESPEasy_cmd_e::provisionfirmware: COMMAND_CASE_A(Command_Provisioning_FirmwareFallback, 1); // Provisioning.h -# endif // ifdef PLUGIN_BUILD_MAX_ESP32 -#endif // if FEATURE_CUSTOM_PROVISIONING - case ESPEasy_cmd_e::pulse: COMMAND_CASE_A(Command_GPIO_Pulse, 3); // GPIO.h -#if FEATURE_MQTT - case ESPEasy_cmd_e::publish: COMMAND_CASE_A(Command_MQTT_Publish, -1); // MQTT.h - case ESPEasy_cmd_e::publishr: COMMAND_CASE_A(Command_MQTT_PublishR, -1); // MQTT.h -#endif // if FEATURE_MQTT -#if FEATURE_PUT_TO_HTTP - case ESPEasy_cmd_e::puttohttp: COMMAND_CASE_A(Command_HTTP_PutToHTTP, -1); // HTTP.h -#endif // if FEATURE_PUT_TO_HTTP - case ESPEasy_cmd_e::pwm: COMMAND_CASE_A(Command_GPIO_PWM, 4); // GPIO.h - case ESPEasy_cmd_e::reboot: COMMAND_CASE_A(Command_System_Reboot, 0); // System.h - case ESPEasy_cmd_e::resetflashwritecounter: COMMAND_CASE_A(Command_RTC_resetFlashWriteCounter, 0); // RTC.h - case ESPEasy_cmd_e::restart: COMMAND_CASE_A(Command_System_Reboot, 0); // System.h - case ESPEasy_cmd_e::rtttl: COMMAND_CASE_A(Command_GPIO_RTTTL, -1); // GPIO.h - case ESPEasy_cmd_e::rules: COMMAND_CASE_A(Command_Rules_UseRules, 1); // Rule.h - case ESPEasy_cmd_e::save: COMMAND_CASE_R(Command_Settings_Save, 0); // Settings.h - case ESPEasy_cmd_e::scheduletaskrun: COMMAND_CASE_A(Command_ScheduleTask_Run, 2); // Tasks.h - -#if FEATURE_SD - case ESPEasy_cmd_e::sdcard: COMMAND_CASE_R(Command_SD_LS, 0); // SDCARDS.h - case ESPEasy_cmd_e::sdremove: COMMAND_CASE_R(Command_SD_Remove, 1); // SDCARDS.h -#endif // if FEATURE_SD - -#if FEATURE_ESPEASY_P2P - - // FIXME TD-er: These send commands, can we determine the nr of arguments? - case ESPEasy_cmd_e::sendto: COMMAND_CASE_A(Command_UPD_SendTo, 2); // UDP.h -#endif // if FEATURE_ESPEASY_P2P -#if FEATURE_SEND_TO_HTTP - case ESPEasy_cmd_e::sendtohttp: COMMAND_CASE_A(Command_HTTP_SendToHTTP, 3); // HTTP.h -#endif // FEATURE_SEND_TO_HTTP - case ESPEasy_cmd_e::sendtoudp: COMMAND_CASE_A(Command_UDP_SendToUPD, 3); // UDP.h -#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - case ESPEasy_cmd_e::serialfloat: COMMAND_CASE_R(Command_SerialFloat, 0); // Diagnostic.h -#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - case ESPEasy_cmd_e::settings: COMMAND_CASE_R(Command_Settings_Print, 0); // Settings.h -#if FEATURE_SERVO - case ESPEasy_cmd_e::servo: COMMAND_CASE_A(Command_Servo, 3); // Servo.h -#endif // if FEATURE_SERVO - - case ESPEasy_cmd_e::status: COMMAND_CASE_A(Command_GPIO_Status, 2); // GPIO.h - case ESPEasy_cmd_e::subnet: COMMAND_CASE_R(Command_Subnet, 1); // Network Command -#if FEATURE_MQTT - case ESPEasy_cmd_e::subscribe: COMMAND_CASE_A(Command_MQTT_Subscribe, 1); // MQTT.h -#endif // if FEATURE_MQTT -#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - case ESPEasy_cmd_e::sysload: COMMAND_CASE_A(Command_SysLoad, 0); // Diagnostic.h -#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - case ESPEasy_cmd_e::taskclear: COMMAND_CASE_R(Command_Task_Clear, 1); // Tasks.h - case ESPEasy_cmd_e::taskclearall: COMMAND_CASE_R(Command_Task_ClearAll, 0); // Tasks.h - case ESPEasy_cmd_e::taskdisable: COMMAND_CASE_R(Command_Task_Disable, 1); // Tasks.h - case ESPEasy_cmd_e::taskenable: COMMAND_CASE_R(Command_Task_Enable, 1); // Tasks.h - case ESPEasy_cmd_e::taskrun: COMMAND_CASE_A(Command_Task_Run, 1); // Tasks.h - case ESPEasy_cmd_e::taskrunat: COMMAND_CASE_A(Command_Task_Run, 2); // Tasks.h - case ESPEasy_cmd_e::taskvalueset: COMMAND_CASE_A(Command_Task_ValueSet, 3); // Tasks.h - case ESPEasy_cmd_e::taskvaluetoggle: COMMAND_CASE_A(Command_Task_ValueToggle, 2); // Tasks.h - case ESPEasy_cmd_e::taskvaluesetandrun: COMMAND_CASE_A(Command_Task_ValueSetAndRun, 3); // Tasks.h - case ESPEasy_cmd_e::timerpause: COMMAND_CASE_A(Command_Timer_Pause, 1); // Timers.h - case ESPEasy_cmd_e::timerresume: COMMAND_CASE_A(Command_Timer_Resume, 1); // Timers.h - case ESPEasy_cmd_e::timerset: COMMAND_CASE_A(Command_Timer_Set, 2); // Timers.h - case ESPEasy_cmd_e::timerset_ms: COMMAND_CASE_A(Command_Timer_Set_ms, 2); // Timers.h - case ESPEasy_cmd_e::timezone: COMMAND_CASE_R(Command_TimeZone, 1); // Time.h - case ESPEasy_cmd_e::tone: COMMAND_CASE_A(Command_GPIO_Tone, 3); // GPIO.h - case ESPEasy_cmd_e::udpport: COMMAND_CASE_R(Command_UDP_Port, 1); // UDP.h -#if FEATURE_ESPEASY_P2P - case ESPEasy_cmd_e::udptest: COMMAND_CASE_R(Command_UDP_Test, 2); // UDP.h -#endif // if FEATURE_ESPEASY_P2P - case ESPEasy_cmd_e::unit: COMMAND_CASE_R(Command_Settings_Unit, 1); // Settings.h - case ESPEasy_cmd_e::unmonitor: COMMAND_CASE_A(Command_GPIO_UnMonitor, 2); // GPIO.h - case ESPEasy_cmd_e::unmonitorrange: COMMAND_CASE_A(Command_GPIO_UnMonitorRange, 3); // GPIO.h - case ESPEasy_cmd_e::usentp: COMMAND_CASE_R(Command_useNTP, 1); // Time.h -#ifndef LIMIT_BUILD_SIZE - case ESPEasy_cmd_e::wdconfig: COMMAND_CASE_R(Command_WD_Config, 3); // WD.h - case ESPEasy_cmd_e::wdread: COMMAND_CASE_R(Command_WD_Read, 2); // WD.h -#endif // ifndef LIMIT_BUILD_SIZE - - case ESPEasy_cmd_e::wifiallowap: COMMAND_CASE_R(Command_Wifi_AllowAP, 0); // WiFi.h - case ESPEasy_cmd_e::wifiapmode: COMMAND_CASE_R(Command_Wifi_APMode, 0); // WiFi.h - case ESPEasy_cmd_e::wificonnect: COMMAND_CASE_A(Command_Wifi_Connect, 0); // WiFi.h - case ESPEasy_cmd_e::wifidisconnect: COMMAND_CASE_A(Command_Wifi_Disconnect, 0); // WiFi.h - case ESPEasy_cmd_e::wifikey: COMMAND_CASE_R(Command_Wifi_Key, 1); // WiFi.h - case ESPEasy_cmd_e::wifikey2: COMMAND_CASE_R(Command_Wifi_Key2, 1); // WiFi.h - case ESPEasy_cmd_e::wifimode: COMMAND_CASE_R(Command_Wifi_Mode, 1); // WiFi.h - case ESPEasy_cmd_e::wifiscan: COMMAND_CASE_R(Command_Wifi_Scan, 0); // WiFi.h - 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 - - - case ESPEasy_cmd_e::NotMatched: - return false; - - // Do not add default: here - // The compiler will then warn when a command is not included - } - - #undef COMMAND_CASE_R - #undef COMMAND_CASE_A - return _data.retval; -} +#include "../Commands/InternalCommands.h" + +#include "../../ESPEasy_common.h" + +#include "../../_Plugin_Helper.h" +#include "../Globals/Settings.h" + +#if FEATURE_BLYNK +# include "../Commands/Blynk.h" +# include "../Commands/Blynk_c015.h" +#endif // if FEATURE_BLYNK + +#include "../Commands/Common.h" +#include "../Commands/Controller.h" +#include "../Commands/Diagnostic.h" +#include "../Commands/GPIO.h" +#include "../Commands/HTTP.h" +#include "../Commands/InternalCommands_decoder.h" +#include "../Commands/i2c.h" +#ifdef FEATURE_CAN +#include "../Commands/CAN.h" +#endif + +#if FEATURE_MQTT +# include "../Commands/MQTT.h" +#endif // if FEATURE_MQTT + +#include "../Commands/Networks.h" +#if FEATURE_NOTIFIER +# include "../Commands/Notifications.h" +#endif // if FEATURE_NOTIFIER +#if FEATURE_DALLAS_HELPER && FEATURE_COMMAND_OWSCAN +#include "../Commands/OneWire.h" +#endif // if FEATURE_DALLAS_HELPER && FEATURE_COMMAND_OWSCAN +#include "../Commands/Provisioning.h" +#include "../Commands/RTC.h" +#include "../Commands/Rules.h" +#include "../Commands/SDCARD.h" +#include "../Commands/Settings.h" +#if FEATURE_SERVO +# include "../Commands/Servo.h" +#endif // if FEATURE_SERVO +#include "../Commands/System.h" +#include "../Commands/Tasks.h" +#include "../Commands/Time.h" +#include "../Commands/Timer.h" +#include "../Commands/UPD.h" +#include "../Commands/wd.h" +#include "../Commands/WiFi.h" + +#include "../DataStructs/TimingStats.h" + +#include "../ESPEasyCore/ESPEasy_Log.h" + +#include "../Helpers/Misc.h" +#include "../Helpers/StringConverter.h" +#include "../Helpers/StringParser.h" + + +bool checkNrArguments(const char *cmd, const String& Line, int nrArguments) { + if (nrArguments < 0) { return true; } + + // 0 arguments means argument on pos1 is valid (the command) and argpos 2 should not be there. + if (HasArgv(Line.c_str(), nrArguments + 2)) { + #ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + String log; + + if (log.reserve(128)) { + log += F("Too many arguments: cmd="); + log += cmd; + + if (nrArguments < 1) { + log += Line; + } else { + // Check for one more argument than allowed, since we apparently have one. + bool done = false; + int i = 1; + + while (!done) { + String parameter; + + if (i == nrArguments) { + parameter = tolerantParseStringKeepCase(Line, i + 1); + } else { + parameter = parseStringKeepCase(Line, i + 1); + } + done = parameter.isEmpty(); + + if (!done) { + if (i <= nrArguments) { + if (Settings.TolerantLastArgParse() && (i == nrArguments)) { + log += F(" (fixed)"); + } + log += F(" Arg"); + } else { + log += F(" ExtraArg"); + } + log += i; + log += '='; + log += parameter; + } + ++i; + } + } + log += F(" lineLength="); + log += Line.length(); + addLogMove(LOG_LEVEL_ERROR, log); + } + addLogMove(LOG_LEVEL_ERROR, strformat(F("Line: _%s_"), Line.c_str())); + + addLogMove(LOG_LEVEL_ERROR, concat(Settings.TolerantLastArgParse() ? + F("Command executed, but may fail.") : F("Command not executed!"), + F(" See: https://github.com/letscontrolit/ESPEasy/issues/2724"))); + } + #endif // ifndef BUILD_NO_DEBUG + + if (Settings.TolerantLastArgParse()) { + return true; + } + return false; + } + return true; +} + +bool checkSourceFlags(EventValueSource::Enum source, EventValueSourceGroup::Enum group) { + if (EventValueSource::partOfGroup(source, group)) { + return true; + } + addLog(LOG_LEVEL_ERROR, return_incorrect_source()); + return false; +} + +command_case_data::command_case_data(const char *cmd, struct EventStruct *event, const char *line) : + cmd(cmd), event(event), line(line) +{ + cmd_lc = cmd; + cmd_lc.toLowerCase(); +} + +InternalCommands::InternalCommands(const char *cmd, struct EventStruct *event, const char *line) + : _data(cmd, event, line) {} + + +// Wrapper to reduce generated code by macro +bool InternalCommands::do_command_case_all(command_function_fs pFunc, + int nrArguments) +{ + return do_command_case(_data, pFunc, nrArguments, EventValueSourceGroup::Enum::ALL); +} + +bool InternalCommands::do_command_case_all(command_function pFunc, + int nrArguments) +{ + return do_command_case(_data, pFunc, nrArguments, EventValueSourceGroup::Enum::ALL); +} + +// Wrapper to reduce generated code by macro +bool InternalCommands::do_command_case_all_restricted(command_function_fs pFunc, + int nrArguments) +{ + return do_command_case(_data, pFunc, nrArguments, EventValueSourceGroup::Enum::RESTRICTED); +} + +bool InternalCommands::do_command_case_all_restricted(command_function pFunc, + int nrArguments) +{ + return do_command_case(_data, pFunc, nrArguments, EventValueSourceGroup::Enum::RESTRICTED); +} + +bool do_command_case_check(command_case_data & data, + int nrArguments, + EventValueSourceGroup::Enum group) +{ + // The data struct is re-used on each attempt to process an internal command. + // Re-initialize the only two members that may have been altered by a previous call. + data.retval = false; + data.status = String(); + + if (!checkSourceFlags(data.event->Source, group)) { + data.status = return_incorrect_source(); + return false; + } + + // FIXME TD-er: Do not check nr arguments from MQTT source. + // See https://github.com/letscontrolit/ESPEasy/issues/3344 + // C005 does recreate command partly from topic and published message + // e.g. ESP_Easy/Bathroom_pir_env/GPIO/14 with data 0 or 1 + // This only allows for 2 parameters, but some commands need more arguments (default to "0") + const bool mustCheckNrArguments = data.event->Source != EventValueSource::Enum::VALUE_SOURCE_MQTT; + + if (mustCheckNrArguments) { + if (!checkNrArguments(data.cmd, data.line, nrArguments)) { + data.status = return_incorrect_nr_arguments(); + + // data.retval = false; + return true; // Command is handled + } + } + data.retval = true; // Mark the command should be executed. + return true; // Command is handled +} + +bool InternalCommands::do_command_case(command_case_data & data, + command_function_fs pFunc, + int nrArguments, + EventValueSourceGroup::Enum group) +{ + if (do_command_case_check(data, nrArguments, group)) { + // It has been handled, check if we need to execute it. + // FIXME TD-er: Must change command function signature to use const String& + START_TIMER; + data.status = pFunc(data.event, data.line.c_str()); + STOP_TIMER(COMMAND_EXEC_INTERNAL); + return true; + } + return false; +} + +bool InternalCommands::do_command_case(command_case_data & data, + command_function pFunc, + int nrArguments, + EventValueSourceGroup::Enum group) +{ + if (do_command_case_check(data, nrArguments, group)) { + // It has been handled, check if we need to execute it. + // FIXME TD-er: Must change command function signature to use const String& + START_TIMER; + data.status = pFunc(data.event, data.line.c_str()); + STOP_TIMER(COMMAND_EXEC_INTERNAL); + return true; + } + return false; +} + +bool InternalCommands::executeInternalCommand() +{ + // Simple macro to match command to function call. + + // EventValueSourceGroup::Enum::ALL + #define COMMAND_CASE_A(C, NARGS) \ + do_command_case_all(&C, NARGS); break; + + // EventValueSourceGroup::Enum::RESTRICTED + #define COMMAND_CASE_R(C, NARGS) \ + do_command_case_all_restricted(&C, NARGS); break; + + + const ESPEasy_cmd_e cmd = match_ESPEasy_internal_command(_data.cmd_lc); + + _data.retval = false; + + if (cmd == ESPEasy_cmd_e::NotMatched) { + return false; + } + + // FIXME TD-er: Should we execute command when number of arguments is wrong? + + // FIXME TD-er: must determine nr arguments where NARGS is set to -1 + switch (cmd) { + case ESPEasy_cmd_e::accessinfo: COMMAND_CASE_A(Command_AccessInfo_Ls, 0); // Network Command + case ESPEasy_cmd_e::asyncevent: COMMAND_CASE_A(Command_Rules_Async_Events, -1); // Rule.h +#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS + case ESPEasy_cmd_e::background: COMMAND_CASE_R(Command_Background, 1); // Diagnostic.h +#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS +#ifdef USES_C012 + case ESPEasy_cmd_e::blynkget: COMMAND_CASE_A(Command_Blynk_Get, -1); +#endif // ifdef USES_C012 +#ifdef USES_C015 + case ESPEasy_cmd_e::blynkset: COMMAND_CASE_R(Command_Blynk_Set, -1); +#endif // ifdef USES_C015 + case ESPEasy_cmd_e::build: COMMAND_CASE_A(Command_Settings_Build, 1); // Settings.h + case ESPEasy_cmd_e::clearaccessblock: COMMAND_CASE_R(Command_AccessInfo_Clear, 0); // Network Command + case ESPEasy_cmd_e::clearpassword: COMMAND_CASE_R(Command_Settings_Password_Clear, 1); // Settings.h + case ESPEasy_cmd_e::clearrtcram: COMMAND_CASE_R(Command_RTC_Clear, 0); // RTC.h +#ifdef ESP8266 + case ESPEasy_cmd_e::clearsdkwifi: COMMAND_CASE_R(Command_System_Erase_SDK_WiFiconfig, 0); // System.h + case ESPEasy_cmd_e::clearwifirfcal: COMMAND_CASE_R(Command_System_Erase_RFcal, 0); // System.h +#endif // ifdef ESP8266 + case ESPEasy_cmd_e::config: COMMAND_CASE_R(Command_Task_RemoteConfig, -1); // Tasks.h + case ESPEasy_cmd_e::controllerdisable: COMMAND_CASE_R(Command_Controller_Disable, 1); // Controller.h + case ESPEasy_cmd_e::controllerenable: COMMAND_CASE_R(Command_Controller_Enable, 1); // Controller.h + case ESPEasy_cmd_e::datetime: COMMAND_CASE_R(Command_DateTime, 2); // Time.h + case ESPEasy_cmd_e::debug: COMMAND_CASE_R(Command_Debug, 1); // Diagnostic.h + case ESPEasy_cmd_e::dec: COMMAND_CASE_A(Command_Rules_Dec, -1); // Rules.h + case ESPEasy_cmd_e::deepsleep: COMMAND_CASE_R(Command_System_deepSleep, 1); // System.h + case ESPEasy_cmd_e::delay: COMMAND_CASE_R(Command_Delay, 1); // Timers.h +#if FEATURE_PLUGIN_PRIORITY + case ESPEasy_cmd_e::disableprioritytask: COMMAND_CASE_R(Command_PriorityTask_Disable, 1); // Tasks.h +#endif // if FEATURE_PLUGIN_PRIORITY + case ESPEasy_cmd_e::dns: COMMAND_CASE_R(Command_DNS, 1); // Network Command + case ESPEasy_cmd_e::dst: COMMAND_CASE_R(Command_DST, 1); // Time.h +#if FEATURE_ETHERNET + case ESPEasy_cmd_e::ethphyadr: COMMAND_CASE_R(Command_ETH_Phy_Addr, 1); // Network Command + case ESPEasy_cmd_e::ethpinmdc: COMMAND_CASE_R(Command_ETH_Pin_mdc, 1); // Network Command + case ESPEasy_cmd_e::ethpinmdio: COMMAND_CASE_R(Command_ETH_Pin_mdio, 1); // Network Command + case ESPEasy_cmd_e::ethpinpower: COMMAND_CASE_R(Command_ETH_Pin_power, 1); // Network Command + case ESPEasy_cmd_e::ethphytype: COMMAND_CASE_R(Command_ETH_Phy_Type, 1); // Network Command + case ESPEasy_cmd_e::ethclockmode: COMMAND_CASE_R(Command_ETH_Clock_Mode, 1); // Network Command + case ESPEasy_cmd_e::ethip: COMMAND_CASE_R(Command_ETH_IP, 1); // Network Command + case ESPEasy_cmd_e::ethgateway: COMMAND_CASE_R(Command_ETH_Gateway, 1); // Network Command + case ESPEasy_cmd_e::ethsubnet: COMMAND_CASE_R(Command_ETH_Subnet, 1); // Network Command + case ESPEasy_cmd_e::ethdns: COMMAND_CASE_R(Command_ETH_DNS, 1); // Network Command + case ESPEasy_cmd_e::ethdisconnect: COMMAND_CASE_A(Command_ETH_Disconnect, 0); // Network Command + case ESPEasy_cmd_e::ethwifimode: COMMAND_CASE_R(Command_ETH_Wifi_Mode, 1); // Network Command +#endif // FEATURE_ETHERNET + case ESPEasy_cmd_e::erasesdkwifi: COMMAND_CASE_R(Command_WiFi_Erase, 0); // WiFi.h + case ESPEasy_cmd_e::event: COMMAND_CASE_A(Command_Rules_Events, -1); // Rule.h + case ESPEasy_cmd_e::executerules: COMMAND_CASE_A(Command_Rules_Execute, -1); // Rule.h + case ESPEasy_cmd_e::factoryreset: COMMAND_CASE_R(Command_Settings_FactoryReset, 0); // Settings.h + case ESPEasy_cmd_e::gateway: COMMAND_CASE_R(Command_Gateway, 1); // Network Command + case ESPEasy_cmd_e::gpio: COMMAND_CASE_A(Command_GPIO, 2); // Gpio.h + case ESPEasy_cmd_e::gpiotoggle: COMMAND_CASE_A(Command_GPIO_Toggle, 1); // Gpio.h + case ESPEasy_cmd_e::hiddenssid: COMMAND_CASE_R(Command_Wifi_HiddenSSID, 1); // wifi.h + case ESPEasy_cmd_e::i2cscanner: COMMAND_CASE_R(Command_i2c_Scanner, -1); // i2c.h + case ESPEasy_cmd_e::inc: COMMAND_CASE_A(Command_Rules_Inc, -1); // Rules.h + case ESPEasy_cmd_e::ip: COMMAND_CASE_R(Command_IP, 1); // Network Command +#if FEATURE_USE_IPV6 + case ESPEasy_cmd_e::ip6: COMMAND_CASE_A(Command_show_all_IP6, 0); // Network Command +#endif +#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS + case ESPEasy_cmd_e::jsonportstatus: COMMAND_CASE_A(Command_JSONPortStatus, -1); // Diagnostic.h +#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS + case ESPEasy_cmd_e::let: COMMAND_CASE_A(Command_Rules_Let, 2); // Rules.h + case ESPEasy_cmd_e::load: COMMAND_CASE_A(Command_Settings_Load, 0); // Settings.h + case ESPEasy_cmd_e::logentry: COMMAND_CASE_A(Command_logentry, -1); // Diagnostic.h + case ESPEasy_cmd_e::looptimerset: COMMAND_CASE_A(Command_Loop_Timer_Set, 3); // Timers.h + case ESPEasy_cmd_e::looptimerset_ms: COMMAND_CASE_A(Command_Loop_Timer_Set_ms, 3); // Timers.h + case ESPEasy_cmd_e::longpulse: COMMAND_CASE_A(Command_GPIO_LongPulse, 5); // GPIO.h + case ESPEasy_cmd_e::longpulse_ms: COMMAND_CASE_A(Command_GPIO_LongPulse_Ms, 5); // GPIO.h +#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS + case ESPEasy_cmd_e::logportstatus: COMMAND_CASE_A(Command_logPortStatus, 0); // Diagnostic.h + case ESPEasy_cmd_e::lowmem: COMMAND_CASE_A(Command_Lowmem, 0); // Diagnostic.h +#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS +#ifdef USES_P009 + case ESPEasy_cmd_e::mcpgpio: COMMAND_CASE_A(Command_GPIO, 2); // Gpio.h + case ESPEasy_cmd_e::mcpgpiorange: COMMAND_CASE_A(Command_GPIO_McpGPIORange, -1); // Gpio.h + case ESPEasy_cmd_e::mcpgpiopattern: COMMAND_CASE_A(Command_GPIO_McpGPIOPattern, -1); // Gpio.h + case ESPEasy_cmd_e::mcpgpiotoggle: COMMAND_CASE_A(Command_GPIO_Toggle, 1); // Gpio.h + case ESPEasy_cmd_e::mcplongpulse: COMMAND_CASE_A(Command_GPIO_LongPulse, 3); // GPIO.h + case ESPEasy_cmd_e::mcplongpulse_ms: COMMAND_CASE_A(Command_GPIO_LongPulse_Ms, 3); // GPIO.h + case ESPEasy_cmd_e::mcpmode: COMMAND_CASE_A(Command_GPIO_Mode, 2); // Gpio.h + case ESPEasy_cmd_e::mcpmoderange: COMMAND_CASE_A(Command_GPIO_ModeRange, 3); // Gpio.h + case ESPEasy_cmd_e::mcppulse: COMMAND_CASE_A(Command_GPIO_Pulse, 3); // GPIO.h +#endif // ifdef USES_P009 + case ESPEasy_cmd_e::monitor: COMMAND_CASE_A(Command_GPIO_Monitor, 2); // GPIO.h + case ESPEasy_cmd_e::monitorrange: COMMAND_CASE_A(Command_GPIO_MonitorRange, 3); // GPIO.h +#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS + case ESPEasy_cmd_e::malloc: COMMAND_CASE_A(Command_Malloc, 1); // Diagnostic.h + case ESPEasy_cmd_e::meminfo: COMMAND_CASE_A(Command_MemInfo, 0); // Diagnostic.h + case ESPEasy_cmd_e::meminfodetail: COMMAND_CASE_A(Command_MemInfo_detail, 0); // Diagnostic.h +#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS + case ESPEasy_cmd_e::name: COMMAND_CASE_R(Command_Settings_Name, 1); // Settings.h + case ESPEasy_cmd_e::nosleep: COMMAND_CASE_R(Command_System_NoSleep, 1); // System.h +#if FEATURE_NOTIFIER + case ESPEasy_cmd_e::notify: COMMAND_CASE_R(Command_Notifications_Notify, -1); // Notifications.h +#endif // if FEATURE_NOTIFIER + case ESPEasy_cmd_e::ntphost: COMMAND_CASE_R(Command_NTPHost, 1); // Time.h +#if FEATURE_DALLAS_HELPER && FEATURE_COMMAND_OWSCAN + case ESPEasy_cmd_e::owscan: COMMAND_CASE_R(Command_OneWire_Owscan, -1); // OneWire.h +#endif // if FEATURE_DALLAS_HELPER && FEATURE_COMMAND_OWSCAN +#ifdef USES_P019 + case ESPEasy_cmd_e::pcfgpio: COMMAND_CASE_A(Command_GPIO, 2); // Gpio.h + case ESPEasy_cmd_e::pcfgpiorange: COMMAND_CASE_A(Command_GPIO_PcfGPIORange, -1); // Gpio.h + case ESPEasy_cmd_e::pcfgpiopattern: COMMAND_CASE_A(Command_GPIO_PcfGPIOPattern, -1); // Gpio.h + case ESPEasy_cmd_e::pcfgpiotoggle: COMMAND_CASE_A(Command_GPIO_Toggle, 1); // Gpio.h + case ESPEasy_cmd_e::pcflongpulse: COMMAND_CASE_A(Command_GPIO_LongPulse, 3); // GPIO.h + case ESPEasy_cmd_e::pcflongpulse_ms: COMMAND_CASE_A(Command_GPIO_LongPulse_Ms, 3); // GPIO.h + case ESPEasy_cmd_e::pcfmode: COMMAND_CASE_A(Command_GPIO_Mode, 2); // Gpio.h + case ESPEasy_cmd_e::pcfmoderange: COMMAND_CASE_A(Command_GPIO_ModeRange, 3); // Gpio.h ************ + case ESPEasy_cmd_e::pcfpulse: COMMAND_CASE_A(Command_GPIO_Pulse, 3); // GPIO.h +#endif // ifdef USES_P019 + case ESPEasy_cmd_e::password: COMMAND_CASE_R(Command_Settings_Password, 1); // Settings.h +#if FEATURE_POST_TO_HTTP + case ESPEasy_cmd_e::posttohttp: COMMAND_CASE_A(Command_HTTP_PostToHTTP, -1); // HTTP.h +#endif // if FEATURE_POST_TO_HTTP +#if FEATURE_CUSTOM_PROVISIONING + case ESPEasy_cmd_e::provision: COMMAND_CASE_A(Command_Provisioning_Dispatcher, -1); // Provisioning.h +# ifdef PLUGIN_BUILD_MAX_ESP32 + + // FIXME DEPRECATED: Fallback for temporary backward compatibility + case ESPEasy_cmd_e::provisionconfig: COMMAND_CASE_A(Command_Provisioning_ConfigFallback, 0); // Provisioning.h + case ESPEasy_cmd_e::provisionsecurity: COMMAND_CASE_A(Command_Provisioning_SecurityFallback, 0); // Provisioning.h +# if FEATURE_NOTIFIER + case ESPEasy_cmd_e::provisionnotification: COMMAND_CASE_A(Command_Provisioning_NotificationFallback, 0); // Provisioning.h +# endif // if FEATURE_NOTIFIER + case ESPEasy_cmd_e::provisionprovision: COMMAND_CASE_A(Command_Provisioning_ProvisionFallback, 0); // Provisioning.h + case ESPEasy_cmd_e::provisionrules: COMMAND_CASE_A(Command_Provisioning_RulesFallback, 1); // Provisioning.h + case ESPEasy_cmd_e::provisionfirmware: COMMAND_CASE_A(Command_Provisioning_FirmwareFallback, 1); // Provisioning.h +# endif // ifdef PLUGIN_BUILD_MAX_ESP32 +#endif // if FEATURE_CUSTOM_PROVISIONING + case ESPEasy_cmd_e::pulse: COMMAND_CASE_A(Command_GPIO_Pulse, 3); // GPIO.h +#if FEATURE_MQTT + case ESPEasy_cmd_e::publish: COMMAND_CASE_A(Command_MQTT_Publish, -1); // MQTT.h + case ESPEasy_cmd_e::publishr: COMMAND_CASE_A(Command_MQTT_PublishR, -1); // MQTT.h +#endif // if FEATURE_MQTT +#if FEATURE_PUT_TO_HTTP + case ESPEasy_cmd_e::puttohttp: COMMAND_CASE_A(Command_HTTP_PutToHTTP, -1); // HTTP.h +#endif // if FEATURE_PUT_TO_HTTP + case ESPEasy_cmd_e::pwm: COMMAND_CASE_A(Command_GPIO_PWM, 4); // GPIO.h + case ESPEasy_cmd_e::reboot: COMMAND_CASE_A(Command_System_Reboot, 0); // System.h + case ESPEasy_cmd_e::resetflashwritecounter: COMMAND_CASE_A(Command_RTC_resetFlashWriteCounter, 0); // RTC.h + case ESPEasy_cmd_e::restart: COMMAND_CASE_A(Command_System_Reboot, 0); // System.h + case ESPEasy_cmd_e::rtttl: COMMAND_CASE_A(Command_GPIO_RTTTL, -1); // GPIO.h + case ESPEasy_cmd_e::rules: COMMAND_CASE_A(Command_Rules_UseRules, 1); // Rule.h + case ESPEasy_cmd_e::save: COMMAND_CASE_R(Command_Settings_Save, 0); // Settings.h + case ESPEasy_cmd_e::scheduletaskrun: COMMAND_CASE_A(Command_ScheduleTask_Run, 2); // Tasks.h + +#if FEATURE_SD + case ESPEasy_cmd_e::sdcard: COMMAND_CASE_R(Command_SD_LS, 0); // SDCARDS.h + case ESPEasy_cmd_e::sdremove: COMMAND_CASE_R(Command_SD_Remove, 1); // SDCARDS.h +#endif // if FEATURE_SD + +#if FEATURE_ESPEASY_P2P + + // FIXME TD-er: These send commands, can we determine the nr of arguments? + case ESPEasy_cmd_e::sendto: COMMAND_CASE_A(Command_UPD_SendTo, 2); // UDP.h +#endif // if FEATURE_ESPEASY_P2P +#if FEATURE_SEND_TO_HTTP + case ESPEasy_cmd_e::sendtohttp: COMMAND_CASE_A(Command_HTTP_SendToHTTP, 3); // HTTP.h +#endif // FEATURE_SEND_TO_HTTP + case ESPEasy_cmd_e::sendtoudp: COMMAND_CASE_A(Command_UDP_SendToUPD, 3); // UDP.h +#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS + case ESPEasy_cmd_e::serialfloat: COMMAND_CASE_R(Command_SerialFloat, 0); // Diagnostic.h +#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS + case ESPEasy_cmd_e::settings: COMMAND_CASE_R(Command_Settings_Print, 0); // Settings.h +#if FEATURE_SERVO + case ESPEasy_cmd_e::servo: COMMAND_CASE_A(Command_Servo, 3); // Servo.h +#endif // if FEATURE_SERVO + + case ESPEasy_cmd_e::status: COMMAND_CASE_A(Command_GPIO_Status, 2); // GPIO.h + case ESPEasy_cmd_e::subnet: COMMAND_CASE_R(Command_Subnet, 1); // Network Command +#if FEATURE_MQTT + case ESPEasy_cmd_e::subscribe: COMMAND_CASE_A(Command_MQTT_Subscribe, 1); // MQTT.h +#endif // if FEATURE_MQTT +#ifndef BUILD_NO_DIAGNOSTIC_COMMANDS + case ESPEasy_cmd_e::sysload: COMMAND_CASE_A(Command_SysLoad, 0); // Diagnostic.h +#endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS + case ESPEasy_cmd_e::taskclear: COMMAND_CASE_R(Command_Task_Clear, 1); // Tasks.h + case ESPEasy_cmd_e::taskclearall: COMMAND_CASE_R(Command_Task_ClearAll, 0); // Tasks.h + case ESPEasy_cmd_e::taskdisable: COMMAND_CASE_R(Command_Task_Disable, 1); // Tasks.h + case ESPEasy_cmd_e::taskenable: COMMAND_CASE_R(Command_Task_Enable, 1); // Tasks.h + case ESPEasy_cmd_e::taskrun: COMMAND_CASE_A(Command_Task_Run, 1); // Tasks.h + case ESPEasy_cmd_e::taskrunat: COMMAND_CASE_A(Command_Task_Run, 2); // Tasks.h + case ESPEasy_cmd_e::taskvalueset: COMMAND_CASE_A(Command_Task_ValueSet, 3); // Tasks.h + case ESPEasy_cmd_e::taskvaluetoggle: COMMAND_CASE_A(Command_Task_ValueToggle, 2); // Tasks.h + case ESPEasy_cmd_e::taskvaluesetandrun: COMMAND_CASE_A(Command_Task_ValueSetAndRun, 3); // Tasks.h + case ESPEasy_cmd_e::timerpause: COMMAND_CASE_A(Command_Timer_Pause, 1); // Timers.h + case ESPEasy_cmd_e::timerresume: COMMAND_CASE_A(Command_Timer_Resume, 1); // Timers.h + case ESPEasy_cmd_e::timerset: COMMAND_CASE_A(Command_Timer_Set, 2); // Timers.h + case ESPEasy_cmd_e::timerset_ms: COMMAND_CASE_A(Command_Timer_Set_ms, 2); // Timers.h + case ESPEasy_cmd_e::timezone: COMMAND_CASE_R(Command_TimeZone, 1); // Time.h + case ESPEasy_cmd_e::tone: COMMAND_CASE_A(Command_GPIO_Tone, 3); // GPIO.h + case ESPEasy_cmd_e::udpport: COMMAND_CASE_R(Command_UDP_Port, 1); // UDP.h +#if FEATURE_ESPEASY_P2P + case ESPEasy_cmd_e::udptest: COMMAND_CASE_R(Command_UDP_Test, 2); // UDP.h +#endif // if FEATURE_ESPEASY_P2P + case ESPEasy_cmd_e::unit: COMMAND_CASE_R(Command_Settings_Unit, 1); // Settings.h + case ESPEasy_cmd_e::unmonitor: COMMAND_CASE_A(Command_GPIO_UnMonitor, 2); // GPIO.h + case ESPEasy_cmd_e::unmonitorrange: COMMAND_CASE_A(Command_GPIO_UnMonitorRange, 3); // GPIO.h + case ESPEasy_cmd_e::usentp: COMMAND_CASE_R(Command_useNTP, 1); // Time.h +#ifndef LIMIT_BUILD_SIZE + case ESPEasy_cmd_e::wdconfig: COMMAND_CASE_R(Command_WD_Config, 3); // WD.h + case ESPEasy_cmd_e::wdread: COMMAND_CASE_R(Command_WD_Read, 2); // WD.h +#endif // ifndef LIMIT_BUILD_SIZE + + case ESPEasy_cmd_e::wifiallowap: COMMAND_CASE_R(Command_Wifi_AllowAP, 0); // WiFi.h + case ESPEasy_cmd_e::wifiapmode: COMMAND_CASE_R(Command_Wifi_APMode, 0); // WiFi.h + case ESPEasy_cmd_e::wificonnect: COMMAND_CASE_A(Command_Wifi_Connect, 0); // WiFi.h + case ESPEasy_cmd_e::wifidisconnect: COMMAND_CASE_A(Command_Wifi_Disconnect, 0); // WiFi.h + case ESPEasy_cmd_e::wifikey: COMMAND_CASE_R(Command_Wifi_Key, 1); // WiFi.h + case ESPEasy_cmd_e::wifikey2: COMMAND_CASE_R(Command_Wifi_Key2, 1); // WiFi.h + case ESPEasy_cmd_e::wifimode: COMMAND_CASE_R(Command_Wifi_Mode, 1); // WiFi.h + case ESPEasy_cmd_e::wifiscan: COMMAND_CASE_R(Command_Wifi_Scan, 0); // WiFi.h + 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 + + case ESPEasy_cmd_e::sendcan: COMMAND_CASE_A(Command_CAN_SendCAN, -1); //CAN.h + case ESPEasy_cmd_e::sendtocan: COMMAND_CASE_A(Command_CAN_SendToCAN, -1); //CAN.h + + + case ESPEasy_cmd_e::NotMatched: + return false; + + // Do not add default: here + // The compiler will then warn when a command is not included + } + + #undef COMMAND_CASE_R + #undef COMMAND_CASE_A + return _data.retval; +} diff --git a/src/src/Commands/InternalCommands_decoder.cpp b/src/src/Commands/InternalCommands_decoder.cpp index 5bbb37c0e0..b26e82f584 100644 --- a/src/src/Commands/InternalCommands_decoder.cpp +++ b/src/src/Commands/InternalCommands_decoder.cpp @@ -276,6 +276,10 @@ const char Internal_commands_w[] PROGMEM = "wdconfig|" "wdread|" #endif // ifndef LIMIT_BUILD_SIZE +#ifdef FEUTER_CAN + "sendcan|" + "sendtocan|" +#endif ; const char* getInternalCommand_Haystack_Offset(const char firstLetter, int& offset) diff --git a/src/src/Commands/InternalCommands_decoder.h b/src/src/Commands/InternalCommands_decoder.h index 1887cc01e8..989b7792f6 100644 --- a/src/src/Commands/InternalCommands_decoder.h +++ b/src/src/Commands/InternalCommands_decoder.h @@ -232,6 +232,11 @@ enum class ESPEasy_cmd_e : uint8_t { wdread, #endif // ifndef LIMIT_BUILD_SIZE +#ifdef FEATURE_CAN + sendcan, + sendtocan, +#endif + NotMatched // Keep as last one }; diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 8af1f883f4..ae92eb6100 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1452,6 +1452,11 @@ To create/register a plugin, you have to : #define USES_C013 // ESPEasy P2P network #endif +#ifdef FEATURE_CAN + #define USES_C020 //CAN + #define USES_C021 + #define USES_P155 +#endif #ifdef NOTIFIER_SET_STABLE #define USES_N001 // Email diff --git a/src/src/Helpers/_Plugin_Helper_CAN.cpp b/src/src/Helpers/_Plugin_Helper_CAN.cpp new file mode 100644 index 0000000000..a5a7098f70 --- /dev/null +++ b/src/src/Helpers/_Plugin_Helper_CAN.cpp @@ -0,0 +1,209 @@ +#include "_Plugin_Helper_CAN.h" +#include "../Globals/Settings.h" +#include "../Globals/RuntimeData.h" +#include "../Commands/InternalCommands.h" +# include + +# define CAN_HELPER_MSG_DATA ((uint8_t)0) +# define CAN_HELPER_MSG_CMD_GPIO ((uint8_t)1) + +union canHelper_byte_field { + float float_var; + uint32_t uint_var; + byte byte_var[4]; +}; + +bool canHelper_sendData(const uint8_t taskIndex, const uint8_t valIndex, const uint8_t sensorType, const uint32_t val32) +{ + canHelper_byte_field val; + val.uint_var = val32; + + CAN.beginPacket(Settings.CAN_node_id & 0x7FF); + CAN.write(CAN_HELPER_MSG_DATA); + CAN.write(taskIndex); + CAN.write(valIndex); + CAN.write(sensorType); + CAN.write(val.byte_var[0]); + CAN.write(val.byte_var[1]); + CAN.write(val.byte_var[2]); + CAN.write(val.byte_var[3]); + int ret = CAN.endPacket(200); + + if (ret == CANControllerClass::SEND_OK) { + String log = "CAN : Send nd:" + String(uint8_t(Settings.CAN_node_id & 0x7FF)) + + " tId:" + String(uint8_t(taskIndex)+1) + + " vId:" + String(uint8_t(valIndex)) + + " (" + String(val.float_var, 2) + ")"; + addLogMove(LOG_LEVEL_DEBUG, log); + return true; + } else if (ret == CANControllerClass::SEND_ACK) { + addLog(LOG_LEVEL_ERROR, F("CAN : CAN ACK error")); + } else { + String log = F("CAN : send message error "); + log += ret; + log += String(sensorType); + addLogMove(LOG_LEVEL_ERROR, log); + } + + return false; +} + +static void canHelper_sendTaskValue(EventStruct *event, const uint8_t val_index, Sensor_VType sensorType) +{ + canHelper_byte_field u; + + if (isFloatOutputDataType(sensorType)) { + u.float_var = UserVar.getFloat(event->TaskIndex, val_index); + } else { + u.uint_var = UserVar.getUint32(event->TaskIndex, val_index); + } + + canHelper_sendData(event->TaskIndex, val_index, static_cast(event->getSensorType()), u.uint_var); +} + +static void canHelper_recvData() +{ + EventStruct tmp; + String dummy; + canHelper_byte_field u; + + tmp.Par2 = int(CAN.read()); // TaskIndex + tmp.idx = int(CAN.read()); // ValueIndex + tmp.sensorType = (Sensor_VType)CAN.read(); + u.byte_var[0] = CAN.read(); + u.byte_var[1] = CAN.read(); + u.byte_var[2] = CAN.read(); + u.byte_var[3] = CAN.read(); + + tmp.Par1 = CAN.packetId(); + + String log = "CAN: Recv nd:" + String(tmp.Par1) + + " tId:" + String(tmp.Par2 + 1) + + " vId:" + String(tmp.idx) + + " sTp:" + static_cast(tmp.sensorType); + if (isFloatOutputDataType(tmp.sensorType)) { + log += " (" + String(u.float_var, 2) + ")"; + } else { + log += " (" + String(u.uint_var) + ")"; + } + addLogMove(LOG_LEVEL_INFO, log); + + for (taskIndex_t x = 0; x < TASKS_MAX; x++) { + constexpr pluginID_t PLUGIN_ID_CAN_HELPER(155); + if (Settings.TaskDeviceEnabled[x] + && (Settings.getPluginID_for_task(x) == PLUGIN_ID_CAN_HELPER) + && (Settings.TaskDevicePluginConfig[x][0] == tmp.Par1) + && (Settings.TaskDevicePluginConfig[x][2] == tmp.Par2) + ) { + tmp.TaskIndex = x; + + if (isFloatOutputDataType(tmp.sensorType)) { + UserVar.setFloat(tmp.TaskIndex, tmp.idx, u.float_var); + } else { + UserVar.setUint32(tmp.TaskIndex, tmp.idx, u.uint_var); + } + + PluginCall(PLUGIN_READ, &tmp, dummy); + } + } +} + +static void canHelper_recvGPIO() +{ + if (CAN.available() != 3) { + addLog(LOG_LEVEL_INFO, "helperCAN: Received CAN gpio packet with wrong size"); + return; + } + + int node = CAN.read(); + + if (node == Settings.CAN_node_id) { + const uint8_t pin = CAN.read(); + const uint8_t value = CAN.read(); + + char cmd[32]; + sprintf(cmd, "GPIO,%u,%u", pin, value); + ExecuteCommand(0, EventValueSource::Enum::VALUE_SOURCE_SERIAL, cmd, false, true, false); + } +} + +void canHelper_recv() +{ + const int packetSize = CAN.parsePacket(); + if (packetSize == 0 && CAN.packetId() == -1) + { + return; + } + + uint8_t msg_type = CAN.read(); + + if (msg_type == CAN_HELPER_MSG_DATA) { + if (CAN.available() == 7) { + canHelper_recvData(); + } else { + addLog(LOG_LEVEL_ERROR, " CAN : Received CAN data packet with wrong size"); + } + } else if (msg_type == CAN_HELPER_MSG_CMD_GPIO) { + canHelper_recvGPIO(); + } +} + +void canHelper_sendTaskData(struct EventStruct *event) +{ + Sensor_VType sensorType = event->getSensorType(); + + if (isFloatOutputDataType(sensorType) + || isUInt32OutputDataType(sensorType) + || isInt32OutputDataType(sensorType) + ) { + const uint8_t valueCount = getValueCountFromSensorType(sensorType); + + for (uint8_t i = 0; i < valueCount; ++i) { + canHelper_sendTaskValue(event, i, sensorType); + } + } else { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + String log = F("canHelper: Not yet implemented sensor type: "); + log += static_cast(event->sensorType); + log += F(" idx: "); + log += event->idx; + addLogMove(LOG_LEVEL_ERROR, log); + } + } +} + +static bool canHelper_sendGPIO(struct EventStruct *event) +{ + CAN.beginPacket(Settings.CAN_node_id & 0x7FF); + CAN.write(CAN_HELPER_MSG_CMD_GPIO); + CAN.write(uint8_t(event->Par2)); + CAN.write(uint8_t(event->Par3)); + CAN.write(uint8_t(event->Par4)); + int ret = CAN.endPacket(200); + + if (ret == CANControllerClass::SEND_OK) { + // String log = "CAN : Send nd:" + String(uint8_t(Settings.CAN_node_id & 0x7FF)) + + // " tId:" + String(uint8_t(event->TaskIndex)+1) + + // " vId:" + String(uint8_t(val_index)) + + // " (" + String(u.float_var, 2) + ")"; + // addLogMove(LOG_LEVEL_INFO, log); + return true; + } else if (ret == CANControllerClass::SEND_ACK) { + addLog(LOG_LEVEL_ERROR, F("canHelper: CAN ACK error")); + } else { + String log = F("canHelper: send message error "); + log += ret; + log += String(int(event->getSensorType())); + addLogMove(LOG_LEVEL_ERROR, log); + } + return false; +} + +bool canHelper_sendCmd(EventStruct *event) +{ + if (event->Par1 == CAN_HELPER_MSG_CMD_GPIO) { + return canHelper_sendGPIO(event); + } + + return false; +} diff --git a/src/src/Helpers/_Plugin_Helper_CAN.h b/src/src/Helpers/_Plugin_Helper_CAN.h new file mode 100644 index 0000000000..4435077da8 --- /dev/null +++ b/src/src/Helpers/_Plugin_Helper_CAN.h @@ -0,0 +1,14 @@ +#ifndef HELPERS__PLUGIN_HELPER_CAN_H +#define HELPERS__PLUGIN_HELPER_CAN_H + +#include "../DataStructs/ESPEasy_EventStruct.h" + +void canHelper_sendTaskData(EventStruct *event); + +bool canHelper_sendData(const uint8_t taskIndex, const uint8_t valIndex, const uint8_t sensorType, const uint32_t val32); + +bool canHelper_sendCmd(EventStruct *event); + +void canHelper_recv(); + +#endif From 2d9e6ce38ebac9517df0b269b633b54676dd07c8 Mon Sep 17 00:00:00 2001 From: Bruno Castro Date: Tue, 10 Sep 2024 21:39:10 -0300 Subject: [PATCH 03/15] Plugin implementation --- src/_P155_CAN_helper.ino | 231 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 src/_P155_CAN_helper.ino diff --git a/src/_P155_CAN_helper.ino b/src/_P155_CAN_helper.ino new file mode 100644 index 0000000000..b79860304a --- /dev/null +++ b/src/_P155_CAN_helper.ino @@ -0,0 +1,231 @@ +#include "_Plugin_Helper.h" +#ifdef USES_P155 + +// ####################################################################################################### +// ################################### Plugin 155: CAN SJA1000 Importer ################################## +// ####################################################################################################### + + +# include "src/Helpers/_Plugin_Helper_serial.h" +# include + + +# define PLUGIN_155 +# define PLUGIN_ID_155 155 +# define PLUGIN_NAME_155 "Communication - CAN Importer" + +# define P155_MAX_NODES 255 +# define P155_TOPIC_MAX_SIZE 16 +# define P155_TIMEOUT 50 + +# define P155_VAL1_FLAG 0x01 +# define P155_VAL2_FLAG 0x02 +# define P155_VAL3_FLAG 0x04 +# define P155_VAL4_FLAG 0x10 + +#define PLUGIN_VALUENAME1_155 "Status" + +void P155_check_timeout(EventStruct *event, int idx) +{ + int16_t idx_flag = (0x01 << idx); + + if (PCONFIG_ULONG(idx) > PCONFIG(2)) + { + if (!(PCONFIG(3) & idx_flag)) + { + String log_debug = F("Disconnected"); + addLogMove(LOG_LEVEL_INFO, log_debug); + } + PCONFIG(3) |= idx_flag; + } + else if (!(PCONFIG(3) & idx_flag)) + { + PCONFIG_ULONG(idx) += 100; + } +} + +boolean Plugin_155(uint8_t function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + Device[++deviceCount].Number = PLUGIN_ID_155; + Device[deviceCount].Type = DEVICE_TYPE_DUMMY; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_NONE; + Device[deviceCount].Ports = 0; + Device[deviceCount].PullUpOption = false; + Device[deviceCount].InverseLogicOption = false; + Device[deviceCount].FormulaOption = false; + Device[deviceCount].ValueCount = 0; + Device[deviceCount].SendDataOption = true; + Device[deviceCount].TimerOption = false; + Device[deviceCount].GlobalSyncOption = false; + Device[deviceCount].TimerOptional = true; + Device[deviceCount].Custom = true; + Device[deviceCount].DecimalsOnly = true; + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_155); + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + const uint8_t valueCount = getValueCountFromSensorType(static_cast(PCONFIG(1))); + if (valueCount > 0) { + String lines[valueCount]; + LoadCustomTaskSettings(event->TaskIndex, lines, valueCount, 0); + + for (int i = 0; i < valueCount; ++i) { + if (lines[i].length() == 0) { + lines[i] = concat(F("val"), i + 1); + } + ExtraTaskSettings.setTaskDeviceValueName(i, lines[i]); + } + } + break; + } + + case PLUGIN_GET_DEVICEVALUECOUNT: + { + event->Par1 = getValueCountFromSensorType(static_cast(PCONFIG(1))); + success = true; + break; + } + + case PLUGIN_GET_DEVICEVTYPE: + { + event->sensorType = static_cast(PCONFIG(1)); + event->idx = 1; + success = true; + break; + } + + case PLUGIN_SET_DEFAULTS: + { + PCONFIG(0) = 0; + PCONFIG(1) = static_cast(Sensor_VType::SENSOR_TYPE_NONE); + PCONFIG(2) = 0; + break; + } + + case PLUGIN_FORMAT_USERVAR: + { + string = UserVar.getAsString(event->TaskIndex, event->idx, static_cast(PCONFIG(1)), 2); + success = true; + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + String lines[VARS_PER_TASK]; + + addRowLabel(F("Node")); + addNumericBox(F("p155_node"), PCONFIG(0), 0, 0xFFFF); + + addRowLabel(F("Task Id")); + addNumericBox(F("p155_task_id"), PCONFIG(2) + 1, 0, 0xFFFF); + + addRowLabel(F("Sensor Type")); + addTextBox(F("p155_sensor_type"), + getSensorTypeLabel(static_cast(PCONFIG(1))), + 32, + true); + + LoadCustomTaskSettings(event->TaskIndex, lines, VARS_PER_TASK, 0); + + for (int i = 0; i < VARS_PER_TASK; ++i) { + addRowLabel(concat(F("Label "), i + 1)); + if (lines[i].length() == 0) { + lines[i] = concat(F("val"), i + 1); + } + addTextBox(concat(F("p155_value"), i), lines[i], 0); + } + + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + String lines[VARS_PER_TASK]; + + addRowLabel(F("Node")); + PCONFIG(0) = getFormItemInt(F("p155_node")); + PCONFIG(2) = getFormItemInt(F("p155_task_id"))-1; + + for (int i = 0; i < VARS_PER_TASK; ++i) { + lines[i] = webArg(concat(F("p155_value"), i)); + if (lines[i].length() == 0) { + lines[i] = concat(F("val"), i + 1); + } + } + + SaveCustomTaskSettings(event->TaskIndex, lines, VARS_PER_TASK, 0); + + success = true; + break; + } + + case PLUGIN_READ: + { + String log = "CAN Import :" + String(event->TaskIndex) + " got READ event with values: " + + String(event->Par1) + String(" ") + String(event->Par2); + addLogMove(LOG_LEVEL_DEBUG, log); + if (event->Par1 == PCONFIG(0) && event->Par2 == PCONFIG(2)) + { + const int16_t sensorTypeVal = static_cast(event->sensorType); + if (PCONFIG(1) != sensorTypeVal) { + PCONFIG(1) = sensorTypeVal; + + //Zero all other values if sensor type changed + for (uint8_t i = 0; i < VARS_PER_TASK; ++i) { + if (i != event->idx) { + UserVar.setUint32(event->TaskIndex, i, 0); + } + } + } + + sendData(event); + } + break; + } + + case PLUGIN_TEN_PER_SECOND: + { + // P155_check_timeout(event, 0); + // P155_check_timeout(event, 1); + // P155_check_timeout(event, 2); + // P155_check_timeout(event, 3); + break; + } + + case PLUGIN_INIT: + { + Settings.TaskDeviceTimer[event->TaskIndex] = 0; + //Clear timeout flags + PCONFIG(3) = 0; + PCONFIG_ULONG(0) = 0; + PCONFIG_ULONG(1) = 0; + PCONFIG_ULONG(2) = 0; + PCONFIG_ULONG(3) = 0; + success = true; + break; + } + + case PLUGIN_EXIT: + { + success = true; + break; + } + } + return success; +} + +#endif // USES_P155 From 612180e04b997c2e82f443c34024fa5abdd340ba Mon Sep 17 00:00:00 2001 From: Bruno Castro Date: Tue, 10 Sep 2024 22:23:07 -0300 Subject: [PATCH 04/15] Fix Markup --- src/src/WebServer/Markup.cpp | 2190 +++++++++++++++++----------------- 1 file changed, 1095 insertions(+), 1095 deletions(-) diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index 0df35bb830..d7fcf50722 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -1,1095 +1,1095 @@ - -#include "../WebServer/Markup.h" - -#include "../WebServer/HTML_wrappers.h" - -#include "../CustomBuild/ESPEasyLimits.h" - -#include "../Globals/Settings.h" - -#include "../Helpers/Convert.h" -#include "../Helpers/Hardware_GPIO.h" -#include "../Helpers/StringConverter_Numerical.h" -#include "../Helpers/StringConverter.h" -#include "../Helpers/StringGenerator_GPIO.h" - -#include "../../ESPEasy_common.h" - -// ******************************************************************************** -// Add Selector -// ******************************************************************************** -void addSelector(const __FlashStringHelper *id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled) -{ - addSelector(String(id), optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); -} - -void addSelector(const String & id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled) -{ - addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); -} - -void addSelector(const String& id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled) -{ - addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); -} - -void addSelector(const String & id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , const String & tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - // FIXME TD-er Change bool to disabled - if (reloadonchange) - { - addSelector_Head_reloadOnChange(id, classname, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } else { - do_addSelector_Head(id, classname, EMPTY_STRING, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } - addSelector_options(optionCount, options, indices, attr, selectedIndex); - addSelector_Foot(); -} - -void addSelector_reloadOnChange( - const String& id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - const String& onChangeCall, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , - const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - // FIXME TD-er Change bool to disabled - do_addSelector_Head(id, classname, onChangeCall, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - addSelector_options(optionCount, options, indices, attr, selectedIndex); - addSelector_Foot(); -} - - -void addSelector(const String & id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - // FIXME TD-er Change bool to disabled - if (reloadonchange) - { - addSelector_Head_reloadOnChange(id, classname, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } else { - do_addSelector_Head(id, classname, EMPTY_STRING, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } - addSelector_options(optionCount, options, indices, attr, selectedIndex); - addSelector_Foot(); -} - -void addSelector_options(int optionCount, const __FlashStringHelper *options[], const int indices[], const String attr[], int selectedIndex) -{ - for (uint8_t x = 0; x < optionCount; ++x) - { - const int index = indices ? indices[x] : x; - addSelector_Item( - options[x], - index, - selectedIndex == index, - false, - attr ? attr[x] : EMPTY_STRING); - if ((x & 0x07) == 0) delay(0); - } -} - -void addSelector_options(int optionCount, const String options[], const int indices[], const String attr[], int selectedIndex) -{ - for (uint8_t x = 0; x < optionCount; ++x) - { - const int index = indices ? indices[x] : x; - addSelector_Item( - options[x], - index, - selectedIndex == index, - false, - attr ? attr[x] : EMPTY_STRING); - if ((x & 0x07) == 0) delay(0); - } -} - -void addSelector_Head(const String& id) { - do_addSelector_Head(id, F("wide"), EMPTY_STRING, false - #if FEATURE_TOOLTIPS - , F("") - #endif // if FEATURE_TOOLTIPS - ); -} - -void addSelector_Head_reloadOnChange(const __FlashStringHelper * id) { - addSelector_Head_reloadOnChange(String(id), F("wide"), false); -} - -/* -void addSelector_Head_reloadOnChange(const String& id) { - addSelector_Head_reloadOnChange(id, F("wide"), false); -} -*/ - -void addSelector_Head_reloadOnChange(const String& id, - const __FlashStringHelper * classname, - bool disabled - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) { - do_addSelector_Head(id, classname, F("return dept_onchange(frmselect)"), disabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); -} - -void addSelector_Head_reloadOnChange(const String& id, const __FlashStringHelper * classname, const String& onChangeCall, bool disabled - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) { - do_addSelector_Head(id, classname, onChangeCall, disabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); -} - - -void do_addSelector_Head(const String& id, const __FlashStringHelper * classname, const String& onChangeCall, const bool& disabled - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - addHtml(F("")); -} - -void addUnit(const __FlashStringHelper *unit) -{ - addHtml(F(" [")); - addHtml(unit); - addHtml(']'); -} - -void addUnit(const String& unit) -{ - if (unit.isEmpty()) return; - addHtml(F(" [")); - addHtml(unit); - addHtml(']'); -} - -void addUnit(char unit) -{ - addHtml(F(" [")); - addHtml(unit); - addHtml(']'); -} - -void addRowLabel_tr_id(const __FlashStringHelper *label, const __FlashStringHelper *id) -{ - addRowLabel_tr_id(label, String(id)); -} - -void addRowLabel_tr_id(const __FlashStringHelper *label, const String& id) -{ - if (id.isEmpty()) { - addRowLabel(label); - } else { - addRowLabel_tr_id(String(label), id); - } -} - -void addRowLabel_tr_id(const String& label, const String& id) -{ - if (id.isEmpty()) { - addRowLabel(label); - } else { - addRowLabel(label, concat(F("tr_"), id)); - } -} - -void addRowLabel(const __FlashStringHelper *label) -{ - html_TR_TD(); - addHtml(concat(label, F(":"))); - html_TD(); -} - -void addRowLabel(const String& label, const String& id) -{ - if (id.length() > 0) { - addHtml(F("")); - } else { - html_TR_TD(); - } - - if (!label.isEmpty()) { - addHtml(label); - addHtml(':'); - } - addHtml(F("")); - html_TD(); -} - -// Add a row label and mark it with copy markers to copy it to clipboard. -void addRowLabel_copy(const __FlashStringHelper *label) { - addHtml(F("")); - html_copyText_TD(); - addHtml(label); - addHtml(':'); - html_copyText_marker(); - html_copyText_TD(); -} - -void addRowLabel_copy(const String& label) { - addHtml(F("")); - html_copyText_TD(); - addHtml(label); - addHtml(':'); - html_copyText_marker(); - html_copyText_TD(); -} - -void addRowLabel(LabelType::Enum label) { - addRowLabel(getLabel(label)); -} - -void addRowLabelValue(LabelType::Enum label) { - addRowLabel(getLabel(label)); - addHtml(getValue(label)); - addUnit(getFormUnit(label)); -} - -void addRowLabelValues(const LabelType::Enum labels[]) { - size_t i = 0; - LabelType::Enum cur = static_cast(pgm_read_byte(labels + i)); - - while (true) { - const LabelType::Enum next = static_cast(pgm_read_byte(labels + i + 1)); - addRowLabelValue(cur); - if (next == LabelType::MAX_LABEL) { - return; - } - ++i; - cur = next; - } -} - -void addRowLabelValue_copy(LabelType::Enum label) { - addRowLabel_copy(getLabel(label)); - addHtml(getValue(label)); - addUnit(getFormUnit(label)); -} - -// ******************************************************************************** -// Add a header -// ******************************************************************************** -void addTableSeparator(const __FlashStringHelper *label, int colspan, int h_size) -{ - addHtml(strformat( - F(""), - colspan, h_size)); - addHtml(label); - addHtml(strformat( - F(""), - h_size)); -} - -void addTableSeparator(const __FlashStringHelper *label, int colspan, int h_size, const __FlashStringHelper *helpButton) -{ - addTableSeparator(String(label), colspan, h_size, String(helpButton)); -} - -void addTableSeparator(const String& label, int colspan, int h_size, const String& helpButton) { - addHtml(strformat( - F(""), - colspan, h_size)); - addHtml(label); - - if (!helpButton.isEmpty()) { - addHelpButton(helpButton); - } - addHtml(strformat( - F(""), - h_size)); -} - -void addFormHeader(const __FlashStringHelper *header) { - addFormHeader(header, F(""), F("")); -} - -void addFormHeader(const __FlashStringHelper *header, - const __FlashStringHelper *helpButton) -{ - addFormHeader(header, helpButton, F("")); -} - -void addFormHeader(const __FlashStringHelper *header, - const __FlashStringHelper *helpButton, - const __FlashStringHelper *rtdHelpButton) -{ - html_TR(); - html_table_header(header, helpButton, rtdHelpButton, 300); - html_table_header(F("")); -} - -/* -void addFormHeader(const String& header, const String& helpButton) { - addFormHeader(header, helpButton, EMPTY_STRING); -} - -void addFormHeader(const String& header, const String& helpButton, const String& rtdHelpButton) -{ - html_TR(); - html_table_header(header, helpButton, rtdHelpButton, 225); - html_table_header(F("")); -} -*/ - -// ******************************************************************************** -// Add a sub header -// ******************************************************************************** -void addFormSubHeader(const __FlashStringHelper *header) { - addTableSeparator(header, 2, 3); -} - -void addFormSubHeader(const String& header) -{ - addTableSeparator(header, 2, 3); -} - -// ******************************************************************************** -// Add a checkbox -// ******************************************************************************** -void addCheckBox(const __FlashStringHelper *id, bool checked, bool disabled) -{ - addCheckBox(String(id), checked, disabled); -} - -void addCheckBox(const String& id, bool checked, bool disabled - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - addHtml(F("")); -} - -// ******************************************************************************** -// Add a numeric box -// ******************************************************************************** -void addNumericBox(const __FlashStringHelper *id, int value, int min, int max, bool disabled) -{ - addNumericBox(String(id), value, min, max, disabled); -} - -void addNumericBox(const String& id, int value, int min, int max - #if FEATURE_TOOLTIPS - , const __FlashStringHelper * classname, const String& tooltip - #endif // if FEATURE_TOOLTIPS - , bool disabled - ) -{ - addHtml(F(" 0) { - addHtmlAttribute(F("title"), tooltip); - } - #endif // if FEATURE_TOOLTIPS - - if (disabled) { - addDisabled(); - } - - if (value < min) { - value = min; - } - - if (value > max) { - value = max; - } - - if (min != INT_MIN) - { - addHtmlAttribute(F("min"), min); - } - - if (max != INT_MAX) - { - addHtmlAttribute(F("max"), max); - } - addHtmlAttribute(F("value"), value); - addHtml('>'); -} - -#if FEATURE_TOOLTIPS -void addNumericBox(const String& id, int value, int min, int max, bool disabled) -{ - addNumericBox(id, value, min, max, F("widenumber"), EMPTY_STRING, disabled); -} - -#endif // if FEATURE_TOOLTIPS - -void addFloatNumberBox(const String& id, float value, float min, float max, unsigned int nrDecimals, float stepsize - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - addHtml(strformat( - F("'); -} - -// ******************************************************************************** -// Add Textbox -// ******************************************************************************** -void addTextBox(const __FlashStringHelper * id, const String& value, int maxlength, bool readonly, bool required, const String& pattern) { - addTextBox(id, value, maxlength, readonly, required, pattern, F("wide")); -} - -void addTextBox(const String& id, const String& value, int maxlength, bool readonly, bool required, const String& pattern) { - addTextBox(id, value, maxlength, readonly, required, pattern, F("wide")); -} - -void addTextBox(const String & id, - const String & value, - int maxlength, - bool readonly, - bool required, - const String & pattern, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - , - const String& datalist - ) -{ - addHtml(F(" 0) { - addHtmlAttribute(F("maxlength"), maxlength); - } - if (!datalist.isEmpty()) { - addHtmlAttribute(F("list"), datalist); - } - addHtmlAttribute(F("value"), value); - - if (readonly) { - addHtml(F(" readonly ")); - } - - if (required) { - addHtml(F(" required ")); - } - - if (pattern.length() > 0) { - addHtmlAttribute(F("pattern"), pattern); - } - - #if FEATURE_TOOLTIPS - - if (tooltip.length() > 0) { - addHtmlAttribute(F("title"), tooltip); - } - #endif // if FEATURE_TOOLTIPS - addHtml('>'); -} - - - -// ******************************************************************************** -// Add Textarea -// ******************************************************************************** -void addTextArea(const String & id, - const String & value, - int maxlength, - int rows, - int columns, - bool readonly, - bool required - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - addHtml(F("")); -} - -// ******************************************************************************** -// Add Help Buttons -// ******************************************************************************** - -// adds a Help Button with points to the the given Wiki Subpage -// If url starts with "RTD", it will be considered as a Read-the-docs link -void addHelpButton(const __FlashStringHelper *url) { - addHelpButton(String(url)); -} - -void addHelpButton(const String& url) { -#ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON - - if (url.startsWith("RTD")) { - addRTDHelpButton(url.substring(3)); - } else { - addHelpButton(url, false); - } -#endif // ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON -} - -void addRTDHelpButton(const String& url) -{ - addHelpButton(url, true); -} - -void addHelpButton(const String& url, bool isRTD) -{ - #ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON - addHtmlLink( - F("button help"), - makeDocLink(url, isRTD), - isRTD ? F("i") : F("?")); - #endif // ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON -} - -void addRTDPluginButton(pluginID_t pluginID) { - addRTDHelpButton( - strformat( - F("Plugin/%s.html"), - get_formatted_Plugin_number(pluginID).c_str())); - - constexpr pluginID_t PLUGIN_ID_P076_HLW8012(76); - constexpr pluginID_t PLUGIN_ID_P077_CSE7766(77); - - if ((pluginID == PLUGIN_ID_P076_HLW8012) || - (pluginID == PLUGIN_ID_P077_CSE7766)) { - addHtmlLink( - F("button help"), - makeDocLink(F("Reference/Safety.html"), true), - F("⚡")); // High voltage sign - } -} - -# ifndef LIMIT_BUILD_SIZE -void addRTDControllerButton(cpluginID_t cpluginID) { - addRTDHelpButton( - strformat( - F("Controller/%s.html"), - get_formatted_Controller_number(cpluginID).c_str())); -} -# endif // ifndef LIMIT_BUILD_SIZE - -String makeDocLink(const String& url, bool isRTD) { - String result; - - if (!url.startsWith(F("http"))) { - if (isRTD) { - result += F("https://espeasy.readthedocs.io/en/latest/"); - } else { - result += F("http://www.letscontrolit.com/wiki/index.php/"); - } - } - result += url; - return result; -} - -void addPinSelect(PinSelectPurpose purpose, const __FlashStringHelper *id, int choice) -{ - addPinSelect(purpose, String(id), choice); -} - -void addPinSelect(PinSelectPurpose purpose, const String& id, int choice) -{ - addSelector_Head(id); - - // At i == 0 && gpio == -1, add the "- None -" option first - int i = 0; - int gpio = -1; - - while (gpio <= MAX_GPIO) { - int pinnr = -1; - bool input, output, warning = false; - - // Make sure getGpioInfo is called (compiler may optimize it away if (i == 0)) - const bool UsableGPIO = getGpioInfo(gpio, pinnr, input, output, warning); - - if (UsableGPIO || (i == 0)) { - addPinSelector_Item( - purpose, - concat( - createGPIO_label(gpio, pinnr, input, output, warning), - getConflictingUse_wrapped(gpio, purpose)), - gpio, - choice == gpio); - - ++i; - } - ++gpio; - } - addSelector_Foot(); -} - -#ifdef ESP32 -void addADC_PinSelect(AdcPinSelectPurpose purpose, const String& id, int choice) -{ - addSelector_Head(id); - - // At i == 0 && gpio == -1, add the "Hall Effect" option first - int i = 0; - int gpio = -1; - - if ( -#if HAS_HALL_EFFECT_SENSOR - (purpose == AdcPinSelectPurpose::ADC_Touch_HallEffect) || -#endif - (purpose == AdcPinSelectPurpose::ADC_Touch_Optional)) { - addPinSelector_Item( - PinSelectPurpose::Generic, - purpose == AdcPinSelectPurpose::ADC_Touch_Optional ? F("- None -") : formatGpioName_ADC(gpio), - gpio, - choice == gpio); - } - - while (i <= MAX_GPIO && gpio <= MAX_GPIO) { - int pinnr = -1; - bool input, output, warning; - - if (purpose == AdcPinSelectPurpose::TouchOnly) { - // For touch only list, sort based on touch number - // Default sort is on GPIO number. - gpio = touchPinToGpio(i); - } else { - ++gpio; - } - - if (getGpioInfo(gpio, pinnr, input, output, warning)) { - int adc, ch, t; - - if (getADC_gpio_info(gpio, adc, ch, t)) { - if ((purpose != AdcPinSelectPurpose::TouchOnly) || (t >= 0)) { - String gpio_label; - gpio_label = formatGpioName_ADC(gpio); - - if (adc != 0) { - gpio_label += F(" / "); - gpio_label += createGPIO_label(gpio, pinnr, input, output, warning); - gpio_label += getConflictingUse_wrapped(gpio); - } - addPinSelector_Item( - PinSelectPurpose::Generic, - gpio_label, - gpio, - choice == gpio); - } - } - } - ++i; - } - addSelector_Foot(); -} - -void addDAC_PinSelect(const String& id, int choice) -{ - addSelector_Head(id); - - // At i == 0 && gpio == -1, add the "- None -" option first - int i = 0; - int gpio = -1; - - while (gpio <= MAX_GPIO) { - int pinnr = -1; - bool input = false; - bool output = false; - bool warning = false; - int dac = 0; - - // Make sure getGpioInfo is called (compiler may optimize it away if (i == 0)) - const bool UsableGPIO = getDAC_gpio_info(gpio, dac); // getGpioInfo(gpio, pinnr, input, output, warning); - - if (UsableGPIO || (i == 0)) { - if (getGpioInfo(gpio, pinnr, input, output, warning) || (i == 0)) { - String gpio_label = formatGpioName_DAC(gpio); - - if (dac != 0) { - gpio_label += F(" / "); - gpio_label += createGPIO_label(gpio, pinnr, input, output, warning); - gpio_label += getConflictingUse_wrapped(gpio, PinSelectPurpose::DAC); - } - addPinSelector_Item( - PinSelectPurpose::DAC, - gpio_label, - gpio, - choice == gpio); - } - ++i; - } - ++gpio; - } - addSelector_Foot(); -} - -#endif // ifdef ESP32 + +#include "../WebServer/Markup.h" + +#include "../WebServer/HTML_wrappers.h" + +#include "../CustomBuild/ESPEasyLimits.h" + +#include "../Globals/Settings.h" + +#include "../Helpers/Convert.h" +#include "../Helpers/Hardware_GPIO.h" +#include "../Helpers/StringConverter_Numerical.h" +#include "../Helpers/StringConverter.h" +#include "../Helpers/StringGenerator_GPIO.h" + +#include "../../ESPEasy_common.h" + +// ******************************************************************************** +// Add Selector +// ******************************************************************************** +void addSelector(const __FlashStringHelper *id, + int optionCount, + const __FlashStringHelper *options[], + const int indices[], + const String attr[], + int selectedIndex, + bool reloadonchange, + bool enabled) +{ + addSelector(String(id), optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); +} + +void addSelector(const String & id, + int optionCount, + const __FlashStringHelper *options[], + const int indices[], + const String attr[], + int selectedIndex, + bool reloadonchange, + bool enabled) +{ + addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); +} + +void addSelector(const String& id, + int optionCount, + const String options[], + const int indices[], + const String attr[], + int selectedIndex, + bool reloadonchange, + bool enabled) +{ + addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); +} + +void addSelector(const String & id, + int optionCount, + const __FlashStringHelper *options[], + const int indices[], + const String attr[], + int selectedIndex, + bool reloadonchange, + bool enabled, + const __FlashStringHelper * classname + #if FEATURE_TOOLTIPS + , const String & tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + // FIXME TD-er Change bool to disabled + if (reloadonchange) + { + addSelector_Head_reloadOnChange(id, classname, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + } else { + do_addSelector_Head(id, classname, EMPTY_STRING, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + } + addSelector_options(optionCount, options, indices, attr, selectedIndex); + addSelector_Foot(); +} + +void addSelector_reloadOnChange( + const String& id, + int optionCount, + const String options[], + const int indices[], + const String attr[], + int selectedIndex, + const String& onChangeCall, + bool enabled, + const __FlashStringHelper * classname + #if FEATURE_TOOLTIPS + , + const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + // FIXME TD-er Change bool to disabled + do_addSelector_Head(id, classname, onChangeCall, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + addSelector_options(optionCount, options, indices, attr, selectedIndex); + addSelector_Foot(); +} + + +void addSelector(const String & id, + int optionCount, + const String options[], + const int indices[], + const String attr[], + int selectedIndex, + bool reloadonchange, + bool enabled, + const __FlashStringHelper * classname + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + // FIXME TD-er Change bool to disabled + if (reloadonchange) + { + addSelector_Head_reloadOnChange(id, classname, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + } else { + do_addSelector_Head(id, classname, EMPTY_STRING, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + } + addSelector_options(optionCount, options, indices, attr, selectedIndex); + addSelector_Foot(); +} + +void addSelector_options(int optionCount, const __FlashStringHelper *options[], const int indices[], const String attr[], int selectedIndex) +{ + for (uint8_t x = 0; x < optionCount; ++x) + { + const int index = indices ? indices[x] : x; + addSelector_Item( + options[x], + index, + selectedIndex == index, + false, + attr ? attr[x] : EMPTY_STRING); + if ((x & 0x07) == 0) delay(0); + } +} + +void addSelector_options(int optionCount, const String options[], const int indices[], const String attr[], int selectedIndex) +{ + for (uint8_t x = 0; x < optionCount; ++x) + { + const int index = indices ? indices[x] : x; + addSelector_Item( + options[x], + index, + selectedIndex == index, + false, + attr ? attr[x] : EMPTY_STRING); + if ((x & 0x07) == 0) delay(0); + } +} + +void addSelector_Head(const String& id) { + do_addSelector_Head(id, F("wide"), EMPTY_STRING, false + #if FEATURE_TOOLTIPS + , F("") + #endif // if FEATURE_TOOLTIPS + ); +} + +void addSelector_Head_reloadOnChange(const __FlashStringHelper * id) { + addSelector_Head_reloadOnChange(String(id), F("wide"), false); +} + +/* +void addSelector_Head_reloadOnChange(const String& id) { + addSelector_Head_reloadOnChange(id, F("wide"), false); +} +*/ + +void addSelector_Head_reloadOnChange(const String& id, + const __FlashStringHelper * classname, + bool disabled + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) { + do_addSelector_Head(id, classname, F("return dept_onchange(frmselect)"), disabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); +} + +void addSelector_Head_reloadOnChange(const String& id, const __FlashStringHelper * classname, const String& onChangeCall, bool disabled + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) { + do_addSelector_Head(id, classname, onChangeCall, disabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); +} + + +void do_addSelector_Head(const String& id, const __FlashStringHelper * classname, const String& onChangeCall, const bool& disabled + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + addHtml(F("")); +} + +void addUnit(const __FlashStringHelper *unit) +{ + addHtml(F(" [")); + addHtml(unit); + addHtml(']'); +} + +void addUnit(const String& unit) +{ + if (unit.isEmpty()) return; + addHtml(F(" [")); + addHtml(unit); + addHtml(']'); +} + +void addUnit(char unit) +{ + addHtml(F(" [")); + addHtml(unit); + addHtml(']'); +} + +void addRowLabel_tr_id(const __FlashStringHelper *label, const __FlashStringHelper *id) +{ + addRowLabel_tr_id(label, String(id)); +} + +void addRowLabel_tr_id(const __FlashStringHelper *label, const String& id) +{ + if (id.isEmpty()) { + addRowLabel(label); + } else { + addRowLabel_tr_id(String(label), id); + } +} + +void addRowLabel_tr_id(const String& label, const String& id) +{ + if (id.isEmpty()) { + addRowLabel(label); + } else { + addRowLabel(label, concat(F("tr_"), id)); + } +} + +void addRowLabel(const __FlashStringHelper *label) +{ + html_TR_TD(); + addHtml(concat(label, F(":"))); + html_TD(); +} + +void addRowLabel(const String& label, const String& id) +{ + if (id.length() > 0) { + addHtml(F("")); + } else { + html_TR_TD(); + } + + if (!label.isEmpty()) { + addHtml(label); + addHtml(':'); + } + addHtml(F("")); + html_TD(); +} + +// Add a row label and mark it with copy markers to copy it to clipboard. +void addRowLabel_copy(const __FlashStringHelper *label) { + addHtml(F("")); + html_copyText_TD(); + addHtml(label); + addHtml(':'); + html_copyText_marker(); + html_copyText_TD(); +} + +void addRowLabel_copy(const String& label) { + addHtml(F("")); + html_copyText_TD(); + addHtml(label); + addHtml(':'); + html_copyText_marker(); + html_copyText_TD(); +} + +void addRowLabel(LabelType::Enum label) { + addRowLabel(getLabel(label)); +} + +void addRowLabelValue(LabelType::Enum label) { + addRowLabel(getLabel(label)); + addHtml(getValue(label)); + addUnit(getFormUnit(label)); +} + +void addRowLabelValues(const LabelType::Enum labels[]) { + size_t i = 0; + LabelType::Enum cur = static_cast(pgm_read_byte(labels + i)); + + while (true) { + const LabelType::Enum next = static_cast(pgm_read_byte(labels + i + 1)); + addRowLabelValue(cur); + if (next == LabelType::MAX_LABEL) { + return; + } + ++i; + cur = next; + } +} + +void addRowLabelValue_copy(LabelType::Enum label) { + addRowLabel_copy(getLabel(label)); + addHtml(getValue(label)); + addUnit(getFormUnit(label)); +} + +// ******************************************************************************** +// Add a header +// ******************************************************************************** +void addTableSeparator(const __FlashStringHelper *label, int colspan, int h_size) +{ + addHtml(strformat( + F(""), + colspan, h_size)); + addHtml(label); + addHtml(strformat( + F(""), + h_size)); +} + +void addTableSeparator(const __FlashStringHelper *label, int colspan, int h_size, const __FlashStringHelper *helpButton) +{ + addTableSeparator(String(label), colspan, h_size, String(helpButton)); +} + +void addTableSeparator(const String& label, int colspan, int h_size, const String& helpButton) { + addHtml(strformat( + F(""), + colspan, h_size)); + addHtml(label); + + if (!helpButton.isEmpty()) { + addHelpButton(helpButton); + } + addHtml(strformat( + F(""), + h_size)); +} + +void addFormHeader(const __FlashStringHelper *header) { + addFormHeader(header, F(""), F("")); +} + +void addFormHeader(const __FlashStringHelper *header, + const __FlashStringHelper *helpButton) +{ + addFormHeader(header, helpButton, F("")); +} + +void addFormHeader(const __FlashStringHelper *header, + const __FlashStringHelper *helpButton, + const __FlashStringHelper *rtdHelpButton) +{ + html_TR(); + html_table_header(header, helpButton, rtdHelpButton, 300); + html_table_header(F("")); +} + +/* +void addFormHeader(const String& header, const String& helpButton) { + addFormHeader(header, helpButton, EMPTY_STRING); +} + +void addFormHeader(const String& header, const String& helpButton, const String& rtdHelpButton) +{ + html_TR(); + html_table_header(header, helpButton, rtdHelpButton, 225); + html_table_header(F("")); +} +*/ + +// ******************************************************************************** +// Add a sub header +// ******************************************************************************** +void addFormSubHeader(const __FlashStringHelper *header) { + addTableSeparator(header, 2, 3); +} + +void addFormSubHeader(const String& header) +{ + addTableSeparator(header, 2, 3); +} + +// ******************************************************************************** +// Add a checkbox +// ******************************************************************************** +void addCheckBox(const __FlashStringHelper *id, bool checked, bool disabled) +{ + addCheckBox(String(id), checked, disabled); +} + +void addCheckBox(const String& id, bool checked, bool disabled + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + addHtml(F("")); +} + +// ******************************************************************************** +// Add a numeric box +// ******************************************************************************** +void addNumericBox(const __FlashStringHelper *id, int value, int min, int max, bool disabled) +{ + addNumericBox(String(id), value, min, max, disabled); +} + +void addNumericBox(const String& id, int value, int min, int max + #if FEATURE_TOOLTIPS + , const __FlashStringHelper * classname, const String& tooltip + #endif // if FEATURE_TOOLTIPS + , bool disabled + ) +{ + addHtml(F(" 0) { + addHtmlAttribute(F("title"), tooltip); + } + #endif // if FEATURE_TOOLTIPS + + if (disabled) { + addDisabled(); + } + + if (value < min) { + value = min; + } + + if (value > max) { + value = max; + } + + if (min != INT_MIN) + { + addHtmlAttribute(F("min"), min); + } + + if (max != INT_MAX) + { + addHtmlAttribute(F("max"), max); + } + addHtmlAttribute(F("value"), value); + addHtml('>'); +} + +#if FEATURE_TOOLTIPS +void addNumericBox(const String& id, int value, int min, int max, bool disabled) +{ + addNumericBox(id, value, min, max, F("widenumber"), EMPTY_STRING, disabled); +} + +#endif // if FEATURE_TOOLTIPS + +void addFloatNumberBox(const String& id, float value, float min, float max, unsigned int nrDecimals, float stepsize + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + addHtml(strformat( + F("'); +} + +// ******************************************************************************** +// Add Textbox +// ******************************************************************************** +void addTextBox(const __FlashStringHelper * id, const String& value, int maxlength, bool readonly, bool required, const String& pattern) { + addTextBox(id, value, maxlength, readonly, required, pattern, F("wide")); +} + +void addTextBox(const String& id, const String& value, int maxlength, bool readonly, bool required, const String& pattern) { + addTextBox(id, value, maxlength, readonly, required, pattern, F("wide")); +} + +void addTextBox(const String & id, + const String & value, + int maxlength, + bool readonly, + bool required, + const String & pattern, + const __FlashStringHelper * classname + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + , + const String& datalist + ) +{ + addHtml(F(" 0) { + addHtmlAttribute(F("maxlength"), maxlength); + } + if (!datalist.isEmpty()) { + addHtmlAttribute(F("list"), datalist); + } + addHtmlAttribute(F("value"), value); + + if (readonly) { + addHtml(F(" readonly ")); + } + + if (required) { + addHtml(F(" required ")); + } + + if (pattern.length() > 0) { + addHtmlAttribute(F("pattern"), pattern); + } + + #if FEATURE_TOOLTIPS + + if (tooltip.length() > 0) { + addHtmlAttribute(F("title"), tooltip); + } + #endif // if FEATURE_TOOLTIPS + addHtml('>'); +} + + + +// ******************************************************************************** +// Add Textarea +// ******************************************************************************** +void addTextArea(const String & id, + const String & value, + int maxlength, + int rows, + int columns, + bool readonly, + bool required + #if FEATURE_TOOLTIPS + , const String& tooltip + #endif // if FEATURE_TOOLTIPS + ) +{ + addHtml(F("")); +} + +// ******************************************************************************** +// Add Help Buttons +// ******************************************************************************** + +// adds a Help Button with points to the the given Wiki Subpage +// If url starts with "RTD", it will be considered as a Read-the-docs link +void addHelpButton(const __FlashStringHelper *url) { + addHelpButton(String(url)); +} + +void addHelpButton(const String& url) { +#ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON + + if (url.startsWith("RTD")) { + addRTDHelpButton(url.substring(3)); + } else { + addHelpButton(url, false); + } +#endif // ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON +} + +void addRTDHelpButton(const String& url) +{ + addHelpButton(url, true); +} + +void addHelpButton(const String& url, bool isRTD) +{ + #ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON + addHtmlLink( + F("button help"), + makeDocLink(url, isRTD), + isRTD ? F("i") : F("?")); + #endif // ifndef WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON +} + +void addRTDPluginButton(pluginID_t pluginID) { + addRTDHelpButton( + strformat( + F("Plugin/%s.html"), + get_formatted_Plugin_number(pluginID).c_str())); + + constexpr pluginID_t PLUGIN_ID_P076_HLW8012(76); + constexpr pluginID_t PLUGIN_ID_P077_CSE7766(77); + + if ((pluginID == PLUGIN_ID_P076_HLW8012) || + (pluginID == PLUGIN_ID_P077_CSE7766)) { + addHtmlLink( + F("button help"), + makeDocLink(F("Reference/Safety.html"), true), + F("⚡")); // High voltage sign + } +} + +# ifndef LIMIT_BUILD_SIZE +void addRTDControllerButton(cpluginID_t cpluginID) { + addRTDHelpButton( + strformat( + F("Controller/%s.html"), + get_formatted_Controller_number(cpluginID).c_str())); +} +# endif // ifndef LIMIT_BUILD_SIZE + +String makeDocLink(const String& url, bool isRTD) { + String result; + + if (!url.startsWith(F("http"))) { + if (isRTD) { + result += F("https://espeasy.readthedocs.io/en/latest/"); + } else { + result += F("http://www.letscontrolit.com/wiki/index.php/"); + } + } + result += url; + return result; +} + +void addPinSelect(PinSelectPurpose purpose, const __FlashStringHelper *id, int choice) +{ + addPinSelect(purpose, String(id), choice); +} + +void addPinSelect(PinSelectPurpose purpose, const String& id, int choice) +{ + addSelector_Head(id); + + // At i == 0 && gpio == -1, add the "- None -" option first + int i = 0; + int gpio = -1; + + while (gpio <= MAX_GPIO) { + int pinnr = -1; + bool input, output, warning = false; + + // Make sure getGpioInfo is called (compiler may optimize it away if (i == 0)) + const bool UsableGPIO = getGpioInfo(gpio, pinnr, input, output, warning); + + if (UsableGPIO || (i == 0)) { + addPinSelector_Item( + purpose, + concat( + createGPIO_label(gpio, pinnr, input, output, warning), + getConflictingUse_wrapped(gpio, purpose)), + gpio, + choice == gpio); + + ++i; + } + ++gpio; + } + addSelector_Foot(); +} + +#ifdef ESP32 +void addADC_PinSelect(AdcPinSelectPurpose purpose, const String& id, int choice) +{ + addSelector_Head(id); + + // At i == 0 && gpio == -1, add the "Hall Effect" option first + int i = 0; + int gpio = -1; + + if ( +#if HAS_HALL_EFFECT_SENSOR + (purpose == AdcPinSelectPurpose::ADC_Touch_HallEffect) || +#endif + (purpose == AdcPinSelectPurpose::ADC_Touch_Optional)) { + addPinSelector_Item( + PinSelectPurpose::Generic, + purpose == AdcPinSelectPurpose::ADC_Touch_Optional ? F("- None -") : formatGpioName_ADC(gpio), + gpio, + choice == gpio); + } + + while (i <= MAX_GPIO && gpio <= MAX_GPIO) { + int pinnr = -1; + bool input, output, warning; + + if (purpose == AdcPinSelectPurpose::TouchOnly) { + // For touch only list, sort based on touch number + // Default sort is on GPIO number. + gpio = touchPinToGpio(i); + } else { + ++gpio; + } + + if (getGpioInfo(gpio, pinnr, input, output, warning)) { + int adc, ch, t; + + if (getADC_gpio_info(gpio, adc, ch, t)) { + if ((purpose != AdcPinSelectPurpose::TouchOnly) || (t >= 0)) { + String gpio_label; + gpio_label = formatGpioName_ADC(gpio); + + if (adc != 0) { + gpio_label += F(" / "); + gpio_label += createGPIO_label(gpio, pinnr, input, output, warning); + gpio_label += getConflictingUse_wrapped(gpio); + } + addPinSelector_Item( + PinSelectPurpose::Generic, + gpio_label, + gpio, + choice == gpio); + } + } + } + ++i; + } + addSelector_Foot(); +} + +void addDAC_PinSelect(const String& id, int choice) +{ + addSelector_Head(id); + + // At i == 0 && gpio == -1, add the "- None -" option first + int i = 0; + int gpio = -1; + + while (gpio <= MAX_GPIO) { + int pinnr = -1; + bool input = false; + bool output = false; + bool warning = false; + int dac = 0; + + // Make sure getGpioInfo is called (compiler may optimize it away if (i == 0)) + const bool UsableGPIO = getDAC_gpio_info(gpio, dac); // getGpioInfo(gpio, pinnr, input, output, warning); + + if (UsableGPIO || (i == 0)) { + if (getGpioInfo(gpio, pinnr, input, output, warning) || (i == 0)) { + String gpio_label = formatGpioName_DAC(gpio); + + if (dac != 0) { + gpio_label += F(" / "); + gpio_label += createGPIO_label(gpio, pinnr, input, output, warning); + gpio_label += getConflictingUse_wrapped(gpio, PinSelectPurpose::DAC); + } + addPinSelector_Item( + PinSelectPurpose::DAC, + gpio_label, + gpio, + choice == gpio); + } + ++i; + } + ++gpio; + } + addSelector_Foot(); +} + +#endif // ifdef ESP32 From 9548e18193cff70274c3c60aa08011b19ed2f373 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 22 Sep 2024 14:35:50 +0200 Subject: [PATCH 05/15] [CAN] Make it compile --- src/_C020.cpp | 20 +++++++++++++++++++- src/src/Commands/CAN.cpp | 9 +++++++-- src/src/Commands/CAN.h | 3 +++ src/src/Commands/InternalCommands.cpp | 6 +++--- src/src/CustomBuild/define_plugin_sets.h | 22 +++++++++++++++------- src/src/DataStructs/SettingsStruct.h | 8 ++++++-- src/src/Helpers/Hardware.h | 1 + src/src/Helpers/_Plugin_Helper_CAN.cpp | 7 ++++++- src/src/Helpers/_Plugin_Helper_CAN.h | 5 +++++ src/src/WebServer/HardwarePage.cpp | 2 ++ 10 files changed, 67 insertions(+), 16 deletions(-) diff --git a/src/_C020.cpp b/src/_C020.cpp index 9d4a24f9ad..8136b6b368 100644 --- a/src/_C020.cpp +++ b/src/_C020.cpp @@ -23,7 +23,25 @@ bool CPlugin_020(CPlugin::Function function, struct EventStruct *event, String& case CPlugin::Function::CPLUGIN_PROTOCOL_ADD: { ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_020; - proto.bits = 0; + /* + // These are by default already set to false + proto.usesMQTT = false; + proto.usesAccount = false; + proto.usesPassword = false; + proto.usesTemplate = false; + proto.usesID = false; + proto.Custom = false; + proto.usesSampleSets = false; + proto.usesExtCreds = false; + proto.allowLocalSystemTime = false; + */ + proto.usesHost = false; + proto.usesPort = false; + proto.usesQueue = false; + proto.usesCheckReply = false; + proto.usesTimeout = false; + proto.needsNetwork = false; + proto.allowsExpire = false; break; } diff --git a/src/src/Commands/CAN.cpp b/src/src/Commands/CAN.cpp index 92df7e2897..abb8f2c66d 100644 --- a/src/src/Commands/CAN.cpp +++ b/src/src/Commands/CAN.cpp @@ -1,4 +1,7 @@ -#include "CAN.h" +#include "../Commands/CAN.h" + +#if FEATURE_CAN + #include "../DataStructs/ESPEasy_EventStruct.h" #include "../Helpers/_Plugin_Helper_CAN.h" @@ -18,4 +21,6 @@ const __FlashStringHelper* Command_CAN_SendCAN(struct EventStruct *event, const } return F("ERROR"); -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/src/Commands/CAN.h b/src/src/Commands/CAN.h index 20b94befcb..74a261a101 100644 --- a/src/src/Commands/CAN.h +++ b/src/src/Commands/CAN.h @@ -3,8 +3,11 @@ #include "../../ESPEasy_common.h" +#if FEATURE_CAN + const __FlashStringHelper* Command_CAN_SendToCAN(struct EventStruct *event, const char * line); const __FlashStringHelper* Command_CAN_SendCAN(struct EventStruct *event, const char * line); #endif +#endif \ No newline at end of file diff --git a/src/src/Commands/InternalCommands.cpp b/src/src/Commands/InternalCommands.cpp index 6a0930dd37..b7bfe48aef 100644 --- a/src/src/Commands/InternalCommands.cpp +++ b/src/src/Commands/InternalCommands.cpp @@ -17,7 +17,7 @@ #include "../Commands/HTTP.h" #include "../Commands/InternalCommands_decoder.h" #include "../Commands/i2c.h" -#ifdef FEATURE_CAN +#if FEATURE_CAN #include "../Commands/CAN.h" #endif @@ -476,10 +476,10 @@ 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 - +#if FEATURE_CAN case ESPEasy_cmd_e::sendcan: COMMAND_CASE_A(Command_CAN_SendCAN, -1); //CAN.h case ESPEasy_cmd_e::sendtocan: COMMAND_CASE_A(Command_CAN_SendToCAN, -1); //CAN.h - +#endif case ESPEasy_cmd_e::NotMatched: return false; diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index ae92eb6100..1574374462 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1124,6 +1124,11 @@ To create/register a plugin, you have to : // FIXME TD-er: Should this be enabled on non-Custom builds??? #define FEATURE_CUSTOM_PROVISIONING 1 + #ifndef FEATURE_CAN + #define FEATURE_CAN 1 + #endif + + // See also PLUGIN_SET_MAX section at end, to include any disabled plugins from other definitions // See also PLUGIN_SET_COLLECTION_ESP32 section at end, @@ -1452,12 +1457,6 @@ To create/register a plugin, you have to : #define USES_C013 // ESPEasy P2P network #endif -#ifdef FEATURE_CAN - #define USES_C020 //CAN - #define USES_C021 - #define USES_P155 -#endif - #ifdef NOTIFIER_SET_STABLE #define USES_N001 // Email #define USES_N002 // Buzzer @@ -3420,6 +3419,11 @@ To create/register a plugin, you have to : #endif #endif +#ifndef FEATURE_CAN + #define FEATURE_CAN 0 +#endif + + #if defined(DISABLE_NEOPIXEL_PLUGINS) && DISABLE_NEOPIXEL_PLUGINS // Disable NeoPixel plugins #ifdef USES_P038 @@ -3443,7 +3447,11 @@ To create/register a plugin, you have to : #endif - +#if FEATURE_CAN + #define USES_C020 //CAN + #define USES_P155 +#endif + #if !defined(CUSTOM_BUILD_CDN_URL) && !defined(FEATURE_ALTERNATIVE_CDN_URL) #if defined(WEBSERVER_EMBED_CUSTOM_CSS) || defined(EMBED_ESPEASY_DEFAULT_MIN_CSS) || defined(EMBED_ESPEASY_DEFAULT_MIN_CSS_USE_GZ) diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 2d8716ff3d..a220388425 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -571,10 +571,14 @@ class SettingsStruct_tmpl int8_t console_serial_txpin = DEFAULT_CONSOLE_PORT_TXPIN; uint8_t console_serial0_fallback = DEFAULT_CONSOLE_SER0_FALLBACK; - uint8_t CAN_Tx_pin = -1; - uint8_t CAN_Rx_pin = -1; long CAN_baudrate = DEFAULT_CAN_BAUDRATE; int CAN_node_id = 0; + uint8_t CAN_Tx_pin = -1; + uint8_t CAN_Rx_pin = -1; + + // Added extra bytes which can be used for something else to make sure we're not having gaps in the settings + uint8_t unused_afterCAN1 = 0; + uint8_t unused_afterCAN2 = 0; // Try to extend settings to make the checksum 4-uint8_t aligned. }; diff --git a/src/src/Helpers/Hardware.h b/src/src/Helpers/Hardware.h index d028f9796d..881de3df3c 100644 --- a/src/src/Helpers/Hardware.h +++ b/src/src/Helpers/Hardware.h @@ -66,6 +66,7 @@ int getCPU_MinFreqMHz(); #if FEATURE_CAN +// FIXME TD-er: Move to Hardware_CAN.h/.cpp void initCAN(); #endif diff --git a/src/src/Helpers/_Plugin_Helper_CAN.cpp b/src/src/Helpers/_Plugin_Helper_CAN.cpp index a5a7098f70..1302be3f56 100644 --- a/src/src/Helpers/_Plugin_Helper_CAN.cpp +++ b/src/src/Helpers/_Plugin_Helper_CAN.cpp @@ -1,7 +1,11 @@ #include "_Plugin_Helper_CAN.h" + +#if FEATURE_CAN + #include "../Globals/Settings.h" #include "../Globals/RuntimeData.h" -#include "../Commands/InternalCommands.h" +//#include "../Commands/InternalCommands.h" +#include "../Commands/ExecuteCommand.h" # include # define CAN_HELPER_MSG_DATA ((uint8_t)0) @@ -207,3 +211,4 @@ bool canHelper_sendCmd(EventStruct *event) return false; } +#endif \ No newline at end of file diff --git a/src/src/Helpers/_Plugin_Helper_CAN.h b/src/src/Helpers/_Plugin_Helper_CAN.h index 4435077da8..3c7ace2267 100644 --- a/src/src/Helpers/_Plugin_Helper_CAN.h +++ b/src/src/Helpers/_Plugin_Helper_CAN.h @@ -1,6 +1,10 @@ #ifndef HELPERS__PLUGIN_HELPER_CAN_H #define HELPERS__PLUGIN_HELPER_CAN_H +#include "../_Plugin_Helper.h" + +#if FEATURE_CAN + #include "../DataStructs/ESPEasy_EventStruct.h" void canHelper_sendTaskData(EventStruct *event); @@ -12,3 +16,4 @@ bool canHelper_sendCmd(EventStruct *event); void canHelper_recv(); #endif +#endif diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 49fc1be5c9..a2477aa06d 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -17,6 +17,7 @@ #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Hardware_GPIO.h" #include "../Helpers/Hardware_I2C.h" +#include "../Helpers/Hardware.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" @@ -82,6 +83,7 @@ void handle_hardware() { Settings.NetworkMedium = static_cast(getFormItemInt(F("ethwifi"))); #endif // if FEATURE_ETHERNET #if FEATURE_CAN + // FIXME TD-er: Should this be in the global settings? Settings.CAN_Rx_pin = getFormItemInt(F("canrxpin"), -1); Settings.CAN_Tx_pin = getFormItemInt(F("cantxpin"), -1); Settings.CAN_baudrate = getFormItemInt(F("canbaudrate"), DEFAULT_CAN_BAUDRATE); From 29f10cf99f7349de7a786f12f56f612738c599d6 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 22 Sep 2024 14:48:53 +0200 Subject: [PATCH 06/15] [CAN] Rename plugin and controller to reserved IDs --- src/{_C020.cpp => _C022.cpp} | 18 +++--- ...55_CAN_helper.ino => _P174_CAN_helper.ino} | 58 +++++++++---------- src/src/CustomBuild/define_plugin_sets.h | 4 +- 3 files changed, 40 insertions(+), 40 deletions(-) rename src/{_C020.cpp => _C022.cpp} (87%) rename src/{_P155_CAN_helper.ino => _P174_CAN_helper.ino} (80%) diff --git a/src/_C020.cpp b/src/_C022.cpp similarity index 87% rename from src/_C020.cpp rename to src/_C022.cpp index 8136b6b368..9d8af6f1c8 100644 --- a/src/_C020.cpp +++ b/src/_C022.cpp @@ -1,20 +1,20 @@ #include "src/Helpers/_CPlugin_Helper.h" -#ifdef USES_C020 +#ifdef USES_C022 // ####################################################################################################### -// ########################### Controller Plugin 020: CAN - SJA1000 ###################################### +// ########################### Controller Plugin 022: CAN - SJA1000 ###################################### // ####################################################################################################### -# define CPLUGIN_020 -# define CPLUGIN_ID_020 20 -# define CPLUGIN_NAME_020 "CAN 2.0 - TWAI" +# define CPLUGIN_022 +# define CPLUGIN_ID_022 22 +# define CPLUGIN_NAME_022 "CAN 2.0 - TWAI" # include "src/DataTypes/ESPEasy_plugin_functions.h" # include "src/Globals/CPlugins.h" # include "src/Helpers/_Plugin_Helper_CAN.h" -bool CPlugin_020(CPlugin::Function function, struct EventStruct *event, String& string) +bool CPlugin_022(CPlugin::Function function, struct EventStruct *event, String& string) { bool success = false; @@ -22,7 +22,7 @@ bool CPlugin_020(CPlugin::Function function, struct EventStruct *event, String& { case CPlugin::Function::CPLUGIN_PROTOCOL_ADD: { - ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_020; + ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_022; /* // These are by default already set to false proto.usesMQTT = false; @@ -47,7 +47,7 @@ bool CPlugin_020(CPlugin::Function function, struct EventStruct *event, String& case CPlugin::Function::CPLUGIN_GET_DEVICENAME: { - string = F(CPLUGIN_NAME_020); + string = F(CPLUGIN_NAME_022); break; } @@ -105,4 +105,4 @@ bool CPlugin_020(CPlugin::Function function, struct EventStruct *event, String& return success; } -#endif // ifdef USES_C020 +#endif // ifdef USES_C022 diff --git a/src/_P155_CAN_helper.ino b/src/_P174_CAN_helper.ino similarity index 80% rename from src/_P155_CAN_helper.ino rename to src/_P174_CAN_helper.ino index b79860304a..924493ef6a 100644 --- a/src/_P155_CAN_helper.ino +++ b/src/_P174_CAN_helper.ino @@ -1,8 +1,8 @@ #include "_Plugin_Helper.h" -#ifdef USES_P155 +#ifdef USES_P174 // ####################################################################################################### -// ################################### Plugin 155: CAN SJA1000 Importer ################################## +// ################################### Plugin 174: CAN SJA1000 Importer ################################## // ####################################################################################################### @@ -10,22 +10,22 @@ # include -# define PLUGIN_155 -# define PLUGIN_ID_155 155 -# define PLUGIN_NAME_155 "Communication - CAN Importer" +# define PLUGIN_174 +# define PLUGIN_ID_174 174 +# define PLUGIN_NAME_174 "Communication - CAN Importer" -# define P155_MAX_NODES 255 -# define P155_TOPIC_MAX_SIZE 16 -# define P155_TIMEOUT 50 +# define P174_MAX_NODES 255 +# define P174_TOPIC_MAX_SIZE 16 +# define P174_TIMEOUT 50 -# define P155_VAL1_FLAG 0x01 -# define P155_VAL2_FLAG 0x02 -# define P155_VAL3_FLAG 0x04 -# define P155_VAL4_FLAG 0x10 +# define P174_VAL1_FLAG 0x01 +# define P174_VAL2_FLAG 0x02 +# define P174_VAL3_FLAG 0x04 +# define P174_VAL4_FLAG 0x10 -#define PLUGIN_VALUENAME1_155 "Status" +#define PLUGIN_VALUENAME1_174 "Status" -void P155_check_timeout(EventStruct *event, int idx) +void P174_check_timeout(EventStruct *event, int idx) { int16_t idx_flag = (0x01 << idx); @@ -44,7 +44,7 @@ void P155_check_timeout(EventStruct *event, int idx) } } -boolean Plugin_155(uint8_t function, struct EventStruct *event, String& string) +boolean Plugin_174(uint8_t function, struct EventStruct *event, String& string) { boolean success = false; @@ -52,7 +52,7 @@ boolean Plugin_155(uint8_t function, struct EventStruct *event, String& string) { case PLUGIN_DEVICE_ADD: { - Device[++deviceCount].Number = PLUGIN_ID_155; + Device[++deviceCount].Number = PLUGIN_ID_174; Device[deviceCount].Type = DEVICE_TYPE_DUMMY; Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_NONE; Device[deviceCount].Ports = 0; @@ -71,7 +71,7 @@ boolean Plugin_155(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_GET_DEVICENAME: { - string = F(PLUGIN_NAME_155); + string = F(PLUGIN_NAME_174); break; } @@ -127,13 +127,13 @@ boolean Plugin_155(uint8_t function, struct EventStruct *event, String& string) String lines[VARS_PER_TASK]; addRowLabel(F("Node")); - addNumericBox(F("p155_node"), PCONFIG(0), 0, 0xFFFF); + addNumericBox(F("p174_node"), PCONFIG(0), 0, 0xFFFF); addRowLabel(F("Task Id")); - addNumericBox(F("p155_task_id"), PCONFIG(2) + 1, 0, 0xFFFF); + addNumericBox(F("p174_task_id"), PCONFIG(2) + 1, 0, 0xFFFF); addRowLabel(F("Sensor Type")); - addTextBox(F("p155_sensor_type"), + addTextBox(F("p174_sensor_type"), getSensorTypeLabel(static_cast(PCONFIG(1))), 32, true); @@ -145,7 +145,7 @@ boolean Plugin_155(uint8_t function, struct EventStruct *event, String& string) if (lines[i].length() == 0) { lines[i] = concat(F("val"), i + 1); } - addTextBox(concat(F("p155_value"), i), lines[i], 0); + addTextBox(concat(F("p174_value"), i), lines[i], 0); } success = true; @@ -157,11 +157,11 @@ boolean Plugin_155(uint8_t function, struct EventStruct *event, String& string) String lines[VARS_PER_TASK]; addRowLabel(F("Node")); - PCONFIG(0) = getFormItemInt(F("p155_node")); - PCONFIG(2) = getFormItemInt(F("p155_task_id"))-1; + PCONFIG(0) = getFormItemInt(F("p174_node")); + PCONFIG(2) = getFormItemInt(F("p174_task_id"))-1; for (int i = 0; i < VARS_PER_TASK; ++i) { - lines[i] = webArg(concat(F("p155_value"), i)); + lines[i] = webArg(concat(F("p174_value"), i)); if (lines[i].length() == 0) { lines[i] = concat(F("val"), i + 1); } @@ -199,10 +199,10 @@ boolean Plugin_155(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_TEN_PER_SECOND: { - // P155_check_timeout(event, 0); - // P155_check_timeout(event, 1); - // P155_check_timeout(event, 2); - // P155_check_timeout(event, 3); + // P174_check_timeout(event, 0); + // P174_check_timeout(event, 1); + // P174_check_timeout(event, 2); + // P174_check_timeout(event, 3); break; } @@ -228,4 +228,4 @@ boolean Plugin_155(uint8_t function, struct EventStruct *event, String& string) return success; } -#endif // USES_P155 +#endif // USES_P174 diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 1574374462..89208a46f4 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -3448,8 +3448,8 @@ To create/register a plugin, you have to : #if FEATURE_CAN - #define USES_C020 //CAN - #define USES_P155 + #define USES_C022 //CAN + #define USES_P174 #endif From 0bc2b45827c7280a41a90d31f97988b2d8eafdca Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 22 Sep 2024 15:20:45 +0200 Subject: [PATCH 07/15] [CAN] Fix command decoder for CAN commands --- .gitignore | 2 ++ src/_P174_CAN_helper.ino | 12 +++++++----- src/src/Commands/InternalCommands_decoder.cpp | 8 ++++---- src/src/Commands/InternalCommands_decoder.h | 10 ++++------ src/src/CustomBuild/define_plugin_sets.h | 18 +++++++++++++++++- 5 files changed, 34 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index a7d3d15ffb..95ee0d2556 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,5 @@ docs/source/Plugin/_plugin_sets_overview.repl .platformio/ + +*.Identifier diff --git a/src/_P174_CAN_helper.ino b/src/_P174_CAN_helper.ino index 924493ef6a..b42c8e891d 100644 --- a/src/_P174_CAN_helper.ino +++ b/src/_P174_CAN_helper.ino @@ -33,8 +33,7 @@ void P174_check_timeout(EventStruct *event, int idx) { if (!(PCONFIG(3) & idx_flag)) { - String log_debug = F("Disconnected"); - addLogMove(LOG_LEVEL_INFO, log_debug); + addLogMove(LOG_LEVEL_INFO, F("Disconnected")); } PCONFIG(3) |= idx_flag; } @@ -175,9 +174,12 @@ boolean Plugin_174(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_READ: { - String log = "CAN Import :" + String(event->TaskIndex) + " got READ event with values: " + - String(event->Par1) + String(" ") + String(event->Par2); - addLogMove(LOG_LEVEL_DEBUG, log); + + addLogMove(LOG_LEVEL_DEBUG, strformat( + F("CAN Import : %u got READ event with values: %d %d"), + event->TaskIndex, + event->Par1, + event->Par2)); if (event->Par1 == PCONFIG(0) && event->Par2 == PCONFIG(2)) { const int16_t sensorTypeVal = static_cast(event->sensorType); diff --git a/src/src/Commands/InternalCommands_decoder.cpp b/src/src/Commands/InternalCommands_decoder.cpp index b26e82f584..f696508d45 100644 --- a/src/src/Commands/InternalCommands_decoder.cpp +++ b/src/src/Commands/InternalCommands_decoder.cpp @@ -204,6 +204,10 @@ const char Internal_commands_s[] PROGMEM = "sdcard|" "sdremove|" #endif // #if FEATURE_SD +#ifdef FEATURE_CAN + "sendcan|" + "sendtocan|" +#endif #if FEATURE_ESPEASY_P2P "sendto|" #endif // #if FEATURE_ESPEASY_P2P @@ -276,10 +280,6 @@ const char Internal_commands_w[] PROGMEM = "wdconfig|" "wdread|" #endif // ifndef LIMIT_BUILD_SIZE -#ifdef FEUTER_CAN - "sendcan|" - "sendtocan|" -#endif ; const char* getInternalCommand_Haystack_Offset(const char firstLetter, int& offset) diff --git a/src/src/Commands/InternalCommands_decoder.h b/src/src/Commands/InternalCommands_decoder.h index 989b7792f6..eb8597ea98 100644 --- a/src/src/Commands/InternalCommands_decoder.h +++ b/src/src/Commands/InternalCommands_decoder.h @@ -168,6 +168,10 @@ enum class ESPEasy_cmd_e : uint8_t { sdcard, sdremove, #endif // #if FEATURE_SD +#ifdef FEATURE_CAN + sendcan, + sendtocan, +#endif #if FEATURE_ESPEASY_P2P sendto, #endif // #if FEATURE_ESPEASY_P2P @@ -232,12 +236,6 @@ enum class ESPEasy_cmd_e : uint8_t { wdread, #endif // ifndef LIMIT_BUILD_SIZE -#ifdef FEATURE_CAN - sendcan, - sendtocan, -#endif - - NotMatched // Keep as last one }; diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 89208a46f4..b43dba2dc4 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -3419,10 +3419,26 @@ To create/register a plugin, you have to : #endif #endif +#ifdef FEATURE_CAN + #if FEATURE_CAN + #ifdef ESP8266 + #undef FEATURE_CAN // Do not use CAN bus for ESP8266 + #define FEATURE_CAN 0 + #endif + #endif +#endif + #ifndef FEATURE_CAN #define FEATURE_CAN 0 #endif - +#if FEATURE_CAN + #ifndef USES_C022 // CAN Controller + #define USE_C022 + #endif + #ifndef USES_P174 + #define USES_P174 // CAN Import plugin + #endif +#endif #if defined(DISABLE_NEOPIXEL_PLUGINS) && DISABLE_NEOPIXEL_PLUGINS // Disable NeoPixel plugins From 280de6c35a1ce354bc9f17ba9e56ec895df899aa Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 22 Sep 2024 16:09:54 +0200 Subject: [PATCH 08/15] [CAN] Make lib compile for ESP-IDF5.1 --- lib/arduino-CAN/src/ESP32SJA1000.cpp | 18 ++++++++++++++---- src/src/CustomBuild/define_plugin_sets.h | 4 ++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/arduino-CAN/src/ESP32SJA1000.cpp b/lib/arduino-CAN/src/ESP32SJA1000.cpp index 30e39e984a..fcbd0f6e4b 100644 --- a/lib/arduino-CAN/src/ESP32SJA1000.cpp +++ b/lib/arduino-CAN/src/ESP32SJA1000.cpp @@ -3,10 +3,13 @@ #ifdef ARDUINO_ARCH_ESP32 -#include "esp_intr.h" +#include "esp_intr_alloc.h" #include "soc/dport_reg.h" #include "driver/gpio.h" +#include "esp32-hal-matrix.h" +#include "soc/gpio_pins.h" + #include "ESP32SJA1000.h" #define REG_BASE 0x3ff6b000 @@ -58,14 +61,21 @@ int ESP32SJA1000Class::begin(long baudRate) // RX pin gpio_set_direction(_rxPin, GPIO_MODE_INPUT); - gpio_matrix_in(_rxPin, CAN_RX_IDX, 0); + pinMatrixInAttach(_rxPin, CAN_RX_IDX, 0); + #if ESP_IDF_VERSION_MAJOR <5 gpio_pad_select_gpio(_rxPin); + #else + esp_rom_gpio_pad_select_gpio(_rxPin); + #endif // TX pin gpio_set_direction(_txPin, GPIO_MODE_OUTPUT); - gpio_matrix_out(_txPin, CAN_TX_IDX, 0, 0); + pinMatrixOutAttach(_txPin, CAN_TX_IDX, 0, 0); + #if ESP_IDF_VERSION_MAJOR <5 gpio_pad_select_gpio(_txPin); - + #else + esp_rom_gpio_pad_select_gpio(_txPin); + #endif modifyRegister(REG_CDR, 0x80, 0x80); // pelican mode modifyRegister(REG_BTR0, 0xc0, 0x40); // SJW = 1 modifyRegister(REG_BTR1, 0x70, 0x10); // TSEG2 = 1 diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index b43dba2dc4..4b832f0ff4 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -3421,8 +3421,8 @@ To create/register a plugin, you have to : #ifdef FEATURE_CAN #if FEATURE_CAN - #ifdef ESP8266 - #undef FEATURE_CAN // Do not use CAN bus for ESP8266 + #if defined(ESP8266) || defined(ESP32C2) + #undef FEATURE_CAN // No HW support #define FEATURE_CAN 0 #endif #endif From f4f27589b314033ed286748223e173f65e160268 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 22 Sep 2024 17:35:47 +0200 Subject: [PATCH 09/15] [CAN] Limit to ESP32-classic only Current implementation can only build on ESP32-classic. May need significant code changes for ESP32C3/C6/S2/S3 --- lib/arduino-CAN/library.properties | 2 +- platformio_core_defs.ini | 1 + platformio_esp32_solo1.ini | 1 + platformio_esp32c2_envs.ini | 1 + platformio_esp32c3_envs.ini | 3 +++ platformio_esp32c6_envs.ini | 1 + platformio_esp32s2_envs.ini | 3 +++ platformio_esp32s3_envs.ini | 1 + src/src/Commands/InternalCommands_decoder.cpp | 2 +- src/src/Commands/InternalCommands_decoder.h | 2 +- src/src/CustomBuild/define_plugin_sets.h | 4 ++-- 11 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/arduino-CAN/library.properties b/lib/arduino-CAN/library.properties index acf321fe86..14e13e0573 100644 --- a/lib/arduino-CAN/library.properties +++ b/lib/arduino-CAN/library.properties @@ -3,7 +3,7 @@ version=0.3.1 author=Sandeep Mistry maintainer=Sandeep Mistry sentence=An Arduino library for sending and receiving data using CAN bus. -paragraph=Supports Microchip MCP2515 based boards/shields and the Espressif ESP32's built-in SJA1000 compatible CAN controller. +paragraph=Supports Microchip MCP2515 based boards/shields and the Espressif ESP32 built-in SJA1000 compatible CAN controller. category=Communication url=https://github.com/sandeepmistry/arduino-CAN architectures=* diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 0743e1af7e..372a6ee521 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -59,6 +59,7 @@ lib_ignore = ESP32_ping ArduinoOTA ESP8266mDNS I2C AXP192 Power management + CAN ; EspSoftwareSerial diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index b27d256b84..880cb6e199 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -23,6 +23,7 @@ platform_packages = framework-arduinoespressif32 @ https://github.com/Ja build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS +lib_ignore = ${esp32_base_idf5.lib_ignore} extra_scripts = ${esp32_base_idf5.extra_scripts} build_unflags = ${esp32_base_idf5.build_unflags} -fexceptions diff --git a/platformio_esp32c2_envs.ini b/platformio_esp32c2_envs.ini index 7f8919bf5e..21df7634f0 100644 --- a/platformio_esp32c2_envs.ini +++ b/platformio_esp32c2_envs.ini @@ -14,6 +14,7 @@ lib_ignore = ${esp32_base_idf5.lib_ignore} NeoPixelBus NeoPixelBus_wrapper Adafruit NeoMatrix via NeoPixelBus + CAN [env:safeboot_ESP32c2_4M_LittleFS] diff --git a/platformio_esp32c3_envs.ini b/platformio_esp32c3_envs.ini index f4a36b912b..f263d8e361 100644 --- a/platformio_esp32c3_envs.ini +++ b/platformio_esp32c3_envs.ini @@ -7,6 +7,7 @@ lib_ignore = ${esp32_always.lib_ignore} ESP32_ping ${no_ir.lib_ignore} ESP32 BLE Arduino + CAN build_flags = ${esp32_base.build_flags} -DFEATURE_ARDUINO_OTA=1 -DESP32C3 @@ -20,6 +21,8 @@ build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS -DESP32C3 +lib_ignore = ${esp32_base_idf5.lib_ignore} + CAN extra_scripts = ${esp32_base_idf5.extra_scripts} build_unflags = ${esp32_base_idf5.build_unflags} -fexceptions diff --git a/platformio_esp32c6_envs.ini b/platformio_esp32c6_envs.ini index af5e25d2c4..eca7f98a89 100644 --- a/platformio_esp32c6_envs.ini +++ b/platformio_esp32c6_envs.ini @@ -11,6 +11,7 @@ build_unflags = ${esp32_base_idf5.build_unflags} -fexceptions board_build.filesystem = littlefs lib_ignore = ${esp32_base_idf5.lib_ignore} + CAN board = esp32c6cdc diff --git a/platformio_esp32s2_envs.ini b/platformio_esp32s2_envs.ini index 4419571ba0..9e086d62a5 100644 --- a/platformio_esp32s2_envs.ini +++ b/platformio_esp32s2_envs.ini @@ -8,6 +8,7 @@ lib_ignore = ${esp32_always.lib_ignore} ESP32_ping ${no_ir.lib_ignore} ESP32 BLE Arduino + CAN build_flags = ${esp32_base.build_flags} -DFEATURE_ARDUINO_OTA=1 -DESP32S2 @@ -21,6 +22,8 @@ build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS -DESP32S2 +lib_ignore = ${esp32_base_idf5.lib_ignore} + CAN extra_scripts = ${esp32_base_idf5.extra_scripts} build_unflags = ${esp32_base_idf5.build_unflags} -fexceptions diff --git a/platformio_esp32s3_envs.ini b/platformio_esp32s3_envs.ini index 59ce3af294..93565063ba 100644 --- a/platformio_esp32s3_envs.ini +++ b/platformio_esp32s3_envs.ini @@ -20,6 +20,7 @@ extends = esp32_base_idf5 lib_ignore = ${esp32_common_LittleFS.lib_ignore} ESP32_ping ${esp32_base_idf5.lib_ignore} + CAN build_flags = ${esp32_base_idf5.build_flags} ; -mtext-section-literals -DFEATURE_ARDUINO_OTA=1 diff --git a/src/src/Commands/InternalCommands_decoder.cpp b/src/src/Commands/InternalCommands_decoder.cpp index f696508d45..697be24fcc 100644 --- a/src/src/Commands/InternalCommands_decoder.cpp +++ b/src/src/Commands/InternalCommands_decoder.cpp @@ -204,7 +204,7 @@ const char Internal_commands_s[] PROGMEM = "sdcard|" "sdremove|" #endif // #if FEATURE_SD -#ifdef FEATURE_CAN +#if FEATURE_CAN "sendcan|" "sendtocan|" #endif diff --git a/src/src/Commands/InternalCommands_decoder.h b/src/src/Commands/InternalCommands_decoder.h index eb8597ea98..aee2e80437 100644 --- a/src/src/Commands/InternalCommands_decoder.h +++ b/src/src/Commands/InternalCommands_decoder.h @@ -168,7 +168,7 @@ enum class ESPEasy_cmd_e : uint8_t { sdcard, sdremove, #endif // #if FEATURE_SD -#ifdef FEATURE_CAN +#if FEATURE_CAN sendcan, sendtocan, #endif diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 4b832f0ff4..0474fe8ec9 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -3421,8 +3421,8 @@ To create/register a plugin, you have to : #ifdef FEATURE_CAN #if FEATURE_CAN - #if defined(ESP8266) || defined(ESP32C2) - #undef FEATURE_CAN // No HW support + #if defined(ESP8266) || defined(ESP32C2)|| defined(ESP32C3)|| defined(ESP32C6)|| defined(ESP32S2)|| defined(ESP32S3) + #undef FEATURE_CAN // No HW support or massive changes needed in library #define FEATURE_CAN 0 #endif #endif From 86d8d1ad98e6eb280ed8cd9aa79c43f238aee9c5 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 22 Sep 2024 17:57:16 +0200 Subject: [PATCH 10/15] [CAN] custom_IR_ESP32c3_4M316k_CDC did not ignore CAN lib --- platformio_esp32c3_envs.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platformio_esp32c3_envs.ini b/platformio_esp32c3_envs.ini index f263d8e361..21961f6366 100644 --- a/platformio_esp32c3_envs.ini +++ b/platformio_esp32c3_envs.ini @@ -46,6 +46,7 @@ build_flags = ${esp32c3_common.build_flags} -DPLUGIN_BUILD_IR lib_ignore = ${esp32_always.lib_ignore} ESP32_ping + CAN extra_scripts = ${esp32c3_common.extra_scripts} pre:tools/pio/pre_custom_esp32_IR.py pre:tools/pio/ir_build_check.py @@ -74,6 +75,7 @@ extends = esp32c3_common board = esp32c3cdc lib_ignore = ${esp32_common.lib_ignore} ${no_ir.lib_ignore} + CAN [env:normal_ESP32c3_4M316k_LittleFS_CDC_ETH] From 9d3783568534deca2d59ee34e844ff309a08b521 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 22 Sep 2024 18:29:01 +0200 Subject: [PATCH 11/15] [CAN] Some more envs with their own lib_ignore not ignoring CAN lib --- platformio_esp32s2_envs.ini | 1 + platformio_esp32s3_envs.ini | 2 ++ 2 files changed, 3 insertions(+) diff --git a/platformio_esp32s2_envs.ini b/platformio_esp32s2_envs.ini index 9e086d62a5..eaf4329b6f 100644 --- a/platformio_esp32s2_envs.ini +++ b/platformio_esp32s2_envs.ini @@ -65,6 +65,7 @@ build_flags = ${esp32s2_common.build_flags} -DPLUGIN_BUILD_IR lib_ignore = ${esp32_always.lib_ignore} ESP32_ping + CAN extra_scripts = ${esp32s2_common.extra_scripts} pre:tools/pio/pre_custom_esp32_IR.py pre:tools/pio/ir_build_check.py diff --git a/platformio_esp32s3_envs.ini b/platformio_esp32s3_envs.ini index 93565063ba..81d53594d9 100644 --- a/platformio_esp32s3_envs.ini +++ b/platformio_esp32s3_envs.ini @@ -8,6 +8,7 @@ lib_ignore = ${esp32_always.lib_ignore} ESP32_ping ${no_ir.lib_ignore} ESP32 BLE Arduino + CAN build_flags = ${esp32_base.build_flags} -DFEATURE_ARDUINO_OTA=1 -DESP32S3 @@ -49,6 +50,7 @@ build_flags = ${esp32s3_common.build_flags} -DPLUGIN_BUILD_IR lib_ignore = ${esp32_always.lib_ignore} ESP32_ping + CAN extra_scripts = ${esp32s3_common.extra_scripts} pre:tools/pio/pre_custom_esp32_IR.py pre:tools/pio/ir_build_check.py From 41d5b372e3a3ab5e81a2195e9d984294a279c028 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 22 Sep 2024 20:12:01 +0200 Subject: [PATCH 12/15] [CAN] FIX ESP8266 builds --- src/src/Helpers/_Plugin_Helper_CAN.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/Helpers/_Plugin_Helper_CAN.cpp b/src/src/Helpers/_Plugin_Helper_CAN.cpp index 1302be3f56..8d4be44150 100644 --- a/src/src/Helpers/_Plugin_Helper_CAN.cpp +++ b/src/src/Helpers/_Plugin_Helper_CAN.cpp @@ -1,4 +1,4 @@ -#include "_Plugin_Helper_CAN.h" +#include "../Helpers/_Plugin_Helper_CAN.h" #if FEATURE_CAN From a554806742bcce54c81bb2990e3e67563c111853 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 22 Sep 2024 22:26:51 +0200 Subject: [PATCH 13/15] [CAN] Fix numerous other ESP8266 builds lib_ignore CAN library --- platformio_esp82xx_envs.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index d459c27b27..d94ea38053 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -33,6 +33,7 @@ lib_ignore = ESP32_ping EspSoftwareSerial LittleFS LittleFS(esp8266) + CAN extra_scripts = pre:tools/pio/pre_custom_esp82xx.py ${extra_scripts_esp8266.extra_scripts} @@ -49,6 +50,7 @@ lib_ignore = ESP32_ping I2C AXP192 Power management LittleFS LittleFS(esp8266) + CAN extra_scripts = pre:tools/pio/pre_custom_esp82xx.py ${extra_scripts_esp8266.extra_scripts} @@ -120,6 +122,7 @@ lib_ignore = ESP32_ping Adafruit NeoMatrix I2C AXP192 Power management EspSoftwareSerial + CAN extra_scripts = pre:tools/pio/pre_custom_esp82xx_IR.py ${extra_scripts_esp8266.extra_scripts} pre:tools/pio/ir_build_check.py @@ -175,6 +178,7 @@ lib_ignore = ESP32_ping ${no_ir.lib_ignore} TinyWireM I2C AXP192 Power management + CAN extra_scripts = ${esp8266_custom_common_312.extra_scripts} From da1837308465f3012473d59976385389a3286ff7 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 22 Sep 2024 23:07:09 +0200 Subject: [PATCH 14/15] [CAN] Fix settings struct size check --- src/src/Helpers/ESPEasy_checks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index e3e59ab14d..93d31a8151 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -80,7 +80,7 @@ void run_compiletime_checks() { constexpr unsigned int SettingsStructSize = (352 + 84 * TASKS_MAX); #endif #ifdef ESP8266 - constexpr unsigned int SettingsStructSize = (316 + 84 * TASKS_MAX); + constexpr unsigned int SettingsStructSize = (328 + 84 * TASKS_MAX); #endif #if FEATURE_CUSTOM_PROVISIONING check_size(); From 9c713ba9a6227b3dfb79c6a46e1b775b6fb62c6d Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 22 Sep 2024 23:56:02 +0200 Subject: [PATCH 15/15] [CAN] And another few envs needing CAN lib_ignore --- platformio_esp82xx_base.ini | 1 + platformio_esp82xx_envs.ini | 2 ++ 2 files changed, 3 insertions(+) diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index 90a1b999e4..4c7e7c9316 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -146,6 +146,7 @@ lib_ignore = ESP32_ping ESP8266mDNS I2C AXP192 Power management EspSoftwareSerial + CAN diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index d94ea38053..3bd799a902 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -92,6 +92,7 @@ lib_ignore = ESP32_ping Adafruit NeoMatrix I2C AXP192 Power management EspSoftwareSerial + CAN extra_scripts = pre:tools/pio/pre_custom_esp82xx_IR.py ${extra_scripts_esp8266.extra_scripts} pre:tools/pio/ir_build_check.py @@ -327,6 +328,7 @@ lib_ignore = ESP32_ping ArduinoOTA ESP8266mDNS I2C AXP192 Power management + CAN