esp32: wifi + bluetooth classic

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Есть работающий проект на ESP-WROOM-32 (ESP32 DEVKITV1). Устройство успешно соединяется с нейрогарнитурой через библиотеку BluetoothSerial (1.6.0), получает и обрабатывает от неё пакеты, реализует обратную связь с пользователем. Всё работает прекрасно. Но когда я решил прикрутить библиотеку WifiManager чтобы поднять телеграм-бота, выяснилось, что wifi падает сразу после создания объекта BluetoothSerial.

Это я где-то туплю, или можно как-то сделать без привлечения дополнительных модулей, вроде HC-05, HC-06?

-NMi-
Offline
Зарегистрирован: 20.08.2018

ПатамушО у блюпупа приоритет, йоптэ!

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Про это я где-то читал. А как лечить то? Вроде как-то в menuconfig можно выбрать настройки совместного использования, это поможет? И как воспользоваться menuconfig-ом, я просто только arduino-ide пока что пользовался...

P.S.: Имелась в виду версия прошивки, в которую входит BluetoothSerial - 1.0.6, опечатался.

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

О, попробую заюзать esp_coex_preference_set(SW_COEXIST_PREFERENCE_BALANCE) ...

-NMi-
Offline
Зарегистрирован: 20.08.2018

Пробуй, пробуй и ещё раз пробуй. Нам только отпиши, чодакак.

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Попробовал esp_coex_preference_set(ESP_COEX_PREFER_BALANCE);. Ноль реакции. Вот как это выглядит в логах, сразу после создания объекта BluetoothSerial вылазит "WIFI_REASON:  200":

E (64) psram: PSRAM ID read error: 0xffffffff
*wm:[1] AutoConnect
*wm:[2] ESP32 event handler enabled
*wm:[2] Setting Hostnames:  nfm
*wm:[2] Setting WiFi hostname
*wm:[2] Connecting as wifi client...
*wm:[2] setSTAConfig static ip not set, skipping
*wm:[1] Connecting to SAVED AP: zenbooster
*wm:[1] connectTimeout not set, ESP waitForConnectResult...
*wm:[2] Connection result: WL_CONNECTED
*wm:[1] AutoConnect: SUCCESS
*wm:[2] Connected in 2927 ms
*wm:[1] STA IP Address: 192.168.1.134
The device started in master mode, make sure remote BT device is on!
*wm:[2] [EVENT] WIFI_REASON:  200
*wm:[2] [EVENT] WIFI_REASON:  200
*wm:[2] [EVENT] WIFI_REASON:  200
*wm:[2] [EVENT] WIFI_REASON:  201
*wm:[2] [EVENT] WIFI_REASON: NO_AP_FOUND
*wm:[2] [EVENT] WIFI_REASON:  201
*wm:[2] [EVENT] WIFI_REASON: NO_AP_FOUND
*wm:[2] [EVENT] WIFI_REASON:  201
*wm:[2] [EVENT] WIFI_REASON: NO_AP_FOUND
*wm:[2] [EVENT] WIFI_REASON:  201
*wm:[2] [EVENT] WIFI_REASON: NO_AP_FOUND

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Новые данные!
Я сделал минимальный пример для воспроизведения проблемы. Если на вайфай поднять простой вэбсервер, он работает. Если поднять телеграм бота, то проблема воспроизводится. Как свет дадут - запощу )

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020
#define TEST 1 // 0 - WebServer (OK) or 1 - Telegram Bot (ERROR)
// Если закоментировать, то будет работать:
#define BT_BEGIN // SerialBT.begin(HOSTNAME, true);

#include <WiFiManager.h>
#if TEST == 0
#include <WebServer.h>
#elif TEST == 1
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>
#endif
// в менеджере плат - esp32 1.0.6
// иначе не будет работать в мастер моде:
#include <BluetoothSerial.h>
#define LED_BUILTIN 2

#define HOSTNAME "test"
#define WIFI_SSID HOSTNAME
#define WIFI_PASS "password"

BluetoothSerial SerialBT;
uint8_t address[6] = {0x20, 0x21, 0x04, 0x08, 0x39, 0x93};
bool connected = false;

WiFiManager wifiManager;
#if TEST == 0
WebServer webServer(80);
String responseHTML = "<!DOCTYPE html><html>"
                      "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
                      "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}"
                      "</style></head>"
                      "<body><h1>ESP32 Web Server</h1>"
                      "<p>Hello World</p>"
                      "</body></html>";
#elif TEST == 1
#define BOT_TOKEN "..."
#define CHAT_ID "..."
const unsigned long BOT_MTBS = 250; // mean time between scan messages

WiFiClientSecure secured_client;
UniversalTelegramBot tgb(BOT_TOKEN, secured_client);
unsigned long tgb_lasttime; // last time messages' scan has been done

void tgb_handle_new_messages(int numNewMessages)
{
  Serial.print("handleNewMessages ");
  Serial.println(numNewMessages);
  for (int i = 0; i < numNewMessages; i++)
  {
    String chat_id = String(tgb.messages[i].chat_id);
    if (chat_id != CHAT_ID )
    {
      tgb.sendMessage(chat_id, "Unauthorized user", "");
    }
    else
    {
      String text = tgb.messages[i].text;
      String from_name = tgb.messages[i].from_name;
      if (from_name == "")
        from_name = "Guest";
      if (text == "/cmd")
      {
        tgb.sendMessage(chat_id, "under construction", "");
      }
      if (text == "/status")
      {
        tgb.sendMessage(chat_id, "under construction", "");
      }
      if (text == "/start")
      {
        String welcome = "Welcome to telegram bot, " + from_name + ".\n";
        welcome += "/cmd : to select meditation treshold\n";
        welcome += "/status : Returns current status\n";
        tgb.sendMessage(chat_id, welcome, "Markdown");
      }
    }
  }
}
#endif

void setup()
{
  Serial.begin(115200);
  wifiManager.setHostname(HOSTNAME);
  wifiManager.autoConnect(WIFI_SSID, WIFI_PASS);

#if TEST == 0
  webServer.onNotFound([]() {
    webServer.send(200, "text/html", responseHTML);
  });
  webServer.begin();
#elif TEST == 1
  secured_client.setCACert(TELEGRAM_CERTIFICATE_ROOT); // Add root certificate for api.telegram.org
  
  //p_tgb = new UniversalTelegramBot(BOT_TOKEN, secured_client);
  tgb_lasttime = 0;
#endif

#ifdef BT_BEGIN
  SerialBT.begin(HOSTNAME, true);
#endif
  Serial.println(F("The device started in master mode, make sure remote BT device is on!"));
}

void loop()
{
#if TEST == 0
  webServer.handleClient();
#elif TEST == 1
  if (millis() - tgb_lasttime > BOT_MTBS)
  {
    int numNewMessages = tgb.getUpdates(tgb.last_message_received + 1);
    while (numNewMessages)
    {
      Serial.println("got response");
      tgb_handle_new_messages(numNewMessages);
      numNewMessages = tgb.getUpdates(tgb.last_message_received + 1);
    }
    tgb_lasttime = millis();
  }
#endif
  // воспроизводится и без этого:
  /*if (SerialBT.available())
  {
    digitalWrite(LED_BUILTIN, HIGH);
    // ...
  }
  else
  if (!SerialBT.connected())
  {
    digitalWrite(LED_BUILTIN, LOW);

    connected = SerialBT.connect(address);
    
    if(connected) {
      Serial.println("Connected Succesfully!");
    } else {
        Serial.println(F("Failed to connect. Make sure remote device is available and in range."));
    }
  }*/
}

 

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

BOT_TOKEN и CHAT_ID надо только заполнить...

Т.к. успевает вызваться только

String UniversalTelegramBot::sendGetToTelegram(const String& command) {
  String body, headers;
  
  // Connect with api.telegram.org if not already connected
  if (!client->connected()) {
    #ifdef TELEGRAM_DEBUG  
        Serial.println(F("[BOT]Connecting to server"));
    #endif
    if (!client->connect(TELEGRAM_HOST, TELEGRAM_SSL_PORT)) {
      #ifdef TELEGRAM_DEBUG  
        Serial.println(F("[BOT]Conection error"));
      #endif
    }
  }
  if (client->connected()) {

    #ifdef TELEGRAM_DEBUG  
        Serial.println("sending: " + command);
    #endif  

    client->print(F("GET /"));
    client->print(command);
    client->println(F(" HTTP/1.1"));
    client->println(F("Host:" TELEGRAM_HOST));
    client->println(F("Accept: application/json"));
    client->println(F("Cache-Control: no-cache"));
    client->println();

    readHTTPAnswer(body, headers);
  }

  return body;
}

 - то видимо, дело в WiFiClientSecure ...

-NMi-
Offline
Зарегистрирован: 20.08.2018

Продолжай докладывать обстаканофку!!!

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

-NMi- пишет:

Продолжай докладывать обстаканофку!!!

А всё, приплыли. Помогайте ) Подозреваю (судя по прочитанному на форумах, коментах, стэкэксченчжах, не помню где...), что проблема решена в прошивке версии 2.0+, но на этих прошивках только блютус-slave работает, а мастер - нет.

vrd
Offline
Зарегистрирован: 20.01.2022

Самое главное - модуль к вифи подключается нормально?

Если ДА - ищите ветку от paha с ботом. Я от-туда сериал под телеграмм делал. Около года тестовый модуль работает без сбоев.

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

В том то и дело, что от вифи отваливается...

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Цитата:
ищите ветку от paha с ботом.

Чего то не нашёл...

vrd
Offline
Зарегистрирован: 20.01.2022
vrd
Offline
Зарегистрирован: 20.01.2022

Перед командой боту - вифи.бегин.

Как вариант перед работой вифи выключить блютуз, а после включить.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

надо подписаться пожалуй

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Попробовал. Post - метод возвращает -1, если попробовать отправить сообщение от имени бота после вызова BluetoothSerial::begin. Если отправлять до этого вызова - работает.

vrd пишет:

Перед командой боту - вифи.бегин.

Как вариант перед работой вифи выключить блютуз, а после включить.

По поводу вифи.бегин - я пробовал инициализировать вифи после инициализации синезуба - так тоже не работает.

Выключить блютуз... У меня устанавливается соединение с гарнитурой от 3 до 5 секунд, а сама гарнитура постоянно шлёт пакеты, раз в секунду. Если выключать блютуз, раз в секунду пакеты принимать не получится... 

-NMi-
Offline
Зарегистрирован: 20.08.2018

Пока всё ровно, на одну антенну одновременно не могут работать и передатчик и приемник.

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

WebServer с BlueToothSerial работает.

-NMi-
Offline
Зарегистрирован: 20.08.2018

О! Это уже радует. Тоесть можно обменяться пакетами данных одновременно и по бп и вифи?
Может можно и второй АЦП запустить с вифи???

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

-NMi- пишет:
Пока всё ровно, на одну антенну одновременно не могут работать и передатчик и приемник.

могут, в сотовом жеж работают, нужен циркулятор, он там есть (в сотовом телефоне)

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Дома ещё раз проверю, будет ли работать вэбсервер при активном обмене данными по блютуз. Пока что проблема возникает, если обмен по вайфай происходит с использованием ssl...

-NMi-
Offline
Зарегистрирован: 20.08.2018

ua6em --- ты сам то хоть понял, что сказал?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

-NMi- пишет:
ua6em --- ты сам то хоть понял, что сказал?

более чем!

-NMi-
Offline
Зарегистрирован: 20.08.2018

Ща до компа доберусь - объясню.

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Кстати, решением проблемы могла бы быть реализация класса WiFiClientSecure без использования ssl api. Надо будет прикинуть, насколько это реально...

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

zenbooster пишет:
Дома ещё раз проверю, будет ли работать вэбсервер при активном обмене данными по блютуз. Пока что проблема возникает, если обмен по вайфай происходит с использованием ssl...

Проверил. Поднял webserver, страницы загружаются, пакеты от гарнитуры приходят!

Нашел реализацию бота на SSLClient для ардуины. Попробую прикрутить...

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Оффтоп: я хочу всё собрать в герметичной коробке. Внутри плата, динамик, аккум 18650 и приемник беспроводной зарядки. Будет кнопка ещё антивандальная с подсветкой rgb. Динамик будет в нужные моменты воспроизводить белый шум, и возможно сигналы от интервального таймера. Как сделать, чтоб и динамик было слышно, и в ванну (к примеру) можно было уронить без последствий? Может динамик от старой мобилы к стенке корпуса приделать... Хотя может и обычного из бт колонки хватит...

mixail844
Offline
Зарегистрирован: 30.04.2012

zenbooster пишет:
Как сделать, чтоб и динамик было слышно, и в ванну (к примеру) можно было уронить без последствий? Может динамик от старой мобилы к стенке корпуса приделать... Хотя может и обычного из бт колонки хватит...

 

есть "вибрационные динамики" (хз как точее определить), т.е. которые крепяться к почти любой поверхности и  сама поверхность уже выводит звук. может вам такой надо прикпрепить на внутренюю сторону коробки ? 

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Думал об этом. Они могут нормально шум воспроизводить? И есть ли компактные варианты?

О, круто, вроде такого:
https://youtu.be/QCx-2N6-qLM

Или вот, ещё лучше:

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Попробовал завести AsyncTelegram2 на SSLClient через WiFiClient. Завелось на половину. В общем данные ходят в обоих направлениях, но видимо есть какой-то баг в либе бота ) Когда я делаю sendto из сетапа, сообщение доходит до телеги и я его вижу. Когда я отправляю сообщение из телеги, я его вижу только в логах. Т.е. либа его получает, пытается распарсить, но getNewMessage или как там его, говорит, что ничего не пришло. Причем сам текст, который отправлял - вижу в логах.
Главное, обходной путь найден )

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Покидал файлы отсюда:
https://github.com/cotestatnt/AsyncTelegram2/tree/master/src

в папку со скетчем, что бы сделать вывод в serial нужных значений в нужных местах. Подключил заголовки через

".\\

а оно работает! Сделал как было (удалил исходники либы из папки со скетчем), всё равно работает.

В ide установлена версия 2.1.3. При этом github говорит: "master is up to date with all commits from 2.1.3.".

Правда перед этим в ide плату ещё пришлось поменять с dev на wrover, что-то там перестало собираться...

В общем вывод такой, что проблему вызывает ssl api, которое использует WiFiClientSecure, или то, как он это api использует. А может BluetoothSerial тоже использует, но как-то не так.

А SSLClient работает! )

P.S.: На самом деле, включив отладочные сообщения, подозреваю, что WiFiClientSecure просто не хватало памяти, а SSLClient потребляет памяти меньше...

b707
Offline
Зарегистрирован: 26.05.2017

zenbooster пишет:

Думал об этом. Они могут нормально шум воспроизводить?


если вы не оговорите более четко что есть "шум" - ответ да. Ведь шум - это и отбойный мотолок и шуршшание мыши в сене. Уж какой-нибудь звук динамик произведет наверняка:)

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

b707 пишет:
если вы не оговорите более четко что есть "шум" - ответ да.

- Некое подобие белого шума, необязательно чтоб он прям строго математически был белым. Достаточно, что бы просто было шипение...

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Пришлось ещё повозиться, чтоб разнести код BT и wifi в разные потоки. Хотел сначала, чтоб вэбсервер крутился в своём потоке, а бот в своём. В итоге так не получилось, ни с AsyncWebServer ни просто с WebServer. Пробовал даже на семафорах разделять доступ к использованию wifi...

Получилось, когда завёл отдельный поток для wifi вообще. Т.е. вся инициализация происходит в основном потоке, а обработка новых подключений к вэбсерверу, проверка новых сообщений боту происходит в отдельном потоке. И это работает! ))

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Уважаю. Если не сложно, параметры потоков напиши (стек, приоритет и прочее), что бы другие себе голову не ломали.

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Код BT выполняется в основном потоке. Всё, что связано с Wifi в отдельном.

Как-то так:

// TWiFiStuff.h:
#pragma once
#include "TPrefs.h"
#include "TWebSrv.h"
#include "TTgmBot.h"

namespace WiFiStuff
{
using namespace TgmBot;
using namespace WebSrv;

class TWiFiStuff
{
  private:
    static int ref_cnt;
    TTgmBot *pTgmBot;
    TWebSrv *pWebSrv;

    static void task(void *p);

  public:
    TWiFiStuff(TPrefs& prefs);
    ~TWiFiStuff();
};
}

// TWiFiStuff.ino:
#include "TWiFiStuff.h"

namespace WiFiStuff
{
int TWiFiStuff::ref_cnt = 0;

void TWiFiStuff::task(void *p)
{
  TWiFiStuff *pthis = static_cast<TWiFiStuff *>(p);

  for(;;)
  {
    pthis->pTgmBot->run();
    pthis->pWebSrv->run();
  }
}

TWiFiStuff::TWiFiStuff(TPrefs& prefs):
  pTgmBot(NULL),
  pWebSrv(NULL)
{
  if(ref_cnt)
  {
    throw "Only one instance of TWiFiStuff allowed!";
  }
  ref_cnt++;

  pTgmBot = new TTgmBot(prefs);
  pWebSrv = new TWebSrv();

  xTaskCreatePinnedToCore(task, "TWiFiStuff::task", 15000, this,
    (tskIDLE_PRIORITY + 3), NULL, portNUM_PROCESSORS - 1);
}

TWiFiStuff::~TWiFiStuff()
{
  // сделать удаление задачи.
  --ref_cnt;
  if(pWebSrv)
    delete pWebSrv;
  if(pTgmBot)
    delete pTgmBot;
}
}

- но эпопея продолжается ) При попытке сохранить некоторое значение в NVS по команде телеграм-боту, происходит перезагрузка... Если закоментить вызов putString объекта Preferences, перезагрузки не происходит. Если из обработчика таймера убрать вызов dac_output_voltage(DAC_CHANNEL_1, val) то тоже работает. Можно конечно семафор поставить, но всё таки интересно, как одно с другим связано...

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

zenbooster пишет:

Можно конечно семафор поставить, но всё таки интересно, как одно с другим связано...

Семафор не помог. Помогло timer_pause / timer_start на время выполнения записи в NVS. При этом даже щелчков никаких не заметно в динамике.

Ещё можно будет как нибудь попробовать оставить в обработчике таймера только xSemaphoreGiveFromISR, завести ещё одну задачу, которая будет делать take для этого семафора, чтобы синхронизироваться с таймером, и выполнять dac_output_voltage(DAC_CHANNEL_1, val). А запись в NVS при этом попробовать делать с и без синхронизации с семафором.

zenbooster
zenbooster аватар
Offline
Зарегистрирован: 14.05.2020

Ура! Ура! Ура! =)

Вот, что получилось:

https://github.com/zenbooster/zenbooster

- каждый день пользуюсь, по этому будут по возможности исправляться баги, добавляться фичи. Бывало такое, что что-то добавишь, и реконнект к Bluetooth отваливается. Но всегда лечилось высвобождением дополнительной памяти, либо путём урезания размеров стека каких нибудь задач, либо отказом от каких-то фич. Например, я хотел по окончании сессии медитации выводить график функции отражающей уровень медитации либо на странице вэбсервера, либо скидывать картинку с графиком от имени телеграм бота. Но похоже на это не хватит памяти. Крайний раз всю голову сломал, почему реконнект не работает. Отключил буфер припасенный для графика, и всё заработало, т.к. стало больше свободной памяти )

Возможно, ещё прикручу MQTT для отправки данных на свой сервер. А по окончании сессии можно будет уже кидать ссылку в телеграмм на страницу сервера с графиком.

https://youtu.be/huRiIhcoKzM

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

zenbooster пишет:

Ура! Ура! Ура! =)

Вот, что получилось:

https://github.com/zenbooster/zenbooster

- каждый день пользуюсь, по этому будут по возможности исправляться баги, добавляться фичи. Бывало такое, что что-то добавишь, и реконнект к Bluetooth отваливается. Но всегда лечилось высвобождением дополнительной памяти, либо путём урезания размеров стека каких нибудь задач, либо отказом от каких-то фич. Например, я хотел по окончании сессии медитации выводить график функции отражающей уровень медитации либо на странице вэбсервера, либо скидывать картинку с графиком от имени телеграм бота. Но похоже на это не хватит памяти. Крайний раз всю голову сломал, почему реконнект не работает. Отключил буфер припасенный для графика, и всё заработало, т.к. стало больше свободной памяти )

Возможно, ещё прикручу MQTT для отправки данных на свой сервер. А по окончании сессии можно будет уже кидать ссылку в телеграмм на страницу сервера с графиком.

https://youtu.be/huRiIhcoKzM

круто конечно, но лично для себя я новый Фобос заказал, 48 каналов однако