The other day, with a lot of free time and being bored, not wanting to demote on apex by playing tilted.
I looked around and noticed an abandoned electronics set that I bought when I was young full of life and dreams, wanting to create robots, and change the world.
to fill that void of feelings and to catch up to my robotics career (also my doorbell broke a couple of days before). I started building a doorbell that sends a you in telegram notifications!
today we’ll build a doorbell that sends messages to telegram through a bot.
Content:
- Hardware required.
- Part - 1 Setup.
- Part - 2 Schematic.
- Part - 3 Connect microcontroller to API.
- Part - 4 Telegram bot.
- Part - 5 back-end
- Conclusion.
suggestion To follow this tutorial you’ll only need to have some prior experience programming with Golang and some JS.
Hardware required.
- NodeMCU - (ESP8266) - check-it here.
- button.
- 10k ohm resistor.
- protoboard.
Part - 1 Setup.
First things first, we need to configure our WiFi Module. all in all, we need to:
- Get the computer to recognize the nodeMCU module.
- Flash Espruino to the nodeMCU module. ^c8384a
- Verify that we can connect to our router.
Get the computer to recognize the microcontroller.
Please check that there is no bare metal (including your desk!) near the board when you plug it in, as it could short it out.
using and USB cable to connect the nodeMCU to the computer.
on the terminal run:
ls /dev/c***
if the output you get is something like this
**/dev/console** **/dev/cu.usbserial-1230** **/dev/cu.wlan-debug**
**/dev/cu.Bluetooth-Incoming-Port** **/dev/cu.wchusbserial1230**
this means the chip is recognized -> /dev/cu.wchusbserial1230 https://github.com/marcelstoer/nodemcu-pyflasher
if not follow this amazing guide to install CH340 drivers
Flash Espruino to the nodeMCU module.
there are multiple ways to flash the Espruino firmware to your microcontroller, in this occasion we’ll use NodeMCU PyFlasher.
the steps we are going to follow are:
- install nodemcu-flasher
- flash espruino_2v14_esp8266_combined_512 to the microcontroller
install nodemcu-flasher.
install the nodemcu-pyflasher the guide itself is exquisitely made and covers everything you need to know in detail.
If everything went well you should see something like this
Burn Espruino to the microcontroller.
Now it’s time to install the firmware.
Download espruino_2v14_esp8266_4mb_combined_4096.bin
open nodemcu-pyflasher, in serial port, select the port corresponding to the NodeMCU microcontroller. on NodeMCU firmware select espruino_2v14_esp8266_combined_512.bin baud rate -> 115200 Flash mode -> Dual I/O (DIO) Erase flash -> yes, wipes all data and click Flash NodeMCU
if the flash was successful the console prints something like
...
...spects of microcontroller...
...
Leaving...
Staying in bootloader.
Firmware successfully flashed. Unplug/replug or reset the device
to switch back to normal boot mode.
Part - 2 Schematic.
The focus of this section is to get the connections in place so we can get to the code part. We will divide this part into two sections.
The first section will focus on connecting the button with the micro-controller, this will be more than enough to accomplish the main goal.
In the following optional section, we are going to add more components to support an AC device let us say a light bulb or an actual speaker to complement the telegram notification.
Connect the stuff.
In this part, we are going to connect a button, so the NodeMCU reads LOW when the button is not pressed and HIGH when is pressed.
Full schematic.
Part - 3 Connect microcontroller to API.
It’s time to write JS code to control the NodeMCU module, for that we are going to use the browser and the Espruino IDE.
:::note Setup make sure that your computer detects the module when you connect it. If that’s not the case you can find some help here Part - 1 Setup. :::
Espruino IDE.
in your favorite browser open https://www.espruino.com/ide/#, in this occasion, I will be using Google Chrome. Click on the top left connect button -> Web Serial -> <your_port_number>
connect to WiFi.
After a successful connection with the IDE, it’s time to write some code.
To use wifi we are going to use the wifi
module, all in all, we are going to need, the WiFi SSID
and password
.
the
The purpose of the code it’s to disconnect previous connections and start a new one. if the connection fails, we print it on the console.
wifi.disconnect(function(){
wifi.connect(SSID, {password:pass,channel:0}, function(e) {
if(e){
print(e);
}
});
});
This is ok we have the driver connected to the computer where we can see the console and debug, but then what if the microcontroller was on its own? we need a way to know if the connection went well or not.
wifi.save("clear");
var endLoading = notifyLoading(); // start loading
wifi.disconnect(function(){
wifi.connect(SSID, {password:pass,channel:0}, function(e) {
if(e){
endLoading(); // end current loading
endLoading = notifyLoading(errorTime); // faster blink indicating that something went wrong.
return;
}
endLoading(); // end loading connection successful.
});
});
the notifyLoading function toggles the build-in led on and off on a timely interval, errorTime
is just a faster interval resulting in a faster blink
var errorTime = 200;
NodeMCU.D4.mode("output");
function notifyLoading(time){
time = time || 1000;
var tiToggle = setInterval(function(){
NodeMCU.D4.toggle();
},time);
return function(){
NodeMCU.D4.write(1);
clearInterval(tiToggle);
};
}
so far so good, our microcontroller can connect to WiFi, and also we have a way to know if the connection went cool or not so cool.
- fast blink == BAD 🫠
- no blink == GOOD 😎
make the button work.
- Now it’s time to call the API every time we click the button.
NodeMCU.D2.mode("input_pulldown");
setWatch(clickButton,NodeMCU.D2,{repeat:true,debounce:50, edge:"rising"});
var afterClicks = 0;
function clickButton() {
times++;
clearTimeout(afterClicks);
afterClicks = setTimeout(sendCode,500);
}
in this case, we are using D2
in input
mode and calling clickButton
clickButton
counts the number of consecutive clicks and calls sendCode
after a 500ms period, for example.
Click.
200 ms passed
Click ..
100 ms passed
Click ...
500 ms passed
number of clicks 3
the meaning of these consecutive clicks is to let you know how important the door call is, probably the person ringing the bell is in a rush. I.e
- 1 - 3 clicks = normal
- 4- 7 clicks = important
- 8 - more clicks = urgent this could be scaled to let’s say recognize morse code, think of the possibilities.
- Before we call the API, we need a way to know if the microcontroller has an internet connection.
var times = 0;
var sendCode = function(){
isConnectedHOC(function(){
clearError();
callAlert(times);
times=0;
});
};
function isConnectedHOC(f) {
wifi.getStatus(function(e){
if(e.station ==="connected"){
f();
}
});
}
In this code you:
- check if the connection
station
=== “connected”- clear previous errors.
- call the API passing
times
and restoring it to 0.
- else ignore API call
Call the API.
In this section, you’ll add a generic function to call A REST API, with the number of times the button was pressed.
var api ="<your_end_point>";
var clearError = notifyLoading;
function callAlert(times){
if (loading) return; // check race condition
var res = http.get(api+"?times="+times, function(res) {
loading = true;
res.on('data', function(data) {
print("HTTP> ", data);
});
res.on('close', function(data) {
loading= false;
print("Connection closed");
});
res.on('error', function(data) {
loading= false;
print("Connection error");
});
});
res.on("error",function(err){
clearError = isError();
});
}
Refer to Tunnel your localhost. to get api
In this code you:
- check if there is a call to the API going on, if that is the case return
- call HTTP.get and update loading = true
- wait for a response from the API
- if res.on “data” print the response
- if res.on “close” or “error” update loading = false
- if the response returns an error we call
isError
, to physically see in the microcontroller that something went wrong and should be addressed.
Part - 4 Telegram bot.
In this section, you’ll
- create a telegram bot
- create a telegram channel
- make the bot admin of the channel
Create a telegram bot.
Creating a bot is a quick rewarding task, follow the Telegram guide to How do I create a bot?
After all, that configuration, try sending a /mybots
to BotFather
in telegram and you should see your shiny new bot.
:::info Key
We’ll need the API key for the REST API that we are going to create.
:::
Create a telegram channel.
To create a channel: iPhone: Start a new message (tap the icon in the top-right corner in Chats). Then ‘New Channel’. Android: Tap the circular pencil icon in the chat list. Then ‘New Channel’. Windows Phone: Tap the ‘+’ button on the bottom bar. Then ‘New Channel’.
Make the make admin of the channel.
Tap on the channel you just created -> administrators -> add admin -> search and select your bot.
Part - 5 back-end.
In this section, you’ll
- create a simple REST API
- configure your telegram bot and chat
- send messages to the channel depending on the
times
the doorbell was pressed. - create a custom messages structure to use depending on the number of
times
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"net/http"
"strconv"
"strings"
"time"
)
type regularMessage struct {
}
type urgentMessage struct {
}
type emote struct {
}
type message interface {
fmt.Stringer
ParseMode() string
}
// custom messages
func (e emote) String() string {
s := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s)
rs := r1.Intn('\U0001F640' - '\U0001F600')
return string(rune('\U0001F600' + rs))
}
func (r regularMessage) String() string {
return fmt.Sprintf("Hey! %s", &emote{})
}
func (r regularMessage) ParseMode() string {
return ""
}
func (r urgentMessage) ParseMode() string {
return "HTML"
}
func (urgentMessage) String() string {
return fmt.Sprintf("<b>%s</b>", strings.ToUpper("atiende la puerta"))
}
const telegramApi = "https://api.telegram.org/<your_bot_key>"
// select the type of message depending on the number of times
// the button was pressed
func getMessageType(times string) message {
timesin, _ := strconv.Atoi(times)
if timesin <= 3 {
return ®ularMessage{}
}
return &urgentMessage{}
}
// broadcast message to telegram channel
func sendAlert(ms message) {
b := map[string]any{
"chat_id": "<chat_id>",
"text": ms.String(),
"parse_mode": ms.ParseMode(),
}
body, _ := json.Marshal(b)
resp, _ := http.NewRequest(http.MethodPost, telegramApi+"/sendMessage", bytes.NewBuffer(body))
resp.Header.Set("content-type", "application/json")
http.DefaultClient.Do(resp)
respb, _ := io.ReadAll(resp.Body)
fmt.Println(string(respb))
}
// GET /
func handleIndex(w http.ResponseWriter, r *http.Request) {
sendAlert(getMessageType(r.URL.Query().Get("times")))
w.WriteHeader(http.StatusOK)
defer r.Body.Close()
}
// start server
func main() {
indexR := http.HandlerFunc(handleIndex)
http.Handle("/", indexR)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Let’s discuss the main parts:
sendAlert
is the function in charge of calling the telegram API that broadcast the message
getMessageType
sends a message type depending on the number of times the button was pressed, this can be scaled to record times between clicks in a determined range.
to start the server run go run main.go
Tunnel your localhost.
You’ll need to host the API you just created, but if you want to quickly test your code, you can create a tunnel to your local host. follow this tutorial from Cloudflare Install Cloudflare https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/
and run cloudflared tunnel --url http://localhost:8080
You should get something like this:
2022-08-04T00:37:22Z INF +--------------------------------------------------------------------------------------------+
2022-08-04T00:37:22Z INF | Your quick Tunnel has been created! Visit it at (it may take some time to be reachable): |
2022-08-04T00:37:22Z INF | *COPY THIS* https://blue-sorts-problem-adrian.trycloudflare.com |
2022-08-04T00:37:22Z INF +--------------------------------------------------------------------------------------------+
copy the link and refer back to the Espruino code you previously wrote and replace ‘api’ with this URL
check a more complete tutorial about how to create preview tunnels https://developers.cloudflare.com/pages/how-to/preview-with-cloudflare-tunnel/
Conclusion.
Nicely done! You’ve just created a doorbell that communicates with a go REST API that uses a telegram BOT to send messages! 😎
Suggested new ideas:
- you could add a “back-up” speaker when there’s no internet connection or the API is down and still know when someone is at the door.
- Integrate Alexa / echo, add a microphone to your circuit and use it as an intercom.