WIP Tuya MCU Bridge driver alternative to the TuyaMCU driver (#17626)

* WIP Tuya MCU Bridge driver alternative to the TuyaMCU driver

The main difference is this driver does not try and wire MCU data points
(Dps) into the tasmota power/light/etc controls. Instead each Dp ends up
being relayed directly to MQTT and the rules subsystem. If you want to
change the state of something wired up to the MCU, you send tuyamcu
specific commands to manipulate the Dp.

Each Dp gets a type and id specific topic that is sent to MQTT. eg, Dp
id 1 type bool looks like tele/%topic%/TUYAMCUBOOL1. To change state you
send a TuyaMCUBool1 command (ie, the command index value is used as the
DpId, which is nice and symmetrical) with the new value.

Currently Rules operate on TuyaMCU#TypeDpid things, eg, "rule1 on
TuyaMCU#Bool1 do power %value% endon" toggle the power on the tasmota
device when the state of the thing on the MCU changes too.

The most obviously missing stuff at the moment is:

- better relaying of the wifi/mqtt status to the MCU
- handling wifi reset requests from the MCU
- low power stuff?
- support for sending status updates and device info queries.
- restarting the tuya mcu state machine?
- restarting the rx state machine when no bytes are rxed for a period of
  time
- time sync

* shorten the log prefix to TYB (3 chars).

requested by arendst

* use the local definition for the SET_DP command.

reaching back to the existing tuyamcu code isnt reliable.

pointed out by arendst

* put the todo list in the code so it can be tracked

* check the wifi/mqtt state every second and update the mcu if it changes.

* fix rule processing when Dp state is changed from a cmnd.

rule processing was done as part of publishing the state, but publishing
the state when it was updated by a command only happened if So59 was
set. split rule processing out of publish and call them separately as
needed.

publish is now called from teleperiod, status updates from the MCU,
and from cmnds if so59 is set. rules are called from status updates from
the MCU and from cmnds.

Co-authored-by: David Gwynne <dlg@defeat.lan.animata.net>
This commit is contained in:
David Gwynne 2023-01-09 02:35:45 +10:00 committed by GitHub
parent 9073fe01c1
commit 17d68750d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1056 additions and 2 deletions

View File

@ -294,6 +294,8 @@ const be_const_member_t lv_gpio_constants[] = {
{ "TM1638STB", (int32_t) GPIO_TM1638STB },
{ "TUYA_RX", (int32_t) GPIO_TUYA_RX },
{ "TUYA_TX", (int32_t) GPIO_TUYA_TX },
{ "TUYAMCUBR_RX", (int32_t) GPIO_TUYAMCUBR_RX },
{ "TUYAMCUBR_TX", (int32_t) GPIO_TUYAMCUBR_TX },
{ "TX2X_TXD_BLACK", (int32_t) GPIO_TX2X_TXD_BLACK },
{ "TXD", (int32_t) GPIO_TXD },
{ "VINDRIKTNING_RX", (int32_t) GPIO_VINDRIKTNING_RX },

View File

@ -41,6 +41,7 @@
#ifndef TUYA_DIMMER_ID
#define TUYA_DIMMER_ID 0 // Default dimmer Id
#endif
#define USE_TUYAMCUBR
#undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code)
#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer (+2k code)
#undef USE_SONOFF_IFAN // Disable support for Sonoff iFan02 and iFan03 (+2k code)

View File

@ -204,6 +204,7 @@ enum UserSelectablePins {
GPIO_LD2410_TX, GPIO_LD2410_RX, // HLK-LD2410
GPIO_MBR_TX_ENA, GPIO_NRG_MBS_TX_ENA, // Modbus Bridge Serial Transmit Enable
GPIO_ME007_TRIG, GPIO_ME007_RX, // ME007 Serial/Trigger interface
GPIO_TUYAMCUBR_TX, GPIO_TUYAMCUBR_RX, // TuyaMCU Bridge
GPIO_SENSOR_END };
// Error as warning to rethink GPIO usage with max 2045
@ -455,6 +456,7 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_LD2410_TX "|" D_SENSOR_LD2410_RX "|"
D_SENSOR_MBR_TX_ENA "|" D_SENSOR_NRG_MBS_TX_ENA "|"
D_SENSOR_ME007_TRIG "|" D_SENSOR_ME007_RX "|"
D_SENSOR_TUYAMCUBR_TX "|" D_SENSOR_TUYAMCUBR_RX "|"
;
const char kSensorNamesFixed[] PROGMEM =
@ -1056,6 +1058,10 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_MIEL_HVAC_TX), // Mitsubishi Electric HVAC TX pin
AGPIO(GPIO_MIEL_HVAC_RX), // Mitsubishi Electric HVAC RX pin
#endif
#ifdef USE_TUYAMCUBR
AGPIO(GPIO_TUYAMCUBR_TX),
AGPIO(GPIO_TUYAMCUBR_RX),
#endif
#ifdef USE_WIEGAND
AGPIO(GPIO_WIEGAND_D0), // Date line D0 of Wiegand devices
AGPIO(GPIO_WIEGAND_D1), // Date line D1 of Wiegand devices

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Дебитомер"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Cabal"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Portata"
#define D_SENSOR_ME007_TRIG "ME007 - Tri"
#define D_SENSOR_ME007_RX "ME007 - RX"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -917,6 +917,8 @@
#define D_SENSOR_FLOWRATEMETER "Flowrate"
#define D_SENSOR_ME007_TRIG "ME007 Tri"
#define D_SENSOR_ME007_RX "ME007 Rx"
#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx"
#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx"
// Units
#define D_UNIT_AMPERE "安培"

View File

@ -533,6 +533,7 @@
#define USE_TUYA_MCU // Add support for Tuya Serial MCU
#define TUYA_DIMMER_ID 0 // Default dimmer Id
#define USE_TUYA_TIME // Add support for Set Time in Tuya MCU
#define USE_TUYAMCUBR // Add support for TuyaMCU Bridge
#define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code)
#define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer (+2k code)
#define USE_SONOFF_IFAN // Add support for Sonoff iFan02 and iFan03 (+2k code)
@ -822,6 +823,7 @@
#define USE_TASMOTA_CLIENT_SERIAL_SPEED 57600 // Depends on the sketch that is running on the Uno/Pro Mini
//#define USE_OPENTHERM // Add support for OpenTherm (+15k code)
//#define USE_MIEL_HVAC // Add support for Mitsubishi Electric HVAC serial interface (+5k code)
//#define USE_TUYAMCUBR // Add support for TuyaMCU Bridge
//#define USE_PROJECTOR_CTRL // Add support for LCD/DLP Projector serial control interface (+2k code)
// #define USE_PROJECTOR_CTRL_NEC // Use codes for NEC
// #define USE_PROJECTOR_CTRL_OPTOMA // Use codes for OPTOMA

View File

@ -867,7 +867,10 @@ void ResponseAppendFeatures(void)
#if defined(USE_I2C) && defined(USE_PCA9632)
feature9 |= 0x00002000;
#endif
// feature9 |= 0x00004000;
#ifdef USE_TUYAMCUBR
feature9 |= 0x00004000; // xdrv_65_tuyamcubr.ino
#endif
// feature9 |= 0x00008000;
// feature9 |= 0x00010000;

Binary file not shown.

View File

@ -24,10 +24,14 @@
#define XDRV_44 44
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
#ifndef CTASSERT
#define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \
__attribute__((__unused__))
#endif
#define MIEL_HVAC_LOGNAME "MiElHVAC"

View File

@ -0,0 +1,980 @@
/*
* xdrv_65_tuyamcubr.ino - TuyaMCU Bridge support for Tasmota
*/
/*
* Copyright (C) 2023 David Gwynne <david@gwynne.id.au>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef USE_TUYAMCUBR
/*
* Tuya MCU Bridge
*/
/*
* TODO:
*
* - handling wifi reset requests from the MCU
* - low power stuff?
* - support for (re)sending status updates and device info queries
* - supporting the raw and string Dp types
* - restarting the tuya mcu state machine?
* - restarting the rx state machine when no bytes are rxed for a while
* - time sync
*/
#define XDRV_65 65
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
#ifndef CTASSERT
#define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \
__attribute__((__unused__))
#endif
#define TUYAMCUBR_LOGNAME "TYB"
#define TUYAMCUBR_FMT(_fmt) PSTR(TUYAMCUBR_LOGNAME ": " _fmt)
#define D_CMND_TUYAMCUBR_PREFIX "TuyaMCU"
#define D_CMND_TUYAMCUBR_DATA_RAW "Raw"
#define D_CMND_TUYAMCUBR_DATA_BOOL "Bool"
#define D_CMND_TUYAMCUBR_DATA_VALUE "Value"
#define D_CMND_TUYAMCUBR_DATA_STRING "String"
#define D_CMND_TUYAMCUBR_DATA_ENUM "Enum"
#include <TasmotaSerial.h>
struct tuyamcubr_header {
uint8_t header[2];
#define TUYAMCUBR_H_ONE 0x55
#define TUYAMCUBR_H_TWO 0xaa
uint8_t version;
uint8_t command;
uint16_t datalen;
};
CTASSERT(sizeof(struct tuyamcubr_header) == 6);
#define TUYAMCUBR_CMD_HEARTBEAT 0x00
#define TUYAMCUBR_CMD_PRODUCT 0x01
#define TUYAMCUBR_CMD_MODE 0x02
#define TUYAMCUBR_CMD_WIFI_STATE 0x03
#define TUYAMCUBR_CMD_WIFI_RESET 0x04
#define TUYAMCUBR_CMD_WIFI_SELECT 0x05
#define TUYAMCUBR_CMD_SET_DP 0x06
#define TUYAMCUBR_CMD_STATE 0x07
#define TUYAMCUBR_CMD_QUERY_STATE 0x08
#define TUYAMCUBR_CMD_INIT_UPGRADE 0x0a
#define TUYAMCUBR_CMD_UPGRADE_PKG 0x0b
#define TUYAMCUBR_CMD_SET_TIME 0x1c
/* wifi state */
#define TUYAMCUBR_NETWORK_STATUS_1 0x00 /* pairing in EZ mode */
#define TUYAMCUBR_NETWORK_STATUS_2 0x01 /* pairing in AP mode */
#define TUYAMCUBR_NETWORK_STATUS_3 0x02 /* WiFi */
#define TUYAMCUBR_NETWORK_STATUS_4 0x03 /* WiFi + router */
#define TUYAMCUBR_NETWORK_STATUS_5 0x04 /* WiFi + router + cloud*/
#define TUYAMCUBR_NETWORK_STATUS_6 0x05 /* low power mode */
#define TUYAMCUBR_NETWORK_STATUS_7 0x06 /* pairing in EZ+AP mode */
/* set dp */
struct tuyamcubr_data_header {
uint8_t dpid;
uint8_t type;
uint16_t len;
/* followed by len bytes */
};
CTASSERT(sizeof(struct tuyamcubr_data_header) == 4);
#define TUYAMCUBR_DATA_TYPE_RAW 0x00
#define TUYAMCUBR_DATA_TYPE_BOOL 0x01
#define TUYAMCUBR_DATA_TYPE_VALUE 0x02
#define TUYAMCUBR_DATA_TYPE_STRING 0x03
#define TUYAMCUBR_DATA_TYPE_ENUM 0x04
struct tuyamcubr_data_type {
const char *t_name;
int t_len;
uint32_t t_max;
uint32_t (*t_rd)(const uint8_t *);
void (*t_wr)(uint8_t *, uint32_t);
};
static uint32_t
tuyamcubr_rd_u8(const uint8_t *b)
{
return (*b);
}
static void
tuyamcubr_wr_u8(uint8_t *b, uint32_t v)
{
*b = v;
}
static uint32_t
tuyamcubr_rd_u32(const uint8_t *b)
{
uint32_t be32;
memcpy(&be32, b, sizeof(be32));
return (ntohl(be32));
}
static void
tuyamcubr_wr_u32(uint8_t *b, uint32_t v)
{
uint32_t be32 = htonl(v);
memcpy(b, &be32, sizeof(be32));
}
static const struct tuyamcubr_data_type tuyamcubr_data_types[] = {
[TUYAMCUBR_DATA_TYPE_RAW] = {
.t_name = D_CMND_TUYAMCUBR_DATA_RAW,
.t_len = -1,
},
[TUYAMCUBR_DATA_TYPE_BOOL] = {
.t_name = D_CMND_TUYAMCUBR_DATA_BOOL,
.t_len = 1,
.t_max = 1,
.t_rd = tuyamcubr_rd_u8,
.t_wr = tuyamcubr_wr_u8,
},
[TUYAMCUBR_DATA_TYPE_VALUE] = {
.t_name = D_CMND_TUYAMCUBR_DATA_VALUE,
.t_len = sizeof(uint32_t),
.t_max = 0xffffffff,
.t_rd = tuyamcubr_rd_u32,
.t_wr = tuyamcubr_wr_u32,
},
[TUYAMCUBR_DATA_TYPE_STRING] = {
.t_name = D_CMND_TUYAMCUBR_DATA_STRING,
.t_len = -1,
},
[TUYAMCUBR_DATA_TYPE_ENUM] = {
.t_name = D_CMND_TUYAMCUBR_DATA_ENUM,
.t_len = 1,
.t_max = 0xff,
.t_rd = tuyamcubr_rd_u8,
.t_wr = tuyamcubr_wr_u8,
},
};
static inline const struct tuyamcubr_data_type *
tuyamcubr_find_data_type(uint8_t type)
{
const struct tuyamcubr_data_type *dt;
if (type > nitems(tuyamcubr_data_types))
return (NULL);
dt = &tuyamcubr_data_types[type];
if (dt->t_name == NULL)
return (NULL);
return (dt);
}
static inline uint8_t
tuyamcubr_cksum_fini(uint8_t sum)
{
/*
* "Start from the header, add up all the bytes, and then divide
* the sum by 256 to get the remainder."
*
* If we accumulate bytes in a uint8_t, we get this for free.
*/
return (sum);
}
enum tuyamcubr_parser_state {
TUYAMCUBR_P_START,
TUYAMCUBR_P_HEADER,
TUYAMCUBR_P_VERSION,
TUYAMCUBR_P_COMMAND,
TUYAMCUBR_P_LEN1,
TUYAMCUBR_P_LEN2,
TUYAMCUBR_P_DATA,
TUYAMCUBR_P_CKSUM,
TUYAMCUBR_P_SKIP,
TUYAMCUBR_P_SKIP_CKSUM,
};
//#ifdef ESP8266
//#define TUYAMCUBR_BUFLEN 256
//#else
#define TUYAMCUBR_BUFLEN 1024
//#endif
struct tuyamcubr_parser {
enum tuyamcubr_parser_state p_state;
unsigned int p_deadline;
uint8_t p_version;
uint8_t p_command;
uint8_t p_sum;
uint16_t p_len;
uint8_t p_off;
uint8_t p_data[TUYAMCUBR_BUFLEN];
};
struct tuyamcubr_dp {
STAILQ_ENTRY(tuyamcubr_dp) dp_entry;
uint8_t dp_id;
uint8_t dp_type;
uint32_t dp_value;
};
STAILQ_HEAD(tuyamcubr_dps, tuyamcubr_dp);
enum tuyamcubr_state {
TUYAMCUBR_S_START,
TUYAMCUBR_S_PROD_INFO,
TUYAMCUBR_S_MODE,
TUYAMCUBR_S_NET_STATUS,
TUYAMCUBR_S_RUNNING,
};
struct tuyamcubr_softc {
TasmotaSerial *sc_serial;
struct tuyamcubr_parser sc_parser;
enum tuyamcubr_state sc_state;
unsigned int sc_deadline;
unsigned int sc_waiting;
uint8_t sc_network_status;
unsigned int sc_clock;
struct tuyamcubr_dps sc_dps;
};
static struct tuyamcubr_softc *tuyamcubr_sc = nullptr;
struct tuyamcubr_recv_command {
uint8_t r_command;
void (*r_func)(struct tuyamcubr_softc *, uint8_t,
const uint8_t *, size_t);
};
static void tuyamcubr_recv_heartbeat(struct tuyamcubr_softc *, uint8_t,
const uint8_t *, size_t);
static void tuyamcubr_recv_product_info(struct tuyamcubr_softc *, uint8_t,
const uint8_t *, size_t);
static void tuyamcubr_recv_mode(struct tuyamcubr_softc *, uint8_t,
const uint8_t *, size_t);
static void tuyamcubr_recv_net_status(struct tuyamcubr_softc *, uint8_t,
const uint8_t *, size_t);
static void tuyamcubr_recv_status(struct tuyamcubr_softc *, uint8_t,
const uint8_t *, size_t);
static const struct tuyamcubr_recv_command tuyamcubr_recv_commands[] = {
{ TUYAMCUBR_CMD_HEARTBEAT, tuyamcubr_recv_heartbeat },
{ TUYAMCUBR_CMD_PRODUCT, tuyamcubr_recv_product_info },
{ TUYAMCUBR_CMD_MODE, tuyamcubr_recv_mode },
{ TUYAMCUBR_CMD_WIFI_STATE, tuyamcubr_recv_net_status },
{ TUYAMCUBR_CMD_STATE, tuyamcubr_recv_status },
};
static void
tuyamcubr_recv(struct tuyamcubr_softc *sc, const struct tuyamcubr_parser *p)
{
const struct tuyamcubr_recv_command *r;
const uint8_t *data = p->p_data;
size_t len = p->p_len;
size_t i;
if (len > 0) {
AddLog(LOG_LEVEL_DEBUG,
TUYAMCUBR_FMT("recv version 0x%02x command 0x%02x: %*_H"),
p->p_version, p->p_command, len, data);
} else {
AddLog(LOG_LEVEL_DEBUG,
TUYAMCUBR_FMT("recv version 0x%02x command 0x%02x"),
p->p_version, p->p_command);
}
for (i = 0; i < nitems(tuyamcubr_recv_commands); i++) {
r = &tuyamcubr_recv_commands[i];
if (r->r_command == p->p_command) {
r->r_func(sc, p->p_version, data, len);
return;
}
}
/* unhandled command? */
}
static enum tuyamcubr_parser_state
tuyamcubr_parse(struct tuyamcubr_softc *sc, uint8_t byte)
{
struct tuyamcubr_parser *p = &sc->sc_parser;
enum tuyamcubr_parser_state nstate = p->p_state;
switch (p->p_state) {
case TUYAMCUBR_P_START:
if (byte != TUYAMCUBR_H_ONE)
return (TUYAMCUBR_P_START);
/* reset state */
p->p_sum = 0;
nstate = TUYAMCUBR_P_HEADER;
break;
case TUYAMCUBR_P_HEADER:
if (byte != TUYAMCUBR_H_TWO)
return (TUYAMCUBR_P_START);
nstate = TUYAMCUBR_P_VERSION;
break;
case TUYAMCUBR_P_VERSION:
p->p_version = byte;
nstate = TUYAMCUBR_P_COMMAND;
break;
case TUYAMCUBR_P_COMMAND:
p->p_command = byte;
nstate = TUYAMCUBR_P_LEN1;
break;
case TUYAMCUBR_P_LEN1:
p->p_len = (uint16_t)byte << 8;
nstate = TUYAMCUBR_P_LEN2;
break;
case TUYAMCUBR_P_LEN2:
p->p_len |= (uint16_t)byte;
p->p_off = 0;
if (p->p_len > sizeof(p->p_data)) {
AddLog(LOG_LEVEL_DEBUG,
TUYAMCUBR_FMT("skipping command %02x"
", too much data %zu/%zu"), p->p_command,
p->p_len, sizeof(p->p_data));
return (TUYAMCUBR_P_SKIP);
}
nstate = (p->p_len > 0) ? TUYAMCUBR_P_DATA : TUYAMCUBR_P_CKSUM;
break;
case TUYAMCUBR_P_DATA:
p->p_data[p->p_off++] = byte;
if (p->p_off >= p->p_len)
nstate = TUYAMCUBR_P_CKSUM;
break;
case TUYAMCUBR_P_CKSUM:
if (tuyamcubr_cksum_fini(p->p_sum) != byte) {
AddLog(LOG_LEVEL_DEBUG,
TUYAMCUBR_FMT("checksum failed, skipping"));
return (TUYAMCUBR_P_START);
}
tuyamcubr_recv(sc, p);
/* this message is done, wait for another */
return (TUYAMCUBR_P_START);
case TUYAMCUBR_P_SKIP:
if (++p->p_off >= p->p_len)
return (TUYAMCUBR_P_SKIP_CKSUM);
return (nstate);
case TUYAMCUBR_P_SKIP_CKSUM:
return (TUYAMCUBR_P_START);
}
p->p_sum += byte;
return (nstate);
}
static uint8_t
tuyamcubr_write(struct tuyamcubr_softc *sc, const void *data, size_t len)
{
TasmotaSerial *serial = sc->sc_serial;
const uint8_t *bytes = (const uint8_t *)data;
uint8_t cksum = 0;
size_t i;
for (i = 0; i < len; i++) {
uint8_t b = bytes[i];
serial->write(b);
cksum += b;
}
return (cksum);
}
static void
tuyamcubr_send(struct tuyamcubr_softc *sc, uint8_t command,
const void *data, size_t len)
{
TasmotaSerial *serial = sc->sc_serial;
struct tuyamcubr_header h = {
.header = { TUYAMCUBR_H_ONE, TUYAMCUBR_H_TWO },
.version = 0x00,
.command = command,
.datalen = htons(len),
};
uint8_t cksum = 0;
if (len) {
AddLog(LOG_LEVEL_DEBUG,
TUYAMCUBR_FMT("send version 0x%02x command 0x%02x: %*_H"),
h.version, h.command, len, data);
} else {
AddLog(LOG_LEVEL_DEBUG,
TUYAMCUBR_FMT("send version 0x%02x command 0x%02x"),
h.version, h.command);
}
cksum += tuyamcubr_write(sc, &h, sizeof(h));
if (len > 0)
cksum += tuyamcubr_write(sc, data, len);
cksum = tuyamcubr_cksum_fini(cksum);
serial->write(cksum);
serial->flush();
}
/* if we have polymorphic funcions then we may as well (ab)use them */
static void
tuyamcubr_send(struct tuyamcubr_softc *sc, uint8_t command)
{
tuyamcubr_send(sc, command, NULL, 0);
}
static void
tuyamcubr_heartbeat(struct tuyamcubr_softc *sc, unsigned int deadline)
{
sc->sc_deadline += deadline;
tuyamcubr_send(sc, TUYAMCUBR_CMD_HEARTBEAT);
}
static struct tuyamcubr_dp *
tuyamcubr_find_dp(struct tuyamcubr_softc *sc, uint32_t index, uint8_t type)
{
struct tuyamcubr_dp *dp;
if (index > 0xff)
return (NULL);
STAILQ_FOREACH(dp, &sc->sc_dps, dp_entry) {
if (dp->dp_id == index &&
dp->dp_type == type)
return (dp);
}
return (NULL);
}
static void
tuyamcubr_cmnd_data(struct tuyamcubr_softc *sc, uint8_t type)
{
const struct tuyamcubr_data_type *dt = &tuyamcubr_data_types[type];
struct {
struct tuyamcubr_data_header h;
uint8_t value[4]; /* only up to 4 bytes */
} data;
size_t len = sizeof(data.h) + dt->t_len;
struct tuyamcubr_dp *dp;
dp = tuyamcubr_find_dp(sc, XdrvMailbox.index, type);
if (dp == NULL) {
ResponseCmndChar_P(PSTR("Unknown DpId"));
return;
}
if (XdrvMailbox.data_len == 0) {
ResponseCmndNumber(dp->dp_value);
return;
}
if (XdrvMailbox.payload < 0x00 || XdrvMailbox.payload > dt->t_max) {
ResponseCmndChar_P(PSTR("Invalid"));
return;
}
dp->dp_value = XdrvMailbox.payload;
data.h.dpid = dp->dp_id;
data.h.type = dp->dp_type;
data.h.len = htons(dt->t_len);
dt->t_wr(data.value, dp->dp_value);
tuyamcubr_send(sc, TUYAMCUBR_CMD_SET_DP, &data, len);
tuyamcubr_rule_dp(sc, dp);
ResponseCmndNumber(dp->dp_value);
/* SetOption59 */
if (Settings->flag3.hass_tele_on_power)
tuyamcubr_publish_dp(sc, dp);
}
static void
tuyamcubr_cmnd_data_bool(void)
{
tuyamcubr_cmnd_data(tuyamcubr_sc, TUYAMCUBR_DATA_TYPE_BOOL);
}
static void
tuyamcubr_cmnd_data_value(void)
{
tuyamcubr_cmnd_data(tuyamcubr_sc, TUYAMCUBR_DATA_TYPE_VALUE);
}
static void
tuyamcubr_cmnd_data_enum(void)
{
tuyamcubr_cmnd_data(tuyamcubr_sc, TUYAMCUBR_DATA_TYPE_ENUM);
}
static void
tuyamcubr_rule_dp(struct tuyamcubr_softc *sc, const struct tuyamcubr_dp *dp)
{
const struct tuyamcubr_data_type *dt =
&tuyamcubr_data_types[dp->dp_type];
/* XXX this only handles numeric types */
Response_P(PSTR("{\"%s\":{\"%s%u\":%u}}"),
D_CMND_TUYAMCUBR_PREFIX,
dt->t_name, dp->dp_id,
dp->dp_value);
XdrvRulesProcess(0);
}
static void
tuyamcubr_publish_dp(struct tuyamcubr_softc *sc, const struct tuyamcubr_dp *dp)
{
const struct tuyamcubr_data_type *dt =
&tuyamcubr_data_types[dp->dp_type];
char topic[64]; /* how long is a (bit of) string? */
/* XXX this only handles numeric types */
snprintf(topic, sizeof(topic), PSTR("%s%s%u"),
D_CMND_TUYAMCUBR_PREFIX, dt->t_name, dp->dp_id);
Response_P(PSTR("%u"), dp->dp_value);
MqttPublishPrefixTopic_P(TELE, topic);
}
static void
tuyamcubr_publish(struct tuyamcubr_softc *sc)
{
struct tuyamcubr_dp *dp;
STAILQ_FOREACH(dp, &sc->sc_dps, dp_entry)
tuyamcubr_publish_dp(sc, dp);
}
static void
tuyamcubr_send_heartbeat(struct tuyamcubr_softc *sc, unsigned int deadline)
{
sc->sc_deadline += deadline;
tuyamcubr_send(sc, TUYAMCUBR_CMD_HEARTBEAT);
}
static void
tuyamcubr_recv_heartbeat(struct tuyamcubr_softc *sc, uint8_t v,
const uint8_t *data, size_t datalen)
{
/* check the data? */
switch (sc->sc_state) {
case TUYAMCUBR_S_START:
sc->sc_state = TUYAMCUBR_S_PROD_INFO;
tuyamcubr_send(sc, TUYAMCUBR_CMD_PRODUCT);
break;
case TUYAMCUBR_S_RUNNING:
sc->sc_waiting = 0;
break;
default:
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("unexpected heartbeat in state %u"),
sc->sc_state);
break;
}
}
static void
tuyamcubr_recv_product_info(struct tuyamcubr_softc *sc, uint8_t v,
const uint8_t *data, size_t datalen)
{
AddLog(LOG_LEVEL_INFO, TUYAMCUBR_FMT("MCU Product ID: %.*s"),
datalen, data);
switch (sc->sc_state) {
case TUYAMCUBR_S_PROD_INFO:
sc->sc_state = TUYAMCUBR_S_MODE;
tuyamcubr_send(sc, TUYAMCUBR_CMD_MODE);
break;
default:
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("unexpected product info in state %u"),
sc->sc_state);
break;
}
}
static void
tuyamcubr_recv_mode(struct tuyamcubr_softc *sc, uint8_t v,
const uint8_t *data, size_t datalen)
{
switch (sc->sc_state) {
case TUYAMCUBR_S_MODE:
switch (datalen) {
case 0:
AddLog(LOG_LEVEL_INFO,
TUYAMCUBR_FMT("MCU Mode: Coordinated"));
break;
case 2:
AddLog(LOG_LEVEL_INFO, TUYAMCUBR_FMT("MCU Mode"
": Status GPIO%u, Reset GPIO%u"),
data[0], data[1]);
sc->sc_state = TUYAMCUBR_S_RUNNING;
tuyamcubr_send(sc, TUYAMCUBR_CMD_QUERY_STATE);
return;
default:
AddLog(LOG_LEVEL_ERROR, TUYAMCUBR_FMT("MCU Mode"
": unexpected data length %zu"), datalen);
break;
}
sc->sc_state = TUYAMCUBR_S_NET_STATUS;
tuyamcubr_send(sc, TUYAMCUBR_CMD_WIFI_STATE,
&sc->sc_network_status, sizeof(sc->sc_network_status));
break;
default:
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("unexpected product info in state %u"),
sc->sc_state);
break;
}
}
static void
tuyamcubr_recv_net_status(struct tuyamcubr_softc *sc, uint8_t v,
const uint8_t *data, size_t datalen)
{
switch (sc->sc_state) {
case TUYAMCUBR_S_NET_STATUS:
sc->sc_state = TUYAMCUBR_S_RUNNING;
tuyamcubr_send(sc, TUYAMCUBR_CMD_QUERY_STATE);
break;
default:
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("unexpected product info in state %u"),
sc->sc_state);
break;
}
}
static void
tuyamcubr_recv_status(struct tuyamcubr_softc *sc, uint8_t v,
const uint8_t *data, size_t datalen)
{
const struct tuyamcubr_data_type *dt;
struct tuyamcubr_dp *dp;
struct tuyamcubr_data_header h;
size_t len;
const uint8_t *b;
uint32_t value;
/* take dp status updates at any time */
do {
if (datalen < sizeof(h)) {
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("status header short %zu<%zu"),
datalen, sizeof(h));
return;
}
memcpy(&h, data, sizeof(h));
data += sizeof(h);
datalen -= sizeof(h);
len = ntohs(h.len);
if (datalen < len) {
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("status data short %zu<%zu"),
datalen, len);
return;
}
b = data;
data += len;
datalen -= len;
dt = tuyamcubr_find_data_type(h.type);
if (dt == NULL ||
dt->t_len == -1) { /* XXX revisit this */
AddLog(LOG_LEVEL_INFO,
TUYAMCUBR_FMT("DpId %u unsupported type 0x%02x"),
h.dpid, h.type);
continue;
}
if (len != dt->t_len) {
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("%s%s%u: unexpected len %zu"),
D_CMND_TUYAMCUBR_PREFIX, dt->t_name, len);
continue;
}
value = dt->t_rd(b);
if (value > dt->t_max) {
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("%s%s%u: unexpected value %u>%u"),
D_CMND_TUYAMCUBR_PREFIX, dt->t_name, value,
dt->t_max);
continue;
}
dp = tuyamcubr_find_dp(sc, h.dpid, h.type);
if (dp == NULL) {
dp = (struct tuyamcubr_dp *)malloc(sizeof(*dp));
if (dp == NULL) {
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("%s%s%u no memory"),
D_CMND_TUYAMCUBR_PREFIX,
tuyamcubr_data_types[h.type], h.dpid);
continue;
}
dp->dp_id = h.dpid;
dp->dp_type = h.type;
STAILQ_INSERT_TAIL(&sc->sc_dps, dp, dp_entry);
} else if (dp->dp_value == value) {
/* nop */
continue;
}
dp->dp_value = value;
tuyamcubr_rule_dp(sc, dp);
tuyamcubr_publish_dp(sc, dp);
} while (datalen > 0);
}
static void
tuyamcubr_tick(struct tuyamcubr_softc *sc, unsigned int ms)
{
int diff;
sc->sc_clock += ms;
diff = sc->sc_clock - sc->sc_deadline;
if (diff < 0) {
/* deadline hasn't been reached, nothing to do */
return;
}
switch (sc->sc_state) {
case TUYAMCUBR_S_START:
tuyamcubr_send_heartbeat(sc, 3000);
break;
case TUYAMCUBR_S_RUNNING:
tuyamcubr_send_heartbeat(sc, 15000);
if (sc->sc_waiting) {
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("no heartbeat response"));
/* XXX restart? */
}
sc->sc_waiting = 1;
break;
}
}
static void
tuyamcubr_every_1sec(struct tuyamcubr_softc *sc)
{
/* start with the assumption that wifi is configured */
uint8_t network_status = TUYAMCUBR_NETWORK_STATUS_3;
if (MqttIsConnected()) {
/* the device is connected to the "cloud" */
network_status = TUYAMCUBR_NETWORK_STATUS_5;
} else {
switch (WifiState()) {
case WIFI_MANAGER:
/* Pairing in AP mode */
network_status = TUYAMCUBR_NETWORK_STATUS_2;
break;
case WIFI_RESTART:
/* WiFi + router */
network_status = TUYAMCUBR_NETWORK_STATUS_4;
break;
}
}
if (sc->sc_network_status != network_status) {
sc->sc_network_status = network_status;
if (sc->sc_state == TUYAMCUBR_S_RUNNING) {
tuyamcubr_send(sc, TUYAMCUBR_CMD_WIFI_STATE,
&network_status, sizeof(network_status));
}
}
}
static void
tuyamcubr_pre_init(void)
{
struct tuyamcubr_softc *sc;
int baudrate;
/*
* SetOption97 - Set Baud rate for TuyaMCU serial communication
* (0 = 9600 or 1 = 115200)
*/
baudrate = (Settings->flag4.tuyamcu_baudrate) ? 115200 : 9600;
if (!PinUsed(GPIO_TUYAMCUBR_TX) || !PinUsed(GPIO_TUYAMCUBR_RX))
return;
sc = (struct tuyamcubr_softc *)calloc(1, sizeof(*sc));
if (sc == NULL) {
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("unable to allocate state"));
return;
}
sc->sc_parser.p_state = TUYAMCUBR_P_START;
sc->sc_state = TUYAMCUBR_S_START;
sc->sc_clock = 0;
sc->sc_network_status = (WifiState() == WIFI_MANAGER) ?
TUYAMCUBR_NETWORK_STATUS_2 : TUYAMCUBR_NETWORK_STATUS_3;
STAILQ_INIT(&sc->sc_dps);
sc->sc_serial = new TasmotaSerial(Pin(GPIO_TUYAMCUBR_RX),
Pin(GPIO_TUYAMCUBR_TX), 2);
if (!sc->sc_serial->begin(baudrate)) {
AddLog(LOG_LEVEL_ERROR,
TUYAMCUBR_FMT("unable to begin serial (baudrate %d)"),
baudrate);
goto del;
}
if (sc->sc_serial->hardwareSerial())
ClaimSerial();
/* commit */
tuyamcubr_sc = sc;
/* kick the state machine off */
tuyamcubr_tick(sc, 0);
return;
del:
delete sc->sc_serial;
free:
free(sc);
}
static void
tuyamcubr_loop(struct tuyamcubr_softc *sc)
{
TasmotaSerial *serial = sc->sc_serial;
while (serial->available()) {
yield();
sc->sc_parser.p_state = tuyamcubr_parse(sc, serial->read());
}
}
/*
* Interface
*/
static const char tuyamcubr_cmnd_names[] PROGMEM =
D_CMND_TUYAMCUBR_PREFIX
"|" D_CMND_TUYAMCUBR_DATA_BOOL
"|" D_CMND_TUYAMCUBR_DATA_VALUE
"|" D_CMND_TUYAMCUBR_DATA_ENUM
;
static void (*const tuyamcubr_cmnds[])(void) PROGMEM = {
&tuyamcubr_cmnd_data_bool,
&tuyamcubr_cmnd_data_value,
&tuyamcubr_cmnd_data_enum,
};
bool
Xdrv65(uint32_t function)
{
bool result = false;
struct tuyamcubr_softc *sc;
switch (function) {
case FUNC_PRE_INIT:
tuyamcubr_pre_init();
return (false);
}
sc = tuyamcubr_sc;
if (sc == NULL)
return (false);
switch (function) {
case FUNC_LOOP:
tuyamcubr_loop(sc);
break;
#if 0
case FUNC_SET_DEVICE_POWER:
result = tuyamcubr_set_power(sc);
break;
#endif
case FUNC_EVERY_100_MSECOND:
tuyamcubr_tick(sc, 100);
break;
case FUNC_EVERY_50_MSECOND:
case FUNC_EVERY_200_MSECOND:
case FUNC_EVERY_250_MSECOND:
break;
case FUNC_EVERY_SECOND:
tuyamcubr_every_1sec(sc);
break;
#if 0
case FUNC_JSON_APPEND:
tuyamcubr_sensor(sc);
break;
#endif
case FUNC_AFTER_TELEPERIOD:
tuyamcubr_publish(sc);
break;
case FUNC_COMMAND:
result = DecodeCommand(tuyamcubr_cmnd_names, tuyamcubr_cmnds);
break;
}
return (result);
}
#endif // USE_TUYAMCUBR

View File

@ -290,7 +290,7 @@ a_features = [[
"USE_SGP40","USE_LUXV30B","USE_CANSNIFFER","USE_QMC5883L",
"USE_MODBUS_ENERGY","USE_SHELLY_PRO","USE_DALI","USE_BP1658CJ",
"USE_DINGTIAN_RELAY","USE_HMC5883L","USE_LD2410","USE_ME007",
"USE_DISPLAY_TM1650","USE_PCA9632","","",
"USE_DISPLAY_TM1650","USE_PCA9632","USE_TUYAMCUBR","",
"","","","",
"","","","",
"","","","",

View File

@ -209,6 +209,8 @@ ZIGBEE_RST = GPIO_ZIGBEE_RST
DYP_RX = GPIO_DYP_RX
MIEL_HVAC_TX = GPIO_MIEL_HVAC_TX
MIEL_HVAC_RX = GPIO_MIEL_HVAC_RX
TUYAMCUBR_TX = GPIO_TUYAMCUBR_TX
TUYAMCUBR_RX = GPIO_TUYAMCUBR_RX
WE517_TX = GPIO_WE517_TX
WE517_RX = GPIO_WE517_RX
AS608_TX = GPIO_AS608_TX