Fixed a bug in the price display by correcting the function to find the current 15-minute interval instead of the next one.
8.1 KiB
Changelog
All notable changes to this project are documented here.
v6.2.1 – Current Interval Fix (2026‑03‑29)
Summary
Fixed a bug where the display was showing prices one hour ahead of the current time.
Problem: Display Showing Next Hour Instead of Current
Root Cause:
The findCurrentPriceIndex() function was finding the next 15-minute interval (first entry with timestamp >= now), but it should find the current interval (the one we're currently IN).
For example, at 17:57:
- The current 15-minute interval is 17:45-18:00 (price indexed at 17:45)
- The next interval is 18:00-18:15 (price indexed at 18:00)
- The buggy function returned the index for 18:00 instead of 17:45
- Result: Display showed hour 18 instead of hour 17
Solution
The fix calculates the next 15-minute boundary and finds the last entry strictly before that boundary:
// Calculate the next 15-minute boundary
const int QUARTER_SECONDS = 15 * 60; // 900 seconds
time_t nextQuarter = ((now + QUARTER_SECONDS - 1) / QUARTER_SECONDS) * QUARTER_SECONDS;
// Find the last entry strictly before nextQuarter
for (size_t i = unixSeconds.size(); i > 0; i--) {
if ((time_t)unixTime < nextQuarter) {
return (int)(i - 1);
}
}
Example:
- At 17:57: nextQuarter = 18:00, finds last entry < 18:00 = 17:45 ✅
- At 18:00: nextQuarter = 18:15, finds last entry < 18:15 = 18:00 ✅
- At 18:46: nextQuarter = 19:00, finds last entry < 19:00 = 18:45 ✅
v6.2.0 – DST (Daylight Saving Time) Handling Fixed (2026‑03‑29)
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:
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:
// 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 hourfindCurrentPriceIndex()– Finds current 15-minute slot using Unix timestampgetHourFromPriceIndex()– Gets hour from price array index
Updated functions:
getHourlyAverage()– Now uses timestamp lookup instead ofhourIndex * 4display15MinuteDetails()– Timestamp-based with hour verification in loopdisplayPriceRow()– Timestamp-based data index lookupdisplayPrimaryList()– Timestamp-based current hour detectionupdateLeds()– 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) (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 samewhiteLedPin. - 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.
- Some patterns could appear "stuck" or inconsistent.
Solution (v6.1.2):
- LED control is now PWM‑only inside
updateLeds():- Use
analogWrite(whiteLedPin, 0)instead ofdigitalWrite(whiteLedPin, LOW). - Use
analogWrite(whiteLedPin, 255)instead ofdigitalWrite(whiteLedPin, HIGH). - Blink/double‑blink toggles now switch between PWM 0 and 255.
- Use
- This ensures the LED is truly off whenever LED output is gated off.
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 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) { ... } - This had two side effects:
- Negative hourly averages were completely skipped.
- A true price of 0.0 was also skipped (even though 0 can be a valid market price).
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).
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.
Fixed: Stuck on NO_DATA_OFFSET After Midnight
Problem (v6.0.0):
- 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):
processJsonData()now determines the "market day" using the lastunix_secondstimestamp.- A scheduled fetch is only successful if
lastProcessJsonAcceptedToday == true. - Midnight retry logic keeps trying until valid "today" data is received.
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"with Wi‑Fi 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.
- After‑midnight: NVS is overwritten with each successful new‑day dataset.
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.