diff --git a/data/ata-boothifier-upgrade(updated).html b/data/ata-boothifier-upgrade(updated).html new file mode 100644 index 0000000..de07c66 --- /dev/null +++ b/data/ata-boothifier-upgrade(updated).html @@ -0,0 +1,531 @@ + + + + + + ATA Firmware Update + + + + +

ATA Firmware Update

+ + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+ +
+ + +
+ + +
+ + + + +
+ + +
+ + +
+ + +
+ + +
+
+ + +
+ +
+ + + + diff --git a/data/system/ble.json b/data/system/ble.json new file mode 100644 index 0000000..c35b40d --- /dev/null +++ b/data/system/ble.json @@ -0,0 +1,9 @@ +{ + "name": "ATALIGHTS", + "lights-service": "FFE0", + "lights-char": "FFE1", + "stick-char": "FFE2", + "upgrade-service": "abcdef01-2345-6789-1234-56789abcdef0", + "upgrade-char1": "abcdef01-2345-6789-1234-56789abcdef1", + "upgrade-char2": "abcdef02-2345-6789-1234-56789abcdef1" +} \ No newline at end of file diff --git a/data/system/update.json b/data/system/update.json index 4e5f82b..02aa6ac 100644 --- a/data/system/update.json +++ b/data/system/update.json @@ -1,4 +1,5 @@ { - "baseurl": "https://storage.googleapis.com/boothifier/", - "folder": "latest/" + "folder": "latest/", + "baseurl": "https://s3-minio.boothwizard.com/boothifier/", + "baseurl2": "https://storage.googleapis.com/boothifier/" } \ No newline at end of file diff --git a/firmware_update/UploadToMinio.py b/firmware_update/UploadToMinio.py new file mode 100644 index 0000000..516301d --- /dev/null +++ b/firmware_update/UploadToMinio.py @@ -0,0 +1,337 @@ +#!/usr/bin/env python3 +"""Upload firmware, manifest, and data assets to a MinIO (S3-compatible) bucket. + +Features preserved from original GCS script: + - Optional backup (copies existing objects under destination prefix to timestamped folder under backups/) + - Upload firmware.bin, update.json, and recursively mirror a data directory + - Cache-Control set to disable caching on clients + +Switches from google.cloud.storage to boto3 (S3 API) for MinIO compatibility. +""" +import os +import sys +import datetime +import json +from pathlib import Path + +try: + import boto3 + from botocore.exceptions import ClientError + from botocore.config import Config +except ImportError: + print("ERROR: boto3 is required. Install with: pip install boto3") + sys.exit(1) + +# ============================================================================= +# CONFIGURATION CONSTANTS (edit as needed or supply via environment variables) +# ============================================================================= + +CREATE_BACKUP = False +UPLOAD_FIRMWARE = False +UPLOAD_MANIFEST = True +UPLOAD_DATA = False + +# Bucket / endpoint configuration +BUCKET_NAME = os.getenv('MINIO_BUCKET', 'boothifier') +DESTINATION_DIR = os.getenv('MINIO_DEST_PREFIX', 'latest') # prefix inside bucket +BACKUPS_DIR = os.getenv('MINIO_BACKUPS_PREFIX', 'backups') + +LOCAL_ROOT_PATH = Path(__file__).parent.resolve() + +# Optional service account style JSON key (generated by MinIO Console). Expected fields: +# {"url":"https://minio.example.com/api/v1/service-account-credentials","accessKey":"...","secretKey":"...","api":"s3v4","path":"auto"} +MINIO_KEY_FILE = LOCAL_ROOT_PATH / 'minio-boothifier-key.json' + +# Defaults before loading file / env +_json_access = None +_json_secret = None +_json_url = None + +def _load_json_key(): + global _json_access, _json_secret, _json_url + try: + if MINIO_KEY_FILE.is_file(): + with open(MINIO_KEY_FILE, 'r', encoding='utf-8') as fh: + data = json.load(fh) + _json_access = data.get('accessKey') or None + _json_secret = data.get('secretKey') or None + _json_url = data.get('url') or None + except Exception as e: + print(f"WARN: Failed to load MinIO key file '{MINIO_KEY_FILE.name}': {e}") + +_load_json_key() + +def _derive_endpoint(url_value: str) -> str: + if not url_value: + return 'https://s3-minio.boothwizard.com' + # Remove known API suffix if present (/api/...) + # e.g. https://s3-minio.boothwizard.com/api/v1/service-account-credentials -> https://s3-minio.boothwizard.com + parts = url_value.split('/api/') + return parts[0] if parts else url_value + +# MinIO credentials with precedence: ENV > JSON file > fallback +MINIO_ENDPOINT = os.getenv('MINIO_ENDPOINT') or _derive_endpoint(_json_url) +MINIO_ACCESS_KEY = os.getenv('MINIO_ACCESS_KEY') or _json_access or 'CHANGE_ME_ACCESS' +MINIO_SECRET_KEY = os.getenv('MINIO_SECRET_KEY') or _json_secret or 'CHANGE_ME_SECRET' +MINIO_REGION = os.getenv('MINIO_REGION', 'us-east-1') # MinIO ignores but boto3 wants some value + + # Addressing / SSL options +MINIO_ADDRESSING = os.getenv('MINIO_ADDRESSING_STYLE', 'path').lower() # 'path' or 'virtual' +MINIO_VERIFY_SSL = os.getenv('MINIO_TLS_VERIFY', '1') not in ('0','false','no') +MINIO_DEBUG = os.getenv('MINIO_DEBUG', '0') in ('1','true','yes') +MINIO_ALLOW_VARIANTS = os.getenv('MINIO_ALLOW_ENDPOINT_VARIANTS', '0') in ('1','true','yes') # normally false with nginx redirect + +LOCAL_FIRMWARE_PATH = str(LOCAL_ROOT_PATH / 'latest' / 'firmware.bin') +LOCAL_MANIFEST_PATH = str(LOCAL_ROOT_PATH / 'latest' / 'update.json') +LOCAL_DATA_DIRECTORY = str(LOCAL_ROOT_PATH / 'latest' / 'data') + +# ============================================================================= +# HELPERS +# ============================================================================= + +def s3_client(): + """Create an S3 client pointed at MinIO endpoint, forcing path-style unless overridden, with short timeouts.""" + addressing = 'path' if MINIO_ADDRESSING not in ('virtual','auto') else 'virtual' + cfg = Config( + s3={'addressing_style': addressing}, + signature_version='s3v4', + connect_timeout=3, + read_timeout=5, + retries={'max_attempts': 2} + ) + if MINIO_DEBUG: + masked_key = (MINIO_ACCESS_KEY[:3] + '...' + MINIO_ACCESS_KEY[-3:]) if MINIO_ACCESS_KEY else 'None' + print(f"[DEBUG] Creating client: endpoint={MINIO_ENDPOINT} addressing={addressing} verifySSL={MINIO_VERIFY_SSL} region={MINIO_REGION} accessKey={masked_key}") + return boto3.client( + 's3', + endpoint_url=MINIO_ENDPOINT, + aws_access_key_id=MINIO_ACCESS_KEY, + aws_secret_access_key=MINIO_SECRET_KEY, + region_name=MINIO_REGION, + verify=MINIO_VERIFY_SSL, + config=cfg, + ) + +def _endpoint_variants(base: str): + """Return endpoint variants only if explicitly allowed; otherwise just the base (nginx handles forwarding).""" + if not MINIO_ALLOW_VARIANTS: + return [base] + # Fallback to previous expanded logic if variants are enabled + try: + variants = [] + if not base: + return variants + base = base.rstrip('/') + proto_sep = '://' + if proto_sep in base: + scheme, rest = base.split(proto_sep,1) + else: + scheme, rest = 'https', base + host_port = rest + if ':' in host_port: + host, port = host_port.split(':',1) + else: + host, port = host_port, '' + variants.append(f"{scheme}://{host_port}") + common_ports = ['9000','443','80'] + for p in common_ports: + if port != p: + variants.append(f"{scheme}://{host}:{p}") + alt_scheme = 'http' if scheme == 'https' else 'https' + variants.append(f"{alt_scheme}://{host_port}") + for p in common_ports: + if port != p: + variants.append(f"{alt_scheme}://{host}:{p}") + seen = set() + uniq = [] + for v in variants: + if v not in seen: + uniq.append(v) + seen.add(v) + return uniq + except Exception: + return [base] + +def create_validated_client(): + """Validate (or create) client using only provided endpoint unless variants enabled.""" + global MINIO_ENDPOINT + primary = MINIO_ENDPOINT + variants = _endpoint_variants(primary) or [primary] + errors = [] + probe_bucket = BUCKET_NAME # we will head the target bucket directly + for candidate in variants: + saved = MINIO_ENDPOINT + MINIO_ENDPOINT = candidate + if MINIO_DEBUG: + print(f"[DEBUG] Probing endpoint candidate: {candidate}") + try: + c = s3_client() + try: + c.head_bucket(Bucket=probe_bucket) + if MINIO_DEBUG: + print(f"[DEBUG] head_bucket succeeded on {candidate} for '{probe_bucket}'.") + return c + except ClientError as e: + msg = str(e) + # Acceptable if bucket not found (we can create later) + if any(code in msg for code in ('404', 'NoSuchBucket', 'NotFound')): + if MINIO_DEBUG: + print(f"[DEBUG] Bucket not found on {candidate} (expected if first deploy). Using this endpoint.") + return c + if 'API Requests must be made to API port' in msg: + errors.append(f"{candidate}: wrong port (console endpoint)") + else: + errors.append(f"{candidate}: {msg}") + MINIO_ENDPOINT = saved + except Exception as ex: + errors.append(f"{candidate}: {ex}") + MINIO_ENDPOINT = saved + continue + print("ERROR: Could not validate any endpoint candidate.") + for e in errors: + print(' - ' + e) + print("Provide correct API endpoint (e.g. https://host:9000) via MINIO_ENDPOINT env var.") + sys.exit(3) + +def list_objects(client, prefix: str): + """Generator yielding object keys under a prefix (non-recursive listing with pagination).""" + kwargs = {'Bucket': BUCKET_NAME, 'Prefix': prefix} + while True: + resp = client.list_objects_v2(**kwargs) + for obj in resp.get('Contents', []): + yield obj['Key'] + if not resp.get('IsTruncated'): + break + kwargs['ContinuationToken'] = resp['NextContinuationToken'] + +def normalize_prefix(p: str) -> str: + p = p.strip('/') + return p + +def join_key(*parts: str) -> str: + parts_clean = [p.strip('/') for p in parts if p is not None and p != ''] + return '/'.join(parts_clean) + +def backup_existing_files(client, destination_prefix: str, backups_prefix: str, backup_folder: str): + if not destination_prefix: + prefix = '' + else: + prefix = destination_prefix + '/' + print(f"Scanning existing objects under '{prefix}' for backup...") + for key in list_objects(client, prefix): + if backups_prefix and key.startswith(backups_prefix + '/'): # Skip prior backups + continue + # relative path within destination + relative = key[len(prefix):] if prefix and key.startswith(prefix) else key + backup_key = join_key(backups_prefix, backup_folder, relative) + print(f"Backup copy: {key} -> {backup_key}") + client.copy_object( + Bucket=BUCKET_NAME, + CopySource={'Bucket': BUCKET_NAME, 'Key': key}, + Key=backup_key, + MetadataDirective='COPY' + ) + +def upload_file(client, local_path: str, key: str, cache_control: str = 'private, max-age=0, no-transform'): + if not os.path.isfile(local_path): + print(f"WARN: File missing, skipping: {local_path}") + return + print(f"Upload: {local_path} -> s3://{BUCKET_NAME}/{key}") + extra_args = { 'CacheControl': cache_control } + client.upload_file(local_path, BUCKET_NAME, key, ExtraArgs=extra_args) + +def upload_directory(client, local_directory: str, destination_prefix: str): + if not os.path.isdir(local_directory): + print(f"WARN: Data directory missing: {local_directory}") + return + for root, _, files in os.walk(local_directory): + for fname in files: + full = os.path.join(root, fname) + rel = os.path.relpath(full, local_directory) + key = join_key(destination_prefix, rel) + upload_file(client, full, key) + +def ensure_bucket(client): + """Ensure bucket exists; provide diagnostics if HeadBucket returns 400/other errors.""" + try: + client.head_bucket(Bucket=BUCKET_NAME) + if MINIO_DEBUG: + print(f"[DEBUG] Bucket '{BUCKET_NAME}' exists.") + return + except ClientError as e: + code = e.response.get('Error', {}).get('Code') + status = e.response.get('ResponseMetadata', {}).get('HTTPStatusCode') + print(f"HeadBucket failed (code={code}, status={status}).") + + # List buckets for diagnostics + try: + resp = client.list_buckets() + bucket_names = [b['Name'] for b in resp.get('Buckets', [])] + print(f"Available buckets: {bucket_names or 'None'}") + except Exception as le: + print(f"WARN: list_buckets failed: {le}") + + if code in ('404', 'NoSuchBucket', 'NotFound'): + print(f"Bucket '{BUCKET_NAME}' not found. Attempting to create...") + try: + client.create_bucket(Bucket=BUCKET_NAME) + print(f"Created bucket '{BUCKET_NAME}'.") + return + except ClientError as ce: + print(f"ERROR: Cannot create bucket: {ce}") + sys.exit(2) + + if status == 400: + print("HINTS: \n - Verify endpoint URL (MINIO_ENDPOINT).\n - Ensure no trailing slash in endpoint.\n - Check that TLS verify matches server cert (set MINIO_TLS_VERIFY=0 to test).\n - Confirm bucket name is correct and DNS compatible.\n - Credentials may lack permission: verify access key policies.") + + # Retry once forcing path style if not already + if MINIO_ADDRESSING != 'path': + print("Retrying with path-style addressing...") + os.environ['MINIO_ADDRESSING_STYLE'] = 'path' + new_client = s3_client() + try: + new_client.head_bucket(Bucket=BUCKET_NAME) + print("Second attempt succeeded with path-style addressing.") + return + except ClientError as e2: + print(f"Second HeadBucket attempt failed: {e2}") + print(f"ERROR: head_bucket ultimately failed: {e}") + sys.exit(2) + +# ============================================================================= +# MAIN +# ============================================================================= + +def main(): + dest_prefix = normalize_prefix(DESTINATION_DIR) + backups_prefix = normalize_prefix(BACKUPS_DIR) if BACKUPS_DIR else '' + client = create_validated_client() + if MINIO_DEBUG: + print("[DEBUG] Starting ensure_bucket phase...") + ensure_bucket(client) + + if CREATE_BACKUP: + ts = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') + backup_folder = f"backup_{ts}" + print(f"Creating backup under '{backups_prefix}/{backup_folder}' from prefix '{dest_prefix}'") + backup_existing_files(client, dest_prefix, backups_prefix, backup_folder) + + # Firmware + if UPLOAD_FIRMWARE: + firmware_key = join_key(dest_prefix, 'firmware.bin') if dest_prefix else 'firmware.bin' + upload_file(client, LOCAL_FIRMWARE_PATH, firmware_key) + + # Manifest + if UPLOAD_MANIFEST: + manifest_key = join_key(dest_prefix, 'update.json') if dest_prefix else 'update.json' + upload_file(client, LOCAL_MANIFEST_PATH, manifest_key) + + # Data directory + if UPLOAD_DATA: + data_prefix = join_key(dest_prefix, 'data') if dest_prefix else 'data' + upload_directory(client, LOCAL_DATA_DIRECTORY, data_prefix) + + print("All uploads complete.") + +if __name__ == '__main__': + main() diff --git a/firmware_update/latest/update.json b/firmware_update/latest/update.json index 4343b57..7c7aa5a 100644 --- a/firmware_update/latest/update.json +++ b/firmware_update/latest/update.json @@ -2,8 +2,14 @@ "version": { "major": 1, "minor": 4, - "patch": 5 + "patch": 7 }, + "release_date": "2024-01-15", + "description": "This is a firmware update.", + "changelog": [ + "Fixed issue with device connectivity.", + "Improved firmware update process." + ], "firmware": { "file": "firmware.bin", "md5": "b8c880418a180efb23260ee093d13e61", diff --git a/firmware_update/minio-boothifier-key.json b/firmware_update/minio-boothifier-key.json new file mode 100644 index 0000000..26c56bc --- /dev/null +++ b/firmware_update/minio-boothifier-key.json @@ -0,0 +1 @@ +{"url":"https://s3-minio.boothwizard.com/api/v1/service-account-credentials","accessKey":"qu3aDl5mWKJjqaQPkR19","secretKey":"5A0rktd88CiwNUQAr6k6OtUuoxJLy2Xqd9E9dmyZ","api":"s3v4","path":"auto"} \ No newline at end of file diff --git a/include/AppUpgrade.h b/include/AppUpgrade.h index 8734aad..41f54f7 100644 --- a/include/AppUpgrade.h +++ b/include/AppUpgrade.h @@ -41,7 +41,8 @@ class AppUpdater { public: Version localVersion; Version otaVersion; - const char* bucketUrl; + // Base URL (bucket) for update resources. Supports either Google Cloud Storage or MinIO (or any HTTP host) + String baseUrl; const char* appName; const char* manifestName; JsonDocument jsonManifest; @@ -68,7 +69,17 @@ class AppUpdater { * @param bucket Base URL for updates * @param fs Filesystem reference */ - AppUpdater(fs::FS& fs, Version localVersion, const char* bucket, const char* manifestName ="update.json", const char* appBin = "firmware.bin" ); + AppUpdater(fs::FS& fs, Version localVersion, const char* bucket, const char* manifestName ="update.json", const char* appBin = "firmware.bin" ); + + /** + * @brief Change the base URL after construction (e.g. switch between MinIO and GCS) + */ + void setBaseUrl(const String& newBaseUrl) { baseUrl = newBaseUrl; } + + /** + * @brief Get the currently configured base URL + */ + const String& getBaseUrl() const { return baseUrl; } /** * @brief Set progress callback function @@ -104,7 +115,7 @@ class AppUpdater { * @param manifestPath Path to manifest file * @return Manifest content as a json document */ - bool checkManifest(void); + bool checkManifest(void); bool updateApp(void); @@ -128,15 +139,21 @@ class AppUpdater { * @param expectedMd5 Expected MD5 hash * @return true if successful */ - bool verifyAndSaveFile(WiFiClient* stream, size_t contentLength, - const char* localPath, const char* expectedMd5); + bool verifyAndSaveFile(WiFiClient* stream, size_t contentLength, + const char* localPath, const char* expectedMd5); /** * @brief Update progress callback * @param percentage Progress percentage * @param newStatus Current status */ - void updateProgress(UpdateStatus newStatus, int percentage, const char* message = nullptr); + void updateProgress(UpdateStatus newStatus, int percentage, const char* message = nullptr); + + /** + * @brief Build a full URL by combining baseUrl and a relative path. Absolute (http/https) paths pass through. + * Ensures exactly one slash joins base and path. Leading slash on path is stripped. + */ + String buildUrl(const char* path) const; String getLocalMD5(const char* filePath); }; diff --git a/include/BleSettings.h b/include/BleSettings.h new file mode 100644 index 0000000..a890a66 --- /dev/null +++ b/include/BleSettings.h @@ -0,0 +1,22 @@ +// BLE settings (loaded from JSON) +#pragma once +#include "Arduino.h" + +// Defaults (used if JSON missing fields) +#define DEFAULT_BT_DEVICE_NAME "ATALIGHTS" +#define DEFAULT_BT_SERVICE "FFE0" +#define DEFAULT_BT_SP110E_CHAR "FFE1" +#define DEFAULT_BT_STICK_CHAR "FFE2" +#define DEFAULT_UPGRADE_SERVICE "abcdef01-2345-6789-1234-56789abcdef0" +#define DEFAULT_UPGRADE_CHAR1 "abcdef01-2345-6789-1234-56789abcdef1" +#define DEFAULT_UPGRADE_CHAR2 "abcdef02-2345-6789-1234-56789abcdef1" + +extern String BTDeviceName; +extern String BTServiceUUID; +extern String BTSP110ECharacteristicUUID; +extern String BTStickCharacteristicUUID; +extern String BTUpgradeServiceUUID; +extern String BTUpgradeCharacteristic1UUID; +extern String BTUpgradeCharacteristic2UUID; + +void Load_BLE_Settings(const String &configPath); \ No newline at end of file diff --git a/src/AppUpgrade.cpp b/src/AppUpgrade.cpp index 991a952..91e6c90 100644 --- a/src/AppUpgrade.cpp +++ b/src/AppUpgrade.cpp @@ -22,9 +22,12 @@ Version otaVersion; AppUpdater::AppUpdater(fs::FS& fs, Version localVersion, const char* bucket, const char* manifestName, const char* appBin) - : localVersion(localVersion), bucketUrl(bucket), manifestName(manifestName), appName(appBin), fileSystem(fs), downloadBuffer(new uint8_t[BUFFER_SIZE]) + : localVersion(localVersion), manifestName(manifestName), appName(appBin), fileSystem(fs), downloadBuffer(new uint8_t[BUFFER_SIZE]) { - ESP_LOGI(TAG, "AppUpdater initialized with version %s", localVersion.toString().c_str()); + baseUrl = bucket ? String(bucket) : String(DEFAULT_MANIFEST_URL); + // Ensure baseUrl ends with a single '/' + if(!baseUrl.endsWith("/")) baseUrl += "/"; + ESP_LOGI(TAG, "AppUpdater initialized (local v%s) baseUrl=%s", localVersion.toString().c_str(), baseUrl.c_str()); } void AppUpdater::setProgressCallback(void (*callback)( UpdateStatus status, int percentage, const char* message)) { @@ -39,7 +42,7 @@ void AppUpdater::updateProgress(UpdateStatus newStatus, int percentage, const ch } bool AppUpdater::checkManifest() { - String url = String(bucketUrl) + manifestName; + String url = buildUrl(manifestName); ESP_LOGD(TAG, "Fetching manifest from: %s", url.c_str()); // Start the HTTP client and Send GET request for manifest @@ -110,7 +113,7 @@ bool AppUpdater::updateFile(const char* remotePath, const char* localPath, const //updateProgress(UpdateStatus::DOWNLOADING, 0, localPath); // Construct full URL - String url = String(bucketUrl) + remotePath; + String url = buildUrl(remotePath); ESP_LOGD(TAG, "Downloading: %s -> %s", url.c_str(), localPath); String localMd5 = getLocalMD5(localPath); @@ -295,7 +298,7 @@ bool AppUpdater::updateApp() { // Get the firmware MD5 hash and URL const char* expectedMd5 = jsonManifest["firmware"]["md5"]; - String firmwareUrl = String(bucketUrl) + appName; + String firmwareUrl = buildUrl(appName); // Download the firmware HTTPClient http; @@ -393,6 +396,19 @@ bool AppUpdater::IsUpdateAvailable(){ return updateAvailable; } +String AppUpdater::buildUrl(const char* path) const { + if(!path || !*path) return baseUrl; // just base + String p(path); + // If already absolute URL, pass through + if(p.startsWith("http://") || p.startsWith("https://")) return p; + // Strip leading slashes to avoid double + while(p.startsWith("/")) p.remove(0,1); + // Ensure baseUrl has single trailing slash + String b = baseUrl; + if(!b.endsWith("/")) b += "/"; + return b + p; +} + AsyncEventSource* eventProgress = nullptr; void startFirmwareUpdateTask(AsyncEventSource* evProg) { @@ -491,8 +507,8 @@ void loadUpdateJson(void) { // Get update configuration JsonObject jObj = doc.as(); - String baseUrl = jsonConstrainString(TAG, jObj, "baseurl", "https://storage.googleapis.com/boothifier/"); String folderName = jsonConstrainString(TAG, jObj, "folder", "latest/"); + String baseUrl = jsonConstrainString(TAG, jObj, "baseurl", "https://s3-minio.boothwizard.com/boothifier/"); updateUrl = baseUrl + folderName; ESP_LOGD(TAG, "updateUrl: %s", updateUrl.c_str()); diff --git a/src/BLE_SP110E.cpp b/src/BLE_SP110E.cpp index 2a44229..8a5868d 100644 --- a/src/BLE_SP110E.cpp +++ b/src/BLE_SP110E.cpp @@ -3,14 +3,24 @@ #include "esp_log.h" #include "WiFi.h" #include "ATALights.h" +#include "BleSettings.h" static const char *tag = "BLE_SP110E"; -#define BT_SERVICE "FFE0" -#define BT_SP110E_CHARACTERISTIC "FFE1" +//#ifndef BT_SERVICE + // #define BT_SERVICE "FFE0" +//#endif + +//#ifndef BT_SP110E_CHARACTERISTIC +// #define BT_SP110E_CHARACTERISTIC "FFE1" +//#endif + NimBLECharacteristic *pSP110ECharacteristic = nullptr; -#define BT_STICK_CHARACTERISTIC "FFE2" +//#ifndef BT_STICK_CHARACTERISTIC +// #define BT_STICK_CHARACTERISTIC "FFE2" +//#endif + NimBLECharacteristic *pStickCharacteristic = nullptr; NimBLEClient* pStickClient; @@ -258,11 +268,11 @@ void Init_BLE_SP110E(NimBLEServer* pServer) { led_status.count_lsb = 20; // Create BLE Service - NimBLEService *pService = pServer->createService( BT_SERVICE ); + NimBLEService *pService = pServer->createService( BTServiceUUID.c_str() ); // Use the defined service UUID // Create FFE1 Characteristic with WRITE and NOTIFY properties pSP110ECharacteristic = pService->createCharacteristic( - BT_SP110E_CHARACTERISTIC, + BTSP110ECharacteristicUUID.c_str(), NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY ); @@ -272,7 +282,7 @@ void Init_BLE_SP110E(NimBLEServer* pServer) { /************* Light Stick Characteristic ***************/ pStickCharacteristic = pService->createCharacteristic( - BT_STICK_CHARACTERISTIC, + BTStickCharacteristicUUID.c_str(), NIMBLE_PROPERTY::NOTIFY ); @@ -288,7 +298,7 @@ void Init_BLE_SP110E(NimBLEServer* pServer) { // Configure Advertising NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); - pAdvertising->addServiceUUID( BT_SERVICE ); // Advertise the FFE0 service UUID + pAdvertising->addServiceUUID( BTServiceUUID.c_str() ); // Advertise the FFE0 service UUID const uint8_t manufacturerData[] = {0x00, 0x00, 0x38, 0x93, 0x0E, 0x12, 0xAA, 0x08}; // Example Manufacturer Data pAdvertising->setManufacturerData(std::string((char *)manufacturerData, sizeof(manufacturerData))); @@ -333,13 +343,13 @@ void BLE_LightStick_Client_Task(void *parameter) { ESP_LOGI(tag, "Connected to the server"); // Get the service. - NimBLERemoteService* pRemoteService = pStickClient->getService(BT_SERVICE); + NimBLERemoteService* pRemoteService = pStickClient->getService(BTServiceUUID.c_str()); if (pRemoteService == nullptr) { - ESP_LOGE(tag, "Failed to find service UUID: %s", BT_SERVICE); + ESP_LOGE(tag, "Failed to find service UUID: %s", BTServiceUUID.c_str()); pStickClient->disconnect(); } else { // Get the characteristic. - pRemoteCharacteristic = pRemoteService->getCharacteristic(BT_STICK_CHARACTERISTIC); + pRemoteCharacteristic = pRemoteService->getCharacteristic(BTStickCharacteristicUUID.c_str()); if (pRemoteCharacteristic != nullptr && pRemoteCharacteristic->canNotify()) { pRemoteCharacteristic->subscribe(true, [](NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) { diff --git a/src/BLE_UpdateService.cpp b/src/BLE_UpdateService.cpp index 258b305..791db2b 100644 --- a/src/BLE_UpdateService.cpp +++ b/src/BLE_UpdateService.cpp @@ -4,12 +4,13 @@ #include "global.h" #include "AppUpgrade.h" #include "AppVersion.h" +#include "BleSettings.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" +//#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; @@ -42,7 +43,7 @@ class UpgradeChar_Callbacks : public NimBLECharacteristicCallbacks { return; } - DynamicJsonDocument doc(512); + JsonDocument doc; DeserializationError err = deserializeJson(doc, value.substr(jsonStart)); if (err) { ESP_LOGW(tag, "JSON parse error for wifi-connect: %s", err.c_str()); @@ -138,10 +139,10 @@ void bleUpgrade_send_message(String s){ void Init_UpgradeBLEService(NimBLEServer *pServer){ // Create Upgrade BLE Service - pUpgradeService= pServer->createService( UPGRADE_SERVICE_UUID ); + pUpgradeService= pServer->createService( BTUpgradeServiceUUID.c_str() ); pUpgradeCharacteristic1 = pUpgradeService->createCharacteristic( - UPGRADE_CHARACTERISTIC1_UUID, + BTUpgradeCharacteristic1UUID.c_str(), NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY ); @@ -151,7 +152,7 @@ void Init_UpgradeBLEService(NimBLEServer *pServer){ pUpgradeCharacteristic2 = pUpgradeService->createCharacteristic( - UPGRADE_CHARACTERISTIC2_UUID, + BTUpgradeCharacteristic2UUID.c_str(), NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY ); @@ -162,7 +163,7 @@ void Init_UpgradeBLEService(NimBLEServer *pServer){ pUpgradeService->start(); NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); - pAdvertising->addServiceUUID( UPGRADE_SERVICE_UUID ); // Advertise service UUID + pAdvertising->addServiceUUID( BTUpgradeServiceUUID.c_str() ); // Advertise service UUID } diff --git a/src/BleServer.cpp b/src/BleServer.cpp index fa0c711..95ef79d 100644 --- a/src/BleServer.cpp +++ b/src/BleServer.cpp @@ -2,6 +2,7 @@ #include "esp_log.h" #include "BLE_SP110E.h" #include "BLE_UpdateService.h" +#include "BleSettings.h" static const char* tag = "BleServer"; @@ -36,8 +37,7 @@ void Init_BleServer( bool isSP110EActive, bool isUpgradeActive) { ESP_LOGI(tag, "Initializing BLE..."); static bool isInitialized = false; if (!isInitialized) { - //NimBLEDevice::init("ATALIGHTS"); - NimBLEDevice::init("SP110E-ATA"); + NimBLEDevice::init(BTDeviceName.c_str()); //NimBLEDevice::setMTU(247); // Set preferred MTU size (max 247 for BLE) isInitialized = true; } diff --git a/src/BleSettings.cpp b/src/BleSettings.cpp new file mode 100644 index 0000000..3e2d549 --- /dev/null +++ b/src/BleSettings.cpp @@ -0,0 +1,69 @@ +#include "BleSettings.h" +#include "FS.h" +#include +#include +#include "esp_log.h" + +static const char *tag = "ble-settings"; + +// Global variables (initialized with defaults) +String BTDeviceName = DEFAULT_BT_DEVICE_NAME; +String BTServiceUUID = DEFAULT_BT_SERVICE; +String BTSP110ECharacteristicUUID = DEFAULT_BT_SP110E_CHAR; +String BTStickCharacteristicUUID = DEFAULT_BT_STICK_CHAR; +String BTUpgradeServiceUUID = DEFAULT_UPGRADE_SERVICE; +String BTUpgradeCharacteristic1UUID = DEFAULT_UPGRADE_CHAR1; +String BTUpgradeCharacteristic2UUID = DEFAULT_UPGRADE_CHAR2; + +static String safeJsonString(JsonVariant obj, const char *key, const char *defVal) { + if (!obj.is()) return defVal; + JsonVariant v = obj[key]; + if (!v.is()) return defVal; + const char *s = v.as(); + if (!s || *s == '\0') return defVal; + return String(s); +} + +void Load_BLE_Settings(const String &configPath) { + File file = LittleFS.open(configPath, "r"); + if (!file) { + ESP_LOGW(tag, "Config %s not found. Using defaults.", configPath.c_str()); + return; // keep defaults + } + + // Use a dynamic document sized to file length (clamped) + size_t sz = file.size(); + if (sz == 0 || sz > 4096) { // protect against unrealistically large file + ESP_LOGE(tag, "Invalid config size: %u", (unsigned)sz); + file.close(); + return; + } + + JsonDocument doc; // heuristic + DeserializationError error = deserializeJson(doc, file); + file.close(); + if (error) { + ESP_LOGE(tag, "Deserialize error: %s", error.c_str()); + return; // keep previous / defaults + } + + JsonObject root = doc.as(); + if (root.isNull()) { + ESP_LOGE(tag, "Empty JSON root"); + return; + } + + // Map expected keys + BTDeviceName = safeJsonString(root, "name", BTDeviceName.c_str()); + BTServiceUUID = safeJsonString(root, "lights-service", BTServiceUUID.c_str()); + BTSP110ECharacteristicUUID = safeJsonString(root, "lights-char", BTSP110ECharacteristicUUID.c_str()); + BTStickCharacteristicUUID = safeJsonString(root, "stick-char", BTStickCharacteristicUUID.c_str()); + BTUpgradeServiceUUID = safeJsonString(root, "upgrade-service", BTUpgradeServiceUUID.c_str()); + BTUpgradeCharacteristic1UUID = safeJsonString(root, "upgrade-char1", BTUpgradeCharacteristic1UUID.c_str()); + BTUpgradeCharacteristic2UUID = safeJsonString(root, "upgrade-char2", BTUpgradeCharacteristic2UUID.c_str()); + + ESP_LOGI(tag, "Loaded BLE config: name=%s svc=%s char1=%s stick=%s upg_svc=%s upg1=%s upg2=%s", + BTDeviceName.c_str(), BTServiceUUID.c_str(), BTSP110ECharacteristicUUID.c_str(), + BTStickCharacteristicUUID.c_str(), BTUpgradeServiceUUID.c_str(), + BTUpgradeCharacteristic1UUID.c_str(), BTUpgradeCharacteristic2UUID.c_str()); +} \ No newline at end of file diff --git a/src/global.cpp b/src/global.cpp index 7b60b3f..388f280 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -45,7 +45,6 @@ void print_chip_info(void) { print_ram_info(); } - void printTaskInfo(TaskStatus_t taskStatus) { uint32_t ulTotalRunTime = taskStatus.ulRunTimeCounter; uint32_t ulStatsAsPercentage = (ulTotalRunTime * 100) / ulTotalRunTime; // Total runtime equals 100% diff --git a/src/main.cpp b/src/main.cpp index b06911d..d6af7c7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -33,6 +33,7 @@ #include "ATALights.h" #include "OnEveryN.h" #include "BLE_SP110E.h" +#include "BleSettings.h" #define FREERTOs_DIAGNOSTICS 0 #define OLED_ENABLED 0 @@ -69,6 +70,8 @@ PWM_Output *pwmOutputs[4]; RAMP_LIGHT *rampLight1; RAMP_LIGHT *rampLight2; +bool UpgradeMode = false; + void Init_ADC(void); float readBoardInputVoltage(void); void setupLogLevels(esp_log_level_t logLevel); @@ -122,6 +125,10 @@ void setup() // Load Board Pins Load_Board_Pins(sys_settings.boardPins, board_file_path); + // Set status pins off + setStatusPin1(false); + setStatusPin2(false); + // Load Booth Settings Load_Booth_Settings(sys_settings, booth_file_path); @@ -143,17 +150,22 @@ void setup() // Initialize Ramp Lights Init_Ramp_Lights(sys_settings.rampLightSettings, boardButtons, pwmOutputs); + // Initialize ADC + Init_ADC(); + // Initialize Temperature Sensor Init_TSensor(72); - Init_ADC(); float val = readBoardInputVoltage(); ESP_LOGI(tag, "Input Volage = %f", val); // Initialize BLE + Load_BLE_Settings("/system/ble.json"); if (digitalRead(sys_settings.boardPins.btn[0]) == LOW) { + setStatusPin1(true); + UpgradeMode = true; ESP_LOGE(tag, "Enabling BLE and Update Service"); Init_BleServer(true, true); ESP_LOGW(tag, "Enabling Wifi AP and Client"); @@ -198,7 +210,14 @@ void loop() // Button Scanning ON_EVERY_N_MILLISECONDS(10) { - if (boardButtons[0] != NULL) + for (int i = 0; i < 3; i++) + { + if (boardButtons[i] != NULL) + { + boardButtons[i]->tick(); + } + } + /* { boardButtons[0]->tick(); } @@ -210,6 +229,7 @@ void loop() { boardButtons[2]->tick(); } + */ } // Temperature Monitor @@ -232,10 +252,10 @@ void loop() } // Update Tune Playing - if (anyrtttl::nonblocking::isPlaying()) - { - anyrtttl::nonblocking::play(); - } + //if (anyrtttl::nonblocking::isPlaying()) + //{ + // anyrtttl::nonblocking::play(); + //} // Animation TestMode Timeout #if LEDS_ENABLED @@ -284,6 +304,14 @@ void loop() } } + // Upgrade Mode Tune + if(UpgradeMode){ + ON_EVERY_N_MILLISECONDS(5000) + { + Buzzer_Play_Tune(TUNE_THUMP, true, true); + } + } + #if FREERTOs_DIAGNOSTICS ON_EVERY_N_MILLISECONDS(60000) { diff --git a/src/my_buzzer.cpp b/src/my_buzzer.cpp index 5452cbc..a45ccc9 100644 --- a/src/my_buzzer.cpp +++ b/src/my_buzzer.cpp @@ -33,32 +33,47 @@ void Init_Buzzer(int8_t pin, const char* configFile) void Buzzer_Play_Tune(TUNE_TYPE tune, bool async, bool hasPriority) { static int prev_tune = -1; - if(buzzPin < 0) return; - - if (hasPriority || !anyrtttl::nonblocking::isPlaying()) { - if (anyrtttl::nonblocking::isPlaying()) { - anyrtttl::nonblocking::stop(); - } + if (buzzPin < 0) return; - // Load nonblocking if different from previous - if (prev_tune != tune && async) { - anyrtttl::nonblocking::begin(buzzPin, buzzTune[tune].melody.c_str()); - } - - ESP_LOGD(tag, "Playing tune: %d, melody: %s", tune, buzzTune[tune].melody.c_str()); - - for (int c = 0; c < buzzTune[tune].cycles; c++) { - if (async) { - anyrtttl::nonblocking::play(); - } else { - anyrtttl::blocking::play(buzzPin, buzzTune[tune].melody.c_str()); - } - } - - prev_tune = tune; - } else { - ESP_LOGD(tag, "buzzer busy"); + // Range / data validation + if (tune < 0 || tune >= TUNE_MAX_COUNT) { + ESP_LOGW(tag, "Invalid tune index: %d", (int)tune); + return; } + const String &melody = buzzTune[tune].melody; + if (melody.isEmpty()) { + ESP_LOGW(tag, "Empty melody for tune %d", (int)tune); + return; + } + + // Async mode: begin once, then caller should periodically call again to advance playback + if (async) { + bool playing = anyrtttl::nonblocking::isPlaying(); + if (hasPriority && playing) { + anyrtttl::nonblocking::stop(); + playing = false; + } + if (!playing || prev_tune != tune) { + // (Re)start tune + anyrtttl::nonblocking::begin(buzzPin, melody.c_str()); + prev_tune = tune; + ESP_LOGD(tag, "Started async tune %d (%s)", (int)tune, melody.c_str()); + } + // Advance playback one tick + anyrtttl::nonblocking::play(); + return; + } + + // Blocking mode: play full tune cycles with optional pause + ESP_LOGD(tag, "Playing blocking tune %d cycles=%d pause=%d", (int)tune, buzzTune[tune].cycles, buzzTune[tune].pause); + for (int c = 0; c < buzzTune[tune].cycles; ++c) { + anyrtttl::blocking::play(buzzPin, melody.c_str()); + if (buzzTune[tune].pause > 0 && c + 1 < buzzTune[tune].cycles) { + delay(buzzTune[tune].pause); // simple pause between cycles + } + yield(); // allow other tasks to run + } + prev_tune = tune; } // TODO Buzzer Beep finish diff --git a/src/my_wifi.cpp b/src/my_wifi.cpp index a47f90c..c2d5ae2 100644 --- a/src/my_wifi.cpp +++ b/src/my_wifi.cpp @@ -20,7 +20,6 @@ static const char *tag = "WIFI"; -volatile bool WifiClientConnected = false; volatile bool InternetAvailable; AsyncWebServer webServer(80); AsyncEventSource eventUpgradeProgress("/upgrade-progress"); @@ -125,7 +124,6 @@ bool StartWifiConnectTask(String ssid = "", String pass = "") if (Wifi_Task_Handle == NULL) { ESP_LOGD(tag, "Creating WiFi task"); - WifiClientConnected = false; xTaskCreatePinnedToCore(Wifi_ConnectTask, "Wifi_Task", 1024 * 4, NULL, 1, &Wifi_Task_Handle, 0); } else @@ -148,9 +146,8 @@ void Wifi_ConnectTask(void *parameter) static const char *tag = "Wifi_Task"; wifi_task_running = true; - if (!WifiClientConnected || client_ssid != WiFi.SSID()) + if (WiFi.status() != WL_CONNECTED || client_ssid != WiFi.SSID()) { - WifiClientConnected = false; ESP_LOGD(tag, "Connecting to: %s", client_ssid.c_str()); // Disconnect and connect to new network @@ -181,7 +178,6 @@ void Wifi_ConnectTask(void *parameter) if (WiFi.status() == WL_CONNECTED) { ESP_LOGI(tag, "Connected to %s", client_ssid.c_str()); - WifiClientConnected = true; WiFi.setAutoReconnect(true); if (!Wifi_Save_Credentials("/system/wifi.json")) @@ -391,7 +387,7 @@ void Setup_WebServer_Handlers(AsyncWebServer &server) request->send(200, "application/json", "{\"status\":\"connecting\"}"); }); server.on("/wifi/status", HTTP_GET, [](AsyncWebServerRequest *request) { - String jsonStr = "{\"status\":\"" + String(WifiClientConnected ? "Connected" : "Disconnected") + + String jsonStr = "{\"status\":\"" + String(WiFi.status() == WL_CONNECTED ? "Connected" : "Disconnected") + "\",\"ip\":\"" + WiFi.localIP().toString() + "\"}"; request->send(200, "application/json", jsonStr); }); server.on("/wifi/scans", HTTP_GET, [](AsyncWebServerRequest *request) @@ -547,7 +543,7 @@ void Setup_WebServer_Handlers(AsyncWebServer &server) // System and LED related handlers server.on("/system/summary", HTTP_GET, [](AsyncWebServerRequest *request) { - String response = "{\"status\":\"" + String(WifiClientConnected ? "Connected" : "Disconnected") + "\"}"; + String response = "{\"status\":\"" + String(WiFi.status() == WL_CONNECTED ? "Connected" : "Disconnected") + "\"}"; request->send(200, "application/json", response); }); server.on("/leds/settings", HTTP_GET, [](AsyncWebServerRequest *request) { @@ -557,25 +553,25 @@ void Setup_WebServer_Handlers(AsyncWebServer &server) serializeJson(jsDoc, summary); request->send(200, "application/json", summary); */ - String response = "{\"status\":\"" + String(WifiClientConnected ? "Connected" : "Disconnected") + "\"}"; + String response = "{\"status\":\"" + String(WiFi.status() == WL_CONNECTED ? "Connected" : "Disconnected") + "\"}"; request->send(200, "application/json", response); }); server.on("/leds/settings", HTTP_POST, [](AsyncWebServerRequest *request) { - String response = "{\"status\":\"" + String(WifiClientConnected ? "Connected" : "Disconnected") + "\"}"; + String response = "{\"status\":\"" + String(WiFi.status() == WL_CONNECTED ? "Connected" : "Disconnected") + "\"}"; request->send(200, "application/json", response); }); // LightStik related handlers server.on("/lightstik/settings", HTTP_GET, [](AsyncWebServerRequest *request) { - String response = "{\"status\":\"" + String(WifiClientConnected ? "Connected" : "Disconnected") + "\"}"; + String response = "{\"status\":\"" + String(WiFi.status() == WL_CONNECTED ? "Connected" : "Disconnected") + "\"}"; request->send(200, "application/json", response); }); server.on("/lightstik/settings", HTTP_POST, [](AsyncWebServerRequest *request) { - String response = "{\"status\":\"" + String(WifiClientConnected ? "Connected" : "Disconnected") + "\"}"; + String response = "{\"status\":\"" + String(WiFi.status() == WL_CONNECTED ? "Connected" : "Disconnected") + "\"}"; request->send(200, "application/json", response); }); server.on("/lightstik/register", HTTP_POST, [](AsyncWebServerRequest *request) { - String response = "{\"status\":\"" + String(WifiClientConnected ? "Connected" : "Disconnected") + "\"}"; + String response = "{\"status\":\"" + String(WiFi.status() == WL_CONNECTED ? "Connected" : "Disconnected") + "\"}"; request->send(200, "application/json", response); }); // Firmware Update Handlers @@ -934,7 +930,6 @@ void onWiFiEvent(WiFiEvent_t event) ESP_LOGD(tag, "Connected to AP"); break; case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: - WifiClientConnected = false; ESP_LOGD(tag, "WiFi Disconnected"); setStatusPin1(false); Buzzer_Play_Tune(TUNE_DISCONNECTED); @@ -943,14 +938,12 @@ void onWiFiEvent(WiFiEvent_t event) ESP_LOGD(tag, "Authentication mode of access point has changed"); break; case ARDUINO_EVENT_WIFI_STA_GOT_IP: - WifiClientConnected = true; ESP_LOGD(tag, "My IP: %s", WiFi.localIP().toString()); // Wifi_Start_MDNS(); setStatusPin1(true); Buzzer_Play_Tune(TUNE_CONNECTED); break; case ARDUINO_EVENT_WIFI_STA_LOST_IP: - WifiClientConnected = false; ESP_LOGD(tag, "Lost IP address and IP address is reset to 0"); break; case ARDUINO_EVENT_WPS_ER_SUCCESS: diff --git a/temporary/ata-boothifier-upgrade(copy).html b/temporary/ata-boothifier-upgrade(copy).html new file mode 100644 index 0000000..e69de29 diff --git a/z_old/wifi_temp.cpp b/z_old/wifi_temp.cpp index 4d2645f..56bcacd 100644 --- a/z_old/wifi_temp.cpp +++ b/z_old/wifi_temp.cpp @@ -313,7 +313,7 @@ void Wifi_Save_Credentials(String newSSID, String newPass) } // Parse the JSON file - DynamicJsonDocument doc(1024); + JsonDocument doc; DeserializationError error = deserializeJson(doc, credsFile); if(!error){ JsonObject cred1Json = doc.createNestedObject("cred1"); @@ -675,7 +675,8 @@ void postBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t else if(!strncmp(dataType, "anim-profile", 12)){ if (strlen(dataType) > 12) { int profile_index = (dataType[12] - '0' - 1) % 8; // extract index, assuming dataType has enough characters - DynamicJsonDocument profJson(4 * 1024); + //DynamicJsonDocument profJson(4 * 1024); + JsonDocument profJson; DeserializationError error = deserializeJson(profJson, postData, total); if(!error){ @@ -711,7 +712,7 @@ void postBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t } } else if(!strcmp(dataType, "play-anim")){ - DynamicJsonDocument animData(1024); + JsonDocument animData; DeserializationError error = deserializeJson(animData, postData, total); if(!error){ ANIMATION_EVENT testEvent; @@ -765,7 +766,7 @@ void postBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t getpostSuccess = true; } else if(!strcmp(dataType, "set-pixel")){ - DynamicJsonDocument js(1024); + JsonDocument js; DeserializationError error = deserializeJson(js, postData, total); if(!error){ ANIMATION_EVENT testEvent; @@ -796,7 +797,7 @@ void postBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t Buzzer_Beep(50, 3000); } else if(!strcmp(dataType, "setup-save")){ - DynamicJsonDocument js(1024); + JsonDocument js; DeserializationError error = deserializeJson(js, postData, total); if(!error){ // If app index is different open app-events.json and update @@ -808,7 +809,7 @@ void postBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t return; } - DynamicJsonDocument doc(2048); + JsonDocument doc; DeserializationError error = deserializeJson(doc, file); file.close(); @@ -846,7 +847,7 @@ void postBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t return; } - DynamicJsonDocument doc(2048); + JsonDocument doc; DeserializationError error = deserializeJson(doc, file); file.close();