ISV IEEE Smart Village
← Village Metering · Overview · Roadmap

TC 2.0 technical report

Technical report on the open EarthSpark / SparkMeter stack (ThunderCloud 2.0 / GroundBolt): field meters and CIUs (Customer Interface Units) on a 2.4 GHz mesh, a USB Gateway as the RF bridge, SparkNet-HTTP and Firefly as software plugins (not standalone radios), GroundBolt as the on-site headend, optional ThunderCloud as that same headend in the cloud. An MDMS keeps the history of those reads: it checks each one, drops or repairs a bad one, and files them by meter and time. It is not a box in this tree. Objects below are those this review already named or drew — GroundBolt Postgres / wire objects, village UN GeoJSON packs, and complementary MeshEMS / OpenAMI — not invented tables. Diagrams share one hosting toggle (site-only vs site+cloud).

How ThunderCloud 2.0 (TC2.0) came to be

SparkMeter built prepaid LV (low-voltage) metering for mini-grids and distribution utilities — meters with built-in RF mesh, a site basestation, and cloud software (ThunderCloud / GroundBolt lineage) for billing and control. That stack served a large installed base, but the operator software was historically hard to run outside the vendor’s managed environment: proprietary internals, Chef-era bootstrap, and a soft lock where leaving the SaaS (software-as-a-service) path meant losing a workable head-end system (HES) — the utility software that talks to meters for billing, disconnect, and config.

In September 2025, EarthSpark took over SparkMeter energy-metering operations (on the order of ~1 M people served on the meters). The mandate was continuity for live fleets and an exit from single-vendor software lock where operators could carry the risk. In July 2026 EarthSpark published ThunderCloud 2.0 (also called TC2.0) under Apache-2.0 as an open foundation — same product family as GroundBolt (on-site) and ThunderCloud (cloud), modernized so it can actually be bootstrapped and self-hosted. Hub: sparkmeter.io/open-source · code: github.com/EarthSpark/thundercloud.

Open-sourcing did not cut over the field. Production basestations still run ThunderCloud 1.x (TC1.x) managed SaaS; TC2.0 is explicitly a pre-production foundation, not a drop-in flash for every live site. Community near-term goal (OSEA Discord / GitHub — Open Source Energy Access community around EnAccess): a stable releasable main (Ansible from-scratch converge, SymmetricDS container CI / continuous integration) before years of deferred product features.

Field today vs ideal below Live fleets still run TC1.x managed SaaS. TC2.0 (Jul 2026, Apache-2.0) is a pre-production foundation — not plug-and-play. This page describes the target open architecture operators can commission toward, not a promise that every basestation upgrades overnight.

What problems TC2.0 is aimed at

Vendor soft-lock on the HES (head-end)

Operators could own meters in the field but not realistically run the head-end system themselves. TC2.0 opens GroundBolt / ThunderCloud so self-host (or mix-and-match with managed cloud) is a real path.

Opaque meter↔app contract

meter-driver-spec publishes an HTTP / SSE (Server-Sent Events; optional gRPC) driver contract — energy, power, voltage, enable/disable, tamper, balance flags — so other OEMs (original equipment manufacturers) can aim at the same basestation later.

No clean “talk to meters without the full cloud”

SparkNet-HTTP is the direct meter/gateway path. TC2.0 uses it natively; operators can also use it outside ThunderCloud as a vendor-exit / alternate HES edge.

Site vs cloud were hard to separate

SymmetricDS makes continuous ground↔cloud database sync explicit. Basestation and cloud are not one process — the WAN (wide-area network / internet uplink) can fail while GroundBolt keeps local prepaid and control alive.

Un-reproducible basestation bootstrap

Chef-era installs blocked outsiders. Ansible converge is the TC2.0 bootstrap path so a GroundBolt box can be rebuilt from public tooling.

Asset / key control

Meter crypto keys can stay with EarthSpark, escrow at A2EI (Access to Energy Institute / Access to Energy gGmbH), or move to the operator. That unlocks sovereign ownership — and creates real brick risk if mishandled.

What TC2.0 does not claim to solve alone: CRM (customer relationship management) depth of a mature SaaS, every deferred product feature from a decade of backlog, or Phase 2 ISV (IEEE Smart Village) work (Street-EMS / OpenAMI MQTT telemetry / feeder intelligence). ISV maps TC2.0 under Phase 1 — open operator software for the installed SparkMeter base — complementary to MPM (metering / prepaid management class systems) and to later OpenAMI tracks. See roadmap · ThunderCloud update.

Ideal system state

A village mini-grid powers homes and civic loads over LV feeders. Each prepaid customer has an SM (SparkMeter) meter and a CIU (Customer Interface Unit — keypad / balance display) on a short local link — no cloud required for that UI. Meters join a 2.4 GHz mesh (encrypted / hidden SSID). A USB Gateway bridges that RF to the site computer; GroundBolt speaks to meters through the Gateway. SparkNet-HTTP and Firefly are software plugins on that path (not separate radio boxes).

When the operator wants remote ops, CRM, or multi-site portfolio tools, SymmetricDS syncs the site database to ThunderCloud in the sky (managed EarthSpark SaaS or self-hosted) — same app family as GroundBolt, cloud deploy. Cloud is optional. Commission the field ↔ Gateway ↔ GroundBolt loop first; attach cloud only after that loop is proven.

SM meter / CIU ↔ 2.4 GHz mesh ↔ USB Gateway ↔ SparkNet-HTTP / Firefly (plugins) ↔ GroundBolt   [optional]  ↔ SymmetricDS ↔ ThunderCloud

Managed path

EarthSpark hosts ThunderCloud / supports basestations. Current TC1.x customers stay here. Lowest ops burden; production improvements land here first.

Self-host path

Run GroundBolt on-site and/or ThunderCloud yourself. Mix-and-match (your site + their cloud, or both). Uses the same TC2.0 codebase. Self-host terms still evolving.

What must work before cloud

Visual 1 · Stack beside village

Left = software / protocol stack. Right = simplified village with the same horizontal bands. Gray = LV power. Amber discs = Firefly mesh among meters, under the Gateway / DCU band. Green = site backhaul. Blue dashed = optional WAN to ThunderCloud.

Hosting (both diagrams)
Stack · village (GroundBolt only) Download
Control down Readings up Firefly RF coverage LV power Site backhaul / serial

Visual 2 · Full village topology

Teaching map for ~100 homes plus market, medical center, cultural center, and ops building. Multiple Fireflies with translucent coverage; GroundBolt inside ops; power feeders in gray/black (no roads). Same hosting toggle as above.

Village plan · GroundBolt only Download
Power LV Firefly RF coverage Serial / backhaul → GroundBolt WAN → ThunderCloud (sky) Meter / CIU

Layer cheat sheet

Headend is the command center on the meter-to-cloud path: reads, disconnect, config, and the prepaid wallet. On this stack that is GroundBolt at the site. ThunderCloud is the same app after SymmetricDS sync, so the headend can be seen off site. Keys and the wallet system of record stay on GroundBolt. MDMS is the next concept up. It keeps the history of reads the headend already collected: checks each read, drops or repairs a bad one, and files them by meter and time. Billing and reports use that file. The open-server note calls this processing, cleaning, and organizing raw meter data, and puts CRM, revenue, and outage above it. This tree does not ship a separate MDMS. On the diagram that box is dashed and sits above L6. Site + ThunderCloud draws the readings arrow from the cloud headend up into it. Site only draws the same arrow from GroundBolt.

Above L6 · MDMS

Checks the headend’s reads, drops or repairs a bad one, and files the rest by meter and time. Not a process found in this tree. The open-server note places it above the headend. TC2.0 does not claim the CRM depth that sits above an MDMS.

L6–L5 · Headend, cloud copy

ThunderCloud — same headend app, off site. SymmetricDS — site↔cloud database sync. Optional after the site loop works.

L4 · Headend, on site

GroundBolt — site headend. UI + Postgres at the ops building. Wallet is the system of record. Survives WAN outages for local control.

L3 · Driver / plugins

SparkNet-HTTP + Firefly (software plugins) + meter-driver-spec — open contract for commands / SSE / meter capabilities (energy, power, voltage, enable/disable, tamper, …).

L2 · Gateway / DCU

Gateway / DCU — USB / serial bridge on site. One level. GroundBolt talks through it.

Mesh

2.4 GHz path among meters and up to the gateway. SparkNet RF / Firefly on that band.

L1 · Field gear

SM meter / CIU at each prepaid home. Gray LV feeders carry power, not data.

GroundBolt as site HES (from this tree)

ThunderCloud and GroundBolt are the same sparkmeter app. On-site: HEROKU unset → GroundBolt. In the cloud: ThunderCloud. Radio is a sibling process. GroundBolt talks HTTP commands out and SSE events in; it never sees SparkNet RF frames inside the USB Gateway.

Source of this section Public TC2.0 developer preview in the thundercloud tree (Aug 2026 pass). README: do not bill real customers. Live fleets still TC1.x managed SaaS. SparkNet RF inside the Gateway is not specified in this repo.

Prepaid loop — pay before watts

POST /api/v0/transaction/ (cash | bonus) → wallet credit → set_balance → CIU → configure_meter enable (if AUTO and credit > 0) → load draws watts → SSE MeterReadingEvent every HEARTBEAT_PERIOD (15 min) → add_reading → CalculateBilling → wallet debit → credit ≤ 0 → configure_meter disable

Data objects

Objects this report accounts for. GroundBolt / ThunderCloud names come from the public thundercloud tree (same pass that produced the stack + village SVGs). Village pack names come from the UN GeoJSON convention. MeshEMS is a complementary Phase 2 track — not a TC2.0 table. Diagram below draws those collections and every ID hop this page already named. A key chip inside the table-card is the nested unique ID that collection stores (join table, unique key, or pack id) — not on the hop line. Unknown hops stay dashed and unlabeled. Wire (SSE) is not stored. Toggle Force for the same nodes as a drag-able graph — no extra FKs.

Collections · ID hops
Download
nested unique ID unknown wire (SSE) · not stored UN asset id (can later) one DB / system

GroundBolt Postgres (system of record)

Readings sync ground→cloud only. SymmetricDS is optional after the site loop works. No Account entity — wallet is meter XOR sales account.

DomainObjectsRole
Site ground · ground_private · address · grounds_addresses One GroundBolt box = one ground row. Address is place, not GIS geometry.
Meter cluster meter · customer (1:1) · meter_config · meter_system_info · meter_billing · sparkmac_node · meter_models · meter_scalars · meter_tag / meters_tags · meter_view Customer is 1:1 with meter. Config (desired) vs system_info (gateway-reported) stay split.
Money wallet · transactions · transaction_sources (cash | bonus) · sales_account HES wallet is system of record. No mobile-money plugin in-repo.
Tariff tariff (flat | blockrate; optional plan / TOU / load-limit JSON nests) Price + limits pushed toward configure_meter.
Reading reading — energy, volts, watts, credit snapshot, state. Unique (meter, heartbeat window). No hop list. hops[] from a legacy emulator is dropped here.
People / ACL user · role · roles_users · users_grounds · sales_accounts_users Operator login. Cloud can map one user to many grounds.
Events / SMS event · sms_config · sms_message · snapshot Audit + low-balance SMS (once per threshold). Snapshot is content-addressed JSON.
Ops / sync config_parameter · system_version · system_state · dashboard_daily_tariff_summary · alembic_version · SymmetricDS sym_* Bootstrap + optional ground↔cloud catalog. Not meter telemetry.

Wire objects (SparkNet-HTTP / plugins)

SSE in — what GroundBolt consumes

meter_reading / meter_reading_phased → add_reading. heartbeat_summary → watchdog log. log → Python logging. Neighbor / network-health / command-lifecycle events exist on the wire; GroundBolt does not persist them.

Commands out

configure_provider (heartbeat 900 s), register_meter, configure_meter (enable/disable, limits), set_balance, unregister_meter. Fire-and-forget via SparkNet-HTTP POST /v1/commands.

On-demand mesh (not stored)

query_meter_neighbors → SSE meter_neighbors (neighbor_id, RSSI, link quality). query_network_health = packet counts + latency, not a path. CLI: meter get-neighborlists.

SparkMAC config, not a route log

sparkmac_node stores forwarding mode (default flooding), optional static_routes, TTL. Site UI “Routing Algorithm” is still an empty TODO. Desired-state pushed to meters — not a hop list per heartbeat.

Village UN GeoJSON pack (geometry, not telemetry)

Separate convention — Esri Utility Network semantics in open files. Same meter id can later bind feeds/registry.json to SparkNet-HTTP / GroundBolt. Geometry stays in files; kWh stays on demand. Full split: Village GeoJSON · UN.

ClassFiles / objects
Essential village.json · network/subnetworks.geojson · structure.geojson · electric-lines.geojson · electric-devices.geojson (meters, transformers, gen/station, EMS cabinets if used) · associations.json (connectivity edges)
Nice-to-have electric-junctions.geojson · enclosure / disconnect / recloser detail · containment / attachment associations · feeds/registry.json (Live Twin API bindings)
Feature keys stable id · assetClass · assetGroup · subnetworkId

MeshEMS / OpenAMI / MPM — complementary, not TC2.0 tables

MeshEMS (NESL 865B board) and Phase 2 OpenAMI MQTT / Street-EMS are a different track from GroundBolt Postgres. See MeshEMS board · OpenAMI. MPM (MicroPowerManager) owns CRM + other prepaid families (STS, OpenPAYGO). Posting /api/v0/transaction/ from MPM into GroundBolt can share a wallet; two systems of record without a split double-count.

Can you trace mesh routing per meter?

Not from stored readings. NAN is proprietary SparkMAC on 2.4 GHz — not Z-Wave. A legacy emulator can attach hops[] (last_hop, RSSI, retry) to SparkMAC replies; MeterReadingEvent / the reading table drop that. Energy, volts, watts, credit, state only.

On-demand: query_meter_neighbors → SSE meter_neighbors (who this meter hears now: neighbor_id, RSSI, link quality). CLI: meter get-neighborlists. query_network_health is packet counts + latency, not a path. GroundBolt does not write those replies to Postgres. You can poll a neighbor graph if the USB Gateway translator implements the command (contract in this tree; live firmware not in-tree). You cannot replay “this heartbeat went hut A → hut B → USB.”

Hypothetical prepaid day using these rules (not live telemetry): Village Simulator.

Complementary to MPM / OpenAMI — not a substitute

ISV Phase 1 parks TC2.0 as the open HES for the installed SparkMeter base. MPM (MicroPowerManager) owns CRM + other prepaid families (STS tokens, OpenPAYGO SHS). Posting /api/v0/transaction/ from MPM into GroundBolt can share a wallet; running both as system of record without a split will double-count. Phase 2 OpenAMI MQTT / Street-EMS is a different track.

Glossary

SparkMAC — SparkMeter NAN protocol on the 2.4 GHz mesh (not Z-Wave).

AUTO — meter state: relay on iff HES credit (and plan) > 0.

HES — head-end system: utility software that commands and reads meters. Here, GroundBolt on site, ThunderCloud as the synced copy.

MDMS — meter data management: checks the headend’s reads, drops or repairs a bad one, and files them by meter and time. Not a service in this tree.

CIU — Customer Interface Unit: prepaid keypad / balance display at the home.

USB Gateway — hardware RF interface (laptop/site) to the meter mesh.

NAN — neighborhood area network: local 2.4 GHz mesh among meters (bridged by USB Gateway).

WAN — wide-area network: usually the site’s internet uplink to cloud.

LV — low voltage: customer / village distribution voltages.

Open Metering Infrastructure — A2EI CRM / operator platform layer; ThunderCloud and Nimbus both sit here.

SSE — Server-Sent Events: one-way live stream from server to client (readings / events).

OEM — original equipment manufacturer (e.g. another meter vendor).

SaaS — software-as-a-service (vendor-hosted cloud).

CRM — customer relationship management (accounts, tickets, portfolio ops).

SGC — Security Group Code (Nimbus universal key used for token generation).

TC1.x / TC2.0 — ThunderCloud software generations (field vs open foundation).

Links

EarthSpark / SparkMeter

ISV Village Metering

Technical report. Diagrams: tc20-stack-village.svg · village-meter-topology.svg · tc20-data-objects.svg (boxes + force). Objects from the public TC2.0 tree + village UN pack already on this wiki — no invented tables.