PIO-1 Example

Top  Previous  Next

Let's put all the stuff from the previous pages together and actually develop a transport class! We'll build upon the Promixis PIO-1 hardware. This is a network connected multi purpose input output device. For example it has 4 IR outputs that you can use to control a TV, DVD player, cable box or anything using an IR remote. On top of that it has 3 relay outputs for things like door openers and automatic locks. The PIO-1 also has 2 serial input/outputs.

 

You can find the API documentation here.

 

First off we see that the PIO-1 employs an easy to use text based interface terminated with an end line character "\n". Next we see that the PIO-1 requires a password to be sent before it allows you to change anything. But first things first. Let's create a lua file in the examples folder called "pio1a.lua". We'll create this new file using the class structure of Lua.

 

local Base = require('Class') -- the base class for this object.

 

-- Includes that we'll need

local Promixis = Promixis

local transport = require('transport')

local print = print

local timer = require("timer")

local table = require("table")

local ipairs = ipairs

 

module (...)

 

Base:subclass( _M)

 

function init ( self, address, port, password )

 

 Base.init(self)

 

 self.password = password

 self.address = address

 self.port = port

 

 if not self.port then

         self.port = 6000        

 end

 if not self.password then

         self.password = "cookie"

 end

         

end

 

function deinit( self )        

 Base.deinit(self)

 

end

 

The code above will provide the framework where all the functionality will be placed. Note that the code above doesn't deal with the transport functionality yet. All it does is store the address and port information and provide the class framework. Now let's add the connect function. It will create a connection object and setup it's parser and callback.

 

local connectionCallback = function(self, event, reason )

 print(event,reason)

end

 

function connect( self )

 

 self.connection = transport.new ( Promixis.Transport.Connection.Type.CON_TCP, self.address, self.port, function ( event, reason )                

         connectionCallback(self, event,reason)

 end)

 

 self.connection:parser( Promixis.Transport.IParser.Type.PARSER_TERMINATED, "\r" )

 self.connection:connect()

 

end

 

Place this code above the init function. The first line we should look at is the transport.new line. This create a TCP connection object connecting to address self.address and port self.port. It also supplies the callback for the connection events. This will allow use to handle connecting, disconnect and connect failure events.

 

To actually use this code we'll create a script action on the Girder tree with this code:

 

local PIO1a = require("pio.pio1a")

 

local address = '192.168.1.107'

 

pio1 = PIO1a.new( address )

pio1:connect()

 

Running this if the IP address is correct will print a single lone "0". Looking up the status codes we find this means Promixis.Transport.IConnectionCallback.Status.CONNECTION_ESTABLISHED. So great! We have a connected object. Now we'll need to send some data over the link. This is done using transactions.

sendCCF

Let's implement the sendccf command of the PIO-1. The API states this to be sendccf bitmask,repeats,ccf\n Easy enough! The PIO-1 will respond with ok if the command is accepted and irdone when the IR code has been completed.

 

function sendCCF ( self, bitmask, repeats, ccf, callback )

 

 if not self.connection then

         return false

 end

 

 local retries = 0

 local devices = {}

 

 local tx = self.connection:newTransaction(

         function()

                 -- sent

                 print("sendIR / Data was sent...")

         end,

         function( data )

                 -- data

                 print("sendIR / Received: ", data)

                 

                 if data == "irdone" then

                         callback( true )                        

                 elseif data == "ok" then

                         print("CCF code sending...")

                         return TXR.TX_RESET_TIMEOUT

                 elseif data == "error 2,0" then

                         print("PIO was busy try again")

                         return TXR.TX_REQUEUE

                 else

                         print("Error could not send")

                         callback(false)

                         return TXR.TX_REMOVE

                 end

         

 

         end,

         function ()

                 -- timeout

                 retries = retries + 1

                 if retries > 5 then

                         print("sendIR / Failed to get devices")

                         callback( false )

                         return TXR.TX_REMOVE

                 else

                         print("sendIR / Retrying...")

                         return TXR.TX_REQUEUE_FIRST

                 end

         end

 )

 

 tx:timeout(5000)

 tx:data("sendccf " .. bitmask .. "," .. repeats .. "," .. ccf .. "\n")

 tx:persistent(false)

 

 self.connection:send(tx, true)

 

end

 

Easy as that! It's pretty much the same code as before with one significant change. When the PIO-1 returns "ok" to signal acceptance of IR code the transaction timeout is reset by returning TXR.TX_RESET_TIMEOUT.

To call this code simply add this to a lua scripting action.

pio1:sendCCF(8,20,'0000 006E 0022 0022 0156 00A9 0015 0012 0015 003F 0015 003F 0015 003F 0015 0012 0015 003F 0015 003F 0015 0012 0015 003F 0015 0012 0015 0012 0015 0012 0015 003F 0015 0012 0015 0012 0015 003F 0015 0012 0015 003F 0015 0015 0015 0012 0015 0015 0015 0012 0015 0015 0015 0015 0015 003F 0015 0015 0015 003F 0015 003F 0015 003F 0015 003F 0015 003F 0015 003F 0015 081F 0156 00A9 0015 0015 0015 003F 0015 003F 0015 003F 0015 0015 0015 003F 0015 003F 0015 0015 0015 003F 0015 0015 0015 0015 0015 0015 0015 003F 0015 0015 0015 0015 0015 003F 0015 0015 0015 003F 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003F 0015 0015 0015 003F 0015 003F 0015 003F 0015 003F 0015 003F 0015 003F 0015 081F', function( success)

  print("IR sent:", success)

end)

 

Persistent transactions

The PIR-1 supports asynchronous notifications of incoming IR codes or state changes on the digital pins. Let's add a callback for the IR code notification.

 

function listenForIR ( self, callback )

 

 if not self.connection then

         return false

 end

 

 local tx = self.connection:newTransaction(

         function()

         end,

         function( data )

                 

                 if  string.find(data, "^nec") or

                         string.find(data, "^rc5") or

                         string.find(data, "^rc6") or

                         string.find(data, "^sirc") then        

                         callback(data)

                         return TXR.TX_KEEP

                 else

                         return TXR.TX_CONTINUE

                 end

 

         end,

         function ()

         end

 )

 

 tx:persistent(true)        

 self.connection:send(tx, true)

 

end

 

This again looks similar to the previous functions. However we now do not need to set the timeout nor the data to send and set persistent to true. You can also see two new transaction return constants. TX_KEEP and TX_CONTINUE. TX_KEEP means keep the transaction in the list but do not pass the data to other transactions. TX_CONTINUE means keep the transaction yet pass the data to other transactions as well.

 

To use this code modify your startup lua action as follows:

 

local PIO1a = require("examples.pio1a")

 

local address = '192.168.1.107'

 

pio1 = PIO1a.new( address )

pio1:connect()

pio1:sendCCF(8,20,'0000 006E 0022 0022 0156 00A9 0015 0012 0015 003F 0015 003F 0015 003F 0015 0012 0015 003F 0015 003F 0015 0012 0015 003F 0015 0012 0015 0012 0015 0012 0015 003F 0015 0012 0015 0012 0015 003F 0015 0012 0015 003F 0015 0015 0015 0012 0015 0015 0015 0012 0015 0015 0015 0015 0015 003F 0015 0015 0015 003F 0015 003F 0015 003F 0015 003F 0015 003F 0015 003F 0015 081F 0156 00A9 0015 0015 0015 003F 0015 003F 0015 003F 0015 0015 0015 003F 0015 003F 0015 0015 0015 003F 0015 0015 0015 0015 0015 0015 0015 003F 0015 0015 0015 0015 0015 003F 0015 0015 0015 003F 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 0015 003F 0015 0015 0015 003F 0015 003F 0015 003F 0015 003F 0015 003F 0015 003F 0015 081F', function( success)

  print("IR sent:", success)

end)

pio1:listenForIR(function(ir)

  print("IR Received:", ir)

end)

 

The full source

You can find the latest version of the full source code in the Lua\pio1\init.lua file. Below is a copy of the file with automatic reconnecting and a few more useful functions.

 

 

local Base = require('Class')

 

local Promixis = Promixis

local transport = require('transport')

local string = require('string')

local print = print

local timer = require("timer")

local table = require("table")

local ipairs = ipairs

local tonumber = tonumber

local gir = gir

 

module (...)

 

Base:subclass( _M)

 

local TXR = Promixis.Transport.ITransactionCallback.Results

 

 

function listenForIR ( self, callback )

 

 if not self.connection then

         return false

 end

 

 local tx = self.connection:newTransaction( 

         function()

         end, 

         function( data )

                 

                 if  string.find(data, "^nec") or

                         string.find(data, "^rc5") or

                         string.find(data, "^rc6") or

                         string.find(data, "^sirc") then                                

                         callback(data)

                         return TXR.TX_KEEP

                 else

                         return TXR.TX_CONTINUE

                 end

 

         end, 

         function ()

         end

 )

 

 tx:persistent(true)        

 self.connection:send(tx, true)

 

end

 

function getmac( self, callback )

 

 if not self.connection then

         return false

 end

 

 if not callback then

         callback = function( ) end

 end        

         

 local retries = 0

 

 local tx = self.connection:newTransaction( 

         nil, function( data )

                 -- data

                 

                 local _,_, mac = string.find(data, "^getmac (%x%x:%x%x:%x%x:%x%x:%x%x:%x%x)")

                 if mac then

                         callback(mac)

                 end

                 

         end, 

         function ()

                 -- timeout

                 retries = retries + 1

                 if retries > 5 then                                

                         callback( false )

                         return TXR.TX_REMOVE

                 else                                

                         return TXR.TX_REQUEUE_FIRST

                 end

         end

 )

 

 tx:timeout(5000)

 tx:data("getmac\n")

 tx:persistent(false)

 

 self.connection:send(tx, true)

 

end

 

function setrelay( self, relay, close, callback )

 

 if not self.connection then

         return false

 end

 

 relay = tonumber(relay)

 if close then

         close = 1

 else

         close = 0

 end

 if relay < 1 or relay > 3 then

         return false

 end

 

 if not callback then

         callback = function( ) end

 end        

         

 local retries = 0

 local devices = {}

 

 local tx = self.connection:newTransaction( 

         function()

                 -- sent

         end, 

         function( data )

                 -- data

                 if data == "ok" then

                         callback(true)

                 end

                 

         end, 

         function ()

                 -- timeout

                 retries = retries + 1

                 if retries > 5 then                                

                         callback( false )

                         return TXR.TX_REMOVE

                 else                                

                         return TXR.TX_REQUEUE_FIRST

                 end

         end

 )

 

 tx:timeout(5000)

 tx:data("setrelay " .. relay .. "," .. close .. "\n")

 tx:persistent(false)

 

 self.connection:send(tx, true)

 

end

 

 

function sendCCF ( self, bitmap, repeats, ccf, callback )

 

 if not self.connection then

         return false

 end

 

 if not callback then

         callback = function( ) end

 end

 

 local retries = 0

 local devices = {}

 

 local tx = self.connection:newTransaction( 

         function()

                 -- sent                        

         end, 

         function( data )

                 -- data                

                 

                 if data == "irdone" then

                         callback( true )                        

                 elseif data == "ok" then                                

                         return TXR.TX_RESET_TIMEOUT

                 elseif data == "error 2,0" then                                

                         return TXR.TX_REQUEUE

                 else                        

                         callback(false)

                         return TXR.TX_REMOVE

                 end

         

 

         end, 

         function ()

                 -- timeout

                 retries = retries + 1

                 if retries > 5 then                                

                         callback( false )

                         return TXR.TX_REMOVE

                 else                                

                         return TXR.TX_REQUEUE_FIRST

                 end

         end

 )

 

 tx:timeout(5000)

 tx:data("sendccf " .. bitmap .. "," .. repeats .. "," .. ccf .. "\n")

 tx:persistent(false)

 

 self.connection:send(tx, true)

 

end

 

 

function getversion ( self, callback )

 

 if not self.connection then

         return false

 end

 

 if not callback then

         callback = function( ) end

 end        

 

 local retries = 0

 local devices = {}

 

 local tx = self.connection:newTransaction( 

         function()

                 -- sent                        

         end, 

         function( data )

                 -- data                        

                 if string.find(data, "^getversion Promixis PIO") then

                         callback(data)

                 end

                 

         end, 

         function ()

                 -- timeout

                 retries = retries + 1

                 if retries > 5 then                                

                         callback( false )

                         return TXR.TX_REMOVE

                 else                                

                         return TXR.TX_REQUEUE_FIRST

                 end

         end

 )

 

 tx:timeout(2000)

 tx:data("getversion\n")

 tx:persistent(false)        

 self.connection:send(tx, true)

end

 

local testconnection = function(self)

 

 getversion(self, function( status )

 

         if not status then

                 self.timer:deinit()

                 self.timer = nil

                 

                 if not self.closing then                        

                         

                         self.connection:close()

                 end

         end

         

 end)

 

end

 

local connectionCallback = function(self, event, reason )

 

 

 if event == Promixis.Transport.IConnectionCallback.Status.CONNECTION_ESTABLISHED then

                         

         listenForIR(self, function( ir )

                 gir.triggerEvent( ir .. " " .. self.mac, 18, Promixis.Event.MOD_ON )

         end)                

         

         getmac(self, function(mac)

                 if mac then                        

                         self.mac = mac

                 end

         end)

         

         self.timer = timer.new( 30000, function(t) 

         

                 testconnection(self)

                 

         end)

         

         self.timer:start()

         

 end

 

 if event == Promixis.Transport.IConnectionCallback.Status.CONNECTION_CLOSED then

         

         if self.timer then

                 self.timer:deinit()

                 self.timer = nil;

         end

         

         if not self.closing then

                 

                 self.connection:reconnect()

         end

         

 end

 

 if event == Promixis.Transport.IConnectionCallback.Status.CONNECTION_FAILED then

         

         if not self.closing then

                 

                 self.connection:reconnect()

         end

 end

 

end

 

function connect( self )

 

 self.connection = transport.new ( Promixis.Transport.Connection.Type.CON_TCP, self.address, self.port, function ( event, reason )                

         connectionCallback(self, event,reason)        

 end)

 

 self.connection:parser( Promixis.Transport.IParser.Type.PARSER_TERMINATED, "\r\n" )

 self.connection:connect()

 

end

 

function init ( self, address, port )

 

 Base.init(self)

 

 self.mac = ''

 self.address = address

 self.port = port

 

 if not self.port then

         self.port = 6000        

 end

         

end

 

function deinit( self )        

 

 Base.deinit(self)

 

 self.closing = true

 self.connection:close()

 

 if self.timer then

         self.timer:deinit()

         self.timer = nil;

 end

 

end