Share My Creation Web-Controlled LED Matrix Display with ESP8266

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:​


  1. Na inicialização, o ESP8266 se conecta a uma rede Wi-Fi pré-configurada.
  2. Um servidor web é iniciado no ESP8266.
  3. O usuário acessa o painel de controle via navegador (digitando o IP local do dispositivo).
  4. Uma página da web é exibida com um campo de entrada de texto e botões de controle.
  5. 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:
Top