vovagorodok / ble_ota_app

Upload firmware over Bluetooth
MIT License
15 stars 3 forks source link

Hardware and software loading #7

Closed faizannazir closed 3 months ago

faizannazir commented 3 months ago

After connected hardware and software loading stucked

faizannazir commented 3 months ago

Arduino code

/*
   MIT License
   Copyright (c) 2021 Felix Biego
   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:
   The above copyright notice and this permission notice shall be included in all
   copies or substantial portions of the Software.
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   SOFTWARE.
*/

#include <Update.h>
#include "FS.h"
#include "FFat.h"
#include "SPIFFS.h"
#include <NimBLEDevice.h>
//#include <SD.h>             // For OTA with SD Card

#define BUILTINLED 2
#define FORMAT_SPIFFS_IF_FAILED true
#define FORMAT_FFAT_IF_FAILED true

#define USE_SPIFFS  //comment to use FFat

#ifdef USE_SPIFFS
#define FLASH SPIFFS
#define FASTMODE false    //SPIFFS write is slow
#else
#define FLASH FFat
#define FASTMODE true    //FFat is faster
#endif

#define NORMAL_MODE   0   // normal
#define UPDATE_MODE   1   // receiving firmware
#define OTA_MODE      2   // installing firmware

uint8_t updater[16384];
uint8_t updater2[16384];

#define SERVICE_UUID              "15c155ca-36c5-11ed-adc0-9741d6a72f04"
#define CHARACTERISTIC_UUID_RX    "15c1564c-36c5-11ed-adc1-a3d6cf5cc2a4"
#define CHARACTERISTIC_UUID_TX    "15c156e2-36c5-11ed-adc2-7396d4fd413a"

static BLECharacteristic* pCharacteristicTX;
static BLECharacteristic* pCharacteristicRX;

static bool deviceConnected = false, sendMode = false, sendSize = true;
static bool writeFile = false, request = false;
static int writeLen = 0, writeLen2 = 0;
static bool current = true;
static int parts = 0, next = 0, cur = 0, MTU = 0;
static int MODE = NORMAL_MODE;
unsigned long rParts, tParts;

static void rebootEspWithReason(String reason) {
  Serial.println(reason);
  delay(1000);
  ESP.restart();
}

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      Serial.println("Connected");
      deviceConnected = true;

    }
    void onDisconnect(BLEServer* pServer) {
      Serial.println("disconnected");
      deviceConnected = false;
    }
};

class MyCallbacks: public BLECharacteristicCallbacks {

    //    void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code) {
    //      Serial.print("Status ");
    //      Serial.print(s);
    //      Serial.print(" on characteristic ");
    //      Serial.print(pCharacteristic->getUUID().toString().c_str());
    //      Serial.print(" with code ");
    //      Serial.println(code);
    //    }

    void onRead(BLECharacteristic* pCharacteristic) {
      Serial.print(pCharacteristic->getUUID().toString().c_str());
      Serial.print(": onRead(), value: ");
      Serial.println(pCharacteristic->getValue().c_str());
    };

    void onNotify(BLECharacteristic *pCharacteristic) {
      //uint8_t* pData;
      std::string pData = pCharacteristic->getValue();
      int len = pData.length();
      //        Serial.print("Notify callback for characteristic ");
      //        Serial.print(pCharacteristic->getUUID().toString().c_str());
      //        Serial.print(" of data length ");
      //        Serial.println(len);
      Serial.print("TX  ");
      for (int i = 0; i < len; i++) {
        Serial.printf("%02X ", pData[i]);
      }
      Serial.println();
    }

    void onWrite(BLECharacteristic *pCharacteristic) {
      //uint8_t* pData;
      std::string pData = pCharacteristic->getValue();
      int len = pData.length();
      // Serial.print("Write callback for characteristic ");
      // Serial.print(pCharacteristic->getUUID().toString().c_str());
      // Serial.print(" of data length ");
      // Serial.println(len);
      // Serial.print("RX  ");
      // for (int i = 0; i < len; i++) {         // leave this commented
      //   Serial.printf("%02X ", pData[i]);
      // }
      // Serial.println();

      if (pData[0] == 0xFB) {
        int pos = pData[1];
        for (int x = 0; x < len - 2; x++) {
          if (current) {
            updater[(pos * MTU) + x] = pData[x + 2];
          } else {
            updater2[(pos * MTU) + x] = pData[x + 2];
          }
        }

      } else if  (pData[0] == 0xFC) {
        if (current) {
          writeLen = (pData[1] * 256) + pData[2];
        } else {
          writeLen2 = (pData[1] * 256) + pData[2];
        }
        current = !current;
        cur = (pData[3] * 256) + pData[4];
        writeFile = true;
        if (cur < parts - 1) {
          request = !FASTMODE;
        }
      } else if (pData[0] == 0xFD) {
        sendMode = true;
        if (FLASH.exists("/update.bin")) {
          FLASH.remove("/update.bin");
        }
      } else if (pData[0] == 0xFE) {
        rParts = 0;
        tParts = (pData[1] * 256 * 256 * 256) + (pData[2] * 256 * 256) + (pData[3] * 256) + pData[4];

        Serial.print("Available space: ");
        Serial.println(FLASH.totalBytes() - FLASH.usedBytes());
        Serial.print("File Size: ");
        Serial.println(tParts);

      } else if  (pData[0] == 0xFF) {
        parts = (pData[1] * 256) + pData[2];
        MTU = (pData[3] * 256) + pData[4];
        MODE = UPDATE_MODE;

      } else if (pData[0] == 0xEF) {
        FLASH.format();
        sendSize = true;
      }

    }

};

static void writeBinary(fs::FS &fs, const char * path, uint8_t *dat, int len) {

  //Serial.printf("Write binary file %s\r\n", path);

  File file = fs.open(path, FILE_APPEND);

  if (!file) {
    Serial.println("- failed to open file for writing");
    return;
  }
  file.write(dat, len);
  file.close();
  writeFile = false;
  rParts += len;
}

void sendOtaResult(String result) {
  byte arr[result.length()];
  result.getBytes(arr, result.length());
  pCharacteristicTX->setValue(arr, result.length());
  pCharacteristicTX->notify();
  delay(200);
}

void performUpdate(Stream &updateSource, size_t updateSize) {
  char s1 = 0x0F;
  String result = String(s1);
  if (Update.begin(updateSize)) {
    size_t written = Update.writeStream(updateSource);
    if (written == updateSize) {
      Serial.println("Written : " + String(written) + " successfully");
    }
    else {
      Serial.println("Written only : " + String(written) + "/" + String(updateSize) + ". Retry?");
    }
    result += "Written : " + String(written) + "/" + String(updateSize) + " [" + String((written / updateSize) * 100) + "%] \n";
    if (Update.end()) {
      Serial.println("OTA done!");
      result += "OTA Done: ";
      if (Update.isFinished()) {
        Serial.println("Update successfully completed. Rebooting...");
        result += "Success!\n";
      }
      else {
        Serial.println("Update not finished? Something went wrong!");
        result += "Failed!\n";
      }

    }
    else {
      Serial.println("Error Occurred. Error #: " + String(Update.getError()));
      result += "Error #: " + String(Update.getError());
    }
  }
  else
  {
    Serial.println("Not enough space to begin OTA");
    result += "Not enough space for OTA";
  }
  if (deviceConnected) {
    sendOtaResult(result);
    delay(5000);
  }
}

void updateFromFS(fs::FS &fs) {
  File updateBin = fs.open("/update.bin");
  if (updateBin) {
    if (updateBin.isDirectory()) {
      Serial.println("Error, update.bin is not a file");
      updateBin.close();
      return;
    }

    size_t updateSize = updateBin.size();

    if (updateSize > 0) {
      Serial.println("Trying to start update");
      performUpdate(updateBin, updateSize);
    }
    else {
      Serial.println("Error, file is empty");
    }

    updateBin.close();

    // when finished remove the binary from spiffs to indicate end of the process
    Serial.println("Removing update file");
    fs.remove("/update.bin");

    rebootEspWithReason("Rebooting to complete OTA update");
  }
  else {
    Serial.println("Could not load update.bin from spiffs root");
  }
}

void initBLE() {
  BLEDevice::init("NimBLE OTA");
  BLEDevice::setMTU(517);
  BLEDevice::setPower(ESP_PWR_LVL_P9);
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  BLEService *pService = pServer->createService(SERVICE_UUID);
  pCharacteristicTX = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ );
  pCharacteristicRX = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE_NR);
  pCharacteristicRX->setCallbacks(new MyCallbacks());
  pCharacteristicTX->setCallbacks(new MyCallbacks());
  pService->start();
  pServer->start();

  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility
  NimBLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  pAdvertising->start();
  //BLEDevice::startAdvertising();
  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE OTA sketch");
  pinMode(BUILTINLED, OUTPUT);

  //SPI.begin(18, 22, 23, 5);       // For OTA with SD Card
  //SD.begin(5);                    // For OTA with SD Card

#ifdef USE_SPIFFS
  if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) {
    Serial.println("SPIFFS Mount Failed");
    return;
  }
#else
  if (!FFat.begin()) {
    Serial.println("FFat Mount Failed");
    if (FORMAT_FFAT_IF_FAILED) FFat.format();
    return;
  }
#endif

  initBLE();

}

void loop() {

  switch (MODE) {

    case NORMAL_MODE:
      if (deviceConnected) {
        digitalWrite(BUILTINLED, HIGH);
        if (sendMode) {
          uint8_t fMode[] = {0xAA, FASTMODE};
          pCharacteristicTX->setValue(fMode, 2);
          pCharacteristicTX->notify();
          delay(50);
          sendMode = false;
        }
        if (sendSize) {
          unsigned long x = FLASH.totalBytes();
          unsigned long y = FLASH.usedBytes();
          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};
          pCharacteristicTX->setValue(fSize, 7);
          pCharacteristicTX->notify();
          delay(50);
          sendSize = false;
        }

        // your loop code here
      } else {
        digitalWrite(BUILTINLED, LOW);
      }

      // or here

      break;

    case UPDATE_MODE:

      if (request) {
        uint8_t rq[] = {0xF1, (cur + 1) / 256, (cur + 1) % 256};
        pCharacteristicTX->setValue(rq, 3);
        pCharacteristicTX->notify();
        delay(50);
        request = false;
      }

      if (writeFile) {
        if (!current) {
          writeBinary(FLASH, "/update.bin", updater, writeLen);
        } else {
          writeBinary(FLASH, "/update.bin", updater2, writeLen2);
        }
        writeFile = false;
      }

      if (cur + 1 == parts) { // received complete file
        uint8_t com[] = {0xF2, (cur + 1) / 256, (cur + 1) % 256};
        pCharacteristicTX->setValue(com, 3);
        pCharacteristicTX->notify();
        delay(50);
        MODE = OTA_MODE;
      }

      break;

    case OTA_MODE:
      if (writeFile) {
        if (!current) {
          writeBinary(FLASH, "/update.bin", updater, writeLen);
        } else {
          writeBinary(FLASH, "/update.bin", updater2, writeLen2);
        }
      }

      if (rParts == tParts) {
        Serial.println("Complete");
        delay(5000);
        updateFromFS(FLASH);
      } else {
        writeFile = true;
        Serial.println("Incomplete");
        Serial.print("Expected: ");
        Serial.print(tParts);
        Serial.print("Received: ");
        Serial.println(rParts);
        delay(2000);
      }
      break;

  }

}
vovagorodok commented 3 months ago

It's not compatible with Felix Biego ESP32_BLE_OTA_Arduino. Decided to create new Arduino/PlatformIO library ArduinoBleOTA from scratch, because ESP32_BLE_OTA_Arduino does not support security and integrity mechanisms

Please use instead: https://github.com/vovagorodok/ArduinoBleOTA/blob/main/examples/basic/main.ino

faizannazir commented 3 months ago

@vovagorodok I also want to develop flutter app could you tell me what steps are needed to upload firmware file using ble ota

vovagorodok commented 3 months ago

Sure. I prefer to work with VS Code with PlatformIO extension to compile and flash ESP32.

  1. MICROCONTROLLER SIDE. Compile and flash example ArduinoBleOTA to ESP32 (prefered env:esp32dev_nimble for now). Or add ArduinoBleOTA library to your existing project by:
    lib_deps =
    https://github.com/vovagorodok/ArduinoBleOTA
    build_flags =
    -D USE_NIM_BLE_ARDUINO_LIB
  2. PHONE SIDE. Compile ble_ota_app and install apk to your phone. If you need compiled apk let me know. I'll release apk in Google Play Store soon (one small issue should be resolved before that)

Please ask me for details. I'll try to answer you ASAP

faizannazir commented 3 months ago

Sure. I prefer to work with VS Code with PlatformIO extension to compile and flash ESP32.

  1. MICROCONTROLLER SIDE. Compile and flash example ArduinoBleOTA to ESP32 (prefered env:esp32dev_nimble for now). Or add ArduinoBleOTA library to your existing project by:
lib_deps =
  https://github.com/vovagorodok/ArduinoBleOTA
build_flags =
  -D USE_NIM_BLE_ARDUINO_LIB
  1. PHONE SIDE. Compile ble_ota_app and install apk to your phone. If you need compiled apk let me know. I'll release apk in Google Play Store soon (one small issue should be resolved before that)

Please ask me for details. I'll try to answer you ASAP

@vovagorodok I am working on flutter app using flutter blue plus library. I facing issue in implementation of sending file to esp32

vovagorodok commented 3 months ago

Not sure what is needed from my side. ble_ota_app uses flutter_reactive_ble package, not flutter_blue_plus. ble_ota_app not for uploading files but for upload/update firmware.

Do You search solution for upload/update firmware or just for sending files over ble?

faizannazir commented 3 months ago

@vovagorodok i want to update firmware from binary file using app

vovagorodok commented 3 months ago

Than please compile and flash ArduinoBleOTA example as first step.

Are You use Arduino IDE or Visual Studio Code + PlatformIO?

faizannazir commented 3 months ago

Than please compile and flash ArduinoBleOTA example as first step.

Are You use Arduino IDE or Visual Studio Code + PlatformIO?

I want to perform ble ota from file which user send using app

vovagorodok commented 3 months ago

ble_ota_app supports it. Upload local files are disabled for registered hardware (in order to protect users from uploads by indecent). If You want to allow/enable local files upload for all please enable option in: Settings -> "Always allow local files upload"