mining monitor active
Monitors a small fleet of open-source solo-mining boards (Bitaxe / NerdAxe-class) plus the
mining pool, run every few hours by the on-device lil-sis agent. Part of the
agents system.
every ~5 hours (lil-sis cron)
│
▼
miner-watchdog.sh
└─ poll each miner's HTTP API; 0 hashrate / no response → restart
│
▼
full-mining-report.sh
├─ miner-status.sh → per-miner info snapshot (hashrate, temp) → JSON
└─ pool-status.sh → pool API: hashrate, workers, earnings, balance
+ live BCH price → balance in USD
│
▼
lil-sis → chat summary: restarts · total hashrate · per-miner · BCH/USD
The hardware
A mix of open-source solo-mining boards, spanning a few ASIC generations:
| Board | ASICs | Clock (stock → tuned) | Core V | Hashrate | Power | Efficiency |
|---|---|---|---|---|---|---|
| NerdQAxe++ (×2) | BM1370 ×4 | 600 → 950 MHz | 1150 → 1220 mV | ~6.2 TH/s each | ~100 W | ~16 J/TH |
| Bitaxe-class | BM1370 ×1 | 675 MHz | 1150 mV | ~1.4 TH/s | ~22 W | ~15 J/TH |
| 8-chip board | BM1368 ×8 | 600 MHz | 1200 mV | ~5.0 TH/s | ~131 W | ~26 J/TH |
Combined that's ≈ 18.8 TH/s at ≈ 354 W.
Tuning for hashrate
The headline experiment is on the two NerdQAxe++ boards: clocked from a stock 600 MHz up to 950 MHz (+58%) with only a modest core-voltage bump (1150 → 1220 mV), nearly doubling per-board output to ~6.2 TH/s while holding ~16 J/TH efficiency and ~53 °C die temps under active cooling (~2,350 rpm fans). The single-chip Bitaxe-class board runs lean (~1.4 TH/s at 22 W, ~15 J/TH, the most efficient of the bunch), while the older 8-chip BM1368 board trades efficiency (~26 J/TH) for raw hashrate.
This is exactly what the per-miner snapshots are for: every reading captures frequency, core voltage, hashrate, power, and die temperature, so a setting can be pushed and then judged against the resulting hashrate/temp/efficiency over time, dialing the clocks in without cooking a board. And when a clock is pushed too far and a unit drops to 0, the watchdog catches it and restarts.
Watchdog
miner-watchdog.sh polls each miner's local HTTP API (/api/system/info). If a board is
unreachable or reporting 0 hashrate, it issues a restart (POST /api/system/restart) and
tallies how many it had to kick, so a hung miner recovers on its own instead of silently
sitting dead until the next time someone looks.
for entry in "${MINERS[@]}"; do # MINERS is a list of name:ip pairs
name="${entry%%:*}"; ip="${entry##*:}"
hashrate=$(curl -s --max-time 10 "http://$ip/api/system/info" \
| python3 -c "import json,sys; print(json.load(sys.stdin).get('hashRate', 0))")
if [ -z "$hashrate" ] || [ "$hashrate" = "0" ]; then
echo "restarting $name ($ip): 0 H/s"
curl -s -X POST "http://$ip/api/system/restart" >/dev/null
else
echo "ok $name: ${hashrate} GH/s"
fi
done
Miner status
miner-status.sh fetches each miner's info and writes a timestamped JSON snapshot,
pretty-printing per-miner stats (hashrate, temperature, and the rest of the device telemetry).
Over time those snapshots are a lightweight time series of the fleet's health.
Pool status
pool-status.sh queries the mining pool's API for overall hashrate, per-worker stats
(10-minute / 1-hour hashrate, reject rate, online status), earnings, and account balance.
It then pulls a live BCH price from a public price feed to convert the balance to USD.
# pull the BCH balance from the pool, convert to USD with a live price
bch=$(curl -s "$POOL_API/account?coin=BCH" -H "X-API-KEY: $KEY" \
| jq -r '.data.balance[] | select(.coin=="BCH").amount')
price=$(curl -s "$PRICE_API?ids=bitcoin-cash&vs_currencies=usd" \
| jq -r '.["bitcoin-cash"].usd')
printf "BCH %.6f = \$%.2f\n" "$bch" "$(echo "$bch * $price" | bc -l)"
Putting it together
full-mining-report.sh runs the miner and pool status back to back; the cron runs the
watchdog first, then the report. lil-sis summarizes the whole thing to chat every ~5 hours:
any restarts, total hashrate, each miner's hashrate and temperature, and the pool balance in
both BCH and USD.
Stack: Bash, curl + jq, miner and pool REST APIs, a price feed, cron via the OpenClaw gateway.