Ding Dong: How a doorbell gets made

Posted on | ~5mins
homeautomation

doorbell installed next to door

This post was originally seen on siliconburnout.

Requirements

Should look relatively like a standard doorbell, when the bell is pressed both our phones get a notification and Alexa gets a notification.

Non-Requirements

Device is to be used inside our condo building but outside the unit, no weather sealing or theft prevention is required.

Architecture

The overall design is to use an ESP8266 + WiFi + AWS API Gateway + SNS. The ESP will wake up, connect to our home WiFi network, send a REST request to AWS which will turn that into a notification to our phones via a standard SMS text message.

Hardware

Battery: https://www.amazon.com/gp/product/B00J2QET64
Main board: https://www.dfrobot.com/product-1634.html (amazon)
Resistors: 2x 10k, 1x 4.7k
Button Switch: https://www.digikey.com/short/zq19c0

The wireless nature of this project made an ESP the obvious choice.

rough schematic pencil drawing

Nothing too terribly difficult with the wiring of this thing. We tie the RST line to vbat through a resistor with a switch (the doorbell button) wired to pull RST to ground. Later we added a voltage divider to A0 so we can get a reading of the battery level from the device and send along with our cloud request. For debugging there was previously an LED on GPIO2, but that is not required for the final version.

Some notes on power Evaluating several ESP32 and ESP8266 boards in deep sleep mode was seeing roughly 5mA in deep sleep, this felt too high for the battery 1200mAh battery I had chosen; roughly 10 days >:( Some googling lead to this site which recommended the Firebeetle board I ended up using. I inadvertently ordered the ESP8266 version of that board (as opposed to the ESP32) and once I got the correct board file the new setup is drawing essentially nothing when off (lower even than Nick on that blog saw with the esp32). When pressed we spend several seconds drawing 11-15mA then back to essentially nothing. There are more tricks I read about for saving energy on these boards, but most of our time is spent in deep sleep and the energy used awake is already pretty minimal.

Enclosure

OpenSCAD file: via dropbox
STL File: via dropbox

Jeff’s employer has a maker space with several 3d printers which we were able to make use of for this project. The one I chose to use was the Ultimaker 3. This was our first go at 3d printing anything and took a few misstarts before we managed to get a reasonable output. We used OpenSCAD to make a simple rounded corner enclosure with a hole for the button. And a back plate to mount on the wall.

a
b
c
d

The back plate and the front enclosure fit snugly and since, assuming would need a redesign at some point, holes for bolts not placed in the case. Currently this doorbell is installed with only the friction of the two parts holding it together. If I were to reprint, I would do something to better hold nuts inside and secure with a bolt.

Software (device)

Board wiki documentation
Board file

We used the Arduino IDE for this project. Thus the code we needed to write is super simple: on wake up; connect to WiFi, make REST request to AWS, return to deep sleep.

The battery reading code is just sending along the raw ADC reading but once the battery runs down we should have some rough calibration on what that reads when it needs to be charged and can change to a “percentage” or add an alert when it gets low.

For the https connection to succeed, the fingerprint of the certificate is hard coded in the FW to obtain this for the AWS end point:

$ openssl x509 -noout -in <(openssl s_client -showcerts \
-servername v1l34k7c28.execute-api.us-east-1.amazonaws.com \
-connect v1l34k7c28.execute-api.us-east-1.amazonaws.com:443 &1 | \
awk '/BEGIN CERT/ {incert=1;} { if(incert) print $0;} /END CERT/ { incert=0; }') \
-fingerprint -sha1 | sed -e's/.*=//' -e's/:/ /g'

72 D4 00 92 77 37 50 C9 9B A1 38 FA 21 8A 9B FD BA CF CD 49

Arduino Code

The code below is extracted from what I actually used, you could do everything from setup() but mine is split to use loop() as well due to some debugging code omitted from this listing:

/*
 * For Doorbell...
 * Board file: FireBeetle-ESP8266
 * Upload speed: 921600
 * CPU Freq: 80mhz
 * Flash size: 4M (3M SPIFFS)
 * ... defaults ...
 * Board file from:
 * https://raw.githubusercontent.com/DFRobot/FireBeetle-ESP8266/master/package_firebeetle8266_index.json
 */

#include <time.h>

#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <WiFiClientSecure.h>

static int battery_level = 0;
static bool doorbell_pressed = false;
static const char *ssid = "MyWifiNetwork";
static const char *password = "MyWifiPassword";

void notify_AWS(void) {
  HTTPClient https;

  char URL[1024];
  snprintf(URL, 1024,
           "https://v1l34k7c28.execute-api.us-east-1.amazonaws.com/default/"
           "Doorbell_Publish?rssi=%d&vcc=%d",
           WiFi.RSSI(), battery_level);
  const char *CA_SHA1 =
      "72 D4 00 92 77 37 50 C9 9B A1 38 FA 21 8A 9B FD BA CF CD 49";
  const char *API_KEY = "MyAPIKey";
  if (https.begin(URL, CA_SHA1)) {
    https.addHeader("X-Api-Key", API_KEY);
    https.addHeader("Content-Type", "application/json");

    int httpCode = https.GET();
    https.end();
  }
}

void doorbell_deep_sleep(void) { ESP.deepSleep(0); }

void on_doorbell_pressed(void) { notify_AWS(); }

void read_battery_level(void) { battery_level = analogRead(A0); }

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.printf("Connecting to WiFi..\n");
  }

  Serial.printf("Connected to the WiFi network\n");
  doorbell_pressed = true;

  read_battery_level();
}

static int loop_count = 0;

void loop() {
  printf("Loop %d ...\n", loop_count);
  loop_count++;

  if (doorbell_pressed) {
    doorbell_pressed = false;
    on_doorbell_pressed();
  }

  delay(1000);
  Serial.printf("Heading to deep sleep\n");

  doorbell_deep_sleep();
}

Software (cloud)

Basic architecture:
APIGateway -> Lambda +-> SNS 
                     +-> CloudWatch
                     +-> AlexaPush
Lambda Function:
def notify_alexa():
    url = "https://api.notifymyecho.com/v1/NotifyMe?notification=Ding%20Dong!&accessCode=amzn1.X"
    with urllib.request.urlopen(url) as response:
        html = response.read()

def lambda_handler(event, context):
    client = boto3.client('sns')
    response = client.publish(
        TopicArn='arn:aws:sns:us-east-1:X:Doorbell',
        Message='Ding Dong',
    )

    notify_alexa()

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!'),
        'sns_response': json.dumps(response)
    }

We could not find a great way to do notifications on the echo, we previously had built some Alexa skills, and were comfortable with how the SDK works, but even so were unable to figure out push notifications in that ecosystem. Luckily Notify Me already solved this and by activating that skill we were able to get the push piece working, though not exactly as we would like.

The logging piece for the battery level is also not shown here.