2020-04-24 01:02:28 +00:00
|
|
|
#include "NRF52Bluetooth.h"
|
2020-07-15 20:10:56 +00:00
|
|
|
#include "BluetoothCommon.h"
|
2020-04-24 01:02:28 +00:00
|
|
|
#include "configuration.h"
|
2020-04-24 04:22:58 +00:00
|
|
|
#include "main.h"
|
2020-04-24 01:02:28 +00:00
|
|
|
#include <bluefruit.h>
|
|
|
|
|
2020-07-15 20:10:56 +00: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];
|
|
|
|
|
|
|
|
class BluetoothPhoneAPI : public PhoneAPI
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
|
|
|
|
*/
|
|
|
|
virtual void onNowHasData(uint32_t fromRadioNum)
|
|
|
|
{
|
|
|
|
PhoneAPI::onNowHasData(fromRadioNum);
|
2020-04-24 01:02:28 +00:00
|
|
|
|
2020-07-15 20:10:56 +00:00
|
|
|
DEBUG_MSG("BLE notify fromNum\n");
|
|
|
|
fromNum.notify32(fromRadioNum);
|
|
|
|
}
|
|
|
|
};
|
2020-04-24 01:02:28 +00:00
|
|
|
|
2020-07-15 20:10:56 +00:00
|
|
|
static BluetoothPhoneAPI *bluetoothPhoneAPI;
|
2020-04-24 01:02:28 +00:00
|
|
|
|
|
|
|
void connect_callback(uint16_t conn_handle)
|
|
|
|
{
|
|
|
|
// Get the reference to current connection
|
|
|
|
BLEConnection *connection = Bluefruit.Connection(conn_handle);
|
|
|
|
|
|
|
|
char central_name[32] = {0};
|
|
|
|
connection->getPeerName(central_name, sizeof(central_name));
|
|
|
|
|
2020-07-15 20:10:56 +00:00
|
|
|
DEBUG_MSG("BLE Connected to %s\n", central_name);
|
2020-04-24 01:02:28 +00: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
|
|
|
|
*/
|
|
|
|
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
|
|
|
|
{
|
|
|
|
(void)conn_handle;
|
|
|
|
|
2020-07-15 20:10:56 +00:00
|
|
|
DEBUG_MSG("BLE Disconnected, reason = 0x%x\n", reason);
|
2020-04-24 01:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void cccd_callback(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
|
|
|
|
{
|
|
|
|
// 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 20:10:56 +00:00
|
|
|
if (chr->uuid == fromNum.uuid) {
|
2020-04-24 01:02:28 +00:00
|
|
|
if (chr->notifyEnabled(conn_hdl)) {
|
2020-07-15 20:10:56 +00:00
|
|
|
DEBUG_MSG("fromNum 'Notify' enabled\n");
|
2020-04-24 01:02:28 +00:00
|
|
|
} else {
|
2020-07-15 20:10:56 +00:00
|
|
|
DEBUG_MSG("fromNum 'Notify' disabled\n");
|
2020-04-24 01:02:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void startAdv(void)
|
|
|
|
{
|
|
|
|
// Advertising packet
|
|
|
|
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
|
|
|
Bluefruit.Advertising.addTxPower();
|
|
|
|
|
|
|
|
// Include HRM Service UUID
|
2020-07-15 20:10:56 +00:00
|
|
|
Bluefruit.Advertising.addService(meshBleService);
|
2020-04-24 01:02:28 +00:00
|
|
|
|
|
|
|
// Include Name
|
|
|
|
Bluefruit.Advertising.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
|
|
|
|
}
|
|
|
|
|
2020-07-15 20:10:56 +00:00
|
|
|
/**
|
|
|
|
* client is starting read, pull the bytes from our API class
|
|
|
|
*/
|
|
|
|
void fromRadioAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request)
|
2020-04-24 01:02:28 +00:00
|
|
|
{
|
2020-07-15 20:10:56 +00:00
|
|
|
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
|
|
|
|
|
|
|
|
DEBUG_MSG("fromRadioAuthorizeCb numBytes=%u\n", numBytes);
|
|
|
|
|
|
|
|
// 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
|
|
|
|
chr->write(fromRadioBytes, numBytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
void toRadioWriteCb(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
void fromNumAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request)
|
|
|
|
{
|
|
|
|
DEBUG_MSG("fromNumAuthorizeCb FIXME - implement\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void setupMeshService(void)
|
|
|
|
{
|
|
|
|
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
|
|
|
bluetoothPhoneAPI->init();
|
|
|
|
|
|
|
|
meshBleService.begin();
|
2020-04-24 01:02:28 +00: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!
|
|
|
|
|
2020-07-15 20:10:56 +00:00
|
|
|
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
|
|
|
fromNum.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME, secure this!!!
|
|
|
|
fromNum.setFixedLen(
|
|
|
|
0); // Variable len (either 0 or 4) FIXME consider changing protocol so it is fixed 4 byte len, where 0 means empty
|
|
|
|
fromNum.setMaxLen(4);
|
|
|
|
fromNum.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
|
|
|
|
fromNum.setReadAuthorizeCallback(fromNumAuthorizeCb);
|
|
|
|
fromNum.begin();
|
|
|
|
// uint8_t hrmdata[2] = {0b00000110, 0x40}; // Set the characteristic to use 8-bit values, with the sensor connected and
|
|
|
|
// detected
|
|
|
|
// hrmc.write(hrmdata, 2);
|
|
|
|
|
|
|
|
fromRadio.setProperties(CHR_PROPS_READ);
|
|
|
|
fromRadio.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME secure this!
|
|
|
|
fromRadio.setMaxLen(512);
|
|
|
|
fromRadio.setReadAuthorizeCallback(fromRadioAuthorizeCb);
|
|
|
|
fromRadio.setBuffer(fromRadioBytes,
|
|
|
|
sizeof(fromRadioBytes)); // we preallocate our fromradio buffer so we won't waste space for two copies
|
|
|
|
fromRadio.begin();
|
|
|
|
|
|
|
|
toRadio.setProperties(CHR_PROPS_WRITE);
|
|
|
|
toRadio.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME secure this!
|
|
|
|
toRadio.setMaxLen(512);
|
|
|
|
toRadio.setWriteCallback(toRadioWriteCb);
|
|
|
|
toRadio.begin();
|
2020-04-24 01:02:28 +00:00
|
|
|
}
|
|
|
|
|
2020-05-26 00:16:09 +00:00
|
|
|
// FIXME, turn off soft device access for debugging
|
|
|
|
static bool isSoftDeviceAllowed = false;
|
|
|
|
|
2020-04-24 01:02:28 +00:00
|
|
|
void NRF52Bluetooth::setup()
|
|
|
|
{
|
|
|
|
// Initialise the Bluefruit module
|
|
|
|
DEBUG_MSG("Initialise the Bluefruit nRF52 module\n");
|
|
|
|
Bluefruit.begin();
|
|
|
|
|
|
|
|
// Set the advertised device name (keep it short!)
|
2020-04-24 04:22:58 +00:00
|
|
|
Bluefruit.setName(getDeviceName()); // FIXME
|
2020-04-24 01:02:28 +00:00
|
|
|
|
|
|
|
// Set the connect/disconnect callback handlers
|
|
|
|
Bluefruit.Periph.setConnectCallback(connect_callback);
|
|
|
|
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
|
|
|
|
|
|
|
|
// Configure and Start the Device Information Service
|
2020-04-24 01:11:32 +00:00
|
|
|
DEBUG_MSG("Configuring the Device Information Service\n");
|
2020-04-24 01:02:28 +00:00
|
|
|
bledis.setManufacturer("meshtastic.org");
|
2020-04-24 01:47:27 +00:00
|
|
|
bledis.setModel("NRF52-meshtastic"); // FIXME
|
2020-04-24 01:02:28 +00:00
|
|
|
bledis.begin();
|
|
|
|
|
|
|
|
// Start the BLE Battery Service and set it to 100%
|
2020-04-24 01:11:32 +00:00
|
|
|
DEBUG_MSG("Configuring the Battery Service\n");
|
2020-04-24 01:02:28 +00:00
|
|
|
blebas.begin();
|
2020-07-15 20:10:56 +00:00
|
|
|
blebas.write(0); // Unknown battery level for now
|
2020-04-24 01:02:28 +00:00
|
|
|
|
2020-04-25 04:04:10 +00:00
|
|
|
bledfu.begin(); // Install the DFU helper
|
|
|
|
|
2020-04-24 01:02:28 +00:00
|
|
|
// Setup the Heart Rate Monitor service using
|
|
|
|
// BLEService and BLECharacteristic classes
|
2020-07-15 20:10:56 +00:00
|
|
|
DEBUG_MSG("Configuring the Mesh bluetooth service\n");
|
|
|
|
setupMeshService();
|
2020-04-24 01:02:28 +00:00
|
|
|
|
2020-05-26 00:16:09 +00:00
|
|
|
// Supposedly debugging works with soft device if you disable advertising
|
|
|
|
if (isSoftDeviceAllowed) {
|
|
|
|
// Setup the advertising packet(s)
|
|
|
|
DEBUG_MSG("Setting up the advertising payload(s)\n");
|
|
|
|
startAdv();
|
2020-04-24 01:02:28 +00:00
|
|
|
|
2020-05-26 00:16:09 +00:00
|
|
|
DEBUG_MSG("Advertising\n");
|
|
|
|
}
|
2020-04-24 01:02:28 +00:00
|
|
|
}
|
|
|
|
|
2020-07-10 02:57:55 +00:00
|
|
|
/// Given a level between 0-100, update the BLE attribute
|
2020-07-15 20:10:56 +00:00
|
|
|
void updateBatteryLevel(uint8_t level)
|
|
|
|
{
|
|
|
|
blebas.write(level);
|
2020-07-10 02:57:55 +00:00
|
|
|
}
|
|
|
|
|
2020-04-24 01:02:28 +00:00
|
|
|
/*
|
|
|
|
void loop()
|
|
|
|
{
|
|
|
|
digitalToggle(LED_RED);
|
|
|
|
|
|
|
|
if ( Bluefruit.connected() ) {
|
|
|
|
uint8_t hrmdata[2] = { 0b00000110, bps++ }; // Sensor connected, increment BPS value
|
|
|
|
|
|
|
|
// Note: We use .notify instead of .write!
|
|
|
|
// If it is connected but CCCD is not enabled
|
|
|
|
// The characteristic's value is still updated although notification is not sent
|
|
|
|
if ( hrmc.notify(hrmdata, sizeof(hrmdata)) ){
|
|
|
|
Serial.print("Heart Rate Measurement updated to: "); Serial.println(bps);
|
|
|
|
}else{
|
|
|
|
Serial.println("ERROR: Notify not set in the CCCD or not connected!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only send update once per second
|
|
|
|
delay(1000);
|
|
|
|
}
|
2020-04-25 04:04:10 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
examples of advanced characteristics. use setReadAuthorizeCallback to prepare data for reads by others
|
|
|
|
|
|
|
|
err_t BLEDfu::begin(void)
|
|
|
|
{
|
|
|
|
// Invoke base class begin()
|
|
|
|
VERIFY_STATUS( BLEService::begin() );
|
|
|
|
|
|
|
|
// No need to keep packet & revision characteristics
|
|
|
|
BLECharacteristic chr_packet(UUID128_CHR_DFU_PACKET);
|
|
|
|
chr_packet.setTempMemory();
|
|
|
|
chr_packet.setProperties(CHR_PROPS_WRITE_WO_RESP);
|
|
|
|
chr_packet.setMaxLen(20);
|
|
|
|
VERIFY_STATUS( chr_packet.begin() );
|
|
|
|
|
|
|
|
_chr_control.setProperties(CHR_PROPS_WRITE | CHR_PROPS_NOTIFY);
|
|
|
|
_chr_control.setMaxLen(23);
|
|
|
|
_chr_control.setWriteAuthorizeCallback(bledfu_control_wr_authorize_cb);
|
|
|
|
VERIFY_STATUS( _chr_control.begin() );
|
|
|
|
|
|
|
|
BLECharacteristic chr_revision(UUID128_CHR_DFU_REVISON);
|
|
|
|
chr_revision.setTempMemory();
|
|
|
|
chr_revision.setProperties(CHR_PROPS_READ);
|
|
|
|
chr_revision.setFixedLen(2);
|
|
|
|
VERIFY_STATUS( chr_revision.begin());
|
|
|
|
chr_revision.write16(DFU_REV_APPMODE);
|
|
|
|
|
|
|
|
return ERROR_NONE;
|
|
|
|
}
|
2020-04-24 01:02:28 +00:00
|
|
|
*/
|