Monday, December 7, 2020

Turning a doorbell switch into a home security camera...and still retain its original function

Today there is an off-the-shelf solution for practically every average-Joe-grade idea one may have. There is always the chance that when one thinks of something that could have a practical purpose and cover a specific need, that there is already a product in the market that will do that function.
Even though home automation may still be in its infancy, today we see a market practically flooded of solutions that one way or another aim to respond in terms of comfort, or to help improve the home experience, or even to enhance the safety and security of its occupants.


The so called IoT devices that we are used to see in the market range from vacuum cleaners, automated window blinds, refrigerators, ovens, light switches, heaters, HVAC's, Smart TVs, Smart doormats, Smart toilets, etc. And then there are the standards, the integrations and all the associated lingo and acronyms: IoT, MQTT, Zigbee, WSN's, AI, etc.

So, a lot keeps popping up, and the comercial front is obviously strong with many players, both from the Western world and from Asia, with the later proving to be quite smart and creative in this arena. Gone are the years where we would see China strictly as a copy cat, in what IPR was concerned. Large companies like Xiaomi have proven to be very capable of competing not strictly through price, but because of delivering good UX and overall original products from their own merit.

Beyond the strictly commercial sphere, there is also the community (and this is the part I find most exciting) with a strong interest in the integration and in covering the bits of effort that enable totally disjoint devices to be orchestrated and used in combination to give a house the impression of being a smart entity. Some open source initiatives have proven to provide functional alternatives to the use of strictly cloud based commercial integrations. It is the case of several projects, some of which I have been referring to in this blog: Home Assistant, and in the devices domain, Tasmota. But there are many other platforms that are popular in the context as well: OpenHAB, Node-RED, Calaos, Domoticz. And beyond this, the building blocks that make these platforms possible or more convenient to setup and use: Docker containerization, MQTT messaging, relational databases, HTTP(s) proxying/balancing, Dynamic DNS agents, etc.

In the hardware domain there have certainly been developments that made feasible many of the concepts that are in place today. It's only due to the fact that we can now squeeze plenty of functionality and computation into tiny and cheap integrated circuits, that we can for example have dozens of independent devices with their own connectivity, and able to collect sensor data and perform tasks. If the required computation would still be bulky and expensive, none of this would be possible. 

The cost basically came down to the point where for a price comparable to a fast food meal, you can get a tiny device with computing power that outperforms some late 90's computers, and also features WiFi and can interface with peripherals such as a camera sensor and MicroSD cards for additional storage. Hardware with these characteristics make a perfect substrate for a variety of automation tasks around the house.

The ESP32 IoT chip

That is the case for the ESP32 chip from Expressif. I have been using a lot of products based on its older sibling, the ESP8266. But while it's already here for a few years, this dual core tiny beast is a game changer for many applications.

While this device's features vary depending on the specific part and generation, the baseline ESP32 is characterized for having 2 main CPU cores (Tensilica Xtensa 32-bit LX6 microprocessor) and features a clock speed that can go up to 240 MHz which allows for up to 600 MIPS. It also features WiFi, Bluetooth 4.2 and Bluetooth Low Energy (BLE). Also it is expandable: while it has some internal RAM and Flash, it can interface with external Flash and RAM, up to 16 MB and 8 MB respectively. The RAM chip it can interface with is designated as "PSRAM", which stands for Pseudo-Static RAM (it is basically a dynamic RAM which has the circuitry that abstracts the refreshing cycles that are needed by the later, making it look like a static RAM for the CPU). In spite of being external, these are memory-mapped to the CPU address space as well.

So this is a pretty feature-rich little package, and the impressive thing is that is so cheap given the scale at which it is produced, and the level of integration that is involved.

Besides the IoT industry, this device soon captured the interest of makers and the open source community. Because there are many tools freely available to develop code for it (including the Arduino IDE), it is relatively easy to start doing interesting things with it.

And because it is so powerful and has relatively fast IO, people are able to do less obvious things with it, such as producing a composite analog video output and render 3d graphics:

The price was certainly an aspect that attracted me. For 8€ give or take, I got a "ESP32-CAM" board, which is basically a board featuring the ESP32 module, an external 4 MB of PSRAM, and also 4 MB of flash ROM. 

For this price it also includes a OV2640 camera, which happens to be a good match, as it is also a pretty sophisticated little package. 

Besides being an image sensor, it integrates the processing power to do the MJPEG compression (along with other output formats) for the entire range of resolutions that it support, which go up to 1600x1200. At this resolution it can still output 15 fps MJPEG compressed video. This allows the impact on the computing resources of the ESP32 to be limited, which is a major difference for certain use cases to even be possible (e.g. streaming the video via IP).

Using the ESP32 for the doorbell switch camera

My idea was to take advantage of this device in order to build a sort of a "smart" doorbell switch. Being able to capture the event of the doorbell switch being pressed would allow automations to be created in Home Assistant to for example send a message via Twitter or Telegram telling that someone was at the door. This could even be further improved to attach a picture taken from the angle of the doorbell switch, eventually allowing the person to be captured in the photo. In another scenario, if intrusion would be detected, this could also be used to capture pictures or video clips to eventually help as evidence for further investigation.

With that in mind, I went on and purchased a basic Legrand doorbell switch which I aimed to convert for this project.

I started by inspecting it after some disassembly in order to better understand how this conversion could be done:

At the center of the switch there is a cavity for housing the neon bulb, which I saw as being the ideal spot to place the camera instead:

Because here the distance between the camera and the ESP32 board would have to be greater than the original flat-flex cable would allow, I had no other choice but to try to find another camera with a longer flat-flex cable. I could easily find other cameras in Banggood. For about 10 € I bought a pack of 3 cameras with longer cables:

I had previously bought a single camera with a long cable and fisheye lens as well, but this one was faulty. Also, one of the cameras in the 3-pack had intermittent failures, so production quality is certainly something to keep in mind regarding these cameras.

I started by opening a slit across the switch in a section not populated by any function:

This would allow the flat-flex cable to go through from the front of the switch to the rear, where I would keep the ESP32 PCB:

The next step would be to build a backplane PCB for properly mounting the ESP32 and the electronics I would need to add: as I wanted to be able to both capture the switch presses and eventually control the doorbell itself (e.g. to use the doorbell as alarm sound in case of intrusion), this required these extra electronics to be added. Basically I needed to have a relay to trigger the doorbell, and route the doorbell switch pins to the ESP32. To control the relay I needed a transistor, as the ESP32 GPIOs are too weak to directly drive the relay:

Because just leaving the camera exposed would not be too elegant and make it obvious that this would be a modified doorbell switch, I made a small polycarbonate window with most of its area painted with a black marker pen:

In order to keep the camera attached to the housing, I cut a small aluminium part which would both act as support and provide some heat dissipation:

In order to maximize  the WiFi range, and because the ESP32 board (where the built-in WiFi antenna is attached to) would be buried inside the wall, I found important to instead use an external antenna. This ESP32-CAM board can undergo a small modification in order to use an external antenna instead of the onboard one. It requires some precision soldering, which is not the easiest task, but it can be done with a proper soldering iron and some skills. Basically there is an SMD resistor between the RF output from the ESP32 and the PCB trace antenna. In order to use an external antenna connected to the IPEX connector, this resistor has to be moved towards the IPEX connector, as shown in green:

This way, the internal antenna is disabled, and an external antenna can be used. In the doorbell switch, I wanted to make sure the antenna would be as unobstructed as possible, especially in respect to metallic parts that could shield or reflect the signal to undesired directions. As such I placed it in the edge of the metallic frame of the switch as shown here:

This in particular is an antenna that I have salvaged from an old laptop computer. The cable already included the IPEX connector, which was convenient.


One issue that up to some point was quite intriguing and which to some extent compromised this project was about stability while I had the ESP32-CAM mounted in the board I've built: initially I tested the ESP32-CAM using this project:

Running the board alone without being attached to anything except a power source (this board can be powered from a +5 V DC source), it would work quite well, achieving a very good framerate, even at the higher resolutions (not the 15 fps the OV2640 chip is capable of, but close enough).

Then I discovered that Tasmota and its ESP32 support was gaining shape, and that ultimately the camera began to be supported as well, and as such I moved to it without much hesitation:

But as soon as I tried to mount the ESP32 board on my perfboard, the device would become very slow, with a poor frame rate and sometimes not even being able to initialize the camera. The UI would also respond very slowly.

After talking with the community and doing some experimentation, I learned that this board has a bit of a unfortunate design flaw: they have decided to use GPIO0 as the pin to which the camera's MCLK pin is connected to. As this GPIO must also be used when programming the ESP32 (it needs to be pulled to GND when booting, in order to enter the programming mode), it is also routed to the pin headers on the board, so that it is accessible for this effect. Now, during runtime, and in particular when the camera is in use, this line sees very high frequency signals, and short rise/fall times are required for proper communication with the camera. As such in these circumstances the design of the traces becomes somewhat critical, and it is very easy to introduce spurous capacitance that will end up having an adverse impact in the integrity of the signals if the traces are not kept as short as possible and following certain rules (almost like when dealing with RF signals).

You may take a peek at how this board is wired here:

As this is part of the board design, there is very little the user can do to mitigate this problem. I have tried adding a "termination" resistor either pulling to GND or VCC, but without any effect in both cases.


The only trick that ended up providing positive results was to trim the GPIO0 pin as much as I could (while still making possible to use it to program the board). This probably resulted in a sufficient reduction in capacitance, while the board is attached to another PCB:

This modification seemed to have allowed the issue to be avoided, allowing the device to be responsive again and the framerate to become acceptable. Perhaps there is still impact in the performance, but there is hardly much more that can be done, given the poor design decision that was taken when creating this ESP32-CAM board. Eventually an adaptor cable between the camera and the ESP32 board could allow this pin to be mapped to a different GPIO pin, but between doing this and fabricating a better board I would prefer putting the effort on the later.

In spite of being recent work with the potential for not being 100% mature compared to the ESP8266 part, Tasmota in this platform so far proved to be quite stable. The firmware inherits much of what you will encounter for the ESP8266, such as the UI which is pretty much the same:

In terms of commands and rules is is all the same as well. I was quite happy to see that Tasmota was at this level, because I wanted to, beyond the IP camera feature, to have MQTT available, so that the doorbell could be controlled, or its events used by other plaforms, especially Home Assistant.

Building the firmware / configuration

In terms of configuration I essentially had to get a branch of Tasmota with support for the camera (as of this writing it is merged with the Tasmota development branch):

and under the file:


add the line:


This allows both the RTSP server to be enabled and used (added by gemu2015), and permits that the stream be enabled without the UI having to be accessed (withouth necessarily using RTSP).

Besides this, it is important to uncomment "tasmota32-webcam" under the platformio_override.ini file:

; *** Build/upload environment
default_envs =
; *** Uncomment the line(s) below to select version(s)
;                tasmota
;                tasmota-debug
;                tasmota-ircustom
;                tasmota-minimal
;                tasmota-lite
;                tasmota-knx
;                tasmota-sensors
;                tasmota-display
;                tasmota-zbbridge
;                tasmota-ir
;                tasmota32
;                tasmota32-minimal
;                tasmota32-lite
;                tasmota32-knx
;                tasmota32-sensors
;                tasmota32-display
;                tasmota32-ir
;                tasmota32-ircustom

and in the same file, set the correct serial port to the one you will be using for programming the device:

; *** Upload Serial reset method for Wemos and NodeMCU
upload_port               = COM3

Then it is a matter of connecting the board to the serial adaptor, pulling GPIO0 to GND, and in Visual Studio Code, select Terminal > Run Task... > PlatformIO > Upload:

Once flashed to the device, is the usual Tasmota stuff: search for the Tasmota WiFi AP, open the captive portal and enter your WiFi credentials to permanently configure the WiFi settings.

Once the device is ready, you will need to configure the module by entering the device web UI and go to Configure Module. For this board and camera in particular, the module type that works is  "ESP32 Cam AiThinker (2)":

I only had to do three customizations to the GPIO settings for this board:
  • even though I did not plan to use it, I set GPIO4 as PWM 1. This allows the built-in power LED to be turned on and have its intensity adjusted. One interesting aspect about this LED is that it is synchronized with the camera, so that it doesn't turn on half way through the scanning of a frame. This prevents half-lit frames or similar artifacts from occuring (which would otherwise be a problem as the LED intensity is PWM modulated);
  • then, GPIO12 was set as a Switch input, so that it receives the doorbell switch events;
  • and finally the GPIO13 was set as a Relay output, for (as expected) controlling the relay that will turn on the actual doorbell;
One last thing that should be done, is to initialize the camera on boot. For that effect all that is needed is to add a rule that runs the initialization command during boot:

on system#boot DO wcinit endon

Integration with Home Assistant

So far I have tested the camera feature by integrating with Home Assistant, and it works very well. I am not using RTSP, as MJPEG as a payload does not seem to be recognized.

For configuring it in Home Assistant, I have basically done the following:
  • defined a MJPEG camera platform in HA's configuration.yaml file:
  - platform: mjpeg
    name: doorbell-cam

then in lovelace, I configured a picture-elements card that points to this new camera entity:

  - entity: camera.doorbell_cam
    type: image
    title: Imagem Entrada
    camera_image: camera.doorbell_cam
    camera_view: live
image: /local/custom_lovelace/house_maps/planta_casa_render_15112020.jpg
type: picture-elements

The result is an element that shows the still images as a thumbnail, and when clicked it presents the live stream:

I haven't yet created the automations I expect to setup. I will dedicate a future blog post to detailing these.

No comments: