Перевод статьи Raspberry Pi Pico: Web Server to Control Outputs (Arduino IDE)Rui Santos & Sara Santos, Random Nerd Tutorials.

Это руководство показывает, как создать простой веб-сервер на Raspberry Pi Pico W, запрограммированном в Arduino IDE, для управления выходами. Мы воспользуемся библиотекой ESPAsyncWebServer, чтобы поднять асинхронный веб-сервер.

Учтите: руководство подходит только для Raspberry Pi Pico W и Raspberry Pi Pico 2W — моделей с поддержкой Wi-Fi.

Raspberry Pi Pico и Arduino IDE#

Вам нужно установить поддержку плат Raspberry Pi Pico в Arduino IDE и уметь заливать код на плату. Если вы этого ещё не делали, сначала разберитесь с настройкой среды под Pico и Pico 2.

Установка библиотек#

Чтобы создать асинхронный веб-сервер на Raspberry Pi Pico в Arduino IDE, мы используем библиотеку ESPAsyncWebServer — она совместима с ESP32, ESP8266, а также с RPi Pico (RP2040 и RP2350) и другими родственными платами. Для Pico дополнительно нужна библиотека RPAsyncTCP.

Итого в Arduino IDE нужно установить:

  • ESPAsyncWebServer от ESP32Async
  • RPAsyncTCP от ayushsharma82

Порядок установки:

  1. Откройте Sketch → Include Library → Manage Libraries или щёлкните иконку менеджера библиотек на левой панели.

  2. Найдите ESPAsyncWebServer и установите библиотеку ESPAsyncWebServer от ESP32Async.

Установка ESPAsyncWebServer в Arduino IDE

Установка ESPAsyncWebServer в Arduino IDE

  1. Найдите RPAsyncTCP и установите библиотеку RPAsyncTCP от Ayush Sharma.
Установка библиотеки RPAsyncTCP

Установка RPAsyncTCP

Обзор проекта#

В этом примере мы создадим простую веб-страницу с двумя кнопками для управления одним из выводов GPIO на Raspberry Pi Pico.

Схема работы веб-сервера на Pico

Как устроено управление выходом через веб-сервер

  1. Когда вы открываете IP-адрес Raspberry Pi Pico в браузере, вы делаете запрос к серверу (то есть к Pico) по корневому URL /.

  2. Pico отвечает HTML-текстом, из которого строится простая веб-страница.

  3. На странице две кнопки — включить и выключить светодиод, подключённый к одному из выводов GPIO. При нажатии выполняется новый запрос к следующим адресам:

  • кнопка «LED on» — запрос к /lighton?
  • кнопка «LED off» — запрос к /lightoff?
  1. Мы используем HTML-шаблон, который подставляет текущее состояние светодиода на страницу сразу после запроса. Иными словами, после запроса плата отправляет HTML-страницу с правильным состоянием вывода.

Замечание: состояние при этом фактически не проверяется — оно просто меняется в соответствии с сделанным запросом.

Схема подключения#

В этом примере мы управляем светодиодом, подключённым к GPIO 2 на Raspberry Pi Pico. Можно использовать любой другой подходящий вывод, если соответственно поправить код.

Подключение светодиода к Raspberry Pi Pico

Светодиод на GPIO 2 через резистор 220 Ом

Что понадобится:

  • Raspberry Pi Pico W / Raspberry Pi Pico 2W
  • 1 светодиод
  • 1 резистор на 220 Ом (или близкого номинала)
  • макетная плата
  • соединительные провода

Код: веб-сервер на Raspberry Pi Pico для управления выходами#

Следующий код поднимает веб-сервер, отдающий показанную выше страницу.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Полное описание проекта: https://RandomNerdTutorials.com/raspberry-pi-pico-web-server-outputs-arduino/
*********/
// Подключаем нужные библиотеки
#include <WiFi.h>
#include <RPAsyncTCP.h>
#include <ESPAsyncWebServer.h>

// Замените на данные своей сети
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Светодиод, которым управляем
#define LED_PIN 2

// Текущее состояние светодиода (для отображения на странице)
String ledState = "OFF";

// Создаём объект AsyncWebServer на порту 80
AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
  <title>Pico Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    html {
      font-family: Arial;
      display: inline-block;
      margin: 0px auto;
      text-align: center;
    }
    body {
      max-width: 600px;
      margin: 0 auto;
      padding: 20px;
    }
    h1 { color: #333; }
    h2 { color: #666; }
    form { margin: 10px 0; }
    .button {
      background-color: #4CAF50;
      border: none;
      color: white;
      padding: 10px 20px;
      text-decoration: none;
      font-size: 20px;
      margin: 2px;
      cursor: pointer;
      border-radius: 5px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.2);
      transition: background-color 0.3s, box-shadow 0.3s;
    }
    .button:hover {
      background-color: #45a049;
      box-shadow: 0 4px 8px rgba(0,0,0,0.3);
    }
    .button2 { background-color: #f44336; }
    .button2:hover { background-color: #da190b; }
    .state {
      font-size: 1.4em;
      margin: 20px 0;
      color: #444;
    }
  </style>
</head>
<body>
  <h1>Raspberry Pi Pico Web Server</h1>
  <h2>Led Control</h2>
  <p class="state">LED State: %STATE%</p>
  <form action="./lighton">
    <input type="submit" value="LED on" class="button" />
  </form>
  <form action="./lightoff">
    <input type="submit" value="LED off" class="button button2" />
  </form>
</body>
</html>
)rawliteral";

// Функция-обработчик шаблона
String processor(const String& var){
  if(var == "STATE"){
    return ledState;
  }
  return String();
}

void setup(){
  Serial.begin(115200);

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  // Подключаемся к Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Печатаем локальный IP-адрес Pico
  Serial.println(WiFi.localIP());

  // Маршрут для корневой страницы /
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/html", index_html, processor);
  });

  // Маршрут для включения светодиода
  server.on("/lighton", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(LED_PIN, HIGH);
    ledState = "ON";
    request->send(200, "text/html", index_html, processor);
  });

  // Маршрут для выключения светодиода
  server.on("/lightoff", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(LED_PIN, LOW);
    ledState = "OFF";
    request->send(200, "text/html", index_html, processor);
  });

  // Запускаем сервер
  server.begin();
}

void loop() {

}

Не забудьте вписать SSID и пароль своей сети в эти строки, после чего можно заливать код на плату.

// Замените на данные своей сети
const char* ssid = "";
const char* password = "";

Как работает код#

Разберём код по частям.

Подключение библиотек#

Сначала подключаем библиотеки, нужные для подключения Pico к сети и создания веб-сервера: WiFi, RPAsyncTCP и ESPAsyncWebServer.

// Подключаем нужные библиотеки
#include <WiFi.h>
#include <RPAsyncTCP.h>
#include <ESPAsyncWebServer.h>

Данные сети#

Впишите в эти строки данные своей сети, чтобы Pico мог к ней подключиться.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Вывод светодиода#

Мы управляем светодиодом на GPIO 2. Можно взять любой другой вывод — достаточно поправить эту строку.

#define LED_PIN 2

Состояние светодиода#

Создаём глобальную переменную, чтобы отслеживать состояние светодиода. При запуске программы он выключен.

String ledState = "OFF";

AsyncWebServer на порту 80#

Создаём объект AsyncWebServer с именем server на порту 80.

AsyncWebServer server(80);

HTML-текст#

Переменная index_html хранит HTML, из которого строится страница, и представляет собой «сырой» строковый литерал: он размещается между R"rawliteral( и )rawliteral". Это позволяет писать строку в несколько строк без экранирующих символов вроде \n и кавычек в каждой строке — очень удобно, чтобы вставить HTML как есть. Переменная хранится во флеш-памяти (PROGMEM).

Секция между тегами <style></style> — это CSS, отвечающий за внешний вид страницы. Дальше идут заголовок и подзаголовок, которые можно поменять на любые:

<h1>Raspberry Pi Pico Web Server</h1>
<h2>Led Control</h2>

Следующий абзац показывает текущее состояние светодиода. %STATE% — это плейсхолдер для фактического значения (переменной ledState). Перед отправкой страницы клиенту функция processor() подставит вместо него правильное значение.

<p class="state">LED State: %STATE%</p>

А эти строки создают кнопки включения и выключения светодиода:

<form action="./lighton">
    <input type="submit" value="LED on" class="button" />
</form>
<form action="./lightoff">
    <input type="submit" value="LED off" class="button button2" />
</form>

Функция processor()#

Функция processor() ищет плейсхолдеры и заменяет их заданными значениями. В нашем случае мы ищем плейсхолдер STATE и подставляем текущее состояние светодиода из переменной ledState.

String processor(const String& var){
  if(var == "STATE"){
    return ledState;
  }
  return String();
}

Функция setup()#

В setup() инициализируем монитор порта на скорости 115200, настраиваем вывод светодиода как выход и устанавливаем его в LOW. Затем подключаем Pico к локальной сети с указанными ранее данными.

// Подключаемся к Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Connecting to WiFi..");
}

Печатаем IP-адрес Pico — позже он понадобится для доступа к странице.

Serial.println(WiFi.localIP());

Обработка запросов#

Следующие строки обрабатывают запросы к Pico по корневому URL / — это происходит, когда вы вводите IP-адрес платы в браузере. Получив такой запрос, Pico отправляет клиенту текст из переменной index_html.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(200, "text/html", index_html, processor);
});

Параметры функции send():

  • 200 — HTTP-код, означающий, что запрос принят;
  • text/html — мы отправляем страницу в формате HTML;
  • index_html — содержимое страницы;
  • processor — функция-обработчик, которая найдёт плейсхолдеры и подставит нужные значения.

При запросе на /lighton или /lightoff плата включает или выключает светодиод функцией digitalWrite() и обновляет переменную ledState. После этого страница отправляется клиенту — обратите внимание, что мы снова передаём функцию processor четвёртым параметром, чтобы страница ушла с правильным состоянием вывода.

// Маршрут для включения светодиода
server.on("/lighton", HTTP_GET, [](AsyncWebServerRequest *request){
  digitalWrite(LED_PIN, HIGH);
  ledState = "ON";
  request->send(200, "text/html", index_html, processor);
});

// Маршрут для выключения светодиода
server.on("/lightoff", HTTP_GET, [](AsyncWebServerRequest *request){
  digitalWrite(LED_PIN, LOW);
  ledState = "OFF";
  request->send(200, "text/html", index_html, processor);
});

Наконец, запускаем веб-сервер.

server.begin();

Функция loop()#

Поскольку веб-сервер асинхронный (функция server.on() срабатывает при получении запроса), в loop() ничего добавлять не нужно. При желании вы можете дописать туда код, необходимый вашему проекту.

void loop() {

}

Заливка кода на Pico#

Чтобы залить код на Raspberry Pi Pico W, плата должна быть в режиме загрузчика. Если сейчас на ней стоит прошивка MicroPython, переведите её в этот режим вручную: подключите Pico к компьютеру, удерживая кнопку BOOTSEL. Откроется окно нового съёмного диска — его можно проигнорировать и закрыть. При последующих загрузках из Arduino IDE плата должна уходить в режим загрузчика автоматически, без нажатия BOOTSEL.

Теперь откройте верхнее выпадающее меню и выберите Select other board and port…. В качестве платы выберите Raspberry Pi Pico W или Raspberry Pi Pico 2W — в зависимости от того, что у вас. COM-порт может не появиться при первой загрузке, поэтому отметьте галочку Show all ports и выберите пункт UF2 Board / UF2 Devices. После этого можно заливать код — должно появиться сообщение об успехе.

Монитор порта#

Теперь нужно узнать IP-адрес Pico — он печатается в монитор порта. Откройте монитор на скорости 115200. Затем отключите плату и подключите её обратно: через несколько секунд она установит связь с монитором и покажет свой IP-адрес. У автора это 192.168.1.83.

IP-адрес Pico в мониторе порта

IP-адрес Pico в мониторе порта

Открываем веб-сервер#

Откройте окно браузера и введите IP-адрес Pico. Плата отдаст содержимое переменной index_html, и вы увидите такую страницу.

Веб-страница управления светодиодом

Страница с кнопками управления светодиодом

Нажимайте кнопки, чтобы управлять светодиодом на GPIO 2.

Светодиод выключен

Светодиод выключен

Светодиод включён

Светодиод включён

Подводя итог#

В этом руководстве вы научились создавать веб-сервер на Raspberry Pi Pico, запрограммированном в Arduino IDE, с помощью библиотеки ESPAsyncWebServer — и управлять через него выводом GPIO.

Эту же библиотеку мы обычно используем для веб-серверов на ESP32 и ESP8266. Методы и функции работы с веб-сервером на платах ESP и на RPi Pico одинаковы.