2021-06-27 10:56:28 -07:00
# include "configuration.h"
2020-04-23 18:02:28 -07:00
# include "NRF52Bluetooth.h"
2020-07-15 13:10:56 -07:00
# include "BluetoothCommon.h"
2020-04-23 21:22:58 -07:00
# include "main.h"
2021-03-14 19:00:20 -07:00
# include "mesh/PhoneAPI.h"
2021-03-27 11:21:43 +08:00
# include "mesh/mesh-pb-constants.h"
# include <bluefruit.h>
2022-02-01 18:32:26 -06:00
# include <utility/bonding.h>
2020-07-15 17:09:09 -07:00
2020-07-15 13:10:56 -07:00
static BLEService meshBleService = BLEService ( BLEUuid ( MESH_SERVICE_UUID_16 ) ) ;
static BLECharacteristic fromNum = BLECharacteristic ( BLEUuid ( FROMNUM_UUID_16 ) ) ;
static BLECharacteristic fromRadio = BLECharacteristic ( BLEUuid ( FROMRADIO_UUID_16 ) ) ;
static BLECharacteristic toRadio = BLECharacteristic ( BLEUuid ( TORADIO_UUID_16 ) ) ;
static BLEDis bledis ; // DIS (Device Information Service) helper class instance
static BLEBas blebas ; // BAS (Battery Service) helper class instance
static BLEDfu bledfu ; // DFU software update helper service
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
// proccess at once
// static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)];
static uint8_t fromRadioBytes [ FromRadio_size ] ;
2020-07-15 17:09:09 -07:00
static uint8_t toRadioBytes [ ToRadio_size ] ;
2020-07-15 13:10:56 -07:00
2022-08-22 16:41:23 -05:00
static uint16_t connectionHandle ;
2021-05-03 15:58:35 +08:00
2020-07-15 13:10:56 -07:00
class BluetoothPhoneAPI : public PhoneAPI
{
/**
* Subclasses can use this as a hook to provide custom notifications for their transport ( i . e . bluetooth notifies )
*/
2022-01-24 17:24:40 +00:00
virtual void onNowHasData ( uint32_t fromRadioNum ) override
2020-07-15 13:10:56 -07:00
{
PhoneAPI : : onNowHasData ( fromRadioNum ) ;
2020-04-23 18:02:28 -07:00
2020-07-15 13:10:56 -07:00
DEBUG_MSG ( " BLE notify fromNum \n " ) ;
fromNum . notify32 ( fromRadioNum ) ;
}
2021-05-03 15:58:35 +08:00
/// Check the current underlying physical link to see if the client is currently connected
2022-01-24 17:24:40 +00:00
virtual bool checkIsConnected ( ) override {
2022-08-22 16:41:23 -05:00
BLEConnection * connection = Bluefruit . Connection ( connectionHandle ) ;
return connection - > connected ( ) ;
2021-05-03 15:58:35 +08:00
}
2020-07-15 13:10:56 -07:00
} ;
2020-04-23 18:02:28 -07:00
2020-07-15 13:10:56 -07:00
static BluetoothPhoneAPI * bluetoothPhoneAPI ;
2020-04-23 18:02:28 -07:00
2022-08-22 16:41:23 -05:00
void onConnect ( uint16_t conn_handle )
2020-04-23 18:02:28 -07:00
{
// Get the reference to current connection
BLEConnection * connection = Bluefruit . Connection ( conn_handle ) ;
2022-08-29 07:31:02 -05:00
connectionHandle = conn_handle ;
2020-04-23 18:02:28 -07:00
char central_name [ 32 ] = { 0 } ;
connection - > getPeerName ( central_name , sizeof ( central_name ) ) ;
2020-07-15 13:10:56 -07:00
DEBUG_MSG ( " BLE Connected to %s \n " , central_name ) ;
2020-04-23 18:02:28 -07:00
}
/**
* 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
*/
2022-08-22 16:41:23 -05:00
void onDisconnect ( uint16_t conn_handle , uint8_t reason )
2020-04-23 18:02:28 -07:00
{
2022-08-23 14:03:10 -05:00
// FIXME - we currently assume only one active connection
2020-07-15 13:10:56 -07:00
DEBUG_MSG ( " BLE Disconnected, reason = 0x%x \n " , reason ) ;
2020-04-23 18:02:28 -07:00
}
2022-08-23 14:03:10 -05:00
void onCccd ( uint16_t conn_hdl , BLECharacteristic * chr , uint16_t cccd_value )
2020-04-23 18:02:28 -07:00
{
// Display the raw request packet
DEBUG_MSG ( " CCCD Updated: %u \n " , cccd_value ) ;
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
2020-07-15 13:10:56 -07:00
if ( chr - > uuid = = fromNum . uuid ) {
2020-04-23 18:02:28 -07:00
if ( chr - > notifyEnabled ( conn_hdl ) ) {
2020-07-15 13:10:56 -07:00
DEBUG_MSG ( " fromNum 'Notify' enabled \n " ) ;
2020-04-23 18:02:28 -07:00
} else {
2020-07-15 13:10:56 -07:00
DEBUG_MSG ( " fromNum 'Notify' disabled \n " ) ;
2020-04-23 18:02:28 -07:00
}
}
}
void startAdv ( void )
{
// Advertising packet
Bluefruit . Advertising . addFlags ( BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE ) ;
2020-07-15 17:09:09 -07:00
// IncludeService UUID
2020-07-17 09:13:47 -07:00
// Bluefruit.ScanResponse.addService(meshBleService);
2020-07-15 17:09:09 -07:00
Bluefruit . ScanResponse . addTxPower ( ) ;
2020-07-17 09:13:47 -07:00
Bluefruit . ScanResponse . addName ( ) ;
2020-04-23 18:02:28 -07:00
// Include Name
2020-07-17 09:13:47 -07:00
// Bluefruit.Advertising.addName();
Bluefruit . Advertising . addService ( meshBleService ) ;
2020-04-23 18:02:28 -07:00
/* 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
2020-07-15 17:09:09 -07:00
Bluefruit . Advertising . start ( 0 ) ; // 0 = Don't stop advertising after n seconds. FIXME, we should stop advertising after X
2020-04-23 18:02:28 -07:00
}
2020-07-17 10:02:07 -07:00
// Just ack that the caller is allowed to read
static void authorizeRead ( uint16_t conn_hdl )
{
ble_gatts_rw_authorize_reply_params_t reply = { . type = BLE_GATTS_AUTHORIZE_TYPE_READ } ;
reply . params . write . gatt_status = BLE_GATT_STATUS_SUCCESS ;
sd_ble_gatts_rw_authorize_reply ( conn_hdl , & reply ) ;
}
2020-07-15 13:10:56 -07:00
/**
* client is starting read , pull the bytes from our API class
*/
2022-08-23 14:03:10 -05:00
void onFromRadioAuthorize ( uint16_t conn_hdl , BLECharacteristic * chr , ble_gatts_evt_read_t * request )
2020-04-23 18:02:28 -07:00
{
2020-07-17 11:12:05 -07:00
if ( request - > offset = = 0 ) {
// If the read is long, we will get multiple authorize invocations - we only populate data on the first
size_t numBytes = bluetoothPhoneAPI - > getFromRadio ( fromRadioBytes ) ;
2020-07-15 13:10:56 -07:00
2020-07-17 11:12:05 -07:00
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
// or make empty if the queue is empty
fromRadio . write ( fromRadioBytes , numBytes ) ;
} else {
// DEBUG_MSG("Ignoring successor read\n");
}
2020-07-17 10:02:07 -07:00
authorizeRead ( conn_hdl ) ;
2020-07-15 13:10:56 -07:00
}
2022-08-23 14:03:10 -05:00
void onToRadioWrite ( uint16_t conn_hdl , BLECharacteristic * chr , uint8_t * data , uint16_t len )
2020-07-15 13:10:56 -07:00
{
DEBUG_MSG ( " toRadioWriteCb data %p, len %u \n " , data , len ) ;
bluetoothPhoneAPI - > handleToRadio ( data , len ) ;
}
/**
* client is starting read , pull the bytes from our API class
*/
2022-08-22 16:41:23 -05:00
void onFromNumAuthorize ( uint16_t conn_hdl , BLECharacteristic * chr , ble_gatts_evt_read_t * request )
2020-07-15 13:10:56 -07:00
{
2020-07-17 10:02:07 -07:00
DEBUG_MSG ( " fromNumAuthorizeCb \n " ) ;
authorizeRead ( conn_hdl ) ;
2020-07-15 13:10:56 -07:00
}
void setupMeshService ( void )
{
bluetoothPhoneAPI = new BluetoothPhoneAPI ( ) ;
meshBleService . begin ( ) ;
2020-04-23 18:02:28 -07:00
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.. Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was 'begin()'ed!
2022-09-09 12:55:31 +02:00
auto secMode = config . bluetooth . mode = = Config_BluetoothConfig_PairingMode_NO_PIN ? SECMODE_OPEN : SECMODE_ENC_NO_MITM ;
2022-08-23 14:03:10 -05:00
fromNum . setProperties ( CHR_PROPS_NOTIFY | CHR_PROPS_READ ) ;
fromNum . setPermission ( secMode , SECMODE_NO_ACCESS ) ; // FIXME, secure this!!!
2022-08-16 20:42:43 -05:00
fromNum . setFixedLen ( 0 ) ; // Variable len (either 0 or 4) FIXME consider changing protocol so it is fixed 4 byte len, where 0 means empty
2020-07-15 13:10:56 -07:00
fromNum . setMaxLen ( 4 ) ;
2022-08-23 14:03:10 -05:00
fromNum . setCccdWriteCallback ( onCccd ) ; // Optionally capture CCCD updates
2020-07-17 10:02:07 -07:00
// We don't yet need to hook the fromNum auth callback
// fromNum.setReadAuthorizeCallback(fromNumAuthorizeCb);
fromNum . write32 ( 0 ) ; // Provide default fromNum of 0
2020-07-15 13:10:56 -07:00
fromNum . begin ( ) ;
fromRadio . setProperties ( CHR_PROPS_READ ) ;
2022-08-23 14:03:10 -05:00
fromRadio . setPermission ( secMode , SECMODE_NO_ACCESS ) ;
2020-07-17 10:40:03 -07:00
fromRadio . setMaxLen ( sizeof ( fromRadioBytes ) ) ;
2022-08-23 14:03:10 -05:00
fromRadio . setReadAuthorizeCallback ( onFromRadioAuthorize , false ) ; // We don't call this callback via the adafruit queue, because we can safely run in the BLE context
2020-07-17 11:12:05 -07:00
fromRadio . setBuffer ( fromRadioBytes , sizeof ( fromRadioBytes ) ) ; // we preallocate our fromradio buffer so we won't waste space
2020-07-17 10:40:03 -07:00
// for two copies
2020-07-15 13:10:56 -07:00
fromRadio . begin ( ) ;
toRadio . setProperties ( CHR_PROPS_WRITE ) ;
2022-08-23 14:03:10 -05:00
toRadio . setPermission ( secMode , secMode ) ; // FIXME secure this!
2020-07-15 17:09:09 -07:00
toRadio . setFixedLen ( 0 ) ;
2020-07-15 13:10:56 -07:00
toRadio . setMaxLen ( 512 ) ;
2020-07-15 17:09:09 -07:00
toRadio . setBuffer ( toRadioBytes , sizeof ( toRadioBytes ) ) ;
2022-08-16 20:42:43 -05:00
// We don't call this callback via the adafruit queue, because we can safely run in the BLE context
2022-08-23 14:03:10 -05:00
toRadio . setWriteCallback ( onToRadioWrite , false ) ;
2020-07-15 13:10:56 -07:00
toRadio . begin ( ) ;
2020-04-23 18:02:28 -07:00
}
2020-05-25 17:16:09 -07:00
// FIXME, turn off soft device access for debugging
2020-07-15 17:09:09 -07:00
static bool isSoftDeviceAllowed = true ;
2022-08-22 16:41:23 -05:00
static uint32_t configuredPasskey ;
2020-05-25 17:16:09 -07:00
2020-10-30 17:05:32 +08:00
void NRF52Bluetooth : : shutdown ( )
{
// Shutdown bluetooth for minimum power draw
DEBUG_MSG ( " Disable NRF52 bluetooth \n " ) ;
Bluefruit . Advertising . stop ( ) ;
}
2020-04-23 18:02:28 -07:00
void NRF52Bluetooth : : setup ( )
{
// Initialise the Bluefruit module
2022-08-23 14:03:10 -05:00
DEBUG_MSG ( " Initialize the Bluefruit nRF52 module \n " ) ;
2021-03-14 19:00:20 -07:00
Bluefruit . autoConnLed ( false ) ;
2022-08-22 16:41:23 -05:00
Bluefruit . configPrphBandwidth ( BANDWIDTH_MAX ) ;
2020-04-23 18:02:28 -07:00
Bluefruit . begin ( ) ;
2022-04-30 18:20:41 -07:00
// Clear existing data.
Bluefruit . Advertising . stop ( ) ;
Bluefruit . Advertising . clearData ( ) ;
Bluefruit . ScanResponse . clearData ( ) ;
2022-09-09 12:55:31 +02:00
if ( config . bluetooth . mode ! = Config_BluetoothConfig_PairingMode_NO_PIN ) {
configuredPasskey = config . bluetooth . mode = = Config_BluetoothConfig_PairingMode_FIXED_PIN ?
2022-08-22 16:41:23 -05:00
config . bluetooth . fixed_pin : random ( 100000 , 999999 ) ;
auto pinString = std : : to_string ( configuredPasskey ) ;
DEBUG_MSG ( " Bluetooth pin set to '%i' \n " , configuredPasskey ) ;
Bluefruit . Security . setPIN ( pinString . c_str ( ) ) ;
Bluefruit . Security . setIOCaps ( true , false , false ) ;
Bluefruit . Security . setPairPasskeyCallback ( NRF52Bluetooth : : onPairingPasskey ) ;
Bluefruit . Security . setPairCompleteCallback ( NRF52Bluetooth : : onPairingCompleted ) ;
Bluefruit . Security . setSecuredCallback ( NRF52Bluetooth : : onConnectionSecured ) ;
meshBleService . setPermission ( SECMODE_ENC_WITH_MITM , SECMODE_ENC_WITH_MITM ) ;
}
else {
Bluefruit . Security . setIOCaps ( false , false , false ) ;
meshBleService . setPermission ( SECMODE_OPEN , SECMODE_OPEN ) ;
}
2020-04-23 18:02:28 -07:00
// Set the advertised device name (keep it short!)
2020-07-15 17:09:09 -07:00
Bluefruit . setName ( getDeviceName ( ) ) ;
2020-04-23 18:02:28 -07:00
// Set the connect/disconnect callback handlers
2022-08-22 16:41:23 -05:00
Bluefruit . Periph . setConnectCallback ( onConnect ) ;
Bluefruit . Periph . setDisconnectCallback ( onDisconnect ) ;
2020-04-23 18:02:28 -07:00
// Configure and Start the Device Information Service
2020-04-23 18:11:32 -07:00
DEBUG_MSG ( " Configuring the Device Information Service \n " ) ;
2020-07-15 17:09:09 -07:00
bledis . setModel ( optstr ( HW_VERSION ) ) ;
bledis . setFirmwareRev ( optstr ( APP_VERSION ) ) ;
2020-04-23 18:02:28 -07:00
bledis . begin ( ) ;
// Start the BLE Battery Service and set it to 100%
2020-04-23 18:11:32 -07:00
DEBUG_MSG ( " Configuring the Battery Service \n " ) ;
2020-04-23 18:02:28 -07:00
blebas . begin ( ) ;
2020-07-15 13:10:56 -07:00
blebas . write ( 0 ) ; // Unknown battery level for now
2020-04-24 21:04:10 -07:00
bledfu . begin ( ) ; // Install the DFU helper
2020-04-23 18:02:28 -07:00
// Setup the Heart Rate Monitor service using
// BLEService and BLECharacteristic classes
2020-07-15 13:10:56 -07:00
DEBUG_MSG ( " Configuring the Mesh bluetooth service \n " ) ;
setupMeshService ( ) ;
2020-04-23 18:02:28 -07:00
2020-05-25 17:16:09 -07:00
// Supposedly debugging works with soft device if you disable advertising
2022-08-16 20:42:43 -05:00
if ( isSoftDeviceAllowed )
{
2020-05-25 17:16:09 -07:00
// Setup the advertising packet(s)
DEBUG_MSG ( " Setting up the advertising payload(s) \n " ) ;
startAdv ( ) ;
2022-08-23 14:03:10 -05:00
2020-05-25 17:16:09 -07:00
DEBUG_MSG ( " Advertising \n " ) ;
}
2020-04-23 18:02:28 -07:00
}
2020-07-09 19:57:55 -07:00
/// Given a level between 0-100, update the BLE attribute
2020-07-15 13:10:56 -07:00
void updateBatteryLevel ( uint8_t level )
{
blebas . write ( level ) ;
2022-01-24 17:24:40 +00:00
}
2022-02-01 18:32:26 -06:00
void NRF52Bluetooth : : clearBonds ( )
{
DEBUG_MSG ( " Clearing bluetooth bonds! \n " ) ;
bond_print_list ( BLE_GAP_ROLE_PERIPH ) ;
bond_print_list ( BLE_GAP_ROLE_CENTRAL ) ;
Bluefruit . Periph . clearBonds ( ) ;
Bluefruit . Central . clearBonds ( ) ;
2022-04-30 18:20:41 -07:00
}
2022-08-22 16:41:23 -05:00
void NRF52Bluetooth : : onConnectionSecured ( uint16_t conn_handle )
{
DEBUG_MSG ( " BLE connection secured \n " ) ;
}
bool NRF52Bluetooth : : onPairingPasskey ( uint16_t conn_handle , uint8_t const passkey [ 6 ] , bool match_request )
{
DEBUG_MSG ( " BLE pairing process started with passkey %.3s %.3s \n " , passkey , passkey + 3 ) ;
screen - > startBluetoothPinScreen ( configuredPasskey ) ;
if ( match_request )
{
uint32_t start_time = millis ( ) ;
while ( millis ( ) < start_time + 30000 )
{
if ( ! Bluefruit . connected ( conn_handle ) ) break ;
}
}
DEBUG_MSG ( " BLE passkey pairing: match_request=%i \n " , match_request ) ;
return true ;
}
void NRF52Bluetooth : : onPairingCompleted ( uint16_t conn_handle , uint8_t auth_status )
{
if ( auth_status = = BLE_GAP_SEC_STATUS_SUCCESS )
DEBUG_MSG ( " BLE pairing success \n " ) ;
else
DEBUG_MSG ( " BLE pairing failed \n " ) ;
screen - > stopBluetoothPinScreen ( ) ;
}