From 432e2d8e750b9391d94e0b14bc10a1fd1152b71a Mon Sep 17 00:00:00 2001 From: Staars Date: Sun, 5 Apr 2020 16:21:27 +0200 Subject: [PATCH] update xsns_61_MI_NRF24.ino --- tasmota/xsns_61_MI_NRF24.ino | 1103 ++++++++++++++++++++-------------- 1 file changed, 655 insertions(+), 448 deletions(-) diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/xsns_61_MI_NRF24.ino index 69a99cb08..d21f9e7e1 100644 --- a/tasmota/xsns_61_MI_NRF24.ino +++ b/tasmota/xsns_61_MI_NRF24.ino @@ -21,6 +21,9 @@ Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- + 0.9.5.0 20200328 integrate - add dew point, multi-page-web ui, refactoring, command interface, + simple beacon + --- 0.9.4.0 20200304 integrate - sensor types can be ignored (default for LYWSD03), add CGD1 (Alarm clock), correct PDU-types for LYWSD02 --- @@ -67,10 +70,19 @@ #define CGG1 5 #define CGD1 6 -/* define sensors to ignore, which can improve performance - pattern: #define IGNORE_sensorname -*/ -#define IGNORE_LYWSD03 +#define D_CMND_NRF "NRF" + +const char S_JSON_NRF_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_NRF "%s\":%d}"; +const char S_JSON_NRF_COMMAND[] PROGMEM = "{\"" D_CMND_NRF "%s\":\"%s\"}"; +const char kNRF_Commands[] PROGMEM = "Ignore|Page|Scan|Beacon|Chan"; + +enum NRF_Commands { // commands useable in console or rules + CMND_NRF_IGNORE, // ignore specific sensor type (1-6) + CMND_NRF_PAGE, // sensor entries per web page, which will be shown alternated + CMND_NRF_SCAN, // simplified passive BLE adv scan + CMND_NRF_BEACON, // even more simplified Beacon, reports time since last sighting + CMND_NRF_CHAN // ignore channel 0-2 (translates to 37-39) + }; const uint16_t kMINRFSlaveID[6]={ 0x0098, // Flora 0x01aa, // MJ_HT_V1 @@ -103,178 +115,49 @@ 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; +struct mi_beacon_t{ + uint16_t productID; + uint8_t counter; + uint8_t Mac[6]; + uint8_t spare; // not on MJ_HT_V1 and CGG1 + uint8_t type; + uint8_t ten; + uint8_t size; + union { + struct{ //0d + int16_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 + }HT; + uint8_t bat; //0a + uint16_t temp; //04 + uint16_t hum; //06 + uint32_t lux:24; //07 + uint8_t moist; //08 + uint16_t fert; //09 + }; }; -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 CGDPacket_t { // related to the whole 32-byte-packet/buffer + uint8_t serial[6]; + uint16_t mode; + union { + struct { + int16_t temp; // -9 - 59 °C + uint16_t hum; + }; + uint8_t bat; + }; }; 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; + mi_beacon_t miBeacon; CGDPacket_t CGDPacket; uint8_t raw[32]; }; @@ -287,8 +170,23 @@ struct { uint16_t timer; uint8_t currentChan=0; - FIFO_t buffer; + uint8_t ignore = 0; //bitfield: 2^sensor type + uint8_t channelIgnore = 0; //bitfield: 2^channel (0=37,1=38,2=39) + uint8_t confirmedSensors = 0; uint8_t packetMode; // 0 - normal BLE-advertisements, 1 - 6 "special" sensor packets + uint8_t perPage = 4; + uint8_t firstUsedPacketMode = 1; + + FIFO_t buffer; + + struct { + uint8_t mac[6]; + uint32_t time; + uint32_t PDU[3]; + bool active = false; + } beacon; + bool activeScan = false; + bool stopScan = false; #ifdef DEBUG_TASMOTA_SENSOR uint8_t streamBuffer[sizeof(buffer)]; // raw data stream bytes @@ -315,11 +213,31 @@ struct mi_sensor_t{ }; }; +struct scan_entry_t { + uint8_t mac[6]; + uint16_t cid; + uint16_t svc; + uint16_t uuid; + uint8_t showedUp; +}; + std::vector MIBLEsensors; +std::vector MINRFscanResult; + +static union{ + scan_entry_t MINRFdummyEntry; + uint8_t MINRFtempBuf[23]; +}; /********************************************************************************************/ - +/** + * @brief + * + * @param _mode Packet mode 0-6 + * @return true If no error occured + * @return false If NRF24L01 is not connected + */ bool MINRFinitBLE(uint8_t _mode) { if (MINRF.timer%1000 == 0){ // only re-init every 20 seconds @@ -344,11 +262,20 @@ bool MINRFinitBLE(uint8_t _mode) return false; } +/** + * @brief cycle through the channels 37-39, skip ignored channel + * + */ void MINRFhopChannel() { - MINRF.currentChan++; - if(MINRF.currentChan >= sizeof(MINRF.channel)) { - MINRF.currentChan = 0; + for (uint32_t i = 0; i<3;i++){ + MINRF.currentChan++; + if(bitRead(MINRF.channelIgnore,MINRF.currentChan)) continue; + if(MINRF.currentChan >= sizeof(MINRF.channel)) { + MINRF.currentChan = 0; + if(bitRead(MINRF.channelIgnore,MINRF.currentChan)) continue; + } + break; } NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] ); } @@ -371,7 +298,7 @@ bool MINRFreceivePacket(void) #ifdef DEBUG_TASMOTA_SENSOR memcpy(&MINRF.streamBuffer, &MINRF.buffer, sizeof(MINRF.buffer)); #endif // DEBUG_TASMOTA_SENSOR - MINRFswapbuf( sizeof(MINRF.buffer) ); + MINRFswapbuf((uint8_t*)&MINRF.buffer, 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); @@ -427,9 +354,9 @@ void MINRFshowBuffer(uint8_t (&buf)[32]){ // we use this only for the 32-byte-FI * * @param len Buffer lenght (could be hardcoded to 32) */ -void MINRFswapbuf(uint8_t len) +void MINRFswapbuf(uint8_t *buf, uint8_t len) { - uint8_t* buf = (uint8_t*)&MINRF.buffer; + // uint8_t* buf = (uint8_t*)&MINRF.buffer; while(len--) { uint8_t a = *buf; uint8_t v = 0; @@ -471,6 +398,181 @@ void MINRFwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr) } } +/*********************************************************************************************\ + * Beacon functions +\*********************************************************************************************/ +bool MINRFhandleBeacon(scan_entry_t * entry, uint32_t offset); + +/** + * @brief handle a generic BLE-packet in the scan process + * + */ +void MINRFhandleScan(void){ + if(MINRFscanResult.size()>20 || MINRF.stopScan) { + MINRF.activeScan=false; + MINRFcomputefirstUsedPacketMode(); + uint32_t i = 0; // pass counter as reference to lambda + MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), + MINRFscanResult.end(), + [&i](scan_entry_t e) { + if(e.showedUp>2) AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: Beacon %02u: %02X%02X%02X%02X%02X%02X Cid: %04X Svc: %04X UUID: %04X"),i,e.mac[0],e.mac[1],e.mac[2],e.mac[3],e.mac[4],e.mac[5],e.cid,e.svc,e.uuid); + i++; + return ((e.showedUp < 3)); + }), + MINRFscanResult.end()); + MINRF.stopScan=false; + return; + } + + MINRFreverseMAC(MINRF.buffer.bleAdv.mac); + for(uint32_t i=0; iregular BLE-ADV, 6->"cutted" BLE-ADV with MAC as PDU + * @return true - when name, cid, uuid or svc is found with any value + * @return false - name, cid, uuid and svc are not found + */ +bool MINRFhandleBeacon(scan_entry_t * entry, uint32_t offset){ + bool success = false; + uint8_t _buf[32+offset]; + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); + MINRFswapbuf((uint8_t*)&MINRF.buffer,sizeof(MINRF.buffer)); + memcpy((uint8_t*)&_buf+offset,MINRF.buffer.raw,32); + MINRFswapbuf((uint8_t*)&_buf,sizeof(_buf)); + MINRFwhiten((uint8_t *)&_buf, sizeof(_buf), MINRF.channel[MINRF.currentChan] | 0x40); + if (offset == 6) MINRFreverseMAC((uint8_t*)&_buf[2]); + + if(memcmp((uint8_t*)&_buf[2],MINRF.beacon.mac,2)==0){ // always at least 2 undestroyed bytes left + if(_buf[8]!=2 && _buf[9]!=1){ + DEBUG_SENSOR_LOG(PSTR("MINRF: unsupported ADV %02x %02x"), _buf[8],_buf[9]); + return success; + } + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Beacon:____________")); + for (uint32_t i = 8; i<32+offset;i++){ + uint32_t size = _buf[i]; + if (size>30) break; + uint32_t ADtype = _buf[i+1]; + // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Size: %u AD: %x i:%u"), size, ADtype,i); + if (size+i>32+offset) size=32-i+offset-2; + if (size>30) break; + char _stemp[(size*2)]; + uint32_t backupSize; + switch(ADtype){ + case 0x01: + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Flags: %02x"), _buf[i+2]); + break; + case 0x02: case 0x03: + entry->uuid = _buf[i+3]*256 + _buf[i+2]; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: UUID: %04x"), entry->uuid); + success = true; + break; + case 0x08: case 0x09: + backupSize = _buf[i+size+1]; + _buf[i+size+1] = 0; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Name: %s"), (char*)&_buf[i+2]); + success = true; + _buf[i+size+1] = backupSize; + break; + case 0x0a: + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: TxPow: %02u"), _buf[i+2]); + break; + case 0xff: + entry->cid = _buf[i+3]*256 + _buf[i+2]; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Cid: %04x"), entry->cid); + ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + success = true; + break; + case 0x16: + entry->svc = _buf[i+3]*256 + _buf[i+2]; + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Svc: %04x"), entry->svc); + ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + success = true; + break; + default: + ToHex_P((unsigned char*)&_buf+i+2,size-1,_stemp,(size*2)); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + } + i+=size; + } + MINRF.beacon.time = 0; + } + return success; +} + +/** + * @brief increase beacon timer every second and process the result + * + */ +void MINRFbeaconCounter(void){ + if(MINRF.beacon.active) { + MINRF.beacon.time++; + char stemp[20]; + snprintf_P(stemp, sizeof(stemp),PSTR("{%s:{\"Beacon\": %u}}"),D_CMND_NRF, MINRF.beacon.time); + AddLog_P2(LOG_LEVEL_DEBUG, stemp); + RulesProcessEvent(stemp); + } +} + +/** + * @brief compute "PDU" from MAC for each possible channel and store it globally + * + */ +void MINRFcomputeBeaconPDU(void){ + for (uint32_t i = 0; i<3; i++){ + bleAdvPacket_t packet; + memcpy((uint8_t *)&packet.mac, (uint8_t *)&MINRF.beacon.mac, sizeof(packet.mac)); + MINRFreverseMAC(packet.mac); + MINRFwhiten((uint8_t *)&packet, sizeof(packet), MINRF.channel[i] | 0x40); + MINRFswapbuf((uint8_t*)&packet,sizeof(packet)); + uint32_t pdu = packet.mac[0]<<24 | packet.mac[1]<<16 | packet.mac[2]<<8 | packet.mac[3]; + MINRF.beacon.PDU[i] = pdu; + } +} + +/*********************************************************************************************\ + * helper functions +\*********************************************************************************************/ + +/** + * @brief reverse 6-byte-array, hard-coded size of 6 + * + * @param _mac pass an uint_t[6] + */ void MINRFreverseMAC(uint8_t _mac[]){ uint8_t _reversedMAC[6]; for (uint8_t i=0; i<6; i++){ @@ -479,6 +581,42 @@ void MINRFreverseMAC(uint8_t _mac[]){ memcpy(_mac,_reversedMAC, sizeof(_reversedMAC)); } +/** + * @brief + * + * @param _string input string in format: AABBCCDDEEFF (upper case!) + * @param _mac target byte array with fixed size of 6 + */ +void MINRFMACStringToBytes(char* _string, uint8_t _mac[]) { //uppercase + uint32_t index = 0; + while (index < 12) { + char c = _string[index]; + uint8_t value = 0; + if(c >= '0' && c <= '9') + value = (c - '0'); + else if (c >= 'A' && c <= 'F') + value = (10 + (c - 'A')); + _mac[(index/2)] += value << (((index + 1) % 2) * 4); + index++; + } + // DEBUG_SENSOR_LOG(PSTR("MINRF: %s to MAC-array: %02X%02X%02X%02X%02X%02X"),_string,_mac[0],_mac[1],_mac[2],_mac[3],_mac[4],_mac[5]); +} + +/** + * @brief helper function, to avoid to start with an ignored sensor type + * + */ +void MINRFcomputefirstUsedPacketMode(void){ + for (uint32_t i = 0; iCGD1) MINRF.firstUsedPacketMode=0; + break; + } + } +} + /** * @brief Set packet mode and fitting PDU-type of the NRF24L01 * @@ -502,7 +640,6 @@ void MINRFchangePacketModeTo(uint8_t _mode) { 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 @@ -541,39 +678,42 @@ uint32_t MINRFgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ DEBUG_SENSOR_LOG(PSTR("MINRF: vector size %u"), MIBLEsensors.size()); for(uint32_t i=0; i 2){ + MINRF.confirmedSensors++; } } } - -void MINRFhandleFloraPacket(void){ - if(MINRF.buffer.floraPacket.T.valueTen!=0x10){ - DEBUG_SENSOR_LOG(PSTR("MINRF: unexpected Flora packet")); - MINRF_LOG_BUFFER(MINRF.buffer.raw); - return; - } - MINRFreverseMAC(MINRF.buffer.floraPacket.T.serial); - uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.floraPacket.T.serial, MINRF.buffer.floraPacket.T.idWord); // T is not specific, any struct would be possible to use - DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot); +/** + * @brief generic MiBeacon parser + * + */ +void MINRFhandleMiBeaconPacket(void){ + MINRFreverseMAC(MINRF.buffer.miBeacon.Mac); + uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.miBeacon.Mac, MINRF.buffer.miBeacon.productID); if(_slot==0xff) return; + DEBUG_SENSOR_LOG(PSTR("MINRF: slot %u, size vector: %u %u"),_slot,MIBLEsensors.size()); + mi_sensor_t *_sensorVec = &MIBLEsensors.at(_slot); float _tempFloat; - switch(MINRF.buffer.floraPacket.L.mode) { // we can use any struct with a mode, they are all same at this point - case 4: - _tempFloat=(float)(MINRF.buffer.floraPacket.T.data)/10.0f; + + if (_sensorVec->type==MJ_HT_V1 || _sensorVec->type==CGG1){ + memcpy(MINRFtempBuf,(uint8_t*)&MINRF.buffer.miBeacon.spare, 32-9); // shift by one byte for the MJ_HT_V1 and CGG1 + memcpy((uint8_t*)&MINRF.buffer.miBeacon.type,MINRFtempBuf, 32-9); // shift by one byte for the MJ_HT_V1 and CGG1 + } + + DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kNRFSlaveType[_sensorVec->type-1],_slot); + switch(MINRF.buffer.miBeacon.type){ + case 0x04: + _tempFloat=(float)(MINRF.buffer.miBeacon.temp)/10.0f; if(_tempFloat<60){ - MIBLEsensors.at(_slot).temp=_tempFloat; + _sensorVec->temp=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); } - DEBUG_SENSOR_LOG(PSTR("Flora: Mode 4: U16: %x Temp"), MINRF.buffer.floraPacket.T.data ); + DEBUG_SENSOR_LOG(PSTR("Mode 4: U16: %u Temp"), MINRF.buffer.miBeacon.temp ); break; - case 7: - if(true){ - MIBLEsensors.at(_slot).lux=MINRF.buffer.floraPacket.L.data; + case 0x06: + _tempFloat=(float)(MINRF.buffer.miBeacon.hum)/10.0f; + if(_tempFloat<101){ + _sensorVec->hum=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); } - DEBUG_SENSOR_LOG(PSTR("Flora: Mode 7: U24: %x Lux"), MINRF.buffer.floraPacket.L.data); + DEBUG_SENSOR_LOG(PSTR("Mode 6: U16: %u Hum"), MINRF.buffer.miBeacon.hum); break; - case 8: - _tempFloat =(float)MINRF.buffer.floraPacket.M.data; + case 0x07: + _sensorVec->lux=MINRF.buffer.miBeacon.lux & 0x00ffffff; + DEBUG_SENSOR_LOG(PSTR("Mode 7: U24: %u Lux"), MINRF.buffer.miBeacon.lux & 0x00ffffff); + break; + case 0x08: + _tempFloat =(float)MINRF.buffer.miBeacon.moist; if(_tempFloat<100){ - MIBLEsensors.at(_slot).moisture=_tempFloat; + _sensorVec->moisture=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); } - DEBUG_SENSOR_LOG(PSTR("Flora: Mode 8: U8: %x Moisture"), MINRF.buffer.floraPacket.M.data); + DEBUG_SENSOR_LOG(PSTR("Mode 8: U8: %u Moisture"), MINRF.buffer.miBeacon.moist); break; - case 9: - _tempFloat=(float)(MINRF.buffer.floraPacket.F.data); + case 0x09: + _tempFloat=(float)(MINRF.buffer.miBeacon.fert); if(_tempFloat<65535){ // ??? - MIBLEsensors.at(_slot).fertility=_tempFloat; + _sensorVec->fertility=_tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); } - DEBUG_SENSOR_LOG(PSTR("Mode 9: U16: %x Fertility"), MINRF.buffer.floraPacket.F.data); - break; - } -} - -void MINRFhandleMJ_HT_V1Packet(void){ - if(MINRF.buffer.MJ_HT_V1Packet.TH.valueTen!=0x10){ - DEBUG_SENSOR_LOG(PSTR("MINRF: unexpected MJ_HT_V1-packet")); - MINRF_LOG_BUFFER(MINRF.buffer.raw); - return; - } - MINRFreverseMAC(MINRF.buffer.MJ_HT_V1Packet.TH.serial); - uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.MJ_HT_V1Packet.TH.serial, MINRF.buffer.MJ_HT_V1Packet.TH.idWord); // B would be possible too - DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot); - if(_slot==0xff) return; - - float _tempFloat; - switch(MINRF.buffer.MJ_HT_V1Packet.TH.mode) { // we can use any struct with a mode, they are all same at this point - case 0x0d: - _tempFloat=(float)(MINRF.buffer.MJ_HT_V1Packet.TH.temp)/10.0f; - if(_tempFloat<60){ - MIBLEsensors.at(_slot).temp = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("MJ_HT_V1: temp updated")); - } - _tempFloat=(float)(MINRF.buffer.MJ_HT_V1Packet.TH.hum)/10.0f; - if(_tempFloat<100){ - MIBLEsensors.at(_slot).hum = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("MJ_HT_V1: hum updated")); - } - DEBUG_SENSOR_LOG(PSTR("MJ_HT_V1 mode:0x0d: U16: %x Temp U16: %x Hum"), MINRF.buffer.MJ_HT_V1Packet.TH.temp, MINRF.buffer.MJ_HT_V1Packet.TH.hum); + DEBUG_SENSOR_LOG(PSTR("Mode 9: U16: %u Fertility"), MINRF.buffer.miBeacon.fert); break; case 0x0a: - if(MINRF.buffer.MJ_HT_V1Packet.B.battery<101){ - MIBLEsensors.at(_slot).bat = MINRF.buffer.MJ_HT_V1Packet.B.battery; - DEBUG_SENSOR_LOG(PSTR("MJ_HT_V1: bat updated")); - } - DEBUG_SENSOR_LOG(PSTR("MJ_HT_V1 mode:0x0a: U8: %x %%"), MINRF.buffer.MJ_HT_V1Packet.B.battery); + if(MINRF.buffer.miBeacon.bat<101){ + _sensorVec->bat = MINRF.buffer.miBeacon.bat; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + DEBUG_SENSOR_LOG(PSTR("Mode a: U8: %u %%"), MINRF.buffer.miBeacon.bat); break; - } -} - -void MINRFhandleLYWSD02Packet(void){ - if(MINRF.buffer.LYWSD02Packet.TH.valueTen!=0x10){ - DEBUG_SENSOR_LOG(PSTR("MINRF: unexpected LYWSD02-packet")); - MINRF_LOG_BUFFER(MINRF.buffer.raw); - return; - } - MINRFreverseMAC(MINRF.buffer.LYWSD02Packet.TH.serial); - uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.LYWSD02Packet.TH.serial, MINRF.buffer.LYWSD02Packet.TH.idWord); // H would be possible too - DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot); - if(_slot==0xff) return; - - float _tempFloat; - switch(MINRF.buffer.LYWSD02Packet.TH.mode) { // we can use any struct with a mode, they are all same at this point - case 4: - _tempFloat=(float)(MINRF.buffer.LYWSD02Packet.TH.data)/10.0f; + case 0x0d: + _tempFloat=(float)(MINRF.buffer.miBeacon.HT.temp)/10.0f; if(_tempFloat<60){ - MIBLEsensors.at(_slot).temp=_tempFloat; + _sensorVec->temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: temp updated")); + } + _tempFloat=(float)(MINRF.buffer.miBeacon.HT.hum)/10.0f; + if(_tempFloat<100){ + _sensorVec->hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); } - DEBUG_SENSOR_LOG(PSTR("LYWSD02: Mode 4: U16: %x Temp"), MINRF.buffer.LYWSD02Packet.TH.data ); - break; - case 6: - _tempFloat=(float)(MINRF.buffer.LYWSD02Packet.TH.data)/10.0f; - if(_tempFloat<101){ - MIBLEsensors.at(_slot).hum=_tempFloat; - } - DEBUG_SENSOR_LOG(PSTR("LYWSD02: Mode 6: U16: %x Hum"), MINRF.buffer.LYWSD02Packet.TH.data ); + DEBUG_SENSOR_LOG(PSTR("Mode d: U16: %x Temp U16: %x Hum"), MINRF.buffer.miBeacon.HT.temp, MINRF.buffer.miBeacon.HT.hum); break; } } - +/** + * @brief more or less a placeholder, at least it is technically possible to really decrypt data, but + * the bind_key must be retrieved with 3rd-party-tools -> TODO + */ void MINRFhandleLYWSD03Packet(void){ // not much to do ATM, just show the sensor without data - MINRFreverseMAC(MINRF.buffer.LYWSD02Packet.TH.serial); //the beginning is equal to the LYWSD02-packet - uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.LYWSD02Packet.TH.serial, MINRF.buffer.LYWSD02Packet.TH.idWord); + MINRFreverseMAC(MINRF.buffer.miBeacon.Mac); + uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.miBeacon.Mac, MINRF.buffer.miBeacon.productID); DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot); if(_slot==0xff) return; @@ -712,67 +839,43 @@ void MINRFhandleLYWSD03Packet(void){ MINRF_LOG_BUFFER(MINRF.buffer.raw); } -void MINRFhandleCGG1Packet(void){ // we assume, that the packet structure is equal to the MJ_HT_V1 - if(MINRF.buffer.MJ_HT_V1Packet.TH.valueTen!=0x10){ - DEBUG_SENSOR_LOG(PSTR("MINRF: unexpected CGG1-packet")); - MINRF_LOG_BUFFER(MINRF.buffer.raw); - return; - } - MINRFreverseMAC(MINRF.buffer.MJ_HT_V1Packet.TH.serial); - uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.MJ_HT_V1Packet.TH.serial, MINRF.buffer.MJ_HT_V1Packet.TH.idWord); // B would be possible too +/** + * @brief parse the Cleargrass-packet + * Note: battery section is based on "internet data" -> not confirmed yet + */ +void MINRFhandleCGD1Packet(void){ // no MiBeacon + MINRFreverseMAC(MINRF.buffer.CGDPacket.serial); + uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.CGDPacket.serial, 0x0576); // This must be hard-coded, no object-id in Cleargrass-packet DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot); if(_slot==0xff) return; - float _tempFloat; - switch(MINRF.buffer.MJ_HT_V1Packet.TH.mode) { // we can use any struct with a mode, they are all same at this point - case 0x0d: - _tempFloat=(float)(MINRF.buffer.MJ_HT_V1Packet.TH.temp)/10.0f; - if(_tempFloat<60){ - MIBLEsensors.at(_slot).temp = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("CGG1: temp updated")); - } - _tempFloat=(float)(MINRF.buffer.MJ_HT_V1Packet.TH.hum)/10.0f; - if(_tempFloat<100){ - MIBLEsensors.at(_slot).hum = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("CGG1: hum updated")); - } - DEBUG_SENSOR_LOG(PSTR("CGG1 mode:0x0d: U16: %x Temp U16: %x Hum"), MINRF.buffer.MJ_HT_V1Packet.TH.temp, MINRF.buffer.MJ_HT_V1Packet.TH.hum); - break; - case 0x0a: - if(MINRF.buffer.MJ_HT_V1Packet.B.battery<101){ - MIBLEsensors.at(_slot).bat = MINRF.buffer.MJ_HT_V1Packet.B.battery; - DEBUG_SENSOR_LOG(PSTR("CGG1: bat updated")); - } - DEBUG_SENSOR_LOG(PSTR("CGG1 mode:0x0a: U8: %x %%"), MINRF.buffer.MJ_HT_V1Packet.B.battery); - break; + switch (MINRF.buffer.CGDPacket.mode){ + case 0x0401: + float _tempFloat; + _tempFloat=(float)(MINRF.buffer.CGDPacket.temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors.at(_slot).temp = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("CGD1: temp updated")); + } + _tempFloat=(float)(MINRF.buffer.CGDPacket.hum)/10.0f; + if(_tempFloat<100){ + MIBLEsensors.at(_slot).hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated")); + } + DEBUG_SENSOR_LOG(PSTR("CGD1: U16: %x Temp U16: %x Hum"), MINRF.buffer.CGDPacket.temp, MINRF.buffer.CGDPacket.hum); + break; + case 0x0102: + if(MINRF.buffer.CGDPacket.bat<101){ + MIBLEsensors.at(_slot).bat = MINRF.buffer.CGDPacket.bat; + DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); + } + break; + default: + DEBUG_SENSOR_LOG(PSTR("MINRF: unexpected CGD1-packet")); + MINRF_LOG_BUFFER(MINRF.buffer.raw); } } -void MINRFhandleCGD1Packet(void){ // - if(MINRF.buffer.CGDPacket.TH.mode!=0x0401){ // not really a mode - DEBUG_SENSOR_LOG(PSTR("MINRF: unexpected CGD1-packet")); - MINRF_LOG_BUFFER(MINRF.buffer.raw); - return; - } - MINRFreverseMAC(MINRF.buffer.CGDPacket.TH.serial); - uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.CGDPacket.TH.serial, 0x0576); // This must be hard-coded, no object-id in Cleargrass-packet - DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot); - if(_slot==0xff) return; - - float _tempFloat; - _tempFloat=(float)(MINRF.buffer.CGDPacket.TH.temp)/10.0f; - if(_tempFloat<60){ - MIBLEsensors.at(_slot).temp = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("CGD1: temp updated")); - } - _tempFloat=(float)(MINRF.buffer.CGDPacket.TH.hum)/10.0f; - if(_tempFloat<100){ - MIBLEsensors.at(_slot).hum = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("CGD1: hum updated")); - } - DEBUG_SENSOR_LOG(PSTR("CGD1: U16: %x Temp U16: %x Hum"), MINRF.buffer.CGDPacket.TH.temp, MINRF.buffer.CGDPacket.TH.hum); -} - /*********************************************************************************************\ * Main loop of the driver \*********************************************************************************************/ @@ -790,163 +893,262 @@ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds // 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; + else { + switch (MINRF.packetMode) { + case 0: + if (MINRF.beacon.active){ + MINRFhandleBeacon(&MINRFdummyEntry,6); + } + else MINRFhandleScan(); + break; + case FLORA: case MJ_HT_V1: case LYWSD02: case CGG1: + MINRFhandleMiBeaconPacket(); + break; + case LYWSD03: + MINRFhandleLYWSD03Packet(); + break; + case CGD1: + MINRFhandleCGD1Packet(); + break; default: - DEBUG_SENSOR_LOG(PSTR("MINRF: unknown Packet")); - break; + 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(); + if (MINRF.beacon.active || MINRF.activeScan) { + MINRF.firstUsedPacketMode=0; } -#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 + MINRF.packetMode = (MINRF.packetMode+1>CGD1) ? MINRF.firstUsedPacketMode : MINRF.packetMode+1; + for (uint32_t i = MINRF.packetMode; i 0) { + if (XdrvMailbox.payload == 0) XdrvMailbox.payload = MINRF.perPage; // ignore 0 + MINRF.perPage = XdrvMailbox.payload; + } + else XdrvMailbox.payload = MINRF.perPage; + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_NRF_IGNORE: + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload == 0){ + MINRF.ignore = 0; + } + else if (XdrvMailbox.payload < CGD1+1) { + bitSet(MINRF.ignore,XdrvMailbox.payload); + MINRFcomputefirstUsedPacketMode(); + MINRF.timer = 5900; + Response_P(S_JSON_NRF_COMMAND, command, kMINRFSlaveType[XdrvMailbox.payload-1]); + } + else if (XdrvMailbox.payload == 255) { + MINRF.ignore = 255; + } + } + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, MINRF.ignore); + break; + case CMND_NRF_SCAN: + if (XdrvMailbox.data_len > 0) { + MINRF.beacon.active = false; + switch(XdrvMailbox.payload){ + case 0: // new scan + MINRF.activeScan = true; + MINRF.stopScan = false; + MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), + MINRFscanResult.end(), + [](scan_entry_t&) { return true; }), + MINRFscanResult.end()); + break; + case 1: // append scan + MINRF.activeScan = true; + MINRF.stopScan = false; + break; + case 2: // stop scan + MINRF.stopScan = true; + break; + } + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); + } + break; + case CMND_NRF_BEACON: + if (XdrvMailbox.data_len > 0) { + if(XdrvMailbox.data_len<3){ // a list entry + if (XdrvMailbox.payload < MINRFscanResult.size()) { + MINRFstartBeacon(XdrvMailbox.payload); + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, XdrvMailbox.payload); + } + } + if (XdrvMailbox.data_len==12){ // a MAC-string + memset(MINRF.beacon.mac,0,sizeof(MINRF.beacon.mac)); + MINRFMACStringToBytes(XdrvMailbox.data, MINRF.beacon.mac); + MINRF.beacon.time=0; + MINRF.beacon.active=true; + Response_P(S_JSON_NRF_COMMAND, command, XdrvMailbox.data); + } + MINRFcomputeBeaconPDU(); + } + break; + case CMND_NRF_CHAN: + if (XdrvMailbox.data_len == 1) { + switch(XdrvMailbox.payload){ + case 0: case 1: case 2: + bitRead(MINRF.channelIgnore,XdrvMailbox.payload) == 0 ? bitSet(MINRF.channelIgnore,XdrvMailbox.payload) : bitClear(MINRF.channelIgnore,XdrvMailbox.payload); + break; + } + } + Response_P(S_JSON_NRF_COMMAND_NVALUE, command, MINRF.channelIgnore); + break; + default: + // else for Unknown command + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + /*********************************************************************************************\ * 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_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%dus/cm{e}"; const char HTTP_MINRF_HL[] PROGMEM = "{s}
{m}
{e}"; +const char HTTP_NRF24NEW[] PROGMEM = "{s}%sL01%c{m}%u%s / %u{e}"; void MINRFShow(bool json) { if (json) { for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { - if(MIBLEsensors.at(i).showedUp < 3){ + if(MIBLEsensors[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-%02x%02x%02x\":{"),kMINRFSlaveType[MIBLEsensors[i].type-1],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); + if (MIBLEsensors[i].type==FLORA && !isnan(MIBLEsensors[i].temp)){ + char stemp[FLOATSZ]; + dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, stemp); + ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), stemp); - 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[i].lux!=0xffffffff){ // this is the error code -> no lux + ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), MIBLEsensors[i].lux); } - 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(!isnan(MIBLEsensors[i].moisture)){ + dtostrfd(MIBLEsensors[i].moisture, 0, stemp); + ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%s"), stemp); } - 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); - } + if(!isnan(MIBLEsensors[i].fertility)){ + dtostrfd(MIBLEsensors[i].fertility, 0, stemp); + ResponseAppend_P(PSTR(",\"Fertility\":%s"), stemp); } - ResponseAppend_P(PSTR("}")); + ResponseJsonEnd(); + } + if (MIBLEsensors[i].type>FLORA){ + if(!isnan(MIBLEsensors[i].temp) && !isnan(MIBLEsensors[i].hum)){ + ResponseAppendTHD(MIBLEsensors[i].temp,MIBLEsensors[i].hum); + } + if(MIBLEsensors[i].bat!=0x00){ // this is the error code -> no battery + ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors[i].bat); + } + ResponseJsonEnd(); + } } + if(MINRF.beacon.active){ + ResponseAppend_P(PSTR(",\"Beacon\":{\"Timer\":%u}"),MINRF.beacon.time); + } + // ResponseJsonEnd(); #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){ + static uint32_t _page = 0; + static uint32_t counter = 0; + int32_t i = _page * MINRF.perPage; + uint32_t j = i + MINRF.perPage; + + if (j+1>MINRF.confirmedSensors){ + j = MINRF.confirmedSensors; + } + char stemp[5] ={0}; + if (MINRF.confirmedSensors-(_page*MINRF.perPage)>1 && MINRF.perPage!=1) { + sprintf_P(stemp,"-%u",j); + } + if (MINRF.confirmedSensors==0) i=-1; // only for the GUI + + WSContentSend_PD(HTTP_NRF24NEW, NRF24type, NRF24.chipType, i+1,stemp,MINRF.confirmedSensors); + for (i ; i no valid value - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kMINRFSlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).lux); + WSContentSend_PD(HTTP_MINRF_MAC, kMINRFSlaveType[MIBLEsensors[i].type-1], D_MAC_ADDRESS, MIBLEsensors[i].serial[0], MIBLEsensors[i].serial[1],MIBLEsensors[i].serial[2],MIBLEsensors[i].serial[3],MIBLEsensors[i].serial[4],MIBLEsensors[i].serial[5]); + if (MIBLEsensors[i].type==FLORA){ + if(!isnan(MIBLEsensors[i].temp)){ + char temperature[FLOATSZ]; + dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); + WSContentSend_PD(HTTP_SNS_TEMP, kMINRFSlaveType[MIBLEsensors[i].type-1], temperature, TempUnit()); } - 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[i].lux!=0xffffffff){ // this is the error code -> no valid value + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kMINRFSlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].lux); } - 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(!isnan(MIBLEsensors[i].moisture)){ // this is the error code -> no valid value + WSContentSend_PD(HTTP_SNS_MOISTURE, kMINRFSlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].moisture); + } + if(!isnan(MIBLEsensors[i].fertility)){ // this is the error code -> no valid value + WSContentSend_PD(HTTP_MINRF_FLORA_DATA, kMINRFSlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].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); + if (MIBLEsensors[i].type>FLORA){ // everything "above" Flora + WSContentSend_THD(kMINRFSlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp, MIBLEsensors[i].hum); + if(MIBLEsensors[i].bat!=0x00){ // without "juice" nothing can be done + WSContentSend_PD(HTTP_BATTERY, kMINRFSlaveType[MIBLEsensors[i].type-1], MIBLEsensors[i].bat); } } } + if(MINRF.beacon.active){ + WSContentSend_PD(HTTP_MINRF_HL); + WSContentSend_PD(HTTP_MINRF_HL); + WSContentSend_PD(HTTP_MINRF_MAC, F("Beacon"), D_MAC_ADDRESS, MINRF.beacon.mac[0], MINRF.beacon.mac[1],MINRF.beacon.mac[2],MINRF.beacon.mac[3],MINRF.beacon.mac[4],MINRF.beacon.mac[5]); + WSContentSend_PD(PSTR("{s}Beacon Time{m}%u seconds{e}"),MINRF.beacon.time); + } + if(counter>3) { + _page++; + counter = 0; + } + counter++; + if(MINRF.confirmedSensors%MINRF.perPage==0 && _page==MINRF.confirmedSensors/MINRF.perPage) _page=0; + if(_page>MINRF.confirmedSensors/MINRF.perPage) _page=0; #endif // USE_WEBSERVER } } @@ -958,7 +1160,6 @@ void MINRFShow(bool json) bool Xsns61(uint8_t function) { bool result = false; - if (NRF24.chipType) { switch (function) { case FUNC_INIT: @@ -968,6 +1169,12 @@ bool Xsns61(uint8_t function) case FUNC_EVERY_50_MSECOND: MINRF_EVERY_50_MSECOND(); break; + case FUNC_EVERY_SECOND: + MINRFbeaconCounter(); + break; + case FUNC_COMMAND: + result = NRFCmd(); + break; case FUNC_JSON_APPEND: MINRFShow(1); break;