fixed config, memory consumption, manual
This commit is contained in:
parent
de44c789af
commit
e677a7a8bf
|
|
@ -118,7 +118,7 @@ Manager features:
|
|||
- Refresh playlist view.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
|
||||
## Mapping figurines (RFID → what to play)
|
||||
|
||||
Option A: Map through the web app (recommended)
|
||||
|
|
|
|||
237
src/config.cpp
237
src/config.cpp
|
|
@ -1,27 +1,30 @@
|
|||
#include "config.h"
|
||||
#include "globals.h"
|
||||
#include <SD.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <strings.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Global config instance
|
||||
Config config;
|
||||
|
||||
const String getConfigFilePath() {
|
||||
static String config_dir;
|
||||
if (config_dir.isEmpty()) {
|
||||
config_dir.concat("/");
|
||||
config_dir.concat(sys_dir);
|
||||
config_dir.concat("/");
|
||||
config_dir.concat(config_dir);
|
||||
const char* getConfigFilePath() {
|
||||
static char path[64];
|
||||
static bool init = false;
|
||||
if (!init) {
|
||||
// Build "/<sys_dir>/<config_file>" once into a static buffer
|
||||
snprintf(path, sizeof(path), "/%s/%s", sys_dir ? sys_dir : "", config_file ? config_file : "");
|
||||
init = true;
|
||||
}
|
||||
|
||||
return config_dir;
|
||||
return path;
|
||||
}
|
||||
|
||||
void setDefaultConfig() {
|
||||
config.initialVolume = 7;
|
||||
config.maxVolume = 15;
|
||||
config.sleepTime = 1800000;
|
||||
config.minVoltage = 3200;
|
||||
config.minVoltage = 3000;
|
||||
config.voltage100Percent = 4200;
|
||||
config.sleepDelay = 1800000;
|
||||
config.sleepMessageDelay = 1798000;
|
||||
|
|
@ -32,127 +35,169 @@ void setDefaultConfig() {
|
|||
}
|
||||
|
||||
bool loadConfig() {
|
||||
String configPath = getConfigFilePath();
|
||||
|
||||
const char* configPath = getConfigFilePath();
|
||||
|
||||
if (!SD.exists(configPath)) {
|
||||
Serial.println(F("Config file not found, using defaults"));
|
||||
setDefaultConfig();
|
||||
return saveConfig(); // Create config file with defaults
|
||||
}
|
||||
|
||||
|
||||
File file = SD.open(configPath, FILE_READ);
|
||||
if (!file) {
|
||||
Serial.println(F("Failed to open config file"));
|
||||
setDefaultConfig();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Set defaults first
|
||||
setDefaultConfig();
|
||||
|
||||
// Parse config file line by line
|
||||
while (file.available()) {
|
||||
String line = file.readStringUntil('\n');
|
||||
line.trim();
|
||||
|
||||
|
||||
// Parse config file line by line using a fixed-size buffer (no dynamic allocation)
|
||||
char line[160];
|
||||
while (true) {
|
||||
int idx = 0;
|
||||
int c;
|
||||
// Read characters until newline or EOF
|
||||
while (idx < (int)sizeof(line) - 1 && (c = file.read()) >= 0) {
|
||||
if (c == '\r') continue;
|
||||
if (c == '\n') break;
|
||||
line[idx++] = (char)c;
|
||||
}
|
||||
if (idx == 0 && c < 0) {
|
||||
// EOF with no more data
|
||||
break;
|
||||
}
|
||||
line[idx] = '\0';
|
||||
|
||||
// Trim leading/trailing whitespace
|
||||
char* s = line;
|
||||
while (*s && isspace((unsigned char)*s)) ++s;
|
||||
char* end = s + strlen(s);
|
||||
while (end > s && isspace((unsigned char)end[-1])) --end;
|
||||
*end = '\0';
|
||||
|
||||
// Skip empty lines and comments
|
||||
if (line.length() == 0 || line.startsWith("#")) {
|
||||
if (*s == '\0' || *s == '#') {
|
||||
if (c < 0) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
int separatorIndex = line.indexOf('=');
|
||||
if (separatorIndex == -1) {
|
||||
|
||||
// Split at '='
|
||||
char* eq = strchr(s, '=');
|
||||
if (!eq) {
|
||||
if (c < 0) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
String key = line.substring(0, separatorIndex);
|
||||
String value = line.substring(separatorIndex + 1);
|
||||
key.trim();
|
||||
value.trim();
|
||||
|
||||
*eq = '\0';
|
||||
char* key = s;
|
||||
char* val = eq + 1;
|
||||
|
||||
// Trim key
|
||||
while (*key && isspace((unsigned char)*key)) ++key;
|
||||
char* kend = key + strlen(key);
|
||||
while (kend > key && isspace((unsigned char)kend[-1])) --kend;
|
||||
*kend = '\0';
|
||||
|
||||
// Trim val
|
||||
while (*val && isspace((unsigned char)*val)) ++val;
|
||||
char* vend = val + strlen(val);
|
||||
while (vend > val && isspace((unsigned char)vend[-1])) --vend;
|
||||
*vend = '\0';
|
||||
|
||||
// Parse each configuration value
|
||||
if (key == "initialVolume") {
|
||||
config.initialVolume = constrain(value.toInt(), 0, 21);
|
||||
} else if (key == "maxVolume") {
|
||||
config.maxVolume = constrain(value.toInt(), 1, 21);
|
||||
} else if (key == "sleepTime") {
|
||||
config.sleepTime = value.toInt();
|
||||
} else if (key == "minVoltage") {
|
||||
config.minVoltage = value.toInt();
|
||||
} else if (key == "voltage100Percent") {
|
||||
config.voltage100Percent = value.toInt();
|
||||
} else if (key == "sleepDelay") {
|
||||
config.sleepDelay = value.toInt();
|
||||
} else if (key == "sleepMessageDelay") {
|
||||
config.sleepMessageDelay = value.toInt();
|
||||
} else if (key == "rfidLoopInterval") {
|
||||
config.rfidLoopInterval = constrain(value.toInt(), 1, 255);
|
||||
} else if (key == "startAtStoredProgress") {
|
||||
config.startAtStoredProgress = (value == "1" || value.equalsIgnoreCase("true"));
|
||||
} else if (key == "wifiSSID") {
|
||||
strncpy(config.wifiSSID, value.c_str(), sizeof(config.wifiSSID) - 1);
|
||||
if (strcmp(key, "initialVolume") == 0) {
|
||||
long v = strtol(val, nullptr, 10);
|
||||
if (v < 0) v = 0; if (v > 21) v = 21;
|
||||
config.initialVolume = (uint8_t)v;
|
||||
} else if (strcmp(key, "maxVolume") == 0) {
|
||||
long v = strtol(val, nullptr, 10);
|
||||
if (v < 1) v = 1; if (v > 21) v = 21;
|
||||
config.maxVolume = (uint8_t)v;
|
||||
} else if (strcmp(key, "sleepTime") == 0) {
|
||||
config.sleepTime = (uint32_t)strtoul(val, nullptr, 10);
|
||||
} else if (strcmp(key, "minVoltage") == 0) {
|
||||
config.minVoltage = (uint16_t)strtoul(val, nullptr, 10);
|
||||
} else if (strcmp(key, "voltage100Percent") == 0) {
|
||||
config.voltage100Percent = (uint16_t)strtoul(val, nullptr, 10);
|
||||
} else if (strcmp(key, "sleepDelay") == 0) {
|
||||
config.sleepDelay = (uint32_t)strtoul(val, nullptr, 10);
|
||||
} else if (strcmp(key, "sleepMessageDelay") == 0) {
|
||||
config.sleepMessageDelay = (uint32_t)strtoul(val, nullptr, 10);
|
||||
} else if (strcmp(key, "rfidLoopInterval") == 0) {
|
||||
long v = strtol(val, nullptr, 10);
|
||||
if (v < 1) v = 1; if (v > 255) v = 255;
|
||||
config.rfidLoopInterval = (uint8_t)v;
|
||||
} else if (strcmp(key, "startAtStoredProgress") == 0) {
|
||||
bool b = (strcmp(val, "1") == 0) || (strcasecmp(val, "true") == 0);
|
||||
config.startAtStoredProgress = b;
|
||||
} else if (strcmp(key, "wifiSSID") == 0) {
|
||||
strncpy(config.wifiSSID, val, sizeof(config.wifiSSID) - 1);
|
||||
config.wifiSSID[sizeof(config.wifiSSID) - 1] = '\0';
|
||||
} else if (key == "wifiPassword") {
|
||||
strncpy(config.wifiPassword, value.c_str(), sizeof(config.wifiPassword) - 1);
|
||||
} else if (strcmp(key, "wifiPassword") == 0) {
|
||||
strncpy(config.wifiPassword, val, sizeof(config.wifiPassword) - 1);
|
||||
config.wifiPassword[sizeof(config.wifiPassword) - 1] = '\0';
|
||||
}
|
||||
|
||||
if (c < 0) break; // EOF after processing last line
|
||||
}
|
||||
|
||||
|
||||
file.close();
|
||||
|
||||
Serial.println("Config loaded successfully");
|
||||
Serial.print("Initial Volume: "); Serial.println(config.initialVolume);
|
||||
Serial.print("Max Volume: "); Serial.println(config.maxVolume);
|
||||
Serial.print("Sleep Delay: "); Serial.println(config.sleepDelay);
|
||||
Serial.print("RFID Interval: "); Serial.println(config.rfidLoopInterval);
|
||||
|
||||
|
||||
Serial.println(F("Config loaded successfully"));
|
||||
Serial.print(F("Initial Volume: ")); Serial.println(config.initialVolume);
|
||||
Serial.print(F("Max Volume: ")); Serial.println(config.maxVolume);
|
||||
Serial.print(F("Sleep Delay: ")); Serial.println(config.sleepDelay);
|
||||
Serial.print(F("RFID Interval: ")); Serial.println(config.rfidLoopInterval);
|
||||
Serial.print(F("min Voltage: ")); Serial.println(config.minVoltage);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool saveConfig() {
|
||||
String configPath = getConfigFilePath();
|
||||
|
||||
const char* configPath = getConfigFilePath();
|
||||
|
||||
File file = SD.open(configPath, FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println(F("Failed to create config file"));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Write config file with comments for user reference
|
||||
file.println("# HannaBox Conf File");
|
||||
file.println("# format: key=value");
|
||||
file.println("");
|
||||
|
||||
file.println("# Audio");
|
||||
file.print("initialVolume="); file.println(config.initialVolume);
|
||||
file.print("maxVolume="); file.println(config.maxVolume);
|
||||
file.println("");
|
||||
|
||||
file.println("# Power Management (in milliseconds)");
|
||||
file.print("sleepTime="); file.println(config.sleepTime);
|
||||
file.print("sleepDelay="); file.println(config.sleepDelay);
|
||||
file.print("sleepMessageDelay="); file.println(config.sleepMessageDelay);
|
||||
file.println("");
|
||||
|
||||
file.println("# Battery (in millivolts)");
|
||||
file.print("minVoltage="); file.println(config.minVoltage);
|
||||
file.print("voltage100Percent="); file.println(config.voltage100Percent);
|
||||
file.println("");
|
||||
|
||||
file.println("# RFID");
|
||||
file.print("rfidLoopInterval="); file.println(config.rfidLoopInterval);
|
||||
file.println("");
|
||||
|
||||
file.println("# Playback");
|
||||
file.print("startAtStoredProgress="); file.println(config.startAtStoredProgress ? "true" : "false");
|
||||
file.println("");
|
||||
|
||||
file.println("# WiFi (leave empty to use current WiFiManager)");
|
||||
file.print("wifiSSID="); file.println(config.wifiSSID);
|
||||
file.print("wifiPassword="); file.println(config.wifiPassword);
|
||||
|
||||
file.println(F("# HannaBox Conf File"));
|
||||
file.println(F("# format: key=value"));
|
||||
file.println();
|
||||
|
||||
file.println(F("# Audio"));
|
||||
file.print(F("initialVolume=")); file.println(config.initialVolume);
|
||||
file.print(F("maxVolume=")); file.println(config.maxVolume);
|
||||
file.println();
|
||||
|
||||
file.println(F("# Power Management (in milliseconds)"));
|
||||
file.print(F("sleepTime=")); file.println(config.sleepTime);
|
||||
file.print(F("sleepDelay=")); file.println(config.sleepDelay);
|
||||
file.print(F("sleepMessageDelay=")); file.println(config.sleepMessageDelay);
|
||||
file.println();
|
||||
|
||||
file.println(F("# Battery (in millivolts)"));
|
||||
file.print(F("minVoltage=")); file.println(config.minVoltage);
|
||||
file.print(F("voltage100Percent=")); file.println(config.voltage100Percent);
|
||||
file.println();
|
||||
|
||||
file.println(F("# RFID"));
|
||||
file.print(F("rfidLoopInterval=")); file.println(config.rfidLoopInterval);
|
||||
file.println();
|
||||
|
||||
file.println(F("# Playback"));
|
||||
file.print(F("startAtStoredProgress=")); file.println(config.startAtStoredProgress ? F("true") : F("false"));
|
||||
file.println();
|
||||
|
||||
file.println(F("# WiFi (leave empty to use current WiFiManager)"));
|
||||
file.print(F("wifiSSID=")); file.println(config.wifiSSID);
|
||||
file.print(F("wifiPassword=")); file.println(config.wifiPassword);
|
||||
|
||||
file.close();
|
||||
|
||||
|
||||
Serial.println(F("Config saved successfully"));
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
40
src/config.h
40
src/config.h
|
|
@ -3,19 +3,30 @@
|
|||
|
||||
#include <Arduino.h>
|
||||
|
||||
// Configuration structure - keep it minimal for memory efficiency
|
||||
// Keep the structure compact to minimize padding and RAM usage.
|
||||
// Order fields from wider to narrower types, and avoid inline default initializers.
|
||||
// Defaults are applied in setDefaultConfig() to keep initialization simple and lightweight.
|
||||
struct Config {
|
||||
uint8_t initialVolume = 7;
|
||||
uint8_t maxVolume = 15;
|
||||
uint32_t sleepTime = 1800000; // 30 minutes in ms
|
||||
uint16_t minVoltage = 3200; // mV - minimum voltage before sleep
|
||||
uint16_t voltage100Percent = 4200; // mV - voltage representing 100% battery
|
||||
uint32_t sleepDelay = 1800000; // 30 minutes in ms
|
||||
uint32_t sleepMessageDelay = 1798000; // 2 seconds before sleep
|
||||
uint8_t rfidLoopInterval = 25; // RFID check interval
|
||||
bool startAtStoredProgress = true; // Resume from last position
|
||||
char wifiSSID[32] = ""; // WiFi SSID (empty = use current mechanism)
|
||||
char wifiPassword[64] = ""; // WiFi password (empty = use current mechanism)
|
||||
// 32-bit values
|
||||
uint32_t sleepTime; // ms
|
||||
uint32_t sleepDelay; // ms
|
||||
uint32_t sleepMessageDelay; // ms
|
||||
|
||||
// 16-bit values
|
||||
uint16_t minVoltage; // mV
|
||||
uint16_t voltage100Percent; // mV
|
||||
|
||||
// 8-bit values
|
||||
uint8_t initialVolume;
|
||||
uint8_t maxVolume;
|
||||
uint8_t rfidLoopInterval;
|
||||
|
||||
// flags
|
||||
bool startAtStoredProgress;
|
||||
|
||||
// WiFi credentials (fixed-size buffers, no heap allocations)
|
||||
char wifiSSID[32]; // empty => use current mechanism
|
||||
char wifiPassword[64]; // empty => use current mechanism
|
||||
};
|
||||
|
||||
// Global config instance
|
||||
|
|
@ -25,6 +36,9 @@ extern Config config;
|
|||
bool loadConfig();
|
||||
bool saveConfig();
|
||||
void setDefaultConfig();
|
||||
const String getConfigFilePath();
|
||||
|
||||
// Returns a pointer to a static buffer with the absolute config file path.
|
||||
// Avoids dynamic allocation/Arduino String to reduce heap fragmentation.
|
||||
const char* getConfigFilePath();
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1697,11 +1697,15 @@ void loop()
|
|||
{
|
||||
lastVoltage = getBatteryVoltageMv();
|
||||
free_heap = xPortGetFreeHeapSize();
|
||||
if (lastVoltage < config.minVoltage)
|
||||
if (lastVoltage < config.minVoltage && config.minVoltage > 0)
|
||||
{
|
||||
if (voltage_threshold_counter > 3)
|
||||
{
|
||||
Serial.println(F("deep sleep due to low volts.."));
|
||||
Serial.print("deep sleep due to low volts (");
|
||||
Serial.print(lastVoltage);
|
||||
Serial.print(") min: ");
|
||||
Serial.println(config.minVoltage);
|
||||
|
||||
lastInteraction = millis() - config.sleepMessageDelay;
|
||||
voltage_threshold_counter = 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue