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):
- WeMos D1 mini board based on ESP8266 (wemos.cc).
- HC-SR04 ultrasonic distance sensor (grobotronics.com).
- SparkFun Logic Level Converter (sparkfun.com).
- Relay board (grobotronics.com).
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):
- Start Arduino IDE and open Preferences window.
- 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.
- 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:
- 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!).
- 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