Theme
OEM telematics mapping
Most fleets do not stream raw OBD-II — they rely on the OEM's own telematics API. This page maps the published vendor schemas onto the N2E IoT contract.
The pattern is the same for every vendor:
[OEM API] ──poll/subscribe──> [Adapter (your service)] ──> /iot/telemetry (N2E)The adapter is just a small worker that runs on a schedule (Mercedes / IVECO / CAT publish at minute-level cadence, not 1 Hz). It pulls vehicle status, transforms each field into N2E's snake_case schema, and forwards the result.
Where to run the adapter
Run the adapter inside the Encore monorepo as a new service (e.g. backend/iot-mb, backend/iot-iveco, backend/iot-cat) — that way it inherits your authentication, observability and deployment story. It pushes into the same telemetryTopic PubSub the WebSocket endpoint uses.
Mercedes-Benz Fleet API (trucks and vans)
Mercedes ships a single B2B fleet API at https://connectivity.mercedes-benz.com that powers both heavy-duty trucks and the Vans Connect program. Auth is OAuth 2.0 (client credentials) tied to a fleet contract.
Typical resources you'll consume:
GET /vehicles/{vin}/odometerGET /vehicles/{vin}/fuelGET /vehicles/{vin}/locationGET /vehicles/{vin}/engineStateGET /vehicles/{vin}/tirepressureGET /vehicles/{vin}/serviceInterval
Responses follow the pattern:
json
{
"odometer": { "value": "142307.1", "unit": "KILOMETERS", "timestamp": "2026-05-19T08:32:11Z" }
}Field mapping
| Mercedes-Benz field | N2E TelemetryEvent field | Conversion |
|---|---|---|
vehicle.vin | vehicle_id | use the VIN as your N2E vehicleId, or look up a tenant-local id |
odometer.value (KILOMETERS) | odometer_km | parseFloat — already km |
odometer.timestamp | timestamp | ISO-8601, copy as-is |
location.latitude | coordinates.lat | float |
location.longitude | coordinates.long | float (note long, not lng) |
vehicleSpeed.value (KILOMETERS_PER_HOUR) | speed_kmh | float |
engineHours.value | engine_hours | float (hours) |
engineSpeed.value (RPM) | powertrain.engine_rpm | int |
engineCoolantTemperature.value (DEGREES_CELSIUS) | powertrain.coolant_temp_c | float |
oilPressure.value (KILOPASCAL) | powertrain.oil_pressure_kpa | float |
fuelConsumptionRate.value (LITRES_PER_HOUR) | powertrain.fuel_rate_lph | float |
| (not exposed) | powertrain.engine_torque_pct | default 0 if unavailable |
| (not exposed) | powertrain.boost_pressure_kpa | default 0 |
noxConcentration.value (PPM) | powertrain.nox_ppm | float |
Adapter sketch
ts
// backend/iot-mb/api.ts
import { CronJob } from 'encore.dev/cron'
import { telemetryTopic } from '../iot/events'
import type { TelemetryEvent } from '../iot/types'
import { fetchMb } from './mb-client'
export const sync = new CronJob('sync-mb', {
title: 'Pull Mercedes-Benz Fleet API',
every: '1m',
endpoint: poll,
})
export const poll = api({ method: 'POST', path: '/iot-mb/poll' }, async () => {
for (const vin of await fleetVins()) {
const [odo, loc, speed, eng] = await Promise.all([
fetchMb(`/vehicles/${vin}/odometer`),
fetchMb(`/vehicles/${vin}/location`),
fetchMb(`/vehicles/${vin}/vehicleSpeed`),
fetchMb(`/vehicles/${vin}/engineState`),
])
const frame: TelemetryEvent = {
vehicle_id: vin,
timestamp: odo.timestamp,
powertrain: {
engine_rpm: Number(eng.engineSpeed?.value ?? 0),
engine_torque_pct: 0,
fuel_rate_lph: Number(eng.fuelConsumptionRate?.value ?? 0),
coolant_temp_c: Number(eng.engineCoolantTemperature?.value ?? 0),
oil_pressure_kpa: Number(eng.oilPressure?.value ?? 0),
boost_pressure_kpa: 0,
nox_ppm: Number(eng.noxConcentration?.value ?? 0),
},
speed_kmh: Number(speed.value ?? 0),
odometer_km: Number(odo.value ?? 0),
engine_hours: Number(eng.engineHours?.value ?? 0),
coordinates: {
lat: Number(loc.latitude),
long: Number(loc.longitude),
},
}
await telemetryTopic.publish(frame)
}
})IVECO ON
IVECO ON is IVECO's connected-services platform for trucks and buses. It exposes a REST API (OAuth 2.0) similar in shape to the Mercedes one. Field names are slightly different but the units are consistent.
| IVECO ON field | N2E TelemetryEvent field | Notes |
|---|---|---|
asset.id or vin | vehicle_id | prefer VIN |
timestamp (ISO) | timestamp | UTC |
position.lat | coordinates.lat | |
position.lon | coordinates.long | rename lon → long |
vehicleSpeed (km/h) | speed_kmh | |
totalOdometer (km) | odometer_km | |
totalEngineHours | engine_hours | |
engineRpm | powertrain.engine_rpm | |
engineLoadPct | powertrain.engine_torque_pct | approximate proxy for torque% |
fuelRate (L/h) | powertrain.fuel_rate_lph | |
coolantTemperature (°C) | powertrain.coolant_temp_c | |
oilPressure (kPa) | powertrain.oil_pressure_kpa | convert from bar × 100 if needed |
turboBoostPressure (kPa) | powertrain.boost_pressure_kpa | |
defNoxOut (ppm) | powertrain.nox_ppm | downstream NOx sensor |
Adapter pattern is identical to the Mercedes one — only the auth and field names change.
Caterpillar VisionLink (cat.com)
VisionLink covers Cat® on-highway trucks, off-highway haulers and heavy equipment. The Unified Suite API exposes asset health and operational data.
| VisionLink field | N2E TelemetryEvent field | Notes |
|---|---|---|
serialNumber or assetId | vehicle_id | use serial for off-highway, VIN for on-highway |
lastReportedTime | timestamp | ISO-8601 |
location.latitude | coordinates.lat | |
location.longitude | coordinates.long | rename |
lastKnownSpeed (km/h) | speed_kmh | convert from mph if applicable |
cumulativeOperatingHours | engine_hours | |
currentHourMeter | engine_hours | fallback when cumulative not present |
cumulativeIdleHours | (drop) | not stored on the wire |
fuelRemaining (%) | (drop) | use fuelConsumptionRate instead |
fuelConsumptionRate (L/h) | powertrain.fuel_rate_lph | |
engineSpeed (rpm) | powertrain.engine_rpm | |
engineLoadFactor (%) | powertrain.engine_torque_pct | proxy for torque% |
coolantTemperature (°C) | powertrain.coolant_temp_c | |
oilPressure (kPa) | powertrain.oil_pressure_kpa | |
intakeBoostPressure (kPa) | powertrain.boost_pressure_kpa | |
aftertreatmentNoxOut (ppm) | powertrain.nox_ppm | |
cumulativeDistance (km) | odometer_km | for on-highway haulers |
Off-highway equipment
For excavators, dozers and other off-highway assets that don't have a meaningful odometer_km or speed_kmh, send 0 for those fields and rely on engine_hours for utilisation. The AI risk rules already tolerate zeroed motion fields.
Generic adapter checklist
When you write a new OEM adapter:
- Authenticate with the OEM (usually OAuth 2.0 client credentials). Cache the token and refresh on 401.
- List the VINs / asset ids your tenant is allowed to query.
- Schedule a poll at the OEM's recommended cadence (typically 60 s).
- For each asset, pull the resources you need and merge them into one
TelemetryEvent. - Drop fields the OEM doesn't expose — set them to
0rather than omitting (the schema is non-optional). - Publish to
telemetryTopicso the rest of the pipeline (storage, AI risk, dashboard) lights up automatically. - Add a
last_seentimestamp per asset so you can surface stale feeds in the Logs module.
Trip-end mapping
OEM APIs typically expose a trip or journey resource that closes when the ignition turns off. Map them to the trip-end payload:
| OEM concept | N2E TripEndEvent field |
|---|---|
trip.id / journey.uuid | trip_id |
trip.endedAt | end_timestamp |
trip.videoFileSizeGb (if dashcam integrated) | video_metadata.file_size_gb |
trip.durationMinutes | video_metadata.duration_minutes |
If the OEM does not stream dashcam metadata, send zeros. The trip-end pipeline keys off vehicle_id + trip_id only.
Field reference disclaimer
Field names above reflect each OEM's publicly documented API surface at the time of writing. Vendor APIs evolve — always confirm exact resource paths, units, and authentication scopes with the OEM's developer portal before going to production:
- Mercedes-Benz: https://connectivity.mercedes-benz.com
- Mercedes-Benz Vans Connect: https://www.mercedes-benz.co.uk/vans/services/connect-your-fleet.html
- IVECO ON: https://www.ivecobus.com/worldwide/IVECO-Services/iveco-on
- Caterpillar VisionLink: https://vl.cat.com/
If a field is missing or the unit differs, do the conversion in your adapter — never change the N2E contract.