Compare commits

...

2 Commits

Author SHA1 Message Date
7f8c320b5a set command type to notify 2025-11-29 23:02:47 +00:00
316fbd7c60 First attempt at file transfer 2025-11-28 23:29:37 +00:00
2 changed files with 205 additions and 22 deletions

View File

@@ -1,24 +1,146 @@
#include "BLEOTA.h"
BLEOTAClass::BLEOTAClass(){
_pVersionNumber = new BLECharacteristic(VERSION_NUMBER_UUID, BLECharacteristic::PROPERTY_READ);
_pVersionNumberDescriptor = new BLEDescriptor(VERSION_NUMBER_DESCRIPTOR_UUID);
/*
MAIN CLASS
*/
BLEOTAClass::BLEOTAClass()
: pVersionNumber_(new BLECharacteristic(VERSION_NUMBER_UUID, BLECharacteristic::PROPERTY_READ)),
pVersionNumberDescriptor_(new BLEDescriptor(VERSION_NUMBER_DESCRIPTOR_UUID)),
pOTACommand_(new BLECharacteristic(OTA_COMMAND_UUID, BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_WRITE)),
pOTACommandDescriptor_(new BLEDescriptor(OTA_COMMAND_DESCRIPTOR_UUID)),
pOTAFile_(new BLECharacteristic(OTA_FILE_UUID, BLECharacteristic::PROPERTY_WRITE)),
pOTAFileDescriptor_(new BLEDescriptor(OTA_FILE_DESCRIPTOR_UUID)) {
cmdCallbacks_ = new OTACommandCallbacks(this);
fileCallbacks_ = new OTAFileCallbacks(this);
};
void BLEOTAClass::begin(BLEServer* server, char* versionNumber) {
// set internal versionNumber
_vNum = versionNumber;
vNum_ = versionNumber;
// set internal server
_pServer = server;
pServer_ = server;
// create service
BLEService* otaService = _pServer->createService(OTA_SERVICE_UUID);
// version number property
otaService->addCharacteristic(_pVersionNumber);
_pVersionNumber->setValue(_vNum);
_pVersionNumberDescriptor->setValue("Version Number");
_pVersionNumber->addDescriptor(_pVersionNumberDescriptor);
BLEService* otaService = pServer_->createService(OTA_SERVICE_UUID);
pVersionNumber_->setValue(vNum_);
pVersionNumberDescriptor_->setValue("Version Number");
pVersionNumber_->addDescriptor(pVersionNumberDescriptor_);
otaService->addCharacteristic(pVersionNumber_);
pOTACommandDescriptor_->setValue("OTA Command");
pOTACommand_->addDescriptor(pOTACommandDescriptor_);
otaService->addCharacteristic(pOTACommand_);
pOTAFileDescriptor_->setValue("OTA File");
pOTAFile_->addDescriptor(pOTAFileDescriptor_);
otaService->addCharacteristic(pOTAFile_);
otaService->start();
setOTAcommand(CMD_ON);
state_ = STAGE_READY;
}
void BLEOTAClass::setOTAcommand(otaCommand cmd) {
uint8_t commandBuffer[1] = {};
commandBuffer[0] = cmd;
pOTACommand_->setValue(commandBuffer, 1);
pOTACommand_->notify();
currentCommand_ = cmd;
}
void BLEOTAClass::loop() {
if (millis() - lastLoop_ > 2000) {
if (state_ == STAGE_ERROR && currentCommand_ != CMD_ERROR) {
setOTAcommand(CMD_ERROR);
}
if (state_ == STAGE_DONE) {
// reboot
ESP.restart();
}
}
}
/*
COMMAND CHARACTERISTIC CALLBACKS
*/
// so that the callback has access to the main class
BLEOTAClass::OTACommandCallbacks::OTACommandCallbacks(BLEOTAClass* pParent) : pParent_(pParent) {};
void BLEOTAClass::OTACommandCallbacks::onWrite(BLECharacteristic* pCharacteristic, esp_ble_gatts_cb_param_t* param) {
size_t cmdLength = pCharacteristic->getLength();
if (cmdLength > 4 && pParent_->state_ == STAGE_READY) {
// error
Serial.println("Error: command too long");
pParent_->state_ = STAGE_ERROR;
} else if (cmdLength > 1 && pParent_->state_ == STAGE_READY) {
// process file size
uint8_t* cmdBuffer = pCharacteristic->getData();
uint32_t cmd = ((uint32_t)cmdBuffer[0] << 24) |
((uint32_t)cmdBuffer[1] << 16) |
((uint32_t)cmdBuffer[2] << 8) | (uint32_t)cmdBuffer[3];
pParent_->fileSize_ = cmd;
// start update
if (!Update.begin(pParent_->fileSize_, U_FLASH)) {
// error
Serial.printf("Error: ");
Update.printError(Serial);
pParent_->state_ = STAGE_ERROR;
return;
}
pParent_->state_ = STAGE_FLASHING;
} else if (cmdLength == 0) {
// process cmd
uint8_t cmd = pCharacteristic->getData()[0];
if (cmd == otaCommand::CMD_AGREE) {
// process agree
if (!Update.end()) {
// error
Serial.println("Error: client and server ready, but Updater is not");
pParent_->state_ = STAGE_ERROR;
} else {
// reboot logic
pParent_->state_ = STAGE_DONE;
}
} else if (cmd == CMD_DISAGREE) {
// process disagree
// start over
Update.abort();
pParent_->fileProgress_ = 0;
} else {
Serial.println("Error: wrong command");
pParent_->state_ = STAGE_ERROR;
}
} else {
Serial.println("Error: command length");
pParent_->state_ = STAGE_ERROR;
}
}
/*
FILE CHARACTERISTIC CALLBACKS
*/
// so that the callback has access to the main class
BLEOTAClass::OTAFileCallbacks::OTAFileCallbacks(BLEOTAClass* pParent) : pParent_(pParent) {};
void BLEOTAClass::OTAFileCallbacks::onWrite(BLECharacteristic* pCharacteristic, esp_ble_gatts_cb_param_t* param) {
size_t packetSize = pCharacteristic->getLength();
if (packetSize == 0) {
// problem
// output error
Serial.println("Error: no packet");
pParent_->state_ = STAGE_ERROR;
} else {
// write file to updater
uint8_t* filePacket = pCharacteristic->getData();
if (Update.write(filePacket, packetSize) != packetSize) {
Update.printError(Serial);
pParent_->state_ = STAGE_ERROR;
return;
}
}
}
BLEOTAClass BLEota;

View File

@@ -3,20 +3,81 @@
#include <Arduino.h>
#include <BLEServer.h>
#include <Update.h>
#define OTA_SERVICE_UUID "71a4438e-fd52-4b15-b3d2-ec0e3e56193b"
#define VERSION_NUMBER_UUID "1978a3df-c009-4837-b295-57ef429dde8c"
#define VERSION_NUMBER_DESCRIPTOR_UUID "1e0c35d1-ba03-4f4d-a99c-bac7664d95ed"
#define OTA_SERVICE_UUID "71a4438e-fd52-4b15-b3d2-ec0e3e561900"
#define VERSION_NUMBER_UUID "71a4438e-fd52-4b15-b3d2-ec0e3e561910"
#define VERSION_NUMBER_DESCRIPTOR_UUID "71a4438e-fd52-4b15-b3d2-ec0e3e561911"
#define OTA_COMMAND_UUID "71a4438e-fd52-4b15-b3d2-ec0e3e561920"
#define OTA_COMMAND_DESCRIPTOR_UUID "71a4438e-fd52-4b15-b3d2-ec0e3e561921"
#define OTA_FILE_UUID "71a4438e-fd52-4b15-b3d2-ec0e3e561930"
#define OTA_FILE_DESCRIPTOR_UUID "71a4438e-fd52-4b15-b3d2-ec0e3e561931"
class BLEOTAClass {
public:
BLEOTAClass();
void begin(BLEServer* server, char* versionNumber = "1.0.0");
private:
BLEServer* _pServer;
char* _vNum;
BLECharacteristic* _pVersionNumber;
BLEDescriptor* _pVersionNumberDescriptor;
public:
BLEOTAClass();
void begin(BLEServer* server, char* versionNumber = "1.0.0");
void loop();
typedef enum otaCommand {
CMD_ON = 0x00,
CMD_READY = 0x01,
CMD_DONE = 0x02,
CMD_AGREE = 0x03,
CMD_DISAGREE = 0x04,
CMD_ERROR = 0x0F
};
typedef enum otaStage {
STAGE_READY,
STAGE_FLASHING,
STAGE_DONE,
STAGE_ERROR
};
private:
void setOTAcommand(otaCommand cmd);
class OTACommandCallbacks : public BLECharacteristicCallbacks {
public:
OTACommandCallbacks(BLEOTAClass* pParent);
void onWrite(BLECharacteristic* pCharacteristic, esp_ble_gatts_cb_param_t* param) override;
private:
BLEOTAClass* pParent_;
};
class OTAFileCallbacks : public BLECharacteristicCallbacks {
public:
OTAFileCallbacks(BLEOTAClass* pParent);
void onWrite(BLECharacteristic* pCharacteristic, esp_ble_gatts_cb_param_t* param) override;
private:
BLEOTAClass* pParent_;
};
BLEServer* pServer_;
char* vNum_;
BLECharacteristic* pVersionNumber_;
BLEDescriptor* pVersionNumberDescriptor_;
BLECharacteristic* pOTACommand_;
BLEDescriptor* pOTACommandDescriptor_;
OTACommandCallbacks* cmdCallbacks_;
otaCommand currentCommand_;
BLECharacteristic* pOTAFile_;
BLEDescriptor* pOTAFileDescriptor_;
OTAFileCallbacks* fileCallbacks_;
uint32_t fileSize_ = 0;
uint32_t fileProgress_ = 0;
otaStage state_ = otaStage::STAGE_READY;
unsigned long lastLoop_ = 0;
};
extern BLEOTAClass BLEota;