John SansburyJerod Zimmerman
Published © GPL3+

MotoidIoT

A telemetry system designed with motorcycles in mind, that will gather data to help improve your performance.

IntermediateFull instructions provided6 hours284
MotoidIoT

Things used in this project

Hardware components

Photon
Particle Photon
×2
Adafruit Ultimate GPS Breakout
Adafruit Ultimate GPS Breakout
If you pair a C1220 backup battery with the GPS it will allow for faster lock times on cold starts.
×1
Adafruit BNO055 Breakout Board
×1
Adafruit SD Card Breakout Board
×1
LED (generic)
LED (generic)
×1
Resistor 330 ohm
Resistor 330 ohm
This is to go in series with the LED to reduce the voltage drop over the LED.
×1
Slide Switch
Slide Switch
×1
Relay Module
×1
Breadboard (generic)
Breadboard (generic)
×2
USB Battery
This is not the battery we used but any USB battery like this will work.
×2
Project Box
Any type of box will do. Even a cardboard one (Thats what I used for my 1st gen prototype). This is just a box I had on hand that fit the bill perfectly.
×1
Jumper wires (generic)
Jumper wires (generic)
×1
AA Batteries
AA Batteries
This is used to supply power to the relay. You can buy a battery rig to connect them or you can do what we did for prototyping purposes and tape them together in series.
×4
microSD Card
×1
Protoyping Board
This is optional. This will work in a breadboard though connectivity issues may occur. By soldering your connections together on this prototyping pcb you will ensure that this does not happen
×1

Software apps and online services

Microsoft Excel

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
This is completely optional. You can leave it on the breadboard if you want and depending on the quality of your wires may not have any connection issues.

Story

Read more

Custom parts and enclosures

MotoidIoT Telemetry Fritzing File

These breakout boards are not ideally set up for the input and output pins. If you were going to make a PCB you would probably need to have it be double sided or have some sort of crossover set up.

Schematics

MotoidIoT Schematic

Please excuse the mess as this is my first time using Fritzing. If you can't tell where wires go, pin connections are also listed below in the code as well as the the Fritzing file being available for download.

Receiver Schematic

The receiver used this 4 channel relay module designed for 3.3V logic:

https://www.amazon.com/gp/product/B00WJXJ27I/ref=oh_aui_search_detailpage?ie=UTF8&psc=1

This module was chosen specifically because the Particle Photon digital pins will register 3.3V when in a HIGH state. The module contains built in transistorized control over the relay coils, using 5V from an additional source, in this case (3) NiMH AA cells in series (in this case just under 5V, but it worked just fine). Initially, a single 9V alkaline cell was used, but would not allow the relay to unlatch after activation, as the voltage on the digital pins in a LOW state were still reading anywhere between 20 and 50mV. Attempts at using a pulldown resistor were unsuccessful. Ultimately, switching to (3) AA cells alleviated this issue.

Note: typically, activating a relay coil will result in switching from NO to NC. However, the module used in this project operated on reversed logic. In order to achieve the NO (default) state, the coil had to be active. While this is simple to account for with software, it is not the preferred method of implementation. A loss of power to the relay module would result in a NC state, thus allowing the load to draw current. You get what you pay for...just something to note for anyone interested in using these relay modules. Yes, the circuit could be altered instead of the software, (and would be preferred for a permanent fixture) but the software change was easier to implement at the time.

Code

MotoidIoT Telemetry system

Arduino
This is the code for the telemetry system. This is still a working prototype and may change in the future.
You will want to go into the proxHome() function and input your own own home's coordinates. Also the wifi does not listen for nearby networks and unless you already have wifi credentials saved into the device you will want to go into the wifiConnect() function and input your credentials.
The majority of this code was hacked from open source code so feel free to take it and improve on it. Finally if you have any suggestions let me know as I am still a budding hacker!
/*
 * Project MotoidIoT_v1.02.1
 * Description: Motorcycle Telemetry system using a Particle Photon,
  the Bosch BNO055 Absolute IMU,  Adafruit Ultimate GPS Breakout board,
  Adafruit SD Card Breakout board, and a two position switch.  Adapted from Rossi telemetry system for
  Arduino Mega board using the same sensor set. Adapted to work with Particle
  for MEGR-3171 IoT Project. This version wifi disabled to focus on datalogging alone by default but
  can be switched on. Wifi and reporting can be enabled by momentarily pressing the setup button once.
  Wifi can then be disabled by pressing and holding the wifi button until the LED turns white then released.
 * Author: John Sansbury
 * Date: Mar 30,2017
 *Revised: April 13, 2017

 Connections
 ===========
 BNO055                 SD Card               GPS                   Switch              LED
 Connect SCL to D1      Connect CS to A2      Connect TX to RX      GND to side one     Long leg (+) to D4
 Connect SDA to D0      Connect DI to A5      Connect RX to TX      3V3 to side two     Short Leg (-) to GND
 Connect Vin to 3V3     Connect DO to A4      Connect GND to GND    D7 to middle        Use a 330 Ohm resistor
 Connect GND to GND     Connect CLK to A3     Connect VIN to 3V3                        in series to reduce voltage
                        Connect GND to GND
                        Connect 3V to 3V3
    BNO055 Board layout
        +----------+
        |         *| RST   PITCH  ROLL  HEADING
    ADR |*        *| SCL
    INT |*        *| SDA     ^            /->
    PS1 |*        *| GND     |            |
    PS0 |*        *| 3VO     Y    Z-->    \-X
        |         *| VIN
        +----------+
  Note for SD Card: Two position switch was added to allow for the SD card to be removed with out causing data
  corruption. Data is only written when switch is in high position (D7 LED will be lit) otherwise loop becomes
  paused until reactivated. Begins a new data file everytime switch is activated.

  Note for BNO055: Calibration for BNO055 must be done prior to running this sketch for optiumum performance.
  Accelerometer and Gyrometer output will be based upon these calibration values. The magnetometer will depend
  upon the calibration values as well but will not give a proper heading until the device has been moved enough
  for it to locate true north.
  */
  /****************************************************************************************************************/
/*
    WARNING: REMOVING SD CARD WITH SWITCH IN HIGH POSITION (D7 LED ON) CAN CAUSE SD CARD CORRUPTION AND A LOSS OF DATA
*/
/******************************************************************************************************************/
  #include "Particle.h"
  SYSTEM_MODE(SEMI_AUTOMATIC)
  //SD Card Pre Code----------------------------------------------------------------------------------------------
#define CLK  A3  // FOR SPI 52 on Mega, 13 on UNO, 24 on Particle (A3)
#define SDO  A4  // AKA MISO 50 on Mega, 12 on UNO, 25 on Particle (A4)
#define SDI  A5  // AKA MOSI 51 on Mega, 11 on UNO, 23 on Particle (A5)
#define SD_card A2  //SS for SD (Whatever pin you select), 2 on Particle (A2)
#include <SdFat.h>
  SdFat SD; File dataFile; char filename[15]; // for SD card datalogging name
  void dataWrite(); void nameFile(); void SD_cardSetup(); //declare function prototypes
 //GPS Pre Code-----------------------------------------------------------------------------------------------------
  #include <Adafruit_GPS.h>
  USARTSerial& mySerial = Serial1;
  Adafruit_GPS GPS(&mySerial);
  uint timeZone = 4; //Changes depending on time zone and day light savings. Without daylight savings EST=4, PST=7. Add one more when not DST.
  #define GPSECHO false // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
  boolean usingInterrupt = false;
  String gpsRead(); void gpsParse(); void proxHome(); //Decleration of function prototype
  uint gpsFix=0; /*uint sat;*/ float lastSpeed; uint g=0; //String gpsString;
  //BNO055 Pre Code--------------------------------------------------------------------------------------------------
  #define BNO055_SAMPLERATE_DELAY_MS (10) /* Set the delay between fresh samples for BNO055 */
  #include "Adafruit_BNO055.h"
  #include "Adafruit_Sensor.h"
  #include "imumaths.h"
  Adafruit_BNO055 bno = Adafruit_BNO055(55); //Create object for BNO055 sensor
  String orientation(); void setCal();

  uint iteration=0; uint32_t timer = millis(); uint32_t gpsTimer = millis(); uint32_t reportTimer=millis();
  uint gpsCount; uint f=0; uint atHome; bool wStatus; uint parseError=0; uint period; uint32_t lastIteration;
  bool lastWstatus=FALSE; bool noWifi=TRUE; void flashError(); uint fileTime=30;//fileTime is the length you want the file to be in minutes
  void wifiConnect(); void report();
//SETUP---------------------------------------------------------------------------------------------------------------------
void setup() {
  pinMode(D7,INPUT);
  pinMode(D4,OUTPUT);
  System.on(button_status, button_handler);
  wifiConnect(); //Currently defaults to wifi off to focus on datalogging but can be turned on via setup button
  Serial.begin(115200);
  /*GPS Sensor Setup-----------------------------------------------------------------------------------------------*/
  //GPS.sendCommand(PMTK_SET_BAUD_57600); //Can change the baud rate to a higher speed but is prone to errors which can cause a temporary lose of fix.
  GPS.begin(9600);
  delay(1000);
  //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY); //Time, Date, Position, Speed
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); //Contains fix and altitude data
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_5HZ);
  GPS.sendCommand(PMTK_API_SET_FIX_CTL_5HZ);
  delay(1000);
  /* Initialise BNO055 sensor------------------------------------------------------------------------------------- */
  while(!bno.begin()) {
    if(!noWifi && Particle.connected()) Particle.publish("BNO055 Sensor Setup", "Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
    flashError();
  }
  setCal(); /* Display some basic information on this sensor */
  bno.setExtCrystalUse(true); //This allows the heading to be zeroed to north rather than start up position. Ensure this comes after all set up and calibration for sensor
  /*SD Card Setup--------------------------------------------------------------------------------------------------*/
  pinMode(SD_card,OUTPUT);
  if (wStatus=digitalRead(D7))  SD_cardSetup();
  timer=millis(); gpsTimer=millis(); reportTimer=millis();
 }

 void loop() {
   if(gpsTimer > millis()) gpsTimer=millis();
   if (timer > millis())  timer = millis();
   if (reportTimer > millis())  timer = millis();
   int elapsedTime = millis()-gpsTimer;
   gpsParse(); // run gpsParse outside of timer loop so that GPS can parse as soon as a new status is available
   gpsFix=GPS.fix;
   wStatus=digitalRead(D7);
   if (wStatus==1 && lastWstatus==FALSE && gpsFix==1) SD_cardSetup();
   if (wStatus==0 && lastWstatus==TRUE){
     dataFile.close();
     lastWstatus=FALSE;
   }
   if(gpsFix==0) digitalWrite(D4,HIGH);
   else digitalWrite(D4,LOW);
   elapsedTime = millis() - timer;
   if(elapsedTime > 9 && gpsFix==1) {
     timer = millis(); // reset the timer
     String dataString=0;
     dataString += String(iteration); dataString += ",";
     dataString += String(elapsedTime); dataString += ",";
     dataString+=gpsRead();
     dataString += orientation();
     dataString += String(parseError); dataString += ",";
     elapsedTime= millis()-timer;
     dataString += String(elapsedTime);
     if (wStatus==1){
       dataWrite(dataString);
       lastWstatus=TRUE;
       iteration++;
     }
   }
   elapsedTime=millis()-reportTimer;
   //Only allow the wifi to be turned on and off (voluntarily)
   if (System.buttonPushed() < 500 && System.buttonPushed() >5 && wStatus==0) {//click and release. LED turns blue and wifi will attempt to connect
     //Only allowed to turn wifi on if not logging data so as not to interrup logging
     if (noWifi==TRUE){
        noWifi=FALSE;
        wifiConnect();
      }
   }
   else if (System.buttonPushed() > 500 && System.buttonPushed() <1500) {// with wifi on click and hold until LED turns white release and wifi will be off
     if (noWifi==FALSE){
       noWifi=TRUE;
       wifiConnect();
     }
   }
   if(!noWifi && !Particle.connected()){// detects if wifi connection has been lost and shuts off the wifi
     //to prioritize data collection and ensure it doesnt get stuck looking for a signal
     noWifi=TRUE;
     wifiConnect();
    }
   if(elapsedTime >1999){
     reportTimer=millis();
     report();
   }
   if (iteration >= lastIteration) nameFile();
 }
 /* Below function is a a testing function that was used for troubleshooting communication between
 the particles when communicating proximity. This allowed the relay on the reciever unit to be activated
 without having to leave my house and instead just use a switch.*/
 /* void loop() {
    wStatus=digitalRead(D7);
    if (wStatus==true) {
      if (timer > millis())  timer = millis();
      elapsedTime = millis() - timer;
      if (elapsedTime > 1999){
      report();
      timer = millis();
    }
    }
    else {
      if (timer > millis())  timer = millis();
      elapsedTime = millis() - timer;
      if (elapsedTime > 1999){
      report();
      timer = millis();
    }
    }
  }*/
 void SD_cardSetup(){
   while(!SD.begin(SD_card)) { //if system is attempting to write but SD card is not inserted or broken wait until working SD card inserted.
     if(!noWifi && Particle.connected()) Particle.publish("SD Card Setup","SD Card failed, or not present.");
     wStatus=digitalRead(D7);
     if(!wStatus) return; //if sd card is not working but write status changes break from loop and return to main script
     flashError();
   }
   strcpy(filename, "GPSLOG00.csv");
   SdFile::dateTimeCallback(dateTime);// Calls time stamp function for the creation of SD card files
   nameFile();
   dataFile = SD.open(filename, FILE_WRITE);
     if( ! dataFile ) {
       if(!noWifi && Particle.connected()) Particle.publish("Couldnt create file:", filename);
       flashError();
     }
     // Places the Headers in the file
     dataFile.print( "Iteration, Cycle Time, Time, Date, Satellites, Fix, Quality, Latitude Degrees, Longitude Degrees, Speed, Delta Speed , Angle, Altitude, Heading, Pitch, Roll, Lat-Accel, Long-Accel, Vert-Accel, X-Accel, Y-Accel, Z-Accel,AV Heading, AV Pitch, AV Roll, Parse Error, Collection Time");
   SdFile::dateTimeCallbackCancel();//Cancels the time stamp function for SD card files so that the time stamp shows creation time and not last modified time.
 }
 void dataWrite(String dataString){
   while(!dataFile.print("\n")) {
       while(!SD.begin(SD_card)) { // attempt to restart card
         if(!noWifi && Particle.connected()) Particle.publish("SD Card Failure","SD Card failed, or not present.");
         flashError();
     }
   }
   dataFile.print(dataString);
 }
 void nameFile(){
   for (uint8_t i = 0; i < 100; i++) {
     filename[6] = '0' + i/10;
     filename[7] = '0' + i%10;
     if (filename[6]==9 && filename[7]==9 && !SD.exists(filename)) flashError();
     // create if does not exist, do not open existing, write, sync after write
     if (!SD.exists(filename)) {
       if(!noWifi && Particle.connected()){
         Particle.publish("Now writing to file:",filename);
         Particle.process();
       }
       break;
     }
   }
   iteration = 0; f=0;
 }

void gpsParse(){
  if (!usingInterrupt) { // in case you are not using the interrupt above, you'll need to 'hand query' the GPS, not suggested :(
  char c = GPS.read(); // read data from the GPS in the 'main loop'
   if (GPSECHO)
     if (c) Serial.print(c);
   }
  if (GPS.newNMEAreceived()) { // if a sentence is received, we can check the checksum, parse it...
   if (!GPS.parse(GPS.lastNMEA()))   {
     if (millis() - gpsTimer > 4999) {
       if(!noWifi && Particle.connected()){
         gpsTimer=millis();
         Particle.publish("GPS", "{ error: \"failed to parse\"}", 60, PRIVATE );
         Particle.process();
       }
      }
      gpsTimer=millis();
      parseError++;
      return;// we can fail to parse a sentence in which case we should just wait for another
    }
  }
}

String gpsRead(){
   float floatBuffer; String gpsString;
   //GPS CODE-----------------------------------------------------------------------------------
   int year=GPS.year, month=GPS.month, day=GPS.day, hour=GPS.hour;
   uint endMonth [12]={31,28,31,30,31,30,31,31,30,31,30,31};
   if((year%4)==0)endMonth[2]=29;
   hour-=timeZone;
   if (hour < 0){
     hour = (hour + 24); // Corrects time and day when day changes for GST but not for location
     if((day-1)<1){
       if((month-1)<1){
         month=12;
         year-=1;
       }
       day=endMonth[month];
     }
   }
   gpsString = String(hour); gpsString += ":";
   gpsString += String(GPS.minute); gpsString += ":";
   gpsString += String(GPS.seconds); gpsString +=",";
   gpsString += String(month); gpsString += "/";
   gpsString+= String(day); gpsString += "/";
   gpsString += String(year); gpsString +=",";
   gpsString+=String(GPS.satellites); gpsString +=",";
   gpsString+=gpsFix; gpsString +=",";
   gpsString+=String(GPS.fixquality); gpsString +=",";
   gpsString +=String(GPS.latitudeDegrees)+",";
   gpsString +=String(GPS.longitudeDegrees)+",";
   floatBuffer = ((GPS.speed) * 1.1508); if (floatBuffer < 1) floatBuffer = 0; // Speed is given in knots and converted to MPH
   gpsString+=String(floatBuffer); gpsString +=",";
   int deltaSpeed = (floatBuffer - lastSpeed); lastSpeed = floatBuffer; //requires lastSpeed to be a global variable which took up to much space on Arduino. Will re-implement if enough RAM.
   gpsString+=String(deltaSpeed); gpsString +=",";
   gpsString+=String(GPS.angle); gpsString +=",";
   gpsString+=String(GPS.altitude); gpsString +=",";
 return gpsString;
}

String orientation(){
  String dataString;
  //Saves absolute orientation to the dataString
  sensors_event_t event; bno.getEvent(&event);
  dataString=String(event.orientation.x); dataString+=","; // Heading
  dataString+=String(event.orientation.y); dataString+=","; //Pitch
  dataString+=String(event.orientation.z); dataString+=","; //Roll
  //dataString+=String(bno.getTemp()); //dataString+=","; //IMU temp sensor. Not accurate for ambient temps/
  //Saves linear acceleration to the dataString
  imu::Vector<3> lAccel = bno.getVector(Adafruit_BNO055::VECTOR_LINEARACCEL);
  dataString+=String(lAccel.x()); dataString+=",";
  dataString+=String(lAccel.y()); dataString+=",";
  dataString+=String(lAccel.z()); dataString+=",";
  //Saves angular acceleration to the dataString
  imu::Vector<3> Accel = bno.getVector(Adafruit_BNO055::VECTOR_ACCELEROMETER);
  dataString+=String(Accel.x()); dataString+=",";
  dataString+=String(Accel.y()); dataString+=",";
  dataString+=String(Accel.z()); dataString+=",";
  //Saves angular acceleration to the dataString
  imu::Vector<3> vAngle = bno.getVector(Adafruit_BNO055::VECTOR_GYROSCOPE);
  dataString+=String(vAngle.x()); dataString+=",";
  dataString+=String(vAngle.y()); dataString+=",";
  dataString+=String(vAngle.z()); dataString+=",";
  return dataString;
}
void setCal(){
  // Calibration sketch must be run for each individual sensor prior to upload and then calibration values copied into this sketch.
  delay(25);
  uint8_t* calibData;
  calibData[0] = 245;
  calibData[1] = 255;
  calibData[2] = 229;
  calibData[3] = 255;
  calibData[4] = 255;
  calibData[5] = 255;

  calibData[6] = 253;
  calibData[7] = 255;
  calibData[8] = 255;
  calibData[9] = 255;
  calibData[10] = 0;
  calibData[11] = 0;

  calibData[12] = 2;
  calibData[13] = 255;
  calibData[14] = 5;
  calibData[15] = 1;
  calibData[16] = 246;
  calibData[17] = 0;

  calibData[18] = 232;
  calibData[19] = 3;

  calibData[20] = 68;
  calibData[21] = 3;
  bno.setSensorOffsets(calibData);
  delay(25);
}
void button_handler(system_event_t event, int duration, void* )
{
      //This function listens for the setup button being pressed and counts the time it is pressed.
      //In the loop there are two if statements that use this information
      //This is based off the built in system function from the Particle and was developed from code in the
      //system documents.
}
void proxHome(){
  /*
  .1°=27,551.8 ft
  .01°=2,755.18 ft
  .001°=275.518 ft
  .0001°=27.5518 ft
  .00001°=2.75518 ft
  */
  double homeLat=-------; double homeLon=---------;
  double latHigh=homeLat+.0005 double latLow=homeLat-.0005; double lonHigh=homeLon+.0005; double lonLow=homeLon-.0005;
  double lat=GPS.latitudeDegrees; double lon=GPS.longitudeDegrees;
  if(lat<=latHigh && lat>=latLow){ //.001°=275.518 ft
    if(lon<=lonHigh && lon>=lonLow && atHome!=1){
      atHome=1;
      digitalWrite(D4,HIGH);
    }
    else if(lon>=lonHigh || lon<=lonLow){
      atHome=0;
      digitalWrite(D4,LOW);
    }
  }
  else{
  atHome=0;
  digitalWrite(D4,LOW);
  }
}
void report(){
  if(!noWifi && Particle.connected()){
    String message= "Write:"+String(wStatus);
    message+=", Fix:"+String(gpsFix);
    message+=", Sat:"+String(GPS.satellites);
    message+=", PE:"+String(parseError);
    Particle.publish("Status Update",message); //Publish a status update
    Particle.process();//Added so that the processor doesn't get stuck trying to publish and continues loop
    if(atHome==1){
      Particle.publish("Home3121","HIGH",60,PUBLIC);
      Particle.process();
    }
    else{
      Particle.publish("Home3121","LOW",60,PUBLIC);
      Particle.process();
    }
  }
}
/* Below function is a a testing function that was used for troubleshooting communication between
the particles when communicating proximity. This allowed the relay on the reciever unit to be activated
without having to leave my house and instead just use a switch.*/
/*void report(){
  if(!noWifi && Particle.connected()){
    String message= "Write:"+String(wStatus);
    message+=", Fix:"+String(gpsFix);
    message+=", Sat:"+String(GPS.satellites);
    message+=", PE:"+String(parseError);
    Particle.publish("Status Update",message); //Publish a status update
    Particle.process();//Added so that the processor doesn't get stuck trying to publish and continues loop
    if(wStatus==1){
      Particle.publish("Home3121","HIGH",60,PUBLIC);
      Particle.process();
    }
    else{
      Particle.publish("Home3121","LOW",60,PUBLIC);
      Particle.process();
    }
  }
}*/
void wifiConnect(){
  if(!noWifi){
    if(!Particle.connected()){
      WiFi.on(); //turn on wifi if it was turned off
      WiFi.setCredentials("-------","-------"); //saved wifi credentials insert your known wifi networks here
      WiFi.setCredentials("------","-------"); //saved wifi credentials insert your known wifi networks here
      WiFi.setCredentials("---------"); //saved wifi credentials insert your known wifi networks here
      WiFi.connect(); //will attempt to connect to one of the saved wifi networks
      while(WiFi.connecting()){
        if (timer > millis())  timer = millis();
        int elapsedTime = millis() - timer;
        if (elapsedTime > 14999) {
          timer = millis(); // reset the timer
          WiFi.off(); //if it takes more than 15 second to connect to WiFi turn off the module
          noWifi=TRUE;
          period=9; //Save data at 100Hz since wifi is off
          break; //break from while loop
        }
      }
      if(WiFi.ready()) Particle.connect(); //will attempt to connect to the cloud following wifi connection
      if(!Particle.connected()){ //check to see if the particle was able to connect if not wait a set period of time
        WiFi.off(); //turn off wifi to save power until next connect attempt is made
        noWifi=TRUE; //temporarily set to true so that it won't try to connect to wifi until told to try again
        period=9; //Save data at 100Hz since wifi is off
      }
      else period=49; //Save data at 20Hz since wifi is on, otherwise it will corrupt the GPS data
    }
  }
  else{
    WiFi.off();
    noWifi=TRUE;
    period=9; //Save data at 100Hz since wifi is off
  }
  lastIteration=(fileTime*60000)/(period+1);//this calculates how many iterations should be present in the file
  //based on the period between iterations and the length of time you want a file to last.
}

void flashError(){
  digitalWrite(D4,HIGH);
  delay(500);
  digitalWrite(D4,LOW);
  delay(250);
  digitalWrite(D4,HIGH);
  delay(500);
  digitalWrite(D4,LOW);
  delay(250);
}
void dateTime(uint16_t* date, uint16_t* time) {
  // Need to create corrections for time and date at end of months and year.
  uint endMonth [12]={31,28,31,30,31,30,31,31,30,31,30,31};
  int year=GPS.year, month=GPS.month, day =GPS.day, hour=GPS.hour, minute=GPS.minute, second=GPS.seconds;
  if((year%4)==0)endMonth[1]=29;
  hour-=timeZone;
  if (hour < 0){
    hour = (hour + 24); // Corrects time and day when day changes for GST but not for location
    if((day-1)<1){
      if((month-1)<1){
        month=12;
        year-=1;
      }
      day=endMonth[month];
    }
  }
  year-=48;//literally no idea why I have to make this correction here but not when writing to the file, but otherwise it kept telling me it was 2065.
  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(year, month, day);

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(hour, minute, second);
}

Proximity Receiver Code

Arduino
Flash this to the receiving device that actuates the relays and controls the load. (With adjustments as necessary)
int indicatorPin = D7; //using D7 to show if variable is high/low
int relayPin = D6; //this should be self explanatory
uint32_t Timer = millis();

void setup() {
    pinMode(indicatorPin, OUTPUT);   // set D7 as an output pin (writing high to activate relay coil)
    pinMode(relayPin, OUTPUT);
    digitalWrite(indicatorPin, LOW); // set low initially. will go high later
    digitalWrite(relayPin, HIGH);
    Particle.subscribe("Home", myHandler);
    
}

void myHandler(const char *event, const char *data)
{
    if (strcmp(data,"HIGH")==0)
    {
        digitalWrite(indicatorPin, HIGH);
        digitalWrite(relayPin, LOW);
        Timer = millis();
    }
    if (strcmp(data,"LOW")==0)
    {
        digitalWrite(indicatorPin, LOW);
        digitalWrite(relayPin, HIGH);
    }
}

void loop() {
    if (Timer > millis()) Timer = millis();
    long long elapsedTime = millis() - Timer;
    
    if (elapsedTime > 4999){ 
        digitalWrite(indicatorPin, LOW);
        digitalWrite(relayPin, HIGH);
    }
    
}

Excel Telemetry Analysis Template

VB.NET
NOTE: This is labeled as visual basic because it had to have a language selected and excel wasn't resent, but this is just an excel template.Since the MotoidIoT saves it's data as a .CSV you can choose to analyze your data however you choose. However, if you have access to Excel than I have a pre-template that you can use and improve. This will allow you to see some statistics about your ride, focus on a section of your ride, see graphs of all your statistics, and see a heat map overlay of your ride in regards to your statistics. In order to use create a new document using the template. Excel will ask you to refresh the data. Choose the .CSV file you wish to analyze and it will upload it to the template. In order to view the Heat Map overlay go to Tab GPSLOG, Insert, and 3D Map. There should already be a pre-made tour with Heat Map settings on it so you don't have to do a thing. Feel free to edit and improve.
No preview (download only).

Adafruit Ultimate GPS Library

Adafruit Ultimate GPS breakout board library for Particle Photon

Adafruit BNO-055

Adafruit BNO-055 breakout board library for Arduino. Worked for Particle without any changes though as firmware updates occur this may change.

SD card Library

Library for using SD card with Particle Photon

Adafruit Sensor CPP

Used with Adafruit Sensor sets.

Credits

John Sansbury

John Sansbury

1 project • 0 followers
Jerod Zimmerman

Jerod Zimmerman

1 project • 0 followers

Comments