Controlando a matriz de LED da Arduino Uno R4 via WiFi Deixe um comentário

A nova Arduino Uno R4 WiFi possui muitas características interessantes, como um microcontrolador muito mais poderoso que a R3 e a inclusão de WiFi diretamente na placa. Neste artigo vamos falar sobre o funcionamento da sua matriz de LED, a qual ocupa uma boa parte da placa.

Matriz de LED Arduino Uno R4 WiFi

Como funciona a Matriz de LED da Arduino Uno R4 WiFi

A matriz de LED contém 96 LEDs organizados em 8 linhas de 12 LEDs. Para conectar este grande número de LEDs usando poucas linhas digitais do microcontrolador é usada uma técnica chamada charlieplexing.

Esta técnica tira proveito do fato que as saídas digitais do microcontrolador podem assumir três estados:

  • Nível ALTO, onde o pino tem uma tensão próxima à da alimentação.
  • Nível BAIXO, onde o pino tem uma tensão próxima a 0V (terra).
  • ALTA IMPEDÂNCIA, onde o pino se comporta como se não estivesse conectado. Esta condição ocorre quando um pino é configurado como entrada digital.

Em cada instante, o programa que controla os LEDs vai deixar apenas duas conexões da matriz ativas, uma em nível ALTO e outra em nível BAIXO, e as demais em ALTA IMPEDÂNCIA. Com isto, pode-se colocar até dois LEDs em cada combinação de pinos.

A figura abaixo mostra a conexão de 6 LEDs usando apenas 3 pinos do microcontrolador:

A tabela abaixo mostra como acender cada LED:

LED Pino 1 Pino 2 Pino 3
1 ALTO BAIXO
2 BAIXO ALTO
3 ALTO BAIXO
4 BAIXO ALTO
5 ALTO BAIXO
6 BAIXO ALTO

Para dar a aparência de que vários LEDs estão ligados simultaneamente, ficamos contínua e rapidamente atualizando os LEDs um a um. A persistência da visão fará com tenhamos a impressão de que os LEDs acendem juntos.

Interface de programação API para a Matriz de LED

Você deve estar pensando que implementar a programação descrita no item anterior é bem complicado. Mas você não precisa fazer esta programação, o Arduino já fornece rotinas para isto. A especificação destas rotinas é o que compõe a interface de programação (API). Ela está descrita em https://docs.arduino.cc/tutorials/uno-r4-wifi/led-matrix#api

Na nossa aplicação vamos usar apenas alguns dos métodos disponíveis no objeto ArduinoLEDMatrix:

  • begin(): inicia o tratamento da matriz
  • on(led): acende um LED da matriz, os LEDs são numerados de 0 a 95.
  • off(led): apaga um LED da matriz
  • loadFrame(frame): atualiza toda a matriz. ‘frame’ é um vetor com 3 inteiros sem sinal de 32 bits onde cada um dos bits corresponde a um LED

A API inclui funções para fazer animações, o que não vamos usar aqui.

Conectando a uma rede WiFi com o Arduino Uno R4

Para ter acesso às classes para uso do WiFi, você precisa colocar no início do seu programa:

#include "WiFiS3.h"

O S3 no nome faz referência ao microcontrolador ESP32-S3 usado na R4 WiFi para comunicação WiFi.

As classes, objetos e métodos são semelhantes às das bibliotecas WiFi para as outras placas e shields da Arduino. A forma mais compacta de conectar a uma rede WiFi é chamar o método WiFi.begin() para disparar a conexão, aguardar um tempo e usar WiFi.status() para verificar se ela foi bem sucedida.  É o que faz o trecho abaixo, presente nos exemplos da IDE:

int status = WL_IDLE_STATUS;
while (status != WL_CONNECTED) {
  status = WiFi.begin(ssid, pass);
  delay(10000);
}

Neste código, ssid é o nome da sua rede e pass é a senha. A convenção dos exemplos, que vamos seguir aqui, é definir estes valores em um arquivo chamado arduino_secrets.h (este arquivo fica em uma aba separada na IDE):

Fazendo um servidor Web com a Arduino Uno R4 WiFi

A comunicação entre um navegador (browser) e um servidor Web segue um protocolo chamado HTTP, que por sua vez é implementado sobre o protocolo de rede TCP. Um servidor Web completo é algo bastante complexo, o que vamos usar aqui é um servidor TCP, que faz parte da biblioteca WiFi da Arduino, e tratar “na unha” uma pequena parte do HTTP.

O primeiro passo é declarar um objeto WiFiServer, indicando a porta TCP na qual ele vai atender conexões (no caso 80, que é a porta padrão para http):

WiFiServer server(80);

Após conseguirmos conectar à rede WiFi, iniciamos o server:

server.begin();

A interação com o navegador é feita em um laço como o abaixo:

// Aguarda uma conexão
  WiFiClient client = server.available();
  if (client) {
    Serial.println("Cliente conectou");
    // Recebe a solicitação do cliente
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();  // Le um caracter enviado pelo navegador
        // Linha vazia indica o final da solicitação
        if (linhaAtualVazia) {
            // envia resposta
        }
        // trata o caracter recebido
        // …
      }
    }
    // aguarda a página ser enviada
    delay(1);
    // Fecha a conexão
    client.stop();
    Serial.println("Cliente desconectou");
  }

O navegador envia ao servidor uma solicitação que começa com um comando “GET recurso” e termina com uma linha vazia. A resposta do servidor contém um cabeçalho e, opcionalmente, um corpo, ambos terminados por uma linha vazia. A primeira linha do cabeçalho contém “HTTP/1.1 status”. Pouco depois de enviar a resposta o servidor fecha a conexão, uma nova conexão será feita quando o navegador fizer uma nova solicitação.

O nosso tratamento do http consiste em capturar o comando GET. Se o recurso solicitado for “/” vamos enviar uma página HTML. Se for outro recurso, vamos responder com o status 404 (indicando que não temos este recurso disponível).

A página HTML contém quatro elementos:

  • Título;
  • Canvas, onde serão controlados os LEDs;
  • Campo invisível, usado para enviar a situação dos LEDs para o servidor;
  • Botão para envio da situação dos LEDs ao servidor. Esta informação será enviada como um parâmetro no comando GET.

A página inclui código em javascript para:

  • Desenhar no canvas os LEDs;
  • Mudar o LED entre apagado e aceso quando for clicado;
  • Preencher o campo invisível com a situação dos LEDs no canvas.

Código para utilização da Matriz de LED da Arduino Uno R4 WiFi

Abaixo você pode conferir o código completo para utilização da Matriz de LED da Arduino Uno R4 WiFi:

/*
  Demonstração de controle dos LEDs via WiFi
  Baseado/inspirado em
  - WiFi Web Server
    https://docs.arduino.cc/tutorials/uno-r4-wifi/wifi-examples#wi-fi-web-server
  - LED Matrix tool https://ledmatrix-editor.arduino.cc/
 */

#include "WiFiS3.h"
#include "Arduino_LED_Matrix.h"
#include "arduino_secrets.h" 

char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;
int status = WL_IDLE_STATUS;
WiFiServer server(80);
ArduinoLEDMatrix matrix;
const uint32_t limpa[3] = { 0, 0, 0 };

// Página enviada
const char *html[] = {
  "<!DOCTYPE html><html>",
  "<head><script>var leds = new Array(8*12).fill('.');",
  "var prog = new URL(window.location.href).searchParams.get(\"leds\");",
  "if (prog != null) { for (i = 0 ; i < 8*12; i++) {",
  "leds[i] = prog[i]; } } </script></head>",
  "<body><h1>Matriz de LEDs do Arduino R4 WiFi</h1><br />",
  "<canvas id=\"matriz\" width=\"480\" height=\"320\" ",
  "style=\"border:1px solid #000000;\"></canvas><br /><br />",
  "<form onsubmit=\"fillLeds()\"><input type=\"hidden\" id=\"leds\" name=\"leds\" >",
  "<button type=\"submit\">Atualiza</button></form>",
  "<script>const canvas = document.getElementById(\"matriz\"); ",
  "const ctx = canvas.getContext(\"2d\");",
  "let ind = 0; for (y = 20; y < 320; y+=40) { for (x = 20; x < 480; x+=40) {",
  "ctx.beginPath(); ctx.arc(x, y, 18, 0, 2 * Math.PI); ctx.stroke(); ",
  "ctx.fillStyle = (leds[ind] == \'*\') ? \"red\":\"white\";",
  "ctx.fill(); ind++; }} canvas.addEventListener(\'click\', ",
  "changeLed, false); canvas.onselectstart = function () { return false; }",
  "function changeLed(event) { var canvas = document.getElementById(\"matriz\"), ",
  "canvasLeft = canvas.offsetLeft + canvas.clientLeft, ",
  "canvasTop = canvas.offsetTop + canvas.clientTop,",
  "ctx = canvas.getContext(\'2d\'); var x = Math.trunc((event.pageX-canvasLeft)/40), ",
  "y = Math.trunc((event.pageY-canvasTop)/40), ind = y*12+x; ",
  "leds[ind] = leds[ind] == \'.\'? \'*\' : \'.\';",
  "ctx.beginPath(); ctx.arc(20+x*40, 20+y*40, 18, 0, 2 * Math.PI); ctx.stroke();",
  "ctx.fillStyle = (leds[ind] == \'*\') ? \"red\" : \"white\"; ctx.fill(); }",
  "function fillLeds() { var ledFld = document.getElementById(\"leds\"); ",
  "ledFld.value = leds.join(\"\"); }</script></body></html>",
  NULL
};

// Iniciação
void setup() {
  // Inicia serial e aguarda PC conectar
  Serial.begin(115200);
  while (!Serial) {
  }
  delay(1000);
  Serial.println("Controle dos LEDs via WiFi");
  // Inicia matriz de LEDs
  matrix.begin();
  // Verifica o módulo WiFi
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Falha na comunicação com o módulo WiFi!");
    while (true)
      ;
  }
  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Atualize o firmware do módulo WiFi");
  }
  // Conecta à rede WiFi
  int led = 0;
  matrix.on(led);
  while (status != WL_CONNECTED) {
    Serial.print("Conectando a rede WiFi... ");
    status = WiFi.begin(ssid, pass);
    uint32_t timeout = millis() + 10000;
    while (millis() < timeout) {
      status = WiFi.status();
      if (status == WL_CONNECTED) {
        Serial.println("Conectado");
        break;
      }
      delay(100);
      matrix.off(led);
      led = (led+1) % 96;
      matrix.on(led);
    }
    if (status == WL_CONNECTED) {
      break;
    }
    Serial.println("Falhou!");
  }
  matrix.loadFrame(limpa);
  server.begin();
  // Conectado
  IPAddress ip = WiFi.localIP();
  Serial.print("Conecte com um browser no endereco: ");
  Serial.println(ip);
}

// Laço Principal
void loop() {
  // Aguarda uma conexão
  WiFiClient client = server.available();
  if (client) {
    Serial.println("Cliente conectou");
    // Recebe a solicitação do cliente
    boolean linhaAtualVazia = true;
    char linhaAtual[200] = "";
    char pgmLeds[12*8+1] = "";
    bool primeiraLinha = true;
    bool enviaPagina = false;
    int pos = 0;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        // Linha vazia indica o final da solicitação
        if (c == '\n' && linhaAtualVazia) {
          if(enviaPagina) {
            Serial.println("Enviando pagina");
            // atualiza os leds
            if (pgmLeds[0] != 0) {
              uint32_t frame[3] = { 0, 0, 0 };
              for (int i = 0; i < 8*12; i++) {
                if (pgmLeds[i] == '*') {
                  frame[i/32] |= (1 << (31 - i%32));
                }
              }
              matrix.loadFrame(frame);
            }
            // envia o cabeçalho HTTP seguido da nossa página
            client.println(F("HTTP/1.1 200 OK"));
            client.println(F("Content-Type: text/html"));
            client.println(F("Connection: close"));
            client.println();
            for (int i = 0; html[i] != NULL; i++) {
              client.println(html[i]);
            }
          } else {
            // rejeita solicitação
            Serial.println("Rejeitando recurso nao disponivel");
            client.println(F("HTTP/1.1 404 Not Found\nConnection: close\n\n"));
          }
          break;
        }
        if (c == '\n') {
          // terminou a linha anterior
          if (primeiraLinha) {
            if (memcmp(linhaAtual, "GET / ", 6) == 0) {
              enviaPagina = true;
            } else if (memcmp(linhaAtual, "GET /?leds=", 11) == 0) {
              enviaPagina = true;
              // extrai programação dos leds
              if (strlen(linhaAtual) > 107) {
                memcpy (pgmLeds, linhaAtual+11, 8*12);
                pgmLeds[8*12] = 0;
              }
            } else {
              enviaPagina = false;
            }
          }
          primeiraLinha = false;
          // começando uma nova linha
          linhaAtualVazia = true;
          pos = 0;
          linhaAtual[0] = 0;
        } else if (c != '\r') {
          // tem algo na linha atual
          linhaAtualVazia = false;
          linhaAtual[pos] = c;
          if (pos < (sizeof(linhaAtual) - 1)) {
            pos = pos+1;
          }
          linhaAtual[pos] = 0;
        }
      }
    }
    // aguarda a página ser enviada
    delay(1);
    // Fecha a conexão
    client.stop();
    Serial.println("Cliente desconectou");
  }
}

Funcionamento da Matriz de LED da Arduino Uno R4 WiFi

Para rodar este exemplo você precisa:

  • Uma placa Uno R4 WiFi;
  • Um cabo USB-C para ligar a placa a um micro;
  • Um micro com a IDE do Arduino, com o suporte ao Arduino Uno R4 instalado;
  • Uma rede WiFi cujo nome e senha você conhece.

Carregue na IDE o programa acima e crie o arquivo arduino_secrets.h com o nome e senha da rede WiFi. Conecte a placa e mande compilar e carregar o programa. Em seguida, abra o monitor serial para iniciar a execução e acompanhar as mensagens.

Após a conexão com a rede, será informado o IP associado à placa:

OBs: A mensagem “Atualize o firmware do módulo WiFi” indica que existe uma versão mais nova. O artigo https://support.arduino.cc/hc/en-us/articles/9670986058780-Update-the-wireless-connectivity-firmware-on-UNO-R4-WiFi explica como atualizar.

Abra um navegador web e digite o IP informado:

Matriz de LED Arduino Uno R4 WiFi

Cada círculo corresponde a um LED. Clicando neles você muda entre apagado e aceso. Após completar o desenho, clique no botão Atualiza para enviar o desenho e apresentá-lo na matriz de LED da placa.

Matriz de LED Arduino Uno R4 WiFi

Próximos Passos

Este exemplo mostrou como:

  • Conectar a sua placa Uno R4 WiFi a uma rede;
  • Controlar por programa a matriz de LED;
  • Como implementar um servidor Web simples.

Agora é deixar correr a imaginação e fazer as suas próprias aplicações! Algumas sugestões:

  • Experimentar as funções de animação da matriz;
  • Fazer uma página web que informa a leitura de um sensor conectado à placa.

E então, gostou de aprender a utilizar a Matriz de LED da Arduino Uno R4 WiFi? Deixe um comentário abaixo dizendo o que achou e quais as modificações que implementou. Para mais conteúdos como este, acesse nosso blog.

Faça seu comentário

Acesse sua conta e participe