mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-27 19:57:38 +00:00
Merge pull request #5287 from tonhuisman/feature/P020-data-handling-improvements
[P020] Improve data handling, add UDP receiver
This commit is contained in:
@@ -45,6 +45,18 @@ Device Settings
|
||||
|
||||
* **TCP Port**: The port for an external network client to read the data from, range 1..65535. The used port number must be unique within the device.
|
||||
|
||||
* **Protocol**: Select the protocol(s) to listen for on the selected port. Available options:
|
||||
|
||||
.. image:: P020_ProtocolOptions.png
|
||||
|
||||
* *TCP (Default)*: The original plugin only listened for TCP connections, so that's the default setting.
|
||||
|
||||
* *UDP*: Listen for incoming data using the UDP protocol.
|
||||
|
||||
* *TCP & UDP*: Listen using TCP for an active connection, and also listen using UDP for incoming data.
|
||||
|
||||
As UDP uses a stateless connection, and is only used for *receiving* data, this is not recognized as an external device listening. Data received via UDP is sent to the serial port verbatim, so a serial port must be configured. TCP and UDP can use a matching connection on the same port number, at the same time.
|
||||
|
||||
* **Baud Rate / Serial config**: See *Serial helper configuration*, above.
|
||||
|
||||
* **Event Processing**: Select the type of data that is expected, to enable correct preprocessing. Available options:
|
||||
@@ -71,6 +83,9 @@ Device Settings
|
||||
|
||||
* **P1 #data event with message**: When enabled, the *P1 WiFi Gateway* Event Processing option will include the received message. **WARNING** This may easily cause memory overflow exceptions, especially when running on ESP8266 or other low-memory situations!
|
||||
|
||||
* **Event data hex format**: When enabled, the data received will be emitted in hex format (starts with ``0x`` and all bytes hex encoded), instead of the regular text format. This enables for received binary data to be passed to an external receiver without losing special bytes like ``0x00`` etc.
|
||||
|
||||
|
||||
When selecting the **Event processing** options *Generic* or *RFLink*, after submitting the page will show extra options for the events generated:
|
||||
|
||||
.. image:: P020_EventOptions.png
|
||||
@@ -138,6 +153,8 @@ Change log
|
||||
.. versionchanged:: 2.0
|
||||
...
|
||||
|
||||
|changed| 2025-03-30: Add Protocol (for receiving data via UDP) and Event data hex format options.
|
||||
|
||||
|changed| 2022-12-13: Merge of P020 and P044 to reduce code size and combine features, as P044 was initially started as a spin-off from P020, but not evolved with the P020 features.
|
||||
|
||||
|added|
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 84 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -35,3 +35,10 @@
|
||||
``'0xXXxx XX,xx-XX:xx'``: A sequence of hexadecimal values (range: 0x00..0xFF), that *can* be separated by a space, comma, dash, colon, semicolon or period, or are just entered adjecent. Only the first 2 characters should be ``0x`` or ``0X``, the rest is interpreted as hex bytes, and appended to the string to send. Quotes are only required if space or comma separators are used.
|
||||
Using this command, either from rules, via http or mqtt, the text that is provided as content is completely sent to the serial port. No extra data is added, other than any (system) variables that are included, being replaced.
|
||||
"
|
||||
"
|
||||
``serialsend_test,'<content to place in serial buffer>'``
|
||||
|
||||
``<content to place in serial buffer>``: Text that will be put into the serial buffer (nearly) unprocessed. Only the regular variable replacements will be applied before sending the content to the serial port.
|
||||
","
|
||||
This command is intended for testing as if data was received via serial. After placing the data in the buffer, the regular handling of that data is initiated.
|
||||
"
|
||||
|
||||
@@ -493,7 +493,7 @@ the '=' sign).
|
||||
For historic reasons, ``%eventvalue%`` without a number, can also be used to access the first event value.
|
||||
Thus it will be the same when using ``%eventvalue1%``.
|
||||
|
||||
There is one exception; When the event starts with an ``!``, ``%eventvalue%`` does refer to the literal event, or the part of the event after the ``#`` character.
|
||||
There is one exception; When the event starts with an ``!``, ``%eventvalue%`` does refer to the literal event, an with ``%eventname%`` and ``%eventpar%`` the part left and right of the ``#`` character can be used (processing ``%eventname%`` and ``%eventpar%`` was added 2025-03-30).
|
||||
This was introduced for the Serial Server plugin (P020) which sends events like ``!Serial#`` followed by the received string.
|
||||
|
||||
|
||||
@@ -931,6 +931,10 @@ Usage: ``{substring:<startpos>:<endpos>:<string>}``
|
||||
|
||||
The position arguments are the same as in Arduino ``String::substring`` , meaning the endpos is 1 position further than the last character you need.
|
||||
|
||||
Added: 2025-03-31
|
||||
|
||||
If ``<endpos>`` is not provided or invalid (the colon has to stay included!), the remainder of ``<string>`` beginning at ``<startpos>`` is returned (like Arduino behavior).
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@@ -238,6 +238,22 @@ void ESPeasySerial::setDebugOutput(bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
size_t ESPeasySerial::setRxBufferSize(size_t new_size)
|
||||
{
|
||||
if (isValid()) {
|
||||
return _serialPort->setRxBufferSize(new_size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t ESPeasySerial::setTxBufferSize(size_t new_size)
|
||||
{
|
||||
if (isValid()) {
|
||||
return _serialPort->setTxBufferSize(new_size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ESPeasySerial::isTxEnabled(void)
|
||||
{
|
||||
return getTxPin() != -1;
|
||||
|
||||
@@ -121,6 +121,11 @@ public:
|
||||
|
||||
void setDebugOutput(bool);
|
||||
|
||||
// Set buffer size, should be called before calling begin()
|
||||
size_t setRxBufferSize(size_t new_size);
|
||||
size_t setTxBufferSize(size_t new_size);
|
||||
|
||||
|
||||
bool isTxEnabled(void);
|
||||
bool isRxEnabled(void);
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
|
||||
inline size_t write(const char *s)
|
||||
{
|
||||
return write((uint8_t *)s, strlen(s));
|
||||
return (s) ? write((uint8_t *)s, strlen(s)) : 0;
|
||||
}
|
||||
|
||||
inline size_t write(unsigned long n)
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
|
||||
virtual void setDebugOutput(bool) = 0;
|
||||
|
||||
|
||||
// Set buffer size, should be called before calling begin()
|
||||
virtual size_t setRxBufferSize(size_t new_size) = 0;
|
||||
virtual size_t setTxBufferSize(size_t new_size) = 0;
|
||||
virtual bool setRS485Mode(int8_t rtsPin, bool enableCollisionDetection) = 0;
|
||||
|
||||
+63
-15
@@ -8,6 +8,17 @@
|
||||
|
||||
/************
|
||||
* Changelog:
|
||||
* 2025-03-26 tonhuisman: Add optional receiving (relaying) of UDP data to Serial. Uses the same port as configured for the (default) TCP
|
||||
* connection. Available on ESP32 only.
|
||||
* Can be enabled for ESP8266 in a Custom build by adding #define P020_USE_PROTOCOL 1
|
||||
* Added logging for pass-through data size from TCP or UDP to Serial.
|
||||
* 2025-03-23 tonhuisman: Add 'Event data hex format' setting to convert received data as hex so it can be sent via SerialSendMix.
|
||||
* Data received via TCP is now sent to serial via the 'write' method instead of 'print' so any binary content is
|
||||
* passed through unaltered.
|
||||
* Add command 'serialsend_test,<content>' for filling the serial buffer with the content, and processing as if
|
||||
* serial data was received.
|
||||
* Use RX Buffer size to allocate receive buffer instead of fixed size 256 (2048 for P1 processing), also used as
|
||||
* limit for receiving via TCP.
|
||||
* 2023-08-26 tonhuisman: P044 mode: Set RX time-out default to 50 msec for better receive pace of P1 data
|
||||
* 2023-08-17 tonhuisman: P1 data: Allow some extra reading timeout between the data and the checksum, as some meters need more time to
|
||||
* calculate the CRC. Add CR/LF before sending P1 data.
|
||||
@@ -181,6 +192,21 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string)
|
||||
addUnit(F("0..65535"));
|
||||
# endif // ifndef LIMIT_BUILD_SIZE
|
||||
|
||||
# if P020_USE_PROTOCOL
|
||||
const __FlashStringHelper*tcpudpOptions[] = {
|
||||
F("TCP"),
|
||||
F("UDP"),
|
||||
F("TCP & UDP"),
|
||||
};
|
||||
constexpr int tcpudpCount = NR_ELEMENTS(tcpudpOptions);
|
||||
FormSelectorOptions tcpudpSelector(tcpudpCount, tcpudpOptions);
|
||||
tcpudpSelector.default_index = 0;
|
||||
tcpudpSelector.addFormSelector(
|
||||
F("Protocol"),
|
||||
F("pproto"),
|
||||
P020_GET_PROTOCOL);
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
|
||||
addFormNumericBox(F("Baud Rate"), F("pbaud"), P020_GET_BAUDRATE, 0);
|
||||
uint8_t serialConfChoice = serialHelper_convertOldSerialConfig(P020_SERIAL_CONFIG);
|
||||
serialHelper_serialconfig_webformLoad(event, serialConfChoice);
|
||||
@@ -192,25 +218,19 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string)
|
||||
F("RFLink"),
|
||||
F("P1 WiFi Gateway")
|
||||
};
|
||||
/*
|
||||
const int optionValues[] = {
|
||||
static_cast<int>(P020_Events::None),
|
||||
static_cast<int>(P020_Events::Generic),
|
||||
static_cast<int>(P020_Events::RFLink),
|
||||
static_cast<int>(P020_Events::P1WiFiGateway),
|
||||
};
|
||||
*/
|
||||
|
||||
constexpr int optionCount = NR_ELEMENTS(options);
|
||||
const FormSelectorOptions selector(optionCount, options /*, optionValues*/);
|
||||
selector.addFormSelector(
|
||||
F("Event processing"),
|
||||
F("pevents"),
|
||||
F("Event processing"),
|
||||
F("pevents"),
|
||||
P020_SERIAL_PROCESSING);
|
||||
}
|
||||
addFormCheckBox(F("P1 #data event with message"), F("pp1event"), P020_GET_P1_EVENT_DATA);
|
||||
# ifndef LIMIT_BUILD_SIZE
|
||||
addFormNote(F("When enabled, passes the entire message in the event. <B>Warning:</B> can cause memory overflow issues!"));
|
||||
# endif // ifndef LIMIT_BUILD_SIZE
|
||||
addFormCheckBox(F("Event data hex format"), F("phexdata"), P020_GET_EVENT_AS_HEX);
|
||||
|
||||
if (P020_Events::Generic == static_cast<P020_Events>(P020_SERIAL_PROCESSING)) {
|
||||
addFormCheckBox(F("Use Serial Port as eventname"), F("pevtname"), P020_GET_EVENT_SERIAL_ID);
|
||||
@@ -289,6 +309,7 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string)
|
||||
bitWrite(lSettings, P020_FLAG_LED_ENABLED, isFormItemChecked(F("pled")));
|
||||
bitWrite(lSettings, P020_FLAG_LED_INVERTED, isFormItemChecked(F("pledinv")));
|
||||
bitWrite(lSettings, P020_FLAG_P1_EVENT_DATA, isFormItemChecked(F("pp1event")));
|
||||
bitWrite(lSettings, P020_FLAG_EVENT_AS_HEX, isFormItemChecked(F("phexdata")));
|
||||
|
||||
if (P020_Events::Generic == static_cast<P020_Events>(P020_SERIAL_PROCESSING)) {
|
||||
bitWrite(lSettings, P020_FLAG_EVENT_SERIAL_ID, isFormItemChecked(F("pevtname")));
|
||||
@@ -303,6 +324,9 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string)
|
||||
} else {
|
||||
bitWrite(lSettings, P020_FLAG_MULTI_LINE, isFormItemChecked(F("pmultiline")));
|
||||
}
|
||||
# if P020_USE_PROTOCOL
|
||||
set2BitToUL(lSettings, P020_FLAG_PROTOCOL, getFormItemInt(F("pproto")));
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
|
||||
P020_FLAGS = lSettings;
|
||||
|
||||
@@ -360,14 +384,22 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string)
|
||||
# ifndef LIMIT_BUILD_SIZE
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: TaskIndex=%d port=%d rxPin=%d txPin=%d BAUDRATE=%d SERVER_PORT=%d SERIAL_PROCESSING=%d"),
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: TaskIndex=%d port=%d rxPin=%d txPin=%d BAUDRATE=%d SERVER_PORT=%d SERIAL_PROCESSING=%d"
|
||||
# if P020_USE_PROTOCOL
|
||||
" Protocol=%d"
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
),
|
||||
event->TaskIndex + 1,
|
||||
CONFIG_PORT,
|
||||
rxPin,
|
||||
txPin,
|
||||
P020_GET_BAUDRATE,
|
||||
P020_GET_SERVER_PORT,
|
||||
P020_SERIAL_PROCESSING));
|
||||
P020_SERIAL_PROCESSING
|
||||
# if P020_USE_PROTOCOL
|
||||
, P020_GET_PROTOCOL
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
));
|
||||
}
|
||||
# endif // ifndef LIMIT_BUILD_SIZE
|
||||
|
||||
@@ -447,10 +479,22 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string)
|
||||
P020_Task *task = static_cast<P020_Task *>(getPluginTaskData(event->TaskIndex));
|
||||
|
||||
if (nullptr != task) {
|
||||
bool hasClient = task->hasClientConnected();
|
||||
const bool hasClient = task->hasClientConnected();
|
||||
# if P020_USE_PROTOCOL
|
||||
const P020_Protocol_e prot = static_cast<P020_Protocol_e>(P020_GET_PROTOCOL);
|
||||
const bool useUdp = P020_Protocol_e::UDP == prot || P020_Protocol_e::TCP_UDP == prot;
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
|
||||
if (P020_IGNORE_CLIENT_CONNECTED || hasClient) {
|
||||
if (hasClient) {
|
||||
if (P020_IGNORE_CLIENT_CONNECTED || hasClient
|
||||
# if P020_USE_PROTOCOL
|
||||
|| useUdp
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
) {
|
||||
if (hasClient
|
||||
# if P020_USE_PROTOCOL
|
||||
|| useUdp
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
) {
|
||||
task->handleClientIn(event);
|
||||
}
|
||||
task->handleSerialIn(event); // in case of second serial connected, PLUGIN_SERIAL_IN is not called anymore
|
||||
@@ -496,6 +540,10 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string)
|
||||
task->ser2netClient.print(string.substring(18));
|
||||
task->ser2netClient.PR_9453_FLUSH_TO_CLEAR();
|
||||
success = true;
|
||||
} else if (equals(command, F("serialsend_test"))) {
|
||||
task->serial_buffer = parseStringToEndKeepCaseNoTrim(string, 2);
|
||||
task->handleSerialIn(event);
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -232,8 +232,8 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string)
|
||||
if (PCONFIG(0) == SER_SWITCH_YEWE)
|
||||
{
|
||||
Plugin_091_numrelay = PCONFIG(1);
|
||||
ESPEASY_SERIAL_0.begin(9600, SERIAL_8N1);
|
||||
ESPEASY_SERIAL_0.setRxBufferSize(BUFFER_SIZE); // Arduino core for ESP8266 WiFi chip 2.4.0
|
||||
ESPEASY_SERIAL_0.begin(9600, SERIAL_8N1);
|
||||
delay(1);
|
||||
getmcustate(); // request status on startup
|
||||
log += strformat(F(" Yewe %d btn"), Plugin_091_numrelay);
|
||||
|
||||
@@ -526,9 +526,12 @@ void parse_string_commands(String& line) {
|
||||
// substring arduino style (first char included, last char excluded)
|
||||
// Syntax like 12345{substring:8:12:ANOTHER HELLO WORLD}67890
|
||||
|
||||
if (arg1valid
|
||||
&& arg2valid) {
|
||||
replacement = arg3.substring(startpos, endpos);
|
||||
if (arg1valid) {
|
||||
if (arg2valid){
|
||||
replacement = arg3.substring(startpos, endpos);
|
||||
} else {
|
||||
replacement = arg3.substring(startpos);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case string_commands_e::indexof:
|
||||
@@ -685,12 +688,13 @@ void substitute_eventvalue(String& line, const String& event) {
|
||||
}
|
||||
|
||||
if (line.indexOf(F("%event")) != -1) {
|
||||
const int equalsPos = event.indexOf('=');
|
||||
|
||||
if (event.charAt(0) == '!') {
|
||||
line.replace(F("%eventvalue%"), event); // substitute %eventvalue% with
|
||||
// literal event string if
|
||||
// starting with '!'
|
||||
} else {
|
||||
const int equalsPos = event.indexOf('=');
|
||||
|
||||
String argString;
|
||||
|
||||
@@ -757,18 +761,18 @@ void substitute_eventvalue(String& line, const String& event) {
|
||||
}
|
||||
eventvalue_pos = line.indexOf(F("%eventvalue"));
|
||||
}
|
||||
}
|
||||
|
||||
if ((line.indexOf(F("%eventname%")) != -1) ||
|
||||
(line.indexOf(F("%eventpar%")) != -1)) {
|
||||
const String eventName = equalsPos == -1 ? event : event.substring(0, equalsPos);
|
||||
if ((line.indexOf(F("%eventname%")) != -1) ||
|
||||
(line.indexOf(F("%eventpar%")) != -1)) {
|
||||
const String eventName = equalsPos == -1 ? event : event.substring(0, equalsPos);
|
||||
|
||||
// Replace %eventname% with the literal event
|
||||
line.replace(F("%eventname%"), eventName);
|
||||
// Replace %eventname% with the literal event
|
||||
line.replace(F("%eventname%"), eventName);
|
||||
|
||||
// Part of %eventname% after the # char
|
||||
const int hash_pos = eventName.indexOf('#');
|
||||
line.replace(F("%eventpar%"), hash_pos == -1 ? EMPTY_STRING : eventName.substring(hash_pos + 1));
|
||||
}
|
||||
// Part of %eventname% after the # char
|
||||
const int hash_pos = eventName.indexOf('#');
|
||||
line.replace(F("%eventpar%"), hash_pos == -1 ? EMPTY_STRING : eventName.substring(hash_pos + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
# include "../Helpers/Misc.h"
|
||||
|
||||
P020_Task::P020_Task(struct EventStruct *event) : _taskIndex(event->TaskIndex) {
|
||||
clearBuffer();
|
||||
|
||||
if (P020_GET_LED_ENABLED) {
|
||||
_ledPin = P020_LED_PIN; // Default pin (12) is already initialized in P020_Task
|
||||
}
|
||||
@@ -23,6 +21,13 @@ P020_Task::P020_Task(struct EventStruct *event) : _taskIndex(event->TaskIndex) {
|
||||
_port = static_cast<ESPEasySerialPort>(CONFIG_PORT);
|
||||
_serialId = P020_GET_EVENT_SERIAL_ID;
|
||||
_appendTaskId = P020_GET_APPEND_TASK_ID;
|
||||
_rxBufferSize = P020_RX_BUFFER;
|
||||
_eventAsHex = P020_GET_EVENT_AS_HEX;
|
||||
# if P020_USE_PROTOCOL
|
||||
_protocol = static_cast<P020_Protocol_e>(P020_GET_PROTOCOL);
|
||||
_udpport = P020_GET_SERVER_PORT;
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
clearBuffer(); // Depends on _rxBufferSize
|
||||
}
|
||||
|
||||
P020_Task::~P020_Task() {
|
||||
@@ -30,6 +35,13 @@ P020_Task::~P020_Task() {
|
||||
delete ser2netServer;
|
||||
ser2netServer = nullptr;
|
||||
}
|
||||
# if P020_USE_PROTOCOL
|
||||
|
||||
if (ser2netUdp != nullptr) {
|
||||
delete ser2netUdp;
|
||||
ser2netUdp = nullptr;
|
||||
}
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
|
||||
if (ser2netSerial != nullptr) {
|
||||
delete ser2netSerial;
|
||||
@@ -46,23 +58,35 @@ bool P020_Task::serverActive(WiFiServer *server) {
|
||||
}
|
||||
|
||||
void P020_Task::startServer(uint16_t portnumber) {
|
||||
if ((gatewayPort == portnumber) && serverActive(ser2netServer)) {
|
||||
// server is already listening on this port
|
||||
return;
|
||||
}
|
||||
stopServer();
|
||||
gatewayPort = portnumber;
|
||||
ser2netServer = new (std::nothrow) WiFiServer(portnumber);
|
||||
# if P020_USE_PROTOCOL
|
||||
|
||||
if ((nullptr != ser2netServer) && NetworkConnected()) {
|
||||
ser2netServer->begin();
|
||||
if ((P020_Protocol_e::TCP == _protocol) || (P020_Protocol_e::TCP_UDP == _protocol))
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
{
|
||||
if ((gatewayPort == portnumber) && serverActive(ser2netServer)) {
|
||||
// server is already listening on this port
|
||||
return;
|
||||
}
|
||||
stopServer();
|
||||
gatewayPort = portnumber;
|
||||
ser2netServer = new (std::nothrow) WiFiServer(portnumber);
|
||||
|
||||
if (serverActive(ser2netServer)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: WiFi server started at port %d"), portnumber));
|
||||
} else {
|
||||
addLog(LOG_LEVEL_ERROR, strformat(F("Ser2Net: WiFi server start FAILED at port %d, retrying..."), portnumber));
|
||||
if ((nullptr != ser2netServer) && NetworkConnected()) {
|
||||
ser2netServer->begin();
|
||||
|
||||
if (serverActive(ser2netServer)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: WiFi server started at port %d"), portnumber));
|
||||
} else {
|
||||
addLog(LOG_LEVEL_ERROR, strformat(F("Ser2Net: WiFi server start FAILED at port %d, retrying..."), portnumber));
|
||||
}
|
||||
}
|
||||
}
|
||||
# if P020_USE_PROTOCOL
|
||||
|
||||
if ((P020_Protocol_e::UDP == _protocol) || (P020_Protocol_e::TCP_UDP == _protocol)) {
|
||||
ser2netUdp = new (std::nothrow) WiFiUDP();
|
||||
}
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
}
|
||||
|
||||
void P020_Task::checkServer() {
|
||||
@@ -74,6 +98,22 @@ void P020_Task::checkServer() {
|
||||
addLog(LOG_LEVEL_INFO, F("Ser2Net: WiFi server started"));
|
||||
}
|
||||
}
|
||||
# if P020_USE_PROTOCOL
|
||||
|
||||
if ((nullptr != ser2netUdp) && !_udpInit && NetworkConnected()) {
|
||||
_udpInit = true; // Init only once
|
||||
|
||||
if (ser2netUdp->begin(_udpport) == 0) {
|
||||
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
|
||||
addLog(LOG_LEVEL_ERROR, strformat(F("Ser2Net: Cannot bind UDP at port %d"), _udpport));
|
||||
}
|
||||
} else {
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: UDP receiver started at port %d"), _udpport));
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
}
|
||||
|
||||
void P020_Task::stopServer() {
|
||||
@@ -85,17 +125,26 @@ void P020_Task::stopServer() {
|
||||
delete ser2netServer;
|
||||
ser2netServer = nullptr;
|
||||
}
|
||||
# if P020_USE_PROTOCOL
|
||||
|
||||
if (nullptr != ser2netUdp) {
|
||||
ser2netUdp->stop();
|
||||
addLog(LOG_LEVEL_INFO, F("Ser2Net: UDP receiver stopped"));
|
||||
delete ser2netUdp;
|
||||
ser2netUdp = nullptr;
|
||||
}
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
}
|
||||
|
||||
bool P020_Task::hasClientConnected() {
|
||||
if ((nullptr != ser2netServer) && ser2netServer->hasClient())
|
||||
{
|
||||
if (ser2netClient) { ser2netClient.stop(); }
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
# if ESP_IDF_VERSION_MAJOR >= 5
|
||||
ser2netClient = ser2netServer->accept();
|
||||
#else
|
||||
# else // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
ser2netClient = ser2netServer->available();
|
||||
#endif
|
||||
# endif // if ESP_IDF_VERSION_MAJOR >= 5
|
||||
|
||||
# ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
|
||||
|
||||
@@ -139,7 +188,7 @@ void P020_Task::clearBuffer() {
|
||||
free_string(serial_buffer);
|
||||
_maxDataGramSize = serial_processing == P020_Events::P1WiFiGateway
|
||||
? P020_P1_DATAGRAM_MAX_SIZE
|
||||
: P020_DATAGRAM_MAX_SIZE;
|
||||
: _rxBufferSize;
|
||||
serial_buffer.reserve(_maxDataGramSize);
|
||||
}
|
||||
|
||||
@@ -174,20 +223,70 @@ void P020_Task::serialEnd() {
|
||||
}
|
||||
|
||||
void P020_Task::handleClientIn(struct EventStruct *event) {
|
||||
size_t count = ser2netClient.available();
|
||||
size_t bytes_read = 0;
|
||||
uint8_t net_buf[_maxDataGramSize];
|
||||
# if P020_USE_PROTOCOL
|
||||
|
||||
if (count > 0) {
|
||||
if (count > _maxDataGramSize) { count = _maxDataGramSize; }
|
||||
bytes_read = ser2netClient.read(net_buf, count);
|
||||
ser2netSerial->write(net_buf, bytes_read);
|
||||
ser2netSerial->flush(); // Waits for the transmission of outgoing serial data to
|
||||
if (((P020_Protocol_e::TCP == _protocol) || (P020_Protocol_e::TCP_UDP == _protocol)) &&
|
||||
(nullptr != ser2netServer))
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
{
|
||||
int count = ser2netClient.available();
|
||||
|
||||
while (ser2netClient.available()) { // flush overflow data if available
|
||||
ser2netClient.read();
|
||||
if (count > 0) {
|
||||
uint8_t net_buf[_maxDataGramSize];
|
||||
|
||||
if (count > static_cast<int>(_maxDataGramSize)) { count = _maxDataGramSize; }
|
||||
const int bytes_read = ser2netClient.read(net_buf, count);
|
||||
|
||||
if (bytes_read) {
|
||||
# ifndef LIMIT_BUILD_SIZE
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: Passing through %d bytes from %s to serial."), bytes_read, FsP(F("TCP"))));
|
||||
}
|
||||
# endif // ifndef LIMIT_BUILD_SIZE
|
||||
ser2netSerial->write(net_buf, bytes_read);
|
||||
ser2netSerial->flush(); // Waits for the transmission of outgoing serial data to
|
||||
}
|
||||
|
||||
while (ser2netClient.available()) { // flush overflow data if available
|
||||
ser2netClient.read();
|
||||
}
|
||||
delay(0);
|
||||
}
|
||||
}
|
||||
# if P020_USE_PROTOCOL
|
||||
|
||||
if (((P020_Protocol_e::UDP == _protocol) || (P020_Protocol_e::TCP_UDP == _protocol)) &&
|
||||
(nullptr != ser2netUdp)) {
|
||||
const int packetSize = ser2netUdp->parsePacket();
|
||||
|
||||
if (packetSize > 0) {
|
||||
std::vector<uint8_t> packetBuffer;
|
||||
packetBuffer.resize(packetSize + 1);
|
||||
|
||||
if (packetBuffer.size() >= static_cast<size_t>(packetSize)) {
|
||||
memset(&packetBuffer[0], 0, packetSize + 1);
|
||||
const int bytes_read = ser2netUdp->read(&packetBuffer[0], packetSize);
|
||||
|
||||
if (bytes_read > 0) {
|
||||
# ifndef LIMIT_BUILD_SIZE
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: Passing through %d bytes from %s to serial."), bytes_read, FsP(F("UDP"))));
|
||||
}
|
||||
# endif // ifndef LIMIT_BUILD_SIZE
|
||||
ser2netSerial->write(&packetBuffer[0], bytes_read);
|
||||
ser2netSerial->flush();
|
||||
|
||||
while (ser2netUdp->available()) {
|
||||
ser2netUdp->read();
|
||||
}
|
||||
}
|
||||
}
|
||||
delay(0);
|
||||
}
|
||||
}
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
}
|
||||
|
||||
void P020_Task::handleSerialIn(struct EventStruct *event) {
|
||||
@@ -251,7 +350,9 @@ void P020_Task::handleSerialIn(struct EventStruct *event) {
|
||||
if ((serial_processing == P020_Events::P1WiFiGateway) && !serial_buffer.endsWith(F("\r\n"))) {
|
||||
serial_buffer += F("\r\n");
|
||||
}
|
||||
ser2netClient.print(serial_buffer);
|
||||
|
||||
// Might include non-printable characters, so use write instead of print
|
||||
ser2netClient.write(reinterpret_cast<const uint8_t *>(serial_buffer.c_str()), serial_buffer.length());
|
||||
}
|
||||
|
||||
blinkLED();
|
||||
@@ -309,7 +410,14 @@ void P020_Task::rulesEngine(const String& message) {
|
||||
eventString += (_taskIndex + 1);
|
||||
}
|
||||
eventString += '#';
|
||||
eventString += message.substring(StartPos, NewLinePos);
|
||||
|
||||
if (_eventAsHex) {
|
||||
eventString += F("0x");
|
||||
eventString += formatToHex_array(reinterpret_cast<const uint8_t *>(message.substring(StartPos, NewLinePos).c_str()),
|
||||
NewLinePos - StartPos);
|
||||
} else {
|
||||
eventString += message.substring(StartPos, NewLinePos);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -368,7 +476,11 @@ void P020_Task::rulesEngine(const String& message) {
|
||||
}
|
||||
|
||||
bool P020_Task::isInit() const {
|
||||
return nullptr != ser2netServer && nullptr != ser2netSerial;
|
||||
return (nullptr != ser2netServer
|
||||
# if P020_USE_PROTOCOL
|
||||
|| nullptr != ser2netUdp
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
) && nullptr != ser2netSerial;
|
||||
}
|
||||
|
||||
void P020_Task::sendConnectedEvent(bool connected)
|
||||
|
||||
@@ -11,6 +11,14 @@
|
||||
# define PLUGIN_020_DEBUG false // when true: extra logging in serial out !?!?!
|
||||
# endif // ifndef PLUGIN_020_DEBUG
|
||||
|
||||
# ifndef P020_USE_PROTOCOL
|
||||
# ifdef ESP32
|
||||
# define P020_USE_PROTOCOL 1
|
||||
# else // ifdef ESP32
|
||||
# define P020_USE_PROTOCOL 0
|
||||
# endif // ifdef ESP32
|
||||
# endif // ifndef P020_USE_PROTOCOL
|
||||
|
||||
# define P020_SET_SERVER_PORT ExtraTaskSettings.TaskDevicePluginConfigLong[0]
|
||||
# define P020_SET_BAUDRATE ExtraTaskSettings.TaskDevicePluginConfigLong[1]
|
||||
|
||||
@@ -37,6 +45,8 @@
|
||||
# define P020_FLAG_P044_MODE_SAVED 8
|
||||
# define P020_FLAG_EVENT_SERIAL_ID 9
|
||||
# define P020_FLAG_APPEND_TASK_ID 10
|
||||
# define P020_FLAG_EVENT_AS_HEX 11
|
||||
# define P020_FLAG_PROTOCOL 12 // Uses 2 bits!
|
||||
# define P020_IGNORE_CLIENT_CONNECTED bitRead(P020_FLAGS, P020_FLAG_IGNORE_CLIENT)
|
||||
# define P020_HANDLE_MULTI_LINE bitRead(P020_FLAGS, P020_FLAG_MULTI_LINE)
|
||||
# define P020_GET_LED_ENABLED bitRead(P020_FLAGS, P020_FLAG_LED_ENABLED)
|
||||
@@ -45,6 +55,8 @@
|
||||
# define P020_GET_P044_MODE_SAVED bitRead(P020_FLAGS, P020_FLAG_P044_MODE_SAVED)
|
||||
# define P020_GET_EVENT_SERIAL_ID bitRead(P020_FLAGS, P020_FLAG_EVENT_SERIAL_ID)
|
||||
# define P020_GET_APPEND_TASK_ID bitRead(P020_FLAGS, P020_FLAG_APPEND_TASK_ID)
|
||||
# define P020_GET_EVENT_AS_HEX bitRead(P020_FLAGS, P020_FLAG_EVENT_AS_HEX)
|
||||
# define P020_GET_PROTOCOL get2BitFromUL(P020_FLAGS, P020_FLAG_PROTOCOL)
|
||||
|
||||
# define P020_DEFAULT_SERVER_PORT 1234
|
||||
# define P020_DEFAULT_BAUDRATE 115200
|
||||
@@ -69,6 +81,14 @@ enum class P020_Events : uint8_t {
|
||||
P1WiFiGateway = 3u,
|
||||
};
|
||||
|
||||
# if P020_USE_PROTOCOL
|
||||
enum class P020_Protocol_e : uint8_t {
|
||||
TCP = 0u,
|
||||
UDP = 1u,
|
||||
TCP_UDP = 2u,
|
||||
};
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
|
||||
struct P020_Task : public PluginTaskData_base {
|
||||
enum class ParserState : uint8_t {
|
||||
WAITING,
|
||||
@@ -133,9 +153,15 @@ struct P020_Task : public PluginTaskData_base {
|
||||
static bool validP1char(char ch);
|
||||
bool handleP1Char(char ch);
|
||||
|
||||
WiFiServer *ser2netServer = nullptr;
|
||||
uint16_t gatewayPort = 0;
|
||||
WiFiClient ser2netClient;
|
||||
WiFiServer *ser2netServer = nullptr;
|
||||
uint16_t gatewayPort = 0;
|
||||
WiFiClient ser2netClient;
|
||||
# if P020_USE_PROTOCOL
|
||||
WiFiUDP *ser2netUdp = nullptr;
|
||||
P020_Protocol_e _protocol = P020_Protocol_e::TCP;
|
||||
uint16_t _udpport;
|
||||
bool _udpInit = false;
|
||||
# endif // if P020_USE_PROTOCOL
|
||||
bool clientConnected = false;
|
||||
String serial_buffer;
|
||||
String net_buffer;
|
||||
@@ -152,11 +178,13 @@ struct P020_Task : public PluginTaskData_base {
|
||||
bool _CRCcheck = false;
|
||||
bool _P1EventData = false;
|
||||
size_t _maxDataGramSize = P020_DATAGRAM_MAX_SIZE;
|
||||
size_t _rxBufferSize = 0;
|
||||
ParserState _state = ParserState::WAITING;
|
||||
char _space = 0;
|
||||
char _newline = 0;
|
||||
bool _serialId = false;
|
||||
bool _appendTaskId = false;
|
||||
bool _eventAsHex = false;
|
||||
|
||||
ESPEasySerialPort _port;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user