running version of TelegramBot that can show 2 lines of Text

This commit is contained in:
Marcel Walter 2025-10-04 21:34:33 +02:00
parent ce8dd2b42f
commit 49d043bd5e
12 changed files with 572 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
/.vscode/extensions.json
secrets.h

35
include/FlipDotDrv.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include <Arduino.h>
#include <Adafruit_GFX.h>
class FlipDotDrv
{
private:
static constexpr uint8_t templateFrameStart {0x02};
static constexpr uint8_t templateFrameAddressByte1 {'1'};
static constexpr uint8_t templateFrameAddressByte2 {'0'};
uint8_t FrameResolution[2] {};
uint8_t *FrameData {nullptr};
static constexpr uint8_t templateFrameEnd {0x03};
uint8_t FrameCrc[2] {};
uint32_t width;
uint32_t height;
uint8_t address;
uint8_t *buffer {nullptr};
uint16_t FrameDataLength;
uint16_t bufferLength;
GFXcanvas1 myCanvas;
public:
FlipDotDrv(uint8_t width, uint8_t height, uint8_t address);
~FlipDotDrv();
void sendRaw(const uint8_t* bmp, uint16_t length);
void printText(const char* txt, uint8_t multiplier = 1);
void printText(const char* txt, uint8_t x, uint8_t y, uint8_t multiplier = 1);
};

14
include/ITrigger.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <Arduino.h>
class ITrigger
{
public:
ITrigger(/* args */) = default;
~ITrigger() = default;
virtual void printText(String text) = 0;
virtual void printOpen() = 0;
virtual void printClose() = 0;
};

39
include/README Normal file
View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

29
include/Telegram.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <Arduino.h>
#include <UniversalTelegramBot.h>
#include <WiFiClientSecure.h>
#include "ITrigger.h"
class Telegram
{
private:
static const char *BOT_TOKEN;
static const unsigned long BOT_MTBS; // mean time between scan messages
unsigned long bot_lasttime; // last time messages' scan has been done
ITrigger &trigger;
WiFiClientSecure &secured_client;
UniversalTelegramBot bot;
void handleNewMessages(int numNewMessages);
public:
Telegram(WiFiClientSecure &sec_client, ITrigger &trig);
~Telegram();
void init();
void cyclic(unsigned long now);
};

View File

@ -0,0 +1,7 @@
#pragma once
#define TELEGRAM_BOT_TOKEN "123123123123:BBCeAAewGN_wayA123VnbqRNJL4l3dpXy2S"
#define TELEGRAM_BOT_OWNER "321123321"
#define WIFI_SSID "Wifi_SSID"
#define WIFI_PASSWORD "secretPassword"

46
lib/README Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

19
platformio.ini Normal file
View File

@ -0,0 +1,19 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =
witnessmenow/UniversalTelegramBot @ ^1.3.0
adafruit/Adafruit GFX Library @ ^1.11.11
build_flags=-DTELEGRAM_DEBUG

115
src/FlipDotDrv.cpp Normal file
View File

@ -0,0 +1,115 @@
#include "FlipDotDrv.h"
#include <Fonts/FreeSans9pt7b.h>
FlipDotDrv::FlipDotDrv(uint8_t width, uint8_t height, uint8_t address)
: myCanvas{width, height}
{
this->width = width;
this->height = height;
this->address = address;
this->FrameDataLength = (width * height / 8 * 2);
this->bufferLength = 5 + FrameDataLength + 3;
this->buffer = new uint8_t[this->bufferLength];
this->FrameData = this->buffer + 5;
pinMode(5,OUTPUT);
digitalWrite(5, LOW);
Serial2.begin(4800, SERIAL_8N1, 16, 17); // (unsigned long baud, uint32_t config = 134217756U, int8_t rxPin = (int8_t)(-1), int8_t txPin = (int8_t)(-1), bool invert = false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = (uint8_t)112U)
}
FlipDotDrv::~FlipDotDrv()
{
free(this->FrameData);
}
void FlipDotDrv::sendRaw(const uint8_t* bmp, uint16_t length)
{
char tmpBuff[3] = {0};
uint8_t chkSum = 0;
if (length != (this->width * this->height / 8))
{
Serial.print("length ");
Serial.print(length);
Serial.println(" [Bytes] of bitmap is invalid");
return;
}
this->buffer[0] = templateFrameStart;
this->buffer[1] = templateFrameAddressByte1;
this->buffer[2] = templateFrameAddressByte2 + this->address;
chkSum += this->buffer[1];
chkSum += this->buffer[2];
snprintf(tmpBuff, sizeof(tmpBuff), "%02X", (this->width * this->height / 8));
this->buffer[3] = tmpBuff[0];
this->buffer[4] = tmpBuff[1];
chkSum += this->buffer[3];
chkSum += this->buffer[4];
for (uint16_t i = 0; i < length; ++i)
{
snprintf(tmpBuff, sizeof(tmpBuff), "%02X", bmp[i]);
this->FrameData[i * 2 + 0] = tmpBuff[0];
this->FrameData[i * 2 + 1] = tmpBuff[1];
chkSum += tmpBuff[0];
chkSum += tmpBuff[1];
}
this->buffer[5 + FrameDataLength + 0] = templateFrameEnd;
chkSum += this->buffer[5 + FrameDataLength + 0];
chkSum ^= 0xff;
chkSum += 1;
snprintf(tmpBuff, sizeof(tmpBuff), "%02X", chkSum);
this->buffer[5 + FrameDataLength + 1] = tmpBuff[0];
this->buffer[5 + FrameDataLength + 2] = tmpBuff[1];
// digitalWrite(5, HIGH);
// delay(200);
Serial2.write(this->buffer, this->bufferLength);
Serial2.flush();
// delay(1000);
// digitalWrite(5, LOW);
}
void FlipDotDrv::printText(const char* txt, uint8_t multiplier)
{
printText(txt, 0, 0, multiplier);
}
void FlipDotDrv::printText(const char* txt, uint8_t x, uint8_t y, uint8_t multiplier)
{
myCanvas.fillScreen(0);
// myCanvas.setFont(&FreeSans9pt7b);
myCanvas.setFont(NULL);
myCanvas.setTextSize(multiplier);
myCanvas.setCursor(x, y);
myCanvas.print(txt);
uint8_t rawBuff[192] = {};
for (int i = 0; i < (this->width * this->height / 8); ++i)
{
rawBuff[i] = (myCanvas.getPixel(i / 2, ((i % 2) * 8) + 0) << 0|
myCanvas.getPixel(i / 2, ((i % 2) * 8) + 1) << 1|
myCanvas.getPixel(i / 2, ((i % 2) * 8) + 2) << 2|
myCanvas.getPixel(i / 2, ((i % 2) * 8) + 3) << 3|
myCanvas.getPixel(i / 2, ((i % 2) * 8) + 4) << 4|
myCanvas.getPixel(i / 2, ((i % 2) * 8) + 5) << 5|
myCanvas.getPixel(i / 2, ((i % 2) * 8) + 6) << 6|
myCanvas.getPixel(i / 2, ((i % 2) * 8) + 7) << 7 );
}
this->sendRaw(rawBuff, sizeof(rawBuff));
}

126
src/Telegram.cpp Normal file
View File

@ -0,0 +1,126 @@
#include <Telegram.h>
#include "secrets.h"
const char *Telegram::BOT_TOKEN = "7502550270:AAEoCSfZJU_wayW453AmarOMIJ7k3iqQb9Y";
const unsigned long Telegram::BOT_MTBS = 1000; // mean time between scan messages
void Telegram::handleNewMessages(int numNewMessages)
{
Serial.println("handleNewMessages");
Serial.println(String(numNewMessages));
for (int i = 0; i < numNewMessages; i++)
{
String chat_id = bot.messages[i].chat_id;
String text = bot.messages[i].text;
String from_name = bot.messages[i].from_name;
if (from_name == "")
from_name = "Guest";
if (text == "/send_test_action")
{
bot.sendChatAction(chat_id, "typing");
delay(4000);
bot.sendMessage(chat_id, "Did you see the action message?");
// You can't use own message, just choose from one of bellow
// typing for text messages
// upload_photo for photos
// record_video or upload_video for videos
// record_audio or upload_audio for audio files
// upload_document for general files
// find_location for location data
// more info here - https://core.telegram.org/bots/api#sendchataction
}
if (text == "/send_test_action2")
{
bot.sendChatAction(chat_id, "find_location");
delay(4000);
bot.sendMessage(chat_id, "Did you see the action message?");
}
if (text.startsWith("/txt "))
{
trigger.printText(text.substring(5));
}
if (text.startsWith("/open"))
{
trigger.printOpen();
}
if (text.startsWith("/close"))
{
trigger.printClose();
}
if (text == "/start")
{
String welcome = "Welcome to Universal Arduino Telegram Bot library, " + from_name + ".\n";
welcome += "This is Chat Action Bot example.\n\n";
welcome += "/send_test_action : to send test chat action message\n";
welcome += "/send_test_action2 : to send test chat action message\n";
bot.sendMessage(chat_id, welcome);
}
}
}
Telegram::Telegram(WiFiClientSecure &sec_client, ITrigger &trig)
: trigger(trig), secured_client(sec_client), bot(BOT_TOKEN, sec_client) {}
Telegram::~Telegram() {}
void Telegram::init()
{
secured_client.setInsecure();
secured_client.setCACert(TELEGRAM_CERTIFICATE_ROOT); // Add root certificate for api.telegram.org
secured_client.setHandshakeTimeout(5);
// secured_client.setTimeout(5);
Serial.println("clearing old messages");
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
while (numNewMessages)
{
for (int i = 0; i < numNewMessages; i++)
{
String chat_id = bot.messages[i].chat_id;
String text = bot.messages[i].text;
String from_name = bot.messages[i].from_name;
String txt = String("ignored '") + text + String("' from ") + from_name +String("(") + chat_id + String(")");
Serial.println(txt);
}
numNewMessages = bot.getUpdates(bot.last_message_received + 1);
}
bot.sendMessage(TELEGRAM_BOT_OWNER, "Hi I'm online.");
bot_lasttime = millis();
}
void Telegram::cyclic(unsigned long now)
{
if (now - bot_lasttime > BOT_MTBS)
{
Serial.print(bot.last_message_received);
Serial.println(" checking");
auto stat = WiFi.status();
Serial.println("WifiStat= ");
Serial.println(stat);
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
while (numNewMessages)
{
handleNewMessages(numNewMessages);
numNewMessages = bot.getUpdates(bot.last_message_received + 1);
}
bot_lasttime = now;
}
}

124
src/main.cpp Normal file
View File

@ -0,0 +1,124 @@
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "Telegram.h"
#include "ITrigger.h"
#include "FlipDotDrv.h"
#include "secrets.h"
// BearSSL::WiFiClientSecure secured_client;
WiFiClientSecure secured_client;
bool Start = false;
FlipDotDrv flip(96, 16, 1);
class myTrigger : public ITrigger
{
private:
/* data */
public:
myTrigger(/* args */) = default;
~myTrigger() = default;
void printText(String text) override
{
Serial.print("Display would show '");
Serial.print(text);
Serial.println("'");
flip.printText(text.c_str());
}
void printOpen() override
{
flip.printText("offen", 18, 0, 2);
}
void printClose() override
{
flip.printText("geschlossen", 15, 4, 1);
}
} trigg;
Telegram tele(secured_client, trigg);
uint8_t rawBuff[192] = {};
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println("Initializing Display");
flip.sendRaw(rawBuff, sizeof(rawBuff));
delay(500);
Serial.println("draw checker");
memset(rawBuff, 0xAA, 192);
for (int i = 0; i < 192; ++i)
{
if ((i % 4) < 2)
rawBuff[i] = 0xAA;
else
rawBuff[i] = 0x55;
}
flip.sendRaw(rawBuff, sizeof(rawBuff));
delay(500);
Serial.println("bigger checker");
for (int i = 0; i < 192; ++i)
{
if ((i % 8) < 4)
rawBuff[i] = 0x33;
else
rawBuff[i] = 0xCC;
}
flip.sendRaw(rawBuff, sizeof(rawBuff));
delay(500);
Serial.println("largest checker");
for (int i = 0; i < 192; ++i)
{
if ((i % 16) < 8)
rawBuff[i] = 0x0F;
else
rawBuff[i] = 0xF0;
}
flip.sendRaw(rawBuff, sizeof(rawBuff));
// attempt to connect to Wifi network:
Serial.print("Connecting to Wifi SSID ");
Serial.print(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(500);
}
Serial.print("\nWiFi connected. IP address: ");
Serial.println(WiFi.localIP());
Serial.print("Retrieving time: ");
configTime(0, 0, "pool.ntp.org"); // get UTC time via NTP
time_t now = time(nullptr);
while (now < 24 * 3600)
{
Serial.print(".");
delay(100);
now = time(nullptr);
}
Serial.println(now);
tele.init();
}
void loop()
{
unsigned long now = millis();
tele.cyclic(now);
yield();
}

11
test/README Normal file
View File

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html