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,772
IoT Drone - Part 2 - Sensors

Things used in this project

Hardware components

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

Story

Read more

Schematics

GPS/IMU Wiring

Guide for hooking up the GPS and IMU Sensors.

Ultrasonic Wiring

Guide for hooking up the Ultrasonic distance sensor.

Code

Particle Photon Code

C/C++
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 */
float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;

//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
  Serial1.begin(9600);
  
  //Begin Diagnostics
  Serial.begin(9600);
  Serial.println(F("Adafruit 10DOF Tester")); Serial.println("");
  
  /* Initialise the sensors */
  if(!accel.begin())
  {
    /* There was a problem detecting the ADXL345 ... check your connections */
    Serial.println(F("Ooops, no LSM303 detected ... Check your wiring!"));
    while(1);
  }
  if(!mag.begin())
  {
    /* There was a problem detecting the LSM303 ... check your connections */
    Serial.println("Ooops, no LSM303 detected ... Check your wiring!");
    while(1);
  }
  if(!bmp.begin())
  {
    /* There was a problem detecting the BMP085 ... check your connections */
    Serial.print("Ooops, no BMP085 detected ... Check your wiring or I2C ADDR!");
    while(1);
  }
  if(!gyro.begin())
  {
    /* There was a problem detecting the L3GD20 ... check your connections */
    Serial.print("Ooops, no L3GD20 detected ... Check your wiring or I2C ADDR!");
    while(1);
  }

}

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 = Serial1.read();
            
            // 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();
        
    }
    else{
        // 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) */
  accel.getEvent(&event);
  acX = event.acceleration.x;
  acY = event.acceleration.y;
  acZ = event.acceleration.z;
  
/* Calculate pitch and roll from the raw accelerometer data */
  accel.getEvent(&accel_event);
  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 */
  mag.getEvent(&mag_event);
  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) */
  gyro.getEvent(&event);
  gyroX = event.gyro.x;
  gyroY = event.gyro.y;
  gyroZ = event.gyro.z;  

  /* Display the pressure sensor results (barometric pressure is measure in hPa) */
  bmp.getEvent(&event);
  if (event.pressure)
  {
    /* Display atmospheric pressure in hPa */

    imuPress = event.pressure;

    /* Display ambient temperature in C */
    float temperature;
    bmp.getTemperature(&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);
    delayMicroseconds(10);
    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;

}

Webpage

JavaScript
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.
<!DOCTYPE HTML>
<html>
      <head>
    <style>
       #map {
        height: 400px;
        width: 50%;
       }
    </style>
  </head>
<body>
    <!--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>

    <br><br>
    <!--Add button to connect to Photon -->
    <button onclick="start()">Connect</button>
    
    <br><br>
    <!--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("https://api.spark.io/v1/devices/" + 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(e.data);
            
            var Pos = parsedData.data;
            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;
            posSpan.style.fontSize = "28px";
            
            var altSpan = document.getElementById("altitude");
            altSpan.innerHTML = "Altitude: " + Alt + " meters";
            altSpan.style.fontSize = "28px";
            
            var spdSpan = document.getElementById("speed");
            spdSpan.innerHTML = "Speed: " + Spd + " km/hr";
            spdSpan.style.fontSize = "28px";
            
            var headSpan = document.getElementById("heading");
            headSpan.innerHTML = "Heading: " + heading + " degrees";
            headSpan.style.fontSize = "28px";
            
            var pitchSpan = document.getElementById("pitch");
            pitchSpan.innerHTML = "Pitch: " + pitch + " degrees";
            pitchSpan.style.fontSize = "28px";
            
            var rollSpan = document.getElementById("roll");
            rollSpan.innerHTML = "Roll: " + roll + " degrees";
            rollSpan.style.fontSize = "28px";
            
            var distSpan = document.getElementById("obstDist");
            distSpan.innerHTML = "Distance to Obstacle: " + cm + " cm";
            distSpan.style.fontSize = "28px";
        
            var tsSpan   = document.getElementById("tstamp");
            tsSpan.innerHTML = "At timestamp " + parsedData.published_at;
            tsSpan.style.fontSize = "12px";
         
            curpos = new google.maps.LatLng(Lat, Lng);
           
            marker.setPosition(curpos); //Set the marker to the current position
            
            //Set the marker heading
            marker.setIcon({
                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>
    <script async defer
    src="ENTER_YOUR_GOOGLE_MAPS_API_HERE"> //Enter your Google Maps API
    </script>
</body>
</html>

Credits

Courtney Achen

Courtney Achen

4 projects • 28 followers
Electro-Mechanical Engineer

Comments