Compare commits

..
6 Commits
Author SHA1 Message Date
Amir 13da0e0c87 Revise README for version 6.2.0 updates
Updated README for version 6.2.0, fixing DST handling and enhancing features.
2026-03-29 17:45:10 +02:00
Amir 8543493ac0 v6.2.0 - Fix DST handling and improve price lookup logic
This release fixes a critical bug related to price display during Daylight Saving Time transitions, implementing timestamp-based lookups for accurate price retrieval. It also includes new and updated functions for better handling of price indices and LED control.
2026-03-29 17:29:43 +02:00
Amir eb5ef5954b Bump version to 6.2.0 with DST improvements
Updated version information to 6.2.0 with critical DST fix and new functions.
2026-03-29 17:28:31 +02:00
Amir 2fff8296c5 Update to v6.2.0 - DST Fix
CRITICAL FIX: DST (Daylight Saving Time) handling is now fully fixed for all days.
2026-03-29 17:25:48 +02:00
Amir 3f09e475fc Update README for version 6.1.2 changes
Add image to README for project visualization.
2026-03-11 20:47:57 +01:00
Amir eb34d07ccf Add files via upload 2026-03-11 20:45:18 +01:00
5 changed files with 1997 additions and 206 deletions
+95 -137
View File
@@ -4,6 +4,82 @@ All notable changes to this project are documented here.
---
## v6.2.0 DST (Daylight Saving Time) Handling Fixed (20260329)
**Summary**
This release fixes a critical bug that caused incorrect price display on DST switch days. On March 29, 2026 (the spring forward day), the ticker at 12:41 showed prices for hours 13:00, 14:00, and 15:00 instead of the correct 12:00, 13:00, and 14:00.
### Problem: Arithmetic-Based Index Calculation
**Root Cause (v6.0.0 v6.1.2):**
The code assumed every day has exactly 96 price entries:
```cpp
int startIndex = hourIndex * 4; // e.g., hour 12 → index 48
```
This assumption breaks on DST switch days:
| Day Type | Hours | Price Entries | Example |
|----------|-------|---------------|---------|
| Normal day | 24 | 96 | Array indices 0-95 |
| Spring forward (March) | 23 | 92 | Index 48 points to wrong time |
| Fall back (October) | 25 | 100 | Index 48 points to wrong time |
At 12:41 on March 29, 2026:
- ESP32 correctly reported `timeinfo.tm_hour = 12`
- Old code calculated `12 × 4 = 48`
- But array only had 92 entries (no index 48 that maps to local hour 12)
- Result: Displayed prices for 13:00, 14:00, 15:00 instead of 12:00, 13:00, 14:00
### Solution: Timestamp-Based Lookups
**New approach (v6.2.0):**
All price lookups now search the `unix_seconds` array using actual timestamps:
```cpp
// Find index by searching for matching hour in timestamps
int findPriceIndexForHour(const JsonArray& unixSeconds, int targetHour) {
for (size_t i = 0; i < unixSeconds.size(); i++) {
unsigned long unixTime = unixSeconds[i].as<unsigned long>();
time_t t = (time_t)unixTime;
struct tm* ptm = localtime(&t);
if (ptm != NULL && ptm->tm_hour == targetHour) {
return (int)i; // Found correct index for this hour
}
}
return -1;
}
```
**New functions added:**
- `findPriceIndexForHour()` Finds first price index for a given hour
- `findCurrentPriceIndex()` Finds current 15-minute slot using Unix timestamp
- `getHourFromPriceIndex()` Gets hour from price array index
**Updated functions:**
- `getHourlyAverage()` Now uses timestamp lookup instead of `hourIndex * 4`
- `display15MinuteDetails()` Timestamp-based with hour verification in loop
- `displayPriceRow()` Timestamp-based data index lookup
- `displayPrimaryList()` Timestamp-based current hour detection
- `updateLeds()` Timestamp-based current interval lookup
### Why This Is Future-Proof
| Scenario | Code Behavior |
|----------|--------------|
| Normal days (96 entries) | Works as before |
| DST spring forward (92 entries) | Timestamp lookup finds correct indices |
| DST fall back (100 entries) | Timestamp lookup finds correct indices |
| EU cancels DST | Only update `TZ_CET_CEST` string; code works unchanged |
If EU parliament ever cancels DST switching, you only need to update the `TZ_CET_CEST` line (one line of code). The price lookup logic requires no changes.
---
## v6.1.2 LED Indicator Restored (ESP32 PWM Fix) (20260311)
**Summary**
@@ -15,11 +91,11 @@ This release fixes a regression introduced in **v6.1.1** where the **white LED p
**Problem (v6.1.1):**
- The sketch uses `analogWrite()` to drive the LED with PWM for breathe/blink patterns.
- However, in some LED OFF branches the code used `digitalWrite(LOW)` on the same `whiteLedPin`.
- However, in some "LED OFF" branches the code used `digitalWrite(LOW)` on the same `whiteLedPin`.
- On ESP32 (LEDC), once PWM is attached to a pin, `digitalWrite(LOW)` may **not fully disable** PWM output.
- Result:
- LED could remain **faintly on** (dim glow) when LEDs were supposed to be off (e.g., presence timeout / backlight off).
- Some patterns could appear stuck or inconsistent.
- LED could remain **faintly on** (dim glow) when LEDs were supposed to be off.
- Some patterns could appear "stuck" or inconsistent.
**Solution (v6.1.2):**
@@ -27,7 +103,7 @@ This release fixes a regression introduced in **v6.1.1** where the **white LED p
- Use `analogWrite(whiteLedPin, 0)` instead of `digitalWrite(whiteLedPin, LOW)`.
- Use `analogWrite(whiteLedPin, 255)` instead of `digitalWrite(whiteLedPin, HIGH)`.
- Blink/doubleblink toggles now switch between PWM **0** and **255**.
- This ensures the LED is **truly off** whenever LED output is gated off (no data, no time sync, or presence timeout).
- This ensures the LED is **truly off** whenever LED output is gated off.
---
@@ -35,31 +111,25 @@ This release fixes a regression introduced in **v6.1.1** where the **white LED p
**Summary**
This release fixes a bug where the **daily lowest / highest hourly price marker** ignored negative prices (and also ignored 0.0), which could cause the ticker to incorrectly mark the **lowest positive[...]
This release fixes a bug where the **daily lowest / highest hourly price marker** ignored negative prices (and also ignored 0.0), which could cause the ticker to incorrectly mark the **lowest positive** price as the daily minimum.
### Fixed: Daily Low/High Marker Ignored Negative & Zero Prices
**Problem (v6.1.0):**
- In `processJsonData()` the daily min/max scan used:
- `if (hourlyAvg > 0) { ... }`
```cpp
if (hourlyAvg > 0) { ... }
```
- This had two side effects:
1. **Negative** hourly averages were completely skipped.
2. A true price of **0.0** was also skipped (even though 0 can be a valid market price).
- Additionally, the daily average was computed as `sum / 24.0` even though hours were being skipped from `sum`, making the daily average incorrect whenever any hour was excluded.
**Solution (v6.1.1):**
- The min/max and average scan now:
- Treats an hour as valid based on **data availability** (having all 4×15minute entries), not based on value sign.
- Includes **all values** (negative, zero, positive) when computing:
- `lowestPriceIndex`
- `highestPriceIndex`
- `averagePrice`
- Computes `averagePrice` using the number of valid hours (normally 24).
- Includes **all values** (negative, zero, positive).
---
@@ -67,112 +137,21 @@ This release fixes a bug where the **daily lowest / highest hourly price marker*
**Summary**
This release fixes a bug where the ticker could remain indefinitely on the **No data for today** screen after midnight, even though the API was already returning fresh data. It also refines the [...]
This release fixes a bug where the ticker could remain indefinitely on the **"No data for today"** screen after midnight, even though the API was already returning fresh data.
### Fixed: Stuck on NO_DATA_OFFSET After Midnight
**Problem (v6.0.0):**
- After midnight, the device:
- Detected day rollover and entered a “no data” state.
- Scheduled an immediate API fetch.
- However:
- The API can continue to serve **yesterdays market day** for some time after local midnight.
- The code only checked the **first** `unix_seconds` entry against the current local day.
- HTTP + JSON success always incremented `apiSuccessCount`, even if `processJsonData()` subsequently decided the dataset was “not for today”.
- The scheduler treated such fetches as **successful**, pushed `nextScheduledFetchTime` 24 hours into the future, and never retried.
- Result: the ticker stayed on **NO_DATA_OFFSET** forever, until:
- A manual longpress triggered a fresh fetch at a time when the API finally returned recognized “today” data, or
- The device was rebooted later in the day.
- The API can continue to serve **yesterday's market day** for some time after local midnight.
- HTTP + JSON success always incremented `apiSuccessCount`, even if the data was "not for today".
- The scheduler treated such fetches as **successful** and never retried.
**Solution (v6.1.0):**
1. **Market Day Detection**:
- `processJsonData()` now determines the “market day” using the **last** `unix_seconds` timestamp from the APIs dataset (assumed to cover one full day in 15minute steps).
- It compares that calendar date (local time) against the current local date.
- If they differ:
- The dataset is treated as **“not for today”**.
- `isTodayDataAvailable = false`.
- The function returns **false**, and a new flag `lastProcessJsonAcceptedToday` remains `false`.
2. **Logical Failure vs HTTP/JSON Failure**:
- New global flag:
- `lastProcessJsonAcceptedToday` `true` only when `processJsonData()` accepts the dataset as “todays” data.
- In `handleDataFetching()`:
- A scheduled fetch is considered a **real success** only if:
- HTTP + JSON succeeded, **and**
- `lastProcessJsonAcceptedToday == true`.
- In all other cases (including “HTTP 200 + parse OK but data still for yesterday”):
- The fetch is treated as **failure** for scheduling purposes.
- If `midnightPhaseActive == true`, `scheduleAfterMidnightFailure()` is invoked to plan a retry.
3. **Midnight Phase Cleanup**:
- When a fetch finally provides a dataset for today:
- `isTodayDataAvailable = true`.
- `lastProcessJsonAcceptedToday = true`.
- `midnightPhaseActive` is cleared; `midnightRetryCount` reset to 0.
- `nextScheduledFetchTime` is set to 24 hours ahead.
As a result, the ticker will **keep retrying** after midnight until a correct marketday dataset appears, instead of giving up after the first HTTP 200.
---
### Changed: AfterMidnight Retry Schedule
In `scheduleAfterMidnightFailure()` the retry strategy when `midnightPhaseActive == true` has been tuned.
**Old behavior (v6.0.0, conceptual):**
- First few failures after midnight:
- Retried every 10 minutes up to 5 attempts.
- Afterwards:
- Switched to hourly retries (top of each hour).
**New behavior (v6.1.0 + user configuration):**
- Fast retry phase fully contained within the **first hour after midnight**.
- You configured:
```cpp
if (midnightRetryCount < 2) {
// Retry every 20 minutes for first 2 attempts (~1 hour window)
midnightRetryCount++;
nextScheduledFetchTime = now + 1200; // 20 minutes
debugPrint(2, "Midnight retry " + String(midnightRetryCount) + "/2 in 20 minutes");
} else {
// After that, retry only at top of each hour
...
}
```
- Timeline:
- 00:00 initial attempt (triggered by day rollover).
- If dataset is still for previous day:
- 1st retry at ~00:20.
- 2nd retry at ~00:40.
- After that:
- Retries only at the **top of the next hours** (01:00, 02:00, …),
until a dataset with market day = today is accepted.
This configuration significantly reduces overnight API load while still ensuring the ticker picks up the new day as soon as the API publishes it.
---
### Other Behavior (Retained From v6.0.0)
- **NVS caching** of daily data:
- On boot, if NVS contains a dataset whose date matches todays local date:
- The JSON is deserialized and processed.
- A new API call is **skipped**.
- After each accepted “today” fetch:
- Raw JSON payload, date, and a timestamp are stored in NVS.
- **Manual longpress refresh**:
- Still triggers immediate fetch via `nextScheduledFetchTime = now;`.
- Now also respects the improved “today” detection; “yesterdays” data is not accepted as today.
- **CET/CEST time handling**:
- Unchanged, still uses `TZ_CET_CEST` with `configTzTime`.
- **Secondary menu and NVS status lines**:
- Retained from v6.0.0; updated only for version string and minor wording.
- `processJsonData()` now determines the "market day" using the **last** `unix_seconds` timestamp.
- A scheduled fetch is only successful if `lastProcessJsonAcceptedToday == true`.
- Midnight retry logic keeps trying until valid "today" data is received.
---
@@ -184,30 +163,9 @@ First major redesign focused on reducing API traffic and improving resilience us
### New
- NVS namespace `"my-ticker"` introduced with keys:
- `ssid`, `pass` WiFi credentials.
- `data_day`, `data_mon`, `data_year` stored data calendar day.
- `data_prc` raw JSON string from API.
- `data_last_store` Unix time when data was stored.
- Boot behavior:
- Try to load and validate NVS data.
- If date matches today → reuse it and **skip** initial API call.
- Aftermidnight behavior:
- NVS is overwritten with each successful newday dataset.
- InRAM data for yesterday is invalidated at day rollover.
### UI / Menu
- Primary list:
- 15minute detail for current hour.
- Hourly averages for current + next 2 hours.
- Secondary list:
- Expanded to 20 lines to include:
- Time/date, last update, daily average.
- WiFi RSSI, IP address.
- API success rate, uptime.
- NVS status block.
- Credits & version line.
- NVS namespace `"my-ticker"` with WiFi credentials and daily price data caching.
- Boot behavior: Try to load and validate NVS data; if date matches today → reuse it and **skip** initial API call.
- Aftermidnight: NVS is overwritten with each successful newday dataset.
---
File diff suppressed because it is too large Load Diff
+79 -34
View File
@@ -1,3 +1,5 @@
![Standalone ESP32 Electricity Price Ticker](device_photo.jpg)
# Electricity Price Ticker for XIAO ESP32C3 (EnergyCharts)
This project is an ArduinoIDEfriendly firmware for the **Seeed XIAO ESP32C3** that:
@@ -9,18 +11,61 @@ This project is an ArduinoIDEfriendly firmware for the **Seeed XIAO ESP32
- Uses a white LED and an optional presence sensor to give quick visual feedback.
- Stores daily price data in **NVS** to survive reboots and reduce API calls.
The latest sketch implements **Version 6.1.2**, focusing on:
The latest sketch implements **Version 6.2.0**, focusing on:
- **Version 6.2.0 FIX**: **DST (Daylight Saving Time) handling fully fixed** the ticker now works correctly on ALL days including DST switch days (spring forward and fall back). Uses timestamp-based price lookups instead of arithmetic calculations.
- Version 6.1.2 fix: restore correct **white LED indicator** behavior (ESP32 PWM fix; no dim glow when off).
- Version 6.1.1 fix: Correct daily **low/high hourly markers** (now includes negative and **0.0** prices).
- Daily (not hourly) API fetching.
- Robust **NVS storage** of daily price data.
- Correct **CET/CEST** handling.
- Resilient **aftermidnight refresh** (no more getting stuck on No data for today).
- Resilient **aftermidnight refresh** (no more getting stuck on "No data for today").
- Preserved UI and button behavior from v5.5.
---
## DST (Daylight Saving Time) How It Works
### v6.2.0: Fully DST-Safe
**Important**: Starting with v6.2.0, the ticker is **fully DST-safe** and requires **no manual intervention** on DST switch days.
The firmware uses **timestamp-based price lookups** that work correctly regardless of whether the day has 23, 24, or 25 hours:
| Day Type | Hours in Day | Price Entries | Status |
|----------|-------------|---------------|--------|
| Normal | 24 | 96 | ✅ Works |
| Spring forward (March) | 23 | 92 | ✅ Works (fixed in v6.2.0) |
| Fall back (October) | 25 | 100 | ✅ Works (fixed in v6.2.0) |
### Timezone Configuration
The firmware uses the `TZ_CET_CEST` timezone string for displaying local time:
```cpp
const char* TZ_CET_CEST = "CET-1CEST,M3.5.0/02:00,M10.5.0/03:00";
```
**Current behavior:**
- Spring forward: Last Sunday of March at 02:00 → 03:00 (CEST, UTC+2)
- Fall back: Last Sunday of October at 03:00 → 02:00 (CET, UTC+1)
### Future-Proof: If EU Cancels DST
If the EU parliament ever cancels DST switching, you only need to update **one line of code**:
```cpp
// Option A - Stay on CET (UTC+1, winter time) permanently:
const char* TZ_CET_CEST = "CET-1";
// Option B - Stay on CEST (UTC+2, summer time) permanently:
const char* TZ_CET_CEST = "CEST-2";
```
The rest of the code works unchanged because it uses timestamp-based lookups.
---
## Bidding Zones (BZN) / Region Selection
The firmware currently uses:
@@ -116,11 +161,12 @@ Typical pins used in the sketch:
**Connections:**
- **LCD backpack XIAO ESP32C3**
- `VCC`**5V** (or 3V3 if your module explicitly supports 3.3V I²C)
- `GND`**GND**
- `SDA` → board I²C SDA pin (see XIAO ESP32C3 documentation)
- `SCL` → board I²C SCL pin
| LCD Backpack | XIAO ESP32C3 |
|-------------|---------------|
| VCC | 5V |
| GND | GND |
| SDA | I²C SDA |
| SCL | I²C SCL |
> Note: On many XIAO ESP32C3 board definitions, SDA/SCL are mapped internally. Just use the default I²C pins as documented by Seeed.
@@ -185,12 +231,14 @@ The presence sensor is used to control LCD backlight and LEDs to save power and
Recommended module: **RCWL0516** microwave motion sensor.
**Wiring (from the v5.5 header, preserved in v6.x):**
**Wiring:**
- `VCC`**3.3V**
- `GND`**GND**
- `OUT``GPIO 9` (`presencePin`)
- **Required**: 10 kΩ pulldown resistor between `GPIO 9` and `GND`.
| RCWL0516 | Connection |
|-----------|------------|
| VCC | 3.3V |
| GND | GND |
| OUT | GPIO 9 (`presencePin`) |
| **Required**: 10kΩ pulldown | Between GPIO 9 and GND |
Characteristics:
@@ -238,23 +286,16 @@ The LED is driven with various patterns to indicate price level; see “LED Pric
---
## Firmware Features (v6.1.2)
## Firmware Features (v6.2.0)
### Core Display & Pricing
- Data source:
```text
https://api.energy-charts.info/price?bzn=SI
```
- Resolution:
- Prices in **15minute intervals** (`price[]`, `unix_seconds[]`).
- Display shows:
- **Row 0**: Current hour, four 15minute values in compact format (`XX XX XX XX`).
- **Rows 13**: Current hour + next two hours as hourly averages.
- Price calculation:
- Raw MWh prices are converted to **EUR/kWh**.
- Data source: `https://api.energy-charts.info/price?bzn=SI`
- Resolution: 15minute intervals with hourly averages
- Display shows:
- **Row 0**: Current hour, four 15minute values
- **Rows 13**: Current hour + next two hours as hourly averages
- Price calculation: Raw MWh → EUR/kWh with configurable surcharges (power company fee + VAT)
- Two configurable surcharges:
- `POWER_COMPANY_FEE_PERCENTAGE` (default `12.0` %).
- `VAT_PERCENTAGE` (default `22.0` %).
@@ -269,14 +310,17 @@ The LED is driven with various patterns to indicate price level; see “LED Pric
The white LED (GPIO 5) reflects the **current 15minute interval** price:
- Very cheap (`<= 0.05 EUR/kWh`) → smooth breathing.
- Cheap / normal → steady on.
- Moderately expensive → slow blink.
- Expensive → faster blink.
- Very expensive → complex “doubleblink with long on” pattern.
- Negative price or no data → LED off.
| Price Level | LED Behavior |
|-------------|--------------|
| Negative / no data | LED off |
| ≤ 0.05 EUR/kWh | Smooth breathing |
| 0.05 0.15 | Steady on |
| 0.15 0.25 | Slow blink |
| 0.25 0.35 | Fast blink |
| 0.35 0.50 | Double blink |
| > 0.50 | Triple blink pattern |
**Important implementation note (v6.1.2):**
**Important implementation note (from v6.1.2 on):**
- On ESP32, avoid mixing PWM (`analogWrite`) and `digitalWrite` on the same LED pin.
- The firmware now uses `analogWrite(pin, 0/255)` consistently to guarantee the LED is fully off when gated off.
@@ -551,7 +595,7 @@ If NVS does not contain valid WiFi credentials, or if connecting fails repeat
- `DNSServer` (from ESP32 core)
- `WebServer` (from ESP32 core)
- `Preferences` (builtin for ESP32)
3. Open the v6.1 `.ino` file (e.g. `20260130_electricity_ticker_6_1_nvs_daily_fetch.ino`).
3. Open the v6.2.0 `.ino` file (e.g. `ESP32_standalone_electricity_ticker_6_1_2_nvs_daily_fetch.ino`).
4. In Tools:
- Board: `Seeed XIAO ESP32C3`
- Port: choose the correct serial port.
@@ -567,6 +611,7 @@ If NVS does not contain valid WiFi credentials, or if connecting fails repeat
## Versioning & Changelog
- **v6.2.0** DST handling fully fixed via timestamp-based lookups
- **v6.1.2** LED indicator restored (broken in previous version):
- Avoid mixing PWM and `digitalWrite` on the same LED pin (ESP32 LEDC behavior).
- Ensures LED is fully off when gated off; patterns operate correctly.
+26 -35
View File
@@ -2,18 +2,24 @@
## Current firmware
- **Version:** 6.1.2
- **Release date:** 2026-03-11
- **Target MCU:** Seeed XIAO ESP32C3
- **Display:** 20x4 I²C LCD (PCF8574, default address `0x27`)
- **API endpoint:** `https://api.energy-charts.info/price?bzn=SI`
- **Resolution:** 15minute intervals, hourly averages for overview
- **Version:** 6.2.0
- **Release date:** 2026-03-29
- **Target MCU:** Seeed XIAO ESP32C3
- **Display:** 20x4 I²C LCD (PCF8574, default address `0x27`)
- **API endpoint:** `https://api.energy-charts.info/price?bzn=SI`
- **Resolution:** 15minute intervals, hourly averages for overview
## Highlights of v6.1.2
## Highlights of v6.2.0
- Fix: restore proper white LED price indicator behavior on ESP32 by avoiding mixing PWM (`analogWrite`) and `digitalWrite` on the same pin.
- Fix: LED is now truly off when backlight/LED gating turns it off (no more dim glow”).
- Change: `updateLeds()` uses PWM only (`analogWrite(pin, 0/255)`) for off/on and blink toggles.
- **CRITICAL FIX**: DST (Daylight Saving Time) handling is now fully fixed for all days.
- Previously, the code assumed every day has exactly 96 price entries (24h × 4). This caused incorrect price display on DST switch days:
- Spring forward (March): Only 92 entries → wrong prices displayed
- Fall back (October): 100 entries → wrong prices displayed
- **Solution**: All price lookups now use timestamp-based searching through the `unix_seconds` array instead of arithmetic calculation (`hourIndex * 4`).
- New functions: `findPriceIndexForHour()`, `findCurrentPriceIndex()`, `getHourFromPriceIndex()`
- Updated functions: `getHourlyAverage()`, `display15MinuteDetails()`, `displayPriceRow()`, `displayPrimaryList()`, `updateLeds()`
- The ticker now works correctly on all days, including DST switch days, with no manual intervention.
- **Future-proof**: If EU cancels DST, only the `TZ_CET_CEST` string needs updating (one line of code).
For full details, see:
@@ -22,34 +28,19 @@ For full details, see:
## Previous firmware
- **Version:** 6.1.1
- **Release date:** 2026-03-07
- **Target MCU:** Seeed XIAO ESP32C3
- **Display:** 20x4 I²C LCD (PCF8574, default address `0x27`)
- **API endpoint:** `https://api.energy-charts.info/price?bzn=SI`
- **Resolution:** 15minute intervals, hourly averages for overview
- **Version:** 6.1.2
- **Release date:** 2026-03-11
- **Target MCU:** Seeed XIAO ESP32C3
## Highlights of v6.1.1
## Highlights of v6.1.2
- Fix: daily **lowest/highest hourly price marker** now includes **negative** and **0.0** prices.
- Fix: daily average is computed over the number of valid hours (instead of always dividing by 24 even when hours were skipped).
- Fix: restore proper white LED price indicator behavior on ESP32 by avoiding mixing PWM (`analogWrite`) and `digitalWrite` on the same pin.
- Fix: LED is now truly off when backlight/LED gating turns it off (no more "dim glow").
## Earlier firmware
- **Version:** 6.0.0
- **Release date:** 2026-01-27
- **Target MCU:** Seeed XIAO ESP32C3
- **Display:** 20x4 I²C LCD (PCF8574, default address `0x27`)
- **API endpoint:** `https://api.energy-charts.info/price?bzn=SI`
- **Resolution:** 15minute intervals, hourly averages for overview
- **Version:** 6.1.1 (2026-03-07) Daily low/high marker includes negative and zero prices
- **Version:** 6.1.0 (2026-01-30) Midnight fetch and market day detection fixes
- **Version:** 6.0.0 (2026-01-27) NVS storage and daily fetch
## Highlights of v6.0.0
- Single **daily fetch** (on boot / after midnight) instead of hourly.
- **NVS storage** of daily data for resilience to power outages.
- Robust midnight rollover and retry logic:
- First immediate fetch.
- Up to 5 × 10minute retries.
- Then topofhour retries until successful.
- Prevents yesterdays prices from ever being shown as todays.
- Secondary info menu extended with **NVS status** and clear version label.
For detailed history, see [CHANGELOG.md](./CHANGELOG.md).
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB