Pi как веб-сервер: пульт управления в браузере
Представь: ты открываешь страницу на телефоне, нажимаешь кнопку — и дома загорается свет. Это веб-сервер на твоём Pi.
Веб-интерфейс — лучший пульт для умного дома: он работает на любом телефоне без установки приложений. Pi легко превращается в маленький сайт.
Pi может сам стать веб-сервером — программой, которая отдаёт страницы и принимает команды по сети. На Python для этого есть лёгкий фреймворк Flask. Открыл адрес Pi в браузере — получил страницу управления.
Почему именно веб-страница, а не отдельное приложение? Потому что браузер уже стоит на всём: на твоём телефоне, на ноутбуке родителей, на планшете младшей сестры. Тебе не нужно ничего устанавливать и обновлять — достаточно открыть адрес, и пульт управления домом готов. Это огромное удобство: один и тот же интерфейс работает на любом устройстве, где есть браузер. Представь, что вместо того чтобы носить кучу пультов от телевизора, кондиционера и гирлянды, у тебя одна страница со всеми кнопками, и она всегда под рукой.
Идея клиент-серверного общения проще, чем кажется. Есть две роли: клиент (браузер на телефоне) задаёт вопросы, а сервер (Flask на Pi) на них отвечает. Браузер спрашивает: "дай мне страницу" или "включи свет" — сервер выполняет и отвечает. Они общаются по протоколу HTTP, тому же самому, по которому ты открываешь любой сайт в интернете. Только здесь сайт крошечный и живёт у тебя дома на плате размером с банковскую карту.
Как работает под капотом
Веб-сервер слушает порт (например 5000) и ждёт запросы. Браузер отправляет запрос "дай мне страницу /" — сервер отвечает HTML. Нажатие кнопки шлёт запрос "/light/on" — сервер ловит его и дёргает GPIO.
Телефон (браузер) Raspberry Pi (Flask) +--------------+ запрос /light/on +------------------+ | [ВКЛ] [ВЫКЛ] | -------------------> | маршрут /light/on | | | <------------------- | -> led.on() | +--------------+ ответ "ок" +------------------+
Сердце сервера — цикл обработки: программа крутится бесконечно, ждёт, пока придёт запрос, разбирает его адрес (путь), находит подходящий обработчик, выполняет его и отправляет ответ обратно. Затем снова возвращается в ожидание следующего запроса. Этот цикл и делает сервер "всегда готовым": он не выполняется один раз и завершается, а живёт постоянно, как продавец за прилавком, который обслуживает покупателей одного за другим.
ожидание -> пришёл запрос -> разобрать путь
^ |
| найти обработчик
| |
отправить ответ <-- выполнить обработчик
Код с реальным GPIO — пример для Pi (Flask плюс gpiozero):
from flask import Flask
from gpiozero import LED
app = Flask(__name__)
led = LED(17)
@app.route("/light/on")
def light_on():
led.on()
return "Свет включён"
app.run(host="0.0.0.0", port=5000)
А маршрутизацию запросов — какой адрес какое действие вызывает — отладим на чистом Python. Это сердце любого веб-сервера. Попробуй сам ▶
# Простейший роутер: сопоставляем путь запроса действию
routes = {
"/light/on": lambda: "Свет ВКЛючен",
"/light/off": lambda: "Свет ВЫКЛючен",
"/status": lambda: "Система работает",
}
def handle(path):
handler = routes.get(path)
if handler is None:
return "404: маршрут не найден"
return handler()
# имитируем входящие запросы
for path in ["/light/on", "/status", "/light/off", "/unknown"]:
print(f"{path:12} -> {handle(path)}")
Обрати внимание: словарь routes — это, по сути, таблица "адрес — действие". Именно так устроена маршрутизация в настоящих фреймворках, только декоратор @app.route прячет эту таблицу от глаз. Когда ты понимаешь, что под капотом просто словарь, фреймворк перестаёт быть магией.
Часто страница должна не только выполнять команду, но и показывать текущее состояние: горит ли свет, какая температура. Смоделируем сервер, который хранит состояние и меняет его по запросам. Попробуй сам ▶
# Сервер с состоянием: помнит, включён ли свет
state = {"light": False}
def handle(path):
if path == "/light/on":
state["light"] = True
return "Свет включён"
if path == "/light/off":
state["light"] = False
return "Свет выключен"
if path == "/status":
return "горит" if state["light"] else "выключен"
return "404: маршрут не найден"
for path in ["/status", "/light/on", "/status", "/light/off", "/status"]:
print(f"{path:12} -> {handle(path)}")
Частые ошибки
- Сервер виден только на Pi. Без
host="0.0.0.0"Flask слушает только сам Pi, и с телефона не зайти. - Забыли про 404. Неизвестный путь должен возвращать понятную ошибку, а не падать.
- Открыли наружу без защиты. Управление светом из интернета без пароля — плохая идея.
- Долгое действие внутри обработчика. Если обработчик надолго заснёт, сервер не ответит на другие запросы — тяжёлую работу выноси отдельно.
- Команда меняет данные через обычную ссылку. Открытие адреса в браузере не должно случайно что-то ломать — для изменений принято использовать отдельные кнопки/формы.
Best practices
- Запускай с
host="0.0.0.0", чтобы сервер был доступен в локальной сети. - Всегда обрабатывай неизвестные маршруты (404).
- Для постоянной работы запускай сервер как сервис (об этом — дальше).
- Держи обработчики короткими: приняли запрос — быстро ответили.
- Показывай на странице текущее состояние, а не только кнопки — так пользователь видит, сработала ли команда.
Итоги. Flask превращает Pi в веб-сервер: страница в браузере становится пультом умного дома. В основе — бесконечный цикл обработки и маршрутизация запросов через таблицу "адрес — действие", которую легко отладить на чистом Python, в том числе с хранением состояния. Дальше сделаем так, чтобы программа работала всегда.