diff --git a/docs/source/Plugin/P020.rst b/docs/source/Plugin/P020.rst index 1ec3a7609a..0373e404db 100644 --- a/docs/source/Plugin/P020.rst +++ b/docs/source/Plugin/P020.rst @@ -26,23 +26,111 @@ Supported hardware |P020_usedby| +Configuration +------------- + +.. image:: P020_DeviceConfiguration.png + +* **Name** In the Name field a unique name should be entered. + +* **Enabled** When unchecked the plugin is not enabled. + Sensor ^^^^^^ See: :ref:`SerialHelper_page` +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. + +* **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: + +.. image:: P020_EventProcessingOptions.png + +* *None*: No special processing, what is received is sent out to the network client, not generating an event. + +* *Generic*: No special processing, received data is sent to the network client, and an event ``!Serial#``, containing the message as is, is generated. Spaces and newlines are processed as configured below. + +* *RFLink*: Specifically designed for receiving serial data from RFLink devices, it follows this process: + + * Remove the regular RFLink ``20;xx;`` prefix + * Check for prefix ``ESPEASY;``, if found, remove the prefix and generate event ``RFLink#``. The ```` will contain commands to be handled by ESPEasy. + * If previous prefix is not found, generate event ``!RFLink#``, containing the entire received data. Spaces and newlines are processed as configured below. + + *Also see the* **Multiple lines processing** *option, below.* + +* *P1 WiFi Gateway*: Process the data, received from a P1 Energy meter, that does a checksum validation, as included in the message. No separate data values are available in ESPEasy, these are usually handled by Home automation systems that support the P1 protocol via TCP network communication. An event ``#Data`` is generated when a valid P1 packet is received. + + Replacing spaces or newlines should be **disabled** for the P1 protocol data to be handled properly as these replacements will disturb the checksum calculation, and also, the **Multiple lines processing** should be disabled if the data is to be handled as P1 protocol data, as that does contain newlines. + +.. spacer + +* **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! + +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 -**TODO**: Complete this documentation... +* **Use Serial Port as eventname**: Instead of the default ``!Serial#`` event, the name of the configured serial port will be used: (**Only** available for *Generic* Event processing) -.. Commands available -.. ^^^^^^^^^^^^^^^^^^ +.. spacer -.. .. include:: P020_commands.repl +* *(Unchecked)* -> ``Serial`` +* *HW Serial0* -> ``serial0`` +* *HW Serial0 swap* -> ``serial0`` +* *HW Serial1* -> ``serial1`` +* *HW Serial2* -> ``serial2`` +* *SW Serial* -> ``serialsw`` +* *I2C Serial* -> ``seriali2c`` +* *USB HWCDC* -> ``serialhwcdc`` +* *USB CDC* -> ``serialcdc`` -.. Events -.. ~~~~~~ +.. spacer -.. .. include:: P020_events.repl +* **Append Task Number to eventname**: Will append the task number to the event name, f.e. ``Serial8`` or ``RFLink12`` when task 8 or 12 is in use for this plugin. Can be combined with **Use Serial Port as eventname** if that is option is shown, resulting f.e. in ``serial0swap6`` etc. (Only available for *Generic* and *RFLink* Event processing) + +.. spacer + +* **Replace spaces in event by**: Here a single character can be selected to replace all spaces during receiving the data. +* **Replace newlines in event by**: Here a single character can be selected to replace all newlines during receiving the data. When enabled, all linefeeds are replaced, and all carriage returns (if any) are discarded. + +.. image:: P020_ReplaceCharInEventOptions.png + +The available set of replacement characters is ``, ; : . ! ^ | / \`` (comma, semicolon, colon, period, exclamation, caret, pipe, slash and backslash). When set to None, no replacement will be done. + +* **Process events without client**: By default, if no network client is connected, no serial data will be received and processed either. Enabling this option enables receiving data and generating events without a TCP client connected. + +* **Multiple lines processing**: When enabled, all received data will be split at a linefeed and sent out/event generated as separate messages. + +* **RX Receive timeout (mSec)**: If parts of serial data packets are somewhat delayed, but should still be handled as a single message, then the delay to wait for the next part can be configured here. 0 disables the delay. + +* **Reset target after init**: Select a GPIO pin that should be pulled low once during initialization of the plugin, used to synchronize the external serial data source with the plugin. + +* **RX Buffer size (bytes)**: To not overburden the memory use of the plugin, the buffer size is set rather low. Some serial devices, like energy meters may require a larger buffer if the message exceeds this size. Range: 256..1024. + +Led +^^^ + +* **Led enabled**: To enable a *data is being processed* activity led. + +* **Led pin**: The GPIO pin the Led is connected to. + +* **Led inverted**: Iverts the on/off state for the Led. + +Data Acquisition +^^^^^^^^^^^^^^^^ + +The Data Acquisition and Send to Controller settings are standard available configuration items. Send to Controller only when one or more Controllers are configured. *Single event with all values* option is not applicable for this plugin. + + +Commands +~~~~~~~~ + +.. include:: P020_commands.repl Change log ---------- @@ -50,6 +138,8 @@ Change log .. versionchanged:: 2.0 ... + |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| Major overhaul for 2.0 release. diff --git a/docs/source/Plugin/P020_DeviceConfiguration.png b/docs/source/Plugin/P020_DeviceConfiguration.png new file mode 100644 index 0000000000..086d5bc326 Binary files /dev/null and b/docs/source/Plugin/P020_DeviceConfiguration.png differ diff --git a/docs/source/Plugin/P020_EventOptions.png b/docs/source/Plugin/P020_EventOptions.png new file mode 100644 index 0000000000..5c5bb82a14 Binary files /dev/null and b/docs/source/Plugin/P020_EventOptions.png differ diff --git a/docs/source/Plugin/P020_EventProcessingOptions.png b/docs/source/Plugin/P020_EventProcessingOptions.png new file mode 100644 index 0000000000..a65fcfbbdb Binary files /dev/null and b/docs/source/Plugin/P020_EventProcessingOptions.png differ diff --git a/docs/source/Plugin/P020_ReplaceCharInEventOptions.png b/docs/source/Plugin/P020_ReplaceCharInEventOptions.png new file mode 100644 index 0000000000..8786ca8887 Binary files /dev/null and b/docs/source/Plugin/P020_ReplaceCharInEventOptions.png differ diff --git a/docs/source/Plugin/P020_commands.repl b/docs/source/Plugin/P020_commands.repl new file mode 100644 index 0000000000..9827b2a650 --- /dev/null +++ b/docs/source/Plugin/P020_commands.repl @@ -0,0 +1,37 @@ +.. csv-table:: + :header: "Command", "Extra information" + :widths: 20, 30 + + " + ``serialsend,`` + + ````: Text that will be sent (nearly) unprocessed. Only the regular variable replacements will be applied before sending the content to the serial port. + "," + 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. + " + " + ``ser2netclientsend,`` + + ````: Text that will be sent (nearly) unprocessed. Only the regular variable replacements will be applied before sending the content to the network client. + "," + This command will only send data to the network client, when there is an active connection. + + Using this command, either from rules, via http or mqtt, the text that is provided as content is completely sent to the network client. No extra data is added, other than any (system) variables that are included, being replaced. + " + " + ``serialsendmix,''[,...]`` + + ````: Text and/or hex byte(s) (having 0x prefix) that will be sent (nearly) unprocessed. Only the regular variable replacements will be applied before sending the content to the serial port. + "," + + This command requires quotes to be used if spaces or commas are part of the content. + + Any data can be sent, even if it can not be typed in a text content, by specifying that as a separate argument: ``serialsendmix,'text, optionally including spaces or commas',0xXX,'0xXXxx XX,xx-XX:xx'`` + + ``'text, optionally including spaces or commas'``: Any text content to be sent to the serial port. Can contain variables. Quotes are only required if spaces or commas (separators) are used. + + ``0xXX``: A single character in hexadecimal notation (range: 0x00..0xFF), that is appended to the data to send. + + ``'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. + " diff --git a/docs/source/Plugin/P037.rst b/docs/source/Plugin/P037.rst index 184813e953..5e3fa5a3cc 100644 --- a/docs/source/Plugin/P037.rst +++ b/docs/source/Plugin/P037.rst @@ -190,11 +190,17 @@ Option: Limit events being generated * **Max. # events in event queue**: As a protection against event-overflow this configures a check for the queue-length, so if more than the selected number of events is still in the queue, new events will be discarded until some events are processed, and the remaining is less than this count. When set to 0 this check is disabled. -Option: Modify separater character in events +Option: Modify separator character in events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * **To replace by comma in event**: Select a character that is to be replaced by a comma, before it is put into the event-queue. There is a limited set of characters that can be replaced, to avoid ending up with malformed events. +Available options: + +.. image:: P037_ReplaceByCommaOptions.png + +The available set of replacement characters is ``! @ $ % ^ & * ; : . | / \`` (exclamation, at, dollar, percent, caret, ampersand, semicolon, colon, period, pipe, slash and backslash). When set to None, no replacement will be done. + This can be used to 'transform' the content of a JSON message so the used separator is a comma, for easier use in rules. Topic Subscriptions diff --git a/docs/source/Plugin/P037_ReplaceByCommaOptions.png b/docs/source/Plugin/P037_ReplaceByCommaOptions.png new file mode 100644 index 0000000000..bba17486e6 Binary files /dev/null and b/docs/source/Plugin/P037_ReplaceByCommaOptions.png differ diff --git a/docs/source/Plugin/P044.rst b/docs/source/Plugin/P044.rst index f50fd2bd7e..3065fe4455 100644 --- a/docs/source/Plugin/P044.rst +++ b/docs/source/Plugin/P044.rst @@ -26,23 +26,46 @@ Supported hardware |P044_usedby| +.. note:: This plugin is now merged (back) into :ref:`P020_page` where it initially was forked off from, with using some predefined settings. + +Configuration +------------- + +.. image:: P044_DeviceConfiguration.png + +* **Name** In the Name field a unique name should be entered. + +* **Enabled** When unchecked the plugin is not enabled. + Sensor ^^^^^^ See: :ref:`SerialHelper_page` +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. -**TODO**: Complete this documentation... +* **Baud Rate / Serial config**: See *Serial helper configuration*, above. -.. Commands available -.. ^^^^^^^^^^^^^^^^^^ +* **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! -.. .. include:: P044_commands.repl +* **Process events without client**: By default, if no network client is connected, no serial data will be received and processed either. Enabling this option enables receiving data and generating events without a TCP client connected. -.. Events -.. ~~~~~~ +* **RX Receive timeout (mSec)**: If parts of serial data packets are somewhat delayed, but should still be handled as a single message, then the delay to wait for the next part can be configured here. 0 disables the delay. + +* **Reset target after init**: Select a GPIO pin that should be pulled low once during initialization of the plugin, used to synchronize the external serial data source with the plugin. + +Led +^^^ + +* **Led enabled**: To enable a *data is being processed* activity led. + +* **Led pin**: The GPIO pin the Led is connected to. + +* **Led inverted**: Iverts the on/off state for the Led. -.. .. include:: P044_events.repl Change log ---------- @@ -50,6 +73,8 @@ Change log .. versionchanged:: 2.0 ... + |changed| 2022-10-08: 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| Major overhaul for 2.0 release. diff --git a/docs/source/Plugin/P044_DeviceConfiguration.png b/docs/source/Plugin/P044_DeviceConfiguration.png new file mode 100644 index 0000000000..a8d83191c0 Binary files /dev/null and b/docs/source/Plugin/P044_DeviceConfiguration.png differ diff --git a/docs/source/Plugin/P044_ReplaceCharInEventOptions.png b/docs/source/Plugin/P044_ReplaceCharInEventOptions.png new file mode 100644 index 0000000000..df68e230c3 Binary files /dev/null and b/docs/source/Plugin/P044_ReplaceCharInEventOptions.png differ diff --git a/docs/source/Reference/Command.rst b/docs/source/Reference/Command.rst index b89da3bfde..76e9a772d1 100644 --- a/docs/source/Reference/Command.rst +++ b/docs/source/Reference/Command.rst @@ -249,10 +249,10 @@ P019 :ref:`P019_page` .. include:: ../Plugin/P019_commands.repl -.. P020 :ref:`P020_page` -.. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +P020 :ref:`P020_page` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. .. include:: ../Plugin/P020_commands.repl +.. include:: ../Plugin/P020_commands.repl P021 :ref:`P021_page` diff --git a/lib/ESPEasySerial/ESPEasySerialPort.cpp b/lib/ESPEasySerial/ESPEasySerialPort.cpp index 92aef1b30e..951c50de5e 100644 --- a/lib/ESPEasySerial/ESPEasySerialPort.cpp +++ b/lib/ESPEasySerial/ESPEasySerialPort.cpp @@ -1,31 +1,32 @@ #include "ESPEasySerialPort.h" -const __FlashStringHelper* ESPEasySerialPort_toString(ESPEasySerialPort port) +const __FlashStringHelper* ESPEasySerialPort_toString(ESPEasySerialPort port, bool shortName) { switch (port) { case ESPEasySerialPort::not_set: break; #if USES_I2C_SC16IS752 - case ESPEasySerialPort::sc16is752: return F("I2C Serial"); + case ESPEasySerialPort::sc16is752: return shortName ? F("seriali2c") : F("I2C Serial"); #endif // if USES_I2C_SC16IS752 #ifdef ESP8266 - case ESPEasySerialPort::serial0_swap: return F("HW Serial0 swap"); + case ESPEasySerialPort::serial0_swap: return shortName ? F("serial0swap") : F("HW Serial0 swap"); #endif // ifdef ESP8266 - case ESPEasySerialPort::serial0: return F("HW Serial0"); + case ESPEasySerialPort::serial0: return shortName ? F("serial0") : F("HW Serial0"); #if SOC_UART_NUM > 1 - case ESPEasySerialPort::serial1: return F("HW Serial1"); + case ESPEasySerialPort::serial1: return shortName ? F("serial1") : F("HW Serial1"); #endif // if SOC_UART_NUM > 1 #if SOC_UART_NUM > 2 - case ESPEasySerialPort::serial2: return F("HW Serial2"); + case ESPEasySerialPort::serial2: return shortName ? F("serial2") : F("HW Serial2"); #endif // if SOC_UART_NUM > 2 #if USES_SW_SERIAL - case ESPEasySerialPort::software: return F("SW Serial"); + case ESPEasySerialPort::software: return shortName ? F("serialsw") : F("SW Serial"); #endif // if USES_SW_SERIAL #if USES_HWCDC - case ESPEasySerialPort::usb_hw_cdc: return F("USB HWCDC"); + case ESPEasySerialPort::usb_hw_cdc: return shortName ? F("serialhwcdc") : F("USB HWCDC"); #endif // if USES_HWCDC #if USES_USBCDC - case ESPEasySerialPort::usb_cdc_0: return F("USB CDC"); -// case ESPEasySerialPort::usb_cdc_1: return F("USB CDC1"); + case ESPEasySerialPort::usb_cdc_0: return shortName ? F("serialcdc") : F("USB CDC"); + + // case ESPEasySerialPort::usb_cdc_1: return F("USB CDC1"); #endif // if USES_USBCDC case ESPEasySerialPort::MAX_SERIAL_TYPE: break; @@ -103,7 +104,7 @@ bool validSerialPort(ESPEasySerialPort port) #endif // if USES_HWCDC #if USES_USBCDC case ESPEasySerialPort::usb_cdc_0: -// case ESPEasySerialPort::usb_cdc_1: + // case ESPEasySerialPort::usb_cdc_1: #endif // if USES_USBCDC return true; diff --git a/lib/ESPEasySerial/ESPEasySerialPort.h b/lib/ESPEasySerial/ESPEasySerialPort.h index 1d66dd3a8d..9d6f767594 100644 --- a/lib/ESPEasySerial/ESPEasySerialPort.h +++ b/lib/ESPEasySerial/ESPEasySerialPort.h @@ -36,7 +36,7 @@ enum class ESPEasySerialPort : uint8_t { }; -const __FlashStringHelper* ESPEasySerialPort_toString(ESPEasySerialPort port); +const __FlashStringHelper* ESPEasySerialPort_toString(ESPEasySerialPort port, bool shortName = false); bool isHWserial(ESPEasySerialPort port); diff --git a/src/_P020_Ser2Net.ino b/src/_P020_Ser2Net.ino index 7c91ab184b..964a3dad30 100644 --- a/src/_P020_Ser2Net.ino +++ b/src/_P020_Ser2Net.ino @@ -1,12 +1,28 @@ #include "_Plugin_Helper.h" -#ifdef USES_P020 +#if defined(USES_P020) || defined(USES_P044) // ####################################################################################################### // #################################### Plugin 020: Ser2Net ############################################## +// #################################### Plugin 044: P1WifiGateway ######################################## // ####################################################################################################### /************ * Changelog: + * 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. + * 2023-08-12 tonhuisman: Strip off occasional 8th bit from received data, to avoid unexpected failures on P1 data reception + * 2023-06-24 tonhuisman: Fix initialization with non-GPIO serial ports like CDC/HW-CDC + * Add option: append the task number to the event-name (Generic and RFLink event options) + * 2023-06-23 tonhuisman: Add option: use Serial Port name (serialxxx -> xxx= 0/1/2/0swap/i2c/sw/hwcdc/cdc) as event-name for Generic events + * 2023-06-02 tonhuisman: Allow buffer up to 2kB. Use ESPEasySerial buffering feature + * 2023-03-25 tonhuisman: Change serialsendmix to handle 0x00 also, by implementing parseHexTextData() + * 2022-12-12 tonhuisman: Add character conversion for the received serial data, act on Space and/or Newline + * 2022-10-11 tonhuisman: Add option for including the message in P1 #data event + * 2022-10-09 tonhuisman: Check P044 migration on PLUGIN_INIT too, still needs a manual save (from UI or by save command) + * 2022-10-08 tonhuisman: Merged code from P044 into this plugin, and use a global flag to emulate P044 with P020 + * When USES_P044 is enabled, also USES_P020 will be enabled! + * Add Led settings, similar to P044 * 2022-05-28 tonhuisman: Add option to generate events for all lines of a multi-line message * 2022-05-26 tonhuisman: Add option to allow processing without webclient connected. * No older changelog available. @@ -22,36 +38,45 @@ # define PLUGIN_NAME_020 "Communication - Serial Server" # define PLUGIN_VALUENAME1_020 "Ser2Net" +# define PLUGIN_ID_020_044 44 +# define PLUGIN_NAME_020_044 "Communication - P1 Wifi Gateway" -# define P020_SET_SERVER_PORT ExtraTaskSettings.TaskDevicePluginConfigLong[0] -# define P020_SET_BAUDRATE ExtraTaskSettings.TaskDevicePluginConfigLong[1] +bool P020_Emulate_P044 = false; // Global flag +# if defined(USES_P044) && !defined(USES_P044_ORG) -# define P020_GET_SERVER_PORT Cache.getTaskDevicePluginConfigLong(event->TaskIndex, 0) -# define P020_GET_BAUDRATE Cache.getTaskDevicePluginConfigLong(event->TaskIndex, 1) +// Emulate P044 using P020 with a global flag +boolean Plugin_044(uint8_t function, struct EventStruct *event, String& string) { + P020_Emulate_P044 = true; -// #define P020_SET_BAUDRATE ExtraTaskSettings.TaskDevicePluginConfigLong[1] -# define P020_RX_WAIT PCONFIG(4) -# define P020_SERIAL_CONFIG PCONFIG(1) -# define P020_SERIAL_PROCESSING PCONFIG(5) -# define P020_RESET_TARGET_PIN PCONFIG(6) -# define P020_RX_BUFFER PCONFIG(7) + boolean result = Plugin_020(function, event, string); -# define P020_FLAGS PCONFIG_LONG(0) -# define P020_FLAG_IGNORE_CLIENT 0 -# define P020_FLAG_MULTI_LINE 1 -# 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_QUERY_VALUE 0 // Temp placement holder until we know what selectors are needed. -# define P020_NR_OUTPUT_OPTIONS 1 -# define P020_QUERY1_CONFIG_POS 3 + P020_Emulate_P044 = false; + return result; +} -# define P020_DEFAULT_SERVER_PORT 1234 -# define P020_DEFAULT_BAUDRATE 115200 -# define P020_DEFAULT_RESET_TARGET_PIN -1 -# define P020_DEFAULT_RX_BUFFER 256 +bool P020_ConvertP044Settings(struct EventStruct *event) { + if (P020_Emulate_P044 && !P020_GET_P044_MODE_SAVED) { + // Convert existing P044 settings to P020 settings + P020_RX_WAIT = PCONFIG(0); // No conflict + // P020_SERIAL_CONFIG = PCONFIG(1); // No need to convert + P020_RESET_TARGET_PIN = CONFIG_PIN1; + + // 'Conflicting' stuff, set defaults to: Serial0, RX=gpio-3 and TX=gpio-1 + CONFIG_PORT = static_cast(ESPEasySerialPort::serial0); // P044 Serial port + CONFIG_PIN1 = 3; // P044 RX pin + CONFIG_PIN2 = 1; // P044 TX pin + + // Former P044 defaults + P020_FLAGS = 0u; // Reset + bitSet(P020_FLAGS, P020_FLAG_LED_ENABLED); // Led enabled... + P020_LED_PIN = P020_STATUS_LED; // ...and connected to GPIO-12 + P020_SERIAL_PROCESSING = static_cast(P020_Events::P1WiFiGateway); // Enable P1 WiFi Gateway processing + return true; + } + return false; +} +# endif // if defined(USES_P044) && !defined(USES_P044_ORG) boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) { @@ -61,7 +86,13 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) { case PLUGIN_DEVICE_ADD: { - Device[++deviceCount].Number = PLUGIN_ID_020; + if (P020_Emulate_P044) { + Device[++deviceCount].Number = PLUGIN_ID_020_044; + Device[deviceCount].SendDataOption = false; + } else { + Device[++deviceCount].Number = PLUGIN_ID_020; + Device[deviceCount].SendDataOption = true; + } Device[deviceCount].Type = DEVICE_TYPE_SERIAL; Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_STRING; Device[deviceCount].Ports = 0; @@ -69,25 +100,42 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].InverseLogicOption = false; Device[deviceCount].FormulaOption = false; Device[deviceCount].ValueCount = 0; - Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = false; Device[deviceCount].GlobalSyncOption = false; break; } case PLUGIN_GET_DEVICENAME: { - string = F(PLUGIN_NAME_020); + string = P020_Emulate_P044 ? F(PLUGIN_NAME_020_044) : F(PLUGIN_NAME_020); break; } case PLUGIN_SET_DEFAULTS: { - P020_SET_BAUDRATE = P020_DEFAULT_BAUDRATE; - P020_SET_SERVER_PORT = P020_DEFAULT_SERVER_PORT; - P020_RESET_TARGET_PIN = P020_DEFAULT_RESET_TARGET_PIN; - P020_RX_BUFFER = P020_DEFAULT_RX_BUFFER; - success = true; + if (P020_Emulate_P044) { + CONFIG_PORT = static_cast(ESPEasySerialPort::serial0); // P044 Serial port + CONFIG_PIN1 = 3; // P044 RX pin + CONFIG_PIN2 = 1; // P044 TX pin + P020_SET_BAUDRATE = P020_DEFAULT_P044_BAUDRATE; + P020_SET_SERVER_PORT = P020_DEFAULT_P044_SERVER_PORT; + P020_RESET_TARGET_PIN = P020_DEFAULT_RESET_TARGET_PIN; + P020_SERIAL_PROCESSING = static_cast(P020_Events::P1WiFiGateway); // Enable P1 WiFi Gateway processing (only) + P020_LED_PIN = P020_STATUS_LED; + P020_RX_WAIT = 50; // 50 msec for proper P1 packet receive mode + P020_REPLACE_SPACE = 0; // Force empty + P020_REPLACE_NEWLINE = 0; + P020_FLAGS = 0u; // Reset + bitSet(P020_FLAGS, P020_FLAG_LED_ENABLED); + bitSet(P020_FLAGS, P020_FLAG_P044_MODE_SAVED); // Inital config, no conversion needed + } else { + P020_SET_BAUDRATE = P020_DEFAULT_BAUDRATE; + P020_SET_SERVER_PORT = P020_DEFAULT_SERVER_PORT; + P020_RESET_TARGET_PIN = P020_DEFAULT_RESET_TARGET_PIN; + P020_RX_BUFFER = P020_DEFAULT_RX_BUFFER; + P020_LED_PIN = -1; + } + success = true; break; } @@ -109,36 +157,111 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) { string = F("RST: "); string += formatGpioLabel(P020_RESET_TARGET_PIN, false); + string += event->String1; + string += F("LED: "); + string += formatGpioLabel(P020_GET_LED_ENABLED ? P020_LED_PIN : -1, false); + + if ((P020_GET_LED_INVERTED == 1) && (P020_GET_LED_ENABLED)) { + string += F(" (inv)"); + } success = true; break; } + # ifdef USES_P044 + case PLUGIN_WEBFORM_PRE_SERIAL_PARAMS: + { + // P044 Settings to convert? + if (P020_Emulate_P044 && P020_ConvertP044Settings(event)) { + addFormNote(F("Settings migrated from previous plugin version.")); + } + break; + } + # endif // ifdef USES_P044 + case PLUGIN_WEBFORM_LOAD: { - addFormNumericBox(F("TCP Port"), F("p020_port"), P020_GET_SERVER_PORT, 0); - addFormNumericBox(F("Baud Rate"), F("p020_baud"), P020_GET_BAUDRATE, 0); + addFormNumericBox(F("TCP Port"), F("pport"), P020_GET_SERVER_PORT, 0); + # ifndef LIMIT_BUILD_SIZE + addUnit(F("0..65535")); + # endif // ifndef LIMIT_BUILD_SIZE + + addFormNumericBox(F("Baud Rate"), F("pbaud"), P020_GET_BAUDRATE, 0); uint8_t serialConfChoice = serialHelper_convertOldSerialConfig(P020_SERIAL_CONFIG); serialHelper_serialconfig_webformLoad(event, serialConfChoice); { - const __FlashStringHelper *options[3] = { - F("None"), - F("Generic"), - F("RFLink") - }; - addFormSelector(F("Event processing"), F("p020_events"), 3, options, nullptr, P020_SERIAL_PROCESSING); - addFormCheckBox(F("Process events without client"), F("p020_ignoreclient"), P020_IGNORE_CLIENT_CONNECTED); + if (!P020_Emulate_P044) { + const __FlashStringHelper *options[] = { + F("None"), + F("Generic"), + F("RFLink"), + F("P1 WiFi Gateway") + }; + const int optionValues[] = { + static_cast(P020_Events::None), + static_cast(P020_Events::Generic), + static_cast(P020_Events::RFLink), + static_cast(P020_Events::P1WiFiGateway), + }; + constexpr int optionCount = NR_ELEMENTS(optionValues); + addFormSelector(F("Event processing"), F("pevents"), + optionCount, options, optionValues, + 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. Warning: can cause memory overflow issues!")); + # endif // ifndef LIMIT_BUILD_SIZE + + if (P020_Events::Generic == static_cast(P020_SERIAL_PROCESSING)) { + addFormCheckBox(F("Use Serial Port as eventname"), F("pevtname"), P020_GET_EVENT_SERIAL_ID); + # ifndef LIMIT_BUILD_SIZE + addFormNote(F("(Event processing: Generic only!)")); + # endif // ifndef LIMIT_BUILD_SIZE + } + + if (P020_Events::P1WiFiGateway != static_cast(P020_SERIAL_PROCESSING)) { + addFormCheckBox(F("Append Task Number to eventname"), F("papptask"), P020_GET_APPEND_TASK_ID); + # ifndef LIMIT_BUILD_SIZE + addFormNote(F("(Event processing: Generic and RFLink only!)")); + # endif // ifndef LIMIT_BUILD_SIZE + } + + if (!P020_Emulate_P044) { // Not appropriate for P1 WiFi Gateway + addFormSeparatorCharInput(F("Replace spaces in event by"), F("replspace"), + P020_REPLACE_SPACE, F(P020_REPLACE_CHAR_SET), F("")); + + addFormSeparatorCharInput(F("Replace newlines in event by"), F("replcrlf"), + P020_REPLACE_NEWLINE, F(P020_REPLACE_CHAR_SET), F("")); + } + + addFormCheckBox(F("Process events without client"), F("pignoreclient"), P020_IGNORE_CLIENT_CONNECTED); # ifndef LIMIT_BUILD_SIZE addFormNote(F("When enabled, will process serial data without a network client connected.")); # endif // ifndef LIMIT_BUILD_SIZE - addFormCheckBox(F("Multiple lines processing"), F("p020_multiline"), P020_HANDLE_MULTI_LINE); + + if (!P020_Emulate_P044) { // Not appropriate for P1 WiFi Gateway + addFormCheckBox(F("Multiple lines processing"), F("pmultiline"), P020_HANDLE_MULTI_LINE); + } + } + { + addFormNumericBox(F("RX Receive Timeout (mSec)"), F("prxwait"), P020_RX_WAIT, 0, 200); + addFormPinSelect(PinSelectPurpose::Generic_output, F("Reset target after init"), F("presetpin"), P020_RESET_TARGET_PIN); + + if (!P020_Emulate_P044) { + addFormNumericBox(F("RX buffer size (bytes)"), F("prx_buffer"), P020_RX_BUFFER, 256, 2048); + # ifndef LIMIT_BUILD_SIZE + addFormNote(F("Standard RX buffer 256B; higher values could be unstable; energy meters could require 1024B")); + # endif // ifndef LIMIT_BUILD_SIZE + } } - addFormNumericBox(F("RX Receive Timeout (mSec)"), F("p020_rxwait"), P020_RX_WAIT, 0, 20); - addFormPinSelect(PinSelectPurpose::Generic, F("Reset target after init"), F("p020_resetpin"), P020_RESET_TARGET_PIN); + { // Led settings + addFormSubHeader(F("Led")); - addFormNumericBox(F("RX buffer size (bytes)"), F("p020_rx_buffer"), P020_RX_BUFFER, 256, 1024); - # ifndef LIMIT_BUILD_SIZE - addFormNote(F("Standard RX buffer 256B; higher values could be unstable; energy meters could require 1024B")); - # endif // ifndef LIMIT_BUILD_SIZE + addFormCheckBox(F("Led enabled"), F("pled"), P020_GET_LED_ENABLED); + addFormPinSelect(PinSelectPurpose::Generic_output, F("Led pin"), F("pledpin"), P020_LED_PIN); + addFormCheckBox(F("Led inverted"), F("pledinv"), P020_GET_LED_INVERTED == 1); + } success = true; break; @@ -146,16 +269,43 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_WEBFORM_SAVE: { - P020_SET_SERVER_PORT = getFormItemInt(F("p020_port")); - P020_SET_BAUDRATE = getFormItemInt(F("p020_baud")); - P020_SERIAL_CONFIG = serialHelper_serialconfig_webformSave(); - P020_SERIAL_PROCESSING = getFormItemInt(F("p020_events")); - P020_RX_WAIT = getFormItemInt(F("p020_rxwait")); - P020_RESET_TARGET_PIN = getFormItemInt(F("p020_resetpin")); - P020_RX_BUFFER = getFormItemInt(F("p020_rx_buffer")); + P020_SET_SERVER_PORT = getFormItemInt(F("pport")); + P020_SET_BAUDRATE = getFormItemInt(F("pbaud")); + P020_SERIAL_CONFIG = serialHelper_serialconfig_webformSave(); + P020_RX_WAIT = getFormItemInt(F("prxwait")); + P020_RESET_TARGET_PIN = getFormItemInt(F("presetpin")); + + if (P020_Emulate_P044) { + P020_SERIAL_PROCESSING = static_cast(P020_Events::P1WiFiGateway); // Force P1 WiFi Gateway processing + } else { + P020_SERIAL_PROCESSING = getFormItemInt(F("pevents")); + P020_RX_BUFFER = getFormItemInt(F("prx_buffer")); + P020_REPLACE_SPACE = getFormItemInt(F("replspace")); + P020_REPLACE_NEWLINE = getFormItemInt(F("replcrlf")); + } + P020_LED_PIN = getFormItemInt(F("pledpin")); + + uint32_t lSettings = 0u; + bitWrite(lSettings, P020_FLAG_IGNORE_CLIENT, isFormItemChecked(F("pignoreclient"))); + 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"))); + + if (P020_Events::Generic == static_cast(P020_SERIAL_PROCESSING)) { + bitWrite(lSettings, P020_FLAG_EVENT_SERIAL_ID, isFormItemChecked(F("pevtname"))); + } + + if (P020_Events::P1WiFiGateway != static_cast(P020_SERIAL_PROCESSING)) { + bitWrite(lSettings, P020_FLAG_APPEND_TASK_ID, isFormItemChecked(F("papptask"))); + } + + if (P020_Emulate_P044) { + bitSet(lSettings, P020_FLAG_P044_MODE_SAVED); // Set to P044 configuration done on every save + } else { + bitWrite(lSettings, P020_FLAG_MULTI_LINE, isFormItemChecked(F("pmultiline"))); + } - bitWrite(P020_FLAGS, P020_FLAG_IGNORE_CLIENT, isFormItemChecked(F("p020_ignoreclient"))); - bitWrite(P020_FLAGS, P020_FLAG_MULTI_LINE, isFormItemChecked(F("p020_multiline"))); + P020_FLAGS = lSettings; success = true; break; @@ -163,6 +313,20 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { + # ifdef USES_P044 + + // P044 Settings to convert? + if (P020_Emulate_P044 && P020_ConvertP044Settings(event)) { + addLog(LOG_LEVEL_INFO, F("P1 : Automatic settings conversion, please save settings manually.")); + bitSet(P020_FLAGS, P020_FLAG_P044_MODE_SAVED); // Set to P044 configuration done on next save + } + # endif // ifdef USES_P044 + + if (P020_GET_LED_ENABLED && validGpio(P020_LED_PIN)) { + pinMode(P020_LED_PIN, OUTPUT); + digitalWrite(P020_LED_PIN, P020_GET_LED_INVERTED ? 1 : 0); + } + if ((P020_GET_SERVER_PORT == 0) || (P020_GET_BAUDRATE == 0)) { clearPluginTaskData(event->TaskIndex); break; @@ -175,22 +339,19 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) // It was already created and initialzed // So don't recreate to keep the webserver running. } else { - initPluginTaskData(event->TaskIndex, new (std::nothrow) P020_Task(event->TaskIndex)); + initPluginTaskData(event->TaskIndex, new (std::nothrow) P020_Task(event)); task = static_cast(getPluginTaskData(event->TaskIndex)); } if (nullptr == task) { break; } - task->handleMultiLine = P020_HANDLE_MULTI_LINE; + task->handleMultiLine = P020_HANDLE_MULTI_LINE && static_cast(P020_SERIAL_PROCESSING) != P020_Events::P1WiFiGateway; - // int rxPin =-1; - // int txPin =-1; int rxPin = CONFIG_PIN1; int txPin = CONFIG_PIN2; const ESPEasySerialPort port = static_cast(CONFIG_PORT); - // const ESPEasySerialPort port= ESPEasySerialPort::serial0; if ((rxPin < 0) && (txPin < 0)) { ESPeasySerialType::getSerialTypePins(port, rxPin, txPin); CONFIG_PIN1 = rxPin; @@ -200,21 +361,14 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) # ifndef LIMIT_BUILD_SIZE if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = F("Ser2net: TaskIndex="); - log += event->TaskIndex; - log += F(" port="); - log += CONFIG_PORT; - log += F(" rxPin="); - log += rxPin; - log += F(" txPin="); - log += txPin; - log += F(" BAUDRATE="); - log += P020_GET_BAUDRATE; - log += F(" SERVER_PORT="); - log += P020_GET_SERVER_PORT; - log += F(" SERIAL_PROCESSING="); - log += P020_SERIAL_PROCESSING; - addLogMove(LOG_LEVEL_INFO, log); + addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: TaskIndex=%d port=%d rxPin=%d txPin=%d BAUDRATE=%d SERVER_PORT=%d SERIAL_PROCESSING=%d"), + event->TaskIndex + 1, + CONFIG_PORT, + rxPin, + txPin, + P020_GET_BAUDRATE, + P020_GET_SERVER_PORT, + P020_SERIAL_PROCESSING)); } # endif // ifndef LIMIT_BUILD_SIZE @@ -245,8 +399,24 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) pinMode(P020_RESET_TARGET_PIN, INPUT_PULLUP); } - task->serial_processing = P020_SERIAL_PROCESSING; - success = true; + task->serial_processing = static_cast(P020_SERIAL_PROCESSING); + task->_P1EventData = P020_GET_P1_EVENT_DATA; + + task->blinkLED(); + + if (task->serial_processing == P020_Events::P1WiFiGateway) { + task->_CRCcheck = P020_GET_BAUDRATE == 115200; + # ifndef BUILD_NO_DEBUG + + if (task->_CRCcheck) { + addLog(LOG_LEVEL_DEBUG, F("P1 : DSMR version 5 meter, CRC on")); + } else { + addLog(LOG_LEVEL_DEBUG, F("P1 : DSMR version 4 meter, CRC off")); + } + # endif // ifndef BUILD_NO_DEBUG + } + + success = true; break; } @@ -266,11 +436,10 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) { P020_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr == task) { - break; + if (nullptr != task) { + task->checkServer(); + success = true; } - task->checkServer(); - success = true; break; } @@ -278,19 +447,18 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) { P020_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr == task) { - break; - } - - bool hasClient = task->hasClientConnected(); + if (nullptr != task) { + bool hasClient = task->hasClientConnected(); - if (P020_IGNORE_CLIENT_CONNECTED || hasClient) { - if (hasClient) { - task->handleClientIn(event); + if (P020_IGNORE_CLIENT_CONNECTED || hasClient) { + if (hasClient) { + task->handleClientIn(event); + } + task->handleSerialIn(event); // in case of second serial connected, PLUGIN_SERIAL_IN is not called anymore } - task->handleSerialIn(event); // in case of second serial connected, PLUGIN_SERIAL_IN is not called anymore + task->checkBlinkLED(); + success = true; } - success = true; break; } @@ -298,48 +466,43 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) { P020_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr == task) { - break; - } - - if (P020_IGNORE_CLIENT_CONNECTED || task->hasClientConnected()) { - task->handleSerialIn(event); - } else { - task->discardSerialIn(); + if (nullptr != task) { + if (P020_IGNORE_CLIENT_CONNECTED || task->hasClientConnected()) { + task->handleSerialIn(event); + } else { + task->discardSerialIn(); + } + success = true; } - success = true; break; } case PLUGIN_WRITE: { - String command = parseString(string, 1); P020_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr == task) { - break; - } + if (nullptr != task) { + String command = parseString(string, 1); - if (equals(command, F("serialsend"))) { - task->ser2netSerial->write(string.substring(11).c_str()); - task->ser2netSerial->flush(); - success = true; - } else if (equals(command, F("serialsendmix"))) { - std::vector argument = parseHexTextData(string); - if (argument.size() > 0) { + if (equals(command, F("serialsend"))) { + task->ser2netSerial->write(string.substring(11).c_str()); + task->ser2netSerial->flush(); + success = true; + } else if (equals(command, F("serialsendmix"))) { + std::vector argument = parseHexTextData(string); task->ser2netSerial->write(&argument[0], argument.size()); task->ser2netSerial->flush(); + success = true; + } else if ((equals(command, F("ser2netclientsend"))) && (task->hasClientConnected())) { + task->ser2netClient.print(string.substring(18)); + task->ser2netClient.flush(); + success = true; } - success = true; - } else if ((equals(command, F("ser2netclientsend"))) && (task->hasClientConnected())) { - task->ser2netClient.print(string.substring(18)); - task->ser2netClient.flush(); - success = true; + break; } - break; } } return success; } -#endif // USES_P020 +#endif // if defined(USES_P020) || defined(USES_P044) diff --git a/src/_P037_MQTTImport.ino b/src/_P037_MQTTImport.ino index 99e0d79c91..69ca168072 100644 --- a/src/_P037_MQTTImport.ino +++ b/src/_P037_MQTTImport.ino @@ -13,6 +13,7 @@ /** * 2023-06-17, tonhuisman: Replace Device[].FormulaOption by Device[].DecimalsOnly option, as no (successful) PLUGIN_READ is done * 2023-03-06, tonhuisman: Fix PLUGIN_INIT behavior to now always return success = true + * 2022-12-13, tonhuisman: Implement separator character input selector * 2022-11-14, tonhuisman: Add support for selecting JSON sub-attributes, using the . notation, like main.sub (1 level only) * 2022-11-02, tonhuisman: Enable plugin to generate events initially, like the plugin did before the mapping, filtering and json parsing * features were added @@ -196,11 +197,8 @@ boolean Plugin_037(uint8_t function, struct EventStruct *event, String& string) } # if P037_REPLACE_BY_COMMA_SUPPORT { - String character = F(" "); - character[0] = (P037_REPLACE_BY_COMMA == 0 ? 0x20 : static_cast(P037_REPLACE_BY_COMMA)); - addRowLabel(F("To replace by comma in event")); - addTextBox(F("preplch"), character, 1, false, false, F("[!@$%^ &*;:.|/\\]"), F("widenumber")); - addUnit(F("Single character only, limited to: ! @ $ % ^ & * ; : . | / \\ is replaced by: , ")); + addFormSeparatorCharInput(F("To replace by comma in event"), F("preplch"), + P037_REPLACE_BY_COMMA, F(P037_REPLACE_CHAR_SET), F("")); } # endif // if P037_REPLACE_BY_COMMA_SUPPORT @@ -253,13 +251,9 @@ boolean Plugin_037(uint8_t function, struct EventStruct *event, String& string) P037_SEND_EVENTS = isFormItemChecked(F("p037_send_events")) ? 1 : 0; P037_DEDUPLICATE_EVENTS = isFormItemChecked(F("pdedupe")) ? 1 : 0; P037_QUEUEDEPTH_EVENTS = getFormItemInt(F("pquedepth")); - # if P037_REPLACE_BY_COMMA_SUPPORT - String character = webArg(F("preplch")); - P037_REPLACE_BY_COMMA = character[0]; - if (P037_REPLACE_BY_COMMA == 0x20) { // Space -> 0 - P037_REPLACE_BY_COMMA = 0x0; - } + # if P037_REPLACE_BY_COMMA_SUPPORT + P037_REPLACE_BY_COMMA = getFormItemInt(F("preplch")); # endif // if P037_REPLACE_BY_COMMA_SUPPORT success = P037_data->webform_save( diff --git a/src/_P044_P1WifiGateway.ino b/src/_P044_P1WifiGateway.ino index a575350dfc..d4465b0e52 100644 --- a/src/_P044_P1WifiGateway.ino +++ b/src/_P044_P1WifiGateway.ino @@ -1,6 +1,7 @@ #include "_Plugin_Helper.h" -#ifdef USES_P044 -//#################################### Plugin 044: P1WifiGateway ######################################## +#ifdef USES_P044_ORG + +// #################################### Plugin 044: P1WifiGateway ######################################## // // based on P020 Ser2Net, extended by Ronald Leenes romix/-at-/macuser.nl // @@ -8,28 +9,22 @@ // Wemos D1 mini (see http://wemos.cc) and // P1 wifi gateway shield (see http://www.esp8266thingies.nl for print design and kits) // See also http://domoticx.com/p1-poort-slimme-meter-hardware/ -//####################################################################################################### - - -#include "src/Helpers/_Plugin_Helper_serial.h" -#include "src/PluginStructs/P044_data_struct.h" -#include - -#define PLUGIN_044 -#define PLUGIN_ID_044 44 -#define PLUGIN_NAME_044 "Communication - P1 Wifi Gateway" +// ####################################################################################################### +/** Changelog: + * 2022-10-08 tonhuisman: Disable plugin-code and merge all functionality into P020 as it was originally a modified copy of that plugin + * *** This code is deprecated *** + * 2022-10-01 tonhuisman: Add Led configuration options (Enabled, Pin, Inverted), changed device configuration + * 2022-10-01 tonhuisman: Format source using Uncrustify + */ -#define P044_SET_WIFI_SERVER_PORT ExtraTaskSettings.TaskDevicePluginConfigLong[0] -#define P044_SET_BAUDRATE ExtraTaskSettings.TaskDevicePluginConfigLong[1] -#define P044_GET_WIFI_SERVER_PORT Cache.getTaskDevicePluginConfigLong(event->TaskIndex, 0) -#define P044_GET_BAUDRATE Cache.getTaskDevicePluginConfigLong(event->TaskIndex, 1) +# include "src/Helpers/_Plugin_Helper_serial.h" +# include "src/PluginStructs/P044_data_struct.h" +# include - -#define P044_RX_WAIT PCONFIG(0) -#define P044_SERIAL_CONFIG PCONFIG(1) -#define P044_RESET_TARGET_PIN CONFIG_PIN1 - +# define PLUGIN_044 +# define PLUGIN_ID_044 44 +# define PLUGIN_NAME_044 "Communication - P1 Wifi Gateway" boolean Plugin_044(uint8_t function, struct EventStruct *event, String& string) @@ -38,165 +33,223 @@ boolean Plugin_044(uint8_t function, struct EventStruct *event, String& string) switch (function) { - case PLUGIN_DEVICE_ADD: - { - Device[++deviceCount].Number = PLUGIN_ID_044; - Device[deviceCount].Type = DEVICE_TYPE_SINGLE; - Device[deviceCount].Custom = true; - Device[deviceCount].TimerOption = false; - break; - } + { + Device[++deviceCount].Number = PLUGIN_ID_044; + Device[deviceCount].Type = DEVICE_TYPE_CUSTOM2; + Device[deviceCount].Custom = true; + Device[deviceCount].TimerOption = false; + break; + } case PLUGIN_GET_DEVICENAME: - { - string = F(PLUGIN_NAME_044); - break; + { + string = F(PLUGIN_NAME_044); + break; + } + + case PLUGIN_SET_DEFAULTS: + { + P044_LED_PIN = P044_STATUS_LED; // Former default + break; + } + + case PLUGIN_WEBFORM_SHOW_GPIO_DESCR: + { + string = concat(F("RST: "), formatGpioLabel(P044_RESET_TARGET_PIN, false)); + string += event->String1; + string += concat(F("LED: "), formatGpioLabel((P044_LED_ENABLED & 0x7f) == 0 ? P044_LED_PIN : -1, false)); + + if ((P044_LED_INVERTED == 1) && ((P044_LED_ENABLED & 0x7f) == 0)) { + string += F(" (inv)"); } + success = true; + break; + } case PLUGIN_WEBFORM_LOAD: - { - addFormNumericBox(F("TCP Port"), F("p044_port"), P044_GET_WIFI_SERVER_PORT, 0); - addFormNumericBox(F("Baud Rate"), F("p044_baud"), P044_GET_BAUDRATE, 0); + { + # ifdef USES_P020 + String msg; + msg.reserve(132); + msg += F("This plugin is deprecated and will be removed in a future release. Please use P020 - "); + msg += getPluginNameFromPluginID(20); + addFormNote(msg); + # endif // ifdef USES_P020 + LoadTaskSettings(event->TaskIndex); + + { // Serial settings + addFormSubHeader(F("Serial")); uint8_t serialConfChoice = serialHelper_convertOldSerialConfig(P044_SERIAL_CONFIG); serialHelper_serialconfig_webformLoad(event, serialConfChoice); + addFormNumericBox(F("Baud Rate"), F("pbaud"), P044_GET_BAUDRATE, 0, 115200); + } + + { // Device settings + addFormSubHeader(F("Device")); + + addFormNumericBox(F("TCP Port"), F("pport"), P044_GET_WIFI_SERVER_PORT, 0, 65535); + # ifndef LIMIT_BUILD_SIZE + addUnit(F("0..65535")); + # endif // ifndef LIMIT_BUILD_SIZE + // FIXME TD-er: Why isn't this using the normal pin selection functions? - addFormPinSelect(PinSelectPurpose::Generic, F("Reset target after boot"), F("taskdevicepin1"), P044_RESET_TARGET_PIN); + addFormPinSelect(PinSelectPurpose::Generic, F("Reset target after boot"), F("taskdevicepin1"), P044_RESET_TARGET_PIN); - addFormNumericBox(F("RX Receive Timeout (mSec)"), F("p044_rxwait"), P044_RX_WAIT, 0); + addFormNumericBox(F("RX Receive Timeout (mSec)"), F("prxwait"), P044_RX_WAIT, 0); + } - success = true; - break; + { // Led settings + addFormSubHeader(F("Led")); + + addFormCheckBox(F("Led enabled"), F("pled"), (P044_LED_ENABLED & 0x7f) == 0); + addFormPinSelect(PinSelectPurpose::Generic, F("Led pin"), F("taskdevicepin2"), P044_LED_PIN); + addFormCheckBox(F("Led inverted"), F("pledinv"), P044_LED_INVERTED == 1); } + success = true; + break; + } + case PLUGIN_WEBFORM_SAVE: - { - P044_SET_WIFI_SERVER_PORT = getFormItemInt(F("p044_port")); - P044_SET_BAUDRATE = getFormItemInt(F("p044_baud")); - P044_RX_WAIT = getFormItemInt(F("p044_rxwait")); - P044_SERIAL_CONFIG = serialHelper_serialconfig_webformSave(); + { + P044_SET_WIFI_SERVER_PORT = getFormItemInt(F("pport")); + P044_SET_BAUDRATE = getFormItemInt(F("pbaud")); + P044_RX_WAIT = getFormItemInt(F("prxwait")); + P044_LED_ENABLED = 0x80 + (isFormItemChecked(F("pled")) ? 0 : 1); // Invert + set 8th bit to confirm new settings have been + // saved + P044_LED_INVERTED = isFormItemChecked(F("pledinv")) ? 1 : 0; + P044_SERIAL_CONFIG = serialHelper_serialconfig_webformSave(); + + success = true; + break; + } - success = true; - break; + case PLUGIN_INIT: + { + if (((P044_LED_ENABLED & 0x7f) == 1) && (P044_LED_PIN != -1)) { + pinMode(P044_LED_PIN, OUTPUT); + digitalWrite(P044_LED_PIN, P044_LED_INVERTED == 1 ? 1 : 0); } - case PLUGIN_INIT: - { - pinMode(P044_STATUS_LED, OUTPUT); - digitalWrite(P044_STATUS_LED, 0); + LoadTaskSettings(event->TaskIndex); - if ((P044_GET_WIFI_SERVER_PORT == 0) || (P044_GET_BAUDRATE == 0)) { - clearPluginTaskData(event->TaskIndex); - break; - } + if ((P044_GET_WIFI_SERVER_PORT == 0) || (P044_GET_BAUDRATE == 0)) { + clearPluginTaskData(event->TaskIndex); + break; + } - // try to reuse to keep webserver running - P044_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr != task && task->isInit()) { - // It was already created and initialzed - // So don't recreate to keep the webserver running. - } else { - initPluginTaskData(event->TaskIndex, new (std::nothrow) P044_Task()); - task = static_cast(getPluginTaskData(event->TaskIndex)); - } - if (nullptr == task) { - break; - } + // try to reuse to keep webserver running + P044_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); - int rxPin; - int txPin; - // FIXME TD-er: Must use proper pin settings and standard ESPEasySerial wrapper - ESPeasySerialType::getSerialTypePins(ESPEasySerialPort::serial0, rxPin, txPin); - uint8_t serialconfig = serialHelper_convertOldSerialConfig(P044_SERIAL_CONFIG); - task->serialBegin(ESPEasySerialPort::not_set, rxPin, txPin, P044_GET_BAUDRATE, serialconfig); - task->startServer(P044_GET_WIFI_SERVER_PORT); - - if (!task->isInit()) { - clearPluginTaskData(event->TaskIndex); - break; - } + if ((nullptr != task) && task->isInit()) { + // It was already created and initialzed + // So don't recreate to keep the webserver running. + } else { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P044_Task(event)); + task = static_cast(getPluginTaskData(event->TaskIndex)); + } - if (validGpio(P044_RESET_TARGET_PIN)) { - pinMode(P044_RESET_TARGET_PIN, OUTPUT); - digitalWrite(P044_RESET_TARGET_PIN, LOW); - delay(500); - digitalWrite(P044_RESET_TARGET_PIN, HIGH); - pinMode(P044_RESET_TARGET_PIN, INPUT_PULLUP); - } + if (nullptr == task) { + break; + } - task->blinkLED(); - if (P044_GET_BAUDRATE == 115200) { - #ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, F("P1 : DSMR version 4 meter, CRC on")); - #endif - task->CRCcheck = true; - } else { - #ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, F("P1 : DSMR version 4 meter, CRC off")); - #endif - task->CRCcheck = false; - } + int rxPin; + int txPin; - success = true; + // FIXME TD-er: Must use proper pin settings and standard ESPEasySerial wrapper + ESPeasySerialType::getSerialTypePins(ESPEasySerialPort::serial0, rxPin, txPin); + uint8_t serialconfig = serialHelper_convertOldSerialConfig(P044_SERIAL_CONFIG); + task->serialBegin(ESPEasySerialPort::not_set, rxPin, txPin, P044_GET_BAUDRATE, serialconfig); + task->startServer(P044_GET_WIFI_SERVER_PORT); + + if (!task->isInit()) { + clearPluginTaskData(event->TaskIndex); break; } - case PLUGIN_EXIT: - { - P044_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); + if (validGpio(P044_RESET_TARGET_PIN)) { + pinMode(P044_RESET_TARGET_PIN, OUTPUT); + digitalWrite(P044_RESET_TARGET_PIN, LOW); + delay(500); + digitalWrite(P044_RESET_TARGET_PIN, HIGH); + pinMode(P044_RESET_TARGET_PIN, INPUT_PULLUP); + } - if (nullptr != task) { - task->stopServer(); - task->serialEnd(); - } + task->blinkLED(); + + if (P044_GET_BAUDRATE == 115200) { + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, F("P1 : DSMR version 5 meter, CRC on")); + # endif // ifndef BUILD_NO_DEBUG + task->CRCcheck = true; + } else { + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, F("P1 : DSMR version 4 meter, CRC off")); + # endif // ifndef BUILD_NO_DEBUG + task->CRCcheck = false; + } - success = true; - break; + success = true; + break; + } + + case PLUGIN_EXIT: + { + P044_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != task) { + task->stopServer(); + task->serialEnd(); } + success = true; + break; + } + case PLUGIN_ONCE_A_SECOND: - { - P044_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr == task) { - break; - } + { + P044_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != task) { task->checkServer(); success = true; - break; } + break; + } case PLUGIN_TEN_PER_SECOND: - { - P044_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr == task) { - break; - } + { + P044_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != task) { if (task->hasClientConnected()) { task->discardClientIn(); } task->checkBlinkLED(); success = true; - break; } + break; + } case PLUGIN_SERIAL_IN: - { - P044_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr == task) { - break; - } + { + P044_Task *task = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != task) { if (task->hasClientConnected()) { task->handleSerialIn(event); } else { task->discardSerialIn(); } success = true; - break; } - + break; + } } return success; } -#endif // USES_P044 + +#endif // USES_P044_ORG diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 5f3d0ee94d..c39f61a93c 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -2340,6 +2340,9 @@ To create/register a plugin, you have to : /******************************************************************************\ * Libraries dependencies ***************************************************** \******************************************************************************/ +#if defined(USES_P044) && !defined(USES_P020) // P020 is used to replace/emulate P044 + #define USES_P020 +#endif #if defined(USES_P020) || defined(USES_P049) || defined(USES_P052) || defined(USES_P053) || defined(USES_P056) || defined(USES_P065) || defined(USES_P071) || defined(USES_P075) || defined(USES_P077) || defined(USES_P078) || defined(USES_P082) || defined(USES_P085) || defined(USES_P087) || defined(USES_P093)|| defined(USES_P094) || defined(USES_P102) || defined(USES_P105) || defined(USES_P108) || defined(USES_P144) || defined(USES_C018) // At least one plugin uses serial. #ifndef PLUGIN_USES_SERIAL diff --git a/src/src/DataTypes/ESPEasy_plugin_functions.h b/src/src/DataTypes/ESPEasy_plugin_functions.h index f59b51c96d..f94648b8f3 100644 --- a/src/src/DataTypes/ESPEasy_plugin_functions.h +++ b/src/src/DataTypes/ESPEasy_plugin_functions.h @@ -62,6 +62,7 @@ enum PluginFunctions_e { PLUGIN_PRIORITY_INIT_ALL , // Pre-initialize all plugins that are set to PowerManager priority (not implemented in plugins) PLUGIN_PRIORITY_INIT , // Pre-initialize a singe plugins that is set to PowerManager priority PLUGIN_WEBFORM_LOAD_ALWAYS , // Loaded *after* PLUGIN_WEBFORM_LOAD, also shown for remote data-feed devices + PLUGIN_WEBFORM_PRE_SERIAL_PARAMS , // Before serial parameters, convert additional parameters like baudrate or specific serial config PLUGIN_MAX_FUNCTION // Leave as last one. }; diff --git a/src/src/Globals/Plugins.cpp b/src/src/Globals/Plugins.cpp index fff0e97a42..7b93b9e1ab 100644 --- a/src/src/Globals/Plugins.cpp +++ b/src/src/Globals/Plugins.cpp @@ -843,6 +843,7 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) case PLUGIN_WEBFORM_SHOW_VALUES: case PLUGIN_WEBFORM_SHOW_CONFIG: case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: + case PLUGIN_WEBFORM_PRE_SERIAL_PARAMS: case PLUGIN_WEBFORM_SHOW_SERIAL_PARAMS: case PLUGIN_WEBFORM_SHOW_GPIO_DESCR: #if FEATURE_PLUGIN_STATS @@ -868,7 +869,8 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) Function == PLUGIN_WEBFORM_LOAD_ALWAYS || Function == PLUGIN_SET_DEFAULTS || Function == PLUGIN_INIT_VALUE_RANGES || - Function == PLUGIN_WEBFORM_SHOW_SERIAL_PARAMS + Function == PLUGIN_WEBFORM_SHOW_SERIAL_PARAMS || + Function == PLUGIN_WEBFORM_PRE_SERIAL_PARAMS ) { LoadTaskSettings(event->TaskIndex); } diff --git a/src/src/Helpers/_Plugin_Helper_serial.cpp b/src/src/Helpers/_Plugin_Helper_serial.cpp index 63ae8a2406..cd815129ab 100644 --- a/src/src/Helpers/_Plugin_Helper_serial.cpp +++ b/src/src/Helpers/_Plugin_Helper_serial.cpp @@ -17,8 +17,8 @@ #include -String serialHelper_getSerialTypeLabel(ESPEasySerialPort serType) { - return ESPEasySerialPort_toString(serType); +String serialHelper_getSerialTypeLabel(ESPEasySerialPort serType, bool shortName) { + return ESPEasySerialPort_toString(serType, shortName); } void serialHelper_log_GpioDescription(ESPEasySerialPort typeHint, int config_pin1, int config_pin2) { diff --git a/src/src/Helpers/_Plugin_Helper_serial.h b/src/src/Helpers/_Plugin_Helper_serial.h index 9bf0086593..1273c40856 100644 --- a/src/src/Helpers/_Plugin_Helper_serial.h +++ b/src/src/Helpers/_Plugin_Helper_serial.h @@ -11,7 +11,8 @@ struct ESPeasySerialType; -String serialHelper_getSerialTypeLabel(ESPEasySerialPort serType); +String serialHelper_getSerialTypeLabel(ESPEasySerialPort serType, + bool shortName = false); void serialHelper_log_GpioDescription(ESPEasySerialPort typeHint, int config_pin1, @@ -40,8 +41,8 @@ ESPEasySerialPort serialHelper_getSerialType(struct EventStruct *event); String serialHelper_getSerialTypeLabel(struct EventStruct *event); #ifndef DISABLE_SC16IS752_Serial -void serialHelper_addI2CuartSelectors(int address, - int channel); +void serialHelper_addI2CuartSelectors(int address, + int channel); #endif // ifndef DISABLE_SC16IS752_Serial void serialHelper_webformLoad(struct EventStruct *event); diff --git a/src/src/PluginStructs/P020_data_struct.cpp b/src/src/PluginStructs/P020_data_struct.cpp index f0734f5dbe..7edc227a9e 100644 --- a/src/src/PluginStructs/P020_data_struct.cpp +++ b/src/src/PluginStructs/P020_data_struct.cpp @@ -10,12 +10,19 @@ # include "../Helpers/ESPEasy_Storage.h" # include "../Helpers/Misc.h" -# define P020_RX_WAIT PCONFIG(4) -# define P020_RX_BUFFER PCONFIG(7) +P020_Task::P020_Task(struct EventStruct *event) : _taskIndex(event->TaskIndex) { + clearBuffer(); - -P020_Task::P020_Task(taskIndex_t taskIndex) : _taskIndex(taskIndex) { - serial_buffer.reserve(P020_DATAGRAM_MAX_SIZE); + if (P020_GET_LED_ENABLED) { + _ledPin = P020_LED_PIN; // Default pin (12) is already initialized in P020_Task + } + _ledEnabled = P020_GET_LED_ENABLED == 1; + _ledInverted = P020_GET_LED_INVERTED == 1; + _space = static_cast(P020_REPLACE_SPACE); + _newline = static_cast(P020_REPLACE_NEWLINE); + _port = static_cast(CONFIG_PORT); + _serialId = P020_GET_EVENT_SERIAL_ID; + _appendTaskId = P020_GET_APPEND_TASK_ID; } P020_Task::~P020_Task() { @@ -23,6 +30,7 @@ P020_Task::~P020_Task() { delete ser2netServer; ser2netServer = nullptr; } + if (ser2netSerial != nullptr) { delete ser2netSerial; ser2netSerial = nullptr; @@ -50,10 +58,9 @@ void P020_Task::startServer(uint16_t portnumber) { ser2netServer->begin(); if (serverActive(ser2netServer)) { - addLog(LOG_LEVEL_INFO, String(F("Ser2Net : WiFi server started at port ")) + portnumber); + addLog(LOG_LEVEL_INFO, strformat(F("Ser2Net: WiFi server started at port %d"), portnumber)); } else { - addLog(LOG_LEVEL_ERROR, String(F("Ser2Net : WiFi server start failed at port ")) + - portnumber + String(F(", retrying..."))); + addLog(LOG_LEVEL_ERROR, strformat(F("Ser2Net: WiFi server start FAILED at port %d, retrying..."), portnumber)); } } } @@ -64,7 +71,7 @@ void P020_Task::checkServer() { ser2netServer->begin(); if (serverActive(ser2netServer)) { - addLog(LOG_LEVEL_INFO, F("Ser2net : WiFi server started")); + addLog(LOG_LEVEL_INFO, F("Ser2Net: WiFi server started")); } } } @@ -74,7 +81,7 @@ void P020_Task::stopServer() { if (ser2netClient) { ser2netClient.stop(); } clientConnected = false; ser2netServer->close(); - addLog(LOG_LEVEL_INFO, F("Ser2net : WiFi server closed")); + addLog(LOG_LEVEL_INFO, F("Ser2Net: WiFi server closed")); delete ser2netServer; ser2netServer = nullptr; } @@ -84,20 +91,24 @@ bool P020_Task::hasClientConnected() { if ((nullptr != ser2netServer) && ser2netServer->hasClient()) { if (ser2netClient) { ser2netClient.stop(); } + #if ESP_IDF_VERSION_MAJOR >= 5 + ser2netClient = ser2netServer->accept(); + #else ser2netClient = ser2netServer->available(); + #endif - #ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + # ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS // See: https://github.com/espressif/arduino-esp32/pull/6676 ser2netClient.setTimeout((CONTROLLER_CLIENTTIMEOUT_DFLT + 500) / 1000); // in seconds!!!! Client *pClient = &ser2netClient; pClient->setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT); - #else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS - ser2netClient.setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT); // in msec as it should be! - #endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + # else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + ser2netClient.setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT); // in msec as it should be! + # endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS sendConnectedEvent(true); - addLog(LOG_LEVEL_INFO, F("Ser2Net : Client connected!")); + addLog(LOG_LEVEL_INFO, F("Ser2Net: Client connected!")); } if (ser2netClient.connected()) @@ -110,7 +121,7 @@ bool P020_Task::hasClientConnected() { { clientConnected = false; sendConnectedEvent(false); - addLog(LOG_LEVEL_INFO, F("Ser2net : Client disconnected!")); + addLog(LOG_LEVEL_INFO, F("Ser2Net: Client disconnected!")); } } return clientConnected; @@ -125,14 +136,17 @@ void P020_Task::discardClientIn() { } void P020_Task::clearBuffer() { - serial_buffer = String(); - serial_buffer.reserve(P020_DATAGRAM_MAX_SIZE); + serial_buffer = String(); + _maxDataGramSize = serial_processing == P020_Events::P1WiFiGateway + ? P020_P1_DATAGRAM_MAX_SIZE + : P020_DATAGRAM_MAX_SIZE; + serial_buffer.reserve(_maxDataGramSize); } void P020_Task::serialBegin(const ESPEasySerialPort port, int16_t rxPin, int16_t txPin, unsigned long baud, uint8_t config) { serialEnd(); - if (rxPin >= 0) { + if (ESPEasySerialPort::not_set != port) { ser2netSerial = new (std::nothrow) ESPeasySerial(port, rxPin, txPin); if (nullptr != ser2netSerial) { @@ -142,7 +156,7 @@ void P020_Task::serialBegin(const ESPEasySerialPort port, int16_t rxPin, int16_t ser2netSerial->begin(baud, config); # endif // if defined(ESP8266) # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, F("Ser2net : Serial opened")); + addLog(LOG_LEVEL_DEBUG, F("Ser2Net: Serial opened")); # endif // ifndef BUILD_NO_DEBUG } } @@ -154,18 +168,18 @@ void P020_Task::serialEnd() { clearBuffer(); ser2netSerial = nullptr; # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, F("Ser2net : Serial closed")); + addLog(LOG_LEVEL_DEBUG, F("Ser2Net: Serial closed")); # endif // ifndef BUILD_NO_DEBUG } } void P020_Task::handleClientIn(struct EventStruct *event) { - int count = ser2netClient.available(); - int bytes_read = 0; - uint8_t net_buf[P020_DATAGRAM_MAX_SIZE]; + size_t count = ser2netClient.available(); + size_t bytes_read = 0; + uint8_t net_buf[_maxDataGramSize]; if (count > 0) { - if (count > P020_DATAGRAM_MAX_SIZE) { count = P020_DATAGRAM_MAX_SIZE; } + 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 @@ -178,21 +192,55 @@ void P020_Task::handleClientIn(struct EventStruct *event) { void P020_Task::handleSerialIn(struct EventStruct *event) { if (nullptr == ser2netSerial) { return; } - int RXWait = P020_RX_WAIT; - int timeOut = RXWait; + int RXWait = P020_RX_WAIT; + int timeOut = RXWait; + int maxExtend = 5; + bool done = false; + char ch; do { if (ser2netSerial->available()) { - if (serial_buffer.length() > static_cast(P020_RX_BUFFER)) { + if ((serial_processing != P020_Events::P1WiFiGateway) // P1 handling without this check + && (serial_buffer.length() > static_cast(P020_RX_BUFFER))) { # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, F("Ser2Net : Error: Buffer overflow, discarded input.")); + addLog(LOG_LEVEL_DEBUG, F("Ser2Net: Error: Buffer overflow, discarded input.")); # endif // ifndef BUILD_NO_DEBUG ser2netSerial->read(); } - else { serial_buffer += (char)ser2netSerial->read(); } + else { + if (_ledEnabled) { + digitalWrite(_ledPin, _ledInverted ? 0 : 1); + } + + ch = static_cast(ser2netSerial->read()); + + if (serial_processing == P020_Events::P1WiFiGateway) { + done = handleP1Char(ch); + } else { + addChar(ch); + } + + if (_ledEnabled) { + digitalWrite(_ledPin, _ledInverted ? 1 : 0); + } + } + + if (done) { + break; + } timeOut = RXWait; // if serial received, reset timeout counter } else { - if (timeOut <= 0) { break; } + if (timeOut <= 0) { + if ((RXWait > 0) && (serial_processing == P020_Events::P1WiFiGateway) && + ((_state == ParserState::READING) || + (_state == ParserState::CHECKSUM)) && + (maxExtend > 0)) { + timeOut = RXWait; + maxExtend--; + } else { + break; + } + } delay(1); --timeOut; } @@ -200,13 +248,19 @@ void P020_Task::handleSerialIn(struct EventStruct *event) { if (serial_buffer.length() > 0) { if (ser2netClient.connected()) { // Only send out if a client is connected + if ((serial_processing == P020_Events::P1WiFiGateway) && !serial_buffer.endsWith(F("\r\n"))) { + serial_buffer += F("\r\n"); + } ser2netClient.print(serial_buffer); } + + blinkLED(); + rulesEngine(serial_buffer); ser2netClient.flush(); clearBuffer(); # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, F("Ser2Net : data send!")); + addLog(LOG_LEVEL_DEBUG, F("Ser2Net: data send!")); # endif // ifndef BUILD_NO_DEBUG } // done } @@ -221,52 +275,78 @@ void P020_Task::discardSerialIn() { // We can also use the rules engine for local control! void P020_Task::rulesEngine(const String& message) { - if (!Settings.UseRules || message.isEmpty()) { return; } + if (!Settings.UseRules || message.isEmpty() || (P020_Events::None == serial_processing)) { return; } int NewLinePos = 0; uint16_t StartPos = 0; - NewLinePos = message.indexOf('\n', StartPos); + NewLinePos = handleMultiLine ? message.indexOf('\n', StartPos) : message.length(); do { if (NewLinePos < 0) { NewLinePos = message.length(); } + String eventString; + // Remove preceeding CR also if ((message[NewLinePos] == '\n') && (message[NewLinePos - 1] == '\r')) { NewLinePos--; } switch (serial_processing) { - case 0: { break; } - case 1: { // Generic + case P020_Events::None: { break; } + case P020_Events::Generic: { // Generic if (NewLinePos > StartPos) { - eventQueue.addMove(std::move(concat( - F("!Serial#"), - message.substring(StartPos, NewLinePos)))); + eventString = '!'; // F("!Serial"); + + if (_serialId) { + eventString += ESPEasySerialPort_toString(_port, true); + } else { + eventString += F("Serial"); + } + + if (_appendTaskId) { + eventString += (_taskIndex + 1); + } + eventString += '#'; + eventString += message.substring(StartPos, NewLinePos); } break; } - case 2: { // RFLink - StartPos += 6; // RFLink, strip 20;xx; from incoming message - - String eventString; + case P020_Events::RFLink: { // RFLink + StartPos += 6; // RFLink, strip 20;xx; from incoming message - if ((NewLinePos - StartPos) >= 8 && - message.substring(StartPos, StartPos + 8) + if (((NewLinePos - StartPos) >= 8) && + message.substring(StartPos, StartPos + 8) .startsWith(F("ESPEASY"))) { // Special treatment for gpio values, strip unneeded parts... StartPos += 8; // Strip "ESPEASY;" - eventString = F("RFLink#"); + eventString = F("RFLink"); } else { - eventString = F("!RFLink#"); // default event as it comes in, literal match needed in rules, using '!' + eventString = F("!RFLink"); // default event as it comes in, literal match needed in rules, using '!' } + if (_appendTaskId) { + eventString += (_taskIndex + 1); + } + eventString += '#'; + if (NewLinePos > StartPos) { eventString += message.substring(StartPos, NewLinePos); } eventQueue.addMove(std::move(eventString)); break; } + case P020_Events::P1WiFiGateway: // P1 WiFi Gateway + eventString = getTaskDeviceName(_taskIndex); + eventString += F("#Data"); + + if (_P1EventData) { + eventString += '='; + eventString += message; // Include entire message, may cause memory overflow! + eventString.replace(F("\n"), F(",")); // Make it a single line, comma-separated, as much as possible + eventString.replace(F("\r"), F("")); // We don't need no st*n carriage returns :) + } + break; } // switch // Skip CR/LF @@ -276,6 +356,9 @@ void P020_Task::rulesEngine(const String& message) { StartPos++; } + if (!eventString.isEmpty()) { + eventQueue.add(eventString); + } NewLinePos = message.indexOf('\n', StartPos); if (handleMultiLine && (NewLinePos < 0)) { @@ -293,4 +376,206 @@ void P020_Task::sendConnectedEvent(bool connected) eventQueue.add(_taskIndex, F("Client"), (connected ? 1 : 0)); } +void P020_Task::blinkLED() { + if (_ledEnabled) { + _blinkLEDStartTime = millis(); + digitalWrite(_ledPin, _ledInverted ? 0 : 1); + } +} + +void P020_Task::checkBlinkLED() { + if (_ledEnabled && (_blinkLEDStartTime > 0) && (timePassedSince(_blinkLEDStartTime) >= 500)) { + digitalWrite(_ledPin, _ledInverted ? 1 : 0); + _blinkLEDStartTime = 0; + } +} + +void P020_Task::addChar(char ch) { + if ((ch == 0x20) && (_space > 0)) { ch = _space; } + + if (_newline > 0) { + if (ch == '\n') { ch = _newline; } + + if (ch == '\r') { return; } // Ignore CR if LF is replaced + } + + serial_buffer += ch; +} + +/* checkDatagram + checks whether the P020_CHECKSUM of the data received from P1 matches the P020_CHECKSUM + attached to the telegram + */ +bool P020_Task::checkDatagram() const { + int endChar = serial_buffer.length() - 1; + + if (_CRCcheck) { + endChar -= P020_CHECKSUM_LENGTH; + } + + if ((endChar < 0) || (serial_buffer[0] != P020_DATAGRAM_START_CHAR) || + (serial_buffer[endChar] != P020_DATAGRAM_END_CHAR)) { + return false; + } + + if (!_CRCcheck) { + return true; + } + + const int checksumStartIndex = endChar + 1; + + # if PLUGIN_020_DEBUG + + for (unsigned int cnt = 0; cnt < serial_buffer.length(); ++cnt) { + serialPrint(serial_buffer.substring(cnt, 1)); + } + # endif // if PLUGIN_020_DEBUG + + // calculate the CRC and check if it equals the hexadecimal one attached to the datagram + unsigned int crc = CRC16(serial_buffer, checksumStartIndex); + return strtoul(serial_buffer.substring(checksumStartIndex).c_str(), nullptr, 16) == crc; +} + +/* + CRC16 + based on code written by Jan ten Hove + https://github.com/jantenhove/P1-Meter-ESP8266 + */ +unsigned int P020_Task::CRC16(const String& buf, int len) { + unsigned int crc = 0; + + for (int pos = 0; pos < len; pos++) { + crc ^= static_cast(buf[pos]); // XOR byte into least sig. byte of crc + + for (int i = 8; i != 0; i--) { // Loop over each bit + if ((crc & 0x0001) != 0) { // If the LSB is set + crc >>= 1; // Shift right and XOR 0xA001 + crc ^= 0xA001; + } else { // Else LSB is not set + crc >>= 1; // Just shift right + } + } + } + + return crc; +} + +/* + validP1char + Checks if the character is valid as part of the P1 datagram contents and/or checksum. + Returns false on a datagram start ('/'), end ('!') or invalid character + */ +bool P020_Task::validP1char(char ch) { + return + isAlphaNumeric(ch) || + ch == '.' || + ch == ' ' || + ch == '\\' || // Single backslash, but escaped in C++ + ch == '\r' || + ch == '\n' || + ch == '(' || + ch == ')' || + ch == '-' || + ch == '*' || + ch == ':' || + ch == '_'; +} + +bool P020_Task::handleP1Char(char ch) { + if (serial_buffer.length() >= _maxDataGramSize - 2) { // room for cr/lf + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, F("P1 : Error: Buffer overflow, discarded input.")); + # endif // ifndef BUILD_NO_DEBUG + _state = ParserState::WAITING; // reset + } + ch &= 0x7F; // Strip off occasional 8th bit for now + + bool done = false; + bool invalid = false; + + switch (_state) { + case ParserState::WAITING: + + if (ch == P020_DATAGRAM_START_CHAR) { + clearBuffer(); + addChar(ch); + _state = ParserState::READING; + } // else ignore data + break; + case ParserState::READING: + + if (validP1char(ch)) { + addChar(ch); + } else if (ch == P020_DATAGRAM_END_CHAR) { + addChar(ch); + + if (_CRCcheck) { + checkI = 0; + _state = ParserState::CHECKSUM; + } else { + done = true; + } + } else if (ch == P020_DATAGRAM_START_CHAR) { + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, F("P1 : Error: Start detected, discarded input.")); + # endif // ifndef BUILD_NO_DEBUG + _state = ParserState::WAITING; // reset + return handleP1Char(ch); + } else { + addLog(LOG_LEVEL_ERROR, strformat(F("P1 : Receiving unknown: %d,'%c'"), ch, ch)); + invalid = true; + } + break; + case ParserState::CHECKSUM: + + if (validP1char(ch)) { + addChar(ch); + ++checkI; + + if (checkI == P020_CHECKSUM_LENGTH) { + done = true; + } + } else { + invalid = true; + } + break; + } // switch + + if (invalid) { + // input is not a datagram char + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, F("P1 : Error: DATA corrupt, discarded input.")); + # endif // ifndef BUILD_NO_DEBUG + + # if PLUGIN_020_DEBUG + serialPrint(F("faulty char>")); + serialPrint(String(ch)); + serialPrintln("<"); + # endif // if PLUGIN_020_DEBUG + _state = ParserState::WAITING; // reset + } + + if (done) { + done = checkDatagram(); + + if (done) { + // add the cr/lf pair to the datagram ahead of reading both + // from serial as the datagram has already been validated + addChar('\r'); + addChar('\n'); + } else if (_CRCcheck) { + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, F("P1 : Error: Invalid CRC, dropped data")); + # endif // ifndef BUILD_NO_DEBUG + } else { + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, F("P1 : Error: Invalid datagram, dropped data")); + # endif // ifndef BUILD_NO_DEBUG + } + _state = ParserState::WAITING; // prepare for next one + } + + return done; +} + #endif // ifdef USES_P020 diff --git a/src/src/PluginStructs/P020_data_struct.h b/src/src/PluginStructs/P020_data_struct.h index 51300ba3c6..8714ca43cb 100644 --- a/src/src/PluginStructs/P020_data_struct.h +++ b/src/src/PluginStructs/P020_data_struct.h @@ -8,48 +8,130 @@ # include # ifndef PLUGIN_020_DEBUG - # define PLUGIN_020_DEBUG false // extra logging in serial out + # define PLUGIN_020_DEBUG false // when true: extra logging in serial out !?!?! # endif // ifndef PLUGIN_020_DEBUG +# define P020_SET_SERVER_PORT ExtraTaskSettings.TaskDevicePluginConfigLong[0] +# define P020_SET_BAUDRATE ExtraTaskSettings.TaskDevicePluginConfigLong[1] + +# define P020_GET_SERVER_PORT Cache.getTaskDevicePluginConfigLong(event->TaskIndex, 0) +# define P020_GET_BAUDRATE Cache.getTaskDevicePluginConfigLong(event->TaskIndex, 1) + +# define P020_REPLACE_CHAR_SET ",;:.!^|/\\" + +# define P020_LED_PIN PCONFIG(0) +# define P020_SERIAL_CONFIG PCONFIG(1) +# define P020_REPLACE_SPACE PCONFIG(2) +# define P020_REPLACE_NEWLINE PCONFIG(3) +# define P020_RX_WAIT PCONFIG(4) +# define P020_SERIAL_PROCESSING PCONFIG(5) +# define P020_RESET_TARGET_PIN PCONFIG(6) +# define P020_RX_BUFFER PCONFIG(7) + +# define P020_FLAGS PCONFIG_ULONG(0) +# define P020_FLAG_IGNORE_CLIENT 0 +# define P020_FLAG_MULTI_LINE 1 +# define P020_FLAG_LED_ENABLED 2 +# define P020_FLAG_LED_INVERTED 3 +# define P020_FLAG_P1_EVENT_DATA 4 +# define P020_FLAG_P044_MODE_SAVED 8 +# define P020_FLAG_EVENT_SERIAL_ID 9 +# define P020_FLAG_APPEND_TASK_ID 10 +# 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) +# define P020_GET_LED_INVERTED bitRead(P020_FLAGS, P020_FLAG_LED_INVERTED) +# define P020_GET_P1_EVENT_DATA bitRead(P020_FLAGS, P020_FLAG_P1_EVENT_DATA) +# 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_DEFAULT_SERVER_PORT 1234 +# define P020_DEFAULT_BAUDRATE 115200 +# define P020_DEFAULT_RESET_TARGET_PIN -1 +# define P020_DEFAULT_RX_BUFFER 256 + # define P020_STATUS_LED 12 # define P020_DATAGRAM_MAX_SIZE 256 + +# define P020_DEFAULT_P044_SERVER_PORT 0 +# define P020_DEFAULT_P044_BAUDRATE 9600 + +# define P020_CHECKSUM_LENGTH 4 +# define P020_DATAGRAM_START_CHAR '/' +# define P020_DATAGRAM_END_CHAR '!' +# define P020_P1_DATAGRAM_MAX_SIZE 2048u + +enum class P020_Events : uint8_t { + None = 0u, + Generic = 1u, + RFLink = 2u, + P1WiFiGateway = 3u, +}; + struct P020_Task : public PluginTaskData_base { - P020_Task(taskIndex_t taskIndex); - P020_Task() = delete; - virtual ~P020_Task(); + enum class ParserState : uint8_t { + WAITING, + READING, + CHECKSUM + }; + + P020_Task(struct EventStruct *event); + ~P020_Task(); inline static bool serverActive(WiFiServer *server); void startServer(uint16_t portnumber); - void checkServer(); - void stopServer(); bool hasClientConnected(); - void discardClientIn(); void clearBuffer(); - void serialBegin(const ESPEasySerialPort port, int16_t rxPin, int16_t txPin, unsigned long baud, uint8_t config); + void serialEnd(); + + void handleSerialIn(struct EventStruct *event); + void handleClientIn(struct EventStruct *event); + void discardSerialIn(); + void rulesEngine(const String& message); - void serialEnd(); + bool isInit() const; - void handleSerialIn(struct EventStruct *event); - void handleClientIn(struct EventStruct *event); - void rulesEngine(const String& message); + void sendConnectedEvent(bool connected); - void discardSerialIn(); + void blinkLED(); + void checkBlinkLED(); - bool isInit() const; + void addChar(char ch); - void sendConnectedEvent(bool connected); + /* checkDatagram + checks whether the P020_CHECKSUM of the data received from P1 matches the P020_CHECKSUM + attached to the telegram + */ + bool checkDatagram() const; + + /* + CRC16 + based on code written by Jan ten Hove + https://github.com/jantenhove/P1-Meter-ESP8266 + */ + static unsigned int CRC16(const String& buf, + int len); + + /* + validP1char + Checks if the character is valid as part of the P1 datagram contents and/or checksum. + Returns false on a datagram start ('/'), end ('!') or invalid character + */ + static bool validP1char(char ch); + bool handleP1Char(char ch); WiFiServer *ser2netServer = nullptr; uint16_t gatewayPort = 0; @@ -59,9 +141,24 @@ struct P020_Task : public PluginTaskData_base { String net_buffer; int checkI = 0; ESPeasySerial *ser2netSerial = nullptr; - uint8_t serial_processing = 0; + P020_Events serial_processing = P020_Events::None; taskIndex_t _taskIndex = INVALID_TASK_INDEX; bool handleMultiLine = false; + + unsigned long _blinkLEDStartTime = 0; + int8_t _ledPin = -1; + bool _ledInverted = false; + bool _ledEnabled = false; + bool _CRCcheck = false; + bool _P1EventData = false; + size_t _maxDataGramSize = P020_DATAGRAM_MAX_SIZE; + ParserState _state = ParserState::WAITING; + char _space = 0; + char _newline = 0; + bool _serialId = false; + bool _appendTaskId = false; + + ESPEasySerialPort _port; }; #endif // ifdef USES_P020 diff --git a/src/src/PluginStructs/P037_data_struct.h b/src/src/PluginStructs/P037_data_struct.h index 5d6b8bc3d5..7c7bd6b9c1 100644 --- a/src/src/PluginStructs/P037_data_struct.h +++ b/src/src/PluginStructs/P037_data_struct.h @@ -94,9 +94,11 @@ # define P037_OPERAND_LIST F("=%") # define P037_FILTER_COUNT 3 -# define P037_FILTER_LIST F("=-:") // Length should at least match P037_FILTER_COUNT +# define P037_FILTER_LIST F("=-:") // Length should at least match P037_FILTER_COUNT -# define P037_VALUE_SEPARATOR '\x02' // Separator outside of the normal ascii character values +# define P037_VALUE_SEPARATOR '\x02' // Separator outside of the normal ascii character values + +# define P037_REPLACE_CHAR_SET "!@$%^&*;:.|/\\" // Allowable set of characters to be replaced by a comma // Data structure struct P037_data_struct : public PluginTaskData_base diff --git a/src/src/PluginStructs/P044_data_struct.cpp b/src/src/PluginStructs/P044_data_struct.cpp index 48d7103af6..3f5ac105e8 100644 --- a/src/src/PluginStructs/P044_data_struct.cpp +++ b/src/src/PluginStructs/P044_data_struct.cpp @@ -1,17 +1,25 @@ #include "../PluginStructs/P044_data_struct.h" -#ifdef USES_P044 +#ifdef USES_P044_ORG -#include "../ESPEasyCore/Serial.h" -#include "../ESPEasyCore/ESPEasyNetwork.h" +# include "../ESPEasyCore/Serial.h" +# include "../ESPEasyCore/ESPEasyNetwork.h" -#include "../Globals/EventQueue.h" +# include "../Globals/EventQueue.h" -#include "../Helpers/ESPEasy_Storage.h" -#include "../Helpers/Misc.h" +# include "../Helpers/ESPEasy_Storage.h" +# include "../Helpers/Misc.h" -#define P044_RX_WAIT PCONFIG(0) +P044_Task::P044_Task(struct EventStruct *event) { + clearBuffer(); + + if (P044_LED_ENABLED & 0x80) { + _ledPin = P044_LED_PIN; // Default pin (12) is already initialized in P044_Task + } + _ledEnabled = (P044_LED_ENABLED & 0x7f) == 0; // Inverted setting and strip off new-settings bit + _ledInverted = P044_LED_INVERTED == 1; +} P044_Task::~P044_Task() { if (P1GatewayServer != nullptr) { @@ -25,11 +33,12 @@ P044_Task::~P044_Task() { } bool P044_Task::serverActive(WiFiServer *server) { -#if defined(ESP8266) + # if defined(ESP8266) return nullptr != server && server->status() != CLOSED; -#elif defined(ESP32) + # endif // if defined(ESP8266) + # if defined(ESP32) return nullptr != server && *server; -#endif // if defined(ESP8266) + # endif // if defined(ESP32) } void P044_Task::startServer(uint16_t portnumber) { @@ -45,7 +54,9 @@ void P044_Task::startServer(uint16_t portnumber) { P1GatewayServer->begin(); if (serverActive(P1GatewayServer)) { + # ifndef LIMIT_BUILD_SIZE addLog(LOG_LEVEL_INFO, concat(F("P1 : WiFi server started at port "), static_cast(portnumber))); + # endif // ifndef LIMIT_BUILD_SIZE } else { addLog(LOG_LEVEL_ERROR, concat(F("P1 : WiFi server start failed at port "), static_cast(portnumber)) + F(", retrying...")); } @@ -58,7 +69,9 @@ void P044_Task::checkServer() { P1GatewayServer->begin(); if (serverActive(P1GatewayServer)) { + # ifndef LIMIT_BUILD_SIZE addLog(LOG_LEVEL_INFO, F("P1 : WiFi server started")); + # endif // ifndef LIMIT_BUILD_SIZE } } } @@ -68,7 +81,9 @@ void P044_Task::stopServer() { if (P1GatewayClient) { P1GatewayClient.stop(); } clientConnected = false; P1GatewayServer->close(); + # ifndef LIMIT_BUILD_SIZE addLog(LOG_LEVEL_INFO, F("P1 : WiFi server closed")); + # endif // ifndef LIMIT_BUILD_SIZE delete P1GatewayServer; P1GatewayServer = nullptr; } @@ -80,17 +95,19 @@ bool P044_Task::hasClientConnected() { if (P1GatewayClient) { P1GatewayClient.stop(); } P1GatewayClient = P1GatewayServer->available(); - #ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + # ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS // See: https://github.com/espressif/arduino-esp32/pull/6676 P1GatewayClient.setTimeout((CONTROLLER_CLIENTTIMEOUT_DFLT + 500) / 1000); // in seconds!!!! Client *pClient = &P1GatewayClient; pClient->setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT); - #else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS - P1GatewayClient.setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT); // in msec as it should be! - #endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + # else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + P1GatewayClient.setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT); // in msec as it should be! + # endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + # ifndef LIMIT_BUILD_SIZE addLog(LOG_LEVEL_INFO, F("P1 : Client connected!")); + # endif // ifndef LIMIT_BUILD_SIZE } if (P1GatewayClient.connected()) @@ -102,7 +119,9 @@ bool P044_Task::hasClientConnected() { if (clientConnected) // there was a client connected before... { clientConnected = false; + # ifndef LIMIT_BUILD_SIZE addLog(LOG_LEVEL_INFO, F("P1 : Client disconnected!")); + # endif // ifndef LIMIT_BUILD_SIZE } } return clientConnected; @@ -117,13 +136,15 @@ void P044_Task::discardClientIn() { } void P044_Task::blinkLED() { - blinkLEDStartTime = millis(); - digitalWrite(P044_STATUS_LED, 1); + if (_ledEnabled) { + blinkLEDStartTime = millis(); + digitalWrite(_ledPin, _ledInverted ? 0 : 1); + } } void P044_Task::checkBlinkLED() { - if ((blinkLEDStartTime > 0) && (timePassedSince(blinkLEDStartTime) >= 500)) { - digitalWrite(P044_STATUS_LED, 0); + if (_ledEnabled && (blinkLEDStartTime > 0) && (timePassedSince(blinkLEDStartTime) >= 500)) { + digitalWrite(_ledPin, _ledInverted ? 1 : 0); blinkLEDStartTime = 0; } } @@ -159,11 +180,12 @@ bool P044_Task::checkDatagram() const { const int checksumStartIndex = endChar + 1; - #ifdef PLUGIN_044_DEBUG - for (unsigned int cnt = 0; cnt < serial_buffer.length(); ++cnt) { - serialPrint(serial_buffer.substring(cnt, 1)); - } - #endif + # ifdef PLUGIN_044_DEBUG + + for (unsigned int cnt = 0; cnt < serial_buffer.length(); ++cnt) { + serialPrint(serial_buffer.substring(cnt, 1)); + } + # endif // ifdef PLUGIN_044_DEBUG // calculate the CRC and check if it equals the hexadecimal one attached to the datagram unsigned int crc = CRC16(serial_buffer, checksumStartIndex); @@ -207,9 +229,9 @@ bool P044_Task::validP1char(char ch) { isAlphaNumeric(ch) || ch == '.' || ch == ' ' || - ch == '\\'|| // Single backslash, but escaped in C++ - ch == '\r'|| - ch == '\n'|| + ch == '\\' || // Single backslash, but escaped in C++ + ch == '\r' || + ch == '\n' || ch == '(' || ch == ')' || ch == '-' || @@ -226,14 +248,14 @@ void P044_Task::serialBegin(const ESPEasySerialPort port, int16_t rxPin, int16_t P1EasySerial = new (std::nothrow) ESPeasySerial(port, rxPin, txPin); if (nullptr != P1EasySerial) { -#if defined(ESP8266) + # if defined(ESP8266) P1EasySerial->begin(baud, (SerialConfig)config); -#elif defined(ESP32) + # elif defined(ESP32) P1EasySerial->begin(baud, config); -#endif // if defined(ESP8266) -# ifndef BUILD_NO_DEBUG + # endif // if defined(ESP8266) + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("P1 : Serial opened")); -#endif + # endif // ifndef BUILD_NO_DEBUG } } state = ParserState::WAITING; @@ -243,9 +265,9 @@ void P044_Task::serialEnd() { if (nullptr != P1EasySerial) { delete P1EasySerial; P1EasySerial = nullptr; -# ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("P1 : Serial closed")); -#endif + # endif // ifndef BUILD_NO_DEBUG } } @@ -257,9 +279,14 @@ void P044_Task::handleSerialIn(struct EventStruct *event) { do { if (P1EasySerial->available()) { - digitalWrite(P044_STATUS_LED, 1); + if (_ledEnabled) { + digitalWrite(_ledPin, _ledInverted ? 0 : 1); + } done = handleChar(P1EasySerial->read()); - digitalWrite(P044_STATUS_LED, 0); + + if (_ledEnabled) { + digitalWrite(_ledPin, _ledInverted ? 1 : 0); + } if (done) { break; } timeOut = RXWait; // if serial received, reset timeout counter @@ -273,9 +300,9 @@ void P044_Task::handleSerialIn(struct EventStruct *event) { if (done) { P1GatewayClient.print(serial_buffer); P1GatewayClient.flush(); -# ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("P1 : data send!")); -#endif + # endif // ifndef BUILD_NO_DEBUG blinkLED(); eventQueue.add(event->TaskIndex, F("Data"), EMPTY_STRING); @@ -284,10 +311,10 @@ void P044_Task::handleSerialIn(struct EventStruct *event) { bool P044_Task::handleChar(char ch) { if (serial_buffer.length() >= P044_DATAGRAM_MAX_SIZE - 2) { // room for cr/lf -# ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("P1 : Error: Buffer overflow, discarded input.")); -#endif - state = ParserState::WAITING; // reset + # endif // ifndef BUILD_NO_DEBUG + state = ParserState::WAITING; // reset } bool done = false; @@ -316,9 +343,9 @@ bool P044_Task::handleChar(char ch) { done = true; } } else if (ch == P044_DATAGRAM_START_CHAR) { -# ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("P1 : Error: Start detected, discarded input.")); -#endif + # endif // ifndef BUILD_NO_DEBUG state = ParserState::WAITING; // reset return handleChar(ch); } else { @@ -342,15 +369,15 @@ bool P044_Task::handleChar(char ch) { if (invalid) { // input is not a datagram char -# ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("P1 : Error: DATA corrupt, discarded input.")); -#endif + # endif // ifndef BUILD_NO_DEBUG - #ifdef PLUGIN_044_DEBUG - serialPrint(F("faulty char>")); - serialPrint(String(ch)); - serialPrintln("<"); - #endif + # ifdef PLUGIN_044_DEBUG + serialPrint(F("faulty char>")); + serialPrint(String(ch)); + serialPrintln("<"); + # endif // ifdef PLUGIN_044_DEBUG state = ParserState::WAITING; // reset } @@ -363,13 +390,13 @@ bool P044_Task::handleChar(char ch) { addChar('\r'); addChar('\n'); } else if (CRCcheck) { -# ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("P1 : Error: Invalid CRC, dropped data")); -#endif + # endif // ifndef BUILD_NO_DEBUG } else { -# ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("P1 : Error: Invalid datagram, dropped data")); -#endif + # endif // ifndef BUILD_NO_DEBUG } state = ParserState::WAITING; // prepare for next one } @@ -390,4 +417,4 @@ bool P044_Task::isInit() const { return nullptr != P1GatewayServer && nullptr != P1EasySerial; } -#endif +#endif // ifdef USES_P044 diff --git a/src/src/PluginStructs/P044_data_struct.h b/src/src/PluginStructs/P044_data_struct.h index 7b523ab72d..632137e931 100644 --- a/src/src/PluginStructs/P044_data_struct.h +++ b/src/src/PluginStructs/P044_data_struct.h @@ -3,17 +3,29 @@ #include "../../_Plugin_Helper.h" -#ifdef USES_P044 +#ifdef USES_P044_ORG -#include +# include // #define PLUGIN_044_DEBUG // extra logging in serial out -#define P044_STATUS_LED 12 -#define P044_CHECKSUM_LENGTH 4 -#define P044_DATAGRAM_START_CHAR '/' -#define P044_DATAGRAM_END_CHAR '!' -#define P044_DATAGRAM_MAX_SIZE 2048u +# define P044_SET_WIFI_SERVER_PORT ExtraTaskSettings.TaskDevicePluginConfigLong[0] +# define P044_SET_BAUDRATE ExtraTaskSettings.TaskDevicePluginConfigLong[1] +# define P044_GET_WIFI_SERVER_PORT Cache.getTaskDevicePluginConfigLong(event->TaskIndex, 0) +# define P044_GET_BAUDRATE Cache.getTaskDevicePluginConfigLong(event->TaskIndex, 1) +# define P044_RX_WAIT PCONFIG(0) +# define P044_SERIAL_CONFIG PCONFIG(1) +# define P044_RESET_TARGET_PIN CONFIG_PIN1 +# define P044_LED_PIN CONFIG_PIN2 +# define P044_LED_ENABLED PCONFIG(2) +# define P044_LED_INVERTED PCONFIG(3) + + +# define P044_STATUS_LED 12 +# define P044_CHECKSUM_LENGTH 4 +# define P044_DATAGRAM_START_CHAR '/' +# define P044_DATAGRAM_END_CHAR '!' +# define P044_DATAGRAM_MAX_SIZE 2048u struct P044_Task : public PluginTaskData_base { @@ -23,7 +35,7 @@ struct P044_Task : public PluginTaskData_base { CHECKSUM }; - P044_Task() = default; + P044_Task(struct EventStruct *event); virtual ~P044_Task(); @@ -70,10 +82,10 @@ struct P044_Task : public PluginTaskData_base { static bool validP1char(char ch); void serialBegin(const ESPEasySerialPort port, - int16_t rxPin, - int16_t txPin, - unsigned long baud, - uint8_t config); + int16_t rxPin, + int16_t txPin, + unsigned long baud, + uint8_t config); void serialEnd(); @@ -96,7 +108,11 @@ struct P044_Task : public PluginTaskData_base { ESPeasySerial *P1EasySerial = nullptr; unsigned long blinkLEDStartTime = 0; size_t maxMessageSize = P044_DATAGRAM_MAX_SIZE / 4; + + int8_t _ledPin = P044_STATUS_LED; // Former default + bool _ledEnabled = true; // Former default + bool _ledInverted = false; }; -#endif -#endif +#endif // ifdef USES_P044_ORG +#endif // ifndef PLUGINSTRUCTS_P044_DATA_STRUCT_H diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index a2fc5c848f..e1216e89e1 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -1148,9 +1148,12 @@ void devicePage_show_serial_config(taskIndex_t taskIndex) { struct EventStruct TempEvent(taskIndex); - serialHelper_webformLoad(&TempEvent); String webformLoadString; + PluginCall(PLUGIN_WEBFORM_PRE_SERIAL_PARAMS, &TempEvent, webformLoadString); + + serialHelper_webformLoad(&TempEvent); + PluginCall(PLUGIN_WEBFORM_SHOW_SERIAL_PARAMS, &TempEvent, webformLoadString); } #endif diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 6e37030090..1febed5e19 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -394,6 +394,32 @@ void addFormIPaccessControlSelect(const __FlashStringHelper * label, const __Fla addIPaccessControlSelect(id, choice); } +// ******************************************************************************** +// a Separator character selector +// ******************************************************************************** +void addFormSeparatorCharInput(const __FlashStringHelper *rowLabel, + const __FlashStringHelper *id, + int value, + const String & charset, + const __FlashStringHelper *additionalText) { + const int len = charset.length() + 1; + String charList[len]; + int charOpts[len]; + + charList[0] = F("None"); + charOpts[0] = 0; + + for (uint16_t i = 0; i < charset.length(); i++) { + charList[i + 1] = charset[i]; + charOpts[i + 1] = static_cast(charset[i]); + } + addFormSelector(rowLabel, id, len, charList, charOpts, value); + + if (!String(additionalText).isEmpty()) { + addUnit(additionalText); + } +} + // ******************************************************************************** // Add a selector form // ******************************************************************************** diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index 7544019e5e..9769c725ea 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -247,6 +247,15 @@ void addFormIPaccessControlSelect(const __FlashStringHelper * label, const __FlashStringHelper * id, int choice); +// ******************************************************************************** +// a Separator character selector +// ******************************************************************************** +void addFormSeparatorCharInput(const __FlashStringHelper *rowLabel, + const __FlashStringHelper *id, + int value, + const String & charset, + const __FlashStringHelper *additionalText); + // ******************************************************************************** // Add a selector form // ********************************************************************************