z3thrustmaster API (zCube Thrustmaster Device Extension)

Thrustmaster F488 challenge Add On USB Device Capabilities

15 RPM LED
Buttons Mapping


Functions

init()
getmodule()
terminate()
getdeviceinfo()
setrpmled()
showme() or sendreport()
setbrightness()
setwheelangle()
startinputlistener()
stopinputlistener()
getinputdata()

Initialization

init() function

init() initialize the device extension

thrustmaster.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.

thrustmaster.getmodule()

Example To Initialize the Thrustmaster device (z3thrustmaster 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 thrustmaster instance
local thrustmaster = require("z3thrustmaster")
-- init core
z3e.init()
--  init thrustmaster device
if thrustmaster.init() then
	-- register main module
	z3e.mainmodule(thrustmaster.getmodule())

	-- your script here

	-- end of script
	thrustmaster.terminate()
end

terminate() function

Use terminate() when you no longer need the extension just before quitting or at the end of script.
This function closes the win32 COM library, stop listening inputs and unloads all services loaded by the extension if needed. It is mandatory to call it with z3thrustmaster device extension to cleanup COM library.

-- end of script
thrustmaster.terminate()

getdeviceinfo(selector) function

get device information and capabilities

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

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

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

	--[[ 
"ticks" 
	]]
	-- return number of ticks elapsed since PC startup
	-- example:
	-- if thrustmaster.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 = thrustmaster.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 = thrustmaster.getdeviceinfo("digits")

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

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

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

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

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

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

	--[[ 
"brightness"
	]]
	-- return the max brightness value of your device, 0 if not supported
	-- example:
		local max_brightness = thrustmaster.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 = thrustmaster.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 = thrustmaster.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 = thrustmaster.getdeviceinfo("wheel angle")

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

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

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.

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

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

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

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

showme() function

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

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

sendreport() function

same as showme() function

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

setbrightness(integer) function

set the britghtness of your device (LED and Digit)

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

    -- brightness
    local max_brightness = thrustmaster.getdeviceinfo("brightness")
    if max_brightness >=100 then 
        thrustmaster.setbrightness(100) end
    end
end

setwheelangle(integer) function

set the wheel angle or DOR of your steering wheel (degrees of rotation maximum). 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 of the z3 runtime to display 
-- a dialog asking the wheel angle to set

-- degrees of rotation (DOR) or wheel andgle supported?
-- return maxt wheel angle otherwise nil or 0 if not supported
local max_rotation = dev.getdeviceinfo("max wheel angle")
if max_rotation == nil then max_rotation = 0 end

if isZ3Runtime and max_rotation>0 then
	-- get the min wheel angle value
	local min_rotation = dev.getdeviceinfo("min wheel angle")
	-- get current wheel angle value
	local current_dor = dev.getdeviceinfo("wheel angle")
	
	-- setup the dialog
	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
		-- convert string to number
		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

startinputlistener() function

start the control input (button or axis) detection of your device.

thrustmaster.startinputlistener()

stopinputlistener() function

stop the detection of control input (button or axis).

thrustmaster.stopinputlistener()

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

get button pressed or axis value information.

Return

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

use startinputlistener() function before polling inputs state with getinputdata()

-- getinputdata example 
-- getting button pressed while LED
-- and digits are updated on the display
if z3e == nil then z3e = require("z3engine") end
-- device
local thrustmaster = require("z3thrustmaster")

-- init z³ engine
z3e.init()

if thrustmaster.init() == true then
	-- register main module
	z3e.mainmodule(thrustmaster.getmodule())
	local max_rpm_leds=thrustmaster.getdeviceinfo("max rpm led")

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

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

	isRunning  = true
	thrustmaster.startinputlistener()
	-- main loop
	while isRunning and z3e.getinfo("is running") do
		-- call main function
		coroutine.resume(co)
		
		-- get buttons, switches, axes status
		deviceType, ctrlType, ctrlPos, value, isNew = thrustmaster.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
				thrustmaster.setwarningled(1,value)
				thrustmaster.sendreport()
			end
		end
		z3e.sleep(1)
	end
end
-- end of script
thrustmaster.terminate()

zCube Thrustmaster Full Example

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

-- # sample script to test device 
-- # 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://www.eksimracing.org/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.

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

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

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")

    -- 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")

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

    -- gear and panels
    local isGearPresent = dev.getdeviceinfo("gear")
    local max_digits = 0
    if dev.getdeviceinfo("panel count") >= 1 then max_digits = dev.getdeviceinfo("digits") end

    -- ---------------------------------------------------
    -- 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 set the rotation to 400°
     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

    -- ---------------------------------------------------
    -- Local functions
    --
    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.setrpmled(i, state)
            end
        end
        if max_ext_leds>0 and type == 3 then
            for i=1,max_ext_leds do
                dev.setrpmled(i, state)
            end
        end
        dev.showme()
    end
    -- toggle RPM LED
    local function toggleRPMLeds(state)
        toggleLeds(1, state)
    end

    -- toggle Warning LED
    local function toggleWarnLeds(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 then dev.setcentralpanel("   ") end
        if max_digits==4 then dev.setleftpanel("    ")   dev.setrightpanel("    ") end
        if max_digits==6 then dev.setleftpanel("      ") dev.setrightpanel("      ") end
        toggleAllLeds(0)
        dev.showme()
     end

     -- ---------------------------------------------------
    -- 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

    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)
    z3e.sleep(100)

    -- 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)

    -- 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 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)
            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 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)
            z3e.sleep(10)
        end

        if string.find(dev_name, "z3sli", 1) ~= nil then 
            dev.setbrightness(200)
        else dev.setbrightness(100) end

        clearDisplay()
    end

    -- 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(400)
        end
        for i=1,mwl do
            dev.setwarningled((mwl+1)-i, 0)
            dev.setwarningled(mwl+i, 0)
            dev.showme()
            z3e.sleep(400)
        end
    end

    -- 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)

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!")

Minimal Script

-- minimal script for Thrustmaster F488 Chalenge Ed.

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

-- 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 Thrustmaster F488 Challenge Edition device

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

-- 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