From 0796274f683e4d355685bf3db2c75ba9703fb211 Mon Sep 17 00:00:00 2001 From: Rythlan Date: Fri, 19 Jun 2026 21:58:27 +0300 Subject: [PATCH 1/2] common.h: config + alert fields --- include/common.h | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/include/common.h b/include/common.h index cfc0f32..b7ac3e1 100644 --- a/include/common.h +++ b/include/common.h @@ -5,7 +5,6 @@ #include #include #include -#include #include #define SD_LOG_INTERVAL_MIN 10 @@ -13,7 +12,6 @@ // --- Hardware Instances --- extern Adafruit_AHTX0 aht; extern Adafruit_BMP280 bmp; -extern Preferences prefs; extern M5Canvas canvas; // --- Sensor Status --- @@ -39,6 +37,17 @@ struct DeviceConfig unsigned long last_sd_log = 0; // CPU frequency: 0 = Auto (240 awake / 80 dimmed), else fixed 240/160/80. int cpu_freq = 0; + + // --- Advanced Settings --- + int volume = 128; + float temp_offset = 0.0f; + float hum_offset = 0.0f; + float press_offset = 0.0f; + bool alerts_enabled = false; + float temp_high_thresh = 35.0f; + float temp_low_thresh = 0.0f; + float hum_high_thresh = 80.0f; + float hum_low_thresh = 20.0f; }; extern DeviceConfig config; @@ -50,8 +59,13 @@ struct DisplayState bool is_dimmed = false; int current_theme = 0; int settings_cursor = 0; + int adv_settings_cursor = 0; bool wasPressed = false; bool settings_dirty = false; + + // Alert state tracking (prevents beeping every loop) + bool prev_t_alert = false; + bool prev_h_alert = false; }; extern DisplayState state; @@ -110,8 +124,10 @@ void drawDashboard(); void drawForecast(); void drawStats(); void drawSettings(); +void drawAdvancedSettings(); void readSensors(); +void checkAlerts(); void updateTrend(float current_slp); void updateHistory(float current_slp); void logToSD(); From f25c598fd36d92bdc0d036bdbe7eca41c1012402 Mon Sep 17 00:00:00 2001 From: Rythlan Date: Fri, 19 Jun 2026 22:27:02 +0300 Subject: [PATCH 2/2] storage: move settings to SD card --- src/main.cpp | 31 ------------ src/sensor.cpp | 56 ---------------------- src/storage.cpp | 125 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 87 deletions(-) create mode 100644 src/storage.cpp diff --git a/src/main.cpp b/src/main.cpp index 852ca1d..585c943 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,7 +4,6 @@ // --- Sensor Instances --- Adafruit_AHTX0 aht; Adafruit_BMP280 bmp; -Preferences prefs; M5Canvas canvas(&M5Cardputer.Display); // --- Sensor Status --- @@ -47,36 +46,6 @@ void playTone(int type) } } -void saveSettings() -{ - prefs.begin("weather_app", false); - prefs.putFloat("alt", config.altitude); - prefs.putInt("intvl", config.update_interval); - prefs.putBool("snd", config.sound_enabled); - prefs.putBool("prs", config.show_pressure); - prefs.putInt("brt", config.screen_brightness); - prefs.putInt("dimto", config.auto_dim_timeout); - prefs.putBool("sdlog", config.sd_logging); - prefs.putInt("cpu", config.cpu_freq); - prefs.end(); - state.settings_dirty = false; -} - -void loadSettings() -{ - prefs.begin("weather_app", true); - config.altitude = prefs.getFloat("alt", 100.0f); - config.update_interval = prefs.getInt("intvl", 1); - config.sound_enabled = prefs.getBool("snd", false); - config.show_pressure = prefs.getBool("prs", true); - state.current_theme = prefs.getInt("thm", 0); - config.screen_brightness = prefs.getInt("brt", 255); - config.auto_dim_timeout = prefs.getInt("dimto", 30); - config.sd_logging = prefs.getBool("sdlog", false); - config.cpu_freq = prefs.getInt("cpu", 0); - prefs.end(); -} - // Apply the configured CPU frequency. Auto mode underclocks to 80 MHz while // dimmed (battery) and returns to 240 MHz when awake void applyCpuFrequency(bool dimmed) diff --git a/src/sensor.cpp b/src/sensor.cpp index 6333caf..0f5e97d 100644 --- a/src/sensor.cpp +++ b/src/sensor.cpp @@ -1,25 +1,4 @@ #include "common.h" -#include -#include - -static SPIClass sdSPI(HSPI); -static bool sd_inited = false; -static bool sd_available = false; - -void toggleSDLogging() -{ - config.sd_logging = !config.sd_logging; - prefs.begin("weather_app", false); - prefs.putBool("sdlog", config.sd_logging); - prefs.end(); - if (!config.sd_logging) - { - sd_inited = false; - sd_available = false; - } - playTone(config.sd_logging ? 1 : -1); - state.redraw = true; -} // --- Zambretti-style forecaster (pressure + 3h tendency only) --- // Returns a code 0..26 where higher values mean worse weather. @@ -227,39 +206,4 @@ void updateTrend(float current_slp) telemetry.slp_rate * 3.0f); } -void logToSD() -{ - if (!config.sd_logging) - return; - - unsigned long interval = (unsigned long)SD_LOG_INTERVAL_MIN * 60UL * 1000UL; - if (config.last_sd_log != 0 && (millis() - config.last_sd_log) < interval) - return; - config.last_sd_log = millis(); - - if (!sd_inited) - { - sdSPI.begin(40, 39, 14, 12); // SCK, MISO, MOSI, CS (Cardputer SD slot) - sd_available = SD.begin(12, sdSPI, 25000000); - sd_inited = true; - } - if (!sd_available) - return; - File f = SD.open("/meteo.csv", FILE_APPEND); - if (!f) - return; - - if (f.size() == 0) - f.println("uptime_ms,temp_c,humidity,pressure_hpa,slp_hpa,trend,rate_hpa_h,bat_pct"); - - int mv = M5Cardputer.Power.getBatteryVoltage(); - int bat = map(constrain(mv, 3300, 4200), 3300, 4200, 0, 100); - char row[112]; - snprintf(row, sizeof(row), "%lu,%.2f,%.2f,%.2f,%.2f,%d,%.3f,%d", - millis(), telemetry.temperature, telemetry.humidity, - telemetry.pressure, telemetry.slp, telemetry.pressure_trend, - telemetry.slp_rate, bat); - f.println(row); - f.close(); -} diff --git a/src/storage.cpp b/src/storage.cpp new file mode 100644 index 0000000..a4ea75a --- /dev/null +++ b/src/storage.cpp @@ -0,0 +1,125 @@ +#include "common.h" +#include +#include + +static SPIClass sdSPI(HSPI); +static bool sd_inited = false; +static bool sd_available = false; + +bool ensureSDReady() { + if (!sd_inited) { + sdSPI.begin(40, 39, 14, 12); + sd_available = SD.begin(12, sdSPI, 25000000); + sd_inited = true; + } + if (sd_available && !SD.exists("/cardmeteo")) { + SD.mkdir("/cardmeteo"); + } + return sd_available; +} + +void deinitSD() { + sd_inited = false; + sd_available = false; +} + +void toggleSDLogging() { + config.sd_logging = !config.sd_logging; + saveSettings(); // Persist immediately to SD + if (!config.sd_logging) deinitSD(); + playTone(config.sd_logging ? 1 : -1); + state.redraw = true; +} + +void loadSettings() { + if (!ensureSDReady()) return; // Fallback to defaults if no SD + + File f = SD.open("/cardmeteo/settings.cfg", FILE_READ); + if (!f) return; + + while (f.available()) { + String line = f.readStringUntil('\n'); + line.trim(); + int eq = line.indexOf('='); + if (eq < 0) continue; + + String key = line.substring(0, eq); + String val = line.substring(eq + 1); + + if (key == "alt") config.altitude = val.toFloat(); + else if (key == "intvl") config.update_interval = val.toInt(); + else if (key == "snd") config.sound_enabled = (val == "1"); + else if (key == "prs") config.show_pressure = (val == "1"); + else if (key == "thm") state.current_theme = val.toInt(); + else if (key == "brt") config.screen_brightness = val.toInt(); + else if (key == "dimto") config.auto_dim_timeout = val.toInt(); + else if (key == "sdlog") config.sd_logging = (val == "1"); + else if (key == "cpu") config.cpu_freq = val.toInt(); + else if (key == "vol") config.volume = val.toInt(); + else if (key == "toffset") config.temp_offset = val.toFloat(); + else if (key == "hoffset") config.hum_offset = val.toFloat(); + else if (key == "poffset") config.press_offset = val.toFloat(); + else if (key == "alert") config.alerts_enabled = (val == "1"); + else if (key == "thi") config.temp_high_thresh = val.toFloat(); + else if (key == "tlo") config.temp_low_thresh = val.toFloat(); + else if (key == "hhi") config.hum_high_thresh = val.toFloat(); + else if (key == "hlo") config.hum_low_thresh = val.toFloat(); + } + f.close(); + + // If alerts are enabled, force sound on + if (config.alerts_enabled) config.sound_enabled = true; +} + +void saveSettings() { + if (!ensureSDReady()) return; + + File f = SD.open("/cardmeteo/settings.cfg", FILE_WRITE); + if (!f) return; + + f.printf("alt=%.1f\n", config.altitude); + f.printf("intvl=%d\n", config.update_interval); + f.printf("snd=%d\n", config.sound_enabled ? 1 : 0); + f.printf("prs=%d\n", config.show_pressure ? 1 : 0); + f.printf("thm=%d\n", state.current_theme); + f.printf("brt=%d\n", config.screen_brightness); + f.printf("dimto=%d\n", config.auto_dim_timeout); + f.printf("sdlog=%d\n", config.sd_logging ? 1 : 0); + f.printf("cpu=%d\n", config.cpu_freq); + f.printf("vol=%d\n", config.volume); + f.printf("toffset=%.2f\n", config.temp_offset); + f.printf("hoffset=%.2f\n", config.hum_offset); + f.printf("poffset=%.2f\n", config.press_offset); + f.printf("alert=%d\n", config.alerts_enabled ? 1 : 0); + f.printf("thi=%.1f\n", config.temp_high_thresh); + f.printf("tlo=%.1f\n", config.temp_low_thresh); + f.printf("hhi=%.1f\n", config.hum_high_thresh); + f.printf("hlo=%.1f\n", config.hum_low_thresh); + f.close(); +} + +void logToSD() { + if (!config.sd_logging) return; + + unsigned long interval = (unsigned long)SD_LOG_INTERVAL_MIN * 60UL * 1000UL; + if (config.last_sd_log != 0 && (millis() - config.last_sd_log) < interval) return; + config.last_sd_log = millis(); + + if (!ensureSDReady()) return; + + File f = SD.open("/cardmeteo/meteo.csv", FILE_APPEND); + if (!f) return; + + if (f.size() == 0) + f.println("uptime_ms,temp_c,humidity,pressure_hpa,slp_hpa,trend,rate_hpa_h,bat_pct"); + + int mv = M5Cardputer.Power.getBatteryVoltage(); + int bat = map(constrain(mv, 3300, 4200), 3300, 4200, 0, 100); + char row[112]; + snprintf(row, sizeof(row), "%lu,%.2f,%.2f,%.2f,%.2f,%d,%.3f,%d", + millis(), telemetry.temperature, telemetry.humidity, + telemetry.pressure, telemetry.slp, telemetry.pressure_trend, + telemetry.slp_rate, bat); + f.println(row); + f.close(); +}