[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Felix Biego\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# ESP32_BLE_OTA_Arduino\nOTA update on ESP32 via BLE\n\n- 1,038,544 bytes uploaded in 1min 25sec\n- Speed: ~ `12kb/s peak`\n\n[`esp32_ble_ota`](https://github.com/fbiego/ESP32_BLE_OTA_Arduino/tree/main/esp32_ble_ota) - 1,008,199 bytes\n\n[`esp32_nim_ble_ota`](https://github.com/fbiego/ESP32_BLE_OTA_Arduino/tree/main/esp32_nim_ble_ota) - 563,051 bytes\n\n\n## Android app\n\n<a href='https://play.google.com/store/apps/details?id=com.fbiego.esp32.ota&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height=\"100px\" src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png'/></a>\n\n## Python Script\nUpdate from your computer\n\n[`BLE_OTA_Python`](https://github.com/fbiego/BLE_OTA_Python)\n\n## Video\n[`DIY ESP32 clock with BLE OTA`](https://youtu.be/TU_O4UPm00A) - 12kb/s peak (optimized)\n\n[`ESP32 OTA via BLE`](https://youtu.be/j4ELTS7QXFM) - 3kb/s (old)\n"
  },
  {
    "path": "esp32_ble_ota/esp32_ble_ota.ino",
    "content": "/*\r\n   MIT License\r\n\r\n   Copyright (c) 2021 Felix Biego\r\n\r\n   Permission is hereby granted, free of charge, to any person obtaining a copy\r\n   of this software and associated documentation files (the \"Software\"), to deal\r\n   in the Software without restriction, including without limitation the rights\r\n   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n   copies of the Software, and to permit persons to whom the Software is\r\n   furnished to do so, subject to the following conditions:\r\n\r\n   The above copyright notice and this permission notice shall be included in all\r\n   copies or substantial portions of the Software.\r\n\r\n   THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r\n   SOFTWARE.\r\n*/\r\n\r\n\r\n#include <Update.h>\r\n#include \"FS.h\"\r\n#include \"FFat.h\"\r\n#include \"SPIFFS.h\"\r\n#include <BLEDevice.h>\r\n#include <BLEUtils.h>\r\n#include <BLEServer.h>\r\n#include <BLE2902.h>\r\n\r\n#define BUILTINLED 2\r\n#define FORMAT_SPIFFS_IF_FAILED true\r\n#define FORMAT_FFAT_IF_FAILED true\r\n\r\n#define USE_SPIFFS  //comment to use FFat\r\n\r\n#ifdef USE_SPIFFS\r\n#define FLASH SPIFFS\r\n#define FASTMODE false    //SPIFFS write is slow\r\n#else\r\n#define FLASH FFat\r\n#define FASTMODE true    //FFat is faster\r\n#endif\r\n\r\n#define NORMAL_MODE   0   // normal\r\n#define UPDATE_MODE   1   // receiving firmware\r\n#define OTA_MODE      2   // installing firmware\r\n\r\nuint8_t updater[16384];\r\nuint8_t updater2[16384];\r\n\r\n#define SERVICE_UUID              \"fb1e4001-54ae-4a28-9f74-dfccb248601d\"\r\n#define CHARACTERISTIC_UUID_RX    \"fb1e4002-54ae-4a28-9f74-dfccb248601d\"\r\n#define CHARACTERISTIC_UUID_TX    \"fb1e4003-54ae-4a28-9f74-dfccb248601d\"\r\n\r\nstatic BLECharacteristic* pCharacteristicTX;\r\nstatic BLECharacteristic* pCharacteristicRX;\r\n\r\nstatic bool deviceConnected = false, sendMode = false, sendSize = true;\r\nstatic bool writeFile = false, request = false;\r\nstatic int writeLen = 0, writeLen2 = 0;\r\nstatic bool current = true;\r\nstatic int parts = 0, next = 0, cur = 0, MTU = 0;\r\nstatic int MODE = NORMAL_MODE;\r\nunsigned long rParts, tParts;\r\n\r\nstatic void rebootEspWithReason(String reason) {\r\n  Serial.println(reason);\r\n  delay(1000);\r\n  ESP.restart();\r\n}\r\n\r\nclass MyServerCallbacks: public BLEServerCallbacks {\r\n    void onConnect(BLEServer* pServer) {\r\n      deviceConnected = true;\r\n\r\n    }\r\n    void onDisconnect(BLEServer* pServer) {\r\n      deviceConnected = false;\r\n    }\r\n};\r\n\r\nclass MyCallbacks: public BLECharacteristicCallbacks {\r\n\r\n    //    void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code) {\r\n    //      Serial.print(\"Status \");\r\n    //      Serial.print(s);\r\n    //      Serial.print(\" on characteristic \");\r\n    //      Serial.print(pCharacteristic->getUUID().toString().c_str());\r\n    //      Serial.print(\" with code \");\r\n    //      Serial.println(code);\r\n    //    }\r\n\r\n    void onNotify(BLECharacteristic *pCharacteristic) {\r\n      uint8_t* pData;\r\n      std::string value = pCharacteristic->getValue();\r\n      int len = value.length();\r\n      pData = pCharacteristic->getData();\r\n      if (pData != NULL) {\r\n        //        Serial.print(\"Notify callback for characteristic \");\r\n        //        Serial.print(pCharacteristic->getUUID().toString().c_str());\r\n        //        Serial.print(\" of data length \");\r\n        //        Serial.println(len);\r\n        Serial.print(\"TX  \");\r\n        for (int i = 0; i < len; i++) {\r\n          Serial.printf(\"%02X \", pData[i]);\r\n        }\r\n        Serial.println();\r\n      }\r\n    }\r\n\r\n    void onWrite(BLECharacteristic *pCharacteristic) {\r\n      uint8_t* pData;\r\n      std::string value = pCharacteristic->getValue();\r\n      int len = value.length();\r\n      pData = pCharacteristic->getData();\r\n      if (pData != NULL) {\r\n        //        Serial.print(\"Write callback for characteristic \");\r\n        //        Serial.print(pCharacteristic->getUUID().toString().c_str());\r\n        //        Serial.print(\" of data length \");\r\n        //        Serial.println(len);\r\n        //        Serial.print(\"RX  \");\r\n        //        for (int i = 0; i < len; i++) {         // leave this commented\r\n        //          Serial.printf(\"%02X \", pData[i]);\r\n        //        }\r\n        //        Serial.println();\r\n\r\n        if (pData[0] == 0xFB) {\r\n          int pos = pData[1];\r\n          for (int x = 0; x < len - 2; x++) {\r\n            if (current) {\r\n              updater[(pos * MTU) + x] = pData[x + 2];\r\n            } else {\r\n              updater2[(pos * MTU) + x] = pData[x + 2];\r\n            }\r\n          }\r\n\r\n        } else if  (pData[0] == 0xFC) {\r\n          if (current) {\r\n            writeLen = (pData[1] * 256) + pData[2];\r\n          } else {\r\n            writeLen2 = (pData[1] * 256) + pData[2];\r\n          }\r\n          current = !current;\r\n          cur = (pData[3] * 256) + pData[4];\r\n          writeFile = true;\r\n          if (cur < parts - 1) {\r\n            request = !FASTMODE;\r\n          }\r\n        } else if (pData[0] == 0xFD) {\r\n          sendMode = true;\r\n          if (FLASH.exists(\"/update.bin\")) {\r\n            FLASH.remove(\"/update.bin\");\r\n          }\r\n        } else if (pData[0] == 0xFE) {\r\n          rParts = 0;\r\n          tParts = (pData[1] * 256 * 256 * 256) + (pData[2] * 256 * 256) + (pData[3] * 256) + pData[4];\r\n\r\n          Serial.print(\"Available space: \");\r\n          Serial.println(FLASH.totalBytes() - FLASH.usedBytes());\r\n          Serial.print(\"File Size: \");\r\n          Serial.println(tParts);\r\n\r\n        } else if  (pData[0] == 0xFF) {\r\n          parts = (pData[1] * 256) + pData[2];\r\n          MTU = (pData[3] * 256) + pData[4];\r\n          MODE = UPDATE_MODE;\r\n\r\n        } else if (pData[0] == 0xEF) {\r\n          FLASH.format();\r\n          sendSize = true;\r\n        }\r\n\r\n\r\n      }\r\n\r\n    }\r\n\r\n\r\n};\r\n\r\nstatic void writeBinary(fs::FS &fs, const char * path, uint8_t *dat, int len) {\r\n\r\n  //Serial.printf(\"Write binary file %s\\r\\n\", path);\r\n\r\n  File file = fs.open(path, FILE_APPEND);\r\n\r\n  if (!file) {\r\n    Serial.println(\"- failed to open file for writing\");\r\n    return;\r\n  }\r\n  file.write(dat, len);\r\n  file.close();\r\n  writeFile = false;\r\n  rParts += len;\r\n}\r\n\r\nvoid sendOtaResult(String result) {\r\n  pCharacteristicTX->setValue(result.c_str());\r\n  pCharacteristicTX->notify();\r\n  delay(200);\r\n}\r\n\r\n\r\nvoid performUpdate(Stream &updateSource, size_t updateSize) {\r\n  char s1 = 0x0F;\r\n  String result = String(s1);\r\n  if (Update.begin(updateSize)) {\r\n    size_t written = Update.writeStream(updateSource);\r\n    if (written == updateSize) {\r\n      Serial.println(\"Written : \" + String(written) + \" successfully\");\r\n    }\r\n    else {\r\n      Serial.println(\"Written only : \" + String(written) + \"/\" + String(updateSize) + \". Retry?\");\r\n    }\r\n    result += \"Written : \" + String(written) + \"/\" + String(updateSize) + \" [\" + String((written / updateSize) * 100) + \"%] \\n\";\r\n    if (Update.end()) {\r\n      Serial.println(\"OTA done!\");\r\n      result += \"OTA Done: \";\r\n      if (Update.isFinished()) {\r\n        Serial.println(\"Update successfully completed. Rebooting...\");\r\n        result += \"Success!\\n\";\r\n      }\r\n      else {\r\n        Serial.println(\"Update not finished? Something went wrong!\");\r\n        result += \"Failed!\\n\";\r\n      }\r\n\r\n    }\r\n    else {\r\n      Serial.println(\"Error Occurred. Error #: \" + String(Update.getError()));\r\n      result += \"Error #: \" + String(Update.getError());\r\n    }\r\n  }\r\n  else\r\n  {\r\n    Serial.println(\"Not enough space to begin OTA\");\r\n    result += \"Not enough space for OTA\";\r\n  }\r\n  if (deviceConnected) {\r\n    sendOtaResult(result);\r\n    delay(5000);\r\n  }\r\n}\r\n\r\nvoid updateFromFS(fs::FS &fs) {\r\n  File updateBin = fs.open(\"/update.bin\");\r\n  if (updateBin) {\r\n    if (updateBin.isDirectory()) {\r\n      Serial.println(\"Error, update.bin is not a file\");\r\n      updateBin.close();\r\n      return;\r\n    }\r\n\r\n    size_t updateSize = updateBin.size();\r\n\r\n    if (updateSize > 0) {\r\n      Serial.println(\"Trying to start update\");\r\n      performUpdate(updateBin, updateSize);\r\n    }\r\n    else {\r\n      Serial.println(\"Error, file is empty\");\r\n    }\r\n\r\n    updateBin.close();\r\n\r\n    // when finished remove the binary from spiffs to indicate end of the process\r\n    Serial.println(\"Removing update file\");\r\n    fs.remove(\"/update.bin\");\r\n\r\n    rebootEspWithReason(\"Rebooting to complete OTA update\");\r\n  }\r\n  else {\r\n    Serial.println(\"Could not load update.bin from spiffs root\");\r\n  }\r\n}\r\n\r\nvoid initBLE() {\r\n  BLEDevice::init(\"ESP32 OTA\");\r\n  BLEServer *pServer = BLEDevice::createServer();\r\n  pServer->setCallbacks(new MyServerCallbacks());\r\n\r\n  BLEService *pService = pServer->createService(SERVICE_UUID);\r\n  pCharacteristicTX = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY );\r\n  pCharacteristicRX = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);\r\n  pCharacteristicRX->setCallbacks(new MyCallbacks());\r\n  pCharacteristicTX->setCallbacks(new MyCallbacks());\r\n  pCharacteristicTX->addDescriptor(new BLE2902());\r\n  pCharacteristicTX->setNotifyProperty(true);\r\n  pService->start();\r\n\r\n\r\n  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility\r\n  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();\r\n  pAdvertising->addServiceUUID(SERVICE_UUID);\r\n  pAdvertising->setScanResponse(true);\r\n  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue\r\n  pAdvertising->setMinPreferred(0x12);\r\n  BLEDevice::startAdvertising();\r\n  Serial.println(\"Characteristic defined! Now you can read it in your phone!\");\r\n}\r\n\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n  Serial.println(\"Starting BLE OTA sketch\");\r\n  pinMode(BUILTINLED, OUTPUT);\r\n\r\n#ifdef USE_SPIFFS\r\n  if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) {\r\n    Serial.println(\"SPIFFS Mount Failed\");\r\n    return;\r\n  }\r\n#else\r\n  if (!FFat.begin()) {\r\n    Serial.println(\"FFat Mount Failed\");\r\n    if (FORMAT_FFAT_IF_FAILED) FFat.format();\r\n    return;\r\n  }\r\n#endif\r\n\r\n\r\n  initBLE();\r\n\r\n}\r\n\r\nvoid loop() {\r\n\r\n  switch (MODE) {\r\n\r\n    case NORMAL_MODE:\r\n      if (deviceConnected) {\r\n        digitalWrite(BUILTINLED, HIGH);\r\n        if (sendMode) {\r\n          uint8_t fMode[] = {0xAA, FASTMODE};\r\n          pCharacteristicTX->setValue(fMode, 2);\r\n          pCharacteristicTX->notify();\r\n          delay(50);\r\n          sendMode = false;\r\n        }\r\n\r\n        if (sendSize) {\r\n          unsigned long x = FLASH.totalBytes();\r\n          unsigned long y = FLASH.usedBytes();\r\n          uint8_t fSize[] = {0xEF, (uint8_t) (x >> 16), (uint8_t) (x >> 8), (uint8_t) x, (uint8_t) (y >> 16), (uint8_t) (y >> 8), (uint8_t) y};\r\n          pCharacteristicTX->setValue(fSize, 7);\r\n          pCharacteristicTX->notify();\r\n          delay(50);\r\n          sendSize = false;\r\n        }\r\n\r\n        // your loop code here\r\n      } else {\r\n        digitalWrite(BUILTINLED, LOW);\r\n      }\r\n\r\n      // or here\r\n\r\n      break;\r\n\r\n    case UPDATE_MODE:\r\n\r\n      if (request) {\r\n        uint8_t rq[] = {0xF1, (cur + 1) / 256, (cur + 1) % 256};\r\n        pCharacteristicTX->setValue(rq, 3);\r\n        pCharacteristicTX->notify();\r\n        delay(50);\r\n        request = false;\r\n      }\r\n\r\n      if (cur + 1 == parts) { // received complete file\r\n        uint8_t com[] = {0xF2, (cur + 1) / 256, (cur + 1) % 256};\r\n        pCharacteristicTX->setValue(com, 3);\r\n        pCharacteristicTX->notify();\r\n        delay(50);\r\n        MODE = OTA_MODE;\r\n      }\r\n\r\n      if (writeFile) {\r\n        if (!current) {\r\n          writeBinary(FLASH, \"/update.bin\", updater, writeLen);\r\n        } else {\r\n          writeBinary(FLASH, \"/update.bin\", updater2, writeLen2);\r\n        }\r\n      }\r\n\r\n      break;\r\n\r\n    case OTA_MODE:\r\n\r\n      if (writeFile) {\r\n        if (!current) {\r\n          writeBinary(FLASH, \"/update.bin\", updater, writeLen);\r\n        } else {\r\n          writeBinary(FLASH, \"/update.bin\", updater2, writeLen2);\r\n        }\r\n      }\r\n\r\n\r\n      if (rParts == tParts) {\r\n        Serial.println(\"Complete\");\r\n        delay(5000);\r\n        updateFromFS(FLASH);\r\n      } else {\r\n        writeFile = true;\r\n        Serial.println(\"Incomplete\");\r\n        Serial.print(\"Expected: \");\r\n        Serial.print(tParts);\r\n        Serial.print(\"Received: \");\r\n        Serial.println(rParts);\r\n        delay(2000);\r\n      }\r\n      break;\r\n\r\n  }\r\n\r\n}\r\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/.gitignore",
    "content": ".pio\n.vscode/.browse.c_cpp.db*\n.vscode/c_cpp_properties.json\n.vscode/launch.json\n.vscode/ipch\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/.vscode/extensions.json",
    "content": "{\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\n    // for the documentation about the extensions.json format\n    \"recommendations\": [\n        \"platformio.platformio-ide\"\n    ],\n    \"unwantedRecommendations\": [\n        \"ms-vscode.cpptools-extension-pack\"\n    ]\n}\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/.vscode/settings.json",
    "content": "{\n    \"cSpell.words\": [\n        \"LOGD\",\n        \"LOGI\",\n        \"LOGW\",\n        \"ledc\",\n        \"LEDC\",\n    ]\n}"
  },
  {
    "path": "esp32_ble_ota_lib_compact/README.md",
    "content": "# Compact version of the BLE OTA example\n\nThis directory contains a PlatformIO project which implement a compact version of the OTA. It also provides a library (in the lib folder) that allows to easily port BLE OTA to your own code.\n\n**Note:** this version of the code is not compatible with the mobile app for the moment (the update mechanism is not exactly the same).\n\nFurthermore, to use the FFAT mode you need to use another partition table (which you must configure in `platformio.ini`).\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/discover.py",
    "content": "import asyncio\nfrom bleak import BleakScanner\n\n\nasync def run():\n    for device in await BleakScanner.discover():\n        print(f'{device.name} - {device.address}')\n\nif __name__ == '__main__':\n    try:\n        asyncio.run(run())\n    except KeyboardInterrupt:\n        pass\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/include/README",
    "content": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro definitions\nto be shared between several project source files. You request the use of a\nheader file in your project source file (C, C++, etc) located in `src` folder\nby including it, with the C preprocessing directive `#include'.\n\n```src/main.c\n\n#include \"header.h\"\n\nint main (void)\n{\n ...\n}\n```\n\nIncluding a header file produces the same results as copying the header file\ninto each source file that needs it. Such copying would be time-consuming\nand error-prone. With a header file, the related declarations appear\nin only one place. If they need to be changed, they can be changed in one\nplace, and programs that include the header file will automatically use the\nnew version when next recompiled. The header file eliminates the labor of\nfinding and changing all the copies as well as the risk that a failure to\nfind one copy will result in inconsistencies within a program.\n\nIn C, the usual convention is to give header files names that end with `.h'.\nIt is most portable to use only letters, digits, dashes, and underscores in\nheader file names, and at most one dot.\n\nRead more about using header files in official GCC documentation:\n\n* Include Syntax\n* Include Operation\n* Once-Only Headers\n* Computed Includes\n\nhttps://gcc.gnu.org/onlinedocs/cpp/Header-Files.html\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/lib/README",
    "content": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries and link into executable file.\n\nThe source code of each library should be placed in a an own separate directory\n(\"lib/your_library_name/[here are source files]\").\n\nFor example, see a structure of the following two libraries `Foo` and `Bar`:\n\n|--lib\n|  |\n|  |--Bar\n|  |  |--docs\n|  |  |--examples\n|  |  |--src\n|  |     |- Bar.c\n|  |     |- Bar.h\n|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html\n|  |\n|  |--Foo\n|  |  |- Foo.c\n|  |  |- Foo.h\n|  |\n|  |- README --> THIS FILE\n|\n|- platformio.ini\n|--src\n   |- main.c\n\nand a contents of `src/main.c`:\n```\n#include <Foo.h>\n#include <Bar.h>\n\nint main (void)\n{\n  ...\n}\n\n```\n\nPlatformIO Library Dependency Finder will find automatically dependent\nlibraries scanning project source files.\n\nMore information about PlatformIO Library Dependency Finder\n- https://docs.platformio.org/page/librarymanager/ldf.html\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/lib/ble_ota_dfu/keywords.txt",
    "content": "﻿# To be filled if you want to use it on Arduino    \n#FUNCTIONS COLOR \t\t\t#D35400 - ORANGE\t\t\tKEYWORD1\n#FUNCTIONS COLOR\t\t\t#D35400 - ORANGE\t\t\tKEYWORD2\n#STRUCTURE COLOR\t \t\t#728E00 - GREEN\t\t\t\tKEYWORD3\n#VARIABLES COLOR \t\t\t#00979C - BLUE\t\t\t\tLITERAL1\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/lib/ble_ota_dfu/library.json",
    "content": "{\n  \"name\": \"ble_ota_dfuU\",\n  \"keywords\": \"BLE OTA DFU\",\n  \"description\": \"BLE OTA DFU\",\n  \"repository\":\n  {\n    \"type\": \"email\",\n    \"url\": \"vincent.stragier@outlook.com\"\n  },\n  \"version\": \"1.0.0\",\n  \"exclude\": \"doc\",\n  \"frameworks\": \"arduino\",\n  \"platforms\": [\"esp32\"]\n}\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/lib/ble_ota_dfu/library.properties",
    "content": "name=ble_ota_dfu\nversion=1.0.0\nauthor=Vincent STRAGIER\nmaintainer=Vincent STRAGIER\nsentence=BLE OTA DFU\nparagraph=BLE OTA DFU\ncategory=Other\nurl=mailto:vincent.stragier@outlook.com\narchitectures=esp32"
  },
  {
    "path": "esp32_ble_ota_lib_compact/lib/ble_ota_dfu/src/ble_ota_dfu.cpp",
    "content": "/* Copyright 2022 Vincent Stragier */\n#include \"ble_ota_dfu.hpp\"\n\nQueueHandle_t start_update_queue;\nQueueHandle_t update_uploading_queue;\n\nvoid task_install_update(void *parameters) {\n  FS file_system = FLASH;\n  const char path[] = \"/update.bin\";\n  uint8_t data = 0;\n  BLE_OTA_DFU *OTA_DFU_BLE;\n  OTA_DFU_BLE = reinterpret_cast<BLE_OTA_DFU *>(parameters);\n  delay(100);\n\n  // Wait for the upload to be completed\n  bool start_update = false;\n  xQueuePeek(start_update_queue, &start_update, portMAX_DELAY);\n  while (!start_update) {\n    delay(500);\n    xQueuePeek(start_update_queue, &start_update, portMAX_DELAY);\n  }\n\n  ESP_LOGE(TAG, \"Starting OTA update\");\n\n  // Open update.bin file.\n  File update_binary = FLASH.open(path);\n\n  // If the file cannot be loaded, return.\n  if (!update_binary) {\n    ESP_LOGE(TAG, \"Could not load update.bin from spiffs root\");\n    vTaskDelete(NULL);\n  }\n\n  // Verify that the file is not a directory\n  if (update_binary.isDirectory()) {\n    ESP_LOGE(TAG, \"Error, update.bin is not a file\");\n    update_binary.close();\n    vTaskDelete(NULL);\n  }\n\n  // Get binary file size\n  size_t update_size = update_binary.size();\n\n  // Proceed to the update if the file is not empty\n  if (update_size <= 0) {\n    ESP_LOGE(TAG, \"Error, update file is empty\");\n    update_binary.close();\n    vTaskDelete(NULL);\n  }\n\n  ESP_LOGI(TAG, \"Starting the update\");\n  // perform_update(update_binary, update_size);\n  String result = (String) static_cast<char>(0x0F);\n\n  // Init update\n  if (Update.begin(update_size)) {\n    // Perform the update\n    size_t written = Update.writeStream(update_binary);\n\n    ESP_LOGI(TAG, \"Written: %d/%d. %s\", written, update_size,\n             written == update_size ? \"Success!\" : \"Retry?\");\n    result += \"Written : \" + String(written) + \"/\" + String(update_size) +\n              \" [\" + String((written / update_size) * 100) + \" %] \\n\";\n\n    // Check update\n    if (Update.end()) {\n      ESP_LOGI(TAG, \"OTA done!\");\n      result += \"OTA Done: \";\n\n      if (Update.isFinished()) {\n        ESP_LOGI(TAG, \"Update successfully completed. Rebooting...\");\n      } else {\n        ESP_LOGE(TAG, \"Update not finished? Something went wrong!\");\n      }\n\n      result += Update.isFinished() ? \"Success!\\n\" : \"Failed!\\n\";\n    } else {\n      ESP_LOGE(TAG, \"Error Occurred. Error #: %d\", Update.getError());\n      result += \"Error #: \" + String(Update.getError());\n    }\n  } else {\n    ESP_LOGE(TAG, \"Not enough space to begin BLE OTA DFU\");\n    result += \"Not enough space to begin BLE OTA DFU\";\n  }\n\n  update_binary.close();\n\n  // When finished remove the binary from spiffs\n  // to indicate the end of the process\n  ESP_LOGI(TAG, \"Removing update file\");\n  FLASH.remove(path);\n\n  if (OTA_DFU_BLE->connected()) {\n    // Return the result to the client (tells the client if the update was a\n    // successfull or not)\n    ESP_LOGI(TAG, \"Sending result to client\");\n    OTA_DFU_BLE->send_OTA_DFU(result);\n    // OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->setValue(result);\n    // OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->notify();\n    ESP_LOGE(TAG, \"%s\", result.c_str());\n    ESP_LOGI(TAG, \"Result sent to client\");\n    delay(5000);\n  }\n\n  ESP_LOGE(TAG, \"Rebooting ESP32: complete OTA update\");\n  delay(5000);\n  ESP.restart();\n\n  // ESP_LOGI(TAG, \"Installation is complete\");\n  vTaskDelete(NULL);\n}\n\n//    void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t\n//    code) {\n//      Serial.print(\"Status \");\n//      Serial.print(s);\n//      Serial.print(\" on characteristic \");\n//      Serial.print(pCharacteristic->getUUID().toString().c_str());\n//      Serial.print(\" with code \");\n//      Serial.println(code);\n//    }\n\nuint16_t BLEOverTheAirDeviceFirmwareUpdate::write_binary(fs::FS *file_system,\n                                                         const char *path,\n                                                         uint8_t *data,\n                                                         uint16_t length,\n                                                         bool keep_open) {\n  static File file;\n\n  // Append data to the file\n\n  if (!file_open) {\n    ESP_LOGI(TAG, \"Opening binary file %s\\r\\n\", path);\n    file = file_system->open(path, FILE_WRITE);\n    file_open = true;\n  }\n\n  if (!file) {\n    ESP_LOGE(TAG, \"Failed to open the file to write\");\n    return 0;\n  }\n\n  if (data != nullptr) {\n    ESP_LOGI(TAG, \"Write binary file %s\\r\\n\", path);\n    file.write(data, length);\n  }\n\n  if (keep_open) {\n    return length;\n  } else {\n    file.close();\n    file_open = false;\n    return 0;\n  }\n}\n\nvoid BLEOverTheAirDeviceFirmwareUpdate::onNotify(\n    BLECharacteristic *pCharacteristic) {\n#ifdef DEBUG_BLE_OTA_DFU_TX\n  // uint8_t *pData;\n  std::string value = pCharacteristic->getValue();\n  uint16_t len = value.length();\n  // pData = pCharacteristic->getData();\n  uint8_t *pData = (uint8_t *)value.data();\n\n  if (pData != NULL) {\n    ESP_LOGD(TAG, \"Notify callback for characteristic %s  of data length %d\",\n             pCharacteristic->getUUID().toString().c_str(), len);\n\n    // Print transferred packets\n    Serial.print(\"TX  \");\n    for (uint16_t i = 0; i < len; i++) {\n      Serial.printf(\"%02X \", pData[i]);\n    }\n    Serial.println();\n  }\n#endif\n}\n\nvoid BLEOverTheAirDeviceFirmwareUpdate::onWrite(\n    BLECharacteristic *pCharacteristic) {\n  // uint8_t *pData;\n  std::string value = pCharacteristic->getValue();\n  uint16_t len = value.length();\n  // pData = pCharacteristic->getData();\n  uint8_t *pData = (uint8_t *)value.data();\n\n  // Check that data have been received\n  if (pData != NULL) {\n// #define DEBUG_BLE_OTA_DFU_RX\n#ifdef DEBUG_BLE_OTA_DFU_RX\n    ESP_LOGD(TAG, \"Write callback for characteristic %s of data length %d\",\n             pCharacteristic->getUUID().toString().c_str(), len);\n    Serial.print(\"RX  \");\n    for (uint16_t index = 0; index < len; index++) {\n      Serial.printf(\"%02X \", pData[i]);\n    }\n    Serial.println();\n#endif\n    switch (pData[0]) {\n      // Send total and used sizes\n    case 0xEF: {\n      FLASH.format();\n\n      // Send flash size\n      uint16_t total_size = FLASH.totalBytes();\n      uint16_t used_size = FLASH.usedBytes();\n      uint8_t flash_size[] = {0xEF,\n                              static_cast<uint8_t>(total_size >> 16),\n                              static_cast<uint8_t>(total_size >> 8),\n                              static_cast<uint8_t>(total_size),\n                              static_cast<uint8_t>(used_size >> 16),\n                              static_cast<uint8_t>(used_size >> 8),\n                              static_cast<uint8_t>(used_size)};\n      OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->setValue(flash_size, 7);\n      OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->notify();\n      delay(10);\n    } break;\n\n      // Write parts to RAM\n    case 0xFB: {\n      // pData[1] is the position of the next part\n      for (uint16_t index = 0; index < len - 2; index++) {\n        updater[!selected_updater][(pData[1] * MTU) + index] = pData[index + 2];\n      }\n    } break;\n\n      // Write updater content to the flash\n    case 0xFC: {\n      selected_updater = !selected_updater;\n      write_len[selected_updater] = (pData[1] * 256) + pData[2];\n      current_progression = (pData[3] * 256) + pData[4];\n\n      received_file_size +=\n          write_binary(&FLASH, \"/update.bin\", updater[selected_updater],\n                       write_len[selected_updater]);\n\n      if ((current_progression < parts - 1) && !FASTMODE) {\n        uint8_t progression[] = {0xF1,\n                                 (uint8_t)((current_progression + 1) / 256),\n                                 (uint8_t)((current_progression + 1) % 256)};\n        OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->setValue(progression, 3);\n        OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->notify();\n        delay(10);\n      }\n\n      ESP_LOGI(TAG, \"Upload progress: %d/%d\", current_progression + 1, parts);\n      if (current_progression + 1 == parts) {\n        // If all the file has been received, send the progression\n        uint8_t progression[] = {0xF2,\n                                 (uint8_t)((current_progression + 1) / 256),\n                                 (uint8_t)((current_progression + 1) % 256)};\n        OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->setValue(progression, 3);\n        OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->notify();\n        delay(10);\n\n        if (received_file_size != expected_file_size) {\n          // received_file_size += (pData[1] * 256) + pData[2];\n          received_file_size +=\n              write_binary(&FLASH, \"/update.bin\", updater[selected_updater],\n                           write_len[selected_updater]);\n\n          if (received_file_size > expected_file_size) {\n            ESP_LOGW(TAG, \"Unexpected size:\\n Expected: %d\\nReceived: %d\",\n                     expected_file_size, received_file_size);\n          }\n\n        } else {\n          ESP_LOGI(TAG, \"Installing update\");\n\n          // Start the installation\n          write_binary(&FLASH, \"/update.bin\", nullptr, 0, false);\n          bool start_update = true;\n          xQueueOverwrite(start_update_queue, &start_update);\n        }\n      }\n    } break;\n\n      // Remove previous file and send transfer mode\n    case 0xFD: {\n      // Remove previous (failed?) update\n      if (FLASH.exists(\"/update.bin\")) {\n        ESP_LOGI(TAG, \"Removing previous update\");\n        FLASH.remove(\"/update.bin\");\n      }\n\n      // Send mode (\"fast\" or \"slow\")\n      uint8_t mode[] = {0xAA, FASTMODE};\n      OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->setValue(mode, 2);\n      OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->notify();\n      delay(10);\n    } break;\n\n      // Keep track of the received file and of the expected file sizes\n    case 0xFE:\n      received_file_size = 0;\n      expected_file_size = (pData[1] * 16777216) + (pData[2] * 65536) +\n                           (pData[3] * 256) + pData[4];\n\n      ESP_LOGI(TAG, \"Available space: %d\\nFile Size: %d\\n\",\n               FLASH.totalBytes() - FLASH.usedBytes(), expected_file_size);\n      break;\n\n      // Switch to update mode\n    case 0xFF:\n      parts = (pData[1] * 256) + pData[2];\n      MTU = (pData[3] * 256) + pData[4];\n      break;\n\n    default:\n      ESP_LOGW(TAG, \"Unknown command: %02X\", pData[0]);\n      break;\n    }\n    // ESP_LOGE(TAG, \"Not stuck in loop\");\n  }\n  delay(1);\n}\n\nbool BLE_OTA_DFU::configure_OTA(NimBLEServer *pServer) {\n  // Init FLASH\n#ifdef USE_SPIFFS\n  if (!SPIFFS.begin(FORMAT_FLASH_IF_MOUNT_FAILED)) {\n    ESP_LOGE(TAG, \"SPIFFS Mount Failed\");\n    return false;\n  }\n  ESP_LOGI(TAG, \"SPIFFS Mounted\");\n#else\n  if (!FFat.begin()) {\n    ESP_LOGE(TAG, \"FFat Mount Failed\");\n    if (FORMAT_FLASH_IF_MOUNT_FAILED)\n      FFat.format();\n    return false;\n  }\n  ESP_LOGI(TAG, \"FFat Mounted\");\n#endif\n\n  // Get the pointer to the pServer\n  this->pServer = pServer;\n  // Create the BLE OTA DFU Service\n  pServiceOTA = pServer->createService(SERVICE_OTA_BLE_UUID);\n\n  if (pServiceOTA == nullptr) {\n    return false;\n  }\n\n  BLECharacteristic *pCharacteristic_BLE_OTA_DFU_RX =\n      pServiceOTA->createCharacteristic(CHARACTERISTIC_OTA_BL_UUID_RX,\n                                        NIMBLE_PROPERTY::WRITE |\n                                            NIMBLE_PROPERTY::WRITE_NR);\n\n  if (pCharacteristic_BLE_OTA_DFU_RX == nullptr) {\n    return false;\n  }\n\n  auto *bleOTACharacteristicCallbacksRX =\n      new BLEOverTheAirDeviceFirmwareUpdate();\n  bleOTACharacteristicCallbacksRX->OTA_DFU_BLE = this;\n  pCharacteristic_BLE_OTA_DFU_RX->setCallbacks(bleOTACharacteristicCallbacksRX);\n\n  pCharacteristic_BLE_OTA_DFU_TX = pServiceOTA->createCharacteristic(\n      CHARACTERISTIC_OTA_BL_UUID_TX, NIMBLE_PROPERTY::NOTIFY);\n\n  if (pCharacteristic_BLE_OTA_DFU_TX == nullptr) {\n    return false;\n  }\n\n  // Start the BLE UART service\n  pServiceOTA->start();\n  return true;\n}\n\nvoid BLE_OTA_DFU::start_OTA() {\n  bool state = false;\n  initialize_queue(&start_update_queue, bool, &state, 1);\n  initialize_queue(&update_uploading_queue, bool, &state, 1);\n  // initialize_empty_queue(&update_queue, uint8_t, UPDATE_QUEUE_SIZE);\n\n  ESP_LOGI(TAG, \"Available heap memory: %d\", ESP.getFreeHeap());\n  // ESP_LOGE(TAG, \"Available stack memory: %d\",\n\n  ESP_LOGI(TAG, \"Available free space: %d\",\n           FLASH.totalBytes() - FLASH.usedBytes());\n  // ESP_LOGE(TAG, \"Available free blocks: %d\", FLASH.blockCount());\n  TaskHandle_t taskInstallUpdate = NULL;\n\n  xTaskCreatePinnedToCoreAndAssert(task_install_update, \"task_install_update\",\n                                   5120, static_cast<void *>(this), 5,\n                                   &taskInstallUpdate, 1);\n\n  ESP_LOGI(TAG, \"Available memory (after task started): %d\", ESP.getFreeHeap());\n}\n\nbool BLE_OTA_DFU::begin(String local_name) {\n  // Create the BLE Device\n  BLEDevice::init(local_name.c_str());\n\n  ESP_LOGI(TAG, \"Starting BLE UART services\");\n\n  // Create the BLE Server\n  pServer = BLEDevice::createServer();\n\n  if (pServer == nullptr) {\n    return false;\n  }\n\n  this->configure_OTA(pServer);\n\n  // Start advertising\n  pServer->getAdvertising()->addServiceUUID(pServiceOTA->getUUID());\n  pServer->getAdvertising()->start();\n\n  this->start_OTA();\n  return true;\n}\n\nbool BLE_OTA_DFU::connected() {\n  // True if connected\n  return pServer->getConnectedCount() > 0;\n}\n\nvoid BLE_OTA_DFU::send_OTA_DFU(uint8_t value) {\n  uint8_t _value = value;\n  this->pCharacteristic_BLE_OTA_DFU_TX->setValue(&_value, 1);\n  this->pCharacteristic_BLE_OTA_DFU_TX->notify();\n}\n\nvoid BLE_OTA_DFU::send_OTA_DFU(uint8_t *value, size_t size) {\n  this->pCharacteristic_BLE_OTA_DFU_TX->setValue(value, size);\n  this->pCharacteristic_BLE_OTA_DFU_TX->notify();\n}\n\nvoid BLE_OTA_DFU::send_OTA_DFU(String value) {\n  this->pCharacteristic_BLE_OTA_DFU_TX->setValue(value.c_str());\n  this->pCharacteristic_BLE_OTA_DFU_TX->notify();\n}\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/lib/ble_ota_dfu/src/ble_ota_dfu.hpp",
    "content": "/* Copyright 2022 Vincent Stragier */\n// Highly inspired by https://github.com/fbiego/ESP32_BLE_OTA_Arduino\n#pragma once\n#ifndef SRC_BLE_OTA_DFU_HPP_\n#define SRC_BLE_OTA_DFU_HPP_\n\n#include \"./freertos_utils.hpp\"\n#include <Arduino.h>\n#include <FS.h>\n#include <NimBLEDevice.h>\n#include <Update.h>\n#include <string>\n\n// comment to use FFat\n#define USE_SPIFFS\n\n#ifdef USE_SPIFFS\n// SPIFFS write is slower\n#include <SPIFFS.h>\n#define FLASH SPIFFS\nconst bool FASTMODE = false;\n#else\n// FFat is faster\n#include <FFat.h>\n#define FLASH FFat\nconst bool FASTMODE = true;\n#endif\n// #endif\n\nconst char SERVICE_OTA_BLE_UUID[] = \"fe590001-54ae-4a28-9f74-dfccb248601d\";\nconst char CHARACTERISTIC_OTA_BL_UUID_RX[] =\n    \"fe590002-54ae-4a28-9f74-dfccb248601d\";\nconst char CHARACTERISTIC_OTA_BL_UUID_TX[] =\n    \"fe590003-54ae-4a28-9f74-dfccb248601d\";\n\nconst bool FORMAT_FLASH_IF_MOUNT_FAILED = true;\nconst uint32_t UPDATER_SIZE = 20000;\n\n/* Dummy class */\nclass BLE_OTA_DFU;\n\nclass BLEOverTheAirDeviceFirmwareUpdate : public BLECharacteristicCallbacks {\nprivate:\n  bool selected_updater = true;\n  bool file_open = false;\n  uint8_t updater[2][UPDATER_SIZE];\n  uint16_t write_len[2] = {0, 0};\n  uint16_t parts = 0, MTU = 0;\n  uint16_t current_progression = 0;\n  uint32_t received_file_size, expected_file_size;\n\npublic:\n  friend class BLE_OTA_DFU;\n  BLE_OTA_DFU *OTA_DFU_BLE;\n\n  uint16_t write_binary(fs::FS *file_system, const char *path, uint8_t *data,\n                        uint16_t length, bool keep_open = true);\n  void onNotify(BLECharacteristic *pCharacteristic);\n  void onWrite(BLECharacteristic *pCharacteristic);\n};\n\nclass BLE_OTA_DFU {\nprivate:\n  BLEServer *pServer = nullptr;\n  BLEService *pServiceOTA = nullptr;\n  BLECharacteristic *pCharacteristic_BLE_OTA_DFU_TX = nullptr;\n  friend class BLEOverTheAirDeviceFirmwareUpdate;\n\npublic:\n  BLE_OTA_DFU() = default;\n  ~BLE_OTA_DFU() = default;\n\n  bool configure_OTA(NimBLEServer *pServer);\n  void start_OTA();\n\n  bool begin(String local_name);\n\n  bool connected();\n\n  void send_OTA_DFU(uint8_t value);\n  void send_OTA_DFU(uint8_t *value, size_t size);\n  void send_OTA_DFU(String value);\n\n  friend void task_install_update(void *parameters);\n};\n\n#endif /* SRC_BLE_OTA_DFU_HPP_ */\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/lib/ble_ota_dfu/src/freertos_utils.cpp",
    "content": "/* Copyright 2022 Vincent Stragier */\n#include \"freertos_utils.hpp\"\n\nBaseType_t xTaskCreatePinnedToCoreAndAssert(\n    TaskFunction_t pvTaskCode, const char *pcName, uint32_t usStackDepth,\n    void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pvCreatedTask,\n    BaseType_t xCoreID) {\n\n  BaseType_t xReturn =\n      xTaskCreatePinnedToCore(pvTaskCode, pcName, usStackDepth, pvParameters,\n                              uxPriority, pvCreatedTask, xCoreID);\n\n  configASSERT(pvCreatedTask);\n\n  return xReturn;\n}\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/lib/ble_ota_dfu/src/freertos_utils.hpp",
    "content": "/* Copyright 2022 Vincent Stragier */\n#pragma once\n#ifndef SRC_FREERTOS_UTILS_HPP_\n#define SRC_FREERTOS_UTILS_HPP_\n\n#include <Arduino.h>\n\n// Initialize the queue\n#define initialize_queue(queue, T, initial_value, size)                        \\\n  _initialize_queue<T>((queue), (#queue), (size), (initial_value))\n#define initialize_empty_queue(queue, T, size)                                 \\\n  _initialize_queue<T>((queue), (#queue), (size))\n\ntemplate <typename T>\nvoid _initialize_queue(QueueHandle_t *queue, const char *queue_name,\n                       size_t size, T *initial_value = nullptr) {\n  *queue = xQueueCreate(size, sizeof(T));\n\n  ESP_LOGI(TAG, \"Creating the queue \\\"%s\\\"\", queue_name);\n  // Init start update queue\n  if (*queue == NULL || *queue == nullptr) {\n    ESP_LOGE(TAG, \"Error creating the queue \\\"%s\\\"\", queue_name);\n  }\n\n  if (initial_value == nullptr || initial_value == NULL) {\n    return;\n  }\n\n  if (!xQueueSend(*queue, initial_value, portMAX_DELAY)) {\n    ESP_LOGE(TAG, \"Error initializing the queue \\\"%s\\\"\", queue_name);\n  }\n}\n\n// Create a task and assert it's creation\nBaseType_t xTaskCreatePinnedToCoreAndAssert(\n    TaskFunction_t pvTaskCode, const char *pcName, uint32_t usStackDepth,\n    void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pvCreatedTask,\n    BaseType_t xCoreID);\n\n#endif /* SRC_FREERTOS_UTILS_HPP_ */\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/ota_updater.py",
    "content": "from __future__ import print_function\nimport os\nimport asyncio\nimport sys\nimport re\nfrom time import sleep\n\nfrom bleak import BleakClient, BleakScanner\n# from bleak.exc import BleakError\n\nheader = \"\"\"#####################################################################\n    ------------------------BLE OTA update---------------------\n    Arduino code @ https://github.com/fbiego/ESP32_BLE_OTA_Arduino\n#####################################################################\"\"\"\n\nUART_SERVICE_UUID = \"fe590001-54ae-4a28-9f74-dfccb248601d\"\nUART_RX_CHAR_UUID = \"fe590002-54ae-4a28-9f74-dfccb248601d\"\nUART_TX_CHAR_UUID = \"fe590003-54ae-4a28-9f74-dfccb248601d\"\n\nPART = 19000\nMTU = 250\n\nble_ota_dfu_end = False\n\n\nasync def start_ota(ble_address: str, filename: str):\n    device = await BleakScanner.find_device_by_address(ble_address, timeout=20.0)\n    disconnected_event = asyncio.Event()\n    total = 0\n    file_content = None\n    client = None\n\n    def handle_disconnect(_: BleakClient):\n        print(\"Device disconnected !\")\n        disconnected_event.set()\n        sleep(1)\n        # sys.exit(0)\n\n    async def handle_rx(_: int, data: bytearray):\n        # print(f'\\nReceived: {data = }\\n')\n        match data[0]:\n            case 0xAA:\n                print(\"Starting transfer, mode:\", data[1])\n                print_progress_bar(0, total, prefix='Upload progress:',\n                                   suffix='Complete', length=50)\n\n                match data[1]:\n                    case 0:  # Slow mode\n                        # Send first part\n                        await send_part(0, file_content, client)\n                    case 1:  # Fast mode\n                        for index in range(file_parts):\n                            await send_part(index, file_content, client)\n                            print_progress_bar(index + 1, total,\n                                               prefix='Upload progress:',\n                                               suffix='Complete', length=50)\n\n            case 0xF1:  # Send next part and update progress bar\n                next_part_to_send = int.from_bytes(\n                    data[2:3], byteorder='little')\n                # print(\"Next part:\", next_part_to_send, \"\\n\")\n                await send_part(next_part_to_send, file_content, client)\n                print_progress_bar(next_part_to_send + 1, total,\n                                   prefix='Upload progress:',\n                                   suffix='Complete', length=50)\n\n            case 0xF2:  # Install firmware\n                # ins = 'Installing firmware'\n                # print(\"Installing firmware\")\n                pass\n\n            case 0x0F:\n                print(\"OTA result: \", str(data[1:], 'utf-8'))\n                global ble_ota_dfu_end\n                ble_ota_dfu_end = True\n\n    def print_progress_bar(iteration: int, total: int, prefix: str = '', suffix: str = '', decimals: int = 1, length: int = 100, filler: str = '█', print_end: str = \"\\r\"):\n        \"\"\"\n        Call in a loop to create terminal progress bar\n        @params:\n            iteration   - Required  : current iteration (Int)\n            total       - Required  : total iterations (Int)\n            prefix      - Optional  : prefix string (Str)\n            suffix      - Optional  : suffix string (Str)\n            decimals    - Optional  : positive number of decimals in percent complete (Int)\n            length      - Optional  : character length of bar (Int)\n            filler      - Optional  : bar fill character (Str)\n            print_end   - Optional  : end character (e.g. \"\\r\", \"\\r\\n\") (Str)\n        \"\"\"\n        percent = (\"{0:.\" + str(decimals) + \"f}\").format(100 *\n                                                         (iteration / float(total)))\n        filled_length = (length * iteration) // total\n        bar = filler * filled_length + '-' * (length - filled_length)\n        print(f'\\r{prefix} |{bar}| {percent} % {suffix}', end=print_end)\n        # Print new line upon complete\n        if iteration == total:\n            print()\n\n    async def send_part(position: int, data: bytearray, client: BleakClient):\n        start = position * PART\n        end = (position + 1) * PART\n        # print(locals())\n        if len(data) < end:\n            end = len(data)\n\n        data_length = end - start\n        parts = data_length // MTU\n        for part_index in range(parts):\n            to_be_sent = bytearray([0xFB, part_index])\n            for mtu_index in range(MTU):\n                to_be_sent.append(\n                    data[(position*PART)+(MTU * part_index) + mtu_index])\n            await send_data(client, to_be_sent)\n\n        if data_length % MTU:\n            remaining = data_length % MTU\n            to_be_sent = bytearray([0xFB, parts])\n            for index in range(remaining):\n                to_be_sent.append(\n                    data[(position*PART)+(MTU * parts) + index])\n            await send_data(client, to_be_sent)\n\n        await send_data(client, bytearray([0xFC, data_length//256, data_length %\n                                           256, position//256, position % 256]), True)\n\n    async def send_data(client: BleakClient, data: bytearray, response: bool = False):\n        # print(f'{locals()[\"data\"]}')\n        await client.write_gatt_char(UART_RX_CHAR_UUID, data, response)\n\n    if not device:\n        print(\"-----------Failed--------------\")\n        print(f\"Device with address {ble_address} could not be found.\")\n        return\n        #raise BleakError(f\"A device with address {ble_address} could not be found.\")\n\n    async with BleakClient(device, disconnected_callback=handle_disconnect) as local_client:\n        client = local_client\n\n        # Load file\n        print(\"Reading from: \", filename)\n        file_content = open(filename, \"rb\").read()\n        file_parts = -(len(file_content) // -PART)\n        total = file_parts\n        file_length = len(file_content)\n        print(f'File size: {len(file_content)}')\n\n        # Set the UUID of the service you want to connect to and the callback\n        await client.start_notify(UART_TX_CHAR_UUID, handle_rx)\n        await asyncio.sleep(1.0)\n\n        # Send file length\n        await send_data(client, bytearray([0xFE,\n                                           file_length >> 24 & 0xFF,\n                                           file_length >> 16 & 0xFF,\n                                           file_length >> 8 & 0xFF,\n                                           file_length & 0xFF]))\n\n        # Send number of parts and MTU value\n        await send_data(client, bytearray([0xFF,\n                                           file_parts//256,\n                                           file_parts % 256,\n                                           MTU // 256,\n                                           MTU % 256]))\n\n        # Remove previous update and receive transfer mode (start the update)\n        await send_data(client, bytearray([0xFD]))\n\n        # Wait til the update is complete\n        while not ble_ota_dfu_end:\n            await asyncio.sleep(1.0)\n\n        print(\"Waiting for disconnect... \", end=\"\")\n\n        await disconnected_event.wait()\n        print(\"-----------Complete--------------\")\n\n\ndef is_valid_address(value: str = None) -> bool:\n    # Regex to check valid MAC address\n    regex_0 = (r\"^([0-9A-Fa-f]{2}[:-])\"\n               r\"{5}([0-9A-Fa-f]{2})|\"\n               r\"([0-9a-fA-F]{4}\\\\.\"\n               r\"[0-9a-fA-F]{4}\\\\.\"\n               r\"[0-9a-fA-F]{4}){17}$\")\n    regex_1 = (r\"^[{]?[0-9a-fA-F]{8}\"\n               r\"-([0-9a-fA-F]{4}-)\"\n               r\"{3}[0-9a-fA-F]{12}[}]?$\")\n\n    # Compile the ReGex\n    regex_0 = re.compile(regex_0)\n    regex_1 = re.compile(regex_1)\n\n    # If the string is empty return false\n    if value is None:\n        return False\n\n    # Return if the string matched the ReGex\n    if re.search(regex_0, value) and len(value) == 17:\n        return True\n\n    return re.search(regex_1, value) and len(value) == 36\n\n\nif __name__ == \"__main__\":\n    print(header)\n    # Check if the user has entered enough arguments\n    # sys.argv.append(\"C8:C9:A3:D2:60:8E\")\n    # sys.argv.append(\"firmware.bin\")\n\n    if len(sys.argv) < 3:\n        print(\"Specify the device address and firmware file\")\n        import sys\n        import os\n        filename = os.path.join(os.path.dirname(\n            __file__), '.pio', 'build', 'esp32doit-devkit-v1', 'firmware.bin')\n        filename = filename if os.path.exists(filename) else \"firmware.bin\"\n        print(\n            f\"$ {sys.executable} \\\"{__file__}\\\" \\\"C8:C9:A3:D2:60:8E\\\" \\\"{filename}\\\"\")\n        exit(1)\n\n    print(\"Trying to start OTA update\")\n    ble_address = sys.argv[1]\n    filename = sys.argv[2]\n\n    # Check if the address is valid\n    if not is_valid_address(ble_address):\n        print(f\"Invalid Address: {ble_address}\")\n        exit(2)\n\n    # Check if the file exists\n    if not os.path.exists(filename):\n        print(f\"File not found: {filename}\")\n        exit(3)\n\n    try:\n        # Start the OTA update\n        asyncio.run(start_ota(ble_address, filename))\n    except KeyboardInterrupt:\n        print(\"\\nExiting...\")\n        exit(0)\n    except OSError:\n        print(\"\\nExiting (OSError)...\")\n        exit(1)\n    except Exception:\n        import traceback\n        traceback.print_exc()\n        exit(2)\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/platformio.ini",
    "content": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom upload port, speed and extra flags\n;   Library options: dependencies, extra library storages\n;   Advanced options: extra scripting\n;\n; Please visit documentation for the other options and examples\n; https://docs.platformio.org/page/projectconf.html\n\n[env:esp32doit-devkit-v1]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\n; ESP_LOGE - error (lowest) = 1\n; ESP_LOGW - warning = 2\n; ESP_LOGI - info = 3\n; ESP_LOGD - debug = 4\n; ESP_LOGV - verbose (highest) = 5\n; build_flags = -DCORE_DEBUG_LEVEL=3\n; build_type = debug\n\n; board_build.partitions = default_ffat.csv\nboard_build.partitions = default.csv\n\n; Serial Monitor options\n; https://docs.platformio.org/en/latest/core/userguide/device/cmd_monitor.html\nmonitor_speed = 115200\nmonitor_rts = 0\nmonitor_dtr = 0\n\nmonitor_filters =\n  ; time\n  ; send_on_enter\n  esp32_exception_decoder\n  ; hexlify\n  ; colorize\n\n; Libraries\nlib_deps =\n  https://github.com/h2zero/NimBLE-Arduino.git\n\n; Run before compilation\n; extra_scripts =\n;   pre:some_script.py\n\n; Configure checking tool\n;, pvs-studio,  \ncheck_tool = cppcheck, clangtidy\ncheck_flags =\n  cppcheck: --addon=cert.py\ncheck_skip_packages = yes\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/src/main.cpp",
    "content": "/* Copyright 2022 Vincent Stragier */\n#include \"main.hpp\"\n\n///////////////////\n///    Setup    ///\n///////////////////\nvoid setup() { ota_dfu_ble.begin(\"Test OTA DFU\"); }\n\n//////////////////\n///    Loop    ///\n//////////////////\nvoid loop() {\n  // Kill the holly loop()\n  // Delete the task, comment if you want to keep the loop()\n  vTaskDelete(NULL);\n}\n\n///////////////////////\n///    Functions    ///\n///////////////////////\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/src/main.hpp",
    "content": "/* Copyright 2022 Vincent Stragier */\n\n#ifndef SRC_MAIN_HPP_\n#define SRC_MAIN_HPP_\n///////////////////////\n///    Libraries    ///\n///////////////////////\n\n#include <Arduino.h>\n#include <ble_ota_dfu.hpp>\n\n///////////////////////\n///    Variables    ///\n///////////////////////\nBLE_OTA_DFU ota_dfu_ble;\n\n#endif /* SRC_MAIN_HPP_ */\n"
  },
  {
    "path": "esp32_ble_ota_lib_compact/test/README",
    "content": "\nThis directory is intended for PlatformIO Unit Testing and project tests.\n\nUnit Testing is a software testing method by which individual units of\nsource code, sets of one or more MCU program modules together with associated\ncontrol data, usage procedures, and operating procedures, are tested to\ndetermine whether they are fit for use. Unit testing finds problems early\nin the development cycle.\n\nMore information about PlatformIO Unit Testing:\n- https://docs.platformio.org/page/plus/unit-testing.html\n"
  },
  {
    "path": "esp32_nim_ble_ota/esp32_nim_ble_ota.ino",
    "content": "/*\n   MIT License\n   Copyright (c) 2021 Felix Biego\n   Permission is hereby granted, free of charge, to any person obtaining a copy\n   of this software and associated documentation files (the \"Software\"), to deal\n   in the Software without restriction, including without limitation the rights\n   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n   copies of the Software, and to permit persons to whom the Software is\n   furnished to do so, subject to the following conditions:\n   The above copyright notice and this permission notice shall be included in all\n   copies or substantial portions of the Software.\n   THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n   SOFTWARE.\n*/\n\n#include <Update.h>\n#include \"FS.h\"\n#include \"FFat.h\"\n#include \"SPIFFS.h\"\n#include <NimBLEDevice.h>\n//#include <SD.h>             // For OTA with SD Card\n\n#define BUILTINLED 2\n#define FORMAT_SPIFFS_IF_FAILED true\n#define FORMAT_FFAT_IF_FAILED true\n\n#define USE_SPIFFS  //comment to use FFat\n\n#ifdef USE_SPIFFS\n#define FLASH SPIFFS\n#define FASTMODE false    //SPIFFS write is slow\n#else\n#define FLASH FFat\n#define FASTMODE true    //FFat is faster\n#endif\n\n#define NORMAL_MODE   0   // normal\n#define UPDATE_MODE   1   // receiving firmware\n#define OTA_MODE      2   // installing firmware\n\nuint8_t updater[16384];\nuint8_t updater2[16384];\n\n#define SERVICE_UUID              \"fb1e4001-54ae-4a28-9f74-dfccb248601d\"\n#define CHARACTERISTIC_UUID_RX    \"fb1e4002-54ae-4a28-9f74-dfccb248601d\"\n#define CHARACTERISTIC_UUID_TX    \"fb1e4003-54ae-4a28-9f74-dfccb248601d\"\n\nstatic BLECharacteristic* pCharacteristicTX;\nstatic BLECharacteristic* pCharacteristicRX;\n\nstatic bool deviceConnected = false, sendMode = false, sendSize = true;\nstatic bool writeFile = false, request = false;\nstatic int writeLen = 0, writeLen2 = 0;\nstatic bool current = true;\nstatic int parts = 0, next = 0, cur = 0, MTU = 0;\nstatic int MODE = NORMAL_MODE;\nunsigned long rParts, tParts;\n\nstatic void rebootEspWithReason(String reason) {\n  Serial.println(reason);\n  delay(1000);\n  ESP.restart();\n}\n\nclass MyServerCallbacks: public BLEServerCallbacks {\n    void onConnect(BLEServer* pServer) {\n      Serial.println(\"Connected\");\n      deviceConnected = true;\n\n    }\n    void onDisconnect(BLEServer* pServer) {\n      Serial.println(\"disconnected\");\n      deviceConnected = false;\n    }\n};\n\nclass MyCallbacks: public BLECharacteristicCallbacks {\n\n    //    void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code) {\n    //      Serial.print(\"Status \");\n    //      Serial.print(s);\n    //      Serial.print(\" on characteristic \");\n    //      Serial.print(pCharacteristic->getUUID().toString().c_str());\n    //      Serial.print(\" with code \");\n    //      Serial.println(code);\n    //    }\n\n    void onRead(BLECharacteristic* pCharacteristic) {\n      Serial.print(pCharacteristic->getUUID().toString().c_str());\n      Serial.print(\": onRead(), value: \");\n      Serial.println(pCharacteristic->getValue().c_str());\n    };\n\n    void onNotify(BLECharacteristic *pCharacteristic) {\n      //uint8_t* pData;\n      std::string pData = pCharacteristic->getValue();\n      int len = pData.length();\n      //        Serial.print(\"Notify callback for characteristic \");\n      //        Serial.print(pCharacteristic->getUUID().toString().c_str());\n      //        Serial.print(\" of data length \");\n      //        Serial.println(len);\n      Serial.print(\"TX  \");\n      for (int i = 0; i < len; i++) {\n        Serial.printf(\"%02X \", pData[i]);\n      }\n      Serial.println();\n    }\n\n    void onWrite(BLECharacteristic *pCharacteristic) {\n      //uint8_t* pData;\n      std::string pData = pCharacteristic->getValue();\n      int len = pData.length();\n      // Serial.print(\"Write callback for characteristic \");\n      // Serial.print(pCharacteristic->getUUID().toString().c_str());\n      // Serial.print(\" of data length \");\n      // Serial.println(len);\n      // Serial.print(\"RX  \");\n      // for (int i = 0; i < len; i++) {         // leave this commented\n      //   Serial.printf(\"%02X \", pData[i]);\n      // }\n      // Serial.println();\n\n      if (pData[0] == 0xFB) {\n        int pos = pData[1];\n        for (int x = 0; x < len - 2; x++) {\n          if (current) {\n            updater[(pos * MTU) + x] = pData[x + 2];\n          } else {\n            updater2[(pos * MTU) + x] = pData[x + 2];\n          }\n        }\n\n      } else if  (pData[0] == 0xFC) {\n        if (current) {\n          writeLen = (pData[1] * 256) + pData[2];\n        } else {\n          writeLen2 = (pData[1] * 256) + pData[2];\n        }\n        current = !current;\n        cur = (pData[3] * 256) + pData[4];\n        writeFile = true;\n        if (cur < parts - 1) {\n          request = !FASTMODE;\n        }\n      } else if (pData[0] == 0xFD) {\n        sendMode = true;\n        if (FLASH.exists(\"/update.bin\")) {\n          FLASH.remove(\"/update.bin\");\n        }\n      } else if (pData[0] == 0xFE) {\n        rParts = 0;\n        tParts = (pData[1] * 256 * 256 * 256) + (pData[2] * 256 * 256) + (pData[3] * 256) + pData[4];\n\n        Serial.print(\"Available space: \");\n        Serial.println(FLASH.totalBytes() - FLASH.usedBytes());\n        Serial.print(\"File Size: \");\n        Serial.println(tParts);\n\n      } else if  (pData[0] == 0xFF) {\n        parts = (pData[1] * 256) + pData[2];\n        MTU = (pData[3] * 256) + pData[4];\n        MODE = UPDATE_MODE;\n\n      } else if (pData[0] == 0xEF) {\n        FLASH.format();\n        sendSize = true;\n      }\n\n\n\n    }\n\n\n};\n\nstatic void writeBinary(fs::FS &fs, const char * path, uint8_t *dat, int len) {\n\n  //Serial.printf(\"Write binary file %s\\r\\n\", path);\n\n  File file = fs.open(path, FILE_APPEND);\n\n  if (!file) {\n    Serial.println(\"- failed to open file for writing\");\n    return;\n  }\n  file.write(dat, len);\n  file.close();\n  writeFile = false;\n  rParts += len;\n}\n\nvoid sendOtaResult(String result) {\n  byte arr[result.length()];\n  result.getBytes(arr, result.length());\n  pCharacteristicTX->setValue(arr, result.length());\n  pCharacteristicTX->notify();\n  delay(200);\n}\n\n\nvoid performUpdate(Stream &updateSource, size_t updateSize) {\n  char s1 = 0x0F;\n  String result = String(s1);\n  if (Update.begin(updateSize)) {\n    size_t written = Update.writeStream(updateSource);\n    if (written == updateSize) {\n      Serial.println(\"Written : \" + String(written) + \" successfully\");\n    }\n    else {\n      Serial.println(\"Written only : \" + String(written) + \"/\" + String(updateSize) + \". Retry?\");\n    }\n    result += \"Written : \" + String(written) + \"/\" + String(updateSize) + \" [\" + String((written / updateSize) * 100) + \"%] \\n\";\n    if (Update.end()) {\n      Serial.println(\"OTA done!\");\n      result += \"OTA Done: \";\n      if (Update.isFinished()) {\n        Serial.println(\"Update successfully completed. Rebooting...\");\n        result += \"Success!\\n\";\n      }\n      else {\n        Serial.println(\"Update not finished? Something went wrong!\");\n        result += \"Failed!\\n\";\n      }\n\n    }\n    else {\n      Serial.println(\"Error Occurred. Error #: \" + String(Update.getError()));\n      result += \"Error #: \" + String(Update.getError());\n    }\n  }\n  else\n  {\n    Serial.println(\"Not enough space to begin OTA\");\n    result += \"Not enough space for OTA\";\n  }\n  if (deviceConnected) {\n    sendOtaResult(result);\n    delay(5000);\n  }\n}\n\nvoid updateFromFS(fs::FS &fs) {\n  File updateBin = fs.open(\"/update.bin\");\n  if (updateBin) {\n    if (updateBin.isDirectory()) {\n      Serial.println(\"Error, update.bin is not a file\");\n      updateBin.close();\n      return;\n    }\n\n    size_t updateSize = updateBin.size();\n\n    if (updateSize > 0) {\n      Serial.println(\"Trying to start update\");\n      performUpdate(updateBin, updateSize);\n    }\n    else {\n      Serial.println(\"Error, file is empty\");\n    }\n\n    updateBin.close();\n\n    // when finished remove the binary from spiffs to indicate end of the process\n    Serial.println(\"Removing update file\");\n    fs.remove(\"/update.bin\");\n\n    rebootEspWithReason(\"Rebooting to complete OTA update\");\n  }\n  else {\n    Serial.println(\"Could not load update.bin from spiffs root\");\n  }\n}\n\nvoid initBLE() {\n  BLEDevice::init(\"NimBLE OTA\");\n  BLEDevice::setMTU(517);\n  BLEDevice::setPower(ESP_PWR_LVL_P9);\n  BLEServer *pServer = BLEDevice::createServer();\n  pServer->setCallbacks(new MyServerCallbacks());\n\n  BLEService *pService = pServer->createService(SERVICE_UUID);\n  pCharacteristicTX = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ );\n  pCharacteristicRX = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE_NR);\n  pCharacteristicRX->setCallbacks(new MyCallbacks());\n  pCharacteristicTX->setCallbacks(new MyCallbacks());\n  pService->start();\n  pServer->start();\n\n  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility\n  NimBLEAdvertising *pAdvertising = BLEDevice::getAdvertising();\n  pAdvertising->addServiceUUID(SERVICE_UUID);\n  pAdvertising->setScanResponse(true);\n  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue\n  pAdvertising->setMinPreferred(0x12);\n  pAdvertising->start();\n  //BLEDevice::startAdvertising();\n  Serial.println(\"Characteristic defined! Now you can read it in your phone!\");\n}\n\nvoid setup() {\n  Serial.begin(115200);\n  Serial.println(\"Starting BLE OTA sketch\");\n  pinMode(BUILTINLED, OUTPUT);\n\n  //SPI.begin(18, 22, 23, 5);       // For OTA with SD Card\n  //SD.begin(5);                    // For OTA with SD Card\n\n#ifdef USE_SPIFFS\n  if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) {\n    Serial.println(\"SPIFFS Mount Failed\");\n    return;\n  }\n#else\n  if (!FFat.begin()) {\n    Serial.println(\"FFat Mount Failed\");\n    if (FORMAT_FFAT_IF_FAILED) FFat.format();\n    return;\n  }\n#endif\n\n\n  initBLE();\n\n}\n\nvoid loop() {\n\n  switch (MODE) {\n\n    case NORMAL_MODE:\n      if (deviceConnected) {\n        digitalWrite(BUILTINLED, HIGH);\n        if (sendMode) {\n          uint8_t fMode[] = {0xAA, FASTMODE};\n          pCharacteristicTX->setValue(fMode, 2);\n          pCharacteristicTX->notify();\n          delay(50);\n          sendMode = false;\n        }\n        if (sendSize) {\n          unsigned long x = FLASH.totalBytes();\n          unsigned long y = FLASH.usedBytes();\n          uint8_t fSize[] = {0xEF, (uint8_t) (x >> 16), (uint8_t) (x >> 8), (uint8_t) x, (uint8_t) (y >> 16), (uint8_t) (y >> 8), (uint8_t) y};\n          pCharacteristicTX->setValue(fSize, 7);\n          pCharacteristicTX->notify();\n          delay(50);\n          sendSize = false;\n        }\n\n        // your loop code here\n      } else {\n        digitalWrite(BUILTINLED, LOW);\n      }\n\n      // or here\n\n      break;\n\n    case UPDATE_MODE:\n\n      if (request) {\n        uint8_t rq[] = {0xF1, (cur + 1) / 256, (cur + 1) % 256};\n        pCharacteristicTX->setValue(rq, 3);\n        pCharacteristicTX->notify();\n        delay(50);\n        request = false;\n      }\n\n      if (writeFile) {\n        if (!current) {\n          writeBinary(FLASH, \"/update.bin\", updater, writeLen);\n        } else {\n          writeBinary(FLASH, \"/update.bin\", updater2, writeLen2);\n        }\n        writeFile = false;\n      }\n\n      if (cur + 1 == parts) { // received complete file\n        uint8_t com[] = {0xF2, (cur + 1) / 256, (cur + 1) % 256};\n        pCharacteristicTX->setValue(com, 3);\n        pCharacteristicTX->notify();\n        delay(50);\n        MODE = OTA_MODE;\n      }\n\n      break;\n\n    case OTA_MODE:\n      if (writeFile) {\n        if (!current) {\n          writeBinary(FLASH, \"/update.bin\", updater, writeLen);\n        } else {\n          writeBinary(FLASH, \"/update.bin\", updater2, writeLen2);\n        }\n      }\n\n\n      if (rParts == tParts) {\n        Serial.println(\"Complete\");\n        delay(5000);\n        updateFromFS(FLASH);\n      } else {\n        writeFile = true;\n        Serial.println(\"Incomplete\");\n        Serial.print(\"Expected: \");\n        Serial.print(tParts);\n        Serial.print(\"Received: \");\n        Serial.println(rParts);\n        delay(2000);\n      }\n      break;\n\n  }\n\n}\n"
  }
]