• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Делюсь опытом. Простой Telegram Bot без библиотеки.

p-a-h-a

Member
Разбирался с телеграм ботом, захотелось написать на POST запросах.
Реализовано - чтение id чата с последним написавшем юзером, определение его имени, отправка сообщения.
Пример использования - датчик открытия двери. Дверь открылась - единственный пользователь получил сообщение.
Для работы необходимо в телеграмме написать боту @botfather /start и создать своего бота, присвоить ему логин и имя.
В ответ получите сообщение вида
По ссылке t.me/ESP_Flo_bot переходите в чат с новоиспеченным ботом и пишете ему /start
Берем свой токен типа 1654645764490:AAH01HEDQguTZp-GjhGjhgjhgivkjbngc и вставляем в скеч
C++:
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
char TelegrammBOTtoken[50] = "165476586:AAH01HfdhytjhmDSHnpZuUoi6YwLzqaIpVPs";// https://api.telegram.org/bot165476586:AAH01HfdhytjhmDSHnpZuUoi6YwLzqaIpVPs/getMe
String Messange = "Сообщение !";

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);//
  WiFi.begin("ssid", "pass");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
   Serial.println();
   Serial.println("UserName: " + UserName());
   Serial.println("chatid: " + String(chatid()));
   SendMessage(chatid(), UserName() + Messange); // Тут отправляем сообщение.  chatid() необходимо запомнить и вставлять сюда uint32_t вместо chatid() что значительно ускорит работу. UserName() тоже нужно заранее запоминать если будем обращаться по имени.
  ESP.deepSleep(0, RF_NO_CAL);
}

void loop() {}

String telegramPOST(String jsonarr) {
  std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
  client->setInsecure();
  HTTPClient https;
  String token = TelegrammBOTtoken;
  if (https.begin(*client, "https://api.telegram.org/bot" + token + "/")) { // HTTPS
    https.addHeader("Content-Type", "application/json");
    https.POST(jsonarr);
    String Answer = https.getString();
    //Serial.println(Answer);
    https.end();
    return Answer;
  }
}

uint32_t chatid() {
  StaticJsonDocument<30> doc; // Создаем и наполняем json для последующей отправки на сервер
  doc["method"] = "getUpdates";
  doc["offset"] = -1; //Для проверки последнего сообщения
  DynamicJsonDocument Answer(1532);
  deserializeJson(Answer, telegramPOST(doc.as<String>()));                     // Парсим JSON-содержимое ответа сервера
  //   serializeJsonPretty(Answer, Serial);    Serial.println();
  return Answer["result"][0]["message"]["chat"]["id"];//id
}

String UserName() {
  StaticJsonDocument<30> doc; // Создаем и наполняем json для последующей отправки на сервер
  doc["method"] = "getUpdates";
  doc["offset"] = -1; //Для проверки последнего сообщения
  DynamicJsonDocument Answer(1532);
  deserializeJson(Answer, telegramPOST(doc.as<String>()));                     // Парсим JSON-содержимое ответа сервера
  //const char* first_name = Answer["result"][0]["message"]["from"]["first_name"];
  return Answer["result"][0]["message"]["from"]["first_name"];//id
}

void SendMessage(uint32_t chatid, String message) {
  StaticJsonDocument<100> doc; // Создаем и наполняем json для последующей отправки на сервер
  doc["method"] = "sendMessage";
  doc["chat_id"] = chatid;
  doc["text"] = message;
  DynamicJsonDocument Answer(1532);
  deserializeJson(Answer, telegramPOST(doc.as<String>()));                     // Парсим JSON-содержимое ответа сервера
  analogWrite(LED_BUILTIN, 0);
}
1612292164293.png
 

vrd

Member
А без бота можно использовать этот код как сериал, для отладки?
 

p-a-h-a

Member
А без бота можно использовать этот код как сериал, для отладки?
Можно. С ботом. Один раз узнайте chatid отправив /start боту и получив id из функции chatid(), и потом достаточно одной функции SendMessage(id_что_узнали, "Отладочное сообщение");
Но бота заранее создать нужно написав в телеграмме боту @botfather
 

p-a-h-a

Member
А без бота можно использовать этот код как сериал, для отладки?
Хорошая идея. Реализовал ее по минимуму сейчас. Все что нужно - зарегистрировать бота и получить api ключ. Написать новоиспеченному боту /start и из прошлого кода узнать chat_id. Далее отправку debug сообщений можно делать из 8 строчек кода функцией TelegramPrint.
Код:
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <ESP8266HTTPClient.h>

void setup() {
  WiFi.mode(WIFI_STA);//
  WiFi.begin("ssid", "pass");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  TelegramPrint("Отладочное сообщение");

  ESP.deepSleep(0, RF_NO_CAL);//спим
}

void loop() {}

void TelegramPrint(String message) {
  std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
  client->setInsecure();
  HTTPClient https;
  if (https.begin(*client, "https://api.telegram.org/bot1663160000:AAH01GFDQguCVp-dnpZuUoi6YwLqwaIpVPs/")) { // Тут свой ключ пишем
    https.addHeader("Content-Type", "application/json");
    https.POST("{\"method\":\"sendMessage\",\"chat_id\":400064015,\"text\":\"" + message + "\"}"); // Тут свой чат id пишем
    https.end();
  }
}
Естественно chat_id, apiKEY должны быть свои. Тут нерабочие данные.
 

vrd

Member
Немного не понял - куда переменные прописывать? Или по умолчанию из сериала подхватывает?
 

p-a-h-a

Member
Переменные прописывать вместо текста "Отладочное сообщение" в виде
Int x = 5;
TelegramPrint(String(x));
А лучше в строку все сложить и за один раз отправить.
String debagStr;
debagStr = "Отладочное сообщение: \n";
Int x = 5;
debagStr += "X=";
debagStr += String(x);
debagStr += "\n Конец сообщения;
TelegramPrint(debagStr);
Каждая отправка занимает время. У меня от секунды до 4х секунд примерно.
 

vrd

Member
Отправка каждого сообщения или пакета из например 10 переменных выходит до 4-х секунд?
Если я возьму три основные переменные (x, y, z) и буду их одной строкой слать в бота каждый цикл (4,5 секунды), это выйдет почти 10-16 секунд на цикл?
 

p-a-h-a

Member
Отправка каждого сообщения или пакета из например 10 переменных выходит до 4-х секунд?
Если я возьму три основные переменные (x, y, z) и буду их одной строкой слать в бота каждый цикл (4,5 секунды), это выйдет почти 10-16 секунд на цикл?
Одно сообщение это и есть пакет из множества символов. Строку собрали за 1-4 секунды ее отправили. Серверы телеграмм долго ответ на запрос посылают. Наверное связано с нагрузкой на серверы. Ботов много развелось нынче.
 

vrd

Member
Спасибо за помощь. Смог оптимизировать отправку сообщений под изменение переменных.

if ( sum != (x + y + z + (int(t / mi)) + (int(check / mi)))) {
mess = " Озонатор ";
mess += " \n";
mess += " X=";
mess += String(x);
mess += " Y=";
mess += String( y );
mess += " Z=";
mess += String(z);
mess += " F=";
mess += String(f);
mess += " \n";
mess += " Срабатывание таймера через-";
mess += String(int((t - (millis() - ti)) / mi));
mess += " мин";
mess += " \n";
mess += " Проверка времени через-";
mess += String(int((check - (millis() - t1)) / mi));
mess += " мин";
mess += " \n";
mess += " ";
mess += " \n";
delay(2);
TelegramPrint(mess);
delay(2);
sum = x + y + z + (int(t / mi)) + (int(check / mi));
delay(2);
mess = " ";
delay(2);
}

Появилась "хотелка" - считывать сообщения пользователя.
 

vrd

Member
Ботов много развелось нынче.
{"ok":true,"result":[{"update_id":74664327,
"message":{"message_id":646,"from":{"id":668666666,"is_bot":false,"first_name":"A","last_name":"Z","language_code":"ru"},"chat":{"id":668666666,"first_name":"A","last_name":"Z","type":"private"},"date":1625078161,"text":"Ozon reb"}}]}
Подскажите как выдрать ("text":"Ozon reb") из этого набора символов.
 

Виктор

New member
А как прочитать сообщение? Чтоб не только уведомления Бот отсылал, но и читать мог для дальнейшего анализа.
 

Сергей_Ф

Moderator
Команда форума

vrd

Member
Доработал таки этот код:
#include <ESP8266mDNS.h>
#include <WiFiClientSecure.h>
#include <ArduinoOTA.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
#include "secrets.h"
int f = 0;
uint32_t t1 = 0;
int check = 0;
String mess;
int sum = 1;
const int h = 3600000;//час
const int mi = 60000;//минута
const int sek = 1000;//секунда
String doc;

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
mess = " Test ";
mess += " \n";
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { //Соеденение с WIFI
delay(100);
digitalWrite(LED_BUILTIN, HIGH);
delay(200);
digitalWrite(LED_BUILTIN, LOW);
delay(100);
f++;
Serial.print(" f=");
Serial.print(f);
if (f >= 999) { //Нет WIFI
Serial.flush();
delay(1000);
SPIFFS.end();
delay(1000);
ESP.restart();
}
Serial.println(" ");
}
mess += "Количество попыток подключения ";
mess += String(f);
mess += " \n";
f = 0;

// Port defaults to 8266
ArduinoOTA.setPort(8266);

// Hostname defaults to esp8266-[ChipID]
ArduinoOTA.setHostname("Test"); //Для каждой платы своё имя

// No authentication by default
ArduinoOTA.setPassword("z"); //Ставим свой пароль - можно закоментить

// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_FS
type = "filesystem";
}

// NOTE: if updating FS this would be the place to unmount FS using FS.end()
//Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
//Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
//Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
mess += "Ошибка ";
//Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
//Serial.println("Auth Failed");
mess += "Auth Failed ";
} else if (error == OTA_BEGIN_ERROR) {
//Serial.println("Begin Failed");
mess += "Begin Failed ";
} else if (error == OTA_CONNECT_ERROR) {
//Serial.println("Connect Failed");
mess += "Connect Failed ";
} else if (error == OTA_RECEIVE_ERROR) {
//Serial.println("Receive Failed");
mess += "Receive Failed ";
} else if (error == OTA_END_ERROR) {
//Serial.println("End Failed");
mess += "End Failed ";
}
mess += " \n";
});
ArduinoOTA.begin();
//Serial.println("Ready");
mess += "Ready ";
//Serial.print("IP address: ");
//Serial.println(WiFi.localIP());
mess += " \n";
}

void loop() {
digitalWrite(LED_BUILTIN, HIGH);
ArduinoOTA.handle();
if ( sum != ( f + check )) {
mess += " F=";
mess += String(f);
mess += " \n";
mess += " ";
mess += " \n";
doc = "";
delay(0);
TelegramPrint(mess, doc);
delay(0);
sum = f + check;
delay(0);
delay(0);
mess = " Test ";
mess += " \n";
}
digitalWrite(LED_BUILTIN, LOW);
if (millis() - t1 >= check) {
//check = mi * 10;
check = h;
t1 = millis();
f++;
}
if (f >= 99) {
f = 0;
check = sek;
delay(1000);
}
doc = Text();
delay(0);
//Работайте со стрингом как хотите
//Вариант на ифах ниже
if (doc.length() >= 7) { //Test f88
if (doc.charAt(0) == 'T') {
if (doc.charAt(1) == 'e') {
if (doc.charAt(2) == 's') {
if (doc.charAt(3) == 't') {
//doc = doc.charAt(6);
doc = doc.substring(6);
f = doc.toInt();
//f = atoi( + 5);
}
}
}
}
}

}

String TelegramPrint(String mess, String doc) {
std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
client->setInsecure();
HTTPClient https;
https.begin(*client, "https://api.telegram.org/токен/" + doc); // Тут свой ключ пишем
https.addHeader("Content-Type", "application/json");
https.POST("{\"method\":\"sendMessage\",\"chat_id\":668666666 ,\"text\":\"" + mess + "\"}"); // Тут свой чат id пишем
delay(1);
String Answer = https.getString();
https.end();
delay(1);
//Serial.println(Answer);
return Answer;
}

String Text() {
delay(1);
doc = "getUpdates?offset=-1";//Для проверки последнего сообщения
delay(1);
DynamicJsonDocument Answer(1532);
deserializeJson(Answer, TelegramPrint(mess, doc), DeserializationOption::NestingLimit(5));// Парсим JSON-содержимое ответа сервера
//serializeJsonPretty(Answer, Serial); //Serial.println();
doc = "";
return Answer["result"][0]["message"]["text"];//text
}

В secrets.h спрятал повторяющиеся постоянные и дефайны.
Лежит этот файлик в "C:\Users\Admin\Documents\Arduino\libraries\Secrets" на вин10.
Присоединяется к любому скетчу как библиотека :)
 

Mоnk

Member
Прошу прощения, а BearSSL с какого ядра для ArduinoIDE появился? Мне с 2.4.2 на какую ближайшую подняться надо?
 
Сверху Снизу