BeaconAir – Track your Pi

iBeacon Software

The iBeacons are problematic. They aren't very accurate, and lots of different environmental factors affect the RSSI (received power). If your BeaconAir 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 and the light range higher clears up this problem.

Two functions used by BeaconAir determine position (Listing 5). First is the calculation of distance from RSSI with a smoothing function on the received values to reduce jitter (lines 1-8). Distance is already scaled in meters. The second key piece is the position calculation using trilateration (lines 10-39), a method of determining the position of a point given the distance to three control points.

Listing 5

iBeacon Positions

01 def calculateDistanceWithRSSI(rssi,beaconnumber):
02
03     beacon = conf.BeaconList[beaconnumber];
04     txPower = beacon[7]
05     ratio_db = txPower - rssi;
06     ratio_linear = pow(10, ratio_db / 10);
07     r = pow(ratio_linear, .5);
08     return r
09
10 def getXYFrom3Beacons(beaconnumbera, beaconnumberb, beaconnumberc, rollingRSSIArray):
11
12     beacona = conf.BeaconList[beaconnumbera];
13     beaconb = conf.BeaconList[beaconnumberb];
14     beaconc = conf.BeaconList[beaconnumberc];
15     xa = float(beacona[2])
16     ya = float(beacona[3])
17     xb = float(beaconb[2])
18     yb = float(beaconb[3])
19     xc = float(beaconc[2])
20     yc = float(beaconc[3])
21
22     ra = float(calculateDistanceWithRSSI(rollingRSSIArray[beaconnumbera], beaconnumbera ))
23     rb = float(calculateDistanceWithRSSI(rollingRSSIArray[beaconnumberb], beaconnumberb ))
24     rc = float(calculateDistanceWithRSSI(rollingRSSIArray[beaconnumberc], beaconnumberc ))
25
26     S = (pow(xc, 2.) - pow(xb, 2.) + pow(yc, 2.) - pow(yb, 2.) + pow(rb, 2.) - pow(rc, 2.)) / 2.0
27     T = (pow(xa, 2.) - pow(xb, 2.) + pow(ya, 2.) - pow(yb, 2.) + pow(rb, 2.) - pow(ra, 2.)) / 2.0
28
29
30     try:
31         y = ((T * (xb - xc)) - (S * (xb - xa))) / (((ya - yb) * (xb - xc)) - ((yc - yb) * (xb - xa)))
32         x = ((y * (ya - yb)) - T) / (xb - xa)
33
34     except ZeroDivisionError as detail:
35         print 'Handling run-time error:', detail
36         return [-1,-1]
37
38     point = [x, y]
39     return point

BeaconAir Control Panel

The BeaconAir control panel is built with the RasPiConnect app [12], which lets you build elaborate control panels on iPhones and iPads with almost no coding and, in particular, no coding on the mobile device itself. The response is good, especially on a local network, and I get to use lots of fun and colorful buttons and controls (see Figure  9).

Figure 9: BeaconAir control screen built with RasPiConnect.

The right side of the control panel has the HTML map and controls for the lights. The Remote Webview HTML control has already been discussed. The Green logging box is a Bubble Talk control that 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 BeaconAir directory.

In the example, you can see the close beacons drifting slightly, changing the ranking of which beacon is closest. Finally, two lights are turned on, as shown in the house map. Below the logging box is a graph showing how many beacons are read. The example graph shows the Rasp Pi going in and out of range. The controls on the left side are used to set the distance in meters at which to turn the lights on, and the second control sets the brightness of the light.

For example, if you set the brightness sensitivity to 1 meter, 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 Philips Hue bulbs according to distance or time of day. The graph on the bottom left is a Dynamic SparkLine control (event driven) set to advance every time the jitter value changes. 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 BeaconAir.

The code for all buttons is quite similar. Pushing a button on the iPad sends an HTML XML packet to the Rasp Pi software, which writes to a command file; BeaconAir then picks it up and executes the requested functions. For the All Lights On button (FB-2, a Feedback Button), the Local.py code (in RasPiConnectServer) is shown in Listing  6.

Listing 6

Local.py

01 # FB-2 -  turns lights on and off
02  if (objectServerID == "FB-2"):
03   #check for validate request
04   # validate allows RasPiConnect to verify this object is here
05   if (validate == "YES"):
06     outgoingXMLData += Validate.buildValidateResponse("YES")
07     outgoingXMLData += BuildResponse.buildFooter()
08     return outgoingXMLData
09
10   # not validate request, so execute
11
12   responseData = "XXX"
13
14
15   if (objectName is None):
16     objectName = "XXX"
17
18   lowername = objectName.lower()
19
20
21   if (lowername == "all lights on"):
22
23     status = sendCommandToBeaconAirAndWait("ALLLIGHTSON")
24     responseData = "all lights off"
25     responseData = responseData.title()
26
27
28   elif (lowername == "all lights off"):
29
30     status = sendCommandToBeaconAirAndWait("ALLLIGHTSOFF")
31     responseData = "all lights on"
32     responseData = responseData.title()
33
34
35    # defaults to on
36   else:
37     status = sendCommandToBeaconAirAndWait("ALLLIGHTSON")
38     lowername = "all lights off"
39     responseData = lowername.title()
40
41
42   outgoingXMLData += BuildResponse.buildResponse(responseData)
43   outgoingXMLData += BuildResponse.buildFooter()
44   return outgoingXMLData

When the button value ("all lights off") comes, the code validates it, sends a command to BeaconAir, 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 (e.g., the button goes blank).

The BeaconAir 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, you need to figure out the control ID you are looking for (FB-2 in this example), track it through Local.py, and then see what the command does in BeaconAir.py.

In some cases, such as the graphs, BeaconAir is building the graph data and writing it to a file; then, RasPiConnectServer reads it in Local.py. In the Remote Webview (the house map), W-10 is the object ID, which is one of the controls that reads in a file generated by BeaconAir.py (see the software in webmap.py [6]). The RasPiConnect XML configuration file for BeaconAir is on GitHub [13].

The last thing to note is how to build the cool BeaconAir background on the control panel screen. The trick is to build a JPEG or PNG file on any graphical app (such as, Grafio or GIMP) and add it to the iPad photo library.

Now you just have to select the control panel background picture for the BeaconAir page in RasPiConnect to get an instant, professional-looking background.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Pi in the Palace

    Welcome to the sixth issue of Raspberry Pi Geek magazine. In case you haven't done the math, six issues for a bimonthly magazine means we're celebrating our one year anniversary! We had a great year, and so did the amazing Raspberry Pi computer. It seems like we were just writing about the 2 millionth Raspberry Pi, and now the Foundation has announced the 3 millionth unit. 

  • Connecting a weather station to your Arduino

    After losing one weather station to tropical winds, the author reboots and designs a PCB that connects to an Arduino and monitors weather instruments.

  • Getting BLE to behave on the Pi

    BLE is a convenient choice for wireless communication where WiFi and Internet connectivity aren't available, but getting it to work on a Raspberry Pi can be a minefield of compatibility problems. We look at the main concepts and debugging tools you will need to get the most out of BLE on your Pi.

  • WiFi and the Raspberry Pi

    This SwitchDoc column looks at various uses for the inexpensive ESP8266 WiFi/processor combination.

  • The Switch Doc rebuilds his cat toy launcher with 3D printing

    To explore the possibilities of 3D printing, we rebuild the cat toy launcher that debuted in Issue 5.