Home ← TI-Nspire Authoring ← TI-Nspire Scripting HQ ← Scripting Tutorial - Lesson 34
Scripting Tutorial - Lesson 34: BLE - Measuring Temperature with the TI Sensor Tag
Lesson 30: Welcome to Bluetooth (BLE)
Supplement: Working with Scripts on the iPad
Lesson 31: BLE - Create your own TI-Nspire Remote
Lesson 32: BLE - Measuring Temperature with the Vernier Go Wireless Temp
Lesson 33: BLE - Measuring Heart Rate
Lesson 34: BLE - Measuring Temperature with the TI Sensor Tag
Lesson 35: BLE - Build Your Own Weather Station with the TI Sensor Tag
Lesson 36: BLE - Exploring Movement and Position with the TI Sensor Tag
Download supporting files for this tutorial
Download this page in PDF format
Texas Instruments TI-Nspire Scripting Support Page
In the previous lesson of this sequence, we developed a working script to connect to and read temperature data from a Vernier Go Wireless Temperature probe.
In this lesson, we extend this to the TI Sensor Tag 2.0 using almost the same script with just a few variations.
Note: The script on this page is designed for the TI SensorTag 2.0 (Or CC2650 SensorTag). It will not give accurate readings for the original (CC2451) SensorTag. For a general script that works for BOTH tag types, refer to the zipped support files: BLE_Sample_ST.tns.
Communicating with the TI SensorTag 2.0
The TI Sensor Tag offers a wonderful array of sensors for collecting real world data: temperature, barometric pressure, light intensity, humidity, acceleration, magnetic field and gyroscope. We will develop scripts for all of these.
We begin with temperature, and uniquely, this device collects TWO different types of temperature data: ambient (or die) temperature (like the Vernier probe) but also target or infra-red (IR) temperature. The SensorTag has a small window - point this at the nearest object and it will read the temperature of that object! Very handy for boiling water and other situations where the device might suffer from too close an encounter!
Because the SensorTag has so many different functions, there is a little more interaction required than for the simple case of the Vernier probe. For example, the TI Sensor Tag uses some 15 UUID values. Each of the six sensors uses at least two: one for reading, and frequently, one for writing to the device (for starting, or setting the sampling period, for example.) The two temperature UUIDs are:
tempRead = 'F000AA01-0451-4000-B000-000000000000'
tempStart = 'F000AA02-0451-4000-B000-000000000000'
Only three changes are required to the Vernier script developed in the last lesson to adapt for the Sensor Tag.
Click the 'init' button to see minor changes to the previous Vernier script to adapt for the SensorTag. The two temperature variables for the Sensor Tag are named dieTemp and irTemp (but could have easily been something common, like Temp1 and Temp2). Instead of the second reading giving Fahrenheit, it provides the Target Temperature in this case. All other functions are common to both devices, so all that remains is to make some adjustments to the last two functions.
Specific BLE Functions for the TI Sensor Tag 2.0
Study the two specific functions for the SensorTag and compare with those for the Vernier temp probe.
The first difference you should note is that the callbackCharacteristics(service) function checks both tempRead and tempStart. This script not only reads from the BLE device, but writes to it. Writing string.char(1) serves to notify the device to begin collecting data.
The callbackCharacteristic(characteristic) function takes the read data, and unpacks it - note that this data consists of both signed and unsigned elements, and these must be unpacked separately as shown. Like the Vernier data, the SensorTag ambient temperature data is easy to compute - after unpacking, the dieTemp simply takes the first two bits and divides these by 128. The irTemp does the same with the last two bits.
platform.apilevel = '2.5'
screen = platform.window
w = screen:width()
h = screen:height()pcall(function () require 'bleCentral' end)
require "color"
local bleState = ''
local bleStatus = 'Stand by'
local peripheralName = ''
local myPeripheral = nil
local characteristicsFound = 0-- TI Sensor Tag Init Variables
local tempRead = 'F000AA01-0451-4000-B000-000000000000'
local tempStart = 'F000AA02-0451-4000-B000-000000000000'
local dieTemp = nil
local irTemp = nil
local units = '°C'
local nameCheckList = {'Tag 2.0', 'CC2650'}
local nameList = {'TI Sensor Tag 2.0', 'CC2650 SensorTag', 'TI SensorTag 20'}
-- Layout Functions (resize and paint)
function on.resize()
w = screen:width() or 841
h = screen:height() or 567pcall(function() ble.addStateListener(listenerCallback) end)
refreshMenu()screen:invalidate()
end
function on.paint(gc)
w = screen:width() or 841
h = screen:height() or 567local fontSize = math.floor(h/28 + 0.5)
fontSize = fontSize < 25 and fontSize or 24
fontSize = fontSize > 6 and fontSize or 7
if bleState:find("ON") then gc:setColorRGB(color.blue) else gc:setColorRGB(color.red) end
gc:setFont("sansserif", "b", fontSize)
gc:drawString(bleState, 0.1*w, 0.1*h)
gc:drawString(bleStatus, 0.1*w, 0.2*h)if peripheralName ~= '' then
gc:setColorRGB(color.blue)
local str = peripheralName..' Temperature'
local sw = gc:getStringWidth(str)
gc:drawString(str, 0.5*w - sw/2, 0.95*h, 'middle')else
gc:setColorRGB(color.gray)
local str = nameList[1]..' Temperature'
local sw = gc:getStringWidth(str)
gc:drawString(str, 0.5*w - sw/2, 0.95*h, 'middle')end
gc:setColorRGB(color.green)
gc:fillRect(0.9*w, 0.9*h, 0.1*w, 0.1*h)
gc:setColorRGB(color.black)
gc:drawRect(0.9*w, 0.9*h, 0.1*w, 0.1*h)
local sw = gc:getStringWidth('Scan')
gc:drawString('Scan', 0.95*w - sw/2, 0.95*h, 'middle')gc:setColorRGB(color.red)
gc:fillRect(0, 0.9*h, 0.1*w, 0.1*h)
gc:setColorRGB(color.black)
gc:drawRect(0, 0.9*h, 0.1*w, 0.1*h)
local sw = gc:getStringWidth('Stop')
gc:drawString('Stop', 0.05*w - sw/2, 0.95*h, 'middle')local fontSize = math.floor(h/20 + 0.5)
fontSize = fontSize < 25 and fontSize or 24
fontSize = fontSize > 6 and fontSize or 7
gc:setFont("sansserif", "b", fontSize)if dieTemp then
local msgT1 = string.format("%s %.1f "..units, "Ambient Temp = ", dieTemp )
local sw = gc:getStringWidth(msgT1)
gc:drawString(msgT1, 0.5*w - sw/2, 0.5*h)end
if irTemp then
local msgT2 = string.format("%s %.1f "..units, "Object Temp = ", irTemp)
local sw = gc:getStringWidth(msgT2)
gc:drawString(msgT2, 0.5*w - sw/2, 0.6*h)end
end
--Menu, Keyboard and Mouse Functions--------------
function refreshMenu()
Menu={
}{"Controls",
},{"Scan and Connect", function() peripheralOn() end},
{"Disconnect", function() peripheralOff() end},
{"Reset", function()
bleState = ''
bleStatus = 'Stand by'
on.resize() end},toolpalette.register(Menu)
end
function on.enterKey()
peripheralOn()
end
function on.escapeKey()
resetall()
end
function resetall()
bleState = ''
dieTemp = nil
irTemp = nil
peripheralOff()
on.resize()end
function on.mouseUp(x, y)
w = screen:width() or 841
h = screen:height() or 567if x > 0.9*w and y > 0.9*h then on.enterKey() end
if x < 0.1*w and y > 0.9*h then on.escapeKey() end
screen:invalidate()end
-- BLE General Functions -----------
function listenerCallback(state, scriptError)
if state == ble.ON then
bleState = 'BLE ON'
elseif state == ble.OFF then
bleState = 'BLE OFF'
elseif state == ble.RESETTING then
bleState = 'BLE RESET'
elseif state == ble.UNSUPPORTED then
bleState = 'UNSUPPORTED'
if scriptError then
print('Error message: BLE not supported')
end
endscreen:invalidate()
end
function peripheralOn()
bleCentral.startScanning(callbackScan)
bleStatus = 'Scanning'
screen:invalidate()end
function peripheralOff()
bleCentral.stopScanning()
if myPeripheral then
myPeripheral:disconnect()
endbleStatus = 'Stand by'
peripheralName = ''
screen:invalidate()end
function callbackScan(peripheral)
if peripheral ~= nil then
peripheralName = peripheral:getName() or 'Unknown Device'
for n =1, #nameCheckList doif peripheralName and peripheralName:find(nameCheckList[n]) then
peripheral:connect(callbackConnect)
elseif not peripheralName:find('unsupported') then
peripheralName = peripheralName..' (unsupported)'
end
end
end
screen:invalidate()end
function callbackConnect(peripheral, event)
if event == bleCentral.CONNECTED then
bleCentral.stopScanning()
bleStatus = 'Connected'
myPeripheral = peripheral
peripheralName = peripheralName:gsub(' %(unsupported%)', '')
peripheral:discoverServices(callbackServices)elseif event == bleCentral.DISCONNECTED then
bleStatus = 'Disconnected'
peripheralName = ''end
screen:invalidate()end
function callbackServices(peripheral)
if peripheral ~= nil and peripheral:getState() and peripheral:getState() == bleCentral.CONNECTED then
local services = peripheral:getServices()
for _,service in ipairs(services) do
service:discoverCharacteristics(callbackCharacteristics)
endend
screen:invalidate()
end
-- BLE Specific Functions ---------
function callbackCharacteristics(service)
local characteristicsList = service:getCharacteristics()
for _,characteristic in ipairs(characteristicsList) do
if characteristic:getUUID() == tempRead then
characteristic:setValueUpdateListener(callbackCharacteristic)
characteristic:setNotify(true)
characteristicsFound = characteristicsFound + 1else
if characteristic:getUUID() == tempStart then
characteristic:setWriteCompleteListener(callbackCharacteristic)
characteristic:write(string.char(1), true)
characteristicsFound = characteristicsFound + 1end
end
end
end
function callbackCharacteristic(characteristic)
if characteristic:getUUID() == tempRead then
--TI Sensor Tag
local value = characteristic:getValue()
if value then
local loWord, hiWord = ble.unpack("s16u16",value)
dieTemp = tonumber(hiWord) / 128
irTemp = tonumber(loWord) / 128
end
screen:invalidate()
end
end
platform.apilevel = '2.5'
screen = platform.window
w = screen:width()
h = screen:height()pcall(function () require 'bleCentral' end)
require "color"
local bleState = ''
local bleStatus = 'Stand by'
local peripheralName = ''
local myPeripheral = nil
local characteristicsFound = 0--TI Sensor Tag Init Variables
local tempRead = 'F000AA01-0451-4000-B000-000000000000'
local tempStart = 'F000AA02-0451-4000-B000-000000000000'
local dieTemp = nil
local irTemp = nil
local units = '°C'
local nameCheckList = {'Tag 2.0', 'CC2650'}
local nameList = {'TI Sensor Tag 2.0', 'CC2650 SensorTag', 'TI SensorTag 20'}
-- Layout Functions (resize and paint)
function on.resize()
w = screen:width() or 841
h = screen:height() or 567pcall(function() ble.addStateListener(listenerCallback) end)
refreshMenu()screen:invalidate()
end
function on.paint(gc)
w = screen:width() or 841
h = screen:height() or 567local fontSize = math.floor(h/28 + 0.5)
fontSize = fontSize < 25 and fontSize or 24
fontSize = fontSize > 6 and fontSize or 7
if bleState:find("ON") then gc:setColorRGB(color.blue) else gc:setColorRGB(color.red) end
gc:setFont("sansserif", "b", fontSize)
gc:drawString(bleState, 0.1*w, 0.1*h)
gc:drawString(bleStatus, 0.1*w, 0.2*h)if peripheralName ~= '' then
gc:setColorRGB(color.blue)
local str = peripheralName..' Temperature'
local sw = gc:getStringWidth(str)
gc:drawString(str, 0.5*w - sw/2, 0.95*h, 'middle')else
gc:setColorRGB(color.gray)
local str = nameList[1]..' Temperature'
local sw = gc:getStringWidth(str)
gc:drawString(str, 0.5*w - sw/2, 0.95*h, 'middle')end
gc:setColorRGB(color.green)
gc:fillRect(0.9*w, 0.9*h, 0.1*w, 0.1*h)
gc:setColorRGB(color.black)
gc:drawRect(0.9*w, 0.9*h, 0.1*w, 0.1*h)
local sw = gc:getStringWidth('Scan')
gc:drawString('Scan', 0.95*w - sw/2, 0.95*h, 'middle')gc:setColorRGB(color.red)
gc:fillRect(0, 0.9*h, 0.1*w, 0.1*h)
gc:setColorRGB(color.black)
gc:drawRect(0, 0.9*h, 0.1*w, 0.1*h)
local sw = gc:getStringWidth('Stop')
gc:drawString('Stop', 0.05*w - sw/2, 0.95*h, 'middle')local fontSize = math.floor(h/20 + 0.5)
fontSize = fontSize < 25 and fontSize or 24
fontSize = fontSize > 6 and fontSize or 7
gc:setFont("sansserif", "b", fontSize)if dieTemp then
local msgT1 = string.format("%s %.1f "..units, "Ambient Temp = ", dieTemp )
local sw = gc:getStringWidth(msgT1)
gc:drawString(msgT1, 0.5*w - sw/2, 0.5*h)end
if irTemp then
local msgT2 = string.format("%s %.1f "..units, "Object Temp = ", irTemp)
local sw = gc:getStringWidth(msgT2)
gc:drawString(msgT2, 0.5*w - sw/2, 0.6*h)end
end
--Menu, Keyboard and Mouse Functions--------------
function refreshMenu()
Menu={
}{"Controls",
},{"Scan and Connect", function() peripheralOn() end},
{"Disconnect", function() peripheralOff() end},
{"Reset", function()
bleState = ''
bleStatus = 'Stand by'
on.resize() end},toolpalette.register(Menu)
end
function on.enterKey()
peripheralOn()
end
function on.escapeKey()
resetall()
end
function resetall()
bleState = ''
die = nil
irTemp = nil
peripheralOff()
on.resize()end
function on.mouseUp(x, y)
w = screen:width() or 841
h = screen:height() or 567if x > 0.9*w and y > 0.9*h then on.enterKey() end
if x < 0.1*w and y > 0.9*h then on.escapeKey() end
screen:invalidate()end
-- BLE General Functions -----------
function listenerCallback(state, scriptError)
if state == ble.ON then
bleState = 'BLE ON'
elseif state == ble.OFF then
bleState = 'BLE OFF'
elseif state == ble.RESETTING then
bleState = 'BLE RESET'
elseif state == ble.UNSUPPORTED then
bleState = 'UNSUPPORTED'
if scriptError then
print('Error message: BLE not supported')
end
endscreen:invalidate()
end
function peripheralOn()
bleCentral.startScanning(callbackScan)
bleStatus = 'Scanning'
screen:invalidate()end
function peripheralOff()
bleCentral.stopScanning()
if myPeripheral then
myPeripheral:disconnect()
endbleStatus = 'Stand by'
peripheralName = ''
screen:invalidate()end
function callbackScan(peripheral)
if peripheral ~= nil then
peripheralName = peripheral:getName() or 'Unknown Device'
for n =1, #nameCheckList doif peripheralName and peripheralName:find(nameCheckList[n]) then
peripheral:connect(callbackConnect)
elseif not peripheralName:find('unsupported') then
peripheralName = peripheralName..' (unsupported)'
end
end
end
screen:invalidate()end
function callbackConnect(peripheral, event)
if event == bleCentral.CONNECTED then
bleCentral.stopScanning()
bleStatus = 'Connected'
myPeripheral = peripheral
peripheralName = peripheralName:gsub(' %(unsupported%)', '')
peripheral:discoverServices(callbackServices)elseif event == bleCentral.DISCONNECTED then
bleStatus = 'Disconnected'
peripheralName = ''end
screen:invalidate()end
function callbackServices(peripheral)
if peripheral ~= nil and peripheral:getState() and peripheral:getState() == bleCentral.CONNECTED then
local services = peripheral:getServices()
for _,service in ipairs(services) do
service:discoverCharacteristics(callbackCharacteristics)
endend
screen:invalidate()
end
-- BLE Specific Functions ---------
function callbackCharacteristics(service)
local characteristicsList = service:getCharacteristics()
for _,characteristic in ipairs(characteristicsList) do
if characteristic:getUUID() == tempRead then
characteristic:setValueUpdateListener(callbackCharacteristic)
characteristic:setNotify(true)
characteristicsFound = characteristicsFound + 1else
if characteristic:getUUID() == tempStart then
characteristic:setWriteCompleteListener(callbackCharacteristic)
characteristic:write(string.char(1), true)
characteristicsFound = characteristicsFound + 1end
end
end
end
function callbackCharacteristic(characteristic)
if characteristic:getUUID() == tempRead then
--TI Sensor Tag
local value = characteristic:getValue()
if value then
local loWord, hiWord = ble.unpack("s16u16",value)
dieTemp = tonumber(hiWord) / 128
irTemp = tonumber(loWord) / 128
end
screen:invalidate()
end
end
Once again, if you have a SensorTag and appropriate device, 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, including a script which will check for and read from both Sensor Tag and Vernier probe. Your home work is to study how this script is just an extension of the two individual scripts we have developed here.
In our next lesson, we will generalise this script to collect data from all of the Sensor Tag sensors.
Home ← TI-Nspire Authoring ← TI-Nspire Scripting HQ ← Scripting Tutorial - Lesson 34