mirror of
https://github.com/Legolas-2025/Standalone-electricity-price-ticker.git
synced 2026-08-18 12:44:54 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b1fb6ac3d | ||
|
|
4fb5a6bc5f | ||
|
|
20bff8b00a | ||
|
|
0933a06973 | ||
|
|
48819161af |
+205
@@ -2,6 +2,211 @@
|
||||
|
||||
All notable changes to this project are documented here.
|
||||
|
||||
## v7.2 - Button Robustness & Screen-Control Fixes (2026-08-04)
|
||||
|
||||
**Summary**
|
||||
|
||||
Bug-fix release that resolves the three control glitches reported for the v7.1
|
||||
firmware on the Seeed XIAO ESP32‑C3:
|
||||
|
||||
- "It is 20:35 and I cannot scroll the values of the primary screen."
|
||||
- "Double click does not switch to the secondary screen."
|
||||
- "Strange behaviour at the end of the day, if there is no tomorrow's data
|
||||
available yet and the time is let's say 22:15."
|
||||
|
||||
No fee/VAT math, NVS layout, API scheduling or 48-hour scrolling behaviour
|
||||
was changed. The v7.1 negative-price provider fee logic is preserved
|
||||
verbatim.
|
||||
|
||||
### Fix 1 – Primary-screen scroll works at any hour of the day
|
||||
|
||||
**Symptom**
|
||||
|
||||
Clicking the button on the primary screen appeared to do nothing — the
|
||||
display stayed on the current hour and refused to advance.
|
||||
|
||||
**Root cause (two compounding bugs)**
|
||||
|
||||
1. `displayPriceRow()` only blanked past hours while `currentHour < 22`:
|
||||
|
||||
```cpp
|
||||
if (localHourIndex < currentHour && currentHour < 22) {
|
||||
lcd.print(" "); return;
|
||||
}
|
||||
```
|
||||
|
||||
After 22:00 the guard fell through, so the screen was allowed to repaint
|
||||
already-finished morning hours (00:00 – 21:59). The moment the user
|
||||
scrolled forward, the new "top" hour was visually overwritten by the
|
||||
previous morning's data, making the screen look frozen.
|
||||
|
||||
2. `displayPrimaryList()` contained an override:
|
||||
|
||||
```cpp
|
||||
if (currentHour >= 21 && timeOffsetHours > 0) {
|
||||
displayStartHourOffset = 21 + timeOffsetHours;
|
||||
}
|
||||
```
|
||||
|
||||
From 21:00 onward this pinned the top row at `21 + offset`. At 22:15
|
||||
every click computed start = 22+offset, was then clamped to 21+offset,
|
||||
and the user saw no movement.
|
||||
|
||||
**Fix**
|
||||
|
||||
- `displayPriceRow()`: simplified the past-hour guard to
|
||||
`if (localHourIndex < currentHour) blank();` so past hours of today are
|
||||
hidden at every hour of the day, not just before 22:00.
|
||||
- `displayPrimaryList()`: removed the `currentHour >= 21` override
|
||||
entirely. Past-hour blanking is now handled correctly by Fix 1a, so the
|
||||
override is no longer needed.
|
||||
|
||||
### Fix 2 – Double-click reliably toggles to the secondary screen
|
||||
|
||||
**Symptom**
|
||||
|
||||
A quick double-click did nothing — the display stayed on the primary
|
||||
price view. In some cases the screen showed a stuck "Long press detected!
|
||||
Release to refresh" message right after the device booted or was reset.
|
||||
|
||||
**Root cause**
|
||||
|
||||
The double-click path itself was correct; it was being starved by a false
|
||||
"Long press detected!" that fired immediately after every reset. On the
|
||||
ESP32-C3 the button pin (configured as `INPUT_PULLUP`) floats HIGH for a
|
||||
few seconds during boot while the internal pull-up is settling and
|
||||
power-rail noise is ringing. Because `buttonPressStartTime` is
|
||||
initialised to `0`, the long-press detector's predicate
|
||||
`millis() - buttonPressStartTime >= 3000` evaluated to
|
||||
`millis() >= 3000` a few seconds after boot — the firmware interpreted
|
||||
the floating-pin noise as a genuine 3-second hold, cleared the LCD to:
|
||||
|
||||
```
|
||||
Long press detected!
|
||||
Release to refresh
|
||||
```
|
||||
|
||||
…and from then on the user could not see any prices to click on
|
||||
(single- and double-click recognisers both still ran, but their visible
|
||||
effect was hidden behind the long-press splash).
|
||||
|
||||
**Fix**
|
||||
|
||||
Added a single new global flag and gated the long-press detector on it:
|
||||
|
||||
```cpp
|
||||
// New flag, set true the first time the pin is observed LOW after boot
|
||||
bool buttonEverReleased = false;
|
||||
|
||||
// In the "reading went LOW" branch:
|
||||
if (reading == LOW) {
|
||||
buttonPressStartTime = millis();
|
||||
longPressDetected = false;
|
||||
buttonEverReleased = true; // v7.2
|
||||
}
|
||||
|
||||
// In the long-press trip block:
|
||||
if (buttonState == LOW && !longPressDetected && buttonEverReleased) { // v7.2
|
||||
if (millis() - buttonPressStartTime >= longPressThreshold) {
|
||||
longPressDetected = true;
|
||||
// ... show "Long press detected! Release to refresh"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The detector now refuses to fire until the user (or the power-rail noise)
|
||||
has released the button at least once. The v7.1 button logic (50 ms
|
||||
debounce, 3 s long-press threshold, 500 ms double-click window, TTP223
|
||||
timing) is otherwise preserved verbatim.
|
||||
|
||||
### Fix 3 – End-of-day scroll is stable with no tomorrow data
|
||||
|
||||
**Symptom**
|
||||
|
||||
Around 22:00 – 23:59 with no tomorrow data in the buffer, scrolling
|
||||
through the primary screen produced a screen full of blank past-hour
|
||||
rows, or wrapped the start hour back to 00:00 in a confusing way.
|
||||
|
||||
**Root cause**
|
||||
|
||||
`advanceDisplayOffset()` contained a hack:
|
||||
|
||||
```cpp
|
||||
if (allowedAhead < 2 && currentHour >= 21 && !isTomorrowDataAvailable)
|
||||
allowedAhead = 2;
|
||||
```
|
||||
|
||||
At 22:00 (with no tomorrow data) this let the user click past hour 23
|
||||
into "24:00 / 25:00". `displayStartHourOffset` then exceeded
|
||||
`maxOffsetLimit = 23` and the wrap logic:
|
||||
|
||||
```cpp
|
||||
if (displayStartHourOffset > maxOffsetLimit)
|
||||
displayStartHourOffset %= (maxOffsetLimit + 1);
|
||||
```
|
||||
|
||||
…wrapped it back to 0. Combined with the buggy past-hour blanking in
|
||||
Fix 1a, the result was a screen full of stale blank rows from 00:00 to
|
||||
21:59.
|
||||
|
||||
**Fix**
|
||||
|
||||
Removed the `allowedAhead = 2` hack. The natural cap is now sufficient:
|
||||
|
||||
- 22:00 → can step 22 → 23, then wraps back to current
|
||||
- 23:00 → cannot step forward at all
|
||||
|
||||
No wrap to 00:00 of the previous day is reachable any more, and Fix 1a
|
||||
ensures any past hour that does briefly land on the screen is blanked
|
||||
correctly.
|
||||
|
||||
### Fix 4 (bonus) – Auto-return timer now resets on every click
|
||||
|
||||
**Symptom**
|
||||
|
||||
Scrolling through the 20-line secondary status page (4 lines at a time)
|
||||
did not push the 10 s auto-return-to-top timeout forward — the display
|
||||
could jump back to the primary price view mid-read.
|
||||
|
||||
**Root cause**
|
||||
|
||||
`lastButtonActivity` and `autoScrollExecuted` were only updated inside the
|
||||
primary-list branches of `advanceDisplayOffset()`. The secondary-list
|
||||
branch scrolled the offset but did not touch the timer.
|
||||
|
||||
**Fix**
|
||||
|
||||
Moved the two resets to the very top of `advanceDisplayOffset()`:
|
||||
|
||||
```cpp
|
||||
void advanceDisplayOffset() {
|
||||
// Any successful click (single, double, or long-press-release) ends up
|
||||
// here, so this is the single place that resets the auto-return timer.
|
||||
lastButtonActivity = millis();
|
||||
autoScrollExecuted = false;
|
||||
// ... rest of the function unchanged
|
||||
}
|
||||
```
|
||||
|
||||
### Other changes (cosmetic / non-behavioural)
|
||||
|
||||
- Filename and three user-visible version strings bumped to v7.2:
|
||||
- `connectToWiFi()` splash: `"Elec. Rate SI v7.1"` → `"v7.2"`
|
||||
- `displaySecondaryList()` credit line (line 18): `"price ticker v7.1"` → `"v7.2"`
|
||||
- `setup()` debug banner: `"Starting Dynamic Electricity Ticker v7.1 (Neg Price Fee) - DST-SAFE"`
|
||||
→ `"Starting Dynamic Electricity Ticker v7.2 (Button Robustness) - DST-SAFE"`
|
||||
- Inline comments added at each fix site explaining what v7.1 did wrong,
|
||||
so future maintainers do not re-introduce the overrides.
|
||||
- New global flag `bool buttonEverReleased` (see Fix 2).
|
||||
|
||||
### Files changed
|
||||
|
||||
| File | Change |
|
||||
|---|---|
|
||||
| `ESP32_standalone_electricity_ticker_7_2.ino` | Filename, header comment, `buttonEverReleased` flag, `handleButton()` long-press gate, `displayPriceRow()` past-hour guard, `displayPrimaryList()` removed override, `advanceDisplayOffset()` timer reset, three version strings |
|
||||
|
||||
---
|
||||
|
||||
## v7.1 - Negative Price Provider Fee (2026-04-06)
|
||||
|
||||
**Summary**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,12 +11,80 @@ This project is an Arduino‑IDE‑friendly 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 7.0** with the revolutionary **Rolling 48-Hour Logic & Midnight Bridge** system.
|
||||
The latest sketch implements **Version 7.2** — a bug-fix release that
|
||||
restores primary-screen scrolling, fixes double-click → secondary-screen
|
||||
switching on the ESP32-C3, and stabilises end-of-day behaviour when no
|
||||
tomorrow data is available yet. The v7.1 negative-price provider fee logic
|
||||
is preserved unchanged.
|
||||
|
||||
---
|
||||
|
||||
## Version Highlights
|
||||
|
||||
### v7.2 - Button Robustness & Screen-Control Fixes (2026-08-04)
|
||||
|
||||
Bug-fix release. Resolves the three control glitches reported for v7.1:
|
||||
"cannot scroll the primary screen at 20:35", "double-click does not switch
|
||||
to the secondary screen", and "strange end-of-day behaviour at 22:15 with
|
||||
no tomorrow data". No fee/VAT math, NVS layout, API scheduling or
|
||||
48-hour scrolling behaviour was changed.
|
||||
|
||||
- **Fix 1 – Primary-screen scroll** works at any hour of the day:
|
||||
past-hour blanking in `displayPriceRow()` simplified from
|
||||
`currentHour < 22` to a plain `localHourIndex < currentHour`, and the
|
||||
`currentHour >= 21` override in `displayPrimaryList()` removed.
|
||||
- **Fix 2 – Double-click** reliably toggles to the secondary screen.
|
||||
A new `bool buttonEverReleased` flag prevents a false
|
||||
"Long press detected!" from firing right after every reset on the
|
||||
ESP32-C3 (floating button pin + `buttonPressStartTime = 0` was making
|
||||
the long-press detector trip on phantom 3-second holds during boot).
|
||||
- **Fix 3 – End-of-day scroll** is stable with no tomorrow data. The
|
||||
`allowedAhead = 2` hack in `advanceDisplayOffset()` is gone, so the
|
||||
user can no longer scroll into "24:00 / 25:00" and wrap back to 00:00.
|
||||
- **Fix 4 (bonus) – Auto-return timer** now resets on every click, so
|
||||
scrolling the secondary status page no longer jumps back to the primary
|
||||
view mid-read.
|
||||
|
||||
See [`VERSION.md`](./VERSION.md) for the highlights and
|
||||
[`CHANGELOG.md`](./CHANGELOG.md) for full implementation details.
|
||||
|
||||
---
|
||||
|
||||
### v7.1 - Negative Price Provider Fee (2026-04-06)
|
||||
|
||||
Added a separate configurable provider fee for negative spot prices, correctly
|
||||
modelling contracts where the provider's fee structure differs between positive
|
||||
and negative market prices.
|
||||
|
||||
**New constant:**
|
||||
```cpp
|
||||
const float NEG_PRICE_COMPANY_FEE_PERCENTAGE = 30.0;
|
||||
```
|
||||
|
||||
**Price calculation is now:**
|
||||
|
||||
| Market price | Formula |
|
||||
|---|---|
|
||||
| Positive (`raw >= 0`) | `raw × (1 + POWER_COMPANY_FEE_PERCENTAGE/100) × (1 + VAT_PERCENTAGE/100)` |
|
||||
| Negative (`raw < 0`) | `raw × (1 - NEG_PRICE_COMPANY_FEE_PERCENTAGE/100) × (1 + VAT_PERCENTAGE/100)` |
|
||||
|
||||
The switch happens on the **raw API price** before any multiplier is applied.
|
||||
VAT is applied to both cases, consistent with net billing contracts where VAT
|
||||
is calculated on the monthly net sum (mathematically equivalent due to VAT
|
||||
being a linear multiplier).
|
||||
|
||||
**Key values for `NEG_PRICE_COMPANY_FEE_PERCENTAGE`:**
|
||||
|
||||
| Value | Meaning |
|
||||
|---|---|
|
||||
| `30.0` | Provider keeps 30%, pays you 70% of the negative market price |
|
||||
| `0.0` | Provider passes the full negative price to you (no fee deducted) |
|
||||
|
||||
All 5 fee calculation sites updated: `updateLeds()`, `format15MinPrice()`,
|
||||
`displayPriceRow()`, `displaySecondaryList()` (daily average), and version strings.
|
||||
|
||||
---
|
||||
|
||||
### v7.0 - Rolling 48-Hour Logic & Midnight Bridge (Major Upgrade)
|
||||
|
||||
This version is the **"Golden Build"** for this hardware platform. It combines all hardware stability fixes from v6.2.4 with a revolutionary new 48-hour price prediction system.
|
||||
@@ -37,7 +105,7 @@ This version is the **"Golden Build"** for this hardware platform. It combines a
|
||||
- **LED Indicators Pinned to Current**: White LED always reflects actual current prices
|
||||
|
||||
### v6.2.4 - Exact-boundary display refresh bug (critical fix of v6.2.3 update)
|
||||
- Problem: At the exact top of the hour (e.g., 20:00:00), the display automatically refreshed but showed the PREVIOUS hour's data (19:00). This happened because the "next-boundary" rounding logic in findCurrentPriceIndex() incorrectly excluded the current interval if the time was exactly on the boundary.
|
||||
- Problem: At the exact top of the hour (e.g., 20:00:00), the display automatically refreshed but showed the PREVIOUS hour's data (19:00). This happened because the "next-boundary" rounding logic in findCurrentPriceIndex() pre-calculated the next boundary.
|
||||
- Fix: Simplified findCurrentPriceIndex() to use a robust "last entry <= now" comparison. This ensures the display transitions to the new hour instantaneously at XX:00:00.
|
||||
|
||||
### v6.2.3 - State-based display refresh logic (critical fix of v6.2.2 update)
|
||||
@@ -57,7 +125,7 @@ This version is the **"Golden Build"** for this hardware platform. It combines a
|
||||
|
||||
Major update 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.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 lookups throughout.
|
||||
- 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.
|
||||
@@ -194,16 +262,16 @@ if (dataIndex == lowIdx) {
|
||||
|
||||
---
|
||||
|
||||
## Behavior & Display States (v7.0)
|
||||
## Behavior & Display States (v7.2)
|
||||
|
||||
The display changes based on which data buffer is being used and the status of the fetch:
|
||||
|
||||
| **State** | **Display Output** | **LED Behavior** |
|
||||
|----------|-------------------|-----------------|
|
||||
| **Normal (Today)** | Shows current prices and 15-min details. Hours are marked as HH:00. | White LED reflects current price status (Breathe, Solid, or Blink). |
|
||||
| **Scrolling (Tomorrow)** | Future prices are displayed. Hours are marked with HH:>> to indicate "Tomorrow". | **Pinned to Today:** The LEDs continue showing the _actual current_ price status even while you scroll through tomorrow. |
|
||||
| **Scrolling (Tomorrow)** | Future prices are displayed. Hours are marked with HH:>> to indicate "Tomorrow". | **Pinned to Today:** The LEDs continue showing the _actual current_ price status even while browsing future hours. |
|
||||
| **No Data** | Displays: "No data for today, Press & hold to, refresh manually." | White LED is turned **OFF** to avoid misleading price signals. |
|
||||
| **Connecting** | "Elec. Rate SI v7.0" followed by "Connecting..." and progress dots. | Built-in LED is **OFF** until connection is established. |
|
||||
| **Connecting** | "Elec. Rate SI v7.2" followed by "Connecting..." and progress dots. | Built-in LED is **OFF** until connection is established. |
|
||||
|
||||
### Key UX Principle: LEDs Stay Pinned to Current Time
|
||||
|
||||
@@ -264,7 +332,7 @@ const char* api_url = "https://api.energy-charts.info/price?bzn=SI";
|
||||
|
||||
to any supported BZN.
|
||||
|
||||
All available bidding zones (from the original README):
|
||||
All available bidding zones:
|
||||
|
||||
- `AT` ‑ Austria
|
||||
- `BE` ‑ Belgium
|
||||
@@ -318,7 +386,7 @@ All available bidding zones (from the original README):
|
||||
|
||||
## Hardware Setup (Detailed)
|
||||
|
||||
This section merges the original v5.5 instructions with the current v7.0 hardware expectations.
|
||||
This section merges the original v5.5 instructions with the current v7.2 hardware expectations.
|
||||
Follow it carefully to reproduce the working setup.
|
||||
|
||||
### 1. Microcontroller
|
||||
@@ -468,7 +536,7 @@ The LED is driven with various patterns to indicate price level; see "LED Price
|
||||
|
||||
---
|
||||
|
||||
## Firmware Features (v7.0)
|
||||
## Firmware Features (v7.2)
|
||||
|
||||
### Core Display & Pricing
|
||||
|
||||
@@ -478,10 +546,18 @@ The LED is driven with various patterns to indicate price level; see "LED Price
|
||||
- **Row 0**: Current hour, four 15‑minute values
|
||||
- **Rows 1–3**: Current hour + next two hours as hourly averages
|
||||
- **v7.0 Feature**: Tomorrow's hours are marked with `HH:>>` format
|
||||
- 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` %).
|
||||
- Price calculation: Raw MWh → EUR/kWh with configurable surcharges
|
||||
- Three configurable constants:
|
||||
- `POWER_COMPANY_FEE_PERCENTAGE` (default `12.0` %) — fee for **positive** spot prices
|
||||
- `NEG_PRICE_COMPANY_FEE_PERCENTAGE` (default `30.0` %) — fee kept by provider on **negative** spot prices
|
||||
- `VAT_PERCENTAGE` (default `22.0` %)
|
||||
- **v7.1**: Positive and negative spot prices use independent fee multipliers:
|
||||
|
||||
| Market price | Formula |
|
||||
|---|---|
|
||||
| Positive (`raw >= 0`) | `raw × (1 + POWER_COMPANY_FEE_PERCENTAGE/100) × (1 + VAT_PERCENTAGE/100)` |
|
||||
| Negative (`raw < 0`) | `raw × (1 - NEG_PRICE_COMPANY_FEE_PERCENTAGE/100) × (1 + VAT_PERCENTAGE/100)` |
|
||||
|
||||
- LCD:
|
||||
- `LiquidCrystal_I2C` with custom characters for:
|
||||
- Local language letters.
|
||||
@@ -535,7 +611,7 @@ One button (or touch) on GPIO 4 controls the UI:
|
||||
- Toggles between:
|
||||
- Primary price view.
|
||||
- Secondary status/info view.
|
||||
- **Long press (~3 seconds in v6.1)**:
|
||||
- **Long press (~3 seconds)**:
|
||||
- While held:
|
||||
- LCD shows: "Long press detected! Release to refresh".
|
||||
- On release:
|
||||
@@ -676,7 +752,7 @@ In `processJsonData()`:
|
||||
|
||||
A **secondary screen** (toggled via **double‑click**) provides 20 lines of status information, displayed 4 lines at a time:
|
||||
|
||||
Typical content (updated for v7.0):
|
||||
Typical content (updated for v7.2):
|
||||
|
||||
1. Current date and time (`HH:MM DD.MM.YYYY`)
|
||||
2. Separator line (`--------------------`)
|
||||
@@ -690,7 +766,7 @@ Typical content (updated for v7.0):
|
||||
10. Local IP address
|
||||
11. API success rate (`API: xx% (succ/fail)`)
|
||||
12. Device uptime in days, hours, minutes
|
||||
13–16. **NVS status block** (enhanced for v7.0):
|
||||
13–16. **NVS status block**:
|
||||
- `NVS status:`
|
||||
- `Data day: DD.MM.YYYY` or `Data day: none`
|
||||
- `Last save: DD.MM.YY` or `Last save: none`
|
||||
@@ -698,8 +774,8 @@ Typical content (updated for v7.0):
|
||||
17–20. Credits and version:
|
||||
- `energy-charts.info`
|
||||
- `dynamic electricity`
|
||||
- `price ticker v7.0`
|
||||
- `by Legolas-2025` (or your preferred credit line)
|
||||
- `price ticker v7.2`
|
||||
- `by Legolas-2025`
|
||||
|
||||
---
|
||||
|
||||
@@ -731,7 +807,7 @@ If NVS does not contain valid Wi‑Fi credentials, or if connecting fails repeat
|
||||
- `DNSServer` (from ESP32 core)
|
||||
- `WebServer` (from ESP32 core)
|
||||
- `Preferences` (built‑in for ESP32)
|
||||
3. Open the v7.0 `.ino` file (e.g. `ESP32_standalone_electricity_ticker_v7_0_Rolling_48H.ino`).
|
||||
3. Open the v7.2 `.ino` file (`ESP32_standalone_electricity_ticker_7_2.ino`).
|
||||
4. In Tools:
|
||||
- Board: `Seeed XIAO ESP32C3`
|
||||
- Port: choose the correct serial port.
|
||||
@@ -741,12 +817,20 @@ If NVS does not contain valid Wi‑Fi credentials, or if connecting fails repeat
|
||||
- NTP sync messages.
|
||||
- NVS load/save status.
|
||||
- Midnight rollover and retry debug output.
|
||||
- **v7.0 NEW**: Tomorrow fetch logs (`Fetching Tomorrow's Data...`)
|
||||
- Tomorrow fetch logs (`Fetching Tomorrow's Data...`)
|
||||
|
||||
---
|
||||
|
||||
## Versioning & Changelog
|
||||
|
||||
- **v7.2** – Button robustness & screen-control fixes (2026-08-04). See
|
||||
highlights above; full details in [`CHANGELOG.md`](./CHANGELOG.md).
|
||||
- **v7.1** – Negative price provider fee:
|
||||
- Separate `NEG_PRICE_COMPANY_FEE_PERCENTAGE` constant (default `30.0` %)
|
||||
- Positive prices: `raw × (1 + pos_fee) × (1 + VAT)`
|
||||
- Negative prices: `raw × (1 - neg_fee) × (1 + VAT)`
|
||||
- Switch on raw API price before any multiplier
|
||||
- All 5 fee calculation sites updated
|
||||
- **v7.0** – Rolling 48-Hour Logic & Midnight Bridge:
|
||||
- Dual-buffer NVS system for today and tomorrow data
|
||||
- Midnight Bridge for seamless day rollover
|
||||
|
||||
+88
-2
@@ -2,13 +2,99 @@
|
||||
|
||||
## Current firmware
|
||||
|
||||
- **Version:** 7.1
|
||||
- **Release date:** 2026-04-06
|
||||
- **Version:** 7.2
|
||||
- **Release date:** 2026-08-04
|
||||
- **Target MCU:** Seeed XIAO ESP32‑C3
|
||||
- **Display:** 20x4 I²C LCD (PCF8574, default address `0x27`)
|
||||
- **API endpoint:** `https://api.energy-charts.info/price?bzn=SI`
|
||||
- **Resolution:** 15‑minute intervals, hourly averages for overview
|
||||
|
||||
## Highlights of v7.2
|
||||
|
||||
### Button Robustness & Screen-Control Fixes
|
||||
|
||||
Bug-fix release that resolves the three control glitches reported for the v7.1
|
||||
firmware on the Seeed XIAO ESP32‑C3: the primary screen looked unscrollable,
|
||||
double-clicks failed to switch to the secondary status screen, and the end-of-day
|
||||
behaviour (no tomorrow data yet) was unstable around 22:00–23:59. No fee/VAT
|
||||
math, NVS layout, API scheduling or 48-hour scrolling behaviour was changed.
|
||||
|
||||
#### Fix 1 – Primary-screen scroll now works at any hour of the day
|
||||
|
||||
Two compounding bugs in `displayPrimaryList()` and `displayPriceRow()` were
|
||||
cancelling each other out and made single-click scrolling look dead:
|
||||
|
||||
- `displayPriceRow()` only blanked past hours while `currentHour < 22`. After
|
||||
22:00 the screen could repaint already-finished morning hours, so as soon
|
||||
as the user scrolled forward the new "top" hour was visually over-written
|
||||
by the previous morning's data. The guard is now
|
||||
`if (localHourIndex < currentHour) blank();`, so past hours of today are
|
||||
hidden at every hour of the day.
|
||||
- `displayPrimaryList()` contained an override
|
||||
`if (currentHour >= 21 && timeOffsetHours > 0) displayStartHourOffset = 21 + timeOffsetHours;`
|
||||
which pinned the top row at 21:00 + offset from 21:00 onward. At 22:15
|
||||
every click computed start = 22+offset, was then clamped to 21+offset, and
|
||||
the user saw no movement. The override is no longer needed and has been
|
||||
removed.
|
||||
|
||||
#### Fix 2 – Double-click on the secondary screen now fires reliably
|
||||
|
||||
The double-click path itself was correct; it was being starved by a false
|
||||
"Long press detected!" message that fired immediately after every reset.
|
||||
On the ESP32-C3 the button pin (`INPUT_PULLUP`) floats HIGH for a few
|
||||
seconds during boot, while `buttonPressStartTime` is initialised to `0`.
|
||||
As soon as `millis()` crossed the 3 s threshold, the long-press detector
|
||||
tripped on a phantom 3-second hold, cleared the LCD to
|
||||
"Long press detected! / Release to refresh", and from then on the user
|
||||
could not see any prices to click on (single- and double-click recognisers
|
||||
both still ran, but their visible effect was hidden behind the long-press
|
||||
splash).
|
||||
|
||||
Fix: a new `bool buttonEverReleased` is set to `true` the first time the
|
||||
pin is observed LOW after boot, and the long-press detector is gated on
|
||||
it: `if (buttonState == LOW && !longPressDetected && buttonEverReleased)`.
|
||||
The detector refuses to fire until the user (or the power-rail noise) has
|
||||
released the button at least once. The v7.1 button logic (debounce, 3 s
|
||||
long-press threshold, 500 ms double-click window, TTP223 timing) is
|
||||
otherwise preserved verbatim.
|
||||
|
||||
#### Fix 3 – End-of-day scroll is now stable when no tomorrow data is available
|
||||
|
||||
`advanceDisplayOffset()` contained a hack
|
||||
`if (allowedAhead < 2 && currentHour >= 21 && !isTomorrowDataAvailable) allowedAhead = 2;`
|
||||
which at 22:00 (with no tomorrow data) let the user click past hour 23 into
|
||||
"24:00 / 25:00", where `displayStartHourOffset` wrapped back to 0 and filled
|
||||
the LCD with the now-unblanked past-hour rows. The hack has been removed;
|
||||
the natural cap is now sufficient:
|
||||
|
||||
- **22:00** → can step 22 → 23, then wraps back to current
|
||||
- **23:00** → cannot step forward at all
|
||||
|
||||
No wrap to 00:00 of the previous day is reachable any more.
|
||||
|
||||
#### Fix 4 (bonus) – Auto-return timer now resets on every click
|
||||
|
||||
`lastButtonActivity` / `autoScrollExecuted` were only updated in the
|
||||
primary-list branches of `advanceDisplayOffset()`. Scrolling the secondary
|
||||
status page therefore did not push the 10 s auto-return-to-top timeout
|
||||
forward. The two resets are now at the top of `advanceDisplayOffset()` so
|
||||
every successful click (single, double, or long-press-release) refreshes
|
||||
the timer regardless of which list is showing.
|
||||
|
||||
#### Cosmetic / non-behavioural changes
|
||||
|
||||
- Filename and three user-visible version strings bumped to v7.2:
|
||||
- `connectToWiFi()` splash: `"Elec. Rate SI v7.1"` → `"v7.2"`
|
||||
- `displaySecondaryList()` credit line: `"price ticker v7.1"` → `"v7.2"`
|
||||
- `setup()` debug banner: `"v7.1 (Neg Price Fee)"` → `"v7.2 (Button Robustness)"`
|
||||
- Inline comments added at each fix site explaining what v7.1 did wrong,
|
||||
so future maintainers do not re-introduce the overrides.
|
||||
- New global flag `bool buttonEverReleased` (see Fix 2).
|
||||
|
||||
See `CHANGELOG.md` for full implementation details.
|
||||
|
||||
---
|
||||
|
||||
## Highlights of v7.1
|
||||
|
||||
### Negative Price Provider Fee
|
||||
|
||||
Reference in New Issue
Block a user