mirror of
https://github.com/Legolas-2025/Standalone-electricity-price-ticker.git
synced 2026-08-17 12:34:55 +02:00
Restores LED indicator functionality by ensuring PWM control for the white LED, fixing issues from v6.1.1 where the LED remained dimly lit and patterns were inconsistent.
223 lines
8.8 KiB
Markdown
223 lines
8.8 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to this project are documented here.
|
||
|
||
---
|
||
|
||
## v6.1.2 – LED Indicator Restored (ESP32 PWM Fix) (2026‑03‑11)
|
||
|
||
**Summary**
|
||
|
||
This release fixes a regression introduced in **v6.1.1** where the **white LED price indicator** could remain **dimly lit** even when the LCD backlight turned off, and the intended **blink/breathe patterns** no longer behaved correctly.
|
||
|
||
### Fixed: White LED Stuck Dim / Patterns Broken (PWM vs Digital)
|
||
|
||
**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`.
|
||
- 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.
|
||
|
||
**Solution (v6.1.2):**
|
||
|
||
- LED control is now **PWM‑only** inside `updateLeds()`:
|
||
- Use `analogWrite(whiteLedPin, 0)` instead of `digitalWrite(whiteLedPin, LOW)`.
|
||
- Use `analogWrite(whiteLedPin, 255)` instead of `digitalWrite(whiteLedPin, HIGH)`.
|
||
- Blink/double‑blink 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).
|
||
|
||
---
|
||
|
||
## v6.1.1 – Daily Min/Max Includes Negative & Zero Prices (2026‑03‑07)
|
||
|
||
**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[...]
|
||
|
||
### Fixed: Daily Low/High Marker Ignored Negative & Zero Prices
|
||
|
||
**Problem (v6.1.0):**
|
||
|
||
- In `processJsonData()` the daily min/max scan used:
|
||
|
||
- `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×15‑minute 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).
|
||
|
||
---
|
||
|
||
## v6.1.0 – Midnight Fetch & Market Day Fix (2026‑01‑30)
|
||
|
||
**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 [...]
|
||
|
||
### 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 **yesterday’s 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 long‑press triggered a fresh fetch at a time when the API finally returned recognized “today” data, or
|
||
- The device was rebooted later in the day.
|
||
|
||
**Solution (v6.1.0):**
|
||
|
||
1. **Market Day Detection**:
|
||
- `processJsonData()` now determines the “market day” using the **last** `unix_seconds` timestamp from the API’s dataset (assumed to cover one full day in 15‑minute 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 “today’s” 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 market‑day dataset appears, instead of giving up after the first HTTP 200.
|
||
|
||
---
|
||
|
||
### Changed: After‑Midnight 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 today’s 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 long‑press refresh**:
|
||
- Still triggers immediate fetch via `nextScheduledFetchTime = now;`.
|
||
- Now also respects the improved “today” detection; “yesterday’s” 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.
|
||
|
||
---
|
||
|
||
## v6.0.0 – NVS Storage & Daily Fetch (2026‑01‑27)
|
||
|
||
**Summary**
|
||
|
||
First major redesign focused on reducing API traffic and improving resilience using non‑volatile storage.
|
||
|
||
### New
|
||
|
||
- NVS namespace `"my-ticker"` introduced with keys:
|
||
- `ssid`, `pass` – Wi‑Fi 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.
|
||
- After‑midnight behavior:
|
||
- NVS is overwritten with each successful new‑day dataset.
|
||
- In‑RAM data for yesterday is invalidated at day rollover.
|
||
|
||
### UI / Menu
|
||
|
||
- Primary list:
|
||
- 15‑minute 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.
|
||
- Wi‑Fi RSSI, IP address.
|
||
- API success rate, uptime.
|
||
- NVS status block.
|
||
- Credits & version line.
|
||
|
||
---
|
||
|
||
## v5.x – Earlier Versions
|
||
|
||
Earlier versions (v5.x and below) had:
|
||
|
||
- No NVS‑based caching of daily API data.
|
||
- More frequent API calls (e.g., hourly refresh pattern).
|
||
- Less robust handling of DST and daily boundaries.
|
||
|
||
For exact details, see older `.ino` files and their header comments in this repository.
|