Введение
Написание графического интерфейса для взаимодействия с микроконтроллером может занять достаточно много времени на изучение фреймворков, а также написание протокола для взаимодействия через UART. При прототипировании устройств это может занять много времени. Взаимодействие через браузер позволяет работать как с телефона, так и компьютера, при этом взаимодействие будет происходит через i2c или SPI, что освобождает от необходимости использования протоколов. Платформа ESP32 c прошивкой micropython позволяет не только работать в качестве web-server, но и вести лог-файл событий с записью прямо во флеш-память, откуда потом файл можно скопировать.
Установка прошивки на ESP32 через Thonny
После подключения ESP32 необходимо установить драйверы на ESP32 чаще всего ставят CH340 или CP2102. После их установки на потребуется установить программу, а далее прошить в нее интерпретатор MicroPython, в котором мы будем писать наш сервер.
После установки прошивки нам необходимо создать новый файл main.py и сохранить его на устройстве. Одна из причин, написание я программы в main.py, а не в boot.py, это более стабильная работа сигнала прерывания выполнения бесконечного цикла, которая вызывается комбинацией клавиш Ctrl+C. Если вы используете прерывание по таймеру, то выполнить прерывание работы порой можно только при перезагрузки с помощью кнопки EN или комбинацией клавиш Ctrl+D с последующим вызовом комбинаций Cntr+C, для удобства можно ставить задержку перед запуском основного скрипта на 2 секунды time.sleep(2).
C помощью боковой панели Файлы мы можем копировать в память файлы HTML, CSS, javascript и фотографии.
Пришло время написать первую прошивку для тестирования. Скрипт мы будет писать в main.py. В качестве примера помигает светодиодом, а также выведем в консоль время включения-отключения и количество доступной памяти. Консоль - это наш главный помощник в отладке кода на micropython. Для прерывания программы нажмем Ctrl+C.
При запуске этого простого примера хотелось бы обратить внимание на такую вещь, как сборщик мусора или очищение кучи. Если в результате работы программы памяти не останется совсем, то контроллер перезагрузиться. А так как любое действие вызывает перезапись объектов и загрузку кучи, то частые вызовы функций, которые работают с длинными строками способны исчерпать память. Поэтому нужно следить за использованием памяти и грамотно проектировать программу, чтобы сборщик мусора успевал ее нам чистить. Так же помощью оператора del можно вручную удалять не используемые переменные.
Статический веб-сервер
Пришло время реализовать статическую веб-страницу. На большинстве ресурсов код HTML страницы хранился прямо в коде сервера. Мы реализуем подход, когда наш сервер будет загружать HTML-код из файла, а также подгружать картинки по запросу браузера. Данный подход удобнее, так как позволяет в начале протестировать нашу HTML страницу в браузере, а затем просто ее загрузить в память ESP32. Для управления светодиодом мы будем использовать не ссылку на мнимую страницу, а создавать POST-запрос. Динамичность нашей страничке будет придавать наш веб-сервер, который будет заменять шаблонные выражения на реальные данные. В качестве шаблонного выражения в index.html используется #url_bulb#, который в зависимости от состояния светодиода меняется на название файл bulb_on.png или bulb_off.png.
Итоговый вид нашей странички будет такой
Ниже представлен вид, который мы будем видеть на стороне нашего сервера.
Сервер настроен на создание точки доступа, к которой подключается клиент. Если требуется настроить защиту паролем, то необходимо раскомментировать строку ap_if.config(authmode=network.AUTH_WPA_WPA2_PSK)
Сервер ищет целевые слова в запросах и по ним открывает целевые файлы, отправляет их браузеру, включает и отключает лампочки.
При запуске в консоли будет текст запросов со стороны веб-браузера
Рассмотрим сообщения веб-браузера серверу.
При запросе страницы по адресу 192.168.4.1 придет следующее сообщение
Время 21:49:31
Осталось памяти начало 145968
b'GET / HTTP/1.1\r\nHost: 192.168.4.1\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: ru-RU,ru;q=0.9,en;q=0.8\r\n\r\n'
Осталось памяти в конце 143360
В ответ на это сервер отправить файл index.html
Загрузив файл index.html, браузер запросит фотографию лампочки, шаблон которой был заменен на название актуальной фотографии.
Время 21:49:31
Осталось памяти начало 143056
b'GET /bulb_off.png HTTP/1.1\r\nHost: 192.168.4.1\r\nConnection: keep-alive\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36\r\nAccept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8\r\nReferer: http://192.168.4.1/\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: ru-RU,ru;q=0.9,en;q=0.8\r\n\r\n'
Осталось памяти в конце 143056
Сервер находит название файла bulb_off, и открывает в бинарном виде файл bulb_off.png, после чего отправляет набор байтов веб-браузеру. Существует альтернативный способ отправить файл фотографии, через формат base64. Для этого фото в начале нужно конвертировать на сайте https://www.base64-image.de, а затем в атрибуте src вместо адреса картинки ввести код картинки сгенерированный сайтом.
<img src="data:image/jpeg;base64,/9j/4AAQSkZ...JRgABAQEASABIAAD//gA9Q1">
Код картинки получается достаточно длинным и дополнительно единовременно грузит нашу кучу. Для картинки размером в 10 кб размер строки у меня получился более 10 тысяч символов. Я не рекомендую использовать данный способ для крупных изображений, если только для очень маленьких картинок-иконок.
Продолжим рассматривать запросы браузера серверу
При нажатии на кнопку браузер будет посылать нам POST запрос на включение или отключение лампочки
Время 21:49:33
Осталось памяти начало 135536
b'POST / HTTP/1.1\r\nHost: 192.168.4.1\r\nConnection: keep-alive\r\nContent-Length: 8\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nOrigin: http://192.168.4.1\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nReferer: http://192.168.4.1/\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: ru-RU,ru;q=0.9,en;q=0.8\r\n\r\nswitch=1'
Осталось памяти в конце 128544
А ниже запрос на выключение
Время 21:49:47
Осталось памяти начало 118816
b'POST / HTTP/1.1\r\nHost: 192.168.4.1\r\nConnection: keep-alive\r\nContent-Length: 8\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nOrigin: http://192.168.4.1\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nReferer: http://192.168.4.1/\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: ru-RU,ru;q=0.9,en;q=0.8\r\n\r\nswitch=0'
Осталось памяти в конце 111824
При этом если бы у нас были в форме еще <input> теги, то их атрибуты name и value отправились бы в одном запросе через амперсанд, что очень удобно. Следующий пример покажет отправку формы со всеми полями
Отправка нескольких данных из формы серверу
Расширим функционал нашей лампочки, добавив к кнопке включения функционал мигания, с возможностью задания интервала длительности.
На HTML-странице все параметры которые должны быть заменены на реальные данные обрамлены решеткой #параметр#. Для оформления страницы были внедрены стили CSS, которые при небольшой доработки можно выделить в отдельный файл, аналогично файлу HTML.
А текст нашего сервера стал более сложный. В нем добавился блок автоматической отправки фотографий, а параметры стали приниматься в словарь, который полностью соответствует структуре JSON.
Надеюсь, мой пример будет полезным людям, кто хочет начать разрабатывать IoT.
Из минусов данного подхода хочется отметить, что страничка тормозит, данные обновляется только при обновлении страницы целиком. Кнопки иногда срабатывают не с первого раза, это связанно с частым вызовом сборщика мусора. Автоматическое обновление можно настроить вставив в раздел <head> тег <meta http-equiv="refresh" content="15">. Однако проблему актуальных данных это не решает. При увеличении количества шаблонов, в которых необходимо делать замены, будет увеличиваться потребление памяти на операцию замены данных. Вручную менять шаблоны достаточно неудобно.
Для решение этих проблем необходимо, чтобы ESP32 отсылал только данных, а браузер с помощью javascript сам редактировал страницу. Данная технология называется AJAX. Реализацию данного подхода можно будет посмотреть в следующей статье.
Прикладываю архив с листингам для удобства читателей.
Прикрепленные файлы:
- esp32_iot.rar (46 Кб)
Автор: Aleksey1408