Перевод статьи Raspberry Pi Pico Web Server: Files from Filesystem (MicroPython) — Random Nerd Tutorials.

Научимся делать на Raspberry Pi Pico веб-сервер, который отдаёт файлы, сохранённые в его файловой системе. Вместо того чтобы писать HTML прямо в скрипте MicroPython, часто удобнее держать HTML и прочие нужные файлы отдельно — в файловой системе Raspberry Pi Pico.

Raspberry Pi Pico W и веб-страница, отдаваемая из файловой системы

Это руководство подходит только для Raspberry Pi Pico W и Raspberry Pi Pico 2 W — у них есть Wi-Fi.

Содержание:

  • Raspberry Pi Pico: отдаём файл из файловой системы (HTML-файл)
  • Веб-сервер на Raspberry Pi Pico (файлы из файловой системы): HTML, CSS и JavaScript

Мы переделаем пример из предыдущего руководства так, чтобы HTML отдавался из файловой системы, а не был записан прямо в скрипте. Заодно добавим файлы CSS и JavaScript — так вы научитесь отдавать несколько файлов.

Если вы впервые делаете веб-сервер на Raspberry Pi Pico с MicroPython, советуем сначала пройти эти руководства, чтобы разобраться в процессе и основах:

Что нужно заранее: прошивка MicroPython#

Для этого руководства на Raspberry Pi Pico должна быть установлена прошивка MicroPython. Ещё понадобится IDE, чтобы писать код и загружать его на плату.

Для Raspberry Pi Pico рекомендуем Thonny IDE. Как установить Thonny IDE, прошить MicroPython и загрузить код на плату — в этом руководстве:

Если вы только начинаете знакомство с Raspberry Pi Pico, пройдите одно из вводных руководств:

Raspberry Pi Pico: отдаём файл из файловой системы (HTML-файл)#

Прежде чем перейти к основному проекту, сделаем простой веб-сервер «Hello, World!», чтобы показать, какие шаги нужны, чтобы Pico W отдавал файлы из файловой системы.

Это удобно, если вы хотите создавать и отдавать отдельные файлы HTML, CSS, JavaScript и прочие, а не писать всё в переменной внутри скрипта MicroPython.

Порядок действий такой.

1) Откройте новый файл в Thonny IDE и скопируйте в него следующий HTML.

<!DOCTYPE html>
<html>
<head>
    <title>Pico Web Server</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <h1>Raspberry Pi Pico Web Server</h1>
    <p>Hello, World!</p>
</body>
</html>
HTML-файл в редакторе Thonny IDE

HTML-файл в Thonny IDE

2) Убедитесь, что Thonny IDE подключена к вашему RPi Pico. Затем загрузите файл в файловую систему Pico: File > Save as… и выберите Raspberry Pi Pico.

Диалог выбора места сохранения в Thonny: This computer или Raspberry Pi Pico

Сохраняем файл на Raspberry Pi Pico

3) Сохраните файл под именем index.html.

Сохранение файла index.html в файловую систему Pico

Имя файла — index.html

Теперь HTML-файл лежит в файловой системе Pico. Если открыть View > Files, вы увидите там файл index.html.

Панель Files в Thonny с файлом index.html на Raspberry Pi Pico

index.html в файловой системе Pico

4) Создайте в Thonny IDE ещё один файл — это будет скрипт MicroPython.

# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-web-server-filesystem-micropython/

# Import necessary modules
import network
import socket
import time

# Wi-Fi credentials
ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'

# Variable to save the HTML file path
HTML_FILE_PATH = "index.html"

# Function to read and return HTML content from the file
def read_file(filepath):
    with open(filepath, "r") as file:
        return file.read()

# Init Wi-Fi Interface
def init_wifi(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    # Connect to your network
    wlan.connect(ssid, password)
    # Wait for Wi-Fi connection
    connection_timeout = 10
    while connection_timeout > 0:
        print(wlan.status())
        if wlan.status() >= 3:
            break
        connection_timeout -= 1
        print('Waiting for Wi-Fi connection...')
        time.sleep(1)
    # Check if connection is successful
    if wlan.status() != 3:
        print('Failed to connect to Wi-Fi')
        return False
    else:
        print('Connection successful!')
        network_info = wlan.ifconfig()
        print('IP address:', network_info[0])
        return True

if not init_wifi(ssid, password):
    print("Exiting program.")
else:
    try:
        # Set up socket and start listening
        addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
        s = socket.socket()
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind(addr)
        s.listen()
        print('Listening on', addr)

        # Main loop to listen for connections
        while True:
            try:
                conn, addr = s.accept()
                print('Got a connection from', addr)

                # Receive and parse the request
                request = conn.recv(1024)
                request_str = request.decode('utf-8')
                print('Request content:')

                # Generate HTML response
                response = read_file(HTML_FILE_PATH)  

                # Send the HTTP response and close the connection
                conn.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
                conn.send(response)
                conn.close()

            except OSError as e:
                conn.close()
                print('Connection closed')

    except KeyboardInterrupt:
            print('Server stopped by user.')

Исходный код на GitHub

В скрипте нужно:

  • указать путь к HTML-файлу в файловой системе. У нас он лежит в корневом каталоге, поэтому путь — просто index.html:
# Variable to save the HTML file path
HTML_FILE_PATH = "index.html"
  • прочитать файл из файловой системы и вернуть его содержимое. Для этого можно написать такую функцию:
# Function to read and return HTML content from the file
def read_file(filepath):
    with open(filepath, "r") as file:
        return file.read()

Подробнее о работе с файлами на Raspberry Pi Pico — в руководстве Raspberry Pi Pico: Handling Files and Directories (MicroPython).

Это простой веб-сервер, который лишь возвращает HTML-страницу при обращении к IP-адресу платы. Поэтому обрабатывать нужно только запрос к корневому URL (/). Главный цикл слушает HTTP-запросы:

# Main loop to listen for connections
while True:
    try:
        conn, addr = s.accept()
        print('Got a connection from', addr)

        # Receive and parse the request
        request = conn.recv(1024)
        request_str = request.decode('utf-8')
        print('Request content:')

Получив запрос, готовим ответ. Его содержимое — HTML-текст из файла index.html. Чтобы сформировать ответ, достаточно прочитать HTML-файл созданной ранее функцией read_file(), передав ей HTML_FILE_PATH:

# Generate HTML response
response = read_file(HTML_FILE_PATH)

Остаётся отправить ответ. Сначала — HTTP-заголовок с типом содержимого: мы отправляем HTML (Content-type: text/html).

conn.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')

Затем — сам HTML-текст, сохранённый в переменной response:

conn.send(response)

И наконец закрываем соединение:

conn.close()

Вот и все шаги, нужные для того, чтобы отдавать файлы из файловой системы Pico.

Проверяем веб-сервер#

Не забудьте вписать данные своей сети в эти строки:

# Wi-Fi credentials
ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'

Чтобы проверить веб-сервер, загрузите скрипт MicroPython на Raspberry Pi Pico и сохраните его под именем main.py.

Сохранение скрипта main.py на Raspberry Pi Pico

Сохраняем скрипт как main.py

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

Панель инструментов Thonny IDE с зелёной кнопкой Run

Кнопка запуска в Thonny IDE

Когда связь между Pico и Thonny IDE установлена, IP-адрес Pico появится в консоли (Shell).

Консоль MicroPython в Thonny с IP-адресом Pico

IP-адрес платы в консоли

Откройте браузер и введите этот IP-адрес. Должна открыться HTML-страница, которую вы сохранили в файловой системе.

Страница Hello, World! от веб-сервера Raspberry Pi Pico в браузере

Страница из файловой системы Pico в браузере

Если нужно отдавать больше файлов — например, CSS или JavaScript, — придётся разбирать HTTP-запрос и формировать ответ соответственно: брать запрошенный файл из файловой системы и ставить в заголовке подходящий Content-type. Как это сделать, покажем дальше.

Веб-сервер на Raspberry Pi Pico (файлы из файловой системы)#

Прежде чем начать, кратко посмотрим, что умеет веб-сервер, который мы сделаем:

Raspberry Pi Pico W и веб-страница с кнопками управления светодиодом и температурой

Готовый проект

Что делает наш пример:

  • это асинхронный веб-сервер;
  • он отдаёт HTML-страницу, на которой есть:
  • HTML-страница хранится в файловой системе Pico;
  • также отдаётся CSS-файл со стилями страницы (тоже в файловой системе);
  • и JavaScript-файл (тоже в файловой системе), который обрабатывает нажатия кнопок.
Схема работы веб-сервера: браузер запрашивает index.html, style.css, script.js и JSON-данные у Pico

Как это работает

Этот веб-сервер основан на более простом, который мы делали в предыдущем руководстве. Если хотите начать с чего-то попроще, загляните сначала туда: Raspberry Pi Pico W: Asynchronous Web Server (MicroPython).

Создаём HTML-файл#

Откройте новый файл в Thonny IDE и скопируйте в него следующий HTML.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Pico Web Server</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="style.css">
    <script src="script.js" defer></script>
</head>
<body>
    <h1>Raspberry Pi Pico Web Server</h1>

    <p>LED State: <strong id="led-status" class="status">{state}</strong></p>

    <p>
        <button onclick="turnOn()" class="button">ON</button>
        <button onclick="turnOff()" class="button button2">OFF</button>
    </p>

    <p>
        <span class="sensor-labels">Temperature:</span>
        <span id="temp_c" class="temp-value">{temperature_c}</span><span class="units">°C</span>
        &nbsp; / &nbsp;
        <span id="temp_f" class="temp-value">{temperature_f}</span><span class="units">°F</span>
    </p>

    <p>
        <button onclick="getNewReading()" class="button-refresh" style="width: 220px;">
            Get New Reading
        </button>
    </p>
</body>
</html>

Исходный код на GitHub

Сохраните этот файл в файловую систему Raspberry Pi Pico: File > Save as…, выберите Raspberry Pi Pico и назовите файл index.html.

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

Кратко разберём части HTML-файла, важные для этого примера. Можно и сразу перейти к следующему разделу.

Подключение файлов CSS и JavaScript

Указываем, что подключаем CSS- и JavaScript-файлы. При загрузке HTML-страницы браузер запросит эти файлы: style.css и script.js.

<link rel="stylesheet" type="text/css" href="style.css">
<script src="script.js" defer></script>

Отображение состояния светодиода

Здесь есть блок, показывающий текущее состояние светодиода. В нём — заполнитель {state}, на место которого код MicroPython подставит текущее состояние с помощью форматирования строк. У элемента с состоянием есть id led-status (id="led-status"), чтобы обращаться к нему из JavaScript.

<p>LED State: <strong id="led-status" class="status">{state}</strong></p>
Строка LED State: OFF на веб-странице

Кнопки ON и OFF

Дальше идёт абзац с двумя кнопками для управления светодиодом. При нажатии ON или OFF вызываются JavaScript-функции turnOn() и turnOff(). Как они работают, увидим в следующем разделе.

<p>
    <button onclick="turnOn()" class="button">ON</button>
    <button onclick="turnOff()" class="button button2">OFF</button>
</p>
Зелёная кнопка ON и красная кнопка OFF

Отображение температуры

Есть блок для вывода температуры. Здесь тоже есть id, чтобы обращаться к этим элементам из JavaScript (id="temp_c" и id="temp_f"), и заполнители {temperature_c} и {temperature_f}.

<p>
    <span class="sensor-labels">Temperature:</span>
    <span id="temp_c" class="temp-value">{temperature_c}</span><span class="units">°C</span>
    &nbsp; / &nbsp;
    <span id="temp_f" class="temp-value">{temperature_f}</span><span class="units">°F</span>
</p>
Строка Temperature с градусами Цельсия и Фаренгейта

Запрос нового значения температуры

И наконец — кнопка для запроса нового значения температуры. При нажатии вызывается JavaScript-функция getNewReading().

<p>
    <button onclick="getNewReading()" class="button-refresh" style="width: 220px;">
        Get New Reading
    </button>
</p>
Кнопка Get New Reading

Создаём JavaScript-файл#

Откройте новый файл в Thonny IDE и скопируйте следующий код на JavaScript. Загрузите его на Raspberry Pi Pico под именем script.js.

// Turn LED ON
async function turnOn() {
    try {
        const response = await fetch('/lighton');
        const data = await response.json();
        document.getElementById('led-status').textContent = data.state;
    } catch (error) {
        console.error('Error turning LED ON:', error);
        alert('Failed to turn LED ON');
    }
}

// Turn LED OFF
async function turnOff() {
    try {
        const response = await fetch('/lightoff');
        const data = await response.json();
        document.getElementById('led-status').textContent = data.state;
    } catch (error) {
        console.error('Error turning LED OFF:', error);
        alert('Failed to turn LED OFF');
    }
}

// Get new temperature reading
async function getNewReading() {
    try {
        const response = await fetch('/temperature');

        const data = await response.json();

        document.getElementById('temp_c').textContent = data.temperature_c;
        document.getElementById('temp_f').textContent = data.temperature_f;
    } catch (error) {
        console.error('Error fetching temperature:', error);
        alert('Failed to get new reading');
    }
}

Исходный код на GitHub

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

Кратко разберём этот код JavaScript. Можно и сразу перейти к следующему разделу.

Включение и выключение светодиода

Функция turnOn() делает запрос по пути /lighton. Ответом будет JSON такого вида (его формирует файл MicroPython):

{
  "state": state
}

Получив ответ, функция берёт его содержимое, data.state, и обновляет состояние светодиода на странице.

async function turnOn() {
    try {
        const response = await fetch('/lighton');
        const data = await response.json();
        updateLedStatus(data.state);
    } catch (error) {
        console.error('Error turning LED ON:', error);
        alert('Failed to turn LED ON');
    }
}

Функция turnOff() работает так же, только для кнопки OFF.

// Turn LED OFF
async function turnOff() {
    try {
        const response = await fetch('/lightoff');
        const data = await response.json();
        updateLedStatus(data.state);
    } catch (error) {
        console.error('Error turning LED OFF:', error);
        alert('Failed to turn LED OFF');
    }
}

Примечание переводчика: в полном листинге выше вместо updateLedStatus() состояние обновляется напрямую через document.getElementById('led-status').textContent — используйте именно полный листинг.

Получение нового значения температуры

Кнопка Get New Reading вызывает JavaScript-функцию getNewReading(), которая делает запрос по URL /temperature. Запрос получает сервер (RPi Pico), а что делать с ним, мы описываем в файле MicroPython. В ответ сервер шлёт JSON такого вида:

{
     "temperature_c": temperature_c,
     "temperature_f": temperature_f
}

Функция getNewReading() получает данные и подставляет их в соответствующие HTML-элементы.

async function getNewReading() {
    try {
        const response = await fetch('/temperature');

        const data = await response.json();

        document.getElementById('temp_c').textContent = data.temperature_c;
        document.getElementById('temp_f').textContent = data.temperature_f;
    } catch (error) {
        console.error('Error fetching temperature:', error);
        alert('Failed to get new reading');
    }
}

Создаём CSS-файл#

Откройте новый файл в Thonny IDE и скопируйте следующий CSS. Сохраните его на Raspberry Pi Pico под именем style.css.

html {
    font-family: Arial;
    background-color: #FFFFF7;
    color: #333;
    margin: 0;
    padding: 0;
}

body {
    max-width: 920px;
    width: 100%;
    margin: 0 auto;
    text-align: center;
    padding: 15px;
    box-sizing: border-box;
}

h1 {
    color: #0F3376;
    padding: 25px 0 15px 0;
    font-size: 2.1rem;
    margin: 0;
}

p {
    font-size: 1.55rem;
    margin: 20px auto;
}

.button {
    display: inline-block;
    background-color: #00A676;
    border: none;
    border-radius: 6px;
    color: white;
    padding: 14px 32px;
    text-decoration: none;
    font-size: 1.25rem;
    margin: 8px 12px;
    cursor: pointer;
    transition: background-color 0.3s ease, transform 0.1s ease;
    box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2);
    width: 170px;
}

.button:hover {
    background-color: #008c63;
    transform: translateY(-2px);
}

.button2 {
    background-color: #f44336;
}

.button2:hover {
    background-color: #d32f2f;
}

.button-refresh {
    background-color: #f0f0f0;
    color: #444;
    border: 1px solid #bbb;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    font-size: 1.15rem;
    width: 230px;
    padding: 12px 24px;
}

.button-refresh:hover {
    background-color: #e0e0e0;
    border-color: #999;
    transform: translateY(-1px);
}

.sensor-labels {
    font-size: 1.55rem;
    font-weight: bold;
    color: #444;
    margin-right: 12px;
}

.temp-value {
    font-size: 1.9rem;
    font-weight: lighter;
    color: #0F3376;
}

.units {
    font-size: 1.45rem;
    color: #555;
    margin-left: 3px;
}

.status {
    font-weight: bold;
    color: #0F3376;
}

a {
    text-decoration: none;
}

Исходный код на GitHub

Скрипт MicroPython: веб-сервер, отдающий файлы из файловой системы#

Теперь, когда все нужные файлы лежат в файловой системе Raspberry Pi Pico, напишем веб-сервер, который будет их отдавать, управлять GPIO и присылать температуру по запросу.

# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-web-server-filesystem-micropython/

# Import necessary modules
import network
import socket
import time
from picozero import pico_temp_sensor #must install the picozero package
from machine import Pin
import json

ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'

# Constant variables to save the files path
HTML_FILE_PATH = "index.html"
CSS_FILE_PATH = "style.css"
JS_FILE_PATH ="script.js"

# Create an LED object on pin 'LED' (this is the onboard LED)
led = Pin('LED', Pin.OUT)

# Initialize LED state
state = 'OFF'

# Function to read content from the file
def read_file(filepath):
    with open(filepath, "r") as file:
        return file.read()

# Get sensor readings
# Pico internal temperature sensor
def get_temperature():
    temperature_c = pico_temp_sensor.temp
    temperature_f = temperature_c * (9/5) + 32

    temperature_c = round(temperature_c)
    temperature_f = round(temperature_f)
    return temperature_c, temperature_f

# HTML template for the webpage
def webpage(state):
    html_content = read_file(HTML_FILE_PATH)
    temperature_c, temperature_f = get_temperature() 
    html = html_content.format(state=state, temperature_c=temperature_c, temperature_f=temperature_f)
    return html

# Init Wi-Fi Interface
def init_wifi(ssid, password):
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    # Connect to your network
    wlan.connect(ssid, password)
    # Wait for Wi-Fi connection
    connection_timeout = 10
    while connection_timeout > 0:
        print(wlan.status())
        if wlan.status() >= 3:
            break
        connection_timeout -= 1
        print('Waiting for Wi-Fi connection...')
        time.sleep(1)
    # Check if connection is successful
    if wlan.status() != 3:
        print('Failed to connect to Wi-Fi')
        return False
    else:
        print('Connection successful!')
        network_info = wlan.ifconfig()
        print('IP address:', network_info[0])
        return True

if not init_wifi(ssid, password):    
    print("Exiting program.")

else:
    try:
        # Set up socket and start listening
        addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
        s = socket.socket()
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind(addr)
        s.listen()
        print('Listening on', addr)

        # Main loop to listen for connections
        while True:
            try:
                conn, addr = s.accept()
                print('Got a connection from', addr)

                # Receive and parse the request
                request = conn.recv(1024)
                request_str = request.decode('utf-8')
                print('Request content:')

                try:
                    path = request.split()[1]
                    print('Requested path:', path)
                except IndexError:
                    pass

                # Process the request and update variables19
                if path == b'/lighton':
                    print('LED on')
                    led.value(1)
                    state = 'ON'
                    response = json.dumps({"state": state})
                    content_type = 'application/json'

                elif path == b'/lightoff':
                    print('LED off')
                    led.value(0)
                    state = 'OFF'
                    response = json.dumps({"state": state})
                    content_type = 'application/json'

                elif path == b'/temperature':
                    print('New temperature value requested')
                    temperature_c, temperature_f = get_temperature()
                    data = {
                        "temperature_c": temperature_c,
                        "temperature_f": temperature_f
                    }
                    response = json.dumps(data)
                    content_type = 'application/json'

                # process the request and send the files  
                elif path == b'/style.css':
                    response = read_file(CSS_FILE_PATH)
                    content_type = 'text/css'

                elif path == b'/script.js':
                    response = read_file(JS_FILE_PATH)
                    content_type = 'text/javascript'

                else: # / root path
                    response = webpage(state)
                    content_type = 'text/html'

                # Send the HTTP response and close the connection
                conn.send(f'HTTP/1.0 200 OK\r\nContent-type: {content_type}\r\n\r\n')
                conn.send(response)
                conn.close()

            except OSError as e:
                conn.close()
                print('Connection closed')

    except KeyboardInterrupt:
            print('Server stopped by user.')

Исходный код на GitHub

Установка пакета picozero

В этом примере на веб-страницу выводятся показания встроенного датчика температуры Pico. Чтобы их получить, используем пакет picozero. В Thonny IDE откройте Tools > Manage packages…, найдите picozero и установите пакет picozero @PyPI.

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

Кратко разберём, как работает код. Можно и сразу перейти к разделу с проверкой.

Пути к файлам

В этом примере нужно отдавать три разных файла: index.html, style.css и script.js. Начинаем с того, что задаём пути к ним. Файлы лежат в корневом каталоге файловой системы, так что достаточно указать их имена.

# Constant variables to save the files path
HTML_FILE_PATH = "index.html"
CSS_FILE_PATH = "style.css"
JS_FILE_PATH ="script.js"

Чтение файла из файловой системы

Как и в предыдущем простом примере, нужна функция, которая читает и возвращает содержимое файлов из файловой системы, — read_file().

# Function to read content from the file
def read_file(filepath):
    with open(filepath, "r") as file:
        return file.read()

HTML-шаблон

Функция webpage() возвращает веб-страницу с текущим состоянием GPIO и последними показаниями температуры. Эти значения подставляются в нужные места содержимого HTML-файла методом format(). Обратите внимание на заполнители в HTML-файле: {state}, {temperature_c} и {temperature_f}.

# HTML template for the webpage
def webpage(state):
    html_content = read_file(HTML_FILE_PATH)
    temperature_c, temperature_f = get_temperature() 
    html = html_content.format(state=state, temperature_c=temperature_c, temperature_f=temperature_f)
    return html

По этому шаблону мы отдаём страницу при первом обращении к корневому URL Raspberry Pi Pico. А при нажатии кнопок JavaScript обновляет только значения, не перезагружая всю страницу.

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

Дальше в главном цикле (while True:) проверяем содержимое запроса, формируем и отправляем соответствующий ответ.

# Main loop to listen for connections
while True:
    try:
        conn, addr = s.accept()
        print('Got a connection from', addr)

        # Receive and parse the request
        request = conn.recv(1024)
        request_str = request.decode('utf-8')
        print('Request content:')

        try:
            path = request.split()[1]
            print('Requested path:', path)
        except IndexError:
            pass

Открывая страницу, браузер запросит файлы style.css и script.js — то есть сделает запросы по путям /style.css и /script.js. Чтобы сформировать ответ, читаем содержимое CSS- или JavaScript-файла из файловой системы функцией read_file(). Также нужно указать соответствующий тип содержимого.

# process the request and send the files  
elif path == b'/style.css':
    response = read_file(CSS_FILE_PATH)
    content_type = 'text/css'

elif path == b'/script.js':
    response = read_file(JS_FILE_PATH)
    content_type = 'text/javascript'

В конце отправляем заголовок ответа с типом содержимого и сам ответ.

# Send the HTTP response and close the connection
conn.send(f'HTTP/1.0 200 OK\r\nContent-type: {content_type}\r\n\r\n')
conn.send(response)

Кроме того, когда пользователь нажимает кнопки на странице, мы отправляем клиенту JSON:

  • кнопка ON → запрос /lighton;
  • кнопка OFF → запрос /lightoff;
  • кнопка Get New Reading → запрос /temperature.

В этом случае тип содержимого ответа — application/json.

# Process the request and update variables19
if path == b'/lighton':
    print('LED on')
    led.value(1)
    state = 'ON'
    response = json.dumps({"state": state})
    content_type = 'application/json'

elif path == b'/lightoff':
    print('LED off')
    led.value(0)
    state = 'OFF'
    response = json.dumps({"state": state})
    content_type = 'application/json'

elif path == b'/temperature':
    print('New temperature value requested')
    temperature_c, temperature_f = get_temperature()
    data = {
        "temperature_c": temperature_c,
        "temperature_f": temperature_f
    }
    response = json.dumps(data)
    content_type = 'application/json'

Наконец, если запрос не совпал ни с одним из перечисленных, значит, клиент обратился к корневому URL / или к какому-то другому, здесь не обрабатываемому. В этом случае просто отдаём HTML-страницу с текущими значениями температуры и состояния светодиода: формируем ответ вызовом webpage(), передав ей текущее состояние светодиода.

else: # / root path
    response = webpage(state)
    content_type = 'text/html'

Проверяем веб-сервер#

Убедитесь, что в файловую систему Pico загружены файлы:

  • index.html
  • script.js
  • style.css

Убедитесь также, что вы установили пакет picozero.

Затем загрузите скрипт MicroPython в файловую систему RPi под именем main.py или просто запустите его зелёной кнопкой Run.

Если открыть View > Files, картина должна быть примерно такой.

Файлы index.html, main.py, script.js и style.css в файловой системе Pico

Файлы в файловой системе Raspberry Pi Pico

Сервер запустится, а IP-адрес Pico появится в консоли. Откройте этот адрес в браузере.

Pico отдаст index.html, style.css и script.js, и вы увидите такую страницу.

Веб-страница Raspberry Pi Pico Web Server с состоянием светодиода, кнопками и температурой

Готовая веб-страница

Кнопками ON и OFF можно управлять светодиодом, а кнопкой Get New Reading — запрашивать новое значение температуры.

Итоги#

В этом руководстве мы сделали на Raspberry Pi Pico веб-сервер, который отдаёт файлы из своей файловой системы. Сначала разобрали, как отдать простую HTML-страницу:

  • сохраняем HTML-файл в файловой системе Pico;
  • пишем функцию, которая читает этот файл и сохраняет его содержимое в переменной MicroPython;
  • формируем HTTP-ответ с этим содержимым.

Затем разобрались, как отдавать остальные нужные странице файлы — CSS и JavaScript, — и как отправлять JSON, который обрабатывает JavaScript.

Если файлов несколько, это более практичный способ делать веб-серверы на Pico: файлы лежат упорядоченно, и вносить изменения потом проще.

Другие руководства по RPi Pico, которые могут вам понравиться: