Pular para o conteúdo

Lopaka Basics 09: além do Arduino ​

Cinco dispositivos mostrando a mesma tela de monitor de plantas com um vaso, BASIL, MOISTURE, uma barra e WATER ME!, um Arduino Uno, um ESP32 com ESPHome, um Pico com MicroPython, um Pico com CircuitPython e um Flipper Zero

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.

Compilado versus interpretado: em cima, um sketch passa por um compilador e vira firmware que é gravado de novo a cada mudança, para Arduino, ESPHome e aplicativos Flipper; abaixo, um interpretador é gravado uma vez e executa main.py no display, salvo e executado de novo, ao custo de velocidade e memória

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.

A lista Platform na caixa de diálogo Create New Project: ESPHome com o selo YAML, Micropython e CircuitPython com o selo Python, e Flipper Zero, One com o selo 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, Y 42, W 26, H 5, e o vaso abaixo dela em X 11, Y 47, W 20, H 15. Ative Filled para os dois.
  • O caule: uma linha de 21, 24 a 21, 41.
  • As folhas: três elipses com Filled ativado e os dois raios iguais: esquerda em X 9, Y 26 com raio 5, direita em X 23, Y 22 com raio 5, e topo em X 15, Y 11 com raio 6.
  • O nome: o texto BASIL em X 44, Y 11, e uma linha sob ele de 44, 13 a 123, 13.
  • A barra de umidade: o texto MOISTURE em X 44, Y 27, um retângulo em X 44, Y 30, W 80, H 10, e um retângulo preenchido dentro dele em X 46, Y 32, W 21, H 6: o nível da água.
  • O lembrete: o texto WATER ME! em X 44, Y 57.

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.

O monitor de plantas no Lopaka em um projeto MicroPython: a camada bar está selecionada com X 44, Y 30, W 80 e H 10 no inspetor, e o painel de código destaca display.rect(44, 30, 80, 10, 1)

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:

python
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, Y 26 e raio 5: o canto superior esquerdo da caixa ao redor dela. ellipse() recebe o centro, então o código diz ellipse(14, 31, 5, 5, ...). O True no fim preenche ela.
  • O texto. Você colocou BASIL em Y 11, e o código diz text("BASIL", 44, 2, ...). O text() 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:

  1. Crie um novo projeto Raspberry Pi Pico com MicroPython.
  2. Adicione um display OLED SSD1306 e conecte quatro fios: GND ao GND, VCC ao 3V3, SCL ao GP5 e SDA ao GP4.
  3. Adicione um segundo arquivo, ssd1306.py, com o driver do display do link no comentário do código gerado. Sua primeira linha importa SSD1306_I2C dele. Sem esse arquivo, o programa para no import.
  4. Cole o código copiado em main.py e 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.

O monitor de plantas no Lopaka em um projeto CircuitPython: a camada de título BASIL está selecionada em X 44, Y 11 com Size 1, e o código mostra title = label.Label(terminalio.FONT, text="BASIL") com title.y = 6

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:

python
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:
    pass

As 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.

Objetos CircuitPython em um grupo: Plant = Group() guarda objetos Rect para a borda, o vaso e a barra, objetos Circle para as folhas e objetos Label para BASIL e os outros textos, e display.root_group mostra o grupo na tela

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_shapes

Cole 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.

O monitor de plantas no Lopaka em um projeto ESPHome: a camada de título BASIL em Roboto, Size 12, em X 44 e Y 14; o painel de código começa com uma seção de fonte, file gfonts://Roboto, id Roboto, size 12

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:

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

yaml
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 Lopaka

Copie 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.

O YAML do dispositivo com a seção de fonte e a lambda destacadas, e uma seta para o src/main.cpp que o ESPHome gera, onde as mesmas linhas filled_rectangle, line, filled_circle e print ficam dentro de set_writer

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.

O monitor de plantas no Lopaka em um projeto Flipper Zero, preto sobre laranja: a camada de título BASIL está selecionada e a lista de fontes mostra FontPrimary, FontSecondary, FontKeyboard e FontBigNumbers

Etapa 11: C para a tela de desenho ​

O Lopaka escreve C para a tela de desenho do Flipper:

c

// [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.

O callback de desenho de um aplicativo Flipper chamando drawPlant(canvas), a função do Lopaka, ao lado de um Flipper Zero mostrando o monitor de plantas preto sobre laranja; monte ele com o SDK de aplicativos 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.

A barra de umidade em cinco linguagens: u8g2.drawFrame(44, 30, 80, 10) para Arduino com U8g2, it.rectangle(44, 30, 80, 10) para ESPHome, display.rect(44, 30, 80, 10, 1) para MicroPython, bar = Rect(44, 30, 80, 10, outline=0xFFFFFF) para CircuitPython e canvas_draw_frame(canvas, 44, 30, 80, 10) para Flipper Zero

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.

O mesmo monitor de plantas com cinco fontes: Petme 8×8 para MicroPython, Terminus para CircuitPython, Roboto 12 para ESPHome, FontPrimary para Flipper Zero e 6x13B para U8g2: os mesmos números, palavras diferentes, as fontes de cada plataforma

Recapitulação ​

  1. 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.
  2. Escolha a plataforma quando criar o projeto: a plataforma decide a linguagem do código, e cada plataforma precisa do próprio projeto.
  3. 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.