Skip to content

Commit 3a80756

Browse files
committed
Update ESP32_BluetoothSerial.ino
1 parent caa4100 commit 3a80756

File tree

1 file changed

+133
-46
lines changed

1 file changed

+133
-46
lines changed

examples/ESP32_BluetoothSerial/ESP32_BluetoothSerial.ino

Lines changed: 133 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
2020
NMEA GGA and RMC are shared with apps like Maps, over the Control Session
2121
as Location Information.
22-
NMEA GGA, RMC and GST are shared with apps like Field Maps, over a dedicated
23-
External Accessory protocol session.
22+
NMEA GGA, RMC, GST, VTG, GSA and GSV are shared with apps like Field Maps,
23+
over a dedicated External Accessory protocol session.
2424
2525
This example contains a copy of Espressif's BluetoothSerial code, with a
2626
modified ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT event.
@@ -50,18 +50,24 @@
5050
(SDP is disabled in the standard libbt.a)
5151
It also corrects the UUID byte-order reversal in add_raw_sdp
5252
53-
Tried and tested on: Arduino esp32 v3.0.7 (IDF 5.1)
53+
Written for and tested on: Arduino esp32 v3.0.7 (IDF 5.1)
5454
5555
*/
5656

57+
// ===================================================================================================================
58+
// ESP32 Service Discovery Protocol
59+
60+
#include "esp_sdp_api.h"
61+
5762
// ===================================================================================================================
5863
// GNSS
5964

6065
#include <SparkFun_u-blox_GNSS_v3.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS_v3
6166
SFE_UBLOX_GNSS myGNSS;
6267
char latestGPGGA[SFE_UBLOX_MAX_NMEA_BYTE_COUNT] = { 0 };
6368
char latestGPRMC[SFE_UBLOX_MAX_NMEA_BYTE_COUNT] = { 0 };
64-
char latestGPGST[SFE_UBLOX_MAX_NMEA_BYTE_COUNT] = { 0 };
69+
const size_t additionalNmeaBufferSize = 4096;
70+
char additionalNmeaBuffer[additionalNmeaBufferSize] = { 0 };
6571

6672
void newGPGGA(NMEA_GGA_data_t *nmeaData)
6773
{
@@ -83,25 +89,15 @@ void newGPRMC(NMEA_RMC_data_t *nmeaData)
8389
}
8490
}
8591

86-
void newGPGST(NMEA_GST_data_t *nmeaData)
87-
{
88-
if (nmeaData->length < SFE_UBLOX_MAX_NMEA_BYTE_COUNT)
89-
{
90-
strncpy(latestGPGST, (const char *)nmeaData->nmea, nmeaData->length);
91-
//Serial.print(latestGPGST); // .nmea is printable (NULL-terminated) and already has \r\n on the end
92-
latestGPGST[nmeaData->length - 2] = 0; // Truncate after checksum. Remove CR LF
93-
}
94-
}
95-
9692
// ===================================================================================================================
9793
// Apple Accessory
9894

9995
#include <SparkFun_Apple_Accessory.h>
10096

10197
SparkFunAppleAccessoryDriver appleAccessory;
10298

103-
const char *accessoryName = "SparkFun MFi Test";
104-
const char *modelIdentifier = "SparkFun MFi Test";
99+
const char *accessoryName = "SparkPNT RTK Flex";
100+
const char *modelIdentifier = "SparkPNT RTK Flex";
105101
const char *manufacturer = "SparkFun Electronics";
106102
const char *serialNumber = "123456";
107103
const char *firmwareVersion = "1.0.0";
@@ -117,10 +113,10 @@ const char *productPlanUID = "0123456789ABCDEF"; // This comes from the MFi Port
117113
#include "src/BluetoothSerial/BluetoothSerial.h" //Local copy for modifying ESP_BT_GAP_ACL_CONN_CMPL_STAT_EVT event
118114
BluetoothSerial SerialBT;
119115

120-
#include "esp_sdp_api.h"
121-
122116
const char *sdp_service_name = "iAP2";
123117

118+
const int rfcommChanneliAP2 = 2; // We can use RFCOMM channel 2 for iAP2
119+
124120
// With the byte-order correction in libbt.a btc_sdp.c add_raw_sdp() (ARRAY_TO_BE_STREAM_REVERSE replaces ARRAY_TO_BE_STREAM),
125121
// we need to provide the UUID as little-endian:
126122
// UUID : Little-Endian
@@ -138,6 +134,52 @@ void transportDisconnect(bool *disconnected)
138134
*disconnected = SerialBT.disconnect();
139135
}
140136

137+
// ===================================================================================================================
138+
// Callback for Service Discovery Protocol
139+
// This allows the iAP2 record to be created _after_ SDP is initialized
140+
// and the Serial Port (SPP) record has been created
141+
142+
volatile bool sdpCreateRecordEvent = false; // Flag to indicate when the iAP2 record has been created
143+
144+
static void esp_sdp_callback(esp_sdp_cb_event_t event, esp_sdp_cb_param_t *param)
145+
{
146+
switch (event) {
147+
case ESP_SDP_INIT_EVT:
148+
Serial.printf("ESP_SDP_INIT_EVT: status: %d\r\n", param->init.status);
149+
if (param->init.status == ESP_SDP_SUCCESS) {
150+
// SDP has been initialized. _Now_ we can create the iAP2 record!
151+
esp_bluetooth_sdp_hdr_overlay_t record = {(esp_bluetooth_sdp_types_t)0};
152+
record.type = ESP_SDP_TYPE_RAW;
153+
record.uuid.len = sizeof(UUID_IAP2);
154+
memcpy(record.uuid.uuid.uuid128, UUID_IAP2, sizeof(UUID_IAP2));
155+
// The service_name isn't critical. But we can't not set one.
156+
// (If we don't set a name, the record doesn't get created.)
157+
record.service_name_length = strlen(sdp_service_name) + 1;
158+
record.service_name = (char *)sdp_service_name;
159+
record.rfcomm_channel_number = rfcommChanneliAP2; // RFCOMM channel
160+
record.l2cap_psm = -1;
161+
record.profile_version = -1;
162+
esp_sdp_create_record((esp_bluetooth_sdp_record_t *)&record);
163+
}
164+
break;
165+
case ESP_SDP_DEINIT_EVT:
166+
Serial.printf("ESP_SDP_DEINIT_EVT: status: %d\r\n", param->deinit.status);
167+
break;
168+
case ESP_SDP_SEARCH_COMP_EVT:
169+
Serial.printf("ESP_SDP_SEARCH_COMP_EVT: status: %d\r\n", param->search.status);
170+
break;
171+
case ESP_SDP_CREATE_RECORD_COMP_EVT:
172+
Serial.printf("ESP_SDP_CREATE_RECORD_COMP_EVT: status: %d\r\n", param->create_record.status);
173+
sdpCreateRecordEvent = true; // Flag that the iAP2 record has been created
174+
break;
175+
case ESP_SDP_REMOVE_RECORD_COMP_EVT:
176+
Serial.printf("ESP_SDP_REMOVE_RECORD_COMP_EVT: status: %d\r\n", param->remove_record.status);
177+
break;
178+
default:
179+
break;
180+
}
181+
}
182+
141183
// ===================================================================================================================
142184

143185
void setup()
@@ -150,9 +192,10 @@ void setup()
150192
// ==============================================================================================================
151193
// Setup Bluetooth
152194

153-
SerialBT.enableSSP(false, true); //Enable secure pairing with PIN
195+
SerialBT.enableSSP(false, false); //Enable secure pairing
154196

155-
SerialBT.begin(accessoryName, true, true); //Bluetooth device name, start in master mode, disable BLE
197+
//Bluetooth device name, master mode, disable BLE, rxQueueSize, txQueueSize
198+
SerialBT.begin(accessoryName, false, true, 2048, 512);
156199

157200
Serial.println("The BT device was started.");
158201

@@ -166,16 +209,10 @@ void setup()
166209
// (But, deleting them is essential if you have changed the secure pairing mode)
167210
Serial.println("Deleting all previous bonded devices.");
168211
SerialBT.deleteAllBondedDevices(); // Must be called after begin
169-
170-
esp_sdp_init();
171212

172-
esp_bluetooth_sdp_hdr_overlay_t record = {(esp_bluetooth_sdp_types_t)0};
173-
record.type = ESP_SDP_TYPE_RAW;
174-
record.uuid.len = sizeof(UUID_IAP2);
175-
memcpy(record.uuid.uuid.uuid128, UUID_IAP2, sizeof(UUID_IAP2));
176-
record.service_name_length = strlen(accessoryName) + 1;
177-
record.service_name = (char *)accessoryName;
178-
esp_sdp_create_record((esp_bluetooth_sdp_record_t *)&record);
213+
// The SDP callback will create the iAP2 record
214+
esp_sdp_register_callback(esp_sdp_callback);
215+
esp_sdp_init();
179216

180217
// ==============================================================================================================
181218
// Setup Apple Accessory and Authentication Coprocessor
@@ -204,8 +241,13 @@ void setup()
204241
appleAccessory.setLocationInfoComponentName(LIComponentName);
205242
appleAccessory.setProductPlanUID(productPlanUID);
206243

207-
// Pass the pointers for the latest NMEA data into the Accessory driver
208-
appleAccessory.setNMEApointers(latestGPGGA, latestGPRMC, latestGPGST);
244+
// Pass the pointers for the latest NMEA GGA and RMC data into the Accessory driver
245+
// GGA and RMC are passed to apps over the iAP2 Control Session
246+
appleAccessory.setNMEApointers(latestGPGGA, latestGPRMC);
247+
248+
// Pass the pointer for additional NMEA GST, VTG, GSA and GSV data
249+
// GGA, RMC, GST, VTG, GSA and GSV are passed to apps over an iAP2 EA Session
250+
appleAccessory.setEASessionPointer(additionalNmeaBuffer);
209251

210252
// Pass the transport connected and disconnect methods into the accessory driver
211253
appleAccessory.setTransportConnectedMethod(&transportConnected);
@@ -216,6 +258,9 @@ void setup()
216258

217259
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
218260

261+
// Storage for NMEA GST, VTG, GSA and GSV. setFileBufferSize must be called _before_ .begin
262+
myGNSS.setFileBufferSize(additionalNmeaBufferSize);
263+
219264
while (!myGNSS.begin())
220265
{
221266
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring."));
@@ -224,11 +269,11 @@ void setup()
224269
// Disable or enable various NMEA sentences over the I2C interface
225270
myGNSS.setI2COutput(COM_TYPE_NMEA | COM_TYPE_UBX); // Turn on both UBX and NMEA sentences on I2C. (Turn off RTCM and SPARTN)
226271
myGNSS.newCfgValset(VAL_LAYER_RAM_BBR); // Use cfgValset to disable / enable individual NMEA messages
227-
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GLL_I2C, 0); // Disable all NMEA messages except GGA and RMC. Enable GST
228-
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSA_I2C, 0);
229-
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSV_I2C, 0);
272+
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GLL_I2C, 0); // Enable required NMEA messages
273+
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSA_I2C, 1);
274+
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSV_I2C, 1);
230275
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_RMC_I2C, 1);
231-
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_VTG_I2C, 0);
276+
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_VTG_I2C, 1);
232277
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_I2C, 1);
233278
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_ZDA_I2C, 0);
234279
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GST_I2C, 1);
@@ -250,9 +295,9 @@ void setup()
250295
// Set up the callback for GPRMC
251296
myGNSS.setNMEAGPRMCcallbackPtr(&newGPRMC);
252297

253-
// Set up the callback for GPGST
254-
myGNSS.setNMEAGPGSTcallbackPtr(&newGPGST);
255-
298+
// Log NMEA GST, VTG, GSA and GSV in the GNSS library file buffer
299+
myGNSS.setNMEALoggingMask(SFE_UBLOX_FILTER_NMEA_GST | SFE_UBLOX_FILTER_NMEA_VTG
300+
| SFE_UBLOX_FILTER_NMEA_GSA | SFE_UBLOX_FILTER_NMEA_GSV);
256301
}
257302

258303
// ===================================================================================================================
@@ -271,20 +316,62 @@ void loop()
271316
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
272317

273318
// ==============================================================================================================
274-
// Check for a new device connection
319+
// Copy latest GST, VTG, GSA and GSV into additionalNmeaBuffer
275320

276-
if (SerialBT.aclConnected() == true)
321+
// If the Apple Accessory is not sending data to the EA Session, copy the data into additionalNmeaBuffer.
322+
// Bad things would happen if we were to manipulate additionalNmeaBuffer while appleAccessory is using it.
323+
if (appleAccessory.latestEASessionDataIsBlocking() == false)
277324
{
278-
Serial.println("Apple Device found, connecting...");
325+
size_t spaceAvailable = additionalNmeaBufferSize - strlen(additionalNmeaBuffer);
326+
if (spaceAvailable >= 1)
327+
spaceAvailable -= 1; // Leave room for the NULL
328+
uint16_t fileBufferAvailable = myGNSS.fileBufferAvailable(); // Check how much data is available
329+
while (spaceAvailable < fileBufferAvailable) // If the buffer is full, delete the oldest message(s)
330+
{
331+
const char *lfPtr = strstr(additionalNmeaBuffer, "\n"); // Find the first LF
332+
if (lfPtr == nullptr)
333+
break; // Something has gone badly wrong...
334+
lfPtr++; // Point at the byte after the LF
335+
size_t oldLen = lfPtr - additionalNmeaBuffer; // This much data is old
336+
size_t newLen = strlen(additionalNmeaBuffer) - oldLen; // This much is new (not old)
337+
for (size_t i = 0; i <= newLen; i++) // Move the new data over the old. Include the NULL
338+
additionalNmeaBuffer[i] = additionalNmeaBuffer[oldLen + i];
339+
spaceAvailable += oldLen;
340+
}
341+
size_t dataLen = strlen(additionalNmeaBuffer);
342+
myGNSS.extractFileBufferData((uint8_t *)&additionalNmeaBuffer[dataLen], fileBufferAvailable); // Add the new NMEA data
343+
dataLen += fileBufferAvailable;
344+
additionalNmeaBuffer[dataLen] = 0; // NULL terminate
345+
}
279346

280-
delay(2);
347+
// ==============================================================================================================
348+
// Check if the iAP2 SDP record has been created
349+
// If it has, restart the SPP Server
281350

282-
SerialBT.connect(SerialBT.aclGetAddress(), 1); //Connect on channel 1
351+
if (sdpCreateRecordEvent)
352+
{
353+
sdpCreateRecordEvent = false;
354+
esp_spp_stop_srv();
355+
esp_spp_start_srv(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_SLAVE, rfcommChanneliAP2, "ESP32SPP2");
356+
}
357+
358+
// ==============================================================================================================
359+
// Check for a new device connection
360+
361+
if (SerialBT.aclConnected() == true)
362+
{
363+
Serial.println("Apple Device found. Waiting for connection...");
283364

284-
if (SerialBT.connected())
365+
unsigned long connectStart = millis();
366+
while ((millis() - connectStart) < 5000)
285367
{
286-
Serial.println("Sending handshake...");
287-
appleAccessory.startHandshake(&SerialBT);
368+
if (SerialBT.connected())
369+
{
370+
Serial.println("Device connected. Sending handshake...");
371+
appleAccessory.startHandshake(&SerialBT);
372+
break;
373+
}
374+
delay(10);
288375
}
289376
}
290377

0 commit comments

Comments
 (0)