Развлекательный информер. Читаем анекдоты на ESP8266.
Хочу представить вашего вниманию простой проект развлекательного информера. Суть его работы такова. Каждую минуту он будет скачивать свежий анекдот, историю, афоризм и тому подобное с сайта РжуНеМогу.ru и выводить его на tft экран. Не смотря на кажущуюся простоту в коде проекта есть несколько интересных моментов. Это использование русского шрифта в библиотеке AdafruitGFX, которая с русским языком изначально работать не умела и постраничный вывод большого текста на экран. Так что данный проект может быть полезен и новичкам в образовательных целях.
Необходимые компоненты
Схема соединений
Соединяем все компоненты по схеме
Прошивка устройства
После сборки устройства по схеме переходим к прошивке ESP8266. Для начала вам необходимо установить в Arduino IDE ядро ESP8266 версии 3.0 и выше. Как это сделать можно узнать в статье Ядро ESP8266 для ArduinoIDE. Установка и обзор библиотек.
Установка дополнительных библиотек
Также вам необходимо скачать и установить дополнительные библиотеки, необходимые для работы программы.
- Adafruit_GFX - 1.1.13 и выше
- Adafruit_ST7735 - 1.9.3 и выше
- Adafruit_ST7789 - 1.9.3 и выше
- EncButton2 - 2.0.0 и выше
- SPI.h - входит в стандартную поставку Arduino IDE
Следующие библиотеки будут установлены автоматически вместе с ядром ESP8266 для Arduino IDE.
- ESP8266WiFi
- ESP8266HTTPClient
- WiFiClient
Русификация библиотеки Adafruit GFX
В программе для отображения текста на TFT экране используется популярная библиотека Adafruit GFX. Но, как оказалось, она не умеет работать с русским шрифтом. Добавить его несложно: нужно лишь заменить в файле glcdfont.c из Adafruit-GFX некоторые символы на русские буквы (в нужной кодировке). Готовый файл glcdfont.c с русским шрифтом находится в архиве проекта
Код программы и описание настроек
После установки всех необходимых библиотек, откройте файл из вложения в Arduino IDE, либо создайте новый проект и скопируйте в него все содержимое из блока ниже
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <<Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <EncButton2.h>>
//Инициализация дисплея
#define TFT_CS D8
#define TFT_RST D6
#define TFT_DC D1
#define BTN_PIN D0
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
//Настройки WiFi
const char* ssid = "ssid"; //Данные для подключения к WiFi
const char* password = "pass";
//настройки сервера
const String api = "http://rzhunemogu.ru/RandJSON.aspx?CType=";
byte category = 1; //категория
String dataText=""; //переменная для хранения текста анекдота
bool scrollflag=false;//флаг необходимости прокрутки
// Инициализация таймеров
// по умолчанию анекдоты обновляются раз в 60 секунд, если весь текст не влезает на экран, то следующая страница подгружается через 30 секунд. Можно период обновления выставить меньше, но сервер может забанить за слишком частые обращения
unsigned long lastTime = 0;
unsigned long scrollLastTime = 0;
unsigned long timerDelay = 60000; //время обновления анекдота 60 секунд
unsigned long scrollDelay = 30000; //время на чтение одной страницы 30 секунд
EncButton2 butt1(INPUT, BTN_PIN); //Инициализация кнопки
void setup() {
Serial.begin(115200);
// Инициализация дисплея
tft.initR(INITR_BLACKTAB); // Init ST7735S chip, black tab
tft.cp437(true);
tft.setRotation (1);
tft.fillScreen(ST77XX_BLACK);
tft.setCursor(0,0);
//Подключение к WiFi
WiFi.begin(ssid, password);
Serial.println("Connecting");
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());
dataText=getData(api,category);//вызов функции скачивания анекдота и добавление текста в строковую перемнную
datacheck(); //Проверка длины строки и в случае не обходимости разделение текста на 2 экрана
}
//Функция скачивания свежего анекдота. Входные параметра адрес API сайта и номер категории
String getData(String api, byte cat){
if(WiFi.status()== WL_CONNECTED){
WiFiClient client;
HTTPClient http;
http.begin(client,api + String(category));
int httpCode = http.GET();
Serial.println(httpCode);
String rawstring=http.getString();
http.end();
return (rawstring.substring(12,rawstring.length()-3));
}
else {
Serial.println("WiFi Disconnected");
return ("Данные не получены");
}
}
//Обработка нажатия кнопки
void btn_read(){
if (butt1.click()) {
category++;
if (category==7 || category==17) category++;
if (category==9) category=11;
if (category==19) category=1;
dataText=getData(api,category);
lastTime = millis();
datacheck();
}
}
//Вывод текста анекдота на экран
void tftprint(String showtext) {
// tft.setFont(&TimesNRCyr10pt8b); // выбор шрифта
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_GREEN,ST77XX_BLACK);
tft.setCursor(40,0);
//Отображение названия категории в соответсвии с параметром выбранным в API
String catname;
switch (category) {
case 1 : catname="Anekdots";
break;
case 2 : catname="Stories";
break;
case 3 : catname="Lyric";
break;
case 4 : catname="Aforizm";
break;
case 5 : catname="Quotes";
break;
case 6 : catname="Tosts";
break;
case 8 : catname="Status";
break;
case 11: catname="Anekdots (18+)";
break;
case 12 : catname="Stories 18+";
break;
case 13 : catname="Lyric (18+)";
break;
case 14 : catname="Aforizm";
break;
case 15 : catname="Quotes (18+)";
break;
case 16 : catname="Tosts (18+)";
break;
case 18 : catname="Status (18+)";
break;
}
tft.println(utf8rus(catname));
tft.setCursor(0,8);
//Serial.println(showtext);
tft.setTextColor(ST77XX_WHITE,ST77XX_BLACK);
//далее идет вывод текста на экран. Для русских букв выполняется перевод кодировки из UTF-8 в Windows-1251
tft.println(utf8rus(showtext));
//tft.setFont();
}
//Проверка длины строки с текстом. Если не влезает на экран, то разбиваем ее на 2 части и запускаем таймер для вывода второй части сообщения
void datacheck()
{
dataText=getData(api,category);
if (dataText.length()>429) {
tftprint(dataText.substring(0,429));
scrollLastTime=millis();
scrollflag=true;
} else tftprint(dataText);
}
//Преобразование 2 байтовой кодировки русских символов в 1 байтовую
String utf8rus(String source)
{
int i,k;
String target;
unsigned char n;
char m[2] = { '0', '\0' };
k = source.length(); i = 0;
while (i < k) {
n = source[i]; i++;
if (n >= 0xBF){
switch (n) {
case 0xD0: {
n = source[i]; i++;
if (n == 0x81) { n = 0xA8; break; }
if (n >= 0x90 && n <= 0xBF) n = n + 0x2F;
break;
}
case 0xD1: {
n = source[i]; i++;
if (n == 0x91) { n = 0xB7; break; }
if (n >= 0x80 && n <= 0x8F) n = n + 0x6F;
break;
}
}
}
m[0] = n; target = target + String(m);
}
return target;
}
void loop() {
butt1.tick();
btn_read();
if ((millis() - lastTime) > timerDelay) {
// Check WiFi connection status
datacheck();
lastTime = millis();
}
if ((millis() - scrollLastTime)> scrollDelay and scrollflag)
{
tftprint(dataText.substring(430,dataText.length()-1));
scrollflag=false;
}
}
Настройки
Перед компиляцией и прошивкой скетча в ESP8266 необходимо провести некоторые настройки
//Настройки WiFi
const char* ssid = "ssid"; //Данные для подключения к WiFi
const char* password = "pass";
//настройки сервера
const String api = "http://rzhunemogu.ru/RandJSON.aspx?CType=";
byte category = 1; //категория
ssid и pass - учетные данные для подключения к вашей сети Wifi. Замените на свои
category = 1. Категория по умолчанию. Может принимать следующие значения: 1 - Анекдот; 2 - Рассказы; 3 - Стишки; 4 - Афоризмы; 5 - Цитаты; 6 - Тосты; 8 - Статусы; 11 - Анекдот (+18); 12 - Рассказы (+18); 13 - Стишки (+18); 14 - Афоризмы (+18); 15 - Цитаты (+18); 16 - Тосты (+18); 18 - Статусы (+18);
unsigned long timerDelay = 60000; //время обновления анекдота 60 секунд
unsigned long scrollDelay = 30000; //время на чтение одной страницы 30 секунд
timerDelay = 60000. Таймер, по истечении которого будет загружен следующий анекдот. По умолчанию стоит 1 минута. Во время тестирования устанавливал на 10 секунд, но через какое-то время анекдоты переставали загружаться. Возможно внутренняя защита сервера от DDOS атак. С интервлом в 1 минуту работает стабильно
На этом настройки программы завершены и можно компилировать и прошивать ее в ESP8266
scrollDelay = 30000. Таймер прокрутки страницы. При загрузке длинных текстов, которые не помещаются на один экран, сообщение разбивается на 2 части. Сначала отображается первая часть сообщения, а при срабатывании таймера вторая. По умолчанию таймер равен 30 секунд.
Замечания по русификации библиотеки Adafruit GFX.
Для русификации библиотеки в файл glcdfont.c со стандартным шрифтом были добавлены русские буквы. Но шрифт рассчитан на однобайтовую кодировку букв, а Arduino IDE использует (для русских букв) двухбайтовую кодировку UTF-8.
Однако оказалось, что в русской кодировке UTF-8 прослеживается определенная последовательность. И она позволяет несложным путем перекодировать из UTF-8 в однобайтовую русскую кодировку Windows-1251, которая и была выбрана для замены букв.
Функция utf8rus() получает исходную строку, символы с кодами 0x00-0xBF пропускает без изменения в выходную строку, а в оставшихся кодах отбирает русские буквы и перекодирует их.
Ниже расположен код данной функции.
String utf8rus(String source)
{
int i,k;
String target;
unsigned char n;
char m[2] = { '0', '\0' };
k = source.length(); i = 0;
while (i < k) {
n = source[i]; i++;
if (n >= 0xBF){
switch (n) {
case 0xD0: {
n = source[i]; i++;
if (n == 0x81) { n = 0xA8; break; }
if (n >= 0x90 && n <= 0xBF) n = n + 0x2F;
break;
}
case 0xD1: {
n = source[i]; i++;
if (n == 0x91) { n = 0xB7; break; }
if (n >= 0x80 && n <= 0x8F) n = n + 0x6F;
break;
}
}
}
m[0] = n; target = target + String(m);
}
return target;
}
Работает все, как и было задумано, но по непонятной причине на экране не отображаются заглавные буквы Р и С. Причину этого так и не удалось выяснить.
Фото устройства и особенности управления.
Все управление устройством осуществляется одной кнопкой, при нажатии на которую меняются категория анекдотов и производится их загрузка и вывод на экран.
Проект собран на макетной плате и на данный момент имеет такой вид