Arrays
Arrays are the primary way to work with ordered collections of data in TSharkRex. They are used extensively for CAN data buffers, UDS request payloads, signal lookup tables, and any scenario where you need to store multiple values of the same type. TSharkRex supports both one-dimensional and two-dimensional arrays with compile-time fixed sizes.
One-Dimensional Arrays
A one-dimensional array is declared using the ARRAY[low..high] OF type syntax inside a VAR block. The index range is inclusive on both ends.
VAR
buffer : ARRAY[0..7] OF BYTE;
flags : ARRAY[0..3] OF BOOL;
values : ARRAY[0..15] OF DINT;
END_VAR;
In this example:
bufferholds 8 bytes (indices 0 through 7) - a common size for a standard CAN frame payload.flagsholds 4 boolean values.valuesholds 16 double-integers (32-bit signed).
Array indices always start from the lower bound you specify. While 0 is the conventional starting index, the language allows any non-negative lower bound:
VAR
sensors : ARRAY[1..4] OF INT; // Indices 1, 2, 3, 4
END_VAR;
0. Stick with this convention unless you have a specific reason to do otherwise - it makes your code more readable and consistent with CAN data indexing.
Two-Dimensional Arrays
Two-dimensional arrays are declared by specifying two index ranges separated by a comma. They are particularly useful for storing multiple CAN frame payloads or UDS DID request sequences.
VAR
payload : ARRAY[0..3, 0..7] OF BYTE := [
0x03, 0x22, 0x42, 0x1B, 0x00, 0x00, 0x00, 0x00,
0x03, 0x22, 0x42, 0x24, 0x00, 0x00, 0x00, 0x00,
0x03, 0x22, 0x42, 0x52, 0x00, 0x00, 0x00, 0x00,
0x03, 0x22, 0x42, 0x22, 0x00, 0x00, 0x00, 0x00
];
END_VAR;
This declares a 4×8 array - four rows of eight bytes each. Each row represents a complete UDS DID request payload:
- Row 0: Request DID
0x421B - Row 1: Request DID
0x4224 - Row 2: Request DID
0x4252 - Row 3: Request DID
0x4222
The first byte 0x03 in each row is the UDS payload length, 0x22 is the “Read Data By Identifier” service ID, and the next two bytes form the DID number. The remaining bytes are padding.
Array Initialization
Arrays can be initialized at declaration time using square bracket syntax with comma-separated values:
VAR
data : ARRAY[0..3] OF BYTE := [0x03, 0x22, 0x45, 0x55];
END_VAR;
For larger arrays, you can spread the initializer across multiple lines for readability:
VAR
lookup : ARRAY[0..7] OF INT := [
0, 100, 200, 400,
800, 1000, 500, 250
];
END_VAR;
Two-dimensional arrays are initialized in row-major order - all elements of row 0 first, then row 1, and so on (as shown in the 2D array example above).
When initializing an array, you must provide exactly as many values as the
array has elements. The compiler will report an error if the count does not match.
For example, ARRAY[0..7] has 8 elements, so you must provide exactly 8 values.
Padding with Zeros
When only the first few bytes carry data and the rest should be zero, you still need to write out all elements explicitly:
VAR
// UDS request: only first 3 bytes matter, but all 8 must be provided
request : ARRAY[0..7] OF BYTE := [0x02, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00];
END_VAR;
If you do not provide any initializer at all (just request : ARRAY[0..7] OF BYTE;),
the array elements default to zero. Only use the := [...] syntax when you need
specific starting values.
Array Access
Individual array elements are accessed using square bracket notation with an index expression:
VAR
buffer : ARRAY[0..7] OF BYTE;
value : BYTE;
END_VAR;
// Read an element
value := buffer[3];
// Write an element
buffer[0] := 0xFF;
For two-dimensional arrays, provide both indices separated by a comma:
VAR
payload : ARRAY[0..3, 0..7] OF BYTE;
row : BYTE := 2;
col : BYTE := 1;
value : BYTE;
END_VAR;
// Read from 2D array
value := payload[row, col];
// Write to 2D array
payload[row, col] := value;
The index can be any integer expression - a literal, a variable, or a computed value:
buffer[i] := buffer[i + 1]; // Copy next element
value := payload[row * 2, offset]; // Computed indices
Common Patterns
CAN Data Buffers
The most ubiquitous use of arrays is as CAN frame data buffers. A standard CAN frame carries up to 8 bytes of payload:
VAR
SENDDATA : ARRAY[0..7] OF BYTE;
RECVDATA : ARRAY[0..7] OF BYTE;
END_VAR;
CAN-FD (Flexible Data-rate) frames can carry up to 64 bytes:
VAR
FDDATA : ARRAY[0..63] OF BYTE;
END_VAR;
DID Module Data Buffers
When using the DID_EXT function block for UDS communication, each DID module needs a data buffer to receive the response. These buffers are typically 64 bytes:
VAR
MODULE_0_DATA : ARRAY[0..63] OF BYTE;
MODULE_1_DATA : ARRAY[0..63] OF BYTE;
MODULE_2_DATA : ARRAY[0..63] OF BYTE;
END_VAR;
Filling an Array with a FOR Loop
A FOR loop is the standard way to initialize or process array elements programmatically:
VAR
buffer : ARRAY[0..7] OF BYTE;
i : BYTE;
END_VAR;
// Clear the buffer
FOR i := 0 TO 7 DO
buffer[i] := 0x00;
END_FOR;
// Fill with incrementing values
FOR i := 0 TO 7 DO
buffer[i] := i * 10;
END_FOR;
Copying Between Arrays
To copy data from one array to another, iterate over the elements:
VAR
source : ARRAY[0..7] OF BYTE;
dest : ARRAY[0..7] OF BYTE;
i : BYTE;
END_VAR;
FOR i := 0 TO 7 DO
dest[i] := source[i];
END_FOR;
Building a CAN Frame Payload
A typical pattern is to construct a CAN frame payload byte-by-byte and then send it:
VAR
CANSEND : CAN_TX;
SENDDATA : ARRAY[0..7] OF BYTE;
hastighet : INT; // Speed value to send
END_VAR;
// Build the payload
SENDDATA[0] := 0x03; // UDS length
SENDDATA[1] := 0x22; // Read By ID service
SENDDATA[2] := SHR(hastighet, 8) BAND 0xFF; // Speed high byte
SENDDATA[3] := hastighet BAND 0xFF; // Speed low byte
SENDDATA[4] := 0x00; // Padding
SENDDATA[5] := 0x00;
SENDDATA[6] := 0x00;
SENDDATA[7] := 0x00;
// Send the frame
CANSEND(ENABLE := TRUE, ID := 0x7DF, EXT := FALSE,
DATALENGTH := 8, DATA := SENDDATA);
Iterating Over 2D Payloads
When you have multiple DID requests stored in a 2D array, you can iterate over them using a row index:
VAR
payload : ARRAY[0..3, 0..7] OF BYTE := [
0x03, 0x22, 0x42, 0x1B, 0x00, 0x00, 0x00, 0x00,
0x03, 0x22, 0x42, 0x24, 0x00, 0x00, 0x00, 0x00,
0x03, 0x22, 0x42, 0x52, 0x00, 0x00, 0x00, 0x00,
0x03, 0x22, 0x42, 0x22, 0x00, 0x00, 0x00, 0x00
];
SENDDATA : ARRAY[0..7] OF BYTE;
current_row : BYTE := 0;
i : BYTE;
END_VAR;
// Copy current row to send buffer
FOR i := 0 TO 7 DO
SENDDATA[i] := payload[current_row, i];
END_FOR;
// Advance to next row (wrap around)
current_row := current_row + 1;
IF current_row > 3 THEN
current_row := 0;
END_IF;
Arrays and Pointers
Arrays work closely with pointers in TSharkRex. You can take the address of an array element using the @ operator and pass it to function blocks that expect pointer parameters. This is covered in detail in the next chapter, Pointers.
VAR
data : ARRAY[0..7] OF BYTE;
pData : POINTER TO BYTE;
END_VAR;
pData := @data[0]; // Point to first element of the array
Size Considerations
Remember that TSharkRex runs on embedded hardware with limited memory. While the compiler will reject programs that exceed available memory, it is good practice to keep arrays as small as necessary:
- Use
BYTEinstead ofINTorDINTwhen your values fit in 8 bits. - Avoid declaring large arrays that you only partially use.
- For CAN-FD buffers, only use 64-byte arrays when you actually need CAN-FD - standard CAN only uses 8 bytes.
ARRAY[0..7] OF BYTE (standard CAN payload) and ARRAY[0..63] OF BYTE (CAN-FD payload or DID response buffer). If you are unsure what size to use, start with one of these.