Merge pull request #2613 from MichaelDvP/dev

dev16
This commit is contained in:
Proddy
2025-08-24 10:22:20 +02:00
committed by GitHub
59 changed files with 681 additions and 406 deletions

View File

@@ -25,8 +25,8 @@
"@alova/adapter-xhr": "2.2.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.2.0",
"@mui/material": "^7.2.0",
"@mui/icons-material": "^7.3.1",
"@mui/material": "^7.3.1",
"@table-library/react-table-library": "4.1.15",
"alova": "3.3.4",
"async-validator": "^4.2.5",
@@ -38,28 +38,28 @@
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-icons": "^5.5.0",
"react-router": "^7.7.1",
"react-router": "^7.8.0",
"react-toastify": "^11.0.5",
"typesafe-i18n": "^5.26.2",
"typescript": "^5.9.2"
},
"devDependencies": {
"@babel/core": "^7.28.0",
"@eslint/js": "^9.32.0",
"@eslint/js": "^9.33.0",
"@preact/compat": "^18.3.1",
"@preact/preset-vite": "^2.10.2",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/node": "^24.2.0",
"@types/react": "^19.1.9",
"@types/node": "^24.2.1",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"concurrently": "^9.2.0",
"eslint": "^9.32.0",
"eslint": "^9.33.0",
"eslint-config-prettier": "^10.1.8",
"prettier": "^3.6.2",
"rollup-plugin-visualizer": "^6.0.3",
"terser": "^5.43.1",
"typescript-eslint": "^8.38.0",
"vite": "^7.0.6",
"typescript-eslint": "^8.39.1",
"vite": "^7.1.2",
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^5.1.4"
},

476
interface/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -259,6 +259,7 @@ export const BOARD_PROFILES: BoardProfiles = {
S32S3: 'BBQKees Gateway S3',
E32: 'BBQKees Gateway E32',
E32V2: 'BBQKees Gateway E32 V2',
E32V2_2: 'BBQKees Gateway E32 V2.2',
NODEMCU: 'NodeMCU 32S',
'MH-ET': 'MH-ET Live D1 Mini',
LOLIN: 'Lolin D32',

View File

@@ -54,7 +54,15 @@ bool PButton::init(uint8_t pin, bool pullMode) {
pullMode_ = pullMode; // 1=HIGH (pullup) 0=LOW (pulldown)
#if defined(ESP32)
#if CONFIG_IDF_TARGET_ESP32
if (pin_ == 34 || pin_ == 35 || pin_ == 36 || pin_ == 39) {
pinMode(pin_, INPUT);
} else {
pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT_PULLDOWN);
}
#else
pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT_PULLDOWN);
#endif
#else // esp8266 and standalone
pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT);
#endif
@@ -93,7 +101,15 @@ bool PButton::check(void) {
// make sure the pin is still input
#if defined(ESP32)
#if CONFIG_IDF_TARGET_ESP32
if (pin_ == 34 || pin_ == 35 || pin_ == 36 || pin_ == 39) {
pinMode(pin_, INPUT);
} else {
pinMode(pin_, pullMode_ ? INPUT_PULLUP : INPUT_PULLDOWN);
}
#else
pinMode(pin_, pullMode_ ? INPUT_PULLUP : INPUT_PULLDOWN);
#endif
#else // esp8266 and standalone
pinMode(pin_, pullMode_ ? INPUT_PULLUP : INPUT);
#endif

View File

@@ -0,0 +1,9 @@
name=eModbus
version=1.7.4
author=bertmelis,Miq1 <miq1@gmx.de>
maintainer=Miq1 <miq1@gmx.de>
sentence=eModbus provides Modbus RTU, ASCII and TCP functions for ESP32.
paragraph=This library is non-blocking for the program using it. Modbus requests and responses will be returned to user-supplied callback functions. All Modbus function codes are supported implicitly, the codes specified by the Modbus specs are parameter-checked.
category=Communication
url=https://github.com/eModbus/eModbus
architectures=esp32,FreeRTOS

View File

@@ -47,6 +47,8 @@ CoilData::~CoilData() {
// Assignment operator
CoilData& CoilData::operator=(const CoilData& m) {
// Avoid self-assignment
if (this == &m) return *this;
// Remove old data
if (CDbuffer) {
delete CDbuffer;

View File

@@ -8,6 +8,7 @@
#include <map>
#include <functional>
#include "ModbusClient.h"
#include "ModbusServer.h"
#include "ModbusClientTCP.h" // Needed for client.setTarget()
#include "RTUutils.h" // Needed for RTScallback
@@ -29,7 +30,7 @@ public:
ModbusBridge();
// Constructors for the RTU variant. Parameters as are for ModbusServerRTU
ModbusBridge(uint32_t timeout, int rtsPin = -1);
explicit ModbusBridge(uint32_t timeout, int rtsPin = -1);
ModbusBridge(uint32_t timeout, RTScallback rts);
// Destructor
@@ -230,42 +231,56 @@ ModbusMessage ModbusBridge<SERVERCLASS>::bridgeWorker(ModbusMessage msg) {
uint8_t aliasID = msg.getServerID();
uint8_t functionCode = msg.getFunctionCode();
ModbusMessage response;
bool foundServer = false;
uint8_t usableID = 255;
// Find the (alias) serverID
if (servers.find(aliasID) != servers.end()) {
foundServer = true;
usableID = aliasID;
} else {
if (servers.find(ANY_SERVER) != servers.end()) {
foundServer = true;
usableID = ANY_SERVER;
}
}
if (foundServer) {
// Found it. We may use servers[aliasID] now without allocating a new map slot
// Request filter hook to be called here
if (servers[aliasID]->requestFilter) {
if (servers[usableID]->requestFilter) {
LOG_D("Calling request filter\n");
msg = servers[aliasID]->requestFilter(msg);
msg = servers[usableID]->requestFilter(msg);
}
// Set real target server ID
msg.setServerID(servers[aliasID]->serverID);
// Issue the request
LOG_D("Request (%02X/%02X) sent\n", servers[aliasID]->serverID, msg.getFunctionCode());
// TCP servers have a target host/port that needs to be set in the client
if (servers[aliasID]->serverType == TCP_SERVER) {
response = reinterpret_cast<ModbusClientTCP *>(servers[aliasID]->client)->syncRequestMT(msg, (uint32_t)millis(), servers[aliasID]->host, servers[aliasID]->port);
} else {
response = servers[aliasID]->client->syncRequestM(msg, (uint32_t)millis());
if (servers[usableID]->serverID != ANY_SERVER) {
msg.setServerID(servers[usableID]->serverID);
}
// Response filter hook to be called here
if (servers[aliasID]->responseFilter) {
LOG_D("Calling response filter\n");
response = servers[aliasID]->responseFilter(response);
// Issue the request
LOG_D("Request (%02X/%02X) sent\n", servers[usableID]->serverID, msg.getFunctionCode());
// TCP servers have a target host/port that needs to be set in the client
if (servers[usableID]->serverType == TCP_SERVER) {
response = reinterpret_cast<ModbusClientTCP *>(servers[usableID]->client)->syncRequestMT(msg, (uint32_t)micros(), servers[usableID]->host, servers[usableID]->port);
} else {
response = servers[usableID]->client->syncRequestM(msg, (uint32_t)micros());
}
// Re-set the requested server ID and function code (may have been modified by filters)
response.setServerID(aliasID);
if (response.getError() != SUCCESS) {
response.setFunctionCode(functionCode | 0x80);
} else {
response.setFunctionCode(functionCode);
}
// Response filter hook to be called here
if (servers[usableID]->responseFilter) {
LOG_D("Calling response filter\n");
response = servers[usableID]->responseFilter(response);
}
} else {
// If we get here, something has gone wrong internally. We send back an error response anyway.
response.setError(aliasID, functionCode, INVALID_SERVER);

View File

@@ -21,6 +21,13 @@ ModbusClient::ModbusClient() :
onError(nullptr),
onResponse(nullptr) { instanceCounter++; }
// Default destructor: reduce number of clients by one
ModbusClient::~ModbusClient() {
if (instanceCounter) {
instanceCounter--;
}
}
// onDataHandler: register callback for data responses
bool ModbusClient::onDataHandler(MBOnData handler) {
if (onData) {

View File

@@ -37,8 +37,8 @@ public:
uint32_t getMessageCount(); // Informative: return number of messages created
uint32_t getErrorCount(); // Informative: return number of errors received
void resetCounts(); // Set both message and error counts to zero
inline Error addRequest(ModbusMessage m, uint32_t token) { return addRequestM(m, token); }
inline ModbusMessage syncRequest(ModbusMessage m, uint32_t token) { return syncRequestM(m, token); }
inline Error addRequest(const ModbusMessage& m, uint32_t token) { return addRequestM(m, token); }
inline ModbusMessage syncRequest(const ModbusMessage& m, uint32_t token) { return syncRequestM(m, token); }
// Template function to generate syncRequest functions as long as there is a
// matching ModbusMessage::setMessage() call
@@ -85,7 +85,7 @@ public:
protected:
ModbusClient(); // Default constructor
virtual void isInstance() = 0; // Make class abstract
virtual ~ModbusClient(); // Destructor
ModbusMessage waitSync(uint8_t serverID, uint8_t functionCode, uint32_t token); // wait for syncRequest response to arrive
// Virtual addRequest variant needed internally. All others done by template!
virtual Error addRequestM(ModbusMessage msg, uint32_t token) = 0;

View File

@@ -86,7 +86,7 @@ void ModbusClientRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterv
char taskName[18];
snprintf(taskName, 18, "Modbus%02XRTU", instanceCounter);
// Start task to handle the queue
xTaskCreatePinnedToCore((TaskFunction_t)&handleConnection, taskName, CLIENT_TASK_STACK, this, 6, &worker, coreID >= 0 ? coreID : NULL);
xTaskCreatePinnedToCore((TaskFunction_t)&handleConnection, taskName, CLIENT_TASK_STACK, this, 6, &worker, coreID >= 0 ? coreID : tskNO_AFFINITY);
LOG_D("Client task %d started. Interval=%d\n", (uint32_t)worker, MR_interval);
}
@@ -151,6 +151,7 @@ void ModbusClientRTU::clearQueue()
{
std::queue<RequestEntry> empty;
LOCK_GUARD(lockGuard, qLock);
// Empty queue
std::swap(requests, empty);
}
@@ -342,9 +343,12 @@ void ModbusClientRTU::handleConnection(ModbusClientRTU *instance) {
{
// Safely lock the queue
LOCK_GUARD(lockGuard, instance->qLock);
// Remove the front queue entry
// Remove the front queue entry if the queue is not empty
if (!instance->requests.empty()) {
instance->requests.pop();
}
}
} else {
delay(1);
}

View File

@@ -67,15 +67,15 @@ protected:
uint32_t token;
ModbusMessage msg;
bool isSyncRequest;
RequestEntry(uint32_t t, ModbusMessage m, bool syncReq = false) :
RequestEntry(uint32_t t, const ModbusMessage& m, bool syncReq = false) :
token(t),
msg(m),
isSyncRequest(syncReq) {}
};
// Base addRequest and syncRequest must be present
Error addRequestM(ModbusMessage msg, uint32_t token);
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
Error addRequestM(ModbusMessage msg, uint32_t token) override;
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
// addToQueue: send freshly created request to queue
bool addToQueue(uint32_t token, ModbusMessage msg, bool syncReq = false);
@@ -89,7 +89,6 @@ protected:
// start background task
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);
void isInstance() { return; } // make class instantiable
queue<RequestEntry> requests; // Queue to hold requests to be processed
#if USE_MUTEX
mutex qLock; // Mutex to protect queue

View File

@@ -18,7 +18,8 @@ ModbusClientTCP::ModbusClientTCP(Client& client, uint16_t queueLimit) :
MT_target(IPAddress(0, 0, 0, 0), 0, DEFAULTTIMEOUT, TARGETHOSTINTERVAL),
MT_defaultTimeout(DEFAULTTIMEOUT),
MT_defaultInterval(TARGETHOSTINTERVAL),
MT_qLimit(queueLimit)
MT_qLimit(queueLimit),
MT_timeoutsToClose(0)
{ }
// Alternative Constructor takes reference to Client (EthernetClient or WiFiClient) plus initial target host
@@ -29,7 +30,8 @@ ModbusClientTCP::ModbusClientTCP(Client& client, IPAddress host, uint16_t port,
MT_target(host, port, DEFAULTTIMEOUT, TARGETHOSTINTERVAL),
MT_defaultTimeout(DEFAULTTIMEOUT),
MT_defaultInterval(TARGETHOSTINTERVAL),
MT_qLimit(queueLimit)
MT_qLimit(queueLimit),
MT_timeoutsToClose(0)
{ }
// Destructor: clean up queue, task etc.
@@ -64,7 +66,7 @@ void ModbusClientTCP::end() {
// begin: start worker task
#if IS_LINUX
void *ModbusClientTCP::pHandle(void *p) {
handleConnection((ModbusClientTCP *)p);
handleConnection(static_cast<ModbusClientTCP *>(p));
return nullptr;
}
#endif
@@ -84,7 +86,7 @@ void ModbusClientTCP::begin(int coreID) {
char taskName[18];
snprintf(taskName, 18, "Modbus%02XTCP", instanceCounter);
// Start task to handle the queue
xTaskCreatePinnedToCore((TaskFunction_t)&handleConnection, taskName, CLIENT_TASK_STACK, this, 5, &worker, coreID >= 0 ? coreID : NULL);
xTaskCreatePinnedToCore((TaskFunction_t)&handleConnection, taskName, CLIENT_TASK_STACK, this, 5, &worker, coreID >= 0 ? coreID : tskNO_AFFINITY);
LOG_D("TCP client worker %s started\n", taskName);
#endif
} else {
@@ -119,9 +121,25 @@ uint32_t ModbusClientTCP::pendingRequests() {
void ModbusClientTCP::clearQueue() {
std::queue<RequestEntry *> empty;
LOCK_GUARD(lockGuard, qLock);
// Delete queue entries if still on the queue
while (!requests.empty()) {
RequestEntry *re = requests.front();
delete re;
requests.pop();
}
// Now flush the queue
std::swap(requests, empty);
}
// Set number of timeouts to tolerate before a connection is forcibly closed.
// 0: never, 1..255: desired number
// Returns previous value.
uint8_t ModbusClientTCP::closeConnectionOnTimeouts(uint8_t n) {
uint8_t oldValue = MT_timeoutsToClose;
MT_timeoutsToClose = n;
return oldValue;
}
// Base addRequest for preformatted ModbusMessage and last set target
Error ModbusClientTCP::addRequestM(ModbusMessage msg, uint32_t token) {
Error rc = SUCCESS; // Return value
@@ -225,6 +243,7 @@ bool ModbusClientTCP::addToQueue(uint32_t token, ModbusMessage request, TargetHo
void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
bool doNotPop;
unsigned long lastRequest = millis();
uint16_t timeoutCount = 0; // Run time counter of consecutive timeouts.
// Loop forever - or until task is killed
while (1) {
@@ -273,6 +292,8 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
// Did we get a normal response?
if (response.getError()==SUCCESS) {
LOG_D("Data response.\n");
// Reset timeout counter
timeoutCount = 0;
// Yes. Is it a synchronous request?
if (request->isSyncRequest) {
// Yes. Put the response into the response map
@@ -299,6 +320,25 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
LOCK_GUARD(responseCnt, instance->countAccessM);
instance->errorCount++;
}
// Is it a TIMEOUT and do we need to track it?
if (response.getError()==TIMEOUT && instance->MT_timeoutsToClose) {
LOG_D("Checking timeout sequence\n");
// Yes. First count timeout conter up
timeoutCount++;
// Is the count above the limit?
if (timeoutCount > instance->MT_timeoutsToClose) {
LOG_D("Timeouts: %d exceeding limit (%d), closing connection\n",
timeoutCount, instance->MT_timeoutsToClose);
// Yes. We need to cut the connection
instance->MT_client.stop();
delay(1);
// reset timeout count
timeoutCount = 0;
}
} else {
// No TIMEOUT or no limit: reset timeout count
timeoutCount = 0;
}
// Is it a synchronous request?
if (request->isSyncRequest) {
// Yes. Put the response into the response map
@@ -345,8 +385,11 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
{
// Safely lock the queue
LOCK_GUARD(lockGuard, instance->qLock);
// Remove the front queue entry
// Remove the front queue entry if the queue is not empty
if (!instance->requests.empty()) {
instance->requests.pop();
}
// Delete request
delete request;
LOG_D("Request popped from queue.\n");

View File

@@ -50,6 +50,11 @@ public:
// Remove all pending request from queue
void clearQueue();
// Set number of timeouts to tolerate before a connection is forcibly closed.
// 0: never, 1..255: desired number
// Returns previous value.
uint8_t closeConnectionOnTimeouts(uint8_t n=3);
protected:
// class describing a target server
struct TargetHost {
@@ -58,7 +63,7 @@ protected:
uint32_t timeout; // Time in ms waiting for a response
uint32_t interval; // Time in ms to wait between requests
inline TargetHost& operator=(TargetHost& t) {
inline TargetHost& operator=(const TargetHost& t) {
host = t.host;
port = t.port;
timeout = t.timeout;
@@ -66,7 +71,7 @@ protected:
return *this;
}
inline TargetHost(TargetHost& t) :
inline TargetHost(const TargetHost& t) :
host(t.host),
port(t.port),
timeout(t.timeout),
@@ -86,13 +91,13 @@ protected:
interval(interval)
{ }
inline bool operator==(TargetHost& t) {
inline bool operator==(const TargetHost& t) {
if (host != t.host) return false;
if (port != t.port) return false;
return true;
}
inline bool operator!=(TargetHost& t) {
inline bool operator!=(const TargetHost& t) {
if (host != t.host) return true;
if (port != t.port) return true;
return false;
@@ -135,7 +140,7 @@ protected:
}
protected:
uint8_t headRoom[6]; // Buffer to hold MSB-first TCP header
uint8_t headRoom[6] = {0,0,0,0,0,0}; // Buffer to hold MSB-first TCP header
};
struct RequestEntry {
@@ -144,7 +149,7 @@ protected:
TargetHost target;
ModbusTCPhead head;
bool isSyncRequest;
RequestEntry(uint32_t t, ModbusMessage m, TargetHost tg, bool syncReq = false) :
RequestEntry(uint32_t t, const ModbusMessage& m, TargetHost tg, bool syncReq = false) :
token(t),
msg(m),
target(tg),
@@ -153,8 +158,8 @@ protected:
};
// Base addRequest and syncRequest must be present
Error addRequestM(ModbusMessage msg, uint32_t token);
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
Error addRequestM(ModbusMessage msg, uint32_t token) override;
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
// TCP-specific addition "...MT()" including adhoc target - used by bridge
Error addRequestMT(ModbusMessage msg, uint32_t token, IPAddress targetHost, uint16_t targetPort);
ModbusMessage syncRequestMT(ModbusMessage msg, uint32_t token, IPAddress targetHost, uint16_t targetPort);
@@ -174,7 +179,6 @@ protected:
// receive: get response via Client connection
ModbusMessage receive(RequestEntry *request);
void isInstance() { return; } // make class instantiable
queue<RequestEntry *> requests; // Queue to hold requests to be processed
#if USE_MUTEX
mutex qLock; // Mutex to protect queue
@@ -185,6 +189,8 @@ protected:
uint32_t MT_defaultTimeout; // Standard timeout value taken if no dedicated was set
uint32_t MT_defaultInterval; // Standard interval value taken if no dedicated was set
uint16_t MT_qLimit; // Maximum number of requests to accept in queue
uint8_t MT_timeoutsToClose; // 0: disregard, 1-255: number of timeouts to tolerate before
// forcibly closing a connection.
// Let any ModbusBridge class use protected members
template<typename SERVERCLASS> friend class ModbusBridge;

View File

@@ -223,19 +223,17 @@ void onAck(size_t len, uint32_t time) {
}
*/
void ModbusClientTCPasync::onPacket(uint8_t* data, size_t length) {
LOG_D("packet received (len:%d)\n", length);
LOG_D("packet received (len:%u)\n", length);
// reset idle timeout
MTA_lastActivity = millis();
if (length) {
LOG_D("parsing (len:%d)\n", length + 1);
LOG_D("parsing (len:%u)\n", length + 1);
}
while (length > 0) {
RequestEntry* request = nullptr;
ModbusMessage* response = nullptr;
uint16_t transactionID = 0;
uint16_t protocolID = 0;
uint16_t messageLength = 0;
bool isOkay = false;
// 1. Check for valid modbus message
@@ -244,8 +242,8 @@ void ModbusClientTCPasync::onPacket(uint8_t* data, size_t length) {
// total message should fit MBAP plus remaining bytes (in data[4], data[5])
if (length > 6) {
transactionID = (data[0] << 8) | data[1];
protocolID = (data[2] << 8) | data[3];
messageLength = (data[4] << 8) | data[5];
uint16_t protocolID = (data[2] << 8) | data[3];
uint16_t messageLength = (data[4] << 8) | data[5];
if (protocolID == 0 &&
length >= (uint32_t)messageLength + 6 &&
messageLength < 256) {

View File

@@ -83,7 +83,7 @@ protected:
return headRoom;
}
inline ModbusTCPhead& operator= (ModbusTCPhead& t) {
inline ModbusTCPhead& operator= (const ModbusTCPhead& t) {
transactionID = t.transactionID;
protocolID = t.protocolID;
len = t.len;
@@ -91,7 +91,7 @@ protected:
}
protected:
uint8_t headRoom[6]; // Buffer to hold MSB-first TCP header
uint8_t headRoom[6] = {0,0,0,0,0,0}; // Buffer to hold MSB-first TCP header
};
struct RequestEntry {
@@ -100,7 +100,7 @@ protected:
ModbusTCPhead head;
uint32_t sentTime;
bool isSyncRequest;
RequestEntry(uint32_t t, ModbusMessage m, bool syncReq = false) :
RequestEntry(uint32_t t, const ModbusMessage& m, bool syncReq = false) :
token(t),
msg(m),
head(ModbusTCPhead()),
@@ -109,8 +109,8 @@ protected:
};
// Base addRequest and syncRequest both must be present
Error addRequestM(ModbusMessage msg, uint32_t token);
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
Error addRequestM(ModbusMessage msg, uint32_t token) override;
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
// addToQueue: send freshly created request to queue
bool addToQueue(int32_t token, ModbusMessage request, bool syncReq = false);
@@ -121,8 +121,6 @@ protected:
// receive: get response via Client connection
// TCPResponse* receive(uint8_t* data, size_t length);
void isInstance() { return; } // make class instantiable
// TCP handling code, all static taking a class instancs as param
void onConnected();
void onDisconnected();

View File

@@ -6,6 +6,7 @@
#undef LOCAL_LOG_LEVEL
// #define LOCAL_LOG_LEVEL LOG_LEVEL_ERROR
#include "Logging.h"
#include <algorithm>
// Default Constructor - takes optional size of MM_data to allocate memory
ModbusMessage::ModbusMessage(uint16_t dataLen) {
@@ -146,21 +147,19 @@ void ModbusMessage::setServerID(uint8_t serverID) {
}
void ModbusMessage::setFunctionCode(uint8_t FC) {
// We accept here that [0], [1] may allocate bytes!
if (MM_data.empty()) {
MM_data.reserve(3); // At least an error message should fit
if (MM_data.size() < 2) {
MM_data.resize(2); // Resize to at least 2 to ensure indices 0 and 1 are valid
MM_data[0] = 0; // Optional: Invalid server ID as a placeholder
}
// No serverID set yet? use a 0 to initialize it to an error-generating value
if (MM_data.size() < 2) MM_data[0] = 0; // intentional invalid server ID!
MM_data[1] = FC;
MM_data[1] = FC; // Safely set the function code
}
// add() variant to copy a buffer into MM_data. Returns updated size
uint16_t ModbusMessage::add(const uint8_t *arrayOfBytes, uint16_t count) {
uint16_t originalSize = MM_data.size();
MM_data.resize(originalSize + count);
// Copy it
while (count--) {
MM_data.push_back(*arrayOfBytes++);
}
std::copy(arrayOfBytes, arrayOfBytes + count, MM_data.begin() + originalSize);
// Return updated size (logical length of message so far)
return MM_data.size();
}
@@ -181,7 +180,7 @@ uint8_t ModbusMessage::determineFloatOrder() {
uint32_t i = 77230; // int value to go into a float without rounding error
float f = i; // assign it
uint8_t *b = (uint8_t *)&f; // Pointer to bytes of f
uint8_t expect[floatSize] = { 0x47, 0x96, 0xd7, 0x00 }; // IEEE754 representation
const uint8_t expect[floatSize] = { 0x47, 0x96, 0xd7, 0x00 }; // IEEE754 representation
uint8_t matches = 0; // number of bytes successfully matched
// Loop over the bytes of the expected sequence
@@ -225,7 +224,7 @@ uint8_t ModbusMessage::determineDoubleOrder() {
uint64_t i = 5791007487489389; // int64 value to go into a double without rounding error
double f = i; // assign it
uint8_t *b = (uint8_t *)&f; // Pointer to bytes of f
uint8_t expect[doubleSize] = { 0x43, 0x34, 0x92, 0xE4, 0x00, 0x2E, 0xF5, 0x6D }; // IEEE754 representation
const uint8_t expect[doubleSize] = { 0x43, 0x34, 0x92, 0xE4, 0x00, 0x2E, 0xF5, 0x6D }; // IEEE754 representation
uint8_t matches = 0; // number of bytes successfully matched
// Loop over the bytes of the expected sequence
@@ -306,10 +305,7 @@ double ModbusMessage::swapDouble(double& f, int swapRule) {
// add() variant for a vector of uint8_t
uint16_t ModbusMessage::add(vector<uint8_t> v) {
for (auto& b: v) {
MM_data.push_back(b);
}
return MM_data.size();
return add(v.data(), v.size());
}
// add() variants for float and double values

View File

@@ -30,7 +30,6 @@ MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) {
svmap = workerMap.find(ANY_SERVER);
if (svmap != workerMap.end()) {
serverFound = true;
serverID = ANY_SERVER;
}
}
// Did we find a serverID?
@@ -49,7 +48,6 @@ MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) {
if (fcmap != svmap->second.end()) {
// Yes. Return the function pointer for it.
functionCodeFound = true;
functionCode = ANY_FUNCTION_CODE;
}
}
if (functionCodeFound) {
@@ -104,6 +102,11 @@ bool ModbusServer::isServerFor(uint8_t serverID) {
// Is there one?
if (svmap != workerMap.end()) {
return true;
} else {
svmap = workerMap.find(ANY_SERVER);
if (svmap != workerMap.end()) {
return true;
}
}
return false;
}

View File

@@ -68,15 +68,12 @@ protected:
ModbusServer();
// Destructor
~ModbusServer();
virtual ~ModbusServer();
// Prevent copy construction or assignment
ModbusServer(ModbusServer& other) = delete;
ModbusServer& operator=(ModbusServer& other) = delete;
// Virtual function to prevent this class being instantiated
virtual void isInstance() = 0;
std::map<uint8_t, std::map<uint8_t, MBSworker>> workerMap; // map on serverID->functionCode->worker function
uint32_t messageCount; // Number of Requests processed
uint32_t errorCount; // Number of errors responded

View File

@@ -15,8 +15,8 @@
// Create own non-virtual EthernetServer class
class EthernetServerEM : public EthernetServer {
public:
EthernetServerEM(uint16_t port) : EthernetServer(port) { }
void begin(uint16_t port = 0) { }
explicit EthernetServerEM(uint16_t port) : EthernetServer(port) { }
void begin(uint16_t port = 0) override { }
};
#include "ModbusServerTCPtemp.h"

View File

@@ -94,7 +94,7 @@ void ModbusServerRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterv
snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter);
// Start task to handle the client
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 8, &serverTask, coreID >= 0 ? coreID : NULL);
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 8, &serverTask, coreID >= 0 ? coreID : tskNO_AFFINITY);
LOG_D("Server task %d started. Interval=%d\n", (uint32_t)serverTask, MSRinterval);
}
@@ -126,6 +126,12 @@ bool ModbusServerRTU::isModbusASCII() {
return MSRuseASCII;
}
// set timeout
void ModbusServerRTU::setModbusTimeout(unsigned long timeout)
{
serverTimeout = timeout;
}
// Toggle skipping of leading 0x00 byte
void ModbusServerRTU::skipLeading0x00(bool onOff) {
MSRskipLeadingZeroByte = onOff;
@@ -231,8 +237,8 @@ void ModbusServerRTU::serve(ModbusServerRTU *myServer) {
response = m;
}
} else {
// No callback. Is at least the serverID valid and no broadcast?
if (myServer->isServerFor(request[0]) && request[0] != 0x00) {
// No callback. Is at least the serverID valid?
if (myServer->isServerFor(request[0])) {
// Yes. Send back a ILLEGAL_FUNCTION error
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_FUNCTION);
}

View File

@@ -47,6 +47,9 @@ public:
// Inquire protocol mode
bool isModbusASCII();
// set timeout
void setModbusTimeout(unsigned long timeout);
// Toggle skipping of leading 0x00 byte
void skipLeading0x00(bool onOff = true);
@@ -61,8 +64,6 @@ protected:
ModbusServerRTU(ModbusServerRTU& m) = delete;
ModbusServerRTU& operator=(ModbusServerRTU& m) = delete;
inline void isInstance() { } // Make class instantiable
// internal common begin function
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);

View File

@@ -32,7 +32,7 @@ ModbusServerTCPasync::mb_client::~mb_client() {
void ModbusServerTCPasync::mb_client::onData(uint8_t* data, size_t len) {
lastActiveTime = millis();
LOG_D("data len %d\n", len);
LOG_D("data len %u\n", len);
Error error = SUCCESS;
size_t i = 0;
@@ -250,7 +250,7 @@ void ModbusServerTCPasync::onClientConnect(AsyncClient* client) {
LOCK_GUARD(lock1, cListLock);
if (clients.size() < maxNoClients) {
clients.emplace_back(new mb_client(this, client));
LOG_D("nr clients: %d\n", clients.size());
LOG_D("nr clients: %u\n", clients.size());
} else {
LOG_D("max number of clients reached, closing new\n");
client->close(true);
@@ -264,5 +264,5 @@ void ModbusServerTCPasync::onClientDisconnect(mb_client* client) {
clients.remove_if([client](mb_client* i) { return i->client == client->client; });
// delete client itself
delete client;
LOG_D("nr clients: %d\n", clients.size());
LOG_D("nr clients: %u\n", clients.size());
}

View File

@@ -76,7 +76,6 @@ class ModbusServerTCPasync : public ModbusServer {
bool isRunning();
protected:
inline void isInstance() { }
void onClientConnect(AsyncClient* client);
void onClientDisconnect(mb_client* client);

View File

@@ -44,8 +44,6 @@ protected:
ModbusServerTCP(ModbusServerTCP& m) = delete;
ModbusServerTCP& operator=(ModbusServerTCP& m) = delete;
inline void isInstance() { }
uint8_t numClients;
TaskHandle_t serverTask;
uint16_t serverPort;
@@ -159,7 +157,7 @@ template <typename ST, typename CT>
snprintf(taskName, 18, "MBserve%04X", port);
// Start task to handle the client
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 5, &serverTask, coreID >= 0 ? coreID : NULL);
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 5, &serverTask, coreID >= 0 ? coreID : tskNO_AFFINITY);
LOG_D("Server task %s started (%d).\n", taskName, (uint32_t)serverTask);
// Wait two seconds for it to establish
@@ -206,7 +204,7 @@ bool ModbusServerTCP<ST, CT>::accept(CT& client, uint32_t timeout, int coreID) {
snprintf(taskName, 18, "MBsrv%02Xclnt", i);
// Start task to handle the client
xTaskCreatePinnedToCore((TaskFunction_t)&worker, taskName, SERVER_TASK_STACK, clients[i], 5, &clients[i]->task, coreID >= 0 ? coreID : NULL);
xTaskCreatePinnedToCore((TaskFunction_t)&worker, taskName, SERVER_TASK_STACK, clients[i], 5, &clients[i]->task, coreID >= 0 ? coreID : tskNO_AFFINITY);
LOG_D("Started client %d task %d\n", i, (uint32_t)(clients[i]->task));
return true;

View File

@@ -109,8 +109,8 @@ const uint8_t swapTables[8][8] = {
enum FCType : uint8_t {
FC01_TYPE, // Two uint16_t parameters (FC 0x01, 0x02, 0x03, 0x04, 0x05, 0x06)
FC07_TYPE, // no additional parameter (FCs 0x07, 0x0b, 0x0c, 0x11)
FC0F_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint16_t* pointer to array of bytes (FC 0x0f)
FC10_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint8_t* pointer to array of words (FC 0x10)
FC0F_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint8_t* pointer to array of bytes (FC 0x0f)
FC10_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint16_t* pointer to array of words (FC 0x10)
FC16_TYPE, // three uint16_t parameters (FC 0x16)
FC18_TYPE, // one uint16_t parameter (FC 0x18)
FCGENERIC, // for FCs not yet explicitly coded (or too complex)

View File

@@ -3,13 +3,13 @@
// MIT license - see license.md for details
// =================================================================================================
#include "options.h"
#if HAS_FREERTOS
#include "ModbusMessage.h"
#include "RTUutils.h"
#undef LOCAL_LOG_LEVEL
// #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
#include "Logging.h"
#if HAS_FREERTOS
// calcCRC: calculate Modbus CRC16 on a given array of bytes
uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) {
// CRC16 pre-calculated tables
@@ -57,10 +57,9 @@ uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) {
uint8_t crcHi = 0xFF;
uint8_t crcLo = 0xFF;
uint8_t index;
while (len--) {
index = crcLo ^ *data++;
uint8_t index = crcLo ^ *data++;
crcLo = crcHi ^ crcHiTable[index];
crcHi = crcLoTable[index];
}
@@ -464,4 +463,5 @@ const char RTUutils::ASCIIread[] = {
const char RTUutils::ASCIIwrite[] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46
};
#endif

View File

@@ -5,9 +5,6 @@
#ifndef _RTU_UTILS_H
#define _RTU_UTILS_H
#include <stdint.h>
#if NEED_UART_PATCH
#include <soc/uart_struct.h>
#endif
#include <vector>
#include "Stream.h"
#include "ModbusTypeDefs.h"
@@ -52,11 +49,13 @@ public:
// RTSauto: dummy callback for auto half duplex RS485 boards
inline static void RTSauto(bool level) { return; } // NOLINT
#if HAS_FREERTOS
// Necessary preparations for a HardwareSerial
static void prepareHardwareSerial(HardwareSerial& s, uint16_t bufferSize = 260) {
s.setRxBufferSize(bufferSize);
s.setTxBufferSize(bufferSize);
}
#endif
protected:
// Printable characters for ASCII protocol: 012345678ABCDEF

View File

@@ -12,7 +12,6 @@
#define HAS_FREERTOS 1
#define HAS_ETHERNET 1
#define IS_LINUX 0
#define NEED_UART_PATCH 1
const unsigned int SERVER_TASK_STACK = 4096;
const unsigned int CLIENT_TASK_STACK = 4096;
@@ -23,7 +22,6 @@ const unsigned int CLIENT_TASK_STACK = 4096;
#define HAS_FREERTOS 0
#define HAS_ETHERNET 0
#define IS_LINUX 0
#define NEED_UART_PATCH 0
/* === LINUX DEFINITIONS AND MACROS === */
#elif defined(__linux__)
@@ -31,7 +29,6 @@ const unsigned int CLIENT_TASK_STACK = 4096;
#define HAS_FREERTOS 0
#define HAS_ETHERNET 0
#define IS_LINUX 1
#define NEED_UART_PATCH 0
#include <cstdio> // for printf()
#include <cstring> // for memcpy(), strlen() etc.
#include <cinttypes> // for uint32_t etc.

View File

@@ -74,7 +74,7 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint
MqttClient::~MqttClient() {
disconnect(true);
_clearQueue(2);
clearQueue(true);
#if defined(ARDUINO_ARCH_ESP32)
vSemaphoreDelete(_xSemaphore);
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {

View File

@@ -122,7 +122,6 @@ void SyslogService::remove_queued_messages(uuid::log::Level level) {
}
log_message_id_ -= offset;
log_message_fails_ += offset;
}
void SyslogService::log_level(uuid::log::Level level) {

View File

@@ -243,6 +243,9 @@ class AsyncWebServer {
void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) {};
};
#ifndef SSE_MAX_QUEUED_MESSAGES
#define SSE_MAX_QUEUED_MESSAGES 32
#endif
class AsyncEventSource : public AsyncWebHandler {
public:
@@ -252,6 +255,9 @@ class AsyncEventSource : public AsyncWebHandler {
size_t count() const {
return 1;
}
size_t avgPacketsWaiting() const {
return 0;
};
void send(const char * message, const char * event = NULL, uint32_t id = 0, uint32_t reconnect = 0) {};
};

View File

@@ -42,8 +42,15 @@ build_flags =
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1 ; force async_tcp task to be on same core as Arduino app (default is any core)
-D CONFIG_ASYNC_TCP_STACK_SIZE=6144 ; stack usage measured: ESP32: ~2.3K, ESP32S3: ~3.5k - (default is 16K)
; ESPAsyncWebServer
-D WS_MAX_QUEUED_MESSAGES=0 ; log messages are already queued in ems-esp
; -D WS_MAX_QUEUED_MESSAGES=0 ; not used, default 8
; -D SSE_MAX_QUEUED_MESSAGES=1 ; for log messages, default 32
-D CORE_DEBUG_LEVEL=0
-D EMSESP_SCHEDULER_RUNNING_CORE=1
-D EMSESP_SCHEDULER_STACKSIZE=8192
-D EMSESP_MQTT_RUNNING_CORE=1 ; default 1
; -D EMSESP_MQTT_STACKSIZE=5120 ; default
-D EMSESP_UART_RUNNING_CORE=1 ; default any core
; -D EMSESP_UART_STACKSIZE=2560 ; default
unbuild_flags =
${common.core_unbuild_flags}

View File

@@ -23,6 +23,7 @@ static String generateClientId() {
MqttSettingsService::~MqttSettingsService() {
delete _mqttClient;
_mqttClient = nullptr;
}
void MqttSettingsService::begin() {
@@ -34,7 +35,7 @@ void MqttSettingsService::startClient() {
static bool isSecure = false;
if (_mqttClient != nullptr) {
// do we need to change the client?
if ((isSecure && _state.enableTLS) || (!isSecure && !_state.enableTLS)) {
if (_state.enabled && ((isSecure && _state.enableTLS) || (!isSecure && !_state.enableTLS))) {
return;
}
delete _mqttClient;
@@ -43,10 +44,10 @@ void MqttSettingsService::startClient() {
#ifndef TASMOTA_SDK
if (_state.enableTLS) {
isSecure = true;
if (emsesp::EMSESP::system_.PSram() > 0) {
_mqttClient = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::YES);
} else {
if (emsesp::EMSESP::system_.PSram() == 0) {
_mqttClient = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO);
} else {
_mqttClient = new espMqttClientSecure(EMSESP_MQTT_PRIORITY, EMSESP_MQTT_RUNNING_CORE);
}
if (!_mqttClient) {
emsesp::EMSESP::logger().warning("MQTT Client alloc failed");
@@ -63,10 +64,10 @@ void MqttSettingsService::startClient() {
}
#endif
isSecure = false;
if (emsesp::EMSESP::system_.PSram() > 0) {
_mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::YES);
} else {
if (emsesp::EMSESP::system_.PSram() == 0) {
_mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::NO);
} else {
_mqttClient = new espMqttClient(EMSESP_MQTT_PRIORITY, EMSESP_MQTT_RUNNING_CORE);
}
static_cast<espMqttClient *>(_mqttClient)->onConnect([this](bool sessionPresent) { onMqttConnect(sessionPresent); });
static_cast<espMqttClient *>(_mqttClient)->onDisconnect([this](espMqttClientTypes::DisconnectReason reason) { onMqttDisconnect(reason); });
@@ -78,6 +79,9 @@ void MqttSettingsService::startClient() {
}
void MqttSettingsService::loop() {
if (!_state.enabled || _mqttClient == nullptr || emsesp::EMSESP::system_.systemStatus() != 0) {
return;
}
if (_reconfigureMqtt || (_disconnectedAt && static_cast<uint32_t>(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
// reconfigure MQTT client
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
@@ -93,11 +97,11 @@ bool MqttSettingsService::isEnabled() {
}
bool MqttSettingsService::isConnected() {
return _mqttClient->connected();
return _mqttClient ? _mqttClient->connected() : false;
}
const char * MqttSettingsService::getClientId() {
return _mqttClient->getClientId();
return _mqttClient ? _mqttClient->getClientId() : "";
}
void MqttSettingsService::onMqttMessage(const espMqttClientTypes::MessageProperties & properties,

View File

@@ -50,6 +50,19 @@
#define FACTORY_MQTT_MAX_TOPIC_LENGTH 128
#endif
#ifndef EMSESP_MQTT_RUNNING_CORE
#define EMSESP_MQTT_RUNNING_CORE 1
#endif
#ifdef EMSESP_MQTT_STACKSIZE
#undef EMC_TASK_STACK_SIZE
#define EMC_TASK_STACK_SIZE EMSESP_MQTT_STACKSIZE
#endif
#ifndef EMSESP_MQTT_PRIORITY
#define EMSESP_MQTT_PRIORITY 1
#endif
class MqttSettings {
public:
bool enabled;

View File

@@ -25,7 +25,7 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
switch (event) {
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
case ARDUINO_EVENT_ETH_DISCONNECTED:
if (_connected) {
if (_connected && emsesp::EMSESP::system_.ntp_connected()) {
emsesp::EMSESP::logger().info("WiFi connection dropped, stopping NTP");
_connected = false;
configureNTP();

View File

@@ -23,7 +23,25 @@ namespace emsesp {
uuid::log::Logger AnalogSensor::logger_{F_(analogsensor), uuid::log::Facility::DAEMON};
void AnalogSensor::start() {
void AnalogSensor::start(const bool factory_settings) {
// if (factory_settings && EMSESP::nvs_.getString("boot").equals("E32V2_2") && EMSESP::nvs_.getString("hwrevision").equals("3.0")) {
if (factory_settings && analogReadMilliVolts(39) > 700) { // core voltage > 2.6V
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
auto newSensor = AnalogCustomization();
newSensor.gpio = 39;
newSensor.name = "core_voltage";
newSensor.offset = 0;
newSensor.factor = 0.00377136; // Divider 24k - 8,66k
newSensor.uom = DeviceValueUOM::VOLTS;
newSensor.type = AnalogType::ADC;
settings.analogCustomizations.push_back(newSensor);
newSensor.gpio = 36;
newSensor.name = "supply_voltage";
newSensor.factor = 0.017; // Divider 24k - 1,5k
settings.analogCustomizations.push_back(newSensor);
return StateUpdateResult::CHANGED; // persist the change
});
}
reload(true); // fetch the list of sensors from our customization service
if (!analog_enabled_) {

View File

@@ -127,7 +127,7 @@ class AnalogSensor {
RGB = 11
};
void start();
void start(const bool factory_settings = false);
void loop();
void publish_sensor(const Sensor & sensor) const;
void publish_values(const bool force);
@@ -168,11 +168,11 @@ class AnalogSensor {
void store_counters();
private:
static constexpr double Beta = 3380;
static constexpr double Beta = 4260;
static constexpr double T0 = 273.15;
static constexpr double T25 = 298.15;
static constexpr double R0 = 10000;
static constexpr double Rt = 10000;
static constexpr double R0 = 100000;
static constexpr double Rt = 60000;
static constexpr uint8_t MAX_SENSORS = 20;
static constexpr uint32_t MEASURE_ANALOG_INTERVAL = 500;

View File

@@ -302,7 +302,7 @@ static void setup_commands(std::shared_ptr<Commands> const & commands) {
std::vector<int8_t> data; // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
std::string board_profile = Helpers::toUpper(arguments.front());
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
shell.println("Invalid board profile (S32, E32, E32V2, MH-ET, NODEMCU, LOLIN, OLIMEX, OLIMEXPOE, C3MINI, S2MINI, S3MINI, S32S3, CUSTOM)");
shell.println("Invalid board profile (S32, E32, E32V2, E32V3, MH-ET, NODEMCU, LOLIN, OLIMEX, OLIMEXPOE, C3MINI, S2MINI, S3MINI, S32S3, CUSTOM)");
return;
}

View File

@@ -293,7 +293,7 @@ enum {
#ifndef STRINGIZE
#define STRINGIZE(s) #s
#endif
#if TASMOTA_SDK
#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)

View File

@@ -139,6 +139,7 @@
{164, DeviceType::SOLAR, "SM200, MS200", DeviceFlags::EMS_DEVICE_FLAG_SM100},
// Mixer Modules - 0x20-0x27 for HC, 0x28-0x29 for WWC and 0x11 for the MP100
{ 8, DeviceType::MIXER, "XCUMixer", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS}, // integrated in XCU_THH (CS6800, etc.)
{ 69, DeviceType::MIXER, "MM10", DeviceFlags::EMS_DEVICE_FLAG_MM10},
{100, DeviceType::MIXER, "IPM", DeviceFlags::EMS_DEVICE_FLAG_IPM},
{102, DeviceType::MIXER, "IPM2", DeviceFlags::EMS_DEVICE_FLAG_IPM},
@@ -157,7 +158,7 @@
{16, DeviceType::HEATPUMP, "CSH5800iG", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Ventilation - 0x51
{231, DeviceType::VENTILATION, "Logavent HRV176", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{231, DeviceType::VENTILATION, "HRV176, HRV156, 5000c, MV200", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Heatsource - 0x60
{228, DeviceType::HEATSOURCE, "AM200", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // alternative heatsource

View File

@@ -1711,8 +1711,8 @@ void EMSESP::start() {
mqtt_.start(); // mqtt init
system_.start(); // starts commands, led, adc, button, network (sets hostname), syslog & uart
shower_.start(); // initialize shower timer and shower alert
temperaturesensor_.start(); // Temperature external sensors
analogsensor_.start(); // Analog external sensors
temperaturesensor_.start(factory_settings); // Temperature external sensors
analogsensor_.start(factory_settings); // Analog external sensors
// start web services
webLogService.start(); // apply settings to weblog service

View File

@@ -524,7 +524,12 @@ bool Helpers::value2number(const char * value, int & value_i, const int min, con
return false;
}
if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x') {
value_i = hextoint(value);
} else {
value_i = atoi(value);
}
if (value_i >= min && value_i <= max) {
return true;
}

View File

@@ -796,6 +796,7 @@ MAKE_TRANSLATION(pumpStatus, "pumpstatus", "pump status (PC1)", "Pumpenstatus HK
MAKE_TRANSLATION(mixerStatus, "valvestatus", "mixing valve actuator (VC1)", "Mischerventilposition (VC1)", "positie mixerklep (VC1)", "Shuntventil Status (VC1)", "siłownik zaworu mieszającego (VC1)", "shuntventil status (VC1)", "actionnement vanne mélangeur (VC1)", "karışım vanası aktüatörü (VC1)", "posizione valvola miscela (VC1)", "pohon zmiešavacieho ventilu (VC1)", "pohon směšovacího ventilu (VC1)")
MAKE_TRANSLATION(flowTempVf, "flowtempvf", "flow temperature in header (T0/Vf)", "Vorlauftemperatur am Verteiler (T0/Vf)", "aanvoertemperatuur verdeler (T0/Vf)", "Flödestemperatur Fördelare (T0/Vf)", "temperatura zasilania na rozdzielaczu (T0/Vf)", "turtemperatur ved fordeleren (T0/Vf)", "température départ collecteur (T0/Vf)", "başlıkta akış sıcaklığı", "Temperatura di mandata al distributore (T0/Vf)", "teplota prívodu v zberači (T0/Vf)", "teplota přívodu v hlavici (T0/Vf)")
MAKE_TRANSLATION(mixerSetTime, "valvesettime", "time to set valve", "Zeit zum einstellen des Ventils", "Inschakeltijd mengklep", "Inställningstid Shuntventil", "czas na ustawienie zaworu", "instillningstid ventil", "délai activation vanne", "vana ayar zamanı", "ritardo attivazione valvola", "čas na nastavenie ventilu", "čas pro nastavení ventilu")
MAKE_TRANSLATION(setDiffPress, "setdiffpress", "set differential pressure", "Pumpensolldruck", "", "Tryckskillnad", "różnica ciśnień", "", "", "", "", "nastaviť diferenčný tlak", "nastavení rozdílového tlaku") // TODO translate
// mixer pool
MAKE_TRANSLATION(poolSetTemp, "poolsettemp", "pool set temperature", "Sollwert Pooltemperatur", "Streeftemperatuur zwembad", "Pool Temperatur Börvärde", "zadana temperatura basenu", "valgt temp basseng", "température consigne piscine", "hedef havuz sıcaklığı", "temperatura nominale piscina", "nastavená teplota bazéna", "cílová teplota bazénu")

View File

@@ -1332,7 +1332,7 @@ bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp,
// add hvac_action - https://github.com/emsesp/EMS-ESP32/discussions/2562
doc["action_topic"] = "~/boiler_data";
doc["action_template"] = "{% if value_json.heatingactive=='on'%}heating{%else%}idle{%endif%}";
doc["action_template"] = "{% if value_json.hpactivity=='cooling'%}cooling{%elif value_json.heatingactive=='on'%}heating{%else%}idle{%endif%}";
// the HA climate component only responds to auto, heat and off
JsonArray modes = doc["modes"].to<JsonArray>();

View File

@@ -50,6 +50,10 @@
#include <HTTPClient.h>
#ifndef EMSESP_STANDALONE
#include "esp_efuse.h"
#endif
namespace emsesp {
// Languages supported. Note: the order is important
@@ -1426,6 +1430,15 @@ bool System::command_service(const char * cmd, const char * value) {
ok = true;
}
}
int n;
if (!ok && Helpers::value2number(value, n)) {
#ifndef EMSESP_STANDALONE
if (!strcmp(cmd, "fuse/mfg")) { // && esp_efuse_read_reg(EFUSE_BLK3, 0) == 0) {
ok = esp_efuse_write_reg(EFUSE_BLK3, 0, (uint32_t)n) == ESP_OK;
LOG_INFO("fuse programed with value '%X': %s", n, ok ? "successful" : "failed");
}
#endif
}
if (ok) {
LOG_INFO("System command '%s' with value '%s'", cmd, value);
@@ -1880,6 +1893,8 @@ bool System::load_board_profile(std::vector<int8_t> & data, const std::string &
data = {2, 4, 5, 17, 33, PHY_type::PHY_TYPE_LAN8720, 16, 1, 0, 0}; // BBQKees Gateway E32
} else if (board_profile == "E32V2") {
data = {2, 14, 4, 5, 34, PHY_type::PHY_TYPE_LAN8720, 15, 0, 1, 0}; // BBQKees Gateway E32 V2
} else if (board_profile == "E32V2_2") {
data = {32, 14, 4, 5, 34, PHY_type::PHY_TYPE_LAN8720, 15, 0, 1, 1}; // BBQKees Gateway E32 V2.2, rgb led
} else if (board_profile == "MH-ET") {
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0, 0}; // MH-ET Live D1 Mini
} else if (board_profile == "NODEMCU") {
@@ -2030,6 +2045,7 @@ bool System::ntp_connected() {
// see if its a BBQKees Gateway by checking the nvs values
String System::getBBQKeesGatewayDetails() {
#ifndef EMSESP_STANDALONE
/*
if (!EMSESP::nvs_.isKey("mfg")) {
return "";
}
@@ -2043,6 +2059,26 @@ String System::getBBQKeesGatewayDetails() {
}
return "BBQKees Gateway Model " + EMSESP::nvs_.getString("model") + " v" + EMSESP::nvs_.getString("hwrevision") + "/" + EMSESP::nvs_.getString("batch");
*/
union {
struct {
uint32_t no : 4;
uint32_t month : 4;
uint32_t year : 8;
uint32_t rev_minor : 4;
uint32_t rev_major : 4;
uint32_t model : 4;
uint32_t mfg : 4;
};
uint32_t reg;
} gw;
gw.reg = esp_efuse_read_reg(EFUSE_BLK3, 0);
const char * mfg[] = {"unknown", "BBQKees Electronics", "", "", "", "", "", ""};
const char * model[] = {"unknown", "S3", "E32V2", "E32V2.2", "S32", "E32", "", "", ""};
return gw.reg ? String(mfg[gw.mfg]) + " " + String(model[gw.model]) + " rev." + String(gw.rev_major) + "." + String(gw.rev_minor) + "/"
+ String(2000 + gw.year) + "-" + String(gw.month) + "-" + String(gw.no) + " (fuse 0x" + String(gw.reg, 16) + ")"
: "";
#else
return "";
#endif
@@ -2161,7 +2197,7 @@ bool System::readCommand(const char * data) {
// first check deviceID
if ((p = strtok(data_args, " ,"))) { // delimiter comma or space
strlcpy(value, p, 10); // get string
strlcpy(value, p, sizeof(value)); // get string
device_id = (uint8_t)Helpers::hextoint(value); // convert hex to int
if (!EMSESP::valid_device(device_id)) {
LOG_ERROR("Invalid device ID (0x%02X) in read command", device_id);
@@ -2173,7 +2209,7 @@ bool System::readCommand(const char * data) {
uint8_t num_args = 0;
while (p != 0) {
if ((p = strtok(nullptr, " ,"))) { // delimiter comma or space
strlcpy(value, p, 10); // get string
strlcpy(value, p, sizeof(value)); // get string
if (num_args == 0) {
type_id = (uint16_t)Helpers::hextoint(value); // convert hex to int
} else if (num_args == 1) {

View File

@@ -649,7 +649,7 @@ uint16_t TxService::read_next_tx(const uint8_t offset, const uint8_t length) {
return 0;
}
// we request all and get a short telegram with requested offset
if ((next_length + next_offset) == 0xFF && old_length < max_length - 1 && offset <= telegram_last_->offset) {
if ((next_length + next_offset) == 0xFF && old_length < max_length - 4 && offset <= telegram_last_->offset) {
return 0;
}
if (offset >= telegram_last_->offset && old_length > 0 && next_length > 0) {

View File

@@ -32,7 +32,9 @@ namespace emsesp {
uuid::log::Logger TemperatureSensor::logger_{F_(temperaturesensor), uuid::log::Facility::DAEMON};
// start the 1-wire
void TemperatureSensor::start() {
void TemperatureSensor::start(const bool factory_settings) {
// set_internal_ = factory_settings && EMSESP::nvs_.getString("boot").equals("E32V2_2") && EMSESP::nvs_.getString("hwrevision").equals("3.0");
set_internal_ = factory_settings && analogReadMilliVolts(39) > 700; // core voltage > 2.6V
reload();
if (!dallas_gpio_) {
@@ -199,6 +201,31 @@ void TemperatureSensor::loop() {
scancnt_ = 0;
} else if (scancnt_ == SCAN_START + 1) { // startup
firstscan_ = sensors_.size();
if (firstscan_ > 0 && set_internal_) {
set_internal_ = false;
Sensor * s = &sensors_[0];
if (firstscan_ > 1) {
std::string s_nvs = EMSESP::nvs_.getString("intTemp").c_str();
for (uint8_t i = 0; i < firstscan_; i++) {
if (s_nvs == sensors_[i].id()) {
s = &sensors_[i];
break;
}
}
}
s->set_name("gateway_temperature");
if (!EMSESP::nvs_.isKey("intTemp")) {
EMSESP::nvs_.putString("intTemp", s->id().c_str());
}
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
auto newSensor = SensorCustomization();
newSensor.id = s->id();
newSensor.name = s->name();
newSensor.offset = 0;
settings.sensorCustomizations.push_back(newSensor);
return StateUpdateResult::CHANGED;
});
}
// LOG_DEBUG("Adding %d sensor(s) from first scan", firstscan_);
} else if ((scancnt_ <= 0) && (firstscan_ != sensors_.size())) { // check 2 times for no change of sensor #
scancnt_ = SCAN_START;

View File

@@ -77,7 +77,7 @@ class TemperatureSensor {
TemperatureSensor() = default;
~TemperatureSensor() = default;
void start();
void start(const bool factory_settings = false);
void loop();
void publish_sensor(const Sensor & sensor);
void publish_values(const bool force);
@@ -170,6 +170,7 @@ class TemperatureSensor {
bool changed_ = false;
uint32_t sensorfails_ = 0;
uint32_t sensorreads_ = 0;
bool set_internal_ = false;
};
} // namespace emsesp

View File

@@ -52,7 +52,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
// not ems1.0, but HT3
if (model() != EMSdevice::EMS_DEVICE_FLAG_EMS) {
register_telegram_type(0x26, "UBASettingsWW", true, MAKE_PF_CB(process_UBASettingsWW));
register_telegram_type(0x27, "UBASettingsWW", true, MAKE_PF_CB(process_UBASettingsWW));
register_telegram_type(0x2A, "MC110Status", false, MAKE_PF_CB(process_MC110Status));
}
@@ -1365,7 +1365,7 @@ void Boiler::process_UBAParameters(std::shared_ptr<const Telegram> telegram) {
* Boiler(0x08) -> Me(0x0B), ?(0x26), data: 01 05 00 0F 00 1E 58 5A
*/
void Boiler::process_UBASettingsWW(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, wwMaxPower_, 7);
has_update(telegram, wwMaxPower_, 10);
}
// 0x33
@@ -2578,7 +2578,7 @@ bool Boiler::set_ww_maxpower(const char * value, const int8_t id) {
return false;
}
write_command(EMS_TYPE_UBASettingsWW, 7, v, EMS_TYPE_UBASettingsWW);
write_command(EMS_TYPE_UBASettingsWW, 10, v, EMS_TYPE_UBASettingsWW);
return true;
}
@@ -2839,7 +2839,7 @@ bool Boiler::set_ww_disinfect(const char * value, const int8_t id) {
}
if (is_received(EMS_TYPE_UBAParameterWWPlus)) {
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x44 : 0x04), 0xE9); // not sure if this is in flags
write_command(0x05, 44, (v ? 0xFF : 0xFE), 0xE9); // https://github.com/emsesp/EMS-ESP32/discussions/2601
} else {
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x44 : 0x04), 0x34);
}
@@ -2916,11 +2916,11 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
return true; // dash
} else if (num == 1) {
// LOG_INFO("Reset boiler maintenance message");
write_command(0x05, 0x08, 0xFF, 0x1C);
write_command(0x05, 8, 0xFF, 0x1C);
return true;
} else if (num == 2) {
// LOG_INFO("Reset boiler error message");
write_command(0x05, 0x00, 0x5A); // error reset
write_command(0x05, 0, 0x5A); // error reset
return true;
} else if (num == 3) {
// LOG_INFO("Reset boiler history");
@@ -2928,7 +2928,11 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
return true;
} else if (num == 4) {
// LOG_INFO("Reset boiler message");
write_command(0x05, 0x08, 0xFF); // same as maintenance
write_command(0x05, 8, 0xFF); // same as maintenance
return true;
} else if (num == 5) {
// LOG_INFO("Factory Reset");
write_command(0x05, 6, 154);
return true;
}
return false;

View File

@@ -43,7 +43,7 @@ class Boiler : public EMSdevice {
uint8_t boilerState_ = EMS_VALUE_UINT8_NOTSET; // Boiler state flag - FOR INTERNAL USE
static constexpr uint8_t EMS_TYPE_UBASettingsWW = 0x26;
static constexpr uint8_t EMS_TYPE_UBASettingsWW = 0x27;
static constexpr uint8_t EMS_TYPE_UBAParameterWW = 0x33;
static constexpr uint8_t EMS_TYPE_UBAFunctionTest = 0x1D;
static constexpr uint8_t EMS_TYPE_UBAFlags = 0x35;

View File

@@ -37,6 +37,9 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT8, FL_(flowSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowSetTemp));
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, FL_(pumpStatus), DeviceValueUOM::NONE, MAKE_CF_CB(set_pump));
register_device_value(tag, &activated_, DeviceValueType::BOOL, FL_(activated), DeviceValueUOM::NONE, MAKE_CF_CB(set_activated));
register_device_value(tag, &flowRate_, DeviceValueType::UINT16, FL_(flow), DeviceValueUOM::LH);
register_device_value(
tag, &pressure_, DeviceValueType::UINT8, DeviceValueNumOp::DV_NUMOP_MUL50, FL_(setDiffPress), DeviceValueUOM::MBAR, MAKE_CF_CB(set_pressure));
register_device_value(tag,
&setValveTime_,
DeviceValueType::UINT8,
@@ -47,6 +50,7 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
10,
600);
register_device_value(tag, &flowTempOffset_, DeviceValueType::UINT8, FL_(flowtempoffset), DeviceValueUOM::K, MAKE_CF_CB(set_flowTempOffset), 0, 20);
// EMSESP::send_read_request(device_id - 0x20 + 0x02CD, device_id, 0, 3);
}
// EMS 1.0
@@ -91,6 +95,7 @@ void Mixer::process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> teleg
has_update(telegram, flowSetTemp_, 5);
has_bitupdate(telegram, pumpStatus_, 0, 0);
has_update(telegram, status_, 2); // valve status
has_update(telegram, flowRate_, 9); // l/h
}
// Mixer IPM - 0x010C
@@ -152,6 +157,7 @@ void Mixer::process_MMPLUSConfigMessage_HC(std::shared_ptr<const Telegram> teleg
has_update(telegram, activated_, 0); // on = 0xFF
has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, default 120 s, max 600 s
has_update(telegram, flowTempOffset_, 2); // Mixer increase [0-20 K]
has_update(telegram, pressure_, 9);
}
// Thermostat(0x10) -> Mixer(0x20), ?(0x2E1), data: 01 1C 64 00 01
@@ -270,4 +276,14 @@ bool Mixer::set_flowTempOffset(const char * value, const int8_t id) {
return false;
}
bool Mixer::set_pressure(const char * value, const int8_t id) {
int v;
if (!Helpers::value2number(value, v)) {
return false;
}
uint8_t hc = device_id() - 0x20;
write_command(0x2CD + hc, 9, v / 50, 0x2CD + hc);
return true;
}
} // namespace emsesp

View File

@@ -45,6 +45,7 @@ class Mixer : public EMSdevice {
bool set_activated(const char * value, const int8_t id);
bool set_setValveTime(const char * value, const int8_t id);
bool set_flowTempOffset(const char * value, const int8_t id);
bool set_pressure(const char * value, const int8_t id);
private:
uint16_t flowTempHc_;
@@ -55,6 +56,8 @@ class Mixer : public EMSdevice {
uint8_t activated_;
uint8_t setValveTime_;
uint8_t flowTempOffset_;
uint16_t flowRate_; // l/h
uint8_t pressure_; // setting 150-750mbar, scale 50
};
} // namespace emsesp

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.7.3-dev.14"
#define EMSESP_APP_VERSION "3.7.3-dev.16"

View File

@@ -33,6 +33,7 @@
namespace emsesp {
uint8_t EMSuart::last_tx_src_ = 0;
static TaskHandle_t xHandle;
static QueueHandle_t uart_queue;
uint8_t tx_mode_ = 0xFF;
uint32_t inverse_mask = 0;
@@ -94,12 +95,18 @@ 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, 129, 0, (EMS_MAXBUFFERSIZE + 1) * 2, &uart_queue, 0); // buffer must be > fifo
uart_driver_install(EMSUART_NUM, UART_FIFO_LEN + 1, 0, (EMS_MAXBUFFERSIZE + 1) * 2, &uart_queue, 0); // buffer must be > fifo
uart_set_rx_full_threshold(EMSUART_NUM, 1);
uart_set_rx_timeout(EMSUART_NUM, 0); // disable
// note esp32s3 crashes with 2k stacksize, stack overflow here sometimes wipes settingsfiles.
xTaskCreate(uart_event_task, "uart_event_task", 2560, NULL, configMAX_PRIORITIES - 1, NULL);
#if defined(CONFIG_FREERTOS_UNICORE) || (EMSESP_UART_RUNNING_CORE < 0)
xTaskCreate(uart_event_task, "uart_event_task", EMSESP_UART_STACKSIZE, NULL, EMSESP_UART_PRIORITY, &xHandle);
#else
xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", EMSESP_UART_STACKSIZE, NULL, EMSESP_UART_PRIORITY, &xHandle, EMSESP_UART_RUNNING_CORE);
#endif
} else {
vTaskResume(xHandle);
}
tx_mode_ = tx_mode;
uart_enable_intr_mask(EMSUART_NUM, UART_BRK_DET_INT_ENA | UART_RXFIFO_FULL_INT_ENA);
@@ -111,7 +118,7 @@ void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t
void EMSuart::stop() {
if (tx_mode_ != 0xFF) { // only call after driver initialisation
uart_disable_intr_mask(EMSUART_NUM, UART_BRK_DET_INT_ENA | UART_RXFIFO_FULL_INT_ENA);
// TODO should we xTaskSuspend() the event task here?
vTaskSuspend(xHandle);
}
};
@@ -139,7 +146,7 @@ void EMSuart::send_poll(const uint8_t data) {
* buf contains the CRC and len is #bytes including the CRC
* returns code, 1=success
*/
uint16_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
uint8_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
if (len == 0 || len >= EMS_MAXBUFFERSIZE) {
return EMS_TX_STATUS_ERR;
}

View File

@@ -24,6 +24,17 @@
#ifndef EMSESP_EMSUART_H
#define EMSESP_EMSUART_H
#ifndef EMSESP_UART_RUNNING_CORE
#define EMSESP_UART_RUNNING_CORE -1
#endif
#ifndef EMSESP_UART_STACKSIZE
#define EMSESP_UART_STACKSIZE 2560
#endif
#ifndef EMSESP_UART_PRIORITY
#define EMSESP_UART_PRIORITY configMAX_PRIORITIES - 1
#endif
#define EMS_MAXBUFFERSIZE 33 // max size of the buffer. EMS packets are max 32 bytes, plus extra for BRK
#define EMSUART_NUM UART_NUM_1 // on C3 and S2 there is no UART2, use UART1 for all
@@ -64,7 +75,7 @@ class EMSuart {
static void start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t tx_gpio);
static void send_poll(const uint8_t data);
static void stop();
static uint16_t transmit(const uint8_t * buf, const uint8_t len);
static uint8_t transmit(const uint8_t * buf, const uint8_t len);
static uint8_t last_tx_src() {
return last_tx_src_;
}

View File

@@ -164,7 +164,7 @@ void WebLogService::show(Shell & shell) {
}
void WebLogService::loop() {
if (!events_.count() || log_messages_.empty()) {
if (!events_.count() || log_messages_.empty() || events_.avgPacketsWaiting() >= SSE_MAX_QUEUED_MESSAGES) {
return;
}

View File

@@ -40,7 +40,12 @@ void WebSchedulerService::begin() {
Mqtt::subscribe(EMSdevice::DeviceType::SCHEDULER, topic, nullptr); // use empty function callback
#ifndef EMSESP_STANDALONE
if (EMSESP::system_.PSram()) {
xTaskCreate((TaskFunction_t)scheduler_task, "scheduler_task", 5120, NULL, 1, NULL);
#if defined(CONFIG_FREERTOS_UNICORE) || (EMSESP_SCHEDULER_RUNNING_CORE < 0)
xTaskCreate((TaskFunction_t)scheduler_task, "scheduler_task", EMSESP_SCHEDULER_STACKSIZE, NULL, EMSESP_SCHEDULER_PRIORITY, NULL);
#else
xTaskCreatePinnedToCore(
(TaskFunction_t)scheduler_task, "scheduler_task", EMSESP_SCHEDULER_STACKSIZE, NULL, EMSESP_SCHEDULER_PRIORITY, NULL, EMSESP_SCHEDULER_RUNNING_CORE);
#endif
}
#endif
}

View File

@@ -22,6 +22,18 @@
#define EMSESP_SCHEDULER_FILE "/config/emsespScheduler.json"
#define EMSESP_SCHEDULER_SERVICE_PATH "/rest/schedule" // GET and POST
#ifndef EMSESP_SCHEDULER_RUNNING_CORE
#define EMSESP_SCHEDULER_RUNNING_CORE 1
#endif
#ifndef EMSESP_SCHEDULER_STACKSIZE
#define EMSESP_SCHEDULER_STACKSIZE 5120
#endif
#ifndef EMSESP_SCHEDULER_PRIORITY
#define EMSESP_SCHEDULER_PRIORITY 1
#endif
// bit flags for the schedule items. Matches those in interface/src/app/main/SchedulerDialog.tsx
// 0-127 (0->0x7F) is day schedule
// 128/0x80 is timer

View File

@@ -179,7 +179,12 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
#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 {
settings.board_profile = "E32V2"; // Ethernet and PSRAM
}
} else {
settings.board_profile = "S32"; // ESP32 standard WiFi with PSRAM
}