Skip to content

Commit 8f9e785

Browse files
authored
Merge pull request #156 from unknownconstant/master
BLE Pairing and Encryption
2 parents b39ac7e + 3dd53d1 commit 8f9e785

27 files changed

+2682
-137
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
/*
2+
Battery Monitor
3+
4+
This example creates a BLE peripheral with the standard battery service and
5+
level characteristic. The A0 pin is used to calculate the battery level.
6+
7+
The circuit:
8+
- Arduino MKR WiFi 1010, Arduino Uno WiFi Rev2 board, Arduino Nano 33 IoT,
9+
Arduino Nano 33 BLE, or Arduino Nano 33 BLE Sense board.
10+
11+
You can use a generic BLE central app, like LightBlue (iOS and Android) or
12+
nRF Connect (Android), to interact with the services and characteristics
13+
created in this sketch.
14+
15+
This example code is in the public domain.
16+
*/
17+
18+
#include <ArduinoBLE.h>
19+
20+
21+
#define PAIR_BUTTON 3 // button for pairing
22+
#define PAIR_LED 24 // LED used to signal pairing
23+
#define PAIR_LED_ON LOW // Blue LED on Nano BLE has inverted logic
24+
#define PAIR_INTERVAL 30000 // interval for pairing after button press in ms
25+
26+
#define CTRL_LED LED_BUILTIN
27+
28+
29+
// BLE Battery Service
30+
BLEService batteryService("180F");
31+
32+
// BLE Battery Level Characteristic
33+
BLEUnsignedCharCharacteristic batteryLevelChar("2A19", // standard 16-bit characteristic UUID
34+
BLERead | BLENotify); // remote clients will be able to get notifications if this characteristic changes
35+
BLEStringCharacteristic stringcharacteristic("183E", BLERead | BLEWrite, 31);
36+
37+
38+
// Add BLEEncryption tag to require pairing. This controls the LED.
39+
BLEUnsignedCharCharacteristic secretValue("2a3F", BLERead | BLEWrite | BLEEncryption);
40+
41+
int oldBatteryLevel = 0; // last battery level reading from analog input
42+
unsigned long previousMillis = 0; // last time the battery level was checked, in ms
43+
unsigned long pairingStarted = 0; // pairing start time when button is pressed
44+
bool wasConnected = 0;
45+
bool acceptOrReject = true;
46+
47+
void setup() {
48+
Serial.begin(9600); // initialize serial communication
49+
while (!Serial);
50+
51+
pinMode(CTRL_LED, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected
52+
pinMode(PAIR_LED, OUTPUT);
53+
pinMode(PAIR_BUTTON, INPUT_PULLUP);
54+
55+
56+
Serial.println("Serial connected");
57+
58+
// Callback function with confirmation code when new device is pairing.
59+
BLE.setDisplayCode([](uint32_t confirmCode){
60+
Serial.println("New device pairing request.");
61+
Serial.print("Confirm code matches pairing device: ");
62+
char code[6];
63+
sprintf(code, "%06d", confirmCode);
64+
Serial.println(code);
65+
});
66+
67+
// Callback to allow accepting or rejecting pairing
68+
BLE.setBinaryConfirmPairing([&acceptOrReject](){
69+
Serial.print("Should we confirm pairing? ");
70+
delay(5000);
71+
if(acceptOrReject){
72+
acceptOrReject = false;
73+
Serial.println("yes");
74+
return true;
75+
}else{
76+
acceptOrReject = true;
77+
Serial.println("no");
78+
return false;
79+
}
80+
});
81+
82+
// IRKs are keys that identify the true owner of a random mac address.
83+
// Add IRKs of devices you are bonded with.
84+
BLE.setGetIRKs([](uint8_t* nIRKs, uint8_t** BDaddrTypes, uint8_t*** BDAddrs, uint8_t*** IRKs){
85+
// Set to number of devices
86+
*nIRKs = 2;
87+
88+
*BDAddrs = new uint8_t*[*nIRKs];
89+
*IRKs = new uint8_t*[*nIRKs];
90+
*BDaddrTypes = new uint8_t[*nIRKs];
91+
92+
// Set these to the mac and IRK for your bonded devices as printed in the serial console after bonding.
93+
uint8_t device1Mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
94+
uint8_t device1IRK[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
95+
96+
uint8_t device2Mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
97+
uint8_t device2IRK[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
98+
99+
100+
(*BDaddrTypes)[0] = 0; // Type 0 is for pubc address, type 1 is for static random
101+
(*BDAddrs)[0] = new uint8_t[6];
102+
(*IRKs)[0] = new uint8_t[16];
103+
memcpy((*IRKs)[0] , device1IRK,16);
104+
memcpy((*BDAddrs)[0], device1Mac, 6);
105+
106+
107+
(*BDaddrTypes)[1] = 0;
108+
(*BDAddrs)[1] = new uint8_t[6];
109+
(*IRKs)[1] = new uint8_t[16];
110+
memcpy((*IRKs)[1] , device2IRK,16);
111+
memcpy((*BDAddrs)[1], device2Mac, 6);
112+
113+
114+
return 1;
115+
});
116+
// The LTK is the secret key which is used to encrypt bluetooth traffic
117+
BLE.setGetLTK([](uint8_t* address, uint8_t* LTK){
118+
// address is input
119+
Serial.print("Received request for address: ");
120+
btct.printBytes(address,6);
121+
122+
// Set these to the MAC and LTK of your devices after bonding.
123+
uint8_t device1Mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
124+
uint8_t device1LTK[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
125+
uint8_t device2Mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
126+
uint8_t device2LTK[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
127+
128+
129+
if(memcmp(device1Mac, address, 6) == 0) {
130+
memcpy(LTK, device1LTK, 16);
131+
return 1;
132+
}else if(memcmp(device2Mac, address, 6) == 0) {
133+
memcpy(LTK, device2LTK, 16);
134+
return 1;
135+
}
136+
return 0;
137+
});
138+
BLE.setStoreIRK([](uint8_t* address, uint8_t* IRK){
139+
Serial.print(F("New device with MAC : "));
140+
btct.printBytes(address,6);
141+
Serial.print(F("Need to store IRK : "));
142+
btct.printBytes(IRK,16);
143+
return 1;
144+
});
145+
BLE.setStoreLTK([](uint8_t* address, uint8_t* LTK){
146+
Serial.print(F("New device with MAC : "));
147+
btct.printBytes(address,6);
148+
Serial.print(F("Need to store LTK : "));
149+
btct.printBytes(LTK,16);
150+
return 1;
151+
});
152+
153+
while(1){
154+
// begin initialization
155+
if (!BLE.begin()) {
156+
Serial.println("starting BLE failed!");
157+
delay(200);
158+
continue;
159+
}
160+
Serial.println("BT init");
161+
delay(200);
162+
163+
/* Set a local name for the BLE device
164+
This name will appear in advertising packets
165+
and can be used by remote devices to identify this BLE device
166+
The name can be changed but maybe be truncated based on space left in advertisement packet
167+
*/
168+
169+
BLE.setDeviceName("Arduino");
170+
BLE.setLocalName("BatteryMonitor");
171+
172+
BLE.setAdvertisedService(batteryService); // add the service UUID
173+
batteryService.addCharacteristic(batteryLevelChar); // add the battery level characteristic
174+
batteryService.addCharacteristic(stringcharacteristic);
175+
batteryService.addCharacteristic(secretValue);
176+
177+
BLE.addService(batteryService); // Add the battery service
178+
batteryLevelChar.writeValue(oldBatteryLevel); // set initial value for this characteristic
179+
char* stringCharValue = new char[32];
180+
stringCharValue = "string";
181+
stringcharacteristic.writeValue(stringCharValue);
182+
secretValue.writeValue(0);
183+
184+
delay(1000);
185+
186+
// prevent pairing until button is pressed (will show a pairing rejected message)
187+
BLE.setPairable(false);
188+
189+
/* Start advertising BLE. It will start continuously transmitting BLE
190+
advertising packets and will be visible to remote BLE central devices
191+
until it receives a new connection */
192+
193+
// start advertising
194+
if(!BLE.advertise()){
195+
Serial.println("failed to advertise bluetooth.");
196+
BLE.stopAdvertise();
197+
delay(500);
198+
}else{
199+
Serial.println("advertising...");
200+
break;
201+
}
202+
BLE.end();
203+
delay(100);
204+
}
205+
}
206+
207+
208+
void loop() {
209+
// wait for a BLE central
210+
BLEDevice central = BLE.central();
211+
212+
213+
// If button is pressed, allow pairing for 30 sec
214+
if (!BLE.pairable() && digitalRead(PAIR_BUTTON) == LOW){
215+
pairingStarted = millis();
216+
BLE.setPairable(Pairable::ONCE);
217+
Serial.println("Accepting pairing for 30s");
218+
} else if (BLE.pairable() && millis() > pairingStarted + PAIR_INTERVAL){
219+
BLE.setPairable(false);
220+
Serial.println("No longer accepting pairing");
221+
}
222+
// Make LED blink while pairing is allowed
223+
digitalWrite(PAIR_LED, (BLE.pairable() ? (millis()%400)<200 : BLE.paired()) ? PAIR_LED_ON : !PAIR_LED_ON);
224+
225+
226+
// if a central is connected to the peripheral:
227+
if (central && central.connected()) {
228+
if (!wasConnected){
229+
wasConnected = true;
230+
Serial.print("Connected to central: ");
231+
// print the central's BT address:
232+
Serial.println(central.address());
233+
}
234+
235+
// check the battery level every 200ms
236+
// while the central is connected:
237+
long currentMillis = millis();
238+
// if 200ms have passed, check the battery level:
239+
if (currentMillis - previousMillis >= 1000) {
240+
previousMillis = currentMillis;
241+
updateBatteryLevel();
242+
digitalWrite(CTRL_LED, secretValue.value()>0 ? HIGH : LOW);
243+
}
244+
} else if (wasConnected){
245+
wasConnected = false;
246+
Serial.print("Disconnected from central: ");
247+
Serial.println(central.address());
248+
}
249+
250+
}
251+
252+
void updateBatteryLevel() {
253+
/* Read the current voltage level on the A0 analog input pin.
254+
This is used here to simulate the charge level of a battery.
255+
*/
256+
int battery = analogRead(A0);
257+
int batteryLevel = map(battery, 0, 1023, 0, 100);
258+
259+
if (batteryLevel != oldBatteryLevel) { // if the battery level has changed
260+
// Serial.print("Battery Level % is now: "); // print it
261+
// Serial.println(batteryLevel);
262+
batteryLevelChar.writeValue(batteryLevel); // and update the battery level characteristic
263+
oldBatteryLevel = batteryLevel; // save the level for next comparison
264+
}
265+
}

Diff for: keywords.txt

+3
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,12 @@ setEventHandler KEYWORD2
7777
setAdvertisingInterval KEYWORD2
7878
setConnectionInterval KEYWORD2
7979
setConnectable KEYWORD2
80+
setPairable KEYWORD2
8081
setTimeout KEYWORD2
8182
debug KEYWORD2
8283
noDebug KEYWORD2
84+
pairable KEYWORD2
85+
paired KEYWORD2
8386

8487
properties KEYWORD2
8588
valueSize KEYWORD2

Diff for: src/ArduinoBLE.h

+1
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@
2424
#include "BLEProperty.h"
2525
#include "BLEStringCharacteristic.h"
2626
#include "BLETypedCharacteristics.h"
27+
#include "utility/btct.h"
2728

2829
#endif

Diff for: src/BLECharacteristic.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ BLECharacteristic::BLECharacteristic(BLERemoteCharacteristic* remote) :
4747
}
4848
}
4949

50-
BLECharacteristic::BLECharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength) :
51-
BLECharacteristic(new BLELocalCharacteristic(uuid, properties, valueSize, fixedLength))
50+
BLECharacteristic::BLECharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength) :
51+
BLECharacteristic(new BLELocalCharacteristic(uuid, permissions, valueSize, fixedLength))
5252
{
5353
}
5454

55-
BLECharacteristic::BLECharacteristic(const char* uuid, uint8_t properties, const char* value) :
56-
BLECharacteristic(new BLELocalCharacteristic(uuid, properties, value))
55+
BLECharacteristic::BLECharacteristic(const char* uuid, uint16_t permissions, const char* value) :
56+
BLECharacteristic(new BLELocalCharacteristic(uuid, permissions, value))
5757
{
5858
}
5959

Diff for: src/BLECharacteristic.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ class BLERemoteCharacteristic;
4545
class BLECharacteristic {
4646
public:
4747
BLECharacteristic();
48-
BLECharacteristic(const char* uuid, uint8_t properties, int valueSize, bool fixedLength = false);
49-
BLECharacteristic(const char* uuid, uint8_t properties, const char* value);
48+
BLECharacteristic(const char* uuid, uint16_t permissions, int valueSize, bool fixedLength = false);
49+
BLECharacteristic(const char* uuid, uint16_t permissions, const char* value);
5050
BLECharacteristic(const BLECharacteristic& other);
5151
virtual ~BLECharacteristic();
5252

Diff for: src/BLEProperty.h

+48-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1818
*/
1919

20+
// #include <stdint.h>
21+
2022
#ifndef _BLE_PROPERTY_H_
2123
#define _BLE_PROPERTY_H_
2224

@@ -26,7 +28,52 @@ enum BLEProperty {
2628
BLEWriteWithoutResponse = 0x04,
2729
BLEWrite = 0x08,
2830
BLENotify = 0x10,
29-
BLEIndicate = 0x20
31+
BLEIndicate = 0x20,
32+
BLEAuthSignedWrite = 1 << 6,
33+
BLEExtProp = 1 << 7,
34+
};
35+
36+
enum BLEPermission {
37+
BLEEncryption = 1 << 9,
38+
BLEAuthentication = 1 << 10,
39+
BLEAuthorization = 1 << 11,
40+
// BLEWriteEncryption = 1 << 11,
41+
// BLEWriteAuthentication = 1 << 12,
42+
// BLEWriteAuthorization = 1 << 13,
43+
};
44+
45+
#define ESP_GATT_CHAR_PROP_BIT_BROADCAST (1 << 0) /* 0x01 */ /* relate to BTA_GATT_CHAR_PROP_BIT_BROADCAST in bta/bta_gatt_api.h */
46+
#define ESP_GATT_CHAR_PROP_BIT_READ (1 << 1) /* 0x02 */ /* relate to BTA_GATT_CHAR_PROP_BIT_READ in bta/bta_gatt_api.h */
47+
#define ESP_GATT_CHAR_PROP_BIT_WRITE_NR (1 << 2) /* 0x04 */ /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE_NR in bta/bta_gatt_api.h */
48+
#define ESP_GATT_CHAR_PROP_BIT_WRITE (1 << 3) /* 0x08 */ /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE in bta/bta_gatt_api.h */
49+
#define ESP_GATT_CHAR_PROP_BIT_NOTIFY (1 << 4) /* 0x10 */ /* relate to BTA_GATT_CHAR_PROP_BIT_NOTIFY in bta/bta_gatt_api.h */
50+
#define ESP_GATT_CHAR_PROP_BIT_INDICATE (1 << 5) /* 0x20 */ /* relate to BTA_GATT_CHAR_PROP_BIT_INDICATE in bta/bta_gatt_api.h */
51+
#define ESP_GATT_CHAR_PROP_BIT_AUTH (1 << 6) /* 0x40 */ /* relate to BTA_GATT_CHAR_PROP_BIT_AUTH in bta/bta_gatt_api.h */
52+
#define ESP_GATT_CHAR_PROP_BIT_EXT_PROP (1 << 7) /* 0x80 */ /* relate to BTA_GATT_CHAR_PROP_BIT_EXT_PROP in bta/bta_gatt_api.h */
53+
54+
#define ESP_GATT_PERM_READ (1 << 0) /* bit 0 - 0x0001 */ /* relate to BTA_GATT_PERM_READ in bta/bta_gatt_api.h */
55+
#define ESP_GATT_PERM_READ_ENCRYPTED (1 << 1) /* bit 1 - 0x0002 */ /* relate to BTA_GATT_PERM_READ_ENCRYPTED in bta/bta_gatt_api.h */
56+
#define ESP_GATT_PERM_READ_ENC_MITM (1 << 2) /* bit 2 - 0x0004 */ /* relate to BTA_GATT_PERM_READ_ENC_MITM in bta/bta_gatt_api.h */
57+
#define ESP_GATT_PERM_WRITE (1 << 4) /* bit 4 - 0x0010 */ /* relate to BTA_GATT_PERM_WRITE in bta/bta_gatt_api.h */
58+
#define ESP_GATT_PERM_WRITE_ENCRYPTED (1 << 5) /* bit 5 - 0x0020 */ /* relate to BTA_GATT_PERM_WRITE_ENCRYPTED in bta/bta_gatt_api.h */
59+
#define ESP_GATT_PERM_WRITE_ENC_MITM (1 << 6) /* bit 6 - 0x0040 */ /* relate to BTA_GATT_PERM_WRITE_ENC_MITM in bta/bta_gatt_api.h */
60+
#define ESP_GATT_PERM_WRITE_SIGNED (1 << 7) /* bit 7 - 0x0080 */ /* relate to BTA_GATT_PERM_WRITE_SIGNED in bta/bta_gatt_api.h */
61+
#define ESP_GATT_PERM_WRITE_SIGNED_MITM (1 << 8) /* bit 8 - 0x0100 */ /* relate to BTA_GATT_PERM_WRITE_SIGNED_MITM in bta/bta_gatt_api.h */
62+
#define ESP_GATT_PERM_READ_AUTHORIZATION (1 << 9) /* bit 9 - 0x0200 */
63+
#define ESP_GATT_PERM_WRITE_AUTHORIZATION (1 << 10) /* bit 10 - 0x0400 */
64+
65+
enum BLE_GATT_PERM_ {
66+
READ = 1 << 0,
67+
READ_ENCRYPTED = 1 << 1,
68+
READ_ENC_MITM = 1 << 2,
69+
WRITE = 1 << 4,
70+
WRITE_ENCRYPTED = 1 << 5,
71+
WRITE_ENC_MITM = 1 << 6,
72+
WRITE_SIGNED = 1 << 7,
73+
WRITE_SIGNED_MITM = 1 << 8,
74+
READ_AUTHORIZATION = 1 << 9,
75+
WRITE_AUTHORIZATION = 1 << 10,
3076
};
3177

78+
3279
#endif

Diff for: src/BLETypedCharacteristic.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
template<typename T> class BLETypedCharacteristic : public BLECharacteristic
2626
{
2727
public:
28-
BLETypedCharacteristic(const char* uuid, unsigned char properties);
28+
BLETypedCharacteristic(const char* uuid, unsigned int permissions);
2929

3030
int writeValue(T value);
3131
int setValue(T value) { return writeValue(value); }
@@ -43,8 +43,8 @@ template<typename T> class BLETypedCharacteristic : public BLECharacteristic
4343
T byteSwap(T value);
4444
};
4545

46-
template<typename T> BLETypedCharacteristic<T>::BLETypedCharacteristic(const char* uuid, unsigned char properties) :
47-
BLECharacteristic(uuid, properties, sizeof(T), true)
46+
template<typename T> BLETypedCharacteristic<T>::BLETypedCharacteristic(const char* uuid, unsigned int permissions) :
47+
BLECharacteristic(uuid, permissions, sizeof(T), true)
4848
{
4949
T value;
5050
memset(&value, 0x00, sizeof(value));

0 commit comments

Comments
 (0)