Appendix C:Control Driver Example ............................................................................................ C-1
Appendix D:Audio Driver Example ............................................................................................... D-1
iv
Chapter
1
Introduction
NetStreams® ships drivers for a variety of third party systems with the DigiLinX™
system. These drivers include Lutron® for lighting, Aprilaire® for HVAC, and GE
Concord® for security. Custom installers can use these included drivers with a
ControLinX™ to enable DigiLinX control of external systems. Sometimes, the
specific needs of a job require DigiLinX to control different third party systems. The
NetStreams StreamNet™ device driver Application Programming Interface (API) is
designed to enable programmers to interface RS-232 and network devices with the
NetStreams DigiLinX system. This gives custom installers the flexibility they need for
their jobs.
The DigiLinX system is a combination of hardware, software, and firmware. To
understand how to interface third party systems with DigiLinX, programmers must
understand the architecture of the system (see Figure 1-1).
User Interface
API
Configuration
Driver
Software
Firmware
Hardwar e
Figure 1-1DigiLinX Hardware/Software model
In the DigiLinX system, the hardware layer is controlled by firmware. Firmware is
controlled and updated by software, which in turn is controlled by configuration files,
and drives the User Interface. NetStreams’ engineering team has created an API that
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
1-1
Writing StreamNet Device Drivers
enables DigiLinX to control and be controlled by third party systems using a driver
(seeFigure 1-2 ).
Figure 1-2Connecting 3rd Party Systems to DigiLinX
Device drivers allow two-way interaction with third party hardware devices over a
communications subsystem, such as RS-232. The ControLinX device includes the
hardware needed to control external systems.
The DigiLinX API is written in a programming language called Lua. Lua is a free,
lightweight, procedural language that is designed to be fast and easy to learn for most
programmers. Third party drivers must be written in Lua to work with the DigiLinX
API.
Third party drivers are composed of objects called Controls. These Controls are
represented by all of the functions and variables in the driver’s code. Drivers may also
contain code to control subcomponents, called subNodes. DigiLinX treats subNodes
as logical entities for both accepting commands and producing status messages. For
example, in a lighting system, a subNode might represent a specific lighting load.
Commands addressed to this subNode would then only affect that lighting load and all
status messages related to the load would be addressed from the subNode.
Introduction to Lua
Lua is a byte-code interpreted language, similar to Java. It provides a small scripting
and user-customizable interface to a subset of an application written in C or C++.
Where Java is primarily focused on providing all of the tools necessary to write a
complete application, Lua is focused on providing minimal system interfaces and a
tightly coupled interface to an underlying application.
The benefits of Lua include:
1-2
Extension Language – Lua is designed from the ground up to be an extension
language. This is a language solely used to extend the functionality of a larger
application. This means there is a two-way interface that easily allows Lua code to
invoke native subroutines and vice-versa. The vast majority of the functional
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Introduction
portion of the control system can be written in C/C++ while a minimum of device
specific code is written in interpreted Lua.
Minimal System Interface and Dependence – The Lua virtual machine and its
associated parser/compiler are written in ANSI-standard C with a minimum system
interface. There is no operating system (OS) interface defined at the script level.
Native String Manipulation – The bulk of the necessary string operations
(concatenation, searching, formatting, regular expressions, etc.) are implemented
in ANSI C, rather than being implemented in the script language. This provides for
substantially improved performance given that the majority of control applications
are string parsing and generation.
Virtual Machine (VM) implementation – The Lua scripting language is compiled
(either at runtime or in advance) into a VM-interpreted language that is
subsequently run on a virtual machine designed with the language constraints in
mind. This substantially improves performance over similar languages that are
fully interpreted.
Object-oriented – While Lua is not an object-oriented language, there are sufficient
language features that it can be treated as such.
The Lua script engine and its associated tools are licensed under a MIT-style license,
which states that it is copyrighted material but free license is granted for commercial
and non-commercial usage.
For the full Lua license see Appendix A, Lua 5.0 License on page A-1.
Further information on Lua is available either from the web site http://www.lua.org or
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Chapter
2
Activating Drivers within DigiLinX Dealer Setup
Starting with version 1.70, DigiLinX Dealer Setup uses an intelligent lookup to
determine if there are custom written drivers present in the system.
In order to load custom written drivers through DigiLinX Dealer Setup, you must first
create a new folder called “drivers” under your DigiLinX Dealer Setup install
directory and copy the .lua driver file to this directory. In most cases, the directory will
be:
After you have created this folder and copied your driver file into the folder, build
your project normally in DigiLinX Dealer Setup. Add a ControLinX or MediaLinX to
your project, selecting the GUI type from the dropdown menu (for example, Tuner,
Lutron, etc.). When you enter the details for your ControLinX or MediaLinX, Dealer
Setup will detect the custom driver in the drivers folder and present the option to select
a new .lua driver file.
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Chapter
3
Handling Commands
The generation and processing of ASCII-formatted commands is central to the
interaction between various StreamNet devices and hence, the heart of a device driver
is how it receives and process ASCII commands. Figure 3-1 shows how the
StreamNet commands flow.
User Presses GUI
Button
Figure 3-1StreamNet command flow
GUI code generates a
StreamNet ASCII
command and sends it
to the StreamNet device
StreamNet device
receives and parses
command and sends it to
the Lua driver
StreamNet command is
routed to the appropriate
section of the Lua driver
code
As commands are received by the host StreamNet device, they are parsed to separate
out the various addressing fields, ASCII command, and command parameters. Once
the command is completely parsed, it is passed to the device driver for handling by the
appropriate Lua code. Refer to Figure 3-2 to see how commands are routed within the
driver.
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
3-1
Writing StreamNet Device Drivers
NOTE
Streamnet command is routed to the appropriate section of the Lua driver code
Start
Handling Commands
It is always possible to
define an appropriate
command-specific
handler, so that the
default_command_hand
ler is not needed.
Programming bestpractices state that a
fallback
default_command_hand
ler should be defined
even if it is not intended
for use.
Is the command
addressed to a
subnode?
YesYes
Is there a subnode
specific command
handler?
Call subnode specific
command handler
No
No
Is there a Control
specific command
handler?
Yes
Call Control specific
command handler
Is there a subnode
default command
handler?
Yes
Call subnode default
command handler
No
No
Call Control default
command handler
Is there a Control
specific c o mmand
handler?
Yes
Call Control specific
command handler
N
o
Call Control def ault
command handler
Figure 3-2Command routing within the driver
For a device driver to receive and handle commands, it is necessary to define Lua
functions with predefined names based on the command to be handled. The routine
that needs to be defined is “handle_<command>” (where <command> is replaced with
the lower cased version of the ASCII command). For example, if a driver needs to
handle #SET commands, it needs to define a function handle_set within the driver. As
a fall-back mechanism, if no command-specific function is defined, the function
“default_command_handler” will be invoked if defined.
If the command is generic to the entire device, the necessary function should be
defined in the outermost control scope of the Lua file, and will be invoked with only
the command as a parameter.
Example: Handle_Set
function handle_set(cmd)
if(cmd.params[1] == “heat”) then
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Handling Commands
NOTE
if(cmd.command == “debug”) then
… handle the #debug command
end
end
If the command is specific to a particular subNode of the driver, it should be defined as
an element in the subNode. It will be passed the subNode itself as its only argument.
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
NOTE
Chapter
4
Handling Communications
Most device drivers will need some mechanism for talking to a controlled device. In
the StreamNet system, that capability is provided by a generalized I/O Stream
mechanism.
A stream can be created using a string-based configuration string, the precise format of
which depends on the details of the connection. Once the stream is created, the details
of the underlying transport mechanism (such as RS-232, TCP/IP, or some other
mechanism) is identical for all streams. This allows the same driver and code to be
used to control a device regardless of the physical connection type.
Use the “:” syntax to
pass the stream as an
argument to the read
and write functions.
A stream is created with the createStream function:
Once created, the stream can be read from and written to using the read and write
functions:
input = stream:read()
stream:write(input)
Many devices have asynchronous outputs, i.e., outputs that are not in direct response
to some command being sent down. The preferred mechanism of dealing with such
responses is to define and enable an asynchronous response handler:
function input(stream, line)
… handle async response in “line”
end
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Chapter
5
Configuration
As for any other StreamNet service, a StreamNet service with a Lua driver is
configured by including an appropriate service tag within the config_current.xml file.
In the case of standard StreamNet device types, the clause will be automatically
generated, but in the case of custom device types, the clause may need to be manually
generated.
-- lines here are passed to the lua interpreter
before
-- the driver file is loaded
config = {};
config.port = “comm://0;baud=9600”;
</SCRIPT_DATA>
</SCRIPT>
</control>
</service>
The SCRIPT_DATA block defines a section of Lua that will be passed to the
interpreter before the driver is loaded, allowing for installation specific parameters,
such as the port settings above, to be passed to the driver.
NOTE: The serviceType in this example is currently required to be “gpio,” and the
serviceNumber is a device specific constant (always 2 in the case of a CL100
or CL100A)
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Chapter
6
Installing the Driver
After writing a driver, you must load it for use with the DigiLinX network. This
chapter provides step by step procedures for installing and running an example driver
for a Panamax® power conditioner. If you have any questions concerning DigiLinX
Dealer Setup, refer to the DigiLinX Dealer Setup Manual located on the Dealer
Documents page of the NetStreams website.
WARNING! You can only load drivers with DigiLinX Dealer Setup versions 1.5 or
higher.
1. Add a ControLinX configured for General Purpose Driver, Serial (see Figure 6-1).
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Figure 6-8Buttons
Useful Development Tips
Installing the Driver
Always compile your script (using your editor’s compile option, or luac.exe)
before transferring the driver to the ControLinX. While this only checks syntax
errors, it will save time when they appear.
Use the categorized debug logging to filter your messages, and turning on only the
level you need.
If you have communication problems, even if they are only in one direction, verify
that you are using the right COM port parameters, and that you don’t need a null
modem adapter.
Y ou can add the attribute autoStart to the SCRIPT block in config_current.xml
and set it equal to 0 to keep the script from loading automatically.
You can send the ASCII commands #script start and #script stop to
the service for your driver to start and stop the service. This is useful for starting
the script with autoStart=0. You can also stop your script, copy a new version,
and then start it again to run the new version without restarting the device.
Something to keep in mind when using this method is that global variables are kept
between stop and start. Although this can be useful (your current debug filter
settings are kept for example) you need to take care when performing operations
that modify the global state.
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Chapter
7
Generating Status Reports
In almost all cases, the generation of the status reports used by the User Interface is
automated. A status report takes the form of a #report ASCII command:
#report {{<report type=”state” field=value…}}
To update the generated status report is to update the responsible status object with the
appropriate field/value pairs. The status report is then generated and transmitted.
As examples, if the base device needs to generate status reports with a time stamp
field, include the code:
setStatus(“timeStamp”, os.uptime())
Care is automatically taken to insure that excessive status reports are not generated,
rather they are specifically only generated as the status itself is modified.
As another example, if a subNode needs to report a status containing a temperature
field, include the code:
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Chapter
8
Diagnostic Messages
Diagnostic messages can be output to the debug viewer using the debug function.
The debug function operates similarly to print when only one parameter is passed.
When two or more parameters are passed, the first argument is converted to a string
and use to a filter the display of that message, based on the current filter settings.
Filter settings can be changes using the setDebug function or the #debug ASCII
command. Filtering for all categories defaults to off.
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Syntax
Chapter
9
Reference
This section defines the StreamNet extensions to the standard Lua function calls
defined in the Lua manual, which can be found at http://www.lua.org/manual/5.1/.
In the descriptions below, the following notations are used:
aType =indicates that the function returns an item of the indicate type, or that a
variable is of the indicated type
Control
default_handle_command (command)
aType:indicates the indicated function should be invoked or defined with the
“:” operator as the “self” argument of type “aType” must always be
given.
aType. indicates the standard lua table lookup rules apply
For purposes of simplicity, all the basic control functions are directly defined in the
outer scope of a Lua driver. In most cases, the only required entries here are the
handle_command entries necessary to handle the commands important to the driver
itself.
default_handle_command is the last effort command handler and is used only if there
is no handle_command function defined to specifically handle a given command.
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
9-1
Writing StreamNet Device Drivers
NOTE
Strictly speaking, this function need never be defined or used since any command will
first be handled by the type specific handler.
It is always possible to
define an appropriate
command-specific
handler, so that the
default_command_hand
ler is not needed.
Programming bestpractices state that a
fallback
default_command_hand
ler should be defined
even if it is not intended
for use.
handle_command(command)
The single argument is an AsciiCommand table as documented in Chapter 2, Handling
Commands on page 3-1. The driver can define a handle_command method to handle
the incoming ASCII command “#command …”. The driver can define a
handle_command method to handle the incoming ASCII command “#command
…”command method to handle the incoming ASCII command “#command …”
The single argument is an AsciiCommand table as documented in Chapter 2, Handling
Commands on page 3-1.
For more information on command handling see Chapter 2, Handling Commands on
page 3-1.
The driver can define a handle_command method to handle the incoming ASCII
command “#command …”
The single argument is an AsciiCommand table as documented in The driver can
define a handle_command method to handle the incoming ASCII command
“#command …”. The driver can define a handle_command method to handle the
incoming ASCII command “#command …”_command method to handle the incoming
ASCII command “#command …”
9-2
The single argument is an AsciiCommand table as documented in Chapter 2, Handling
Commands on page 3-1.
aNode = getSubNode(strNodeName)
Returns the node associated with strNodeName by fetching subNodes[strNodeName].
Device drivers may replace this routine with one of their own if they wish to create
nodes on the fly as they are addressed by commands. Such an implementation might
look like:
function getSubNode(strNodeName)
if(subNodes[strNodeName] == nil) then
node = createSubNode(strNodeName)
… do some more initialization here
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
setStatus(strField, value)
aString = getStatus(strField, value)
SubNode
aSubNode = createSubNode(strName)
Reference
This extension updates the value of a particular field in the associated status object.
This is shorthand for status.setField(field, value). A status field can be deleted by
setting it’s value to nil.
Returns the value for the field specified, or nil if the field has not yet been defined.
createSubNode is invoked to create a new named subNode. The newly created
subNode is automatically registered with the control by placing it in the subNodes
table using strName as an index. The newly created subNode will have an empty
status object associated with it and only minimal commands will be handled.
aTable = subNodes
subNodes is a table containing each of the subNodes created with createSubNode,
indexed by subNode name.
aString = aSubNode.name
Holds the name of the subNode as set in createSubNode. Once created the name of the
subNode should not be altered.
aStatus = aSubNode.status
This extension holds the status object for the subNode. Under normal circumstances
the setStatus and getStatus methods should be used in lieu of directly accessing the
status object.
aSubNode:setStatus(strField, value)
Updates or creates a status report field with the indicated value. If the value is actually
being changed this will trigger the automatic generation of a status report when the
execution of the current command, timer, or async input function is completed. A field
that is no longer appropriate can be deleted by calling aSubNode:setStatus(strField,
nil)
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
9-3
Writing StreamNet Device Drivers
aString = aSubNode:getStatus(strField, value)
Fetch the most recently assigned value of a status report field. If the report field is
currently undefined, nil will be returned.
aSubNode:default_handle_command(command)
default_handle_command is the last effort command handler and is used only if there
is no handle_command function defined to specifically handle a given command.
Strictly speaking, this function need never be defined or used since any command will
first be handled by the type specific handler.
The single argument is an AsciiCommand table as documented in Chapter 2, Handling
Commands on page 3-1. The driver can define a handle_command method to handle
the incoming ASCII command “#command …”command method to handle the
incoming ASCII command “#command …”
The single argument is an AsciiCommand table as documented in Chapter 2, Handling
Commands on page 3-1.
aSubNode:handle_command(command)
The driver can define a handle_command method to handle the incoming ASCII
command “#command …”
The single argument is an AsciiCommand table as documented in Chapter 2, Handling
Commands on page 3-1.
AsciiCommand
An AsciiCommand object contains the information and parameters contained in an
incoming ASCII command.
aString = command.command
The lower-cased string version of the command itself. For example, in the case of
“#SET FAN,OFF” command.command would contain “set”.
aTable = command.params
A 1-based vector of the parameters specified to the command; e.g., in the case of
“#SET FAN,OFF”, command.params would contain:
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Individual parameters can be referenced as command.params[n] where n is between 1
and the number of command specified.
aString = command.to
The entire to address as specified in the original command. Contains nil if no address
was specified.
aString = command.toNode
The node portion only of the address specified in the original command. If the to
address given was “@Aprilaire~zone1”, then toNode would contain “Aprilaire”.
Contains nil if no address was specified.
aString = command.toSubNode
The subNode portion of the address specified in the original command. If the to
address given was “@Aprilaire~zone1”, then toNode would contain “zone1”.
Contains nil if no address was specified or if no subNode was specified in the original
address.
Reference
aString = command.from
aString = command.fromNode
aString = command.fromSubNode
Stream
The entire from address as specified in the original command. Contains nil if no
address was specified.
The node portion only of the from address specified in the original command. If the
from address given was “:Aprilaire~zone1”, then fromNode would contain
“Aprilaire”. Contains nil if no address was specified.
The subNode portion of the from address specified in the original command. If the
from address given was “:Aprilaire~zone1”, then fromNode would contain “zone1”.
Contains nil if no address was specified or if no subNode was specified in the original
address.
Creating a stream allows a device driver to communicate with an external device by
RS-232 or TCP/IP. More connection types may be defined in the future. Once the
stream has been created, data can be read from or written to the stream with no regard
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
9-5
Writing StreamNet Device Drivers
for the underlying transport mechanism. Facilities are also provided for handling of
asynchronous streams with defined message boundaries with no need on the drivers’
part for explicitly polling the stream.
aStream = createStream(strPort)
Creates a new stream from the string specification in strPort. The form of strPort is:
Where protocol and address are taken from the table below:
comm0 .. # of supported device portsOpens an RS-232 serial connection
{protocol}://{address}[;{options}…]
ProtocolAddressComments
socket{host}:{port}Opens a TCP/IP connection to the indicated
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
NOTE
aString = stream:read(max)
Mixing synchronous
input via read and
asynchronous input via
startAsyncInput will
yield inconsistent and
variable results. If
mixing is required,
async input should
always be stopped via
stopAsyncInput before
read is used.
Reference
In the case of the socket protocol, no options are currently supported.
In the case of the comm protocol, the following options may be specified:
Reads data from the stream and returns it in the form of a string. If max is specified, no
more than max characters will be returned. This call will never block, but rather will
return whatever data is immediately available up to the specified maximum.
stream:write(aString)
Writes data from a string to the stream.
anInteger = stream:available()
Returns the number of bytes of data immediately available on the port.
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Writing StreamNet Device Drivers
NOTE
NOTE
stream:startAsyncInput(aFunction, options)
Mixing synchronous
input via read and
asynchronous input via
startAsyncInput will
yield inconsistent and
variable results. If
mixing is required,
async input should
always be stopped via
stopAsyncInput before
read is used.
Enables asynchronous input on the associated stream. Has completed “messages” are
received aFunction will be invoked with the stream and message as parameters. The
options parameter contains an optional table that can be used to define a “message”:
KeyDefaultNotes
endStringNilIf non-nil defines a string which will be used to recognize the end of a
message. Common values might be “\r”, “\n”, or “\r\n” Completed
messages will contain all the data between endString terminators and the
terminator itself.
timeout0If > 0 specifies the maximum time to wait for any input. Every “timeout” ms
any available data will be passed to the input function.
readIdle0If > 0 specifies the maximum time to wait while no data is being received. If
the readIdle timeout elapses with no new data being received any available
data will be passed to the input function.
maxRead1024The maximum # of characters to process before invoking the input
function.
trailing0If > 0 specifies that additional data should be returned after a message has
be recognized based on an endString. So specifying an endString of “\n”
and a trailing of 2 would return the all text up to and including the newline
and the following two characters.
Asynchronous input is
not truly asynchronous
as the input function
will never interrupt
execution of an
asynchronous stream
handler, command
handler, or timer
handler.
stream:stopAsyncInput()
The options parameter is in the form of a table, such as:
Stream:startAsyncInput(onInput, {
endString = “\r”,
trailing = 2
})
Stops asynchronous input previously started with startAsyncInput.
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
9-9
Writing StreamNet Device Drivers
NOTE
NOTE
Timer
Timers are used to schedule a function to be called at some predetermined time in the
future.
Due to the scheduling
inconsistencies in a
multi-tasking
environment, the only
guarantee that can be
made about when the
timer will be executed is
that it will be executed
no sooner than the time
specified in the
createTimer call. No
real guarantees can be
placed on the maximum
time that will elapse
before the timer is
executed.
Timers are not truly
asynchronous events as
they will never interrupt
execution of an
asynchronous stream
handler, command
handler, or another
timer handler.
aTimer = createTimer(nMS, aFunction)
anInteger = aTimer.duration
Creates a timer object and schedules it for execution in nMS milliseconds. When the
timer executes it will invoke aFunction with the timer itself as it’s sole parameter. If
the timer function returns a non-zero value, the return value will be used to reschedule
the timer at another future time.
The duration last specified for the timer, either in createTimer, queue, or as a return
value from timer execution.
aTimer:cancel()
Cancels the timer and prevents any future execution until queue is called.
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
NOTE
aTimer:queue(nMS)
Debug
debug(category, …)
Reference
Queues the timer object to be executed in nMS milliseconds subject to the vagaries of
operating system scheduling. If the timer is already queued for execution, the timeout
will be rescheduled.
The category is coerced
to a lower case string
before any processing is
done. The special
category “all” controls
all categories. The
default filtering for all
messages is off.
setDebug(category, <”on”|”off”|”toggle”>)
Displays in the Debug Viewer the remaining (…) parameters if debug filtering is on
for category. If there is only one parameter, it is always displayed in the debug
viewer. Parameters are displayed concatenated with a tab character.
Enables, disables or toggles display of messages for the given category. If the
given category is “all” then the operation will apply to all messages. The equivalent
ASCII command is:
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Control Driver Example
Appendix
C
Control Driver Example
This example shows how to control a Panamax MAX 5410 Pro AC power source via
its RS-232 interface. The driver will create six control subNodes which will represent
six buttons, each having a distinct function. These buttons can then be displayed on the
DigiLinX GUI. The subNodes will represent the following functions:
subNode Function Description
1 Power On Initiates power-on sequence
2 Power Off Initiates power-off sequence
3 Switched Cycles power to the switched outlets
4 High Current Cycles power to the high current outlets
5 All Off Turns off all outlets
6 Cycle Initiates a power-cycle sequence
The example uses a user created function, createButton, to encapsulate the repetitive
tasks for creating each of the button subNodes. Each subNode will have four
variables, Name, the name of the subNode used for addressing, xmit, the RS-232
string to transmit when the button is pressed, Label, the text that will appear on the
GUI button face, and indicatorState, which will determine whether or not the indicator
LED on the GUI button will be on or off. Each subNode will also contain one
function, handle_button, which is called whenever the corresponding GUI button is
pressed.
Because of the way Lua parses the source files, the functions that the example uses
must be defined before they are actually used, so that is why the functions will appear
first.
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Writing StreamNet Device Drivers
The example also uses two timer driven functions, pollResponse and queryStatus. The
first function, pollResponse, reads data from the serial port every second looking for
messages from the device. Depending on what the response is, the driver may turn the
indicatorState variable on or off (1 or 0 respectively). The second function queryStatus
will send a query to the device every 10 seconds to ensure that the driver stays in sync
with the device. These functions are created by calling the createTimer function.
--
-- The service itself is set up with a subNode for
each of the switchable
-- power supplies (switchable outlets and high power
outlets) and a subNode
-- for all power. Each zone responds to a #BUTTON
PRESS by sending out
-- the requested command and then issuing an
?OUTLETSTAT to find out which
-- outlets are on.
-- To allow the GUI to display an appropriate label
on the button, each
-- subNode also reports status in the common form:
-- <report type="indicatorState" label="<label
text>" indicatorState="<0/1>" />
--
--
-- We have to create our functions first then we can
use them below
-- function createButton( Name, Label, xmitString )
--
-- create a button node
--
function createButton( Name, Label, xmitString )
-- create a subNode for each button
local node = createSubNode(Name)
--
-- the serial to xmit string that dealer setup writes
Main +1 512.977-9393 / fax +1 512.977.9398 / Toll Free Technical Support +1 866-353-3496
3600 W. Parmer Lane, Suite 100; Austin, TX 78727 / www.netstreams.com.
Audio Driver Example
Appendix
D
Audio Driver Example
The following example audio and control drivers in commented code-form are
available as further resources.
All drivers can be found in your DigiLinX Dealer Setup folder under the ./upgrades/
[MM-DD-YYYY]/drivers/ folder. Open the .lua file with your preferred text editor to
see the complete commented code created by NetStreams engineers.
Parasound zTuner
-- zTuner.lua
-- driver for Parasound Ztuner V1 and V2
-- file version 1.0.0
-- There are many places where we check to see if we are a V1 or V2 tuner
since their protocols
-- are different. The V2 protocol is more straight forward than the V1 as
will be evident in
-- how much extra work is required to setup the outgoing tune commands and
with the extra work
-- required to know what piece of information we are currently expecting
back from the tuner
-- (this is the FREQUENCY_FIRST, LAST, BAND, etc stuff)