From ede9dcf183dbb0d4be1208d19965d2666856cbe7 Mon Sep 17 00:00:00 2001 From: Florian Muellerklein Date: Thu, 17 Apr 2025 19:36:14 -0400 Subject: [PATCH 1/3] added basic ble notify example for sendin gps data --- bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino | 230 ++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino diff --git a/bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino b/bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino new file mode 100644 index 0000000..6e4d94a --- /dev/null +++ b/bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino @@ -0,0 +1,230 @@ +/* + Heavily based on the example BLE notify code provided with ESP32 on Arduino IDE. + Great video that goes over BLE concepts with ESP32: https://youtu.be/0Yvd_k0hbVs?si=qxlBrfBaO1deO5Bh + + This basic example will create a BLE notify server and send updates everytime the GPS module updates. + A 32 byte payload is created and transfered on each GPS update + - bytes 0-7 latitude + - bytes 8-15 longitude + - bytes 16-23 speed m/s + - bytes 24-25 year + - bytes 26 month + - bytes 27 day + - bytes 28 hours + - bytes 29 minute + - bytes 30 second + - bytes 31 centisecond + + **Pin Configuration:** + - Rx to GPS module (tx from ESP32) - pin 17 of SV1 - GPIO40 of ESP32 + - Tx from GPS module (rx to ESP32) - pin 18 of SV1 - GPIO41 of ESP32 + - P1PPS from GPS module - pin 16 of SV1 - GPIO39 of ESP32 + - PSE_SEL from GPS moduel - pin 11 of SV1 - GPIO45 of ESP32 +*/ +#include +#include +#include +#include +#include +#include +#include + +// Change to give device custom name that displays during GPS search +String deviceName = "DIY GPS"; + +// Change to whatever you'd like, good generator here: https://www.uuidgenerator.net +// UUIDs are required +#define SERVICE_UUID "ECBB8159-FBA9-4123-AEF0-CA06E1D390D9" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +BLEServer *pServer = NULL; +BLECharacteristic *pCharacteristic = NULL; +BLE2901 *descriptor_2901 = NULL; + +bool deviceConnected = false; +bool oldDeviceConnected = false; + +// GPS parsing +TinyGPSPlus gps; + +// type defs for packing gps data into bytes for ble +union latBytes { + double lat; + uint8_t bytes[8]; +} latUnion; + +union longBytes { + double lng; + uint8_t bytes[8]; +} longUnion; + +union speedBytes { + double speed; + uint8_t bytes[8]; +} speedUnion; + +union yearBytes { + int year; + uint8_t bytes[2]; +} yearUnion; + +union monthBytes { + int month; + uint8_t bytes[1]; +} monthUnion; + +union dayBytes { + int day; + uint8_t bytes[1]; +} dayUnion; + +union hourBytes { + int hour; + uint8_t bytes[1]; +} hourUnion; + +union minBytes { + int min; + uint8_t bytes[1]; +} minUnion; + +union secBytes { + int sec; + uint8_t bytes[1]; +} secUnion; + +union centBytes { + int cent; + uint8_t bytes[1]; +} centUnion; + +class MyServerCallbacks : public BLEServerCallbacks { + void onConnect(BLEServer *pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer *pServer) { + deviceConnected = false; + } +}; + + +void setup() { + Serial.begin(9600); + // start reading from the gps module + Serial2.begin(9600, SERIAL_8N1, 41, 40); // Initialize serial communication with the GPS module + // Note: Replace 41 and 40 with actual RX and TX pins if required by your hardware. + + Serial.println("GPS Started!"); + + BLEDevice::init(deviceName); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_NOTIFY + ); + + // Creates BLE Descriptor 0x2902: Client Characteristic Configuration Descriptor (CCCD) + pCharacteristic->addDescriptor(new BLE2902()); + // Adds also the Characteristic User Description - 0x2901 descriptor + descriptor_2901 = new BLE2901(); + descriptor_2901->setDescription("GPS Data"); + descriptor_2901->setAccessPermissions(ESP_GATT_PERM_READ); // enforce read only - default is Read|Write + pCharacteristic->addDescriptor(descriptor_2901); + + // Start the service + pService->start(); + + // Start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter + BLEDevice::startAdvertising(); + Serial.println("Waiting a client connection to notify..."); + +} + + +void loop() { + while (Serial2.available()) { + gps.encode(Serial2.read()); + } + + if (deviceConnected && gps.location.isUpdated()) { + // parse GPS data + latUnion.lat = gps.location.lat(); + longUnion.lng = gps.location.lng(); + speedUnion.speed = gps.speed.mps(); + yearUnion.year = gps.date.year(); + monthUnion.month = gps.date.month(); + dayUnion.day = gps.date.day(); + hourUnion.hour = gps.time.hour(); + minUnion.min = gps.time.minute(); + secUnion.sec = gps.time.second(); + centUnion.cent = gps.time.centisecond(); + + // create data packet for BLE transfer + uint8_t dataPacket[32]; + dataPacket[0] = latUnion.bytes[0]; + dataPacket[1] = latUnion.bytes[1]; + dataPacket[2] = latUnion.bytes[2]; + dataPacket[3] = latUnion.bytes[3]; + dataPacket[4] = latUnion.bytes[4]; + dataPacket[5] = latUnion.bytes[5]; + dataPacket[6] = latUnion.bytes[6]; + dataPacket[7] = latUnion.bytes[7]; + dataPacket[8] = longUnion.bytes[0]; + dataPacket[9] = longUnion.bytes[1]; + dataPacket[10] = longUnion.bytes[2]; + dataPacket[11] = longUnion.bytes[3]; + dataPacket[12] = longUnion.bytes[4]; + dataPacket[13] = longUnion.bytes[5]; + dataPacket[14] = longUnion.bytes[6]; + dataPacket[15] = longUnion.bytes[7]; + dataPacket[16] = speedUnion.bytes[0]; + dataPacket[17] = speedUnion.bytes[1]; + dataPacket[18] = speedUnion.bytes[2]; + dataPacket[19] = speedUnion.bytes[3]; + dataPacket[20] = speedUnion.bytes[4]; + dataPacket[21] = speedUnion.bytes[5]; + dataPacket[22] = speedUnion.bytes[6]; + dataPacket[23] = speedUnion.bytes[7]; + dataPacket[24] = yearUnion.bytes[0]; + dataPacket[25] = yearUnion.bytes[1]; + dataPacket[26] = monthUnion.bytes[0]; + dataPacket[27] = dayUnion.bytes[0]; + dataPacket[28] = hourUnion.bytes[0]; + dataPacket[29] = minUnion.bytes[0]; + dataPacket[30] = secUnion.bytes[0]; + dataPacket[31] = centUnion.bytes[0]; + + // notify changed value + if (deviceConnected) { + pCharacteristic->setValue((uint8_t *)&dataPacket, 32); + pCharacteristic->notify(); + delay(10); + } + } + + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + + // connecting + if (deviceConnected && !oldDeviceConnected) { + oldDeviceConnected = deviceConnected; + } +} From 58c0136352e07f3b46e0a609046dcac0ef158e9f Mon Sep 17 00:00:00 2001 From: Florian Muellerklein Date: Tue, 20 May 2025 20:37:38 -0400 Subject: [PATCH 2/3] added the ability to switch GPS update rates, defaulting to 10hz --- bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino | 67 ++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino b/bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino index 6e4d94a..59a7148 100644 --- a/bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino +++ b/bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino @@ -30,13 +30,21 @@ #include // Change to give device custom name that displays during GPS search -String deviceName = "DIY GPS"; +String deviceName = "ASL ESP32 + GPS"; // Change to whatever you'd like, good generator here: https://www.uuidgenerator.net // UUIDs are required #define SERVICE_UUID "ECBB8159-FBA9-4123-AEF0-CA06E1D390D9" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" +// GPS Settings + +// Desired update rate in Hz (1,5, 10, 25, 50) +static const uint8_t updateRateHz = 10; +// Baud-rate code for 115200 is 5 +static const uint8_t baudCode115k = 5; + + BLEServer *pServer = NULL; BLECharacteristic *pCharacteristic = NULL; BLE2901 *descriptor_2901 = NULL; @@ -98,6 +106,55 @@ union centBytes { uint8_t bytes[1]; } centUnion; +void configureSkytraqUpdateRate(uint8_t rateHz) { + // Payload bytes: { MsgID=0x0E, Rate, Attributes=0 } + const uint8_t payloadLen = 3; + uint8_t payload[payloadLen] = { 0x0E, rateHz, 0x00 }; + + // XOR‐checksum over the payload bytes + uint8_t checksum = 0; + for (uint8_t i = 0; i < payloadLen; i++) { + checksum ^= payload[i]; + } + + // Build the full packet: + // A0 A1 00 03 [payload…] [checksum] 0D 0A + uint8_t pkt[2 + 2 + payloadLen + 1 + 2] = { + 0xA0, 0xA1, // header + 0x00, payloadLen, // payload length MSB=0, LSB=3 + payload[0], payload[1], payload[2], // payload + checksum, // XOR of payload[] + 0x0D, 0x0A // terminator + }; + + Serial2.write(pkt, sizeof(pkt)); +} + + +void configureSkytraqBaudRate(uint8_t baudCode) { + // Payload bytes: { MsgID=0x05, COM_port=0, BaudCode, Attributes=0 } + const uint8_t payloadLen = 4; + uint8_t payload[payloadLen] = { 0x05, 0x00, baudCode, 0x00 }; + + // XOR-checksum over the payload bytes + uint8_t checksum = 0; + for (uint8_t i = 0; i < payloadLen; i++) { + checksum ^= payload[i]; + } + + // Build the full packet: + // A0 A1 00 04 [payload…] [checksum] 0D 0A + uint8_t pkt[2 + 2 + payloadLen + 1 + 2] = { + 0xA0, 0xA1, // header + 0x00, payloadLen, // payload length MSB=0, LSB=4 + payload[0], payload[1], payload[2], payload[3], // payload + checksum, // XOR of payload[] + 0x0D, 0x0A // terminator + }; + + Serial2.write(pkt, sizeof(pkt)); +} + class MyServerCallbacks : public BLEServerCallbacks { void onConnect(BLEServer *pServer) { deviceConnected = true; @@ -111,11 +168,17 @@ class MyServerCallbacks : public BLEServerCallbacks { void setup() { Serial.begin(9600); + // start reading from the gps module Serial2.begin(9600, SERIAL_8N1, 41, 40); // Initialize serial communication with the GPS module // Note: Replace 41 and 40 with actual RX and TX pins if required by your hardware. + configureSkytraqBaudRate(baudCode115k); + // give module a moment to switch + delay(1000); + Serial2.begin(115200); + configureSkytraqUpdateRate(updateRateHz); + Serial.println("GPS Module Initialized"); - Serial.println("GPS Started!"); BLEDevice::init(deviceName); From 2b8e2b2f766e16dbba17846208fc79bfec4e685a Mon Sep 17 00:00:00 2001 From: Florian Muellerklein Date: Sun, 8 Jun 2025 21:44:01 -0400 Subject: [PATCH 3/3] removed hardware serial import, switched to Serial1 --- bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino b/bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino index 59a7148..3ad0bc7 100644 --- a/bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino +++ b/bolt-ons/gps-bolt-on/GPS_BLE_Notify.ino @@ -1,3 +1,5 @@ +#include + /* Heavily based on the example BLE notify code provided with ESP32 on Arduino IDE. Great video that goes over BLE concepts with ESP32: https://youtu.be/0Yvd_k0hbVs?si=qxlBrfBaO1deO5Bh @@ -26,7 +28,6 @@ #include #include #include -#include #include // Change to give device custom name that displays during GPS search @@ -127,7 +128,7 @@ void configureSkytraqUpdateRate(uint8_t rateHz) { 0x0D, 0x0A // terminator }; - Serial2.write(pkt, sizeof(pkt)); + Serial1.write(pkt, sizeof(pkt)); } @@ -152,7 +153,7 @@ void configureSkytraqBaudRate(uint8_t baudCode) { 0x0D, 0x0A // terminator }; - Serial2.write(pkt, sizeof(pkt)); + Serial1.write(pkt, sizeof(pkt)); } class MyServerCallbacks : public BLEServerCallbacks { @@ -170,12 +171,12 @@ void setup() { Serial.begin(9600); // start reading from the gps module - Serial2.begin(9600, SERIAL_8N1, 41, 40); // Initialize serial communication with the GPS module + Serial1.begin(9600, SERIAL_8N1, 41, 40); // Initialize serial communication with the GPS module // Note: Replace 41 and 40 with actual RX and TX pins if required by your hardware. configureSkytraqBaudRate(baudCode115k); // give module a moment to switch delay(1000); - Serial2.begin(115200); + Serial1.begin(115200); configureSkytraqUpdateRate(updateRateHz); Serial.println("GPS Module Initialized"); @@ -218,8 +219,8 @@ void setup() { void loop() { - while (Serial2.available()) { - gps.encode(Serial2.read()); + while (Serial1.available()) { + gps.encode(Serial1.read()); } if (deviceConnected && gps.location.isUpdated()) { @@ -290,4 +291,4 @@ void loop() { if (deviceConnected && !oldDeviceConnected) { oldDeviceConnected = deviceConnected; } -} +} \ No newline at end of file