CAPL Script

CAPL Programming Basics

Learn units and state before adding more events

CAPL resembles C in expressions and control flow, but its execution context and variable lifetime deserve separate attention. This lesson uses one keyboard-controlled state model to connect variables, functions, messages and timers. It is an independent exercise, not the door protocol from lesson 06.

Use a CANoe Simulation Setup node, CAN1 and classical CAN standard ID 0x210. There is no DBC dependency. Disable other senders of this ID and use an isolated configuration. The source below has not been compiled or executed here; resolve installed-version compiler diagnostics before running it.

Choose a type and name its unit

Type or value Meaning in this exercise Common mistake
byte Unsigned state 0 or 1 Using subtraction at zero without a guard
long Signed 32-bit arithmetic Assuming every integer type has the same range
dword Unsigned 32-bit value Ignoring wraparound in a long-running counter
message CAN1.0x210 A message object for an explicit channel and ID Treating it as the last frame received
msTimer Timer delay expressed in milliseconds Declaring timer and assuming milliseconds
timer Timer delay expressed in seconds setTimer(t, 100) means 100 seconds for this type
timeNow() Measurement time in ticks of 10 microseconds 5,000 ticks = 50 ms; 50,000 ticks = 500 ms

Declare a timer in the variables section when a separate on timer procedure needs to reference it. A timer declared only inside an on key block is not visible to that separate event procedure.

A complete state and timer exercise

Press u to request unlocked state (1), or l for locked state (0). The first command is accepted immediately. After acceptance, other commands are ignored until a 50 ms timer expires; rejected commands do not extend that interval. This is a minimum-spacing gate, not a physical switch-bounce model. Periodic state reports are requested every nominal 3 seconds.

variables
{
  msTimer reportTimer;
  msTimer debounceTimer;
  byte unlocked;
  byte debounceActive;
  message CAN1.0x210 status;
}

void reportState()
{
  status.RTR = 0;
  status.DLC = 1;
  status.byte(0) = unlocked;
  output(status);
  write("State output requested: unlocked=%d", unlocked);
}

int acceptCommand()
{
  if (debounceActive != 0) return 0;
  debounceActive = 1;
  setTimer(debounceTimer, 50);
  return 1;
}

on start
{
  cancelTimer(reportTimer);
  cancelTimer(debounceTimer);
  unlocked = 0;
  debounceActive = 0;
  reportState();
  setTimer(reportTimer, 3000);
}

on key 'u'
{
  if (acceptCommand() == 0)
  {
    write("Unlock ignored: 50 ms gate active");
    return;
  }
  unlocked = 1;
  reportState();
}

on key 'l'
{
  if (acceptCommand() == 0)
  {
    write("Lock ignored: 50 ms gate active");
    return;
  }
  unlocked = 0;
  reportState();
}

on timer debounceTimer
{
  debounceActive = 0;
}

on timer reportTimer
{
  reportState();
  setTimer(reportTimer, 3000);
}

The function acceptCommand() returns a decision; the key event performs the state change. The timer event only opens the gate again. Sharing one gate means a lock command shortly after an unlock is also rejected. If you need separate limits per command, define that requirement before changing the state variables.

setTimer schedules a one-shot expiration. Only the report event rearms itself, so reporting repeats while the gate expires once. Relative rearming does not guarantee a drift-free absolute schedule. See periodic timers for measuring the difference.

output(status) requests transmission and does not confirm reception. Check channel, RTR, DLC and data in Trace and in a separate receiving observer. The Write line deliberately describes a request.

Local scope is different from variable lifetime

CAPL local variables retain their values across calls; declaration initialization is not a per-call reset. For a fresh accumulation, assign explicitly inside the function:

long sumBytes(byte values[])
{
  long total;
  long i;
  total = 0;
  for (i = 0; i < elcount(values); i++)
  {
    total = total + values[i];
  }
  return total;
}

This standalone function fragment assumes a small array whose sum fits long. Calling it twice with the same array should return the same result. Removing total = 0 is a deliberate negative exercise: the accumulator can carry state across calls. Separate nodes do not share variables merely because those variables have the same name.

Raw signal values and physical values

With a DBC, msg.Rpm accesses the raw signal value; msg.Rpm.phys requests physical interpretation. For a hypothetical signal with factor 0.25 and offset 0, raw 8000 represents 2000 rpm. Assigning raw 2000 represents 500 rpm, not 2000 rpm. Check the actual signal's factor, offset, range, encoding and rounding; these numbers are a worked example, not a supplied database.

// Fragment: requires EngineData.Rpm with the stated DBC scaling.
message EngineData msg;
msg.Rpm.phys = 2000.0;

Setting a signal does not establish that the message was sent, and reading a cached signal does not establish freshness. Keep those checks separate.

Acceptance worksheet

Experiment Expected result What it distinguishes
Start measurement State 0; one initial report requested Explicit initialization
Issue the first u command State 1 immediately No initial 50 ms lockout
Inject l clearly inside the next 50 ms State remains 1 Shared gate blocks a second command
Issue l after gate expiration State 0 One-shot timer reopened the gate
Observe without input Nominal 3-second reports Rearmed reporting timer
Stop and restart State resets to 0 State does not leak between exercises

Human key presses are unsuitable for a precise 49/51 ms boundary test. Use a controlled event sequence and preserve timestamps; avoid drawing a timing conclusion from events exactly at the expiration boundary, where event ordering matters. These are expected results to verify, not a recorded passing run.

The unit and lifetime rules follow the authorized Vector reference for setTimer, timeNow, variable initialization and symbolic signal access. The gate design, scaling calculation and worksheet are this site's analysis. Continue with bus interaction.