Skip to content

Instantly share code, notes, and snippets.

@bitbank2
Last active October 6, 2021 01:56
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bitbank2/90773114eee37bff40b82770c4ac0ce4 to your computer and use it in GitHub Desktop.
Save bitbank2/90773114eee37bff40b82770c4ac0ce4 to your computer and use it in GitHub Desktop.
//
// Demo sketch to read the data from a RadioLand RDL52832 iBeacon
//
// Displays the data on an M5StickC-Plus without using the M5Stack library
// Also works on the Nano33 BLE
// Parses (in a brute-force way) the large advertisement packet transmitted
// by the iBeacon.
//
// Written by Larry Bank
// February 21, 2021
//
//
// Data format for the RadioLand RDL52832 iBeacon
//
// service UUID 0x0318:
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
// | Th | Tl | Hh | Hl | Xs | Xw | Xt | Xh | Ys | Yw | Yt | Yh | Zs | Zw | Zt | Zh |
// +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
// Temperature (T), stored as a 16-bit value equal to Celcius * 256
// Humidity (H), stored as a 16-bit value equal to Humidity % * 256
// Accelerometer Axes: (X,Y,Z)
// s = sign (1=negative, 0=positive)
// w = whole value (0 or 1)
// t = tenths (0-9)
// h = hundredths (0-9)
//
// iBeacon packet introducer
const uint8_t uciBeacon[] = {0x02, 0x01, 0x06, 0x1a, 0xff, 0x4c, 0x0, 0x2, 0x15};
#ifdef HAL_ESP32_HAL_H_
#include <BLEDevice.h>
#include <BLEUtils.h>
#endif
#ifdef ARDUINO_ARDUINO_NANO33BLE
#include <ArduinoBLE.h>
static BLEDevice peripheral;
#endif
#include <bb_spi_lcd.h>
#include <Wire.h>
SPILCD lcd;
static uint8_t ucTXBuf[1024];
// M5StickC-Plus
#ifdef HAL_ESP32_HAL_H_
#define TFT_CS 5
#define TFT_RST 18
#define TFT_DC 23
#define TFT_CLK 13
#define TFT_MOSI 15
#define BUTTON_A 37
#define BUTTON_B 39
#define LCD_TYPE LCD_ST7789_135
#else // Nano33 BLE + ILI9341
#define LCD_TYPE LCD_ILI9341
#define TFT_CS -1
#define TFT_RST A2
#define TFT_DC A3
#define TFT_CLK -1
#define TFT_MOSI -1
#endif
static int T, H, iMaxT, iMinT, iMaxH, iMinH;
static int iRSSI;
float X, Y, Z, fDistance;
std::string service_data;
char Scanned_BLE_Name[32];
String Scanned_BLE_Address;
#ifdef HAL_ESP32_HAL_H_
BLEScanResults foundDevices;
BLEScan *pBLEScan;
static BLEAddress *Server_BLE_Address;
// Support functions for I2C devices on M5StickC-Plus
void Write1Byte( uint8_t Addr , uint8_t Data )
{
Wire1.beginTransmission(0x34);
Wire1.write(Addr);
Wire1.write(Data);
Wire1.endTransmission();
}
uint8_t Read8bit( uint8_t Addr )
{
Wire1.beginTransmission(0x34);
Wire1.write(Addr);
Wire1.endTransmission();
Wire1.requestFrom(0x34, 1);
return Wire1.read();
}
void AxpBrightness(uint8_t brightness)
{
if (brightness > 12)
{
brightness = 12;
}
uint8_t buf = Read8bit( 0x28 );
Write1Byte( 0x28 , ((buf & 0x0f) | (brightness << 4)) );
}
void AxpPowerUp()
{
Wire1.begin(21, 22);
Wire1.setClock(400000);
// Set LDO2 & LDO3(TFT_LED & TFT) 3.0V
Write1Byte(0x28, 0xcc);
// Set ADC sample rate to 200hz
Write1Byte(0x84, 0b11110010);
// Set ADC to All Enable
Write1Byte(0x82, 0xff);
// Bat charge voltage to 4.2, Current 100MA
Write1Byte(0x33, 0xc0);
// Depending on configuration enable LDO2, LDO3, DCDC1, DCDC3.
byte buf = (Read8bit(0x12) & 0xef) | 0x4D;
// if(disableLDO3) buf &= ~(1<<3);
// if(disableLDO2) buf &= ~(1<<2);
// if(disableDCDC3) buf &= ~(1<<1);
// if(disableDCDC1) buf &= ~(1<<0);
Write1Byte(0x12, buf);
// 128ms power on, 4s power off
Write1Byte(0x36, 0x0C);
if (1) //if(!disableRTC)
{
// Set RTC voltage to 3.3V
Write1Byte(0x91, 0xF0);
// Set GPIO0 to LDO
Write1Byte(0x90, 0x02);
}
// Disable vbus hold limit
Write1Byte(0x30, 0x80);
// Set temperature protection
Write1Byte(0x39, 0xfc);
// Enable RTC BAT charge
// Write1Byte(0x35, 0xa2 & (disableRTC ? 0x7F : 0xFF));
Write1Byte(0x35, 0xa2);
// Enable bat detection
Write1Byte(0x32, 0x46);
// Set Power off voltage 3.0v
Write1Byte(0x31 , (Read8bit(0x31) & 0xf8) | (1 << 2));
} /* AxpPowerUp() */
void lightSleep(uint64_t time_in_us)
{
esp_sleep_enable_timer_wakeup(time_in_us);
esp_light_sleep_start();
}
// Called for each device found during a BLE scan by the client
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks
{
void onResult(BLEAdvertisedDevice advertisedDevice) {
// Serial.printf("Scan Result: %s \n", advertisedDevice.toString().c_str());
const char *szName = advertisedDevice.getName().c_str();
// Serial.printf("Name = %s\n", szName);
if (memcmp(szName, "RDL52832", 8) == 0) {
const char *s = service_data.c_str();
int i, iLen = service_data.length();
uint8_t *p = (uint8_t *)s; // unsigned data
service_data = advertisedDevice.getServiceData();
// Serial.printf("Advertised device info: %s \n", advertisedDevice.toString().c_str());
// Serial.printf("MAC: %s, service data len=%d\n", szAddr, iLen);
// if (iLen != 0) {
// for (i=0; i<iLen; i++) {
// Serial.printf("0x%02x,", p[i]);
// }
// Serial.printf("\n");
// }
// The iBeacon sends a large packet with multiple service UUIDs in it
// the ESP32 BLE library doesn't parse this so we need to ask for the raw payload
// and parse it outselves
iLen = advertisedDevice.getPayloadLength();
if (iLen != 0) {
// Serial.printf("payload size = %d\n", iLen);
p = (uint8_t *)advertisedDevice.getPayload();
iRSSI = advertisedDevice.getRSSI();
ParseScanData(p, iLen);
}
}
}
};
#endif // ESP32
void ParseScanData(uint8_t *p, int iLen)
{
int i;
if (memcmp(p, uciBeacon, 9) == 0) {// iBeacon info?
int txCalibratedPower = (int8_t)p[29]; // get the 1 meter RSSI value
// Serial.printf("iBeacon info - Tx @ 1M = %ddB, RSSI = %ddB\n", txCalibratedPower, iRSSI);
// Calculate estimated distance
// The RSSI value seems to drop off too quickly with the M5StickC
// compared to my Android phone
int ratio_dB = (txCalibratedPower - iRSSI)/4; // <-- fudge factor for ESP32
float ratio_linear = powf(10, (float)ratio_dB / 10.0f);
fDistance = sqrtf(ratio_linear);
// Serial.printf("Approximate distance = %f\n", fDistance);
for (i=0; i<iLen; i++) {
// Search for the start of the 0x0318 UUID with the data we want
if (p[i] == 0x13 && p[i+1] ==0x16 && p[i+2] == 0x18 && p[i+3] ==0x03) {
// Serial.print("UUID 0x0318 data received!");
i += 4; // start of the data we want
T = (((p[i] << 8) + p[i+1]) * 10) / 256; // 1 decimal place is enough precision
H = p[i+2]; // no need for fractions of a % because the sensor isn't that good anyway
if (T > iMaxT) iMaxT = T;
if (T < iMinT) iMinT = T;
if (H > iMaxH) iMaxH = H;
if (H < iMinH) iMinH = H;
X = (float)p[i+5] + (float)p[i+6]/10.0f + (float)p[i+7] / 100.0f;
if (p[i+4] == 1) X = -X;
Y = (float)p[i+9] + (float)p[i+10]/10.0f + (float)p[i+11] / 100.0f;
if (p[i+8] == 1) Y = -Y;
Z = (float)p[i+13] + (float)p[i+14]/10.0f + (float)p[i+15] / 100.0f;
if (p[i+12] == 1) Z = -Z;
// Dump the data as hex
// for (; i<iLen; i++) {
// Serial.printf("0x%02x,", p[i]);
// }
// Serial.printf("\n");
}
}
}
} /* ParseScanData() */
//
// Display all of the sensor info on the M5StickC LCD
//
void ShowInfo(void)
{
char szTemp[64];
sprintf(szTemp, "Temperature: %d.%01dC", T/10, T % 10);
spilcdWriteString(&lcd, 0, 4, szTemp, 0xffff, 0, FONT_12x16, DRAW_TO_LCD);
sprintf(szTemp, "Humidity: %d%%", H);
spilcdWriteString(&lcd, 0, 24, szTemp, 0xffff, 0, FONT_12x16, DRAW_TO_LCD);
sprintf(szTemp, "RSSI: %d, Dist: %f ", iRSSI, fDistance);
spilcdWriteString(&lcd, 0, 44, szTemp, 0xffe0, 0, FONT_12x16, DRAW_TO_LCD);
sprintf(szTemp, "X: %.2f ", X);
spilcdWriteString(&lcd, 0, 64, szTemp, 0x7e0, 0, FONT_16x16, DRAW_TO_LCD);
sprintf(szTemp, "Y: %.2f ", Y);
spilcdWriteString(&lcd, 0, 84, szTemp, 0x7e0, 0, FONT_16x16, DRAW_TO_LCD);
sprintf(szTemp, "Z: %.2f ", Z);
spilcdWriteString(&lcd, 0, 104, szTemp, 0x7e0, 0, FONT_16x16, DRAW_TO_LCD);
}
void setup() {
#ifdef HAL_ESP32_HAL_H_
AxpPowerUp();
AxpBrightness(9); // turn on backlight (0-12)
#endif
iMaxT = 0;
iMinT = 1000;
iMaxH = 0;
iMinH = 99;
spilcdSetTXBuffer(ucTXBuf, sizeof(ucTXBuf));
spilcdInit(&lcd, LCD_TYPE, FLAGS_NONE, 40000000, TFT_CS, TFT_DC, TFT_RST, -1, -1, TFT_MOSI, TFT_CLK); // M5Stick-V pin numbering, 40Mhz
spilcdSetOrientation(&lcd, LCD_ORIENTATION_90);
spilcdFill(&lcd, 0, DRAW_TO_LCD);
Serial.begin(115200);
while (!Serial) {};
Serial.println("About to start BLE");
#ifdef HAL_ESP32_HAL_H_
BLEDevice::init("ESP32BLE");
pBLEScan = BLEDevice::getScan(); //create new scan
// Serial.println("getScan returned");
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); //Call the class that is defined above
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value
#endif
#ifdef ARDUINO_ARDUINO_NANO33BLE
BLE.begin();
BLE.setLocalName("Nano33BLE");
#endif
} /* setup() */
void loop() {
#ifdef HAL_ESP32_HAL_H_
BLEDevice::init("ESP32BLE");
foundDevices = pBLEScan->start(5, false); //Scan for 5 seconds to find the Fitness band
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
pBLEScan->stop();
BLEDevice::deinit(false);
// lightSleep(10000000); // wait 10 seconds, then start another scan
#endif
#ifdef ARDUINO_ARDUINO_NANO33BLE
BLE.begin();
BLE.setLocalName("Nano33BLE");
BLE.scanForName("RDL52832", true);
long lTime = millis();
while ((millis() - lTime) < 5000L)
{
// check if a peripheral has been discovered
peripheral = BLE.available();
if (peripheral) // since we searched on the name, we know we found it
{
uint8_t ucTemp[256];
int iLen;
BLE.stopScan();
iRSSI = peripheral.rssi();
iLen = peripheral.getRawAdvertisement(ucTemp, sizeof(ucTemp));
ParseScanData(ucTemp, iLen);
} // peripheral located
}
BLE.end();
#endif
ShowInfo();
} /* loop */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment