StartDiag Developer Guide

## 0 StartDiag Developer Guide

28th July 2025 at 8:20am

Revision History

This manual conventions

References

1 General LUA Introduction
2 General CAN Introduction
2.1 General CAN Filter
3 General UDS Introduction
4 General J1939 Introduction
5 General PID Introduction
5.1 PID Configuration
6 CAN Logger
7 StartDiag LUA API
8 LUA API Documentation
A. Your First LUA Script
B. Data and Endianness
C. PID definition examples
D. Error Codes
Copyright: TomElectronics SRL

## 0.0 Revision History

30th April 2026 at 1:28pm

## REVISIONS HISTORY

Ver 1.0.0:
- Initial Release

## 0.1 This manual conventions

15th April 2026 at 2:20pm

This document uses some conventions that are going to be respected throughout all the manual to facilitate the identification of the important information.

Specially in the LUA APIs documentation, various colors are associated to various type of information.

Whenever needed we use this notation to represent a definition of an API:

COMMAND (PARAM1, PARAM2)

Note that in the definitions, there is no information about the type of the parameters, only the name.

Each parameter has a type that must be respected or the LUA engine will throw an error. In some cases the same parameter can be of two or more types, but for each of them there will be a short description in the API documentation page.

These are the colors associated to the various types of parameters:

PARAM -number-

PARAM -string-

PARAM -table-

PARAM -boolean-

PARAM -nil-

Same applies to the values returned by the APIs, for each returned value there will be a type associated, and the types will have the same color convention as the parameters.

Often for the APIs there is also one or more example of usage, generally with a quick comment:

COMAMND (PARAM1, PARAM2) – Comment to the function

## 0.2 References

15th December 2023 at 11:38am

CAN: this is a communication protocol defined in ISO 11898-2

UDS: stands for Unified Diagnostic Service, it is a standard to transmit data for diagnostic purposes in automotive. It is defined in ISO 14229

UDSOnCAN : this a protocol designed to allow UDS messages to be transported over CAN bus in a standardized manner. It is defined in ISO 14229-3

ISO-TP: this is a transport protocol defined in ISO 15765 that defines a way to transmit messages longer than 8 bytes on the CAN bus defining how to split and reconstruct the data

LUA: scripting language embedded in StartDiag System.

## 1 General LUA Introduction

15th April 2026 at 2:32pm
LUATopic

# GENERAL INFORMATION ABOUT LUA AND CAVEAT OF USING LUA WITH STARTDIAG API

LUA is a small and light scripting language, very often embedded into more complicated systems to allow customization by the end users.

It is used in the StartDiag to allow you to implement your own CAN communication with the connected vehicle and access data.

If you are not familiar with LUA language and development, please refer to https://www.lua.org/ which is the official LUA website. There you will find all the information to get started developing your own LUA script.

The basic LUA features and commands have been preserved in StartDiag but some had to be removed to adapt the LUA environment to work in an embedded device like StartDiag.

In parallel, custom APIs have been exposed to the user that are present only in StartDiag.

## REMOVED FEATURES

Some of the LUA os methods have been removed:
- os.execute
- os.remove
- os.rename
For a complete list of the standard LUA features not supported look at LUA API Documentation.

The embedded LUA interpreter also lack the ability to handle the coroutines, so none of the relative features is available in the StartDiag System.

The full support to UTF8 classes has also been removed. StartDiag LUA interpreter handles only ASCII encoded files.

Finally, the LUA debugging features have also been removed completely from the embedded version used by StartDiag.

## ADDED API

The description and full documentation for the added API is discussed later on, but in general the added APIs allow to interface with the device itself (print strings, wait for a given amount of time) and most importantly to interact with the CAN bus.

LUA UI module
LUA CAN module
LUA UDS module
LUA J1939 module
LUA PID module

## INTORDUCTION TO NUMBERS

When we work with numbers, in LUA a whichever programming language, it is important to remember that, for a digital device to be able to process them, it is required that they fit in the memory of the device.

StartDiag is a 32bits system, as such the maximum size of data that it can handle directly is 32bits long.

Speaking about integer numbers, they are limited to be at most 32bits long:

- Unsigned Integer 32bits: in decimal [0 - 4,294,967,295] / in hex: [0 - 0xFFFFFFFF]
- Signed Integer 32bit: in decimal [-2,147,483,648 - 2,147,483,647] / in hex: [0x80000000 - 0x7FFFFFFF]

Different is the approach for real numbers, numbers that have integer and decimals values. Real numbers can have infinite values however we pick an interval in them, so the problem is how we can make an infinitely long potential number of decimal values into a fixed quantity of bits. Simply we can't. In general, in digital electronics the representation of a real number is adapted to fit into what is called a floating point number (normally called float), where the input number is represented as

- a sign bit which tells if the number is positive or negative
- an exponent which tells the order of magnitude of the number
- a mantissa which tells the values representing the number

Since the exponent is not always the same, the position of the dot in the float can vary, so the dot "floats", moves respect to the order of magnitude of the number.

The important concept here is that when we use a float value to represent a real number, there will be rounding to make it fit, and it can happen that even small numbers get a representation that is not exactly equal to the input.

## STARTDIAG LIMITATIONS ON LUA

These are the limitations that StartDiag System impose on LUA APIs:

- At most 8 files can be open at the same time

## 2 General CAN Introduction

15th April 2026 at 2:48pm
CANTopic

# GENERAL CAN BUS INFORMATION

This is not going to be a full description of the CAN bus protocol, but we are going to highlight the most important points required to use the StartDiag System.

CAN is a communication protocol which uses a physical medium consisting of a pair of conductors to transmit and receive data without the need of a clock signal. Since there is no synchronization involved in the communication, it is very important that all the devices on the common bus share the same bitrate settings. This hard requirement allows the CAN devices to identify the new messages on the bus using automatic synchronization techniques based on the bits sent. Since the CAN devices uses the bits of the stream to keep synchronization in the communication, on the single BUS a single bitrate is allowed. Having devices with different bitrate will effectively destroy the communication triggering BUS errors in all the connected devices. Moreover, a "valid" CAN bus must have at least 2 devices, configured with the same bitrate. A single device does not constitute a valid CAN bus!

On the CAN bus data is delivered using packets. Each packet contains some basic information about which service the message is supposed to be handled by (CAN identification, in short CAN ID), the length of the data contained (a number between 0 and 8 called DLC which stands for Data Length Code) and the data itself (a set of bytes).

Another feature of the CAN bus that is useful to know is the concept of priority. The devices connected to the CAN bus have a direct connection to the bus itself. This allows them to read all the packets that transit on the bus and to send their own packets when needed. Since the bus is only one, it is mandatory to avoid that more than one device can send data at the same time, since this would simply destroy the flow of bits on the bus. To solve this issue, the CAN protocol prescribes that the CAN ID acts also as priority information, which means that the low IDs have priorities over the highers. (For a deeper explanation about how this process works, please refer to the documentations available online about the CAN protocol, specifically the topic of dominant and recessive bits in CAN).

## CAN ID

The first field we are going to discuss is the CAN ID (ID for short from now on). This is a field that indicates the number of the service that the packet is for. Even if it may seems simple to consider the ID as the address of the device that will receive the packet, that is not how CAN protocol uses the IDs. They have to be considered more like the identification of the kind of information that the packet brings: whoever is interested in that kind of information will accept the CAN packet and act accordingly on it.

There are 2 different types of IDs that differs for the length (and consequently also for the allowed values):

- 11bits : this allows to have ID in the range 0x000-0x7FF
- 29bits: this allows to have ID in the range 0x00000000-0x1FFFFFFF

On the same bus, both the ID types can coexist, thanks to the priority mechanism that the CAN protocol implements.

## DLC

This field defines the number of bytes contained in the packet. Valid values are between 0 and 8.

## DATA

This is the real payload of the packet, a set of bytes (between 0 and 8) that are sent. The amount of data sent matches the DLC value for the packet.

## FILTER

This topic is explained deeper in the section CAN filter, but to give here an intuitive idea about it, we can imagine the filter as way to accept CAN packets that have certain IDs. All the packets that are not accepted are simply dropped and for the StartDiag Controller it is like they have never been received.

## CAN BAUDRATE AND ERRORS

CAN Bus is characterized by a very important parameter: baudrate.
This parameter says the speed of the data on the bus itself, how long each bit takes to be transmitted.
By design the CAN baudrate is a property of the bus, meaning that the designer of the bus decides the value of this parameter, and then all the devices that have to connect to the bus have to simply adapt to the chosen value.
As a consequence of this, CAN specifications do not provide any direct way to "identify" the baudrate used by the bus to which a device is connected to.
Moreover, by CAN specifications, CAN devices must implement two counters for errors, one for transmission errors and one for reception errors.
Each of these counters can increase (when the specific errors happen) or decrease (when the operation succeeds without errors).
The values of these counters changes the state of the CAN controllers, from the "normal" state, to "ERROR passive", and if the counters keep increasing, to "BUS Off" (in this state the controller will not send any type of data on the BUS itself).
Furthermore, as introduced before, to be valid, a CAN bus must have at least two CAN nodes connected, with the proper bus terminations and configured to share the same baudrate.
If you want to know more about the CAN error handling, please look online or read the official CAN specifications.

## CAN BAUDRATE HANDLING IN STARDIAG SYSTEM

Considering the wide range of CAN busses to which StartDiag System can be connected to, we had to tackle this issue as well. StartDiag System provides a feature of "auto-identification" for the baudrate of the connected CAN bus. This works asking you for a custom baudrate to be tested, and this is tested FIRST. If this baudarte is not found to be valid, a set of predefined baudrates are tested as well (500000, 250000, 50000, 125000). At the end of the test you will get a report with the baudrate found, if any. The most commonly used CAN baudrate on the OBD port of a vehicle is 500000 baud. NOTE: in order for the "auto-identification" of the baudrate to work properly (to verify the baudrate respect to the connected bus), StartDiag System MUST be connected to a valid CAN bus! (the bus must be properly terminated, and at least another active device must active be on the bus).

WARNING: as said above, trying an invalid baudrate could trigger errors in ALL the connected CAN devices. In a vehicle there is a high number of interconnected CAN devices, and some may be less resilient to CAN errors than others. This test may lead to DTCs (Diagnostic Trouble Codes) or disconnections in those ECUs, which may manifest as possible warning lamps turning on on the vehicle dashboard, or may just be seen using vehicle diagnostic tools (with the proper information, StartDiag System can read and clean them too, obviously).

Inside the scripts, instead, you can both get the currently set CAN baudrate on the StartDiag Monitor, or you can directly initialize the CAN bus with a specific baudrate. The latter will also validate if the baudrate provided is valid and in case return an error. If the provided baudrate is valid, CAN will be turned on and the rest of the script will have access to the CAN bus until it is uninitialized.

## 2.1 General CAN Filter

15th April 2026 at 3:04pm
CANTopic

CAN filter is a very important feature of the StartDiag Controller.

When inspecting the traffic on a CAN bus, it can be clearly seen how it is quite busy. On a CAN bus it is not uncommon to have various modules that speak to each other with messages sent and regulated only by the underlying priority rule of the CAN protocol and the work of the system designers.

In order to allow to be more selective for CAN modules, all of them implement some sort of filtering which allows to accept only some selected packets.

CAN filtering is based on the IDs, meaning that configuring CAN filters is nothing more than setting some IDs that will be accepted.

For StartDiag, CAN Filter is a very important feature but has to be understood and used correctly or the script will not behave as expected.

As said in General UDS Introduction, UDSOnCAN is a protocol to manage a request/response on top of CAN, overcoming also the limit of maximum 8 bytes per packet that CAN implements. The big picture can be seen like: - module A sends a message with ID=0x7E0 and some data - module B handles the CAN ID 0x7E0 so read the message and send the response to 0x7E8 (Note that module B will always respond to messages with ID=0x7E0 sending data to 0x7E8, this is an hardcoded connection, so whoever wants to receive responses to 0x7E0 must waits on 0x7E8!) - if module A wants to receive the response, it must be able to accept packets with ID=0x7E8 and handle the data transmission in accordance to the UDCOnCAN specifications.

The StartDiag Controller offers an API to handle the UDS request/response easily from LUA scripting, and offers APIs to access the Controller CAN filtering capability in the LUA CAN module. The configuration of the Controller filters, though, is totally dependent on the user settings, meaning that it is your responsibility to manage the CAN filter in the LUA scripts accordingly to your objective.

If in the previous example module A would have been a StartDiag with filter set for 0x7E1 instead of x07E8, the response to 0x7E8 would have not be accepted and the API would have reported back a Timeout error (the expected response packet was not received because the filter dropped it). If, on the other hand, a filter for 0x7E8 would have been set, the message would be accepted by the filter peripheral and the Controller would handle it, passing it to the UDS API and returning a result of OK with the data contained in the received response.

## CAN FILTER SETUP

The CAN Filter can be altered in various way, and some may not be immediately obvious: - LUA Script: using the exposed APIs you can setup the filter as you need to perform various CAN operations. The modification done in a LUA script will be preserved after the LUA execution is terminated, but pay attention to the cases below since those introduce automatic filter reset to perform the operations needed. - Dashboards: Since the PIDs are requested using UDS protocol, the Controller setups the filter accordingly to the defined Local IDs in the Request configurations for the dashboard to be able to receive the expected responses, overriding potential custom setups. - Retrieving Vehicle information: in order to identify the VIN and other information about the connected vehicle, the StartDiag Controller performs various requests and some of them use the UDS protocol; this implies that the CAN Filter has to be adapted to receive the responses overriding potential custom setups. - DTC Read and Clear: both these functions are performed with UDS messages, which implies that the CAN Filter is implicitly setup for reading the expected responses, overriding potential custom setups.

NOTE: The CAN Filter has important interactions with CAN Logger feature.

The CAN Logger exists to capture CAN traffic for a later inspection. It is deeply embedded in the StartDiag Controller device but it has no direct connection with the CAN BUS, in fact it is behind the CAN Filter. This means that only the CAN packets that are let through the CAN Filter can reach the CAN Logger and so be saved.

## CAN FILTER LUA SCRIPT ADVICES

Here are some points that can help when dealing with the CAN Filters setup in the LUA scripts:

- Do not assume that the CAN filter is already setup or enabled! If you need to setup some settings in the CAN filter, do it explicitly!

- If you plan to reduce the amount of packets capture by a CAN Logging started by the LUA script itself, first setup the CAN filter, then wait a bit (normally 1 seconds is enough) and after start the CAN Logging. This allows it to process the messages already on the bus. The inverse process should be done to turn off the CAN Logging (first stop the CAN Logging, then alter the CAN filter if needed), no wait is required in this case.

## 3 General UDS Introduction

15th April 2026 at 3:31pm
TopicUDS

As presented in General CAN Introduction, CAN protocol works using packets and each packet allows to send a variable amount of data between 0 and 8 bytes maximum. When performing communication with modules on the CAN bus, it is often required to send more than 8 bytes for the same message.

In order to overcome this limitation, ECU developers designed a standard that utilizes the CAN as a lower level protocol, but extends it with some rules to allows more efficient communication.

UDSOnCAN is such a protocol (for a deeper explanation please check ISO 15765-3:2004); it implements UDS using the CAN bus as transport layer.

UDS protocol sets rules to allow to send requests and have responses to it, sending up to 4095 bytes in a single UDS message.

In regards to StartDiag, the LUA module UDS implements some simple APIs that offer the user a way to send UDS requests and wait for the response accordingly to UDS protocol.

## UDS FOR STARTDIAG USERS

When we speak about UDS protocol, it is important to remember that the main point of it is the couple of words request/response. The client sends properly formatted requests to the server (ECU), which after doing something, sends a responses. This is where a question arises: how can the client know if the data on the CAN bus are the expected response? The answer is in the UDS protocol itself. Together with CAN ID, which is discussed in General CAN Introduction, UDS defines a standard to setup the requests and the responses using some additional information in the first bytes of the UDS message: * SID: Service ID * PID: Parameter ID * SUB: Sub-parameter NOTE: we are not discussing in depth the UDS protocol implementations, those are handled by the StartDiag Controller; we are just touching the information needed to program and use the StartDiag UDS feature via LUA.

Let's see the main syntax of the uds.xmit LUA API so we can make some examples and see how it can be used:

uds.xmit(RID, SID, DID, DATA, LID, TIMEOUT)

NOTE: as explained in uds.xmit, in the LUA API we use DID to define both a single PID and the couple PID+SUB, check there for a more in depth explanation.

Let's suppose that we want to ask for the SID=0x01 and PID=0x00, no SUB, to Remote ID=0x7E0, and waiting the response on Local ID=0x7E8. We would use: uds.xmit(0x7E0, 0x01, 0x00, "", 0x7E8, 50) which would send a single CAN packet ID:0x7E0-DLC:8-DATA:[ 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] (where 0x02 is the "message length" as define by UDSOnCAN protocol, following there is ServiceID, PID, no SUB is requested, so after the first zero (which is the PID) we have the "remaining" bytes for padding and DLC is set 8).

A response to this could be: ID:0x7E8-DLC:8-DATA:[ 0x06, 0x41, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0x00] (where 0x41 marks the answer for 0x01 (UDSOnCAN adds 0x40 to the requested ISD to indicate a positive response as in 0x01+0x40 = 0x41 for 0x01), the byte after 0x00 is the ECHO of the PID, no SUB is echoed (since no SUB was provided in the request) 4 bytes are the data sent back. For the length, 4 + 1+ 1 = 6 which is the length reported in the first byte, ending the payload there is the padding bytes to fill to 8).

In order for a UDS response to be accepted by the StartDiag Controller, it must have a CAN ID that passes the currently set CAN Filter, plus must respect the UDS protocol for encoding the data length, must have first UDS data byte set to ServiceID + 0x40 (or be an error: [0x7F, echo_SID ,error_code_byte]), moreover must report the echo of DID.

Ex: Request:

ID:0x7E0-DLC:8-DATA:[ 0x03, 0x01, 0xAA, 0xBB, 0x00, 0x00, 0x00, 0x00 ] (here we are declaring that we are sending both a PID and SUB (SID + PID + SUB = 3 bytes, first byte is set to 3) )

Positive Responses:

ID:0x7E8-DLC:8-DATA:[ 0x04, 0x41, 0xAA, 0xBB, 0xCC, 0x00, 0x00, 0x00 ] is considered a valid response

ID:0x7E8-DLC:8-DATA:[ 0x04, 0x11, 0xAA, 0xBB, 0xCC, 0x00, 0x00, 0x00 ] is NOT considered a valid response (SID does not match)

ID:0x7E8-DLC:8-DATA:[ 0x04, 0x41, 0xAA, 0x00, 0xCC, 0x00, 0x00, 0x00 ] is NOT considered a valid response (SUB does not match)

ID:0x7E8-DLC:8-DATA:[ 0x04, 0x41, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00 ] is NOT considered a valid response (PID and SUB do not match)

Negative Responses:

ID:0x7E8-DLC:8-DATA:[ 0x03, 0x7F, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00 ] is considered a valid response (SID does match) 0x10 is the cause of the rejection.

ID:0x7E8-DLC:8-DATA:[ 0x03, 0x7F, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00 ] is NOT considered a valid response (SID does not match)

This examples bring us to understand how important is to use the proper fields in the uds.xmit API. If the fields SID, PID, SUB are used, then they MUST match in the response, if we declare a byte of data in the request as SUB and the other modules answer without echoing it back, then the CAN packet received will not match the UDS implementation and will not be reported as response to the request, even if a CAN log may show the data as on the bus!

Ex: Response:

ID:0x7E8-DLC:8-DATA:[ 0x04, 0x41, 0xAA, 0xBB, 0xCC, 0x00, 0x00, 0x00 ]

Is accepted as response for:

uds.xmit(0x7E0, 0x01, 0xAABB, "", 0x7E8, 50) –request SID(0x01) + PID(0xAA) + SUB(0xBB)

ID:0x7E0-DLC:8-DATA:[ 0x04, 0x01, 0xAA, 0xBB, 0xFF, 0x00, 0x00, 0x00 ] (0xFF is the data)

Is accepted as response for:

uds.xmit(0x7E0, 0x01, 0xAA, "0xBB", 0x7E8, 50) –request SID(0x01) + PID(0xAA), no SUB

ID:0x7E0-DLC:8-DATA:[ 0x03, 0x01, 0xAA, 0xBB, 0x00, 0x00, 0x00, 0x00 ] (no SUB passed, 0xBB is DATA)

but NOT for:

uds.xmit(0x7E0, 0x01, 0xAADD, "0xBB", 0x7E8, 50) –request SID(0x01) + PID(0xAA) + SUB(0xDD)

ID:0x7E0-DLC:8-DATA:[ 0x04, 0x01, 0xAA, 0xDD, 0xBB, 0x00, 0x00, 0x00 ] (0xBB is the DATA and 0xDD does appear in the response for SUB)

## 4 General J1939 Introduction

15th April 2026 at 3:33pm
J1939Topic

J1939 is a communication protocol based on CAN (in fact it uses standard CAN 2.0B features to implement itss own functionalities) which is mostly used on medium and heavy vehicles.

The working principles for J1939 are very different than UDS, but the main concept stays the same: provide a standardized way to share messages between nodes and allow transmission of long messages that are bigger than the maximum payload defined for a CAN packet.

Another difference is that while UDS can be implemented using both 11 bit addresses and 29 bit addresses, J1939 works only on 29 bit addresses. This because a big part of the protocol message information are embedded in the long address.

## 5 General PID Introduction

15th April 2026 at 3:37pm
PIDTopic

When interfacing with an ECU, a common topic is the access to PIDs.
PIDs are parameters that can be read from the ECU, and this is commonly done to present the read values to the user.
PIDs are normally instantaneous values that ECU has read and can report to a client that connects to it, and do the proper requests.
StartDiag supports an automatic way to define PID requests and have the result displayed in one of the 4 Dashboards available, even though nothing prevents a user from developing a LUA Script that performs the proper requests and just log them in the script interface, or to a file on SD.
PIDs are normally accessed by UDSOnCAN protocol (see General UDS Introduction for more information on this topic) so in order to access them, two IDs are required:
- RemoteID (ID of the service that will send back the data)
- LocalID (ID that is expected to receive the response)
Together with these, also a ServiceID is required and potentially a DataID to identify the specific data from the service.
Each ECU is different, and different brands may respond on different ServiceID, RemoteID, DataID and so on (even just different firmware versions can change the parameters needed for the requests).
To delve deeper in how to setup custom PIDs request for the StartDiag, go to PID Configuration section.

## 5.1 PID Configuration

17th April 2026 at 8:02am
LUAPIDTopic

StartDiag Display allows the user to easily access four different Dashboards to show the selected parameters, each configurable both in layout and data shown.

Each Dashboard consists of a set of Gauges, and each Gauge is used to represent a single value. Gauges are of 2 types:
* Analog, which are represented by a rounded display where a needle indicates the current value displayed
* Digital, which are represented by a rectangle display where the current value is displayed in textual format

While the layout can be configured by the GUI itself, the core configuration of the values shown is done via four scripts in the "config" folder of the SD card, called Dshbrd_A, Dshbrd_B, Dshbrd_C, Dshbrd_D. Each one is used by the relative Dashboard.

The content of these scripts allows StartDiag Display to show data as requested by the user while StartDiag Controller requests the parameters that user wants to see. Because of these different sections, for each parameter (also called PID) there are two main different parts that needs to be compiled by the user in a Dashboard file: - a PID Request configuration - a PID Gauge configuration.

NOTE: respect to other API, where the name of the parameter is not relevant, in the PID configuration files the names in the tables are MANDATORY! They are deeply embedded in the configuration process and need to be written CORRECLY or they will not be identified, manifesting errors during the process of initialization of the Dashboard! In the example below, missing the RemoteID name in the definition of the pc1 object will result in a LUA error, as well as misspelling Units as units (note the lower case u)

Let's see here an example of a simple configuration file that setup a single PID for a Dashboard:
– Setup a pConf object (these objects specify how to request data to the ECU)

pc1 = pid.pidBegin( {RemoteID=0x7E0, LocalID=0x7E8, SID=0x01, DID=0x000D, Start=0, Length=1, Offset=0, Scale=1} )

– Setup a gConf object (these objects specify the graphical settings to be sued for the assigned gauge)

gc1 = { DigitVal = 0, Units = "km/h", Desc = "Speed", ValMin = 0, ValMax = 255, DecNumb = 0, ZoneAColorID = "Green", ZoneBColorID = "Yellow", ZoneCColorID = "Red", ZoneBstart = 50, ZoneCstart = 130, AlarmMin = 30, AlarmMinOn = 0, AlarmMax = 100.0, AlarmMaxOn = 0, Weight = 1}

– Connect the data request, the gauge configuration and the gauge inside the dashboard to use

pid.request({GaugeID=1, gConf=gc1, pConf=pc1})

## PID Request configuration

This section of the Dashboards configuration is relevant for the StartDiag Controller. Here you can setup the CAN / UDS parameters for the request, together with a scale factor, and offset value and even declare successive operation to be applied before the data are shown on the Display Dashboard.

Let's see an example of such a configuration:

pc1 = pid.pidBegin( {RemoteID=0x7E0, LocalID=0x7E8, SID=0x01, DID=0x000D, Start=0, Length=1, Offset=0, Scale=1} ) – Speed

pc1 is the name of the configuration (this will be used later in the "Merging the configuration" section). This name can be whatever you want, you are totally free to pick whichever name you want as long as it is a valid LUA variable name. In our examples we tend to use pcX as a reminder that it is a PID Request configuration.

NOTE: It may be useful to remember that for the order in which Offset and Scale are applied is important here. Offset is applied AFTER Scale (Offset is in the same scale as the final result). Let's make an example to clarify the point: Let's suppose we have a PID which output will be % (0-100) and is represented by a single byte (let's say it is 120d, 0x64 hex). Considering this, the Scale would be set to 120d/255d = 0.47d (47%). Now let's consider the case in which we want to add a constant value of 20% to it, so the output will be 20-120%, and for this case 47% becomes 67%. It is enough to set Offset to 20 and you will get the expected result.

StartDiag implements the concept of Composite PID. Each PID is handled as Composite PID, even if it defined a single value. For example:

pc1 = pid.pidBegin( {RemoteID=0x7E0, LocalID=0x7E8, SID=0x01, DID=0x000D, Start=0, Length=1, Offset=0, Scale=1} ) – Speed

is a Composite PID defined by only an UDS request and nothing else.

Other operations can be concatenated to an already properly defined (using pid.pidBegin function) Composite PID as in the next example:

pc1 = pid.pidBegin( {RemoteID=0x7E0, LocalID=0x7E8, SID=0x01, DID=0x000D, Start=0, Length=1, Offset=0, Scale=1} ) – Speed
pid.pidAdd(pc1, {RemoteID=0x7E0, LocalID=0x7E8, SID=0x01, DID=0x0C, Start=0, Length=2, Offset=0, Scale=0.25} ) – Read the RPM and add them to the Speed
pid.pidDiv(pc1, 100 ) – Divide (Speed + RPM) by 100

Here we have defined a Composite PID that will request to the ECU 2 values, add them and then divide the sum by 100. Clearly in this case the result does not make much sense, but for the sake of explaining the methods it is a fully correct Composite PID definition.

NOTE: for a Composite PID at most 3 operations can be concatenated, adding more will result in an error at Dashboard loading.

For a full description of the parameters, please refer to request configuration table.

## PID Gauge configuration

This section contains parameters that configure the graphical representation of the gauge in the Dashboards.

Let's see and example of such a configuration:

gc1 = { DigitVal = 0, Units = "km/h", Desc = "Speed", ValMin = 0, ValMax = 255, DecNumb = 0, ZoneAColorID = "Green", ZoneBColorID = "Yellow", ZoneCColorID = "Red", ZoneBstart = 50, ZoneCstart = 130, AlarmMin = 30, AlarmMinOn = 0, AlarmMax = 100.0, AlarmMaxOn = 0, Weight = 1}

gc1 is the name of the configuration table, this will be used later on to be bind to the Request configuration.

In order to clarify the meaning of the next set of parameters it is useful to briefly describe the main idea behind the zones that are possible in a Gauge in StartDiag. When we represent a value in a Gauge, it may be useful to have a more visual indication if the value is under or above a certain thresholds. Let's imagine for example the value of the engine coolant temperature in a vehicle, it is useful to have an indication if such a temperature is above a set threshold. To achieve that, StartDiag offers the ability to define 3 zone for each Gauge, each of them can have an assigned color (drawn as an arc in the analog representation or indicated by a colored bar under the digital numbers). The following parameters (optional) allow the user to customize where the zones start and the color to assign to them. It is also possible to setup an alarm if the values fall in one of the external zones.

For a full description of the parameters, please refer to gauge configuration table.

## Binding the configurations

After both the configurations are defined, they can be merged in a main table as follow:

pid.request( {GaugeID=1, gConf=gc1, pConf=pc1}, {GaugeID=2, gConf=gc2, pConf=pc2} )

GaugeID is the ID of the gauge in the Dashboard that will display the data received from pc2 and will be configured as defined in gc2. GaugeId must be a number between 1 and 6.

gConf is the Gauge configuration to be used for this Gauge.

pConf is the Request configuration to be used for this Gauge.

A Dashboard holds up to 6 gauges, and so up to 6 Request and Gauge configurations can be defined for a single Dashboard configuration file, even if a Dashboard layout implements less than 6 gauges, they can be defined, but only ones available in the Dashboard will be shown and used. On the other hand, if the pid.request is called with less than number of available Gauges for the Dashboard, only the declared ones will be set up while the others will be disabled.

See the section relative to LUA PID module for a more in depth explanation of the API usable in the PID definition scripts.

## 6 CAN Logger

15th April 2026 at 4:04pm
CANTopic

When working with the CAN protocol, be it simply CAN or with the overlay of the UDS via UDSOnCAN, it can be hard to track down possible communication issues.

Sometimes it is very useful to be able to capture and observe on PC the flow of the traffic on the CAN bus.

Normally, to do that, it is necessary to use a separate device that performs only that feature and logs the CAN packets directly on a PC. While this solution offers the highest performance for the capture, StartDiag takes a different approach, allowing you to have at your disposal a portable device that you can bring with you and allows you to do CAN logging without the use of a PC (at least for the phase of packets capture).

But there is another trick that StartDiag offers you. Leveraging the LUA Scripting capabilities you can start the CAN Logger feature in a script and at the same time send CAN packets or UDS messages to poke the other devices on the bus. That way you can later inspect the traffic on the bus and find the responses to the activity you have generated. This allows you to interact with the devices and discover how they react and how to handle properly those responses even without prior knowledge of the system setup.

## CAN LOGGER AND FILTERS

This is an important thing to keep in mind: the CAN Logger feature works only for the data that reaches the Controller CAN driver! This means that the only logged CAN packets are the ones sent that are on the CAN bus and match the currently set filters.

(See General CAN Filter for more information about this topic)

## CAN LOGGER PERFORMANCES

Capture and save all the traffic on a CAN bus can be very computationally expensive activity to do. The more the bus is busy, the highest the number of packets to be captured, the more time has to be spent in such an activity. This is clearly in direct competition with the other ongoing functions of the StartDiag.

It can happen that you face Notice Windows warning you about that when starting some of the StartDig feature while the CAN Logger is active. This is perfectly normal, it is just a reminder about such condition.

Moreover when inspecting a CAN capture log you may see a special indication reporting that some CAN Packets have been lost. This can happen independently of our best efforts to make the system as quick as possible.

In order to not impact uselessly on the StartDiag performances, do not leave the CAN Logger activated at the end of a LUA script unless you absolutely need it.

## CAN LOGGIN FILE PARSING

The content of the files generated by the CAN Logger feature are not directly readable because they are saved in binary format. A specific tool has been provided in the StartDiag Suite, specifically the CAN Log Parser workspace that takes as input the log file and generates an easy to read text file with the full list of the packets that have been captured.

## 7 StartDiag LUA API

15th April 2026 at 4:19pm
LUATopic

The API exposed by StartDiag to be used inside the users LUA scripts are divided into 5 main modules:

- UI
- CAN
- UDS
- J1939
- PID

Each module is responsible only for its specific field of applicability, even though inside a script those can be mixed in whichever way make sense for you to achieve the desired result.
Together with the expanded APIs added to interface the StartDiag Controller with the CAN bus, some of the standard APIs of the basic LUA environment have been removed because they were not pertinent to the use-case of the StartDiag System.

Some of the LUA os methods have been removed:
- os.execute
- os.remove
- os.rename

The embedded LUA interpreter also lack the ability to handle the coroutines, so none of the relative features is available in the StartDiag System.

The full support to UTF8 classes has also been removed, StartDiag LUA interpreter handles only ASCII chars.

Finally, the LUA debugging features have also been removed completely from the embedded version used by StartDiag.

## 7.1 LUA UI module

15th April 2026 at 4:21pm
LUAUI

This module offers access to features of the UI of StartDiag device (Display side only).
ui.setLedOrange allows to control the ORANGE led of the Display unit
ui.setLedGreen allows to control the GREEN led of the Display unit
ui.setLedRed allows to control the RED led of the Display unit
ui.startSound allows to play a tone on the Display unit
ui.stopSound allows to stop a tone playing on the Display unit
ui.beep allows to play a beep on the Display unit (similar to ui.sound but plays a predefined frequency tone)
ui.sleep allows to pause the execution of the scrip for some time
ui.clear allows to clear the current content of the script window
ui.pause allows to interrupt the script execution until the use presses the continue button
ui.inputValue allows to ask the user to input a numeric value
ui.messageBox allows to show a messageBox to the user. One or two buttons can be defined and the text of the pressed button is returned
ui.getVersion This API allows to get the numeric representation of the firmware currently installed on the StartDiag System

## 7.2 LUA CAN module

17th April 2026 at 8:26am
CANLUA

This module provides access to the raw CAN level of communication offered by the Controller unit.
It also offers a very important interface: the one with the CAN Filter peripheral, which exposes the filtering capabilities extensively used to simplify the process of reading only some specific CAN packets.
BEWARE! A bad configuration of the filter can easily drop packages that were expected to be received, so be very careful where and how you use such a feature!
can.readFilters allows to read the current configuration of the filters on the Controller
can.setFilters allows to set a filter for a given ID or range of IDs
can.disableFilters allows to disable the filtering peripheral, independently of the current filter settings (all the packets will be received)
can.enableFilters allows to enable the filtering (the currently set filter configuration will be used) (only the packets matching the current filter configuration will be received)
can.resetFilters allows to reset the settings of the filtering peripheral (no filter will be set after this command and no packet will be received)
can.recv allows to receive a CAN packet
can.send allows to send a CAN packet
can.startLog allows to start the CAN Logger feature of the Controller
can.stopLog allows to stop the CAN Logger feature of the Controller
Using the following APIs, you can initialize the CAN peripherals with a given baudrate.

BEWARE! Setting a baudrate different than the one used by the currently connected CAN bus using can.init will probably result in the generation of a lot of errors and some of the modules connected to the bus would disconnect from it. DO NOT USE SCRIPT THAT DO ACTIVE ACCESS TO THE CAN BUS WITH A MOVING VEHICLE!!!!!

can.init allows to enable the CAN peripheral with the given baudrate
can.uninit allows to disable the CAN peripheral
can.getStatus allows to obtain the CAN bus status from the StartDiag Controller
can.getCurrentBaudrate allows to get the currently set baudrate for StartDiag System, if any.
NOTE: access to the CAN bus will fail for all LUA API CAN and UDS unless a successful previous call to can.init has been done before. can.uninit will also disable the CAN peripheral on the StartDiag Controller, preventing LUA API CAN and UDS from working from there on until a successive can.init call.
In the light of what was said above, the suggested approach to keep in writing LUA scripts that need to access the CAN BUS is the following:

    local rc, currentBR = can.getCurrentBaudrate()
    if (currentBR == 0) then
    	--Ask the user here, since there is no current CAN baudrate set and validated
    	currentBR = ui.inputValue('Set CAN Baudrate', 'u', 1, 500000, 500000)
    end

    if (currentBR == nil) then
    	error('No CAN Baudrate provided.')
    end

    rc = can.init(currentBR)
    if (rc ~= 0) then
        error("CAN Init failed! Error: " .. rc .. " (Invalid BUS / Invalid CAN baudrate / Key off)")
    end

    local PID = 0x05 -- Engine coolant temperature

    f = io.open('PIDTest.log', 'w') -- Log file will be created in the root of the SD card!

    for i = 1, 5 do
        local rc, data = uds.xmit(0x7E0, 0x01, PID, "", 0x7E8, 100*3)
        local msg

        if rc == 0 then
            msg = string.format('rc: %d -- Hex: 0x%02X -- Value: %d', rc, data:byte(1), data:byte(1) - 40)
        else
            msg = string.format('rc: %d (Error)', rc)
        end

        print(msg)
        f:write(msg..'\n')

    end

    f:close()

You can also try to identify the baudrate manually in a similar way the DstartDiag System does, but this will be slower:

    brList = {
    	500000,	--500k (most frequent!)
    	250000, --250k
    	50000, -- 50k
    	125000,	-- 125k
    }

    foundBR = nil
    for _, v in pairs(brList) do
    	rc = can.init(v, true) -- do a can.init with the baudrate to test AND forceActive flag to true
    	print('can.init ' .. tostring(v) .. ' returned:' .. tostring(rc) )
    	can.uninit()

    	if rc == 0 then
    		-- Previous can.init was correct -> that is the proper baudrate for the bus!
    		-- leave now the loop
    		foundBR = v
    		break
    	else
    		-- the baudrate just tested is NOT correct...
    	end

    	-- This delay allows for an alive CAN bus to recover from the possible errors introduced by the previous test
    	-- time should be tweaked based on the bus occupancy (how frequently messages pass on it)
    	ui.sleep(100*5)
    end

    if foundBR ~= nil then
    	print( 'Found CAN bus baudrate: ' .. tostring(foundBR) )
    else
    	print( 'No valid CAN baudrate found...')
    end
    -- since we have found a valid baudrate, here we can turn the CAN peripheral right ON with that baudrate
    can.init(foundBR , true)

    --Do your can operations here
    rc = can.send(0x123, '\x00\x01\x02\x03', 100*1);
    -- good practice is to check the rc of the functions called
    print('can.send rc: ' .. tostring(rc))
    ui.sleep(100*3) --delay of 3 seconds just so simulate some operations

    -- Good practice: call explicitly the can.uninit before ending the script
    can.uninit()

Note how the use of the forceActive flag = true allows to identify bus where there is not passing traffic, but easily triggers CAN bus errors.

## 7.3 LUA UDS module

16th April 2026 at 8:42am
LUAUDS

This module offers access to the implementation of the UDS protocol offered by the StartDiag Controller unit.

uds.xmit allows to send a chunk of data that will be automatically encapsulated in the UDS protocol and to wait for a proper response by other modules on the same CAN bus.

## 7.4 LUA J1939 module

16th April 2026 at 9:47am

This module offers access to feature of the J1939 stack implemented in StartDiag device.
j1939.calculatePGN allows to calculate the PGN given the required information
j1939.testAddress verifies if the given address is already assigned to another CA in on the connected bus
j1939.requestPGN allows to request a given PGN on the connected J1939 bus
j1939.scanConnectedDevices allows to scan the bus and reports all the devices connected with a valid address claimed and their relative J1939 NAME
j1939.profileBus allows to scan the bus and reports all PGNs that the StartDiag Controller can identify in the received CAN packets IDs
j1939.sendMessage allows to send data for a given PGN to a given address
j1939.setAddressAndName allows to setup an address and a j1939 name to be used for PID readings

## 7.5 LUA PID module

17th April 2026 at 8:03am
LUAPID

This modules allows you to define the PIDs read from an ECU, the relative Gauge Configuration and bind them together to configure the StartDiag Display Dashboards.
pid.pidBegin allows to begin the definition of a Composite PID to be used in the Dashboards
pid.pidAdd allows to concatenate more data to a currently defined Composite PID by adding them to the original value
pid.pidSub allows to concatenate more data to a currently defined Composite PID by subtracting them from the original value
pid.pidMul allows to concatenate more data to a currently defined Composite PID by multiplying them by the original value
pid.pidDiv| allows to concatenate more data to a currently defined Composite PID by dividing the original value by the new one
pid.pidSetDelay allows to define a small delay to be applied after each PID in the Dashboard configurations have been read
pid.request allows to assign to each gauge, the Gauge configuration and Request configuration using a set of tables
request configuration table is NOT an API, but it is a fundamental part of the APIs that manage the Composite PIDs
gauge configuration table is NOT an API, but it is a fundamental part of the pid.request API

## 8 LUA API Documentation

17th April 2026 at 8:21am
APILUATopic

## STARTDIAG SPECIFIC APIS

ui.setLedOrange allows to control the ORANGE led of the Display unit
ui.setLedGreen allows to control the GREEN led of the Display unit
ui.setLedRed allows to control the RED led of the Display unit
ui.startSound allows to play a tone on the Display unit
ui.stopSound allows to stop a tone playing on the Display unit
ui.beep allows to play a beep on the Display unit (similar to ui.sound but plays a predefined frequency tone)
ui.sleep allows to pause the execution of the scrip for some time
ui.clear allows to clear the current content of the script window
ui.pause allows to interrupt the script execution until the use presses the continue button
ui.inputValue allows to ask the user to input a numeric value
ui.messageBox allows to show a messageBox to the user. One or two buttons can be defined and the text of the pressed button is returned
ui.getVersion allows to get the numeric representation of the firmware currently installed on the StartDiag System
can.readFilters allows to read the current configuration of the filters on the Controller
can.setFilters allows to set a filter for a given ID or range of IDs
can.disableFilters allows to disable the filtering peripheral, independently of the current filter settings (all the packets will be received)
can.enableFilters allows to enable the filtering (the currently set filter configuration will be used) (only the packets matching the current filter configuration will be received)
can.resetFilters allows to reset the settings of the filtering peripheral (no filter will be set after this command and no packet will be received)
can.recv allows to receive a CAN packet
can.send allows to send a CAN packet
can.startLog allows to start the CAN Logger feature of the Controller
can.stopLog allows to stop the CAN Logger feature of the Controller

BEWARE! Setting a baudrate different than the one used by the currently connected CAN bus using can.init will probably result in the generation of a lot of errors and some of the modules connected to the bus would disconnect from it. DO NOT USE SCRIPT THAT DO ACTIVE ACCESS TO THE CAN BUS WITH A MOVING VEHICLE!!!!!

can.init allows to enable the CAN peripheral with the given baudrate
can.uninit allows to disable the CNA preripheral
can.getStatus allows to obtain the CAN bus status from the StartDiag Controller
can.getCurrentBaudrate allows to get the currently set baudrate for StartDiag System, if any.
uds.xmit allows to send a chunk of data that will be automatically encapsulated in UDS protocol and to wait for a proper response by other modules on the same CAN bus
j1939.calculatePGN allows to calculate the PGN given the required information
j1939.testAddress verify if the given address is already assigned to another CA in on the connected bus
j1939.requestPGN allows request agiven PGN on the connected J1939 bus
j1939.scanConnectedDevices allows to scan the bus and report all the devices connected with a valid address calimed and their relative J1939 NAME
j1939.profileBus allows to scan the bus and report all PGNs that the StartDiag Controller can identify in the received CAN packets IDs
j1939.sendMessage allows to send data for a given PGN to a given address
j1939.setAddressAndName allows to setup an address and a j1939 name to be used for PID readings
pid.pidBegin allows to begin the definition of a Composite PID to be used in the Dashboards
pid.pidAdd allows to concatenate more data to a currently defined Composite PID by adding them to the original value
pid.pidSub allows to concatenate more data to a currently defined Composite PID by subtracting them from the original value
pid.pidMul allows to concatenate more data to a currently defined Composite PID by multiplying them by the original value
pid.pidDiv| allows to concatenate more data to a currently defined Composite PID by dividing the original value by the new one
pid.pidSetDelay allows to define a small delay to be applied after each PID in the Dashboard configurations have been read
pid.request allows to assign to each gauge, the Gauge configuration and Request configuration using a set of tables
request configuration table is NOT an API, but it is a fundamental part of the APIs that manage the Composite PIDs
gauge configuration table is NOT an API, but it is a fundamental part of the pid.request API

## DEACTIVATED STANDARD APIS

Following standard LUA 5.4 API are not available on StartDiag System:
LUA os methods removed:
- os.execute
- os.remove
- os.rename
The full coroutine module
The full UTF-8 module.
The full debug module.

## OTHER API LIMITATIONS:

Since StartDiag uses LUA in an embedded device, it is natural that it imposes some limitations that normally are not present on a computer. Even more, for our device these are set to preserve the content of the SD card and your own safety.

LUA API SDCARD ACCESS LIMITAIONS:

NOTE: this limitations apply only if the Paranoid Mode is active.
To know more about Paranoid Mode, look for "Paranoid Mode" on the Device Manual.
LUA PAI can freely create files and read them form the SD, but some folders are not allowed to be accessed by the:
- System: accesses are not allowed to system in order to prevent LUA from overwriting the important setup you can make to customize the device to your own needs.
- Config: accesses are not allowed to config to avoid LUA script to alter your custom defined Dashboards or corrupt backgrounds
- Docs: this folder contains a copy of the documentation we provide. LUA is not allowed to alter this in any way to preserve the original copy of such files.
- PC_Tools: this folder contains a copy of the StartDiagSuit software. LUa is not allowed to alter the content of this folder in any way to preserve the integrity of the data and executables.
If you try to open a file like "system\\hello.txt" in LUA using the scrips connected to the Menu and Paranoid Mode is enabled you will get the request to confirm access to the file. If you deny such access, an error about the file path provided not being valid.

LUA SCRIPT ENCODING:

To simplify the management of your LUA script files, to maximize the interoperability and script sharing, StartDiag requires your script to be saved with ASCII encoding (UTF-8 is NOT officially supported nor recommended).

## 8.1.01 ui.setLedOrange

16th April 2026 at 10:11am
APILUAUI

This API provides access to the control of the ORANGE LED on the StartDiag Display.

StartDiag Display has 3 LED colors: RED, GREEN and ORANGE. Each of them is considered as an individual status for the LEDs of the Display, meaning that you can turn them ON and OFF as you prefer, but only one can be ON at the same time. (Ex: ui.setLedRed(true) will turn ON the LED as RED. If after you write ui.setLedGreen(true), it will turn OFF the RED first, then turn ON the GREEN, leaving ON only that.)

DEFINITION:

ui.setLedOrange(STATUS)

PARAMETERS:

STATUS -bool-

the status requested for the LED (true to turn ON, false to turn OFF)

RETURNS:

None

EXAMPLES:

ui.setLedOrange(true) – This turn ON the ORANGE LED

ui.setLedOrange(false) – This turn OFF the ORANGE LED

## 8.1.02 ui.setLedGreen

16th April 2026 at 10:23am
APILUAUI

This API provides access to the control of the GREEN LED on the StartDiag Display.

StartDiag Display has 3 LED colors: RED, GREEN and ORANGE. Each of them is considered as an individual status for the LEDs of the Display, meaning that you can turn them ON and OFF as you prefer, but only one can be ON at the same time. (Ex: ui.setLedRed(true) will turn ON the LED as RED. If after you write ui.setLedGreen(true), it will turn OFF the RED first, then turn ON the GREEN, leaving ON only that.)

DEFINITION:

ui.setLedGreen(STATUS)

PARAMETERS:

STATUS -bool-

the status requested for the LED (true to turn ON, false to turn OFF)

RETURNS:

None

EXAMPLES:

ui.setLedGreen(true) – This turn ON the GREEN LED

ui.setLedGreen(false) – This turn OFF the GREEN LED

## 8.1.03 ui.setLedRed

16th April 2026 at 10:33am
APILUAUI

This API provides access to the control of the RED LED on the StartDiag Display.

StartDiag Display has 3 LED colors: RED, GREEN and ORANGE. Each of them is considered as an individual status for the LEDs of the Display, meaning that you can turn them ON and OFF as you prefer, but only one can be ON at the same time. (Ex: ui.setLedRed(true) will turn ON the LED as RED. If after you write ui.setLedGreen(true), it will turn OFF the RED first, then turn ON the GREEN, leaving ON only that.)

DEFINITION:

ui.setLedRed(STATUS)

PARAMETERS:

STATUS -bool-

the status requested for the LED (true to turn ON, false to turn OFF)

RETURNS:

None

EXAMPLES:

ui.setLedRed(true) – This turn ON the RED LED

ui.setLedRed(false) – This turn OFF the RED LED

## 8.1.04 ui.startSound

16th April 2026 at 10:57am
APILUAUI

This API provides access to the buzzer installed on the StartDiag Display unit and allows to play a tone on it. Differently than the ui.beep function, with this you can change the frequency of the sound produced.

NOTE: If a previous call to ui.startSound is already playing a tone, a new call to ui.startSound allows to change the played frequency. The new tone will play for the time set in the last call.

DEFINITION:

ui.startSound(FREQ, TIME)

PARAMETERS:

FREQ -number-

Frequency to be played [500-10000] Hz.

TIME -number-

How long to play the sound for (hundredths of a second) [must be >= 1]

RETURNS:

None

EXAMPLES:

ui.startSound(5000, 100) – Plays a tone at 5kHz for 100 hundredths of a second (1 second)

NOTE: it is important to remember that even if the sound is playing, the script execution continues. This means that, differently than ui.beep that plays and only when done the successive instructions are executed, for ui.sound the next instructions are executed while the sound is still playing.

## 8.1.05 ui.stopSound

16th April 2026 at 11:31am
APILUAUI

This API allows to stop a tone playing on StartDiag Display.

DEFINITION:

ui.stopSound()

PARAMETERS:

None

RETURNS:

None

EXAMPLES:

ui.stopSound() – If a tone is playing (started by a call to ui.startSound) this will stop it. Does nothing otherwise.

## 8.1.06 ui.beep

16th April 2026 at 11:35am
APILUAUI

This API allows to play a beep. This is less flexible than the ui.startSound but it is easier to handle.

DEFINITION:

ui.beep()

PARAMETERS:

None

RETURNS:

None

EXAMPLES:

ui.beep() – Plays a tone at 800 Hz for 1/4 of a second

## 8.1.07 ui.sleep

16th April 2026 at 11:57am
APILUAUI

This API allows to pause the execution of the script for a given amount of time.

DEFINITION:

ui.sleep(DELAY)

PARAMETERS:

DELAY -number-

the amount of time to sleep (in hundredths of a second)

RETURNS:

None

EXAMPLES:

ui.sleep(100) – This will pause the flow of the program for 100 hundredths of a second (1 second)

## 8.1.08 ui.clear

16th April 2026 at 11:59am
APILUAUI

This API allows to clear the content of the script screen from previous prints.

DEFINITION:

ui.clear()

PARAMETERS:

None

RETURNS:

None

EXAMPLES:

ui.clear() – Clears the Script screen

## 8.1.09 ui.pause

16th April 2026 at 12:07pm
APILUAUI

This API allows to pause the execution of the script until the user presses the Continue button.

DEFINITION:

ui.pause(SHOW)

PARAMETERS:

SHOW -bool- -optional-

boolean flag (default to true). If true, a predefined message is displayed in the text area. No message is shown if the flag is set to false.

RETURNS:

None

EXAMPLES:

ui.pause() – This will pause the flow of the program until the user press Continue. *** PAUSE *** is printed in the text area.

ui.pause(false) – This will pause the flow of the program until the user press Continue. Nothing is printed in the text area.

## 8.1.10 ui.inputValue

16th April 2026 at 1:45pm
APILUAUI

This API allows ask the user to input a NUMERIC value. It can ask for a specific format of input and returns the number from the user or nil if the user pressed Cancel.

For Integers, Unsigned Integers and Hexadecimal values:

DEFINITION:

ui.inputValue(Title, Type, Minimum, Maximum, Initial)

PARAMETERS:

Title -string-

a String that will be used as title of the numpad.

Type -string-

a String with a SINGLE CHARACTER specifying the format of the number to be requested. Can only be "i" for Integers 32 bits, "u" for Unsigned Integers 32 bits, "x" for Hexadecimal 32 bits, "f" for Float 32 bits. See the section about numerical values for more information on this topic.

Minimum -number-

minimum value accepted from the user.

Maximum -number-

maximum value accepted from the user.

Initial -number- -optional-

if specified, this number will be proposed to the user when the numpad is shown. (0 if not set)

For Floats:

DEFINITION:

ui.inputValue(Title, Type, NumOfDecimals, Minimum, Maximum, Initial)

PARAMETERS:

Title -string-

a String that will be used a title of the numpad.

Type -string-

a String with a SINGLE CHARACTER specifying the format of the number to be requested. Can only be "i" for Integers 32 bits, "u" for Unsigned Integers 32 bits, "x" for Hexadecimal 32 bits, "f" for Float 32 bits. See the section about numerical values for more information on this topic.

NumOfDecimals -number-

maximum number of decimal values the user can provide. If 0 is provided, the . button will be disabled in the numpad and only integer value will be input. Must be in the range [0-6]

Minimum -number-

minimum value accepted from the user.

Maximum -number-

maximum value accepted from the user.

Initial -number- -optional-

if specified, this number will be proposed to the user when the numpad is shown. (0 if not set)

NOTE: If you specify a Minimum and a Maximum that do not comprise the Initial value, there will be an error.

RETURNS:

Value -number-

value set by the user

or

nil

returned if the user pressed Cancel in the numpad window

EXAMPLES:

ui.inputValue("INPUT INTEGER", "i", -2147483648, 2147483647) – Integer 32 bits, no initial value proposed

ui.inputValue("INPUT INTEGER W INIT", "i", -2147483648, 2147483647, -100) – Integer 32 bits, initial value proposed = 100

ui.inputValue("INPUT UINTEGERE", "u", 0, 4294967295) – Unsigned Integer 32 bits, no initial value proposed

ui.inputValue("INPUT UINTEGERE W INIT", "u", 0, 4294967295, 100) – Unsigned Integer 32 bits, initial value proposed = 100

ui.inputValue("INPUT HEXADECIMAL", "x", 0, 4294967295) – Hexadecimal 32 bits, no initial value proposed

ui.inputValue("INPUT HEXADECIMAL W INIT", "x", 0, 4294967295, 100) – Hexadecimal 32 bits, initial value proposed = 100 (64h)

ui.inputValue("INPUT FLOAT 0 DEC", "f", 0, -10.0, 10.0) – Float 32 bits, no decimal digits, no initial value proposed

ui.inputValue("INPUT FLOAT 3 DEC", "f", 3, -10.0, 10.0) – Float 32 bits, three decimal digits, no initial value proposed

ui.inputValue("INPUT FLOAT W INIT", "f", 3, -10.0, 10.0, 1.0) – Float 32 bits, three decimal digits, initial value proposed = 1.0

## 8.1.11 ui.messageBox

16th April 2026 at 1:58pm
APILUAUI

This API allows show a messageBox to the user. One or two buttons can be defined and the text of the pressed button is returned.

DEFINITION:

ui.messageBox(Title, Message, Icon, Btn1Text, Btn2Text)

PARAMETERS:

Title -string-

a string that will be used a title of the messageBox.

Message -string-

the string with the message to be shown in the body of the messageBox

Icon -string-

a SINGLE CHARACTER string specifying the icon to be shown to user in the messageBox. Can be "n" for None, "i" for Info, "w" for Warning, "e" for Error of "q" for Question

Btn1Text -string-

Text for the first button

Btn2Text -string- -optional-

Text for the second button. If not provided the second button will be drawn disabled.

RETURNS:

Button -string-

Text of the button pressed by the user

EXAMPLES:

btn = ui.messageBox('MyTitle', 'Message Text', 'i', 'OK') – Show a MessageBox, with title "MyTitle", "Message Text" as message, the Info icon and only a button OK. btn will only be 'OK'

btn = ui.messageBox('MyTitle', 'Message Text', 'i', 'OK', 'CANC') – Show a MessageBox, with title "MyTitle", "Message Text" as message, the Info icon and an OK button and a CANC button. btn will be 'OK' or 'CANC' based on which button the user pressed

## 8.1.12 ui.getVersion

16th April 2026 at 3:07pm
APILUAUI

This API allows to get the numeric representation of the firmware currently installed on the StartDiag System.

DEFINITION:

ui.getVersion()

PARAMETERS:

None

RETURNS:

Version -number-

Numeric version of the installed firmware

EXAMPLES:

v = ui.getVersion()

## 8.2.01 can.readFilters

16th April 2026 at 3:10pm
APICANLUA

This API allows to read form the Controller the current configuration of the CAN acceptance filter.

NOTE: access to CAN peripheral will fail for all LUA API CAN and UDS unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully called.

DEFINITION:

can.readFilters()

PARAMETERS:

None

RETURNS:

On success:

RC -number-

0 = OK

SETTINGS -table-

is a table with the current filter settings on the Controller

On error:

RC -number-

is the Return Code of the function

nil

If the filters settings are read correctly the returned table is defined as:

SETTINGS -table-

{

Status -string-

"On" if the filters are enabled, "Off" if the filters are disable and everything is dropped, "Bypass" if everything is accepted

Single_11 -number-

is the value of single address filter for 11bit ID

Start_11 -number-

is the start address of range filter for 11bit ID

End_11 -number-

is the end address of range filter for 11bit ID

Single_29 -number-

is the value of single address filter for 29bit ID

Start_29 -number-

is the start address of range filter for 29bit ID

End_29 -number-

is the end address of range filter for 29bit ID

}

EXAMPLES:

rc, settings = can.readFilters()

This is an example that shows also how to access the received result (for sake of brevity all the error handling code has been omitted):

    	rc, filters = can.readFilters()
    	if (rc == 0) then
    		-- Filters read successfully :)
    		print('CAN readFilter: SUCCESS!')

    		local txt = string.format('Status:%s, Single_11:%08X, Start_11:%08X, End_11:%08X, Single_29:%08X, Start_29:%08X, End_29:%08X',
    			filters.Status, filters.Single_11, filters.Start_11, filters.End_11, filters.Single_29, filters.Start_29, filters.End_29)
    		print( txt )
    	else
    		-- Error reading filters :(
    		print('Error reading filters configuration RESET')
    	end

## 8.2.02 can.setFilters

16th April 2026 at 2:21pm
APICANLUA

This API allows to setup single values or range values for the the CAN acceptance filter.

NOTE: access to CAN peripheral will fail for all LUA API CAN and UDS unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully called.

This API handles both 11bit and 29bit long addresses, but they can not be mixed in the same call!

DEFINITION:

can.setFilters(VAL1, VAL2)

PARAMETERS:

VAL1 -number-

is the Start value for the addresses range to accept, or the Single value set if only this is provided.

VAL2 -number- -optional-

is the End value for the addresses range to accept

NOTE: if Start and End are equal then a single address filter is set automatically.

RETURNS:

RC -number-

is the Return Code of the function (0 = Success, Error Code otherwise)

NOTE:

When this function is called, after changing the filter configuration it also empties the buffer of the received CAN messages.

EXAMPLES:

can.setFilters(0x7E8) – Setup a Single address filter for the 11bit address 0x7E8 (Only the ID 0x7E8 will be accepted)

can.setFilters(0x7E8, 0x7E8) – Setup a Single address filter for the 11bit address 0x7E8 (Only the ID 0x7E8 will be accepted)

can.setFilters(0x7E0, 0x7E8) – Setup a Address range filter for the 11bit addresses between 0x7E0 and 0x7E8 (0x7E0 and 0x7E8 are part of the accepted range)

can.setFilters(0x18DA1033) – Setup a Single address filter for the 29bit address 0x18DA1033 (Only the ID 0x18DA1033 will be accepted)

can.setFilters(0x18DA1033,0x18DA1033) – Setup a Single address filter for the 29bit address 0x18DA1033 (Only the ID 0x18DA1033 will be accepted)

can.setFilters(0x18DA1033, 0x18DA1042) – Setup a Address range filter for the 11bit addresses between 0x18DA1033 and 0x18DA1042 (0x18DA1033 and 0x18DA1042 are part of the accepted range)

## 8.2.03 can.disableFilters

16th April 2026 at 2:25pm
APICANLUA

This API allows to disable the CAN acceptance filter. The filter is kept active but set in bypass mode, meaning that all the received CAN packets are accepted.

NOTE: access to CAN peripheral will fail for all LUA API CAN and UDS unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully call.

DEFINITION:

can.disableFilters()

PARAMETERS:

None

RETURNS:

RC -number-

is the Return Code of the function (0 = Success, Error Code otherwise)

NOTE:

When this function is called, after changing the filter configuration it also empties the buffer of the received CAN messages.

EXAMPLES:

can.disableFilters() – This sets the CAN acceptance filter in bypass mode

## 8.2.04 can.enableFilters

16th April 2026 at 2:27pm
APICANLUA

This API allows to enable the CAN acceptance filter. When the filtering feature is enabled only the CAN packets matching the currently setup of the filter are received.

NOTE: access to CAN peripheral will fail for all LUA API CAN and UDS unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully called.

DEFINITION:

can.enableFilters()

PARAMETERS:

None

RETURNS:

RC -number-

is the Return Code of the function (0 = Success, Error Code otherwise)

NOTE:

When this function is called, after changing the filter configuration it also empties the buffer of the received CAN messages.

EXAMPLES:

can.enableFilters() – Enable the CAN filter peripheral

## 8.2.05 can.resetFilters

16th April 2026 at 2:31pm
APICANLUA

This API allows to bring the CAN acceptance filter to the default configuration, independently of the values currently set. The filter is turned off and no packet will be received after the call to this API. The Filter values are deleted and if enableFilters is called in this configuration all the incoming packets will be blocked.

NOTE: access to CAN peripheral will fail for all LUA API CAN and UDS unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully called.

DEFINITION:

can.resetFilters()

PARAMETERS:

None

RETURNS:

RC -number-

is the Return Code of the function (0 = Success, Error Code otherwise)

NOTE:

When this function is called, after changing the filter configuration it also empties the buffer of the received CAN messages.

EXAMPLES:

can.resetFilters() – Resets filter values

## 8.2.06 can.recv

16th April 2026 at 2:37pm
APICANLUA

This API allows to receive a CAN packet with the given ID, waiting at most the given TIMEOUT.

NOTE: access to CAN peripheral will fail for all LUA API CAN and UDS unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully call.

DEFINITION:

can.recv(ID, TIMEOUT)

PARAMETERS:

ID -number-

is the ID of the message we want to read back

TIMEOUT -number-

is maximum time to wait for a message with the given ID to arrive (in hundredth of a second)

ClearRecvBuf -bool- -optional-

when this flag is present and set to true, before start waiting for the requested CAN message, the buffer of the received message will be cleared.

RETURNS:

On success:

RC -number-

0 = OK

DATA -string-

is a string containing the content of the received packet (in HEX format string)

On error:

RC -number-

is the Return Code of the function

nil

EXAMPLES:

rc, data = can.recv(0x7E8, 50) – This waits for a message with ID 0x7E8 for at most half a second

## 8.2.07 can.send

16th April 2026 at 2:42pm
APICANLUA

This API allows to send a raw CAN packet.

NOTE: access to CAN peripheral will fail for all LUA API CAN and UDS unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully call.

DEFINITION:

can.send(ID, DATA, TIMEOUT)

PARAMETERS:

ID -number-

is the ID of the message we want to send

DATA -string-

is a string representing the data to be sent in the packet. Must be a HEX string with at most 8 values

TIMEOUT -number-

is maximum time to wait for a message to be sent form StartDiag Controller (in hundredth of a second)

PADDING -bool- -optional-

when this flag is present and set to false, force the CAN packet to have no padding, so that if 3 bytes are given in the DATA field, DLC is set to 3 and only 3 bytes are delivered on the CAN bus. This is set to true by default if not passed.

ClearRecvBuf -bool- -optional-

when this flag is present and set to true, after sending the requested CAN message, the buffer of the received message will be cleared.

RETURNS:

RC -number-

is the Return Code of the function (0 = Success, Error Code otherwise)

EXAMPLES:

can.send(0x7E0, '\x01\x00', 50) – This is a packet with ID = 0x7E0 containing 0x01 0x00 and waits at most half a second for the send to happen. Padding to 8 bytes is applied.

can.send(0x7E0, '\x01\x00', 50, false) – This is a packet with ID = 0x7E0 containing 0x01 0x00 and waits at most half a second for the send to happen. No padding applied (only 0x01 0x00 will be sent) and DLC will be set to 2.

## 8.2.08 can.startLog

16th April 2026 at 3:04pm
APICANLUA

This API allows a script to start a CAN Logger session that goes on in parallel with the rest of the instructions of the script.

This is very useful to poke around when not much information is available about the other devices on the CAN bus.

NOTE: the status of the filter has a big impact on the capture and can even be changed during an ongoing capture.

NOTE: if a CAN Logger is already ongoing, the previous one will be stopped automatically before starting the new one and a notice windows will warn you about this situation.

NOTE: access to CAN peripheral will fail for all LUA API CAN and UDS unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully called.

DEFINITION:

can.startLog(FILE)

PARAMETERS:

FILE -string-

is a string with the name of the file where data will be saved to. This must be in 8.3 format and if the file already exists, it will be overwritten

RETURNS:

RC -number-

is the Return Code of the function (0 = Success, Error Code otherwise)

EXAMPLES:

rc = can.startLog('Logfile.log') – Starts the capture on the CAN peripheral

For more information about the CAN Logger feature, see CAN Logger.

## 8.2.09 can.stopLog

16th April 2026 at 2:47pm
APICANLUA

This API allows a script to stop a CAN Logger session that is ongoing.

DEFINITION:

can.stopLog()

PARAMETERS:

None

RETURNS:

RC -number-

is the Return Code of the function (0 = Success, Error Code otherwise)

EXAMPLES:

rc = can.stopLog() – Stops an ongoing CAN Logger capture. Does nothing if there is no ongoing capture

For more information about the CAN Logger feature, see CAN Logger

## 8.2.10 can.init

16th April 2026 at 2:54pm
APICANLUA

This API allows to enable the CAN peripheral with the given baudrate.

With this call, Controller will verify if the CAN baudrate passed is compatible with the connected CAN bus, and if everything is fine it will return success.
In order for this test to be done, it is necessary that the CAN bus is correctly working:
- the proper terminations must be present
- there is at least another device alive on the bus
- all the devices on the bus (including the StartDiag Controller) have the same baudrate and it must match the one requested

NOTE: after enabling the CAN peripheral, this function also clears the current filter configuration (same as resetFilter and enable the reception for all CAN packets setting the bypass mode (same as disableFilter)!

BEWARE! Setting a baudrate different than the one used by the currently connected CAN bus using can.init with the forceActive flag enabled will probably result in the generation of errors and some of the modules connected to the bus could disconnect from it. DO NOT USE SCRIPT THAT DO ACTIVE ACCESS TO THE CAN BUS WITH A MOVING VEHICLE!!!!!

DEFINITION:

can.init(baudrate, forceActive)

PARAMETERS:

baudrate -number-

Requested CAN baudrate (in bit per second).

forceActive -bool- -optional-

If specified as True, this will force the Controller to connect to the CAN bus with the given baudrate using the ACTIVE baudrate detection. If the specified baudrate is not compatible with the CAN bus, this will introduce error in the other connected devices, potentially disconnecting them. Use with extreme caution!

RETURNS:

RC -number-

is the Return Code of the function (0 = Success, Error Code otherwise)

There are two special errors that can.init can return and deserve and explanation here:

11800 = returned when the can.init fails because the specified baudrate is not compatible with the connected bus or if the forceActive flag is not set and the bus have no passing data.

11802 = returned by the can.init is called after a previous successful call to can.init itself and no can.uninit has been done. Simply put it means "you can't call can.init when the can peripheral is already on"

NOTE: access to CAN bus will fail for all LUA API (CAN and UDS) unless a previous call to can.init has been done before with the correct CAN baudrate for the bus and the bus is a properly configured, terminated and alive (at least another device is connected to the bus). can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive proper can.init calls to turn on the CAN peripheral.

NOTE: can.init can not be called in boot.sds sctipt file!

EXAMPLES:

rc = can.init(500000) –Set the custom baudrate to 500000 and try to connect to the CAN bus. rc will tell if the operation succeeded. This will successfully connect to a CAN bus only if there is ongoing traffic at 500kbaud.

rc = can.init(500000, true) –Set the custom baudrate to 500000 and try to connect to the CAN bus. rc will tell if the operation succeeded. This will successfully connect to a CAN bus if there is at least another device and all the devices connected. No need for traffic to pass on the bus.

## 8.2.11 can.uninit

16th April 2026 at 2:57pm
APICANLUA

This API allows to disable the CAN peripheral.

DEFINITION:

can.uninit()

PARAMETERS:

None

RETURNS:

RC -number-

is the Return Code of the function (0 = Success, Error Code otherwise)

NOTE: access to CAN bus will fail for all LUA API (CAN and UDS) unless a previous call to can.init has been done before with the correct CAN baudrate for the bus and the bus is a properly configured, terminated and alive (at least another device is connected to the bus). can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive proper can.init calls to turn on the CAN peripheral.

EXAMPLES:

can.uninit() – Turns off CAN

## 8.2.12 can.getStatus

16th April 2026 at 3:10pm
APICANLUA

This API allows obtain the CAN bus status from the StartDiag Controller.

DEFINITION:

ui.getStatus()

PARAMETERS:

None

RETURNS:

STATUS -table-

is a table with the current filter settings on the Controller

STATUS -table-

{

Speed -number-

last validated baudrate used. If the connected bus did not change, this is the baudrate of the bus. This value is reset to 0 by any call to can.init or can.uninit

Active -bool-

true if the StartDiag Controller CAN periphery is connected to a CA bus, false otherwise.

a11bits -bool-

true if when we connected to the CAN bus there were 11bits address packets ongoing

a29bits -bool-

true if when we connected to the CAN bus there were 29bits address packets ongoing

EXAMPLES:

rc, cfg = can.getStatus()

Here is a full exmaple of possible use:

    rc, cfg = can.getStatus()
    print('can.getStatusrc: ' ..tostring(rc))
    if (cfg ~= nil) then
    	print( string.format('can.getStatus() RC:%d canCfg={Speed=%d, Active=%s, a11bits:%s, a29bits:%s}', rc, cfg.Speed, tostring(cfg.Active), tostring(cfg.a11bits), tostring(cfg.a29bits)) )
    end

## 8.2.13 can.getCurrentBaudrate

17th April 2026 at 8:21am
APICANLUA

This API allows to obtain the CAN baudrate set and verified from StartDiag System. If none is set or verified, 0 is returned instead.

DEFINITION:

ui.getCurrentBaudrate()

PARAMETERS:

None

RETURNS:

RC -number-

is the Return Code of the function (0 = Success, Error Code otherwise)

CustomBaudrate -number-

Custom baudrate currently set. 0 if none or not verified.

EXAMPLES:

rc, custombr = can.getCurrentBaudrate()

Here is a real exmaple of possible use:

    local rc, currentBR = can.getCurrentBaudrate()
    if (currentBR == 0) then
    	--Ask the user here, since there is no current CAN baudrate set and validated
    	currentBR = ui.inputValue('Set CAN Baudrate', 'u', 1, 500000, 500000)
    end

    if (currentBR == nil) then
    	error('No CAN Baudrate provided.')
    end

    -- form here on currentBR can be used...

## 8.3.1 uds.xmit

16th April 2026 at 3:32pm
APILUAUDS

This API allows to send a UDS message and wait the given amount of time for a response (must be a full UDS compatible transaction).

Respect to the normal CAN packet, the requirements for this API are more stringent.

NOTE: See [General UDS Introduction|3 General UDS Introduction]] for more information about the SID and DID peculiarities in the UDS protocol as implemented in StartDiag.

NOTE: access to CAN peripheral will fail for all LUA API CAN and UDS unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully called.

DEFINITION:

usd.xmit(RID, SID, DID, DATA, LID, TIMEOUT, PADDING)

PARAMETERS:

RID -number-

(RemoteID) ID of the message we are sending

SID -number-

service ID that we are communicating with

DID -number-

or

nil

DataID of the data we are requesting. Must be in the range 0x0000-0xFFFF

DATA -string-

string representing the data to be sent in the packet. Must be a string of HEX values. Can be an empty string if no data have to be sent

LID -number-

(LocalID) ID of the response messages we are waiting for

TIMEOUT -number-

maximum time to wait for a message with the given ID to arrive (in hundredth of a second)

PADDING -bool- -optional-

if defined and set to false, no padding will be applied to the packets sent, padding to 8 bytes will be applied otherwise

NOTE: an important note must be done in regards to DID parameter. The UDS protocol generally use two possible bytes to identify the data for a UDS request: Parameter ID (PID), and Sub-Parameter (SUB). To simplify the syntax of this API, if the value of DID is in the range [0x00-0xFF] then it will used as PID and SUB will be nil (no SUB will be used). If the value is greater than 0xFF, then it will be considered a 2-bytes value where the highest will be considered the PID, and the lowest will be considered as SUB. Ex: DID=[0x0A] will bring to PID=0x0A, SUB=nil Ex: DID=[0x000A] will bring to PID=0x0A, SUB=nil Ex: DID=[0x0A0B] will bring to PID=0x0A, SUB=0x0B

RETURNS:

On success:

RC -number-

0 = OK

DATA -string-

string containing the content of the received message (in HEX format)

On error:

RC -number-

Return Code of the function (0 = success)

nil

EXAMPLES:

rc, data = uds.xmit(0x7E0, 0x01, 0x0203, "", 0x7E8 , 50) – this will send a message to 0x7E0, with SID=0x01 , PID = 0x02, SUB = 0x03, padding to 8 bytes total, and waits at most half a second for a response with ID 0x7E8.

rc, data = uds.xmit(0x7E0, 0x01, 0x0000, "", 0x7E8 , 50) – this will send a message to 0x7E0, with SID=0x01 , PID = 0x00, no SUB, padding to 8 bytes total, and waits at most half a second for a response with ID 0x7E8.

rc, data = uds.xmit(0x7E0, 0x01, nil, "", 0x7E8 , 50) – this will send a message to 0x7E0, with SID=0x01 , no PID, no SUB, padding to 8 bytes total, and waits at most half a second for a response with ID 0x7E8.

rc, data = uds.xmit(0x7E0, 0x01, 0x0203, "", 0x7E8 , 50, false) – this will send a message to 0x7E0, with SID=0x01 , PID = 0x02, SUB = 0x03, no padding , and waits at most half a second for a response with ID 0x7E8.

## 8.4.1 j1939.calculatePGN

16th April 2026 at 3:42pm
APIJ1939LUA

This API is just a helper function to allow you to get a calculated 29 bits CAN ID given some basic parameters:
- priority
- source address
- destination address
- pgn

DEFINITION:

j1939.calculatePGN(PRIO, SRC_ADDR, DST_ADDR, PGN)

PARAMETERS:

PRIO -number-

value for the priotity field (must be in the range [0-7]) (lower means higher priority)

SRC_ADDR -number-

source address (must be in the range [0-254])

DST_ADDR -number-

destination address (must be in the range [0-255], but not 254)

PGN -number-

required PGN

RETURNS:

RC-number-

Return code for the operation (0 = OK)

CAN_ID-number-

29 bit address to be used for the given parameters

EXAMPLES:

rc, id = j1939.calculatePGN(6, 0x37, 0x42, 0xE000) – Return rc = 0, id = 0x18E04237

rc, id = j1939.calculatePGN(6, 0x37, 0x42, 0xFEEC) – Return rc = 0, id = 0x18FEEC37

NOTE: This function automatically handles the different type of PDUs as per J1939 protocol.

## 8.4.2 j1939.testAddress

16th April 2026 at 3:49pm
APIJ1939LUA

This API verifies if the given address is already assigned to another CA in on the connected bus.

NOTE: access to CAN peripheral will fail for all LUA API CAN unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully called.

This API performs "loosely" the requirements for the J1939 claim address procedure, but doesn't adhere to it completely. Specifically, it tries to see if the given address is already in use. If it is free it returns True. If it is already in use, it returns False. (No full NAME comparison is done)

This is a design choice to minimize the impact of the connection of StartDiag System to a J1939 bus.

After the call is done, no address is reserved by the Controller on the J1939 bus, so if another device that uses the requested address is connected to the bus, it simply will get it without any notification.

NOTE: It is meant to be used only in the LUA scripting.

DEFINITION:

j1939.testAddress(ADDR, NAME)

PARAMETERS:

DST_ADDR -number-

address (must be in the range [0-253])

NAME -string- -optional-

is a string representing the J1939 name to be used in the packet. Must be a HEX string with 8 bytes

RETURNS:

RC-number-

Return code for the operation (0 = OK and the address is free to use) (3 = address already in use) (Error otherwise)

EXAMPLE

Bus has two nodes with claimed address 0x42 and 0x39 respectively.

claimed = j1939.testAddress(0x42) – returns false (address already claimed)

claimed = j1939.testAddress(0x52) – returns true (address available)

## 8.4.3 j1939.requestPGN

16th April 2026 at 3:55pm
APIJ1939LUA

This API allows to request a given PGN on the J1939 bus and returns the response.

NOTE: access to CAN peripheral will fail for all LUA API CAN unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully called.

DEFINITION:

j1939.requestPGN(SRC_ADDR, DST_ADDR, PGN, TIMEOUT)

PARAMETERS:

SRC_ADDR -number-

source address (must be in the range [0-254])

DST_ADDR -number-

destination address (must be in the range [0-255], but not 254)

PGN -number-

request PGN

TIMEOUT -number-

the maximum time to wait for a response to arrive (in hundredth of a second)

RETURNS:

RC-number-

Return code for the operation (0 = OK)

CAN_ID-number-

29 bit address to be used for the given parameters

EXAMPLES:

rc, data = j1939.requestPGN(0x39, 0x42, 0xFEEC, 100*10)

NOTE: This function automatically handles the different type of PDUs as per J1939 protocol.

## 8.4.4 j1939.scanConnectedDevices

16th April 2026 at 4:02pm
APIJ1939LUA

This API allows scan the connected J1939 bus, identifying all the devices that have a claimed address and report back the list with the associated J1939 NAME.

NOTE: access to CAN peripheral will fail for all LUA API CAN unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully called.

DEFINITION:

j1939.scanConnectedDevices()

PARAMETERS:

None

RETURNS:

On Success:

RC-number-

Return code for the operation (0 = OK)

DEVICES -table-

29 bit address to be used for the given parameters

{

ENTRY -table-

Device0

ENTRY -table-

Device1

And so on...

}

DISCOVERED DEVICES -number-

Number of device discovered. Each had one ENTRY in the DEVICES table

Each ENTRY table has the following definition:

ENTRY -table-

{

</div>

ID -number-

29bit CAN Id of the response from this device

LEN -number-

DLC of the response message (fixed 8 by J1939 protocol)

DATA -string-

CAN packet payload. This is the J1939 NAME of the device

}

On Error :

RC-number-

Error code

nil

29 bit address to be used for the given parameters

DISCOVERED DEVICES -number-

0

EXAMPLES:

rc, data, size = j1939.scanConnectedDevices()

NOTE: It is possible that there is no device on the bus with claimed address. In such a case RC will be 0, DEVICES will be a not nil table, but will have no ENTRY inside, and DISCOVERED DEVICES will be 0.

## 8.4.5 j1939.profileBus

16th April 2026 at 4:07pm
APIJ1939LUA

This API allows scan the connected J1939 bus, logging all the received PGN in the CAN IDs received.

NOTE: access to CAN peripheral will fail for all LUA API CAN unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully called.

DEFINITION:

j1939.profileBus(DURATION)

PARAMETERS:

DURATION-number-

how long to wait for incoming PGNs (in hundredth of a second)

RETURNS:

On Success:

RC-number-

Return code for the operation (0 = OK)

PGNS-table-

29 bit address to be used for the given parameters

{

ENTRY -table-

PGN0

ENTRY -table-

PGN1

And so on...

}

DISCOVERED PGNS-number-

Number of received PGNs. Each had one ENTRY in the PGNS table

Each ENTRY table has the following definition:

ENTRY -table-

{

</div>

PGN -number-

PGN received

CNT -number-

Number of times this PGN has been received in the given scan period

}

On Error :

RC-number-

Error code

nil

29 bit address to be used for the given parameters

DISCOVERED PGNS -number-

0

EXAMPLES:

rc, data, size = j1939.profileBus(100 * 30) – wait for packets on the bus for 30 seconds

NOTE: It is possible that there is not PGNs passing on the bus . In such a case RC will be 0, PGNS will be a not nil table, but will have no ENTRY inside, and DISCOVERED PGNS will be 0.

## 8.4.6 j3919.sendMessage

16th April 2026 at 4:12pm
APIJ1939LUA

This API allows to send data for a given PGN to a given address

NOTE: access to CAN peripheral will fail for all LUA API CAN unless a previous call to can.init has been done successfully before. can.uninit will disable the CAN peripheral on the StartDiag Controller, preventing any LUA API CAN and UDS from working from there on until a successive can.init is successfully called.

DEFINITION:

j1939.sendMessage(SRC_ADDR, DST_ADDR, PGN, PRIORITY, DATA)

PARAMETERS:

SRC_ADDR -number-

source address (must be in the range [0-254])

DST_ADDR -number-

destination address (must be in the range [0-255], but not 254)

PGN -number-

request PGN

PRIORITY-number-

priority to be used. Valid values are [0-7]. The lower is the values, the higher is the priority.

DATA -string-

a string representing the data to be sent. At most 1785 (255*7) bytes can be sent.

RETURNS:

RC-number-

Return code for the operation (0 = OK)

EXAMPLES:

rc = j1939.sendMessage(0x69, 0x42 ,0x1200 , 6 , '\x01\x02\x03\x04')

NOTE: This function automatically handles the different type of PDUs as per J1939 protocol, together with using TP protocol for messages bigger than 8 bytes.

## 8.4.7 j1939.setAddressAndName

16th April 2026 at 4:15pm
APIJ1939LUA

This API allows to setup an address and a j1939 name to be used for PID readings.

This is mandatory to be added to the PID configuration script when the J1939 PIDs are required.

It must be present before calling pid.request.

NOTE: It can be present more than once, but only the last will take effect.

NOTE: Differently than j1939.testAddress, this API does not check at the moment of the execution if the address is available. This will setup the relevant data on the Controller so that, when the J1939 PIDs are requested, those operation will be performed claiming the declared address with the set name.

DEFINITION:

j1939.setAddressAndName(ADDR, NAME)

PARAMETERS:

DST_ADDR -number-

address (must be in the range [0-253])

NAME -string-

a string representing the J1939 name to be used in the packet. Must be a HEX string with 8 bytes

RETURNS:

RC-number-

Return code for the operation (0 = OK)

EXAMPLE:

rc = j1939.setAddressAndName(0x42, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF")

## 8.5.1 pid.pidBegin

17th April 2026 at 7:58am
APILUAPID

This API initializes and returns a PID Request configuration that can later be used in a Dashboard gauge.

DEFINITION:

pid.pidBegin(REQUEST)

PARAMETERS:

This API accepts a single parameter that can be of various types:

REQUEST -table-

is a table that MUST respect the definition of request configuration table

REQUEST -table-

{

SensorID -string-

is used to access parameters exposed by StartDiag Controller or StartDiag EGT. MUST be a table with a single field named SensorID and must have one of "Temp", "Volt", "EGTI", "EGTE" as value

}

RETURNS:

CompPID -table-

is a table representing the current Composite PID configuration

EXAMPLES:

pc1 = pid.pidBegin( {SensorID="Temp"} ) – Set the first value in the PID Request configuration as SatrtDiag Controller internal temperature

pc1 = pid.pidBegin( {RemoteID=0x7E0, LocalID=0x7E8, SID=0x01, DID=0x0C, Start=0, Length=2, Offset=0, Scale=0.25} ) – Set the first value in the PID Request configuration to the data read by the given request configuration table

NOTE: this is the description of accepted StartDiag SensorID values:

- "Temp" : Temperature of the StartDiag Controller
- "Volt" : Voltage read by the StartDiag controller
- "EGTI" : Internal temperature of the StartDiag EGT unit (this is measured inside the resin-filled unit)
- "EGTE" : External temperature of the StartDiag EGT unit (This is measured at the tip of the EGT thermocouple sensor)

## 8.5.2 pidAdd

17th April 2026 at 7:59am
APILUAPID

This API concatenates a PID Request configuration already defined with pid.pidBegin with another source using an addition operation.

DEFINITION:

pid.pidAdd(CompPID, CONCATENATE)

PARAMETERS:

CompPID -table-

is the Request configuration to expand. MUST be an object returned by pid.pidBegin

CONCATENATE can be one of the following 3 types:

CONCATENATE -table-

is a table that MUST respect the definition of request configuration table

CONCATENATE -table-

{

SensorID -string-

is used to access a parameter exposed by StartDiag Controller or StartDiag EGT. MUST be a table with a single field named SensorID and must have one of "Temp", "Volt", "EGTI", "EGTE" as value

}

CONCATENATE -number-

is a constant value

RETURNS:

None (The modifications are done to CompPID)

EXAMPLES:

pid.pidAdd(pc1, {RemoteID=0x7E0, LocalID=0x7E8, SID=0x01, DID=0x0C, Start=0, Length=2, Offset=0, Scale=0.25}) – Add to the definition of pc1 a new PID request defined by the given request configuration table and perform the addition of the new read value to pc1

pid.pidAdd(pc1, {SendorID='Temp'}) – Add to the definition of pc1 the current value of the StartDiag Controller internal temperature and perform the addition with the Controller internal temperature

pid.pidAdd(pc1, 42) – Add to the definition of pc1 the constant value 42 and perform the addition with such a constant .

NOTE: this is the description of accepted StartDiag SensorID values:

- "Temp" : Temperature of the StartDiag Controller
- "Volt" : Voltage read by the StartDiag controller
- "EGTI" : Internal temperature of the StartDiag EGT unit (this is measured inside the resin-filled unit)
- "EGTE" : External temperature of the StartDiag EGT unit (This is measured at the tip of the EGT thermocouple sensor)

## 8.5.3 pidSub

17th April 2026 at 7:59am
APILUAPID

This API concatenates a PID Request configuration with another source using a subtraction operation.

DEFINITION:

pid.pidSub(CompPID, CONCATENATE)

PARAMETERS:

CompPID -table-

is the Request configuration to expand. MUST be an object returned by pid.pidBegin

CONCATENATE can be one of the following 3 types:

CONCATENATE -table-

is a table that MUST respect the definition of request configuration table

CONCATENATE -table-

{

SensorID -string-

is used to access parameter exposed by StartDiag Controller or StartDiag EGT. MUST be a table with a single field named SensorID and must have one of "Temp", "Volt", "EGTI", "EGTE" as value

}

CONCATENATE -number-

is a constant value

RETURNS:

None (The modifications are done to CompPID)

EXAMPLES:

pid.pidSub(pc1, {RemoteID=0x7E0, LocalID=0x7E8, SID=0x01, DID=0x0C, Start=0, Length=2, Offset=0, Scale=0.25}) – Add to the definition of pc1 a new PID request defined by the given request configuration table and perform the subtraction of the new read value from pc1

pid.pidSub(pc1, {SendorID='Temp'}) – Add to the definition of pc1 the current value of the StartDiag Controller internal temperature and perform the subtraction of the Controller internal temperature from pc1

pid.pidSub(pc1, 42) – Add to the definition of pc1 the constant value 42 and perform the subtraction of the constant 42 from pc1.

NOTE: this is the description of accepted StartDiag SensorID values:

- "Temp" : Temperature of the StartDiag Controller
- "Volt" : Voltage read by the StartDiag controller
- "EGTI" : Internal temperature of the StartDiag EGT unit (this is measured inside the resin-filled unit)
- "EGTE" : External temperature of the StartDiag EGT unit (This is measured at the tip of the EGT thermocouple sensor)

## 8.5.4. pidMul

17th April 2026 at 7:59am
APILUAPID

This API concatenates a Request configuration with another source using a multiplication operation.

DEFINITION:

pid.pidMul(CompPID, CONCATENATE)

PARAMETERS:

CompPID -table-

is the Request configuration to expand. MUST be an object returned by pid.pidBegin

CONCATENATE can be one of the following 3 types:

CONCATENATE -table-

is a table that MUST respect the definition of request configuration table

CONCATENATE -table-

{

SensorID -string-

is used to access a parameter exposed by StartDiag Controller or StartDiag EGT. MUST be a table with a single field named SensorID and must have one of "Temp", "Volt", "EGTI", "EGTE" as value

}

CONCATENATE -number-

is a constant value

RETURNS:

None (The modifications are done to CompPID)

EXAMPLES:

pid.pidMul(pc1, {RemoteID=0x7E0, LocalID=0x7E8, SID=0x01, DID=0x0C, Start=0, Length=2, Offset=0, Scale=0.25}) – Add to the definition of pc1 a new PID request defined by the given request definition table and perform the multiplication by the new read value

pid.pidMul(pc1, {SendorID='Temp'}) – Add to the definition of pc1 the current value of the StartDiag Controller internal temperature and perform the multiplication by the Controller internal temperature

pid.pidMul(pc1, 42) – Add to the definition of pc1 the constant value 42 and perform the multiplication by the constant 42.

NOTE: this is the description of accepted StartDiag SensorID values:

- "Temp" : Temperature of the StartDiag Controller
- "Volt" : Voltage read by the StartDiag controller
- "EGTI" : Internal temperature of the StartDiag EGT unit (this is measured inside the resin-filled unit)
- "EGTE" : External temperature of the StartDiag EGT unit (This is measured at the tip of the EGT thermocouple sensor)

## 8.5.5 pid.pidDiv

17th April 2026 at 7:59am
APILUAPID

This API concatenates a Request configuration with another source using a multiplication operation.

DEFINITION:

pid.pidDiv(CompPID, CONCATENATE)

PARAMETERS:

CompPID -table-

is the Request configuration to expand. MUST be an object returned by pid.pidBegin

CONCATENATE can be one of the following 3 types:

CONCATENATE -table-

is a table that MUST respect the definition of request configuration table

CONCATENATE -table-

{

SensorID -string-

is used to access parameters exposed by StartDiag Controller or StartDiag EGT. MUST be a table with a single field named SensorID and must have one of "Temp", "Volt", "EGTI", "EGTE" as value

}

CONCATENATE -number-

is a constant value

RETURNS:

None (The modifications are done to CompPID)

EXAMPLES:

pid.pidDiv(pc1, {RemoteID=0x7E0, LocalID=0x7E8, SID=0x01, DID=0x0C, Start=0, Length=2, Offset=0, Scale=0.25}) – Add to the definition of pc1 a new PID request defined by the given request definition table and perform the division by the new read value

pid.pidDiv(pc1, {SendorID='Temp'}) – Add to the definition of pc1 the current value of the StartDiag Controller internal temperature and perform the division by the Controller internal temperature

pid.pidDiv(pc1, 42) – Add to the definition of pc1 the constant value 42 and perform the division by the constant 42.

NOTE: this is the description of accepted StartDiag SensorID values:

- "Temp" : Temperature of the StartDiag Controller
- "Volt" : Voltage read by the StartDiag controller
- "EGTI" : Internal temperature of the StartDiag EGT unit (this is measured inside the resin-filled unit)
- "EGTE" : External temperature of the StartDiag EGT unit (This is measured at the tip of the EGT thermocouple sensor)

## 8.5.6 pid.pidSetDelay

17th April 2026 at 7:49am
APILUAPID

This API allows you to set a fixed delay that is applied after each single CAN PID is requested. This is required for proper PID reading on some vehicles.

DEFINITION:

pid.pidSetDelay(DELAY)

PARAMETERS:

DELAY-number-

is the length of the delay to be applied (in hundredth of a second)

RETURNS:

None (Errors are handled by the Dashboard internally)

EXAMPLES:

pid.pidSetDelay(3) – Add a delay of 3 hundredths of a second after each CAN PID is read.

NOTE: The delay is applied ONLY and to ALL the PIDs that are requested via CAN to an ECU.

NOTE: This API should be called BEFORE pid.request to have any effect.

## 8.5.7 pid.request

17th April 2026 at 8:04am
APILUAPID

This API is the final step in the Dashboard configuration.

This API takes as parameter a table which elements are tables that connect together the Gauge ID, the PID Request configuration and the Gauge configuration.

DEFINITION:

pid.request(DASHTable)

PARAMETERS:

DASHTable -table-

{

GAUGEDef -table-

{

GaugeID -number-

is the ID of the Gauge that this configuration applies to (must be in the range 1 - 6)

GaugeConf -table-

is the gauge configuration table to be used for the data of the Gauge indicated by GaugeID

ReqConf -table-

is the request configuration table to be used for reading the data to be displayed by GaugeID (must be a Request configuration generated by a call to pid.pidBegin)

}

}

RETURNS:

None (Errors are handled by the Dashboard internally)

EXAMPLES:

pid.request( {GaugeID=1, gConf=gc1, pConf=pc1} ) – Setup the first Gauge in the Dashboard with the Gauge configuration gc1 and Request configuration pc1

## 8.5.8 request configuration table

17th April 2026 at 8:07am
LUAPID

When you need to setup one of the PID request you have to define a request definition table and pass it as a paremeter to a pid.pidBegin call in order to activate the configuration and get back a composite PID object that will allow you to define more complex data for your Dashboard.
Currently there are two different table formats that you can use: one for defining UDS PIDs and one for defining J1939 PIDs. Those should not be mixed as the startDiag System currently does not support this feature, and second because is not common, nor recommanded to have a mix of the protocols on the same bus!
To get more information about the definitions, parameters and exmaples of the two tables you can follow the links below,
8.5.8.1 request configuration table UDS
8.5.8.2 request configuration table J1939

## 8.5.8.1 request configuration table UDS

17th April 2026 at 8:06am
LUAPID

DEFINITION:

{RemoteID, LocalID, SID, DID, Start, Length, Offset, Scale}

PARAMETERS:

RemoteID -number-

is the ID that will be used to send the data request

LocalID-number-

is the ID that will be used to receive the data requested (this value will setup a CAN filter, altering the behavior of a possible ongoing CAN Logger and possibly successive UDS messages reception)

SID -number-

is ServiceID to request data to

DID -number-

is DataID for the requested data (for more information about this parameter and how to set it up properly, please refer to General UDS Introduction)

Start -number-

defines the position (offset in bytes from the beginning) of first byte to be used for the parameter inside the received data

Length -number-

defines the length of the parameter information (in bytes)

Offset -number- -optional-

is a fixed value that will be ADDED to the parameter received (by default set to 0 if is not assigned)

Scale -number- -optional-

is a fixed value that will be MULTIPLIED by received value (before the the Offset is applied) (for more information relative to how the Offset and Scale interact, please refer to PID configuration) (by default set to 1 if not assigned)

NOTE: the Length field can accept 1, 2, 4 as valid value. These are used to define data that is returned in Big Endian format (most significant byte first). It also accept -2 and -4 as valid values. This allows to interpret the endianness of the data as Little Endian (the least significant byte first)

Please refer to B. Data and Endianness for more in depth explanation.

EXAMPLES:

{RemoteID=0x7E0, LocalID=0x7E8, SID=0x01, DID=0x000D, Start=0, Length=1, Offset=0, Scale=1}

This will send an UDS request with ID=0x7E0, with ServiceID=0x01, DID=0x0D, no SUB. The response will be awaited with ID=0x7E8. When the data will be received, the value of the PID will be read starting at byte 0, and will be a single byte long. To the received parameter value, a Scale of 1 will be calculated (no Scale) and an Offset of 0 will be applied (no Offset).

## 8.5.8.2 request configuration table J1939

17th April 2026 at 8:07am
LUAPID

DEFINITION:

{PGN, RAddr, Start, Length, Mask, ValidityTime, Offset, Scale, IsActive}

PARAMETERS:

PGN -number-

is the PGN that will be used to send the data request. This can be seen as the ID of a set of data request, from which we can extract the value we need

RAddr -number-

is the Remote address that we are asking the PGN to. Can be seen as the source of the data we want to read.

Start -number-

is the position where the value starts in the received data. Remember that ,as for UDS, the first value has offset 0!

Length -number-

indicates how many bytes we want extract form the received response. Note that data received by J1939 is always interpreted as Little Endian! (Please refer to B. Data and Endianness for more in depth explanation about endianness.)

Mask -number-

defines a bitmask to be used to extract some specific bits form the extracted value. Normally this is just 0xFF repeated Length times in order to get a normal numeric value.

ValidityTime -number-

defines the validity time for the received data. If the data received is a passive message (so a message that is normally passing periodically on the bus) this number represent how often the value should arrive. If data is not updated at least every ValidityTime it will be indicate on the Dashboard as Stale. If the data is instead an active request, this value specify how often it will be requested. This number is expressed in hundredths of a second.

Offset -number- -optional-

is a value that will be ADDED to the parameter received (by default set to 0 if is not assigned)

Scale -number- -optional-

is a value that will be MULTIPLIED by received value (before the the Offset is applied) (for more information relative to how the Offset and Scale interact, please refer to PID configuration) (by default set to 1 if not assigned)

IsActive -boolean- -optional-

is a flag indicating if this PID is obtained from data normally sent on the bus by other devices or if it has to be actively requested by the StartDiag System (bye default set to false if not assigned).

NOTE: the Length field can accept 1, 2, 4 as valid value.

EXAMPLES:

{PGN = 0xFEF5, RAddr = 0x42, Start = 3, Length = 1, Mask = 0xFF, ValidityTime = 200, Offset = 0, Scale = 1, IsActive = false}

This will handle J1939 data for PGN 0xFEE5, for the currently claimed address (or for broadcast) from the ECU with address 0x42. Will extract 1 single byte in position 3 (forth byte). The full byte will be used (the mask is set to 0xFF). No offset, nor scale will be applied.

{ PGN = 0x1000, RAddr = 0x42, Start = 1, Length = 2, Mask = 0xFFFF, ValidityTime = 200, Offset = 0, Scale = 1, IsActive = true }

This will request J1939 data for PGN 0x1000, using the currently claimed address from the ECU with address 0x42. Will extract 2 single byte in position 1 (second byte). Both the bytes are fully used (Mask = 0xFFFF). Will apply no offset, nor Scale. The request will be done actively, every 2 seconds.

## 8.5.9 gauge configuration table

17th April 2026 at 8:04am
LUAPID

DEFINITION:

{ Units, Desc, ValMin, ValMax, DecNumb, DigitVal, ZoneAColorID, ZoneBColorID, ZoneCColorID, ZoneBstart, ZoneCstart, AlarmMin, AlarmMinOn, AlarmMax, AlarmMaxOn }

PARAMETERS:

Units -string-

is the measurement unit to be used in the gauge (must be shorted than 12 characters)

Desc -string-

is the description to be used in the gauge (must be shorted than 12 characters)

ValMin -number-

is the minimum value shown by the Gauge (this will limit an analog Gauge)

ValMax -number-

is the maximum value shown by the Gauge (this will limit an analog Gauge)

DecNumb -number-

is the number of decimal digit to be shown. (This parameter has a meaning only for digital Gauges)

DigitVal -bool-

is a value indicating if the analog representation of the value have to be discretized (This parameter has a meaning only for analog Gauges)

ZoneAColorID -string- -optional-

is the color applied to first/lowest section of the defined values range (only the following values can be used: "None", "White", "Gray", "Black", "Red" ,"Magenta" ,"Blue","Azure" ,"Green", "Yellow". If no value is specified, "None" is used)

ZoneBColorID -string- -optional-

is the color applied to the second/middle section of the defined values range (only the following values can be used: "None", "White", "Gray", "Black","Red" ,"Magenta" ,"Blue","Azure" ,"Green" ,"Yellow". If no value is specified, "None" is used)

ZoneCColorID -string- -optional-

is the color applied to the third/highest section of the defined values range (only the following values can be used: "None", "White", "Gray", "Black","Red" ,"Magenta" ,"Blue" ,"Azure" ,"Green" ,"Yellow". If no value is specified, None is used)

ZoneBstart -number- -optional-

is where the second/middle section of the defined values range starts

ZoneCstart -number- -optional-

is where the third/highest section of the defined values range starts

AlarmMin -number- -optional-

is the value that will trigger the "under minimum value" alarm: for values smaller than this, the alarm will be turned ON

AlarmMinOn -bool- -optional-

is a flag dictating if the "under minimum value" alarm is active or not. If not defined or set to false, the alarm is deactivated.

AlarmMax -number-

is the value that will trigger the "above maximum value" alarm: for values greater than this the alarm will be turned ON

AlarmMaxOn -bool- -optional-

is a flag dictating if the "above maximum value" alarm is active or not. If not defined or set to false, the alarm is deactivated

Weight -number- -optional-

iParameter of the filter to be applied to the values for this PID. Must be greater than 0 and lower or equal to 1. If not defined, it will be set to 1 (1 effectively disables the filter providing the data as arrived).

NOTE: ValMin must be smaller than ValMax, as well as AlarmMin must be smaller than AlarmMax!

EXAMPLES:

{ DigitVal = 0, Units = "km/h", Desc = "Speed", ValMin = 0, ValMax = 255, DecNumb = 0, ZoneAColorID = "Green", ZoneBColorID = "Yellow", ZoneCColorID = "Red", ZoneBstart = 50, ZoneCstart = 130, AlarmMin = 30, AlarmMinOn = 0, AlarmMax = 100.0, AlarmMaxOn = 0, Weight =0.5 }

## A. Your First LUA Script

17th April 2026 at 8:27am
AppendixExampleLUA

For sake of simplicity we assume that we know that the CAN bus is operating at 500000 (500k) baud which is the most common baudrate found on vehicles OBD ports, and that the ECUs on the vehicle are communicating so the passive connection with can.init will suffice.

In this section we will develop together a simple LUA script that will leverage the StartDiag System LUA Interpreter and CAN interface to read the VIN number of the connected vehicle and display it.

In order to do this, we will try to use the service 0x09 which provides vehicle information, specifically the sub-function 0x02 to request the VIN in 17 bytes format. Let's do a clarification here: not all the vehicles implement this sub-function, which means that some ECMs may not reply to this command, or respond with a NAK response (which means that the request has been rejected). Even if your vehicle refuses this command, this section is very relevant for you, in fact this will discuss how to develop the script from the ground up providing some hints on the design process, after you will be able to adapt it to your specific vehicle.

To begin with, let's make sure our vehicle is recognized by the StartDiag System. Start up the System and go into "Info" -> "Vehicle Information", if you see a VIN displayed, it means that StartDiag already knows how to read the VIN, and how to do it is written in that same page, take notes of the "Remote CAN ID" and "Local CAN ID" which will be used for our request. Write down also the "VIN request" value, in fact the first 4 digits show the Service ID used and the other 4 digit show the sub-function/DID. If do not get a VIN in "Vehicle Information", please make sure you have the vehicle in Key ON and if it fails also after that check, please follow the section about troubleshooting in the StartDiag Device Guide document. You can still follow this section, but you will not be able to try it out directly.

So, let's start with the assumptions and initial hypothesis:
- the ECM that we are going to ask for the VIN responds on CAN ID = 0x7E0 (this is the most commonly used currently)
- the ECM responds with CAN ID = 0x7E8 (also this is the most used currently for these requests)
- the request will use Service ID = 0x09 and parameter ID = 0x02
- we use the UDS protocol and we are going to use the LUA API for it (uds.xmit)

Now, let's do a brief design of the steps needed to get the information we want. In this case it is quite simple, we have to do the request, wait for the response and then we have the VIN.

    rc = can.init(500000) -- Here we should checkif rc is ok (0 = OK)

    rc, data = uds.xmit(0x7E0, 0x09, 0x02, '', 0x7E8, 100)   -- Request to 0x7E0 with SID 0x09, SUB/DID 0x02, no data, response on 0x7E8, wait 1 second

Save the file on the SD, I used the name Test.sds but it is not mandatory as long as the name is at most 8 characters long.

Looking at the (uds.xmit) we can see that it returns two values: the return code (0 = OK, Error otherwise) and the data read (the bytes of the VIN in this case).

If we stop here and we try to execute this simple script, it starts, and then just finish, nothing is presented on the screen. Ok, let's make it a bit more interesting

    rc = can.init(500000) -- Here we should checkif rc is ok (0 = OK)

    rc, data = uds.xmit(0x7E0, 0x09, 0x02, '', 0x7E8, 100)   -- Request to 0x7E0 with SID 0x09,SUB/DID 0x02, no data, response on 0x7E8, wait 1 second

    for i = 1,#data do  -- The index i will assume numerical values between 1 and the number of elements in data (#data)
      print( string.sub(data, i, i) ) -- Print the i-th character of the received VIN
    end

Time to explain a bit more about the FOR loop.
FOR loops are used when you need to repeat some instructions a specific number of times. In this case we use it because we know we have to work on a group of bytes and we can easily get the total number of bytes we have by using the # operator to get the length of it (#data is equal to the number of bytes in data).
The syntax of the FOR loop is the following:

    FOR counrintg_variable = start_value, end_value DO
      instructions
    END

Note that FOR, DO and END are all mandatory. In the above script the (end value) is the size of data, in this case #data returns the length of data which is a group of bytes and LUA handles that as a string made of bytes.

OK, let's explain what we did here: - we do the same request as before, so we should get the data = the bytes of the VIN - we do a loop, to cycle on all the received data, and for each iteration of the loop we get a byte from the data and we simply "print" it on the screen.

If you try to run this script, you may notice that the characters of the VIN are printed, but one per line, so you at the end you do not really see your VIN, but only the last 9 chars. This is because the "print" function goes to a new line every time it has done with printing what it is required to.

Let's try to print all on the same line. In order to do this we will use a feature of LUA which is the string concatenation. What it does is to take strings and put them together, this is done putting the two the strings to be merged one after the other separated by two dots (string1 .. string2 .. string3 and so on)

Ex:

    txt = 'Hello '
    txt = txt .. 'World!'
    print (txt)  -- Will print "Hello World!"

To use this for printing the VIN we have to
- add a variable to hold the VIN string while we are building it
- inside the FOR loop we do not print the char but we add to the VIN string
- at the end of the FOR loop we print the resulting VIN string

    rc = can.init(500000) -- Here we should checkif rc is ok (0 = OK)

    VINString = ''  -- This is where we will construct the string of the VIN
    rc, data = uds.xmit(0x7E0, 0x09, 0x02, '', 0x7E8, 100)   -- Request to 0x7E0 with SID 0x09,SUB/DID 0x02, no data, response on 0x7E8, wait 1 second

    for i = 1,#data do  -- The index i will assume numerical values between 1 and the number of elements in data (#data)
      VINString = VINString .. string.sub(data, i, i)
    end

    print( VINString )  -- Print the VINString compiled

And here we have it! If all went fine, this time we should have on the StartDiag Display the VIN of the connected vehicle!

But wait, something is still missing. Let's try this: turn key OFF and restart the script (many ECM will still respond to requests for some time, even for 10 minutes or more!), so if you do not get an error here, wait more) Start the script again and let's see the result.

This time we get LUA error complaining about using a trying to get the length of a nil value, this is a good occasion to learn more about reading the LUA errors:

ABORTED: unprotected error in call to Lua API (Script\Test.sds:3) attempt to get length of a nil value (global 'data'))

What is happening here?

To discover what is going on we have to start from the error message itself... It is telling us that the error happened in line 3 (the number next to the script name, Test.sds in my case), also it is telling us that we are trying to get the length of a nil value. nil is a valid value but it is basically a "nothing" value, and Lua does not know how to get the length of such a value. To finish our diagnosis, the name of the nil variable is "data".

The point here is that #data is trying to get the length of a nil value, but why? The root cause is that since the vehicle is in key OFF, we can't really ask him anything, it will not respond. Since it is not responding, the uds.xmit returned an error (we can check that rc in this case has a value different than 0, it has 1 which means timeout). We sent the request to the vehicle and we did not get the response in the set 1 second time because the key is OFF.

Having a script fail because of unexpected error is not a very nice thing to do, it is much better to show an error on the screen and then, in case, leave or do the cleanup that may be needed. Let's do some proper error handling to the example code. For this we will use the IF statement of LUA, which allows us to do something only if a certain condition is true (for the IF to work it is necessary that is called on a statement that evaluate to true or false).

The IF statement , in LUA, has the following syntax:

    IF condition THEN
      instructions
    END

IF, THEN, and END are all part of the mandatory syntax. The (instructions) are executed only if (condition) evaluate to true. Let's see how to use it to verify the rc in the previous example.

    rc = can.init(500000) -- Here we should checkif rc is ok (0 = OK)

    VINString = ''  -- This is where we will construct the string of the VIN
    rc, data = uds.xmit(0x7E0, 0x09, 0x02, '', 0x7E8, 100)   -- Request to 0x7E0 with SID 0x09,SUB/DID 0x02, no data, response on 0x7E8, wait 1 second

    if rc ~= 0 then
      print('VIN request failed.')
      return  --This will terminate the script execution here
    end

    for i = 1,#data do  -- The index i will assume numerical values between 1 and the number of elements in data (#data)
      VINString = VINString .. string.sub(data, i, i)
    end

    print( VINString )  -- Print the VINString compiled

Now, in the same conditions as before we do not see the error from LUA but we have the proper error message.

A small clarification: here we handled the problem of the nil value on data considering it as only possible when the request fails. Another option would have been to instead ignore the rc value and decide that we are going to prepare the VIN string only if there are data to be used.

    rc = can.init(500000) -- Here we should checkif rc is ok (0 = OK)

    VINString = ''  -- This is where we will construct the string of the VIN
    rc, data = uds.xmit(0x7E0, 0x09, 0x02, '', 0x7E8, 100)   -- Request to 0x7E0 with SID 0x09,SUB/DID 0x02, no data, response on 0x7E8, wait 1 second

    if data == nil then
      VINString = 'Got no VIN data.'
    else
        for i = 1,#data do  -- The index i will assume numerical values between 1 and the number of elements in data (#data)
          VINString = VINString .. string.sub(data, i, i)
        end
    end

    print( VINString )  -- Print the VINString compiled

In this case we see the "Got no VIN data" message. The behavior may looks the same, but in reality the logic behind it is very different: in the second case we are deciding that since we need the VIN data we handle the lack of that, and not an error in the communication. This approach covers not only the case of a negative response from the vehicle, but also the case in which the response is OK but there is no data sent in it!

Also this was a good occasion to show you another construct: IF .. ELSE: this not only provides you with a way to so something if a given condition is true, but also allows you to define what to do otherwise.

The syntax of the IF .. ELSE statement is the following:

    IF condition THEN
      instructions_for_true
    ELSE
      instructions_for_false
    END

The keywords IF, THEN, ELSE, END are all mandatory. For this statement, instructions_for_true are executed only if condition is true, otherwise the instructions_for_false are executed instead.

This section starts with some assumptions, like fixing in the script the cAN baudrate to use. For more dynamic approaches, take a look at the sample code in section LUA CAN module.

## B. Data and Endianness

17th April 2026 at 8:46am
Appendix

B. Data and Endianness
Appendix
Endianness is the term used in informatics to identify the order in which the data is stored by a system.
Before going on with endianness, let's define two other terms that we have to understand:
- LSB: acronym of least significant byte (or sometimes bit, depends on the context)
- MSB: acronym of most significant byte (or sometimes bit, depends on the context)
Given a sequence of bytes, LSB is the one holding the value in the least significant position, while MSB is the one holding the most significant position.
Let's make an example:
1943 (decimal)
In this number, 1 is the most significant digit (it is the analog of the MSB, but in base 10), while 3 is the least significant digit (the analog of LSB).
The most significant digit holds the value in the position with the biggest multiplier (1000), while the least significant digit is the value in
the position with the smallest multiplier (1).
This is what we are normally used to consider when dealing with numbers in base ten, so they are natural for us.
When we work with bytes, though, the things can be a little bit more tricky.
Let's suppose we want to represent in hexadecimal the number 32d, it becomes 0x20.
All simple here since we are dealing with a single byte.
Now let's suppose that we want to represent a decimal number bigger than 256d, for example 1943. For that we get 0x797, which is 2 bytes: 0x07 and 0x97.
0x97 is the LSB , while 0x07 is the MSB.
Also here the things are going quite smoothly.
Now let's face the point: in informatics we have often to deal with the concept of architecture, which define how a system has been designed. Part of the architecture is to define the order in which the bytes are stored for given data: this is the endianness of the architecture.
Let's go back to the example, we may decide to store 1943 = 0x0797 as [0x07, 0x97] or as [0x97, 0x07]. Both are fine, as long as we know which order to use and we stick completely to the decided one.
The first representation is the Big Endian (the value associated to the biggest multiplier comes first) representation, while the second is the Little Endian (the value associated to the smallest multiplier comes first).
When dealing with data incoming from different devices on the bus, we may be in doubt about which of the two endianness is used. As long as it is
not mandate by the protocol (for example J1939 mandate data to be passed in Little Endian format) there is no direct way to know... unless you know.
For example SAE PIDs are normally sent Big Endian (so the most significant byte is the first received, which is the one the leftmost for the LUA
standard of the Hexadecimal String). The same is not necessarily true for data from Service ID 0x22.
Let's do some examples:
- 1 byte (endianness does not change anything): 0x20
32d
- 2 bytes: 0x0797
(Big Endian) [MSB = 0x07, LSB = 0x97] = 1943
(Little Endian) [MSB = 0x97, LSB = 0x07]= 38663
- 4 bytes: 0x01020304
(Big Endian) [MSB = 0x01, LSB = 0x04] = 16909060
(Little Endian) [MSB = 0x04, LSB = 0x01] = 67305985

## C. PID definition examples

17th April 2026 at 9:06am
AppendixPID

Here we are going to propose some commented example on how to define PIDs for the StartDiag Devices.
For this section we are going to use standard SAE PIDs (Service ID = 0x01 from a vehicle ECU), so they are more commonly available on a big variety of busses. The same approach can be used for the custom PIDs (as for example the one provided by SID 0x22, but those are generally proprietary and specific for each vehicle , or even ECM firmware version).
Example 1:
Let's start with the most simple one, a PID defined by only 1 byte.
Looking at the SAE PIDs, we can see that "Engine Coolant Temperature" is such a PID.

ID:0x05, LEN:1, Description: Engine coolant temperature, Min:-40, Max: 215, Units:°C, Formula: A-40

These are indeed all the information we need to request and decode the data.
- As we said above, we are considering the Standard SAE PIDs, so SID=0x01.
- From the specific definition we know the ID is 0x05, so DID=0x0005 (or 0x05).
Now the most complicated part: how to interprete the formula. It is A-40, but what is A? A is the simply the first byte in the received data. Moreover in the formula only A is used, so the information is only 1 byte long, which confirms the given LEN from the description.
This means that:
- Start is 0 (remember that received data has 0 as initial byte offset)
- Length is 1
From the formula we can see that we need to remove 40 from the received byte value, and no scale have to applied:
- Offset is -40
- Scale is 1
The only missing information is the RemoteID and the LocalID to be used. Unfortunately that information is not generally available, and is vehicle dependent. For most of cases the RemoteID 0x7E0 (2016d) and LocalID 0x7E8 (2024d) can be used, but there is no warranty there.
If we would need the LUA Code for the request definition, it would be:

    PR_0001 = {	RemoteID = 2016, LocalID = 2024, SID = 1, DID = 5, Start = 0, Length = 1, Offset = -40,	Scale = 1}

Ok, this is done. Let's pass on something a bit more complicate.
Example 2:
Now we are going to look for another single byte SAE PID, but this time with a bit more complicated formula.
Let's try the "Calculated engine load". Not all vehicles have this, but as an example it fits nicely.

ID:0x04, LEN:1, Description: Calculated engine load, Min:0, Max: 100, Units:%, Formula:((A*100)/128)

The IDs to use are:
- SID = 0x01 (Standard SAE PIDs)
- DID = 0x0004 (or 0x04) (Calculated engine load ID)
As above we find in the Formula only one byte used: A.
- Start is 0 (we use the first byte received)
- Length is 1
From the formula we see we have a scale factor (a value multiplying the byte received) and we have no offset:
- Offset is 0
- Scale is 100/128 (the equivalent value is 0.78125)
In this example we will consider again the same RemoteID and LocalID as before.
The equivalent LUA configuration code is:

    PR_0001 = {	RemoteID = 2016, LocalID = 2024, SID = 1, DID = 4, Start = 0, Length = 1, Offset = 0, Scale = 0.78125}

Good, we managed to see and example with a scale value. Now we have all the information to go to the next step: a multi bytes value that can wrap up everything we learned.
Example 3:
Let's see, a good candidate can be "Mass air flow sensor".
The description is:

ID:0x10, LEN:2, Description: Mass air flow sensor (MAF) air flow rate, Min:0, Max: 655.35, Units:g/s, Formula: (256*A + B)/100

Now we have to handle 2 bytes, and this complicates the things a little bit. First and foremost, if you do not have clear the concept of endianness, please refer to the appendix B. Data and Endianness. This is an important topic because we need to understand why the A byte is multiplied by 256 and then B is added up.
The formula tells us that we are dealing with 2 bytes (same information arrives again from the LEN part of the definition too).
Having to deal with 2 bytes brings us to the question: are these bytes presented in Little Endian or Big Endian?
Generally we have to know it in advance, or try out the possible case and see which gives us the right result.
Having the formula, though, clears it up in this case: we are dealing with data in Big Endian format, in fact the first byte is multiplied by 256, while the second is left alone (we can implicitly see that as being multiplied by 1).
Since 256 is the highest multiplication value in the formula, that is the most significant byte, and is sent first, so the data are sent in Big Endian.
(Note that multiplying a byte by 256 is equivalent to left-shifting its value by 8 bits, effectively moving the byte value to the byte on the left: Ex: (0x0020 << 8) = 0x2000).
Good, with those new information we can now see how to handle the data for the "Mass air flow sensor" reading.
After the request we will receive 2 bytes [A ,B] and those have to be interpreted as Big Endian. SatrtDiag System already interprete the data as Big Endian by default, so we have to just provide the proper size, start and it will read the value.
Moreover, with what we learned in the Example 2 we know that the value have a scale factor of 1/4 applied. Let's put it all together:
- RemoteID = 0x7E0 (as used in the previous examples)
- LocalID = 0x7E8 (as used in the previous examples)
- SID = 0x01 (SAE Standard)
- DID = 0x0010
- Start is 0
- Length is 2
- Offset is 0 (no offset defined in the formula)
- Scale is 1/4 (0.25)
The equivalente LUA request definition becomes:

    PR_0001 = {	RemoteID = 2016, LocalID = 2024, SID = 1, DID = 16, Start = 0, Length = 2, Offset = 0, Scale = 0.25}

These examples refer to UDS SAE PIDs but the exact same considerations apply to the custom PIDs obtain from whichever other source you may have on the bus, even for J1939 PIDs, just change the table definition accordingly.

## D. Error Codes

17th April 2026 at 9:23am

Currently StartDiag System can show some errors depending on the operation that as ongoing at the moment of the issue.
We can split them in various categories:
- LUA CAN API Errors
- LUA J1939 API Errors
The descriptions below use decimal notation for the error code.

## StartDiag System errors:

These errors are related to the connection between StartDiag Controller and Display:
1 = returned if the communication between StartDiag Controller and StartDiag Display timed out.

## Peripheral errors:

There are 2 special errors that have a precise meaning and deserve clarification:
11800 = returned when the can.init fails because the specified baudrate is not compatible with the connected bus or if the forceActive flag is not set and the bus has no passing data.
11802 = returned when the can.init is called after a previous successful call to can.init itself and no can.uninit has been done. Simply put, it means "you can't call can.init when the can peripheral is already on"

## CAN Errors:

CAN errors can be shown in the Dashboard if the CAN communication with the requested ECUs is not working correctly, but can also show up during script execution if the requested CAN command encounters unexpected results.
All those errors are derived from the Controller and displayed on the Display unit.
Below you can find the list of the CAN errors that are currently documented
0 = Ok: No Error all went fine
10001 = Timeout error: we did not get a response in the prescribed time to a CAN request
10003 = NACK: using UDS protocol the remote ECU reported an explicit deny to the request made
10004 to 10019 = Protocol error: One of the steps for UDS protocol was not respecting the required phases
10020 = XMIT BUS Error: The controller is not able to send packets on the bus. Generally this is due to Controller becoming the only alive CAN unit on the bus (so the CAN protocol ACK is not received) or the bus has defective units that bring the Controller in BUS OFF state (this situation is described deeper in the official CAN documentation).Another possible cause for this erro is the use a force configuration file with incorrect parameters for the BUS to which the Controller is currently connected.
10021 = Peripheral Disabled error: the requested operation can't be performed because the required peripheral is not active. This normally relates to the CAN periphery on the Controller and it can happen when the Controller is booting, the CAN bus is not active and no communication is ongoing on the BUS. This situation prevents StartDiag Controller from being able to identify the baudrate of the bus and turns off the CAN periphery to avoid to trigger errors on the other units connected to the bus that may come alive later on.
(If at boot the StartDiag goes straight to Dashboard but the Controller did not identify the BUS parameters, instead of the error 21 the error 11800 which has the same meaning).

## J1939 Errors:

J1939 errors can be shown in the Dashboard if the communications with ECUs are not working as expected. They can also be presented as a result of calling J1939 API in LUA scripting.
Below you can find a list of the J1939 errors that are currently documented:
0 = OK: No error, all was fine
193901 = Timeout error: we did not get a response in the prescribed time
193903 = NACK: using J1939 protocol the remote ECU reported an explicit deny to the request made
193905 = Not Enough Space. Some features have a limited number of available slots on StartDiag Controller, for example the requesting of a PGN requires the use of a free PGN slot. On StartDiag Controller there are 18 slots available. If you try to register more than 18 PGN, you will get this error.
193907 = Subscribe Error: a request to handle a specific PGN was not able to register in the subscription list on Controller
193908 = Connect Error: Controller was not able to connect as prescribed to the J1939 BUS (can be that the used address is already taken)
193909 = TX Error: Controller was not able to send data on the bus
193910 = RX Error: Controller was not able to read data form the bus
193911 to 193917 = J1939 Protocol Error: one of the phases of the J1939 protocol was not respected
When in a Dashboard, another error can be seen on the Gauges: it may happen that a pink square with the indication "00" is presented. That is not an error strictly speaking, in fact it shows the value of 0. It instead indicates that for the definition of the Gauge, a ValidityTime has been provided and there has been no update to the values before ValidityTime expired. Basically it just indicates that the data shown in the Dashboard is considered Stale (not updated in time).
10021 = Same as 10021 for CAN: CAN peripheray is disabled. No previous can.init was successfully done before.
When entering the Dashboard moreover it can happen that the CAN peripheral on StartDiag Controller is not enabled (See the section of "CAN Errors" for a deeper explanation of the cases in which it can happen). In that case also for J1939 Dashboards the error 11800 is presented.

## PID Errors:

In Dashboards, the errors are represented in colored squares.
Also, in the gauges the errors are represented in Hexadecimal format (each digit can be 0,1, 2, 3, 4, 5 ,6 ,7 ,8 ,9 ,A ,B ,C, D, E ,F where A=10, B=11, C=12, D=13, E=14, F=15) and only the last 2 digits are presented for brevity.