Knowledge Base ยท Vehicle Sensor Networks

Network Bridges and Gateways

How vehicle sensor buses reach the cloud: gateway classes, uplink behaviour, store-and-forward, MQTT delivery semantics, clock drift, failure modes, payload volumes and multi-tenant design, with parameters for the Sense simulator.

Prepared 12 Oct 2026 Updated 13 Oct 2026 Feeds: Sense simulator data generator

1. Overview and where bridges sit

A bridge layer sits between in-vehicle buses and sensors and the cloud broker. It reads frames from CAN (J1939, ISO-TP), NMEA 2000, NMEA 0183, Modbus RTU, LIN, BLE and one-way TPMS radio, stamps and filters the data, buffers it, and publishes it over a cellular, LoRaWAN, Wi-Fi or satellite uplink.

Source figures are tagged in the research as sourced from a URL or as an engineering estimate. Values marked estimate below are engineering estimates, not verified from a fetched source, and should be used as priors with wide jitter.

Bus-level framing and fragmentation
ItemValueBasis
CAN 2.0B classic8 B per frame; about 130 bits including stuffing; at 250 kbps about 0.5 ms wire time, 1.3-1.6 ms effective with arbitrationEstimate
J1939 TP.CM_BAM (broadcast)Up to 1785 B = 255 packets x 7 B; PGN 0xEC00 (CM) and 0xEB00 (DT); inter-packet gap 50-200 ms (50 ms typical)Partly sourced; numbers are estimates
J1939 TP RTS/CTS (peer-to-peer)RTS, CTS (n packets), DT x n, EndOfMsgACK; timers T1 750 ms, T2 1250 ms, T3 1250 ms, T4 1050 ms, Tr 200 ms, Th 500 msEstimate; walkthrough sourced
TP failure modesMissed DT aborts the session (timeout, duplicate or bad sequence); BAM has no retry so one lost frame drops the whole message; connection exhaustionSourced
J1939 typical PGNsEEC1 (61444) 20 ms engine speed; CCVS (65265) 100 ms; ET1 (65262) 1 s; DM1 (65226) 1 s while fault active (BAM if more than 1 DTC); VIN (65260) multi-frame on requestEstimate
ISO-TP (15765-2)Single frame up to 7 B (6 with extended addressing); first frame carries length up to 4095 B (larger in CAN FD); consecutive frame sequence 0-15 wraps; flow control with block size and STmin (0-127 ms, 0xF1-F9 = 100-900 us)Sourced
NMEA 2000250 kbps CAN, 29-bit ID; Fast Packet up to 223 B = 6 + 31 x 7 over 32 frames; byte 0 = 3-bit sequence id + 5-bit frame counter; frame 0 byte 1 = total lengthSourced
Fast-packet lossMissing frame or sequence mismatch discards the whole PGN; concurrent senders separated by sequence id 0-7 and source addressSourced
NMEA 2000 ratesPosition rapid 10 Hz (129025); COG/SOG 4 Hz (129026); engine rapid 100 ms (127488); engine dynamic 500 ms (127489, fast-packet); GNSS 129029 at 1 Hz, 43 B fast-packet; AIS 129038 fast-packetEstimate
NMEA 01834800 baud default (AIS 38400); 8N1 gives about 480 B/s; sentence up to 82 chars; XOR checksum; ceiling about 10 sentences/s; full NGW-1 conversion can overload the 4800 output so a selective conversion list is neededSourced
Modbus RTU9600-19200 baud typical; poll 100 ms-1 s per register block; CRC16; timeout 100-500 ms; 3-10% retries on noisy powerEstimate
LIN19.2 kbps; master schedule slots 5-20 ms; low-value body data; bridged via a LIN-CAN nodeEstimate
BLE / TPMSBLE adverts every 1-10 s; gateway scan misses 5-20% of adverts; RSSI -60 to -95 dBm; TPMS is one-way 315/433 MHz with manufacturer-defined interval, assumed 60-300 s parked and 3-30 s moving or on pressure changeSource for TPMS; intervals are estimates

2. Gateway types and protocol translation

Gateway classes and behaviour
ClassExamplesInputsUplinkNotes
CAN-to-MQTT/TCP telematics boxTeltonika FMC/FMB, CalAmp, Geotab GO, Samsara, Webfleet, Digital Matter1-2 CAN, J1939/OBD, K-line, GNSS, accelerometer4G Cat-1/Cat-4, LTE-MRecords every 1-10 s, batched 30-300 s; binary codec over TCP or JSON over MQTT
NMEA gatewayActisense NGW-1/NGX-1, Yacht Devices YDGW-02, Digital Yacht, MaretronN2K, 0183Wi-Fi, Ethernet or serial to an onboard router (4G, VSAT, Starlink)Raw-CAN mode emits hundreds of frames/s on a busy bus
Industrial edgeAdvantech, Moxa, Siemens, Node-RED, GreengrassModbus, CAN, GPIOEthernet, 4GSparkplug B or JSON
LPWAN nodeLoRaWAN class AAnalog, BLE, UARTLoRaWAN gatewayTiny payloads
Cloud-definedAWS IoT FleetWise (signal catalog, campaigns, condition-based collection)CAN, OBDMQTT, ProtobufCollection rules are defined in the cloud

A Teltonika-style binary AVL record is about 40-120 B, with up to 255 records per packet (estimate).

Protocol translation steps in the research: reassemble multi-frame transport (J1939 TP, ISO-TP, N2K Fast Packet), decode raw signals with scaling and sentinel handling, filter by deadband or exception, then encode into the uplink codec (see sections 4 and 7).

Link latency (round-trip time) and throughput
LinkRTTThroughputBasis
4G LTE30-60 ms (p95 150)5-50 up / 20-100 down MbpsEstimate
LTE-M100-300 ms (3GPP target under 10 s)Up to about 1 MbpsSourced
NB-IoT1.5-10 s; up to 128 UL repetitionsAbout 20-60 kbpsSourced
5G10-40 ms20-300 MbpsEstimate
Wi-Fi offload (depot or port)5-30 ms20-200 MbpsEstimate
LoRaWAN EU8681-5 s0.3-5.5 kbps; app payload 51 B (DR0-2), 115 B (DR3), 222 B (DR4-7)Sourced
GEO VSAT600-800 ms (field 700-800)E.g. 3 (max 10) Mbps down / 1 (max 2) up; HTS 50-150 MbpsSourced
Starlink maritime25-60 ms advertised; polar test 19-99 msPolar test about 133 down / 5 up MbpsSourced
Iridium Certus500-600 ms typical; polar 71 ms-10 s704/352 kbps (Certus 700); Certus 100 about 88/22 kbpsSourced
Iridium SBD20-60 s340 B up / 270 B down per messageEstimate
Link loss and outage behaviour
LinkLoss and outage
4G LTELoss 0.1-1%; handover blips 50-500 ms; weak-cell stalls 2-10 s
LTE-MPSM/eDRX wake delays of seconds to minutes; some plans bill a minimum 2 kB per upload
NB-IoTPoor mobility (reselection, no connected handover); MCL 164 dB
5GHandover 20-100 ms; LTE fallback
Wi-Fi offloadAssociation 1-5 s; roam failures; used for backfill, OTA and high-rate logs
LoRaWAN EU86810-30% loss for moving urban; retries up to 8. 1% duty cycle: 55 B at SF7 = 123 ms airtime (12.3 s wait), at SF12 = 2957 ms
GEO VSATRain fade, mast blockage and turns: 5-15% outage, episodes of 1-30 min
Starlink maritimeObstruction drops of 1-15 s common; re-acquisition 1-5 min (estimate)
Iridium CertusSession drops near the poles
Iridium SBDPer-message cost
Coverage and outage patterns by scenario (all estimates)
ScenarioPattern
Urban bus99%+ coverage; dead spots of 5-30 s at depots and underpasses; handover glitch every 1-5 km
Tunnels30 s-10 min full loss; GNSS also lost; data buffered
Rural truck3-10% of drive time without service; episodes 1-30 min, some over 1 h
AmbulanceMostly urban; alarms prioritised; reconnect bursts
Ship near coastCellular up to about 20-40 km, then VSAT or Starlink; switchover 5-60 s; port Wi-Fi/4G backfill
Ship at seaVSAT 95-99.5% availability; Starlink 97-99.5% with short drops; Iridium 99%+ at low rate
PowerKey-off sleep with CAN or accelerometer wake; cranking brownout (under 6 V) reboots 0.1-1 per day on weak-battery trucks

4. Store-and-forward, batching and MQTT delivery semantics

Store-and-forward (all values estimates)

Store-and-forward parameters
ParameterValue
MediaRAM ring 10-100 kB to 1 MB plus flash 4-128 MB; SD on NMEA loggers
Capacity128 MB at 40 B/record is about 3M records; common specs 100k-1M records
PolicyFIFO drop-oldest (silent loss) versus drop-newest; priority lane for alarms and DTCs
ReplayRate-limited at 1-10x realtime, interleaved with live data, which causes out-of-order arrival; or newest-first then backfill
CorruptionFlash CRC fail 0.01-0.1%; power loss mid-write loses the last batch
OverflowAfter 6-72 h offline at 1 Hz, records are dropped; sequence gap visible

Batching, compression and edge filtering

Batching and filtering techniques
TechniqueParametersEffect
Time batching10 s live, 60 s typical, 300 s eco; flush on ignition-off or buffer at 70-80%Fewer publishes; latency equals the window
Count batching20-255 recordsCodec limit
Compressiongzip/zstd/LZ4 on a JSON batch gives 70-90% reduction; delta-of-delta timestamps plus XOR floats 8-12xEstimate
DeadbandSpeed 1 km/h, coolant 1 degC, fuel 0.5%, GPS 10-50 m or 15-30 deg headingRemoves 60-90% of samples (estimate)
Report by exceptionSend on change plus heartbeat every 30-300 s; DTC on set and clearEstimate
Tiered rateRunning 1 Hz, parked 1/600 s, trip start and end eventsEstimate
Link-adaptiveVSAT/Iridium 1 sample per 30-300 s, alarms firstEstimate

MQTT delivery semantics

MQTT QoS levels
QoSHandshakeLossDuplicatesOrderUse
0NoneYes on any link drop; broker usually does not queue for offline subscribersNoIn order per connectionHigh-rate position
1PUBLISH/PUBACK; retry with DUP=1 and the same packet idNo if session keptYes: a lost PUBACK causes redelivery; DUP is only a hint. Model 0.1-2% on lossy cell and 5-15% of in-flight messages around reconnects (estimate)Retried messages can be overtaken by new onesDefault telemetry
2PUBREC/PUBREL/PUBCOMPNoNo at protocol levelIn orderAlarms and billing; about 2x round trips, rare on cellular
  • Resend happens only on reconnect with Clean Start=0 and a session present. A clean session discards all in-flight and queued messages.
  • Packet ids are reused after completion, so DUP=1 with the same id is ambiguous.
  • Keepalive 30-120 s; cellular NAT timeouts 30-300 s so use 60 s or less; reconnect backoff 1-60 s with jitter; TLS handshake 2-4 RTT and 3-6 kB (resumption about 1 kB) (estimates).
  • AWS IoT Core allows 128 KB maximum message size; device buffers are often 16-64 KB.
  • Cloud dedupe key: (device_id, boot_id, seq). A sequence reset on reboot is a classic source of false duplicates and gaps.

5. Timestamps, clock drift and ordering

Timestamp behaviours for the generator (all estimates)
SourceBehaviour
GNSS-disciplined timeError under 1 ms to 1 s; lost in tunnels, then free-runs on RTC
Crystal RTC 20-50 ppmDrift 1.7-4.3 s/day; a cheap RTC up to 100 ppm drifts 8.6 s/day
Bus frames carry no timestampGateway stamps at ISR (jitter 0.1-5 ms) or at batch time (error equals the batch window, up to 60 s)
Boot without fixTimestamp is 1970-01-01, 2000-01-01 or last-known; flag 0.1-1% of records
Backfill after outageRecord timestamp preserved; received_at is broker time; delta from minutes to days
Skew tail0.5-2% of devices offset by more than 5 s; 0.05% by more than 1 h (dead RTC battery, GPS week rollover)
NMEA 0183Time only in RMC/ZDA (1 Hz); other sentences are untimed

Ordering: QoS 1 retries can be overtaken by newer messages, and replay interleaved with live data arrives out of order (sections 4 and 6). Consumers should order by the record timestamp and detect gaps by sequence, not by arrival time.

6. Bridge failure modes (with frequency/signatures)

All inject rates below are estimates from the research file.

Failure modes, causes, signatures and rates
FailureCauseSignatureRate
CAN bus-offTEC above 255 (wiring, bitrate mismatch)All signals vanish for 0.1-5 s (hardware recovery 128x11 recessive bits, app restart 100 ms-10 s) or until power cycle0.05-0.5 episodes/vehicle/day; 1% of devices chronic
Wrong bitrateInstall or configError frames, no data0.5% of installs
Dropped framesRX FIFO overrun above 70% bus loadGaps in cyclic PGNs, alive-counter skips0.01-0.5%; up to 5% on an overloaded gateway
Duplicate framesGateway retry, bridged-bus echo, TP retransmitIdentical timestamp and value0.1-1%
TP session failureLost DT, timer expiryDM1/VIN missing, abort codes0.5-3% of multi-frame messages
Fast-packet corruptionSequence mismatch129029/127489 missing or stale0.5-2%
Buffer overflowLong outage, DTC stormSequence gap, oldest dropped0.5-2% of outages over 6 h
Timestamp skewSee section 5Future timestamp or 19700.5-2% of devices
Encoding errorsScaling x10/x100, signed/unsigned, endianness, J1939 0xFF/0xFFFF not-available and 0xFE/0xFExx error sentinels, NaNImpossible values (6553.5 km/h, 25.5)0.1-1% of records; sentinels frequent when a sensor is absent
Firmware or schema mismatchGateway firmware versus cloud parser or DBC versionUnknown IDs, shifted signals, dropped fields2-10% of fleet on old firmware for 1-4 weeks after rollout
Frozen valueECU fault or gateway repeats last valueFlatline with valid timestamp0.1-1% of signals
Reconnect stormTower restore, broker restartMass reconnect, burst replay, duplicatesEvent-based
Clock jumpNTP or GNSS correctionTimestamp steps by plus or minus secondsA few per month
GNSS jitterUrban canyon5-50 m jumps, HDOP above 5Continuous in cities

Format-level errors from the research: JSON can carry NaN/Inf, invalid UTF-8 and truncation at the buffer limit; a protobuf schema mismatch drops unknown fields and defaults to 0; a vendor binary firmware or parser mismatch garbles IO IDs.

7. Payload formats and data volumes per vehicle type

Payload formats relative to JSON
FormatSize vs JSONNotes
JSON1.0 (250-600 B per 10-signal record)Repeated keys, text numbers; errors include NaN/Inf, invalid UTF-8, truncation at buffer limit
CBORAbout 0.5-0.8 (one test 0.79); integer keys about 0.3-0.51:1 JSON data model
Protobuf0.2-0.5 numeric-heavy (4-5x smaller); about 0.66 mixed (9% smaller when both gzip)Schema mismatch drops unknown fields, defaults to 0
Vendor binary0.1-0.3Firmware or parser mismatch garbles IO IDs
Sparkplug BProtobuf plus metric aliasNBIRTH/DBIRTH/NDATA/NDEATH; topic spBv1.0/group/msgtype/edge/device has no tenant level

Measured reference points: Digital Matter reports 1-3 MB/month for battery trackers, 5-10 MB/month for wired trackers, 1-1.5 kB per NB-IoT heartbeat, and firmware of 300-500 kB.

Planning figures per vehicle type (estimates; overhead per publish about 50-100 B MQTT/TLS plus 40-80 B TCP/IP; fresh TLS 4-10 kB)
VehicleStreamsPublishesBytes per publishRecords/sDaily volume
Basic trackerGPS every 30 s moving, ignition1 per 60-300 s150-600 B JSON, 50-200 B binary0.030.1-0.5 MB
City bus (8-16 h)GNSS 1 Hz, speed, doors, passenger count, FMS J1939, TPMS1 per 10-60 s0.5-4 kB JSON (0.2-1 kB CBOR/PB)2-105-40 MB JSON; 1-8 MB binary or compressed
Long-haul truck (10-14 h)J1939 rpm/speed/fuel/DPF/EBS/axle, reefer Modbus, TPMS 6-18 wheels, GNSS1 per 10-30 s1-6 kB JSON5-3010-60 MB JSON; 2-15 MB binary
Ambulance (6-24 h, mostly idle)GNSS, siren/lights, engine, patient-bay temp/O2 (Modbus/BLE), device status1 per 5-30 s (1 s on dispatch)0.5-3 kB1-15 bursty3-30 MB
Ship (24 h)N2K engines 1-10 Hz x2, GNSS 1-10 Hz, nav, tanks, bilge, AIS, 0183, Modbus gensets1 per 5-60 s over satellite2-30 kB batched20-300 raw, 1-20 after deadband50 MB-2 GB raw; 5-100 MB reduced; under 5 MB on Iridium
Full raw CAN traceAll framesContinuous-500-3000 frames/s0.5-5 GB (not normally streamed)
Monthly volume per vehicle (estimates)
VehicleMonthly volume
Tracker3-10 MB
Bus150-1000 MB
Truck300-1500 MB
Ambulance100-600 MB
Ship1-30 GB (VSAT/Starlink); 50-300 MB (Iridium)

8. Multi-tenant topic, schema and policy design

Identity and multi-tenancy patterns
ConcernPattern
Device identityX.509 certificate per device (CN = device_id or IMEI); thing name {tenant}-{class}-{serial}; legacy user/password fallback; ICCID/SIM swaps cause identity drift
Topic namespacet/{tenantId}/v1/{vehicleClass}/{deviceId}/{telemetry|event|status|cmd|ack}; Sparkplug variant t/{tenant}/spBv1.0/{group}/NDATA/{edge}/{device}; LWT retained on .../status; the v1 segment allows schema evolution
IsolationACL: a device publishes only under t/${tenant}/.../${deviceId}/# and subscribes only to its own cmd; AWS policy variables such as ${iot:Connection.Thing.ThingName}; hard isolation via separate accounts
Per-tenant schemaschema_id or version in header or topic; tenants differ in signal set (bus: doors/pax; ambulance: patient-bay temp; marine: N2K PGN names), units (km/h vs kn, degC vs degF), key naming (camel vs snake), time format (epoch ms vs ISO-8601), and missing as null vs sentinel
QuotasPer-tenant msgs/s and bytes/day caps; throttling drops QoS 0 and delays QoS 1
Edge casesDevice moved between tenants (stale retained messages); duplicate client IDs cause connect-kick flapping every 1-5 s; wrong-prefix publish is rejected or disconnected by the ACL
Tenant variants: sampling policy, payload format and retention
TenantSampling policyPayload formatRetention
A: bus1 Hz GPS with 10 m deadbandSignals: doors, passenger count (research gives no format; see inference below)Raw 7-30 d; 1-min rollups 13 months; 1 h rollups 5-7 y; driver GPS trails shortened or anonymised after N days (GDPR)
B: ambulance1 Hz always, engine every 5 sSignals: patient-bay temperature (research gives no format)Medical audit 6-10 y; legal-hold flag
C: marine10 s on satellite, 1 s on port Wi-FiN2K PGN names, units in kn (research gives no format)Safety logs per flag state

Sampling policy is pushed through a retained config topic or device shadow; 1-10% of devices run an old policy for hours (config lag). Payload formats per tenant are not specified in the research. Inference: assign tenants from the format table in section 7 (for example JSON for bus, CBOR or Protobuf for ambulance, batched Protobuf for marine) as generator defaults.

9. Generator guidance for the simulator

The pipeline below is from the research. The concrete parameter choices in the blocks that follow are inference built from the cited ranges, not additional sourced facts.

Pipeline

  1. Truth signals at native bus rates, then the bus layer (frame loss, TP aborts, bus-off windows, sentinels).
  2. Gateway: drifting clock, deadband, batch window, codec, finite buffer.
  3. Link state machine: up, degraded and down per link, with Markov dwell times from section 3.
  4. Broker semantics: QoS, duplicates, reorder on replay.
  5. Cloud record with received_at, boot_id, seq, batch_id, fw_version, schema_version and tenant.
  6. Per-vehicle latent traits: clock ppm, firmware version, buffer size, link type, chronic-fault flag.
  7. Emit a ground-truth label for every injected imperfection.

Clock and timestamp parameters (inference from section 5)

  • Per-vehicle drift ppm: draw from uniform(20, 50); with 10% probability use uniform(50, 100). Drift seconds per day = ppm x 0.0864.
  • Skew tail: with p = 0.005-0.02 add an offset above 5 s; with p = 0.0005 add an offset above 1 h.
  • Record stamp error: at ISR, uniform(0.1, 5) ms; at batch time, uniform(0, batch_window) up to 60 s.
  • Boot without fix: with p = 0.001-0.01 per record set ts to 1970-01-01 or 2000-01-01 and label it.
  • Backfill: keep record ts, set received_at = broker time, so received_at minus ts equals the outage duration plus replay delay.

Link state machine (inference from sections 3 and 6)

  • States: up, degraded, down. Draw dwell times from exponential or lognormal distributions fitted to the outage ranges in the table in section 3.
  • Urban bus: down episodes uniform(5, 30) s, one handover glitch every uniform(1, 5) km, tunnel episodes uniform(30, 600) s.
  • Rural truck: 3-10% of drive time down, episode length 1-30 min with a heavy tail beyond 1 h.
  • Ship at sea: VSAT down 0.5-5% of time in episodes of 1-30 min; Starlink obstruction drops uniform(1, 15) s with re-acquisition uniform(1, 5) min; switchover from cellular uniform(5, 60) s.
  • Message loss on the wire: LTE 0.1-1%, LoRaWAN moving urban 10-30%.

Delivery and buffering (inference from sections 4 and 6)

  • QoS 1 duplicate probability: 0.1-2% per publish on a lossy cell; 5-15% of in-flight messages after a reconnect.
  • Buffer records capacity = flash_bytes / bytes_per_record, for example 128 MB / 40 B = about 3M. Overflow when outage_seconds x record_rate exceeds capacity; drop-oldest by default and leave a seq gap.
  • Replay at rate_multiplier uniform(1, 10) x realtime, interleaved with live data so arrival order differs from ts order.
  • Dedupe key (device_id, boot_id, seq); reset seq to 0 and bump boot_id on a reboot. Power-brownout reboots at 0.1-1 per day for weak-battery trucks.

Daily volume check (inference)

  • bytes_per_day = records_per_s x active_seconds x bytes_per_record x format_ratio, where format_ratio is taken from the section 7 format table (CBOR 0.5-0.8, Protobuf 0.2-0.5, vendor binary 0.1-0.3).
  • Validate output against the per-vehicle daily ranges in section 7; for example a city bus should land at 5-40 MB JSON per day.
Per-tenant variant table for the generator (policy values from the research; format and unit defaults are inference)
ParameterA: busB: ambulanceC: marine
GPS sampling1 Hz, 10 m deadband1 Hz always10 s on satellite, 1 s on port Wi-Fi
Engine samplingPer bus profile (research gives no value)5 sPer N2K rates (127488 at 100 ms, 127489 at 500 ms)
Extra signalsDoors, passenger countPatient-bay temperature, O2N2K PGN names
Default payload format (inference)JSONCBOR or ProtobufBatched Protobuf
Speed unit (inference)km/hkm/hkn
Typical publish interval10-60 s5-30 s (1 s on dispatch)5-60 s over satellite
RetentionRaw 7-30 d; rollups 13 months and 5-7 y; GDPR trail shortening6-10 y auditPer flag state
Config lag1-10% of devices on old policy for hours1-10%1-10%

10. How Sense and Queen carry this data

Source and status. This section restates TENANT_SUBVARIANT.md (checked against the code on 12 Oct 2026), the Sense documents layout-v1.md and BUILD.md, and META_STORAGE.md (prepared as local edits on 13 Oct 2026). On 13 Oct 2026 the database migrations were applied: the ClickHouse meta-storage migration (five statements), the static variant master (table and 13 seed rows for bus, truck and ambulance) and the subvariant column on the tenant table of all 16 business schemas that have one. Before the ClickHouse migration was applied it was run in a throwaway clickhouse-local process on the live server version (26.8.16.41); that run found a rejected tenant_profile definition, which was fixed with the table setting allow_dimensions_outside_sorting_key = 1. The new Queen, the platform API code and the new Sense are not deployed. Volume figures are estimates, not measurements. Queen is the cloud service that receives Sense's transmissions and writes them to ClickHouse.

10.1 The subvariant is stored on the tenant

A tenant with a variant (for example bus) can carry an optional subvariant (for example city-transit); an empty string means none. The field is a lowercase string matching [a-z][a-z0-9_-]{0,39}, accepted only when a variant is given; malformed values are rejected with HTTP 400. An existing tenant without one returns an empty string, and a later create call with a different non-empty value updates it (an empty or missing value never removes it).

  • Business database: column TENANT_DEF.subvariant VARCHAR(40) NOT NULL DEFAULT '', added by the migration 2026-10-12-tenant-subvariant.sql.
  • Static database (pn_static_db): master table TENANT_VARIANT_MASTER with id, variant, subvariant, label, description, sort order, active flag and created date, unique on (variant, subvariant); the row with an empty subvariant is the variant itself. Created and seeded with the bus subvariants by 2026-10-12-tenant-variant-master.sql; truck and ambulance are added by 2026-10-13-tenant-variant-master-truck-ambulance.sql. Further variants need only inserted rows, no code change.
  • Runtime tolerance: the platform PHP detects the column and the table at runtime, so the code can be deployed before the migrations. Without the column the subvariant is returned empty and not stored; without the master table (or with the static database unreachable) only the regular expression is checked. If the master has active subvariants for the variant, an unknown value is rejected with the allowed values listed.
  • Who reads it: the tenant create, list, keys-all and links endpoints return it; get_tenant_layout.php adds it to its tenant block, which is what Sense reads for the tenant identity. Sense keeps it in the setting building.subvariant only to choose the vehicle profile of its compatibility check. The stored layout format and set_tenant_layout.php are unchanged, so the layout sent to the platform does not carry it. The Controller and the simulator desktop app read it too (the simulator's change was uncommitted when checked).
Subvariants seeded in the variant master (applied to the static database on 13 Oct 2026)
VariantSubvariants (code: label)Migration
trucklong-haul: Long-haul truck; reefer: Reefer / cold-chain truck; tanker: Tanker truck2026-10-13
busstaff-shuttle: Hospital staff shuttle; city-transit: City transit; intercity-coach: Intercity coach; electric-fleet: Electric fleet2026-10-12
ambulancebls: Basic life support; als: Advanced life support; critical-care: Critical-care transport2026-10-13

10.2 Tenant meta in the transmission

Sense adds the subvariant to the tenant meta of every transmission (readings, alarms and messages) under the name subvariant, after the building manager's custom values. A custom value that is itself named subvariant is kept and the platform's is not sent; nothing is sent when the tenant has none. Sense and Queen are independent in this: a transmission with or without the subvariant meta is valid for both. In a transmission the entity group is the path down to the controller without the network, and the network is sent in the entity's meta.network.

10.3 What Queen stores: new columns and the tenant_profile table

Before this change Queen kept only a fraction of what it parsed: all tenant meta, tags, path and the non-coordinate location parts, the fixedread node meta, all entity meta and the impact and actor names of reading entities were lost on every table. The change is additive (nothing dropped or renamed, no TTL).

New columns on the three data tables (all CODEC(ZSTD); reading, notification, change)
ColumnHolds
tenant_meta (Map)The tenant meta, including the subvariant
tenant_tags, tenant_path, tenant_locationTags as sent; path as JSON text; name, address, lat, lng, city, state, country and region as text
node_locationThe node's location only when it differs from the tenant's (empty means the same)
meta (reading only)The fixedread node meta
entity_metaEntity meta: a map on reading, one map per entity (parallel to entity.name) on notification and change
entity_impact, entity_actorsImpact entity names and actor names of the entity
detail (change only)Entity manager, actors and each entity's impact entities in full, as JSON
  • bhairava.tenant_profile (new, durable, written by Queen): the tenant's own description (variant, group, tags, path, location, meta) per tenant and content, joined to the other tables on (owner_oid, tenant_type, tenant_name). It is an AggregatingMergeTree keyed with a meta_hash (first 16 hex digits of the SHA-256 of variant, group, tags, path, location and meta, with meta keys sorted), so a change of the subvariant or any custom value becomes a new row and the history is kept with the first and last time each was seen. Queen writes a row only when the profile changed or an hour has passed, about 24 small rows per tenant per day.
  • bhairava.sensor_profile.entity_meta (new column, written by the sampler): the stable part of a sensor's newest entity meta (alias, controller alias, network, device, address, object type and similar), with impact and actor names under the keys @impact and @actors. Per-packet keys (raw datagram hex, source and receive times, sequence numbers, status flags, values over 200 characters) are left out.

Still lost, by design or because the code cannot carry it: the transmission @ts (the parser never reads it, and Sense does not send it), the changerecord entity @variant, and the per-packet entity meta and fixedread node meta after the pruning below.

10.4 Pruning of readings and what lasts

The sampler (SensorSampler, every 5 minutes) drops each hourly bhairava.reading partition that ends at or before the watermark minus a 10-minute overlap, after every successful run. A reading therefore lives about 1 h 10 min to 2 h 15 min, longer only while the sampler is failing or disabled (a failed run drops nothing). Anything stored only in reading is gone after that; this is why the durable parts are copied to two small durable places.

What is kept, where and for how long
WhatWhereKept
Raw datagram, per-packet entity meta, fixedread node meta, the values themselvesreadingAbout 1 h 10 min to 2 h 15 min, then the partition is dropped
Rollups (entity_rollup, sensor_pass)Rollup tables24 hours (controller_rollup_daily 48 hours); they carry no meta
Tenant meta (subvariant, custom values), tags, path, location, variant, grouptenant_profileKept, one row per content change, with first and last seen
Stable entity meta, impact and actor names of a sensorsensor_profile.entity_metaKept; the latest, replaced as it changes (no history)
Notifications and state changes with their tenant and entity metanotification, changeKept (no TTL)
Sensor, controller and incident profiles; tagssensor_profile, controller_profile, incident, incident_windowKept

Estimate, not measured: the new columns add about 1.4 KB (0.9-2.0 KB) per reading row, mostly the raw datagram; at 26 events per second per bus and 24 buses that is about 75 GB written per day, but because of the pruning only about 3.8-7.1 GB uncompressed is on disk at any moment. Compression was not measured (5x to 10x is an expectation). The durable additions are small: about 0.3 MB a day written for tenant_profile for 24 buses. Whether raw meta should be kept at all, or for how long, is left to the owner; no TTL was applied.

10.5 Safe rollout order

  1. Done on 13 Oct 2026: run the migration migrations/2026-10-13-meta-storage.sql on ClickHouse (additive, metadata only, safe to re-run): the meta columns, sensor_profile.entity_meta and tenant_profile. The Queen that is running now names its columns in every statement, so it keeps working and the new columns stay empty. Run it right after a sampler run (they are 5 minutes apart).
  2. Not done yet: deploy the new Queen.
  3. Not done yet: deploy the new Sense where subvariants are wanted.

The platform migrations of 10.1 can run in any order relative to the code, because the PHP detects the column and table at runtime. Rolling Queen back afterwards is safe.

If the new Queen runs before the ClickHouse migration
MissingEffect
A new column on a data tableThe insert is refused (code 16). The table now falls back at once to the old column list: rows are saved without their meta (that meta is lost for good, the rows are not), an ERROR line says so, and the full insert is retried every 30 s. Before this change the same refusal either blocked all tables behind one stuck writer or dropped the rows.
tenant_profileIts rows are dropped and counted (profile_dropped), with an ERROR line once per 5 minutes; the data tables are not affected.
sensor_profile.entity_metaEvery sampler run fails, so the watermark does not move and nothing is pruned; no rows are lost, and reading grows until the migration runs. There is deliberately no fallback for this one.

10.6 What is not verified

  • Not deployed: the new Queen, the platform API code and the new Sense. The migrations are applied; the code that writes the new columns and tables is not running yet, so they are empty.
  • Validated in clickhouse-local before applying: the ALTER and CREATE TABLE statements (applied to the old definition they give the same columns as a fresh install, and a second run changes nothing), the insert formats with the new columns, and two profile rows merging into one with the first and last seen times kept. Not validated: ARRAY JOIN over the parallel entity arrays, and argMax over a Map. The HTTP status ClickHouse returns for code 16 was not observed (both 4xx and 5xx are handled and tested with a fake server).
  • That ADD COLUMN on reading blocks nothing while the sampler runs is reasoned, not tested. The tests (Queen's save handler and sampler checks) pass against a fake HTTP server and an in-memory store, not against ClickHouse.
  • The dashboard is not changed and does not use the new meta.

11. Uncertainty and assumptions

Most numbers in this report are engineering estimates, not verified from a fetched source; the research file tags them as such. Use them as priors and apply wide jitter.

  • Sourced figures: ISO-TP and NMEA 2000 framing, NMEA 0183 limits, MQTT QoS behaviour, LoRaWAN EU868 payload sizes and duty cycle, LTE-M, NB-IoT, VSAT, Starlink and Iridium Certus link figures, format size ratios, and Digital Matter data use.
  • Estimates: all failure rates, store-and-forward parameters, clock drift tails, coverage patterns, per-vehicle data volumes and the 4G, 5G and Wi-Fi link figures.
  • Inference (added in this report): the per-tenant payload format and unit defaults, and the generator distributions in section 9. The research gives tenant sampling policy and retention but not formats.
  • Source citations for some rows are partial: the J1939 BAM row cites standards documents but the numbers are estimates.
  • The topic structure and ACL patterns are design suggestions, not mandated standards. Sparkplug B has no tenant level in its topic namespace.

12. Sources

  1. AUTOSAR SAE J1939 Transport Layer specification
  2. Transport Protocol Vulnerabilities on SAE J1939 Networks (Colorado State)
  3. Copperhill: J1939 multipacket RTS/CTS session
  4. NDSS VehicleSec 2023 slides
  5. ISO 15765-2 (Wikipedia)
  6. Zephyr ISO-TP documentation
  7. Pico Technology: NMEA 2000 decoding
  8. open-ships/n2k framer
  9. Actisense: choosing the right NGW-1
  10. Panbo: AMEC NK-80 0183/N2K adaptor
  11. Tomorrow's Technician: TPMS sensor signals
  12. AWS IoT FleetWise
  13. Hubble: JSON vs CBOR for IoT payloads
  14. Auth0: beating JSON performance with Protobuf
  15. Eclipse Sparkplug topic namespace and state management v2.2
  16. EMQX: introduction to MQTT QoS
  17. ThingsBoard: MQTT QoS
  18. MQTT Google Group thread
  19. AWS re:Post: maximum MQTT payload length
  20. DTU: benchmarking cellular IoT technology
  21. Digital Matter: how much data do devices use
  22. arXiv 2106.13286 (NB-IoT)
  23. Embedded.com article on NB-IoT
  24. The Things Network: EU868 regional parameters
  25. The Things Network: fair use policy explained
  26. Boatnews: satellite internet for boats
  27. AWI: satellite connectivity notes
  28. UNOLS: Starlink appendix
  29. Iridium: maritime low latency
  30. Iridium Certus 100 fact sheet
  31. Digital Matter: SIM cards and data usage
  32. 1NCE playbook: vehicle telematics
  33. AWS IoT: connect and publish
  34. AWS IoT: connect policy
  35. HiveMQ: understanding MQTT topic namespace for IIoT