diff --git a/code/espurna/config/all.h b/code/espurna/config/all.h
index c1c08c54..843288c9 100644
--- a/code/espurna/config/all.h
+++ b/code/espurna/config/all.h
@@ -37,6 +37,6 @@
#include "general.h"
#include "defaults.h"
#include "deprecated.h"
-#include "dependencies.h"
#include "sensors.h"
+#include "dependencies.h"
#include "webui.h"
diff --git a/code/espurna/config/arduino.h b/code/espurna/config/arduino.h
index f5e92627..d0c9fdbb 100644
--- a/code/espurna/config/arduino.h
+++ b/code/espurna/config/arduino.h
@@ -191,39 +191,40 @@
// Features (values below are non-default values)
//--------------------------------------------------------------------------------
-//#define ALEXA_SUPPORT 0
-//#define API_SUPPORT 0
-//#define BUTTON_SUPPORT 0
-//#define DEBUG_SERIAL_SUPPORT 0
-//#define DEBUG_TELNET_SUPPORT 0
-//#define DEBUG_UDP_SUPPORT 1
-//#define DEBUG_WEB_SUPPORT 0
-//#define DOMOTICZ_SUPPORT 0
-//#define ENCODER_SUPPORT 1
-//#define HOMEASSISTANT_SUPPORT 0
-//#define I2C_SUPPORT 1
-//#define INFLUXDB_SUPPORT 1
-//#define IR_SUPPORT 1
-//#define LED_SUPPORT 0
-//#define LLMNR_SUPPORT 1
-//#define MDNS_SERVER_SUPPORT 0
-//#define MQTT_SUPPORT 0
-//#define NETBIOS_SUPPORT 1
-//#define NOFUSS_SUPPORT 1
-//#define NTP_SUPPORT 0
-//#define OTA_ARDUINOOTA_SUPPORT 1
-//#define RFM69_SUPPORT 1
-//#define RFB_SUPPORT 1
-//#define RPN_RULES_SUPPORT 0
-//#define SCHEDULER_SUPPORT 0
-//#define SPIFFS_SUPPORT 1
-//#define SSDP_SUPPORT 1
-//#define TELNET_SUPPORT 0
-//#define TERMINAL_SUPPORT 0
-//#define THINGSPEAK_SUPPORT 0
-//#define TUYA_SUPPORT 0
-//#define UART_MQTT_SUPPORT 1
-//#define WEB_SUPPORT 0
+//#define ALEXA_SUPPORT 0
+//#define API_SUPPORT 0
+//#define BUTTON_SUPPORT 0
+//#define DEBUG_SERIAL_SUPPORT 0
+//#define DEBUG_TELNET_SUPPORT 0
+//#define DEBUG_UDP_SUPPORT 1
+//#define DEBUG_WEB_SUPPORT 0
+//#define DOMOTICZ_SUPPORT 0
+//#define ENCODER_SUPPORT 1
+//#define HOMEASSISTANT_SUPPORT 0
+//#define I2C_SUPPORT 1
+//#define INFLUXDB_SUPPORT 1
+//#define IR_SUPPORT 1
+//#define LED_SUPPORT 0
+//#define LLMNR_SUPPORT 1
+//#define MDNS_SERVER_SUPPORT 0
+//#define MQTT_SUPPORT 0
+//#define NETBIOS_SUPPORT 1
+//#define NOFUSS_SUPPORT 1
+//#define NTP_SUPPORT 0
+//#define OTA_ARDUINOOTA_SUPPORT 1
+//#define RFM69_SUPPORT 1
+//#define RFB_SUPPORT 1
+//#define RPN_RULES_SUPPORT 0
+//#define SCHEDULER_SUPPORT 0
+//#define SPIFFS_SUPPORT 1
+//#define SSDP_SUPPORT 1
+//#define TELNET_SUPPORT 0
+//#define TERMINAL_SUPPORT 0
+//#define TERMINAL_SERIAL_SUPPORT 0
+//#define THINGSPEAK_SUPPORT 0
+//#define TUYA_SUPPORT 0
+//#define UART_MQTT_SUPPORT 1
+//#define WEB_SUPPORT 0
//--------------------------------------------------------------------------------
// Sensors (values below are non-default values)
diff --git a/code/espurna/config/defaults.h b/code/espurna/config/defaults.h
index 7555a41e..b24aaf20 100644
--- a/code/espurna/config/defaults.h
+++ b/code/espurna/config/defaults.h
@@ -1400,6 +1400,94 @@
#define TUYA_SW8_DPID 0
#endif
+// -----------------------------------------------------------------------------
+// UART
+// -----------------------------------------------------------------------------
+
+#ifndef UART1_BAUDRATE
+#define UART1_BAUDRATE 115200
+#endif
+
+#ifndef UART1_TX_PIN
+#define UART1_TX_PIN 1
+#endif
+
+#ifndef UART1_RX_PIN
+#define UART1_RX_PIN 3
+#endif
+
+#ifndef UART1_DATA_BITS
+#define UART1_DATA_BITS 8
+#endif
+
+#ifndef UART1_PARITY
+#define UART1_PARITY None
+#endif
+
+#ifndef UART1_STOP_BITS
+#define UART1_STOP_BITS 1
+#endif
+
+#ifndef UART1_INVERT
+#define UART1_INVERT 0
+#endif
+
+#ifndef UART2_BAUDRATE
+#define UART2_BAUDRATE 115200
+#endif
+
+#ifndef UART2_TX_PIN
+#define UART2_TX_PIN GPIO_NONE
+#endif
+
+#ifndef UART2_RX_PIN
+#define UART2_RX_PIN GPIO_NONE
+#endif
+
+#ifndef UART2_DATA_BITS
+#define UART2_DATA_BITS 8
+#endif
+
+#ifndef UART2_PARITY
+#define UART2_PARITY None
+#endif
+
+#ifndef UART2_STOP_BITS
+#define UART2_STOP_BITS 1
+#endif
+
+#ifndef UART2_INVERT
+#define UART2_INVERT 0
+#endif
+
+#ifndef UART3_BAUDRATE
+#define UART3_BAUDRATE 115200
+#endif
+
+#ifndef UART3_TX_PIN
+#define UART3_TX_PIN GPIO_NONE
+#endif
+
+#ifndef UART3_RX_PIN
+#define UART3_RX_PIN GPIO_NONE
+#endif
+
+#ifndef UART3_DATA_BITS
+#define UART3_DATA_BITS 8
+#endif
+
+#ifndef UART3_PARITY
+#define UART3_PARITY None
+#endif
+
+#ifndef UART3_STOP_BITS
+#define UART3_STOP_BITS 1
+#endif
+
+#ifndef UART3_INVERT
+#define UART3_INVERT 0
+#endif
+
// -----------------------------------------------------------------------------
// General
// -----------------------------------------------------------------------------
diff --git a/code/espurna/config/dependencies.h b/code/espurna/config/dependencies.h
index 09c46db8..0c85ede9 100644
--- a/code/espurna/config/dependencies.h
+++ b/code/espurna/config/dependencies.h
@@ -30,14 +30,34 @@
#if UART_MQTT_SUPPORT
#undef MQTT_SUPPORT
-#define MQTT_SUPPORT 1 // UART<->MQTT requires MQTT and no serial debug
+#define MQTT_SUPPORT 1 // UART<->MQTT requires MQTT and no serial debug & terminal
+#undef UART_SUPPORT
+#define UART_SUPPORT 1
#undef DEBUG_SERIAL_SUPPORT
-#define DEBUG_SERIAL_SUPPORT 0 // TODO: compare UART_MQTT_PORT with DEBUG_PORT? (as strings)
+#define DEBUG_SERIAL_SUPPORT 0
+#undef TERMINAL_SERIAL_SUPPORT
+#define TERMINAL_SERIAL_SUPPORT 0
+#endif
+
+#if RELAY_PROVIDER_STM_SUPPORT || RELAY_PROVIDER_DUAL_SUPPORT
+#undef UART_SUPPORT
+#define UART_SUPPORT 1
+#undef DEBUG_SERIAL_SUPPORT
+#define DEBUG_SERIAL_SUPPORT 0
+#undef TERMINAL_SERIAL_SUPPORT
+#define TERMINAL_SERIAL_SUPPORT 0
+#endif
+
+#if not UART_SUPPORT
+#undef DEBUG_SERIAL_SUPPORT
+#define DEBUG_SERIAL_SUPPORT 0
+#undef TERMINAL_SERIAL_SUPPORT
+#define TERMINAL_SERIAL_SUPPORT 0
#endif
#if ALEXA_SUPPORT
#undef RELAY_SUPPORT
-#define RELAY_SUPPORT 1 // and switches
+#define RELAY_SUPPORT 1 // alexa needs some switches support to work
#endif
#if RPN_RULES_SUPPORT
@@ -196,3 +216,62 @@
#undef ADC_MODE_VALUE
#define ADC_MODE_VALUE ADC_TOUT
#endif
+
+//------------------------------------------------------------------------------
+// RFBRIDGE EFM provider needs serial support
+#if RFB_PROVIDER == RFB_PROVIDER_EFM8BB1
+#undef UART_SUPPORT
+#define UART_SUPPORT 1
+#endif
+
+//------------------------------------------------------------------------------
+// Forcibly disable UART logger and terminal, these modules usually
+// are expecting to work with the port exclusivelly
+// (an so we could more easily describe things in hardware .h)
+#if (\
+ ((CSE7766_SUPPORT) && (CSE7766_PORT == DEBUG_SERIAL_PORT)) || \
+ ((EZOPH_SUPPORT) && (EZOPH_PORT == DEBUG_SERIAL_PORT)) || \
+ ((KINGART_CURTAIN_SUPPORT) && (KINGART_CURTAIN_PORT == DEBUG_SERIAL_PORT)) || \
+ ((MHZ19_SUPPORT) && (MHZ19_PORT == DEBUG_SERIAL_PORT)) || \
+ ((PM1006_SUPPORT) && (PM1006_PORT == DEBUG_SERIAL_PORT)) || \
+ ((PMSX003_SUPPORT) && (PMSX003_PORT == DEBUG_SERIAL_PORT)) || \
+ ((PZEM004TV30_SUPPORT) && (PZEM004TV30_PORT == DEBUG_SERIAL_PORT)) || \
+ ((PZEM004T_SUPPORT) && (PZEM004T_PORT == DEBUG_SERIAL_PORT)) || \
+ ((RELAY_PROVIDER_DUAL_SUPPORT) && (RELAY_PROVIDER_DUAL_PORT == DEBUG_SERIAL_PORT)) || \
+ ((RELAY_PROVIDER_STM_SUPPORT) && (RELAY_PROVIDER_STM_PORT == DEBUG_SERIAL_PORT)) || \
+ ((RFB_PROVIDER == RFB_PROVIDER_EFM8BB1) && (RFB_PORT == DEBUG_SERIAL_PORT)) || \
+ ((SDS011_SUPPORT) && (SDS011_PORT == DEBUG_SERIAL_PORT)) || \
+ ((SENSEAIR_SUPPORT) && (SENSEAIR_PORT == DEBUG_SERIAL_PORT)) || \
+ ((SM300D2_SUPPORT) && (SM300D2_PORT == DEBUG_SERIAL_PORT)) || \
+ ((T6613_SUPPORT) && (T6613_PORT == DEBUG_SERIAL_PORT)) || \
+ ((TUYA_SUPPORT) && (TUYA_PORT == DEBUG_SERIAL_PORT)) || \
+ ((V9261F_SUPPORT) && (V9261F_PORT == DEBUG_SERIAL_PORT)) || \
+ (defined(FOXEL_LIGHTFOX_DUAL) && (LIGHTFOX_PORT == DEBUG_SERIAL_PORT)) \
+)
+#undef DEBUG_SERIAL_SUPPORT
+#define DEBUG_SERIAL_SUPPORT 0
+#endif
+
+#if (\
+ ((CSE7766_SUPPORT) && (CSE7766_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((EZOPH_SUPPORT) && (EZOPH_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((KINGART_CURTAIN_SUPPORT) && (KINGART_CURTAIN_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((MHZ19_SUPPORT) && (MHZ19_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((PM1006_SUPPORT) && (PM1006_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((PMSX003_SUPPORT) && (PMSX003_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((PZEM004TV30_SUPPORT) && (PZEM004TV30_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((PZEM004T_SUPPORT) && (PZEM004T_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((RELAY_PROVIDER_DUAL_SUPPORT) && (RELAY_PROVIDER_DUAL_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((RELAY_PROVIDER_STM_SUPPORT) && (RELAY_PROVIDER_STM_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((RFB_PROVIDER == RFB_PROVIDER_EFM8BB1) && (RFB_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((SDS011_SUPPORT) && (SDS011_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((SENSEAIR_SUPPORT) && (SENSEAIR_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((SM300D2_SUPPORT) && (SM300D2_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((T6613_SUPPORT) && (T6613_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((TUYA_SUPPORT) && (TUYA_PORT == TERMINAL_SERIAL_PORT)) || \
+ ((V9261F_SUPPORT) && (V9261F_PORT == TERMINAL_SERIAL_PORT)) || \
+ (defined(FOXEL_LIGHTFOX_DUAL) && (LIGHTFOX_PORT == TERMINAL_SERIAL_PORT)) \
+)
+#undef TERMINAL_SERIAL_SUPPORT
+#define TERMINAL_SERIAL_SUPPORT 0
+#endif
diff --git a/code/espurna/config/deprecated.h b/code/espurna/config/deprecated.h
index 5a62f05b..01c984e3 100644
--- a/code/espurna/config/deprecated.h
+++ b/code/espurna/config/deprecated.h
@@ -100,8 +100,8 @@
#endif
#ifdef CSE7766_PIN
-#warning "CSE7766_PIN is deprecated! Please use CSE7766_RX_PIN instead"
-#define CSE7766_RX_PIN CSE7766_PIN
+#warning "CSE7766_PIN is deprecated! Please use UART[1-3]_RX_PIN"
+#define UART1_RX_PIN CSE7766_PIN
#endif
#ifdef WIFI_FALLBACK_APMODE
@@ -170,3 +170,8 @@
#define SENSOR_REAL_TIME_VALUES API_REAL_TIME_VALUES
#warning "API_REAL_TIME_VALUES is deprecated! Please use SENSOR_REAL_TIME_VALUES"
#endif
+
+#ifdef SERIAL_BAUDRATE
+#warning "SERIAL_BAUDRATE is deprecated! Please use UART[1-3]_BAUDRATE"
+#define UART1_BAUDRATE SERIAL_BAUDRATE
+#endif
diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h
index 473c9572..785ff829 100644
--- a/code/espurna/config/general.h
+++ b/code/espurna/config/general.h
@@ -59,12 +59,8 @@
#define DEBUG_SERIAL_SUPPORT 1 // Enable serial debug log
#endif
-#ifndef DEBUG_PORT
-#define DEBUG_PORT Serial // Default debugging port
-#endif
-
-#ifndef SERIAL_BAUDRATE
-#define SERIAL_BAUDRATE 115200 // Default baudrate
+#ifndef DEBUG_SERIAL_PORT
+#define DEBUG_SERIAL_PORT 1 // Default debugging port
#endif
#ifndef DEBUG_ADD_TIMESTAMP
@@ -72,22 +68,6 @@
// (in millis overflowing every 1000 seconds)
#endif
-// Second serial port (used for RX)
-
-#ifndef SERIAL_RX_ENABLED
-#define SERIAL_RX_ENABLED 0 // Secondary serial port for RX
-#endif
-
-#ifndef SERIAL_RX_PORT
-#define SERIAL_RX_PORT Serial // This setting is usually defined
- // in the hardware.h file for those
- // boards that require it
-#endif
-
-#ifndef SERIAL_RX_BAUDRATE
-#define SERIAL_RX_BAUDRATE 115200 // Default baudrate
-#endif
-
//------------------------------------------------------------------------------
// UDP debug log
@@ -175,16 +155,16 @@
//------------------------------------------------------------------------------
#ifndef TERMINAL_SUPPORT
-#define TERMINAL_SUPPORT 1 // Enable terminal commands (0.97Kb)
+#define TERMINAL_SUPPORT 1 // Enable terminal commands (0.97Kb)
#endif
#ifndef TERMINAL_SERIAL_SUPPORT
-#define TERMINAL_SERIAL_SUPPORT 1 // Enable terminal over UART
+#define TERMINAL_SERIAL_SUPPORT 1 // Enable terminal over UART
#endif
#ifndef TERMINAL_SERIAL_PORT
-#define TERMINAL_SERIAL_PORT Serial // Use specific 'global' Arduino HardwareSerial object
- // (UART0 by default)
+#define TERMINAL_SERIAL_PORT 1 // Use specific port configured as UART#_PORT
+ // (first port by default)
#endif
#ifndef TERMINAL_SERIAL_BUFFER_SIZE
@@ -389,11 +369,19 @@
#define RELAY_PROVIDER_STM_SUPPORT 0
#endif
+#ifndef RELAY_PROVIDER_STM_PORT
+#define RELAY_PROVIDER_STM_PORT 1
+#endif
+
// Sonoff Dual, using serial protocol
#ifndef RELAY_PROVIDER_DUAL_SUPPORT
#define RELAY_PROVIDER_DUAL_SUPPORT 0
#endif
+#ifndef RELAY_PROVIDER_DUAL_PORT
+#define RELAY_PROVIDER_DUAL_PORT 1
+#endif
+
// Default boot mode: 0 means OFF, 1 ON and 2 whatever was before
#ifndef RELAY_BOOT_MODE
#define RELAY_BOOT_MODE RELAY_BOOT_OFF
@@ -883,6 +871,20 @@
#define NOFUSS_INTERVAL 3600000 // Check for updates every hour
#endif
+// -----------------------------------------------------------------------------
+// UART
+// -----------------------------------------------------------------------------
+
+#ifndef UART_SUPPORT
+#define UART_SUPPORT 1
+#endif
+
+#ifndef UART_SOFTWARE_SUPPORT
+#define UART_SOFTWARE_SUPPORT 0 // (optional) allows to set TX and RX pins
+ // to values other than just 1, 2, 3, 13 or 15
+ // which by default use hardware UART
+#endif
+
// -----------------------------------------------------------------------------
// UART <-> MQTT
// -----------------------------------------------------------------------------
@@ -891,30 +893,8 @@
#define UART_MQTT_SUPPORT 0 // Not enabled by default
#endif
-#ifndef UART_MQTT_BAUDRATE
-#define UART_MQTT_BAUDRATE 115200
-#endif
-
-#ifndef UART_MQTT_CONFIG
-#define UART_MQTT_CONFIG SERIAL_8N1 // Arduino-specific serial configuration
- // 8bit, no parity bit, stop 1bit by default
- // (note that software serial config is prefixed with SW)
-#endif
-
-#ifndef UART_MQTT_SOFTWARE_SERIAL
-#define UART_MQTT_SOFTWARE_SERIAL 0 // Enable when hardware port needs to be replaced by a software one
- // (*not recommended* for full-duplex communication, it is possible
- // to lose data due to timing constraints)
- // Depends on TX and RX setting below and *will* use hardware
- // port when using known pins like 1 and 3, or 13 and 15
-#endif
-
-#ifndef UART_MQTT_TX_PIN
-#define UART_MQTT_TX_PIN 1
-#endif
-
-#ifndef UART_MQTT_RX_PIN
-#define UART_MQTT_RX_PIN 3
+#ifndef UART_MQTT_PORT
+#define UART_MQTT_PORT 1
#endif
#ifndef UART_MQTT_BUFFER_SIZE
@@ -1491,8 +1471,12 @@
#define RFB_PROVIDER RFB_PROVIDER_RCSWITCH
#endif
+#ifndef RFB_PORT
+#define RFB_PORT 1 // If EFM is enabled, use this UART port
+#endif
+
#ifndef RFB_RX_PIN
-#define RFB_RX_PIN GPIO_NONE
+#define RFB_RX_PIN GPIO_NONE // If RCSWITCH is enabled, use these pins
#endif
#ifndef RFB_TX_PIN
@@ -1712,8 +1696,8 @@
#define TUYA_SUPPORT 0
#endif
-#ifndef TUYA_SERIAL
-#define TUYA_SERIAL Serial
+#ifndef TUYA_PORT
+#define TUYA_PORT 1
#endif
#ifndef TUYA_FILTER_ENABLED
@@ -1777,6 +1761,26 @@
// Clamp duty value, prevent 100% power
#endif
+// =============================================================================
+// Curtain hardware support
+// =============================================================================
+
+#ifndef KINGART_CURTAIN_SUPPORT
+#define KINGART_CURTAIN_SUPPORT 0
+#endif
+
+#ifndef KINGART_CURTAIN_PORT
+#define KINGART_CURTAIN_PORT 1
+#endif
+
+#ifndef KINGART_CURTAIN_BUFFER_SIZE
+#define KINGART_CURTAIN_BUFFER_SIZE 128
+#endif
+
+#ifndef KINGART_CURTAIN_TERMINATION
+#define KINGART_CURTAIN_TERMINATION '\e' // Termination character after each message
+#endif
+
// =============================================================================
// Configuration helpers to help detect features
// =============================================================================
diff --git a/code/espurna/config/hardware.h b/code/espurna/config/hardware.h
index 6c26d2fd..f6180d0e 100644
--- a/code/espurna/config/hardware.h
+++ b/code/espurna/config/hardware.h
@@ -72,6 +72,7 @@
#define SENSOR_SUPPORT 0
#define TERMINAL_SERIAL_SUPORT 0
#define THINGSPEAK_SUPPORT 0
+ #define UART_SUPPORT 0
#define WEB_SUPPORT 0
#define DEBUG_TELNET_SUPPORT 1
@@ -111,6 +112,7 @@
#define SENSOR_SUPPORT 0
#define TERMINAL_SERIAL_SUPORT 0
#define THINGSPEAK_SUPPORT 0
+ #define UART_SUPPORT 0
// Small webpage to upload the .bin
#define MDNS_SERVER_SUPPORT 0
@@ -569,14 +571,19 @@
#define LED1_PIN 13
#define LED1_PIN_INVERSE 1
- // Disable UART noise
- #define DEBUG_SERIAL_SUPPORT 0
+ // Sensor uses UART
+ #define UART_SUPPORT 1
+
+ #define UART1_BAUDRATE 4800
+ #define UART1_TX_PIN GPIO_NONE
+ #define UART1_RX_PIN 3
// CSE7766
#ifndef CSE7766_SUPPORT
#define CSE7766_SUPPORT 1
#endif
- #define CSE7766_RX_PIN 3
+
+ #define CSE7766_PORT 1
#elif defined(ITEAD_SONOFF_POW_R3)
@@ -597,26 +604,33 @@
#define LED1_PIN 13
#define LED1_PIN_INVERSE 1
- // Disable UART noise
- #define DEBUG_SERIAL_SUPPORT 0
+ // Sensor uses UART0
+ #define UART_SUPPORT 1
+
+ #define UART1_BAUDRATE 4800
+ #define UART1_TX_PIN GPIO_NONE
+ #define UART1_RX_PIN 3
// CSE7766
#ifndef CSE7766_SUPPORT
#define CSE7766_SUPPORT 1
#endif
- #define CSE7766_RX_PIN 3
#elif defined(ITEAD_SONOFF_DUAL)
// Info
#define MANUFACTURER "ITEAD"
#define DEVICE "SONOFF_DUAL"
- #define SERIAL_BAUDRATE 19230
// LEDs
#define LED1_PIN 13
#define LED1_PIN_INVERSE 1
+ // Device uses serial port to control relays
+ #define UART1_BAUDRATE 19230
+ #define UART1_TX_PIN 1
+ #define UART1_RX_PIN 3
+
// Relays
#define RELAY_PROVIDER_DUAL_SUPPORT 1
@@ -627,9 +641,6 @@
// "Buttons" are attached to a secondary MCU and RELAY_PROVIDER_DUAL handles that
#define BUTTON_PROVIDER_GPIO_SUPPORT 0
- // Conflicts with relay operation
- #define DEBUG_SERIAL_SUPPORT 0
-
#elif defined(ITEAD_SONOFF_DUAL_R2)
#define MANUFACTURER "ITEAD"
@@ -852,18 +863,15 @@
#define RFB_SUPPORT 1
- // When using un-modified harware, ESPurna communicates with the secondary
- // MCU EFM8BB1 via UART at 19200 bps so we need to change the speed of
- // the port and remove UART noise on serial line
+ // When using un-modified harware, we communicate with EFM8BB1 MCU
+ // using the UART connection (needs to be configured to 19200 bps)
#ifndef RFB_PROVIDER
#define RFB_PROVIDER RFB_PROVIDER_EFM8BB1
#endif
- #ifndef DEBUG_SERIAL_SUPPORT
- #define DEBUG_SERIAL_SUPPORT 0
- #endif
-
- #define SERIAL_BAUDRATE 19200
+ #define UART1_BAUDRATE 19200
+ #define UART1_TX_PIN 1
+ #define UART1_RX_PIN 3
// Only used when RFB_PROVIDER is RCSWITCH
#define RFB_RX_PIN 4
@@ -1038,12 +1046,15 @@
#define LED1_PIN 13
#define LED1_PIN_INVERSE 1
- // Disable UART noise
- #define DEBUG_SERIAL_SUPPORT 0
+ // Sensor uses UART0
+ #define UART_SUPPORT 1
+
+ #define UART1_BAUDRATE 4800
+ #define UART1_TX_PIN GPIO_NONE
+ #define UART1_RX_PIN 3
// CSE7766
#define CSE7766_SUPPORT 1
- #define CSE7766_RX_PIN 3
#elif defined(ITEAD_SONOFF_S31_LITE)
@@ -1434,7 +1445,7 @@
#define DEVICE "ZJ_WFMN_C_11"
#define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER
- // Buttons
+ // Buttons
#define BUTTON1_PIN 0
#define BUTTON1_CONFIG BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON1_RELAY 1
@@ -1493,8 +1504,10 @@
#define MANUFACTURER "HUACANXING"
#define DEVICE "H801"
#define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER
- #define DEBUG_PORT Serial1
- #define SERIAL_RX_ENABLED 1
+
+ // Debug
+ #define UART1_TX_PIN 2
+ #define UART2_RX_PIN GPIO_NONE
// LEDs
#define LED1_PIN 5
@@ -1513,8 +1526,10 @@
#define MANUFACTURER "HUACANXING"
#define DEVICE "H802"
#define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER
- #define DEBUG_PORT Serial1
- #define SERIAL_RX_ENABLED 1
+
+ // Debug
+ #define UART1_TX_PIN 2
+ #define UART2_RX_PIN GPIO_NONE
// Light
#define LIGHT_CH1_PIN 12 // RED
@@ -1749,8 +1764,16 @@
// V9261F
#define V9261F_SUPPORT 1
- #define V9261F_PIN 2
- #define V9261F_PIN_INVERSE 1
+
+ // UART only reads data
+ #define UART_SUPPORT 1
+
+ #define UART1_BAUDRATE 4800
+ #define UART1_TX_PIN GPIO_NONE
+ #define UART1_RX_PIN 3
+ #define UART1_INVERSE 1
+
+ #define V9261F_PORT 1
// -----------------------------------------------------------------------------
// ECH1560
@@ -1779,10 +1802,23 @@
#define MANUFACTURER "GENERIC"
#define DEVICE "PZEM004T"
- #define PZEM004T_SUPPORT 1
+ #define PZEM004T_SUPPORT 1
- #define ALEXA_SUPPORT 0
- #define DEBUG_SERIAL_SUPPORT 0
+ #define ALEXA_SUPPORT 0
+
+// -----------------------------------------------------------------------------
+// PZEM004TV30
+// -----------------------------------------------------------------------------
+
+#elif defined(GENERIC_PZEM004TV30)
+
+ // Info
+ #define MANUFACTURER "GENERIC"
+ #define DEVICE "PZEM004TV30"
+
+ #define PZEM004TV30_SUPPORT 1
+
+ #define ALEXA_SUPPORT 0
// -----------------------------------------------------------------------------
// ESP-01 generic esp8266 board with 512 kB flash
@@ -1839,10 +1875,10 @@
// DS18B20
#ifndef DALLAS_SUPPORT
- #define DALLAS_SUPPORT 1
+ #define DALLAS_SUPPORT 1
#endif
- #define DALLAS_PIN 2
- #define DALLAS_UPDATE_INTERVAL 5000
+ #define DALLAS_PIN 2
+ #define DALLAS_UPDATE_INTERVAL 5000
#define TEMPERATURE_MIN_CHANGE 1.0
// -----------------------------------------------------------------------------
@@ -2000,7 +2036,7 @@
// LEDs
#define LED1_PIN 4 //BLUE
#define LED1_PIN_INVERSE 0
- #define LED2_PIN 5 //RED
+ #define LED2_PIN 5 //RED
#define LED2_PIN_INVERSE 1
// -----------------------------------------------------------------------------
@@ -2183,11 +2219,17 @@
#define MANUFACTURER "STM_RELAY"
#define DEVICE "2CH"
+ // UART
+ #define UART_SUPPORT 1
+ #define UART1_TX_PIN 1
+ #define UART1_RX_PIN 3
+
// Relays
#define RELAY_PROVIDER_STM_SUPPORT 1
+ #define RELAY_PROVIDER_STM_PORT 1
- #define RELAY1_PROVIDER RELAY_PROVIDER_STM
- #define RELAY2_PROVIDER RELAY_PROVIDER_STM
+ #define RELAY1_PROVIDER RELAY_PROVIDER_STM
+ #define RELAY2_PROVIDER RELAY_PROVIDER_STM
// Make sure we space out serial writes when relays are in sync. ref:
// - https://github.com/xoseperez/espurna/issues/1130
@@ -2195,10 +2237,6 @@
// - https://github.com/xoseperez/espurna/pull/1520
#define RELAY_DELAY_INTERLOCK 100
- // Remove UART noise on serial line
- // (or use `#define DEBUG_PORT Serial1` instead)
- #define DEBUG_SERIAL_SUPPORT 0
-
// -----------------------------------------------------------------------------
// Tonbux Powerstrip02
// -----------------------------------------------------------------------------
@@ -2209,6 +2247,9 @@
#define MANUFACTURER "TONBUX"
#define DEVICE "POWERSTRIP02"
+ // Disable UART noise since this board uses GPIO3
+ #define UART_SUPPORT 0
+
// Buttons
#define BUTTON1_PIN 5
#define BUTTON1_CONFIG BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
@@ -2314,7 +2355,7 @@
#define HLW8012_VOLTAGE_R_UP ( 2 * 1000000 ) // Upstream voltage resistor
// LED1 on RX pin
- #define DEBUG_SERIAL_SUPPORT 1
+ #define UART1_RX_PIN GPIO_NONE
// -----------------------------------------------------------------------------
// Maxcio W-DE004
@@ -2417,6 +2458,9 @@
#define MANUFACTURER "YIDIAN"
#define DEVICE "XSSSA05"
+ // Disable UART noise since this board uses GPIO3
+ #define UART_SUPPORT 0
+
// Buttons
#define BUTTON1_PIN 13
#define BUTTON1_CONFIG BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
@@ -2748,9 +2792,8 @@
#define MANUFACTURER "ZHILDE"
#define DEVICE "44EU_W"
- // Based on the reporter, this product uses GPIO1 and 3 for the button
- // and onboard LED, so hardware serial should be disabled...
- #define DEBUG_SERIAL_SUPPORT 0
+ // Disable UART noise since this board uses GPIO1 and GPIO3
+ #define UART_SUPPORT 0
// Buttons
#define BUTTON1_PIN 3
@@ -2784,7 +2827,7 @@
// Based on https://templates.blakadder.com/ZLD64-EU-W.html ,
// This product uses GPIO1 for LED and 3 for the button, so hardware serial should be disabled...
- #define DEBUG_SERIAL_SUPPORT 0
+ #define UART_SUPPORT 0
#define BUTTON1_PIN 3
#define BUTTON1_CONFIG BUTTON_PUSHBUTTON | BUTTON_SET_PULLUP | BUTTON_DEFAULT_HIGH
@@ -3048,7 +3091,7 @@
#define DEVICE "ZLD_34EU"
// Disable UART noise since this board uses GPIO3
- #define DEBUG_SERIAL_SUPPORT 0
+ #define UART_SUPPORT 0
// Buttons
#define BUTTON1_PIN 16
@@ -3086,7 +3129,6 @@
#define LED3_RELAY 3
#define LED4_RELAY 4
-
// -----------------------------------------------------------------------------
// Bruno Horta's OnOfre
// https://www.bhonofre.pt/
@@ -3205,7 +3247,7 @@
#define HLW8012_INTERRUPT_ON FALLING
// BUTTON1 and LED1 are using Serial pins
- #define DEBUG_SERIAL_SUPPORT 0
+ #define UART_SUPPORT 0
// -----------------------------------------------------------------------------
// Similar to both devices above but also with switchable USB ports
@@ -3240,14 +3282,19 @@
#define LED2_MODE LED_MODE_FINDME
#define LED2_RELAY 1
- // Disable UART noise
- #define DEBUG_SERIAL_SUPPORT 0
+ // Sensor works through serial port
+ #define UART_SUPPORT 1
+
+ #define UART1_BAUDRATE 4800
+ #define UART1_TX_PIN GPIO_NONE
+ #define UART1_RX_PIN 3
// CSE7766
#ifndef CSE7766_SUPPORT
- #define CSE7766_SUPPORT 1
+ #define CSE7766_SUPPORT 1
#endif
- #define CSE7766_RX_PIN 3
+
+ #define CSE7766_PORT 1
// -----------------------------------------------------------------------------
// Teckin SP21
@@ -3272,7 +3319,6 @@
#define LED1_PIN 2
#define LED1_PIN_INVERSE 1
-
// -----------------------------------------------------------------------------
// Teckin SP22 v1.4 - v1.6
// -----------------------------------------------------------------------------
@@ -3315,7 +3361,7 @@
#define HLW8012_INTERRUPT_ON FALLING
// BUTTON1 and LED1 are using Serial pins
- #define DEBUG_SERIAL_SUPPORT 0
+ #define UART_SUPPORT 0
// -----------------------------------------------------------------------------
// Teckin SP22 v1.4 - v1.6
@@ -3432,7 +3478,7 @@
#define LED1_PIN_INVERSE 1
// LED1 is using TX pin
- #define DEBUG_SERIAL_SUPPORT 0
+ #define UART_SUPPORT 0
// ----------------------------------------------------------------------------------------
// Power socket 16A similar to BLITZWOLF_BWSHPX but button pin differs
@@ -3478,8 +3524,8 @@
#define HLW8012_INTERRUPT_ON FALLING
// ----------------------------------------------------------------------------------------
-// Power strip 15A - 3 Sockets + 3 USB ports
-// This device uses just one digital button (main one). All other three buttons are connected
+// Power strip 15A - 3 Sockets + 3 USB ports
+// This device uses just one digital button (main one). All other three buttons are connected
// to the ADC pin via resistors with different values. This approach is known under the name of
// "Resistor Ladder" - https://en.wikipedia.org/wiki/Resistor_ladder
// https://www.amazon.de/-/en/gp/product/B085XXCPRD
@@ -3491,14 +3537,10 @@
#define MANUFACTURER "GOSUND"
#define DEVICE "P1"
-
//Enable this to view buttons analog level.
//Or, use adc terminal command
//#define ANALOG_SUPPORT 1
- // Disable UART noise
- #define DEBUG_SERIAL_SUPPORT 0
-
// Buttons
#define BUTTON_PROVIDER_GPIO_SUPPORT 1
#define BUTTON_PROVIDER_ANALOG_SUPPORT 1
@@ -3531,11 +3573,19 @@
// LEDs
#define LED1_PIN 2
- #define LED1_PIN_INVERSE 1
+ #define LED1_PIN_INVERSE 1
+
+ // Sensor uses UART0
+ #define UART_SUPPORT 1
+
+ #define UART1_BAUDRATE 4800
+ #define UART1_TX_PIN GPIO_NONE
+ #define UART1_RX_PIN 3
// CSE7766
#define CSE7766_SUPPORT 1
- #define CSE7766_RX_PIN 3
+
+ #define CSE7766_PORT 1
// ----------------------------------------------------------------------------------------
// Homecube 16A is similar but some pins differ and it also has RGB LEDs
@@ -3619,9 +3669,6 @@
#define LED2_MODE LED_MODE_FINDME
#define LED2_RELAY 1
- // Disable UART noise
- #define DEBUG_SERIAL_SUPPORT 0
-
// HJL01 / BL0937
#ifndef HLW8012_SUPPORT
#define HLW8012_SUPPORT 1
@@ -3630,6 +3677,9 @@
#define HLW8012_CF1_PIN 14
#define HLW8012_CF_PIN 5
+ // Disable UART noise
+ #define UART_SUPPORT 0
+
#define HLW8012_SEL_CURRENT LOW
#define HLW8012_CURRENT_RATIO 25740
#define HLW8012_VOLTAGE_RATIO 313400
@@ -4342,7 +4392,12 @@
// Info
#define MANUFACTURER "FOXEL"
#define DEVICE "LIGHTFOX_DUAL"
- #define SERIAL_BAUDRATE 19200
+
+ #define UART1_BAUDRATE 19200
+ #define UART1_TX_PIN 1
+ #define UART1_RX_PIN 3
+
+ #define LIGHTFOX_PORT 1
// Relays
#define LIGHTFOX_RELAYS 2
@@ -4360,9 +4415,6 @@
#define BUTTON3_RELAY 2
#define BUTTON4_RELAY 1
- // Conflicts with relay operation
- #define DEBUG_SERIAL_SUPPORT 0
-
// -----------------------------------------------------------------------------
// Teckin SP20
// -----------------------------------------------------------------------------
@@ -4522,13 +4574,17 @@
#define TUYA_SUPPORT 1
#define LIGHT_PROVIDER LIGHT_PROVIDER_CUSTOM
+ #define TUYA_PORT 1
+
+ #define UART1_BAUDRATE 9600
+ #define UART1_TX_PIN 1
+ #define UART1_RX_PIN 3
+
#define LED1_GPIO 14
#define TUYA_CH_STATE_DPID 1
#define TUYA_CH1_DPID 2
- #define DEBUG_SERIAL_SUPPORT 0
-
// -----------------------------------------------------------------------------
// Etekcity ESW01-USA
// https://www.amazon.com/Etekcity-Voltson-Outlet-Monitoring-Required/dp/B01M3MYIFS
@@ -4583,6 +4639,7 @@
#define DEVICE "UAP1"
// Inputs
+ #define DIGITAL_SUPPORT 1
#define DIGITAL1_PIN 4
#define DIGITAL2_PIN 5
@@ -4601,7 +4658,7 @@
#define LED1_PIN 2
// Disable UART noise
- #define DEBUG_SERIAL_SUPPORT 0
+ #define UART_SUPPORT 0
// -----------------------------------------------------------------------------
// TFLAG NX-SM100 & NX-SM200
@@ -4764,9 +4821,12 @@
#define LED1_PIN 13
#define LED1_PIN_INVERSE 1
- // KINGART module handles the UART, can't print any debug messages
#define KINGART_CURTAIN_SUPPORT 1
- #define DEBUG_SERIAL_SUPPORT 0
+ #define KINGART_CURTAIN_PORT 1
+
+ #define UART1_BAUDRATE 19200
+ #define UART1_TX_PIN 1
+ #define UART1_RX_PIN 3
// -----------------------------------------------------------------------------
// LSC Smart LED Light Strip (Smart CXonnect Series) available ACTION (Germany)
@@ -4862,9 +4922,8 @@
#define MANUFACTURER "NEDIS"
#define DEVICE "WIFIP310FWT"
- // Based on the reporter, this product uses GPIO1 and 3 for the button
- // and onboard LED, so hardware serial should be disabled...
- #define DEBUG_SERIAL_SUPPORT 0
+ // Disable UART noise since this board uses GPIO1 and GPIO3
+ #define UART_SUPPORT 0
// Buttons
#define BUTTON1_PIN 3
@@ -4917,6 +4976,9 @@
#define MANUFACTURER "ARLEC"
#define DEVICE "PB89HA"
+ // Disable UART noise since this board uses GPIO1 and GPIO3
+ #define UART_SUPPORT 0
+
// Buttons
#define BUTTON1_PIN 3
#define BUTTON1_CONFIG BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
@@ -5041,7 +5103,7 @@
// -----------------------------------------------------------------------------
// Mirabella Genio White A60
// https://www.woolworths.com.au/shop/productdetails/877102/mirabella-smart-led-gls-es-9w-cool-white
-// Like https://www.mirabellagenio.com.au/product-range/mirabella-genio-wi-fi-dimmable-9w-led-gls-bulb/
+// Like https://www.mirabellagenio.com.au/product-range/mirabella-genio-wi-fi-dimmable-9w-led-gls-bulb/
// but in cardboard box, Item # I002604
// -----------------------------------------------------------------------------
@@ -5073,6 +5135,9 @@
#define MANUFACTURER "YAGUSMART"
#define DEVICE "TOUCH_HWMOD_1G"
+ // Disable UART noise since this board uses GPIO3
+ #define UART_SUPPORT 0
+
#define BUTTON1_CONFIG BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON1_PIN 3
#define BUTTON1_RELAY 1
@@ -5091,12 +5156,13 @@
#define LED1_PIN_INVERSE 1
#define LED1_MODE LED_MODE_WIFI
- #define DEBUG_SERIAL_SUPPORT 0
-
#elif defined(YAGUSMART_TOUCH_HWMOD_2G)
#define MANUFACTURER "YAGUSMART"
#define DEVICE "TOUCH_HWMOD_2G"
+ // Disable UART noise since this board uses GPIO3
+ #define UART_SUPPORT 0
+
#define BUTTON1_CONFIG BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define BUTTON1_PIN 3
#define BUTTON1_RELAY 1
@@ -5128,12 +5194,13 @@
#define LED1_PIN_INVERSE 1
#define LED1_MODE LED_MODE_WIFI
- #define DEBUG_SERIAL_SUPPORT 0
-
#elif defined(YAGUSMART_TOUCH_HWMOD_3G)
#define MANUFACTURER "YAGUSMART"
#define DEVICE "TOUCH_HWMOD_3G"
+ // Disable UART noise since this board uses GPIO1 and GPIO3
+ #define UART_SUPPORT 0
+
#define BUTTON1_CONFIG BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH | BUTTON_SET_PULLUP
#define BUTTON1_PIN 12
#define BUTTON1_PRESS BUTTON_ACTION_TOGGLE
@@ -5175,8 +5242,6 @@
#define LED1_PIN_INVERSE 1
#define LED1_MODE LED_MODE_WIFI
- #define DEBUG_SERIAL_SUPPORT 0
-
#else
#error "UNSUPPORTED HARDWARE!!"
diff --git a/code/espurna/config/sensors.h b/code/espurna/config/sensors.h
index e94567e8..76578e98 100644
--- a/code/espurna/config/sensors.h
+++ b/code/espurna/config/sensors.h
@@ -264,22 +264,11 @@
#define CSE7766_SUPPORT 0
#endif
-#ifndef CSE7766_RX_PIN
-#define CSE7766_RX_PIN 3 // RX pin connected to the CSE7766
- // As we never transmit anything, this is the only pin used
+#ifndef CSE7766_PORT
+#define CSE7766_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 4800`)
#endif
-#ifndef CSE7766_PIN_INVERSE
-#define CSE7766_PIN_INVERSE 0 // Signal is inverted
-#endif
-
-#define CSE7766_SYNC_INTERVAL 300 // Safe time between transmissions (ms)
-#define CSE7766_BAUDRATE 4800 // UART baudrate
-
-#define CSE7766_V1R 1.0 // 1mR current resistor
-#define CSE7766_V2R 1.0 // 1M voltage resistor
-
-
//------------------------------------------------------------------------------
// Digital sensor
// Enable support by passing DIGITAL_SUPPORT=1 build flag
@@ -812,12 +801,9 @@
#define MHZ19_SUPPORT 0
#endif
-#ifndef MHZ19_RX_PIN
-#define MHZ19_RX_PIN 13
-#endif
-
-#ifndef MHZ19_TX_PIN
-#define MHZ19_TX_PIN 15
+#ifndef MHZ19_PORT
+#define MHZ19_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 9600`)
#endif
//------------------------------------------------------------------------------
@@ -913,12 +899,9 @@
#define PM1006_SUPPORT 0
#endif
-#ifndef PM1006_RX_PIN
-#define PM1006_RX_PIN 3
-#endif
-
-#ifndef PM1006_BAUDRATE
-#define PM1006_BAUDRATE 9600
+#ifndef PM1006_PORT
+#define PM1006_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 9600`)
#endif
//------------------------------------------------------------------------------
@@ -930,6 +913,11 @@
#define PMSX003_SUPPORT 0
#endif
+#ifndef PMS_PORT
+#define PMS_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 9600`)
+#endif
+
#ifndef PMS_TYPE
#define PMS_TYPE PMS_TYPE_X003
#endif
@@ -941,22 +929,6 @@
#define PMS_SMART_SLEEP 0
#endif
-#ifndef PMS_USE_SOFT
-#define PMS_USE_SOFT 0 // If PMS_USE_SOFT == 1, DEBUG_SERIAL_SUPPORT must be 0
-#endif
-
-#ifndef PMS_RX_PIN
-#define PMS_RX_PIN 13 // Software serial RX GPIO (if PMS_USE_SOFT == 1)
-#endif
-
-#ifndef PMS_TX_PIN
-#define PMS_TX_PIN 15 // Software serial TX GPIO (if PMS_USE_SOFT == 1)
-#endif
-
-#ifndef PMS_HW_PORT
-#define PMS_HW_PORT Serial // Hardware serial port (if PMS_USE_SOFT == 0)
-#endif
-
//------------------------------------------------------------------------------
// Pulse Meter Energy monitor
// Enable support by passing PULSEMETER_SUPPORT=1 build flag
@@ -990,22 +962,9 @@
#define PZEM004T_SUPPORT 0
#endif
-#ifndef PZEM004T_USE_SOFT
-#define PZEM004T_USE_SOFT 0 // By default, use Hardware serial with GPIO15 (TX) and GPIO13 (RX)
- // (but, make sure to change DEBUG_PORT to Serial1 or set DEBUG_SERIAL_SUPPORT to 0)
-#endif
-
-#ifndef PZEM004T_RX_PIN
-#define PZEM004T_RX_PIN 13 // Serial RX GPIO (if PZEM004T_USE_SOFT == 1, creates a SoftwareSerial object)
-#endif
-
-#ifndef PZEM004T_TX_PIN
-#define PZEM004T_TX_PIN 15 // Serial TX GPIO (if PZEM004T_USE_SOFT == 1, creates a SoftwareSerial object)
-#endif
-
-#ifndef PZEM004T_HW_PORT
-#define PZEM004T_HW_PORT Serial // Hardware serial port (if PZEM004T_USE_SOFT == 0)
- // ESP8266: Serial1 does not allow receiving data, no point in changing this setting
+#ifndef PZEM004T_PORT
+#define PZEM004T_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 9600`)
#endif
#ifndef PZEM004T_READ_INTERVAL
@@ -1047,22 +1006,9 @@
#define PZEM004TV30_ADDRESS 0xF8 // Default: factory value
#endif
-#ifndef PZEM004TV30_USE_SOFT
-#define PZEM004TV30_USE_SOFT 0 // By default, use Hardware serial with GPIO15 (TX) and GPIO13 (RX)
- // (but, make sure to change DEBUG_PORT to Serial1 or set DEBUG_SERIAL_SUPPORT to 0)
-#endif
-
-#ifndef PZEM004TV30_HW_PORT
-#define PZEM004TV30_HW_PORT Serial // Hardware serial port (if PZEM004TV30_USE_SOFT == 0)
- // ESP8266: Serial1 does not allow receiving data, no point in changing this setting
-#endif
-
-#ifndef PZEM004TV30_RX_PIN
-#define PZEM004TV30_RX_PIN 13 // Serial RX GPIO (if PZEM004T_USE_SOFT == 1, creates a SoftwareSerial object)
-#endif
-
-#ifndef PZEM004TV30_TX_PIN
-#define PZEM004TV30_TX_PIN 15 // Serial TX GPIO (if PZEM004T_USE_SOFT == 1, creates a SoftwareSerial object)
+#ifndef PZEM004TV30_PORT
+#define PZEM004TV30_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 9600`)
#endif
#ifndef PZEM004TV30_DEBUG
@@ -1078,12 +1024,9 @@
#define SDS011_SUPPORT 0
#endif
-#ifndef SDS011_RX_PIN
-#define SDS011_RX_PIN 14
-#endif
-
-#ifndef SDS011_TX_PIN
-#define SDS011_TX_PIN 12
+#ifndef SDS011_PORT
+#define SDS011_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 9600`)
#endif
//------------------------------------------------------------------------------
@@ -1095,12 +1038,9 @@
#define SENSEAIR_SUPPORT 0
#endif
-#ifndef SENSEAIR_RX_PIN
-#define SENSEAIR_RX_PIN 0
-#endif
-
-#ifndef SENSEAIR_TX_PIN
-#define SENSEAIR_TX_PIN 2
+#ifndef SENSEAIR_PORT
+#define SENSEAIR_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 9600`)
#endif
//------------------------------------------------------------------------------
@@ -1138,12 +1078,9 @@
#define SM300D2_SUPPORT 0
#endif
-#ifndef SM300D2_RX_PIN
-#define SM300D2_RX_PIN 13
-#endif
-
-#ifndef SM300D2_BAUDRATE
-#define SM300D2_BAUDRATE 9600
+#ifndef SM300D2_PORT
+#define SM300D2_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 9600`)
#endif
//------------------------------------------------------------------------------
@@ -1193,12 +1130,9 @@
#define T6613_SUPPORT 0
#endif
-#ifndef T6613_RX_PIN
-#define T6613_RX_PIN 4
-#endif
-
-#ifndef T6613_TX_PIN
-#define T6613_TX_PIN 5
+#ifndef T6613_PORT
+#define T6613_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 9600`)
#endif
//------------------------------------------------------------------------------
@@ -1223,16 +1157,14 @@
#define V9261F_SUPPORT 0
#endif
-#ifndef V9261F_PIN
-#define V9261F_PIN 2 // TX pin from the V9261F
-#endif
-
-#ifndef V9261F_PIN_INVERSE
-#define V9261F_PIN_INVERSE 1 // Signal is inverted
+#ifndef V9261F_PORT
+#define V9261F_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 4800` and `UART#_INVERSE 1`)
#endif
+#ifndef V9261F_SYNC_INTERVAL
#define V9261F_SYNC_INTERVAL 600 // Sync signal length (ms)
-#define V9261F_BAUDRATE 4800 // UART baudrate
+#endif
// Default ratios
#define V9261F_CURRENT_FACTOR 79371434.0
@@ -1320,12 +1252,9 @@
#define EZOPH_SUPPORT 0
#endif
-#ifndef EZOPH_RX_PIN
-#define EZOPH_RX_PIN 13 // Software serial RX GPIO
-#endif
-
-#ifndef EZOPH_TX_PIN
-#define EZOPH_TX_PIN 15 // Software serial TX GPIO
+#ifndef EZOPH_PORT
+#define EZOPH_PORT 1 // By default, use the first port
+ // (needs `UART#_BAUDRATE 9600`)
#endif
#ifndef EZOPH_SYNC_INTERVAL
@@ -1477,6 +1406,26 @@
// Configuration helpers
// =============================================================================
+// UART support for sensors using serial port
+// (notice that baudrate and mode config is set *externally*)
+#if (\
+ CSE7766_SUPPORT || \
+ MHZ19_SUPPORT || \
+ PM1006_SUPPORT || \
+ PMSX003_SUPPORT || \
+ PZEM004T_SUPPORT || \
+ SENSEAIR_SUPPORT || \
+ SDS011_SUPPORT || \
+ SM300D2_SUPPORT || \
+ T6613_SUPPORT || \
+ V9261F_SUPPORT || \
+ EZOPH_SUPPORT || \
+ PZEM004TV30_SUPPORT \
+)
+#undef UART_SUPPORT
+#define UART_SUPPORT 1
+#endif
+
// I2C support when sensor needs it
#if ( ADE7953_SUPPORT || \
AM2320_SUPPORT || \
diff --git a/code/espurna/curtain_kingart.cpp b/code/espurna/curtain_kingart.cpp
index a0536f21..4097b4da 100644
--- a/code/espurna/curtain_kingart.cpp
+++ b/code/espurna/curtain_kingart.cpp
@@ -21,17 +21,6 @@ Copyright (C) 2020 - Eric Chauvet
#include "settings.h"
#include "ws.h"
-#ifndef KINGART_CURTAIN_PORT
-#define KINGART_CURTAIN_PORT Serial // Hardware serial port by default
-#endif
-
-#ifndef KINGART_CURTAIN_BUFFER_SIZE
-#define KINGART_CURTAIN_BUFFER_SIZE 100 // Local UART buffer size
-#endif
-
-#define KINGART_CURTAIN_TERMINATION '\e' // Termination character after each message
-#define KINGART_CURTAIN_BAUDRATE 19200 // Serial speed is fixed for the device
-
// --> Let see after if we define a curtain generic switch, use these for now
#define CURTAIN_BUTTON_UNKNOWN -1
#define CURTAIN_BUTTON_PAUSE 0
@@ -48,6 +37,8 @@ Copyright (C) 2020 - Eric Chauvet
#define KINGART_DEBUG_MSG_P(...) do { if (_curtain_debug_flag) { DEBUG_MSG_P(__VA_ARGS__); } } while(0)
+Stream* _curtain_port { nullptr };
+
char _KACurtainBuffer[KINGART_CURTAIN_BUFFER_SIZE];
bool _KACurtainNewData = false;
@@ -117,9 +108,9 @@ void _KASetMoving() {
//------------------------------------------------------------------------------
//Send a buffer to serial
void _KACurtainSend(const char * tx_buffer) {
- KINGART_CURTAIN_PORT.print(tx_buffer);
- KINGART_CURTAIN_PORT.print(KINGART_CURTAIN_TERMINATION);
- KINGART_CURTAIN_PORT.flush();
+ _curtain_port->print(tx_buffer);
+ _curtain_port->print(KINGART_CURTAIN_TERMINATION);
+ _curtain_port->flush();
KINGART_DEBUG_MSG_P(PSTR("[KA] UART OUT %s\n"), tx_buffer);
}
@@ -192,8 +183,8 @@ void _KAStopMoving() {
//Receive a buffer from serial
bool _KACurtainReceiveUART() {
static unsigned char ndx = 0;
- while (KINGART_CURTAIN_PORT.available() > 0 && !_KACurtainNewData) {
- char rc = KINGART_CURTAIN_PORT.read();
+ while (_curtain_port->available() > 0 && !_KACurtainNewData) {
+ char rc = _curtain_port->read();
if (rc != KINGART_CURTAIN_TERMINATION) {
_KACurtainBuffer[ndx] = rc;
if (ndx < KINGART_CURTAIN_BUFFER_SIZE - 1) ndx++;
@@ -468,9 +459,12 @@ void _KACurtainLoop() {
//------------------------------------------------------------------------------
void kingartCurtainSetup() {
+ const auto port = uartPort(KINGART_CURTAIN_PORT - 1);
+ if (!port || (!port->tx || !port->rx)) {
+ return;
+ }
- // Init port to receive and send messages
- KINGART_CURTAIN_PORT.begin(KINGART_CURTAIN_BAUDRATE);
+ _curtain_port = port->stream;
#if MQTT_SUPPORT
// Register MQTT callback only when supported
diff --git a/code/espurna/debug.cpp b/code/espurna/debug.cpp
index 2b450e49..cce88fd8 100644
--- a/code/espurna/debug.cpp
+++ b/code/espurna/debug.cpp
@@ -431,11 +431,58 @@ void dump(Print& out) {
#if DEBUG_SERIAL_SUPPORT
namespace serial {
-void output(Print& out, const char (&prefix)[10], const char* message, size_t len) {
+using Output = void(*)(const char (&)[10], const char*, size_t);
+
+void null_output(const char (&)[10], const char*, size_t) {
+}
+
+namespace internal {
+
+Print* port { nullptr };
+Output output { null_output };
+
+} // namespace
+
+void output(const char (&prefix)[10], const char* message, size_t len) {
+ internal::output(prefix, message, len);
+}
+
+void port_output(const char (&prefix)[10], const char* message, size_t len) {
if (prefix[0] != '\0') {
- out.write(&prefix[0], sizeof(prefix) - 1);
+ internal::port->write(&prefix[0], sizeof(prefix) - 1);
}
- out.write(message, len);
+ internal::port->write(message, len);
+}
+
+void setup() {
+ // HardwareSerial::begin() will automatically enable this when
+ // `#if defined(DEBUG_ESP_PORT) && !defined(NDEBUG)`
+ // Do not interfere when that is the case
+ const auto port = uartPort(DEBUG_SERIAL_PORT - 1);
+ if (!port || !port->tx) {
+ return;
+ }
+
+ // TODO: notice that SDK accepts anything as putc / printf,
+ // but we don't really have a good reason to wrire both
+ // this debug output and the one from SDK
+ // (and most of the time this is need to grab boot info from a
+ // physically connected device)
+ if (!build::coreDebug() && settings::sdkDebug()) {
+ switch (port->type) {
+ case driver::uart::Type::Uart0:
+ uart_set_debug(0);
+ break;
+ case driver::uart::Type::Uart1:
+ uart_set_debug(1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ internal::port = port->stream;
+ internal::output = port_output;
}
} // namespace serial
@@ -511,7 +558,7 @@ void send(const char* message, size_t len, Timestamp timestamp) {
bool pause { false };
#if DEBUG_SERIAL_SUPPORT
- serial::output(DEBUG_PORT, prefix, message, len);
+ serial::output(prefix, message, len);
#endif
#if DEBUG_UDP_SUPPORT
@@ -574,13 +621,6 @@ bool status(espurna::heartbeat::Mask mask) {
}
void configure() {
- // HardwareSerial::begin() will automatically enable this when
- // `#if defined(DEBUG_ESP_PORT) && !defined(NDEBUG)`
- // Do not interfere when that is the case
- if (!build::coreDebug()) {
- DEBUG_PORT.setDebugOutput(settings::sdkDebug());
- }
-
#if DEBUG_LOG_BUFFER_SUPPORT
if (settings::buffer()) {
debug::buffer::enable(settings::bufferSize());
@@ -701,16 +741,14 @@ void debugShowBanner() {
void debugSetup() {
#if DEBUG_SERIAL_SUPPORT
- DEBUG_PORT.begin(SERIAL_BAUDRATE);
+ espurna::debug::serial::setup();
#endif
-
#if DEBUG_UDP_SUPPORT
if (espurna::debug::syslog::build::enabled()) {
espurna::debug::syslog::configure();
espurnaRegisterReload(espurna::debug::syslog::configure);
}
#endif
-
#if DEBUG_LOG_BUFFER_SUPPORT
#if TERMINAL_SUPPORT
espurna::debug::terminal::setup();
diff --git a/code/espurna/espurna.h b/code/espurna/espurna.h
index 0f896527..34512060 100644
--- a/code/espurna/espurna.h
+++ b/code/espurna/espurna.h
@@ -34,6 +34,7 @@ along with this program. If not, see .
#include "settings.h"
#include "system.h"
#include "terminal.h"
+#include "uart.h"
#include "utils.h"
#include "network.h"
#include "wifi.h"
diff --git a/code/espurna/garland/color.h b/code/espurna/garland/color.h
index 96f5225a..fac72f05 100644
--- a/code/espurna/garland/color.h
+++ b/code/espurna/garland/color.h
@@ -96,15 +96,11 @@ public:
return r == 0 && g == 0 && b == 0;
}
- void println() const {
- Serial.print(("r="));Serial.print(r);Serial.print((" "));
- Serial.print(("g="));Serial.print(g);Serial.print( (" "));
- Serial.print(("b="));Serial.println(b);
- }
-
String to_str() {
char buf[20];
- sprintf(buf, "r=%hhu, g=%hhu, b=%hhu", r, g, b);
+ snprintf_P(buf, sizeof(buf),
+ PSTR("r=%hhu, g=%hhu, b=%hhu"),
+ r, g, b);
return buf;
}
};
diff --git a/code/espurna/gpio.cpp b/code/espurna/gpio.cpp
index 9d866850..c6c79280 100644
--- a/code/espurna/gpio.cpp
+++ b/code/espurna/gpio.cpp
@@ -19,9 +19,8 @@ Copyright (C) 2017-2019 by Xose Pérez
#include "ws.h"
namespace espurna {
-namespace {
-
namespace peripherals {
+namespace {
// TODO: `struct Register { ... }` with additional size params?
// e.g. GPIO IN / OUT register is techically u16, and we don't detect writing past the allowed 'mask'
@@ -399,8 +398,9 @@ void mode(uint8_t pin, uint8_t mode) {
}
} // namespace pin
-} // namespace peripherals
+
} // namespace
+} // namespace peripherals
namespace gpio {
namespace {
diff --git a/code/espurna/lightfox.cpp b/code/espurna/lightfox.cpp
index d6d310d7..27e0ec28 100644
--- a/code/espurna/lightfox.cpp
+++ b/code/espurna/lightfox.cpp
@@ -38,6 +38,8 @@ constexpr size_t _lightfoxBuildRelays() {
return LIGHTFOX_RELAYS;
}
+static Stream* _lightfox_port { nullptr };
+
// -----------------------------------------------------------------------------
// PROTOCOL
// -----------------------------------------------------------------------------
@@ -56,8 +58,8 @@ void _lightfoxSend(uint8_t code) {
static_cast('\r'),
static_cast('\n')
};
- Serial.write(data, sizeof(data));
- Serial.flush();
+ _lightfox_port->write(data, sizeof(data));
+ _lightfox_port->flush();
DEBUG_MSG_P(PSTR("[LIGHTFOX] Code %02X sent\n"), code);
}
@@ -89,11 +91,6 @@ public:
}
bool setup() override {
- static bool once { false };
- if (!once) {
- once = true;
- Serial.begin(SERIAL_BAUDRATE);
- }
return true;
}
@@ -125,8 +122,8 @@ public:
DEBUG_MSG_P(PSTR("[LIGHTFOX] Sending DUAL mask: 0x%02X\n"), mask);
uint8_t buffer[4] { 0xa0, 0x04, static_cast(mask), 0xa1 };
- Serial.write(buffer, sizeof(buffer));
- Serial.flush();
+ _lightfox_port->write(buffer, sizeof(buffer));
+ _lightfox_port->flush();
}
private:
@@ -193,12 +190,12 @@ void _lightfoxCommandsSetup() {
// -----------------------------------------------------------------------------
void _lightfoxInputLoop() {
- if (Serial.available() < 4) {
+ if (_lightfox_port->available() < 4) {
return;
}
unsigned char bytes[4] = {0};
- Serial.readBytes(bytes, 4);
+ _lightfox_port->readBytes(bytes, 4);
if ((bytes[0] != 0xA0) && (bytes[1] != 0x04) && (bytes[3] != 0xA1)) {
return;
}
@@ -218,6 +215,13 @@ void _lightfoxInputLoop() {
void lightfoxSetup() {
+ const auto port = uartPort(LIGHTFOX_PORT - 1);
+ if (!port || !port.tx) {
+ return;
+ }
+
+ _lightfox_port = port->stream;
+
#if WEB_SUPPORT
wsRegister()
.onVisible(_lightfoxWebSocketOnVisible)
diff --git a/code/espurna/main.cpp b/code/espurna/main.cpp
index f3a5e484..1221aba5 100644
--- a/code/espurna/main.cpp
+++ b/code/espurna/main.cpp
@@ -113,6 +113,9 @@ void setup() {
// Init persistance
settingsSetup();
+ // Init hardware / software UART ports
+ uartSetup();
+
// Configure logger and crash recorder
#if DEBUG_SUPPORT
debugConfigureBoot();
diff --git a/code/espurna/relay.cpp b/code/espurna/relay.cpp
index 75cd0bbc..6dbf5911 100644
--- a/code/espurna/relay.cpp
+++ b/code/espurna/relay.cpp
@@ -1370,8 +1370,11 @@ public:
static bool once { false };
if (!once) {
once = true;
- Serial.begin(SERIAL_BAUDRATE);
- espurnaRegisterLoop(loop);
+ const auto port = uartPort(RELAY_PROVIDER_DUAL_PORT - 1);
+ if (port) {
+ _port = port->stream;
+ espurnaRegisterLoop(loop);
+ }
}
return true;
}
@@ -1421,17 +1424,17 @@ public:
DEBUG_MSG_P(PSTR("[RELAY] Sending DUAL mask: %s\n"), mask.toString().c_str());
uint8_t buffer[4] { 0xa0, 0x04, static_cast(mask.toUnsigned()), 0xa1 };
- Serial.write(buffer, sizeof(buffer));
- Serial.flush();
+ _port->write(buffer, sizeof(buffer));
+ _port->flush();
}
static void loop() {
- if (Serial.available() < 4) {
+ if (_port->available() < 4) {
return;
}
unsigned char bytes[4] = {0};
- Serial.readBytes(bytes, 4);
+ _port->readBytes(bytes, 4);
if ((bytes[0] != 0xA0) && (bytes[1] != 0x04) && (bytes[3] != 0xA1)) {
return;
}
@@ -1453,6 +1456,7 @@ public:
}
private:
+ Stream* _port { nullptr };
size_t _id;
static std::vector _instances;
@@ -1480,7 +1484,10 @@ public:
static bool once { false };
if (!once) {
once = true;
- Serial.begin(SERIAL_BAUDRATE);
+ const auto port = uartPort(RELAY_PROVIDER_STM_PORT - 1);
+ if (port) {
+ _port = port->stream;
+ }
}
return true;
}
@@ -1492,19 +1499,22 @@ public:
}
void change(bool status) override {
- Serial.flush();
- Serial.write(0xA0);
- Serial.write(_id + 1);
- Serial.write(status);
- Serial.write(0xA1 + status + _id);
+ if (_port) {
+ _port->flush();
+ _port->write(0xA0);
+ _port->write(_id + 1);
+ _port->write(status);
+ _port->write(0xA1 + status + _id);
- // TODO: is this really solved via interlock delay, so we don't have to switch contexts here?
- //delay(100);
+ // TODO: is this really solved via interlock delay, so we don't have to switch contexts here?
+ //delay(100);
- Serial.flush();
+ _port->flush();
+ }
}
private:
+ Stream* _port;
size_t _id;
};
diff --git a/code/espurna/rfbridge.cpp b/code/espurna/rfbridge.cpp
index edbc6375..b0566a3d 100644
--- a/code/espurna/rfbridge.cpp
+++ b/code/espurna/rfbridge.cpp
@@ -39,6 +39,7 @@ bool _rfb_transmit { false };
#else
+static Stream* _rfb_port { nullptr };
constexpr bool _rfb_receive { true };
constexpr bool _rfb_transmit { true };
@@ -654,7 +655,7 @@ bool _rfbEnqueue(const char* code, size_t length, unsigned char repeats = 1u) {
}
void _rfbSendRaw(const uint8_t* message, size_t size) {
- Serial.write(message, size);
+ _rfb_port->write(message, size);
}
void _rfbAckImpl() {
@@ -663,8 +664,8 @@ void _rfbAckImpl() {
};
DEBUG_MSG_P(PSTR("[RF] Sending ACK\n"));
- Serial.write(message, sizeof(message));
- Serial.flush();
+ _rfb_port->write(message, sizeof(message));
+ _rfb_port->flush();
}
void _rfbLearnImpl() {
@@ -673,16 +674,16 @@ void _rfbLearnImpl() {
};
DEBUG_MSG_P(PSTR("[RF] Sending LEARN\n"));
- Serial.write(message, sizeof(message));
- Serial.flush();
+ _rfb_port->write(message, sizeof(message));
+ _rfb_port->flush();
}
void _rfbSendImpl(const RfbMessage& message) {
- Serial.write(CodeStart);
- Serial.write(CodeSendBasic);
+ _rfb_port->write(CodeStart);
+ _rfb_port->write(CodeSendBasic);
_rfbSendRaw(message.code, sizeof(message.code));
- Serial.write(CodeEnd);
- Serial.flush();
+ _rfb_port->write(CodeEnd);
+ _rfb_port->flush();
}
void _rfbParse(uint8_t code, const std::vector& payload) {
@@ -759,8 +760,8 @@ static RfbParser _rfb_parser(_rfbParse);
void _rfbReceiveImpl() {
- while (Serial.available()) {
- auto c = Serial.read();
+ while (_rfb_port->available()) {
+ auto c = _rfb_port->read();
if (c < 0) {
continue;
}
@@ -1350,7 +1351,12 @@ void _rfbSettingsMigrate(int version) {
void rfbSetup() {
#if RFB_PROVIDER == RFB_PROVIDER_EFM8BB1
- Serial.begin(SERIAL_BAUDRATE);
+ const auto port = uartPort(RFB_PORT - 1);
+ if (!port || (!port.tx || !port.rx)) {
+ return;
+ }
+
+ _rfb_port = port->stream;
_rfb_parser.reserve(RfbParser::MessageSizeBasic);
#elif RFB_PROVIDER == RFB_PROVIDER_RCSWITCH
diff --git a/code/espurna/rtcmem.cpp b/code/espurna/rtcmem.cpp
index 6ab5b861..25d74066 100644
--- a/code/espurna/rtcmem.cpp
+++ b/code/espurna/rtcmem.cpp
@@ -19,9 +19,9 @@ volatile RtcmemData* Rtcmem = reinterpret_cast(RtcmemBegin
namespace espurna {
namespace peripherals {
-namespace rtc {
namespace {
+namespace rtc {
namespace internal {
bool status = false;
@@ -134,8 +134,9 @@ void setup() {
}
-} // namespace
} // namespace rtc
+
+} // namespace
} // namespace peripherals
} // namespace espurna
diff --git a/code/espurna/sensor.cpp b/code/espurna/sensor.cpp
index f6236c61..11640218 100644
--- a/code/espurna/sensor.cpp
+++ b/code/espurna/sensor.cpp
@@ -2066,8 +2066,14 @@ void load() {
#if CSE7766_SUPPORT
{
+ const auto port = uartPort(CSE7766_PORT - 1);
+ if (!port) {
+ return;
+ }
+
auto* sensor = new CSE7766Sensor();
- sensor->setRX(CSE7766_RX_PIN);
+ sensor->setPort(port->stream);
+
add(sensor);
}
#endif
@@ -2260,9 +2266,13 @@ void load() {
#if MHZ19_SUPPORT
{
+ const auto port = uartPort(MHZ19_PORT - 1);
+ if (!port) {
+ return;
+ }
+
auto* sensor = new MHZ19Sensor();
- sensor->setRX(MHZ19_RX_PIN);
- sensor->setTX(MHZ19_TX_PIN);
+ sensor->setPort(port->stream);
sensor->setCalibrateAuto(getSetting("mhz19CalibrateAuto", false));
add(sensor);
}
@@ -2306,21 +2316,26 @@ void load() {
#if PM1006_SUPPORT
{
+ const auto port = uartPort(PM1006_PORT - 1);
+ if (!port) {
+ return;
+ }
+
auto* sensor = new PM1006Sensor();
- sensor->setRX(PM1006_RX_PIN);
+ sensor->setPort(port->stream);
add(sensor);
}
#endif
#if PMSX003_SUPPORT
{
+ const auto port = uartPort(PMS_PORT - 1);
+ if (!port) {
+ return;
+ }
+
auto* sensor = new PMSX003Sensor();
-#if PMS_USE_SOFT
- sensor->setRX(PMS_RX_PIN);
- sensor->setTX(PMS_TX_PIN);
-#else
- sensor->setSerial(& PMS_HW_PORT);
-#endif
+ sensor->setPort(port->stream);
sensor->setType(PMS_TYPE);
add(sensor);
}
@@ -2340,24 +2355,14 @@ void load() {
#if PZEM004T_SUPPORT
{
- PZEM004TSensor::PortPtr port;
-
- auto rx = getSetting("pzemRX", PZEM004TSensor::RxPin);
- auto tx = getSetting("pzemTX", PZEM004TSensor::TxPin);
-
- if (getSetting("pzemSoft", PZEM004TSensor::useSoftwareSerial())) {
- port = PZEM004TSensor::makeSoftwarePort(rx, tx);
- } else {
- port = PZEM004TSensor::makeHardwarePort(
- PZEM004TSensor::defaultHardwarePort(), rx, tx);
- }
-
+ const auto port = uartPort(PZEM004T_PORT - 1);
if (!port) {
return;
}
- bool initialized { false };
+ auto serial = std::make_shared(port->stream);
+ bool initialized { false };
#if !defined(PZEM004T_ADDRESSES)
for (size_t index = 0; index < PZEM004TSensor::DevicesMax; ++index) {
auto address = getSetting({"pzemAddr", index}, PZEM004TSensor::defaultAddress(index));
@@ -2365,7 +2370,7 @@ void load() {
break;
}
- auto* ptr = PZEM004TSensor::make(port, address);
+ auto* ptr = PZEM004TSensor::make(serial, address);
if (ptr) {
add(ptr);
initialized = true;
@@ -2384,7 +2389,7 @@ void load() {
size_t device{0};
char* address{strtok(buffer, " ")};
while ((device < PZEM004TSensor::DevicesMax) && (address != nullptr)) {
- auto* ptr = PZEM004TSensor::make(port, address);
+ auto* ptr = PZEM004TSensor::make(serial, IPAddress(address));
if (ptr) {
add(ptr);
initialized = true;
@@ -2400,18 +2405,26 @@ void load() {
#if SENSEAIR_SUPPORT
{
+ const auto port = uartPort(SENSEAIR_PORT - 1);
+ if (!port) {
+ return;
+ }
+
auto* sensor = new SenseAirSensor();
- sensor->setRX(SENSEAIR_RX_PIN);
- sensor->setTX(SENSEAIR_TX_PIN);
+ sensor->setPort(port->stream);
add(sensor);
}
#endif
#if SDS011_SUPPORT
{
+ const auto port = uartPort(SDS011_PORT - 1);
+ if (!port) {
+ return;
+ }
+
auto* sensor = new SDS011Sensor();
- sensor->setRX(SDS011_RX_PIN);
- sensor->setTX(SDS011_TX_PIN);
+ sensor->setPort(port->stream);
add(sensor);
}
#endif
@@ -2434,17 +2447,26 @@ void load() {
#if SM300D2_SUPPORT
{
+ const auto port = uartPort(SM300D2_PORT - 1);
+ if (!port) {
+ return;
+ }
+
auto* sensor = new SM300D2Sensor();
- sensor->setRX(SM300D2_RX_PIN);
+ sensor->setPort(port->stream);
add(sensor);
}
#endif
#if T6613_SUPPORT
{
+ const auto port = uartPort(T6613_PORT - 1);
+ if (!port) {
+ return;
+ }
+
auto* sensor = new T6613Sensor();
- sensor->setRX(T6613_RX_PIN);
- sensor->setTX(T6613_TX_PIN);
+ sensor->setPort(port->stream);
add(sensor);
}
#endif
@@ -2459,9 +2481,13 @@ void load() {
#if V9261F_SUPPORT
{
+ const auto port = uartPort(V9261F_PORT - 1);
+ if (!port) {
+ return;
+ }
+
auto* sensor = new V9261FSensor();
- sensor->setRX(V9261F_PIN);
- sensor->setInverted(V9261F_PIN_INVERSE);
+ sensor->setPort(port->stream);
add(sensor);
}
#endif
@@ -2499,9 +2525,13 @@ void load() {
#if EZOPH_SUPPORT
{
+ const auto port = uartPort(EZOPH_PORT - 1);
+ if (!port) {
+ return;
+ }
+
auto* sensor = new EZOPHSensor();
- sensor->setRX(EZOPH_RX_PIN);
- sensor->setTX(EZOPH_TX_PIN);
+ sensor->setPort(port->stream);
add(sensor);
}
#endif
@@ -2532,31 +2562,18 @@ void load() {
#if PZEM004TV30_SUPPORT
{
- auto rx = getSetting("pzemv30RX", PZEM004TV30Sensor::RxPin);
- auto tx = getSetting("pzemv30TX", PZEM004TV30Sensor::TxPin);
-
- //TODO: getSetting("pzemv30*Cfg", (SW)SERIAL_8N1); ?
- //TODO: getSetting("serial*Cfg", ...); and attach index of the port ?
- //TODO: more than one sensor on port, like the v1
- PZEM004TV30Sensor::PortPtr port;
- if (getSetting("pzemSoft", PZEM004TV30Sensor::useSoftwareSerial())) {
- port = PZEM004TV30Sensor::makeSoftwarePort(rx, tx);
- } else {
- port = PZEM004TV30Sensor::makeHardwarePort(
- PZEM004TV30Sensor::defaultHardwarePort(), rx, tx);
+ const auto port = uartPort(PZEM004TV30_PORT - 1);
+ if (!port) {
+ return;
}
- if (port) {
- port->begin(PZEM004TV30Sensor::Baudrate);
+ auto* sensor = PZEM004TV30Sensor::make(port->stream,
+ getSetting("pzemv30Addr", PZEM004TV30Sensor::DefaultAddress),
+ getSetting("pzemv30ReadTimeout", PZEM004TV30Sensor::DefaultReadTimeout));
+ sensor->setDebug(
+ getSetting("pzemv30Debug", PZEM004TV30Sensor::DefaultDebug));
- auto* sensor = PZEM004TV30Sensor::make(std::move(port),
- getSetting("pzemv30Addr", PZEM004TV30Sensor::DefaultAddress),
- getSetting("pzemv30ReadTimeout", PZEM004TV30Sensor::DefaultReadTimeout));
- sensor->setDebug(
- getSetting("pzemv30Debug", PZEM004TV30Sensor::DefaultDebug));
-
- add(sensor);
- }
+ add(sensor);
}
#endif
}
diff --git a/code/espurna/sensors/CSE7766Sensor.h b/code/espurna/sensors/CSE7766Sensor.h
index dd99268c..2c856afe 100644
--- a/code/espurna/sensors/CSE7766Sensor.h
+++ b/code/espurna/sensors/CSE7766Sensor.h
@@ -8,11 +8,14 @@
#pragma once
+#define CSE7766_SYNC_INTERVAL 300 // Safe time between transmissions (ms)
+
+#define CSE7766_V1R 1.0 // 1mR current resistor
+#define CSE7766_V2R 1.0 // 1M voltage resistor
+
#include "BaseSensor.h"
#include "BaseEmonSensor.h"
-#include
-
class CSE7766Sensor : public BaseEmonSensor {
public:
@@ -49,30 +52,6 @@ class CSE7766Sensor : public BaseEmonSensor {
// ---------------------------------------------------------------------
- void setRX(unsigned char pin_rx) {
- if (_pin_rx == pin_rx) return;
- _pin_rx = pin_rx;
- _dirty = true;
- }
-
- void setInverted(bool inverted) {
- if (_inverted == inverted) return;
- _inverted = inverted;
- _dirty = true;
- }
-
- // ---------------------------------------------------------------------
-
- unsigned char getRX() const {
- return _pin_rx;
- }
-
- bool getInverted() const {
- return _inverted;
- }
-
- // ---------------------------------------------------------------------
-
double getRatio(unsigned char index) const override {
switch (index) {
case 0:
@@ -102,6 +81,13 @@ class CSE7766Sensor : public BaseEmonSensor {
}
}
+ // ---------------------------------------------------------------------
+
+ void setPort(Stream* port) {
+ _dirty = true;
+ _serial = port;
+ }
+
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@@ -113,22 +99,6 @@ class CSE7766Sensor : public BaseEmonSensor {
if (!_dirty) return;
- if (_serial) {
- _serial.reset(nullptr);
- }
-
- if (3 == _pin_rx) {
- Serial.begin(CSE7766_BAUDRATE);
- } else if (13 == _pin_rx) {
- Serial.begin(CSE7766_BAUDRATE);
- Serial.flush();
- Serial.swap();
- } else {
- _serial = std::make_unique(_pin_rx, -1, _inverted);
- _serial->enableIntTx(false);
- _serial->begin(CSE7766_BAUDRATE);
- }
-
_last_index_reset = TimeSource::now();
_ready = true;
@@ -138,19 +108,12 @@ class CSE7766Sensor : public BaseEmonSensor {
// Descriptive name of the sensor
String description() const override {
- if (_serial_is_hardware()) {
- return F("CSE7766 @ HwSerial");
- } else {
- char buffer[28];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("CSE7766 @ SwSerial(%u,NULL)"), _pin_rx);
- return String(buffer);
- }
+ return F("CSE7766");
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char) const override {
- return String(_pin_rx, 10);
+ return String(CSE7766_PORT, 10);
}
// Loop-like method, call it in your main loop
@@ -305,7 +268,7 @@ class CSE7766Sensor : public BaseEmonSensor {
_error = SENSOR_ERROR_OK;
- while (_serial_available()) {
+ while (_serial->available() > 0) {
// A 24 bytes message takes ~55ms to go through at 4800 bps
// Reset counter if more than 1000ms have passed since last byte.
@@ -315,7 +278,7 @@ class CSE7766Sensor : public BaseEmonSensor {
_last_index_reset = TimeSource::now();
- uint8_t byte = _serial_read();
+ uint8_t byte = _serial->read();
// first byte must be 0x55 or 0xF?
if (0 == _data_index) {
@@ -333,7 +296,7 @@ class CSE7766Sensor : public BaseEmonSensor {
_data[_data_index++] = byte;
if (_data_index > 23) {
- _serial_flush();
+ _serial->flush();
break;
}
@@ -349,39 +312,7 @@ class CSE7766Sensor : public BaseEmonSensor {
// ---------------------------------------------------------------------
- bool _serial_is_hardware() const {
- return (3 == _pin_rx) || (13 == _pin_rx);
- }
-
- bool _serial_available() const {
- if (_serial_is_hardware()) {
- return Serial.available();
- } else {
- return _serial->available();
- }
- }
-
- void _serial_flush() {
- if (_serial_is_hardware()) {
- return Serial.flush();
- } else {
- return _serial->flush();
- }
- }
-
- uint8_t _serial_read() const {
- if (_serial_is_hardware()) {
- return Serial.read();
- } else {
- return _serial->read();
- }
- }
-
- // ---------------------------------------------------------------------
-
- unsigned char _pin_rx = CSE7766_RX_PIN;
- bool _inverted = CSE7766_PIN_INVERSE;
- std::unique_ptr _serial;
+ Stream* _serial;
double _active = 0;
double _reactive = 0;
diff --git a/code/espurna/sensors/EZOPHSensor.h b/code/espurna/sensors/EZOPHSensor.h
index 1aba3a60..25bb3ef6 100644
--- a/code/espurna/sensors/EZOPHSensor.h
+++ b/code/espurna/sensors/EZOPHSensor.h
@@ -1,7 +1,6 @@
// -----------------------------------------------------------------------------
// EZO™ pH Circuit from Atlas Scientific
//
-// Uses SoftwareSerial library
// Copyright (C) 2018 by Rui Marinho
// -----------------------------------------------------------------------------
@@ -9,38 +8,17 @@
#pragma once
-#include
-
#include "BaseSensor.h"
class EZOPHSensor : public BaseSensor {
public:
- // ---------------------------------------------------------------------
-
- void setRX(unsigned char pin_rx) {
- if (_pin_rx == pin_rx) return;
- _pin_rx = pin_rx;
+ void setPort(Stream* port) {
+ _serial = port;
_dirty = true;
}
- void setTX(unsigned char pin_tx) {
- if (_pin_tx == pin_tx) return;
- _pin_tx = pin_tx;
- _dirty = true;
- }
-
- // ---------------------------------------------------------------------
-
- unsigned char getRX() {
- return _pin_rx;
- }
-
- unsigned char getTX() {
- return _pin_tx;
- }
-
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@@ -56,33 +34,18 @@ class EZOPHSensor : public BaseSensor {
// Initialization method, must be idempotent
void begin() override {
if (!_dirty) return;
-
- if (_serial) {
- _serial.reset(nullptr);
- }
-
- _serial = std::make_unique(_pin_rx, _pin_tx);
- _serial->enableIntTx(false);
- _serial->begin(9600);
-
_ready = true;
_dirty = false;
}
// Descriptive name of the sensor
String description() const override {
- char buffer[28];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("EZOPH @ SwSerial(%u,%u)"), _pin_rx, _pin_tx);
- return String(buffer);
+ return F("EZOPH");
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char index) const override {
- char buffer[8];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("%hhu:%hhu"), _pin_rx, _pin_tx);
- return String(buffer);
+ return String(EZOPH_PORT, 10);
}
// Type for slot # index
@@ -185,9 +148,7 @@ class EZOPHSensor : public BaseSensor {
TimeSource::time_point _timestamp;
double _ph = 0;
- unsigned char _pin_rx;
- unsigned char _pin_tx;
- std::unique_ptr _serial;
+ Stream* _serial;
};
diff --git a/code/espurna/sensors/MHZ19Sensor.h b/code/espurna/sensors/MHZ19Sensor.h
index a1f0da49..3aad1854 100644
--- a/code/espurna/sensors/MHZ19Sensor.h
+++ b/code/espurna/sensors/MHZ19Sensor.h
@@ -2,7 +2,6 @@
// MHZ19 CO2 sensor
// Based on: https://github.com/nara256/mhz19_uart
// http://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf
-// Uses SoftwareSerial library
// Copyright (C) 2017-2019 by Xose Pérez
// -----------------------------------------------------------------------------
@@ -10,8 +9,6 @@
#pragma once
-#include
-
#include "BaseSensor.h"
#include
@@ -45,28 +42,11 @@ class MHZ19Sensor : public BaseSensor {
public:
- void setRX(unsigned char pin_rx) {
- if (_pin_rx == pin_rx) return;
- _pin_rx = pin_rx;
+ void setPort(Stream* port) {
+ _serial = port;
_dirty = true;
}
- void setTX(unsigned char pin_tx) {
- if (_pin_tx == pin_tx) return;
- _pin_tx = pin_tx;
- _dirty = true;
- }
-
- // ---------------------------------------------------------------------
-
- unsigned char getRX() const {
- return _pin_rx;
- }
-
- unsigned char getTX() const {
- return _pin_tx;
- }
-
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@@ -81,37 +61,21 @@ class MHZ19Sensor : public BaseSensor {
// Initialization method, must be idempotent
void begin() override {
-
if (!_dirty) return;
-
- if (_serial) {
- _serial.reset(nullptr);
- }
-
- _serial = std::make_unique(_pin_rx, _pin_tx, false);
- _serial->enableIntTx(false);
- _serial->begin(9600);
calibrateAuto(_calibrateAuto);
_ready = true;
_dirty = false;
-
}
// Descriptive name of the sensor
String description() const override {
- char buffer[28];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("MHZ19 @ SwSerial(%hhu,%hhu)"), _pin_rx, _pin_tx);
- return String(buffer);
+ return F("MHZ19");
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char) const override {
- char buffer[8];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("%hhu:%hhu"), _pin_rx, _pin_tx);
- return String(buffer);
+ return String(MHZ19_PORT, 10);
}
// Type for slot # index
@@ -225,10 +189,8 @@ class MHZ19Sensor : public BaseSensor {
}
double _co2 = 0;
- unsigned char _pin_rx;
- unsigned char _pin_tx;
bool _calibrateAuto = false;
- std::unique_ptr _serial;
+ Stream* _serial { nullptr };
};
diff --git a/code/espurna/sensors/PM1006Sensor.h b/code/espurna/sensors/PM1006Sensor.h
index 07433df1..58eb048e 100644
--- a/code/espurna/sensors/PM1006Sensor.h
+++ b/code/espurna/sensors/PM1006Sensor.h
@@ -1,7 +1,6 @@
// -----------------------------------------------------------------------------
// Wuhan Cubic PM1006
// Used in the IKEA VINDRIKTNING Air Quality Sensor
-// Uses SoftwareSerial library
// Copyright (C) 2022 by Xose Pérez
// -----------------------------------------------------------------------------
@@ -9,24 +8,17 @@
#pragma once
-#include
-
#include "BaseSensor.h"
class PM1006Sensor : public BaseSensor {
public:
- void setRX(unsigned char pin_rx) {
- if (_pin_rx == pin_rx) return;
- _pin_rx = pin_rx;
+ void setPort(Stream* port) {
+ _serial = port;
_dirty = true;
}
- unsigned char getRX() const {
- return _pin_rx;
- }
-
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@@ -41,47 +33,20 @@ class PM1006Sensor : public BaseSensor {
// Initialization method, must be idempotent
void begin() override {
-
if (!_dirty) return;
- if (_serial) {
- _serial.reset(nullptr);
- }
-
- if (3 == _pin_rx) {
- Serial.begin(PM1006_BAUDRATE);
- } else if (13 == _pin_rx) {
- Serial.begin(PM1006_BAUDRATE);
- Serial.flush();
- Serial.swap();
- } else {
- _serial = std::make_unique(_pin_rx, -1, false);
- _serial->enableIntTx(false);
- _serial->begin(PM1006_BAUDRATE);
- }
-
_ready = true;
_dirty = false;
-
}
// Descriptive name of the sensor
String description() const override {
- if (_serial_is_hardware()) {
- return F("PM1006 @ HwSerial");
- }
-
- char buffer[32];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("PM1006 @ SwSerial(%hhu,NULL)"), _pin_rx);
- return String(buffer);
+ return F("PM1006");
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char index) const override {
- char buffer[4];
- snprintf_P(buffer, sizeof(buffer), PSTR("%hhu"), _pin_rx);
- return String(buffer);
+ return String(PM1006_PORT, 10);
}
// Type for slot # index
@@ -107,36 +72,6 @@ class PM1006Sensor : public BaseSensor {
// Protected
// ---------------------------------------------------------------------
- bool _serial_is_hardware() const {
- return (3 == _pin_rx) || (13 == _pin_rx);
- }
-
- bool _serial_available() const {
- if (_serial_is_hardware()) {
- return Serial.available();
- } else {
- return _serial->available();
- }
- }
-
- void _serial_flush() {
- if (_serial_is_hardware()) {
- return Serial.flush();
- } else {
- return _serial->flush();
- }
- }
-
- uint8_t _serial_read() {
- if (_serial_is_hardware()) {
- return Serial.read();
- } else {
- return _serial->read();
- }
- }
-
- // ---------------------------------------------------------------------
-
void _parse() {
#if SENSOR_DEBUG
DEBUG_MSG_P(PSTR("[SENSOR] PM1006: %s\n"), hexEncode(_buffer).c_str());
@@ -167,9 +102,9 @@ class PM1006Sensor : public BaseSensor {
void _read() {
- while(_serial_available()) {
+ while(_serial->available()) {
- unsigned char ch = _serial_read();
+ unsigned char ch = _serial->read();
if ((_position > 0) || (ch == 0x16)) {
_buffer[_position] = ch;
_position++;
@@ -191,9 +126,7 @@ class PM1006Sensor : public BaseSensor {
unsigned char _position = 0;
double _pm25 = 0;
-
- unsigned char _pin_rx = PM1006_RX_PIN;
- std::unique_ptr _serial;
+ Stream* _serial { nullptr };
};
diff --git a/code/espurna/sensors/PMSX003Sensor.h b/code/espurna/sensors/PMSX003Sensor.h
index e5784159..9cae350d 100644
--- a/code/espurna/sensors/PMSX003Sensor.h
+++ b/code/espurna/sensors/PMSX003Sensor.h
@@ -1,6 +1,5 @@
// -----------------------------------------------------------------------------
// PMS Dust Sensor
-// Uses SoftwareSerial library
// Contribution by Òscar Rovira López
// Refine to support PMS5003T/PMS5003ST by Yonsm Guo
// -----------------------------------------------------------------------------
@@ -11,11 +10,6 @@
#include "BaseSensor.h"
-#include
-
-// Generic data
-#define PMS_BAUD_RATE 9600
-
// Type of sensor
#define PMS_TYPE_X003 0
#define PMS_TYPE_X003_9 1
@@ -163,26 +157,8 @@ class PMSX003Sensor : public BaseSensor, PMSX003 {
};
public:
-
- ~PMSX003Sensor() {
- removeSerial();
- }
-
- void setRX(unsigned char pin_rx) {
- if (_pin_rx == pin_rx) return;
- _pin_rx = pin_rx;
- _dirty = true;
- }
-
- void setTX(unsigned char pin_tx) {
- if (_pin_tx == pin_tx) return;
- _pin_tx = pin_tx;
- _dirty = true;
- }
-
- void setSerial(HardwareSerial * serial) {
- _soft = false;
- _serial = serial;
+ void setPort(Stream* port) {
+ _serial = port;
_dirty = true;
}
@@ -191,16 +167,6 @@ class PMSX003Sensor : public BaseSensor, PMSX003 {
_type = type;
}
- // ---------------------------------------------------------------------
-
- unsigned char getRX() {
- return _pin_rx;
- }
-
- unsigned char getTX() {
- return _pin_tx;
- }
-
unsigned char getType() {
return _type;
}
@@ -219,21 +185,8 @@ class PMSX003Sensor : public BaseSensor, PMSX003 {
// Initialization method, must be idempotent
void begin() override {
-
if (!_dirty) return;
- if (_soft) {
- if (_serial) removeSerial();
- _serial = new SoftwareSerial(_pin_rx, _pin_tx, false);
- static_cast(_serial)->enableIntTx(false);
- }
-
- if (_soft) {
- static_cast(_serial)->begin(PMS_BAUD_RATE);
- } else {
- static_cast(_serial)->begin(PMS_BAUD_RATE);
- }
-
passiveMode();
_startTime = TimeSource::now();
@@ -244,40 +197,21 @@ class PMSX003Sensor : public BaseSensor, PMSX003 {
// Descriptive name of the sensor
String description() const override {
- char buffer[28];
- if (_soft) {
- snprintf_P(buffer, sizeof(buffer),
- PSTR("%s @ SwSerial(%u,%u)"),
- Specs[_type].name, _pin_rx, _pin_tx);
- } else {
- snprintf_P(buffer, sizeof(buffer),
- PSTR("%s @ HwSerial"), Specs[_type].name);
- }
-
- return String(buffer);
+ return String(Specs[_type].name);
}
// Descriptive name of the slot # index
String description(unsigned char index) const override {
- char buffer[36] = {0};
- if (_soft) {
- snprintf_P(buffer, sizeof(buffer),
- PSTR("%d @ %s @ SwSerial(%u,%u)"),
- int(index + 1), Specs[_type].name, _pin_rx, _pin_tx);
- } else {
- snprintf_P(buffer, sizeof(buffer),
- PSTR("%d @ %s @ HwSerial"),
- int(index + 1), Specs[_type].name);
- }
- return String(buffer);
+ String out;
+ out += String(index + 1, 10);
+ out += " @ ";
+ out += description();
+ return out;
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char index) const override {
- char buffer[8];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("%hhu:%hhu"), _pin_rx, _pin_tx);
- return String(buffer);
+ return String(PMS_PORT, 10);
}
// Type for slot # index
@@ -381,18 +315,7 @@ class PMSX003Sensor : public BaseSensor, PMSX003 {
return _slot_values[index];
}
- private:
- void removeSerial() {
- if (_serial && _soft) {
- delete static_cast(_serial);
- }
- }
-
protected:
- bool _soft = true;
- unsigned char _pin_rx;
- unsigned char _pin_tx;
-
using TimeSource = espurna::time::CoreClock;
TimeSource::time_point _startTime;
bool _warmedUp = false;
diff --git a/code/espurna/sensors/PZEM004TSensor.h b/code/espurna/sensors/PZEM004TSensor.h
index 9a24b44d..9c3d4c7d 100644
--- a/code/espurna/sensors/PZEM004TSensor.h
+++ b/code/espurna/sensors/PZEM004TSensor.h
@@ -44,17 +44,211 @@
// ----------
// UART/TTL-Serial network with single master and multiple slaves:
// http://cool-emerald.blogspot.com/2009/10/multidrop-network-for-rs232.html
+//
+// Original code:
+// --------------
+// * https://github.com/olehs/PZEM004T
+//
+// MIT License
+//
+// Copyright (c) 2018 Oleg Sokolov
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
#pragma once
-#include
-
#include "BaseSensor.h"
#include "BaseEmonSensor.h"
#include "../sensor.h"
#include "../terminal.h"
+#define PZEM_VOLTAGE (uint8_t)0xB0
+#define RESP_VOLTAGE (uint8_t)0xA0
+
+#define PZEM_CURRENT (uint8_t)0xB1
+#define RESP_CURRENT (uint8_t)0xA1
+
+#define PZEM_POWER (uint8_t)0xB2
+#define RESP_POWER (uint8_t)0xA2
+
+#define PZEM_ENERGY (uint8_t)0xB3
+#define RESP_ENERGY (uint8_t)0xA3
+
+#define PZEM_SET_ADDRESS (uint8_t)0xB4
+#define RESP_SET_ADDRESS (uint8_t)0xA4
+
+#define PZEM_POWER_ALARM (uint8_t)0xB5
+#define RESP_POWER_ALARM (uint8_t)0xA5
+
+#define PZEM_DEFAULT_READ_TIMEOUT 1000
+#define PZEM_ERROR_VALUE -1.0
+
+#define RESPONSE_SIZE sizeof(PZEMCommand)
+#define RESPONSE_DATA_SIZE RESPONSE_SIZE - 2
+
+struct PZEMCommand {
+ uint8_t command;
+ uint8_t addr[4];
+ uint8_t data;
+ uint8_t crc;
+};
+
+class PZEM004T {
+public:
+ PZEM004T() = delete;
+ explicit PZEM004T(Stream *port) :
+ _serial(port)
+ {}
+
+ void setReadTimeout(unsigned long msec) {
+ _readTimeOut = msec;
+ }
+
+ unsigned long readTimeout() {return _readTimeOut;}
+
+ float voltage(const IPAddress &addr);
+ float current(const IPAddress &addr);
+ float power(const IPAddress &addr);
+ float energy(const IPAddress &addr);
+
+ bool setAddress(const IPAddress &newAddr);
+ bool setPowerAlarm(const IPAddress &addr, uint8_t threshold);
+
+private:
+ Stream* _serial;
+ unsigned long _readTimeOut = PZEM_DEFAULT_READ_TIMEOUT;
+
+ void send(const IPAddress &addr, uint8_t cmd, uint8_t data = 0);
+ bool recieve(uint8_t resp, uint8_t *data = 0);
+
+ uint8_t crc(uint8_t *data, uint8_t sz);
+};
+
+float PZEM004T::voltage(const IPAddress &addr)
+{
+ uint8_t data[RESPONSE_DATA_SIZE];
+
+ send(addr, PZEM_VOLTAGE);
+ if(!recieve(RESP_VOLTAGE, data))
+ return PZEM_ERROR_VALUE;
+
+ return (data[0] << 8) + data[1] + (data[2] / 10.0);
+}
+
+float PZEM004T::current(const IPAddress &addr)
+{
+ uint8_t data[RESPONSE_DATA_SIZE];
+
+ send(addr, PZEM_CURRENT);
+ if(!recieve(RESP_CURRENT, data))
+ return PZEM_ERROR_VALUE;
+
+ return (data[0] << 8) + data[1] + (data[2] / 100.0);
+}
+
+float PZEM004T::power(const IPAddress &addr)
+{
+ uint8_t data[RESPONSE_DATA_SIZE];
+
+ send(addr, PZEM_POWER);
+ if(!recieve(RESP_POWER, data))
+ return PZEM_ERROR_VALUE;
+
+ return (data[0] << 8) + data[1];
+}
+
+float PZEM004T::energy(const IPAddress &addr)
+{
+ uint8_t data[RESPONSE_DATA_SIZE];
+
+ send(addr, PZEM_ENERGY);
+ if(!recieve(RESP_ENERGY, data))
+ return PZEM_ERROR_VALUE;
+
+ return ((uint32_t)data[0] << 16) + ((uint16_t)data[1] << 8) + data[2];
+}
+
+bool PZEM004T::setAddress(const IPAddress &newAddr)
+{
+ send(newAddr, PZEM_SET_ADDRESS);
+ return recieve(RESP_SET_ADDRESS);
+}
+
+bool PZEM004T::setPowerAlarm(const IPAddress &addr, uint8_t threshold)
+{
+ send(addr, PZEM_POWER_ALARM, threshold);
+ return recieve(RESP_POWER_ALARM);
+}
+
+void PZEM004T::send(const IPAddress &addr, uint8_t cmd, uint8_t data)
+{
+ PZEMCommand pzem;
+
+ pzem.command = cmd;
+ for(size_t i=0; iavailable()) {
+ _serial->read();
+ }
+
+ _serial->write(bytes, sizeof(pzem));
+}
+
+bool PZEM004T::recieve(uint8_t resp, uint8_t *data)
+{
+ uint8_t buffer[RESPONSE_SIZE];
+
+ unsigned long startTime = millis();
+ uint8_t len = 0;
+ while((len < RESPONSE_SIZE) && (millis() - startTime < _readTimeOut))
+ {
+ if (_serial->available() > 0)
+ {
+ uint8_t c = (uint8_t)_serial->read();
+ if(!c && !len)
+ continue; // skip 0 at startup
+ buffer[len++] = c;
+ }
+ yield(); // do background netw tasks while blocked for IO (prevents ESP watchdog trigger)
+ }
+
+ if(len != RESPONSE_SIZE)
+ return false;
+
+ if(buffer[6] != crc(buffer, len - 1))
+ return false;
+
+ if(buffer[0] != resp)
+ return false;
+
+ if(data)
+ {
+ for(size_t i=0; i;
- virtual const char* tag() const = 0;
- virtual void flush() = 0;
-
SerialPort() = delete;
- SerialPort(PzemPtr pzem, unsigned char rx, unsigned char tx) :
- _pzem(std::move(pzem)),
- _rx(rx),
- _tx(tx)
+
+ explicit SerialPort(PzemPtr pzem) :
+ _pzem(pzem)
+ {}
+
+ explicit SerialPort(Stream* stream) :
+ _pzem(std::make_unique(stream))
{}
float read(const IPAddress& address, unsigned char magnitude) {
@@ -192,79 +370,17 @@ public:
bool address(const IPAddress& address) {
return _pzem->setAddress(address);
- }
-
- unsigned char rx() const {
- return _rx;
- }
-
- unsigned char tx() const {
- return _tx;
+ send(newAddr, PZEM_SET_ADDRESS);
+ return recieve(RESP_SET_ADDRESS);
}
private:
PzemPtr _pzem;
bool _busy { false };
- unsigned char _rx;
- unsigned char _tx;
- };
-
- struct SoftwareSerialPort : public SerialPort {
- SoftwareSerialPort() = delete;
- SoftwareSerialPort(unsigned char rx, unsigned char tx) :
- SerialPort(std::make_unique(rx, tx), rx, tx)
- {}
-
- const char* tag() const override {
- return "Sw";
- }
-
- void flush() override {
- }
- };
-
- struct HardwareSerialPort : public SerialPort {
- HardwareSerialPort() = delete;
- HardwareSerialPort(HardwareSerial* serial, unsigned char rx, unsigned char tx) :
- SerialPort(std::make_unique(serial), rx, tx),
- _serial(serial)
- {
- if ((rx == 13) && (tx == 15)) {
- _serial->flush();
- _serial->swap();
- }
- }
-
- const char* tag() const override {
- return "Hw";
- }
-
- void flush() override {
- // Clear buffer in case of late response (Timeout)
- // This we cannot do it from outside the library
- while (_serial->available() > 0) {
- _serial->read();
- }
- }
-
- private:
- HardwareSerial* _serial;
};
using PortPtr = std::shared_ptr;
- static PortPtr makeHardwarePort(HardwareSerial* port, unsigned char rx, unsigned char tx) {
- auto ptr = std::make_shared(port, rx, tx);
- _ports.push_back(ptr);
- return ptr;
- }
-
- static PortPtr makeSoftwarePort(unsigned char rx, unsigned char tx) {
- auto ptr = std::make_shared(rx, tx);
- _ports.push_back(ptr);
- return ptr;
- }
-
private:
PZEM004TSensor(PortPtr port, IPAddress address) :
BaseEmonSensor(Magnitudes),
@@ -368,11 +484,7 @@ public:
// Descriptive name of the sensor
String description() const override {
- char buffer[32];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("PZEM004T @ %sSerial(%hhu,%hhu)"),
- _port->tag(), _port->rx(), _port->tx());
- return String(buffer);
+ return F("PZEM004T");
}
// Descriptive name of the slot # index
@@ -514,8 +626,8 @@ void pzem_ports(::terminal::CommandContext&& ctx) {
auto print = [&](const size_t index, const PortWeakPtr& ptr) {
auto port = ptr.lock();
if (port) {
- ctx.output.printf_P(PSTR("%u -> %sSerial (%hhu,%hhu)\n"),
- index, port->tag(), port->rx(), port->tx());
+ ctx.output.printf_P(PSTR("%u -> (%p)\n"),
+ index, port.get());
} else {
ctx.output.print(F("%u -> (not configured)\n"));
}
diff --git a/code/espurna/sensors/PZEM004TV30Sensor.h b/code/espurna/sensors/PZEM004TV30Sensor.h
index 345cfc41..f4e48722 100644
--- a/code/espurna/sensors/PZEM004TV30Sensor.h
+++ b/code/espurna/sensors/PZEM004TV30Sensor.h
@@ -19,9 +19,6 @@ Copyright (C) 2020 by Maxim Prokhorov
#include
#include
-// TODO: keep this until we have external API giving us swserial stream objects
-#include
-
#if DEBUG_SUPPORT
#define PZEM_DEBUG_MSG_P(...) do { if (_debug) {\
DEBUG_MSG_P(__VA_ARGS__); }\
@@ -33,114 +30,16 @@ Copyright (C) 2020 by Maxim Prokhorov
class PZEM004TV30Sensor : public BaseEmonSensor {
public:
using TimeSource = espurna::time::CoreClock;
-
- static constexpr unsigned char RxPin { PZEM004TV30_RX_PIN };
- static constexpr unsigned char TxPin { PZEM004TV30_TX_PIN };
-
- static constexpr bool useSoftwareSerial() {
- return 1 == PZEM004TV30_USE_SOFT;
- }
-
- static HardwareSerial* defaultHardwarePort() {
- return &PZEM004TV30_HW_PORT;
- }
-
- static constexpr unsigned long Baudrate = 9600u;
-
- struct SerialPort {
- virtual const char* tag() const = 0;
- virtual void begin(unsigned long baudrate) = 0;
- virtual Stream* operator->() = 0;
-
- SerialPort() = delete;
- SerialPort(unsigned char rx, unsigned char tx) :
- _rx(rx),
- _tx(tx)
- {}
-
- unsigned char rx() const {
- return _rx;
- }
-
- unsigned char tx() const {
- return _tx;
- }
-
- private:
- unsigned char _rx;
- unsigned char _tx;
- };
-
- struct SoftwarePort : public SerialPort {
- SoftwarePort() = delete;
- SoftwarePort(unsigned char rx, unsigned char tx) :
- SerialPort(rx, tx),
- _serial(std::make_unique(rx, tx))
- {}
-
- const char* tag() const override {
- return "Sw";
- }
-
- void begin(unsigned long baudrate) override {
- _serial->begin(baudrate);
- }
-
- Stream* operator->() override {
- return static_cast(_serial.get());
- }
-
- private:
- std::unique_ptr _serial;
- };
-
- struct HardwarePort : public SerialPort {
- HardwarePort() = delete;
- HardwarePort(HardwareSerial* serial, unsigned char rx, unsigned char tx) :
- SerialPort(rx, tx),
- _serial(serial)
- {
- if ((rx == 13) && (tx == 15)) {
- _serial->flush();
- _serial->swap();
- }
- }
-
- void begin(unsigned long baudrate) override {
- _serial->begin(baudrate);
- }
-
- const char* tag() const override {
- return "Hw";
- }
-
- Stream* operator->() override {
- return static_cast(_serial);
- }
-
- private:
- HardwareSerial* _serial;
- };
-
- using PortPtr = std::unique_ptr;
using Instance = std::unique_ptr;
- static PortPtr makeHardwarePort(HardwareSerial* port, unsigned char rx, unsigned char tx) {
- return std::make_unique(port, rx, tx);
- }
-
- static PortPtr makeSoftwarePort(unsigned char rx, unsigned char tx) {
- return std::make_unique(rx, tx);
- }
-
// Note that the device (aka slave) address needs be changed first via
// - some external tool. For example, using USB2TTL adapter and a PC app
// - `pzem.address` with **only** one device on the line
// (because we would change all 0xf8-addressed devices at the same time)
- static PZEM004TV30Sensor* make(PortPtr port, uint8_t address, TimeSource::duration timeout) {
+ static PZEM004TV30Sensor* make(Stream* port, uint8_t address, TimeSource::duration timeout) {
static_assert(std::is_same::value, "");
if (!_instance) {
- _instance.reset(new PZEM004TV30Sensor(std::move(port), address, timeout));
+ _instance.reset(new PZEM004TV30Sensor(port, address, timeout));
return _instance.get();
}
@@ -308,7 +207,7 @@ public:
return;
}
- (*_port)->write(builder.buffer.data(), builder.size);
+ _port->write(builder.buffer.data(), builder.size);
size_t expect = modbusExpect(builder);
if (!expect) {
@@ -329,7 +228,7 @@ public:
// TODO: testing is much easier, b/c we can just grab any modbus simulator and set up multiple devices
const auto ts = TimeSource::now();
while ((bytes < expect) && (TimeSource::now() - ts < _read_timeout)) {
- int c = (*_port)->read();
+ int c = _port->read();
if (c < 0) {
continue;
}
@@ -574,7 +473,7 @@ public:
}
void flush() {
- while ((*_port)->read() >= 0) {
+ while (_port->read() >= 0) {
}
}
@@ -616,11 +515,8 @@ public:
}
String description() const override {
- static const String base(F("PZEM004T V3.0"));
- return base + " @ "
- + _port->tag()
- + F("Serial, 0x")
- + String(_address, 16);
+ static const String base(F("PZEM004TV30"));
+ return base + " @ 0x" + String(_address, 16);
}
String address(unsigned char) const override {
@@ -675,16 +571,16 @@ public:
private:
PZEM004TV30Sensor() = delete;
- PZEM004TV30Sensor(PortPtr port, uint8_t address, TimeSource::duration timeout) :
+ PZEM004TV30Sensor(Stream* port, uint8_t address, TimeSource::duration timeout) :
BaseEmonSensor(Magnitudes),
- _port(std::move(port)),
+ _port(port),
_address(address),
_read_timeout(timeout)
{}
static Instance _instance;
- PortPtr _port;
+ Stream* _port { nullptr };
uint8_t _address { DefaultAddress };
TimeSource::duration _read_timeout { DefaultReadTimeout };
diff --git a/code/espurna/sensors/SDS011Sensor.h b/code/espurna/sensors/SDS011Sensor.h
index a4278b0b..500d5310 100644
--- a/code/espurna/sensors/SDS011Sensor.h
+++ b/code/espurna/sensors/SDS011Sensor.h
@@ -2,7 +2,6 @@
// SDS011 dust sensor
// Based on: https://github.com/ricki-z/SDS011
//
-// Uses SoftwareSerial library
// Copyright (C) 2018 by Lucas Pleß
// -----------------------------------------------------------------------------
@@ -10,37 +9,15 @@
#pragma once
-#include
-
#include "BaseSensor.h"
-
class SDS011Sensor : public BaseSensor {
-
public:
-
- void setRX(unsigned char pin_rx) {
- if (_pin_rx == pin_rx) return;
- _pin_rx = pin_rx;
+ void setPort(Stream* port) {
+ _serial = port;
_dirty = true;
}
- void setTX(unsigned char pin_tx) {
- if (_pin_tx == pin_tx) return;
- _pin_tx = pin_tx;
- _dirty = true;
- }
-
- // ---------------------------------------------------------------------
-
- unsigned char getRX() const {
- return _pin_rx;
- }
-
- unsigned char getTX() const {
- return _pin_tx;
- }
-
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@@ -55,34 +32,19 @@ class SDS011Sensor : public BaseSensor {
// Initialization method, must be idempotent
void begin() override {
-
if (!_dirty) return;
-
- if (_serial) {
- _serial.reset(nullptr);
- }
-
- _serial = std::make_unique(_pin_rx, _pin_tx);
- _serial->begin(9600);
-
_ready = true;
_dirty = false;
}
// Descriptive name of the sensor
String description() const override {
- char buffer[32];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("SDS011 @ SwSerial(%hhu,%hhu)"), _pin_rx, _pin_tx);
- return String(buffer);
+ return F("SDS011");
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char) const override {
- char buffer[8];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("%hhu:%hhu"), _pin_rx, _pin_tx);
- return String(buffer);
+ return String(SDS011_PORT, 10);
}
// Type for slot # index
@@ -103,8 +65,6 @@ class SDS011Sensor : public BaseSensor {
return 0;
}
-
-
protected:
// ---------------------------------------------------------------------
@@ -156,10 +116,7 @@ class SDS011Sensor : public BaseSensor {
double _p2dot5 = 0;
double _p10 = 0;
- unsigned char _pin_rx;
- unsigned char _pin_tx;
- std::unique_ptr _serial;
-
+ Stream* _serial;
};
#endif // SENSOR_SUPPORT && SDS011_SUPPORT
diff --git a/code/espurna/sensors/SM300D2Sensor.h b/code/espurna/sensors/SM300D2Sensor.h
index 3ba0ad92..a9025655 100644
--- a/code/espurna/sensors/SM300D2Sensor.h
+++ b/code/espurna/sensors/SM300D2Sensor.h
@@ -1,7 +1,6 @@
// -----------------------------------------------------------------------------
// SmartMeasure SM300D2-VO2
// https://es.aliexpress.com/item/32984571140.html
-// Uses SoftwareSerial library
// Copyright (C) 2021 by Xose Pérez
// -----------------------------------------------------------------------------
@@ -9,24 +8,17 @@
#pragma once
-#include
-
#include "BaseSensor.h"
class SM300D2Sensor : public BaseSensor {
public:
- void setRX(unsigned char pin_rx) {
- if (_pin_rx == pin_rx) return;
- _pin_rx = pin_rx;
+ void setPort(Stream* port) {
+ _serial = port;
_dirty = true;
}
- unsigned char getRX() const {
- return _pin_rx;
- }
-
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@@ -41,47 +33,20 @@ class SM300D2Sensor : public BaseSensor {
// Initialization method, must be idempotent
void begin() override {
-
if (!_dirty) return;
- if (_serial) {
- _serial.reset(nullptr);
- }
-
- if (3 == _pin_rx) {
- Serial.begin(SM300D2_BAUDRATE);
- } else if (13 == _pin_rx) {
- Serial.begin(SM300D2_BAUDRATE);
- Serial.flush();
- Serial.swap();
- } else {
- _serial = std::make_unique(_pin_rx, -1, false);
- _serial->enableIntTx(false);
- _serial->begin(SM300D2_BAUDRATE);
- }
-
_ready = true;
_dirty = false;
-
}
// Descriptive name of the sensor
String description() const override {
- if (_serial_is_hardware()) {
- return F("SM300D2 @ HwSerial");
- }
-
- char buffer[28];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("SM300D2 @ SwSerial(%hhu,NULL)"), _pin_rx);
- return String(buffer);
+ return F("SM300D2");
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char index) const override {
- char buffer[4];
- snprintf_P(buffer, sizeof(buffer), PSTR("%hhu"), _pin_rx);
- return String(buffer);
+ return String(SM300D2_PORT, 10);
}
// Type for slot # index
@@ -119,36 +84,6 @@ class SM300D2Sensor : public BaseSensor {
// Protected
// ---------------------------------------------------------------------
- bool _serial_is_hardware() const {
- return (3 == _pin_rx) || (13 == _pin_rx);
- }
-
- bool _serial_available() const {
- if (_serial_is_hardware()) {
- return Serial.available();
- } else {
- return _serial->available();
- }
- }
-
- void _serial_flush() {
- if (_serial_is_hardware()) {
- return Serial.flush();
- } else {
- return _serial->flush();
- }
- }
-
- uint8_t _serial_read() const {
- if (_serial_is_hardware()) {
- return Serial.read();
- } else {
- return _serial->read();
- }
- }
-
- // ---------------------------------------------------------------------
-
void _parse() {
#if SENSOR_DEBUG
@@ -201,9 +136,9 @@ class SM300D2Sensor : public BaseSensor {
void _read() {
- while(_serial_available()) {
+ while(_serial->available()) {
- unsigned char ch = _serial_read();
+ unsigned char ch = _serial->read();
if ((_position > 0) || (ch == 0x3C)) {
_buffer[_position] = ch;
_position++;
@@ -231,8 +166,7 @@ class SM300D2Sensor : public BaseSensor {
double _temperature = 0;
double _humidity = 0;
- unsigned char _pin_rx = SM300D2_RX_PIN;
- std::unique_ptr _serial;
+ Stream* _serial { nullptr };
};
diff --git a/code/espurna/sensors/SenseAirSensor.h b/code/espurna/sensors/SenseAirSensor.h
index d0dfc30f..833c6bf6 100644
--- a/code/espurna/sensors/SenseAirSensor.h
+++ b/code/espurna/sensors/SenseAirSensor.h
@@ -1,6 +1,5 @@
// -----------------------------------------------------------------------------
// SenseAir S8 CO2 Sensor
-// Uses SoftwareSerial library
// Contribution by Yonsm Guo
// -----------------------------------------------------------------------------
@@ -8,11 +7,8 @@
#pragma once
-#include
-
#include "BaseSensor.h"
-
// SenseAir sensor utils. Notice that we only read a single register.
// 0xFE is the address aka "Any sensor"
class SenseAir {
@@ -127,28 +123,11 @@ private:
class SenseAirSensor : public BaseSensor, SenseAir {
public:
- void setRX(unsigned char pin_rx) {
- if (_pin_rx == pin_rx) return;
- _pin_rx = pin_rx;
+ void setPort(Stream* port) {
+ _serial = port;
_dirty = true;
}
- void setTX(unsigned char pin_tx) {
- if (_pin_tx == pin_tx) return;
- _pin_tx = pin_tx;
- _dirty = true;
- }
-
- // ---------------------------------------------------------------------
-
- unsigned char getRX() const {
- return _pin_rx;
- }
-
- unsigned char getTX() const {
- return _pin_tx;
- }
-
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@@ -163,19 +142,8 @@ class SenseAirSensor : public BaseSensor, SenseAir {
// Initialization method, must be idempotent
void begin() override {
-
if (!_dirty) return;
- if (_serial) {
- _serial.reset(nullptr);
- }
-
- _serial = std::make_unique(_pin_rx, _pin_tx, false);
- _serial->enableIntTx(false);
- _serial->begin(9600);
- _serial->enableRx(true);
- _serial->setTimeout(200);
-
_startTime = TimeSource::now();
_warmedUp = false;
@@ -185,18 +153,12 @@ class SenseAirSensor : public BaseSensor, SenseAir {
// Descriptive name of the sensor
String description() const override {
- char buffer[28];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("SenseAir S8 @ SwSerial(%hhu,%hhu)"), _pin_rx, _pin_tx);
- return String(buffer);
+ return F("SenseAir");
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char) const override {
- char buffer[8];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("%hhu:%hhu"), _pin_rx, _pin_tx);
- return String(buffer);
+ return String(SENSEAIR_PORT, 10);
}
// Type for slot # index
@@ -249,16 +211,12 @@ class SenseAirSensor : public BaseSensor, SenseAir {
}
private:
-
- std::unique_ptr _serial;
+ Stream* _serial;
using TimeSource = espurna::time::CoreClock;
TimeSource::time_point _startTime;
bool _warmedUp = false;
- unsigned char _pin_rx;
- unsigned char _pin_tx;
-
int16_t _co2 = 0;
int16_t _lastCo2 = 0;
};
diff --git a/code/espurna/sensors/T6613Sensor.h b/code/espurna/sensors/T6613Sensor.h
index 08ae081a..35823b63 100644
--- a/code/espurna/sensors/T6613Sensor.h
+++ b/code/espurna/sensors/T6613Sensor.h
@@ -1,7 +1,6 @@
// -----------------------------------------------------------------------------
// T6613 CO2 sensor
// https://www.amphenol-sensors.com/en/telaire/co2/525-co2-sensor-modules/321-t6613
-// Uses SoftwareSerial library
// Copyright (C) 2017-2019 by Xose Pérez
// -----------------------------------------------------------------------------
@@ -9,11 +8,8 @@
#pragma once
-#include
-
#include "BaseSensor.h"
-
class T6613Sensor : public BaseSensor {
public:
@@ -35,28 +31,11 @@ class T6613Sensor : public BaseSensor {
using TimeSource = espurna::time::CoreClock;
static constexpr auto Timeout = espurna::duration::Milliseconds(1000);
- void setRX(unsigned char pin_rx) {
- if (_pin_rx == pin_rx) return;
- _pin_rx = pin_rx;
+ void setPort(Stream* port) {
+ _serial = port;
_dirty = true;
}
- void setTX(unsigned char pin_tx) {
- if (_pin_tx == pin_tx) return;
- _pin_tx = pin_tx;
- _dirty = true;
- }
-
- // ---------------------------------------------------------------------
-
- unsigned char getRX() const {
- return _pin_rx;
- }
-
- unsigned char getTX() const {
- return _pin_tx;
- }
-
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@@ -71,37 +50,19 @@ class T6613Sensor : public BaseSensor {
// Initialization method, must be idempotent
void begin() override {
-
if (!_dirty) return;
-
- if (_serial) {
- _serial.reset(nullptr);
- }
-
- _serial = std::make_unique(_pin_rx, _pin_tx, false);
- _serial->enableIntTx(false);
- _serial->begin(19200);
-
_ready = true;
_dirty = false;
-
}
// Descriptive name of the sensor
String description() const override {
- char buffer[32];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("T6613 @ SwSerial(%hhu,%hhu)"),
- _pin_rx, _pin_tx);
- return String(buffer);
+ return F("T6613");
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char) const override {
- char buffer[8];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("%hhu:%hhu"), _pin_rx, _pin_tx);
- return String(buffer);
+ return String(T6613_PORT, 10);
}
// Type for slot # index
@@ -191,9 +152,7 @@ class T6613Sensor : public BaseSensor {
}
double _co2 = 0;
- unsigned char _pin_rx;
- unsigned char _pin_tx;
- std::unique_ptr _serial;
+ Stream* _serial { nullptr };
};
diff --git a/code/espurna/sensors/V9261FSensor.h b/code/espurna/sensors/V9261FSensor.h
index 5f52198e..c9a7f4e2 100644
--- a/code/espurna/sensors/V9261FSensor.h
+++ b/code/espurna/sensors/V9261FSensor.h
@@ -7,8 +7,6 @@
#pragma once
-#include
-
#include "BaseEmonSensor.h"
#include "../libs/fs_math.h"
@@ -34,30 +32,11 @@ class V9261FSensor : public BaseEmonSensor {
BaseEmonSensor(Magnitudes)
{}
- // ---------------------------------------------------------------------
-
- void setRX(unsigned char pin_rx) {
- if (_pin_rx == pin_rx) return;
- _pin_rx = pin_rx;
+ void setPort(Stream* port) {
+ _serial = port;
_dirty = true;
}
- void setInverted(bool inverted) {
- if (_inverted == inverted) return;
- _inverted = inverted;
- _dirty = true;
- }
-
- // ---------------------------------------------------------------------
-
- unsigned char getRX() const {
- return _pin_rx;
- }
-
- bool getInverted() const {
- return _inverted;
- }
-
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
@@ -72,34 +51,20 @@ class V9261FSensor : public BaseEmonSensor {
// Initialization method, must be idempotent
void begin() override {
-
if (!_dirty) return;
-
- if (_serial) {
- _serial.reset(nullptr);
- }
-
- _serial = std::make_unique(_pin_rx, -1, _inverted);
- _serial->enableIntTx(false);
- _serial->begin(V9261F_BAUDRATE);
-
_reading = false;
_ready = true;
_dirty = false;
-
}
// Descriptive name of the sensor
String description() const override {
- char buffer[28];
- snprintf_P(buffer, sizeof(buffer),
- PSTR("V9261F @ SwSerial(%u,NULL)"), _pin_rx);
- return String(buffer);
+ return F("V9261F");
}
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char) const override {
- return String(_pin_rx, 10);
+ return String(V9261F_PORT, 10);
}
// Loop-like method, call it in your main loop
@@ -302,9 +267,7 @@ class V9261FSensor : public BaseEmonSensor {
// ---------------------------------------------------------------------
- unsigned char _pin_rx { V9261F_PIN };
- bool _inverted { V9261F_PIN_INVERSE };
- std::unique_ptr _serial;
+ Stream* _serial { nullptr };
using TimeSource = espurna::time::CoreClock;
static constexpr auto SyncInterval = TimeSource::duration { V9261F_SYNC_INTERVAL };
diff --git a/code/espurna/terminal.cpp b/code/espurna/terminal.cpp
index 1766e268..a9df3864 100644
--- a/code/espurna/terminal.cpp
+++ b/code/espurna/terminal.cpp
@@ -48,7 +48,9 @@ constexpr size_t serialBufferSize() {
return TERMINAL_SERIAL_BUFFER_SIZE;
}
-Stream& SerialPort = TERMINAL_SERIAL_PORT;
+constexpr size_t serialPort() {
+ return TERMINAL_SERIAL_PORT - 1;
+}
} // namespace build
@@ -381,11 +383,22 @@ void setup() {
#if TERMINAL_SERIAL_SUPPORT
namespace serial {
-void loop() {
+using LoopFunc = void(*)();
+void empty_loop() {
+}
+
+namespace internal {
+
+Stream* stream { nullptr };
+LoopFunc loop { empty_loop };
+
+} // namespace internal
+
+void processing_loop() {
using LineBuffer = LineBuffer;
static LineBuffer buffer;
- static auto& port = build::SerialPort;
+ auto& port = *internal::stream;
#if defined(ARDUINO_ESP8266_RELEASE_2_7_2) \
|| defined(ARDUINO_ESP8266_RELEASE_2_7_3) \
@@ -428,6 +441,20 @@ void loop() {
}
}
+void loop() {
+ internal::loop();
+}
+
+void setup() {
+ auto port = uartPort(build::serialPort());
+ if (!port || (!port->rx || !port->tx)) {
+ return;
+ }
+
+ internal::stream = port->stream;
+ internal::loop = processing_loop;
+}
+
} // namespace serial
#endif
@@ -705,12 +732,15 @@ void setup() {
void loop() {
#if TERMINAL_SERIAL_SUPPORT
- // TODO: check if something else is using this port?
serial::loop();
#endif
}
void setup() {
+#if TERMINAL_SERIAL_SUPPORT
+ serial::setup();
+#endif
+
#if WEB_SUPPORT
// Show DEBUG panel with input
web::setup();
diff --git a/code/espurna/tuya.cpp b/code/espurna/tuya.cpp
index 4a0df235..68617615 100644
--- a/code/espurna/tuya.cpp
+++ b/code/espurna/tuya.cpp
@@ -40,8 +40,6 @@ namespace tuya {
return false;
}
- constexpr unsigned long SerialSpeed { 9600u };
-
constexpr unsigned long DiscoveryTimeout { 1500u };
constexpr unsigned long HeartbeatSlow { 9000u };
@@ -66,7 +64,7 @@ namespace tuya {
String value;
};
- Transport tuyaSerial(TUYA_SERIAL);
+ Transport* tuyaSerial { nullptr };
std::priority_queue outputFrames;
template
@@ -429,19 +427,18 @@ error:
}
void processSerial(State& state) {
+ while (tuyaSerial->available()) {
- while (tuyaSerial.available()) {
+ tuyaSerial->read();
- tuyaSerial.read();
-
- if (tuyaSerial.done()) {
- processFrame(state, tuyaSerial);
- tuyaSerial.reset();
+ if (tuyaSerial->done()) {
+ processFrame(state, *tuyaSerial);
+ tuyaSerial->reset();
}
- if (tuyaSerial.full()) {
- tuyaSerial.rewind();
- tuyaSerial.reset();
+ if (tuyaSerial->full()) {
+ tuyaSerial->rewind();
+ tuyaSerial->reset();
}
}
@@ -462,7 +459,7 @@ error:
// flush serial buffer before transmitting anything
// send fast heartbeat until mcu responds with something
case State::INIT:
- tuyaSerial.rewind();
+ tuyaSerial->rewind();
state = State::BOOT;
case State::BOOT:
sendHeartbeat(Heartbeat::Boot);
@@ -509,10 +506,10 @@ error:
}
}
- if (TUYA_SERIAL && !outputFrames.empty()) {
+ if (!outputFrames.empty()) {
auto& frame = outputFrames.top();
dataframeDebugSend("=>", frame);
- tuyaSerial.write(frame.serialize());
+ tuyaSerial->write(frame.serialize());
outputFrames.pop();
}
@@ -658,6 +655,15 @@ error:
#endif
void setup() {
+ const auto port = uartPort(TUYA_PORT - 1);
+
+ // No point starting up when port is unusable
+ if (!port || (!port->tx || !port->rx)) {
+ return;
+ }
+
+ tuyaSerial = new Transport(*port->stream);
+
#if TERMINAL_SUPPORT
tuya::terminalSetup();
#endif
@@ -669,8 +675,7 @@ error:
filter = getSetting("tuyaFilter", 1 == TUYA_FILTER_ENABLED);
// Install main loop method and WiFiStatus ping (only works with specific mode)
- TUYA_SERIAL.begin(SerialSpeed);
-
+
::espurnaRegisterLoop(loop);
::wifiRegister([](espurna::wifi::Event event) {
switch (event) {
diff --git a/code/espurna/uart.cpp b/code/espurna/uart.cpp
new file mode 100644
index 00000000..ee0cadc2
--- /dev/null
+++ b/code/espurna/uart.cpp
@@ -0,0 +1,566 @@
+/*
+
+UART MODULE
+
+Copyright (C) 2022 by Maxim Prokhorov
+
+*/
+
+#include "espurna.h"
+
+#if UART_SUPPORT
+
+#include "uart.h"
+#include "utils.h"
+
+#if UART_SOFTWARE_SUPPORT
+#include
+#endif
+
+#include
+#include
+
+namespace espurna {
+namespace driver {
+namespace uart {
+namespace {
+
+enum class Parity {
+ None,
+ Even,
+ Odd,
+};
+
+struct Config {
+ uint8_t data_bits;
+ Parity parity;
+ uint8_t stop_bits;
+};
+
+} // namespace
+} // namespace uart
+} // namespace driver
+
+namespace settings {
+namespace internal {
+namespace {
+
+alignas(4) static constexpr char ParityNone[] PROGMEM = "none";
+alignas(4) static constexpr char ParityEven[] PROGMEM = "even";
+alignas(4) static constexpr char ParityOdd[] PROGMEM = "odd";
+
+static constexpr std::array, 3> ParityOptions PROGMEM {
+ {{driver::uart::Parity::None, ParityNone},
+ {driver::uart::Parity::Even, ParityEven},
+ {driver::uart::Parity::Odd, ParityOdd}}
+};
+
+} // namespace
+
+template <>
+driver::uart::Parity convert(const String& value) {
+ return convert(ParityOptions, value, driver::uart::Parity::None);
+}
+
+String serialize(driver::uart::Parity value) {
+ return espurna::settings::internal::serialize(ParityOptions, value);
+}
+
+} // namespace internal
+} // namespace settings
+
+namespace driver {
+namespace uart {
+namespace {
+
+namespace build {
+
+// i.e. uart0, uart1 and a single sw port
+// for general use, hardware uart *should* be prefered method
+constexpr size_t PortsMax { 3 };
+
+// todo; technically, tx==2 is also possible
+// but, we reserve that for the uart1 TX-only interface
+constexpr bool uart0_normal(uint8_t tx, uint8_t rx) {
+ return ((tx == 1) && (rx == 3))
+ || ((tx == GPIO_NONE) && (rx == 3))
+ || ((tx == 1) && (rx == GPIO_NONE));
+}
+
+constexpr bool uart0_swapped(uint8_t tx, uint8_t rx) {
+ return ((tx == 15) && (rx == 13))
+ || ((tx == GPIO_NONE) && (rx == 13))
+ || ((tx == 15) && (rx == GPIO_NONE));
+}
+
+constexpr bool uart1_normal(uint8_t tx, uint8_t rx) {
+ return (tx == 2) && (rx == GPIO_NONE);
+}
+
+constexpr uint8_t tx(size_t index) {
+ return (0 == index) ? (UART1_TX_PIN) :
+ (1 == index) ? (UART2_TX_PIN) :
+ (2 == index) ? (UART3_TX_PIN) :
+ GPIO_NONE;
+}
+
+constexpr uint8_t rx(size_t index) {
+ return (0 == index) ? (UART1_RX_PIN) :
+ (1 == index) ? (UART2_RX_PIN) :
+ (2 == index) ? (UART3_RX_PIN) :
+ GPIO_NONE;
+}
+
+constexpr uint32_t baudrate(size_t index) {
+ return (0 == index) ? (UART1_BAUDRATE) :
+ (1 == index) ? (UART2_BAUDRATE) :
+ (2 == index) ? (UART3_BAUDRATE) :
+ 0;
+}
+
+constexpr uint8_t data_bits(size_t index) {
+ return (0 == index) ? (UART1_DATA_BITS) :
+ (1 == index) ? (UART2_DATA_BITS) :
+ (2 == index) ? (UART3_DATA_BITS)
+ : 0;
+}
+
+constexpr Parity parity(size_t index) {
+ return (0 == index) ? (Parity::UART1_PARITY) :
+ (1 == index) ? (Parity::UART2_PARITY) :
+ (2 == index) ? (Parity::UART3_PARITY)
+ : Parity::None;
+}
+
+constexpr uint8_t stop_bits(size_t index) {
+ return (0 == index) ? (UART1_STOP_BITS) :
+ (1 == index) ? (UART2_STOP_BITS) :
+ (2 == index) ? (UART3_STOP_BITS)
+ : 0;
+}
+
+constexpr bool invert(size_t index) {
+ return (0 == index) ? (UART1_INVERT == 1) :
+ (1 == index) ? (UART2_INVERT == 1) :
+ (2 == index) ? (UART3_INVERT == 1)
+ : false;
+}
+
+} // namespace build
+
+constexpr int data_bits_from_config(uint8_t bits) {
+ return (bits == 5) ? 0 :
+ (bits == 6) ? 0b100 :
+ (bits == 7) ? 0b1000 :
+ (bits == 8) ? 0b1100 :
+ data_bits_from_config(8);
+}
+
+constexpr int parity_from_config(Parity parity) {
+ return (parity == Parity::None) ? 0 :
+ (parity == Parity::Even) ? 0b10 :
+ (parity == Parity::Odd) ? 0b11 :
+ parity_from_config(Parity::None);
+}
+
+constexpr int stop_bits_from_config(uint8_t bits) {
+ return (bits == 1) ? 0b10000 :
+ (bits == 2) ? 0b110000 :
+ stop_bits_from_config(1);
+}
+
+template
+constexpr T from_config(Config);
+
+template <>
+constexpr ::SerialConfig from_config(Config config) {
+ return static_cast<::SerialConfig>(
+ data_bits_from_config(config.data_bits)
+ | parity_from_config(config.parity)
+ | stop_bits_from_config(config.stop_bits));
+}
+
+namespace settings {
+namespace keys {
+
+alignas(4) static constexpr char TxPin[] PROGMEM = "uartTx";
+alignas(4) static constexpr char RxPin[] PROGMEM = "uartRx";
+
+alignas(4) static constexpr char Baudrate[] PROGMEM = "uartBaud";
+
+alignas(4) static constexpr char DataBits[] PROGMEM = "uartDataBits";
+alignas(4) static constexpr char StopBits[] PROGMEM = "uartStopBits";
+alignas(4) static constexpr char Parity[] PROGMEM = "uartParity";
+
+alignas(4) static constexpr char Invert[] PROGMEM = "uartInv";
+
+} // namespace keys
+
+uint8_t tx(size_t index) {
+ return getSetting({keys::TxPin, index}, build::tx(index));
+}
+
+uint8_t rx(size_t index) {
+ return getSetting({keys::RxPin, index}, build::rx(index));
+}
+
+uint32_t baudrate(size_t index) {
+ return getSetting({keys::Baudrate, index}, build::baudrate(index));
+}
+
+uint8_t data_bits(size_t index) {
+ return getSetting({keys::DataBits, index}, build::data_bits(index));
+}
+
+uint8_t stop_bits(size_t index) {
+ return getSetting({keys::StopBits, index}, build::stop_bits(index));
+}
+
+Parity parity(size_t index) {
+ return getSetting({keys::Parity, index}, build::parity(index));
+}
+
+bool invert(size_t index) {
+ return getSetting({keys::Invert, index}, build::invert(index));
+}
+
+} // namespace settings
+
+using StreamPtr = std::unique_ptr;
+
+struct BasePort {
+ Type type;
+ bool tx;
+ bool rx;
+ StreamPtr stream;
+};
+
+using BasePortPtr = std::unique_ptr;
+
+BasePortPtr hardware_port(
+ uint32_t baudrate, uint8_t tx, uint8_t rx, Config config, bool invert)
+{
+ const int number =
+ build::uart0_normal(tx, rx)
+ ? 0 :
+ build::uart0_swapped(tx, rx)
+ ? 0 :
+ build::uart1_normal(tx, rx)
+ ? 1
+ : -1;
+ if (number < 0) {
+ return nullptr;
+ }
+
+ const int mode =
+ (tx == GPIO_NONE)
+ ? SERIAL_RX_ONLY :
+ (rx == GPIO_NONE)
+ ? SERIAL_TX_ONLY :
+ ((tx != GPIO_NONE) && (rx != GPIO_NONE))
+ ? SERIAL_FULL
+ : -1;
+ if (mode < 0) {
+ return nullptr;
+ }
+
+ auto* ptr = new HardwareSerial(number);
+ ptr->begin(baudrate,
+ from_config<::SerialConfig>(config),
+ static_cast(mode),
+ tx, invert);
+ if ((number == 0) && (build::uart0_swapped(tx, rx))) {
+ ptr->flush();
+ ptr->swap();
+ }
+
+ return std::make_unique(
+ BasePort{
+ .type = (number == 0) ? Type::Uart0 : Type::Uart1,
+ .tx = (tx != GPIO_NONE),
+ .rx = (rx != GPIO_NONE),
+ .stream = StreamPtr(ptr),
+ });
+}
+
+// based on the values in v6 of the lib. still, return bits instead of the octal notation used there
+#if UART_SOFTWARE_SUPPORT
+constexpr int software_serial_data_bits_from_config(uint8_t bits) {
+ return (bits == 5) ? 0 :
+ (bits == 6) ? 0b1 :
+ (bits == 7) ? 0b10 :
+ (bits == 8) ? 0b11 :
+ software_serial_data_bits_from_config(8);
+}
+
+// btw, SoftwareSerial also has Mark and Space
+// no support on the hardware peripheral though (afaik)
+constexpr int software_serial_parity_from_config(Parity parity) {
+ return (parity == Parity::None) ? 0 :
+ (parity == Parity::Even) ? 0b10000 :
+ (parity == Parity::Odd) ? 0b11000 :
+ software_serial_parity_from_config(Parity::None);
+}
+
+constexpr int software_serial_stop_bits_from_config(uint8_t bits) {
+ return (bits == 1) ? 0b0 :
+ (bits == 2) ? 0b10000000 :
+ software_serial_stop_bits_from_config(1);
+}
+
+template <>
+constexpr ::SoftwareSerialConfig from_config(Config config) {
+ return static_cast<::SoftwareSerialConfig>(
+ software_serial_data_bits_from_config(config.data_bits)
+ | software_serial_parity_from_config(config.parity)
+ | software_serial_stop_bits_from_config(config.stop_bits));
+
+}
+
+BasePortPtr software_serial_port(
+ uint32_t baudrate, uint8_t tx, uint8_t rx, Config config, bool invert)
+{
+ const int8_t tx_pin = (tx == GPIO_NONE) ? -1 : tx;
+ const int8_t rx_pin = (rx == GPIO_NONE) ? -1 : rx;
+
+ auto* ptr = new SoftwareSerial(tx_pin, rx_pin, invert);
+ ptr->begin(baudrate, from_config<::SoftwareSerialConfig>(config));
+
+ return std::make_unique(
+ BasePort{
+ .type = Type::Software,
+ .tx = (tx_pin > 0),
+ .rx = (rx_pin > 0),
+ .stream = StreamPtr(ptr),
+ });
+}
+#endif
+
+BasePortPtr make_port(size_t index) {
+ BasePortPtr out;
+
+ const auto tx = settings::tx(index);
+ const auto rx = settings::rx(index);
+ if ((tx == GPIO_NONE) && (rx == GPIO_NONE)) {
+ return out;
+ }
+
+ if ((tx != GPIO_NONE) && !gpioLock(tx)) {
+ return out;
+ }
+
+ if ((rx != GPIO_NONE) && !gpioLock(rx)) {
+ gpioUnlock(tx);
+ return out;
+ }
+
+ const auto config = Config{
+ .data_bits = settings::data_bits(index),
+ .parity = settings::parity(index),
+ .stop_bits = settings::stop_bits(index),
+ };
+
+ const auto baudrate = settings::baudrate(index);
+ const auto invert = settings::invert(index);
+
+ if (build::uart0_normal(tx, rx)
+ || build::uart0_swapped(tx, rx)
+ || build::uart1_normal(tx, rx))
+ {
+ out = hardware_port(baudrate, tx, rx, config, invert);
+ }
+#if UART_SOFTWARE_SUPPORT
+ else {
+ out = software_serial_port(baudrate, tx, rx, config, invert);
+ }
+#endif
+
+ return out;
+}
+
+namespace internal {
+
+BasePortPtr ports[build::PortsMax];
+
+} // namespace internal
+
+size_t ports() {
+ size_t out = 0;
+ for (const auto& port : internal::ports) {
+ if (!port) {
+ break;
+ }
+ ++out;
+ }
+
+ return out;
+}
+
+namespace settings {
+namespace query {
+
+#define ID_VALUE(NAME)\
+String NAME (size_t id) {\
+ return espurna::settings::internal::serialize(\
+ espurna::driver::uart::settings::NAME(id));\
+}
+
+ID_VALUE(tx)
+ID_VALUE(rx)
+ID_VALUE(baudrate)
+ID_VALUE(data_bits)
+ID_VALUE(stop_bits)
+ID_VALUE(parity)
+ID_VALUE(invert)
+
+#undef ID_VALUE
+
+static constexpr espurna::settings::query::IndexedSetting IndexedSettings[] PROGMEM {
+ {keys::TxPin, query::tx},
+ {keys::RxPin, query::rx},
+ {keys::Baudrate, query::baudrate},
+ {keys::DataBits, query::data_bits},
+ {keys::StopBits, query::stop_bits},
+ {keys::Parity, query::parity},
+ {keys::Invert, query::invert},
+};
+
+bool checkSamePrefix(StringView key) {
+ alignas(4) static constexpr char Prefix[] PROGMEM = "uart";
+ return espurna::settings::query::samePrefix(key, Prefix);
+}
+
+String findIndexedValueFrom(StringView key) {
+ return espurna::settings::query::IndexedSetting::findValueFrom(
+ ports(), IndexedSettings, key);
+
+}
+
+void setup() {
+ settingsRegisterQueryHandler({
+ .check = checkSamePrefix,
+ .get = findIndexedValueFrom,
+ });
+}
+
+} // namespace query
+} // namespace settings
+
+#if TERMINAL_SUPPORT
+namespace terminal {
+namespace commands {
+
+String port_type(Type type) {
+ const char* out = PSTR("UNKNOWN");
+
+ switch (type) {
+ case Type::Unknown:
+ break;
+ case Type::Software:
+ out = PSTR("SOFTWARE");
+ break;
+ case Type::Uart0:
+ out = PSTR("UART0");
+ break;
+ case Type::Uart1:
+ out = PSTR("UART1");
+ break;
+ }
+
+ return out;
+}
+
+void uart(::terminal::CommandContext&& ctx) {
+ if (ctx.argv.size() == 1) {
+ for (size_t index = 0; index < std::size(internal::ports); ++index) {
+ const auto& port = internal::ports[index];
+ if (!port) {
+ break;
+ }
+
+ ctx.output.printf_P(
+ PSTR("%zu - %s{tx=%c rx=%c}\n"),
+ index, port_type(port->type).c_str(),
+ port->tx ? 'y' : 'n',
+ port->rx ? 'y' : 'n');
+ }
+
+ } else if (ctx.argv.size() == 2) {
+ const auto parse_id = espurna::settings::internal::convert;
+ const auto id = parse_id(ctx.argv[1]);
+ if (id >= ports()) {
+ terminalError(ctx, F("Invalid ID"));
+ return;
+ }
+
+ settingsDump(ctx, settings::query::IndexedSettings, id);
+ } else {
+ terminalError(ctx, F("UART []"));
+ return;
+ }
+
+ terminalOK(ctx);
+}
+
+alignas(4) static constexpr char Uart[] PROGMEM = "UART";
+
+static constexpr ::terminal::Command List[] PROGMEM {
+ {Uart, commands::uart},
+};
+
+} // namespace commands
+
+void setup() {
+ espurna::terminal::add(commands::List);
+}
+
+} // namespace terminal
+#endif
+
+PortPtr port(size_t index) {
+ const auto& ptr = internal::ports[index];
+ if ((index < std::size(internal::ports)) && (ptr)) {
+ return std::make_unique(
+ Port{
+ .type = ptr->type,
+ .tx = ptr->tx,
+ .rx = ptr->rx,
+ .stream = ptr->stream.get(),
+ });
+ }
+
+ return nullptr;
+}
+
+void setup() {
+#if TERMINAL_SUPPORT
+ terminal::setup();
+#endif
+
+ settings::query::setup();
+
+ for (size_t index = 0; index < build::PortsMax; ++index) {
+ auto& port = internal::ports[index];
+
+ port = make_port(index);
+ if (!port) {
+ break;
+ }
+ }
+}
+
+} // namespace uart
+
+} // namespace
+} // namespace driver
+} // namespace espurna
+
+espurna::driver::uart::PortPtr uartPort(size_t index) {
+ return espurna::driver::uart::port(index);
+}
+
+void uartSetup() {
+ espurna::driver::uart::setup();
+}
+
+#endif // UART_SUPPORT
diff --git a/code/espurna/uart.h b/code/espurna/uart.h
new file mode 100644
index 00000000..b6140af3
--- /dev/null
+++ b/code/espurna/uart.h
@@ -0,0 +1,40 @@
+/*
+
+UART MODULE
+
+Copyright (C) 2022 by Maxim Prokhorov
+
+*/
+
+#pragma once
+
+#include
+
+#include
+
+namespace espurna {
+namespace driver {
+namespace uart {
+
+enum class Type {
+ Unknown,
+ Software,
+ Uart0,
+ Uart1,
+};
+
+struct Port {
+ Type type;
+ bool tx;
+ bool rx;
+ Stream* stream;
+};
+
+using PortPtr = std::unique_ptr;
+
+} // namespace uart
+} // namespace driver
+} // namespace espurna
+
+espurna::driver::uart::PortPtr uartPort(size_t index);
+void uartSetup();
diff --git a/code/espurna/uartmqtt.cpp b/code/espurna/uartmqtt.cpp
index 6ae50047..3eff77ce 100644
--- a/code/espurna/uartmqtt.cpp
+++ b/code/espurna/uartmqtt.cpp
@@ -20,10 +20,6 @@ Support queueing and handling input without termination by Maxim Prokhorov
#include
-#if UART_MQTT_SOFTWARE_SERIAL
-#include
-#endif
-
namespace espurna {
namespace uart_mqtt {
namespace {
@@ -44,24 +40,6 @@ constexpr uint8_t TerminateOut { UART_MQTT_TERMINATE_OUT };
constexpr bool Encode { UART_MQTT_ENCODE };
constexpr bool Decode { UART_MQTT_DECODE };
-constexpr size_t Baudrate PROGMEM { UART_MQTT_BAUDRATE };
-constexpr auto Config PROGMEM = UART_MQTT_CONFIG;
-
-constexpr uint8_t RxPin { UART_MQTT_RX_PIN };
-constexpr uint8_t TxPin { UART_MQTT_TX_PIN };
-
-constexpr bool uart0_normal() {
- return (build::TxPin == 1) && (build::RxPin == 3);
-}
-
-constexpr bool uart0_swapped() {
- return (build::TxPin == 15) && (build::RxPin == 13);
-}
-
-HardwareSerial& uart0() {
- return Serial;
-}
-
} // namespace build
// Output is capped, prepare using a fixed-size buffer
@@ -75,62 +53,13 @@ using Buffer = std::array;
// Prefer smaller output instead of using `Serialized` directly
using Queue = std::queue;
-#if UART_MQTT_SOFTWARE_SERIAL
-Stream& software_serial_port() {
- static auto& port = ([]() -> Stream& {
- auto* port = new SoftwareSerial(build::RxPin, build::TxPin);
- port->begin(build::Baudrate, SWSERIAL_8N1);
- return *port;
- })();
-
- return port;
-}
-#endif
-
-#if __cplusplus >= 201703L
-#define CONSTEXPR constexpr
-#else
-#define CONSTEXPR
-#endif
-
-Stream& hardware_port() {
- // TODO: instantiate port outside of here, without Arduino config stuff
- // TODO: this swap() thing is esp8266-specific
- static auto& port = ([]() -> Stream& {
- auto& port = build::uart0();
-
- port.begin(build::Baudrate, build::Config);
- if CONSTEXPR (build::uart0_swapped()) {
- port.flush();
- port.swap();
- }
-
- return port;
- })();
-
- return port;
-}
-
-Stream& port() {
-#if UART_MQTT_SOFTWARE_SERIAL
- if CONSTEXPR (build::uart0_normal() || build::uart0_swapped()) {
- return hardware_port();
- }
-
- return software_serial_port();
-#else
- return hardware_port();
-#endif
-}
-
-#undef CONSTEXPR
-
namespace internal {
Buffer buffer;
auto cursor = buffer.begin();
Queue queue;
+Stream* port;
} // namespace internal
@@ -278,7 +207,7 @@ void enqueue(String data) {
internal::queue.emplace(std::move(data));
}
-void write(Stream& stream, uint8_t termination, bool decode) {
+void write(Print& print, uint8_t termination, bool decode) {
using Clock = time::CoreClock;
const auto start = Clock::now();
@@ -291,16 +220,16 @@ void write(Stream& stream, uint8_t termination, bool decode) {
decoded.data(), decoded.size());
if (size) {
- stream.write(decoded.data(), size);
+ print.write(decoded.data(), size);
}
if (size && termination) {
- stream.write(termination);
+ print.write(termination);
}
} else {
- stream.write(front.begin(), front.length());
+ print.write(front.begin(), front.length());
if (termination) {
- stream.write(termination);
+ print.write(termination);
}
}
@@ -326,15 +255,22 @@ void mqtt_callback(unsigned int type, const char* topic, const char* payload) {
}
void loop() {
- read(port(),
+ read(*internal::port,
build::TerminateIn,
build::Encode);
- write(port(),
+ write(*internal::port,
build::TerminateOut,
build::Decode);
}
void setup() {
+ const auto port = uartPort(UART_MQTT_PORT - 1);
+ if (!port || (!port->rx || !port->tx)) {
+ return;
+ }
+
+ internal::port = port->stream;
+
mqttRegister(mqtt_callback);
espurnaRegisterLoop(loop);
}
diff --git a/code/espurna/web.cpp b/code/espurna/web.cpp
index 7648cdd7..4c557c05 100644
--- a/code/espurna/web.cpp
+++ b/code/espurna/web.cpp
@@ -96,7 +96,7 @@ bool AsyncWebPrint::_addBuffer() {
// - Returning 0 will immediatly close the connection from our side
// - Calling _prepareRequest() **before** _buffers are filled will result in returning 0
// - Calling yield() / delay() while request AsyncWebPrint is active **may** trigger this callback out of sequence
-// (e.g. Serial.print(..), DEBUG_MSG(...), or any other API trying to switch contexts)
+// (e.g. Stream.write(...), Stream.read(...), DEBUG_MSG(...), or any other API trying to switch contexts)
// - Receiving data (tcp ack from the previous packet) **will** trigger the callback when switching contexts.
void AsyncWebPrint::_prepareRequest() {
diff --git a/code/platformio.ini b/code/platformio.ini
index e10beafd..5df3c503 100644
--- a/code/platformio.ini
+++ b/code/platformio.ini
@@ -152,7 +152,6 @@ shared_lib_deps =
https://github.com/256dpi/arduino-mqtt#196556b6
https://github.com/mcspr/nofuss.git#0.4.0
paulstoffregen/OneWire@^2.3.5
- olehs/PZEM004T@^1.1.5
knolleary/PubSubClient@^2.8.0
https://github.com/1technophile/rc-switch#11402652
lowpowerlab/SPIFlash@^101.1.3
diff --git a/code/test/build/rfbridge.h b/code/test/build/rfbridge.h
index fe79bdd5..f304cdb4 100644
--- a/code/test/build/rfbridge.h
+++ b/code/test/build/rfbridge.h
@@ -1,5 +1,4 @@
#define DUMMY_RELAY_COUNT 8
-#define SERIAL_BAUDRATE 19200
-#define DEBUG_SERIAL_SUPPORT 0
+#define RELAY_SUPPORT 1
#define RFB_SUPPORT 1
#define RFB_PROVIDER RFB_PROVIDER_EFM8BB1