This was purely an agentic AI exercise as I was curious to learn how fast I could get my AI agent to modify an Arduino version of bluekitchen's btstack I had found online. I had no way of testing this btstack version with other MCU's so thought to give it a go anyway.
Well, it took a couple of hours to crack it, with the majority time spent testing 2 examples. The impressive part is that it started by (re?)creating a "HAL" of sorts for the MAX32630FTHR in seconds once I had pointed it to be previous manual attempt of uploading the service pack and configuring the RX and TX pins correctly. It worked first time without error.
Header: hal_max32630fthr.h
#ifndef HAL_MAX32630FTHR_H
#define HAL_MAX32630FTHR_H
#include <stdint.h>
#if defined __cplusplus
extern "C" {
#endif
void max32630fthr_bluetooth_setup(void);
void max32630fthr_bluetooth_set_baud(uint32_t baud);
#if defined __cplusplus
}
#endif
#endif
Source: hal_max32630fthr.cpp
#include "hal_max32630fthr.h"
#if defined(ARDUINO_MAX32630FTHR) || defined(TARGET_MAX32630FTHR) || defined(MAX32630FTHR) || defined(MAX32630)
#include <Arduino.h>
#include "pwrseq_regs.h"
#include "rtc_regs.h"
#include "ioman_regs.h"
#include "gpio_regs.h"
// Pin Definitions for MAX32630FTHR
#define BLE_SHUTDOWN_PIN P1_6 // nSHUTD (Active Low)
#define M4_RTS_OUT_TO_BLE P0_2 // M4 RTS Output
#define BLE_RTS_IN_TO_M4 P0_3 // BLE Module RTS Input (M4 CTS)
#define M4_TX_PIN P0_0 // UART0 TX (Mapping B)
#define M4_RX_PIN P0_1 // UART0 RX (Mapping B)
extern "C" void max32630fthr_bluetooth_setup(void) {
// --- 1. SET VOLTAGES ---
useVDDIO(BLE_SHUTDOWN_PIN);
useVDDIO(M4_TX_PIN);
useVDDIO(M4_RX_PIN);
useVDDIO(M4_RTS_OUT_TO_BLE);
useVDDIO(BLE_RTS_IN_TO_M4);
// --- 2. INITIALIZE HARDWARE ---
// Configure 32.768 kHz Clock (P1.7)
MXC_RTCCFG->clk_ctrl |= MXC_F_RTC_CLK_CTRL_NANO_EN;
MXC_RTCCFG->osc_ctrl |= MXC_F_RTC_OSC_CTRL_OSC_WARMUP_ENABLE;
MXC_RTCTMR->prescale = MXC_V_RTC_PRESCALE_DIV_2_0;
MXC_PWRSEQ->reg4 |= MXC_F_PWRSEQ_REG4_PWR_PSEQ_32K_EN;
// Prepare Pins: Set M4 RTS LOW BEFORE the module wakes up to signal "Ready"
pinMode(M4_RTS_OUT_TO_BLE, OUTPUT);
digitalWrite(M4_RTS_OUT_TO_BLE, LOW);
pinMode(BLE_RTS_IN_TO_M4, INPUT);
pinMode(M4_RX_PIN, INPUT);
// Pulse Reset (nSHUTD)
pinMode(BLE_SHUTDOWN_PIN, OUTPUT);
digitalWrite(BLE_SHUTDOWN_PIN, LOW);
delay(100);
digitalWrite(BLE_SHUTDOWN_PIN, HIGH);
// Give the TI Controller time to wake up and stabilize
delay(500);
}
extern "C" void max32630fthr_bluetooth_set_baud(uint32_t baud) {
Serial0.begin(baud);
// Force IOMAN to Mapping B (Crossover).
// Serial0.begin() can revert to Mapping A under the hood. We force it back!
MXC_IOMAN->uart0_req = 0x11;
while (MXC_IOMAN->uart0_ack != 0x11) continue;
}
#endif // MAX32630FTHR
Then it was a case of adding in the service pack info for the CC256x.
Finally, it was tweaking a couple of files to align them with the MAX32630FTHR. The main file that was tweaked was bsp_arduino_em9301.cpp.
/**
* Arduino + Energia Wrapper for BTstack
*/
#if !defined(ARDUINO)
#error "Not compiling for Arduino/Energia"
#endif
#include <Arduino.h>
#ifdef ENERGIA
#include <Energia.h>
#endif
#include <SPI.h>
#include "btstack/hal_uart_dma.h"
#if defined(__arm__)
// --- TRUE UART IMPLEMENTATION FOR ARM / MAX32630FTHR ---
#include "utility/hal_max32630fthr.h"
// Define the hardware serial port connected to your CC256x
#if defined(ARDUINO_MAX32630FTHR) || defined(TARGET_MAX32630FTHR) || defined(MAX32630FTHR) || defined(MAX32630)
#define BT_SERIAL Serial0
#else
#define BT_SERIAL Serial1
#endif
static uint16_t bytes_to_read = 0;
static uint8_t * rx_buffer_ptr = NULL;
static uint16_t bytes_to_write = 0;
static uint8_t * tx_buffer_ptr = NULL;
static void dummy_handler(void){};
static void (*rx_done_handler)(void) = dummy_handler;
static void (*tx_done_handler)(void) = dummy_handler;
// Large secondary ring buffer to absorb Arduino loop() / Serial.print() latency
#define RX_RING_BUFFER_SIZE 1024
static uint8_t rx_ring_buffer[RX_RING_BUFFER_SIZE];
static volatile uint16_t rx_ring_head = 0;
static volatile uint16_t rx_ring_tail = 0;
extern "C" __attribute__((weak)) void max32630fthr_bluetooth_setup(void) {}
extern "C" __attribute__((weak)) void max32630fthr_bluetooth_set_baud(uint32_t baud) {
BT_SERIAL.begin(baud);
}
extern "C" void hal_uart_dma_init(void){
max32630fthr_bluetooth_setup();
max32630fthr_bluetooth_set_baud(115200);
}
extern "C" int hal_uart_dma_set_baud(uint32_t baud){
max32630fthr_bluetooth_set_baud(baud);
return 0;
}
extern "C" void hal_uart_dma_send_block(const uint8_t *buffer, uint16_t length){
tx_buffer_ptr = (uint8_t *) buffer;
bytes_to_write = length;
}
extern "C" void hal_uart_dma_receive_block(uint8_t *buffer, uint16_t length){
rx_buffer_ptr = buffer;
bytes_to_read = length;
}
static inline void drain_hardware_buffer(void) {
// Drain Hardware UART buffer into our massive Ring Buffer to prevent overflows
while (BT_SERIAL.available() > 0) {
uint16_t next_head = (rx_ring_head + 1) % RX_RING_BUFFER_SIZE;
if (next_head != rx_ring_tail) {
rx_ring_buffer[rx_ring_head] = BT_SERIAL.read();
rx_ring_head = next_head;
} else {
break; // Ring buffer full!
}
}
}
extern "C" void hal_uart_dma_process(void){
// 1. Process Transmit (Blocking write for reliability during init script upload)
if (bytes_to_write > 0) {
BT_SERIAL.write(tx_buffer_ptr, bytes_to_write);
bytes_to_write = 0;
(*tx_done_handler)();
}
// 2. Drain Hardware UART buffer
drain_hardware_buffer();
// 3. Process Receive (Feed BTstack from Ring Buffer)
while (bytes_to_read > 0 && rx_ring_head != rx_ring_tail) {
*rx_buffer_ptr++ = rx_ring_buffer[rx_ring_tail];
rx_ring_tail = (rx_ring_tail + 1) % RX_RING_BUFFER_SIZE;
bytes_to_read--;
if (bytes_to_read == 0) {
(*rx_done_handler)();
}
}
}
// Override Arduino's weak yield() to aggressively drain the UART buffer
// while the user sketch is blocked on delay() or Serial.print()!
extern "C" void yield(void) {
drain_hardware_buffer();
}
extern "C" void hal_uart_dma_set_sleep(uint8_t sleep){}
extern "C" void hal_uart_dma_set_csr_irq_handler( void (*csr_irq_handler)(void)){}
extern "C" void hal_uart_dma_set_block_received( void (*block_handler)(void)){ rx_done_handler = block_handler ? block_handler : dummy_handler; }
extern "C" void hal_uart_dma_set_block_sent( void (*block_handler)(void)){ tx_done_handler = block_handler ? block_handler : dummy_handler; }
extern "C" uint32_t hal_time_ms(void){ return millis(); }
#else
// --- ORIGINAL EM9301 SPI IMPLEMENTATION (AVR) ---
#define HAVE_SHUTDOWN
#ifdef ENERGIA
// CMM 9301 Configuration for TI Launchpad
#define PIN_SPI_SCK 7
#define PIN_CS 8
#define PIN_SHUTDOWN 11
#define PIN_IRQ_DATA 13
#define PIN_SPI_MISO 14
#define PIN_SPI_MOSI 15
#else // ARDUINO
// CMM 9301 Configuration for Arduino
#define PIN_IRQ_DATA 2
#define PIN_CS 4
#define PIN_SHUTDOWN 5
// -- SPI defines for Arduino Mega
#ifndef PIN_SPI_MISO
#define PIN_SPI_MISO 50
#endif
#ifndef PIN_SPI_MOSI
#define PIN_SPI_MOSI 51
#endif
#ifndef PIN_SPI_SCK
#define PIN_SPI_SCK 52
#endif
#endif
// rx state
static uint16_t bytes_to_read = 0;
static uint8_t * rx_buffer_ptr = 0;
// tx state
static uint16_t bytes_to_write = 0;
static uint8_t * tx_buffer_ptr = 0;
// handlers
static void dummy_handler(void){};
static void (*rx_done_handler)(void) = dummy_handler;
static void (*tx_done_handler)(void) = dummy_handler;
static void bt_setup(void){
pinMode(PIN_CS, OUTPUT);
pinMode(PIN_SPI_MOSI, OUTPUT);
pinMode(PIN_SPI_SCK, OUTPUT);
pinMode(PIN_SHUTDOWN, OUTPUT);
pinMode(PIN_IRQ_DATA, INPUT);
digitalWrite(PIN_CS, HIGH);
digitalWrite(PIN_SPI_MOSI, LOW);
digitalWrite(PIN_SHUTDOWN, HIGH);
// SPI settings are reset in SPI.begin() - calls hang on Arduino Zero, too.
// SPI.setBitOrder(MSBFIRST);
// SPI.setDataMode(SPI_MODE0);
// SPI.end();
}
#ifdef HAVE_SHUTDOWN
static void bt_power_cycle(void){
// power cycle. set CPU outputs to input to not power EM9301 via IOs
// pinMode(PIN_SPI_MOSI, INPUT);
// pinMode(PIN_CS, INPUT);
pinMode(PIN_CS, OUTPUT);
pinMode(PIN_SPI_MOSI, OUTPUT);
pinMode(PIN_SPI_SCK, OUTPUT);
pinMode(PIN_SHUTDOWN, OUTPUT);
digitalWrite(PIN_CS, LOW);
digitalWrite(PIN_SPI_MOSI, LOW);
digitalWrite(PIN_SPI_SCK, LOW);
digitalWrite(PIN_SHUTDOWN, HIGH);
delay(500);
pinMode(PIN_SPI_MOSI, OUTPUT);
pinMode(PIN_CS, OUTPUT);
digitalWrite(PIN_SPI_MOSI, LOW);
digitalWrite(PIN_CS, HIGH);
digitalWrite(PIN_SHUTDOWN, LOW);
delay(1000);
}
#endif
#ifndef HAVE_SHUTDOWN
static void bt_send_illegal(void){
digitalWrite(PIN_SPI_MOSI, HIGH);
digitalWrite(PIN_CS, LOW);
printf("Illegal start\n");
SPI.begin();
int i;
for (i=0;i<255;i++){
SPI.transfer(0xff);
printf(".");
}
SPI.end();
printf("\nIllegal stop\n");
digitalWrite(PIN_CS, HIGH);
}
static void bt_flush_input(void){
digitalWrite(PIN_SPI_MOSI, LOW);
digitalWrite(PIN_CS, LOW);
SPI.begin();
while (digitalRead(PIN_IRQ_DATA) == HIGH){
SPI.transfer(0x00);
}
SPI.end();
digitalWrite(PIN_CS, HIGH);
}
static void bt_send_reset(void){
digitalWrite(PIN_SPI_MOSI, HIGH);
digitalWrite(PIN_CS, LOW);
SPI.begin();
SPI.transfer(0x01);
SPI.transfer(0x03);
SPI.transfer(0x0c);
SPI.transfer(0x00);
SPI.end();
digitalWrite(PIN_CS, HIGH);
}
#endif
static void bt_try_send(void){
if (!bytes_to_write) return;
// activate module
pinMode(PIN_SPI_MOSI, OUTPUT);
digitalWrite(PIN_SPI_MOSI, HIGH);
digitalWrite(PIN_CS, LOW);
// module ready
int tx_done = 0;
if (digitalRead(PIN_SPI_MISO) == HIGH){
// printf("Sending: ");
SPI.begin();
while (bytes_to_write){
// printf("%02x ", *tx_buffer_ptr);
SPI.transfer(*tx_buffer_ptr);
tx_buffer_ptr++;
bytes_to_write--;
}
SPI.end();
// printf(".\n");
tx_done = 1;
}
// deactivate module
digitalWrite(PIN_CS, HIGH);
digitalWrite(PIN_SPI_MOSI, LOW);
pinMode(PIN_SPI_MOSI, OUTPUT);
// notify upper layer
if (tx_done) {
(*tx_done_handler)();
}
}
static int bt_try_read(void){
// check if data available and buffer is ready
if (digitalRead(PIN_IRQ_DATA) == LOW) return 0;
if (bytes_to_read == 0) return 0;
int num_bytes_read = 0;
// printf("Reading (%u): ", bytes_to_read);
// activate module
digitalWrite(PIN_SPI_MOSI, LOW);
digitalWrite(PIN_CS, LOW);
SPI.begin();
do {
uint8_t byte_read = SPI.transfer(0x00);
// printf("%02x ", byte_read);
*rx_buffer_ptr = byte_read;
rx_buffer_ptr++;
bytes_to_read--;
num_bytes_read++;
} while (bytes_to_read > 0);
SPI.end();
digitalWrite(PIN_CS, HIGH);
// printf("\n");
// notify upper layer
(*rx_done_handler)();
return num_bytes_read;
}
extern "C" void hal_uart_dma_init(void){
bt_setup();
#ifdef HAVE_SHUTDOWN
bt_power_cycle();
#else
// bring EM9301 into defined state
bt_send_illegal();
bt_send_reset();
bt_flush_input();
#endif
}
extern "C" void hal_uart_dma_set_block_received( void (*block_handler)(void)){
rx_done_handler = block_handler;
}
extern "C" void hal_uart_dma_set_block_sent( void (*block_handler)(void)){
tx_done_handler = block_handler;
}
extern "C" void hal_uart_dma_set_csr_irq_handler( void (*csr_irq_handler)(void)){
// only used for eHCILL
}
extern "C" int hal_uart_dma_set_baud(uint32_t baud){
return 0;
}
extern "C" void hal_uart_dma_send_block(const uint8_t *buffer, uint16_t length){
// printf("send_block, bytes %u\n", length);
tx_buffer_ptr = (uint8_t *) buffer;
bytes_to_write = length;
}
extern "C" void hal_uart_dma_receive_block(uint8_t *buffer, uint16_t length){
rx_buffer_ptr = buffer;
bytes_to_read = length;
}
extern "C" void hal_uart_dma_set_sleep(uint8_t sleep){
// not needed for SPI (doesn't need internal clock to work)
}
extern "C" void hal_uart_dma_process(void){
int num_bytes_read = bt_try_read();
if (num_bytes_read == 0){
bt_try_send();
}
}
extern "C" uint32_t hal_time_ms(void){
return millis();
}
#endif
Once the btstack library compiled it was onto creating two examples. Here I modified one example (LEPeripheral.ino) and created a new example (LEScanner.ino). The other examples in the btstack library were not tested and would need to be modified to get them to work with the MAX32630FTHR board.
The modified version of `LEPeripheral.ino` basically creates a LED service which allows you to modify the onboard RGB LED's colour, brightness and a blinking pattern. It does this through the use of two characteristics. I tried to add in a button service but the CC256b BLE device did not have enough memory. This was because I was using 128-bit UUID's. It's a well known problem with old (v4.x) BLE devices.
// LE Peripheral Example - Adapted for MAX32630FTHR
#include <BTstack.h>
// Bring in the native C BTstack headers
extern "C" {
#include "btstack_memory.h"
#include <btstack/run_loop.h>
#include <bt_control.h>
#include "btstack/hal_uart_dma.h"
#include "att_server.h"
// Expose the TI CC256x controller
extern bt_control_t * bt_control_cc256x_instance(void);
}
// GATT Characteristic Handles
uint16_t handle_rgb_color;
uint16_t handle_rgb_state;
// State Variables
uint8_t color_r = 0;
uint8_t color_g = 0;
uint8_t color_b = 255; // Default to Blue
uint8_t intensity = 100;
uint8_t state_mode = 0; // 0=Off, 1=Solid, 2=Blink
uint16_t state_period = 1000;
uint8_t state_duty = 50;
// Blink tracking
uint32_t last_blink_time = 0;
bool blink_state = false;
// Update physical RGB LED (Active LOW on MAX32630FTHR)
void updateLEDs(bool is_on) {
if (!is_on || state_mode == 0) {
// Active LOW: 255 turns the LED completely off
analogWrite(RED_LED, 255);
analogWrite(GREEN_LED, 255);
analogWrite(BLUE_LED, 255);
} else {
// Calculate intensity-scaled values
uint8_t r = 255 - ((uint16_t)color_r * intensity / 100);
uint8_t g = 255 - ((uint16_t)color_g * intensity / 100);
uint8_t b = 255 - ((uint16_t)color_b * intensity / 100);
analogWrite(RED_LED, r);
analogWrite(GREEN_LED, g);
analogWrite(BLUE_LED, b);
}
}
void setup(void){
Serial.begin(115200);
while(!Serial) continue;
Serial.println("\r\nStarting LEPeripheral setup for MAX32630FTHR...");
pinMode(RED_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(BLUE_LED, OUTPUT);
updateLEDs(false); // Start OFF
// set callbacks
BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback);
BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback);
BTstack.setGATTCharacteristicRead(gattReadCallback);
BTstack.setGATTCharacteristicWrite(gattWriteCallback);
// --- SETUP GATT DATABASE ---
// 1. rgbService
BTstack.addGATTService(new UUID("00000001-1111-2222-3333-444444444444"));
handle_rgb_color = BTstack.addGATTCharacteristicDynamic(
new UUID("00000002-1111-2222-3333-444444444444"),
ATT_PROPERTY_READ | ATT_PROPERTY_WRITE_WITHOUT_RESPONSE, 0);
handle_rgb_state = BTstack.addGATTCharacteristicDynamic(
new UUID("00000003-1111-2222-3333-444444444444"),
ATT_PROPERTY_READ | ATT_PROPERTY_WRITE_WITHOUT_RESPONSE, 0);
// Pass the official CC256X controller flag so it loads the TI Service patch!
BTstack.setup(BT_CONTROLLER_CC256X);
BTstack.startAdvertising();
}
void loop(void){
BTstack.loop();
// 1. Non-blocking Blink Logic
if (state_mode == 2) {
uint32_t now = millis();
uint32_t on_time = (uint32_t)state_period * state_duty / 100;
uint32_t off_time = state_period - on_time;
if (blink_state && (now - last_blink_time >= on_time)) {
blink_state = false;
last_blink_time = now;
updateLEDs(false);
} else if (!blink_state && (now - last_blink_time >= off_time)) {
blink_state = true;
last_blink_time = now;
updateLEDs(true);
}
}
}
void deviceConnectedCallback(BLEStatus status, BLEDevice *device) {
if (status == BLE_STATUS_OK) {
Serial.println("Device connected!");
}
}
void deviceDisconnectedCallback(BLEDevice * device){
Serial.println("Disconnected. Scanning resumes automatically.");
}
uint16_t gattReadCallback(uint16_t value_handle, uint8_t * buffer, uint16_t buffer_size){
if (value_handle == handle_rgb_color) {
if (buffer && buffer_size >= 4) {
buffer[0] = color_r;
buffer[1] = color_g;
buffer[2] = color_b;
buffer[3] = intensity;
}
return 4;
} else if (value_handle == handle_rgb_state) {
if (buffer && buffer_size >= 4) {
buffer[0] = state_mode;
buffer[1] = (state_period >> 8) & 0xFF;
buffer[2] = state_period & 0xFF;
buffer[3] = state_duty;
}
return 4;
}
return 0;
}
int gattWriteCallback(uint16_t value_handle, uint8_t *buffer, uint16_t size){
if (value_handle == handle_rgb_color && size >= 4) {
color_r = buffer[0];
color_g = buffer[1];
color_b = buffer[2];
intensity = buffer[3];
Serial.print("RGB updated: "); Serial.print(color_r); Serial.print(",");
Serial.print(color_g); Serial.print(","); Serial.print(color_b);
Serial.print(" @ "); Serial.print(intensity); Serial.println("%");
if (state_mode == 1 || (state_mode == 2 && blink_state)) {
updateLEDs(true);
}
} else if (value_handle == handle_rgb_state && size >= 4) {
state_mode = buffer[0];
state_period = (buffer[1] << 8) | buffer[2];
state_duty = buffer[3];
Serial.print("State updated: Mode="); Serial.print(state_mode);
Serial.print(", Period="); Serial.print(state_period);
Serial.print("ms, Duty="); Serial.print(state_duty); Serial.println("%");
last_blink_time = millis();
blink_state = true;
if (state_mode == 0) {
updateLEDs(false);
} else {
updateLEDs(true);
}
}
return 0;
}
The other example was a scanning app. This sketch scanned for a particular 128-bit UUID. This app revealed a few low level issues which were readily resolved.
/*
* Stripped down LE Central Scanner
* Scans for a specific 16-bit Service UUID and retrieves the MAC address.
* Does NOT initiate a connection.
*/
// Target Luggage Tag Service UUID (16-bit)
// Note. It will also search for iBeacons using same UUID.
// Use the full 128-bit Bluetooth Base UUID to safely bypass the 16-bit parsing bug
#include <BTstack.h>
// Bring in the native C BTstack headers
extern "C" {
#include "btstack_memory.h"
#include <btstack/run_loop.h>
#include <bt_control.h>
#include "btstack/hal_uart_dma.h"
// Expose the TI CC256x controller and the H4 UART transport
extern bt_control_t * bt_control_cc256x_instance(void);
extern hci_transport_t * hci_transport_h4_dma_instance(void);
}
UUID targetLuggageTagUUID("0000FEED-0000-1000-8000-00805F9B34FB");
// Hardware definitions
const int LED_PIN = LED_BUILTIN; // Triggers the MAX32630FTHR's onboard LED
uint32_t lastTagSeenTime = 0;
// Globals to pass data from the fast callback to the main loop
bool targetFound = false;
int targetRssi = 0;
char targetMac[18] = {0};
void setup(void){
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
// Wait for the native USB serial port to connect before booting BTstack
while(!Serial) continue;
Serial.println("\r\nStarting LECentral Scanner");
// Only register the advertisement callback (used for scanning)
BTstack.setBLEAdvertisementCallback(advertisementCallback);
// 1. Disable packet logging now that everything is working!
// (Printing massive raw hex dumps to USB causes the hardware UART buffer to overflow and drop packets)
// BTstack.enablePacketLogger();
// BTstack.enableDebugLogger();
// 2. Use the official setup (now that we patched BTstack.cpp to use the CC256x!)
BTstack.setup(BT_CONTROLLER_CC256X);
BTstack.bleStartScanning();
}
void loop(void){
// Must be called continuously to process Bluetooth events
BTstack.loop();
// Turn off LED if we haven't seen the target tag in the last 3 seconds
if (millis() - lastTagSeenTime > 3000) {
digitalWrite(LED_PIN, HIGH); // Note: Built-in LEDs are often active-LOW on Maxim boards!
}
// Process the discovery safely outside the callback
if (targetFound) {
targetFound = false; // clear the flag
lastTagSeenTime = millis();
digitalWrite(LED_PIN, LOW); // Turn ON the LED!
// Rough distance calculation (assuming Tx Power at 1 meter is ~ -65 dBm)
// Distance = 10 ^ ((TxPower - RSSI) / (10 * N)) -> N = Path loss exponent (usually ~2.5)
float distance = pow(10.0, (-65.0 - targetRssi) / (10.0 * 2.5))*0.6;
static uint32_t lastPrintTime = 0;
if (millis() - lastPrintTime > 100) {
lastPrintTime = millis();
Serial.print("\r\n *** Target Tag Found! MAC: ");
Serial.print(targetMac);
Serial.print(" | RSSI: ");
Serial.print(targetRssi);
Serial.print(" dBm | Est. Distance: ");
Serial.print(distance);
Serial.println(" meters");
}
}
}
void advertisementCallback(BLEAdvertisement *bleAdvertisement) {
bool isTarget = false;
// 1. Check if the advertisement contains our target as a standard GATT Service UUID
if (bleAdvertisement->containsService(&targetLuggageTagUUID)) {
isTarget = true;
}
// 2. Check if the beacon is an iBeacon, which hides the UUID inside Manufacturer Specific Data
if (bleAdvertisement->isIBeacon()) {
if (targetLuggageTagUUID.matches((UUID*)bleAdvertisement->getIBeaconUUID())) {
isTarget = true;
}
}
if (isTarget) {
// Quickly copy the data to our globals and get out of the callback!
targetRssi = bleAdvertisement->getRssi();
// getAddressString() uses a single static buffer internally in BTstack,
// so we copy it to our own safe array using strncpy.
strncpy(targetMac, bleAdvertisement->getBdAddr()->getAddressString(), sizeof(targetMac));
targetFound = true;
}
}
Here is a video demonstration of this sketch:
The whole library can be found here should anyone find it useful - I just compressed it as a zip file rather than uploading it onto Github: https://drive.google.com/file/d/18-V3d7NckCjgbqI09-9MtiYFfE9YE3BZ/view?usp=sharing
I hope you find this useful. Any question please feel free to ask.