Created
February 1, 2020 08:49
-
-
Save Staars/224653f8b887644e5bb0ce61d7dbcf56 to your computer and use it in GitHub Desktop.
Xiaomi test (from cbm80amiga) with the OLED-part removed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Reading Xiaomi Mi BLE Temperature & Humidity Monitor | |
// Code for the video: https://youtu.be/pyhpXnFzNhU | |
// (C)2019 Pawel A. Hernik | |
// BLE code based on Dmitry Grinberg and Florian Echtler work | |
/* PINOUT | |
nRF24L01 from pin side/top: | |
------------- | |
|1 3 5 7 | | |
|2 4 6 8 | | |
| | | |
| | | |
| | | |
| | | |
| | | |
------------- | |
1 - GND blk GND | |
2 - VCC wht 3V3 | |
3 - CE orng 9 | |
4 - CSN yell 10 | |
5 - SCK grn 13 | |
6 - MOSI blue 11 | |
7 - MISO viol 12 | |
8 - IRQ gray 2 | |
More info about nRF24L01: | |
http://arduinoinfo.mywikis.net/wiki/Nrf24L01-2.4GHz-HowTo#po1 | |
*/ | |
#include <SPI.h> | |
#include <RF24.h> | |
#define MOSI 13 | |
#define MISO 12 | |
#define SCK 14 | |
RF24 radio(5,4); | |
// 0 - no debug on OLED | |
// 1 - packet counter/message type only | |
// 2 - full info with message timers | |
int debug = 2; | |
// serial debug - all packets dump | |
bool sdebug = true; | |
// ------------------------- | |
// ------------------------- | |
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) | |
struct bleAdvPacket { // for nRF24L01 max 32 bytes = 2+6+24 | |
uint8_t pduType; | |
uint8_t payloadSize; // payload size | |
uint8_t mac[6]; | |
uint8_t payload[24]; | |
}; | |
uint8_t currentChan=0; | |
bleAdvPacket buffer; | |
void swapbuf(uint8_t len); | |
void whiten(uint8_t len); | |
void initBLE() | |
{ | |
radio.begin(); | |
radio.setAutoAck(false); | |
radio.setDataRate(RF24_1MBPS); | |
radio.disableCRC(); | |
radio.setChannel( frequency[currentChan] ); | |
radio.setRetries(0,0); | |
radio.setPALevel(RF24_PA_MAX); | |
radio.setAddressWidth(4); | |
radio.openReadingPipe(0,0x6B7D9171); // advertisement address: 0x8E89BED6 (bit-reversed -> 0x6B7D9171) | |
radio.openWritingPipe( 0x6B7D9171); | |
radio.powerUp(); | |
} | |
void hopChannel() | |
{ | |
currentChan++; | |
if(currentChan >= sizeof(channel)) currentChan = 0; | |
radio.setChannel( frequency[currentChan] ); | |
} | |
bool receiveBLE(int timeout) | |
{ | |
radio.startListening(); | |
delay(timeout); | |
if(!radio.available()) return false; | |
uint8_t i = 0; | |
while(radio.available()) { | |
radio.read( &buffer, sizeof(buffer) ); | |
swapbuf( sizeof(buffer) ); | |
whiten( sizeof(buffer) ); | |
i++; | |
if (i>32) break; | |
} | |
return true; | |
} | |
// change buffer content to "wire bit order" | |
void swapbuf(uint8_t len) | |
{ | |
uint8_t* buf = (uint8_t*)&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; | |
} | |
} | |
void whiten(uint8_t len) | |
{ | |
uint8_t* buf = (uint8_t*)&buffer; | |
// initialize LFSR with current channel, set bit 6 | |
uint8_t lfsr = channel[currentChan] | 0x40; | |
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; | |
} | |
} | |
// ------------------------- | |
// real width is wd+6 | |
void drawBattBig(int x, int y, int wd, int perc) | |
{ | |
} | |
// ------------------------- | |
char buf[100]; | |
int temp=-1000; | |
int hum=-1; | |
int bat=-1; | |
int x,cnt=0,mode=0,v1,v10; | |
int tempOld=-123; | |
int humOld=-123; | |
int batOld=-123; | |
int cntOld = -1; | |
unsigned long tmT=0; | |
unsigned long tmH=0; | |
unsigned long tmB=0; | |
unsigned long tmD=0; | |
char *modeTxt=""; | |
// Xiaomi advertisement packet decoding | |
// 18 21 22 23 24 | |
// mm tl th hl hh | |
// a8 65 4c 0d 10 04 da 00 de 01 -> temperature+humidity | |
// mm hl hh | |
// a8 65 4c 06 10 04 da 01 -> humidity | |
// mm tl th | |
// a8 65 4c 04 10 04 db 00 -> temperature | |
// mm bb | |
// a8 75 4c 0a 10 01 60 -> battery | |
// 75 e7 f7 e5 bf 23 e3 20 0d 00 -> ??? | |
// 21 e6 f6 18 dc c6 01 -> ??? | |
// b8 65 5c 0e 10 41 60 -> battery?? | |
// a8 65 4c 46 10 02 d4 01 -> ?? | |
void setup() | |
{ | |
SPI.pins(SCK,MOSI,MISO,-1); | |
initBLE(); | |
Serial.begin(115200); | |
} | |
void loop() | |
{ | |
//Serial.println(F("BLE - Loop")); | |
receiveBLE(100); | |
uint8_t *recv = buffer.payload; | |
//if(buffer.mac[5]==0x4c && buffer.mac[0]==0xe) // limit to my Xiaomi MAC address (1st and last number only) | |
if(recv[5]==0x95 && recv[6]==0xfe && recv[7]==0x50 && recv[8]==0x20) | |
{ | |
cnt=recv[11]; | |
mode=recv[18]; | |
int mesSize=recv[3]; | |
int plSize=buffer.payloadSize-6; | |
if(mode==0x0d && plSize==25) { // temperature + humidity (missing msb, lsb is reconstructed from previous value) | |
temp=recv[21]+recv[22]*256; | |
modeTxt="TH"; | |
if(sdebug) snprintf(buf,100,"#%02x %02x %s %02x %3d'C (%3d%%)",cnt,mode,modeTxt,recv[3],recv[21]+recv[22]*256,(recv[23]+256)); | |
if(humOld>0) { // reconstructing humidity from previous value and new lsb | |
hum = (humOld & ~0xff) | recv[23]; | |
if(hum-humOld>128) hum -= 256; else | |
if(humOld-hum>128) hum += 256; | |
Serial.print(hum); | |
tmH = millis(); | |
} | |
tmT = millis(); | |
} else if(mode==0x04 && plSize==23) { // temperature | |
temp=recv[21]+recv[22]*256; | |
modeTxt="T "; | |
if(sdebug) snprintf(buf,100,"#%02x %02x %s %02x %3d'C ",cnt,mode,modeTxt,recv[3],recv[21]+recv[22]*256); | |
tmT = millis(); | |
} else if(mode==0x06 && plSize==23) { // humidity | |
hum=recv[21]+recv[22]*256; | |
modeTxt="H "; | |
if(sdebug) snprintf(buf,100,"#%02x %02x %s %02x %3d%% ",cnt,mode,modeTxt,recv[3],recv[21]+recv[22]*256); | |
tmH = millis(); | |
} else if(mode==0x0a && plSize==22) { // battery level | |
bat=recv[21]; | |
modeTxt="B "; | |
if(sdebug) snprintf(buf,100,"#%02x %02x %s %02x %03d%% batt ",cnt,mode,modeTxt,recv[3],recv[21]); | |
tmB = millis(); | |
} else { | |
modeTxt="??"; | |
if(sdebug) snprintf(buf,100,"!!!!!!%02x %02x %s %02x %03d %03d",cnt,mode,modeTxt,recv[3],recv[21],recv[22]); | |
} | |
if(sdebug) { | |
Serial.println(F("Debug")); | |
Serial.print(buf); | |
snprintf(buf,100," [%02x:%02x:%02x:%02x:%02x:%02x] ch%d s=%02d: ",buffer.mac[5],buffer.mac[4],buffer.mac[3],buffer.mac[2],buffer.mac[1],buffer.mac[0],currentChan,plSize); | |
Serial.print(buf); | |
int n = plSize<=24?plSize:24; | |
for(uint8_t i=0; i<n; i++) { snprintf(buf,100,"%02x ",buffer.payload[i]); Serial.print(buf); } | |
Serial.println(); | |
} | |
} | |
hopChannel(); | |
if(tempOld==temp && humOld==hum && batOld==bat) return; | |
tempOld=temp; humOld=hum; batOld=bat; | |
if(bat<0 || bat>100) | |
strcpy(buf," --"); | |
else { | |
snprintf(buf,100,"%3d",bat); | |
} | |
if(temp<=-400 || temp>800) | |
strcpy(buf,"--_- "); | |
else { | |
v1=temp/10; | |
v10=temp-v1*10; | |
snprintf(buf,100,"%2d_%d ",v1,v10); | |
} | |
if(hum<0 || hum>1000) | |
strcpy(buf,"--_- "); | |
else { | |
v1=hum/10; | |
v10=hum-v1*10; | |
snprintf(buf,100,"%2d_%d ",v1,v10); | |
} | |
Serial.print(bat); | |
Serial.print(temp); | |
Serial.print(hum); | |
Serial.println(buf); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment