Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into feature/Cleanup-and-provisioning-fallback-cmds

This commit is contained in:
Ton Huisman
2026-02-14 15:05:57 +01:00
47 changed files with 1095 additions and 677 deletions
+1 -1
View File
@@ -171,7 +171,7 @@ extra_scripts = ${esp82xx_common.extra_scripts}
;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1311-2008-5.5_orig/framework-arduinoespressif32-release_v5.5_orig-276436da.tar.xz
platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF55_gcc15
platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/0401-1225-5.5/framework-arduinoespressif32-release_v5.5-ad03770f.tar.xz
platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1002-1125-5.5_gcc151/framework-arduinoespressif32-release_v5.5_gcc151-7eede06a.tar.xz
custom_remove_include = true
+5
View File
@@ -10,6 +10,9 @@ build_unflags = ${esp32_base_idf5_5.build_unflags}
-fexceptions
board_build.filesystem = littlefs
lib_ignore = ${esp32_base_idf5_5.lib_ignore}
PPP
HeatpumpIR
IRremoteESP8266
board = esp32c6cdc
@@ -78,6 +81,7 @@ build_flags = ${esp32c6_common_LittleFS.build_flags}
-D ST7789_EXTRA_INIT=1
-D P116_EXTRA_ST7789=1
extra_scripts = ${esp32c6_common_LittleFS.extra_scripts}
lib_ignore = ${esp32_base_idf5_5.lib_ignore}
[env:max_ESP32c6_16M8M]
@@ -91,6 +95,7 @@ build_flags = ${esp32c6_common_LittleFS.build_flags}
-D ST7789_EXTRA_INIT=1
-D P116_EXTRA_ST7789=1
extra_scripts = ${esp32c6_common_LittleFS.extra_scripts}
lib_ignore = ${esp32_base_idf5_5.lib_ignore}
@@ -229,13 +229,15 @@ void NW004_data_struct_ETH_SPI::webform_load(EventStruct *event)
addFormNote(F("I²C-address of Ethernet PHY"));
}
{
if ((getSPIBusCount() > 1) && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) {
if (Settings.getNrConfiguredSPI_buses()) {
const int key = NW004_KEY_SPI_BUS;
const uint8_t spiBus = _kvs->getValueAsInt(key);
KVS_StorageType::Enum storageType;
SPIInterfaceSelector(getLabelString(key, true, storageType),
getLabelString(key, false, storageType),
spiBus);
} else {
// TODO TD-er: Add error message as we need a SPI bus
}
const int gpio_keys[] = {
@@ -446,7 +448,7 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() {
// FIXME TD-er: Fallback for ETH01-EVO board
SPI_host = spi_host_device_t::SPI2_HOST;
Settings.InitSPI = static_cast<int>(SPI_Options_e::UserDefined);
Settings.InitSPI = static_cast<int>(SPI_Options_e::UserDefined_VSPI);
Settings.SPI_SCLK_pin = 7;
Settings.SPI_MISO_pin = 3;
Settings.SPI_MOSI_pin = 10;
@@ -454,7 +456,8 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() {
}
// else
{
if (SPI_host != spi_host_device_t::SPI_HOST_MAX) {
// TODO TD-er: Do we need to include the CLK, MISO, MOSI pins in the call or do we need to start the SPI bus first?
# if ETH_SPI_SUPPORTS_CUSTOM
success = iface->begin(
to_ESP_phy_type(phyType),
+1 -1
View File
@@ -107,7 +107,7 @@ bool checkNrArguments(const char *cmd, const String& Line, int nrArguments) {
++i;
}
}
log += F(" lineLength=");
log += F(" lineLength:");
log += Line.length();
addLogMove(LOG_LEVEL_ERROR, log);
}
+7
View File
@@ -103,6 +103,9 @@ To create/register a plugin, you have to :
#ifndef WEBSERVER_HARDWARE
#define WEBSERVER_HARDWARE
#endif
#ifndef WEBSERVER_INTERFACES
#define WEBSERVER_INTERFACES
#endif
#ifndef WEBSERVER_PINSTATES
#define WEBSERVER_PINSTATES
#endif
@@ -2179,6 +2182,10 @@ To create/register a plugin, you have to :
#define PLUGIN_DESCR "Climate A"
#endif
#ifndef BUILD_NO_DEBUG
#define BUILD_NO_DEBUG
#endif
// Features and plugins cherry picked from stable set
#ifndef FEATURE_SERVO
#define FEATURE_SERVO 1
+3
View File
@@ -371,6 +371,7 @@ public:
bool noCheck = false) const;
bool isSPI_enabled(uint8_t spi_bus) const;
uint8_t getNrConfiguredSPI_buses() const;
#ifdef ESP32
spi_host_device_t getSPI_host(uint8_t spi_bus = 0) const;
#endif
@@ -395,6 +396,8 @@ public:
// Return true if I2C settings are correct
bool isI2CEnabled(uint8_t i2cBus) const;
uint8_t getNrConfiguredI2C_buses() const;
uint8_t getI2CInterface(taskIndex_t TaskIndex) const;
int8_t getI2CSdaPin(uint8_t i2cBus) const;
+38 -7
View File
@@ -993,6 +993,17 @@ bool SettingsStruct_tmpl<N_TASKS>::isSPI_enabled(uint8_t spi_bus) const {
return SPI_Options_e::None != SPI_selection;
}
template<uint32_t N_TASKS>
uint8_t SettingsStruct_tmpl<N_TASKS>::getNrConfiguredSPI_buses() const
{
uint8_t res{};
if (isSPI_valid(0u)) ++res;
#ifdef ESP32
if (getSPIBusCount() > 1 && isSPI_valid(1u)) ++res;
#endif
return res;
}
template<uint32_t N_TASKS>
bool SettingsStruct_tmpl<N_TASKS>::getSPI_pins(int8_t spi_gpios[3], uint8_t spi_bus, bool noCheck) const {
spi_gpios[0] = -1;
@@ -1020,7 +1031,10 @@ bool SettingsStruct_tmpl<N_TASKS>::getSPI_pins(int8_t spi_gpios[3], uint8_t spi_
break;
}
#endif
case SPI_Options_e::UserDefined:
case SPI_Options_e::UserDefined_VSPI:
#if SOC_SPI_PERIPH_NUM > 2
case SPI_Options_e::UserDefined_HSPI:
#endif
{
#ifdef ESP32
if (0 == spi_bus)
@@ -1059,6 +1073,7 @@ spi_host_device_t SettingsStruct_tmpl<N_TASKS>::getSPI_host(uint8_t spi_bus) con
const SPI_Options_e SPI_selection = static_cast<SPI_Options_e>(0 == spi_bus ? InitSPI : InitSPI1);
switch (SPI_selection) {
case SPI_Options_e::Vspi_Fspi:
case SPI_Options_e::UserDefined_VSPI:
{
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
return static_cast<spi_host_device_t>(FSPI_HOST);
@@ -1072,14 +1087,12 @@ spi_host_device_t SettingsStruct_tmpl<N_TASKS>::getSPI_host(uint8_t spi_bus) con
return static_cast<spi_host_device_t>(HSPI_HOST);
}
#endif
case SPI_Options_e::UserDefined:
#if SOC_SPI_PERIPH_NUM > 2
case SPI_Options_e::UserDefined_HSPI:
{
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
return static_cast<spi_host_device_t>(FSPI_HOST);
#else
return static_cast<spi_host_device_t>(VSPI_HOST);
#endif
return static_cast<spi_host_device_t>(HSPI_HOST);
}
#endif
case SPI_Options_e::None:
break;
}
@@ -1202,6 +1215,24 @@ bool SettingsStruct_tmpl<N_TASKS>::isI2CEnabled(uint8_t i2cBus) const {
(getI2CClockSpeedSlow(i2cBus) > 0);
}
template<uint32_t N_TASKS>
uint8_t SettingsStruct_tmpl<N_TASKS>::getNrConfiguredI2C_buses() const
{
#ifdef ESP32
uint8_t res{};
if (isI2CEnabled(0)) ++res;
if (getI2CBusCount() > 1) {
if (isI2CEnabled(1)) ++res;
#if FEATURE_I2C_INTERFACE_3
if (isI2CEnabled(2)) ++res;
#endif
}
return res;
#else
return isI2CEnabled(0) ? 1 : 0;
#endif
}
// stored in I2C_SPI_bus_Flags per Task
template<uint32_t N_TASKS>
uint8_t SettingsStruct_tmpl<N_TASKS>::getSPIBusForTask(taskIndex_t TaskIndex) const {
+69 -34
View File
@@ -3,6 +3,8 @@
#ifdef ESP32
#include "../Helpers/Hardware_device_info.h"
// ESP32 VSPI:
// SCK = 18
// MISO = 19
@@ -35,28 +37,31 @@
# if CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3
#define VSPI_FSPI_SHORT_STRING "FSPI"
#define VSPI_FSPI_SHORT_STRING "FSPI (" STRINGIFY(FSPI_HOST) ")"
# elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2
#define VSPI_FSPI_SHORT_STRING "FSPI"
# elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2
#define VSPI_FSPI_SHORT_STRING "FSPI"
# elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3
#define VSPI_FSPI_SHORT_STRING "SPI"
# elif CONFIG_IDF_TARGET_ESP32C5 // ESP32-C5
#define VSPI_FSPI_SHORT_STRING "FSPI"
# elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6
#define VSPI_FSPI_SHORT_STRING "FSPI"
# elif CONFIG_IDF_TARGET_ESP32C61 // ESP32-C61
#define VSPI_FSPI_SHORT_STRING "FSPI"
# elif CONFIG_IDF_TARGET_ESP32P4 // ESP32-P4
#define VSPI_FSPI_SHORT_STRING "FSPI"
#define VSPI_FSPI_SHORT_STRING "FSPI (" STRINGIFY(FSPI_HOST) ")"
#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61 || CONFIG_IDF_TARGET_ESP32P4
# if SOC_SPI_PERIPH_NUM > 2
#define VSPI_FSPI_SHORT_STRING "FSPI (" STRINGIFY(VSPI_HOST) ")"
# else
#define VSPI_FSPI_SHORT_STRING "SPI" // STRINGIFY(VSPI_HOST)
# endif
# elif CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#define VSPI_FSPI_SHORT_STRING "VSPI"
#define VSPI_FSPI_SHORT_STRING "VSPI (" STRINGIFY(VSPI_HOST) ")"
# else // if CONFIG_IDF_TARGET_ESP32S2
# error Target CONFIG_IDF_TARGET is not supported
# endif // if CONFIG_IDF_TARGET_ESP32S2
#if SOC_SPI_PERIPH_NUM > 2
#ifdef ESP32_CLASSIC
#define HSPI_SHORT_STRING "HSPI (" STRINGIFY(HSPI_HOST) ")"
#else
#define HSPI_SHORT_STRING STRINGIFY(HSPI_HOST)
#endif
#endif
const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option) {
switch (option) {
@@ -65,43 +70,73 @@ const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option) {
case SPI_Options_e::Vspi_Fspi:
return F(
VSPI_FSPI_SHORT_STRING
": CLK=GPIO-" STRINGIFY(VSPI_FSPI_SCK)
", MISO=GPIO-" STRINGIFY(VSPI_FSPI_MISO)
", MOSI=GPIO-" STRINGIFY(VSPI_FSPI_MOSI) );
": CLK=" STRINGIFY(VSPI_FSPI_SCK)
", MISO=" STRINGIFY(VSPI_FSPI_MISO)
", MOSI=" STRINGIFY(VSPI_FSPI_MOSI) );
#ifdef ESP32_CLASSIC
case SPI_Options_e::Hspi:
return F(
"HSPI"
": CLK=GPIO-" STRINGIFY(HSPI_SCLK)
", MISO=GPIO-" STRINGIFY(HSPI_MISO)
", MOSI=GPIO-" STRINGIFY(HSPI_MOSI) );
HSPI_SHORT_STRING
": CLK=" STRINGIFY(HSPI_SCLK)
", MISO=" STRINGIFY(HSPI_MISO)
", MOSI=" STRINGIFY(HSPI_MOSI) );
#endif
case SPI_Options_e::UserDefined:
return F("User-defined: CLK, MISO, MOSI GPIO-pins");
case SPI_Options_e::UserDefined_VSPI:
return F("User-defined " VSPI_FSPI_SHORT_STRING);
#if SOC_SPI_PERIPH_NUM > 2
case SPI_Options_e::UserDefined_HSPI:
return F("User-defined " HSPI_SHORT_STRING);
#endif
}
return F("Unknown");
}
const __FlashStringHelper* get_vspi_fspi_str()
{
return F(VSPI_FSPI_SHORT_STRING);
}
const String getSPI_optionToShortString(SPI_Options_e option, uint8_t spi_bus) {
#ifdef ESP32
String res;
switch (option) {
case SPI_Options_e::None:
return F("Disabled");
case SPI_Options_e::Vspi_Fspi:
res = F(VSPI_FSPI_SHORT_STRING);
break;
#ifdef ESP32_CLASSIC
case SPI_Options_e::Hspi:
res = F("HSPI");
break;
#endif
case SPI_Options_e::UserDefined_VSPI:
res = F("User-defined " VSPI_FSPI_SHORT_STRING);
break;
#if SOC_SPI_PERIPH_NUM > 2
case SPI_Options_e::UserDefined_HSPI:
res = F("User-defined " HSPI_SHORT_STRING);
break;
#endif
}
if (!res.isEmpty()) {
if (getSPIBusCount() > 1) {
return concat(res + F(" bus "), spi_bus);
}
return res;
}
#else
switch (option) {
case SPI_Options_e::None:
return F("Disabled");
case SPI_Options_e::Vspi_Fspi:
return F(VSPI_FSPI_SHORT_STRING);
#ifdef ESP32_CLASSIC
case SPI_Options_e::Hspi:
return F("HSPI");
#endif
case SPI_Options_e::UserDefined:
#ifdef ESP32
return concat(F("User-defined SPI Bus "), spi_bus);
#endif // ifdef ESP32
#ifdef ESP8266
case SPI_Options_e::UserDefined_VSPI:
return F("User-defined SPI");
#endif // ifdef ESP8266
}
#endif
return F("Unknown");
}
+6 -2
View File
@@ -14,7 +14,7 @@
// ESP32-S2/S3 : FSPI
// ESP32 classic:
// SPI_HOST = SPI1_HOST // Only usable on ESP32
// SPI_HOST = SPI1_HOST // Only usable on ESP32, when all functions doing SPI operations are in IRAM
// HSPI_HOST = SPI2_HOST
// VSPI_HOST = SPI3_HOST
//
@@ -73,12 +73,16 @@ enum class SPI_Options_e {
// For ESP32 classic, this is called VSPI
// For later versions it is called FSPI
// N.B. the ESP32-C3 does not seem to name these as there is no SPI3_HOST.
UserDefined = 9 // Leave some room for other, possible future, hardware-related options
UserDefined_VSPI = 9 // Leave some room for other, possible future, hardware-related options
#if SOC_SPI_PERIPH_NUM > 2
,UserDefined_HSPI = 10
#endif
};
#ifdef ESP32
const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option);
const __FlashStringHelper* get_vspi_fspi_str();
const String getSPI_optionToShortString(SPI_Options_e option, uint8_t spi_bus = 0);
#endif // ifdef ESP32
+7 -4
View File
@@ -17,7 +17,7 @@
#include <ESPEasySerialPort.h>
#ifdef ESP32
#include <esp32-hal-periman.h>
# include <esp32-hal-periman.h>
#endif
@@ -241,11 +241,12 @@ void EspEasy_Console_t::begin(uint32_t baudrate)
if (_fallbackSerial._serial != nullptr) {
_fallbackSerial._serial->begin(baudrate);
#ifdef ESP32
# ifdef ESP32
// Need to have this string as C-string, not F-string
perimanSetPinBusExtraType(SOC_RX0, "Console");
perimanSetPinBusExtraType(SOC_TX0, "Console");
#endif
# endif // ifdef ESP32
addLog(LOG_LEVEL_INFO, F("ESPEasy console fallback enabled"));
}
@@ -329,8 +330,10 @@ bool EspEasy_Console_t::process_serialWriteBuffer() {
res = true;
}
#endif // if USES_ESPEASY_CONSOLE_FALLBACK_PORT
#if FEATURE_TIMING_STATS
STOP_TIMER(CONSOLE_WRITE_SERIAL);
if (res) { STOP_TIMER(CONSOLE_WRITE_SERIAL); }
#endif // if FEATURE_TIMING_STATS
return res;
}
+34 -11
View File
@@ -18,10 +18,10 @@
#ifdef ESP32
#define CONSOLE_INPUT_BUFFER_SIZE 1280
# define CONSOLE_INPUT_BUFFER_SIZE 1280
#else
#define CONSOLE_INPUT_BUFFER_SIZE 128
#endif
# define CONSOLE_INPUT_BUFFER_SIZE 128
#endif // ifdef ESP32
/*
@@ -31,19 +31,20 @@
*/
EspEasy_Console_Port::EspEasy_Console_Port(LogDestination log_destination)
: _serialWriteBuffer(log_destination)
: _serialWriteBuffer(log_destination)
{
InputBuffer_Serial = (char*)calloc(1, CONSOLE_INPUT_BUFFER_SIZE);
InputBuffer_Serial = (char *)calloc(1, CONSOLE_INPUT_BUFFER_SIZE);
}
EspEasy_Console_Port::~EspEasy_Console_Port()
{
#if FEATURE_DEFINE_SERIAL_CONSOLE_PORT
if (_serial != nullptr) {
delete _serial;
_serial = nullptr;
}
#endif
#endif // if FEATURE_DEFINE_SERIAL_CONSOLE_PORT
free(InputBuffer_Serial);
}
@@ -111,14 +112,36 @@ void EspEasy_Console_Port::endPort()
}
}
bool EspEasy_Console_Port::process_serialWriteBuffer()
{
if (_serial != nullptr) {
#ifdef ESP32
if (!xPortCanYield()) return false;
#endif
return _serialWriteBuffer.process(_serial, _serial->availableForWrite());
if (!xPortCanYield()) { return false; }
#endif // ifdef ESP32
size_t availableForWrite = _serial->availableForWrite();
if (availableForWrite == 0) { return false; }
if (availableForWrite == 1) {
// For only a single byte, just write it directly
return _serialWriteBuffer.process(_serial, availableForWrite);
}
if (availableForWrite > 64) {
// Set to max. of 64 bytes as this is the optimum 'chunk size' for most
// serial ports, like the CDC ports and I2C to UART.
// Also it is relatively fast to allocate.
availableForWrite = 64;
}
PrintToString str;
str.reserve(availableForWrite);
if (_serialWriteBuffer.process(&str, availableForWrite)) {
_serial->write(str.get().c_str(), str.length());
return true;
}
}
return false;
}
@@ -147,7 +170,7 @@ bool EspEasy_Console_Port::process_consoleInput(uint8_t SerialInByte)
Logging.consolePrintln(concat('>', cmd));
#endif
ExecuteCommand_all({EventValueSource::Enum::VALUE_SOURCE_SERIAL, std::move(cmd)}, true);
ExecuteCommand_all({ EventValueSource::Enum::VALUE_SOURCE_SERIAL, std::move(cmd) }, true);
SerialInByteCounter = 0;
InputBuffer_Serial[0] = 0; // serial data processed, clear buffer
return true;
+1 -1
View File
@@ -466,7 +466,7 @@ bool BuildFixes()
}
if (Settings.Build < 20115) {
if (Settings.InitSPI != static_cast<int>(SPI_Options_e::UserDefined)) { // User-defined SPI pins set to None
if (Settings.InitSPI != static_cast<int>(SPI_Options_e::UserDefined_VSPI)) { // User-defined SPI pins set to None
Settings.SPI_SCLK_pin = -1;
Settings.SPI_MISO_pin = -1;
Settings.SPI_MOSI_pin = -1;
+1 -1
View File
@@ -168,7 +168,7 @@ void run_compiletime_checks() {
#endif
}
constexpr size_t offset_WireClockStretchLimit = offsetof(SettingsStruct, WireClockStretchLimit);
// constexpr size_t offset_WireClockStretchLimit = offsetof(SettingsStruct, WireClockStretchLimit);
constexpr size_t offset_ConnectionFailuresThreshold = offsetof(SettingsStruct, ConnectionFailuresThreshold);
static_assert(184 == offset_ConnectionFailuresThreshold, "");
+1 -8
View File
@@ -13,14 +13,7 @@
void initI2C() {
// configure hardware pins according to eeprom settings.
if (!Settings.isI2CEnabled(0)
#if FEATURE_I2C_MULTIPLE
&& !Settings.isI2CEnabled(1)
# if FEATURE_I2C_INTERFACE_3
&& !Settings.isI2CEnabled(2)
# endif // if FEATURE_I2C_INTERFACE_3
#endif // if FEATURE_I2C_MULTIPLE
)
if (Settings.getNrConfiguredI2C_buses() == 0)
{
return;
}
+1 -5
View File
@@ -36,11 +36,7 @@ void initializeSPIBuses() {
#endif // if FEATURE_ETHERNET
if (Settings.isSPI_valid(0u)
#ifdef ESP32
|| Settings.isSPI_valid(1u)
#endif // ifdef ESP32
)
if (Settings.getNrConfiguredSPI_buses())
{
#ifdef ESP32
+2 -1
View File
@@ -266,7 +266,8 @@ uint32_t getFlashChipSpeed() {
// spi_clk is equal to system clock
return getApbFrequency();
}
return spiClockDivToFrequency(spi_clock);
// TODO TD-er: For P4 a pointer to the SPI bus is needed.
return spiClockDivToFrequency(nullptr, spi_clock);
#endif
# endif // if ESP_IDF_STILL_NEEDS_SPI_REGISTERS_FIXED
#endif // ifdef ESP8266
+9 -8
View File
@@ -103,6 +103,7 @@ constexpr uint8_t getI2CBusCount() {
#if FEATURE_I2C_MULTIPLE
// Not querying the supported nr. of I2C busses in hardware, but using software multiplexing
// Assume/expect IDF 5.x
// FIXME TD-er: Maybe we should look at SOC_HP_I2C_NUM ????
// # if defined(SOC_I2C_SUPPORTED) && SOC_I2C_SUPPORTED
# if FEATURE_I2C_INTERFACE_3
return 3u; // SOC_I2C_NUM; // Let's go for all I2C busses, including LP_I2C (low power, where available)
@@ -117,16 +118,16 @@ constexpr uint8_t getI2CBusCount() {
#endif
}
// Get the number of usable SPI buses
constexpr uint8_t getSPIBusCount() {
#if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4)
return 2u;
#elif defined(ESP32C2) || defined(ESP32C3) || defined(ESP32C5) || defined(ESP32C6) || defined(ESP32C61) || defined(ESP32H2)
return 1u;
#elif defined(ESP8266)
return 1u;
#else // if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4)
#if SOC_SPI_PERIPH_NUM > 3
static_assert(false, "Implement processor architecture");
#endif // if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4)
return 2u;
#elif SOC_SPI_PERIPH_NUM > 2
return 2u;
#else
return 1u;
#endif
}
/********************************************************************************************\
+1 -7
View File
@@ -509,13 +509,7 @@ void I2CInterfaceSelector(String label,
String id,
uint8_t choice,
bool reloadWhenNeeded) {
const uint8_t i2cMaxBusCount = (getI2CBusCount() > 1
? ((Settings.isI2CEnabled(1) ? 1 : 0)
# if FEATURE_I2C_INTERFACE_3
+ (Settings.isI2CEnabled(2) ? 1 : 0)
# endif // if FEATURE_I2C_INTERFACE_3
)
: 0) + (Settings.isI2CEnabled(0) ? 1 : 0);
const uint8_t i2cMaxBusCount = Settings.getNrConfiguredI2C_buses();
if (i2cMaxBusCount > 1) {
static uint8_t i2cBusCount = 0;
+13 -3
View File
@@ -121,6 +121,8 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv)
const size_t nrValues = kv._values.size();
const bool forceString = kv._format == KeyValueStruct::Format::PreFormatted;
if (!kv._isArray) {
// Either 1 value or empty value
if (nrValues == 0) {
@@ -129,7 +131,7 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv)
pr.write('"');
}
else {
writeValue(kv._values[0]);
writeValue(kv._values[0], forceString);
}
} else {
// Multiple values, so we must wrap it in []
@@ -153,7 +155,7 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv)
pr.write('\t');
#endif // ifdef USE_KWH_JSON_PRETTY_PRINT
writeValue(kv._values[i]);
writeValue(kv._values[i], forceString);
}
getPrint().write(']');
#ifndef USE_KWH_JSON_PRETTY_PRINT
@@ -162,7 +164,7 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv)
}
}
void KeyValueWriter_JSON::writeValue(const ValueStruct& val)
void KeyValueWriter_JSON::writeValue(const ValueStruct& val, bool forceString)
{
if (!val.isSet()) { return; }
auto& pr = getPrint();
@@ -196,6 +198,14 @@ void KeyValueWriter_JSON::writeValue(const ValueStruct& val)
case ValueStruct::ValueType::Unset:
case ValueStruct::ValueType::String:
case ValueStruct::ValueType::FlashString:
if (forceString) {
String tmp(to_json_value(str));
if (!isWrappedWithQuotes(tmp)) {
tmp = wrap_String(tmp, '"');
}
pr.print(tmp);
return;
}
break;
}
pr.print(to_json_value(str));
+1 -1
View File
@@ -73,7 +73,7 @@ public:
private:
void writeValue(const ValueStruct& value);
void writeValue(const ValueStruct& value, bool forceString);
#ifdef USE_KWH_JSON_PRETTY_PRINT
+6 -6
View File
@@ -14,7 +14,7 @@ void LogStreamWriter::clear()
_readpos = 0;
}
bool LogStreamWriter::process(Stream*stream, size_t availableForWrite)
bool LogStreamWriter::process(Print*stream, size_t availableForWrite)
{
if (stream == nullptr) { return false; }
return write(*stream, availableForWrite) != 0;
@@ -30,7 +30,7 @@ uint32_t LogStreamWriter::getNrMessages() const
return Logging.getNrMessages(_log_destination);
}
size_t LogStreamWriter::write(Stream& stream, size_t nrBytesToWrite)
size_t LogStreamWriter::write(Print& stream, size_t nrBytesToWrite)
{
size_t bytesWritten = 0;
@@ -45,7 +45,7 @@ size_t LogStreamWriter::write(Stream& stream, size_t nrBytesToWrite)
return bytesWritten;
}
size_t LogStreamWriter::write_single_item(Stream& stream,
size_t LogStreamWriter::write_single_item(Print& stream,
size_t nrBytesToWrite)
{
const size_t res = write_item(stream, nrBytesToWrite);
@@ -54,7 +54,7 @@ size_t LogStreamWriter::write_single_item(Stream& stream,
return res;
}
size_t LogStreamWriter::write_item(Stream& stream,
size_t LogStreamWriter::write_item(Print& stream,
size_t nrBytesToWrite)
{
size_t bytesWritten = 0;
@@ -103,7 +103,7 @@ size_t LogStreamWriter::write_item(Stream& stream,
++_readpos;
} else {
if ((bytesWritten + 2) > nrBytesToWrite) { return bytesWritten; }
bytesWritten += stream.println();
bytesWritten += stream.print(F("\r\n")); // stream.println();
// Done with entry, cleanup and leave
clear();
@@ -113,6 +113,6 @@ size_t LogStreamWriter::write_item(Stream& stream,
return bytesWritten;
}
size_t LogStreamWriter::write_skipping(Stream& stream) { return 0; }
size_t LogStreamWriter::write_skipping(Print& stream) { return 0; }
void LogStreamWriter::prepare_prefix() {}
+5 -5
View File
@@ -11,7 +11,7 @@ public:
virtual ~LogStreamWriter() {}
virtual bool process(Stream* stream, size_t availableForWrite);
virtual bool process(Print* stream, size_t availableForWrite);
// Only use this from derived classes, as we need a Stream to further process
virtual bool process();
@@ -24,20 +24,20 @@ protected:
// Write continuously until either nrBytesToWrite was reached or no new messages were available to process.
// @retval Number of bytes written. Zero when no new message was available to process.
virtual size_t write(Stream& stream,
virtual size_t write(Print& stream,
size_t nrBytesToWrite);
// Write single item and clear() on return.
// This way each call starts with a new item and long messages may get truncated based on nrBytesToWrite
// @retval Number of bytes written. Zero when no new message was available to process.
virtual size_t write_single_item(Stream& stream,
virtual size_t write_single_item(Print& stream,
size_t nrBytesToWrite);
virtual size_t write_item(Stream& stream,
virtual size_t write_item(Print& stream,
size_t nrBytesToWrite);
virtual size_t write_skipping(Stream& stream);
virtual size_t write_skipping(Print& stream);
virtual void prepare_prefix();
+2 -6
View File
@@ -34,14 +34,10 @@ String SerialWriteBuffer_t::colorize(const String& str) const {
}
size_t SerialWriteBuffer_t::write_skipping(Stream& stream)
size_t SerialWriteBuffer_t::write_skipping(Print& stream)
{
size_t bytesWritten{};
// Mark with empty line we skipped the rest of the message.
bytesWritten += stream.println(F(" ..."));
bytesWritten += stream.println();
return bytesWritten;
return stream.print(F(" ...\r\n\r\n"));
}
void SerialWriteBuffer_t::prepare_prefix()
+1 -1
View File
@@ -14,7 +14,7 @@ public:
private:
size_t write_skipping(Stream& stream) override;
size_t write_skipping(Print& stream) override;
void prepare_prefix() override;
+9 -24
View File
@@ -480,13 +480,13 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue)
{
return KeyValueStruct(F("Show Unit of Measure"), Settings.ShowUnitOfMeasureOnDevicesPage());
}
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
#if FEATURE_MQTT_CONNECT_BACKGROUND
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
#if FEATURE_MQTT_CONNECT_BACKGROUND
case LabelType::MQTT_CONNECT_IN_BACKGROUND:
{
return KeyValueStruct(F("MQTT Connect in background"), Settings.MQTTConnectInBackground());
}
#endif // if FEATURE_MQTT_CONNECT_BACKGROUND
#endif // if FEATURE_MQTT_CONNECT_BACKGROUND
#if CONFIG_SOC_WIFI_SUPPORT_5G
case LabelType::WIFI_BAND_MODE:
@@ -544,7 +544,9 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue)
}
KeyValueStruct kv(F("RSSI"), WiFi.RSSI());
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
KV_SETUNIT(UOM_dBm);
if (!extendedValue) {
KV_SETUNIT(UOM_dBm);
}
#endif
return kv;
}
@@ -1025,11 +1027,11 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue)
KeyValueStruct kv(F("Sketch Size"), str);
KV_SETID(F("sketch_size"));
if (!extendedValue) {
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
if (!extendedValue) {
KV_SETUNIT(UOM_kB);
#endif
}
#endif
return kv;
}
case LabelType::SKETCH_FREE:
@@ -1043,18 +1045,6 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue)
}
case LabelType::FS_SIZE:
{
String size;
if (extendedValue) {
size = strformat(
F("%d [kB] (%d kB free)"),
SpiffsTotalBytes() / 1024,
SpiffsFreeSpace() / 1024);
}
else {
size = (SpiffsTotalBytes() >> 10);
}
KeyValueStruct kv(
#ifdef USE_LITTLEFS
F("Little FS Size"),
@@ -1063,15 +1053,10 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue)
#endif // ifdef USE_LITTLEFS
SpiffsTotalBytes() >> 10);
KV_SETID(F("fs_size"));
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
KV_SETUNIT(UOM_kB);
#endif
if (!extendedValue) {
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
KV_SETUNIT(UOM_kB);
#endif
}
return kv;
}
case LabelType::FS_FREE:
+3 -1
View File
@@ -239,7 +239,9 @@ String serializeDomoticzJson(struct EventStruct *event)
}
} else {
writer.write({ F("nvalue"), F("0") });
writer.write({ F("svalue"), formatDomoticzSensorType(event) });
writer.write({ F("svalue"),
formatDomoticzSensorType(event),
KeyValueStruct::Format::PreFormatted });
}
}
@@ -12,6 +12,8 @@
#include "../WebServer/ESPEasy_WebServer.h"
#include "../WebServer/Markup.h"
#include "../WebServer/Markup_Forms.h"
#include "../ESPEasyCore/Controller.h"
#include "../DataStructs/ControllerSettingsStruct.h"
/*********************************************************************************************\
* Functions to load and store controller settings on the web page.
+78 -25
View File
@@ -341,12 +341,12 @@ static const char jsClipboardCopyPart2[] PROGMEM = {
// "if(t==null){i= max_loop + 1;"
"if(t==null){i=101;"
"}else{"
"cb+=t.innerHTML.replace(/<[Bb][Rr]\\s*\\/?>/gim,'\\n')+'"
"const visibleLines=Array.from(t.children).filter((e=>null!==e.offsetParent)).map((e=>e.innerHTML));visibleLines.length>0&&(cb+=visibleLines.join('\\n').replace(/<[Bb][Rr]\\s*\\/?>/gim,'\\n')+'"
};
//Fix HTML
static const char jsClipboardCopyPart3[] PROGMEM = {
"';}}"
"');}}"
"cb=cb.replace(/<\\/[Dd][Ii][Vv]\\s*\\/?>/gim,'\\n');"
"cb=cb.replace(/<[^>]*>/gim,'');"
"var ti=document.createElement('textarea');"
@@ -381,38 +381,91 @@ static const char jsSplitMultipleFields[] PROGMEM = {
#ifdef WEBSERVER_INCLUDE_JS
static const char DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS[] PROGMEM = {
"function elId(e){return document.getElementById(e)}"
"function loopDeLoop(e,s){var o,a,n=0;isNaN(s)&&(s=1),null==e&&(e=1e3),"
"i=setInterval(function(){if(n>0){clearInterval(i);return}"
"++s>1||fetch('/json?view=sensorupdate').then(function(s){var n;if(200!==s.status){console.log('Looks like there was a problem. Status Code: '+s.status);return}"
"s.json().then(function(s){e=s.TTL;"
"loopDeLoop(1000);"
"const SENSOR_URL='/json?view=sensorupdate';"
"function loopDeLoop(e=1000){setTimeout(function(){fetch(SENSOR_URL)"
".then(e=>{if(!e.ok)throw new Error(e.status);return e.json()})"
".then(o=>{e=o.TTL||e;"
#if (defined(FEATURE_TASKVALUE_UNIT_OF_MEASURE) && FEATURE_TASKVALUE_UNIT_OF_MEASURE) || (defined(FEATURE_STRING_VARIABLES) && FEATURE_STRING_VARIABLES)
"var r=void 0===s.ShowUoM||s.ShowUoM&&'false'!==s.ShowUoM;"
"const n=void 0===o.ShowUoM||o.ShowUoM&&'false'!==o.ShowUoM;"
#endif // if (defined(FEATURE_TASKVALUE_UNIT_OF_MEASURE) && FEATURE_TASKVALUE_UNIT_OF_MEASURE) || (defined(FEATURE_STRING_VARIABLES) && FEATURE_STRING_VARIABLES)
"for(o=0;o<s.Sensors.length;o++)if(s.Sensors[o].hasOwnProperty('TaskValues'))"
"for(a=0;a<s.Sensors[o].TaskValues.length;a++)try{n=s.Sensors[o].TaskValues[a].Value}catch(l){n=l.name}"
"finally{if('TypeError'!==n){var u=s.Sensors[o].TaskValues[a].Value,t=s.Sensors[o].TaskValues[a].NrDecimals;t<255&&(u=parseFloat(u).toFixed(t));"
"for(let i=0;i<o.Sensors.length;i++){const t=o.Sensors[i];"
"if(t.TaskValues)for(let j=0;j<t.TaskValues.length;j++){const r=t.TaskValues[j];"
"if(!r||null==r.Value)continue;"
"let s=r.Value;const d=r.NrDecimals;"
"d<255&&(s=parseFloat(s).toFixed(d));"
#if (defined(FEATURE_TASKVALUE_UNIT_OF_MEASURE) && FEATURE_TASKVALUE_UNIT_OF_MEASURE) || (defined(FEATURE_STRING_VARIABLES) && FEATURE_STRING_VARIABLES)
"var $=s.Sensors[o].TaskValues[a].UoM,T=s.Sensors[o].TaskValues[a].Presentation;T?u=T:$&&r&&(u+=' '+$);"
"r.Presentation?s=r.Presentation:"
"r.UoM&&n&&(s+=' '+r.UoM);"
#endif // if (defined(FEATURE_TASKVALUE_UNIT_OF_MEASURE) && FEATURE_TASKVALUE_UNIT_OF_MEASURE) || (defined(FEATURE_STRING_VARIABLES) && FEATURE_STRING_VARIABLES)
"var S='value_'+(s.Sensors[o].TaskNumber-1)+'_'+(s.Sensors[o].TaskValues[a].ValueNumber-1),"
"k='valuename_'+(s.Sensors[o].TaskNumber-1)+'_'+(s.Sensors[o].TaskValues[a].ValueNumber-1),"
"V=elId(S),f=elId(k);V&&(V.innerHTML=u),"
"f&&(f.innerHTML=s.Sensors[o].TaskValues[a].Name+':')}}"
"clearInterval(i),loopDeLoop(e,0)})}).catch(function(s){console.log(s.message),e=5e3,clearInterval(i),loopDeLoop(e,0)}),n=1},e)}"
"loopDeLoop(1e3,0);"
"const l=t.TaskNumber-1+'_'+(r.ValueNumber-1),"
"a=elId('value_'+l);"
"a&&(a.innerHTML=s);"
"const u=elId('valuename_'+l);"
"u&&(u.innerHTML=r.Name+':')"
"}}loopDeLoop(e)})"
".catch(e=>{loopDeLoop(5000)})},e)}"
};
#endif // WEBSERVER_INCLUDE_JS
#ifdef WEBSERVER_INCLUDE_JS
static const char DATA_FETCH_AND_PARSE_LOG_JS[] PROGMEM = {
"function elId(e){return document.getElementById(e)}"
"function getBrowser(){var e,o=navigator.userAgent,r=o.match(/(opera|chrome|safari|firefox|msie|trident(?=\\/))\\/?\\s*(\\d+)/i)||[];return/trident/i.test(r[1])?{name:'IE',version:(e=/\brv[ :]+(\\d+)/g.exec(o)||[])[1]||''}:'Chrome'===r[1]&&null!=(e=o.match(/\bOPR|Edge\\/(\\d+)/))?{name:'Opera',version:e[1]}:(r=r[2]?[r[1],r[2]]:[navigator.appName,navigator.appVersion,'-?'],null!=(e=o.match(/version\\/(\\d+)/i))&&r.splice(1,1,e[1]),{name:r[0],version:r[1]})}"
"var browser=getBrowser(),currentBrowser=browser.name+browser.version;textToDisplay=(browser.name=browser.version<12)?'Error: '+currentBrowser+' is not supported! Please try a modern web browser.':'Fetching log entries...',elId('copyText_1').innerHTML=textToDisplay,loopDeLoop(1e3,0);var logLevel=['Unused','Error','Info','Debug','Debug More','Undefined','Undefined','Undefined','Undefined','Debug Dev'];"
"function loopDeLoop(e,o){let r='copyText_1';isNaN(o)&&(o=1),null==e&&(e=1e3),scrolling_type=e<=500?'auto':'smooth';var n,t,i='',l=0,"
"s=setInterval(function(){if(l>0){clearInterval(s);return}++o>1?l=1:fetch('/logjson').then(function(o){if(200!==o.status){console.log('Looks like there was a problem. Status Code: '+o.status);return}o.json()"
".then(function(o){var l;for(null==t&&(t=''),n=0;n<o.Log.nrEntries;++n)try{l=o.Log.Entries[n].timestamp}catch(a){l=a.name}finally{'TypeError'!==l&&(i=o.Log.Entries[n].timestamp,t+='<div class=level_'+o.Log.Entries[n].level+' id='+i+'><font color=\"gray\">'+o.Log.Entries[n].timestamp+':</font> '+o.Log.Entries[n].text+'</div>')}e=o.Log.TTL,''!==t&&('Fetching log entries...'==elId(r).innerHTML&&(elId(r).innerHTML=''),elId(r).innerHTML+=t),t='',!0==(autoscroll_on=elId('autoscroll').checked)&&''!==i&&elId(i).scrollIntoView({behavior:scrolling_type}),elId('current_loglevel').innerHTML='Logging: '+logLevel[o.Log.SettingsWebLogLevel]+' ('+o.Log.SettingsWebLogLevel+')',clearInterval(s),"
"loopDeLoop(e,0)})}).catch(function(o){elId(r).innerHTML+='<div>>> '+o.message+' <<</div>',autoscroll_on=elId('autoscroll').checked,elId(r).scrollTop=elId(r).scrollHeight,e=5e3,clearInterval(s),"
"loopDeLoop(e,0)}),l=1},e)}"
"function elId(e){return document.getElementById(e)}"
"const ct1 = elId('copyText_1');"
"window.fetch?ct1.textContent='Fetching log entries...':"
"ct1.textContent='Error: This browser is not supported. Please use a modern browser.';"
"const logLevel={0:'Unused',1:'Error',2:'Info',3:'Debug',4:'Debug More',9:'Debug Dev'};"
"function loopDeLoop(e=1000){setTimeout(function(){fetch('/logjson')"
".then(e=>{if(!e.ok)throw Error(e.status);return e.json()})"
".then(t=>{let o='';"
"for(let l=0;l<t.Log.nrEntries;l++){let i=t.Log.Entries[l];"
"i&&i.timestamp&&(o+=`<div class=level_${i.level}>"
"<font color='gray'>${i.timestamp}:</font> ${i.text}</div>`)}"
"e=t.Log.TTL||e;"
"o&&(ct1.textContent==='Fetching log entries...'&&(ct1.innerHTML=''),ct1.innerHTML+=o);"
"applyLogFilter();"
"elId('autoscroll')?.checked&&ct1.scrollTo({top:ct1.scrollHeight,behavior:e<=500?'auto':'smooth'});"
"let n=t.Log.SettingsWebLogLevel,r=logLevel[n]?" "?'Undefined';"
"elId('current_loglevel').textContent=`Logging: ${r} (${n})`;"
"loopDeLoop(e)})"
".catch(e=>{ct1.innerHTML+=`<div>>> ${e.message} <<</div>`;"
"ct1.scrollTop=ct1.scrollHeight;loopDeLoop(5000)})},e)}"
"function applyLogFilter(){"
"let e=elId('logfilter').value.split(';').map(e=>e.trim()).filter(Boolean),"
"t=document.querySelectorAll('#copyText_1 > div'),"
"o=e.some(e=>!e.startsWith('!'));"
"for(let l of t){"
"l.dataset.originalHtml||(l.dataset.originalHtml=l.innerHTML);"
"let i=l.dataset.originalHtml,n=l.textContent.toLowerCase(),r=[],s=!1,a=!o;"
"for(let c of e){"
"if(c.startsWith('!')){let g=c.slice(1);"
"if(!g.includes('&')||g.startsWith('&')||g.endsWith('&')){"
"if(g&&n.includes(g.toLowerCase())){s=!0;break}}"
"else{let f=g.split('&').map(e=>e.trim()).filter(Boolean),u=-1,d=!0;"
"for(let p of f){let h=n.indexOf(p.toLowerCase(),u+1);if(h===-1){d=!1;break}u=h}"
"if(d){s=!0;break}}continue}"
"if(c.includes('&')&&!c.startsWith('&')&&!c.endsWith('&')){"
"let L=c.split('&').map(e=>e.trim()).filter(Boolean);"
"if(L.length>=2){let _=-1,$=!0;"
"for(let b of L){let m=n.indexOf(b.toLowerCase(),_+1);if(m===-1){$=!1;break}_=m}"
"if($){r.push(...L),a=!0;break}}continue}"
"if(n.includes(c.toLowerCase())){r.push(c),a=!0;break}}"
"if(s||!a){l.style.display='none';continue}"
"l.style.display='';l.innerHTML=highlightOutsideTags(i,r)}}"
"function highlightOutsideTags(e,t){if(!t.length)return e;"
"let o='',l=!1,i=0;"
"for(;i<e.length;){let n=e[i];"
"if('<'===n&&(l=!0),'>'===n){l=!1;o+=n;i++;continue}"
"if(!l){let r=!1;"
"for(let s of t){let a=e.slice(i,i+s.length);"
"if(a.toLowerCase()===s.toLowerCase()){"
"o+=`<span style='background-color: #bb9300;color: black;'>${a}</span>`;"
"i+=s.length;r=!0;break}}"
"if(r)continue}"
"o+=n;i++}return o}"
"loopDeLoop(1000);"
"const input=document.getElementById('logfilter');"
"input&&(input.placeholder='\"!\"=exclude \";\"=or \"&\"=and (e.g. !act)');"
};
#endif // WEBSERVER_INCLUDE_JS
+3 -10
View File
@@ -1409,11 +1409,11 @@ void devicePage_show_serial_config(taskIndex_t taskIndex)
void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex)
{
if (Device[DeviceIndex].isSPI()
&& !(Settings.isSPI_valid(0u) || (getSPIBusCount() > 1 && Settings.isSPI_valid(1u)))) {
&& Settings.getNrConfiguredSPI_buses() == 0) {
addFormNote(F("SPI Bus not configured yet (Hardware page)."));
}
#ifdef ESP32
if (Device[DeviceIndex].SpiBusSelect && getSPIBusCount() > 1 && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) {
if (Device[DeviceIndex].SpiBusSelect && getSPIBusCount() > 1 && (Settings.getNrConfiguredSPI_buses() != 0)) {
uint8_t spiBus = Settings.getSPIBusForTask(taskIndex);
SPIInterfaceSelector(F("SPI Bus"),
F("pspibus"),
@@ -1428,14 +1428,7 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex
addFormSubHeader(F("I2C options"));
if (!Settings.isI2CEnabled(0)
# if FEATURE_I2C_MULTIPLE
&& (getI2CBusCount() > 1 && !Settings.isI2CEnabled(1))
# if FEATURE_I2C_INTERFACE_3
&& (getI2CBusCount() > 2 && !Settings.isI2CEnabled(2))
# endif // if FEATURE_I2C_INTERFACE_3
# endif // if FEATURE_I2C_MULTIPLE
) {
if (Settings.getNrConfiguredI2C_buses() == 0) {
addFormNote(F("I2C Bus is not configured yet (Hardware page)."));
}
+4
View File
@@ -16,6 +16,7 @@
#include "../WebServer/FileList.h"
#include "../WebServer/HTML_wrappers.h"
#include "../WebServer/HardwarePage.h"
#include "../WebServer/InterfacesPage.h"
#include "../WebServer/I2C_Scanner.h"
#include "../WebServer/JSON.h"
#include "../WebServer/LoadFromFS.h"
@@ -272,6 +273,9 @@ void WebServerInit()
#ifdef WEBSERVER_HARDWARE
web_server.on(F("/hardware"), handle_hardware);
#endif // ifdef WEBSERVER_HARDWARE
#ifdef WEBSERVER_INTERFACES
web_server.on(F("/interfaces"), handle_interfaces);
#endif
#ifdef WEBSERVER_I2C_SCANNER
web_server.on(F("/i2cscanner"), handle_i2cscanner);
#endif // ifdef WEBSERVER_I2C_SCANNER
+6 -284
View File
@@ -16,16 +16,10 @@
#include "../Helpers/ESPEasy_Storage.h"
#include "../Helpers/Hardware_GPIO.h"
#include "../Helpers/Hardware_I2C.h"
#include "../Helpers/Hardware_SPI.h"
#include "../Helpers/SPI_Helper.h"
#include "../Helpers/StringConverter.h"
#include "../Helpers/StringGenerator_GPIO.h"
#if FEATURE_I2C_MULTIPLE
#include "../Helpers/I2C_access.h"
#include "../Helpers/Hardware_device_info.h"
#endif // if FEATURE_I2C_MULTIPLE
// ********************************************************************************
// Web Interface hardware page
@@ -45,90 +39,6 @@ void handle_hardware() {
Settings.Pin_status_led = getFormItemInt(F("pled"));
Settings.Pin_status_led_Inversed = isFormItemChecked(F("pledi"));
Settings.Pin_Reset = getFormItemInt(F("pres"));
#if FEATURE_PLUGIN_PRIORITY
if (!isI2CPriorityTaskActive(0))
#endif //if FEATURE_PLUGIN_PRIORITY
{
update_whenset_FormItemInt(F("psda0"), Settings.Pin_i2c_sda);
update_whenset_FormItemInt(F("pscl0"), Settings.Pin_i2c_scl);
}
Settings.I2C_clockSpeed = getFormItemInt(F("pi2csp0"), DEFAULT_I2C_CLOCK_SPEED);
Settings.I2C_clockSpeed_Slow = getFormItemInt(F("pi2cspslow0"), DEFAULT_I2C_CLOCK_SPEED_SLOW);
Settings.WireClockStretchLimit = getFormItemInt(F("wirestretch"));
#if FEATURE_I2CMULTIPLEXER
Settings.I2C_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype0"));
if (Settings.I2C_Multiplexer_Type != I2C_MULTIPLEXER_NONE) {
Settings.I2C_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr0"));
} else {
Settings.I2C_Multiplexer_Addr = -1;
}
Settings.I2C_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset0"));
#endif // if FEATURE_I2CMULTIPLEXER
#if FEATURE_I2C_MULTIPLE // No loop used here, to avoid adding setters to the SettingsStruct template code
if (getI2CBusCount() > 1) {
#if FEATURE_PLUGIN_PRIORITY
if (!isI2CPriorityTaskActive(1))
#endif //if FEATURE_PLUGIN_PRIORITY
{
update_whenset_FormItemInt(F("psda1"), Settings.Pin_i2c2_sda);
update_whenset_FormItemInt(F("pscl1"), Settings.Pin_i2c2_scl);
}
Settings.I2C2_clockSpeed = getFormItemInt(F("pi2csp1"), DEFAULT_I2C_CLOCK_SPEED);
Settings.I2C2_clockSpeed_Slow = getFormItemInt(F("pi2cspslow1"), DEFAULT_I2C_CLOCK_SPEED_SLOW);
#if FEATURE_I2CMULTIPLEXER
Settings.I2C2_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype1"));
if (Settings.I2C2_Multiplexer_Type != I2C_MULTIPLEXER_NONE) {
Settings.I2C2_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr1"));
} else {
Settings.I2C2_Multiplexer_Addr = -1;
}
Settings.I2C2_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset1"));
#endif // if FEATURE_I2CMULTIPLEXER
}
#if FEATURE_I2C_INTERFACE_3
if (getI2CBusCount() > 2) {
#if FEATURE_PLUGIN_PRIORITY
if (!isI2CPriorityTaskActive(2))
#endif //if FEATURE_PLUGIN_PRIORITY
{
update_whenset_FormItemInt(F("psda2"), Settings.Pin_i2c3_sda);
update_whenset_FormItemInt(F("pscl2"), Settings.Pin_i2c3_scl);
}
Settings.I2C3_clockSpeed = getFormItemInt(F("pi2csp2"), DEFAULT_I2C_CLOCK_SPEED);
Settings.I2C3_clockSpeed_Slow = getFormItemInt(F("pi2cspslow2"), DEFAULT_I2C_CLOCK_SPEED_SLOW);
#if FEATURE_I2CMULTIPLEXER
Settings.I2C3_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype2"));
if (Settings.I2C3_Multiplexer_Type != I2C_MULTIPLEXER_NONE) {
Settings.I2C3_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr2"));
} else {
Settings.I2C3_Multiplexer_Addr = -1;
}
Settings.I2C3_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset2"));
#endif // if FEATURE_I2CMULTIPLEXER
}
#endif // if FEATURE_I2C_INTERFACE_3
set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf")));
#endif // if FEATURE_I2C_MULTIPLE
#ifdef ESP32
Settings.InitSPI = getFormItemInt(F("initspi0"), static_cast<int>(SPI_Options_e::None));
// User-defined SPI bus 0 GPIO pins
Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk0"), -1);
Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso0"), -1);
Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi0"), -1);
Settings.InitSPI1 = getFormItemInt(F("initspi1"), static_cast<int>(SPI_Options_e::None));
// User-defined SPI bus 1 GPIO pins
Settings.SPI1_SCLK_pin = getFormItemInt(F("spipinsclk1"), -1);
Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1);
Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1);
for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) {
if (Settings.isSPI_enabled(spi_bus) && !Settings.isSPI_valid(spi_bus)) { // Checks
error += strformat(F("SPI bus %u pins not configured correctly!<BR>"), spi_bus);
}
}
#else //for ESP8266 we keep the old UI
Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init
#endif
#if defined(ESP32) && FEATURE_SD
Settings.setSPIBusForSDCard(getFormItemInt(F("sdspibus"), 0));
#endif // if defined(ESP32) && FEATURE_SD
@@ -149,12 +59,6 @@ void handle_hardware() {
}
error += SaveSettings();
addHtmlError(error);
if (error.isEmpty()) {
// Apply I2C settings.
initI2C();
// Apply SPI settings
initializeSPIBuses();
}
}
addHtml(F("<form method='post'>"));
@@ -164,180 +68,20 @@ void handle_hardware() {
addFormSubHeader(F("Wifi Status LED"));
addFormPinSelect(PinSelectPurpose::Status_led, formatGpioName_output(F("LED")), F("pled"), Settings.Pin_status_led);
addFormCheckBox(F("Inversed LED"), F("pledi"), Settings.Pin_status_led_Inversed);
addFormNote(F("Use &rsquo;GPIO-2 (D4)&rsquo; with &rsquo;Inversed&rsquo; checked for onboard LED"));
addFormNote(F("Use &rsquo;GPIO-2"
#ifdef ESP8266
" (D4)"
#endif
"&rsquo; with &rsquo;Inversed&rsquo; checked for onboard LED"));
addFormSubHeader(F("Reset Pin"));
addFormPinSelect(PinSelectPurpose::Reset_pin, formatGpioName_input(F("Switch")), F("pres"), Settings.Pin_Reset);
addFormNote(F("Press about 10s for factory reset"));
#if FEATURE_I2CMULTIPLEXER
const __FlashStringHelper *i2c_muxtype_options[] = {
F("- None -"),
F("TCA9548a - 8 channel"),
F("TCA9546a - 4 channel"),
F("TCA9543a - 2 channel"),
F("PCA9540 - 2 channel (experimental)")
};
const int i2c_muxtype_choices[] = {
I2C_MULTIPLEXER_NONE,
I2C_MULTIPLEXER_TCA9548A,
I2C_MULTIPLEXER_TCA9546A,
I2C_MULTIPLEXER_TCA9543A,
I2C_MULTIPLEXER_PCA9540
};
const FormSelectorOptions muxSelector(NR_ELEMENTS(i2c_muxtype_choices),
i2c_muxtype_options, i2c_muxtype_choices);
// Select the I2C address for a port multiplexer
String i2c_mux_options[9];
int i2c_mux_choices[9];
uint8_t mux_opt = 0;
i2c_mux_options[mux_opt] = F("- None -");
i2c_mux_choices[mux_opt] = I2C_MULTIPLEXER_NONE;
mux_opt++;
for (int8_t x = 0; x < 8; x++) {
i2c_mux_options[mux_opt] = formatToHex_decimal(0x70 + x);
if (x == 0) { // PCA9540 has a fixed address 0f 0x70
i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a, PCA9540]");
} else if (x < 4) {
i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a]");
} else {
i2c_mux_options[mux_opt] += F(" [TCA9546a/8a]");
}
i2c_mux_choices[mux_opt] = 0x70 + x;
mux_opt++;
}
const FormSelectorOptions addrSelector(mux_opt, i2c_mux_options, i2c_mux_choices);
#endif // if FEATURE_I2CMULTIPLEXER
uint8_t i2cBus = 0;
#if FEATURE_I2C_MULTIPLE
for (uint8_t i2cBus = 0; i2cBus < getI2CBusCount(); ++i2cBus)
#endif // if FEATURE_I2C_MULTIPLE
{
#if !FEATURE_I2C_MULTIPLE
addFormSubHeader(F("I2C Bus"));
#else // if !FEATURE_I2C_MULTIPLE
addFormSubHeader(strformat(F("I2C Bus %u"), i2cBus));
#endif // if !FEATURE_I2C_MULTIPLE
#if FEATURE_PLUGIN_PRIORITY
if (isI2CPriorityTaskActive(i2cBus)) {
I2CShowSdaSclReadonly(Settings.getI2CSdaPin(i2cBus), Settings.getI2CSclPin(i2cBus), i2cBus);
} else
#endif // if FEATURE_PLUGIN_PRIORITY
{
addFormPinSelectI2C(formatGpioName_bidirectional(F("SDA")), strformat(F("psda%u"), i2cBus), i2cBus, Settings.getI2CSdaPin(i2cBus));
addFormPinSelectI2C(formatGpioName_output(F("SCL")), strformat(F("pscl%u"), i2cBus), i2cBus, Settings.getI2CSclPin(i2cBus));
}
addFormNumericBox(F("Clock Speed"), strformat(F("pi2csp%u"), i2cBus), Settings.getI2CClockSpeed(i2cBus), 100, 3400000);
addUnit(F("Hz"));
addFormNote(F("Use 100 kHz for old I2C devices, 400 kHz is max for most."));
addFormNumericBox(F("Slow device Clock Speed"), strformat(F("pi2cspslow%u"), i2cBus), Settings.getI2CClockSpeedSlow(i2cBus), 100, 3400000);
addUnit(F("Hz"));
if (0 == i2cBus) { // Only support Clock-stretching on Bus 1
addFormNumericBox(F("I2C ClockStretchLimit"), F("wirestretch"), Settings.WireClockStretchLimit, 0);
#ifdef ESP8266
addUnit(F("usec"));
#endif
#ifdef ESP32
addUnit(F("1/80 usec"));
#endif
}
#if FEATURE_I2CMULTIPLEXER
#if !FEATURE_I2C_MULTIPLE
addFormSubHeader(F("I2C Multiplexer"));
#else // if !FEATURE_I2C_MULTIPLE
addFormSubHeader(strformat(F("I2C Multiplexer %u"), i2cBus));
#endif // if FEATURE_I2C_MULTIPLE
// Select the type of multiplexer to use
{
muxSelector.addFormSelector(F("I2C Multiplexer type"), strformat(F("pi2cmuxtype%u"), i2cBus), Settings.getI2CMultiplexerType(i2cBus));
addrSelector.addFormSelector(F("I2C Multiplexer address"), strformat(F("pi2cmuxaddr%u"), i2cBus), Settings.getI2CMultiplexerAddr(i2cBus));
// addFormPinSelect(PinSelectPurpose::Generic_output, formatGpioName_output_optional(F("Reset")), strformat(F("pi2cmuxreset%u"), i2cBus), Settings.getI2CMultiplexerResetPin(i2cBus));
const String id = strformat(F("pi2cmuxreset%u"), i2cBus);
addRowLabel_tr_id(formatGpioName_output_optional(F("Reset")), id);
addPinSelect(PinSelectPurpose::Generic_output, id, Settings.getI2CMultiplexerResetPin(i2cBus));
addFormNote(F("Will be pulled low to force a reset. Reset is not available on PCA9540."));
}
#endif // if FEATURE_I2CMULTIPLEXER
}
#if FEATURE_I2C_MULTIPLE
const uint8_t i2cMaxBusCount = (getI2CBusCount() > 1
? ((Settings.isI2CEnabled(1) ? 1 : 0)
# if FEATURE_I2C_INTERFACE_3
+ (Settings.isI2CEnabled(2) ? 1 : 0)
# endif // if FEATURE_I2C_INTERFACE_3
)
: 0) + (Settings.isI2CEnabled(0) ? 1 : 0);
if (i2cMaxBusCount > 1) {
addFormSubHeader(F("PCF &amp; MCP Direct I/O"));
const uint8_t i2cBus = Settings.getI2CInterfacePCFMCP();
I2CInterfaceSelector(F("I2C Bus"),
F("pi2cbuspcf"),
i2cBus,
false);
}
#endif // if FEATURE_I2C_MULTIPLE
#ifdef ESP32
for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) {
// SPI Init
addFormSubHeader(concat(F("SPI Bus "), spi_bus));
{
// Script to show GPIO pins for User-defined SPI GPIOs
// html_add_script(F("function spiOptionChanged(elem) {var spipinstyle = elem.value == 9 ? '' : 'none';document.getElementById('tr_spipinsclk').style.display = spipinstyle;document.getElementById('tr_spipinmiso').style.display = spipinstyle;document.getElementById('tr_spipinmosi').style.display = spipinstyle;}"),
// Minified:
html_add_script(strformat(F("function spi%uOptionChanged(e){var i=9==e.value?'':'none';"
"document.getElementById('tr_spipinsclk%u').style.display=i,"
"document.getElementById('tr_spipinmiso%u').style.display=i,"
"document.getElementById('tr_spipinmosi%u').style.display=i"
"}"), spi_bus, spi_bus, spi_bus, spi_bus, spi_bus),
false);
const __FlashStringHelper * spi_options[] = {
getSPI_optionToString(SPI_Options_e::None),
getSPI_optionToString(SPI_Options_e::Vspi_Fspi),
#ifdef ESP32_CLASSIC
getSPI_optionToString(SPI_Options_e::Hspi),
#endif
getSPI_optionToString(SPI_Options_e::UserDefined)};
const int spi_index[] = {
static_cast<int>(SPI_Options_e::None),
static_cast<int>(SPI_Options_e::Vspi_Fspi),
#ifdef ESP32_CLASSIC
static_cast<int>(SPI_Options_e::Hspi),
#endif
static_cast<int>(SPI_Options_e::UserDefined)
};
constexpr size_t nrOptions = NR_ELEMENTS(spi_index);
FormSelectorOptions selector(nrOptions, spi_options, spi_index);
selector.onChangeCall = strformat(F("spi%uOptionChanged(this)"), spi_bus);
selector.addFormSelector(concat(F("Init SPI Bus "), spi_bus), concat(F("initspi"), spi_bus), 0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1);
int8_t spi_gpio[3];
Settings.getSPI_pins(spi_gpio, spi_bus, true); // Load GPIO pins even if wrongly configured
// User-defined pins
addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("CLK")), concat(F("spipinsclk"), spi_bus), spi_gpio[0]);
addFormPinSelect(PinSelectPurpose::SPI_MISO, formatGpioName_input(F("MISO")), concat(F("spipinmiso"), spi_bus), spi_gpio[1]);
addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("MOSI")), concat(F("spipinmosi"), spi_bus), spi_gpio[2]);
html_add_script(strformat(F("document.getElementById('initspi%u').onchange();"), spi_bus), false); // Initial trigger onchange script
// addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!"));
addFormNote(F("Chip Select (CS) config must be done in the plugin"));
}
}
#else //for ESP8266 we keep the existing UI
addFormSubHeader(F("SPI Bus 0"));
addFormCheckBox(F("Init SPI"), F("initspi"), Settings.InitSPI > static_cast<int>(SPI_Options_e::None));
addFormNote(F("CLK=GPIO-14 (D5), MISO=GPIO-12 (D6), MOSI=GPIO-13 (D7)"));
addFormNote(F("Chip Select (CS) config must be done in the plugin"));
#endif // ifdef ESP32
#if FEATURE_SD
addFormSubHeader(F("SD Card"));
#ifdef ESP32
if (getSPIBusCount() > 1 && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) {
if (getSPIBusCount() > 1 && (Settings.getNrConfiguredSPI_buses() != 0)) {
uint8_t spiBus = Settings.getSPIBusForSDCard();
SPIInterfaceSelector(F("SPI Bus"),
F("sdspibus"),
@@ -365,26 +109,4 @@ void handle_hardware() {
TXBuffer.endStream();
}
#if FEATURE_PLUGIN_PRIORITY
bool isI2CPriorityTaskActive(uint8_t i2cBus) {
bool hasI2CPriorityTask = false;
for (taskIndex_t taskIndex = 0; taskIndex < TASKS_MAX && !hasI2CPriorityTask; taskIndex++) {
hasI2CPriorityTask |= isPluginI2CPowerManager_from_TaskIndex(taskIndex, i2cBus);
}
return hasI2CPriorityTask;
}
void I2CShowSdaSclReadonly(int8_t i2c_sda, int8_t i2c_scl, uint8_t i2cBus) {
int pinnr = -1;
bool input, output, warning = false;
addFormNote(strformat(F("I2C (%d) GPIO pins can't be changed when an I2C Priority task is configured."), i2cBus));
addRowLabel(formatGpioName_bidirectional(F("SDA")));
getGpioInfo(i2c_sda, pinnr, input, output, warning);
addHtml(createGPIO_label(i2c_sda, pinnr, true, true, false));
addRowLabel(formatGpioName_output(F("SCL")));
getGpioInfo(i2c_scl, pinnr, input, output, warning);
addHtml(createGPIO_label(i2c_scl, pinnr, true, true, false));
}
#endif // if FEATURE_PLUGIN_PRIORITY
#endif // ifdef WEBSERVER_HARDWARE
-6
View File
@@ -10,12 +10,6 @@
// ********************************************************************************
void handle_hardware();
# if FEATURE_PLUGIN_PRIORITY
bool isI2CPriorityTaskActive(uint8_t i2cBus);
void I2CShowSdaSclReadonly(int8_t i2c_sda,
int8_t i2c_scl,
uint8_t i2cBus);
# endif // if FEATURE_PLUGIN_PRIORITY
#endif // ifdef WEBSERVER_HARDWARE
#endif // ifndef WEBSERVER_WEBSERVER_HARDWAREPAGE_H
+412
View File
@@ -0,0 +1,412 @@
#include "../WebServer/InterfacesPage.h"
#ifdef WEBSERVER_INTERFACES
# include "../WebServer/ESPEasy_WebServer.h"
# include "../WebServer/HTML_wrappers.h"
# include "../WebServer/Markup.h"
# include "../WebServer/Markup_Buttons.h"
# include "../WebServer/Markup_Forms.h"
# include "../CustomBuild/ESPEasyLimits.h"
# include "../DataStructs/DeviceStruct.h"
# include "../Globals/Settings.h"
# include "../Helpers/ESPEasy_Storage.h"
# include "../Helpers/Hardware_GPIO.h"
# include "../Helpers/Hardware_I2C.h"
# include "../Helpers/Hardware_SPI.h"
# include "../Helpers/SPI_Helper.h"
# include "../Helpers/StringConverter.h"
# include "../Helpers/StringGenerator_GPIO.h"
# if FEATURE_I2C_MULTIPLE
# include "../Helpers/I2C_access.h"
# include "../Helpers/Hardware_device_info.h"
# endif // if FEATURE_I2C_MULTIPLE
void save_interfaces() {
String error;
bool updated{};
if (isFormItem(F("pi2csp0"))) {
updated = true;
# if FEATURE_PLUGIN_PRIORITY
if (!isI2CPriorityTaskActive(0))
# endif // if FEATURE_PLUGIN_PRIORITY
{
update_whenset_FormItemInt(F("psda0"), Settings.Pin_i2c_sda);
update_whenset_FormItemInt(F("pscl0"), Settings.Pin_i2c_scl);
}
Settings.I2C_clockSpeed = getFormItemInt(F("pi2csp0"), DEFAULT_I2C_CLOCK_SPEED);
Settings.I2C_clockSpeed_Slow = getFormItemInt(F("pi2cspslow0"), DEFAULT_I2C_CLOCK_SPEED_SLOW);
Settings.WireClockStretchLimit = getFormItemInt(F("wirestretch"));
# if FEATURE_I2CMULTIPLEXER
Settings.I2C_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype0"));
if (Settings.I2C_Multiplexer_Type != I2C_MULTIPLEXER_NONE) {
Settings.I2C_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr0"));
} else {
Settings.I2C_Multiplexer_Addr = -1;
}
Settings.I2C_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset0"));
# endif // if FEATURE_I2CMULTIPLEXER
}
# if FEATURE_I2C_MULTIPLE // No loop used here, to avoid adding setters to the SettingsStruct template code
if ((getI2CBusCount() > 1) && isFormItem(F("pi2csp1"))) {
updated = true;
# if FEATURE_PLUGIN_PRIORITY
if (!isI2CPriorityTaskActive(1))
# endif // if FEATURE_PLUGIN_PRIORITY
{
update_whenset_FormItemInt(F("psda1"), Settings.Pin_i2c2_sda);
update_whenset_FormItemInt(F("pscl1"), Settings.Pin_i2c2_scl);
}
Settings.I2C2_clockSpeed = getFormItemInt(F("pi2csp1"), DEFAULT_I2C_CLOCK_SPEED);
Settings.I2C2_clockSpeed_Slow = getFormItemInt(F("pi2cspslow1"), DEFAULT_I2C_CLOCK_SPEED_SLOW);
# if FEATURE_I2CMULTIPLEXER
Settings.I2C2_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype1"));
if (Settings.I2C2_Multiplexer_Type != I2C_MULTIPLEXER_NONE) {
Settings.I2C2_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr1"));
} else {
Settings.I2C2_Multiplexer_Addr = -1;
}
Settings.I2C2_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset1"));
# endif // if FEATURE_I2CMULTIPLEXER
}
# if FEATURE_I2C_INTERFACE_3
if ((getI2CBusCount() > 2) && isFormItem(F("pi2csp2"))) {
updated = true;
# if FEATURE_PLUGIN_PRIORITY
if (!isI2CPriorityTaskActive(2))
# endif // if FEATURE_PLUGIN_PRIORITY
{
update_whenset_FormItemInt(F("psda2"), Settings.Pin_i2c3_sda);
update_whenset_FormItemInt(F("pscl2"), Settings.Pin_i2c3_scl);
}
Settings.I2C3_clockSpeed = getFormItemInt(F("pi2csp2"), DEFAULT_I2C_CLOCK_SPEED);
Settings.I2C3_clockSpeed_Slow = getFormItemInt(F("pi2cspslow2"), DEFAULT_I2C_CLOCK_SPEED_SLOW);
# if FEATURE_I2CMULTIPLEXER
Settings.I2C3_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype2"));
if (Settings.I2C3_Multiplexer_Type != I2C_MULTIPLEXER_NONE) {
Settings.I2C3_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr2"));
} else {
Settings.I2C3_Multiplexer_Addr = -1;
}
Settings.I2C3_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset2"));
# endif // if FEATURE_I2CMULTIPLEXER
}
# endif // if FEATURE_I2C_INTERFACE_3
if (isFormItem(F("pi2cbuspcf"))) {
updated = true;
set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf")));
}
# endif // if FEATURE_I2C_MULTIPLE
{
# ifdef ESP32
bool SPI_updated{};
if (update_whenset_FormItemInt(F("initspi0"), Settings.InitSPI)) {
SPI_updated = true;
// User-defined SPI bus 0 GPIO pins
Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk0"), -1);
Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso0"), -1);
Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi0"), -1);
}
if (update_whenset_FormItemInt(F("initspi1"), Settings.InitSPI1)) {
SPI_updated = true;
// User-defined SPI bus 1 GPIO pins
Settings.SPI1_SCLK_pin = getFormItemInt(F("spipinsclk1"), -1);
Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1);
Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1);
}
if (SPI_updated) {
updated = true;
for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) {
if (Settings.isSPI_enabled(spi_bus) && !Settings.isSPI_valid(spi_bus)) { // Checks
error += strformat(F("SPI bus %u pins not configured correctly!<BR>"), spi_bus);
}
}
}
# else // for ESP8266 we keep the old UI
Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init
updated = true;
# endif // ifdef ESP32
}
if (updated) {
error += SaveSettings();
addHtmlError(error);
if (error.isEmpty()) {
// Apply I2C settings.
initI2C();
// Apply SPI settings
initializeSPIBuses();
}
}
}
// ********************************************************************************
// Web Interface hardware page
// ********************************************************************************
void handle_interfaces() {
# ifndef BUILD_NO_RAM_TRACKER
checkRAM(F("handle_interfaces"));
# endif
if (!isLoggedIn()) { return; }
navMenuIndex = MENU_INDEX_INTERFACES;
TXBuffer.startStream();
sendHeadandTail_stdtemplate(_HEAD);
save_interfaces();
addHtml(F("<form method='post'>"));
html_table_class_normal();
addFormHeader(F("Interfaces Settings"), F(""), F("Interfaces/Interfaces.html"));
# if FEATURE_I2CMULTIPLEXER
const __FlashStringHelper *i2c_muxtype_options[] = {
F("- None -"),
F("TCA9548a - 8 channel"),
F("TCA9546a - 4 channel"),
F("TCA9543a - 2 channel"),
F("PCA9540 - 2 channel (experimental)")
};
const int i2c_muxtype_choices[] = {
I2C_MULTIPLEXER_NONE,
I2C_MULTIPLEXER_TCA9548A,
I2C_MULTIPLEXER_TCA9546A,
I2C_MULTIPLEXER_TCA9543A,
I2C_MULTIPLEXER_PCA9540
};
const FormSelectorOptions muxSelector(NR_ELEMENTS(i2c_muxtype_choices),
i2c_muxtype_options, i2c_muxtype_choices);
// Select the I2C address for a port multiplexer
String i2c_mux_options[9];
int i2c_mux_choices[9];
uint8_t mux_opt = 0;
i2c_mux_options[mux_opt] = F("- None -");
i2c_mux_choices[mux_opt] = I2C_MULTIPLEXER_NONE;
mux_opt++;
for (int8_t x = 0; x < 8; x++) {
i2c_mux_options[mux_opt] = formatToHex_decimal(0x70 + x);
if (x == 0) { // PCA9540 has a fixed address 0f 0x70
i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a, PCA9540]");
} else if (x < 4) {
i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a]");
} else {
i2c_mux_options[mux_opt] += F(" [TCA9546a/8a]");
}
i2c_mux_choices[mux_opt] = 0x70 + x;
mux_opt++;
}
const FormSelectorOptions addrSelector(mux_opt, i2c_mux_options, i2c_mux_choices);
# endif // if FEATURE_I2CMULTIPLEXER
uint8_t i2cBus = 0;
# if FEATURE_I2C_MULTIPLE
for (uint8_t i2cBus = 0; i2cBus < getI2CBusCount(); ++i2cBus)
# endif // if FEATURE_I2C_MULTIPLE
{
# if !FEATURE_I2C_MULTIPLE
addFormSubHeader(F("I2C Bus"));
# else
addFormSubHeader(strformat(F("I2C Bus %u"), i2cBus));
# endif // if !FEATURE_I2C_MULTIPLE
# if FEATURE_PLUGIN_PRIORITY
if (isI2CPriorityTaskActive(i2cBus)) {
I2CShowSdaSclReadonly(Settings.getI2CSdaPin(i2cBus), Settings.getI2CSclPin(i2cBus), i2cBus);
} else
# endif // if FEATURE_PLUGIN_PRIORITY
{
addFormPinSelectI2C(formatGpioName_bidirectional(F("SDA")), strformat(F("psda%u"), i2cBus), i2cBus, Settings.getI2CSdaPin(i2cBus));
addFormPinSelectI2C(formatGpioName_output(F("SCL")), strformat(F("pscl%u"), i2cBus), i2cBus, Settings.getI2CSclPin(i2cBus));
}
addFormNumericBox(F("Clock Speed"), strformat(F("pi2csp%u"), i2cBus), Settings.getI2CClockSpeed(i2cBus), 100, 3400000);
addUnit(F("Hz"));
addFormNote(F("Use 100 kHz for old I2C devices, 400 kHz is max for most."));
addFormNumericBox(F("Slow device Clock Speed"), strformat(F("pi2cspslow%u"), i2cBus), Settings.getI2CClockSpeedSlow(i2cBus), 100,
3400000);
addUnit(F("Hz"));
if (0 == i2cBus) { // Only support Clock-stretching on Bus 1
addFormNumericBox(F("I2C ClockStretchLimit"), F("wirestretch"), Settings.WireClockStretchLimit, 0);
# ifdef ESP8266
addUnit(F("usec"));
# endif
# ifdef ESP32
addUnit(F("1/80 usec"));
# endif
}
# if FEATURE_I2CMULTIPLEXER
# if !FEATURE_I2C_MULTIPLE
addFormSubHeader(F("I2C Multiplexer"));
# else // if !FEATURE_I2C_MULTIPLE
addFormSubHeader(strformat(F("I2C Multiplexer %u"), i2cBus));
# endif // if !FEATURE_I2C_MULTIPLE
// Select the type of multiplexer to use
{
muxSelector.addFormSelector(F("I2C Multiplexer type"), strformat(F("pi2cmuxtype%u"), i2cBus), Settings.getI2CMultiplexerType(i2cBus));
addrSelector.addFormSelector(F("I2C Multiplexer address"), strformat(F("pi2cmuxaddr%u"), i2cBus),
Settings.getI2CMultiplexerAddr(i2cBus));
// addFormPinSelect(PinSelectPurpose::Generic_output, formatGpioName_output_optional(F("Reset")), strformat(F("pi2cmuxreset%u"),
// i2cBus), Settings.getI2CMultiplexerResetPin(i2cBus));
const String id = strformat(F("pi2cmuxreset%u"), i2cBus);
addRowLabel_tr_id(formatGpioName_output_optional(F("Reset")), id);
addPinSelect(PinSelectPurpose::Generic_output, id, Settings.getI2CMultiplexerResetPin(i2cBus));
addFormNote(F("Will be pulled low to force a reset. Reset is not available on PCA9540."));
}
# endif // if FEATURE_I2CMULTIPLEXER
}
# if FEATURE_I2C_MULTIPLE
const uint8_t i2cMaxBusCount = Settings.getNrConfiguredI2C_buses();
if (i2cMaxBusCount > 1) {
addFormSubHeader(F("PCF &amp; MCP Direct I/O"));
const uint8_t i2cBus = Settings.getI2CInterfacePCFMCP();
I2CInterfaceSelector(F("I2C Bus"),
F("pi2cbuspcf"),
i2cBus,
false);
}
# endif // if FEATURE_I2C_MULTIPLE
# ifdef ESP32
for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) {
// SPI Init
addFormSubHeader(concat(F("SPI Bus "), spi_bus));
{
// Script to show GPIO pins for User-defined SPI GPIOs
// html_add_script(F("function spiOptionChanged(elem) {var spipinstyle = elem.value == 9 ? '' :
// 'none';document.getElementById('tr_spipinsclk').style.display = spipinstyle;document.getElementById('tr_spipinmiso').style.display
// = spipinstyle;document.getElementById('tr_spipinmosi').style.display = spipinstyle;}"),
// Minified:
html_add_script(strformat(F("function spi%uOptionChanged(e){var i=9==e.value?'':'none';"
"document.getElementById('tr_spipinsclk%u').style.display=i,"
"document.getElementById('tr_spipinmiso%u').style.display=i,"
"document.getElementById('tr_spipinmosi%u').style.display=i"
"}"), spi_bus, spi_bus, spi_bus, spi_bus, spi_bus),
false);
const __FlashStringHelper *spi_options[] = {
getSPI_optionToString(SPI_Options_e::None),
getSPI_optionToString(SPI_Options_e::Vspi_Fspi),
# ifdef ESP32_CLASSIC
getSPI_optionToString(SPI_Options_e::Hspi),
# endif
getSPI_optionToString(SPI_Options_e::UserDefined_VSPI)
# if SOC_SPI_PERIPH_NUM > 2
, getSPI_optionToString(SPI_Options_e::UserDefined_HSPI)
# endif
};
const int spi_index[] = {
static_cast<int>(SPI_Options_e::None),
static_cast<int>(SPI_Options_e::Vspi_Fspi),
# ifdef ESP32_CLASSIC
static_cast<int>(SPI_Options_e::Hspi),
# endif
static_cast<int>(SPI_Options_e::UserDefined_VSPI)
# if SOC_SPI_PERIPH_NUM > 2
, static_cast<int>(SPI_Options_e::UserDefined_HSPI)
# endif
};
constexpr size_t nrOptions = NR_ELEMENTS(spi_index);
FormSelectorOptions selector(nrOptions, spi_options, spi_index);
selector.onChangeCall = strformat(F("spi%uOptionChanged(this)"), spi_bus);
selector.addFormSelector(concat(F("Init SPI Bus "), spi_bus),
concat(F("initspi"), spi_bus),
0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1);
int8_t spi_gpio[3];
Settings.getSPI_pins(spi_gpio, spi_bus, true); // Load GPIO pins even if wrongly configured
// User-defined pins
addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("CLK")), concat(F("spipinsclk"), spi_bus), spi_gpio[0]);
addFormPinSelect(PinSelectPurpose::SPI_MISO, formatGpioName_input(F("MISO")), concat(F("spipinmiso"), spi_bus), spi_gpio[1]);
addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("MOSI")), concat(F("spipinmosi"), spi_bus), spi_gpio[2]);
html_add_script(strformat(F("document.getElementById('initspi%u').onchange();"), spi_bus), false); // Initial trigger onchange script
// addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!"));
addFormNote(F("Chip Select (CS) config must be done in the plugin"));
}
}
# else // for ESP8266 we keep the existing UI
addFormSubHeader(F("SPI Bus 0"));
addFormCheckBox(F("Init SPI"), F("initspi"), Settings.InitSPI > static_cast<int>(SPI_Options_e::None));
addFormNote(F("CLK=GPIO-14 (D5), MISO=GPIO-12 (D6), MOSI=GPIO-13 (D7)"));
addFormNote(F("Chip Select (CS) config must be done in the plugin"));
# endif // ifdef ESP32
addFormSeparator(2);
html_TR_TD();
html_TD();
addSubmitButton();
html_TR_TD();
html_end_table();
html_end_form();
sendHeadandTail_stdtemplate(_TAIL);
TXBuffer.endStream();
}
# if FEATURE_PLUGIN_PRIORITY
bool isI2CPriorityTaskActive(uint8_t i2cBus) {
bool hasI2CPriorityTask = false;
for (taskIndex_t taskIndex = 0; taskIndex < TASKS_MAX && !hasI2CPriorityTask; taskIndex++) {
hasI2CPriorityTask |= isPluginI2CPowerManager_from_TaskIndex(taskIndex, i2cBus);
}
return hasI2CPriorityTask;
}
void I2CShowSdaSclReadonly(int8_t i2c_sda, int8_t i2c_scl, uint8_t i2cBus) {
int pinnr = -1;
bool input, output, warning = false;
addFormNote(strformat(F("I2C (%d) GPIO pins can't be changed when an I2C Priority task is configured."), i2cBus));
addRowLabel(formatGpioName_bidirectional(F("SDA")));
getGpioInfo(i2c_sda, pinnr, input, output, warning);
addHtml(createGPIO_label(i2c_sda, pinnr, true, true, false));
addRowLabel(formatGpioName_output(F("SCL")));
getGpioInfo(i2c_scl, pinnr, input, output, warning);
addHtml(createGPIO_label(i2c_scl, pinnr, true, true, false));
}
# endif // if FEATURE_PLUGIN_PRIORITY
#endif // ifdef WEBSERVER_INTERFACES
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include "../WebServer/common.h"
#ifdef WEBSERVER_INTERFACES
// ********************************************************************************
// Web Interface hardware page
// ********************************************************************************
void handle_interfaces();
# if FEATURE_PLUGIN_PRIORITY
bool isI2CPriorityTaskActive(uint8_t i2cBus);
void I2CShowSdaSclReadonly(int8_t i2c_sda,
int8_t i2c_scl,
uint8_t i2cBus);
# endif
#endif
+1
View File
@@ -247,6 +247,7 @@ void handle_json()
LabelType::FLASH_CHIP_SPEED,
LabelType::FLASH_IDE_MODE,
LabelType::FS_SIZE,
LabelType::FS_FREE,
LabelType::SUNRISE,
LabelType::SUNSET,
+2
View File
@@ -6,6 +6,7 @@
#include "../WebServer/JSON.h"
#include "../WebServer/Markup.h"
#include "../WebServer/Markup_Buttons.h"
#include "../WebServer/Markup_Forms.h"
#include "../DataStructs/LogBuffer.h"
#include "../DataStructs/TimingStats.h"
@@ -35,6 +36,7 @@ void handle_log() {
"</TR></table><div id='current_loglevel' style='font-weight: bold;'>Logging: </div><div class='logviewer' id='copyText_1'></div>"));
addHtml(F("Autoscroll: "));
addCheckBox(F("autoscroll"), true);
addFormTextBox(F("Filter"), F("logfilter"), "", 30);
addHtml(F("<BR></body>"));
serve_JS(JSfiles_e::FetchAndParseLog);
+29
View File
@@ -660,6 +660,16 @@ bool getCheckWebserverArg_int(const String& key, int& value) {
return res;
}
bool getCheckWebserverArg_int(const String& key,
uint32_t & value) {
const String valueStr = webArg(key);
if (valueStr.isEmpty()) return false;
uint32_t tmp{};
const bool res = validUIntFromString(valueStr, tmp);
value = tmp;
return res;
}
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
int & value)
{
@@ -676,6 +686,25 @@ bool update_whenset_FormItemInt(const String& key, int& value) {
return false;
}
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
uint32_t & value)
{
return update_whenset_FormItemInt(String(key), value);
}
bool update_whenset_FormItemInt(const String& key,
uint32_t & value)
{
uint32_t tmpVal;
if (getCheckWebserverArg_int(key, tmpVal)) {
value = tmpVal;
return true;
}
return false;
}
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
int8_t& value)
{
+9
View File
@@ -318,12 +318,21 @@ int getFormItemInt(const String& key, int defaultValue);
bool getCheckWebserverArg_int(const String& key,
int & value);
bool getCheckWebserverArg_int(const String& key,
uint32_t & value);
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
int & value);
bool update_whenset_FormItemInt(const String& key,
int & value);
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
uint32_t & value);
bool update_whenset_FormItemInt(const String& key,
uint32_t & value);
bool update_whenset_FormItemInt(const __FlashStringHelper * key,
int8_t & value);
+1
View File
@@ -640,6 +640,7 @@ void handle_sysinfo_Storage() {
LabelType::OTA_2STEP,
# endif // ifdef ESP8266
LabelType::FS_SIZE,
LabelType::FS_FREE,
LabelType::MAX_LABEL
};
+12
View File
@@ -55,6 +55,14 @@
#endif
#endif // ifndef MENU_INDEX_HARDWARE_VISIBLE
#ifndef MENU_INDEX_INTERFACES_VISIBLE
#ifdef WEBSERVER_INTERFACES
# define MENU_INDEX_INTERFACES_VISIBLE true
#else
# define MENU_INDEX_INTERFACES_VISIBLE false
#endif
#endif
#ifndef MENU_INDEX_DEVICES_VISIBLE
#ifdef WEBSERVER_DEVICES
# define MENU_INDEX_DEVICES_VISIBLE true
@@ -107,6 +115,7 @@ const __FlashStringHelper* getGpMenuIcon(uint8_t index) {
case MENU_INDEX_NETWORK: return ICON("&#127760;"); // Alternative &#128423; (not working on Apple)
case MENU_INDEX_CONTROLLERS: return ICON("&#9990;");
case MENU_INDEX_HARDWARE: return ICON("&#9783;");
case MENU_INDEX_INTERFACES: return ICON("&#10057;");
case MENU_INDEX_DEVICES: return ICON("&#10070;");
case MENU_INDEX_RULES: return ICON("&#10740;");
case MENU_INDEX_NOTIFICATIONS: return ICON("&#9993;");
@@ -122,6 +131,7 @@ const __FlashStringHelper* getGpMenuLabel(uint8_t index) {
case MENU_INDEX_NETWORK: return F("Network");
case MENU_INDEX_CONTROLLERS: return F("Controllers");
case MENU_INDEX_HARDWARE: return F("Hardware");
case MENU_INDEX_INTERFACES:return F("Interfaces");
case MENU_INDEX_DEVICES: return F("Devices");
case MENU_INDEX_RULES: return F("Rules");
case MENU_INDEX_NOTIFICATIONS: return F("Notifications");
@@ -137,6 +147,7 @@ const __FlashStringHelper* getGpMenuURL(uint8_t index) {
case MENU_INDEX_NETWORK: return F("/network");
case MENU_INDEX_CONTROLLERS: return F("/controllers");
case MENU_INDEX_HARDWARE: return F("/hardware");
case MENU_INDEX_INTERFACES: return F("/interfaces");
case MENU_INDEX_DEVICES: return F("/devices");
case MENU_INDEX_RULES: return F("/rules");
case MENU_INDEX_NOTIFICATIONS: return F("/notifications");
@@ -152,6 +163,7 @@ bool GpMenuVisible(uint8_t index) {
case MENU_INDEX_NETWORK: return MENU_INDEX_NETWORK_VISIBLE;
case MENU_INDEX_CONTROLLERS: return MENU_INDEX_CONTROLLERS_VISIBLE;
case MENU_INDEX_HARDWARE: return MENU_INDEX_HARDWARE_VISIBLE;
case MENU_INDEX_INTERFACES: return MENU_INDEX_INTERFACES_VISIBLE;
case MENU_INDEX_DEVICES: return MENU_INDEX_DEVICES_VISIBLE;
case MENU_INDEX_RULES: return MENU_INDEX_RULES_VISIBLE;
case MENU_INDEX_NOTIFICATIONS: return MENU_INDEX_NOTIFICATIONS_VISIBLE;
+8 -7
View File
@@ -12,13 +12,14 @@
#define MENU_INDEX_MAIN 0
#define MENU_INDEX_CONFIG 1
#define MENU_INDEX_HARDWARE 2
#define MENU_INDEX_NETWORK 3
#define MENU_INDEX_CONTROLLERS 4
#define MENU_INDEX_BUSES 5
#define MENU_INDEX_DEVICES 6
#define MENU_INDEX_RULES 7
#define MENU_INDEX_NOTIFICATIONS 8
#define MENU_INDEX_TOOLS 9
#define MENU_INDEX_INTERFACES 3
#define MENU_INDEX_NETWORK 4
#define MENU_INDEX_CONTROLLERS 5
#define MENU_INDEX_BUSES 6
#define MENU_INDEX_DEVICES 7
#define MENU_INDEX_RULES 8
#define MENU_INDEX_NOTIFICATIONS 9
#define MENU_INDEX_TOOLS 10
#define MENU_INDEX_SETUP 254
#define MENU_INDEX_CUSTOM_PAGE 255
+1 -1
View File
@@ -457,7 +457,7 @@ textarea,
display: flex;
position: inherit;
top: 54px;
max-width: 960px;
max-width: 1180px;
}
a.menu {
+1 -1
View File
File diff suppressed because one or more lines are too long
+206 -117
View File
@@ -1,124 +1,213 @@
//---------------no need to be included into WebStaticData.h---------------------------------------------
// if no filter input exists, create it
document.getElementById("logfilter") ||
(document.querySelector("section").innerHTML +=
'<label>Filter: <input id="logfilter" size="35"></label>');
//------------------------------------------------------------
function elId(e) {
return document.getElementById(e);
return document.getElementById(e);
}
function getBrowser() {
var ua = navigator.userAgent,
tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
if (/trident/i.test(M[1])) {
tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
return {
name: 'IE',
version: (tem[1] || '')
};
}
if (M[1] === 'Chrome') {
tem = ua.match(/\bOPR|Edge\/(\d+)/);
if (tem != null) {
return {
name: 'Opera',
version: tem[1]
};
}
}
M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
if ((tem = ua.match(/version\/(\d+)/i)) != null) {
M.splice(1, 1, tem[1]);
}
return {
name: M[0],
version: M[1]
};
}
var browser = getBrowser();
var currentBrowser = browser.name + browser.version;
if (browser.name = 'IE' && browser.version < 12) {
textToDisplay = 'Error: ' + currentBrowser + ' is not supported! Please try a modern web browser.'
} else {
textToDisplay = 'Fetching log entries...';
}
elId('copyText_1').innerHTML = textToDisplay;
loopDeLoop(1000, 0);
var logLevel = new Array('Unused', 'Error', 'Info', 'Debug', 'Debug More', 'Undefined', 'Undefined', 'Undefined', 'Undefined', 'Debug Dev');
function loopDeLoop(timeForNext, activeRequests) {
var maximumRequests = 1;
const ct1 = elId('copyText_1');
if (!window.fetch) {
ct1.textContent = 'Error: This browser is not supported. Please use a modern browser.';
} else {
ct1.textContent = 'Fetching log entries...';
}
const logLevel = {
0: 'Unused',
1: 'Error',
2: 'Info',
3: 'Debug',
4: 'Debug More',
9: 'Debug Dev'
};
loopDeLoop(1000);
function loopDeLoop(timeForNext = 1000) {
const url = '/logjson';
const ct1 = 'copyText_1';
if (isNaN(activeRequests)) {
activeRequests = maximumRequests;
}
if (timeForNext == null) {
timeForNext = 1000;
}
if (timeForNext <= 500) {
scrolling_type = 'auto';
} else {
scrolling_type = 'smooth';
}
var c;
var logEntriesChunk;
var currentIDtoScrollTo = '';
var check = 0;
var i = setInterval(function() {
if (check > 0) {
clearInterval(i);
return;
}
++activeRequests;
if (activeRequests > maximumRequests) {
check = 1;
} else {
fetch(url).then(function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' + response.status);
return;
setTimeout(function () {
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(response.status);
}
response.json().then(function(data) {
var logEntry;
if (logEntriesChunk == null) {
logEntriesChunk = '';
}
for (c = 0; c < data.Log.nrEntries; ++c) {
try {
logEntry = data.Log.Entries[c].timestamp;
} catch (err) {
logEntry = err.name;
} finally {
if (logEntry !== "TypeError") {
currentIDtoScrollTo = data.Log.Entries[c].timestamp;
logEntriesChunk += '<div class=level_' + data.Log.Entries[c].level + ' id=' + currentIDtoScrollTo + '><font color="gray">' + data.Log.Entries[c].timestamp + ':</font> ' + data.Log.Entries[c].text + '</div>';
}
}
}
timeForNext = data.Log.TTL;
if (logEntriesChunk !== '') {
if (elId(ct1).innerHTML == 'Fetching log entries...') {
elId(ct1).innerHTML = '';
}
elId(ct1).innerHTML += logEntriesChunk;
}
logEntriesChunk = '';
autoscroll_on = elId('autoscroll').checked;
if (autoscroll_on == true && currentIDtoScrollTo !== '') {
elId(currentIDtoScrollTo).scrollIntoView({
behavior: scrolling_type
});
}
elId('current_loglevel').innerHTML = 'Logging: ' + logLevel[data.Log.SettingsWebLogLevel] + ' (' + data.Log.SettingsWebLogLevel + ')';
clearInterval(i);
loopDeLoop(timeForNext, 0);
return;
})
}).catch(function(err) {
elId(ct1).innerHTML += '<div>>> ' + err.message + ' <<</div>';
autoscroll_on = elId('autoscroll').checked;
elId(ct1).scrollTop = elId(ct1).scrollHeight;
timeForNext = 5000;
clearInterval(i);
loopDeLoop(timeForNext, 0);
return;
return response.json();
})
};
check = 1;
.then(data => {
let logEntriesChunk = '';
for (let c = 0; c < data.Log.nrEntries; c++) {
const entry = data.Log.Entries[c];
if (!entry || !entry.timestamp) continue;
logEntriesChunk +=
`<div class="level_${entry.level}">
<font color="gray">${entry.timestamp}:</font> ${entry.text}
</div>`;
}
timeForNext = data.Log.TTL || timeForNext;
if (logEntriesChunk) {
if (ct1.textContent === 'Fetching log entries...') {
ct1.innerHTML = '';
}
ct1.innerHTML += logEntriesChunk;
}
applyLogFilter();
if (elId('autoscroll')?.checked) {
ct1.scrollTo({
top: ct1.scrollHeight,
behavior: timeForNext <= 500 ? 'auto' : 'smooth'
});
}
const level = data.Log.SettingsWebLogLevel;
const levelName = logLevel[level] ?? 'Undefined';
elId('current_loglevel').textContent =
`Logging: ${levelName} (${level})`;
loopDeLoop(timeForNext);
})
.catch(err => {
ct1.innerHTML += `<div>>> ${err.message} <<</div>`;
ct1.scrollTop = ct1.scrollHeight;
loopDeLoop(5000);
});
}, timeForNext);
}
}
function applyLogFilter() {
const filterGroups = elId('logfilter').value
.split(';').map(s => s.trim()).filter(Boolean);
const entries = document.querySelectorAll('#copyText_1 > div');
const hasPositiveFilter = filterGroups.some(g => !g.startsWith('!'));
for (const entry of entries) {
if (!entry.dataset.originalHtml) entry.dataset.originalHtml = entry.innerHTML;
let html = entry.dataset.originalHtml;
const textLower = entry.textContent.toLowerCase();
let matched = [];
let hiddenByExclusion = false;
let isMatch = !hasPositiveFilter;
for (const group of filterGroups) {
// exclusion rule with ordered AND
if (group.startsWith('!')) {
const g = group.slice(1);
if (g.includes('&') && !g.startsWith('&') && !g.endsWith('&')) {
const parts = g.split('&').map(s => s.trim()).filter(Boolean);
let lastIndex = -1;
let ok = true;
for (const p of parts) {
const idx = textLower.indexOf(p.toLowerCase(), lastIndex + 1);
if (idx === -1) {
ok = false;
break;
}
lastIndex = idx;
}
if (ok) {
hiddenByExclusion = true;
break;
}
} else {
if (g && textLower.includes(g.toLowerCase())) {
hiddenByExclusion = true;
break;
}
}
continue;
}
// ordered AND: a&b&c
if (group.includes('&') && !group.startsWith('&') && !group.endsWith('&')) {
const parts = group.split('&').map(s => s.trim()).filter(Boolean);
if (parts.length >= 2) {
let lastIndex = -1;
let ok = true;
for (const p of parts) {
const idx = textLower.indexOf(p.toLowerCase(), lastIndex + 1);
if (idx === -1) {
ok = false;
break;
}
lastIndex = idx;
}
if (ok) {
matched.push(...parts);
isMatch = true;
break;
}
}
continue;
}
// normal include
if (textLower.includes(group.toLowerCase())) {
matched.push(group);
isMatch = true;
break;
}
}
if (hiddenByExclusion || !isMatch) {
entry.style.display = 'none';
continue;
}
entry.style.display = '';
entry.innerHTML = highlightOutsideTags(html, matched);
}
}
function highlightOutsideTags(html, terms) {
if (!terms.length) return html;
let result = '';
let insideTag = false;
let i = 0;
while (i < html.length) {
const char = html[i];
if (char === '<') insideTag = true;
if (char === '>') {
insideTag = false;
result += char;
i++;
continue;
}
if (!insideTag) {
let matched = false;
for (const term of terms) {
const slice = html.slice(i, i + term.length);
if (slice.toLowerCase() === term.toLowerCase()) {
result += `<span style="background-color: #bb9300;color: black;">${slice}</span>`;
i += term.length;
matched = true;
break;
}
}
if (matched) continue;
}
result += char;
i++;
}
return result;
}
const input = document.getElementById("logfilter");
input && (input.placeholder = '"!"=exclude ";"=or "&"=and (e.g. !act)');
+10 -1
View File
@@ -11,7 +11,16 @@ function setGithubClipboard() {
if (i % 2 == 0) {
separatorSymbol += '\n'
}
clipboard += test.innerHTML.replace(/<[Bb][Rr]\s*\/?>/gim, '\n') + separatorSymbol;
// collect only visible log entry divs
const visibleLines = Array.from(test.children)
.filter(div => div.offsetParent !== null) // visible only
.map(div => div.innerHTML);
if (visibleLines.length > 0) {
clipboard +=
visibleLines.join('\n').replace(/<[Bb][Rr]\s*\/?>/gim, '\n') +
separatorSymbol;
}
}
}
clipboard = clipboard.replace(/<\/[Dd][Ii][Vv]\s*\/?>/gim, '\n');
+54 -78
View File
@@ -1,88 +1,64 @@
loopDeLoop(1000, 0);
loopDeLoop(1000);
function elId(e) {
return document.getElementById(e);
return document.getElementById(e);
}
function loopDeLoop(timeForNext, activeRequests) {
var maximumRequests = 1;
var c;
var k;
var err = '';
var url = '/json?view=sensorupdate';
var check = 0;
if (isNaN(activeRequests)) {
activeRequests = maximumRequests;
}
if (timeForNext == null) {
timeForNext = 1000;
}
i = setInterval(function() {
if (check > 0) {
clearInterval(i);
return;
}
++activeRequests;
if (activeRequests > maximumRequests) {
check = 1;
} else {
fetch(url).then(function(response) {
var valueEntry;
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' + response.status);
return;
const SENSOR_URL = '/json?view=sensorupdate';
function loopDeLoop(timeForNext = 1000) {
setTimeout(function () {
fetch(SENSOR_URL)
.then(response => {
if (!response.ok) {
throw new Error(response.status);
}
response.json().then(function(data) {
timeForNext = data.TTL;
var showUoM = data.ShowUoM === undefined || (data.ShowUoM && data.ShowUoM !== 'false');
for (c = 0; c < data.Sensors.length; c++) {
if (data.Sensors[c].hasOwnProperty('TaskValues')) {
for (k = 0; k < data.Sensors[c].TaskValues.length; k++) {
try {
valueEntry = data.Sensors[c].TaskValues[k].Value;
} catch (err) {
valueEntry = err.name;
} finally {
if (valueEntry !== 'TypeError') {
var tempValue = data.Sensors[c].TaskValues[k].Value;
var decimalsValue = data.Sensors[c].TaskValues[k].NrDecimals;
if (decimalsValue < 255) {
tempValue = parseFloat(tempValue).toFixed(decimalsValue);
}
var tempUoM = data.Sensors[c].TaskValues[k].UoM;
var tempPres = data.Sensors[c].TaskValues[k].Presentation;
if (tempPres) {
tempValue = tempPres;
} else
if (tempUoM && showUoM) {
tempValue += ' ' + tempUoM;
}
var valueID = 'value_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1);
var valueNameID = 'valuename_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1);
var valueElement = elId(valueID);
var valueNameElement = elId(valueNameID);
if (valueElement) {
valueElement.innerHTML = tempValue;
}
if (valueNameElement) {
valueNameElement.innerHTML = data.Sensors[c].TaskValues[k].Name + ':';
}
}
}
}
return response.json();
})
.then(data => {
timeForNext = data.TTL || timeForNext;
const showUoM = data.ShowUoM === undefined || (data.ShowUoM && data.ShowUoM !== 'false');
for (let c = 0; c < data.Sensors.length; c++) {
const sensor = data.Sensors[c];
if (!sensor.TaskValues) continue;
for (let k = 0; k < sensor.TaskValues.length; k++) {
const taskValue = sensor.TaskValues[k];
if (!taskValue || taskValue.Value == null) continue;
let tempValue = taskValue.Value;
const decimals = taskValue.NrDecimals;
if (decimals < 255) {
tempValue = parseFloat(tempValue).toFixed(decimals);
}
if (taskValue.Presentation) {
tempValue = taskValue.Presentation;
} else if (taskValue.UoM && showUoM) {
tempValue += ' ' + taskValue.UoM;
}
const baseId = (sensor.TaskNumber - 1) + '_' + (taskValue.ValueNumber - 1);
const valueEl = elId('value_' + baseId);
if (valueEl) {
valueEl.innerHTML = tempValue;
}
const nameEl = elId('valuename_' + baseId);
if (nameEl) {
nameEl.innerHTML = taskValue.Name + ':';
}
}
clearInterval(i);
loopDeLoop(timeForNext, 0);
return;
});
}).catch(function(err) {
}
loopDeLoop(timeForNext);
})
.catch(err => {
console.log(err.message);
timeForNext = 5000;
clearInterval(i);
loopDeLoop(timeForNext, 0);
return;
loopDeLoop(5000);
});
check = 1;
}
}, timeForNext);
}
+5 -6
View File
@@ -2,7 +2,6 @@ import argparse
import json
import sys
import os
import collections
manifest_binfiles = {}
@@ -35,14 +34,14 @@ def create_display_text(description, version, families):
esp32_split.append('C3')
if 'ESP32-C5' in families:
esp32_split.append('C5')
if 'ESP32-C6' in families:
esp32_split.append('C6')
if 'ESP32-C61' in families:
esp32_split.append('C61')
elif 'ESP32-C6' in families:
esp32_split.append('C6')
if 'ESP32-H2' in families:
esp32_split.append('H2')
if 'ESP32-H21' in families:
esp32_split.append('H21')
elif 'ESP32-H2' in families:
esp32_split.append('H2')
if 'ESP32-P4' in families:
esp32_split.append('P4')
@@ -383,7 +382,7 @@ def generate_manifest_files(bin_folder, output_prefix):
' </style>\n',
' <script\n',
' type="module"\n',
' src="https://unpkg.com/tasmota-esp-web-tools@10.0.3/dist/web/install-button.js?module"\n',
' src="https://unpkg.com/tasmota-esp-web-tools@11.1.2/dist/web/install-button.js?module"\n',
' ></script>\n',
' </head>\n',
' <body>\n',