© John C. Shovic 2016

John C. Shovic, Raspberry Pi IoT Projects, 10.1007/978-1-4842-1377-3_4

4. Changing Your Environment with IOT and iBeacons

John C. Shovic

(1)Liberty Lake, Washington, USA

Chapter Goal: Gather Location Data on Your IOT device: Locate Where You Are and Deliver Location Dependent Data and Conduct Actions on Where You Are

Topics Covered in This Chapter:

  • iBeacons and how can you use them

  • Detecting and reading iBeacons from the Raspberry Pi

  • Finding your location – Trilateralization in a fuzzy world

  • Displaying your Location on a Control Panel

  • Turning lights off and on by your location

In Chapter 3, you saw a flexible, solar powered system to deliver data to the IOT (in this case including the National Oceanic and Atmospheric Administration (NOAA) through the weather interface, CWOP. IOTWeatherPi delivered a lot of information on a regular basis.

In this chapter, we take our new IOT device, IOTBeaconAir, to another level. Now we are collecting less information (and even sending less to the IOT), but we are taking data on the environment (where you are) and using it to change the environment (the amount of lighting where you are). This takes us to quite another level of interaction with the IOT.

The IOTBeaconAir

IOTBeaconAir is a portable Raspberry Pi based project that reads the “advertising” packets emitted by iBeacons, roughly calculates your position, and then turns on lights that are close to you. The Pi then calculates the brightness based on just how close you are. The idea is that you can walk around your house with your Pi and the lights will follow you.

In other words, I am using iBeacons to figure out where my portable Pi is physically located (in a pouch on my hip as I walk around the house) and then I control various devices with the Pi.

The unique aspect of IOTBeaconAir versus the many other extant Pi based iBeacon projects is that I am not programming the Raspberry Pi to be an iBeacon; I am doing the opposite. I am using the Pi to read other iBeacons. I am using specialized iBeacons in this project, but you could also build your own iBeacons out of Raspberry Pi's and then read them via Bluetooth with this project.

This project is based around a portable Raspberry Pi ModelB connected with a Bluetooth 4.0 USB dongle and a Wi-Pi Wireless USB dongle. The completed IOTBeaconAir Portable Pi project is shown in Figure 4-1.

A367492_1_En_4_Fig1_HTML.jpg
Figure 4-1. IOTBeaconAir Portable Pi

IOT Characterization of This Project

As I discussed in Chapter 1, the first thing to do to understand an IOT project is to look at our six different aspects of IOT. IOTBeaconAir is a much simpler project than IOTWeatherPi. Table 4-1 shows our six components.

Table 4-1. Components of the IOTBeaconAir Project

IOTBeaconAir Characterization (CPLPFC )

Aspect

Rating

Comments

Communications

9

WiFi connection to Internet - can do AdHoc mesh-type communication and Bluetooth

Processor Power

8

Raspberry Pi B+ / 256Mb RAM

Local Storage

8

8GB of SD Card

Power Consumption

1

∼300mA consumption - Not reasonable for small batteries

Functionality

8

Full Linux-based system. MySQL, etc.

Cost

1

Expensive for many applications. Board is ∼$25+

Ratings are from 1–10, 1 being the least suitable for IOT and 10 being the most suitable for IOT applications.

This gives us a CPLPFC rating of 5.8, a little less than IOTWeatherPi (6). Great for learning, not so good for deployment for most applications.

No doubt about it, the Raspberry Pi is a very flexible and powerful IOT platform. However, the power consumption, cost, and physical size of the device make it more suitable for prototyping or for stand-alone highly functional IOT units.

How Does This Device Hook Up to the IOT?

With IOTBeaconAir, like the previous chapter, we have a lot of options. We can hook up to the Internet using the WiFi connector. We can use the Bluetooth to hook up to local devices, and we can also use the WiFi in AdHoc mode to make local connections. In this chapter, we will be using the WiFi interface to talk to the wider world of IOT devices.

Hardware List

Following is a list of the hardware you’ll need in order to build this project:

  • Raspberry Pi Model B+

  • Adafruit USB Battery Pack for Raspberry Pi - 10000mAh - 2 x 5V @ 2A

  • Estimote Beacons Developer Kit

  • KST Technologies Particle iBeacons

  • IOGear Bluetooth 4.0 USB Micro Adapter - Model GBU521

  • Wi-Pi Raspberry Pi 802.11n Wireless Adapter

iBeacons

iBeacon is the Apple trademark for a low-powered Bluetooth device. An iBeacon is a low-powered, low-cost transmitter that can notify nearby devices of their presence and a rough approximation of range. There are a number of manufacturers that are producing these devices and most smartphones (and Raspberry Pi's!) can be made to act as an iBeacon. It uses Bluetooth Low Energy (BLE ), also known as Bluetooth Smart iBeacons can also be received on Bluetooth 4.0 devices that support dual mode (such as the IOGear dongle specified above). Note that receiving iBeacons on a generic Bluetooth dongle can be quite problematic. Stick with the IOGear Model GBU521 if you can.

Applications of iBeacons include location-aware advertising, social media check-ins, or notifications sent to your smartphone or Pi. An iBeacon transmits an advertising packet containing an UDID (Unique Device Identifier ) that identifies the manufacturer and then a major and minor number that can be used to identify the specific device. It also sends out an RSSI (Relative Signal Strength Indicator) that can be used to approximate the distance to the iBeacon device.

It is important to note that almost all the logic behind an iBeacon deployment is through the supporting application on the device (a Raspberry Pi in our case). The only role of the iBeacon is to advertise to the device of its own existence at a physical location. In some cases, you can connect to an individual device through the iBeacon's GATT (General ATTribute profile) although some iBeacons have a proprietary interface (like the Estimote iBeacons) that prohibit this.

This requirement of having the application device (like a smartphone or Raspberry Pi) read and take actions on the position of the iBeacons remains a roadblock to widespread adoption of iBeacons in the marketplace. Doing it any other way (say for the iBeacons to detect your phone) is a big privacy concern and so things are likely to stay this way for the foreseeable future. See Chapter 7, “Computer Security and the IOT,” for a number of reasons that this is a good thing.

The iBeacons we used are shown in Figure 4-2.

A367492_1_En_4_Fig2_HTML.jpg
Figure 4-2. iBeacons

I used two types of iBeacons: Estimote and KS Technologies Particles. Both worked adequately with regard to receiving advertising packets, but the Estimote beacons have a proprietary interface that makes it not Linux and Raspberry Pi friendly, so I recommend the Particle iBeacons because you can read and write to the devices from the Raspberry Pi. The Estimote only supports a proprietary SDK on Android and iPhone. Of course, you can always roll your own iBeacon using a Raspberry Pi [ www.wadewegner.com/2014/05/create-an-ibeacon-transmitter-with-the-raspberry-pi/ ]. The Particle iBeacon is shown in Figure 4-3.

A367492_1_En_4_Fig3_HTML.jpg
Figure 4-3. Inside of a Particle iBeacon

There are four major pieces of software in IOTBeaconAir: the Bluetooth iBeacon Scanner, the Philips Hue interface, the main IOTBeaconAir software, and the RasPiConnect Server software.

Bluetooth iBeacon Scanner

Technically, this was the most difficult part of the IOTBeaconAir system. The software available to do this was not very reliable and did not produce the kind of information I was interested in. Figure 4-4 shows the iBeacons near to my lab bench using the BTLExplorer App on my iPhone from KS Technologies.

A367492_1_En_4_Fig4_HTML.jpg
Figure 4-4. BTLExplorer App Showing iBeacons

Note that we are picking up on an Estimote beacon and two Particle beacons. Interestingly enough, we are also picking up an Apple TV located about 40 feet away. I was not aware that the Apple TV was broadcasting an iBeacon packet, but on checking it is used for an undocumented way of setting up the Apple TV from your iPhone. The numbers don't make a lot of sense in the iBeacon advertising packet, but that is a problem for another day.

The biggest issue with this project was to be able to reliably read iBeacon data from a Bluetooth Dongle (I'm using an IOGear Bluetooth 4.0 USB Micro Adapter - Model GBU521). A number of the methods out there on the Web were less than satisfactory (doing hcidump scans) and often ended up hanging the Bluetooth on the Pi, requiring a reboot. Once I went to using my software library, I have zero hang-ups and the software runs for days.

iBeacons use Bluetooth Low Energy (BLE) protocols to communicate, which is a relatively new type of Bluetooth and has spotty support. Finally I stumbled upon a program using blueZ (Linux Bluetooth Library) native calls, which with a lot of modifications, bug fixes, and cutting of code I didn't need, I had a iBeacon scanner that worked every time. I have posted my working version on the SwitchDoc Labs github ( github.com/switchdoclabs/iBeacon-Scanner- ) so you can download it and test your setup.

The blescan.py program is easy to test and use but requires some setup on the Raspberry Pi. See the section below on installing all the required software on the Raspberry Pi.

Here is the output from the programming running in SwitchDoc Labs. We have a lot of iBeacons sitting around.

pi@BeaconAir ∼/blescanner $ sudo python testblescan.py
ble thread started
----------
cf:68:cc:c7:33:10,b9407f30f5f8466eaff925556b57fe6d,13072,52423,-74,-78
cf:68:cc:c7:33:10,74696d6f74650e160a181033c7cc68cf,46608,13255,-52,-77
da:f4:2e:a0:70:b1,b9407f30f5f8466eaff925556b57fe6d,28849,11936,-74,-79
da:f4:2e:a0:70:b1,74696d6f74650e160a18b170a02ef4da,46769,28832,46,-78
dd:5d:d3:35:09:dd,8aefb0316c32486f825be26fa193487d,1,1,-64,-78
c3:11:48:9b:cf:fa,8aefb0316c32486f825be26fa193487d,0,0,-64,-73
fd:5b:12:7f:02:e4,b9407f30f5f8466eaff925556b57fe6d,740,4735,-74,-79
fd:5b:12:7f:02:e4,74696d6f74650e160a18e4027f125bfd,46820,639,18,-80
dd:5d:d3:35:09:dd,8aefb0316c32486f825be26fa193487d,1,1,-64,-77

We are finding eight different iBeacons, which matches the actual count. Before you can do this, you need to install the latest version of bluez, the Bluetooth stack for the Raspberry Pi (instructions below). Note: You could use apt-get, but the apt-get version is old and has patchy support for iBeacons.

Phillips Hue Lighting System

The Phillips Hue lighting system is a Zigbee-based wireless way of controlling intensity, color combinations, and on/off from a Phillips Hub based on your local network. The standard apps for Android and iOS are very powerful, but for us Raspberry Pi people, the best part is that Phillips has released the API for the hub for the DIY crowd. It's somewhat expensive ($60/bulb) but robust and very easy to use and to hack. All commands are sent via wireless or Ethernet to the Phillips Hue Hub and the Hub communicates to the individual devices.

What Is Zigbee ?

ZigBee is a joint specification for a suite of high-level communication protocols used to create personal area networks built from small, low-power digital radios. In this regard, it is very similar to Low Power Bluetooth. The transmission distance is limited to about 10–100 meters, depending on the power output and the transmission environment. The Phillips Hue ZigBee devices work well within a house, but sometimes you will see lights jump on and off the network in what seems to be humidity-related events. Given the frequencies and low-power characteristics of ZigBee, this could certainly be the case.

The ZigBee technology is meant to be simpler and less expensive than Bluetooth and WiFi, not only in dollar cost, but also processor overhead to deal with the communications channel. Just imagine what the processor has to do to interpret an incoming TCP/IP packet and get to the user data. ZigBee can be used without the really “heavy” protocol stack to communicate with local devices through a mesh network to reach more distant Zigbees (see Chapter 6) and then pass to a “beefier” processor, such as a Raspberry Pi to send information into the IOT on the Internet.

ZigBee is typically used in low-data rate IOT applications that require long battery life (a very important feature!) and secure networking. ZigBee networks are secured by 128-bit symmetric encryption keys. See Chapter 7 for a discussion of encryption keys. ZigBee has a defined rate of 250 kbit/s, which is not very fast for web access but you can send a lot of data with that speed.

The ZigBee name refers to the waggle dance of honey bees after their return to the beehive .

Phillips Hue Hub

The Phillips Hue hub communicates via authenticated JSON packets. There are a number of Python packages designed for communication with the Phillips Hue Hub. We chose to use one written by Studio Imaginaire ( studioimaginaire.com/en ) called phue. It is a group of smart French people that did a great job producing the phue library. Considering the IOTBeaconAir logo was designed in France, it seemed appropriate to use this library. You can download it at github.com/studioimaginaire/phue . See installation instructions below.

Our test rooms for IOTBeaconAir has 10 Phillips Hue A19 Standard bulbs, 3 Phillips Hue BR-30 down wash lights, and 2 Phillips Friends of Hue Bloom lights. It was expensive but worth it (the A19 bulbs are $60 apiece, BR30 bulbs $60 apiece, and the Blooms are $80 apiece). These prices should decrease in the future.

BeaconAir Hardware, Software, and Configuration

To work with the BeaconAir, you need to know about the hardware and software. You also need to know about how the software is configured. The following subsections cover each of these topics.

BeaconAir Hardware Description

The IOTBeaconAir hardware is pretty straightforward. We use a stock Raspberry Model B+ with a Wi-Pi WiFi USB dongle and an IOGear Bluetooth 4.0 USB dongle. Everything else is done in software. Figure 4-5 shows the system, as well as showing how iBeacons that allow us to find the approximate physical position of our IOTBeaconAir Portable Pi.

A367492_1_En_4_Fig5_HTML.jpg
Figure 4-5. IOTBeaconAir System Diagram

BeaconAir Software Description

The IOTBeaconAir software consists of four major pieces. I have described the iBeacon Scanner and the Phillips Hue Python library phue above. The two major pieces remaining are the main program loop and the RasPiConnect Local.py / control panel.

The IOTBeaconAir software block diagram is shown in Figure 4-6.

A367492_1_En_4_Fig6_HTML.jpg
Figure 4-6. IOTBeaconAir Software Block Diagram

The main software runs in a loop, with a sleep of 0.25 seconds at the end. It checks for two sources of work. First it checks a queue that is connected to the iBeacon Scanning software running in a background thread. If the queue is empty, we have no new iBeacon reports so we go down and check to see if there are commands waiting from the RasPiConnect control panel.

if (queueBLE.empty() == False):
        result = queueBLE.get(False)


   # process commands from RasPiConnect
        processCommand()

If the queue has iBeacon results to deliver, we then go through the main loop and process the iBeacon information, set various informational parameters, build the new web page to deliver to RasPiConnect, and control the lights.

I have removed the debugging information to make things clearer. All calculations are done in meters and converted to pixels for display.

The first thing we do is process the incoming iBeacon list to fill our beacon arrays. We then clear out old values.

result = queueBLE.get(False)
utils.processiBeaconList(result,currentiBeaconRSSI, currentiBeaconTimeStamp,rollingiBeaconRSSI)
utils.clearOldValues(10,currentiBeaconRSSI, currentiBeaconTimeStamp,rollingiBeaconRSSI)

Next we calculate the current IOTBeaconAir physical position but only if we have greater than three beacons.

# update position
if (utils.haveThreeGoodBeacons(rollingiBeaconRSSI) >= 3):
        oldbeacons = beacons
        beacons = utils.get3ClosestBeacons(rollingiBeaconRSSI)
        if (cmp(oldbeacons, beacons) != 0):
bubblelog.writeToBubbleLog("closebeacons:%i,%i,%i" % (beacons[0], beacons[1], beacons[2]))
        myPosition = utils.XgetXYFrom3Beacons(beacons[0],beacons[1],beacons[2], rollingiBeaconRSSI)

I now have the latest calculated position. Next I calculate the jitter in the position. A big value of jitter says either you are moving or there are significant amounts of noise in the iBeacon reports or both.

# calculate jitter in position
jitter = (((lastPosition[0] - myPosition[0])/lastPosition[0]) + ((lastPosition[1] - myPosition[1])/lastPosition[1]))/2.0
jitter = jitter * 100.0   # to get to percent
lastPosition = myPosition

Now I write out the jitter for RasPiconnect to read and send to the jitter graph on the control panel.

f = open("/home/pi/BeaconAir/state/distancejitter.txt", "w")

f.write(str(jitter))
f.close()

Next I calculate the distance from my position to all the lights and then turn the light on, change brightness, or turn it off depending on the distance.

lights.checkForLightTrigger(myPosition, LIGHT_DISTANCE_SENSITIVITY, LIGHT_BRIGHTNESS_SENSITIVITY, currentLightState)

Next the web page is built for display on RasPiConnect.

# build webpage
webmap.buildWebMapToFile(myPosition, rollingiBeaconRSSI, currentLightState, DISPLAY_BEACON_ON, DISPLAY_LIGHTS_ON)

Finally, I update the current beacon count and build the graph for display on RasPiConnect.

# build beacon count graph
iBeaconChart.iBeacondetect(rollingiBeaconRSSI)
else:
# lost position
myPosition = [-myPosition[0], -myPosition[1]]

That is the entire main program for IOTBeaconAir.

Following is the full listing of the main program of IOTBeaconAir:

#!/usr/bin/python

# BeaconAir - Reads iBeacons and controls HUE lights
# SwitchDoc Labs February 2016
#
#
import sys
import time
import utils


sys.path.append('./ble')
sys.path.append('./config')


# if conflocal.py is not found, import default conf.py

# Check for user imports
try:
        import conflocal as conf
except ImportError:
        import conf


import bleThread

import lights
import webmap
import bubblelog
import iBeaconChart


from threading import Thread
from Queue import Queue


# State Variables

currentiBeaconRSSI=[]
rollingiBeaconRSSI=[]
currentiBeaconTimeStamp=[]


# Light State Variables

currentLightState= []

LIGHT_BRIGHTNESS_SENSITIVITY = 2.0
LIGHT_DISTANCE_SENSITIVITY = 2.0
BEACON_ON = True
DISPLAY_BEACON_ON = True
DISPLAY_LIGHTS_ON = True


# init state variables
for beacon in conf.BeaconList:
        currentiBeaconRSSI.append(0)
        rollingiBeaconRSSI.append(0)
        currentiBeaconTimeStamp.append(time.time())


# init light state variables
for light in conf.LightList:
        currentLightState.append(0)


lights.initializeHue('192.168.1.6')

lights.setInitialLightState(currentLightState)

# recieve commands from RasPiConnect Execution Code

def completeCommand():

        f = open("/home/pi/BeaconAir/state/BeaconAirCommand.txt", "w")
        f.write("DONE")
        f.close()


def processCommand():
        global LIGHT_BRIGHTNESS_SENSITIVITY
        global LIGHT_DISTANCE_SENSITIVITY
        global BEACON_ON
        global DISPLAY_BEACON_ON
        global DISPLAY_LIGHTS_ON
        global currentLightState


        f = open("/home/pi/BeaconAir/state/BeaconAirCommand.txt", "r")
        command = f.read()
        f.close()


        if (command == "") or (command == "DONE"):
                # Nothing to do
                return False


        # Check for our commands

        print "Processing Command: ", command

        if (command == "BEACONON"):
                BEACON_ON = True
                completeCommand()
                return True


        if (command == "BEACONOFF"):
                BEACON_ON = False
                completeCommand()
                return True


        if (command == "ALLLIGHTSON"):
                lights.allLights(True, currentLightState )
                completeCommand()
                return True


        if (command == "ALLLIGHTSOFF"):
                lights.allLights(False, currentLightState)
                completeCommand()
                return True


        if (command == "BEACONON"):
                BEACON_ON = True
                completeCommand()
                return True


        if (command == "BEACONOFF"):
                BEACON_ON = False
                completeCommand()
                return True


        if (command == "DISPLAYBEACONON"):
                DISPLAY_BEACON_ON = True
                completeCommand()
                return True


        if (command == "DISPLAYBEACONOFF"):
                DISPLAY_BEACON_ON = False
                completeCommand()
                return True


        if (command == "DISPLAYLIGHTSON"):
                DISPLAY_LIGHTS_ON = True
                completeCommand()
                return True


        if (command == "DISPLAYLIGHTSOFF"):
                DISPLAY_LIGHTS_ON = False
                completeCommand()
                return True


        if (command == "UPDATESENSITIVITIES"):

                try:   
                        f = open("/home/pi/BeaconAir/state/distanceSensitivity.txt", "r")
                        commandresponse = f.read()
                        LIGHT_DISTANCE_SENSITIVITY = float(commandresponse)
                        f.close()
                except:
                        LIGHT_DISTANCE_SENSITIVITY = 2.0


                try:   
                        f = open("/home/pi/BeaconAir/state/brightnessSensitivity.txt", "r")
                        commandresponse = f.read()
                        f.close()
                        LIGHT_BRIGHTNESS_SENSITIVITY = float(commandresponse)
                except:
                        LIGHT_BRIGHTNESS_SENSITIVITY = 2.0
                print "LIGHT_DISTANCE_SENSITIVITY, LIGHT_BRIGHTNESS_SENSITIVITY= ", LIGHT_DISTANCE_SENSITIVITY, LIGHT_BRIGHTNESS_SENSITIVITY
                completeCommand()
                return True


        completeCommand()
        return True


# build configuration Table

# set up BLE thread
# set up a communication queue


queueBLE = Queue()
BLEThread = Thread(target=bleThread.bleDetect, args=(__name__,10,queueBLE,))
BLEThread.daemon = True
BLEThread.start()


bubblelog.writeToBubbleLog("BeaconAir Started")

# the main loop of BeaconAir
myPosition = [0,0]
lastPosition = [1,1]
beacons = []
while True:
        if (BEACON_ON == True):
                # check for iBeacon Updates
                print "Queue Length =", queueBLE.qsize()
                if (queueBLE.empty() == False):
                        result = queueBLE.get(False)
                        print "------"
                        utils.processiBeaconList(result,currentiBeaconRSSI, currentiBeaconTimeStamp,rollingiBeaconRSSI)
                        utils.clearOldValues(10,currentiBeaconRSSI, currentiBeaconTimeStamp,rollingiBeaconRSSI)
                        for beacon in conf.BeaconList:
                                utils.printBeaconDistance(beacon, currentiBeaconRSSI, currentiBeaconTimeStamp,rollingiBeaconRSSI)
                        # update position
                        if (utils.haveThreeGoodBeacons(rollingiBeaconRSSI) >= 3):
                                oldbeacons = beacons
                                beacons = utils.get3ClosestBeacons(rollingiBeaconRSSI)
                                print "beacons=", beacons
                                if (cmp(oldbeacons, beacons) != 0):
                                        bubblelog.writeToBubbleLog("closebeacons:%i,%i,%i" % (beacons[0], beacons[1], beacons[2]))


                                # setup for Kludge
                                #rollingiBeaconRSSI[7] = rollingiBeaconRSSI[6]


                                myPosition = utils.getXYFrom3Beacons(beacons[0],beacons[1],beacons[2], rollingiBeaconRSSI)
                                print "myPosition1 = %3.2f,%3.2f" % (myPosition[0], myPosition[1])
                                #bubblelog.writeToBubbleLog("position updated:%3.2f,%3.2f" % (myPosition[0], myPosition[1]))


                                # calculate jitter in position
                                jitter = (((lastPosition[0] - myPosition[0])/lastPosition[0]) + ((lastPosition[1] - myPosition[1])/lastPosition[1]))/2.0
                                jitter = jitter * 100.0   # to get to percent
                                lastPosition = myPosition
                                print "jitter=", jitter


                                f = open("/home/pi/BeaconAir/state/distancejitter.txt", "w")

                                f.write(str(jitter))
                                f.close()


                                for light in conf.LightList:
                                        lightdistance = utils.distanceBetweenTwoPoints([light[2],light[3]], myPosition)
                                        print "distance to light %i : %3.2f" % (light[0], lightdistance)
                                print "LIGHT_DISTANCE_SENSITIVITY, LIGHT_BRIGHTNESS_SENSITIVITY= ", LIGHT_DISTANCE_SENSITIVITY, LIGHT_BRIGHTNESS_SENSITIVITY
                                lights.checkForLightTrigger(myPosition, LIGHT_DISTANCE_SENSITIVITY, LIGHT_BRIGHTNESS_SENSITIVITY, currentLightState)
                                print "DISPLAY_BEACON_ON, DISPLAY_LIGHTS_ON", DISPLAY_BEACON_ON, DISPLAY_LIGHTS_ON
                                # build webpage
                                webmap.buildWebMapToFile(myPosition, rollingiBeaconRSSI, currentLightState, DISPLAY_BEACON_ON, DISPLAY_LIGHTS_ON)


                                # build beacon count graph
                                iBeaconChart.iBeacondetect(rollingiBeaconRSSI)
                        else:
                                # lost position
                                myPosition = [-myPosition[0], -myPosition[1]]


                #print currentiBeaconRSSI
                #print currentiBeaconTimeStamp


        # end of BEACON_ON - always process commands
        else:
                if (queueBLE.empty() == False):
                        result = queueBLE.get(False)
                print "------"
                print "Beacon Disabled"
        # process commands from RasPiConnect


        processCommand()

        time.sleep(0.25)

One very interesting part of IOTBeaconAir is building the HTML-based map showing position, beacons, and lights.

As I started this project, I felt that building this live map was going to be the biggest problem. I looked at building a live map with Matplotlib on the Pi, but it was computationally expensive and complicated. Then I looked at HTML drawing solutions and I found that it was almost trivial to do so. I used a Remote Webview in RasPiConnect for the control panel. Figure 4-7 shows the completed HTML map .

A367492_1_En_4_Fig7_HTML.jpg
Figure 4-7. HTML House Map

To make this HTML map work, follow these steps:

  1. Build a JPEG with the plan of your office or house. I took a picture of the house plans and then used GIMP [ www.gimp.org ] to draw the walls on the JPEG and then remove the JPEG layer. Worked like a champ. Then I had to measure a wall in meters and use GIMP to measure the same wall in pixels, and I had my meters to pixels constant (0.0375 m/px in my case). I put the 0,0 point at the top of the JPEG and then x is positive going to the right and y is positive down the left side.

  2. In the configuration file, figure out the positions x,y for each of the lights and the beacon and put in the configuration file.

    • Run the software. The resulting HTML code looks like the following :

    <html><head><title></title><style>body,html,iframe{margin:0;padding:0;}
    </style></head><body><div style='position: relative; left: 0; top: 0;'>
    <img src='http://example.example.com:9600/static/mainplanfull.png' style='position: relative; top: 0; left: 0;'/>
    <img src='http://example.example.com:9600/static/iBeacon.png' style='position: absolute; top: 490px; left: 299px;'/>
    <img src='http://example.example.com:9600/static/iBeacon.png' style='position: absolute; top: 19px; left: 122px;'/>
    <img src='http://example.example.com:9600/static/iBeacon.png' style='position: absolute; top: 127px; left: 122px;'/>
    <img src='http://example.example.com:9600/static/iBeacon.png' style='position: absolute; top: 40px; left: 173px;'/>
    <img src='http://example.example.com:9600/static/iBeacon.png' style='position: absolute; top: 118px; left: 183px;'/>
    <img src='http://example.example.com:9600/static/iBeacon.png' style='position: absolute; top: 128px; left: 257px;'/>
    <img src='http://example.example.com:9600/static/iBeacon.png' style='position: absolute; top: 418px; left: 300px;'/>
    <img src='http://example.example.com:9600/static/iBeacon.png' style='position: absolute; top: 453px; left: 275px;'/>
    <img src='http://example.example.com:9600/static/OffLightBulb.png' style='position: absolute; top: 418px; left: 315px;'/>
    <img src='http://example.example.com:9600/static/OffLightBulb.png' style='position: absolute; top: 473px; left: 315px;'/>
    <img src='http://example.example.com:9600/static/OffLightBulb.png' style='position: absolute; top: 19px; left: 132px;'/>
    <img src='http://example.example.com:9600/static/OffLightBulb.png' style='position: absolute; top: 30px; left: 173px;'/>
    <img src='http://example.example.com:9600/static/OffLightBulb.png' style='position: absolute; top: 118px; left: 173px;'/>
    <img src='http://example.example.com:9600/static/OffLightBulb.png' style='position: absolute; top: 109px; left: 122px;'/>
    <img src='http://example.example.com:9600/static/OffLightBulb.png' style='position: absolute; top: 8px; left: 222px;'/>
    <img src='http://example.example.com:9600/static/OffLightBulb.png' style='position: absolute; top: 42px; left: 16px;'/>
    <img src='http://example.example.com:9600/static/OffLightBulb.png' style='position: absolute; top: 98px; left: 16px;'/>
    <img src='http://example.example.com:9600/static/red-pin.png' style='position: absolute; top: 378px; left: 217px;'/>
    </div></body></html>
This works like a champ. I made my icons with transparent backgrounds (again using GIMP)                                                                              .

BeaconAir Configuration File

The IOTBeaconAir Configuration file needs to be set up before the system can be used. It is located under config in the main directory. The three major parts of the configuration file are the following:

Scaling Factors
Beacon Configuration
Light Configuration

The following declarations show two of the scaling factors used in the project:

def pixelConv(pixels):
        return pixels * 0.0375    # in meters


def meterToPixel(meters):
        return int(meters / 0.0375)    # in pixels

Our beacon configuration is accomplished as follows:

# Beacon format:
#     BeaconNumber, LocalName, x, y, UDID, Major, Minor, Measured Power (from spec), x in px, y in px
# BeaconNumber is incremental from 0 up.  Don't skip a number
BeaconList=[]
BeaconCount = 0


Beacon = [BeaconCount,"Estimote #0 Beacon", pixelConv(314), pixelConv(507),  "b9407f30f5f8466eaff925556b57fe6d", 64507, 5414, -64, 314, 507]
BeaconList.append(Beacon)
BeaconCount += 1

Finally, there is our light configuration:

#list of lights
#Light Format
#     LightNumber, LocalName, x, y, pixel x, pixel y, light on/off (1/0), huelightnumber


LightList=[]
LightCount = 0
Light = [LightCount, "Lab Left", pixelConv(330), pixelConv(435),330, 435,0, 2]
LightList.append(Light)
LightCount += 1

You can get the Hue light number from the Phillips App under Light overview, or you can write a short program (look at the phue examples) to get the dictionary from the Phillips Hue Hub.

iBeacon Software

The iBeacons are problematic. They aren't very accurate and lots of different environmental factors affect the RSSI (received power). If you have your IOTBeaconAir sensitivities set high, you can sit in one place and watch the lights grow brighter and dimmer as the received signals vary. It's kind of a visual map of the electromagnetic spectrum. Setting your brightness sensitivities lower will increase the sensitivity, while the light range higher clears this up.

There are two functions used by IOTBeaconAir to determine position. First is the calculation of distance from RSSI . We use a smoothing function on the received RSSI values to reduce the jitter. For example:

def calculateDistanceWithRSSI(rssi,beaconnumber):

        beacon = conf.BeaconList[beaconnumber];
        txPower = beacon[7]
        ratio_db = txPower - rssi;
        ratio_linear = pow(10, ratio_db / 10);
        r = pow(ratio_linear, .5);
        return r

The result from this function is already scaled in meters.

Trilateralization

The second key piece is the calculation of position by using Trilateration. Trilateration is the method of determining the position of a point, given the distance to three control points. [citation: en.wikipedia.org/wiki/Trilateration ].

SwitchDoc Note

When you hear someone talking about detecting where something is by measuring distance from points, they usually say “Triangulation” where they really mean “Trilateration.” What is the difference? You use triangulation when you know the angle to an object from two different sources. Then you can locate the object on a plane.

In the case of iBeacons, all we know is the distance. We don’t know anything about direction. We can use three distances (or iBeacons in this case) and fix the location of the Raspberry Pi on a plane. It occurs to me that we could put a directional Bluetooth antenna on a stepper motor and possibly use that to use triangulation, which means we would only need two iBeacons to find our position, rather than three as in trilateration.

The purpose of the following getXYFrom3Beacons function is to take the information from the three selected iBeacons found (a,b,c) and calculate the XY coordinates of your IOTBeaconAir device.

def getXYFrom3Beacons(beaconnumbera, beaconnumberb, beaconnumberc, rollingRSSIArray):

        beacona = conf.BeaconList[beaconnumbera];
        beaconb = conf.BeaconList[beaconnumberb];
        beaconc = conf.BeaconList[beaconnumberc];
        xa = float(beacona[2])
        ya = float(beacona[3])
        xb = float(beaconb[2])
        yb = float(beaconb[3])
        xc = float(beaconc[2])
        yc = float(beaconc[3])


        ra = float(calculateDistanceWithRSSI(rollingRSSIArray[beaconnumbera],beaconnumbera ))
        rb = float(calculateDistanceWithRSSI(rollingRSSIArray[beaconnumberb],beaconnumberb ))
        rc = float(calculateDistanceWithRSSI(rollingRSSIArray[beaconnumberc],beaconnumberc ))


        S = (pow(xc, 2.) - pow(xb, 2.) + pow(yc, 2.) - pow(yb, 2.) + pow(rb, 2.) - pow(rc, 2.)) / 2.0
        T = (pow(xa, 2.) - pow(xb, 2.) + pow(ya, 2.) - pow(yb, 2.) + pow(rb, 2.) - pow(ra, 2.)) / 2.0


        try:
                y = ((T * (xb - xc)) - (S * (xb - xa))) / (((ya - yb) * (xb - xc)) - ((yc - yb) * (xb - xa)))
                x = ((y * (ya - yb)) - T) / (xb - xa)


        except ZeroDivisionError as detail:
                print 'Handling run-time error:', detail
                return [-1,-1]


        point = [x, y]
        return point

The IOTBeaconAir Control Panel

The IOTBeaconAir control panel is built using an app called RasPiConnect ( www.milocreek.com ). It allows me to build elaborate control panels on iPhones and iPads with almost no coding and especially no coding at all on the iPad/iPhone. The response is good, especially on a local network, and I get a lot of fun and colorful buttons and controls to use. I have used this app on four projects now and I'm getting quite good at using it.

The completed control panel is shown in Figure 4-8.

A367492_1_En_4_Fig8_HTML.jpg
Figure 4-8. RasPiConnect Control Screen for IOTBeaconAir

The right side of the control panel has to do with the HTML map and control of the lights. The Remote Webview HTML control has already been discussed. The Green logging box is a Bubble Talk control and can be set up to read periodically from the server and write out logging information to the control panel. The code is contained in bubblelog.py in the IOTBeaconAir directory. In the above example you can see the close beacons drifting slightly, changing the ranking of which beacon is closest. Finally we see that two lights are turned on (you can see the results in the house map). Below that is a graph, showing how many beacons are being read. The above graph could happen if you walked out of range of the beacons and then walked back into the range.

The controls on the left side are used to set the distance in meters for which to turn the lights on for and the second control sets the brightness of the light. For example, you can set the brightness sensitivity to 1 meter and it will start getting bright at 1 meter and grow brighter as you get closer. It would be easy to modify the software to change the colors of the Phillips Hue bulbs according to distance or time of day. The graph on the bottom of the right side is a Dynamic SparkLine control set to advance every time (event driven) there is a change to the jitter value. You could also set it to a timed event, which means it advances all the time and just adds new values as they come in from IOTBeaconAir.

The code for all of the buttons is quite similar. Pushing a button on the iPad sends an HTML XML packet to the Raspberry Pi software, which then writes to a command file, which IOTBeaconAir then picks up and executes the requested functions .

In RasPiConnectServer (Local.py file) the block of code for the “All Lights On” button (a Feedback Button – FB-2) is the following:

# FB-2 -  turns lights on and off         
 if (objectServerID == "FB-2"):
         #check for validate request
         # validate allows RasPiConnect to verify this object is here
         if (validate == "YES"):
                 outgoingXMLData += Validate.buildValidateResponse("YES")
                 outgoingXMLData += BuildResponse.buildFooter()
                 return outgoingXMLData


         # not validate request, so execute

         responseData = "XXX"

         if (objectName is None):
                 objectName = "XXX"


         lowername = objectName.lower()

         if (lowername == "all lights on"):

                 status = sendCommandToBeaconAirAndWait("ALLLIGHTSON")
                 responseData = "all lights off"
                 responseData = responseData.title()


         elif (lowername == "all lights off"):

                 status = sendCommandToBeaconAirAndWait("ALLLIGHTSOFF")
                 responseData = "all lights on"
                 responseData = responseData.title()


          # defaults to on
         else:
                 status = sendCommandToBeaconAirAndWait("ALLLIGHTSON")
                 lowername = "all lights off"
                 responseData = lowername.title()


         outgoingXMLData += BuildResponse.buildResponse(responseData)
         outgoingXMLData += BuildResponse.buildFooter()
         return outgoingXMLData

When the button value comes (“all lights off”), the code compares it, sends a command to IOTBeaconAir, and then toggles the value (“all lights on”) and sends it back to the RasPiConnect App setting up the next button push (which will next turn all the lights on). The rest of the code is boilerplate building the RasPiConnect XML request and handling an error condition that sometimes happens (the button goes blank).

The IOTBeaconAir code to handle the command file request is simple:

if (command == "ALLLIGHTSON"):
        lights.allLights(True, currentLightState )
        completeCommand()
        return True


if (command == "ALLLIGHTSOFF"):
        lights.allLights(False, currentLightState)
        completeCommand()
        return True

All of the controls follow the same design pattern, although the graphing controls are a bit more complicated. To follow any command through the system, figure out what the control ID you are looking for is (FB-2 in our example above), and track it through Local.py and then see what the command does in IOTBeaconAir.py. In some cases, such as the graphs, IOTBeaconAir is building the graph data, writing it to a file, and then RasPiConnectServer reads it in Local.py. The Remote Webview (the house map, W-10 is the object ID) is one of these controls that reads in a file generated by IOTBeaconAir.py (see the software in webmap.py). The RasPiConnect XML configuration file for IOTBeaconAir is on github.com/switchdoclabs.

The last thing to note is how to build the cool IOTBeaconAir background on the control panel screen. The trick is to build a JPEG or PNG file on any graphical app (such as Grafio on the iPad[tentouchapps.com/grafio/] or GIMP), add it to the iPad photo library, and then select the picture for the control panel background for the IOTBeaconAir page in RaspiConnect. This gives you an instant professional-looking background.

Installing blueZ and phue on the Raspberry Pi

Now it’s time to install blueZ and phue on the Pi. Be prepared. The installs will take a while, too, especially the install of bluez. You can use the standard apt-get version of blueZ, but the standard apt-get version is old and has patchy support for iBeacons. Instead of using apt-get for the main blueZ package, we are downloading the source and compiling it on the Raspberry Pi. All the commands for doing this are included in the following steps. It is still a very good apt-get for the auxiliary package, which installs as shown below.

BlueZ

To install blueZ on a Raspberry Pi 3 or an older version of the Pi running the new Jessie distribution or newer distribution, run the following commands in a terminal window:

sudo apt-get update && sudo apt-get upgrade

sudo apt-get install libusb-dev
sudo apt-get install libglib2.0-dev --fix-missing
sudo apt-get install libudev-dev
sudo apt-get install libical-dev
sudo apt-get install libreadline-dev
sudo apt-get install libdbus-glib-1-dev


sudo apt-get install bluetooth bluez blueman

sudo apt-get install python-bluez
sudo shutdown -r now

To install a new version of blueZ on the Pi on older versions of the Raspberry Pi (RPi2 and before the Raspian Jessie distribution), do the following:

sudo apt-get install libusb-dev
sudo apt-get install libdbus-1-dev
sudo apt-get install libglib2.0-dev --fix-missing
sudo apt-get install libudev-dev
sudo apt-get install libical-dev
sudo apt-get install libreadline-dev


sudo mkdir bluez
cd bluez
sudo wget www.kernel.org/pub/linux/bluetooth/bluez-5.19.tar.gz
sudo gunzip bluez-5.19.tar.gz
sudo tar xvf bluez-5.19.tar
cd bluez-5.19
sudo ./configure --disable-systemd
sudo make
sudo make install


sudo apt-get install python-bluez

sudo shutdown -r now

Now you have bluez installed and running on your Raspberry Pi. Next install your USB Bluetooth 4.0 Dongle and start the checkout. For example:

pi@BeaconAir ∼/BeaconAir/ble $ lsusb                  

Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 004: ID 0a5c:21e8 Broadcom Corp.

Note, your USB Bluetooth dongle should show up something like this depending on what you have plugged into your USB bus. You can see a lot more information about the USB device by typing this:

sudo lsusb -v -d 0a5c:

Now you can look for the Bluetooth device using hciconfig:

pi@BeaconAir ∼/BeaconAir/ble $ hciconfig
hci0: Type: BR/EDR Bus: USB
 BD Address: 00:02:72:CC:DF:D1 ACL MTU: 1021:8 SCO MTU: 64:1
 UP RUNNING
 RX bytes:9071808 acl:0 sco:0 events:230151 errors:0
 TX bytes:1166 acl:0 sco:0 commands:100 errors:0
Finally, turn on the device:


pi@BeaconAir ∼/BeaconAir/ble $ sudo hciconfig hci0 up

And now run the blescanner ( github.com/switchdoclabs/iBeacon-Scanner- ) command to see what iBeacons might be around you. If you don't have an iBeacon, you can simulate it on with either your iPhone or Android phone with any number of apps on the App Store.

cd /home/pi/BeaconAir/ble

pi@BeaconAir ∼/ble $ sudo python testblescan.py
ble thread started
----------
cf:68:cc:c7:33:10,b9407f30f5f8466eaff925556b57fe6d,13072,52423,-74,-78
cf:68:cc:c7:33:10,74696d6f74650e160a181033c7cc68cf,46608,13255,-52,-77
da:f4:2e:a0:70:b1,b9407f30f5f8466eaff925556b57fe6d,28849,11936,-74,-79
da:f4:2e:a0:70:b1,74696d6f74650e160a18b170a02ef4da,46769,28832,46,-78
dd:5d:d3:35:09:dd,8aefb0316c32486f825be26fa193487d,1,1,-64,-78
c3:11:48:9b:cf:fa,8aefb0316c32486f825be26fa193487d,0,0,-64,-73
fd:5b:12:7f:02:e4,b9407f30f5f8466eaff925556b57fe6d,740,4735,-74,-79
fd:5b:12:7f:02:e4,74696d6f74650e160a18e4027f125bfd,46820,639,18,-80
dd:5d:d3:35:09:dd,8aefb0316c32486f825be26fa193487d,1,1,-64,-77

The content of each line above is this:

example: cf:68:cc:c7:33:10,b9407f30f5f8466eaff925556b57fe6d,13072,52423,-74,-78
Beacon MAC Address,iBeacon UDID, iBeacon Major Number, iBeacon Minor Number, TX Power at 1m, RSSI

Note that there are some odd devices above that are NOT my Estimote (b9407f30f5f8466eaff925556b57fe6d) or Particle (8aefb0316c32486f825be26fa193487d) iBeacons. The txPower for the Estimote and Particle devices behave correctly. The odd devices have larger numbers or actually numbers that vary. Interesting information .

phue

The installation of phue on your Pi is simple. One approach is to execute the so-called easy install. For example:

sudo easy_install phue

The alternative is to invoke pip as follows:

sudo pip install phue

Note that after the first time you run IOTBeaconAir on your Raspberry Pi, the phue library will quit telling you that you need to push the Phillips Hue Hub button and rerun the software. IOTBeaconAir will then be paired with the Phillips Hue Hub.

RasPiConnectServer Startup

Now that all the code has been installed for iBeaconIOT, you need to start up the code. The startup procedure steps follow.

Startup Procedure

To start up IOTBeaconAir, you need to do three things.

  1. Turn on the Bluetooth dongle as follows:

                sudo hciconfig hci0 up                    
  2. Change to the IOTBeaconAir directory. Start up the IOTBeaconAir python programming by invoking either

                sudo python IOTBeaconAir.py (in its own terminal window)

                              or

                sudo nohup python IOTBeaconAir.py & (from the command line)
  3. Change to the RasPiConnectServer directory. Start the RasPiConnectServer. You can execute either this command:

                sudo sh startserver.sh (in its own terminal window)
                or this one:
                sudo nohup sh startserver.sh & (from the command line)

If you use nohup, you can close the terminal window and the program keeps running in the background until you reboot or kill the program. All of the debug data goes into a file nohup.out in the start directory. If you want to watch what is going on using nohup, go to the program directory and type tail -f nohup.out on the command line .

Making IOTBeaconAir Start on Bootup

You can make IOTBeaconAir start when the Pi boots by editing /etc/rc.local. The /etc/rc.local script is a script on the Raspberry Pi that runs when Linux first boots. To edit it, you will need root privileges. Invoking the editor via sudo is the way to go here:

sudo nano /etc/rc.local                  

Then add the following lines to the rc.local file. Place the newly added lines before the exit 0 statement.

sudo hciconfig hci0 up

date >> /var/log/RasPiConnectServer.log
echo "Starting RasPiConnectServer…" >> /var/log/RasPiConnectServer.log
nohup /home/pi/RasPiConnectServer/startserver.sh >>/var/log/RasPiConnectServer.log 2>&1 &


date >> /var/log/BeaconAir.log
echo "Starting IOTBeaconAir.." >> /var/log/BeaconAir.log
cd /home/pi/BeaconAir/
nohup sudo python IOTBeaconAir.py >>/var/log/RasPiConnectServer.log 2>&1 &

How It Works in Practice

IOTBeaconAir does work. As I walk around with the IOTBeaconAir Portable Pi in a fanny pack, the lights come on and off. I have published a video of the action on www.switchdoc.com .

However, the iBeacons are not very reliable and will vary significantly just sitting in one spot. You can watch the little red pin bounce around on the control panel. A lot could be done to smooth this out by doing a little signal processing at the cost of response time. It certainly makes an interesting demo to show people.

Things to Do

You can use the MQTT techniques of Chapters 5 and 6 to connect the IOTBeaconAir to an external IOT Server or tweet your location as in Chapter 3.

One idea I had for another project revolving around iBeacons was to reverse the system. Carry an iBeacon and build a mesh network of Raspberry Pi's all listening to the iBeacon via my BLE scanner program and then communicating the RSSI information to a central Pi that would figure out the location of the iBeacon and report it to the control panel. Now granted, it's more expensive than buying a bunch of iBeacons and one Pi, but it would have some really interesting data flows and the response time could be excellent. It would be a lot better for the user as she would just have to carry a small iBeacon in her pocket! Better for the fashion conscious than a fanny pack. Figure 4-9 shows IOTBeaconAir installed in a very fashion-conscious fanny pack.

A367492_1_En_4_Fig9_HTML.jpg
Figure 4-9. IOTBeaconAir Ready to Walk

The first thing I would add to this unit is the ability for the Raspberry Pi to sense the light levels in the environment. Why turn the lights on if it is already bright in the room?

The Classic Distributed System Problems

Now I will talk about the classic problem with groups of IOT devices. What do you do if two IOT units are telling your lights two different things? Who wins?

If we were to build a bunch of these devices and put them on other members of the household (other people or the cat), we now need to build an arbitration system. What do I mean by that? We have to “arbitrate” the information coming in from the IOT devices. In computer speak, a group of IOTBeaconAir units with no central coordinator becomes a distributed system. From the Wikipedia definition of distributed systems [ en.wikipedia.org/wiki/Distributed_computing ], “A distributed system is a software system in which components located on networked computers communicate and coordinate their actions by passing messages. The components interact with each other in order to achieve a common goal. Three significant characteristics of distributed systems are: concurrency of components, lack of a global clock, and independent failure of components.” You can see from this that any group of IOT devices fit within this definition.

Since there is no global server for IOTBeaconAir, there is no mechanism currently defined to handle arbitration of actions based on detection. In general, it is easier to resolve these kinds of issues by using a global server. But, this makes your system more susceptible to failure of the server. Group self-organization and group arbitration is a better way to go, but it is much more complicated to design, implement, and test.

Here are some of the questions to arbitrate once you have multiple IOT units of IOTBeaconAir:

  • Which IOTBeaconAir turns the lights on?

  • Which IOTBeaconAir turns the lights off?

  • How do you handle one person leaving the room?

  • How do you rank the IOTBeaconAir units to one another? Is the cat on the bottom or top of the priority list?

  • How do you handle people turning lights on or off by hand?

  • How do you detect a person sitting down to read versus walking through the house?

And the list goes on. Any time you put more than one IOT device in control of anything physical at all, things can get very complicated.

Conclusion

In Chapter 3, we built an IOT Weather device that generated and supplied information to the rest of the Internet. In this chapter you saw how IOTBeaconAir reverses that. It gathers information from the IOT devices (iBeacons) around itself, and then modifies the environment without going out to the Internet (or the wider IOT) at all. This is a much more localized IOT application.

In Chapter 6, we’ll extend this idea further by building an RFID (Radio Frequency IDentification) unit that will gather information from passive units (that are only activated when they are being interrogated for information) and then do both major functions. Send data to the Internet and modify the local environment.

For all of the software, see the following:

  • github/switchdoclabs/BeaconAirPython

  • github/switchdoclabs/BeaconAirRasPiConnectLocal

  • github/switchdoclabs/iBeacon-Scanner-

For more on IOTBeaconAir and discussion: www.switchdoc.com

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.232.128