You are on page 1of 8

SmartStruxure

Lite
Solution

Application Notes
AN005 - Lua BACnet Client Operations
Get Control. Get Efficient. Get Value
TM
AN005 - Lua BACnet Client Operations Application Notes AN005
2
Schneider Electric- Small Building Systems Tel. Americas: North Andover, MA 1-800-225-0962 Tel. Europe: Malm, Sweden +46 40 38 68 50 Tel. Asia Pacific: Hong Kong +852 2565 0621
Product Name AN005 - Lua BACnet Client Operations - 02 www.schneider-electric.com/buildings October 2013 ct


2
0
1
3

S
c
h
n
e
i
d
e
r

E
l
e
c
t
r
i
c
.

A
l
l

r
i
g
h
t
s

r
e
s
e
r
v
e
d
.

A
l
l

b
r
a
n
d

n
a
m
e
s
,

t
r
a
d
e
m
a
r
k
s
,

a
n
d

r
e
g
i
s
t
e
r
e
d

t
r
a
d
e
m
a
r
k
s

a
r
e

t
h
e

p
r
o
p
e
r
t
y

o
f

t
h
e
i
r

r
e
s
p
e
c
t
i
v
e

o
w
n
e
r
s
.

I
n
f
o
r
m
a
t
i
o
n

c
o
n
t
a
i
n
e
d

w
i
t
h
i
n

t
h
i
s

d
o
c
u
m
e
n
t

i
s

s
u
b
j
e
c
t

t
o

c
h
a
n
g
e

w
i
t
h
o
u
t

n
o
t
i
c
e
.

Introduction and Context
When integrating SmartStruxure Lite Managers with third-party devices, sometimes a
BACnet client functionality can be useful. This application note gives an overview of the four
basic Lua functions to access remote BACnet points.
Note that all SmartStruxure Lite Managers have full BACnet server capabilities, however
the BACnet client functionality is currently only available through Lua. Also, with the current
basic client capabilities, the maximum number of devices supported on a single BACnet
network is 1000.
NOTE: If using BACnet client tools in version 2.6.x or later, it is necessary to modify your
script to prevent BACnet communication from starting 90 seconds after the controller
initialization.
This can be done by encapsulating your script logic within the following loop:
if scl.sysclock() > 90 then
Case 1: Four Lines
The three basic BACnet commands that can be performed are who-is, read and write
requests for speci points. A fourth function is available to obtain the resulting value of a
read request.
The below shows a simple four-line script that does a who-is, reads a value, displays the
value, does a write.
bacnet.whois(500, 500)
bacnet.read(500, AV1)
print(bacnet.value())
bacnet.write(500, AV1, 32)
AN005 - Lua BACnet Client Operations Application Notes AN005
3
Schneider Electric- Small Building Systems Tel. Americas: North Andover, MA 1-800-225-0962 Tel. Europe: Malm, Sweden +46 40 38 68 50 Tel. Asia Pacific: Hong Kong +852 2565 0621
Product Name AN005 - Lua BACnet Client Operations - 02 www.schneider-electric.com/buildings October 2013 ct


2
0
1
3

S
c
h
n
e
i
d
e
r

E
l
e
c
t
r
i
c
.

A
l
l

r
i
g
h
t
s

r
e
s
e
r
v
e
d
.

A
l
l

b
r
a
n
d

n
a
m
e
s
,

t
r
a
d
e
m
a
r
k
s
,

a
n
d

r
e
g
i
s
t
e
r
e
d

t
r
a
d
e
m
a
r
k
s

a
r
e

t
h
e

p
r
o
p
e
r
t
y

o
f

t
h
e
i
r

r
e
s
p
e
c
t
i
v
e

o
w
n
e
r
s
.

I
n
f
o
r
m
a
t
i
o
n

c
o
n
t
a
i
n
e
d

w
i
t
h
i
n

t
h
i
s

d
o
c
u
m
e
n
t

i
s

s
u
b
j
e
c
t

t
o

c
h
a
n
g
e

w
i
t
h
o
u
t

n
o
t
i
c
e
.

Step 2: Step-by-Step Process
The below shows a more thorough step-by-step example on how to access a remote BACnet
device point in Lua.
if scl.sysclock() > 90 then
if whois ~= true then
bacnet.whois(500, 500) -- Step one: who is? (device or range)
whois = true -- Next run: skip to step two
elseif fetch ~= 0 then
fetch = bacnet.read(500, AV1) -- Step two: read! (specic point)
else
value, stamp = bacnet.value() -- Step three: check value and time
if old_stamp ~= stamp then -- If time differs: value has changed!
old_stamp = stamp -- Keep track of time stamp
print(DEV500.AV1:, value) -- Optional: display the changed value
ME.AV2_Present_Value.value = value -- Optional: keep value locally
end
fetch = false -- Next run: go back to step two (to read again)
end
end
AN005 - Lua BACnet Client Operations Application Notes AN005
4
Schneider Electric- Small Building Systems Tel. Americas: North Andover, MA 1-800-225-0962 Tel. Europe: Malm, Sweden +46 40 38 68 50 Tel. Asia Pacific: Hong Kong +852 2565 0621
Product Name AN005 - Lua BACnet Client Operations - 02 www.schneider-electric.com/buildings October 2013 ct


2
0
1
3

S
c
h
n
e
i
d
e
r

E
l
e
c
t
r
i
c
.

A
l
l

r
i
g
h
t
s

r
e
s
e
r
v
e
d
.

A
l
l

b
r
a
n
d

n
a
m
e
s
,

t
r
a
d
e
m
a
r
k
s
,

a
n
d

r
e
g
i
s
t
e
r
e
d

t
r
a
d
e
m
a
r
k
s

a
r
e

t
h
e

p
r
o
p
e
r
t
y

o
f

t
h
e
i
r

r
e
s
p
e
c
t
i
v
e

o
w
n
e
r
s
.

I
n
f
o
r
m
a
t
i
o
n

c
o
n
t
a
i
n
e
d

w
i
t
h
i
n

t
h
i
s

d
o
c
u
m
e
n
t

i
s

s
u
b
j
e
c
t

t
o

c
h
a
n
g
e

w
i
t
h
o
u
t

n
o
t
i
c
e
.

Case 3: More Options
For a simpler way to change the remote BACnet point specications, another example is
provided below. This Lua uses two top variables to specify the remote device instance and
the object name.
if scl.sysclock() > 90 then
dev_inst = 500 -- Device instance
obj_name = AV1 -- Object type and number
if whois ~= true then
bacnet.whois(dev_inst, dev_inst) -- Step one: who is? (device or range)
whois = true -- Next run: skip to step two
elseif fetch ~= 0 then
fetch = bacnet.read(dev_inst, obj_name) -- Step two: read! (specic point)
else
value, stamp = bacnet.value() -- Step three: check value and time
if old_stamp ~= stamp then -- If time differs: value has changed!
old_stamp = stamp -- Keep track of time stamp
-- Optional: display the changed value
print(DEV .. dev_inst .. . .. obj_name .. :, value)
ME.AV2_Present_Value.value = value -- Optional: keep value locally
bacnet.write(dev_inst, obj_name, value + 1) -- Optional: increase value by +1
end
fetch = false -- Next run: go back to step two (to read again)
end
end
Simply changing the dev_inst and obj_name variables will sufce in using the script in real-life
situations.
Note that as an additional step, the above script example is also writing to the remote object.
This means that every time the script is executed (e.g. every few seconds), the remote
value will be incremented and read back again. As a result, the last optional lines should be
adjusted for the specic needs of the integrator.
AN005 - Lua BACnet Client Operations Application Notes AN005
5
Schneider Electric- Small Building Systems Tel. Americas: North Andover, MA 1-800-225-0962 Tel. Europe: Malm, Sweden +46 40 38 68 50 Tel. Asia Pacific: Hong Kong +852 2565 0621
Product Name AN005 - Lua BACnet Client Operations - 02 www.schneider-electric.com/buildings October 2013 ct


2
0
1
3

S
c
h
n
e
i
d
e
r

E
l
e
c
t
r
i
c
.

A
l
l

r
i
g
h
t
s

r
e
s
e
r
v
e
d
.

A
l
l

b
r
a
n
d

n
a
m
e
s
,

t
r
a
d
e
m
a
r
k
s
,

a
n
d

r
e
g
i
s
t
e
r
e
d

t
r
a
d
e
m
a
r
k
s

a
r
e

t
h
e

p
r
o
p
e
r
t
y

o
f

t
h
e
i
r

r
e
s
p
e
c
t
i
v
e

o
w
n
e
r
s
.

I
n
f
o
r
m
a
t
i
o
n

c
o
n
t
a
i
n
e
d

w
i
t
h
i
n

t
h
i
s

d
o
c
u
m
e
n
t

i
s

s
u
b
j
e
c
t

t
o

c
h
a
n
g
e

w
i
t
h
o
u
t

n
o
t
i
c
e
.

Case 4: Reading Multiple Devices and Characters
When many BACnet remote points need to be read, the below lus function can be used.
This Lua loops through a list of device instances and object names.
if scl.sysclock() > 90 then
obj_list = { -- Local and remote objects list
AV21 = {600, AV1},
AV22 = {700, AV2},
AV23 = {800, AV3}}
if whois ~= true then
bacnet.whois(600, 800) -- Step one: who is? (devices range)
whois = true -- Next run: skip to step two
elseif fetch ~= 0 then
obj_link, obj_info = next(obj_list, obj_link) -- Find next objects pair
if obj_link == nil then -- Loop around on last pair
obj_link, obj_info = next(obj_list, obj_link)
end
dev_inst = obj_info[1]
obj_name = obj_info[2]
fetch = bacnet.read(dev_inst, obj_name) -- Step two: read request!
if fetch ~= 0 then -- On error: print message
print(BACnet read failure on DEV .. dev_inst .. . .. obj_name)
else
old_value, old_stamp = bacnet.value() -- Reset the timeout
end
else
value, stamp = bacnet.value() -- Step three: check value and time
if old_stamp ~= stamp then -- If time differs: value has changed!
ME[obj_link .. _Present_Value].value = value -- Update value
print(string.sub(os.date(), 10, 17), ME. .. obj_link,
DEV .. dev_inst .. . .. obj_name, value) -- Display value
elseif scl.sysclock() - old_stamp > 3 then -- Timeout after 3 seconds
print(BACnet read timeout on DEV .. dev_inst .. . .. obj_name)
end
fetch = false -- Next run: read next object in list
end
end
Simply adjusting the obj_list table content should sufce in using the script in real-life
situations. This specic example fetches three remote points (DEV600.AV1, DEV600.AV2,
and DEV600.AV3) from three different remote BACnet devices. It then stores the results
into local analog values (ME.AV21, ME.AV22, and ME.AV23).
Note the resulting update period for those variables is twice the PG script period,
multiplied by the number of remote objects actively fetched. In this case, since the script
is running every second, and because three objects are read, the resulting typical object
update period is every 6 seconds (1 2 3).
AN005 - Lua BACnet Client Operations Application Notes AN005
6
Schneider Electric- Small Building Systems Tel. Americas: North Andover, MA 1-800-225-0962 Tel. Europe: Malm, Sweden +46 40 38 68 50 Tel. Asia Pacific: Hong Kong +852 2565 0621
Product Name AN005 - Lua BACnet Client Operations - 02 www.schneider-electric.com/buildings October 2013 ct


2
0
1
3

S
c
h
n
e
i
d
e
r

E
l
e
c
t
r
i
c
.

A
l
l

r
i
g
h
t
s

r
e
s
e
r
v
e
d
.

A
l
l

b
r
a
n
d

n
a
m
e
s
,

t
r
a
d
e
m
a
r
k
s
,

a
n
d

r
e
g
i
s
t
e
r
e
d

t
r
a
d
e
m
a
r
k
s

a
r
e

t
h
e

p
r
o
p
e
r
t
y

o
f

t
h
e
i
r

r
e
s
p
e
c
t
i
v
e

o
w
n
e
r
s
.

I
n
f
o
r
m
a
t
i
o
n

c
o
n
t
a
i
n
e
d

w
i
t
h
i
n

t
h
i
s

d
o
c
u
m
e
n
t

i
s

s
u
b
j
e
c
t

t
o

c
h
a
n
g
e

w
i
t
h
o
u
t

n
o
t
i
c
e
.

Case 5: Full Read and Write Script
In a situation where some points need to be updated more often than others, an extra
time condition can be used to only perform read operations when required. On top of the
read interval, the below example script adds the ability to specify which objects should be
written when their value changes.
-- *** Variables Initialization ***
if scl.sysclock() > 90 then
if var_init == nil then
var_init = true
-- Local and remote objects list, in the format:
-- LocalObject = {RemoteDevice, RemoteObject, ReadSecs, Write, KeptValue, KeptSecs}
obj_list = {
AV21 = {600, AV1, 10, false, 0, 0}, -- Read DEV600.AV1 every 10s into AV21
AV22 = {700, AV2, 10, false, 0, 0}, -- Read DEV700.AV2 every 10s into AV22
AV23 = {800, AV3, 30, true, 0, 0}} -- Read every 10s, write if AV23 changes
-- This is just a quick function to print with a timestamp:
function display(...) print(os.date():sub(10, 17), ...) end
end
-- *** Program Execution ***
if whois ~= true then
bacnet.whois(600, 800) -- Step one: who is? (devices range)
whois = true -- Next run: skip to step two
ecount = 0 -- Reset the error count
display(Sending who-is...)
elseif fetch ~= 0 then
repeat
obj_link, obj_info = next(obj_list, obj_link) -- Find next pair
if obj_link == nil then -- Loop around
obj_link, obj_info = next(obj_list, obj_link)
end
obj_item = ME[obj_link .. _Present_Value] -- Keep object pointer
-- Read condition, when time interval has elapsed:
obj_read = (scl.sysclock() - obj_item.time > obj_info[3])
AN005 - Lua BACnet Client Operations Application Notes AN005
7
Schneider Electric- Small Building Systems Tel. Americas: North Andover, MA 1-800-225-0962 Tel. Europe: Malm, Sweden +46 40 38 68 50 Tel. Asia Pacific: Hong Kong +852 2565 0621
Product Name AN005 - Lua BACnet Client Operations - 02 www.schneider-electric.com/buildings October 2013 ct


2
0
1
3

S
c
h
n
e
i
d
e
r

E
l
e
c
t
r
i
c
.

A
l
l

r
i
g
h
t
s

r
e
s
e
r
v
e
d
.

A
l
l

b
r
a
n
d

n
a
m
e
s
,

t
r
a
d
e
m
a
r
k
s
,

a
n
d

r
e
g
i
s
t
e
r
e
d

t
r
a
d
e
m
a
r
k
s

a
r
e

t
h
e

p
r
o
p
e
r
t
y

o
f

t
h
e
i
r

r
e
s
p
e
c
t
i
v
e

o
w
n
e
r
s
.

I
n
f
o
r
m
a
t
i
o
n

c
o
n
t
a
i
n
e
d

w
i
t
h
i
n

t
h
i
s

d
o
c
u
m
e
n
t

i
s

s
u
b
j
e
c
t

t
o

c
h
a
n
g
e

w
i
t
h
o
u
t

n
o
t
i
c
e
.

-- Write conditions:
obj_write = (obj_info[4] -- When writeable, and:
and ((obj_info[5] ~= obj_item.value) -- Either value changed
or (obj_info[6] ~= obj_item.time))) -- Or timestamp changed
until obj_read or obj_write -- Loop until read or write has been found...
-- Step two: read or write request!
if obj_write then
display(ME. .. obj_link .. DEV .. obj_info[1]
.. . .. obj_info[2] .. write: .. obj_item.value)
bacnet.write(obj_info[1], obj_info[2], obj_item.value) -- Write value
obj_info[5] = obj_item.value -- Keep last object value
obj_info[6] = obj_item.time -- Keep last object time
else
fetch = bacnet.read(obj_info[1], obj_info[2]) -- Read at time interval
if fetch ~= 0 then -- On error: print message
display(BACnet read failure on DEV .. obj_info[1] .. . .. obj_info[2])
else
old_value, old_stamp = bacnet.value() -- Reset the timeout
end
end
else
value, stamp = bacnet.value() -- Step three: check value and time
if old_stamp ~= stamp then -- If time differs: value has changed!
obj_item.value = value -- Update value
obj_info[5] = obj_item.value -- Keep last object value
obj_info[6] = obj_item.time -- Keep last object time
display(ME. .. obj_link .. DEV .. obj_info[1]
.. . .. obj_info[2] .. read: .. value)
ecount = 0 -- Reset the error count
elseif scl.sysclock() - old_stamp > 3 then -- Timeout after 3 seconds
display(BACnet read timeout on DEV .. obj_info[1] .. . .. obj_info[2])
-- Send another who-is? after 3 read errors:
ecount = ecount + 1
if ecount >= 3 then
whois = false
end
end
fetch = false -- Next run: read next object in list
end
end
AN005 - Lua BACnet Client Operations Application Notes AN005
8
Schneider Electric- Small Building Systems Tel. Americas: North Andover, MA 1-800-225-0962 Tel. Europe: Malm, Sweden +46 40 38 68 50 Tel. Asia Pacific: Hong Kong +852 2565 0621
Product Name AN005 - Lua BACnet Client Operations - 02 www.schneider-electric.com/buildings October 2013 ct


2
0
1
3

S
c
h
n
e
i
d
e
r

E
l
e
c
t
r
i
c
.

A
l
l

r
i
g
h
t
s

r
e
s
e
r
v
e
d
.

A
l
l

b
r
a
n
d

n
a
m
e
s
,

t
r
a
d
e
m
a
r
k
s
,

a
n
d

r
e
g
i
s
t
e
r
e
d

t
r
a
d
e
m
a
r
k
s

a
r
e

t
h
e

p
r
o
p
e
r
t
y

o
f

t
h
e
i
r

r
e
s
p
e
c
t
i
v
e

o
w
n
e
r
s
.

I
n
f
o
r
m
a
t
i
o
n

c
o
n
t
a
i
n
e
d

w
i
t
h
i
n

t
h
i
s

d
o
c
u
m
e
n
t

i
s

s
u
b
j
e
c
t

t
o

c
h
a
n
g
e

w
i
t
h
o
u
t

n
o
t
i
c
e
.

Simply adjusting the obj_list table content should sufce in using that script in real-life
situations. The above example fetches three remote points (DEV600.AV1, DEV600.AV2,
and DEV600.AV3) from three different remote BACnet devices. It then stores the results into
local analog values (ME.AV21, ME.AV22, and ME.AV23). It performs the read requests at the
specied ReadSecs interval for each object (10 seconds, 10 seconds, and 30
seconds).
This script also writes back, when specied, the object value when it changes, (in the above
example), only if the third line of the if obj_list table has its Write parameter set to true, which
will write ME.AV23, if changed, into DEV600.AV3). It then performs a BACnet write operation
to the remote device, effectively sending out the new value.
Note if the BACnet connectivity is completely lost, an error count gets incremented to trigger
another who-is request periodically (after every 3 failures) to effectively re-acquire the devices
once the connectivity is restored.

You might also like