I've spent an embarrassing amount of my life staring at my car's dashboard wondering what's actually going on underneath all that sheet metal. RPM, coolant temp, throttle position, battery voltage...the data is all right there, flying by on a wire. For most of us it may as well be locked in a vault!
In reality it's not locked away at all. Every modern vehicle is a rolling sensor network, and with a little hardware you can tap into that network, decode it, and (because you knew I was going there) sync it to the cloud with Blues Notecard and Notehub.
In this blog post we'll cover what the CAN bus actually is, how it differs from OBD-II (these get conflated often), how to physically tap the bus and read raw frames, how to decode those frames into real signals, and finally how to bridge all of it to the cloud with a Notecard.
Want to skip ahead and just build the thing? I put the complete project up on Hackster: Monitoring Vehicle CAN Data Over OBD-II with Blues Notecard
What Is the CAN Bus?
CAN (Controller Area Network) is a serial communication protocol that lets the dozens of independent computers in your vehicle (the engine control unit, the ABS module, the body control module, and so on) talk to each other without a central master device sitting in the middle.
Bosch introduced it back in 1986 for exactly the kind of environment a car presents: electrically noisy, safety-critical, and full of components from different suppliers that all need to agree on how to communicate. It's been the backbone of automotive electronics ever since, and it's extremely common in industrial automation for the same reasons.
A few things make CAN well-suited to a moving vehicle:
- It's a two-wire differential bus (
CAN-HandCAN-L). Reading the difference between the two lines shrugs off the electrical noise that a single-ended signal would choke on. - Nodes coordinate without a controller. Any module can broadcast, and a clever bit-by-bit arbitration scheme decides who wins when two modules talk at once, and the lower message ID gets priority.
- It's robust and standardized by ISO. Roughly 40 meters of range at 1 Mbps, and much further at lower speeds.
If you want to go deeper on the electrical signaling and arbitration, we have a whole chapter on it in our sensor interfaces guide.
For our purposes here, just hold onto one idea: the modules in your car are constantly broadcasting little 8-byte messages to each other, and the CAN bus is the wire they shout across.
CAN Bus vs OBD-II: Connector vs Protocol
This is the single most important distinction in this entire post, so let's nail it down before we touch any hardware.
OBD-II is not the CAN bus. OBD-II (On-Board Diagnostics, second generation) is a standardized diagnostic interface that's been mandatory on US vehicles since 1996. It's two things bundled together:
- A physical connector, the trapezoidal 16-pin port usually hiding under the steering column.
- A request/response protocol, where you ask the vehicle a question ("what's the engine RPM?") and it answers.
On almost every vehicle built in the last 15+ years, OBD-II rides on top of the CAN bus. So when you plug into the OBD-II port and request data, you're really sending CAN frames to the vehicle's ECU and getting CAN frames back, just in a nicely standardized, documented format.
Physically Tapping the Bus
The friendliest way onto a vehicle's CAN network is the one that requires no cutting or splicing at all: the OBD-II port.
The OBD-II Port

The OBD-II connector breaks out CAN directly on two of its pins:
| OBD-II Pin | Signal |
|---|---|
| Pin 6 | CAN-H |
| Pin 14 | CAN-L |
| Pin 16 | 12V battery (always on) |
| Pins 4 / 5 | Chassis / signal ground |
This is wonderful for a couple of reasons. You get clean access to CAN-H and
CAN-L and a 12V supply, all from one connector, with zero cutting or
splicing. Grab an OBD-II breakout cable and you've got a tidy place to land your
wires.
You Still Need a Transceiver
Whichever way you get onto the bus, your microcontroller cannot talk to CAN directly. CAN signals at differential voltage levels that an MCU's GPIO pins can't produce or safely read. You need two pieces in the chain:
- A CAN controller that handles framing, arbitration, and error checking.
- A CAN transceiver that converts the controller's logic-level signals into
the actual
CAN-H/CAN-Lvoltages on the bus (and back).
Some MCUs (like the ESP32) have a CAN controller built in and just need an external transceiver. To keep my own project simple, I reached for the Adafruit Feather M4 CAN Express, which has both the controller and an onboard transceiver. One board, done.
Reading Raw CAN Frames
With a transceiver wired up, listening to the bus is refreshingly simple. A raw CAN frame is just an ID plus up to 8 data bytes, and a library like Adafruit's CAN support hands them to you one at a time.
#include <CANSAME5x.h>
CANSAME5x CAN;
void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
// Most vehicle buses run at 500 kbps; some comfort/body buses are slower.
if (!CAN.begin(500000)) {
Serial.println("Starting CAN failed!");
while (1) delay(10);
}
Serial.println("Listening for CAN frames...");
}
void loop() {
int packetSize = CAN.parsePacket();
if (packetSize) {
Serial.print("0x");
Serial.print(CAN.packetId(), HEX);
Serial.print(" [");
Serial.print(packetSize);
Serial.print("] ");
while (CAN.available()) {
Serial.print(CAN.read(), HEX);
Serial.print(" ");
}
Serial.println();
}
}Fire that up with the engine running and your serial monitor turns into a firehose:
0x7E8 [8] 4 41 C 1A F0 0 0 0
0x244 [8] 0 0 5 DC 0 0 1 8C
0x7E8 [8] 4 41 D 2D 0 0 0 0
0x3B7 [8] 20 0 0 0 FF FF 0 0
...Congratulations, you are now eavesdropping on your car. The problem is
immediately obvious: what does any of this mean? That 0x244 frame is some
module broadcasting something, but without a key, it's just hex.
That's the whole game from here on: turning those bytes into signals.
Decoding the Frames
The easiest path to meaning is to stop passively listening and instead ask the
vehicle for specific values using OBD-II PIDs (Parameter IDs). You send a request
frame to ID 0x7DF (the
OBD-II broadcast address), the ECU answers on 0x7E8, and the PID format is
publicly documented and identical across compliant vehicles.
To request engine RPM (Mode 01, PID 0x0C):
// Request: [num data bytes][mode][PID][padding...]
uint8_t request[8] = { 0x02, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00 };
CAN.beginPacket(0x7DF);
CAN.write(request, 8);
CAN.endPacket();The ECU responds with a frame whose data bytes follow a known formula. For RPM,
the value lives in bytes A and B and the formula is ((A * 256) + B) / 4:
// response data: 41 0C A B ...
if (mode == 0x41 && pid == 0x0C) {
float rpm = ((data[3] * 256.0) + data[4]) / 4.0;
Serial.printf("Engine RPM: %.0f\n", rpm);
}A handful of PIDs covers most of what people actually want:
| PID | Signal | Formula |
|---|---|---|
0x0C | Engine RPM | ((A*256)+B) / 4 |
0x0D | Vehicle speed | A (km/h) |
0x05 | Coolant temp | A - 40 (°C) |
0x11 | Throttle position | A * 100 / 255 (%) |
0x2F | Fuel level | A * 100 / 255 (%) |
0x42 | Module voltage | ((A*256)+B) / 1000 (V) |
Because OBD-II PIDs are standardized, you can write decoding logic once and have it work across nearly every vehicle on the road. This is exactly why I built my project around PIDs rather than raw frames.
Bridging Vehicle Data to the Cloud
Decoding CAN data on a board sitting in your car is neat. Getting it onto a dashboard you can pull up from your couch is the part that turns a science experiment into an actual IoT product.
That's where Notecard comes in. It's a small system-on-module that provides cellular connectivity (with satellite/NTN, LoRa, and WiFi variants also available), and you communicate with it using JSON requests over I2C or serial.
In my build, the Feather M4 CAN Express decodes the PIDs, and the Notecard, on a
Notecarrier F,
handles getting the data out. Queuing up a reading is just a note.add:
J *req = NoteNewRequest("note.add");
JAddStringToObject(req, "file", "diagnostics.qo");
JAddBoolToObject(req, "sync", true);
J *body = JCreateObject();
JAddNumberToObject(body, "rpm", rpm);
JAddNumberToObject(body, "speed_kph", speed);
JAddNumberToObject(body, "coolant_c", coolantTemp);
JAddNumberToObject(body, "throttle_pct", throttle);
JAddItemToObject(req, "body", body);
NoteRequest(req);That Note (i.e. event) gets queued on the Notecard, synced over cellular to Notehub (Blues' cloud service), and from there a Notehub Route forwards it to whatever cloud or dashboard you like, something like this can be pretty easily vibe-coded with virtually any AI these days using the Notehub API:

A couple of details from my Hackster build that are worth stealing:
- Two queues, two cadences. Routine diagnostics batch up and sync on an
interval (every 30 minutes was plenty for my use case), which keeps cellular
usage (and power draw) low. A separate
alerts.qofile syncs immediately when something interesting happens. - The car powers the rig. OBD-II pin 16 gives you always-on 12V. A buck converter drops it to 5V for the electronics, and a small LiPo on the Notecarrier rides out the moment the engine is off.

Is it the prettiest wiring job I've ever done? It is not. But it sat in my center console, sipped power, and faithfully streamed my car's vitals to the cloud the entire time.

A Few Honest Trade-Offs
Before you go zip-tying a microcontroller to your steering column, let's talk about the things the cheerful tutorials tend to skip.
Stay read-only. This is the big one. Listening to a CAN bus is low risk. Transmitting arbitrary frames onto a live vehicle bus is a genuinely different animal. You're sharing a network with systems that steer, brake, and manage the engine. Request OBD-II PIDs, listen to broadcasts, and treat the bus as read-only. Save any bus-writing experiments for a bench setup or a vehicle you fully understand and control.
Mind your sampling rate. It's tempting to poll every PID as fast as possible, but each OBD-II request is a round-trip to the ECU, and hammering the bus is both rude and pointless, since most of these values don't change meaningfully at 100 Hz. Pick a sensible interval per signal (RPM fast, coolant temp slow) and you'll get cleaner data with less overhead.
Don't sync every frame to the cloud. A car's bus produces thousands of frames per second. Cellular data and battery life are finite. The whole reason the Notecard's queue-and-batch model works so well here is that it lets you do the filtering and aggregation on the edge: decide what actually matters, then send that.
Bit rate and PID support vary. Most modern vehicles run their powertrain bus at 500 kbps, but not all, and not every vehicle supports every PID. A little trial and error per vehicle is normal.
Wrapping Up
The data your car has been quietly broadcasting this whole time is more accessible than it looks. Get onto the bus with a transceiver (the OBD-II port is the friendly way in), request standardized OBD-II PIDs, do your filtering at the edge, and let Notecard carry the interesting bits to the cloud.
If you want to build the exact rig from this post, the full parts list, wiring, and firmware are waiting for you over on Hackster: Monitoring Vehicle CAN Data Over OBD-II with Blues Notecard.
And when you're ready to add cellular to your own automotive project, the easiest way to get everything you need in one box is the Blues Starter Kit. It pairs a Notecard with a Notecarrier so you can go from unboxing to syncing data in minutes.
Happy Hacking, and keep your hands off CAN-H! 🚗💙

