Back to Spy NATS
Spy NATS Blog/ Connected Vehicle Telemetry and Remote Commands with NATS

Connected Vehicle Telemetry and Remote Commands with NATS

An automotive use case combining core pub/sub telemetry streaming from a connected car with request/reply for remote commands (like remote-lock) that need a guaranteed acknowledgement.

4 min read
  • nats
  • automotive
  • iot
  • request-reply
  • tutorial

The scenario

A connected-vehicle platform has two very different communication needs on the same car: continuous telemetry (speed, battery level, location — fire-and-forget, high frequency) and occasional remote commands (lock the doors, honk the horn — must know for certain whether the car actually received and executed it).

Using the wrong pattern for either would be a mistake: request/reply for telemetry would force the app to wait on thousands of tiny round-trips it doesn't need acknowledged; pub/sub for a lock command would leave the app with no way to know if the door actually locked.

Step 1 — start NATS

terminal 1
docker run -d --name nats-server -p 4222:4222 -p 8222:8222 nats:latest
nats context add local --server="nats://localhost:4222" --select

Step 2 — telemetry: plain pub/sub, high frequency, no reply needed

terminal 2 — the owner's app dashboard watches one car's telemetry
nats sub "telemetry.VIN123"
terminal 3 — simulate the car streaming readings every second
for i in $(seq 1 5); do
  nats pub telemetry.VIN123 "{\"speed\":$((30+i)),\"battery\":78,\"lat\":37.77,\"lon\":-122.41}"
done

None of these publishes wait for anything — that's correct for high-frequency telemetry, where an occasional dropped reading is acceptable and blocking on an ack per message would add needless latency and complexity.

Step 3 — remote lock command: request/reply, must be confirmed

terminal 2 — the car subscribes and replies to lock commands, simulated here
nats reply "command.VIN123.lock" '{"result":"locked","batteryImpact":"negligible"}'
terminal 4 — the owner's app requests the lock and waits for confirmation
nats request "command.VIN123.lock" '{"requestedBy":"owner-app"}' --timeout=5s

The app only shows "🔒 Locked" in its UI after this reply arrives — never optimistically, since a car with no connectivity should surface as a failure, not a false success.

Step 4 — the car is offline: the command must fail loudly, not silently

terminal 4 — no responder running (car is out of signal range)
nats request "command.VIN456.lock" '{"requestedBy":"owner-app"}' --timeout=3s
expected output
nats: error: no responders available for request

The app must surface this as "Couldn't reach your car — try again when it has signal," rather than silently retrying forever or falsely reporting success.

Step 5 — combining both: telemetry confirms the command's real-world effect

For extra confidence beyond the command reply itself, the app can also watch telemetry for the resulting state change — useful since a reply only confirms the car received the command, while telemetry confirms the physical outcome:

terminal 2 — car's telemetry also reports lock state changes
nats pub telemetry.VIN123 '{"speed":0,"battery":78,"doorsLocked":true}'

Troubleshooting

SymptomLikely causeFix
App shows "locked" but the car never actually lockedApp treated the request/reply's success as final truth without also checking telemetry-confirmed physical stateTreat the reply as "command received," and optionally cross-check the next telemetry update for the actual state change on safety-critical commands
Telemetry dashboard misses occasional readingsExpected — plain pub/sub is fire-and-forget by design; a dropped reading here is low-stakesDon't add request/reply overhead to high-frequency telemetry; if historical completeness matters, use JetStream instead of upgrading to request/reply
Remote command hangs the app's UI indefinitelyNo --timeout set on the requestAlways set an explicit timeout appropriate to a "car might be offline" scenario (a few seconds, not indefinite)
Two different remote commands appear to cross-talkBoth commands published to the same subject instead of a command-specific oneUse one subject per command type per vehicle, e.g. command.<vin>.lock vs command.<vin>.honk

Recap

NeedPatternWhy
High-frequency telemetryCore pub/sub (nats pub/nats sub)Fire-and-forget is correct — no per-message ack overhead
Remote command (lock, honk)Request/reply (nats request/nats reply)The app needs to know definitively whether the car received it
Car offlineno responders available errorMust be surfaced to the user, never silently retried forever or assumed successful

SpyNATS is an independent tool and is not affiliated with, sponsored by, or endorsed by Synadia Communications or the NATS.io project.