EchoGarage Project

Today I am very excited to share with you the EchoGarage project, my first prototype ever!! I have no programming or electronics background. I recently became interested in Arduino projects and I found thinger.io that I think it has got a very intuitive approach even for a novice person like me. My goal was to make a universal cloud-based garage door opener with ultrasonic monitoring. EchoGarage is powered by the ESP8266 chip and the Arduino IDE for programming. It supports remote access through the cloud-based thinger.io platform.

A wireless garage door opener is not a new concept. In fact, there are a handful of approaches out there. Besides other aspects of the technical details, the discussion about which type of monitoring sensor might serve a garage opener better has been very limited. I think that ultrasound sensors are very handy, flexible and reliable tools for garage monitoring.

Hardware is really simple. It consists of (see fritzing diagram and breadboard photo below):

EchoGarage Dashboard Features (see screenshots above):

  • Real time display of current door state plus time interval since door was opened (text).
  • Real time display of distance over time (chart).
  • Real time display of current distance (slider bar that mimics physical door).
  • Push button for triggering garage door actions.
  • Email Alerts when door remains open for a certain time period (Endpoint).
  • Configurable alert time interval in min (default 60).
  • Configurable distance threshold in cm for defining when door is open or closed (default 40).
  • Logging of current door state plus timestamp (Data Bucket).

The first step to start building EchoGarage is to proper configure the Arduino IDE. I used the latest v1.8.1 for building this project, so I cannot guarantee that it will work with previous versions. Then, install Arduino core for ESP8266 WiFi chip using Boards Manager (v2.3.0):

  1. Start Arduino IDE and open Preferences window.
  2. Enter “http://arduino.esp8266.com/stable/package_esp8266com_index.json” into Additional Board Manager URLs field. You can add multiple URLs, separating them with commas.
  3. Open Boards Manager from Tools > Board menu and install esp8266 platform (and don’t forget to select WeMos D1 mini from Tools > Board menu after installation).

You will need to install some extra libraries into the Arduino IDE:

  • WiFiManager.h (v0.12). Install through the Arduino Library Manager or download from github.com/tzapu/WiFiManager.
  • ThingerESP8266.h (v2.5.9). Install through the Arduino Library Manager or download from github.com/thinger-io/Arduino-Library.
  • NewPing.h (v1.8). Download from playground.arduino.cc/Code/NewPing.
  • SimpleTimer.h. Download from playground.arduino.cc/Code/SimpleTimer.

Now you are ready to upload the EchoGarage sketch to ESP8266. Just copy-paste my code below into a new Arduino sketch and click upload (don’t forget to first connect EchoGarage into your PC USB port).

#include <FS.h>                          // File system wrapper, this library is part of the esp8266 core for Arduino.
#include <ESP8266WiFi.h>                 // ESP8266 Core WiFi Library, this library is part of the esp8266 core for Arduino.
#include <DNSServer.h>                   // Local DNS Server used for redirecting all requests to the configuration portal, this library is part of the esp8266 core for Arduino.
#include <ESP8266WebServer.h>            // Local WebServer used to serve the configuration portal, this library is part of the esp8266 core for Arduino.
#include <WiFiManager.h>                 // WiFi Connection manager with fallback web configuration portal. https://github.com/tzapu/WiFiManager
#include <ThingerWebConfig.h>            // Client Library to connect your IoT devices to the thinger.io platform. https://github.com/thinger-io/Arduino-Library
#include <NewPing.h>                     // Advanced library for interfacing with HC-SR04 sensor with many features. http://playground.arduino.cc/Code/NewPing)
#include <SimpleTimer.h>                 // Simple library based on millis() to launch timed actions. http://playground.arduino.cc/Code/SimpleTimer

SimpleTimer timer;                       // There must be one global SimpleTimer object.

ThingerWebConfig thing;                  // ThingerWebConfig object that creates an access point for initial setup and connects the ESP8266 to the cloud server.
 
#define TRIGGER_PIN  12                  // ESP8266 chip pin tied to trigger pin on ping sensor. It corresponds to pin D6 on WeMos D1 mini board.
#define ECHO_PIN     14                  // ESP8266 chip pin tied to echo pin on ping sensor. It corresponds to pin D5 on WeMos D1 mini board.
#define MAX_DISTANCE 400                 // Allows setting of a maximum distance in cm where pings beyond that distance are read as no ping or clear (defauld 400).
 
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);    // NewPing object setup of pins and maximum distance.

#define RELAY_1 16                       // ESP8266 chip pin tied to IN1 pin on relay board. It corresponds to pin D0 on WeMos D1 mini board.
#define PINGSPEED 50

unsigned long previousMillis = 0;        // Holds last time when the door was opened.
unsigned long timeInterval = 0;          // Holds time interval in min since door was opened.
unsigned long timeInterval_ms = 0;       // Holds time interval in ms since door was opened.

String currentDoorState = "";            // Holds current door state (open or closed).
String previousDoorState = "";           // Holds previous door state (open or closed).
unsigned int distance = 0;               // Holds ultrasonic ping distance (in cm).
unsigned int distanceThreshold = 40;     // Configuration option in cm that used to define door state (default 40).
unsigned long echoTime = 0;              // Holds echo time in microseconds.

int alertInterval = 60;                  // Configuration option in min that used to call the EndPoint (default 60).
int alertTimerId;                        // Holds the specified Timer slot for alert() function.
int distanceTimerId;                     // Holds the specified Timer slot for distance_update() function.



/********************************    Functions   **********************************/

void distance_update(){
      echoTime = sonar.ping_median(5);              // Do multiple pings (5), discard out of range pings and return median in microseconds. 
      distance = sonar.convert_cm(echoTime);        // Converts microseconds to distance in centimeters.
}

void door_state_update(){
  if (distance < distanceThreshold && (previousDoorState == "Closed" || previousDoorState == "")){
      currentDoorState = "Open";           
      previousMillis = millis();
      thing.stream(thing["doorState"]);
      } else if (distance > distanceThreshold && (previousDoorState == "Open" || previousDoorState == "")){
      currentDoorState = "Closed";
      timeInterval_ms = 0;
      timeInterval = 0;
      thing.stream(thing["doorState"]);
      } else if (distance < distanceThreshold && previousDoorState == "Open"){
      timeInterval_ms = (millis() - previousMillis);
      timeInterval = timeInterval_ms / 60000;
      }
 previousDoorState = currentDoorState;
}

void check_endpoint_call(){
  if (currentDoorState == "Closed"){
     timer.disable(alertTimerId);     
     }
  if (currentDoorState == "Open" && timeInterval_ms == 0){
     timer.enable(alertTimerId);
     timer.restartTimer(alertTimerId);
     }
}

void alert() {
    pson data;
    data["The garage door is open for"] = timeInterval;
    thing.call_endpoint("alert", data);
}

/***********************************************************************************/

void setup() {
  
  pinMode(RELAY_1, OUTPUT);              // set pin D0 (GPIO16) output.
  digitalWrite(RELAY_1, HIGH);           // set pin D0 (GPIO16) high.
  
// timed actions setup
  alertTimerId = timer.setInterval(alertInterval * 60000, alert);
  distanceTimerId = timer.setInterval(PINGSPEED, distance_update);
 
// output resource (distance)
  thing["distance"] >> outputValue(distance);

// output resource (doorState)
  thing["doorState"] >> outputValue(currentDoorState);
  
// output resource (door state and time interval)
  thing["doorState_timeInterval"] >> [](pson& out){
    if (currentDoorState == "Open"){
       String doorState_timeInterval = String("Door   is   OPEN   for   ") + timeInterval + String("   min");
       out = doorState_timeInterval.c_str();
       } else if (currentDoorState == "Closed"){
       String doorState_timeInterval = String("Door   is   closed");
       out = doorState_timeInterval.c_str();
       }
    };
  
// input resource (open-close the door by pushing the dashboard button)
  thing["relay_1"] << [](pson& relay_1){ 
    if(relay_1.is_empty()){
       relay_1 = false;
       }
       else{
       digitalWrite(RELAY_1, LOW); delay(300); digitalWrite(RELAY_1, HIGH); 
       }
    };

// input resource to remotelly modify the alertInterval variable defined as a global variable.
  thing["alertInterval"] << [](pson& in){
    if (in.is_empty()){
        in = alertInterval;
        } else {
          alertInterval = in;
          } 
    if (alertInterval == 0){
        timer.deleteTimer(alertTimerId);
        } else {
          timer.deleteTimer(alertTimerId);
          alertTimerId = timer.setInterval(alertInterval * 60000, alert);
          }
    };

// input resource to remotelly modify the distanceThreshold variable defined as a global variable.
  thing["distanceThreshold"] << [](pson& in){
    if (in.is_empty()){
        in = distanceThreshold;
        } else {
          distanceThreshold = in;
          } 
    };
}

void loop() { 
  /*   It is important here to do not add delays inside the loop method, 
  as it will prevent the required execution of the thing.handle() method.  */

  timer.run();                         

  thing.handle();

  door_state_update();

  check_endpoint_call();
}

Mounting. The EchoGarage is typically mounted to the ceiling at the garage, with the distance sensor facing down. When the garage door is closed, it reads the distance to the ground, or the top of your car if you’ve parked in the garage. When the door opens and comes up, the sensor reads the distance to the door, which is a much smaller value. So by checking the distance value, it can tell if the door is open or closed. The distance value can also tell you if your car is parked in the garage or not, which is an additional benefit that can be useful at times. For roll-up garage doors, the ceiling mount would not work. Instead, you can mount it to the side of the door, with the sensor facing the outside. This way the logic is reversed: if the distance reading is small, it means the door is closed, and vice versa.

Interfacing with Garage Door System. EchoGarage uses a relay to simulate door button clicks. For most garage door systems, you can simply connect the two wires from the Relay output to where you would normally insert your door button wires into.

Initial Software Setup. Before proceeding, it’s recommended that you create a thinger.io account, log in, and then register a device. Follow the instructions at http://docs.thinger.io/console/#-adding-devices, keep safe your device credentials (you’ll need these later). EchoGarage is powered by a microUSB cable. At default settings, it boots into Access Point (AP) mode, creating a WiFi network named Thinger-Device. Use your phone or a computer to connect to this network using “thinger.io” as WiFi password. Wait for the configuration window or open a browser and type in 192.168.4.1 to access the AP homepage. You will see a list of nearby WiFi networks scanned by the controller. Configure the wifi where the ESP8266 will be connected, and your thinger.io device credentials. Your EchoGarage should be now connected to the platform.

Thinger.io Console Setup. You should login to your thinger.io account. Go to Dashboards page and create a new one. Add six widgets and configure their settings accordingly:

1. Type = Text/Value, Text Value = From Device, Select Device = , Select Resource = doorState_timeInterval, Refresh Mode = Sampling Interval (1s).
2. Type = Time Series Chart, Chart Input = From Device, Select Device = , Select Resource = distance, Refresh Mode = Sampling Interval (1s), Time Period = 10 min.
3. Type = Progressbar, Progressbar Value = From Device, Select Device = , Select Resource = distance, Refresh Mode = Sampling Interval (1s), Min/Max Value = Based on your garage distance measurements.
4. Type = on/off State, Select Device = , Select Resource = relay_1.
5. Type = Slider, Select Device = , Select Resource = distanceThreshold, Min Value = 0, Max Value = 400, Step Width = 1.
6. Type = Slider, Select Device = , Select Resource = alertInterval, Min Value = 0, Max Value = 240, Step Width = 1.

Then, go to Endpoints page and add a new one with the following settings:
Endpoint Identifier = alert, Endpoint Type = Email, Email Address = , Email Body = Send device data as JSON.

Finally, you should go to Data Buckets page and add a new one with the following settings:
Enabled = Yes, Data Source = From Device, Select Device = , Select Resource = doorState, Refresh Mode = Update by Device.

Enjoy!!

I have used EchoGarage for a while and, under several hypothetical scenarios, it always works as expected. It is my intention to further improve EchoGarage by adding new features, code optimisation etc. I’ll keep this topic updated on the progress. All suggestions are welcome.

My To-Do list:

  1. Configuration of an Endpoint to send SMS alerts using twilio or nexmo platforms (all my efforts have been wasted, any help would be much appreciated!).
  2. Addition of a new member to EchoGarage family (ultrasonic parking assistant module). Connection of the two modules via Thinger.io platform.

Best wishes
Sotirios

PS. EchoGarage was inspired by another open-software and open-hardware project (OpenGarage). You can find more information at: https://opengarage.io

2 Likes

Try using an IFTTT channel with an endpoint. Those have worked well for me. I have one the notifies me of state changes and another that adds a line to a spreadsheet for record keeping fun. I can see how many times the auto-close feature works as well as when the door opened and closed.
Note that IFTTT can take a few minutes for the notification to come through - anywhere from a few seconds to 30 minutes!

Unfortunately I haven’t been able to set up an IFTTT endpoint so far. I haven’t figured out why it doesn’t work. It is not a fault in my code because email endpoint works just fine. I carefully read the documentation and follow the instructions but nothing! :disappointed:

Any way, over all I don’t think that IFTTT is the most reliable platform for notifications. It allows only 10 SMS/month for international users (I am from Greece) and I totally agree with you that some times it takes 30 minutes for a message to come through. This is not a “notification”!

On the other hand, yes, you have to pay for commercial SMS platforms like twilio or nexmo but the prices are very low and you get instant notifications even at places without 3G signal!

I am looking forward to seeing at least one of the above platforms natively supported by thinger.io Endpoints in the near future.

Nice project @szormpas!! and a very detailed description :open_mouth:

You can share also your project also in hackster.io, tagging the hardware and software used, so you can reach greater visibility, and probably more feedback! You can just practically copy and paste your description!

Do you have an example REST API call for sending messages from Twillio? Maybe I can define a new template, like the one with IFTTT…

Bests!

Thanks @alvarolb , I will follow your suggestion and upload my project in hackster.io.

I have found an example REST API call at twilio API reference:

{
  "sid": "MMc781610ec0b3400c9e0cab8e757da937",
  "date_created": "Mon, 19 Oct 2015 07:07:03 +0000",
  "date_updated": "Mon, 19 Oct 2015 07:07:03 +0000",
  "date_sent": null,
  "account_sid": "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "to": "+15558675309",
  "from": "+15017250604",
  "body": "This is the ship that made the Kessel Run in fourteen parsecs?",
  "status": "queued",
  "num_segments": "1",
  "num_media": "1",
  "direction": "outbound-api",
  "api_version": "2010-04-01",
  "price": null,
  "price_unit": "USD",
  "error_code": null,
  "error_message": null,
  "uri": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/MMc781610ec0b3400c9e0cab8e757da937.json",
  "subresource_uris": {
    "media": "/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/MMc781610ec0b3400c9e0cab8e757da937/Media.json"
  }
}

Hi all,

I have just completed a new upgraded version of EchoGarage. New features has been added like detection of the car presence using an extra HC-SR04 ultrasonic sensor and parking assistance with the aid of a nice neopixel led ring!

You will find detailed instructions here.

thanks but i have my project here failed to send in thinger platform what wrong?#include <HCSR04.h>
#include <ThingerESP8266.h>
#include <ESP8266WiFi.h>
#include “arduino_secrets.h”

const int trigPinIn = D1;
const int echoPinIn = D2;
const int trigPinOut = D7;
const int echoPinOut = D8;

#define USERNAME “esromu”
#define DEVICE_ID “ESP”
#define DEVICE_CREDENTIAL “4t?emE?4iw4t+PKA”

//credentials for wifi connections
const char* ssid = SECRET_SSID;
const char* password = SECRET_PASSWORD;

ThingerESP8266 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);
//initialize distance
int max_capacity = 4; // maximum occupancy capacity
int occupancy = 0; // current occupancy

int distance_in = 0;
int distance_out = 0;

const unsigned long START_TIME = 19 * 60 * 60 * 1000; // 19:00 in milliseconds
const unsigned long END_TIME = 1 * 60 * 60 * 1000; //1:00 in miliseconds

int getDistanceIn(int trigPin, int echoPin) {
int duration,distance;
digitalWrite(trigPinIn, LOW);
delayMicroseconds(2);
digitalWrite(trigPinIn, HIGH);
delayMicroseconds(10);
digitalWrite(trigPinIn, LOW);
duration = pulseIn(echoPinIn, HIGH);
distance = duration * 0.034 / 2;
return distance;
}

int getDistanceOut(int trigPin, int echoPin) {
int duration,distance;
digitalWrite(trigPinOut, LOW);
delayMicroseconds(2);
digitalWrite(trigPinOut, HIGH);
delayMicroseconds(10);
digitalWrite(trigPinOut, LOW);
duration = pulseIn(echoPinOut, HIGH);
distance = duration * 0.034 / 2;
return distance;
}

void setupUltrasonic() {
pinMode(trigPinIn, OUTPUT);
pinMode(echoPinIn, INPUT);
pinMode(trigPinOut, OUTPUT);
pinMode(echoPinOut, INPUT);
pinMode(LED_BUILTIN, OUTPUT);

// Register the ultrasonic sensors as Thinger.io resources
thing.add_wifi(ssid, password);
thing[“students_in”] >> [](pson& out) {
out[“students_in”] = getDistanceIn(trigPinIn, echoPinIn);

};

thing[“students_out”]>>[](pson& out){
out[“students_out”] = getDistanceOut(trigPinOut, echoPinOut);

};
}

void setup() {
Serial.begin(115200);
thing.add_wifi(ssid, password);
setupUltrasonic();//calling setup function

// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi..");

// Initialize the Thinger.io client
thing["led"] << digitalPin(LED_BUILTIN);

}

//function for updating values
void updateCount() {
unsigned long current_time = millis();
if (current_time < START_TIME || current_time > END_TIME) {
occupancy = 0;
return; // do not send data if current time is outside the specified range
}
int distanceIn = getDistanceIn(trigPinIn, echoPinIn);
int distanceOut = getDistanceOut(trigPinOut, echoPinOut);

if (distanceIn>=1 && distanceIn<=25) {
occupancy+=1;
if(occupancy>=max_capacity){
occupancy-=1;
thing.write_bucket(“BucketIn”,“students_in”);
Serial.println(“data sent”);

}

}

if (distanceOut >=1 && distanceOut<=20) {
occupancy+=1;

if(occupancy>=max_capacity){
thing.write_bucket("BucketOut",String(occupancy).c_str());
Serial.print("data sent to thinger  ");
Serial.println(occupancy);
}

}
Serial.print(“Students IN:”);
Serial.println(occupancy);
// Serial.print(" cm, OUT: “);
// Serial.print(distance_out);
// Serial.print(” ,Occupancy: ");
// Serial.println(occupancy);

}

void loop() {
delay(200);
thing.handle();
updateCount();
}