Browse Source

uart: standalone configuration

- setup uart at boot instead of delaying until some module needs it
  removes global `SERIAL_BAUDRATE` and `DEBUG_PORT` in favour or
  globally accessible numbered port configurations with `MODULE_NAME_PORT`
- automagically enable uart support for sensors that need it
- allow every sensor to configure uart0 (normal and swapped), uart1
  and an optional software-serial mode support with an extra build flag
  remove individual includes across the sensors
- settings queries and runtime port configuration (prefixed with uart)
- update dependencies header to cross-reference used ports at build time
- update deprecations header with serial baudrate notice
pull/2552/head
Maxim Prokhorov 1 year ago
parent
commit
574fbf1960
39 changed files with 1656 additions and 1331 deletions
  1. +1
    -1
      code/espurna/config/all.h
  2. +34
    -33
      code/espurna/config/arduino.h
  3. +88
    -0
      code/espurna/config/defaults.h
  4. +82
    -3
      code/espurna/config/dependencies.h
  5. +7
    -2
      code/espurna/config/deprecated.h
  6. +55
    -51
      code/espurna/config/general.h
  7. +151
    -86
      code/espurna/config/hardware.h
  8. +60
    -111
      code/espurna/config/sensors.h
  9. +12
    -18
      code/espurna/curtain_kingart.cpp
  10. +52
    -14
      code/espurna/debug.cpp
  11. +1
    -0
      code/espurna/espurna.h
  12. +3
    -7
      code/espurna/garland/color.h
  13. +3
    -3
      code/espurna/gpio.cpp
  14. +15
    -11
      code/espurna/lightfox.cpp
  15. +3
    -0
      code/espurna/main.cpp
  16. +25
    -15
      code/espurna/relay.cpp
  17. +18
    -12
      code/espurna/rfbridge.cpp
  18. +3
    -2
      code/espurna/rtcmem.cpp
  19. +75
    -58
      code/espurna/sensor.cpp
  20. +18
    -87
      code/espurna/sensors/CSE7766Sensor.h
  21. +5
    -44
      code/espurna/sensors/EZOPHSensor.h
  22. +5
    -43
      code/espurna/sensors/MHZ19Sensor.h
  23. +7
    -74
      code/espurna/sensors/PM1006Sensor.h
  24. +9
    -86
      code/espurna/sensors/PMSX003Sensor.h
  25. +208
    -96
      code/espurna/sensors/PZEM004TSensor.h
  26. +10
    -114
      code/espurna/sensors/PZEM004TV30Sensor.h
  27. +5
    -48
      code/espurna/sensors/SDS011Sensor.h
  28. +7
    -73
      code/espurna/sensors/SM300D2Sensor.h
  29. +5
    -47
      code/espurna/sensors/SenseAirSensor.h
  30. +5
    -46
      code/espurna/sensors/T6613Sensor.h
  31. +5
    -42
      code/espurna/sensors/V9261FSensor.h
  32. +34
    -4
      code/espurna/terminal.cpp
  33. +22
    -17
      code/espurna/tuya.cpp
  34. +566
    -0
      code/espurna/uart.cpp
  35. +40
    -0
      code/espurna/uart.h
  36. +15
    -79
      code/espurna/uartmqtt.cpp
  37. +1
    -1
      code/espurna/web.cpp
  38. +0
    -1
      code/platformio.ini
  39. +1
    -2
      code/test/build/rfbridge.h

+ 1
- 1
code/espurna/config/all.h View File

@ -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"

+ 34
- 33
code/espurna/config/arduino.h View File

@ -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)


+ 88
- 0
code/espurna/config/defaults.h View File

@ -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
// -----------------------------------------------------------------------------


+ 82
- 3
code/espurna/config/dependencies.h View File

@ -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

+ 7
- 2
code/espurna/config/deprecated.h View File

@ -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

+ 55
- 51
code/espurna/config/general.h View File

@ -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
@ -884,37 +872,29 @@
#endif
// -----------------------------------------------------------------------------
// UART <-> MQTT
// UART
// -----------------------------------------------------------------------------
#ifndef UART_MQTT_SUPPORT
#define UART_MQTT_SUPPORT 0 // Not enabled by default
#endif
#ifndef UART_MQTT_BAUDRATE
#define UART_MQTT_BAUDRATE 115200
#ifndef UART_SUPPORT
#define UART_SUPPORT 1
#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)
#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
#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
// -----------------------------------------------------------------------------
// UART <-> MQTT
// -----------------------------------------------------------------------------
#ifndef UART_MQTT_TX_PIN
#define UART_MQTT_TX_PIN 1
#ifndef UART_MQTT_SUPPORT
#define UART_MQTT_SUPPORT 0 // Not enabled by default
#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
// =============================================================================


+ 151
- 86
code/espurna/config/hardware.h View File

@ -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!!"


+ 60
- 111
code/espurna/config/sensors.h View File

@ -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 || \


+ 12
- 18
code/espurna/curtain_kingart.cpp View File

@ -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


+ 52
- 14
code/espurna/debug.cpp View File

@ -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);
}
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;
}
}
out.write(message, len);
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();


+ 1
- 0
code/espurna/espurna.h View File

@ -34,6 +34,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "settings.h"
#include "system.h"
#include "terminal.h"
#include "uart.h"
#include "utils.h"
#include "network.h"
#include "wifi.h"


+ 3
- 7
code/espurna/garland/color.h View File

@ -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;
}
};

+ 3
- 3
code/espurna/gpio.cpp View File

@ -19,9 +19,8 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
#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 {


+ 15
- 11
code/espurna/lightfox.cpp View File

@ -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<uint8_t>('\r'),
static_cast<uint8_t>('\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<uint8_t>(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)


+ 3
- 0
code/espurna/main.cpp View File

@ -113,6 +113,9 @@ void setup() {
// Init persistance
settingsSetup();
// Init hardware / software UART ports
uartSetup();
// Configure logger and crash recorder
#if DEBUG_SUPPORT
debugConfigureBoot();


+ 25
- 15
code/espurna/relay.cpp View File

@ -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<unsigned char>(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<DualProvider*> _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;
};


+ 18
- 12
code/espurna/rfbridge.cpp View File

@ -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<uint8_t>& 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


+ 3
- 2
code/espurna/rtcmem.cpp View File

@ -19,9 +19,9 @@ volatile RtcmemData* Rtcmem = reinterpret_cast<volatile RtcmemData*>(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


+ 75
- 58
code/espurna/sensor.cpp View File

@ -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<PZEM004TSensor::SerialPort>(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(std::move(port),
getSetting("pzemv30Addr", PZEM004TV30Sensor::DefaultAddress),
getSetting("pzemv30ReadTimeout", PZEM004TV30Sensor::DefaultReadTimeout));
sensor->setDebug(
getSetting("pzemv30Debug", PZEM004TV30Sensor::DefaultDebug));
auto* sensor = PZEM004TV30Sensor::make(port->stream,
getSetting("pzemv30Addr", PZEM004TV30Sensor::DefaultAddress),
getSetting("pzemv30ReadTimeout", PZEM004TV30Sensor::DefaultReadTimeout));
sensor->setDebug(
getSetting("pzemv30Debug", PZEM004TV30Sensor::DefaultDebug));
add(sensor);
}
add(sensor);
}
#endif
}


+ 18
- 87
code/espurna/sensors/CSE7766Sensor.h View File

@ -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 <SoftwareSerial.h>
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<SoftwareSerial>(_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<SoftwareSerial> _serial;
Stream* _serial;
double _active = 0;
double _reactive = 0;


+ 5
- 44
code/espurna/sensors/EZOPHSensor.h View File

@ -1,7 +1,6 @@
// -----------------------------------------------------------------------------
// EZO pH Circuit from Atlas Scientific
//
// Uses SoftwareSerial library
// Copyright (C) 2018 by Rui Marinho <ruipmarinho at gmail dot com>
// -----------------------------------------------------------------------------
@ -9,38 +8,17 @@
#pragma once
#include <SoftwareSerial.h>
#include "BaseSensor.h"
class EZOPHSensor : public BaseSensor {
public:
// ---------------------------------------------------------------------
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;
void setPort(Stream* port) {
_serial = port;
_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<SoftwareSerial>(_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<SoftwareSerial> _serial;
Stream* _serial;
};


+ 5
- 43
code/espurna/sensors/MHZ19Sensor.h View File

@ -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 <xose dot perez at gmail dot com>
// -----------------------------------------------------------------------------
@ -10,8 +9,6 @@
#pragma once
#include <SoftwareSerial.h>
#include "BaseSensor.h"
#include <array>
@ -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;
_dirty = true;
}
void setTX(unsigned char pin_tx) {
if (_pin_tx == pin_tx) return;
_pin_tx = pin_tx;
void setPort(Stream* port) {
_serial = port;
_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<SoftwareSerial>(_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<SoftwareSerial> _serial;
Stream* _serial { nullptr };
};


+ 7
- 74
code/espurna/sensors/PM1006Sensor.h View File

@ -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 <xose dot perez at gmail dot com>
// -----------------------------------------------------------------------------
@ -9,24 +8,17 @@
#pragma once
#include <SoftwareSerial.h>
#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<SoftwareSerial>(_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<SoftwareSerial> _serial;
Stream* _serial { nullptr };
};


+ 9
- 86
code/espurna/sensors/PMSX003Sensor.h View File

@ -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 <SoftwareSerial.h>
// 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<SoftwareSerial*>(_serial)->enableIntTx(false);
}
if (_soft) {
static_cast<SoftwareSerial*>(_serial)->begin(PMS_BAUD_RATE);
} else {
static_cast<HardwareSerial*>(_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<SoftwareSerial*>(_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;


+ 208
- 96
code/espurna/sensors/PZEM004TSensor.h View File

@ -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 <PZEM004T.h>
#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; i<sizeof(pzem.addr); i++)
pzem.addr[i] = addr[i];
pzem.data = data;
uint8_t *bytes = (uint8_t*)&pzem;
pzem.crc = crc(bytes, sizeof(pzem) - 1);
while (_serial->available()) {
_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<RESPONSE_DATA_SIZE; i++)
data[i] = buffer[1 + i];
}
return true;
}
uint8_t PZEM004T::crc(uint8_t *data, uint8_t sz)
{
uint16_t crc = 0;
for(uint8_t i=0; i<sz; i++)
crc += *data++;
return (uint8_t)(crc & 0xFF);
}
class PZEM004TSensor : public BaseEmonSensor {
private:
// Track instances returned by 'make()' in a singly linked list
@ -89,17 +283,6 @@ private:
};
public:
static constexpr unsigned char RxPin { PZEM004T_RX_PIN };
static constexpr unsigned char TxPin { PZEM004T_TX_PIN };
static HardwareSerial* defaultHardwarePort() {
return &PZEM004T_HW_PORT;
}
static constexpr bool useSoftwareSerial() {
return 1 == PZEM004T_USE_SOFT;
}
static constexpr TimeSource::duration ReadInterval { PZEM004T_READ_INTERVAL };
static constexpr size_t DevicesMax { PZEM004T_DEVICES_MAX };
@ -118,22 +301,17 @@ public:
return out;
}
// TODO: PZEM lib wants us to compose things this way. prefer Stream interface
// and port the existing code here so we don't have to pass software / hardware pointers,
// and configure everything ourselves on a global level
// TODO: also notice neither class returns pins in use... and these
// should go away when migrated to global serial config
struct SerialPort {
using PzemPtr = std::unique_ptr<PZEM004T>;
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<PZEM004T>(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<PZEM004T>(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<PZEM004T>(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<SerialPort>;
static PortPtr makeHardwarePort(HardwareSerial* port, unsigned char rx, unsigned char tx) {
auto ptr = std::make_shared<HardwareSerialPort>(port, rx, tx);
_ports.push_back(ptr);
return ptr;
}
static PortPtr makeSoftwarePort(unsigned char rx, unsigned char tx) {
auto ptr = std::make_shared<SoftwareSerialPort>(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"));
}


+ 10
- 114
code/espurna/sensors/PZEM004TV30Sensor.h View File

@ -19,9 +19,6 @@ Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
#include <cstdint>
#include <array>
// TODO: keep this until we have external API giving us swserial stream objects
#include <SoftwareSerial.h>
#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 <prokhorov dot max at outlook dot com>
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<SoftwareSerial>(rx, tx))
{}
const char* tag() const override {
return "Sw";
}
void begin(unsigned long baudrate) override {
_serial->begin(baudrate);
}
Stream* operator->() override {
return static_cast<Stream*>(_serial.get());
}
private:
std::unique_ptr<SoftwareSerial> _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<Stream*>(_serial);
}
private:
HardwareSerial* _serial;
};
using PortPtr = std::unique_ptr<SerialPort>;
using Instance = std::unique_ptr<PZEM004TV30Sensor>;
static PortPtr makeHardwarePort(HardwareSerial* port, unsigned char rx, unsigned char tx) {
return std::make_unique<HardwarePort>(port, rx, tx);
}
static PortPtr makeSoftwarePort(unsigned char rx, unsigned char tx) {
return std::make_unique<SoftwarePort>(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<TimeSource::duration, espurna::duration::Milliseconds>::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 };


+ 5
- 48
code/espurna/sensors/SDS011Sensor.h View File

@ -2,7 +2,6 @@
// SDS011 dust sensor
// Based on: https://github.com/ricki-z/SDS011
//
// Uses SoftwareSerial library
// Copyright (C) 2018 by Lucas Pleß <hello at lucas-pless dot com>
// -----------------------------------------------------------------------------
@ -10,37 +9,15 @@
#pragma once
#include <SoftwareSerial.h>
#include "BaseSensor.h"
class SDS011Sensor : public BaseSensor {
public:
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;
void setPort(Stream* port) {
_serial = port;
_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<SoftwareSerial>(_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<SoftwareSerial> _serial;
Stream* _serial;
};
#endif // SENSOR_SUPPORT && SDS011_SUPPORT

+ 7
- 73
code/espurna/sensors/SM300D2Sensor.h View File

@ -1,7 +1,6 @@
// -----------------------------------------------------------------------------
// SmartMeasure SM300D2-VO2
// https://es.aliexpress.com/item/32984571140.html
// Uses SoftwareSerial library
// Copyright (C) 2021 by Xose Pérez <xose dot perez at gmail dot com>
// -----------------------------------------------------------------------------
@ -9,24 +8,17 @@
#pragma once
#include <SoftwareSerial.h>
#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<SoftwareSerial>(_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<SoftwareSerial> _serial;
Stream* _serial { nullptr };
};


+ 5
- 47
code/espurna/sensors/SenseAirSensor.h View File

@ -1,6 +1,5 @@
// -----------------------------------------------------------------------------
// SenseAir S8 CO2 Sensor
// Uses SoftwareSerial library
// Contribution by Yonsm Guo
// -----------------------------------------------------------------------------
@ -8,11 +7,8 @@
#pragma once
#include <SoftwareSerial.h>
#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;
_dirty = true;
}
void setTX(unsigned char pin_tx) {
if (_pin_tx == pin_tx) return;
_pin_tx = pin_tx;
void setPort(Stream* port) {
_serial = port;
_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<SoftwareSerial>(_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<SoftwareSerial> _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;
};


+ 5
- 46
code/espurna/sensors/T6613Sensor.h View File

@ -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 <xose dot perez at gmail dot com>
// -----------------------------------------------------------------------------
@ -9,11 +8,8 @@
#pragma once
#include <SoftwareSerial.h>
#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;
_dirty = true;
}
void setTX(unsigned char pin_tx) {
if (_pin_tx == pin_tx) return;
_pin_tx = pin_tx;
void setPort(Stream* port) {
_serial = port;
_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<SoftwareSerial>(_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<SoftwareSerial> _serial;
Stream* _serial { nullptr };
};


+ 5
- 42
code/espurna/sensors/V9261FSensor.h View File

@ -7,8 +7,6 @@
#pragma once
#include <SoftwareSerial.h>
#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<SoftwareSerial>(_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<SoftwareSerial> _serial;
Stream* _serial { nullptr };
using TimeSource = espurna::time::CoreClock;
static constexpr auto SyncInterval = TimeSource::duration { V9261F_SYNC_INTERVAL };


+ 34
- 4
code/espurna/terminal.cpp View File

@ -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<build::serialBufferSize()>;
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();


+ 22
- 17
code/espurna/tuya.cpp View File

@ -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<DataFrame> outputFrames;
template <typename T>
@ -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) {


+ 566
- 0
code/espurna/uart.cpp View File

@ -0,0 +1,566 @@
/*
UART MODULE
Copyright (C) 2022 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
*/
#include "espurna.h"
#if UART_SUPPORT
#include "uart.h"
#include "utils.h"
#if UART_SOFTWARE_SUPPORT
#include <SoftwareSerial.h>
#endif
#include <array>
#include <utility>
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<settings::options::Enumeration<driver::uart::Parity>, 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 <typename T>
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<Stream>;
struct BasePort {
Type type;
bool tx;
bool rx;
StreamPtr stream;
};
using BasePortPtr = std::unique_ptr<BasePort>;
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<SerialMode>(mode),
tx, invert);
if ((number == 0) && (build::uart0_swapped(tx, rx))) {
ptr->flush();
ptr->swap();
}
return std::make_unique<BasePort>(
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>(
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<size_t>;
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 [<ID>]"));
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>(
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

+ 40
- 0
code/espurna/uart.h View File

@ -0,0 +1,40 @@
/*
UART MODULE
Copyright (C) 2022 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
*/
#pragma once
#include <Arduino.h>
#include <memory>
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<Port>;
} // namespace uart
} // namespace driver
} // namespace espurna
espurna::driver::uart::PortPtr uartPort(size_t index);
void uartSetup();

+ 15
- 79
code/espurna/uartmqtt.cpp View File

@ -20,10 +20,6 @@ Support queueing and handling input without termination by Maxim Prokhorov <prok
#include <array>
#include <queue>
#if UART_MQTT_SOFTWARE_SERIAL
#include <SoftwareSerial.h>
#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<uint8_t, build::BufferSize>;
// Prefer smaller output instead of using `Serialized` directly
using Queue = std::queue<String>;
#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);
}


+ 1
- 1
code/espurna/web.cpp View File

@ -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() {


+ 0
- 1
code/platformio.ini View File

@ -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


+ 1
- 2
code/test/build/rfbridge.h View File

@ -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

Loading…
Cancel
Save