Lua en STM32

¡Hola!

A veces quieres probar rápidamente algo en el microcontrolador, programar un pequeño prototipo funcional de una idea. Para estos fines, como saben, los lenguajes de script son muy adecuados. En este artículo quiero decir cómo usar Embox para ejecutar el intérprete de Lua (estándar, no eLua) en STM32. Para demostrarlo, parpadeamos el LED a través de la red usando la biblioteca luasocket y también trabajamos un poco con http.



Lua es un lenguaje de programación de secuencias de comandos cuyo intérprete es lo suficientemente ligero como para integrarse fácilmente en otros proyectos, además de una licencia gratuita de MIT. Hemos estado interesados ​​en este idioma durante mucho tiempo, por lo tanto, bajo Embox, hay soporte Lua en qemu i386. Y dado que el intérprete es liviano y compatible con POSIX, surgió la idea de lanzar Lua de la misma manera que lo hicimos con otras bibliotecas como Pjsip , OpenCV , Qt .

Notaré de inmediato que dado que comenzaremos con el soporte de luasocket, elegiremos no stm32f4-discovery (1 MB flash, 192 KB RAM), sino un poco más - stm32f7-discovery(1 MB de memoria flash, 320 KB de RAM y SDRAM adicional). Al mismo tiempo, no hay duda de que la versión básica de Lua sin soporte de red puede comenzar fácilmente en stm32f4 (ver más abajo).

Para comenzar, preparemos un ejemplo simple: calcular el enésimo número de Fibonacci:

function fib(n)
    if n < 3 then
        return 1
    else
        return fib(n - 1) + fib(n - 2)
    end
end

print("fib(7) = " .. fib(7))

Comencemos primero con Linux y veamos cuánta memoria tomó. Y luego lo transferiremos a Embox sin cambios. Primero, descargue la lua fresca. Descargué 5.3.5 y lo compilé como "make linux". Pero, de hecho, para nuestros propósitos, puede ponerlo desde el repositorio. A continuación, ejecute nuestro fib.lua:

$ valgrind --tool=massif --massif-out-file=fib.massif lua fib.lua
$ ms_print fib.massif > fib.out

Ahora puede buscar en fib.out y descubrir que la cantidad máxima de memoria asignada en el almacenamiento dinámico es de aproximadamente 30 Kb. Aquí, por supuesto, no diré que este es el tamaño mínimo. Por ejemplo, en el artículo en la sección “Requisitos para RAM”, se dan requisitos sustancialmente menores, pero solo al momento de encender la máquina Lua. Pero, en cualquier caso, 30 Kb parece alentador: tenemos 320 Kb.

Ahora construya la plantilla arm / stm32f746g-discovery-lua para Embox:

$ make confload-arm/stm32f746g-discovery-lua
$ make -j $(nproc)

Firmware build / base / bin / embox en el tablero como se indica en nuestra wiki y ejecuta:

embox> lua fib.lua 
fib(7) = 13

Genial, todo es exactamente como en Linux (no es sorprendente :)).

Vamos a complicar la tarea, pero ahora la red es una necesidad en casi todas partes. Envíe una solicitud HTTP / GET en algún lugar de Internet. Para hacer esto, necesita la biblioteca luasocket . Luego no comencé a recopilar los códigos fuente, sino que usé el paquete listo ("sudo apt-get install lua-socket").

Para enviar una solicitud HTTP, use este sencillo ejemplo:

local http = require("socket.http")

if not arg or not arg[1] then
    print("lua http_request.lua <url>")
    os.exit(0)
end

local body, code = http.request(arg[1])
print(code)
print(body)

os.exit(code == 200)

Nuevamente, todo se puede verificar en Linux:

lua http_request.lua http://example.com

Devolverá el código 200 y el contenido de la página. Veamos eso de memoria. Si ejecuta valgrind nuevamente, puede ver que el consumo máximo de memoria en el montón ha aumentado a 242 Kb. Así que aquí finalmente descubrí que stm32f4 no encajará sin optimizaciones adicionales. Parece que es posible obtener stm32f7: hay 320 Kb, pero teniendo en cuenta que todavía hay un sistema operativo con un sistema de archivos y una red, decidí jugar un poco con un montón de SDRAM externa.

Activamos (en la configuración, esto ya está incluido) el montón habitual en RAM, y el adicional en SDRAM.

    include embox.mem.static_heap(heap_size=0x10000)
    include embox.mem.fixed_heap(heap_size=0x40000, heap_start=0x60000000)

Comprobamos stm32f7-discovery, funciona como en Linux.

Finalmente parpadeemos el LED "remotamente".

local socket = require("socket")

port = arg[1] or 1027
udp = assert(socket.udp())
assert(udp:setsockname('*', port))
print("Lua UDP server started on port " .. port .. "...")
while 1 do
    cmd, ip, port = assert(udp:receivefrom())
    print("Execute '" .. cmd .. "' from " .. ip .. ":" .. port)
    os.execute(cmd)
end

Aquí solo obtenemos los datos UDP del puerto especificado y los pasamos a os.execute (). Como un comando que se ejecutará, enviaremos un comando pin desde el cliente. Este es un comando simple en Embox que controla GPIO; por ejemplo, para stm32f7-disco necesita ejecutar "pin gpioi 1 toggle", lo que significa cambiar la primera línea a GPIOI.

Un ejemplo estándar de luasocket se tomará como cliente:

echoclnt.lua
-----------------------------------------------------------------------------
-- UDP sample: echo protocol client
-- LuaSocket sample files
-- Author: Diego Nehab
-----------------------------------------------------------------------------
local socket = require("socket")

host = "localhost"
port = 1027
if arg then
    host = arg[1] or host
    port = arg[2] or port
end

udp = assert(socket.udp())
assert(udp:setpeername(host, port))
print("Using remote host '" ..host.. "' and port " .. port .. "...")
while 1 do
    line = io.read()
    if not line or line == "" then os.exit() end
    assert(udp:send(line))
end


Como de costumbre, primero verifique en Linux. Valgrind muestra que el consumo máximo de memoria en el montón es de aproximadamente 70 Kb, que es más de 2 veces más que el caso con Fibonacci (30 Kb), pero significativamente menor que en el ejemplo con HTTP (242 Kb).

Inmediatamente demuestre un ejemplo de lanzamiento en Embox.

En Linux:

$ lua echoclnt.lua 192.168.0.128 1027
Using remote host '192.168.0.128' and port 1027...
pin gpioi 1 toggle
pin gpioi 1 toggle

En Embox:

embox>                                                   
embox>lua udp_server.lua                                                        
Lua UDP server started on port 1027...
Execute 'pin gpioi 1 toggle' from 192.168.0.102:34300
Execute 'pin gpioi 1 toggle' from 192.168.0.102:34300

Conclusión


Logramos ejecutar Lua + luasocket en stm32f7-discovery. Y con su ayuda, parpadee el LED de forma remota a través de la red. Y resultó ser bastante simple, porque como se señaló al principio, los lenguajes de script pueden ahorrar tiempo en la creación de prototipos.

También quiero señalar que existe un proyecto de eLua que se ejecuta en metal desnudo sin un sistema operativo. Pero tiene varios matices. En primer lugar, soporte de red limitado: UDP no está allí, y la documentación dice que la red está "en progreso" por ahora. En segundo lugar, no pude encontrar soporte para ningún tipo de stm'ku F3, F4 (solo F1). Y en general, debe estar de acuerdo, es mucho más útil utilizar proyectos ampliamente respaldados, y en este sentido, elua es, por supuesto, inferior al proyecto principal.

Todos los ejemplos descritos en el artículo están en nuestro repositorio.

Embox también tiene otros lenguajes de script: python (tinipy), Ruby (mruby), Tcl. Pero esto está más allá del alcance de este artículo :)

Nuestros contactos:
Boletín informativo: embox-ru@googlegroups.com
Telegram chat: t.me/embox_chat

All Articles