Skip to content

Commit c3ca1f0

Browse files
authored
Merge pull request #740 from sparkfun/pcUpdates
Updates for MFi (BLUETOOTH_RADIO_SPP_ACCESSORY_MODE)
2 parents 5e8192f + 3f3abde commit c3ca1f0

File tree

11 files changed

+147
-51
lines changed

11 files changed

+147
-51
lines changed

.github/workflows/compile-rtk-everywhere.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ jobs:
9797
"SparkFun UM980 Triband RTK GNSS Arduino Library"@1.0.5
9898
"SparkFun LG290P Quadband RTK GNSS Arduino Library"@1.0.8
9999
"SparkFun I2C Expander Arduino Library"@1.0.1
100-
"SparkFun Apple Accessory Arduino Library"@3.0.8
100+
"SparkFun Apple Accessory Arduino Library"@3.0.9
101101
"SparkFun Authentication Coprocessor Arduino Library"@1.0.0
102102
"SparkFun Toolkit"@1.0.6
103103

.github/workflows/non-release-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ jobs:
9797
"SparkFun UM980 Triband RTK GNSS Arduino Library"@1.0.5
9898
"SparkFun LG290P Quadband RTK GNSS Arduino Library"@1.0.8
9999
"SparkFun I2C Expander Arduino Library"@1.0.1
100-
"SparkFun Apple Accessory Arduino Library"@3.0.8
100+
"SparkFun Apple Accessory Arduino Library"@3.0.9
101101
"SparkFun Authentication Coprocessor Arduino Library"@1.0.0
102102
"SparkFun Toolkit"@1.0.6
103103

Firmware/RTK_Everywhere/AuthCoPro.ino

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
const char *manufacturer = "SparkFun Electronics";
44
const char *hardwareVersion = "1.0.0";
55
const char *EAProtocol = "com.sparkfun.rtk";
6-
const char *BTTransportName = "com.sparkfun.bt";
6+
//const char *BTTransportName = "com.sparkfun.bt";
7+
const char *BTTransportName = "Bluetooth";
78
const char *LIComponentName = "com.sparkfun.li";
89
const char *productPlanUID =
910
"0123456789ABCDEF"; // This comes from the MFi Portal, when you register the product with Apple
@@ -70,17 +71,7 @@ void beginAuthCoPro(TwoWire *i2cBus)
7071
systemPrintln("Authentication coprocessor online");
7172
}
7273

73-
static char *bda2str(esp_bd_addr_t bda, char *str, size_t size)
74-
{
75-
if (bda == NULL || str == NULL || size < 18)
76-
{
77-
return NULL;
78-
}
79-
80-
uint8_t *p = bda;
81-
snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2], p[3], p[4], p[5]);
82-
return str;
83-
}
74+
extern char *bda2str(esp_bd_addr_t bda, char *str, size_t size);
8475

8576
void updateAuthCoPro()
8677
{
@@ -98,31 +89,36 @@ void updateAuthCoPro()
9889
// The internal flag is automatically cleared when aclConnected returns true
9990
if (bluetoothSerialSpp->aclConnected() == true)
10091
{
101-
// //
102-
// https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/DiscoverConnect/DiscoverConnect.ino
103-
// std::map<int, std::string> channels =
104-
// bluetoothSerialSpp->getChannels(bluetoothSerialSpp->aclGetAddress());
92+
char bda_str[18];
93+
systemPrintf("Apple Device %s found\r\n", bda2str(bluetoothSerialSpp->aclGetAddress(), bda_str, 18));
10594

106-
// int channel = 0; // Channel 0 for auto-detect
107-
// if (channels.size() > 0)
108-
// channel = channels.begin()->first;
95+
// We need to connect _almost_ immediately for a successful pairing
96+
// This is really brittle.
97+
// Having core debug enabled adds enough delay to make this work.
98+
// With debug set to none, we need to insert a _small_ delay...
99+
// Too much delay and we get Connection Unsuccessful.
100+
delay(2);
109101

110102
int channel = 1;
103+
if (settings.debugNetworkLayer)
104+
systemPrintf("Connecting on channel %d\r\n", channel);
111105

112-
char bda_str[18];
113-
bda2str(bluetoothSerialSpp->aclGetAddress(), bda_str, 18);
114-
115-
systemPrintf("Apple Device %s found, connecting on channel %d\r\n", bda_str, channel);
116-
117-
bluetoothSerialSpp->connect(bluetoothSerialSpp->aclGetAddress(), channel);
106+
bluetoothSerialSpp->connect(bluetoothSerialSpp->aclGetAddress(), channel); // Blocking for READY_TIMEOUT
118107

119108
if (bluetoothSerialSpp->connected())
120109
{
110+
systemPrintln("Connected. Sending handshake...");
121111
appleAccessory->startHandshake((Stream *)bluetoothSerialSpp);
122112
}
113+
else
114+
{
115+
systemPrintln("Connection failed / timed out! Handshake will be sent if device connects...");
116+
sendAccessoryHandshakeOnBtConnect = true;
117+
}
123118
}
124119

125120
// That's all folks!
121+
// If a timeout occurred, Handshake is sent by bluetoothUpdate()
126122
// Everything else is handled by the Apple Accessory Library
127123
}
128124
}

Firmware/RTK_Everywhere/Bluetooth.ino

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ void bluetoothUpdate()
7272
// LED is controlled by tickerBluetoothLedUpdate()
7373

7474
btPrintEchoExit = false; // Reset the exiting of config menus and/or command modes
75+
76+
#ifdef COMPILE_AUTHENTICATION
77+
if (sendAccessoryHandshakeOnBtConnect)
78+
{
79+
appleAccessory->startHandshake((Stream *)bluetoothSerialSpp);
80+
sendAccessoryHandshakeOnBtConnect = false; // One-shot
81+
}
82+
#endif
7583
}
7684

7785
else if ((bluetoothState == BT_CONNECTED) && (!bluetoothIsConnected()))
@@ -411,6 +419,12 @@ void bluetoothFlush()
411419
#endif // COMPILE_BT
412420
}
413421

422+
void BTConfirmRequestCallback(uint32_t numVal) {
423+
systemPrintf("Device sent PIN: %06lu. Sending confirmation\r\n", numVal);
424+
bluetoothSerialSpp->confirmReply(true); // AUTO_PAIR - equivalent to enableSSP(false, true);
425+
// TODO: if the RTK device has an OLED, we should display the PIN so user can confirm
426+
}
427+
414428
// Get MAC, start radio
415429
// Tack device's MAC address to end of friendly broadcast name
416430
// This allows multiple units to be on at same time
@@ -533,10 +547,23 @@ void bluetoothStart()
533547
}
534548
else if (settings.bluetoothRadioType == BLUETOOTH_RADIO_SPP_ACCESSORY_MODE)
535549
{
536-
// Support Apple Accessory
537-
538-
bluetoothSerialSpp->enableSSP(false,
539-
false); // Enable secure pairing, authenticate without displaying anything
550+
// Uncomment the next line to force deletion of all paired (bonded) devices
551+
// (This should only be necessary if you have changed the SSP pairing type)
552+
//settings.clearBtPairings = true;
553+
554+
// Enable secure pairing without PIN :
555+
// iPhone displays Connection Unsuccessful - but then connects anyway...
556+
bluetoothSerialSpp->enableSSP(false, false);
557+
558+
// Enable secure pairing with PIN :
559+
//bluetoothSerialSpp->enableSSP(false, true);
560+
561+
// Accessory Protocol recommends using a PIN
562+
// Support Apple Accessory: Device to Accessory
563+
// 1. Search for an accessory from the device and initiate pairing.
564+
// 2. Verify pairing is successful after exchanging a pin code.
565+
//bluetoothSerialSpp->enableSSP(true, true); // Enable secure pairing with PIN
566+
//bluetoothSerialSpp->onConfirmRequest(&BTConfirmRequestCallback); // Callback to verify the PIN
540567

541568
beginSuccess &= bluetoothSerialSpp->begin(
542569
deviceName, true, true, settings.sppRxQueueSize, settings.sppTxQueueSize, 0, 0,
@@ -546,16 +573,32 @@ void bluetoothStart()
546573
{
547574
// bluetoothSerialSpp.getBtAddress(btMACAddress); // Read the ESP32 BT MAC Address
548575

576+
if (settings.clearBtPairings)
577+
{
578+
// Paired / bonded devices are stored in flash. Only a full flash erase
579+
// or deleteAllBondedDevices() will clear them all. They can be deleted
580+
// individually, but that would need a menu and more functions added to
581+
// the BT classes.
582+
// Deleting all bonded devices after a factory reset seems sensible.
583+
// TODO: test all the possibilities / overlap of this and "Forget Device"
584+
if (settings.debugNetworkLayer)
585+
systemPrintln("Deleting all bonded devices");
586+
bluetoothSerialSpp->deleteAllBondedDevices(); // Must be called after begin
587+
settings.clearBtPairings = false;
588+
recordSystemSettings();
589+
}
590+
549591
esp_sdp_init();
550592

551593
esp_bluetooth_sdp_hdr_overlay_t record = {(esp_bluetooth_sdp_types_t)0};
552594
record.type = ESP_SDP_TYPE_RAW;
553595
record.uuid.len = sizeof(UUID_IAP2);
554596
memcpy(record.uuid.uuid.uuid128, UUID_IAP2, sizeof(UUID_IAP2));
555-
record.service_name_length = strlen(sdp_service_name) + 1;
556-
record.service_name = (char *)sdp_service_name;
557-
// record.service_name_length = strlen(deviceName) + 1; // Doesn't seem to help the failed connects
558-
// record.service_name = (char *)deviceName;
597+
//record.service_name_length = strlen(sdp_service_name) + 1;
598+
//record.service_name = (char *)sdp_service_name;
599+
// Use the same EIR Local Name parameter as the Name in the IdentificationInformation
600+
record.service_name_length = strlen(deviceName) + 1;
601+
record.service_name = (char *)deviceName;
559602
// record.rfcomm_channel_number = 1; // Doesn't seem to help the failed connects
560603
esp_sdp_create_record((esp_bluetooth_sdp_record_t *)&record);
561604
}
-84 Bytes
Binary file not shown.

Firmware/RTK_Everywhere/RTK_Everywhere.ino

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -812,10 +812,10 @@ SparkFunAppleAccessoryDriver *appleAccessory; // Instantiated by beginAuthCoPro
812812

813813
const char *sdp_service_name = "iAP2";
814814

815-
// UUID: Big-Endian
816-
static const uint8_t UUID_IAP2[] = {0x00, 0x00, 0x00, 0x00, 0xDE, 0xCA, 0xFA, 0xDE, 0xDE, 0xCA, 0xDE, 0xAF, 0xDE, 0xCA, 0xCA, 0xFF};
817-
// UUID: Little-Endian
818-
//static const uint8_t UUID_IAP2[] = {0xFF, 0xCA, 0xCA, 0xDE, 0xAF, 0xDE, 0xCA, 0xDE, 0xDE, 0xFA, 0xCA, 0xDE, 0x00, 0x00, 0x00, 0x00};
815+
// UUID : Big-Endian
816+
//static const uint8_t UUID_IAP2[] = {0x00, 0x00, 0x00, 0x00, 0xDE, 0xCA, 0xFA, 0xDE, 0xDE, 0xCA, 0xDE, 0xAF, 0xDE, 0xCA, 0xCA, 0xFF};
817+
// UUID : Little-Endian
818+
static const uint8_t UUID_IAP2[] = {0xFF, 0xCA, 0xCA, 0xDE, 0xAF, 0xDE, 0xCA, 0xDE, 0xDE, 0xFA, 0xCA, 0xDE, 0x00, 0x00, 0x00, 0x00};
819819

820820
#endif
821821

@@ -845,6 +845,7 @@ uint32_t powerPressedStartTime; // Times how long the user has been holdin
845845
bool inMainMenu; // Set true when in the serial config menu system.
846846
bool btPrintEcho; // Set true when in the serial config menu system via Bluetooth.
847847
bool btPrintEchoExit; // When true, exit all config menus.
848+
bool sendAccessoryHandshakeOnBtConnect = false; // Send accessory handshake on BT connect
848849

849850
bool forceDisplayUpdate = true; // Goes true when setup is pressed, causes the display to refresh in real-time
850851
uint32_t lastSystemStateUpdate;

Firmware/RTK_Everywhere/bluetoothSelect.h

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ class BTSerialInterface : public virtual Stream
3737
virtual bool aclConnected() = 0;
3838
virtual uint8_t *aclGetAddress() = 0;
3939
virtual std::map<int, std::string> getChannels(const BTAddress &remoteAddress) = 0;
40+
41+
virtual void onConfirmRequest(void (*cbPtr)(uint32_t)) = 0;
42+
virtual void confirmReply(bool confirm) = 0;
43+
virtual void respondPasskey(uint32_t passkey) = 0;
44+
45+
virtual void deleteAllBondedDevices() = 0;
4046
};
4147

4248
class BTClassicSerial : public virtual BTSerialInterface, public BluetoothSerial
@@ -119,7 +125,7 @@ class BTClassicSerial : public virtual BTSerialInterface, public BluetoothSerial
119125

120126
void enableSSP(bool inputCapability, bool outputCapability)
121127
{
122-
return (BluetoothSerial::enableSSP(inputCapability, outputCapability));
128+
BluetoothSerial::enableSSP(inputCapability, outputCapability);
123129
}
124130

125131
bool aclConnected()
@@ -136,6 +142,26 @@ class BTClassicSerial : public virtual BTSerialInterface, public BluetoothSerial
136142
{
137143
return (BluetoothSerial::getChannels(remoteAddress));
138144
}
145+
146+
void onConfirmRequest(void (*cbPtr)(uint32_t))
147+
{
148+
BluetoothSerial::onConfirmRequest(cbPtr);
149+
}
150+
151+
void confirmReply(bool confirm)
152+
{
153+
BluetoothSerial::confirmReply((boolean)confirm);
154+
}
155+
156+
void respondPasskey(uint32_t passkey)
157+
{
158+
BluetoothSerial::respondPasskey(passkey);
159+
}
160+
161+
void deleteAllBondedDevices()
162+
{
163+
BluetoothSerial::deleteAllBondedDevices();
164+
}
139165
};
140166

141167
class BTLESerial : public virtual BTSerialInterface, public BleSerial
@@ -234,6 +260,14 @@ class BTLESerial : public virtual BTSerialInterface, public BleSerial
234260
return empty;
235261
}
236262

263+
void onConfirmRequest(void (*cbPtr)(uint32_t)) {}
264+
265+
void confirmReply(bool confirm) {}
266+
267+
void respondPasskey(uint32_t passkey) {}
268+
269+
void deleteAllBondedDevices() {}
270+
237271
// Callbacks removed in v2 of BleSerial. Using polled connected() in bluetoothUpdate()
238272
// override BLEServerCallbacks
239273
// void Server->onConnect(BLEServer *pServer)

Firmware/RTK_Everywhere/menuMain.ino

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -580,12 +580,14 @@ BluetoothRadioType_e mmChangeBluetoothProtocol(BluetoothRadioType_e bluetoothUse
580580
}
581581

582582
// Restart Bluetooth radio if settings have changed
583-
void mmSetBluetoothProtocol(BluetoothRadioType_e bluetoothUserChoice)
583+
void mmSetBluetoothProtocol(BluetoothRadioType_e bluetoothUserChoice, bool clearBtPairings)
584584
{
585-
if (bluetoothUserChoice != settings.bluetoothRadioType)
585+
if ((bluetoothUserChoice != settings.bluetoothRadioType)
586+
|| (clearBtPairings != settings.clearBtPairings))
586587
{
587588
bluetoothStop();
588589
settings.bluetoothRadioType = bluetoothUserChoice;
590+
settings.clearBtPairings = clearBtPairings;
589591
bluetoothStart();
590592
}
591593
}
@@ -594,6 +596,7 @@ void mmSetBluetoothProtocol(BluetoothRadioType_e bluetoothUserChoice)
594596
void menuRadio()
595597
{
596598
BluetoothRadioType_e bluetoothUserChoice = settings.bluetoothRadioType;
599+
bool clearBtPairings = settings.clearBtPairings;
597600

598601
while (1)
599602
{
@@ -668,6 +671,12 @@ void menuRadio()
668671
// Display Bluetooth menu
669672
mmDisplayBluetoothRadioMenu('b', bluetoothUserChoice);
670673

674+
// If in BLUETOOTH_RADIO_SPP_ACCESSORY_MODE, allow user to delete all pairings
675+
if (bluetoothUserChoice == BLUETOOTH_RADIO_SPP_ACCESSORY_MODE)
676+
{
677+
systemPrintf("c) Clear BT pairings: %s\r\n", clearBtPairings ? "Yes" : "No");
678+
}
679+
671680
systemPrintln("x) Exit");
672681

673682
byte incoming = getUserInputCharacterNumber();
@@ -676,6 +685,10 @@ void menuRadio()
676685
if (incoming == 'b')
677686
bluetoothUserChoice = mmChangeBluetoothProtocol(bluetoothUserChoice);
678687

688+
// Allow user to clear BT pairings - when BTClassicSerial is next begun
689+
else if ((incoming == 'c') && (bluetoothUserChoice == BLUETOOTH_RADIO_SPP_ACCESSORY_MODE))
690+
clearBtPairings ^= 1;
691+
679692
else if (incoming == 1)
680693
{
681694
settings.enableEspNow ^= 1;
@@ -808,7 +821,7 @@ void menuRadio()
808821
wifiEspNowOn(__FILE__, __LINE__);
809822

810823
// Restart Bluetooth radio if settings have changed
811-
mmSetBluetoothProtocol(bluetoothUserChoice);
824+
mmSetBluetoothProtocol(bluetoothUserChoice, clearBtPairings);
812825

813826
// LoRa radio state machine will start/stop radio upon next updateLora in loop()
814827

Firmware/RTK_Everywhere/menuSystem.ino

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,8 @@ void menuSystem()
417417
printUnknown(incoming);
418418
}
419419

420-
// Restart Bluetooth radio if settings have changed
421-
mmSetBluetoothProtocol(bluetoothUserChoice);
420+
// Restart Bluetooth radio if settings have changed (ignore clearBtPairings)
421+
mmSetBluetoothProtocol(bluetoothUserChoice, settings.clearBtPairings);
422422

423423
clearBuffer(); // Empty buffer of any newline chars
424424
}

Firmware/RTK_Everywhere/settings.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,7 @@ struct Settings
699699
BluetoothRadioType_e bluetoothRadioType = BLUETOOTH_RADIO_SPP_AND_BLE;
700700
uint16_t sppRxQueueSize = 512 * 4;
701701
uint16_t sppTxQueueSize = 32;
702+
bool clearBtPairings = true; // Clear MFi Accessory SSP pairings
702703

703704
// Corrections
704705
int correctionsSourcesLifetime_s = 30; // Expire a corrections source if no data is seen for this many seconds
@@ -1341,6 +1342,7 @@ const RTK_Settings_Entry rtkSettingsEntries[] =
13411342
{ 1, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, tBtRadio, 0, & settings.bluetoothRadioType, "bluetoothRadioType", },
13421343
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _uint16_t, 0, & settings.sppRxQueueSize, "sppRxQueueSize", },
13431344
{ 0, 0, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _uint16_t, 0, & settings.sppTxQueueSize, "sppTxQueueSize", },
1345+
{ 0, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _bool, 0, & settings.clearBtPairings, "clearBtPairings", },
13441346

13451347
// Corrections
13461348
{ 1, 1, 0, 1, 1, 1, 1, 1, 1, ALL, 1, _int, 0, & settings.correctionsSourcesLifetime_s, "correctionsSourcesLifetime", },

0 commit comments

Comments
 (0)