#include "BluetoothSoftwareUpdate.h" #include "BluetoothUtil.h" #include "CallbackCharacteristic.h" #include "RadioLibInterface.h" #include "configuration.h" #include "lock.h" #include #include #include #include #include using namespace meshtastic; CRC32 crc; uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes) uint32_t updateExpectedSize, updateActualSize; Lock *updateLock; class TotalSizeCharacteristic : public CallbackCharacteristic { public: TotalSizeCharacteristic() : CallbackCharacteristic("e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e", BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ) { } void onWrite(BLECharacteristic *c) { BLEKeepAliveCallbacks::onWrite(c); 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 } } }; #define MAX_BLOCKSIZE 512 class DataCharacteristic : public CallbackCharacteristic { public: DataCharacteristic() : CallbackCharacteristic("e272ebac-d463-4b98-bc84-5cc1a39ee517", BLECharacteristic::PROPERTY_WRITE) {} void onWrite(BLECharacteristic *c) { BLEKeepAliveCallbacks::onWrite(c); 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; } }; static BLECharacteristic *resultC; class CRC32Characteristic : public CallbackCharacteristic { public: CRC32Characteristic() : CallbackCharacteristic("4826129c-c22a-43a3-b066-ce8f0d5bacc6", BLECharacteristic::PROPERTY_WRITE) {} void onWrite(BLECharacteristic *c) { BLEKeepAliveCallbacks::onWrite(c); 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 } else { if (Update.end()) { DEBUG_MSG("OTA done, rebooting in 5 seconds!\n"); rebootAtMsec = millis() + 5000; } else { DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError()); } result = Update.getError(); } if (RadioLibInterface::instance) RadioLibInterface::instance->startReceive(); // Resume radio assert(resultC); resultC->setValue(&result, 1); resultC->notify(); } }; void bluetoothRebootCheck() { if (rebootAtMsec && millis() > rebootAtMsec) ESP.restart(); } /* See bluetooth-api.md */ BLEService *createUpdateService(BLEServer *server, std::string hwVendor, std::string swVersion, std::string hwVersion) { if (!updateLock) updateLock = new 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; }