Lopaka Basics 09: além do Arduino

C++. YAML. Python. Mais Python. E C para um Flipper Zero. Cinco programas diferentes desenham a mesma tela, e o Lopaka escreveu o código de desenho para cada um deles.
Neste episódio, você vai descobrir por que placas executam código em linguagens tão diferentes, e o que separa firmware compilado de um interpretador Python. Depois você vai desenhar um pequeno monitor de plantas no Lopaka para MicroPython, montar a mesma tela para CircuitPython, ESPHome e Flipper Zero e executar três deles em placas simuladas.
O que você vai construir: uma tela de monitor de plantas de 128×64: um vaso com três folhas, o nome da planta, uma barra de umidade e um lembrete para regar ela, em quatro plataformas. Do que você precisa: um navegador e o simulador online do episódio 1. Placas são opcionais: um Raspberry Pi Pico ou um ESP32 com um OLED SSD1306 de 128×64, ou um Flipper Zero.
Dois jeitos de executar código
Compilado: o firmware inteiro, toda vez
Código Arduino é compilado. Um compilador transforma seu sketch em firmware, instruções de máquina brutas para o chip, e você grava o firmware inteiro na placa. Mude uma linha, e você compila e grava tudo de novo.
ESPHome funciona do mesmo jeito: seu arquivo YAML vira C++, e o C++ vira firmware. Um aplicativo Flipper Zero também é compilado.
Interpretado: um arquivo de texto na placa
MicroPython e CircuitPython invertem isso. Você grava um interpretador na placa uma vez. Depois disso, seu programa é um arquivo de texto simples na placa, como main.py, e o interpretador lê ele linha por linha. Mude uma linha, salve o arquivo, e ele executa de novo, sem compilador no meio.
O preço é velocidade e memória: o próprio interpretador roda no chip e ocupa a parte dele. Em placas pequenas, mantenha o desenho simples.

Etapa 1: escolha uma plataforma
No Lopaka, a plataforma é a primeira coisa que você escolhe em um novo projeto. Clique em + New Project e abra a lista Platform. Ao lado do nome de cada plataforma fica um selo com a linguagem do código dela: C++, YAML, Python ou C.

Comece com Micropython. Mantenha o tamanho da tela em 128×64, dê um título ao projeto, "Plant Monitor", e clique em Salvar.
Etapa 2: desenhe a tela
O design é um monitor de plantas minúsculo. Ele usa só retângulos, círculos, linhas e texto, então cada plataforma neste episódio consegue desenhar ele.
Desenhe ele com as ferramentas Rectangle, elipse, linha e texto (Aa), e digite os valores exatos no inspetor:
- O vaso: dois retângulos preenchidos, a borda em X
8, Y42, W26, H5, e o vaso abaixo dela em X11, Y47, W20, H15. Ative Filled para os dois. - O caule: uma linha de
21, 24a21, 41. - As folhas: três elipses com Filled ativado e os dois raios iguais: esquerda em X
9, Y26com raio5, direita em X23, Y22com raio5, e topo em X15, Y11com raio6. - O nome: o texto
BASILem X44, Y11, e uma linha sob ele de44, 13a123, 13. - A barra de umidade: o texto
MOISTUREem X44, Y27, um retângulo em X44, Y30, W80, H10, e um retângulo preenchido dentro dele em X46, Y32, W21, H6: o nível da água. - O lembrete: o texto
WATER ME!em X44, Y57.
MicroPython tem uma fonte, 8×8 pixels, então as camadas de texto não têm lista de fontes.
Por fim, dê nomes a tudo. Clique duas vezes em Screen 1 no painel de camadas e renomeie ela para Plant, depois nomeie as camadas rim, pot, stem, leaf_left, leaf_right, leaf_top, title, rule, caption, bar, level e hint. O nome da tela vira o nome da função, e os nomes das camadas viram comentários no código.

Etapa 3: seu código
Uma chamada por camada
O código é Python simples. Cada camada vira uma chamada no objeto do display: fill_rect, rect, line, ellipse e text. No fim, display.show() envia a imagem pronta para a tela.
O código gerado do Lopaka fica entre os marcadores BEGIN e END. Algumas linhas suas ficam ao redor dele: quatro acima dos marcadores configuram o display, e uma abaixo chama a função. Digite elas direto no painel de código do Lopaka. O Lopaka mantém suas linhas fora dos marcadores, e Copiar pega o arquivo inteiro:
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
i2c = I2C(0, scl=Pin(5), sda=Pin(4))
display = SSD1306_I2C(128, 64, i2c)
# [BEGIN lopaka generated]
# SSD1306 OLED display library: https://github.com/PerfecXX/MicroPython-SSD1306/blob/main/ssd1306.py
def drawPlant(display):
# rim
display.fill_rect(8, 42, 26, 5, 1)
# pot
display.fill_rect(11, 47, 20, 15, 1)
# stem
display.line(21, 24, 21, 41, 1)
# leaf_left
display.ellipse(14, 31, 5, 5, 1, True)
# leaf_right
display.ellipse(28, 27, 5, 5, 1, True)
# leaf_top
display.ellipse(21, 17, 6, 6, 1, True)
# title
display.text("BASIL", 44, 2, 1)
# rule
display.line(44, 13, 123, 13, 1)
# caption
display.text("MOISTURE", 44, 18, 1)
# bar
display.rect(44, 30, 80, 10, 1)
# level
display.fill_rect(46, 32, 21, 6, 1)
# hint
display.text("WATER ME!", 44, 48, 1)
display.show()
# [END lopaka generated]
drawPlant(display)Suas quatro linhas criam um barramento I2C nos pinos GP5 (SCL) e GP4 (SDA) do Pico e um objeto de display de 128×64 nele. O último argumento da maioria das chamadas, 1, é a cor: pixel aceso.
Os números que mudaram
Dois tipos de números neste código diferem do inspetor:
- As folhas. No inspetor, a folha da esquerda tem X
9, Y26e raio5: o canto superior esquerdo da caixa ao redor dela.ellipse()recebe o centro, então o código dizellipse(14, 31, 5, 5, ...). OTrueno fim preenche ela. - O texto. Você colocou
BASILem Y11, e o código diztext("BASIL", 44, 2, ...). Otext()do MicroPython recebe o canto superior esquerdo do texto, então o Lopaka converte a posição para você.
Etapa 4: execute ele no simulador
O simulador executa MicroPython real em um Raspberry Pi Pico simulado:
- Crie um novo projeto Raspberry Pi Pico com MicroPython.
- Adicione um display OLED SSD1306 e conecte quatro fios: GND ao GND, VCC ao 3V3, SCL ao GP5 e SDA ao GP4.
- Adicione um segundo arquivo,
ssd1306.py, com o driver do display do link no comentário do código gerado. Sua primeira linha importaSSD1306_I2Cdele. Sem esse arquivo, o programa para noimport. - Cole o código copiado em
main.pye pressione Play.
A planta aparece no display. Não há etapa de compilação: o interpretador executa o arquivo como ele está.
Em um Pico real, copie os dois arquivos, main.py e ssd1306.py, para a placa. A placa executa main.py toda vez que começa.
Etapa 5: um projeto CircuitPython
Agora a mesma tela em CircuitPython. Um projeto mantém a plataforma que você escolheu, então a nova plataforma precisa de um novo projeto: + New Project, plataforma CircuitPython, tamanho 128×64.
Coloque as mesmas camadas com os mesmos números e dê à tela e às camadas os mesmos nomes. Duas coisas parecem diferentes no inspetor:
- Um círculo tem um raio, R, em vez de dois.
- O texto usa Terminus, a fonte integrada ao CircuitPython. O campo Size dela multiplica a fonte:
1é o tamanho normal.

Etapa 6: objetos em um grupo
O código
O código parece diferente. Cada forma é um objeto: um Rect, um Circle, um Line ou um Label para texto. Cada objeto é adicionado a um grupo, e o display mostra aquele grupo:
import board, busio, displayio, terminalio
import adafruit_displayio_ssd1306
from adafruit_display_text import label
from adafruit_display_shapes.rect import Rect
from adafruit_display_shapes.circle import Circle
from adafruit_display_shapes.line import Line
displayio.release_displays()
i2c = busio.I2C(board.GP5, board.GP4)
bus = displayio.I2CDisplay(i2c, device_address=0x3C)
display = adafruit_displayio_ssd1306.SSD1306(bus, width=128, height=64)
# [BEGIN lopaka generated]
# Init display
Plant = displayio.Group()
display.root_group = Plant
def drawPlant():
# rim
rim = Rect(8, 42, 26, 5, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(rim)
# pot
pot = Rect(11, 47, 20, 15, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(pot)
# stem
stem = Line(21, 24, 21, 41, color=0xFFFFFF)
Plant.append(stem)
# leaf_left
leaf_left = Circle(14, 31, 5, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(leaf_left)
# leaf_right
leaf_right = Circle(28, 27, 5, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(leaf_right)
# leaf_top
leaf_top = Circle(21, 17, 6, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(leaf_top)
# title
title = label.Label(terminalio.FONT, text="BASIL", color=0xFFFFFF)
title.x = 44
title.y = 6
Plant.append(title)
# rule
rule = Line(44, 13, 123, 13, color=0xFFFFFF)
Plant.append(rule)
# caption
caption = label.Label(terminalio.FONT, text="MOISTURE", color=0xFFFFFF)
caption.x = 44
caption.y = 22
Plant.append(caption)
# bar
bar = Rect(44, 30, 80, 10, outline=0xFFFFFF)
Plant.append(bar)
# level
level = Rect(46, 32, 21, 6, fill=0xFFFFFF, outline=0xFFFFFF)
Plant.append(level)
# hint
hint = label.Label(terminalio.FONT, text="WATER ME!", color=0xFFFFFF)
hint.x = 44
hint.y = 52
Plant.append(hint)
# [END lopaka generated]
drawPlant()
while True:
passAs onze linhas acima dos marcadores são suas: os imports para as formas e o rótulo de texto, e a configuração do display nos mesmos pinos, GP5 e GP4, no endereço I2C usual destes módulos, 0x3C. displayio.release_displays() libera o display caso uma execução anterior ainda segure ele. Abaixo dos marcadores, drawPlant() monta o grupo, e while True: pass mantém o programa executando. Quando um programa CircuitPython termina, a placa pega o display de volta para a própria saída de texto dela, e a planta sumiria.
O display continua mostrando o grupo
display.root_group = Plant diz ao display qual grupo mostrar. A partir daí, o display redesenha aquele grupo sozinho. Seu código nunca envia um quadro: se você move um objeto ou muda o texto de um rótulo, a tela acompanha.

Cores são valores hexadecimais de 24 bits: 0xFFFFFF é branco, um pixel aceso neste display. E title.y = 6 é o meio da linha de texto: um Label é posicionado pela borda esquerda dele e pelo centro vertical dele, então o Lopaka converte o Y 11 do inspetor.
Execute ele no simulador
Crie um novo projeto Raspberry Pi Pico com CircuitPython e ligue o display como na etapa 4. Adicione um arquivo requirements.txt com as três bibliotecas de display da Adafruit que o código importa:
adafruit_displayio_ssd1306
adafruit_display_text
adafruit_display_shapesCole o código em code.py e pressione Play. As bibliotecas carregam quando a simulação começa, e a mesma planta aparece. Em uma placa real, copie essas três bibliotecas para a pasta lib da unidade CIRCUITPY.
Etapa 7: um projeto ESPHome
ESPHome é feito para dispositivos de casa inteligente: você descreve o dispositivo em um arquivo YAML, e ele aparece no Home Assistant. Crie um novo projeto com a plataforma ESPHome, tamanho 128×64, e coloque as mesmas camadas com os mesmos números.
O texto funciona de outro jeito aqui. ESPHome usa fontes TrueType em qualquer tamanho, e a lista Font oferece Courier Prime, Fira Code, Merriweather, Montserrat, Roboto, Roboto Mono e Ubuntu. Selecione BASIL, escolha Roboto e defina Size como 12. O próximo texto que você adicionar mantém Roboto, mas começa no tamanho 14, então defina 12 em MOISTURE e WATER ME! também. Cada fonte e tamanho que você usa vira uma entrada separada no código, e o dispositivo armazena cada um.
No ESPHome, o Y de um texto é a parte inferior da caixa de texto dele, e Roboto 12 faz uma caixa de 14 pixels de altura. Em Y 11, o topo de BASIL ficaria para fora da tela. Mova os três textos um pouco mais para baixo: Y 14, 30 e 59.

Etapa 8: uma fonte e uma lambda
O que o Lopaka escreve
No ESPHome, o design vira duas peças de YAML: uma seção font: que puxa a fonte do Google Fonts, e uma lambda, algumas linhas de C++ que moram dentro do arquivo YAML:
# [BEGIN lopaka generated]
font:
- file: 'gfonts://Roboto'
id: Roboto
size: 12
display:
lambda: |-
// rim
it.filled_rectangle(8, 42, 26, 5);
// pot
it.filled_rectangle(11, 47, 20, 15);
// stem
it.line(21, 24, 21, 41);
// leaf_left
it.filled_circle(14, 31, 5);
// leaf_right
it.filled_circle(28, 27, 5);
// leaf_top
it.filled_circle(21, 17, 6);
// title
it.print(44, 0, id(Roboto), "BASIL");
// rule
it.line(44, 13, 123, 13);
// caption
it.print(44, 16, id(Roboto), "MOISTURE");
// bar
it.rectangle(44, 30, 80, 10);
// level
it.filled_rectangle(46, 32, 21, 6);
// hint
it.print(44, 45, id(Roboto), "WATER ME!");
# [END lopaka generated]it é o display. filled_circle recebe o centro e o raio, como o ellipse do MicroPython. print(44, 0, ...) recebe o canto superior esquerdo da caixa de texto: a caixa de BASIL vai da linha 0 à linha 14, e o inspetor mostra a parte inferior dela, 14.
Coloque ele na configuração do seu dispositivo
O resto do arquivo YAML descreve seu dispositivo, e você escreve ele: a placa, os pinos I2C e o display. Coloque a seção font: do Lopaka no nível superior e a lambda: dele sob o seu display, com o recuo como está:
esphome:
name: plant-monitor
esp32:
board: esp32dev
framework:
type: esp-idf
i2c:
sda: 21
scl: 22
font:
- file: 'gfonts://Roboto'
id: Roboto
size: 12
display:
- platform: ssd1306_i2c
model: 'SSD1306 128x64'
address: 0x3C
lambda: |-
// rim
it.filled_rectangle(8, 42, 26, 5);
// ...the rest of the lambda from LopakaCopie a lambda inteira, de // rim ao último it.print. A única linha que o Lopaka não oferece sob display: é - platform: ssd1306_i2c com o modelo e o endereço dela: essa parte diz ao ESPHome qual display você tem.
Etapa 9: monte o firmware e execute ele
ESPHome é compilado. Salve o arquivo como plant.yaml e execute esphome compile plant.yaml. ESPHome transforma o arquivo inteiro em C++, com sua lambda dentro dele linha por linha, e compila firmware para o ESP32.

No simulador, crie um projeto ESP32 e conecte o display: SDA ao pino 21, SCL ao pino 22, GND ao GND e VCC ao 3V3. Carregue o firmware.factory.bin compilado com o comando Upload Firmware and Start Simulation do simulador. A planta está na tela.
Em um ESP32 real, esphome run plant.yaml monta o mesmo firmware e grava ele por USB. Como o desenho mora em uma lambda, as mesmas linhas podem imprimir valores de sensor ao vivo depois, por exemplo a umidade real em vez de uma barra fixa.
Etapa 10: um projeto Flipper Zero
O Flipper Zero tem a própria tela, também de 128×64 pixels, preta sobre laranja. Crie um novo projeto com a plataforma Flipper Zero, One. Esta plataforma não tem tamanho padrão: abra Screen Size e escolha Zero Horizontal (128×64).
Coloque as mesmas camadas com os mesmos números. O Lopaka conhece as quatro fontes integradas do Flipper: FontPrimary, FontSecondary, FontKeyboard e FontBigNumbers. Texto novo começa em FontPrimary, que é a fonte para os três textos aqui.

Etapa 11: C para a tela de desenho
O Lopaka escreve C para a tela de desenho do Flipper:
// [BEGIN lopaka generated]
void drawPlant(Canvas* canvas) {
canvas_set_bitmap_mode(canvas, true);
// rim
canvas_draw_box(canvas, 8, 42, 26, 5);
// pot
canvas_draw_box(canvas, 11, 47, 20, 15);
// stem
canvas_draw_line(canvas, 21, 24, 21, 41);
// leaf_left
canvas_draw_disc(canvas, 14, 31, 5);
// leaf_right
canvas_draw_disc(canvas, 28, 27, 5);
// leaf_top
canvas_draw_disc(canvas, 21, 17, 6);
// title
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 44, 11, "BASIL");
// rule
canvas_draw_line(canvas, 44, 13, 123, 13);
// caption
canvas_draw_str(canvas, 44, 27, "MOISTURE");
// bar
canvas_draw_frame(canvas, 44, 30, 80, 10);
// level
canvas_draw_box(canvas, 46, 32, 21, 6);
// hint
canvas_draw_str(canvas, 44, 57, "WATER ME!");
}
// [END lopaka generated]canvas_draw_frame desenha um contorno, canvas_draw_box um retângulo preenchido e canvas_draw_disc um círculo preenchido ao redor do centro dele. O y de canvas_draw_str é a linha de base, o mesmo número do inspetor, então os textos mantêm 11, 27 e 57. Os três textos usam FontPrimary, então canvas_set_font aparece uma vez.
Esta função vai para o callback de desenho de um aplicativo Flipper: a função que o Flipper chama sempre que redesenha a tela do seu aplicativo. Você monta o aplicativo com as próprias ferramentas do Flipper.

Cinco plataformas lado a lado
Lado a lado: um Arduino Uno, um ESP32 executando ESPHome, um Pico com MicroPython, um Pico com CircuitPython, e a versão Flipper Zero, pronta para o aplicativo dela. O Arduino Uno executa o mesmo design em um projeto U8g2, configurado como no episódio 1, com a fonte 6x13B.
O que muda entre plataformas
Os mesmos números, palavras diferentes
Olhe a barra de umidade nas cinco plataformas. Quarenta e quatro, trinta, oitenta, dez: os números são os mesmos em todo lugar. Só as palavras ao redor deles mudam: o nome da chamada, o 1 para a cor no MicroPython, o outline= no CircuitPython, o canvas na frente no Flipper.

Cada plataforma tem as próprias fontes
As fontes também mudam, porque cada plataforma tem o próprio conjunto: Petme 8×8 no MicroPython, Terminus no CircuitPython, Roboto 12 no ESPHome, FontPrimary no Flipper Zero e 6x13B no U8g2. É por isso que os textos precisaram de um Y diferente no ESPHome. O Lopaka desenha cada projeto com as próprias fontes da plataforma, então a tela de desenho mostra o texto que você vai obter no dispositivo.

Recapitulação
- Compilado ou interpretado, os pixels continuam os mesmos: aplicativos Arduino, ESPHome e Flipper são compilados em firmware; MicroPython e CircuitPython executam um arquivo de texto por um interpretador.
- Escolha a plataforma quando criar o projeto: a plataforma decide a linguagem do código, e cada plataforma precisa do próprio projeto.
- As mesmas camadas, uma nova linguagem: coloque as mesmas camadas com os mesmos números, e o Lopaka escreve o código na linguagem daquela plataforma.
Próximo episódio: E-paper & Shipping. Telas que mantêm a imagem sem energia. Leia o episódio 10 ou assista no YouTube.
Pronto para testar? Abra o Lopaka e leve sua tela para uma nova plataforma.