mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-27 19:57:38 +00:00
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:
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user