Skip to content

Instantly share code, notes, and snippets.

@Staars
Created February 1, 2020 08:49
Show Gist options
  • Save Staars/224653f8b887644e5bb0ce61d7dbcf56 to your computer and use it in GitHub Desktop.
Save Staars/224653f8b887644e5bb0ce61d7dbcf56 to your computer and use it in GitHub Desktop.
Xiaomi test (from cbm80amiga) with the OLED-part removed
// 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