NimBLE
ericlangel opened this issue · comments
This is not a real issue...more a Feature Request
i ported your ESP32 Code to the ESP32 NimBLE Stack: https://github.com/h2zero/NimBLE-Arduino
first i had an issue with the CRC of the update File, so the Update failed.
Because of this issue i changed the FS to SD Card (SPI Mode) and compared both files.
They are exactly the same and the Update with SD Card is successful.
Now the more Interesting Part:
The Transfer of around 1,2MB Update File was done in 29 Seconds (SD Card) and 43Seconds to FFAT.
And the NimBLE Stack takes less flash Space
605978 Bytes (46%) with NimBLE Stack
1031430 Bytes (78%) with ESP32 BLE Stack
Maybe someone knows the reson for failed Checksum when using FFAT?
Trying to start update
Written : 1188512 successfully
E (73242) esp_image: Checksum failed. Calculated 0xd1 read 0x0
Error Occurred. Error #: 9
Here is the changed code:
#include <Update.h>
#include "FS.h"
#include "FFat.h"
#include "SPIFFS.h"
//#include <SD.h>
//#include <BLEDevice.h>
//#include <BLEUtils.h>
//#include <BLEServer.h>
//#include <BLE2902.h>
#include <NimBLEDevice.h>
#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 "fb1e4001-54ae-4a28-9f74-dfccb248601d"
#define CHARACTERISTIC_UUID_RX "fb1e4002-54ae-4a28-9f74-dfccb248601d"
#define CHARACTERISTIC_UUID_TX "fb1e4003-54ae-4a28-9f74-dfccb248601d"
static BLECharacteristic* pCharacteristicTX;
static BLECharacteristic* pCharacteristicRX;
static bool deviceConnected = false, sendMode = false;
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;
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();
//pData = pCharacteristic->getData();
//if (pData != NULL) {
// 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();
//pData = pCharacteristic->getData();
//if (pData != NULL) {
// 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] == 0xFF) {
parts = (pData[1] * 256) + pData[2];
MTU = (pData[3] * 256) + pData[4];
MODE = UPDATE_MODE;
}
//}
}
};
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();
}
void initBLE() {
BLEDevice::init("SBBUA");
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());
//pCharacteristicTX->addDescriptor(new BLE2902());
//pCharacteristicTX->setNotifyProperty(true);
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);
//SD.begin(5);
#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;
}
// 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 (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;
}
if (writeFile) {
if (!current) {
writeBinary(FLASH, "/update.bin", updater, writeLen);
} else {
writeBinary(FLASH, "/update.bin", updater2, writeLen2);
}
writeFile = false;
}
break;
case OTA_MODE:
updateFromFS(FLASH);
break;
}
}
void sendOtaResult(String result) {
pCharacteristicTX->setValue(result.c_str());
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");
}
}
Awesome, can you create a PR on a new branch?
yes,
i hope it worked. It's my first PR
that was great