Projeto: Display de matriz de LED controlado por Web com ESP8266
Este projeto utiliza o microcontrolador ESP8266 para controlar um display LED Matrix através de uma interface web, permitindo atualizações de mensagens remotas em tempo real. É ideal para painéis de informações, notificações dinâmicas ou até mesmo decorações inteligentes.
Componentes usados:
- ESP8266 (NodeMCU ou Wemos D1 Mini)
- Display de matriz de LED (normalmente com chip MAX7219)
- Fonte de alimentação de 5V para a matriz
- Fios de ligação em ponte
- Conexão Wi-Fi
Características:
- Acesse a tela por meio de um navegador da Web (servidor da Web hospedado no ESP8266)
- Interface simples e intuitiva para envio de mensagens de texto
- Texto de rolagem automática
- Atualizações de conteúdo em tempo real sem reprogramação
- Capacidade de ajustar a velocidade de rolagem e o brilho
Como funciona:
- Na inicialização, o ESP8266 se conecta a uma rede Wi-Fi pré-configurada.
- Um servidor web é iniciado no ESP8266.
- O usuário acessa o painel de controle via navegador (digitando o IP local do dispositivo).
- Uma página da web é exibida com um campo de entrada de texto e botões de controle.
- Quando uma mensagem é enviada, o texto é processado e exibido na matriz de LED, rolando da direita para a esquerda.
Características opcionais:
- Ajuste remoto de brilho e velocidade por meio de controles deslizantes
- Suporte MQTT para integração de casa inteligente ou assistente
- Modo de ponto de acesso para trabalhar sem Wi-Fi externo
- Salve a última mensagem na EEPROM/Flash para mantê-la após a reinicialização
Bibliotecas utilizadas (Arduino IDE / B4R):
- ESP8266WiFi.h
- ESP8266WebServer.h
- WiFiManager.h (opcional, para simplificar a configuração do Wi-Fi)
- LedControl.h ou MD_MAX72XX.h + MD_Parola.h para animações
B4R:
Sub Process_Globals
Public Serial1 As Serial
Public Wifi As ESP8266WiFi
Private led As LedControl
Public DINPin As Pin
Public CSpin As Pin
Public CLKPin As Pin
Public Net As Pin
Public Timer1 As Timer
Public Timerinterval As Long = 5 ' Intervalo do timer em milissegundo
Public maxDevices As Byte = 4 ' Número de matrizes de LEDs conectadas
Public scrollPos As Int ' Posição de rolagem do texto
Public buffer(400) As Byte ' Buffer para armazenar o texto
Public esp As ESP8266
Public eeprom As EEPROM
Public eeprom2 As EEPROM
Private const MAGIC_EEPROM As Byte = 213
Private sr As B4RSerializator
Private bc As ByteConverter
End Sub
Private Sub AppStart
Serial1.Initialize(115200)
Log("Iniciando ESP8266...")
Timer1.Initialize("Timer1_Tick", Timerinterval)
' Inicializa pinos para MAX7219
DINPin.Initialize(12, DINPin.MODE_OUTPUT) ' D7 (GPIO15)
CSpin.Initialize(5, CSpin.MODE_OUTPUT) ' D4 (GPIO5)
CLKPin.Initialize(14, CLKPin.MODE_OUTPUT) ' D5 (GPIO14)
Net.Initialize(4, Net.MODE_OUTPUT) ' D2 (GPIO4)
Net.DigitalWrite(True)
ConnectToNetwork(0)
'Inicializa o MAX7219
led.Initialize(DINPin.PinNumber, CLKPin.PinNumber, CSpin.PinNumber, maxDevices)
For i = 0 To maxDevices - 1
led.Shutdown(i, True) 'DESLIGA para reset total
Delay(100) 'Espera para garantir que desligou
led.Shutdown(i, False)
led.SetIntensity(i, 2) ' Ajusta brilho (0-15)
Next
scrollPos = buffer.Length - (maxDevices * 8)
End Sub
private Sub ConnectToNetwork(tag As Byte)
Dim SSID, PASS As String
Wifi.Disconnect
Dim datas() As Byte = GetStoredData
Dim ObjectsBuffer(16) As Object
Dim Objects() As Object = sr.ConvertBytesToArray(datas, ObjectsBuffer)
SSID = bc.StringFromBytes(Objects(0))
PASS = bc.StringFromBytes(Objects(1))
Log("Trying to connect to: [",SSID, "] password: [", PASS,"]")
'example of connecting to a local network
If Wifi.Connect2(SSID,PASS) Then
Log("Conectado a Internet IP :",Wifi.LocalIp)
Timer1.Enabled = True
Net.DigitalWrite(False)
Else
Log("Failed to connect to network")
CallSubPlus("ConnectToNetwork",60*1000,1) 'try to connect again after 1mn
Net.DigitalWrite(True)
End If
WiFiServer.Start
End Sub
Public Sub SaveNetworkDetails(Data() As Byte)
Log("data to eeprom=",Data)
eeprom.WriteBytes(Array As Byte(MAGIC_EEPROM, Data.Length), 0)
eeprom.WriteBytes(Data, 2)
Delay(200)
esp.Restart
End Sub
Public Sub GetStoredDataLength As Byte
Dim header() As Byte = eeprom.ReadBytes(0, 2)
If header(0) = MAGIC_EEPROM Then
Return header(1)
End If
Return 0
End Sub
Sub GetStoredData As Byte()
Dim length As Byte = GetStoredDataLength
If length > 0 Then
Dim Data() As Byte = eeprom.ReadBytes(2, length)
Return Data
Else
Dim Data(2) As Byte =sr.ConvertArrayToBytes(Array( "_".getbytes,"_".getbytes,"_"))
Return Data
End If
End Sub
Public Sub ClearStoredDataLength
Dim header() As Byte = eeprom.ReadBytes(0, 2)
If header(0) = MAGIC_EEPROM Then
header(1) = 0
eeprom.WriteBytes(header,0)
Log("clear done")
End If
End Sub
Sub esp_reset(tag As Byte)
esp.Restart
End Sub
public Sub StopAP
Log("Stop AP")
RunNative("stopAP", Null)
End Sub
#if C
#include <ESP8266WiFi.h>
void stopAP (B4R::Object* u) {
WiFi.softAPdisconnect(1);
}
#end if
Sub SaveMessage(Mensagem As String)
Dim bufferIndex As Int = 0
' Limpa o buffer antes de gravar nova mensagem
For i = 0 To buffer.Length - 1
buffer(i) = 0
Next
For i = 0 To Mensagem.Length - 1
Dim charIndex As Int = Mensagem.GetBytes(i) - 32
If charIndex >= 0 And charIndex <= 94 Then
For j = 0 To 4 ' Cada caractere tem 5 colunas
If bufferIndex < buffer.Length - 8 Then ' Garante espaço para o final
buffer(bufferIndex) = GetByte(charIndex * 5 + j)
bufferIndex = bufferIndex + 1
End If
Next
' Adiciona um pequeno espaço entre caracteres
If bufferIndex < buffer.Length - 8 Then
buffer(bufferIndex) = 0
bufferIndex = bufferIndex + 1
End If
End If
Next
End Sub
Sub Timer1_Tick
' Desloca o buffer para a direita
' Dim firstByte As Byte = buffer(buffer.Length - 1)
' For i = buffer.Length - 1 To 1 Step -1
' buffer(i) = buffer(i - 1)
' Next
' buffer(0) = firstByte
' Desloca o buffer para a esquerda continuamente
Dim firstByte As Byte = buffer(0)
For i = 0 To buffer.Length - 2
buffer(i) = buffer(i + 1)
Next
buffer(buffer.Length - 1) = firstByte ' Mantém o fluxo do texto
' Atualiza as matrizes de LED usando setLed
For matrix = 0 To maxDevices
For col = 0 To 7
Dim rowData As Byte = buffer(matrix * 8 + col)
For row = 0 To 7
Dim ledState As Boolean = Bit.And(rowData, Bit.ShiftLeft(1, row)) <> 0
led.SetLed(maxDevices - matrix,row, col, ledState)
Next
Next
Next
End Sub
' Função para obter os dados da fonte de caracteres
Sub GetByte(Index As UInt) As Byte
Return RunNative("getdata", Index)
End Sub
#if C
#include <avr/pgmspace.h>
const PROGMEM byte data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, // (space)
0x00, 0x00, 0x2f, 0x00, 0x00, // !
0x00, 0x07, 0x00, 0x07, 0x00, // "
0x14, 0x7f, 0x14, 0x7f, 0x14, // #
0x24, 0x2a, 0x7f, 0x2a, 0x12, // $
0xc4, 0xc8, 0x10, 0x26, 0x46, // %
0x36, 0x49, 0x55, 0x22, 0x50, // &
0x00, 0x05, 0x03, 0x00, 0x00, // '
0x00, 0x1c, 0x22, 0x41, 0x00, // (
0x00, 0x41, 0x22, 0x1c, 0x00, // )
0x14, 0x08, 0x3E, 0x08, 0x14, // *
0x08, 0x08, 0x3E, 0x08, 0x08, // +
0x00, 0x00, 0x50, 0x30, 0x00, // ,
0x10, 0x10, 0x10, 0x10, 0x10, // -
0x00, 0x60, 0x60, 0x00, 0x00, // .
0x20, 0x10, 0x08, 0x04, 0x02, // /
0x3E, 0x51, 0x49, 0x45, 0x3E, // 0
0x00, 0x42, 0x7F, 0x40, 0x00, // 1
0x42, 0x61, 0x51, 0x49, 0x46, // 2
0x21, 0x41, 0x45, 0x4B, 0x31, // 3
0x18, 0x14, 0x12, 0x7F, 0x10, // 4
0x27, 0x45, 0x45, 0x45, 0x39, // 5
0x3C, 0x4A, 0x49, 0x49, 0x30, // 6
0x01, 0x71, 0x09, 0x05, 0x03, // 7
0x36, 0x49, 0x49, 0x49, 0x36, // 8
0x06, 0x49, 0x49, 0x29, 0x1E, // 9
0x00, 0x36, 0x36, 0x00, 0x00, // :
0x00, 0x56, 0x36, 0x00, 0x00, // ;
0x08, 0x14, 0x22, 0x41, 0x00, // <
0x14, 0x14, 0x14, 0x14, 0x14, // =
0x00, 0x41, 0x22, 0x14, 0x08, // >
0x02, 0x01, 0x51, 0x09, 0x06, // ?
0x32, 0x49, 0x59, 0x51, 0x3E, // @
0x0C, 0x1E, 0x3F, 0x1E, 0x0C, // A
0x7F, 0x49, 0x49, 0x49, 0x36, // B
0x3E, 0x41, 0x41, 0x41, 0x22, // C
0x7F, 0x41, 0x41, 0x41, 0x3E, // D
0x7F, 0x49, 0x49, 0x49, 0x41, // E
0x7F, 0x09, 0x09, 0x09, 0x01, // F
0x3E, 0x41, 0x49, 0x49, 0x7A, // G
0x7F, 0x08, 0x08, 0x08, 0x7F, // H
0x00, 0x41, 0x7F, 0x41, 0x00, // I
0x20, 0x40, 0x41, 0x3F, 0x01, // J
0x7F, 0x08, 0x14, 0x22, 0x41, // K
0x7F, 0x40, 0x40, 0x40, 0x40, // L
0x7F, 0x02, 0x0C, 0x02, 0x7F, // M
0x7F, 0x04, 0x08, 0x10, 0x7F, // N
0x3E, 0x41, 0x41, 0x41, 0x3E, // O
0x7F, 0x09, 0x09, 0x09, 0x06, // P
0x3E, 0x41, 0x51, 0x21, 0x5E, // Q
0x7F, 0x09, 0x19, 0x29, 0x46, // R
0x46, 0x49, 0x49, 0x49, 0x31, // S
0x01, 0x01, 0x7F, 0x01, 0x01, // T
0x3F, 0x40, 0x40, 0x40, 0x3F, // U
0x1F, 0x20, 0x40, 0x20, 0x1F, // V
0x3F, 0x40, 0x38, 0x40, 0x3F, // W
0x63, 0x14, 0x08, 0x14, 0x63, // X
0x07, 0x08, 0x70, 0x08, 0x07, // Y
0x61, 0x51, 0x49, 0x45, 0x43, // Z
0x00, 0x7F, 0x41, 0x41, 0x00, // [
0x02, 0x04, 0x08, 0x10, 0x20, // "\"
0x00, 0x41, 0x41, 0x7F, 0x00, // ]
0x04, 0x02, 0x01, 0x02, 0x04, // ^
0x40, 0x40, 0x40, 0x40, 0x40, // _
0x00, 0x01, 0x02, 0x04, 0x00, // `
0x20, 0x54, 0x54, 0x54, 0x78, // a
0x7F, 0x48, 0x44, 0x44, 0x38, // b
0x38, 0x44, 0x44, 0x44, 0x20, // c
0x38, 0x44, 0x44, 0x48, 0x7F, // d
0x38, 0x54, 0x54, 0x54, 0x18, // e
0x08, 0x7E, 0x09, 0x01, 0x02, // f
0x08, 0x14, 0x54, 0x54, 0x3C, // g
0x7F, 0x08, 0x04, 0x04, 0x78, // h
0x00, 0x44, 0x7D, 0x40, 0x00, // i
0x20, 0x40, 0x44, 0x3D, 0x00, // j
0x00, 0x7F, 0x10, 0x28, 0x44, // k
0x00, 0x41, 0x7F, 0x40, 0x00, // l
0x7C, 0x04, 0x18, 0x04, 0x78, // m
0x7C, 0x08, 0x04, 0x04, 0x78, // n
0x38, 0x44, 0x44, 0x44, 0x38, // o
0x7C, 0x14, 0x14, 0x14, 0x08, // p
0x08, 0x14, 0x14, 0x18, 0x7C, // q
0x7C, 0x08, 0x04, 0x04, 0x08, // r
0x48, 0x54, 0x54, 0x54, 0x20, // s
0x04, 0x3F, 0x44, 0x40, 0x20, // t
0x3C, 0x40, 0x40, 0x20, 0x7C, // u
0x1C, 0x20, 0x40, 0x20, 0x1C, // v
0x3C, 0x40, 0x30, 0x40, 0x3C, // w
0x44, 0x28, 0x10, 0x28, 0x44, // x
0x0C, 0x50, 0x50, 0x50, 0x3C, // y
0x44, 0x64, 0x54, 0x4C, 0x44, // z
0x00, 0x08, 0x36, 0x41, 0x41, // {
0x00, 0x00, 0x7F, 0x00, 0x00, // |
0x41, 0x41, 0x36, 0x08, 0x00, // }
0x10, 0x08, 0x08, 0x10, 0x08, // ~
//0x0C, 0x1E, 0x3F, 0x1E, 0x0C, // ?
};
B4R::Object beo1;
B4R::Object* getdata(B4R::Object* o) {
int idx = (int)o->toLong();
return beo1.wrapNumber(pgm_read_byte_near(data + idx));
}
#end if
https://www.facebook.com/share/r/16QESfuZN9/
Last edited: