Lua на STM32

от автора

Привет!

Иногда хочется быстро что-то попробовать на микроконтроллере, запрограммировать маленький работающий прототип какой-то идеи. Для этих целей, как известно, хорошо подходят скриптовые языки. В этой статье я хочу рассказать, как с помощью Embox запустить интерпретатор Lua (cтандартный, не eLua) на STM32. Для демонстрации помигаем светодиодом по сети с помощью библиотеки luasocket, а также немного поработаем с http.

Lua — это скриптовый язык программирования, интерпретатор которого является достаточно легковесным, чтобы его можно было легко встраивать в другие проекты, плюс свободная лицензия MIT. Нам давно был интересен этот язык, поэтому под Embox имеется поддержка Lua на qemu i386. А так как интерпретатор легковесный и POSIX-совместимый, появилась идея запустить Lua так же, как мы уже проделали с другими библиотеками типа Pjsip, OpenCV, Qt.

Сразу отмечу, что так как запускать будем с поддержкой luasocket, то выберем не stm32f4-discovery (1 Мб флэш, 192 Кб ОЗУ), а чуть побольше — stm32f7-discovery (1 Мб флэш, 320 Кб ОЗУ, и имеется дополнительная память SDRAM). При этом нет сомнений, что базовая версия Lua без поддержки сети легко запустится и на stm32f4 (увидим дальше).

Для начала подготовим простой пример — вычисление n-го числа Фибоначчи:

function fib(n)     if n < 3 then         return 1     else         return fib(n - 1) + fib(n - 2)     end end  print("fib(7) = " .. fib(7)) 

Давайте сначала запустим его под Линуксом, и посмотрим сколько потребовалось памяти. А потом без изменений перенесем в Embox. Сначала скачиваем свежий lua. Я загрузил 5.3.5, и собрал как “make linux”. Но на самом деле для наших целей можно и из репозитория поставить. Далее, запускаем наш fib.lua:

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

Теперь можно посмотреть в fib.out и выяснить, что максимальный объем выделяемой памяти в куче около 30 Кб. Тут я конечно же не буду говорить, что это минимальный размер. Например, в статье в разделе “Требования к оперативной памяти” приводятся существенно меньшие требования, но только лишь на момент включения Lua-машины. Но в любом случае 30 Кб выглядят обнадеживающими — у нас то 320 Кб есть.

Теперь собираем темплейт arm/stm32f746g-discovery-lua для Embox:

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

Прошиваем build/base/bin/embox на плату как указано на нашем wiki и запускаем:

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

Отлично, все в точности как на Линуксе (не удивительно :)).

Давайте усложним задачу, все-таки сейчас сеть — это необходимость почти всюду. Отправим HTTP /GET запрос куда-нибудь в интернет. Для этого понадобится библиотека luasocket. Тут я уже не стал собирать из исходников, а воспользовался готовым пакетом («sudo apt-get install lua-socket»).

Для отправки HTTP запроса используем вот такой простой пример:

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) 

Опять же, все можно проверить на Линуксе:

lua http_request.lua http://example.com 

Вернет код 200 и содержимое страницы. Давайте посмотрим что по памяти. Если снова запустить valgrind, то видно, что пик потребления памяти в куче возрос до 242 Кб. Так что вот тут я окончательно и выяснил, что в stm32f4 будет не влезть без дополнительных оптимизаций. На stm32f7 влезть вроде бы можно — там 320 Кб, но учитывая что еще есть и ОС c файловой системой, сетью, то немного повозившись я решил остановиться на том, что использую внешнюю SDRAM под кучу.

Включаем (в конфиге это уже включено) обычную кучу в RAM, и дополнительную в SDRAM.

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

Проверяем на stm32f7-discovery, работает как на Линуксе.

Давайте, наконец, помигаем светодиодом “удаленно”.

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 

Здесь мы просто получаем UDP данные с указанного порта и передаем их в os.execute(). В качестве команды, которая будет исполняться будем посылать из клиента команду “pin”. Это простая команда в Embox, которая управляет GPIO — например, для stm32f7-disco нужно выполнить “pin gpioi 1 toggle”, что означает изменить 1-ую линию на GPIOI.

В качестве клиента будет взят стандартный пример из luasocket:

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 

Как обычно, сначала проверяем на Линуксе. Valgrind показывает, что пиковый расход памяти в куче составляет порядка 70 Кб, что более чем в 2 раза больше, чем случае с Фибоначчи (30 Кб), но существенно меньше, чем в примере с HTTP (242 Кб).

Сразу продемонстрирую пример запуска на Embox.

На 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 

На Еmbox:

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 

Заключение

Нам удалось запустить Lua + luasocket на stm32f7-discovery. И с их помощью помигать светодиодом удаленно по сети. Причем получилось это довольно просто, ведь как отмечалось в начале, скриптовые языки позволяют экономить время на прототипировании.

Также хочу отметить, что есть такой проект eLua, который запускается на голом железе без ОС. Но у него есть несколько нюансов. Во-первых, ограниченная поддержка сети: UDP там нет, и в документации говорится, что сеть пока “in progress”. Во-вторых, я не смог найти поддержку ни под одну stm’ку типа F3, F4 (только F1). Да и в целом, согласитесь, куда полезнее использовать широко поддерживаемые проекты, и в этом плане elua конечно уступает основному проекту.

Все примеры, описанные в статье, есть у нас в репозитории.

В Embox также имеются и другие скриптовые языки — python (tinipy), Ruby (mruby), Tcl. Но это уже за рамками данной статьи 🙂

Наши контакты:
Рассылка: embox-ru@googlegroups.com
Телеграмм чат: t.me/embox_chat

ссылка на оригинал статьи https://habr.com/ru/company/embox/blog/496146/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *