Merge pull request #5287 from tonhuisman/feature/P020-data-handling-improvements

[P020] Improve data handling, add UDP receiver
This commit is contained in:
TD-er
2025-04-02 22:23:18 +02:00
committed by GitHub
13 changed files with 309 additions and 66 deletions
+17
View File
@@ -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

+7
View File
@@ -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.
"
+5 -1
View File
@@ -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
+16
View File
@@ -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;
+5
View File
@@ -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);
+3 -1
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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);
+17 -13
View File
@@ -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));
}
}
}
+144 -32
View File
@@ -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)
+31 -3
View File
@@ -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;
};