Introduction: Push Me - Telegram Bot to Push the Button

Everyone needs to switch something On or Off. For most of our needs, there is already an available solution that we can use. But what makes us makers is the urge to do it yourself.

Let's say we have some appliances that we cannot thinker with. We cannot disassemble them and make the changes that we need. Or we just do not want to do that. So the question is how to control them?

My particular case involves a button that has to be pushed remotely but it cannot be disassembled because of a warranty. In this case, I have three options:

  1. Buy commercial solution
  2. Build my own solution

As a starter yes I can buy something premade and yes it will work but where is the fun of that? So we start the fun part!

Let us build a button pusher!

Supplies

Most of the time what have laying around is what we need to start with the hardware:

  • ESP8266 - in my case D1 mini
  • Servo motor - for me it is SG90
  • 3D Printer to print the enclosure

For the programming part:

  • Arduino IDE - or whatever you like at the moment
  • USB cable to connect to the board

Step 1: Hardware Connections

To operate with the servo we need to connect it to the board. In my case, I have soldered everything to the board to decrease enclosure size. Afcource you can use board headers and jumper wires for that.

Step 2: What Is Telegram Bot

The explanation that we have for Telegram Bot is: Bots are third-party applications that run inside Telegram. Users can interact with bots by sending them messages, commands, and inline requests. You control your bots using HTTPS requests to Telegram's Bot API. [source]

In our case, this is software that is communicating with Telegram and controls the microcontroller depending on the messages received from a specific user. And the software that we are talking about resides on your ESP8266 in your network.

Step 3: Create Telegram Bot

First of all, you need to have an account for telegram. You can access telegram on your PC or via your phone.

To create a bot we must communicate with almighty BotFather:

  1. Search for BotFather as you searching for any other contact
  2. Execute command to BotFather "/start"
  3. Choose to create a new bot "/newbot"

Ok, we are almost done. In the next step, we will give a name to our bot.

Step 4: Name Your Bot and Save the Token

After you have executed the last command you have to think a bit to choose the best name for your bot.

  1. My bot will be named "myBestBotEverMade".
  2. Also, we will be asked to give our bot a user name with the suffix "_bot", so my bot user name will be "myBestBotEverMade_bot".
  3. Now we are on the last step. If we did everything correctly Bot father should return a message that will contain our secret bot token. Save this token aside and do not share it.


Step 5: Save Our Own Identification

This step is required because we do not want everyone to tell our bot what to do. We want to be the one and only commander of our bot.

For that to happen we need to get our Telegram ID and put it aside for now for later use.

  1. In telegram find Bot with name IDBot.
  2. Send him "/getid" command
  3. You will receive your ID that we will need later to configure our bot

Step 6: Get to Know the Basics

As most of you already know almost everything we want to make is already out there and what we do is to stitch it together.

This case is not different the goal is to control the servo motor via telegram.

For the telegram, I am going to use CTBot. CTBot is a simple Arduino class to manage Telegram Bot on the ESP8266/ESP32 platform. It relies on the ArduinoJson library so, in order to use a CTBot object, you need to install the ArduinoJson library first (you can use library manager). You also need to install the ESP8266 Arduino Core and Library or the ESP32 Arduino Core and Library.

For the connection to my router and the internet, I will use WiFiManager. Espressif ESPx WiFi Connection manager with fallback web configuration portal. This connection manager will provide me with the possibility to save the additional configuration to the board that we can use with the bot without redeployment.

OK, now we have a brief idea of what libraries we need. But how we will start? What do we need before writing two or five lines of code that will do what we want? It is simple we need to check some of the examples provided with the libraries we have discussed:

Now we should have an understanding of what we have to use to make our bot work:

  • WiFi manager will help us store SSID and password for our router and Bot Token and our user ID
  • CTBot will use already stored Token and ID to communicate with Telegram

Step 7: Code Review and Upload

#include <FS.h>                   //this needs to be first, or it all crashes and burns...
#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager

#ifdef ESP32
  #include <SPIFFS.h>
#endif
#include "CTBot.h"
CTBot myBot;
#include <Servo.h>
Servo servo;

#include <ArduinoJson.h>          //https://github.com/bblanchon/ArduinoJson

//define your default values here, if there are different values in config.json, they are overwritten.
char bot_token[100];
char chat_id[16] = "123455";

//flag for saving data
bool shouldSaveConfig = false;

//callback notifying us of the need to save config
void saveConfigCallback () {
  Serial.println("Should save config");
  shouldSaveConfig = true;
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println();
 //read configuration from FS json
  Serial.println("mounting FS...");


  if (SPIFFS.begin()) {
    Serial.println("mounted file system");
    if (SPIFFS.exists("/config.json")) {
      //file exists, reading and loading
      Serial.println("reading config file");
      File configFile = SPIFFS.open("/config.json", "r");
      if (configFile) {
        Serial.println("opened config file");
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);
       configFile.readBytes(buf.get(), size);
 #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
        DynamicJsonDocument json(1024);
        auto deserializeError = deserializeJson(json, buf.get());
        serializeJson(json, Serial);
        if ( ! deserializeError ) {
#else
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
#endif
          Serial.println("\nparsed json");
          strcpy(bot_token, json["bot_token"]);
          strcpy(chat_id, json["chat_id"]);
          //strcpy(api_token, json["api_token"]);
        } else {
          Serial.println("failed to load json config");
        }
        configFile.close();
      }
    }
  } else {
    Serial.println("failed to mount FS");
  }
  //end read
  // The extra parameters to be configured (can be either global or just in the setup)
  // After connecting, parameter.getValue() will get you the configured value
  // id/name placeholder/prompt default length
  WiFiManagerParameter custom_bot_token("bot_token", "Bot token from BotFather", bot_token, 100);
  WiFiManagerParameter custom_chat_id("chat_id", "Your chat ID from IDBot", chat_id, 16);
  //WiFiManagerParameter custom_api_token("apikey", "API token", api_token, 32);

  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;

  //set config save notify callback
  wifiManager.setSaveConfigCallback(saveConfigCallback);

  //set static ip
  //wifiManager.setSTAStaticIPConfig(IPAddress(10, 0, 1, 99), IPAddress(10, 0, 1, 1), IPAddress(255, 255, 255, 0));

  //add all your parameters here
  wifiManager.addParameter(&custom_bot_token);
  wifiManager.addParameter(&custom_chat_id);
  
  if (!wifiManager.autoConnect("AutoConnectAP", "password")) {
    Serial.println("failed to connect and hit timeout");
    delay(3000);
    //reset and try again, or maybe put it to deep sleep
    ESP.restart();
    delay(5000);
  }

  //if you get here you have connected to the WiFi
  Serial.println("connected...yeey :)");

  //read updated parameters
  strcpy(bot_token, custom_bot_token.getValue());
  strcpy(chat_id, custom_chat_id.getValue());
  //strcpy(api_token, custom_api_token.getValue());
  Serial.println("The values in the file are: ");
  Serial.println("\bot_token : " + String(bot_token));
  Serial.println("\chat_id : " + String(chat_id));
  //Serial.println("\tapi_token : " + String(api_token));


  //save the custom parameters to FS
  if (shouldSaveConfig) {
    Serial.println("saving config");
 #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
    DynamicJsonDocument json(1024);
#else
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
#endif
    json["bot_token"] = bot_token;
    json["chat_id"] = chat_id;
    //json["api_token"] = api_token;


    File configFile = SPIFFS.open("/config.json", "w");
    if (!configFile) {
      Serial.println("failed to open config file for writing");
    }
#if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
    serializeJson(json, Serial);
    serializeJson(json, configFile);
#else
    json.printTo(Serial);
    json.printTo(configFile);
#endif
    configFile.close();
    //end save
  }
  myBot.setTelegramToken(bot_token);

  // check if all things are ok
  if (myBot.testConnection())
    Serial.println("\ntestConnection OK");
  else
    Serial.println("\ntestConnection NOK");
  servo.attach(D1);
  servo.write(180);
  delay(1000);
 
  myBot.sendMessage(atoi(chat_id), "Till i get my satisfaction /PUSH_ME");
  Serial.println("local ip");
  Serial.println(WiFi.localIP());
}

void loop() {
  // a variable to store telegram message data
  TBMessage msg;
   //myBot.sendMessage(msg.sender.id, msg.sender.id.text);
  // if there is an incoming message...
  
    if (CTBotMessageText == myBot.getNewMessage(msg)) {
      if (msg.sender.id==atoi(chat_id)){
        if (msg.text.equalsIgnoreCase("/PUSH_ME")) {              // if the received message is "PUSH_ME"...
          servo.write(0);
          delay(1000);
          servo.write(180);
          delay(1000);                           
          myBot.sendMessage(msg.sender.id, "Push me and then just touch me Till I can get my satisfaction");  // notify the sender
        }
      
      }
    }
  // wait 500 milliseconds
  delay(500);
}


You can see that the code is not much different from the examples that we already saw in the previous step. The only difference is that we are using other properties that we want to save and one additional library "Servo.h" to control the motor.

After you add the code to your Arduino IDE compile it to ensure there are no errors. After ensuring there are no errors you can proceed to upload it to your controller.

Step 8: Enclosure

For the enclosure, we will use the same approach as we used for the software. What I mean by this is as follows:

  • We need to find two enclosures one for the servo and one for the board.
  • Check the licenses that those 3d models use and if we can reuse them in the way we need
  • Merge them together using your favorite software in my case ThinkerCad

OK, after long searching I think that I found the needed model to merge:

  1. For the controller box, we can use - https://www.printables.com/model/111038-d1-mini-v3-snapcase-low-profile-with-wifi-logo go and check other models made by Kevin
  2. Unfortunately, I was unable to find anything that can fit my needs so grab your calipers and measure everything we will design our own servo mount

Step 9: First Steps in Designing Our Servo Mount

The first step is to try to find some measurements online if we are lucky we don't have to search where we have left our calipers. In this case, we have some luck there is an image with the dimensions of our servo.

As a first step, we need to make the base a little bit wider to fit the enclosure that we are going to use let's say we will make it 33 mm.

Now the sides should be as tall as the measurement is showing plus the height of the base.

Shoulders should be the same dimensions as messured.

Step 10: Merge the 3d Models

We are on the final stretch here. All we need to do is to merge both models together in ThinkerCad and print them.



Step 11: Hardware Assembly

So this is assembled button pusher. You may notice that there is an additional servo hand that I am using and it is not described here. Yes, it is not described because I am still testing it and I am not sure if this hand will do the job.

Step 12: Software Configuration

It is time to plug it in and see what is going to happen.

On the first run when you open your phone in the WiFi setting you will find an access point with the name "AutoConnectAP" with the password "password". This configuration can be seen in the code.

After connecting to this access point navigate in the browser to 192.168.4.1.

  1. Choose "Configure Wifi"
  2. On this screen, you will see options to :
  3. Select SSID
  4. Enter the password for the selected SSID
  5. Bot token from BotFater (check STEP 4)
  6. Id token from IDBot (check STEP 5)

Now you can click on the save button.

Step 13: Demo

Now we are finally ready to see our bot in action.

What we need to do is to send the appropriate command from our user. And what better name for command than "/PUSH_ME".