Brian Rashap
Published © GPL3+

Automated Pothole Detection

An accelerometer measure bumps/potholes. These are geo-tagged with a GPS-unit and saved to an uSD card. Visualization via Google Maps.

IntermediateFull instructions provided4 hours2,500
Automated Pothole Detection

Things used in this project

Hardware components

Teensy USB 3.2 Development Board
Teensy USB 3.2 Development Board
×1
Adafruit Micro-SD Breakout Board
×1
NEO 6M GPS Module
×1
GY-61 3-Axis Accelerometer
×1
0.96 OLED Display (128x64)
×1
12x12x7.3 Tactile Button - Green
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

PotHole Detector Breadboard Layout

Schematic

Code

pothole.ino

C/C++
/*
 * Project: PotHole
 * Description: Use GY-21 to detect pot holes, tag with GSP, and save to SD Card
 * Author: Brian Rashap
 * Date: 03-May-2020
 */

#include <TinyGPS.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Time.h>
#include <SPI.h>
#include <SdFat.h>

// Configure microSD
const int chipSelect = 10;
#define FILE_BASE_NAME "Data"
SdFat sd;
SdFile file;
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
char fileName[13] = FILE_BASE_NAME "00.csv";

// Configure OLED Display
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Configure GPS
TinyGPS gps;
#define Uart Serial1
void gpsdump(TinyGPS &gps);
const int UTC_offset = -6;   // Mountain Daylight Time
void printFloat(double f, int digits = 2);

// Declare Variables
int startPin=23;
int zPin=16;
int ledPin=3;
float zAcc;
float cal;
int i;
bool logStart;

void setup() {

  Serial.begin(9600);
  Uart.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

  pinMode(startPin,INPUT);
  pinMode(zPin,INPUT);
  pinMode(ledPin,OUTPUT);

  display.setTextSize(1);             
  display.setTextColor(SSD1306_WHITE);       
  display.clearDisplay();

  // Calibrate Accelerometer
  for(i=0;i<20;i++) {
    cal += analogRead(zPin);
    delay(250);
  }
  cal = cal / 20.0;
  display.setCursor(0,0);
  display.printf("Calibration = %0.2f",cal);

  // Initialize microSD
  if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
    sd.initErrorHalt();  
  }
  if (BASE_NAME_SIZE > 6) {
    Serial.println("FILE_BASE_NAME too long");
    while(1);
  }
  

  // Get inital GPS
  display.setCursor(0,16);             // Start at top-left corner
  display.println("GPS Acquiring"); 
  display.display();
  getGPS();

  logStart = false;
  Serial.println("Push button to begin");
 
}

void loop() {
  logStart=digitalRead(startPin);
  if (logStart==true) {
    Serial.printf("Starting Data Logging \n");
    digitalWrite(ledPin,HIGH);
    while (sd.exists(fileName)) {
      if (fileName[BASE_NAME_SIZE + 1] != '9') {
        fileName[BASE_NAME_SIZE + 1]++;
      } else if (fileName[BASE_NAME_SIZE] != '9') {
        fileName[BASE_NAME_SIZE + 1] = '0';
        fileName[BASE_NAME_SIZE]++;
      } else {
        Serial.println("Can't create file name");
        while(1);
      }
    }
    if (!file.open(fileName, O_WRONLY | O_CREAT | O_EXCL)) {
      Serial.println("file.open");
      file.printf("TimeStamp, Acceleration,Latitude-Longitude \n");
    }
    Serial.printf("Logging to: %s \n",fileName);
    delay(1000); //give chance to release button
  }
  while(logStart==true) {
    zAcc = analogRead(zPin)/cal;
    if (zAcc > 1.1) { 
      Serial.printf("Z Acceleration = %0.2f \n",zAcc); 
      getGPS();
    }
    if (!file.sync() || file.getWriteError()) {
    Serial.printf("write error");
    }
    logStart =  !digitalRead(startPin);
    if (logStart==false) {
      file.close();
      Serial.printf("Done \n");
      digitalWrite(ledPin,LOW);
      delay(2000);
      Serial.printf("Ready for next data log \n");
    }
  }



  
 
}


void getGPS() {
  bool newdata = false;
  unsigned long start = millis();
  Serial.println("Begin Acquire");

  // Every 5 seconds we print an update
  while (millis() - start < 5000) {
    if (Uart.available()) {
      char c = Uart.read();
      Serial.print(c);  // uncomment to see raw GPS data
      if (gps.encode(c)) {
        newdata = true;
        //break;  // uncomment to print new data immediately!
      }
    }
  }
  if (newdata) {
    Serial.println("Acquired Data");
    Serial.println("-------------");
    gpsdump(gps);
    Serial.println("-------------");
    Serial.println();
  }
}


void gpsdump(TinyGPS &gps)
{
  long lat, lon;
  float flat, flon;
  unsigned long tage, tdate, ttime, chars;
  int tyear;
  byte tmonth, tday, thour, tminute, tsecond, hundredths;
  unsigned short sentences, failed;

  gps.get_position(&lat, &lon, &tage);
  Serial.print("Lat/Long(10^-5 deg): "); Serial.print(lat); Serial.print(", "); Serial.print(lon); 
  Serial.print(" Fix age: "); Serial.print(tage); Serial.println("ms.");

  gps.f_get_position(&flat, &flon, &tage);
  Serial.print("Lat/Long(float): "); printFloat(flat, 7); Serial.print(", "); printFloat(flon, 7);
  Serial.print(" Fix age: "); Serial.print(tage); Serial.println("ms.");

  gps.get_datetime(&tdate, &ttime, &tage);
  Serial.print("Date(ddmmyy): "); Serial.print(tdate); Serial.print(" Time(hhmmsscc): ");
    Serial.print(ttime);
  Serial.print(" Fix age: "); Serial.print(tage); Serial.println("ms.");

  gps.crack_datetime(&tyear, &tmonth, &tday, &thour, &tminute, &tsecond, &hundredths, &tage);
  Serial.print("Date: "); Serial.print(static_cast<int>(tmonth)); Serial.print("/"); 
    Serial.print(static_cast<int>(tday)); Serial.print("/"); Serial.print(tyear);
  Serial.print("  Time: "); Serial.print(static_cast<int>(thour)); Serial.print(":"); 
    Serial.print(static_cast<int>(tminute)); Serial.print(":"); Serial.print(static_cast<int>(tsecond));
    Serial.print("."); Serial.print(static_cast<int>(hundredths));
  Serial.print("  Fix age: ");  Serial.print(tage); Serial.println("ms.");

        // Set Time from GPS data string
        setTime(thour, tminute, tsecond, tday, tmonth, tyear);
        // Calc current Time Zone time by offset value
        adjustTime(UTC_offset * SECS_PER_HOUR); 
        time_t t=now();
        Serial.print("Time: "); 
        Serial.print(hour(t));
        Serial.print(":");
        Serial.print(minute(t));
        Serial.print(":");
        if (second(t)<10) {
          Serial.print("0");
        }
        Serial.println(second(t));

  OLEDlonglat(flat,flon);
  SDlonglat(zAcc,flon,flat);

  Serial.print("Alt(cm): "); Serial.print(gps.altitude()); Serial.print(" Course(10^-2 deg): ");
    Serial.print(gps.course()); Serial.print(" Speed(10^-2 knots): "); Serial.println(gps.speed());
  Serial.print("Alt(float): "); printFloat(gps.f_altitude()); Serial.print(" Course(float): ");
    printFloat(gps.f_course()); Serial.println();
  Serial.print("Speed(knots): "); printFloat(gps.f_speed_knots()); Serial.print(" (mph): ");
    printFloat(gps.f_speed_mph());
  Serial.print(" (mps): "); printFloat(gps.f_speed_mps()); Serial.print(" (kmph): ");
    printFloat(gps.f_speed_kmph()); Serial.println();

  Serial.print("Satellites in view: ");
  Serial.println(gps.satellites(), DEC);

  gps.stats(&chars, &sentences, &failed);
  Serial.print("Stats: characters: "); Serial.print(chars); Serial.print(" sentences: ");
    Serial.print(sentences); Serial.print(" failed checksum: "); Serial.println(failed);
}


void OLEDlonglat(float longitude, float latitude) {
  display.clearDisplay();
  
  display.setCursor(0,0);             // Start at top-left corner

        time_t t=now();               // display time
        display.print("Time: "); 
        display.print(hour(t));
        display.print(":");
        display.print(minute(t));
        display.print(":");
        if (second(t)<10) {
          display.print("0");
        }
        display.println(second(t));

  display.print("Longitude:  "); display.println(longitude);
  display.print("Latitude: "); display.println(latitude);  
  display.print("Satellites: "); display.println(gps.satellites(), DEC);
  display.display();
}

void printFloat(double number, int digits)
{
  // Handle negative numbers
  if (number < 0.0) {
     Serial.print('-');
     number = -number;
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;
  
  number += rounding;

  // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
  double remainder = number - (double)int_part;
  Serial.print(int_part);

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0)
    Serial.print("."); 

  // Extract digits from the remainder one at a time
  while (digits-- > 0) {
    remainder *= 10.0;
    int toPrint = int(remainder);
    Serial.print(toPrint);
    remainder -= toPrint;
  }
}

void SDlonglat(float acc, float plong, float plat) {
  time_t t2=now();
  Serial.printf("\n \n %i:%i:%i, %0.2f, %0.6f-%0.6f \n \n",hour(t2),minute(t2),second(t2),acc,plat,plong);
  file.printf("%i:%i:%i, %0.2f, %0.6f Z %0.6f\n",hour(t2),minute(t2),second(t2),acc,plat,plong);
}

Credits

Brian Rashap

Brian Rashap

12 projects • 107 followers
Former General Manager of US Facilities Operations at Intel Corporation. Currently loving my encore career as a teacher focused on IoT.

Comments