В данной статье мы узнаем, как программировать плату Raspberry Pi Pico W на языке Micropython, подробно узнаем о его функциях, о том, как использовать Wi-Fi для подключения к Интернету и, наконец, что не менее важно, о том, как управлять светодиодом с помощью специального веб-сервера из любой точки мира.
Ранее на нашем сайте мы рассматривали аналогичные проекты веб-сервера на основе модуля ESP32, на Raspberry Pi с использованием Node.js, на основе ESP8266 и платы STM32F103C8, а также веб-сервер AJAX на ESP8266.
Raspberry Pi Pico W
Фонд Raspberry Pi выпустил плату Pico W (купить на AliExpress), представляющую собой микроконтроллер на базе чипа RP2040. Его цена составляла 6 долларов. Pico W оснащен радиомодулем Wi-Fi 802.11n, что делает его подходящим для проектов Интернета вещей.
Pico W можно использовать в качестве замены проектам, основанным на его предшественнике Pico, предлагая удобство встроенного Wi-Fi и Bluetooth.
Недавно в программный стек была добавлена поддержка Bluetooth, но даже тогда она была ограничена языком программирования C/C++. Поддержка Micropython еще не добавлена полноценно в Pico W (поэтому в этой статье мы не будем рассматривать Bluetooth, будет рассмотрен только Wi-Fi).
Схема проекта
Схема проекта веб-сервера на Raspberry Pi Pico W для мигания светодиодом представлена на следующем рисунке.
Настройка Raspberry Pi Pico W для Micropython
- Подключите Raspberry Pi Pico W к компьютеру с помощью кабеля micro USB.
- Загрузите и установите Thonny IDE.
- Откройте Thonny и нажмите Tools>options («Инструменты»>«Параметры»).
- В меню параметров нажмите "Interpreter" («Интерпретатор») и выберите из списка «Raspberry Pi Pico».
- Thonny установит на Pico W необходимую прошивку и подготовит ее к программированию.
- Скопируйте и вставьте приведенный ниже код в thonny ide. Этот код должен заставить мигать встроенный светодиод на Pico W.
1 2 3 4 5 6 |
import machine import time led_pin = machine.Pin(25, machine.Pin.OUT) while True: led_pin.toggle() time.sleep(1) |
Результат выполнения этого кода будет выглядеть примерно так:
Загрузка кода в Raspberry Pi Pico W
- Подключите Raspberry Pi Pico W к компьютеру с помощью кабеля micro USB.
- Нажмите "File" в строке меню и выберите "Save As" («Сохранить как»), чтобы сохранить файл с кодом.
- Выберите подходящее место на Raspberry Pi Pico и сохраните файл с расширением «.py» (например, «blink.py»).
- В Thonny нажмите "Run" («Выполнить») в строке меню и выберите "Run current script («Выполнить текущий скрипт») или нажмите клавишу F5.
- Thonny загрузит код в Pico W, и веб-сервер LED начнет работать.
Написание кода Micropython для подключения к Wi-Fi
Давайте проверим простой код для подключения платы Raspberry Pi Pico W к точке доступа Wi-Fi. Первым делом нам нужно импортировать все необходимые библиотеки для подключения к Wi-Fi в Thonny IDE.
1 2 3 |
import network from time import sleep import machine |
ssid = 'NAME OF YOUR WIFI NETWORK' (имя вашей сети Wi-Fi).
Кроме ssid также необходимо указать и пароль для входа в вашу сеть Wi-Fi.
1 |
password = 'YOUR SECRET PASSWORD' |
Далее напишем функцию, которая будет активировать WLAN, что, в свою очередь, поможет нам установить соединение. Первая строка network.WLAN(network.STA_IF) настраивает устройство в качестве клиента. wlan.active(True) активирует Wi-Fi, а wlan.connect(ssid,password) устанавливает соединение Wi-Fi. Следующая строка — это цикл while, который ожидает соединения и его установления. Когда цикл заканчивается, печатаются IP-адрес Wi-Fi и другие данные.
1 2 3 4 5 6 7 8 |
def connect(): wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) while wlan.isconnected() == False: print('Waiting for connection...') sleep(1) print(wlan.ifconfig()) |
В следующем фрагменте кода мы будем пытаться вызвать функцию connect, а если поступит прерывание от клавиатуры мы будем автоматически сбрасывать состояние платы.
1 2 3 4 |
try: connect() except KeyboardInterrupt(): machine.reset() |
Написание кода Micropython для веб-сервера мигания светодиодом
Теперь, когда мы закончили с подключением нашей платы к Wi-Fi, давайте выясним, как мы можем создать веб-сервер, через который мы сможем управлять нашим светодиодом. Ниже приведено объяснение фрагментов этого кода. Полный код программы можно найти в самом конце данной статьи.
Следующий код пытается импортировать модуль usocket, который является специфичной для MicroPython реализацией модуля socket. Если это не удается, он возвращается к импорту стандартного модуля socket.
1 2 3 4 |
try: import usocket as socket except: import socket |
Следующие строки импортируют необходимые нам модули: Pin из модуля machine для управления выводами GPIO, network для сетевых операций. Вызов gc.collect() выполняет сбор мусора для освобождения памяти.
1 2 3 4 |
from machine import Pin import network import gc gc.collect() |
Далее мы задаем имя сети Wi-Fi (ssid) и пароль (password). Вам необходимо заменить ssid и пароль на их значения для вашей точки доступа. Переменная station представляет интерфейс станции Wi-Fi. Код активирует интерфейс станции и подключается к указанной сети, используя предоставленные учетные данные.
1 2 3 4 5 |
ssid = 'your ssid' password = 'your password' station = network.WLAN(network.STA_IF) station.active(True) station.connect(ssid, password) |
Следующий цикл ожидает, пока интерфейс станции не будет успешно подключен к сети Wi-Fi.
1 2 |
while station.isconnected() == False: pass |
Как только соединение будет установлено, мы будем печатать сообщение об успехе и отображать IP-адрес, назначенный микроконтроллеру.
1 2 |
print('Connection successful') print(station.ifconfig()) |
Следующий код инициализирует переменную светодиода для управления светодиодом, подключенным к выводу GPIO с именем «LED», который является светодиодом по умолчанию, подключенным к выводу 25. Он настраивает вывод как выход (Pin.OUT) и устанавливает начальное состояние светодиода на "ВЫКЛЮЧЕННЫЙ".
1 2 |
led = Pin('LED', Pin.OUT) led_state = "OFF" |
HTML-код находится внутри функции с именем webpage.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
def wepage(): <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous"> <style> html { font-family: Arial; display: inline-block; margin: 0px auto; text-align: center; } .button { background-color: #ce1b0e; border: none; color: white; padding: 16px 40px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; } .button1 { background-color: #000000; } </style> </head> <body> <h2>Raspberry Pi Pico Web Server</h2> <p>LED state: <strong>""" + led_state + """</strong></p> <p> <i class="fas fa-lightbulb fa-3x" style="color:#c81919;"></i> <a href=\"led_on\"><button class="button">LED ON</button></a> </p> <p> <i class="far fa-lightbulb fa-3x" style="color:#000000;"></i> <a href=\"led_off\"><button class="button button1">LED OFF</button></a> </p> </body> </html> |
Этот HTML-код создает простую веб-страницу со следующими элементами.
Раздел <head> включает метатег области просмотра для адаптивного дизайна и ссылку для включения файла CSS.
Стили CSS, определенные в тегах <style>, применяются к элементам HTML. Здесь элемент html центрирован, и стили определены для двух классов: .button и .button1.
Раздел <body> содержит содержимое веб-страницы.
В заголовке <h2> отображается текст «Raspberry Pi Pico Web Serve».
Параграф <p> отображает текущее состояние светодиода с помощью переменной led_state.
Есть два абзаца с кнопками, каждый из которых содержит значок и элемент кнопки, заключенный в тег привязки <a>. Первая кнопка имеет кнопку класса, а вторая кнопка имеет классы button и button1.
Когда эта HTML-страница обслуживается веб-сервером, она отображает состояние светодиода и предоставляет кнопки для включения и выключения светодиода.
Следующий код создает сокет с помощью функции socket.socket() и указывает семейство адресов (socket.AF_INET) и тип сокета (socket.SOCK_STREAM).
1 |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
Далее привязываем сокет к указанному IP-адресу ('' означает все доступные интерфейсы) и номеру порта 80.
1 |
s.bind(('', 80)) |
Затем устанавливаем сокет на прослушивание входящих соединений. Параметр 5 указывает максимальное количество соединений в очереди.
1 |
s.listen(5) |
После этого в коде у нас бесконечный цикл для непрерывного приема входящих соединений.
1 2 3 4 5 6 7 8 9 |
while True: try: conn, addr = s.accept() conn.settimeout(3.0) print('Received HTTP GET connection request from %s' % str(addr)) request = conn.recv(1024) conn.settimeout(None) request = str(request) print('GET Request Content = %s' % request) |
s.accept() ждет, пока клиент подключится, и возвращает новый разъем сокета для связи и адрес клиента addr.
conn.settimeout(3.0) устанавливает тайм-аут в 3 секунды для операций сокета.
Код печатает информацию о клиенте, установившем соединение, и получает от клиента HTTP-запрос GET с помощью conn.recv(1024). Затем запрос преобразуется в строку для дальнейшей обработки.
Затем мы ищем наличие «/led_on» и «/led_off» в полученном запросе, используя метод find().
Если «/led_on» находится в позиции 6, это означает, что светодиод должен быть включен. Код устанавливает состояние светодиода, включает его и соответствующим образом обновляет переменную led_state.
Если «/led_off» находится в позиции 6, это означает, что светодиод следует выключить. Код устанавливает состояние светодиода, выключает его и соответствующим образом обновляет переменную led_state.
1 2 3 4 5 6 7 8 9 10 |
led_on = request.find('/led_on') led_off = request.find('/led_off') if led_on == 6: print('LED ON -> GPIO25') led_state = "ON" led.on() if led_off == 6: print('LED OFF -> GPIO25') led_state = "OFF" led.off() |
Следующий код генерирует ответ HTML с помощью функции web_page(). Он отправляет заголовки HTTP-ответа клиенту с помощью conn.send(). Также он отправляет содержимое ответа с помощью conn.sendall(). Наконец, он закрывает соединение с клиентом с помощью conn.close().
1 2 3 4 5 6 |
response = web_page() conn.send('HTTP/1.1 200 OK\n') conn.send('Content-Type: text/html\n') conn.send('Connection: close\n\n') conn.sendall(response) conn.close() |
Если во время выполнения возникает ошибка OSError, это означает, что возникла проблема с соединением. Код закрывает соединение с клиентом и печатает сообщение о том, что соединение закрыто.
1 2 3 |
except OSError as e: conn.close() print('Connection closed') |
Рассмотренный здесь код настраивает базовый веб-сервер, который прослушивает запросы HTTP GET, обрабатывает запросы на включение или выключение светодиода и отправляет HTML-ответ обратно клиенту.
Проверка работы веб-сервера
- Откройте веб-браузер на любом устройстве, подключенном к той же сети, что и Raspberry Pi Pico W.
- Введите IP-адрес Pico W в адресную строку подключенного устройства. В этом примере я использовал свой смартфон. Вы можете найти IP-адрес, напечатанный на экране, после запуска приведенного выше кода.
- Когда вы вводите адрес в адресную строку браузера вы должны увидеть простую веб-страницу с кнопками для включения и выключения светодиода.
- Нажимайте на кнопки, чтобы управлять светодиодом.
Исходный код программы
Весь необходимый код программы для представленного проекта вы можете скачать по следующей ссылке. Также он представлен в виде следующего листинга.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# Pico W led blink import machine import time led_pin = machine.Pin(25, machine.Pin.OUT) while True: led_pin.toggle() time.sleep(1) # Pico W led webserver try: import usocket as socket except: import socket from machine import Pin import network import gc gc.collect() ssid = 'Enter_Your_SSID' password = 'Enter_Your_PASSWORD' station = network.WLAN(network.STA_IF) station.active(True) station.connect(ssid, password) while station.isconnected() == False: pass print('Connection successful') print(station.ifconfig()) led = Pin('LED', Pin.OUT) led_state = "OFF" def web_page(): html = """<html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous"> <style> html { font-family: Arial; display: inline-block; margin: 0px auto; text-align: center; } .button { background-color: #ce1b0e; border: none; color: white; padding: 16px 40px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; } .button1 { background-color: #000000; } </style> </head> <body> <h2>Raspberry Pi Pico Web Server</h2> <p>LED state: <strong>""" + led_state + """</strong></p> <p> <i class="fas fa-lightbulb fa-3x" style="color:#c81919;"></i> <a href=\"led_on\"><button class="button">LED ON</button></a> </p> <p> <i class="far fa-lightbulb fa-3x" style="color:#000000;"></i> <a href=\"led_off\"><button class="button button1">LED OFF</button></a> </p> </body> </html>""" return html s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('', 80)) s.listen(5) while True: try: conn, addr = s.accept() conn.settimeout(3.0) print('Received HTTP GET connection request from %s' % str(addr)) request = conn.recv(1024) conn.settimeout(None) request = str(request) print('GET Rquest Content = %s' % request) led_on = request.find('/led_on') led_off = request.find('/led_off') if led_on == 6: print('LED ON -> GPIO25') led_state = "ON" led.on() if led_off == 6: print('LED OFF -> GPIO25') led_state = "OFF" led.off() response = web_page() conn.send('HTTP/1.1 200 OK\n') conn.send('Content-Type: text/html\n') conn.send('Connection: close\n\n') conn.sendall(response) conn.close() except OSError as e: conn.close() print('Connection closed') |