2 Commits

Author SHA1 Message Date
Proddy
469d412951 Merge pull request #3045 from MichaelDvP/dev
fix legegram length, #2969
2026-04-24 17:14:58 +02:00
MichaelDvP
6edbac86e2 fix legegram length, #2969 2026-04-24 14:46:53 +02:00
49 changed files with 580 additions and 975 deletions

View File

@@ -12,7 +12,6 @@ For more details go to [emsesp.org](https://emsesp.org/).
- prometheus metrics for temperature/analog/scheduler/custom [#2962](https://github.com/emsesp/EMS-ESP32/issues/2962)
- boiler pumpkick [#2965](https://github.com/emsesp/EMS-ESP32/discussions/2965)
- heatpump reset [#2933](https://github.com/emsesp/EMS-ESP32/issues/2933)
- e-mail notification using ReadyMail Client
- 2.nd freshwater module (dhw4, dhw5) [#2991](https://github.com/emsesp/EMS-ESP32/issues/2991)
- full system backup and restore
@@ -32,8 +31,6 @@ For more details go to [emsesp.org](https://emsesp.org/).
- support `minflowtemp` and `baseflowtemp` [#2969](https://github.com/emsesp/EMS-ESP32/discussions/2969)
- update version if it is 00.00 in first read [#2981](https://github.com/emsesp/EMS-ESP32/issues/2981)
- device class for % values [#2980](https://github.com/emsesp/EMS-ESP32/issues/2980)
- use tasmota core 2026.03.30
- secure mqtt uses ESP_SSLClient
- fetch telegrams: set length to fetch [#3017](https://github.com/emsesp/EMS-ESP32/issues/3017)
- move http client from stack to heap
- heap optimizations [#3021](https://github.com/emsesp/EMS-ESP32/discussions/3021)

View File

@@ -5,7 +5,7 @@
},
"core": "esp32",
"extra_flags": [
"-DNO_TLS_SUPPORT",
"-DTASMOTA_SDK",
"-DARDUINO_LOLIN_C3_MINI",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1"

View File

@@ -6,7 +6,7 @@
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DNO_TLS_SUPPORT",
"-DTASMOTA_SDK",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0"
],

View File

@@ -21,7 +21,7 @@
"arduino",
"espidf"
],
"name": "Tasmota ESP32-S3 32M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS",
"name": "Espressif ESP32-S3 32M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS",
"upload": {
"flash_size": "32MB",
"maximum_ram_size": 327680,

View File

@@ -1,7 +1,7 @@
{
"build": {
"core": "esp32",
"extra_flags": "-DNO_TLS_SUPPORT",
"extra_flags": "-DTASMOTA_SDK",
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",
@@ -19,7 +19,7 @@
"arduino",
"espidf"
],
"name": "Tasmota ESP32 16M Flash, 4608KB Code/OTA, 2MB FS",
"name": "Espressif ESP32 16M Flash, 4608KB Code/OTA, 2MB FS",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,

View File

@@ -19,7 +19,7 @@
"arduino",
"espidf"
],
"name": "Tasmota ESP32 16M Flash DIO PSRAM, 4608KB Code/OTA, 2MB FS",
"name": "Espressif ESP32 16M Flash DIO PSRAM, 4608KB Code/OTA, 2MB FS",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,

View File

@@ -1,7 +1,7 @@
{
"build": {
"core": "esp32",
"extra_flags": "-DNO_TLS_SUPPORT",
"extra_flags": "-DTASMOTA_SDK",
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "dio",

View File

@@ -2,7 +2,6 @@
"build": {
"core": "esp32",
"extra_flags": [
"-DNO_TLS_SUPPORT",
"-DARDUINO_XIAO_ESP32C6",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1"

View File

@@ -30,7 +30,7 @@
"@mui/material": "^9.0.0",
"@preact/compat": "^18.3.2",
"@table-library/react-table-library": "4.1.15",
"alova": "^3.5.1",
"alova": "3.5.1",
"async-validator": "^4.2.5",
"etag": "^1.8.1",
"formidable": "^3.5.4",
@@ -64,7 +64,8 @@
"terser": "^5.46.1",
"typescript-eslint": "^8.59.0",
"vite": "^8.0.9",
"vite-plugin-imagemin": "^0.6.1"
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^6.1.1"
},
"packageManager": "pnpm@10.33.1+sha512.05ba3c1d5d1c18f68df06470d74055e62d41fc110a0c660db1b2dfb2785327f04cf0f68345d4609bc52089e7fa0343c31593b2f9594e2c5d5da426230acc9820"
}

View File

@@ -30,7 +30,7 @@ importers:
specifier: 4.1.15
version: 4.1.15(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
alova:
specifier: ^3.5.1
specifier: 3.5.1
version: 3.5.1
async-validator:
specifier: ^4.2.5
@@ -126,6 +126,9 @@ importers:
vite-plugin-imagemin:
specifier: ^0.6.1
version: 0.6.1(vite@8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1))
vite-tsconfig-paths:
specifier: ^6.1.1
version: 6.1.1(typescript@6.0.3)(vite@8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1))
packages:
@@ -1983,6 +1986,9 @@ packages:
resolution: {integrity: sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==}
engines: {node: '>=8'}
globrex@0.1.2:
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
@@ -3170,6 +3176,16 @@ packages:
peerDependencies:
typescript: '>=4.8.4'
tsconfck@3.1.6:
resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==}
engines: {node: ^18 || >=20}
hasBin: true
peerDependencies:
typescript: ^5.0.0
peerDependenciesMeta:
typescript:
optional: true
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@@ -3258,6 +3274,11 @@ packages:
peerDependencies:
vite: 5.x || 6.x || 7.x || 8.x
vite-tsconfig-paths@6.1.1:
resolution: {integrity: sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==}
peerDependencies:
vite: '*'
vite@8.0.9:
resolution: {integrity: sha512-t7g7GVRpMXjNpa67HaVWI/8BWtdVIQPCL2WoozXXA7LBGEFK4AkkKkHx2hAQf5x1GZSlcmEDPkVLSGahxnEEZw==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -5244,6 +5265,8 @@ snapshots:
merge2: 1.4.1
slash: 3.0.0
globrex@0.1.2: {}
gopd@1.2.0: {}
got@7.1.0:
@@ -6377,6 +6400,10 @@ snapshots:
dependencies:
typescript: 6.0.3
tsconfck@3.1.6(typescript@6.0.3):
optionalDependencies:
typescript: 6.0.3
tslib@2.8.1: {}
tunnel-agent@0.6.0:
@@ -6489,6 +6516,16 @@ snapshots:
stack-trace: 1.0.0-pre2
vite: 8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1)
vite-tsconfig-paths@6.1.1(typescript@6.0.3)(vite@8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1)):
dependencies:
debug: 4.4.3
globrex: 0.1.2
tsconfck: 3.1.6(typescript@6.0.3)
vite: 8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1)
transitivePeerDependencies:
- supports-color
- typescript
vite@8.0.9(@types/node@25.6.0)(esbuild@0.27.4)(terser@5.46.1):
dependencies:
lightningcss: 1.32.0

View File

@@ -43,16 +43,6 @@ export interface Settings {
modbus_port: number;
modbus_max_clients: number;
modbus_timeout: number;
email_enabled: boolean;
email_ssl?: boolean;
email_starttls?: boolean;
email_server: string;
email_port: number;
email_login: string;
email_pass: string;
email_sender: string;
email_recp: string;
email_subject: string;
developer_mode: boolean;
}

View File

@@ -28,7 +28,6 @@ import {
FormLoader,
MessageBox,
SectionContent,
ValidatedPasswordField,
ValidatedTextField,
useLayoutTitle
} from 'components';
@@ -352,156 +351,6 @@ const ApplicationSettings = () => {
</Grid>
</Grid>
)}
<Typography color="secondary">eMail</Typography>
<BlockFormControlLabel
control={
<Checkbox
checked={data.email_enabled}
onChange={updateFormValue}
name="email_enabled"
disabled={!hardwareData.psram}
/>
}
label={
<Typography color={!hardwareData.psram ? 'grey' : 'default'}>
Enable eMail notification
{!hardwareData.psram && (
<Typography variant="caption">
&nbsp; &#40;{LL.IS_REQUIRED('PSRAM')}&#41;
</Typography>
)}
</Typography>
}
/>
{data.email_enabled && (
<>
<Grid
container
spacing={2}
direction="row"
justifyContent="flex-start"
alignItems="flex-start"
>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="email_server"
label="SMTP Server"
variant="outlined"
value={data.email_server}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
sx={{ width: '12ch' }}
name="email_port"
variant="outlined"
label="Port"
value={numberValue(data.email_port)}
type="number"
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid size={4} mt={!data.email_ssl && !data.email_starttls ? 0 : 3}>
{!data.email_starttls && (
<BlockFormControlLabel
sx={{ width: '12ch' }}
control={
<Checkbox
checked={data.email_ssl}
onChange={updateFormValue}
name="email_ssl"
disabled={
data.email_starttls || data.email_ssl === undefined
}
/>
}
label="SSL/TLS"
/>
)}
{!data.email_ssl && (
<BlockFormControlLabel
sx={{ width: '12ch' }}
control={
<Checkbox
checked={data.email_starttls}
onChange={updateFormValue}
name="email_starttls"
disabled={
data.email_ssl || data.email_starttls === undefined
}
/>
}
label="STARTTLS"
/>
)}
</Grid>
</Grid>
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="email_login"
label="Login"
variant="outlined"
value={data.email_login}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid>
<ValidatedPasswordField
fieldErrors={fieldErrors || {}}
name="email_pass"
label="Password"
variant="outlined"
value={data.email_pass}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
</Grid>
<Grid container spacing={2} rowSpacing={0}>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="email_sender"
label="From"
variant="outlined"
value={data.email_sender}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="email_recp"
label="To"
variant="outlined"
value={data.email_recp}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
<Grid>
<ValidatedTextField
fieldErrors={fieldErrors || {}}
name="email_subject"
label="Subject"
variant="outlined"
value={data.email_subject}
onChange={updateFormValue}
margin="normal"
/>
</Grid>
</Grid>
</>
)}
<Typography sx={{ pb: 1, pt: 2 }} variant="h6" color="primary">
{LL.SENSORS()}
</Typography>

View File

@@ -315,13 +315,22 @@ const InstallDialog = memo(
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
)}
</Typography>
{upgradeImportantMessageType === 1 && LL.UPGRADE_IMPORTANT_MESSAGES_1()}
{upgradeImportantMessageType === 2 && LL.UPGRADE_IMPORTANT_MESSAGES_2()}
{upgradeImportantMessageType === 1 && (
<>
{LL.UPGRADE_IMPORTANT_MESSAGES_1()}
<Typography sx={{ mt: 2 }}>
<Link to="/settings/downloadUpload" style={{ color: 'lightblue' }}>
{LL.DOWNLOAD_SYSTEM_BACKUP()}
</Link>
</Typography>
</>
)}
<Typography sx={{ mt: 2 }}>
<Link
target="_blank"
to="https://docs.emsesp.org/FAQ#upgrading-the-firmware"
target="_blank"
rel="noreferrer"
style={{ color: 'lightblue' }}
>
{LL.ONLINE_HELP()}

View File

@@ -49,10 +49,6 @@ the LICENSE file.
#define EMC_CLIENTID_LENGTH 23 + 1
#endif
#ifdef EMSESP_MQTT_STACKSIZE
#define EMC_TASK_STACK_SIZE EMSESP_MQTT_STACKSIZE
#endif
#ifndef EMC_TASK_STACK_SIZE
#define EMC_TASK_STACK_SIZE 5120
#endif
@@ -77,3 +73,7 @@ the LICENSE file.
#define EMC_SIZE_POOL_ELEMENTS 128
#endif
#endif
#ifndef TASMOTA_SDK
#define EMC_CLIENT_SECURE
#endif

View File

@@ -62,19 +62,14 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint
_xSemaphore = xSemaphoreCreateMutex();
EMC_SEMAPHORE_GIVE(); // release before first use
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {
if (core > 1) {
xTaskCreate((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle);
} else {
xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core);
}
}
#else
(void) useInternalTask;
(void) priority;
(void) core;
#endif
_clientId = _generatedClientId;
_core = core;
}
MqttClient::~MqttClient() {

View File

@@ -69,16 +69,6 @@ class MqttClient {
const char* getClientId() const;
size_t queueSize(); // No const because of mutex
void loop();
uint32_t stack() {
#ifndef EMSESP_STANDALONE
return uxTaskGetStackHighWaterMark(_taskHandle);
#else
return 0;
#endif
}
uint8_t core() {
return _core;
}
protected:
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1);
@@ -108,7 +98,6 @@ class MqttClient {
uint8_t _willQos;
bool _willRetain;
uint32_t _timeout;
uint8_t _core;
// state is protected to allow state changes by the transport system, defined in child classes
// eg. to allow AsyncTCP

View File

@@ -8,7 +8,7 @@ the LICENSE file.
#include "ClientPosix.h"
#if defined(__linux__) || defined(__APPLE__)
#if defined(__linux__)
namespace espMqttClientInternals {

View File

@@ -8,7 +8,7 @@ the LICENSE file.
#pragma once
#if defined(__linux__) || defined(__APPLE__)
#if defined(__linux__)
#include <stdio.h>
#include <stdlib.h>

View File

@@ -6,31 +6,28 @@ For a copy, see <https://opensource.org/licenses/MIT> or
the LICENSE file.
*/
#ifndef NO_TLS_SUPPORT
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
#include "ClientSecureSync.h"
#include <lwip/sockets.h>
#include "../Config.h"
#include <lwip/sockets.h> // socket options
namespace espMqttClientInternals {
ClientSecureSync::ClientSecureSync()
: client() {
client.setClient(&basic_client, true);
client.setBufferSizes(EMC_RX_BUFFER_SIZE, EMC_TX_BUFFER_SIZE);
client.setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
}
ClientSecureSync::~ClientSecureSync() {
stop();
// empty
}
bool ClientSecureSync::connect(IPAddress ip, uint16_t port) {
bool ret = client.connect(ip, port); // implicit conversion of return code int --> bool
if (ret) {
#if defined(ARDUINO_ARCH_ESP8266)
client.setNoDelay(true);
#elif defined(ARDUINO_ARCH_ESP32)
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
int val = true;
basic_client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
#endif
}
return ret;
}
@@ -38,9 +35,13 @@ bool ClientSecureSync::connect(IPAddress ip, uint16_t port) {
bool ClientSecureSync::connect(const char* host, uint16_t port) {
bool ret = client.connect(host, port); // implicit conversion of return code int --> bool
if (ret) {
#if defined(ARDUINO_ARCH_ESP8266)
client.setNoDelay(true);
#elif defined(ARDUINO_ARCH_ESP32)
// Set TCP option directly to bypass lack of working setNoDelay for WiFiClientSecure
int val = true;
basic_client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
client.setSocketOption(IPPROTO_TCP, TCP_NODELAY, &val, sizeof(int));
#endif
}
return ret;
}

View File

@@ -8,11 +8,15 @@ the LICENSE file.
#pragma once
#ifndef NO_TLS_SUPPORT
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
// #include "esp_tls.h"
// Added for EMS-ESP
#include "../Config.h"
#if defined(EMC_CLIENT_SECURE)
#include <WiFiClientSecure.h> // includes IPAddress
#else
#include <WiFiClient.h>
#include <ESP_SSLClient.h>
#endif
#include "Transport.h"
namespace espMqttClientInternals {
@@ -20,7 +24,6 @@ namespace espMqttClientInternals {
class ClientSecureSync : public Transport {
public:
ClientSecureSync();
~ClientSecureSync();
bool connect(IPAddress ip, uint16_t port) override;
bool connect(const char * host, uint16_t port) override;
size_t write(const uint8_t * buf, size_t size) override;
@@ -28,9 +31,12 @@ class ClientSecureSync : public Transport {
void stop() override;
bool connected() override;
bool disconnected() override;
WiFiClient basic_client;
ESP_SSLClient client;
// added for EMS-ESP
#if defined(EMC_CLIENT_SECURE)
WiFiClientSecure client;
#else
WiFiClient client;
#endif
};
} // namespace espMqttClientInternals

View File

@@ -8,6 +8,50 @@ the LICENSE file.
#include "espMqttClient.h"
#if defined(ARDUINO_ARCH_ESP8266)
espMqttClient::espMqttClient()
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
, _client() {
_transport = &_client;
}
espMqttClientSecure::espMqttClientSecure()
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
, _client() {
_transport = &_client;
}
espMqttClientSecure & espMqttClientSecure::setInsecure() {
_client.client.setInsecure();
return *this;
}
espMqttClientSecure & espMqttClientSecure::setFingerprint(const uint8_t fingerprint[20]) {
_client.client.setFingerprint(fingerprint);
return *this;
}
espMqttClientSecure & espMqttClientSecure::setTrustAnchors(const X509List * ta) {
_client.client.setTrustAnchors(ta);
return *this;
}
espMqttClientSecure & espMqttClientSecure::setClientRSACert(const X509List * cert, const PrivateKey * sk) {
_client.client.setClientRSACert(cert, sk);
return *this;
}
espMqttClientSecure & espMqttClientSecure::setClientECCert(const X509List * cert, const PrivateKey * sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
_client.client.setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type);
return *this;
}
espMqttClientSecure & espMqttClientSecure::setCertStore(CertStoreBase * certStore) {
_client.client.setCertStore(certStore);
return *this;
}
#endif
#if defined(ARDUINO_ARCH_ESP32)
espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask)
: MqttClientSetup(useInternalTask)
@@ -34,45 +78,51 @@ espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core)
}
espMqttClientSecure & espMqttClientSecure::setInsecure() {
#ifndef NO_TLS_SUPPORT
#if defined(EMC_CLIENT_SECURE)
_client.client.setInsecure();
#endif
return *this;
}
espMqttClientSecure & espMqttClientSecure::setCACert(const char * rootCA) {
#ifndef NO_TLS_SUPPORT
#if defined(EMC_CLIENT_SECURE)
_client.client.setCACert(rootCA);
#endif
return *this;
}
espMqttClientSecure & espMqttClientSecure::setCertificate(const char * clientCa) {
#ifndef NO_TLS_SUPPORT
#if defined(EMC_CLIENT_SECURE)
_client.client.setCertificate(clientCa);
#endif
return *this;
}
espMqttClientSecure & espMqttClientSecure::setPrivateKey(const char * privateKey) {
#ifndef NO_TLS_SUPPORT
#if defined(EMC_CLIENT_SECURE)
_client.client.setPrivateKey(privateKey);
#endif
return *this;
}
espMqttClientSecure & espMqttClientSecure::setPreSharedKey(const char * pskIdent, const char * psKey) {
#ifndef NO_TLS_SUPPORT
#if defined(EMC_CLIENT_SECURE)
_client.client.setPreSharedKey(pskIdent, psKey);
#endif
return *this;
}
#endif
#if defined(__linux__) || defined(__APPLE__)
#if defined(__linux__)
espMqttClient::espMqttClient()
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO)
, _client() {
_transport = &_client;
}
#elif defined(_WIN32) || defined(__APPLE__)
// Windows
espMqttClient::espMqttClient()
: MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) {
}
#endif

View File

@@ -14,7 +14,7 @@ the LICENSE file.
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
#include "Transport/ClientSync.h"
#include "Transport/ClientSecureSync.h"
#elif defined(__linux__) || defined(__APPLE__)
#elif defined(__linux__)
#include "Transport/ClientPosix.h"
#endif
@@ -65,16 +65,10 @@ class espMqttClientSecure : public MqttClientSetup<espMqttClientSecure> {
espMqttClientSecure & setPreSharedKey(const char * pskIdent, const char * psKey);
protected:
#ifndef NO_TLS_SUPPORT
espMqttClientInternals::ClientSecureSync _client;
#else
espMqttClientInternals::ClientSync _client;
#endif
};
#endif
#if defined(__linux__) || defined(__APPLE__)
#elif defined(__linux__)
class espMqttClient : public MqttClientSetup<espMqttClient> {
public:
espMqttClient();
@@ -82,4 +76,10 @@ class espMqttClient : public MqttClientSetup<espMqttClient> {
protected:
espMqttClientInternals::ClientPosix _client;
};
#elif defined(_WIN32) || defined(__APPLE__)
class espMqttClient : public MqttClientSetup<espMqttClient> {
public:
espMqttClient();
};
#endif

View File

@@ -21,7 +21,7 @@ extra_configs =
pio_local.ini
[common]
core_build_flags = -std=gnu++20 -O3 -flto=auto -Wno-type-limits -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-format -Wno-missing-field-initializers
core_build_flags = -std=gnu++2a -O3 -flto=auto -Wno-type-limits -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-format -Wno-missing-field-initializers
core_unbuild_flags = -std=gnu++11 -std=gnu++14 -std=gnu++17 -fno-lto
my_build_flags =
@@ -53,21 +53,13 @@ build_flags =
unbuild_flags =
${common.core_unbuild_flags}
; 4MB Flash variants
[espressif32_base_4M]
framework = arduino
board_build.partitions = partitions/esp32_partition_4M.csv
board_upload.flash_size = 4MB
board_build.app_partition_name = app0
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.04.50/platform-espressif32.zip ; Platform 2026.04.50 Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407
; 16MB Flash variants
[espressif32_base_16M]
framework = arduino
board_build.partitions = partitions/esp32_partition_16M.csv
board_upload.flash_size = 16MB
board_build.app_partition_name = app0
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.04.50/platform-espressif32.zip ; Platform 2026.04.50 Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407
platform = espressif32@6.13.0 ; Arduino Core 2.0.17 / IDF 4.4.7
; 32MB Flash variants
[espressif32_base_32M]
@@ -75,7 +67,29 @@ framework = arduino
board_build.partitions = partitions/esp32_partition_32M.csv
board_upload.flash_size = 32MB
board_build.app_partition_name = app0
platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.04.50/platform-espressif32.zip ; Platform 2026.04.50 Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407
platform = espressif32@6.13.0 ; Arduino Core 2.0.17 / IDF 4.4.7
; use Tasmota's library for 4MB Flash variants.
; Removes libs (like mbedtsl, so no WiFi_secure.h) to increase available heap
[espressif32_base_T_4M]
framework = arduino
board_build.partitions = partitions/esp32_partition_4M.csv
board_upload.flash_size = 4MB
board_build.app_partition_name = app0
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.00/platform-espressif32.zip ; Arduino Core 2.0.18 with IPv6 support, based on IDF 4.4.8
; Tasmota Arduino Core 3.1.3.250302 based on IDF 5.3.2.250228
; platform = https://github.com/tasmota/platform-espressif32/releases/download/2025.03.30/platform-espressif32.zip
; use Tasmota's library for 16MB Flash variants.
; Removes libs (like mbedtsl, so no WiFi_secure.h) to increase available heap
[espressif32_base_T_16M]
framework = arduino
board_build.partitions = partitions/esp32_partition_16M.csv
board_upload.flash_size = 16MB
board_build.app_partition_name = app0
platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.00/platform-espressif32.zip ; Arduino Core 2.0.18 with IPv6 support, based on IDF 4.4.8
; Tasmota Arduino Core 3.1.3.250302 based on IDF 5.3.2.250228
; platform = https://github.com/tasmota/platform-espressif32/releases/download/2025.03.30/platform-espressif32.zip
[env]
build_flags =
@@ -92,8 +106,6 @@ lib_deps =
bblanchon/ArduinoJson @ 7.4.3
ESP32Async/AsyncTCP @ 3.4.10
ESP32Async/ESPAsyncWebServer @ 3.10.3
https://github.com/mobizt/ReadyMail.git @ 0.4.0
https://github.com/mobizt/ESP_SSLClient.git @ 3.1.3
; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
; builds the web interface only, not the firmware
@@ -106,17 +118,18 @@ build_src_filter = -<*>
;
; Builds for different board types
; We use Tasmota for boards without PSRAM as this framework has mbedtls removed to save memory.
; If you're building for a single target environment, we recommend creating a pio_local.ini (see example file)
;
[env:s_4M]
; 4MB ESP32 - no SSL, no PSRAM - like a BBQKees older S32 and E32 models
extends = espressif32_base_4M
; 4MB ESP32 - no SSL, no PSRAM - like a BBQKees older S32 and E32 models - uses Tasmota
extends = espressif32_base_T_4M
board = s_4M
[env:s_16M]
; 16MB ESP32 - no PSRAM - like a BBQKees later S32 V2 models
extends = espressif32_base_16M
; 16MB ESP32 - no PSRAM - like a BBQKees later S32 V2 models - uses Tasmota
extends = espressif32_base_T_16M
board = s_16M
[env:s_16M_P]
@@ -135,30 +148,24 @@ extends = espressif32_base_32M
board = s3_32M_P
[env:s2_4M_P]
; based on lolin_s2_mini 4MB with 2MB PSRAM
extends = espressif32_base_4M
; based on lolin_s2_mini 4MB with 2MB PSRAM - uses Tasmota
extends = espressif32_base_T_4M
board = s2_4M_P
[env:c3_mini_4M]
; based on lolin_c3_mini 4MB, no PSRAM
extends = espressif32_base_4M
; based on lolin_c3_mini 4MB, no PSRAM - uses Tasmota
extends = espressif32_base_T_4M
board = c3_mini_4M
; lolin C3 mini v1 needs special wifi initialization
; https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi
[env:c3_miniv1_4M]
extends = espressif32_base_4M
extends = espressif32_base_T_4M
board = c3_mini_4M
build_flags =
${common.build_flags}
-DBOARD_C3_MINI_V1
; XIAO ESP32C - 512KB SRAM & 4MB Flash - https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/
[env:c6_xiao_4M]
extends = espressif32_base_4M
board_build.app_partition_name = app0
board = seeed_xiao_esp32c6
; foundation for building and testing natively, standalone without an ESP32
; use the `standalone` environment instead of `native` for testing
[env:native]
@@ -168,8 +175,7 @@ build_flags =
build_src_flags =
-DEMSESP_STANDALONE -DEMSESP_TEST
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1
-DNO_TLS_SUPPORT
-std=gnu++20 -Og -ggdb
-std=gnu++2a -Og -ggdb
-Wall -Wextra
-Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces
-Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare
@@ -207,9 +213,8 @@ build_src_flags =
-DEMSESP_STANDALONE -DEMSESP_TEST
-DEMSESP_UNITY
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1
-DNO_TLS_SUPPORT
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
-std=gnu++20 -Og -ggdb
-std=gnu++2a -Og -ggdb
-Wall -Wextra
-Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces
-Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare

View File

@@ -10,6 +10,7 @@ APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityM
, _reconfigureAp(false)
, _connected(0) {
addUpdateHandler([this] { reconfigureAP(); }, false);
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
}
void APSettingsService::begin() {
@@ -18,27 +19,39 @@ void APSettingsService::begin() {
// reconfigureAP();
}
// wait 10 sec on STA disconnect before starting AP
void APSettingsService::WiFiEvent(WiFiEvent_t event) {
const uint8_t was_connected = _connected;
switch (event) {
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
_connected &= ~1U;
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
_connected &= ~2U;
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
_connected |= 1U;
break;
case ARDUINO_EVENT_ETH_GOT_IP:
case ARDUINO_EVENT_ETH_GOT_IP6:
_connected |= 2U;
break;
default:
return;
}
// wait 10 sec before starting AP
if (was_connected && !_connected) {
_lastManaged = uuid::get_uptime();
}
}
void APSettingsService::reconfigureAP() {
_lastManaged = uuid::get_uptime() - MANAGE_NETWORK_DELAY;
_reconfigureAp = true;
}
void APSettingsService::loop() {
const uint8_t was_connected = _connected;
if (WiFi.isConnected()) {
_connected |= 1U;
} else {
_connected &= ~1U;
}
if (ETH.connected()) {
_connected |= 2U;
} else {
_connected &= ~2U;
}
// wait 10 sec before starting AP
if (was_connected && !_connected) {
_lastManaged = uuid::get_uptime();
}
const unsigned long currentMillis = uuid::get_uptime();
if ((currentMillis - _lastManaged) >= MANAGE_NETWORK_DELAY) {
_lastManaged = currentMillis;
@@ -63,7 +76,11 @@ void APSettingsService::manageAP() {
}
void APSettingsService::startAP() {
#if ESP_IDF_VERSION_MAJOR < 5
WiFi.softAPenableIpV6(); // force IPV6, same as for WiFi - fixes https://github.com/emsesp/EMS-ESP32/issues/1922
#else
WiFi.softAPenableIPv6(); // force IPV6, same as for WiFi - fixes https://github.com/emsesp/EMS-ESP32/issues/1922
#endif
WiFi.softAPConfig(_state.localIP, _state.gatewayIP, _state.subnetMask);
esp_wifi_set_bandwidth(static_cast<wifi_interface_t>(ESP_IF_WIFI_AP), WIFI_BW_HT20);
WiFi.softAP(_state.ssid.c_str(), _state.password.c_str(), _state.channel, _state.ssidHidden, _state.maxClients);

View File

@@ -104,6 +104,7 @@ class APSettingsService : public StatefulService<APSettings> {
void startAP();
void stopAP();
void handleDNS();
void WiFiEvent(WiFiEvent_t event);
};
#endif

View File

@@ -6,7 +6,7 @@
static constexpr const char CACHE_CONTROL[] = "public,max-age=60";
// Single static-content handler serving all assets embedded in WWWData.h
// Single static-content handler serving all assets embedded in WWWData.h.
class StaticContentHandler : public AsyncWebHandler {
public:
bool canHandle(AsyncWebServerRequest * request) const override {
@@ -15,7 +15,8 @@ class StaticContentHandler : public AsyncWebHandler {
}
void handleRequest(AsyncWebServerRequest * request) override {
// OPTIONS is handled generically - the server-level CORS headers are attached via DefaultHeaders in ESP32React::begin().
// OPTIONS is handled generically - the server-level CORS headers are
// attached via DefaultHeaders in ESP32React::begin().
if (request->method() == HTTP_OPTIONS) {
request->send(200);
return;
@@ -56,7 +57,8 @@ class StaticContentHandler : public AsyncWebHandler {
}
// Returns the /index.html asset, used as the SPA fallback for any GET
// that didn't match an embedded asset (React Router handles routing on the client side).
// that didn't match an embedded asset (React Router handles routing on
// the client side).
static const WWWAsset * index_asset() {
static const WWWAsset * cached = nullptr;
if (cached == nullptr) {
@@ -104,5 +106,4 @@ void ESP32React::loop() {
_networkSettingsService.loop();
_apSettingsService.loop();
_mqttSettingsService.loop();
_ntpSettingsService.loop();
}

View File

@@ -9,6 +9,7 @@ MqttSettingsService::MqttSettingsService(AsyncWebServer * server, FS * fs, Secur
, _disconnectedAt(0)
, _disconnectReason(espMqttClientTypes::DisconnectReason::TCP_DISCONNECTED)
, _mqttClient(nullptr) {
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
addUpdateHandler([this] { onConfigUpdated(); }, false);
}
@@ -28,7 +29,6 @@ MqttSettingsService::~MqttSettingsService() {
void MqttSettingsService::begin() {
_fsPersistence.readFromFS();
startClient();
_reconfigureMqtt = true;
}
void MqttSettingsService::startClient() {
@@ -41,7 +41,7 @@ void MqttSettingsService::startClient() {
delete _mqttClient;
_mqttClient = nullptr;
}
#ifndef NO_TLS_SUPPORT
#ifndef TASMOTA_SDK
if (_state.enableTLS) {
isSecure = true;
if (emsesp::EMSESP::system_.PSram() == 0) {
@@ -79,10 +79,6 @@ void MqttSettingsService::startClient() {
}
void MqttSettingsService::loop() {
if (_state.enabled && _mqttClient && _mqttClient->connected() && !emsesp::EMSESP::system_.network_connected()) {
// emsesp::EMSESP::logger().info("Network connection dropped, stopping MQTT client");
_mqttClient->disconnect(true);
}
if (_reconfigureMqtt || (_disconnectedAt && static_cast<uint32_t>(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
// reconfigure MQTT client
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
@@ -146,6 +142,28 @@ void MqttSettingsService::onConfigUpdated() {
emsesp::EMSESP::mqtt_.start(); // reload EMS-ESP MQTT settings
}
void MqttSettingsService::WiFiEvent(WiFiEvent_t event) {
switch (event) {
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
case ARDUINO_EVENT_ETH_GOT_IP:
case ARDUINO_EVENT_ETH_GOT_IP6:
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
if (_state.enabled && !_mqttClient->connected()) {
onConfigUpdated();
}
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
case ARDUINO_EVENT_ETH_DISCONNECTED:
if (_state.enabled) {
_mqttClient->disconnect(true);
}
break;
default:
break;
}
}
bool MqttSettingsService::configureMqtt() {
// disconnect if already connected
if (_mqttClient->connected()) {
@@ -164,7 +182,7 @@ bool MqttSettingsService::configureMqtt() {
}
_reconfigureMqtt = false;
#ifndef NO_TLS_SUPPORT
#ifndef TASMOTA_SDK
if (_state.enableTLS) {
if (_state.rootCA == "insecure") {
emsesp::EMSESP::logger().debug("Start insecure MQTT");
@@ -201,7 +219,7 @@ bool MqttSettingsService::configureMqtt() {
}
void MqttSettings::read(MqttSettings & settings, JsonObject root) {
#ifndef NO_TLS_SUPPORT
#ifndef TASMOTA_SDK
root["enableTLS"] = settings.enableTLS;
root["rootCA"] = settings.rootCA;
#endif
@@ -240,7 +258,7 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
MqttSettings newSettings;
bool changed = false;
#ifndef NO_TLS_SUPPORT
#ifndef TASMOTA_SDK
newSettings.enableTLS = root["enableTLS"];
newSettings.rootCA = root["rootCA"] | "";
#else
@@ -371,7 +389,7 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
emsesp::EMSESP::mqtt_.set_publish_time_heartbeat(newSettings.publish_time_heartbeat);
}
#ifndef NO_TLS_SUPPORT
#ifndef TASMOTA_SDK
// strip down to certificate only
newSettings.rootCA.replace("\r", "");
newSettings.rootCA.replace("\n", "");

View File

@@ -134,6 +134,7 @@ class MqttSettingsService : public StatefulService<MqttSettings> {
// the MQTT client instance
MqttClient * _mqttClient;
void WiFiEvent(WiFiEvent_t event);
void onMqttConnect(bool sessionPresent);
void onMqttDisconnect(espMqttClientTypes::DisconnectReason reason);
void

View File

@@ -11,18 +11,37 @@ NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, Securit
configureTime(request, json);
});
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); });
addUpdateHandler([this] { configureNTP(); }, false);
}
void NTPSettingsService::begin() {
_fsPersistence.readFromFS();
configureNTP();
}
void NTPSettingsService::loop() {
if (_connected != emsesp::EMSESP::system_.network_connected()) {
_connected = emsesp::EMSESP::system_.network_connected();
// handles both WiFI and Ethernet
void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
switch (event) {
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
case ARDUINO_EVENT_ETH_DISCONNECTED:
if (_connected && emsesp::EMSESP::system_.ntp_connected()) {
emsesp::EMSESP::logger().info("WiFi connection dropped, stopping NTP");
_connected = false;
configureNTP();
}
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
case ARDUINO_EVENT_ETH_GOT_IP:
// emsesp::EMSESP::logger().info("Got IP address, starting NTP synchronization");
_connected = true;
configureNTP();
break;
default:
break;
}
}
// https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm
@@ -36,11 +55,9 @@ void NTPSettingsService::configureNTP() {
} else {
setenv("TZ", _state.tzFormat.c_str(), 1);
tzset();
if (esp_sntp_enabled()) {
esp_sntp_stop();
}
}
}
void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVariant json) {
if (json.is<JsonObject>()) {
@@ -51,12 +68,7 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari
tm.tm_isdst = -1; // not set by strptime, tells mktime to determine daylightsaving
time_t time = mktime(&tm);
struct timeval now = {.tv_sec = time, .tv_usec = {}};
#if CONFIG_IDF_TARGET_ESP32C3
// settimeofday and adjtime() does not work, unknown how to set time
emsesp::EMSESP::logger().warning("manual clock setting not possible");
#else
settimeofday(&now, nullptr);
#endif
AsyncWebServerResponse * response = request->beginResponse(200);
request->send(response);
return;

View File

@@ -44,7 +44,6 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
void begin();
void loop();
static void ntp_received(struct timeval * tv);
private:
@@ -52,6 +51,7 @@ class NTPSettingsService : public StatefulService<NTPSettings> {
FSPersistence<NTPSettings> _fsPersistence;
volatile bool _connected;
void WiFiEvent(WiFiEvent_t event);
void configureNTP();
void configureTime(AsyncWebServerRequest * request, JsonVariant json);
};

View File

@@ -9,7 +9,7 @@ NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs,
, _stopping(false) {
addUpdateHandler([this] { reconfigureWiFiConnection(); }, false);
// Eth is also bound to the WifiGeneric event handler
// Network.onEvent([this](arduino_event_id_t event, arduino_event_info_t info) { WiFiEvent(event, info); });
WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event, info); });
}
static bool formatBssid(const String & bssid, uint8_t (&mac)[6]) {
@@ -34,8 +34,8 @@ void NetworkSettingsService::begin() {
WiFi.persistent(false);
WiFi.setAutoReconnect(false);
WiFi.mode(WIFI_STA);
WiFi.mode(WIFI_OFF);
WiFi.mode(WIFI_MODE_MAX);
WiFi.mode(WIFI_MODE_NULL);
// scan settings give connect issues since arduino 2.0.14 and arduino 3.x.x with some wifi systems
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN
@@ -62,124 +62,28 @@ void NetworkSettingsService::loop() {
_lastConnectionAttempt = currentMillis;
manageSTA();
}
static uint8_t connect = 0;
enum uint8_t {
CONNECT_IDLE = 0,
CONNECT_WAIT_ETH,
CONNECT_WAIT_IP4,
CONNECT_WAIT_ETH_IP4,
CONNECT_WAIT_IP6,
CONNECT_WAIT_ETH_IP6,
CONNECT_ETH_ACTIVE,
CONNECT_WIFI_ACTIVE
};
switch (connect) {
default:
connect = CONNECT_IDLE;
break;
case CONNECT_IDLE:
if (ETH.started() && _state.ssid.length() == 0) {
emsesp::EMSESP::logger().info("ETH started");
ETH.setHostname(emsesp::EMSESP::system_.hostname().c_str());
ETH.enableIPv6(true);
if (_state.staticIPConfig) {
ETH.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2);
}
connect = CONNECT_WAIT_ETH;
}
if (WiFi.isConnected()) {
emsesp::EMSESP::logger().info("Wifi connected");
if (_state.tx_power == 0) {
setWiFiPowerOnRSSI();
}
mDNS_start();
emsesp::EMSESP::system_.has_ipv6(true);
connect = CONNECT_WAIT_IP4;
}
break;
case CONNECT_WAIT_ETH:
if (ETH.connected()) {
emsesp::EMSESP::logger().info("ETH connected");
emsesp::EMSESP::system_.ethernet_connected(true);
mDNS_start();
emsesp::EMSESP::system_.has_ipv6(true);
connect = CONNECT_WAIT_ETH_IP4;
}
break;
case CONNECT_WAIT_ETH_IP4:
if (ETH.hasIP()) {
emsesp::EMSESP::logger().info("ETH IPv4: %s", ETH.localIP().toString().c_str());
connect = CONNECT_WAIT_ETH_IP6;
}
if (!ETH.connected()) {
connect = CONNECT_ETH_ACTIVE;
}
break;
case CONNECT_WAIT_ETH_IP6:
if (ETH.hasLinkLocalIPv6() && ETH.hasGlobalIPv6()) {
emsesp::EMSESP::system_.has_ipv6(true);
connect = CONNECT_ETH_ACTIVE;
}
if (!ETH.connected()) {
connect = CONNECT_ETH_ACTIVE;
}
break;
case CONNECT_ETH_ACTIVE:
if (!ETH.connected()) {
emsesp::EMSESP::logger().info("ETH disconnected");
emsesp::EMSESP::system_.ethernet_connected(false);
emsesp::EMSESP::system_.has_ipv6(false);
connect = CONNECT_IDLE;
}
break;
case CONNECT_WAIT_IP4:
if (!WiFi.localIP().toString().isEmpty()) {
emsesp::EMSESP::logger().info("Wifi IPv4: %s", WiFi.localIP().toString().c_str());
connect = CONNECT_WAIT_IP6;
}
if (!WiFi.isConnected()) {
connect = CONNECT_ETH_ACTIVE;
}
break;
case CONNECT_WAIT_IP6:
if (WiFi.linkLocalIPv6().toString() != "::" && WiFi.globalIPv6().toString() != "::") {
emsesp::EMSESP::logger().info("Wifi IPv6: %s, %s", WiFi.linkLocalIPv6().toString().c_str(), WiFi.globalIPv6().toString().c_str());
emsesp::EMSESP::system_.has_ipv6(true);
connect = CONNECT_WIFI_ACTIVE;
}
if (!WiFi.isConnected()) {
connect = CONNECT_WIFI_ACTIVE;
}
break;
case CONNECT_WIFI_ACTIVE:
if (!WiFi.isConnected()) {
emsesp::EMSESP::logger().info("WiFi disconnected");
if (_stopping) {
_lastConnectionAttempt = 0;
_stopping = false;
}
emsesp::EMSESP::system_.has_ipv6(false);
connect = CONNECT_IDLE;
}
break;
}
}
void NetworkSettingsService::manageSTA() {
// Abort if already connected, or if we have no SSID
if (WiFi.isConnected() || _state.ssid.length() == 0) {
#if ESP_IDF_VERSION_MAJOR >= 5
if (_state.ssid.length() == 0) {
ETH.enableIPv6(true);
}
#endif
return;
}
// Connect or reconnect as required
if ((WiFi.getMode() & WIFI_STA) == 0) {
WiFi.setHostname(_state.hostname.c_str()); // updates shared default_hostname buffer
WiFi.enableSTA(true); // creates the STA netif
WiFi.STA.setHostname(_state.hostname.c_str()); // pushes to esp_netif_set_hostname
#if ESP_IDF_VERSION_MAJOR >= 5
WiFi.enableIPv6(true);
#endif
if (_state.staticIPConfig) {
WiFi.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2); // configure for static IP
}
WiFi.setHostname(_state.hostname.c_str()); // set hostname
// www.esp32.com/viewtopic.php?t=12055
if (_state.bandwidth20) {
@@ -389,7 +293,7 @@ const char * NetworkSettingsService::disconnectReason(uint8_t code) {
}
// handles both WiFI and Ethernet
void NetworkSettingsService::WiFiEvent(arduino_event_id_t event, arduino_event_info_t info) {
void NetworkSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
#ifndef EMSESP_STANDALONE
switch (event) {
@@ -401,7 +305,7 @@ void NetworkSettingsService::WiFiEvent(arduino_event_id_t event, arduino_event_i
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
connectcount_ = connectcount_ + 1; // count the number of WiFi reconnects
connectcount_++; // count the number of WiFi reconnects
emsesp::EMSESP::logger().warning("WiFi disconnected (#%d). Reason: %s (%d)",
connectcount_,
disconnectReason(info.wifi_sta_disconnected.reason),
@@ -421,11 +325,11 @@ void NetworkSettingsService::WiFiEvent(arduino_event_id_t event, arduino_event_i
break;
case ARDUINO_EVENT_ETH_START:
// apply hostname FIRST so DHCP DISCOVER carries the correct name
ETH.setHostname(emsesp::EMSESP::system_.hostname().c_str());
// configure for static IP
if (_state.staticIPConfig) {
ETH.config(_state.localIP, _state.gatewayIP, _state.subnetMask, _state.dnsIP1, _state.dnsIP2);
}
ETH.setHostname(emsesp::EMSESP::system_.hostname().c_str());
break;
case ARDUINO_EVENT_ETH_GOT_IP:
@@ -456,15 +360,25 @@ void NetworkSettingsService::WiFiEvent(arduino_event_id_t event, arduino_event_i
if (_state.tx_power == 0) {
setWiFiPowerOnRSSI();
}
#if ESP_IDF_VERSION_MAJOR < 5
WiFi.enableIpV6(); // force ipv6
#endif
break;
case ARDUINO_EVENT_ETH_CONNECTED:
#if ESP_IDF_VERSION_MAJOR < 5
ETH.enableIpV6(); // force ipv6
#endif
break;
// IPv6 specific - WiFi/Eth
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
case ARDUINO_EVENT_ETH_GOT_IP6: {
#if !TASMOTA_SDK && ESP_IDF_VERSION_MAJOR < 5
auto ip6 = IPv6Address((uint8_t *)info.got_ip6.ip6_info.ip.addr).toString();
#else
auto ip6 = IPAddress(IPv6, (uint8_t *)info.got_ip6.ip6_info.ip.addr, 0).toString();
#endif
const char * link = event == ARDUINO_EVENT_ETH_GOT_IP6 ? "Eth" : "WiFi";
if (ip6.startsWith("fe80")) {
emsesp::EMSESP::logger().info("IPv6 (%s) local: %s", link, ip6.c_str());

View File

@@ -107,7 +107,7 @@ class NetworkSettingsService : public StatefulService<NetworkSettings> {
volatile uint16_t connectcount_ = 0; // number of wifi reconnects
void WiFiEvent(arduino_event_id_t event, arduino_event_info_t info);
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
void mDNS_start() const;
const char * disconnectReason(uint8_t code);
void reconfigureWiFiConnection();

View File

@@ -2,7 +2,7 @@
#include <emsesp.h>
#ifdef NO_TLS_SUPPORT
#ifdef TASMOTA_SDK
#include "lwip/dns.h"
#endif
@@ -32,12 +32,21 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
if (ethernet_connected) {
// Ethernet
root["local_ip"] = ETH.localIP().toString();
#if ESP_IDF_VERSION_MAJOR < 5
root["local_ipv6"] = ETH.localIPv6().toString();
#else
root["local_ipv6"] = ETH.linkLocalIPv6().toString();
#endif
root["mac_address"] = ETH.macAddress();
root["subnet_mask"] = ETH.subnetMask().toString();
root["gateway_ip"] = ETH.gatewayIP().toString();
#ifdef TASMOTA_SDK
IPAddress dnsIP1 = IPAddress(dns_getserver(0));
IPAddress dnsIP2 = IPAddress(dns_getserver(1));
#else
IPAddress dnsIP1 = ETH.dnsIP(0);
IPAddress dnsIP2 = ETH.dnsIP(1);
#endif
if (IPUtils::isSet(dnsIP1)) {
root["dns_ip_1"] = dnsIP1.toString();
}
@@ -46,7 +55,11 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
}
} else if (wifi_status == WL_CONNECTED) {
root["local_ip"] = WiFi.localIP().toString();
#if ESP_IDF_VERSION_MAJOR < 5
root["local_ipv6"] = WiFi.localIPv6().toString();
#else
root["local_ipv6"] = WiFi.linkLocalIPv6().toString();
#endif
root["mac_address"] = WiFi.macAddress();
root["rssi"] = WiFi.RSSI();
root["ssid"] = WiFi.SSID();
@@ -58,8 +71,14 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
if (WiFi.gatewayIP() != INADDR_NONE) {
root["gateway_ip"] = WiFi.gatewayIP().toString();
}
#ifdef TASMOTA_SDK
IPAddress dnsIP1 = IPAddress(dns_getserver(0));
IPAddress dnsIP2 = IPAddress(dns_getserver(1));
#else
IPAddress dnsIP1 = WiFi.dnsIP(0);
IPAddress dnsIP2 = WiFi.dnsIP(1);
#endif
if (dnsIP1 != INADDR_NONE) {
root["dns_ip_1"] = dnsIP1.toString();
}

View File

@@ -347,13 +347,23 @@ void AnalogSensor::reload(bool get_nvs) {
sensor.polltime_ = sensor.value() != 0 ? uuid::get_uptime() + (sensor.factor() * 1000) : 0;
} else if (sensor.type() >= AnalogType::PWM_0 && sensor.type() <= AnalogType::PWM_2) {
LOG_DEBUG("PWM output on GPIO %02d", sensor.gpio());
#if ESP_IDF_VERSION_MAJOR >= 5
ledcAttach(sensor.gpio(), sensor.factor(), 13);
#else
uint8_t channel = sensor.type() - AnalogType::PWM_0;
ledcSetup(channel, sensor.factor(), 13);
ledcAttachPin(sensor.gpio(), channel);
#endif
if (sensor.offset() > 100) {
sensor.set_offset(100);
} else if (sensor.offset() < 0) {
sensor.set_offset(0);
}
#if ESP_IDF_VERSION_MAJOR >= 5
ledcWrite(sensor.gpio(), (uint32_t)(sensor.offset() * 8191 / 100));
#else
ledcWrite(channel, (uint32_t)(sensor.offset() * 8191 / 100));
#endif
sensor.set_value(sensor.offset());
sensor.set_uom(DeviceValueUOM::PERCENT);
publish_sensor(sensor);
@@ -1031,7 +1041,12 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
}
sensor.set_offset(val);
sensor.set_value(val);
#if ESP_IDF_VERSION_MAJOR >= 5
ledcWrite(sensor.gpio(), (uint32_t)(sensor.offset() * 8191 / 100));
#else
uint8_t channel = sensor.type() - AnalogType::PWM_0;
ledcWrite(channel, (uint32_t)(val * 8191 / 100));
#endif
} else {
return false;
}

View File

@@ -285,8 +285,6 @@ enum {
#define EMSESP_PLATFORM "ESP32S3"
#elif CONFIG_IDF_TARGET_ESP32 || EMSESP_STANDALONE
#define EMSESP_PLATFORM "ESP32"
#elif CONFIG_IDF_TARGET_ESP32C6
#define EMSESP_PLATFORM "ESP32C6"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
@@ -295,9 +293,12 @@ enum {
#ifndef STRINGIZE
#define STRINGIZE(s) #s
#endif
#ifdef TASMOTA_SDK
#define ARDUINO_VERSION_STR(major, minor, patch) "Tasmota Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
#else
#define ARDUINO_VERSION_STR(major, minor, patch) "ESP32 Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
#endif
#define ARDUINO_VERSION ARDUINO_VERSION_STR(ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH)
#endif
#endif
#endif

View File

@@ -1727,8 +1727,6 @@ void EMSESP::start() {
#ifndef EMSESP_STANDALONE
if (factory_settings) {
LOG_WARNING("No settings found on filesystem. Using factory settings.");
// make sure OTAdata is updated with core3 format
esp_ota_set_boot_partition(esp_ota_get_running_partition());
}
#endif

View File

@@ -380,16 +380,12 @@ std::string commands(std::string & expr, bool quotes) {
if (return_code != CommandRet::OK && return_code != CommandRet::NO_VALUE) {
return expr = "";
}
std::string data;
if (output["api_data"].is<std::string>()) {
data = output["api_data"].as<std::string>();
std::string data = output["api_data"] | "";
if (!isnum(data) && quotes) {
data.insert(data.begin(), '"');
data.insert(data.end(), '"');
}
} else {
serializeJson(output, data);
}
expr.replace(f, l, data);
e = f + data.length();
expr_new = Helpers::toLower(expr);
@@ -704,6 +700,7 @@ std::string compute(const std::string & expr) {
std::string cmd = expr_new.substr(f, e - f).c_str();
JsonDocument doc;
if (DeserializationError::Ok == deserializeJson(doc, cmd)) {
HTTPClient * http = new HTTPClient;
std::string url, header_s, value_s, method_s, key_s, keys_s;
// search keys lower case
for (JsonPair p : doc.as<JsonObject>()) {
@@ -723,126 +720,33 @@ std::string compute(const std::string & expr) {
keys_s = p.key().c_str();
}
}
bool content_set = false;
std::string value = doc[value_s] | "";
std::string method = doc[method_s] | "GET";
if (value.length()) {
method = "POST";
}
std::string result;
int httpResult = 0;
#ifndef NO_TLS_SUPPORT
if (Helpers::toLower(url.c_str()).starts_with("https://")) {
WiFiClient * basic_client = new WiFiClient;
ESP_SSLClient * ssl_client = new ESP_SSLClient;
ssl_client->setInsecure(); // with root CA we should set here: ssl_client->setCACert(rootCACert);
ssl_client->setBufferSizes(1024, 1024);
ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
url.replace(0, 8, "");
std::string host = url;
auto index = url.find_first_of('/');
if (index != std::string::npos) {
host = url.substr(0, index);
url.replace(0, index, "");
}
/*
index = host.find_first_of('@');
std::string auth;
if (index != std::string::npos) {
auth = base64::encode(host.substr(0, index));
host.replace(0, index, "");
}
*/
ssl_client->setClient(basic_client);
if (ssl_client->connect(host.c_str(), 443)) {
if (value.length() || Helpers::toLower(method) == "post") {
ssl_client->print("POST ");
ssl_client->print(url.c_str());
ssl_client->println(" HTTP/1.1");
ssl_client->print("Host: ");
ssl_client->println(host.c_str());
for (JsonPair p : doc[header_s].as<JsonObject>()) {
content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type");
ssl_client->print(p.key().c_str());
ssl_client->print(": ");
ssl_client->println(p.value().as<std::string>().c_str());
}
if (!content_set) {
ssl_client->print("Content-Type: ");
if (value.starts_with('{')) {
ssl_client->println(asyncsrv::T_application_json);
} else {
ssl_client->println(asyncsrv::T_text_plain);
}
}
ssl_client->print("Content-Length: ");
ssl_client->println(value.length());
ssl_client->println("Connection: close");
ssl_client->print("\r\n");
ssl_client->print(value.c_str());
} else {
ssl_client->print("GET ");
ssl_client->print(url.c_str());
ssl_client->println(" HTTP/1.1");
ssl_client->print("Host: ");
ssl_client->println(host.c_str());
for (JsonPair p : doc[header_s].as<JsonObject>()) {
ssl_client->print(p.key().c_str());
ssl_client->print(": ");
ssl_client->println(p.value().as<std::string>().c_str());
}
ssl_client->println("Connection: close");
}
auto ms = millis();
while (!ssl_client->available() && millis() - ms < 3000) {
delay(0);
}
while (ssl_client->available()) {
result += (char)ssl_client->read();
}
ssl_client->stop();
index = result.find_first_of(' ');
if (index != std::string::npos) {
httpResult = stoi(result.substr(index + 1, 3));
}
index = result.find("\r\n\r\n");
if (index != std::string::npos) {
result.replace(0, index + 4, "");
}
}
delete ssl_client;
delete basic_client;
} else
#endif
if (Helpers::toLower(url.c_str()).starts_with("http://")) {
HTTPClient * http = new HTTPClient;
if (http->begin(url.c_str())) {
int httpResult = 0;
for (JsonPair p : doc[header_s].as<JsonObject>()) {
http->addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type");
}
std::string value = doc[value_s] | "";
std::string method = doc[method_s] | "get";
// if there is data, force a POST
if (value.length() || Helpers::toLower(method) == "post") {
if (!content_set) {
http->addHeader("Content-Type", value.starts_with('{') ? asyncsrv::T_application_json : asyncsrv::T_text_plain);
if (value.find_first_of('{') != std::string::npos) {
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
}
httpResult = http->POST(value.c_str());
} else {
httpResult = http->GET(); // normal GET
}
if (httpResult > 0) {
result = http->getString().c_str();
}
}
http->end();
delete http;
}
if (httpResult == 200) {
std::string result = http->getString().c_str();
std::string key = doc[key_s] | "";
JsonDocument keys_doc; // JsonDocument to hold "keys" after doc is parsed with HTTP body
if (doc[keys_s].is<JsonArray>()) {
keys_doc.set(doc[keys_s].as<JsonArray>());
}
JsonArray keys = keys_doc.as<JsonArray>();
if (key.length() || !keys.isNull()) {
doc.clear();
if (DeserializationError::Ok == deserializeJson(doc, result)) {
@@ -863,9 +767,10 @@ std::string compute(const std::string & expr) {
}
}
}
expr_new.replace(f, e - f, result);
} else if (httpResult != 0) {
EMSESP::logger().warning("URL command failed with https code: %d, response: %s", httpResult, result.c_str());
expr_new.replace(f, e - f, result.c_str());
}
http->end();
delete http;
}
}
f = expr_new.find_first_of('{', e);

View File

@@ -37,15 +37,6 @@
#include "../test/test.h"
#endif
#ifndef NO_TLS_SUPPORT
#define ENABLE_SMTP
#define USE_ESP_SSLCLIENT
#define READYCLIENT_SSL_CLIENT ESP_SSLClient
#define READYCLIENT_TYPE_1 // TYPE 1 when using ESP_SSLClient
#include <ESP_SSLClient.h>
#include <ReadyMail.h>
#endif
namespace emsesp {
// Languages supported. Note: the order is important
@@ -114,115 +105,6 @@ bool System::command_send(const char * value, const int8_t id) {
return EMSESP::txservice_.send_raw(value); // ignore id
}
bool System::command_sendmail(const char * value, const int8_t id) {
bool enabled = false;
bool ssl, starttls;
uint16_t port;
String server, login, pass, sender, recp, subject;
EMSESP::webSettingsService.read([&](WebSettings & settings) {
enabled = settings.email_enabled;
ssl = settings.email_ssl;
starttls = settings.email_starttls;
server = settings.email_server;
port = settings.email_port;
login = settings.email_login;
pass = settings.email_pass;
sender = settings.email_sender;
recp = settings.email_recp;
subject = settings.email_subject;
});
if (!enabled) {
return false;
}
LOG_DEBUG("Command sendmail port %d%s called with '%s'", port, ssl ? " (SSL)" : starttls ? " (STARTTLS)" : "", value);
// LOG_DEBUG("Command sendmail port %d called with '%s'", port, value);
bool success = false;
#ifndef NO_TLS_SUPPORT
WiFiClient * basic_client = new WiFiClient;
ESP_SSLClient * ssl_client = new ESP_SSLClient;
ReadyClient * r_client = new ReadyClient(*ssl_client);
SMTPClient * smtp = new SMTPClient(*r_client);
ssl_client->setClient(basic_client);
ssl_client->setInsecure();
ssl_client->setBufferSizes(1024, 1024);
r_client->addPort(port, starttls ? readymail_protocol_tls : ssl ? readymail_protocol_ssl : readymail_protocol_plain_text);
// smtp->connect(server, port, sendmailCallback);
smtp->connect(server, port);
if (!smtp->isConnected()) {
LOG_ERROR("Sendmail connection error");
delete smtp;
delete r_client;
delete ssl_client;
delete basic_client;
return false;
}
// LOG_INFO("authenticate %s:%s", login.c_str(), pass.c_str());
smtp->authenticate(login, pass, readymail_auth_password);
if (!smtp->isAuthenticated()) {
LOG_ERROR("Sendmail authenticate error");
delete smtp;
delete r_client;
delete ssl_client;
delete basic_client;
return false;
}
JsonDocument doc;
String body = value;
if (body.length()) {
auto error = deserializeJson(doc, (const char *)value);
if (!error && doc.as<JsonObject>().size() >= 0) {
subject = doc["subject"] | subject;
recp = doc["to"] | recp;
sender = doc["from"] | sender;
body = doc["body"] | body;
}
}
SMTPMessage & msg = smtp->getMessage();
msg.headers.add(rfc822_subject, subject);
msg.headers.add(rfc822_from, sender);
msg.headers.add(rfc822_to, recp);
// Use addCustom to add custom header e.g. Importance and Priority.
// msg.headers.addCustom("Importance", PRIORITY);
// msg.headers.addCustom("X-MSMail-Priority", PRIORITY);
// msg.headers.addCustom("X-Priority", PRIORITY_NUM);
EMSESP::webSchedulerService.computed_value.clear();
EMSESP::webSchedulerService.raw_value = body.c_str();
for (uint16_t wait = 0; wait < 2000 && !EMSESP::webSchedulerService.raw_value.empty(); wait++) {
delay(1);
}
if (!EMSESP::webSchedulerService.computed_value.empty()) {
body = EMSESP::webSchedulerService.computed_value.c_str();
EMSESP::webSchedulerService.computed_value.clear();
EMSESP::webSchedulerService.computed_value.shrink_to_fit(); // free allocated memory
}
msg.text.body(body);
// bodyText.replace("\r\n", "<br>\r\n");
// msg.html.body("<html><body><div style=\"color:#cc0066;\">" + bodyText + "</div></body></html>");
// msg.html.transferEncoding("base64");
// With embedFile function, the html message will send as attachment.
// if (EMBED_MESSAGE)
// msg.html.embedFile(true, "msg.html", embed_message_type_attachment);
msg.timestamp = time(nullptr);
success = smtp->send(msg);
delete smtp;
delete r_client;
delete ssl_client;
delete basic_client;
#endif
return success;
}
// return string of languages and count
std::string System::languages_string() {
std::string languages_string = std::to_string(NUM_LANGUAGES) + " languages (";
@@ -355,8 +237,7 @@ bool System::command_message(const char * value, const int8_t id, JsonObject out
LOG_INFO("Message: %s", EMSESP::webSchedulerService.computed_value.c_str()); // send to log
Mqtt::queue_publish(F_(message), EMSESP::webSchedulerService.computed_value); // send to MQTT if enabled
output["api_data"] = EMSESP::webSchedulerService.computed_value; // send to API
EMSESP::webSchedulerService.computed_value.clear();
EMSESP::webSchedulerService.computed_value.shrink_to_fit();
return true;
}
@@ -580,6 +461,7 @@ void System::system_restart(const char * partitionname) {
Mqtt::disconnect(); // gracefully disconnect MQTT, needed for QOS1
EMSuart::stop(); // stop UART so there is no interference
#ifndef EMSESP_STANDALONE
delay(1000); // wait 1 second
ESP.restart(); // ka-boom! - this is the only place where the ESP32 restart is called
@@ -681,6 +563,7 @@ void System::store_settings(WebSettings & settings) {
locale_ = settings.locale;
developer_mode_ = settings.developer_mode;
// start services
if (settings.modbus_enabled) {
if (EMSESP::modbus_ == nullptr) {
@@ -722,11 +605,20 @@ void System::start() {
appfree_ = esp_ota_get_running_partition()->size / 1024 - appused_;
refreshHeapMem(); // refresh free heap and max alloc heap
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
#if ESP_IDF_VERSION_MAJOR < 5
temp_sensor_config_t temp_sensor = TSENS_CONFIG_DEFAULT();
temp_sensor_get_config(&temp_sensor);
temp_sensor.dac_offset = TSENS_DAC_DEFAULT; // DEFAULT: range:-10℃ ~ 80℃, error < 1℃.
temp_sensor_set_config(temp_sensor);
temp_sensor_start();
temp_sensor_read_celsius(&temperature_);
#else
temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80);
temperature_sensor_install(&temp_sensor_config, &temperature_handle_);
temperature_sensor_enable(temperature_handle_);
temperature_sensor_get_celsius(temperature_handle_, &temperature_);
#endif
#endif
#endif
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
@@ -947,9 +839,16 @@ void System::send_info_mqtt() {
doc["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
doc["IPv4 nameserver"] = uuid::printable_to_string(WiFi.dnsIP());
#if ESP_IDF_VERSION_MAJOR < 5
if (WiFi.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.localIPv6().toString() != "::") {
doc["IPv6 address"] = uuid::printable_to_string(WiFi.localIPv6());
}
#else
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
doc["IPv6 address"] = uuid::printable_to_string(WiFi.linkLocalIPv6());
}
#endif
}
#endif
Mqtt::queue_publish_retain(F_(info), doc.as<JsonObject>()); // topic called "info" and it's Retained
@@ -1058,11 +957,12 @@ void System::network_init() {
delay(500);
digitalWrite(eth_power_, HIGH);
}
#if ESP_IDF_VERSION_MAJOR < 5
eth_present_ = ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode);
#else
eth_present_ = ETH.begin(type, phy_addr, mdc, mdio, power, clock_mode);
if (eth_present_) {
// Push hostname to the ETH netif immediately after it's created
ETH.setHostname(hostname_.c_str());
}
#endif
#endif
}
@@ -1074,9 +974,13 @@ void System::system_check() {
#ifndef EMSESP_STANDALONE
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
#if ESP_IDF_VERSION_MAJOR < 5
temp_sensor_read_celsius(&temperature_);
#else
temperature_sensor_get_celsius(temperature_handle_, &temperature_);
#endif
#endif
#endif
#ifdef EMSESP_PINGTEST
static uint64_t ping_count = 0;
@@ -1128,7 +1032,6 @@ void System::commands_init() {
Command::add(EMSdevice::DeviceType::SYSTEM, F_(read), System::command_read, FL_(read_cmd), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, FL_(send_cmd), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, FL_(fetch_cmd), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(sendmail), System::command_sendmail, FL_(sendmail_cmd), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, FL_(restart_cmd), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(format), System::command_format, FL_(format_cmd), CommandFlag::ADMIN_ONLY);
Command::add(EMSdevice::DeviceType::SYSTEM, F_(txpause), System::command_txpause, FL_(txpause_cmd), CommandFlag::ADMIN_ONLY);
@@ -1377,9 +1280,16 @@ void System::show_system(uuid::console::Shell & shell) {
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str());
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(WiFi.gatewayIP()).c_str());
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(WiFi.dnsIP()).c_str());
#if ESP_IDF_VERSION_MAJOR < 5
if (WiFi.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.localIPv6().toString() != "::") {
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(WiFi.localIPv6()).c_str());
}
#else
if (WiFi.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && WiFi.linkLocalIPv6().toString() != "::") {
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(WiFi.linkLocalIPv6()).c_str());
}
#endif
break;
case WL_CONNECT_FAILED:
@@ -1410,9 +1320,15 @@ void System::show_system(uuid::console::Shell & shell) {
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(ETH.gatewayIP()).c_str());
shell.printfln(" IPv4 nameserver: %s", uuid::printable_to_string(ETH.dnsIP()).c_str());
#if ESP_IDF_VERSION_MAJOR < 5
if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.localIPv6().toString() != "::") {
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.localIPv6()).c_str());
}
#else
if (ETH.linkLocalIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000" && ETH.linkLocalIPv6().toString() != "::") {
shell.printfln(" IPv6 address: %s", uuid::printable_to_string(ETH.linkLocalIPv6()).c_str());
}
#endif
}
shell.println();
@@ -1434,10 +1350,10 @@ void System::show_system(uuid::console::Shell & shell) {
}
shell.println();
#endif
}
// see if there is a restore of an older settings file that needs to be applied
// note there can be only one file at a time
bool System::check_restore() {
@@ -1737,8 +1653,8 @@ bool System::check_upgrade() {
return false; // no reboot required
}
#ifndef EMSESP_STANDALONE
// map each config filename to its human-readable section key
#ifndef EMSESP_STANDALONE
static const std::pair<const char *, const char *> SECTION_MAP[] = {
{NETWORK_SETTINGS_FILE, "Network"},
{AP_SETTINGS_FILE, "AP"},
@@ -1853,16 +1769,27 @@ void System::exportSystemBackup(JsonObject output) {
const char * nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, "nvs1") ? "nvs1" : "nvs"; // nvs1 is on 16MBs
nvs_iterator_t it = nullptr;
#if ESP_IDF_VERSION_MAJOR < 5
it = nvs_entry_find(nvs_part, "ems-esp", NVS_TYPE_ANY);
if (it == nullptr) {
#else
esp_err_t err = nvs_entry_find(nvs_part, "ems-esp", NVS_TYPE_ANY, &it);
if (err != ESP_OK) {
#endif
LOG_ERROR("Failed to find NVS entry for %s", nvs_part);
return;
}
JsonArray entries = node["nvs"].to<JsonArray>();
#if ESP_IDF_VERSION_MAJOR < 5
while (it != nullptr) {
nvs_entry_info_t info;
nvs_entry_info(it, &info);
#else
while (err == ESP_OK) {
nvs_entry_info_t info;
nvs_entry_info(it, &info);
#endif
JsonObject entry = entries.add<JsonObject>();
entry["type"] = info.type;
entry["key"] = info.key;
@@ -1897,8 +1824,14 @@ void System::exportSystemBackup(JsonObject output) {
entry["value"] = EMSESP::nvs_.getString(info.key);
break;
}
#if ESP_IDF_VERSION_MAJOR < 5
it = nvs_entry_next(it);
}
#else
err = nvs_entry_next(&it);
}
#endif
if (it != nullptr) {
nvs_release_iterator(it);
@@ -3396,10 +3329,6 @@ void System::set_valid_system_gpios() {
} else {
valid_system_gpios_ = string_range_to_vector("0-39", "6-11, 20, 24, 28-31");
}
#elif CONFIG_IDF_TARGET_ESP32C6
// https://docs.espressif.com/projects/esp-idf/en/v5.5.3/esp32c6/api-reference/peripherals/gpio.html
// 24-30 used for flash, 12-13 USB, 16-17 uart0
valid_system_gpios_ = string_range_to_vector("0-30", "12-13, 16-17, 24-30");
#elif defined(EMSESP_STANDALONE)
valid_system_gpios_ = string_range_to_vector("0-39");
#endif

View File

@@ -37,7 +37,11 @@
#include <uuid/log.h>
#include <PButton.h>
#if ESP_ARDUINO_VERSION_MAJOR < 3
#define EMSESP_RGB_WRITE neopixelWrite
#else
#define EMSESP_RGB_WRITE rgbLedWrite
#endif
#if CONFIG_IDF_TARGET_ESP32
// there is no official API available on the original ESP32
@@ -45,8 +49,12 @@ extern "C" {
uint8_t temprature_sens_read();
}
#elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
#if ESP_IDF_VERSION_MAJOR < 5
#include "driver/temp_sensor.h"
#else
#include "driver/temperature_sensor.h"
#endif
#endif
using uuid::console::Shell;
@@ -95,7 +103,6 @@ class System {
static bool command_info(const char * value, const int8_t id, JsonObject output);
static bool command_response(const char * value, const int8_t id, JsonObject output);
static bool command_service(const char * cmd, const char * value);
static bool command_sendmail(const char * value, const int8_t id);
static bool command_txpause(const char * value, const int8_t id);
static bool get_value_info(JsonObject root, const char * cmd);
@@ -495,7 +502,9 @@ class System {
uint32_t appfree_;
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
#if ESP_IDF_VERSION_MAJOR >= 5
temperature_sensor_handle_t temperature_handle_ = NULL;
#endif
#endif
float temperature_ = 0;
};

View File

@@ -163,7 +163,6 @@ class EMSbus {
static constexpr uint8_t EMS_MASK_HT3 = 0x80; // EMS bus type Junkers/HT3
static constexpr uint8_t EMS_MASK_BUDERUS = 0xFF; // EMS bus type Buderus
static constexpr uint8_t EMS_TX_ERROR_LIMIT = 10; // % limit of failed Tx read/write attempts before showing a warning
static constexpr uint8_t EMS_POLL_MATCH_LIMIT = 3; // consecutive poll matches needed before declaring bus connected
static bool is_ht3() {
return (ems_mask_ == EMS_MASK_HT3);
@@ -211,6 +210,7 @@ class EMSbus {
if (!last_bus_activity_) {
bus_uptime_start_ = timestamp;
}
last_bus_activity_ = timestamp;
bus_connected_ = true;
}

View File

@@ -173,7 +173,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
for (uint8_t i = 0; i < monitor_size; i++) {
register_telegram_type(monitor_typeids[i], "RC300Monitor", false, MAKE_PF_CB(process_RC300Monitor), 33);
register_telegram_type(set_typeids[i], "RC300Set", false, MAKE_PF_CB(process_RC300Set), 29);
register_telegram_type(summer_typeids[i], "RC300Summer", false, MAKE_PF_CB(process_RC300Summer), 13);
register_telegram_type(summer_typeids[i], "RC300Summer", false, MAKE_PF_CB(process_RC300Summer), 14);
register_telegram_type(curve_typeids[i], "RC300Curves", false, MAKE_PF_CB(process_RC300Curve), 9);
register_telegram_type(summer2_typeids[i], "RC300Summer2", false, MAKE_PF_CB(process_RC300Summer2), 8);
}

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.9.0-dev.0"
#define EMSESP_APP_VERSION "3.8.2-dev.19"

View File

@@ -39,7 +39,7 @@ uint32_t inverse_mask = 0;
// receive task, wait for break and call incoming_telegram
void EMSuart::uart_event_task(void * pvParameters) {
uart_event_t event;
uint8_t telegram[UART_HW_FIFO_LEN(EMSUART_NUM) + 1]; // same size as in driver_install
uint8_t telegram[UART_FIFO_LEN + 1]; // same size as in driver_install
uint8_t length = 0;
while (1) {
@@ -66,23 +66,18 @@ void EMSuart::uart_event_task(void * pvParameters) {
// initialize UART driver
void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t tx_gpio) {
if (tx_mode_ == EMS_TXMODE_INIT) {
#if CONFIG_IDF_TARGET_ESP32C6
uart_config_t uart_config = {.baud_rate = EMSUART_BAUD,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 0};
#else
uart_config_t uart_config = {.baud_rate = EMSUART_BAUD,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.rx_flow_ctrl_thresh = 0,
.source_clk = UART_SCLK_APB,
.flags = {0}};
.source_clk = UART_SCLK_APB
#if ESP_ARDUINO_VERSION_MAJOR >= 3
,
.flags = {0}
#endif
};
#if defined(EMSUART_RX_INVERT)
inverse_mask |= UART_SIGNAL_RXD_INV;
#endif
@@ -92,12 +87,7 @@ void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t
uart_param_config(EMSUART_NUM, &uart_config);
uart_set_pin(EMSUART_NUM, tx_gpio, rx_gpio, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
uart_set_line_inverse(EMSUART_NUM, inverse_mask);
uart_driver_install(EMSUART_NUM,
UART_HW_FIFO_LEN(EMSUART_NUM) + 1,
0,
UART_HW_FIFO_LEN(EMSUART_NUM) + 3,
&uart_queue,
0); // buffer must be > fifo, queue can hold data+break+overflow message
uart_driver_install(EMSUART_NUM, UART_FIFO_LEN + 1, 0, UART_FIFO_LEN + 3, &uart_queue, 0); // buffer must be > fifo, queue can hold data+break+overflow message
uart_set_rx_full_threshold(EMSUART_NUM, 1);
uart_set_rx_timeout(EMSUART_NUM, 0); // disable

View File

@@ -54,10 +54,10 @@ void WebCustomization::read(WebCustomization & customizations, JsonObject root)
JsonArray sensorsJson = root["ts"].to<JsonArray>();
for (const SensorCustomization & sensor : customizations.sensorCustomizations) {
JsonObject sensorJson = sensorsJson.add<JsonObject>();
sensorJson["id"] = (const char *)sensor.id; // ID of dallas temperature sensor chip
sensorJson["id"] = (const char *)sensor.id; // ID of chip
sensorJson["name"] = (const char *)sensor.name; // n
sensorJson["offset"] = sensor.offset; // o
sensorJson["is_system"] = sensor.is_system; // s for gateway_temperature
sensorJson["is_system"] = sensor.is_system; // s for core_voltage, supply_voltage
}
// Analog Sensor customization

View File

@@ -352,6 +352,7 @@ bool WebSchedulerService::command(const char * name, const std::string & command
// parse json
JsonDocument doc;
if (deserializeJson(doc, cmd) == DeserializationError::Ok) {
HTTPClient * http = new HTTPClient;
std::string url = doc["url"] | "";
// for a GET with parameters replace commands with values
// don't search the complete url, it may contain a devicename in path
@@ -362,139 +363,40 @@ bool WebSchedulerService::command(const char * name, const std::string & command
commands(s, false);
url.replace(q + 1, l, s);
}
std::string value = doc["value"] | data; // extract value if its in the command, or take the data
std::string method = doc["method"] | "GET"; // default GET
commands(value, false);
if (value.length()) {
method = "POST";
}
std::string result;
int httpResult = 0;
#ifndef NO_TLS_SUPPORT
if (Helpers::toLower(url.c_str()).starts_with("https://")) {
WiFiClient * basic_client = new WiFiClient;
ESP_SSLClient * ssl_client = new ESP_SSLClient;
ssl_client->setInsecure(); // with root CA we should set here: ssl_client->setCACert(rootCACert);
ssl_client->setBufferSizes(1024, 1024);
ssl_client->setSessionTimeout(120); // Set the timeout in seconds (>=120 seconds)
url.replace(0, 8, "");
std::string host = url;
auto index = url.find_first_of('/');
if (index != std::string::npos) {
host = url.substr(0, index);
url.replace(0, index, "");
}
// EMSESP::logger().debug("Host: %s, URL: %s", host.c_str(), url.c_str());
ssl_client->setClient(basic_client);
if (ssl_client->connect(host.c_str(), 443)) {
if (value.length() || Helpers::toLower(method) == "post") {
// EMSESP::logger().debug("POST %s HTTP/1.1", url.c_str());
ssl_client->print("POST ");
ssl_client->print(url.c_str());
ssl_client->println(" HTTP/1.1");
ssl_client->print("Host: ");
ssl_client->println(host.c_str());
bool content_set = false;
for (JsonPair p : doc["header"].as<JsonObject>()) {
content_set |= (emsesp::Helpers::toLower(p.key().c_str()) == "content-type");
ssl_client->print(p.key().c_str());
ssl_client->print(": ");
ssl_client->println(p.value().as<std::string>().c_str());
}
if (!content_set) {
ssl_client->print("Content-Type: ");
if (value.starts_with('{')) {
ssl_client->println(asyncsrv::T_application_json);
} else {
ssl_client->println(asyncsrv::T_text_plain);
}
}
ssl_client->print("Content-Length: ");
ssl_client->println(value.length());
ssl_client->println("Connection: close");
ssl_client->print("\r\n");
ssl_client->print(value.c_str());
} else {
// EMSESP::logger().debug("GET %s HTTP/1.1", url.c_str());
ssl_client->print("GET ");
ssl_client->print(url.c_str());
ssl_client->println(" HTTP/1.1");
ssl_client->print("Host: ");
ssl_client->println(host.c_str());
for (JsonPair p : doc["header"].as<JsonObject>()) {
ssl_client->print(p.key().c_str());
ssl_client->print(": ");
ssl_client->println(p.value().as<std::string>().c_str());
}
ssl_client->println("Connection: close");
}
auto ms = millis();
while (ssl_client->connected() && !ssl_client->available() && millis() - ms < 3000) {
delay(0);
}
while (ssl_client->available()) {
result += (char)ssl_client->read();
}
ssl_client->stop();
// EMSESP::logger().debug("HTTPS response: %s", result.c_str());
index = result.find_first_of(' ');
if (index != std::string::npos) {
httpResult = stoi(result.substr(index + 1, 3));
// EMSESP::logger().debug("HTTPS code: %i", httpResult);
}
index = result.find("\r\n\r\n");
if (index != std::string::npos) {
result.replace(0, index + 4, "");
// EMSESP::logger().debug("HTTPS response: %s", result.c_str());
}
} else {
EMSESP::logger().warning("HTTPS connection failed");
}
delete ssl_client;
delete basic_client;
// check HTTP return code
if (httpResult != 200) {
EMSESP::logger().warning("Schedule '%s': URL command failed with http code %d", name, httpResult);
return false;
}
return true;
} else
#endif
if (Helpers::toLower(url.c_str()).starts_with("http://")) {
HTTPClient * http = new HTTPClient;
if (http->begin(url.c_str())) {
bool content_set = false;
// add any given headers
for (JsonPair p : doc["header"].as<JsonObject>()) {
http->addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
content_set |= p.key() == "content-type";
http->addHeader(p.key().c_str(), p.value().as<String>().c_str());
}
std::string value = doc["value"] | data.c_str(); // extract value if its in the command, or take the data
std::string method = doc["method"] | "GET"; // default GET
commands(value, false);
// if there is data, force a POST
if (Helpers::toLower(method) == "post") { // we have all lowercase
if (!content_set) {
// http->addHeader("Content-Type", value.find_first_of('{') != std::string::npos ? "application/json" : "text/plain");
if (value.starts_with('{')) {
int httpResult = 0;
if (value.length() || method == "post") { // we have all lowercase
if (value.find_first_of('{') != std::string::npos) {
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_application_json, false); // auto-set to JSON
} else {
http->addHeader(asyncsrv::T_Content_Type, asyncsrv::T_text_plain, false); // auto-set to JSON
}
}
httpResult = http->POST(value.c_str());
} else {
httpResult = http->GET(); // normal GET
if (httpResult > 0) {
result = http->getString().c_str();
}
}
}
http->end();
delete http;
// check HTTP return code
if (httpResult != 200) {
EMSESP::logger().warning("Schedule '%s': URL command failed with http code %d", name, httpResult);
char error[100];
snprintf(error, sizeof(error), "Schedule %s: URL command failed with http code %d", name, httpResult);
EMSESP::logger().warning(error);
return false;
}
#if defined(EMSESP_DEBUG)
EMSESP::logger().debug("Schedule %s: URL '%s' command successful with http code %d", name, url.c_str(), httpResult);
char msg[100];
snprintf(msg, sizeof(msg), "Schedule %s: URL command successful with http code %d", name, httpResult);
EMSESP::logger().debug(msg);
#endif
return true;
}

View File

@@ -83,20 +83,6 @@ void WebSettings::read(WebSettings & settings, JsonObject root) {
root["modbus_max_clients"] = settings.modbus_max_clients;
root["modbus_timeout"] = settings.modbus_timeout;
root["developer_mode"] = settings.developer_mode;
#ifndef NO_TLS_SUPPORT
root["email_enabled"] = settings.email_enabled;
#else
root["email_enabled"] = false;
#endif
root["email_ssl"] = settings.email_ssl;
root["email_starttls"] = settings.email_starttls;
root["email_server"] = settings.email_server;
root["email_port"] = settings.email_port;
root["email_login"] = settings.email_login;
root["email_pass"] = settings.email_pass;
root["email_sender"] = settings.email_sender;
root["email_recp"] = settings.email_recp;
root["email_subject"] = settings.email_subject;
}
// call on initialization and also when settings are updated/saved via web or console
@@ -257,9 +243,13 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
// Modbus settings
settings.modbus_enabled = root["modbus_enabled"] | EMSESP_DEFAULT_MODBUS_ENABLED;
check_flag(original_settings.modbus_enabled, settings.modbus_enabled, ChangeFlags::RESTART);
settings.modbus_port = root["modbus_port"] | EMSESP_DEFAULT_MODBUS_PORT;
check_flag(original_settings.modbus_port, settings.modbus_port, ChangeFlags::RESTART);
settings.modbus_max_clients = root["modbus_max_clients"] | EMSESP_DEFAULT_MODBUS_MAX_CLIENTS;
check_flag(original_settings.modbus_max_clients, settings.modbus_max_clients, ChangeFlags::RESTART);
settings.modbus_timeout = root["modbus_timeout"] | EMSESP_DEFAULT_MODBUS_TIMEOUT;
check_flag(original_settings.modbus_timeout, settings.modbus_timeout, ChangeFlags::RESTART);
//
// these may need mqtt restart to rebuild HA discovery topics
@@ -310,20 +300,6 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL;
settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT;
settings.email_enabled = root["email_enabled"] | FACTORY_EMAIL_ENABLE;
settings.email_ssl = root["email_ssl"] | FACTORY_EMAIL_SSL;
settings.email_starttls = root["email_starttls"] | FACTORY_EMAIL_STARTTLS;
settings.email_server = root["email_server"] | FACTORY_EMAIL_SERVER;
settings.email_port = root["email_port"] | FACTORY_EMAIL_PORT;
settings.email_login = root["email_login"] | FACTORY_EMAIL_LOGIN;
settings.email_pass = root["email_pass"] | FACTORY_EMAIL_PASSWORD;
settings.email_sender = root["email_sender"] | FACTORY_EMAIL_FROM;
settings.email_recp = root["email_recp"] | FACTORY_EMAIL_TO;
settings.email_subject = root["email_subject"] | FACTORY_EMAIL_SUBJECT;
if (settings.email_ssl && settings.email_starttls) {
settings.email_ssl = false;
}
// if no psram limit weblog buffer to 25 messages
if (EMSESP::system_.PSram() > 0) {
settings.weblog_buffer = root["weblog_buffer"] | EMSESP_DEFAULT_WEBLOG_BUFFER;
@@ -482,14 +458,23 @@ void WebSettings::set_board_profile(WebSettings & settings) {
#if CONFIG_IDF_TARGET_ESP32
// check for no PSRAM, could be a E32 or S32?
if (!ESP.getPsramSize()) {
#if ESP_ARDUINO_VERSION_MAJOR < 3
if (ETH.begin(1, 16, 23, 18, ETH_PHY_LAN8720, ETH_CLOCK_GPIO0_IN)) {
#else
if (ETH.begin(ETH_PHY_LAN8720, 1, 23, 18, 16, ETH_CLOCK_GPIO0_IN)) {
#endif
settings.board_profile = "E32"; // Ethernet without PSRAM
} else {
settings.board_profile = "S32"; // ESP32 standard WiFi without PSRAM
}
} else {
// check for boards with PSRAM, could be a E32V2 otherwise default back to the S32
#if ESP_ARDUINO_VERSION_MAJOR < 3
if (ETH.begin(0, 15, 23, 18, ETH_PHY_LAN8720, ETH_CLOCK_GPIO0_OUT)) {
#else
if (ETH.begin(ETH_PHY_LAN8720, 0, 23, 18, 15, ETH_CLOCK_GPIO0_OUT)) {
#endif
if (analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
settings.board_profile = "E32V2_2"; // Ethernet, PSRAM, internal sensors
} else {

View File

@@ -26,36 +26,6 @@
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/settings"
#define EMSESP_BOARD_PROFILE_SERVICE_PATH "/rest/boardProfile"
#ifndef FACTORY_EMAIL_ENABLE
#define FACTORY_EMAIL_ENABLE false
#endif
#ifndef FACTORY_EMAIL_SSL
#define FACTORY_EMAIL_SSL false
#endif
#ifndef FACTORY_EMAIL_STARTTLS
#define FACTORY_EMAIL_STARTTLS true
#endif
#ifndef FACTORY_EMAIL_PORT
#define FACTORY_EMAIL_PORT 587
#endif
#ifndef FACTORY_EMAIL_SERVER
#define FACTORY_EMAIL_SERVER "smtp.example.net"
#endif
#ifndef FACTORY_EMAIL_LOGIN
#define FACTORY_EMAIL_LOGIN ""
#endif
#ifndef FACTORY_EMAIL_PASSWORD
#define FACTORY_EMAIL_PASSWORD ""
#endif
#ifndef FACTORY_EMAIL_FROM
#define FACTORY_EMAIL_FROM "ems-esp@example.net"
#endif
#ifndef FACTORY_EMAIL_TO
#define FACTORY_EMAIL_TO ""
#endif
#ifndef FACTORY_EMAIL_SUBJECT
#define FACTORY_EMAIL_SUBJECT "ems-esp notification"
#endif
namespace emsesp {
class WebSettings {
@@ -107,16 +77,6 @@ class WebSettings {
uint16_t modbus_port;
uint8_t modbus_max_clients;
uint32_t modbus_timeout;
bool email_enabled;
bool email_ssl;
bool email_starttls;
String email_server;
uint16_t email_port;
String email_login;
String email_pass;
String email_sender;
String email_recp;
String email_subject;
uint8_t phy_type;
int8_t eth_power; // -1 means disabled