Haltech CAN Protocol Specification

Technical Library › Haltech CAN Protocol InformationUpdated Sep 2026

If you're building a logger, a gauge or your own controller that listens to a Haltech ECU, the protocol documents are only useful once you know how to read them. This guide explains the notation Haltech uses, how to turn raw numbers into real units, and how to pull several on/off flags out of a single value.

Who this is for

Haltech publishes CAN protocol documents that list every message its ECUs and devices put on the bus: which ID, which bytes, and how to scale the numbers. They're written for people integrating other hardware, such as a data logger, a microcontroller project or a third-party display. The documents all share the same conventions, and those conventions are what this article covers. For the actual message lists, see the Haltech CAN ECU broadcast protocol and the PD16 protocol.

The bus basics

PropertyHaltech CANWhat it means for your device
Bit rate1 Mbit/sYour CAN controller has to be set to 1000 kbit/s or it won't see valid frames
Identifier length11-bit (standard) IDsDon't configure extended 29-bit filters
How IDs are writtenHexadecimalAn ID in the document like 0x360 is 864 in decimal
Byte orderBig-endianIn a multi-byte value, the first byte holds the most significant part

Each CAN frame carries up to eight data bytes. Haltech counts them from zero, so the first is byte 0 and the last is byte 7. Inside a byte, bit 7 is the most significant and bit 0 the least.

Reading the location notation

Every channel in a protocol table has a position that tells you exactly where its raw number lives in the frame. There are four forms you'll run into:

Written asMeaningExample
4One whole byteThe value is just byte 4, 0 to 255
2 - 3Several whole bytes, both ends includedA 16-bit value made from bytes 2 and 3
5:1One bit, written byte:bitBit 1 of byte 5, a single on/off flag
6:3 - 7:0A run of bits that can cross a byte boundary, both ends includedA 12-bit value from bit 3 of byte 6 down to bit 0 of byte 7

Putting a multi-byte value together

Because the data is big-endian, the lower-numbered byte is the high byte. For a channel at bytes 2 - 3:

raw = (byte2 × 256) + byte3

So if byte 2 is 0x01 and byte 3 is 0xC8, the raw value is 256 + 200 = 456. A four-byte value at 4 - 7 works the same way, with byte 4 as the most significant. If the protocol lists a channel as signed, interpret the assembled number as two's complement before scaling it.

Pulling out a bit range

For a bit range, shift and mask. Using the 6:3 - 7:0 example: take bits 3 to 0 of byte 6 (four bits) as the top of the value and all eight bits of byte 7 as the bottom, giving 12 bits in total. In code that's ((byte6 & 0x0F) << 8) | byte7. Draw the bits out on paper the first time. It saves a lot of guessing.

From raw number to real units

Raw values are integers, so Haltech scales them to keep resolution. Each channel in the protocol table has a "Conversion from Raw" formula and a units column. In the formula, x is the raw number from the frame and y is the result in the listed units.

Take the raw value 456 we assembled above. If that channel were listed with a conversion of y = x / 10 and units of %, your device would display 45.6%. Some channels also carry an offset, for example a subtraction, so always apply the whole formula exactly as written.

The conversion only gets you to Haltech's units. If you want something else, like psi or Fahrenheit, your device has to do that step itself. The table below collects the conversions we use most often.

Units used in Haltech protocols

UnitMeasuresUseful to know
RPMRotational speedUsually engine (crankshaft) speed
kPa (abs)Absolute pressure0 is a perfect vacuum, standard atmosphere is 101.3
kPa (gauge)Pressure relative to atmosphere0 is standard atmosphere, a perfect vacuum is -101.3
%PercentageThrottle, duty cycles and similar
°AngleOne revolution is 360°; a full four-stroke cycle is 720° of crank rotation
lambdaMixture1.0 is stoichiometric for whatever fuel is in use
KTemperatureKelvin steps are the same size as Celsius steps; 0 K is absolute zero
km/hSpeedDivide by 1.609 for mph, or by 3.6 for m/s
m/s²AccelerationDivide by 9.8 for G
cc/minVolume flowThe usual rating for injectors
cc and LVolume1000 cc is 1 L; divide cc by 29.574 for US fl oz, and L by 3.785 for US gallons
ppmConcentrationOne million ppm is a pure substance
g/m³DensityWater is about one million g/m³
V, dBVoltage, decibelsUsed as-is
ms, s, min, hTime1000 ms per second, 60 s per minute, 60 min per hour

Pressure is always absolute

Every pressure Haltech sends is absolute. That catches people out when a boost gauge shows 101 at idle with the engine off. To get gauge pressure, subtract 101.3 kPa. To get psi, divide kPa by 6.895. So a raw MAP reading that converts to 239 kPa (abs) is about 137.7 kPa gauge, or roughly 20 psi of boost.

Temperature arrives in Kelvin

  • Celsius: °C = K - 273.15
  • Fahrenheit: °F = (K - 273.15) × 9/5 + 32

As a sanity check, water freezes at 273.15 K and boils at 373.15 K. A coolant reading of 363.15 K is 90 °C, or 194 °F.

Lambda to AFR

Multiply lambda by the stoichiometric ratio of the fuel: 14.7 for gasoline, 9 for ethanol, 6.47 for methanol. A lambda of 0.80 on gasoline is 11.76:1 AFR. If you're running a flex fuel blend, remember the correct multiplier changes with ethanol content, which is one reason we prefer to display lambda.

Decoding bit fields

Some channels pack several yes/no states into one number. Each state is given its own power of two (1, 2, 4, 8 and so on), and the value sent is the sum of every state that's currently true. Haltech's "Engine Limiting Method" channel is a good example: fuel limiting is 1 and ignition limiting is 2. If both are active, the value is 3.

By hand

  1. Find the largest listed value that fits into the number you have. That state is on.
  2. Subtract it.
  3. Repeat with what's left until you reach zero.

Here's a made-up field to practice on: A = 1, B = 2, C = 4, D = 8, and the received value is 13. The largest that fits is 8, so D is on, leaving 5. Then 4 fits, so C is on, leaving 1. Then 1 fits, so A is on, leaving 0. States A, C and D are active and B is not.

With a calculator

Windows Calculator has a Programmer mode (open the menu and pick Programmer). Make sure it's in DEC, type the number, and read the BIN line. Reading right to left, the digits are worth 1, 2, 4, 8, 16 and so on, and every 1 is a state that's on. 13 shows as 1101: the 1, 4 and 8 positions are set.

In code

Test each flag with a bitwise AND, for example if (value & 0x02) to check whether the state worth 2 is set. It's faster and clearer than repeated subtraction.

Troubleshooting a CAN integration

SymptomUsually
No frames received at allWrong bit rate, CAN High and Low swapped, or missing termination
Values jump wildly or look hugeBytes assembled in little-endian order instead of big-endian
Boost reads about 101 kPa with the engine offAbsolute pressure being shown as gauge; subtract 101.3
Temperatures read around 300Kelvin not converted
A value is off by a factor of 10 or 1000Conversion formula skipped or applied twice

CAN protocol FAQ

Can I add my own device to a Haltech CAN bus?

Yes, as long as it runs at 1 Mbit/s, uses 11-bit IDs and doesn't transmit on IDs Haltech devices already use. If you want more than one controller sending data, read multi-master CAN first.

What hardware do I need to connect?

A 4-port CAN hub and a Haltech CAN cable make it easy to tap into the bus without cutting the factory wiring. The Haltech CAN system overview explains how the network is laid out.

Where do I get the full message list?

Start with the broadcast protocol article linked above. If you need a specific protocol document for your project, contact us and we'll point you to the right version.