/* 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.4.0 20200304 integrate - sensor types can be ignored (default for LYWSD03), add CGD1 (Alarm clock), correct PDU-types for LYWSD02 --- 0.9.3.0 20200222 integrate - use now the correct id-word instead of MAC-OUI, add CGG1 --- 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, GCx * * Usage: Configure NRF24 \*********************************************************************************************/ #define XSNS_61 61 #include #define FLORA 1 #define MJ_HT_V1 2 #define LYWSD02 3 #define LYWSD03 4 #define CGG1 5 #define CGD1 6 /* define sensors to ignore, which can improve performance pattern: #define IGNORE_sensorname */ #define IGNORE_LYWSD03 const uint16_t kMINRFSlaveID[6]={ 0x0098, // Flora 0x01aa, // MJ_HT_V1 0x045b, // LYWSD02 0x055b, // LYWSD03 0x0347, // CGG1 0x0576 // CGD1 }; const char kMINRFSlaveType1[] PROGMEM = "Flora"; const char kMINRFSlaveType2[] PROGMEM = "MJ_HT_V1"; const char kMINRFSlaveType3[] PROGMEM = "LYWSD02"; const char kMINRFSlaveType4[] PROGMEM = "LYWSD03"; const char kMINRFSlaveType5[] PROGMEM = "CGG1"; const char kMINRFSlaveType6[] PROGMEM = "CGD1"; const char * kMINRFSlaveType[] PROGMEM = {kMINRFSlaveType1,kMINRFSlaveType2,kMINRFSlaveType3,kMINRFSlaveType4,kMINRFSlaveType5,kMINRFSlaveType6}; // 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,0x71dafb46}; // const uint32_t kMINRFL3PDU[3] = {0x4760dd78,0xdbcc1ccd,0xffffffff}; //encrypted - 58 58 const uint32_t kMINRFL3PDU[3] = {0x4760cb78,0xdbcc0acd,0x33048beb}; //unencrypted - 30 58 const uint32_t kMINRFCGGPDU[3] = {0x4760cd6e,0xdbcc0cdb,0x33048dfd}; const uint32_t kMINRFCGDPDU[3] = {0x5da0d752,0xc10c16e7,0x29c497c1}; // 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, CGx #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 }; union CGDPacket_t { // related to the whole 32-byte-packet/buffer struct { uint8_t serial[6]; uint16_t mode; int16_t temp; // -9 - 59 °C uint16_t hum; } TH; // This is no MiBeacon }; 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; CGDPacket_t CGDPacket; 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 - 6 "special" sensor packets #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; CGG1=5; CGD1=6 uint8_t serial[6]; uint8_t showedUp; float temp; //Flora, MJ_HT_V1, LYWSD0x, CGx union { struct { float moisture; float fertility; uint32_t lux; }; // Flora struct { float hum; uint8_t bat; }; // MJ_HT_V1, LYWSD0x, CGx }; }; 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; case 5: MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); // "CGG1" mode break; case 6: MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); // "CGD1" 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; case 5: // special CGG1 packet NRF24radio.openReadingPipe(0,kMINRFCGGPDU[_nextchannel]); // 95 fe 50 30 -> CGG1 break; case 6: // special CGD1 packet NRF24radio.openReadingPipe(0,kMINRFCGDPDU[_nextchannel]); // cd fd 08 0c -> CGD1 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 * @return uint32_t Known or new slot in the sensors-vector */ uint32_t MINRFgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ DEBUG_SENSOR_LOG(PSTR("MINRF: will test ID-type: %x"), _type); bool _success = false; for (uint32_t i=0;i<6;i++){ // i < sizeof(kMINRFSlaveID) gives compiler warning if(_type == kMINRFSlaveID[i]){ DEBUG_SENSOR_LOG(PSTR("MINRF: ID is type %u"), i); _type = i+1; _success = true; } else { DEBUG_SENSOR_LOG(PSTR("MINRF: ID-type is not: %x"),kMINRFSlaveID[i]); } } if(!_success) return 0xff; 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(); } else if (MINRF.packetMode == CGG1){ MINRFhandleCGG1Packet(); } else if (MINRF.packetMode == CGD1){ MINRFhandleCGD1Packet(); } #ifdef IGNORE_FLORA if (MINRF.packetMode+1 == FLORA) MINRF.packetMode++; #endif // IGNORE_LYWSD03 #ifdef IGNORE_MJ_HT_V1 if (MINRF.packetMode+1 == MJ_HT_V1) MINRF.packetMode++; #endif //IGNORE_MJ_HT_V1 #ifdef IGNORE_LYWSD02 if (MINRF.packetMode+1 == LYWSD02) MINRF.packetMode++; #endif // IGNORE_LYWSD02 #ifdef IGNORE_LYWSD03 if (MINRF.packetMode+1 == LYWSD03) MINRF.packetMode++; #endif // IGNORE_LYWSD03 #ifdef IGNORE_CGG1 if (MINRF.packetMode+1 == CGG1) MINRF.packetMode++; #endif // IGNORE_CGG1 #ifdef IGNORE_CGD1 if (MINRF.packetMode+1 == CGD1) MINRF.packetMode=0; #endif // IGNORE_CGD1 MINRFinitBLE(++MINRF.packetMode); if (MINRF.packetMode > CGD1){ MINRFinitBLE(1); // no real ble packets in release mode, otherwise for developing use 0 } 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!=0x00){ // 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!=0x00){ // without "juice" nothing can be done 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