NodeMCU Web-server
Большинство найденных в интернете примеров веб-серверов для NodeMCU написанных на Lua - это примеры простых статических страниц с отсутствием возможности "одновременной" загрузки доп. файлов css, js, и т.д. Как правило, html код встраивается непосредственно в скрипт, в результате этого занимает место в памяти. Из найденных примеров веб-серверов, которые более менее соответствовали моим требованиям это nodemcu-httpserver, но он громоздкий, съедает много памяти и есть ряд трудностей в работе с ним. Хотелось что-то оптимальное между базовым функционалом и производительностью, так я начал изобретать свой "велосипед".
Была поставлена задача для проекта:
- Написать основной код (шаблон, для будущих проектов).
- Написать базовую HTML страницу, стили (css) и обработчик (js).
- Добавить возможность настраивать модуль с HTML страницы.
От веб сервера требовалось:
- Возможность загрузки доп. файлов (js, css, ico,txt,jpg).
- Минимальный размер кода в памяти, в режиме ожидания.
- Возможность включение LUA кода в HTML страницу.
- Запуск LUA скриптов и передача им параметров по средствам POST и GET запросов.
- Минимальная аутентификация.
- Возможность загрузки сжатых файлов (.gz).
Структура.
Следуя поставленным задачам появилась вот такая структура проекта.
Инициализация:
- init.lua - инициализации настроек и wi-fi.
- init_settings.lua - получение настроек, так же хранятся настройки по умолчанию.
- init_wifi.lua - подключение к wifi сети.
Сервер состоит из четырех основных скриптов:
- web.lua - сам веб сервер.
- web_request.lua - разбор ответов от клиента.
- web_file.lua - передача файлов, запуск скриптов и загрузка html страниц с кодом lua.
- web_control.lua - аутентификация, сохранения параметров, получения списка точек доступа.
Файлы:
- favicon.ico - иконка.
- index.html - главная страница.
- settings.html - страница настроек.
- login.html - страница аутентификация.
- script_settings.js.gz - js скрипт (сжатый) для обработки и отправки форм.
- style.css.gz - файл стилей (сжатый).
Описание и установка.
Для работы веб сервера потребуются модули (crypto, file, GPIO, net, node, SJSON, timer, UART, WiFi ), далее можете добавить нужные вам. Как собрать прошивку, прошить и залить скрипты, я уже писал. После того как это было сделано можно запускать модуль. При первом старте модуль запустится в режиме точки доступа, через Wi-Fi нужно подключиться к открытой точки доступа с названием "ESP-XXXXXX". Как подключились, заходим в браузер и переходим по адресу 192.168.4.1 . Далее мы попадем на страницу аутентификации, где нужно вести логин и пароль для доступа к модулю, по умолчанию который (логин admin, пароль 0000)

Нас перекинет на главную страницу веб сервера. Это тестовая страница, на ней отображаются данные ESP чипа, количество памяти и список файлов хранящихся на файловой системе чипа. Эту страницу вы можете использовать на свое усмотрение в ваших проектах и также можете создавать свои.

Перейдя по ссылки "Настройки" вы попадете на страницу настроек, на которой можно поменять настройки wi-fi вручную или просканировать сеть нажав на кнопку "Поиск", ниже появится список доступных сетей. Также можно сменить логин и пароль аутентификации или отключить её.

Ограничения.
Сервер обрабатывает файлы по разному, для файлов с расширением .html чтение из файла идет построчное, это сделано для обработки встроенного Lua кода, на размер файла ограничений нет. C файлами с расширением .lua размер отправленных данных не более 4KB. Все остальные файлы передаются побайтно (1024 байт за раз), ограничений на размер файла нет. Сервер не может принимать данные более 1.4KB (данные + заголовок). Пока не было такой необходимости.)))
Пример страницы и добавление в него кода Lua.
Страницу вы можете создать свою или воспользоваться уже готовым шаблоном template.html. В шаблоне подключен файл стилей (style.css.gz), в нем уже есть базовые классы разметки, формы, кнопки и т.д. Подробней можно узнать тут. Фаил стилей постарался сделать как можно компактней (размер в сжатом виде ~ 3кБ).
При добавлении Lua кода в html страницу учитывайте:
- Lua код должен быть в одну строку. (Чтение файлов .html идет построчное)
- Объем одной строки Lua кода не более 1KB.
- Переменные должны быть объявлены как локальные "Local".(Чтобы не засорять глобальное пространство имён)
- Глобальные переменные используйте по необходимости, для постоянных значений.
- Старайтесь чтобы размер страницы не превышал 5KB (При большем размере увеличится время загрузки страницы).
- Для оптимизации и уменьшения размера страницы можно ее сжать, удалив все пробелы и переносы строк, (только помните, что длина одной строки не должна превышать 1KB
<!-- правильно -->
<div class="group">
<input type="number" id="num" required="" value="<?lua local x = 6 return(x\*2) ?>">
<label for="num">Number</label>
</div>
<!-- тоже правильно -->
<div class="group"><input type="number" id="num" required="" value="
<?lua local x = 6 return(x\*2) ?>"><label for="num">Number</label></div>
<!-- так тоже работает -->
<input type="number" min="<?lua return(x\_min) ?>" max="<?lua return(x\_max) ?>">
<!-- Не правильно (код не будет обработан, так как есть перенос строк в lua коде) -->
<div class="group">
<input type="number" id="num" required="" value="
<?lua x = 6
return(x\*2)
?>">
<label for="num">Number</label>
</div>
Как создавать Lua скрипты для сервера.
Скрипт должен быть в виде функции, которая желательно возвращает текстовое значение или таблицу. Таблица в свою очередь будут автоматически преобразована в JSON, это позволяет взаимодействовать с клиентской частью на JavaScript. При таком подходе к написанию скриптов преследуется две цели. Во первых, нам нужно принять какие-то данные от клиента, обработать их нашим скриптом и передать результат обратно, а иначе зачем нам вообще веб морда и веб сервер. Во вторых, после выполнения такого скрипта, сборщик мусора удалит его из памяти, главное не забывать объявлять все переменные и функции локальными.
Рассмотрим пример с файлом "template.lua". Параметры от GET запроса будут переданы в таблицу args. http://IP/template.lua?key=value&name=CodeDevice
Local function arg_to_str(val)
local str = ""
for k, v in pairs(val) do
str = str..k.." : "..v.."
end
return str
end
return function (args)
return arg_to_str(args)
end
Иногда бывает нужно выполнить какую нибудь асинхронную операцию (которая требует времени на обработку), как например, получение списка доступных wi-fi сетей. Конечно можно зациклить и ждать пока функция отработает, но крайне не желательно так делать. Выйти из положения можно другим путем, при первом запросе запускаем поиск сетей и после того как скрипт отработает он запишет полученные данные в файл, а уже вторым запросом прочитаем эти данные из файла. Так же нужно поступать если хотите получить большой объем данных (>4KB) от Lua скрипта, например результат выполнения записать в файл big_data.txt и вторым запросом получить его. Так как ограничения на чтения файлов (.txt и др.) нет. http://IP/big_data.txt
Советы по оптимизации.
- Для ускорения загрузки - сжимайте (.gz) текстовые файлы .css, .js, .txt
- Если в Html страницы нет кода Lua, то ее тоже можно сжать
- Не делайте частых ( <1 раза в сек) запросов к серверу
- Для передачи параметров скрипту, давайте предпочтение javascript и POST запросам (запросы выполняются в фоновом режиме, без перезагрузки страницы)
- Не используйте много контента на странице.
Нужно понимать что ресурсы ESP ограничены и все реализовать в одном проекте не получится, придется чем-то жертвовать.
