CTBot
CTBot copied to clipboard
CTBot in its own thread
Thank you for all the good work on this library.
Just wanted to share a wrapper i wrote, it allows to have the CTBot playing in its own thread without disturbing the main thread. It does not wrap all the features, but only the one i am currently using, but it would be very straightforward to write the missing functions.
The thread is looping forever and stacking the received messages in a std::queue. The messages are removed from queue when getNewMessage is called from the main thread
The usage is very simple, after including CTBot.h, include ThreadedCTBot.h then replace your CTBot instance by a ThreadedCTBot. The thread is created in the setTelegramToken function, then all wrapped functions are simply locked by a mutex. Note that this should be only working on an ESP32.
Some additionnal work would be needed in order to make it bullet proof, but the concept does work.
#include <queue>
class LockGuard
{
public:
LockGuard()
{
if(!semaphore)
vSemaphoreCreateBinary(semaphore);
if(semaphore)
xSemaphoreTake(semaphore,portMAX_DELAY);
}
virtual ~LockGuard()
{
if(semaphore)
xSemaphoreGive(semaphore);
}
private:
static SemaphoreHandle_t semaphore;
};
SemaphoreHandle_t LockGuard::semaphore = nullptr;
class ThreadedCTBot
{
public:
ThreadedCTBot() {}
static ThreadedCTBot* getInstance() { return instance; }
bool wifiConnect(const String& ssid, const String& password = "")
{
LockGuard lock();
return bot.wifiConnect(ssid, password);
}
bool testConnection(void)
{
LockGuard lock();
return bot.testConnection();
}
void setTelegramToken(const String& token)
{
instance = this;
if(nullptr == BotTaskHandle)
xTaskCreatePinnedToCore(ThreadedCTBot::BotTask, "Telegram_Bot_task", 10 * 1024, nullptr, 1, &BotTaskHandle, 1);
LockGuard lock();
bot.setTelegramToken(token);
}
bool hasMessages()
{
LockGuard lock();
return !messages.empty();
}
TBMessage nextMessage()
{
LockGuard lock();
TBMessage message = std::move(messages.front());
messages.pop();
return message;
}
CTBotMessageType getNewMessage(TBMessage &message, bool blocking = false)
{
if(blocking)
{
while(!hasMessages())
{
delay(1);
}
}
if(!hasMessages())
return CTBotMessageNoData;
LockGuard lock();
message = nextMessage();
return message.messageType;
}
bool getMe(TBUser &user)
{
LockGuard lock();
return bot.getMe(user);
}
int32_t sendMessage(int64_t id, const String& message, const String& keyboard = "")
{
LockGuard lock();
return bot.sendMessage(id, message, keyboard);
}
int32_t sendMessage(int64_t id, const String& message, CTBotInlineKeyboard &keyboard)
{
LockGuard lock();
return bot.sendMessage(id, message, keyboard);
}
int32_t sendMessage(int64_t id, const String& message, CTBotReplyKeyboard &keyboard)
{
LockGuard lock();
return bot.sendMessage(id, message, keyboard);
}
private:
// Task callback
static void BotTask(void* pvParameters)
{
ThreadedCTBot* ThreadedCTBot = ThreadedCTBot::getInstance();
if(ThreadedCTBot)
ThreadedCTBot->loop();
}
void loop()
{
while(true)
{
TBMessage message;
// no lock here
CTBotMessageType messagetype = bot.getNewMessage(message, true);
if(messagetype != CTBotMessageNoData)
{
LockGuard lock();
messages.push(std::move(message));
}
}
}
private:
CTBot bot;
std::queue<TBMessage> messages;
static ThreadedCTBot* instance;
static TaskHandle_t BotTaskHandle;
};
ThreadedCTBot* ThreadedCTBot::instance = nullptr;
TaskHandle_t ThreadedCTBot::BotTaskHandle = nullptr;
and as an example, the echoBot implementation:
/*
Name: echoBot.ino
Created: 12/21/2017
Author: Stefano Ledda <[email protected]>
Description: a simple example that check for incoming messages
and reply the sender with the received message
*/
#define CTBOT_DEBUG_MODE CTBOT_DEBUG_ALL
#include "CTBot.h"
#include "ThreadedCTBot.h"
ThreadedCTBot myBot;
String ssid = "mySSID" ; // REPLACE mySSID WITH YOUR WIFI SSID
String pass = "myPassword"; // REPLACE myPassword YOUR WIFI PASSWORD, IF ANY
String token = "myToken" ; // REPLACE myToken WITH YOUR TELEGRAM BOT TOKEN
void setup() {
// initialize the Serial
Serial.begin(115200);
Serial.println("Starting TelegramBot...");
// connect the ESP8266 to the desired access point
myBot.wifiConnect(ssid, pass);
// set the telegram bot token
myBot.setTelegramToken(token);
// check if all things are ok
if (myBot.testConnection())
Serial.println("\ntestConnection OK");
else
Serial.println("\ntestConnection NOK");
}
void loop() {
// a variable to store telegram message data
TBMessage msg;
// if there is an incoming message...
if (myBot.getNewMessage(msg))
// ...forward it to the sender
myBot.sendMessage(msg.sender.id, msg.text);
/* or could be replaced by:
if (myBot.hasMessages())
{
TBMessage msg = myBot.nextMessage();
myBot.sendMessage(msg.sender.id, msg.text);
}
*/
// wait 500 milliseconds
delay(500);
}
Hello grosdax, thank you for using the library and for your suggestions, they are very useful! The only problem that I notice is the incompatibility with the ESP8266 paltform, as you pointed out. So I'm wondering if make a "wrapper, threaded" class only for the ESP32... Thanks a lot! Cheers,
Stefano