mirror of
https://github.com/meshtastic/firmware.git
synced 2025-02-04 11:49:58 +00:00
769 lines
24 KiB
C++
769 lines
24 KiB
C++
#include "BluetoothUtil.h"
|
|
#include "BluetoothSoftwareUpdate.h"
|
|
#include "configuration.h"
|
|
#include <Arduino.h>
|
|
#include <BLE2902.h>
|
|
#include <Update.h>
|
|
#include <esp_gatt_defs.h>
|
|
|
|
#ifdef CONFIG_BLUEDROID_ENABLED
|
|
|
|
SimpleAllocator btPool;
|
|
|
|
bool _BLEClientConnected = false;
|
|
|
|
class MyServerCallbacks : public BLEServerCallbacks
|
|
{
|
|
void onConnect(BLEServer *pServer) { _BLEClientConnected = true; };
|
|
|
|
void onDisconnect(BLEServer *pServer) { _BLEClientConnected = false; }
|
|
};
|
|
|
|
#define MAX_DESCRIPTORS 32
|
|
#define MAX_CHARACTERISTICS 32
|
|
|
|
static BLECharacteristic *chars[MAX_CHARACTERISTICS];
|
|
static size_t numChars;
|
|
static BLEDescriptor *descs[MAX_DESCRIPTORS];
|
|
static size_t numDescs;
|
|
|
|
/// Add a characteristic that we will delete when we restart
|
|
BLECharacteristic *addBLECharacteristic(BLECharacteristic *c)
|
|
{
|
|
assert(numChars < MAX_CHARACTERISTICS);
|
|
chars[numChars++] = c;
|
|
return c;
|
|
}
|
|
|
|
/// Add a characteristic that we will delete when we restart
|
|
BLEDescriptor *addBLEDescriptor(BLEDescriptor *c)
|
|
{
|
|
assert(numDescs < MAX_DESCRIPTORS);
|
|
descs[numDescs++] = c;
|
|
|
|
return c;
|
|
}
|
|
|
|
// Help routine to add a description to any BLECharacteristic and add it to the service
|
|
// We default to require an encrypted BOND for all these these characterstics
|
|
void addWithDesc(BLEService *service, BLECharacteristic *c, const char *description)
|
|
{
|
|
c->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
|
|
|
BLEDescriptor *desc = new BLEDescriptor(BLEUUID((uint16_t)ESP_GATT_UUID_CHAR_DESCRIPTION), strlen(description) + 1);
|
|
assert(desc);
|
|
desc->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
|
|
desc->setValue(description);
|
|
c->addDescriptor(desc);
|
|
service->addCharacteristic(c);
|
|
addBLECharacteristic(c);
|
|
addBLEDescriptor(desc);
|
|
}
|
|
|
|
/**
|
|
* Create standard device info service
|
|
**/
|
|
BLEService *createDeviceInfomationService(BLEServer *server, std::string hwVendor, std::string swVersion,
|
|
std::string hwVersion = "")
|
|
{
|
|
BLEService *deviceInfoService = server->createService(BLEUUID((uint16_t)ESP_GATT_UUID_DEVICE_INFO_SVC));
|
|
|
|
BLECharacteristic *swC =
|
|
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
|
|
BLECharacteristic *mfC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_MANU_NAME), BLECharacteristic::PROPERTY_READ);
|
|
// BLECharacteristic SerialNumberCharacteristic(BLEUUID((uint16_t) ESP_GATT_UUID_SERIAL_NUMBER_STR),
|
|
// BLECharacteristic::PROPERTY_READ);
|
|
|
|
/*
|
|
* Mandatory characteristic for device info service?
|
|
|
|
BLECharacteristic *m_pnpCharacteristic = m_deviceInfoService->createCharacteristic(ESP_GATT_UUID_PNP_ID,
|
|
BLECharacteristic::PROPERTY_READ);
|
|
|
|
uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version;
|
|
uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >>
|
|
8), (uint8_t) version }; m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
|
|
*/
|
|
swC->setValue(swVersion);
|
|
deviceInfoService->addCharacteristic(addBLECharacteristic(swC));
|
|
mfC->setValue(hwVendor);
|
|
deviceInfoService->addCharacteristic(addBLECharacteristic(mfC));
|
|
if (!hwVersion.empty()) {
|
|
BLECharacteristic *hwvC =
|
|
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_HW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
|
|
hwvC->setValue(hwVersion);
|
|
deviceInfoService->addCharacteristic(addBLECharacteristic(hwvC));
|
|
}
|
|
// SerialNumberCharacteristic.setValue("FIXME");
|
|
// deviceInfoService->addCharacteristic(&SerialNumberCharacteristic);
|
|
|
|
// m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29,
|
|
// BLECharacteristic::PROPERTY_READ); m_manufacturerCharacteristic->setValue(name);
|
|
|
|
/* add these later?
|
|
ESP_GATT_UUID_SYSTEM_ID
|
|
*/
|
|
|
|
// caller must call service->start();
|
|
return deviceInfoService;
|
|
}
|
|
|
|
static BLECharacteristic *batteryLevelC;
|
|
|
|
/**
|
|
* Create a battery level service
|
|
*/
|
|
BLEService *createBatteryService(BLEServer *server)
|
|
{
|
|
// Create the BLE Service
|
|
BLEService *pBattery = server->createService(BLEUUID((uint16_t)0x180F));
|
|
|
|
batteryLevelC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_BATTERY_LEVEL),
|
|
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
|
|
|
|
addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100");
|
|
batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
|
|
|
|
// I don't think we need to advertise this? and some phones only see the first thing advertised anyways...
|
|
// server->getAdvertising()->addServiceUUID(pBattery->getUUID());
|
|
pBattery->start();
|
|
|
|
return pBattery;
|
|
}
|
|
|
|
/**
|
|
* Update the battery level we are currently telling clients.
|
|
* level should be a pct between 0 and 100
|
|
*/
|
|
void updateBatteryLevel(uint8_t level)
|
|
{
|
|
if (batteryLevelC) {
|
|
DEBUG_MSG("set BLE battery level %u\n", level);
|
|
batteryLevelC->setValue(&level, 1);
|
|
batteryLevelC->notify();
|
|
}
|
|
}
|
|
|
|
void dumpCharacteristic(BLECharacteristic *c)
|
|
{
|
|
std::string value = c->getValue();
|
|
|
|
if (value.length() > 0) {
|
|
DEBUG_MSG("New value: ");
|
|
for (int i = 0; i < value.length(); i++)
|
|
DEBUG_MSG("%c", value[i]);
|
|
|
|
DEBUG_MSG("\n");
|
|
}
|
|
}
|
|
|
|
/** converting endianness pull out a 32 bit value */
|
|
uint32_t getValue32(BLECharacteristic *c, uint32_t defaultValue)
|
|
{
|
|
std::string value = c->getValue();
|
|
uint32_t r = defaultValue;
|
|
|
|
if (value.length() == 4)
|
|
r = value[0] | (value[1] << 8UL) | (value[2] << 16UL) | (value[3] << 24UL);
|
|
|
|
return r;
|
|
}
|
|
|
|
class MySecurity : public BLESecurityCallbacks
|
|
{
|
|
protected:
|
|
bool onConfirmPIN(uint32_t pin)
|
|
{
|
|
Serial.printf("onConfirmPIN %u\n", pin);
|
|
return false;
|
|
}
|
|
|
|
uint32_t onPassKeyRequest()
|
|
{
|
|
Serial.println("onPassKeyRequest");
|
|
return 123511; // not used
|
|
}
|
|
|
|
void onPassKeyNotify(uint32_t pass_key)
|
|
{
|
|
Serial.printf("onPassKeyNotify %06u\n", pass_key);
|
|
startCb(pass_key);
|
|
}
|
|
|
|
bool onSecurityRequest()
|
|
{
|
|
Serial.println("onSecurityRequest");
|
|
return true;
|
|
}
|
|
|
|
void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl)
|
|
{
|
|
if (cmpl.success) {
|
|
uint16_t length;
|
|
esp_ble_gap_get_whitelist_size(&length);
|
|
Serial.printf(" authenticated and connected to phone\n");
|
|
} else {
|
|
Serial.printf("phone authenticate failed %d\n", cmpl.fail_reason);
|
|
}
|
|
|
|
// Remove our custom PIN request screen.
|
|
stopCb();
|
|
}
|
|
|
|
public:
|
|
StartBluetoothPinScreenCallback startCb;
|
|
StopBluetoothPinScreenCallback stopCb;
|
|
};
|
|
|
|
BLEServer *pServer;
|
|
|
|
BLEService *pDevInfo, *pUpdate, *pBattery;
|
|
|
|
void deinitBLE()
|
|
{
|
|
assert(pServer);
|
|
|
|
pServer->getAdvertising()->stop();
|
|
|
|
if (pUpdate != NULL) {
|
|
destroyUpdateService();
|
|
|
|
pUpdate->stop(); // we delete them below
|
|
pUpdate->executeDelete();
|
|
}
|
|
|
|
pBattery->stop();
|
|
pBattery->executeDelete();
|
|
|
|
pDevInfo->stop();
|
|
pDevInfo->executeDelete();
|
|
|
|
// First shutdown bluetooth
|
|
BLEDevice::deinit(false);
|
|
|
|
// do not delete this - it is dynamically allocated, but only once - statically in BLEDevice
|
|
// delete pServer->getAdvertising();
|
|
|
|
if (pUpdate != NULL)
|
|
delete pUpdate;
|
|
delete pDevInfo;
|
|
delete pBattery;
|
|
delete pServer;
|
|
|
|
batteryLevelC = NULL; // Don't let anyone generate bogus notifies
|
|
|
|
for (int i = 0; i < numChars; i++) {
|
|
delete chars[i];
|
|
}
|
|
numChars = 0;
|
|
|
|
for (int i = 0; i < numDescs; i++)
|
|
delete descs[i];
|
|
numDescs = 0;
|
|
|
|
btPool.reset();
|
|
}
|
|
|
|
BLEServer *initBLE(StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoothPinScreenCallback stopBtPinScreen,
|
|
std::string deviceName, std::string hwVendor, std::string swVersion, std::string hwVersion)
|
|
{
|
|
BLEDevice::init(deviceName);
|
|
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
|
|
|
|
/*
|
|
* Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation
|
|
*/
|
|
static MySecurity mySecurity;
|
|
mySecurity.startCb = startBtPinScreen;
|
|
mySecurity.stopCb = stopBtPinScreen;
|
|
BLEDevice::setSecurityCallbacks(&mySecurity);
|
|
|
|
// Create the BLE Server
|
|
pServer = BLEDevice::createServer();
|
|
static MyServerCallbacks myCallbacks;
|
|
pServer->setCallbacks(&myCallbacks);
|
|
|
|
pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion);
|
|
|
|
pBattery = createBatteryService(pServer);
|
|
|
|
#define BLE_SOFTWARE_UPDATE
|
|
#ifdef BLE_SOFTWARE_UPDATE
|
|
pUpdate = createUpdateService(pServer, hwVendor, swVersion,
|
|
hwVersion); // We need to advertise this so our android ble scan operation can see it
|
|
|
|
pUpdate->start();
|
|
#endif
|
|
|
|
// It seems only one service can be advertised - so for now don't advertise our updater
|
|
// pServer->getAdvertising()->addServiceUUID(pUpdate->getUUID());
|
|
|
|
// start all our services (do this after creating all of them)
|
|
pDevInfo->start();
|
|
|
|
// FIXME turn on this restriction only after the device is paired with a phone
|
|
// advert->setScanFilter(false, true); // We let anyone scan for us (FIXME, perhaps only allow that until we are paired with a
|
|
// phone and configured) but only let whitelist phones connect
|
|
|
|
static BLESecurity security; // static to avoid allocs
|
|
BLESecurity *pSecurity = &security;
|
|
pSecurity->setCapability(ESP_IO_CAP_OUT);
|
|
|
|
// FIXME - really should be ESP_LE_AUTH_REQ_SC_BOND but it seems there is a bug right now causing that bonding info to be lost
|
|
// occasionally?
|
|
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND);
|
|
|
|
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
|
|
|
|
return pServer;
|
|
}
|
|
|
|
// Called from loop
|
|
void loopBLE()
|
|
{
|
|
bluetoothRebootCheck();
|
|
}
|
|
|
|
// This routine is called multiple times, once each time we come back from sleep
|
|
void reinitBluetooth()
|
|
{
|
|
DEBUG_MSG("Starting bluetooth\n");
|
|
|
|
// Note: these callbacks might be coming in from a different thread.
|
|
BLEServer *serve = initBLE(
|
|
[](uint32_t pin) {
|
|
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
|
screen.startBluetoothPinScreen(pin);
|
|
},
|
|
[]() { screen.stopBluetoothPinScreen(); }, getDeviceName(), HW_VENDOR, optstr(APP_VERSION),
|
|
optstr(HW_VERSION)); // FIXME, use a real name based on the macaddr
|
|
createMeshBluetoothService(serve);
|
|
|
|
// Start advertising - this must be done _after_ creating all services
|
|
serve->getAdvertising()->start();
|
|
}
|
|
|
|
#else
|
|
|
|
#include "PhoneAPI.h"
|
|
#include "host/util/util.h"
|
|
#include "main.h"
|
|
#include "nimble/NimbleDefs.h"
|
|
#include "services/gap/ble_svc_gap.h"
|
|
#include "services/gatt/ble_svc_gatt.h"
|
|
|
|
static uint8_t own_addr_type;
|
|
|
|
// 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(FromRadio_size, ToRadio_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);
|
|
|
|
DEBUG_MSG("BLE notify fromNum\n");
|
|
// fromNum.notify32(fromRadioNum);
|
|
}
|
|
};
|
|
|
|
static BluetoothPhoneAPI *bluetoothPhoneAPI;
|
|
|
|
int toradio_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
|
{
|
|
auto om = ctxt->om;
|
|
uint16_t len = 0;
|
|
|
|
auto rc = ble_hs_mbuf_to_flat(om, trBytes, sizeof(trBytes), &len);
|
|
if (rc != 0) {
|
|
return BLE_ATT_ERR_UNLIKELY;
|
|
}
|
|
|
|
DEBUG_MSG("toRadioWriteCb data %p, len %u\n", trBytes, len);
|
|
|
|
bluetoothPhoneAPI->handleToRadio(trBytes, len);
|
|
return 0;
|
|
}
|
|
|
|
int fromradio_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
|
{
|
|
DEBUG_MSG("BLE fromRadio called\n");
|
|
size_t numBytes = bluetoothPhoneAPI->getFromRadio(trBytes);
|
|
|
|
// 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
|
|
auto rc = os_mbuf_append(ctxt->om, trBytes, numBytes);
|
|
assert(rc == 0);
|
|
|
|
return 0; // success
|
|
}
|
|
|
|
int fromnum_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
|
{
|
|
static uint32_t fromNum = 0;
|
|
|
|
DEBUG_MSG("BLE fromNum called\n");
|
|
auto rc = os_mbuf_append(ctxt->om, &fromNum,
|
|
sizeof(fromNum)); // FIXME - once we report real numbers we will need to consider endianness
|
|
assert(rc == 0);
|
|
|
|
return 0; // success
|
|
}
|
|
|
|
// Force arduino to keep ble data around
|
|
extern "C" bool btInUse()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/// Given a level between 0-100, update the BLE attribute
|
|
void updateBatteryLevel(uint8_t level)
|
|
{
|
|
// FIXME
|
|
}
|
|
|
|
void deinitBLE()
|
|
{
|
|
// FIXME - do we need to dealloc things? - what needs to stay alive across light sleep?
|
|
auto ret = nimble_port_stop();
|
|
assert(ret == ESP_OK);
|
|
}
|
|
|
|
void loopBLE()
|
|
{
|
|
// FIXME
|
|
}
|
|
|
|
extern "C" void ble_store_config_init(void);
|
|
|
|
/// Print a macaddr
|
|
static void print_addr(const uint8_t *v) {}
|
|
|
|
/**
|
|
* Logs information about a connection to the console.
|
|
*/
|
|
static void print_conn_desc(struct ble_gap_conn_desc *desc)
|
|
{
|
|
DEBUG_MSG("handle=%d our_ota_addr_type=%d our_ota_addr=", desc->conn_handle, desc->our_ota_addr.type);
|
|
print_addr(desc->our_ota_addr.val);
|
|
DEBUG_MSG(" our_id_addr_type=%d our_id_addr=", desc->our_id_addr.type);
|
|
print_addr(desc->our_id_addr.val);
|
|
DEBUG_MSG(" peer_ota_addr_type=%d peer_ota_addr=", desc->peer_ota_addr.type);
|
|
print_addr(desc->peer_ota_addr.val);
|
|
DEBUG_MSG(" peer_id_addr_type=%d peer_id_addr=", desc->peer_id_addr.type);
|
|
print_addr(desc->peer_id_addr.val);
|
|
DEBUG_MSG(" conn_itvl=%d conn_latency=%d supervision_timeout=%d "
|
|
"encrypted=%d authenticated=%d bonded=%d\n",
|
|
desc->conn_itvl, desc->conn_latency, desc->supervision_timeout, desc->sec_state.encrypted,
|
|
desc->sec_state.authenticated, desc->sec_state.bonded);
|
|
}
|
|
|
|
static void advertise();
|
|
|
|
/**
|
|
* The nimble host executes this callback when a GAP event occurs. The
|
|
* application associates a GAP event callback with each connection that forms.
|
|
* bleprph uses the same callback for all connections.
|
|
*
|
|
* @param event The type of event being signalled.
|
|
* @param ctxt Various information pertaining to the event.
|
|
* @param arg Application-specified argument; unused by
|
|
* bleprph.
|
|
*
|
|
* @return 0 if the application successfully handled the
|
|
* event; nonzero on failure. The semantics
|
|
* of the return code is specific to the
|
|
* particular GAP event being signalled.
|
|
*/
|
|
static int bleprph_gap_event(struct ble_gap_event *event, void *arg)
|
|
{
|
|
struct ble_gap_conn_desc desc;
|
|
int rc;
|
|
|
|
switch (event->type) {
|
|
case BLE_GAP_EVENT_CONNECT:
|
|
/* A new connection was established or a connection attempt failed. */
|
|
DEBUG_MSG("connection %s; status=%d ", event->connect.status == 0 ? "established" : "failed", event->connect.status);
|
|
if (event->connect.status == 0) {
|
|
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
|
|
assert(rc == 0);
|
|
print_conn_desc(&desc);
|
|
}
|
|
DEBUG_MSG("\n");
|
|
|
|
if (event->connect.status != 0) {
|
|
/* Connection failed; resume advertising. */
|
|
advertise();
|
|
}
|
|
return 0;
|
|
|
|
case BLE_GAP_EVENT_DISCONNECT:
|
|
DEBUG_MSG("disconnect; reason=%d ", event->disconnect.reason);
|
|
// bleprph_print_conn_desc(&event->disconnect.conn);
|
|
DEBUG_MSG("\n");
|
|
|
|
/* Connection terminated; resume advertising. */
|
|
advertise();
|
|
return 0;
|
|
|
|
case BLE_GAP_EVENT_CONN_UPDATE:
|
|
/* The central has updated the connection parameters. */
|
|
DEBUG_MSG("connection updated; status=%d ", event->conn_update.status);
|
|
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
|
|
assert(rc == 0);
|
|
// bleprph_print_conn_desc(&desc);
|
|
DEBUG_MSG("\n");
|
|
return 0;
|
|
|
|
case BLE_GAP_EVENT_ADV_COMPLETE:
|
|
DEBUG_MSG("advertise complete; reason=%d", event->adv_complete.reason);
|
|
advertise();
|
|
return 0;
|
|
|
|
case BLE_GAP_EVENT_ENC_CHANGE:
|
|
/* Encryption has been enabled or disabled for this connection. */
|
|
DEBUG_MSG("encryption change event; status=%d ", event->enc_change.status);
|
|
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
|
assert(rc == 0);
|
|
// bleprph_print_conn_desc(&desc);
|
|
DEBUG_MSG("\n");
|
|
return 0;
|
|
|
|
case BLE_GAP_EVENT_SUBSCRIBE:
|
|
DEBUG_MSG("subscribe event; conn_handle=%d attr_handle=%d "
|
|
"reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
|
|
event->subscribe.conn_handle, event->subscribe.attr_handle, event->subscribe.reason,
|
|
event->subscribe.prev_notify, event->subscribe.cur_notify, event->subscribe.prev_indicate,
|
|
event->subscribe.cur_indicate);
|
|
return 0;
|
|
|
|
case BLE_GAP_EVENT_MTU:
|
|
DEBUG_MSG("mtu update event; conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id,
|
|
event->mtu.value);
|
|
return 0;
|
|
|
|
case BLE_GAP_EVENT_REPEAT_PAIRING:
|
|
/* We already have a bond with the peer, but it is attempting to
|
|
* establish a new secure link. This app sacrifices security for
|
|
* convenience: just throw away the old bond and accept the new link.
|
|
*/
|
|
|
|
/* Delete the old bond. */
|
|
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
|
|
assert(rc == 0);
|
|
ble_store_util_delete_peer(&desc.peer_id_addr);
|
|
|
|
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
|
|
* continue with the pairing operation.
|
|
*/
|
|
return BLE_GAP_REPEAT_PAIRING_RETRY;
|
|
|
|
case BLE_GAP_EVENT_PASSKEY_ACTION:
|
|
DEBUG_MSG("PASSKEY_ACTION_EVENT started \n");
|
|
struct ble_sm_io pkey = {0};
|
|
|
|
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
|
pkey.action = event->passkey.params.action;
|
|
pkey.passkey = 123456; // This is the passkey to be entered on peer
|
|
DEBUG_MSG("Enter passkey %d on the peer side", pkey.passkey);
|
|
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
|
DEBUG_MSG("ble_sm_inject_io result: %d\n", rc);
|
|
} else {
|
|
DEBUG_MSG("FIXME - unexpected auth type\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
/**
|
|
* Enables advertising with the following parameters:
|
|
* o General discoverable mode.
|
|
* o Undirected connectable mode.
|
|
*/
|
|
static void advertise(void)
|
|
{
|
|
struct ble_gap_adv_params adv_params;
|
|
struct ble_hs_adv_fields fields;
|
|
const char *name;
|
|
int rc;
|
|
|
|
/**
|
|
* Set the advertisement data included in our advertisements:
|
|
* o Flags (indicates advertisement type and other general info).
|
|
* o Advertising tx power.
|
|
* o Device name.
|
|
* o 16-bit service UUIDs (alert notifications).
|
|
*/
|
|
|
|
memset(&fields, 0, sizeof fields);
|
|
|
|
/* Advertise two flags:
|
|
* o Discoverability in forthcoming advertisement (general)
|
|
* o BLE-only (BR/EDR unsupported).
|
|
*/
|
|
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
|
|
|
|
/* Indicate that the TX power level field should be included; have the
|
|
* stack fill this value automatically. This is done by assigning the
|
|
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
|
|
*/
|
|
fields.tx_pwr_lvl_is_present = 1;
|
|
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
|
|
|
name = ble_svc_gap_device_name();
|
|
fields.name = (uint8_t *)name;
|
|
fields.name_len = strlen(name);
|
|
fields.name_is_complete = 1;
|
|
|
|
// fields.uuids16 = (ble_uuid16_t[]){BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)};
|
|
// fields.num_uuids16 = 1;
|
|
// fields.uuids16_is_complete = 1;
|
|
|
|
rc = ble_gap_adv_set_fields(&fields);
|
|
if (rc != 0) {
|
|
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
|
|
return;
|
|
}
|
|
|
|
/* Begin advertising. */
|
|
memset(&adv_params, 0, sizeof adv_params);
|
|
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
|
|
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
|
// FIXME - use RPA for first parameter
|
|
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params, bleprph_gap_event, NULL);
|
|
if (rc != 0) {
|
|
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void on_reset(int reason)
|
|
{
|
|
// 19 == BLE_HS_ETIMEOUT_HCI
|
|
DEBUG_MSG("Resetting state; reason=%d\n", reason);
|
|
}
|
|
|
|
static void on_sync(void)
|
|
{
|
|
int rc;
|
|
|
|
rc = ble_hs_util_ensure_addr(0);
|
|
assert(rc == 0);
|
|
|
|
/* Figure out address to use while advertising (no privacy for now) */
|
|
rc = ble_hs_id_infer_auto(0, &own_addr_type);
|
|
if (rc != 0) {
|
|
DEBUG_MSG("error determining address type; rc=%d\n", rc);
|
|
return;
|
|
}
|
|
|
|
/* Printing ADDR */
|
|
uint8_t addr_val[6] = {0};
|
|
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
|
|
|
|
DEBUG_MSG("Device Address: ");
|
|
print_addr(addr_val);
|
|
DEBUG_MSG("\n");
|
|
/* Begin advertising. */
|
|
advertise();
|
|
}
|
|
|
|
static void ble_host_task(void *param)
|
|
{
|
|
DEBUG_MSG("BLE task running\n");
|
|
nimble_port_run(); // This function will return only when nimble_port_stop() is executed.
|
|
|
|
nimble_port_deinit(); // teardown nimble datastructures
|
|
nimble_port_freertos_deinit(); // delete the task
|
|
|
|
auto ret = esp_nimble_hci_and_controller_deinit();
|
|
assert(ret == ESP_OK);
|
|
}
|
|
|
|
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
|
|
{
|
|
char buf[BLE_UUID_STR_LEN];
|
|
|
|
switch (ctxt->op) {
|
|
case BLE_GATT_REGISTER_OP_SVC:
|
|
DEBUG_MSG("registered service %s with handle=%d\n", ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf), ctxt->svc.handle);
|
|
break;
|
|
|
|
case BLE_GATT_REGISTER_OP_CHR:
|
|
DEBUG_MSG("registering characteristic %s with "
|
|
"def_handle=%d val_handle=%d\n",
|
|
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), ctxt->chr.def_handle, ctxt->chr.val_handle);
|
|
break;
|
|
|
|
case BLE_GATT_REGISTER_OP_DSC:
|
|
DEBUG_MSG("registering descriptor %s with handle=%d\n", ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf), ctxt->dsc.handle);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// This routine is called multiple times, once each time we come back from sleep
|
|
void reinitBluetooth()
|
|
{
|
|
DEBUG_MSG("Starting bluetooth\n");
|
|
esp_log_level_set("BTDM_INIT", ESP_LOG_VERBOSE);
|
|
|
|
if (!bluetoothPhoneAPI) {
|
|
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
|
bluetoothPhoneAPI->init();
|
|
}
|
|
|
|
// FIXME - if waking from light sleep, only esp_nimble_hci_init
|
|
// FIXME - why didn't this version work?
|
|
auto res = esp_nimble_hci_and_controller_init();
|
|
// auto res = esp_nimble_hci_init();
|
|
// DEBUG_MSG("BLE result %d\n", res);
|
|
assert(res == ESP_OK);
|
|
|
|
nimble_port_init();
|
|
|
|
/* Initialize the NimBLE host configuration. */
|
|
ble_hs_cfg.reset_cb = on_reset;
|
|
ble_hs_cfg.sync_cb = on_sync;
|
|
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
|
|
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
|
|
|
ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY;
|
|
ble_hs_cfg.sm_bonding = 1;
|
|
ble_hs_cfg.sm_mitm = 1;
|
|
ble_hs_cfg.sm_sc = 1;
|
|
ble_hs_cfg.sm_our_key_dist = 1;
|
|
ble_hs_cfg.sm_their_key_dist = 1;
|
|
|
|
// add standard GAP services
|
|
ble_svc_gap_init();
|
|
ble_svc_gatt_init();
|
|
|
|
res = ble_gatts_count_cfg(
|
|
gatt_svr_svcs); // assigns handles? see docstring for note about clearing the handle list before calling SLEEP SUPPORT
|
|
assert(res == 0);
|
|
|
|
res = ble_gatts_add_svcs(gatt_svr_svcs);
|
|
assert(res == 0);
|
|
|
|
/* Set the default device name. */
|
|
res = ble_svc_gap_device_name_set(getDeviceName());
|
|
assert(res == 0);
|
|
|
|
/* XXX Need to have template for store */
|
|
ble_store_config_init();
|
|
|
|
nimble_port_freertos_init(ble_host_task);
|
|
}
|
|
|
|
#endif
|