From 2dbd1ef97387fa20242a9f58ace80592ed8abca3 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Sat, 16 Jan 2021 15:39:33 +0100
Subject: [PATCH 001/186] Add support for 24/26/32/34 bit RFID Wiegand
Add support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) by Sigurd Leuther (#3647, #10565)
---
CHANGELOG.md | 1 +
RELEASENOTES.md | 1 +
tasmota/language/af_AF.h | 2 +
tasmota/language/bg_BG.h | 2 +
tasmota/language/cs_CZ.h | 2 +
tasmota/language/de_DE.h | 2 +
tasmota/language/el_GR.h | 2 +
tasmota/language/en_GB.h | 2 +
tasmota/language/es_ES.h | 2 +
tasmota/language/fr_FR.h | 2 +
tasmota/language/he_HE.h | 2 +
tasmota/language/hu_HU.h | 2 +
tasmota/language/it_IT.h | 2 +
tasmota/language/ko_KO.h | 2 +
tasmota/language/nl_NL.h | 2 +
tasmota/language/pl_PL.h | 2 +
tasmota/language/pt_BR.h | 2 +
tasmota/language/pt_PT.h | 2 +
tasmota/language/ro_RO.h | 2 +
tasmota/language/ru_RU.h | 2 +
tasmota/language/sk_SK.h | 2 +
tasmota/language/sv_SE.h | 2 +
tasmota/language/tr_TR.h | 2 +
tasmota/language/uk_UA.h | 2 +
tasmota/language/vi_VN.h | 2 +
tasmota/language/zh_CN.h | 2 +
tasmota/language/zh_TW.h | 2 +
tasmota/my_user_config.h | 3 +-
tasmota/support_features.ino | 4 +-
tasmota/tasmota_template.h | 6 +
tasmota/xsns_82_wiegand.ino | 429 +++++++++++++++++++++++++++++++++++
tools/decode-status.py | 4 +-
32 files changed, 493 insertions(+), 5 deletions(-)
create mode 100644 tasmota/xsns_82_wiegand.ino
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 79e41be9e..743b0fcaa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file.
- Support for up to 4 I2C SEESAW_SOIL Capacitance & Temperature sensors by Peter Franck (#10481)
- ESP8266 Support for 2MB and up linker files with 1MB and up LittleFS
- ESP32 support for TLS MQTT using BearSSL (same as ESP8266)
+- Support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) by Sigurd Leuther (#3647)
### Breaking Changed
- ESP32 switch from default SPIFFS to default LittleFS file system loosing current (zigbee) files
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index b3f8e38a0..136d679f3 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -78,6 +78,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Support for disabling 38kHz IR modulation using ``#define IR_SEND_USE_MODULATION false`` [#10301](https://github.com/arendst/Tasmota/issues/10301)
- Support for SPI display driver for ST7789 TFT by Gerhard Mutz [#9037](https://github.com/arendst/Tasmota/issues/9037)
- Support for time proportioned (``#define USE_TIMEPROP``) and optional PID (``#define USE_PID``) relay control [#10412](https://github.com/arendst/Tasmota/issues/10412)
+- Support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) by Sigurd Leuther [#3647](https://github.com/arendst/Tasmota/issues/3647)
- Support rotary encoder on Shelly Dimmer [#10407](https://github.com/arendst/Tasmota/issues/10407#issuecomment-756240920)
- Support character `#` to be replaced by `space`-character in command ``Publish`` topic [#10258](https://github.com/arendst/Tasmota/issues/10258)
- Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin [#8630](https://github.com/arendst/Tasmota/issues/8630)
diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h
index f6fb4a662..b13396097 100644
--- a/tasmota/language/af_AF.h
+++ b/tasmota/language/af_AF.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h
index f00b30d2c..b69e1b81c 100644
--- a/tasmota/language/bg_BG.h
+++ b/tasmota/language/bg_BG.h
@@ -781,6 +781,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h
index d529a4e16..6ed6b3b63 100644
--- a/tasmota/language/cs_CZ.h
+++ b/tasmota/language/cs_CZ.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h
index 071bfaaa0..635318b54 100644
--- a/tasmota/language/de_DE.h
+++ b/tasmota/language/de_DE.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h
index 9f1342b0b..718ee89d8 100644
--- a/tasmota/language/el_GR.h
+++ b/tasmota/language/el_GR.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h
index 11acc25fb..9d0a73354 100644
--- a/tasmota/language/en_GB.h
+++ b/tasmota/language/en_GB.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h
index bb9bdd4d0..b35de9d0b 100644
--- a/tasmota/language/es_ES.h
+++ b/tasmota/language/es_ES.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h
index 709788ecc..5dfc7c5ba 100644
--- a/tasmota/language/fr_FR.h
+++ b/tasmota/language/fr_FR.h
@@ -778,6 +778,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "CarteSD CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h
index 1ed935184..b36412ac1 100644
--- a/tasmota/language/he_HE.h
+++ b/tasmota/language/he_HE.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h
index b8d4d5347..62f2131ce 100644
--- a/tasmota/language/hu_HU.h
+++ b/tasmota/language/hu_HU.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h
index 6791d768a..391f166a7 100644
--- a/tasmota/language/it_IT.h
+++ b/tasmota/language/it_IT.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 - CS"
#define D_SENSOR_SSD1331_DC "SSD1331 - DC"
#define D_SENSOR_SDCARD_CS "Scheda SD - CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h
index a51333856..7593261df 100644
--- a/tasmota/language/ko_KO.h
+++ b/tasmota/language/ko_KO.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h
index 185e3a0c0..0eea3bb75 100644
--- a/tasmota/language/nl_NL.h
+++ b/tasmota/language/nl_NL.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h
index 9da13e702..2d614d037 100644
--- a/tasmota/language/pl_PL.h
+++ b/tasmota/language/pl_PL.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h
index f48328e4c..ecb4a8afc 100644
--- a/tasmota/language/pt_BR.h
+++ b/tasmota/language/pt_BR.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h
index f7893127c..6f7a11f01 100644
--- a/tasmota/language/pt_PT.h
+++ b/tasmota/language/pt_PT.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h
index b4641fca8..270b251d7 100644
--- a/tasmota/language/ro_RO.h
+++ b/tasmota/language/ro_RO.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h
index 9d19762a4..e37ed4e8f 100644
--- a/tasmota/language/ru_RU.h
+++ b/tasmota/language/ru_RU.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "А"
diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h
index 5c46b4b89..2eebf1331 100644
--- a/tasmota/language/sk_SK.h
+++ b/tasmota/language/sk_SK.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h
index a6e6aa1f6..ed97b84e8 100644
--- a/tasmota/language/sv_SE.h
+++ b/tasmota/language/sv_SE.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h
index 21ac79065..659dcf15b 100644
--- a/tasmota/language/tr_TR.h
+++ b/tasmota/language/tr_TR.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h
index b32761e2e..a350cef9b 100644
--- a/tasmota/language/uk_UA.h
+++ b/tasmota/language/uk_UA.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "А"
diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h
index e5d7d6c35..86eb6eb2e 100644
--- a/tasmota/language/vi_VN.h
+++ b/tasmota/language/vi_VN.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h
index 2041c1483..d0a29c3c1 100644
--- a/tasmota/language/zh_CN.h
+++ b/tasmota/language/zh_CN.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "安"
diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h
index 46814ce24..02d898d5a 100644
--- a/tasmota/language/zh_TW.h
+++ b/tasmota/language/zh_TW.h
@@ -782,6 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
// Units
#define D_UNIT_AMPERE "安培"
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 12f47d073..f62d2a6b7 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -710,6 +710,7 @@
#define MAX31865_REF_RES 430 // Reference resistor (Usually 430Ω for a PT100, 4300Ω for a PT1000)
#define MAX31865_PTD_BIAS 0 // To calibrate your not-so-good PTD
//#define USE_LMT01 // Add support for TI LMT01 temperature sensor, count pulses on single GPIO (+0k5 code)
+//#define USE_WIEGAND // Add support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) (+1k7 code)
// -- IR Remote features - all protocols from IRremoteESP8266 --------------------------
// IR Full Protocols mode is activated through platform.io only.
@@ -767,8 +768,6 @@
#define USE_ZIGBEE_MAXTIME_SENSOR 60*60 // 1h
#define USE_ZIGBEE_MAXTIME_LIGHT 60*60 // 1h
-
-
// -- Other sensors/drivers -----------------------
//#define USE_TM1638 // Add support for TM1638 switches copying Switch1 .. Switch8 (+1k code)
diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino
index 3a3597f2a..49c158653 100644
--- a/tasmota/support_features.ino
+++ b/tasmota/support_features.ino
@@ -704,7 +704,9 @@ void ResponseAppendFeatures(void)
#ifdef USE_SEESAW_SOIL
feature7 |= 0x02000000; // xsns_81_seesaw_soil.ino
#endif
-// feature7 |= 0x04000000;
+#ifdef USE_WIEGAND
+ feature7 |= 0x04000000; // xsns_82_wiegand.ino
+#endif
// feature7 |= 0x08000000;
// feature7 |= 0x10000000;
diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h
index f2dbae6c4..40e90cccd 100644
--- a/tasmota/tasmota_template.h
+++ b/tasmota/tasmota_template.h
@@ -146,6 +146,7 @@ enum UserSelectablePins {
GPIO_ROT1A_NP, GPIO_ROT1B_NP, // Rotary switch
GPIO_ADC_PH, // Analog PH Sensor
GPIO_BS814_CLK, GPIO_BS814_DAT, // Holtek BS814A2 touch ctrlr
+ GPIO_WIEGAND_D0, GPIO_WIEGAND_D1, // Wiegand Data lines
GPIO_SENSOR_END };
enum ProgramSelectablePins {
@@ -312,6 +313,7 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_ROTARY " A_n|" D_SENSOR_ROTARY " B_n|"
D_SENSOR_ADC_PH "|"
D_SENSOR_BS814_CLK "|" D_SENSOR_BS814_DAT "|"
+ D_SENSOR_WIEGAND_D0 "|" D_SENSOR_WIEGAND_D1 "|"
;
const char kSensorNamesFixed[] PROGMEM =
@@ -735,6 +737,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_WIEGAND
+ AGPIO(GPIO_WIEGAND_D0), // Date line D0 of Wiegand devices
+ AGPIO(GPIO_WIEGAND_D1), // Date line D1 of Wiegand devices
+#endif
/*-------------------------------------------------------------------------------------------*\
* ESP32 specifics
diff --git a/tasmota/xsns_82_wiegand.ino b/tasmota/xsns_82_wiegand.ino
new file mode 100644
index 000000000..57ebfba2a
--- /dev/null
+++ b/tasmota/xsns_82_wiegand.ino
@@ -0,0 +1,429 @@
+/*
+ xsns_82_wiegand.ino - Support for Wiegand Interface 125kHz NFC Tag Reader for Tasmota
+
+ Copyright (C) 2021 Sigurd Leuther and Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef USE_WIEGAND
+/*********************************************************************************************\
+ MQTT:
+ %prefix%/%topic%/SENSOR = {"Time":"2021-01-13T12:30:38","Wiegand":{"UID":"rfid tag"}}
+
+ Domoticz:
+ The nvalue will be always 0 and the svalue will contain the tag UID as string.
+\*********************************************************************************************/
+#warning **** Wiegand interface enabled ****
+
+#define XSNS_82 82
+
+#define WIEGAND_BIT_TIMEOUT 25 //time to be wait after last bit detected.
+// use only a randomly generate RFID for testing. using #define will save some space in the final code
+// DEV_WIEGAND_TEST_MODE 1 : testing with random rfid without hardware connected, but GPIOs set correctly
+// DEV_WIEGAND_TEST_MODE 2 : testing with hardware corretly connected.
+//
+#define DEV_WIEGAND_TEST_MODE 0
+
+#ifdef DEV_WIEGAND_TEST_MODE
+ #if (DEV_WIEGAND_TEST_MODE==0)
+ #elif (DEV_WIEGAND_TEST_MODE==1)
+ #warning "Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 1 (Random RFID)"
+ #elif (DEV_WIEGAND_TEST_MODE==2)
+ #warning "Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 2 (Hardware connected)"
+ #else
+ #warning "Wiegand Interface compiled with unknown mode"
+ #endif
+#endif
+
+class Wiegand {
+ public:
+
+ Wiegand(void);
+ void Init(void);
+ void ScanForTag(void);
+#ifdef USE_WEBSERVER
+ void Show(void);
+#endif
+
+ private:
+
+ uint64_t HexStringToDec(uint64_t);
+ uint64_t CheckAndConvertRfid(uint64_t,uint16_t);
+ char translateEnterEscapeKeyPress(char);
+ uint8_t CalculateParities(uint64_t, int);
+ bool WiegandConversion (void);
+ static void handleD0Interrupt(void);
+ static void handleD1Interrupt(void);
+
+ uint64_t rfid;
+ uint8_t tagSize;
+
+ static volatile uint64_t rfidBuffer;
+ static volatile uint16_t bitCount;
+ static volatile uint32_t lastFoundTime;
+ static volatile uint8_t timeOut;
+ bool isInit = false;
+
+#if (DEV_WIEGAND_TEST_MODE)==1
+ uint64_t GetRandomRfid(uint8_t);
+#endif
+};
+
+Wiegand* oWiegand = new Wiegand();
+uint8_t scanDelay;
+
+volatile uint64_t Wiegand::rfidBuffer;
+volatile uint16_t Wiegand::bitCount;
+volatile uint32_t Wiegand::lastFoundTime;
+volatile uint8_t Wiegand::timeOut;
+
+Wiegand::Wiegand() {
+ rfid = 0;
+ lastFoundTime = 0;
+ tagSize = 0;
+ rfidBuffer = 0;
+ bitCount = 0 ;
+ timeOut = 0;
+ isInit= false;
+}
+
+#if (DEV_WIEGAND_TEST_MODE)==1
+uint64_t Wiegand::GetRandomRfid(uint8_t tag_size=34) {
+ //todo add support for 4 and 8 bit keyboard "tags"
+ uint64_t result = (uint32_t)HwRandom();
+ uint8_t parities = 0;
+ bitCount = tag_size;
+ timeOut=millis() - WIEGAND_BIT_TIMEOUT;
+ result = result << 32;
+ result += HwRandom();
+
+ switch (tag_size){
+ case 24:
+ result = (result & 0x7FFFFE) >>1;
+ break;
+ case 26:
+ result = (result & 0x1FFFFFE) >>1;
+ break;
+ case 32:
+ result = (result & 0x7FFFFFFE) >>1;
+ break;
+ case 34:
+ result = (result & 0x3FFFFFFFE) >>1;
+ break;
+ default:
+ break;
+ }
+ parities = CalculateParities(result, tag_size);
+
+ result = (result << 1) | (parities & 0x01); //set LSB parity
+ if (parities & 0x80) { //MSB parity is 1
+ switch (tag_size) {
+ case 24:
+ result |= 0x800000;
+ break;
+ case 26:
+ result |= 0x2000000;
+ break;
+ case 32:
+ result |= 0x80000000;
+ break;
+ case 34:
+ result |= 0x400000000;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return result;
+}
+#endif
+
+void ICACHE_RAM_ATTR Wiegand::handleD1Interrupt() { // receive a 1 bit. (D0=high & D1=low)
+ rfidBuffer = (rfidBuffer << 1) | 1; // leftshift + 1 bit
+ bitCount++; //increment the counter
+ lastFoundTime = millis(); // last time bit found
+}
+
+void ICACHE_RAM_ATTR Wiegand::handleD0Interrupt() { // receive a 0 bit. (D0=low & D1=high)
+ rfidBuffer = rfidBuffer << 1; // leftshift the 0 bit is now at the end of rfidBuffer
+ bitCount++; //increment the counter
+ lastFoundTime = millis(); //last time bit found
+}
+
+void Wiegand::Init() {
+ isInit = false;
+ if (PinUsed(GPIO_WIEGAND_D0) && PinUsed(GPIO_WIEGAND_D1)) { //only start, if the Wiegang pins are
+ #if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: Init()"));
+ #endif
+ pinMode(Pin(GPIO_WIEGAND_D0), INPUT_PULLUP);
+ pinMode(Pin(GPIO_WIEGAND_D1), INPUT_PULLUP);
+ attachInterrupt(Pin(GPIO_WIEGAND_D0), handleD0Interrupt, FALLING);
+ attachInterrupt(Pin(GPIO_WIEGAND_D1), handleD1Interrupt, FALLING);
+ isInit = true; // helps to run only if correctly setup
+ #if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: Testmode")); // for tests without reader attaiched
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: D0:%u"),Pin(GPIO_WIEGAND_D0));
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: D1:%u"),Pin(GPIO_WIEGAND_D1));
+ #else
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: D0=%u, D1=%u"),Pin(GPIO_WIEGAND_D0), Pin(GPIO_WIEGAND_D1));
+ #endif
+ }
+ #if (DEV_WIEGAND_TEST_MODE)>0
+ else {
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: no GPIOs."));
+ }
+ #endif
+}
+
+uint64_t Wiegand::CheckAndConvertRfid(uint64_t rfidIn, uint16_t bitcount) {
+ uint8_t evenParityBit = 0;
+ uint8_t oddParityBit = (uint8_t) (rfidIn & 0x1); // last bit = odd parity
+ uint8_t calcParity = 0;
+ switch (bitcount) {
+ case 24:
+ evenParityBit = (rfidIn & 0x800000) ? 0x80 : 0;
+ rfidIn = (rfidIn & 0x7FFFFE) >>1;
+ break;
+
+ case 26:
+ evenParityBit = (rfidIn & 0x2000000) ? 0x80 : 0;
+ rfidIn = (rfidIn & 0x1FFFFFE) >>1;
+ break;
+
+ case 32:
+ evenParityBit = (rfidIn & 0x80000000) ? 0x80 : 0;
+ rfidIn = (rfidIn & 0x7FFFFFFE) >>1;
+ break;
+
+ case 34:
+ evenParityBit = (rfidIn & 0x400000000) ? 0x80 : 0;
+ rfidIn = (rfidIn & 0x3FFFFFFFE) >>1;
+ break;
+
+ default:
+ break;
+ }
+ calcParity = CalculateParities(rfidIn, bitCount); //ckeck result on http://www.ccdesignworks.com/wiegand_calc.htm with raw tag as input
+ if (calcParity != (evenParityBit | oddParityBit)) { // Paritybit is wrong
+ rfidIn=0;
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: %llu parity error"), rfidIn);
+ }
+ #if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: even (left) parity: %u "), (evenParityBit>>7));
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: even (calc) parity: %u "), (calcParity & 0x80)>>7);
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: odd (right) parity: %u "), oddParityBit);
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: odd (calc) parity: %u "), (calcParity & 0x01));
+ #endif
+ return rfidIn;
+}
+
+uint8_t Wiegand::CalculateParities(uint64_t tagWithoutParities, int tag_size=26) {
+ //tag_size is the size of the final tag including the 2 parity bits
+ //so length if the tagWithoutParities should be (tag_size-2) !! That will be not profed and
+ //lead to wrong results if the input value is larger!
+ //calculated start parity (even) will be returned as bit 8
+ //calculated end parity (odd) will be returned as bit 1
+ uint8_t retValue=0;
+ tag_size -= 2;
+ if (tag_size<=0) { return retValue; } //prohibit div zero exception and other wrong inputs
+ uint8_t parity=1; //check for odd parity on LSB
+ for (uint8_t i=0; i<(tag_size/2); i++) {
+ parity^=(tagWithoutParities & 1);
+ tagWithoutParities>>=1;
+ }
+ retValue |= parity;
+
+ parity=0; //check for even parity on MSB
+ while (tagWithoutParities) {
+ parity^=(tagWithoutParities & 1);
+ tagWithoutParities>>=1;
+ }
+ retValue |= (parity<<7);
+
+ return retValue;
+}
+
+char Wiegand::translateEnterEscapeKeyPress(char oKeyPressed) {
+ switch(oKeyPressed) {
+ case 0x0b: // 11 or * key
+ return 0x0d; // 13 or ASCII ENTER
+
+ case 0x0a: // 10 or # key
+ return 0x1b; // 27 or ASCII ESCAPE
+
+ default:
+ return oKeyPressed;
+ }
+}
+
+bool Wiegand::WiegandConversion ()
+{
+ bool bRet = false;
+ unsigned long nowTick = millis();
+ if ((nowTick - lastFoundTime) > WIEGAND_BIT_TIMEOUT) //last bit found is WIEGAND_BIT_TIMEOUT ms ago
+ {
+ #if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: raw tag: %llu "), rfidBuffer);
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: bit count: %u "), bitCount);
+ #endif
+ if ((bitCount==4)||(bitCount==8)||(bitCount==24)||(bitCount==26)||(bitCount==32)||(bitCount==34)) {
+ if ((bitCount==24)||(bitCount==26)||(bitCount==32)||(bitCount==34)) {
+ // 24,26,32,34-bit Wiegand codes
+ rfid = CheckAndConvertRfid( rfidBuffer, bitCount);
+ tagSize=bitCount;
+ bitCount=0;
+ rfidBuffer=0;
+ bRet=true;
+ }
+ if (bitCount==4) {
+ // 4-bit Wiegand codes for keypads
+ rfid = (int)translateEnterEscapeKeyPress(rfidBuffer & 0x0000000F);
+ tagSize = bitCount;
+ bitCount = 0;
+ rfidBuffer = 0;
+ bRet=true;
+ }
+ if (bitCount==8){
+ // 8-bit Wiegand codes for keypads with integrity
+ // 8-bit Wiegand keyboard data, high nibble is the "NOT" of low nibble
+ // eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001
+ char highNibble = (rfidBuffer & 0xf0) >>4;
+ char lowNibble = (rfidBuffer & 0x0f);
+ if (lowNibble == (~highNibble & 0x0f)) // check if low nibble matches the "NOT" of high nibble.
+ {
+ rfid = (int)translateEnterEscapeKeyPress(lowNibble);
+ bRet=true;
+ }
+ else {
+ lastFoundTime=nowTick;
+ bRet=false;
+ }
+ tagSize=bitCount;
+ bitCount=0;
+ rfidBuffer=0;
+ }
+ }
+ else {
+ // time reached but unknown bitCount, clear and start again
+ lastFoundTime=nowTick;
+ bitCount=0;
+ rfidBuffer=0;
+ bRet=false;
+ }
+ }
+ else{
+ bRet=false; // watching time not finished
+ }
+ #if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: tag out: %llu "), rfid);
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: tag size: %u"), tagSize);
+ #endif
+ return bRet;
+}
+
+void Wiegand::ScanForTag() {
+
+ if (!isInit) { return;}
+ #if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: ScanForTag()."));
+ #if (DEV_WIEGAND_TEST_MODE==1)
+ switch (millis() %4 ) {
+ case 0:
+ rfidBuffer = GetRandomRfid(24);
+ break;
+ case 1:
+ rfidBuffer = GetRandomRfid(26);
+ break;
+ case 2:
+ rfidBuffer = GetRandomRfid(32);
+ break;
+ case 3:
+ rfidBuffer = GetRandomRfid(34);
+ break;
+ default:
+ rfidBuffer = GetRandomRfid(34);
+ break;
+ }
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: raw generated: %lX"), rfidBuffer); // for tests without reader attaiched
+ #endif
+ #endif
+ if (bitCount > 0) {
+ uint64_t oldTag = rfid;
+ bool newKey = WiegandConversion();
+ #if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: previous tag: %llu"), oldTag);
+ #endif
+ if(newKey && (oldTag != rfid)) {
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: new= %llu"), rfid);
+ }
+ else
+ { AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: prev= %llu"), rfid);}
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: bits= %u"), tagSize);
+ ResponseTime_P(PSTR(",\"Wiegand\":{\"UID\":\"%0llu\"}}"), rfid);
+ MqttPublishTeleSensor();
+ }
+}
+
+#ifdef USE_WEBSERVER
+void Wiegand::Show(void) {
+ if (!isInit) { return; }
+ WSContentSend_PD(PSTR("{s}Wiegand UID{m}%llu {e}"), rfid);
+ #if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO,PSTR("WIE: Tag: %llu"), rfid);
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: %u bits"), bitCount);
+ #endif
+}
+#endif // USE_WEBSERVER
+
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xsns82(byte function) {
+ bool result = false;
+
+ switch (function) {
+ case FUNC_INIT:
+ oWiegand->Init();
+ scanDelay = 1;
+ break;
+
+ case FUNC_EVERY_250_MSECOND: // some tags need more time, don't try shorter period
+ #if (DEV_WIEGAND_TEST_MODE)==1
+ if (scanDelay>=4) // give a second because of the log entries to be send.
+ #else
+ if (scanDelay>=2) // only run every (delay * 250 ms) (every 250ms is too fast for some tags)
+ #endif
+ {
+ oWiegand->ScanForTag();
+ scanDelay = 1;
+ }
+ else {
+ scanDelay++;
+ }
+ break;
+
+ #ifdef USE_WEBSERVER
+ case FUNC_WEB_SENSOR:
+ oWiegand->Show();
+ break;
+ #endif // USE_WEBSERVER
+ }
+ return result;
+}
+#endif // USE_WIEGAND
\ No newline at end of file
diff --git a/tools/decode-status.py b/tools/decode-status.py
index 8d8bfc7ba..f6b6f923d 100755
--- a/tools/decode-status.py
+++ b/tools/decode-status.py
@@ -244,7 +244,7 @@ a_features = [[
"USE_SHELLY_DIMMER","USE_RC522","USE_FTC532","USE_DISPLAY_EPAPER_42",
"USE_DISPLAY_ILI9488","USE_DISPLAY_SSD1351","USE_DISPLAY_RA8876","USE_DISPLAY_ST7789",
"USE_DISPLAY_SSD1331","USE_UFILESYS","USE_TIMEPROP","USE_PID",
- "USE_BS814A2","USE_SEESAW_SOIL","","",
+ "USE_BS814A2","USE_SEESAW_SOIL","USE_WIEGAND","",
"","","",""
]]
@@ -273,7 +273,7 @@ else:
obj = json.load(fp)
def StartDecode():
- print ("\n*** decode-status.py v20210111 by Theo Arends and Jacek Ziolkowski ***")
+ print ("\n*** decode-status.py v20210116 by Theo Arends and Jacek Ziolkowski ***")
# print("Decoding\n{}".format(obj))
From e47065a66d3555132dab750b03fa236ccad8596f Mon Sep 17 00:00:00 2001
From: Matt
Date: Sat, 16 Jan 2021 15:14:36 +0000
Subject: [PATCH 002/186] Put back SetOption40 support.
---
tasmota/support_button.ino | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/tasmota/support_button.ino b/tasmota/support_button.ino
index 53fb218ba..473183665 100644
--- a/tasmota/support_button.ino
+++ b/tasmota/support_button.ino
@@ -270,6 +270,15 @@ void ButtonHandler(void) {
ExecuteCommand(scmnd, SRC_BUTTON);
}
}
+ else
+ {
+ if (Settings.param[P_HOLD_IGNORE] > 0) { // SetOption40 (0) - Do not ignore button hold
+ if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) {
+ Button.hold_timer[button_index] = 0; // Reset button hold counter to stay below hold trigger
+ Button.press_counter[button_index] = 0; // Discard button press to disable functionality
+ }
+ }
+ }
}
}
}
From a8235842384c56cbe2e2c3e27b19ba00914233eb Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Sat, 16 Jan 2021 16:49:37 +0100
Subject: [PATCH 003/186] ESP32 use 4K RSA for TLS
---
platformio_tasmota32.ini | 2 ++
1 file changed, 2 insertions(+)
diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini
index a41222bb4..717ca03a5 100644
--- a/platformio_tasmota32.ini
+++ b/platformio_tasmota32.ini
@@ -86,6 +86,8 @@ build_flags = ${esp_defaults.build_flags}
-Dsint16_t=int16_t
-Dmemcpy_P=memcpy
-Dmemcmp_P=memcmp
+ ;for TLS we can afford compiling for 4K RSA keys
+ -DUSE_4K_RSA
[core32]
From a90eb18f1e90fe7e9ef1cb430e089d2041cec41d Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Sat, 16 Jan 2021 17:11:45 +0100
Subject: [PATCH 004/186] Fix Web UI that would not change TLS mode
---
tasmota/xdrv_02_mqtt.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino
index 9a0acba75..6324a584b 100644
--- a/tasmota/xdrv_02_mqtt.ino
+++ b/tasmota/xdrv_02_mqtt.ino
@@ -1315,7 +1315,7 @@ void MqttSaveSettings(void)
WebGetArg("ml", tmp, sizeof(tmp));
Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp);
#ifdef USE_MQTT_TLS
- Mqtt.mqtt_tls = Webserver->hasArg("b3"); // SetOption102 - Enable MQTT TLS
+ Settings.flag4.mqtt_tls = Webserver->hasArg("b3"); // SetOption102 - Enable MQTT TLS
#endif
WebGetArg("mc", tmp, sizeof(tmp));
SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp);
From f52f26f5663047695e64dd80ad4de2dcb751e48c Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Sat, 16 Jan 2021 18:07:01 +0100
Subject: [PATCH 005/186] Added ``USE_MQTT_TLS_DROP_OLD_FINGERPRINT`` compile
time option to drop old (less secure) TLS fingerprint (#10584)
Co-authored-by: Stephan Hadinger
---
CHANGELOG.md | 1 +
tasmota/WiFiClientSecureLightBearSSL.cpp | 4 ++++
tasmota/my_user_config.h | 3 +++
3 files changed, 8 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 743b0fcaa..44b5e00c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file.
- ESP8266 Support for 2MB and up linker files with 1MB and up LittleFS
- ESP32 support for TLS MQTT using BearSSL (same as ESP8266)
- Support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) by Sigurd Leuther (#3647)
+- Added ``USE_MQTT_TLS_DROP_OLD_FINGERPRINT`` compile time option to drop old (less secure) TLS fingerprint
### Breaking Changed
- ESP32 switch from default SPIFFS to default LittleFS file system loosing current (zigbee) files
diff --git a/tasmota/WiFiClientSecureLightBearSSL.cpp b/tasmota/WiFiClientSecureLightBearSSL.cpp
index 6d7ed37b4..60534c369 100755
--- a/tasmota/WiFiClientSecureLightBearSSL.cpp
+++ b/tasmota/WiFiClientSecureLightBearSSL.cpp
@@ -804,6 +804,7 @@ extern "C" {
return 0;
}
+#ifndef USE_MQTT_TLS_DROP_OLD_FINGERPRINT
// No match under new algorithm, do some basic checking on the key.
//
// RSA keys normally have an e value of 65537, which is three bytes long.
@@ -838,6 +839,9 @@ extern "C" {
pubkeyfingerprint_pubkey_fingerprint(xc, false);
return 0;
+#else // USE_TLS_OLD_FINGERPRINT_COMPAT
+ return 1; // no match, error
+#endif // USE_TLS_OLD_FINGERPRINT_COMPAT
} else {
// Default (no validation at all) or no errors in prior checks = success.
return 0;
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index f62d2a6b7..8f3fa5a89 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -401,6 +401,9 @@
// #define USE_MQTT_AWS_IOT // [Deprecated] Enable MQTT for AWS IoT - requires a private key (+11.9k code, +0.4k mem)
// Note: you need to generate a private key + certificate per device and update 'tasmota/tasmota_aws_iot.cpp'
// Full documentation here: https://github.com/arendst/Tasmota/wiki/AWS-IoT
+// #define USE_MQTT_TLS_DROP_OLD_FINGERPRINT // If you use fingerprint (i.e. not CA) validation, the algorithm changed to a more secure one.
+ // Any valid fingerprint with the old algo will be automatically updated to the new algo.
+ // Enable this if you want to disable the old algo check, which should be more secure
// for USE_4K_RSA (support for 4096 bits certificates, instead of 2048), you need to uncommend `-DUSE_4K_RSA` in `build_flags` from `platform.ini` or `platform_override.ini`
// -- Telegram Protocol ---------------------------
From ba12d8911ef1bef59aba98821d02e12184ebca5b Mon Sep 17 00:00:00 2001
From: sle
Date: Sun, 17 Jan 2021 11:39:21 +0100
Subject: [PATCH 006/186] only in case of valid key do action. Issue#10585 and
block very slow incoming signals
---
tasmota/xsns_82_wiegand.ino | 135 +++++++++++++++++++-----------------
1 file changed, 71 insertions(+), 64 deletions(-)
diff --git a/tasmota/xsns_82_wiegand.ino b/tasmota/xsns_82_wiegand.ino
index 57ebfba2a..59505a5fc 100644
--- a/tasmota/xsns_82_wiegand.ino
+++ b/tasmota/xsns_82_wiegand.ino
@@ -274,65 +274,72 @@ bool Wiegand::WiegandConversion ()
{
bool bRet = false;
unsigned long nowTick = millis();
- if ((nowTick - lastFoundTime) > WIEGAND_BIT_TIMEOUT) //last bit found is WIEGAND_BIT_TIMEOUT ms ago
- {
- #if (DEV_WIEGAND_TEST_MODE)>0
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: raw tag: %llu "), rfidBuffer);
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: bit count: %u "), bitCount);
- #endif
- if ((bitCount==4)||(bitCount==8)||(bitCount==24)||(bitCount==26)||(bitCount==32)||(bitCount==34)) {
- if ((bitCount==24)||(bitCount==26)||(bitCount==32)||(bitCount==34)) {
- // 24,26,32,34-bit Wiegand codes
- rfid = CheckAndConvertRfid( rfidBuffer, bitCount);
- tagSize=bitCount;
- bitCount=0;
- rfidBuffer=0;
- bRet=true;
- }
- if (bitCount==4) {
- // 4-bit Wiegand codes for keypads
- rfid = (int)translateEnterEscapeKeyPress(rfidBuffer & 0x0000000F);
- tagSize = bitCount;
- bitCount = 0;
- rfidBuffer = 0;
- bRet=true;
- }
- if (bitCount==8){
- // 8-bit Wiegand codes for keypads with integrity
- // 8-bit Wiegand keyboard data, high nibble is the "NOT" of low nibble
- // eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001
- char highNibble = (rfidBuffer & 0xf0) >>4;
- char lowNibble = (rfidBuffer & 0x0f);
- if (lowNibble == (~highNibble & 0x0f)) // check if low nibble matches the "NOT" of high nibble.
- {
- rfid = (int)translateEnterEscapeKeyPress(lowNibble);
- bRet=true;
- }
- else {
- lastFoundTime=nowTick;
- bRet=false;
- }
- tagSize=bitCount;
- bitCount=0;
- rfidBuffer=0;
- }
- }
- else {
- // time reached but unknown bitCount, clear and start again
- lastFoundTime=nowTick;
+//add a maximum wait time for new bits
+ unsigned long diffTicks = nowTick - lastFoundTime;
+ if ((diffTicks > WIEGAND_BIT_TIMEOUT) && (diffTicks >= 5000 )) { //max. 5 secs between 2 bits comming in
+ bitCount=0;
+ rfidBuffer=0;
+ lastFoundTime=nowTick;
+ return bRet;
+ }
+ if (diffTicks > WIEGAND_BIT_TIMEOUT) { //last bit found is WIEGAND_BIT_TIMEOUT ms ago
+ #if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: raw tag: %llu "), rfidBuffer);
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: bit count: %u "), bitCount);
+ #endif
+ if ((bitCount==4)||(bitCount==8)||(bitCount==24)||(bitCount==26)||(bitCount==32)||(bitCount==34)) {
+ if ((bitCount==24)||(bitCount==26)||(bitCount==32)||(bitCount==34)) {
+ // 24,26,32,34-bit Wiegand codes
+ rfid = CheckAndConvertRfid( rfidBuffer, bitCount);
+ tagSize=bitCount;
+ bitCount=0;
+ rfidBuffer=0;
+ bRet=true;
+ }
+ if (bitCount==4) {
+ // 4-bit Wiegand codes for keypads
+ rfid = (int)translateEnterEscapeKeyPress(rfidBuffer & 0x0000000F);
+ tagSize = bitCount;
+ bitCount = 0;
+ rfidBuffer = 0;
+ bRet=true;
+ }
+ if (bitCount==8){
+ // 8-bit Wiegand codes for keypads with integrity
+ // 8-bit Wiegand keyboard data, high nibble is the "NOT" of low nibble
+ // eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001
+ char highNibble = (rfidBuffer & 0xf0) >>4;
+ char lowNibble = (rfidBuffer & 0x0f);
+ if (lowNibble == (~highNibble & 0x0f)) // check if low nibble matches the "NOT" of high nibble.
+ {
+ rfid = (int)translateEnterEscapeKeyPress(lowNibble);
+ bRet=true;
+ }
+ else {
+ lastFoundTime=nowTick;
+ bRet=false;
+ }
+ tagSize=bitCount;
bitCount=0;
rfidBuffer=0;
- bRet=false;
}
}
- else{
- bRet=false; // watching time not finished
+ else {
+ // time reached but unknown bitCount, clear and start again
+ lastFoundTime=nowTick;
+ bitCount=0;
+ rfidBuffer=0;
+ bRet=false;
}
- #if (DEV_WIEGAND_TEST_MODE)>0
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: tag out: %llu "), rfid);
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: tag size: %u"), tagSize);
- #endif
- return bRet;
+ }
+ else{
+ bRet=false; // watching time not finished
+ }
+ #if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: tag out: %llu "), rfid);
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: tag size: %u"), tagSize);
+ #endif
+ return bRet;
}
void Wiegand::ScanForTag() {
@@ -363,19 +370,19 @@ void Wiegand::ScanForTag() {
#endif
if (bitCount > 0) {
uint64_t oldTag = rfid;
- bool newKey = WiegandConversion();
+ bool validKey = WiegandConversion();
#if (DEV_WIEGAND_TEST_MODE)>0
AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: previous tag: %llu"), oldTag);
#endif
- if(newKey && (oldTag != rfid)) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: new= %llu"), rfid);
- }
- else
- { AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: prev= %llu"), rfid);}
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: bits= %u"), tagSize);
- ResponseTime_P(PSTR(",\"Wiegand\":{\"UID\":\"%0llu\"}}"), rfid);
- MqttPublishTeleSensor();
- }
+ // only in case of valid key do action. Issue#10585
+ if(validKey) {
+ if (oldTag != rfid) { AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: new= %llu"), rfid); }
+ else { AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: prev= %llu"), rfid); }
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: bits= %u"), tagSize);
+ ResponseTime_P(PSTR(",\"Wiegand\":{\"UID\":\"%0llu\"}}"), rfid);
+ MqttPublishTeleSensor();
+ }
+ }
}
#ifdef USE_WEBSERVER
From 8c5d4ea26d349ef7c4460a933299e2a6aa67500c Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Sun, 17 Jan 2021 12:30:20 +0100
Subject: [PATCH 007/186] fix esp32 download large files
---
tasmota/xdrv_50_filesystem.ino | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/tasmota/xdrv_50_filesystem.ino b/tasmota/xdrv_50_filesystem.ino
index 0b7fc53d2..fb12e3b9d 100644
--- a/tasmota/xdrv_50_filesystem.ino
+++ b/tasmota/xdrv_50_filesystem.ino
@@ -402,6 +402,7 @@ void UFSDelete(void) {
* Web support
\*********************************************************************************************/
+
#ifdef USE_WEBSERVER
const char UFS_WEB_DIR[] PROGMEM =
@@ -459,6 +460,8 @@ void UfsDirectory(void) {
if (UfsDownloadFile(cp)) {
// is directory
strcpy(ufs_path, cp);
+ } else {
+ return;
}
}
@@ -588,7 +591,9 @@ void UfsListDir(char *path, uint8_t depth) {
}
}
-
+#ifdef ESP32
+#define ESP32_DOWNLOAD_TASK
+#endif // ESP32
uint8_t UfsDownloadFile(char *file) {
File download_file;
@@ -609,7 +614,7 @@ uint8_t UfsDownloadFile(char *file) {
return 1;
}
-#ifdef ESP8266
+#ifndef ESP32_DOWNLOAD_TASK
WiFiClient download_Client;
uint32_t flen = download_file.size();
@@ -649,10 +654,10 @@ uint8_t UfsDownloadFile(char *file) {
}
download_file.close();
download_Client.stop();
-#endif // esp8266
+#endif // ESP32_DOWNLOAD_TASK
-#ifdef ESP32
+#ifdef ESP32_DOWNLOAD_TASK
download_file.close();
if (download_busy == true) {
@@ -664,16 +669,16 @@ uint8_t UfsDownloadFile(char *file) {
char *path = (char*)malloc(128);
strcpy(path,file);
xTaskCreatePinnedToCore(donload_task, "DT", 6000, (void*)path, 3, NULL, 1);
-#endif // ESP32
+#endif // ESP32_DOWNLOAD_TASK
return 0;
}
-#ifdef ESP32
+#ifdef ESP32_DOWNLOAD_TASK
#ifndef DOWNLOAD_SIZE
#define DOWNLOAD_SIZE 4096
-#endif
+#endif // DOWNLOAD_SIZE
void donload_task(void *path) {
File download_file;
WiFiClient download_Client;
@@ -714,7 +719,7 @@ void donload_task(void *path) {
download_busy = false;
vTaskDelete( NULL );
}
-#endif // ESP32
+#endif // ESP32_DOWNLOAD_TASK
bool UfsUploadFileOpen(const char* upload_filename) {
From 6cfc7d549ee27adcfb2baee8faf3e73144d0999e Mon Sep 17 00:00:00 2001
From: bovirus <1262554+bovirus@users.noreply.github.com>
Date: Sun, 17 Jan 2021 14:16:56 +0100
Subject: [PATCH 008/186] Update Italian language
---
tasmota/language/it_IT.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h
index 391f166a7..fbf0d4baa 100644
--- a/tasmota/language/it_IT.h
+++ b/tasmota/language/it_IT.h
@@ -1,7 +1,7 @@
/*
it-IT.h - localization for Italian - Italy for Tasmota
- Copyright (C) 2021 Gennaro Tortone - some mods by Antonio Fragola - Updated by bovirus - rev. 09.01.2021
+ Copyright (C) 2021 Gennaro Tortone - some mods by Antonio Fragola - Updated by bovirus - rev. 17.01.2021
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -782,8 +782,8 @@
#define D_SENSOR_SSD1331_CS "SSD1331 - CS"
#define D_SENSOR_SSD1331_DC "SSD1331 - DC"
#define D_SENSOR_SDCARD_CS "Scheda SD - CS"
-#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
-#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_WIEGAND_D0 "Wiegand - D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand - D1"
// Units
#define D_UNIT_AMPERE "A"
From 5f04cf2ec84e4584d4f9986da6ef337c593b91a3 Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Sun, 17 Jan 2021 16:12:25 +0100
Subject: [PATCH 009/186] Zigbee support for Lidl Livarno Lux Remote Control
Dimmer
---
tasmota/xdrv_23_zigbee_5__constants.ino | 434 ++++++++++++------------
tasmota/xdrv_23_zigbee_6_commands.ino | 1 +
2 files changed, 219 insertions(+), 216 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_5__constants.ino b/tasmota/xdrv_23_zigbee_5__constants.ino
index 0a3926db7..7889e957a 100644
--- a/tasmota/xdrv_23_zigbee_5__constants.ino
+++ b/tasmota/xdrv_23_zigbee_5__constants.ino
@@ -73,7 +73,7 @@ def strings_to_pmem(arg):
DO NOT EDIT
*/
-const char Z_strings[] PROGMEM =
+const char Z_strings[] PROGMEM =
"\x00"
"00" "\x00"
"00190200" "\x00"
@@ -278,6 +278,7 @@ const char Z_strings[] PROGMEM =
"LastMessageLQI" "\x00"
"LastMessageRSSI" "\x00"
"LastSetTime" "\x00"
+ "LidlPower" "\x00"
"LocalTemperature" "\x00"
"LocalTemperatureCalibration" "\x00"
"LocalTime" "\x00"
@@ -699,221 +700,222 @@ enum Z_offsets {
Zo_LastMessageLQI = 3308,
Zo_LastMessageRSSI = 3323,
Zo_LastSetTime = 3339,
- Zo_LocalTemperature = 3351,
- Zo_LocalTemperatureCalibration = 3368,
- Zo_LocalTime = 3396,
- Zo_LocationAge = 3406,
- Zo_LocationMethod = 3418,
- Zo_LocationType = 3433,
- Zo_LockState = 3446,
- Zo_LockType = 3456,
- Zo_LongPollInterval = 3465,
- Zo_LongPollIntervalMin = 3482,
- Zo_MainsFrequency = 3502,
- Zo_MainsVoltage = 3517,
- Zo_Manufacturer = 3530,
- Zo_MaxTempExperienced = 3543,
- Zo_MeterTypeID = 3562,
- Zo_MinTempExperienced = 3574,
- Zo_Mode = 3593,
- Zo_Model = 3598,
- Zo_ModelId = 3604,
- Zo_MotorStepSize = 3612,
- Zo_Movement = 3626,
- Zo_MullerLightMode = 3635,
- Zo_MultiApplicationType = 3651,
- Zo_MultiDescription = 3672,
- Zo_MultiInApplicationType = 3689,
- Zo_MultiInDescription = 3712,
- Zo_MultiInNumberOfStates = 3731,
- Zo_MultiInOutOfService = 3753,
- Zo_MultiInReliability = 3773,
- Zo_MultiInStatusFlags = 3792,
- Zo_MultiInValue = 3811,
- Zo_MultiNumberOfStates = 3824,
- Zo_MultiOutApplicationType = 3844,
- Zo_MultiOutDescription = 3868,
- Zo_MultiOutNumberOfStates = 3888,
- Zo_MultiOutOfService = 3911,
- Zo_MultiOutOutOfService = 3929,
- Zo_MultiOutReliability = 3950,
- Zo_MultiOutRelinquishDefault = 3970,
- Zo_MultiOutStatusFlags = 3996,
- Zo_MultiOutValue = 4016,
- Zo_MultiReliability = 4030,
- Zo_MultiRelinquishDefault = 4047,
- Zo_MultiStatusFlags = 4070,
- Zo_MultiValue = 4087,
- Zo_MultipleScheduling = 4098,
- Zo_NumberOfDevices = 4117,
- Zo_NumberOfPrimaries = 4133,
- Zo_NumberOfResets = 4151,
- Zo_NumberofActuationsLift = 4166,
- Zo_NumberofActuationsTilt = 4189,
- Zo_Occupancy = 4212,
- Zo_OccupancySensorType = 4222,
- Zo_OccupiedCoolingSetpoint = 4242,
- Zo_OccupiedHeatingSetpoint = 4266,
- Zo_OnOffTransitionTime = 4290,
- Zo_OpenPeriod = 4310,
- Zo_OppleMode = 4321,
- Zo_OutdoorTemperature = 4331,
- Zo_OverTempTotalDwell = 4350,
- Zo_PICoolingDemand = 4369,
- Zo_PIHeatingDemand = 4385,
- Zo_POD = 4401,
- Zo_Panic = 4405,
- Zo_PartNumber = 4411,
- Zo_PersistentMemoryWrites = 4422,
- Zo_PersonalAlarm = 4445,
- Zo_PhysicalClosedLimit = 4459,
- Zo_PhysicalClosedLimitLift = 4479,
- Zo_PhysicalClosedLimitTilt = 4503,
- Zo_Power = 4527,
- Zo_Power2 = 4533,
- Zo_Power3 = 4540,
- Zo_Power4 = 4547,
- Zo_PowerOffEffect = 4554,
- Zo_PowerOnRecall = 4569,
- Zo_PowerOnTimer = 4583,
- Zo_PowerSource = 4596,
- Zo_PowerThreshold = 4608,
- Zo_Pressure = 4623,
- Zo_PressureMaxMeasuredValue = 4632,
- Zo_PressureMaxScaledValue = 4657,
- Zo_PressureMinMeasuredValue = 4680,
- Zo_PressureMinScaledValue = 4705,
- Zo_PressureScale = 4728,
- Zo_PressureScaledTolerance = 4742,
- Zo_PressureScaledValue = 4766,
- Zo_PressureTolerance = 4786,
- Zo_Primary1Intensity = 4804,
- Zo_Primary1X = 4822,
- Zo_Primary1Y = 4832,
- Zo_Primary2Intensity = 4842,
- Zo_Primary2X = 4860,
- Zo_Primary2Y = 4870,
- Zo_Primary3Intensity = 4880,
- Zo_Primary3X = 4898,
- Zo_Primary3Y = 4908,
- Zo_ProductCode = 4918,
- Zo_ProductRevision = 4930,
- Zo_ProductURL = 4946,
- Zo_QualityMeasure = 4957,
- Zo_RMSCurrent = 4972,
- Zo_RMSVoltage = 4983,
- Zo_ReactivePower = 4994,
- Zo_RecallScene = 5008,
- Zo_RemainingTime = 5020,
- Zo_RemoteSensing = 5034,
- Zo_RemoveAllGroups = 5048,
- Zo_RemoveAllScenes = 5064,
- Zo_RemoveGroup = 5080,
- Zo_RemoveScene = 5092,
- Zo_ResetAlarm = 5104,
- Zo_ResetAllAlarms = 5115,
- Zo_SWBuildID = 5130,
- Zo_Sat = 5140,
- Zo_SatMove = 5144,
- Zo_SatStep = 5152,
- Zo_SceneCount = 5160,
- Zo_SceneValid = 5171,
- Zo_ScheduleMode = 5182,
- Zo_SeaPressure = 5195,
- Zo_ShortPollInterval = 5207,
- Zo_Shutter = 5225,
- Zo_ShutterClose = 5233,
- Zo_ShutterLift = 5246,
- Zo_ShutterOpen = 5258,
- Zo_ShutterStop = 5270,
- Zo_ShutterTilt = 5282,
- Zo_SoftwareRevision = 5294,
- Zo_StackVersion = 5311,
- Zo_StandardTime = 5324,
- Zo_StartUpOnOff = 5337,
- Zo_Status = 5350,
- Zo_StoreScene = 5357,
- Zo_SwitchType = 5368,
- Zo_SystemMode = 5379,
- Zo_TRVBoost = 5390,
- Zo_TRVChildProtection = 5399,
- Zo_TRVMirrorDisplay = 5418,
- Zo_TRVMode = 5435,
- Zo_TRVWindowOpen = 5443,
- Zo_TempTarget = 5457,
- Zo_Temperature = 5468,
- Zo_TemperatureMaxMeasuredValue = 5480,
- Zo_TemperatureMinMeasuredValue = 5508,
- Zo_TemperatureTolerance = 5536,
- Zo_TerncyDuration = 5557,
- Zo_TerncyRotate = 5572,
- Zo_ThSetpoint = 5585,
- Zo_Time = 5596,
- Zo_TimeEpoch = 5601,
- Zo_TimeStatus = 5611,
- Zo_TimeZone = 5622,
- Zo_TotalProfileNum = 5631,
- Zo_TuyaAutoLock = 5647,
- Zo_TuyaAwayDays = 5660,
- Zo_TuyaAwayTemp = 5673,
- Zo_TuyaBattery = 5686,
- Zo_TuyaBoostTime = 5698,
- Zo_TuyaChildLock = 5712,
- Zo_TuyaComfortTemp = 5726,
- Zo_TuyaEcoTemp = 5742,
- Zo_TuyaFanMode = 5754,
- Zo_TuyaForceMode = 5766,
- Zo_TuyaMaxTemp = 5780,
- Zo_TuyaMinTemp = 5792,
- Zo_TuyaPreset = 5804,
- Zo_TuyaScheduleHolidays = 5815,
- Zo_TuyaScheduleWorkdays = 5836,
- Zo_TuyaTempTarget = 5857,
- Zo_TuyaValveDetection = 5872,
- Zo_TuyaValvePosition = 5891,
- Zo_TuyaWeekSelect = 5909,
- Zo_TuyaWindowDetection = 5924,
- Zo_UnoccupiedCoolingSetpoint = 5944,
- Zo_UnoccupiedHeatingSetpoint = 5970,
- Zo_UtilityName = 5996,
- Zo_ValidUntilTime = 6008,
- Zo_ValvePosition = 6023,
- Zo_VelocityLift = 6037,
- Zo_ViewGroup = 6050,
- Zo_ViewScene = 6060,
- Zo_Water = 6070,
- Zo_WhitePointX = 6076,
- Zo_WhitePointY = 6088,
- Zo_WindowCoveringType = 6100,
- Zo_X = 6119,
- Zo_Y = 6121,
- Zo_ZCLVersion = 6123,
- Zo_ZoneState = 6134,
- Zo_ZoneStatus = 6144,
- Zo_ZoneStatusChange = 6155,
- Zo_ZoneType = 6172,
- Zo_xx = 6181,
- Zo_xx000A00 = 6184,
- Zo_xx0A = 6193,
- Zo_xx0A00 = 6198,
- Zo_xx19 = 6205,
- Zo_xx190A = 6210,
- Zo_xx190A00 = 6217,
- Zo_xxxx = 6226,
- Zo_xxxx00 = 6231,
- Zo_xxxx0A00 = 6238,
- Zo_xxxxyy = 6247,
- Zo_xxxxyyyy = 6254,
- Zo_xxxxyyyy0A00 = 6263,
- Zo_xxxxyyzz = 6276,
- Zo_xxyy = 6285,
- Zo_xxyy0A00 = 6290,
- Zo_xxyyyy = 6299,
- Zo_xxyyyy000000000000 = 6306,
- Zo_xxyyyy0A0000000000 = 6325,
- Zo_xxyyyyzz = 6344,
- Zo_xxyyyyzzzz = 6353,
- Zo_xxyyzzzz = 6364,
+ Zo_LidlPower = 3351,
+ Zo_LocalTemperature = 3361,
+ Zo_LocalTemperatureCalibration = 3378,
+ Zo_LocalTime = 3406,
+ Zo_LocationAge = 3416,
+ Zo_LocationMethod = 3428,
+ Zo_LocationType = 3443,
+ Zo_LockState = 3456,
+ Zo_LockType = 3466,
+ Zo_LongPollInterval = 3475,
+ Zo_LongPollIntervalMin = 3492,
+ Zo_MainsFrequency = 3512,
+ Zo_MainsVoltage = 3527,
+ Zo_Manufacturer = 3540,
+ Zo_MaxTempExperienced = 3553,
+ Zo_MeterTypeID = 3572,
+ Zo_MinTempExperienced = 3584,
+ Zo_Mode = 3603,
+ Zo_Model = 3608,
+ Zo_ModelId = 3614,
+ Zo_MotorStepSize = 3622,
+ Zo_Movement = 3636,
+ Zo_MullerLightMode = 3645,
+ Zo_MultiApplicationType = 3661,
+ Zo_MultiDescription = 3682,
+ Zo_MultiInApplicationType = 3699,
+ Zo_MultiInDescription = 3722,
+ Zo_MultiInNumberOfStates = 3741,
+ Zo_MultiInOutOfService = 3763,
+ Zo_MultiInReliability = 3783,
+ Zo_MultiInStatusFlags = 3802,
+ Zo_MultiInValue = 3821,
+ Zo_MultiNumberOfStates = 3834,
+ Zo_MultiOutApplicationType = 3854,
+ Zo_MultiOutDescription = 3878,
+ Zo_MultiOutNumberOfStates = 3898,
+ Zo_MultiOutOfService = 3921,
+ Zo_MultiOutOutOfService = 3939,
+ Zo_MultiOutReliability = 3960,
+ Zo_MultiOutRelinquishDefault = 3980,
+ Zo_MultiOutStatusFlags = 4006,
+ Zo_MultiOutValue = 4026,
+ Zo_MultiReliability = 4040,
+ Zo_MultiRelinquishDefault = 4057,
+ Zo_MultiStatusFlags = 4080,
+ Zo_MultiValue = 4097,
+ Zo_MultipleScheduling = 4108,
+ Zo_NumberOfDevices = 4127,
+ Zo_NumberOfPrimaries = 4143,
+ Zo_NumberOfResets = 4161,
+ Zo_NumberofActuationsLift = 4176,
+ Zo_NumberofActuationsTilt = 4199,
+ Zo_Occupancy = 4222,
+ Zo_OccupancySensorType = 4232,
+ Zo_OccupiedCoolingSetpoint = 4252,
+ Zo_OccupiedHeatingSetpoint = 4276,
+ Zo_OnOffTransitionTime = 4300,
+ Zo_OpenPeriod = 4320,
+ Zo_OppleMode = 4331,
+ Zo_OutdoorTemperature = 4341,
+ Zo_OverTempTotalDwell = 4360,
+ Zo_PICoolingDemand = 4379,
+ Zo_PIHeatingDemand = 4395,
+ Zo_POD = 4411,
+ Zo_Panic = 4415,
+ Zo_PartNumber = 4421,
+ Zo_PersistentMemoryWrites = 4432,
+ Zo_PersonalAlarm = 4455,
+ Zo_PhysicalClosedLimit = 4469,
+ Zo_PhysicalClosedLimitLift = 4489,
+ Zo_PhysicalClosedLimitTilt = 4513,
+ Zo_Power = 4537,
+ Zo_Power2 = 4543,
+ Zo_Power3 = 4550,
+ Zo_Power4 = 4557,
+ Zo_PowerOffEffect = 4564,
+ Zo_PowerOnRecall = 4579,
+ Zo_PowerOnTimer = 4593,
+ Zo_PowerSource = 4606,
+ Zo_PowerThreshold = 4618,
+ Zo_Pressure = 4633,
+ Zo_PressureMaxMeasuredValue = 4642,
+ Zo_PressureMaxScaledValue = 4667,
+ Zo_PressureMinMeasuredValue = 4690,
+ Zo_PressureMinScaledValue = 4715,
+ Zo_PressureScale = 4738,
+ Zo_PressureScaledTolerance = 4752,
+ Zo_PressureScaledValue = 4776,
+ Zo_PressureTolerance = 4796,
+ Zo_Primary1Intensity = 4814,
+ Zo_Primary1X = 4832,
+ Zo_Primary1Y = 4842,
+ Zo_Primary2Intensity = 4852,
+ Zo_Primary2X = 4870,
+ Zo_Primary2Y = 4880,
+ Zo_Primary3Intensity = 4890,
+ Zo_Primary3X = 4908,
+ Zo_Primary3Y = 4918,
+ Zo_ProductCode = 4928,
+ Zo_ProductRevision = 4940,
+ Zo_ProductURL = 4956,
+ Zo_QualityMeasure = 4967,
+ Zo_RMSCurrent = 4982,
+ Zo_RMSVoltage = 4993,
+ Zo_ReactivePower = 5004,
+ Zo_RecallScene = 5018,
+ Zo_RemainingTime = 5030,
+ Zo_RemoteSensing = 5044,
+ Zo_RemoveAllGroups = 5058,
+ Zo_RemoveAllScenes = 5074,
+ Zo_RemoveGroup = 5090,
+ Zo_RemoveScene = 5102,
+ Zo_ResetAlarm = 5114,
+ Zo_ResetAllAlarms = 5125,
+ Zo_SWBuildID = 5140,
+ Zo_Sat = 5150,
+ Zo_SatMove = 5154,
+ Zo_SatStep = 5162,
+ Zo_SceneCount = 5170,
+ Zo_SceneValid = 5181,
+ Zo_ScheduleMode = 5192,
+ Zo_SeaPressure = 5205,
+ Zo_ShortPollInterval = 5217,
+ Zo_Shutter = 5235,
+ Zo_ShutterClose = 5243,
+ Zo_ShutterLift = 5256,
+ Zo_ShutterOpen = 5268,
+ Zo_ShutterStop = 5280,
+ Zo_ShutterTilt = 5292,
+ Zo_SoftwareRevision = 5304,
+ Zo_StackVersion = 5321,
+ Zo_StandardTime = 5334,
+ Zo_StartUpOnOff = 5347,
+ Zo_Status = 5360,
+ Zo_StoreScene = 5367,
+ Zo_SwitchType = 5378,
+ Zo_SystemMode = 5389,
+ Zo_TRVBoost = 5400,
+ Zo_TRVChildProtection = 5409,
+ Zo_TRVMirrorDisplay = 5428,
+ Zo_TRVMode = 5445,
+ Zo_TRVWindowOpen = 5453,
+ Zo_TempTarget = 5467,
+ Zo_Temperature = 5478,
+ Zo_TemperatureMaxMeasuredValue = 5490,
+ Zo_TemperatureMinMeasuredValue = 5518,
+ Zo_TemperatureTolerance = 5546,
+ Zo_TerncyDuration = 5567,
+ Zo_TerncyRotate = 5582,
+ Zo_ThSetpoint = 5595,
+ Zo_Time = 5606,
+ Zo_TimeEpoch = 5611,
+ Zo_TimeStatus = 5621,
+ Zo_TimeZone = 5632,
+ Zo_TotalProfileNum = 5641,
+ Zo_TuyaAutoLock = 5657,
+ Zo_TuyaAwayDays = 5670,
+ Zo_TuyaAwayTemp = 5683,
+ Zo_TuyaBattery = 5696,
+ Zo_TuyaBoostTime = 5708,
+ Zo_TuyaChildLock = 5722,
+ Zo_TuyaComfortTemp = 5736,
+ Zo_TuyaEcoTemp = 5752,
+ Zo_TuyaFanMode = 5764,
+ Zo_TuyaForceMode = 5776,
+ Zo_TuyaMaxTemp = 5790,
+ Zo_TuyaMinTemp = 5802,
+ Zo_TuyaPreset = 5814,
+ Zo_TuyaScheduleHolidays = 5825,
+ Zo_TuyaScheduleWorkdays = 5846,
+ Zo_TuyaTempTarget = 5867,
+ Zo_TuyaValveDetection = 5882,
+ Zo_TuyaValvePosition = 5901,
+ Zo_TuyaWeekSelect = 5919,
+ Zo_TuyaWindowDetection = 5934,
+ Zo_UnoccupiedCoolingSetpoint = 5954,
+ Zo_UnoccupiedHeatingSetpoint = 5980,
+ Zo_UtilityName = 6006,
+ Zo_ValidUntilTime = 6018,
+ Zo_ValvePosition = 6033,
+ Zo_VelocityLift = 6047,
+ Zo_ViewGroup = 6060,
+ Zo_ViewScene = 6070,
+ Zo_Water = 6080,
+ Zo_WhitePointX = 6086,
+ Zo_WhitePointY = 6098,
+ Zo_WindowCoveringType = 6110,
+ Zo_X = 6129,
+ Zo_Y = 6131,
+ Zo_ZCLVersion = 6133,
+ Zo_ZoneState = 6144,
+ Zo_ZoneStatus = 6154,
+ Zo_ZoneStatusChange = 6165,
+ Zo_ZoneType = 6182,
+ Zo_xx = 6191,
+ Zo_xx000A00 = 6194,
+ Zo_xx0A = 6203,
+ Zo_xx0A00 = 6208,
+ Zo_xx19 = 6215,
+ Zo_xx190A = 6220,
+ Zo_xx190A00 = 6227,
+ Zo_xxxx = 6236,
+ Zo_xxxx00 = 6241,
+ Zo_xxxx0A00 = 6248,
+ Zo_xxxxyy = 6257,
+ Zo_xxxxyyyy = 6264,
+ Zo_xxxxyyyy0A00 = 6273,
+ Zo_xxxxyyzz = 6286,
+ Zo_xxyy = 6295,
+ Zo_xxyy0A00 = 6300,
+ Zo_xxyyyy = 6309,
+ Zo_xxyyyy000000000000 = 6316,
+ Zo_xxyyyy0A0000000000 = 6335,
+ Zo_xxyyyyzz = 6354,
+ Zo_xxyyyyzzzz = 6363,
+ Zo_xxyyzzzz = 6374,
};
diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino
index 89e3a6e65..a8566705a 100644
--- a/tasmota/xdrv_23_zigbee_6_commands.ino
+++ b/tasmota/xdrv_23_zigbee_6_commands.ino
@@ -69,6 +69,7 @@ const Z_CommandConverter Z_Commands[] PROGMEM = {
{ Z_(PowerOffEffect), 0x0006, 0x40, 0x81, Z_(xxyy) }, // Power Off With Effect
{ Z_(PowerOnRecall), 0x0006, 0x41, 0x81, Z_() }, // Power On With Recall Global Scene
{ Z_(PowerOnTimer), 0x0006, 0x42, 0x81, Z_(xxyyyyzzzz) }, // Power On with Timed Off
+ { Z_(LidlPower), 0x0006, 0xFD, 0x01, Z_(xx) }, // Lidl specific encoding
{ Z_(Power), 0x0006, 0xFF, 0x01, Z_() }, // 0=Off, 1=On, 2=Toggle
{ Z_(Dimmer), 0x0008, 0x04, 0x01, Z_(xx0A00) }, // Move to Level with On/Off, xx=0..254 (255 is invalid)
{ Z_(DimmerUp), 0x0008, 0x06, 0x01, Z_(00190200) }, // Step up by 10%, 0.2 secs
From 454892c971c81fac37c801e6ba958ec64cc4d003 Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Sun, 17 Jan 2021 16:39:40 +0100
Subject: [PATCH 010/186] Zigbee report colors as RGB
---
tasmota/xdrv_23_zigbee_5__constants.ino | 232 ++++++++++++------------
tasmota/xdrv_23_zigbee_5_converters.ino | 34 +++-
2 files changed, 150 insertions(+), 116 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_5__constants.ino b/tasmota/xdrv_23_zigbee_5__constants.ino
index 7889e957a..4176c7ee4 100644
--- a/tasmota/xdrv_23_zigbee_5__constants.ino
+++ b/tasmota/xdrv_23_zigbee_5__constants.ino
@@ -73,7 +73,7 @@ def strings_to_pmem(arg):
DO NOT EDIT
*/
-const char Z_strings[] PROGMEM =
+const char Z_strings[] PROGMEM =
"\x00"
"00" "\x00"
"00190200" "\x00"
@@ -380,6 +380,7 @@ const char Z_strings[] PROGMEM =
"ProductRevision" "\x00"
"ProductURL" "\x00"
"QualityMeasure" "\x00"
+ "RGB" "\x00"
"RMSCurrent" "\x00"
"RMSVoltage" "\x00"
"ReactivePower" "\x00"
@@ -802,120 +803,121 @@ enum Z_offsets {
Zo_ProductRevision = 4940,
Zo_ProductURL = 4956,
Zo_QualityMeasure = 4967,
- Zo_RMSCurrent = 4982,
- Zo_RMSVoltage = 4993,
- Zo_ReactivePower = 5004,
- Zo_RecallScene = 5018,
- Zo_RemainingTime = 5030,
- Zo_RemoteSensing = 5044,
- Zo_RemoveAllGroups = 5058,
- Zo_RemoveAllScenes = 5074,
- Zo_RemoveGroup = 5090,
- Zo_RemoveScene = 5102,
- Zo_ResetAlarm = 5114,
- Zo_ResetAllAlarms = 5125,
- Zo_SWBuildID = 5140,
- Zo_Sat = 5150,
- Zo_SatMove = 5154,
- Zo_SatStep = 5162,
- Zo_SceneCount = 5170,
- Zo_SceneValid = 5181,
- Zo_ScheduleMode = 5192,
- Zo_SeaPressure = 5205,
- Zo_ShortPollInterval = 5217,
- Zo_Shutter = 5235,
- Zo_ShutterClose = 5243,
- Zo_ShutterLift = 5256,
- Zo_ShutterOpen = 5268,
- Zo_ShutterStop = 5280,
- Zo_ShutterTilt = 5292,
- Zo_SoftwareRevision = 5304,
- Zo_StackVersion = 5321,
- Zo_StandardTime = 5334,
- Zo_StartUpOnOff = 5347,
- Zo_Status = 5360,
- Zo_StoreScene = 5367,
- Zo_SwitchType = 5378,
- Zo_SystemMode = 5389,
- Zo_TRVBoost = 5400,
- Zo_TRVChildProtection = 5409,
- Zo_TRVMirrorDisplay = 5428,
- Zo_TRVMode = 5445,
- Zo_TRVWindowOpen = 5453,
- Zo_TempTarget = 5467,
- Zo_Temperature = 5478,
- Zo_TemperatureMaxMeasuredValue = 5490,
- Zo_TemperatureMinMeasuredValue = 5518,
- Zo_TemperatureTolerance = 5546,
- Zo_TerncyDuration = 5567,
- Zo_TerncyRotate = 5582,
- Zo_ThSetpoint = 5595,
- Zo_Time = 5606,
- Zo_TimeEpoch = 5611,
- Zo_TimeStatus = 5621,
- Zo_TimeZone = 5632,
- Zo_TotalProfileNum = 5641,
- Zo_TuyaAutoLock = 5657,
- Zo_TuyaAwayDays = 5670,
- Zo_TuyaAwayTemp = 5683,
- Zo_TuyaBattery = 5696,
- Zo_TuyaBoostTime = 5708,
- Zo_TuyaChildLock = 5722,
- Zo_TuyaComfortTemp = 5736,
- Zo_TuyaEcoTemp = 5752,
- Zo_TuyaFanMode = 5764,
- Zo_TuyaForceMode = 5776,
- Zo_TuyaMaxTemp = 5790,
- Zo_TuyaMinTemp = 5802,
- Zo_TuyaPreset = 5814,
- Zo_TuyaScheduleHolidays = 5825,
- Zo_TuyaScheduleWorkdays = 5846,
- Zo_TuyaTempTarget = 5867,
- Zo_TuyaValveDetection = 5882,
- Zo_TuyaValvePosition = 5901,
- Zo_TuyaWeekSelect = 5919,
- Zo_TuyaWindowDetection = 5934,
- Zo_UnoccupiedCoolingSetpoint = 5954,
- Zo_UnoccupiedHeatingSetpoint = 5980,
- Zo_UtilityName = 6006,
- Zo_ValidUntilTime = 6018,
- Zo_ValvePosition = 6033,
- Zo_VelocityLift = 6047,
- Zo_ViewGroup = 6060,
- Zo_ViewScene = 6070,
- Zo_Water = 6080,
- Zo_WhitePointX = 6086,
- Zo_WhitePointY = 6098,
- Zo_WindowCoveringType = 6110,
- Zo_X = 6129,
- Zo_Y = 6131,
- Zo_ZCLVersion = 6133,
- Zo_ZoneState = 6144,
- Zo_ZoneStatus = 6154,
- Zo_ZoneStatusChange = 6165,
- Zo_ZoneType = 6182,
- Zo_xx = 6191,
- Zo_xx000A00 = 6194,
- Zo_xx0A = 6203,
- Zo_xx0A00 = 6208,
- Zo_xx19 = 6215,
- Zo_xx190A = 6220,
- Zo_xx190A00 = 6227,
- Zo_xxxx = 6236,
- Zo_xxxx00 = 6241,
- Zo_xxxx0A00 = 6248,
- Zo_xxxxyy = 6257,
- Zo_xxxxyyyy = 6264,
- Zo_xxxxyyyy0A00 = 6273,
- Zo_xxxxyyzz = 6286,
- Zo_xxyy = 6295,
- Zo_xxyy0A00 = 6300,
- Zo_xxyyyy = 6309,
- Zo_xxyyyy000000000000 = 6316,
- Zo_xxyyyy0A0000000000 = 6335,
- Zo_xxyyyyzz = 6354,
- Zo_xxyyyyzzzz = 6363,
- Zo_xxyyzzzz = 6374,
+ Zo_RGB = 4982,
+ Zo_RMSCurrent = 4986,
+ Zo_RMSVoltage = 4997,
+ Zo_ReactivePower = 5008,
+ Zo_RecallScene = 5022,
+ Zo_RemainingTime = 5034,
+ Zo_RemoteSensing = 5048,
+ Zo_RemoveAllGroups = 5062,
+ Zo_RemoveAllScenes = 5078,
+ Zo_RemoveGroup = 5094,
+ Zo_RemoveScene = 5106,
+ Zo_ResetAlarm = 5118,
+ Zo_ResetAllAlarms = 5129,
+ Zo_SWBuildID = 5144,
+ Zo_Sat = 5154,
+ Zo_SatMove = 5158,
+ Zo_SatStep = 5166,
+ Zo_SceneCount = 5174,
+ Zo_SceneValid = 5185,
+ Zo_ScheduleMode = 5196,
+ Zo_SeaPressure = 5209,
+ Zo_ShortPollInterval = 5221,
+ Zo_Shutter = 5239,
+ Zo_ShutterClose = 5247,
+ Zo_ShutterLift = 5260,
+ Zo_ShutterOpen = 5272,
+ Zo_ShutterStop = 5284,
+ Zo_ShutterTilt = 5296,
+ Zo_SoftwareRevision = 5308,
+ Zo_StackVersion = 5325,
+ Zo_StandardTime = 5338,
+ Zo_StartUpOnOff = 5351,
+ Zo_Status = 5364,
+ Zo_StoreScene = 5371,
+ Zo_SwitchType = 5382,
+ Zo_SystemMode = 5393,
+ Zo_TRVBoost = 5404,
+ Zo_TRVChildProtection = 5413,
+ Zo_TRVMirrorDisplay = 5432,
+ Zo_TRVMode = 5449,
+ Zo_TRVWindowOpen = 5457,
+ Zo_TempTarget = 5471,
+ Zo_Temperature = 5482,
+ Zo_TemperatureMaxMeasuredValue = 5494,
+ Zo_TemperatureMinMeasuredValue = 5522,
+ Zo_TemperatureTolerance = 5550,
+ Zo_TerncyDuration = 5571,
+ Zo_TerncyRotate = 5586,
+ Zo_ThSetpoint = 5599,
+ Zo_Time = 5610,
+ Zo_TimeEpoch = 5615,
+ Zo_TimeStatus = 5625,
+ Zo_TimeZone = 5636,
+ Zo_TotalProfileNum = 5645,
+ Zo_TuyaAutoLock = 5661,
+ Zo_TuyaAwayDays = 5674,
+ Zo_TuyaAwayTemp = 5687,
+ Zo_TuyaBattery = 5700,
+ Zo_TuyaBoostTime = 5712,
+ Zo_TuyaChildLock = 5726,
+ Zo_TuyaComfortTemp = 5740,
+ Zo_TuyaEcoTemp = 5756,
+ Zo_TuyaFanMode = 5768,
+ Zo_TuyaForceMode = 5780,
+ Zo_TuyaMaxTemp = 5794,
+ Zo_TuyaMinTemp = 5806,
+ Zo_TuyaPreset = 5818,
+ Zo_TuyaScheduleHolidays = 5829,
+ Zo_TuyaScheduleWorkdays = 5850,
+ Zo_TuyaTempTarget = 5871,
+ Zo_TuyaValveDetection = 5886,
+ Zo_TuyaValvePosition = 5905,
+ Zo_TuyaWeekSelect = 5923,
+ Zo_TuyaWindowDetection = 5938,
+ Zo_UnoccupiedCoolingSetpoint = 5958,
+ Zo_UnoccupiedHeatingSetpoint = 5984,
+ Zo_UtilityName = 6010,
+ Zo_ValidUntilTime = 6022,
+ Zo_ValvePosition = 6037,
+ Zo_VelocityLift = 6051,
+ Zo_ViewGroup = 6064,
+ Zo_ViewScene = 6074,
+ Zo_Water = 6084,
+ Zo_WhitePointX = 6090,
+ Zo_WhitePointY = 6102,
+ Zo_WindowCoveringType = 6114,
+ Zo_X = 6133,
+ Zo_Y = 6135,
+ Zo_ZCLVersion = 6137,
+ Zo_ZoneState = 6148,
+ Zo_ZoneStatus = 6158,
+ Zo_ZoneStatusChange = 6169,
+ Zo_ZoneType = 6186,
+ Zo_xx = 6195,
+ Zo_xx000A00 = 6198,
+ Zo_xx0A = 6207,
+ Zo_xx0A00 = 6212,
+ Zo_xx19 = 6219,
+ Zo_xx190A = 6224,
+ Zo_xx190A00 = 6231,
+ Zo_xxxx = 6240,
+ Zo_xxxx00 = 6245,
+ Zo_xxxx0A00 = 6252,
+ Zo_xxxxyy = 6261,
+ Zo_xxxxyyyy = 6268,
+ Zo_xxxxyyyy0A00 = 6277,
+ Zo_xxxxyyzz = 6290,
+ Zo_xxyy = 6299,
+ Zo_xxyy0A00 = 6304,
+ Zo_xxyyyy = 6313,
+ Zo_xxyyyy000000000000 = 6320,
+ Zo_xxyyyy0A0000000000 = 6339,
+ Zo_xxyyyyzz = 6358,
+ Zo_xxyyyyzzzz = 6367,
+ Zo_xxyyzzzz = 6378,
};
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index fe4d219c0..28f222c99 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -509,6 +509,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
{ Zuint16, Cx0300, 0x003A, Z_(ColorPointBX), Cm1, 0 },
{ Zuint16, Cx0300, 0x003B, Z_(ColorPointBY), Cm1, 0 },
{ Zuint8, Cx0300, 0x003C, Z_(ColorPointBIntensity), Cm1, 0 },
+ { Zoctstr, Cx0300, 0xFFF0, Z_(RGB), Cm1, 0 }, // synthetic argument to show color as RGB (converted from HueSat or XY)
// Illuminance Measurement cluster
{ Zuint16, Cx0400, 0x0000, Z_(Illuminance), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_PIR, illuminance) }, // Illuminance (in Lux)
@@ -1332,7 +1333,38 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
}
}
break;
- case 0x04030000: // Pressure
+ case 0x03000000: // Hue
+ case 0x03000001: // Sat
+ case 0x03000003: // X
+ case 0x03000004: // Y
+ { // generate synthetic RGB
+ const Z_attribute * attr_rgb = attr_list.findAttribute(0x0300, 0xFFF0);
+ if (attr_rgb == nullptr) { // make sure we didn't already computed it
+ uint8_t r,g,b;
+ bool is_rgb = false;
+ const Z_attribute * attr_hue = attr_list.findAttribute(0x0300, 0x0000);
+ const Z_attribute * attr_sat = attr_list.findAttribute(0x0300, 0x0001);
+ const Z_attribute * attr_x = attr_list.findAttribute(0x0300, 0x0003);
+ const Z_attribute * attr_y = attr_list.findAttribute(0x0300, 0x0004);
+ if (attr_hue && attr_sat) {
+ uint8_t sat = changeUIntScale(attr_sat->getUInt(), 0, 254, 0, 255);
+ HsToRgb(attr_hue->getUInt(), sat, &r, &g, &b);
+ is_rgb = true;
+ } else if (attr_x && attr_y) {
+ XyToRgb(attr_x->getUInt() / 65535.0f, attr_y->getUInt() / 65535.0f, &r, &g, &b);
+ is_rgb = true;
+ }
+ if (is_rgb) {
+ SBuffer rgb(3);
+ rgb.add8(r);
+ rgb.add8(g);
+ rgb.add8(b);
+ attr_list.addAttribute(0x0300, 0xFFF0).setBuf(rgb, 0, 3);
+ }
+ }
+ }
+ break;
+ case 0x04030000: // SeaPressure
{
int16_t pressure = attr.getInt();
int16_t pressure_sealevel = (pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0f), 5.255f)) - 21.6f;
From 47e23082cc5f5ee430d6e0b97966a008d2a6b8eb Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Sun, 17 Jan 2021 17:01:52 +0100
Subject: [PATCH 011/186] Update changelog
---
CHANGELOG.md | 3 ++-
RELEASENOTES.md | 2 ++
tasmota/support_button.ino | 18 ++++++++----------
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44b5e00c4..7cb4ac228 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,7 +13,8 @@ All notable changes to this project will be documented in this file.
- ESP8266 Support for 2MB and up linker files with 1MB and up LittleFS
- ESP32 support for TLS MQTT using BearSSL (same as ESP8266)
- Support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) by Sigurd Leuther (#3647)
-- Added ``USE_MQTT_TLS_DROP_OLD_FINGERPRINT`` compile time option to drop old (less secure) TLS fingerprint
+- Compile time option ``USE_MQTT_TLS_DROP_OLD_FINGERPRINT`` to drop old (less secure) TLS fingerprint
+- Command ``SetOption40 0..250`` to disable button functionality if activated for over 0.1 second re-introduced
### Breaking Changed
- ESP32 switch from default SPIFFS to default LittleFS file system loosing current (zigbee) files
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 136d679f3..9c1e88528 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -61,6 +61,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Command ``CTRange`` to specify the visible CT range the bulb is capable of [#10311](https://github.com/arendst/Tasmota/issues/10311)
- Command ``RuleTimer0`` to access all RuleTimers at once [#10352](https://github.com/arendst/Tasmota/issues/10352)
- Command ``VirtualCT`` to simulate or fine tune CT bulbs with 3,4,5 channels [#10311](https://github.com/arendst/Tasmota/issues/10311)
+- Command ``SetOption40 0..250`` to disable button functionality if activated for over 0.1 second re-introduced
- Command ``SetOption43 1..255`` to control Rotary step (#10407)
- Command ``SetOption118 1`` to move ZbReceived from JSON message and into the subtopic replacing "SENSOR" default [#10353](https://github.com/arendst/Tasmota/issues/10353)
- Command ``SetOption119 1`` to remove the device addr from json payload, can be used with zb_topic_fname where the addr is already known from the topic [#10355](https://github.com/arendst/Tasmota/issues/10355)
@@ -83,6 +84,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Support character `#` to be replaced by `space`-character in command ``Publish`` topic [#10258](https://github.com/arendst/Tasmota/issues/10258)
- Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin [#8630](https://github.com/arendst/Tasmota/issues/8630)
- SPI display driver SSD1331 Color oled by Jeroen Vermeulen [#10376](https://github.com/arendst/Tasmota/issues/10376)
+- Compile time option ``USE_MQTT_TLS_DROP_OLD_FINGERPRINT`` to drop old (less secure) TLS fingerprint
### Breaking Changed
- ESP32 switch from default SPIFFS to default LittleFS file system loosing current (zigbee) files
diff --git a/tasmota/support_button.ino b/tasmota/support_button.ino
index 473183665..f1bd22c0f 100644
--- a/tasmota/support_button.ino
+++ b/tasmota/support_button.ino
@@ -263,22 +263,20 @@ void ButtonHandler(void) {
SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); // Execute Hold command via MQTT if ButtonTopic is set
}
} else {
- if (!Settings.flag.button_restrict) { // SetOption1 - Control button multipress
+ if (Settings.flag.button_restrict) { // SetOption1 (0) - Control button multipress
+ if (Settings.param[P_HOLD_IGNORE] > 0) { // SetOption40 (0) - Do not ignore button hold
+ if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) {
+ Button.hold_timer[button_index] = 0; // Reset button hold counter to stay below hold trigger
+ Button.press_counter[button_index] = 0; // Discard button press to disable functionality
+ }
+ }
+ } else {
if ((Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10)) { // SetOption32 (40) - Button held for factor times longer
Button.press_counter[button_index] = 0;
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1"));
ExecuteCommand(scmnd, SRC_BUTTON);
}
}
- else
- {
- if (Settings.param[P_HOLD_IGNORE] > 0) { // SetOption40 (0) - Do not ignore button hold
- if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) {
- Button.hold_timer[button_index] = 0; // Reset button hold counter to stay below hold trigger
- Button.press_counter[button_index] = 0; // Discard button press to disable functionality
- }
- }
- }
}
}
}
From d402060e5616e7521195e978cfb5d12553be923f Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Sun, 17 Jan 2021 17:50:58 +0100
Subject: [PATCH 012/186] Clean up Wiegand
---
tasmota/xsns_82_wiegand.ino | 276 ++++++++++++++++++------------------
1 file changed, 135 insertions(+), 141 deletions(-)
diff --git a/tasmota/xsns_82_wiegand.ino b/tasmota/xsns_82_wiegand.ino
index 59505a5fc..ca707eaf7 100644
--- a/tasmota/xsns_82_wiegand.ino
+++ b/tasmota/xsns_82_wiegand.ino
@@ -30,21 +30,21 @@
#define XSNS_82 82
#define WIEGAND_BIT_TIMEOUT 25 //time to be wait after last bit detected.
-// use only a randomly generate RFID for testing. using #define will save some space in the final code
+
+// Use only a randomly generate RFID for testing. using #define will save some space in the final code
// DEV_WIEGAND_TEST_MODE 1 : testing with random rfid without hardware connected, but GPIOs set correctly
// DEV_WIEGAND_TEST_MODE 2 : testing with hardware corretly connected.
-//
#define DEV_WIEGAND_TEST_MODE 0
#ifdef DEV_WIEGAND_TEST_MODE
- #if (DEV_WIEGAND_TEST_MODE==0)
- #elif (DEV_WIEGAND_TEST_MODE==1)
- #warning "Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 1 (Random RFID)"
- #elif (DEV_WIEGAND_TEST_MODE==2)
- #warning "Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 2 (Hardware connected)"
- #else
- #warning "Wiegand Interface compiled with unknown mode"
- #endif
+ #if (DEV_WIEGAND_TEST_MODE==0)
+ #elif (DEV_WIEGAND_TEST_MODE==1)
+ #warning "Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 1 (Random RFID)"
+ #elif (DEV_WIEGAND_TEST_MODE==2)
+ #warning "Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 2 (Hardware connected)"
+ #else
+ #warning "Wiegand Interface compiled with unknown mode"
+ #endif
#endif
class Wiegand {
@@ -96,16 +96,16 @@ Wiegand::Wiegand() {
rfidBuffer = 0;
bitCount = 0 ;
timeOut = 0;
- isInit= false;
+ isInit = false;
}
#if (DEV_WIEGAND_TEST_MODE)==1
uint64_t Wiegand::GetRandomRfid(uint8_t tag_size=34) {
- //todo add support for 4 and 8 bit keyboard "tags"
+ // Todo add support for 4 and 8 bit keyboard "tags"
uint64_t result = (uint32_t)HwRandom();
uint8_t parities = 0;
bitCount = tag_size;
- timeOut=millis() - WIEGAND_BIT_TIMEOUT;
+ timeOut = millis() - WIEGAND_BIT_TIMEOUT;
result = result << 32;
result += HwRandom();
@@ -127,8 +127,8 @@ uint64_t Wiegand::GetRandomRfid(uint8_t tag_size=34) {
}
parities = CalculateParities(result, tag_size);
- result = (result << 1) | (parities & 0x01); //set LSB parity
- if (parities & 0x80) { //MSB parity is 1
+ result = (result << 1) | (parities & 0x01); // Set LSB parity
+ if (parities & 0x80) { // MSB parity is 1
switch (tag_size) {
case 24:
result |= 0x800000;
@@ -151,47 +151,47 @@ uint64_t Wiegand::GetRandomRfid(uint8_t tag_size=34) {
}
#endif
-void ICACHE_RAM_ATTR Wiegand::handleD1Interrupt() { // receive a 1 bit. (D0=high & D1=low)
- rfidBuffer = (rfidBuffer << 1) | 1; // leftshift + 1 bit
- bitCount++; //increment the counter
- lastFoundTime = millis(); // last time bit found
+void ICACHE_RAM_ATTR Wiegand::handleD1Interrupt() { // Receive a 1 bit. (D0=high & D1=low)
+ rfidBuffer = (rfidBuffer << 1) | 1; // Leftshift + 1 bit
+ bitCount++; // Increment the counter
+ lastFoundTime = millis(); // Last time bit found
}
-void ICACHE_RAM_ATTR Wiegand::handleD0Interrupt() { // receive a 0 bit. (D0=low & D1=high)
- rfidBuffer = rfidBuffer << 1; // leftshift the 0 bit is now at the end of rfidBuffer
- bitCount++; //increment the counter
- lastFoundTime = millis(); //last time bit found
+void ICACHE_RAM_ATTR Wiegand::handleD0Interrupt() { // Receive a 0 bit. (D0=low & D1=high)
+ rfidBuffer = rfidBuffer << 1; // Leftshift the 0 bit is now at the end of rfidBuffer
+ bitCount++; // Increment the counter
+ lastFoundTime = millis(); // Last time bit found
}
void Wiegand::Init() {
isInit = false;
- if (PinUsed(GPIO_WIEGAND_D0) && PinUsed(GPIO_WIEGAND_D1)) { //only start, if the Wiegang pins are
- #if (DEV_WIEGAND_TEST_MODE)>0
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: Init()"));
- #endif
+ if (PinUsed(GPIO_WIEGAND_D0) && PinUsed(GPIO_WIEGAND_D1)) { // Only start, if the Wiegang pins are
+#if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: Init()"));
+#endif
pinMode(Pin(GPIO_WIEGAND_D0), INPUT_PULLUP);
pinMode(Pin(GPIO_WIEGAND_D1), INPUT_PULLUP);
- attachInterrupt(Pin(GPIO_WIEGAND_D0), handleD0Interrupt, FALLING);
- attachInterrupt(Pin(GPIO_WIEGAND_D1), handleD1Interrupt, FALLING);
- isInit = true; // helps to run only if correctly setup
- #if (DEV_WIEGAND_TEST_MODE)>0
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: Testmode")); // for tests without reader attaiched
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: D0:%u"),Pin(GPIO_WIEGAND_D0));
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: D1:%u"),Pin(GPIO_WIEGAND_D1));
- #else
+ attachInterrupt(Pin(GPIO_WIEGAND_D0), handleD0Interrupt, FALLING);
+ attachInterrupt(Pin(GPIO_WIEGAND_D1), handleD1Interrupt, FALLING);
+ isInit = true; // Helps to run only if correctly setup
+#if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: Testmode")); // For tests without reader attaiched
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: D0:%u"),Pin(GPIO_WIEGAND_D0));
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: D1:%u"),Pin(GPIO_WIEGAND_D1));
+#else
AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: D0=%u, D1=%u"),Pin(GPIO_WIEGAND_D0), Pin(GPIO_WIEGAND_D1));
- #endif
+#endif
}
- #if (DEV_WIEGAND_TEST_MODE)>0
+#if (DEV_WIEGAND_TEST_MODE)>0
else {
AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: no GPIOs."));
}
- #endif
+#endif
}
uint64_t Wiegand::CheckAndConvertRfid(uint64_t rfidIn, uint16_t bitcount) {
uint8_t evenParityBit = 0;
- uint8_t oddParityBit = (uint8_t) (rfidIn & 0x1); // last bit = odd parity
+ uint8_t oddParityBit = (uint8_t) (rfidIn & 0x1); // Last bit = odd parity
uint8_t calcParity = 0;
switch (bitcount) {
case 24:
@@ -217,42 +217,42 @@ uint64_t Wiegand::CheckAndConvertRfid(uint64_t rfidIn, uint16_t bitcount) {
default:
break;
}
- calcParity = CalculateParities(rfidIn, bitCount); //ckeck result on http://www.ccdesignworks.com/wiegand_calc.htm with raw tag as input
- if (calcParity != (evenParityBit | oddParityBit)) { // Paritybit is wrong
+ calcParity = CalculateParities(rfidIn, bitCount); // check result on http://www.ccdesignworks.com/wiegand_calc.htm with raw tag as input
+ if (calcParity != (evenParityBit | oddParityBit)) { // Paritybit is wrong
rfidIn=0;
AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: %llu parity error"), rfidIn);
}
- #if (DEV_WIEGAND_TEST_MODE)>0
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: even (left) parity: %u "), (evenParityBit>>7));
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: even (calc) parity: %u "), (calcParity & 0x80)>>7);
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: odd (right) parity: %u "), oddParityBit);
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: odd (calc) parity: %u "), (calcParity & 0x01));
- #endif
+#if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: even (left) parity: %u "), (evenParityBit>>7));
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: even (calc) parity: %u "), (calcParity & 0x80)>>7);
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: odd (right) parity: %u "), oddParityBit);
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: odd (calc) parity: %u "), (calcParity & 0x01));
+#endif
return rfidIn;
}
uint8_t Wiegand::CalculateParities(uint64_t tagWithoutParities, int tag_size=26) {
- //tag_size is the size of the final tag including the 2 parity bits
- //so length if the tagWithoutParities should be (tag_size-2) !! That will be not profed and
- //lead to wrong results if the input value is larger!
- //calculated start parity (even) will be returned as bit 8
- //calculated end parity (odd) will be returned as bit 1
+ // tag_size is the size of the final tag including the 2 parity bits
+ // So length if the tagWithoutParities should be (tag_size-2) !! That will be not profed and
+ // lead to wrong results if the input value is larger!
+ // Calculated start parity (even) will be returned as bit 8
+ // calculated end parity (odd) will be returned as bit 1
uint8_t retValue=0;
tag_size -= 2;
- if (tag_size<=0) { return retValue; } //prohibit div zero exception and other wrong inputs
- uint8_t parity=1; //check for odd parity on LSB
- for (uint8_t i=0; i<(tag_size/2); i++) {
- parity^=(tagWithoutParities & 1);
- tagWithoutParities>>=1;
- }
+ if (tag_size <= 0) { return retValue; } // Prohibit div zero exception and other wrong inputs
+ uint8_t parity = 1; // Check for odd parity on LSB
+ for (uint8_t i = 0; i < (tag_size / 2); i++) {
+ parity ^= (tagWithoutParities & 1);
+ tagWithoutParities >>= 1;
+ }
retValue |= parity;
- parity=0; //check for even parity on MSB
+ parity = 0; // Check for even parity on MSB
while (tagWithoutParities) {
- parity^=(tagWithoutParities & 1);
- tagWithoutParities>>=1;
- }
- retValue |= (parity<<7);
+ parity ^= (tagWithoutParities & 1);
+ tagWithoutParities >>= 1;
+ }
+ retValue |= (parity << 7);
return retValue;
}
@@ -260,10 +260,10 @@ uint8_t Wiegand::CalculateParities(uint64_t tagWithoutParities, int tag_size=26)
char Wiegand::translateEnterEscapeKeyPress(char oKeyPressed) {
switch(oKeyPressed) {
case 0x0b: // 11 or * key
- return 0x0d; // 13 or ASCII ENTER
+ return 0x0d; // 13 or ASCII ENTER
case 0x0a: // 10 or # key
- return 0x1b; // 27 or ASCII ESCAPE
+ return 0x1b; // 27 or ASCII ESCAPE
default:
return oKeyPressed;
@@ -274,19 +274,19 @@ bool Wiegand::WiegandConversion ()
{
bool bRet = false;
unsigned long nowTick = millis();
-//add a maximum wait time for new bits
+ // Add a maximum wait time for new bits
unsigned long diffTicks = nowTick - lastFoundTime;
- if ((diffTicks > WIEGAND_BIT_TIMEOUT) && (diffTicks >= 5000 )) { //max. 5 secs between 2 bits comming in
- bitCount=0;
- rfidBuffer=0;
- lastFoundTime=nowTick;
+ if ((diffTicks > WIEGAND_BIT_TIMEOUT) && (diffTicks >= 5000 )) { // Max. 5 secs between 2 bits comming in
+ bitCount = 0;
+ rfidBuffer = 0;
+ lastFoundTime = nowTick;
return bRet;
- }
- if (diffTicks > WIEGAND_BIT_TIMEOUT) { //last bit found is WIEGAND_BIT_TIMEOUT ms ago
- #if (DEV_WIEGAND_TEST_MODE)>0
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: raw tag: %llu "), rfidBuffer);
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: bit count: %u "), bitCount);
- #endif
+ }
+ if (diffTicks > WIEGAND_BIT_TIMEOUT) { // Last bit found is WIEGAND_BIT_TIMEOUT ms ago
+#if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: raw tag: %llu "), rfidBuffer);
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: bit count: %u "), bitCount);
+#endif
if ((bitCount==4)||(bitCount==8)||(bitCount==24)||(bitCount==26)||(bitCount==32)||(bitCount==34)) {
if ((bitCount==24)||(bitCount==26)||(bitCount==32)||(bitCount==34)) {
// 24,26,32,34-bit Wiegand codes
@@ -310,79 +310,74 @@ bool Wiegand::WiegandConversion ()
// eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001
char highNibble = (rfidBuffer & 0xf0) >>4;
char lowNibble = (rfidBuffer & 0x0f);
- if (lowNibble == (~highNibble & 0x0f)) // check if low nibble matches the "NOT" of high nibble.
- {
+ if (lowNibble == (~highNibble & 0x0f)) { // Check if low nibble matches the "NOT" of high nibble.
rfid = (int)translateEnterEscapeKeyPress(lowNibble);
- bRet=true;
+ bRet = true;
+ } else {
+ lastFoundTime = nowTick;
+ bRet = false;
}
- else {
- lastFoundTime=nowTick;
- bRet=false;
- }
- tagSize=bitCount;
- bitCount=0;
- rfidBuffer=0;
+ tagSize = bitCount;
+ bitCount = 0;
+ rfidBuffer = 0;
}
+ } else {
+ // Time reached but unknown bitCount, clear and start again
+ lastFoundTime = nowTick;
+ bitCount = 0;
+ rfidBuffer = 0;
+ bRet = false;
}
- else {
- // time reached but unknown bitCount, clear and start again
- lastFoundTime=nowTick;
- bitCount=0;
- rfidBuffer=0;
- bRet=false;
- }
+ } else {
+ bRet = false; // watching time not finished
}
- else{
- bRet=false; // watching time not finished
- }
- #if (DEV_WIEGAND_TEST_MODE)>0
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: tag out: %llu "), rfid);
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: tag size: %u"), tagSize);
- #endif
+#if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: tag out: %llu "), rfid);
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: tag size: %u"), tagSize);
+#endif
return bRet;
}
void Wiegand::ScanForTag() {
-
if (!isInit) { return;}
- #if (DEV_WIEGAND_TEST_MODE)>0
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: ScanForTag()."));
- #if (DEV_WIEGAND_TEST_MODE==1)
- switch (millis() %4 ) {
- case 0:
- rfidBuffer = GetRandomRfid(24);
- break;
- case 1:
- rfidBuffer = GetRandomRfid(26);
- break;
- case 2:
- rfidBuffer = GetRandomRfid(32);
- break;
- case 3:
- rfidBuffer = GetRandomRfid(34);
- break;
- default:
- rfidBuffer = GetRandomRfid(34);
- break;
- }
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: raw generated: %lX"), rfidBuffer); // for tests without reader attaiched
- #endif
- #endif
+#if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: ScanForTag()."));
+#if (DEV_WIEGAND_TEST_MODE==1)
+ switch (millis() %4 ) {
+ case 0:
+ rfidBuffer = GetRandomRfid(24);
+ break;
+ case 1:
+ rfidBuffer = GetRandomRfid(26);
+ break;
+ case 2:
+ rfidBuffer = GetRandomRfid(32);
+ break;
+ case 3:
+ rfidBuffer = GetRandomRfid(34);
+ break;
+ default:
+ rfidBuffer = GetRandomRfid(34);
+ break;
+ }
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: raw generated: %lX"), rfidBuffer); // for tests without reader attaiched
+#endif
+#endif
if (bitCount > 0) {
uint64_t oldTag = rfid;
bool validKey = WiegandConversion();
- #if (DEV_WIEGAND_TEST_MODE)>0
- AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: previous tag: %llu"), oldTag);
- #endif
+#if (DEV_WIEGAND_TEST_MODE)>0
+ AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: previous tag: %llu"), oldTag);
+#endif
// only in case of valid key do action. Issue#10585
if(validKey) {
if (oldTag != rfid) { AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: new= %llu"), rfid); }
else { AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: prev= %llu"), rfid); }
AddLog_P(LOG_LEVEL_INFO, PSTR("WIE: bits= %u"), tagSize);
ResponseTime_P(PSTR(",\"Wiegand\":{\"UID\":\"%0llu\"}}"), rfid);
- MqttPublishTeleSensor();
- }
- }
+ MqttPublishTeleSensor();
+ }
+ }
}
#ifdef USE_WEBSERVER
@@ -410,27 +405,26 @@ bool Xsns82(byte function) {
scanDelay = 1;
break;
- case FUNC_EVERY_250_MSECOND: // some tags need more time, don't try shorter period
- #if (DEV_WIEGAND_TEST_MODE)==1
- if (scanDelay>=4) // give a second because of the log entries to be send.
- #else
- if (scanDelay>=2) // only run every (delay * 250 ms) (every 250ms is too fast for some tags)
- #endif
+ case FUNC_EVERY_250_MSECOND: // Some tags need more time, don't try shorter period
+#if (DEV_WIEGAND_TEST_MODE)==1
+ if (scanDelay >= 4) // Give a second because of the log entries to be send.
+#else
+ if (scanDelay >= 2) // Only run every (delay * 250 ms) (every 250ms is too fast for some tags)
+#endif
{
oWiegand->ScanForTag();
scanDelay = 1;
- }
- else {
+ } else {
scanDelay++;
}
break;
-
- #ifdef USE_WEBSERVER
+#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
oWiegand->Show();
break;
- #endif // USE_WEBSERVER
+#endif // USE_WEBSERVER
}
return result;
}
+
#endif // USE_WIEGAND
\ No newline at end of file
From 06221f3ef1f64d086b6473bbf482d87fdfb4fe7d Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Sun, 17 Jan 2021 17:52:08 +0100
Subject: [PATCH 013/186] Zigbee add RGBb taking into account brightness
---
tasmota/xdrv_23_zigbee_5__constants.ino | 230 ++++++++++++------------
tasmota/xdrv_23_zigbee_5_converters.ino | 19 ++
2 files changed, 135 insertions(+), 114 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_5__constants.ino b/tasmota/xdrv_23_zigbee_5__constants.ino
index 4176c7ee4..fc8b95661 100644
--- a/tasmota/xdrv_23_zigbee_5__constants.ino
+++ b/tasmota/xdrv_23_zigbee_5__constants.ino
@@ -381,6 +381,7 @@ const char Z_strings[] PROGMEM =
"ProductURL" "\x00"
"QualityMeasure" "\x00"
"RGB" "\x00"
+ "RGBb" "\x00"
"RMSCurrent" "\x00"
"RMSVoltage" "\x00"
"ReactivePower" "\x00"
@@ -804,120 +805,121 @@ enum Z_offsets {
Zo_ProductURL = 4956,
Zo_QualityMeasure = 4967,
Zo_RGB = 4982,
- Zo_RMSCurrent = 4986,
- Zo_RMSVoltage = 4997,
- Zo_ReactivePower = 5008,
- Zo_RecallScene = 5022,
- Zo_RemainingTime = 5034,
- Zo_RemoteSensing = 5048,
- Zo_RemoveAllGroups = 5062,
- Zo_RemoveAllScenes = 5078,
- Zo_RemoveGroup = 5094,
- Zo_RemoveScene = 5106,
- Zo_ResetAlarm = 5118,
- Zo_ResetAllAlarms = 5129,
- Zo_SWBuildID = 5144,
- Zo_Sat = 5154,
- Zo_SatMove = 5158,
- Zo_SatStep = 5166,
- Zo_SceneCount = 5174,
- Zo_SceneValid = 5185,
- Zo_ScheduleMode = 5196,
- Zo_SeaPressure = 5209,
- Zo_ShortPollInterval = 5221,
- Zo_Shutter = 5239,
- Zo_ShutterClose = 5247,
- Zo_ShutterLift = 5260,
- Zo_ShutterOpen = 5272,
- Zo_ShutterStop = 5284,
- Zo_ShutterTilt = 5296,
- Zo_SoftwareRevision = 5308,
- Zo_StackVersion = 5325,
- Zo_StandardTime = 5338,
- Zo_StartUpOnOff = 5351,
- Zo_Status = 5364,
- Zo_StoreScene = 5371,
- Zo_SwitchType = 5382,
- Zo_SystemMode = 5393,
- Zo_TRVBoost = 5404,
- Zo_TRVChildProtection = 5413,
- Zo_TRVMirrorDisplay = 5432,
- Zo_TRVMode = 5449,
- Zo_TRVWindowOpen = 5457,
- Zo_TempTarget = 5471,
- Zo_Temperature = 5482,
- Zo_TemperatureMaxMeasuredValue = 5494,
- Zo_TemperatureMinMeasuredValue = 5522,
- Zo_TemperatureTolerance = 5550,
- Zo_TerncyDuration = 5571,
- Zo_TerncyRotate = 5586,
- Zo_ThSetpoint = 5599,
- Zo_Time = 5610,
- Zo_TimeEpoch = 5615,
- Zo_TimeStatus = 5625,
- Zo_TimeZone = 5636,
- Zo_TotalProfileNum = 5645,
- Zo_TuyaAutoLock = 5661,
- Zo_TuyaAwayDays = 5674,
- Zo_TuyaAwayTemp = 5687,
- Zo_TuyaBattery = 5700,
- Zo_TuyaBoostTime = 5712,
- Zo_TuyaChildLock = 5726,
- Zo_TuyaComfortTemp = 5740,
- Zo_TuyaEcoTemp = 5756,
- Zo_TuyaFanMode = 5768,
- Zo_TuyaForceMode = 5780,
- Zo_TuyaMaxTemp = 5794,
- Zo_TuyaMinTemp = 5806,
- Zo_TuyaPreset = 5818,
- Zo_TuyaScheduleHolidays = 5829,
- Zo_TuyaScheduleWorkdays = 5850,
- Zo_TuyaTempTarget = 5871,
- Zo_TuyaValveDetection = 5886,
- Zo_TuyaValvePosition = 5905,
- Zo_TuyaWeekSelect = 5923,
- Zo_TuyaWindowDetection = 5938,
- Zo_UnoccupiedCoolingSetpoint = 5958,
- Zo_UnoccupiedHeatingSetpoint = 5984,
- Zo_UtilityName = 6010,
- Zo_ValidUntilTime = 6022,
- Zo_ValvePosition = 6037,
- Zo_VelocityLift = 6051,
- Zo_ViewGroup = 6064,
- Zo_ViewScene = 6074,
- Zo_Water = 6084,
- Zo_WhitePointX = 6090,
- Zo_WhitePointY = 6102,
- Zo_WindowCoveringType = 6114,
- Zo_X = 6133,
- Zo_Y = 6135,
- Zo_ZCLVersion = 6137,
- Zo_ZoneState = 6148,
- Zo_ZoneStatus = 6158,
- Zo_ZoneStatusChange = 6169,
- Zo_ZoneType = 6186,
- Zo_xx = 6195,
- Zo_xx000A00 = 6198,
- Zo_xx0A = 6207,
- Zo_xx0A00 = 6212,
- Zo_xx19 = 6219,
- Zo_xx190A = 6224,
- Zo_xx190A00 = 6231,
- Zo_xxxx = 6240,
- Zo_xxxx00 = 6245,
- Zo_xxxx0A00 = 6252,
- Zo_xxxxyy = 6261,
- Zo_xxxxyyyy = 6268,
- Zo_xxxxyyyy0A00 = 6277,
- Zo_xxxxyyzz = 6290,
- Zo_xxyy = 6299,
- Zo_xxyy0A00 = 6304,
- Zo_xxyyyy = 6313,
- Zo_xxyyyy000000000000 = 6320,
- Zo_xxyyyy0A0000000000 = 6339,
- Zo_xxyyyyzz = 6358,
- Zo_xxyyyyzzzz = 6367,
- Zo_xxyyzzzz = 6378,
+ Zo_RGBb = 4986,
+ Zo_RMSCurrent = 4991,
+ Zo_RMSVoltage = 5002,
+ Zo_ReactivePower = 5013,
+ Zo_RecallScene = 5027,
+ Zo_RemainingTime = 5039,
+ Zo_RemoteSensing = 5053,
+ Zo_RemoveAllGroups = 5067,
+ Zo_RemoveAllScenes = 5083,
+ Zo_RemoveGroup = 5099,
+ Zo_RemoveScene = 5111,
+ Zo_ResetAlarm = 5123,
+ Zo_ResetAllAlarms = 5134,
+ Zo_SWBuildID = 5149,
+ Zo_Sat = 5159,
+ Zo_SatMove = 5163,
+ Zo_SatStep = 5171,
+ Zo_SceneCount = 5179,
+ Zo_SceneValid = 5190,
+ Zo_ScheduleMode = 5201,
+ Zo_SeaPressure = 5214,
+ Zo_ShortPollInterval = 5226,
+ Zo_Shutter = 5244,
+ Zo_ShutterClose = 5252,
+ Zo_ShutterLift = 5265,
+ Zo_ShutterOpen = 5277,
+ Zo_ShutterStop = 5289,
+ Zo_ShutterTilt = 5301,
+ Zo_SoftwareRevision = 5313,
+ Zo_StackVersion = 5330,
+ Zo_StandardTime = 5343,
+ Zo_StartUpOnOff = 5356,
+ Zo_Status = 5369,
+ Zo_StoreScene = 5376,
+ Zo_SwitchType = 5387,
+ Zo_SystemMode = 5398,
+ Zo_TRVBoost = 5409,
+ Zo_TRVChildProtection = 5418,
+ Zo_TRVMirrorDisplay = 5437,
+ Zo_TRVMode = 5454,
+ Zo_TRVWindowOpen = 5462,
+ Zo_TempTarget = 5476,
+ Zo_Temperature = 5487,
+ Zo_TemperatureMaxMeasuredValue = 5499,
+ Zo_TemperatureMinMeasuredValue = 5527,
+ Zo_TemperatureTolerance = 5555,
+ Zo_TerncyDuration = 5576,
+ Zo_TerncyRotate = 5591,
+ Zo_ThSetpoint = 5604,
+ Zo_Time = 5615,
+ Zo_TimeEpoch = 5620,
+ Zo_TimeStatus = 5630,
+ Zo_TimeZone = 5641,
+ Zo_TotalProfileNum = 5650,
+ Zo_TuyaAutoLock = 5666,
+ Zo_TuyaAwayDays = 5679,
+ Zo_TuyaAwayTemp = 5692,
+ Zo_TuyaBattery = 5705,
+ Zo_TuyaBoostTime = 5717,
+ Zo_TuyaChildLock = 5731,
+ Zo_TuyaComfortTemp = 5745,
+ Zo_TuyaEcoTemp = 5761,
+ Zo_TuyaFanMode = 5773,
+ Zo_TuyaForceMode = 5785,
+ Zo_TuyaMaxTemp = 5799,
+ Zo_TuyaMinTemp = 5811,
+ Zo_TuyaPreset = 5823,
+ Zo_TuyaScheduleHolidays = 5834,
+ Zo_TuyaScheduleWorkdays = 5855,
+ Zo_TuyaTempTarget = 5876,
+ Zo_TuyaValveDetection = 5891,
+ Zo_TuyaValvePosition = 5910,
+ Zo_TuyaWeekSelect = 5928,
+ Zo_TuyaWindowDetection = 5943,
+ Zo_UnoccupiedCoolingSetpoint = 5963,
+ Zo_UnoccupiedHeatingSetpoint = 5989,
+ Zo_UtilityName = 6015,
+ Zo_ValidUntilTime = 6027,
+ Zo_ValvePosition = 6042,
+ Zo_VelocityLift = 6056,
+ Zo_ViewGroup = 6069,
+ Zo_ViewScene = 6079,
+ Zo_Water = 6089,
+ Zo_WhitePointX = 6095,
+ Zo_WhitePointY = 6107,
+ Zo_WindowCoveringType = 6119,
+ Zo_X = 6138,
+ Zo_Y = 6140,
+ Zo_ZCLVersion = 6142,
+ Zo_ZoneState = 6153,
+ Zo_ZoneStatus = 6163,
+ Zo_ZoneStatusChange = 6174,
+ Zo_ZoneType = 6191,
+ Zo_xx = 6200,
+ Zo_xx000A00 = 6203,
+ Zo_xx0A = 6212,
+ Zo_xx0A00 = 6217,
+ Zo_xx19 = 6224,
+ Zo_xx190A = 6229,
+ Zo_xx190A00 = 6236,
+ Zo_xxxx = 6245,
+ Zo_xxxx00 = 6250,
+ Zo_xxxx0A00 = 6257,
+ Zo_xxxxyy = 6266,
+ Zo_xxxxyyyy = 6273,
+ Zo_xxxxyyyy0A00 = 6282,
+ Zo_xxxxyyzz = 6295,
+ Zo_xxyy = 6304,
+ Zo_xxyy0A00 = 6309,
+ Zo_xxyyyy = 6318,
+ Zo_xxyyyy000000000000 = 6325,
+ Zo_xxyyyy0A0000000000 = 6344,
+ Zo_xxyyyyzz = 6363,
+ Zo_xxyyyyzzzz = 6372,
+ Zo_xxyyzzzz = 6383,
};
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index 28f222c99..1f47204a0 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -510,6 +510,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
{ Zuint16, Cx0300, 0x003B, Z_(ColorPointBY), Cm1, 0 },
{ Zuint8, Cx0300, 0x003C, Z_(ColorPointBIntensity), Cm1, 0 },
{ Zoctstr, Cx0300, 0xFFF0, Z_(RGB), Cm1, 0 }, // synthetic argument to show color as RGB (converted from HueSat or XY)
+ { Zoctstr, Cx0300, 0xFFF1, Z_(RGBb), Cm1, 0 }, // synthetic argument to show color as RGB including last known brightness
// Illuminance Measurement cluster
{ Zuint16, Cx0400, 0x0000, Z_(Illuminance), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_PIR, illuminance) }, // Illuminance (in Lux)
@@ -1287,6 +1288,7 @@ void ZCLFrame::removeInvalidAttributes(Z_attribute_list& attr_list) {
// Note: both function are now split to compute on extracted attributes
//
void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
+ const Z_Device & device = zigbee_devices.findShortAddr(_srcaddr);
const char * model_c = zigbee_devices.getModelId(_srcaddr); // null if unknown
String modelId((char*) model_c);
// scan through attributes and apply specific converters
@@ -1360,6 +1362,23 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
rgb.add8(g);
rgb.add8(b);
attr_list.addAttribute(0x0300, 0xFFF0).setBuf(rgb, 0, 3);
+
+ // do we know ZbData for this bulb
+ uint8_t brightness = 255;
+ if (device.valid()) {
+ const Z_Data_Light & light = device.data.find(_srcendpoint);
+ if (light.validDimmer()) {
+ // Dimmer has a valid value
+ brightness = changeUIntScale(light.getDimmer(), 0, 254, 0, 255); // range is 0..255
+ }
+ }
+ r = changeUIntScale(r, 0, 255, 0, brightness);
+ g = changeUIntScale(g, 0, 255, 0, brightness);
+ b = changeUIntScale(b, 0, 255, 0, brightness);
+ rgb.set8(0, r);
+ rgb.set8(1, g);
+ rgb.set8(2, b);
+ attr_list.addAttribute(0x0300, 0xFFF1).setBuf(rgb, 0, 3);
}
}
}
From e089fae11a001b47712549ee3ab29a1381547ddb Mon Sep 17 00:00:00 2001
From: Simon Hailes
Date: Sun, 17 Jan 2021 17:05:10 +0000
Subject: [PATCH 014/186] update NimBLE to
5dc72ab10d9f928442a25ef3bdcf8a31a7e16301
---
lib/libesp32/NimBLE-Arduino/CHANGELOG.md | 69 ++++
lib/libesp32/NimBLE-Arduino/README.md | 4 +-
.../docs/Command_line_config.md | 93 +++++
.../NimBLE_Secure_Client.ino | 91 +++++
.../NimBLE_Secure_Server.ino | 37 ++
.../BLE_notify/BLE_notify.ino | 6 +-
.../BLE_server/BLE_server.ino | 7 +-
.../BLE_server_multiconnect.ino | 6 +-
lib/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp | 10 +-
lib/libesp32/NimBLE-Arduino/src/FreeRTOS.h | 6 +-
.../NimBLE-Arduino/src/NimBLE2904.cpp | 2 +-
.../NimBLE-Arduino/src/NimBLEAdvertising.cpp | 289 ++++++++++++----
.../NimBLE-Arduino/src/NimBLEAdvertising.h | 11 +-
.../src/NimBLECharacteristic.cpp | 3 +-
.../NimBLE-Arduino/src/NimBLEClient.cpp | 318 ++++++++++++------
.../NimBLE-Arduino/src/NimBLEClient.h | 11 +-
.../NimBLE-Arduino/src/NimBLEDevice.cpp | 94 ++++--
.../NimBLE-Arduino/src/NimBLEDevice.h | 2 +
.../NimBLE-Arduino/src/NimBLEHIDDevice.cpp | 233 +++++++++++++
.../NimBLE-Arduino/src/NimBLEHIDDevice.h | 85 +++++
.../src/NimBLERemoteCharacteristic.cpp | 46 ++-
.../src/NimBLERemoteDescriptor.cpp | 4 +-
.../src/NimBLERemoteService.cpp | 34 +-
.../NimBLE-Arduino/src/NimBLERemoteService.h | 1 +
.../NimBLE-Arduino/src/NimBLEScan.cpp | 103 +++---
lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h | 4 +-
.../NimBLE-Arduino/src/NimBLEServer.cpp | 5 +
.../NimBLE-Arduino/src/NimBLEServer.h | 9 +
.../NimBLE-Arduino/src/NimBLEUUID.cpp | 31 ++
.../src/esp-hci/src/esp_nimble_hci.c | 97 ++++--
.../NimBLE-Arduino/src/esp_nimble_cfg.h | 4 +
.../src/nimble/host/src/ble_eddystone.c | 2 +-
.../src/nimble/host/src/ble_gap.c | 6 +-
.../src/nimble/host/src/ble_hs_conn.c | 106 +++---
.../host/store/config/src/ble_store_nvs.c | 42 ++-
lib/libesp32/NimBLE-Arduino/src/nimconfig.h | 44 ++-
36 files changed, 1537 insertions(+), 378 deletions(-)
create mode 100644 lib/libesp32/NimBLE-Arduino/docs/Command_line_config.md
create mode 100644 lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Client/NimBLE_Secure_Client.ino
create mode 100644 lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Server/NimBLE_Secure_Server.ino
create mode 100644 lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp
create mode 100644 lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.h
diff --git a/lib/libesp32/NimBLE-Arduino/CHANGELOG.md b/lib/libesp32/NimBLE-Arduino/CHANGELOG.md
index 8dfc5a141..128d3c93d 100644
--- a/lib/libesp32/NimBLE-Arduino/CHANGELOG.md
+++ b/lib/libesp32/NimBLE-Arduino/CHANGELOG.md
@@ -2,6 +2,75 @@
All notable changes to this project will be documented in this file.
+## [Unreleased]
+
+### Added
+- `NimBLEDevice::setOwnAddrType` added to enable the use of random and random-resolvable addresses, by asukiaaa
+
+- New examples for securing and authenticating client/server connections, by mblasee.
+
+- `NimBLEAdvertiseing::SetMinPreferred` and `NimBLEAdvertiseing::SetMinPreferred` re-added.
+
+- Conditional checks added for command line config options in `nimconfig.h` to support custom configuration in platformio.
+
+- `NimBLEClient::setValue` Now takes an extra bool parameter `response` to enable the use of write with response (default = false).
+
+- `NimBLEClient::getCharacteristic(uint16_t handle)` Enabling the use of the characteristic handle to be used to find
+the NimBLERemoteCharacteristic object.
+
+- `NimBLEHIDDevice` class added by wakwak-koba.
+
+- `NimBLEServerCallbacks::onDisconnect` overloaded callback added to provide a ble_gap_conn_desc parameter for the application
+to obtain information about the disconnected client.
+
+- Conditional checks in `nimconfig.h` for command line defined macros to support platformio config settings.
+
+### Changed
+- `NimBLEAdvertising::start` now returns a bool value to indicate success/failure.
+
+- Some asserts were removed in `NimBLEAdvertising::start` and replaced with better return code handling and logging.
+
+- If a host reset event occurs, scanning and advertising will now only be restarted if their previous duration was indefinite.
+
+- `NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::registerForNotify` will now set the callback
+regardless of the existance of the CCCD and return true unless the descriptor write operation failed.
+
+- Advertising tx power level is now sent in the advertisement packet instead of scan response.
+
+- `NimBLEScan` When the scan ends the scan stopped flag is now set before calling the scan complete callback (if used)
+this allows the starting of a new scan from the callback function.
+
+### Fixed
+- Sometimes `NimBLEClient::connect` would hang on the task block if no event arrived to unblock.
+A time limit has been added to timeout appropriately.
+
+- When getting descriptors for a characterisic the end handle of the service was used as a proxy for the characteristic end
+handle. This would be rejected by some devices and has been changed to use the next characteristic handle as the end when possible.
+
+- An exception could occur when deleting a client instance if a notification arrived while the attribute vectors were being
+deleted. A flag has been added to prevent this.
+
+- An exception could occur after a host reset event when the host re-synced if the tasks that were stopped during the event did
+not finish processing. A yield has been added after re-syncing to allow tasks to finish before proceeding.
+
+- Occasionally the controller would fail to send a disconnected event causing the client to indicate it is connected
+and would be unable to reconnect. A timer has been added to reset the host/controller if it expires.
+
+- Occasionally the call to start scanning would get stuck in a loop on BLE_HS_EBUSY, this loop has been removed.
+
+- 16bit and 32bit UUID's in some cases were not discovered or compared correctly if the device
+advertised them as 16/32bit but resolved them to 128bits. Both are now checked.
+
+- `FreeRTOS` compile errors resolved in latest Ardruino core and IDF v3.3.
+
+- Multiple instances of `time()` called inside critical sections caused sporadic crashes, these have been moved out of critical regions.
+
+- Advertisement type now correctly set when using non-connectable (advertiser only) mode.
+
+- Advertising payload length correction, now accounts for appearance.
+
+- (Arduino) Ensure controller mode is set to BLE Only.
+
## [1.0.2] - 2020-09-13
### Changed
diff --git a/lib/libesp32/NimBLE-Arduino/README.md b/lib/libesp32/NimBLE-Arduino/README.md
index 120b0c782..ea28b8811 100644
--- a/lib/libesp32/NimBLE-Arduino/README.md
+++ b/lib/libesp32/NimBLE-Arduino/README.md
@@ -68,9 +68,9 @@ such as increasing max connections, default is 3, absolute maximum connections i
# Development Status
-This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@95bd864.](https://github.com/espressif/esp-nimble)
+This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@f4ae049.](https://github.com/espressif/esp-nimble)
-Also tracking the NimBLE related changes in ESP-IDF, master branch, currently [@2ef4890.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
+Also tracking the NimBLE related changes in ESP-IDF, master branch, currently [@3caa969.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
# Acknowledgments
diff --git a/lib/libesp32/NimBLE-Arduino/docs/Command_line_config.md b/lib/libesp32/NimBLE-Arduino/docs/Command_line_config.md
new file mode 100644
index 000000000..813156f76
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/docs/Command_line_config.md
@@ -0,0 +1,93 @@
+# Arduino command line and platformio config options
+
+`CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED`
+
+ If defined, NimBLE Client functions will not be included.
+- Reduces flash size by approx. 7kB.
+
+
+`CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED`
+
+If defined, NimBLE Scan functions will not be included.
+- Reduces flash size by approx. 26kB.
+
+
+`CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED`
+
+If defined NimBLE Server functions will not be included.
+- Reduces flash size by approx. 16kB.
+
+
+`CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED`
+
+If defined, NimBLE Advertising functions will not be included.
+- Reduces flash size by approx. 5kB.
+
+
+`CONFIG_BT_NIMBLE_DEBUG`
+
+If defined, enables debug log messages from the NimBLE host
+- Uses approx. 32kB of flash memory.
+
+
+`CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT`
+
+If defined, NimBLE host return codes will be printed as text in debug log messages.
+- Uses approx. 7kB of flash memory.
+
+
+`CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT`
+
+If defined, GAP event codes will be printed as text in debug log messages.
+- Uses approx. 1kB of flash memory.
+
+
+`CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT`
+
+If defined, advertisment types will be printed as text while scanning in debug log messages.
+- Uses approx. 250 bytes of flash memory.
+
+
+`CONFIG_BT_NIMBLE_PINNED_TO_CORE`
+
+Sets the core the NimBLE host stack will run on
+- Options: 0 or 1
+
+
+`CONFIG_BT_NIMBLE_TASK_STACK_SIZE`
+
+Set the task stack size for the NimBLE core.
+- Default is 4096
+
+
+
+`CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL`
+
+Sets the NimBLE stack to use external PSRAM will be loaded
+- Must be defined with a value of 1; Default is CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
+
+
+`CONFIG_BT_NIMBLE_MAX_CONNECTIONS`
+
+Sets the number of simultaneous connections (esp controller max is 9)
+- Default value is 3
+
+
+`CONFIG_BT_NIMBLE_MAX_BONDS`
+
+Sets the number of devices allowed to store/bond with
+- Default value is 3
+
+
+`CONFIG_BT_NIMBLE_MAX_CCCDS`
+
+Sets the maximum number of CCCD subscriptions to store
+- Default value is 8
+
+
+`CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME`
+
+Set the default device name
+- Default value is "nimble"
+
+
diff --git a/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Client/NimBLE_Secure_Client.ino b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Client/NimBLE_Secure_Client.ino
new file mode 100644
index 000000000..6f9af4f74
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Client/NimBLE_Secure_Client.ino
@@ -0,0 +1,91 @@
+/** NimBLE_Secure_Client Demo:
+ *
+ * This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client.
+ * Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective.
+ * To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash.
+ *
+ * Created: on Jan 08 2021
+ * Author: mblasee
+ */
+
+#include
+
+class ClientCallbacks : public NimBLEClientCallbacks
+{
+ uint32_t onPassKeyRequest()
+ {
+ Serial.println("Client Passkey Request");
+ /** return the passkey to send to the server */
+ /** Change this to be different from NimBLE_Secure_Server if you want to test what happens on key mismatch */
+ return 123456;
+ };
+};
+static ClientCallbacks clientCB;
+
+void setup()
+{
+ Serial.begin(115200);
+ Serial.println("Starting NimBLE Client");
+
+ NimBLEDevice::init("");
+ NimBLEDevice::setPower(ESP_PWR_LVL_P9);
+ NimBLEDevice::setSecurityAuth(true, true, true);
+ NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY);
+ NimBLEScan *pScan = NimBLEDevice::getScan();
+ NimBLEScanResults results = pScan->start(5);
+
+ NimBLEUUID serviceUuid("ABCD");
+
+ for (int i = 0; i < results.getCount(); i++)
+ {
+ NimBLEAdvertisedDevice device = results.getDevice(i);
+ Serial.println(device.getName().c_str());
+
+ if (device.isAdvertisingService(serviceUuid))
+ {
+ NimBLEClient *pClient = NimBLEDevice::createClient();
+ pClient->setClientCallbacks(&clientCB, false);
+
+ if (pClient->connect(&device))
+ {
+ pClient->secureConnection();
+ NimBLERemoteService *pService = pClient->getService(serviceUuid);
+ if (pService != nullptr)
+ {
+ NimBLERemoteCharacteristic *pNonSecureCharacteristic = pService->getCharacteristic("1234");
+
+ if (pNonSecureCharacteristic != nullptr)
+ {
+ // Testing to read a non secured characteristic, you should be able to read this even if you have mismatching passkeys.
+ std::string value = pNonSecureCharacteristic->readValue();
+ // print or do whatever you need with the value
+ Serial.println(value.c_str());
+ }
+
+ NimBLERemoteCharacteristic *pSecureCharacteristic = pService->getCharacteristic("1235");
+
+ if (pSecureCharacteristic != nullptr)
+ {
+ // Testing to read a secured characteristic, you should be able to read this only if you have matching passkeys, otherwise you should
+ // get an error like this. E NimBLERemoteCharacteristic: "<< readValue rc=261"
+ // This means you are trying to do something without the proper permissions.
+ std::string value = pSecureCharacteristic->readValue();
+ // print or do whatever you need with the value
+ Serial.println(value.c_str());
+ }
+ }
+ }
+ else
+ {
+ // failed to connect
+ Serial.println("failed to connect");
+ }
+
+ NimBLEDevice::deleteClient(pClient);
+ }
+ }
+}
+
+void loop()
+{
+}
diff --git a/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Server/NimBLE_Secure_Server.ino b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Server/NimBLE_Secure_Server.ino
new file mode 100644
index 000000000..f63cbc162
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Server/NimBLE_Secure_Server.ino
@@ -0,0 +1,37 @@
+/** NimBLE_Secure_Server Demo:
+ *
+ * This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client.
+ * Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective.
+ * To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash.
+ *
+ * Created: on Jan 08 2021
+ * Author: mblasee
+ */
+
+#include
+
+void setup() {
+ Serial.begin(115200);
+ Serial.println("Starting NimBLE Server");
+ NimBLEDevice::init("NimBLE");
+ NimBLEDevice::setPower(ESP_PWR_LVL_P9);
+
+ NimBLEDevice::setSecurityAuth(true, true, true);
+ NimBLEDevice::setSecurityPasskey(123456);
+ NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
+ NimBLEServer *pServer = NimBLEDevice::createServer();
+ NimBLEService *pService = pServer->createService("ABCD");
+ NimBLECharacteristic *pNonSecureCharacteristic = pService->createCharacteristic("1234", NIMBLE_PROPERTY::READ );
+ NimBLECharacteristic *pSecureCharacteristic = pService->createCharacteristic("1235", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::READ_AUTHEN);
+
+ pService->start();
+ pNonSecureCharacteristic->setValue("Hello Non Secure BLE");
+ pSecureCharacteristic->setValue("Hello Secure BLE");
+
+ NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
+ pAdvertising->addServiceUUID("ABCD");
+ pAdvertising->start();
+}
+
+void loop() {
+}
diff --git a/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino
index 83f129b83..cb0488819 100644
--- a/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino
+++ b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino
@@ -116,9 +116,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
- /**This method is removed as it was no longer useful and consumed advertising space
- * pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
- */
+ /** Note, this could be left out as that is the default value */
+ pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
+
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
}
diff --git a/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino
index 652d77685..faa4d88ea 100644
--- a/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino
+++ b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino
@@ -44,10 +44,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
- /**These methods are removed as they are no longer useful and consumed advertising space
- * pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
- * pAdvertising->setMinPreferred(0x12);
- */
+ pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
+ pAdvertising->setMaxPreferred(0x12);
+
BLEDevice::startAdvertising();
Serial.println("Characteristic defined! Now you can read it in your phone!");
}
diff --git a/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino
index 2ec38c481..9ae3859c7 100644
--- a/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino
+++ b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino
@@ -120,9 +120,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
- /**This method is removed it was no longer useful and consumed advertising space
- * pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
- */
+ /** Note, this could be left out as that is the default value */
+ pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
+
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
}
diff --git a/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp b/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp
index 4ebab3958..1c398cbf3 100644
--- a/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp
@@ -264,10 +264,14 @@ void FreeRTOS::Semaphore::setName(std::string name) {
* @param [in] length The amount of storage to allocate for the ring buffer.
* @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF.
*/
-#if defined(ESP_IDF_VERSION) && !defined(ESP_IDF_VERSION_VAL) //Quick hack to detect if using IDF version that replaced ringbuf_type_t, ESP_IDF_VERSION_VAL is for IDF>4.0.0
-Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
+#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
+ Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
#else
-Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
+ Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
+#endif
+#else
+ Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
#endif
m_handle = ::xRingbufferCreate(length, type);
} // Ringbuffer
diff --git a/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.h b/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.h
index f93f0b1a0..fa33921fe 100644
--- a/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.h
+++ b/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.h
@@ -68,8 +68,12 @@ public:
*/
class Ringbuffer {
public:
-#if defined(ESP_IDF_VERSION) && !defined(ESP_IDF_VERSION_VAL) //Quick hack to detect if using IDF version that replaced ringbuf_type_t, ESP_IDF_VERSION_VAL is for IDF>4.0.0
+#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
Ringbuffer(size_t length, RingbufferType_t type = RINGBUF_TYPE_NOSPLIT);
+#else
+ Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
+#endif
#else
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
#endif
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp
index d85cd87e4..80318b5b8 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp
@@ -37,7 +37,7 @@ NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacterisitic)
m_data.m_unit = 0;
m_data.m_description = 0;
setValue((uint8_t*) &m_data, sizeof(m_data));
-} // BLE2902
+} // BLE2904
/**
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
index 36bdbf9e9..a10480410 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
@@ -32,7 +32,7 @@ static const char* LOG_TAG = "NimBLEAdvertising";
/**
* @brief Construct a default advertising object.
*/
-NimBLEAdvertising::NimBLEAdvertising() {
+NimBLEAdvertising::NimBLEAdvertising() : m_slaveItvl() {
memset(&m_advData, 0, sizeof m_advData);
memset(&m_scanData, 0, sizeof m_scanData);
memset(&m_advParams, 0, sizeof m_advParams);
@@ -41,15 +41,20 @@ NimBLEAdvertising::NimBLEAdvertising() {
m_advData.name = (uint8_t *)name;
m_advData.name_len = strlen(name);
m_advData.name_is_complete = 1;
- m_scanData.tx_pwr_lvl_is_present = 1;
- m_scanData.tx_pwr_lvl = NimBLEDevice::getPower();
+ m_advData.tx_pwr_lvl_is_present = 1;
+ m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
m_advData.appearance = 0;
m_advData.appearance_is_present = 0;
m_advData.mfg_data_len = 0;
m_advData.mfg_data = nullptr;
+ m_advData.slave_itvl_range = nullptr;
+#if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+ m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON;
+#else
m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND;
+#endif
m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
m_advParams.itvl_min = 0;
m_advParams.itvl_max = 0;
@@ -58,6 +63,8 @@ NimBLEAdvertising::NimBLEAdvertising() {
m_customScanResponseData = false;
m_scanResp = true;
m_advDataSet = false;
+ // Set this to non-zero to prevent auto start if host reset before started by app.
+ m_duration = BLE_HS_FOREVER;
} // NimBLEAdvertising
@@ -86,7 +93,6 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
* @param [in] serviceUUID The UUID of the service to expose.
*/
void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
- //m_serviceUUIDs.erase(std::remove_if(m_serviceUUIDs.begin(), m_serviceUUIDs.end(),[serviceUUID](const NimBLEUUID &s) {return serviceUUID == s;}), m_serviceUUIDs.end());
for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) {
if((*it) == serviceUUID) {
m_serviceUUIDs.erase(it);
@@ -112,11 +118,9 @@ void NimBLEAdvertising::setAppearance(uint16_t appearance) {
/**
* @brief Set the type of advertisment to use.
* @param [in] adv_type:
- * * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising
- * * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertisng - high duty cycle
- * * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response
- * * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertisng - not connectable
- * * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle
+ * * BLE_GAP_CONN_MODE_NON (0) - not connectable advertising
+ * * BLE_GAP_CONN_MODE_DIR (1) - directed connectable advertising
+ * * BLE_GAP_CONN_MODE_UND (2) - undirected connectable advertising
*/
void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){
m_advParams.conn_mode = adv_type;
@@ -141,6 +145,64 @@ void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) {
} // setMaxInterval
+/**
+ * @brief Set the advertised min connection interval preferred by this device.
+ * @param [in] mininterval the max interval value. Range = 0x0006 to 0x0C80.
+ * @details Values not within the range will cancel advertising of this data.\n
+ * Consumes 6 bytes of advertising space (combined with max interval).
+ */
+void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
+ // invalid paramters, set the slave interval to null
+ if(mininterval < 0x0006 || mininterval > 0x0C80) {
+ m_advData.slave_itvl_range = nullptr;
+ return;
+ }
+
+ if(m_advData.slave_itvl_range == nullptr) {
+ m_advData.slave_itvl_range = m_slaveItvl;
+ }
+
+ m_slaveItvl[0] = mininterval;
+ m_slaveItvl[1] = mininterval >> 8;
+
+ uint16_t maxinterval = *(uint16_t*)(m_advData.slave_itvl_range+2);
+
+ // If mininterval is higher than the maxinterval make them the same
+ if(mininterval > maxinterval) {
+ m_slaveItvl[2] = m_slaveItvl[0];
+ m_slaveItvl[3] = m_slaveItvl[1];
+ }
+} // setMinPreferred
+
+
+/**
+ * @brief Set the advertised max connection interval preferred by this device.
+ * @param [in] maxinterval the max interval value. Range = 0x0006 to 0x0C80.
+ * @details Values not within the range will cancel advertising of this data.\n
+ * Consumes 6 bytes of advertising space (combined with min interval).
+ */
+void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
+ // invalid paramters, set the slave interval to null
+ if(maxinterval < 0x0006 || maxinterval > 0x0C80) {
+ m_advData.slave_itvl_range = nullptr;
+ return;
+ }
+ if(m_advData.slave_itvl_range == nullptr) {
+ m_advData.slave_itvl_range = m_slaveItvl;
+ }
+ m_slaveItvl[2] = maxinterval;
+ m_slaveItvl[3] = maxinterval >> 8;
+
+ uint16_t mininterval = *(uint16_t*)(m_advData.slave_itvl_range);
+
+ // If mininterval is higher than the maxinterval make them the same
+ if(mininterval > maxinterval) {
+ m_slaveItvl[0] = m_slaveItvl[2];
+ m_slaveItvl[1] = m_slaveItvl[3];
+ }
+} // setMaxPreferred
+
+
/**
* @brief Set if scan response is available.
* @param [in] set true = scan response available.
@@ -156,7 +218,8 @@ void NimBLEAdvertising::setScanResponse(bool set) {
* @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
*/
void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
- NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly);
+ NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d",
+ scanRequestWhitelistOnly, connectWhitelistOnly);
if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_advParams.filter_policy = BLE_HCI_ADV_FILT_NONE;
NIMBLE_LOGD(LOG_TAG, "<< setScanFilter");
@@ -194,7 +257,8 @@ void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisem
(uint8_t*)advertisementData.getPayload().data(),
advertisementData.getPayload().length());
if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
}
m_customAdvData = true; // Set the flag that indicates we are using custom advertising data.
NIMBLE_LOGD(LOG_TAG, "<< setAdvertisementData");
@@ -213,7 +277,8 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
(uint8_t*)advertisementData.getPayload().data(),
advertisementData.getPayload().length());
if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
}
m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data.
NIMBLE_LOGD(LOG_TAG, "<< setScanResponseData");
@@ -225,13 +290,14 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
* @param [in] duration The duration, in seconds, to advertise, 0 == advertise forever.
* @param [in] advCompleteCB A pointer to a callback to be invoked when advertising ends.
*/
-void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) {
- NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData);
+bool NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) {
+ NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d",
+ m_customAdvData, m_customScanResponseData);
// If Host is not synced we cannot start advertising.
if(!NimBLEDevice::m_synced) {
NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
- return;
+ return false;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
@@ -240,17 +306,21 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(!pServer->m_gattsStarted){
pServer->start();
} else if(pServer->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) {
- NIMBLE_LOGW(LOG_TAG, "Max connections reached - not advertising");
- return;
+ NIMBLE_LOGE(LOG_TAG, "Max connections reached - not advertising");
+ return false;
}
}
#endif
// If already advertising just return
if(ble_gap_adv_active()) {
- return;
+ NIMBLE_LOGW(LOG_TAG, "Advertising already active");
+ return false;
}
+ // Save the duration incase of host reset so we can restart with the same params
+ m_duration = duration;
+
if(duration == 0){
duration = BLE_HS_FOREVER;
}
@@ -260,16 +330,31 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
m_advCompCB = advCompleteCB;
+ m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
+ m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
+ if(m_advParams.conn_mode == BLE_GAP_CONN_MODE_NON) {
+ if(!m_scanResp) {
+ m_advParams.disc_mode = BLE_GAP_DISC_MODE_NON;
+ m_advData.flags = BLE_HS_ADV_F_BREDR_UNSUP;
+ }
+ }
+
int rc = 0;
if (!m_customAdvData && !m_advDataSet) {
//start with 3 bytes for the flags data
- uint8_t payloadLen = 3;
+ uint8_t payloadLen = (2 + 1);
+ if(m_advData.appearance_is_present)
+ payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN);
+ if(m_advData.tx_pwr_lvl_is_present)
+ payloadLen += (2 + 1);
+ if(m_advData.slave_itvl_range != nullptr)
+ payloadLen += (2 + 4);
for(auto &it : m_serviceUUIDs) {
if(it.getNative()->u.type == BLE_UUID_TYPE_16) {
int add = (m_advData.num_uuids16 > 0) ? 2 : 4;
- if((payloadLen + add) > 31){
+ if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids16_is_complete = 0;
continue;
}
@@ -278,7 +363,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16,
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))
{
- NIMBLE_LOGE(LOG_TAG, "Error, no mem");
+ NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids16[m_advData.num_uuids16].value,
@@ -290,7 +375,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
if(it.getNative()->u.type == BLE_UUID_TYPE_32) {
int add = (m_advData.num_uuids32 > 0) ? 4 : 6;
- if((payloadLen + add) > 31){
+ if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids32_is_complete = 0;
continue;
}
@@ -299,7 +384,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32,
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))
{
- NIMBLE_LOGE(LOG_TAG, "Error, no mem");
+ NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids32[m_advData.num_uuids32].value,
@@ -311,7 +396,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
if(it.getNative()->u.type == BLE_UUID_TYPE_128){
int add = (m_advData.num_uuids128 > 0) ? 16 : 18;
- if((payloadLen + add) > 31){
+ if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids128_is_complete = 0;
continue;
}
@@ -320,7 +405,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
{
- NIMBLE_LOGE(LOG_TAG, "Error, no mem");
+ NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids128[m_advData.num_uuids128].value,
@@ -333,54 +418,74 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
// check if there is room for the name, if not put it in scan data
- if((payloadLen + m_advData.name_len) > 29) {
+ if((payloadLen + (2 + m_advData.name_len)) > BLE_HS_ADV_MAX_SZ) {
if(m_scanResp){
m_scanData.name = m_advData.name;
m_scanData.name_len = m_advData.name_len;
- m_scanData.name_is_complete = m_advData.name_is_complete;
+ if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) {
+ m_scanData.name_len = BLE_HS_ADV_MAX_SZ - 2;
+ m_scanData.name_is_complete = 0;
+ } else {
+ m_scanData.name_is_complete = 1;
+ }
m_advData.name = nullptr;
m_advData.name_len = 0;
+ m_advData.name_is_complete = 0;
} else {
+ if(m_advData.tx_pwr_lvl_is_present) {
+ m_advData.tx_pwr_lvl = 0;
+ m_advData.tx_pwr_lvl_is_present = 0;
+ payloadLen -= (2 + 1);
+ }
// if not using scan response just cut the name down
// leaving 2 bytes for the data specifier.
- m_advData.name_len = (29 - payloadLen);
+ if(m_advData.name_len > (BLE_HS_ADV_MAX_SZ - payloadLen - 2)) {
+ m_advData.name_len = (BLE_HS_ADV_MAX_SZ - payloadLen - 2);
+ m_advData.name_is_complete = 0;
+ }
}
- m_advData.name_is_complete = 0;
- }
-
- if(m_advData.name_len > 0) {
- payloadLen += (m_advData.name_len + 2);
}
if(m_scanResp) {
- // name length + type byte + length byte + tx power type + length + data
- if((m_scanData.name_len + 5) > 31) {
- // prioritize name data over tx power
- m_scanData.tx_pwr_lvl_is_present = 0;
- m_scanData.tx_pwr_lvl = 0;
- // limit name to 29 to leave room for the data specifiers
- if(m_scanData.name_len > 29) {
- m_scanData.name_len = 29;
- m_scanData.name_is_complete = false;
- }
- }
-
rc = ble_gap_adv_rsp_set_fields(&m_scanData);
- if (rc != 0) {
- NIMBLE_LOGC(LOG_TAG, "error setting scan response data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
- abort();
+ switch(rc) {
+ case 0:
+ break;
+
+ case BLE_HS_EBUSY:
+ NIMBLE_LOGE(LOG_TAG, "Already advertising");
+ break;
+
+ case BLE_HS_EMSGSIZE:
+ NIMBLE_LOGE(LOG_TAG, "Scan data too long");
+ break;
+
+ default:
+ NIMBLE_LOGE(LOG_TAG, "Error setting scan response data; rc=%d, %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ break;
}
- // if not using scan response and there is room,
- // put the tx power data into the advertisment
- } else if (payloadLen < 29) {
- m_advData.tx_pwr_lvl_is_present = 1;
- m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
}
- rc = ble_gap_adv_set_fields(&m_advData);
- if (rc != 0) {
- NIMBLE_LOGC(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
- abort();
+ if(rc == 0) {
+ rc = ble_gap_adv_set_fields(&m_advData);
+ switch(rc) {
+ case 0:
+ break;
+
+ case BLE_HS_EBUSY:
+ NIMBLE_LOGE(LOG_TAG, "Already advertising");
+ break;
+
+ case BLE_HS_EMSGSIZE:
+ NIMBLE_LOGE(LOG_TAG, "Advertisement data too long");
+ break;
+
+ default:
+ NIMBLE_LOGE(LOG_TAG, "Error setting advertisement data; rc=%d, %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ break;
+ }
}
if(m_advData.num_uuids128 > 0) {
@@ -401,24 +506,54 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
m_advData.num_uuids16 = 0;
}
+ if(rc !=0) {
+ return false;
+ }
+
m_advDataSet = true;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
- rc = ble_gap_adv_start(0, NULL, duration,
+ rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration,
&m_advParams,
- (pServer != nullptr) ? NimBLEServer::handleGapEvent : NimBLEAdvertising::handleGapEvent,
+ (pServer != nullptr) ? NimBLEServer::handleGapEvent :
+ NimBLEAdvertising::handleGapEvent,
(pServer != nullptr) ? (void*)pServer : (void*)this);
#else
- rc = ble_gap_adv_start(0, NULL, duration,
+ rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration,
&m_advParams, NimBLEAdvertising::handleGapEvent, this);
#endif
- if (rc != 0) {
- NIMBLE_LOGC(LOG_TAG, "Error enabling advertising; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
- abort();
+ switch(rc) {
+ case 0:
+ break;
+
+ case BLE_HS_EINVAL:
+ NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long");
+ break;
+
+ case BLE_HS_EPREEMPTED:
+ NIMBLE_LOGE(LOG_TAG, "Unable to advertise - busy");
+ break;
+
+ case BLE_HS_ETIMEOUT_HCI:
+ case BLE_HS_EOS:
+ case BLE_HS_ECONTROLLER:
+ case BLE_HS_ENOTSYNCED:
+ NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset");
+ break;
+
+ default:
+ NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ break;
+ }
+
+ if(rc != 0) {
+ return false;
}
NIMBLE_LOGD(LOG_TAG, "<< Advertising start");
+ return true;
} // start
@@ -427,9 +562,11 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
*/
void NimBLEAdvertising::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop");
+
int rc = ble_gap_adv_stop();
if (rc != 0 && rc != BLE_HS_EALREADY) {
- NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
return;
}
@@ -460,8 +597,17 @@ bool NimBLEAdvertising::isAdvertising() {
* Host reset seems to clear advertising data,
* we need clear the flag so it reloads it.
*/
-void NimBLEAdvertising::onHostReset() {
+void NimBLEAdvertising::onHostSync() {
+ NIMBLE_LOGD(LOG_TAG, "Host re-synced");
+
m_advDataSet = false;
+ // If we were advertising forever, restart it now
+ if(m_duration == 0) {
+ start(m_duration, m_advCompCB);
+ } else {
+ // Otherwise we should tell the app that advertising stopped.
+ advCompleteCB();
+ }
}
@@ -475,6 +621,19 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEAdvertising *pAdv = (NimBLEAdvertising*)arg;
if(event->type == BLE_GAP_EVENT_ADV_COMPLETE) {
+ switch(event->adv_complete.reason) {
+ // Don't call the callback if host reset, we want to
+ // preserve the active flag until re-sync to restart advertising.
+ case BLE_HS_ETIMEOUT_HCI:
+ case BLE_HS_EOS:
+ case BLE_HS_ECONTROLLER:
+ case BLE_HS_ENOTSYNCED:
+ NIMBLE_LOGC(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
+ NimBLEDevice::onReset(event->adv_complete.reason);
+ return 0;
+ default:
+ break;
+ }
pAdv->advCompleteCB();
}
return 0;
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
index 2fab71004..0d97ecbbc 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
@@ -77,7 +77,7 @@ public:
void addServiceUUID(const NimBLEUUID &serviceUUID);
void addServiceUUID(const char* serviceUUID);
void removeServiceUUID(const NimBLEUUID &serviceUUID);
- void start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
+ bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
void stop();
void setAppearance(uint16_t appearance);
void setAdvertisementType(uint8_t adv_type);
@@ -87,13 +87,15 @@ public:
void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
void setScanResponseData(NimBLEAdvertisementData& advertisementData);
void setScanResponse(bool);
+ void setMinPreferred(uint16_t);
+ void setMaxPreferred(uint16_t);
void advCompleteCB();
bool isAdvertising();
private:
friend class NimBLEDevice;
- void onHostReset();
+ void onHostSync();
static int handleGapEvent(struct ble_gap_event *event, void *arg);
ble_hs_adv_fields m_advData;
@@ -104,8 +106,9 @@ private:
bool m_customScanResponseData;
bool m_scanResp;
bool m_advDataSet;
- void (*m_advCompCB)(NimBLEAdvertising *pAdv);
-
+ void (*m_advCompCB)(NimBLEAdvertising *pAdv);
+ uint8_t m_slaveItvl[4];
+ uint32_t m_duration;
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
index 6f7d3d7e0..e9a5a49c9 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
@@ -473,9 +473,10 @@ void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
return;
}
+ time_t t = time(nullptr);
portENTER_CRITICAL(&m_valMux);
m_value = std::string((char*)data, length);
- m_timestamp = time(nullptr);
+ m_timestamp = t;
portEXIT_CRITICAL(&m_valMux);
NIMBLE_LOGD(LOG_TAG, "<< setValue");
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp
index 539a7a016..ddd3abecc 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp
@@ -24,6 +24,9 @@
#include
#include
+#include "nimble/nimble_port.h"
+
+
static const char* LOG_TAG = "NimBLEClient";
static NimBLEClientCallbacks defaultCallbacks;
@@ -56,11 +59,10 @@ static NimBLEClientCallbacks defaultCallbacks;
NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(peerAddress) {
m_pClientCallbacks = &defaultCallbacks;
m_conn_id = BLE_HS_CONN_HANDLE_NONE;
- m_isConnected = false;
- m_waitingToConnect = false;
m_connectTimeout = 30000;
m_deleteCallbacks = false;
m_pTaskData = nullptr;
+ m_connEstablished = false;
m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default)
m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default)
@@ -70,6 +72,9 @@ NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(pee
m_pConnParams.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT; // timeout = 400*10ms = 4000ms
m_pConnParams.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units
m_pConnParams.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units
+
+ ble_npl_callout_init(&m_dcTimer, nimble_port_get_dflt_eventq(),
+ NimBLEClient::dcTimerCb, this);
} // NimBLEClient
@@ -89,6 +94,19 @@ NimBLEClient::~NimBLEClient() {
} // ~NimBLEClient
+/**
+ * @brief If we have asked to disconnect and the event does not
+ * occur within the supervision timeout + added delay, this will
+ * be called to reset the host in the case of a stalled controller.
+ */
+void NimBLEClient::dcTimerCb(ble_npl_event *event) {
+ NimBLEClient *pClient = (NimBLEClient*)event->arg;
+ NIMBLE_LOGC(LOG_TAG, "Timed out disconnecting from %s - resetting host",
+ std::string(pClient->getPeerAddress()).c_str());
+ ble_hs_sched_reset(BLE_HS_ECONTROLLER);
+}
+
+
/**
* @brief Delete all service objects created by this client and clear the vector.
*/
@@ -164,12 +182,9 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
return false;
}
- if(ble_gap_conn_active()) {
- NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait.");
- return false;
- }
-
- if(!NimBLEDevice::getScan()->stop()) {
+ if(isConnected() || m_pTaskData != nullptr) {
+ NIMBLE_LOGE(LOG_TAG, "Client busy, connected to %s, id=%d",
+ std::string(m_peerAddress).c_str(), getConnId());
return false;
}
@@ -180,54 +195,97 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
m_peerAddress = address;
}
- ble_addr_t peerAddrt;
- memcpy(&peerAddrt.val, m_peerAddress.getNative(),6);
- peerAddrt.type = m_peerAddress.getType();
+ ble_addr_t peerAddr_t;
+ memcpy(&peerAddr_t.val, m_peerAddress.getNative(),6);
+ peerAddr_t.type = m_peerAddress.getType();
+
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
- m_pTaskData = &taskData;
-
int rc = 0;
/* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
* timeout (default value of m_connectTimeout).
* Loop on BLE_HS_EBUSY if the scan hasn't stopped yet.
*/
- do{
- rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, &m_pConnParams,
- NimBLEClient::handleGapEvent, this);
- if(rc == BLE_HS_EBUSY) {
- vTaskDelay(1 / portTICK_PERIOD_MS);
+ do {
+ rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t,
+ m_connectTimeout, &m_pConnParams,
+ NimBLEClient::handleGapEvent, this);
+ switch (rc) {
+ case 0:
+ m_pTaskData = &taskData;
+ break;
+
+ case BLE_HS_EBUSY:
+ // Scan was still running, stop it and try again
+ if (!NimBLEDevice::getScan()->stop()) {
+ return false;
+ }
+ break;
+
+ case BLE_HS_EDONE:
+ // A connection to this device already exists, do not connect twice.
+ NIMBLE_LOGE(LOG_TAG, "Already connected to device; addr=%s",
+ std::string(m_peerAddress).c_str());
+ return false;
+
+ case BLE_HS_EALREADY:
+ // Already attemting to connect to this device, cancel the previous
+ // attempt and report failure here so we don't get 2 connections.
+ NIMBLE_LOGE(LOG_TAG, "Already attempting to connect to %s - cancelling",
+ std::string(m_peerAddress).c_str());
+ ble_gap_conn_cancel();
+ return false;
+
+ default:
+ NIMBLE_LOGE(LOG_TAG, "Failed to connect to %s, rc=%d; %s",
+ std::string(m_peerAddress).c_str(),
+ rc, NimBLEUtils::returnCodeToString(rc));
+ return false;
}
- }while(rc == BLE_HS_EBUSY);
- if (rc != 0 && rc != BLE_HS_EDONE) {
- NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; "
- "addr=%s, rc=%d; %s",
- std::string(m_peerAddress).c_str(),
- rc, NimBLEUtils::returnCodeToString(rc));
+ } while (rc == BLE_HS_EBUSY);
+
+ // Wait for the connect timeout time +1 second for the connection to complete
+ if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) {
m_pTaskData = nullptr;
- m_waitingToConnect = false;
+ // If a connection was made but no response from MTU exchange; disconnect
+ if(isConnected()) {
+ NIMBLE_LOGE(LOG_TAG, "Connect timeout - no response");
+ disconnect();
+ } else {
+ // workaround; if the controller doesn't cancel the connection
+ // at the timeout cancel it here.
+ NIMBLE_LOGE(LOG_TAG, "Connect timeout - cancelling");
+ ble_gap_conn_cancel();
+ }
+
return false;
- }
- m_waitingToConnect = true;
-
- // Wait for the connection to complete.
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
-
- if(taskData.rc != 0){
+ } else if(taskData.rc != 0){
+ NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s",
+ taskData.rc,
+ NimBLEUtils::returnCodeToString(taskData.rc));
+ // If the failure was not a result of a disconnection
+ // make sure we disconnect now to avoid dangling connections
+ if(isConnected()) {
+ ble_gap_terminate(m_conn_id, BLE_ERR_REM_USER_CONN_TERM);
+ }
return false;
+ } else {
+ NIMBLE_LOGI(LOG_TAG, "Connection established");
}
if(deleteAttibutes) {
deleteServices();
}
+ m_connEstablished = true;
m_pClientCallbacks->onConnect(this);
NIMBLE_LOGD(LOG_TAG, "<< connect()");
- return true;
+ // Check if still connected before returning
+ return isConnected();
} // connect
@@ -267,13 +325,31 @@ bool NimBLEClient::secureConnection() {
*/
int NimBLEClient::disconnect(uint8_t reason) {
NIMBLE_LOGD(LOG_TAG, ">> disconnect()");
+
int rc = 0;
- if(m_isConnected){
+ if(isConnected()){
rc = ble_gap_terminate(m_conn_id, reason);
- if(rc != 0){
- NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc,
- NimBLEUtils::returnCodeToString(rc));
+ if (rc == 0) {
+ ble_addr_t peerAddr_t;
+ memcpy(&peerAddr_t.val, m_peerAddress.getNative(),6);
+ peerAddr_t.type = m_peerAddress.getType();
+
+ // Set the disconnect timeout to the supervison timeout time + 1 second
+ // In case the event triggers shortly after the supervision timeout.
+ // We don't want to prematurely reset the host.
+ ble_gap_conn_desc desc;
+ if(ble_gap_conn_find_by_addr(&peerAddr_t, &desc) == 0){
+ ble_npl_time_t ticks;
+ ble_npl_time_ms_to_ticks((desc.supervision_timeout + 100) * 10, &ticks);
+ ble_npl_callout_reset(&m_dcTimer, ticks);
+ NIMBLE_LOGD(LOG_TAG, "DC TIMEOUT = %dms", (desc.supervision_timeout + 100) * 10);
+ }
+ } else if (rc != BLE_HS_EALREADY) {
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
}
+ } else {
+ NIMBLE_LOGD(LOG_TAG, "Not connected to any peers");
}
NIMBLE_LOGD(LOG_TAG, "<< disconnect()");
@@ -454,6 +530,16 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
if(m_servicesVector.size() > prev_size) {
return m_servicesVector.back();
}
+
+ // If the request was successful but 16/32 bit service not found
+ // try again with the 128 bit uuid.
+ if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
+ uuid.bitSize() == BLE_UUID_TYPE_32)
+ {
+ NimBLEUUID uuid128(uuid);
+ uuid128.to128();
+ return getService(uuid128);
+ }
}
NIMBLE_LOGD(LOG_TAG, "<< getService: not found");
@@ -510,7 +596,7 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveServices");
- if(!m_isConnected){
+ if(!isConnected()){
NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting");
return false;
}
@@ -618,10 +704,11 @@ std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUU
* @param [in] serviceUUID The service that owns the characteristic.
* @param [in] characteristicUUID The characteristic whose value we wish to write.
* @param [in] value The value to write to the characteristic.
+ * @param [in] response If true, uses write with response operation.
* @returns true if successful otherwise false
*/
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
- const std::string &value)
+ const std::string &value, bool response)
{
NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s",
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
@@ -632,7 +719,7 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
if(pService != nullptr) {
NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID);
if(pChar != nullptr) {
- ret = pChar->writeValue(value);
+ ret = pChar->writeValue(value, response);
}
}
@@ -641,6 +728,31 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
} // setValue
+/**
+ * @brief Get the remote characteristic with the specified handle.
+ * @param [in] handle The handle of the desired characteristic.
+ * @returns The matching remote characteristic, nullptr otherwise.
+ */
+NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(const uint16_t handle)
+{
+ NimBLERemoteService *pService = nullptr;
+ for(auto it = m_servicesVector.begin(); it != m_servicesVector.end(); ++it) {
+ if ((*it)->getStartHandle() <= handle && handle <= (*it)->getEndHandle()) {
+ pService = *it;
+ break;
+ }
+ }
+
+ if (pService != nullptr) {
+ for (auto it = pService->begin(); it != pService->end(); ++it) {
+ if ((*it)->getHandle() == handle) {
+ return *it;
+ }
+ }
+ }
+
+ return nullptr;
+}
/**
* @brief Get the current mtu of this connection.
@@ -656,7 +768,8 @@ uint16_t NimBLEClient::getMTU() {
* @param [in] event The event structure sent by the NimBLE stack.
* @param [in] arg A pointer to the client instance that registered for this callback.
*/
- /*STATIC*/ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
+ /*STATIC*/
+ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEClient* client = (NimBLEClient*)arg;
int rc;
@@ -665,61 +778,66 @@ uint16_t NimBLEClient::getMTU() {
switch(event->type) {
case BLE_GAP_EVENT_DISCONNECT: {
- if(!client->m_isConnected)
- return 0;
-
- if(client->m_conn_id != event->disconnect.conn.conn_handle)
- return 0;
-
- client->m_isConnected = false;
- client->m_waitingToConnect=false;
- // Remove the device from ignore list so we will scan it again
- NimBLEDevice::removeIgnored(client->m_peerAddress);
-
- NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", event->disconnect.reason,
- NimBLEUtils::returnCodeToString(event->disconnect.reason));
-
+ rc = event->disconnect.reason;
// If Host reset tell the device now before returning to prevent
// any errors caused by calling host functions before resyncing.
- switch(event->disconnect.reason) {
- case BLE_HS_ETIMEOUT_HCI:
- case BLE_HS_EOS:
+ switch(rc) {
case BLE_HS_ECONTROLLER:
+ case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_ENOTSYNCED:
- NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
- NimBLEDevice::onReset(event->disconnect.reason);
+ case BLE_HS_EOS:
+ NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", rc);
+ NimBLEDevice::onReset(rc);
break;
default:
+ // Check that the event is for this client.
+ if(client->m_conn_id != event->disconnect.conn.conn_handle) {
+ return 0;
+ }
break;
}
- //client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
+ client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
+
+ // Stop the disconnect timer since we are now disconnected.
+ ble_npl_callout_stop(&client->m_dcTimer);
+
+ // Remove the device from ignore list so we will scan it again
+ NimBLEDevice::removeIgnored(client->m_peerAddress);
+
+ // If we received a connected event but did not get established (no PDU)
+ // then a disconnect event will be sent but we should not send it to the
+ // app for processing. Instead we will ensure the task is released
+ // and report the error.
+ if(!client->m_connEstablished)
+ break;
+
+ NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+
+ client->m_connEstablished = false;
client->m_pClientCallbacks->onDisconnect(client);
- rc = event->disconnect.reason;
break;
} // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_CONNECT: {
-
- if(!client->m_waitingToConnect)
+ // If we aren't waiting for this connection response
+ // we should drop the connection immediately.
+ if(client->isConnected() || client->m_pTaskData == nullptr) {
+ ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return 0;
+ }
- //if(client->m_conn_id != BLE_HS_CONN_HANDLE_NONE)
- // return 0;
-
- client->m_waitingToConnect=false;
-
- if (event->connect.status == 0) {
- client->m_isConnected = true;
-
- NIMBLE_LOGD(LOG_TAG, "Connection established");
+ rc = event->connect.status;
+ if (rc == 0) {
+ NIMBLE_LOGI(LOG_TAG, "Connected event");
client->m_conn_id = event->connect.conn_handle;
rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL);
if(rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc,
- NimBLEUtils::returnCodeToString(rc));
+ NIMBLE_LOGE(LOG_TAG, "MTU exchange error; rc=%d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
break;
}
@@ -727,14 +845,10 @@ uint16_t NimBLEClient::getMTU() {
// scanning since we are already connected to it
NimBLEDevice::addIgnored(client->m_peerAddress);
} else {
- NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d %s",
- event->connect.status,
- NimBLEUtils::returnCodeToString(event->connect.status));
-
- client->m_isConnected = false;
- rc = event->connect.status;
+ client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
break;
}
+
return 0;
} // BLE_GAP_EVENT_CONNECT
@@ -742,7 +856,14 @@ uint16_t NimBLEClient::getMTU() {
if(client->m_conn_id != event->notify_rx.conn_handle)
return 0;
- NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",event->notify_rx.attr_handle);
+ // If a notification comes before this flag is set we might
+ // access a vector while it is being cleared in connect()
+ if(!client->m_connEstablished) {
+ return 0;
+ }
+
+ NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",
+ event->notify_rx.attr_handle);
for(auto &it: client->m_servicesVector) {
// Dont waste cycles searching services without this handle in its range
@@ -752,8 +873,8 @@ uint16_t NimBLEClient::getMTU() {
auto cVector = &it->m_characteristicVector;
NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d",
- it->getUUID().toString().c_str(),
- event->notify_rx.attr_handle);
+ it->getUUID().toString().c_str(),
+ event->notify_rx.attr_handle);
auto characteristic = cVector->cbegin();
for(; characteristic != cVector->cend(); ++characteristic) {
@@ -762,16 +883,19 @@ uint16_t NimBLEClient::getMTU() {
}
if(characteristic != cVector->cend()) {
- NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", (*characteristic)->toString().c_str());
+ NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s",
+ (*characteristic)->toString().c_str());
+ time_t t = time(nullptr);
portENTER_CRITICAL(&(*characteristic)->m_valMux);
- (*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data, event->notify_rx.om->om_len);
- (*characteristic)->m_timestamp = time(nullptr);
+ (*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data,
+ event->notify_rx.om->om_len);
+ (*characteristic)->m_timestamp = t;
portEXIT_CRITICAL(&(*characteristic)->m_valMux);
if ((*characteristic)->m_notifyCallback != nullptr) {
NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s",
- (*characteristic)->toString().c_str());
+ (*characteristic)->toString().c_str());
(*characteristic)->m_notifyCallback(*characteristic, event->notify_rx.om->om_data,
event->notify_rx.om->om_len,
!event->notify_rx.indication);
@@ -790,10 +914,10 @@ uint16_t NimBLEClient::getMTU() {
}
NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters");
NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d",
- event->conn_update_req.peer_params->itvl_min,
- event->conn_update_req.peer_params->itvl_max,
- event->conn_update_req.peer_params->latency,
- event->conn_update_req.peer_params->supervision_timeout);
+ event->conn_update_req.peer_params->itvl_min,
+ event->conn_update_req.peer_params->itvl_max,
+ event->conn_update_req.peer_params->latency,
+ event->conn_update_req.peer_params->supervision_timeout);
rc = client->m_pClientCallbacks->onConnParamsUpdateRequest(client,
event->conn_update_req.peer_params) ? 0 : BLE_ERR_CONN_PARMS;
@@ -827,7 +951,9 @@ uint16_t NimBLEClient::getMTU() {
return 0;
}
- if(event->enc_change.status == 0 || event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
+ if(event->enc_change.status == 0 ||
+ event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING))
+ {
struct ble_gap_conn_desc desc;
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
assert(rc == 0);
@@ -922,7 +1048,9 @@ uint16_t NimBLEClient::getMTU() {
if(client->m_pTaskData != nullptr) {
client->m_pTaskData->rc = rc;
- xTaskNotifyGive(client->m_pTaskData->task);
+ if(client->m_pTaskData->task) {
+ xTaskNotifyGive(client->m_pTaskData->task);
+ }
client->m_pTaskData = nullptr;
}
@@ -935,7 +1063,7 @@ uint16_t NimBLEClient::getMTU() {
* @return True if we are connected and false if we are not connected.
*/
bool NimBLEClient::isConnected() {
- return m_isConnected;
+ return m_conn_id != BLE_HS_CONN_HANDLE_NONE;
} // isConnected
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.h
index ddeef3cc3..a4b847000 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.h
@@ -30,6 +30,7 @@
#include
class NimBLERemoteService;
+class NimBLERemoteCharacteristic;
class NimBLEClientCallbacks;
class NimBLEAdvertisedDevice;
@@ -54,7 +55,8 @@ public:
size_t deleteService(const NimBLEUUID &uuid);
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
- const std::string &value);
+ const std::string &value, bool response = false);
+ NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle);
bool isConnected();
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
bool deleteCallbacks = true);
@@ -82,16 +84,17 @@ private:
const struct ble_gatt_error *error,
const struct ble_gatt_svc *service,
void *arg);
+ static void dcTimerCb(ble_npl_event *event);
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
NimBLEAddress m_peerAddress;
uint16_t m_conn_id;
- bool m_isConnected;
- bool m_waitingToConnect;
+ bool m_connEstablished;
bool m_deleteCallbacks;
int32_t m_connectTimeout;
NimBLEClientCallbacks* m_pClientCallbacks;
- ble_task_data_t *m_pTaskData;
+ ble_task_data_t* m_pTaskData;
+ ble_npl_callout m_dcTimer;
std::vector m_servicesVector;
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
index fb36e6e57..f34a522f7 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
@@ -25,6 +25,7 @@
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
+#include "host/ble_hs_pvcy.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
@@ -60,6 +61,7 @@ std::list NimBLEDevice::m_cList;
#endif
std::list NimBLEDevice::m_ignoreList;
NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
+uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC;
/**
@@ -144,8 +146,8 @@ void NimBLEDevice::stopAdvertising() {
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
/* STATIC */ NimBLEClient* NimBLEDevice::createClient(NimBLEAddress peerAddress) {
if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) {
- NIMBLE_LOGW("Number of clients exceeds Max connections. Max=(%d)",
- NIMBLE_MAX_CONNECTIONS);
+ NIMBLE_LOGW(LOG_TAG,"Number of clients exceeds Max connections. Cur=%d Max=%d",
+ m_cList.size(), NIMBLE_MAX_CONNECTIONS);
}
NimBLEClient* pClient = new NimBLEClient(peerAddress);
@@ -165,26 +167,31 @@ void NimBLEDevice::stopAdvertising() {
return false;
}
+ // Set the connection established flag to false to stop notifications
+ // from accessing the attribute vectors while they are being deleted.
+ pClient->m_connEstablished = false;
int rc =0;
- if(pClient->m_isConnected) {
+ if(pClient->isConnected()) {
rc = pClient->disconnect();
if (rc != 0 && rc != BLE_HS_EALREADY && rc != BLE_HS_ENOTCONN) {
return false;
}
- while(pClient->m_isConnected) {
- vTaskDelay(10);
+ while(pClient->isConnected()) {
+ taskYIELD();
}
- }
+ // Since we set the flag to false the app will not get a callback
+ // in the disconnect event so we call it here for good measure.
+ pClient->m_pClientCallbacks->onDisconnect(pClient);
- if(pClient->m_waitingToConnect) {
+ } else if(pClient->m_pTaskData != nullptr) {
rc = ble_gap_conn_cancel();
if (rc != 0 && rc != BLE_HS_EALREADY) {
return false;
}
- while(pClient->m_waitingToConnect) {
- vTaskDelay(10);
+ while(pClient->m_pTaskData != nullptr) {
+ taskYIELD();
}
}
@@ -405,30 +412,16 @@ void NimBLEDevice::stopAdvertising() {
m_synced = false;
-#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
- if(m_pScan != nullptr) {
- m_pScan->onHostReset();
- }
-#endif
-
-/* Not needed
- if(m_pServer != nullptr) {
- m_pServer->onHostReset();
- }
-
- for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
- (*it)->onHostReset();
- }
-*/
-
-#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
- if(m_bleAdvertising != nullptr) {
- m_bleAdvertising->onHostReset();
- }
-#endif
-
NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason,
NimBLEUtils::returnCodeToString(reason));
+
+#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
+ if(initialized) {
+ if(m_pScan != nullptr) {
+ m_pScan->onHostReset();
+ }
+ }
+#endif
} // onReset
@@ -448,20 +441,22 @@ void NimBLEDevice::stopAdvertising() {
int rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
+ // Yield for houskeeping before returning to operations.
+ // Occasionally triggers exception without.
+ taskYIELD();
+
m_synced = true;
if(initialized) {
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
if(m_pScan != nullptr) {
- // Restart scanning with the last values sent, allow to clear results.
- m_pScan->start(m_pScan->m_duration, m_pScan->m_scanCompleteCB);
+ m_pScan->onHostSync();
}
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
if(m_bleAdvertising != nullptr) {
- // Restart advertisng, parameters should already be set.
- m_bleAdvertising->start();
+ m_bleAdvertising->onHostSync();
}
#endif
}
@@ -705,6 +700,35 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
} // setSecurityCallbacks
+/**
+ * @brief Set the own address type.
+ * @param [in] own_addr_type Own Bluetooth Device address type.\n
+ * The available bits are defined as:
+ * * 0x00: BLE_OWN_ADDR_PUBLIC
+ * * 0x01: BLE_OWN_ADDR_RANDOM
+ * * 0x02: BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT
+ * * 0x03: BLE_OWN_ADDR_RPA_RANDOM_DEFAULT
+ * @param [in] useNRPA If true, and address type is random, uses a non-resolvable random address.
+ */
+void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
+ m_own_addr_type = own_addr_type;
+ switch (own_addr_type) {
+ case BLE_OWN_ADDR_PUBLIC:
+ ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY);
+ break;
+ case BLE_OWN_ADDR_RANDOM:
+ setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
+ ble_hs_pvcy_rpa_config(useNRPA ? NIMBLE_HOST_ENABLE_NRPA : NIMBLE_HOST_ENABLE_RPA);
+ break;
+ case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT:
+ case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT:
+ setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
+ ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA);
+ break;
+ }
+} // setOwnAddrType
+
+
/**
* @brief Start the connection securing and authorization for this connection.
* @param conn_id The connection id of the peer device.
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
index 252c52afd..2d586bb42 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
@@ -116,6 +116,7 @@ public:
static void setSecurityPasskey(uint32_t pin);
static uint32_t getSecurityPasskey();
static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks);
+ static void setOwnAddrType(uint8_t own_addr_type, bool useNRPA=false);
static int startSecurity(uint16_t conn_id);
static int setMTU(uint16_t mtu);
static uint16_t getMTU();
@@ -182,6 +183,7 @@ private:
static uint32_t m_passkey;
static ble_gap_event_listener m_listener;
static gap_event_handler m_customGapHandler;
+ static uint8_t m_own_addr_type;
};
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp
new file mode 100644
index 000000000..39f07dede
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp
@@ -0,0 +1,233 @@
+/*
+ * NimBLEHIDDevice.cpp
+ *
+ * Created: on Oct 06 2020
+ * Author wakwak-koba
+ *
+ * Originally:
+ *
+ * BLEHIDDevice.cpp
+ *
+ * Created on: Jan 03, 2018
+ * Author: chegewara
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include "nimconfig.h"
+#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+
+#include "NimBLEHIDDevice.h"
+#include "NimBLE2904.h"
+
+NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) {
+ /*
+ * Here we create mandatory services described in bluetooth specification
+ */
+ m_deviceInfoService = server->createService(NimBLEUUID((uint16_t) 0x180a));
+ m_hidService = server->createService(NimBLEUUID((uint16_t) 0x1812), 40);
+ m_batteryService = server->createService(NimBLEUUID((uint16_t) 0x180f));
+
+ /*
+ * Mandatory characteristic for device info service
+ */
+ m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, NIMBLE_PROPERTY::READ);
+
+ /*
+ * Mandatory characteristics for HID service
+ */
+ m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, NIMBLE_PROPERTY::READ);
+ m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, NIMBLE_PROPERTY::READ);
+ m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, NIMBLE_PROPERTY::WRITE_NR);
+ m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ);
+
+ /*
+ * Mandatory battery level characteristic with notification and presence descriptor
+ */
+ m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
+ NimBLE2904* batteryLevelDescriptor = (NimBLE2904*)m_batteryLevelCharacteristic->createDescriptor((uint16_t) 0x2904);
+ batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
+ batteryLevelDescriptor->setNamespace(1);
+ batteryLevelDescriptor->setUnit(0x27ad);
+
+ /*
+ * This value is setup here because its default value in most usage cases, its very rare to use boot mode
+ * and we want to simplify library using as much as possible
+ */
+ const uint8_t pMode[] = { 0x01 };
+ protocolMode()->setValue((uint8_t*) pMode, 1);
+}
+
+NimBLEHIDDevice::~NimBLEHIDDevice() {
+}
+
+/*
+ * @brief
+ */
+void NimBLEHIDDevice::reportMap(uint8_t* map, uint16_t size) {
+ m_reportMapCharacteristic->setValue(map, size);
+}
+
+/*
+ * @brief This function suppose to be called at the end, when we have created all characteristics we need to build HID service
+ */
+void NimBLEHIDDevice::startServices() {
+ m_deviceInfoService->start();
+ m_hidService->start();
+ m_batteryService->start();
+}
+
+/*
+ * @brief Create manufacturer characteristic (this characteristic is optional)
+ */
+NimBLECharacteristic* NimBLEHIDDevice::manufacturer() {
+ m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, NIMBLE_PROPERTY::READ);
+ return m_manufacturerCharacteristic;
+}
+
+/*
+ * @brief Set manufacturer name
+ * @param [in] name manufacturer name
+ */
+void NimBLEHIDDevice::manufacturer(std::string name) {
+ m_manufacturerCharacteristic->setValue(name);
+}
+
+/*
+ * @brief
+ */
+void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) {
+ uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version };
+ m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
+}
+
+/*
+ * @brief
+ */
+void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
+ uint8_t info[] = { 0x11, 0x1, country, flags };
+ m_hidInfoCharacteristic->setValue(info, sizeof(info));
+}
+
+/*
+ * @brief Create input report characteristic that need to be saved as new characteristic object so can be further used
+ * @param [in] reportID input report ID, the same as in report map for input object related to created characteristic
+ * @return pointer to new input report characteristic
+ */
+NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
+ NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
+ NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908);
+
+ uint8_t desc1_val[] = { reportID, 0x01 };
+ inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
+
+ return inputReportCharacteristic;
+}
+
+/*
+ * @brief Create output report characteristic that need to be saved as new characteristic object so can be further used
+ * @param [in] reportID Output report ID, the same as in report map for output object related to created characteristic
+ * @return Pointer to new output report characteristic
+ */
+NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) {
+ NimBLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
+ NimBLEDescriptor* outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t) 0x2908);
+
+ uint8_t desc1_val[] = { reportID, 0x02 };
+ outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
+
+ return outputReportCharacteristic;
+}
+
+/*
+ * @brief Create feature report characteristic that need to be saved as new characteristic object so can be further used
+ * @param [in] reportID Feature report ID, the same as in report map for feature object related to created characteristic
+ * @return Pointer to new feature report characteristic
+ */
+NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) {
+ NimBLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
+ NimBLEDescriptor* featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t) 0x2908);
+
+ uint8_t desc1_val[] = { reportID, 0x03 };
+ featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);
+
+ return featureReportCharacteristic;
+}
+
+/*
+ * @brief
+ */
+NimBLECharacteristic* NimBLEHIDDevice::bootInput() {
+ return m_hidService->createCharacteristic((uint16_t) 0x2a22, NIMBLE_PROPERTY::NOTIFY);
+}
+
+/*
+ * @brief
+ */
+NimBLECharacteristic* NimBLEHIDDevice::bootOutput() {
+ return m_hidService->createCharacteristic((uint16_t) 0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
+}
+
+/*
+ * @brief
+ */
+NimBLECharacteristic* NimBLEHIDDevice::hidControl() {
+ return m_hidControlCharacteristic;
+}
+
+/*
+ * @brief
+ */
+NimBLECharacteristic* NimBLEHIDDevice::protocolMode() {
+ return m_protocolModeCharacteristic;
+}
+
+void NimBLEHIDDevice::setBatteryLevel(uint8_t level) {
+ m_batteryLevelCharacteristic->setValue(&level, 1);
+}
+/*
+ * @brief Returns battery level characteristic
+ * @ return battery level characteristic
+ *//*
+BLECharacteristic* BLEHIDDevice::batteryLevel() {
+ return m_batteryLevelCharacteristic;
+}
+
+
+
+BLECharacteristic* BLEHIDDevice::reportMap() {
+ return m_reportMapCharacteristic;
+}
+
+BLECharacteristic* BLEHIDDevice::pnp() {
+ return m_pnpCharacteristic;
+}
+
+
+BLECharacteristic* BLEHIDDevice::hidInfo() {
+ return m_hidInfoCharacteristic;
+}
+*/
+/*
+ * @brief
+ */
+NimBLEService* NimBLEHIDDevice::deviceInfo() {
+ return m_deviceInfoService;
+}
+
+/*
+ * @brief
+ */
+NimBLEService* NimBLEHIDDevice::hidService() {
+ return m_hidService;
+}
+
+/*
+ * @brief
+ */
+NimBLEService* NimBLEHIDDevice::batteryService() {
+ return m_batteryService;
+}
+
+#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+#endif // #if defined(CONFIG_BT_ENABLED)
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.h
new file mode 100644
index 000000000..3e7a6f759
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.h
@@ -0,0 +1,85 @@
+/*
+ * NimBLEHIDDevice.h
+ *
+ * Created: on Oct 06 2020
+ * Author wakwak-koba
+ *
+ * Originally:
+ *
+ * BLEHIDDevice.h
+ *
+ * Created on: Jan 03, 2018
+ * Author: chegewara
+ */
+
+#ifndef _BLEHIDDEVICE_H_
+#define _BLEHIDDEVICE_H_
+
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include "nimconfig.h"
+#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
+
+#include "NimBLECharacteristic.h"
+#include "NimBLEService.h"
+#include "NimBLEDescriptor.h"
+#include "HIDTypes.h"
+
+#define GENERIC_HID 0x03C0
+#define HID_KEYBOARD 0x03C1
+#define HID_MOUSE 0x03C2
+#define HID_JOYSTICK 0x03C3
+#define HID_GAMEPAD 0x03C4
+#define HID_TABLET 0x03C5
+#define HID_CARD_READER 0x03C6
+#define HID_DIGITAL_PEN 0x03C7
+#define HID_BARCODE 0x03C8
+
+class NimBLEHIDDevice {
+public:
+ NimBLEHIDDevice(NimBLEServer*);
+ virtual ~NimBLEHIDDevice();
+
+ void reportMap(uint8_t* map, uint16_t);
+ void startServices();
+
+ NimBLEService* deviceInfo();
+ NimBLEService* hidService();
+ NimBLEService* batteryService();
+
+ NimBLECharacteristic* manufacturer();
+ void manufacturer(std::string name);
+ //NimBLECharacteristic* pnp();
+ void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
+ //NimBLECharacteristic* hidInfo();
+ void hidInfo(uint8_t country, uint8_t flags);
+ //NimBLECharacteristic* batteryLevel();
+ void setBatteryLevel(uint8_t level);
+
+
+ //NimBLECharacteristic* reportMap();
+ NimBLECharacteristic* hidControl();
+ NimBLECharacteristic* inputReport(uint8_t reportID);
+ NimBLECharacteristic* outputReport(uint8_t reportID);
+ NimBLECharacteristic* featureReport(uint8_t reportID);
+ NimBLECharacteristic* protocolMode();
+ NimBLECharacteristic* bootInput();
+ NimBLECharacteristic* bootOutput();
+
+private:
+ NimBLEService* m_deviceInfoService; //0x180a
+ NimBLEService* m_hidService; //0x1812
+ NimBLEService* m_batteryService = 0; //0x180f
+
+ NimBLECharacteristic* m_manufacturerCharacteristic; //0x2a29
+ NimBLECharacteristic* m_pnpCharacteristic; //0x2a50
+ NimBLECharacteristic* m_hidInfoCharacteristic; //0x2a4a
+ NimBLECharacteristic* m_reportMapCharacteristic; //0x2a4b
+ NimBLECharacteristic* m_hidControlCharacteristic; //0x2a4c
+ NimBLECharacteristic* m_protocolModeCharacteristic; //0x2a4e
+ NimBLECharacteristic* m_batteryLevelCharacteristic; //0x2a19
+};
+#endif // CONFIG_BT_NIMBLE_ROLE_BROADCASTER
+#endif // CONFIG_BT_ENABLED
+#endif /* _BLEHIDDEVICE_H_ */
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp
index 154206c73..8bc5090b1 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp
@@ -38,7 +38,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService,
const struct ble_gatt_chr *chr)
{
-
+ NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteCharacteristic()");
switch (chr->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(chr->uuid.u16.value);
@@ -50,7 +50,6 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_uuid = NimBLEUUID(const_cast(&chr->uuid.u128));
break;
default:
- m_uuid = nullptr;
break;
}
@@ -61,6 +60,8 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_notifyCallback = nullptr;
m_timestamp = 0;
m_valMux = portMUX_INITIALIZER_UNLOCKED;
+
+ NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str());
} // NimBLERemoteCharacteristic
@@ -208,15 +209,21 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
+ uint16_t endHandle = getRemoteService()->getEndHandle(this);
+ if(m_handle >= endHandle) {
+ return false;
+ }
+
int rc = 0;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
desc_filter_t filter = {uuid_filter, &taskData};
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
m_handle,
- getRemoteService()->getEndHandle(),
+ endHandle,
NimBLERemoteCharacteristic::descriptorDiscCB,
&filter);
+
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
@@ -225,12 +232,13 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc != 0) {
+ NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: startHandle:%d endHandle:%d taskData.rc=%d %s", m_handle, endHandle, taskData.rc, NimBLEUtils::returnCodeToString(0x0100+taskData.rc));
return false;
}
return true;
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size());
-} // getDescriptors
+} // retrieveDescriptors
/**
@@ -243,7 +251,7 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
for(auto &it: m_descriptorVector) {
if(it->getUUID() == uuid) {
- NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found");
+ NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str());
return it;
}
}
@@ -253,7 +261,18 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
if(m_descriptorVector.size() > prev_size) {
return m_descriptorVector.back();
}
+
+ // If the request was successful but 16/32 bit descriptor not found
+ // try again with the 128 bit uuid.
+ if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
+ uuid.bitSize() == BLE_UUID_TYPE_32)
+ {
+ NimBLEUUID uuid128(uuid);
+ uuid128.to128();
+ return getDescriptor(uuid128);
+ }
}
+
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found");
return nullptr;
} // getDescriptor
@@ -447,9 +466,10 @@ std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
}
} while(rc != 0 && retryCount--);
+ time_t t = time(nullptr);
portENTER_CRITICAL(&m_valMux);
m_value = value;
- m_timestamp = time(nullptr);
+ m_timestamp = t;
if(timestamp != nullptr) {
*timestamp = m_timestamp;
}
@@ -506,19 +526,19 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
* @param [in] notifyCallback A callback to be invoked for a notification.
* @param [in] response If write response required set this to true.
* If NULL is provided then no callback is performed.
- * @return true if successful.
+ * @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val);
+ m_notifyCallback = notifyCallback;
+
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
if(desc == nullptr) {
- NIMBLE_LOGE(LOG_TAG, "<< setNotify(): Could not get descriptor");
- return false;
+ NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found");
+ return true;
}
- m_notifyCallback = notifyCallback;
-
NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
return desc->writeValue((uint8_t *)&val, 2, response);
@@ -531,7 +551,7 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyC
* @param [in] notifyCallback A callback to be invoked for a notification.
* @param [in] response If true, require a write response from the descriptor write operation.
* If NULL is provided then no callback is performed.
- * @return true if successful.
+ * @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) {
if(notifications) {
@@ -545,7 +565,7 @@ bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback n
/**
* @brief Unsubscribe for notifications or indications.
* @param [in] response bool if true, require a write response from the descriptor write operation.
- * @return true if successful.
+ * @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::unsubscribe(bool response) {
return setNotify(0x00, nullptr, response);
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp
index 9281e7df7..fc0f06b67 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp
@@ -31,6 +31,7 @@ static const char* LOG_TAG = "NimBLERemoteDescriptor";
NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic,
const struct ble_gatt_dsc *dsc)
{
+ NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteDescriptor()");
switch (dsc->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(dsc->uuid.u16.value);
@@ -42,12 +43,13 @@ NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemo
m_uuid = NimBLEUUID(const_cast(&dsc->uuid.u128));
break;
default:
- m_uuid = nullptr;
break;
}
m_handle = dsc->handle;
m_pRemoteCharacteristic = pRemoteCharacteristic;
+
+ NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteDescriptor(): %s", m_uuid.toString().c_str());
}
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp
index 8901175dc..cb20e0b3f 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp
@@ -44,12 +44,11 @@ NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble
m_uuid = NimBLEUUID(const_cast(&service->uuid.u128));
break;
default:
- m_uuid = nullptr;
break;
}
m_startHandle = service->start_handle;
m_endHandle = service->end_handle;
- NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService()");
+ NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService(): %s", m_uuid.toString().c_str());
}
@@ -95,8 +94,11 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* u
* @return A pointer to the characteristic object, or nullptr if not found.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) {
+ NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str());
+
for(auto &it: m_characteristicVector) {
if(it->getUUID() == uuid) {
+ NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str());
return it;
}
}
@@ -106,8 +108,19 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
if(m_characteristicVector.size() > prev_size) {
return m_characteristicVector.back();
}
+
+ // If the request was successful but 16/32 bit characteristic not found
+ // try again with the 128 bit uuid.
+ if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
+ uuid.bitSize() == BLE_UUID_TYPE_32)
+ {
+ NimBLEUUID uuid128(uuid);
+ uuid128.to128();
+ return getCharacteristic(uuid128);
+ }
}
+ NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found");
return nullptr;
} // getCharacteristic
@@ -236,6 +249,23 @@ uint16_t NimBLERemoteService::getEndHandle() {
return m_endHandle;
} // getEndHandle
+/**
+ * @brief Get the end handle of specified NimBLERemoteCharacteristic.
+ */
+
+uint16_t NimBLERemoteService::getEndHandle(NimBLERemoteCharacteristic *pCharacteristic) {
+ uint16_t endHandle = m_endHandle;
+
+ for(auto &it: m_characteristicVector) {
+ uint16_t defHandle = it->getDefHandle() - 1;
+ if(defHandle > pCharacteristic->getDefHandle() && endHandle > defHandle) {
+ endHandle = defHandle;
+ }
+ }
+
+ return endHandle;
+} // getEndHandle
+
/**
* @brief Get the service start handle.
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h
index 751c9effb..4920844e4 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h
@@ -70,6 +70,7 @@ private:
uint16_t getStartHandle();
uint16_t getEndHandle();
+ uint16_t getEndHandle(NimBLERemoteCharacteristic *pCharacteristic);
void releaseSemaphores();
// Properties
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
index dc82de549..122ff3332 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
@@ -30,7 +30,6 @@ static const char* LOG_TAG = "NimBLEScan";
* @brief Scan constuctor.
*/
NimBLEScan::NimBLEScan() {
- m_own_addr_type = 0;
m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL;
m_scan_params.passive = 1; // If set, don’t send scan requests to advertisers (i.e., don’t request additional advertising data).
m_scan_params.itvl = 0; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec)
@@ -38,9 +37,10 @@ NimBLEScan::NimBLEScan() {
m_scan_params.limited = 0; // If set, only discover devices in limited discoverable mode.
m_scan_params.filter_duplicates = 0; // If set, the controller ignores all but the first advertisement from each device.
m_pAdvertisedDeviceCallbacks = nullptr;
- m_stopped = true;
+ m_ignoreResults = false;
m_wantDuplicates = false;
m_pTaskData = nullptr;
+ m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset
}
@@ -63,8 +63,8 @@ NimBLEScan::~NimBLEScan() {
switch(event->type) {
case BLE_GAP_EVENT_DISC: {
- if(pScan->m_stopped) {
- NIMBLE_LOGE(LOG_TAG, "Scan stop called, ignoring results.");
+ if(pScan->m_ignoreResults) {
+ NIMBLE_LOGE(LOG_TAG, "Scan op in progress - ignoring results");
return 0;
}
@@ -129,7 +129,6 @@ NimBLEScan::~NimBLEScan() {
pScan->m_scanCompleteCB(pScan->m_scanResults);
}
- pScan->m_stopped = true;
if(pScan->m_pTaskData != nullptr) {
pScan->m_pTaskData->rc = event->disc_complete.reason;
xTaskNotifyGive(pScan->m_pTaskData->task);
@@ -238,7 +237,7 @@ void NimBLEScan::setWindow(uint16_t windowMSecs) {
* @return true if scanning or scan starting.
*/
bool NimBLEScan::isScanning() {
- return !m_stopped;
+ return ble_gap_disc_active();
}
@@ -252,25 +251,6 @@ bool NimBLEScan::isScanning() {
bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) {
NIMBLE_LOGD(LOG_TAG, ">> start(duration=%d)", duration);
- // If Host is not synced we cannot start scanning.
- if(!NimBLEDevice::m_synced) {
- NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync.");
- return false;
- }
-
- if(ble_gap_conn_active()) {
- NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait.");
- return false;
- }
-
- // If we are already scanning don't start again or we will get stuck on the semaphore.
- if(!m_stopped || ble_gap_disc_active()) { // double check - can cause host reset.
- NIMBLE_LOGE(LOG_TAG, "Scan already in progress");
- return false;
- }
-
- m_stopped = false;
-
// Save the callback to be invoked when the scan completes.
m_scanCompleteCB = scanCompleteCB;
// Save the duration in the case that the host is reset so we can reuse it.
@@ -281,32 +261,51 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
duration = BLE_HS_FOREVER;
}
else{
- duration = duration*1000; // convert duration to milliseconds
+ // convert duration to milliseconds
+ duration = duration * 1000;
}
- // if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals
- // then we should not clear vector or we will connect the same device few times
+ // Set the flag to ignore the results while we are deleting the vector
if(!is_continue) {
- clearResults();
+ m_ignoreResults = true;
}
- int rc = 0;
- do{
- rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params,
- NimBLEScan::handleGapEvent, this);
- if(rc == BLE_HS_EBUSY) {
- vTaskDelay(1 / portTICK_PERIOD_MS);
- }
- } while(rc == BLE_HS_EBUSY);
+ int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type, duration, &m_scan_params,
+ NimBLEScan::handleGapEvent, this);
- if (rc != 0 && rc != BLE_HS_EDONE) {
- NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s",
- rc, NimBLEUtils::returnCodeToString(rc));
- m_stopped = true;
+ switch(rc) {
+ case 0:
+ if(!is_continue) {
+ clearResults();
+ }
+ break;
+
+ case BLE_HS_EALREADY:
+ break;
+
+ case BLE_HS_EBUSY:
+ NIMBLE_LOGE(LOG_TAG, "Unable to scan - connection in progress.");
+ break;
+
+ case BLE_HS_ETIMEOUT_HCI:
+ case BLE_HS_EOS:
+ case BLE_HS_ECONTROLLER:
+ case BLE_HS_ENOTSYNCED:
+ NIMBLE_LOGC(LOG_TAG, "Unable to scan - Host Reset");
+ break;
+
+ default:
+ NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ break;
+ }
+
+ m_ignoreResults = false;
+ NIMBLE_LOGD(LOG_TAG, "<< start()");
+
+ if(rc != 0 && rc != BLE_HS_EALREADY) {
return false;
}
-
- NIMBLE_LOGD(LOG_TAG, "<< start()");
return true;
} // start
@@ -347,8 +346,6 @@ bool NimBLEScan::stop() {
return false;
}
- m_stopped = true;
-
if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) {
m_scanCompleteCB(m_scanResults);
}
@@ -381,13 +378,25 @@ void NimBLEScan::erase(const NimBLEAddress &address) {
/**
- * @brief If the host reset the scan will have stopped so we should set the flag as stopped.
+ * @brief Called when host reset, we set a flag to stop scanning until synced.
*/
void NimBLEScan::onHostReset() {
- m_stopped = true;
+ m_ignoreResults = true;
}
+/**
+ * @brief If the host reset and re-synced this is called.
+ * If the application was scanning indefinitely with a callback, restart it.
+ */
+void NimBLEScan::onHostSync() {
+ m_ignoreResults = false;
+
+ if(m_duration == 0 && m_pAdvertisedDeviceCallbacks != nullptr) {
+ start(m_duration, m_scanCompleteCB);
+ }
+}
+
/**
* @brief Get the results of the scan.
* @return NimBLEScanResults object.
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h
index 822629025..9007a7dd8 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h
@@ -83,12 +83,12 @@ private:
~NimBLEScan();
static int handleGapEvent(ble_gap_event* event, void* arg);
void onHostReset();
+ void onHostSync();
NimBLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr;
void (*m_scanCompleteCB)(NimBLEScanResults scanResults);
ble_gap_disc_params m_scan_params;
- uint8_t m_own_addr_type;
- bool m_stopped;
+ bool m_ignoreResults;
bool m_wantDuplicates;
NimBLEScanResults m_scanResults;
uint32_t m_duration;
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
index 8c75192a9..655511aea 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
@@ -296,6 +296,7 @@ size_t NimBLEServer::getConnectedCount() {
}
server->m_pServerCallbacks->onDisconnect(server);
+ server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn);
if(server->m_advertiseOnDisconnect) {
server->startAdvertising();
@@ -658,6 +659,10 @@ void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
} // onDisconnect
+void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
+ NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
+} // onDisconnect
+
uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
return 123456;
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h
index 1fa24b23c..bedf9cf58 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h
@@ -114,6 +114,15 @@ public:
*/
virtual void onDisconnect(NimBLEServer* pServer);
+ /**
+ * @brief Handle a client disconnection.
+ * This is called when a client discconnects.
+ * @param [in] pServer A pointer to the %BLE server that received the client disconnection.
+ * @param [in] desc A pointer to the connection description structure containig information
+ * about the connection.
+ */
+ virtual void onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
+
/**
* @brief Called when a client requests a passkey for pairing.
* @return The passkey to be sent to the client.
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
index 1b00a3237..21ff27047 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
@@ -264,6 +264,37 @@ std::string NimBLEUUID::toString() const {
*/
bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
if(m_valueSet && rhs.m_valueSet) {
+ NIMBLE_LOGD(LOG_TAG,"Comparing UUIDs; type %u to %u; UUID %s to %s",
+ m_uuid.u.type, rhs.m_uuid.u.type,
+ this->toString().c_str(), rhs.toString().c_str());
+
+ if(m_uuid.u.type != rhs.m_uuid.u.type) {
+ uint8_t uuidBase[16] = {
+ 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ if(m_uuid.u.type == BLE_UUID_TYPE_128){
+ if(rhs.m_uuid.u.type == BLE_UUID_TYPE_16){
+ memcpy(uuidBase+12, &rhs.m_uuid.u16.value, 2);
+ } else if (rhs.m_uuid.u.type == BLE_UUID_TYPE_32){
+ memcpy(uuidBase+12, &rhs.m_uuid.u32.value, 4);
+ }
+ return memcmp(m_uuid.u128.value,uuidBase,16) == 0;
+
+ } else if(rhs.m_uuid.u.type == BLE_UUID_TYPE_128) {
+ if(m_uuid.u.type == BLE_UUID_TYPE_16){
+ memcpy(uuidBase+12, &m_uuid.u16.value, 2);
+ } else if (m_uuid.u.type == BLE_UUID_TYPE_32){
+ memcpy(uuidBase+12, &m_uuid.u32.value, 4);
+ }
+ return memcmp(rhs.m_uuid.u128.value,uuidBase,16) == 0;
+
+ } else {
+ return false;
+ }
+ }
+
return ble_uuid_cmp(&m_uuid.u, &rhs.m_uuid.u) == 0;
}
diff --git a/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c b/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
index 0ba15e9c3..98c133fa5 100644
--- a/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
+++ b/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
@@ -19,6 +19,13 @@
* under the License.
*/
+/*
+ * This file has been modified by Ryan Powell, aka h2zero.
+ * The modifications are for the purpose of improving performance and support
+ * for Esprssif versions used by the ardruino-esp32 core that are less current
+ * than the esp-idf releases.
+ */
+
#include
#include "sysinit/sysinit.h"
#include "nimble/hci_common.h"
@@ -30,8 +37,10 @@
#include "esp_bt.h"
#include "freertos/semphr.h"
#include "esp_compiler.h"
+/* IPC is used to improve performance when calls come from a processor not running the NimBLE stack */
+/* but does not exist for solo */
#ifndef CONFIG_FREERTOS_UNICORE
-#include "esp_ipc.h"
+ #include "esp_ipc.h"
#endif
#define NIMBLE_VHCI_TIMEOUT_MS 2000
@@ -81,31 +90,40 @@ void ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb,
ble_hci_rx_acl_hs_arg = acl_arg;
}
-void ble_hci_trans_hs_cmd_tx_on_core_0(void *arg)
+/* Added; Called from the core NimBLE is running on, not used for unicore */
+#ifndef CONFIG_FREERTOS_UNICORE
+void ble_hci_trans_hs_cmd_tx_on_core(void *arg)
{
- uint8_t *cmd = arg;
- uint16_t len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1;
- esp_vhci_host_send_packet(cmd, len);
+ // Ugly but necessary as the arduino core does not provide enough IPC stack for variables.
+ esp_vhci_host_send_packet((uint8_t*)arg, *((uint8_t*)arg + 3) + 1 + BLE_HCI_CMD_HDR_LEN);
}
+#endif
+/* Modified to use ipc calls in arduino to correct performance issues */
int ble_hci_trans_hs_cmd_tx(uint8_t *cmd)
{
+ uint16_t len;
uint8_t rc = 0;
assert(cmd != NULL);
*cmd = BLE_HCI_UART_H4_CMD;
+ len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1;
if (!esp_vhci_host_check_send_available()) {
ESP_LOGD(TAG, "Controller not ready to receive packets");
}
if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
- if (xPortGetCoreID() != 0) {
+/* esp_ipc_call_blocking does not exist for solo */
#ifndef CONFIG_FREERTOS_UNICORE
- esp_ipc_call_blocking(0, ble_hci_trans_hs_cmd_tx_on_core_0, cmd);
-#endif
+ if (xPortGetCoreID() != CONFIG_BT_NIMBLE_PINNED_TO_CORE) {
+ esp_ipc_call_blocking(CONFIG_BT_NIMBLE_PINNED_TO_CORE,
+ ble_hci_trans_hs_cmd_tx_on_core, cmd);
} else {
- ble_hci_trans_hs_cmd_tx_on_core_0(cmd);
+ esp_vhci_host_send_packet(cmd, len);
}
+#else /* Unicore */
+ esp_vhci_host_send_packet(cmd, len);
+#endif
} else {
rc = BLE_HS_ETIMEOUT_HCI;
}
@@ -124,21 +142,21 @@ int ble_hci_trans_ll_evt_tx(uint8_t *hci_ev)
return rc;
}
-void ble_hci_trans_hs_acl_tx_on_core_0(void *arg)
+/* Added; Called from the core NimBLE is running on, not used for unicore */
+#ifndef CONFIG_FREERTOS_UNICORE
+void ble_hci_trans_hs_acl_tx_on_core(void *arg)
{
- uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 1];
- struct os_mbuf *om = arg;
- uint16_t len = 1 + OS_MBUF_PKTLEN(om);
-
- data[0] = BLE_HCI_UART_H4_ACL;
- os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
-
- esp_vhci_host_send_packet(data, len);
+ // Ugly but necessary as the arduino core does not provide enough IPC stack for variables.
+ esp_vhci_host_send_packet((uint8_t*)arg + 2, *(uint16_t*)arg);
}
+#endif
+/* Modified to use ipc calls in arduino to correct performance issues */
int ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
{
- uint8_t rc = 0;
+ uint16_t len = 0;
+ uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 3], rc = 0;
+ bool tx_using_nimble_core = 0;
/* If this packet is zero length, just free it */
if (OS_MBUF_PKTLEN(om) == 0) {
os_mbuf_free_chain(om);
@@ -149,14 +167,36 @@ int ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
ESP_LOGD(TAG, "Controller not ready to receive packets");
}
- if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
- if (xPortGetCoreID() != 0) {
+ len = 1 + OS_MBUF_PKTLEN(om);
+/* Don't check core ID if unicore */
#ifndef CONFIG_FREERTOS_UNICORE
- esp_ipc_call_blocking(0, ble_hci_trans_hs_acl_tx_on_core_0, om);
+ tx_using_nimble_core = xPortGetCoreID() != CONFIG_BT_NIMBLE_PINNED_TO_CORE;
+ if (tx_using_nimble_core) {
+ data[0] = len;
+ data[1] = (len >> 8);
+ data[2] = BLE_HCI_UART_H4_ACL;
+ os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[3]);
+ } else {
+ data[0] = BLE_HCI_UART_H4_ACL;
+ os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
+ }
+#else /* Unicore */
+ data[0] = BLE_HCI_UART_H4_ACL;
+ os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
#endif
+
+ if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
+/* esp_ipc_call_blocking does not exist for solo */
+#ifndef CONFIG_FREERTOS_UNICORE
+ if (tx_using_nimble_core) {
+ esp_ipc_call_blocking(CONFIG_BT_NIMBLE_PINNED_TO_CORE,
+ ble_hci_trans_hs_acl_tx_on_core, data);
} else {
- ble_hci_trans_hs_acl_tx_on_core_0(om);
+ esp_vhci_host_send_packet(data, len);
}
+#else /* Unicore */
+ esp_vhci_host_send_packet(data, len);
+#endif
} else {
rc = BLE_HS_ETIMEOUT_HCI;
}
@@ -367,6 +407,13 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len)
totlen = BLE_HCI_EVENT_HDR_LEN + data[2];
assert(totlen <= UINT8_MAX + BLE_HCI_EVENT_HDR_LEN);
+ if (totlen > MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) {
+ ESP_LOGE(TAG, "Received HCI data length at host (%d) exceeds maximum configured HCI event buffer size (%d).",
+ totlen, MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE));
+ ble_hs_sched_reset(BLE_HS_ECONTROLLER);
+ return 0;
+ }
+
if (data[1] == BLE_HCI_EVCODE_HW_ERROR) {
assert(0);
}
@@ -476,6 +523,9 @@ esp_err_t esp_nimble_hci_and_controller_init(void)
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
+ /* Added to ensure BLE only mode */
+ bt_cfg.mode = ESP_BT_MODE_BLE;
+ /* Added to set max connections from nimconfig */
bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
@@ -485,6 +535,7 @@ esp_err_t esp_nimble_hci_and_controller_init(void)
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) {
return ret;
}
+
return esp_nimble_hci_init();
}
diff --git a/lib/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h b/lib/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h
index 384ec4a56..bafbeac8f 100644
--- a/lib/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h
+++ b/lib/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h
@@ -483,6 +483,10 @@
#define MYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
#endif
+#ifndef MYNEWT_VAL_BLE_L2CAP_COC_MPS
+#define MYNEWT_VAL_BLE_L2CAP_COC_MPS (MYNEWT_VAL_MSYS_1_BLOCK_SIZE - 8)
+#endif
+
#ifndef MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS
#define MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS (1)
#endif
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c
index 7d80d134d..eccb3e988 100644
--- a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c
+++ b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c
@@ -76,7 +76,7 @@ ble_eddystone_set_adv_data_gen(struct ble_hs_adv_fields *adv_fields,
if (adv_fields->num_uuids16 > BLE_EDDYSTONE_MAX_UUIDS16) {
return BLE_HS_EINVAL;
}
- if (svc_data_len > BLE_EDDYSTONE_MAX_SVC_DATA_LEN) {
+ if (svc_data_len > (BLE_EDDYSTONE_MAX_SVC_DATA_LEN - BLE_EDDYSTONE_SVC_DATA_BASE_SZ)) {
return BLE_HS_EINVAL;
}
if (adv_fields->num_uuids16 > 0 && !adv_fields->uuids16_is_complete) {
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c
index 09ae27047..d77ff6a87 100644
--- a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c
+++ b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c
@@ -1017,7 +1017,7 @@ ble_gap_master_failed(int status)
#endif
default:
- BLE_HS_DBG_ASSERT(0);
+ //BLE_HS_DBG_ASSERT(0);
break;
}
}
@@ -1458,8 +1458,8 @@ ble_gap_rx_periodic_adv_rpt(struct hci_le_subev_periodic_adv_rpt *evt)
{
struct ble_hs_periodic_sync *psync;
struct ble_gap_event event;
- ble_gap_event_fn *cb;
- void *cb_arg;
+ ble_gap_event_fn *cb = NULL;
+ void *cb_arg = NULL;
ble_hs_lock();
psync = ble_hs_periodic_sync_find_by_handle(evt->sync_handle);
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c
index eb65e3288..b45128e23 100644
--- a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c
+++ b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c
@@ -470,29 +470,52 @@ ble_hs_conn_timer(void)
int32_t time_diff;
uint16_t conn_handle;
- conn_handle = BLE_HS_CONN_HANDLE_NONE;
- next_exp_in = BLE_HS_FOREVER;
- now = ble_npl_time_get();
+ for (;;) {
+ conn_handle = BLE_HS_CONN_HANDLE_NONE;
+ next_exp_in = BLE_HS_FOREVER;
+ now = ble_npl_time_get();
- ble_hs_lock();
+ ble_hs_lock();
- /* This loop performs one of two tasks:
- * 1. Determine if any connections need to be terminated due to timeout.
- * If so, break out of the loop and terminate the connection. This
- * function will need to be executed again.
- * 2. Otherwise, determine when the next timeout will occur.
- */
- SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
- if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
+ /* This loop performs one of two tasks:
+ * 1. Determine if any connections need to be terminated due to timeout.
+ * If so, break out of the loop and terminate the connection. This
+ * function will need to be executed again.
+ * 2. Otherwise, determine when the next timeout will occur.
+ */
+ SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
+ if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
#if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
- /* Check each connection's rx fragment timer. If too much time
- * passes after a partial packet is received, the connection is
- * terminated.
- */
- if (conn->bhc_rx_chan != NULL) {
- time_diff = conn->bhc_rx_timeout - now;
+ /* Check each connection's rx fragment timer. If too much time
+ * passes after a partial packet is received, the connection is
+ * terminated.
+ */
+ if (conn->bhc_rx_chan != NULL) {
+ time_diff = conn->bhc_rx_timeout - now;
+ if (time_diff <= 0) {
+ /* ACL reassembly has timed out. Remember the connection
+ * handle so it can be terminated after the mutex is
+ * unlocked.
+ */
+ conn_handle = conn->bhc_handle;
+ break;
+ }
+
+ /* Determine if this connection is the soonest to time out. */
+ if (time_diff < next_exp_in) {
+ next_exp_in = time_diff;
+ }
+ }
+#endif
+
+#if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
+ /* Check each connection's rx queued write timer. If too much
+ * time passes after a prep write is received, the queue is
+ * cleared.
+ */
+ time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
if (time_diff <= 0) {
/* ACL reassembly has timed out. Remember the connection
* handle so it can be terminated after the mutex is
@@ -506,45 +529,22 @@ ble_hs_conn_timer(void)
if (time_diff < next_exp_in) {
next_exp_in = time_diff;
}
- }
#endif
-
-#if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
- /* Check each connection's rx queued write timer. If too much
- * time passes after a prep write is received, the queue is
- * cleared.
- */
- time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
- if (time_diff <= 0) {
- /* ACL reassembly has timed out. Remember the connection
- * handle so it can be terminated after the mutex is
- * unlocked.
- */
- conn_handle = conn->bhc_handle;
- break;
}
-
- /* Determine if this connection is the soonest to time out. */
- if (time_diff < next_exp_in) {
- next_exp_in = time_diff;
- }
-#endif
}
+
+ ble_hs_unlock();
+
+ /* If a connection has timed out, terminate it. We need to repeatedly
+ * call this function again to determine when the next timeout is.
+ */
+ if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
+ ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
+ continue;
+ }
+
+ return next_exp_in;
}
-
- ble_hs_unlock();
-
- /* If a connection has timed out, terminate it. We need to recursively
- * call this function again to determine when the next timeout is. This
- * is a tail-recursive call, so it should be optimized to execute in the
- * same stack frame.
- */
- if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
- ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
- return ble_hs_conn_timer();
- }
-
- return next_exp_in;
}
int
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c
index a6fea44c7..13dae8d2b 100644
--- a/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c
+++ b/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c
@@ -180,7 +180,9 @@ static int
get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
{
union ble_store_value cur = {0};
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
struct ble_hs_dev_records p_dev_rec = {0};
+#endif
esp_err_t err;
int i, count = 0, max_limit = 0;
char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN];
@@ -190,11 +192,15 @@ get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
for (i = 1; i <= max_limit; i++) {
get_nvs_key_string(obj_type, i, key_string);
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
if (obj_type != BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
+#endif
err = get_nvs_db_value(obj_type, key_string, &cur);
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
} else {
err = get_nvs_peer_record(key_string, &p_dev_rec);
}
+#endif
/* Check if the user is searching for empty index to write to */
if (err == ESP_ERR_NVS_NOT_FOUND) {
if (empty) {
@@ -206,10 +212,13 @@ get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
/* If user has provided value, then the purpose is to find
* non-matching entry from NVS */
if (value) {
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
if (obj_type == BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
err = get_nvs_matching_index(&p_dev_rec, value, num_value,
sizeof(struct ble_hs_dev_records));
- } else {
+ } else
+#endif
+ {
if (obj_type != BLE_STORE_OBJ_TYPE_CCCD) {
err = get_nvs_matching_index(&cur.sec, value, num_value,
sizeof(struct ble_store_value_sec));
@@ -376,7 +385,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
{
uint8_t *db_item = (uint8_t *)dst;
union ble_store_value cur = {0};
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
struct ble_hs_dev_records p_dev_rec = {0};
+#endif
esp_err_t err;
int i;
@@ -385,8 +396,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
for (i = 1; i <= get_nvs_max_obj_value(obj_type); i++) {
get_nvs_key_string(obj_type, i, key_string);
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
if (obj_type != BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
-
+#endif
err = get_nvs_db_value(obj_type, key_string, &cur);
if (err == ESP_ERR_NVS_NOT_FOUND) {
continue;
@@ -394,6 +406,7 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
ESP_LOGE(TAG, "NVS read operation failed !!");
return -1;
}
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
} else {
err = get_nvs_peer_record(key_string, &p_dev_rec);
if (err == ESP_ERR_NVS_NOT_FOUND) {
@@ -410,7 +423,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
memcpy(db_item, &p_dev_rec, sizeof(struct ble_hs_dev_records));
db_item += sizeof(struct ble_hs_dev_records);
(*db_num)++;
- } else {
+ } else
+#endif
+ {
if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) {
ESP_LOGD(TAG, "CCCD in RAM is filled up from NVS index = %d", i);
memcpy(db_item, &cur.cccd, sizeof(struct ble_store_value_cccd));
@@ -492,6 +507,11 @@ int ble_store_config_persist_cccds(void)
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_CCCD, 0, NULL, 0);
+ if (nvs_count == -1) {
+ ESP_LOGE(TAG, "NVS operation failed while persisting CCCD");
+ return BLE_HS_ESTORE_FAIL;
+ }
+
if (nvs_count < ble_store_config_num_cccds) {
/* NVS db count less than RAM count, write operation */
@@ -518,6 +538,11 @@ int ble_store_config_persist_peer_secs(void)
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_SEC, 0, NULL, 0);
+ if (nvs_count == -1) {
+ ESP_LOGE(TAG, "NVS operation failed while persisting peer sec");
+ return BLE_HS_ESTORE_FAIL;
+ }
+
if (nvs_count < ble_store_config_num_peer_secs) {
/* NVS db count less than RAM count, write operation */
@@ -544,6 +569,11 @@ int ble_store_config_persist_our_secs(void)
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_OUR_SEC, 0, NULL, 0);
+ if (nvs_count == -1) {
+ ESP_LOGE(TAG, "NVS operation failed while persisting our sec");
+ return BLE_HS_ESTORE_FAIL;
+ }
+
if (nvs_count < ble_store_config_num_our_secs) {
/* NVS db count less than RAM count, write operation */
@@ -573,7 +603,13 @@ int ble_store_persist_peer_records(void)
struct ble_hs_dev_records *peer_dev_rec = ble_rpa_get_peer_dev_records();
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_DEV_REC, 0, NULL, 0);
+ if (nvs_count == -1) {
+ ESP_LOGE(TAG, "NVS operation failed while persisting peer_dev_rec");
+ return BLE_HS_ESTORE_FAIL;
+ }
+
if (nvs_count < ble_store_num_peer_dev_rec) {
+
/* NVS db count less than RAM count, write operation */
ESP_LOGD(TAG, "Persisting peer dev record to NVS...");
peer_rec = peer_dev_rec[ble_store_num_peer_dev_rec - 1];
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimconfig.h b/lib/libesp32/NimBLE-Arduino/src/nimconfig.h
index 2f10fa2fc..d90921fa1 100644
--- a/lib/libesp32/NimBLE-Arduino/src/nimconfig.h
+++ b/lib/libesp32/NimBLE-Arduino/src/nimconfig.h
@@ -15,8 +15,13 @@
* This converts them to "CONFIG_BT_NIMBLE_" format used in the latest IDF.
*/
-/* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig) */
-#if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
+/* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig)
+ *
+ * Note: We do not use #ifdef CONFIG_BT_NIMBLE_ENABLED since we cannot enable NimBLE when using
+ * Arduino as a component and the esp-nimble-compnent, so we check if other config options are defined.
+ * We also need to use a config parameter that must be present and not likely defined in the command line.
+ */
+#if defined(CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN) || defined(CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN)
#if defined(CONFIG_NIMBLE_ENABLED) && !defined(CONFIG_BT_NIMBLE_ENABLED)
#define CONFIG_BT_NIMBLE_ENABLED
@@ -51,22 +56,30 @@
/** @brief Comment out if not using NimBLE Client functions \n
* Reduces flash size by approx. 7kB.
*/
+#ifndef CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
+#endif
/** @brief Comment out if not using NimBLE Scan functions \n
* Reduces flash size by approx. 26kB.
*/
+#ifndef CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
+#endif
/** @brief Comment out if not using NimBLE Server functions \n
* Reduces flash size by approx. 16kB.
*/
-// #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
+#ifndef CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
+#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
+#endif
/** @brief Comment out if not using NimBLE Advertising functions \n
* Reduces flash size by approx. 5kB.
*/
-// #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
+#ifndef CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED
+#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
+#endif
/* Uncomment to see debug log messages from the NimBLE host
* Uses approx. 32kB of flash memory.
@@ -89,29 +102,46 @@
// #define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
/** @brief Sets the core NimBLE host runs on */
+#ifndef CONFIG_BT_NIMBLE_PINNED_TO_CORE
#define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0
+#endif
/** @brief Sets the stack size for the NimBLE host task */
+#ifndef CONFIG_BT_NIMBLE_TASK_STACK_SIZE
#define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096
+#endif
/**
* @brief Sets the memory pool where NimBLE will be loaded
* @details By default NimBLE is loaded in internal ram.\n
* To use external PSRAM you must change this to `#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL 1`
*/
+#ifndef CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL
#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
+#endif
/** @brief Sets the number of simultaneous connections (esp controller max is 9) */
+#ifndef CONFIG_BT_NIMBLE_MAX_CONNECTIONS
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
+#endif
/** @brief Sets the number of devices allowed to store/bond with */
+#ifndef CONFIG_BT_NIMBLE_MAX_BONDS
#define CONFIG_BT_NIMBLE_MAX_BONDS 3
+#endif
/** @brief Sets the maximum number of CCCD subscriptions to store */
+#ifndef CONFIG_BT_NIMBLE_MAX_CCCDS
#define CONFIG_BT_NIMBLE_MAX_CCCDS 8
+#endif
+
+/** @brief Default device name */
+#ifndef CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME
+#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
+#endif
/** @brief Set if CCCD's and bond data should be stored in NVS */
-#define CONFIG_BT_NIMBLE_NVS_PERSIST 0
+#define CONFIG_BT_NIMBLE_NVS_PERSIST 1
/** @brief Allow legacy paring */
#define CONFIG_BT_NIMBLE_SM_LEGACY 1
@@ -119,9 +149,6 @@
/** @brief Allow BLE secure connections */
#define CONFIG_BT_NIMBLE_SM_SC 1
-/** @brief Default device name */
-#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
-
/** @brief Max device name length (bytes) */
#define CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN 31
@@ -154,7 +181,6 @@
*/
#define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
-
/** @brief Random address refresh time in seconds */
#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
From 37beaca1198da70b2dc31aab1d5383e5961773c7 Mon Sep 17 00:00:00 2001
From: Simon Hailes
Date: Sun, 17 Jan 2021 17:08:54 +0000
Subject: [PATCH 015/186] Implements xdrv_52 - BLE_ESP32 Modifies xsns_52 -
iBeacon, and xsns_62 - MI32 to use the new BLE driver
---
tasmota/my_user_config.h | 3 +
tasmota/support_command.ino | 11 +
tasmota/xdrv_01_webserver.ino | 14 +-
tasmota/xdrv_52_BLE_ESP32.ino | 3613 ++++++++++++++++++++++++
tasmota/xsns_52_ibeacon.ino | 17 +-
tasmota/xsns_52_ibeacon_BLE_ESP32.ino | 952 +++++++
tasmota/xsns_62_MI_ESP32.ino | 53 +-
tasmota/xsns_62_MI_ESP32_BLE_ESP32.ino | 2742 ++++++++++++++++++
8 files changed, 7376 insertions(+), 29 deletions(-)
create mode 100644 tasmota/xdrv_52_BLE_ESP32.ino
create mode 100644 tasmota/xsns_52_ibeacon_BLE_ESP32.ino
create mode 100644 tasmota/xsns_62_MI_ESP32_BLE_ESP32.ino
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 8f3fa5a89..8cf0316f8 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -861,6 +861,9 @@
//#define USE_SPI // Add support for hardware SPI
#define USE_MI_ESP32 // Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
+//#define USE_BLE_ESP32 // Add support for ESP32 as a BLE-bridge (+9k2? mem, +292k? flash)
+//#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
+//#define USE_IBEACON_ESP32
//#define USE_WEBCAM // Add support for webcam
#endif // ESP32
diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino
index 1c5e32474..32180783d 100644
--- a/tasmota/support_command.ino
+++ b/tasmota/support_command.ino
@@ -690,6 +690,12 @@ void CmndSleep(void)
}
+#ifdef USE_BLE_ESP32
+ // declare the fn
+ int ExtStopBLE();
+#endif
+
+
void CmndUpgrade(void)
{
// Check if the payload is numerically 1, and had no trailing chars.
@@ -700,6 +706,11 @@ void CmndUpgrade(void)
TasmotaGlobal.ota_state_flag = 3;
char stemp1[TOPSZ];
Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), XdrvMailbox.command, TasmotaGlobal.version, GetOtaUrl(stemp1, sizeof(stemp1)));
+
+#ifdef USE_BLE_ESP32
+ ExtStopBLE();
+#endif
+
} else {
Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), XdrvMailbox.command, TasmotaGlobal.version);
}
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index d68cf9cd0..f148a5a6e 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -2374,6 +2374,11 @@ void UploadServices(uint32_t start_service) {
}
}
+#ifdef USE_BLE_ESP32
+ // declare the fn
+ int ExtStopBLE();
+#endif
+
void HandleUploadLoop(void) {
// Based on ESP8266HTTPUpdateServer.cpp uses ESP8266WebServer Parsing.cpp and Cores Updater.cpp (Update)
static uint32_t upload_size;
@@ -2409,6 +2414,11 @@ void HandleUploadLoop(void) {
}
SettingsSave(1); // Free flash for upload
+#ifdef USE_BLE_ESP32
+ ExtStopBLE();
+#endif
+
+
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_FILE " %s"), upload.filename.c_str());
if (UPL_SETTINGS == Web.upload_file_type) {
@@ -2632,7 +2642,9 @@ void HandleUploadLoop(void) {
Web.upload_error = 7; // Upload aborted
if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); }
}
- delay(0);
+ // do actually wait a little to allow ESP32 tasks to tick
+ // fixes task timeout in ESP32Solo1 style unicore code.
+ delay(10);
OsWatchLoop();
// Scheduler(); // Feed OsWatch timer to prevent restart on long uploads
}
diff --git a/tasmota/xdrv_52_BLE_ESP32.ino b/tasmota/xdrv_52_BLE_ESP32.ino
new file mode 100644
index 000000000..90739757d
--- /dev/null
+++ b/tasmota/xdrv_52_BLE_ESP32.ino
@@ -0,0 +1,3613 @@
+/*
+ xdrv_52_BLE_ESP32.ino - BLE via ESP32 support for Tasmota
+
+ Copyright (C) 2020 Christian Baars and Theo Arends and Simon Hailes
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+
+ --------------------------------------------------------------------------------------------
+ Version yyyymmdd Action Description
+ --------------------------------------------------------------------------------------------
+*/
+
+/*
+ xdrv_52:
+ This driver uses the ESP32 BLE functionality to hopefully provide enough
+ BLE functionality to implement specific drivers on top of it.
+
+ As a generic driver, it can:
+ Be asked to
+ connect/write to a MAC/Service/Characteristic
+ connect/read from a MAC/Service/Characteristic
+ connect/write/awaitnotify from a MAC/Service/Characteristic/NotifyCharacteristic
+ connect/read/awaitnotify from a MAC/Service/Characteristic/NotifyCharacteristic
+
+ Cmnds:
+ BLEOp0 - requests status of operations
+ BLEOp1 MAC - create an operation in preparation, and populate it's MAC address
+ BLEOp2 Service - add a serviceUUID to the operation in preparation
+ BLEOp3 Characteristic - add a CharacteristicUUID to the operation in preparation for read/write
+ BLEOp4 writedata - optional:add data to write to the operation in preparation - hex string
+ BLEOp5 - optional:signify that a read should be done
+ BLEOp6 NotifyCharacteristic - optional:add a NotifyCharacteristicUUID to the operation in preparation to wait for a notify
+ BLEOp9 - publish the 'operation in preparation' to MQTT.
+ BLEOp10 - add the 'operation in preparation' to the queue of operations to perform.
+
+ Other drivers can add callbacks to receive advertisements
+ Other drivers can add 'operations' to be performed and receive callbacks from the operation's success or failure
+
+Example:
+Write and request next notify:
+backlog BLEOp1 001A22092EE0; BLEOp2 3e135142-654f-9090-134a-a6ff5bb77046; BLEOp3 3fa4585a-ce4a-3bad-db4b-b8df8179ea09; BLEOp4 03; BLEOp6 d0e8434d-cd29-0996-af41-6c90f4e0eb2a;
+BLEOp10 ->
+19:25:08 RSL: tele/tasmota_E89E98/SENSOR = {"BLEOperation":{"opid":"3,"state":"1,"MAC":"001A22092EE0","svc":"3e135142-654f-9090-134a-a6ff5bb77046","char":"3fa4585a-ce4a-3bad-db4b-b8df8179ea09","wrote":"03}}
+19:25:08 queued 0 sent {"BLEOperation":{"opid":"3,"state":"1,"MAC":"001A22092EE0","svc":"3e135142-654f-9090-134a-a6ff5bb77046","char":"3fa4585a-ce4a-3bad-db4b-b8df8179ea09","wrote":"03}}
+19:25:08 RSL: stat/tasmota_E89E98/RESULT = {"BLEOp":"Done"}
+.....
+19:25:11 RSL: tele/tasmota_E89E98/SENSOR = {"BLEOperation":{"opid":"3,"state":"11,"MAC":"001A22092EE0","svc":"3e135142-654f-9090-134a-a6ff5bb77046","char":"3fa4585a-ce4a-3bad-db4b-b8df8179ea09","wrote":"03","notify":"020109000428}}
+
+state: 1 -> starting,
+7 -> read complete
+8 -> write complete
+11 -> notify complete
+-ve + -> failure (see GEN_STATE_FAILED_XXXX constants below.)
+
+
+The driver can also be used by other drivers, using the functions:
+
+void registerForAdvertismentCallbacks(char *loggingtag, ADVERTISMENT_CALLBACK* pFn);
+void registerForOpCallbacks(char *loggingtag, OPCOMPLETE_CALLBACK* pFn);
+bool extQueueOperation(generic_sensor_t** op);
+
+These allow other code to
+ receive advertisements
+ receive operation callbacks.
+ create and start an operation, and get a callback when done/failed.
+
+i.e. the Bluetooth of the ESP can be shared without conflict.
+
+*/
+
+
+// TEMPORARILY define ESP32 and USE_BLE_ESP32 so VSCODE shows highlighting....
+//#define VSCODE_DEV
+
+#ifdef VSCODE_DEV
+#define ESP32
+#define USE_BLE_ESP32
+#endif
+
+#ifdef ESP32 // ESP32 only. Use define USE_HM10 for ESP8266 support
+
+#ifdef USE_BLE_ESP32
+
+#define BLE_ESP32_ALIASES
+
+// uncomment for more diagnostic/information messages - + more flash use.
+//#define BLE_ESP32_DEBUG
+
+
+
+#define XDRV_52 52
+#define USE_MI_DECRYPTION
+
+#include
+#include
+#include
+#include
+#ifdef USE_MI_DECRYPTION
+#include
+#endif //USE_MI_DECRYPTION
+
+#include
+#include
+#include "NimBLEEddystoneURL.h"
+#include "NimBLEEddystoneTLM.h"
+#include "NimBLEBeacon.h"
+
+// from ble_gap.c
+extern "C" void ble_gap_conn_broken(uint16_t conn_handle, int reason);
+
+void installExamples();
+void sendExample();
+
+
+namespace BLE_ESP32 {
+
+
+// generic sensor type used as during
+// connect/read/wrtie/notify operations
+// only one operation will happen at a time
+
+#pragma pack( push, 0 ) // aligned structures for speed. but be sepcific
+
+/////////////////////////////////////////////////////
+// states for runTaskDoneOperation
+#define GEN_STATE_IDLE 0
+#define GEN_STATE_START 1
+#define GEN_STATE_STARTED 2
+
+#define GEN_STATE_READDONE 3
+#define GEN_STATE_WRITEDONE 4
+#define GEN_STATE_WAITNOTIFY 5
+#define GEN_STATE_WAITINDICATE 6
+
+#define GEN_STATE_NOTIFIED 7
+
+
+// Errors are all base on 0x100
+#define GEN_STATE_FAILED -1
+#define GEN_STATE_FAILED_CANTNOTIFYORINDICATE -2
+#define GEN_STATE_FAILED_CANTREAD -3
+#define GEN_STATE_FAILED_CANTWRITE -4
+#define GEN_STATE_FAILED_NOSERVICE -5
+#define GEN_STATE_FAILED_NO_RW_CHAR -6
+#define GEN_STATE_FAILED_NONOTIFYCHAR -7
+#define GEN_STATE_FAILED_NOTIFYTIMEOUT -8
+#define GEN_STATE_FAILED_READ -9
+#define GEN_STATE_FAILED_WRITE -10
+#define GEN_STATE_FAILED_CONNECT -11
+#define GEN_STATE_FAILED_NOTIFY -12
+#define GEN_STATE_FAILED_INDICATE -13
+#define GEN_STATE_FAILED_NODEVICE -14
+#define GEN_STATE_FAILED_NOREADWRITE -15
+#define GEN_STATE_FAILED_CANCEL -16
+//
+/////////////////////////////////////////////////////
+
+#define BLE_ESP32_MAXNAMELEN 32
+#define BLE_ESP32_MAXALIASLEN 20
+
+
+#define MAX_BLE_DATA_LEN 100
+struct generic_sensor_t {
+ int16_t state;
+ uint32_t opid; // incrementing id so we can find them
+ uint64_t notifytimer;
+
+ // uint8_t cancel;
+ // uint8_t requestType;
+ NimBLEAddress addr;
+ NimBLEUUID serviceUUID;
+ NimBLEUUID characteristicUUID;
+ NimBLEUUID notificationCharacteristicUUID;
+ uint8_t dataToWrite[MAX_BLE_DATA_LEN];
+ uint8_t writelen;
+ uint8_t dataRead[MAX_BLE_DATA_LEN];
+ uint8_t readlen;
+ uint8_t readtruncated;
+ uint8_t dataNotify[MAX_BLE_DATA_LEN];
+ uint8_t notifylen;
+ uint8_t notifytruncated;
+
+ // NOTE!!!: this callback is called DIRECTLY from the operation task, so be careful about cross-thread access of data
+ // if is called after read, so that you can do a read/modify/write operation on a characteristic.
+ // i.e. modify dataToWrite and writelen according to what you see in readData and readlen.
+ // for a normal read, please use the OPCOMPLETE_CALLBACK 'completecallback'
+ // normally null
+ void *readmodifywritecallback; // READ_CALLBACK function, used by external drivers
+
+ void *completecallback; // OPCOMPLETE_CALLBACK function, used by external drivers
+ void *context; // opaque context, used by external drivers, or can be set to a long for MQTT
+};
+
+
+////////////////////////////////////////////////////////////////
+// structure for callbacks from other drivers from advertisements.
+struct ble_advertisment_t {
+ BLEAdvertisedDevice *advertisedDevice; // the full NimBLE advertisment, in case people need MORE info.
+ uint32_t totalCount;
+
+ uint8_t addr[6];
+ uint8_t addrtype;
+ int8_t RSSI;
+ char name[BLE_ESP32_MAXNAMELEN+1];
+};
+
+struct ble_alias_t {
+ uint8_t addr[6];
+ char name[BLE_ESP32_MAXALIASLEN+1];
+};
+
+/*
+This is probabyl what you are looking for:
+ble_gap_addr_t gap_addr;
+gap_addr.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC; //Public address 0x00
+gap_addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC; //Random static address 0x01
+gap_addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE; //Random private resolvable address 0x02
+gap_addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE; //Random private non-resolvable address 0x03
+*/
+
+#pragma pack( pop ) // byte-aligned structures to read the sensor data
+
+////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////
+// External interface to this driver for use by others.
+//
+// callback types to be used by external drivers
+//
+// returns -
+// 0 = let others see this,
+// 1 = I processed this, no need to give it to the next callback
+// 2 = I want this device erased from the scan
+typedef int ADVERTISMENT_CALLBACK(BLE_ESP32::ble_advertisment_t *pStruct);
+// returns - 0 = let others see this, 1 = I processed this, no need to give it to the next callback, or post on MQTT
+typedef int OPCOMPLETE_CALLBACK(BLE_ESP32::generic_sensor_t *pStruct);
+
+// NOTE!!!: this callback is called DIRECTLY from the operation task, so be careful about cross-thread access of data
+// if is called after read, so that you can do a read/modify/write operation on a characteristic.
+typedef int READ_CALLBACK(BLE_ESP32::generic_sensor_t *pStruct);
+
+typedef int SCANCOMPLETE_CALLBACK(NimBLEScanResults results);
+
+// tag is just a name for logging
+void registerForAdvertismentCallbacks(const char *tag, BLE_ESP32::ADVERTISMENT_CALLBACK* pFn);
+void registerForOpCallbacks(const char *tag, BLE_ESP32::OPCOMPLETE_CALLBACK* pFn);
+void registerForScanCallbacks(const char *tag, BLE_ESP32::SCANCOMPLETE_CALLBACK* pFn);
+
+////////////////////////////////////////////////////
+// BLE operations: these are currently 'new'ed and 'delete'ed.
+// in the future, they may be allocated from some constant menory store to avoid fragmentation.
+// so PLEASE don't create or destroy them yourselves.
+// create a new BLE operation.
+int newOperation(BLE_ESP32::generic_sensor_t** op);
+// free a BLE operation - this should be done if you did not call extQueueOperation for some reason
+int freeOperation(BLE_ESP32::generic_sensor_t** op);
+// queue a BLE operation - it will happen some time in the future.
+// Note: you do not need to free an operation once it have been queued. it will be freed by the driver.
+int extQueueOperation(BLE_ESP32::generic_sensor_t** op);
+const char * getStateString(int state);
+///////////////////////////////////////////////////////////////////////
+
+#define USE_NATIVE_LOGGING
+
+
+// a temporay safe logging mechanism. This has a max of 40 chars, and a max of 15 slots per 50ms
+//int SafeAddLog_P(uint32_t loglevel, PGM_P formatP, ...);
+
+static void BLEDiag();
+const char *getAlias(uint8_t *addr);
+//void BLEAliasMqttList();
+void BLEAliasListResp();
+////////////////////////////////////////////////////////////////////////
+// utilities
+// dump a binary to hex
+char * dump(char *dest, int maxchars, const uint8_t *src, int len);
+
+
+
+
+struct BLE_simple_device_t {
+ uint8_t mac[6];
+ uint8_t addrtype;
+ char name[BLE_ESP32_MAXNAMELEN+1];
+ int8_t RSSI;
+ uint64_t lastseen; // last seen us
+ uint16_t maxAge; // maximum observed age of this device
+};
+
+
+
+// this protects our queues, which can be accessed by multiple tasks
+SemaphoreHandle_t BLEOperationsRecursiveMutex;
+SemaphoreHandle_t BLEDevicesMutex;
+
+
+// only run from main thread, because it deletes things that were newed there...
+static void mainThreadOpCallbacks();
+static void mainThreadBLETimeouts();
+
+int addOperation(std::deque *ops, BLE_ESP32::generic_sensor_t** op);
+BLE_ESP32::generic_sensor_t* nextOperation(std::deque *ops);
+std::string BLETriggerResponse(BLE_ESP32::generic_sensor_t *toSend);
+static void BLEscanEndedCB(NimBLEScanResults results);
+static void BLEGenNotifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify);
+
+// this is called from the advert callback, be careful
+void BLEPostAdvert(ble_advertisment_t *Advertisment);
+static void BLEPostMQTTSeenDevices(int type);
+
+static void BLEShow(bool json);
+static void BLEPostMQTT(bool json);
+static void BLEStartOperationTask();
+
+// these are only run from the run task
+static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOperation, NimBLEClient **ppClient);
+static void BLETaskRunTaskDoneOperation(BLE_ESP32::generic_sensor_t** op, NimBLEClient **ppClient);
+int BLETaskStartScan(int time);
+
+
+// these are run from main thread
+static int StartBLE(void);
+static int StopBLE(void);
+
+// called from advert callback
+void setDetails(ble_advertisment_t *ad);
+
+#undef EXAMPLE_ADVERTISMENT_CALLBACK
+#undef EXAMPLE_OPERATION_CALLBACK
+
+#ifdef EXAMPLE_ADVERTISMENT_CALLBACK
+int myAdvertCallback(BLE_ESP32::ble_advertisment_t *pStruct);
+#endif
+#ifdef EXAMPLE_OPERATION_CALLBACK
+int myOpCallback(BLE_ESP32::generic_sensor_t *pStruct);
+int myOpCallback2(BLE_ESP32::generic_sensor_t *pStruct);
+#endif
+
+
+// single storage for advert callbacks....
+static ble_advertisment_t BLEAdvertisment;
+
+
+//////////////////////////////////////////////////
+// general variables for running the driver
+TaskHandle_t TasmotaMainTask;
+
+
+static int BLEMasterEnable = 0;
+static int BLEInitState = 0;
+static int BLERunningScan = 0;
+static uint32_t BLEScanCount = 0;
+static uint8_t BLEScanActiveMode = 0;
+static uint32_t BLELoopCount = 0;
+static uint32_t BLEOpCount = 0;
+
+static int BLEPublishDevices = 0; // causes MQTT publish of device list (each scan end)
+static BLEScan* ble32Scan = nullptr;
+bool BLERunning = false;
+// time we last started a scan in uS using esp_timer_get_time();
+// used to setect a scan which did not call back?
+uint64_t BLEScanStartedAt = 0;
+uint64_t BLEScanToEndBefore = 0;
+uint8_t BLEStopScan = 0;
+uint8_t BLEOtaStallBLE = 0;
+uint8_t BLEDebugMode = 0;
+int BLEMaxTaskLoopTime = 120; // we expect the task to NOT take > 120s per loop!!!
+uint64_t BLELastLoopTime = 0;
+int BLEScanTimeS = 20; // scan duraiton in S
+int BLEMaxTimeBetweenAdverts = 120; // we expect an advert at least this frequently, else restart BLE (in S)
+uint64_t BLEScanLastAdvertismentAt = 0;
+uint32_t lastopid = 0; // incrementing uinique opid
+uint32_t BLEResets = 0;
+// controls request of details about one device
+uint8_t BLEDetailsRequest = 0;
+uint8_t BLEDetailsMac[6];
+uint8_t BLEAliasListTrigger = 0;
+// triggers send for ALL operations known about
+uint8_t BLEPostMQTTTrigger = 0;
+int BLEMaxAge = 60*10; // 10 minutes
+int BLEAddressFilter = 3;
+
+
+//////////////////////////////////////////////////
+
+
+// operation being prepared through commands
+BLE_ESP32::generic_sensor_t* prepOperation = nullptr;
+
+// operations which have been queued
+std::deque queuedOperations;
+// operations in progress (at the moment, only one)
+std::deque currentOperations;
+// operations which have completed or failed, ready to send to MQTT
+std::deque completedOperations;
+
+// seen devices
+#define MAX_BLE_DEVICES_LOGGED 80
+std::deque seenDevices;
+std::deque freeDevices;
+
+
+
+// list of registered callbacks for advertisements
+// register using void registerForAdvertismentCallbacks(const char *somename ADVERTISMENT_CALLBACK* pFN);
+std::deque advertismentCallbacks;
+
+std::deque operationsCallbacks;
+
+std::deque scancompleteCallbacks;
+
+
+#ifdef BLE_ESP32_ALIASES
+std::deque aliases;
+#endif
+
+
+/*********************************************************************************************\
+ * constants
+\*********************************************************************************************/
+
+#define D_CMND_BLE "BLE"
+
+const char kBLE_Commands[] PROGMEM = D_CMND_BLE "|"
+ "Period|Adv|Op|Mode|Details|Scan|Alias|Name|Debug|Devices|MaxAge|AddrFilter";
+
+static void CmndBLEPeriod(void);
+static void CmndBLEAdv(void);
+static void CmndBLEOperation(void);
+static void CmndBLEMode(void);
+static void CmndBLEDetails(void);
+static void CmndBLEScan(void);
+static void CmndBLEAlias(void);
+static void CmndBLEName(void);
+static void CmndBLEDebug(void);
+static void CmndBLEDevices(void);
+static void CmndBLEMaxAge(void);
+static void CmndBLEAddrFilter(void);
+
+void (*const BLE_Commands[])(void) PROGMEM = {
+ &BLE_ESP32::CmndBLEPeriod,
+ &BLE_ESP32::CmndBLEAdv,
+ &BLE_ESP32::CmndBLEOperation,
+ &BLE_ESP32::CmndBLEMode,
+ &BLE_ESP32::CmndBLEDetails,
+ &BLE_ESP32::CmndBLEScan,
+ &BLE_ESP32::CmndBLEAlias,
+ &BLE_ESP32::CmndBLEName,
+ &BLE_ESP32::CmndBLEDebug,
+ &BLE_ESP32::CmndBLEDevices,
+ &BLE_ESP32::CmndBLEMaxAge,
+ &BLE_ESP32::CmndBLEAddrFilter
+};
+
+const char *successStates[] PROGMEM = {
+ PSTR("IDLE"), // 0
+ PSTR("START"),
+ PSTR("STARTED"),
+ PSTR("DONEREAD"),
+ PSTR("DONEWRITE"),
+ PSTR("WAITNOTIFY"),
+ PSTR("WAITINDICATE"),
+ PSTR("DONENOTIFIED") // 7
+};
+
+const char *failStates[] PROGMEM = {
+ PSTR("IDLE"), //0
+ PSTR("FAILED"), //-1
+ PSTR("FAILCANTNOTIFYORINDICATE"),
+ PSTR("FAILCANTREAD"),
+ PSTR("FAILCANTWRITE"),
+ PSTR("FAILNOSERVICE"),
+ PSTR("FAILNORWCHAR"), //-6
+ PSTR("FAILNONOTIFYCHAR"),
+ PSTR("FAILNOTIFYTIMEOUT"),
+ PSTR("FAILEREAD"),
+ PSTR("FAILWRITE"),
+ PSTR("FAILCONNECT"),
+ PSTR("FAILNOTIFY"),
+ PSTR("FAILINDICATE"),
+ PSTR("FAILNODEVICE"),
+ PSTR("FAILNOREADWRITE"),
+ PSTR("FAILCANCEL")// -16
+};
+
+const char * getStateString(int state){
+ if ((state >= 0) && (state < sizeof(successStates)/sizeof(*successStates))){
+ return successStates[state];
+ }
+
+ state = -state;
+ if ((state >= 0) && (state < sizeof(failStates)/sizeof(*failStates))){
+ return failStates[state];
+ }
+
+ return PSTR("STATEINVALID");
+}
+
+/*********************************************************************************************\
+ * enumerations
+\*********************************************************************************************/
+
+enum BLE_Commands { // commands useable in console or rules
+ CMND_BLE_PERIOD, // set period like TELE-period in seconds between read-cycles
+ CMND_BLE_ADV, // change advertisment options at runtime
+ CMND_BLE_OP, // connect/read/write/notify operations
+ CMND_BLE_MODE, // change mode of ble - BLE_MODES
+ CMND_BLE_DETAILS, // get details for one device's adverts
+ CMND_BLE_SCAN // Scan control
+ };
+
+enum {
+ BLEModeDisabled = 0, // BLE is disabled
+ BLEModeScanByCommand = 1, // BLE is activeated by commands only
+ BLEModeRegularScan = 2, // BLE is scanning all the time
+} BLE_SCAN_MODES;
+
+// values of BLEAdvertMode
+enum {
+ BLE_NO_ADV_SEND = 0, // driver is silent on MQTT regarding adverts
+ BLE_ADV_TELE = 1, // driver sends a summary at tele period
+ //BLE_ADV_ALL = 2, // driver sends every advert with full data to MQTT
+} BLEADVERTMODE;
+
+
+uint8_t BLEMode = BLEModeRegularScan;
+//uint8_t BLEMode = BLEModeScanByCommand;
+uint8_t BLETriggerScan = 0;
+uint8_t BLEAdvertMode = BLE_ADV_TELE;
+uint8_t BLEdeviceLimitReached = 0;
+
+uint8_t BLEStop = 0;
+uint64_t BLEStopAt = 0;
+
+uint8_t BLERestartTasmota = 0;
+uint8_t BLERestartNimBLE = 0;
+const char *BLE_RESTART_TEAMOTA_REASON_UNKNOWN = PSTR("unknown");
+const char *BLE_RESTART_TEAMOTA_REASON_RESTARTING_BLE_TIMEOUT = PSTR("restarting BLE took > 5s");
+const char *BLE_RESTART_TEAMOTA_REASON_BLE_LOOP_STALLED = PSTR("BLE loop stalled > 120s");
+const char *BLE_RESTART_TEAMOTA_REASON_BLE_DISCONNECT_FAIL = PSTR("BLE disconnect taking > 60s");
+const char *BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_UNKNOWN;
+
+const char *BLE_RESTART_BLE_REASON_UNKNOWN = PSTR("unknown");
+const char *BLE_RESTART_BLE_REASON_ADVERT_BLE_TIMEOUT = PSTR("no adverts in 120s");
+const char *BLE_RESTART_BLE_REASON_CONN_LIMIT = PSTR("connect failed with connection limit reached");
+const char *BLE_RESTART_BLE_REASON_CONN_EXISTS = PSTR("connect failed with connection exists");
+const char *BLERestartBLEReason = nullptr;
+
+
+/*********************************************************************************************\
+ * log of all devices present
+\*********************************************************************************************/
+
+void initSeenDevices(){
+ /* added dynamically below, but never removed.
+ for (int i = 0; i < MAX_BLE_DEVICES_LOGGED; i++){
+ BLE_ESP32::BLE_simple_device_t* dev = new BLE_ESP32::BLE_simple_device_t;
+ freeDevices.push_back(dev);
+ }
+ */
+ return;
+}
+
+int addSeenDevice(const uint8_t *mac, uint8_t addrtype, const char *name, int8_t RSSI){
+ int res = 0;
+ uint64_t now = esp_timer_get_time();
+ TasAutoMutex localmutex(&BLEDevicesMutex, "BLEAdd");
+
+ int devicefound = 0;
+ // do we already know this device?
+ for (int i = 0; i < seenDevices.size(); i++){
+ if (!memcmp(seenDevices[i]->mac, mac, 6)){
+ seenDevices[i]->lastseen = now;
+ seenDevices[i]->addrtype = addrtype;
+ seenDevices[i]->RSSI = RSSI;
+ if ((!seenDevices[i]->name[0]) && name[0]){
+ strncpy(seenDevices[i]->name, name, sizeof(seenDevices[i]->name));
+ seenDevices[i]->name[sizeof(seenDevices[i]->name)-1] = 0;
+ }
+ devicefound = 1;
+ break;
+ }
+ }
+ if (!devicefound){
+ // if no free slots, add one if we have not reached our limit
+ if (!freeDevices.size()){
+ int total = seenDevices.size();
+ if (total < MAX_BLE_DEVICES_LOGGED){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("new seendev slot %d"), total);
+#endif
+ BLE_ESP32::BLE_simple_device_t* dev = new BLE_ESP32::BLE_simple_device_t;
+ freeDevices.push_back(dev);
+ } else {
+ // flag we hit the limit
+ BLEdeviceLimitReached ++;
+ if (BLEdeviceLimitReached >= 254){
+ BLEdeviceLimitReached = 254;
+ }
+ }
+ }
+
+ // get a new device from the free list
+ if (freeDevices.size()){
+ BLE_ESP32::BLE_simple_device_t* dev = freeDevices[0];
+ freeDevices.erase(freeDevices.begin());
+ memcpy(dev->mac, mac, 6);
+ strncpy(dev->name, name, sizeof(dev->name));
+ dev->name[sizeof(dev->name)-1] = 0;
+ dev->lastseen = now;
+ dev->addrtype = addrtype;
+ dev->RSSI = RSSI;
+ dev->maxAge = 1;
+ seenDevices.push_back(dev);
+ res = 2; // added
+ }
+ } else {
+ res = 1; // already there
+ }
+ return res;
+}
+
+// remove devices from the seen list by age, and add them to the free list
+// set ageS to 0 to delete all...
+int deleteSeenDevices(int ageS = 0){
+ int res = 0;
+ uint64_t now = esp_timer_get_time();
+ now = now/1000L;
+ now = now/1000L;
+ uint32_t nowS = (uint32_t)now;
+ uint32_t mintime = nowS - ageS;
+
+ {
+ TasAutoMutex localmutex(&BLEDevicesMutex, "BLEDel");
+
+ for (int i = seenDevices.size()-1; i >= 0; i--){
+ BLE_ESP32::BLE_simple_device_t* dev = seenDevices[i];
+ uint64_t lastseen = dev->lastseen/1000L;
+ lastseen = lastseen/1000L;
+ uint32_t lastseenS = (uint32_t) lastseen;
+ uint32_t del_at = lastseenS + ageS;
+ uint32_t devAge = nowS - lastseenS;
+ if (dev->maxAge < devAge){
+ dev->maxAge = devAge;
+ }
+
+ uint8_t filter = 0;
+ if (dev->addrtype > BLEAddressFilter){
+ filter = 1;
+ }
+
+ if ((del_at < nowS) || (ageS == 0) || filter){
+#ifdef BLE_ESP32_DEBUG
+ char addr[20];
+ dump(addr, 20, dev->mac, 6);
+ const char *alias = getAlias(dev->mac);
+ if (!filter){
+ AddLog_P(LOG_LEVEL_INFO,PSTR("delete device %s(%s) by age lastseen %u + maxage %u < now %u."),
+ addr, alias, lastseenS, ageS, nowS);
+ } else {
+ AddLog_P(LOG_LEVEL_INFO,PSTR("delete device %s(%s) by addrtype filter %d > %d."),
+ addr, alias, dev->addrtype, BLEAddressFilter);
+ }
+#endif
+ seenDevices.erase(seenDevices.begin()+i);
+ freeDevices.push_back(dev);
+ res++;
+ }
+ }
+ }
+ if (res){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_INFO,PSTR("BLE deleted %d devices"), res);
+#endif
+ }
+ return res;
+}
+
+int deleteSeenDevice(uint8_t *mac){
+ int res = 0;
+ TasAutoMutex localmutex(&BLEDevicesMutex, "BLEDel2");
+ for (int i = 0; i < seenDevices.size(); i++){
+ if (!memcmp(seenDevices[i]->mac, mac, 6)){
+ BLE_ESP32::BLE_simple_device_t* dev = seenDevices[i];
+ seenDevices.erase(seenDevices.begin()+i);
+ freeDevices.push_back(dev);
+ res = 1;
+ break;
+ }
+ }
+ return res;
+}
+
+
+void checkDeviceTimouts(){
+ if (BLEMaxAge){
+ deleteSeenDevices(BLEMaxAge);
+ }
+}
+
+
+///////////////////////////////////////////////////////
+// returns age of device or 0. if age IS0, returns 1s
+uint32_t devicePresent(uint8_t *mac){
+ int res = 0;
+ uint64_t now = esp_timer_get_time();
+ now = now/1000L;
+ now = now/1000L;
+ uint32_t nowS = (uint32_t)now;
+
+ TasAutoMutex localmutex(&BLEDevicesMutex, "BLEPRes");
+ for (int i = 0; i < seenDevices.size(); i++){
+ if (!memcmp(seenDevices[i]->mac, mac, 6)){
+ uint64_t lastseen = seenDevices[i]->lastseen/1000L;
+ lastseen = lastseen/1000L;
+ uint32_t lastseenS = (uint32_t) lastseen;
+ uint32_t ageS = nowS-lastseenS;
+ if (!ageS) ageS++;
+ res = ageS;
+ break;
+ }
+ }
+ return res;
+}
+
+
+// the MAX we could expect.
+#define MAX_DEV_JSON_NAME_LEN BLE_ESP32_MAXNAMELEN
+#define MAX_DEV_JSON_RSSI_LEN 3
+#define MAX_DEV_JSON_INDEX_LEN 3
+#define MAX_DEV_JSON_ALIAS_LEN BLE_ESP32_MAXALIASLEN
+// "001122334455":{"i":123,"n":"01234567890123456789","r":-77}\0
+#define MIN_REQUIRED_DEVJSON_LEN \
+ (1+12+1 + 1 + 1 + \
+ +4 + MAX_DEV_JSON_INDEX_LEN \
+ +1 + 4 + MAX_DEV_JSON_NAME_LEN + 2 \
+ +1 + 4 + MAX_DEV_JSON_RSSI_LEN + 2 \
+ +1 + 4 + MAX_DEV_JSON_ALIAS_LEN + 2 \
+ +1 +1 \
+ )
+int getSeenDeviceToJson(int index, BLE_ESP32::BLE_simple_device_t* dev, char **dest, int *maxlen){
+ char *p = *dest;
+ // add 20 to be sure
+ if (*maxlen < MIN_REQUIRED_DEVJSON_LEN+20){
+ return 0;
+ }
+ // add mac as key
+ *((*dest)++) = '"';
+ dump((*dest), 20, dev->mac, 6);
+ (*dest) += 12;
+ *((*dest)++) = '"';
+ *((*dest)++) = ':';
+
+ // add a structure, so we COULD add more than name later
+ *((*dest)++) = '{';
+ *((*dest)++) = '"';
+ *((*dest)++) = 'i'; // index
+ *((*dest)++) = '"';
+ *((*dest)++) = ':';
+ sprintf((*dest), "%d", index);
+ (*dest) += strlen((*dest));
+
+ if (dev->name[0]){
+ *((*dest)++) = ',';
+ *((*dest)++) = '"';
+ *((*dest)++) = 'n';
+ *((*dest)++) = '"';
+ *((*dest)++) = ':';
+ *((*dest)++) = '"';
+ *(*dest) = 0; // must term, else it adds to the *end* of old data!
+ strncat((*dest), dev->name, MAX_DEV_JSON_NAME_LEN);
+ (*dest) += strlen((*dest));
+ *((*dest)++) = '"';
+ }
+ *((*dest)++) = ',';
+ *((*dest)++) = '"';
+ *((*dest)++) = 'r';
+ *((*dest)++) = '"';
+ *((*dest)++) = ':';
+ sprintf((*dest), "%d", dev->RSSI);
+ (*dest) += strlen((*dest));
+
+ const char *alias = getAlias(dev->mac);
+ if (alias && alias[0]){
+ *((*dest)++) = ',';
+ *((*dest)++) = '"';
+ *((*dest)++) = 'a';
+ *((*dest)++) = '"';
+ *((*dest)++) = ':';
+ *((*dest)++) = '"';
+ sprintf((*dest), "%s", alias);
+ (*dest) += strlen((*dest));
+ *((*dest)++) = '"';
+ }
+
+ *((*dest)++) = '}';
+ *maxlen -= (*dest - p);
+ return 1;
+}
+
+
+int nextSeenDev = 0;
+
+int getSeenDevicesToJson(char *dest, int maxlen){
+
+ if ((nextSeenDev == 0) || (nextSeenDev >= seenDevices.size())){
+ nextSeenDev = 0;
+ }
+
+ // deliberate test of SafeAddLog_P from main thread...
+ //AddLog_P(LOG_LEVEL_INFO,PSTR("getSeen %d"), seenDevices.size());
+
+
+ int len;
+ if (!maxlen) return 0;
+ strcpy((dest), ",\"BLEDevices\":{");
+ len = strlen(dest);
+ dest += len;
+ maxlen -= len;
+
+ int added = 0;
+ TasAutoMutex localmutex(&BLEDevicesMutex, "BLEGet");
+
+ snprintf((dest), maxlen-5, "\"total\":%d", seenDevices.size());
+ len = strlen(dest);
+ dest += len;
+ maxlen -= len;
+ added = 1; // trigger ','
+
+ for (; nextSeenDev < seenDevices.size(); nextSeenDev++){
+ if (maxlen > MIN_REQUIRED_DEVJSON_LEN + 3){
+ if (added){
+ *(dest++) = ',';
+ maxlen--;
+ }
+ int res = getSeenDeviceToJson(nextSeenDev, seenDevices[nextSeenDev], &dest, &maxlen);
+ if (res) {
+ added++;
+ } else {
+ if (added){
+ dest--; // reverse out comma it the string did not get added
+ maxlen++;
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ *(dest++) = '}';
+ *(dest++) = '}';
+ *(dest++) = 0;
+ int remains = (seenDevices.size() - nextSeenDev);
+ return remains;
+}
+
+
+
+
+/*********************************************************************************************\
+ * Mutex protected logging - max 5 logs of 40 chars
+\*********************************************************************************************/
+
+/*
+#ifdef BLE_ESP32_DEBUG
+ #define MAX_SAFELOG_LEN 40
+ #define MAX_SAFELOG_COUNT 25
+#else
+ #define MAX_SAFELOG_LEN 20
+ #define MAX_SAFELOG_COUNT 5
+#endif
+
+struct safelogdata {
+ int level;
+ char log_data[MAX_SAFELOG_LEN];
+};
+
+std::deque freelogs;
+std::deque filledlogs;
+uint8_t filledlogsOverflows = 0;
+SemaphoreHandle_t SafeLogMutex;
+
+
+void initSafeLog(){
+ TasmotaMainTask = xTaskGetCurrentTaskHandle();
+ SafeLogMutex = xSemaphoreCreateMutex();
+
+ for (int i = 0; i < MAX_SAFELOG_COUNT; i++){
+ BLE_ESP32::safelogdata* logdata = new BLE_ESP32::safelogdata;
+ freelogs.push_back(logdata);
+ }
+}
+
+int SafeAddLog_P(uint32_t loglevel, PGM_P formatP, ...) {
+ TaskHandle_t thistask = xTaskGetCurrentTaskHandle();
+ int added = 0;
+
+ // if the log would not be output do nothing here.
+ if ((loglevel > Settings.weblog_level) &&
+ (loglevel > TasmotaGlobal.seriallog_level) &&
+ (loglevel > Settings.mqttlog_level) &&
+ (loglevel > TasmotaGlobal.syslog_level)){
+ return added;
+ }
+
+ char BLE_temp_log_data[MAX_SAFELOG_LEN];
+ // as these are'expensive', let's not bother unless they are lower than the serial log level
+#ifndef USE_NATIVE_LOGGING
+ xSemaphoreTake(SafeLogMutex, portMAX_DELAY);
+#endif
+ int maxlen = sizeof(BLE_temp_log_data)-3;
+ if (thistask == TasmotaMainTask){
+ maxlen -= 13; // room for "-!MAINTHREAD!"
+ }
+ // assume this is thread safe - it may not be
+ va_list arg;
+ va_start(arg, formatP);
+ vsnprintf_P(BLE_temp_log_data, maxlen, formatP, arg);
+ va_end(arg);
+#ifdef USE_NATIVE_LOGGING
+ AddLog_P(loglevel, PSTR("%s"), BLE_temp_log_data);
+ return 1;
+#else
+ if (thistask == TasmotaMainTask){
+ loglevel = LOG_LEVEL_ERROR;
+ snprintf(BLE_temp_log_data + strlen(BLE_temp_log_data), 13, "-!MAINTHREAD!");
+ xSemaphoreGive(SafeLogMutex); // release mutex
+ AddLog_P(loglevel, PSTR("%s"), BLE_temp_log_data);
+ return 0;
+ }
+
+ if (freelogs.size()){
+ BLE_ESP32::safelogdata* logdata = (freelogs)[0];
+ freelogs.pop_front();
+ logdata->level = loglevel;
+ memcpy(logdata->log_data, BLE_temp_log_data, sizeof(logdata->log_data));
+ filledlogs.push_back(logdata);
+ added = 1;
+ } else {
+ // can't log it?
+ filledlogsOverflows++;
+ }
+ xSemaphoreGive(SafeLogMutex); // release mutex
+ return added;
+#endif
+}
+
+BLE_ESP32::safelogdata* GetSafeLog() {
+ xSemaphoreTake(SafeLogMutex, portMAX_DELAY);
+ if (filledlogs.size()){
+ BLE_ESP32::safelogdata* logdata = (filledlogs)[0];
+ filledlogs.pop_front();
+ xSemaphoreGive(SafeLogMutex); // release mutex
+ return logdata;
+ }
+ xSemaphoreGive(SafeLogMutex); // release mutex
+ return nullptr;
+}
+
+void ReleaseSafeLog(BLE_ESP32::safelogdata* logdata){
+ xSemaphoreTake(SafeLogMutex, portMAX_DELAY);
+ freelogs.push_back(logdata);
+ xSemaphoreGive(SafeLogMutex); // release mutex
+}
+*/
+
+/*********************************************************************************************\
+ * Helper functions
+\*********************************************************************************************/
+
+/**
+ * @brief Simple pair of functions to dump to a hex string.
+ *
+ */
+static const char h[] PROGMEM = "0123456789ABCDEF";
+void hex(char *dest, uint8_t v){
+ *(dest++) = h[(v>>4)&0xf];
+ *(dest++) = h[v&0xf];
+ *(dest) = 0;
+}
+
+// convert from binary to hex.
+// add a '+' on the end if not enough room.
+char * dump(char *dest, int maxchars, const uint8_t *src, int len){
+ int lenmax = (maxchars-1)/2;
+ int actuallen = 0;
+ for (actuallen = 0; actuallen < lenmax && actuallen < len; actuallen++){
+ if (actuallen < lenmax){
+ hex(dest+actuallen*2, src[actuallen]);
+ }
+ }
+ if (actuallen != len){
+ *(dest+(actuallen*2)) = '+';
+ *(dest+(actuallen*2)+1) = 0;
+ }
+ return dest;
+}
+
+// convert from a hex string to binary
+int fromHex(uint8_t *dest, const char *src, int maxlen){
+ int srclen = strlen(src)/2;
+ if (srclen > maxlen){
+ return 0;
+ }
+
+ for (int i = 0; i < srclen; i++){
+ char t[3];
+ if (!isalnum(src[i*2])){
+ return 0;
+ }
+ if (!isalnum(src[i*2 + 1])){
+ return 0;
+ }
+
+ t[0] = src[i*2];
+ t[1] = src[i*2 + 1];
+ t[2] = 0;
+
+ int byte = strtol(t, NULL, 16);
+ *dest++ = byte;
+ }
+ return srclen;
+}
+
+
+/**
+ * @brief Reverse an array of 6 bytes
+ *
+ * @param _mac a byte array of size 6 (typicalliy representing a MAC address)
+ */
+void ReverseMAC(uint8_t _mac[]){
+ uint8_t _reversedMAC[6];
+ for (uint8_t i=0; i<6; i++){
+ _reversedMAC[5-i] = _mac[i];
+ }
+ memcpy(_mac,_reversedMAC, sizeof(_reversedMAC));
+}
+
+
+
+
+/*********************************************************************************************\
+ * Advertisment details
+\*********************************************************************************************/
+
+//ble_advertisment_t BLEAdvertismentDetails;
+#define MAX_ADVERT_DETAILS 200
+char BLEAdvertismentDetailsJson[MAX_ADVERT_DETAILS];
+uint8_t BLEAdvertismentDetailsJsonSet = 0;
+uint8_t BLEAdvertismentDetailsJsonLost = 0;
+
+
+void setDetails(ble_advertisment_t *ad){
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLESetDet");
+ if (BLEAdvertismentDetailsJsonSet){
+ BLEAdvertismentDetailsJsonLost = 1;
+ return;
+ }
+ char *p = BLEAdvertismentDetailsJson;
+ int maxlen = sizeof(BLEAdvertismentDetailsJson);
+ // just in case someone tries to read whilst we are writing
+ BLEAdvertismentDetailsJson[sizeof(BLEAdvertismentDetailsJson)-1] = 0;
+
+ *(p++) = '{';
+ maxlen--;
+ strcpy(p, "\"details\":{");
+ int len = strlen(p);
+ p += len;
+ maxlen -= len;
+
+ strcpy(p, "\"mac\":\"");
+ len = strlen(p);
+ p += len;
+ maxlen -= len;
+ dump(p, 14, ad->addr, 6);
+ len = strlen(p);
+ p += len;
+ maxlen -= len;
+ *(p++) = '\"'; maxlen--;
+
+ if (BLEAdvertismentDetailsJsonLost){
+ BLEAdvertismentDetailsJsonLost = 0;
+ strcpy(p, ",\"lost\":true");
+ len = strlen(p);
+ p += len;
+ maxlen -= len;
+ }
+
+ BLEAdvertisedDevice *advertisedDevice = ad->advertisedDevice;
+
+ uint8_t* payload = advertisedDevice->getPayload();
+ size_t payloadlen = advertisedDevice->getPayloadLength();
+ if (payloadlen && (maxlen > 30)){ // will truncate if not enough space
+ strcpy(p, ",\"p\":\"");
+ p += 6;
+ maxlen -= 6;
+ dump(p, maxlen-10, payload, payloadlen);
+ int len = strlen(p);
+ p += len;
+ maxlen -= len;
+ *(p++) = '\"'; maxlen--;
+ }
+
+ int svcdataCount = advertisedDevice->getServiceDataCount();
+ if (svcdataCount){
+ for (int i = 0; i < svcdataCount; i++){
+ NimBLEUUID UUID = advertisedDevice->getServiceDataUUID(i);//.getNative()->u16.value;
+ std::string ServiceData = advertisedDevice->getServiceData(i);
+
+ size_t ServiceDataLength = ServiceData.length();
+ const uint8_t *serviceData = (const uint8_t *)ServiceData.data();
+
+ //char svcuuidstr[20];
+ std::string strUUID = UUID;
+
+ int svclen = strUUID.length();
+ svclen++; // ,
+ svclen += 3; // "":
+ svclen += ServiceDataLength*2;
+ svclen += 3; // ""}
+
+ if (maxlen -10 > svclen){
+ *(p++) = ',';
+ *(p++) = '\"';
+ strcpy(p, strUUID.c_str());
+ p += strUUID.length();
+ *(p++) = '\"';
+ *(p++) = ':';
+ *(p++) = '\"';
+ dump(p, ServiceDataLength*2+2, (uint8_t*)serviceData, ServiceDataLength);
+ int len = strlen(p);
+ p += len;
+ *(p++) = '\"';
+ maxlen -= len;
+ }
+ }
+ }
+
+ *(p++) = '}'; maxlen--;
+ *(p++) = '}'; maxlen--;
+ *(p++) = 0; maxlen--;
+
+ BLEAdvertismentDetailsJsonSet = 1;
+}
+
+
+// call from main thread only!
+// post advertisment detail if available, then clear.
+void postAdvertismentDetails(){
+// if (TasmotaGlobal.ota_state_flag) return;
+
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEPostAdd");
+ if (BLEAdvertismentDetailsJsonSet){
+ strncpy(TasmotaGlobal.mqtt_data, BLEAdvertismentDetailsJson, sizeof(TasmotaGlobal.mqtt_data));
+ TasmotaGlobal.mqtt_data[sizeof(TasmotaGlobal.mqtt_data)-1] = 0;
+ BLEAdvertismentDetailsJsonSet = 0;
+ // we got the data, give before MQTT call.
+ localmutex.give();
+ // no retain - this is present devices, not historic
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), 0);
+ } else {
+ }
+}
+
+
+
+/*********************************************************************************************\
+ * Classes
+\*********************************************************************************************/
+
+// does not really take any action
+class BLESensorCallback : public NimBLEClientCallbacks {
+ void onConnect(NimBLEClient* pClient) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("onConnect %s"), ((std::string)pClient->getPeerAddress()).c_str());
+#endif
+ }
+ void onDisconnect(NimBLEClient* pClient) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("onDisconnect %s"), ((std::string)pClient->getPeerAddress()).c_str());
+#endif
+ }
+ bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("onConnParamsUpdateRequest %s"), ((std::string)pClient->getPeerAddress()).c_str());
+#endif
+
+// if(params->itvl_min < 24) { /** 1.25ms units */
+// return false;
+// } else if(params->itvl_max > 300) { /** 1.25ms units */
+// return false;
+// } else if(params->latency > 2) { /** Number of intervals allowed to skip */
+// return false;
+// } else if(params->supervision_timeout > 6000) { /** 10ms units */
+// return false;
+// }
+
+/*
+ if(params->itvl_min < 24) { // 1.25ms units
+ return false;
+ } else if(params->itvl_max > 40) { // 1.25ms units
+ return false;
+ } else if(params->latency > 2) { // Number of intervals allowed to skip
+ return false;
+ } else if(params->supervision_timeout > 200) { /// 10ms units
+ return false;
+ }
+
+ return true;
+*/
+ // just always reject thiers, and use ours.
+ return false;
+
+ }
+};
+
+static BLESensorCallback clientCB;
+
+
+class BLEAdvCallbacks: public NimBLEAdvertisedDeviceCallbacks {
+ void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEAddCB");
+ uint64_t now = esp_timer_get_time();
+ BLEScanLastAdvertismentAt = now; // note the time of the last advertisment
+
+ uint32_t totalCount = BLEAdvertisment.totalCount;
+ memset(&BLEAdvertisment, 0, sizeof(BLEAdvertisment));
+ BLEAdvertisment.totalCount = totalCount+1;
+
+ BLEAdvertisment.advertisedDevice = advertisedDevice;
+
+ // keep sign - char seems unsigned
+ int8_t RSSI = (char)advertisedDevice->getRSSI();
+ NimBLEAddress address = advertisedDevice->getAddress();
+
+ BLEAdvertisment.addrtype = address.getType();
+
+ memcpy(BLEAdvertisment.addr, address.getNative(), 6);
+ ReverseMAC(BLEAdvertisment.addr);
+
+ BLEAdvertisment.RSSI = RSSI;
+
+ char addrstr[20];
+ dump(addrstr, 20, BLEAdvertisment.addr, 6);
+
+ // this mjust survive the scope of the callbacks
+ std::string name = "";
+ const char *namestr = name.c_str();
+ if (advertisedDevice->haveName()){
+ name = advertisedDevice->getName();
+ namestr = name.c_str();
+ strncpy(BLEAdvertisment.name, namestr, sizeof(BLEAdvertisment.name)-1);
+ BLEAdvertisment.name[sizeof(BLEAdvertisment.name)-1] = 0;
+ }
+
+
+ // log this device safely
+ if (BLEAdvertisment.addrtype <= BLEAddressFilter){
+ addSeenDevice(BLEAdvertisment.addr, BLEAdvertisment.addrtype, BLEAdvertisment.name, BLEAdvertisment.RSSI);
+ }
+
+ if (BLEDetailsRequest){
+ switch (BLEDetailsRequest){
+ case 1:{ // one advert for one device
+ BLEDetailsRequest = 0; // only one requested if 2, it's a request all
+ if (!memcmp(BLEDetailsMac, BLEAdvertisment.addr, 6)){
+ setDetails(&BLEAdvertisment);
+ }
+ } break;
+ case 2:{ // all adverts for one device - may not get them all
+ if (!memcmp(BLEDetailsMac, BLEAdvertisment.addr, 6)){
+ setDetails(&BLEAdvertisment);
+ }
+ } break;
+ case 3:{ // all adverts for ALL DEVICES - may not get them all
+ // ignore from here on if filtered on addrtype
+ if (BLEAdvertisment.addrtype > BLEAddressFilter){
+ return;
+ }
+ setDetails(&BLEAdvertisment);
+ } break;
+ }
+ }
+
+ // ignore from here on if filtered on addrtype
+ if (BLEAdvertisment.addrtype > BLEAddressFilter){
+ return;
+ }
+
+ // call anyone who asked about advertisements
+ for (int i = 0; i < advertismentCallbacks.size(); i++) {
+ try {
+ ADVERTISMENT_CALLBACK* pFN;
+ pFN = advertismentCallbacks[i];
+ int res = pFN(&BLEAdvertisment);
+
+ // if this callback wants to stop here, then do so.
+ if (1 == res) break;
+
+ // if this callback wants to kill this device
+ if (2 == res) {
+ //BLEScan->erase(address);
+ }
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("exception in advertismentCallbacks"));
+#endif
+ }
+ }
+
+ }
+};
+
+
+static BLEAdvCallbacks BLEScanCallbacks;
+static BLESensorCallback BLESensorCB;
+
+/*********************************************************************************************\
+ * BLE callback functions
+\*********************************************************************************************/
+
+static void BLEscanEndedCB(NimBLEScanResults results){
+
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("Scan ended"));
+#endif
+ for (int i = 0; i < scancompleteCallbacks.size(); i++){
+ try {
+ SCANCOMPLETE_CALLBACK *pFn = scancompleteCallbacks[i];
+ int callbackres = pFn(results);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("scancompleteCallbacks %d %d"), i, callbackres);
+#endif
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("exception in operationsCallbacks"));
+#endif
+ }
+ }
+
+ BLERunningScan = 2;
+ BLEScanToEndBefore = 0L;
+ BLEScanCount++;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+// !!!!!!!!!!@@@@@@@@@@@@@@@@
+// NOTE: this can callback BEFORE the write is completed.
+// so we should not do any actions against the device if we can help it
+// this COULD be the reason for the BLE stack hanging up....
+///////////////////////////////////////////////////////////////////////
+static void BLEGenNotifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
+ NimBLEClient *pRClient;
+
+ if (!pRemoteCharacteristic){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("Notify: no remote char!!??"));
+#endif
+ return;
+ }
+
+
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("Notified length: %u"),length);
+#endif
+ // find the operation this is associated with
+ NimBLERemoteService *pSvc = pRemoteCharacteristic->getRemoteService();
+
+ if (!pSvc){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Notify: no remote service found"));
+#endif
+ return;
+ }
+
+ pRClient = pSvc->getClient();
+ if (!pRClient){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Notify: no remote client!!??"));
+#endif
+ return;
+ }
+ NimBLEAddress devaddr = pRClient->getPeerAddress();
+
+ generic_sensor_t *thisop = nullptr;
+ {
+ // make sure we are not disturbed
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLENotif");
+
+ for (int i = 0; i < currentOperations.size(); i++){
+ generic_sensor_t *op = currentOperations[i];
+ if (!op){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Notify: null op in currentOperations!!??"));
+#endif
+ } else {
+ if (devaddr == op->addr){
+ thisop = op;
+ break;
+ }
+ }
+ }
+ }
+
+ // we'll try without
+ //pRemoteCharacteristic->unsubscribe();
+
+ if (!thisop){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("no op for notify"));
+#endif
+ return;
+ }
+
+ for (int i = 0; i < length && i < sizeof(thisop->dataNotify); i++){
+ thisop->dataNotify[i] = pData[i];
+ }
+ thisop->notifylen = length;
+ if (length > sizeof(thisop->dataNotify)){
+ thisop->notifytruncated = 1;
+ } else {
+ thisop->notifytruncated = 0;
+ }
+ // we will NOT change the state here...
+ // rely on thisop->notifylen as a flag notify is complete
+ //thisop->state = GEN_STATE_NOTIFIED;
+
+ // this triggers our notify complete, either at the end of read/write, or next 1s cycle.
+ thisop->notifytimer = 0;
+
+}
+
+
+
+
+/*********************************************************************************************\
+ * functions for registering callbacks against the driver
+\*********************************************************************************************/
+
+void registerForAdvertismentCallbacks(const char *tag, BLE_ESP32::ADVERTISMENT_CALLBACK* pFn){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: registerForAdvertismentCallbacks %s:%x"), tag, (uint32_t) pFn);
+#endif
+ advertismentCallbacks.push_back(pFn);
+}
+
+void registerForOpCallbacks(const char *tag, BLE_ESP32::OPCOMPLETE_CALLBACK* pFn){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: registerForOpCallbacks %s:%x"), tag, (uint32_t) pFn);
+#endif
+ operationsCallbacks.push_back(pFn);
+}
+
+void registerForScanCallbacks(const char *tag, BLE_ESP32::SCANCOMPLETE_CALLBACK* pFn){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: registerForScnCallbacks %s:%x"), tag, (uint32_t) pFn);
+#endif
+ scancompleteCallbacks.push_back(pFn);
+}
+
+
+/*********************************************************************************************\
+ * init NimBLE
+\*********************************************************************************************/
+static void BLEPreInit(void) {
+ BLEInitState = 0;
+ prepOperation = nullptr;
+}
+
+
+static void BLEInit(void) {
+ if (BLEMode == BLEModeDisabled) return;
+
+ if (BLEInitState) { return; }
+
+ if (TasmotaGlobal.global_state.wifi_down) { return; }
+ if (WiFi.getSleep() == false) {
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: WiFi modem not in sleep mode, BLE cannot start yet"));
+#endif
+ if (0 == Settings.flag3.sleep_normal) {
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: About to restart to put WiFi modem in sleep mode"));
+ Settings.flag3.sleep_normal = 1; // SetOption60 - Enable normal sleep instead of dynamic sleep
+ TasmotaGlobal.restart_flag = 2;
+ }
+ return;
+ }
+
+
+ // this is only for testing, does nothin if examples are undefed
+ installExamples();
+ //initSafeLog();
+ initSeenDevices();
+
+ uint64_t now = esp_timer_get_time();
+ BLEScanLastAdvertismentAt = now; // initialise the time of the last advertisment
+ BLELastLoopTime = now;
+
+ BLEInitState = 1;
+
+ // dont start of disabled
+ BLEMasterEnable = Settings.flag5.mi32_enable;
+ if (!BLEMasterEnable) return;
+
+
+ StartBLE();
+
+ return;
+}
+
+/*********************************************************************************************\
+ * Task section
+\*********************************************************************************************/
+
+static void BLEOperationTask(void *pvParameters);
+
+static void BLEStartOperationTask(){
+ if (BLERunning == false){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Start operations"),D_CMND_BLE);
+#endif
+ BLERunning = true;
+
+ xTaskCreatePinnedToCore(
+ BLE_ESP32::BLEOperationTask, /* Function to implement the task */
+ "BLEOperationTask", /* Name of the task */
+ 4096, /* Stack size in bytes */
+ NULL, /* Task input parameter */
+ 0, /* Priority of the task */
+ NULL, /* Task handle. */
+#ifdef CONFIG_FREERTOS_UNICORE
+ 0); /* Core where the task should run */
+#else
+ 1); /* Core where the task should run */
+#endif
+ }
+}
+
+
+static void BLETaskStopStartNimBLE(NimBLEClient **ppClient, bool start = true){
+
+ if (*ppClient){
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLETask:Stopping NimBLE"));
+
+ (*ppClient)->setClientCallbacks(nullptr, false);
+
+ try {
+ if ((*ppClient)->isConnected()){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_INFO,PSTR("disconnecting connected client"));
+#endif
+ (*ppClient)->disconnect();
+ }
+ NimBLEDevice::deleteClient((*ppClient));
+ (*ppClient) = nullptr;
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_INFO,PSTR("deleted client"));
+#endif
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Stopping NimBLE:exception in delete client"));
+#endif
+ }
+
+ if (ble32Scan){
+ ble32Scan->setAdvertisedDeviceCallbacks(nullptr,true);
+ ble32Scan->stop();
+ ble32Scan = nullptr;
+ }
+
+ // wait second
+ vTaskDelay(100/ portTICK_PERIOD_MS);
+ NimBLEDevice::deinit(true);
+ }
+ BLERunningScan = 0;
+
+ if (start){
+ AddLog_P(LOG_LEVEL_INFO,PSTR("BLETask:Starting NimBLE"));
+ NimBLEDevice::init("BLE_ESP32");
+
+ *ppClient = NimBLEDevice::createClient();
+ (*ppClient)->setClientCallbacks(&clientCB, false);
+ /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
+ * These settings are safe for 3 clients to connect reliably, can go faster if you have less
+ * connections. Timeout should be a multiple of the interval, minimum is 100ms.
+ * Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout
+ */
+ (*ppClient)->setConnectionParams(12,12,0,51);
+ /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
+ (*ppClient)->setConnectTimeout(15);
+ }
+
+ uint64_t now = esp_timer_get_time();
+
+ // don't restart because of these for a while
+ BLELastLoopTime = now; // initialise the time of the last advertisment
+ BLEScanLastAdvertismentAt = now; // initialise the time of the last advertisment
+
+}
+
+int BLETaskStartScan(int time){
+ if (!ble32Scan) return -1;
+ if (BLEMode == BLEModeDisabled) return -4;
+ // don't scan whilst OTA in progress
+ if (BLEOtaStallBLE) return -5;
+ if (currentOperations.size()) return -3;
+
+ if (BLERunningScan) {
+ // if we hit 2, wait one more time before starting
+ if (BLERunningScan == 2){
+ // wait 100ms
+ vTaskDelay(100/ portTICK_PERIOD_MS);
+ BLERunningScan = 0;
+ }
+ return -2;
+ }
+
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: Startscan"));
+#endif
+ //vTaskDelay(500/ portTICK_PERIOD_MS);
+ ble32Scan->setActiveScan(BLEScanActiveMode ? 1: 0);
+
+
+ // seems we could get the callback within the start call....
+ // so set these before starting
+ BLERunningScan = 1;
+ BLEScanStartedAt = esp_timer_get_time();
+ if (BLETriggerScan){
+ time = BLETriggerScan;
+ BLETriggerScan = 0;
+ }
+ ble32Scan->start(time, BLEscanEndedCB, (BLEScanActiveMode == 2)); // 20s scans, restarted when then finish
+ //vTaskDelay(500/ portTICK_PERIOD_MS);
+ return 0;
+}
+
+// this runs one operation
+// if the passed pointer is empty, it tries to get a next one.
+static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOperation, NimBLEClient **ppClient){
+ if (!pCurrentOperation) return;
+
+ NimBLEClient *pClient = *ppClient;
+ if (!*pCurrentOperation) {
+ *pCurrentOperation = nextOperation(&queuedOperations);
+ if (*pCurrentOperation){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: new currentOperation"));
+#endif
+ BLEOpCount++;
+ generic_sensor_t* temp = *pCurrentOperation;
+ //this will null it out, so save and restore.
+ addOperation(¤tOperations, pCurrentOperation);
+ *pCurrentOperation = temp;
+ }
+ }
+ if (!*pCurrentOperation) return;
+
+
+
+ // if awaiting notification
+ if ((*pCurrentOperation)->notifytimer){
+ // if it took too long, then disconnect
+ uint64_t now = esp_timer_get_time();
+ uint64_t diff = now - (*pCurrentOperation)->notifytimer;
+ diff = diff/1000;
+ if (diff > 20000){ // 20s
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: notify timeout"));
+#endif
+ (*pCurrentOperation)->state = GEN_STATE_FAILED_NOTIFYTIMEOUT;
+ (*pCurrentOperation)->notifytimer = 0;
+ }
+ // we can't process any further, because op will be at state readdone or writedone
+ return;
+ }
+
+
+ switch((*pCurrentOperation)->state){
+ case GEN_STATE_WAITINDICATE:
+ case GEN_STATE_WAITNOTIFY:
+ //(*pCurrentOperation)->notifytimer == 0 at this point, so must be done
+ (*pCurrentOperation)->state = GEN_STATE_NOTIFIED;
+ // just stay here until this is removed by the main thread
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: notify operation complete"));
+#endif
+ BLE_ESP32::BLETaskRunTaskDoneOperation(pCurrentOperation, ppClient);
+ pClient = *ppClient;
+ return;
+ break;
+ case GEN_STATE_READDONE:
+ case GEN_STATE_WRITEDONE:
+ case GEN_STATE_NOTIFIED: // - may have completed DURING our read/write to get here
+ // just stay here until this is removed by the main thread
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: operation complete"));
+#endif
+ BLE_ESP32::BLETaskRunTaskDoneOperation(pCurrentOperation, ppClient);
+ pClient = *ppClient;
+ return;
+ break;
+
+ case GEN_STATE_START:
+ // continue to start the process here.
+ break;
+
+ default:
+ break;
+ }
+
+
+ if (!*pCurrentOperation) return;
+
+ if ((*pCurrentOperation)->state <= GEN_STATE_FAILED){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLETask: op failed %d"), (*pCurrentOperation)->state);
+#endif
+ BLE_ESP32::BLETaskRunTaskDoneOperation(pCurrentOperation, ppClient);
+ pClient = *ppClient;
+ return;
+ }
+
+ if ((*pCurrentOperation)->state != GEN_STATE_START){
+ return;
+ }
+
+ if (pClient->isConnected()){
+ // don't do anything if we are still connected
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: still connected"));
+#endif
+ return;
+ }
+
+
+ // if we managed to run operations back to back with long connection timeouts,
+ // then we may NOT see advertisements.
+ // so to prevent triggering of the advert timeout restart mechanism,
+ // set the last advert time each time we start an operation
+ uint64_t now = esp_timer_get_time();
+ BLEScanLastAdvertismentAt = now; // initialise the time of the last advertisment
+
+
+ generic_sensor_t* op = *pCurrentOperation;
+
+ int newstate = GEN_STATE_STARTED;
+ op->state = GEN_STATE_STARTED;
+
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: attempt connect %s"), ((std::string)op->addr).c_str());
+#endif
+
+ if (!op->serviceUUID.bitSize()){
+ op->state = GEN_STATE_FAILED_NOSERVICE;
+ return;
+ }
+
+ if (pClient->connect(op->addr, true)) {
+
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("connected %s -> getservice"), ((std::string)op->addr).c_str());
+#endif
+ NimBLERemoteService *pService = pClient->getService(op->serviceUUID);
+ int waitNotify = false;
+ int notifystate = 0;
+ op->notifytimer = 0L;
+
+ if (pService != nullptr) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("got service"));
+#endif
+ // pre-set to fail if no operations requested
+ //newstate = GEN_STATE_FAILED_NOREADWRITE;
+
+ ///////////////////////////////////////////////////////////////////////
+ // !!!!!!!!!!@@@@@@@@@@@@@@@@
+ // NOTE: Notify callback can happen BEFORE the read/write is completed.
+ // this COULD be the reason for the BLE stack hanging up....
+ ///////////////////////////////////////////////////////////////////////
+
+ // if we have been asked to get a notification
+ if (op->notificationCharacteristicUUID.bitSize()) {
+ NimBLERemoteCharacteristic *pNCharacteristic =
+ pService->getCharacteristic(op->notificationCharacteristicUUID);
+ if (pNCharacteristic != nullptr) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("got notify characteristic"));
+#endif
+ op->notifylen = 0;
+ if(pNCharacteristic->canNotify()) {
+ if(pNCharacteristic->subscribe(true, BLE_ESP32::BLEGenNotifyCB)) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("subscribe for notify"));
+#endif
+ uint64_t now = esp_timer_get_time();
+ op->notifytimer = now;
+ // this will get changed to read or write,
+ // but here in case it's notify only (can that happen?)
+ notifystate = GEN_STATE_WAITNOTIFY;
+ waitNotify = true;
+ } else {
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("failed subscribe for notify"));
+#endif
+ newstate = GEN_STATE_FAILED_NOTIFY;
+ }
+ } else {
+ if(pNCharacteristic->canIndicate()) {
+ if(pNCharacteristic->subscribe(false, BLE_ESP32::BLEGenNotifyCB)) {
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("subscribe for indicate"));
+#endif
+ notifystate = GEN_STATE_WAITINDICATE;
+ uint64_t now = esp_timer_get_time();
+ op->notifytimer = now;
+ waitNotify = true;
+ } else {
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("failed subscribe for indicate"));
+#endif
+ newstate = GEN_STATE_FAILED_INDICATE;
+ }
+ } else {
+ newstate = GEN_STATE_FAILED_CANTNOTIFYORINDICATE;
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("characteristic can't notify"));
+#endif
+ }
+ }
+ } else {
+ newstate = GEN_STATE_FAILED_NONOTIFYCHAR;
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("notify characteristic not found"));
+#endif
+ }
+
+ // force the 'error' of the notify coming in before the read/write for testing
+ //vTaskDelay(1000/ portTICK_PERIOD_MS);
+ } // no supplied notify char is ok
+
+ // this will only happen if you ask for a notify char which is not there?
+ if (!(newstate <= GEN_STATE_FAILED)){
+ if (op->characteristicUUID.bitSize()) {
+ // read or write characteristic - we always need this?
+ NimBLERemoteCharacteristic *pCharacteristic = nullptr;
+
+ pCharacteristic = pService->getCharacteristic(op->characteristicUUID);
+ if (pCharacteristic != nullptr) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("got read/write characteristic"));
+#endif
+ newstate = GEN_STATE_FAILED_NOREADWRITE; // overwritten on failure
+
+ if (op->readlen){
+ if(pCharacteristic->canRead()) {
+ std::string value = pCharacteristic->readValue();
+ op->readlen = value.length();
+ memcpy(op->dataRead, value.data(),
+ (op->readlen > sizeof(op->dataRead))?
+ sizeof(op->dataRead):
+ op->readlen);
+ if (op->readlen > sizeof(op->dataRead)){
+ op->readtruncated = 1;
+ } else {
+ op->readtruncated = 0;
+ }
+ if (op->readmodifywritecallback){
+ READ_CALLBACK *pFn = (READ_CALLBACK *)op->readmodifywritecallback;
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("read characteristic with readmodifywritecallback"));
+#endif
+ pFn(op);
+ } else {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("read characteristic"));
+#endif
+ }
+
+ // only change it to a 'finished' state if we really are
+ if (!waitNotify && !op->writelen) newstate = GEN_STATE_READDONE;
+
+ } else {
+ newstate = GEN_STATE_FAILED_CANTREAD;
+ }
+ }
+ if (op->writelen){
+ if(pCharacteristic->canWrite()) {
+ if (!pCharacteristic->writeValue(op->dataToWrite, op->writelen, true)){
+ newstate = GEN_STATE_FAILED_WRITE;
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("characteristic write fail"));
+#endif
+ } else {
+ if (!waitNotify) newstate = GEN_STATE_WRITEDONE;
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("write characteristic"));
+#endif
+ }
+ } else {
+ newstate = GEN_STATE_FAILED_CANTWRITE;
+ }
+ }
+ // print or do whatever you need with the value
+
+ } else {
+ newstate = GEN_STATE_FAILED_NO_RW_CHAR;
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("r/w characteristic not found"));
+#endif
+ }
+ }
+ }
+
+
+ // disconnect if not waiting for notify,
+ if (!op->notifytimer){
+ if (waitNotify){
+ vTaskDelay(50/ portTICK_PERIOD_MS);
+ // must have completed during our read/write operation
+ newstate = GEN_STATE_NOTIFIED;
+ }
+ } else {
+ newstate = notifystate;
+ }
+ } else {
+ newstate = GEN_STATE_FAILED_NOSERVICE;
+ // failed to get a service
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("failed - svc not on device?"));
+#endif
+ }
+
+ } else { // connect itself failed
+ newstate = GEN_STATE_FAILED_CONNECT;
+#ifdef NIMBLE_CLIENT_HAS_GETRESULT
+ int rc = pClient->getResult();
+
+ switch (rc){
+ case (0x0200+BLE_ERR_CONN_LIMIT ):
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Hit connection limit? - restarting NimBLE"));
+#endif
+ BLERestartNimBLE = 1;
+ BLERestartBLEReason = BLE_RESTART_BLE_REASON_CONN_LIMIT;
+ break;
+ case (0x0200+BLE_ERR_ACL_CONN_EXISTS):
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Connection exists? - restarting NimBLE"));
+#endif
+ BLERestartNimBLE = 1;
+ BLERestartBLEReason = BLE_RESTART_BLE_REASON_CONN_EXISTS;
+ break;
+ }
+#endif
+
+ // failed to connect
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("failed to connect to device %d"), rc);
+#endif
+ }
+ op->state = newstate;
+}
+
+
+
+// this disconnects from a device if necessary, and then
+// moves the operation from 'currentOperations' to 'completedOperations'.
+
+// for safety's sake, only call from the run task
+static void BLETaskRunTaskDoneOperation(BLE_ESP32::generic_sensor_t** op, NimBLEClient **ppClient){
+ try {
+ if ((*ppClient)->isConnected()){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("runTaskDoneOperation: disconnecting connected client"));
+#endif
+ (*ppClient)->disconnect();
+ // wait for 1/2 second after disconnect
+ int waits = 0;
+ do {
+ vTaskDelay(500/ portTICK_PERIOD_MS);
+ if (waits) {
+ //(*ppClient)->disconnect();
+ // we will stall here forever!!! - as testing
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE wait discon%d"), waits);
+#endif
+ vTaskDelay(500/ portTICK_PERIOD_MS);
+ }
+ waits++;
+ if (waits == 5){
+ int conn_id = (*ppClient)->getConnId();
+ ble_gap_conn_broken(conn_id, -1);
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE wait discon%d - kill connection"), waits);
+#endif
+ }
+ if (waits == 60){
+ AddLog_P(LOG_LEVEL_ERROR,PSTR(">60s waiting -> BLE Failed, restart Tasmota %d"), waits);
+ BLEStop = 1;
+ BLEStopAt = esp_timer_get_time();
+
+ BLERestartTasmota = 10;
+ BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_BLE_DISCONNECT_FAIL;
+ break;
+ }
+ } while ((*ppClient)->isConnected());
+ }
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("runTaskDoneOperation: exception in disconnect"));
+#endif
+ }
+
+
+ {
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEDoneOp");
+
+ // find this operation in currentOperations, and remove it.
+ for (int i = 0; i < currentOperations.size(); i++){
+ if (currentOperations[i]->opid == (*op)->opid){
+ currentOperations.erase(currentOperations.begin() + i);
+ break;
+ }
+ }
+ }
+
+
+ // by adding it to this list, this will cause it to be sent to MQTT
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("runTaskDoneOperation: add to completedOperations"));
+#endif
+ addOperation(&completedOperations, op);
+ return;
+}
+
+
+
+
+// this IS as task.
+// we MAY be able to run a few of these simultaneously, but this is not yet tested.
+// and probably not required. But everything is there to do so....
+static void BLEOperationTask(void *pvParameters){
+
+ BLELoopCount = 0;
+ BLEOpCount = 0;;
+
+ uint32_t timer = 0;
+ // operation which is currently in progress in THIS TASK
+ generic_sensor_t* currentOperation = nullptr;
+
+ NimBLEClient *pClient = nullptr;
+ BLE_ESP32::BLETaskStopStartNimBLE(&pClient);
+
+ for(;;){
+ BLELastLoopTime = esp_timer_get_time();
+ BLELoopCount++;
+
+ BLE_ESP32::BLETaskRunCurrentOperation(¤tOperation, &pClient);
+
+ // start a scan if possible
+ if ((BLEMode == BLEModeRegularScan) || (BLETriggerScan)){
+ BLEScan* lastScan = ble32Scan;
+ ble32Scan = NimBLEDevice::getScan();
+ if (lastScan != ble32Scan){
+ //ble32Scan->setInterval(70);
+ //ble32Scan->setWindow(50);
+ ble32Scan->setInterval(0x40);
+ ble32Scan->setWindow(0x20);
+ ble32Scan->setAdvertisedDeviceCallbacks(&BLEScanCallbacks,true);
+ }
+
+ BLE_ESP32::BLETaskStartScan(20);
+ }
+
+ if (BLEStopScan){
+ ble32Scan->stop();
+ BLEStopScan = 0;
+ }
+
+ // come around every 1/10s
+ vTaskDelay(100/ portTICK_PERIOD_MS);
+
+ if (BLEStop == 1){
+ break;
+ }
+
+ if (BLERestartNimBLE){
+ BLERestartNimBLE = 0;
+ BLERestartTasmota = 10;
+ BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_RESTARTING_BLE_TIMEOUT;
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLETask: Restart NimBLE - restart Tasmota in 10 if not complt"));
+ BLE_ESP32::BLETaskStopStartNimBLE(&pClient);
+ BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_UNKNOWN;
+ BLERestartTasmota = 0;
+ BLEResets ++;
+ }
+ }
+
+ BLE_ESP32::BLETaskStopStartNimBLE(&pClient, false);
+
+ // wait 1/10 second
+ vTaskDelay(100/ portTICK_PERIOD_MS);
+
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLEOperationTask: Left task"));
+#endif
+ deleteSeenDevices();
+
+ BLEStop = 2;
+ BLERunning = false;
+ vTaskDelete( NULL );
+}
+
+
+
+
+
+/***********************************************************************\
+ * Regular Tasmota called functions
+ *
+\***********************************************************************/
+void BLEEvery50mSecond(){
+/* if (BLEAliasListTrigger){
+ BLEAliasListTrigger = 0;
+ BLEAliasMqttList();
+ }*/
+ postAdvertismentDetails();
+}
+
+
+
+/**
+ * @brief Main loop of the driver, "high level"-loop
+ *
+ */
+
+static void BLEEverySecond(bool restart){
+
+ BLEDiag();
+
+ checkDeviceTimouts();
+
+
+ if (Settings.flag5.mi32_enable != BLEMasterEnable){
+ if (Settings.flag5.mi32_enable){
+ if (StartBLE()){
+ BLEMasterEnable = Settings.flag5.mi32_enable;
+ }
+ } else {
+ if (StopBLE()){
+ BLEMasterEnable = Settings.flag5.mi32_enable;
+ }
+ }
+ AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: MasterEnable->%d"), BLEMasterEnable);
+ }
+
+
+ // check for application callbacks here.
+ // this may remove complete items.
+ BLE_ESP32::mainThreadOpCallbacks();
+
+ // post any MQTT data if we completed anything in the last second
+ if (completedOperations.size()){
+ BLE_ESP32::BLEPostMQTT(true); // send only completed
+ }
+
+ // request send of ALL oeprations prepped, queued, in progress -
+ // in separate MQTT messages.
+ if (BLEPostMQTTTrigger){
+ BLEPostMQTTTrigger = 0;
+ BLE_ESP32::BLEPostMQTT(false); // show all operations, not just completed
+ }
+
+ if (BLEPublishDevices){
+ BLEPostMQTTSeenDevices(BLEPublishDevices);
+ BLEPublishDevices = 0;
+ }
+
+ // we have been asked to restart in this many seconds....
+ if (BLERestartTasmota){
+ BLERestartTasmota--;
+ // 2 seconds to go, post to BLE topic on MQTT our reason
+ if (BLERestartTasmota == 2){
+ if (!BLERestartTasmotaReason) BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_UNKNOWN;
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("{\"reboot\":\"%s\"}"), BLERestartTasmotaReason);
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE Failure! Restarting Tasmota in %d seconds because %s"), BLERestartTasmota, BLERestartTasmotaReason);
+ }
+
+ if (!BLERestartTasmota){
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE Failure! Restarting Tasmota because %s"), BLERestartTasmotaReason);
+ // just a normal restart
+ TasmotaGlobal.restart_flag = 1;
+ }
+ }
+
+ if (BLERestartBLEReason){ // just use the ptr as the trigger to send MQTT
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("{\"blerestart\":\"%s\"}"), BLERestartBLEReason);
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE Failure! Restarting BLE Stack because %s"), BLERestartBLEReason);
+ BLERestartBLEReason = nullptr;
+ }
+
+
+ BLE_ESP32::mainThreadBLETimeouts();
+ if (!BLEMasterEnable){
+ return;
+ }
+
+}
+
+
+
+
+
+/*********************************************************************************************\
+ * Operations queue functions - all to do with read/write and notify for a device
+\*********************************************************************************************/
+
+// this retrievs the next operation from the passed list, and removes it from the list.
+// or returns null if none.
+generic_sensor_t* nextOperation(std::deque *ops){
+ generic_sensor_t* op = nullptr;
+ if (ops->size()){
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLENExtOp");
+ op = (*ops)[0];
+ ops->pop_front();
+ }
+ return op;
+}
+
+// this adds an operation to the end of passed list.
+// it also sets the op pointer passed to null.
+int addOperation(std::deque *ops, generic_sensor_t** op){
+ int res = 0;
+ {
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEAddOp");
+ if (ops->size() < 10){
+ ops->push_back(*op);
+ *op = nullptr;
+ res = 1;
+ }
+ }
+ if (res){
+ //AddLog_P(LOG_LEVEL_DEBUG,PSTR("added operation"));
+ } else {
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE op - no room"));
+ }
+ return res;
+}
+
+
+int newOperation(BLE_ESP32::generic_sensor_t** op){
+ if (!op) {
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE op inv in newOperation"));
+ return 0;
+ }
+
+ BLE_ESP32::generic_sensor_t *o = new BLE_ESP32::generic_sensor_t;
+
+ // clear to zeros, but not the NimBLE classes
+ o->state = 0;
+ o->opid = 0; // incrementing id so we can find them
+ o->notifytimer = 0L;
+ //uint8_t writeRead[MAX_BLE_DATA_LEN];
+ o->writelen = 0;
+ //uint8_t dataRead[MAX_BLE_DATA_LEN];
+ o->readlen = 0;
+ o->readtruncated = 0;
+ //uint8_t dataNotify[MAX_BLE_DATA_LEN];
+ o->notifylen = 0;
+ o->notifytruncated = 0;
+ o->readmodifywritecallback = nullptr; // READ_CALLBACK function, used by external drivers
+ o->completecallback = nullptr; // OPCOMPLETE_CALLBACK function, used by external drivers
+ o->context = nullptr; // opaque context, used by external drivers, or can be set to a long for MQTT
+
+ (*op) = o;
+ return 1;
+}
+
+int freeOperation(BLE_ESP32::generic_sensor_t** op){
+ if (!op) {
+ return 0;
+ }
+
+ delete (*op);
+ (*op) = nullptr;
+ return 1;
+}
+
+
+int extQueueOperation(BLE_ESP32::generic_sensor_t** op){
+ if (!op || !(*op)) {
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: op invalid"));
+ return 0;
+ }
+ (*op)->state = GEN_STATE_START; // trigger request later
+ (*op)->opid = lastopid++;
+
+ int res = addOperation(&queuedOperations, op);
+ if (!res){
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("extQueueOperation: op added id %d failed"), (lastopid-1));
+ }
+ return res;
+}
+
+
+/*********************************************************************************************\
+ * BLE Name alisaes
+\*********************************************************************************************/
+#ifdef BLE_ESP32_ALIASES
+int addAlias( uint8_t *addr, char *name){
+ if (!addr || !name){
+ return 0;
+ }
+
+ int count = aliases.size();
+ // replace name for existing address
+ for (int i = 0; i < count; i++){
+ if (!memcmp(aliases[i]->addr, addr, 6)){
+ strncpy(aliases[i]->name, name, sizeof(aliases[i]->name));
+ aliases[i]->name[sizeof(aliases[i]->name)-1] = 0;
+ return 2;
+ }
+ }
+
+ // replace addr for existing name
+ for (int i = 0; i < count; i++){
+ if (!strcmp(aliases[i]->name, name)){
+ memcpy(aliases[i]->addr, addr, 6);
+ return 2;
+ }
+ }
+
+ BLE_ESP32::ble_alias_t *alias = new BLE_ESP32::ble_alias_t;
+ memcpy(alias->addr, addr, 6);
+ strncpy(alias->name, name, sizeof(alias->name));
+ alias->name[sizeof(alias->name)-1] = 0;
+ aliases.push_back(alias);
+ return 1;
+}
+#endif
+
+/**
+ * @brief Remove all colons from null terminated char array
+ *
+ * @param _string Typically representing a MAC-address like AA:BB:CC:DD:EE:FF
+ */
+void stripColon(char* _string){
+ uint32_t _length = strlen(_string);
+ uint32_t _index = 0;
+ while (_index < _length) {
+ char c = _string[_index];
+ if(c==':'){
+ memmove(_string+_index,_string+_index+1,_length-_index);
+ }
+ _index++;
+ }
+ _string[_index] = 0;
+}
+
+
+//////////////////////////////////////////////////
+// use this for address interpretaton from string
+// it looks for aliases, and converts AABBCCDDEEFF and AA:BB:CC:DD:EE:FF
+int getAddr(uint8_t *dest, char *src){
+ if (!dest || !src){
+ return 0;
+ }
+#ifdef BLE_ESP32_ALIASES
+ for (int i = 0; i < aliases.size(); i++){
+ if (!strcmp(aliases[i]->name, src)){
+ memcpy(dest, aliases[i]->addr, 6);
+ return 2; //was an alias
+ }
+ }
+#endif
+
+ char tmp[12+5+1];
+ if (strlen(src) == 12+5){
+ strcpy(tmp, src);
+ stripColon(tmp);
+ src = tmp;
+ }
+
+ int len = fromHex(dest, src, 6);
+ if (len == 6){
+ return 1;
+ }
+ // not found
+ return 0;
+}
+
+static const char *noAlias = PSTR("");
+
+////////////////////////////////////////////
+// use to display the alias name if required
+const char *getAlias(uint8_t *addr){
+ if (!addr){
+ return noAlias;
+ }
+#ifdef BLE_ESP32_ALIASES
+ for (int i = 0; i < aliases.size(); i++){
+ if (!memcmp(aliases[i]->addr, addr, 6)){
+ return aliases[i]->name; //was an alias
+ }
+ }
+#endif
+ return noAlias;
+}
+
+
+/*********************************************************************************************\
+ * Highest level BLE task control functions
+\*********************************************************************************************/
+
+static int StartBLE(void) {
+ if (BLEStop != 1){
+ BLE_ESP32::BLEStartOperationTask();
+ return 1;
+ }
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("StartBLE - wait as BLEStop==1"));
+
+ return 0;
+}
+
+static int StopBLE(void){
+ if (BLERunning){
+ if (BLEStop != 1){
+ BLEStop = 1;
+ AddLog_P(LOG_LEVEL_INFO,PSTR("StopBLE - BLEStop->1"));
+ BLEStopAt = esp_timer_get_time();
+ // give a little time for it to stop.
+ vTaskDelay(1000/ portTICK_PERIOD_MS);
+ return 1;
+ }
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("StopBLE - wait as BLEStop==1"));
+ return 0;
+ } else {
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("StopBLE - was not running"));
+ return 1;
+ }
+}
+
+
+/*********************************************************************************************\
+ * Commands
+\*********************************************************************************************/
+
+static void CmndBLEPeriod(void) {
+ //ResponseCmndNumber(BLE.period);
+ ResponseCmndDone();
+}
+
+
+//////////////////////////////////////////////////////////////
+// Determine what to do with advertismaents
+// BLEAdv0 -> suppress MQTT about devices found
+// BLEAdv1 -> send MQTT about devices found after each scan
+void CmndBLEAdv(void){
+ switch(XdrvMailbox.index){
+ case 0:
+ BLEAdvertMode = BLE_ESP32::BLE_NO_ADV_SEND;
+ break;
+ case 1:
+ BLEAdvertMode = BLE_ESP32::BLE_ADV_TELE;
+ break;
+ /*case 2:
+ BLEAdvertMode = BLE_ADV_ALL;
+ break;*/
+ case 3:
+ break;
+ }
+
+ ResponseCmndNumber(BLEAdvertMode);
+}
+
+
+//////////////////////////////////////////////////////////////
+// Determine what to do with advertismaents
+// BLEAdv0 -> suppress MQTT about devices found
+// BLEAdv1 -> send MQTT about devices found after each scan
+void CmndBLEDebug(void){
+ BLEDebugMode = XdrvMailbox.index;
+ ResponseCmndNumber(BLEDebugMode);
+}
+
+void CmndBLEDevices(void){
+ switch(XdrvMailbox.index){
+ case 0:{
+ // clear devices delete
+ deleteSeenDevices();
+ } break;
+ case 1:{
+ BLEPublishDevices = 2; // mqtt publish as 'STAT'
+ } break;
+ }
+ ResponseCmndDone();
+}
+
+void CmndBLEMaxAge(void){
+ switch(XdrvMailbox.index){
+ case 1:{
+ if (XdrvMailbox.data_len > 0) {
+ BLEMaxAge = XdrvMailbox.payload;
+ }
+ } break;
+ }
+ ResponseCmndIdxNumber(BLEMaxAge);
+ if (BLEMaxAge) deleteSeenDevices(BLEMaxAge);
+}
+
+void CmndBLEAddrFilter(void){
+ switch(XdrvMailbox.index){
+ case 1:{
+ if (XdrvMailbox.data_len > 0) {
+ BLEAddressFilter = XdrvMailbox.payload;
+ }
+ } break;
+ }
+ ResponseCmndIdxNumber(BLEAddressFilter);
+}
+
+
+//////////////////////////////////////////////////////////////
+// Scan options
+// BLEScan0 -> do a scan now if BLEMode == BLEModeScanByCommand
+// BLEScan0 -> do a scan now if BLEMode == BLEModeScanByCommand for timesec seconds
+// BLEScan1 0 -> Scans are passive
+// BLEScan1 1 -> Scans are active
+// more options could be added...
+void CmndBLEScan(void){
+ switch(XdrvMailbox.index){
+ case 0:{
+ if (XdrvMailbox.data_len > 0) {
+ BLEScanActiveMode = XdrvMailbox.payload;
+ ResponseCmndNumber(BLEScanActiveMode);
+ } else {
+ ResponseCmndChar("Invalid");
+ }
+ } break;
+
+ case 1: // do a manual scan now
+ switch (BLEMode){
+ case BLEModeScanByCommand: {
+ int time = 20;
+ if (XdrvMailbox.data_len > 0) {
+ time = XdrvMailbox.payload;
+ if (time < 2) time = 2;
+ if (time > 40) time = 40;
+ }
+ BLETriggerScan = time;
+ ResponseCmndNumber(time); // -ve for fail for a few reasons
+ } break;
+ case BLEModeDisabled:
+ ResponseCmndChar("BLEDisabled");
+ break;
+ case BLEModeRegularScan:
+ ResponseCmndChar("BLEActive");
+ break;
+ }
+ break;
+ default:
+ ResponseCmndChar("Invalid");
+ break;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////
+// Determine what to do with advertismaents
+// BLEMode0 -> kill BLE completely
+// BLEMode1 -> start BLE, scan by command
+// BLEMode2 -> start BLE, regular scan
+void CmndBLEMode(void){
+ int val = XdrvMailbox.index;
+ if (XdrvMailbox.data_len > 0) {
+ val = XdrvMailbox.payload;
+ }
+
+ switch(val){
+ case BLEModeDisabled:{
+ if (BLEMode != BLEModeDisabled){
+ BLEMode = BLEModeDisabled;
+ StopBLE();
+ ResponseCmndChar("StoppingBLE");
+ } else {
+ ResponseCmndChar("Disabled");
+ }
+ } break;
+ case BLEModeScanByCommand:{
+ uint64_t now = esp_timer_get_time();
+ switch(BLEMode){
+ // when changing from regular to by command,
+ // stop the scan next loop
+ case BLEModeRegularScan: {
+ BLEMode = BLEModeScanByCommand;
+ BLEStopScan = 1;
+ ResponseCmndChar("BLEStopScan");
+ } break;
+ case BLEModeDisabled: {
+ BLEMode = BLEModeScanByCommand;
+ StartBLE();
+ ResponseCmndChar("StartingBLE");
+ } break;
+ case BLEModeScanByCommand:{
+ ResponseCmndChar("BLERunning");
+ } break;
+ }
+ BLEScanLastAdvertismentAt = now; // note the time of the last advertisment
+ } break;
+ case BLEModeRegularScan:{
+ uint64_t now = esp_timer_get_time();
+ switch(BLEMode){
+ case BLEModeDisabled: {
+ BLEMode = BLEModeRegularScan;
+ StartBLE();
+ ResponseCmndChar("StartingBLE");
+ } break;
+ case BLEModeScanByCommand:{
+ BLEMode = BLEModeRegularScan;
+ ResponseCmndChar("BLEEnableScan");
+ } break;
+ case BLEModeRegularScan:{
+ BLEMode = BLEModeRegularScan;
+ ResponseCmndChar("BLERunning");
+ } break;
+ }
+ BLEScanLastAdvertismentAt = now; // note the time of the last advertisment
+ } break;
+ default:
+ ResponseCmndChar("InvalidIndex");
+ break;
+ }
+}
+
+
+//////////////////////////////////////////
+// get more drtails for a single MAC address
+// BLEDetails0 -> don;t send me anything
+// BLEDetails1 -> send me details for once
+// BLEDetails2 -> send me details for every advert if possible
+// example: BLEDetails1 001A22092C9A
+// details look like:
+// MQT: tele/tasmota_esp32/BLE = {"details":{"mac":"001A22092C9A","p":"0C0943432D52542D4D2D424C450CFF0000000000000000000000"}}
+// and incliude mac, complete advert payload, plus optional ,"lost":true if an advert was not captured because MQTT we already
+// had one waiting to be sent
+void CmndBLEDetails(void){
+ switch(XdrvMailbox.index){
+ case 0:
+ BLEDetailsRequest = 0;
+ ResponseCmndNumber(BLEDetailsRequest);
+ break;
+
+ case 1:
+ case 2:{
+ BLEDetailsRequest = 0;
+ if (getAddr(BLEDetailsMac, XdrvMailbox.data)){
+ BLEDetailsRequest = XdrvMailbox.index;
+ ResponseCmndIdxChar(XdrvMailbox.data);
+ } else {
+ ResponseCmndChar("InvalidMac");
+ }
+ } break;
+
+ case 3:{
+ BLEDetailsRequest = XdrvMailbox.index;
+ ResponseCmndNumber(BLEDetailsRequest);
+ } break;
+
+ default:
+ ResponseCmndChar("InvalidIndex");
+ break;
+ }
+}
+
+
+void CmndBLEAlias(void){
+#ifdef BLE_ESP32_ALIASES
+ int op = XdrvMailbox.index;
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("Alias %d %s"), op, XdrvMailbox.data);
+
+ int res = -1;
+ switch(op){
+ case 0:
+ case 1:{
+ char *p = strtok(XdrvMailbox.data, " ,=");
+ bool trigger = false;
+ int added = 0;
+
+ do {
+ if (!p || !(*p)){
+ break;
+ }
+
+ uint8_t addr[6];
+ char *mac = p;
+ int len = fromHex(addr, p, sizeof(addr));
+ if (len != 6){
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Alias invalid mac %s"), p);
+ ResponseCmndChar("invalidmac");
+ return;
+ }
+
+ p = strtok(nullptr, " ,=");
+ char *name = p;
+ if (!p || !(*p)){
+ int i = 0;
+ for (i = 0; i < aliases.size(); i++){
+ BLE_ESP32::ble_alias_t *alias = aliases[i];
+ if (!memcmp(alias->addr, addr, 6)){
+ aliases.erase(aliases.begin() + i);
+ BLEAliasListResp();
+ return;
+ }
+ }
+ ResponseCmndChar("invalidmac");
+ return;
+ }
+
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Add Alias mac %s = name %s"), mac, p);
+ if (addAlias( addr, name )){
+ added++;
+ }
+ p = strtok(nullptr, " ,=");
+ } while (p);
+
+ if (added){
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("Added %d Aliases"), added);
+ BLEAliasListResp();
+ } else {
+ BLEAliasListResp();
+ }
+ return;
+ } break;
+ case 2:{ // clear
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLEAlias clearing %d"), aliases.size());
+ for (int i = aliases.size()-1; i >= 0; i--){
+ BLE_ESP32::ble_alias_t *alias = aliases[i];
+ aliases.pop_back();
+ delete alias;
+ }
+ BLEAliasListResp();
+ return;
+ } break;
+ }
+ ResponseCmndChar("invalididx");
+#endif
+}
+
+
+// SET the BLE name for a device -
+// uses s:1800 c:2a00 and writes name to DEVICE
+void CmndBLEName(void) {
+ char *p = strtok(XdrvMailbox.data, " ");
+
+ if (!p || !(*p)){
+ ResponseCmndIdxChar(PSTR("invalid"));
+ return;
+ }
+
+ uint8_t addrbin[6];
+ int addrres = BLE_ESP32::getAddr(addrbin, p);
+ NimBLEAddress addr(addrbin);
+
+ if (addrres){
+ if (addrres == 2){
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE addr used alias: %s"), p);
+ }
+
+//#ifdef EQ3_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE cmd addr: %s -> %s"), p, addr.toString().c_str());
+//#endif
+ } else {
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE addr invalid: %s"), p);
+ ResponseCmndIdxChar(PSTR("invalidaddr"));
+ return;
+ }
+
+ BLE_ESP32::generic_sensor_t *op = nullptr;
+ // ALWAYS use this function to create a new one.
+ int res = BLE_ESP32::newOperation(&op);
+ if (!res){
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Can't get a newOperation"));
+ ResponseCmndChar(PSTR("FAIL"));
+ return;
+ } else {
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("got a newOperation from BLE"));
+ }
+
+ op->addr = addr;
+ op->serviceUUID = NimBLEUUID("1800");
+ op->characteristicUUID = NimBLEUUID("2A00");
+
+ // get next part of cmd
+ char *name = strtok(nullptr, " ");
+ bool write = false;
+ if (name && *name){
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("write name %s"), name);
+ op->writelen = strlen(name);
+ memcpy(op->dataToWrite, name, op->writelen);
+ write = true;
+ } else {
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("read name"));
+ op->readlen = 1;
+ }
+
+ res = BLE_ESP32::extQueueOperation(&op);
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("queue res %d"), res);
+ if (!res){
+ // if it fails to add to the queue, do please delete it
+ BLE_ESP32::freeOperation(&op);
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Failed to queue new operation - deleted"));
+ ResponseCmndChar(PSTR("QUEUEFAIL"));
+ return;
+ }
+
+ if (write){
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("BLE: will write name"));
+ } else {
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("BLE: will read name"));
+ }
+ ResponseCmndDone();
+ return;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// Command to cause BLE read/write/notify operations to be run.
+//////////////////////////////////////////////////////////////////////////
+
+// we expect BLEOp0 - poll state
+// we expect BLEOp1 m:MAC s:svc
+// we expect BLEOp2 trigger queue of op. return is opid
+
+// returns: Done|FailCreate|FailNoOp|FailQueue|InvalidIndex|
+
+// BLEop0/1/2 will cause an MQTT send of ops currently known.
+// on op complete/op fail, a MQTT send is triggered of all known ops, and the completed/failed op removed.
+
+// example:
+// BLEOp1 M:001A22092CDB s:3e135142-654f-9090-134a-a6ff5bb77046 c:3fa4585a-ce4a-3bad-db4b-b8df8179ea09 w:03 n:d0e8434d-cd29-0996-af41-6c90f4e0eb2a go
+// requests write of 03, and request wait for notify.
+
+// You may queue up operations. they are currently processed serially.
+void CmndBLEOperation(void){
+
+ int op = XdrvMailbox.index;
+
+ //AddLog_P(LOG_LEVEL_INFO,PSTR("op %d"), op);
+
+ int res = -1;
+
+ // if in progress, only op 0 or 11 are allowed
+ switch(op) {
+ case 0:
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("preview"));
+#endif
+ BLEPostMQTTTrigger = 1;
+ break;
+ case 1: {
+ if (prepOperation){
+ BLE_ESP32::freeOperation(&prepOperation);
+ }
+ int opres = BLE_ESP32::newOperation(&prepOperation);
+ if (!opres){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Could not create new operation"));
+#endif
+ ResponseCmndChar("FailCreate");
+ return;
+ }
+ // expect m:MAC s:svc
+ // < > are optional
+ char *p = strtok(XdrvMailbox.data, " ,");
+ bool trigger = false;
+
+ while (p){
+ switch(*p | 0x20){
+ case 'm':{
+ uint8_t addr[6];
+ if (getAddr(addr, p+2)){
+ prepOperation->addr = NimBLEAddress(addr);
+ } else {
+ prepOperation->addr = NimBLEAddress();
+ }
+ } break;
+ case 's':{
+ prepOperation->serviceUUID = NimBLEUUID(p+2);
+ } break;
+ case 'c':
+ prepOperation->characteristicUUID = NimBLEUUID(p+2);
+ //strncpy(prepOperation->characteristicStr, p+2, sizeof(prepOperation->characteristicStr)-1);
+ break;
+ case 'n':
+ prepOperation->notificationCharacteristicUUID = NimBLEUUID(p+2);
+ //strncpy(prepOperation->notificationCharacteristicStr, p+2, sizeof(prepOperation->notificationCharacteristicStr)-1);
+ break;
+ case 'w':
+ prepOperation->writelen = fromHex(prepOperation->dataToWrite, p+2, sizeof(prepOperation->dataToWrite));
+ break;
+ case 'u': // 'unique' context for this request
+ prepOperation->context = (void *)atoi(p+2);
+ break;
+ case 'r':
+ prepOperation->readlen = 1;
+ break;
+ case 'g':
+ if ((*(p+1))|0x20 == 'o'){
+ trigger = true;
+ }
+ break;
+ }
+
+ p = strtok(nullptr, " ,");
+ }
+
+ if (trigger){
+ int u = (int)prepOperation->context;
+ int opres = BLE_ESP32::extQueueOperation(&prepOperation);
+ if (!opres){
+ // NOTE: prepOperation will NOT have been deleted.
+ // this means you could retry with another BLEOp10.
+ // it WOULD be deleted if you sent another BELOP1
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Could not queue new operation"));
+#endif
+ ResponseCmndChar("FailQueue");
+ return;
+ } else {
+ // NOTE: prepOperation has been set to null if we queued sucessfully.
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("Operations queued:%d"), queuedOperations.size());
+#endif
+ char temp[40];
+ sprintf(temp, "{\"opid\":%d,\"u\":%d}", lastopid-1, u);
+ Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, temp);
+ // don't do this here... overwrites response
+ //BLE_ESP32::BLEPostMQTT(false);
+ return;
+ }
+ } else {
+ ResponseCmndChar("Prepared");
+ //BLE_ESP32::BLEPostMQTT(false);
+ return;
+ }
+ } break;
+
+ case 2: {
+ if (!prepOperation) {
+ ResponseCmndChar("FailNoOp");
+ return;
+ }
+ //prepOperation->requestType = atoi(XdrvMailbox.data);
+ int u = (int)prepOperation->context;
+ int opres = BLE_ESP32::extQueueOperation(&prepOperation);
+ if (!opres){
+ // NOTE: prepOperation will NOT have been deleted.
+ // this means you could retry with another BLEOp10.
+ // it WOULD be deleted if you sent another BELOP1
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("Could not queue new operation"));
+#endif
+ ResponseCmndChar("FailQueue");
+ } else {
+ // NOTE: prepOperation has been set to null if we queued sucessfully.
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("Operations queued:%d"), queuedOperations.size());
+#endif
+ char temp[40];
+ sprintf(temp, "{\"opid\":%d,\"u\":%d}", lastopid-1, u);
+ Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, temp);
+ }
+ return;
+ } break;
+
+ default:
+ ResponseCmndChar("InvalidIndex");
+ return;
+ }
+
+ ResponseCmndDone();
+ return;
+}
+
+
+/*********************************************************************************************\
+ * Presentation
+\*********************************************************************************************/
+static void BLEPostMQTTSeenDevices(int type) {
+ int remains = 0;
+ nextSeenDev = 0;
+
+ memset(TasmotaGlobal.mqtt_data, 0, sizeof(TasmotaGlobal.mqtt_data));
+ ResponseTime_P(PSTR(""));
+ int timelen = strlen(TasmotaGlobal.mqtt_data);
+ char *dest = TasmotaGlobal.mqtt_data + timelen;
+ int maxlen = (sizeof(TasmotaGlobal.mqtt_data)-20) - timelen;
+
+// if (!TasmotaGlobal.ota_state_flag){
+ do {
+ remains = getSeenDevicesToJson(dest, maxlen);
+ // no retain - this is present devices, not historic
+ if (type == 1){
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), 0);
+ } else {
+ MqttPublishPrefixTopic_P(STAT, PSTR("BLE"), 0);
+ }
+ } while (remains);
+// }
+}
+
+static void BLEPostMQTT(bool onlycompleted) {
+// if (TasmotaGlobal.ota_state_flag) return;
+
+
+ if (prepOperation || completedOperations.size() || queuedOperations.size() || currentOperations.size()){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("some to show"));
+#endif
+ if (prepOperation && !onlycompleted){
+ std::string out = BLETriggerResponse(prepOperation);
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str());
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("prep sent %s"), out.c_str());
+#endif
+ }
+
+ if (queuedOperations.size() && !onlycompleted){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("queued %d"), queuedOperations.size());
+#endif
+ for (int i = 0; i < queuedOperations.size(); i++){
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEPost1");
+
+ generic_sensor_t *toSend = queuedOperations[i];
+ if (!toSend) {
+ break;
+ } else {
+ std::string out = BLETriggerResponse(toSend);
+ localmutex.give();
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str());
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("queued %d sent %s"), i, out.c_str());
+#endif
+ //break;
+ }
+ }
+ }
+
+ if (currentOperations.size() && !onlycompleted){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("current %d"), currentOperations.size());
+#endif
+ for (int i = 0; i < currentOperations.size(); i++){
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEPost2");
+ generic_sensor_t *toSend = currentOperations[i];
+ if (!toSend) {
+ break;
+ } else {
+ std::string out = BLETriggerResponse(toSend);
+ localmutex.give();
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str());
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("curr %d sent %s"), i, out.c_str());
+#endif
+ //break;
+ }
+ }
+ }
+
+ if (completedOperations.size()){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("completed %d"), completedOperations.size());
+#endif
+ do {
+ generic_sensor_t *toSend = nextOperation(&completedOperations);
+ if (!toSend) {
+ break; // break from while loop
+ } else {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE:completedOperation removed"));
+#endif
+ std::string out = BLETriggerResponse(toSend);
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str());
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+ // we alreayd removed this from the queues, so now delete
+ delete toSend;
+ //break;
+ }
+ //break;
+ } while (1);
+ }
+ } else {
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("{\"BLEOperation\":{}}"));
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+ }
+}
+
+static void mainThreadBLETimeouts() {
+ uint64_t now = esp_timer_get_time();
+
+ if (!BLERunning){
+ BLELastLoopTime = now; // initialise the time of the last advertisment
+ BLEScanLastAdvertismentAt = now; // initialise the time of the last advertisment
+ return;
+ }
+
+ if (BLEStop == 1){
+ if (BLEStopAt + 30L*1000L*1000L < now){ // if asked to stop > 30s ago...
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Stop Timeout - restart Tasmota"));
+ BLERestartTasmota = 2;
+ BLEStopAt = now;
+ }
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Awaiting BLEStop"));
+ return;
+ }
+
+ // if no adverts for 120s, and BLE is running, retsart NimBLE.
+ // belt and braces....
+ uint64_t adTimeout = ((uint64_t)BLEMaxTimeBetweenAdverts)*1000L*1000L;
+ if (BLEScanLastAdvertismentAt + adTimeout < now){
+ BLEScanLastAdvertismentAt = now; // initialise the time of the last advertisment
+ BLERestartNimBLE = 1;
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: scan stall? no adverts > 120s, restart BLE"));
+
+ BLERestartBLEReason = BLE_RESTART_BLE_REASON_ADVERT_BLE_TIMEOUT;
+ }
+
+ // if stuck and have not done task for 120s, something is seriously wrong.
+ // restart Tasmota completely. (belt and braces)
+ uint64_t bleLoopTimeout = ((uint64_t)BLEMaxTaskLoopTime)*1000L*1000L;
+ if (BLELastLoopTime + bleLoopTimeout < now){
+ BLELastLoopTime = now; // initialise the time of the last advertisment
+ BLERestartTasmota = 10;
+ AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask stall > 120s, restart Tasmota in 10s"));
+ BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_BLE_LOOP_STALLED;
+ }
+}
+
+
+static void mainThreadOpCallbacks() {
+ if (completedOperations.size()){
+ //AddLog_P(LOG_LEVEL_INFO,PSTR("completed %d"), completedOperations.size());
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEMainCB");
+
+ // find this operation in currentOperations, and remove it.
+ // in reverse so we can erase them safely.
+ for (int i = completedOperations.size()-1; i >= 0 ; i--){
+ generic_sensor_t *op = completedOperations[i];
+
+ bool callbackres = false;
+
+ if (op->completecallback){
+ try {
+ OPCOMPLETE_CALLBACK *pFn = (OPCOMPLETE_CALLBACK *)(op->completecallback);
+ callbackres = pFn(op);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("op->completecallback %d"), callbackres);
+#endif
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("exception in op->completecallback"));
+#endif
+ }
+ }
+
+ if (!callbackres){
+ for (int i = 0; i < operationsCallbacks.size(); i++){
+ try {
+ OPCOMPLETE_CALLBACK *pFn = operationsCallbacks[i];
+ callbackres = pFn(op);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("operationsCallbacks %d %d"), i, callbackres);
+#endif
+ if (callbackres){
+ break; // this callback ate the op.
+ }
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog_P(LOG_LEVEL_ERROR,PSTR("exception in operationsCallbacks"));
+#endif
+ }
+ }
+ }
+
+ // if some callback told us not to send on MQTT, then remove from completed and delete the data
+ if (callbackres){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("callbackres true -> delete op"));
+#endif
+ completedOperations.erase(completedOperations.begin() + i);
+ delete op;
+ }
+ }
+ }
+}
+
+
+static void BLEShow(bool json)
+{
+ if (json){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("show json %d"),json);
+#endif
+ uint32_t totalCount = BLEAdvertisment.totalCount;
+ uint32_t deviceCount = seenDevices.size();
+
+ ResponseAppend_P(PSTR(",\"BLE\":{\"scans\":%u,\"adverts\":%u,\"devices\":%u,\"resets\":%u}"), BLEScanCount, totalCount, deviceCount, BLEResets);
+ }
+#ifdef USE_WEBSERVER
+ else {
+ //WSContentSend_PD(HTTP_MI32, i+1,stemp,MIBLEsensors.size());
+ }
+#endif // USE_WEBSERVER
+
+}
+
+/*void BLEAliasMqttList(){
+ ResponseTime_P(PSTR(",\"BLEAlias\":["));
+ for (int i = 0; i < aliases.size(); i++){
+ if (i){
+ ResponseAppend_P(PSTR(","));
+ }
+ char tmp[20];
+ ToHex_P(aliases[i]->addr,6,tmp,20,0);
+ ResponseAppend_P(PSTR("{\"%s\":\"%s\"}"), tmp, aliases[i]->name);
+ }
+ ResponseAppend_P(PSTR("]}"));
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+}*/
+
+void BLEAliasListResp(){
+ Response_P(PSTR("{\"BLEAlias\":{"));
+ for (int i = 0; i < aliases.size(); i++){
+ if (i){
+ ResponseAppend_P(PSTR(","));
+ }
+ char tmp[20];
+ ToHex_P(aliases[i]->addr,6,tmp,20,0);
+ ResponseAppend_P(PSTR("\"%s\":\"%s\""), tmp, aliases[i]->name);
+ }
+ ResponseAppend_P(PSTR("}}"));
+}
+
+
+static void BLEDiag()
+{
+ uint32_t totalCount = BLEAdvertisment.totalCount;
+ uint32_t deviceCount = seenDevices.size();
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE:scans:%u,advertisements:%u,devices:%u,resets:%u,BLEStop:%d,BLERunning:%d,BLERunningScan:%d,BLELoopCount:%u,BLEOpCount:%u"), BLEScanCount, totalCount, deviceCount, BLEResets, BLEStop, BLERunning, BLERunningScan, BLELoopCount, BLEOpCount);
+#endif
+}
+
+/**
+ * @brief creates a JSON representing a single operation.
+ *
+ */
+std::string BLETriggerResponse(generic_sensor_t *toSend){
+ char temp[100];
+ if (!toSend) return "";
+ std::string out = "{\"BLEOperation\":{\"opid\":\"";
+ sprintf(temp, "%d", toSend->opid); // note only 10 long!
+ out = out + temp;
+/* out = out + "\",\"state\":\"";
+ sprintf(t, "%d", toSend->state);
+ out = out + t;*/
+ out = out + "\",\"stat\":\"";
+ sprintf(temp, "%d", toSend->state);
+ out = out + temp;
+ out = out + "\",\"state\":\"";
+ out = out + getStateString(toSend->state);
+
+ if (toSend->addr != NimBLEAddress()){
+ out = out + "\",\"MAC\":\"";
+ uint8_t addrrev[6];
+ memcpy(addrrev, toSend->addr.getNative(), 6);
+ ReverseMAC(addrrev);
+ dump(temp, 13, addrrev, 6);
+ out = out + temp;
+ }
+ if (toSend->context){
+ out = out + "\",\"u\":\"";
+ sprintf(temp, "%d", (int32_t)toSend->context);
+ out = out + temp;
+ }
+ if (toSend->serviceUUID.bitSize()){
+ out = out + "\",\"svc\":\"";
+ out = out + toSend->serviceUUID.toString();
+ }
+ if (toSend->characteristicUUID.bitSize()){
+ out = out + "\",\"char\":\"";
+ out = out + toSend->characteristicUUID.toString();
+ }
+ if (toSend->notificationCharacteristicUUID.bitSize()){
+ out = out + "\",\"notifychar\":\"";
+ out = out + toSend->notificationCharacteristicUUID.toString();
+ }
+ out = out + "\"";
+ if (toSend->readlen){
+ dump(temp, 99, toSend->dataRead, toSend->readlen);
+ if (toSend->readtruncated){
+ strcat(temp, "+");
+ }
+ out = out + ",\"read\":\"";
+ out = out + temp;
+ out = out + "\"";
+ }
+ if (toSend->writelen){
+ dump(temp, 99, toSend->dataToWrite, toSend->writelen);
+ out = out + ",\"write\":\"";
+ out = out + temp;
+ out = out + "\"";
+ }
+ if (toSend->notifylen){
+ dump(temp, 99, toSend->dataNotify, toSend->notifylen);
+ if (toSend->notifytruncated){
+ strcat(temp, "+");
+ }
+ out = out + ",\"notify\":\"";
+ out = out + temp;
+ out = out + "\"";
+ }
+ out = out + "}}";
+ return out;
+}
+
+#ifdef USE_WEBSERVER
+
+#define WEB_HANDLE_BLE "ble"
+#define D_CONFIGURE_BLE "Configure BLE"
+#define D_BLE_PARAMETERS "Bluetooth Settings"
+#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
+#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_DEVICES "Devices Seen"
+
+const char HTTP_BTN_MENU_BLE[] PROGMEM =
+ "
";
+
+const char HTTP_FORM_BLE[] PROGMEM =
+ "