From d2926c7eae4ea6e4808e1908170916a95811579c Mon Sep 17 00:00:00 2001 From: Linar Yusupov Date: Thu, 22 Feb 2024 14:57:29 +0300 Subject: [PATCH] split Bluetooth driver over platform-specific files --- .../source/SoftRF/src/driver/Bluetooth.cpp | 1851 +---------------- .../source/SoftRF/src/driver/Bluetooth.h | 159 +- .../SoftRF/src/platform/bluetooth/BTstack.cpp | 909 ++++++++ .../SoftRF/src/platform/bluetooth/BTstack.h | 64 + .../src/platform/bluetooth/Bluedroid.cpp | 442 ++++ .../SoftRF/src/platform/bluetooth/Bluedroid.h | 50 + .../src/platform/bluetooth/Bluefruit.cpp | 557 +++++ .../SoftRF/src/platform/bluetooth/Bluefruit.h | 90 + 8 files changed, 2116 insertions(+), 2006 deletions(-) create mode 100644 software/firmware/source/SoftRF/src/platform/bluetooth/BTstack.cpp create mode 100644 software/firmware/source/SoftRF/src/platform/bluetooth/BTstack.h create mode 100644 software/firmware/source/SoftRF/src/platform/bluetooth/Bluedroid.cpp create mode 100644 software/firmware/source/SoftRF/src/platform/bluetooth/Bluedroid.h create mode 100644 software/firmware/source/SoftRF/src/platform/bluetooth/Bluefruit.cpp create mode 100644 software/firmware/source/SoftRF/src/platform/bluetooth/Bluefruit.h diff --git a/software/firmware/source/SoftRF/src/driver/Bluetooth.cpp b/software/firmware/source/SoftRF/src/driver/Bluetooth.cpp index 52aa02e2f..3d5be970f 100644 --- a/software/firmware/source/SoftRF/src/driver/Bluetooth.cpp +++ b/software/firmware/source/SoftRF/src/driver/Bluetooth.cpp @@ -16,1853 +16,4 @@ * along with this program. If not, see . */ -#if defined(ESP32) -#include "sdkconfig.h" -#endif - -#if defined(ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) - -#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) -#error Bluetooth is not enabled! -#endif - -/* - BLE code is based on Neil Kolban example for IDF: - https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp - Ported to Arduino ESP32 by Evandro Copercini - HM-10 emulation and adaptation for SoftRF is done by Linar Yusupov. -*/ -#include -#include -#include -#include - -#include "esp_gap_bt_api.h" - -#include "../system/SoC.h" -#include "EEPROM.h" -#include "Bluetooth.h" -#include "WiFi.h" -#include "Battery.h" - -#include - -BLEServer* pServer = NULL; -BLECharacteristic* pUARTCharacteristic = NULL; -BLECharacteristic* pBATCharacteristic = NULL; - -BLECharacteristic* pModelCharacteristic = NULL; -BLECharacteristic* pSerialCharacteristic = NULL; -BLECharacteristic* pFirmwareCharacteristic = NULL; -BLECharacteristic* pHardwareCharacteristic = NULL; -BLECharacteristic* pSoftwareCharacteristic = NULL; -BLECharacteristic* pManufacturerCharacteristic = NULL; - -bool deviceConnected = false; -bool oldDeviceConnected = false; - -#if defined(USE_BLE_MIDI) -BLECharacteristic* pMIDICharacteristic = NULL; -#endif /* USE_BLE_MIDI */ - -cbuf *BLE_FIFO_RX, *BLE_FIFO_TX; - -#if defined(CONFIG_IDF_TARGET_ESP32) -#include -BluetoothSerial SerialBT; -#endif /* CONFIG_IDF_TARGET_ESP32 */ - -#if defined(ENABLE_BT_VOICE) -#include "BluetoothA2DPSource.h" -#include "piano16bit.h" - -BluetoothA2DPSource a2dp_source; -SoundData *sound_data = new OneChannelSoundData((int16_t*)piano16bit_raw, piano16bit_raw_len/2); -#endif /* ENABLE_BT_VOICE */ - -String BT_name = HOSTNAME; - -static unsigned long BLE_Notify_TimeMarker = 0; -static unsigned long BLE_Advertising_TimeMarker = 0; - -BLEDescriptor UserDescriptor(BLEUUID((uint16_t)0x2901)); - -class MyServerCallbacks: public BLEServerCallbacks { - void onConnect(BLEServer* pServer) { - deviceConnected = true; - }; - - void onDisconnect(BLEServer* pServer) { - deviceConnected = false; - BLE_Advertising_TimeMarker = millis(); - } -}; - -class UARTCallbacks: public BLECharacteristicCallbacks { - void onWrite(BLECharacteristic *pUARTCharacteristic) { -#if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR>=5 - String rxValue = pUARTCharacteristic->getValue(); -#else - std::string rxValue = pUARTCharacteristic->getValue(); -#endif /* ESP_IDF_VERSION_MAJOR */ - - if (rxValue.length() > 0) { - BLE_FIFO_RX->write(rxValue.c_str(), - (BLE_FIFO_RX->room() > rxValue.length() ? - rxValue.length() : BLE_FIFO_RX->room())); - } - } -}; - -static void ESP32_Bluetooth_setup() -{ - BT_name += "-"; - BT_name += String(SoC->getChipId() & 0x00FFFFFFU, HEX); - - switch (settings->bluetooth) - { -#if defined(CONFIG_IDF_TARGET_ESP32) - case BLUETOOTH_SPP: - { - esp_bt_controller_mem_release(ESP_BT_MODE_BLE); - - SerialBT.begin(BT_name.c_str()); - } - break; -#endif /* CONFIG_IDF_TARGET_ESP32 */ - case BLUETOOTH_LE_HM10_SERIAL: - { - BLE_FIFO_RX = new cbuf(BLE_FIFO_RX_SIZE); - BLE_FIFO_TX = new cbuf(BLE_FIFO_TX_SIZE); - -#if defined(CONFIG_IDF_TARGET_ESP32) - esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); -#endif /* CONFIG_IDF_TARGET_ESP32 */ - - // Create the BLE Device - BLEDevice::init((BT_name+"-LE").c_str()); - - /* - * Set the MTU of the packets sent, - * maximum is 500, Apple needs 23 apparently. - */ - // BLEDevice::setMTU(23); - - // Create the BLE Server - pServer = BLEDevice::createServer(); - pServer->setCallbacks(new MyServerCallbacks()); - - // Create the BLE Service - BLEService *pService = pServer->createService(BLEUUID(UART_SERVICE_UUID16)); - - // Create a BLE Characteristic - pUARTCharacteristic = pService->createCharacteristic( - BLEUUID(UART_CHARACTERISTIC_UUID16), - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_NOTIFY | - BLECharacteristic::PROPERTY_WRITE_NR - ); - - UserDescriptor.setValue("HMSoft"); - pUARTCharacteristic->addDescriptor(&UserDescriptor); - pUARTCharacteristic->addDescriptor(new BLE2902()); - - pUARTCharacteristic->setCallbacks(new UARTCallbacks()); - - // Start the service - pService->start(); - - // Create the BLE Service - pService = pServer->createService(BLEUUID(UUID16_SVC_BATTERY)); - - // Create a BLE Characteristic - pBATCharacteristic = pService->createCharacteristic( - BLEUUID(UUID16_CHR_BATTERY_LEVEL), - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_NOTIFY - ); - pBATCharacteristic->addDescriptor(new BLE2902()); - - // Start the service - pService->start(); - - // Create the BLE Service - pService = pServer->createService(BLEUUID(UUID16_SVC_DEVICE_INFORMATION)); - - // Create BLE Characteristics - pModelCharacteristic = pService->createCharacteristic( - BLEUUID(UUID16_CHR_MODEL_NUMBER_STRING), - BLECharacteristic::PROPERTY_READ - ); - pSerialCharacteristic = pService->createCharacteristic( - BLEUUID(UUID16_CHR_SERIAL_NUMBER_STRING), - BLECharacteristic::PROPERTY_READ - ); - pFirmwareCharacteristic = pService->createCharacteristic( - BLEUUID(UUID16_CHR_FIRMWARE_REVISION_STRING), - BLECharacteristic::PROPERTY_READ - ); - pHardwareCharacteristic = pService->createCharacteristic( - BLEUUID(UUID16_CHR_HARDWARE_REVISION_STRING), - BLECharacteristic::PROPERTY_READ - ); - pSoftwareCharacteristic = pService->createCharacteristic( - BLEUUID(UUID16_CHR_SOFTWARE_REVISION_STRING), - BLECharacteristic::PROPERTY_READ - ); - pManufacturerCharacteristic = pService->createCharacteristic( - BLEUUID(UUID16_CHR_MANUFACTURER_NAME_STRING), - BLECharacteristic::PROPERTY_READ - ); - - const char *Model = hw_info.model == SOFTRF_MODEL_STANDALONE ? "Standalone Edition" : - hw_info.model == SOFTRF_MODEL_PRIME_MK2 ? "Prime Mark II" : - hw_info.model == SOFTRF_MODEL_PRIME_MK3 ? "Prime Mark III" : - hw_info.model == SOFTRF_MODEL_HAM ? "Ham Edition" : - hw_info.model == SOFTRF_MODEL_MIDI ? "Midi Edition" : - "Unknown"; - char SerialNum[9]; - snprintf(SerialNum, sizeof(SerialNum), "%08X", SoC->getChipId()); - - const char *Firmware = "Arduino ESP32 " ARDUINO_ESP32_RELEASE; - - char Hardware[9]; - snprintf(Hardware, sizeof(Hardware), "%08X", hw_info.revision); - - const char *Manufacturer = SOFTRF_IDENT; - const char *Software = SOFTRF_FIRMWARE_VERSION; - - pModelCharacteristic-> setValue((uint8_t *) Model, strlen(Model)); - pSerialCharacteristic-> setValue((uint8_t *) SerialNum, strlen(SerialNum)); - pFirmwareCharacteristic-> setValue((uint8_t *) Firmware, strlen(Firmware)); - pHardwareCharacteristic-> setValue((uint8_t *) Hardware, strlen(Hardware)); - pSoftwareCharacteristic-> setValue((uint8_t *) Software, strlen(Software)); - pManufacturerCharacteristic->setValue((uint8_t *) Manufacturer, strlen(Manufacturer)); - - // Start the service - pService->start(); - -#if defined(USE_BLE_MIDI) - // Create the BLE Service - pService = pServer->createService(BLEUUID(MIDI_SERVICE_UUID)); - - // Create a BLE Characteristic - pMIDICharacteristic = pService->createCharacteristic( - BLEUUID(MIDI_CHARACTERISTIC_UUID), - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_WRITE | - BLECharacteristic::PROPERTY_NOTIFY | - BLECharacteristic::PROPERTY_WRITE_NR - ); - - // Create a BLE Descriptor - pMIDICharacteristic->addDescriptor(new BLE2902()); - - // Start the service - pService->start(); -#endif /* USE_BLE_MIDI */ - - // Start advertising - BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); -#if 0 - pAdvertising->addServiceUUID(BLEUUID(UART_SERVICE_UUID16)); - pAdvertising->addServiceUUID(BLEUUID(UUID16_SVC_BATTERY)); -#if defined(USE_BLE_MIDI) - pAdvertising->addServiceUUID(BLEUUID(MIDI_SERVICE_UUID)); -#endif /* USE_BLE_MIDI */ -#else - /* work around https://github.com/espressif/arduino-esp32/issues/6750 */ - BLEAdvertisementData BLEAdvData; - BLEAdvData.setFlags(0x06); - BLEAdvData.setCompleteServices(BLEUUID(UART_SERVICE_UUID16)); - BLEAdvData.setCompleteServices(BLEUUID(UUID16_SVC_BATTERY)); -#if defined(USE_BLE_MIDI) - BLEAdvData.setCompleteServices(BLEUUID(MIDI_SERVICE_UUID)); -#endif /* USE_BLE_MIDI */ - pAdvertising->setAdvertisementData(BLEAdvData); -#endif - pAdvertising->setScanResponse(true); - pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue - pAdvertising->setMaxPreferred(0x12); - BLEDevice::startAdvertising(); - - BLE_Advertising_TimeMarker = millis(); - } - break; - case BLUETOOTH_A2DP_SOURCE: -#if defined(ENABLE_BT_VOICE) - //a2dp_source.set_auto_reconnect(false); - a2dp_source.start("BT SPEAKER"); - a2dp_source.set_volume(100); - a2dp_source.write_data(sound_data); -#endif - break; - case BLUETOOTH_NONE: - default: - break; - } -} - -static void ESP32_Bluetooth_loop() -{ - switch (settings->bluetooth) - { - case BLUETOOTH_LE_HM10_SERIAL: - { - // notify changed value - // bluetooth stack will go into congestion, if too many packets are sent - if (deviceConnected && (millis() - BLE_Notify_TimeMarker > 10)) { /* < 18000 baud */ - - uint8_t chunk[BLE_MAX_WRITE_CHUNK_SIZE]; - size_t size = BLE_FIFO_TX->available(); - size = size < BLE_MAX_WRITE_CHUNK_SIZE ? size : BLE_MAX_WRITE_CHUNK_SIZE; - - if (size > 0) { - BLE_FIFO_TX->read((char *) chunk, size); - - pUARTCharacteristic->setValue(chunk, size); - pUARTCharacteristic->notify(); - } - - BLE_Notify_TimeMarker = millis(); - } - // disconnecting - if (!deviceConnected && oldDeviceConnected && (millis() - BLE_Advertising_TimeMarker > 500) ) { - // give the bluetooth stack the chance to get things ready - pServer->startAdvertising(); // restart advertising - oldDeviceConnected = deviceConnected; - BLE_Advertising_TimeMarker = millis(); - } - // connecting - if (deviceConnected && !oldDeviceConnected) { - // do stuff here on connecting - oldDeviceConnected = deviceConnected; - } - if (deviceConnected && isTimeToBattery()) { - uint8_t battery_level = Battery_charge(); - - pBATCharacteristic->setValue(&battery_level, 1); - pBATCharacteristic->notify(); - } - } - break; - case BLUETOOTH_NONE: - case BLUETOOTH_SPP: - case BLUETOOTH_A2DP_SOURCE: - default: - break; - } -} - -static void ESP32_Bluetooth_fini() -{ - /* TBD */ -} - -static int ESP32_Bluetooth_available() -{ - int rval = 0; - - switch (settings->bluetooth) - { -#if defined(CONFIG_IDF_TARGET_ESP32) - case BLUETOOTH_SPP: - rval = SerialBT.available(); - break; -#endif /* CONFIG_IDF_TARGET_ESP32 */ - case BLUETOOTH_LE_HM10_SERIAL: - rval = BLE_FIFO_RX->available(); - break; - case BLUETOOTH_NONE: - case BLUETOOTH_A2DP_SOURCE: - default: - break; - } - - return rval; -} - -static int ESP32_Bluetooth_read() -{ - int rval = -1; - - switch (settings->bluetooth) - { -#if defined(CONFIG_IDF_TARGET_ESP32) - case BLUETOOTH_SPP: - rval = SerialBT.read(); - break; -#endif /* CONFIG_IDF_TARGET_ESP32 */ - case BLUETOOTH_LE_HM10_SERIAL: - rval = BLE_FIFO_RX->read(); - break; - case BLUETOOTH_NONE: - case BLUETOOTH_A2DP_SOURCE: - default: - break; - } - - return rval; -} - -static size_t ESP32_Bluetooth_write(const uint8_t *buffer, size_t size) -{ - size_t rval = size; - - switch (settings->bluetooth) - { -#if defined(CONFIG_IDF_TARGET_ESP32) - case BLUETOOTH_SPP: - rval = SerialBT.write(buffer, size); - break; -#endif /* CONFIG_IDF_TARGET_ESP32 */ - case BLUETOOTH_LE_HM10_SERIAL: - rval = BLE_FIFO_TX->write((char *) buffer, - (BLE_FIFO_TX->room() > size ? size : BLE_FIFO_TX->room())); - break; - case BLUETOOTH_NONE: - case BLUETOOTH_A2DP_SOURCE: - default: - break; - } - - return rval; -} - -IODev_ops_t ESP32_Bluetooth_ops = { - "ESP32 Bluetooth", - ESP32_Bluetooth_setup, - ESP32_Bluetooth_loop, - ESP32_Bluetooth_fini, - ESP32_Bluetooth_available, - ESP32_Bluetooth_read, - ESP32_Bluetooth_write -}; - -#elif defined(ARDUINO_ARCH_NRF52) - -#include "../system/SoC.h" -#include "Bluetooth.h" - -#include -#include -#include -#include -#include -#if defined(USE_BLE_MIDI) -#include -#endif /* USE_BLE_MIDI */ - -#include "WiFi.h" -#include "Battery.h" -#include "GNSS.h" -#include "RF.h" -#include "../protocol/radio/Legacy.h" -#include "Baro.h" -#include "EEPROM.h" -#include "Sound.h" -#include "../protocol/data/NMEA.h" -#include "../protocol/data/GDL90.h" -#include "../protocol/data/D1090.h" - -/* - * SensorBox Serivce: aba27100-143b-4b81-a444-edcd0000f020 - * Navigation : aba27100-143b-4b81-a444-edcd0000f022 - * Movement : aba27100-143b-4b81-a444-edcd0000f023 - * GPS2 : aba27100-143b-4b81-a444-edcd0000f024 - * System : aba27100-143b-4b81-a444-edcd0000f025 - */ - -const uint8_t SENSBOX_UUID_SERVICE[] = -{ - 0x20, 0xF0, 0x00, 0x00, 0xCD, 0xED, 0x44, 0xA4, - 0x81, 0x4B, 0x3B, 0x14, 0x00, 0x71, 0xA2, 0xAB, -}; - -const uint8_t SENSBOX_UUID_NAVIGATION[] = -{ - 0x22, 0xF0, 0x00, 0x00, 0xCD, 0xED, 0x44, 0xA4, - 0x81, 0x4B, 0x3B, 0x14, 0x00, 0x71, 0xA2, 0xAB, -}; - -const uint8_t SENSBOX_UUID_MOVEMENT[] = -{ - 0x23, 0xF0, 0x00, 0x00, 0xCD, 0xED, 0x44, 0xA4, - 0x81, 0x4B, 0x3B, 0x14, 0x00, 0x71, 0xA2, 0xAB, -}; - -const uint8_t SENSBOX_UUID_GPS2[] = -{ - 0x24, 0xF0, 0x00, 0x00, 0xCD, 0xED, 0x44, 0xA4, - 0x81, 0x4B, 0x3B, 0x14, 0x00, 0x71, 0xA2, 0xAB, -}; - -const uint8_t SENSBOX_UUID_SYSTEM[] = -{ - 0x25, 0xF0, 0x00, 0x00, 0xCD, 0xED, 0x44, 0xA4, - 0x81, 0x4B, 0x3B, 0x14, 0x00, 0x71, 0xA2, 0xAB, -}; - -BLESensBox::BLESensBox(void) : - BLEService (SENSBOX_UUID_SERVICE), - _sensbox_nav (SENSBOX_UUID_NAVIGATION), - _sensbox_move(SENSBOX_UUID_MOVEMENT), - _sensbox_gps2(SENSBOX_UUID_GPS2), - _sensbox_sys (SENSBOX_UUID_SYSTEM) -{ - -} - -err_t BLESensBox::begin(void) -{ - VERIFY_STATUS( BLEService::begin() ); - - _sensbox_nav.setProperties(CHR_PROPS_NOTIFY); - _sensbox_nav.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); - _sensbox_nav.setFixedLen(sizeof(sensbox_navigation_t)); - _sensbox_move.setProperties(CHR_PROPS_NOTIFY); - _sensbox_move.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); - _sensbox_move.setFixedLen(sizeof(sensbox_movement_t)); - _sensbox_gps2.setProperties(CHR_PROPS_NOTIFY); - _sensbox_gps2.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); - _sensbox_gps2.setFixedLen(sizeof(sensbox_gps2_t)); - _sensbox_sys.setProperties(CHR_PROPS_NOTIFY); - _sensbox_sys.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); - _sensbox_sys.setFixedLen(sizeof(sensbox_system_t)); - VERIFY_STATUS( _sensbox_nav.begin() ); - VERIFY_STATUS( _sensbox_move.begin() ); - VERIFY_STATUS( _sensbox_gps2.begin() ); - VERIFY_STATUS( _sensbox_sys.begin() ); - - return ERROR_NONE; -} - -bool BLESensBox::notify_nav(uint8_t status) -{ - if (!_sensbox_nav.notifyEnabled(Bluefruit.connHandle())) - return false; - - sensbox_navigation_t data = {0}; - - data.timestamp = ThisAircraft.timestamp; - data.lat = (int32_t) (ThisAircraft.latitude * 10000000); - data.lon = (int32_t) (ThisAircraft.longitude * 10000000); - data.gnss_alt = (int16_t) ThisAircraft.altitude; - data.pres_alt = (int16_t) ThisAircraft.pressure_altitude; - data.status = status; - - return _sensbox_nav.notify(&data, sizeof(sensbox_navigation_t)) > 0; -} - -bool BLESensBox::notify_move(uint8_t status) -{ - if (!_sensbox_move.notifyEnabled(Bluefruit.connHandle())) - return false; - - sensbox_movement_t data = {0}; - - data.pres_alt = (int32_t) (ThisAircraft.pressure_altitude * 100); - data.vario = (int16_t) ((ThisAircraft.vs * 10) / (_GPS_FEET_PER_METER * 6)); - data.gs = (int16_t) (ThisAircraft.speed * _GPS_MPS_PER_KNOT * 10); - data.cog = (int16_t) (ThisAircraft.course * 10); - data.status = status; - - return _sensbox_move.notify(&data, sizeof(sensbox_movement_t)) > 0; -} - -bool BLESensBox::notify_gps2(uint8_t status) -{ - if (!_sensbox_gps2.notifyEnabled(Bluefruit.connHandle())) - return false; - - sensbox_gps2_t data = {0}; - - data.sats = (uint8_t) gnss.satellites.value(); - data.status = status; - - return _sensbox_gps2.notify(&data, sizeof(sensbox_gps2_t)) > 0; -} - -bool BLESensBox::notify_sys(uint8_t status) -{ - if (!_sensbox_sys.notifyEnabled(Bluefruit.connHandle())) - return false; - - sensbox_system_t data = {0}; - - data.battery = (uint8_t) Battery_charge(); - data.temp = (int16_t) (Baro_temperature() * 10); - data.status = status; - - return _sensbox_sys.notify(&data, sizeof(sensbox_system_t)) > 0; -} - -static unsigned long BLE_Notify_TimeMarker = 0; -static unsigned long BLE_SensBox_TimeMarker = 0; - -/********************************************************************* - This is an example for our nRF52 based Bluefruit LE modules - - Pick one up today in the adafruit shop! - - Adafruit invests time and resources providing this open source code, - please support Adafruit and open-source hardware by purchasing - products from Adafruit! - - MIT license, check LICENSE for more information - All text above, and the splash screen below must be included in - any redistribution -*********************************************************************/ - -// BLE Service -BLEDfu bledfu; // OTA DFU service -BLEDis bledis; // device information -BLEUart_HM10 bleuart_HM10; // TI UART over BLE -#if !defined(EXCLUDE_NUS) -BLEUart bleuart_NUS; // Nordic UART over BLE -#endif /* EXCLUDE_NUS */ -BLEBas blebas; // battery -BLESensBox blesens; // SensBox - -#if defined(USE_BLE_MIDI) -BLEMidi blemidi; - -MIDI_CREATE_INSTANCE(BLEMidi, blemidi, MIDI_BLE); -#endif /* USE_BLE_MIDI */ - -#if defined(ENABLE_REMOTE_ID) -#include "../protocol/radio/RemoteID.h" - -#define UUID16_COMPANY_ID_ASTM 0xFFFA - -static unsigned long RID_Time_Marker = 0; - -BLEService BLE_ODID_service; - -static const uint8_t ODID_Uuid[] = {0x00, 0x00, 0xff, 0xfa, 0x00, 0x00, 0x10, 0x00, - 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}; -#endif /* ENABLE_REMOTE_ID */ - -String BT_name = HOSTNAME; - -#define UUID16_COMPANY_ID_NORDIC 0x0059 - -static uint8_t BeaconUuid[16] = -{ - /* https://openuuid.net: becf4a85-29b8-476e-928f-fce11f303344 */ - 0xbe, 0xcf, 0x4a, 0x85, 0x29, 0xb8, 0x47, 0x6e, - 0x92, 0x8f, 0xfc, 0xe1, 0x1f, 0x30, 0x33, 0x44 -}; - -// UUID, Major, Minor, RSSI @ 1M -BLEBeacon iBeacon(BeaconUuid, 0x0102, 0x0304, -64); - -void startAdv(void) -{ - bool no_data = (settings->nmea_out != NMEA_BLUETOOTH && - settings->gdl90 != GDL90_BLUETOOTH && - settings->d1090 != D1090_BLUETOOTH); - - // Advertising packet - -#if defined(USE_IBEACON) - if (no_data && settings->volume == BUZZER_OFF) { - uint32_t id = SoC->getChipId(); - uint16_t major = (id >> 16) & 0x0000FFFF; - uint16_t minor = (id ) & 0x0000FFFF; - - // Manufacturer ID is required for Manufacturer Specific Data - iBeacon.setManufacturer(UUID16_COMPANY_ID_NORDIC); - iBeacon.setMajorMinor(major, minor); - - // Set the beacon payload using the BLEBeacon class - Bluefruit.Advertising.setBeacon(iBeacon); - } else -#endif /* USE_IBEACON */ - { - Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); - Bluefruit.Advertising.addTxPower(); - -#if defined(ENABLE_REMOTE_ID) - if (rid_enabled()) { - Bluefruit.Advertising.addService(BLE_ODID_service); - Bluefruit.Advertising.addName(); - } else -#endif /* ENABLE_REMOTE_ID */ -#if defined(USE_BLE_MIDI) - if (settings->volume != BUZZER_OFF) { - Bluefruit.Advertising.addService(blemidi, bleuart_HM10); - } else -#endif /* USE_BLE_MIDI */ - { - Bluefruit.Advertising.addService( -#if !defined(EXCLUDE_NUS) - bleuart_NUS, -#endif /* EXCLUDE_NUS */ - bleuart_HM10); - } - } - - // Secondary Scan Response packet (optional) - // Since there is no room for 'Name' in Advertising packet -#if defined(ENABLE_REMOTE_ID) - if (!rid_enabled()) -#endif /* ENABLE_REMOTE_ID */ - { - Bluefruit.ScanResponse.addName(); - } - - /* Start Advertising - * - Enable auto advertising if disconnected - * - Interval: fast mode = 20 ms, slow mode = 152.5 ms - * - Timeout for fast mode is 30 seconds - * - Start(timeout) with timeout = 0 will advertise forever (until connected) - * - * For recommended advertising interval - * https://developer.apple.com/library/content/qa/qa1931/_index.html - */ - Bluefruit.Advertising.restartOnDisconnect(true); - Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms - Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode - Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds -} - -// callback invoked when central connects -void connect_callback(uint16_t conn_handle) -{ -#if DEBUG_BLE - // Get the reference to current connection - BLEConnection* connection = Bluefruit.Connection(conn_handle); - - char central_name[32] = { 0 }; - connection->getPeerName(central_name, sizeof(central_name)); - - Serial.print("Connected to "); - Serial.println(central_name); -#endif -} - -/** - * Callback invoked when a connection is dropped - * @param conn_handle connection where this event happens - * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h - */ -void disconnect_callback(uint16_t conn_handle, uint8_t reason) -{ -#if DEBUG_BLE - (void) conn_handle; - (void) reason; - - Serial.println(); - Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX); -#endif -} - -void nRF52_Bluetooth_setup() -{ - BT_name += "-"; - BT_name += String(SoC->getChipId() & 0x00FFFFFFU, HEX); - -#if defined(ENABLE_REMOTE_ID) - rid_init(); - RID_Time_Marker = millis(); -#endif /* ENABLE_REMOTE_ID */ - - // Setup the BLE LED to be enabled on CONNECT - // Note: This is actually the default behavior, but provided - // here in case you want to control this LED manually via PIN 19 - Bluefruit.autoConnLed(LED_BLUE == SOC_GPIO_LED_BLE ? true : false); - - // Config the peripheral connection with maximum bandwidth - // more SRAM required by SoftDevice - // Note: All config***() function must be called before begin() - Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); - - Bluefruit.begin(); - Bluefruit.setTxPower(4); // Check bluefruit.h for supported values - Bluefruit.setName((BT_name+"-LE").c_str()); - Bluefruit.Periph.setConnectCallback(connect_callback); - Bluefruit.Periph.setDisconnectCallback(disconnect_callback); - - // To be consistent OTA DFU should be added first if it exists - bledfu.begin(); - - // Configure and Start Device Information Service - bledis.setManufacturer(nRF52_Device_Manufacturer); - bledis.setModel(nRF52_Device_Model); - bledis.setHardwareRev(hw_info.revision > 2 ? - Hardware_Rev[3] : Hardware_Rev[hw_info.revision]); - bledis.setSoftwareRev(SOFTRF_FIRMWARE_VERSION); - bledis.begin(); - - // Configure and Start BLE Uart Service - bleuart_HM10.begin(); -#if !defined(EXCLUDE_NUS) - bleuart_NUS.begin(); - bleuart_NUS.bufferTXD(true); -#endif /* EXCLUDE_NUS */ - - // Start BLE Battery Service - blebas.begin(); - blebas.write(100); - -#if defined(ENABLE_REMOTE_ID) - if (rid_enabled()) { - BLE_ODID_service.setUuid(BLEUuid(ODID_Uuid /* UUID16_COMPANY_ID_ASTM */)); - BLE_ODID_service.begin(); - } -#endif /* ENABLE_REMOTE_ID */ - - // Start SensBox Service - blesens.begin(); - -#if defined(USE_BLE_MIDI) - // Initialize MIDI with no any input channels - // This will also call blemidi service's begin() - MIDI_BLE.begin(MIDI_CHANNEL_OFF); -#endif /* USE_BLE_MIDI */ - - // Set up and start advertising - startAdv(); - -#if DEBUG_BLE - Serial.println("Please use Adafruit's Bluefruit LE app to connect in UART mode"); - Serial.println("Once connected, enter character(s) that you wish to send"); -#endif - - BLE_Notify_TimeMarker = millis(); - BLE_SensBox_TimeMarker = millis(); -} - -/********************************************************************* - End of Adafruit licensed text -*********************************************************************/ - -static void nRF52_Bluetooth_loop() -{ - // notify changed value - // bluetooth stack will go into congestion, if too many packets are sent - if ( Bluefruit.connected() && - bleuart_HM10.notifyEnabled() && - (millis() - BLE_Notify_TimeMarker > 10)) { /* < 18000 baud */ - bleuart_HM10.flushTXD(); - - BLE_Notify_TimeMarker = millis(); - } - - if (isTimeToBattery()) { - blebas.write(Battery_charge()); - } - - if (Bluefruit.connected() && isTimeToSensBox()) { - uint8_t sens_status = isValidFix() ? GNSS_STATUS_3D_MOVING : GNSS_STATUS_NONE; - blesens.notify_nav (sens_status); - blesens.notify_move(sens_status); - blesens.notify_gps2(sens_status); - blesens.notify_sys (sens_status); - BLE_SensBox_TimeMarker = millis(); - } - -#if defined(ENABLE_REMOTE_ID) - if (rid_enabled() && isValidFix()) { - if ((millis() - RID_Time_Marker) > 74) { - rid_encode((void *) &utm_data, &ThisAircraft); - squitter.transmit(&utm_data); - - RID_Time_Marker = millis(); - } - } -#endif /* ENABLE_REMOTE_ID */ -} - -static void nRF52_Bluetooth_fini() -{ - uint8_t sd_en = 0; - (void) sd_softdevice_is_enabled(&sd_en); - - if ( Bluefruit.connected() ) { - if ( bleuart_HM10.notifyEnabled() ) { - // flush TXD since we use bufferTXD() - bleuart_HM10.flushTXD(); - } - -#if !defined(EXCLUDE_NUS) - if ( bleuart_NUS.notifyEnabled() ) { - // flush TXD since we use bufferTXD() - bleuart_NUS.flushTXD(); - } -#endif /* EXCLUDE_NUS */ - } - - if (Bluefruit.Advertising.isRunning()) { - Bluefruit.Advertising.stop(); - } - - if (sd_en) sd_softdevice_disable(); -} - -static int nRF52_Bluetooth_available() -{ - int rval = 0; - - if ( !Bluefruit.connected() ) { - return rval; - } - - /* Give priority to HM-10 input */ - if ( bleuart_HM10.notifyEnabled() ) { - return bleuart_HM10.available(); - } - -#if !defined(EXCLUDE_NUS) - if ( bleuart_NUS.notifyEnabled() ) { - rval = bleuart_NUS.available(); - } -#endif /* EXCLUDE_NUS */ - - return rval; -} - -static int nRF52_Bluetooth_read() -{ - int rval = -1; - - if ( !Bluefruit.connected() ) { - return rval; - } - - /* Give priority to HM-10 input */ - if ( bleuart_HM10.notifyEnabled() ) { - return bleuart_HM10.read(); - } - -#if !defined(EXCLUDE_NUS) - if ( bleuart_NUS.notifyEnabled() ) { - rval = bleuart_NUS.read(); - } -#endif /* EXCLUDE_NUS */ - - return rval; -} - -static size_t nRF52_Bluetooth_write(const uint8_t *buffer, size_t size) -{ - size_t rval = size; - - if ( !Bluefruit.connected() ) { - return rval; - } - - /* Give priority to HM-10 output */ - if ( bleuart_HM10.notifyEnabled() && size > 0) { - return bleuart_HM10.write(buffer, size); - } - -#if !defined(EXCLUDE_NUS) - if ( bleuart_NUS.notifyEnabled() && size > 0) { - rval = bleuart_NUS.write(buffer, size); - } -#endif /* EXCLUDE_NUS */ - - return rval; -} - -IODev_ops_t nRF52_Bluetooth_ops = { - "nRF52 Bluetooth", - nRF52_Bluetooth_setup, - nRF52_Bluetooth_loop, - nRF52_Bluetooth_fini, - nRF52_Bluetooth_available, - nRF52_Bluetooth_read, - nRF52_Bluetooth_write -}; - -#elif defined(ARDUINO_ARCH_RP2040) -#include "../system/SoC.h" -#if !defined(EXCLUDE_BLUETOOTH) - -#include -#include -#include -#include - -#include - -#include "EEPROM.h" -#include "WiFi.h" -#include "Bluetooth.h" -#include "Battery.h" - -#if defined(ENABLE_BT_VOICE) -#include "AudioTools.h" -#include "BTstack_A2DP.h" - -SineWaveGenerator sineWave(32000); -GeneratedSoundStream in(sineWave); -#endif /* ENABLE_BT_VOICE */ - -static bool _running = false; -static mutex_t _mutex; -static bool _overflow = false; -static volatile bool _connected = false; - -static uint32_t _writer; -static uint32_t _reader; -static size_t _fifoSize = 32; -static uint8_t *_queue = NULL; - -static const int RFCOMM_SERVER_CHANNEL = 1; - -static uint16_t _channelID; -static uint8_t _spp_service_buffer[150]; -static btstack_packet_callback_registration_t _hci_event_callback_registration; - -static volatile int _writeLen = 0; -static const void *_writeBuff; - -RingBufferN BLE_FIFO_TX = RingBufferN(); -RingBufferN BLE_FIFO_RX = RingBufferN(); - -String BT_name = HOSTNAME; - -/* ------- SPP BEGIN ------ */ - -static void hci_spp_packet_handler(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size) { - UNUSED(channel); - bd_addr_t event_addr; - //uint8_t rfcomm_channel_nr; - //uint16_t mtu; - int i; - - switch (type) { - case HCI_EVENT_PACKET: - switch (hci_event_packet_get_type(packet)) { - case HCI_EVENT_PIN_CODE_REQUEST: - //Serial.printf("Pin code request - using '0000'\n"); - hci_event_pin_code_request_get_bd_addr(packet, event_addr); - gap_pin_code_response(event_addr, "0000"); - break; - - case HCI_EVENT_USER_CONFIRMATION_REQUEST: - // ssp: inform about user confirmation request - //Serial.printf("SSP User Confirmation Request with numeric value '%06" PRIu32 "'\n", little_endian_read_32(packet, 8)); - //Serial.printf("SSP User Confirmation Auto accept\n"); - break; - - case RFCOMM_EVENT_INCOMING_CONNECTION: - rfcomm_event_incoming_connection_get_bd_addr(packet, event_addr); - //rfcomm_channel_nr = rfcomm_event_incoming_connection_get_server_channel(packet); - _channelID = rfcomm_event_incoming_connection_get_rfcomm_cid(packet); - //Serial.printf("RFCOMM channel %u requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr)); - rfcomm_accept_connection(_channelID); - break; - - case RFCOMM_EVENT_CHANNEL_OPENED: - if (rfcomm_event_channel_opened_get_status(packet)) { - //Serial.printf("RFCOMM channel open failed, status 0x%02x\n", rfcomm_event_channel_opened_get_status(packet)); - } else { - _channelID = rfcomm_event_channel_opened_get_rfcomm_cid(packet); - //mtu = rfcomm_event_channel_opened_get_max_frame_size(packet); - //Serial.printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_channel_id, mtu); - _connected = true; - } - break; - case RFCOMM_EVENT_CAN_SEND_NOW: - rfcomm_send(_channelID, (uint8_t *)_writeBuff, _writeLen); - _writeLen = 0; - break; - case RFCOMM_EVENT_CHANNEL_CLOSED: - //Serial.printf("RFCOMM channel closed\n"); - _channelID = 0; - _connected = false; - break; - - default: - break; - } - break; - - case RFCOMM_DATA_PACKET: - for (i = 0; i < size; i++) { - auto next_writer = _writer + 1; - if (next_writer == _fifoSize) { - next_writer = 0; - } - if (next_writer != _reader) { - _queue[_writer] = packet[i]; - asm volatile("" ::: "memory"); // Ensure the queue is written before the written count advances - // Avoid using division or mod because the HW divider could be in use - _writer = next_writer; - } else { - _overflow = true; - } - } - break; - - default: - break; - } -} - -/* ------- SPP END ------ */ - -/* ------- BLE BEGIN------ */ - -#define REPORT_INTERVAL_MS 3000 -#define MAX_NR_CONNECTIONS 3 -#define APP_AD_FLAGS 0x06 - -#define DEBUG_BLE 0 - -uint8_t *_advData = nullptr; -uint8_t _advDataLen = 0; -uint8_t *_attdb = nullptr; -size_t _attdbLen = 0; - -void _buildAdvData(const char *completeLocalName) { - free(_advData); - _advDataLen = 9 + strlen(completeLocalName); - _advData = (uint8_t*) malloc(_advDataLen); - int i = 0; - // Flags general discoverable, BR/EDR not supported - // 0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06, - _advData[i++] = 0x02; - _advData[i++] = BLUETOOTH_DATA_TYPE_FLAGS; - _advData[i++] = 0x06; - // Name - // 0x0d, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, - _advData[i++] = 1 + strlen(completeLocalName); - _advData[i++] = BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME; - memcpy(_advData + i, completeLocalName, strlen(completeLocalName)); - i += strlen(completeLocalName); - // 16-bit Service UUIDs - _advData[i++] = 0x03; - _advData[i++] = BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS; - _advData[i++] = 0xe0; - _advData[i++] = 0xff; -} - -static constexpr const uint8_t _attdb_head[] = { - // ATT DB Version - 1, - - // 0x0001 PRIMARY_SERVICE-GAP_SERVICE - 0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x18, - // 0x0002 CHARACTERISTIC-GAP_DEVICE_NAME - READ - 0x0d, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x28, 0x02, 0x03, 0x00, 0x00, 0x2a, -}; - -static constexpr const uint8_t _attdb_tail[] = { - // Specification Type org.bluetooth.service.battery_service - // https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.battery_service.xml - // Battery Service 180F - // 0x0004 PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE - 0x0a, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x28, 0x0f, 0x18, - // 0x0005 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL - DYNAMIC | READ | NOTIFY - 0x0d, 0x00, 0x02, 0x00, 0x05, 0x00, 0x03, 0x28, 0x12, 0x06, 0x00, 0x19, 0x2a, - // 0x0006 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL - DYNAMIC | READ | NOTIFY - // READ_ANYBODY - 0x08, 0x00, 0x02, 0x01, 0x06, 0x00, 0x19, 0x2a, - // 0x0007 CLIENT_CHARACTERISTIC_CONFIGURATION - // READ_ANYBODY, WRITE_ANYBODY - 0x0a, 0x00, 0x0e, 0x01, 0x07, 0x00, 0x02, 0x29, 0x00, 0x00, - - // Specification Type org.bluetooth.service.device_information - // https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.device_information.xml - // Device Information 180A - // 0x0008 PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION - 0x0a, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x28, 0x0a, 0x18, - // 0x0009 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING - DYNAMIC | READ - 0x0d, 0x00, 0x02, 0x00, 0x09, 0x00, 0x03, 0x28, 0x02, 0x0a, 0x00, 0x29, 0x2a, - // 0x000a VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING - DYNAMIC | READ - // READ_ANYBODY - 0x08, 0x00, 0x02, 0x01, 0x0a, 0x00, 0x29, 0x2a, - // 0x000b CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING - DYNAMIC | READ - 0x0d, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x03, 0x28, 0x02, 0x0c, 0x00, 0x24, 0x2a, - // 0x000c VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING - DYNAMIC | READ - // READ_ANYBODY - 0x08, 0x00, 0x02, 0x01, 0x0c, 0x00, 0x24, 0x2a, - // 0x000d CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING - DYNAMIC | READ - 0x0d, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x03, 0x28, 0x02, 0x0e, 0x00, 0x25, 0x2a, - // 0x000e VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING - DYNAMIC | READ - // READ_ANYBODY - 0x08, 0x00, 0x02, 0x01, 0x0e, 0x00, 0x25, 0x2a, - // 0x000f CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING - DYNAMIC | READ - 0x0d, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x03, 0x28, 0x02, 0x10, 0x00, 0x27, 0x2a, - // 0x0010 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING - DYNAMIC | READ - // READ_ANYBODY - 0x08, 0x00, 0x02, 0x01, 0x10, 0x00, 0x27, 0x2a, - // 0x0011 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING - DYNAMIC | READ - 0x0d, 0x00, 0x02, 0x00, 0x11, 0x00, 0x03, 0x28, 0x02, 0x12, 0x00, 0x26, 0x2a, - // 0x0012 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING - DYNAMIC | READ - // READ_ANYBODY - 0x08, 0x00, 0x02, 0x01, 0x12, 0x00, 0x26, 0x2a, - // 0x0013 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING - DYNAMIC | READ - 0x0d, 0x00, 0x02, 0x00, 0x13, 0x00, 0x03, 0x28, 0x02, 0x14, 0x00, 0x28, 0x2a, - // 0x0014 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING - DYNAMIC | READ - // READ_ANYBODY - 0x08, 0x00, 0x02, 0x01, 0x14, 0x00, 0x28, 0x2a, - // 0x0015 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID - DYNAMIC | READ - 0x0d, 0x00, 0x02, 0x00, 0x15, 0x00, 0x03, 0x28, 0x02, 0x16, 0x00, 0x23, 0x2a, - // 0x0016 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID - DYNAMIC | READ - // READ_ANYBODY - 0x08, 0x00, 0x02, 0x01, 0x16, 0x00, 0x23, 0x2a, - // 0x0017 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST - DYNAMIC | READ - 0x0d, 0x00, 0x02, 0x00, 0x17, 0x00, 0x03, 0x28, 0x02, 0x18, 0x00, 0x2a, 0x2a, - // 0x0018 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST - DYNAMIC | READ - // READ_ANYBODY - 0x08, 0x00, 0x02, 0x01, 0x18, 0x00, 0x2a, 0x2a, - // 0x0019 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID - DYNAMIC | READ - 0x0d, 0x00, 0x02, 0x00, 0x19, 0x00, 0x03, 0x28, 0x02, 0x1a, 0x00, 0x50, 0x2a, - // 0x001a VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID - DYNAMIC | READ - // READ_ANYBODY - 0x08, 0x00, 0x02, 0x01, 0x1a, 0x00, 0x50, 0x2a, - - // 0x001b PRIMARY_SERVICE-FFE0 - 0x0a, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x00, 0x28, 0xe0, 0xff, - // 0x001c CHARACTERISTIC-FFE1 - READ | WRITE_WITHOUT_RESPONSE | NOTIFY | DYNAMIC - 0x0d, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x03, 0x28, 0x16, 0x1d, 0x00, 0xe1, 0xff, - // 0x001d VALUE CHARACTERISTIC-FFE1 - READ | WRITE_WITHOUT_RESPONSE | NOTIFY | DYNAMIC - 0x08, 0x00, 0x06, 0x01, 0x1d, 0x00, 0xe1, 0xff, - // 0x001e CLIENT_CHARACTERISTIC_CONFIGURATION - // READ_ANYBODY, WRITE_ANYBODY - 0x0a, 0x00, 0x0e, 0x01, 0x1e, 0x00, 0x02, 0x29, 0x00, 0x00, - // 0x001f USER_DESCRIPTION-READ-HMSoft - // READ_ANYBODY, WRITE_ANYBODY - 0x08, 0x00, 0x0a, 0x01, 0x1f, 0x00, 0x01, 0x29, - // END - 0x00, 0x00, -}; - -void _buildAttdb(const char *Name) { - free(_attdb); - _attdbLen = sizeof(_attdb_head) + 8 + strlen(Name) + sizeof(_attdb_tail); - _attdb = (uint8_t *) malloc(_attdbLen); - memcpy(_attdb, _attdb_head, sizeof(_attdb_head)); - // 0x0003 VALUE CHARACTERISTIC-GAP_DEVICE_NAME - READ - // READ_ANYBODY - // 0x11, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x2a, 0x48, 0x49, 0x44, 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, - int i = sizeof(_attdb_head); - _attdb[i++] = 8 + strlen(Name); - _attdb[i++] = 0x00; - _attdb[i++] = 0x02; - _attdb[i++] = 0x00; - _attdb[i++] = 0x03; - _attdb[i++] = 0x00; - _attdb[i++] = 0x00; - _attdb[i++] = 0x2a; - memcpy(_attdb + i, Name, strlen(Name)); - i += strlen(Name); - memcpy(_attdb + i, _attdb_tail, sizeof(_attdb_tail)); -} - -// support for multiple clients -typedef struct { - char name; - int le_notification_enabled; - uint16_t value_handle; - hci_con_handle_t connection_handle; - int counter; - char test_data[200]; - int test_data_len; - uint32_t test_data_sent; - uint32_t test_data_start; -} le_streamer_connection_t; - -static le_streamer_connection_t le_streamer_connections[MAX_NR_CONNECTIONS]; - -// round robin sending -static int connection_index; - -static void init_connections(void){ - // track connections - int i; - for (i=0;itest_data_start = btstack_run_loop_get_time_ms(); - context->test_data_sent = 0; -} - -static void test_track_sent(le_streamer_connection_t * context, int bytes_sent){ - context->test_data_sent += bytes_sent; - // evaluate - uint32_t now = btstack_run_loop_get_time_ms(); - uint32_t time_passed = now - context->test_data_start; - if (time_passed < REPORT_INTERVAL_MS) return; - // print speed - int bytes_per_second = context->test_data_sent * 1000 / time_passed; -#if DEBUG_BLE - Serial.printf("%c: %"PRIu32" bytes sent-> %u.%03u kB/s\r\n", context->name, context->test_data_sent, bytes_per_second / 1000, bytes_per_second % 1000); -#endif /* DEBUG_BLE */ - - // restart - context->test_data_start = now; - context->test_data_sent = 0; -} - -static void streamer(void){ - - // find next active streaming connection - int old_connection_index = connection_index; - while (true) { - // active found? - if ((le_streamer_connections[connection_index].connection_handle != HCI_CON_HANDLE_INVALID) && - (le_streamer_connections[connection_index].le_notification_enabled)) break; - - // check next - next_connection_index(); - - // none found - if (connection_index == old_connection_index) return; - } - - le_streamer_connection_t * context = &le_streamer_connections[connection_index]; - - size_t size = BLE_FIFO_TX.available(); - size = size < context->test_data_len ? size : context->test_data_len; - size = size < BLE_MAX_WRITE_CHUNK_SIZE ? size : BLE_MAX_WRITE_CHUNK_SIZE; - - if (size > 0) { - for (int i=0; i < size; i++) { - context->test_data[i] = BLE_FIFO_TX.read_char(); - } - - // send - att_server_notify(context->connection_handle, context->value_handle, (uint8_t*) context->test_data, size); - - // request next send event - att_server_request_can_send_now_event(context->connection_handle); - } - - // track - test_track_sent(context, size); - - // check next - next_connection_index(); -} - -static void hci_le_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - UNUSED(channel); - UNUSED(size); - - if (packet_type != HCI_EVENT_PACKET) return; - - uint16_t conn_interval; - hci_con_handle_t con_handle; - static const char * const phy_names[] = { - "1 M", "2 M", "Codec" - }; - - switch (hci_event_packet_get_type(packet)) { - case BTSTACK_EVENT_STATE: - // BTstack activated, get started - if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) { - //Serial.printf("To start the streaming, please run the le_streamer_client example on other device, or use some GATT Explorer, e.g. LightBlue, BLExplr.\r\n"); - } - break; - case HCI_EVENT_DISCONNECTION_COMPLETE: - con_handle = hci_event_disconnection_complete_get_connection_handle(packet); -#if DEBUG_BLE - Serial.printf("- LE Connection 0x%04x: disconnect, reason %02x\r\n", con_handle, hci_event_disconnection_complete_get_reason(packet)); -#endif /* DEBUG_BLE */ - break; - case HCI_EVENT_LE_META: - switch (hci_event_le_meta_get_subevent_code(packet)) { - case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: - // print connection parameters (without using float operations) - con_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); - conn_interval = hci_subevent_le_connection_complete_get_conn_interval(packet); -#if DEBUG_BLE - Serial.printf("- LE Connection 0x%04x: connected - connection interval %u.%02u ms, latency %u\r\n", con_handle, conn_interval * 125 / 100, - 25 * (conn_interval & 3), hci_subevent_le_connection_complete_get_conn_latency(packet)); - - // request min con interval 15 ms for iOS 11+ - Serial.printf("- LE Connection 0x%04x: request 15 ms connection interval\r\n", con_handle); -#endif /* DEBUG_BLE */ - gap_request_connection_parameter_update(con_handle, 12, 12, 0, 0x0048); - break; - case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE: - // print connection parameters (without using float operations) - con_handle = hci_subevent_le_connection_update_complete_get_connection_handle(packet); - conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet); -#if DEBUG_BLE - Serial.printf("- LE Connection 0x%04x: connection update - connection interval %u.%02u ms, latency %u\r\n", con_handle, conn_interval * 125 / 100, - 25 * (conn_interval & 3), hci_subevent_le_connection_update_complete_get_conn_latency(packet)); -#endif /* DEBUG_BLE */ - break; - case HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE: - con_handle = hci_subevent_le_data_length_change_get_connection_handle(packet); -#if DEBUG_BLE - Serial.printf("- LE Connection 0x%04x: data length change - max %u bytes per packet\r\n", con_handle, - hci_subevent_le_data_length_change_get_max_tx_octets(packet)); -#endif /* DEBUG_BLE */ - break; - case HCI_SUBEVENT_LE_PHY_UPDATE_COMPLETE: - con_handle = hci_subevent_le_phy_update_complete_get_connection_handle(packet); - Serial.printf("- LE Connection 0x%04x: PHY update - using LE %s PHY now\r\n", con_handle, - phy_names[hci_subevent_le_phy_update_complete_get_tx_phy(packet)]); - break; - default: - break; - } - break; - - default: - break; - } -} - -static void att_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - UNUSED(channel); - UNUSED(size); - - int mtu; - le_streamer_connection_t * context; - switch (packet_type) { - case HCI_EVENT_PACKET: - switch (hci_event_packet_get_type(packet)) { - case ATT_EVENT_CONNECTED: - // setup new - context = connection_for_conn_handle(HCI_CON_HANDLE_INVALID); - if (!context) break; - context->counter = 'A'; - context->connection_handle = att_event_connected_get_handle(packet); - context->test_data_len = btstack_min(att_server_get_mtu(context->connection_handle) - 3, sizeof(context->test_data)); -#if DEBUG_BLE - Serial.printf("%c: ATT connected, handle %04x, test data len %u\r\n", context->name, context->connection_handle, context->test_data_len); -#endif /* DEBUG_BLE */ - break; - case ATT_EVENT_MTU_EXCHANGE_COMPLETE: - mtu = att_event_mtu_exchange_complete_get_MTU(packet) - 3; - context = connection_for_conn_handle(att_event_mtu_exchange_complete_get_handle(packet)); - if (!context) break; - context->test_data_len = btstack_min(mtu - 3, sizeof(context->test_data)); -#if DEBUG_BLE - Serial.printf("%c: ATT MTU = %u => use test data of len %u\r\n", context->name, mtu, context->test_data_len); -#endif /* DEBUG_BLE */ - break; - case ATT_EVENT_CAN_SEND_NOW: - streamer(); - break; - case ATT_EVENT_DISCONNECTED: - context = connection_for_conn_handle(att_event_disconnected_get_handle(packet)); - if (!context) break; - // free connection -#if DEBUG_BLE - Serial.printf("%c: ATT disconnected, handle %04x\r\n", context->name, context->connection_handle); -#endif /* DEBUG_BLE */ - context->le_notification_enabled = 0; - context->connection_handle = HCI_CON_HANDLE_INVALID; - break; - default: - break; - } - break; - default: - break; - } -} - -static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){ - UNUSED(offset); - -#if DEBUG_BLE - Serial.printf("att_write_callback att_handle %04x, transaction mode %u size %u offset %u\r\n", att_handle, transaction_mode, buffer_size, offset); -#endif /* DEBUG_BLE */ - if (transaction_mode != ATT_TRANSACTION_MODE_NONE) return 0; - le_streamer_connection_t * context = connection_for_conn_handle(con_handle); - switch(att_handle){ - case ATT_CHARACTERISTIC_FFE1_01_CLIENT_CONFIGURATION_HANDLE: - context->le_notification_enabled = little_endian_read_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION; - //Serial.printf("%c: Notifications enabled %u\r\n", context->name, context->le_notification_enabled); - if (context->le_notification_enabled){ - switch (att_handle){ - case ATT_CHARACTERISTIC_FFE1_01_CLIENT_CONFIGURATION_HANDLE: - context->value_handle = ATT_CHARACTERISTIC_FFE1_01_VALUE_HANDLE; - break; - default: - break; - } - att_server_request_can_send_now_event(context->connection_handle); - } - test_reset(context); - break; - case ATT_CHARACTERISTIC_FFE1_01_VALUE_HANDLE: -#if DEBUG_BLE - Serial.printf("Write to 0x%04x, len %u offset %u\r\n", att_handle, buffer_size, offset); -#endif /* DEBUG_BLE */ - if (buffer_size > 0 && offset == 0) { - size_t size = BLE_FIFO_RX.availableForStore(); - size = (size < buffer_size) ? size : buffer_size; - for (size_t i = 0; i < size; i++) { - BLE_FIFO_RX.store_char(buffer[i]); - } - } - break; - default: - Serial.printf("Write to 0x%04x, len %u\r\n", att_handle, buffer_size); - break; - } - return 0; -} - -#define ATT_VALUE_MAX_LEN 50 -#define ATT_NUM_ATTRIBUTES 10 - -typedef struct { - uint16_t handle; - uint16_t len; - uint8_t value[ATT_VALUE_MAX_LEN]; -} attribute_t; - -static attribute_t att_attributes[ATT_NUM_ATTRIBUTES]; - -// handle == 0 finds free attribute -static int att_attribute_for_handle(uint16_t aHandle){ - int i; - for (i=0;igetChipId() & 0x00FFFFFFU, HEX); - - switch (settings->bluetooth) - { - case BLUETOOTH_SPP: - { - mutex_init(&_mutex); - _overflow = false; - - _queue = new uint8_t[_fifoSize]; - _writer = 0; - _reader = 0; - - // register for HCI events - _hci_event_callback_registration.callback = &hci_spp_packet_handler; - hci_add_event_handler(&_hci_event_callback_registration); - - l2cap_init(); - -#ifdef ENABLE_BLE - // Initialize LE Security Manager. Needed for cross-transport key derivation - sm_init(); -#endif - - rfcomm_init(); - rfcomm_register_service(hci_spp_packet_handler, RFCOMM_SERVER_CHANNEL, 0xffff); // reserved channel, mtu limited by l2cap - - // init SDP, create record for SPP and register with SDP - sdp_init(); - bzero(_spp_service_buffer, sizeof(_spp_service_buffer)); - spp_create_sdp_record(_spp_service_buffer, 0x10001, RFCOMM_SERVER_CHANNEL, "CYW43_SPP"); - sdp_register_service(_spp_service_buffer); - - gap_discoverable_control(1); - gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO); - gap_set_local_name(BT_name.c_str()); - - hci_power_control(HCI_POWER_ON); - - _running = true; - } - break; - case BLUETOOTH_LE_HM10_SERIAL: - { - mutex_init(&_mutex); - - BT_name += "-LE"; - _buildAdvData(BT_name.c_str()); - _buildAttdb(BT_name.c_str()); - - l2cap_init(); -// l2cap_set_max_le_mtu(26); - - // setup SM: Display only - sm_init(); - - // setup ATT server - att_server_init(_attdb, NULL, att_write_callback); - - // Setup battery service - battery_service_server_init((uint8_t) Battery_charge()); - - static char SerialNum[9]; - snprintf(SerialNum, sizeof(SerialNum), "%08X", SoC->getChipId()); - static char Hardware[9]; - snprintf(Hardware, sizeof(Hardware), "%08X", hw_info.revision); - - const char *Firmware = "Arduino RP2040 " ARDUINO_PICO_VERSION_STR; - - // Setup device information service - device_information_service_server_init(); - device_information_service_server_set_manufacturer_name(RP2040_Device_Manufacturer); - device_information_service_server_set_model_number(RP2040_Device_Model); - device_information_service_server_set_serial_number(SerialNum); - device_information_service_server_set_hardware_revision(Hardware); - device_information_service_server_set_firmware_revision(Firmware); - device_information_service_server_set_software_revision(SOFTRF_FIRMWARE_VERSION); - - // register for HCI events - _hci_event_callback_registration.callback = &hci_le_packet_handler; - hci_add_event_handler(&_hci_event_callback_registration); - - att_attributes_init(); - - // register for ATT events - att_server_register_packet_handler(att_packet_handler); - - // setup advertisements - uint16_t adv_int_min = 0x0030; - uint16_t adv_int_max = 0x0030; - uint8_t adv_type = 0; - bd_addr_t null_addr; - memset(null_addr, 0, 6); - gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00); - gap_advertisements_set_data(_advDataLen, _advData); - gap_advertisements_enable(1); - - // init client state - init_connections(); - - hci_power_control(HCI_POWER_ON); - - BLE_Aux_Tx_TimeMarker = millis(); - _running = true; - } - break; - case BLUETOOTH_A2DP_SOURCE: -#if defined(ENABLE_BT_VOICE) - A2DPSource.setVolume(50); - A2DPSource.begin(in); -#endif - break; - case BLUETOOTH_NONE: - default: - break; - } -} - -static void CYW43_Bluetooth_loop() -{ - switch (settings->bluetooth) - { - case BLUETOOTH_LE_HM10_SERIAL: - if (_running && (millis() - BLE_Aux_Tx_TimeMarker > 100)) { - if (BLE_FIFO_TX.available() > 0) { - streamer(); - } - BLE_Aux_Tx_TimeMarker = millis(); - } - - if (isTimeToBattery()) { - battery_service_server_set_battery_value((uint8_t) Battery_charge()); - } - break; - case BLUETOOTH_NONE: - case BLUETOOTH_SPP: - case BLUETOOTH_A2DP_SOURCE: - default: - break; - } -} - -static void CYW43_Bluetooth_fini() -{ - switch (settings->bluetooth) - { - case BLUETOOTH_SPP: - case BLUETOOTH_LE_HM10_SERIAL: - { - if (!_running) { - return; - } - _running = false; - - hci_power_control(HCI_POWER_OFF); - lockBluetooth(); - if (_queue != NULL) delete[] _queue; - unlockBluetooth(); - } - break; - case BLUETOOTH_A2DP_SOURCE: - case BLUETOOTH_NONE: - default: - break; - } -} - -static int CYW43_Bluetooth_available() -{ - int rval = 0; - - switch (settings->bluetooth) - { - case BLUETOOTH_SPP: - { - CoreMutex m(&_mutex); - if (_running && m) { - rval = (_fifoSize + _writer - _reader) % _fifoSize; - } - } - break; - case BLUETOOTH_LE_HM10_SERIAL: - rval = BLE_FIFO_RX.available(); - break; - case BLUETOOTH_NONE: - case BLUETOOTH_A2DP_SOURCE: - default: - break; - } - - return rval; -} - -static int CYW43_Bluetooth_read() -{ - int rval = -1; - - switch (settings->bluetooth) - { - case BLUETOOTH_SPP: - { - CoreMutex m(&_mutex); - if (_running && m && _writer != _reader) { - auto ret = _queue[_reader]; - asm volatile("" ::: "memory"); // Ensure the value is read before advancing - auto next_reader = (_reader + 1) % _fifoSize; - asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly - _reader = next_reader; - rval = ret; - } - } - break; - case BLUETOOTH_LE_HM10_SERIAL: - rval = BLE_FIFO_RX.read_char(); - break; - case BLUETOOTH_NONE: - case BLUETOOTH_A2DP_SOURCE: - default: - break; - } - - return rval; -} - -static size_t CYW43_Bluetooth_write(const uint8_t *buffer, size_t size) -{ - size_t rval = size; - - switch (settings->bluetooth) - { - case BLUETOOTH_SPP: - { - CoreMutex m(&_mutex); - if (!_running || !m || !_connected || !size) { - return 0; - } - _writeBuff = buffer; - _writeLen = size; - lockBluetooth(); - rfcomm_request_can_send_now_event(_channelID); - unlockBluetooth(); - while (_connected && _writeLen) { - /* noop busy wait */ - } - } - break; - case BLUETOOTH_LE_HM10_SERIAL: - { - size_t avail = BLE_FIFO_TX.availableForStore(); - if (size > avail) { - rval = avail; - } - for (size_t i = 0; i < rval; i++) { - BLE_FIFO_TX.store_char(buffer[i]); - } - } - break; - case BLUETOOTH_NONE: - case BLUETOOTH_A2DP_SOURCE: - default: - break; - } - - return rval; -} - -IODev_ops_t CYW43_Bluetooth_ops = { - "CYW43 Bluetooth", - CYW43_Bluetooth_setup, - CYW43_Bluetooth_loop, - CYW43_Bluetooth_fini, - CYW43_Bluetooth_available, - CYW43_Bluetooth_read, - CYW43_Bluetooth_write -}; -#endif /* EXCLUDE_BLUETOOTH */ -#endif /* ESP32 or ARDUINO_ARCH_NRF52 or ARDUINO_ARCH_RP2040 */ +/* Superseded by files in 'src/platform/bluetooth' folder */ diff --git a/software/firmware/source/SoftRF/src/driver/Bluetooth.h b/software/firmware/source/SoftRF/src/driver/Bluetooth.h index 3328c74a0..f3c132afb 100644 --- a/software/firmware/source/SoftRF/src/driver/Bluetooth.h +++ b/software/firmware/source/SoftRF/src/driver/Bluetooth.h @@ -32,164 +32,11 @@ enum #endif #if defined(ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) - -#define UART_SERVICE_UUID16 ((uint16_t) 0xFFE0) -#define UART_CHARACTERISTIC_UUID16 ((uint16_t) 0xFFE1) -#define UART_SERVICE_UUID128 "0000ffe0-0000-1000-8000-00805f9b34fb" -#define UART_CHARACTERISTIC_UUID128 "0000ffe1-0000-1000-8000-00805f9b34fb" - -#define MIDI_SERVICE_UUID "03b80e5a-ede8-4b33-a751-6ce34ec4c700" -#define MIDI_CHARACTERISTIC_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3" - -#define UUID16_SVC_BATTERY ((uint16_t) 0x180F) -#define UUID16_CHR_BATTERY_LEVEL ((uint16_t) 0x2A19) - -#define UUID16_SVC_DEVICE_INFORMATION ((uint16_t) 0x180A) -#define UUID16_CHR_MODEL_NUMBER_STRING ((uint16_t) 0x2A24) -#define UUID16_CHR_SERIAL_NUMBER_STRING ((uint16_t) 0x2A25) -#define UUID16_CHR_FIRMWARE_REVISION_STRING ((uint16_t) 0x2A26) -#define UUID16_CHR_HARDWARE_REVISION_STRING ((uint16_t) 0x2A27) -#define UUID16_CHR_SOFTWARE_REVISION_STRING ((uint16_t) 0x2A28) -#define UUID16_CHR_MANUFACTURER_NAME_STRING ((uint16_t) 0x2A29) - -#define SENSBOX_SERVICE_UUID "aba27100-143b-4b81-a444-edcd0000f020" -#define NAVIGATION_CHARACTERISTIC_UUID "aba27100-143b-4b81-a444-edcd0000f022" -#define MOVEMENT_CHARACTERISTIC_UUID "aba27100-143b-4b81-a444-edcd0000f023" -#define GPS2_CHARACTERISTIC_UUID "aba27100-143b-4b81-a444-edcd0000f024" -#define SYSTEM_CHARACTERISTIC_UUID "aba27100-143b-4b81-a444-edcd0000f025" - -/* (FLAA x MAX_TRACKING_OBJECTS + GNGGA + GNRMC + FLAU) x 80 symbols */ -#define BLE_FIFO_TX_SIZE 1024 -#define BLE_FIFO_RX_SIZE 256 - -#define BLE_MAX_WRITE_CHUNK_SIZE 20 - -extern IODev_ops_t ESP32_Bluetooth_ops; - +#include "../platform/bluetooth/Bluedroid.h" #elif defined(ARDUINO_ARCH_NRF52) - -#include "bluefruit_common.h" - -#include "BLECharacteristic.h" -#include "BLEService.h" - -class BLESensBox : public BLEService -{ - protected: - BLECharacteristic _sensbox_nav; - BLECharacteristic _sensbox_move; - BLECharacteristic _sensbox_gps2; - BLECharacteristic _sensbox_sys; - - public: - BLESensBox(void); - - virtual err_t begin(void); - - bool notify_nav (uint8_t); - bool notify_move(uint8_t); - bool notify_gps2(uint8_t); - bool notify_sys (uint8_t); -}; - -/* - * Source: - * https://github.com/flytec/SensBoxLib_iOS/blob/master/_SensBox%20Documentation/SensorBox%20BLE%20Protocol.pdf - */ -typedef struct { - uint32_t timestamp; /* Date/Time (UTC), UnixTime */ - int32_t lat; /* deg * 10^7 */ - int32_t lon; /* deg * 10^7 */ - int16_t gnss_alt; /* GPS Hight MSL, m */ - int16_t pres_alt; /* Pressure Altitude, m */ - int16_t vario; /* Vario 1 Hz, cm/s */ - uint8_t status; /* Status: 0..2 - GNSS, 3 - time, 4 - charge, 5 - Bat, 6 - Log */ -} __attribute__((packed)) sensbox_navigation_t; - -typedef struct { - int32_t pres_alt; /* Pressure Altitude, cm */ - int16_t vario; /* Vario 8 Hz, cm/s */ - int16_t gs; /* Ground Speed, dm/s */ - int16_t cog; /* GPS Heading, deg * 10 */ - int16_t pitch; /* deg * 10 */ - int16_t yaw; /* deg * 10 */ - int16_t roll; /* deg * 10 */ - uint16_t accel; /* Acceleration, 'g' * 10 */ - uint8_t status; /* Status: same as above */ -} __attribute__((packed)) sensbox_movement_t; - -typedef struct { - uint16_t accuracy_h; /* Horizontal Accuracy, dm */ - uint16_t accuracy_v; /* Vertical Accuracy, dm */ - int16_t geo_separ; /* GPS Height Ellipsoid, m */ - uint8_t sats; /* Number of satellites */ - uint8_t status; /* Status: same as above */ -} __attribute__((packed)) sensbox_gps2_t; - -typedef struct { - uint32_t timestamp; /* Date/Time (UTC), UnixTime */ - uint8_t battery; /* Battery Level, % */ - uint8_t log; /* Logging level, % */ - int16_t temp; /* Temperature, °C * 10 */ - uint8_t status; /* Status: same as above */ - uint8_t status2; - uint16_t qnh; /* QNH, Pa * 10 */ - int32_t pressure; /* mPa */ -} __attribute__((packed)) sensbox_system_t; - -#define isTimeToSensBox() (millis() - BLE_SensBox_TimeMarker > 500) /* 2 Hz */ - -extern IODev_ops_t nRF52_Bluetooth_ops; - +#include "../platform/bluetooth/Bluefruit.h" #elif defined(ARDUINO_ARCH_RP2040) && defined(ARDUINO_RASPBERRY_PI_PICO_W) - -// -// list service handle ranges -// -#define ATT_SERVICE_GAP_SERVICE_START_HANDLE 0x0001 -#define ATT_SERVICE_GAP_SERVICE_END_HANDLE 0x0003 -#define ATT_SERVICE_GAP_SERVICE_01_START_HANDLE 0x0001 -#define ATT_SERVICE_GAP_SERVICE_01_END_HANDLE 0x0003 -#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE_START_HANDLE 0x0004 -#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE_END_HANDLE 0x0007 -#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE_01_START_HANDLE 0x0004 -#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE_01_END_HANDLE 0x0007 -#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION_START_HANDLE 0x0008 -#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION_END_HANDLE 0x001a -#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION_01_START_HANDLE 0x0008 -#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION_01_END_HANDLE 0x001a -#define ATT_SERVICE_FFE0_START_HANDLE 0x001b -#define ATT_SERVICE_FFE0_END_HANDLE 0x001f -#define ATT_SERVICE_FFE0_01_START_HANDLE 0x001b -#define ATT_SERVICE_FFE0_01_END_HANDLE 0x001f - -// -// list mapping between characteristics and handles -// -#define ATT_CHARACTERISTIC_GAP_DEVICE_NAME_01_VALUE_HANDLE 0x0003 -#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_01_VALUE_HANDLE 0x0006 -#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_01_CLIENT_CONFIGURATION_HANDLE 0x0007 -#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING_01_VALUE_HANDLE 0x000a -#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING_01_VALUE_HANDLE 0x000c -#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING_01_VALUE_HANDLE 0x000e -#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING_01_VALUE_HANDLE 0x0010 -#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING_01_VALUE_HANDLE 0x0012 -#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING_01_VALUE_HANDLE 0x0014 -#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID_01_VALUE_HANDLE 0x0016 -#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST_01_VALUE_HANDLE 0x0018 -#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID_01_VALUE_HANDLE 0x001a -#define ATT_CHARACTERISTIC_FFE1_01_VALUE_HANDLE 0x001d -#define ATT_CHARACTERISTIC_FFE1_01_CLIENT_CONFIGURATION_HANDLE 0x001e -#define ATT_CHARACTERISTIC_FFE1_01_USER_DESCRIPTION_HANDLE 0x001f - -/* (FLAA x MAX_TRACKING_OBJECTS + GNGGA + GNRMC + FLAU) x 80 symbols */ -#define BLE_FIFO_TX_SIZE 1024 -#define BLE_FIFO_RX_SIZE 256 - -#define BLE_MAX_WRITE_CHUNK_SIZE 20 - -extern IODev_ops_t CYW43_Bluetooth_ops; - +#include "../platform/bluetooth/BTstack.h" #endif /* ESP32 or ARDUINO_ARCH_NRF52 or ARDUINO_ARCH_RP2040 */ #endif /* BLUETOOTHHELPER_H */ diff --git a/software/firmware/source/SoftRF/src/platform/bluetooth/BTstack.cpp b/software/firmware/source/SoftRF/src/platform/bluetooth/BTstack.cpp new file mode 100644 index 000000000..12a8998d5 --- /dev/null +++ b/software/firmware/source/SoftRF/src/platform/bluetooth/BTstack.cpp @@ -0,0 +1,909 @@ +/* + * BluetoothHelper.cpp + * Copyright (C) 2018-2024 Linar Yusupov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#if defined(ARDUINO_ARCH_RP2040) + +#include "../../system/SoC.h" + +#if !defined(EXCLUDE_BLUETOOTH) + +#include +#include +#include +#include + +#include + +#include "../../driver/EEPROM.h" +#include "../../driver/WiFi.h" +#include "../../driver/Bluetooth.h" +#include "../../driver/Battery.h" + +#if defined(ENABLE_BT_VOICE) +#include "AudioTools.h" +#include "BTstack_A2DP.h" + +SineWaveGenerator sineWave(32000); +GeneratedSoundStream in(sineWave); +#endif /* ENABLE_BT_VOICE */ + +static bool _running = false; +static mutex_t _mutex; +static bool _overflow = false; +static volatile bool _connected = false; + +static uint32_t _writer; +static uint32_t _reader; +static size_t _fifoSize = 32; +static uint8_t *_queue = NULL; + +static const int RFCOMM_SERVER_CHANNEL = 1; + +static uint16_t _channelID; +static uint8_t _spp_service_buffer[150]; +static btstack_packet_callback_registration_t _hci_event_callback_registration; + +static volatile int _writeLen = 0; +static const void *_writeBuff; + +RingBufferN BLE_FIFO_TX = RingBufferN(); +RingBufferN BLE_FIFO_RX = RingBufferN(); + +String BT_name = HOSTNAME; + +/* ------- SPP BEGIN ------ */ + +static void hci_spp_packet_handler(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size) { + UNUSED(channel); + bd_addr_t event_addr; + //uint8_t rfcomm_channel_nr; + //uint16_t mtu; + int i; + + switch (type) { + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + case HCI_EVENT_PIN_CODE_REQUEST: + //Serial.printf("Pin code request - using '0000'\n"); + hci_event_pin_code_request_get_bd_addr(packet, event_addr); + gap_pin_code_response(event_addr, "0000"); + break; + + case HCI_EVENT_USER_CONFIRMATION_REQUEST: + // ssp: inform about user confirmation request + //Serial.printf("SSP User Confirmation Request with numeric value '%06" PRIu32 "'\n", little_endian_read_32(packet, 8)); + //Serial.printf("SSP User Confirmation Auto accept\n"); + break; + + case RFCOMM_EVENT_INCOMING_CONNECTION: + rfcomm_event_incoming_connection_get_bd_addr(packet, event_addr); + //rfcomm_channel_nr = rfcomm_event_incoming_connection_get_server_channel(packet); + _channelID = rfcomm_event_incoming_connection_get_rfcomm_cid(packet); + //Serial.printf("RFCOMM channel %u requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr)); + rfcomm_accept_connection(_channelID); + break; + + case RFCOMM_EVENT_CHANNEL_OPENED: + if (rfcomm_event_channel_opened_get_status(packet)) { + //Serial.printf("RFCOMM channel open failed, status 0x%02x\n", rfcomm_event_channel_opened_get_status(packet)); + } else { + _channelID = rfcomm_event_channel_opened_get_rfcomm_cid(packet); + //mtu = rfcomm_event_channel_opened_get_max_frame_size(packet); + //Serial.printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_channel_id, mtu); + _connected = true; + } + break; + case RFCOMM_EVENT_CAN_SEND_NOW: + rfcomm_send(_channelID, (uint8_t *)_writeBuff, _writeLen); + _writeLen = 0; + break; + case RFCOMM_EVENT_CHANNEL_CLOSED: + //Serial.printf("RFCOMM channel closed\n"); + _channelID = 0; + _connected = false; + break; + + default: + break; + } + break; + + case RFCOMM_DATA_PACKET: + for (i = 0; i < size; i++) { + auto next_writer = _writer + 1; + if (next_writer == _fifoSize) { + next_writer = 0; + } + if (next_writer != _reader) { + _queue[_writer] = packet[i]; + asm volatile("" ::: "memory"); // Ensure the queue is written before the written count advances + // Avoid using division or mod because the HW divider could be in use + _writer = next_writer; + } else { + _overflow = true; + } + } + break; + + default: + break; + } +} + +/* ------- SPP END ------ */ + +/* ------- BLE BEGIN------ */ + +#define REPORT_INTERVAL_MS 3000 +#define MAX_NR_CONNECTIONS 3 +#define APP_AD_FLAGS 0x06 + +#define DEBUG_BLE 0 + +uint8_t *_advData = nullptr; +uint8_t _advDataLen = 0; +uint8_t *_attdb = nullptr; +size_t _attdbLen = 0; + +void _buildAdvData(const char *completeLocalName) { + free(_advData); + _advDataLen = 9 + strlen(completeLocalName); + _advData = (uint8_t*) malloc(_advDataLen); + int i = 0; + // Flags general discoverable, BR/EDR not supported + // 0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06, + _advData[i++] = 0x02; + _advData[i++] = BLUETOOTH_DATA_TYPE_FLAGS; + _advData[i++] = 0x06; + // Name + // 0x0d, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, + _advData[i++] = 1 + strlen(completeLocalName); + _advData[i++] = BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME; + memcpy(_advData + i, completeLocalName, strlen(completeLocalName)); + i += strlen(completeLocalName); + // 16-bit Service UUIDs + _advData[i++] = 0x03; + _advData[i++] = BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS; + _advData[i++] = 0xe0; + _advData[i++] = 0xff; +} + +static constexpr const uint8_t _attdb_head[] = { + // ATT DB Version + 1, + + // 0x0001 PRIMARY_SERVICE-GAP_SERVICE + 0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x18, + // 0x0002 CHARACTERISTIC-GAP_DEVICE_NAME - READ + 0x0d, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x28, 0x02, 0x03, 0x00, 0x00, 0x2a, +}; + +static constexpr const uint8_t _attdb_tail[] = { + // Specification Type org.bluetooth.service.battery_service + // https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.battery_service.xml + // Battery Service 180F + // 0x0004 PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE + 0x0a, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x28, 0x0f, 0x18, + // 0x0005 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL - DYNAMIC | READ | NOTIFY + 0x0d, 0x00, 0x02, 0x00, 0x05, 0x00, 0x03, 0x28, 0x12, 0x06, 0x00, 0x19, 0x2a, + // 0x0006 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL - DYNAMIC | READ | NOTIFY + // READ_ANYBODY + 0x08, 0x00, 0x02, 0x01, 0x06, 0x00, 0x19, 0x2a, + // 0x0007 CLIENT_CHARACTERISTIC_CONFIGURATION + // READ_ANYBODY, WRITE_ANYBODY + 0x0a, 0x00, 0x0e, 0x01, 0x07, 0x00, 0x02, 0x29, 0x00, 0x00, + + // Specification Type org.bluetooth.service.device_information + // https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.device_information.xml + // Device Information 180A + // 0x0008 PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION + 0x0a, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x28, 0x0a, 0x18, + // 0x0009 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING - DYNAMIC | READ + 0x0d, 0x00, 0x02, 0x00, 0x09, 0x00, 0x03, 0x28, 0x02, 0x0a, 0x00, 0x29, 0x2a, + // 0x000a VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING - DYNAMIC | READ + // READ_ANYBODY + 0x08, 0x00, 0x02, 0x01, 0x0a, 0x00, 0x29, 0x2a, + // 0x000b CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING - DYNAMIC | READ + 0x0d, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x03, 0x28, 0x02, 0x0c, 0x00, 0x24, 0x2a, + // 0x000c VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING - DYNAMIC | READ + // READ_ANYBODY + 0x08, 0x00, 0x02, 0x01, 0x0c, 0x00, 0x24, 0x2a, + // 0x000d CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING - DYNAMIC | READ + 0x0d, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x03, 0x28, 0x02, 0x0e, 0x00, 0x25, 0x2a, + // 0x000e VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING - DYNAMIC | READ + // READ_ANYBODY + 0x08, 0x00, 0x02, 0x01, 0x0e, 0x00, 0x25, 0x2a, + // 0x000f CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING - DYNAMIC | READ + 0x0d, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x03, 0x28, 0x02, 0x10, 0x00, 0x27, 0x2a, + // 0x0010 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING - DYNAMIC | READ + // READ_ANYBODY + 0x08, 0x00, 0x02, 0x01, 0x10, 0x00, 0x27, 0x2a, + // 0x0011 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING - DYNAMIC | READ + 0x0d, 0x00, 0x02, 0x00, 0x11, 0x00, 0x03, 0x28, 0x02, 0x12, 0x00, 0x26, 0x2a, + // 0x0012 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING - DYNAMIC | READ + // READ_ANYBODY + 0x08, 0x00, 0x02, 0x01, 0x12, 0x00, 0x26, 0x2a, + // 0x0013 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING - DYNAMIC | READ + 0x0d, 0x00, 0x02, 0x00, 0x13, 0x00, 0x03, 0x28, 0x02, 0x14, 0x00, 0x28, 0x2a, + // 0x0014 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING - DYNAMIC | READ + // READ_ANYBODY + 0x08, 0x00, 0x02, 0x01, 0x14, 0x00, 0x28, 0x2a, + // 0x0015 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID - DYNAMIC | READ + 0x0d, 0x00, 0x02, 0x00, 0x15, 0x00, 0x03, 0x28, 0x02, 0x16, 0x00, 0x23, 0x2a, + // 0x0016 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID - DYNAMIC | READ + // READ_ANYBODY + 0x08, 0x00, 0x02, 0x01, 0x16, 0x00, 0x23, 0x2a, + // 0x0017 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST - DYNAMIC | READ + 0x0d, 0x00, 0x02, 0x00, 0x17, 0x00, 0x03, 0x28, 0x02, 0x18, 0x00, 0x2a, 0x2a, + // 0x0018 VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST - DYNAMIC | READ + // READ_ANYBODY + 0x08, 0x00, 0x02, 0x01, 0x18, 0x00, 0x2a, 0x2a, + // 0x0019 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID - DYNAMIC | READ + 0x0d, 0x00, 0x02, 0x00, 0x19, 0x00, 0x03, 0x28, 0x02, 0x1a, 0x00, 0x50, 0x2a, + // 0x001a VALUE CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID - DYNAMIC | READ + // READ_ANYBODY + 0x08, 0x00, 0x02, 0x01, 0x1a, 0x00, 0x50, 0x2a, + + // 0x001b PRIMARY_SERVICE-FFE0 + 0x0a, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x00, 0x28, 0xe0, 0xff, + // 0x001c CHARACTERISTIC-FFE1 - READ | WRITE_WITHOUT_RESPONSE | NOTIFY | DYNAMIC + 0x0d, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x03, 0x28, 0x16, 0x1d, 0x00, 0xe1, 0xff, + // 0x001d VALUE CHARACTERISTIC-FFE1 - READ | WRITE_WITHOUT_RESPONSE | NOTIFY | DYNAMIC + 0x08, 0x00, 0x06, 0x01, 0x1d, 0x00, 0xe1, 0xff, + // 0x001e CLIENT_CHARACTERISTIC_CONFIGURATION + // READ_ANYBODY, WRITE_ANYBODY + 0x0a, 0x00, 0x0e, 0x01, 0x1e, 0x00, 0x02, 0x29, 0x00, 0x00, + // 0x001f USER_DESCRIPTION-READ-HMSoft + // READ_ANYBODY, WRITE_ANYBODY + 0x08, 0x00, 0x0a, 0x01, 0x1f, 0x00, 0x01, 0x29, + // END + 0x00, 0x00, +}; + +void _buildAttdb(const char *Name) { + free(_attdb); + _attdbLen = sizeof(_attdb_head) + 8 + strlen(Name) + sizeof(_attdb_tail); + _attdb = (uint8_t *) malloc(_attdbLen); + memcpy(_attdb, _attdb_head, sizeof(_attdb_head)); + // 0x0003 VALUE CHARACTERISTIC-GAP_DEVICE_NAME - READ + // READ_ANYBODY + // 0x11, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x2a, 0x48, 0x49, 0x44, 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, + int i = sizeof(_attdb_head); + _attdb[i++] = 8 + strlen(Name); + _attdb[i++] = 0x00; + _attdb[i++] = 0x02; + _attdb[i++] = 0x00; + _attdb[i++] = 0x03; + _attdb[i++] = 0x00; + _attdb[i++] = 0x00; + _attdb[i++] = 0x2a; + memcpy(_attdb + i, Name, strlen(Name)); + i += strlen(Name); + memcpy(_attdb + i, _attdb_tail, sizeof(_attdb_tail)); +} + +// support for multiple clients +typedef struct { + char name; + int le_notification_enabled; + uint16_t value_handle; + hci_con_handle_t connection_handle; + int counter; + char test_data[200]; + int test_data_len; + uint32_t test_data_sent; + uint32_t test_data_start; +} le_streamer_connection_t; + +static le_streamer_connection_t le_streamer_connections[MAX_NR_CONNECTIONS]; + +// round robin sending +static int connection_index; + +static void init_connections(void){ + // track connections + int i; + for (i=0;itest_data_start = btstack_run_loop_get_time_ms(); + context->test_data_sent = 0; +} + +static void test_track_sent(le_streamer_connection_t * context, int bytes_sent){ + context->test_data_sent += bytes_sent; + // evaluate + uint32_t now = btstack_run_loop_get_time_ms(); + uint32_t time_passed = now - context->test_data_start; + if (time_passed < REPORT_INTERVAL_MS) return; + // print speed + int bytes_per_second = context->test_data_sent * 1000 / time_passed; +#if DEBUG_BLE + Serial.printf("%c: %"PRIu32" bytes sent-> %u.%03u kB/s\r\n", context->name, context->test_data_sent, bytes_per_second / 1000, bytes_per_second % 1000); +#endif /* DEBUG_BLE */ + + // restart + context->test_data_start = now; + context->test_data_sent = 0; +} + +static void streamer(void){ + + // find next active streaming connection + int old_connection_index = connection_index; + while (true) { + // active found? + if ((le_streamer_connections[connection_index].connection_handle != HCI_CON_HANDLE_INVALID) && + (le_streamer_connections[connection_index].le_notification_enabled)) break; + + // check next + next_connection_index(); + + // none found + if (connection_index == old_connection_index) return; + } + + le_streamer_connection_t * context = &le_streamer_connections[connection_index]; + + size_t size = BLE_FIFO_TX.available(); + size = size < context->test_data_len ? size : context->test_data_len; + size = size < BLE_MAX_WRITE_CHUNK_SIZE ? size : BLE_MAX_WRITE_CHUNK_SIZE; + + if (size > 0) { + for (int i=0; i < size; i++) { + context->test_data[i] = BLE_FIFO_TX.read_char(); + } + + // send + att_server_notify(context->connection_handle, context->value_handle, (uint8_t*) context->test_data, size); + + // request next send event + att_server_request_can_send_now_event(context->connection_handle); + } + + // track + test_track_sent(context, size); + + // check next + next_connection_index(); +} + +static void hci_le_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(channel); + UNUSED(size); + + if (packet_type != HCI_EVENT_PACKET) return; + + uint16_t conn_interval; + hci_con_handle_t con_handle; + static const char * const phy_names[] = { + "1 M", "2 M", "Codec" + }; + + switch (hci_event_packet_get_type(packet)) { + case BTSTACK_EVENT_STATE: + // BTstack activated, get started + if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) { + //Serial.printf("To start the streaming, please run the le_streamer_client example on other device, or use some GATT Explorer, e.g. LightBlue, BLExplr.\r\n"); + } + break; + case HCI_EVENT_DISCONNECTION_COMPLETE: + con_handle = hci_event_disconnection_complete_get_connection_handle(packet); +#if DEBUG_BLE + Serial.printf("- LE Connection 0x%04x: disconnect, reason %02x\r\n", con_handle, hci_event_disconnection_complete_get_reason(packet)); +#endif /* DEBUG_BLE */ + break; + case HCI_EVENT_LE_META: + switch (hci_event_le_meta_get_subevent_code(packet)) { + case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: + // print connection parameters (without using float operations) + con_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); + conn_interval = hci_subevent_le_connection_complete_get_conn_interval(packet); +#if DEBUG_BLE + Serial.printf("- LE Connection 0x%04x: connected - connection interval %u.%02u ms, latency %u\r\n", con_handle, conn_interval * 125 / 100, + 25 * (conn_interval & 3), hci_subevent_le_connection_complete_get_conn_latency(packet)); + + // request min con interval 15 ms for iOS 11+ + Serial.printf("- LE Connection 0x%04x: request 15 ms connection interval\r\n", con_handle); +#endif /* DEBUG_BLE */ + gap_request_connection_parameter_update(con_handle, 12, 12, 0, 0x0048); + break; + case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE: + // print connection parameters (without using float operations) + con_handle = hci_subevent_le_connection_update_complete_get_connection_handle(packet); + conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet); +#if DEBUG_BLE + Serial.printf("- LE Connection 0x%04x: connection update - connection interval %u.%02u ms, latency %u\r\n", con_handle, conn_interval * 125 / 100, + 25 * (conn_interval & 3), hci_subevent_le_connection_update_complete_get_conn_latency(packet)); +#endif /* DEBUG_BLE */ + break; + case HCI_SUBEVENT_LE_DATA_LENGTH_CHANGE: + con_handle = hci_subevent_le_data_length_change_get_connection_handle(packet); +#if DEBUG_BLE + Serial.printf("- LE Connection 0x%04x: data length change - max %u bytes per packet\r\n", con_handle, + hci_subevent_le_data_length_change_get_max_tx_octets(packet)); +#endif /* DEBUG_BLE */ + break; + case HCI_SUBEVENT_LE_PHY_UPDATE_COMPLETE: + con_handle = hci_subevent_le_phy_update_complete_get_connection_handle(packet); + Serial.printf("- LE Connection 0x%04x: PHY update - using LE %s PHY now\r\n", con_handle, + phy_names[hci_subevent_le_phy_update_complete_get_tx_phy(packet)]); + break; + default: + break; + } + break; + + default: + break; + } +} + +static void att_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(channel); + UNUSED(size); + + int mtu; + le_streamer_connection_t * context; + switch (packet_type) { + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + case ATT_EVENT_CONNECTED: + // setup new + context = connection_for_conn_handle(HCI_CON_HANDLE_INVALID); + if (!context) break; + context->counter = 'A'; + context->connection_handle = att_event_connected_get_handle(packet); + context->test_data_len = btstack_min(att_server_get_mtu(context->connection_handle) - 3, sizeof(context->test_data)); +#if DEBUG_BLE + Serial.printf("%c: ATT connected, handle %04x, test data len %u\r\n", context->name, context->connection_handle, context->test_data_len); +#endif /* DEBUG_BLE */ + break; + case ATT_EVENT_MTU_EXCHANGE_COMPLETE: + mtu = att_event_mtu_exchange_complete_get_MTU(packet) - 3; + context = connection_for_conn_handle(att_event_mtu_exchange_complete_get_handle(packet)); + if (!context) break; + context->test_data_len = btstack_min(mtu - 3, sizeof(context->test_data)); +#if DEBUG_BLE + Serial.printf("%c: ATT MTU = %u => use test data of len %u\r\n", context->name, mtu, context->test_data_len); +#endif /* DEBUG_BLE */ + break; + case ATT_EVENT_CAN_SEND_NOW: + streamer(); + break; + case ATT_EVENT_DISCONNECTED: + context = connection_for_conn_handle(att_event_disconnected_get_handle(packet)); + if (!context) break; + // free connection +#if DEBUG_BLE + Serial.printf("%c: ATT disconnected, handle %04x\r\n", context->name, context->connection_handle); +#endif /* DEBUG_BLE */ + context->le_notification_enabled = 0; + context->connection_handle = HCI_CON_HANDLE_INVALID; + break; + default: + break; + } + break; + default: + break; + } +} + +static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){ + UNUSED(offset); + +#if DEBUG_BLE + Serial.printf("att_write_callback att_handle %04x, transaction mode %u size %u offset %u\r\n", att_handle, transaction_mode, buffer_size, offset); +#endif /* DEBUG_BLE */ + if (transaction_mode != ATT_TRANSACTION_MODE_NONE) return 0; + le_streamer_connection_t * context = connection_for_conn_handle(con_handle); + switch(att_handle){ + case ATT_CHARACTERISTIC_FFE1_01_CLIENT_CONFIGURATION_HANDLE: + context->le_notification_enabled = little_endian_read_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION; + //Serial.printf("%c: Notifications enabled %u\r\n", context->name, context->le_notification_enabled); + if (context->le_notification_enabled){ + switch (att_handle){ + case ATT_CHARACTERISTIC_FFE1_01_CLIENT_CONFIGURATION_HANDLE: + context->value_handle = ATT_CHARACTERISTIC_FFE1_01_VALUE_HANDLE; + break; + default: + break; + } + att_server_request_can_send_now_event(context->connection_handle); + } + test_reset(context); + break; + case ATT_CHARACTERISTIC_FFE1_01_VALUE_HANDLE: +#if DEBUG_BLE + Serial.printf("Write to 0x%04x, len %u offset %u\r\n", att_handle, buffer_size, offset); +#endif /* DEBUG_BLE */ + if (buffer_size > 0 && offset == 0) { + size_t size = BLE_FIFO_RX.availableForStore(); + size = (size < buffer_size) ? size : buffer_size; + for (size_t i = 0; i < size; i++) { + BLE_FIFO_RX.store_char(buffer[i]); + } + } + break; + default: + Serial.printf("Write to 0x%04x, len %u\r\n", att_handle, buffer_size); + break; + } + return 0; +} + +#define ATT_VALUE_MAX_LEN 50 +#define ATT_NUM_ATTRIBUTES 10 + +typedef struct { + uint16_t handle; + uint16_t len; + uint8_t value[ATT_VALUE_MAX_LEN]; +} attribute_t; + +static attribute_t att_attributes[ATT_NUM_ATTRIBUTES]; + +// handle == 0 finds free attribute +static int att_attribute_for_handle(uint16_t aHandle){ + int i; + for (i=0;igetChipId() & 0x00FFFFFFU, HEX); + + switch (settings->bluetooth) + { + case BLUETOOTH_SPP: + { + mutex_init(&_mutex); + _overflow = false; + + _queue = new uint8_t[_fifoSize]; + _writer = 0; + _reader = 0; + + // register for HCI events + _hci_event_callback_registration.callback = &hci_spp_packet_handler; + hci_add_event_handler(&_hci_event_callback_registration); + + l2cap_init(); + +#ifdef ENABLE_BLE + // Initialize LE Security Manager. Needed for cross-transport key derivation + sm_init(); +#endif + + rfcomm_init(); + rfcomm_register_service(hci_spp_packet_handler, RFCOMM_SERVER_CHANNEL, 0xffff); // reserved channel, mtu limited by l2cap + + // init SDP, create record for SPP and register with SDP + sdp_init(); + bzero(_spp_service_buffer, sizeof(_spp_service_buffer)); + spp_create_sdp_record(_spp_service_buffer, 0x10001, RFCOMM_SERVER_CHANNEL, "CYW43_SPP"); + sdp_register_service(_spp_service_buffer); + + gap_discoverable_control(1); + gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO); + gap_set_local_name(BT_name.c_str()); + + hci_power_control(HCI_POWER_ON); + + _running = true; + } + break; + case BLUETOOTH_LE_HM10_SERIAL: + { + mutex_init(&_mutex); + + BT_name += "-LE"; + _buildAdvData(BT_name.c_str()); + _buildAttdb(BT_name.c_str()); + + l2cap_init(); +// l2cap_set_max_le_mtu(26); + + // setup SM: Display only + sm_init(); + + // setup ATT server + att_server_init(_attdb, NULL, att_write_callback); + + // Setup battery service + battery_service_server_init((uint8_t) Battery_charge()); + + static char SerialNum[9]; + snprintf(SerialNum, sizeof(SerialNum), "%08X", SoC->getChipId()); + static char Hardware[9]; + snprintf(Hardware, sizeof(Hardware), "%08X", hw_info.revision); + + const char *Firmware = "Arduino RP2040 " ARDUINO_PICO_VERSION_STR; + + // Setup device information service + device_information_service_server_init(); + device_information_service_server_set_manufacturer_name(RP2040_Device_Manufacturer); + device_information_service_server_set_model_number(RP2040_Device_Model); + device_information_service_server_set_serial_number(SerialNum); + device_information_service_server_set_hardware_revision(Hardware); + device_information_service_server_set_firmware_revision(Firmware); + device_information_service_server_set_software_revision(SOFTRF_FIRMWARE_VERSION); + + // register for HCI events + _hci_event_callback_registration.callback = &hci_le_packet_handler; + hci_add_event_handler(&_hci_event_callback_registration); + + att_attributes_init(); + + // register for ATT events + att_server_register_packet_handler(att_packet_handler); + + // setup advertisements + uint16_t adv_int_min = 0x0030; + uint16_t adv_int_max = 0x0030; + uint8_t adv_type = 0; + bd_addr_t null_addr; + memset(null_addr, 0, 6); + gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00); + gap_advertisements_set_data(_advDataLen, _advData); + gap_advertisements_enable(1); + + // init client state + init_connections(); + + hci_power_control(HCI_POWER_ON); + + BLE_Aux_Tx_TimeMarker = millis(); + _running = true; + } + break; + case BLUETOOTH_A2DP_SOURCE: +#if defined(ENABLE_BT_VOICE) + A2DPSource.setVolume(50); + A2DPSource.begin(in); +#endif + break; + case BLUETOOTH_NONE: + default: + break; + } +} + +static void CYW43_Bluetooth_loop() +{ + switch (settings->bluetooth) + { + case BLUETOOTH_LE_HM10_SERIAL: + if (_running && (millis() - BLE_Aux_Tx_TimeMarker > 100)) { + if (BLE_FIFO_TX.available() > 0) { + streamer(); + } + BLE_Aux_Tx_TimeMarker = millis(); + } + + if (isTimeToBattery()) { + battery_service_server_set_battery_value((uint8_t) Battery_charge()); + } + break; + case BLUETOOTH_NONE: + case BLUETOOTH_SPP: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } +} + +static void CYW43_Bluetooth_fini() +{ + switch (settings->bluetooth) + { + case BLUETOOTH_SPP: + case BLUETOOTH_LE_HM10_SERIAL: + { + if (!_running) { + return; + } + _running = false; + + hci_power_control(HCI_POWER_OFF); + lockBluetooth(); + if (_queue != NULL) delete[] _queue; + unlockBluetooth(); + } + break; + case BLUETOOTH_A2DP_SOURCE: + case BLUETOOTH_NONE: + default: + break; + } +} + +static int CYW43_Bluetooth_available() +{ + int rval = 0; + + switch (settings->bluetooth) + { + case BLUETOOTH_SPP: + { + CoreMutex m(&_mutex); + if (_running && m) { + rval = (_fifoSize + _writer - _reader) % _fifoSize; + } + } + break; + case BLUETOOTH_LE_HM10_SERIAL: + rval = BLE_FIFO_RX.available(); + break; + case BLUETOOTH_NONE: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } + + return rval; +} + +static int CYW43_Bluetooth_read() +{ + int rval = -1; + + switch (settings->bluetooth) + { + case BLUETOOTH_SPP: + { + CoreMutex m(&_mutex); + if (_running && m && _writer != _reader) { + auto ret = _queue[_reader]; + asm volatile("" ::: "memory"); // Ensure the value is read before advancing + auto next_reader = (_reader + 1) % _fifoSize; + asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly + _reader = next_reader; + rval = ret; + } + } + break; + case BLUETOOTH_LE_HM10_SERIAL: + rval = BLE_FIFO_RX.read_char(); + break; + case BLUETOOTH_NONE: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } + + return rval; +} + +static size_t CYW43_Bluetooth_write(const uint8_t *buffer, size_t size) +{ + size_t rval = size; + + switch (settings->bluetooth) + { + case BLUETOOTH_SPP: + { + CoreMutex m(&_mutex); + if (!_running || !m || !_connected || !size) { + return 0; + } + _writeBuff = buffer; + _writeLen = size; + lockBluetooth(); + rfcomm_request_can_send_now_event(_channelID); + unlockBluetooth(); + while (_connected && _writeLen) { + /* noop busy wait */ + } + } + break; + case BLUETOOTH_LE_HM10_SERIAL: + { + size_t avail = BLE_FIFO_TX.availableForStore(); + if (size > avail) { + rval = avail; + } + for (size_t i = 0; i < rval; i++) { + BLE_FIFO_TX.store_char(buffer[i]); + } + } + break; + case BLUETOOTH_NONE: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } + + return rval; +} + +IODev_ops_t CYW43_Bluetooth_ops = { + "CYW43 Bluetooth", + CYW43_Bluetooth_setup, + CYW43_Bluetooth_loop, + CYW43_Bluetooth_fini, + CYW43_Bluetooth_available, + CYW43_Bluetooth_read, + CYW43_Bluetooth_write +}; +#endif /* EXCLUDE_BLUETOOTH */ +#endif /* ARDUINO_ARCH_RP2040 */ diff --git a/software/firmware/source/SoftRF/src/platform/bluetooth/BTstack.h b/software/firmware/source/SoftRF/src/platform/bluetooth/BTstack.h new file mode 100644 index 000000000..51b4d3cde --- /dev/null +++ b/software/firmware/source/SoftRF/src/platform/bluetooth/BTstack.h @@ -0,0 +1,64 @@ +/* + * BluetoothHelper.h + * Copyright (C) 2018-2024 Linar Yusupov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// +// list service handle ranges +// +#define ATT_SERVICE_GAP_SERVICE_START_HANDLE 0x0001 +#define ATT_SERVICE_GAP_SERVICE_END_HANDLE 0x0003 +#define ATT_SERVICE_GAP_SERVICE_01_START_HANDLE 0x0001 +#define ATT_SERVICE_GAP_SERVICE_01_END_HANDLE 0x0003 +#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE_START_HANDLE 0x0004 +#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE_END_HANDLE 0x0007 +#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE_01_START_HANDLE 0x0004 +#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE_01_END_HANDLE 0x0007 +#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION_START_HANDLE 0x0008 +#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION_END_HANDLE 0x001a +#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION_01_START_HANDLE 0x0008 +#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION_01_END_HANDLE 0x001a +#define ATT_SERVICE_FFE0_START_HANDLE 0x001b +#define ATT_SERVICE_FFE0_END_HANDLE 0x001f +#define ATT_SERVICE_FFE0_01_START_HANDLE 0x001b +#define ATT_SERVICE_FFE0_01_END_HANDLE 0x001f + +// +// list mapping between characteristics and handles +// +#define ATT_CHARACTERISTIC_GAP_DEVICE_NAME_01_VALUE_HANDLE 0x0003 +#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_01_VALUE_HANDLE 0x0006 +#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_01_CLIENT_CONFIGURATION_HANDLE 0x0007 +#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING_01_VALUE_HANDLE 0x000a +#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING_01_VALUE_HANDLE 0x000c +#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING_01_VALUE_HANDLE 0x000e +#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING_01_VALUE_HANDLE 0x0010 +#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING_01_VALUE_HANDLE 0x0012 +#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING_01_VALUE_HANDLE 0x0014 +#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID_01_VALUE_HANDLE 0x0016 +#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST_01_VALUE_HANDLE 0x0018 +#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID_01_VALUE_HANDLE 0x001a +#define ATT_CHARACTERISTIC_FFE1_01_VALUE_HANDLE 0x001d +#define ATT_CHARACTERISTIC_FFE1_01_CLIENT_CONFIGURATION_HANDLE 0x001e +#define ATT_CHARACTERISTIC_FFE1_01_USER_DESCRIPTION_HANDLE 0x001f + +/* (FLAA x MAX_TRACKING_OBJECTS + GNGGA + GNRMC + FLAU) x 80 symbols */ +#define BLE_FIFO_TX_SIZE 1024 +#define BLE_FIFO_RX_SIZE 256 + +#define BLE_MAX_WRITE_CHUNK_SIZE 20 + +extern IODev_ops_t CYW43_Bluetooth_ops; diff --git a/software/firmware/source/SoftRF/src/platform/bluetooth/Bluedroid.cpp b/software/firmware/source/SoftRF/src/platform/bluetooth/Bluedroid.cpp new file mode 100644 index 000000000..72e620b36 --- /dev/null +++ b/software/firmware/source/SoftRF/src/platform/bluetooth/Bluedroid.cpp @@ -0,0 +1,442 @@ +/* + * BluetoothHelper.cpp + * Copyright (C) 2018-2024 Linar Yusupov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#if defined(ESP32) +#include "sdkconfig.h" +#endif + +#if defined(ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) + +#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) +#error Bluetooth is not enabled! +#endif + +/* + BLE code is based on Neil Kolban example for IDF: + https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + HM-10 emulation and adaptation for SoftRF is done by Linar Yusupov. +*/ +#include +#include +#include +#include + +#include "esp_gap_bt_api.h" + +#include "../../system/SoC.h" +#include "../../driver/EEPROM.h" +#include "../../driver/Bluetooth.h" +#include "../../driver/WiFi.h" +#include "../../driver/Battery.h" + +#include + +BLEServer* pServer = NULL; +BLECharacteristic* pUARTCharacteristic = NULL; +BLECharacteristic* pBATCharacteristic = NULL; + +BLECharacteristic* pModelCharacteristic = NULL; +BLECharacteristic* pSerialCharacteristic = NULL; +BLECharacteristic* pFirmwareCharacteristic = NULL; +BLECharacteristic* pHardwareCharacteristic = NULL; +BLECharacteristic* pSoftwareCharacteristic = NULL; +BLECharacteristic* pManufacturerCharacteristic = NULL; + +bool deviceConnected = false; +bool oldDeviceConnected = false; + +#if defined(USE_BLE_MIDI) +BLECharacteristic* pMIDICharacteristic = NULL; +#endif /* USE_BLE_MIDI */ + +cbuf *BLE_FIFO_RX, *BLE_FIFO_TX; + +#if defined(CONFIG_IDF_TARGET_ESP32) +#include +BluetoothSerial SerialBT; +#endif /* CONFIG_IDF_TARGET_ESP32 */ + +#if defined(ENABLE_BT_VOICE) +#include "BluetoothA2DPSource.h" +#include "piano16bit.h" + +BluetoothA2DPSource a2dp_source; +SoundData *sound_data = new OneChannelSoundData((int16_t*)piano16bit_raw, piano16bit_raw_len/2); +#endif /* ENABLE_BT_VOICE */ + +String BT_name = HOSTNAME; + +static unsigned long BLE_Notify_TimeMarker = 0; +static unsigned long BLE_Advertising_TimeMarker = 0; + +BLEDescriptor UserDescriptor(BLEUUID((uint16_t)0x2901)); + +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + BLE_Advertising_TimeMarker = millis(); + } +}; + +class UARTCallbacks: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic *pUARTCharacteristic) { +#if defined(ESP_IDF_VERSION_MAJOR) && ESP_IDF_VERSION_MAJOR>=5 + String rxValue = pUARTCharacteristic->getValue(); +#else + std::string rxValue = pUARTCharacteristic->getValue(); +#endif /* ESP_IDF_VERSION_MAJOR */ + + if (rxValue.length() > 0) { + BLE_FIFO_RX->write(rxValue.c_str(), + (BLE_FIFO_RX->room() > rxValue.length() ? + rxValue.length() : BLE_FIFO_RX->room())); + } + } +}; + +static void ESP32_Bluetooth_setup() +{ + BT_name += "-"; + BT_name += String(SoC->getChipId() & 0x00FFFFFFU, HEX); + + switch (settings->bluetooth) + { +#if defined(CONFIG_IDF_TARGET_ESP32) + case BLUETOOTH_SPP: + { + esp_bt_controller_mem_release(ESP_BT_MODE_BLE); + + SerialBT.begin(BT_name.c_str()); + } + break; +#endif /* CONFIG_IDF_TARGET_ESP32 */ + case BLUETOOTH_LE_HM10_SERIAL: + { + BLE_FIFO_RX = new cbuf(BLE_FIFO_RX_SIZE); + BLE_FIFO_TX = new cbuf(BLE_FIFO_TX_SIZE); + +#if defined(CONFIG_IDF_TARGET_ESP32) + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); +#endif /* CONFIG_IDF_TARGET_ESP32 */ + + // Create the BLE Device + BLEDevice::init((BT_name+"-LE").c_str()); + + /* + * Set the MTU of the packets sent, + * maximum is 500, Apple needs 23 apparently. + */ + // BLEDevice::setMTU(23); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(BLEUUID(UART_SERVICE_UUID16)); + + // Create a BLE Characteristic + pUARTCharacteristic = pService->createCharacteristic( + BLEUUID(UART_CHARACTERISTIC_UUID16), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE_NR + ); + + UserDescriptor.setValue("HMSoft"); + pUARTCharacteristic->addDescriptor(&UserDescriptor); + pUARTCharacteristic->addDescriptor(new BLE2902()); + + pUARTCharacteristic->setCallbacks(new UARTCallbacks()); + + // Start the service + pService->start(); + + // Create the BLE Service + pService = pServer->createService(BLEUUID(UUID16_SVC_BATTERY)); + + // Create a BLE Characteristic + pBATCharacteristic = pService->createCharacteristic( + BLEUUID(UUID16_CHR_BATTERY_LEVEL), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_NOTIFY + ); + pBATCharacteristic->addDescriptor(new BLE2902()); + + // Start the service + pService->start(); + + // Create the BLE Service + pService = pServer->createService(BLEUUID(UUID16_SVC_DEVICE_INFORMATION)); + + // Create BLE Characteristics + pModelCharacteristic = pService->createCharacteristic( + BLEUUID(UUID16_CHR_MODEL_NUMBER_STRING), + BLECharacteristic::PROPERTY_READ + ); + pSerialCharacteristic = pService->createCharacteristic( + BLEUUID(UUID16_CHR_SERIAL_NUMBER_STRING), + BLECharacteristic::PROPERTY_READ + ); + pFirmwareCharacteristic = pService->createCharacteristic( + BLEUUID(UUID16_CHR_FIRMWARE_REVISION_STRING), + BLECharacteristic::PROPERTY_READ + ); + pHardwareCharacteristic = pService->createCharacteristic( + BLEUUID(UUID16_CHR_HARDWARE_REVISION_STRING), + BLECharacteristic::PROPERTY_READ + ); + pSoftwareCharacteristic = pService->createCharacteristic( + BLEUUID(UUID16_CHR_SOFTWARE_REVISION_STRING), + BLECharacteristic::PROPERTY_READ + ); + pManufacturerCharacteristic = pService->createCharacteristic( + BLEUUID(UUID16_CHR_MANUFACTURER_NAME_STRING), + BLECharacteristic::PROPERTY_READ + ); + + const char *Model = hw_info.model == SOFTRF_MODEL_STANDALONE ? "Standalone Edition" : + hw_info.model == SOFTRF_MODEL_PRIME_MK2 ? "Prime Mark II" : + hw_info.model == SOFTRF_MODEL_PRIME_MK3 ? "Prime Mark III" : + hw_info.model == SOFTRF_MODEL_HAM ? "Ham Edition" : + hw_info.model == SOFTRF_MODEL_MIDI ? "Midi Edition" : + "Unknown"; + char SerialNum[9]; + snprintf(SerialNum, sizeof(SerialNum), "%08X", SoC->getChipId()); + + const char *Firmware = "Arduino ESP32 " ARDUINO_ESP32_RELEASE; + + char Hardware[9]; + snprintf(Hardware, sizeof(Hardware), "%08X", hw_info.revision); + + const char *Manufacturer = SOFTRF_IDENT; + const char *Software = SOFTRF_FIRMWARE_VERSION; + + pModelCharacteristic-> setValue((uint8_t *) Model, strlen(Model)); + pSerialCharacteristic-> setValue((uint8_t *) SerialNum, strlen(SerialNum)); + pFirmwareCharacteristic-> setValue((uint8_t *) Firmware, strlen(Firmware)); + pHardwareCharacteristic-> setValue((uint8_t *) Hardware, strlen(Hardware)); + pSoftwareCharacteristic-> setValue((uint8_t *) Software, strlen(Software)); + pManufacturerCharacteristic->setValue((uint8_t *) Manufacturer, strlen(Manufacturer)); + + // Start the service + pService->start(); + +#if defined(USE_BLE_MIDI) + // Create the BLE Service + pService = pServer->createService(BLEUUID(MIDI_SERVICE_UUID)); + + // Create a BLE Characteristic + pMIDICharacteristic = pService->createCharacteristic( + BLEUUID(MIDI_CHARACTERISTIC_UUID), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE_NR + ); + + // Create a BLE Descriptor + pMIDICharacteristic->addDescriptor(new BLE2902()); + + // Start the service + pService->start(); +#endif /* USE_BLE_MIDI */ + + // Start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); +#if 0 + pAdvertising->addServiceUUID(BLEUUID(UART_SERVICE_UUID16)); + pAdvertising->addServiceUUID(BLEUUID(UUID16_SVC_BATTERY)); +#if defined(USE_BLE_MIDI) + pAdvertising->addServiceUUID(BLEUUID(MIDI_SERVICE_UUID)); +#endif /* USE_BLE_MIDI */ +#else + /* work around https://github.com/espressif/arduino-esp32/issues/6750 */ + BLEAdvertisementData BLEAdvData; + BLEAdvData.setFlags(0x06); + BLEAdvData.setCompleteServices(BLEUUID(UART_SERVICE_UUID16)); + BLEAdvData.setCompleteServices(BLEUUID(UUID16_SVC_BATTERY)); +#if defined(USE_BLE_MIDI) + BLEAdvData.setCompleteServices(BLEUUID(MIDI_SERVICE_UUID)); +#endif /* USE_BLE_MIDI */ + pAdvertising->setAdvertisementData(BLEAdvData); +#endif + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue + pAdvertising->setMaxPreferred(0x12); + BLEDevice::startAdvertising(); + + BLE_Advertising_TimeMarker = millis(); + } + break; + case BLUETOOTH_A2DP_SOURCE: +#if defined(ENABLE_BT_VOICE) + //a2dp_source.set_auto_reconnect(false); + a2dp_source.start("BT SPEAKER"); + a2dp_source.set_volume(100); + a2dp_source.write_data(sound_data); +#endif + break; + case BLUETOOTH_NONE: + default: + break; + } +} + +static void ESP32_Bluetooth_loop() +{ + switch (settings->bluetooth) + { + case BLUETOOTH_LE_HM10_SERIAL: + { + // notify changed value + // bluetooth stack will go into congestion, if too many packets are sent + if (deviceConnected && (millis() - BLE_Notify_TimeMarker > 10)) { /* < 18000 baud */ + + uint8_t chunk[BLE_MAX_WRITE_CHUNK_SIZE]; + size_t size = BLE_FIFO_TX->available(); + size = size < BLE_MAX_WRITE_CHUNK_SIZE ? size : BLE_MAX_WRITE_CHUNK_SIZE; + + if (size > 0) { + BLE_FIFO_TX->read((char *) chunk, size); + + pUARTCharacteristic->setValue(chunk, size); + pUARTCharacteristic->notify(); + } + + BLE_Notify_TimeMarker = millis(); + } + // disconnecting + if (!deviceConnected && oldDeviceConnected && (millis() - BLE_Advertising_TimeMarker > 500) ) { + // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + oldDeviceConnected = deviceConnected; + BLE_Advertising_TimeMarker = millis(); + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } + if (deviceConnected && isTimeToBattery()) { + uint8_t battery_level = Battery_charge(); + + pBATCharacteristic->setValue(&battery_level, 1); + pBATCharacteristic->notify(); + } + } + break; + case BLUETOOTH_NONE: + case BLUETOOTH_SPP: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } +} + +static void ESP32_Bluetooth_fini() +{ + /* TBD */ +} + +static int ESP32_Bluetooth_available() +{ + int rval = 0; + + switch (settings->bluetooth) + { +#if defined(CONFIG_IDF_TARGET_ESP32) + case BLUETOOTH_SPP: + rval = SerialBT.available(); + break; +#endif /* CONFIG_IDF_TARGET_ESP32 */ + case BLUETOOTH_LE_HM10_SERIAL: + rval = BLE_FIFO_RX->available(); + break; + case BLUETOOTH_NONE: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } + + return rval; +} + +static int ESP32_Bluetooth_read() +{ + int rval = -1; + + switch (settings->bluetooth) + { +#if defined(CONFIG_IDF_TARGET_ESP32) + case BLUETOOTH_SPP: + rval = SerialBT.read(); + break; +#endif /* CONFIG_IDF_TARGET_ESP32 */ + case BLUETOOTH_LE_HM10_SERIAL: + rval = BLE_FIFO_RX->read(); + break; + case BLUETOOTH_NONE: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } + + return rval; +} + +static size_t ESP32_Bluetooth_write(const uint8_t *buffer, size_t size) +{ + size_t rval = size; + + switch (settings->bluetooth) + { +#if defined(CONFIG_IDF_TARGET_ESP32) + case BLUETOOTH_SPP: + rval = SerialBT.write(buffer, size); + break; +#endif /* CONFIG_IDF_TARGET_ESP32 */ + case BLUETOOTH_LE_HM10_SERIAL: + rval = BLE_FIFO_TX->write((char *) buffer, + (BLE_FIFO_TX->room() > size ? size : BLE_FIFO_TX->room())); + break; + case BLUETOOTH_NONE: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } + + return rval; +} + +IODev_ops_t ESP32_Bluetooth_ops = { + "ESP32 Bluetooth", + ESP32_Bluetooth_setup, + ESP32_Bluetooth_loop, + ESP32_Bluetooth_fini, + ESP32_Bluetooth_available, + ESP32_Bluetooth_read, + ESP32_Bluetooth_write +}; + +#endif /* ESP32 */ diff --git a/software/firmware/source/SoftRF/src/platform/bluetooth/Bluedroid.h b/software/firmware/source/SoftRF/src/platform/bluetooth/Bluedroid.h new file mode 100644 index 000000000..a5586e204 --- /dev/null +++ b/software/firmware/source/SoftRF/src/platform/bluetooth/Bluedroid.h @@ -0,0 +1,50 @@ +/* + * BluetoothHelper.h + * Copyright (C) 2018-2024 Linar Yusupov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define UART_SERVICE_UUID16 ((uint16_t) 0xFFE0) +#define UART_CHARACTERISTIC_UUID16 ((uint16_t) 0xFFE1) +#define UART_SERVICE_UUID128 "0000ffe0-0000-1000-8000-00805f9b34fb" +#define UART_CHARACTERISTIC_UUID128 "0000ffe1-0000-1000-8000-00805f9b34fb" + +#define MIDI_SERVICE_UUID "03b80e5a-ede8-4b33-a751-6ce34ec4c700" +#define MIDI_CHARACTERISTIC_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3" + +#define UUID16_SVC_BATTERY ((uint16_t) 0x180F) +#define UUID16_CHR_BATTERY_LEVEL ((uint16_t) 0x2A19) + +#define UUID16_SVC_DEVICE_INFORMATION ((uint16_t) 0x180A) +#define UUID16_CHR_MODEL_NUMBER_STRING ((uint16_t) 0x2A24) +#define UUID16_CHR_SERIAL_NUMBER_STRING ((uint16_t) 0x2A25) +#define UUID16_CHR_FIRMWARE_REVISION_STRING ((uint16_t) 0x2A26) +#define UUID16_CHR_HARDWARE_REVISION_STRING ((uint16_t) 0x2A27) +#define UUID16_CHR_SOFTWARE_REVISION_STRING ((uint16_t) 0x2A28) +#define UUID16_CHR_MANUFACTURER_NAME_STRING ((uint16_t) 0x2A29) + +#define SENSBOX_SERVICE_UUID "aba27100-143b-4b81-a444-edcd0000f020" +#define NAVIGATION_CHARACTERISTIC_UUID "aba27100-143b-4b81-a444-edcd0000f022" +#define MOVEMENT_CHARACTERISTIC_UUID "aba27100-143b-4b81-a444-edcd0000f023" +#define GPS2_CHARACTERISTIC_UUID "aba27100-143b-4b81-a444-edcd0000f024" +#define SYSTEM_CHARACTERISTIC_UUID "aba27100-143b-4b81-a444-edcd0000f025" + +/* (FLAA x MAX_TRACKING_OBJECTS + GNGGA + GNRMC + FLAU) x 80 symbols */ +#define BLE_FIFO_TX_SIZE 1024 +#define BLE_FIFO_RX_SIZE 256 + +#define BLE_MAX_WRITE_CHUNK_SIZE 20 + +extern IODev_ops_t ESP32_Bluetooth_ops; diff --git a/software/firmware/source/SoftRF/src/platform/bluetooth/Bluefruit.cpp b/software/firmware/source/SoftRF/src/platform/bluetooth/Bluefruit.cpp new file mode 100644 index 000000000..adf76c2e8 --- /dev/null +++ b/software/firmware/source/SoftRF/src/platform/bluetooth/Bluefruit.cpp @@ -0,0 +1,557 @@ +/* + * BluetoothHelper.cpp + * Copyright (C) 2018-2024 Linar Yusupov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#if defined(ARDUINO_ARCH_NRF52) + +#include "../../system/SoC.h" +#include "../../driver/Bluetooth.h" + +#include +#include +#include +#include +#include +#if defined(USE_BLE_MIDI) +#include +#endif /* USE_BLE_MIDI */ + +#include "../../driver/WiFi.h" +#include "../../driver/Battery.h" +#include "../../driver/GNSS.h" +#include "../../driver/RF.h" +#include "../../protocol/radio/Legacy.h" +#include "../../driver/Baro.h" +#include "../../driver/EEPROM.h" +#include "../../driver/Sound.h" +#include "../../protocol/data/NMEA.h" +#include "../../protocol/data/GDL90.h" +#include "../../protocol/data/D1090.h" + +/* + * SensorBox Serivce: aba27100-143b-4b81-a444-edcd0000f020 + * Navigation : aba27100-143b-4b81-a444-edcd0000f022 + * Movement : aba27100-143b-4b81-a444-edcd0000f023 + * GPS2 : aba27100-143b-4b81-a444-edcd0000f024 + * System : aba27100-143b-4b81-a444-edcd0000f025 + */ + +const uint8_t SENSBOX_UUID_SERVICE[] = +{ + 0x20, 0xF0, 0x00, 0x00, 0xCD, 0xED, 0x44, 0xA4, + 0x81, 0x4B, 0x3B, 0x14, 0x00, 0x71, 0xA2, 0xAB, +}; + +const uint8_t SENSBOX_UUID_NAVIGATION[] = +{ + 0x22, 0xF0, 0x00, 0x00, 0xCD, 0xED, 0x44, 0xA4, + 0x81, 0x4B, 0x3B, 0x14, 0x00, 0x71, 0xA2, 0xAB, +}; + +const uint8_t SENSBOX_UUID_MOVEMENT[] = +{ + 0x23, 0xF0, 0x00, 0x00, 0xCD, 0xED, 0x44, 0xA4, + 0x81, 0x4B, 0x3B, 0x14, 0x00, 0x71, 0xA2, 0xAB, +}; + +const uint8_t SENSBOX_UUID_GPS2[] = +{ + 0x24, 0xF0, 0x00, 0x00, 0xCD, 0xED, 0x44, 0xA4, + 0x81, 0x4B, 0x3B, 0x14, 0x00, 0x71, 0xA2, 0xAB, +}; + +const uint8_t SENSBOX_UUID_SYSTEM[] = +{ + 0x25, 0xF0, 0x00, 0x00, 0xCD, 0xED, 0x44, 0xA4, + 0x81, 0x4B, 0x3B, 0x14, 0x00, 0x71, 0xA2, 0xAB, +}; + +BLESensBox::BLESensBox(void) : + BLEService (SENSBOX_UUID_SERVICE), + _sensbox_nav (SENSBOX_UUID_NAVIGATION), + _sensbox_move(SENSBOX_UUID_MOVEMENT), + _sensbox_gps2(SENSBOX_UUID_GPS2), + _sensbox_sys (SENSBOX_UUID_SYSTEM) +{ + +} + +err_t BLESensBox::begin(void) +{ + VERIFY_STATUS( BLEService::begin() ); + + _sensbox_nav.setProperties(CHR_PROPS_NOTIFY); + _sensbox_nav.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + _sensbox_nav.setFixedLen(sizeof(sensbox_navigation_t)); + _sensbox_move.setProperties(CHR_PROPS_NOTIFY); + _sensbox_move.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + _sensbox_move.setFixedLen(sizeof(sensbox_movement_t)); + _sensbox_gps2.setProperties(CHR_PROPS_NOTIFY); + _sensbox_gps2.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + _sensbox_gps2.setFixedLen(sizeof(sensbox_gps2_t)); + _sensbox_sys.setProperties(CHR_PROPS_NOTIFY); + _sensbox_sys.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + _sensbox_sys.setFixedLen(sizeof(sensbox_system_t)); + VERIFY_STATUS( _sensbox_nav.begin() ); + VERIFY_STATUS( _sensbox_move.begin() ); + VERIFY_STATUS( _sensbox_gps2.begin() ); + VERIFY_STATUS( _sensbox_sys.begin() ); + + return ERROR_NONE; +} + +bool BLESensBox::notify_nav(uint8_t status) +{ + if (!_sensbox_nav.notifyEnabled(Bluefruit.connHandle())) + return false; + + sensbox_navigation_t data = {0}; + + data.timestamp = ThisAircraft.timestamp; + data.lat = (int32_t) (ThisAircraft.latitude * 10000000); + data.lon = (int32_t) (ThisAircraft.longitude * 10000000); + data.gnss_alt = (int16_t) ThisAircraft.altitude; + data.pres_alt = (int16_t) ThisAircraft.pressure_altitude; + data.status = status; + + return _sensbox_nav.notify(&data, sizeof(sensbox_navigation_t)) > 0; +} + +bool BLESensBox::notify_move(uint8_t status) +{ + if (!_sensbox_move.notifyEnabled(Bluefruit.connHandle())) + return false; + + sensbox_movement_t data = {0}; + + data.pres_alt = (int32_t) (ThisAircraft.pressure_altitude * 100); + data.vario = (int16_t) ((ThisAircraft.vs * 10) / (_GPS_FEET_PER_METER * 6)); + data.gs = (int16_t) (ThisAircraft.speed * _GPS_MPS_PER_KNOT * 10); + data.cog = (int16_t) (ThisAircraft.course * 10); + data.status = status; + + return _sensbox_move.notify(&data, sizeof(sensbox_movement_t)) > 0; +} + +bool BLESensBox::notify_gps2(uint8_t status) +{ + if (!_sensbox_gps2.notifyEnabled(Bluefruit.connHandle())) + return false; + + sensbox_gps2_t data = {0}; + + data.sats = (uint8_t) gnss.satellites.value(); + data.status = status; + + return _sensbox_gps2.notify(&data, sizeof(sensbox_gps2_t)) > 0; +} + +bool BLESensBox::notify_sys(uint8_t status) +{ + if (!_sensbox_sys.notifyEnabled(Bluefruit.connHandle())) + return false; + + sensbox_system_t data = {0}; + + data.battery = (uint8_t) Battery_charge(); + data.temp = (int16_t) (Baro_temperature() * 10); + data.status = status; + + return _sensbox_sys.notify(&data, sizeof(sensbox_system_t)) > 0; +} + +static unsigned long BLE_Notify_TimeMarker = 0; +static unsigned long BLE_SensBox_TimeMarker = 0; + +/********************************************************************* + This is an example for our nRF52 based Bluefruit LE modules + + Pick one up today in the adafruit shop! + + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + MIT license, check LICENSE for more information + All text above, and the splash screen below must be included in + any redistribution +*********************************************************************/ + +// BLE Service +BLEDfu bledfu; // OTA DFU service +BLEDis bledis; // device information +BLEUart_HM10 bleuart_HM10; // TI UART over BLE +#if !defined(EXCLUDE_NUS) +BLEUart bleuart_NUS; // Nordic UART over BLE +#endif /* EXCLUDE_NUS */ +BLEBas blebas; // battery +BLESensBox blesens; // SensBox + +#if defined(USE_BLE_MIDI) +BLEMidi blemidi; + +MIDI_CREATE_INSTANCE(BLEMidi, blemidi, MIDI_BLE); +#endif /* USE_BLE_MIDI */ + +#if defined(ENABLE_REMOTE_ID) +#include "../../protocol/radio/RemoteID.h" + +#define UUID16_COMPANY_ID_ASTM 0xFFFA + +static unsigned long RID_Time_Marker = 0; + +BLEService BLE_ODID_service; + +static const uint8_t ODID_Uuid[] = {0x00, 0x00, 0xff, 0xfa, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}; +#endif /* ENABLE_REMOTE_ID */ + +String BT_name = HOSTNAME; + +#define UUID16_COMPANY_ID_NORDIC 0x0059 + +static uint8_t BeaconUuid[16] = +{ + /* https://openuuid.net: becf4a85-29b8-476e-928f-fce11f303344 */ + 0xbe, 0xcf, 0x4a, 0x85, 0x29, 0xb8, 0x47, 0x6e, + 0x92, 0x8f, 0xfc, 0xe1, 0x1f, 0x30, 0x33, 0x44 +}; + +// UUID, Major, Minor, RSSI @ 1M +BLEBeacon iBeacon(BeaconUuid, 0x0102, 0x0304, -64); + +void startAdv(void) +{ + bool no_data = (settings->nmea_out != NMEA_BLUETOOTH && + settings->gdl90 != GDL90_BLUETOOTH && + settings->d1090 != D1090_BLUETOOTH); + + // Advertising packet + +#if defined(USE_IBEACON) + if (no_data && settings->volume == BUZZER_OFF) { + uint32_t id = SoC->getChipId(); + uint16_t major = (id >> 16) & 0x0000FFFF; + uint16_t minor = (id ) & 0x0000FFFF; + + // Manufacturer ID is required for Manufacturer Specific Data + iBeacon.setManufacturer(UUID16_COMPANY_ID_NORDIC); + iBeacon.setMajorMinor(major, minor); + + // Set the beacon payload using the BLEBeacon class + Bluefruit.Advertising.setBeacon(iBeacon); + } else +#endif /* USE_IBEACON */ + { + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + +#if defined(ENABLE_REMOTE_ID) + if (rid_enabled()) { + Bluefruit.Advertising.addService(BLE_ODID_service); + Bluefruit.Advertising.addName(); + } else +#endif /* ENABLE_REMOTE_ID */ +#if defined(USE_BLE_MIDI) + if (settings->volume != BUZZER_OFF) { + Bluefruit.Advertising.addService(blemidi, bleuart_HM10); + } else +#endif /* USE_BLE_MIDI */ + { + Bluefruit.Advertising.addService( +#if !defined(EXCLUDE_NUS) + bleuart_NUS, +#endif /* EXCLUDE_NUS */ + bleuart_HM10); + } + } + + // Secondary Scan Response packet (optional) + // Since there is no room for 'Name' in Advertising packet +#if defined(ENABLE_REMOTE_ID) + if (!rid_enabled()) +#endif /* ENABLE_REMOTE_ID */ + { + Bluefruit.ScanResponse.addName(); + } + + /* Start Advertising + * - Enable auto advertising if disconnected + * - Interval: fast mode = 20 ms, slow mode = 152.5 ms + * - Timeout for fast mode is 30 seconds + * - Start(timeout) with timeout = 0 will advertise forever (until connected) + * + * For recommended advertising interval + * https://developer.apple.com/library/content/qa/qa1931/_index.html + */ + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms + Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode + Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds +} + +// callback invoked when central connects +void connect_callback(uint16_t conn_handle) +{ +#if DEBUG_BLE + // Get the reference to current connection + BLEConnection* connection = Bluefruit.Connection(conn_handle); + + char central_name[32] = { 0 }; + connection->getPeerName(central_name, sizeof(central_name)); + + Serial.print("Connected to "); + Serial.println(central_name); +#endif +} + +/** + * Callback invoked when a connection is dropped + * @param conn_handle connection where this event happens + * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h + */ +void disconnect_callback(uint16_t conn_handle, uint8_t reason) +{ +#if DEBUG_BLE + (void) conn_handle; + (void) reason; + + Serial.println(); + Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX); +#endif +} + +void nRF52_Bluetooth_setup() +{ + BT_name += "-"; + BT_name += String(SoC->getChipId() & 0x00FFFFFFU, HEX); + +#if defined(ENABLE_REMOTE_ID) + rid_init(); + RID_Time_Marker = millis(); +#endif /* ENABLE_REMOTE_ID */ + + // Setup the BLE LED to be enabled on CONNECT + // Note: This is actually the default behavior, but provided + // here in case you want to control this LED manually via PIN 19 + Bluefruit.autoConnLed(LED_BLUE == SOC_GPIO_LED_BLE ? true : false); + + // Config the peripheral connection with maximum bandwidth + // more SRAM required by SoftDevice + // Note: All config***() function must be called before begin() + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + + Bluefruit.begin(); + Bluefruit.setTxPower(4); // Check bluefruit.h for supported values + Bluefruit.setName((BT_name+"-LE").c_str()); + Bluefruit.Periph.setConnectCallback(connect_callback); + Bluefruit.Periph.setDisconnectCallback(disconnect_callback); + + // To be consistent OTA DFU should be added first if it exists + bledfu.begin(); + + // Configure and Start Device Information Service + bledis.setManufacturer(nRF52_Device_Manufacturer); + bledis.setModel(nRF52_Device_Model); + bledis.setHardwareRev(hw_info.revision > 2 ? + Hardware_Rev[3] : Hardware_Rev[hw_info.revision]); + bledis.setSoftwareRev(SOFTRF_FIRMWARE_VERSION); + bledis.begin(); + + // Configure and Start BLE Uart Service + bleuart_HM10.begin(); +#if !defined(EXCLUDE_NUS) + bleuart_NUS.begin(); + bleuart_NUS.bufferTXD(true); +#endif /* EXCLUDE_NUS */ + + // Start BLE Battery Service + blebas.begin(); + blebas.write(100); + +#if defined(ENABLE_REMOTE_ID) + if (rid_enabled()) { + BLE_ODID_service.setUuid(BLEUuid(ODID_Uuid /* UUID16_COMPANY_ID_ASTM */)); + BLE_ODID_service.begin(); + } +#endif /* ENABLE_REMOTE_ID */ + + // Start SensBox Service + blesens.begin(); + +#if defined(USE_BLE_MIDI) + // Initialize MIDI with no any input channels + // This will also call blemidi service's begin() + MIDI_BLE.begin(MIDI_CHANNEL_OFF); +#endif /* USE_BLE_MIDI */ + + // Set up and start advertising + startAdv(); + +#if DEBUG_BLE + Serial.println("Please use Adafruit's Bluefruit LE app to connect in UART mode"); + Serial.println("Once connected, enter character(s) that you wish to send"); +#endif + + BLE_Notify_TimeMarker = millis(); + BLE_SensBox_TimeMarker = millis(); +} + +/********************************************************************* + End of Adafruit licensed text +*********************************************************************/ + +static void nRF52_Bluetooth_loop() +{ + // notify changed value + // bluetooth stack will go into congestion, if too many packets are sent + if ( Bluefruit.connected() && + bleuart_HM10.notifyEnabled() && + (millis() - BLE_Notify_TimeMarker > 10)) { /* < 18000 baud */ + bleuart_HM10.flushTXD(); + + BLE_Notify_TimeMarker = millis(); + } + + if (isTimeToBattery()) { + blebas.write(Battery_charge()); + } + + if (Bluefruit.connected() && isTimeToSensBox()) { + uint8_t sens_status = isValidFix() ? GNSS_STATUS_3D_MOVING : GNSS_STATUS_NONE; + blesens.notify_nav (sens_status); + blesens.notify_move(sens_status); + blesens.notify_gps2(sens_status); + blesens.notify_sys (sens_status); + BLE_SensBox_TimeMarker = millis(); + } + +#if defined(ENABLE_REMOTE_ID) + if (rid_enabled() && isValidFix()) { + if ((millis() - RID_Time_Marker) > 74) { + rid_encode((void *) &utm_data, &ThisAircraft); + squitter.transmit(&utm_data); + + RID_Time_Marker = millis(); + } + } +#endif /* ENABLE_REMOTE_ID */ +} + +static void nRF52_Bluetooth_fini() +{ + uint8_t sd_en = 0; + (void) sd_softdevice_is_enabled(&sd_en); + + if ( Bluefruit.connected() ) { + if ( bleuart_HM10.notifyEnabled() ) { + // flush TXD since we use bufferTXD() + bleuart_HM10.flushTXD(); + } + +#if !defined(EXCLUDE_NUS) + if ( bleuart_NUS.notifyEnabled() ) { + // flush TXD since we use bufferTXD() + bleuart_NUS.flushTXD(); + } +#endif /* EXCLUDE_NUS */ + } + + if (Bluefruit.Advertising.isRunning()) { + Bluefruit.Advertising.stop(); + } + + if (sd_en) sd_softdevice_disable(); +} + +static int nRF52_Bluetooth_available() +{ + int rval = 0; + + if ( !Bluefruit.connected() ) { + return rval; + } + + /* Give priority to HM-10 input */ + if ( bleuart_HM10.notifyEnabled() ) { + return bleuart_HM10.available(); + } + +#if !defined(EXCLUDE_NUS) + if ( bleuart_NUS.notifyEnabled() ) { + rval = bleuart_NUS.available(); + } +#endif /* EXCLUDE_NUS */ + + return rval; +} + +static int nRF52_Bluetooth_read() +{ + int rval = -1; + + if ( !Bluefruit.connected() ) { + return rval; + } + + /* Give priority to HM-10 input */ + if ( bleuart_HM10.notifyEnabled() ) { + return bleuart_HM10.read(); + } + +#if !defined(EXCLUDE_NUS) + if ( bleuart_NUS.notifyEnabled() ) { + rval = bleuart_NUS.read(); + } +#endif /* EXCLUDE_NUS */ + + return rval; +} + +static size_t nRF52_Bluetooth_write(const uint8_t *buffer, size_t size) +{ + size_t rval = size; + + if ( !Bluefruit.connected() ) { + return rval; + } + + /* Give priority to HM-10 output */ + if ( bleuart_HM10.notifyEnabled() && size > 0) { + return bleuart_HM10.write(buffer, size); + } + +#if !defined(EXCLUDE_NUS) + if ( bleuart_NUS.notifyEnabled() && size > 0) { + rval = bleuart_NUS.write(buffer, size); + } +#endif /* EXCLUDE_NUS */ + + return rval; +} + +IODev_ops_t nRF52_Bluetooth_ops = { + "nRF52 Bluetooth", + nRF52_Bluetooth_setup, + nRF52_Bluetooth_loop, + nRF52_Bluetooth_fini, + nRF52_Bluetooth_available, + nRF52_Bluetooth_read, + nRF52_Bluetooth_write +}; + +#endif /* ARDUINO_ARCH_NRF52 */ diff --git a/software/firmware/source/SoftRF/src/platform/bluetooth/Bluefruit.h b/software/firmware/source/SoftRF/src/platform/bluetooth/Bluefruit.h new file mode 100644 index 000000000..4ddcd2bcf --- /dev/null +++ b/software/firmware/source/SoftRF/src/platform/bluetooth/Bluefruit.h @@ -0,0 +1,90 @@ +/* + * BluetoothHelper.h + * Copyright (C) 2018-2024 Linar Yusupov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "bluefruit_common.h" + +#include "BLECharacteristic.h" +#include "BLEService.h" + +class BLESensBox : public BLEService +{ + protected: + BLECharacteristic _sensbox_nav; + BLECharacteristic _sensbox_move; + BLECharacteristic _sensbox_gps2; + BLECharacteristic _sensbox_sys; + + public: + BLESensBox(void); + + virtual err_t begin(void); + + bool notify_nav (uint8_t); + bool notify_move(uint8_t); + bool notify_gps2(uint8_t); + bool notify_sys (uint8_t); +}; + +/* + * Source: + * https://github.com/flytec/SensBoxLib_iOS/blob/master/_SensBox%20Documentation/SensorBox%20BLE%20Protocol.pdf + */ +typedef struct { + uint32_t timestamp; /* Date/Time (UTC), UnixTime */ + int32_t lat; /* deg * 10^7 */ + int32_t lon; /* deg * 10^7 */ + int16_t gnss_alt; /* GPS Hight MSL, m */ + int16_t pres_alt; /* Pressure Altitude, m */ + int16_t vario; /* Vario 1 Hz, cm/s */ + uint8_t status; /* Status: 0..2 - GNSS, 3 - time, 4 - charge, 5 - Bat, 6 - Log */ +} __attribute__((packed)) sensbox_navigation_t; + +typedef struct { + int32_t pres_alt; /* Pressure Altitude, cm */ + int16_t vario; /* Vario 8 Hz, cm/s */ + int16_t gs; /* Ground Speed, dm/s */ + int16_t cog; /* GPS Heading, deg * 10 */ + int16_t pitch; /* deg * 10 */ + int16_t yaw; /* deg * 10 */ + int16_t roll; /* deg * 10 */ + uint16_t accel; /* Acceleration, 'g' * 10 */ + uint8_t status; /* Status: same as above */ +} __attribute__((packed)) sensbox_movement_t; + +typedef struct { + uint16_t accuracy_h; /* Horizontal Accuracy, dm */ + uint16_t accuracy_v; /* Vertical Accuracy, dm */ + int16_t geo_separ; /* GPS Height Ellipsoid, m */ + uint8_t sats; /* Number of satellites */ + uint8_t status; /* Status: same as above */ +} __attribute__((packed)) sensbox_gps2_t; + +typedef struct { + uint32_t timestamp; /* Date/Time (UTC), UnixTime */ + uint8_t battery; /* Battery Level, % */ + uint8_t log; /* Logging level, % */ + int16_t temp; /* Temperature, °C * 10 */ + uint8_t status; /* Status: same as above */ + uint8_t status2; + uint16_t qnh; /* QNH, Pa * 10 */ + int32_t pressure; /* mPa */ +} __attribute__((packed)) sensbox_system_t; + +#define isTimeToSensBox() (millis() - BLE_SensBox_TimeMarker > 500) /* 2 Hz */ + +extern IODev_ops_t nRF52_Bluetooth_ops;