I love GPS location data. The data themselves are simple, beautiful, and evocative, and the applications are endless. From just times, places, and identifiers, we can learn so much about how people use cities.

Using these data I’ve written several papers on park access and neighborhood mobility. One of the chief questions that I got while developing this work is why I trust these data. In the past, I’ve addressed this problem in three ways:

  1. Comparisons to the Census: I compare device counts to population counts from the Census to measure demographic bias. As many of the location intelligence companies themselves have shown, they are quite representative – though wealthy and educated areas are a bit more-represented and Hispanic areas a bit less so.
  2. Robustness Checks: I show that the specific observables constructed for each study, and the relationships ultimately described, are robust to controls for the data rates.
  3. Picking at Scabs: After (substantial) data cleaning, basically every “feature” that I have seen in the data has turned out to be real. In many cases, I’ve confirmed things by going and visiting locations to understand what the data meant; my favorite example of this was the parking lot by the Jackson Park Lagoon, which is used for grilling and partying.

Ultimately, these exercises gave me confidence with the data. But Luc Anselin (my advisor in CSDS) kept pushing on this question, and with abundant ignorance, I responded that we could create other groundtruths using electronics or computer vision, and compare those to counts from the GPS location data. Of course, I had never used computer vision or network cameras before, so there was that. I’ll describe that process in another post.

Here, I’ll describe a box with a ultrasonic and passive infrared sensors, built using the Particle ecosystem, and uploading data to InfluxCloud over 3G. It looks like this:

A completed 'Olm-meter.'

I call it an Olm-meter, since it’s intended to count people in parks.

The Electronics

The electronics for this box are uncomplicated; it took just a few hours to make a prototype. The entire challenge was (and continues to be) to make it work outside. Let’s start with the easy part!

The box contains two sensors: ultrasonic and passive infrared. I wanted multiple sensors, to be able to compare performance over time. One could also watch for broken laser beams, or any other manner of motion detection devices. Ultrasonic sensors just let out a chirp and the listen for the echo. As for PIRs, Adafruit has a very nice explanation of how they actually work, the short version of which is that they house two IR sensors side by side, behind “interwoven” Fresnel lenses (as in a lighthouse). If that doesn’t any make sense, it’s because it’s delightfully ingenious, and well worth your read. In my initial tests, I used the HC-SR04 for the ultrasound and HC-SR501 for the PIR. Both of these cost only about $2. For the final version, I switched the sonic sensor to the waterproof JSN-SR04T.

The wiring diagram is basically just this:

'Fritzing' diagram of the Olm-meter. The Particle Electron at left powers, controls, and receives data from PIR and ultrasonic sensors, top and bottom respectively. (Click to expand.)

You wire ground to ground, voltage to voltage, and inputs and outputs to the digital GPIO pins. There are a few caveats, though. The sensors run at 5V, and while I initially tested with a separate 5V supply alongside the 3.3V battery, I found this a bit annoying. I thus decided to power the whole thing from USB, to sidestep the power issue. Adafruit has 5V USB batteries that actually provide a potential of 5V. The other wrinkle is that the JSN-SR04T’s input is 5V, which you should step down to 3.3V for the GPIO pin.

This works “out of the box” at home or in the lab, but of course we also want it to work outside. Enter Particle. Particle devices are basically Arduino devices (the same, c++ setup() / loop() scheme) but with wireless communications “solved.” With a single call of

Particle.publish("my_var", var_to_publish);

a reading from the device is pushed to the cloud. Friends at the SAIC introduced me to these, and they are great. I began with the Wi-Fi based Argon, but jumped over to the 3G Electron to go to the field. It is notable that Particle is now free for up to 100 devices, which is amazing.

Part List

Part Cost Use
Particle Electron $71 Microcontroller and wiresess communication
JSN-SR04T $13 Waterproof ultrasonic
HC-SR04 $2 “Stock” ultrasonic (not used)
HC-SR501 $2 Passive Infrared
Silicone Caulk $6 Waterproof PIR port
Gasket Rubber $9 Sheet for cutting box gasket
Glands $15 (bulk) Allow cable from JSN-SR04T to pass into box
Breadboard $2 Breadboard for mounting the Electron
3D-printed box $40 Put everything together.
Total ~$140 (Some parts are bulk, etc.)

The price for the box is for the materials only, and includes neither the labor nor the equipment. The price at Shapeways would have run to hundreds of dollars. Note that this does not include the cost of other equipment either – wire, solder, soldering irons, voltimeter. It’s hard to really do this without a lab or a full closet.

The Code

The Arduino Firmware

There are many Arduino tutorials online for the HC-SR04 and HC-SR501 (examples in links). This project simply requires you to interleave those two parts, apply a little bit of processing, and upload the variables. For example, two “counts” from an ultrasonic sensor (distance below threshold) 20 ms apart do not count as “separate people.” We must decide what thresholds to apply and what separation in time counts as a “new” person. I aggregate counts for a minute, and then push a fragment of Influx line protocol to the cloud:

snprintf(pub_var, sizeof(pub_var), "p=%d,s=%d", pir_count, ultrasonic sensor_count);
Particle.publish("park_count", pub_var);

Particle / Influx Integration

You can create webhooks when “events” arrive in the Particle cloud, to send data to a more-permanent location.

Particle actually has a page on how to do this using Telegraf. Basically it’s: ramp up an extra service in between Particle and Influx to make more-regular ingestion. I think this is overkill. You can have the Particle webhook write directly to the Influx endpoint, by sending the line protocol:

count,device=muir {{{PARTICLE_EVENT_VALUE}}} {{time}}

where muir is my device and {{{PARTICLE_EVENT_VALUE}}} is the tag I sent from the device. You just have to copy in your organization and token into the HTML headers (Authorization: Token XYZ).

The Box

The entire challenge with this project was to make it portable and watertight. I’ve solved most, though not all, of these issues. I had not used CAD for 3D parts before, and I opted to make a custom 3D box for this. Iterating on this during COVID-times was somewhat slow, since many labs are limited right now.

I used FreeCAD for the design, mostly just using the Part / Part Design workbenches. If you are familiar with basic geometric manipulation a la GDAL, then this will all make sense. I measured every piece of electronics with calipers, and parameterized the entire piece from that “spreadsheet.” This way, changing the entire box was “automatic” as a function of the composite parts.1 The design is here:

As you can see, there are two holes in the front for the PIR and sonic senor. The ultrasonic sensor is waterproof and on a lead, which enters via a gland (not shown) into the main box. The lens of the PIR is detachable and it plugs into the box directly, sealed with silicon caulk. The PIR itself plugs into its mount. On the back there are mounts for a little breadboard (holding the Electron) and the controller-board for the JSN-SR04T. The large box on the right side is for the battery, and the one on the left is for the antenna.

The lid slides over the box, and incorporates holes for mounting the whole thing on an octagonal light-pole. The complete box was printed using FDM by the Hack Arts Lab at the University. Since 3D printing rubber is a bit more complicated, the gasket between the two pieces is laser cut.

Is this compact? No. Is there wasted space? Yes. Does it work? Mostly. It’s fine.

Assembly Pics

The Data

Finally, we have data. It’s apparent that the two sensors are recording very different numbers of hits per minute. I am currently tuning this, to understand it better and make them more consistent over time. As it stands, they can actually flip (sonic > PIR) when I rejigger the device!

Person counts in the InfluxCloud, uploaded by the Particle Electron over 3G.

Works in Progress

Obviously, data quality is the core issue – I’d like to actually use the data.

The other big thing to solve is continuous power. One battery lasts for almost exactly one day. Swapping them out is OK for a week or so, but clearly it does not scale. I can’t install solar without some sort of blessing from the city, but all these poles have power in them, and I’d like for them to just give me access!


  1. The one thing that was not clear to me was how to apply filleting, so that it did not get lost if clearances or part sizes changed.