Este artigo compõe a série Primeiros Passos com o ESP32 e o Apache NuttX. Uma série cujo objetivo é apresentar uma introdução ao Sistema Operacional de Tempo Real Apache NuttX e prover instruções para utilizar o Apache NuttX no ESP32.
Caso você esteja iniciando no universo Apache NuttX, recomendo a leitura deste artigo, no qual Sara Monteiro explica o processo de preparação do ambiente de desenvolvimento e descreve os passos necessários para compilar o Apache NuttX e fazer o upload do firmware para a memória Flash do ESP32.
O que é Lua?
Segundo o seu próprio criador, Lua é uma linguagem de programação interpretada, de script em alto nível, com tipagem dinâmica e multiparadigma, reflexiva e leve, projetada por Tecgraf da PUC-Rio em 1993. Seu objetivo é expandir aplicações em geral, de forma extensível, para prototipagem e para ser embarcada em softwares complexos.
Lua é muito usada em diversas aplicações, inclusive em software embarcado. A motivação em usar Lua está no fato de mudar o comportamento de um dispositivo de forma dinâmica e remota sem necessariamente ter que fazer um upload de um novo firmware.
Podemos manter o mesmo firmware, que é uma aplicação que contém o interpretador de Lua, e assim apenas atualizar o(s) seu(s) script(s). Isso exige muito menos comunicação de dados e é uma operação potencialmente mais segura, uma vez que o script Lua pode ser executado como se estivesse em um ambiente de sandbox.
Outra motivação é que via Lua é possível criar programas que serão executados no NuttX, sem necessariamente ter que saber como escrever um programa em C usando as APIs e modelos POSIX do NuttX. Para alguns leitores essa pode ser uma forma interessante de ingressar no mundo Embarcado!
O que é o NuttX?
O NuttX é um sistema operacional de tempo real (RTOS) de grande versatilidade e facilidade de uso, ao mesmo tempo que segue o padrão POSIX utilizado em diversos sistemas operacionais. Para mais informações sobre o NuttX sugiro a leitura série de artigos disponíveis no Portal Embarcados sobre o tema.
Uma das características do NuttX é ter suporte a diversos fabricantes de MCUs. Para demonstrar o uso do interpretador Lua embarcado, vamos usar uma placa ESP-WROVER-KIT da Espressif. Para mais informações sobre o hardware utilizado, acesse a documentação oficial da placa.
Essa placa tem alguns periféricos acessíveis que vamos usar:
- LED de usuário
- Botão de Usuário
- Slot para MMC/SD Card
Caso desejado, como possível alternativa, o leitor poderá utilizar um DevKit baseado no ESP32, desde que monte um circuito equivalente ao usado no do ESP-WROVER-KIT.
Nesse caso as conexões necessárias são:
- Um LED conectado ao GPIO4 do ESP32 através de um resistor de 330 ohms;
- Um botão conectado ao GPIO0 do ESP32 em pullup (usualmente o botão “BOOT” de um DevKit);
- UART0 usada para comunicação serial do Console do NuttX;
- Um módulo de Cartão SD Card ou Micro SD ligado aos pinos GPIO2 (MISO), GPIO15 (MOSI), GPIO14 (CLK), GPIO13 (CS), além dos pinos de VCC e GND.
A placa ESP-WROVER-KIT tem todas essas conexões e outros mais dispositivos:
O NuttX usa o RGB LED acima como userled e o Boot Button em seu subsistema de botões.
O RGB LED dessa placa está ligado a 3 LEDs, através do GPIO0 (Red), GPIO2 (Green) e GPIO4 (Blue). O leitor pode se questionar sobre o GPIO0 também ser usado pelos botões e o GPIO2 como MISO no subsistema MMC/SD. Neste caso, ao usar os três subsistemas simultaneamente, somente o LED Azul (GPIO4) será acionado pelo subsistema userled. Os LEDs Vermelho e Verde ficarão sempre ao nível lógico alto (ligados) e suas portas serão controladas pelos subsistemas Button e MMCSD. Dessa maneira, evitamos o conflito entre os GPIOs utilizados.
Configurando NuttX e Lua
Para preparar a compilação do sistema NuttX para a execução do interpretador Lua na placa ESP-WROVER-KIT, devemos utilizar a configuração incluída no próprio RTOS:
cd ~/nuttxspace/nuttx
make distclean
./tools/configure.sh esp32-wrover-kit:lua
make -j8 download ESPTOOL_PORT=/dev/ttyUSB0
Com isso aplicamos a configuração necessária com o interpretador Lua e os subsistemas necessários, fazemos a compilação e o carregamento para a placa. Não se esqueça de verificar se a porta conectada à sua placa é, de fato, a /dev/ttyUSB0.
Logo após o upload, será possível iniciar o console do NuttX via porta serial, usando o programa de sua preferência, como por exemplo, picocom, minicom ou miniterm.
picocom /dev/ttyUSB0 -b 115200Ao conectar e apertar a tecla Enter, o console do NuttX deverá ser visível. Tente os comandos help e ls /dev para observar o que está disponível ao usuário.
nsh> help
help usage: help [-v] [<cmd>]
. cd dmesg free mkfatfs pwd test unset
[ cp echo help mkrd rm time uptime
? cmp env hexdump mount rmdir true usleep
basename dirname exec kill mv set truncate xd
break dd exit ls printf sleep uname
cat df false mkdir ps source umount
Builtin Apps:
buttons sh nsh leds lua
nsh> ls /dev
/dev:
buttons
console
mmcsd0
null
ttyS0
userleds
nsh>
Podemos ver que temos vários comandos disponíveis, como os de exemplos de userleds (leds), botões (buttons) e o interpretador Lua (lua). Assim como temos os device drivers desses subsistemas (/dev/userleds e /dev/buttons) em conjunto com o SDCard (/dev/mmcsd0).
Usando o Interpretador Lua
Para usar o interpretador é muito simples, basta digitar lua no console do NuttX. Podemos verificar a versão do interpretador Lua sendo executado através do comando abaixo:
nsh> lua
Lua 5.4.0 Copyright (C) 1994-2020 Lua.org, PUC-Rio
> print("Temos a versão ".._VERSION)
Temos a versão Lua 5.4
>Em seguida podemos realizar alguns experimentos como, por exemplo, acionar o LED azul. Para ligar o LED, devemos escrever em seu arquivo de device driver (/dev/userleds ) como no exemplo a seguir:
> –- abre o arquivo de device driver para escrita binária
> led = io.open("/dev/userleds", "wb")
> -- escreve o valor 0x01 no buffer do device driver
> led:write(string.pack("I", 1))
file (0x3ffed9c0)
> -- envia todos os dados do buffer para o device driver
> led:flush()
true
>
Para desligar, basta executar o mesmo comando porém utilizando o valor “0” desta vez:
> led:write(string.pack("I", 0))
file (0x3ffed9c0)
> led:flush()
true
>
Em Lua, as funções read() e write() sempre retornam ou tomam como argumento uma String. Portanto, é necessário converter valores numéricos para String e vice-versa. Para usar write(string), vamos usar o método string.pack() para converter números em uma String que contenha esse valor (o carácter equivalente em ASCII).
De forma análoga, ao ler via read(), devemos converter a String de retorno em um número. Para isso existe o método string.byte(string, n), que converte em um byte o caractere n dessa string (nesse caso retorna o valor ASCII do caractere).
Usaremos isso mais adiante no script Lua apresentado neste artigo.
Exemplo, sabendo-se que 65 representa ‘A’ na tabela ASCII:
> print(string.pack("I", 65))
A
> print(string.byte("ABC"))
65
> print(string.byte("ABC", 2))
66
Criando uma aplicação Lua com NuttX
Podemos agora criar diversas aplicações utilizando a linguagem Lua. Nesse exemplo, criaremos uma função que controla o LED, dependendo de um parâmetro booleano passado para a mesma:
-- função que liga ou desliga um USER LED
-- retorna true em caso de sucesso e false caso falhe
function userLed(on)
-- checa o tipo do argumento passado
if type(on) ~= "boolean" then
print("userLed() exige um argumento booleano")
return false
end
-- abre o device driver para escrita binária
local f, err = io.open("/dev/userleds", "wb")
-- testa se o arquivo foi aberto corretamente
if f then
if (on) then
f:write(string.pack("I", 1))
print("userLed(ligado)")
else
f:write(string.pack("I", 0))
print("userLed(desligado)")
end
f:flush()
f:close()
return true
else
print("Erro ao abrir o arquivo de dispositivo UserLeds: " .. err)
return false
end
end
A função acima permite controlar facilmente o LED Azul da placa, bastando para isso apenas chamar userLed(true) para ligar o LED ou userLed(false) para desligá-lo.
Nosso código está ficando grande para ser digitado toda vez… Portanto vamos editar e gravar um arquivo chamado teste.lua em um cartão microSD em um computador para em seguida inserir esse cartão microSD no slot da placa ESP-WROVER-KIT.
Para acessar o sistema de arquivos FAT do cartão microSD no NuttX, devemos montar o cartão no sistema de arquivos utilizando o comando mount:
nsh> mount -t vfat /dev/mmcsd0 /mntAgora, para executar o script criado no SDCard, devemos executar o interpretador Lua passando o caminho do script que criamos como argumento:
nsh> lua mnt/teste.luaExemplo de Script em Lua
Irei demonstrar um script simples que utiliza o botão BOOT da placa para, ao ser pressionado, ligar ou desligar o LED Azul. Com isso demonstramos o uso de GPIOs para leitura e para escrita digital, através do NuttX e do Lua.
Para isso, além da função de controle do LED Azul da placa, iremos criar uma função de leitura do botão BOOT da placa (GPIO0), como demonstrado abaixo:
-- função que lê o estado do botão BOOT
-- retorna um par de valores: o estado do Botão (true = apertado)
-- e no segundo elemento, true se houve sucesso na operação
function button()
local f, err = io.open("/dev/buttons", "rb") -- leitura binária
if f then
-- lê 32 bits em uma string
local buttonVal = f:read(4)
f:close()
-- checa o estado do botão BOOT
if (string.byte(buttonVal) ~= 0) then
return true, true
else
return false, true
end
else
print("Erro ao abrir o arquivo de dispositivo do Botão: " .. err)
return nil, false
end
endNeste caso, vemos que fazemos a leitura do /dev/buttons para saber o estado do botão e assim devolver true caso esteja apertado e false caso não.
Por fim, criaremos um loop infinito que irá verificar quando o botão é pressionado/solto para então ligar/desligar o LED.
-- iniciando com o LED desligado
LEDState = false
userLed(LEDState)
lastButtonState = false
while true do
local currentButtonState, returnCodeOK = button()
if returnCodeOK then
-- se o botão é apertado, inverte o estado do LED
if lastButtonState ~= currentButtonState then
lastButtonState = currentButtonState
if currentButtonState then
-- inverte o estado do LED
print("---- Botão Apertado")
LEDState = not LEDState
userLed(LEDState)
else
print("---- Botão Solto")
end
end
else
print("FALHA --- main loop button()")
end
-- executa o gerenciamento automático de memória
collectgarbage("collect")
end
Para fazer o programa final funcionar, junte, na mesma ordem aqui apresentada, o código das duas funções com o bloco principal do script Lua acima. Em seguida grave o arquivo teste.lua com todo o código e execute o script como já explicado anteriormente.
O resultado deverá ser semelhante ao abaixo:
nsh> lua mnt/main.lua
userLed(desligado)
---- Botão Apertado
userLed(ligado)
---- Botão Solto
---- Botão Apertado
userLed(desligado)
---- Botão Solto
---- Botão Apertado
userLed(ligado)
---- Botão Solto
---- Botão Apertado
userLed(desligado)
---- Botão Solto
---- Botão Apertado
userLed(ligado)
---- Botão Solto
---- Botão Apertado
userLed(desligado)
---- Botão Solto
Conclusão
Nesse tutorial vimos como configurar o NuttX para a utilização do interpretador Lua e a execução de alguns scripts de exemplo.
Caso o usuário deseje mudar o GPIO do LED, acrescentar mais LEDs/GPIOs a ser controlados ou mesmo adicionar mais GPIOs que serão monitorados pelo subsistema de botões, bastaria criar uma configuração para uma placa no NuttX e adicionar o código necessário nos arquivos do sistema. Mas isso ficará para um próximo artigo!
Bom proveito e até a próxima!
Referências
- Documentação do NuttX https://nuttx.apache.org/docs/latest/index.html
- Documentação da placa ESP-WROVER-KIT https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-wrover-kit.html
- Site da linguagem de programação Lua https://www.lua.org/portugues.html
- Manual de referência da linguagem de programação Lua 5.4 https://www.lua.org/manual/5.4/
- Exemplos em NuttX usados para criar os scripts em Lua:
Autores
Rodrigo Garcia
Bacharel em engenharia de computação, formado pela UNICAMP com especialização em Economia Financeira também pela UNICAMP. Trabalhou em diversas empresas da área de TI e Telecomunicações e atualmente atua como Engenheiro de Software Embarcado na equipe de desenvolvimento do Core Arduino da Espressif Systems.
Lucas Saavedra Vaz
Bacharel em Ciência e Tecnologia e Engenharia de Computação pela Universidade Federal de São Paulo com publicações na área de rádio LoRa para nanossatélites. Começou trabalhando com desenvolvimento de PCBs, Zephyr RTOS e Linux embarcado aplicado a estações solarimétricas. Atualmente trabalha como Engenheiro de Software Embarcado na equipe de NuttX da Espressif Systems.




