diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index f2e8df82..08362a80 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -741,7 +741,8 @@ public: #endif #elif defined(NRF52_PLATFORM) #ifdef BLE_PIN_CODE - #error "BLE not defined yet" + #include + SerialBLEInterface serial_interface; #else #include ArduinoSerialInterface serial_interface; @@ -806,6 +807,14 @@ void setup() { #if defined(NRF52_PLATFORM) InternalFS.begin(); +#ifdef BLE_PIN_CODE + serial_interface.begin("MeshCore", BLE_PIN_CODE); +#else + pinMode(WB_IO2, OUTPUT); + serial_interface.begin(Serial); +#endif + serial_interface.enable(); + the_mesh.begin(InternalFS, serial_interface, trng); #elif defined(ESP32) SPIFFS.begin(true); diff --git a/platformio.ini b/platformio.ini index f0121655..c4f948c6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -462,3 +462,19 @@ lib_deps = ${rak4631.lib_deps} adafruit/RTClib @ ^2.1.3 densaugeo/base64 @ ~1.4.0 + +[env:RAK_4631_companion_radio_ble] +extends = rak4631 +build_flags = + ${rak4631.build_flags} + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=1 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${rak4631.build_src_filter} + +<../examples/companion_radio/main.cpp> +lib_deps = + ${rak4631.lib_deps} + adafruit/RTClib @ ^2.1.3 + densaugeo/base64 @ ~1.4.0 diff --git a/src/helpers/ArduinoSerialInterface.h b/src/helpers/ArduinoSerialInterface.h index bef7bdf2..43613af2 100644 --- a/src/helpers/ArduinoSerialInterface.h +++ b/src/helpers/ArduinoSerialInterface.h @@ -10,6 +10,8 @@ class ArduinoSerialInterface : public BaseSerialInterface { uint16_t rx_len; #ifdef LILYGO_T3S3 HWCDC* _serial; +#elif defined(NRF52_PLATFORM) + Adafruit_USBD_CDC* _serial; #else HardwareSerial* _serial; #endif @@ -20,6 +22,8 @@ public: #ifdef LILYGO_T3S3 void begin(HWCDC& serial) { _serial = &serial; } +#elif defined(NRF52_PLATFORM) + void begin(Adafruit_USBD_CDC& serial) { _serial = &serial; } #else void begin(HardwareSerial& serial) { _serial = &serial; } #endif diff --git a/src/helpers/nrf52/SerialBLEInterface.cpp b/src/helpers/nrf52/SerialBLEInterface.cpp new file mode 100644 index 00000000..bfc204b2 --- /dev/null +++ b/src/helpers/nrf52/SerialBLEInterface.cpp @@ -0,0 +1,149 @@ +#include "SerialBLEInterface.h" + +void SerialBLEInterface::begin(const char* device_name, uint32_t pin_code) { + _pin_code = pin_code; + + Bluefruit.begin(); + Bluefruit.setTxPower(4); // Check bluefruit.h for supported values + + // To be consistent OTA DFU should be added first if it exists + //bledfu.begin(); +} + +void SerialBLEInterface::startAdv() { + // Advertising packet + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + + // Include the BLE UART (AKA 'NUS') 128-bit UUID + Bluefruit.Advertising.addService(bleuart); + + // Secondary Scan Response packet (optional) + // Since there is no room for 'Name' in Advertising packet + 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 +} + +// ---------- public methods + +void SerialBLEInterface::enable() { + if (_isEnabled) return; + + _isEnabled = true; + clearBuffers(); + + // Configure and start the BLE Uart service + bleuart.begin(); + + // Start advertising + startAdv(); + + checkAdvRestart = false; +} + +void SerialBLEInterface::disable() { + _isEnabled = false; + + BLE_DEBUG_PRINTLN("SerialBLEInterface::disable"); + + Bluefruit.Advertising.stop(); + + oldDeviceConnected = deviceConnected = false; + checkAdvRestart = false; +} + +size_t SerialBLEInterface::writeFrame(const uint8_t src[], size_t len) { + if (len > MAX_FRAME_SIZE) { + BLE_DEBUG_PRINTLN("writeFrame(), frame too big, len=%d", len); + return 0; + } + + if (deviceConnected && len > 0) { + if (send_queue_len >= FRAME_QUEUE_SIZE) { + BLE_DEBUG_PRINTLN("writeFrame(), send_queue is full!"); + return 0; + } + + send_queue[send_queue_len].len = len; // add to send queue + memcpy(send_queue[send_queue_len].buf, src, len); + send_queue_len++; + + return len; + } + return 0; +} + +#define BLE_WRITE_MIN_INTERVAL 20 + +bool SerialBLEInterface::isWriteBusy() const { + return millis() < _last_write + BLE_WRITE_MIN_INTERVAL; // still too soon to start another write? +} + +size_t SerialBLEInterface::checkRecvFrame(uint8_t dest[]) { + if (send_queue_len > 0 // first, check send queue + && millis() >= _last_write + BLE_WRITE_MIN_INTERVAL // space the writes apart + ) { + _last_write = millis(); + bleuart.write(send_queue[0].buf, send_queue[0].len); + BLE_DEBUG_PRINTLN("writeBytes: sz=%d, hdr=%d", (uint32_t)send_queue[0].len, (uint32_t) send_queue[0].buf[0]); + + send_queue_len--; + for (int i = 0; i < send_queue_len; i++) { // delete top item from queue + send_queue[i] = send_queue[i + 1]; + } + } else { + int len = bleuart.available(); + if (len > 0) { + bleuart.readBytes(dest, len); + BLE_DEBUG_PRINTLN("readBytes: sz=%d, hdr=%d", len, (uint32_t) dest[0]); + return len; + } + } + + if (Bluefruit.connected() == 0) deviceConnected = false; + + if (deviceConnected != oldDeviceConnected) { + if (!deviceConnected) { // disconnecting + clearBuffers(); + + BLE_DEBUG_PRINTLN("SerialBLEInterface -> disconnecting..."); + delay(500); // give the bluetooth stack the chance to get things ready + + checkAdvRestart = true; + } else { + BLE_DEBUG_PRINTLN("SerialBLEInterface -> stopping advertising"); + BLE_DEBUG_PRINTLN("SerialBLEInterface -> connecting..."); + // connecting + // do stuff here on connecting + Bluefruit.Advertising.stop(); + checkAdvRestart = false; + } + oldDeviceConnected = deviceConnected; + } + + if (checkAdvRestart) { + if (Bluefruit.connected() == 0) { + BLE_DEBUG_PRINTLN("SerialBLEInterface -> re-starting advertising"); + startAdv(); + } + checkAdvRestart = false; + } + return 0; +} + +bool SerialBLEInterface::isConnected() const { + return deviceConnected; //pServer != NULL && pServer->getConnectedCount() > 0; +} diff --git a/src/helpers/nrf52/SerialBLEInterface.h b/src/helpers/nrf52/SerialBLEInterface.h new file mode 100644 index 00000000..9824a9bd --- /dev/null +++ b/src/helpers/nrf52/SerialBLEInterface.h @@ -0,0 +1,58 @@ +#pragma once + +#include "../BaseSerialInterface.h" +#include + +class SerialBLEInterface : public BaseSerialInterface { + BLEUart bleuart; + bool deviceConnected; + bool oldDeviceConnected; + bool checkAdvRestart; + bool _isEnabled; + uint32_t _pin_code; + unsigned long _last_write; + + struct Frame { + uint8_t len; + uint8_t buf[MAX_FRAME_SIZE]; + }; + + #define FRAME_QUEUE_SIZE 4 + int send_queue_len; + Frame send_queue[FRAME_QUEUE_SIZE]; + + void clearBuffers() { send_queue_len = 0; } + void startAdv(); + +public: + SerialBLEInterface() { + deviceConnected = false; + oldDeviceConnected = false; + checkAdvRestart = false; + _isEnabled = false; + _last_write = 0; + send_queue_len = 0; + } + + void begin(const char* device_name, uint32_t pin_code); + + // BaseSerialInterface methods + void enable() override; + void disable() override; + bool isEnabled() const override { return _isEnabled; } + + bool isConnected() const override; + + bool isWriteBusy() const override; + size_t writeFrame(const uint8_t src[], size_t len) override; + size_t checkRecvFrame(uint8_t dest[]) override; +}; + +#if BLE_DEBUG_LOGGING && ARDUINO + #include + #define BLE_DEBUG_PRINT(F, ...) Serial.printf("BLE: " F, ##__VA_ARGS__) + #define BLE_DEBUG_PRINTLN(F, ...) Serial.printf("BLE: " F "\n", ##__VA_ARGS__) +#else + #define BLE_DEBUG_PRINT(...) {} + #define BLE_DEBUG_PRINTLN(...) {} +#endif