Ankle Monitor Part 2

I've had the parts, and more importantly, a plan, for the new ankle monitor for a few weeks now. I want to share what I've learned and what my plans are, so this will be a bit of a disorganized brain dump.

The plan #

I broke up the new ankle monitor into its component parts:

  1. The Ankle Monitor Hardware
    1. The brains (electronics)
      1. Location
      2. Transmission
      3. Control
    2. The power (a battery + charging)
    3. The casing
      1. IP68 rating
    4. The belt
      1. Attaching it to the case
      2. Detecting cuts
  2. The Software
    1. The listen server (receiving transmitted data)
    2. Displaying data
    3. Alerting

Right now, everything except the casing and the belt is in place to be built.

The Brains #

This is the term I'm using to refer to the electronic components inside the ankle monitor itself. I chose to go with pycom ’s GPy paired with the Pytrack expansion board; this combination gives me a GPS+Acceleromter mated to an all-in-one chip that can transmit on WiFi, Bluetooth, and the new LTE-M/NB-IoTstandards. The board is presented to the computer as a serial port, and you can use pycom's pymakr extension for VSCode to communicate with the REPL.

Micropython is similar enough to Python that I was able to get going fairly quickly. I've got a program loop that does the basics in place:

  1. Disable Heartbeat*
  2. Get Position
    1. If we can’t, exit!
  3. Determine network
    1. Try Wifi
    2. Try LTE
    3. Sync with NTP server
    4. If neither work, log position to a text file
  4. Encode the data packet as JSON:
    {
      'timestamp':[timestamp],
      'location': [
        'lat': [lat],
        'lon': [lon]
      ],
      'network':[w or c] // for wifi or cellular
      'device_id': [device identifier/authorization token]
    }
    
  5. POST current position to endpoint
    1. If this fails, log position to a text file
  6. Upload text file line by line to endpoint, one request per-line since every line in the text file is JSON
    1. Erase the line as we upload it
  7. Check for any configuration updates from the server
    1. Apply any changes if they’re found
  8. Reenable heartbeat
  9. Sleep

* The heartbeat is a function on the board that blinks the LED every 4 seconds to let you know it's powered on. In this case, I want to disable it so I can use the LED to indicate what's happening on the device.

The code for this isn't super complicated (it's very procedural), but there's a lot of improvements I want to make, like adding support for threading and asynchronous execution. Wrapping my head around that is going to be a challenge though.

It's also worth noting that one of my goals with this project was to simplify the process of transmitting data to the endpoint. As I said with the previous monitor, it transmitted data by connecting to the remote server over a socket connection, a level of networking I was unfamiliar with. Contrast that to the new ankle monitor and micropython libraries that set up basic functions for making HTTP requests; these are significantly easier to receive and process since they're just plain ole' HTTP.

What if plain ole' HTTP wasn't better though? HTTP is a complicated, verbose protocol. You can see the urequests library I'm using on the hardware writing out the HTTP packets. It's super cool, but also a bit of a problem.

See, Verizon has a service level that caters these LTE-M Internet of Things devices. For $1/year I get a gigabyte of data. That's pretty generous, but a gig isn't a lot, and it's unclear whether or not I can add more data to the plan when I run out. So we maybe don't want to be speaking HTTP. And we certainly don't need to be sending JSON over HTTP.

At the end of the day, it's going to turn out that using a low-level socket is the best solution. The hardest part of that operation is setting up a server to listen for that incoming connection. I've already done that part once, so doing it again won't be difficult at all. It also means we can trim a lot of bits out of the data packet itself. JSON is cool, but we're transmitting a small amount of data in a known format to a static host. There's a reason the MT60X protocol is just a bunch of comma-separated data.

The Software (a.k.a. The Endpoint) #

Much of this is just rehashing work I've already done on this project and others.

The Listen Server #

I need to set up a listen socket to receive UDP packets on a specific port. The last iteration used a python script, but I want to see if I can get a similar result with PHP. PHP isn't well-suited to this use-case, but the rest of the endpoint is PHP so it makes sense.

Once it actually receives a packet, it's a simple matter of splitting it up and inserting the right data into the correct columns in MySQL (okay TECHNICALLY we're using MariaDB, but it's essentially MySQL).

Doing Stuff™ With Data #

Once I have the data in the DB, it's trivial to work with it. Parsing it, displaying it on a map, etc. Except not in this case because I'm going to over-do it. Long-term, I want to set up two tiers of access to the data:

Plus, like any good programmer, this will all be designed to be modular, so I can support multiple ankle monitors. Most importantly though, it needs to be SECURE AS FUCK, because in case you'd forgotten, we're working with my GPS coordinates here.

Ideally I'll support some sort of encryption for the data on the server when its at rest (in the database), but that's another long-term goal. I'm having enough trouble as it is setting up a fucking LEMP server ever since I stopped doing that.

Notifications #

The other fun part about ankle monitors are people being alerted when the ankle monitor is removed, or taken outside of a prescribed allowed area. I'm still debating how I want to handle this exactly, but it will probably wind up being a Discord server or Telegram bot that people can join/subscribe to.