Closed szabota closed 2 years ago
к сожалению ничем не могу помочь) бот использует стандартную SecureBearSSL либу и делает обычный GET запрос
И отдельно сам по себе бот норм работает
И отдельно сам по себе бот норм работает
С этим не поспоришь)
Понял
можно попробовать зарулить в код с EmbUI вот эти либы и создать экземпляр
#include <WiFiClientSecure.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecureBearSSL.h>
static std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
static HTTPClient https;
И сделать запрос
client->setInsecure();
https.begin(*client, (String)"https://api.telegram.org/")
https.GET();
https.end();
В общем, сейчас заметил, что при первом же вызове bot.tick(), сжирается сразу 20кб памяти. И вылеты с ошибкой связаны как раз с нехваткой памяти.
Обработчик сообщений пустой - просто Serail.print(msg.text)
В чате много сообщений непрочитанных ботом? В библиотеке можно настроить лимит по количеству сообщений и количеству символов, которые бот вытащит из чата
Да нет, в чате несколько тестовых сообщений
Ну вот попробовать сделать чистый гет запрос по токену и посмотреть что будет. Может библиотека ссл клиента выделяет себе буфер под страницу
Да, так и есть, память жрёт https.GET()
Можно попробовать открыть либу, мб там есть настройки размера буфера
В общем, если объявление std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure)
(без static) перенести в функции tickManual
и sendRequest
(и ещё туда же нужно перенести и client->setInsecure()
из инициализации), то тогда "умный указатель" std::unique_ptr
начнёт работать как положено и освобождать память.
Вот тестовый скетч для проверки:
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecureBearSSL.h>
#define BAUD_RATE 74880
#define SSID "SSID"
#define PASS "12345678"
#define URL "https://api.telegram.org/bot1234567890:12345678901234567890123456789012345/getUpdates?limit=10&offset=0"
static HTTPClient https;
void logVar(const String &sender, const String &varName, long long varVal){
Serial.println(sender + F("(): ") + varName + F(" = ") + String(varVal));
}
void logVar(const String &sender, const String &varName, const String &varVal){
Serial.println(sender + F("(): ") + varName + F(" = ") + varVal);
}
bool isTimeHasCome(unsigned long seconds, unsigned long &prevMillis){
bool it_is = millis() >= (prevMillis + seconds*1000);
if ( it_is ) prevMillis = millis();
return( it_is );
}
void TestGet(){
int err;
std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
client->setInsecure();
String url = URL;
logVar(F("TestGetBegin"), F("FreeHeap"), ESP.getFreeHeap());
logVar(F("TestGet"), F("url"), url);
https.begin(*client, url);
err=https.GET();
if (err == HTTP_CODE_OK || err == HTTP_CODE_MOVED_PERMANENTLY) {
String payload = https.getString();
logVar(F("TestGet"), F("payload"), payload);
}
else logVar(F("TestGet"), F("https error:"), err);
https.end();
logVar(F("TestGetEnd"), F("FreeHeap"), ESP.getFreeHeap());
}
void connect(){
Serial.print(F("Connecting to WiFi"));
WiFi.begin(F(SSID), F(PASS));
while (WiFi.status() != WL_CONNECTED) {
delay(500); Serial.print(".");
if (millis() > 15000) ESP.restart();
}
Serial.println(F("Connected!"));
}
void setup() {
Serial.begin(BAUD_RATE);
connect();
}
void loop() {
static unsigned long tmr1=0;
static unsigned long tmr2=0;
if (isTimeHasCome(1, tmr1)) logVar(F("loop"), F("FreeHeap"), ESP.getFreeHeap());
if (isTimeHasCome(5, tmr2)) TestGet();
}
звучит логично. Но не будет ли слишком затратно на каждом тике всё это загружать и выгружать? Так то можно и переделать!
Тогда нужен выбор режима - для нагруженных систем "скоростной" либо для экономии памяти "тормозной"
А вообще, бот тикает раз в секунду и есть ли большой смысл занимать почти половину оперативки такое длительное время вообще без всякой на то нужды ?
ну, я не знал что эта библа столько сожрёт если честно
так не пойдёт, если отправлять реквест внутри тика (как в примере EchoBot, отправка сообщения из обработчика) - отправка не работает, а также создаются сразу два WiFiClientSecure. Я сейчас попробовал сделать так:
Создаём BearSSL::WiFiClientSecure *client;
и bool clientExist = false;
как члены класса
И дальше вот такая конструкция для tickManual и sendRequest, которая позволяет им обоим работать с одним и тем же указателем и удалять его в любом случае.
uint8_t tickManual() {
uint8_t status = 1;
clientExist = true;
client = new BearSSL::WiFiClientSecure();
client->setInsecure();
if (https.begin(*client, (String)_FB_host + _token + "/getUpdates?limit=" + _limit + "&offset=" + ID)) {
if (https.GET() == HTTP_CODE_OK) status = parse(https.getString());
else status = 3;
https.end();
} else status = 4;
delete client;
clientExist = false;
return status;
}
uint8_t sendRequest(String& req) {
uint8_t status = 1;
if (!clientExist) {
client = new BearSSL::WiFiClientSecure();
client->setInsecure();
}
if (https.begin(*client, req)) {
if (https.GET() != HTTP_CODE_OK) status = 3;
https.end();
} else status = 4;
if (!clientExist) delete client;
return status;
}
Всё работает. Можно так сделать?
Потестировал и так, и эдак, и стало понятно, что это всё безполезно. Дело в том, что запрос https.GET() отрабатывает в среднем 1200 миллисекунд и всё это время занято больше половины оперативки. И не получается отрисовать вэб-нитерфейс с такими затратами ресурсов - постоянно крашится с ошибкой OOM. Кстати, пока это всё тестировал, заметил, что нет смысла тикать ботом чаще, чем раз в 4000мс., не позволяет сервер - там похоже ограничение где-то секунд 5 между ответами.
Не, где то ошибка. К серверу можно стучаться раз 5 в секунду, я обрабатывал почти сотню сообщений в секунду за 4 запроса во время эксперимента
Всё-таки есть возможность управлять размером буфера: client->setBufferSizes(512, 512); (перед https.begin) Судя по тестам экономия памяти довольно существенная!
Вот с этими правками всё работает отлично и памяти жрет примерно в три раза меньше:
В функции tickManual:
#ifdef FB_DYNAMIC_HTTP
clientExist = true;
client = new BearSSL::WiFiClientSecure();
client->setBufferSizes(512, 512);
client->setInsecure();
#endif
и в функции sendRequest:
#ifdef FB_DYNAMIC_HTTP
if (!clientExist) {
client = new BearSSL::WiFiClientSecure();
client->setBufferSizes(512, 512);
client->setInsecure();
}
#endif
добавил возможность задать размер буфера в дефайне, появится в v1.7
так погоди, динамический режим вообще нужен? По моим тестам создание нового WiFiClientSecure занимает 1 секунду, это дофига. В то же время объект сожрёт минимум 1 кб рамы, так как там 2x512 буфер минимум. Но его можно сделать общим на всю библиотеку, мало ли несколько ботов надо. В то же время оперативки на борту 50 килобайт
убрал крч
Прикрепил бота к проекту, который использует EmbUI. Всё хорошо работает, до момента обращения к web-интерфейсу из браузера - сразу вылетает сабж. Отключаю бота - проект работает.