MessagePack vs JSON on Devices: What We Switched and Why

Reading time: 5 minutes

Last modified:

A tracker on a cellular link pays for every byte twice: once on the data plan, and once in the time the modem stays awake on a battery. In May 2024 we changed the message format on the Micare GPS tracker from JSON to MessagePack. On FlowID, a BLE-configured water filter, we kept JSON on purpose and added more of it. Both calls fit their channel.

Our tickets record what we changed and why, but they hold few measurements. We did not log airtime or cost per message, so this post stays qualitative where the data runs out and says so.

A battery tracker wakes, gets a GPS fix, powers the modem, reports and sleeps. The shorter the transmission, the sooner it sleeps, and the less data the SIM plan meters. The Micare GPS tracker ran on 2G and later LTE modems, with a USSD and SMS fallback behind the main channel.

The earlier USSD channel was the cheapest path. Its payload was a delimited text string: latitude, longitude, battery, device ID and timestamp, separated by semicolons. That is compact, but it has no field names. Our server parser assumed all five fields in a fixed order. Real messages arrived with a varying number of fields and a shuffled order, so we rewrote the parser to accept any order and require only the device ID.

Compactness without structure breaks when fields go missing. That lesson shaped the later choice: keep named fields, shrink the encoding.

What we changed and when

  • October 2023: the firmware moved its transport from USSD to MQTT against our own broker.
  • May 2024: the firmware serialization call changed from JSON to MessagePack. In the same two weeks, time fetching moved from NTP over TCP to NTP over UDP. The ticket states no reason for that change. A later ticket also decided the clock syncs only from the GPS fix, with no NTP or cell-tower sync.
  • Late May 2024: the API shipped to production with a decoding step that tries MessagePack first and falls back to JSON. Devices on old and new firmware could report to the same backend.

Two things went wrong in the API work. A desktop MQTT client could not reproduce device traffic: what we published reached the server in a different form than what the device sent. We tested against a real device on staging instead. We also dropped a framework payload decorator, because it ran the decoding step twice with different input.

For the library, we tried two JavaScript implementations. One returned buffers, which did not fit our pipeline. We took the official @msgpack/msgpack, then at a beta release on npm.

What we measured, and what the docs say

Our tickets hold no byte counts for this change. The project page says the switch was meant to cut payload size, and nothing more specific is recorded. The primary sources add:

  • The MessagePack site says small integers fit in one byte and a short string needs one extra byte besides its text. Numeric telemetry gains most.
  • ArduinoJson documents the same GPS reading in both formats: 62 bytes as JSON (we counted the documented string) and 49 as MessagePack, about 21 percent smaller. That is the vendor’s example, not our device.
  • The specification keeps map keys as values in the stream, so a flat object still pays for its key names. Dropping keys means moving to arrays, which costs readability.
  • The msgpack-javascript README shows its decoder running behind JSON.parse in its benchmark. Server-side parse time was not our constraint, but the format is not faster everywhere.

When JSON is still the better choice

FlowID shows the other side. Its device kept a JSON sending queue from 2018 for MQTT messages during connectivity gaps. A 2021 hardware ticket suspected the device looped while sending that queue. It proposed storing the queue in binary and serializing to JSON only at send time, which would remove a double serialization. It also proposed replacing the JSON library with plain printf for the few messages the device builds. The ticket lists these as likely optimizations, so we cannot say from it which shipped.

In 2024 we went the other way for the phone link. A new BLE characteristic carries all flow data in one JSON structure, unified across device models. The MQTT service gained new JSON topic versions, with the old ones kept for compatibility. The ticket states one reason: a unified data structure across device models. We would add two more:

  • A person reads that traffic: developers, support, and the phone app team.
  • One structure across models matters more than a few bytes on a short-range link.

MessagePack needs no schema either, so a unified structure works in both formats. The channel decides the format.

A checklist

  1. Write down bytes per message, messages per day and the billing unit (byte, message or airtime). If you cannot, you do not yet have a problem MessagePack solves.
  2. Switch the metered link only. Keep JSON for BLE configuration, logs and bring-up.
  3. Keep named fields, or document the positions. Our positional USSD string broke when fields went missing.
  4. Accept both formats on the server during the rollout, and make them distinguishable. A try-decode fallback worked for us; an explicit format marker is safer.
  5. Log decoded payloads as JSON on the server so people can still read them.
  6. Test with a real device before trusting a desktop MQTT client.
  7. Pin the library version. Our decoder was a beta build.
  8. Measure airtime and cost per message before and after. We skipped this step. Do not.

Talk to us

We build IoT hardware, firmware and backends, including the cellular and BLE sides. See our IoT product development service, the Micare GPS tracker case and the FlowID case. To discuss your device’s data format, contact us.

Frequently Asked Questions

Is MessagePack smaller than JSON?

Usually. The MessagePack site says small integers take one byte and a short string costs one byte on top of its text. ArduinoJson's own GPS example is 62 bytes as JSON and 49 as MessagePack. Maps still carry their key names, so the saving depends on how many numbers and how many keys you send.

Does MessagePack need a schema?

No. The specification defines a type system, not a schema, so you can send the same loose structures as in JSON. Both ends still have to agree on the field names and meanings.

When should I keep JSON on an embedded device?

Keep JSON where a person reads the data: BLE configuration, logs, bring-up and support. It also fits channels where bytes are free. Switch the metered cellular link, not every link.

Can a server accept both JSON and MessagePack?

Yes. Our tracker API decodes each MQTT message as MessagePack first and falls back to JSON, so devices on old and new firmware report to the same backend during a rollout.

CimpleO IoT
MessagePack vs JSON on Devices: What We Switched and Why
OCT 14 2026 · 5 MIN
Table of Contents