Refactor code structure and clean up unused files
This commit is contained in:
parent
f7950c417f
commit
88c2ff3def
@ -104,7 +104,7 @@
|
||||
"size": 168,
|
||||
"chip": "SK6812",
|
||||
"rgb-order": "rgb",
|
||||
"shift":-52,
|
||||
"shift":-5,
|
||||
"offset": 0,
|
||||
"power-div": 0,
|
||||
"i2s-ch": 0,
|
||||
|
||||
@ -27,10 +27,11 @@ typedef struct{
|
||||
}BOARD_PINS;
|
||||
|
||||
extern BOARD_PINS* thisBoardPins;
|
||||
#define setStatusPin1(state) digitalWrite(thisBoardPins->stat[0], state);
|
||||
#define setStatusPin2(state) digitalWrite(thisBoardPins->stat[1], state);
|
||||
// Safe status pin macros: only write when configured (>=0)
|
||||
#define setStatusPin1(state) do { if (thisBoardPins && thisBoardPins->stat[0] >= 0) digitalWrite(thisBoardPins->stat[0], state); } while(0)
|
||||
#define setStatusPin2(state) do { if (thisBoardPins && thisBoardPins->stat[1] >= 0) digitalWrite(thisBoardPins->stat[1], state); } while(0)
|
||||
|
||||
void Load_Board_Pins(BOARD_PINS& boardPins, String& path);
|
||||
bool Load_Board_Pins(BOARD_PINS& boardPins, const String& path);
|
||||
void Init_Board_Basic(BOARD_PINS& boardPins);
|
||||
void updateFanControl(float temperature);
|
||||
void Initialize_Rear_Control(int relayIndex, int buttonIndex, int rampTime, int steps, float min, float max);
|
||||
|
||||
@ -4,11 +4,11 @@
|
||||
#include "OneButton.h"
|
||||
|
||||
extern OneButton *boardButtons[3];
|
||||
|
||||
#define Update_Buttons() boardButtons[1]->tick(); boardButtons[2]->tick(); boardButtons[3]->tick();
|
||||
|
||||
void Init_ButtonEvents(int8_t (&pin)[3]);
|
||||
|
||||
// Safely tick any initialized buttons (nullptr-aware)
|
||||
void Update_Buttons();
|
||||
|
||||
void btn1_click();
|
||||
void btn1_doubleClick();
|
||||
void btn1_LongPressStart();
|
||||
|
||||
@ -34,6 +34,6 @@ build_flags =
|
||||
-D CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=1
|
||||
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
-D CONFIG_ARDUHAL_LOG_COLORS=1
|
||||
upload_port = COM11
|
||||
upload_port = COM5
|
||||
debug_init_break = tbreak setup
|
||||
monitor_port = COM11
|
||||
monitor_port = COM5
|
||||
|
||||
@ -19,7 +19,7 @@ TaskHandle_t Animation_Task_Handle;
|
||||
LEDSTRIP_SETTINGS ledSettings[2];
|
||||
volatile bool AnimationLooping = false;
|
||||
ANIM_EVENT prevAnimEvent = {0};
|
||||
QueueHandle_t animationQueue = xQueueCreate( 1, 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){
|
||||
|
||||
@ -352,8 +352,12 @@ void Lights_Control_Task(void *parameters){
|
||||
ESP_LOGD(tag, "New Animation Event: Index: %d", AnimEvent.AnimationIndex);
|
||||
switch (AnimEvent.AnimationIndex) {
|
||||
case -3: // Set Pixel by index
|
||||
if (AnimEvent.data.data[7] >= 0 && AnimEvent.data.data[7] < ledSettings[0].size) {
|
||||
ledSettings[0].leds[AnimEvent.data.data[7]] = CRGB(AnimEvent.data.red, AnimEvent.data.grn, AnimEvent.data.blu);
|
||||
FastLED.show();
|
||||
} else {
|
||||
ESP_LOGW(tag, "Pixel index out of range: %d", AnimEvent.data.data[7]);
|
||||
}
|
||||
break;
|
||||
case -2: // Fill Static Color
|
||||
col = CRGB(AnimEvent.data.red, AnimEvent.data.grn, AnimEvent.data.blu);
|
||||
|
||||
@ -71,7 +71,16 @@ void Animation_Loop_Duration(bool volatile& loop_active_flag, int speed, TickTyp
|
||||
xLastWakeTime = xTaskGetTickCount();
|
||||
|
||||
// Call animation function
|
||||
int speedIncrease = callback(); // Call animation function
|
||||
int speedIncrease = 0;
|
||||
try {
|
||||
speedIncrease = callback(); // Call animation function
|
||||
} catch (const std::exception& e) {
|
||||
ESP_LOGE("Animation_Loop_Duration", "Callback exception: %s", e.what());
|
||||
break;
|
||||
} catch (...) {
|
||||
ESP_LOGE("Animation_Loop_Duration", "Callback unknown exception");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!loop_active_flag) return;
|
||||
|
||||
@ -94,11 +103,7 @@ void Animation_Loop_Duration(bool volatile& loop_active_flag, int speed, TickTyp
|
||||
TickType_t totalElapsed = xTaskGetTickCount() - startTicks;
|
||||
|
||||
if (totalElapsed >= durationMs) {
|
||||
while(loop_active_flag){
|
||||
if (ulTaskNotifyTake(pdTRUE, 50)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Auto-terminate the loop when duration reached
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -121,7 +126,16 @@ void Animation_Loop_Cycles(bool volatile& loop_active_flag, int speed, uint32_t
|
||||
for(;;) {
|
||||
xLastWakeTime = xTaskGetTickCount();
|
||||
|
||||
int speedIncrease = callback(); // Call animation function
|
||||
int speedIncrease = 0;
|
||||
try {
|
||||
speedIncrease = callback(); // Call animation function
|
||||
} catch (const std::exception& e) {
|
||||
ESP_LOGE("Animation_Loop_Cycles", "Callback exception: %s", e.what());
|
||||
break;
|
||||
} catch (...) {
|
||||
ESP_LOGE("Animation_Loop_Cycles", "Callback unknown exception");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!loop_active_flag) return;
|
||||
|
||||
@ -139,14 +153,8 @@ void Animation_Loop_Cycles(bool volatile& loop_active_flag, int speed, uint32_t
|
||||
// Delay and Check for termination request
|
||||
if (ulTaskNotifyTake(pdTRUE, delayTicks)) { break; }
|
||||
|
||||
// Check if cycles reached and wait for loop_active_flag
|
||||
// Check if cycles reached and exit
|
||||
if (loop_cycle_count >= loop_cycles) {
|
||||
while(loop_active_flag){
|
||||
if (ulTaskNotifyTake(pdTRUE, 50)) {
|
||||
//loop_active_flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -189,6 +197,7 @@ void Anim_Fire(bool volatile& activeFlag, CRGB* leds, int size, int speed, const
|
||||
|
||||
// Calculate half size for mirroring
|
||||
const int halfSize = size / 2;
|
||||
if (halfSize <= 0) return;
|
||||
|
||||
// Create heat array for half the size
|
||||
uint8_t* heat = new (std::nothrow) uint8_t[halfSize];
|
||||
@ -211,7 +220,8 @@ void Anim_Fire(bool volatile& activeFlag, CRGB* leds, int size, int speed, const
|
||||
|
||||
// Randomly ignite new sparks at bottom
|
||||
if(random8() < FIRE_SPARKING) {
|
||||
y = random8(7);
|
||||
// ensure y is in-bounds for small strips
|
||||
y = min<int>(random8(7), halfSize - 1);
|
||||
heat[y] = qadd8(heat[y], random8(160, 240));
|
||||
}
|
||||
|
||||
@ -382,11 +392,11 @@ void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PA
|
||||
// Set fade factor
|
||||
uint8_t fadeFactor = shorterTail ? COMET_FADE_FACTOR2 : COMET_FADE_FACTOR1;
|
||||
|
||||
// Initialize comet positions with fixed array
|
||||
// Initialize comet positions with fixed array, evenly distributed
|
||||
int cometPositions[MAX_COMETS] = {0};
|
||||
int spacing = size / totalComets;
|
||||
for (int i = 0; i < totalComets; i++) {
|
||||
cometPositions[i] = i * spacing;
|
||||
// Even distribution even when size not divisible by totalComets
|
||||
cometPositions[i] = (i * size) / totalComets;
|
||||
}
|
||||
|
||||
// Animation loop
|
||||
@ -414,9 +424,11 @@ void Anim_Comets(bool volatile& activeFlag, CRGB* leds, int size, const COLOR_PA
|
||||
}
|
||||
|
||||
// Draw comet with solid color
|
||||
colorPack.col[i % colorPack.size];
|
||||
color = colorPack.col[i % colorPack.size];
|
||||
for (int j = 0; j < cometSize; j++) {
|
||||
pos = (cometPositions[i] - j + size) % size;
|
||||
// Tail follows the direction of movement
|
||||
pos = direction ? (cometPositions[i] - j) : (cometPositions[i] + j);
|
||||
pos = (pos % size + size) % size; // safe modulus
|
||||
leds[pos] += color;
|
||||
}
|
||||
}
|
||||
@ -439,7 +451,7 @@ 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) {
|
||||
if (!leds || size <= 0 || totalDurationMs <= 0) return;
|
||||
if (!leds || size <= 1 || totalDurationMs <= 0) return;
|
||||
|
||||
const int halfSize = size / 2;
|
||||
const float msPerLed = totalDurationMs / (float)halfSize;
|
||||
@ -486,35 +498,35 @@ void Anim_ColorBreath(bool volatile& activeFlag, CRGB* leds, int size, const COL
|
||||
unsigned long startTime = millis();
|
||||
const uint32_t halfTime = timeMs / 2;
|
||||
|
||||
uint8_t origBright = FastLED.getBrightness();
|
||||
const uint8_t origBright = FastLED.getBrightness();
|
||||
FastLED.setBrightness(255);
|
||||
|
||||
uint8_t brightness = MIN_BRIGHTNESS;
|
||||
uint8_t breath = MIN_BRIGHTNESS;
|
||||
uint32_t elapsedTime;
|
||||
CRGB scaledColor, correctedColor;
|
||||
unsigned long currentTime;
|
||||
CRGB outColor;
|
||||
Animation_Loop(activeFlag, speed, [&]() -> int {
|
||||
// Calculate elapsed time in current breath cycle
|
||||
// Elapsed time in the current breath cycle
|
||||
currentTime = millis();
|
||||
elapsedTime = currentTime - startTime;
|
||||
|
||||
// Calculate brightness using a linear approach
|
||||
// Triangle wave brightness: up for half, down for half
|
||||
if (elapsedTime < halfTime) {
|
||||
brightness = map(elapsedTime, 0, halfTime, MIN_BRIGHTNESS, 255); // Brighten
|
||||
breath = map(elapsedTime, 0, halfTime, MIN_BRIGHTNESS, 255); // Brighten
|
||||
} else {
|
||||
brightness = map(elapsedTime, halfTime, timeMs, 255, MIN_BRIGHTNESS); // Dim
|
||||
breath = map(elapsedTime, halfTime, timeMs, 255, MIN_BRIGHTNESS); // Dim
|
||||
}
|
||||
|
||||
// Scale the color directly
|
||||
scaledColor = colors.col[colorIndex];
|
||||
scaledColor.nscale8(brightness * origBright / 255);
|
||||
// Combine breath with original global brightness (rounded)
|
||||
uint16_t prod = static_cast<uint16_t>(breath) * static_cast<uint16_t>(origBright);
|
||||
uint8_t finalBright = static_cast<uint8_t>((prod + 127) / 255);
|
||||
|
||||
// Correct the color scale for vision
|
||||
correctedColor = scaledColor;
|
||||
correctedColor.nscale8_video(brightness);
|
||||
// Apply perceptual scaling once with combined brightness
|
||||
outColor = colors.col[colorIndex];
|
||||
outColor.nscale8_video(finalBright);
|
||||
|
||||
// Fill all LEDs with scaled color
|
||||
fill_solid(leds, size, correctedColor);
|
||||
fill_solid(leds, size, outColor);
|
||||
FastLED.show();
|
||||
|
||||
if (elapsedTime >= timeMs) {
|
||||
@ -534,21 +546,17 @@ void Anim_GradientRotate(bool volatile& activeFlag, CRGB* leds, int size, const
|
||||
|
||||
CRGB color1, color2;
|
||||
|
||||
// Create initial gradient
|
||||
int segmentLength = size / colors.size;
|
||||
for(int i = 0; i < size; i++) {
|
||||
// Determine which color segment we're in
|
||||
int segment = i / segmentLength;
|
||||
// Create initial gradient: evenly distribute blends across the strip
|
||||
// For i in [0..size-1], compute position in color space [0..colors.size)
|
||||
for (int i = 0; i < size; i++) {
|
||||
uint32_t pos256 = (uint32_t)i * (uint32_t)colors.size * 256u / (uint32_t)size; // 8.8 fixed point
|
||||
int segment = (int)(pos256 >> 8); // 0..colors.size-1
|
||||
uint8_t blendPos = (uint8_t)(pos256 & 0xFF); // 0..255
|
||||
int nextSegment = (segment + 1) % colors.size;
|
||||
|
||||
// Calculate blend amount within segment
|
||||
uint8_t blendPos = map(i % segmentLength, 0, segmentLength - 1, 0, 255);
|
||||
|
||||
// Create local copies for blending
|
||||
// Local copies for blending
|
||||
color1 = colors.col[segment];
|
||||
color2 = colors.col[nextSegment];
|
||||
|
||||
// Set initial gradient
|
||||
leds[i] = blend(color1, color2, blendPos);
|
||||
}
|
||||
|
||||
|
||||
@ -4,8 +4,10 @@
|
||||
#include <LittleFS.h>
|
||||
#include <memory>
|
||||
#include "global.h"
|
||||
#include "jsonConstrain.h"
|
||||
#include "JsonConstrain.h"
|
||||
#include "BLE_UpdateService.h"
|
||||
#include <HTTPClient.h>
|
||||
#include <Update.h>
|
||||
|
||||
static const char* TAG = "AppUpdater";
|
||||
TaskHandle_t Update_Task_Handle = NULL;
|
||||
@ -90,7 +92,8 @@ bool AppUpdater::checkManifest() {
|
||||
|
||||
// Check if an update is available
|
||||
updateAvailable = false;
|
||||
if (otaVersion < localVersion) {
|
||||
// Only mark update available if remote is strictly newer than local
|
||||
if (otaVersion <= localVersion) {
|
||||
ESP_LOGI(TAG, "No updates available");
|
||||
return false;
|
||||
}else{
|
||||
@ -163,7 +166,8 @@ bool AppUpdater::verifyAndSaveFile(WiFiClient* stream, size_t contentLength, con
|
||||
|
||||
//updateProgress(UpdateStatus::DOWNLOADING, 0, localPath);
|
||||
|
||||
// Single pass: Save file and calculate MD5
|
||||
if (contentLength > 0) {
|
||||
// Single pass with known content length
|
||||
while (totalRead < contentLength) {
|
||||
size_t available = stream->available();
|
||||
if (available) {
|
||||
@ -184,6 +188,26 @@ bool AppUpdater::verifyAndSaveFile(WiFiClient* stream, size_t contentLength, con
|
||||
}
|
||||
yield();
|
||||
}
|
||||
} else {
|
||||
// Unknown content length: read until stream ends
|
||||
for (;;) {
|
||||
size_t readLen = stream->readBytes(downloadBuffer.get(), BUFFER_SIZE);
|
||||
if (readLen == 0) {
|
||||
break;
|
||||
}
|
||||
if (file.write(downloadBuffer.get(), readLen) != readLen) {
|
||||
ESP_LOGE(TAG, "Failed to write to temporary file");
|
||||
file.close();
|
||||
fileSystem.remove(tempPath.c_str());
|
||||
return false;
|
||||
}
|
||||
md5.add(downloadBuffer.get(), readLen);
|
||||
totalRead += readLen;
|
||||
// Progress unknown; emit periodic heartbeats at 0%
|
||||
updateProgress(UpdateStatus::DOWNLOADING, 0, localPath);
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
md5.calculate();
|
||||
@ -286,7 +310,7 @@ bool AppUpdater::updateApp() {
|
||||
|
||||
// Check available space
|
||||
size_t firmwareSize = http.getSize();
|
||||
if (!Update.begin(firmwareSize)) {
|
||||
if (!Update.begin(firmwareSize > 0 ? firmwareSize : UPDATE_SIZE_UNKNOWN)) {
|
||||
ESP_LOGE(TAG, "Firmware: Not enough space for update");
|
||||
updateProgress(UpdateStatus::ERROR, 0, "Firmware: Not enough space for update");
|
||||
http.end();
|
||||
@ -299,6 +323,7 @@ bool AppUpdater::updateApp() {
|
||||
|
||||
// Download and verify firmware
|
||||
WiFiClient* stream = http.getStreamPtr();
|
||||
if (firmwareSize > 0) {
|
||||
size_t remaining = firmwareSize;
|
||||
while (remaining > 0) {
|
||||
size_t chunk = std::min(remaining, size_t(BUFFER_SIZE));
|
||||
@ -307,7 +332,6 @@ bool AppUpdater::updateApp() {
|
||||
// Check for timeout
|
||||
if (read == 0) {
|
||||
ESP_LOGE(TAG, "Read timeout");
|
||||
|
||||
Update.abort();
|
||||
http.end();
|
||||
return false;
|
||||
@ -325,6 +349,21 @@ bool AppUpdater::updateApp() {
|
||||
remaining -= read;
|
||||
updateProgress(UpdateStatus::DOWNLOADING, (firmwareSize - remaining) * 100 / firmwareSize, "firmware");
|
||||
}
|
||||
} else {
|
||||
// Unknown size: stream until end
|
||||
for (;;) {
|
||||
size_t read = stream->readBytes(downloadBuffer.get(), BUFFER_SIZE);
|
||||
if (read == 0) break;
|
||||
md5.add(downloadBuffer.get(), read);
|
||||
if (Update.write(downloadBuffer.get(), read) != read) {
|
||||
ESP_LOGE(TAG, "Write failed");
|
||||
Update.abort();
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
updateProgress(UpdateStatus::DOWNLOADING, 0, "firmware");
|
||||
}
|
||||
}
|
||||
|
||||
// Verify MD5
|
||||
md5.calculate();
|
||||
@ -469,6 +508,7 @@ void updateProgress(AppUpdater::UpdateStatus newStatus, int percentage, const ch
|
||||
const char* msg;
|
||||
bool isComplete = false;
|
||||
|
||||
const char* safeMsg = message ? message : "";
|
||||
switch (newStatus) {
|
||||
case AppUpdater::UpdateStatus::IDLE:
|
||||
snprintf(buffer, sizeof(buffer), "Update idle");
|
||||
@ -478,23 +518,23 @@ void updateProgress(AppUpdater::UpdateStatus newStatus, int percentage, const ch
|
||||
msg = message ? message : "";
|
||||
break;
|
||||
case AppUpdater::UpdateStatus::DOWNLOADING:
|
||||
snprintf(buffer, sizeof(buffer), "%s: Download progress: %d%%", message, percentage);
|
||||
snprintf(buffer, sizeof(buffer), "%s: Download progress: %d%%", safeMsg, percentage);
|
||||
msg = buffer;
|
||||
break;
|
||||
case AppUpdater::UpdateStatus::VERIFYING:
|
||||
snprintf(buffer, sizeof(buffer), "%s: Verifying update: %d%%", message, percentage);
|
||||
snprintf(buffer, sizeof(buffer), "%s: Verifying update: %d%%", safeMsg, percentage);
|
||||
msg = buffer;
|
||||
break;
|
||||
case AppUpdater::UpdateStatus::FILE_SKIPPED:
|
||||
snprintf(buffer, sizeof(buffer), "%s: Skipping file update, already up to date", message);
|
||||
snprintf(buffer, sizeof(buffer), "%s: Skipping file update, already up to date", safeMsg);
|
||||
msg = buffer;
|
||||
break;
|
||||
case AppUpdater::UpdateStatus::FILE_SAVED:
|
||||
snprintf(buffer, sizeof(buffer), "%s: File Saved", message);
|
||||
snprintf(buffer, sizeof(buffer), "%s: File Saved", safeMsg);
|
||||
msg = buffer;
|
||||
break;
|
||||
case AppUpdater::UpdateStatus::MD5_FAILED:
|
||||
snprintf(buffer, sizeof(buffer), "%s: MD5 Verification Failed", message);
|
||||
snprintf(buffer, sizeof(buffer), "%s: MD5 Verification Failed", safeMsg);
|
||||
msg = buffer;
|
||||
break;
|
||||
case AppUpdater::UpdateStatus::COMPLETE:
|
||||
@ -503,7 +543,7 @@ void updateProgress(AppUpdater::UpdateStatus newStatus, int percentage, const ch
|
||||
isComplete = true;
|
||||
break;
|
||||
case AppUpdater::UpdateStatus::ERROR:
|
||||
snprintf(buffer, sizeof(buffer), "Error!: %s", message);
|
||||
snprintf(buffer, sizeof(buffer), "Error!: %s", safeMsg);
|
||||
msg = buffer;
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -55,7 +55,11 @@ class SP110ECallbacks : public NimBLECharacteristicCallbacks {
|
||||
std::string rawValue = pCharacteristic->getValue();
|
||||
const uint8_t* value = reinterpret_cast<const uint8_t*>(rawValue.data());
|
||||
size_t length = rawValue.length();
|
||||
if (length >= 3) {
|
||||
ESP_LOGI(tag, "Data received 0x%02X, 0x%02X, 0x%02X (length %zu):", value[0], value[1], value[2], length);
|
||||
} else {
|
||||
ESP_LOGI(tag, "Data received (length %zu)", length);
|
||||
}
|
||||
|
||||
sendToAllClients(value, length);
|
||||
process_BLE_SP110E_Command(value, length, pCharacteristic);
|
||||
|
||||
@ -35,20 +35,36 @@ class UpgradeChar_Callbacks : public NimBLECharacteristicCallbacks {
|
||||
ESP_LOGD(tag, "Upgrade Char written with value: %s", value.c_str());
|
||||
|
||||
if (value.compare(0, 12, "wifi-connect") == 0) { // Update WiFi credentials
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, value.substr(13));
|
||||
// Expecting: "wifi-connect:{\"ssid\":...,\"pass\":...}"
|
||||
size_t jsonStart = (value.size() > 12 && value[12] == ':') ? 13 : 12;
|
||||
if (value.size() <= jsonStart) {
|
||||
ESP_LOGW(tag, "wifi-connect command missing JSON payload");
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(512);
|
||||
DeserializationError err = deserializeJson(doc, value.substr(jsonStart));
|
||||
if (err) {
|
||||
ESP_LOGW(tag, "JSON parse error for wifi-connect: %s", err.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject wifiJson = doc.as<JsonObject>();
|
||||
String ssid = wifiJson["ssid"].as<String>();
|
||||
String pass = wifiJson["pass"].as<String>();
|
||||
ESP_LOGI(tag, "Wifi Credentials: %s, %s", ssid.c_str(), pass.c_str());
|
||||
if (ssid.length() == 0) {
|
||||
ESP_LOGW(tag, "wifi-connect missing ssid");
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(tag, "WiFi connect requested: ssid='%s', pass len=%u", ssid.c_str(), (unsigned)pass.length());
|
||||
|
||||
bool status = StartWifiConnectTask(ssid, pass);
|
||||
if(status == true){
|
||||
bool started = StartWifiConnectTask(ssid, pass);
|
||||
if (started) {
|
||||
updatePacket.wifiStatus = WIFI_DISCONNECTED;
|
||||
updatePacket.wifiOnline = false;
|
||||
updatePacket.wifiIP[0] = updatePacket.wifiIP[1] = updatePacket.wifiIP[2] = updatePacket.wifiIP[3] = 0;
|
||||
}else{
|
||||
ESP_LOGI(tag, "Failed to start WiFi connection task");
|
||||
} else {
|
||||
ESP_LOGW(tag, "Failed to start WiFi connection task");
|
||||
}
|
||||
}
|
||||
else if (value.compare("version-check") == 0) { // Check if new version is available
|
||||
@ -73,23 +89,20 @@ class UpgradeChar_Callbacks : public NimBLECharacteristicCallbacks {
|
||||
|
||||
void onRead(NimBLECharacteristic *pCharacteristic) override {
|
||||
updatePacket.wifiOnline = InternetAvailable;
|
||||
if(WiFi.status() == WL_CONNECTED){
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
updatePacket.wifiStatus = WIFI_CONNECTED;
|
||||
if(updatePacket.wifiIP[0] == 0){
|
||||
updatePacket.wifiIP[0] = WiFi.localIP()[0];
|
||||
updatePacket.wifiIP[1] = WiFi.localIP()[1];
|
||||
updatePacket.wifiIP[2] = WiFi.localIP()[2];
|
||||
updatePacket.wifiIP[3] = WiFi.localIP()[3];
|
||||
}
|
||||
}else{
|
||||
IPAddress ip = WiFi.localIP();
|
||||
updatePacket.wifiIP[0] = ip[0];
|
||||
updatePacket.wifiIP[1] = ip[1];
|
||||
updatePacket.wifiIP[2] = ip[2];
|
||||
updatePacket.wifiIP[3] = ip[3];
|
||||
} else {
|
||||
updatePacket.wifiStatus = WIFI_DISCONNECTED;
|
||||
if(updatePacket.wifiIP[0] > 0){
|
||||
updatePacket.wifiIP[0] = 0;
|
||||
updatePacket.wifiIP[1] = 0;
|
||||
updatePacket.wifiIP[2] = 0;
|
||||
updatePacket.wifiIP[3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//update version
|
||||
if(otaVersion.major() != 0){
|
||||
@ -99,19 +112,24 @@ class UpgradeChar_Callbacks : public NimBLECharacteristicCallbacks {
|
||||
updatePacket.newVersion[2] = otaVersion.patch();
|
||||
}
|
||||
|
||||
// Only populate the control characteristic with the status packet
|
||||
if (pCharacteristic == pUpgradeCharacteristic1) {
|
||||
pCharacteristic->setValue(reinterpret_cast<uint8_t*>(&updatePacket), sizeof(updatePacket));
|
||||
ESP_LOGI(tag, "Upgrade Char read");
|
||||
ESP_LOGI(tag, "Upgrade status read");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void bleUpgrade_send_message(String s){
|
||||
if(pUpgradeCharacteristic2){
|
||||
if (s != nullptr) {
|
||||
pUpgradeCharacteristic2->setValue(s);
|
||||
if (s.length() == 0) {
|
||||
return;
|
||||
}
|
||||
// Set value and notify only if there are subscribers to avoid unnecessary work
|
||||
pUpgradeCharacteristic2->setValue(s.c_str());
|
||||
if (pUpgradeCharacteristic2->getSubscribedCount() > 0) {
|
||||
pUpgradeCharacteristic2->notify();
|
||||
} else {
|
||||
ESP_LOGW(tag, "Null string passed to bleUpgrade_send_message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,18 +13,22 @@ class ServerCallbacks : public NimBLEServerCallbacks {
|
||||
// Ensure advertising remains active even after a client connects
|
||||
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
|
||||
if (pAdvertising != nullptr) {
|
||||
if (!pAdvertising->isAdvertising()) {
|
||||
pAdvertising->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onDisconnect(NimBLEServer* pServer) override {
|
||||
ESP_LOGI(tag, "Client disconnected");
|
||||
// Restart advertising on disconnect to keep it active always
|
||||
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
|
||||
if (pAdvertising != nullptr) {
|
||||
if (!pAdvertising->isAdvertising()) {
|
||||
pAdvertising->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void Init_BleServer( bool isSP110EActive, bool isUpgradeActive) {
|
||||
@ -35,6 +39,7 @@ void Init_BleServer( bool isSP110EActive, bool isUpgradeActive) {
|
||||
//NimBLEDevice::init("ATALIGHTS");
|
||||
NimBLEDevice::init("SP110E-ATA");
|
||||
//NimBLEDevice::setMTU(247); // Set preferred MTU size (max 247 for BLE)
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
NimBLEServer *pServer = NimBLEDevice::createServer();
|
||||
@ -59,7 +64,7 @@ void Init_BleServer( bool isSP110EActive, bool isUpgradeActive) {
|
||||
// Start BLE advertising
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
if (pAdvertising != nullptr) {
|
||||
if (!pAdvertising->start()) {
|
||||
if (!pAdvertising->isAdvertising() && !pAdvertising->start()) {
|
||||
ESP_LOGE(tag, "Failed to start advertising");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3,7 +3,10 @@
|
||||
#include <esp_log.h>
|
||||
|
||||
template <typename T>
|
||||
void logConstrainedValue(const char* tag, const char* key, T value);
|
||||
void logConstrainedValue(const char* tag, const char* key, T) {
|
||||
// Generic fallback when no specialization provided
|
||||
ESP_LOGD(tag, "Key [%s] value set", key);
|
||||
}
|
||||
|
||||
template <>
|
||||
void logConstrainedValue<int>(const char* tag, const char* key, int value) {
|
||||
@ -16,7 +19,10 @@ void logConstrainedValue<float>(const char* tag, const char* key, float value) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void logClamping(const char* tag, const char* key, T value, T limit, const char* condition);
|
||||
void logClamping(const char* tag, const char* key, T, T, const char* condition) {
|
||||
// Generic fallback when no specialization provided
|
||||
ESP_LOGW(tag, "Key [%s] value too %s. Clamping.", key, condition);
|
||||
}
|
||||
|
||||
template <>
|
||||
void logClamping<int>(const char* tag, const char* key, int value, int limit, const char* condition) {
|
||||
@ -72,12 +78,12 @@ const char* jsonConstrainChar(const char *tag, const JsonObject &jsonObject, con
|
||||
}
|
||||
|
||||
String value = jsonObject[key].as<String>();
|
||||
if (value.isEmpty()) {
|
||||
if (value.length() == 0) {
|
||||
ESP_LOGW(tag, "Key [%s] value is empty. Using default value.", key);
|
||||
return strdup(def);
|
||||
}
|
||||
|
||||
ESP_LOGD(tag, "Key [%s] value: %s", key, value);
|
||||
ESP_LOGD(tag, "Key [%s] value: %s", key, value.c_str());
|
||||
return strdup(value.c_str());
|
||||
}
|
||||
|
||||
@ -93,7 +99,7 @@ String jsonConstrainString(const char *tag, const JsonObject &jsonObject, const
|
||||
String value = jsonObject[key].as<String>();
|
||||
|
||||
// Check if the value is empty
|
||||
if (value.isEmpty()) {
|
||||
if (value.length() == 0) {
|
||||
ESP_LOGW(tag, "Key [%s] value is empty. Using default value [%s].", key, def.c_str());
|
||||
return def;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "PWM_Output.h"
|
||||
#include <Arduino.h>
|
||||
#include "global.h"
|
||||
|
||||
|
||||
const char* tag = "pwmout";
|
||||
@ -56,6 +57,11 @@ void PWM_Output::setOutput(float duty){
|
||||
outDutyVal = static_cast<int>(duty * this->standardFactor);
|
||||
}
|
||||
|
||||
// Clamp to valid resolution range [0, 2^res - 1]
|
||||
int maxVal = static_cast<int>(binaryPow[this->res]);
|
||||
if (outDutyVal < 0) outDutyVal = 0;
|
||||
if (outDutyVal > maxVal) outDutyVal = maxVal;
|
||||
|
||||
ledcWrite(this->ch, outDutyVal);
|
||||
this->currOutVal = outDutyVal;
|
||||
this->currDuty = duty;
|
||||
@ -77,8 +83,9 @@ void PWM_Output::setResolution(uint8_t res){
|
||||
if(this->res < 4) this->res = 4;
|
||||
if(this->res > 16) this->res = 16;
|
||||
|
||||
this->standardFactor = binaryPow[res] * 0.01;
|
||||
this->visionFactor = binaryPow[res] * 0.0001;
|
||||
// Use the clamped resolution when computing factors
|
||||
this->standardFactor = binaryPow[this->res] * 0.01f;
|
||||
this->visionFactor = binaryPow[this->res] * 0.0001f;
|
||||
ESP_LOGD(tag, "factor=%f, vision=%f", this->standardFactor, this->visionFactor);
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,14 @@
|
||||
|
||||
#define MAX_HUE 360.0
|
||||
|
||||
// Normalize a hue into [0, MAX_HUE)
|
||||
static inline float wrapHue(float h)
|
||||
{
|
||||
while (h >= MAX_HUE) h -= MAX_HUE;
|
||||
while (h < 0.0f) h += MAX_HUE;
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
/************************* CLASS - HUE PALLET DISPENSER ************************/
|
||||
|
||||
@ -11,59 +19,51 @@ HUE_PALLET_DISPENSER::HUE_PALLET_DISPENSER(void){
|
||||
}
|
||||
|
||||
void HUE_PALLET_DISPENSER::Initialize(float hue, float range, int colSteps){
|
||||
this->range = range;
|
||||
this->hueSteps = colSteps;
|
||||
|
||||
this->startHue = hue - this->range / 2;
|
||||
if(this->startHue < 0.0){
|
||||
this->startHue += MAX_HUE;
|
||||
}else if(this->startHue > MAX_HUE){
|
||||
this->startHue -= MAX_HUE;
|
||||
}
|
||||
// Clamp and normalize inputs
|
||||
this->range = constrain(range, 0.0f, (float)MAX_HUE);
|
||||
this->hueSteps = (colSteps < 0) ? 0 : colSteps;
|
||||
|
||||
this->startHue = wrapHue(hue - (this->range * 0.5f));
|
||||
this->hueIndex = 0;
|
||||
|
||||
if(this->hueSteps <= 1){
|
||||
this->hueIncrement = 0.0;
|
||||
this->currHue = hue;
|
||||
}else{
|
||||
this->hueIncrement = this->range / (this->hueSteps - 1);
|
||||
if (this->hueSteps <= 1) {
|
||||
this->hueIncrement = 0.0f;
|
||||
this->currHue = wrapHue(hue);
|
||||
} else {
|
||||
this->hueIncrement = (this->range / (this->hueSteps - 1));
|
||||
this->currHue = this->startHue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int HUE_PALLET_DISPENSER::GetNextPalletHue(void){
|
||||
if(this->hueSteps > 1){
|
||||
this->currHue = this->startHue + this->hueIndex * this->hueIncrement;
|
||||
if(this->currHue < 0){
|
||||
this->currHue += MAX_HUE;
|
||||
}else if(this->currHue > MAX_HUE){
|
||||
this->currHue -= MAX_HUE;
|
||||
if (this->hueSteps > 1) {
|
||||
this->currHue = wrapHue(this->startHue + (this->hueIndex * this->hueIncrement));
|
||||
this->hueIndex = (this->hueIndex + 1) % this->hueSteps;
|
||||
}
|
||||
|
||||
// TODO Remove later
|
||||
//rgbpixel_t p = HUEtoRGB(huePallet.currHue);
|
||||
//Log.traceln("<anim> index: %d, hue= %F, col: %d, %d, %d", huePallet.hueIndex, huePallet.currHue, p.red, p.grn, p.blu);
|
||||
|
||||
this->hueIndex = ++this->hueIndex % this->hueSteps;
|
||||
return round(this->currHue);
|
||||
}else{
|
||||
return round(this->currHue);
|
||||
}
|
||||
// Convert to integer hue in [0, 359]
|
||||
int out = (int)(this->currHue + 0.5f);
|
||||
if (out >= (int)MAX_HUE) out -= (int)MAX_HUE;
|
||||
if (out < 0) out = 0; // safety
|
||||
return out;
|
||||
}
|
||||
|
||||
float HUE_PALLET_DISPENSER::PeekNextPalletHue(int hueOffset){
|
||||
float tempHue = this->startHue + (this->hueIndex + hueOffset) * this->hueIncrement;
|
||||
|
||||
if(tempHue < 0){
|
||||
tempHue += MAX_HUE;
|
||||
}else if(tempHue > MAX_HUE){
|
||||
tempHue -= MAX_HUE;
|
||||
}
|
||||
|
||||
float tempHue = wrapHue(this->startHue + ((this->hueIndex + hueOffset) * this->hueIncrement));
|
||||
return tempHue;
|
||||
}
|
||||
|
||||
void HUE_PALLET_DISPENSER::SetHueIndex(int hueIndex){
|
||||
this->currHue = hueIndex;
|
||||
if (this->hueSteps > 0) {
|
||||
int n = this->hueSteps;
|
||||
int idx = hueIndex % n;
|
||||
if (idx < 0) idx += n;
|
||||
this->hueIndex = idx;
|
||||
if (this->hueSteps > 1) {
|
||||
this->currHue = wrapHue(this->startHue + (this->hueIndex * this->hueIncrement));
|
||||
}
|
||||
} else {
|
||||
this->hueIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include <Wifi.h>
|
||||
#include <WiFi.h>
|
||||
#include <FS.h>
|
||||
#include <LittleFS.h>
|
||||
#include <ArduinoJson.h>
|
||||
@ -257,6 +257,12 @@ void Log_CPU_Load(void) {
|
||||
|
||||
// Get task stats
|
||||
task_count = uxTaskGetSystemState(task_array, task_count, &total_runtime);
|
||||
if (total_runtime == 0) {
|
||||
ESP_LOGW(tag, "Runtime stats not ready yet (total_runtime==0)");
|
||||
free(stats);
|
||||
free(task_array);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find IDLE tasks
|
||||
for (UBaseType_t i = 0; i < task_count; i++) {
|
||||
|
||||
@ -93,8 +93,7 @@ void setup()
|
||||
{
|
||||
// Serial Port
|
||||
Serial.begin(115200);
|
||||
while (!Serial)
|
||||
;
|
||||
while (!Serial);
|
||||
|
||||
// Initialize I2C Port for TSensor, ...
|
||||
Wire.begin(I2C_SDA1_Pin, I2C_SCL1_Pin);
|
||||
@ -281,7 +280,7 @@ void loop()
|
||||
{
|
||||
static bool ledState = false;
|
||||
// digitalWrite(sys_settings.boardPins.stat[1], ledState = !ledState);
|
||||
setStatusPin2(ledState = !ledState)
|
||||
setStatusPin2(ledState = !ledState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,13 +12,29 @@ static const char* tag = "board";
|
||||
|
||||
BOARD_PINS* thisBoardPins;
|
||||
|
||||
void Load_Board_Pins(BOARD_PINS& boardPins, String& path){
|
||||
// Basic validator for ESP32-S3 GPIOs; rejects common reserved/USB/strap pins
|
||||
static bool isValidGpio(int pin) {
|
||||
if (pin < 0 || pin > 48) return false;
|
||||
switch (pin) {
|
||||
case 19: // USB D-
|
||||
case 20: // USB D+
|
||||
case 45: // strapping
|
||||
case 46: // strapping
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Load_Board_Pins(BOARD_PINS& boardPins, const String& path){
|
||||
// Default initialize to -1 to avoid stale values on partial loads
|
||||
memset(&boardPins, -1, sizeof(boardPins));
|
||||
thisBoardPins = &boardPins;
|
||||
File file = LittleFS.open(path);
|
||||
|
||||
if (!file) {
|
||||
ESP_LOGE(tag, "Error opening %s...", path.c_str());
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
@ -27,7 +43,7 @@ void Load_Board_Pins(BOARD_PINS& boardPins, String& path){
|
||||
|
||||
if (error) {
|
||||
ESP_LOGE(tag, "%s deserialize error!..", path.c_str());
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonObject boardJson = doc.as<JsonObject>();
|
||||
@ -60,9 +76,28 @@ void Load_Board_Pins(BOARD_PINS& boardPins, String& path){
|
||||
boardPins.rf433tx = jsonConstrain<int>(tag, boardJson, "rf433tx", -1, 48, -1);
|
||||
boardPins.rf433rx = jsonConstrain<int>(tag, boardJson, "rf433rx", -1, 48, -1);
|
||||
|
||||
// TODO Add hardware version to log
|
||||
ESP_LOGI(tag, "loaded Pins from %s", path.c_str());
|
||||
// Validate pins against reserved GPIOs
|
||||
auto clampPin = [](int v){ return isValidGpio(v) ? v : -1; };
|
||||
boardPins.rgb1 = clampPin(boardPins.rgb1);
|
||||
boardPins.rgb2 = clampPin(boardPins.rgb2);
|
||||
for (int i=0;i<3;i++) boardPins.btn[i] = clampPin(boardPins.btn[i]);
|
||||
boardPins.buzzer = clampPin(boardPins.buzzer);
|
||||
for (int i=0;i<5;i++) boardPins.touch[i] = clampPin(boardPins.touch[i]);
|
||||
boardPins.shield = clampPin(boardPins.shield);
|
||||
for (int i=0;i<4;i++) boardPins.relay[i] = clampPin(boardPins.relay[i]);
|
||||
for (int i=0;i<2;i++) boardPins.stat[i] = clampPin(boardPins.stat[i]);
|
||||
boardPins.adc1 = clampPin(boardPins.adc1);
|
||||
boardPins.oled_dc = clampPin(boardPins.oled_dc);
|
||||
boardPins.oled_rst = clampPin(boardPins.oled_rst);
|
||||
boardPins.oled_mosi = clampPin(boardPins.oled_mosi);
|
||||
boardPins.oled_sck = clampPin(boardPins.oled_sck);
|
||||
boardPins.oled_cs = clampPin(boardPins.oled_cs);
|
||||
for (int i=0;i<2;i++) boardPins.ext[i] = clampPin(boardPins.ext[i]);
|
||||
boardPins.rf433tx = clampPin(boardPins.rf433tx);
|
||||
boardPins.rf433rx = clampPin(boardPins.rf433rx);
|
||||
|
||||
ESP_LOGI(tag, "loaded Pins from %s", path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -8,17 +8,29 @@ OneButton *boardButtons[3];
|
||||
void Init_ButtonEvents(int8_t (&pin)[3]){
|
||||
|
||||
|
||||
if(pin[0] >= 0) {
|
||||
if (pin[0] >= 0) {
|
||||
if (boardButtons[0] == nullptr) {
|
||||
boardButtons[0] = new OneButton(pin[0], true, true);
|
||||
ESP_LOGD(tag, "Button1 Events, pin=%d", pin[0]);
|
||||
} else {
|
||||
ESP_LOGD(tag, "Button1 already initialized (pin=%d)", pin[0]);
|
||||
}
|
||||
if(pin[1] >= 0) {
|
||||
}
|
||||
if (pin[1] >= 0) {
|
||||
if (boardButtons[1] == nullptr) {
|
||||
boardButtons[1] = new OneButton(pin[1], true, true);
|
||||
ESP_LOGD(tag, "Button2 Events, pin=%d", pin[1]);
|
||||
} else {
|
||||
ESP_LOGD(tag, "Button2 already initialized (pin=%d)", pin[1]);
|
||||
}
|
||||
if(pin[2] >= 0) {
|
||||
}
|
||||
if (pin[2] >= 0) {
|
||||
if (boardButtons[2] == nullptr) {
|
||||
boardButtons[2] = new OneButton(pin[2], true, false);
|
||||
ESP_LOGD(tag, "Button3 Events, pin=%d", pin[2]);
|
||||
} else {
|
||||
ESP_LOGD(tag, "Button3 already initialized (pin=%d)", pin[2]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -62,6 +74,14 @@ void Init_ButtonEvents(int8_t (&pin)[3]){
|
||||
|
||||
}
|
||||
|
||||
void Update_Buttons() {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (boardButtons[i] != nullptr) {
|
||||
boardButtons[i]->tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void btn1_click() {
|
||||
//IncrementEventIndex();
|
||||
//Pulse_LED_Status(150);
|
||||
|
||||
@ -1,38 +1,77 @@
|
||||
#include "my_tsensor.h"
|
||||
#include <Temperature_LM75_Derived.h>
|
||||
#include "global.h"
|
||||
|
||||
static const char* tag = "tsensor";
|
||||
|
||||
T_SENSOR tSensorSettings;
|
||||
TI_TMP102_Compatible *tSensor;
|
||||
TI_TMP102_Compatible *tSensor = nullptr;
|
||||
|
||||
/******************* Temperature Control ********************/
|
||||
void Init_TSensor(uint8_t addr) {
|
||||
//tSensor = new TI_TMP102_Compatible(72);
|
||||
// Initialize the temperature sensor once with the provided I2C address
|
||||
if (tSensor == nullptr) {
|
||||
tSensor = new TI_TMP102_Compatible(addr);
|
||||
ESP_LOGI(tag, "TSensor initialized at I2C addr 0x%02X", addr);
|
||||
} else {
|
||||
ESP_LOGW(tag, "TSensor already initialized; ignoring re-init request (addr 0x%02X)", addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline float clampf(float v, float lo, float hi) {
|
||||
if (v < lo) return lo;
|
||||
if (v > hi) return hi;
|
||||
return v;
|
||||
}
|
||||
|
||||
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 < (tSensorSettings.Setpoint2 - tSensorSettings.hyst))) {
|
||||
newDuty = tSensorSettings.fanPower1;
|
||||
if ((FanState == 2) && (temperature < (sp2 - hyst))) {
|
||||
newDuty = fp1;
|
||||
FanState = 1;
|
||||
//ESP_LOGD(tag, "Dropping down to FanPower1");
|
||||
}
|
||||
else if ((FanState == 1) && (temperature < (tSensorSettings.Setpoint1 - tSensorSettings.hyst))) {
|
||||
else if ((FanState == 1) && (temperature < (sp1 - hyst))) {
|
||||
newDuty = 0;
|
||||
FanState = 0;
|
||||
//ESP_LOGD(tag, "Dropping down to FanPower0");
|
||||
}
|
||||
else if ((FanState <= 1) && (temperature > tSensorSettings.Setpoint1)) {
|
||||
newDuty = tSensorSettings.fanPower1;
|
||||
if (temperature > tSensorSettings.Setpoint2) {
|
||||
newDuty = tSensorSettings.fanPower2;
|
||||
else if ((FanState <= 1) && (temperature > sp1)) {
|
||||
newDuty = fp1;
|
||||
if (temperature > sp2) {
|
||||
newDuty = fp2;
|
||||
FanState = 2;
|
||||
//ESP_LOGD(tag, "Raising up to FanPower2");
|
||||
} //else {
|
||||
@ -43,6 +82,6 @@ TI_TMP102_Compatible *tSensor;
|
||||
// Apply new duty cycle if changed
|
||||
if (currentDuty != newDuty) {
|
||||
pwmOut->setOutput(newDuty);
|
||||
ESP_LOGD(tag, "Board T: %.2f, Fan -> %.2f", temperature, newDuty);
|
||||
ESP_LOGD(tag, "Board T: %.2f F, Fan -> %.2f (state=%u)", temperature, newDuty, FanState);
|
||||
}
|
||||
}
|
||||
@ -1036,37 +1036,81 @@ bool writeFile(fs::FS &fs, const char *path, const char *message)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open file with error checking
|
||||
File file = fs.open(path, "w");
|
||||
if (!file)
|
||||
// Normalize and validate path
|
||||
String finalPath(path);
|
||||
if (!finalPath.startsWith("/"))
|
||||
{
|
||||
ESP_LOGE(tag, "Failed to open file: %s", path);
|
||||
finalPath = String("/") + finalPath;
|
||||
}
|
||||
// Prevent directory traversal
|
||||
if (finalPath.indexOf("..") >= 0)
|
||||
{
|
||||
ESP_LOGE(tag, "Rejected unsafe path: %s", finalPath.c_str());
|
||||
return false;
|
||||
}
|
||||
// Collapse duplicate slashes (optional hardening)
|
||||
while (finalPath.indexOf("//") >= 0)
|
||||
{
|
||||
finalPath.replace("//", "/");
|
||||
}
|
||||
|
||||
// Size checks
|
||||
const size_t MAX_FILE_SIZE = 1024 * 1024; // 1MB cap (aligned with readFile)
|
||||
const size_t totalSize = strlen(message);
|
||||
if (totalSize > MAX_FILE_SIZE)
|
||||
{
|
||||
ESP_LOGE(tag, "Write too large: %u bytes for %s", (unsigned)totalSize, finalPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write with error handling
|
||||
try
|
||||
// Write to a temporary file first for atomicity
|
||||
String tmpPath = finalPath + ".tmp";
|
||||
File tmp = fs.open(tmpPath.c_str(), "w");
|
||||
if (!tmp)
|
||||
{
|
||||
size_t bytesWritten = file.print(message);
|
||||
if (bytesWritten == 0)
|
||||
{
|
||||
ESP_LOGE(tag, "Failed to write to file: %s", path);
|
||||
file.close();
|
||||
ESP_LOGE(tag, "Failed to open temp file: %s", tmpPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure all data is written
|
||||
file.flush();
|
||||
file.close();
|
||||
ESP_LOGD(tag, "Successfully wrote %u bytes to %s", bytesWritten, path);
|
||||
// Write in a loop to ensure all bytes are written
|
||||
size_t written = 0;
|
||||
const uint8_t *buf = reinterpret_cast<const uint8_t *>(message);
|
||||
while (written < totalSize)
|
||||
{
|
||||
size_t n = tmp.write(buf + written, totalSize - written);
|
||||
if (n == 0)
|
||||
{
|
||||
ESP_LOGE(tag, "Write failed to temp file: %s at %u/%u bytes", tmpPath.c_str(), (unsigned)written, (unsigned)totalSize);
|
||||
tmp.close();
|
||||
fs.remove(tmpPath.c_str());
|
||||
return false;
|
||||
}
|
||||
written += n;
|
||||
}
|
||||
|
||||
// Flush and close temp file
|
||||
tmp.flush();
|
||||
tmp.close();
|
||||
|
||||
// Replace the target file atomically: remove existing then rename
|
||||
if (fs.exists(finalPath.c_str()))
|
||||
{
|
||||
if (!fs.remove(finalPath.c_str()))
|
||||
{
|
||||
ESP_LOGE(tag, "Failed to remove existing file: %s", finalPath.c_str());
|
||||
fs.remove(tmpPath.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!fs.rename(tmpPath.c_str(), finalPath.c_str()))
|
||||
{
|
||||
ESP_LOGE(tag, "Failed to rename %s to %s", tmpPath.c_str(), finalPath.c_str());
|
||||
fs.remove(tmpPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGD(tag, "Successfully wrote %u bytes to %s", (unsigned)totalSize, finalPath.c_str());
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
ESP_LOGE(tag, "Exception while writing file %s: %s", path, e.what());
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
char *readFile(fs::FS &fs, const char *path)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user