z3fanatec API (zCube Fanatec Device Extension)

Fanatec Racing Device Capabilities

9 RPM LED (RGB*)
6 Warning LED (RGB*)
Brightness Management
Buttons, Encoders, HAT, Joystick
Central 3xDigits or OLED display
Wheel Motors, Pedal Motors
Wheel Angle (or DOR) Management
*RGB LED supported on Formula rim


Functions

init()
getmodule()
terminate()
getdeviceinfo()
setgear()
setcentralpanel()
setrpmled()
setwarningled() or setflagled()
loadrpmledcolor()
loadwarningledcolor() or loadflagledcolor()
setrgbrpmled()
setrgbwarningled() or setrgbflagled()
resetcolors()
showme() or sendreport()
setglobalbrightness()
setrpmledbrightness()
setwarningledbrightness() or setflagledbrightness()
setwheelangle() or setdor()
loadwheelmotor()
setwheelmotor()
loadpedalmotor()
setpedalmotor()
startinputlistener()
stopinputlistener()
getinputdata()

Initialization

init() function

init() initialize the device extension

fanatec.init()

getmodule() function

getmodule() is used to register the module with zCube core engine (included in zCube runtime app or z3engine DLL)
both functions init() and getmodule() are required to initialize zCube core engine, see the example below.

fanatec.getmodule()

Example To Initialize the Fanatec device (z3fanatec device extension)

-- load core engine and create the global z3e instance, 
-- if not already created by zCube Runtime
if z3e == nil then z3e = require("z3engine") end

-- load device extension and create the fanatec instance
local fanatec = require("z3fanatec")
-- init core
z3e.init()
--  init fanatec device
if fanatec.init() then
	-- register main module
	z3e.mainmodule(fanatec.getmodule())

	-- your script here

	-- end of script
	fanatec.terminate()
end

terminate() function

Use terminate() when you no longer need the extension just before quitting or at the end of script.

-- end of script
fanatec.terminate()

getdeviceinfo(selector) function

get device information and capabilities

-- SELECTOR STRING
-- ---------------------------------------------------------------------------
	--[[ 
"version" 
	]]
	-- return version as string
	-- example:
		local z3dll_vers = fanatec.getdeviceinfo("version")

	--[[ 
"name" 
	]]
	-- return name of extension as string
	-- example:
		local device_name = fanatec.getdeviceinfo("name")

	--[[ 
"type"
	]]
	-- return the class number of the device (see the device type class) 
	-- example:
		local class_num = fanatec.getdeviceinfo("type")
		

	--[[ 
"ticks" 
	]]
	-- return number of ticks elapsed since PC startup
	-- example:
	-- if fanatec.getdeviceinfo("ticks") > old_tick then print("time elapsed!") end

-- DEVICE CAPABILITIES
-- ---------------------------------------------------------------------------
	--[[ 
"panel count"
	]]
	-- return number of digits panels supported by the device 0 or 1 or 2
	-- example:
		local max_panels = fanatec.getdeviceinfo("panel count")

	--[[ 
"digits"
	]]
	-- return the number of digits in each panel 6 or 4 or 3, 0 if not supported
	-- example:
		local num_digits = fanatec.getdeviceinfo("digits")

	--[[ 
"gear" 
	]]
	-- return 1 if central gear digit is present, 0 if not supported
	-- example:
		local isGearPresent = fanatec.getdeviceinfo("gear")

	--[[ 
"max rpm led" 
	]]
	-- return the max number of RPM LED allowed, 0 if not supported
	-- example:
		local max_rpm_leds = fanatec.getdeviceinfo("max rpm led")

	--[[ 
"max warning led"
	]]
	or ["max flag led"]
	-- return the max number of external LED allowed, 0 if not supported
	-- example:
		local max_warn_leds = fanatec.getdeviceinfo("max warning led")

	--[[ 
"max external led" 
	]]
	-- return the max number of external LED allowed, 0 if not supported
	-- example:
		local max_ext_leds = fanatec.getdeviceinfo("max external led")

	--[[ 
"product id" 
	]]
	-- return the max number of external LED allowed, 0 if not supported
	-- example:
		local prod_id = fanatec.getdeviceinfo("product id")

	--[[ 
"vendor id"
	]]
	-- return the max number of external LED allowed, 0 if not supported
	-- example:
		local vend_id = fanatec.getdeviceinfo("vendor id")

	--[[ 
"brightness"
	]]
	-- return the max brightness value of your device, 0 if not supported
	-- example:
		local max_brightness = fanatec.getdeviceinfo("brightness")

	--[[ 
"max wheel angle"
	]]
	or ["max dor"]
	-- return max degrees of rotation (DOR) of your steering wheel (i.e. 900) or 0 if this is unsupported
	-- example:
		local max_dor = fanatec.getdeviceinfo("max wheel angle")

	--[[ 
"min wheel angle"
	]]
	or ["min dor"]
	-- return the minimum degrees of rotation (DOR) of your steering wheel (i.e. 40) or 0 if this is unsupported
	-- example:
		local min_dor = fanatec.getdeviceinfo("min wheel angle")

	--[[ 
"wheel angle"
	]]
	or ["dor"]
	-- return the current degrees of rotation (DOR) of your steering wheel (i.e. 40) or 0 if this is unsupported
	-- example:
		local current_dor = fanatec.getdeviceinfo("wheel angle")

	--[[ 
"analog"
	]]
	-- return number of axes supported or 0
	-- example:
		local ax_count = fanatec.getdeviceinfo("analog")

	--[[ 
"digital"
	]]
	-- return number of buttons supported or 0
	-- example:
		local btn_count = fanatec.getdeviceinfo("digital")

	-- [[
"wheel motor"
	]]
	-- return number of motors supported or 0
	-- example:
		local btn_count = fanatec.getdeviceinfo("wheel motor")

	--[[ 
"pedal motor"
	]]
	-- return number of motors supported or 0
	-- example:
		local btn_count = fanatec.getdeviceinfo("pedal motor")

	--[[ 
"wheel type"
	]]
	-- return the ID of wheel base or 0
	-- example:
		local btn_count = fanatec.getdeviceinfo("wheel type")

	--[[ 
"rim type"
	]]
	-- return the ID of rim or 0
	-- example:
		local btn_count = fanatec.getdeviceinfo("rim type")

	--[[ 
"wheel base"
	]]
	-- return internal name of wheel or nil
	-- example:
		local btn_count = fanatec.getdeviceinfo("wheel base")

	--[[ 
 "revled on base"
	]]
	-- return true if revled is present on wheel base (CSL)
	-- example:
		local btn_count = fanatec.getdeviceinfo("revled on base")

	--[[ 
"oled present"
	]]
	-- return true if OLED display is present
	-- example:
		local btn_count = fanatec.getdeviceinfo( "oled present")

	--[[ 
"rgb led"
	]]
	-- return true if RGB LED is supported
	-- example:
		local btn_count = fanatec.getdeviceinfo( "rgb led")

setgear(string[, boolean]) function

set central gear indicator emulation on central display, the left and right digits are blanked, passing string, char and number is allowed, passing true in optional second parameter display the dot.

fanatec.setgear(2)
-- or
fanatec.setgear("3")
-- show dot
fanatec.setgear(6, true)

setcentralpanel(string) function

set text in central 3x digits panel, use getdeviceinfo(“panel count”) and getdeviceinfo(“digits”) to get the panels capabilities of your device.

-- initilization
if z3e == nil then z3e = require("z3engine") end
z3e.init()
local fanatec = require "z3fanatec"

if fanatec.init() then
	-- register main module
	z3e.mainmodule(fanatec.getmodule())

	-- get the number of digits per panel
	local max_digits = 0
	if fanatec.getdeviceinfo("panel count") == 1 then
		max_digits = fanatec.getdeviceinfo("digits")
	end

	if max_digits==3 then
		fanatec.setleftpanel("888")
	end
	fanatec.showme()
	
	-- wait 2sec
	z3e.sleep(2000)

	-- cleanup panels
	if max_digits==3 then
		fanatec.setrightpanel("   ")
	end
end
fanatec.terminate()

setrpmled(integer, integer) function

toggle the state of one RPM LED, param 1 is the index of the LED and param 2 is the state of LED 0 (OFF) or 1 (ON). Use getdeviceinfo(“max rpm led”) to get the maximum number of RPM led of your device.

if your rim supported RGB color (check with getdeviceinfo(“rgb led”)) and you want to change the color of the LED use loadrpmledcolor() to load the new color before calling setrpmled().

-- initialization
if z3e == nil then z3e = require("z3engine") end
z3e.init()
local fanatec = require "z3fanatec"
if fanatec.init() then
	-- register main module
	z3e.mainmodule(fanatec.getmodule())

	-- get the number of RPM LED
	local max_rpm_leds = fanatec.getdeviceinfo("max rpm led")

	-- loop forward and backward to lit up/down each LED
	fori=1,max_rpm_leds  do
		-- turn on
		fanatec.setrpmled(i,1)
		fanatec.showme()
	   z3e.sleep(50)
	end

	fori=max_rpm_leds,1,-1  do
		-- turn off
		fanatec.setrpmled(i,0)
		fanatec.showme()
	   z3e.sleep(50)
	end
	
end
-- end of script
fanatec.terminate()

setwarningled(integer, integer) function

toggle the state of one warning (marshal) led, param 1 is the index of the LED and param 2 is the state of LED 0 (OFF) or 1 (ON). Use getdeviceinfo(“max warning led”) to get the maximum number of warning/marshal LED of your device.

if your rim supported RGB color (check with getdeviceinfo(“rgb led”)) and you want to change the color of the LED use loadwarningledcolor() to load the new color before calling setwarningled().

if getdeviceinfo("max warning led") >= 6 then 
	fanatec.setwarningled(1,1) -- lit up
	fanatec.setwarningled(6,1) -- lit up
	fanatec.showme()

	z3e.sleep(1000) -- wait 1s
	
	fanatec.setwarningled(1,0) -- lit off
	fanatec.setwarningled(6,0) -- lit off
	fanatec.showme()
end

setflagled(integer, integer) function

same as setwarningled() function

fanatec.setflagled(5,1) -- flad led 5 state ON
fanatec.showme()

loadrpmledcolor(integer, integer) function

change the color of one rpm LED, passing the index of rpm led in param 1 and the index of color in the default colors table (see below the example). This function doesn’t show the LED on rim it only changes the color internally. Use getdeviceinfo(“rgb led”) to check if RGB LED is supported.

local rgbled_allowed = dev.getdeviceinfo("rgb led")
if rgbled_allowed then
	-- color index
	-- 1 RED  
	-- 2 GREEN 
	-- 3 BLUE 
	-- 4 YELLOW
	-- 5 CYAN
	-- 6 MAGENTA
	-- 7 WHITE
	dev.loadrpmledcolor(1,7)
	dev.setrpmled(1,1)
	dev.showme()
end	

loadwarningledcolor(integer, integer) function

change the color of one flag LED, passing the index of flag led in param 1 and the index of color in the default colors table (see below the example). This function doesn’t show the LED on rim it only changes the color internally. Use getdeviceinfo(“rgb led”) to check if RGB LED is supported.

local rgbled_allowed = dev.getdeviceinfo("rgb led")
if rgbled_allowed then
	-- color index
	-- 1 RED  
	-- 2 GREEN 
	-- 3 BLUE 
	-- 4 YELLOW
	-- 5 CYAN
	-- 6 MAGENTA
	-- 7 WHITE
	dev.loadflagledcolor(1,7)
	dev.setflagled(1,1)
	dev.showme()
end	

loadflagledcolor(integer, integer) function

same as loadwarningledcolor() function

-- see the example above

setrgbrpmled(integer led_index, integer r, integer g, integer b) function

lit up one RPM LED with a custom color, param 1 is the index of the LED and param 2 to 4 is the percentage of red , green and blue from 0 to 100 which define the color. Use getdeviceinfo(“rgb led”) to check if your device support RGB LED.

dev.setrgbrpmled(i, 43, 61, 95)	
dev.showme()

setrgbwarningled(integer led_index, integer r, integer g, integer b) function

lit up one Warning/Flag LED with a custom color, param 1 is the index of the LED and param 2 to 4 is the percentage of red , green and blue from 0 to 100 which define the color. Use getdeviceinfo(“rgb led”) to check if your device support RGB LED.

dev.setrgbwarningled(i, 43, 61, 95)	
dev.showme()

setrgbflagled(integer led_index, integer r, integer g, integer b) function

same as setrgbwarningled() function

dev.setrgbflagled(i, 43, 61, 95)	
dev.showme()

showme() function

show the result onto your device (send the usb report)

fori=1,6 do
    fanatec.setwarningled(i,0)
    fanatec.showme()
   z3e.sleep(200)
end

sendreport() function

same as showme() function

fori=1,6 do
    fanatec.setwarningled(i,0)
    fanatec.sendreport()
   z3e.sleep(200)
end

setglobalbrightness(integer) function

set the brightness of all LED (RPM LED and FLAG LED) if supported by the device.

if z3e == nil then z3e = require("z3engine") end
z3e.init()
local dev= require "z3fanatec"
if dev.init() then
    -- register main module
    z3e.mainmodule(dev.getmodule())

    -- brightness
    local max_brightness = dev.getdeviceinfo("brightness")
    if max_brightness >=100 then 
        dev.setglobalbrightness(70)
        dev.setrpmled(1,1)
        dev.setrpmled(4,1)
        dev.setrpmled(7,1)
    end
    z3e.sleep(2000)

    dev.setrpmled(1,0)
    dev.setrpmled(4,0)
    dev.setrpmled(7,0)
    dev.terminate()
end

setrpmledbrightness(integer ledindex, integer value) function

set the brightness of one RPM LED if supported by the device. param 1 is the index of the LED and param 2 is the percentage of brightness.

local max_rpm_leds = dev.getdeviceinfo("max rpm led")
local max_brightness = dev.getdeviceinfo("brightness")
if max_rpm_leds >0 and max_brightness >0 then 
        dev.setrpmbrightness(1, 50)
        dev.setrpmled(1,1)
end

setwarningledbrightness(integer ledindex, integer value) function

set the brightness of one Warning/Flag LED if supported by the device. param 1 is the index of the LED and param 2 is the percentage of brightness.

local max_flag_leds = dev.getdeviceinfo("max warning led")
local max_brightness = dev.getdeviceinfo("brightness")
if max_flag_leds >0 and max_brightness >0 then 
        dev.setflagbrightness(1, 50)
        dev.setflagled(1,1)
end

setflagledbrightness(integer ledindex, integer value) function

same as setwarningbrightness() function

-- see example above

setwheelangle(integer) function

Set the wheel angle or DOR (degrees of rotation maximum) of your steering wheel. Use getdeviceinfo(“max wheel angle”), getdeviceinfo(“min wheel angle”) to get the wheel angle capabilities of your device.


-- ---------------------------------------------------
-- test steering wheel angle
-- this example uses the zCube UI API in zCube runtime to display 
-- a dialog asking the wheel angle to set

local function WheelAngleTest()
	-- test steering wheel angle
	-- degrees of rotation (DOR) or wheel andgle supported?
	-- return nil or 0 if not supported
	local max_rotation = dev.getdeviceinfo("max wheel angle")
	if max_rotation == nil then max_rotation = 0 end
	-- example
	if isZ3Runtime and max_rotation>0 then
		local min_rotation = dev.getdeviceinfo("min wheel angle")
		local current_dor = dev.getdeviceinfo("wheel angle")
		
		local result = z3e.z3settingsbox("create", "Change Wheel Angle (DOR)", 210, 80)
		if result == 0 then     
			-- selector "okbutton" 
			-- params (minimum 2): left, top [, width=50, height=14]
			z3e.z3settingsbox("okbutton", 90, 54)
			-- selector "cancelbutton" 
			-- params (minimum 2): left, top [, width=50, height=14]
			z3e.z3settingsbox("cancelbutton", 145, 54)
			-- edit number with 400 default value
			local id_control1 = z3e.z3settingsbox("editnumber", "Enter Wheel Anngle from "..min_rotation.." to "..max_rotation, 130, 24, min_rotation, max_rotation, current_dor )
			-- selector "domodal" or "showdialog" to show the dialog
			-- params: id of controls 
			-- return = -1 (fist param) if canceled and fill all params if OK
			local param1 = z3e.z3settingsbox("domodal", id_control1)
			--  result
			-- before apply, check result, is value changed and if in correct range
			local dor = param1+0
			if dor ~= 0 and dor ~= current_dor and dor>=min_rotation and dor<=max_rotation then
				dev.setdor(dor)
			end
		end     
	end
end

WheelAngleTest()

loadwheelmotor(integer, integer) function

setup a motor of the wheel, param 1 is the index of the motor 1 or 2, param 2 is the power value from 0 to 100. call setwheelmotor() to activate the motor. Use getdeviceinfo(“wheel motor”) to check the capabilities of your device.

local function WheelMotorsTest()	
	-- Alternate small & big motor vibration
	if (max_wheel_motors ==2 ) then
		print("Wheel Motors")
		if max_digits==3 and panel_count == 1 then dev.setcentralpanel("PWR") dev.showme() end 
		 z3e.sleep(100);
		for i = 1, 10 do 
			if (i % 2)==0 then
				dev.loadwheelmotor(1,100)
				dev.loadwheelmotor(2,0)
				dev.setwheelmotor()
				if max_digits==3 and panel_count == 1 then dev.setcentralpanel(" M1") dev.showme() end 
			 else
				dev.loadwheelmotor(1,0)
				dev.loadwheelmotor(2,100)
			   dev.setwheelmotor()
				if max_digits==3 and panel_count == 1 then dev.setcentralpanel(" M2") dev.showme() end 
			 end
			
			z3e.sleep(500);
		end
		dev.loadwheelmotor(1,0)
		dev.loadwheelmotor(2,0)
		dev.setwheelmotor()
	end
end

WheelMotorsTest()

setwheelmotor() function

start the motor of the wheel. call loadwheelmotor() function to setup the motor before calling this function.

dev.loadwheelmotor(2,100)
dev.setwheelmotor()

loadpedalmotor(integer, integer) function

setup a motor of the CSP Pedals Set V3, param 1 is the index of the motor 1 or 2 (throttle, brake), param 2 is the power value from 0 to 100. call startcspmotor() to activate the motor. Use getdeviceinfo(“csp motor”) to check the capabilities of your device.

local function CSPMotorsTest()
	-- CSP Motors Alternate small & big motor vibration
	if (max_pedal_motors >=1 ) then
		print("Pedal Motors")
		if max_digits==3 and panel_count == 1 then dev.setcentralpanel("CSP") dev.showme() end 
		 z3e.sleep(100);
		for i = 1, 10 do 
		   if (i % 2)==0 then
				dev.loadpedalmotor(1,100)
				if (max_pedal_motors ==2 ) then dev.loadpedalmotor(2,0) end
				dev.setpedalmotor()
				if max_digits==3 and panel_count == 1 then dev.setcentralpanel(" M1") dev.showme() end 					
			else
				dev.loadpedalmotor(1,0)
				if (max_pedal_motors ==2 ) then dev.loadpedalmotor(2,100) end
				dev.setpedalmotor()
				if max_digits==3 and panel_count == 1 then dev.setcentralpanel(" M2") dev.showme() end 
			end
			z3e.sleep(500);
		end
		dev.loadpedalmotor(1,0)
		if (max_pedal_motors ==2 ) then dev.setpedalmotor(2,0) end
		dev.setpedalmotor()
	end
end

setpedalmotor()

start or stop the motor of the pedal. call loadpedalmotor() function to setup the motor before calling this function.

dev.loadpedalmotor(2,100)
dev.setpedalmotor()

startinputlistener() function

start the control input (button or axis) detection of your device. see getinputdata() example below for more info.

fanatec.startinputlistener()

stopinputlistener() function

stop the detection of control input (button or axis). see getinputdata() example below for more info.

fanatec.stopinputlistener()

deviceType, ctrlType, ctrlIndex, value, isNew = getinputdata() function

get button pressed or axis value information.

Return

integer device type (return unique ID of the device)
integer input type button (1) or axis / switch (0)
integer index of button pressed or axis triggered
integer value is the current value of axis or button down >0; button up = 0
boolean isNew is true if this is a new input event

use startinputlistener() function before polling inputs state with getinputdata() see the example below.

-- getinputdata() function example 

-- getting button pressed while LED
-- and digits are updated on the display

if z3e == nil then z3e = require("z3engine") end
-- load device extension
local dev= require("z3fanatec")
-- init zCube engine
z3e.init()
if dev.init() == true then
	-- register main module
	z3e.mainmodule(dev.getmodule())

	local max_rpm_leds=dev.getdeviceinfo("max rpm led")
	co = coroutine.create( function ()
		for i = 0, max_rpm_leds do 
		--	print("LED:", i)
			 dev.setrpmled(i, 1) -- lit up led
			 dev.sendreport()
			z3e.sleep(250)
			coroutine.yield()
		end

		for i = 0, max_rpm_leds do 
		--	print("LED:", max_rpm_leds-i)
			dev.setrpmled(max_rpm_leds-i, 0)
			dev.sendreport()
			z3e.sleep(250)
			coroutine.yield()
		end
		isRunning  = false
	end)

	isRunning  = true

	-- start the listener
	dev.startinputlistener()

	-- main loop
	while isRunning and z3e.getinfo("is running") do
		-- call main function
		coroutine.resume(co)
		
		-- get buttons, switches, axes status
		deviceType, ctrlType, ctrlIndex, value, isNew = dev.getinputdata()
		-- is it a new input control report
		if isNew then
			print(deviceType, ctrlType, ctrlIndex, value, isNew)
			if ctrlType == 1 and ctrlIndex==2 then
				-- lit up warning LED 1 if button 2 is pressed
				dev.setwarningled(1,value)
				dev.sendreport()
			end
		end
		z3e.sleep(1)
	end
end
-- end of script
-- we do not need to call stopinputlistener() here 
-- as terminate() function call it internally.
dev.terminate()

zCube Testing Fanatec Device – Full Example

-- #####################################################################

-- # sample script to test Fanatec devices 
-- # for zCUBE Engine Lua extension module 
-- # Copyright (c)2022 by Zappadoc - All Rights Reserved.
-- # https://www.eksimracing.org

-- This source code, module, lib and all information, data, and algorithms
-- associated with it are subject to change without notice.
                                                                     
-- Change history: 
				-- 2022.01.30: created - zappadoc                                        
-- License
-- https://zappadoc.com/terms-conditions

-- DISCLAIMER:
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-- OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-- OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
-- IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-- All product names are trademarks or registered trademarks of their respective holders.

-- #####################################################################

package.path  = package.path..";.\\lua\\?.lua;.\\lua\\scripts\\?.lua;.\\lua\\socket\\?.lua;.\\lua\\mime\\?.lua;.\\lua\\jit\\?.lua"
package.cpath = package.cpath..";.\\lua\\?.dl;.\\lua\\scripts\\?.dlll"

function full_test_device()
    if z3e == nil then z3e = require("z3engine") end
    z3e.init()
    -- replace z3fanatec extension to your display device
    local dev = require("z3fanatec")

    if dev.init() then
        -- register main module
        z3e.mainmodule(dev.getmodule())

        print("Start Testing...")

        dev.startinputlistener()
        -- ---------------------------------------------------
        -- constants
        local z3vers = z3e.getinfo("version")

        -- is UI functions present? 
        local z3api = z3e.getinfo("engine")
        isZ3Runtime = false
        if z3api == "extended" then isZ3Runtime = true end
        
        -- device capabilities
        local dev_name = dev.getdeviceinfo("name")

        local dev_vendid = dev.getdeviceinfo("vendor id")
        local dev_prodid = dev.getdeviceinfo("product id")

        -- fw
        local fw = dev.getdeviceinfo("firmware")
        
        -- inputs
        local btns = dev.getdeviceinfo("digital")
        local sw = dev.getdeviceinfo("analog")

        -- LED
        local max_rpm_leds = dev.getdeviceinfo("max rpm led")
        local max_warn_leds = dev.getdeviceinfo("max warning led")
        local max_ext_leds = dev.getdeviceinfo("max external led")
        local rgbled_allowed = dev.getdeviceinfo("rgb led")

        -- brigthness
        local max_brightness = dev.getdeviceinfo("brightness")

		-- -------------------------------------------------------------
        -- wheel motors
        local max_wheel_motors = dev.getdeviceinfo("wheel motor")

        -- pedal motors CSP V3
        local max_pedal_motors = dev.getdeviceinfo("pedal motor")
		
		-- wheel type id
        local wheel_type = dev.getdeviceinfo("wheel type")
		
		-- rim type id
        local rim_type = dev.getdeviceinfo("rim type")
		
		-- base name
        local base_name = dev.getdeviceinfo("wheel base")
		
		-- revled detected on base true/false
        local base_name = dev.getdeviceinfo("revled on base")
		
		-- OLED display present? true/false
        local base_name = dev.getdeviceinfo("oled present")
        
        -- gear and panels
        local isGearPresent = dev.getdeviceinfo("gear")
        local panel_count = dev.getdeviceinfo("panel count")
        local max_digits = 0
        if panel_count >= 1 then max_digits = dev.getdeviceinfo("digits") end

        -- ---------------------------------------------------
        -- Local functions
		
		local function WheelAngleTest()
			-- test steering wheel angle
			-- degrees of rotation (DOR) or wheel andgle supported?
			-- return nil or 0 if not supported
			local max_rotation = dev.getdeviceinfo("max wheel angle")
			if max_rotation == nil then max_rotation = 0 end
			-- example
			if isZ3Runtime and max_rotation>0 then
				local min_rotation = dev.getdeviceinfo("min wheel angle")
				local current_dor = dev.getdeviceinfo("wheel angle")
				
				local result = z3e.z3settingsbox("create", "Change Wheel Angle (DOR)", 210, 80)
				if result == 0 then     
					-- selector "okbutton" 
					-- params (minimum 2): left, top [, width=50, height=14]
					z3e.z3settingsbox("okbutton", 90, 54)
					-- selector "cancelbutton" 
					-- params (minimum 2): left, top [, width=50, height=14]
					z3e.z3settingsbox("cancelbutton", 145, 54)
					-- edit number with 400 default value
					local id_control1 = z3e.z3settingsbox("editnumber", "Enter Wheel Anngle from "..min_rotation.." to "..max_rotation, 130, 24, min_rotation, max_rotation, current_dor )
					-- selector "domodal" or "showdialog" to show the dialog
					-- params: id of controls 
					-- return = -1 (fist param) if canceled and fill all params if OK
					local param1 = z3e.z3settingsbox("domodal", id_control1)
					--  result
					-- before apply, check result, is value changed and if in correct range
					local dor = param1+0
					if dor ~= 0 and dor ~= current_dor and dor>=min_rotation and dor<=max_rotation then
						dev.setdor(dor)
					end
				end     
			end
		end
		
        local function checkCharsTable()
            if isGearPresent > 0 then
                for i=1,254 do
                    dev.setgear(string.char(i))
                    dev.showme()
                    print(i)
                    os.execute("pause") -- work best with Windows console not in VS Code
                end
            end
        end
        -- checkCharsTable() -- explore the chars table of your device

        -- toggle LED functions
        local function toggleLeds(type, state)
            if max_rpm_leds>0 and type == 1 then
                for i=1,max_rpm_leds do
                    dev.setrpmled(i, state)
                end
            end
            if max_warn_leds>0 and type == 2 then
                for i=1,max_warn_leds do
                    dev.setwarningled(i, state)
                end
            end
            if max_ext_leds>0 and type == 3 then
                for i=1,max_ext_leds do
                    dev.setexternalled(i, state)
                end
            end
        end
        -- toggle RPM LED
        local function toggleRPMLeds(state)
            toggleLeds(1, state)
        end

        -- toggle Warning/Flag LED
        local function toggleWarnLeds(state)
            toggleLeds(2, state)
        end

        -- toggle Warning/Flag LED
        local function toggleFlagLeds(state)
            toggleLeds(2, state)
        end

        -- toggle external LED
        local function toggleExtLeds(state)
            toggleLeds(3, state)
        end

        -- toggle external LED
        local function toggleAllLeds(state)
            toggleRPMLeds(0)
            toggleWarnLeds(0)
            toggleExtLeds(0)
        end

        -- empty gear, digits and turn off LED
        local function clearDisplay()
            if isGearPresent > 0 then dev.setgear(" ",false) end
            if max_digits==3 and panel_count == 1 then dev.setcentralpanel("   ") end
            if max_digits==4 then dev.setleftpanel("    ")   dev.setrightpanel("    ") end
            if max_digits==6 then dev.setleftpanel("      ") dev.setrightpanel("      ") end
            dev.showme()
			toggleAllLeds(0)
			dev.showme()
        end

		local function RPMLEDTest()	
			print("RPM LED Test...")
			for i=1,max_rpm_leds do
				dev.setrpmled(i,1)
				if isGearPresent > 0 then
					if(i>9) then
						dev.setgear(i-10,true)
					else
						dev.setgear(i)
					end
				end
				dev.showme()
				z3e.sleep(200)
			end
			for i=max_rpm_leds,1,-1 do
				dev.setrpmled(i,0)
				if isGearPresent > 0 then
					if(i>9) then
						dev.setgear(i-10,true)
					else
						dev.setgear(i)
					end
				end
			dev.showme()
				z3e.sleep(200)
			end
			
			-- turn on RPM LED
			toggleRPMLeds(1)
			 dev.showme()
			z3e.sleep(100)
		end

		local function PanelsTest()	
			-- init panels
			if max_digits==3 then
				dev.setcentralpanel("888")
			elseif max_digits==4 then
				dev.setleftpanel("8888")
				dev.setrightpanel("-Z3-")
			elseif max_digits==6 then
				dev.setleftpanel("888888")
				dev.setrightpanel(" ZCubE")
			end
			dev.showme()
			z3e.sleep(250)
		end


		local function BrightnessTest()	
			-- brightness stuff
			if max_brightness>0 then
				dev.setbrightness(max_brightness)
				if isGearPresent > 0 then
					dev.setgear("_",true)
					dev.showme()
				end
				-- decrease
				print("Decrease Brightness...")
				for i=max_brightness,1,-1 do
					if max_digits==3 and panel_count == 1 then dev.setcentralpanel(string.format("%03d",i)) dev.showme() 
					elseif max_digits==4 then dev.setleftpanel(string.format("%03d ",i)) dev.setrightpanel("DOWN") dev.showme() 
					elseif max_digits==6 then dev.setleftpanel(string.format("  %03d ",i)) dev.setrightpanel("888888") dev.showme()
					else print("Decrease Brightness..."..i) end
					dev.setbrightness(i)
					toggleRPMLeds(1)
					dev.showme()
					z3e.sleep(10)
				end
				z3e.sleep(1000)

				if isGearPresent > 0 then
					if string.find(dev_name, "z3sli", 1) ~= nil then dev.setgear("\126",true)  -- bodnar racing display only
					else dev.setgear("-",true) end
					dev.showme()
				end

				-- increase
				print("Increase Brightness...")
				for i=1,max_brightness do
					if max_digits==3 and panel_count == 1 then dev.setcentralpanel(string.format("%03d",i)) dev.showme() 
					elseif max_digits==4 then dev.setleftpanel(string.format("%03d ",i)) dev.setrightpanel(" UP ") dev.showme() 
					elseif max_digits==6 then dev.setleftpanel(string.format("  %03d ",i)) dev.setrightpanel("--UP--") dev.showme()
					else print("Decrease Brightness..."..i) end
					dev.setbrightness(i)
					toggleRPMLeds(1)
					dev.showme()
					z3e.sleep(10)
				end

				if string.find(dev_name, "z3sli", 1) ~= nil then 
					dev.setbrightness(200)
				else dev.setbrightness(100) end
				toggleRPMLeds(1)
				dev.showme()
	 
				clearDisplay()
			end
		end
		
		local function FlagLEDTest()	
			-- warning/marshal LED
			if max_warn_leds > 0 then
				print("Warning LED...")
				local mwl = (max_warn_leds / 2)
				for i=1,mwl do
					dev.setwarningled(i, 1)
					dev.setwarningled((max_warn_leds+1)-i, 1)
					dev.showme()
					z3e.sleep(500)
				end
				for i=1,mwl do
					dev.setwarningled((mwl+1)-i, 0)
					dev.setwarningled(mwl+i, 0)
					dev.showme()
					z3e.sleep(500)
				end
			end
		end

		local function ExternalLEDTest()	
			-- external LED
			if max_ext_leds > 0 then
				print("External LED...")
				for i=1,max_ext_leds do
					dev.setexternalled(i, 1)
					dev.showme()
					z3e.sleep(400)
				end
				for i=max_ext_leds,1,-1 do
					dev.setexternalled(i, 0)
					dev.showme()
					z3e.sleep(400)
				end
			end
		
			-- turn off ALL LED
			toggleAllLeds(0)
			dev.showme()
		end
		
		local function WheelMotorsTest()	
			-- Alternate small & big motor vibration
			if (max_wheel_motors ==2 ) then
				print("Wheel Motors")
				if max_digits==3 and panel_count == 1 then dev.setcentralpanel("PWR") dev.showme() end 
				 z3e.sleep(100);
				for i = 1, 10 do 
					if (i % 2)==0 then
						dev.setwheelmotor(1,100)
						dev.setwheelmotor(2,0)
						dev.startwheelmotor()
						if max_digits==3 and panel_count == 1 then dev.setcentralpanel(" M1") dev.showme() end 
					 else
						dev.setwheelmotor(1,0)
						dev.setwheelmotor(2,100)
					   dev.startwheelmotor()
						if max_digits==3 and panel_count == 1 then dev.setcentralpanel(" M2") dev.showme() end 
					 end
					
					z3e.sleep(500);
				end
				dev.setwheelmotor(1,0)
				dev.setwheelmotor(2,0)
				dev.startwheelmotor()
			end
		end

		local function CSPMotorsTest()
			-- CSP Motors Alternate small & big motor vibration
			if (max_pedal_motors >=1 ) then
				print("Pedal Motors")
				if max_digits==3 and panel_count == 1 then dev.setcentralpanel("CSP") dev.showme() end 
				 z3e.sleep(100);
				for i = 1, 10 do 
				   if (i % 2)==0 then
						dev.setpedalmotor(1,100)
						if (max_pedal_motors ==2 ) then dev.setpedalmotor(2,0) end
						dev.startpedalmotor()
						if max_digits==3 and panel_count == 1 then dev.setcentralpanel(" M1") dev.showme() end 					
					else
						dev.setpedalmotor(1,0)
						if (max_pedal_motors ==2 ) then dev.setpedalmotor(2,100) end
						dev.startpedalmotor()
						if max_digits==3 and panel_count == 1 then dev.setcentralpanel(" M2") dev.showme() end 
					end
					z3e.sleep(500);
				end
				dev.setpedalmotor(1,0)
				if (max_pedal_motors ==2 ) then dev.setpedalmotor(2,0) end
				dev.startpedalmotor()
			end
		end
		
		local function RPMFlagColorTest()
			if rgbled_allowed then
				-- color index
				-- 1 RED  
				-- 2 GREEN 
				-- 3 BLUE 
				-- 4 YELLOW
				-- 5 CYAN
				-- 6 MAGENTA
				-- 7 WHITE
				print("RPM & FLAG RGB LED Test Indexed Colors From 1 to 7...")
				for c=1,7 do
					if max_digits==3 and panel_count == 1 then 
						dev.setcentralpanel(string.format("%03d",c))
						dev.showme() 
					elseif max_digits==4 then dev.setleftpanel(string.format("%03d ",c)) dev.setrightpanel(" RGB") dev.showme() 
					elseif max_digits==6 then dev.setleftpanel(string.format("  %03d ",c)) dev.setrightpanel("  rGb ") dev.showme()
					end
					for i=1,max_rpm_leds do
						dev.loadrpmledcolor(i,c)				
					end
					for i=1,max_warn_leds do
						dev.loadflagledcolor(i,c)				
					end
					toggleRPMLeds(1)
					toggleWarnLeds(1)
					dev.showme()
					z3e.sleep(150)
					toggleRPMLeds(0)
					toggleWarnLeds(0)
					dev.showme()
					z3e.sleep(25)
				end
			end
		end
			
		local function FlagRGBLEDTest()
			if rgbled_allowed then
				print("FLAG RGB LED Test Percentage R,G,B Color...")
				for c=1,100 do
					if max_digits==3 and panel_count == 1 then 
						if (c%2==0) then 
							dev.setcentralpanel("RGB") 
						else
							dev.setcentralpanel(string.format("%03d",c))
						end 
						dev.showme() 
					elseif max_digits==4 then dev.setleftpanel(string.format("%03d ",c)) dev.setrightpanel("FLAG") dev.showme() 
					elseif max_digits==6 then dev.setleftpanel(string.format("  %03d ",c)) dev.setrightpanel(" FLAG ") dev.showme()
					end
					for i=1,max_warn_leds do
						dev.setrgbwarningled(i, 100-c, ((c%2==0) and c or (100-c)), c)				
					end  
					dev.showme()
					z3e.sleep(40)
				end
			end
		end
			
		-- ---------------------------------------------------
		-- TESTING DEVICE
		
		WheelAngleTest()
		
        -- init brightness
        if max_brightness >=100 then 
            if string.find(dev_name, "z3sli", 1) ~= nil then 
                dev.setbrightness(200)
            else dev.setbrightness(100) end
        end
        -- init gear
        if isGearPresent > 0 then
            dev.setgear("-")
            dev.showme()
        end
		
		RPMLEDTest()
		PanelsTest()
		BrightnessTest()
		FlagLEDTest()
		ExternalLEDTest()
		WheelMotorsTest()	
		CSPMotorsTest()
		RPMFlagColorTest()
		FlagRGBLEDTest()
		
		-- restore to default colors 
		dev.resetcolors()
		
		-- clear display and LED
		clearDisplay()
		
    else
        print("Device Error!\n- device not connected (connect your device)\n- or license not found (register your device)\n- or the default device (SLIPRO) is not found (change the script with the correct zCube extension!")
    end

    -- notify device extension
    dev.terminate()

    print("Test Done!")
end

Minimal Script

-- minimal script for Fanatec device

if z3e == nil then local z3e = require(“z3engine”) end
local dev = require("z3fanatec")

-- init core
z3e.init()

-- init device
if dev.init() then
	 -- register main module
	 z3e.mainmodule(dev.getmodule())

	 -- your custom script here

	 dev.terminate()
end

Minimal Event Driven Script (zCube Runtime)

-- minimal script for Fanatec device

if z3e == nil then local z3e = require(“z3engine”) end
local dev = require("z3fanatec")

-- init core
z3e.init()

local isRunning = false


function z3open()
	-- z3open even at startup
	-- init device
	if dev.init() then
		-- register main module
		z3e.mainmodule(dev.getmodule())
		isRunning = true

		-- your custom script here
		
		
	else
		print("Device Error!\n- device not connected (connect your device)\n- or license not found (register your device)!")
	end
end

function z3loop()
	-- your main loop
	if isRunning then
	
		-- your custom script here
	
	end
end
		
function z3close()
	-- z3close event when app quit
	isRunning = false
	
	 -- your custom script here

	 dev.terminate()
end