Глупое устройство

Описание

В этой лабораторной мы поднимем web сервер на esp8266 и сделаем на его основе "интернет-вещь" (IoT)

UI Устройства|=center=|600
UI Устройства

Теория

Микроконтроллеры семейства ESP

В наборе вы найдете плату разработчика с микроконтроллером esp8266. Официально, он устарел в пользу esp32-C2, но все еще продается и благодаря большой базе знаний будет актуален еще долго.
Важные практические особенности esp8266:

  • Цена - миска риса.
  • wifi на борту (+ bluetooth для esp32)
  • БОльшая производительность относительно atmega.
    За это приходится платить:
  • GPIO мало и ведут они себя странно - например некоторые пины требуют определенного логического сигнала на них при старте МК (иначе он не запустится).
  • Большая часть пинов выдает сигнал при запуске. Единственными спокойными пинами являются D1 и D2 - если вы управляете, скажем, нагрузкой, это ваш единственный безопасный вариант.
  • Номера пинов не совпадают с номерами GPIO (e.g GPIO12 = D6), в Arduino среде лучше обращаться к ним по константе: digitalWrite(D6, LOW);
  • Ужасный АЦП. Еще хуже он работает при включенном wifi.

Для работы c esp в Arduino IDE, ее нужно настроить

WiFi

WiFi может работать в режимах:

  • AP (Access Point) - МК поднимет свою точку доступа, к которой мы сможем подключиться
  • Station (STA) - МК подключится к нашей точке доступа.

Точка поднимается элементарно:

#include <ESP8266WiFi.h>

void setup() {
	...
	WiFi.mode(WIFI_AP);
	WiFi.softAP("SSID", "PASSWORD"); // ❗️парольн не меньше 8 символов
}

МК доступен по 192.168.4.1.

WebServer

Клиент-серверное взаимодействие|=center=|600
Клиент-серверное взаимодействие

Когда мы вводим 192.168.4.1 в адресную строку, браузер отправляет http GET запрос на микроконтроллер. МК смотрит в свою файловую систему и возвращает index.html - UI проекта, который мы заранее подготовили и загрузили - для простоты соберем в этом файле разметку (html) логику работы (js) и стилизацию (css). Теперь браузеру есть что показать. С этого момента страница контролирует все запросы к серверу самостельно.

Изначально, страница не знает текущее состояние МК. Это мы предусмотрели, поэтому, первым делом javascript отправляет GET запрос на эндпоинт /state, используя метод fetch(). На этот раз, сервер отвечает нам не файлом, а текстовыми данными, сериализованными в формате json - как только мы их получили, подставляем их в нужные прямоугольники на экране.

Чтобы управлять микроконтроллером, javascript навешивает обработчики событий (например onClick()) на кнопки. Обработчик посылает POST запрос (традиционно, запросы, которые хотят изменить состояние сервера, используют метод POST) так же с помощью метода fetch().

Такой подход называется SPA (single page application) - мы скачиваем приложение (тот самый index.html) и оно живет своей жизнью - отправляет запросы на сервер, обновляет свое содержимое.

Реализация сервера

Мы будем использовать библиотеку ESPAsyncWebServer
Для парсинга json применяем ArduinoJson
Все подобные библиотеки выделяют методы для настройки эндпоинтов(часть url запроса) и их обработчиков:

	// передаем обработчик как лямбду
    server.on("/hi", HTTP_GET, [](AsyncWebServerRequest *request) {   
        request->send(200, "text/plain", "Hello, world");  
    });
	// или передаем обработчик как объект
	server.addHandler(handler);

// Не забываем про cors
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");  
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Content-Type");

Реализация клиента

Разработка браузерных приложений выходит за рамки курса. Для лабораторной подготовлен шаблон, который можно подправить под ваши нужды или взять как-есть (в этом случае, api вашего сервера должно строго соответствовать клиенту).

WebSocket

Представим теперь, что мы хотим управлять умным устройством с телефона, компьютера и дашборда прибитого к стене. Как дашборд узнает, что мы щелкнули реле с телефона и состояние изменилось?
Приложение может опрашивать сервер с некоторым интервалом (polling) - грустно и неэффективно. Гораздо интереснее, если бы сервер мог сам оповестить всех клиентов и произошедшем событии. Для этого существует протокол WebSocket. Железка поднимает websocket-сервер, все наши браузерные приложения к нему подключаются. Если железяка обновила свое состояние, она отправляет сообщение с сериализованным состояниям (для простоты будем отправлять целиком) всем клиентам системы (в нашем случае всем открытым вкладкам с приложением), которые в свою очередь обновляют содержимое прямоугольников на экране.

AsyncWebServer server(80);  
AsyncWebSocket ws("/ws");

...
void setup() {
...  
  server.addHandler(&ws);
...
}

void loop() {
...
  if(state.changed) {  
    serializeState();  
    ws.textAll(buffer);  
    state.changed = false;  
  }
}

Задачи

5.1 - Поднимаем сервер

Поднять на esp8266 WiFi точку доступа и Web-сервер. GET запрос на эндпоинты /switch1 /switch2 должен включать/выключать соответствующие светодиоды и возвращать json true/false (boolean - это валидный json) - в какое состояние переключились. GET запрос на /state должен возвращать сериализованное в json общее состояние МК:

{"switch1":false,"switch2":false,"rgb":{"r":0,"g":0,"b":0},"lightSensor":0}

Проверяем curl'ом или httpie.

5.2 - POST Запрос

Поднимаем POST endpoint /rgb который в теле запроса будет принимать json следующего содержания:

{r: 0-255, g: 0-255, b: 0-255}

И отображать этот цвет на ленте. С 3.3V логикой лента работает нестабильно, по-хорошему нужен конвертер логических уровней. На практике достаточно вызвать FastLED.show() несколько раз подряд.

5.3 - WebSocket

Любое изменение состояния должно сериализовываться и отправляться всем подключенным ws клиентам. Изменение может порождать как один из клиентов (кнопки на UI), так и сам МК - для этого подключим к нему две кнопки.

5.4 - Железяки

Нужно подключить реле и датчик освещенности. Последний опрашивать в некотором интервале и оповещать всех участников по ws. Реле должны щелкать как по GET запросу, так и при нажатии по кнопкам, подключенным к МК.

5.5 - Загрузка файла

Нужно научиться загружать html файл (наше приложение) на микроконтроллер. Для этого выводим отдельный эндпоинт /upload. GET запрос на него должен вернуть захардкоженую форму с кнопкой upload. Загрузку файла по кнопке обработает POST метод сервера, который запишет файл на файловую систему используя LittleFS. Теперь GET запрос на 192.168.4.1 должен возвращать наше приложение, которое микроконтроллер вытащил со своей файловой системы.