Courtney Achen
Published © GPL3+

IoT Drone - Part 2 - Sensors

Part 2 incorporates a GPS receiver, 10 DOF IMU, and Ultrasonic distance sensor to be used for flight controls.

IntermediateWork in progress4 hours2,675
IoT Drone - Part 2 - Sensors

Things used in this project

Hardware components

Particle Photon
MikroE GNSS Click
Adafruit 10-DOF IMU
Ultrasonic Distance Sensor


Read more


GPS/IMU Wiring

Guide for hooking up the GPS and IMU Sensors.

Ultrasonic Wiring

Guide for hooking up the Ultrasonic distance sensor.


Particle Photon Code

This is the code that will run on the Photon and publish information to the cloud. Note: TinyGPS and Adafruit_10DOF_IMU Libraries are needed in addition to this code.
// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_10DOF_IMU/Adafruit_10DOF_IMU.h"

// This #include statement was automatically added by the Particle IDE.
#include "TinyGPS/TinyGPS.h"

//Set-up GPS Library
TinyGPS gps;
char szInfo[100];
int updateRate = 1000; //update rate for the sensors in milliseconds
bool isValidGPS = false;

//GPS Global Variables
float lat, lon, falt, fkmph;
unsigned long age;

//IMU Global Variables
float acX, acY, acZ, gyroX, gyroY, gyroZ, pitch, roll, heading, imuPress, imuAlt, imuTemp;

//Set-up IMU
Adafruit_10DOF                dof   = Adafruit_10DOF();
Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(30301);
Adafruit_LSM303_Mag_Unified   mag   = Adafruit_LSM303_Mag_Unified(30302);
Adafruit_BMP085_Unified       bmp   = Adafruit_BMP085_Unified(18001);
Adafruit_L3GD20_Unified       gyro  = Adafruit_L3GD20_Unified(20);

/* Update this with the correct SLP for accurate altitude measurements */

//Ultrasonic Sensor Variables
float cm;

// Pins for Ultrasonic Distance Sensor
const int TRIG_PIN = 6;
const int ECHO_PIN = 7;

// Anything over 400 cm (23200 us pulse) is "out of range"
const unsigned int MAX_DIST = 23200;

void setup() {
    // The Trigger pin will tell the sensor to range find
  pinMode(TRIG_PIN, OUTPUT);
  digitalWrite(TRIG_PIN, LOW);
  //Begin serial communication for GPS data
  //Begin Diagnostics
  Serial.println(F("Adafruit 10DOF Tester")); Serial.println("");
  /* Initialise the sensors */
    /* There was a problem detecting the ADXL345 ... check your connections */
    Serial.println(F("Ooops, no LSM303 detected ... Check your wiring!"));
    /* There was a problem detecting the LSM303 ... check your connections */
    Serial.println("Ooops, no LSM303 detected ... Check your wiring!");
    /* There was a problem detecting the BMP085 ... check your connections */
    Serial.print("Ooops, no BMP085 detected ... Check your wiring or I2C ADDR!");
    /* There was a problem detecting the L3GD20 ... check your connections */
    Serial.print("Ooops, no L3GD20 detected ... Check your wiring or I2C ADDR!");


void loop() { 
    checkGPS(); //Gets GPS Values
    checkIMU();  //Gets IMU Values
    checkDist();  //Gets Distance to Obstacle
    //Assembles the message to publish to particle cloud
   sprintf(szInfo, "%.6f,%.6f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f", lat, lon, falt, fkmph, imuAlt, acX, acY, acZ,
            gyroX, gyroY, acZ, pitch, roll, heading, imuPress, imuTemp, cm);
    Spark.publish("gpsloc", szInfo); //Publish Data

    delay(updateRate);  //Loop Delay

void checkGPS(){
    for (unsigned long start = millis(); millis() - start < 1000;){
        // Check GPS data is available
        while (Serial1.available()){
            char c =;
            // parse GPS data
            if (gps.encode(c))
                isValidGPS = true;
    // If we have a valid GPS location then get it
    if (isValidGPS){
        gps.f_get_position(&lat, &lon, &age);
        falt = gps.f_altitude();
        fkmph = gps.f_speed_kmph();
        // Not a valid GPS location, just pass 0.0
        lat = 0.0;
        lon = 0.0;
        falt = 0.0;
        fkmph = 0.0;

void checkIMU(){
     /* Get a new sensor event */
  sensors_event_t event;
  sensors_event_t accel_event;
  sensors_event_t mag_event;
  sensors_vec_t   orientation;
  /* Display the results (acceleration is measured in m/s^2) */
  acX = event.acceleration.x;
  acY = event.acceleration.y;
  acZ = event.acceleration.z;
/* Calculate pitch and roll from the raw accelerometer data */
  if (dof.accelGetOrientation(&accel_event, &orientation))
    /* 'orientation' should have valid .roll and .pitch fields */
    roll = orientation.roll;
    pitch = orientation.pitch;
  /* Calculate the heading using the magnetometer */
  if (dof.magGetOrientation(SENSOR_AXIS_Z, &mag_event, &orientation))
    /* 'orientation' should have valid .heading data now */
    heading = orientation.heading;

  /* Display the results (gyrocope values in rad/s) */
  gyroX = event.gyro.x;
  gyroY = event.gyro.y;
  gyroZ = event.gyro.z;  

  /* Display the pressure sensor results (barometric pressure is measure in hPa) */
  if (event.pressure)
    /* Display atmospheric pressure in hPa */

    imuPress = event.pressure;

    /* Display ambient temperature in C */
    float temperature;
    imuTemp = temperature;

    /* Then convert the atmospheric pressure, SLP and temp to altitude    */
    /* Update this next line with the current SLP for better results      */
    float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;
    imuAlt = bmp.pressureToAltitude(seaLevelPressure, event.pressure, temperature); 


void checkDist (){
    unsigned long t1;
    unsigned long t2;
    unsigned long pulse_width;

    // Hold the trigger pin high for at least 10 us
    digitalWrite(TRIG_PIN, HIGH);
    digitalWrite(TRIG_PIN, LOW);

    // Wait for pulse on echo pin
    while ( digitalRead(ECHO_PIN) == 0 );

    // Measure how long the echo pin was held high (pulse width)
    // Note: the micros() counter will overflow after ~70 min
    t1 = micros();
    while ( digitalRead(ECHO_PIN) == 1);
    t2 = micros();
    pulse_width = t2 - t1;

    // Calculate distance in centimeters and inches. The constants
    // are found in the datasheet, and calculated from the assumed speed 
    //of sound in air at sea level (~340 m/s).
    cm = pulse_width / 58.0;



This is the webpage that will display the information from the sensors. Note: You will need to enter your Photon ID, Photon Access Token, and Google Maps API Key.
       #map {
        height: 400px;
        width: 50%;
    <!--Add variables -->
    <span id="position"></span><br>
    <span id="altitude"></span><br>
    <span id="speed"></span><br>
    <span id="heading"></span><br>
    <span id="pitch"></span><br>
    <span id="roll"></span><br>
    <span id="obstDist"></span><br>
    <span id="tstamp"></span>

    <!--Add button to connect to Photon -->
    <button onclick="start()">Connect</button>
    <!--Add Map -->
    <div id="map"></div>

    <script type="text/javascript">
    function start() {
        //variable to store map position
        var curpos = new google.maps.LatLng(parseFloat(0.0,0.0));
        //Set-up map parameters
        var map = new google.maps.Map(document.getElementById('map'), {
            zoom: 20, //set zoom level of map
            center: curpos, //center the map on the current position
            mapTypeId: 'satellite' //set to sattellite view
        //Set-up marker parameters
        var marker = new google.maps.Marker({
                position: curpos, //set the position of the marker to current gps position
                icon: {
                    path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW, //Set the icon for the maker
                    rotation: heading, //set the heading of the marker
                    fillColor: 'red', //set the fill color of the marker
                    fillOpacity: 1, //set the opacity of the marker
                    strokeWeight: 1.5, //set the weight of the border
                    scale: 5 //set the size of the icon
                map: map

        document.getElementById("position").innerHTML = "Waiting for data...";
        var deviceID = "YOUR_PHOTON_ID"; //Photon device ID
        var accessToken = "YOUR_PHOTON_ACCESS_TOKEN"; //Photon access token
        var eventSource = new EventSource("" + deviceID + "/events/?access_token=" + accessToken);

        eventSource.addEventListener('open', function(e) {
            console.log("Opened!"); },false);

        eventSource.addEventListener('error', function(e) {
            console.log("Errored!"); },false);

        eventSource.addEventListener('gpsloc', function(e) {
            //parse data from Photon
            var parsedData = JSON.parse(;
            var Pos =;
            var parsedPos = Pos.split(",");
            var Lat = parseFloat(parsedPos[0]);
            var Lng = parseFloat(parsedPos[1]);
            var Alt = parseFloat(parsedPos[2]);
            var Spd = parseFloat(parsedPos[3]);
            var imuAlt = parseFloat(parsedPos[4]);
            var acX = parseFloat(parsedPos[5]);
            var acY = parseFloat(parsedPos[6]);
            var acZ = parseFloat(parsedPos[7]);
            var gyroX = parseFloat(parsedPos[8]);
            var gyroY = parseFloat(parsedPos[9]);
            var gyroZ = parseFloat(parsedPos[10]);
            var pitch = parseFloat(parsedPos[11]);
            var roll = parseFloat(parsedPos[12]);
            var heading = parseFloat(parsedPos[13]);
            var Press = parseFloat(parsedPos[14]);
            var Temp = parseFloat(parsedPos[15]);
            var cm = parseFloat(parsedPos[16]);
            //Set the data for each variable and display
            var posSpan = document.getElementById("position");
            posSpan.innerHTML = "Lat/Long: " + Lat + ", " + Lng;
   = "28px";
            var altSpan = document.getElementById("altitude");
            altSpan.innerHTML = "Altitude: " + Alt + " meters";
   = "28px";
            var spdSpan = document.getElementById("speed");
            spdSpan.innerHTML = "Speed: " + Spd + " km/hr";
   = "28px";
            var headSpan = document.getElementById("heading");
            headSpan.innerHTML = "Heading: " + heading + " degrees";
   = "28px";
            var pitchSpan = document.getElementById("pitch");
            pitchSpan.innerHTML = "Pitch: " + pitch + " degrees";
   = "28px";
            var rollSpan = document.getElementById("roll");
            rollSpan.innerHTML = "Roll: " + roll + " degrees";
   = "28px";
            var distSpan = document.getElementById("obstDist");
            distSpan.innerHTML = "Distance to Obstacle: " + cm + " cm";
   = "28px";
            var tsSpan   = document.getElementById("tstamp");
            tsSpan.innerHTML = "At timestamp " + parsedData.published_at;
   = "12px";
            curpos = new google.maps.LatLng(Lat, Lng);
            marker.setPosition(curpos); //Set the marker to the current position
            //Set the marker heading
                path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
                    rotation: heading,
                    fillColor: 'red',
                    fillOpacity: 1,
                    strokeWeight: 1.5,
                    scale: 5
            map.setCenter(curpos); //Center the map on the current position
        }, false);
    <script async defer
    src="ENTER_YOUR_GOOGLE_MAPS_API_HERE"> //Enter your Google Maps API


Courtney Achen

Courtney Achen

3 projects • 26 followers
Electro-Mechanical Engineer


Add projectSign up / Login