Skip to content

Commit a8ee795

Browse files
committed
Initial commit.
0 parents  commit a8ee795

File tree

5 files changed

+378
-0
lines changed

5 files changed

+378
-0
lines changed

README.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# microbit-beacon-finder
2+
3+
The micro:bit finds various of BLE Beacons, and display the ID to LEDs.
4+
5+
## Supported Beacons
6+
* iBeacon
7+
* AltBeacon
8+
* Eddystone-UID
9+
* Eddystone-URL
10+
11+
## How to Build
12+
This project uses yotta to build, not pxt.
13+
This project uses the another SoftDevice(S130). That enables BLE Central feature.
14+
15+
Follow these steps to build the project.
16+
** Don't forget copying NRF51822_S130.ld to NRF51822.ld ! **
17+
18+
```bash
19+
# set target to use S130 SoftDevice.
20+
yt target bbc-microbit-classic-gcc-s130
21+
22+
# the linker uses `NRF51822.ld` file, then copy `NRF51822_S130.ld` to `NRF51822.ld`.
23+
cp yotta_targets/bbc-microbit-classic-gcc-s130/ld/NRF51822_S130.ld yotta_targets/bbc-microbit-classic-gcc-s130/ld/NRF51822.ld
24+
25+
# build the project
26+
yt build
27+
28+
# transfer the hex file to micro:bit. (for example, macOS X)
29+
cp build/bbc-microbit-classic-gcc-s130/source/microbit-ble-bridge-combined.hex /Volumes/MICROBIT/microbit-ble-bridge-combined.hex
30+
```

config.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"microbit": {
3+
"S130": true,
4+
"configfile": "source/MicroBitCustomConfig.h"
5+
}
6+
}

module.json

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "microbit-ble-bridge",
3+
"version": "0.0.2-dev01",
4+
"description": "A BLE to USB Serial bridge for the micro:bit, using a micro:bit",
5+
"licenses": [
6+
{
7+
"url": "https://spdx.org/licenses/Apache-2.0",
8+
"type": "Apache-2.0"
9+
}
10+
],
11+
"dependencies": {
12+
"mbed-classic": "lancaster-university/mbed-classic#microbit_hfclk+mb6",
13+
"ble": "lancaster-university/BLE_API#v2.5.0+mb3",
14+
"ble-nrf51822": "lancaster-university/nrf51822#v2.5.0+mb7",
15+
"nrf51-sdk": "lancaster-university/nrf51-sdk#v2.2.0+mb4",
16+
"microbit-dal": "lancaster-university/microbit-dal#v2.0.0-rc10",
17+
"microbit": "lancaster-university/microbit"
18+
},
19+
"targetDependencies": {},
20+
"bin": "./source"
21+
}
22+

source/MicroBitCustomConfig.h

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Custom Compile time configuration options for the micro:bit runtime.
3+
*/
4+
5+
#ifndef MICROBIT_CUSTOM_CONFIG_H
6+
#define MICROBIT_CUSTOM_CONFIG_H
7+
8+
//
9+
// We don't want to use any of the micro:bit BLE services, so we disable them
10+
11+
#define MICROBIT_BLE_ENABLED 0
12+
#define MICROBIT_BLE_BLUEZONE 0 // We don't need this either
13+
14+
// But we are going to bring up the S130 soft-device. The DAL assumes that
15+
// if MICROBIT_BLE_ENABLED is not set then the memory from the softdevice
16+
// can be re-used as extra heap, but we don't want this, so we set the memory
17+
// limits explicity
18+
19+
// The lowest address of memory that is safe to use as heap storage when BLE is ENABLED
20+
// Used to define the base of the heap when MICROBIT_HEAP_REUSE_SD is defined.
21+
#define MICROBIT_HEAP_BASE_BLE_ENABLED 0x20002500 // S130 build value
22+
#define MICROBIT_HEAP_BASE_BLE_DISABLED MICROBIT_HEAP_BASE_BLE_ENABLED // Force the MicrobitHeapAllocator not to use all the S130 memory
23+
24+
#endif

source/main.cpp

+296
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
#include "MicroBitCustomConfig.h"
2+
#include "MicroBit.h"
3+
#include "ble/DiscoveredCharacteristic.h"
4+
#include "ble/DiscoveredService.h"
5+
6+
#if YOTTA_CFG_MICROBIT_S130 != 1
7+
#error This code *only* works with the Nordic S130 softdevice
8+
#endif
9+
10+
#if CONFIG_ENABLED(MICROBIT_DBG)
11+
#error use of the serial port by MICROBIT_DBG clashes with our use of the serial port - not uspported
12+
#endif
13+
14+
MicroBit uBit;
15+
16+
void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params)
17+
{
18+
const uint8_t len = params->advertisingDataLen;
19+
if (len == 27)
20+
{
21+
// check iBeacon header
22+
if (
23+
params->advertisingData[0] == 0x1a &&
24+
params->advertisingData[1] == 0xff &&
25+
params->advertisingData[2] == 0x4c &&
26+
params->advertisingData[3] == 0x00 &&
27+
params->advertisingData[4] == 0x02 &&
28+
params->advertisingData[5] == 0x15)
29+
{
30+
char uuid[33]{};
31+
uint8_t unib{};
32+
uint8_t lnib{};
33+
for (int i = 0; i < 16; ++i)
34+
{
35+
unib = params->advertisingData[6 + i] >> 4;
36+
lnib = params->advertisingData[6 + i] & 0xf;
37+
38+
if (unib < 10)
39+
{
40+
uuid[i * 2] = '0' + unib;
41+
}
42+
else
43+
{
44+
uuid[i * 2] = 'A' + unib - 10;
45+
}
46+
if (lnib < 10)
47+
{
48+
uuid[i * 2 + 1] = '0' + lnib;
49+
}
50+
else
51+
{
52+
uuid[i * 2 + 1] = 'A' + lnib - 10;
53+
}
54+
}
55+
56+
uBit.display.scrollAsync(uuid);
57+
}
58+
}
59+
else if (len == 28)
60+
{
61+
// AltBeacon https://github.com/AltBeacon/spec
62+
// check AltBeacon header
63+
if (
64+
params->advertisingData[0] == 0x1b &&
65+
params->advertisingData[1] == 0xff &&
66+
params->advertisingData[4] == 0xbe &&
67+
params->advertisingData[5] == 0xac)
68+
{
69+
char beaconId[41]{};
70+
uint8_t unib{};
71+
uint8_t lnib{};
72+
for (int i = 0; i < 20; ++i)
73+
{
74+
unib = params->advertisingData[6 + i] >> 4;
75+
lnib = params->advertisingData[6 + i] & 0xf;
76+
77+
if (unib < 10)
78+
{
79+
beaconId[i * 2] = '0' + unib;
80+
}
81+
else
82+
{
83+
beaconId[i * 2] = 'A' + unib - 10;
84+
}
85+
if (lnib < 10)
86+
{
87+
beaconId[i * 2 + 1] = '0' + lnib;
88+
}
89+
else
90+
{
91+
beaconId[i * 2 + 1] = 'A' + lnib - 10;
92+
}
93+
}
94+
95+
uBit.display.scrollAsync(beaconId);
96+
}
97+
}
98+
else
99+
{
100+
if (
101+
params->advertisingData[0] == 0x03 &&
102+
params->advertisingData[1] == 0x03 &&
103+
params->advertisingData[2] == 0xaa &&
104+
params->advertisingData[3] == 0xfe)
105+
{
106+
// Eddystone https://github.com/google/eddystone/blob/master/protocol-specification.md
107+
if (params->advertisingData[8] == 0x00)
108+
{
109+
// Eddystone UID
110+
// [10bytes]-[6bytes]
111+
char beaconId[34]{};
112+
beaconId[20] = '-';
113+
uint8_t unib{};
114+
uint8_t lnib{};
115+
for (int i = 0; i < 10; ++i)
116+
{
117+
unib = params->advertisingData[10 + i] >> 4;
118+
lnib = params->advertisingData[10 + i] & 0xf;
119+
120+
if (unib < 10)
121+
{
122+
beaconId[i * 2] = '0' + unib;
123+
}
124+
else
125+
{
126+
beaconId[i * 2] = 'A' + unib - 10;
127+
}
128+
if (lnib < 10)
129+
{
130+
beaconId[i * 2 + 1] = '0' + lnib;
131+
}
132+
else
133+
{
134+
beaconId[i * 2 + 1] = 'A' + lnib - 10;
135+
}
136+
}
137+
for (int i = 10; i < 16; ++i)
138+
{
139+
unib = params->advertisingData[10 + i] >> 4;
140+
lnib = params->advertisingData[10 + i] & 0xf;
141+
142+
if (unib < 10)
143+
{
144+
beaconId[i * 2 + 1] = '0' + unib;
145+
}
146+
else
147+
{
148+
beaconId[i * 2 + 1] = 'A' + unib - 10;
149+
}
150+
if (lnib < 10)
151+
{
152+
beaconId[i * 2 + 2] = '0' + lnib;
153+
}
154+
else
155+
{
156+
beaconId[i * 2 + 2] = 'A' + lnib - 10;
157+
}
158+
}
159+
160+
uBit.display.scrollAsync(beaconId);
161+
}
162+
else if (params->advertisingData[8] == 0x10)
163+
{
164+
// Eddystone URL
165+
uint8_t length = params->advertisingData[4];
166+
if (length < 6)
167+
{
168+
// invalid data
169+
return;
170+
}
171+
ManagedString urlStr("");
172+
switch (params->advertisingData[10])
173+
{
174+
case 0x00:
175+
urlStr = urlStr + "http://www.";
176+
break;
177+
case 0x01:
178+
urlStr = urlStr + "https://www.";
179+
break;
180+
case 0x02:
181+
urlStr = urlStr + "http://";
182+
break;
183+
case 0x03:
184+
urlStr = urlStr + "https://";
185+
break;
186+
}
187+
for (int i = 0; i < length - 6; ++i)
188+
{
189+
uint8_t urlChar = params->advertisingData[11 + i];
190+
if (urlChar > 0x20 && urlChar < 0x7f)
191+
{
192+
urlStr = urlStr + ManagedString((const char)urlChar);
193+
}
194+
else
195+
{
196+
switch (urlChar)
197+
{
198+
case 0x00:
199+
urlStr = urlStr + ".com/";
200+
break;
201+
case 0x01:
202+
urlStr = urlStr + ".org/";
203+
break;
204+
case 0x02:
205+
urlStr = urlStr + ".edu/";
206+
break;
207+
case 0x03:
208+
urlStr = urlStr + ".net/";
209+
break;
210+
case 0x04:
211+
urlStr = urlStr + ".info/";
212+
break;
213+
case 0x05:
214+
urlStr = urlStr + ".biz/";
215+
break;
216+
case 0x06:
217+
urlStr = urlStr + ".gov/";
218+
break;
219+
case 0x07:
220+
urlStr = urlStr + ".com";
221+
break;
222+
case 0x08:
223+
urlStr = urlStr + ".org";
224+
break;
225+
case 0x09:
226+
urlStr = urlStr + ".edu";
227+
break;
228+
case 0x0a:
229+
urlStr = urlStr + ".net";
230+
break;
231+
case 0x0b:
232+
urlStr = urlStr + ".info";
233+
break;
234+
case 0x0c:
235+
urlStr = urlStr + ".biz";
236+
break;
237+
case 0x0d:
238+
urlStr = urlStr + ".gov";
239+
break;
240+
}
241+
}
242+
}
243+
244+
uBit.display.scrollAsync(urlStr);
245+
}
246+
}
247+
else
248+
{
249+
#if 0
250+
// The other data
251+
char data[len]{};
252+
uint8_t unib{};
253+
uint8_t lnib{};
254+
for (int i = 0; i < len; ++i)
255+
{
256+
unib = params->advertisingData[i] >> 4;
257+
lnib = params->advertisingData[i] & 0xf;
258+
259+
if (unib < 10)
260+
{
261+
data[i * 2] = '0' + unib;
262+
}
263+
else
264+
{
265+
data[i * 2] = 'A' + unib - 10;
266+
}
267+
if (lnib < 10)
268+
{
269+
data[i * 2 + 1] = '0' + lnib;
270+
}
271+
else
272+
{
273+
data[i * 2 + 1] = 'A' + lnib - 10;
274+
}
275+
}
276+
277+
uBit.display.scrollAsync(data);
278+
#endif
279+
}
280+
}
281+
}
282+
283+
int main()
284+
{
285+
uBit.ble = new BLEDevice();
286+
uBit.ble->init();
287+
288+
uBit.ble->gap().setScanParams(500, 400);
289+
uBit.ble->gap().startScan(advertisementCallback);
290+
291+
while (true)
292+
{
293+
uBit.ble->waitForEvent();
294+
}
295+
return 0;
296+
}

0 commit comments

Comments
 (0)