DIY IoT Remote Control to map environment and civic issues on your street

IoT DataLogger

Category: Local Data/Mapping
Components: GPS
Created By: Anonymous
Difficulty Level: Intermediate
Input Sensor: button
Microcontroller: NodeMCU
Output Device: Google Sheets
Prototyped by: @gautamp
Status: In Progress

Discover:

Mapping local issues is time consuming. If using a chatbot, you need to type keyword, select category, share location and then submit. If using a mapping tool, you need to login, and then locate yourself and fill in the same details. What if you could map issues with the press of a button (assumption - your device is connected to a wifi hotspot)

Investigation

Using the chatbot, we tried mapping, and this took about 1 minute, but users felt that it took 3 minutes to do the same. This button pressing action felt easier. Product Demo is below.

https://youtu.be/5g8Ln7TixCE

Solution (V1)

Component Connected to pin on NodeMCU
GPS VCC 3V3
GPS GND GND
GPS RX D4
GPS TX D3
Button leg 1 3V3
Button leg 2 Via resistor (220 ohm) shorting ground and D1
#include <TinyGPS++.h>
#include <SoftwareSerial.h>

#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino 
//needed for library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
#include <WiFiClientSecure.h>

/*
   This sample sketch demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object.
   It requires the use of SoftwareSerial, and assumes that you have a
   4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx). Thank you Mikal for the library.
*/
static const int RXPin = 0, TXPin = 2; //change if you use different pins
static const uint32_t GPSBaud = 9600;

//Button
int button = 5;

//Device ID
String device = "gp001"; //Change this
String tim  = "";

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

//Google Server Setup Section
const char* host = "script.google.com";

const int httpsPort = 443;
WiFiClientSecure client;
const char* fingerprint = "46 B2 C3 44 9C 59 09 8B 01 B6 F8 BD 4C FB 00 74 91 2F EF F6";
String GAS_ID = "AKfycbxtSVvRVh16mXK44mzzjaKJrWrNEdN-eOEmgaibG3ETyvAn_rP6-uaNgUU2mqZXgrsG";  // Replace by your Google App S service id

//Set up to send to GSheets
String readString;

void setup()
{
  Serial.begin(115200);
  ss.begin(GPSBaud);

  pinMode(button, INPUT);

  WiFiManager wifiManager;
  wifiManager.setTimeout(180);

  if (!wifiManager.autoConnect("SetupWIFIDataLogger")) {
    Serial.println("failed to connect and hit timeout");
    delay(3000);
    //reset and try again, or maybe put it to deep sleep
    ESP.reset();
    delay(5000);
  }
  //if you get here you have connected to the WiFi
  Serial.println("connected...yeey :)");
  delay(3000);
}

void loop()
{
  // This sketch displays information every time a new sentence is correctly encoded.
  while (ss.available() > 0)
    if (gps.encode(ss.read())) {
      //      displayInfo();
      int buttonpress = digitalRead (button);
      if (buttonpress != 0) {
        Serial.println("button pressed");
        displayInfo();
      }
    }

  if (millis() > 5000 && gps.charsProcessed() < 10)
  {
    Serial.println(F("No GPS detected: check wiring."));
    while (true);
  }
}

void displayInfo()
{
  Serial.print(F("Location: "));
  if (gps.location.isValid())
  {
    Serial.print(gps.location.lat(), 6);
    Serial.print(F(","));
    Serial.print(gps.location.lng(), 6);
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.print(F("  Date/Time: "));
  if (gps.date.isValid())
  {
    Serial.print(gps.date.month());
    Serial.print(F("/"));
    Serial.print(gps.date.day());
    Serial.print(F("/"));
    Serial.print(gps.date.year());
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.print(F(" "));
  if (gps.time.isValid())
  {
    if (gps.time.hour() < 10) Serial.print(F("0"));
    Serial.print(gps.time.hour());
    Serial.print(F(":"));
    if (gps.time.minute() < 10) Serial.print(F("0"));
    Serial.print(gps.time.minute());
    Serial.print(F(":"));
    if (gps.time.second() < 10) Serial.print(F("0"));
    Serial.print(gps.time.second());
    Serial.print(F("."));
    if (gps.time.centisecond() < 10) Serial.print(F("0"));
    Serial.print(gps.time.centisecond());

    tim = "test";
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.println();
  senddata();
}

void senddata() {
  client.setInsecure();
  Serial.print("connecting to ");
  Serial.println(host);
  if (!client.connect(host, httpsPort)) {
    Serial.println("connection failed");
    return;
  }
  if (client.verify(fingerprint, host)) {
    Serial.println("certificate matches");
  } else {
    Serial.println("certificate doesn't match");
  }
  String url = "/macros/s/" + GAS_ID + "/exec?device=" + device + "&lati=" + String(gps.location.lat(), 6) + "&longi=" + String(gps.location.lng(), 6) + "&time=" + tim;
  Serial.print("requesting URL: ");
  Serial.println(url);
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "User-Agent: BuildFailureDetectorESP8266\r\n" +
               "Connection: close\r\n\r\n");
  Serial.println("request sent");
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    if (line == "\r") {
      Serial.println("headers received");
      break;
    }
  }
  String line = client.readStringUntil('\n');
  if (line.startsWith("{\"state\":\"success\"")) {
    Serial.println("esp8266/Arduino CI successfull!");
  } else {
    Serial.println("esp8266/Arduino CI has failed");
  }
  Serial.println("reply was:");
  Serial.println("==========");
  Serial.println(line);
  Serial.println("==========");
  Serial.println("closing connection");
}

Server Side setup (if you want to keep your data with you only, and not share with commons)

  • Create a google sheet and make it open to anyone to read
  • Create App, and paste this code in it -
    • name script same name as google sheet
    • copy google sheet ID and paste in “sheet_id”
    • deploy, using your email ID, but usable by all and copy Google App Script ID and paste in Arduino Code under “GAS_ID”
function doGet(e) { 
  Logger.log( JSON.stringify(e) );  // view parameters
  var result = 'Ok'; // assume success
  if (e.parameter == 'undefined') {
    result = 'No Parameters';
  }
  else {
    var sheet_id = '1Qjt4O5MYtNIwlHFQ6k29tWzjXiUHS98LWcbdNwbuxzU'; 		// Spreadsheet ID
    var sheet = SpreadsheetApp.openById(sheet_id).getActiveSheet();		// get Active sheet
    var newRow = sheet.getLastRow() + 1;						
    var rowData = [];
    rowData[0] = new Date(); 											// Timestamp in column A
    for (var param in e.parameter) {
      Logger.log('In for loop, param=' + param);
      var value = stripQuotes(e.parameter[param]);
      Logger.log(param + ':' + e.parameter[param]);
      switch (param) {
        case 'device': //Parameter
          rowData[1] = value; //Value in column B
          result = 'Written on column B';
          break;
        case 'lati': //Parameter
          rowData[2] = value; //Value in column C
          result += ' ,Written on column C';
          break;
        case 'longi': //Parameter
          rowData[3] = value; //Value in column C
          result += ' ,Written on column D';
          break;
        case 'time': //Parameter
          rowData[4] = value; //Value in column C
          result += ' ,Written on column E';
          break;  
        default:
          result = "unsupported parameter";
      }
    }
    Logger.log(JSON.stringify(rowData));
    // Write new row below
    var newRange = sheet.getRange(newRow, 1, 1, rowData.length);
    newRange.setValues([rowData]);
  }
  // Return result of operation
  return ContentService.createTextOutput(result);
}
/**
* Remove leading and trailing single or double quotes
*/
function stripQuotes( value ) {
  return value.replace(/^["']|['"]$/g, "");
}

Dashboard (embedded here)

https://datastudio.google.com/reporting/87818c3c-5ed4-424d-b4af-1fa5ebb18bab

RoadMap

Version 2 - multibutton

IMG_8010.JPG

Component Connected to pin on NodeMCU
GPS VCC 3V3
GPS GND GND
GPS RX D4
GPS TX D3
Button1 leg 1 3V3
Button1 leg 2 Via resistor (220 ohm) shorting ground and D1
Button2 leg 2 Via resistor (220 ohm) shorting ground and D5
#include <TinyGPS++.h>
#include <SoftwareSerial.h>

#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino 
//needed for library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
#include <WiFiClientSecure.h>

/*
   This sample sketch demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object.
   It requires the use of SoftwareSerial, and assumes that you have a
   4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
*/
static const int RXPin = 0, TXPin = 2;
static const uint32_t GPSBaud = 9600;

//Button
int button = 5;
int button2 = 14;
int buttonpress, buttonpress2 = 0;

//Device ID
String device = "gp001";
String tim  = "";
String url = "";

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

//Google Server Setup Section
const char* host = "script.google.com";

const int httpsPort = 443;
WiFiClientSecure client;
const char* fingerprint = "46 B2 C3 44 9C 59 09 8B 01 B6 F8 BD 4C FB 00 74 91 2F EF F6";
String GAS_ID = "AKfycbxtSVvRVh16mXK44mzzjaKJrWrNEdN-eOEmgaibG3ETyvAn_rP6-uaNgUU2mqZXgrsG";  // Replace by your Google App S service id

//Set up to send to GSheets
String readString;

void setup()
{
  Serial.begin(115200);
  ss.begin(GPSBaud);

  pinMode(button, INPUT);
  pinMode(button2, INPUT);

  WiFiManager wifiManager;
  wifiManager.setTimeout(180);

  if (!wifiManager.autoConnect("SetupWIFIDataLogger")) {
    Serial.println("failed to connect and hit timeout");
    delay(3000);
    //reset and try again, or maybe put it to deep sleep
    ESP.reset();
    delay(5000);
  }
  //if you get here you have connected to the WiFi
  Serial.println("connected...yeey :)");
  delay(3000);
}

void loop()
{
  // This sketch displays information every time a new sentence is correctly encoded.
  while (ss.available() > 0)
    if (gps.encode(ss.read())) {
      //      displayInfo();
      buttonpress = digitalRead (button);
      buttonpress2 = digitalRead (button2);
      if (buttonpress != 0) {
        Serial.println("button pressed");
        //String url = "/macros/s/" + GAS_ID + "/exec?device=" + "pothole" + "&lati=" + String(gps.location.lat(), 6) + "&longi=" + String(gps.location.lng(), 6) + "&time=" + tim;
        displayInfo();
      }

      if (buttonpress2 != 0) {
        Serial.println("button2 pressed");
        //String url = "/macros/s/" + GAS_ID + "/exec?device=" + "streetlight" + "&lati=" + String(gps.location.lat(), 6) + "&longi=" + String(gps.location.lng(), 6) + "&time=" + tim;
        displayInfo();
      }
    }

  if (millis() > 5000 && gps.charsProcessed() < 10)
  {
    Serial.println(F("No GPS detected: check wiring."));
    while (true);
  }
}

void displayInfo()
{
  Serial.print(F("Location: "));
  if (gps.location.isValid())
  {
    Serial.print(gps.location.lat(), 6);
    Serial.print(F(","));
    Serial.print(gps.location.lng(), 6);
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.print(F("  Date/Time: "));
  if (gps.date.isValid())
  {
    Serial.print(gps.date.month());
    Serial.print(F("/"));
    Serial.print(gps.date.day());
    Serial.print(F("/"));
    Serial.print(gps.date.year());
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.print(F(" "));
  if (gps.time.isValid())
  {
    if (gps.time.hour() < 10) Serial.print(F("0"));
    Serial.print(gps.time.hour());
    Serial.print(F(":"));
    if (gps.time.minute() < 10) Serial.print(F("0"));
    Serial.print(gps.time.minute());
    Serial.print(F(":"));
    if (gps.time.second() < 10) Serial.print(F("0"));
    Serial.print(gps.time.second());
    Serial.print(F("."));
    if (gps.time.centisecond() < 10) Serial.print(F("0"));
    Serial.print(gps.time.centisecond());
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.println();
  senddata(url);
}

void senddata(String url) {
  client.setInsecure();
  Serial.print("connecting to ");
  Serial.println(host);
  if (!client.connect(host, httpsPort)) {
    Serial.println("connection failed");
    return;
  }
  if (client.verify(fingerprint, host)) {
    Serial.println("certificate matches");
  } else {
    Serial.println("certificate doesn't match");
  }

  if (buttonpress != 0) {
    url = "/macros/s/" + GAS_ID + "/exec?device=" + "pothole" + "&lati=" + String(gps.location.lat(), 6) + "&longi=" + String(gps.location.lng(), 6) + "&time=" + tim;
  }

  else if (buttonpress2 != 0) {
    url = "/macros/s/" + GAS_ID + "/exec?device=" + "streetlight" + "&lati=" + String(gps.location.lat(), 6) + "&longi=" + String(gps.location.lng(), 6) + "&time=" + tim;
  }

  //String url = "/macros/s/" + GAS_ID + "/exec?device=" + device + "&lati=" + String(gps.location.lat(), 6) + "&longi=" + String(gps.location.lng(), 6) + "&time=" + tim;
  Serial.print("requesting URL: ");
  Serial.println(url);
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "User-Agent: BuildFailureDetectorESP8266\r\n" +
               "Connection: close\r\n\r\n");
  Serial.println("request sent");
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    if (line == "\r") {
      Serial.println("headers received");
      break;
    }
  }
  String line = client.readStringUntil('\n');
  if (line.startsWith("{\"state\":\"success\"")) {
    Serial.println("esp8266/Arduino CI successfull!");
  } else {
    Serial.println("esp8266/Arduino CI has failed");
  }
  Serial.println("reply was:");
  Serial.println("==========");
  Serial.println(line);
  Serial.println("==========");
  Serial.println("closing connection");
}
1 Like

Version 3 of the code

#include <TinyGPS++.h>
#include <SoftwareSerial.h>

#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino 
//needed for library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
#include <WiFiClientSecure.h>

/*
   This sample sketch demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object.
   It requires the use of SoftwareSerial, and assumes that you have a
   4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
*/
static const int RXPin = 0, TXPin = 2;
static const uint32_t GPSBaud = 9600;

//Button
int button = 5;
int button2 = 14;
int buttonpress, buttonpress2 = 0;

//Device ID
String device = "gp001";
String tim  = "";
String url = "";

// The TinyGPS++ object
TinyGPSPlus gps;

// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

//Google Server Setup Section
const char* host = "script.google.com";

const int httpsPort = 443;
WiFiClientSecure client;
const char* fingerprint = "46 B2 C3 44 9C 59 09 8B 01 B6 F8 BD 4C FB 00 74 91 2F EF F6";
String GAS_ID = "AKfycbxtSVvRVh16mXK44mzzjaKJrWrNEdN-eOEmgaibG3ETyvAn_rP6-uaNgUU2mqZXgrsG";  // Replace by your Google App S service id

//Set up to send to GSheets
String readString;

void setup()
{
  Serial.begin(115200);
  ss.begin(GPSBaud);

  pinMode(button, INPUT);
  pinMode(button2, INPUT);

  WiFiManager wifiManager;
  wifiManager.setTimeout(180);

  if (!wifiManager.autoConnect("SetupDatalogger001")) {
    Serial.println("failed to connect and hit timeout");
    delay(3000);
    //reset and try again, or maybe put it to deep sleep
    ESP.reset();
    delay(5000);
  }
  //if you get here you have connected to the WiFi
  Serial.println("connected...yeey :)");
  delay(3000);
}

void loop()
{
  // This sketch displays information every time a new sentence is correctly encoded.
  while (ss.available() > 0)
    if (gps.encode(ss.read())) {
      //      displayInfo();
      buttonpress = digitalRead (button);
      buttonpress2 = digitalRead (button2);
      if (buttonpress != 0) {
        Serial.println("button pressed");
        //String url = "/macros/s/" + GAS_ID + "/exec?device=" + "pothole" + "&lati=" + String(gps.location.lat(), 6) + "&longi=" + String(gps.location.lng(), 6) + "&time=" + tim;
        displayInfo();
      }

      if (buttonpress2 != 0) {
        Serial.println("button2 pressed");
        //String url = "/macros/s/" + GAS_ID + "/exec?device=" + "streetlight" + "&lati=" + String(gps.location.lat(), 6) + "&longi=" + String(gps.location.lng(), 6) + "&time=" + tim;
        displayInfo();
      }
    }

  if (millis() > 5000 && gps.charsProcessed() < 10)
  {
    Serial.println(F("No GPS detected: check wiring."));
    while (true);
  }
}

void displayInfo()
{
  Serial.print(F("Location: "));
  if (gps.location.isValid())
  {
    Serial.print(gps.location.lat(), 6);
    Serial.print(F(","));
    Serial.print(gps.location.lng(), 6);
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.print(F("  Date/Time: "));
  if (gps.date.isValid())
  {
    Serial.print(gps.date.month());
    Serial.print(F("/"));
    Serial.print(gps.date.day());
    Serial.print(F("/"));
    Serial.print(gps.date.year());
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.print(F(" "));
  if (gps.time.isValid())
  {
    if (gps.time.hour() < 10) Serial.print(F("0"));
    Serial.print(gps.time.hour());
    Serial.print(F(":"));
    if (gps.time.minute() < 10) Serial.print(F("0"));
    Serial.print(gps.time.minute());
    Serial.print(F(":"));
    if (gps.time.second() < 10) Serial.print(F("0"));
    Serial.print(gps.time.second());
    Serial.print(F("."));
    if (gps.time.centisecond() < 10) Serial.print(F("0"));
    Serial.print(gps.time.centisecond());
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.println();
  senddata(url);
}

void senddata(String url) {
  client.setInsecure();
  Serial.print("connecting to ");
  Serial.println(host);
  if (!client.connect(host, httpsPort)) {
    Serial.println("connection failed");
    return;
  }
  if (client.verify(fingerprint, host)) {
    Serial.println("certificate matches");
  } else {
    Serial.println("certificate doesn't match");
  }

  if (buttonpress != 0) {
    url = "/macros/s/" + GAS_ID + "/exec?device=" + "pothole" + "&lati=" + String(gps.location.lat(), 6) + "&longi=" + String(gps.location.lng(), 6) + "&time=" + device;
  }

  else if (buttonpress2 != 0) {
    url = "/macros/s/" + GAS_ID + "/exec?device=" + "garbage" + "&lati=" + String(gps.location.lat(), 6) + "&longi=" + String(gps.location.lng(), 6) + "&time=" + device;
  }

  //String url = "/macros/s/" + GAS_ID + "/exec?device=" + device + "&lati=" + String(gps.location.lat(), 6) + "&longi=" + String(gps.location.lng(), 6) + "&time=" + tim;
  Serial.print("requesting URL: ");
  Serial.println(url);
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "User-Agent: BuildFailureDetectorESP8266\r\n" +
               "Connection: close\r\n\r\n");
  Serial.println("request sent");
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    if (line == "\r") {
      Serial.println("headers received");
      break;
    }
  }
  String line = client.readStringUntil('\n');
  if (line.startsWith("{\"state\":\"success\"")) {
    Serial.println("esp8266/Arduino CI successfull!");
  } else {
    Serial.println("esp8266/Arduino CI has failed");
  }
  Serial.println("reply was:");
  Serial.println("==========");
  Serial.println(line);
  Serial.println("==========");
  Serial.println("closing connection");
}
1 Like

Server Side setup (if you want to keep your data with you only, and not share it with the commons)

  • Create a google sheet and make it open to anyone to read
  • Go to Extensions>Open Apps Script, and paste the below code in it -
    • name the script the same as the name of your google sheet
    • copy google sheet ID (the URL in the address bar between /d/ and /edit) and paste it in “sheet_id”
    • Click on “Deploy”, use your email ID, choose “Web app” but usable by all and copy Deployment ID and paste in Arduino Code under “GAS_ID”
1 Like

If the button is not working, use a higher resistance resistor above or equal to 10k

1 Like