Utilizar um display em seus projetos ajuda bastante a exibir informações úteis de forma fácil para o usuário. Portanto, neste post, veremos o que é o display OLED (focando no SSD1306), bem como algumas de suas características e aprenderemos a utilizar ele com o Arduino. 

Informações básicas

O que é o display OLED

O display OLED é uma telinha formada por uma matriz de pequenos LEDs. No caso do módulo de display mais comum de encontrar (parte esquerda da imagem seguinte), ele possui uma matriz de LEDs com 128 colunas e 64 linhas, totalizando 8192 LEDs (chamados de pixels). E, cada um destes LEDs pode ser acionado individualmente, o que permite a criação de imagens personalizadas. Apesar de parecer muita coisa, ainda é muito pouco se comparado aos monitores de computador com matrizes de 1920 por 1080 (normalmente são displays LCD). Entretanto, para muitas aplicações de sistemas embarcados, o tamanho de 128×64 pode ser mais do que o suficiente.

Veja abaixo os módulos de display OLED mais comuns de encontrar. O da esquerda é de 0,96 polegadas e possui resolução de 128×64 conforme comentei, e o outro é de 0,91 polegadas e possui resolução de 128×32. Para o restante do post, vou focar nos dois.

Display OLED módulos

Ao contrário do OLED, os displays LCD normalmente possuem uma luz na parte de trás que serve para iluminar todos os pixels do display. E, como o display OLED faz o controle individual de cada LED sem acender tudo necessariamente, ele é capaz de gastar menos energia que o display LCD. Citando um exemplo, o display OLED de 0,96 polegadas gasta algumas dezenas de miliampères, enquanto um display LCD 16×2 gasta mais de 100 miliampères.

Existem alguns displays OLED para Arduino que são coloridos, mas os mais comuns (os dois da imagem anterior) são monocromáticos. É possível encontrar displays monocromáticos com luz branca, azul ou amarela.

Aplicações do display OLED

Adiante estão algumas aplicações:

  • Aplicações que precisem de um display capaz de desenhar imagens personalizas.
    • Ao contrário display LCD 16×2 ou 16×4, o OLED aceita desenhos personalizados. O LCD até aceita criar um caractere personalizado, mas a resolução é pequena e não é possível desenhar continuamente em todo o display.
  • Aplicações que exigem um display pequeno para portabilidade.
    • Conforme comentei, os displays comuns são pequenos (ex: 0,96 polegadas) e ideais para aplicações portáteis.
  • Dispositivos alimentados por bateria.
    • Como o display OLED consome pouca energia comparado ao LCD, ele é mais recomendado para esses casos.

Veja adiante um projeto que criei que utiliza o display OLED. Decidi utilizá-lo pelos 3 motivos citados anteriormente.

Display OLED vs módulo

Quando mostrei a imagem dos display, chamei eles de módulos. Isso, porque o display OLED em si é só a parte central e a parte azul é uma plaquinha (PCB) com componentes essenciais para o display. Veja o display sozinho na imagem abaixo.

Fonte: tcclcd

É totalmente possível utilizar o display sem a PCB, mas, para isso, você precisa ligar os outros componentes conforme veremos adiante. Entretanto, a PCB facilita bastante o uso, necessitando ligar apenas 4 fios no display.

Características do display OLED

Neste tópico, irei mostrar características dos dois módulos display OLED mostrados na primeira imagem do post. E não serão informações cruciais para a implementação no Arduino. Então, se desejar, pode pular para o tópico seguinte.

Características

  • 3,3 a 5V de alimentação (Vcc ou Vdd):
    • O display OLED em si trabalha com 3,3V, mas o módulo possui um regulador de tensão para 3,3V. Então é possível alimentar ele com 5V também.
    • Existe um tipo de configuração do display (sem o módulo) que exige que ele seja alimentado com tensões acima de 7 ou 10V (depende do modelo). Veremos isso no tópico do esquemático.
  • Pinos de dados não tolerantes a 5V:
    • Os pinos de dados (SCK e SDA) não podem ser ligados diretamente a quaisquer pinos digitais do Arduino. Teoricamente, os pinos da interface I2C do Arduino não colocam sinais de 5V, pois eles trabalham apenas como saída em nível baixo (0V) ou entrada (estado de alta impedância) e utilizam resistores de pull-up pra criar o nível lógico alto (isso se estiverem trabalhando como I2C).
    • Em suma, nunca vi ninguém queimando display OLED por ligar no Arduino. Mas é sempre bom ter em mente esse limite de tensão.
  • Controlador do display (driver):
    • SSD1306.
    • É um circuito integrado que fica dentro do display e realiza o controle da matriz dos LEDs facilitando para nós o seu uso.
    • Inclusive, você encontra o display para comprar com esse nome.
  • Interface I2C:
    • A forma de comunicar com o módulo é por meio de sua interface I2C (pinos SDA e SCK). 
    • O endereço padrão é 0x3C, mas pode ser mudado (tópico mais adiante).
    • O módulo possui resistores de pull-up.
    • Existem também módulos de display OLED que são SPI, mas o foco deste post são os I2C.
  • Consumo de corrente:
    • Até algumas dezenas de mA.
    • Detalhes estão no tópico seguinte.

Mais informações podem ser lidas nos datasheets: OLED 0,96″ e OLED 0,91″. O controlador SSD1306 possui algumas características interessantes até, mas não pretendo descrevê-las. Se tiver interesse, dê uma lida neste datasheet.

Consumo de corrente

O consumo de corrente varia dependendo do contraste (intensidade da luz) e de quantos pixels (LEDs) estão acesos. Este site mostra alguns valores para diferentes valores de contraste e pixels acesos.

Mudar endereço I2C

O driver SSD1306 aceita que o endereço I2C seja alterado entre 0x3C e 0x3D. No caso do módulo de 0,96 polegadas, isso pode ser feito alternando o lado onde um determinado resistor fica soldado na parte de baixo da placa. Veja abaixo:

Parte de trás do display OLED

Para alternar o endereço, é preciso dessoldar o resistor indicado e soldar ele no terminal ao lado. Como também pode ser visto na imagem, infelizmente não é possível fazer isso facilmente no display de 0,91 polegadas. Mas, se você quiser se aventurar, o datasheet indica que o pino 10 (D/C#) deve ficar em nível alto para alternar o endereço.

Repare que o endereço gravado na PCB é 0x78 ou 0x7A, que nada mais é do que o endereço 0x3C ou 0x3D com os bits deslocados 1 casa para esquerda. Não sei porquê colocaram desse jeito, mas no código, você precisar usar o 0x3C.

Circuito do módulo

O circuito do display pode parecer assustador por ser pequeno, mas é até bem simples. Se você seguir os datasheets (aqui ou aqui), é possível entender as ligações sem problemas. Veja adiante um esquemático de um módulo que encontrei:

Fonte: Adafruit

A partir da imagem, é possível ver que o SSD1306 exige relativamente poucos componentes. Além disso, o display em si exige tensões elevadas (7,25 ou 12V), mas o controlador SSD1306 possui um circuito conversor DC/DC interno que permite a geração desta tensão elevada a partir de uma tensão de 3,3 a 4,2V. E é justamente esse tipo de configuração que os módulos usam, pois, caso contrário, seria bem complicado ligar o display no Arduino.

Utilizando OLED com Arduino

Circuito

A ligação do display OLED é bem direta, pois ele tem 4 pinos e não são necessários componentes adicionais. Basta alimentar ele em 3,3V ou 5V e ligar os pinos de dados nos pinos A4 (SDA) e A5 (SCK) do Arduino UNO. Veja adiante:

Circuito do display OLED

Baixando biblioteca

A biblioteca pode ser baixada na própria IDE do Arduino em Sketch -> Incluir Biblioteca -> Gerenciar Bibliotecas… E é só pesquisar por “SSD1306” e baixar a opção que tem como autor “Adafruit”.

Ao clicar pra baixar, aparece uma janela pedindo pra baixar outras bibliotecas que são pré-requisito. Você precisa aceitar para evitar erros na hora de usar a biblioteca.

Comandos disponíveis

Antes de mostrar o código base com os comandos de inicialização, vou comentar sobre alguns dos comandos que a biblioteca oferece para desenhar e escrever no display. Tenha em mente que X e Y que falarei adiante são coordenadas do display referentes à coluna e linha respectivamente. Isto é, para o display 128×64, X vai de 0 a 127 e Y vai de 0 a 63.

Vejamos os comandos:

  • display.display();
    • Atualiza a tela. Deve ser executado após qualquer outro comando para que o desenho apareça na tela.
  • display.clearDisplay();
    • Limpa a tela.
  • display.drawLine(x1, y1, x2, y2, cor);
    • Desenha uma linha que sai de um ponto (x1,y1) e vai até outro (x2,y2).
    • x1 e y1 são as coordenadas do ponto de partida da linha.
    • x2 e y2 são as coordenadas do ponto final da linha.
    • cor é a cor para desenhar o retângulo. 1 = branco e 0 = preto.
  • display.drawRect(x1, y1, w, h, cor);
    • Desenha um retângulo sem preenchimento.
    • x1 e y1 são as coordenadas do vértice superior esquerdo do retângulo.
    • w e h são a largura e altura do retângulo respetivamente.
    • cor: 1 = branco e 0 = preto.
  • display.fillCircle(x1, y2, raio, cor);
    • Desenha um circulo com preenchimento.
    • x1 e y1 são as coordenadas do centro do círculo.
    • raio é o raio do círculo.
    • cor: 1 = branco e 0 = preto.
  • display.setCursor(x, y);
    • Define a posição do cursor do texto.
    • x e y são as coordenadas do cursor.
  • display.setTextSize(tamanho);
    • Define o tamanho da fonte do texto.
    • tamanho: pode ser 1, 2 ou 3.
  • display.setTextColor(cor);
    • Define a cor do texto.
    • cor: 1 = branco e 0 = preto.
  • display.print(texto);
    • Escreve um texto na última posição definida pelo cursor.

Desenhando imagens personalizadas

É possível também desenhar uma imagem personalizada que você tenha salvo no seu computador. Para isso, precisamos da ferramenta image2cpp para converter a imagem em um formato que o código consegue ler e reproduzir no display. Vejamos os passos para isso então:

  • 1 – Acesse o site image2cpp.
  • 2 – Escolha a imagem desejada clicando em “Escolher arquivos”.
    • Como o display é monocromático, o ideal é que a imagem a ser utilizada esteja convertida para escala de cinza (preto e branco). Se não tiver, não tem problema também.
  • 3 – Ajuste os parâmetros para deixar a imagem convertida da forma desejada.
    • Como o display possui uma resolução de 128×64 ou 128×32, precisamos ir em “Canvas size” e definir o tamanho que queremos reduzir a imagem para um tamanho dentro destes limites. É importante ter em mente que a qualidade da imagem vai cair consideravelmente.
    • Além de ajustar o “Canvas size”, é preciso ir em “Scalling” e selecionar “scale to fit, keeping proportions” para de fato fazer a imagem ser redimensionada.
    • Você pode testar os outros parâmetros e observar o resultado final na seção “Preview”.  Então, mexa até ficar do jeito que você achar melhor.
    • O parâmetro “Brightness / alpha threshold” ajusta o limiar que considera um pixel como branco ou preto na imagem final.
    • Veja abaixo as minhas configurações:
  • 4 – Verifique se em “Code output format” a opção “Arduino” está selecionada.
  • 5 – Clique em “Generate code”.
    • Você pode alterar o prefixo do nome da variável gerada em “Identifier/Prefix”, mas eu acho mais fácil simplesmente gerar a variável e depois mudar o nome dela.
  • 6 – Copie o código gerado.
  • 7 – Por fim, volte à IDE do Arduino e cole o código no início do programa (acima do void setup).

Pronto, agora resta apenas chamar o comando para desenhar a sua imagem. E você pode fazer isso da seguinte forma:

display.drawBitmap(x, y, nome_variável, w, h, cor);

Onde:

  • x e y são as coordenadas para desenhar a imagem (começando do canto superior esquerdo).
  • nome_variável é o nome da variável da imagem.
  • w e h são a largura e altura da imagem respectivamente.
    • Esse valor deve ser o mesmo que você colocou lá em “Canvas size” no site.
  • cor é a cor da imagem.
    • 1 é branco e 0 é preto.

Código de exemplo

O código abaixo contém alguns dos comandos que citei anteriormente incluindo o desenho de uma imagem personalizada. Além disso, o código tem os comandos de inicialização e configuração do display. Leia os comentários para entender os detalhes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // Largura do display
#define SCREEN_HEIGHT 32 // Altura do display

#define SCREEN_ADDRESS 0x3C // Endereço do display (tente trocar por 0x3D se não funcionar)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);


// Imagem personalizada
const unsigned char bitmap_ci [] PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x25, 0xa4, 0x80, 0x01, 0x25, 0xb4, 0xc0,
    0x03, 0x6d, 0xb6, 0xc0, 0x07, 0xff, 0xff, 0xe0, 0x0c, 0x00, 0x00, 0x30, 0x3d, 0x80, 0x01, 0xbc,
    0x05, 0xc0, 0x03, 0xb0, 0x0d, 0x80, 0x01, 0xb0, 0x3c, 0x3f, 0xfc, 0x3c, 0x04, 0x20, 0x04, 0x20,
    0x04, 0x20, 0x04, 0x20, 0x3c, 0x20, 0x04, 0x3c, 0x04, 0x20, 0x04, 0x20, 0x0c, 0x20, 0x04, 0x30,
    0x3c, 0x20, 0x04, 0x3c, 0x04, 0x20, 0x04, 0x20, 0x0c, 0x20, 0x04, 0x30, 0x3c, 0x20, 0x04, 0x3c,
    0x04, 0x20, 0x04, 0x20, 0x0c, 0x20, 0x04, 0x30, 0x3c, 0x3f, 0xfc, 0x3c, 0x05, 0xc0, 0x03, 0xa0,
    0x1d, 0xc0, 0x03, 0xb8, 0x3d, 0x80, 0x01, 0xbc, 0x07, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xe0,
    0x01, 0x25, 0xb4, 0xc0, 0x01, 0x25, 0xb4, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};


void setup()
{
  Serial.begin(9600);

  // Inicializa o display
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
  {
    Serial.println(F("Erro na inicialização!"));
    while(1);
  }

  display.clearDisplay();

  // Retângulo ao redor da tela
  display.drawRect(0, 0, display.width()-1, display.height()-1, 1);
  // Circulo na parte direita da tela
  display.fillCircle(display.width() - 12, 16, 7, 1);
 
  // Configura fonte do texto
  display.setTextSize(1); // Tamanho
  display.setTextColor(1); // Cor
  display.setCursor(4, 8); // Cursor pode ir de 0,0 a 128,32
  display.print("Mundo Projetado");
  display.setCursor(4, 16);
  display.print("Teste");

  // Atualiza todas as mudanças
  display.display();

  // Espera 4 segundos e exibe a imagem
  delay(4000);
  display.clearDisplay();
  display.drawBitmap(64-16, 0, bitmap_ci, 32, 32, 1);
  display.display();
}

void loop()
{

}