A Two-Year Log of Istanbul's Weekend Power Cuts, Kept on a Pi

这篇内容暂时只有 English 版本,以下是原文。

Two winters ago I started noticing that the power in our flat on the Asian side dropped most Saturday mornings, usually between 09:10 and 09:40, and almost never on Sundays. The fridge clock and the router were the only evidence. I wanted a record I could actually trust, not my memory of “it happened again.”

I already had a Raspberry Pi 3 in a drawer and a cheap USB UPS hat that reports battery voltage over I2C. The Pi draws about 3W; the hat holds it up for roughly 90 seconds after mains drops, which is plenty to write a shutdown record. The main trick was not measuring the outage—it was measuring the gap between last heartbeat and next boot, because the Pi can’t log while it’s dead.

A one-minute cron job writes a heartbeat with the current boot ID:

# /etc/cron.d/heartbeat
* * * * * root /usr/local/bin/hb.sh

# hb.sh
#!/bin/sh
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
BOOT=$(cat /proc/sys/kernel/random/boot_id)
echo "$NOW $BOOT" >> /var/log/power/hb.log

On boot, a systemd unit reads the last line of hb.log, compares the boot ID, and appends a row with the gap in seconds:

#!/bin/sh
LAST=$(tail -n1 /var/log/power/hb.log)
LAST_TS=$(echo "$LAST" | awk '{print $1}')
LAST_BOOT=$(echo "$LAST" | awk '{print $2}')
NOW_BOOT=$(cat /proc/sys/kernel/random/boot_id)
if [ "$LAST_BOOT" != "$NOW_BOOT" ]; then
  GAP=$(( $(date -u -d "$(date -u +%Y-%m-%dT%H:%M:%SZ)" +%s) - $(date -u -d "$LAST_TS" +%s) ))
  echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) reboot gap=${GAP}s" >> /var/log/power/outages.log
fi

The UPS hat also logs voltage every 10 seconds, so a short dip that doesn’t kill the Pi still shows up as a sag. That gave me two signals: hard reboots and brownouts.

Why this shape instead of a smart plug with cloud logging? The plug is easier, but it dies with the router, and the router is on the same circuit. The Pi plus UPS is self-contained: it records the outage from the inside, which is the only place that stays alive long enough to say anything. The cost is precision. I know a reboot happened, not the exact second mains dropped—the gap is between last heartbeat and boot, so the error bar is about 60 seconds. For a pattern over two years that’s fine.

Where it fails: if the outage lasts longer than the UPS, the Pi loses the last few heartbeats (I lost about 20 minutes once during a long cut and had to infer it from the router’s own log). If the SD card corrupts on power loss—it has twice—the heartbeat file is gone and I have a hole. And clock drift matters: without NTP after boot, the gap calculation is garbage. I now force ntpdate before the boot unit runs, or skip the row if the clock is clearly wrong.

What I ended up with is a CSV of about 140 reboot rows over two years, most clustered on Saturday mornings, plus a smaller set of brownouts on hot afternoons in July. The pattern is real and boring, which is what I wanted.

For anyone who has done something similar: how do you handle the clock-drift problem on a device that boots with no network? I’ve been fudging it with a rough last-known-time file, and I suspect there’s a cleaner way.

0
赞同度 100% · 0 条评论 · 2 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。