Skip to content

Commit f54e7a2

Browse files
committed
Add method to control and query pairing
1 parent 915e914 commit f54e7a2

File tree

8 files changed

+190
-62
lines changed

8 files changed

+190
-62
lines changed

examples/Peripheral/EncryptedBatteryMonitor/EncryptedBatteryMonitor.ino

+48-19
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
#include <ArduinoBLE.h>
1919

2020

21+
#define PAIR_BUTTON D3 // 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+
2129
// BLE Battery Service
2230
BLEService batteryService("180F");
2331

@@ -31,13 +39,17 @@ BLEStringCharacteristic stringcharacteristic("183E", BLERead | BLEWrite, 31);
3139
BLEUnsignedCharCharacteristic secretValue("2a3F", BLERead | BLEWrite | BLEEncryption);
3240

3341
int oldBatteryLevel = 0; // last battery level reading from analog input
34-
long previousMillis = 0; // last time the battery level was checked, in ms
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;
3545

3646
void setup() {
3747
Serial.begin(9600); // initialize serial communication
3848
while (!Serial);
3949

40-
pinMode(LED_BUILTIN, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected
50+
pinMode(CTRL_LED, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected
51+
pinMode(PAIR_LED, OUTPUT);
52+
pinMode(PAIR_BUTTON, INPUT_PULLUP);
4153

4254

4355
Serial.println("Serial connected");
@@ -145,6 +157,9 @@ void setup() {
145157
secretValue.writeValue(0);
146158

147159
delay(1000);
160+
161+
// prevent pairing until button is pressed (will show a pairing rejected message)
162+
BLE.setPairable(false);
148163

149164
/* Start advertising BLE. It will start continuously transmitting BLE
150165
advertising packets and will be visible to remote BLE central devices
@@ -169,30 +184,44 @@ void loop() {
169184
// wait for a BLE central
170185
BLEDevice central = BLE.central();
171186

187+
188+
// If button is pressed, allow pairing for 30 sec
189+
if (!BLE.pairable() && digitalRead(PAIR_BUTTON) == LOW){
190+
pairingStarted = millis();
191+
BLE.setPairable(Pairable::ONCE);
192+
Serial.println("Accepting pairing for 30s");
193+
} else if (BLE.pairable() && millis() > pairingStarted + PAIR_INTERVAL){
194+
BLE.setPairable(false);
195+
Serial.println("No longer accepting pairing");
196+
}
197+
// Make LED blink while pairing is allowed
198+
digitalWrite(PAIR_LED, (BLE.pairable() ? (millis()%400)<200 : BLE.paired()) ? PAIR_LED_ON : !PAIR_LED_ON);
199+
200+
172201
// if a central is connected to the peripheral:
173-
if (central) {
174-
Serial.print("Connected to central: ");
175-
// print the central's BT address:
176-
Serial.println(central.address());
202+
if (central && central.connected()) {
203+
if (!wasConnected){
204+
wasConnected = true;
205+
Serial.print("Connected to central: ");
206+
// print the central's BT address:
207+
Serial.println(central.address());
208+
}
177209

178210
// check the battery level every 200ms
179211
// while the central is connected:
180-
while (central.connected()) {
181-
long currentMillis = millis();
182-
// if 200ms have passed, check the battery level:
183-
if (currentMillis - previousMillis >= 1000) {
184-
previousMillis = currentMillis;
185-
updateBatteryLevel();
186-
if(secretValue.value()>0){
187-
digitalWrite(13,HIGH);
188-
}else{
189-
digitalWrite(13,LOW);
190-
}
191-
}
212+
long currentMillis = millis();
213+
// if 200ms have passed, check the battery level:
214+
if (currentMillis - previousMillis >= 1000) {
215+
previousMillis = currentMillis;
216+
updateBatteryLevel();
217+
digitalWrite(CTRL_LED, secretValue.value()>0 ? HIGH : LOW);
192218
}
219+
} else if (wasConnected){
220+
wasConnected = false;
193221
Serial.print("Disconnected from central: ");
194222
Serial.println(central.address());
195223
}
224+
196225
}
197226

198227
void updateBatteryLevel() {
@@ -208,4 +237,4 @@ void updateBatteryLevel() {
208237
batteryLevelChar.writeValue(batteryLevel); // and update the battery level characteristic
209238
oldBatteryLevel = batteryLevel; // save the level for next comparison
210239
}
211-
}
240+
}

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

src/local/BLELocalDevice.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,16 @@ bool BLELocalDevice::connected() const
214214
return ATT.connected();
215215
}
216216

217+
/*
218+
* Whether there is at least one paired device
219+
*/
220+
bool BLELocalDevice::paired()
221+
{
222+
HCI.poll();
223+
224+
return ATT.paired();
225+
}
226+
217227
bool BLELocalDevice::disconnect()
218228
{
219229
return ATT.disconnect();
@@ -395,6 +405,22 @@ void BLELocalDevice::setTimeout(unsigned long timeout)
395405
ATT.setTimeout(timeout);
396406
}
397407

408+
/*
409+
* Control whether pairing is allowed or rejected
410+
* Use true/false or the Pairable enum
411+
*/
412+
void BLELocalDevice::setPairable(uint8_t pairable)
413+
{
414+
L2CAPSignaling.setPairingEnabled(pairable);
415+
}
416+
417+
/*
418+
* Whether pairing is currently allowed
419+
*/
420+
bool BLELocalDevice::pairable()
421+
{
422+
return L2CAPSignaling.isPairingEnabled();
423+
}
398424

399425
void BLELocalDevice::setGetIRKs(int (*getIRKs)(uint8_t* nIRKs, uint8_t** BADDR_type, uint8_t*** BADDRs, uint8_t*** IRKs)){
400426
HCI._getIRKs = getIRKs;

src/local/BLELocalDevice.h

+10
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424
#include "BLEService.h"
2525
#include "BLEAdvertisingData.h"
2626

27+
enum Pairable {
28+
NO = 0,
29+
YES = 1,
30+
ONCE = 2,
31+
};
32+
2733
class BLELocalDevice {
2834
public:
2935
BLELocalDevice();
@@ -80,6 +86,10 @@ class BLELocalDevice {
8086

8187
virtual void debug(Stream& stream);
8288
virtual void noDebug();
89+
90+
virtual void setPairable(uint8_t pairable);
91+
virtual bool pairable();
92+
virtual bool paired();
8393

8494
/// TODO: Put in actual variable names
8595
virtual void setStoreIRK(int (*storeIRK)(uint8_t*, uint8_t*));

src/utility/ATT.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,32 @@ bool ATTClass::connected(uint16_t handle) const
497497
return false;
498498
}
499499

500+
/*
501+
* Return true if any of the known devices is paired (peer encrypted)
502+
* Does not check if the paired device is also connected
503+
*/
504+
bool ATTClass::paired() const
505+
{
506+
for(int i=0; i<ATT_MAX_PEERS; i++){
507+
if((_peers[i].encryption & PEER_ENCRYPTION::ENCRYPTED_AES) > 0){
508+
return true;
509+
}
510+
}
511+
return false;
512+
}
513+
514+
/*
515+
* Return true if the specified device is paired (peer encrypted)
516+
*/
517+
bool ATTClass::paired(uint16_t handle) const
518+
{
519+
for(int i=0; i<ATT_MAX_PEERS; i++){
520+
if(_peers[i].connectionHandle != handle){continue;}
521+
return (_peers[i].encryption & PEER_ENCRYPTION::ENCRYPTED_AES) > 0;
522+
}
523+
return false; // unknown handle
524+
}
525+
500526
uint16_t ATTClass::mtu(uint16_t handle) const
501527
{
502528
for (int i = 0; i < ATT_MAX_PEERS; i++) {

src/utility/ATT.h

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ class ATTClass {
7575
virtual bool connected() const;
7676
virtual bool connected(uint8_t addressType, const uint8_t address[6]) const;
7777
virtual bool connected(uint16_t handle) const;
78+
virtual bool paired() const;
79+
virtual bool paired(uint16_t handle) const;
7880
virtual uint16_t mtu(uint16_t handle) const;
7981

8082
virtual bool disconnect();

src/utility/L2CAPSignaling.cpp

+63-42
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ L2CAPSignalingClass::L2CAPSignalingClass() :
3232
_minInterval(0),
3333
_maxInterval(0),
3434
_supervisionTimeout(0)
35+
,_pairing_enabled(1)
3536
{
3637
}
3738

@@ -131,53 +132,64 @@ void L2CAPSignalingClass::handleSecurityData(uint16_t connectionHandle, uint8_t
131132
btct.printBytes(data,dlen);
132133
#endif
133134
if (code == CONNECTION_PAIRING_REQUEST) {
134-
// 0x1
135-
struct __attribute__ ((packed)) PairingRequest {
136-
uint8_t ioCapability;
137-
uint8_t oobDataFlag;
138-
uint8_t authReq;
139-
uint8_t maxEncSize;
140-
uint8_t initiatorKeyDistribution;
141-
uint8_t responderKeyDistribution;
142-
} *pairingRequest = (PairingRequest*)l2capSignalingHdr->data;
143-
144-
145-
ATT.remoteKeyDistribution = KeyDistribution(pairingRequest->initiatorKeyDistribution);
146-
ATT.localKeyDistribution = KeyDistribution(pairingRequest->responderKeyDistribution);
147-
KeyDistribution rkd(pairingRequest->responderKeyDistribution);
148-
AuthReq req(pairingRequest->authReq);
149-
KeyDistribution responseKD = KeyDistribution();
150-
responseKD.setIdKey(true);
135+
136+
if (isPairingEnabled()){
137+
if (_pairing_enabled >= 2) _pairing_enabled = 0; // 2 = pair once only
138+
139+
// 0x1
140+
struct __attribute__ ((packed)) PairingRequest {
141+
uint8_t ioCapability;
142+
uint8_t oobDataFlag;
143+
uint8_t authReq;
144+
uint8_t maxEncSize;
145+
uint8_t initiatorKeyDistribution;
146+
uint8_t responderKeyDistribution;
147+
} *pairingRequest = (PairingRequest*)l2capSignalingHdr->data;
148+
149+
150+
ATT.remoteKeyDistribution = KeyDistribution(pairingRequest->initiatorKeyDistribution);
151+
ATT.localKeyDistribution = KeyDistribution(pairingRequest->responderKeyDistribution);
152+
KeyDistribution rkd(pairingRequest->responderKeyDistribution);
153+
AuthReq req(pairingRequest->authReq);
154+
KeyDistribution responseKD = KeyDistribution();
155+
responseKD.setIdKey(true);
151156
#ifdef _BLE_TRACE_
152-
Serial.print("Req has properties: ");
153-
Serial.print(req.Bonding()?"bonding, ":"no bonding, ");
154-
Serial.print(req.CT2()?"CT2, ":"no CT2, ");
155-
Serial.print(req.KeyPress()?"KeyPress, ":"no KeyPress, ");
156-
Serial.print(req.MITM()?"MITM, ":"no MITM, ");
157-
Serial.print(req.SC()?"SC, ":"no SC, ");
157+
Serial.print("Req has properties: ");
158+
Serial.print(req.Bonding()?"bonding, ":"no bonding, ");
159+
Serial.print(req.CT2()?"CT2, ":"no CT2, ");
160+
Serial.print(req.KeyPress()?"KeyPress, ":"no KeyPress, ");
161+
Serial.print(req.MITM()?"MITM, ":"no MITM, ");
162+
Serial.print(req.SC()?"SC, ":"no SC, ");
158163
#endif
159164

160-
uint8_t peerIOCap[3];
161-
peerIOCap[0] = pairingRequest->authReq;
162-
peerIOCap[1] = pairingRequest->oobDataFlag;
163-
peerIOCap[2] = pairingRequest->ioCapability;
164-
ATT.setPeerIOCap(connectionHandle, peerIOCap);
165-
ATT.setPeerEncryption(connectionHandle, ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::PAIRING_REQUEST);
165+
uint8_t peerIOCap[3];
166+
peerIOCap[0] = pairingRequest->authReq;
167+
peerIOCap[1] = pairingRequest->oobDataFlag;
168+
peerIOCap[2] = pairingRequest->ioCapability;
169+
ATT.setPeerIOCap(connectionHandle, peerIOCap);
170+
ATT.setPeerEncryption(connectionHandle, ATT.getPeerEncryption(connectionHandle) | PEER_ENCRYPTION::PAIRING_REQUEST);
166171
#ifdef _BLE_TRACE_
167-
Serial.print("Peer encryption : 0b");
168-
Serial.println(ATT.getPeerEncryption(connectionHandle), BIN);
172+
Serial.print("Peer encryption : 0b");
173+
Serial.println(ATT.getPeerEncryption(connectionHandle), BIN);
169174
#endif
170-
struct __attribute__ ((packed)) PairingResponse {
171-
uint8_t code;
172-
uint8_t ioCapability;
173-
uint8_t oobDataFlag;
174-
uint8_t authReq;
175-
uint8_t maxEncSize;
176-
uint8_t initiatorKeyDistribution;
177-
uint8_t responderKeyDistribution;
178-
} response = { CONNECTION_PAIRING_RESPONSE, LOCAL_IOCAP, 0, LOCAL_AUTHREQ, 0x10, responseKD.getOctet(), responseKD.getOctet()};
179-
180-
HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response);
175+
struct __attribute__ ((packed)) PairingResponse {
176+
uint8_t code;
177+
uint8_t ioCapability;
178+
uint8_t oobDataFlag;
179+
uint8_t authReq;
180+
uint8_t maxEncSize;
181+
uint8_t initiatorKeyDistribution;
182+
uint8_t responderKeyDistribution;
183+
} response = { CONNECTION_PAIRING_RESPONSE, LOCAL_IOCAP, 0, LOCAL_AUTHREQ, 0x10, responseKD.getOctet(), responseKD.getOctet()};
184+
185+
HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(response), &response);
186+
187+
} else {
188+
// Pairing not enabled
189+
uint8_t ret[2] = {CONNECTION_PAIRING_FAILED, 0x05}; // reqect pairing
190+
HCI.sendAclPkt(connectionHandle, SECURITY_CID, sizeof(ret), ret);
191+
ATT.setPeerEncryption(connectionHandle, NO_ENCRYPTION);
192+
}
181193
}
182194
else if (code == CONNECTION_PAIRING_RANDOM)
183195
{
@@ -392,6 +404,15 @@ void L2CAPSignalingClass::setSupervisionTimeout(uint16_t supervisionTimeout)
392404
_supervisionTimeout = supervisionTimeout;
393405
}
394406

407+
void L2CAPSignalingClass::setPairingEnabled(uint8_t enabled)
408+
{
409+
_pairing_enabled = enabled;
410+
}
411+
bool L2CAPSignalingClass::isPairingEnabled()
412+
{
413+
return _pairing_enabled > 0;
414+
}
415+
395416
void L2CAPSignalingClass::connectionParameterUpdateRequest(uint16_t handle, uint8_t identifier, uint8_t dlen, uint8_t data[])
396417
{
397418
struct __attribute__ ((packed)) L2CAPConnectionParameterUpdateRequest {

src/utility/L2CAPSignaling.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,15 @@
4141
#define CONNECTION_PAIRING_DHKEY_CHECK 0x0D
4242
#define CONNECTION_PAIRING_KEYPRESS 0x0E
4343

44+
#define IOCAP_DISPLAY_ONLY 0x00
45+
#define IOCAP_DISPLAY_YES_NO 0x01
46+
#define IOCAP_KEYBOARD_ONLY 0x02
47+
#define IOCAP_NO_INPUT_NO_OUTPUT 0x03
48+
#define IOCAP_KEYBOARD_DISPLAY 0x04
49+
50+
4451
#define LOCAL_AUTHREQ 0b00101101
45-
#define LOCAL_IOCAP 0x3
52+
#define LOCAL_IOCAP IOCAP_NO_INPUT_NO_OUTPUT // will use JustWorks pairing
4653

4754
class L2CAPSignalingClass {
4855
public:
@@ -63,6 +70,9 @@ class L2CAPSignalingClass {
6370
virtual void setConnectionInterval(uint16_t minInterval, uint16_t maxInterval);
6471

6572
virtual void setSupervisionTimeout(uint16_t supervisionTimeout);
73+
74+
virtual void setPairingEnabled(uint8_t enabled);
75+
virtual bool isPairingEnabled();
6676

6777

6878

@@ -78,6 +88,7 @@ class L2CAPSignalingClass {
7888
uint16_t _minInterval;
7989
uint16_t _maxInterval;
8090
uint16_t _supervisionTimeout;
91+
uint8_t _pairing_enabled;
8192
};
8293

8394
extern L2CAPSignalingClass& L2CAPSignaling;

0 commit comments

Comments
 (0)