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 Connected | Function |
---|---|---|
GPIO08 | Relay | OUTPUT |
GPIO10 | PIR Sensor | INPUT |
GPIO14 | LED | OUTPUT |
GPIO15 | Push Button | INPUT |
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.