WIP begin changing software update service over to nimble

This commit is contained in:
geeksville 2020-07-23 08:37:00 -07:00
parent 0415a3c369
commit 00ca351169
5 changed files with 158 additions and 157 deletions

View File

@ -1,130 +1,106 @@
#include <Arduino.h>
#ifdef CONFIG_BLUEDROID_ENABLED
#include "../concurrency/LockGuard.h"
#include "../timing.h"
#include "BluetoothSoftwareUpdate.h"
#include "BluetoothUtil.h"
#include "RadioLibInterface.h"
#include "configuration.h"
#include <BLE2902.h>
#include <CRC32.h>
#include <Update.h>
#include <esp_gatt_defs.h>
#include "CallbackCharacteristic.h"
CRC32 crc;
uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
int16_t updateResultHandle;
uint32_t updateExpectedSize, updateActualSize;
static CRC32 crc;
static uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
concurrency::Lock *updateLock;
static uint32_t updateExpectedSize, updateActualSize;
class TotalSizeCharacteristic : public CallbackCharacteristic
static concurrency::Lock *updateLock;
/// Handle writes & reads to total size
int totalSize_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
public:
TotalSizeCharacteristic()
: CallbackCharacteristic("e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e",
BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ)
{
}
concurrency::LockGuard g(updateLock);
// Check if there is enough to OTA Update
uint32_t len = getValue32(c, 0);
updateExpectedSize = len;
updateActualSize = 0;
crc.reset();
bool canBegin = Update.begin(len);
DEBUG_MSG("Setting update size %u, result %d\n", len, canBegin);
if (!canBegin) {
// Indicate failure by forcing the size to 0
uint32_t zero = 0;
c->setValue(zero);
} else {
// This totally breaks abstraction to up up into the app layer for this, but quick hack to make sure we only
// talk to one service during the sw update.
// DEBUG_MSG("FIXME, crufty shutdown of mesh bluetooth for sw update.");
// void stopMeshBluetoothService();
// stopMeshBluetoothService();
void onWrite(BLECharacteristic *c)
{
concurrency::LockGuard g(updateLock);
// Check if there is enough to OTA Update
uint32_t len = getValue32(c, 0);
updateExpectedSize = len;
updateActualSize = 0;
crc.reset();
bool canBegin = Update.begin(len);
DEBUG_MSG("Setting update size %u, result %d\n", len, canBegin);
if (!canBegin) {
// Indicate failure by forcing the size to 0
uint32_t zero = 0;
c->setValue(zero);
} else {
// This totally breaks abstraction to up up into the app layer for this, but quick hack to make sure we only
// talk to one service during the sw update.
// DEBUG_MSG("FIXME, crufty shutdown of mesh bluetooth for sw update.");
// void stopMeshBluetoothService();
// stopMeshBluetoothService();
if (RadioLibInterface::instance)
RadioLibInterface::instance->sleep(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we are
// writing flash - shut the radio off during updates
}
if (RadioLibInterface::instance)
RadioLibInterface::instance->sleep(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we are
// writing flash - shut the radio off during updates
}
};
}
#define MAX_BLOCKSIZE 512
class DataCharacteristic : public CallbackCharacteristic
/// Handle writes to data
int totalSize_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
public:
DataCharacteristic() : CallbackCharacteristic("e272ebac-d463-4b98-bc84-5cc1a39ee517", BLECharacteristic::PROPERTY_WRITE) {}
concurrency::LockGuard g(updateLock);
std::string value = c->getValue();
uint32_t len = value.length();
assert(len <= MAX_BLOCKSIZE);
static uint8_t
data[MAX_BLOCKSIZE]; // we temporarily copy here because I'm worried that a fast sender might be able overwrite srcbuf
memcpy(data, c->getData(), len);
// DEBUG_MSG("Writing %u\n", len);
crc.update(data, len);
Update.write(data, len);
updateActualSize += len;
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); // Not exactly correct, but we want to force the device to not sleep now
}
void onWrite(BLECharacteristic *c)
{
concurrency::LockGuard g(updateLock);
std::string value = c->getValue();
uint32_t len = value.length();
assert(len <= MAX_BLOCKSIZE);
static uint8_t
data[MAX_BLOCKSIZE]; // we temporarily copy here because I'm worried that a fast sender might be able overwrite srcbuf
memcpy(data, c->getData(), len);
// DEBUG_MSG("Writing %u\n", len);
crc.update(data, len);
Update.write(data, len);
updateActualSize += len;
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); // Not exactly correct, but we want to force the device to not sleep now
}
};
static BLECharacteristic *resultC;
class CRC32Characteristic : public CallbackCharacteristic
/// Handle writes to crc32
int totalSize_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
public:
CRC32Characteristic() : CallbackCharacteristic("4826129c-c22a-43a3-b066-ce8f0d5bacc6", BLECharacteristic::PROPERTY_WRITE) {}
concurrency::LockGuard g(updateLock);
uint32_t expectedCRC = getValue32(c, 0);
uint32_t actualCRC = crc.finalize();
DEBUG_MSG("expected CRC %u\n", expectedCRC);
void onWrite(BLECharacteristic *c)
uint8_t result = 0xff;
if (updateActualSize != updateExpectedSize) {
DEBUG_MSG("Expected %u bytes, but received %u bytes!\n", updateExpectedSize, updateActualSize);
result = 0xe1; // FIXME, use real error codes
} else if (actualCRC != expectedCRC) // Check the CRC before asking the update to happen.
{
concurrency::LockGuard g(updateLock);
uint32_t expectedCRC = getValue32(c, 0);
uint32_t actualCRC = crc.finalize();
DEBUG_MSG("expected CRC %u\n", expectedCRC);
uint8_t result = 0xff;
if (updateActualSize != updateExpectedSize) {
DEBUG_MSG("Expected %u bytes, but received %u bytes!\n", updateExpectedSize, updateActualSize);
result = 0xe1; // FIXME, use real error codes
} else if (actualCRC != expectedCRC) // Check the CRC before asking the update to happen.
{
DEBUG_MSG("Invalid CRC! expected=%u, actual=%u\n", expectedCRC, actualCRC);
result = 0xe0; // FIXME, use real error codes
DEBUG_MSG("Invalid CRC! expected=%u, actual=%u\n", expectedCRC, actualCRC);
result = 0xe0; // FIXME, use real error codes
} else {
if (Update.end()) {
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
rebootAtMsec = timing::millis() + 5000;
} else {
if (Update.end()) {
DEBUG_MSG("OTA done, rebooting in 5 seconds!\n");
rebootAtMsec = timing::millis() + 5000;
} else {
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
}
result = Update.getError();
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
}
if (RadioLibInterface::instance)
RadioLibInterface::instance->startReceive(); // Resume radio
assert(resultC);
resultC->setValue(&result, 1);
resultC->notify();
result = Update.getError();
}
};
if (RadioLibInterface::instance)
RadioLibInterface::instance->startReceive(); // Resume radio
assert(resultC);
resultC->setValue(&result, 1);
resultC->notify();
}
void bluetoothRebootCheck()
{
@ -138,47 +114,8 @@ void bluetoothRebootCheck()
See bluetooth-api.md
*/
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion)
void createUpdateService()
{
if (!updateLock)
updateLock = new concurrency::Lock();
// Create the BLE Service
BLEService *service = server->createService(BLEUUID("cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"), 25, 0);
assert(!resultC);
resultC = new BLECharacteristic("5e134862-7411-4424-ac4a-210937432c77",
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
addWithDesc(service, new TotalSizeCharacteristic, "total image size");
addWithDesc(service, new DataCharacteristic, "data");
addWithDesc(service, new CRC32Characteristic, "crc32");
addWithDesc(service, resultC, "result code");
resultC->addDescriptor(addBLEDescriptor(new BLE2902())); // Needed so clients can request notification
BLECharacteristic *swC =
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_SW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
swC->setValue(swVersion);
service->addCharacteristic(addBLECharacteristic(swC));
BLECharacteristic *mfC = new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_MANU_NAME), BLECharacteristic::PROPERTY_READ);
mfC->setValue(hwVendor);
service->addCharacteristic(addBLECharacteristic(mfC));
BLECharacteristic *hwvC =
new BLECharacteristic(BLEUUID((uint16_t)ESP_GATT_UUID_HW_VERSION_STR), BLECharacteristic::PROPERTY_READ);
hwvC->setValue(hwVersion);
service->addCharacteristic(addBLECharacteristic(hwvC));
return service;
}
void destroyUpdateService()
{
assert(resultC);
resultC = NULL;
}
#endif

View File

@ -1,15 +1,26 @@
#pragma once
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include "nimble/NimbleDefs.h"
#ifdef CONFIG_BLUEDROID_ENABLED
void createUpdateService();
BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion);
void destroyUpdateService();
void bluetoothRebootCheck();
#ifdef __cplusplus
extern "C" {
#endif
int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
int update_result_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
extern const struct ble_gatt_svc_def gatt_update_svcs[];
extern const ble_uuid128_t update_result_uuid;
extern int16_t updateResultHandle;
#ifdef __cplusplus
};
#endif

View File

@ -0,0 +1,61 @@
#include "BluetoothSoftwareUpdate.h"
// NRF52 wants these constants as byte arrays
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
// "cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"
const ble_uuid128_t update_service_uuid =
BLE_UUID128_INIT(0x30, 0xee, 0x44, 0x31, 0x2e, 0x44, 0xbb, 0xbd, 0x0d, 0x4c, 0x4c, 0xa8, 0x0b, 0x9a, 0x0b, 0xcb);
// "e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e" write|read
const ble_uuid128_t update_size_uuid =
BLE_UUID128_INIT(0x1e, 0x8e, 0xea, 0xdb, 0xe1, 0xf0, 0xa1, 0x95, 0x6f, 0x4a, 0x01, 0xa3, 0xc0, 0xd9, 0x4d, 0xe7);
// "e272ebac-d463-4b98-bc84-5cc1a39ee517" write
const ble_uuid128_t update_data_uuid =
BLE_UUID128_INIT(0x17, 0xe5, 0x9e, 0xa3, 0xc1, 0x5c, 0x84, 0xbc, 0x98, 0x4b, 0x63, 0xd4, 0xac, 0xeb, 0x72, 0xe2);
// "4826129c-c22a-43a3-b066-ce8f0d5bacc6" write
const ble_uuid128_t update_crc32_uuid =
BLE_UUID128_INIT(0xc6, 0xac, 0x5b, 0x0d, 0x8f, 0xce, 0x66, 0xb0, 0xa3, 0x43, 0x2a, 0xc2, 0x9c, 0x12, 0x26, 0x48);
// "5e134862-7411-4424-ac4a-210937432c77" read|notify
const ble_uuid128_t update_result_uuid =
BLE_UUID128_INIT(0x77, 0x2c, 0x43, 0x37, 0x09, 0x21, 0x4a, 0xac, 0x24, 0x44, 0x11, 0x74, 0x62, 0x48, 0x13, 0x5e);
const struct ble_gatt_svc_def gatt_update_svcs[] = {
{
/*** Service: Security test. */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &update_service_uuid.u,
.characteristics =
(struct ble_gatt_chr_def[]){{
.uuid = &update_size_uuid.u,
.access_cb = update_size_callback,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN | BLE_GATT_CHR_F_READ |
BLE_GATT_CHR_F_READ_AUTHEN,
},
{
.uuid = &update_data_uuid.u,
.access_cb = update_data_callback,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
},
{
.uuid = &update_crc32_uuid.u,
.access_cb = update_crc32_callback,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
},
{
.uuid = &update_result_uuid.u,
.access_cb = update_size_callback,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_NOTIFY,
},
{
0, /* No more characteristics in this service. */
}},
},
{
0, /* No more services. */
},
};

View File

@ -1,3 +1,4 @@
#include "BluetoothSoftwareUpdate.h"
#include "PowerFSM.h"
#include "configuration.h"
#include "esp_task_wdt.h"
@ -9,7 +10,6 @@
#include <nvs.h>
#include <nvs_flash.h>
void getMacAddr(uint8_t *dmac)
{
assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
@ -82,6 +82,7 @@ void esp32Loop()
{
esp_task_wdt_reset(); // service our app level watchdog
loopBLE();
bluetoothRebootCheck();
// for debug printing
// radio.radioIf.canSleep();

View File

@ -1,15 +1,7 @@
#include "NimbleDefs.h"
// A C++ version of BLE_UUID128_INIT
#define BLE_UUID128_INIT_CPP(uuid128...) \
{ \
u : { \
type: \
BLE_UUID_TYPE_128 \
} \
, value: { uuid128 } \
}
// NRF52 wants these constants as byte arrays
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
const ble_uuid128_t mesh_service_uuid =
BLE_UUID128_INIT(0xfd, 0xea, 0x73, 0xe2, 0xca, 0x5d, 0xa8, 0x9f, 0x1f, 0x46, 0xa8, 0x15, 0x18, 0xb2, 0xa1, 0x6b);
@ -29,7 +21,6 @@ const struct ble_gatt_svc_def gatt_svr_svcs[] = {
.uuid = &mesh_service_uuid.u,
.characteristics =
(struct ble_gatt_chr_def[]){{
// FIXME - remove non ENC access
.uuid = &toradio_uuid.u,
.access_cb = toradio_callback,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,