woot! using new BLE api approximately works for reading

This commit is contained in:
geeksville 2020-04-23 11:02:14 -07:00
parent c67b53b969
commit 3673f95fe5
3 changed files with 184 additions and 186 deletions

View File

@ -1,70 +1,68 @@
#include "BluetoothUtil.h" #include "BluetoothUtil.h"
#include "BluetoothSoftwareUpdate.h" #include "BluetoothSoftwareUpdate.h"
#include <esp_gatt_defs.h>
#include <BLE2902.h>
#include <Arduino.h>
#include <Update.h>
#include "configuration.h" #include "configuration.h"
#include <Arduino.h>
#include <BLE2902.h>
#include <Update.h>
#include <esp_gatt_defs.h>
SimpleAllocator btPool; SimpleAllocator btPool;
/** /**
* Create standard device info service * Create standard device info service
**/ **/
BLEService *createDeviceInfomationService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion = "") 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)); 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 *swC =
BLECharacteristic *mfC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_MANU_NAME), BLECharacteristic::PROPERTY_READ); new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
// BLECharacteristic SerialNumberCharacteristic(BLEUUID((uint16_t) ESP_GATT_UUID_SERIAL_NUMBER_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? * 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; BLECharacteristic *m_pnpCharacteristic = m_deviceInfoService->createCharacteristic(ESP_GATT_UUID_PNP_ID,
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 }; BLECharacteristic::PROPERTY_READ);
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); uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version;
// m_manufacturerCharacteristic->setValue(name); 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);
/* add these later? // m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29,
ESP_GATT_UUID_SYSTEM_ID // BLECharacteristic::PROPERTY_READ); m_manufacturerCharacteristic->setValue(name);
*/
// caller must call service->start(); /* add these later?
return deviceInfoService; ESP_GATT_UUID_SYSTEM_ID
*/
// caller must call service->start();
return deviceInfoService;
} }
bool _BLEClientConnected = false; bool _BLEClientConnected = false;
class MyServerCallbacks : public BLEServerCallbacks class MyServerCallbacks : public BLEServerCallbacks
{ {
void onConnect(BLEServer *pServer) void onConnect(BLEServer *pServer) { _BLEClientConnected = true; };
{
_BLEClientConnected = true;
};
void onDisconnect(BLEServer *pServer) void onDisconnect(BLEServer *pServer) { _BLEClientConnected = false; }
{
_BLEClientConnected = false;
}
}; };
#define MAX_DESCRIPTORS 32 #define MAX_DESCRIPTORS 32
@ -78,34 +76,34 @@ static size_t numDescs;
/// Add a characteristic that we will delete when we restart /// Add a characteristic that we will delete when we restart
BLECharacteristic *addBLECharacteristic(BLECharacteristic *c) BLECharacteristic *addBLECharacteristic(BLECharacteristic *c)
{ {
assert(numChars < MAX_CHARACTERISTICS); assert(numChars < MAX_CHARACTERISTICS);
chars[numChars++] = c; chars[numChars++] = c;
return c; return c;
} }
/// Add a characteristic that we will delete when we restart /// Add a characteristic that we will delete when we restart
BLEDescriptor *addBLEDescriptor(BLEDescriptor *c) BLEDescriptor *addBLEDescriptor(BLEDescriptor *c)
{ {
assert(numDescs < MAX_DESCRIPTORS); assert(numDescs < MAX_DESCRIPTORS);
descs[numDescs++] = c; descs[numDescs++] = c;
return c; return c;
} }
// Help routine to add a description to any BLECharacteristic and add it to the service // 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 // We default to require an encrypted BOND for all these these characterstics
void addWithDesc(BLEService *service, BLECharacteristic *c, const char *description) void addWithDesc(BLEService *service, BLECharacteristic *c, const char *description)
{ {
c->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); 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); BLEDescriptor *desc = new BLEDescriptor(BLEUUID((uint16_t)ESP_GATT_UUID_CHAR_DESCRIPTION), strlen(description) + 1);
assert(desc); assert(desc);
desc->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); desc->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
desc->setValue(description); desc->setValue(description);
c->addDescriptor(desc); c->addDescriptor(desc);
service->addCharacteristic(c); service->addCharacteristic(c);
addBLECharacteristic(c); addBLECharacteristic(c);
addBLEDescriptor(desc); addBLEDescriptor(desc);
} }
static BLECharacteristic *batteryLevelC; static BLECharacteristic *batteryLevelC;
@ -115,19 +113,20 @@ static BLECharacteristic *batteryLevelC;
*/ */
BLEService *createBatteryService(BLEServer *server) BLEService *createBatteryService(BLEServer *server)
{ {
// Create the BLE Service // Create the BLE Service
BLEService *pBattery = server->createService(BLEUUID((uint16_t)0x180F)); 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); batteryLevelC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_BATTERY_LEVEL),
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100"); addWithDesc(pBattery, batteryLevelC, "Percentage 0 - 100");
batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification batteryLevelC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
// I don't think we need to advertise this // I don't think we need to advertise this
// server->getAdvertising()->addServiceUUID(pBattery->getUUID()); // server->getAdvertising()->addServiceUUID(pBattery->getUUID());
pBattery->start(); pBattery->start();
return pBattery; return pBattery;
} }
/** /**
@ -136,87 +135,82 @@ BLEService *createBatteryService(BLEServer *server)
*/ */
void updateBatteryLevel(uint8_t level) void updateBatteryLevel(uint8_t level)
{ {
// Pretend to update battery levels - fixme do elsewhere // Pretend to update battery levels - fixme do elsewhere
if (batteryLevelC) if (batteryLevelC) {
{ batteryLevelC->setValue(&level, 1);
batteryLevelC->setValue(&level, 1); batteryLevelC->notify();
batteryLevelC->notify(); }
}
} }
void dumpCharacteristic(BLECharacteristic *c) void dumpCharacteristic(BLECharacteristic *c)
{ {
std::string value = c->getValue(); std::string value = c->getValue();
if (value.length() > 0) if (value.length() > 0) {
{ DEBUG_MSG("New value: ");
DEBUG_MSG("New value: "); for (int i = 0; i < value.length(); i++)
for (int i = 0; i < value.length(); i++) DEBUG_MSG("%c", value[i]);
DEBUG_MSG("%c", value[i]);
DEBUG_MSG("\n"); DEBUG_MSG("\n");
} }
} }
/** converting endianness pull out a 32 bit value */ /** converting endianness pull out a 32 bit value */
uint32_t getValue32(BLECharacteristic *c, uint32_t defaultValue) uint32_t getValue32(BLECharacteristic *c, uint32_t defaultValue)
{ {
std::string value = c->getValue(); std::string value = c->getValue();
uint32_t r = defaultValue; uint32_t r = defaultValue;
if (value.length() == 4) if (value.length() == 4)
r = value[0] | (value[1] << 8UL) | (value[2] << 16UL) | (value[3] << 24UL); r = value[0] | (value[1] << 8UL) | (value[2] << 16UL) | (value[3] << 24UL);
return r; return r;
} }
class MySecurity : public BLESecurityCallbacks class MySecurity : public BLESecurityCallbacks
{ {
protected: protected:
bool onConfirmPIN(uint32_t pin) 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 %u\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; Serial.printf("onConfirmPIN %u\n", pin);
esp_ble_gap_get_whitelist_size(&length); return false;
Serial.printf(" onAuthenticationComplete -> success size: %d\n", length);
}
else
{
Serial.printf("onAuthenticationComplete -> fail %d\n", cmpl.fail_reason);
} }
// Remove our custom PIN request screen. uint32_t onPassKeyRequest()
stopCb(); {
} Serial.println("onPassKeyRequest");
return 123511; // not used
}
public: void onPassKeyNotify(uint32_t pass_key)
StartBluetoothPinScreenCallback startCb; {
StopBluetoothPinScreenCallback stopCb; Serial.printf("onPassKeyNotify %u\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; BLEServer *pServer;
@ -225,88 +219,88 @@ BLEService *pDevInfo, *pUpdate;
void deinitBLE() void deinitBLE()
{ {
assert(pServer); assert(pServer);
pServer->getAdvertising()->stop(); pServer->getAdvertising()->stop();
destroyUpdateService(); destroyUpdateService();
pUpdate->stop(); pUpdate->stop();
pDevInfo->stop(); pDevInfo->stop();
pUpdate->stop(); // we delete them below pUpdate->stop(); // we delete them below
// First shutdown bluetooth // First shutdown bluetooth
BLEDevice::deinit(false); BLEDevice::deinit(false);
// do not delete this - it is dynamically allocated, but only once - statically in BLEDevice // do not delete this - it is dynamically allocated, but only once - statically in BLEDevice
// delete pServer->getAdvertising(); // delete pServer->getAdvertising();
delete pUpdate; delete pUpdate;
delete pDevInfo; delete pDevInfo;
delete pServer; delete pServer;
batteryLevelC = NULL; // Don't let anyone generate bogus notifies batteryLevelC = NULL; // Don't let anyone generate bogus notifies
for (int i = 0; i < numChars; i++) for (int i = 0; i < numChars; i++)
delete chars[i]; delete chars[i];
numChars = 0; numChars = 0;
for (int i = 0; i < numDescs; i++) for (int i = 0; i < numDescs; i++)
delete descs[i]; delete descs[i];
numDescs = 0; numDescs = 0;
btPool.reset(); btPool.reset();
} }
BLEServer *initBLE( BLEServer *initBLE(StartBluetoothPinScreenCallback startBtPinScreen, StopBluetoothPinScreenCallback stopBtPinScreen,
StartBluetoothPinScreenCallback startBtPinScreen, std::string deviceName, std::string hwVendor, std::string swVersion, std::string hwVersion)
StopBluetoothPinScreenCallback stopBtPinScreen,
std::string deviceName, std::string hwVendor, std::string swVersion, std::string hwVersion)
{ {
BLEDevice::init(deviceName); BLEDevice::init(deviceName);
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT); BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
/* /*
* Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation * Required in authentication process to provide displaying and/or input passkey or yes/no butttons confirmation
*/ */
static MySecurity mySecurity; static MySecurity mySecurity;
mySecurity.startCb = startBtPinScreen; mySecurity.startCb = startBtPinScreen;
mySecurity.stopCb = stopBtPinScreen; mySecurity.stopCb = stopBtPinScreen;
BLEDevice::setSecurityCallbacks(&mySecurity); BLEDevice::setSecurityCallbacks(&mySecurity);
// Create the BLE Server // Create the BLE Server
pServer = BLEDevice::createServer(); pServer = BLEDevice::createServer();
static MyServerCallbacks myCallbacks; static MyServerCallbacks myCallbacks;
pServer->setCallbacks(&myCallbacks); pServer->setCallbacks(&myCallbacks);
pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion); pDevInfo = createDeviceInfomationService(pServer, hwVendor, swVersion, hwVersion);
// We now let users create the battery service only if they really want (not all devices have a battery) // We now let users create the battery service only if they really want (not all devices have a battery)
// BLEService *pBattery = createBatteryService(pServer); // BLEService *pBattery = createBatteryService(pServer);
pUpdate = createUpdateService(pServer, hwVendor, swVersion, hwVersion); // We need to advertise this so our android ble scan operation can see it pUpdate = createUpdateService(pServer, hwVendor, swVersion,
hwVersion); // We need to advertise this so our android ble scan operation can see it
// It seems only one service can be advertised - so for now don't advertise our updater // It seems only one service can be advertised - so for now don't advertise our updater
// pServer->getAdvertising()->addServiceUUID(pUpdate->getUUID()); // pServer->getAdvertising()->addServiceUUID(pUpdate->getUUID());
// start all our services (do this after creating all of them) // start all our services (do this after creating all of them)
pDevInfo->start(); pDevInfo->start();
pUpdate->start(); pUpdate->start();
// FIXME turn on this restriction only after the device is paired with a phone // 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 // 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 static BLESecurity security; // static to avoid allocs
BLESecurity *pSecurity = &security; BLESecurity *pSecurity = &security;
pSecurity->setCapability(ESP_IO_CAP_OUT); pSecurity->setCapability(ESP_IO_CAP_OUT);
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND); pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
return pServer; return pServer;
} }
// Called from loop // Called from loop
void loopBLE() void loopBLE()
{ {
bluetoothRebootCheck(); bluetoothRebootCheck();
} }

View File

@ -97,6 +97,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
case STATE_SEND_NODEINFO: { case STATE_SEND_NODEINFO: {
const NodeInfo *info = nodeInfoForPhone; const NodeInfo *info = nodeInfoForPhone;
nodeInfoForPhone = NULL; // We just consumed a nodeinfo, will need a new one next time
if (info) { if (info) {
DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id, DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id,
@ -140,8 +141,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
// Do we have a message from the mesh? // Do we have a message from the mesh?
if (fromRadioScratch.which_variant != 0) { if (fromRadioScratch.which_variant != 0) {
// Encapsulate as a FromRadio packet // Encapsulate as a FromRadio packet
size_t numbytes = pb_encode_to_bytes(buf, sizeof(FromRadio_size), FromRadio_fields, &fromRadioScratch); DEBUG_MSG("encoding toPhone packet to phone variant=%d", fromRadioScratch.which_variant);
DEBUG_MSG("delivering toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_variant, numbytes); size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch);
DEBUG_MSG(", %d bytes\n", numbytes);
return numbytes; return numbytes;
} }

View File

@ -192,6 +192,8 @@ class FromRadioCharacteristic : public CallbackCharacteristic
// or make empty if the queue is empty // or make empty if the queue is empty
if (numBytes) { if (numBytes) {
c->setValue(trBytes, numBytes); c->setValue(trBytes, numBytes);
} else {
c->setValue((uint8_t *)"", 0);
} }
} }
}; };