diff --git a/tasmota/xsns_61_MI_BLE.ino b/tasmota/xsns_61_MI_BLE.ino
deleted file mode 100644
index c07323684..000000000
--- a/tasmota/xsns_61_MI_BLE.ino
+++ /dev/null
@@ -1,858 +0,0 @@
-/*
- xsns_61_Ml_BLE.ino - MI-BLE-sensors via nrf24l01 support for Tasmota
-
- Copyright (C) 2020 Christian Baars 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 .
-
-
- --------------------------------------------------------------------------------------------
- Version yyyymmdd Action Description
- --------------------------------------------------------------------------------------------
-
-
- 0.9.1.0 20200117 integrate - Added support for the LYWSD02
- ---
- 0.9.0.0 20191127 started - further development by Christian Baars
- base - code base from cbm80amiga, floe, Dmitry.GR
- forked - from arendst/tasmota - https://github.com/arendst/Tasmota
-
-*/
-
-#ifdef USE_SPI
-#ifdef USE_NRF24
-#ifdef USE_MIBLE
-
-#ifdef DEBUG_TASMOTA_SENSOR
- #define MIBLE_LOG_BUFFER(x) MIBLEshowBuffer(x);
-#else
- #define MIBLE_LOG_BUFFER(x)
-#endif
-
-
-/*********************************************************************************************\
-* MIBLE
-* BLE-Sniffer/Bridge for MIJIA/XIAOMI Temperatur/Humidity-Sensor, Mi Flora
-*
-* Usage: Configure NRF24
-\*********************************************************************************************/
-
-#define XSNS_61 61
-
-#include
-
-const char MIBLESlaveFlora[] PROGMEM = "Flora";
-const char MIBLESlaveMJ_HT_V1[] PROGMEM = "MJ_HT_V1";
-const char MIBLESlaveLYWSD02[] PROGMEM = "LYWSD02";
-
-#pragma pack(1) // important!!
-struct MJ_HT_V1Header_t {// related to the payload
- uint8_t padding[3];
- uint8_t mesSize; // 3
- uint8_t padding2;
- uint16_t uuid; // 5,6 -> 0xFE95
- uint16_t type; // 7,8 -> 0x2050 MI-TH-V1
- uint8_t padding3[2];
- uint8_t counter; // 11 - counts up with every sent record
- uint8_t serial[6]; // 12 - 17
- uint8_t mode; // 18
- uint8_t padding5;
- uint8_t effectiveDataLength;
- };
-
-struct FlowerHeader_t { // related to the payload
- uint8_t padding[4];
- uint8_t padding2;
- uint16_t uuid; // 5,6 -> 0xFE95
- uint8_t mesSize;
- uint8_t padding22;
- uint16_t uuid2; // 9,10 -> 0xFE95
- uint16_t type; // 11,12 -> 0x7120 Flowercare
- uint8_t padding3[2];
- uint8_t counter; // 15 - counts up with every sent record
- uint8_t serial[6]; // 16 - 21
- uint8_t padding4; //22
- uint8_t mode; // 23
- };
-
-union floraPacket_t { // related to the whole 32-byte-packet/buffer
- struct {
- uint16_t idWord;
- uint8_t padding;
- uint8_t serial[6];
- uint8_t padding4;
- uint8_t mode;
- uint8_t valueTen;
- uint8_t effectiveDataLength; // 1
- uint16_t data;
- } T; // mode 04
- struct {
- uint16_t idWord;
- uint8_t padding;
- uint8_t serial[6];
- uint8_t padding4;
- uint8_t mode;
- uint8_t valueTen;
- uint8_t effectiveDataLength; // 3
- uint16_t data;
- uint8_t data2; // unknown meaning, maybe it is a real uint24_t (data with data2)
- } L; // mode 07
- struct {
- uint8_t padding[3];
- uint8_t serial[6];
- uint8_t padding4;
- uint8_t mode;
- uint8_t valueTen;
- uint8_t effectiveDataLength; // 1
- uint8_t data;
- } M; // mode 08
- struct {
- uint8_t padding[3];
- uint8_t serial[6];
- uint8_t padding4;
- uint8_t mode;
- uint8_t valueTen;
- uint8_t effectiveDataLength; // 2
- uint16_t data;
- } F; // mode 09
-};
-
-union MJ_HT_V1Packet_t { // related to the whole 32-byte-packet/buffer
- struct {
- uint16_t idWord;
- uint8_t padding;
- uint8_t serial[6];
- uint8_t mode;
- uint8_t valueTen;
- uint8_t effectiveDataLength; // 4
- uint16_t temp;
- uint16_t hum;
- } TH; // mode 0d
- struct {
- uint8_t padding[3];
- uint8_t serial[6];
- uint8_t mode;
- uint8_t valueTen;
- uint8_t effectiveDataLength; // 1
- uint8_t battery;
- } B; // mode 0a
- // We do NOT need the isolated T and H packet
-};
-
-union LYWSD02Packet_t { // related to the whole 32-byte-packet/buffer
- struct {
- uint16_t idWord;
- uint8_t padding;
- uint8_t serial[6];
- uint8_t padding4;
- uint8_t mode;
- uint8_t valueTen;
- uint8_t effectiveDataLength;
- uint16_t data;
- } TH; // mode 04 or 06
-};
-
-struct bleAdvPacket_t { // for nRF24L01 max 32 bytes = 2+6+24
- uint8_t pduType;
- uint8_t payloadSize;
- uint8_t mac[6];
- union {
- uint8_t payload[24];
- MJ_HT_V1Header_t header;
- FlowerHeader_t flowerHeader;
- struct {
- uint8_t padding[21];
- uint16_t temp;
- uint8_t hum_lb; // the high byte does not fit into the RX_buffer
- } TH; // mode 0d
- struct {
- uint8_t padding[21];
- uint16_t temp;
- } T; // mode 04
- struct {
- uint8_t padding[21];
- uint16_t hum;
- } H; // mode 06
- struct {
- uint8_t padding[21];
- uint8_t battery;
- } B; // mode 0a
- struct {
- uint8_t padding[2];
- uint8_t mode;
- uint16_t size; // 2
- uint16_t data;
- } F_T; // mode 04
- struct {
- uint8_t padding[2];
- uint8_t mode;
- uint16_t size; // 3
- uint16_t data;
- uint8_t data2; // unknown meaning, maybe it is a real uint24_t (data with data2)
- } F_L; // mode 07
- struct {
- uint8_t padding[2];
- uint8_t mode;
- uint16_t size; // 1
- uint8_t data;
- } F_M; // mode 08
- struct {
- uint8_t padding[2];
- uint8_t mode;
- uint16_t size; // 2
- uint16_t data;
- } F_F; // mode 09
- };
-};
-
-union FIFO_t{
- bleAdvPacket_t bleAdv;
- floraPacket_t floraPacket;
- MJ_HT_V1Packet_t MJ_HT_V1Packet;
- LYWSD02Packet_t LYWSD02Packet;
- uint8_t raw[32];
-};
-
-#pragma pack(0)
-
-struct {
- const uint8_t channel[3] = {37,38,39}; // BLE advertisement channel number
- const uint8_t frequency[3] = { 2,26,80}; // real frequency (2400+x MHz)
-
- uint16_t timer;
- uint8_t currentChan=0;
- FIFO_t buffer;
- uint8_t packetMode; // 0 - normal BLE-advertisements, 1 - special "flora"-packet, 2 - special "MJ_HT_V1"-packet
-
-#ifdef DEBUG_TASMOTA_SENSOR
- uint8_t streamBuffer[sizeof(buffer)]; // raw data stream bytes
- uint8_t lsfrBuffer[sizeof(buffer)]; // correpsonding lfsr-bytes for the buffer, probably only useful for a BLE-packet
-#endif // DEBUG_TASMOTA_SENSOR
-
-} MIBLE;
-
-struct mi_sensor_t{
- uint8_t type; //flora = 1; MI-HT_V1=2; LYWSD02=3
- uint8_t serial[6];
- uint8_t showedUp;
- union {
- struct {
- float temp;
- float moisture;
- float fertility;
- uint16_t lux;
- } Flora;
- struct {
- float temp;
- float hum;
- uint8_t bat;
- } MJ_HT_V1;
- struct {
- float temp;
- float hum;
- } LYWSD02;
- };
-};
-
-std::vector MIBLEsensors;
-
-/********************************************************************************************/
-
-
-bool MIBLEinitBLE(uint8_t _mode)
-{
- NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]);
- NRF24radio.setAutoAck(false);
- NRF24radio.setDataRate(RF24_1MBPS);
- NRF24radio.disableCRC();
- NRF24radio.setChannel( MIBLE.frequency[MIBLE.currentChan] );
- NRF24radio.setRetries(0,0);
- NRF24radio.setPALevel(RF24_PA_MIN); // we only receive
- NRF24radio.setAddressWidth(4);
- // NRF24radio.openReadingPipe(0,0x6B7D9171); // advertisement address: 0x8E89BED6 (bit-reversed -> 0x6B7D9171)
- // NRF24radio.openWritingPipe( 0x6B7D9171); // not used ATM
- NRF24radio.powerUp();
-
- if(NRF24radio.isChipConnected()){
- DEBUG_SENSOR_LOG(PSTR("MIBLE chip connected"));
- MIBLEchangePacketModeTo(_mode);
- return true;
- }
- DEBUG_SENSOR_LOG(PSTR("MIBLE chip NOT !!!! connected"));
- return false;
-}
-
-void MIBLEhopChannel()
-{
- MIBLE.currentChan++;
- if(MIBLE.currentChan >= sizeof(MIBLE.channel)) {
- MIBLE.currentChan = 0;
- }
- NRF24radio.setChannel( MIBLE.frequency[MIBLE.currentChan] );
-}
-
-/**
- * @brief Read out FIFO-buffer, swap buffer and whiten
- *
- * @return true - If something is in the buffer
- * @return false - Nothing is in the buffer
- */
-bool MIBLEreceivePacket(void)
-{
- if(!NRF24radio.available()) {
- return false;
- }
- while(NRF24radio.available()) {
- // static uint8_t _lsfr = 0; //-> for testing out suitable lsfr-start-values for yet unknown packets
- // _lsfr++;
- NRF24radio.read( &MIBLE.buffer, sizeof(MIBLE.buffer) );
-#ifdef DEBUG_TASMOTA_SENSOR
- memcpy(&MIBLE.streamBuffer, &MIBLE.buffer, sizeof(MIBLE.buffer));
-#endif // DEBUG_TASMOTA_SENSOR
- MIBLEswapbuf( sizeof(MIBLE.buffer) );
- // MIBLE_LOG_BUFFER();
- switch (MIBLE.packetMode) {
- case 0:
- MIBLEwhiten((uint8_t *)&MIBLE.buffer, sizeof(MIBLE.buffer), MIBLE.channel[MIBLE.currentChan] | 0x40);
- break;
- case 1:
- MIBLEwhiten((uint8_t *)&MIBLE.buffer, sizeof(MIBLE.buffer), 0x17); // "flora" mode 0x17
- break;
- case 2:
- MIBLEwhiten((uint8_t *)&MIBLE.buffer, sizeof(MIBLE.buffer), 0x72); // "MJ_HT_V1" mode 0x72
- break;
- case 3:
- MIBLEwhiten((uint8_t *)&MIBLE.buffer, sizeof(MIBLE.buffer), 0x17); // "LYWSD02" mode 0x17
- break;
- }
- // DEBUG_SENSOR_LOG(PSTR("MIBLE: LSFR:%x"),_lsfr);
- // if (_lsfr>254) _lsfr=0;
- }
- // DEBUG_SENSOR_LOG(PSTR("MIBLE: did read FIFO"));
- return true;
-}
-
-#ifdef DEBUG_TASMOTA_SENSOR
-void MIBLEshowBuffer(uint8_t (&buf)[32]){ // we use this only for the 32-byte-FIFO-buffer, so 32 is hardcoded
- // DEBUG_SENSOR_LOG(PSTR("MIBLE: Buffer: %c %c %c %c %c %c %c %c"
- // " %c %c %c %c %c %c %c %c"
- // " %c %c %c %c %c %c %c %c"
- // " %c %c %c %c %c %c %c %c")
- DEBUG_SENSOR_LOG(PSTR("MIBLE: Buffer: %02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x ")
- ,buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10],buf[11],
- buf[12],buf[13],buf[14],buf[15],buf[16],buf[17],buf[18],buf[19],buf[20],buf[21],buf[22],buf[23],
- buf[24],buf[25],buf[26],buf[27],buf[28],buf[29],buf[30],buf[31]
- );
-}
-#endif // DEBUG_TASMOTA_SENSOR
-
-/**
- * @brief change lsfrBuffer content to "wire bit order"
- *
- * @param len Buffer lenght (could be hardcoded to 32)
- */
-void MIBLEswapbuf(uint8_t len)
-{
- uint8_t* buf = (uint8_t*)&MIBLE.buffer;
- while(len--) {
- uint8_t a = *buf;
- uint8_t v = 0;
- if (a & 0x80) v |= 0x01;
- if (a & 0x40) v |= 0x02;
- if (a & 0x20) v |= 0x04;
- if (a & 0x10) v |= 0x08;
- if (a & 0x08) v |= 0x10;
- if (a & 0x04) v |= 0x20;
- if (a & 0x02) v |= 0x40;
- if (a & 0x01) v |= 0x80;
- *(buf++) = v;
- }
-}
-
-/**
- * @brief Whiten the packet buffer
- *
- * @param buf The packet buffer
- * @param len Lenght of the packet buffer
- * @param lfsr Start lsfr-byte
- */
-void MIBLEwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr)
-{
- while(len--) {
- uint8_t res = 0;
- // LFSR in "wire bit order"
- for (uint8_t i = 1; i; i <<= 1) {
- if (lfsr & 0x01) {
- lfsr ^= 0x88;
- res |= i;
- }
- lfsr >>= 1;
- }
- *(buf++) ^= res;
-#ifdef DEBUG_TASMOTA_SENSOR
- MIBLE.lsfrBuffer[31-len] = lfsr;
-#endif //DEBUG_TASMOTA_SENSOR
- }
-}
-
-
-/**
- * @brief Set packet mode and fitting PDU-type of the NRF24L01
- *
- * @param _mode The internal packet mode number
- */
-void MIBLEchangePacketModeTo(uint8_t _mode) {
- switch(_mode){
- case 0: // normal BLE advertisement
- NRF24radio.openReadingPipe(0,0x6B7D9171); // advertisement address: 0x8E89BED6 (bit-reversed -> 0x6B7D9171)
- break;
- case 1: // special flora packet
- NRF24radio.openReadingPipe(0,0xef3b8730); // 95 fe 71 20 -> flora, needs lfsr 0x17
- break;
- case 2: // special MJ_HT_V1 packet
- NRF24radio.openReadingPipe(0,0xdbcc0cd3); // 95 fe 50 20 -> MJ_HT_V1, needs lsfr 0x72
- break;
- case 3: // special LYWSD02 packet
- NRF24radio.openReadingPipe(0,0xef3b0730); // 95 fe 70 20 -> LYWSD02, needs lfsr 0x17
- break;
- }
- DEBUG_SENSOR_LOG(PSTR("MIBLE: Change Mode to %u"),_mode);
- MIBLE.timer = 0;
- MIBLE.packetMode = _mode;
-}
-
-/**
- * @brief Return the slot number of a known sensor or return create new sensor slot
- *
- * @param _serial BLE address of the sensor
- * @param _type Type number of the sensor
- * @return uint32_t Known or new slot in the sensors-vector
- */
-uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint8_t _type){
- DEBUG_SENSOR_LOG(PSTR("MIBLE: vector size %u"), MIBLEsensors.size());
- for(uint32_t i=0; i600){ // Change read mode every n/10 seconds
- if(++_purgeCounter>8){ // happens every 8 x 600 = 4800 seconds
- DEBUG_SENSOR_LOG(PSTR("MIBLE: check for FAKE sensors"));
- MIBLEpurgeFakeSensors();
- }
- DEBUG_SENSOR_LOG(PSTR("MIBLE: Change packet mode after 60 seconds, MIBLE.timer: %u"),MIBLE.timer);
- if (MIBLE.packetMode == 3){
- MIBLEinitBLE(1); // no real ble packets in release mode, otherwise for developing use 0
- }
- else {
- MIBLEinitBLE(MIBLE.packetMode + 1);
- }
- }
- MIBLE.timer++;
-
- if (!MIBLEreceivePacket()){
- MIBLEhopChannel();
- NRF24radio.startListening();
- return;
- }
-
- if(MIBLE.buffer.bleAdv.header.uuid==0xfe95){ // XIAOMI-BLE-Packet
- MIBLE_LOG_BUFFER(MIBLE.streamBuffer);
- MIBLE_LOG_BUFFER(MIBLE.lsfrBuffer);
- MIBLE_LOG_BUFFER(MIBLE.buffer.raw);
- DEBUG_SENSOR_LOG(PSTR("MIBLE: Type: %x"), MIBLE.buffer.bleAdv.header.type);
- switch(MIBLE.buffer.bleAdv.header.type){
- case 0x2050:
- DEBUG_SENSOR_LOG(PSTR("MIBLE: MJ_HT_V1 Packet"));
- break;
- case 0x1613:case 0x1614:case 0x1615:
- DEBUG_SENSOR_LOG(PSTR("MIBLE: Flora Packet"));
- break;
- default:
- DEBUG_SENSOR_LOG(PSTR("MIBLE: unknown Packet"));
- break;
- }
- }
- if (MIBLE.packetMode == 1){ // "flora" mode
- MIBLEhandleFloraPacket();
- }
- if (MIBLE.packetMode == 2){ // "MJ_HT_V1" mode
- MIBLEhandleMJ_HT_V1Packet();
- }
- if (MIBLE.packetMode == 3){ // "LYWSD02" mode
- MIBLEhandleLYWSD02Packet();
- }
-
- MIBLEhopChannel();
- NRF24radio.startListening();
-}
-
-const char HTTP_MIBLE_SERIAL[] PROGMEM =
- "{s}%s" " Address" "{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}";
-const char HTTP_BATTERY[] PROGMEM =
- "{s}%s" " Battery" "{m}%u%%{e}";
-
-const char HTTP_MIBLE_FLORA_DATA[] PROGMEM =
- "{s}%s" " Fertility" "{m}%sus/cm{e}";
-
-
-void MIBLEShow(bool json)
-{
- if (json) {
- if (!MIBLEsensors.size()) { return; }
-
- for (uint32_t i = 0; i < MIBLEsensors.size(); i++) {
- char slave[33];
- switch(MIBLEsensors.at(i).type){
- case 1:
- if(MIBLEsensors.at(i).showedUp < 3){
- DEBUG_SENSOR_LOG(PSTR("MIBLE: sensor not fully registered yet"));
- break;
- }
- sprintf_P(slave,"%s-%02x%02x%02x",MIBLESlaveFlora,MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[0]);
- char temperature_flora[33];
- dtostrfd(MIBLEsensors.at(i).Flora.temp, Settings.flag2.temperature_resolution, temperature_flora);
- char lux_flora[33];
- dtostrfd((float)MIBLEsensors.at(i).Flora.lux, 0, lux_flora);
- char moisture_flora[33];
- dtostrfd(MIBLEsensors.at(i).Flora.moisture, 0, moisture_flora);
- char fertility_flora[33];
- dtostrfd(MIBLEsensors.at(i).Flora.fertility, 0, fertility_flora);
- ResponseAppend_P(PSTR(",\"%s\":{"),slave);
- if(MIBLEsensors.at(i).Flora.temp!=-1000.0f){ // this is the error code -> no temperature
- ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature_flora);
- }
- if(MIBLEsensors.at(i).Flora.lux!=0xffff){ // this is the error code -> no temperature
- ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%s"), lux_flora);
- }
- if(MIBLEsensors.at(i).Flora.moisture!=-1000.0f){ // this is the error code -> no temperature
- ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%s"), moisture_flora);
- }
- if(MIBLEsensors.at(i).Flora.fertility!=-1000.0f){ // this is the error code -> no temperature
- ResponseAppend_P(PSTR(",\"Fertility\":%s"), fertility_flora);
- }
- ResponseAppend_P(PSTR("}"));
- break;
- case 2:
- if(MIBLEsensors.at(i).showedUp < 3){
- DEBUG_SENSOR_LOG(PSTR("MIBLE: sensor not fully registered yet"));
- break;
- }
- sprintf_P(slave,"%s-%02x%02x%02x",MIBLESlaveMJ_HT_V1,MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[0]);
- char temperature[33];
- dtostrfd(MIBLEsensors.at(i).MJ_HT_V1.temp, Settings.flag2.temperature_resolution, temperature);
- char humidity[33];
- dtostrfd(MIBLEsensors.at(i).MJ_HT_V1.hum, 1, humidity);
- ResponseAppend_P(PSTR(",\"%s\":{"),slave);
- if(MIBLEsensors.at(i).MJ_HT_V1.temp!=-1000.0f){ // this is the error code -> no temperature
- ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature);
- }
- if(MIBLEsensors.at(i).MJ_HT_V1.hum!=-1000.0f){ // this is the error code -> no temperature
- ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity);
- }
- if(MIBLEsensors.at(i).MJ_HT_V1.bat!=0xff){ // this is the error code -> no temperature
- ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors.at(i).MJ_HT_V1.bat);
- }
- ResponseAppend_P(PSTR("}"));
- break;
- case 3:
- if(MIBLEsensors.at(i).showedUp < 3){
- DEBUG_SENSOR_LOG(PSTR("MIBLE: sensor not fully registered yet"));
- break;
- }
- sprintf_P(slave,"%s-%02x%02x%02x",MIBLESlaveLYWSD02,MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[0]);
- dtostrfd(MIBLEsensors.at(i).LYWSD02.temp, Settings.flag2.temperature_resolution, temperature);
- dtostrfd(MIBLEsensors.at(i).LYWSD02.hum, 1, humidity);
- ResponseAppend_P(PSTR(",\"%s\":{"),slave);
- if(MIBLEsensors.at(i).LYWSD02.temp!=-1000.0f){ // this is the error code -> no temperature
- ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature);
- }
- if(MIBLEsensors.at(i).LYWSD02.hum!=-1000.0f){ // this is the error code -> no temperature
- ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity);
- }
- ResponseAppend_P(PSTR("}"));
- break;
- }
- }
-#ifdef USE_WEBSERVER
- } else {
- WSContentSend_PD(HTTP_NRF24, NRF24type, NRF24.chipType);
-
- if (!MIBLEsensors.size()) { return; }
-
- for (uint32_t i = 0; i < MIBLEsensors.size(); i++) {
- switch(MIBLEsensors.at(i).type){
- case 1:
- if(MIBLEsensors.at(i).showedUp < 3){
- DEBUG_SENSOR_LOG(PSTR("MIBLE: sensor not fully registered yet"));
- break;
- }
- char temperature_flora[33];
- dtostrfd(MIBLEsensors.at(i).Flora.temp, Settings.flag2.temperature_resolution, temperature_flora);
- char lux_flora[33];
- dtostrfd((float)MIBLEsensors.at(i).Flora.lux, 0, lux_flora);
- char fertility_flora[33];
- dtostrfd(MIBLEsensors.at(i).Flora.fertility, 0, fertility_flora);
-
- WSContentSend_PD(HTTP_MIBLE_SERIAL, F("Flora "), MIBLEsensors.at(i).serial[5], MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[0]);
- if(MIBLEsensors.at(i).Flora.temp!=-1000.0f){ // this is the error code -> no temperature
- WSContentSend_PD(HTTP_SNS_TEMP, MIBLESlaveFlora, temperature_flora, TempUnit());
- }
- if(MIBLEsensors.at(i).Flora.lux!=0xffff){ // this is the error code -> no temperature
- WSContentSend_PD(HTTP_SNS_ILLUMINANCE, MIBLESlaveFlora, MIBLEsensors.at(i).Flora.lux);
- }
- if(MIBLEsensors.at(i).Flora.moisture!=-1000.0f){ // this is the error code -> no temperature
- WSContentSend_PD(HTTP_SNS_MOISTURE, MIBLESlaveFlora, MIBLEsensors.at(i).Flora.moisture);
- }
- if(MIBLEsensors.at(i).Flora.fertility!=-1000.0f){ // this is the error code -> no temperature
- WSContentSend_PD(HTTP_MIBLE_FLORA_DATA, MIBLESlaveFlora, fertility_flora);
- }
- break;
- case 2:
- if(MIBLEsensors.at(i).showedUp < 3){
- DEBUG_SENSOR_LOG(PSTR("MIBLE: sensor not fully registered yet"));
- break;
- }
- char temperature[33];
- dtostrfd(MIBLEsensors.at(i).MJ_HT_V1.temp, Settings.flag2.temperature_resolution, temperature);
- char humidity[33];
- dtostrfd(MIBLEsensors.at(i).MJ_HT_V1.hum, 1, humidity);
-
- WSContentSend_PD(HTTP_MIBLE_SERIAL, MIBLESlaveMJ_HT_V1, MIBLEsensors.at(i).serial[5], MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[0]);
- if(MIBLEsensors.at(i).MJ_HT_V1.temp!=-1000.0f){
- WSContentSend_PD(HTTP_SNS_TEMP, MIBLESlaveMJ_HT_V1, temperature, TempUnit());
- }
- if(MIBLEsensors.at(i).MJ_HT_V1.hum!=-1.0f){
- WSContentSend_PD(HTTP_SNS_HUM, MIBLESlaveMJ_HT_V1, humidity);
- }
- if(MIBLEsensors.at(i).MJ_HT_V1.bat!=0xff){
- WSContentSend_PD(HTTP_BATTERY, MIBLESlaveMJ_HT_V1, MIBLEsensors.at(i).MJ_HT_V1.bat);
- }
- break;
- case 3:
- if(MIBLEsensors.at(i).showedUp < 3){
- DEBUG_SENSOR_LOG(PSTR("MIBLE: sensor not fully registered yet"));
- break;
- }
- dtostrfd(MIBLEsensors.at(i).LYWSD02.temp, Settings.flag2.temperature_resolution, temperature);
- dtostrfd(MIBLEsensors.at(i).LYWSD02.hum, 1, humidity);
-
- WSContentSend_PD(HTTP_MIBLE_SERIAL, MIBLESlaveLYWSD02, MIBLEsensors.at(i).serial[5], MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[0]);
- if(MIBLEsensors.at(i).LYWSD02.temp!=-1000.0f){
- WSContentSend_PD(HTTP_SNS_TEMP, MIBLESlaveLYWSD02, temperature, TempUnit());
- }
- if(MIBLEsensors.at(i).LYWSD02.hum!=-1.0f){
- WSContentSend_PD(HTTP_SNS_HUM, MIBLESlaveLYWSD02, humidity);
- }
- break;
- }
- }
- }
-#endif // USE_WEBSERVER
-}
-
-/*********************************************************************************************\
- * Interface
-\*********************************************************************************************/
-
-bool Xsns61(uint8_t function)
-{
- bool result = false;
-
- if (NRF24.chipType) {
- switch (function) {
- case FUNC_INIT:
- MIBLEinitBLE(1);
- AddLog_P2(LOG_LEVEL_INFO,PSTR("MIBLE: started"));
- break;
- case FUNC_EVERY_100_MSECOND:
- MIBLE_EVERY_100_MSECOND();
- break;
- case FUNC_JSON_APPEND:
- MIBLEShow(1);
- break;
-#ifdef USE_WEBSERVER
- case FUNC_WEB_SENSOR:
- MIBLEShow(0);
- break;
-#endif // USE_WEBSERVER
- }
- }
- return result;
-}
-
-#endif // USE_MIBLE
-#endif // USE_NRF24
-#endif // USE_SPI
-
diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/xsns_61_MI_NRF24.ino
new file mode 100644
index 000000000..8527c9749
--- /dev/null
+++ b/tasmota/xsns_61_MI_NRF24.ino
@@ -0,0 +1,859 @@
+/*
+ xsns_61_MI_NRF24.ino - MI-BLE-sensors via nrf24l01 support for Tasmota
+
+ Copyright (C) 2020 Christian Baars 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 .
+
+
+ --------------------------------------------------------------------------------------------
+ Version yyyymmdd Action Description
+ --------------------------------------------------------------------------------------------
+
+ 0.9.2.0 20200212 integrate - "backports" from MI-HM10, change reading pattern,
+ add missing PDU-types, renaming driver
+ ---
+ 0.9.1.0 20200117 integrate - Added support for the LYWSD02
+ ---
+ 0.9.0.0 20191127 started - further development by Christian Baars
+ base - code base from cbm80amiga, floe, Dmitry.GR
+ forked - from arendst/tasmota - https://github.com/arendst/Tasmota
+
+*/
+
+#ifdef USE_SPI
+#ifdef USE_NRF24
+#ifdef USE_MIBLE
+
+#ifdef DEBUG_TASMOTA_SENSOR
+ #define MINRF_LOG_BUFFER(x) MINRFshowBuffer(x);
+#else
+ #define MINRF_LOG_BUFFER(x)
+#endif
+
+
+/*********************************************************************************************\
+* MINRF
+* BLE-Sniffer/Bridge for MIJIA/XIAOMI Temperatur/Humidity-Sensor, Mi Flora, LYWSD02
+*
+* Usage: Configure NRF24
+\*********************************************************************************************/
+
+#define XSNS_61 61
+
+#include
+
+#define FLORA 1
+#define MJ_HT_V1 2
+#define LYWSD02 3
+#define LYWSD03 4
+
+uint8_t kMINRFSlaveID[4][3] = { 0xC4,0x7C,0x8D, // Flora
+ 0x58,0x2D,0x34, // MJ_HT_V1
+ 0xE7,0x2E,0x00, // LYWSD02
+ 0xA4,0xC1,0x38, // LYWSD03
+ };
+
+const char kMINRFSlaveType1[] PROGMEM = "Flora";
+const char kMINRFSlaveType2[] PROGMEM = "MJ_HT_V1";
+const char kMINRFSlaveType3[] PROGMEM = "LYWSD02";
+const char kMINRFSlaveType4[] PROGMEM = "LYWSD03";
+const char * kMINRFSlaveType[] PROGMEM = {kMINRFSlaveType1,kMINRFSlaveType2,kMINRFSlaveType3,kMINRFSlaveType4};
+
+// PDU's or different channels 37-39
+const uint32_t kMINRFFloPDU[3] = {0x3eaa857d,0xef3b8730,0x71da7b46};
+const uint32_t kMINRFMJPDU[3] = {0x4760cd66,0xdbcc0cd3,0x33048df5};
+const uint32_t kMINRFL2PDU[3] = {0x3eaa057d,0xef3b0730,0x71da7646}; // 1 and 3 unsure
+// const uint32_t kMINRFL3PDU[3] = {0x4760dd78,0xdbcc1ccd,0xffffffff}; //encrypted - 58 58
+const uint32_t kMINRFL3PDU[3] = {0x4760cb78,0xdbcc0acd,0x33048beb}; //unencrypted - 30 58
+
+// start-LSFR for different channels 37-39
+const uint8_t kMINRFlsfrList_A[3] = {0x4b,0x17,0x23}; // Flora, LYWSD02
+const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; // MJ_HT_V1, LYWSD03
+
+
+#pragma pack(1) // important!!
+struct MJ_HT_V1Header_t {// related to the payload
+ uint8_t padding[3];
+ uint8_t mesSize; // 3
+ uint8_t padding2;
+ uint16_t uuid; // 5,6 -> 0xFE95
+ uint16_t type; // 7,8 -> 0x2050 MI-TH-V1
+ uint8_t padding3[2];
+ uint8_t counter; // 11 - counts up with every sent record
+ uint8_t serial[6]; // 12 - 17
+ uint8_t mode; // 18
+ uint8_t padding5;
+ uint8_t effectiveDataLength;
+ };
+
+struct FlowerHeader_t { // related to the payload
+ uint8_t padding[4];
+ uint8_t padding2;
+ uint16_t uuid; // 5,6 -> 0xFE95
+ uint8_t mesSize;
+ uint8_t padding22;
+ uint16_t uuid2; // 9,10 -> 0xFE95
+ uint16_t type; // 11,12 -> 0x7120 Flowercare
+ uint8_t padding3[2];
+ uint8_t counter; // 15 - counts up with every sent record
+ uint8_t serial[6]; // 16 - 21
+ uint8_t padding4; //22
+ uint8_t mode; // 23
+ };
+
+union floraPacket_t { // related to the whole 32-byte-packet/buffer
+ struct {
+ uint16_t idWord;
+ uint8_t padding;
+ uint8_t serial[6];
+ uint8_t padding4;
+ uint8_t mode;
+ uint8_t valueTen;
+ uint8_t effectiveDataLength; // 1
+ uint16_t data;
+ } T; // mode 04
+ struct {
+ uint16_t idWord;
+ uint8_t padding;
+ uint8_t serial[6];
+ uint8_t padding4;
+ uint8_t mode;
+ uint8_t valueTen;
+ uint8_t effectiveDataLength; // 3
+ uint32_t data:24; // it is probably a real uint24_t
+ } L; // mode 07
+ struct {
+ uint8_t padding[3];
+ uint8_t serial[6];
+ uint8_t padding4;
+ uint8_t mode;
+ uint8_t valueTen;
+ uint8_t effectiveDataLength; // 1
+ uint8_t data;
+ } M; // mode 08
+ struct {
+ uint8_t padding[3];
+ uint8_t serial[6];
+ uint8_t padding4;
+ uint8_t mode;
+ uint8_t valueTen;
+ uint8_t effectiveDataLength; // 2
+ uint16_t data;
+ } F; // mode 09
+};
+
+union MJ_HT_V1Packet_t { // related to the whole 32-byte-packet/buffer
+ struct {
+ uint16_t idWord;
+ uint8_t padding;
+ uint8_t serial[6];
+ uint8_t mode;
+ uint8_t valueTen;
+ uint8_t effectiveDataLength; // 4
+ uint16_t temp;
+ uint16_t hum;
+ } TH; // mode 0d
+ struct {
+ uint8_t padding[3];
+ uint8_t serial[6];
+ uint8_t mode;
+ uint8_t valueTen;
+ uint8_t effectiveDataLength; // 1
+ uint8_t battery;
+ } B; // mode 0a
+ // We do NOT need the isolated T and H packet
+};
+
+union LYWSD02Packet_t { // related to the whole 32-byte-packet/buffer
+ struct {
+ uint16_t idWord;
+ uint8_t padding;
+ uint8_t serial[6];
+ uint8_t padding4;
+ uint8_t mode;
+ uint8_t valueTen;
+ uint8_t effectiveDataLength;
+ uint16_t data;
+ } TH; // mode 04 or 06
+};
+
+struct bleAdvPacket_t { // for nRF24L01 max 32 bytes = 2+6+24
+ uint8_t pduType;
+ uint8_t payloadSize;
+ uint8_t mac[6];
+ union {
+ uint8_t payload[24];
+ MJ_HT_V1Header_t header;
+ FlowerHeader_t flowerHeader;
+ struct {
+ uint8_t padding[21];
+ uint16_t temp;
+ uint8_t hum_lb; // the high byte does not fit into the RX_buffer
+ } TH; // mode 0d
+ struct {
+ uint8_t padding[21];
+ uint16_t temp;
+ } T; // mode 04
+ struct {
+ uint8_t padding[21];
+ uint16_t hum;
+ } H; // mode 06
+ struct {
+ uint8_t padding[21];
+ uint8_t battery;
+ } B; // mode 0a
+ struct {
+ uint8_t padding[2];
+ uint8_t mode;
+ uint16_t size; // 2
+ uint16_t data;
+ } F_T; // mode 04
+ struct {
+ uint8_t padding[2];
+ uint8_t mode;
+ uint16_t size; // 3
+ uint16_t data;
+ uint8_t data2; // unknown meaning, maybe it is a real uint24_t (data with data2)
+ } F_L; // mode 07
+ struct {
+ uint8_t padding[2];
+ uint8_t mode;
+ uint16_t size; // 1
+ uint8_t data;
+ } F_M; // mode 08
+ struct {
+ uint8_t padding[2];
+ uint8_t mode;
+ uint16_t size; // 2
+ uint16_t data;
+ } F_F; // mode 09
+ };
+};
+
+union FIFO_t{
+ bleAdvPacket_t bleAdv;
+ floraPacket_t floraPacket;
+ MJ_HT_V1Packet_t MJ_HT_V1Packet;
+ LYWSD02Packet_t LYWSD02Packet;
+ uint8_t raw[32];
+};
+
+#pragma pack(0)
+
+struct {
+ const uint8_t channel[3] = {37,38,39}; // BLE advertisement channel number
+ const uint8_t frequency[3] = { 2,26,80}; // real frequency (2400+x MHz)
+
+ uint16_t timer;
+ uint8_t currentChan=0;
+ FIFO_t buffer;
+ uint8_t packetMode; // 0 - normal BLE-advertisements, 1 - special "flora"-packet, 2 - special "MJ_HT_V1"-packet
+
+#ifdef DEBUG_TASMOTA_SENSOR
+ uint8_t streamBuffer[sizeof(buffer)]; // raw data stream bytes
+ uint8_t lsfrBuffer[sizeof(buffer)]; // correpsonding lfsr-bytes for the buffer, probably only useful for a BLE-packet
+#endif // DEBUG_TASMOTA_SENSOR
+
+} MINRF;
+
+struct mi_sensor_t{
+ uint8_t type; //Flora = 1; MJ_HT_V1=2; LYWSD02=3; LYWSD03=4
+ uint8_t serial[6];
+ uint8_t showedUp;
+ float temp; //Flora, MJ_HT_V1, LYWSD0x
+ union {
+ struct {
+ float moisture;
+ float fertility;
+ uint32_t lux;
+ }; // Flora
+ struct {
+ float hum;
+ uint8_t bat;
+ }; // MJ_HT_V1, LYWSD0x
+ };
+};
+
+std::vector MIBLEsensors;
+
+/********************************************************************************************/
+
+
+bool MINRFinitBLE(uint8_t _mode)
+{
+ if (MINRF.timer%1000 == 0){ // only re-init every 20 seconds
+ NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]);
+ NRF24radio.setAutoAck(false);
+ NRF24radio.setDataRate(RF24_1MBPS);
+ NRF24radio.disableCRC();
+ NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] );
+ NRF24radio.setRetries(0,0);
+ NRF24radio.setPALevel(RF24_PA_MIN); // we only receive
+ NRF24radio.setAddressWidth(4);
+ // NRF24radio.openReadingPipe(0,0x6B7D9171); // advertisement address: 0x8E89BED6 (bit-reversed -> 0x6B7D9171)
+ // NRF24radio.openWritingPipe( 0x6B7D9171); // not used ATM
+ NRF24radio.powerUp();
+ }
+ if(NRF24radio.isChipConnected()){
+ // DEBUG_SENSOR_LOG(PSTR("MINRF chip connected"));
+ MINRFchangePacketModeTo(_mode);
+ return true;
+ }
+ // DEBUG_SENSOR_LOG(PSTR("MINRF chip NOT !!!! connected"));
+ return false;
+}
+
+void MINRFhopChannel()
+{
+ MINRF.currentChan++;
+ if(MINRF.currentChan >= sizeof(MINRF.channel)) {
+ MINRF.currentChan = 0;
+ }
+ NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] );
+}
+
+/**
+ * @brief Read out FIFO-buffer, swap buffer and whiten
+ *
+ * @return true - If something is in the buffer
+ * @return false - Nothing is in the buffer
+ */
+bool MINRFreceivePacket(void)
+{
+ if(!NRF24radio.available()) {
+ return false;
+ }
+ while(NRF24radio.available()) {
+ // static uint8_t _lsfr = 0; //-> for testing out suitable lsfr-start-values for yet unknown packets
+ // _lsfr++;
+ NRF24radio.read( &MINRF.buffer, sizeof(MINRF.buffer) );
+#ifdef DEBUG_TASMOTA_SENSOR
+ memcpy(&MINRF.streamBuffer, &MINRF.buffer, sizeof(MINRF.buffer));
+#endif // DEBUG_TASMOTA_SENSOR
+ MINRFswapbuf( sizeof(MINRF.buffer) );
+ // MINRF_LOG_BUFFER();
+
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: _lsfrlist: %x, chan: %u, mode: %u"),_lsfrlist[MINRF.currentChan],MINRF.currentChan, MINRF.packetMode);
+ switch (MINRF.packetMode) {
+ case 0:
+ MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40);
+ break;
+ case 1:
+ MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); // "flora" mode
+ break;
+ case 2:
+ MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); // "MJ_HT_V1" mode
+ break;
+ case 3:
+ MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); // "LYWSD02" mode
+ break;
+ case 4:
+ MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); // "LYWSD03" mode
+ break;
+ }
+ // DEBUG_SENSOR_LOG(PSTR("MINRF: LSFR:%x"),_lsfr);
+ // if (_lsfr>254) _lsfr=0;
+ }
+ // DEBUG_SENSOR_LOG(PSTR("MINRF: did read FIFO"));
+ return true;
+}
+
+#ifdef DEBUG_TASMOTA_SENSOR
+void MINRFshowBuffer(uint8_t (&buf)[32]){ // we use this only for the 32-byte-FIFO-buffer, so 32 is hardcoded
+ // DEBUG_SENSOR_LOG(PSTR("MINRF: Buffer: %c %c %c %c %c %c %c %c"
+ // " %c %c %c %c %c %c %c %c"
+ // " %c %c %c %c %c %c %c %c"
+ // " %c %c %c %c %c %c %c %c")
+ DEBUG_SENSOR_LOG(PSTR("MINRF: Buffer: %02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x ")
+ ,buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10],buf[11],
+ buf[12],buf[13],buf[14],buf[15],buf[16],buf[17],buf[18],buf[19],buf[20],buf[21],buf[22],buf[23],
+ buf[24],buf[25],buf[26],buf[27],buf[28],buf[29],buf[30],buf[31]
+ );
+}
+#endif // DEBUG_TASMOTA_SENSOR
+
+/**
+ * @brief change lsfrBuffer content to "wire bit order"
+ *
+ * @param len Buffer lenght (could be hardcoded to 32)
+ */
+void MINRFswapbuf(uint8_t len)
+{
+ uint8_t* buf = (uint8_t*)&MINRF.buffer;
+ while(len--) {
+ uint8_t a = *buf;
+ uint8_t v = 0;
+ if (a & 0x80) v |= 0x01;
+ if (a & 0x40) v |= 0x02;
+ if (a & 0x20) v |= 0x04;
+ if (a & 0x10) v |= 0x08;
+ if (a & 0x08) v |= 0x10;
+ if (a & 0x04) v |= 0x20;
+ if (a & 0x02) v |= 0x40;
+ if (a & 0x01) v |= 0x80;
+ *(buf++) = v;
+ }
+}
+
+/**
+ * @brief Whiten the packet buffer
+ *
+ * @param buf The packet buffer
+ * @param len Lenght of the packet buffer
+ * @param lfsr Start lsfr-byte
+ */
+void MINRFwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr)
+{
+ while(len--) {
+ uint8_t res = 0;
+ // LFSR in "wire bit order"
+ for (uint8_t i = 1; i; i <<= 1) {
+ if (lfsr & 0x01) {
+ lfsr ^= 0x88;
+ res |= i;
+ }
+ lfsr >>= 1;
+ }
+ *(buf++) ^= res;
+#ifdef DEBUG_TASMOTA_SENSOR
+ MINRF.lsfrBuffer[31-len] = lfsr;
+#endif //DEBUG_TASMOTA_SENSOR
+ }
+}
+
+void MINRFreverseMAC(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));
+}
+
+/**
+ * @brief Set packet mode and fitting PDU-type of the NRF24L01
+ *
+ * @param _mode The internal packet mode number
+ */
+void MINRFchangePacketModeTo(uint8_t _mode) {
+ uint32_t (_nextchannel) = MINRF.currentChan+1;
+ if (_nextchannel>2) _nextchannel=0;
+
+ switch(_mode){
+ case 0: // normal BLE advertisement
+ NRF24radio.openReadingPipe(0,0x6B7D9171); // advertisement address: 0x8E89BED6 (bit-reversed -> 0x6B7D9171)
+ break;
+ case 1: // special flora packet
+ NRF24radio.openReadingPipe(0,kMINRFFloPDU[_nextchannel]); // 95 fe 71 20 -> flora
+ break;
+ case 2: // special MJ_HT_V1 packet
+ NRF24radio.openReadingPipe(0,kMINRFMJPDU[_nextchannel]); // 95 fe 50 20 -> MJ_HT_V1
+ break;
+ case 3: // special LYWSD02 packet
+ NRF24radio.openReadingPipe(0,kMINRFL2PDU[_nextchannel]);// 95 fe 70 20 -> LYWSD02
+ break;
+ case 4: // special LYWSD03 packet
+ if(kMINRFL3PDU[_nextchannel]==0xffffffff) break;
+ NRF24radio.openReadingPipe(0,kMINRFL3PDU[_nextchannel]);// 95 fe 58 30 -> LYWSD03 (= no data message)
+ break;
+ }
+ // DEBUG_SENSOR_LOG(PSTR("MINRF: Change Mode to %u"),_mode);
+ MINRF.packetMode = _mode;
+}
+
+/**
+ * @brief Return the slot number of a known sensor or return create new sensor slot
+ *
+ * @param _serial BLE address of the sensor
+ * @param _type Type number of the sensor, 0xff for Auto-type
+ * @return uint32_t Known or new slot in the sensors-vector
+ */
+uint32_t MINRFgetSensorSlot(uint8_t (&_serial)[6], uint8_t _type){
+ if(_type==0xff){
+ DEBUG_SENSOR_LOG(PSTR("MINRF: will test MAC-type"));
+ for (uint32_t i=0;i<4;i++){
+ if(memcmp(_serial,kMINRFSlaveID+i,3)==0){
+ DEBUG_SENSOR_LOG(PSTR("MINRF: MAC is type %u"), i);
+ _type = i+1;
+ }
+ else {
+ DEBUG_SENSOR_LOG(PSTR("MINRF: MAC-type is unknown"));
+ }
+ }
+ }
+ if(_type==0xff) return _type; // error
+
+ DEBUG_SENSOR_LOG(PSTR("MINRF: vector size %u"), MIBLEsensors.size());
+ for(uint32_t i=0; i6000){ // happens every 6000/20 = 300 seconds
+ DEBUG_SENSOR_LOG(PSTR("MINRF: check for FAKE sensors"));
+ MINRFpurgeFakeSensors();
+ MINRF.timer=0;
+ }
+ MINRF.timer++;
+
+ if (!MINRFreceivePacket()){
+ // DEBUG_SENSOR_LOG(PSTR("MINRF: nothing received"));
+ }
+
+ else if(MINRF.buffer.bleAdv.header.uuid==0xfe95){ // XIAOMI-BLE-Packet
+ MINRF_LOG_BUFFER(MINRF.streamBuffer);
+ MINRF_LOG_BUFFER(MINRF.lsfrBuffer);
+ MINRF_LOG_BUFFER(MINRF.buffer.raw);
+ DEBUG_SENSOR_LOG(PSTR("MINRF: Type: %x"), MINRF.buffer.bleAdv.header.type);
+ switch(MINRF.buffer.bleAdv.header.type){
+ case 0x2050:
+ DEBUG_SENSOR_LOG(PSTR("MINRF: MJ_HT_V1 Packet"));
+ break;
+ case 0x1613:case 0x1614:case 0x1615:
+ DEBUG_SENSOR_LOG(PSTR("MINRF: Flora Packet"));
+ break;
+ default:
+ DEBUG_SENSOR_LOG(PSTR("MINRF: unknown Packet"));
+ break;
+ }
+ }
+ else if (MINRF.packetMode == FLORA){
+ MINRFhandleFloraPacket();
+ }
+ else if (MINRF.packetMode == MJ_HT_V1){
+ MINRFhandleMJ_HT_V1Packet();
+ }
+ else if (MINRF.packetMode == LYWSD02){
+ MINRFhandleLYWSD02Packet();
+ }
+ else if (MINRF.packetMode == LYWSD03){
+ MINRFhandleLYWSD03Packet();
+ }
+
+ // DEBUG_SENSOR_LOG(PSTR("MINRF: Change packet mode every 50 msec"));
+ if (MINRF.packetMode == LYWSD03){
+ MINRFinitBLE(1); // no real ble packets in release mode, otherwise for developing use 0
+ }
+ else {
+ MINRFinitBLE(++MINRF.packetMode);
+ }
+
+ MINRFhopChannel();
+ NRF24radio.startListening();
+}
+/*********************************************************************************************\
+ * Presentation
+\*********************************************************************************************/
+
+const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}";
+const char HTTP_MINRF_MAC[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}";
+const char HTTP_MINRF_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%sus/cm{e}";
+const char HTTP_MINRF_HL[] PROGMEM = "{s}
{m}
{e}";
+
+void MINRFShow(bool json)
+{
+ if (json) {
+ for (uint32_t i = 0; i < MIBLEsensors.size(); i++) {
+ if(MIBLEsensors.at(i).showedUp < 3){
+ DEBUG_SENSOR_LOG(PSTR("MINRF: sensor not fully registered yet"));
+ break;
+ }
+ char slave[33];
+ sprintf_P(slave,"%s-%02x%02x%02x",kMINRFSlaveType[MIBLEsensors.at(i).type-1],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]);
+ char temperature[33]; // all sensors have temperature
+ dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature);
+
+ ResponseAppend_P(PSTR(",\"%s\":{"),slave);
+ if(MIBLEsensors.at(i).temp!=-1000.0f){ // this is the error code -> no temperature
+ ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature);
+ }
+ if (MIBLEsensors.at(i).type==FLORA){
+ char lux[33];
+ char moisture[33];
+ char fertility[33];
+ dtostrfd((float)MIBLEsensors.at(i).lux, 0, lux);
+ dtostrfd(MIBLEsensors.at(i).moisture, 0, moisture);
+ dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility);
+ if(MIBLEsensors.at(i).lux!=0xffff){ // this is the error code -> no temperature
+ ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%s"), lux);
+ }
+ if(MIBLEsensors.at(i).moisture!=-1000.0f){ // this is the error code -> no moisture
+ ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%s"), moisture);
+ }
+ if(MIBLEsensors.at(i).fertility!=-1000.0f){ // this is the error code -> no fertility
+ ResponseAppend_P(PSTR(",\"Fertility\":%s"), fertility);
+ }
+ }
+ if (MIBLEsensors.at(i).type>FLORA){
+ char humidity[33];
+ dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity);
+ if(MIBLEsensors.at(i).hum!=-1.0f){ // this is the error code -> no humidity
+ ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity);
+ }
+ if(MIBLEsensors.at(i).bat!=0xff){ // this is the error code -> no battery
+ ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors.at(i).bat);
+ }
+ }
+ ResponseAppend_P(PSTR("}"));
+ }
+#ifdef USE_WEBSERVER
+ } else {
+ WSContentSend_PD(HTTP_NRF24, NRF24type, NRF24.chipType);
+ for (uint32_t i = 0; i < MIBLEsensors.size(); i++) {
+ if(MIBLEsensors.at(i).showedUp < 3){
+ DEBUG_SENSOR_LOG(PSTR("MINRF: sensor not fully registered yet"));
+ break;
+ }
+ WSContentSend_PD(HTTP_MINRF_HL);
+ WSContentSend_PD(HTTP_MINRF_MAC, kMINRFSlaveType[MIBLEsensors.at(i).type-1], D_MAC_ADDRESS, MIBLEsensors.at(i).serial[0], MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]);
+ if(MIBLEsensors.at(i).temp!=-1000.0f){
+ char temperature[33];
+ dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature);
+ WSContentSend_PD(HTTP_SNS_TEMP, kMINRFSlaveType[MIBLEsensors.at(i).type-1], temperature, TempUnit());
+ }
+ if (MIBLEsensors.at(i).type==FLORA){
+ if(MIBLEsensors.at(i).lux!=0x00ffffff){ // this is the error code -> no valid value
+ WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kMINRFSlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).lux);
+ }
+ if(MIBLEsensors.at(i).moisture!=-1000.0f){ // this is the error code -> no valid value
+ WSContentSend_PD(HTTP_SNS_MOISTURE, kMINRFSlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).moisture);
+ }
+ if(MIBLEsensors.at(i).fertility!=-1000.0f){ // this is the error code -> no valid value
+ char fertility[33];
+ dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility);
+ WSContentSend_PD(HTTP_MINRF_FLORA_DATA, kMINRFSlaveType[MIBLEsensors.at(i).type-1], fertility);
+ }
+ }
+ if (MIBLEsensors.at(i).type>FLORA){ // everything "above" Flora
+ if(MIBLEsensors.at(i).hum!=-1.0f){ // this is the error code -> no humidity
+ char humidity[33];
+ dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity);
+ WSContentSend_PD(HTTP_SNS_HUM, kMINRFSlaveType[MIBLEsensors.at(i).type-1], humidity);
+ }
+ if(MIBLEsensors.at(i).bat!=0xff){
+ WSContentSend_PD(HTTP_BATTERY, kMINRFSlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).bat);
+ }
+ }
+ }
+#endif // USE_WEBSERVER
+ }
+}
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xsns61(uint8_t function)
+{
+ bool result = false;
+
+ if (NRF24.chipType) {
+ switch (function) {
+ case FUNC_INIT:
+ MINRFinitBLE(1);
+ AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: started"));
+ break;
+ case FUNC_EVERY_50_MSECOND:
+ MINRF_EVERY_50_MSECOND();
+ break;
+ case FUNC_JSON_APPEND:
+ MINRFShow(1);
+ break;
+#ifdef USE_WEBSERVER
+ case FUNC_WEB_SENSOR:
+ MINRFShow(0);
+ break;
+#endif // USE_WEBSERVER
+ }
+ }
+ return result;
+}
+
+#endif // USE_MIBLE
+#endif // USE_NRF24
+#endif // USE_SPI
+
+