Merge pull request #5460 from tonhuisman/feature/P087-add-binary-read-fixed-length-hex-event

[P087] Add support for receiving binary data, fixed length data, and event as hex string
This commit is contained in:
TD-er
2026-01-05 22:07:52 +01:00
committed by GitHub
7 changed files with 151 additions and 49 deletions
+14
View File
@@ -90,6 +90,18 @@ Statistic data is only visible if the plugin is configured and enabled.
This shows the latest data received and some statistics.
Data options
^^^^^^^^^^^^
(These options are only available if build is without ``LIMIT_BUILD_SIZE`` set)
* **Receive binary data**: When enabled accepts any character for input. Does not terminate on a CR character received, but on end of data (no more bytes available), or when using the **Fixed length input data** setting.
* **Fixed length input data**: When 0 this feature is disabled. When set > 0 (max. length 255 characters), reading is stopped after this number of characters. Best used for reading binary data.
* **Event with hex. data (no prefix)**: When checked will generate the event with the received data converted to hexadecimal data (length is doubled), without a prefix (often hex data is prefixed with ``0x``). Data is still filtered (when enabled) and proxied *without* conversion!
.. include:: DataAcquisition.repl
* **Interval** By default, Interval will be set to 60 sec.
@@ -226,6 +238,8 @@ Change log
.. versionchanged:: 2.0
...
|added| 2025-12-23 Add support for binary data, fixed length input and convert event data to hex.
|added| 2024-02-25 Add support for ``serialproxy_test`` command and retrieving the separate groups from parsed regex.
|added| 2023-03-22 Add support for writing any binary data out via the serial port.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 51 KiB

@@ -93,7 +93,7 @@
.. |P087_type| replace:: :cyan:`Communication`
.. |P087_typename| replace:: :cyan:`Communication - SerialProxy`
.. |P087_porttype| replace:: `Serial`
.. |P087_status| replace:: :yellow:`COLLECTION C`
.. |P087_status| replace:: :yellow:`COLLECTION C` :yellow:`ENERGY`
.. |P087_github| replace:: P087_SerialProxy.ino
.. _P087_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P087_SerialProxy.ino
.. |P087_usedby| replace:: `.`
+37 -22
View File
@@ -11,6 +11,8 @@
/**
* Changelog:
* 2025-12-22 tonhuisman: Add support for receiving binary data, fixed length receive and sending events with hex data
* Moved most #define variables to P087_data_struct.h
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Serial Proxy)
* 2024-02-27 tonhuisman: Always process the regular expression like 'Global Match' to enable retrieving the available values
* 2024-02-26 tonhuisman: Apply log-string and other code optimizations
@@ -38,19 +40,6 @@
# define PLUGIN_NAME_087 "Communication - Serial Proxy"
# define P087_BAUDRATE PCONFIG_LONG(0)
# define P087_BAUDRATE_LABEL PCONFIG_LABEL(0)
# define P087_SERIAL_CONFIG PCONFIG_LONG(1)
# define P087_QUERY_VALUE 0 // Temp placement holder until we know what selectors are needed.
# define P087_NR_OUTPUT_OPTIONS 1
# define P087_NR_OUTPUT_VALUES 1
# define P087_QUERY1_CONFIG_POS 3
# define P087_DEFAULT_BAUDRATE 38400
// Plugin settings:
// Validate:
// - [0..9]
@@ -168,6 +157,14 @@ boolean Plugin_087(uint8_t function, struct EventStruct *event, String& string)
addFormSubHeader(F("Statistics"));
P087_html_show_stats(event);
# ifndef LIMIT_BUILD_SIZE
addFormSubHeader(F("Data options"));
addFormCheckBox(F("Receive binary data"), P087_READ_BIN_LABEL, P087_CONFIG_GET_READ_BIN);
addFormNumericBox(F("Fixed length input data"), P087_FIXED_LENGTH_LABEL, P087_CONFIG_GET_FIXED_LENGTH, 0, 255);
addUnit(F("0 = off, max. 255"));
addFormCheckBox(F("Event with hex. data (no prefix)"), P087_EVENT_HEX_LABEL, P087_CONFIG_GET_EVENT_HEX);
# endif // ifndef LIMIT_BUILD_SIZE
success = true;
break;
}
@@ -175,6 +172,11 @@ boolean Plugin_087(uint8_t function, struct EventStruct *event, String& string)
case PLUGIN_WEBFORM_SAVE: {
P087_BAUDRATE = getFormItemInt(P087_BAUDRATE_LABEL);
P087_SERIAL_CONFIG = serialHelper_serialconfig_webformSave();
# ifndef LIMIT_BUILD_SIZE
P087_CONFIG_SET_READ_BIN(isFormItemChecked(P087_READ_BIN_LABEL));
P087_CONFIG_SET_FIXED_LENGTH(getFormItemInt(P087_FIXED_LENGTH_LABEL));
P087_CONFIG_SET_EVENT_HEX(isFormItemChecked(P087_EVENT_HEX_LABEL));
# endif // ifndef LIMIT_BUILD_SIZE
P087_data_struct *P087_data =
static_cast<P087_data_struct *>(getPluginTaskData(event->TaskIndex));
@@ -207,6 +209,15 @@ boolean Plugin_087(uint8_t function, struct EventStruct *event, String& string)
if (P087_data->init(port, serial_rx, serial_tx, P087_BAUDRATE, static_cast<uint8_t>(P087_SERIAL_CONFIG))) {
LoadCustomTaskSettings(event->TaskIndex, P087_data->_lines, P87_Nlines, 0);
P087_data->post_init();
# ifndef LIMIT_BUILD_SIZE
P087_data->setHandleBinary(P087_CONFIG_GET_READ_BIN);
P087_data->setEventAsHex(P087_CONFIG_GET_EVENT_HEX);
if (P087_CONFIG_GET_FIXED_LENGTH > 0) {
P087_data->setMaxLength(P087_CONFIG_GET_FIXED_LENGTH);
}
P087_data->setFixedLength(P087_CONFIG_GET_FIXED_LENGTH);
# endif // ifndef LIMIT_BUILD_SIZE
success = true;
serialHelper_log_GpioDescription(port, serial_rx, serial_tx);
} else {
@@ -216,17 +227,15 @@ boolean Plugin_087(uint8_t function, struct EventStruct *event, String& string)
}
case PLUGIN_FIFTY_PER_SECOND: {
if (Settings.TaskDeviceEnabled[event->TaskIndex]) {
P087_data_struct *P087_data =
static_cast<P087_data_struct *>(getPluginTaskData(event->TaskIndex));
P087_data_struct *P087_data =
static_cast<P087_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P087_data) && P087_data->loop()) {
Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 10);
delay(0); // Processing a full sentence may take a while, run some
// background tasks.
}
success = true;
if ((nullptr != P087_data) && P087_data->loop()) {
Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 10);
delay(0); // Processing a full sentence may take a while, run some
// background tasks.
}
success = true;
break;
}
@@ -237,6 +246,12 @@ boolean Plugin_087(uint8_t function, struct EventStruct *event, String& string)
if ((nullptr != P087_data) && P087_data->getSentence(event->String2)) {
if (Plugin_087_match_all(event->TaskIndex, event->String2)) {
// sendData(event);
# ifndef LIMIT_BUILD_SIZE
if (P087_data->isEventAsHex() && !event->String2.isEmpty()) { // Convert to hex without prefix
event->String2 = formatToHex_array(reinterpret_cast<const uint8_t *>(&event->String2[0]), event->String2.length());
}
# endif // ifndef LIMIT_BUILD_SIZE
# ifndef BUILD_NO_DEBUG
addLog(LOG_LEVEL_DEBUG, event->String2);
# endif // ifndef BUILD_NO_DEBUG
+3
View File
@@ -1851,6 +1851,9 @@ To create/register a plugin, you have to :
#ifndef USES_P085
#define USES_P085 // AcuDC24x
#endif
#if !defined(USES_P087) && defined(ESP32)
#define USES_P087 // SerialProxy
#endif
#ifndef USES_P089
#define USES_P089 // Ping
#endif
+47 -26
View File
@@ -106,42 +106,53 @@ bool P087_data_struct::loop() {
while (available > 0 && !fullSentenceReceived) {
// Look for end marker
char c = easySerial->read();
uint8_t c = easySerial->read();
--available;
if (available == 0) {
available = easySerial->available();
delay(0);
}
const size_t length = sentence_part.length();
# ifdef LIMIT_BUILD_SIZE
const bool addCharacter = (10u != c);
const bool finished = (13u == c);
# else // ifdef LIMIT_BUILD_SIZE
const bool addCharacter = (10u != c) || handle_binary; // Skip LF in ascii-mode
bool finished = (13u == c) && !handle_binary; // Done on CR in ascii-mode
switch (c) {
case 13:
{
const size_t length = sentence_part.length();
bool valid = length > 0;
if (0 != fixed_length) {
finished = (length == fixed_length);
for (size_t i = 0; i < length && valid; ++i) {
if ((sentence_part[i] > 127) || (sentence_part[i] < 32)) {
sentence_part = EMPTY_STRING;
++sentences_received_error;
valid = false;
}
}
if (valid) {
fullSentenceReceived = true;
last_sentence = sentence_part;
sentence_part = EMPTY_STRING;
}
break;
if (finished) {
available = 0; // Forced exit
}
case 10:
}
# endif // ifdef LIMIT_BUILD_SIZE
// Ignore LF
break;
default:
sentence_part += c;
break;
if (finished) {
bool valid = length > 0;
for (size_t i = 0; i < length && valid
# ifndef LIMIT_BUILD_SIZE
&& !handle_binary // Skip valid-ascii check
# endif // ifndef LIMIT_BUILD_SIZE
; ++i) {
if ((sentence_part[i] > 127) || (sentence_part[i] < 32)) {
sentence_part = EMPTY_STRING;
++sentences_received_error;
valid = false;
}
}
if (valid) {
fullSentenceReceived = true;
last_sentence = sentence_part;
sentence_part = EMPTY_STRING;
}
}
else if (addCharacter) {
sentence_part += static_cast<char>(c);
}
if (max_length_reached()) { fullSentenceReceived = true; }
@@ -152,6 +163,16 @@ bool P087_data_struct::loop() {
++sentences_received;
length_last_received = last_sentence.length();
}
# ifndef LIMIT_BUILD_SIZE
else if (handle_binary && last_sentence.isEmpty() && !sentence_part.isEmpty()) { // Receive binary data (no end-marker)
fullSentenceReceived = true;
last_sentence = sentence_part;
sentence_part = EMPTY_STRING;
++sentences_received;
length_last_received = last_sentence.length();
}
# endif // ifndef LIMIT_BUILD_SIZE
return fullSentenceReceived;
}
+49
View File
@@ -21,6 +21,31 @@
# define P87_Nchars 128
# define P87_MAX_CAPTURE_INDEX 32
# define P087_BAUDRATE PCONFIG_LONG(0)
# define P087_BAUDRATE_LABEL PCONFIG_LABEL(0)
# define P087_SERIAL_CONFIG PCONFIG_LONG(1)
# define P087_QUERY_VALUE 0 // Temp placement holder until we know what selectors are needed.
# define P087_NR_OUTPUT_OPTIONS 1
# define P087_NR_OUTPUT_VALUES 1
# define P087_QUERY1_CONFIG_POS 3
# define P087_DEFAULT_BAUDRATE 38400
# define P087_READ_BIN_LABEL PCONFIG_LABEL(1)
# define P087_EVENT_HEX_LABEL PCONFIG_LABEL(2)
# define P087_FIXED_LENGTH_LABEL PCONFIG_LABEL(3)
# define P087_CONFIG_FLAGS PCONFIG_ULONG(2)
# define P087_FLAG_READ_BIN 0
# define P087_FLAG_EVENT_HEX 1
# define P087_FLAG_FIXED_LENGTH 2 // 8 bit
# define P087_CONFIG_SET_READ_BIN(S) (bitWrite(P087_CONFIG_FLAGS, P087_FLAG_READ_BIN, (S)))
# define P087_CONFIG_GET_READ_BIN (bitRead(P087_CONFIG_FLAGS, P087_FLAG_READ_BIN))
# define P087_CONFIG_SET_EVENT_HEX(S) (bitWrite(P087_CONFIG_FLAGS, P087_FLAG_EVENT_HEX, (S)))
# define P087_CONFIG_GET_EVENT_HEX (bitRead(P087_CONFIG_FLAGS, P087_FLAG_EVENT_HEX))
# define P087_CONFIG_SET_FIXED_LENGTH(S) (set8BitToUL(P087_CONFIG_FLAGS, P087_FLAG_FIXED_LENGTH, (S)))
# define P087_CONFIG_GET_FIXED_LENGTH (get8BitFromUL(P087_CONFIG_FLAGS, P087_FLAG_FIXED_LENGTH))
enum P087_Filter_Comp {
Equal = 0,
@@ -115,6 +140,25 @@ public:
bool plugin_get_config_value(struct EventStruct *event,
String & string);
# ifndef LIMIT_BUILD_SIZE
void setHandleBinary(bool state) {
handle_binary = state;
}
void setEventAsHex(bool state) {
event_hex = state;
}
bool isEventAsHex() const {
return event_hex;
}
void setFixedLength(uint8_t value) {
fixed_length = value;
}
# endif // ifndef LIMIT_BUILD_SIZE
private:
bool max_length_reached() const;
@@ -133,6 +177,11 @@ private:
bool capture_index_used[P87_MAX_CAPTURE_INDEX] = { 0 };
bool capture_index_must_not_match[P87_MAX_CAPTURE_INDEX] = { 0 };
bool regex_empty = false;
# ifndef LIMIT_BUILD_SIZE
bool handle_binary = false;
bool event_hex = false;
uint8_t fixed_length{};
# endif // ifndef LIMIT_BUILD_SIZE
};