#4_03 Changing Your Environment with IOT and iBeacons
Posted by Superadmin on November 29 2018 12:31:54

Changing Your Environment with IOT and iBeacons

 

 

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:

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.

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:

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.

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.

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.

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.

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.

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 .

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.

  •  
    1. 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.

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 gra