diff --git a/Firmware/RTK_Surveyor/Begin.ino b/Firmware/RTK_Surveyor/Begin.ino index f64b9c2c0..e1aa45311 100644 --- a/Firmware/RTK_Surveyor/Begin.ino +++ b/Firmware/RTK_Surveyor/Begin.ino @@ -338,7 +338,7 @@ void pinUART2Task( void *pvParameters ) vTaskDelete( NULL ); //Delete task once it has run once } -//Serial Read/Write tasks for the F9P must be started after BT is up and running otherwise SerialBT.available will cause reboot +//Serial Read/Write tasks for the F9P must be started after BT is up and running otherwise SerialBT->available will cause reboot void startUART2Tasks() { //Start the tasks for handling incoming and outgoing BT bytes to/from ZED-F9P diff --git a/Firmware/RTK_Surveyor/Bluetooth.ino b/Firmware/RTK_Surveyor/Bluetooth.ino index 07d6659df..d56d381cd 100644 --- a/Firmware/RTK_Surveyor/Bluetooth.ino +++ b/Firmware/RTK_Surveyor/Bluetooth.ino @@ -28,8 +28,7 @@ Bluetooth States: //---------------------------------------- #ifdef COMPILE_BT - -static BluetoothSerial bluetoothSerial; +BTSerialInterface *bluetoothSerial; static volatile byte bluetoothState = BT_OFF; //---------------------------------------- @@ -74,7 +73,7 @@ byte bluetoothGetState() bool bluetoothIsCongested() { #ifdef COMPILE_BT - return bluetoothSerial.isCongested(); + return bluetoothSerial->isCongested(); #else //COMPILE_BT return false; #endif //COMPILE_BT @@ -84,7 +83,7 @@ bool bluetoothIsCongested() int bluetoothReadBytes(uint8_t * buffer, int length) { #ifdef COMPILE_BT - return bluetoothSerial.readBytes(buffer, length); + return bluetoothSerial->readBytes(buffer, length); #else //COMPILE_BT return 0; #endif //COMPILE_BT @@ -94,7 +93,7 @@ int bluetoothReadBytes(uint8_t * buffer, int length) bool bluetoothRxDataAvailable() { #ifdef COMPILE_BT - return bluetoothSerial.available(); + return bluetoothSerial->available(); #else //COMPILE_BT return false; #endif //COMPILE_BT @@ -116,7 +115,15 @@ void bluetoothStart() sprintf(deviceName, "%s %s%02X%02X", platformPrefix, stateName, unitMACAddress[4], unitMACAddress[5]); - if (bluetoothSerial.begin(deviceName, false, settings.sppRxQueueSize, settings.sppTxQueueSize) == false) //localName, isMaster, rxBufferSize, txBufferSize + // BLE vs Bluetooth Classic + if (settings.enableBLE) + bluetoothSerial = new BTLESerial(); + else + { + bluetoothSerial = new BTClassicSerial(); + } + + if (bluetoothSerial->begin(deviceName, false, settings.sppRxQueueSize, settings.sppTxQueueSize) == false) //localName, isMaster, rxBufferSize, txBufferSize { Serial.println("An error occurred initializing Bluetooth"); @@ -145,8 +152,8 @@ void bluetoothStart() esp_bt_gap_set_pin(pin_type, 4, pin_code); //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - bluetoothSerial.register_callback(bluetoothCallback); //Controls BT Status LED on Surveyor - bluetoothSerial.setTimeout(250); + bluetoothSerial->register_callback(bluetoothCallback); //Controls BT Status LED on Surveyor + bluetoothSerial->setTimeout(250); Serial.print("Bluetooth broadcasting as: "); Serial.println(deviceName); @@ -172,10 +179,10 @@ void bluetoothStop() #ifdef COMPILE_BT if (bluetoothState == BT_NOTCONNECTED || bluetoothState == BT_CONNECTED) { - bluetoothSerial.register_callback(NULL); - bluetoothSerial.flush(); //Complete any transfers - bluetoothSerial.disconnect(); //Drop any clients - bluetoothSerial.end(); //bluetoothSerial.end() will release significant RAM (~100k!) but a bluetoothSerial.start will crash. + bluetoothSerial->register_callback(NULL); + bluetoothSerial->flush(); //Complete any transfers + bluetoothSerial->disconnect(); //Drop any clients + bluetoothSerial->end(); //bluetoothSerial->end() will release significant RAM (~100k!) but a bluetoothSerial->start will crash. log_d("Bluetooth turned off"); @@ -191,7 +198,7 @@ int bluetoothWriteBytes(const uint8_t * buffer, int length) { #ifdef COMPILE_BT //Push new data to BT SPP - return bluetoothSerial.write(buffer, length); + return bluetoothSerial->write(buffer, length); #else //COMPILE_BT return 0; #endif //COMPILE_BT diff --git a/Firmware/RTK_Surveyor/NVM.ino b/Firmware/RTK_Surveyor/NVM.ino index 734cc3c14..cf2c3d6d6 100644 --- a/Firmware/RTK_Surveyor/NVM.ino +++ b/Firmware/RTK_Surveyor/NVM.ino @@ -267,6 +267,7 @@ void recordSystemSettingsToFile(File * settingsFile) } settingsFile->printf("%s=%d\n\r", "espnowPeerCount", settings.espnowPeerCount); settingsFile->printf("%s=%d\n\r", "enableNtripServerMessageParsing", settings.enableNtripServerMessageParsing); + settingsFile->printf("%s=%d\n\r", "enableBLE", settings.enableBLE); //Record constellation settings for (int x = 0 ; x < MAX_CONSTELLATIONS ; x++) @@ -875,6 +876,8 @@ bool parseLine(char* str, Settings *settings) settings->espnowPeerCount = d; else if (strcmp(settingName, "enableNtripServerMessageParsing") == 0) settings->enableNtripServerMessageParsing = d; + else if (strcmp(settingName, "enableBLE") == 0) + settings->enableBLE = d; //Check for bulk settings (constellations, message rates, ESPNOW Peers) //Must be last on else list diff --git a/Firmware/RTK_Surveyor/RTK_Surveyor.ino b/Firmware/RTK_Surveyor/RTK_Surveyor.ino index 594ab03a7..ee76c394e 100644 --- a/Firmware/RTK_Surveyor/RTK_Surveyor.ino +++ b/Firmware/RTK_Surveyor/RTK_Surveyor.ino @@ -242,8 +242,16 @@ float battChangeRate = 0.0; //Hardware serial and BT buffers //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #ifdef COMPILE_BT +// See bluetoothSelect.h for implemenation +#include "bluetoothSelect.h" + //We use a local copy of the BluetoothSerial library so that we can increase the RX buffer. See issue: https://github.com/sparkfun/SparkFun_RTK_Firmware/issues/23 -#include "src/BluetoothSerial/BluetoothSerial.h" +// #include "src/BluetoothSerial/BluetoothSerial.h" +// BluetoothSerial SerialBT; + +// BLE Support originally from https://github.com/avinabmalla/ESP32_BleSerial/tree/bad5ff841800853a61e431ea751f8ea9d7a1df21 +// #include "src/BleSerial/BleSerial.h" +// BleSerial SerialBLE; #endif char platformPrefix[55] = "Surveyor"; //Sets the prefix for broadcast names diff --git a/Firmware/RTK_Surveyor/bluetoothSelect.h b/Firmware/RTK_Surveyor/bluetoothSelect.h new file mode 100644 index 000000000..af708a904 --- /dev/null +++ b/Firmware/RTK_Surveyor/bluetoothSelect.h @@ -0,0 +1,161 @@ +#ifdef COMPILE_BT + +#include "src/BluetoothSerial/BluetoothSerial.h" +#include "src/BleSerial/BleSerial.h" + +class BTSerialInterface +{ + public: + virtual bool begin(String deviceName, bool isMaster, uint16_t rxQueueSize, uint16_t txQueueSize) = 0; + virtual void disconnect() = 0; + virtual void end() = 0; + virtual esp_err_t register_callback(esp_spp_cb_t * callback) = 0; + virtual void setTimeout(unsigned long timeout) = 0; + + virtual int available() = 0; + virtual size_t readBytes(uint8_t *buffer, size_t bufferSize) = 0; + + virtual bool isCongested() = 0; + virtual size_t write(const uint8_t *buffer, size_t size) = 0; + virtual void flush() = 0; +}; + + +class BTClassicSerial : public virtual BTSerialInterface, public BluetoothSerial +{ + // Everything is already implemented in BluetoothSerial since the code was + // originally written using that class + public: + bool begin(String deviceName, bool isMaster, uint16_t rxQueueSize, uint16_t txQueueSize) + { + return BluetoothSerial::begin(deviceName, isMaster, rxQueueSize, txQueueSize); + } + + void disconnect() + { + BluetoothSerial::disconnect(); + } + + void end() + { + BluetoothSerial::end(); + } + + esp_err_t register_callback(esp_spp_cb_t * callback) + { + return BluetoothSerial::register_callback(callback); + } + + void setTimeout(unsigned long timeout) + { + BluetoothSerial::setTimeout(timeout); + } + + int available() + { + return BluetoothSerial::available(); + } + + size_t readBytes(uint8_t *buffer, size_t bufferSize) + { + return BluetoothSerial::readBytes(buffer, bufferSize); + } + + bool isCongested() + { + return BluetoothSerial::isCongested(); + } + + size_t write(const uint8_t *buffer, size_t size) + { + return BluetoothSerial::write(buffer, size); + } + + void flush() + { + BluetoothSerial::flush(); + } +}; + + +class BTLESerial: public virtual BTSerialInterface, public BleSerial +{ + public: + // Missing from BleSerial + bool begin(String deviceName, bool isMaster, uint16_t rxQueueSuze, uint16_t txQueueSize) + { + // Curretnly ignoring rxQueueSize + // transmitBufferLength = txQueueSize; + BleSerial::begin(deviceName.c_str()); + return true; + } + + void disconnect() + { + Server->disconnect(Server->getConnId()); + } + + void end() + { + BleSerial::end(); + } + + esp_err_t register_callback(esp_spp_cb_t * callback) + { + connectionCallback = callback; + return ESP_OK; + } + + void setTimeout(unsigned long timeout) + { + BleSerial::setTimeout(timeout); + } + + int available() + { + return BleSerial::available(); + } + + size_t readBytes(uint8_t *buffer, size_t bufferSize) + { + return BleSerial::readBytes(buffer, bufferSize); + } + + bool isCongested() + { + // not currently supported in this implementation + return false; + } + + size_t write(const uint8_t *buffer, size_t size) + { + return BleSerial::write(buffer, size); + } + + void flush() + { + BleSerial::flush(); + } + + // override BLEServerCallbacks + void onConnect(BLEServer *pServer) + { + bleConnected = true; + connectionCallback(ESP_SPP_SRV_OPEN_EVT, nullptr); + } + + void onDisconnect(BLEServer *pServer) + { + bleConnected = false; + connectionCallback(ESP_SPP_CLOSE_EVT, nullptr); + Server->startAdvertising(); + } + + private: + esp_spp_cb_t * connectionCallback; + +}; + + +#endif + diff --git a/Firmware/RTK_Surveyor/menuBluetooth.ino b/Firmware/RTK_Surveyor/menuBluetooth.ino new file mode 100644 index 000000000..e63d93187 --- /dev/null +++ b/Firmware/RTK_Surveyor/menuBluetooth.ino @@ -0,0 +1,45 @@ +void menuBluetooth() +{ + while (1) + { + Serial.println(); + Serial.println(F("Menu: Blueooth Menu")); + + Serial.println(); + Serial.print(F("Current Bluetooth Mode: ")); + if (settings.enableBLE == true) + Serial.println(F("BLE")); + else + Serial.println(F("Classic")); + + Serial.println(); + Serial.println(F("1) Set Bluetooth Mode to Classic")); + Serial.println(F("2) Set Bluetooth Mode to BLE")); + Serial.println(F("x) Exit")); + + byte incoming = getByteChoice(menuTimeout); + if (incoming == '1') + { + // Restart Bluetooth + bluetoothStop(); + settings.enableBLE = false; + bluetoothStart(); + } + else if (incoming == '2') + { + // restart Bluetooth + bluetoothStop(); + settings.enableBLE = true; + bluetoothStart(); + } + else if (incoming == 'x') + break; + else if (incoming == STATUS_GETBYTE_TIMEOUT) + break; + else + printUnknown(incoming); + } + + while (Serial.available()) Serial.read(); +} + diff --git a/Firmware/RTK_Surveyor/menuMain.ino b/Firmware/RTK_Surveyor/menuMain.ino index 480255596..50f5f5836 100644 --- a/Firmware/RTK_Surveyor/menuMain.ino +++ b/Firmware/RTK_Surveyor/menuMain.ino @@ -51,6 +51,8 @@ void menuMain() Serial.println("5) Configure Logging"); + Serial.println("6) Configure Bluetooth"); + Serial.println("p) Configure Profiles"); #ifdef COMPILE_ESPNOW @@ -81,6 +83,8 @@ void menuMain() menuPorts(); else if (incoming == '5') menuLog(); + else if (incoming == '6') + menuBluetooth(); else if (incoming == 's') menuSystem(); else if (incoming == 'p') diff --git a/Firmware/RTK_Surveyor/settings.h b/Firmware/RTK_Surveyor/settings.h index 893ca13be..cc06b8c60 100644 --- a/Firmware/RTK_Surveyor/settings.h +++ b/Firmware/RTK_Surveyor/settings.h @@ -278,6 +278,8 @@ typedef struct { bool enableExternalHardwareEventLogging = false; //Log when INT/TM2 pin goes low bool enableMarksFile = false; //Log marks to the marks file + bool enableBLE = false; + ubxMsg ubxMessages[MAX_UBX_MSG] = //Report rates for all known messages { //NMEA diff --git a/Firmware/RTK_Surveyor/src/BleSerial/BleSerial.cpp b/Firmware/RTK_Surveyor/src/BleSerial/BleSerial.cpp new file mode 100644 index 000000000..bcb5cb620 --- /dev/null +++ b/Firmware/RTK_Surveyor/src/BleSerial/BleSerial.cpp @@ -0,0 +1,184 @@ +#include "BleSerial.h" +using namespace std; + +bool BleSerial::connected() +{ + return Server->getConnectedCount() > 0; +} + +void BleSerial::onConnect(BLEServer *pServer) +{ + bleConnected = true; + if(ENABLE_LED) digitalWrite(BLUE_LED, HIGH); +} + +void BleSerial::onDisconnect(BLEServer *pServer) +{ + bleConnected = false; + if(ENABLE_LED) digitalWrite(BLUE_LED, LOW); + Server->startAdvertising(); +} + +int BleSerial::read() +{ + uint8_t result = this->receiveBuffer.pop(); + if (result == (uint8_t)'\n') + { + this->numAvailableLines--; + } + return result; +} + +size_t BleSerial::readBytes(uint8_t *buffer, size_t bufferSize) +{ + int i = 0; + while (i < bufferSize && available()) + { + buffer[i] = this->receiveBuffer.pop(); + i++; + } + return i; +} + +int BleSerial::peek() +{ + if (this->receiveBuffer.getLength() == 0) + return -1; + return this->receiveBuffer.get(0); +} + +int BleSerial::available() +{ + return this->receiveBuffer.getLength(); +} + +size_t BleSerial::print(const char *str) +{ + if (Server->getConnectedCount() <= 0) + { + return 0; + } + + size_t written = 0; + for (size_t i = 0; str[i] != '\0'; i++) + { + written += this->write(str[i]); + } + flush(); + return written; +} + +size_t BleSerial::write(const uint8_t *buffer, size_t bufferSize) +{ + if (Server->getConnectedCount() <= 0) + { + return 0; + } + size_t written = 0; + for (int i = 0; i < bufferSize; i++) + { + written += this->write(buffer[i]); + } + flush(); + return written; +} + +size_t BleSerial::write(uint8_t byte) +{ + if (Server->getConnectedCount() <= 0) + { + return 0; + } + this->transmitBuffer[this->transmitBufferLength] = byte; + this->transmitBufferLength++; + if (this->transmitBufferLength == sizeof(this->transmitBuffer)) + { + flush(); + } + return 1; +} + +void BleSerial::flush() +{ + if (this->transmitBufferLength > 0) + { + TxCharacteristic->setValue(this->transmitBuffer, this->transmitBufferLength); + this->transmitBufferLength = 0; + } + this->lastFlushTime = millis(); + TxCharacteristic->notify(true); +} + +void BleSerial::begin(const char *name) +{ + //characteristic property is what the other device does. + + ConnectedDeviceCount = 0; + BLEDevice::init(name); + //BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); + + Server = BLEDevice::createServer(); + Server->setCallbacks(this); + + SetupSerialService(); + + pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(BLE_SERIAL_SERVICE_UUID); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue + pAdvertising->setMinPreferred(0x12); + pAdvertising->start(); + + //pSecurity = new BLESecurity(); + + //Set static pin + //uint32_t passkey = 123456; + //esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t)); + //pSecurity->setCapability(ESP_IO_CAP_OUT); +} + +void BleSerial::end() +{ + BLEDevice::deinit(); +} + +void BleSerial::onWrite(BLECharacteristic *pCharacteristic) +{ + if (pCharacteristic->getUUID().toString() == BLE_RX_UUID) + { + std::string value = pCharacteristic->getValue(); + + if (value.length() > 0) + { + for (int i = 0; i < value.length(); i++) + receiveBuffer.add(value[i]); + } + } +} + +void BleSerial::SetupSerialService() +{ + SerialService = Server->createService(BLE_SERIAL_SERVICE_UUID); + + RxCharacteristic = SerialService->createCharacteristic( + BLE_RX_UUID, BLECharacteristic::PROPERTY_WRITE); + + TxCharacteristic = SerialService->createCharacteristic( + BLE_TX_UUID, BLECharacteristic::PROPERTY_NOTIFY); + + TxCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED); + RxCharacteristic->setAccessPermissions(ESP_GATT_PERM_WRITE_ENCRYPTED); + + TxCharacteristic->addDescriptor(new BLE2902()); + RxCharacteristic->addDescriptor(new BLE2902()); + + TxCharacteristic->setReadProperty(true); + RxCharacteristic->setWriteProperty(true); + RxCharacteristic->setCallbacks(this); + SerialService->start(); +} + +BleSerial::BleSerial() +{ +} + diff --git a/Firmware/RTK_Surveyor/src/BleSerial/BleSerial.h b/Firmware/RTK_Surveyor/src/BleSerial/BleSerial.h new file mode 100644 index 000000000..13d4cd3cd --- /dev/null +++ b/Firmware/RTK_Surveyor/src/BleSerial/BleSerial.h @@ -0,0 +1,76 @@ +#pragma once +#include + +#include +#include +#include +#include +#include "ByteRingBuffer.h" + +//Connection status LED +#define ENABLE_LED 0 +#define BLUE_LED 13 + +#define BLE_BUFFER_SIZE 500 //must be greater than MTU, less than ESP_GATT_MAX_ATTR_LEN + + + +class BleSerial : public BLECharacteristicCallbacks, public BLEServerCallbacks, public Stream +{ +public: + BleSerial(); + + void begin(const char *name); + void end(); + void onWrite(BLECharacteristic *pCharacteristic); + int available(); + int read(); + size_t readBytes(uint8_t *buffer, size_t bufferSize); + int peek(); + size_t write(uint8_t byte); + void flush(); + size_t write(const uint8_t *buffer, size_t bufferSize); + size_t print(const char *value); + void onConnect(BLEServer *pServer); + void onDisconnect(BLEServer *pServer); + + bool connected(); + + BLEServer *Server; + + BLEAdvertising *pAdvertising; + //BLESecurity *pSecurity; + + //Services + BLEService *SerialService; + + //Serial Characteristics + BLECharacteristic *TxCharacteristic; + BLECharacteristic *RxCharacteristic; + +protected: + size_t transmitBufferLength; + bool bleConnected; + +private: + BleSerial(BleSerial const &other) = delete; // disable copy constructor + void operator=(BleSerial const &other) = delete; // disable assign constructor + + ByteRingBuffer receiveBuffer; + size_t numAvailableLines; + + unsigned long long lastFlushTime; + uint8_t transmitBuffer[BLE_BUFFER_SIZE]; + + int ConnectedDeviceCount; + void SetupSerialService(); + + /* + Bluetooth LE GATT UUIDs for the Nordic UART profile + Change UUID here if required + */ + const char *BLE_SERIAL_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"; + const char *BLE_RX_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"; + const char *BLE_TX_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"; +}; + diff --git a/Firmware/RTK_Surveyor/src/BleSerial/ByteRingBuffer.h b/Firmware/RTK_Surveyor/src/BleSerial/ByteRingBuffer.h new file mode 100644 index 000000000..aaa81184a --- /dev/null +++ b/Firmware/RTK_Surveyor/src/BleSerial/ByteRingBuffer.h @@ -0,0 +1,45 @@ +#pragma once +#include + + +template +class ByteRingBuffer +{ +private: + uint8_t ringBuffer[N]; + size_t newestIndex = 0; + size_t length = 0; + +public: + void add(uint8_t value) + { + ringBuffer[newestIndex] = value; + newestIndex = (newestIndex + 1) % N; + length = min(length + 1, N); + } + uint8_t pop() + { // pops the oldest value off the ring buffer + if (length == 0) + { + return -1; + } + uint8_t result = ringBuffer[(N + newestIndex - length) % N]; + length -= 1; + return result; + } + void clear() + { + newestIndex = 0; + length = 0; + } + uint8_t get(size_t index) + { // this.get(0) is the oldest value, this.get(this.getLength() - 1) is the newest value + if (index < 0 || index >= length) + { + return -1; + } + return ringBuffer[(N + newestIndex - length + index) % N]; + } + size_t getLength() { return length; } +}; +