mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-01-26 16:49:11 +03:00
@@ -25,8 +25,8 @@
|
|||||||
"@alova/adapter-xhr": "2.2.1",
|
"@alova/adapter-xhr": "2.2.1",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@mui/icons-material": "^7.2.0",
|
"@mui/icons-material": "^7.3.1",
|
||||||
"@mui/material": "^7.2.0",
|
"@mui/material": "^7.3.1",
|
||||||
"@table-library/react-table-library": "4.1.15",
|
"@table-library/react-table-library": "4.1.15",
|
||||||
"alova": "3.3.4",
|
"alova": "3.3.4",
|
||||||
"async-validator": "^4.2.5",
|
"async-validator": "^4.2.5",
|
||||||
@@ -38,28 +38,28 @@
|
|||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-router": "^7.7.1",
|
"react-router": "^7.8.0",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"typesafe-i18n": "^5.26.2",
|
"typesafe-i18n": "^5.26.2",
|
||||||
"typescript": "^5.9.2"
|
"typescript": "^5.9.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.28.0",
|
"@babel/core": "^7.28.0",
|
||||||
"@eslint/js": "^9.32.0",
|
"@eslint/js": "^9.33.0",
|
||||||
"@preact/compat": "^18.3.1",
|
"@preact/compat": "^18.3.1",
|
||||||
"@preact/preset-vite": "^2.10.2",
|
"@preact/preset-vite": "^2.10.2",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||||
"@types/node": "^24.2.0",
|
"@types/node": "^24.2.1",
|
||||||
"@types/react": "^19.1.9",
|
"@types/react": "^19.1.10",
|
||||||
"@types/react-dom": "^19.1.7",
|
"@types/react-dom": "^19.1.7",
|
||||||
"concurrently": "^9.2.0",
|
"concurrently": "^9.2.0",
|
||||||
"eslint": "^9.32.0",
|
"eslint": "^9.33.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"rollup-plugin-visualizer": "^6.0.3",
|
"rollup-plugin-visualizer": "^6.0.3",
|
||||||
"terser": "^5.43.1",
|
"terser": "^5.43.1",
|
||||||
"typescript-eslint": "^8.38.0",
|
"typescript-eslint": "^8.39.1",
|
||||||
"vite": "^7.0.6",
|
"vite": "^7.1.2",
|
||||||
"vite-plugin-imagemin": "^0.6.1",
|
"vite-plugin-imagemin": "^0.6.1",
|
||||||
"vite-tsconfig-paths": "^5.1.4"
|
"vite-tsconfig-paths": "^5.1.4"
|
||||||
},
|
},
|
||||||
|
|||||||
476
interface/pnpm-lock.yaml
generated
476
interface/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -259,6 +259,7 @@ export const BOARD_PROFILES: BoardProfiles = {
|
|||||||
S32S3: 'BBQKees Gateway S3',
|
S32S3: 'BBQKees Gateway S3',
|
||||||
E32: 'BBQKees Gateway E32',
|
E32: 'BBQKees Gateway E32',
|
||||||
E32V2: 'BBQKees Gateway E32 V2',
|
E32V2: 'BBQKees Gateway E32 V2',
|
||||||
|
E32V2_2: 'BBQKees Gateway E32 V2.2',
|
||||||
NODEMCU: 'NodeMCU 32S',
|
NODEMCU: 'NodeMCU 32S',
|
||||||
'MH-ET': 'MH-ET Live D1 Mini',
|
'MH-ET': 'MH-ET Live D1 Mini',
|
||||||
LOLIN: 'Lolin D32',
|
LOLIN: 'Lolin D32',
|
||||||
|
|||||||
@@ -54,7 +54,15 @@ bool PButton::init(uint8_t pin, bool pullMode) {
|
|||||||
pullMode_ = pullMode; // 1=HIGH (pullup) 0=LOW (pulldown)
|
pullMode_ = pullMode; // 1=HIGH (pullup) 0=LOW (pulldown)
|
||||||
|
|
||||||
#if defined(ESP32)
|
#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);
|
pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT_PULLDOWN);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT_PULLDOWN);
|
||||||
|
#endif
|
||||||
#else // esp8266 and standalone
|
#else // esp8266 and standalone
|
||||||
pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT);
|
pinMode(pin_, pullMode ? INPUT_PULLUP : INPUT);
|
||||||
#endif
|
#endif
|
||||||
@@ -93,7 +101,15 @@ bool PButton::check(void) {
|
|||||||
|
|
||||||
// make sure the pin is still input
|
// make sure the pin is still input
|
||||||
#if defined(ESP32)
|
#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);
|
pinMode(pin_, pullMode_ ? INPUT_PULLUP : INPUT_PULLDOWN);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
pinMode(pin_, pullMode_ ? INPUT_PULLUP : INPUT_PULLDOWN);
|
||||||
|
#endif
|
||||||
#else // esp8266 and standalone
|
#else // esp8266 and standalone
|
||||||
pinMode(pin_, pullMode_ ? INPUT_PULLUP : INPUT);
|
pinMode(pin_, pullMode_ ? INPUT_PULLUP : INPUT);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
9
lib/eModbus/library.properties
Normal file
9
lib/eModbus/library.properties
Normal 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
|
||||||
@@ -47,6 +47,8 @@ CoilData::~CoilData() {
|
|||||||
|
|
||||||
// Assignment operator
|
// Assignment operator
|
||||||
CoilData& CoilData::operator=(const CoilData& m) {
|
CoilData& CoilData::operator=(const CoilData& m) {
|
||||||
|
// Avoid self-assignment
|
||||||
|
if (this == &m) return *this;
|
||||||
// Remove old data
|
// Remove old data
|
||||||
if (CDbuffer) {
|
if (CDbuffer) {
|
||||||
delete CDbuffer;
|
delete CDbuffer;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include "ModbusClient.h"
|
#include "ModbusClient.h"
|
||||||
|
#include "ModbusServer.h"
|
||||||
#include "ModbusClientTCP.h" // Needed for client.setTarget()
|
#include "ModbusClientTCP.h" // Needed for client.setTarget()
|
||||||
#include "RTUutils.h" // Needed for RTScallback
|
#include "RTUutils.h" // Needed for RTScallback
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ public:
|
|||||||
ModbusBridge();
|
ModbusBridge();
|
||||||
|
|
||||||
// Constructors for the RTU variant. Parameters as are for ModbusServerRTU
|
// 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);
|
ModbusBridge(uint32_t timeout, RTScallback rts);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
@@ -230,42 +231,56 @@ ModbusMessage ModbusBridge<SERVERCLASS>::bridgeWorker(ModbusMessage msg) {
|
|||||||
uint8_t aliasID = msg.getServerID();
|
uint8_t aliasID = msg.getServerID();
|
||||||
uint8_t functionCode = msg.getFunctionCode();
|
uint8_t functionCode = msg.getFunctionCode();
|
||||||
ModbusMessage response;
|
ModbusMessage response;
|
||||||
|
bool foundServer = false;
|
||||||
|
uint8_t usableID = 255;
|
||||||
|
|
||||||
// Find the (alias) serverID
|
// Find the (alias) serverID
|
||||||
if (servers.find(aliasID) != servers.end()) {
|
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
|
// Found it. We may use servers[aliasID] now without allocating a new map slot
|
||||||
|
|
||||||
// Request filter hook to be called here
|
// Request filter hook to be called here
|
||||||
if (servers[aliasID]->requestFilter) {
|
if (servers[usableID]->requestFilter) {
|
||||||
LOG_D("Calling request filter\n");
|
LOG_D("Calling request filter\n");
|
||||||
msg = servers[aliasID]->requestFilter(msg);
|
msg = servers[usableID]->requestFilter(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set real target server ID
|
// Set real target server ID
|
||||||
msg.setServerID(servers[aliasID]->serverID);
|
if (servers[usableID]->serverID != ANY_SERVER) {
|
||||||
|
msg.setServerID(servers[usableID]->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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response filter hook to be called here
|
// Issue the request
|
||||||
if (servers[aliasID]->responseFilter) {
|
LOG_D("Request (%02X/%02X) sent\n", servers[usableID]->serverID, msg.getFunctionCode());
|
||||||
LOG_D("Calling response filter\n");
|
// TCP servers have a target host/port that needs to be set in the client
|
||||||
response = servers[aliasID]->responseFilter(response);
|
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)
|
// Re-set the requested server ID and function code (may have been modified by filters)
|
||||||
response.setServerID(aliasID);
|
response.setServerID(aliasID);
|
||||||
|
|
||||||
if (response.getError() != SUCCESS) {
|
if (response.getError() != SUCCESS) {
|
||||||
response.setFunctionCode(functionCode | 0x80);
|
response.setFunctionCode(functionCode | 0x80);
|
||||||
} else {
|
} else {
|
||||||
response.setFunctionCode(functionCode);
|
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 {
|
} else {
|
||||||
// If we get here, something has gone wrong internally. We send back an error response anyway.
|
// If we get here, something has gone wrong internally. We send back an error response anyway.
|
||||||
response.setError(aliasID, functionCode, INVALID_SERVER);
|
response.setError(aliasID, functionCode, INVALID_SERVER);
|
||||||
|
|||||||
@@ -21,6 +21,13 @@ ModbusClient::ModbusClient() :
|
|||||||
onError(nullptr),
|
onError(nullptr),
|
||||||
onResponse(nullptr) { instanceCounter++; }
|
onResponse(nullptr) { instanceCounter++; }
|
||||||
|
|
||||||
|
// Default destructor: reduce number of clients by one
|
||||||
|
ModbusClient::~ModbusClient() {
|
||||||
|
if (instanceCounter) {
|
||||||
|
instanceCounter--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// onDataHandler: register callback for data responses
|
// onDataHandler: register callback for data responses
|
||||||
bool ModbusClient::onDataHandler(MBOnData handler) {
|
bool ModbusClient::onDataHandler(MBOnData handler) {
|
||||||
if (onData) {
|
if (onData) {
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ public:
|
|||||||
uint32_t getMessageCount(); // Informative: return number of messages created
|
uint32_t getMessageCount(); // Informative: return number of messages created
|
||||||
uint32_t getErrorCount(); // Informative: return number of errors received
|
uint32_t getErrorCount(); // Informative: return number of errors received
|
||||||
void resetCounts(); // Set both message and error counts to zero
|
void resetCounts(); // Set both message and error counts to zero
|
||||||
inline Error addRequest(ModbusMessage m, uint32_t token) { return addRequestM(m, token); }
|
inline Error addRequest(const ModbusMessage& m, uint32_t token) { return addRequestM(m, token); }
|
||||||
inline ModbusMessage syncRequest(ModbusMessage m, uint32_t token) { return syncRequestM(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
|
// Template function to generate syncRequest functions as long as there is a
|
||||||
// matching ModbusMessage::setMessage() call
|
// matching ModbusMessage::setMessage() call
|
||||||
@@ -85,7 +85,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
ModbusClient(); // Default constructor
|
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
|
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 addRequest variant needed internally. All others done by template!
|
||||||
virtual Error addRequestM(ModbusMessage msg, uint32_t token) = 0;
|
virtual Error addRequestM(ModbusMessage msg, uint32_t token) = 0;
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ void ModbusClientRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterv
|
|||||||
char taskName[18];
|
char taskName[18];
|
||||||
snprintf(taskName, 18, "Modbus%02XRTU", instanceCounter);
|
snprintf(taskName, 18, "Modbus%02XRTU", instanceCounter);
|
||||||
// Start task to handle the queue
|
// 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);
|
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;
|
std::queue<RequestEntry> empty;
|
||||||
LOCK_GUARD(lockGuard, qLock);
|
LOCK_GUARD(lockGuard, qLock);
|
||||||
|
// Empty queue
|
||||||
std::swap(requests, empty);
|
std::swap(requests, empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,9 +343,12 @@ void ModbusClientRTU::handleConnection(ModbusClientRTU *instance) {
|
|||||||
{
|
{
|
||||||
// Safely lock the queue
|
// Safely lock the queue
|
||||||
LOCK_GUARD(lockGuard, instance->qLock);
|
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();
|
instance->requests.pop();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
delay(1);
|
delay(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,15 +67,15 @@ protected:
|
|||||||
uint32_t token;
|
uint32_t token;
|
||||||
ModbusMessage msg;
|
ModbusMessage msg;
|
||||||
bool isSyncRequest;
|
bool isSyncRequest;
|
||||||
RequestEntry(uint32_t t, ModbusMessage m, bool syncReq = false) :
|
RequestEntry(uint32_t t, const ModbusMessage& m, bool syncReq = false) :
|
||||||
token(t),
|
token(t),
|
||||||
msg(m),
|
msg(m),
|
||||||
isSyncRequest(syncReq) {}
|
isSyncRequest(syncReq) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Base addRequest and syncRequest must be present
|
// Base addRequest and syncRequest must be present
|
||||||
Error addRequestM(ModbusMessage msg, uint32_t token);
|
Error addRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
|
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
|
|
||||||
// addToQueue: send freshly created request to queue
|
// addToQueue: send freshly created request to queue
|
||||||
bool addToQueue(uint32_t token, ModbusMessage msg, bool syncReq = false);
|
bool addToQueue(uint32_t token, ModbusMessage msg, bool syncReq = false);
|
||||||
@@ -89,7 +89,6 @@ protected:
|
|||||||
// start background task
|
// start background task
|
||||||
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);
|
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
|
queue<RequestEntry> requests; // Queue to hold requests to be processed
|
||||||
#if USE_MUTEX
|
#if USE_MUTEX
|
||||||
mutex qLock; // Mutex to protect queue
|
mutex qLock; // Mutex to protect queue
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ ModbusClientTCP::ModbusClientTCP(Client& client, uint16_t queueLimit) :
|
|||||||
MT_target(IPAddress(0, 0, 0, 0), 0, DEFAULTTIMEOUT, TARGETHOSTINTERVAL),
|
MT_target(IPAddress(0, 0, 0, 0), 0, DEFAULTTIMEOUT, TARGETHOSTINTERVAL),
|
||||||
MT_defaultTimeout(DEFAULTTIMEOUT),
|
MT_defaultTimeout(DEFAULTTIMEOUT),
|
||||||
MT_defaultInterval(TARGETHOSTINTERVAL),
|
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
|
// 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_target(host, port, DEFAULTTIMEOUT, TARGETHOSTINTERVAL),
|
||||||
MT_defaultTimeout(DEFAULTTIMEOUT),
|
MT_defaultTimeout(DEFAULTTIMEOUT),
|
||||||
MT_defaultInterval(TARGETHOSTINTERVAL),
|
MT_defaultInterval(TARGETHOSTINTERVAL),
|
||||||
MT_qLimit(queueLimit)
|
MT_qLimit(queueLimit),
|
||||||
|
MT_timeoutsToClose(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
// Destructor: clean up queue, task etc.
|
// Destructor: clean up queue, task etc.
|
||||||
@@ -64,7 +66,7 @@ void ModbusClientTCP::end() {
|
|||||||
// begin: start worker task
|
// begin: start worker task
|
||||||
#if IS_LINUX
|
#if IS_LINUX
|
||||||
void *ModbusClientTCP::pHandle(void *p) {
|
void *ModbusClientTCP::pHandle(void *p) {
|
||||||
handleConnection((ModbusClientTCP *)p);
|
handleConnection(static_cast<ModbusClientTCP *>(p));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -84,7 +86,7 @@ void ModbusClientTCP::begin(int coreID) {
|
|||||||
char taskName[18];
|
char taskName[18];
|
||||||
snprintf(taskName, 18, "Modbus%02XTCP", instanceCounter);
|
snprintf(taskName, 18, "Modbus%02XTCP", instanceCounter);
|
||||||
// Start task to handle the queue
|
// 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);
|
LOG_D("TCP client worker %s started\n", taskName);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
@@ -119,9 +121,25 @@ uint32_t ModbusClientTCP::pendingRequests() {
|
|||||||
void ModbusClientTCP::clearQueue() {
|
void ModbusClientTCP::clearQueue() {
|
||||||
std::queue<RequestEntry *> empty;
|
std::queue<RequestEntry *> empty;
|
||||||
LOCK_GUARD(lockGuard, qLock);
|
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);
|
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
|
// Base addRequest for preformatted ModbusMessage and last set target
|
||||||
Error ModbusClientTCP::addRequestM(ModbusMessage msg, uint32_t token) {
|
Error ModbusClientTCP::addRequestM(ModbusMessage msg, uint32_t token) {
|
||||||
Error rc = SUCCESS; // Return value
|
Error rc = SUCCESS; // Return value
|
||||||
@@ -225,6 +243,7 @@ bool ModbusClientTCP::addToQueue(uint32_t token, ModbusMessage request, TargetHo
|
|||||||
void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
|
void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
|
||||||
bool doNotPop;
|
bool doNotPop;
|
||||||
unsigned long lastRequest = millis();
|
unsigned long lastRequest = millis();
|
||||||
|
uint16_t timeoutCount = 0; // Run time counter of consecutive timeouts.
|
||||||
|
|
||||||
// Loop forever - or until task is killed
|
// Loop forever - or until task is killed
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -273,6 +292,8 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
|
|||||||
// Did we get a normal response?
|
// Did we get a normal response?
|
||||||
if (response.getError()==SUCCESS) {
|
if (response.getError()==SUCCESS) {
|
||||||
LOG_D("Data response.\n");
|
LOG_D("Data response.\n");
|
||||||
|
// Reset timeout counter
|
||||||
|
timeoutCount = 0;
|
||||||
// Yes. Is it a synchronous request?
|
// Yes. Is it a synchronous request?
|
||||||
if (request->isSyncRequest) {
|
if (request->isSyncRequest) {
|
||||||
// Yes. Put the response into the response map
|
// Yes. Put the response into the response map
|
||||||
@@ -299,6 +320,25 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
|
|||||||
LOCK_GUARD(responseCnt, instance->countAccessM);
|
LOCK_GUARD(responseCnt, instance->countAccessM);
|
||||||
instance->errorCount++;
|
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?
|
// Is it a synchronous request?
|
||||||
if (request->isSyncRequest) {
|
if (request->isSyncRequest) {
|
||||||
// Yes. Put the response into the response map
|
// Yes. Put the response into the response map
|
||||||
@@ -345,8 +385,11 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
|
|||||||
{
|
{
|
||||||
// Safely lock the queue
|
// Safely lock the queue
|
||||||
LOCK_GUARD(lockGuard, instance->qLock);
|
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();
|
instance->requests.pop();
|
||||||
|
}
|
||||||
// Delete request
|
// Delete request
|
||||||
delete request;
|
delete request;
|
||||||
LOG_D("Request popped from queue.\n");
|
LOG_D("Request popped from queue.\n");
|
||||||
|
|||||||
@@ -50,6 +50,11 @@ public:
|
|||||||
// Remove all pending request from queue
|
// Remove all pending request from queue
|
||||||
void clearQueue();
|
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:
|
protected:
|
||||||
// class describing a target server
|
// class describing a target server
|
||||||
struct TargetHost {
|
struct TargetHost {
|
||||||
@@ -58,7 +63,7 @@ protected:
|
|||||||
uint32_t timeout; // Time in ms waiting for a response
|
uint32_t timeout; // Time in ms waiting for a response
|
||||||
uint32_t interval; // Time in ms to wait between requests
|
uint32_t interval; // Time in ms to wait between requests
|
||||||
|
|
||||||
inline TargetHost& operator=(TargetHost& t) {
|
inline TargetHost& operator=(const TargetHost& t) {
|
||||||
host = t.host;
|
host = t.host;
|
||||||
port = t.port;
|
port = t.port;
|
||||||
timeout = t.timeout;
|
timeout = t.timeout;
|
||||||
@@ -66,7 +71,7 @@ protected:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline TargetHost(TargetHost& t) :
|
inline TargetHost(const TargetHost& t) :
|
||||||
host(t.host),
|
host(t.host),
|
||||||
port(t.port),
|
port(t.port),
|
||||||
timeout(t.timeout),
|
timeout(t.timeout),
|
||||||
@@ -86,13 +91,13 @@ protected:
|
|||||||
interval(interval)
|
interval(interval)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
inline bool operator==(TargetHost& t) {
|
inline bool operator==(const TargetHost& t) {
|
||||||
if (host != t.host) return false;
|
if (host != t.host) return false;
|
||||||
if (port != t.port) return false;
|
if (port != t.port) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator!=(TargetHost& t) {
|
inline bool operator!=(const TargetHost& t) {
|
||||||
if (host != t.host) return true;
|
if (host != t.host) return true;
|
||||||
if (port != t.port) return true;
|
if (port != t.port) return true;
|
||||||
return false;
|
return false;
|
||||||
@@ -135,7 +140,7 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
struct RequestEntry {
|
||||||
@@ -144,7 +149,7 @@ protected:
|
|||||||
TargetHost target;
|
TargetHost target;
|
||||||
ModbusTCPhead head;
|
ModbusTCPhead head;
|
||||||
bool isSyncRequest;
|
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),
|
token(t),
|
||||||
msg(m),
|
msg(m),
|
||||||
target(tg),
|
target(tg),
|
||||||
@@ -153,8 +158,8 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Base addRequest and syncRequest must be present
|
// Base addRequest and syncRequest must be present
|
||||||
Error addRequestM(ModbusMessage msg, uint32_t token);
|
Error addRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
|
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
// TCP-specific addition "...MT()" including adhoc target - used by bridge
|
// TCP-specific addition "...MT()" including adhoc target - used by bridge
|
||||||
Error addRequestMT(ModbusMessage msg, uint32_t token, IPAddress targetHost, uint16_t targetPort);
|
Error addRequestMT(ModbusMessage msg, uint32_t token, IPAddress targetHost, uint16_t targetPort);
|
||||||
ModbusMessage syncRequestMT(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
|
// receive: get response via Client connection
|
||||||
ModbusMessage receive(RequestEntry *request);
|
ModbusMessage receive(RequestEntry *request);
|
||||||
|
|
||||||
void isInstance() { return; } // make class instantiable
|
|
||||||
queue<RequestEntry *> requests; // Queue to hold requests to be processed
|
queue<RequestEntry *> requests; // Queue to hold requests to be processed
|
||||||
#if USE_MUTEX
|
#if USE_MUTEX
|
||||||
mutex qLock; // Mutex to protect queue
|
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_defaultTimeout; // Standard timeout value taken if no dedicated was set
|
||||||
uint32_t MT_defaultInterval; // Standard interval 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
|
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
|
// Let any ModbusBridge class use protected members
|
||||||
template<typename SERVERCLASS> friend class ModbusBridge;
|
template<typename SERVERCLASS> friend class ModbusBridge;
|
||||||
|
|||||||
@@ -223,19 +223,17 @@ void onAck(size_t len, uint32_t time) {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
void ModbusClientTCPasync::onPacket(uint8_t* data, size_t length) {
|
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
|
// reset idle timeout
|
||||||
MTA_lastActivity = millis();
|
MTA_lastActivity = millis();
|
||||||
|
|
||||||
if (length) {
|
if (length) {
|
||||||
LOG_D("parsing (len:%d)\n", length + 1);
|
LOG_D("parsing (len:%u)\n", length + 1);
|
||||||
}
|
}
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
RequestEntry* request = nullptr;
|
RequestEntry* request = nullptr;
|
||||||
ModbusMessage* response = nullptr;
|
ModbusMessage* response = nullptr;
|
||||||
uint16_t transactionID = 0;
|
uint16_t transactionID = 0;
|
||||||
uint16_t protocolID = 0;
|
|
||||||
uint16_t messageLength = 0;
|
|
||||||
bool isOkay = false;
|
bool isOkay = false;
|
||||||
|
|
||||||
// 1. Check for valid modbus message
|
// 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])
|
// total message should fit MBAP plus remaining bytes (in data[4], data[5])
|
||||||
if (length > 6) {
|
if (length > 6) {
|
||||||
transactionID = (data[0] << 8) | data[1];
|
transactionID = (data[0] << 8) | data[1];
|
||||||
protocolID = (data[2] << 8) | data[3];
|
uint16_t protocolID = (data[2] << 8) | data[3];
|
||||||
messageLength = (data[4] << 8) | data[5];
|
uint16_t messageLength = (data[4] << 8) | data[5];
|
||||||
if (protocolID == 0 &&
|
if (protocolID == 0 &&
|
||||||
length >= (uint32_t)messageLength + 6 &&
|
length >= (uint32_t)messageLength + 6 &&
|
||||||
messageLength < 256) {
|
messageLength < 256) {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ protected:
|
|||||||
return headRoom;
|
return headRoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ModbusTCPhead& operator= (ModbusTCPhead& t) {
|
inline ModbusTCPhead& operator= (const ModbusTCPhead& t) {
|
||||||
transactionID = t.transactionID;
|
transactionID = t.transactionID;
|
||||||
protocolID = t.protocolID;
|
protocolID = t.protocolID;
|
||||||
len = t.len;
|
len = t.len;
|
||||||
@@ -91,7 +91,7 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
struct RequestEntry {
|
||||||
@@ -100,7 +100,7 @@ protected:
|
|||||||
ModbusTCPhead head;
|
ModbusTCPhead head;
|
||||||
uint32_t sentTime;
|
uint32_t sentTime;
|
||||||
bool isSyncRequest;
|
bool isSyncRequest;
|
||||||
RequestEntry(uint32_t t, ModbusMessage m, bool syncReq = false) :
|
RequestEntry(uint32_t t, const ModbusMessage& m, bool syncReq = false) :
|
||||||
token(t),
|
token(t),
|
||||||
msg(m),
|
msg(m),
|
||||||
head(ModbusTCPhead()),
|
head(ModbusTCPhead()),
|
||||||
@@ -109,8 +109,8 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Base addRequest and syncRequest both must be present
|
// Base addRequest and syncRequest both must be present
|
||||||
Error addRequestM(ModbusMessage msg, uint32_t token);
|
Error addRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
|
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
|
|
||||||
// addToQueue: send freshly created request to queue
|
// addToQueue: send freshly created request to queue
|
||||||
bool addToQueue(int32_t token, ModbusMessage request, bool syncReq = false);
|
bool addToQueue(int32_t token, ModbusMessage request, bool syncReq = false);
|
||||||
@@ -121,8 +121,6 @@ protected:
|
|||||||
// receive: get response via Client connection
|
// receive: get response via Client connection
|
||||||
// TCPResponse* receive(uint8_t* data, size_t length);
|
// 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
|
// TCP handling code, all static taking a class instancs as param
|
||||||
void onConnected();
|
void onConnected();
|
||||||
void onDisconnected();
|
void onDisconnected();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#undef LOCAL_LOG_LEVEL
|
#undef LOCAL_LOG_LEVEL
|
||||||
// #define LOCAL_LOG_LEVEL LOG_LEVEL_ERROR
|
// #define LOCAL_LOG_LEVEL LOG_LEVEL_ERROR
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
// Default Constructor - takes optional size of MM_data to allocate memory
|
// Default Constructor - takes optional size of MM_data to allocate memory
|
||||||
ModbusMessage::ModbusMessage(uint16_t dataLen) {
|
ModbusMessage::ModbusMessage(uint16_t dataLen) {
|
||||||
@@ -146,21 +147,19 @@ void ModbusMessage::setServerID(uint8_t serverID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ModbusMessage::setFunctionCode(uint8_t FC) {
|
void ModbusMessage::setFunctionCode(uint8_t FC) {
|
||||||
// We accept here that [0], [1] may allocate bytes!
|
if (MM_data.size() < 2) {
|
||||||
if (MM_data.empty()) {
|
MM_data.resize(2); // Resize to at least 2 to ensure indices 0 and 1 are valid
|
||||||
MM_data.reserve(3); // At least an error message should fit
|
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
|
MM_data[1] = FC; // Safely set the function code
|
||||||
if (MM_data.size() < 2) MM_data[0] = 0; // intentional invalid server ID!
|
|
||||||
MM_data[1] = FC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add() variant to copy a buffer into MM_data. Returns updated size
|
// 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 ModbusMessage::add(const uint8_t *arrayOfBytes, uint16_t count) {
|
||||||
|
uint16_t originalSize = MM_data.size();
|
||||||
|
MM_data.resize(originalSize + count);
|
||||||
// Copy it
|
// Copy it
|
||||||
while (count--) {
|
std::copy(arrayOfBytes, arrayOfBytes + count, MM_data.begin() + originalSize);
|
||||||
MM_data.push_back(*arrayOfBytes++);
|
|
||||||
}
|
|
||||||
// Return updated size (logical length of message so far)
|
// Return updated size (logical length of message so far)
|
||||||
return MM_data.size();
|
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
|
uint32_t i = 77230; // int value to go into a float without rounding error
|
||||||
float f = i; // assign it
|
float f = i; // assign it
|
||||||
uint8_t *b = (uint8_t *)&f; // Pointer to bytes of f
|
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
|
uint8_t matches = 0; // number of bytes successfully matched
|
||||||
|
|
||||||
// Loop over the bytes of the expected sequence
|
// 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
|
uint64_t i = 5791007487489389; // int64 value to go into a double without rounding error
|
||||||
double f = i; // assign it
|
double f = i; // assign it
|
||||||
uint8_t *b = (uint8_t *)&f; // Pointer to bytes of f
|
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
|
uint8_t matches = 0; // number of bytes successfully matched
|
||||||
|
|
||||||
// Loop over the bytes of the expected sequence
|
// 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
|
// add() variant for a vector of uint8_t
|
||||||
uint16_t ModbusMessage::add(vector<uint8_t> v) {
|
uint16_t ModbusMessage::add(vector<uint8_t> v) {
|
||||||
for (auto& b: v) {
|
return add(v.data(), v.size());
|
||||||
MM_data.push_back(b);
|
|
||||||
}
|
|
||||||
return MM_data.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add() variants for float and double values
|
// add() variants for float and double values
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) {
|
|||||||
svmap = workerMap.find(ANY_SERVER);
|
svmap = workerMap.find(ANY_SERVER);
|
||||||
if (svmap != workerMap.end()) {
|
if (svmap != workerMap.end()) {
|
||||||
serverFound = true;
|
serverFound = true;
|
||||||
serverID = ANY_SERVER;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Did we find a serverID?
|
// Did we find a serverID?
|
||||||
@@ -49,7 +48,6 @@ MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) {
|
|||||||
if (fcmap != svmap->second.end()) {
|
if (fcmap != svmap->second.end()) {
|
||||||
// Yes. Return the function pointer for it.
|
// Yes. Return the function pointer for it.
|
||||||
functionCodeFound = true;
|
functionCodeFound = true;
|
||||||
functionCode = ANY_FUNCTION_CODE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (functionCodeFound) {
|
if (functionCodeFound) {
|
||||||
@@ -104,6 +102,11 @@ bool ModbusServer::isServerFor(uint8_t serverID) {
|
|||||||
// Is there one?
|
// Is there one?
|
||||||
if (svmap != workerMap.end()) {
|
if (svmap != workerMap.end()) {
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
svmap = workerMap.find(ANY_SERVER);
|
||||||
|
if (svmap != workerMap.end()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,15 +68,12 @@ protected:
|
|||||||
ModbusServer();
|
ModbusServer();
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~ModbusServer();
|
virtual ~ModbusServer();
|
||||||
|
|
||||||
// Prevent copy construction or assignment
|
// Prevent copy construction or assignment
|
||||||
ModbusServer(ModbusServer& other) = delete;
|
ModbusServer(ModbusServer& other) = delete;
|
||||||
ModbusServer& operator=(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
|
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 messageCount; // Number of Requests processed
|
||||||
uint32_t errorCount; // Number of errors responded
|
uint32_t errorCount; // Number of errors responded
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
// Create own non-virtual EthernetServer class
|
// Create own non-virtual EthernetServer class
|
||||||
class EthernetServerEM : public EthernetServer {
|
class EthernetServerEM : public EthernetServer {
|
||||||
public:
|
public:
|
||||||
EthernetServerEM(uint16_t port) : EthernetServer(port) { }
|
explicit EthernetServerEM(uint16_t port) : EthernetServer(port) { }
|
||||||
void begin(uint16_t port = 0) { }
|
void begin(uint16_t port = 0) override { }
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "ModbusServerTCPtemp.h"
|
#include "ModbusServerTCPtemp.h"
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ void ModbusServerRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterv
|
|||||||
snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter);
|
snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter);
|
||||||
|
|
||||||
// Start task to handle the client
|
// 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);
|
LOG_D("Server task %d started. Interval=%d\n", (uint32_t)serverTask, MSRinterval);
|
||||||
}
|
}
|
||||||
@@ -126,6 +126,12 @@ bool ModbusServerRTU::isModbusASCII() {
|
|||||||
return MSRuseASCII;
|
return MSRuseASCII;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set timeout
|
||||||
|
void ModbusServerRTU::setModbusTimeout(unsigned long timeout)
|
||||||
|
{
|
||||||
|
serverTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
// Toggle skipping of leading 0x00 byte
|
// Toggle skipping of leading 0x00 byte
|
||||||
void ModbusServerRTU::skipLeading0x00(bool onOff) {
|
void ModbusServerRTU::skipLeading0x00(bool onOff) {
|
||||||
MSRskipLeadingZeroByte = onOff;
|
MSRskipLeadingZeroByte = onOff;
|
||||||
@@ -231,8 +237,8 @@ void ModbusServerRTU::serve(ModbusServerRTU *myServer) {
|
|||||||
response = m;
|
response = m;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No callback. Is at least the serverID valid and no broadcast?
|
// No callback. Is at least the serverID valid?
|
||||||
if (myServer->isServerFor(request[0]) && request[0] != 0x00) {
|
if (myServer->isServerFor(request[0])) {
|
||||||
// Yes. Send back a ILLEGAL_FUNCTION error
|
// Yes. Send back a ILLEGAL_FUNCTION error
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_FUNCTION);
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_FUNCTION);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ public:
|
|||||||
// Inquire protocol mode
|
// Inquire protocol mode
|
||||||
bool isModbusASCII();
|
bool isModbusASCII();
|
||||||
|
|
||||||
|
// set timeout
|
||||||
|
void setModbusTimeout(unsigned long timeout);
|
||||||
|
|
||||||
// Toggle skipping of leading 0x00 byte
|
// Toggle skipping of leading 0x00 byte
|
||||||
void skipLeading0x00(bool onOff = true);
|
void skipLeading0x00(bool onOff = true);
|
||||||
|
|
||||||
@@ -61,8 +64,6 @@ protected:
|
|||||||
ModbusServerRTU(ModbusServerRTU& m) = delete;
|
ModbusServerRTU(ModbusServerRTU& m) = delete;
|
||||||
ModbusServerRTU& operator=(ModbusServerRTU& m) = delete;
|
ModbusServerRTU& operator=(ModbusServerRTU& m) = delete;
|
||||||
|
|
||||||
inline void isInstance() { } // Make class instantiable
|
|
||||||
|
|
||||||
// internal common begin function
|
// internal common begin function
|
||||||
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);
|
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ ModbusServerTCPasync::mb_client::~mb_client() {
|
|||||||
|
|
||||||
void ModbusServerTCPasync::mb_client::onData(uint8_t* data, size_t len) {
|
void ModbusServerTCPasync::mb_client::onData(uint8_t* data, size_t len) {
|
||||||
lastActiveTime = millis();
|
lastActiveTime = millis();
|
||||||
LOG_D("data len %d\n", len);
|
LOG_D("data len %u\n", len);
|
||||||
|
|
||||||
Error error = SUCCESS;
|
Error error = SUCCESS;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
@@ -250,7 +250,7 @@ void ModbusServerTCPasync::onClientConnect(AsyncClient* client) {
|
|||||||
LOCK_GUARD(lock1, cListLock);
|
LOCK_GUARD(lock1, cListLock);
|
||||||
if (clients.size() < maxNoClients) {
|
if (clients.size() < maxNoClients) {
|
||||||
clients.emplace_back(new mb_client(this, client));
|
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 {
|
} else {
|
||||||
LOG_D("max number of clients reached, closing new\n");
|
LOG_D("max number of clients reached, closing new\n");
|
||||||
client->close(true);
|
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; });
|
clients.remove_if([client](mb_client* i) { return i->client == client->client; });
|
||||||
// delete client itself
|
// delete client itself
|
||||||
delete client;
|
delete client;
|
||||||
LOG_D("nr clients: %d\n", clients.size());
|
LOG_D("nr clients: %u\n", clients.size());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ class ModbusServerTCPasync : public ModbusServer {
|
|||||||
bool isRunning();
|
bool isRunning();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
inline void isInstance() { }
|
|
||||||
void onClientConnect(AsyncClient* client);
|
void onClientConnect(AsyncClient* client);
|
||||||
void onClientDisconnect(mb_client* client);
|
void onClientDisconnect(mb_client* client);
|
||||||
|
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ protected:
|
|||||||
ModbusServerTCP(ModbusServerTCP& m) = delete;
|
ModbusServerTCP(ModbusServerTCP& m) = delete;
|
||||||
ModbusServerTCP& operator=(ModbusServerTCP& m) = delete;
|
ModbusServerTCP& operator=(ModbusServerTCP& m) = delete;
|
||||||
|
|
||||||
inline void isInstance() { }
|
|
||||||
|
|
||||||
uint8_t numClients;
|
uint8_t numClients;
|
||||||
TaskHandle_t serverTask;
|
TaskHandle_t serverTask;
|
||||||
uint16_t serverPort;
|
uint16_t serverPort;
|
||||||
@@ -159,7 +157,7 @@ template <typename ST, typename CT>
|
|||||||
snprintf(taskName, 18, "MBserve%04X", port);
|
snprintf(taskName, 18, "MBserve%04X", port);
|
||||||
|
|
||||||
// Start task to handle the client
|
// 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);
|
LOG_D("Server task %s started (%d).\n", taskName, (uint32_t)serverTask);
|
||||||
|
|
||||||
// Wait two seconds for it to establish
|
// 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);
|
snprintf(taskName, 18, "MBsrv%02Xclnt", i);
|
||||||
|
|
||||||
// Start task to handle the client
|
// 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));
|
LOG_D("Started client %d task %d\n", i, (uint32_t)(clients[i]->task));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -109,8 +109,8 @@ const uint8_t swapTables[8][8] = {
|
|||||||
enum FCType : uint8_t {
|
enum FCType : uint8_t {
|
||||||
FC01_TYPE, // Two uint16_t parameters (FC 0x01, 0x02, 0x03, 0x04, 0x05, 0x06)
|
FC01_TYPE, // Two uint16_t parameters (FC 0x01, 0x02, 0x03, 0x04, 0x05, 0x06)
|
||||||
FC07_TYPE, // no additional parameter (FCs 0x07, 0x0b, 0x0c, 0x11)
|
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)
|
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 uint8_t* pointer to array of words (FC 0x10)
|
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)
|
FC16_TYPE, // three uint16_t parameters (FC 0x16)
|
||||||
FC18_TYPE, // one uint16_t parameter (FC 0x18)
|
FC18_TYPE, // one uint16_t parameter (FC 0x18)
|
||||||
FCGENERIC, // for FCs not yet explicitly coded (or too complex)
|
FCGENERIC, // for FCs not yet explicitly coded (or too complex)
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
// MIT license - see license.md for details
|
// MIT license - see license.md for details
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#if HAS_FREERTOS
|
||||||
#include "ModbusMessage.h"
|
#include "ModbusMessage.h"
|
||||||
#include "RTUutils.h"
|
#include "RTUutils.h"
|
||||||
#undef LOCAL_LOG_LEVEL
|
#undef LOCAL_LOG_LEVEL
|
||||||
// #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
|
// #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
|
||||||
#if HAS_FREERTOS
|
|
||||||
// calcCRC: calculate Modbus CRC16 on a given array of bytes
|
// calcCRC: calculate Modbus CRC16 on a given array of bytes
|
||||||
uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) {
|
uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) {
|
||||||
// CRC16 pre-calculated tables
|
// 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 crcHi = 0xFF;
|
||||||
uint8_t crcLo = 0xFF;
|
uint8_t crcLo = 0xFF;
|
||||||
uint8_t index;
|
|
||||||
|
|
||||||
while (len--) {
|
while (len--) {
|
||||||
index = crcLo ^ *data++;
|
uint8_t index = crcLo ^ *data++;
|
||||||
crcLo = crcHi ^ crcHiTable[index];
|
crcLo = crcHi ^ crcHiTable[index];
|
||||||
crcHi = crcLoTable[index];
|
crcHi = crcLoTable[index];
|
||||||
}
|
}
|
||||||
@@ -464,4 +463,5 @@ const char RTUutils::ASCIIread[] = {
|
|||||||
const char RTUutils::ASCIIwrite[] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
const char RTUutils::ASCIIwrite[] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||||
0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46
|
0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,9 +5,6 @@
|
|||||||
#ifndef _RTU_UTILS_H
|
#ifndef _RTU_UTILS_H
|
||||||
#define _RTU_UTILS_H
|
#define _RTU_UTILS_H
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#if NEED_UART_PATCH
|
|
||||||
#include <soc/uart_struct.h>
|
|
||||||
#endif
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
#include "ModbusTypeDefs.h"
|
#include "ModbusTypeDefs.h"
|
||||||
@@ -52,11 +49,13 @@ public:
|
|||||||
// RTSauto: dummy callback for auto half duplex RS485 boards
|
// RTSauto: dummy callback for auto half duplex RS485 boards
|
||||||
inline static void RTSauto(bool level) { return; } // NOLINT
|
inline static void RTSauto(bool level) { return; } // NOLINT
|
||||||
|
|
||||||
|
#if HAS_FREERTOS
|
||||||
// Necessary preparations for a HardwareSerial
|
// Necessary preparations for a HardwareSerial
|
||||||
static void prepareHardwareSerial(HardwareSerial& s, uint16_t bufferSize = 260) {
|
static void prepareHardwareSerial(HardwareSerial& s, uint16_t bufferSize = 260) {
|
||||||
s.setRxBufferSize(bufferSize);
|
s.setRxBufferSize(bufferSize);
|
||||||
s.setTxBufferSize(bufferSize);
|
s.setTxBufferSize(bufferSize);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Printable characters for ASCII protocol: 012345678ABCDEF
|
// Printable characters for ASCII protocol: 012345678ABCDEF
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#define HAS_FREERTOS 1
|
#define HAS_FREERTOS 1
|
||||||
#define HAS_ETHERNET 1
|
#define HAS_ETHERNET 1
|
||||||
#define IS_LINUX 0
|
#define IS_LINUX 0
|
||||||
#define NEED_UART_PATCH 1
|
|
||||||
const unsigned int SERVER_TASK_STACK = 4096;
|
const unsigned int SERVER_TASK_STACK = 4096;
|
||||||
const unsigned int CLIENT_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_FREERTOS 0
|
||||||
#define HAS_ETHERNET 0
|
#define HAS_ETHERNET 0
|
||||||
#define IS_LINUX 0
|
#define IS_LINUX 0
|
||||||
#define NEED_UART_PATCH 0
|
|
||||||
|
|
||||||
/* === LINUX DEFINITIONS AND MACROS === */
|
/* === LINUX DEFINITIONS AND MACROS === */
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
@@ -31,7 +29,6 @@ const unsigned int CLIENT_TASK_STACK = 4096;
|
|||||||
#define HAS_FREERTOS 0
|
#define HAS_FREERTOS 0
|
||||||
#define HAS_ETHERNET 0
|
#define HAS_ETHERNET 0
|
||||||
#define IS_LINUX 1
|
#define IS_LINUX 1
|
||||||
#define NEED_UART_PATCH 0
|
|
||||||
#include <cstdio> // for printf()
|
#include <cstdio> // for printf()
|
||||||
#include <cstring> // for memcpy(), strlen() etc.
|
#include <cstring> // for memcpy(), strlen() etc.
|
||||||
#include <cinttypes> // for uint32_t etc.
|
#include <cinttypes> // for uint32_t etc.
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint
|
|||||||
|
|
||||||
MqttClient::~MqttClient() {
|
MqttClient::~MqttClient() {
|
||||||
disconnect(true);
|
disconnect(true);
|
||||||
_clearQueue(2);
|
clearQueue(true);
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
vSemaphoreDelete(_xSemaphore);
|
vSemaphoreDelete(_xSemaphore);
|
||||||
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {
|
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ void SyslogService::remove_queued_messages(uuid::log::Level level) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log_message_id_ -= offset;
|
log_message_id_ -= offset;
|
||||||
log_message_fails_ += offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyslogService::log_level(uuid::log::Level level) {
|
void SyslogService::log_level(uuid::log::Level level) {
|
||||||
|
|||||||
@@ -243,6 +243,9 @@ class AsyncWebServer {
|
|||||||
void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) {};
|
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 {
|
class AsyncEventSource : public AsyncWebHandler {
|
||||||
public:
|
public:
|
||||||
@@ -252,6 +255,9 @@ class AsyncEventSource : public AsyncWebHandler {
|
|||||||
size_t count() const {
|
size_t count() const {
|
||||||
return 1;
|
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) {};
|
void send(const char * message, const char * event = NULL, uint32_t id = 0, uint32_t reconnect = 0) {};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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_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)
|
-D CONFIG_ASYNC_TCP_STACK_SIZE=6144 ; stack usage measured: ESP32: ~2.3K, ESP32S3: ~3.5k - (default is 16K)
|
||||||
; ESPAsyncWebServer
|
; 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 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 =
|
unbuild_flags =
|
||||||
${common.core_unbuild_flags}
|
${common.core_unbuild_flags}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ static String generateClientId() {
|
|||||||
|
|
||||||
MqttSettingsService::~MqttSettingsService() {
|
MqttSettingsService::~MqttSettingsService() {
|
||||||
delete _mqttClient;
|
delete _mqttClient;
|
||||||
|
_mqttClient = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttSettingsService::begin() {
|
void MqttSettingsService::begin() {
|
||||||
@@ -34,7 +35,7 @@ void MqttSettingsService::startClient() {
|
|||||||
static bool isSecure = false;
|
static bool isSecure = false;
|
||||||
if (_mqttClient != nullptr) {
|
if (_mqttClient != nullptr) {
|
||||||
// do we need to change the client?
|
// do we need to change the client?
|
||||||
if ((isSecure && _state.enableTLS) || (!isSecure && !_state.enableTLS)) {
|
if (_state.enabled && ((isSecure && _state.enableTLS) || (!isSecure && !_state.enableTLS))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
delete _mqttClient;
|
delete _mqttClient;
|
||||||
@@ -43,10 +44,10 @@ void MqttSettingsService::startClient() {
|
|||||||
#ifndef TASMOTA_SDK
|
#ifndef TASMOTA_SDK
|
||||||
if (_state.enableTLS) {
|
if (_state.enableTLS) {
|
||||||
isSecure = true;
|
isSecure = true;
|
||||||
if (emsesp::EMSESP::system_.PSram() > 0) {
|
if (emsesp::EMSESP::system_.PSram() == 0) {
|
||||||
_mqttClient = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::YES);
|
|
||||||
} else {
|
|
||||||
_mqttClient = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO);
|
_mqttClient = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO);
|
||||||
|
} else {
|
||||||
|
_mqttClient = new espMqttClientSecure(EMSESP_MQTT_PRIORITY, EMSESP_MQTT_RUNNING_CORE);
|
||||||
}
|
}
|
||||||
if (!_mqttClient) {
|
if (!_mqttClient) {
|
||||||
emsesp::EMSESP::logger().warning("MQTT Client alloc failed");
|
emsesp::EMSESP::logger().warning("MQTT Client alloc failed");
|
||||||
@@ -63,10 +64,10 @@ void MqttSettingsService::startClient() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
isSecure = false;
|
isSecure = false;
|
||||||
if (emsesp::EMSESP::system_.PSram() > 0) {
|
if (emsesp::EMSESP::system_.PSram() == 0) {
|
||||||
_mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::YES);
|
|
||||||
} else {
|
|
||||||
_mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::NO);
|
_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)->onConnect([this](bool sessionPresent) { onMqttConnect(sessionPresent); });
|
||||||
static_cast<espMqttClient *>(_mqttClient)->onDisconnect([this](espMqttClientTypes::DisconnectReason reason) { onMqttDisconnect(reason); });
|
static_cast<espMqttClient *>(_mqttClient)->onDisconnect([this](espMqttClientTypes::DisconnectReason reason) { onMqttDisconnect(reason); });
|
||||||
@@ -78,6 +79,9 @@ void MqttSettingsService::startClient() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MqttSettingsService::loop() {
|
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)) {
|
if (_reconfigureMqtt || (_disconnectedAt && static_cast<uint32_t>(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) {
|
||||||
// reconfigure MQTT client
|
// reconfigure MQTT client
|
||||||
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
|
_disconnectedAt = configureMqtt() ? 0 : uuid::get_uptime();
|
||||||
@@ -93,11 +97,11 @@ bool MqttSettingsService::isEnabled() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool MqttSettingsService::isConnected() {
|
bool MqttSettingsService::isConnected() {
|
||||||
return _mqttClient->connected();
|
return _mqttClient ? _mqttClient->connected() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * MqttSettingsService::getClientId() {
|
const char * MqttSettingsService::getClientId() {
|
||||||
return _mqttClient->getClientId();
|
return _mqttClient ? _mqttClient->getClientId() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttSettingsService::onMqttMessage(const espMqttClientTypes::MessageProperties & properties,
|
void MqttSettingsService::onMqttMessage(const espMqttClientTypes::MessageProperties & properties,
|
||||||
|
|||||||
@@ -50,6 +50,19 @@
|
|||||||
#define FACTORY_MQTT_MAX_TOPIC_LENGTH 128
|
#define FACTORY_MQTT_MAX_TOPIC_LENGTH 128
|
||||||
#endif
|
#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 {
|
class MqttSettings {
|
||||||
public:
|
public:
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
|
|||||||
switch (event) {
|
switch (event) {
|
||||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||||
if (_connected) {
|
if (_connected && emsesp::EMSESP::system_.ntp_connected()) {
|
||||||
emsesp::EMSESP::logger().info("WiFi connection dropped, stopping NTP");
|
emsesp::EMSESP::logger().info("WiFi connection dropped, stopping NTP");
|
||||||
_connected = false;
|
_connected = false;
|
||||||
configureNTP();
|
configureNTP();
|
||||||
|
|||||||
@@ -23,7 +23,25 @@ namespace emsesp {
|
|||||||
|
|
||||||
uuid::log::Logger AnalogSensor::logger_{F_(analogsensor), uuid::log::Facility::DAEMON};
|
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
|
reload(true); // fetch the list of sensors from our customization service
|
||||||
|
|
||||||
if (!analog_enabled_) {
|
if (!analog_enabled_) {
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ class AnalogSensor {
|
|||||||
RGB = 11
|
RGB = 11
|
||||||
};
|
};
|
||||||
|
|
||||||
void start();
|
void start(const bool factory_settings = false);
|
||||||
void loop();
|
void loop();
|
||||||
void publish_sensor(const Sensor & sensor) const;
|
void publish_sensor(const Sensor & sensor) const;
|
||||||
void publish_values(const bool force);
|
void publish_values(const bool force);
|
||||||
@@ -168,11 +168,11 @@ class AnalogSensor {
|
|||||||
void store_counters();
|
void store_counters();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr double Beta = 3380;
|
static constexpr double Beta = 4260;
|
||||||
static constexpr double T0 = 273.15;
|
static constexpr double T0 = 273.15;
|
||||||
static constexpr double T25 = 298.15;
|
static constexpr double T25 = 298.15;
|
||||||
static constexpr double R0 = 10000;
|
static constexpr double R0 = 100000;
|
||||||
static constexpr double Rt = 10000;
|
static constexpr double Rt = 60000;
|
||||||
static constexpr uint8_t MAX_SENSORS = 20;
|
static constexpr uint8_t MAX_SENSORS = 20;
|
||||||
static constexpr uint32_t MEASURE_ANALOG_INTERVAL = 500;
|
static constexpr uint32_t MEASURE_ANALOG_INTERVAL = 500;
|
||||||
|
|
||||||
|
|||||||
@@ -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::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());
|
std::string board_profile = Helpers::toUpper(arguments.front());
|
||||||
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ enum {
|
|||||||
#ifndef STRINGIZE
|
#ifndef STRINGIZE
|
||||||
#define STRINGIZE(s) #s
|
#define STRINGIZE(s) #s
|
||||||
#endif
|
#endif
|
||||||
#if TASMOTA_SDK
|
#ifdef TASMOTA_SDK
|
||||||
#define ARDUINO_VERSION_STR(major, minor, patch) "Tasmota Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
|
#define ARDUINO_VERSION_STR(major, minor, patch) "Tasmota Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
|
||||||
#else
|
#else
|
||||||
#define ARDUINO_VERSION_STR(major, minor, patch) "ESP32 Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
|
#define ARDUINO_VERSION_STR(major, minor, patch) "ESP32 Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
|
||||||
|
|||||||
@@ -139,6 +139,7 @@
|
|||||||
{164, DeviceType::SOLAR, "SM200, MS200", DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
{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
|
// 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},
|
{ 69, DeviceType::MIXER, "MM10", DeviceFlags::EMS_DEVICE_FLAG_MM10},
|
||||||
{100, DeviceType::MIXER, "IPM", DeviceFlags::EMS_DEVICE_FLAG_IPM},
|
{100, DeviceType::MIXER, "IPM", DeviceFlags::EMS_DEVICE_FLAG_IPM},
|
||||||
{102, DeviceType::MIXER, "IPM2", 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},
|
{16, DeviceType::HEATPUMP, "CSH5800iG", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||||
|
|
||||||
// Ventilation - 0x51
|
// 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
|
// Heatsource - 0x60
|
||||||
{228, DeviceType::HEATSOURCE, "AM200", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // alternative heatsource
|
{228, DeviceType::HEATSOURCE, "AM200", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // alternative heatsource
|
||||||
|
|||||||
@@ -1711,8 +1711,8 @@ void EMSESP::start() {
|
|||||||
mqtt_.start(); // mqtt init
|
mqtt_.start(); // mqtt init
|
||||||
system_.start(); // starts commands, led, adc, button, network (sets hostname), syslog & uart
|
system_.start(); // starts commands, led, adc, button, network (sets hostname), syslog & uart
|
||||||
shower_.start(); // initialize shower timer and shower alert
|
shower_.start(); // initialize shower timer and shower alert
|
||||||
temperaturesensor_.start(); // Temperature external sensors
|
temperaturesensor_.start(factory_settings); // Temperature external sensors
|
||||||
analogsensor_.start(); // Analog external sensors
|
analogsensor_.start(factory_settings); // Analog external sensors
|
||||||
|
|
||||||
// start web services
|
// start web services
|
||||||
webLogService.start(); // apply settings to weblog service
|
webLogService.start(); // apply settings to weblog service
|
||||||
|
|||||||
@@ -524,7 +524,12 @@ bool Helpers::value2number(const char * value, int & value_i, const int min, con
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x') {
|
||||||
|
value_i = hextoint(value);
|
||||||
|
} else {
|
||||||
value_i = atoi(value);
|
value_i = atoi(value);
|
||||||
|
}
|
||||||
|
|
||||||
if (value_i >= min && value_i <= max) {
|
if (value_i >= min && value_i <= max) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(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(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(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
|
// 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")
|
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")
|
||||||
|
|||||||
@@ -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
|
// add hvac_action - https://github.com/emsesp/EMS-ESP32/discussions/2562
|
||||||
doc["action_topic"] = "~/boiler_data";
|
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
|
// the HA climate component only responds to auto, heat and off
|
||||||
JsonArray modes = doc["modes"].to<JsonArray>();
|
JsonArray modes = doc["modes"].to<JsonArray>();
|
||||||
|
|||||||
@@ -50,6 +50,10 @@
|
|||||||
|
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
|
#ifndef EMSESP_STANDALONE
|
||||||
|
#include "esp_efuse.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
// Languages supported. Note: the order is important
|
// Languages supported. Note: the order is important
|
||||||
@@ -1426,6 +1430,15 @@ bool System::command_service(const char * cmd, const char * value) {
|
|||||||
ok = true;
|
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) {
|
if (ok) {
|
||||||
LOG_INFO("System command '%s' with value '%s'", cmd, value);
|
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
|
data = {2, 4, 5, 17, 33, PHY_type::PHY_TYPE_LAN8720, 16, 1, 0, 0}; // BBQKees Gateway E32
|
||||||
} else if (board_profile == "E32V2") {
|
} else if (board_profile == "E32V2") {
|
||||||
data = {2, 14, 4, 5, 34, PHY_type::PHY_TYPE_LAN8720, 15, 0, 1, 0}; // BBQKees Gateway E32 V2
|
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") {
|
} 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
|
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") {
|
} else if (board_profile == "NODEMCU") {
|
||||||
@@ -2030,6 +2045,7 @@ bool System::ntp_connected() {
|
|||||||
// see if its a BBQKees Gateway by checking the nvs values
|
// see if its a BBQKees Gateway by checking the nvs values
|
||||||
String System::getBBQKeesGatewayDetails() {
|
String System::getBBQKeesGatewayDetails() {
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
|
/*
|
||||||
if (!EMSESP::nvs_.isKey("mfg")) {
|
if (!EMSESP::nvs_.isKey("mfg")) {
|
||||||
return "";
|
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");
|
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
|
#else
|
||||||
return "";
|
return "";
|
||||||
#endif
|
#endif
|
||||||
@@ -2161,7 +2197,7 @@ bool System::readCommand(const char * data) {
|
|||||||
|
|
||||||
// first check deviceID
|
// first check deviceID
|
||||||
if ((p = strtok(data_args, " ,"))) { // delimiter comma or space
|
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
|
device_id = (uint8_t)Helpers::hextoint(value); // convert hex to int
|
||||||
if (!EMSESP::valid_device(device_id)) {
|
if (!EMSESP::valid_device(device_id)) {
|
||||||
LOG_ERROR("Invalid device ID (0x%02X) in read command", 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;
|
uint8_t num_args = 0;
|
||||||
while (p != 0) {
|
while (p != 0) {
|
||||||
if ((p = strtok(nullptr, " ,"))) { // delimiter comma or space
|
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) {
|
if (num_args == 0) {
|
||||||
type_id = (uint16_t)Helpers::hextoint(value); // convert hex to int
|
type_id = (uint16_t)Helpers::hextoint(value); // convert hex to int
|
||||||
} else if (num_args == 1) {
|
} else if (num_args == 1) {
|
||||||
|
|||||||
@@ -649,7 +649,7 @@ uint16_t TxService::read_next_tx(const uint8_t offset, const uint8_t length) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// we request all and get a short telegram with requested offset
|
// 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;
|
return 0;
|
||||||
}
|
}
|
||||||
if (offset >= telegram_last_->offset && old_length > 0 && next_length > 0) {
|
if (offset >= telegram_last_->offset && old_length > 0 && next_length > 0) {
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ namespace emsesp {
|
|||||||
uuid::log::Logger TemperatureSensor::logger_{F_(temperaturesensor), uuid::log::Facility::DAEMON};
|
uuid::log::Logger TemperatureSensor::logger_{F_(temperaturesensor), uuid::log::Facility::DAEMON};
|
||||||
|
|
||||||
// start the 1-wire
|
// 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();
|
reload();
|
||||||
|
|
||||||
if (!dallas_gpio_) {
|
if (!dallas_gpio_) {
|
||||||
@@ -199,6 +201,31 @@ void TemperatureSensor::loop() {
|
|||||||
scancnt_ = 0;
|
scancnt_ = 0;
|
||||||
} else if (scancnt_ == SCAN_START + 1) { // startup
|
} else if (scancnt_ == SCAN_START + 1) { // startup
|
||||||
firstscan_ = sensors_.size();
|
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_);
|
// 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 #
|
} else if ((scancnt_ <= 0) && (firstscan_ != sensors_.size())) { // check 2 times for no change of sensor #
|
||||||
scancnt_ = SCAN_START;
|
scancnt_ = SCAN_START;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class TemperatureSensor {
|
|||||||
TemperatureSensor() = default;
|
TemperatureSensor() = default;
|
||||||
~TemperatureSensor() = default;
|
~TemperatureSensor() = default;
|
||||||
|
|
||||||
void start();
|
void start(const bool factory_settings = false);
|
||||||
void loop();
|
void loop();
|
||||||
void publish_sensor(const Sensor & sensor);
|
void publish_sensor(const Sensor & sensor);
|
||||||
void publish_values(const bool force);
|
void publish_values(const bool force);
|
||||||
@@ -170,6 +170,7 @@ class TemperatureSensor {
|
|||||||
bool changed_ = false;
|
bool changed_ = false;
|
||||||
uint32_t sensorfails_ = 0;
|
uint32_t sensorfails_ = 0;
|
||||||
uint32_t sensorreads_ = 0;
|
uint32_t sensorreads_ = 0;
|
||||||
|
bool set_internal_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
|||||||
|
|
||||||
// not ems1.0, but HT3
|
// not ems1.0, but HT3
|
||||||
if (model() != EMSdevice::EMS_DEVICE_FLAG_EMS) {
|
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));
|
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
|
* Boiler(0x08) -> Me(0x0B), ?(0x26), data: 01 05 00 0F 00 1E 58 5A
|
||||||
*/
|
*/
|
||||||
void Boiler::process_UBASettingsWW(std::shared_ptr<const Telegram> telegram) {
|
void Boiler::process_UBASettingsWW(std::shared_ptr<const Telegram> telegram) {
|
||||||
has_update(telegram, wwMaxPower_, 7);
|
has_update(telegram, wwMaxPower_, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x33
|
// 0x33
|
||||||
@@ -2578,7 +2578,7 @@ bool Boiler::set_ww_maxpower(const char * value, const int8_t id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_command(EMS_TYPE_UBASettingsWW, 7, v, EMS_TYPE_UBASettingsWW);
|
write_command(EMS_TYPE_UBASettingsWW, 10, v, EMS_TYPE_UBASettingsWW);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2839,7 +2839,7 @@ bool Boiler::set_ww_disinfect(const char * value, const int8_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_received(EMS_TYPE_UBAParameterWWPlus)) {
|
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 {
|
} else {
|
||||||
write_command(EMS_TYPE_UBAFlags, 0, (v ? 0x44 : 0x04), 0x34);
|
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
|
return true; // dash
|
||||||
} else if (num == 1) {
|
} else if (num == 1) {
|
||||||
// LOG_INFO("Reset boiler maintenance message");
|
// LOG_INFO("Reset boiler maintenance message");
|
||||||
write_command(0x05, 0x08, 0xFF, 0x1C);
|
write_command(0x05, 8, 0xFF, 0x1C);
|
||||||
return true;
|
return true;
|
||||||
} else if (num == 2) {
|
} else if (num == 2) {
|
||||||
// LOG_INFO("Reset boiler error message");
|
// LOG_INFO("Reset boiler error message");
|
||||||
write_command(0x05, 0x00, 0x5A); // error reset
|
write_command(0x05, 0, 0x5A); // error reset
|
||||||
return true;
|
return true;
|
||||||
} else if (num == 3) {
|
} else if (num == 3) {
|
||||||
// LOG_INFO("Reset boiler history");
|
// LOG_INFO("Reset boiler history");
|
||||||
@@ -2928,7 +2928,11 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
|
|||||||
return true;
|
return true;
|
||||||
} else if (num == 4) {
|
} else if (num == 4) {
|
||||||
// LOG_INFO("Reset boiler message");
|
// 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 true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class Boiler : public EMSdevice {
|
|||||||
|
|
||||||
uint8_t boilerState_ = EMS_VALUE_UINT8_NOTSET; // Boiler state flag - FOR INTERNAL USE
|
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_UBAParameterWW = 0x33;
|
||||||
static constexpr uint8_t EMS_TYPE_UBAFunctionTest = 0x1D;
|
static constexpr uint8_t EMS_TYPE_UBAFunctionTest = 0x1D;
|
||||||
static constexpr uint8_t EMS_TYPE_UBAFlags = 0x35;
|
static constexpr uint8_t EMS_TYPE_UBAFlags = 0x35;
|
||||||
|
|||||||
@@ -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, &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, &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, &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,
|
register_device_value(tag,
|
||||||
&setValveTime_,
|
&setValveTime_,
|
||||||
DeviceValueType::UINT8,
|
DeviceValueType::UINT8,
|
||||||
@@ -47,6 +50,7 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
|
|||||||
10,
|
10,
|
||||||
600);
|
600);
|
||||||
register_device_value(tag, &flowTempOffset_, DeviceValueType::UINT8, FL_(flowtempoffset), DeviceValueUOM::K, MAKE_CF_CB(set_flowTempOffset), 0, 20);
|
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
|
// EMS 1.0
|
||||||
@@ -91,6 +95,7 @@ void Mixer::process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> teleg
|
|||||||
has_update(telegram, flowSetTemp_, 5);
|
has_update(telegram, flowSetTemp_, 5);
|
||||||
has_bitupdate(telegram, pumpStatus_, 0, 0);
|
has_bitupdate(telegram, pumpStatus_, 0, 0);
|
||||||
has_update(telegram, status_, 2); // valve status
|
has_update(telegram, status_, 2); // valve status
|
||||||
|
has_update(telegram, flowRate_, 9); // l/h
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mixer IPM - 0x010C
|
// 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, activated_, 0); // on = 0xFF
|
||||||
has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, default 120 s, max 600 s
|
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, flowTempOffset_, 2); // Mixer increase [0-20 K]
|
||||||
|
has_update(telegram, pressure_, 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thermostat(0x10) -> Mixer(0x20), ?(0x2E1), data: 01 1C 64 00 01
|
// 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;
|
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
|
} // namespace emsesp
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class Mixer : public EMSdevice {
|
|||||||
bool set_activated(const char * value, const int8_t id);
|
bool set_activated(const char * value, const int8_t id);
|
||||||
bool set_setValveTime(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_flowTempOffset(const char * value, const int8_t id);
|
||||||
|
bool set_pressure(const char * value, const int8_t id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint16_t flowTempHc_;
|
uint16_t flowTempHc_;
|
||||||
@@ -55,6 +56,8 @@ class Mixer : public EMSdevice {
|
|||||||
uint8_t activated_;
|
uint8_t activated_;
|
||||||
uint8_t setValveTime_;
|
uint8_t setValveTime_;
|
||||||
uint8_t flowTempOffset_;
|
uint8_t flowTempOffset_;
|
||||||
|
uint16_t flowRate_; // l/h
|
||||||
|
uint8_t pressure_; // setting 150-750mbar, scale 50
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace emsesp
|
} // namespace emsesp
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.7.3-dev.14"
|
#define EMSESP_APP_VERSION "3.7.3-dev.16"
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
uint8_t EMSuart::last_tx_src_ = 0;
|
uint8_t EMSuart::last_tx_src_ = 0;
|
||||||
|
|
||||||
|
static TaskHandle_t xHandle;
|
||||||
static QueueHandle_t uart_queue;
|
static QueueHandle_t uart_queue;
|
||||||
uint8_t tx_mode_ = 0xFF;
|
uint8_t tx_mode_ = 0xFF;
|
||||||
uint32_t inverse_mask = 0;
|
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_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_pin(EMSUART_NUM, tx_gpio, rx_gpio, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||||
uart_set_line_inverse(EMSUART_NUM, inverse_mask);
|
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_full_threshold(EMSUART_NUM, 1);
|
||||||
uart_set_rx_timeout(EMSUART_NUM, 0); // disable
|
uart_set_rx_timeout(EMSUART_NUM, 0); // disable
|
||||||
|
|
||||||
// note esp32s3 crashes with 2k stacksize, stack overflow here sometimes wipes settingsfiles.
|
// 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;
|
tx_mode_ = tx_mode;
|
||||||
uart_enable_intr_mask(EMSUART_NUM, UART_BRK_DET_INT_ENA | UART_RXFIFO_FULL_INT_ENA);
|
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() {
|
void EMSuart::stop() {
|
||||||
if (tx_mode_ != 0xFF) { // only call after driver initialisation
|
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);
|
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
|
* buf contains the CRC and len is #bytes including the CRC
|
||||||
* returns code, 1=success
|
* 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) {
|
if (len == 0 || len >= EMS_MAXBUFFERSIZE) {
|
||||||
return EMS_TX_STATUS_ERR;
|
return EMS_TX_STATUS_ERR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,17 @@
|
|||||||
#ifndef EMSESP_EMSUART_H
|
#ifndef EMSESP_EMSUART_H
|
||||||
#define 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 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
|
#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 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 send_poll(const uint8_t data);
|
||||||
static void stop();
|
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() {
|
static uint8_t last_tx_src() {
|
||||||
return last_tx_src_;
|
return last_tx_src_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ void WebLogService::show(Shell & shell) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebLogService::loop() {
|
void WebLogService::loop() {
|
||||||
if (!events_.count() || log_messages_.empty()) {
|
if (!events_.count() || log_messages_.empty() || events_.avgPacketsWaiting() >= SSE_MAX_QUEUED_MESSAGES) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,12 @@ void WebSchedulerService::begin() {
|
|||||||
Mqtt::subscribe(EMSdevice::DeviceType::SCHEDULER, topic, nullptr); // use empty function callback
|
Mqtt::subscribe(EMSdevice::DeviceType::SCHEDULER, topic, nullptr); // use empty function callback
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
if (EMSESP::system_.PSram()) {
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,18 @@
|
|||||||
#define EMSESP_SCHEDULER_FILE "/config/emsespScheduler.json"
|
#define EMSESP_SCHEDULER_FILE "/config/emsespScheduler.json"
|
||||||
#define EMSESP_SCHEDULER_SERVICE_PATH "/rest/schedule" // GET and POST
|
#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
|
// bit flags for the schedule items. Matches those in interface/src/app/main/SchedulerDialog.tsx
|
||||||
// 0-127 (0->0x7F) is day schedule
|
// 0-127 (0->0x7F) is day schedule
|
||||||
// 128/0x80 is timer
|
// 128/0x80 is timer
|
||||||
|
|||||||
@@ -179,7 +179,12 @@ StateUpdateResult WebSettings::update(JsonObject root, WebSettings & settings) {
|
|||||||
#else
|
#else
|
||||||
if (ETH.begin(ETH_PHY_LAN8720, 0, 23, 18, 15, ETH_CLOCK_GPIO0_OUT)) {
|
if (ETH.begin(ETH_PHY_LAN8720, 0, 23, 18, 15, ETH_CLOCK_GPIO0_OUT)) {
|
||||||
#endif
|
#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
|
settings.board_profile = "E32V2"; // Ethernet and PSRAM
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
settings.board_profile = "S32"; // ESP32 standard WiFi with PSRAM
|
settings.board_profile = "S32"; // ESP32 standard WiFi with PSRAM
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user