Бегущая строка на NodeMCU с управлением через WiFi
Ещё один интересный и даже полезный проект на NodeMCU — бегущая строка с WiFi-управлением. Наверняка все из нас каждый день видят подобные устройства на улицах города. К примеру, бегущая строка широко применяется на транспорте для вывода номера маршрута, следующей остановки и навязчивой рекламы всякой всячины.
Типичная бегущая строка представляет собой светодиодную матрицу со схемой управления развёрткой и микроконтроллером на борту. Текст, ползущий по этой матрице, может храниться локально либо обновляться динамически через WiFi или GSM.
Разумеется, имея такую мощную платформу, как NodeMCU (или любой другой ESP8266), бегущую строку можно сделать в домашних условиях. Установить её рационально где-нибудь в публичном месте, например в школе. Через такое информационное табло будет удобно сообщать внутренние школьные новости, температуру за окном или даже фамилии отличников!
Совет
В нашей лаборатории мы установили бегущую строку в окне на первом этаже. С помощью этого IoT-устройства мы поздравляли всех прохожих с Новым Годом и Рождеством :)
Подключение светодиодной матрицы к NodeMCU
Будем работать с готовыми модулями матрицы под управлением микросхемы MAX7219.
Вкратце, у каждого такого модуля есть 10 контактов. Пять с одной стороны и столько же с другой. Это сделано для того, чтобы модули можно было соединять друг за другом в цепочку. На входе имеем:
два контакта для питания: земля
GNDи+5V;три контакта для шины SPI:
CS,DIN,CLK.
Допустим, мы хотим сделать бегущую строку из четырёх таких модулей. Берём первый модуль и подключаем его к NodeMCU согласно схеме:
Светодиодная матрица 8×8 с MAX7219 |
VCC |
GND |
DIN |
CS |
CLK |
|---|---|---|---|---|---|
NodeMCU |
+5V |
GND |
D7 |
D8 |
D5 |
Затем к первому модулю подключаем остальные три:
Внешний вид стенда:
Вместо четырёх отдельных модулей имеет смысл использовать готовую сборку, например, такую:
Программа для управления MAX7219 на NodeMCU
Попробуем запустить бегущую строку на матрицах, пока без возможности удалённо подключаться к NodeMCU. То есть бегущая строка будет крутить какой-то статичный текст.
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
Max72xxPanel matrix = Max72xxPanel(D8, 4, 1);
unsigned long ticker_next;
String tape = "Hello from AlashEd!";
int spacer = 1;
int width = 5 + spacer;
void setup(void){
matrix.setIntensity(7);
}
void handleTicker(){
for ( int i = 0 ; i < width * tape.length() + matrix.width() - 1 - spacer; i++ ) {
matrix.fillScreen(LOW);
int letter = i / width;
int x = (matrix.width() - 1) - i % width;
int y = (matrix.height() - 8) / 2;
while ( x + width - spacer >= 0 && letter >= 0 ) {
if ( letter < tape.length() ) {
matrix.drawChar(x, y, tape[letter], HIGH, LOW, 1);
}
letter--;
x -= width;
}
matrix.write();
delay(50);
}
}
void loop(void){
handleTicker();
}
Подаём питание, и на матрице начинает двигаться бегущая строка с текстом «Hello from AlashEd!»
Веб-сервер на NodeMCU для управления светодиодной матрицей
Теперь добавим в программу веб-сервер, который будет показывать одну единственную HTML-страницу с полем для ввода текста бегущей строки и с кнопкой.
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
const char* ssid = "ESP"; // запускаем WiFi точку ESP
ESP8266WebServer server(80); // запускаем сервер на порту 80
Max72xxPanel matrix = Max72xxPanel(D8, 4, 1);
unsigned long ticker_next;
String tape = "AlashEd";
int spacer = 1;
int width = 5 + spacer;
// HTML страница index.html
const char page[] =
"<html>"
"<title>Ticker control</title>"
"<body>"
"<form action=\"\" method=\"post\">"
"<input type=\"text\" name=\"text\"/>"
"<input type=\"submit\" value=\"Set text\">"
"</form>"
"</body>"
"</html>";
// функция вызывается, когда клиент жмет кнопку
void handleSubmit(){
tape = server.arg("text");
server.send(200, "text/html", page);
}
void handleRoot() {
if (server.hasArg("text")) {
handleSubmit();
}
else {
server.send(200, "text/html", page);
}
}
void setup(void){
delay(1000);
WiFi.softAP(ssid);
server.on("/", handleRoot);
server.begin();
matrix.setIntensity(7);
}
void handleTicker(){
for ( int i = 0 ; i < width * tape.length() + matrix.width() - 1 - spacer; i++ ) {
matrix.fillScreen(LOW);
int letter = i / width;
int x = (matrix.width() - 1) - i % width;
int y = (matrix.height() - 8) / 2; // центровка по вертикали
while ( x + width - spacer >= 0 && letter >= 0 ) {
if ( letter < tape.length() ) {
matrix.drawChar(x, y, tape[letter], HIGH, LOW, 1);
server.handleClient();
}
letter--;
x -= width;
}
matrix.write();
delay(50);
}
}
void loop(void){
server.handleClient();
handleTicker();
}
Загружаем программу на NodeMCU и подаём питание. По умолчанию бегущая строка будет крутить текст «AlashEd». Чтобы его изменить, необходимо подключиться к WiFi-точке «ESP» и зайти через браузер по адресу: 192.168.4.1.
В ответ появится страница с полем для ввода текста бегущей строки и кнопкой Set text. Вводим в поле новый текст, жмём кнопку и смотрим на бегущую строку!
Бегущая строка на русском языке
В текущем виде наш аппарат не поддерживает русский язык.
Предупреждение
Если попытаться ввести текст на русском, на матрице вместо букв появятся UTF-8 коды.
Чтобы это исправить, нам потребуется дополнительная функция utf2rus. Кроме этого, добавим в программу пароль для WiFi-точки.
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
const char* ssid = "ESP";
const char* pwd = "makemyday"; // пароль для WiFi точки
ESP8266WebServer server(80);
Max72xxPanel matrix = Max72xxPanel(D8, 4, 1);
unsigned long ticker_next;
String tape = "AlashEd";
int spacer = 1;
int width = 5 + spacer;
const char page[] =
"<html>"
"<meta charset=\"utf-8\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
"<title>Ticker control</title>"
"<body>"
"<form action=\"\" method=\"post\">"
"<input type=\"text\" name=\"text\"/>"
"<input type=\"submit\" value=\"Set text\">"
"</form>"
"</body>"
"</html>";
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 >= 0xC0) {
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;
}
}
} // switch
m[0] = n; target = target + String(m);
}
return target;
}
void handleSubmit(){
tape = utf8rus(server.arg("text"));
server.send(200, "text/html", page);
}
void handleRoot() {
if (server.hasArg("text")) {
handleSubmit();
} else {
server.send(200, "text/html", page);
}
}
void setup(void){
tape = utf8rus("Учим электронику с AlashEd");
delay(1000);
WiFi.softAP(ssid, pwd);
server.on("/", handleRoot);
server.begin();
matrix.setIntensity(7);
}
void handleTicker(){
for ( int i = 0 ; i < width * tape.length() + matrix.width() - 1 - spacer; i++ ) {
matrix.fillScreen(LOW);
int letter = i / width;
int x = (matrix.width() - 1) - i % width;
int y = (matrix.height() - 8) / 2;
while ( x + width - spacer >= 0 && letter >= 0 ) {
if ( letter < tape.length() ) {
matrix.drawChar(x, y, tape[letter], HIGH, LOW, 1);
server.handleClient();
}
letter--;
x -= width;
}
matrix.write();
delay(50);
}
}
void loop(void){
server.handleClient();
handleTicker();
}
Совет
Готово! Теперь устройство готово к непрерывной эксплуатации. Осталось сделать крепление для матрицы и поставить бегущую строку на видное место. Чертежи подходящего крепления можно найти на Thingiverse.