boothifier/src/BLE_UpdateService.cpp
2025-09-07 23:38:56 -07:00

238 lines
9.3 KiB
C++

#include "BLE_UpdateService.h"
#include "WiFi.h"
#include "my_wifi.h"
#include "global.h"
#include "AppUpgrade.h"
#include "AppVersion.h"
#include "BleSettings.h"
#include "version.h"
static const char *tag = "BLE_UpdateService";
//#define UPGRADE_SERVICE_UUID "abcdef01-2345-6789-1234-56789abcdef0"
//#define UPGRADE_CHARACTERISTIC1_UUID "abcdef01-2345-6789-1234-56789abcdef1"
//#define UPGRADE_CHARACTERISTIC2_UUID "abcdef02-2345-6789-1234-56789abcdef1"
NimBLEService *pUpgradeService = nullptr;
NimBLECharacteristic *pUpgradeCharacteristic1 = nullptr;
NimBLECharacteristic *pUpgradeCharacteristic2 = nullptr;
enum WIFI_STAT : byte { WIFI_DISCONNECTED=0, WIFI_BAD_CREDS=1, WIFI_NO_AP=2, WIFI_CONNECTED=3 };
struct updateStatus {
WIFI_STAT wifiStatus = WIFI_DISCONNECTED;
bool wifiOnline = false;
byte wifiIP[4] = {0, 0, 0, 0};
byte currVersion[3] = {FIRMWARE_VERSION_MAJOR, FIRMWARE_VERSION_MINOR, FIRMWARE_VERSION_PATCH};
byte newVersion[3] = {0, 0, 0};
char wifiSSID[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
}updatePacket;
// Class for handling characteristic events
class UpgradeChar_Callbacks : public NimBLECharacteristicCallbacks {
void onWrite(NimBLECharacteristic *pCharacteristic) override {
std::string value = pCharacteristic->getValue();
ESP_LOGD(tag, "Upgrade Char written with value: %s", value.c_str());
if (value.compare(0, 12, "wifi-connect") == 0) { // Update WiFi credentials
// Expecting: "wifi-connect:{\"ssid\":...,\"pass\":...}"
size_t jsonStart = (value.size() > 12 && value[12] == ':') ? 13 : 12;
if (value.size() <= jsonStart) {
ESP_LOGW(tag, "wifi-connect command missing JSON payload");
return;
}
JsonDocument doc;
DeserializationError err = deserializeJson(doc, value.substr(jsonStart));
if (err) {
ESP_LOGW(tag, "JSON parse error for wifi-connect: %s", err.c_str());
return;
}
JsonObject wifiJson = doc.as<JsonObject>();
String ssid = wifiJson["ssid"].as<String>();
String pass = wifiJson["pass"].as<String>();
if (ssid.length() == 0) {
ESP_LOGW(tag, "wifi-connect missing ssid");
return;
}
ESP_LOGI(tag, "WiFi connect requested: ssid='%s', pass len=%u", ssid.c_str(), (unsigned)pass.length());
bool started = StartWifiConnectTask(ssid, pass);
if (started) {
updatePacket.wifiStatus = WIFI_DISCONNECTED;
updatePacket.wifiOnline = false;
updatePacket.wifiIP[0] = updatePacket.wifiIP[1] = updatePacket.wifiIP[2] = updatePacket.wifiIP[3] = 0;
strncpy(updatePacket.wifiSSID, ssid.c_str(), sizeof(updatePacket.wifiSSID) - 1);
} else {
ESP_LOGW(tag, "Failed to start WiFi connection task");
}
}
else if (value.compare("version-check") == 0) { // Check if new version is available
ESP_LOGI(tag, "Version check command received: newVersion=%d.%d.%d", otaVersion.major(), otaVersion.minor(), otaVersion.patch());
if(updatePacket.newVersion[0] == 0){
startVersionCheckTask(); // start the task and done
}else{
ESP_LOGI(tag, "Version already checked");
}
}
else if (value.compare("upgrade-start") == 0) { // Start OTA update
ESP_LOGI(tag, "Start OTA update command received");
setUpdateModeBoth();
startFirmwareUpdateTask(nullptr); // start the task
}
else if (value.compare("upgrade-start-files-only") == 0) { // Start OTA update
ESP_LOGI(tag, "Start OTA update files-only command received");
setUpdateModeFilesOnly();
startFirmwareUpdateTask(nullptr); // start the task
}
else if (value.compare("upgrade-start-firmware-only") == 0) { // Start OTA update
ESP_LOGI(tag, "Start OTA update firmware-only command received");
setUpdateModeFirmwareOnly();
startFirmwareUpdateTask(nullptr); // start the task
}
else if (value.compare("rename-device") == 0) { // Start renaming device
ESP_LOGI(tag, "Start renane device command received");
}
else {
ESP_LOGW(tag, "Unknown command received: %s", value.c_str());
}
}
void onRead(NimBLECharacteristic *pCharacteristic) override {
updatePacket.wifiOnline = InternetAvailable;
if (WiFi.status() == WL_CONNECTED) {
updatePacket.wifiStatus = WIFI_CONNECTED;
IPAddress ip = WiFi.localIP();
updatePacket.wifiIP[0] = ip[0];
updatePacket.wifiIP[1] = ip[1];
updatePacket.wifiIP[2] = ip[2];
updatePacket.wifiIP[3] = ip[3];
strncpy(updatePacket.wifiSSID, WiFi.SSID().c_str(), sizeof(updatePacket.wifiSSID) - 1);
ESP_LOGI(tag, "WiFi packet: SSID='%s'", updatePacket.wifiSSID);
} else {
updatePacket.wifiStatus = WIFI_DISCONNECTED;
updatePacket.wifiIP[0] = 0;
updatePacket.wifiIP[1] = 0;
updatePacket.wifiIP[2] = 0;
updatePacket.wifiIP[3] = 0;
memset(updatePacket.wifiSSID, 0, sizeof(updatePacket.wifiSSID));
}
//update version
if(otaVersion.major() != 0){
ESP_LOGI(tag, "Updated new version: major=%d, minor=%d, patch=%d", otaVersion.major(), otaVersion.minor(), otaVersion.patch());
updatePacket.newVersion[0] = otaVersion.major();
updatePacket.newVersion[1] = otaVersion.minor();
updatePacket.newVersion[2] = otaVersion.patch();
}
// Only populate the control characteristic with the status packet
if (pCharacteristic == pUpgradeCharacteristic1) {
pCharacteristic->setValue(reinterpret_cast<uint8_t*>(&updatePacket), sizeof(updatePacket));
ESP_LOGI(tag, "Upgrade status read");
}
}
};
void bleUpgrade_send_message(String s){
if(pUpgradeCharacteristic2){
if (s.length() == 0) {
return;
}
// Log message details before sending
ESP_LOGI(tag, "Sending BLE message, length=%d bytes", s.length());
if (s.length() < 100) {
ESP_LOGI(tag, "Message content: '%s'", s.c_str());
}
// For testing - ensure string is null-terminated properly
String paddedString = s;
// Explicitly set using raw bytes with explicit length
const char* raw = paddedString.c_str();
size_t rawLen = paddedString.length();
// Explicitly handle null-termination ourselves
std::string stdStr(raw, rawLen);
pUpgradeCharacteristic2->setValue(stdStr);
// Log the value that was actually set
std::string valueAfterSet = pUpgradeCharacteristic2->getValue();
ESP_LOGI(tag, "Value after set: length=%d bytes", valueAfterSet.length());
if (pUpgradeCharacteristic2->getSubscribedCount() > 0) {
pUpgradeCharacteristic2->notify();
ESP_LOGI(tag, "Notification sent");
} else {
ESP_LOGW(tag, "No subscribers for notification");
}
}
}
/*
void bleUpgrade_send_message(String s) {
if(pUpgradeCharacteristic2) {
if (s.length() == 0) {
return;
}
// OPTION 1: Sanitize non-printable characters
String sanitized = "";
for (size_t i = 0; i < s.length(); i++) {
char c = s[i];
// Only keep printable ASCII characters and common whitespace
if ((c >= 32 && c <= 126) || c == '\n' || c == '\r' || c == '\t') {
sanitized += c;
} else {
// Replace non-printable with hexadecimal representation or skip
sanitized += String("[0x") + String(c, HEX) + "]";
// OR just: continue; // to skip unprintable chars
}
}
// Set value and notify only if there are subscribers
pUpgradeCharacteristic2->setValue(sanitized.c_str());
if (pUpgradeCharacteristic2->getSubscribedCount() > 0) {
pUpgradeCharacteristic2->notify();
}
}
}
*/
void Init_UpgradeBLEService(NimBLEServer *pServer){
// Create Upgrade BLE Service
pUpgradeService= pServer->createService( BTUpgradeServiceUUID.c_str() );
pUpgradeCharacteristic1 = pUpgradeService->createCharacteristic(
BTUpgradeCharacteristic1UUID.c_str(),
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY
);
// Register the callback with the characteristic
pUpgradeCharacteristic1->setCallbacks(new UpgradeChar_Callbacks());
ESP_LOGI(tag, "Upgrade callback registered!");
pUpgradeCharacteristic2 = pUpgradeService->createCharacteristic(
BTUpgradeCharacteristic2UUID.c_str(),
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY
);
// Register the callback with the characteristic
pUpgradeCharacteristic2->setCallbacks(new UpgradeChar_Callbacks());
ESP_LOGI(tag, "Upgrade callback registered!");
pUpgradeService->start();
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->addServiceUUID( BTUpgradeServiceUUID.c_str() ); // Advertise service UUID
}