Voltage
-- V
Raspberry Pi Pico W · Home
Voltage
-- V
Current
-- A
Power
-- W
Power Factor
--
Today
-- kWh
This month
-- kWh
Total energy
-- kWh
Bill so far
₹--
Current estimated bill
₹--
-- units · —
Predicted bill (period end)
₹--
-- units projected · -- days left
Average per day
-- kWh
Yesterday -- kWh · rate ₹--/kWh
| Date | kWh | Peak W | Cost |
|---|
| Slab | Units | Rate | Amount |
|---|
Projection uses the average of your last 7 complete days, applied to the -- days remaining in this billing period.
Automatic billing runs on day 1 of every period and e-mails the invoice to —.
| Period | Units | Amount | Actions |
|---|
Leave “up to” empty on the last row to mean “and above”.
Separate multiple addresses with commas. The mail server is configured in .env.
| Reading | Unit | What it is |
|---|---|---|
| Voltage | V | RMS mains voltage measured by the ZMPT101B. Should sit near 230 V in India. |
| Current | A | RMS current drawn by the load, measured by the ACS712. |
| Power | W | Real power — instantaneous V × I averaged over the waveform. This is what you actually pay for, not V × I of the RMS values. |
| Power Factor | — | Real power ÷ apparent power. 1.00 for a heater or bulb; 0.5–0.8 for motors, fridges and cheap SMPS. A low PF means current is flowing that does no useful work. |
| Today | kWh | Energy consumed since local midnight. |
| This month | kWh | Energy consumed this calendar month — not the billing period, which differs if your billing day is not the 1st. |
| Total energy | kWh | Everything ever recorded by the server, across all periods. |
| Bill so far | ₹ | The current billing period's consumption run through your tariff. |
1 kWh = one “unit” on your electricity bill = a 1000 W load running for one hour.
A slab is a price band. Instead of one flat rate, the utility charges more per unit as you consume more. The default tariff here:
| Slab | Units | Rate |
|---|---|---|
| 1 | first 100 | ₹3.50 |
| 2 | next 100 (101–200) | ₹4.50 |
| 3 | next 200 (201–400) | ₹6.00 |
| 4 | above 400 | ₹7.50 |
“Telescopic” means the rates stack. At 250 units you do not pay 250 × ₹6.00. Each band is charged at its own rate:
first 100 units x Rs 3.50 = Rs 350.00
next 100 units x Rs 4.50 = Rs 450.00
next 50 units x Rs 6.00 = Rs 300.00
-----------
energy charge = Rs 1100.00
+ fixed charge Rs 50.00
+ 5% tax Rs 57.50
-----------
total Rs 1207.50
Edit the bands in Settings → Tariff & Billing. Leave “up to” empty on the last row to mean “and above”. If your utility charges one flat rate, untick Use telescopic slab tariff.
The fixed charge is billed every period regardless of consumption — even at zero units. It covers the meter, the line to your premises, maintenance and billing admin. Your bill may call it fixed charge, demand charge, service charge or minimum charge.
Tax applies to the subtotal, so the fixed charge is taxed too:
subtotal = energy charge + fixed charge tax = subtotal x tax% total = subtotal + tax
Many states scale the fixed charge by sanctioned load (e.g. ₹50 per kW, so a 3 kW connection pays ₹150). This app uses one flat figure per period — enter the already-multiplied amount. Set it to 0 if your tariff has none.
Consequence: at zero consumption the bill still shows ₹52.50, not ₹0. That is correct — it is what the utility would charge.
No AI, no trained model. It is a simple moving average with linear extrapolation — a deterministic, closed-form calculation over your own recorded history.
| Symbol | Meaning |
|---|---|
P = [s, e] | billing period, start to end (inclusive) |
d | today's local date |
E_i | kWh recorded on day i |
T(.) | the slab tariff function |
1. units so far
E_used = SUM( E_i ) for i = s .. d
2. baseline daily rate - mean of the last 7 COMPLETE days,
clipped so the window never starts before the period
W = [ max(d - 7, s) , d - 1 ]
Ebar = SUM( E_i for i in W ) / |W|
3. extrapolate over the days that remain
N_left = |P| - (d - s + 1)
E_proj = E_used + Ebar * N_left
4. price it with the same tariff as the real bill
Bill_pred = T( E_proj )
Today is excluded from the window because it is still in progress and would drag the mean down.
There is no complete day yet, so today's partial usage is scaled up by the fraction of the day elapsed:
f = (3600*h + 60*m + s) / 86400 Ebar = E_today / f only when f > 0.02
The f > 0.02 guard (about 29 minutes) stops a near-zero denominator producing an absurd figure.
Because T is piecewise-linear and increasing, T(E_proj) is not T(E_used) × E_proj/E_used. Going from 190 to 210 units costs more per unit than going from 90 to 110. The projected unit count has to go through the slabs — you cannot multiply the current bill by a ratio.
N_left approaches 0, so the estimate converges onto the real bill. Error is largest at the start of a period.Improvement path, all still closed-form: weighted moving average (recent days weighted higher) → weekday/weekend segmentation → same-month-last-year seasonal comparison. None of these need machine learning.
A period runs from your configured billing day to the day before the next one. With billing day 15, a period is 15 Aug → 14 Sep. With billing day 1 it is simply the calendar month.
Consumption is accumulated per local day from the Pico's cumulative energy counter, stored as deltas — so a device reboot does not double-count, and a counter reset is detected and handled.
Every bill is viewable at /bill/<id> (printable) and can be re-sent from the Billing tab.
| Type | Fires when |
|---|---|
voltage_high | Voltage above the max threshold |
voltage_low | Voltage below the min threshold. Readings under 30 V are treated as “no mains”, not under-voltage. |
current_high | Load current over the limit |
power_high | Instantaneous watts over the limit |
daily_kwh | Today's consumption over the daily budget |
monthly_bill | Projected bill over the budget |
device_offline | No telemetry for offline_minutes |
Each type has its own cooldown, so a sustained fault sends one e-mail per cooldown window rather than one every two seconds. Alerts appear as a toast, in the Alerts tab, and by e-mail if enabled.
| Part | Connect to |
|---|---|
| Raspberry Pi Pico W | — |
| ZMPT101B voltage sensor | OUT → GP26 (ADC0), VCC 3V3, GND |
| ACS712 current sensor | OUT → divider → GP27 (ADC1), VCC 5V (VBUS), GND |
| 16x2 I2C LCD (PCF8574) | SDA → GP4, SCL → GP5, VCC 5V (VBUS), GND |
ICALIBRATION absorbs the ratio during calibration.
The 16x2 display cycles through three pages, one every 3 seconds:
Page 1 — live
V:231.4 I:2.15 P:480.2W PF:0.96
Page 2 — energy
Energy:1.234kWh Cost: 4.32
Page 3 — network
WiFi OK -58dB 192.168.1.77
The cost on page 2 uses the ₹/kWh rate the server pushes back with every telemetry reply — change the tariff here and the LCD follows within seconds. Other states: WiFi lost / Reconnecting..., Zero calibrating, Rebooting....
VCALIBRATION in main.ino: new = old × (measured / displayed)ICALIBRATION the same way against a clamp meter.Trim the ZMPT101B's on-board pot until its output sits near 1.65 V with no clipping before calibrating voltage.
| Method | Path | Purpose |
|---|---|---|
| POST | /api/telemetry | Device → server; returns the tariff and any queued commands |
| GET | /api/state, /api/summary | Live values, billing summary |
| GET | /api/history?hours= | Averaged power / V / I history |
| GET | /api/usage/daily?days=, /api/usage/monthly?months= | Aggregates |
| GET/POST | /api/settings | Read / update settings |
| POST | /api/settings/test-email | Send a test e-mail |
| GET/POST | /api/alerts, /api/alerts/ack, /api/alerts/clear | Alerts |
| GET/POST | /api/bills, /api/bills/generate, /api/bills/{id}/email | Bills |
| POST | /api/command | Queue resetEnergy / calibrateZero / setCalibration / reboot |
| POST | /api/usage/reset | Clear history (day / period / all) |
| WS | /ws | Live state + alert push |
Set DEVICE_TOKEN in .env to require the Pico to send a matching X-Device-Token header on /api/telemetry. Off by default.
power_monitor.db — back that one file up.