Home ← STEM HQ ← TI-Nspire Authoring ← TI-Nspire Scripting HQ ← Scripting Tutorial - Lesson 41
Scripting Tutorial - Lesson 41: Lua Scripting and the TI Innovator™ Hub:
1. SENDING to the Hub
TI Innovator™ Hub Lesson 0: Using Lua to store and edit TI-BASIC programs for the Innovator™ Hub (Lua Scripting Lesson 40)
TI Innovator™ Hub Lesson 1: SENDING to the Hub: Controlling the Action (Lua Scripting Lesson 41)
TI Innovator™ Hub Lesson 2: READING from the Hub: Real world interaction and data (Lua Scripting Lesson 42)
TI Innovator™ Hub Lesson 3: Exploring the OneShotTimer: Learning to Wait! (Lua Scripting Lesson 43)
TI Innovator™ Hub Lesson 4: Making Music with the Innovator™ Hub (Lua Scripting Lesson 44)
TI Innovator™ Hub Lesson 5: Build your own Innovator™ Hub Robot (Lua Scripting Lesson 45)
TI Innovator™ Hub Lesson 6: Create a general Innovator™ Hub Document (Lua Scripting Lesson 46)
TI Innovator™ Hub Lesson 7: Create a General Innovator™ Hub and BLE Document (Lua Scripting Lesson 47)
Download supporting file for this tutorial
Texas Instruments TI-Nspire Scripting Support Page
Welcome to the TI Innovator™ Hub
The TI Innovator™ Hub is the centerpiece of the TI-Innovator™ System, a project kit that extends the functionality of Texas Instruments (TI) graphing calculators to make coding and engineering design accessible to students in the classroom. Plug-and-play and ready-to-use with TI graphing calculators, the TI-Innovator™ Hub enables students to learn basic coding and design, use those skills to program and build working solutions, and connect science, technology, engineering and math (STEM) concepts.
The Hub itself is based around a TI MSP432 LaunchPad board, which carries on it a complex series of scripts (called 'sketches') which translate commands from the user into Hub actions and responses. To learn more about programming such boards, feel free to explore the LaunchPad tutorials on this site.
More importantly, the Innovator Hub can be programmed using TI-BASIC, the simple and readily accessible programming language available on TI handhelds (both TI-Nspire and TI-84 series) and software. Students use simple commands and programs to engage with and control the Hub, bringing together all the key elements of STEM and more.
Which raises the question: why use Lua with the Innovator Hub?
The main advantage of Lua for use with the Hub will obviously lie in the creation of interactive documents for teaching and learning. Lua and the TI-Nspire are now well-recognised as a powerful partnership for authoring, and the Innovator Hub adds significantly to the opportunities for active and appropriate student engagement across a wide range of curriculum topics, particularly in relation to STEM. For example, consider the activity shown in the animated GIF opposite, in which the Innovator brightness sensor is used to determine the growth rate of plants under different light conditions. Or have students learn the real principles of acceleration controlling a Lunar Lander!
While TI-BASIC offers a great platform for simple interactions and programs, a limitation exists which becomes obvious when seeking to collect data samples: while such samples can be readily collected, you must wait until the entire program has run before you will see the results on your handheld or computer. So a graph of light intensity, for example, will not update until all samples have been collected. This is not a limitation when using Lua on desktop or handheld.
Interacting with the Innovator Hub, using either TI-BASIC of Lua, is mostly centred around two key activities: SETTing and READing.
SENDing lies at the heart of all interactions with the Hub: whether from a TI-BASIC program or from a Lua script, commands are sent to the Hub via USB connection (also called Asynchronous Interface or ASI). Such commands must be in the right format and syntax, and a well-authored Lua script can take care of much of the uncertainty that students may face in this regard.
SET commands will involve on-board functionality, such as the in-built LEDs and speaker, or attached devices such as lights, relays, servos, and more. These will tend to be either digital (ON/OFF) or analog, and so commands will often be of the form "SET ... ON" or "SET ... OFF".
READing occurs when the Hub sends a response back to the sent request. this may be as simple as letting the device know that the hub is ready, but will commonly involve the transmission of data from Hub or attached peripheral (sensors, servo motors, LEDs, relays, and more). Such data may be in the form of a single numerical value (for example, brightness or distance), or could be a list of values - for example, a 3-axis accelerometer will send a list of three values for x, y and z axes, while a barometer will commonly send air pressure, temperature, altitude and more.
The focus of this first lesson is on the TI Innovator SEND command, and you will learn how to send commands to the Hub, and to make certain simple things happen on board.
Scripting for the Innovator Hub: Lesson 1: SENDING to the Hub
The heavy lifting in the scripts in this section was all achieved by Fred Fotsch - thank you Fred!
Every effort has been made to make this script as simple as possible, so there is very little in the way of layout and design - no buttons, sliders, or anything fancy. However, you will see that the Innovator functions are, in fact, far from simple, but this should not deter you from trying some of your own scripting ideas.
If you have previously worked through some or all of the Lua BLE lessons, you will understand that much of the hard work has been done, and may be readily accomplished using copy and paste. Focus on the general principles and those sections specified for the user to interact with the script.
To help with this, once again, I have broken the script down into main sections, which may be viewed individually (or viewed as a whole, and copied and pasted to make a full working document).
First notice that we are now using APIlevel 2.7!! Specifically designed around the Innovator Hub.
You might also see a simple function called 'addMsg', which is used to print information to the screen as well as to the script editor console. In your future scripts you probably won't need your users to see the details of connection and activity, but for our purposes here it is quite instructive to follow the process as it occurs "under the hood".
Look closely at the three highlighted lines at the start of on.construction: these make the magic happen!
You will see that the menu is used to save a lot of layout and screen display, and makes it easy to include a range of examples for you to explore. The default action for this document is to turn the red LED ("LIGHT") on and off by simply pressing down and up on the screen.
Most of the commands are of the same form: "SET ... ON" or "SET ... OFF". So "SET LIGHT ON" is the default and is easily changed to, for example, "SET COLOR.BLUE ON".
The in-built speaker is introduced here: "SET SOUND 220 5" plays an A tone for 5 seconds.
The remaining menu items introduce external peripherals: LED, speaker, servo motor and two generic ones (very useful indeed!): ANALOG.OUT and DIGITAL.OUT. These last two may be used for any appropriate peripheral, and when used with the breadboard connectors (BB1 to BB10) give the Innovator Hub enormous flexibility.
Note the servo: The in-built functions are simple, but when using external connections, then these must first be CONNECTed to the appropriate port: for a servo, we generally use OUT3, which supplies 5V (OUT1 and OUT2 are 3.3V ports). So choosing this menu option first sends a command to the Hub to connect a servo to OUT3. Subsequent commands will be of the form "SET SERVO 1 CW 50 5" which turns the servo clockwise for 5 seconds (or until it receives a zero time command).
- Very simple - note the neat color change when the hub is connected?
- Again, the user interaction here is so simple: press the mouse down and one command is sent to the Hub, release and another is sent. NOTE that the three TI_Innovator functions here are NOT required for this document - they only come into play when feedback from the Hub is read. This will be explored in the next lesson.
You may be well familiar with the Lua on.timer command (as was I before I started working with the Innovator) but this is another approach altogether. Think of it like an advanced "wait" command. This interesting function takes a time to wait (in milliseconds, so use 1000 to wait for 1 second), and then calls a function or list of functions and parameters. The OneShotTimer will be covered in more detail later: in this document, it is used only when the Hub is being scanned and connected - mostly because the device (developed around the handheld and its limitations) needs time to process each step of the process.
- If you have worked with the BLE functions for Lua, then the basic principles of this will be familiar. Feel free to browse through the functions and follow the process by which the Innovator is scanned, found, connected, etc - but expect to just copy and paste this code into your own scripts to make them "Hub-enabled"!
Once again, if you have a Hub, you can test this script yourself by copying and pasting from this page. Of course, prepared versions are also available from the download link for this page.
end-- TI Innovator Init Values
platform.apilevel = '2.7'
local screen = platform.window
local date = "021517"require 'color'
pcall(function() require 'asi' end)local isASIavailable = false
local hubStrOn, hubStrOff, msgStrfunction addMsg(input)
msgStr = input
print(msgStr)
screen:invalidate()end
function on.construction()
end--the next three lines are required for Hub connection
TI_Innovator.init(TI_InnovatorStateCallback)
TI_Innovator.setReadListener(TI_InnovatorReadCallback)
TI_Innovator.connect()Menu={
{"About",}
{" ©2017 Texas Instruments", function() end},},
{" Version "..date, function() end},
{" Contact: steve@compasstech.com.au", function() end},
{"Controls",
{"Scan and Connect", function() TI_Innovator.connect() end},},
{"Disconnect", function() TI_Innovator.disconnect() end},
{"RESET", function() on.resize() end},
{"SET Samples",
{"SET LIGHT ON/OFF (default)", function() hubStrOn = "SET LIGHT ON" hubStrOff = "SET LIGHT OFF" TI_Innovator.Send(hubStrOn) end},},
{"SET COLOR.RED ON/OFF", function() hubStrOn = "SET COLOR.RED ON" hubStrOff = "SET COLOR.RED OFF" TI_Innovator.Send(hubStrOn) end},
{"SET COLOR.GREEN ON/OFF", function() hubStrOn = "SET COLOR.GREEN ON" hubStrOff = "SET COLOR.GREEN OFF" TI_Innovator.Send(hubStrOn) end},
{"SET COLOR.BLUE ON/OFF", function() hubStrOn = "SET COLOR.BLUE ON" hubStrOff = "SET COLOR.BLUE OFF" TI_Innovator.Send(hubStrOn) end},
{"SET SOUND 220 5", function() hubStrOn = "SET SOUND 220 5" hubStrOff = "SET SOUND 0 1" TI_Innovator.Send(hubStrOn) end},
{"SET SPEAKER 1 220 5", function() hubStrOn = "SET SPEAKER 1 220 5" TI_Innovator.Send("CONNECT SPEAKER 1 OUT1") TI_Innovator.Send(hubStrOn) hubStrOff = "SET SPEAKER 1 0 1" end},
{"SET LED 1 ON", function() hubStrOn = "SET LED 1 ON" TI_Innovator.Send("CONNECT LED 1 OUT 1") TI_Innovator.Send(hubStrOn) hubStrOff = "SET LED 1 0FF" end},
{"SET SPEAKER 1 ON", function() hubStrOn = "SET SPEAKER 1 ON" TI_Innovator.Send("CONNECT SPEAKER 1 OUT 1") TI_Innovator.Send(hubStrOn) hubStrOff = "SET SPEAKER 1 0FF" end},
{"SET SERVO 1 CW 50 5", function() TI_Innovator.Send("CONNECT SERVO 1 OUT3") hubStrOn = "SET SERVO 1 CW 50 5" hubStrOff = "SET SERVO 1 CW 50 0" TI_Innovator.Send(hubStrOn) end},
{"SET ANALOG.OUT 1 100", function() hubStrOn = "SET ANALOG.OUT 1 100" TI_Innovator.Send("CONNECT ANALOG.OUT 1 OUT 1") TI_Innovator.Send(hubStrOn) hubStrOff = "SET ANALOG.OUT 1 0" end},
{"SET DIGITAL.OUT 1 ON", function() hubStrOn = "SET DIGITAL.OUT 1 ON" TI_Innovator.Send("CONNECT DIGITAL.OUT 1 OUT 1") TI_Innovator.Send(hubStrOn) hubStrOff = "SET DIGITAL.OUT 1 0FF" end},
toolpalette.register(Menu)
screen:invalidate()
-- Layout Functions
function on.resize(width, height)
w = screen:width()end
h = screen:height()
hubStrOn, hubStrOff, msgStr = "SET LIGHT ON", "SET LIGHT OFF", "SET LIGHT OFF"
if TI_Innovator.isConnected() then TI_Innovator.Send('BEGIN') addMsg("TI Innovator Hub ready") end
screen:invalidate()
function on.paint (gc)
endif TI_Innovator.isConnected() then
gc:setColorRGB (0,255,0)else
gc:fillRect (0, 0, w, 0.025*h)
gc:setColorRGB (255,0,0)
gc:fillRect (0, 0, w, 0.025*h)
gc:drawString("Wait for connection, then", 0.1*w, 0.1*h, "middle")
gc:drawString("press anywhere to activate Hub", 0.1*w, 0.2*h, "middle")
gc:drawString(msgStr, 0.1*w, 0.9*h, "middle")
-- TI Innovator User set up
function on.escapeKey()
TI_Innovator.Send(hubStrOff)end
on.resize()
screen:invalidate()
function on.mouseDown(x, y)
TI_Innovator.Send(hubStrOn) -- This is an example of sending a SET command to the Hubend
screen:invalidate()
function on.mouseUp (x,y)
TI_Innovator.Send(hubStrOff)end
screen:invalidate()
function str2list(input, def)
local list = {}
if input:find(def) then
list = input:split(def)else
table.foreachi(list,print)
list = {input}end
return list
endfunction TI_InnovatorReadCallback(port, error_msg) -- this is the callback function that is defined above and catches the result of a READ command send to the Hub.
rxValue = port:getValue() or '' -- this gets the actual return value from the hub and puts it in the variable rxValue. The user may choose any variable name and also may do any calibration or ranging of the value here. Note that rxValue may be numeric, list or string (hence the utility function list2str)end
if tonumber(rxValue) then rxValue = math.floor(100*rxValue+0.5)/100
else rxValue = tostring(rxValue) rxValue = str2list(rxValue,string.char(10))[1] end
addMsg(hubStrOn)
if tonumber(rxValue) then voltage = (rxValue/2^14)*3.3 end -- an example of converting a raw 14 bit ADC value into voltage.
screen:invalidate()
function TI_InnovatorStateCallback(event)
endaddMsg("TI_InnovatorStateCallback")
if 'ready' == event then
TI_InnovatorConfig()
elseif "disconnected" == event then
-- user may choose to do some clean up or display a msg when the Hub is disconnected.
addMsg("TI_Innovator Hub disconnected")
end
screen:invalidate()
function TI_InnovatorConfig() -- this function is called from TI_InnovatorStateCallback() when a ready connection is succesful.
-- place CONNECT and other Hub startup commands hereend
addMsg("TI_Innovator Hub connected")
--End of user code do not modify below this line
-- TI Innovator Code Do not modify any code below this line
-- oneShotTimer BEGINS
local timerstart = timer.start
local timerstop = timer.stop
timer.start = nil
timer.stop = nil
local currentTimer = nillocal function timerStart(t)
endcurrentTimer = t
timerstart(t.period/1000)
local function timerStop()
endcurrentTimer = nil
timerstop()
local function setTimer(t)
endif t.period==nil or type(t.period)~='number' or t.period < 10 then error('period in milliseconds >= 10') end
timerStart(t)
return t
function oneShotTimer(period, listenerHandler, ...)
endif type(listenerHandler) ~= 'function' then
error('createTimerOneShot: function expected')end
setTimer {
period = period,}
oneShot = true,
listenerHandler = listenerHandler,
params = { ... },
function on.timer()
endlocal ct = currentTimer
if currentTimer == nil then
timerStop()end
if currentTimer.oneShot then
currentTimer = nilend
timerStop()
ct.listenerHandler(unpack(ct.params))
-- oneShotTimer ENDS
-- TI Innovator BEGINS
TI_Innovator = { }
function TI_Innovator.init(theStateCallback)
endif not pcall(function() require 'asi' end) then
addMsg('Hub NOT available')end
return
addMsg('Hub available')
isASIavailable = true
local HANDSHAKE_GREETING = 'ISTI\n'
local HANDSHAKE_ANSWER = 'TISTEM'
local portFoundList = { }
local state
local handshakeState
local notifyEvent
local asiStateListener
local startScanning
local stopScanning
local portFoundListener
local portStateListener
local handshake_SendListener
local handshake_readListener
local handshake_port
local TI_Innovator_port
local baudRate = asi.BAUD_RATE_DEFAULT
local readTimeout = asi.READ_TIMEOUT_DEFAULT-- User callbacks
local stateCallback = theStateCallback
local SendCallback
local readCallbackfunction notifyEvent(event)
addMsg('notifying '.. event)end
if stateCallback then
stateCallback(event)else
addMsg('no callback registered')end
function startScanning()
if isASIavailable thenend
asi.startScanning(portFoundListener)end
notifyEvent('scanning')
function stopScanning()
handshake_port = nilend
portFoundList = { }
asi.stopScanning()
function asiStateListener(asiState)
if asi.ON == asiState thenend
if state == 'active' thenelseif asi.UNSUPPORTED == asiState then
startScanning()end
notifyEvent(asi.UNSUPPORTED)end
function portFoundListener(portFound)
addMsg('portFoundListener '.. portFound:getName())end
table.insert(portFoundList, portFound)
if not TI_Innovator_port and not handshake_port then
handshake_port = portFoundList[1]end
oneShotTimer(100, handshake_port.connect, handshake_port, portStateListener)
function portStateListener(reportedPort, event, error_msg)
addMsg('portStateListener '..reportedPort:getName()..' '..event)end
if asi.CONNECTED == event then
-- configure port for handshakeelseif asi.CONNECTING_FAILED == event then
reportedPort:setWriteListener(handshake_writeListener)
reportedPort:setReadListener(handshake_readListener)
reportedPort:setReadTimeout(500)
handshakeState = 'handshake'
-- write handshake greeting
addMsg("HANDSHAKE_GREETING: "..HANDSHAKE_GREETING)
oneShotTimer(200, reportedPort.write, reportedPort, HANDSHAKE_GREETING)
if reportedPort == handshake_port thenelseif asi.DISCONNECTED == event then
table.remove(portFoundList, 1)elseif reportedPort == TI_Innovator_port then
if #portFoundList>0 then
handshake_port = portFoundList[1]else
handshake_port:connect(portStateListener)
handshake_port = nilend
TI_Innovator_port = nilend
asi.startScanning(portFoundListener)
if reportedPort == TI_Innovator_port thenend
if state == 'active' thenend
if reportedPort:getState() == asi.DISCONNECTED thennotifyEvent('disconnected')
reportedPort:connect(portStateListener)else
TI_Innovator_port = nilend
asi.startScanning(portFoundListener)
function handshake_writeListener(reportedPort, error_msg)
if error_msg thenend
addMsg('handshake_writeListener '..error_msg)end
return
if 'handshake' == handshakeState then
reportedPort:read(#HANDSHAKE_ANSWER+2)end
function handshake_readListener(reportedPort, error_msg)
if error_msg thenend
addMsg('handshake_writeListener '..error_msg)end
return
local answer = reportedPort:getValue()
if 'handshake' == handshakeState then
-- Validate answerelseif 'ready' == handshakeState then
if answer and answer:find(HANDSHAKE_ANSWER) then
addMsg("HANDSHAKE_ANSWER: "..HANDSHAKE_ANSWER)else
stopScanning()
handshakeState = 'ready'
reportedPort:write('BEGIN\n')
reportedPort:read(7)
reportedPort:disconnect()end
table.remove(portFoundList, 1)
if #portFoundList>0 then
handshake_port = portFoundList[1]else
handshake_port:connect(portStateListener)
handshake_port = nilend
if answer and answer:find('READY') thenend
-- Configure port for normal useend
TI_Innovator_port = reportedPort
TI_Innovator_port:setReadTimeout(readTimeout)
TI_Innovator.setWriteListener(SendCallback)
TI_Innovator.setReadListener(readCallback)
-- Notify launchpad is ready
notifyEvent('ready')
-- INTERFACE -- BEGINS --
function TI_Innovator.connect()
state = 'active'end
startScanning()
function TI_Innovator.disconnect()
state = 'inactive'end
if isASIavailable then
if TI_Innovator_port thenend
TI_Innovator_port:disconnect()end
TI_Innovator_port = nil
stopScanning()
function TI_Innovator.setWriteListener(newWriteCallback)
writeCallback = newWriteCallbackend
if TI_Innovator_port then
TI_Innovator_port:setWriteListener(writeCallback)end
function TI_Innovator.setReadListener(newReadCallback, newReadObject)
readCallback = newReadCallbackend
if TI_Innovator_port then
TI_Innovator_port:setReadListener(readCallback)end
function TI_Innovator.setBaudRate(newBaudRate)
baudRate = newBaudRateend
if TI_Innovator_port then
TI_Innovator_port:setBaudRate(baudRate)end
function TI_Innovator.setReadTimeout(newReadTimeout)
readTimeout = newReadTimeoutend
if TI_Innovator_port then
TI_Innovator_port:setReadTimeout(readTimeout)end
function TI_Innovator.Send(data)
if not TI_Innovator_port thenend
addMsg('No Hub attached')end
return 'No Hub attached'
local result = TI_Innovator_port:write(data .. '\n')
if result then addMsg(tostring(data)..': '.. tostring(result))
else addMsg(tostring(data)) end
return result
function TI_Innovator.Read(bytes_to_read)
addMsg('TI_Innovator.Read')end
if not TI_Innovator_port then
return "No TI Hub attached"end
return TI_Innovator_port:read(bytes_to_read)
function TI_Innovator.request(request)
if not TI_Innovator_port thenend
return "No TI Hub attached"end
local result = TI_Innovator_port:write(request .. '\n')
if result then addMsg(tostring(request)..': '.. tostring(result))
else addMsg(tostring(request)) end
result = TI_Innovator_port:read()
addMsg('read() '.. tostring(result))
if result then
addMsg('Error sending request: ' .. tostring(request))end
return result
function TI_Innovator.isConnected()
if not TI_Innovator_port thenend
return falseend
return TI_Innovator_port:getState() == asi.CONNECTED
function TI_Innovator.getName()
if not TI_Innovator_port thenend
return "No TI Hub attached"end
return TI_Innovator_port:getName()
function TI_Innovator.getIdentifier()
if not TI_Innovator_port thenend
return "No TI Hub attached"end
return TI_Innovator_port:getIdentifier()
-- INTERFACE -- ENDS --
-- Wait for the ASI to be up and running
asi.addStateListener(asiStateListener)
end
-- TI Innovator Init Values
platform.apilevel = '2.7'
local screen = platform.window
local date = "021517"require 'color'
pcall(function() require 'asi' end)local isASIavailable = false
local hubStrOn, hubStrOff, msgStrfunction addMsg(input)
msgStr = input
print(msgStr)
screen:invalidate()end
function on.construction()
end--the next three lines are required for Hub connection
TI_Innovator.init(TI_InnovatorStateCallback)
TI_Innovator.setReadListener(TI_InnovatorReadCallback)
TI_Innovator.connect()Menu={
{"About",}
{" ©2017 Texas Instruments", function() end},},
{" Version "..date, function() end},
{" Contact: steve@compasstech.com.au", function() end},
{"Controls",
{"Scan and Connect", function() TI_Innovator.connect() end},},
{"Disconnect", function() TI_Innovator.disconnect() end},
{"RESET", function() on.resize() end},
{"SET Samples",
{"SET LIGHT ON/OFF (default)", function() hubStrOn = "SET LIGHT ON" hubStrOff = "SET LIGHT OFF" TI_Innovator.Send(hubStrOn) end},},
{"SET COLOR.RED ON/OFF", function() hubStrOn = "SET COLOR.RED ON" hubStrOff = "SET COLOR.RED OFF" TI_Innovator.Send(hubStrOn) end},
{"SET COLOR.GREEN ON/OFF", function() hubStrOn = "SET COLOR.GREEN ON" hubStrOff = "SET COLOR.GREEN OFF" TI_Innovator.Send(hubStrOn) end},
{"SET COLOR.BLUE ON/OFF", function() hubStrOn = "SET COLOR.BLUE ON" hubStrOff = "SET COLOR.BLUE OFF"TI_Innovator.Send(hubStrOn) end},
{"SET SOUND 220 5", function() hubStrOn = "SET SOUND 220 5" hubStrOff = "SET SOUND 0 1" TI_Innovator.Send(hubStrOn) end},
{"SET SPEAKER 1 220 5", function() hubStrOn = "SET SPEAKER 1 220 5" TI_Innovator.Send("CONNECT SPEAKER 1 OUT1") TI_Innovator.Send(hubStrOn) hubStrOff = "SET SPEAKER 1 0 1" end},
{"SET LED 1 ON", function() hubStrOn = "SET LED 1 ON" TI_Innovator.Send("CONNECT LED 1 OUT 1") TI_Innovator.Send(hubStrOn) hubStrOff = "SET LED 1 0FF" end},
{"SET SPEAKER 1 ON", function() hubStrOn = "SET SPEAKER 1 ON" TI_Innovator.Send("CONNECT SPEAKER 1 OUT 1") TI_Innovator.Send(hubStrOn) hubStrOff = "SET SPEAKER 1 0FF" end},
{"SET SERVO 1 CW 50 5", function() TI_Innovator.Send("CONNECT SERVO 1 OUT3") hubStrOn = "SET SERVO 1 CW 50 5" hubStrOff = "SET SERVO 1 CW 50 0" TI_Innovator.Send(hubStrOn) end},
{"SET ANALOG.OUT 1 100", function() hubStrOn = "SET ANALOG.OUT 1 100" TI_Innovator.Send("CONNECT ANALOG.OUT 1 OUT 1") TI_Innovator.Send(hubStrOn) hubStrOff = "SET ANALOG.OUT 1 0" end},
{"SET DIGITAL.OUT 1 ON", function() hubStrOn = "SET DIGITAL.OUT 1 ON" TI_Innovator.Send("CONNECT DIGITAL.OUT 1 OUT 1") TI_Innovator.Send(hubStrOn) hubStrOff = "SET DIGITAL.OUT 1 0FF" end},
toolpalette.register(Menu)
screen:invalidate()
-- Layout Functions
function on.resize(width, height)
w = screen:width()end
h = screen:height()
hubStrOn, hubStrOff, msgStr = "SET LIGHT ON", "SET LIGHT OFF", "SET LIGHT OFF"
if TI_Innovator.isConnected() then TI_Innovator.Send('BEGIN') addMsg("TI Innovator Hub ready") end
screen:invalidate()
function on.paint (gc)
endif TI_Innovator.isConnected() then
gc:setColorRGB (0,255,0)else
gc:fillRect (0, 0, w, 0.025*h)
gc:setColorRGB (255,0,0)end
gc:fillRect (0, 0, w, 0.025*h)
gc:drawString("Wait for connection, then", 0.1*w, 0.1*h, "middle")
gc:drawString("press anywhere to activate Hub", 0.1*w, 0.2*h, "middle")
gc:drawString(msgStr, 0.1*w, 0.9*h, "middle")
-- TI Innovator User set up
function on.escapeKey()
TI_Innovator.Send(hubStrOff)end
on.resize()
screen:invalidate()
function on.mouseDown(x, y)
TI_Innovator.Send(hubStrOn) -- This is an example of sending a SET command to the Hubend
screen:invalidate()
function on.mouseUp (x,y)
TI_Innovator.Send(hubStrOff)end
screen:invalidate()
function str2list(input, def)
local list = {}
if input:find(def) then
list = input:split(def)else
table.foreachi(list,print)
list = {input}end
return list
endfunction TI_InnovatorReadCallback(port, error_msg) -- this is the callback function that is defined above and catches the result of a READ command send to the Hub.
rxValue = port:getValue() or '' -- this gets the actual return value from the hub and puts it in the variable rxValue. The user may choose any variable name and also may do any calibration or ranging of the value here. Note that rxValue may be numeric, list or string (hence the utility function list2str)end
if tonumber(rxValue) then rxValue = math.floor(100*rxValue+0.5)/100
else rxValue = tostring(rxValue) rxValue = str2list(rxValue,string.char(10))[1] end
addMsg(hubStrOn)
if tonumber(rxValue) then voltage = (rxValue/2^14)*3.3 end -- an example of converting a raw 14 bit ADC value into voltage.
screen:invalidate()
function TI_InnovatorStateCallback(event)
endaddMsg("TI_InnovatorStateCallback")
if 'ready' == event then
TI_InnovatorConfig()
elseif "disconnected" == event then
-- user may choose to do some clean up or display a msg when the Hub is disconnected.
addMsg("TI_Innovator Hub disconnected")
end
screen:invalidate()
function TI_InnovatorConfig() -- this function is called from TI_InnovatorStateCallback() when a ready connection is succesful.
-- place CONNECT and other Hub startup commands hereend
addMsg("TI_Innovator Hub connected")
-- oneShotTimer BEGINS
local timerstart = timer.start
local timerstop = timer.stop
timer.start = nil
timer.stop = nil
local currentTimer = nillocal function timerStart(t)
endcurrentTimer = t
timerstart(t.period/1000)
local function timerStop()
endcurrentTimer = nil
timerstop()
local function setTimer(t)
endif t.period==nil or type(t.period)~='number' or t.period < 10 then error('period in milliseconds >= 10') end
timerStart(t)
return t
function oneShotTimer(period, listenerHandler, ...)
endif type(listenerHandler) ~= 'function' then
error('createTimerOneShot: function expected')end
setTimer {
period = period,}
oneShot = true,
listenerHandler = listenerHandler,
params = { ... },
function on.timer()
endlocal ct = currentTimer
if currentTimer == nil then
timerStop()end
if currentTimer.oneShot then
currentTimer = nilend
timerStop()
ct.listenerHandler(unpack(ct.params))
-- oneShotTimer ENDS
-- TI Innovator BEGINS
TI_Innovator = { }
function TI_Innovator.init(theStateCallback)
endif not pcall(function() require 'asi' end) then
addMsg('Hub NOT available')end
return
addMsg('Hub available')
isASIavailable = true
local HANDSHAKE_GREETING = 'ISTI\n'
local HANDSHAKE_ANSWER = 'TISTEM'
local portFoundList = { }
local state
local handshakeState
local notifyEvent
local asiStateListener
local startScanning
local stopScanning
local portFoundListener
local portStateListener
local handshake_SendListener
local handshake_readListener
local handshake_port
local TI_Innovator_port
local baudRate = asi.BAUD_RATE_DEFAULT
local readTimeout = asi.READ_TIMEOUT_DEFAULT-- User callbacks
local stateCallback = theStateCallback
local SendCallback
local readCallbackfunction notifyEvent(event)
addMsg('notifying '.. event)end
if stateCallback then
stateCallback(event)else
addMsg('no callback registered')end
function startScanning()
if isASIavailable thenend
asi.startScanning(portFoundListener)end
notifyEvent('scanning')
function stopScanning()
handshake_port = nilend
portFoundList = { }
asi.stopScanning()
function asiStateListener(asiState)
if asi.ON == asiState thenend
if state == 'active' thenelseif asi.UNSUPPORTED == asiState then
startScanning()end
notifyEvent(asi.UNSUPPORTED)end
function portFoundListener(portFound)
addMsg('portFoundListener '.. portFound:getName())end
table.insert(portFoundList, portFound)
if not TI_Innovator_port and not handshake_port then
handshake_port = portFoundList[1]end
oneShotTimer(100, handshake_port.connect, handshake_port, portStateListener)
function portStateListener(reportedPort, event, error_msg)
addMsg('portStateListener '..reportedPort:getName()..' '..event)end
if asi.CONNECTED == event then
-- configure port for handshakeelseif asi.CONNECTING_FAILED == event then
reportedPort:setWriteListener(handshake_writeListener)
reportedPort:setReadListener(handshake_readListener)
reportedPort:setReadTimeout(500)
handshakeState = 'handshake'
-- write handshake greeting
addMsg("HANDSHAKE_GREETING: "..HANDSHAKE_GREETING)
oneShotTimer(200, reportedPort.write, reportedPort, HANDSHAKE_GREETING)
if reportedPort == handshake_port thenelseif asi.DISCONNECTED == event then
table.remove(portFoundList, 1)elseif reportedPort == TI_Innovator_port then
if #portFoundList>0 then
handshake_port = portFoundList[1]else
handshake_port:connect(portStateListener)
handshake_port = nilend
TI_Innovator_port = nilend
asi.startScanning(portFoundListener)
if reportedPort == TI_Innovator_port thenend
if state == 'active' thenend
if reportedPort:getState() == asi.DISCONNECTED thennotifyEvent('disconnected')
reportedPort:connect(portStateListener)else
TI_Innovator_port = nilend
asi.startScanning(portFoundListener)
function handshake_writeListener(reportedPort, error_msg)
if error_msg thenend
addMsg('handshake_writeListener '..error_msg)end
return
if 'handshake' == handshakeState then
reportedPort:read(#HANDSHAKE_ANSWER+2)end
function handshake_readListener(reportedPort, error_msg)
if error_msg thenend
addMsg('handshake_writeListener '..error_msg)end
return
local answer = reportedPort:getValue()
if 'handshake' == handshakeState then
-- Validate answerelseif 'ready' == handshakeState then
if answer and answer:find(HANDSHAKE_ANSWER) then
addMsg("HANDSHAKE_ANSWER: "..HANDSHAKE_ANSWER)else
stopScanning()
handshakeState = 'ready'
reportedPort:write('BEGIN\n')
reportedPort:read(7)
reportedPort:disconnect()end
table.remove(portFoundList, 1)
if #portFoundList>0 then
handshake_port = portFoundList[1]else
handshake_port:connect(portStateListener)
handshake_port = nilend
if answer and answer:find('READY') thenend
-- Configure port for normal useend
TI_Innovator_port = reportedPort
TI_Innovator_port:setReadTimeout(readTimeout)
TI_Innovator.setWriteListener(SendCallback)
TI_Innovator.setReadListener(readCallback)
-- Notify launchpad is ready
notifyEvent('ready')
-- INTERFACE -- BEGINS --
function TI_Innovator.connect()
state = 'active'end
startScanning()
function TI_Innovator.disconnect()
state = 'inactive'end
if isASIavailable then
if TI_Innovator_port thenend
TI_Innovator_port:disconnect()end
TI_Innovator_port = nil
stopScanning()
function TI_Innovator.setWriteListener(newWriteCallback)
writeCallback = newWriteCallbackend
if TI_Innovator_port then
TI_Innovator_port:setWriteListener(writeCallback)end
function TI_Innovator.setReadListener(newReadCallback, newReadObject)
readCallback = newReadCallbackend
if TI_Innovator_port then
TI_Innovator_port:setReadListener(readCallback)end
function TI_Innovator.setBaudRate(newBaudRate)
baudRate = newBaudRateend
if TI_Innovator_port then
TI_Innovator_port:setBaudRate(baudRate)end
function TI_Innovator.setReadTimeout(newReadTimeout)
readTimeout = newReadTimeoutend
if TI_Innovator_port then
TI_Innovator_port:setReadTimeout(readTimeout)end
function TI_Innovator.Send(data)
if not TI_Innovator_port thenend
addMsg('No Hub attached')end
return 'No Hub attached'
local result = TI_Innovator_port:write(data .. '\n')
if result then addMsg(tostring(data)'): '.. tostring(result))
else addMsg(tostring(data)')') end
return result
function TI_Innovator.Read(bytes_to_read)
addMsg('TI_Innovator.Read')end
if not TI_Innovator_port then
return "No TI Hub attached"end
return TI_Innovator_port:read(bytes_to_read)
function TI_Innovator.request(request)
if not TI_Innovator_port thenend
return "No TI Hub attached"end
local result = TI_Innovator_port:write(request .. '\n')
if result then addMsg('write(tostring(request)'): '.. tostring(result))
else addMsg(tostring(request)')') end
result = TI_Innovator_port:read()
addMsg('read() '.. tostring(result))
if result then
addMsg('Error sending request: ' .. tostring(request))end
return result
function TI_Innovator.isConnected()
if not TI_Innovator_port thenend
return falseend
return TI_Innovator_port:getState() == asi.CONNECTED
function TI_Innovator.getName()
if not TI_Innovator_port thenend
return "No TI Hub attached"end
return TI_Innovator_port:getName()
function TI_Innovator.getIdentifier()
if not TI_Innovator_port thenend
return "No TI Hub attached"end
return TI_Innovator_port:getIdentifier()
-- INTERFACE -- ENDS --
-- Wait for the ASI to be up and running
asi.addStateListener(asiStateListener)
end
Home ← STEM HQ ← TI-Nspire Authoring ← TI-Nspire Scripting HQ ← Scripting Tutorial - Lesson 41