Project tutorial

GPS Tracking Using Helium, Azure IoT Hub, and Power BI © Apache-2.0

Building a wireless, battery-powered GPS tracker using a Helium Atom, Arduino Zero, Azure IoT Hub, and Power BI.

  • 6,973 views
  • 2 comments
  • 24 respects

Components and supplies

Apps and online services

About this project

About this project

Helium exists to help developers build low power, secure, connected devices. In this project, we'll walk through using the Helium Atom Prototyping module with an Arduino Zero and Adafruit Ultimate GPS Breakout to seamless send data to Azure IoT Hub and then on to a Power BI report to visualize the data.

You can learn more about Helium's low power, secure, long range wireless products and buy all the required hardware at helium.com/store.

Hardware

The first thing you'll need to do is attach your Helium Atom Prototyping module to the Helium Atom Arduino adapter, and then to your Arduino. We're using an Arduino Zero for this guide, but you can use a Uno, Due, M0 or any other Arduino you'd like.

Once you've connected the Atom to the Arduino, you should have something that looks like this:

Next, we'll need to attach the Adafruit Ultimate GPS Breakout to the Arduino sandwich. We're going to use a breadboard here just to make things easier and clearer, but you could just as easily wire the GPS breakout directly to the Arduino. Your wiring should be in the form of:

Arduino 5v -> GPS VIN

Arduino GND -> GPS GND

Arduino D12 -> GPS TX

Arduino D10-> GPS RX

You should have something that looks like this once you're done:

Ok, now that we have the Arduino side of things setup we need to get the Helium Element connected and powered up. If you have a Cellular Element, you just need to power it on and wait for the light to turn a teal color. That's it. If you have an Ethernet Element, or if you prefer to use an Ethernet connection for your Cellular Element, plug the Ethernet port of your Element into a DHCP-enabled port like the back of a cable modem or wireless router. Once you power up the Element and the light on the front of the Element turns green, you're good to go.

Now that you've got the Arduino and Atom hardware setup, and the Element is powered up and connected, it's time to set this all up in the Helium Dashboard.

Helium Dashboard & Channels

First, we need to add our Atom and Element to our user account in the dashboard. If you don't have a user account but have equipment in hand, please email us at support@helium.com, otherwise login with the credentials you received when you purchased your hardware from Helium.

Once logged in, we'll need to first activate our Element. Clicking on Elements on the left, then Add New in the top right gets you to the right place. Type in the last 4 of the MAC address and the HVV from the label on the back of the Element, give it a name, and optionally add some tags. Click Activate Element to get it going.

We'll then need to repeat the same process for the Atom. Again, click Atoms on the left, and Add New, and supply the MAC and HVV from the Atom label, optionally add some tags, and click Activate Atom.

Next, we'll need to create an Azure IoT Hub to act as an ingestion point to Azure for all your Helium Atom devices. We're going to assume you have an Azure account already, if not you'll need to create one.

Once you've logged in to Azure, we're going to create an IoT Hub. Click on the + in the top left, then Internet of Things, then IoT Hub:

You'll need to make some choices about how you want to set your IoT Hub up, and then hit Create. We recommend that you select Pin to Dashboard at the bottom to make it easier to find later:

Once you're done and the hub is finished deploying (this might take a few minutes), click on it from your dashboard. To setup the Helium Channel we'll need one piece of information from within the hub. From within the IoT Hub choose Shared Access Policies on the left, select RegistryReadWrite, and then click the copy icon next to Connection string—primary key on the right. This will copy the connection string to the clipboard which we'll be using back at the Helium Dashboard. Be careful not to share this string with anyone you don't trust. (We're just using a temporary hub in this example.)

Back at the Helium Dashboard, we now need to create a Channel that maps to the Azure IoT Hub you just created. First, click on Channels on the left, and click on Azure IoT Hub below Create New Channel. We're going to paste in the Connection String we copied from Azure to create the Channel on Helium:

Give the Channel a name and then hit Create. It's important to remember the name, as you'll need that in your Arduino code to send data to this Channel.

Arduino Sketch

With the Azure IoT Hub set up and a Helium Channel created, we now need an appropriate Arduino Sketch that makes use of the Helium Arduino and Adafruit GPS libraries to send GPS locations to Helium, and then on to our newly created Azure channel.

Firstly, you'll need both the Helium Arduino library and the Adafruit GPS library installed in your Arduino IDE. The easiest way to accomplish this is to go to the Sketch -> Include Library -> Manage Libraries menu from within the IDE, and search for both Helium and Adafruit GPS and hit Install.

The below sketch initializes the Atom, creates a Channel that matches the Azure IoT Hub Channel we created in the previous step, collects GPS data from the Adafruit GPS board, formats it into JSON, then sends it over the Helium network.

There's more detail and guides for the Helium Atom Arduino library here.

/* 
* Copyright 2017, Helium Systems, Inc. 
* All Rights Reserved. See LICENCE.txt for license information 
*/ 
#include "Arduino.h" 
#include "avr/dtostrf.h" 
#include "Helium.h" 
#include <math.h> 
#include <Adafruit_GPS.h> 
#include "wiring_private.h" // pinPeripheral() function 
#define atom_serial Serial1
//for GPS 
#define PIN_SERIAL2_RX (34ul) // Pin description number for PIO_SERCOM on D12 
#define PIN_SERIAL2_TX (36ul) // Pin description number for PIO_SERCOM on D10 
#define PAD_SERIAL2_TX (UART_TX_PAD_2) // SERCOM pad 2 
#define PAD_SERIAL2_RX (SERCOM_RX_PAD_3) // SERCOM pad 3 
Uart Serial2(&sercom1, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX);
void SERCOM1_Handler() 
{ 
 Serial2.IrqHandler(); 
}
Helium helium(&atom_serial); 
Channel channel(&helium);
//GPS stuff 
#define gps_serial Serial2 
Adafruit_GPS GPS(&gps_serial);
void setup() {   
   pinPeripheral(10, PIO_SERCOM); 
   pinPeripheral(12, PIO_SERCOM);
   Serial.begin(115200); 
   Serial.println("Starting");
   // Begin communication with the Helium Atom 
   // The baud rate differs per supported board 
   // and is configured in Board.h 
   helium.begin(helium_baud_b115200 );
     
   // Connect the Atom to the Helium Network 
   Serial.print("Connecting - "); 
   int status = helium.connect();     
   while (status != helium_status_OK) 
   { 
       status = helium.connect(); 
   } 
   Serial.println("Succeeded");
   // Begin communicating with the channel. This should only need to 
   // be done once. 
   // 
   // NOTE: Please ensure you've created a matching channel in the Helium Dashbaord 
   int8_t result; 
   Serial.print("Creating Channel - "); 
   status = channel.begin("Hackster Guide", &result); 
   if (helium_status_OK == status && result == 0) 
   { 
       Serial.println("Succeeded"); 
   } 
   else 
   { 
       Serial.println("Failed"); 
   }
   delay(1000); 
   //Init GPS 
   GPS.begin(9600); 
   GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); 
   GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); 
   GPS.sendCommand(PGCMD_NOANTENNA); 
   delay(2000); 
}
uint32_t timer = millis(); 
void loop() 
{ 
 char c = GPS.read(); 
 if (GPS.newNMEAreceived()) { 
   if (!GPS.parse(GPS.lastNMEA())) 
     return; 
 }
 if (timer > millis())  timer = millis(); 
 if (millis() - timer > 2000) { 
   timer = millis(); // reset the timer 
   if (GPS.fix) {          
     char lat[10]; 
     char lon[10];       
     char data[100];       
     dtostrf(GPS.latitudeDegrees, 4, 4, lat); 
     dtostrf(GPS.longitudeDegrees, 4, 4, lon); 
     //format a JSON object of lat/lon to send to Azure 
     sprintf(data, "{\"lat\": \"%s\", \"lon\": \"%s\"}", lat, lon); 
     // Send the location data to the configured channel 
     int8_t result; //the response from the Helium Router 
     Serial.print("Sending - "); 
     int status = channel.send(data, strlen(data), &result); //status is the response from the Atom serial connection 
     if (helium_status_OK == status && result == 0) 
     { 
         Serial.print("Successfully sent "); 
         Serial.println(data); 
     } 
     else 
     { 
         Serial.println("Failed"); 
     } 
   } else { 
     Serial.println("No GPS fix!"); 
   } 
 } 
} 

With the libraries installed, create a new sketch (File -> New from within the Arduino IDE), and paste in the above code. Connect up your Arduino/Atom sandwich via USB, and hit the Upload button which looks like a right-hand arrow.

After a few seconds you should see the Atom lights start blinking red and blue, and then settle into a steady red blinking pattern. This means the Atom is now connected to the Helium network. The GPS FIX light should also be blinking with a slow red pattern. Once this stops, the GPS module has fix on your location and will start sending data to Helium. It helps to try and place the GPS near a window or ideally be completely outside.

You can check the Serial Monitor (Tools -> Serial Monitor) from within the Arduino IDE to see the debug output. You should see messages showing connecting, creating channel, and then a sequence of No GPS fix! messages until a satellite fix is acquired.

Checking that data is being sent correctly

Now that the Arduino, Atom and GPS are up and running, we can return to the Helium Dashboard and make sure that data is making its way up to the Azure channel.

Clicking on Channels on the left and then selecting the Azure channel you already made in the Active Channels section should bring you to a detail view - scrolling down here should show you the Event Log, where you'll hopefully see something like the screenshot below. This shows the timestamp and size of data, as well as reporting on whether it was successfully routed to Azure:

If you take a look at your Azure IoT Hub under the Device Explorer navigation you should also see your Helium Atom device listed under its MAC address. Clicking on it will also confirm that we've secured this device automatically, without any configuration required, using a hardware-signed X509 certificate.

Getting the data into Power BI

We'll assume that you have a Power BI account already setup, and a workspace already created. (If not, head over to www.powerbi.com and set one up.)

With your Power BI account setup, we'll now need to create an Azure Stream Analytics Job to take the data from the Azure IoT Hub and deliver it to Power BI.

From within the Azure dashboard, click New in the top left, and search for Stream Analytics Job, selecting it once it appears. Click Create in the bottom left, select the options, and hit create to set it up.

Once deployed, we'll need to tell the Streaming Job to use the IoT Hub as an input, and Power BI as an output. From within the Streaming Job page, click Inputs from the left menu, then Add.

Give the job an alias (name), then select IoT Hub from the source dropdown. Pick the appropriate IoT Hub from the IoT Hub dropdown, and leave everything else as default. You should end up with something looking like this:

Once the input is created, we'll create an output that connects to Power BI. From the Streaming Job page, click Outputs from the left menu, and then Add. We'll choose Power BI from the Sink dropdown. You'll have to then authorize your Power BI account and select the Workspace you want to use. Create a name for the table. You'll end up with something like this:

The final step is to create a Query that links the input to the output. Again, click Query from the left menu. You'll want your query to basically be in the form that matches the screenshot below, depending on what you named your inputs and outputs from the previous step. This will select all data coming from the IoT Hub and output it to the Power BI workspace.

Hit Save in the top left, and assuming you named everything correctly, you're almost done. Now return to the Overview page and hit Start in the top navigation bar, and after a few minutes you'll have data streaming into Power BI.

Visualizing in Power BI

Now that the data is flowing, we can do a quick visualization in Power BI to show us where everything is on a map.

Head over to your Power BI Workspace, and click + Create in the top right corner, and select Report. You'll then select the dataset you created in the Azure Streaming Job and hit Create.

Next we'll choose the map visualization in the right hand pane, it looks like a white globe. On the far right you'll see the fields of data that we've been sending from the Arduino. Drag lat to Latitude and lon to Longitude in the pane to the left. You can also drag EventProcessedUTCTime to Tooltips if you want to see when it was sent on mouseover. Then hit Save in the top right. That's it! You'll now have a map showing the location of your Helium Atom attached to the Arduino.

Code

Atom + Arduino + Adafruit GPSC/C++
/*
 * Copyright 2017, Helium Systems, Inc.
 * All Rights Reserved. See LICENCE.txt for license information
*/

#include "Arduino.h"
#include "avr/dtostrf.h"
#include "Helium.h"
#include <math.h>
#include <Adafruit_GPS.h>
#include "wiring_private.h" // pinPeripheral() function

#define atom_serial Serial1
//for GPS
#define PIN_SERIAL2_RX (34ul) // Pin description number for PIO_SERCOM on D12
#define PIN_SERIAL2_TX (36ul) // Pin description number for PIO_SERCOM on D10
#define PAD_SERIAL2_TX (UART_TX_PAD_2) // SERCOM pad 2
#define PAD_SERIAL2_RX (SERCOM_RX_PAD_3) // SERCOM pad 3

Uart Serial2(&sercom1, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX);

void SERCOM1_Handler()
{
  Serial2.IrqHandler();
}

Helium helium(&atom_serial);
Channel channel(&helium);

//GPS stuff
#define gps_serial Serial2
Adafruit_GPS GPS(&gps_serial);

void setup() {  
    pinPeripheral(10, PIO_SERCOM);
    pinPeripheral(12, PIO_SERCOM);
    
    Serial.begin(115200);
    Serial.println("Starting");

    // Begin communication with the Helium Atom
    // The baud rate differs per supported board
    // and is configured in Board.h
    helium.begin(helium_baud_b115200);    

    // Connect the Atom to the Helium Network
    Serial.print("Connecting - ");
    int status = helium.connect();    
    while (status != helium_status_OK)
    {
        status = helium.connect();
    }
    Serial.println("Succeeded");
    
    // Begin communicating with the channel. This should only need to
    // be done once.
    //
    // NOTE: Please ensure you've created a matching channel in the Helium Dashbaord
    int8_t result;
    Serial.print("Creating Channel - ");
    status = channel.begin("Hackster Guide", &result);
    if (helium_status_OK == status && result == 0)
    {
        Serial.println("Succeeded");
    }
    else
    {
        Serial.println("Failed");
    }
    
    delay(1000);

    //Init GPS
    GPS.begin(9600);

    GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
    GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
    GPS.sendCommand(PGCMD_NOANTENNA);

    delay(2000);
}

uint32_t timer = millis();
void loop()
{
  char c = GPS.read();
  
  if (GPS.newNMEAreceived()) {
    if (!GPS.parse(GPS.lastNMEA()))
      return;
  }
  
  if (timer > millis())  timer = millis();

  if (millis() - timer > 2000) {
    timer = millis(); // reset the timer
    if (GPS.fix) {         
      char lat[10];
      char lon[10];      
      char data[100];      
      dtostrf(GPS.latitudeDegrees, 4, 4, lat);
      dtostrf(GPS.longitudeDegrees, 4, 4, lon);
      //format a JSON object of lat/lon to send to Azure
      sprintf(data, "{\"lat\": \"%s\", \"lon\": \"%s\"}", lat, lon);

      // Send the location data to the configured channel
      int8_t result; //the response from the Helium Router
      Serial.print("Sending - ");
      int status = channel.send(data, strlen(data), &result); //status is the response from the Atom serial connection
      if (helium_status_OK == status && result == 0)
      {
          Serial.print("Successfully sent ");
          Serial.println(data);
      }
      else
      {
          Serial.println("Failed");
      }
    } else {
      Serial.println("No GPS fix!");
    }
  }
}

Comments

Similar projects you might like

A DIY Smart Insole to Check Your Pressure Distribution

Project tutorial by Juliette van der Pas

  • 1,678 views
  • 7 comments
  • 15 respects

Arduino LIDAR

Project tutorial by abhinav

  • 14,829 views
  • 4 comments
  • 24 respects

Relativ - Build Your Own VR Headset for $100

Project showcase by Relativty

  • 4,855 views
  • 1 comment
  • 5 respects

VU Meter on Steroids: Arduino Nano and WS2812Bs

Project in progress by WannaDuino

  • 4,451 views
  • 14 comments
  • 16 respects

NeoPixel Lightsabers w/ Party Modes -- Arduino-Controlled

Project tutorial by Modustrial Maker

  • 451 views
  • 0 comments
  • 5 respects

Sprout: Modern Indoor Self Watering Planter

Project tutorial by Jonathan Pereira

  • 4,353 views
  • 4 comments
  • 24 respects
Add projectSign up / Login