Lua - TI SensorTag
Introduction
Recently I have bought a couple of SensorTag from TI SensorTag wiki for a project I have been asked to produce a feasibility study.
SensorTag is effectively an evaluation board that integrates 6 types of sensors within a tiny board:
- target thermometer - Hygrometer - Barometer - Gyroscope - Accelerometer - Magnetometer(Compass)
all sensors are available via Bluetooth 4.0 (BLE) interrogating the integrated GATT server within the delivered firmware of the evaluatoin board.
Firstly I installed the SensorTag app in my Android device and, after pressing the side small button, I could connect it to gather the measures. After played a while, I noticed that Accelerometer was not sending any data to my android device so I have upgraded the SensorTag Firmware from versin 1.4 to 1.5 and all data were received.
As I have been playing with domoticz for about 6 months, I thought it would be worth to integrate also the SensorTag and collect some of the provided measures. In particular I am interested in collecting target temperature provided by IR Thermometer, ambient tempertature and humidity and, why not, even atmosferic pressure. These data, correlated with gas consumption, can easily provide characteristics of any gas heating system.
Googoling around, I noticed that only some python codes has been pubblished, exploiting bluez (opensource bluetooth stack) gatttool function. As python is a scripting language not far from LUA, and, as domoticz provide valuable functions in LUA, I started playing with those.
Bluetooth Low Energy
Job start
download bluez and install it
sudo apt-get install bluez
you may also download source code and compile it, if needed
connect your Bluetooth 4.0 (BLE) dongle to your RP (or other linux computer)
dmesg
you should see something like
[88770.582050] usb 1-1.4.3: new full-speed USB device number 13 using dwc_otg
[88770.699670] usb 1-1.4.3: New USB device found, idVendor=0a5c, idProduct=21e2
[88770.699709] usb 1-1.4.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[88770.699726] usb 1-1.4.3: Product: BCM920702 Bluetooth 4.0 Zero Touch Dongle
[88770.699740] usb 1-1.4.3: Manufacturer: Broadcom Corp
[88770.699754] usb 1-1.4.3: SerialNumber: 001986000C68
if everything is fine, issue the following command
hciconfig
and you should verify that hci interface is 'up'
hci0: Type: BR/EDR Bus: USB
BD Address: 00:19:86:00:0C:68 ACL MTU: 1021:8 SCO MTU: 64:1
UP RUNNING PSCAN
RX bytes:2096258 acl:52125 sco:0 events:155153 errors:0
TX bytes:1890903 acl:105482 sco:0 commands:38767 errors:0
.. and now start playing with the sensorscan.
To verify that SensorTag is detected press the small side button and issue
sudo hcitool lescan
LE Scan ...
B4:99:4C:64:30:B7 (unknown)
B4:99:4C:64:30:B7 SensorTag
(press ctrl C to stop LE scan)
when you have read the sensortag MAC address you may start with LUA. What you will find below, it concerns only teh following sensors: a) target temperature b) humidity c) atmosferic pressure
as actually those are my needs and domoticz already provides all the necessary to have virtual devices. all 3 above mentioned sensors integrate 3 different ambient termometers and I though it would be nice to get the measurement from all of them. So I created 4 virtual temperature sensors (3 ambient and 1 target), 1 virtual Higrometer and 1 summary sensor that includes temperature, relative humidity and atmosferic pressure (automatically, I found also a barometer virtual device in the whether tab). Totally 7 virtual devices.
Lua code
.. and finally here you are
function os.capture(cmd, raw)
local f = assert(io.popen(cmd, 'r'))
local s = assert(f:read('*a'))
f:close()
if raw then
return s
end
s = string.gsub(s, '^%s+', '')
s = string.gsub(s, '%s+$', '')
s = string.gsub(s, '[\n\r]+', ' ')
return s
end
function sensor_write(mac, handle, value)
-- write value addressed by handle(tag)
temp = 'sudo gatttool -b ' .. mac .. ' --char-write -a ' .. handle .. ' -n ' .. value
os.execute(temp)
end
function sensor_read(mac, handle)
local ht, htt, lt, ltt
-- read values from sensor. values are returned in decimal (signed or unsigned),
-- in accordance to attribute table
local c = 'sudo gatttool -b ' .. mac .. ' --char-read -a ' .. handle
f = os.capture(c,true)
-- debug
-- print(f)
lt, ht, ltt, htt = string.match(f, '(%x%x) (%x%x) (%x%x) (%x%x)')
s1 = ht .. lt
s2 = htt .. ltt
val1 = hex2num(s1,"signed")
val2 = hex2num(s2,"signed")
--debug
-- print(val1,val2)
return val1, val2
end
function hex2num(h, switch)
r = tonumber(h,16)
if (switch == "signed") then
if (r > 32767) then
r = r - 65536
end
end
return r
end
function cal_read(mac, handle)
local c0, c1, c2, c3, c4, c5, c6, c7
-- read values from sensor. values are returned in decimal (sigend or unsigned),
-- in accordance to attribute table
local c = 'sudo gatttool -b ' .. mac .. ' --char-read -a ' .. handle
local f = os.capture(c,true)
--debug
-- print(f)
l0, h0, l1, h1, l2, h2, l3, h3, l4, h4, l5, h5, l6, h6, l7, h7 = string.match(f, '(%x%x) (%x%x) (%x%x) (%x%x) (%x%x) (%x%x) (%x%x) (%x%x) (%x%x) (%x%x) (%x%x) (%x%x) (%x%x) (%x%x) (%x%x) (%x%x)')
c0 = hex2num(h0 .. l0,"no")
c1 = hex2num(h1 .. l1,"no")
c2 = hex2num(h2 .. l2,"no")
c3 = hex2num(h3 .. l3,"no")
c4 = hex2num(h4 .. l4,"signed")
c5 = hex2num(h5 .. l5,"signed")
c6 = hex2num(h6 .. l6,"signed")
c7 = hex2num(h7 .. l7,"signed")
--debug
-- print(c0 .. " " .. c1 .. " " .. c2 .. " " .. c3 .. " " .. c4 .. " " .. c5 .. " " .. c6 .. " " .. c7)
return c0, c1, c2, c3, c4, c5, c6, c7
end
commandArray = {}
-------------data-------------------------------------
------------SENSORTAG BLE MAC ------------------------
-- insert the your SensorTag MAC address
blemac = 'B4:99:4C:64:30:B7'
--------------SENSOR SWITCHES ------------------------
sensor_t = 1
sensor_h = 1
sensor_b = 1
--------------Virtual device ID number ------------------------
-- get your virtual device ID and insert the correct vaules
v_temp1 = 9
v_target = 12
v_temp2 = 10
v_humidity = 14
v_temp3 = 16
v_baro = 15
v_summa = 17
--------------Start with SensorTag measurement activation -----
--------------Thermometer sensor ------------------------------
if sensor_t == 1 then
-- temperature sensor: obtain ambience temperature and target temperature
-- write handle and value
tag = '0x29'
val = '01'
--write remote registry
sensor_write(blemac, tag, val)
end
-----------------Humidity sensor ------------------------------
if sensor_h == 1 then
tag = '0x3f'
val = '01'
--write remote registry
sensor_write(blemac, tag, val)
end
-----------------Barometer sensor ------------------------------
if sensor_b == 1 then
tag = '0x55'
val = '02'
--write remote registry
sensor_write(blemac, tag, val)
--retrive sensor calibration values
tag = '0x5b'
c0, c1, c2, c3, c4, c5, c6, c7 =cal_read(blemac, tag)
--write remote registry
tag = '0x55'
val = '01'
sensor_write(blemac, tag, val)
end
----------------------measurement time-------------------------
os.execute('sleep 0.5')
-------------------- mesurement reading part ------------------
----------- temperature sensor (ambient and target) -----------
if sensor_t == 1 then
-- read tag
tag = '0x25'
-- raw decimal values
tt, ta = sensor_read(blemac, tag)
-- calculation
temp_amb_1 = ta / 128
Tdie2 = temp_amb_1 + 273.15
Vobj2 = tt * 0.00000015625
-- calibration factor Typical values for S0 are between 5 × 10^(–14) and 7 × 10^(–14)
S0 = 6.4e-14
a1 = 1.75e-3
a2 = -1.678e-5
b0 = -2.94e-5
b1 = -5.7e-7
b2 = 4.63e-9
d2 = 13.4
Tref = 298.15
S = S0*(1+a1*(Tdie2 - Tref)+a2 * (Tdie2 - Tref) ^ 2 )
Vos = b0 + b1*(Tdie2 - Tref) + b2 * (Tdie2 - Tref) ^ 2
fObj = (Vobj2 - Vos) + d2 * (Vobj2 - Vos) ^ 2
tObj = (Tdie2 ^ 4 + fObj/S) ^ 0.25
temp_obj = (tObj - 273.15)
----- format values for printout ------------------------------
temp_amb_1 = string.format("%.2f",temp_amb_1)
temp_obj = string.format("%.2f",temp_obj)
commandArray[1] = {['UpdateDevice'] = v_temp1 .. "|0|" .. temp_amb_1}
commandArray[2] = {['UpdateDevice'] = v_target .. "|0|" .. temp_obj}
end
------Humidity sensor(ambient temp & relative humidity)-------
if sensor_h == 1 then
-- read tag
tag = '0x3b'
-- raw decimal values
tt, ta = sensor_read(blemac, tag)
-- calculation
temp_amb_2 = -46.85 + 175.72/65536 * tt
rel_humid = -6.0 + 125.0/65536 * ta
----- format values for printout ------------------------------
temp_amb_2 = string.format("%.2f",temp_amb_2)
rel_humid = string.format("%.2f",rel_humid)
commandArray[3] = {['UpdateDevice'] = v_temp2 .. "|0|" .. temp_amb_2}
commandArray[4] = {['UpdateDevice'] = v_humidity .. "|" .. rel_humid .. "|" .. "0"}
end
--------------barometer sensor (temperature & pressure)--------
if sensor_b == 1 then
-- read tag
tag = '0x51'
-- raw decimal values
tt, ta = sensor_read(blemac, tag)
-- barometer values shall converted to
-- unsigned interger
-- two's complement
if ta < 0 then
ta = ta + 65536
end
-- calculation
temp_amb_3 = ((c0 * tt) / 2^24) + (c1 / 2^10)
sensitivity = (c2 + ((c3 * tt) / 2^17) + ((c4 * tt^2) / 2^34))
offset = (c5 * 2^14) + ((c6 * tt) / 2^3) + ((c7 * tt^2) / 2^19)
press_hPa = ((sensitivity * ta + offset) / 2^14) / 100
----- format values for printout ------------------------------
temp_amb_3 = string.format("%.2f",temp_amb_3)
press_hPa = string.format("%.1f",press_hPa)
commandArray[5] = {['UpdateDevice'] = v_temp3 .. "|0|" .. temp_amb_3}
-- combo temp, humidity and barometer
-- virtual device
commandArray[6] = {['UpdateDevice'] = v_summa .. "|0|" .. temp_amb_3 .. ";" ..rel_humid .. ";0;" .. press_hPa .. ";0"}
end
return commandArray