CAPL CAN Messages and Periodic Timers
Start with a small, observable contract
This walkthrough sends a classical CAN data frame on CAN1, ID 0x200, with two bytes: a fixed marker 0xAA and a rolling counter. A millisecond timer requests the next transmission after 100 ms. The receiver checks the length before reading those bytes. This separates message construction, scheduling and observation so that each can be investigated independently.
Use an isolated simulation or test bench with CAN1 configured and no competing transmitter for this ID. These numbers are a teaching protocol, not a real ECU specification. No DBC is required for the numeric-ID example. The code has been checked against the available Vector reference, but has not been compiled or run in your CANoe version. Confirm the node type, channel mapping and API availability in the installed help before running it.
Send periodically and stop deliberately
Place this source in a CAPL simulation node. The first request occurs when the initial timer expires; it is not an immediate transmission at measurement start.
variables
{
msTimer heartbeatTimer;
message CAN1.0x200 heartbeat;
int running;
byte sequence;
}
on start
{
running = 1;
sequence = 0;
heartbeat.DLC = 2;
heartbeat.RTR = 0;
setTimer(heartbeatTimer, 100);
}
on timer heartbeatTimer
{
if (!running) return;
heartbeat.byte(0) = 0xAA;
heartbeat.byte(1) = sequence;
output(heartbeat);
sequence = (sequence + 1) & 0xFF;
setTimer(heartbeatTimer, 100);
}
on key 'x'
{
running = 0;
cancelTimer(heartbeatTimer);
write("Heartbeat scheduling stopped");
}
on key 's'
{
if (running) return;
running = 1;
setTimer(heartbeatTimer, 100);
}
on preStop
{
running = 0;
cancelTimer(heartbeatTimer);
}
The DLC is set before any payload access is transmitted. The marker and counter have separate byte positions. The counter wraps after 255; that wrap is intentional and is unrelated to a measurement timestamp reset.
The timer handler explicitly schedules the next expiration. A single setTimer call is not a recurring schedule. Press x to stop scheduling, and s to restart with a fresh 100 ms delay. The running guard prevents a second start request from resetting an already-running schedule. cancelTimer stops a pending expiration; it does not retract a CAN frame that was already submitted to output.
Choose timer units explicitly
| Call | Requested delay | Common mistake |
|---|---|---|
| setTimer(anMsTimer, 100) | 100 milliseconds | Treating the value as microseconds |
| setTimer(aTimer, 1) | 1 second in the two-argument timer overload | Assuming all timer arguments use milliseconds |
| cancelTimer(aTimerVariable) | Cancel a pending timer | Expecting it to cancel an already transmitted frame |
Do not infer nanosecond scheduling accuracy from a timestamp's unit. Timer scheduling, controller transmission and a receiver's observation are different points. Consult setTimer for overloads and cancelTimer for cancellation.
Inspect reception separately
Use this handler in a separate observer node. Verify what Rx means at that node's placement. In particular, a simulation-node view and a measurement-chain view need not have the same forwarding behavior.
on message CAN1.0x200
{
if (this.DIR != Rx || this.RTR != 0) return;
if (this.DLC < 2)
{
write("0x200: payload shorter than two bytes");
return;
}
write("0x200: marker=0x%02X counter=%u", this.byte(0), this.byte(1));
}
The handler does not retransmit the observed frame. Copying output(this) into a generic handler can change traffic, and its forwarding behavior depends on the setup. Keep an observation-only node free of forwarding until you have an explicit gateway requirement. If you put CAPL in a measurement evaluation branch, check whether downstream data must be forwarded; use a separate observer setup for this lesson.
Verification matrix
These are expected checks, not recorded test results:
| Action | Inspect | What it establishes |
|---|---|---|
| Start measurement | CAN1, ID 0x200, data frame, DLC 2, marker AA | Construction and channel selection |
| Capture several frames | Counter increases and wraps at 255 | Sender sequence behavior, not proof that every frame was captured |
| Press x | No newly scheduled heartbeat after any already-pending traffic clears | Stop behavior |
| Press s once | Heartbeat resumes after a new delay | Restart behavior |
| Inject DLC 1 from a separate controlled source | Observer prints the short-payload message | Length guard is exercised |
Export a filtered timestamp column and use the interval analyzer to compare the observed period against a justified tolerance. Do not claim exact 100 ms spacing just because the code calls setTimer with 100. Follow the timestamp walkthrough to choose the right clock and stream.
If the behavior differs
No frame: inspect measurement state, channel mapping and whether the timer handler runs. Wrong bytes: inspect DLC and whether another node uses 0x200. Apparently doubled frequency: separate channels and directions before analyzing. One final frame after x: distinguish an already submitted frame from a newly rearmed timer. For a bounded automated availability check, continue to test modules.
The explanations and example arrangement are site guidance; API semantics follow the authorized Vector reference. See sources and validation limits.