commit-9-16-25
This commit is contained in:
parent
084de5cd44
commit
0040c64da8
14
.vscode/tasks.json
vendored
Normal file
14
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "PlatformIO Build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "C:\\Users\\davie\\.platformio\\penv\\Scripts\\platformio.exe",
|
||||||
|
"args": [
|
||||||
|
"run"
|
||||||
|
],
|
||||||
|
"group": "build"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -18,36 +18,36 @@
|
|||||||
{
|
{
|
||||||
"en": true,
|
"en": true,
|
||||||
"freq": 250,
|
"freq": 250,
|
||||||
"min": 0,
|
"min": 0.0,
|
||||||
"max": 100,
|
"max": 100.0,
|
||||||
"default": 0,
|
"default": 0.0,
|
||||||
"vision": false,
|
"vision": false,
|
||||||
"deltarate": 0
|
"deltarate": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"en": true,
|
"en": true,
|
||||||
"freq": 250,
|
"freq": 250,
|
||||||
"min": 0,
|
"min": 0.0,
|
||||||
"max": 100,
|
"max": 100.0,
|
||||||
"default": 0,
|
"default": 0.0,
|
||||||
"vision": false,
|
"vision": false,
|
||||||
"deltarate": 0
|
"deltarate": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"en": true,
|
"en": true,
|
||||||
"freq": 250,
|
"freq": 250,
|
||||||
"min": 0,
|
"min": 0.0,
|
||||||
"max": 100,
|
"max": 100.0,
|
||||||
"default": 0,
|
"default": 0.0,
|
||||||
"vision": true,
|
"vision": true,
|
||||||
"deltarate": 0
|
"deltarate": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"en": true,
|
"en": true,
|
||||||
"freq": 250,
|
"freq": 250,
|
||||||
"min": 0,
|
"min": 0.0,
|
||||||
"max": 100,
|
"max": 100.0,
|
||||||
"default": 0,
|
"default": 0.0,
|
||||||
"vision": true,
|
"vision": true,
|
||||||
"deltarate": 0
|
"deltarate": 0
|
||||||
}
|
}
|
||||||
@ -61,6 +61,7 @@
|
|||||||
"min": 0.0,
|
"min": 0.0,
|
||||||
"max": 100.0,
|
"max": 100.0,
|
||||||
"step": 1.5,
|
"step": 1.5,
|
||||||
|
"rate": 30.0,
|
||||||
"skip-count": 5,
|
"skip-count": 5,
|
||||||
"vision": true
|
"vision": true
|
||||||
},
|
},
|
||||||
@ -71,6 +72,7 @@
|
|||||||
"min": 0.0,
|
"min": 0.0,
|
||||||
"max": 100.0,
|
"max": 100.0,
|
||||||
"step": 1.5,
|
"step": 1.5,
|
||||||
|
"rate": 30.0,
|
||||||
"skip-count": 5,
|
"skip-count": 5,
|
||||||
"vision": true
|
"vision": true
|
||||||
}
|
}
|
||||||
@ -83,7 +85,7 @@
|
|||||||
"t-sensor": {
|
"t-sensor": {
|
||||||
"en": true,
|
"en": true,
|
||||||
"addr": 72,
|
"addr": 72,
|
||||||
"sp1": 85.0,
|
"sp1": 80.0,
|
||||||
"fan-pwr1": 50.0,
|
"fan-pwr1": 50.0,
|
||||||
"sp2": 90.0,
|
"sp2": 90.0,
|
||||||
"fan-pwr2": 100.0,
|
"fan-pwr2": 100.0,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ATALIGHTS",
|
"name": "ATALIGHTS",
|
||||||
|
"unique": true,
|
||||||
"lights-service": "FFE0",
|
"lights-service": "FFE0",
|
||||||
"lights-char": "FFE1",
|
"lights-char": "FFE1",
|
||||||
"stick-char": "FFE2",
|
"stick-char": "FFE2",
|
||||||
|
|||||||
@ -256,6 +256,8 @@
|
|||||||
<div class="status-label" id="status-wifi-client">WiFi Client: ...</div>
|
<div class="status-label" id="status-wifi-client">WiFi Client: ...</div>
|
||||||
<div class="status-indicator" id="indicator-internet"></div>
|
<div class="status-indicator" id="indicator-internet"></div>
|
||||||
<div class="status-label" id="status-internet">Internet: ...</div>
|
<div class="status-label" id="status-internet">Internet: ...</div>
|
||||||
|
<div class="status-indicator" id="indicator-temperature"></div>
|
||||||
|
<div class="status-label" id="status-temperature">Temperature: ...</div>
|
||||||
<span></span> <div class="status-label" id="status-current-version">Current Version: ...</div>
|
<span></span> <div class="status-label" id="status-current-version">Current Version: ...</div>
|
||||||
<span></span> <div class="status-label" id="status-new-version">New Version: ...</div>
|
<span></span> <div class="status-label" id="status-new-version">New Version: ...</div>
|
||||||
</div>
|
</div>
|
||||||
@ -402,9 +404,9 @@ IMPORTANT NOTES:
|
|||||||
const BLE_CHARACTERISTIC1_UUID = "abcdef01-2345-6789-1234-56789abcdef1";
|
const BLE_CHARACTERISTIC1_UUID = "abcdef01-2345-6789-1234-56789abcdef1";
|
||||||
const BLE_CHARACTERISTIC2_UUID = "abcdef02-2345-6789-1234-56789abcdef1";
|
const BLE_CHARACTERISTIC2_UUID = "abcdef02-2345-6789-1234-56789abcdef1";
|
||||||
|
|
||||||
const PACKET_LEN = 32;
|
const PACKET_LEN = 30; // Packed struct: 1+1+4+4+3+3+20 = 36 bytes, but check actual size
|
||||||
const OFF_WIFI_STATUS = 0, OFF_WIFI_ONLINE = 1, OFF_WIFI_IP = 2;
|
const OFF_WIFI_STATUS = 0, OFF_WIFI_ONLINE = 1, OFF_TEMPERATURE = 2;
|
||||||
const OFF_CURR_VER = 6, OFF_NEW_VER = 9, OFF_WIFI_SSID = 12;
|
const OFF_WIFI_IP = 6, OFF_CURR_VER = 10, OFF_NEW_VER = 13, OFF_WIFI_SSID = 16;
|
||||||
|
|
||||||
const WIFI_STAT = { DISCONNECTED:0, BAD_CREDS:1, NO_AP:2, CONNECTED:3 };
|
const WIFI_STAT = { DISCONNECTED:0, BAD_CREDS:1, NO_AP:2, CONNECTED:3 };
|
||||||
const WIFI_STAT_TEXT = ["Disconnected", "Bad Credentials", "AP Not Found", "Connected"];
|
const WIFI_STAT_TEXT = ["Disconnected", "Bad Credentials", "AP Not Found", "Connected"];
|
||||||
@ -413,9 +415,11 @@ IMPORTANT NOTES:
|
|||||||
/* ================= State ================= */
|
/* ================= State ================= */
|
||||||
let bleDevice = null, bleCharacteristic1 = null, bleCharacteristic2 = null;
|
let bleDevice = null, bleCharacteristic1 = null, bleCharacteristic2 = null;
|
||||||
let bleConnected = false;
|
let bleConnected = false;
|
||||||
|
let versionCheckPerformed = false;
|
||||||
const state = {
|
const state = {
|
||||||
wifiStatus: WIFI_STAT.DISCONNECTED,
|
wifiStatus: WIFI_STAT.DISCONNECTED,
|
||||||
wifiOnline: false,
|
wifiOnline: false,
|
||||||
|
temperature: 0.0,
|
||||||
wifiIP: [0, 0, 0, 0],
|
wifiIP: [0, 0, 0, 0],
|
||||||
currVersion: [0, 0, 0],
|
currVersion: [0, 0, 0],
|
||||||
newVersion: [0, 0, 0],
|
newVersion: [0, 0, 0],
|
||||||
@ -429,9 +433,11 @@ IMPORTANT NOTES:
|
|||||||
el.bleIndicator = document.getElementById('indicator-ble');
|
el.bleIndicator = document.getElementById('indicator-ble');
|
||||||
el.wifiIndicator = document.getElementById('indicator-wifi');
|
el.wifiIndicator = document.getElementById('indicator-wifi');
|
||||||
el.internetIndicator = document.getElementById('indicator-internet');
|
el.internetIndicator = document.getElementById('indicator-internet');
|
||||||
|
el.temperatureIndicator = document.getElementById('indicator-temperature');
|
||||||
el.lblBle = document.getElementById('status-ble-connection');
|
el.lblBle = document.getElementById('status-ble-connection');
|
||||||
el.lblWifi = document.getElementById('status-wifi-client');
|
el.lblWifi = document.getElementById('status-wifi-client');
|
||||||
el.lblInternet = document.getElementById('status-internet');
|
el.lblInternet = document.getElementById('status-internet');
|
||||||
|
el.lblTemperature = document.getElementById('status-temperature');
|
||||||
el.lblCurrVer = document.getElementById('status-current-version');
|
el.lblCurrVer = document.getElementById('status-current-version');
|
||||||
el.lblNewVer = document.getElementById('status-new-version');
|
el.lblNewVer = document.getElementById('status-new-version');
|
||||||
el.lblDeviceName = document.getElementById('ble-device-name-label');
|
el.lblDeviceName = document.getElementById('ble-device-name-label');
|
||||||
@ -491,17 +497,35 @@ IMPORTANT NOTES:
|
|||||||
|
|
||||||
/* ================= Packet Handling ================= */
|
/* ================= Packet Handling ================= */
|
||||||
function parsePacket(data) {
|
function parsePacket(data) {
|
||||||
if(data.length !== PACKET_LEN) return false;
|
if(data.length < PACKET_LEN) return false;
|
||||||
|
|
||||||
|
// Debug: Log received packet info
|
||||||
|
console.log(`Received packet: ${data.length} bytes`);
|
||||||
|
console.log('Packet bytes:', Array.from(data.slice(0, 12)).map(b => b.toString(16).padStart(2, '0')).join(' '));
|
||||||
|
|
||||||
state.wifiStatus = data[OFF_WIFI_STATUS];
|
state.wifiStatus = data[OFF_WIFI_STATUS];
|
||||||
if(state.wifiStatus > WIFI_STAT.CONNECTED) state.wifiStatus = WIFI_STAT.DISCONNECTED;
|
if(state.wifiStatus > WIFI_STAT.CONNECTED) state.wifiStatus = WIFI_STAT.DISCONNECTED;
|
||||||
|
|
||||||
state.wifiOnline = !!data[OFF_WIFI_ONLINE];
|
state.wifiOnline = !!data[OFF_WIFI_ONLINE];
|
||||||
|
|
||||||
|
// Parse temperature (4-byte little-endian float at offset 2)
|
||||||
|
const tempBytes = data.slice(OFF_TEMPERATURE, OFF_TEMPERATURE + 4);
|
||||||
|
const tempDataView = new DataView(tempBytes.buffer, tempBytes.byteOffset, 4);
|
||||||
|
state.temperature = tempDataView.getFloat32(0, true); // true = little-endian
|
||||||
|
|
||||||
|
// Debug: Log temperature parsing
|
||||||
|
//console.log(`Temperature bytes: ${Array.from(tempBytes).map(b => b.toString(16).padStart(2, '0')).join(' ')}`);
|
||||||
|
//console.log(`Parsed temperature: ${state.temperature}`);
|
||||||
|
|
||||||
state.wifiIP = Array.from(data.slice(OFF_WIFI_IP, OFF_WIFI_IP + 4));
|
state.wifiIP = Array.from(data.slice(OFF_WIFI_IP, OFF_WIFI_IP + 4));
|
||||||
state.currVersion = Array.from(data.slice(OFF_CURR_VER, OFF_CURR_VER + 3));
|
state.currVersion = Array.from(data.slice(OFF_CURR_VER, OFF_CURR_VER + 3));
|
||||||
state.newVersion = Array.from(data.slice(OFF_NEW_VER, OFF_NEW_VER + 3));
|
state.newVersion = Array.from(data.slice(OFF_NEW_VER, OFF_NEW_VER + 3));
|
||||||
|
|
||||||
let rawSsidBytes = data.slice(OFF_WIFI_SSID, OFF_WIFI_SSID + 20);
|
let rawSsidBytes = data.slice(OFF_WIFI_SSID, OFF_WIFI_SSID + 20);
|
||||||
let zeroIndex = rawSsidBytes.indexOf(0);
|
let zeroIndex = rawSsidBytes.indexOf(0);
|
||||||
if(zeroIndex >= 0) rawSsidBytes = rawSsidBytes.slice(0, zeroIndex);
|
if(zeroIndex >= 0) rawSsidBytes = rawSsidBytes.slice(0, zeroIndex);
|
||||||
state.wifiSSID = rawSsidBytes.length ? String.fromCharCode(...rawSsidBytes) : "";
|
state.wifiSSID = rawSsidBytes.length ? String.fromCharCode(...rawSsidBytes) : "";
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,26 +551,48 @@ IMPORTANT NOTES:
|
|||||||
el.lblInternet.textContent = 'Internet: ' + (state.wifiOnline ? 'Online' : 'Offline');
|
el.lblInternet.textContent = 'Internet: ' + (state.wifiOnline ? 'Online' : 'Offline');
|
||||||
setIndicatorStatus(el.internetIndicator, state.wifiOnline ? 'is-success' : '');
|
setIndicatorStatus(el.internetIndicator, state.wifiOnline ? 'is-success' : '');
|
||||||
|
|
||||||
|
// Temperature
|
||||||
|
|
||||||
|
if(state.temperature > 0) {
|
||||||
|
el.lblTemperature.textContent = `Temperature: ${state.temperature.toFixed(1)}°F`;
|
||||||
|
// Set indicator based on temperature ranges
|
||||||
|
if(state.temperature > 90) {
|
||||||
|
setIndicatorStatus(el.temperatureIndicator, 'is-warning'); // Warning for high temp
|
||||||
|
} else if(state.temperature > 0) {
|
||||||
|
setIndicatorStatus(el.temperatureIndicator, 'is-success'); // Normal temp
|
||||||
|
} else {
|
||||||
|
setIndicatorStatus(el.temperatureIndicator, ''); // Unknown/invalid temp
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
el.lblTemperature.textContent = 'Temperature: ...';
|
||||||
|
setIndicatorStatus(el.temperatureIndicator, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Versions
|
// Versions
|
||||||
el.lblCurrVer.textContent = state.currVersion[0] ? 'Current Version: ' + state.currVersion.join('.') : 'Current Version: ...';
|
el.lblCurrVer.textContent = state.currVersion[0] ? 'Current Version: ' + state.currVersion.join('.') : 'Current Version: ...';
|
||||||
el.lblNewVer.textContent = state.newVersion[0] ? 'New Version: ' + state.newVersion.join('.') : 'New Version: ...';
|
el.lblNewVer.textContent = state.newVersion[0] ? 'New Version: ' + state.newVersion.join('.') : 'New Version: ...';
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
|
el.btnBleConnect.disabled = bleConnected;
|
||||||
|
el.btnBleConnect.textContent = bleConnected ? 'Connected' : 'Connect';
|
||||||
el.btnCheckStatus.disabled = !bleConnected;
|
el.btnCheckStatus.disabled = !bleConnected;
|
||||||
el.btnWifiConnect.disabled = !bleConnected;
|
el.btnWifiConnect.disabled = !bleConnected;
|
||||||
el.btnCheckVersion.disabled = !(bleConnected && state.wifiOnline);
|
el.btnCheckVersion.disabled = !(bleConnected && state.wifiOnline);
|
||||||
const newerAvail = state.newVersion[0] && state.wifiOnline && compareVersions(state.newVersion, state.currVersion) > 0;
|
const newerAvail = versionCheckPerformed && state.newVersion[0] && state.wifiOnline && compareVersions(state.newVersion, state.currVersion) > 0;
|
||||||
el.btnStartUpgrade.disabled = !newerAvail;
|
el.btnStartUpgrade.disabled = !newerAvail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================= BLE Operations ================= */
|
/* ================= BLE Operations ================= */
|
||||||
async function connectToBle(){
|
async function connectToBle(){
|
||||||
if(!navigator.bluetooth){ logMessage('Web Bluetooth not supported.'); return; }
|
if(!navigator.bluetooth){ logMessage('Web Bluetooth not supported.'); return; }
|
||||||
try{
|
|
||||||
bleDevice = await navigator.bluetooth.requestDevice({
|
try{
|
||||||
filters:[{ name: el.inDeviceName.value || BLE_SERVER_NAME }],
|
bleDevice = await navigator.bluetooth.requestDevice({
|
||||||
optionalServices:[BLE_SERVICE_UUID]
|
filters:[{ namePrefix: el.inDeviceName.value || BLE_SERVER_NAME }],
|
||||||
|
optionalServices:[BLE_SERVICE_UUID]
|
||||||
});
|
});
|
||||||
|
|
||||||
const server = await bleDevice.gatt.connect();
|
const server = await bleDevice.gatt.connect();
|
||||||
const service = await server.getPrimaryService(BLE_SERVICE_UUID);
|
const service = await server.getPrimaryService(BLE_SERVICE_UUID);
|
||||||
bleCharacteristic1 = await service.getCharacteristic(BLE_CHARACTERISTIC1_UUID);
|
bleCharacteristic1 = await service.getCharacteristic(BLE_CHARACTERISTIC1_UUID);
|
||||||
@ -621,6 +667,7 @@ IMPORTANT NOTES:
|
|||||||
|
|
||||||
function onDisconnect() {
|
function onDisconnect() {
|
||||||
bleConnected = false;
|
bleConnected = false;
|
||||||
|
versionCheckPerformed = false; // Reset version check flag on disconnect
|
||||||
el.lblDeviceName.textContent = 'Device Name';
|
el.lblDeviceName.textContent = 'Device Name';
|
||||||
updateUI();
|
updateUI();
|
||||||
logMessage('BLE disconnected');
|
logMessage('BLE disconnected');
|
||||||
@ -676,6 +723,7 @@ IMPORTANT NOTES:
|
|||||||
|
|
||||||
async function checkVersion() {
|
async function checkVersion() {
|
||||||
el.btnCheckVersion.disabled = true;
|
el.btnCheckVersion.disabled = true;
|
||||||
|
versionCheckPerformed = false; // Reset flag at start
|
||||||
logMessage('Checking for new version...');
|
logMessage('Checking for new version...');
|
||||||
await sendPacket('version-check');
|
await sendPacket('version-check');
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
@ -683,6 +731,7 @@ IMPORTANT NOTES:
|
|||||||
await delay(750);
|
await delay(750);
|
||||||
await readPacket();
|
await readPacket();
|
||||||
if(state.newVersion[0]) {
|
if(state.newVersion[0]) {
|
||||||
|
versionCheckPerformed = true; // Set flag when version is received
|
||||||
if(compareVersions(state.newVersion, state.currVersion) > 0) {
|
if(compareVersions(state.newVersion, state.currVersion) > 0) {
|
||||||
logMessage('New version available: ' + state.newVersion.join('.'));
|
logMessage('New version available: ' + state.newVersion.join('.'));
|
||||||
} else {
|
} else {
|
||||||
@ -691,7 +740,10 @@ IMPORTANT NOTES:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!state.newVersion[0]) logMessage('No new version info received');
|
if(!state.newVersion[0]) {
|
||||||
|
logMessage('No new version info received');
|
||||||
|
versionCheckPerformed = false; // Ensure flag is false if no version received
|
||||||
|
}
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
4
docs/links.txt
Normal file
4
docs/links.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
SP110E Protocol:
|
||||||
|
https://gist.github.com/mbullington/37957501a07ad065b67d4e8d39bfe012
|
||||||
|
|
||||||
@ -3,11 +3,15 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <FastLED.h>
|
#include <FastLED.h>
|
||||||
#include "ColorPalettes.h"
|
#include "ColorPalettes.h"
|
||||||
|
#include "PWM_Output.h"
|
||||||
|
|
||||||
#define PIXEL_INDEX -3
|
#define PIXEL_INDEX -3
|
||||||
#define SOLID_COLOR_INDEX -2
|
#define SOLID_COLOR_INDEX -2
|
||||||
#define OFF_INDEX -1
|
#define OFF_INDEX -1
|
||||||
|
|
||||||
|
#define WITH_GAPS true
|
||||||
|
#define NO_GAPS false
|
||||||
|
|
||||||
extern uint32_t whiteTimeout;
|
extern uint32_t whiteTimeout;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -44,27 +48,39 @@ typedef struct {
|
|||||||
|
|
||||||
extern LEDSTRIP_SETTINGS ledSettings[2];
|
extern LEDSTRIP_SETTINGS ledSettings[2];
|
||||||
|
|
||||||
// Forward declarations
|
|
||||||
//extern CRGB* leds1;
|
|
||||||
//extern CRGB* leds2;
|
|
||||||
|
|
||||||
void Lights_Control_Task(void *parameters);
|
void RGB_Lights_Control_Task(void *parameters);
|
||||||
void Init_Lights_Task(void);
|
|
||||||
void Lights_Control_Task_Resume(void);
|
void Init_RGB_Lights_Task(void);
|
||||||
void Init_Strip(CRGB* leds, uint8_t pin, int size, const String& colorOrder, const String& chipType, uint8_t bright);
|
|
||||||
|
void Init_RGB_Strip(CRGB* leds, uint8_t pin, int size, const String& colorOrder, const String& chipType, uint8_t bright);
|
||||||
|
|
||||||
EOrder GetEOrderByString(const String& rgbStr);
|
EOrder GetEOrderByString(const String& rgbStr);
|
||||||
|
|
||||||
void Lights_Set_Animation(int animIndex, uint8_t red, uint8_t grn, uint8_t blu);
|
void RGB_Lights_Set_Animation(int animIndex, uint8_t red, uint8_t grn, uint8_t blu);
|
||||||
void Lights_Set_ON(void);
|
|
||||||
void Lights_Set_OFF(void);
|
void RGB_Animations_ON(void);
|
||||||
void Lights_Set_Brightness(uint8_t scale);
|
|
||||||
|
void RGB_Animations_OFF(void);
|
||||||
|
|
||||||
|
void RGB_Lights_Set_Brightness(uint8_t scale);
|
||||||
|
|
||||||
void Lights_Set_White(uint8_t val);
|
void Lights_Set_White(uint8_t val);
|
||||||
|
|
||||||
//void createFirePalette(CRGBPalette16& palette, COLOR_PACK& colorPack);
|
//void createFirePalette(CRGBPalette16& palette, COLOR_PACK& colorPack);
|
||||||
void createFirePalette(CRGBPalette16& palette, const COLOR_PACK& colorPack);
|
void createFirePalette(CRGBPalette16& palette, const COLOR_PACK& colorPack);
|
||||||
|
|
||||||
void loadColorPack(COLOR_PACK& dest, const COLOR_PACK& src);
|
void loadColorPack(COLOR_PACK& dest, const COLOR_PACK& src);
|
||||||
|
|
||||||
|
|
||||||
|
//void Init_Ramp_Front_Light_Task(void);
|
||||||
|
|
||||||
|
//void Ramp_Front_Light_Control_Task(void *parameters);
|
||||||
|
|
||||||
|
//void Start_RampUp_Front_Light(int _rampTime=1000, int _startDelay=0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
#include <FastLED.h>
|
#include <FastLED.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include "ColorPalettes.h"
|
#include "ColorPalettes.h"
|
||||||
|
#include "PWM_Output.h"
|
||||||
|
#include "system.h"
|
||||||
|
|
||||||
#define MinLoopDelay 2
|
#define MinLoopDelay 2
|
||||||
#define MaxLoopDelay 200
|
#define MaxLoopDelay 200
|
||||||
@ -24,19 +26,32 @@ void Anim_Rainbow(bool volatile& loop_active_flag, CRGB* leds, int size, int spe
|
|||||||
|
|
||||||
void Anim_Fire(bool volatile& activeFlag, CRGB* leds, int size, int speed, const CRGBPalette16& firePalette, int shift);
|
void Anim_Fire(bool volatile& activeFlag, CRGB* leds, int size, int speed, const CRGBPalette16& firePalette, int shift);
|
||||||
|
|
||||||
void Anim_Color_Sectors(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, uint8_t numRepeats, int speed);
|
void Anim_Color_Sectors(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, bool gaps, uint8_t numRepeats, int minSpeed, int maxSpeed) ;
|
||||||
|
|
||||||
//template<typename TPalette>
|
//template<typename TPalette>
|
||||||
//void Anim_Color_Gradient_Trans(bool volatile& activeFlag, CRGB* leds, int size, TPalette& palette, uint8_t paletteSize, uint8_t numRepeats, int speed);
|
//void Anim_Color_Gradient_Trans(bool volatile& activeFlag, CRGB* leds, int size, TPalette& palette, uint8_t paletteSize, uint8_t numRepeats, int speed);
|
||||||
//void Anim_Color_Gradient_Red_Yellow_Violet(bool volatile& activeFlag, CRGB* leds, int size, int speed);
|
//void Anim_Color_Gradient_Red_Yellow_Violet(bool volatile& activeFlag, CRGB* leds, int size, int speed);
|
||||||
|
|
||||||
void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int speed, bool randomDecay, bool shorterTail, int cometMultiplier);
|
void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int minSpeed, int maxSpeed, bool randomDecay, int cometMultiplier = 1);
|
||||||
//void Anim_Comet_Rainbow(bool volatile& activeFlag, CRGB* leds, int size, int speed);
|
//void Anim_Comet_Rainbow(bool volatile& activeFlag, CRGB* leds, int size, int speed);
|
||||||
|
|
||||||
void Anim_TimedFill(bool volatile& activeFlag, CRGB* leds, int size, CRGB col1, CRGB col2, int totalDurationMs, int shift);
|
void Anim_TimedFill(bool volatile& activeFlag, CRGB* leds, int size, CRGB col1, CRGB col2, int totalDurationMs, int shift);
|
||||||
|
void Anim_TimedFill_Flash(bool volatile& activeFlag, CRGB* leds, int size, PWM_Output* pwmOut, PWM_OUT_SETTINGS* pwmSettings, int pwmOutDelay, int flashTimeout, CRGB baseCol, CRGB fillCol, int totalDurationMs, int shift = 0);
|
||||||
|
|
||||||
void Anim_ColorBreath(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colors, uint32_t timeMs, int speed);
|
void Anim_ColorBreath(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colors, uint32_t timeMs, int speed);
|
||||||
|
|
||||||
|
void Anim_Sparkle(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colors, int speed, uint8_t sparkleChance);
|
||||||
|
|
||||||
void Anim_GradientRotate(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colors, int speed);
|
void Anim_GradientRotate(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colors, int speed);
|
||||||
|
|
||||||
|
void Anim_SolidWhite(bool volatile& activeFlag, CRGB* leds, PWM_Output* pwmOut, int size, int brightness, int timeoutMs = -1);
|
||||||
|
|
||||||
|
void Anim_Roating_Snakes(bool volatile& activeFlag, CRGB* leds, const COLOR_PACK& colorPack, int brightness);
|
||||||
|
|
||||||
|
void Anim_Snakes(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int speed, int multiplier = 1);
|
||||||
|
|
||||||
|
void Anim_Birds(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int speed , int multiplier = 1);
|
||||||
|
|
||||||
|
void Anim_Morph(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int speed , int morphSteps);
|
||||||
|
|
||||||
uint32_t getRandomValue(uint32_t maxValue);
|
uint32_t getRandomValue(uint32_t maxValue);
|
||||||
|
|||||||
@ -18,6 +18,15 @@
|
|||||||
#define HTTP_RETRY_COUNT 5 // Increased from 3
|
#define HTTP_RETRY_COUNT 5 // Increased from 3
|
||||||
#define HTTP_RETRY_DELAY_MS 1000 // Increased from 500
|
#define HTTP_RETRY_DELAY_MS 1000 // Increased from 500
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update mode enumeration
|
||||||
|
*/
|
||||||
|
enum class UpdateMode {
|
||||||
|
UPDATE_FILES_ONLY, ///< Update files only, skip firmware
|
||||||
|
UPDATE_FIRMWARE_ONLY, ///< Update firmware only, skip files
|
||||||
|
UPDATE_BOTH ///< Update both files and firmware (default)
|
||||||
|
};
|
||||||
|
|
||||||
// Allow external cancellation
|
// Allow external cancellation
|
||||||
extern volatile bool g_UpdateCancelFlag;
|
extern volatile bool g_UpdateCancelFlag;
|
||||||
|
|
||||||
@ -35,15 +44,6 @@ extern TaskHandle_t Update_Task_Handle;
|
|||||||
|
|
||||||
extern Version otaVersion;
|
extern Version otaVersion;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update mode enumeration
|
|
||||||
*/
|
|
||||||
enum class UpdateMode {
|
|
||||||
UPDATE_FILES_ONLY, ///< Update files only, skip firmware
|
|
||||||
UPDATE_FIRMWARE_ONLY, ///< Update firmware only, skip files
|
|
||||||
UPDATE_BOTH ///< Update both files and firmware (default)
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief File information structure
|
* @brief File information structure
|
||||||
*/
|
*/
|
||||||
@ -241,4 +241,5 @@ void setGlobalUpdateMode(UpdateMode mode);
|
|||||||
UpdateMode getGlobalUpdateMode();
|
UpdateMode getGlobalUpdateMode();
|
||||||
void setUpdateModeFilesOnly();
|
void setUpdateModeFilesOnly();
|
||||||
void setUpdateModeFirmwareOnly();
|
void setUpdateModeFirmwareOnly();
|
||||||
void setUpdateModeBoth();
|
void setUpdateModeBoth();
|
||||||
|
|
||||||
|
|||||||
@ -9,33 +9,182 @@ typedef struct {
|
|||||||
} COLOR_PACK;
|
} COLOR_PACK;
|
||||||
|
|
||||||
|
|
||||||
const COLOR_PACK colorPack_FireRed PROGMEM = { 4, { CRGB::Red, CRGB::OrangeRed, CRGB::Yellow, CRGB::Black } };
|
const COLOR_PACK colorPack_Fire_Red PROGMEM = { 4, { CRGB::Red, CRGB::OrangeRed, CRGB::Yellow, CRGB::Black } };
|
||||||
const COLOR_PACK colorPack_FireGreen PROGMEM = { 4, { CRGB::DarkGreen, CRGB::Green, CRGB::LightGreen, CRGB::Black } };
|
const COLOR_PACK colorPack_Fire_Green PROGMEM = { 4, { CRGB::DarkGreen, CRGB::Green, CRGB::LightGreen, CRGB::Black } };
|
||||||
const COLOR_PACK colorPack_FireBlue PROGMEM = { 4, { CRGB::DarkBlue, CRGB::Blue, CRGB::LightBlue, CRGB::Black } };
|
const COLOR_PACK colorPack_Fire_Blue PROGMEM = { 4, { CRGB::DarkBlue, CRGB::Blue, CRGB::LightBlue, CRGB::Black } };
|
||||||
const COLOR_PACK colorPack_FireViolet PROGMEM = { 4, { CRGB::Purple, CRGB::Blue, CRGB::Violet, CRGB::Black } };
|
const COLOR_PACK colorPack_Fire_Violet PROGMEM = { 4, { CRGB::Purple, CRGB::Blue, CRGB::Violet, CRGB::Black } };
|
||||||
|
|
||||||
// Fire (compacted: single PROGMEM array, removes duplicate size constants)
|
// Fire (compacted: single PROGMEM array, removes duplicate size constants)
|
||||||
const COLOR_PACK fireColorPacks[] PROGMEM = {
|
const COLOR_PACK fireColorPacks[] PROGMEM = {
|
||||||
colorPack_FireRed,
|
colorPack_Fire_Red,
|
||||||
colorPack_FireGreen,
|
colorPack_Fire_Green,
|
||||||
colorPack_FireBlue,
|
colorPack_Fire_Blue,
|
||||||
colorPack_FireViolet
|
colorPack_Fire_Violet
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Dual Color Packs
|
||||||
|
const COLOR_PACK colorPack_RED_WHITE PROGMEM = { 2, { CRGB::Red, CRGB::White } };
|
||||||
|
const COLOR_PACK colorPack_ORANGE_WHITE PROGMEM = { 2, { CRGB::DarkOrange, CRGB::White } };
|
||||||
|
const COLOR_PACK colorPack_GREEN_WHITE PROGMEM = { 2, { CRGB::Green, CRGB::White } };
|
||||||
|
const COLOR_PACK colorPack_BLUE_WHITE PROGMEM = { 2, { CRGB::Blue, CRGB::White } };
|
||||||
|
const COLOR_PACK colorPack_PINK_WHITE PROGMEM = { 2, { CRGB::Pink, CRGB::White } };
|
||||||
|
const COLOR_PACK colorPack_PURPLE_WHITE PROGMEM = { 2, { CRGB::DarkViolet, CRGB::White } };
|
||||||
|
const COLOR_PACK colorPack_PURPLE_PINK PROGMEM = { 2, { CRGB::DarkViolet, CRGB::Pink } };
|
||||||
|
const COLOR_PACK colorPack_PURPLE_YELLOW PROGMEM = { 2, { CRGB::Purple, CRGB::Yellow } };
|
||||||
|
|
||||||
|
|
||||||
|
const COLOR_PACK colorPack_RED_YELLOW PROGMEM = { 2, { CRGB::Red, CRGB::Yellow } };
|
||||||
|
const COLOR_PACK colorPack_BLUE_YELLOW PROGMEM = { 2, { CRGB::Blue, CRGB::Yellow } };
|
||||||
|
const COLOR_PACK colorPack_GREEN_YELLOW PROGMEM = { 2, { CRGB::Green, CRGB::Yellow } };
|
||||||
|
const COLOR_PACK colorPack_BLUE_GREEN PROGMEM = { 2, { CRGB::Blue, CRGB::Green } };
|
||||||
|
const COLOR_PACK colorPack_BLUE_RED PROGMEM = { 2, { CRGB::Blue, CRGB::Red } };
|
||||||
|
|
||||||
|
// Additional complementary color pairs
|
||||||
|
|
||||||
|
const COLOR_PACK colorPack_ORANGE_BLUE PROGMEM = { 2, { CRGB::DarkOrange, CRGB::Blue } };
|
||||||
|
const COLOR_PACK colorPack_RED_GREEN PROGMEM = { 2, { CRGB::Red, CRGB::Green } };
|
||||||
|
const COLOR_PACK colorPack_CYAN_RED PROGMEM = { 2, { CRGB::Cyan, CRGB::Red } };
|
||||||
|
const COLOR_PACK colorPack_MAGENTA_GREEN PROGMEM = { 2, { CRGB::Magenta, CRGB::Green } };
|
||||||
|
|
||||||
|
// Warm/cool combinations
|
||||||
|
const COLOR_PACK colorPack_ORANGE_CYAN PROGMEM = { 2, { CRGB::DarkOrange, CRGB::Cyan } };
|
||||||
|
const COLOR_PACK colorPack_PINK_GREEN PROGMEM = { 2, { CRGB::Pink, CRGB::Green } };
|
||||||
|
const COLOR_PACK colorPack_VIOLET_LIME PROGMEM = { 2, { CRGB::DarkViolet, CRGB::Lime } };
|
||||||
|
|
||||||
|
// Analogous combinations
|
||||||
|
const COLOR_PACK colorPack_RED_ORANGE PROGMEM = { 2, { CRGB::Red, CRGB::DarkOrange } };
|
||||||
|
const COLOR_PACK colorPack_BLUE_PURPLE PROGMEM = { 2, { CRGB::Blue, CRGB::Purple } };
|
||||||
|
const COLOR_PACK colorPack_YELLOW_LIME PROGMEM = { 2, { CRGB::Yellow, CRGB::Lime } };
|
||||||
|
|
||||||
|
// Triadic combinations
|
||||||
|
const COLOR_PACK colorPack_RED_CYAN PROGMEM = { 2, { CRGB::Red, CRGB::Cyan } };
|
||||||
|
const COLOR_PACK colorPack_YELLOW_MAGENTA PROGMEM = { 2, { CRGB::Yellow, CRGB::Magenta } };
|
||||||
|
const COLOR_PACK colorPack_GREEN_MAGENTA PROGMEM = { 2, { CRGB::Green, CRGB::Magenta } };
|
||||||
|
|
||||||
|
// 25 Dual Color Packs
|
||||||
|
const COLOR_PACK double_colorPacks[] PROGMEM = {
|
||||||
|
colorPack_RED_WHITE,
|
||||||
|
colorPack_RED_YELLOW,
|
||||||
|
colorPack_BLUE_WHITE,
|
||||||
|
colorPack_PINK_WHITE,
|
||||||
|
colorPack_GREEN_WHITE,
|
||||||
|
colorPack_PURPLE_WHITE,
|
||||||
|
colorPack_GREEN_YELLOW,
|
||||||
|
colorPack_PURPLE_PINK,
|
||||||
|
colorPack_CYAN_RED,
|
||||||
|
|
||||||
|
colorPack_ORANGE_WHITE,
|
||||||
|
|
||||||
|
colorPack_PURPLE_PINK,
|
||||||
|
colorPack_PURPLE_YELLOW,
|
||||||
|
|
||||||
|
colorPack_BLUE_YELLOW,
|
||||||
|
|
||||||
|
colorPack_BLUE_GREEN,
|
||||||
|
colorPack_BLUE_RED,
|
||||||
|
colorPack_ORANGE_BLUE,
|
||||||
|
colorPack_RED_GREEN,
|
||||||
|
|
||||||
|
colorPack_MAGENTA_GREEN,
|
||||||
|
colorPack_ORANGE_CYAN,
|
||||||
|
colorPack_PINK_GREEN,
|
||||||
|
colorPack_VIOLET_LIME,
|
||||||
|
colorPack_RED_ORANGE,
|
||||||
|
colorPack_BLUE_PURPLE,
|
||||||
|
colorPack_YELLOW_LIME,
|
||||||
|
colorPack_RED_CYAN,
|
||||||
|
colorPack_YELLOW_MAGENTA,
|
||||||
|
colorPack_GREEN_MAGENTA
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Sectors
|
// Sectors
|
||||||
const COLOR_PACK colorPack_RAINBOW PROGMEM = { 7, { CRGB::Red, CRGB::OrangeRed, CRGB::Yellow, CRGB::Green, CRGB::Blue, CRGB::BlueViolet, CRGB::MediumVioletRed } };
|
|
||||||
const COLOR_PACK colorPack_USA PROGMEM = { 3, { CRGB::Red, CRGB::White, CRGB::Blue } };
|
|
||||||
const COLOR_PACK colorPack_MEXICO PROGMEM = { 3, { CRGB::Green, CRGB::White, CRGB::Red } };
|
|
||||||
const COLOR_PACK colorPack_CANADA PROGMEM = { 2, { CRGB::Red, CRGB::White } };
|
|
||||||
const COLOR_PACK colorPack_GERMANY PROGMEM = { 3, { CRGB::Black, CRGB::Red, CRGB::Yellow } };
|
|
||||||
|
|
||||||
const COLOR_PACK combo_colorPacks[] PROGMEM = {
|
const COLOR_PACK colorPack_RAINBOW PROGMEM = { 7, { CRGB::Red, CRGB::OrangeRed, CRGB::Yellow, CRGB::Green, CRGB::Blue, CRGB::BlueViolet, CRGB::MediumVioletRed } };
|
||||||
colorPack_RAINBOW,
|
// Triple Color Packs, Common Flags
|
||||||
colorPack_USA,
|
const COLOR_PACK colorPack_RED_WHITE_BLUE PROGMEM = { 3, { CRGB::Red, CRGB::White, CRGB::Blue } };
|
||||||
colorPack_MEXICO,
|
const COLOR_PACK colorPack_RED_WHITE_GREEN PROGMEM = { 3, { CRGB::Red, CRGB::White, CRGB::Green } };
|
||||||
colorPack_CANADA,
|
const COLOR_PACK colorPack_RED_YELLOW_BLUE PROGMEM = { 3, { CRGB::Red, CRGB::Yellow, CRGB::Blue } };
|
||||||
colorPack_GERMANY
|
const COLOR_PACK colorPack_RED_ORANGE_YELLOW PROGMEM = { 3, { CRGB::Red, CRGB::DarkOrange, CRGB::Yellow } };
|
||||||
|
const COLOR_PACK colorPack_RED_YELLOW_GREEN PROGMEM = { 3, { CRGB::Red, CRGB::Yellow, CRGB::Green } };
|
||||||
|
const COLOR_PACK colorPack_RED_PURPLE_BLUE PROGMEM = { 3, { CRGB::Red, CRGB::Purple, CRGB::Blue } };
|
||||||
|
|
||||||
|
const COLOR_PACK colorPack_GREEN_WHITE_RED PROGMEM = { 3, { CRGB::Green, CRGB::White, CRGB::Red } };
|
||||||
|
const COLOR_PACK colorPack_GREEN_WHITE_ORANGE PROGMEM = { 3, { CRGB::Green, CRGB::White, CRGB::DarkOrange } };
|
||||||
|
const COLOR_PACK colorPack_GREEN_WHITE_BLUE PROGMEM = { 3, { CRGB::Green, CRGB::White, CRGB::Blue } };
|
||||||
|
|
||||||
|
|
||||||
|
const COLOR_PACK colorPack_BLUE_WHITE_GREEN PROGMEM = { 3, { CRGB::Blue, CRGB::White, CRGB::Green } };
|
||||||
|
const COLOR_PACK colorPack_BLUE_YELLOW_GREEN PROGMEM = { 3, { CRGB::Blue, CRGB::Yellow, CRGB::Green } };
|
||||||
|
const COLOR_PACK colorPack_BLUE_YELLOW_RED PROGMEM = { 3, { CRGB::Blue, CRGB::Yellow, CRGB::Red } };
|
||||||
|
|
||||||
|
// Additional triple color combinations
|
||||||
|
const COLOR_PACK colorPack_PURPLE_PINK_CYAN PROGMEM = { 3, { CRGB::Purple, CRGB::Pink, CRGB::Cyan } };
|
||||||
|
const COLOR_PACK colorPack_ORANGE_YELLOW_LIME PROGMEM = { 3, { CRGB::DarkOrange, CRGB::Yellow, CRGB::Lime } };
|
||||||
|
const COLOR_PACK colorPack_MAGENTA_YELLOW_CYAN PROGMEM = { 3, { CRGB::Magenta, CRGB::Yellow, CRGB::Cyan } };
|
||||||
|
const COLOR_PACK colorPack_LIME_CYAN_MAGENTA PROGMEM = { 3, { CRGB::Lime, CRGB::Cyan, CRGB::Magenta } };
|
||||||
|
|
||||||
|
// Warm tone combinations
|
||||||
|
const COLOR_PACK colorPack_RED_ORANGE_PINK PROGMEM = { 3, { CRGB::Red, CRGB::DarkOrange, CRGB::Pink } };
|
||||||
|
const COLOR_PACK colorPack_YELLOW_ORANGE_RED PROGMEM = { 3, { CRGB::Yellow, CRGB::DarkOrange, CRGB::Red } };
|
||||||
|
const COLOR_PACK colorPack_PINK_PURPLE_MAGENTA PROGMEM = { 3, { CRGB::Pink, CRGB::Purple, CRGB::Magenta } };
|
||||||
|
|
||||||
|
// Cool tone combinations
|
||||||
|
const COLOR_PACK colorPack_BLUE_CYAN_LIME PROGMEM = { 3, { CRGB::Blue, CRGB::Cyan, CRGB::Lime } };
|
||||||
|
const COLOR_PACK colorPack_PURPLE_BLUE_CYAN PROGMEM = { 3, { CRGB::Purple, CRGB::Blue, CRGB::Cyan } };
|
||||||
|
const COLOR_PACK colorPack_GREEN_CYAN_BLUE PROGMEM = { 3, { CRGB::Green, CRGB::Cyan, CRGB::Blue } };
|
||||||
|
|
||||||
|
// High contrast combinations
|
||||||
|
const COLOR_PACK colorPack_RED_GREEN_BLUE PROGMEM = { 3, { CRGB::Red, CRGB::Green, CRGB::Blue } };
|
||||||
|
const COLOR_PACK colorPack_YELLOW_PURPLE_ORANGE PROGMEM = { 3, { CRGB::Yellow, CRGB::Purple, CRGB::DarkOrange } };
|
||||||
|
const COLOR_PACK colorPack_PINK_LIME_PURPLE PROGMEM = { 3, { CRGB::Pink, CRGB::Lime, CRGB::Purple } };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 25 Triple Color Packs
|
||||||
|
const COLOR_PACK tripple_colorPacks[] PROGMEM = {
|
||||||
|
colorPack_RED_WHITE_BLUE,
|
||||||
|
colorPack_RED_WHITE_GREEN,
|
||||||
|
colorPack_GREEN_WHITE_ORANGE,
|
||||||
|
colorPack_BLUE_YELLOW_GREEN,
|
||||||
|
colorPack_RED_GREEN_BLUE,
|
||||||
|
colorPack_PURPLE_PINK_CYAN,
|
||||||
|
colorPack_YELLOW_ORANGE_RED,
|
||||||
|
colorPack_BLUE_CYAN_LIME,
|
||||||
|
colorPack_PINK_PURPLE_MAGENTA,
|
||||||
|
|
||||||
|
colorPack_RED_YELLOW_BLUE,
|
||||||
|
colorPack_RED_ORANGE_YELLOW,
|
||||||
|
colorPack_RED_YELLOW_GREEN,
|
||||||
|
colorPack_RED_PURPLE_BLUE,
|
||||||
|
colorPack_GREEN_WHITE_RED,
|
||||||
|
colorPack_GREEN_WHITE_BLUE,
|
||||||
|
colorPack_BLUE_WHITE_GREEN,
|
||||||
|
colorPack_BLUE_YELLOW_RED,
|
||||||
|
colorPack_ORANGE_YELLOW_LIME,
|
||||||
|
colorPack_MAGENTA_YELLOW_CYAN,
|
||||||
|
colorPack_LIME_CYAN_MAGENTA,
|
||||||
|
colorPack_RED_ORANGE_PINK,
|
||||||
|
colorPack_PURPLE_BLUE_CYAN,
|
||||||
|
colorPack_GREEN_CYAN_BLUE,
|
||||||
|
colorPack_YELLOW_PURPLE_ORANGE,
|
||||||
|
colorPack_PINK_LIME_PURPLE
|
||||||
|
};
|
||||||
|
|
||||||
|
const COLOR_PACK colorPack_grad_blueish PROGMEM = { 3, { CRGB::Blue, CRGB::Cyan, CRGB::Green } };
|
||||||
|
const COLOR_PACK colorPack_grad_greenish PROGMEM = { 3, { CRGB::Green, CRGB::Lime, CRGB::Cyan } };
|
||||||
|
const COLOR_PACK colorPack_grad_redish PROGMEM = { 3, { CRGB::Red, CRGB::Magenta, CRGB::Pink } };
|
||||||
|
const COLOR_PACK colorPack_grad_violetish PROGMEM = { 3, { CRGB::Purple, CRGB::Magenta, CRGB::Violet } };
|
||||||
|
const COLOR_PACK colorPack_grad_yellowish PROGMEM = { 3, { CRGB::OrangeRed, CRGB::DarkOrange, CRGB::Yellow } };
|
||||||
|
|
||||||
|
|
||||||
|
const COLOR_PACK gradient_colorPack[] PROGMEM = {
|
||||||
|
colorPack_grad_blueish,
|
||||||
|
colorPack_grad_greenish,
|
||||||
|
colorPack_grad_redish,
|
||||||
|
colorPack_grad_violetish,
|
||||||
|
colorPack_grad_yellowish
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -45,20 +194,22 @@ const COLOR_PACK colorPack_Single_Red PROGMEM = { 1, { CRGB::Red } };
|
|||||||
const COLOR_PACK colorPack_Single_Orange PROGMEM = { 1, { CRGB::OrangeRed } };
|
const COLOR_PACK colorPack_Single_Orange PROGMEM = { 1, { CRGB::OrangeRed } };
|
||||||
const COLOR_PACK colorPack_Single_Yellow PROGMEM = { 1, { CRGB::Yellow } };
|
const COLOR_PACK colorPack_Single_Yellow PROGMEM = { 1, { CRGB::Yellow } };
|
||||||
const COLOR_PACK colorPack_Single_Green PROGMEM = { 1, { CRGB::Green } };
|
const COLOR_PACK colorPack_Single_Green PROGMEM = { 1, { CRGB::Green } };
|
||||||
|
const COLOR_PACK colorPack_Single_Cyan PROGMEM = { 1, { CRGB::Cyan } };
|
||||||
const COLOR_PACK colorPack_Single_Blue PROGMEM = { 1, { CRGB::Blue } };
|
const COLOR_PACK colorPack_Single_Blue PROGMEM = { 1, { CRGB::Blue } };
|
||||||
const COLOR_PACK colorPack_Single_Viloet PROGMEM = { 1, { CRGB::DarkViolet } };
|
const COLOR_PACK colorPack_Single_Viloet PROGMEM = { 1, { CRGB::DarkViolet } };
|
||||||
const COLOR_PACK colorPack_Single_Magenta PROGMEM = { 1, { CRGB::Magenta } };
|
const COLOR_PACK colorPack_Single_Magenta PROGMEM = { 1, { CRGB::Magenta } };
|
||||||
const COLOR_PACK colorPack_Single_White PROGMEM = { 1, { CRGB::White } };
|
const COLOR_PACK colorPack_Single_White PROGMEM = { 1, { CRGB::White } };
|
||||||
|
|
||||||
|
// 9 Single Color Packs
|
||||||
const COLOR_PACK single_colorPacks[] PROGMEM = {
|
const COLOR_PACK single_colorPacks[] PROGMEM = {
|
||||||
|
colorPack_Single_White,
|
||||||
colorPack_Single_Red,
|
colorPack_Single_Red,
|
||||||
colorPack_Single_Orange,
|
colorPack_Single_Orange,
|
||||||
colorPack_Single_Yellow,
|
colorPack_Single_Yellow,
|
||||||
colorPack_Single_Green,
|
colorPack_Single_Green,
|
||||||
|
colorPack_Single_Cyan,
|
||||||
colorPack_Single_Blue,
|
colorPack_Single_Blue,
|
||||||
colorPack_Single_Viloet,
|
|
||||||
colorPack_Single_Magenta,
|
colorPack_Single_Magenta,
|
||||||
colorPack_Single_White
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -66,10 +217,12 @@ const COLOR_PACK single_colorPacks[] PROGMEM = {
|
|||||||
|
|
||||||
|
|
||||||
// Dashes
|
// Dashes
|
||||||
|
//const COLOR_PACK colorPack_RAINBOW_Dashes PROGMEM = { 14, { CRGB::Red, CRGB::Black, CRGB::OrangeRed, CRGB::Black, CRGB::Yellow, CRGB::Black, CRGB::Green, CRGB::Black, CRGB::Blue, CRGB::Black, CRGB::BlueViolet, CRGB::Black, CRGB::MediumVioletRed, CRGB::Black } };
|
||||||
const COLOR_PACK colorPack_RedBlack PROGMEM = { 2, { CRGB::Red, CRGB::Black } };
|
const COLOR_PACK colorPack_RedBlack PROGMEM = { 2, { CRGB::Red, CRGB::Black } };
|
||||||
const COLOR_PACK colorPack_OrangeBlack PROGMEM = { 2, { CRGB::DarkOrange, CRGB::Black } };
|
const COLOR_PACK colorPack_OrangeBlack PROGMEM = { 2, { CRGB::DarkOrange, CRGB::Black } };
|
||||||
const COLOR_PACK colorPack_YellowBlack PROGMEM = { 2, { CRGB::Yellow, CRGB::Black } };
|
const COLOR_PACK colorPack_YellowBlack PROGMEM = { 2, { CRGB::Yellow, CRGB::Black } };
|
||||||
const COLOR_PACK colorPack_GreenBlack PROGMEM = { 2, { CRGB::Green, CRGB::Black } };
|
const COLOR_PACK colorPack_GreenBlack PROGMEM = { 2, { CRGB::Green, CRGB::Black } };
|
||||||
|
const COLOR_PACK colorPack_CyanBlack PROGMEM = { 2, { CRGB::Cyan, CRGB::Black } };
|
||||||
const COLOR_PACK colorPack_BlueBlack PROGMEM = { 2, { CRGB::Blue, CRGB::Black } };
|
const COLOR_PACK colorPack_BlueBlack PROGMEM = { 2, { CRGB::Blue, CRGB::Black } };
|
||||||
const COLOR_PACK colorPack_IndigoBlack PROGMEM = { 2, { CRGB::Indigo, CRGB::Black } };
|
const COLOR_PACK colorPack_IndigoBlack PROGMEM = { 2, { CRGB::Indigo, CRGB::Black } };
|
||||||
const COLOR_PACK colorPack_VioletBlack PROGMEM = { 2, { CRGB::MediumVioletRed, CRGB::Black } };
|
const COLOR_PACK colorPack_VioletBlack PROGMEM = { 2, { CRGB::MediumVioletRed, CRGB::Black } };
|
||||||
@ -90,8 +243,6 @@ const COLOR_PACK dashes_ColorPacks[] PROGMEM = {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Create_Red_Yellow_Violet_Palette(CRGBPalette16& customPalette);
|
void Create_Red_Yellow_Violet_Palette(CRGBPalette16& customPalette);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -18,11 +18,14 @@ class PWM_Output {
|
|||||||
void setFreq(uint32_t fq);
|
void setFreq(uint32_t fq);
|
||||||
int linearizeOutput(float inp);
|
int linearizeOutput(float inp);
|
||||||
void setMaxDuty(float duty);
|
void setMaxDuty(float duty);
|
||||||
int getOutVal(void);
|
float getMaxDuty(void){
|
||||||
void setResolution(uint8_t res);
|
|
||||||
float getMaxDuty(void) {
|
|
||||||
return maxDuty;
|
return maxDuty;
|
||||||
}
|
}
|
||||||
|
float getDuty(void) {
|
||||||
|
return currDuty;
|
||||||
|
}
|
||||||
|
int getOutVal(void);
|
||||||
|
void setResolution(uint8_t res);
|
||||||
|
|
||||||
int currOutVal;
|
int currOutVal;
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -7,13 +7,13 @@ enum RAMP_STATE { RampingUp = 0, RampingDown };
|
|||||||
|
|
||||||
class RAMP_LIGHT {
|
class RAMP_LIGHT {
|
||||||
public:
|
public:
|
||||||
RAMP_LIGHT(OneButton* button, PWM_Output* pwmOutput, int min, int max, float step);
|
RAMP_LIGHT(OneButton* button, PWM_Output* pwmOutput, float min, float max, float step);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PWM_Output* pwmOutput;
|
PWM_Output* pwmOutput;
|
||||||
OneButton* button;
|
OneButton* button;
|
||||||
int min;
|
float min;
|
||||||
int max;
|
float max;
|
||||||
float step;
|
float step;
|
||||||
float currentValue;
|
float currentValue;
|
||||||
bool IsOn = false;
|
bool IsOn = false;
|
||||||
|
|||||||
@ -4,16 +4,13 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include "appVersion.h"
|
#include "appVersion.h"
|
||||||
|
|
||||||
//#define FIRMWARE_VERSION "1.0.0"
|
|
||||||
//#define FIRMWARE_VERSION_MAJOR 1
|
|
||||||
//#define FIRMWARE_VERSION_MINOR 4
|
|
||||||
//#define FIRMWARE_VERSION_PATCH 4
|
|
||||||
|
|
||||||
extern Version localVersion;
|
extern Version localVersion;
|
||||||
|
|
||||||
enum COMM_MODE { COMM_WIFI_AP_BLE, COMM_WIFI_AP_CLIENT };
|
enum COMM_MODE { COMM_WIFI_AP_BLE, COMM_WIFI_AP_CLIENT };
|
||||||
extern enum COMM_MODE commMode;
|
extern enum COMM_MODE commMode;
|
||||||
|
|
||||||
|
extern float boardTemperature ;
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
int count;
|
int count;
|
||||||
TaskHandle_t handle[16];
|
TaskHandle_t handle[16];
|
||||||
@ -36,20 +33,27 @@ extern CHIP_INFO chipInfo;
|
|||||||
//#define ENABLE_SYSTEM_STATS 0
|
//#define ENABLE_SYSTEM_STATS 0
|
||||||
|
|
||||||
int findUnusedLedcChannel(void);
|
int findUnusedLedcChannel(void);
|
||||||
//void read_system_settings(void);
|
|
||||||
//void read_app_events_settings(void);
|
|
||||||
//void read_animations_list(void);
|
|
||||||
void report_system_stats(void);
|
void report_system_stats(void);
|
||||||
|
|
||||||
void print_chip_info(void);
|
void print_chip_info(void);
|
||||||
|
|
||||||
void get_chip_mac(char* macStr, size_t size);
|
void get_chip_mac(char* macStr, size_t size);
|
||||||
|
|
||||||
void print_ram_info(void);
|
void print_ram_info(void);
|
||||||
//void Pulse_LED_Status(int mSecs);
|
|
||||||
void printTaskInfo(void);
|
void printTaskInfo(void);
|
||||||
|
|
||||||
void printTaskCPUUsage(TaskHandle_t xTask);
|
void printTaskCPUUsage(TaskHandle_t xTask);
|
||||||
|
|
||||||
void printTaskStackWatermark(TaskHandle_t xTask);
|
void printTaskStackWatermark(TaskHandle_t xTask);
|
||||||
|
|
||||||
void addTaskHandleToList(SYS_TASK_HANDLES &list);
|
void addTaskHandleToList(SYS_TASK_HANDLES &list);
|
||||||
|
|
||||||
String getMacAddress(void);
|
String getMacAddress(void);
|
||||||
|
|
||||||
String macToStr(uint8_t* mac);
|
String macToStr(uint8_t* mac);
|
||||||
|
|
||||||
bool updateJsonDocument(JsonDocument& doc, const char* filePath);
|
bool updateJsonDocument(JsonDocument& doc, const char* filePath);
|
||||||
|
|
||||||
void Log_CPU_Load(void);
|
void Log_CPU_Load(void);
|
||||||
|
|||||||
18
include/my_device.h
Normal file
18
include/my_device.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "system.h"
|
||||||
|
#include "PWM_Output.h"
|
||||||
|
#include "Ramp_Lights.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern SYS_SETTINGS sys_settings;
|
||||||
|
extern PWM_Output *pwmOutputs[4];
|
||||||
|
extern RAMP_LIGHT *rampLight1;
|
||||||
|
extern RAMP_LIGHT *rampLight2;
|
||||||
|
|
||||||
|
void Init_ADC(void);
|
||||||
|
float readBoardInputVoltage(void);
|
||||||
|
void Get_Board_and_Booth_File_Paths(const char *, String &, String &);
|
||||||
|
void Load_Booth_Settings(SYS_SETTINGS &sys, const String &boothPath);
|
||||||
|
void Init_PWM_Outputs(int8_t (&pin)[4], PWM_OUT_SETTINGS (&pwmSettings)[4]);
|
||||||
|
void Init_Ramp_Lights(RAMP_LIGHT_SETTINGS (&settings)[2], OneButton *(&btn)[3], PWM_Output *(&pwm)[4]);
|
||||||
@ -2,21 +2,12 @@
|
|||||||
|
|
||||||
#include <Temperature_LM75_Derived.h>
|
#include <Temperature_LM75_Derived.h>
|
||||||
#include "PWM_Output.h"
|
#include "PWM_Output.h"
|
||||||
|
#include "system.h"
|
||||||
|
|
||||||
typedef struct{
|
extern TSENSOR_SETTINGS *t_settings;
|
||||||
bool enabled;
|
|
||||||
float temperature;
|
|
||||||
float Setpoint1;
|
|
||||||
float Setpoint2;
|
|
||||||
float fanPower1;
|
|
||||||
float fanPower2;
|
|
||||||
float hyst;
|
|
||||||
}T_SENSOR;
|
|
||||||
|
|
||||||
extern T_SENSOR tSensorSettings;
|
|
||||||
extern TI_TMP102_Compatible *tSensor;
|
extern TI_TMP102_Compatible *tSensor;
|
||||||
|
|
||||||
void Init_TSensor(uint8_t addr);
|
void Init_TSensor(uint8_t addr, TSENSOR_SETTINGS *tsettings);
|
||||||
void UpdateFanControl(float temperature, PWM_Output* pwmOut);
|
void UpdateFanControl(float temperature, PWM_Output* pwmOut);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,10 @@
|
|||||||
#include "ColorPalettes.h"
|
#include "ColorPalettes.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
//#include <crgb.h>
|
//#include <crgb.h>
|
||||||
|
#include "PWM_Output.h"
|
||||||
|
#include "my_device.h"
|
||||||
|
#include "system.h"
|
||||||
|
#include "my_device.h"
|
||||||
|
|
||||||
#define FASTLED_CORE 0
|
#define FASTLED_CORE 0
|
||||||
static const char* tag = "strips";
|
static const char* tag = "strips";
|
||||||
@ -17,12 +21,15 @@ static const char* tag = "strips";
|
|||||||
uint32_t whiteTimeout = 0;
|
uint32_t whiteTimeout = 0;
|
||||||
|
|
||||||
TaskHandle_t Animation_Task_Handle;
|
TaskHandle_t Animation_Task_Handle;
|
||||||
|
TaskHandle_t Ramp_Front_Light_Task_Handle;
|
||||||
LEDSTRIP_SETTINGS ledSettings[2];
|
LEDSTRIP_SETTINGS ledSettings[2];
|
||||||
volatile bool AnimationLooping = false;
|
volatile bool AnimationLooping = false;
|
||||||
ANIM_EVENT prevAnimEvent = {0};
|
ANIM_EVENT prevAnimEvent = {0};
|
||||||
QueueHandle_t animationQueue = xQueueCreate( 4, sizeof( ANIM_EVENT ) );
|
QueueHandle_t animationQueue = xQueueCreate( 4, sizeof( ANIM_EVENT ) );
|
||||||
|
|
||||||
void Lights_Set_Animation(int animIndex, uint8_t red, uint8_t grn, uint8_t blu){
|
bool fillAnimationActive = false;
|
||||||
|
|
||||||
|
void RGB_Lights_Set_Animation(int animIndex, uint8_t red, uint8_t grn, uint8_t blu){
|
||||||
|
|
||||||
// Trigger to exit curring looping animation
|
// Trigger to exit curring looping animation
|
||||||
if(AnimationLooping){
|
if(AnimationLooping){
|
||||||
@ -39,65 +46,163 @@ void Lights_Set_Animation(int animIndex, uint8_t red, uint8_t grn, uint8_t blu){
|
|||||||
xQueueSend( animationQueue, &event, 25 );
|
xQueueSend( animationQueue, &event, 25 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lights_Set_ON(){
|
void RGB_Animations_ON(){
|
||||||
Lights_Set_Animation(prevAnimEvent.AnimationIndex, prevAnimEvent.data.red, prevAnimEvent.data.grn, prevAnimEvent.data.blu);
|
RGB_Lights_Set_Animation(prevAnimEvent.AnimationIndex, prevAnimEvent.data.red, prevAnimEvent.data.grn, prevAnimEvent.data.blu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lights_Set_OFF(){
|
void RGB_Animations_OFF(){
|
||||||
Lights_Set_Animation(OFF_INDEX, 0, 0, 0);
|
RGB_Lights_Set_Animation(OFF_INDEX, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lights_Set_Brightness(uint8_t scale){
|
void RGB_Lights_Set_Brightness(uint8_t scale){
|
||||||
FastLED.setBrightness(scale);
|
FastLED.setBrightness(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the white LEDs brightness (0-255)
|
||||||
void Lights_Set_White(uint8_t val){
|
void Lights_Set_White(uint8_t val){
|
||||||
//pwmOut[0]->setOutput(val / 2.5f);
|
//pwmOut[0]->setOutput(val / 2.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init_Lights_Task(void){
|
void Init_RGB_Lights_Task(void){
|
||||||
|
|
||||||
ledSettings[0].leds = new CRGB[ledSettings[0].size];
|
ledSettings[0].leds = new CRGB[ledSettings[0].size];
|
||||||
Init_Strip(ledSettings[0].leds, ledSettings[0].pin, ledSettings[0].size, ledSettings[0].rgbOrder, ledSettings[0].chip, ledSettings[0].bright);
|
Init_RGB_Strip(ledSettings[0].leds, ledSettings[0].pin, ledSettings[0].size, ledSettings[0].rgbOrder, ledSettings[0].chip, ledSettings[0].bright);
|
||||||
ESP_LOGD(tag, "Initializing Strip1: Pin: %d, size: %d, order: %s, chip: %s", ledSettings[0].pin, ledSettings[0].size, ledSettings[0].rgbOrder, ledSettings[0].chip);
|
ESP_LOGD(tag, "Initializing Strip1: Pin: %d, size: %d, order: %s, chip: %s", ledSettings[0].pin, ledSettings[0].size, ledSettings[0].rgbOrder, ledSettings[0].chip);
|
||||||
|
|
||||||
|
|
||||||
ledSettings[1].leds = new CRGB[ledSettings[1].size];;
|
ledSettings[1].leds = new CRGB[ledSettings[1].size];;
|
||||||
Init_Strip(ledSettings[1].leds, ledSettings[1].pin, ledSettings[1].size, ledSettings[1].rgbOrder, ledSettings[1].chip, ledSettings[1].bright);
|
Init_RGB_Strip(ledSettings[1].leds, ledSettings[1].pin, ledSettings[1].size, ledSettings[1].rgbOrder, ledSettings[1].chip, ledSettings[1].bright);
|
||||||
ESP_LOGD(tag, "Initializing Strip2: Pin: %d, size: %d, order: %s, chip: %s", ledSettings[1].pin, ledSettings[1].size, ledSettings[1].rgbOrder, ledSettings[1].chip);
|
ESP_LOGD(tag, "Initializing Strip2: Pin: %d, size: %d, order: %s, chip: %s", ledSettings[1].pin, ledSettings[1].size, ledSettings[1].rgbOrder, ledSettings[1].chip);
|
||||||
|
|
||||||
xTaskCreatePinnedToCore(Lights_Control_Task, "Lights_Task", 1024*6, NULL, 1, &Animation_Task_Handle, FASTLED_CORE);
|
xTaskCreatePinnedToCore(RGB_Lights_Control_Task, "Lights_Task", 1024*6, NULL, 1, &Animation_Task_Handle, FASTLED_CORE);
|
||||||
ESP_LOGI(tag, "Lights Task Created...");
|
ESP_LOGI(tag, "Lights Task Created...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void Init_Ramp_Lights_Task(void){
|
void Init_Ramp_Front_Light_Task(void){
|
||||||
|
// Check if pwmOutputs[0] is available before creating the task
|
||||||
xTaskCreatePinnedToCore(Ramp_Lights_Control_Task, "Ramp_Lights_Task", 1024*1, NULL, 1, &Animation_Task_Handle, (FASTLED_CORE ? 1 : 0));
|
if(pwmOutputs[0] == nullptr) {
|
||||||
ESP_LOGI(tag, "Ramp Lights Task Created...");
|
ESP_LOGE(tag, "Cannot create Ramp Front Light Task: pwmOutputs[0] is not initialized");
|
||||||
}
|
return;
|
||||||
|
|
||||||
void RampUpLights(int level)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Ramp_Lights_Control_Task(void *parameters)
|
|
||||||
{
|
|
||||||
static *OutputPWM* pwmOut = NULL;
|
|
||||||
pwmOut = pwmOut[sys_settings.rampLightSettings[0].pwmOutIndex];
|
|
||||||
|
|
||||||
while(1){
|
|
||||||
sys_settings.rampLightSettings[rampIndex].pwmOutIndex
|
|
||||||
while()
|
|
||||||
|
|
||||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
|
||||||
vTaskSuspend(NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create task with moderate stack size - 2KB should be sufficient
|
||||||
|
BaseType_t result = xTaskCreatePinnedToCore(Ramp_Front_Light_Control_Task, "Ramp_Front_Light_Task", 1024*2, NULL, 1, &Ramp_Front_Light_Task_Handle, (FASTLED_CORE ? 1 : 0));
|
||||||
|
|
||||||
|
if(result != pdPASS) {
|
||||||
|
ESP_LOGE(tag, "Failed to create Ramp Front Light Task, error: %d", result);
|
||||||
|
Ramp_Front_Light_Task_Handle = NULL;
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(tag, "Ramp Front Light Task Created...");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define RAMP_INTERVAL 25
|
||||||
|
#define FRONTLIGHT_TIMEOUT 20000
|
||||||
|
int startDelay = 0;
|
||||||
|
int rampTime = 0;
|
||||||
|
bool rampRestart = false;
|
||||||
|
float FrontLightMin = 0.0;
|
||||||
|
|
||||||
|
void Ramp_Front_Light_Control_Task(void *parameters)
|
||||||
|
{
|
||||||
|
float currPwmValue = 0.0;
|
||||||
|
float outValue = 0.0;
|
||||||
|
int timeCounter = 0;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
|
||||||
|
if(rampRestart == false){
|
||||||
|
vTaskSuspend(NULL); // Wait to be triggered
|
||||||
|
}
|
||||||
|
rampRestart = false; // Clear the flag immediately
|
||||||
|
|
||||||
|
// Safety check: Ensure pwmOutputs[0] is initialized
|
||||||
|
if(pwmOutputs[0] == nullptr) {
|
||||||
|
ESP_LOGE(tag, "pwmOutputs[0] is null, cannot control front light");
|
||||||
|
rampRestart = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for start delay to finish
|
||||||
|
while(!rampRestart && fillAnimationActive && startDelay > 0){
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
startDelay -= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rampRestart){
|
||||||
|
continue; // Restart if requested during delay
|
||||||
|
}
|
||||||
|
|
||||||
|
//currPwmValue = pwmOutputs[0]->getDuty(); // Get current PWM value
|
||||||
|
currPwmValue = FrontLightMin; // Always start from min value
|
||||||
|
pwmOutputs[0]->setOutput(currPwmValue); // Ensure starting point is set
|
||||||
|
|
||||||
|
// Ramp Up the Front Light linearly
|
||||||
|
timeCounter = 0; // Reset timer for ramp up
|
||||||
|
int rampMs = rampTime;
|
||||||
|
while(!rampRestart && fillAnimationActive && rampMs > 0){
|
||||||
|
timeCounter += RAMP_INTERVAL;
|
||||||
|
outValue = map(timeCounter, 0, rampTime, currPwmValue, 100);
|
||||||
|
pwmOutputs[0]->setOutput(outValue);
|
||||||
|
vTaskDelay(RAMP_INTERVAL / portTICK_PERIOD_MS);
|
||||||
|
rampMs -= RAMP_INTERVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rampRestart){
|
||||||
|
continue; // Restart the ramp up if requested
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max Wait fill animation to end or timeout
|
||||||
|
int frontLightTimeout = FRONTLIGHT_TIMEOUT;
|
||||||
|
while(!rampRestart && fillAnimationActive && frontLightTimeout > 0){
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
frontLightTimeout -= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rampRestart){
|
||||||
|
pwmOutputs[0]->setOutput(0); // Ensure it's fully off
|
||||||
|
continue; // Restart the ramp up if requested
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ramp Down the Front Light linearly
|
||||||
|
timeCounter = 0; // Reset timer for ramp down
|
||||||
|
rampMs = rampTime;
|
||||||
|
while(!rampRestart && fillAnimationActive && rampMs > 0){
|
||||||
|
timeCounter += RAMP_INTERVAL;
|
||||||
|
outValue = map(timeCounter, 0, rampTime, 100 , currPwmValue);
|
||||||
|
pwmOutputs[0]->setOutput(outValue);
|
||||||
|
vTaskDelay(RAMP_INTERVAL / portTICK_PERIOD_MS);
|
||||||
|
rampMs -= RAMP_INTERVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cycle completed - ensure clean final state
|
||||||
|
if(!rampRestart){
|
||||||
|
pwmOutputs[0]->setOutput(FrontLightMin); // Ensure final state is minimum
|
||||||
|
fillAnimationActive = false; // Clear animation flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Start_RampUp_Front_Light(int _rampTime, int _startDelay)
|
||||||
|
{
|
||||||
|
// Check if the task exists before trying to control it
|
||||||
|
if(Ramp_Front_Light_Task_Handle == NULL) {
|
||||||
|
ESP_LOGW(tag, "Cannot start front light ramp: task not created");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rampTime = _rampTime;
|
||||||
|
startDelay = _startDelay;
|
||||||
|
fillAnimationActive = true;
|
||||||
|
rampRestart = true;
|
||||||
|
vTaskResume(Ramp_Front_Light_Task_Handle);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
void Animation_Loop_Exit(void){
|
void Animation_Loop_Exit(void){
|
||||||
if( Animation_Task_Handle ){
|
if( Animation_Task_Handle ){
|
||||||
xTaskNotifyGive( Animation_Task_Handle );
|
xTaskNotifyGive( Animation_Task_Handle );
|
||||||
@ -117,17 +222,6 @@ EOrder GetEOrderByString(const String& rgbStr){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LightsON(void){
|
|
||||||
FastLED.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void LightsOff(void){
|
|
||||||
FastLED.clear();
|
|
||||||
FastLED.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void setPixel(LEDSTRIP_SETTINGS& strip, int pixelIndex, CRGB col){
|
void setPixel(LEDSTRIP_SETTINGS& strip, int pixelIndex, CRGB col){
|
||||||
int x = calcPixelIndex(strip, pixelIndex);
|
int x = calcPixelIndex(strip, pixelIndex);
|
||||||
@ -169,7 +263,7 @@ inline void setPixel2(int pixelIndex, const CRGB col) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Init_Strip(CRGB* leds, uint8_t pin, int size, const String& colorOrder, const String& chipType, uint8_t bright) {
|
void Init_RGB_Strip(CRGB* leds, uint8_t pin, int size, const String& colorOrder, const String& chipType, uint8_t bright) {
|
||||||
// Validate inputs
|
// Validate inputs
|
||||||
if (!leds || size <= 0) {
|
if (!leds || size <= 0) {
|
||||||
ESP_LOGE(tag, "Invalid LED array or size");
|
ESP_LOGE(tag, "Invalid LED array or size");
|
||||||
@ -354,25 +448,18 @@ void Init_Strip(CRGB* leds, uint8_t pin, int size, const String& colorOrder, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Lights_Control_Task_Resume(void){
|
void RGB_Lights_Control_Task(void *parameters){
|
||||||
vTaskResume(Animation_Task_Handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Lights_Control_Task(void *parameters){
|
|
||||||
|
|
||||||
ANIM_EVENT AnimEvent;
|
ANIM_EVENT AnimEvent;
|
||||||
CRGB col;
|
CRGB col;
|
||||||
COLOR_PACK colorPack;
|
COLOR_PACK colorPack;
|
||||||
CRGBPalette16 firePalette;
|
CRGBPalette16 firePalette;
|
||||||
|
|
||||||
ESP_LOGD(tag, "Lights Control Task Entered & Suspended...");
|
// Wait for other tasks to initialize (including front light task)
|
||||||
vTaskSuspend(NULL);
|
vTaskDelay(pdMS_TO_TICKS(500)); // wait for everything to settle
|
||||||
ESP_LOGD(tag, "Lights Control Task Resumed...");
|
|
||||||
vTaskDelay(1000);
|
|
||||||
|
|
||||||
Lights_Set_Brightness(48);
|
RGB_Lights_Set_Brightness(48);
|
||||||
Lights_Set_Animation(1, 0, 0, 0); // set rainbow animation
|
RGB_Lights_Set_Animation(23, 0, 0, 0); // set comet rainbow animation
|
||||||
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -399,47 +486,97 @@ void Lights_Control_Task(void *parameters){
|
|||||||
ESP_LOGD(tag, "LEDs Off");
|
ESP_LOGD(tag, "LEDs Off");
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
|
fill_solid(ledSettings[0].leds, ledSettings[0].size, CRGB::Black);
|
||||||
|
FastLED.show();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1: case 2: case 3: case 4: case 5:{ // Timed Fill Animations
|
||||||
Anim_Rainbow(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, 30);
|
int timeDuration = AnimEvent.AnimationIndex * 1000;
|
||||||
break;
|
int whiteDelay = timeDuration - 1000;
|
||||||
case 2: case 3: case 4: { // Timed Fill Animations
|
Anim_TimedFill_Flash(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, pwmOutputs[0], &sys_settings.pwmOutSettings[0], whiteDelay, 10000, CRGB::Black, CRGB::White, timeDuration, ledSettings[0].shift);
|
||||||
int timeDuration = (AnimEvent.AnimationIndex-1) * 1000;
|
|
||||||
Anim_TimedFill(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, CRGB::Black, CRGB::White, timeDuration, ledSettings[0].shift);
|
|
||||||
whiteTimeout = 20;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 5: case 6: case 7: case 8: {// Fire Animations
|
case 6:
|
||||||
COLOR_PACK fp = fireColorPacks[AnimEvent.AnimationIndex - 5]; // copy const pack to mutable
|
// RGB White and Dedicated White all on with timeout and brightness reduction
|
||||||
|
if (pwmOutputs[0] != nullptr) {
|
||||||
|
Anim_SolidWhite(AnimationLooping, ledSettings[0].leds, pwmOutputs[0], ledSettings[0].size, 240, 30000);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
Anim_Rainbow(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, 60);
|
||||||
|
break;
|
||||||
|
case 8: case 9: case 10: case 11: case 12:
|
||||||
|
Anim_GradientRotate(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, gradient_colorPack[AnimEvent.AnimationIndex - 8], 80);
|
||||||
|
break;
|
||||||
|
case 13: case 14: case 15: case 16: case 17: { // Fire Animations
|
||||||
|
COLOR_PACK fp = fireColorPacks[AnimEvent.AnimationIndex - 13]; // copy const pack to mutable
|
||||||
createFirePalette(firePalette, fp);
|
createFirePalette(firePalette, fp);
|
||||||
Anim_Fire(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, 60, firePalette, ledSettings[0].shift);
|
Anim_Fire(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, 60, firePalette, ledSettings[0].shift);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 9: case 10: case 11: case 12: case 13: {//
|
case 18: case 19: case 20:// Sparkle/Twinkle
|
||||||
COLOR_PACK ccp = combo_colorPacks[AnimEvent.AnimationIndex - 9]; // copy const pack to mutable
|
Anim_Sparkle(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, tripple_colorPacks[0], 40, 20);
|
||||||
//loadColorPack(colorPack, colorPack_USA);
|
|
||||||
Anim_Color_Sectors(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, ccp, 1, 80);
|
|
||||||
break;
|
break;
|
||||||
}
|
case 21:
|
||||||
case 14: case 15: case 16: case 17: case 18: {
|
//Anim_ColorBreath(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, colorPack_RAINBOW, 7000, 90);
|
||||||
COLOR_PACK dcp = dashes_ColorPacks[AnimEvent.AnimationIndex - 14]; // copy const pack to mutable
|
Anim_Morph(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, colorPack_RAINBOW, 20, 40);
|
||||||
//loadColorPack(colorPack, colorPack_RedBlack);
|
|
||||||
Anim_Color_Sectors(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, dcp, 4, 80);
|
|
||||||
break;
|
break;
|
||||||
}
|
case 22: // Rain Animation
|
||||||
case 19: case 20: case 21: case 22: case 23: {
|
|
||||||
int idx = AnimEvent.AnimationIndex - 19;
|
|
||||||
Anim_Comets(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, combo_colorPacks[idx], 80, RANDOM_DECAY, true, 1);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case 24:
|
|
||||||
loadColorPack(colorPack, colorPack_RAINBOW);
|
// Comets
|
||||||
Anim_ColorBreath(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, colorPack, 7000, 90);
|
case 23:
|
||||||
|
Anim_Comets(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, colorPack_RAINBOW, 40, 85, RANDOM_DECAY, 1);
|
||||||
break;
|
break;
|
||||||
case 25:
|
case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31:
|
||||||
loadColorPack(colorPack, colorPack_USA);
|
Anim_Comets(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, single_colorPacks[AnimEvent.AnimationIndex - 24], 40, 85, RANDOM_DECAY, 6);
|
||||||
Anim_GradientRotate(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, colorPack, 50);
|
|
||||||
break;
|
break;
|
||||||
|
case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40:
|
||||||
|
Anim_Comets(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, double_colorPacks[AnimEvent.AnimationIndex - 32], 40, 85, RANDOM_DECAY, 3);
|
||||||
|
break;
|
||||||
|
case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49:
|
||||||
|
Anim_Comets(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, tripple_colorPacks[AnimEvent.AnimationIndex - 41], 40, 85, RANDOM_DECAY, 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Snakes
|
||||||
|
case 50:
|
||||||
|
Anim_Snakes(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, colorPack_RAINBOW, 50);
|
||||||
|
break;
|
||||||
|
case 51: case 52: case 53: case 54: case 55: case 56: case 57: case 58:
|
||||||
|
Anim_Snakes(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, single_colorPacks[AnimEvent.AnimationIndex - 51], 60, 6);
|
||||||
|
break;
|
||||||
|
case 59: case 60: case 61: case 62: case 63: case 64: case 65: case 66: case 67:
|
||||||
|
Anim_Snakes(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, double_colorPacks[AnimEvent.AnimationIndex - 59], 60, 3);
|
||||||
|
break;
|
||||||
|
case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 75: case 76:
|
||||||
|
Anim_Snakes(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, tripple_colorPacks[AnimEvent.AnimationIndex - 68], 60, 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Sectors
|
||||||
|
case 77:
|
||||||
|
Anim_Color_Sectors(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, colorPack_RAINBOW, NO_GAPS, 1, 40, 90);
|
||||||
|
break;
|
||||||
|
case 78: case 79: case 80: case 81: case 82: case 83: case 84: case 85: case 86:
|
||||||
|
Anim_Color_Sectors(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, double_colorPacks[AnimEvent.AnimationIndex - 78], NO_GAPS, 3, 40, 90);
|
||||||
|
break;
|
||||||
|
case 87: case 88: case 89: case 90: case 91: case 92: case 93: case 94: case 95:
|
||||||
|
Anim_Color_Sectors(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, tripple_colorPacks[AnimEvent.AnimationIndex - 87], NO_GAPS, 2, 40, 90);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Dashes
|
||||||
|
case 96:
|
||||||
|
Anim_Color_Sectors(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, colorPack_RAINBOW, WITH_GAPS, 1, 40, 90);
|
||||||
|
break;
|
||||||
|
case 97:case 98:case 99: case 100: case 101: case 102: case 103: case 104:
|
||||||
|
Anim_Color_Sectors(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, single_colorPacks[AnimEvent.AnimationIndex - 97], WITH_GAPS, 6, 40, 90);
|
||||||
|
break;
|
||||||
|
case 105: case 106: case 107: case 108: case 109: case 110: case 111: case 112: case 113:
|
||||||
|
Anim_Color_Sectors(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, double_colorPacks[AnimEvent.AnimationIndex - 105], WITH_GAPS, 3, 40, 90);
|
||||||
|
break;
|
||||||
|
case 114: case 115: case 116: case 117: case 118: case 119: case 120: case 121: case 122:
|
||||||
|
Anim_Color_Sectors(AnimationLooping, ledSettings[0].leds, ledSettings[0].size, tripple_colorPacks[AnimEvent.AnimationIndex - 114], WITH_GAPS, 2, 40, 90);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ESP_LOGW(tag, "Loop default");
|
ESP_LOGW(tag, "Loop default");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -10,7 +10,9 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include "PWM_Output.h"
|
#include "PWM_Output.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
static const char* tag = "anims";
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
float minSpeed;
|
float minSpeed;
|
||||||
@ -64,6 +66,46 @@ void Animation_Loop(bool volatile& loop_active_flag, int speed, std::function<in
|
|||||||
loop_active_flag = false;
|
loop_active_flag = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Animation_Loop_Variable(bool volatile& loop_active_flag, std::function<int()> callback) {
|
||||||
|
if (!callback) {
|
||||||
|
ESP_LOGE("Animation_Loop", "Invalid callback function");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop_active_flag = true;
|
||||||
|
|
||||||
|
ulTaskNotifyTake(pdTRUE, 0); // Clear any pending notifications
|
||||||
|
|
||||||
|
TickType_t xLastWakeTime;
|
||||||
|
TickType_t elapsedTicks;
|
||||||
|
TickType_t delayTicks;
|
||||||
|
while(loop_active_flag) {
|
||||||
|
xLastWakeTime = xTaskGetTickCount();
|
||||||
|
|
||||||
|
int loopDelay = 0;
|
||||||
|
try {
|
||||||
|
loopDelay = callback(); // Call animation function and get delay value
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
ESP_LOGE("Animation_Loop", "Callback exception: %s", e.what());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!loop_active_flag) return;
|
||||||
|
|
||||||
|
// Ensure minimum delay protection
|
||||||
|
loopDelay = max(loopDelay, MinLoopDelay);
|
||||||
|
|
||||||
|
// Calculate remaining time with overflow protection
|
||||||
|
elapsedTicks = xTaskGetTickCount() - xLastWakeTime;
|
||||||
|
delayTicks = (elapsedTicks < loopDelay) ? (loopDelay - elapsedTicks) : 0;
|
||||||
|
|
||||||
|
// Check for termination request
|
||||||
|
if (ulTaskNotifyTake(pdTRUE, delayTicks)) { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
loop_active_flag = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Animation Loop Template
|
// Animation Loop Template
|
||||||
void Animation_Loop_Duration(bool volatile& loop_active_flag, int speed, TickType_t durationMs, std::function<int()> callback) {
|
void Animation_Loop_Duration(bool volatile& loop_active_flag, int speed, TickType_t durationMs, std::function<int()> callback) {
|
||||||
loop_active_flag = true;
|
loop_active_flag = true;
|
||||||
@ -195,8 +237,8 @@ void Anim_Rainbow(bool volatile& activeFlag, CRGB* leds, int size, int speed){
|
|||||||
|
|
||||||
// Fire parameters (adjustable)
|
// Fire parameters (adjustable)
|
||||||
const uint8_t FIRE_COOLING = 66;
|
const uint8_t FIRE_COOLING = 66;
|
||||||
const uint8_t FIRE_SPARKING = 62;
|
const uint8_t FIRE_SPARKING = 55;
|
||||||
const uint8_t FIRE_brightness = 255;
|
const uint8_t FIRE_brightness = 240;
|
||||||
|
|
||||||
void Anim_Fire(bool volatile& activeFlag, CRGB* leds, int size, int speed, const CRGBPalette16& firePalette, int shift = 0) {
|
void Anim_Fire(bool volatile& activeFlag, CRGB* leds, int size, int speed, const CRGBPalette16& firePalette, int shift = 0) {
|
||||||
if (!leds || size <= 0) return;
|
if (!leds || size <= 0) return;
|
||||||
@ -252,31 +294,60 @@ void Anim_Fire(bool volatile& activeFlag, CRGB* leds, int size, int speed, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Anim_Color_Sectors(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, uint8_t numRepeats, int speed) {
|
void Anim_Color_Sectors(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, bool gaps, uint8_t numRepeats, int minSpeed, int maxSpeed) {
|
||||||
// Validate inputs
|
// Validate inputs
|
||||||
if (size <= 0 || numRepeats <= 0 || colorPack.size <= 0) return;
|
if (size <= 0 || numRepeats <= 0 || colorPack.size <= 0) return;
|
||||||
|
|
||||||
|
// Calculate effective colors per repeat (original colors + gaps if enabled)
|
||||||
|
int effectiveColorsPerRepeat = gaps ? (colorPack.size * 2) : colorPack.size;
|
||||||
|
|
||||||
// Calculate sector size
|
// Calculate sector size
|
||||||
int sectorSize = size / (colorPack.size * numRepeats);
|
int sectorSize = size / (effectiveColorsPerRepeat * numRepeats);
|
||||||
if (sectorSize < 1) sectorSize = 1;
|
if (sectorSize < 1) sectorSize = 1;
|
||||||
|
|
||||||
// Initialize pattern
|
// Initialize pattern
|
||||||
for (int repeat = 0; repeat < numRepeats; repeat++) {
|
for (int repeat = 0; repeat < numRepeats; repeat++) {
|
||||||
for (int color = 0; color < colorPack.size; color++) {
|
for (int color = 0; color < colorPack.size; color++) {
|
||||||
int startIdx = (repeat * colorPack.size + color) * sectorSize;
|
if (gaps) {
|
||||||
int endIdx = startIdx + sectorSize;
|
// With gaps: each color takes position color*2, gap takes color*2+1
|
||||||
if (endIdx > size) endIdx = size;
|
int colorStartIdx = (repeat * effectiveColorsPerRepeat + color * 2) * sectorSize;
|
||||||
|
int colorEndIdx = colorStartIdx + sectorSize;
|
||||||
// Fill sector with the specified color
|
if (colorEndIdx > size) colorEndIdx = size;
|
||||||
fill_solid(&leds[startIdx], endIdx - startIdx, colorPack.col[color]);
|
|
||||||
|
// Fill sector with the specified color
|
||||||
|
if (colorStartIdx < size) {
|
||||||
|
fill_solid(&leds[colorStartIdx], colorEndIdx - colorStartIdx, colorPack.col[color]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add black gap after the color
|
||||||
|
int gapStartIdx = (repeat * effectiveColorsPerRepeat + color * 2 + 1) * sectorSize;
|
||||||
|
int gapEndIdx = gapStartIdx + sectorSize;
|
||||||
|
if (gapEndIdx > size) gapEndIdx = size;
|
||||||
|
|
||||||
|
if (gapStartIdx < size) {
|
||||||
|
fill_solid(&leds[gapStartIdx], gapEndIdx - gapStartIdx, CRGB::Black);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Without gaps: original behavior
|
||||||
|
int startIdx = (repeat * colorPack.size + color) * sectorSize;
|
||||||
|
int endIdx = startIdx + sectorSize;
|
||||||
|
if (endIdx > size) endIdx = size;
|
||||||
|
|
||||||
|
// Fill sector with the specified color
|
||||||
|
fill_solid(&leds[startIdx], endIdx - startIdx, colorPack.col[color]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animate rotation
|
// Animate rotation
|
||||||
bool direction = true; // true = forward, false = backward
|
bool direction = true; // true = forward, false = backward
|
||||||
int loopCounter = 0;
|
int loopCounter = 0;
|
||||||
|
int speed = minSpeed;
|
||||||
|
int loopDuration = (size * CYCLES_PER_DIRECTION);
|
||||||
|
int thirdLoop = loopDuration / 3;
|
||||||
|
int twoThirdLoop = (2 * loopDuration) / 3;
|
||||||
CRGB temp;
|
CRGB temp;
|
||||||
Animation_Loop(activeFlag, speed, [&]() -> int {
|
Animation_Loop_Variable(activeFlag, [&]() -> int {
|
||||||
|
|
||||||
if (direction) {
|
if (direction) {
|
||||||
temp = leds[0];
|
temp = leds[0];
|
||||||
@ -288,15 +359,32 @@ void Anim_Color_Sectors(bool volatile& activeFlag, CRGB* leds, int size, const C
|
|||||||
leds[0] = temp;
|
leds[0] = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Direction Switching Logic
|
// Speed ramping: 1/3 aggressive ramp up, 1/3 constant, 1/3 aggressive ramp down
|
||||||
loopCounter++;
|
if (loopCounter < thirdLoop) {
|
||||||
if(loopCounter >= (size * CYCLES_PER_DIRECTION)){
|
// First third: aggressive ease-out acceleration (very fast start, sharp slowdown)
|
||||||
|
float progress = (float)loopCounter / (float)thirdLoop; // 0.0 to 1.0
|
||||||
|
float eased = 1.0f - pow(1.0f - progress, 4.0f); // ease-out quartic (power of 4)
|
||||||
|
speed = minSpeed + (int)((maxSpeed - minSpeed) * eased);
|
||||||
|
}
|
||||||
|
else if (loopCounter < twoThirdLoop) {
|
||||||
|
// Middle third: constant at maxSpeed
|
||||||
|
speed = maxSpeed;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Last third: aggressive ease-in deceleration (sharp start, fast finish)
|
||||||
|
float progress = (float)(loopCounter - twoThirdLoop) / (float)thirdLoop; // 0.0 to 1.0
|
||||||
|
float eased = pow(progress, 4.0f); // ease-in quartic (power of 4)
|
||||||
|
speed = maxSpeed - (int)((maxSpeed - minSpeed) * eased);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direction switching logic (single increment)
|
||||||
|
if (++loopCounter >= loopDuration) {
|
||||||
direction = !direction;
|
direction = !direction;
|
||||||
loopCounter = 0;
|
loopCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FastLED.show();
|
FastLED.show();
|
||||||
return 0;
|
return max(MaxSpeed - speed, MinLoopDelay);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,10 +394,10 @@ void Anim_Color_Sectors(bool volatile& activeFlag, CRGB* leds, int size, const C
|
|||||||
* Comets Animation
|
* Comets Animation
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
#define COMET_SIZE_FACTOR 0.2
|
#define COMET_SIZE_FACTOR 0.2
|
||||||
#define COMET_FADE_FACTOR1 64 /* longer tail */
|
#define COMET_FADE_FACTOR1 32 /* longer tail */
|
||||||
#define COMET_FADE_FACTOR2 192 /* shorter tail */
|
#define COMET_FADE_FACTOR2 192 /* shorter tail */
|
||||||
#define COMET_FADE_FACTOR COMET_FADE_FACTOR2
|
//#define COMET_FADE_FACTOR COMET_FADE_FACTOR2
|
||||||
#define MAX_COMETS 16 // Maximum number of comets supported
|
#define MAX_COMETS 8 // Maximum number of comets supported
|
||||||
/*
|
/*
|
||||||
void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int speed, bool randomDecay, bool shorterTail, int cometMultiplier = 1) {
|
void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int speed, bool randomDecay, bool shorterTail, int cometMultiplier = 1) {
|
||||||
// Validate inputs
|
// Validate inputs
|
||||||
@ -336,7 +424,8 @@ void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PA
|
|||||||
bool direction = true; // true = forward, false = backward
|
bool direction = true; // true = forward, false = backward
|
||||||
int loopCounter = 0;
|
int loopCounter = 0;
|
||||||
try {
|
try {
|
||||||
Animation_Loop(activeFlag, speed, [&]() -> int {
|
const int loopTick = 30; // ms per animation tick
|
||||||
|
Animation_Loop(activeFlag, loopTick, [&]() -> int {
|
||||||
// Fade all LEDs
|
// Fade all LEDs
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
if(!randomDecay) {
|
if(!randomDecay) {
|
||||||
@ -380,8 +469,7 @@ void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PA
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int minSpeed, int maxSpeed, bool randomDecay, int cometMultiplier) {
|
||||||
void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int speed, bool randomDecay, bool shorterTail, int cometMultiplier = 1) {
|
|
||||||
// Validate inputs
|
// Validate inputs
|
||||||
int numComets = colorPack.size;
|
int numComets = colorPack.size;
|
||||||
int totalComets = numComets * cometMultiplier;
|
int totalComets = numComets * cometMultiplier;
|
||||||
@ -396,7 +484,8 @@ void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PA
|
|||||||
if (cometSize < 1) cometSize = 1;
|
if (cometSize < 1) cometSize = 1;
|
||||||
|
|
||||||
// Set fade factor
|
// Set fade factor
|
||||||
uint8_t fadeFactor = shorterTail ? COMET_FADE_FACTOR2 : COMET_FADE_FACTOR1;
|
uint8_t fadeFactor = map(totalComets, 1, MAX_COMETS, COMET_FADE_FACTOR1, COMET_FADE_FACTOR2);
|
||||||
|
fadeFactor = constrain(fadeFactor, COMET_FADE_FACTOR1, COMET_FADE_FACTOR2);
|
||||||
|
|
||||||
// Initialize comet positions with fixed array, evenly distributed
|
// Initialize comet positions with fixed array, evenly distributed
|
||||||
int cometPositions[MAX_COMETS] = {0};
|
int cometPositions[MAX_COMETS] = {0};
|
||||||
@ -410,8 +499,12 @@ void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PA
|
|||||||
int loopCounter = 0;
|
int loopCounter = 0;
|
||||||
int pos;
|
int pos;
|
||||||
CRGB color;
|
CRGB color;
|
||||||
|
int speed = minSpeed;
|
||||||
|
int loopDuration = (size * CYCLES_PER_DIRECTION);
|
||||||
|
int thirdLoop = loopDuration / 3;
|
||||||
|
int twoThirdLoop = (2 * loopDuration) / 3;
|
||||||
try {
|
try {
|
||||||
Animation_Loop(activeFlag, speed, [&]() -> int {
|
Animation_Loop_Variable(activeFlag, [&]() -> int {
|
||||||
// Fade all LEDs
|
// Fade all LEDs
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
if (!randomDecay) {
|
if (!randomDecay) {
|
||||||
@ -421,7 +514,7 @@ void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move and draw comets
|
// Move and draw comets
|
||||||
for (int i = 0; i < totalComets; i++) {
|
for (int i = 0; i < totalComets; i++) {
|
||||||
if (direction) {
|
if (direction) {
|
||||||
cometPositions[i] = (cometPositions[i] + 1) % size;
|
cometPositions[i] = (cometPositions[i] + 1) % size;
|
||||||
@ -432,22 +525,44 @@ void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PA
|
|||||||
// Draw comet with solid color
|
// Draw comet with solid color
|
||||||
color = colorPack.col[i % colorPack.size];
|
color = colorPack.col[i % colorPack.size];
|
||||||
for (int j = 0; j < cometSize; j++) {
|
for (int j = 0; j < cometSize; j++) {
|
||||||
// Tail follows the direction of movement
|
// Tail follows the direction of movement
|
||||||
pos = direction ? (cometPositions[i] - j) : (cometPositions[i] + j);
|
pos = direction ? (cometPositions[i] - j) : (cometPositions[i] + j);
|
||||||
pos = (pos % size + size) % size; // safe modulus
|
pos = (pos % size + size) % size; // safe modulus
|
||||||
leds[pos] += color;
|
leds[pos] += color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Direction change
|
// Speed ramping: 1/3 aggressive ramp up, 1/3 constant, 1/3 aggressive ramp down
|
||||||
if (++loopCounter >= (size * CYCLES_PER_DIRECTION)) {
|
if (loopCounter < thirdLoop) {
|
||||||
|
// First third: aggressive ease-out acceleration (very fast start, sharp slowdown)
|
||||||
|
float progress = (float)loopCounter / (float)thirdLoop; // 0.0 to 1.0
|
||||||
|
float eased = 1.0f - pow(1.0f - progress, 4.0f); // ease-out quartic (power of 4)
|
||||||
|
speed = minSpeed + (int)((maxSpeed - minSpeed) * eased);
|
||||||
|
}
|
||||||
|
else if (loopCounter < twoThirdLoop) {
|
||||||
|
// Middle third: constant at maxSpeed
|
||||||
|
speed = maxSpeed;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Last third: aggressive ease-in deceleration (sharp start, fast finish)
|
||||||
|
float progress = (float)(loopCounter - twoThirdLoop) / (float)thirdLoop; // 0.0 to 1.0
|
||||||
|
float eased = pow(progress, 4.0f); // ease-in quartic (power of 4)
|
||||||
|
speed = maxSpeed - (int)((maxSpeed - minSpeed) * eased);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++loopCounter >= loopDuration) {
|
||||||
direction = !direction;
|
direction = !direction;
|
||||||
loopCounter = 0;
|
loopCounter = 0;
|
||||||
|
speed = minSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
FastLED.show();
|
FastLED.show();
|
||||||
return 0;
|
|
||||||
|
return max(MaxSpeed - speed, MinLoopDelay);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fill_solid(leds, size, CRGB::Black);
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
ESP_LOGE("Anim_Comets", "Exception in Animation_Loop: %s", e.what());
|
ESP_LOGE("Anim_Comets", "Exception in Animation_Loop: %s", e.what());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
@ -455,107 +570,195 @@ void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void Anim_TimedFill(bool volatile& activeFlag, CRGB* leds, int size, CRGB baseCol, CRGB fillCol, int totalDurationMs, int shift = 0, PWM_Output* pwmOutput = nullptr) {
|
void Anim_TimedFill(bool volatile& activeFlag, CRGB* leds, int size, CRGB baseCol, CRGB fillCol, int totalDurationMs, int shift = 0)
|
||||||
|
{
|
||||||
if (!leds || size <= 1 || totalDurationMs <= 0) return;
|
if (!leds || size <= 1 || totalDurationMs <= 0) return;
|
||||||
|
|
||||||
const int halfSize = size / 2;
|
const int halfSize = size / 2;
|
||||||
const float msPerLed = totalDurationMs / (float)halfSize;
|
const float msPerLed = totalDurationMs / (float)halfSize;
|
||||||
unsigned long startTime = millis();
|
unsigned long startTime = millis();
|
||||||
|
|
||||||
// Define the point at which PWM begins ramping (75% of fill time)
|
fill_solid(leds, size, baseCol);
|
||||||
const float pwmStartPoint = 0.75f;
|
|
||||||
const int pwmStartLeds = halfSize * pwmStartPoint;
|
int prevLedsToLight = 0;
|
||||||
|
unsigned long currentTime;
|
||||||
|
unsigned long elapsedTime;
|
||||||
|
int ledsToLight, pos;
|
||||||
|
int loopInterval = 90;
|
||||||
|
Animation_Loop(activeFlag, loopInterval, [&]() -> int {
|
||||||
|
currentTime = millis();
|
||||||
|
elapsedTime = currentTime - startTime;
|
||||||
|
|
||||||
|
// return 0 to loop infinitely
|
||||||
|
if(elapsedTime > (totalDurationMs + loopInterval)) return 0;
|
||||||
|
|
||||||
|
// Calculate how many LEDs should be lit based on elapsed time
|
||||||
|
ledsToLight = (elapsedTime / msPerLed);
|
||||||
|
if (ledsToLight > halfSize) ledsToLight = halfSize;
|
||||||
|
|
||||||
|
// Fill LEDs up to current position
|
||||||
|
for (int i = 0; i < ledsToLight; i++) {
|
||||||
|
pos = (i + shift + size) % size;
|
||||||
|
leds[pos] = fillCol;
|
||||||
|
leds[(size - 1 - i + shift + size) % size] = fillCol; // Correct mirroring calculation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update LEDs only when necessary
|
||||||
|
if(prevLedsToLight < ledsToLight){
|
||||||
|
FastLED.show();
|
||||||
|
}
|
||||||
|
prevLedsToLight = ledsToLight;
|
||||||
|
|
||||||
|
return 0; // Always return 0 to continue looping
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Anim_TimedFill_Flash(bool volatile& activeFlag, CRGB* leds, int size, PWM_Output* pwmOut, PWM_OUT_SETTINGS* pwmSettings, int pwmOutDelay, int flashTimeout, CRGB baseCol, CRGB fillCol, int totalDurationMs, int shift)
|
||||||
|
{
|
||||||
|
if (!leds || size <= 1 || totalDurationMs <= 0 || !pwmOut || !pwmSettings) return;
|
||||||
|
|
||||||
|
const int halfSize = size / 2;
|
||||||
|
const float msPerLed = totalDurationMs / (float)halfSize;
|
||||||
|
unsigned long startTime = millis();
|
||||||
|
unsigned long flashStartTime = 0;
|
||||||
|
bool flashTimeoutStarted = false;
|
||||||
|
bool pwmStarted = false;
|
||||||
|
|
||||||
|
// Calculate PWM parameters
|
||||||
|
const int pwmRampDuration = totalDurationMs - pwmOutDelay;
|
||||||
|
const float pwmMin = pwmSettings->min;
|
||||||
|
const float pwmMax = pwmSettings->max;
|
||||||
|
const float pwmRange = pwmMax - pwmMin;
|
||||||
|
|
||||||
|
fill_solid(leds, size, baseCol);
|
||||||
|
|
||||||
// Initialize PWM output to 0 if provided
|
// Set initial PWM to minimum
|
||||||
if (pwmOutput) {
|
pwmOut->setOutput(pwmMin);
|
||||||
pwmOutput->setOutput(0.0f);
|
|
||||||
|
int prevLedsToLight = 0;
|
||||||
|
unsigned long currentTime;
|
||||||
|
unsigned long elapsedTime;
|
||||||
|
int ledsToLight, pos;
|
||||||
|
float pwmValue;
|
||||||
|
int loopInterval = 90;
|
||||||
|
|
||||||
|
Animation_Loop(activeFlag, loopInterval, [&]() -> int {
|
||||||
|
currentTime = millis();
|
||||||
|
elapsedTime = currentTime - startTime;
|
||||||
|
|
||||||
|
// Phase 1: Fill animation with synchronized PWM ramp
|
||||||
|
if (elapsedTime <= (totalDurationMs + loopInterval)) {
|
||||||
|
// Calculate how many LEDs should be lit based on elapsed time
|
||||||
|
ledsToLight = (elapsedTime / msPerLed);
|
||||||
|
if (ledsToLight > halfSize) ledsToLight = halfSize;
|
||||||
|
|
||||||
|
// Fill LEDs up to current position
|
||||||
|
for (int i = 0; i < ledsToLight; i++) {
|
||||||
|
pos = (i + shift + size) % size;
|
||||||
|
leds[pos] = fillCol;
|
||||||
|
leds[(size - 1 - i + shift + size) % size] = fillCol; // Correct mirroring calculation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update LEDs only when necessary
|
||||||
|
if(prevLedsToLight < ledsToLight){
|
||||||
|
FastLED.show();
|
||||||
|
}
|
||||||
|
prevLedsToLight = ledsToLight;
|
||||||
|
|
||||||
|
// PWM control: start ramping after pwmOutDelay
|
||||||
|
if (elapsedTime >= pwmOutDelay && !pwmStarted) {
|
||||||
|
pwmStarted = true;
|
||||||
|
ESP_LOGI("Anim_TimedFill_Flash", "Starting PWM ramp from %.1f to %.1f over %dms", pwmMin, pwmMax, pwmRampDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pwmStarted && pwmRampDuration > 0) {
|
||||||
|
// Linear interpolation from min to max over remaining time
|
||||||
|
int pwmElapsed = elapsedTime - pwmOutDelay;
|
||||||
|
if (pwmElapsed < 0) pwmElapsed = 0;
|
||||||
|
if (pwmElapsed > pwmRampDuration) pwmElapsed = pwmRampDuration;
|
||||||
|
|
||||||
|
float progress = (float)pwmElapsed / (float)pwmRampDuration;
|
||||||
|
pwmValue = pwmMin + (progress * pwmRange);
|
||||||
|
pwmOut->setOutput(pwmValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Phase 2: Animation complete, start flash timeout
|
||||||
|
else if (!flashTimeoutStarted) {
|
||||||
|
flashTimeoutStarted = true;
|
||||||
|
flashStartTime = currentTime;
|
||||||
|
ESP_LOGI("Anim_TimedFill_Flash", "Fill complete, starting flash timeout of %dms", flashTimeout);
|
||||||
|
}
|
||||||
|
// Phase 3: Flash timeout period
|
||||||
|
else {
|
||||||
|
unsigned long flashElapsed = currentTime - flashStartTime;
|
||||||
|
if (flashElapsed >= flashTimeout) {
|
||||||
|
// Flash timeout expired, set PWM to minimum
|
||||||
|
pwmOut->setOutput(pwmMin);
|
||||||
|
ESP_LOGI("Anim_TimedFill_Flash", "Flash timeout expired, PWM set to minimum");
|
||||||
|
}
|
||||||
|
// Continue in infinite loop regardless of timeout status
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // Always return 0 to continue looping infinitely
|
||||||
|
});
|
||||||
|
|
||||||
|
pwmOut->setOutput(pwmMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Anim_SolidWhite(bool volatile& activeFlag, CRGB* leds, PWM_Output* pwmOut, int size, int brightness, int timeoutMs) {
|
||||||
|
if (!leds || size <= 0 || !pwmOut) return;
|
||||||
|
|
||||||
|
const uint8_t origBright = FastLED.getBrightness();
|
||||||
|
const uint8_t fullBrightness = brightness;
|
||||||
|
const uint8_t reducedBrightness = brightness / 2; // 50% brightness
|
||||||
|
const float reducedPwmOutput = 50.0f; // 50% PWM output
|
||||||
|
|
||||||
|
unsigned long startTime = millis();
|
||||||
|
bool timeoutReached = false;
|
||||||
|
bool brightnessReduced = false;
|
||||||
|
|
||||||
|
// Set initial full brightness
|
||||||
|
FastLED.setBrightness(fullBrightness);
|
||||||
|
pwmOut->setOutput(100);
|
||||||
|
|
||||||
|
fill_solid(leds, size, CRGB::White);
|
||||||
|
FastLED.show();
|
||||||
|
|
||||||
|
Animation_Loop(activeFlag, 50, [&]() -> int {
|
||||||
|
// Check if timeout is specified and has been reached
|
||||||
|
if (timeoutMs > 0 && !timeoutReached) {
|
||||||
|
unsigned long currentTime = millis();
|
||||||
|
unsigned long elapsedTime = currentTime - startTime;
|
||||||
|
|
||||||
|
if (elapsedTime >= timeoutMs) {
|
||||||
|
timeoutReached = true;
|
||||||
|
|
||||||
|
// Reduce brightness to 50% for both RGB and PWM
|
||||||
|
if (!brightnessReduced) {
|
||||||
|
if(pwmOut){
|
||||||
|
pwmOut->setOutput(reducedPwmOutput);
|
||||||
|
}
|
||||||
|
FastLED.setBrightness(reducedBrightness);
|
||||||
|
FastLED.show(); // Update display with new brightness
|
||||||
|
brightnessReduced = true;
|
||||||
|
//ESP_LOGI("Anim_SolidWhite", "Timeout reached - brightness reduced to 50%%");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No animation, just maintain the solid white state
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
if(pwmOut){
|
||||||
|
pwmOut->setOutput(0); // Turn off PWM output
|
||||||
}
|
}
|
||||||
|
FastLED.setBrightness(origBright); // Restore original brightness
|
||||||
fill_solid(leds, size, baseCol);
|
|
||||||
|
|
||||||
int prevLedsToLight = 0;
|
|
||||||
unsigned long currentTime;
|
|
||||||
unsigned long elapsedTime;
|
|
||||||
int ledsToLight, pos;
|
|
||||||
Animation_Loop(activeFlag, 90, [&]() -> int {
|
|
||||||
currentTime = millis();
|
|
||||||
elapsedTime = currentTime - startTime;
|
|
||||||
|
|
||||||
// Calculate how many LEDs should be lit based on elapsed time
|
|
||||||
ledsToLight = (elapsedTime / msPerLed);
|
|
||||||
if (ledsToLight > halfSize) ledsToLight = halfSize;
|
|
||||||
|
|
||||||
// Fill LEDs up to current position
|
|
||||||
for (int i = 0; i < ledsToLight; i++) {
|
|
||||||
pos = (i + shift + size) % size;
|
|
||||||
leds[pos] = fillCol;
|
|
||||||
leds[(size - 1 - i + shift + size) % size] = fillCol; // Correct mirroring calculation
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle PWM output ramp starting at 75% of fill time
|
|
||||||
if (pwmOutput && ledsToLight >= pwmStartLeds) {
|
|
||||||
// Calculate PWM value as percentage of remaining fill time
|
|
||||||
// Map from [pwmStartLeds, halfSize] to [0, 100]
|
|
||||||
float pwmValue = map(ledsToLight, pwmStartLeds, halfSize, 0, 100);
|
|
||||||
// Ensure pwmValue is in range [0, 100]
|
|
||||||
pwmValue = constrain(pwmValue, 0.0f, 100.0f);
|
|
||||||
// Set the PWM output
|
|
||||||
pwmOutput->setOutput(pwmValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update LEDs only when necessary
|
|
||||||
if(prevLedsToLight < ledsToLight){
|
|
||||||
FastLED.show();
|
|
||||||
}
|
|
||||||
prevLedsToLight = ledsToLight;
|
|
||||||
|
|
||||||
// Return 1 when complete
|
|
||||||
return (ledsToLight >= halfSize) ? 1 : 0;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
void Anim_TimedFill(bool volatile& activeFlag, CRGB* leds, int size, CRGB baseCol, CRGB fillCol, int totalDurationMs, int shift = 0) {
|
|
||||||
if (!leds || size <= 1 || totalDurationMs <= 0) return;
|
|
||||||
|
|
||||||
const int halfSize = size / 2;
|
|
||||||
const float msPerLed = totalDurationMs / (float)halfSize;
|
|
||||||
unsigned long startTime = millis();
|
|
||||||
|
|
||||||
fill_solid(leds, size, baseCol);
|
|
||||||
|
|
||||||
int prevLedsToLight = 0;
|
|
||||||
unsigned long currentTime;
|
|
||||||
unsigned long elapsedTime;
|
|
||||||
int ledsToLight, pos;
|
|
||||||
Animation_Loop(activeFlag, 90, [&]() -> int {
|
|
||||||
currentTime = millis();
|
|
||||||
elapsedTime = currentTime - startTime;
|
|
||||||
|
|
||||||
// Calculate how many LEDs should be lit based on elapsed time
|
|
||||||
ledsToLight = (elapsedTime / msPerLed);
|
|
||||||
if (ledsToLight > halfSize) ledsToLight = halfSize;
|
|
||||||
|
|
||||||
// Fill LEDs up to current position
|
|
||||||
for (int i = 0; i < ledsToLight; i++) {
|
|
||||||
pos = (i + shift + size) % size;
|
|
||||||
leds[pos] = fillCol;
|
|
||||||
leds[(size - 1 - i + shift + size) % size] = fillCol; // Correct mirroring calculation
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update LEDs only when necessary
|
|
||||||
if(prevLedsToLight < ledsToLight){
|
|
||||||
FastLED.show();
|
|
||||||
}
|
|
||||||
prevLedsToLight = ledsToLight;
|
|
||||||
|
|
||||||
// Return 1 when complete
|
|
||||||
return (ledsToLight >= halfSize) ? 1 : 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define MIN_BRIGHTNESS 2
|
#define MIN_BRIGHTNESS 2
|
||||||
void Anim_ColorBreath(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colors, uint32_t timeMs, int speed) {
|
void Anim_ColorBreath(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colors, uint32_t timeMs, int speed) {
|
||||||
@ -657,6 +860,7 @@ void Anim_ColorWipe(bool volatile& activeFlag, CRGB* leds, int size, const COLOR
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Anim_Sparkle(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colors, int speed, uint8_t sparkleChance) {
|
void Anim_Sparkle(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colors, int speed, uint8_t sparkleChance) {
|
||||||
if (!leds || size <= 0 || colors.size <= 0) return;
|
if (!leds || size <= 0 || colors.size <= 0) return;
|
||||||
|
|
||||||
@ -675,6 +879,7 @@ void Anim_Sparkle(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_P
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Anim_TheaterChase(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colors, int speed, uint8_t spacing) {
|
void Anim_TheaterChase(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colors, int speed, uint8_t spacing) {
|
||||||
if (!leds || size <= 0 || colors.size <= 0 || spacing == 0) return;
|
if (!leds || size <= 0 || colors.size <= 0 || spacing == 0) return;
|
||||||
|
|
||||||
@ -697,6 +902,350 @@ void Anim_TheaterChase(bool volatile& activeFlag, CRGB* leds, int size, const CO
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define SNAKE_CYCLES_PER_ROTATION 4
|
||||||
|
void Anim_Snakes(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int speed , int multiplier) {
|
||||||
|
if (!leds || size <= 0 || colorPack.size <= 0 || multiplier <= 0) return;
|
||||||
|
|
||||||
|
// Determine number of snakes (colors * multiplier), cap to a safe maximum
|
||||||
|
const int USER_NUM = colorPack.size * multiplier;
|
||||||
|
const int MAX_SNAKES = 32;
|
||||||
|
int numSnakes = USER_NUM;
|
||||||
|
if (numSnakes > MAX_SNAKES) numSnakes = MAX_SNAKES;
|
||||||
|
if (numSnakes <= 0) return;
|
||||||
|
|
||||||
|
// Snake parameters: compute per-snake max length based on total snakes
|
||||||
|
const int maxSnakeLengthPerSection = size / numSnakes; // Full space available per snake
|
||||||
|
if (maxSnakeLengthPerSection < 2) return; // Need at least 2 pixels per snake section
|
||||||
|
|
||||||
|
enum SnakeState : uint8_t { GROWING = 0, FILLING = 1, SHRINKING = 2 };
|
||||||
|
|
||||||
|
struct Snake {
|
||||||
|
int head;
|
||||||
|
int length;
|
||||||
|
int maxLength;
|
||||||
|
int startPos;
|
||||||
|
int endPos;
|
||||||
|
bool forward;
|
||||||
|
CRGB color;
|
||||||
|
SnakeState state;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate snakes and per-snake cycle flags
|
||||||
|
Snake* snakes = new (std::nothrow) Snake[numSnakes];
|
||||||
|
if (!snakes) return;
|
||||||
|
bool* cycleDone = new (std::nothrow) bool[numSnakes];
|
||||||
|
if (!cycleDone) { delete[] snakes; return; }
|
||||||
|
for (int i = 0; i < numSnakes; i++) cycleDone[i] = false;
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
bool globalDirection = true; // rotation direction and snake forward when cycle resets
|
||||||
|
int completedCycles = 0;
|
||||||
|
int rotationOffset = 0; // total rotation applied to whole-array
|
||||||
|
|
||||||
|
for (int i = 0; i < numSnakes; i++) {
|
||||||
|
snakes[i].startPos = (i * size) / numSnakes;
|
||||||
|
snakes[i].endPos = ((i + 1) * size) / numSnakes - 1;
|
||||||
|
snakes[i].head = snakes[i].startPos;
|
||||||
|
snakes[i].length = 1;
|
||||||
|
snakes[i].maxLength = maxSnakeLengthPerSection;
|
||||||
|
snakes[i].forward = globalDirection;
|
||||||
|
snakes[i].color = colorPack.col[i % colorPack.size];
|
||||||
|
snakes[i].state = GROWING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary buffer to draw snakes before rotating whole array
|
||||||
|
CRGB* buf = new (std::nothrow) CRGB[size];
|
||||||
|
if (!buf) { delete[] snakes; delete[] cycleDone; return; }
|
||||||
|
|
||||||
|
Animation_Loop(activeFlag, speed, [&]() -> int {
|
||||||
|
// Clear drawing buffer
|
||||||
|
fill_solid(buf, size, CRGB::Black);
|
||||||
|
|
||||||
|
// Update snake states
|
||||||
|
bool anyGrowing = false;
|
||||||
|
for (int s = 0; s < numSnakes; s++) {
|
||||||
|
Snake& sn = snakes[s];
|
||||||
|
|
||||||
|
switch (sn.state) {
|
||||||
|
case GROWING:
|
||||||
|
// Move head toward wall and extend
|
||||||
|
if (sn.forward) {
|
||||||
|
if (sn.head < sn.endPos) { sn.head++; sn.length++; }
|
||||||
|
else { sn.state = FILLING; }
|
||||||
|
} else {
|
||||||
|
if (sn.head > sn.startPos) { sn.head--; sn.length++; }
|
||||||
|
else { sn.state = FILLING; }
|
||||||
|
}
|
||||||
|
if (sn.length > sn.maxLength) sn.length = sn.maxLength;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FILLING:
|
||||||
|
if (sn.length < sn.maxLength) { sn.length++; }
|
||||||
|
else { sn.state = SHRINKING; }
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHRINKING:
|
||||||
|
if (sn.length > 1) { sn.length--; }
|
||||||
|
else {
|
||||||
|
// Completed one grow+shrink cycle for this snake
|
||||||
|
sn.forward = globalDirection; // align to current global direction
|
||||||
|
sn.state = GROWING;
|
||||||
|
sn.length = 1;
|
||||||
|
cycleDone[s] = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sn.state == GROWING || sn.state == FILLING) anyGrowing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all snakes signaled completion, count a completed cycle and reset flags
|
||||||
|
bool allDone = true;
|
||||||
|
for (int s = 0; s < numSnakes; s++) { if (!cycleDone[s]) { allDone = false; break; } }
|
||||||
|
if (allDone) {
|
||||||
|
completedCycles++;
|
||||||
|
for (int s = 0; s < numSnakes; s++) cycleDone[s] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After required cycles, flip global direction
|
||||||
|
if (completedCycles >= SNAKE_CYCLES_PER_ROTATION) {
|
||||||
|
globalDirection = !globalDirection;
|
||||||
|
completedCycles = 0;
|
||||||
|
// apply new direction to snakes so their next grow starts in that direction
|
||||||
|
for (int s = 0; s < numSnakes; s++) snakes[s].forward = globalDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotation speed: 2x while any snake is growing/filling, 1x otherwise
|
||||||
|
int rotationSpeed = anyGrowing ? 2 : 1;
|
||||||
|
if (globalDirection) rotationOffset = (rotationOffset + rotationSpeed) % size;
|
||||||
|
else rotationOffset = (rotationOffset - rotationSpeed + size) % size;
|
||||||
|
|
||||||
|
// Draw snakes into buffer (no rotation applied here)
|
||||||
|
for (int s = 0; s < numSnakes; s++) {
|
||||||
|
Snake& sn = snakes[s];
|
||||||
|
for (int i = 0; i < sn.length; i++) {
|
||||||
|
int pos = sn.forward ? (sn.head - i) : (sn.head + i);
|
||||||
|
if (pos < sn.startPos || pos > sn.endPos) continue;
|
||||||
|
if (pos < 0 || pos >= size) continue;
|
||||||
|
|
||||||
|
float brightness = 1.0f - (float(i) / float(sn.maxLength)) * 0.7f;
|
||||||
|
uint8_t scale = static_cast<uint8_t>(constrain((int)(brightness * 255.0f), 0, 255));
|
||||||
|
CRGB pixel = sn.color;
|
||||||
|
pixel.nscale8_video(scale);
|
||||||
|
|
||||||
|
if (buf[pos] == CRGB::Black) buf[pos] = pixel; else buf[pos] += pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate the whole buffer into the real LED array using rotationOffset
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
int dst = (i + rotationOffset) % size;
|
||||||
|
leds[dst] = buf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
FastLED.show();
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
delete[] buf;
|
||||||
|
delete[] snakes;
|
||||||
|
delete[] cycleDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BIRD_CYCLES_PER_ROTATION 4
|
||||||
|
void Anim_Birds(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int speed , int multiplier) {
|
||||||
|
if (!leds || size <= 0 || colorPack.size <= 0 || multiplier <= 0) return;
|
||||||
|
|
||||||
|
// Determine number of snakes (colors * multiplier), cap to a safe maximum
|
||||||
|
const int USER_NUM = colorPack.size * multiplier;
|
||||||
|
const int MAX_SNAKES = 32;
|
||||||
|
int numSnakes = USER_NUM;
|
||||||
|
if (numSnakes > MAX_SNAKES) numSnakes = MAX_SNAKES;
|
||||||
|
if (numSnakes <= 0) return;
|
||||||
|
|
||||||
|
// Snake parameters: compute per-snake max length based on total snakes
|
||||||
|
const int maxSnakeLengthPerSection = size / numSnakes; // Full space available per snake
|
||||||
|
if (maxSnakeLengthPerSection < 2) return; // Need at least 2 pixels per snake section
|
||||||
|
|
||||||
|
enum SnakeState : uint8_t { GROWING = 0, FILLING = 1, SHRINKING = 2 };
|
||||||
|
|
||||||
|
struct Snake {
|
||||||
|
int head;
|
||||||
|
int length;
|
||||||
|
int maxLength;
|
||||||
|
int startPos;
|
||||||
|
int endPos;
|
||||||
|
bool forward;
|
||||||
|
CRGB color;
|
||||||
|
SnakeState state;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate snakes and per-snake cycle flags
|
||||||
|
Snake* snakes = new (std::nothrow) Snake[numSnakes];
|
||||||
|
if (!snakes) return;
|
||||||
|
bool* cycleDone = new (std::nothrow) bool[numSnakes];
|
||||||
|
if (!cycleDone) { delete[] snakes; return; }
|
||||||
|
for (int i = 0; i < numSnakes; i++) cycleDone[i] = false;
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
bool globalDirection = true; // rotation direction and snake forward when cycle resets
|
||||||
|
int completedCycles = 0;
|
||||||
|
int rotationOffset = 0; // total rotation applied to whole-array
|
||||||
|
|
||||||
|
for (int i = 0; i < numSnakes; i++) {
|
||||||
|
snakes[i].startPos = (i * size) / numSnakes;
|
||||||
|
snakes[i].endPos = ((i + 1) * size) / numSnakes - 1;
|
||||||
|
snakes[i].head = snakes[i].startPos;
|
||||||
|
snakes[i].length = 1;
|
||||||
|
snakes[i].maxLength = maxSnakeLengthPerSection;
|
||||||
|
snakes[i].forward = globalDirection;
|
||||||
|
snakes[i].color = colorPack.col[i % colorPack.size];
|
||||||
|
snakes[i].state = GROWING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary buffer to draw snakes before rotating whole array
|
||||||
|
CRGB* buf = new (std::nothrow) CRGB[size];
|
||||||
|
if (!buf) { delete[] snakes; delete[] cycleDone; return; }
|
||||||
|
|
||||||
|
Animation_Loop(activeFlag, speed, [&]() -> int {
|
||||||
|
// Clear drawing buffer
|
||||||
|
fill_solid(buf, size, CRGB::Black);
|
||||||
|
|
||||||
|
// Update snake states
|
||||||
|
bool anyGrowing = false;
|
||||||
|
for (int s = 0; s < numSnakes; s++) {
|
||||||
|
Snake& sn = snakes[s];
|
||||||
|
|
||||||
|
switch (sn.state) {
|
||||||
|
case GROWING:
|
||||||
|
// Move head toward wall and extend
|
||||||
|
if (sn.forward) {
|
||||||
|
if (sn.head < sn.endPos) { sn.head++; sn.length++; }
|
||||||
|
else { sn.state = FILLING; }
|
||||||
|
} else {
|
||||||
|
if (sn.head > sn.startPos) { sn.head--; sn.length++; }
|
||||||
|
else { sn.state = FILLING; }
|
||||||
|
}
|
||||||
|
if (sn.length > sn.maxLength) sn.length = sn.maxLength;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FILLING:
|
||||||
|
if (sn.length < sn.maxLength) { sn.length++; }
|
||||||
|
else { sn.state = SHRINKING; }
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHRINKING:
|
||||||
|
if (sn.length > 1) { sn.length--; }
|
||||||
|
else {
|
||||||
|
// Completed one grow+shrink cycle for this snake
|
||||||
|
sn.forward = globalDirection; // align to current global direction
|
||||||
|
sn.state = GROWING;
|
||||||
|
sn.length = 1;
|
||||||
|
cycleDone[s] = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sn.state == GROWING || sn.state == FILLING) anyGrowing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all snakes signaled completion, count a completed cycle and reset flags
|
||||||
|
bool allDone = true;
|
||||||
|
for (int s = 0; s < numSnakes; s++) { if (!cycleDone[s]) { allDone = false; break; } }
|
||||||
|
if (allDone) {
|
||||||
|
completedCycles++;
|
||||||
|
for (int s = 0; s < numSnakes; s++) cycleDone[s] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After required cycles, flip global direction
|
||||||
|
if (completedCycles >= SNAKE_CYCLES_PER_ROTATION) {
|
||||||
|
globalDirection = !globalDirection;
|
||||||
|
completedCycles = 0;
|
||||||
|
// apply new direction to snakes so their next grow starts in that direction
|
||||||
|
for (int s = 0; s < numSnakes; s++) snakes[s].forward = globalDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotation speed: 2x while any snake is growing/filling, 1x otherwise
|
||||||
|
int rotationSpeed = anyGrowing ? 2 : 1;
|
||||||
|
if (globalDirection) rotationOffset = (rotationOffset + rotationSpeed) % size;
|
||||||
|
else rotationOffset = (rotationOffset - rotationSpeed + size) % size;
|
||||||
|
|
||||||
|
// Draw snakes into buffer (no rotation applied here)
|
||||||
|
for (int s = 0; s < numSnakes; s++) {
|
||||||
|
Snake& sn = snakes[s];
|
||||||
|
for (int i = 0; i < sn.length; i++) {
|
||||||
|
int pos = sn.forward ? (sn.head - i) : (sn.head + i);
|
||||||
|
if (pos < sn.startPos || pos > sn.endPos) continue;
|
||||||
|
if (pos < 0 || pos >= size) continue;
|
||||||
|
|
||||||
|
float brightness = 1.0f - (float(i) / float(sn.maxLength)) * 0.7f;
|
||||||
|
uint8_t scale = static_cast<uint8_t>(constrain((int)(brightness * 255.0f), 0, 255));
|
||||||
|
CRGB pixel = sn.color;
|
||||||
|
pixel.nscale8_video(scale);
|
||||||
|
|
||||||
|
if (buf[pos] == CRGB::Black) buf[pos] = pixel; else buf[pos] += pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate the whole buffer into the real LED array using rotationOffset
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
int dst = (i + rotationOffset) % size;
|
||||||
|
leds[dst] = buf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
FastLED.show();
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
delete[] buf;
|
||||||
|
delete[] snakes;
|
||||||
|
delete[] cycleDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Morph between colors in the color pack smoothly
|
||||||
|
void Anim_Morph(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PACK& colorPack, int speed , int morphSteps) {
|
||||||
|
if (!leds || size <= 0 || colorPack.size < 2 || morphSteps <= 0) return;
|
||||||
|
|
||||||
|
int currentColorIndex = 0;
|
||||||
|
int nextColorIndex = 1;
|
||||||
|
int step = 0;
|
||||||
|
|
||||||
|
CRGB startColor, endColor, blendedColor;
|
||||||
|
Animation_Loop(activeFlag, speed, [&]() -> int {
|
||||||
|
// Get start and end colors for current morph
|
||||||
|
startColor = colorPack.col[currentColorIndex];
|
||||||
|
endColor = colorPack.col[nextColorIndex];
|
||||||
|
|
||||||
|
// Blend colors based on current step.
|
||||||
|
// Compute blendAmount in [0..255] so that step==0 => 0, step==morphSteps => 255.
|
||||||
|
uint32_t ba = 0;
|
||||||
|
if (morphSteps > 0) {
|
||||||
|
ba = (uint32_t)step * 255u / (uint32_t)morphSteps;
|
||||||
|
if (ba > 255u) ba = 255u;
|
||||||
|
}
|
||||||
|
uint8_t blendAmount = (uint8_t)ba;
|
||||||
|
blendedColor = blend(startColor, endColor, blendAmount);
|
||||||
|
|
||||||
|
// Fill all LEDs with the blended color
|
||||||
|
fill_solid(leds, size, blendedColor);
|
||||||
|
FastLED.show();
|
||||||
|
|
||||||
|
// Advance to next step. When step reaches morphSteps we have shown the
|
||||||
|
// final blended color (endColor). After that, advance to the next pair.
|
||||||
|
if (step < morphSteps) {
|
||||||
|
step++;
|
||||||
|
} else {
|
||||||
|
// completed this morph
|
||||||
|
step = 0;
|
||||||
|
currentColorIndex = nextColorIndex;
|
||||||
|
nextColorIndex = (nextColorIndex + 1) % colorPack.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t getRandomValue(uint32_t maxValue) {
|
uint32_t getRandomValue(uint32_t maxValue) {
|
||||||
return esp_random() % maxValue;
|
return esp_random() % maxValue;
|
||||||
|
|||||||
@ -237,11 +237,20 @@ bool AppUpdater::verifyAndSaveFile(WiFiClient* stream, size_t contentLength, con
|
|||||||
md5.begin();
|
md5.begin();
|
||||||
size_t totalRead = 0;
|
size_t totalRead = 0;
|
||||||
|
|
||||||
// Create temporary filename - try root directory first to avoid path issues
|
// Create temporary filename in the same directory as the target file
|
||||||
String baseName = String(localPath);
|
String targetDir = String(localPath);
|
||||||
baseName.replace("/", "_");
|
int lastSlash = targetDir.lastIndexOf('/');
|
||||||
baseName.replace("\\", "_");
|
String tempPath;
|
||||||
String tempPath = "/temp_" + baseName + ".download";
|
|
||||||
|
if (lastSlash >= 0) {
|
||||||
|
// Extract directory and filename
|
||||||
|
String directory = targetDir.substring(0, lastSlash + 1);
|
||||||
|
String filename = targetDir.substring(lastSlash + 1);
|
||||||
|
tempPath = directory + "temp_" + filename + ".download";
|
||||||
|
} else {
|
||||||
|
// File is in root directory
|
||||||
|
tempPath = "/temp_" + String(localPath) + ".download";
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up any existing temp file first
|
// Clean up any existing temp file first
|
||||||
if (fileSystem.exists(tempPath.c_str())) {
|
if (fileSystem.exists(tempPath.c_str())) {
|
||||||
@ -360,9 +369,9 @@ bool AppUpdater::verifyAndSaveFile(WiFiClient* stream, size_t contentLength, con
|
|||||||
// We'll still keep this file but report it as a verification failure
|
// We'll still keep this file but report it as a verification failure
|
||||||
// Ensure target directory exists before rename
|
// Ensure target directory exists before rename
|
||||||
String dirPath = String(localPath);
|
String dirPath = String(localPath);
|
||||||
int lastSlash = dirPath.lastIndexOf('/');
|
int targetLastSlash = dirPath.lastIndexOf('/');
|
||||||
if (lastSlash > 0) {
|
if (targetLastSlash > 0) {
|
||||||
dirPath = dirPath.substring(0, lastSlash);
|
dirPath = dirPath.substring(0, targetLastSlash);
|
||||||
if (!fileSystem.exists(dirPath.c_str())) {
|
if (!fileSystem.exists(dirPath.c_str())) {
|
||||||
ESP_LOGI(TAG, "Creating target directory: %s", dirPath.c_str());
|
ESP_LOGI(TAG, "Creating target directory: %s", dirPath.c_str());
|
||||||
String dummyFile = dirPath + "/.dummy";
|
String dummyFile = dirPath + "/.dummy";
|
||||||
@ -396,9 +405,9 @@ bool AppUpdater::verifyAndSaveFile(WiFiClient* stream, size_t contentLength, con
|
|||||||
|
|
||||||
// Ensure target directory exists before rename
|
// Ensure target directory exists before rename
|
||||||
String dirPath = String(localPath);
|
String dirPath = String(localPath);
|
||||||
int lastSlash = dirPath.lastIndexOf('/');
|
int targetLastSlash = dirPath.lastIndexOf('/');
|
||||||
if (lastSlash > 0) {
|
if (targetLastSlash > 0) {
|
||||||
dirPath = dirPath.substring(0, lastSlash);
|
dirPath = dirPath.substring(0, targetLastSlash);
|
||||||
if (!fileSystem.exists(dirPath.c_str())) {
|
if (!fileSystem.exists(dirPath.c_str())) {
|
||||||
ESP_LOGI(TAG, "Creating target directory: %s", dirPath.c_str());
|
ESP_LOGI(TAG, "Creating target directory: %s", dirPath.c_str());
|
||||||
String dummyFile = dirPath + "/.dummy";
|
String dummyFile = dirPath + "/.dummy";
|
||||||
@ -922,7 +931,7 @@ void firmwareUpdateTask(void* parameter) {
|
|||||||
|
|
||||||
if (updater->IsUpdateAvailable()) {
|
if (updater->IsUpdateAvailable()) {
|
||||||
bool filesUpdated = true;
|
bool filesUpdated = true;
|
||||||
bool firmwareUpdated = true;
|
bool firmwareUpdated = false; // Initialize to false - only set to true if firmware is actually updated
|
||||||
|
|
||||||
// Update files based on update mode
|
// Update files based on update mode
|
||||||
if (g_UpdateMode == UpdateMode::UPDATE_FILES_ONLY || g_UpdateMode == UpdateMode::UPDATE_BOTH) {
|
if (g_UpdateMode == UpdateMode::UPDATE_FILES_ONLY || g_UpdateMode == UpdateMode::UPDATE_BOTH) {
|
||||||
@ -962,7 +971,7 @@ void firmwareUpdateTask(void* parameter) {
|
|||||||
|
|
||||||
if (needsRestart) {
|
if (needsRestart) {
|
||||||
ESP_LOGI(TAG, "Firmware update successful, restarting...");
|
ESP_LOGI(TAG, "Firmware update successful, restarting...");
|
||||||
sendUpdateMessage("Restarting ", true, 100);
|
sendUpdateMessage("Restarting... ", true, 100);
|
||||||
vTaskDelay(2000);
|
vTaskDelay(2000);
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
} else {
|
} else {
|
||||||
@ -971,7 +980,6 @@ void firmwareUpdateTask(void* parameter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
ESP_LOGE(TAG, "Update failed with exception: %s", e.what());
|
ESP_LOGE(TAG, "Update failed with exception: %s", e.what());
|
||||||
updateProgress(AppUpdater::UpdateStatus::ERROR, 0, e.what());
|
updateProgress(AppUpdater::UpdateStatus::ERROR, 0, e.what());
|
||||||
@ -980,6 +988,7 @@ cleanup:
|
|||||||
updateProgress(AppUpdater::UpdateStatus::ERROR, 0, "Unknown error during update");
|
updateProgress(AppUpdater::UpdateStatus::ERROR, 0, "Unknown error during update");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
// Clean up watchdog before exit
|
// Clean up watchdog before exit
|
||||||
esp_task_wdt_delete(NULL);
|
esp_task_wdt_delete(NULL);
|
||||||
|
|
||||||
|
|||||||
@ -60,27 +60,44 @@ uint8_t calculateChecksum(const uint8_t bArr[]) {
|
|||||||
return (uint8_t)((bArr[2]) | ((bArr[0] << 1) & 254 & 105) | bArr[1]);
|
return (uint8_t)((bArr[2]) | ((bArr[0] << 1) & 254 & 105) | bArr[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Class for handling characteristic events
|
// Class for handling characteristic events
|
||||||
/*
|
|
||||||
class SP110ECallbacks : public NimBLECharacteristicCallbacks {
|
class SP110ECallbacks : public NimBLECharacteristicCallbacks {
|
||||||
void onWrite(NimBLECharacteristic *pCharacteristic) override {
|
private:
|
||||||
std::string rawValue = pCharacteristic->getValue();
|
static constexpr uint32_t DUPLICATE_FILTER_MS = 100; // 100ms filter window
|
||||||
const uint8_t* value = reinterpret_cast<const uint8_t*>(rawValue.data());
|
static constexpr size_t MAX_PACKET_SIZE = 32; // Maximum expected packet size
|
||||||
size_t length = rawValue.length();
|
|
||||||
if (length >= 3) {
|
uint32_t lastPacketTime = 0;
|
||||||
ESP_LOGI(tag, "Data received 0x%02X, 0x%02X, 0x%02X (length %zu):", value[0], value[1], value[2], length);
|
uint8_t lastPacketData[MAX_PACKET_SIZE] = {0};
|
||||||
} else {
|
size_t lastPacketLen = 0;
|
||||||
ESP_LOGI(tag, "Data received (length %zu)", length);
|
|
||||||
|
bool isDuplicatePacket(const uint8_t* data, size_t len) {
|
||||||
|
uint32_t currentTime = millis();
|
||||||
|
|
||||||
|
// Check if within the filter time window
|
||||||
|
if (currentTime - lastPacketTime > DUPLICATE_FILTER_MS) {
|
||||||
|
return false; // Outside filter window, not a duplicate
|
||||||
}
|
}
|
||||||
|
|
||||||
sendToAllClients(value, length);
|
// Check if packet length and content match
|
||||||
process_BLE_SP110E_Command(value, length, pCharacteristic);
|
if (len != lastPacketLen) {
|
||||||
|
return false; // Different length, not a duplicate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare packet data
|
||||||
|
if (memcmp(data, lastPacketData, len) != 0) {
|
||||||
|
return false; // Different content, not a duplicate
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Same packet within filter window
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateLastPacket(const uint8_t* data, size_t len) {
|
||||||
|
lastPacketTime = millis();
|
||||||
|
lastPacketLen = len > MAX_PACKET_SIZE ? MAX_PACKET_SIZE : len;
|
||||||
|
memcpy(lastPacketData, data, lastPacketLen);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Class for handling characteristic events
|
|
||||||
class SP110ECallbacks : public NimBLECharacteristicCallbacks {
|
|
||||||
public:
|
public:
|
||||||
void onWrite(NimBLECharacteristic* pCharacteristic) override {
|
void onWrite(NimBLECharacteristic* pCharacteristic) override {
|
||||||
if (!pCharacteristic) return;
|
if (!pCharacteristic) return;
|
||||||
@ -94,6 +111,15 @@ public:
|
|||||||
|
|
||||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(raw.data());
|
const uint8_t* data = reinterpret_cast<const uint8_t*>(raw.data());
|
||||||
|
|
||||||
|
// Check for duplicate packets within the filter window
|
||||||
|
if (isDuplicatePacket(data, len)) {
|
||||||
|
ESP_LOGD(tag, "Duplicate packet filtered (len=%zu)", len);
|
||||||
|
return; // Ignore duplicate packet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last packet tracking
|
||||||
|
updateLastPacket(data, len);
|
||||||
|
|
||||||
// Log up to first 16 bytes to avoid log spam
|
// Log up to first 16 bytes to avoid log spam
|
||||||
//logBytes(data, len);
|
//logBytes(data, len);
|
||||||
|
|
||||||
@ -126,6 +152,7 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class LightStickCallbacks : public NimBLECharacteristicCallbacks {
|
class LightStickCallbacks : public NimBLECharacteristicCallbacks {
|
||||||
void onRead(NimBLECharacteristic *pCharacteristic) override {
|
void onRead(NimBLECharacteristic *pCharacteristic) override {
|
||||||
pCharacteristic->setValue("Hello...");
|
pCharacteristic->setValue("Hello...");
|
||||||
@ -133,6 +160,7 @@ class LightStickCallbacks : public NimBLECharacteristicCallbacks {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Function to send data to all connected clients in chunks based on MTU
|
// Function to send data to all connected clients in chunks based on MTU
|
||||||
void sendToAllClients(const uint8_t* data, size_t len) {
|
void sendToAllClients(const uint8_t* data, size_t len) {
|
||||||
if (!pStickCharacteristic) {
|
if (!pStickCharacteristic) {
|
||||||
@ -205,40 +233,32 @@ void process_BLE_SP110E_Command(const uint8_t* val, uint8_t len, NimBLECharacter
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t command = val[3];
|
uint8_t command = val[3];
|
||||||
ESP_LOGI(tag, "Command received: 0x%02X", command);
|
//ESP_LOGI(tag, "Command received: 0x%02X, length: %d", command, len);
|
||||||
|
|
||||||
uint8_t response[sizeof(INFO_PACK)]; // Use a single response buffer
|
uint8_t response[sizeof(INFO_PACK)]; // Use a single response buffer
|
||||||
|
|
||||||
// Handle different commands
|
// Handle different commands
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case TURN_ON:
|
case TURN_ON:
|
||||||
Lights_Set_ON();
|
RGB_Animations_ON();
|
||||||
led_status.enable = 1;
|
led_status.enable = 1;
|
||||||
//ESP_LOGI(tag, "Lights ON");
|
ESP_LOGI(tag, "RGB Lights ON");
|
||||||
break;
|
break;
|
||||||
case TURN_OFF:
|
case TURN_OFF:
|
||||||
Lights_Set_OFF();
|
RGB_Animations_OFF();
|
||||||
led_status.enable = 0;
|
led_status.enable = 0;
|
||||||
//ESP_LOGI(tag, "Lights OFF");
|
ESP_LOGI(tag, "RGB Lights OFF");
|
||||||
break;
|
break;
|
||||||
case SET_STATIC_COLOR:
|
case SET_STATIC_COLOR:
|
||||||
if(len < 7) {
|
|
||||||
ESP_LOGW(tag, "SET_STATIC_COLOR command requires 3 parameters (R,G,B)");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
led_status.red = val[1];
|
led_status.red = val[1];
|
||||||
led_status.green = val[2];
|
led_status.green = val[2];
|
||||||
led_status.blue = val[0];
|
led_status.blue = val[0];
|
||||||
Lights_Set_Animation(SOLID_COLOR_INDEX, val[0], val[1], val[2]);
|
RGB_Lights_Set_Animation(SOLID_COLOR_INDEX, val[0], val[1], val[2]);
|
||||||
//ESP_LOGI(tag, "Color set to R:%d G:%d B:%d", led_status.red, led_status.green, led_status.blue);
|
//ESP_LOGI(tag, "Color set to R:%d G:%d B:%d", led_status.red, led_status.green, led_status.blue);
|
||||||
break;
|
break;
|
||||||
case SET_BRIGHT:
|
case SET_BRIGHT:
|
||||||
if(len < 5) {
|
|
||||||
ESP_LOGW(tag, "SET_BRIGHT command requires 1 parameter (brightness)");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
led_status.bright = val[0];
|
led_status.bright = val[0];
|
||||||
Lights_Set_Brightness(val[0]);
|
RGB_Lights_Set_Brightness(val[0]);
|
||||||
//ESP_LOGI(tag, "Bright set to %d", led_status.bright);
|
//ESP_LOGI(tag, "Bright set to %d", led_status.bright);
|
||||||
break;
|
break;
|
||||||
case SET_WHITE:
|
case SET_WHITE:
|
||||||
@ -248,7 +268,7 @@ void process_BLE_SP110E_Command(const uint8_t* val, uint8_t len, NimBLECharacter
|
|||||||
break;
|
break;
|
||||||
case SET_PRESET:
|
case SET_PRESET:
|
||||||
led_status.preset = val[0];
|
led_status.preset = val[0];
|
||||||
Lights_Set_Animation(val[0], val[1], val[2], 0);
|
RGB_Lights_Set_Animation(val[0], val[1], val[2], 0);
|
||||||
//ESP_LOGI(tag, "Animation set to %d", led_status.preset);
|
//ESP_LOGI(tag, "Animation set to %d", led_status.preset);
|
||||||
break;
|
break;
|
||||||
case SET_SPEED:
|
case SET_SPEED:
|
||||||
@ -294,7 +314,6 @@ void process_BLE_SP110E_Command(const uint8_t* val, uint8_t len, NimBLECharacter
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Init_BLE_SP110E(NimBLEServer* pServer) {
|
void Init_BLE_SP110E(NimBLEServer* pServer) {
|
||||||
if (!pServer) {
|
if (!pServer) {
|
||||||
ESP_LOGE(tag, "Invalid BLE server pointer");
|
ESP_LOGE(tag, "Invalid BLE server pointer");
|
||||||
|
|||||||
@ -19,9 +19,10 @@ NimBLECharacteristic *pUpgradeCharacteristic2 = nullptr;
|
|||||||
|
|
||||||
enum WIFI_STAT : byte { WIFI_DISCONNECTED=0, WIFI_BAD_CREDS=1, WIFI_NO_AP=2, WIFI_CONNECTED=3 };
|
enum WIFI_STAT : byte { WIFI_DISCONNECTED=0, WIFI_BAD_CREDS=1, WIFI_NO_AP=2, WIFI_CONNECTED=3 };
|
||||||
|
|
||||||
struct updateStatus {
|
struct __attribute__((packed)) updateStatus {
|
||||||
WIFI_STAT wifiStatus = WIFI_DISCONNECTED;
|
WIFI_STAT wifiStatus = WIFI_DISCONNECTED;
|
||||||
bool wifiOnline = false;
|
bool wifiOnline = false;
|
||||||
|
float temperature = 0.0;
|
||||||
byte wifiIP[4] = {0, 0, 0, 0};
|
byte wifiIP[4] = {0, 0, 0, 0};
|
||||||
byte currVersion[3] = {FIRMWARE_VERSION_MAJOR, FIRMWARE_VERSION_MINOR, FIRMWARE_VERSION_PATCH};
|
byte currVersion[3] = {FIRMWARE_VERSION_MAJOR, FIRMWARE_VERSION_MINOR, FIRMWARE_VERSION_PATCH};
|
||||||
byte newVersion[3] = {0, 0, 0};
|
byte newVersion[3] = {0, 0, 0};
|
||||||
@ -129,11 +130,23 @@ class UpgradeChar_Callbacks : public NimBLECharacteristicCallbacks {
|
|||||||
updatePacket.newVersion[2] = otaVersion.patch();
|
updatePacket.newVersion[2] = otaVersion.patch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePacket.temperature = boardTemperature; // Reset temperature after read
|
||||||
|
|
||||||
// Only populate the control characteristic with the status packet
|
// Only populate the control characteristic with the status packet
|
||||||
if (pCharacteristic == pUpgradeCharacteristic1) {
|
if (pCharacteristic == pUpgradeCharacteristic1) {
|
||||||
|
ESP_LOGI(tag, "Packet size: %d bytes, temp: %.2f", sizeof(updatePacket), updatePacket.temperature);
|
||||||
|
|
||||||
|
// Debug: Log first few bytes of packet for verification
|
||||||
|
uint8_t* packetBytes = reinterpret_cast<uint8_t*>(&updatePacket);
|
||||||
|
ESP_LOGI(tag, "Packet bytes: [0]=%d [1]=%d [2-5]=%.2f [6-9]=%d.%d.%d.%d",
|
||||||
|
packetBytes[0], packetBytes[1], updatePacket.temperature,
|
||||||
|
packetBytes[6], packetBytes[7], packetBytes[8], packetBytes[9]);
|
||||||
|
|
||||||
pCharacteristic->setValue(reinterpret_cast<uint8_t*>(&updatePacket), sizeof(updatePacket));
|
pCharacteristic->setValue(reinterpret_cast<uint8_t*>(&updatePacket), sizeof(updatePacket));
|
||||||
ESP_LOGI(tag, "Upgrade status read");
|
ESP_LOGI(tag, "Upgrade status read");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -35,12 +35,13 @@ class ServerCallbacks : public NimBLEServerCallbacks {
|
|||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
class ServerCallbacks : public NimBLEServerCallbacks {
|
class ServerCallbacks : public NimBLEServerCallbacks {
|
||||||
public:
|
public:
|
||||||
void onConnect(NimBLEServer* /*pServer*/) override {
|
void onConnect(NimBLEServer* /*pServer*/) override {
|
||||||
ESP_LOGI(tag, "Client connected");
|
ESP_LOGI(tag, "Client connected");
|
||||||
ensureAdvertising("onConnect");
|
ensureAdvertising("onConnect");
|
||||||
Buzzer_Play_Tune(TUNE_CONNECTED);
|
Buzzer_Play_Tune(TUNE_CONNECTED, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDisconnect(NimBLEServer* /*pServer*/) override {
|
void onDisconnect(NimBLEServer* /*pServer*/) override {
|
||||||
@ -68,6 +69,7 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void Init_BleServer( bool isSP110EActive, bool isUpgradeActive) {
|
void Init_BleServer( bool isSP110EActive, bool isUpgradeActive) {
|
||||||
|
|
||||||
@ -111,6 +113,7 @@ void Init_BleServer( bool isSP110EActive, bool isUpgradeActive) {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
void Init_BleServer(bool isSP110EActive, bool isUpgradeActive) {
|
void Init_BleServer(bool isSP110EActive, bool isUpgradeActive) {
|
||||||
ESP_LOGI(tag, "Initializing BLE...");
|
ESP_LOGI(tag, "Initializing BLE...");
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "global.h" // for get_chip_mac
|
||||||
|
|
||||||
static const char *tag = "ble-settings";
|
static const char *tag = "ble-settings";
|
||||||
|
|
||||||
@ -62,6 +63,15 @@ void Load_BLE_Settings(const String &configPath) {
|
|||||||
BTUpgradeCharacteristic1UUID = safeJsonString(root, "upgrade-char1", BTUpgradeCharacteristic1UUID.c_str());
|
BTUpgradeCharacteristic1UUID = safeJsonString(root, "upgrade-char1", BTUpgradeCharacteristic1UUID.c_str());
|
||||||
BTUpgradeCharacteristic2UUID = safeJsonString(root, "upgrade-char2", BTUpgradeCharacteristic2UUID.c_str());
|
BTUpgradeCharacteristic2UUID = safeJsonString(root, "upgrade-char2", BTUpgradeCharacteristic2UUID.c_str());
|
||||||
|
|
||||||
|
// Appeand a semi unique suffix from the MAC address to the device name
|
||||||
|
char macSuffix[13] = {0}; // Just need 2 chars + null terminator
|
||||||
|
get_chip_mac(macSuffix, sizeof(macSuffix)); // Only get first 2 chars of MAC
|
||||||
|
BTDeviceName += "-"; // Add a separator
|
||||||
|
BTDeviceName += macSuffix[0]; // Add first character
|
||||||
|
BTDeviceName += macSuffix[1]; // Add second character
|
||||||
|
|
||||||
|
BTDeviceName = "SP110E";
|
||||||
|
|
||||||
ESP_LOGI(tag, "Loaded BLE config: name=%s svc=%s char1=%s stick=%s upg_svc=%s upg1=%s upg2=%s",
|
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(),
|
BTDeviceName.c_str(), BTServiceUUID.c_str(), BTSP110ECharacteristicUUID.c_str(),
|
||||||
BTStickCharacteristicUUID.c_str(), BTUpgradeServiceUUID.c_str(),
|
BTStickCharacteristicUUID.c_str(), BTUpgradeServiceUUID.c_str(),
|
||||||
|
|||||||
@ -6,7 +6,7 @@ static const char* tag = "ramp";
|
|||||||
// TODO add tickSkip to instanciation
|
// TODO add tickSkip to instanciation
|
||||||
#define TickDelayCount 5
|
#define TickDelayCount 5
|
||||||
|
|
||||||
RAMP_LIGHT::RAMP_LIGHT(OneButton* button, PWM_Output* pwmOutput, int min, int max, float step)
|
RAMP_LIGHT::RAMP_LIGHT(OneButton* button, PWM_Output* pwmOutput, float min, float max, float step)
|
||||||
: button(button), pwmOutput(pwmOutput), min(min), max(max), step(step) {
|
: button(button), pwmOutput(pwmOutput), min(min), max(max), step(step) {
|
||||||
|
|
||||||
button->attachClick([](void* context) { static_cast<RAMP_LIGHT*>(context)->singleClick(); }, this);
|
button->attachClick([](void* context) { static_cast<RAMP_LIGHT*>(context)->singleClick(); }, this);
|
||||||
@ -52,10 +52,11 @@ void RAMP_LIGHT::longPressStop(){
|
|||||||
void RAMP_LIGHT::duringLongPress(){
|
void RAMP_LIGHT::duringLongPress(){
|
||||||
if(IsOn){
|
if(IsOn){
|
||||||
if (tickCount > 0 && --tickCount == 0) {
|
if (tickCount > 0 && --tickCount == 0) {
|
||||||
currentValue += (rampState == RampingUp) ? step : -step;
|
// When ramping down, currentValue may go below min if step is large; constrain ensures bounds.
|
||||||
|
// Ensure currentValue stays within [min, max] bounds for safe PWM operation
|
||||||
currentValue = constrain(currentValue, min, max);
|
currentValue = constrain(currentValue, min, max);
|
||||||
pwmOutput->setOutput(currentValue);
|
pwmOutput->setOutput(currentValue);
|
||||||
ESP_LOGD(tag, "duty: %f, sent val: %d, actual val: %d", currentValue, pwmOutput->currOutVal, pwmOutput->getOutVal());
|
//ESP_LOGD(tag, "duty: %f, sent val: %d, actual val: %d", currentValue, pwmOutput->currOutVal, pwmOutput->getOutVal());
|
||||||
tickCount = TickDelayCount;
|
tickCount = TickDelayCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,9 @@ enum COMM_MODE commMode = COMM_WIFI_AP_BLE;
|
|||||||
CHIP_INFO chipInfo;
|
CHIP_INFO chipInfo;
|
||||||
Version localVersion = {FIRMWARE_VERSION_MAJOR, FIRMWARE_VERSION_MINOR, FIRMWARE_VERSION_PATCH};
|
Version localVersion = {FIRMWARE_VERSION_MAJOR, FIRMWARE_VERSION_MINOR, FIRMWARE_VERSION_PATCH};
|
||||||
|
|
||||||
|
float boardTemperature = 10.3; // More realistic temperature for testing
|
||||||
|
|
||||||
|
// Get the MAC address of the chip and format it as a string
|
||||||
void get_chip_mac(char* macStr, size_t size) {
|
void get_chip_mac(char* macStr, size_t size) {
|
||||||
uint64_t chipMAC = ESP.getEfuseMac();
|
uint64_t chipMAC = ESP.getEfuseMac();
|
||||||
uint8_t macByte[6];
|
uint8_t macByte[6];
|
||||||
|
|||||||
462
src/main.cpp
462
src/main.cpp
@ -35,7 +35,9 @@
|
|||||||
#include "BLE_SP110E.h"
|
#include "BLE_SP110E.h"
|
||||||
#include "BleSettings.h"
|
#include "BleSettings.h"
|
||||||
|
|
||||||
#define FREERTOs_DIAGNOSTICS 0
|
#include "my_device.h"
|
||||||
|
|
||||||
|
#define FREERTOS_DIAGNOSTICS 0
|
||||||
#define OLED_ENABLED 0
|
#define OLED_ENABLED 0
|
||||||
#define WIFI_ENABLED 0
|
#define WIFI_ENABLED 0
|
||||||
#define STRIPS_ENABLED 1
|
#define STRIPS_ENABLED 1
|
||||||
@ -65,20 +67,59 @@ LUMA_PACKET lumaPacket;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const char *tag = "main";
|
static const char *tag = "main";
|
||||||
SYS_SETTINGS sys_settings;
|
|
||||||
PWM_Output *pwmOutputs[4];
|
|
||||||
RAMP_LIGHT *rampLight1;
|
|
||||||
RAMP_LIGHT *rampLight2;
|
|
||||||
|
|
||||||
bool UpgradeMode = false;
|
// Timer handles for periodic tasks
|
||||||
|
TimerHandle_t buttonScanTimer = NULL;
|
||||||
|
#define buttonScanInterval 10 // ms
|
||||||
|
|
||||||
|
TimerHandle_t temperatureTimer = NULL;
|
||||||
|
TimerHandle_t statusLedTimer = NULL;
|
||||||
|
#define statusLedInterval 500 // ms
|
||||||
|
|
||||||
|
TimerHandle_t upgradeHeartbeatTimer = NULL;
|
||||||
|
#define upgradeHeartbeatInterval 5000 // ms
|
||||||
|
|
||||||
|
TimerHandle_t diagnosticsTimer = NULL;
|
||||||
|
#define diagnosticsInterval 60000 // ms
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Init_ADC(void);
|
|
||||||
float readBoardInputVoltage(void);
|
|
||||||
void setupLogLevels(esp_log_level_t logLevel);
|
void setupLogLevels(esp_log_level_t logLevel);
|
||||||
void Get_Board_and_Booth_File_Paths(const char *, String &, String &);
|
|
||||||
void Load_Booth_Settings(SYS_SETTINGS &sys, const String &boothPath);
|
// Timer callback functions
|
||||||
void Init_PWM_Outputs(int8_t (&pin)[4], PWM_OUT_SETTINGS (&pwmSettings)[4]);
|
void ButtonScanCallback(TimerHandle_t xTimer) {
|
||||||
void Init_Ramp_Lights(RAMP_LIGHT_SETTINGS (&settings)[2], OneButton *(&btn)[3], PWM_Output *(&pwm)[4]);
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (boardButtons[i] != NULL) {
|
||||||
|
boardButtons[i]->tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TemperatureCallback(TimerHandle_t xTimer) {
|
||||||
|
if (sys_settings.tSensorSettings.enabled) {
|
||||||
|
boardTemperature = tSensor->readTemperatureF();
|
||||||
|
// Fan Control
|
||||||
|
UpdateFanControl(boardTemperature, pwmOutputs[sys_settings.tSensorSettings.pwmIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatusLedCallback(TimerHandle_t xTimer) {
|
||||||
|
if (sys_settings.boardPins.stat[1] >= 0) {
|
||||||
|
static bool ledState = false;
|
||||||
|
setStatusPin2(ledState = !ledState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpgradeHeartbeatCallback(TimerHandle_t xTimer) {
|
||||||
|
Buzzer_Play_Tune(TUNE_LOWBEEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiagnosticsCallback(TimerHandle_t xTimer) {
|
||||||
|
#if FREERTOs_DIAGNOSTICS
|
||||||
|
print_task_watermarks();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void checkLEDCChannels()
|
void checkLEDCChannels()
|
||||||
{
|
{
|
||||||
@ -94,6 +135,8 @@ void checkLEDCChannels()
|
|||||||
#define Button1Pin 8
|
#define Button1Pin 8
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
|
bool UpgradeMode = false;
|
||||||
|
|
||||||
// Serial Port
|
// Serial Port
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
while (!Serial);
|
while (!Serial);
|
||||||
@ -120,7 +163,7 @@ void setup()
|
|||||||
Init_File_System();
|
Init_File_System();
|
||||||
|
|
||||||
// Print all system files
|
// Print all system files
|
||||||
if (digitalRead(sys_settings.boardPins.btn[0]) == LOW){
|
if (digitalRead(sys_settings.boardPins.btn[1]) == LOW){
|
||||||
printAllSystemFiles();
|
printAllSystemFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,25 +203,25 @@ void setup()
|
|||||||
Init_ADC();
|
Init_ADC();
|
||||||
|
|
||||||
// Initialize Temperature Sensor
|
// Initialize Temperature Sensor
|
||||||
Init_TSensor(72);
|
Init_TSensor(72, &sys_settings.tSensorSettings);
|
||||||
|
|
||||||
|
|
||||||
float val = readBoardInputVoltage();
|
float val = readBoardInputVoltage();
|
||||||
ESP_LOGI(tag, "Input Volage = %f", val);
|
ESP_LOGI(tag, "Input Volage = %f", val);
|
||||||
|
|
||||||
// Initialize BLE
|
// Initialize BLE & Wifi
|
||||||
|
// If button 1 is held during boot, enable upgrade mode
|
||||||
Load_BLE_Settings("/system/ble.json");
|
Load_BLE_Settings("/system/ble.json");
|
||||||
if (digitalRead(sys_settings.boardPins.btn[0]) == LOW)
|
if (digitalRead(sys_settings.boardPins.btn[0]) == LOW)
|
||||||
{
|
{
|
||||||
setStatusPin1(true);
|
setStatusPin1(true);
|
||||||
UpgradeMode = true;
|
|
||||||
ESP_LOGW(tag, "Upgrade Mode Triggered");
|
ESP_LOGW(tag, "Upgrade Mode Triggered");
|
||||||
ESP_LOGW(tag, "Enabling BLE and Update Service");
|
ESP_LOGW(tag, "Enabling BLE and Update Service");
|
||||||
Init_BleServer(true, true);
|
Init_BleServer(true, true);
|
||||||
ESP_LOGW(tag, "Enabling Wifi AP and Client");
|
ESP_LOGW(tag, "Enabling Wifi AP and Client");
|
||||||
Wifi_Init();
|
Wifi_Init();
|
||||||
|
UpgradeMode = true;
|
||||||
//Buzzer_Play_Tune(TUNE_UPGRADE_MODE);
|
upgradeHeartbeatTimer = xTimerCreate("UpgradeHeartbeat", pdMS_TO_TICKS(5000), pdTRUE, NULL, UpgradeHeartbeatCallback);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -198,54 +241,46 @@ void setup()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if STRIPS_ENABLED
|
#if STRIPS_ENABLED
|
||||||
Init_Lights_Task();
|
Init_RGB_Lights_Task();
|
||||||
|
//vTaskDelay(100);
|
||||||
|
//Init_Ramp_Front_Light_Task();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Buzzer_Play_Tune(TUNE_BOOT);
|
// Create and start software timers
|
||||||
|
buttonScanTimer = xTimerCreate("ButtonScan", pdMS_TO_TICKS(buttonScanInterval), pdTRUE, NULL, ButtonScanCallback);
|
||||||
|
temperatureTimer = xTimerCreate("Temperature", pdMS_TO_TICKS(sys_settings.tSensorSettings.intervalMs), pdTRUE, NULL, TemperatureCallback);
|
||||||
|
statusLedTimer = xTimerCreate("StatusLED", pdMS_TO_TICKS(statusLedInterval), pdTRUE, NULL, StatusLedCallback);
|
||||||
|
|
||||||
|
|
||||||
|
#if FREERTOs_DIAGNOSTICS
|
||||||
|
diagnosticsTimer = xTimerCreate("Diagnostics", pdMS_TO_TICKS(60000), pdTRUE, NULL, DiagnosticsCallback);
|
||||||
|
#endif
|
||||||
|
|
||||||
// TODO... Test if this is still necessary need to configure pin 0 for some reason
|
// Start the timers
|
||||||
// pinMode(0, INPUT); // button0/boot pin
|
if (buttonScanTimer) xTimerStart(buttonScanTimer, 25);
|
||||||
|
if (temperatureTimer && sys_settings.tSensorSettings.enabled) xTimerStart(temperatureTimer, 100);
|
||||||
|
if (statusLedTimer) xTimerStart(statusLedTimer, 0);
|
||||||
|
if (upgradeHeartbeatTimer && UpgradeMode) xTimerStart(upgradeHeartbeatTimer, upgradeHeartbeatInterval);
|
||||||
|
|
||||||
|
#if FREERTOS_DIAGNOSTICS
|
||||||
|
if (diagnosticsTimer) xTimerStart(diagnosticsTimer, diagnosticsInterval);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(UpgradeMode){
|
||||||
|
Buzzer_Play_Tune(TUNE_BOOT);
|
||||||
|
}else{
|
||||||
|
Buzzer_Play_Tune(TUNE_BOOT);
|
||||||
|
}
|
||||||
|
|
||||||
#if LUMASTIK_ENABLED
|
#if LUMASTIK_ENABLED
|
||||||
Init_Luma_Master();
|
Init_Luma_Master();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
vTaskDelay(100);
|
|
||||||
Lights_Control_Task_Resume();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
// Button Scanning
|
// Animation TestMode Timeout (keep in loop as it's event-driven)
|
||||||
ON_EVERY_N_MILLISECONDS(10)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
if (boardButtons[i] != NULL)
|
|
||||||
{
|
|
||||||
boardButtons[i]->tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temperature Monitor
|
|
||||||
static OnEveryMsVariable temperatureMonitorTimer;
|
|
||||||
if (sys_settings.tSensorSettings.enabled)
|
|
||||||
{
|
|
||||||
if (temperatureMonitorTimer.ready(sys_settings.tSensorSettings.intervalMs))
|
|
||||||
{
|
|
||||||
static float boardTemperature;
|
|
||||||
boardTemperature = tSensor->readTemperatureF();
|
|
||||||
// ESP_LOGI(tag, "Board T: %F", boardTemperature);
|
|
||||||
|
|
||||||
// Fan Control
|
|
||||||
UpdateFanControl(boardTemperature, pwmOutputs[sys_settings.tSensorSettings.pwmIndex]);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Animation TestMode Timeout
|
|
||||||
#if LEDS_ENABLED
|
#if LEDS_ENABLED
|
||||||
if (animStatus.EventTestCountdown)
|
if (animStatus.EventTestCountdown)
|
||||||
{
|
{
|
||||||
@ -261,7 +296,7 @@ void loop()
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Reboot requested
|
// Reboot requested (keep in loop as it's a state-based countdown)
|
||||||
#if WIFI_ENABLED
|
#if WIFI_ENABLED
|
||||||
if (RebootSystem)
|
if (RebootSystem)
|
||||||
{
|
{
|
||||||
@ -281,46 +316,8 @@ void loop()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Toggle Status LED L2
|
// Small delay to prevent busy-waiting
|
||||||
ON_EVERY_N_MILLISECONDS(500)
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
{
|
|
||||||
if (sys_settings.boardPins.stat[1] >= 0)
|
|
||||||
{
|
|
||||||
static bool ledState = false;
|
|
||||||
// digitalWrite(sys_settings.boardPins.stat[1], ledState = !ledState);
|
|
||||||
setStatusPin2(ledState = !ledState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upgrade Mode Hearbeat tune
|
|
||||||
if(UpgradeMode){
|
|
||||||
ON_EVERY_N_MILLISECONDS(5000)
|
|
||||||
{
|
|
||||||
Buzzer_Play_Tune(TUNE_LOWBEEP);
|
|
||||||
//ESP_LOGI(tag, "Upgrade Mode Heartbeat");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FREERTOs_DIAGNOSTICS
|
|
||||||
ON_EVERY_N_MILLISECONDS(60000)
|
|
||||||
{
|
|
||||||
print_task_watermarks();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Turn off white light after timeout
|
|
||||||
ON_EVERY_N_MILLISECONDS(100)
|
|
||||||
{
|
|
||||||
// Only decrement if timeout is active
|
|
||||||
if (whiteTimeout > 0) {
|
|
||||||
whiteTimeout--;
|
|
||||||
if (whiteTimeout == 0) {
|
|
||||||
Lights_Set_White(0);
|
|
||||||
ESP_LOGD(tag, "White light timeout triggered");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupLogLevels(esp_log_level_t logLevel)
|
void setupLogLevels(esp_log_level_t logLevel)
|
||||||
@ -344,280 +341,5 @@ void setupLogLevels(esp_log_level_t logLevel)
|
|||||||
esp_log_level_set("tsensor", logLevel);
|
esp_log_level_set("tsensor", logLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Restore original setOutput code..
|
|
||||||
#define RELAY_RES 10
|
|
||||||
void Init_PWM_Outputs(int8_t (&pin)[4], PWM_OUT_SETTINGS (&pwmSettings)[4])
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
int chIndex = findUnusedLedcChannel();
|
|
||||||
if (chIndex < 0)
|
|
||||||
{
|
|
||||||
ESP_LOGE(tag, "No available LEDC channel for PWM Output%d", i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pwmOutputs[i] = new PWM_Output(pin[i], chIndex, RELAY_RES, pwmSettings[i].freq, pwmSettings[i].max, false);
|
|
||||||
pwmOutputs[i]->setOutput(pwmSettings[i].def);
|
|
||||||
// pwmOutputs[i]->setOutput(5.0);
|
|
||||||
ESP_LOGI(tag, "PWM Output%d: Pin=%d, Freq=%d, ch=%d", i, pin[i], pwmSettings[i].freq, chIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Init_Ramp_Lights(RAMP_LIGHT_SETTINGS (&settings)[2], OneButton *(&btn)[3], PWM_Output *(&pwm)[4])
|
|
||||||
{
|
|
||||||
if (settings[0].enabled)
|
|
||||||
{
|
|
||||||
rampLight1 = new RAMP_LIGHT(btn[settings[0].btnIndex], pwm[settings[0].pwmOutIndex], 5.0, 100.0, 1.5);
|
|
||||||
ESP_LOGD(tag, "RampLight%d: btn=%d, pwmIndex=%d", 1, settings[0].btnIndex, settings[0].pwmOutIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings[1].enabled)
|
|
||||||
{
|
|
||||||
rampLight2 = new RAMP_LIGHT(btn[settings[1].btnIndex], pwm[settings[1].pwmOutIndex], 5.0, 100.0, 1.5);
|
|
||||||
ESP_LOGD(tag, "RampLight%d: btn=%d, pwmIndex=%d", 2, settings[1].btnIndex, settings[1].pwmOutIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the files that should be used to setup the system
|
|
||||||
void Get_Board_and_Booth_File_Paths(const char *sysPath, String &boardPath, String &boothPath)
|
|
||||||
{
|
|
||||||
File file = LittleFS.open(sysPath);
|
|
||||||
|
|
||||||
if (!file)
|
|
||||||
{
|
|
||||||
ESP_LOGE(tag, "Error opening %s...", sysPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonDocument doc;
|
|
||||||
DeserializationError error = deserializeJson(doc, file);
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
ESP_LOGE(tag, "%s deserialize error!..", sysPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get hardware version string
|
|
||||||
boardPath = jsonConstrainString(tag, doc.as<JsonObject>(), "boardfile", "/cfg/boards/board15.json");
|
|
||||||
boothPath = jsonConstrainString(tag, doc.as<JsonObject>(), "configfile", "/cfg/booths/custom.json");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Load_Booth_Settings(SYS_SETTINGS &sys, const String &boothPath)
|
|
||||||
{
|
|
||||||
File file = LittleFS.open(boothPath);
|
|
||||||
if (!file)
|
|
||||||
{
|
|
||||||
ESP_LOGE(tag, "Error opening %s...", boothPath.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonDocument doc;
|
|
||||||
DeserializationError error = deserializeJson(doc, file);
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
ESP_LOGE(tag, "%s deserialize error!..", boothPath.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ********** Mode ***********
|
|
||||||
String modeStr = jsonConstrainString(tag, doc.as<JsonObject>(), "mode", "booth");
|
|
||||||
if (modeStr == "roamer")
|
|
||||||
{
|
|
||||||
sys_settings.mode = BOOTH_MODE_ROAMER;
|
|
||||||
}
|
|
||||||
else if (modeStr == "stik")
|
|
||||||
{
|
|
||||||
sys_settings.mode = BOOTH_MODE_STIK;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sys_settings.mode = BOOTH_MODE_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ********** PWM Out ***********
|
|
||||||
JsonArray pwmJsonArray = doc["pwmout"];
|
|
||||||
if (!pwmJsonArray.isNull())
|
|
||||||
{
|
|
||||||
int pwmIndex = 0;
|
|
||||||
for (JsonObject obj : pwmJsonArray)
|
|
||||||
{
|
|
||||||
if (pwmIndex >= 4)
|
|
||||||
break;
|
|
||||||
sys_settings.pwmOutSettings[pwmIndex].enabled = jsonConstrainBool(tag, obj, "en", true);
|
|
||||||
sys_settings.pwmOutSettings[pwmIndex].freq = jsonConstrain<int>(tag, obj, "freq", 100, 5000, 250);
|
|
||||||
sys_settings.pwmOutSettings[pwmIndex].min = jsonConstrain<float>(tag, obj, "min", 0.0, 95.0, 0.0);
|
|
||||||
sys_settings.pwmOutSettings[pwmIndex].max = jsonConstrain<float>(tag, obj, "max", 5.0, 100.0, 100.0);
|
|
||||||
sys_settings.pwmOutSettings[pwmIndex].def = jsonConstrain<float>(tag, obj, "default", 0.0, 100.0, 0.0);
|
|
||||||
sys_settings.pwmOutSettings[pwmIndex].deltaRate = 1.0;
|
|
||||||
// sys_settings.pwmOutSettings[pwmIndex]. = jsonConstrainBool(tag, obj, "vision", true);
|
|
||||||
pwmIndex++;
|
|
||||||
}
|
|
||||||
ESP_LOGI(tag, "Loaded PWmOutput settings...");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ESP_LOGE(tag, "Error!, %s key: pwmout not found..", boothPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ********** Ramp Lights ***********
|
|
||||||
JsonArray rampJsonArray = doc["ramp-lights"];
|
|
||||||
if (!rampJsonArray.isNull())
|
|
||||||
{
|
|
||||||
int rampIndex = 0;
|
|
||||||
for (JsonObject obj : rampJsonArray)
|
|
||||||
{
|
|
||||||
if (rampIndex >= 2)
|
|
||||||
break;
|
|
||||||
sys_settings.rampLightSettings[rampIndex].enabled = jsonConstrainBool(tag, obj, "en", true);
|
|
||||||
sys_settings.rampLightSettings[rampIndex].vision = jsonConstrainBool(tag, obj, "vision", true);
|
|
||||||
sys_settings.rampLightSettings[rampIndex].pwmOutIndex = jsonConstrain<int>(tag, obj, "relay-index", 0, 1, 0);
|
|
||||||
sys_settings.rampLightSettings[rampIndex].btnIndex = jsonConstrain<int>(tag, obj, "button-index", 0, 1, 0);
|
|
||||||
rampIndex++;
|
|
||||||
}
|
|
||||||
ESP_LOGI(tag, "Loaded Ramp Lights settings...");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ESP_LOGE(tag, "Error!, %s key: ramp-lights not found..", boothPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ********** Fan ***********
|
|
||||||
JsonObject sensorJson = doc["t-sensor"];
|
|
||||||
if (!sensorJson.isNull())
|
|
||||||
{
|
|
||||||
sys_settings.tSensorSettings.enabled = jsonConstrainBool(tag, sensorJson, "en", true);
|
|
||||||
sys_settings.tSensorSettings.pwmIndex = jsonConstrain<int>(tag, sensorJson, "relay", 0, 3, 3);
|
|
||||||
sys_settings.tSensorSettings.setpoint1 = jsonConstrain<float>(tag, sensorJson, "sp1", 0.0, 100.0, 80.0);
|
|
||||||
sys_settings.tSensorSettings.setpoint2 = jsonConstrain<float>(tag, sensorJson, "sp2", 0.0, 100.0, 85.0);
|
|
||||||
sys_settings.tSensorSettings.fanPower1 = jsonConstrain<float>(tag, sensorJson, "fan-pwr1", 0.0, 100.0, 50.0);
|
|
||||||
sys_settings.tSensorSettings.fanPower2 = jsonConstrain<float>(tag, sensorJson, "fan-pwr2", 0.0, 100.0, 50.0);
|
|
||||||
sys_settings.tSensorSettings.hyst = jsonConstrain<float>(tag, sensorJson, "hyst", 1.0, 10.0, 1.0);
|
|
||||||
sys_settings.tSensorSettings.intervalMs = jsonConstrain<int>(tag, sensorJson, "interval", 1000, 30000, 5000);
|
|
||||||
ESP_LOGI(tag, "Loaded TSensor settings...");
|
|
||||||
ESP_LOGD(tag, " SP1: %F, SP2 %F, Hyst: %F", sys_settings.tSensorSettings.setpoint1, sys_settings.tSensorSettings.setpoint2, sys_settings.tSensorSettings.hyst);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ESP_LOGE(tag, "Error!, %s key: t-sensor not found..", boothPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ********** RGB Strips ***********
|
|
||||||
JsonArray stripsJsonArray = doc["strips"];
|
|
||||||
if (!stripsJsonArray.isNull())
|
|
||||||
{
|
|
||||||
int stripIndex = 0;
|
|
||||||
for (JsonObject obj : stripsJsonArray)
|
|
||||||
{
|
|
||||||
if (stripIndex >= 2)
|
|
||||||
break;
|
|
||||||
sys_settings.ledStripSettings[stripIndex]->enabled = jsonConstrainBool(tag, obj, "en", true);
|
|
||||||
sys_settings.ledStripSettings[stripIndex]->size = jsonConstrain<int>(tag, obj, "size", 1, 250, 25);
|
|
||||||
sys_settings.ledStripSettings[stripIndex]->chip = jsonConstrainString(tag, obj, "chip", "WS2812B");
|
|
||||||
sys_settings.ledStripSettings[stripIndex]->rgbOrder = jsonConstrainString(tag, obj, "rgb-order", "WS2812B");
|
|
||||||
sys_settings.ledStripSettings[stripIndex]->shift = jsonConstrain<int>(tag, obj, "shift", -250, 250, 0);
|
|
||||||
sys_settings.ledStripSettings[stripIndex]->offset = jsonConstrain<int>(tag, obj, "offset", -250, 250, 0);
|
|
||||||
sys_settings.ledStripSettings[stripIndex]->bright = jsonConstrain<int>(tag, obj, "bright", 5, 255, 200);
|
|
||||||
sys_settings.ledStripSettings[stripIndex]->powerDiv = 0;
|
|
||||||
sys_settings.ledStripSettings[stripIndex]->i2sCh = 0;
|
|
||||||
sys_settings.ledStripSettings[stripIndex]->core = jsonConstrain<int>(tag, obj, "core", 0, 1, 0);
|
|
||||||
stripIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
sys_settings.ledStripSettings[0]->pin = sys_settings.boardPins.rgb1;
|
|
||||||
sys_settings.ledStripSettings[1]->pin = sys_settings.boardPins.rgb2;
|
|
||||||
ESP_LOGI(tag, "Loaded LED Strip settings...");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ESP_LOGE(tag, "Error!, %s key: strips not found..");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ********** BLE ***********
|
|
||||||
JsonObject bleJson = doc["ble"];
|
|
||||||
if (!bleJson.isNull())
|
|
||||||
{
|
|
||||||
sys_settings.bleSettings.enabled = jsonConstrainBool(tag, bleJson, "en", true);
|
|
||||||
sys_settings.bleSettings.name = jsonConstrainString(tag, bleJson, "name", "ATA_LIGHTS");
|
|
||||||
|
|
||||||
ESP_LOGI(tag, "Loaded BLE settings...");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ESP_LOGE(tag, "Error!, %s key: ble not found..", boothPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ********** RF Remote***********
|
|
||||||
/*
|
|
||||||
JsonObject rfJson = doc["wifi-ap"];
|
|
||||||
if(!rfJson.isNull()){
|
|
||||||
//sys_settings.rampLights[0].enabled = jsonConstrainBool(tag, wifiApJson, "en", true);
|
|
||||||
ESP_LOGI(tag, "Loaded RF Remote settings...");
|
|
||||||
}else{
|
|
||||||
ESP_LOGE(tag, "Error!, %s key: wifi-ap not found..", boothPath);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void Init_ADC(void)
|
|
||||||
{
|
|
||||||
// Configure ADC
|
|
||||||
analogReadResolution(12); // 12-bit ADC
|
|
||||||
analogSetAttenuation(ADC_11db);
|
|
||||||
if (sys_settings.boardPins.adc1 >= 0)
|
|
||||||
{
|
|
||||||
analogSetPinAttenuation(sys_settings.boardPins.adc1, ADC_11db);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable ADC calibration
|
|
||||||
esp_adc_cal_characteristics_t adc_chars;
|
|
||||||
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 0, &adc_chars);
|
|
||||||
|
|
||||||
// Check calibration success
|
|
||||||
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK)
|
|
||||||
{
|
|
||||||
ESP_LOGI(tag, "ADC calibration: Using Two Point values from eFuse");
|
|
||||||
}
|
|
||||||
else if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK)
|
|
||||||
{
|
|
||||||
ESP_LOGI(tag, "ADC calibration: Using reference voltage from eFuse");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ESP_LOGW(tag, "ADC calibration: Using default reference voltage");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float readBoardInputVoltage(void)
|
|
||||||
{
|
|
||||||
const int SAMPLES = 64;
|
|
||||||
uint32_t reading = 0;
|
|
||||||
|
|
||||||
if (sys_settings.boardPins.adc1 < 0)
|
|
||||||
{
|
|
||||||
ESP_LOGE(tag, "ADC Pin not valid");
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiple readings for averaging
|
|
||||||
for (int i = 0; i < SAMPLES; i++)
|
|
||||||
{
|
|
||||||
reading += analogRead(sys_settings.boardPins.adc1);
|
|
||||||
delayMicroseconds(50); // Small delay between samples
|
|
||||||
}
|
|
||||||
reading /= SAMPLES;
|
|
||||||
|
|
||||||
// Convert raw ADC to voltage using calibration
|
|
||||||
uint32_t voltage_mv;
|
|
||||||
esp_adc_cal_characteristics_t adc_chars;
|
|
||||||
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adc_chars);
|
|
||||||
voltage_mv = esp_adc_cal_raw_to_voltage(reading, &adc_chars);
|
|
||||||
|
|
||||||
// Scale to 12V range
|
|
||||||
float voltage = (voltage_mv / 1000.0f) * (10470.0f / 470.0f);
|
|
||||||
|
|
||||||
return voltage;
|
|
||||||
}
|
|
||||||
318
src/my_device.cpp
Normal file
318
src/my_device.cpp
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
#include "my_device.h"
|
||||||
|
#include "JsonConstrain.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include "system.h"
|
||||||
|
#include "my_buttons.h"
|
||||||
|
#include "PWM_Output.h"
|
||||||
|
#include "Ramp_Lights.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include <FS.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <esp_system.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_adc_cal.h>
|
||||||
|
#include <driver/adc.h>
|
||||||
|
#include <driver/ledc.h>
|
||||||
|
#include <OneButton.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
static const char *tag = "my_device";
|
||||||
|
|
||||||
|
SYS_SETTINGS sys_settings;
|
||||||
|
PWM_Output *pwmOutputs[4];
|
||||||
|
RAMP_LIGHT *rampLight1;
|
||||||
|
RAMP_LIGHT *rampLight2;
|
||||||
|
|
||||||
|
// TODO Restore original setOutput code..
|
||||||
|
#define RELAY_RES 10
|
||||||
|
void Init_PWM_Outputs(int8_t (&pin)[4], PWM_OUT_SETTINGS (&pwmSettings)[4])
|
||||||
|
{
|
||||||
|
// Initialize all pointers to nullptr first
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
pwmOutputs[i] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
int chIndex = findUnusedLedcChannel();
|
||||||
|
if (chIndex < 0)
|
||||||
|
{
|
||||||
|
ESP_LOGE(tag, "No available LEDC channel for PWM Output%d", i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pwmOutputs[i] = new PWM_Output(pin[i], chIndex, RELAY_RES, pwmSettings[i].freq, pwmSettings[i].max, false);
|
||||||
|
pwmOutputs[i]->setOutput(pwmSettings[i].def);
|
||||||
|
// pwmOutputs[i]->setOutput(5.0);
|
||||||
|
ESP_LOGI(tag, "PWM Output%d: Pin=%d, Freq=%d, ch=%d", i, pin[i], pwmSettings[i].freq, chIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Init_Ramp_Lights(RAMP_LIGHT_SETTINGS (&settings)[2], OneButton *(&btn)[3], PWM_Output *(&pwm)[4])
|
||||||
|
{
|
||||||
|
if (settings[0].enabled)
|
||||||
|
{
|
||||||
|
rampLight1 = new RAMP_LIGHT(btn[settings[0].btnIndex], pwm[settings[0].pwmOutIndex], settings[0].min, settings[0].max, settings[0].step);
|
||||||
|
ESP_LOGD(tag, "RampLight%d: btn=%d, pwmIndex=%d", 1, settings[0].btnIndex, settings[0].pwmOutIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings[1].enabled)
|
||||||
|
{
|
||||||
|
rampLight2 = new RAMP_LIGHT(btn[settings[1].btnIndex], pwm[settings[1].pwmOutIndex], settings[1].min, settings[1].max, settings[1].step);
|
||||||
|
ESP_LOGD(tag, "RampLight%d: btn=%d, pwmIndex=%d", 2, settings[1].btnIndex, settings[1].pwmOutIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Get the files that should be used to setup the system
|
||||||
|
void Get_Board_and_Booth_File_Paths(const char *sysPath, String &boardPath, String &boothPath)
|
||||||
|
{
|
||||||
|
File file = LittleFS.open(sysPath);
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
ESP_LOGE(tag, "Error opening %s...", sysPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDocument doc;
|
||||||
|
DeserializationError error = deserializeJson(doc, file);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
ESP_LOGE(tag, "%s deserialize error!..", sysPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get hardware version string
|
||||||
|
boardPath = jsonConstrainString(tag, doc.as<JsonObject>(), "boardfile", "/cfg/boards/board15.json");
|
||||||
|
boothPath = jsonConstrainString(tag, doc.as<JsonObject>(), "configfile", "/cfg/booths/custom.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Load_Booth_Settings(SYS_SETTINGS &sys, const String &boothPath)
|
||||||
|
{
|
||||||
|
File file = LittleFS.open(boothPath);
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
ESP_LOGE(tag, "Error opening %s...", boothPath.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDocument doc;
|
||||||
|
DeserializationError error = deserializeJson(doc, file);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
ESP_LOGE(tag, "%s deserialize error!..", boothPath.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ********** Mode ***********
|
||||||
|
String modeStr = jsonConstrainString(tag, doc.as<JsonObject>(), "mode", "booth");
|
||||||
|
if (modeStr == "roamer")
|
||||||
|
{
|
||||||
|
sys_settings.mode = BOOTH_MODE_ROAMER;
|
||||||
|
}
|
||||||
|
else if (modeStr == "stik")
|
||||||
|
{
|
||||||
|
sys_settings.mode = BOOTH_MODE_STIK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sys_settings.mode = BOOTH_MODE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ********** PWM Out ***********
|
||||||
|
JsonArray pwmJsonArray = doc["pwmout"];
|
||||||
|
if (!pwmJsonArray.isNull())
|
||||||
|
{
|
||||||
|
int pwmIndex = 0;
|
||||||
|
for (JsonObject obj : pwmJsonArray)
|
||||||
|
{
|
||||||
|
if (pwmIndex >= sizeof(sys_settings.pwmOutSettings) / sizeof(sys_settings.pwmOutSettings[0]))
|
||||||
|
break;
|
||||||
|
sys_settings.pwmOutSettings[pwmIndex].enabled = jsonConstrainBool(tag, obj, "en", true);
|
||||||
|
sys_settings.pwmOutSettings[pwmIndex].freq = jsonConstrain<int>(tag, obj, "freq", 100, 5000, 250);
|
||||||
|
sys_settings.pwmOutSettings[pwmIndex].min = jsonConstrain<float>(tag, obj, "min", 0.0, 95.0, 0.0);
|
||||||
|
sys_settings.pwmOutSettings[pwmIndex].max = jsonConstrain<float>(tag, obj, "max", 5.0, 100.0, 100.0);
|
||||||
|
sys_settings.pwmOutSettings[pwmIndex].def = jsonConstrain<float>(tag, obj, "default", 0.0, 100.0, 0.0);
|
||||||
|
sys_settings.pwmOutSettings[pwmIndex].deltaRate = 1.0;
|
||||||
|
// sys_settings.pwmOutSettings[pwmIndex]. = jsonConstrainBool(tag, obj, "vision", true);
|
||||||
|
pwmIndex++;
|
||||||
|
}
|
||||||
|
ESP_LOGI(tag, "Loaded PWmOutput settings...");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE(tag, "Error!, %s key: pwmout not found..", boothPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ********** Ramp Lights ***********
|
||||||
|
JsonArray rampJsonArray = doc["ramp-lights"];
|
||||||
|
if (!rampJsonArray.isNull())
|
||||||
|
{
|
||||||
|
int rampIndex = 0;
|
||||||
|
for (JsonObject obj : rampJsonArray)
|
||||||
|
{
|
||||||
|
if (rampIndex >= sizeof(sys_settings.rampLightSettings) / sizeof(sys_settings.rampLightSettings[0]))
|
||||||
|
break;
|
||||||
|
sys_settings.rampLightSettings[rampIndex].enabled = jsonConstrainBool(tag, obj, "en", true);
|
||||||
|
sys_settings.rampLightSettings[rampIndex].vision = jsonConstrainBool(tag, obj, "vision", true);
|
||||||
|
sys_settings.rampLightSettings[rampIndex].pwmOutIndex = jsonConstrain<int>(tag, obj, "relay-index", 0, 1, 0);
|
||||||
|
sys_settings.rampLightSettings[rampIndex].btnIndex = jsonConstrain<int>(tag, obj, "button-index", 0, 1, 0);
|
||||||
|
sys_settings.rampLightSettings[rampIndex].min = jsonConstrain<float>(tag, obj, "min", 0.0, 95.0, 0.0);
|
||||||
|
sys_settings.rampLightSettings[rampIndex].max = jsonConstrain<float>(tag, obj, "max", 5.0, 100.0, 100.0);
|
||||||
|
sys_settings.rampLightSettings[rampIndex].step = jsonConstrain<float>(tag, obj, "step", 0.1, 10.0, 1.5);
|
||||||
|
rampIndex++;
|
||||||
|
}
|
||||||
|
ESP_LOGI(tag, "Loaded Ramp Lights settings...");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE(tag, "Error!, %s key: ramp-lights not found..", boothPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ********** Fan ***********
|
||||||
|
JsonObject sensorJson = doc["t-sensor"];
|
||||||
|
if (!sensorJson.isNull())
|
||||||
|
{
|
||||||
|
sys_settings.tSensorSettings.enabled = jsonConstrainBool(tag, sensorJson, "en", true);
|
||||||
|
sys_settings.tSensorSettings.pwmIndex = jsonConstrain<int>(tag, sensorJson, "relay", 0, 3, 3);
|
||||||
|
sys_settings.tSensorSettings.setpoint1 = jsonConstrain<float>(tag, sensorJson, "sp1", 50.0, 100.0, 80.0);
|
||||||
|
sys_settings.tSensorSettings.setpoint2 = jsonConstrain<float>(tag, sensorJson, "sp2", 60.0, 110.0, 90.0);
|
||||||
|
sys_settings.tSensorSettings.fanPower1 = jsonConstrain<float>(tag, sensorJson, "fan-pwr1", 0.0, 100.0, 50.0);
|
||||||
|
sys_settings.tSensorSettings.fanPower2 = jsonConstrain<float>(tag, sensorJson, "fan-pwr2", 50.0, 100.0, 50.0);
|
||||||
|
sys_settings.tSensorSettings.hyst = jsonConstrain<float>(tag, sensorJson, "hyst", 1.0, 10.0, 1.0);
|
||||||
|
sys_settings.tSensorSettings.intervalMs = jsonConstrain<int>(tag, sensorJson, "interval", 1000, 30000, 5000);
|
||||||
|
ESP_LOGI(tag, "Loaded TSensor settings...");
|
||||||
|
ESP_LOGI(tag, " SP1: %F, SP2 %F, Hyst: %F", sys_settings.tSensorSettings.setpoint1, sys_settings.tSensorSettings.setpoint2, sys_settings.tSensorSettings.hyst);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE(tag, "Error!, %s key: t-sensor not found..", boothPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ********** RGB Strips ***********
|
||||||
|
JsonArray stripsJsonArray = doc["strips"];
|
||||||
|
if (!stripsJsonArray.isNull())
|
||||||
|
{
|
||||||
|
int stripIndex = 0;
|
||||||
|
for (JsonObject obj : stripsJsonArray)
|
||||||
|
{
|
||||||
|
if (stripIndex >= 2)
|
||||||
|
break;
|
||||||
|
sys_settings.ledStripSettings[stripIndex]->enabled = jsonConstrainBool(tag, obj, "en", true);
|
||||||
|
sys_settings.ledStripSettings[stripIndex]->size = jsonConstrain<int>(tag, obj, "size", 1, 250, 25);
|
||||||
|
sys_settings.ledStripSettings[stripIndex]->chip = jsonConstrainString(tag, obj, "chip", "WS2812B");
|
||||||
|
sys_settings.ledStripSettings[stripIndex]->rgbOrder = jsonConstrainString(tag, obj, "rgb-order", "WS2812B");
|
||||||
|
sys_settings.ledStripSettings[stripIndex]->shift = jsonConstrain<int>(tag, obj, "shift", -250, 250, 0);
|
||||||
|
sys_settings.ledStripSettings[stripIndex]->offset = jsonConstrain<int>(tag, obj, "offset", -250, 250, 0);
|
||||||
|
sys_settings.ledStripSettings[stripIndex]->bright = jsonConstrain<int>(tag, obj, "bright", 5, 255, 200);
|
||||||
|
sys_settings.ledStripSettings[stripIndex]->powerDiv = 0;
|
||||||
|
sys_settings.ledStripSettings[stripIndex]->i2sCh = 0;
|
||||||
|
sys_settings.ledStripSettings[stripIndex]->core = jsonConstrain<int>(tag, obj, "core", 0, 1, 0);
|
||||||
|
stripIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_settings.ledStripSettings[0]->pin = sys_settings.boardPins.rgb1;
|
||||||
|
sys_settings.ledStripSettings[1]->pin = sys_settings.boardPins.rgb2;
|
||||||
|
ESP_LOGI(tag, "Loaded LED Strip settings...");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE(tag, "Error!, %s key: strips not found..");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ********** BLE ***********
|
||||||
|
JsonObject bleJson = doc["ble"];
|
||||||
|
if (!bleJson.isNull())
|
||||||
|
{
|
||||||
|
sys_settings.bleSettings.enabled = jsonConstrainBool(tag, bleJson, "en", true);
|
||||||
|
sys_settings.bleSettings.name = jsonConstrainString(tag, bleJson, "name", "ATA_LIGHTS");
|
||||||
|
|
||||||
|
ESP_LOGI(tag, "Loaded BLE settings...");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE(tag, "Error!, %s key: ble not found..", boothPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ********** RF Remote***********
|
||||||
|
/*
|
||||||
|
JsonObject rfJson = doc["wifi-ap"];
|
||||||
|
if(!rfJson.isNull()){
|
||||||
|
//sys_settings.rampLights[0].enabled = jsonConstrainBool(tag, wifiApJson, "en", true);
|
||||||
|
ESP_LOGI(tag, "Loaded RF Remote settings...");
|
||||||
|
}else{
|
||||||
|
ESP_LOGE(tag, "Error!, %s key: wifi-ap not found..", boothPath);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Init_ADC(void)
|
||||||
|
{
|
||||||
|
// Configure ADC
|
||||||
|
analogReadResolution(12); // 12-bit ADC
|
||||||
|
analogSetAttenuation(ADC_11db);
|
||||||
|
if (sys_settings.boardPins.adc1 >= 0)
|
||||||
|
{
|
||||||
|
analogSetPinAttenuation(sys_settings.boardPins.adc1, ADC_11db);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable ADC calibration
|
||||||
|
esp_adc_cal_characteristics_t adc_chars;
|
||||||
|
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 0, &adc_chars);
|
||||||
|
|
||||||
|
// Check calibration success
|
||||||
|
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGI(tag, "ADC calibration: Using Two Point values from eFuse");
|
||||||
|
}
|
||||||
|
else if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGI(tag, "ADC calibration: Using reference voltage from eFuse");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGW(tag, "ADC calibration: Using default reference voltage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float readBoardInputVoltage(void)
|
||||||
|
{
|
||||||
|
const int SAMPLES = 64;
|
||||||
|
uint32_t reading = 0;
|
||||||
|
|
||||||
|
if (sys_settings.boardPins.adc1 < 0)
|
||||||
|
{
|
||||||
|
ESP_LOGE(tag, "ADC Pin not valid");
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple readings for averaging
|
||||||
|
for (int i = 0; i < SAMPLES; i++)
|
||||||
|
{
|
||||||
|
reading += analogRead(sys_settings.boardPins.adc1);
|
||||||
|
delayMicroseconds(50); // Small delay between samples
|
||||||
|
}
|
||||||
|
reading /= SAMPLES;
|
||||||
|
|
||||||
|
// Convert raw ADC to voltage using calibration
|
||||||
|
uint32_t voltage_mv;
|
||||||
|
esp_adc_cal_characteristics_t adc_chars;
|
||||||
|
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adc_chars);
|
||||||
|
voltage_mv = esp_adc_cal_raw_to_voltage(reading, &adc_chars);
|
||||||
|
|
||||||
|
// Scale to 12V range
|
||||||
|
float voltage = (voltage_mv / 1000.0f) * (10470.0f / 470.0f);
|
||||||
|
|
||||||
|
return voltage;
|
||||||
|
}
|
||||||
@ -1,22 +1,31 @@
|
|||||||
#include "my_tsensor.h"
|
#include "my_tsensor.h"
|
||||||
#include <Temperature_LM75_Derived.h>
|
#include <Temperature_LM75_Derived.h>
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
#include "system.h"
|
||||||
|
|
||||||
static const char* tag = "tsensor";
|
static const char* tag = "tsensor";
|
||||||
|
|
||||||
T_SENSOR tSensorSettings;
|
TSENSOR_SETTINGS *t_settings = nullptr; // Global pointer reference
|
||||||
TI_TMP102_Compatible *tSensor = nullptr;
|
TI_TMP102_Compatible *tSensor = nullptr;
|
||||||
|
|
||||||
/******************* Temperature Control ********************/
|
/******************* Temperature Control ********************/
|
||||||
void Init_TSensor(uint8_t addr) {
|
void Init_TSensor(uint8_t addr, TSENSOR_SETTINGS *tsettings) {
|
||||||
|
|
||||||
|
if(tsettings != nullptr) {
|
||||||
|
t_settings = tsettings; // Store pointer reference
|
||||||
|
ESP_LOGI(tag, "TSensor settings loaded: SP1=%.1f, SP2=%.1f, FP1=%.1f, FP2=%.1f, Hyst=%.1f",
|
||||||
|
t_settings->setpoint1, t_settings->setpoint2,
|
||||||
|
t_settings->fanPower1, t_settings->fanPower2, t_settings->hyst);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the temperature sensor once with the provided I2C address
|
// Initialize the temperature sensor once with the provided I2C address
|
||||||
if (tSensor == nullptr) {
|
if (tSensor == nullptr) {
|
||||||
tSensor = new TI_TMP102_Compatible(addr);
|
tSensor = new TI_TMP102_Compatible(addr);
|
||||||
ESP_LOGI(tag, "TSensor initialized at I2C addr 0x%02X", addr);
|
ESP_LOGI(tag, "TSensor initialized at I2C addr 0x%02X", addr);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(tag, "TSensor already initialized; ignoring re-init request (addr 0x%02X)", addr);
|
ESP_LOGW(tag, "TSensor already initialized; ignoring re-init request (addr 0x%02X)", addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline float clampf(float v, float lo, float hi) {
|
static inline float clampf(float v, float lo, float hi) {
|
||||||
@ -26,62 +35,149 @@ TI_TMP102_Compatible *tSensor = nullptr;
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UpdateFanControl(float temperature, PWM_Output* pwmOut) {
|
void UpdateFanControl(float temperature, PWM_Output* pwmOut) {
|
||||||
|
|
||||||
if (pwmOut == nullptr) {
|
if (pwmOut == nullptr) {
|
||||||
ESP_LOGW(tag, "UpdateFanControl called with null PWM output");
|
ESP_LOGW(tag, "UpdateFanControl called with null PWM output");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isnan(temperature) || isinf(temperature)) {
|
if (isnan(temperature) || isinf(temperature)) {
|
||||||
ESP_LOGW(tag, "Invalid temperature reading: %f", temperature);
|
ESP_LOGW(tag, "Invalid temperature reading: %f", temperature);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t FanState = 0;
|
if (t_settings == nullptr || !t_settings->enabled) {
|
||||||
tSensorSettings.temperature = temperature; // cache last temp
|
ESP_LOGV(tag, "Temperature control disabled or not initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool fanIsOn = false;
|
||||||
float currentDuty = pwmOut->currDuty;
|
float currentDuty = pwmOut->currDuty;
|
||||||
float newDuty = currentDuty;
|
float newDuty = currentDuty;
|
||||||
|
|
||||||
// Sanitize settings locally (do not modify globals)
|
// Use TSENSOR_SETTINGS field names with pointer dereferencing
|
||||||
float sp1 = tSensorSettings.Setpoint1;
|
float sp1 = t_settings->setpoint1;
|
||||||
float sp2 = tSensorSettings.Setpoint2;
|
float sp2 = t_settings->setpoint2;
|
||||||
float hyst = tSensorSettings.hyst;
|
float hyst = t_settings->hyst;
|
||||||
float fp1 = tSensorSettings.fanPower1;
|
float fp1 = t_settings->fanPower1;
|
||||||
float fp2 = tSensorSettings.fanPower2;
|
float fp2 = t_settings->fanPower2;
|
||||||
|
|
||||||
if (hyst < 0.0f) hyst = 0.0f;
|
if (hyst < 0.0f) hyst = 0.0f;
|
||||||
if (sp2 < sp1) {
|
if (sp2 < sp1) {
|
||||||
// Ensure sp2 >= sp1
|
// Ensure sp2 >= sp1
|
||||||
float tmp = sp1; sp1 = sp2; sp2 = tmp;
|
float tmp = sp1; sp1 = sp2; sp2 = tmp;
|
||||||
|
tmp = fp1; fp1 = fp2; fp2 = tmp; // Also swap corresponding fan powers
|
||||||
}
|
}
|
||||||
const float maxDuty = pwmOut->getMaxDuty();
|
const float maxDuty = pwmOut->getMaxDuty();
|
||||||
fp1 = clampf(fp1, 0.0f, maxDuty);
|
fp1 = clampf(fp1, 0.0f, maxDuty);
|
||||||
fp2 = clampf(fp2, 0.0f, maxDuty);
|
fp2 = clampf(fp2, 0.0f, maxDuty);
|
||||||
|
|
||||||
// Fan State Logic
|
// Fan control with linear scaling and hysteresis
|
||||||
if ((FanState == 2) && (temperature < (sp2 - hyst))) {
|
if (!fanIsOn) {
|
||||||
newDuty = fp1;
|
// Fan is off - check if we should turn it on
|
||||||
FanState = 1;
|
if (temperature >= sp1) {
|
||||||
ESP_LOGD(tag, "Dropping down to FanPower1");
|
fanIsOn = true;
|
||||||
}
|
ESP_LOGI(tag, "Fan turning ON - temp %.2f >= setpoint1 %.2f", temperature, sp1);
|
||||||
else if ((FanState == 1) && (temperature < (sp1 - hyst))) {
|
} else {
|
||||||
newDuty = 0;
|
newDuty = 0.0f; // Stay off
|
||||||
FanState = 0;
|
}
|
||||||
ESP_LOGD(tag, "Dropping down to FanPower0");
|
} else {
|
||||||
}
|
// Fan is on - check if we should turn it off
|
||||||
else if ((FanState <= 1) && (temperature > sp1)) {
|
if (temperature < (sp1 - hyst)) {
|
||||||
newDuty = fp1;
|
fanIsOn = false;
|
||||||
if (temperature > sp2) {
|
newDuty = 0.0f;
|
||||||
newDuty = fp2;
|
ESP_LOGI(tag, "Fan turning OFF - temp %.2f < (setpoint1 - hyst) %.2f", temperature, sp1 - hyst);
|
||||||
FanState = 2;
|
}
|
||||||
ESP_LOGD(tag, "Raising up to FanPower2");
|
|
||||||
} else {
|
|
||||||
ESP_LOGD(tag, "Raising up to FanPower1");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply new duty cycle if changed
|
// If fan should be on, calculate linear scaling
|
||||||
if (currentDuty != newDuty) {
|
if (fanIsOn) {
|
||||||
pwmOut->setOutput(newDuty);
|
if (temperature <= sp1) {
|
||||||
ESP_LOGI(tag, "Board T: %.2f F, Fan -> %.2f (state=%u)", temperature, newDuty, FanState);
|
// At or just above sp1, use minimum fan power
|
||||||
|
newDuty = fp1;
|
||||||
|
} else if (temperature >= sp2) {
|
||||||
|
// At or above sp2, use maximum fan power
|
||||||
|
newDuty = fp2;
|
||||||
|
} else {
|
||||||
|
// Linear interpolation between sp1 and sp2
|
||||||
|
float tempRange = sp2 - sp1;
|
||||||
|
float powerRange = fp2 - fp1;
|
||||||
|
float tempRatio = (temperature - sp1) / tempRange;
|
||||||
|
newDuty = fp1 + (tempRatio * powerRange);
|
||||||
|
|
||||||
|
ESP_LOGV(tag, "Linear scaling: temp=%.2f, ratio=%.3f, duty=%.2f", temperature, tempRatio, newDuty);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure duty is within bounds
|
||||||
|
newDuty = clampf(newDuty, 0.0f, maxDuty);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Apply new duty cycle if changed (with small tolerance to avoid constant updates)
|
||||||
|
if (fabs(currentDuty - newDuty) > 0.1f) {
|
||||||
|
pwmOut->setOutput(newDuty);
|
||||||
|
ESP_LOGI(tag, "Board T: %.2f F, Fan -> %.2f%% (on=%s)", temperature, newDuty, fanIsOn ? "true" : "false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void UpdateFanControl(float temperature, PWM_Output* pwmOut) {
|
||||||
|
if (pwmOut == nullptr) {
|
||||||
|
ESP_LOGW(tag, "UpdateFanControl called with null PWM output");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isnan(temperature) || isinf(temperature)) {
|
||||||
|
ESP_LOGW(tag, "Invalid temperature reading: %f", temperature);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t FanState = 0;
|
||||||
|
tSensorSettings.temperature = temperature; // cache last temp
|
||||||
|
float currentDuty = pwmOut->currDuty;
|
||||||
|
float newDuty = currentDuty;
|
||||||
|
|
||||||
|
// Sanitize settings locally (do not modify globals)
|
||||||
|
float sp1 = tSensorSettings.Setpoint1;
|
||||||
|
float sp2 = tSensorSettings.Setpoint2;
|
||||||
|
float hyst = tSensorSettings.hyst;
|
||||||
|
float fp1 = tSensorSettings.fanPower1;
|
||||||
|
float fp2 = tSensorSettings.fanPower2;
|
||||||
|
|
||||||
|
if (hyst < 0.0f) hyst = 0.0f;
|
||||||
|
if (sp2 < sp1) {
|
||||||
|
// Ensure sp2 >= sp1
|
||||||
|
float tmp = sp1; sp1 = sp2; sp2 = tmp;
|
||||||
|
}
|
||||||
|
const float maxDuty = pwmOut->getMaxDuty();
|
||||||
|
fp1 = clampf(fp1, 0.0f, maxDuty);
|
||||||
|
fp2 = clampf(fp2, 0.0f, maxDuty);
|
||||||
|
|
||||||
|
// Fan State Logic
|
||||||
|
if ((FanState == 2) && (temperature < (sp2 - hyst))) {
|
||||||
|
newDuty = fp1;
|
||||||
|
FanState = 1;
|
||||||
|
ESP_LOGD(tag, "Dropping down to FanPower1");
|
||||||
|
}
|
||||||
|
else if ((FanState == 1) && (temperature < (sp1 - hyst))) {
|
||||||
|
newDuty = 0;
|
||||||
|
FanState = 0;
|
||||||
|
ESP_LOGD(tag, "Dropping down to FanPower0");
|
||||||
|
}
|
||||||
|
else if ((FanState <= 1) && (temperature > sp1)) {
|
||||||
|
newDuty = fp1;
|
||||||
|
if (temperature > sp2) {
|
||||||
|
newDuty = fp2;
|
||||||
|
FanState = 2;
|
||||||
|
ESP_LOGD(tag, "Raising up to FanPower2");
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(tag, "Raising up to FanPower1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply new duty cycle if changed
|
||||||
|
if (currentDuty != newDuty) {
|
||||||
|
pwmOut->setOutput(newDuty);
|
||||||
|
ESP_LOGI(tag, "Board T: %.2f F, Fan -> %.2f (state=%u)", temperature, newDuty, FanState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
@ -145,6 +145,12 @@ void Wifi_Load_Settings(String path)
|
|||||||
local_IP.fromString(jsonConstrainString(tag, apJson, "ip", "192.168.10.1"));
|
local_IP.fromString(jsonConstrainString(tag, apJson, "ip", "192.168.10.1"));
|
||||||
gateway.fromString(jsonConstrainString(tag, apJson, "gateway", "192.168.10.1"));
|
gateway.fromString(jsonConstrainString(tag, apJson, "gateway", "192.168.10.1"));
|
||||||
subnet.fromString(jsonConstrainString(tag, apJson, "subnet", "255.255.255.0"));
|
subnet.fromString(jsonConstrainString(tag, apJson, "subnet", "255.255.255.0"));
|
||||||
|
|
||||||
|
char macSuffix[13] = {0}; // Just need 2 chars + null terminator
|
||||||
|
get_chip_mac(macSuffix, sizeof(macSuffix)); // Only get first 2 chars of MAC
|
||||||
|
ap_ssid += "-"; // Add a separator
|
||||||
|
ap_ssid += macSuffix[0]; // Add first character
|
||||||
|
ap_ssid += macSuffix[1]; // Add second character
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load Client settings
|
// Load Client settings
|
||||||
|
|||||||
0
temporary/AppUpgrade copy.cpp
Normal file
0
temporary/AppUpgrade copy.cpp
Normal file
Loading…
x
Reference in New Issue
Block a user