Working Eddystone Beacon implementation in part7-bluetooth

This commit is contained in:
Adam Greenwood-Byrne 2020-08-05 21:30:54 +01:00
parent c299194f88
commit 65b85ada97
6 changed files with 125 additions and 46 deletions

View file

@ -104,29 +104,8 @@ The firmware is simply a sequence of HCI commands following this format:
We check each HCI command succeeds as we go, wait a second and then return. If it runs without error then we've loaded our firmware and we're ready to start some Bluetooth communications! We check each HCI command succeeds as we go, wait a second and then return. If it runs without error then we've loaded our firmware and we're ready to start some Bluetooth communications!
The Bluetooth spec Todo
------------------ ----
From here, you must tackle the [Bluetooth specification](https://www.bluetooth.com/specifications/bluetooth-core-specification/). You really owe it to yourself to begin with a good understanding of how Bluetooth works - something that is beyond the scope of my write-up! * Write up scanning implementation (receiving advertising reports)
* Write up advertising implementation (simple Eddystone Beacon)
I just picked some easy Bluetooth LE commands to implement for our "proof of life" (see the _LE CONTROLLER COMMANDS_ section of the spec):
* setLEeventmask
* setLEscanenable
* setLEscanparameters
Read through my implementation alongside the Bluetooth spec - you should see what's going on. Fortunately, these are all we need to start scanning for BLE devices around us. If I can show that I'm seeing other devices, then I've got my proof of life.
Implementing scanning
---------------------
If you look in _kernel.c_, you'll see the main bulk of our scanning code. Exactly as we described above, we first reset the chip, then load the firmware. After that, we set the LE event mask (tell the chip what we want to be notified about) and start scanning for events. `bt_update()` then polls the chip to see if any events are awaiting our attention.
There may be one or more **ad report** in each event, and each contains the 48-bit Bluetooth address of the associated device e.g. `AA:BB:CC:DD:EE:FF` (bd_addr) as well as the data. I found a great article on the [Advertising Data Format from Silicon Labs](https://www.silabs.com/community/wireless/bluetooth/knowledge-base.entry.html/2017/02/10/bluetooth_advertisin-hGsf). Reading this will help you interpret my decode routines.
Right now, I'm just printing the bd_addr (using the convenient `uart_hex` routine added to _io.c_), the ad report type & length, and the friendly/full device name if received (ad_type 0x08/0x09 respectively).
Proof of life!
--------------
And, when I build and run this, I see reports coming in from my neighbouring Bluetooth speaker - magic! _This is our first step towards low-level Bluetooth control of the RPi4._

View file

@ -39,6 +39,7 @@ void bt_writeByte(char byte)
{ {
while ((mmio_read(ARM_UART0_FR) & 0x20) != 0); while ((mmio_read(ARM_UART0_FR) & 0x20) != 0);
mmio_write(ARM_UART0_DR, (unsigned int)byte); mmio_write(ARM_UART0_DR, (unsigned int)byte);
uart_byte(byte);
} }
void bt_flushrx() void bt_flushrx()
@ -74,15 +75,17 @@ enum {
OGF_LE_CONTROL = 0x08, OGF_LE_CONTROL = 0x08,
OGF_VENDOR = 0x3f, OGF_VENDOR = 0x3f,
COMMAND_SET_BDADDR = 0x01,
COMMAND_RESET_CHIP = 0x03, COMMAND_RESET_CHIP = 0x03,
COMMAND_SET_BAUD = 0x18,
COMMAND_LOAD_FIRMWARE = 0x2e, COMMAND_LOAD_FIRMWARE = 0x2e,
HCI_COMMAND_PKT = 0x01, HCI_COMMAND_PKT = 0x01,
HCI_EVENT_PKT = 0x04, HCI_EVENT_PKT = 0x04,
COMMAND_COMPLETE_CODE = 0x0e, COMMAND_COMPLETE_CODE = 0x0e,
LL_SCAN_ACTIVE = 0x01 LL_SCAN_ACTIVE = 0x01,
LL_ADV_NONCONN_IND = 0x03
}; };
unsigned char empty[] = {}; unsigned char empty[] = {};
@ -91,12 +94,15 @@ int hciCommandBytes(unsigned char *opcodebytes, unsigned char *data, unsigned ch
{ {
unsigned char c=0; unsigned char c=0;
uart_writeText("HCI_START\n");
bt_writeByte(HCI_COMMAND_PKT); bt_writeByte(HCI_COMMAND_PKT);
bt_writeByte(opcodebytes[0]); bt_writeByte(opcodebytes[0]);
bt_writeByte(opcodebytes[1]); bt_writeByte(opcodebytes[1]);
bt_writeByte(length); bt_writeByte(length);
uart_writeText("\n______ DATA:\n");
while (c++<length) bt_writeByte(*data++); while (c++<length) bt_writeByte(*data++);
uart_writeText("\nHCI_END\n");
if (bt_waitReadByte() != HCI_EVENT_PKT) return 0; if (bt_waitReadByte() != HCI_EVENT_PKT) return 0;
if (bt_waitReadByte() != COMMAND_COMPLETE_CODE) return 0; if (bt_waitReadByte() != COMMAND_COMPLETE_CODE) return 0;
@ -117,6 +123,10 @@ int hciCommand(unsigned short ogf, unsigned short ocf, unsigned char *data, unsi
return hciCommandBytes(opcodebytes, data, length); return hciCommandBytes(opcodebytes, data, length);
} }
void bt_reset() {
if (!hciCommand(OGF_HOST_CONTROL, COMMAND_RESET_CHIP, empty, 0)) uart_writeText("bt_reset() failed\n");
}
void bt_loadfirmware() void bt_loadfirmware()
{ {
if (!hciCommand(OGF_VENDOR, COMMAND_LOAD_FIRMWARE, empty, 0)) uart_writeText("loadFirmware() failed\n"); if (!hciCommand(OGF_VENDOR, COMMAND_LOAD_FIRMWARE, empty, 0)) uart_writeText("loadFirmware() failed\n");
@ -142,6 +152,35 @@ void bt_loadfirmware()
wait_msec(0x100000); wait_msec(0x100000);
} }
void bt_setbaud()
{
static unsigned char params[] = { 0, 0, 0x00, 0xc2, 0x01, 0x00 }; // little endian, 115200
if (!hciCommand(OGF_VENDOR, COMMAND_SET_BAUD, params, 6)) uart_writeText("bt_setbaud() failed\n");
}
void bt_setbdaddr()
{
static unsigned char params[] = { 0xee, 0xff, 0xc0, 0xee, 0xff, 0xc0 }; // reversed
if (!hciCommand(OGF_VENDOR, COMMAND_SET_BDADDR, params, 6)) uart_writeText("bt_setbdaddr() failed\n");
}
void bt_getbdaddr(unsigned char *bdaddr) {
bt_writeByte(HCI_COMMAND_PKT);
bt_writeByte(0x09);
bt_writeByte(0x10);
bt_writeByte(0x00);
if (bt_waitReadByte() != HCI_EVENT_PKT) return;
if (bt_waitReadByte() != COMMAND_COMPLETE_CODE) return;
if (bt_waitReadByte() != 0x0a) return;
if (bt_waitReadByte() != 1) return;
if (bt_waitReadByte() != 0x09) return;
if (bt_waitReadByte() != 0x10) return;
if (bt_waitReadByte() != 0x00) return;
for (int c=0;c<6;c++) bdaddr[c] = bt_waitReadByte();
}
void setLEeventmask(unsigned char mask) void setLEeventmask(unsigned char mask)
{ {
unsigned char params[] = { mask, 0, 0, 0, 0, 0, 0, 0 }; unsigned char params[] = { mask, 0, 0, 0, 0, 0, 0, 0 };
@ -158,6 +197,35 @@ void setLEscanparameters(unsigned char type, unsigned char linterval, unsigned c
if (!hciCommand(OGF_LE_CONTROL, 0x0b, params, 7)) uart_writeText("setLEscanparameters failed\n"); if (!hciCommand(OGF_LE_CONTROL, 0x0b, params, 7)) uart_writeText("setLEscanparameters failed\n");
} }
void setLEadvertenable(unsigned char state) {
unsigned char params[] = { state };
uart_writeText("doing the HCIcommand\n");
if (!hciCommand(OGF_LE_CONTROL, 0x0a, params, 1)) uart_writeText("setLEadvertenable failed\n");
}
void setLEadvertparameters(unsigned char type, unsigned char linterval_min, unsigned char hinterval_min, unsigned char linterval_max, unsigned char hinterval_max, unsigned char own_address_type, unsigned char filter_policy) {
unsigned char params[16] = { linterval_min, hinterval_min, linterval_max, hinterval_max, type, own_address_type, 0, 0, 0, 0, 0, 0, 0, 0x07, filter_policy };
uart_writeText("doing the HCIcommand\n");
if (!hciCommand(OGF_LE_CONTROL, 0x06, params, 15)) uart_writeText("setLEadvertparameters failed\n");
}
void setLEadvertdata() {
static unsigned char params[] = { 0x19,
0x02, 0x01, 0x06,
0x03, 0x03, 0xAA, 0xFE,
0x11, 0x16, 0xAA, 0xFE, 0x10, 0x00, 0x03, 0x69, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x69, 0x6d, 0x2e, 0x65, 0x73,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
if (!hciCommand(OGF_LE_CONTROL, 0x08, params, 32)) uart_writeText("setLEadvertdata failed\n");
}
void stopScanning() {
setLEscanenable(0, 0);
}
void stopAdvertising() {
setLEadvertenable(0);
}
void startActiveScanning() { void startActiveScanning() {
float BleScanUnitsPerSecond = 1600; float BleScanUnitsPerSecond = 1600;
float BleScanInterval = 0.8; float BleScanInterval = 0.8;
@ -170,10 +238,15 @@ void startActiveScanning() {
setLEscanenable(1, 0); setLEscanenable(1, 0);
} }
void stopScanning() { void startActiveAdvertising() {
setLEscanenable(0, 0); float advertMinFreq = 100; // every 48ms
} float advertMaxFreq = 100; // every 48ms
float bleGranularity = 0.625;
void bt_reset() { unsigned int min_interval = advertMinFreq / bleGranularity;
if (!hciCommand(OGF_HOST_CONTROL, COMMAND_RESET_CHIP, empty, 0)) uart_writeText("bt_reset() failed\n"); unsigned int max_interval = advertMaxFreq / bleGranularity;
setLEadvertparameters(LL_ADV_NONCONN_IND, lo(min_interval), hi(min_interval), lo(max_interval), hi(max_interval), 0, 0);
setLEadvertdata();
setLEadvertenable(1);
} }

View file

@ -1,7 +1,11 @@
void bt_reset(); void bt_reset();
void bt_init();
void bt_loadfirmware(); void bt_loadfirmware();
void setLEeventmask(unsigned char mask); void bt_setbaud();
void startActiveScanning(); void bt_setbdaddr();
void bt_getbdaddr(unsigned char *bdaddr);
void bt_init();
unsigned int bt_isReadByteReady(); unsigned int bt_isReadByteReady();
unsigned char bt_readByte(); unsigned char bt_readByte();
void setLEeventmask(unsigned char mask);
void startActiveScanning();
void startActiveAdvertising();

View file

@ -177,3 +177,17 @@ void uart_hex(unsigned int d) {
uart_writeByteBlockingActual(n); uart_writeByteBlockingActual(n);
} }
} }
void uart_byte(unsigned char b) {
unsigned int n;
int c;
for(c=4;c>=0;c-=4) {
// get highest tetrad
n=(b>>c)&0xF;
// 0-9 => '0'-'9', 10-15 => 'A'-'F'
n+=n>9?0x37:0x30;
uart_writeByteBlockingActual(n);
}
uart_writeByteBlockingActual(' ');
}

View file

@ -11,3 +11,4 @@ void mmio_write(long reg, unsigned int val);
unsigned int mmio_read(long reg); unsigned int mmio_read(long reg);
void gpio_useAsAlt3(unsigned int pin_number); void gpio_useAsAlt3(unsigned int pin_number);
void uart_hex(unsigned int d); void uart_hex(unsigned int d);
void uart_byte(unsigned char b);

View file

@ -60,17 +60,6 @@ unsigned char *poll()
return 0; return 0;
} }
void print_bdaddr(long bd_addr)
{
unsigned char byte;
for (int c=9;c>=4;c--) {
byte = (unsigned char)((bd_addr >> ((c-4)*8) & 0xff));
uart_hex(byte);
if (c != 4) uart_writeText(":");
}
}
void bt_update() void bt_update()
{ {
unsigned char *buf; unsigned char *buf;
@ -132,8 +121,27 @@ void main()
uart_writeText("bt_loadfirmware()\n"); uart_writeText("bt_loadfirmware()\n");
bt_loadfirmware(); bt_loadfirmware();
uart_writeText("bt_setbaud()\n");
bt_setbaud();
uart_writeText("bt_setbdaddr()\n");
bt_setbdaddr();
/*
uart_writeText("startActiveScanning()\n");
setLEeventmask(0xff); setLEeventmask(0xff);
startActiveScanning(); startActiveScanning();
*/
unsigned char local_addr[6];
uart_writeText("bt_getbdaddr()\n");
bt_getbdaddr(local_addr);
uart_writeText("\nBD_ADDR is ");
for (int c=5;c>=0;c--) uart_byte(local_addr[c]);
uart_writeText("\n");
uart_writeText("startActiveAdvertising()\n");
startActiveAdvertising();
uart_writeText("Waiting for input...\n"); uart_writeText("Waiting for input...\n");
while (1) { while (1) {