A to B in 8KB: Using HERE on Arduino

08 August 2016 by Alex Osaki

I'm not a programmer. My degree is in anthropology. Ask me about the Mayan afterlife and I can help. Ask me about threading and I'll tell you I mostly stick to cotton. 

So naturally, I decided it would be a good idea to learn how Arduino works. And of course, I knew that in addition to our native SDKs on mobile platforms, HERE has a set of cross-platform APIs. That was my starting point. 

At first, I wanted to build an LCD screen that would tell you when the next train was coming. But then I thought: location technology is all about digitizing the real world. Why not take that data, and bring it back to life as something physical? 

The goal became to build a gadget that compares driving, public transit and biking routes, finds the fastest one, then moves a needle to tell you how long the route will take. In the end, the hardware kit consists of: 

  • An Arduino Mega2560
  • An Ethernet shield
  • A 9g, 5V servomotor
  • A momentary button
  • A linear B50K potentiometer
  • 3 green, 3 red, and 1 blue LED (and their resistors)

HERE has REST APIs for geocoding, indoor maps, weather, traffic and a bunch of other things. But what I was interested in was the Routing API. At least, it looked simple: the routing API takes a start and end point and a travel mode, then returns the route details. Sounds good! 

I figured this was easiest to tackle in sequence: 

  1. Connect to the HERE routing service
  2. Make sense of the response
  3. Do some Arduino magic

 Step One: Making a connection

First things first! A request to the HERE routing service looks like this:

I put my app details in (you can get your own here), picked a start and end point (in latitude,longitude format), chose "shortest;car" as my mode and pasted the whole string into a browser to see if I got something back. Great success! At least, not an error code:

So I needed to consider three variables: waypoint0 (my starting point), waypoint1 (my destination) and mode (my travel type). To compare the fastest way to get between point A and B, though, only travel type needs to change. The others are constants. So I decided to start like this: 

I called it "basicAuth" because it doesn't change and it authenticates me to HERE (I capitalized "Auth" because that's a thing programmers do, right?). With that, we're ready to test this out on the Arduino. I'm using Arduino's Ethernet library, with a new EthernetClient helpfully named "client", connecting to server "route.cit.api.here.com":

Now, Arduino does not exist in an event-driven universe. There is a loop, and it runs forever. In this case, while we have a connection, this is stored in a buffer until you do something with it. Let's try:

And the serial monitor shows us:

Eek. We've hit our first roadblock: the response from the HERE routing server is compressed. While I'm sure there are gzip libraries for Arduino, to my social sciences mind it doesn't make sense to try to implement them. Instead, we get around this with an additional header:

Back to the serial monitor:

That's more like it!

Step Two: ETA! JSON? IDK :(

Now, the response that comes back from the routing server contains a lot of information. It includes the parameters in your request (start point, end point, travel type) and details about the service itself (the map version, routing version, timestamp). It includes a separate child for each maneuver, and a simple English instruction to guide you along it ("Turn left onto 4th St; go for 320 m"). But I don't care about that! I just want to know how long it takes, remember?

Thankfully, that information can be seen in the child "summary":

Now it's time for roadblock number two. The data from our service comes back in JSON format. I'm not unfamiliar with JSON. In Python, this would be a simple matter of importing the json library and doing something like:

But we're not using Python, we're using a $12 board with 8 kilobytes of RAM. Deserializing the route response data properly would take more memory than we have. So instead, we're going to use force force. Warning: this is the kind of hack you get when you let an anthropologist write code. We start by creating a buffer string where we can stuff things, and then we take our response into bite-sized chunks:

Basically, we take data from the Ethernet buffer and push it into "results." I know that the data, being JSON, is separated by commas. I know that the "travelTime" element is always going to be pretty small. So every time we hit a comma or "results" gets to 64 characters, we can start over. 

But first, we check and see what's sitting in "results." I know that if "results" contains the travelTime element, "travelTime" will be the characters between 1 and 11 in the string. And if that's true, then removing that substring and some whitespace using Arduino's built-in string functions will give us the value of "travelTime." What's left is an integer (208, in this case) that counts the route ETA in seconds.

Mission accomplished: we now know it will take 208 seconds to drive between my start and end points. Great! 

Step Three: Switching it all up.

Now, my goal is to compare three different options. But all this takes is changing one variable — the mode — in the request I'm sending to the routing API. In the end, we'll have three integers for each of the three modes. And to keep things simple, one more integer to keep track of the fastest mode:

Because only one variable in the request needs to change, I decided the best way was to set a single variable telling us which mode we're using and one storing the fastest mode:

That way, in the function that handles the connection to the server, we can do everything via a switch:

This also lets us handle a couple of mode-specific variables. For car routing, I want to take traffic into account. For public transit routing, I need to specify a departure time. The rest of the code is run the exact same way — read the JSON into my ad hoc string buffer until you find the travel time — except that now, depending on what mode_type is set to, it saves the travel_time to the appropriate value, like so:

This logic also checks to see if the travel time is faster than the existing "fastest_traveltime". If so, it updates that variable and sets the variable "fastest_mode" appropriately. Also, I made driving mode 1 instead of 0, so that the Arduino doesn't bother executing any code unless mode_type > 0 — that is, unless we're actively calculating route ETAs. This also means that when we've calculated the last route (a bike route) it resets the variable to 0 and lets the client disconnect.

Step Four: Activate all Blinkenlights!

By the end of this exercise, I know which mode turned out to be the fastest and I know how long the route will take to complete. Everything else is fairly simple. I use one LED for each mode, so if transit turns out to be the fastest mode, a line like "digitalWrite(transit_led,HIGH)" will turn its light on. That takes us to the dial, which points to indicate how long the trip will take.

The servomotor articulates between 0 and 180 degrees, with my dial taking up about 120 degrees of arc. Let's say that represents an hour. In this case, every degree represents 30 seconds (3600 seconds in an hour / 120 degrees). So to know how far we need to move the servo, we can do this: 

Back in the beginning I said that a few more LEDs and a potentiometer were involved? What's that about? Well, I wanted to be able to show that the best way to get from A to B can depend on different situations. For example, depending on traffic, if your start and end points are close to a train station it might be faster to take a train than to drive. If your end point is up a one-way street, it might be faster if you're a cyclist who dismounts and walks up the street (because you'd never bike that — right?) rather than to drive in a circle.

So I rebuilt the code that connects to the HERE routing APIs. I kept the same starting point, but picked five different end points of varying locations and distances. Like so:

Then, all we have to do is add one more variable to our connection string:

I use the position of a potentiometer to determine which of the five waypoints I want to route to, and use three red LEDs to indicate which one is being used (1 only, 1 and 2, 2 only, 2 and 3 and 3 only). Because of the flexibility of HERE's routing APIs, you could scale this as far as you want. One idea: use two pots, and use them to set bearing and distance away from your start point so you can calculate any route!

Conclusion

Obviously, I can't swear by all this code — though I can definitely swear at it. But it proved to be an interesting challenge, and a good way of demonstrating how simple, effective services can move topics like location into entirely new places. The idea that $20 in Arduino bits and LEDs can turn into a machine that predicts the fastest way to get around your town is pretty cool to me. That it can tell you how long it will take, including real-time information on train schedules and traffic conditions? Even cooler.

The fact that it's all handled via an intuitive REST interface means that essentially any connected device has access to a full set of location services. Not just phones, or GPS devices, but literally anything you can connect to the Internet, and in just a few lines of code. In other words, the best way to make it big is to make it small — lightweight, flexible and powerful enough to be accessible from any device, anywhere. 

Even an anthropologist can understand that :)

top 

You cannot use this account to purchase a commercial plan on Developer Portal, as it is already associated to plans with different payment methods.

To purchase a commercial plan on Developer Portal, please register for or sign in with a different HERE Account.

Something took longer than expected.

The project should be available soon under your projects page.

Sorry, our services are not available in this region.

Something seems to have gone wrong. Please try again later.

We've detected that your account is set to Australian Dollars (AUD).
Unfortunately, we do not offer checkouts in AUD anymore.
You can continue using your current plan as normal, but to subscribe to one of our new plans,
please register for a new HERE account or contact us for billing questions on selfservesupport@here.com.