Hey Everyone!
Thanks for clicking on my project, let me tell you a little bit of what inspired me to embark on this "adventure". I have been wanting to dip my feet in the home automation scene for a while, but never took the initiative. This all changed one day as I was out browsing the local Best Buy and I saw a promotion for a Google Home Mini bundle with a Smart Bulb. It was a great price so I jumped on it. Excited to get back to my apartment and test it out. I unboxed the kit and started setting up my Google Assistant. After playing with the routines and configurations for what seemed like forever, I decided to get down to why I made the purchase. I jump on top of my bed to reach the light fixture to change out the bulb, or so I thought. As I unscrewed the protective diffusion cover I saw my worst enemy, an integrated LED array of lights. I sat back down on my bed feeling utterly defeated and humiliated. I didn't want to go back to the store to return this bundle as I enjoyed having the NSA spy on me via the Google Home Mini. So I mustered the will to prove them wrong. Enough of that backstory, lets get into how you too can easily enter the home automation scene.
First, let us start of with a basic overview of the overall contraption you see hanging on my wall (with duct tape 😲 and zip ties). I simply have to activate my Google Assistant, be it through my Google Home Mini or on my phone, and give her a command to turn on or off my lights. Google then sends a command to a web service which sends a command to my openHAB server on my Orange Pi Zero (sadly not delicious to ingest 😢). The Orange Pi Zero processes this command and sends instructions to the MSP430G2553 LaunchPad, which then finally tells the servo to rotate to either turn the light switch on or off.
If you're already lost, don't worry, I made a small demonstration which takes this giant paragraph and condenses it into 15 short seconds.
Phew, that was a doozy, but once we break it down it will make more sense. Lets dive right into the tech jargon portion of this project. Bear with me for a little bit, this may seem a little confusing, but after we wire everything, we will be working backwards on this project, since we need to setup the proper infrastructure to be able to send and receive those web commands.
If you've taken a look at the components and parts section you'll know what we need, but let me refresh your memory. We will be commanding the RC Servo via the MSP430 LaunchPad. A small disclaimer, since this was a school project with a deadline fast approaching, I ran out of time to setup proper communication via I2C or UART between the Orange Pi Zero and the LaunchPad, so I will be simply using two GPIO pins to receive the command. With this in mind let's talk wiring.
What we have here is quite simple. The MSP430G2553 LaunchPad will be powered by the micro-USB port which will be tethered to a wall outlet via a phone charger. The rest of the components are powered by a 5V regulator. This regulator will take a 6V 2A connection from the wall outlet and manage it for our devices (when wiring this up, make sure to include the 10uf capacitors). One power line will go into the servo and the other into the Orange Pi Zero. After all our components have power and are properly grounded, we must set up how they will talk. The servo's input is handled by a single port on the LaunchPad which will be setup later as a Timer Compare and Control. Now two GPIO ports from the LaunchPad later setup as inputs will connect to two GPIO ports on the Pi which will be later setup as outputs. This wraps up all the wiring you will need to do.
If you've made it this far, congrats, that was probably the scariest part, because now any mistake you make doesn't have a chance to brick your components. Let's talk about the source code of the LaunchPad. We will be mainly covering the declarations. For the output which tells the servo which angle to go to, we need to setup in TA1CCR1 as shown below (if you want to double check my work you can go to the user manual and look up the port register definitions) :
P2DIR |= 0x10; //P2.4 output direction
P2SEL |= 0x10; //P2.4 config
P2SEL2 &= ~0x10; //P2.4 config
TA1CCR0 = 40000; //PWM Period of 20ms
TA1CCTL2 = OUTMOD_7; //outpout mode: reset/set for servo2
TA1CTL = TASSEL_2 + ID_3 + MC_1; //clock source select SMCLK, Input divider 8 mode, control:up mode
Next lets setup those two GPIO ports, in this case 2.6 and 2.7:
P2DIR &= ~0xC0; // Set Direction for 2.6 and 2.7 to input
P2REN |= 0xC0; // Enable on-board resistors to act as switches
P2OUT |= 0xC0; // Set resistor to pulldown
P2SEL &= ~0xC0; // Set pins to I/O
P2SEL2 &= ~0xC0; // ^^^
After those declarations, lets get to how we process the inputs from the Pi. For simplicity, each GPIO port is responsible for one state of the Light Switch. This means both should be low when not in use, and only of them will be set to high at a time. If it is 2.6 then we turn the light on, if it is 2.7 we turn the light off. Here is how I do it through bit masking:
if ((P2IN &= 0x40) == 0x40) { //p2.6 active // Light On
PulseWidth = onPos;
}else if ((P2IN &= 0x80) == 0x80) { //p2.7 active // Light Off
PulseWidth = offPos;
}else { //when not changing state, sit in neutral position in case google assistant brakes.
PulseWidth = restPos;
}
TA1CCR2 = PulseWidth; //aka to rotate servo to specific angle
To achieve a consistent result you will need to tune your individual servo. Below is a simple way to use a terminal interface like TeraTerm to connect to the uart of the LaunchPad to change variables on the fly, in my case, the desired angle.
if(IFG2&UCA0RXIFG) { // USCI_A0 requested RX interrupt (UCA0RXBUF is full)
IntakeChar = UCA0RXBUF; //read in value sent over UART from terminal port
sendchar(IntakeChar); //echo value back to console
//if statements to determine what angle value is desired to change servo's PW on the fly
if (IntakeChar == 61){ //ASCII char equals to =
angle = angle + 5;
}else if (IntakeChar == 45){ //ASCII char equals to -
angle = angle - 5;
}else{ //any other ASCII char
angle = 90;
}
IFG2 &= ~UCA0RXIFG;
}
This is pretty much the gist of the code on the MSP430G2553 LaunchPad, if you have a proper project creator (if you need help with this let me know so I can send you in the right direction). Time to dive into what you're here for, the openHAB server on the Pi.
On the Orange Pi Zero we have armbian installed and running. If you are not familiar with the Orange Pi series of products (don't worry I wasn't either), they work pretty much the same way a Raspberry Pi does. This means you can look up help for both and will probably get what you are looking for since the Raspberry Pi is more mainstream. The basics for the setup of the Pi:
- Connect to the internet.
- Profit.
I wish it was that simple. In reality, I struggled a lot, but I'm here to share my knowledge. It came through running into the same wall for hours at a time until I realized I didn't have the proper package installed to do what I needed to. I took the hard way around, doing all the manual installations myself as depicted in this step-by-step document. I found out about a simpler and faster method after I was already underway, and being stubborn I decided to not switch and continue to struggle 🙃. The best way if you do not plan on doing anything else on your Pi, is to not install armbian, instead you will want to install openHABian following this step-by-step document for a hassle-free openHAB setup which comes with all the packages you want. Because this feature exists, I will not go into detail on the first method and the endless hours spent installing packages.
Assuming you now have everything properly installed, lets dive into the declarations and setup required for openHAB. I won't lie, this is where I spent most of my time stuck. I had no idea where to find the error logs to find out why things weren't working. Even after find the error logs, they were um, not very descriptive. So I had to troubleshoot my syntax errors in java without knowing what was actually wrong, I did this by completely pivoting the code several times (would not recommend). Let me introduce you to how openHAB works. There are items which is what you would see and interact with in say a switch, stirng, volume knob, slider, etc.. In order for these items to do anything we must have something they can relate to, these are called bindings. These add-ons are what allow openHAB to communicate externally with a magnitude of services ranging from HUE lights to bluetooth to google to twitter. As you can see, the possibilities are endless. I however, will be taking the easy road, so you don't need to buckle up. Outside of bindings, there are a bunch more of add-ons used for actions, transformations, user interfaces, and more, which you can find here. But you may be asking yourself, how do we link our items to these bindings, well we have just the THING for you, a series of objects called things. I kid you not. This naming scheme seems very, um, not complete, but once you get the hang of it, it becomes second nature. One last worthy mention, is how we can automatically get things to work, this is done through rules, which are basically If Statements.
(Insert noise of car tires screeching to a halt) I gotta level with you. I tried to use the GPIO binding in openHAB and I was getting no where, so I decided to take this to outside the server and brute force it. Real quick lets just go over how you will go about declaring the GPIO ports to output and how to set them high and low. You will need to download and install a GPIO repository and setup proper permissions, you can follow the instructions here. Do notice the difference when they write to the GPIO pins and how we will be writing. Ok, now that you know how GPIO ports work, here's how I declare mine in a shell script (named start.sh):
#!/bin/bash
gpio mode 7 out //init port 7 as GPIO output
gpio mode 3 out //init port 3 as GPIO output
gpio write 7 0 //set output of port 7 to low
gpio write 3 0 //set output of port 3 to low
Pretty self explanatory I think. Since we are already in brute force mode, lets just make two more shell scripts, one for telling the LaunchPad to turn on the light (named LightOn.sh):
#!/bin/bash
echo "Get Lit" //write to console
gpio write 3 1 //send signal to MSP to turn on light
sleep 1s //sleep for 1 second so MSP has time to drive servo
gpio write 3 0 //set signal back to 0 so MSP can put servo in neutral
And another to tell the LaunchPad to turn off the light (named LightOff.sh):
#!/bin/bash
echo "Turn Down" //write to console
gpio write 7 1 //send signal to MSP to turn off light
sleep 1s //sleep for 1 second so MSP has time to drive servo
gpio write 7 0 //set signal back to 0 so MSP can put servo in neutral
(Refer back to the schematic to remember that GPIO port 3 on the Pi will send a signal to GPIO port 2.6 on the LaunchPad). Save these scripts somewhere easy to remember, maybe in the scripts folder in the openHAB directory 🤷♂️. (Insert noise of car speeding away to fade back into openHAB talk).
I hope you were paying attention when we were talking about the concepts of openHAB, because I wasn't. All that rambling, but what does it actually mean? The best way to explain is to just show my examples and walk you through it. First, lets set up a simple item, let it be a switch, which will be the indicator for our light. Here is how you would set that up:
Switch TestLight <light>
Wow that was easy! Important: Do read through the comments I made within the code files I attached. Those will tell you where each file needs to reside, as well as the naming convention it must follow. As a general rule, each type declaration will be within the folder in the openhab directory with its name, and the file type MUST BE the type of declaration, in this case " .items
". It does not matter what the file name is, but do try to keep it constant.
I was kidding, there is a little more we need to do in the items file, this part will require us to use the exec bind (You can ignore the other lines in the attached code as those I used for troubleshooting my pains away):
Switch LightOn {channel="exec:command:TurnLightOn:run"} //in charge of running thing TurnLightOn
Switch LightOff {channel="exec:command:TurnLightOff:run"} //in charge of running thing TurnLightOff
Switch StartupGPIO {channel="exec:command:StartupGPIO:run"} //in charge of running thing StartupGPIO
Whats important to note here is the "channel
" portion inside the curly brackets. This is a property of the exec bind. Inside there the only thing you can change is the name in between "command
" and "run
". I will explain this further in detail as we look at the things declarations.
Here we will be linking those names you made up previously to the specific shell scripts we made earlier before the burnout. Inside the quotes you need to place the directory location of where your specific shell file is in. Lets talk about those properties in the back. The interval is default to not 0, therefore if you do not set this to 0, this thing will auto execute every X seconds. Timeout will just be when to kill the scripts in case it gets stuck. Autorun means the script will run when the thing is called, even if not told to run.
Thing exec:command:TurnLightOn [command="/etc/openhab2/scripts/LightOn.sh", interval=0; timeout=5, autorun = false]
Thing exec:command:TurnLightOff [command="/etc/openhab2/scripts/LightOff.sh", interval=0; timeout=5, autorun = false]
Thing exec:command:StartupGPIO [command="/etc/openhab2/scripts/start.sh", interval=0; timeout=5, autorun = true]
We are making great progress and we are close to wrapping this up, keep it up!
Next, let's cover when these items and things will be executed. The rules files has simple If This Then That statements. Follow along this simple rule that triggers when item TestLight changes to ON (soon we will get to how to do this via the Google Assistant) :
rule "Light Update to ON"
when
Item TestLight changed to ON
then
LightOn.sendCommand(ON) //switch item LightOn to ON until its done performing script.
while(LightOn.state != OFF) { //sleep until script is done
Thread::sleep(100)
}
Thread::sleep(400) //give some buffer time to make sure everything runs properly.
YourText.postUpdate(TestLight.state.toString) //send state of TestLight to string in PaperUI
end
This rule can be copied and slightly modified for the opposite case of turning the lights off. What I need to point out, is that when the Orange Pi starts up, the GPIO ports you were using are probably defaulted to not have GPIO functionality. I hate this and didn't figure out how to set an initialization file for them, so I just made a new rule that runs our declaration script when the system turns on, as shown bellow:
rule "Startup GPIO" //Rule file that is triggered when the system starts up
when
System started
then
StartupGPIO.sendCommand(ON)
end
This wraps up the files needed to run the openHAB server for our functionality. There is an extra file called sitemaps, which declares the BasicUI if you wanted to access your server from the web and have a nice interface where you can toggle your light switch from.
So now, how do we connect our server to get those commands from the Google Assistant? We will need to install a system integrator add-on, the openHAB Cloud Connector. This allows your server to be accessed from the web (which is also needed to access the PaperUI remotely). This add-on will have a UUIC and Secret code which you will need to link to your new openhab account at https://myopenhab.org/
Small little hiccup, who actually wants to figure out how the Google API works and struggle with that when website like https://ifttt.com/ exist? This website is Great! It does all that hard work for you. Let me walk you through how to setup an applet to connect your Google Assistant to openHAB. First you need to make an account and link your google account. After, go to create a new applet. For your service, choose Google Assistant
, and for the trigger choose Say a simple phrase.
Below is a sample setup, you can choose up to three different ways of saying your phrase, then choose what you will hear back if the command is received by Google.
After you create this trigger, you will choose your action service to be openHAB. (Do take notice of the hundreds of options for your future home automation endeavors). There is only one action possible, so select the Send a command
. Then select which item you want this action to send a command to, and what the command will be.
NOTE: If your item does not appear on the drop-down, I know why. I struggled with that for an amount of time I should not disclose. Let's talk about it!
I'm not sure why, but items only appear there after 3 conditions are met:
- Item has been set for external use in PaperUI settings
- Item has changed state since previous condition is met
- 15 minutes (that's what everyone told me but mine took forever) have passed since previous condition is met
Sorry for throwing the PaperUI at you like that. This is how you access your server from a web browser if you don't like the traditional command line based method. If you are on the Orange Pi Zero, it does not have a GUI, so you need to do it through a different computer. You will log onto your myopenhab.org account and be greeted by this nice screen:
If it says your server is offline, then refer back to this documentation to help you bring it online via the command line prompts.
If your server is online like mine, click on the link to go to your dashboard, where you will be greeted by 4 more hard life choices. You can explore these later in your own time, right now we are focused on the PAPER UI,
so do click on that please:
Success! Now, as shown below, navigate like this Configuration -> Services -> IO
and click on CONFIGURE
.
You will be prompted with a pop up settings box. We are interested in the section labeled Items to expose to apps such as IFTTT
. Bingo. Click on that and select your switch Item you created before. Click Save
to exit and you can close out of there.
Sidenote: The PaperUI can be a great place to explore the non-code side of openHAB with different add-ons. HOWEVER keep in mind that depending on your internet connection this interface is slow at best. Also, any items you create in code cannot be deleted here, and items created here can be deleted anywhere. Therefore it is preferable to always add your items in the code side, so that you know they cannot go anywhere, as you will also have more configuration options for these items in the .items
file.
Wrapping back to our second condition, there are several ways to do this. You can do it in the command line as shown in the previously linked console documentation, or through the BasicUI if you set that up. After that is done, and you have waited sufficiently, go back to the IFTTT website and check if your item appeared in the drop down.
Given I didn't forget anything (Highly improbable) and you followed all the steps thoroughly (more likely tbh), this should be your outcome: (If it's not post below and I will be glad to help!)
As I posted this project, I realized I didn't once mention the servo switch plate mount. Oops. Yeah so you need to print that little bracket, I used zipties to mount the servo to the bracket. The bracket will use the screws from your current switch plate to hold it in place.
-Mateus de Camargo Jonas
UIUC SE 423 Class Project Submission
Comments