Build a CODESYS J1939 generator alarm panel

9 min read · Published November 3, 2026

Yes, CODESYS does this job. The J1939 diagnosis API gives you Read_DM1 and Read_DM2 for the active and stored fault lists, PGN parameters decode as engineering value = raw × resolution + offset, and start, stop and fault clear stay a separate task that needs the engine manufacturer's command document. Commission the monitoring panel first, then add commands once that document exists.

CODESYS does this job, and the reading half is the easy part. The J1939 diagnosis API that ships with the CODESYS J1939 library gives you ready-made Read_DM1 and Read_DM2 function blocks: DM1 is the active fault list with the dash lamps, DM2 is the stored history. Operating parameters decode with one line of arithmetic, engineering value = raw × resolution + offset. The command half, start, stop and fault clear, is a separate project that stands on the engine manufacturer's command interface, and until that document is on your desk the panel is a monitor. Build it in that order and the alarms are on screen in the first week.

What you need before you write logic

  • A CODESYS-capable controller with a CAN port the runtime drives, plus the J1939 library and its diagnosis API for that runtime version. Running CODESYS gives you the runtime; the CAN port and the library support come from the device description, so check both before you buy hardware.
  • The engine's J1939 interface document: bus speed, the engine's source address, the PGN list with byte positions, resolutions, offsets and units, the fault text list, and any commands the engine accepts.
  • Two 120 Ω terminators, one for each end of the bus.
  • The generator set's own controller display or an engine service tool, to check the panel against the engine's own truth.

Bring the bus up before decoding anything

  1. Wire CAN-H and CAN-L straight through between the engine and the controller, and fit a 120 Ω terminator at each physical end. Keep the backbone under 40 m, the length the J1939-11 physical layer allows at 250 kbit/s, and keep stubs short.
  2. With power off, measure resistance across CAN-H and CAN-L. About 60 Ω, anywhere in 55 to 65 Ω, says both terminators are present. A reading near 120 Ω says one end is open; a reading in the thousands says the bus is broken.
  3. Configure the port at 250 kbit/s, the bitrate J1939 specifies, and load the J1939 driver with the controller on a source address the engine and the genset controller do not use.
  4. Watch the frame and error counters in the CAN diagnostics. Errors that climb point at wiring, speed or termination. Frames that count with nothing decoded point at source address selection, which is the next step.
  5. Identify the engine by source address. The standard assigns address 0 to the first engine, and an engine re-claims its address at power-up through address claiming, so bind the application to the identity the engine claims. With the genset controller also on the bus, check you are reading the engine as its own source.

Read the fault lists with Read_DM1 and Read_DM2

DM1, PGN 65226, is the active list: the codes failing now, plus the state of the protect, amber warning, red stop and malfunction indicator lamps. It repeats about once a second while a fault is active. DM2, PGN 65227, is the previously active list, the engine's own history. Show the two on separate panel pages so a stored code never reads as a live condition.

Each code is 4 bytes: a 19-bit suspect parameter number (SPN), a 5-bit failure mode identifier (FMI), a 7-bit occurrence count and one spurious-fault bit. The function blocks return these as fields.

Block PGN What the panel gets Acceptance check
Read_DM1 65226 Active codes and lamp states, repeating about every 1 s while a fault is active Matches the service tool's active list and the engine's physical lamps
Read_DM2 65227 Previously active codes Matches the service tool's stored list
  1. Wire the blocks as the library help documents for your runtime version: source selection, trigger, completion and error outputs. Pin names move between versions, so read the block page first.
  2. Map each SPN and FMI pair to the engine's fault text list. The job is the same one as configuring operator text in HMI alarms: the code picks the row, the words are written for the operator.
  3. Drive the panel's alarm lamps straight from the DM1 lamp bytes, so the panel and the dash never disagree.
  4. Treat an empty list as healthy only after a completed read. A timeout or an unsupported response raises a communication alarm; it never displays as "no faults".
  5. A DM1 with several active codes arrives as multiple frames under the J1939 transport protocol. If the panel shows one code while the service tool shows three, check the transport receive path and its buffer.

Decode the operating parameters

A PGN identifies a message, and one message carries several parameters, each at a fixed byte position with its own scaling. Take every item from the engine document; the standard definitions in SAE J1939-71 fill the gaps, and the engine document wins where the two differ.

Parameter PGN SPN Length Decode
Engine speed 61444 190 2 bytes raw × 0.125 rpm + 0 rpm
Coolant temperature 65265 110 1 byte raw × 1 °C - 40 °C
  1. Read each parameter's PGN, start byte, bit length, byte order, resolution, offset and special-value encodings from the engine document. J1939 sends multi-byte values least significant byte first.
  2. Decode linear parameters with engineering value = raw × resolution + offset.
  3. Screen the special encodings before the arithmetic. For a 2-byte parameter, 0xFFFF is "not available" and 0xFEFE is "error"; show both as a dash with the reason, never as a number.
  4. Put a freshness timer on every displayed value. Three seconds is a working figure: short enough that a dead bus is flagged inside a glance, long enough to ride normal gaps between transmissions. When it expires, grey the value out and show the time of the last update.
  5. Decode one parameter first and check it against the engine's own instrument before wiring the rest. The example below does exactly that.
  6. When a client elsewhere needs the same numbers, publish them from the same variables and set up an OPC UA server in CODESYS for that client.

For a walk-through of one engine data message and what usually breaks it, read troubleshooting J1939 PGN 127488 engine data.

Example: decoding engine speed on a 50 Hz generator set

Inputs:

  • Parameter: SPN 190, engine speed, carried in PGN 61444, the engine controller's EEC1 message, 2 bytes at bytes 4 and 5. The definition comes from SAE J1939-71 and is printed in the engine's J1939 document: resolution 0.125 rpm per bit, offset 0 rpm.
  • Raw bytes captured on the running set: 0xE0 then 0x2E.
  • The engine document says the alternator is a 4-pole machine making 50 Hz.

Rule: assemble the two bytes least significant first into the raw word, then compute engineering value = raw × resolution + offset.

Arithmetic:

  • Raw word: 0x2EE0 = 12000.
  • 12000 × 0.125 rpm = 1500 rpm.
  • 1500 rpm + 0 rpm = 1500 rpm.
  • Cross-check: a 4-pole alternator at 50 Hz spins at 120 × 50 / 4 = 1500 rpm.

Result: the panel shows 1500 rpm, and the number agrees with the machine's tachometer and with the mains frequency.

Decision: if the panel agrees within 8 counts, that is 1 rpm, the byte position, byte order and scaling are all correct, and you copy the pattern to every other linear parameter. A result near 5.75 rpm means one byte was read alone (46 × 0.125). A result near 7174 rpm means the bytes were assembled most significant first (0xE02E × 0.125). On a 60 Hz 4-pole set the same decode reads 1800 rpm, raw 14400.

Start, stop and fault clear: the command half

The diagnosis blocks read the engine. Commands are a different interface, and the engine manufacturer defines it: proprietary PGNs on the bus, discrete wiring, or the genset controller's remote input. Write none of it from guesswork.

  1. Get the command document: the message or the wire, the remote-mode enable, the permissives such as crank limits and cooldown, the timing, and the feedback signals. A document shaped like the engine throttle control SPN and PGN configuration example shows the usual pattern: enable first, command value second, feedback third.
  2. Gate every command on the enable and the permissives. The panel sends a request; the engine controller decides.
  3. Close the loop on feedback. A start is complete when engine speed rises and the alternator reaches voltage; a stop is complete when speed reaches 0.
  4. Clear faults with the J1939 clear services: DM3 clears previously active codes, DM11 clears active codes. Whether the library ships a block for them, read its function block list; the engine decides whether a clear is allowed while running. After any clear, run fresh Read_DM1 and Read_DM2 reads and show the result.

Keep the emergency stop and the engine's protective shutdowns wired in hardware, outside the panel, so the engine protects itself if the panel or the bus dies, and put a physical remote-mode selector in front of the start command so a touch never cranks the set.

Keep the panel's alarm acknowledge separate from the engine's fault state. Acknowledging on the HMI silences the horn and marks the row; the code stays active in DM1 until the engine clears it.

Make the alarms readable where people stand

Size the text for the distance it is read from. Plan capitals at 30 ft of viewing distance per inch of letter height, the FHWA figure, and use 20 ft per inch for alarm text an operator must read fast, with line spacing 1.7 times the capital height. At a 10 ft reading distance that is 0.5 in capitals. Give active faults a colour no status row uses.

If the plant also wants a board the whole floor can watch, that is a separate read-only screen, and the choice between the two jobs is laid out in factory status display or operator HMI. The screen options for that board sit in our shop-floor status displays section, and if the board must show live values instead of a playlist, the ways to do it are in displaying a live dashboard on a signage screen.

How you know the panel works

  • Bus: about 60 Ω across the pair powered down, and error counters flat after an hour at speed.
  • Faults: unplug a sensor and the panel shows the same SPN, FMI and occurrence count as the service tool, with panel lamps matching the engine's lamps.
  • Decodes: engine speed within 1 rpm of the tachometer at 1500 rpm, coolant temperature within 2 °C of a mechanical gauge.
  • Freshness: pull the bus connector and every value greys out within 3 s with a communication banner. No number freezes on screen looking live.
  • Commands: start and stop act only in remote mode, feedback closes the loop each time, and the engine still shuts down on its own protections with the panel powered off.
  • Clearing: after a clear, fresh reads show both lists empty only when the service tool agrees.

Recommendation: Commission this panel in two stages and accept each stage against the engine service tool. Stage one is monitoring: Read_DM1, Read_DM2, the scaled PGN decodes and the freshness checks above; put it in service. Stage two is start, stop and fault clear, built only when the engine manufacturer's command document names the message, the enable and the permissives. If that document names no remote commands, leave the start circuit with the genset controller and let the panel do the watching.

Questions

How many fault codes can one DM1 message carry?

As many as the engine has active. A short list fits one CAN frame; a long list arrives as several frames under the J1939 transport protocol, which carries up to 1785 bytes in a single message. Size the receive buffer for a realistic list, twenty codes is generous, and report a truncated read as an error on the panel.

The DM1 lamp bytes report values like 'other'. What should the panel show?

Each lamp byte reports off, on, other or not supported. Show on and off plainly, hide not supported, and show other as a warning state with its own symbol, because the lamp state falls outside the normal pair. Never paint other as off or green.

Can one CAN port read two generator engines?

Only if the engines hold different source addresses, and both arrive from the factory claiming address 0, the standard engine address. Re-addressing one engine is an engine controller configuration job. The clean design is one CAN port per engine, which also keeps a wiring fault on one set from silencing the other.

How do I time stamp faults when DM1 carries no clock?

Stamp transitions yourself. When the active SPN and FMI set changes, log the first-seen and the cleared times from the controller clock and keep the log in non-volatile memory. Without a battery-backed clock the stamp resets at power-up, so record that reset as an event of its own.

Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.

Keep reading

Build your signAsk for a quote