Last active
October 6, 2021 01:56
-
-
Save bitbank2/90773114eee37bff40b82770c4ac0ce4 to your computer and use it in GitHub Desktop.
This file contains 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
// | |
// 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