Overview

Although the time has passed to submit to the Home Automation challenge, issues seen previously during the project work have been resolved. In the last post for this project, the GPIO features of the MATRIX Creator were to be shown however there were issues with getting the values consistently from openHAB.  These have since been resolved so now the GPIO ports from P2 on the MATRIX Creator can be used to set and collect port states from the openHAB Basic UI.  The main issue was sending Exec (executable) commands from openHAB to the Python helper script passing multiple parameters. For some reason openHAB would not allow for this, so to resolve the issue, the parameters were packed into a since string to the Python script and then unpacked before sending to the main script to issue commands to MATRIX Creator.   This is the same basic method that was used to send the RGB values from openHAB to the Eveloop LED array on the Creator.  Since this issue was resolved, it is still a valid part of the project and is included, although not expected to be considered as part of the submission.

 

Previous Posts

OpenHAB 2 with Matrix Creator and RasPi 3 A+: Intro

OpenHAB 2 with Matrix Creator and RasPi 3 A+: MATRIX Lite Python

OpenHAB 2 with Matrix Creator and RasPi 3 A+: OpenHAB 2 MQTT Binding

OpenHAB 2 with Matrix Creator and RasPi 3 A+: OpenHAB 2 Exec Binding

OpenHAB 2 with Matrix Creator and RasPi 3 A+: Everloop and demo

 

MATRIX Creator GPIO P2 control in openHAB

 

ITEMS

 

In openHAB 2, the main definitions needed when adding a Thing, are items, sitemap, rules, and things.   An item definitions, defines the main structure of the Thing, such as whether it is Text, Color, a Switch or other Item Types, and the associated channel IDs.  The channel ID can be Exec (executable), MQTT, Network Connection and so on. To communicate with the P2 GPIO ports on the MATRIX Creator, the Exec Binding was used, however instead of configuring this using the Paper UI, these were defined manually due to issues with running commands with multiple parameters in the Thing config from the Paper UI.

 

GPIO .items definition

Group MatrixGPIO
Switch SetMatrixGpio "Set Matrix GPIO" <switch> (MatrixGPIO)  { channel="exec:command:set_matrix_gpio:online" }
Switch SetMatrixRelay "Set Matrix Relay" <switch> (MatrixGPIO)  { channel="exec:command:set_matrix_relay:online" }
String GetMatrixPIR "Matrix PIR" (MatrixGPIO) { channel="exec:command:get_matrix_pir:output", channel="exec:command:get_matrix_pir:run" }
String GetMatrixSwitch "Matrix Switch" (MatrixGPIO) { channel="exec:command:get_matrix_switch:output", channel="exec:command:get_matrix_switch:run" }

 

 

Things

 

A Thing in openHAB 2 is the basic structure of an device or interface added in openHAB.  These Things provide channels that can be used to create different functions which are then used to communicate with the end device or interface. A Thing can be added in the Paper UI, but these can also be added manually in instances where the UI does not provided the necessary options for the Thing.

 

GPIO .things definition

Thing exec:command:set_matrix_gpio "Set GPIO" [ command="%2$s", transform="REGEX((.*))", interval=0, autorun=false ]
Thing exec:command:set_matrix_relay "Set Relay" [ command="%2$s", transform="REGEX((.*))", interval=0, autorun=false ]
Thing exec:command:get_matrix_pir "Get PIR Val" [ command="%2$s", transform="REGEX((.*))", interval=0, autorun=true ]
Thing exec:command:get_matrix_switch "Get Switch Val" [ command="%2$s", transform="REGEX((.*))", interval=0, autorun=true ]

 

Sitemaps

 

Sitemaps in openHAB are used to assemble the Items and Things into a structure that can be viewed using the Basic UI, Android openHAB app and other interfaces. This allows the user to interact with the Things using an Switch,  Color or related type or view data in Text type items to display things such as sensor data, environmental values or other related items and things.  The main format structures are the Frame and Label where items can be grouped into a collection of common items.  Here the GPIO Items are collected under the Heading of MatrixGPIO.

 

GPIO .sitemap definition

Frame label="MatrixGPIO" {   


Text item=GetMatrixPIR label="PIR [%s]" icon="motion" 


Text item=GetMatrixSwitch label="Switch [%s]" icon="motion" 


Switch item=SetMatrixGpio label="Set Gpio" icon="light"


Switch item=SetMatrixRelay label="Set Relay" icon="light"





 

Basic UI view

 

 

PAPER UI Control View

 

 

 

 

Rules

    Rules in openHAB provide a method to customize the actions that are to take when a specific event occurs, such as turn on an LED when a Switch is pressed or send an Exec command to the commandline. With the GPIO configuration, the rules that were used included an Initialization of the MATRIX Creator GPIO ports used (Ports 08, 10, 14 and 15 in this instance), a timer to trigger reading from a PIR sensor and a push button, and capture events from the Basic UI Switch definitions for an LED and a Relay.  When these events occur, the message is created and placed in a String variable and then sent to the command line via the 'executeCommandLine(). method.  This passes the messages to the host OS command line to be run as if the user typed it directly themselves.

 

Example:

    var String RelayInit = gpioCMD +  '-g ' +  '\"' + "init:08:out" + '\"'   <--  Create a Command String   

    executeCommandLine(RelayInit, 1000)                                     <-- Send the string to the command line

 

  NOTE:  The command had to be packed into a single string to be passed to the Python script otherwise it did not seem to work and was not reaching the Python script.

 

 

GPIO .rules file

var String gpioCMD = '/usr/bin/python3 /opt/openhab2/conf/scripts/newTempScript.py '

var gpio14 = '14'
var gON = '1'
var gOFF = '0'

rule "GPIO INIT"
when
    System started
then

    var String PIRInit = gpioCMD +  '-g ' +  '\"' + "init:10:in" + '\"'
    var String SwitchInit = gpioCMD +  '-g ' +  '\"' + "init:15:in" + '\"'
    var String LEDInit = gpioCMD +  '-g ' +  '\"' + "init:14:out" + '\"'
    var String RelayInit = gpioCMD +  '-g ' +  '\"' + "init:08:out" + '\"'
    executeCommandLine(RelayInit, 1000)
    Thread::sleep(10)
    executeCommandLine(PIRInit, 1000)
    Thread::sleep(10)
    executeCommandLine(LEDInit, 1000)
    Thread::sleep(10)
    executeCommandLine(SwitchInit, 1000)
    Thread::sleep(10)
        var String gpioOFF = gpioCMD + '-g ' + '\"' +  "out:08:0" +'\"'
    executeCommandLine(gpioOFF)
    Thread::sleep(10)
end

rule "Get Matrix GPIO"
when
    Time cron "0/10 * * * * ?"
then

    var String getPIRState = gpioCMD +  '-g ' +  '\"' + "in:10" +'\"'
    var String getSwitchState = gpioCMD +  '-g ' +  '\"' + "in:15" +'\"'
    logInfo("Get PIR State", getPIRState)
    logInfo("Get Switch State", getSwitchState)
    var String pState = executeCommandLine(getPIRState, 1000)
    Thread::sleep(5)
    var String swState = executeCommandLine(getSwitchState, 1000)
    Thread::sleep(5)
    GetMatrixPIR.postUpdate(pState)
    GetMatrixSwitch.postUpdate(swState)
    if((pState !== null) && (pState == gON)) {
        logInfo("PIR STATE", pState)
        var String eLoopRed = gpioCMD + '-e' + ' \"'+ 'red' + '\"'
        logInfo("SetELoopRed", "setting everloop to red")
        executeCommandLine(eLoopRed, 1000)

    } else if((swState !== null) && (swState == gOFF)) {
        logInfo("Switch STATE", swState)
        var String eLoopBlue = gpioCMD + '-e' + ' \"'+ 'blue' + '\"'
        logInfo("SetELoopBlue", "setting everloop to blue")
        executeCommandLine(eLoopBlue, 1000)
    } else {
        var String eLoopOff = gpioCMD + '-e' + ' \"'+ 'off' + '\"'
        logInfo("SetELoopBlue", "setting everloop to off")
        //executeCommandLine(eLoopOff, 1000)
    }

end

rule "Set Matrix Gpio"
when
    Item SetMatrixGpio received command
then
    var String LEDInit = gpioCMD +  '-g ' +  '\"' + "init:14:out" + '\"'
    executeCommandLine(LEDInit, 1000)
    Thread::sleep(3)
    if(receivedCommand == ON) {
        logInfo("SetMatrixGpio", "*** Test GPIO ON***")
        var String gpioON = gpioCMD + '-g ' +  '\"' + "out:14:1" + '\"'
        logInfo("SetMatrixGpio", gpioON)
        executeCommandLine(gpioON)
    }
    else {
        logInfo("SetMatrixGpio", "*** Test GPIO OFF***")
        var String gpioOFF = gpioCMD + '-g ' + '\"' +  "out:14:0" +'\"'
        logInfo("SetMatrixGpio", gpioOFF)
        executeCommandLine(gpioOFF)
    }
end

rule "Set Matrix Relay"
when
    Item SetMatrixRelay received command
then
    var String LEDInit = gpioCMD +  '-g ' +  '\"' + "init:08:out" + '\"'
    executeCommandLine(LEDInit, 1000)
    Thread::sleep(3)
    if(receivedCommand == ON) {
        logInfo("SetMatrixRelay", "*** Test Relay ON***")
        var String gpioON = gpioCMD + '-g ' +  '\"' + "out:08:1" + '\"'
        logInfo("SetMatrixRelay", gpioON)
        executeCommandLine(gpioON)
    }
    else {
        logInfo("SetMatrixRelay", "*** Test Relay OFF***")
        var String gpioOFF = gpioCMD + '-g ' + '\"' +  "out:08:0" +'\"'
        logInfo("SetMatrixRelay", gpioOFF)
        executeCommandLine(gpioOFF)
    }
end

 

 

Python Parsing of command string from openHAB.

def main(args):
    #today = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    #print(SEND_CMD)
    if args.pressure:
        #TEMP_CMD = " -p \"temp\"" 
        TEMP_CMD = " -p " + "\"" + args.pressure + "\"" 
    elif args.humid:
        TEMP_CMD = " -hu " + "\"" + args.humid + "\"" 
    elif args.imu:
        TEMP_CMD = " --imu " + "\"" + args.imu + "\""  
    elif args.eloop:
        TEMP_CMD = " --eloop " + "\"" + args.eloop + "\""  
        #print("Send ELoop %s" % TEMP_CMD)
    elif args.color:
        TEMP_CMD = " --color " + "\"" + args.color + "\""  
    elif args.gpio:
        gpioList = args.gpio[0]
        gpioList = gpioList.replace("\"", "")
        gpioList = gpioList.replace(":", " ")
        TEMP_CMD = " --gpio " + gpioList

    else:
        #syslog.syslog("*** OPPS OH! ***")
        TEMP_CMD = " -h" 

    # openhab user is required when running a script with 'sudo -i su -s
    SEND_CMD = SUDO_CMD + "\"" + PYTHON_CMD + " "  + MATRIX_CMD + TEMP_CMD + " | " + GREP_CMD  + "\"" + " openhab" 
    #print("Sending : %s" % SEND_CMD)
    sp.call(SEND_CMD, shell=True)
    del SEND_CMD

 

 

  In the script, the GPIO command line arguments list has the ":" replaced with a blank space (" ") and then the extra quotes are removed and then add the "--gpio" argument before being passed to the main Python script for processing the command to the GPIO port.

 

elif args.gpio:
gpioList = args.gpio[0]
gpioList = gpioList.replace("\"", "")

gpioList = gpioList.replace(":", " ")

TEMP_CMD = " --gpio " + gpioList

 

 

This is a bit kludgy but it is what I came up with to solve the issue of sending commands from openHAB.

 

 

Debug with Syslog

One other issue I ran into was that I could not tell if openHAB actually send the command to the python script or not other than an error that was seen in the Paper UI if there was a syntax error.  Since the command did not seem to be passed to the python script, some 'syslog' messages were added to the script to send a message to '/var/log/syslog.log' when the script was being accessed and what arguments were passed. This allowed me to solve the issue with the commands being sent from openHAB.

 

Example of adding syslog in a Python script to send messages to /var/log/syslog.log

import syslog

    args = parser.parse_args()
    #print("ARGS: %s" % args)
    if (args.gpio):
        syslog.syslog("*** OPEN HAB GPIO ARGS***")

 

Output shown in syslog.log

Mar 17 11:05:01 jomoaplus /newTempScript.py: *** OPEN HAB GPIO ARGS***
Mar 17 11:05:01 jomoaplus /newTempScript.py:  --gpio in 15
Mar 17 11:05:09 jomoaplus /newTempScript.py: *** OPEN HAB GPIO ARGS***
Mar 17 11:05:09 jomoaplus /newTempScript.py:  --gpio init 08 out
Mar 17 11:05:10 jomoaplus /newTempScript.py: *** OPEN HAB GPIO ARGS***
Mar 17 11:05:10 jomoaplus /newTempScript.py:  --gpio out 08 1
Mar 17 11:05:10 jomoaplus /newTempScript.py: *** OPEN HAB GPIO ARGS***
Mar 17 11:05:10 jomoaplus /newTempScript.py:  --gpio in 10
Mar 17 11:05:10 jomoaplus /newTempScript.py: *** OPEN HAB GPIO ARGS***
Mar 17 11:05:10 jomoaplus /newTempScript.py:  --gpio init 08 out
Mar 17 11:05:11 jomoaplus /newTempScript.py: *** OPEN HAB GPIO ARGS***
Mar 17 11:05:11 jomoaplus /newTempScript.py:  --gpio in 15
Mar 17 11:05:11 jomoaplus /newTempScript.py: *** OPEN HAB GPIO ARGS***
Mar 17 11:05:11 jomoaplus /newTempScript.py:  --gpio out 08 0

 

   Here there are a number of messages that are being processed from openHAB back end system where messages are being passed to a Python script which then controls the GPIO interface on the MATRIX Creator.

Example:

  --gpio init 08 out   <-- Initializes GPIO port 08 as an input port

  --gpio out 08 1      <--  Sends a 1 (On) to GPIO Port 08 after processing

 

Using matrix-lite-py Interfaces

The commands with the listed arguments are then sent to another Python script that was discussed in a previous post that is based on the sample matrix-lite-py script.  

 

First the commands are picked up by argparser and passed to the main method.

if __name__ == "__main__":
    import argparse


    parser = argparse.ArgumentParser(description="matrix-lite script")
    parser.add_argument("-g", "--gpio", dest="gpio",
                        action="append", nargs='+', help="run gpio")
                        #action="store_true", default=False,
                        #help="run gpio") 


    args = parser.parse_args()
    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit()


    try:
        main(args)
        #user_input = input()
    except KeyboardInterrupt:
        sys.exit(0)

 

NOTE: argparse is set to append the parameters which creates a Python list

The main method picks up the arguments, parses the commands and arguments and passes them to the appropriate gpio method; InitGPIO, getGPIOState and setGPIOState.

main


def main(args):
    matrixSen = MatrixSensors()
    if args.eLoop:
        matrixSen.runEverloop(args.eLoop)
    elif args.color:
        matrixSen.setEverloop(args.color)
    elif args.gpio:
        if ("run" == args.gpio[0][0]):
            matrixSen.runGPIO()
        elif ("in" == args.gpio[0][0]):
            pinState = matrixSen.getGPIOState(int(args.gpio[0][1]))
            print(pinState)
        elif ("out" == args.gpio[0][0]):
            matrixSen.setGPIOState(int(args.gpio[0][1]), int(args.gpio[0][2]))
        elif ("init" == args.gpio[0][0]):
            matrixSen.initGPIO(int(args.gpio[0][1]), args.gpio[0][2])
        else:
            print ("Opps!")
            print (args.gpio)


    elif args.imu:
        imu_x, imu_y, imu_z = matrixSen.getIMU(args.imu)
        if (args.imu == "euler"):
            print("yaw:%f pitch:%f roll:%f" % (imu_x, imu_y, imu_z))
        else:
            print("x:%f y:%f z:%f" % (imu_x, imu_y, imu_z))
    elif args.humid:
        envCond = matrixSen.getHumid(args.humid)
        print (envCond)
    elif args.pressure:
        press = matrixSen.getPressure(args.pressure)
        print (press)
    elif args.uv:
        print(matrixSen.getUV())
    else:
        print("ERROR: option not recognized!")
        raise Exception('Options not found')

 

runGPIO

The run method from the example is pretty much left in place but first initializes the predefined pins and uses an input pin to read in and then set an output pin based on the input value (1=ON, 0=OFF).

    def runGPIO(self):
        ## GPIO ##
        print(dir(self.gpio))


        self.initGPIO(self.gpioOUT, "out")
        self.initGPIO(self.gpioIN, "in")
        self.initGPIO(self.gpioPIR, "in")


        while True:
            os.system("clear")
            print("GPIO TEST")
            pinState = self.gpio.getValue(self.gpioPIR)
            print("GPIO State: %d" % pinState)
            self.gpio.setDigital(self.gpioOUT,pinState)
            sleep(0.05)

 

initGPIO

Initialization method, initGPIO, receives a pin number and the direction the pin should be set to (IN or OUT)

    def initGPIO(self, pinVal, pinDirect):
        # turn pin 1 on
        self.gpio = hal.gpio()
        if (pinDirect.lower() == "out"): 
            # Set pin to output
            self.gpio.setMode(pinVal,1)
            self.gpio.setFunction(pinVal,0)
            self.gpio.setDigital(pinVal,1)

 

setGPIOState

The setGPIOState method sets the pinState (0 or 1)

    def setGPIOState(self, pinVal, pinState):
        self.gpio = hal.gpio()
        self.gpio.setDigital(pinVal, pinState)

 

getGPIOState

The getGPIOState returns the current state of the pin value (0 or 1)

    def getGPIOState(self, pinVal):
        self.gpio = hal.gpio()
        pinState = self.gpio.getValue(pinVal)
        return pinState

 

 

Config used

 

   MATRIX Creator GPIO Port Mapping

 

GPIO #Device ConnectedFunction
GPIO08RelayOUTPUT
GPIO10PIR SensorINPUT
GPIO14LEDOUTPUT
GPIO15Push ButtonINPUT

 

MATRIX Creator Expansion GPIO

https://matrix-io.github.io/matrix-documentation/matrix-creator/resources/pinout/

 

 

Connection Mess

 

 

 

Video Demo of openHAB control of the MATRIX Creator GPIO

 

 

 

 

Conclusion

This only touches a minor part of the functionality of the MATRIX Creator, specifically the Expansion GPIO ports, however hopefully it is helpful to someone who is looking to use this very versatile device in a project in the future.   As time permits, more content will be added showing other features of the MATRIX Creator.

On to the next project.