commit 8-19-25-950
This commit is contained in:
parent
88c2ff3def
commit
d5dcf6c0fe
531
data/ata-boothifier-upgrade(updated).html
Normal file
531
data/ata-boothifier-upgrade(updated).html
Normal file
@ -0,0 +1,531 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ATA Firmware Update</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #f4f4f4;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.status-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.status-indicator-ble {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.status-indicator-wifi {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.status-indicator-internet {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: gray;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Adds space above the WiFi Connect button */
|
||||
.btn-container.wifi {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
max-width: 130px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
button:hover:not(:disabled) {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 90%;
|
||||
max-width: 300px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 14px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>ATA Firmware Update</h1>
|
||||
|
||||
<!-- Status Indicators -->
|
||||
<div class="status-container">
|
||||
<span class="status-indicator-ble"></span>
|
||||
<label id="status-ble-connection">BLE Status: ...</label>
|
||||
</div>
|
||||
|
||||
<div class="status-container">
|
||||
<span class="status-indicator-wifi"></span>
|
||||
<label id="status-wifi-client">Wifi Client: ...</label>
|
||||
</div>
|
||||
|
||||
<div class="status-container">
|
||||
<span class="status-indicator-internet"></span>
|
||||
<label id="status-internet">Internet: ...</label>
|
||||
</div>
|
||||
|
||||
<div class="status-container">
|
||||
<label id="status-current-version">Curr Version: ...</label>
|
||||
</div>
|
||||
<div class="status-container">
|
||||
<label id="status-new-version">New Version: ...</label>
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="btn-container">
|
||||
<button id="bleConnectBtn">Connect</button>
|
||||
<button id="checkStatusBtn" disabled>Check Status</button>
|
||||
</div>
|
||||
|
||||
<!-- Log Area -->
|
||||
<textarea id="logArea" readonly></textarea>
|
||||
|
||||
<div class="btn-container">
|
||||
<button id="checkVersionBtn" disabled>Check Version</button>
|
||||
<button id="startUpgradeBtn" disabled>Start Update</button>
|
||||
</div>
|
||||
|
||||
<!-- Wi-Fi Input Fields -->
|
||||
<div class="input-container">
|
||||
<input type="text" id="wifissid" name="wifissid" placeholder="Enter WiFi SSID" required>
|
||||
<input type="password" id="wifipassword" name="wifipassword" placeholder="Enter WiFi Password" required>
|
||||
<div style="display: flex; align-items: center; gap: 5px;">
|
||||
<input type="checkbox" id="showPassword" style="width: auto;">
|
||||
<label for="showPassword">Show Password</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Added margin-top above this button -->
|
||||
<div class="btn-container wifi">
|
||||
<button id="wifiConnectBtn" disabled>Connect Wifi</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
'use strict';
|
||||
|
||||
// Constants
|
||||
const BLE_SERVER_NAME = "ATALIGHTS"; // Replace with your server name
|
||||
const BLE_SERVICE_UUID = "abcdef01-2345-6789-1234-56789abcdef0"; // Replace with your service UUID
|
||||
const BLE_CHARACTERISTIC1_UUID = "abcdef01-2345-6789-1234-56789abcdef1"; // Replace with your characteristic UUID
|
||||
const BLE_CHARACTERISTIC2_UUID = "abcdef02-2345-6789-1234-56789abcdef1"; // Replace with your characteristic UUID
|
||||
|
||||
let bleDevice = null;
|
||||
let bleCharacteristic1 = null;
|
||||
let bleCharacteristic2 = null;
|
||||
let bleConnected = false;
|
||||
|
||||
const WIFI_STAT = { WIFI_DISCONNECTED:0, WIFI_BAD_CREDS:1, WIFI_NO_AP:2, WIFI_CONNECTED:3 };
|
||||
|
||||
const updatePacket = {
|
||||
wifiConnected: false,
|
||||
wifiOnline: false,
|
||||
wifiIP: [0, 0, 0, 0],
|
||||
currVersion: [0, 0, 0],
|
||||
newVersion: [0, 0, 0]
|
||||
};
|
||||
|
||||
function compareVersions(a, b){
|
||||
for(let i=0;i<3;i++){ if(a[i] > b[i]) return 1; if(a[i] < b[i]) return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Log messages to the textarea
|
||||
function logMessage(message) {
|
||||
const logArea = document.getElementById('logArea');
|
||||
logArea.value += message + '\n';
|
||||
logArea.scrollTop = logArea.scrollHeight;
|
||||
}
|
||||
|
||||
// Function to scan for BLE devices
|
||||
async function scanForDevices() {
|
||||
logMessage('Scanning for BLE devices...');
|
||||
try {
|
||||
const device = await navigator.bluetooth.requestDevice({
|
||||
acceptAllDevices: true,
|
||||
optionalServices: [BLE_SERVICE_UUID]
|
||||
});
|
||||
|
||||
if (device) {
|
||||
logMessage(`Found device: ${device.name || "Unnamed"} (ID: ${device.id})`);
|
||||
} else {
|
||||
logMessage('No devices found.');
|
||||
}
|
||||
} catch (error) {
|
||||
logMessage(`Scan failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to connect to the BLE server
|
||||
async function connectToBle() {
|
||||
if(!navigator.bluetooth){
|
||||
logMessage('Web Bluetooth not supported in this browser.');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
bleDevice = await navigator.bluetooth.requestDevice({
|
||||
filters: [{ name: BLE_SERVER_NAME }],
|
||||
optionalServices: [BLE_SERVICE_UUID]
|
||||
});
|
||||
|
||||
//logMessage(`Connecting to ${bleDevice.name}`);
|
||||
const server = await bleDevice.gatt.connect();
|
||||
//await server.setPreferredMtu(247); // Request larger MTU size
|
||||
|
||||
const service = await server.getPrimaryService(BLE_SERVICE_UUID);
|
||||
|
||||
bleCharacteristic1 = await service.getCharacteristic(BLE_CHARACTERISTIC1_UUID);
|
||||
|
||||
// Subscribe to notifications
|
||||
//await bleCharacteristic1.startNotifications();
|
||||
|
||||
// Add event listener for incoming notifications
|
||||
//bleCharacteristic1.addEventListener('characteristicvaluechanged', handleChar1Notifications);
|
||||
|
||||
|
||||
//logMessage('Getting characteristic...');
|
||||
bleCharacteristic2 = await service.getCharacteristic(BLE_CHARACTERISTIC2_UUID);
|
||||
|
||||
// Subscribe to notifications
|
||||
await bleCharacteristic2.startNotifications();
|
||||
|
||||
// Add event listener for incoming notifications
|
||||
bleCharacteristic2.addEventListener('characteristicvaluechanged', (event) => {
|
||||
const value = event.target.value;
|
||||
const decoder = new TextDecoder();
|
||||
const decodedValue = decoder.decode(value);
|
||||
logMessage('--> ' + decodedValue);
|
||||
});
|
||||
|
||||
bleConnected = true;
|
||||
// Auto-reconnect / state reset handler
|
||||
bleDevice.addEventListener('gattserverdisconnected', handleDisconnect);
|
||||
document.getElementById('bleConnectBtn').disabled = true;
|
||||
document.querySelector('.status-indicator-ble').style.backgroundColor = 'green';
|
||||
document.getElementById('status-ble-connection').textContent ="BLE Status: Connected";
|
||||
document.getElementById('wifiConnectBtn').disabled = false;
|
||||
document.getElementById('checkStatusBtn').disabled = false;
|
||||
logMessage(`Connected to ${bleDevice.name}`);
|
||||
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
|
||||
} catch (error) {
|
||||
if (error.message.includes("cancelled")) {
|
||||
logMessage("Connection cancelled by user.");
|
||||
} else {
|
||||
logMessage(`Connection failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleDisconnect(){
|
||||
bleConnected = false;
|
||||
document.querySelector('.status-indicator-ble').style.backgroundColor = 'gray';
|
||||
document.getElementById('status-ble-connection').textContent = 'BLE Status: Disconnected';
|
||||
document.getElementById('bleConnectBtn').disabled = false;
|
||||
document.getElementById('checkStatusBtn').disabled = true;
|
||||
document.getElementById('wifiConnectBtn').disabled = true;
|
||||
logMessage('BLE disconnected');
|
||||
}
|
||||
|
||||
async function sendPacket(packetMsg) {
|
||||
if (!bleCharacteristic1) {
|
||||
console.log("Cannot send packet: Not connected to BLE server.");
|
||||
return;
|
||||
}
|
||||
|
||||
const maxRetries = 3;
|
||||
const retryDelay = 1000; // 1 second
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
//logMessage(`Sending request: ${packetMsg} (Attempt ${attempt + 1})`);
|
||||
const encoder = new TextEncoder();
|
||||
await bleCharacteristic1.writeValueWithResponse(encoder.encode(packetMsg));
|
||||
//console.log("Request sent successfully");
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(`Failed to send packet: ${error.message}`);
|
||||
attempt++;
|
||||
if (attempt < maxRetries) {
|
||||
console.log(`Retrying in ${retryDelay / 1000} seconds...`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||
} else {
|
||||
console.error("Max retries reached. Failed to send request.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function readPacket() {
|
||||
if (!bleCharacteristic1) {
|
||||
console.log("Cannot read packet: Not connected to BLE server.");
|
||||
return;
|
||||
}
|
||||
|
||||
const maxRetries = 3;
|
||||
const retryDelay = 1000; // 1 second
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
const value = await bleCharacteristic1.readValue();
|
||||
const data = new Uint8Array(value.buffer);
|
||||
if (data.length === 12) {
|
||||
updatePacket.wifiConnected = data[0] !== 0;
|
||||
updatePacket.wifiOnline = data[1] !== 0;
|
||||
updatePacket.wifiIP = [data[2], data[3], data[4], data[5]];
|
||||
updatePacket.currVersion = [data[6], data[7], data[8]];
|
||||
updatePacket.newVersion = [data[9], data[10], data[11]];
|
||||
|
||||
//processUpdatePacket(updatePacket);
|
||||
return;
|
||||
}
|
||||
console.log("Invalid packet length");
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(`Failed to read packet: ${error.message}`);
|
||||
attempt++;
|
||||
if (attempt < maxRetries) {
|
||||
console.log(`Retrying in ${retryDelay / 1000} seconds...`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||
} else {
|
||||
console.error("Max retries reached. Failed to read packet.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process update packet
|
||||
function processUpdatePacket(packet) {
|
||||
// Process the packet data
|
||||
//console.log("Processing update packet:", packet);
|
||||
if(packet.wifiConnected === true) {
|
||||
if(packet.wifiConnected && packet.wifiIP[0] > 0) {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: Connected (' + packet.wifiIP.join('.') + ')';
|
||||
} else {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: Connected';
|
||||
}
|
||||
document.querySelector('.status-indicator-wifi').style.backgroundColor = 'green';
|
||||
} else {
|
||||
document.getElementById('status-wifi-client').textContent = 'Wifi Client: ...';
|
||||
document.querySelector('.status-indicator-wifi').style.backgroundColor = 'gray';
|
||||
}
|
||||
|
||||
if(packet.wifiOnline === true) {
|
||||
document.getElementById('status-internet').textContent = 'Online';
|
||||
document.querySelector('.status-indicator-internet').style.backgroundColor = 'green';
|
||||
document.getElementById('checkVersionBtn').disabled = false;
|
||||
|
||||
} else {
|
||||
document.getElementById('status-internet').textContent = 'Offline';
|
||||
document.querySelector('.status-indicator-internet').style.backgroundColor = 'gray';
|
||||
document.getElementById('checkVersionBtn').disabled = true;
|
||||
}
|
||||
|
||||
if (packet.currVersion[0] > 0) {
|
||||
document.getElementById('status-current-version').textContent = 'Curr Version: ' + packet.currVersion.join('.');
|
||||
} else {
|
||||
document.getElementById('status-current-version').textContent = 'Curr Version: ...';
|
||||
}
|
||||
|
||||
if (packet.newVersion[0] > 0) {
|
||||
document.getElementById('status-new-version').textContent = 'New Version: ' + packet.newVersion.join('.');
|
||||
document.getElementById('checkVersionBtn').disabled = true;
|
||||
|
||||
if(packet.wifiOnline && compareVersions(packet.newVersion, packet.currVersion) > 0) {
|
||||
|
||||
//enable start upgrade button
|
||||
logMessage("New Version Available:");
|
||||
document.getElementById('startUpgradeBtn').disabled = false;
|
||||
} else {
|
||||
//disable start upgrade button
|
||||
document.getElementById('startUpgradeBtn').disabled = true;
|
||||
logMessage("New Version: Not Available");
|
||||
}
|
||||
} else {
|
||||
document.getElementById('status-new-version').textContent = 'New Version: ...';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//BLE_Characteristic.addEventListener('characteristicvaluechanged', handleNotifications);
|
||||
function handleChar1Notifications(event) {
|
||||
const data = new Uint8Array(event.data);
|
||||
|
||||
if (data.length !== 12) { // 1 byte for id, 4 bytes for booleans, 4 bytes for wifiIP, 3 bytes for currVersion, 3 bytes for newVersion
|
||||
console.log("Invalid packet length");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update existing updatePacket object instead of creating new one
|
||||
updatePacket.wifiConnected = data[0] !== 0;
|
||||
updatePacket.wifiOnline = data[1] !== 0;
|
||||
updatePacket.wifiIP = [data[2], data[3], data[4], data[5]];
|
||||
updatePacket.currVersion = [data[6], data[7], data[8]];
|
||||
updatePacket.newVersion = [data[9], data[10], data[11]];
|
||||
|
||||
processUpdatePacket(updatePacket);
|
||||
}
|
||||
|
||||
document.getElementById('showPassword').addEventListener('change', function() {
|
||||
const passwordInput = document.getElementById('wifipassword');
|
||||
passwordInput.type = this.checked ? 'text' : 'password';
|
||||
});
|
||||
|
||||
// Event listeners for buttons
|
||||
document.getElementById('bleConnectBtn').addEventListener('click', connectToBle);
|
||||
document.getElementById('checkStatusBtn').addEventListener('click', async () => {
|
||||
if (bleCharacteristic1) {
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
} else {
|
||||
logMessage('BLE device not connected.');
|
||||
}
|
||||
});
|
||||
document.getElementById('checkVersionBtn').addEventListener('click', async () => {
|
||||
await sendPacket('version-check');
|
||||
// loop and monitor the the updatePacket.newVersion
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
let success = false;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await readPacket();
|
||||
if (updatePacket.newVersion[0] > 0) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
if(success) {
|
||||
processUpdatePacket(updatePacket);
|
||||
logMessage("New Version: Available");
|
||||
} else {
|
||||
logMessage("New Version: Not Available");
|
||||
}
|
||||
|
||||
});
|
||||
document.getElementById('wifiConnectBtn').addEventListener('click', async () => {
|
||||
const ssid = document.getElementById('wifissid').value;
|
||||
const password = document.getElementById('wifipassword').value;
|
||||
if (ssid && password) {
|
||||
// Send credentials to the device (retain original spacing format expected by firmware)
|
||||
const jsonString = ' {"ssid":"' + ssid.trim() + '","pass":"' + password + '"} ';
|
||||
await sendPacket('wifi-connect' + jsonString);
|
||||
|
||||
await readPacket();
|
||||
processUpdatePacket(updatePacket);
|
||||
} else {
|
||||
alert('Please enter both SSID and password.');
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
document.getElementById('startUpgradeBtn').addEventListener('click', async () => {
|
||||
try {
|
||||
await sendPacket('upgrade-start');
|
||||
logMessage("Upgrade Starting... Please wait.");
|
||||
} catch (error) {
|
||||
logMessage(`Error starting upgrade: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Initial call to process the update packet with defaults
|
||||
processUpdatePacket(updatePacket);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
9
data/system/ble.json
Normal file
9
data/system/ble.json
Normal file
@ -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"
|
||||
}
|
||||
@ -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/"
|
||||
}
|
||||
337
firmware_update/UploadToMinio.py
Normal file
337
firmware_update/UploadToMinio.py
Normal file
@ -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()
|
||||
@ -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",
|
||||
|
||||
1
firmware_update/minio-boothifier-key.json
Normal file
1
firmware_update/minio-boothifier-key.json
Normal file
@ -0,0 +1 @@
|
||||
{"url":"https://s3-minio.boothwizard.com/api/v1/service-account-credentials","accessKey":"qu3aDl5mWKJjqaQPkR19","secretKey":"5A0rktd88CiwNUQAr6k6OtUuoxJLy2Xqd9E9dmyZ","api":"s3v4","path":"auto"}
|
||||
@ -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;
|
||||
@ -70,6 +71,16 @@ class AppUpdater {
|
||||
*/
|
||||
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
|
||||
* @param callback Function to call with progress updates
|
||||
@ -138,6 +149,12 @@ class AppUpdater {
|
||||
*/
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
22
include/BleSettings.h
Normal file
22
include/BleSettings.h
Normal file
@ -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);
|
||||
@ -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<JsonObject>();
|
||||
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());
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
69
src/BleSettings.cpp
Normal file
69
src/BleSettings.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include "BleSettings.h"
|
||||
#include "FS.h"
|
||||
#include <LittleFS.h>
|
||||
#include <ArduinoJson.h>
|
||||
#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<JsonObject>()) return defVal;
|
||||
JsonVariant v = obj[key];
|
||||
if (!v.is<const char*>()) return defVal;
|
||||
const char *s = v.as<const char*>();
|
||||
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<JsonObject>();
|
||||
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());
|
||||
}
|
||||
@ -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%
|
||||
|
||||
40
src/main.cpp
40
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)
|
||||
{
|
||||
|
||||
@ -35,30 +35,45 @@ 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();
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
// Async mode: begin once, then caller should periodically call again to advance playback
|
||||
if (async) {
|
||||
anyrtttl::nonblocking::play();
|
||||
} else {
|
||||
anyrtttl::blocking::play(buzzPin, buzzTune[tune].melody.c_str());
|
||||
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;
|
||||
}
|
||||
|
||||
prev_tune = tune;
|
||||
} else {
|
||||
ESP_LOGD(tag, "buzzer busy");
|
||||
// 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
|
||||
|
||||
@ -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:
|
||||
|
||||
0
temporary/ata-boothifier-upgrade(copy).html
Normal file
0
temporary/ata-boothifier-upgrade(copy).html
Normal file
@ -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();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user