Compare commits

...
23 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
Amir dcc434f7e4 Update README for version 6.1.2 changes 2026-03-11 20:19:54 +01:00
Amir 6c7a66be8e Bump version to 6.1.2 and update highlights
Updated version information to 6.1.2 with release date 2026-03-11. Added highlights and fixes for the new version.
2026-03-11 20:05:01 +01:00
Amir e1ad1b01c3 Fix LED indicator issues in v6.1.2 release
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.
2026-03-11 20:04:13 +01:00
Amir f77f50f1d4 Update to v6.1.2 - broken LED indicator fix
VERSION 6.1.2 CHANGES (2026-03-11):
  ----------------------------------
FIX: Restore correct white LED price indicator behavior (to mach the one in prrevious v6.1) on ESP32 (XIAO ESP32C3) by avoiding mixing analogWrite() (LEDC PWM) and digitalWrite() on the same pin. When LED should be OFF we now use analogWrite(whiteLedPin, 0) (instead of digitalWrite LOW). When LED should be fully ON we now use analogWrite(whiteLedPin, 255) (instead of digitalWrite HIGH). Blink / double-blink patterns now toggle between PWM 0 and 255 so the LED is truly off when backlight/presence logic turns LEDs off (no more dimly-lit LED).

NOTE: This version is identical to v6.1.1 except for: updateLeds() LED control fix (PWM-only on whiteLedPin), UI version strings ("v6.1.2") & header comment version/date.
2026-03-11 20:01:08 +01:00
Amir 45cabc17dd Update to v6.1.2 - broken LED indicator fix
VERSION 6.1.2 CHANGES (2026-03-12):
  ----------------------------------
Fixed LED indicator not working properly (constantly dimly lit, not responding to price patterns, staying on when LCD backlight turns off). Root cause: Mixing analogWrite() and digitalWrite() on ESP32 causes PWM channel to remain active even after digitalWrite(LOW), resulting in dim LED. LED now properly turns off when backlight is off (areLedsOn = false). LED now correctly shows breathing/blinking patterns based on price thresholds.

  VERSION 6.1.1 CHANGES (2026-03-07):
  ----------------------------------
Fixed daily lowest/highest hourly indicator (and daily average) now correctly considers ALL hourly averages, including negative values and 0.0 (which can be real prices). The daily average is now computed using the number of valid hours actually present in the dataset (validHourCount), instead of always dividing by 24.

  NOTE:
  - This version includes all fixes from v6.1.1 plus the LED fix.
2026-03-11 19:50:48 +01:00
Amir fedef66ae0 Update to v6.1.1 - daily lowest hourly indicator fix
NOTE:
  - This version is identical to v6.1 except for:
      * processJsonData() min/max/average fix
      * UI version strings ("v6.1.1")
      * header comment version/date
2026-03-11 18:48:55 +01:00
Amir 65fdb14cec Fix LED indicator stuck on; bump to v6.1.2 2026-03-11 18:32:23 +01:00
Amir 3da3dec344 Wiring diagram for XIAO ESP32-C3 connections 2026-03-07 11:31:14 +01:00
Amir 6a6cf113b3 Wiring diagram and peripheral details
Wiring diagram and notes for the Electricity Price Ticker project, including optional peripherals and GPIO connections.
2026-03-07 11:17:58 +01:00
Amir 26b1a309e1 Add files via upload 2026-03-07 11:05:41 +01:00
Amir 4c5d7d2ebc Price ticker v6.1.1 - lowest price indicator update
NOTE:  This version is identical to v6.1 except for the FIX: Daily lowest/highest hourly indicator (and daily average) now correctly considers ALL hourly averages, including negative values and 0.0 (which can be real prices).
2026-03-07 10:40:18 +01:00
Amir b54c40cdf7 Delete ESP32_stanadlone_electricity_ticker_6_1_1.ino 2026-03-07 10:29:47 +01:00
Amir 08db5888cb Update VERSION.md for firmware 6.1.1 release
Updated version information for firmware 6.1.1, including fixes for price markers and daily average calculations.
2026-03-07 10:19:23 +01:00
Amir 8f1f017232 Update README for firmware version 6.1.1
Updated README to reflect changes in firmware version from 6.1.0 to 6.1.1, including fixes for daily low/high hourly markers and other enhancements.
2026-03-07 10:17:10 +01:00
Amir 189350e4fe Fix daily min/max calculations for negative and zero prices
This release addresses a bug in the daily min/max price calculations, ensuring that negative and zero prices are now included. It also corrects the daily average computation by considering all valid hours.
2026-03-07 10:08:48 +01:00
Amir 827623112f Create new sketch file ESP32_stanadlone_electricity_ticker_6_1_1.ino by copying and updating from v6.1 2026-03-07 10:01:08 +01:00
Amir fd5dad4cb5 Delete 20251005_electricity_ticker_10_5.5_latest_scrl23_old.ino 2026-02-01 22:11:16 +01:00
9 changed files with 5445 additions and 1655 deletions
File diff suppressed because it is too large Load Diff
+139 -122
View File
@@ -4,116 +4,154 @@ 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**
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.
- Some patterns could appear "stuck" or inconsistent.
**Solution (v6.1.2):**
- LED control is now **PWMonly** inside `updateLeds()`:
- 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.
---
## v6.1.1 Daily Min/Max Includes Negative & Zero Prices (20260307)
**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:
```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).
**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).
---
## v6.1.0 Midnight Fetch & Market Day Fix (20260130)
**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 aftermidnight retry schedule.
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.
---
@@ -125,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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+92
View File
@@ -0,0 +1,92 @@
# Hardware Wiring Diagram (Seeed XIAO ESP32C3 Electricity Price Ticker)
This document depicts the wiring for the **Electricity Price Ticker** project based on the connection instructions in `README.md`.
It includes:
- Required peripherals (LCD + button)
- Supported optional peripherals (presence sensor + white LED + optional TTP223 touch button alternative)
> Further reference (official board documentation):
> - https://wiki.seeedstudio.com/XIAO_ESP32C3_Getting_Started/
> Notes:
> - Always connect **all grounds together** (ESP32 GND, LCD GND, sensor GND, LED GND).
> - Verify your **XIAO ESP32C3 pinout** in the Seeed documentation link above.
> - **Important:** The **10 kΩ pull-down resistor on D9 is mandatory at all times** (even if the presence sensor is not connected).
> - Button input is on **D2**. You may use either a **mechanical pushbutton** (default) *or* a **TTP223 touch module** (alternative), but not both in parallel unless you know what youre doing.
---
## 1) Wiring overview (diagram)
```mermaid
flowchart TB
MCU["Seeed XIAO ESP32-C3"]:::mcu
LCD["20x4 I2C LCD 2004<br/>PCF8574 backpack<br/>I2C addr 0x27"]:::lcd
%% Button input options (XIAO D2)
D2NODE["D2 node<br/>(buttonPin input)"]:::io
BTN["Mechanical pushbutton<br/>default option<br/>active LOW to GND"]:::btn
TTP["TTP223 capacitive touch<br/>optional alternative<br/>OUT is HIGH when touched"]:::touch
%% Presence-sensor input stage (XIAO D9) - always present
D9NODE["D9 node<br/>(presencePin input)"]:::io
RPD["10k pull-down resistor<br/>D9 to GND<br/>MANDATORY"]:::res
PRES["RCWL-0516 presence sensor<br/>optional"]:::pres
%% White LED output (XIAO D3)
LED1["White indicator LED<br/>optional"]:::led
PSU5V["5V supply<br/>USB-C or regulated 5V"]:::pwr
V33["3.3V rail from XIAO"]:::pwr
GND["Common GND"]:::gnd
%% Power
PSU5V -->|"5V"| MCU
PSU5V -->|"5V or 3V3 if LCD supports"| LCD
MCU -->|"3V3"| V33
V33 -->|"3V3"| PRES
V33 -->|"3V3"| TTP
%% Grounds
MCU --- GND
LCD --- GND
BTN --- GND
TTP --- GND
D2NODE --- GND
D9NODE --- GND
PRES --- GND
LED1 --- GND
PSU5V --- GND
%% I2C
MCU -->|"SDA D4"| LCD
MCU -->|"SCL D5"| LCD
%% Button input stage (always)
MCU -->|"D2 (buttonPin)"| D2NODE
BTN -->|"button to GND"| D2NODE
TTP -->|"OUT to D2 node"| D2NODE
%% Presence input stage (always)
MCU -->|"D9 (presencePin)"| D9NODE
D9NODE ---|"10k"| RPD
RPD -->|"to GND"| GND
PRES -->|"OUT to D9 node"| D9NODE
%% White LED (single LED, directly from pin with series resistor)
MCU -->|"D3 (whiteLedPin)"| LED1
classDef mcu fill:#e8f0ff,stroke:#2b5fd9,stroke-width:1px,color:#000;
classDef lcd fill:#fff4e5,stroke:#cc7a00,stroke-width:1px,color:#000;
classDef btn fill:#eaffea,stroke:#2d8a2d,stroke-width:1px,color:#000;
classDef touch fill:#e6fcff,stroke:#0077b6,stroke-width:1px,color:#000;
classDef pres fill:#f3e8ff,stroke:#7b2cbf,stroke-width:1px,color:#000;
classDef led fill:#ffe8ef,stroke:#c9184a,stroke-width:1px,color:#000;
classDef res fill:#f5f5f5,stroke:#444,stroke-width:1px,color:#000;
classDef pwr fill:#fff,stroke:#444,stroke-width:1px,color:#000;
classDef gnd fill:#fff,stroke:#000,stroke-width:1.5px,color:#000;
classDef io fill:#f5f5f5,stroke:#111,stroke-width:1px,color:#000;
+99 -39
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,16 +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.0**, 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:
@@ -114,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.
@@ -183,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:
@@ -236,23 +286,16 @@ The LED is driven with various patterns to indicate price level; see “LED Pric
---
## Firmware Features (v6.1.0)
## 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` %).
@@ -260,17 +303,27 @@ The LED is driven with various patterns to indicate price level; see “LED Pric
- `LiquidCrystal_I2C` with custom characters for:
- Local language letters.
- Lowprice and highprice indicators.
- **Daily min/max markers**:
- The low/high hourly indicators consider **negative**, **0.0**, and positive prices (v6.1.1 fix).
### LED Price Signalling
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 (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.
LED is **disabled** when:
@@ -349,7 +402,7 @@ After successful NTP time sync:
---
## Daily Fetch Strategy (v6.1.0)
## Daily Fetch Strategy (v6.1.0+)
### Goals
@@ -542,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.
@@ -555,13 +608,15 @@ If NVS does not contain valid WiFi credentials, or if connecting fails repeat
---
## Versioning & Changelog
- **v5.5** 15minute detail mode, LED based on current 15minute slot, improved DST handling (see file `20251027a_electricity_ticker_10_5_5_latest_DST_and_midnight_fix.ino`).
- **v6.0.0** First NVSenabled version:
- Store daily price data in NVS.
- Reduce API calls to “boot + aftermidnight”.
- Add NVS status section to secondary menu.
- **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.
- **v6.1.1** Daily low/high marker fix:
- Daily min/max and average now include negative and **0.0** prices.
- **v6.1.0** Midnight fetch & “today” detection fixes:
- Correctly detect **market day** using the last `unix_seconds` timestamp.
- Distinguish between:
@@ -571,6 +626,11 @@ If NVS does not contain valid WiFi credentials, or if connecting fails repeat
- Two retries every 20 minutes in the first hour (~00:20, ~00:40).
- Then hourly retries (topofhour) until todays dataset is available.
- Behavior on reboot and manual longpress is unchanged, but now respects the improved “today” logic.
- **v6.0.0** NVS storage & daily fetch:
- Store daily price data in NVS.
- Reduce API calls to “boot + aftermidnight”.
- Add NVS status section to secondary menu.
- **v5.5** 15minute detail mode, LED based on current 15minute slot, improved DST handling (see file `20251027a_electricity_ticker_10_5_5_latest_DST_and_midnight_fix.ino`).
See [`CHANGELOG.md`](./CHANGELOG.md) for more details.
+35 -15
View File
@@ -2,25 +2,45 @@
## Current 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.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.0.0
## Highlights of v6.2.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.
- **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:
- [CHANGELOG.md](./CHANGELOG.md)
- [README.md](./README.md)
## Previous firmware
- **Version:** 6.1.2
- **Release date:** 2026-03-11
- **Target MCU:** Seeed XIAO ESP32C3
## Highlights of v6.1.2
- 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.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
For detailed history, see [CHANGELOG.md](./CHANGELOG.md).
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB