Introduction
Lots of cool boards for makers and developers have been coming out, and the C.H.I.P. is one that boasts both built-in WiFi and Bluetooth. What really makes the C.H.I.P. standout, however, is that itonly costs $9.
You read that right.
So, this WiFi-based presence detector will only cost you $9. It’ll take you hardly any time at all, and you’ll suddenly have a base for triggering all sorts of things when someone is detected.
Project level: Beginner
Approximate time to complete: An hour or less
Cost: Less than what you paid for lunch (ok, maybe not for you but it's close)
In this step-by-step tutorial, you will:
- connect to the C.H.I.P. for the first time
- setup the C.H.I.P. without a monitor or keyboard/mouse
- setup the C.H.I.P. to scan for devices on a WiFi network and use that to determine who is “home”
- stream collected data to a web service you can access from anywhere
- setup a “service” to run your script every time the C.H.I.P. boots up
Part 1. Equipment
Pages 11
Clone this wiki locally
This is maybe the shortest (and cheapest) supply list I’ve ever written:
- C.H.I.P. (https://getchip.com/pages/store)
Stuff you probably already have:
- Micro USB to USB cable (https://www.amazon.com/Micro-USB-to-Cable/dp/B004GETLY2)
- 5V Micro USB power supply (https://www.amazon.com/Raspberry-Pi-Micro-Supply-Charger/dp/B00DZLSEVI/)
On to setting up the C.H.I.P.!
Part 2. Setting up the CHIP
Pages 11
Clone this wiki locally
I talked a little bit about the C.H.I.P. earlier, and all we’re using it for is it’s built-in WiFi and Python, but here’s a look at all the other stuff you could hook up to it:
So many possibilities.
Another great thing about it is how easy it is to set up without ever connecting it to a monitor. This means that, while you can totally hook the C.H.I.P. up to a TV and game on it with a bluetooth keyboard, you don’t need those things to get it up and running.
Get that Micro-USB to USB cable ready – we’re connecting it to our laptops!
Part 2. Connecting to WiFi
Pages 11
Clone this wiki locally
The entire reason the C.H.I.P. is awesome is it’s built-in WiFi, so let’s get connected!
First, connect the C.H.I.P. to your laptop with the USB cable. Communicating with the C.H.I.P. differs between Windows and Mac:
Mac
- Open Terminal (or your console of choice) – Applications > Utilities > Terminal
- Find the address of your USB port. This should output something like “tty.usbmodem1411”:
cd /dev
ls -ltr /dev/*usb*
- Open a serial connection to the C.H.I.P. from the USB port using the address you just found:
screen /dev/tty.usbmodem1***
- The C.H.I.P.’s username and password are both “chip”, so enter that when prompted. You should see
chip@chip:~$
appear. Scroll down past the Windows section to continue!
Note: Enter Ctrl+A followed by Ctrl+\ to quit this connection
Windows
- Open device manager
- Find the address of your USB port under “Ports”. Find “PI USB to Serial” and note the COM number (like COM4).
- Open putty, choose “Serial” connection
- Using the number from before, open serial line = COM#
- The C.H.I.P.’s username and password are both “chip”, so enter that when prompted. You should see
chip@chip:~$
appear.
Set Network Preferences
Adding your WiFi network info to the C.H.I.P. is super easy:
sudo nano /etc/network/interfaces
If you get prompted for a password, remember, it’s always “chip”!
You should see a file with nothing but comments. At the bottom of the file add:
auto wlan0
iface wlan0 inet dhcp
wpa-ssid YourWiFiSSID
wpa-psk YourWiFiPassword
Save and exit with Ctl-x and y. Now reboot the C.H.I.P. for the settings to update:
sudo reboot
Once the C.H.I.P. reboots (which is pretty quickly), connect to it again using the same steps as before. We want to a) make sure we actually have an internet connection and b) figure out the IP address so we can SSH in.
When you’re connected, check your internet connections with:
sudo ifconfig
Note: At first I thought that the C.H.I.P. didn’t run commands like ifconfig because running without sudo gave an error message sounding like it wasn’t installed – so always try sudo if something doesn’t work!
You’ll see a lot of output, but look for wlan0. You need to see two different series of numbers – one is the local address and the other (which should start with 10) is the address we want:
wlan0 Link encap:Ethernet HWaddr cc:79:cf:21:c8:eb
inet addr:10.10.7.192 Bcast:10.10.7.255 Mask:255.255.252.0
inet6 addr: fe80::ce79:cfff:fe21:c8eb/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:199 errors:0 dropped:0 overruns:0 frame:0
TX packets:74 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:45570 (44.5 KiB) TX bytes:12253 (11.9 KiB)
Write or copy that number down somewhere because you’re going to need it.
If you don’t see a number, then you might not have an internet connection. You can test to make sure your SSID and password work by entering them in the following line:
sudo nmcli d wifi connect WIFISSID password WIFIPW ifname wlan0
Shutdown the C.H.I.P. so that you can unplug it from your laptop. This can be done by either pressing and holding the button on the C.H.I.P. for ~10 seconds or by entering:
sudo shutdown -h now
Plug your power supply into your outlet of choice and then plug it into your C.H.I.P. Using eitherthe terminal or PUTTY, establish an SSH connection with the C.H.I.P. using the IP address you just wrote down or you can try setting up zero configuration networking and use chip.local:
ssh chip@IPADDRESS
ssh chip@chip.local
Note: If you receive a permissions error, you may need to use sudo. Be cautious though – this is your laptop’s password, not the C.H.I.P.’s! You should always pay careful attention when using sudo. On something like the C.H.I.P. it’s not such a big deal, but on your personal or work laptop it could be!
Once you’re in it’ll look the same as when you connected through the USB cable. Now we’re ready to start the fun stuff!
Part 3. Presence Detection
Pages 11
Clone this wiki locally
The way that we’re going to detect “presence” is by scanning the WiFi network for certain devices’ MAC addresses. These addresses are the unique identifier that your phone or laptop gives when connecting to a network. The C.H.I.P. needs to be connected to the same network your device will connect to. If you have 2.4Ghz and 5Ghz options on your router, the C.H.I.P. can only connect to the 2.4Ghz option – no worries, though! It can still detect devices on the 5Ghz band.
Detecting devices can also be done via Bluetooth; however, I hate leaving my Bluetooth on, so I figured WiFi would be a bit more reliable.
Before we get started, we need to make sure that the C.H.I.P. is updated. This could take a little while:
sudo apt-get update
Now let’s move on to scanning for devices!
Part 3. Scanning for Devices
Pages 11
Clone this wiki locally
I decided to use a tool called arp-scan to look for devices on my network. Arp-scan is an Address Resolution Protocol packet scanner that shows every active IPv4 device on your local internet.
To install arp-scan, enter:
sudo apt-get install arp-scan
Once it’s installed, you can test arp-scan by running:
sudo arp-scan -l
You should see a list of devices and corresponding MAC addresses run down the screen (it could take a little while to load if on a large network).
So we can see connected devices! But we want to know when a particular device is connected, which means we need to know that device’s MAC address. I decided that my phone was the best device to use to detect my presence, so I looked up how to get it’s address (this can be done for any other internet connected device too).
On an iPhone
- Go to Settings
- -> General
- -> About
- Scroll down until you see “Wi-Fi Address”
On an Android phone
- Go to Menu
- -> Settings
- -> About Tablet
- -> Status
- Scroll down until you see “Wi-Fi MAC Address”
The series of 12 letters and numbers separated by colons is the phone’s MAC address.
You can check to see if your phone appears on the arp-scan list of devices by running the earlier command and looking for it, or you can run:
sudo arp-scan -l | grep PHONEMACADDRESS
If your phone was found, the command will output its address. If it wasn’t found, make sure that it is connected to the same WiFi network as the C.H.I.P. You may also need to wake up your device as many devices disappear when not actually accessing the internet.
The last thing we need to install is the Initial State python streamer so that we can stream who’s home and who’s not!
Part 3. Initial State
Pages 11
Clone this wiki locally
We want to stream a message every time someone enters or leaves the area to a cloud service and have that service turn our data into a nice dashboard that we can access from our laptop or mobile device.
Step 1: Register for Initial State Account
Go to https://app.initialstate.com/#/register/ and create a new account.
Step 2: Install the ISStreamer
Before we can install the streamer, we need to install the cURL tool. This can be done in the C.H.I.P. GUI using the Synaptic Package Manager (Computer Things! -> Settings) or via the command line:
sudo apt-get install curl
Note: If this returns an error, make sure that you’ve run
sudo apt-get update
Install the Initial State Python module onto your C.H.I.P.:
$ \curl -sSL https://get.initialstate.com/python -o - | sudo bash
Security Note: The above command has some important anatomy that the user should be aware of. 1) There is a preceding
\
beforecurl
. This is important to ensure no alias ofcurl
gets run if one was created. This helps mitigate risk of the command doing more than intended. 2) The command is a piped command, so when running, you are piping the output of a script that is being retrieved fromhttps://get.initialstate.com/python
into the commandsudo bash
. This is done to simplify installation, however, it should be noted thathttps
is important here for helping ensure no man-in-the-middle manipulation of the install script, especially since the script is being run with elevated privileges. This is a common way to simplify install and setup, but if you are a little more wary there are some slightly less convenient alternatives: you can break the command out into two steps and investigate the bash script being downloaded from the curl command yourself to insure it’s fidelity OR you can follow the pip instructions, you just wont get an automatically generated example script.
Step 3: Make some Automagic
After Step 2 you will see something similar to the following output to the screen:
pi@raspberrypi ~ $ \curl -sSL https://get.initialstate.com/python -o - | sudo bash
Password:
Beginning ISStreamer Python Easy Installation!
This may take a couple minutes to install, grab some coffee :)
But don't forget to come back, I'll have questions later!
Found easy_install: setuptools 1.1.6
Found pip: pip 1.5.6 from /Library/Python/2.7/site-packages/pip-1.5.6- py2.7.egg (python 2.7)
pip major version: 1
pip minor version: 5
ISStreamer found, updating...
Requirement already up-to-date: ISStreamer in /Library/Python/2.7/site-packages
Cleaning up...
Do you want automagically get an example script? [y/N]
(the output may be different and take longer if you have never installed the Initial State Python streaming module before)
When prompted to automatically get an example script, type y. This will create a test script that we can run to ensure that we can stream data to Initial State from our Pi. You will be prompted:
Where do you want to save the example? [default: ./is_example.py]:
You can either type a custom local path or hit enter to accept the default.
You will be prompted for your username and password that you just created when you registered your Initial State account. Enter both and the installation will complete.
Step 4: Access Keys
Let’s take a look at the example script that was created.
$ nano is_example.py
On line 15, you will see a line that starts with streamer = Streamer(bucket_ ...
. This lines creates a new data bucket named “Python Stream Example” and is associated with your account. This association happens because of the access_key=”...”
parameter on that same line. That long series of letters and numbers is your Initial State account access key. If you go to your Initial State account in your web browser, click on your username in the top right, then go to “my account”, you will find that same access key at the bottom of the page under “Streaming Access Keys”.
Every time you create a data stream, that access key will direct that data stream to your account (so don’t share your key with anyone).
Step 5: Run the Example
Run the test script to make sure we can create a data stream to your Initial State account:
$ python is_example.py
Step 6: Profit
Go back to your Initial State account in your web browser. A new data bucket called “Python Stream Example” should have shown up on the left in your log shelf (you may have to refresh the page). Click on this bucket and then click on the Waves icon to view the test data.
You will want to step through the Waves tutorial to familiarize yourself with how to use this data visualization tool. Next, view the data in Tiles to see this same data in dashboard form.
You are now ready to start streaming real data from your C.H.I.P.!
Part 3. The Code
Pages 11
Clone this wiki locally
Time for the fun part.
You can find the presence.py script here: https://github.com/initialstate/CHIP-streaming-presence-detector/blob/master/presence.py
I’m going to go over it briefly so you know what’s happening!
First we import important packages:
import subprocess
from time import sleep
from threading import Thread
from ISStreamer.Streamer import Streamer
“subprocess” allows us to make calls like we would in the command line from the script. “time” is so that we can wait between actions. “threading” is necessary because we are looking for more than one device. Threads are basically separated pieces of code that can run at the exact same time as other threads. “Streamer” is the Initial State streamer that let’s us send data to a web-based dashboard.
Next we do some initializing:
# Edit these for how many people/devices you want to track
occupant = ["Rachel","Josh"]
# MAC addresses for our phones
address = ["6c:xx:xx:xx:xx:xx","18:xx:xx:xx:xx:xx"]
# Sleep for a minute to wait for internet connection
sleep(60)
# Initialize the Initial State streamer
# Be sure to add your unique access key
streamer = Streamer(bucket_name=":homes:Who's Home?", bucket_key="chip_home", access_key="YOUR_ACCESS_KEY")
# Some arrays to help minimize streaming and account for devices
# disappearing from the network when asleep
firstRun = [1,1]
presentSent = [0,0]
notPresentSent = [0,0]
counter = [0,0]
In my use case, I wanted to know when my husband or I were home, so I have an array with our names. The address array contains the corresponding MAC addresses for our phones. If you want to add more than 2 devices, simply add more values to both arrays.
We sleep for a minute before trying to initialize the Initial State streamer because we’ll want to run this script when the C.H.I.P. boots up and need to wait for an internet connection. Then we can create a stream with visible bucket name “:homes:Who’s Home?”, hidden bucket key “chip_home”, and unique access key.
Be sure to replace YOUR_ACCESS_KEY with your Initial State access key!
Because we a) don’t want to stream a ton of messages constantly if a device’s presence hasn’t changed and b) want to wait a reasonable amount of time before declaring a device absent since some phone’s disappear when their screen goes black, we create some arrays to handle when something should be streamed.
Now for the meat of our code:
# Function that checks for device presence
def whosHome(i):
try:
# Loop through checking for devices and counting if they're not present
while True:
# Assign list of devices on the network to "output"
output = subprocess.check_output("sudo arp-scan -l", shell=True)
# If a listed device address is present print and stream
if address[i] in output:
print(occupant[i] + "'s device is connected to your network")
if presentSent[i] == 0:
# Stream that device is present
streamer.log(occupant[i],":house_with_garden:")
streamer.flush()
print(occupant[i] + " present streamed")
# Reset counters so another stream isn't sent if the device
# is still present
presentSent[i] = 1
notPresentSent[i] = 0
counter[i] = 0
else:
# If a stream's already been sent, just wait for 5 minutes
counter[i] = 0
sleep(300)
The function “whosHome()” is where we check for the device and steam if it’s present or not. On line 31, we use arp-scan like before and look for the device address. If it’s there, the following if statement is called. We check to see if “device present” has been streamed and either wait 5 minutes if it has or stream it!
# If a listed device address is not present, print and stream
else:
print(occupant[i] + "'s device is not present")
# Only consider a device offline if it's counter has reached 30
# This is the same as 15 minutes passing
if counter[i] == 30 or firstRun[i] == 1:
firstRun[i] = 0
if notPresentSent[i] == 0:
# Stream that device is not present
streamer.log(occupant[i],":no_entry_sign::house_with_garden:")
streamer.flush()
print(occupant[i] + " not present streamed")
# Reset counters so another stream isn't sent if the device
# is still present
notPresentSent[i] = 1
presentSent[i] = 0
counter[i] = 0
else:
# If a stream's already been sent, wait 30 seconds
counter[i] = 0
sleep(30)
# Count how many 30 second intervals have happened since the device
# disappeared from the network
else:
counter[i] = counter[i] + 1
print(occupant[i] + "'s counter at " + str(counter[i]))
sleep(30)
If the device’s address isn’t present, the else statement is called. Like above, we check to see if “device not present” has been streamed, but only if the device hasn’t shown up on the network in a 15 minute period. This way your device will hopefully be picked up even if it’s been sleeping for a little bit.
# Return on a Keyboard Interrupt
except KeyboardInterrupt:
return
# Start the thread
# It will start as many threads as there are values in the occupant array
for i in range(len(occupant)):
t = Thread(target=whosHome, args=(i,))
t.start()
Because we have multiple devices, we create a thread for each one here. Since threads are almost completely separate from the main script, the keyboard exception is placed inside of the thread function.
Now run the script with:
sudo python presence.py
Watch what prints to the terminal to make sure your devices are being detected and that streaming is working. Head over to Initial State to checkout your streams!
Part 3. Your Personal Dashboard
Pages 11
Clone this wiki locally
After you’ve confirmed your script is working, go check out your dashboard! You should see some tiles with a large emoji home or “no” sign and then a home.
The dashboard isn’t super exciting with just two people, but adding other things like a temperature/humidity reading or the current weather would be both easy and interesting.
The main reason I wanted to do this project was as a proof of concept for triggering some sort of action when a certain device was detected – like playing theme music when I entered the apartment! If I get that working, I’ll be sure to add it here.
Something annoying that I noticed about running the script while SSH’d into the C.H.I.P. was that it quit anytime the SSH connection was severed. To solve that, keep reading on how to run the script at boot.
Part 3. Run the Script from Boot
Pages 11
Clone this wiki locally
Usually if you’re planning on running a script for a long time on a device that you’re SSH’d into, you would add things like nohup and & to your run-the-script command. I tried both of these and a couple variations but my script was always “stopped” when I disconnected from the C.H.I.P.
In order to fix this (and really this was something that I intended to do eventually), I set up the script to run every time the C.H.I.P. booted up.
You can usually do this simply using crontab, but it didn’t seem to work on the first try. So instead, I created a service and used systemd to run that service at boot.
I’ll walk you through the steps here.
- First we need to create the service file inside of the systemd directory. I named mine “presence.service”:
nano /etc/systemd/system/presence.service
- Populate this file with where to find your file and extra “instructions”. If your presence.py file is in the C.H.I.P.’s home directory, then the path is the same as what I have here. The settings under [Unit] are trying to tell the C.H.I.P. not to run the service till after the network is online, however it only seems to wait until the network service has started (hence the 60 second wait in our presence.py script). Save and exit:
[Unit]
Description=start presence.py to check for presence
Wants=network-online.target
After=network-online.target
After=multi-user.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/python /home/chip/presence.py
[Install]
WantedBy=multi-user.target
- Enable the service:
sudo systemctl enable /etc/systemd/system/presence.service
- You can check if the service is at least streaming with:
sudo service presence start
Note: You won’t see any of the debug output you did when running the script manually. Check Initial State to see if any new events have come in! You could even set up your script to stream the debug info instead of print it, but that would be a lot of events!
- And you can check the status of the process with:
sudo service presence status -l
Reboot your C.H.I.P. and see if it’s working!
Now that that’s all done, you can think of cool things to trigger whenever a presence is sensed and just edit the presence.py file!
Part 4. (Bonus) Text Alerts
Pages 11
Clone this wiki locally
If you want a text or email alert every time someone’s device leaves or arrives, you can easily set it up inside of Initial State!
Note: You will need to be on the Pro plan to use triggers
We are going to follow the Trigger notification setup process outlined at here.
Just click on the shelf icon (in the top tray to the left of the Initial State logo) if it’s not already open, and click on “settings” beneath your “Who’s Home?” bucket name. Click on the “Triggers” tab in the settings menu.
You’ll want to set up your triggers like mine on the left. Select the data stream to trigger on (you can use the drop-down list to select from existing streams once a data bucket has loaded or you can type in the stream name/key manually; *note Safari does not support HTML5 dropdown lists). In my example screenshot, I selected both “Rachel” and “Josh”.
Select the conditional operator, in this case ‘=’, and set it equal to either present (which I symbolized with the emoji , aka “house_with_garden”) or whatever you used for not present.
Click the ‘+’ button to add the Trigger condition and choose either email or SMS. Click the ‘+’ button to add the action. You’ll need to input any verification code if adding a new phone number or email to complete setup. Click done to return to the main screen.
Your trigger is now live and will fire when the condition is met!
I set mine up to text me whenever someone was present: