Dynam Labs VCU+ Plugin Reference Manual

April 26, 2026

Firmware Version 2.0.14

Overview

Plugins enable users to add custom functionality to the VCU+ while keeping its core firmware lightweight and efficient. By allowing customization without modifying the core, plugins ensure the system remains maintainable and adaptable to a wide range of application-specific needs.

With plugins, users can implement tailored features, such as custom control algorithms or data processing workflows, without the overhead of embedding these directly into the core firmware. This flexibility empowers users to quickly adapt the VCU to their unique requirements.

This reference manual provides a comprehensive guide to using plugins, including an overview of available functions, configuration options, and instructions for writing, testing, and deploying custom Lua-based logic.

The plugin language is Lua, chosen for its simplicity, low learning curve, and lightweight nature. Lua is widely regarded for its ease of integration into embedded systems and its ability to execute scripts efficiently, making it ideal for the resource-constrained environment of the VCU+. Its straightforward syntax and minimal setup allow both novice and experienced developers to quickly write and test custom logic.

For more information about Lua, refer to the official documentation:

This reference manual provides a comprehensive guide to using plugins, including an overview of available functions, configuration options, and instructions for writing, testing, and deploying custom Lua-based logic.

Core Loop

The core logic of a plugin is implemented within the onTick() function. This function is automatically called by the VCU at a fixed interval, serving as the main loop for your script. All continuous tasks, such as reading sensors, making decisions, and controlling outputs, should be placed within this function.

onTick()

This function is called repeatedly by the VCU firmware. Implement your main control logic inside this function.

Example Use Case: Continuously monitoring battery temperature and activating a cooling fan if it exceeds a threshold.

-- This function will be called on every tick of the plugin execution cycle
function onTick()
    local temp = getSignal(BatteryTemp)
    if temp > 80 then
        setOut(HS1) -- Turn on cooling fan
    else
        clearOut(HS1) -- Turn off cooling fan
    end
end
-- This function will be called on every tick of the plugin execution cycle
function onTick()
    local temp = getSignal(BatteryTemp)
    if temp > 80 then
        setOut(HS1) -- Turn on cooling fan
    else
        clearOut(HS1) -- Turn off cooling fan
    end
end

onSleep()

This optional callback is called by the VCU just before the device enters sleep mode. Use it to perform any necessary cleanup, save state, or send a final CAN message before power is removed. The VCU waits for this function to complete before proceeding with the sleep sequence.

Example Use Case: Sending a final CAN message or saving state before the VCU goes to sleep.

function onSleep()
    sendCAN(CAN2, 0x7FF, {0x00}) -- notify other devices we're going to sleep
end
function onSleep()
    sendCAN(CAN2, 0x7FF, {0x00}) -- notify other devices we're going to sleep
end

Functions

The following is a list of all currently supported functions available for use in plugins. These functions form the building blocks for creating powerful and highly customized logic within your plugins, giving you full control over VCU outputs, sensor data, communication, and control algorithms. The list is continuously growing as more functions are added.

setOut(pin)

This function sets the specified output pin to its active state:

  • High-Side Outputs: The pin transitions to a high state (voltage applied).
  • Low-Side Outputs: The pin transitions to a low state (connected to ground).

Example Use Case: Activating a high-side output pin to power a relay or a low-side output pin for devices requiring a ground-side switch.

-- Activate a high-side output 1
setOut(HS1)
-- Activate a low-side output 2
setOut(LS2)
-- Activate a high-side output 1
setOut(HS1)
-- Activate a low-side output 2
setOut(LS2)

clearOut(pin)

This function clears the specified output pin, setting it to its inactive state:

  • High-Side Outputs: The pin transitions to a low state (voltage removed).
  • Low-Side Outputs: The pin transitions to a high state (disconnected from ground).

Example Use Case: Deactivating a relay or turning off a light connected to the VCU.

-- Deactivate the high-side output 1
clearOut(HS1)
-- Deactivate the low-side output 2
clearOut(LS2)
-- Deactivate the high-side output 1
clearOut(HS1)
-- Deactivate the low-side output 2
clearOut(LS2)

getSignal(signalId)(Requires firmware v2.0.14 or later)

Retrieves the current value of the specified signal. This allows the plugin to access real-time data, such as temperature, speed, or pressure. Returns nil if the signal state is invalid. Always check the return value before performing arithmetic operations. The return value is always a floating point number, so it must be rounded or converted to an integer before using it in bitwise operations.

Arguments:
  • signalId: Which signal to read, using one of the built-in constants (e.g. Temp1, BatteryVoltage); see the list below for all identifiers.

Example Use Case: Reading a temperature value for dynamic control logic.

List of available signals:

Accelerator Pedal
  • AcceleratorPedal (accelerator pedal position (0-100%))
  • AcceleratorPedalPrimary (primary accelerator pedal sensor (0-100%))
  • AcceleratorPedalSecondary (secondary accelerator pedal sensor (0-100%))
High-Voltage Battery
  • BatteryCurrent (high-voltage pack current reported by the BMS (A))
  • BatteryVoltage (high-voltage pack voltage reported by the BMS (V))
  • BatteryTemp (highest cell/module temperature reported by the BMS (°C))
  • BatteryCharge (pack state of charge reported by the BMS (%))
Primary Drive Inverter
  • DI_inverterTemp (primary inverter module temperature (°C))
  • DI_statorTemp (primary motor stator temperature (°C))
  • DI_heatSinkTemp (primary inverter heat-sink temperature (°C))
  • DI_inletTemp (primary inverter coolant inlet temperature (°C))
  • DI_InverterCurr (primary inverter DC current (A))
Secondary Drive Inverter
  • DIS_inverterTemp (secondary inverter module temperature (°C))
  • DIS_statorTemp (secondary motor stator temperature (°C))
  • DIS_heatSinkTemp (secondary inverter heat-sink temperature (°C))
  • DIS_inletTemp (secondary inverter coolant inlet temperature (°C))
  • DIS_InverterCurr (secondary inverter DC current (A))
Auxiliary Temperatures
  • Temp1 (auxiliary temperature sensor 1 (°C))
  • Temp2 (auxiliary temperature sensor 2 (°C))
  • Temp3 (auxiliary temperature sensor 3 (°C))
  • Temp4 (auxiliary temperature sensor 4 (°C))
Motor Speed
  • RPM (drive motor speed (rpm))
  • RPMPrimary (primary drive motor speed (rpm))
  • RPMSecondary (secondary drive motor speed (RPM))
Digital Inputs
  • Din1 (state of digital input 1 (0 or 1))
  • Din2 (state of digital input 2 (0 or 1))
  • Din3 (state of digital input 3 (0 or 1))
  • Din4 (state of digital input 4 (0 or 1))
  • Din5 (state of digital input 5 (0 or 1))
  • Din6 (state of digital input 6 (0 or 1))
  • Din7 (state of digital input 7 (0 or 1))
  • Din8 (state of digital input 8 (0 or 1))
  • Din9 (state of digital input 9 (0 or 1))
  • Din10 (state of digital input 10 (0 or 1))
  • Din11 (state of digital input 11 (0 or 1))
  • Din12 (state of digital input 12 (0 or 1))
  • Din13 (state of digital input 13 (0 or 1))
  • Din14 (state of digital input 14 (0 or 1))
  • Din15 (state of digital input 15 (0 or 1))
  • DinNone (placeholder for unused digital input)
System
  • LVBatt (low-voltage (12 V) battery voltage (V))
  • Ignition (ignition / key-on input state (0 or 1))
  • Gear (currently selected gear)
  • None (placeholder for an unassigned signal)
CAN Keypad Buttons
  • CAN_KEYPAD_B1 (state of CAN keypad button 1 (0 or 1))
  • CAN_KEYPAD_B2 (state of CAN keypad button 2 (0 or 1))
  • CAN_KEYPAD_B3 (state of CAN keypad button 3 (0 or 1))
  • CAN_KEYPAD_B4 (state of CAN keypad button 4 (0 or 1))
  • CAN_KEYPAD_B5 (state of CAN keypad button 5 (0 or 1))
  • CAN_KEYPAD_B6 (state of CAN keypad button 6 (0 or 1))
  • CAN_KEYPAD_B7 (state of CAN keypad button 7 (0 or 1))
  • CAN_KEYPAD_B8 (state of CAN keypad button 8 (0 or 1))
Analog Inputs
  • Ain1 (analog input 1 voltage (V))
  • Ain2 (analog input 2 voltage (V))
  • Ain3 (analog input 3 voltage (V))
  • Ain4 (analog input 4 voltage (V))
  • Ain5 (analog input 5 voltage (V))
  • Ain6 (analog input 6 voltage (V))
  • Ain7 (analog input 7 voltage (V))
  • Ain8 (analog input 8 voltage (V))
-- Read temperature signal 1
local temperature = getSignal(Temp1)
-- Read accelerator pedal and use it in logic
local pedal = getSignal(AcceleratorPedal)
-- Read temperature signal 1
local temperature = getSignal(Temp1)
-- Read accelerator pedal and use it in logic
local pedal = getSignal(AcceleratorPedal)

getSensor(signalId)Deprecated since v2.0.14 — use getSignal()

Deprecated alias for getSignal(). Retained for backwards compatibility with plugins written before firmware 2.0.14. Functionally identical to getSignal() — new code should prefer getSignal().

Arguments:
  • signalId: Which signal to read, using one of the built-in constants (e.g. Temp1, BatteryVoltage); see the list below for all identifiers.

Example Use Case: Existing plugins written for firmware prior to 2.0.14.

-- Equivalent to getSignal(Temp1)
local temperature = getSensor(Temp1)
-- Equivalent to getSignal(Temp1)
local temperature = getSensor(Temp1)

sendCAN(canBus, canID, data)

Sends a CAN message with the specified ID and data payload. Enables communication with other devices on the CAN bus.

Note: Only CAN2 and CAN3 are supported. Calls with CAN1 are silently ignored.

Arguments:
  • canBus: The physical bus to send the message on. (CAN2 or CAN3 only)
  • canID: The identifier of the CAN message.
  • data: The payload, a byte array of up to 8 bytes.

Example Use Case: Sending commands to external controllers or reporting custom data.

-- Send a CAN message on CAN2 with ID 0x123 and data payload {0x01, 0x02, 0x03}
sendCAN(CAN2, 0x123, {0x01, 0x02, 0x03})
-- Example of sending a command to an external device on CAN3
sendCAN(CAN3, 0x200, {0xFF, 0xAA, 0x55})
-- Send a CAN message on CAN2 with ID 0x123 and data payload {0x01, 0x02, 0x03}
sendCAN(CAN2, 0x123, {0x01, 0x02, 0x03})
-- Example of sending a command to an external device on CAN3
sendCAN(CAN3, 0x200, {0xFF, 0xAA, 0x55})

receiveCAN(canBus, canID, handler)

Registers a CAN handler that gets called when a CAN message is received. Only gets called when a message on the bus with correct canID is received. This function only needs to be called once, from global scope. The message handler gets called at the loop rate - prior to the onTick() function, not at the exact moment the CAN message is received.

Arguments:
  • canBus: The physical bus to send the message on. (CAN1/CAN2/CAN3)
  • canID: The identifier of the CAN message.
  • handler: function that gets called when CAN message is received

Example Use Case: Receiving data from external controllers.

function handleMessage(data)
  local temp = (data[2] << 8) | data[1]
  print("Temp DLC: " .. #data .. " Temp: " .. temp)
end

receiveCAN(CAN2, 0x123, handleMessage)
function handleMessage(data)
  local temp = (data[2] << 8) | data[1]
  print("Temp DLC: " .. #data .. " Temp: " .. temp)
end

receiveCAN(CAN2, 0x123, handleMessage)

newPID(p, i, d)

Creates a new PID controller instance with the specified tuning parameters.

Arguments:
  • p: Proportional gain
  • i: Integral gain
  • d: Derivative gain

Example Use Case: Initializing a PID controller to maintain target speeds or stabilize temperatures.

-- Create a PID controller with P=1.0, I=0.5, D=0.1
local pid = newPID(1.0, 0.5, 0.1)
-- Create a PID controller with P=1.0, I=0.5, D=0.1
local pid = newPID(1.0, 0.5, 0.1)

pid:compute(target, actual)

Computes the control output of an existing PID controller based on a target value and the actual measured value. This function is supposed to be called in a loop.

Arguments:
  • target: The desired setpoint value.
  • actual: The current measured value.

Example Use Case: Adjusting a fan speed to match the desired temperature.

-- Create a PID controller
local pid = newPID(1.0, 0.5, 0.1)
-- Compute the control output
local targetTemp = 100 -- Target temperature
-- getSignal() requires firmware 2.0.14+
local actualTemp = getSignal(Temp1)
local controlOutput = pid:compute(targetTemp, actualTemp)
-- Create a PID controller
local pid = newPID(1.0, 0.5, 0.1)
-- Compute the control output
local targetTemp = 100 -- Target temperature
-- getSignal() requires firmware 2.0.14+
local actualTemp = getSignal(Temp1)
local controlOutput = pid:compute(targetTemp, actualTemp)

setPWMFreq(channel, frequency)

Sets the frequency of the specified PWM (Pulse Width Modulation) channel in Hertz (Hz). This determines how many PWM cycles are generated per second.

Arguments:
  • channel: The PWM channel to configure (e.g., PWM1, PWM2).
  • frequency: The desired frequency in Hertz.

Example Use Case: Setting the PWM frequency for a fan or pump controller.

setPWMFreq(PWM1, 750)
setPWMFreq(PWM1, 750)

setPWMDuty(channel, duty)

Sets the duty cycle of the specified PWM (Pulse Width Modulation) a channel in percent (%).

Arguments:
  • channel: The PWM channel to configure (e.g., PWM1, PWM2).
  • duty: The desired duty cycle in percent.

Example Use Case: Controlling flow for a PWM-enabled water pump.

setPWMDuty(PWM1, 25)
setPWMDuty(PWM1, 25)

print(string)

Prints a message to the plugin log inside of Bolt.

Arguments:
  • string: String to print

Example Use Case: Debug print statement during plugin development.

-- getSignal() requires firmware 2.0.14+
local ain3_val = getSignal(Ain3)
print("Ain3 Value: " .. ain3_val)
-- getSignal() requires firmware 2.0.14+
local ain3_val = getSignal(Ain3)
print("Ain3 Value: " .. ain3_val)

printStatistics()

Prints detailed memory usage and system statistics to the plugin log.

Example Use Case: Useful for debugging memory leaks and monitoring performance.

-- Print memory and performance stats to the log
printStatistics()
-- Print memory and performance stats to the log
printStatistics()

setLogEntry(entry, value)

Writes a floating-point value into one of the VCU plugin log slots so it can be surfaced alongside other plugin telemetry. The VCU exposes 8 plugin log slots indexed from 1 to 8.

Note: If entry is outside the valid range or value is not a number, the call is rejected and an error is logged to the plugin console.

Arguments:
  • entry: Plugin log slot index (integer between 1 and 8).
  • value: Floating-point value to store in the slot.

Example Use Case: Surfacing a derived signal (e.g. a computed power figure) in the plugin log output.

-- Publish a derived signal to plugin log slot 1
local power = getSignal(BatteryVoltage) * getSignal(BatteryCurrent)
setLogEntry(1, power)
-- Publish a derived signal to plugin log slot 1
local power = getSignal(BatteryVoltage) * getSignal(BatteryCurrent)
setLogEntry(1, power)

getTime()

Returns elapsed time in ms since the VCU booted.

Example Use Case: Handle timing operations

if getTime() > 2500 then
  someFunction()
end
if getTime() > 2500 then
  someFunction()
end

requestGear(gear)

Requests a new gear.

Arguments:
  • gear: Requested Gear (GEAR_P, GEAR_R, GEAR_N, GEAR_D)

Example Use Case: Custom shifter implementations

if someVar == 2500 then
  requestGear(GEAR_D)
end
if someVar == 2500 then
  requestGear(GEAR_D)
end

setDrivemode(mode)(Requires firmware v1.1.7 or later)

Sets a new drive-mode.

Arguments:
  • mode: New drive mode (CHILL, NORMAL, SPORT, VALET)

Example Use Case: Conditional drive mode changes

if msg[1] == 100 then
  setDrivemode(SPORT)
end
if msg[1] == 100 then
  setDrivemode(SPORT)
end

configureBmsVolt(bus, canId, startBit, length, isSigned, scale, offset, encoding)

Configures decoding of the battery pack voltage from a CAN message. Once configured, the VCU will automatically decode the specified signal and use it as the BMS voltage input.

Note: The BMS type must be set to Custom (Plugin) for this function to take effect. This function can be global and does not have to be called repeatedly.

Required signals: When using a Custom (Plugin) BMS, the pack voltage (configureBmsVolt), discharge current limit (DCL) (configureBmsDisLim), and charge current limit (CCL) (configureBmsChgLim) must all be configured. Their CAN messages must be received at least every 500 ms, otherwise the BMS will be treated as offline. Pack current (configureBmsCurr) and state of charge (configureBmsSoc) are optional.

Arguments:
  • bus: CAN bus to read the message from (CAN1, CAN2, or CAN3)
  • canId: CAN ID containing the voltage signal
  • startBit: start decoding data at this bit of the CAN frame
  • length: number of bits to decode
  • isSigned: decode value as a signed number
  • scale: encoded data scaled by this factor
  • offset: encoded data offset
  • encoding: CAN_MOTOROLA or CAN_INTEL

Example Use Case: Custom BMS integration

configureBmsVolt(CAN2, 0x301, 0, 16, false, 1, 0.0, CAN_INTEL)
configureBmsVolt(CAN2, 0x301, 0, 16, false, 1, 0.0, CAN_INTEL)

configureBmsCurr(bus, canId, startBit, length, isSigned, scale, offset, encoding)

Configures decoding of the battery pack current from a CAN message. The decoded value will be used as the BMS current measurement within the VCU.

Note: The BMS type must be set to Custom (Plugin) for this function to take effect. This function can be global and does not have to be called repeatedly.

Required signals: When using a Custom (Plugin) BMS, the pack voltage (configureBmsVolt), discharge current limit (DCL) (configureBmsDisLim), and charge current limit (CCL) (configureBmsChgLim) must all be configured. Their CAN messages must be received at least every 500 ms, otherwise the BMS will be treated as offline. Pack current (configureBmsCurr) and state of charge (configureBmsSoc) are optional.

Arguments:
  • bus: CAN bus to read the message from (CAN1, CAN2, or CAN3)
  • canId: CAN ID containing the current signal
  • startBit: start decoding data at this bit of the CAN frame
  • length: number of bits to decode
  • isSigned: decode value as a signed number
  • scale: encoded data scaled by this factor
  • offset: encoded data offset
  • encoding: CAN_MOTOROLA or CAN_INTEL

Example Use Case: Custom BMS integration

configureBmsCurr(CAN2, 0x302, 0, 16, true, 0.1, 0.0, CAN_INTEL)
configureBmsCurr(CAN2, 0x302, 0, 16, true, 0.1, 0.0, CAN_INTEL)

configureBmsSoc(bus, canId, startBit, length, isSigned, scale, offset, encoding)

Configures decoding of the battery state of charge (SOC) from a CAN message. The decoded value will be used by the VCU for SOC display.

Note: The BMS type must be set to Custom (Plugin) for this function to take effect. This function can be global and does not have to be called repeatedly.

Required signals: When using a Custom (Plugin) BMS, the pack voltage (configureBmsVolt), discharge current limit (DCL) (configureBmsDisLim), and charge current limit (CCL) (configureBmsChgLim) must all be configured. Their CAN messages must be received at least every 500 ms, otherwise the BMS will be treated as offline. Pack current (configureBmsCurr) and state of charge (configureBmsSoc) are optional.

Arguments:
  • bus: CAN bus to read the message from (CAN1, CAN2, or CAN3)
  • canId: CAN ID containing the SOC signal
  • startBit: start decoding data at this bit of the CAN frame
  • length: number of bits to decode
  • isSigned: decode value as a signed number
  • scale: encoded data scaled by this factor
  • offset: encoded data offset
  • encoding: CAN_MOTOROLA or CAN_INTEL

Example Use Case: Custom BMS integration

configureBmsSoc(CAN2, 0x303, 0, 8, false, 1.0, 0.0, CAN_INTEL)
configureBmsSoc(CAN2, 0x303, 0, 8, false, 1.0, 0.0, CAN_INTEL)

configureBmsDisLim(bus, canId, startBit, length, isSigned, scale, offset, encoding)

Configures decoding of the battery discharge current limit (DCL) from a CAN message. The decoded value defines the maximum allowable discharge current reported by the BMS.

Note: The BMS type must be set to Custom (Plugin) for this function to take effect. This function can be global and does not have to be called repeatedly.

Required signals: When using a Custom (Plugin) BMS, the pack voltage (configureBmsVolt), discharge current limit (DCL) (configureBmsDisLim), and charge current limit (CCL) (configureBmsChgLim) must all be configured. Their CAN messages must be received at least every 500 ms, otherwise the BMS will be treated as offline. Pack current (configureBmsCurr) and state of charge (configureBmsSoc) are optional.

Arguments:
  • bus: CAN bus to read the message from (CAN1, CAN2, or CAN3)
  • canId: CAN ID containing the DCL signal
  • startBit: start decoding data at this bit of the CAN frame
  • length: number of bits to decode
  • isSigned: decode value as a signed number
  • scale: encoded data scaled by this factor
  • offset: encoded data offset
  • encoding: CAN_MOTOROLA or CAN_INTEL

Example Use Case: Custom BMS integration

configureBmsDisLim(CAN2, 0x304, 0, 16, false, 0.1, 0.0, CAN_INTEL)
configureBmsDisLim(CAN2, 0x304, 0, 16, false, 0.1, 0.0, CAN_INTEL)

configureBmsChgLim(bus, canId, startBit, length, isSigned, scale, offset, encoding)

Configures decoding of the battery charge current limit (CCL) from a CAN message. The decoded value defines the maximum allowable charging current reported by the BMS.

Note: The BMS type must be set to Custom (Plugin) for this function to take effect. This function can be global and does not have to be called repeatedly.

Required signals: When using a Custom (Plugin) BMS, the pack voltage (configureBmsVolt), discharge current limit (DCL) (configureBmsDisLim), and charge current limit (CCL) (configureBmsChgLim) must all be configured. Their CAN messages must be received at least every 500 ms, otherwise the BMS will be treated as offline. Pack current (configureBmsCurr) and state of charge (configureBmsSoc) are optional.

Arguments:
  • bus: CAN bus to read the message from (CAN1, CAN2, or CAN3)
  • canId: CAN ID containing the CCL signal
  • startBit: start decoding data at this bit of the CAN frame
  • length: number of bits to decode
  • isSigned: decode value as a signed number
  • scale: encoded data scaled by this factor
  • offset: encoded data offset
  • encoding: CAN_MOTOROLA or CAN_INTEL

Example Use Case: Custom BMS integration

configureBmsChgLim(CAN2, 0x304, 16, 16, false, 0.1, 0.0, CAN_INTEL)
configureBmsChgLim(CAN2, 0x304, 16, 16, false, 0.1, 0.0, CAN_INTEL)

GPSFixAcquired()(Requires firmware v1.1.7 or later)

Checks if a valid GPS fix has been acquired.

Example Use Case: Custom CAN message implementations.

Returns: True if a valid GPS fix has been acquired, false otherwise.

if GPSFixAcquired() then
  send_GPS_data()
end
if GPSFixAcquired() then
  send_GPS_data()
end

getGPSData(data)(Requires firmware v1.1.7 or later)

Fetches GPS data.

Arguments:
  • data:

    GPS data requested. Supported types:

    • LATITUDE (deg)
    • LONGITUDE (deg)
    • ALTITUDE (m)
    • GROUNDSPEED (km/h)
    • HEADING (deg)
    • TIME (unix timestamp, seconds since epoch)

Example Use Case: Custom CAN message implementations.

Returns: Value of the data requested, nil if no valid GPS fix has been acquired.

local heading = getGPSData(HEADING)
local heading = getGPSData(HEADING)

canEncode(payload, data, startBit, length [, scale [, offset [, encoding]]])

Encodes data into a CAN message payload. The payload table is modified in-place. All parameters after length are optional.

Arguments:
  • payload: CAN message data (byte array to write into)
  • data: Data to encode
  • startBit: start encoding data at this bit of the CAN frame
  • length: number of bits in the CAN frame
  • scale (optional): scale data to be encoded
  • offset (optional): value offset for the encoded data
  • encoding (optional): CAN_MOTOROLA or CAN_INTEL

Example Use Case: Custom CAN message implementations.

local sensorPayload = {0, 0, 0, 0, 0, 0, 0, 0}
local rpm = 2500
canEncode(sensorPayload, rpm, 0, 16, 1.0, 0.0, CAN_INTEL)
sendCAN(CAN2, 0x123, sensorPayload)
local sensorPayload = {0, 0, 0, 0, 0, 0, 0, 0}
local rpm = 2500
canEncode(sensorPayload, rpm, 0, 16, 1.0, 0.0, CAN_INTEL)
sendCAN(CAN2, 0x123, sensorPayload)

canDecode(payload, startBit, length [, isSigned [, scale [, offset [, encoding]]]])

Decodes data from a CAN message. Should be called from a CAN receive callback. All parameters after length are optional.

Arguments:
  • payload: CAN data (byte array)
  • startBit: start decoding data at this bit of the CAN frame
  • length: number of bits to decode
  • isSigned (optional): decode as a signed number
  • scale (optional): encoded data scaled by this factor
  • offset (optional): value offset for the encoded data
  • encoding (optional): CAN_MOTOROLA or CAN_INTEL

Example Use Case: Custom CAN message implementations.

Returns: Decoded value

local rpm = canDecode(canData, 0, 16, false, 1.0, 0.0, CAN_INTEL)
-- Minimal form — unsigned, scale 1.0, offset 0.0, Intel byte order
local soc = canDecode(canData, 0, 8)
local rpm = canDecode(canData, 0, 16, false, 1.0, 0.0, CAN_INTEL)
-- Minimal form — unsigned, scale 1.0, offset 0.0, Intel byte order
local soc = canDecode(canData, 0, 8)

getPrechargeState()(Requires firmware v1.1.8 or later)

Retrieves the current state of the pre-charge sequence.

Example Use Case: Check if the pre-charge sequence is complete before closing contactors.

Returns: A number representing the pre-charge state (e.g., 0 for inactive, 1 for in-progress, 2 for complete).

if getPrechargeState() == 2 then
  -- Precharge is complete, ready to close contactors
  setOut(HS1)
end
if getPrechargeState() == 2 then
  -- Precharge is complete, ready to close contactors
  setOut(HS1)
end

setPrechargeDone()(Requires firmware v1.1.8 or later)

Manually marks the pre-charge sequence as complete.

Example Use Case: Used in custom pre-charge logic where the completion criteria is determined by the plugin.

-- If custom conditions are met, mark precharge as done
-- getSignal() requires firmware 2.0.14+
if getSignal(BatteryVoltage) > 300 then
  setPrechargeDone()
end
-- If custom conditions are met, mark precharge as done
-- getSignal() requires firmware 2.0.14+
if getSignal(BatteryVoltage) > 300 then
  setPrechargeDone()
end

setAnalogOut(channel, voltage)

Sets the output voltage on an analog output channel. The voltage is clamped to the 0-5 V range.

Note: This function is unavailable when:

  • The drive inverter type is set to Tesla Large, Tesla Small, or Tesla 3 Rear (these inverter types require the analog outputs for their own operation).
  • The pedal output type is set to Analog, Tesla S/X, or Tesla 3/Y.

In either case, the call is rejected with an error logged to the plugin console.

Arguments:
  • channel: Analog output channel (AOUT1 or AOUT2).
  • voltage: Output voltage in Volts (0.0-5.0 V). Values outside this range are clamped.

Example Use Case: Driving an analog gauge or an external controller that accepts a 0-5 V signal.

-- Set AOUT1 to 2.5 V
setAnalogOut(AOUT1, 2.5)
-- Set AOUT1 to 2.5 V
setAnalogOut(AOUT1, 2.5)

Constants

The plugin environment provides a set of predefined constants for easy access to pins, channels, and system states.

Output Pins

  • High-Side Outputs: HS1, HS2, HS3, HS4, HS5, HS6, HS7, HS8
  • Low-Side Outputs: LS1, LS2, LS3, LS4, LS5, LS6, LS7, LS8, LS9, LS10, LS11, LS12
  • CAN Keypad LEDs: CAN-KEY1, CAN-KEY2, CAN-KEY3, CAN-KEY4, CAN-KEY5, CAN-KEY6, CAN-KEY7, CAN-KEY8

PWM Channels

  • PWM1, PWM2, PWM3, PWM4, PWM5, PWM6, PWM7
  • Hardware revision v1 only: PWM8, PWM9, PWM10

CAN Buses

  • CAN1 (typically reserved for the inverter)
  • CAN2 (free for general use, but is automatically reserved for the secondary inverter when one is configured)
  • CAN3 (general-purpose bus shared by other VCU features and plugins)

Gear Selection

  • GEAR_P (Park)
  • GEAR_R (Reverse)
  • GEAR_N (Neutral)
  • GEAR_D (Drive)

Drive Modes

  • CHILL (relaxed throttle response, softer acceleration)
  • NORMAL (default balanced response)
  • SPORT (aggressive throttle response, full power)
  • VALET (reduced-power limited mode)

GPS Data Types

  • LATITUDE (latitude in degrees)
  • LONGITUDE (longitude in degrees)
  • ALTITUDE (altitude in meters)
  • GROUNDSPEED (ground speed in km/h)
  • HEADING (heading in degrees)
  • TIME (unix timestamp (seconds since epoch))

Analog Outputs

AOUT1, AOUT2

CAN Encoding

  • CAN_INTEL (little-endian)
  • CAN_MOTOROLA (big-endian)