Урок 10. Музыкальный сервер
А теперь перейдём к созданию музыкального сервера. На этот раз не будем пользоваться сторонними библиотеками, а воспользуемся встроенными возможностями. Также задействуем созданный в прошлом уроке модуль.
Для начала сохраним наш прошлый скрипт как melodies.py на компьютере. А затем с помощью встроенного в IDE файлового менеджера скопируем этот скрипт на контроллер. После этого мы сможем вызывать функции elochka()
и antoshka()
из других модулей.
Вы уже знаете что на контроллере присутствует файл boot.py
. Изменим содержимое этого файла. В нём будем производить подключение к сети WiFi.
Помимо этого для создания сервера нам понадобится модуль socket
. Из документации по языку указано что библиотеку лучше подключать следующим способом:
try: import usocket as socket except: import socket
Затем укажем что нам не нужны отладочные сообщения платы:
import esp esp.osdebug(None)
Далее оставим сборщик мусора, который уже присутствует в скрипте загрузки:
import gc gc.collect()
И подключим необходимые для работы модули. Из модуля machine
импортируем класс Pin
, который отвечает за выводы контроллера. Также импортируем модуль, отвечающий за работу со временем time
и не забудем про модуль для работы с подключением к сети network
.
from machine import Pin import time import network
Выведем отладочное сообщение чтобы понимать порядок запуска процедур и функций:
print("Это загрузочный модуль")
Подключение к сети практически не отличается от уже изученных нами уроков:
# параметры сети wifi_name = "qwerty" wifi_pass = "12345678" # соединение с wifi в качестве клиента wifi = network.WLAN(network.STA_IF) wifi.active(True) if not wifi.isconnected(): wifi.connect(wifi_name, wifi_pass) while not wifi.isconnected(): print("Попытка подключения...") time.sleep(1) # вывод IP-адреса IP = wifi.ifconfig()[0] print("Успешно подключились. IP адрес: {IP}".format(IP=IP))
На этом закончим редактирование скрипта boot.py
. Полностью его код будет выглядеть следующим образом:
try: import usocket as socket except: import socket import esp esp.osdebug(None) import gc gc.collect() from machine import Pin import time import network print("Это загрузочный модуль") # параметры сети wifi_name = "qwerty" wifi_pass = "12345678" # соединение с wifi в качестве клиента wifi = network.WLAN(network.STA_IF) wifi.active(True) if not wifi.isconnected(): wifi.connect(wifi_name, wifi_pass) while not wifi.isconnected(): print("Попытка подключения...") time.sleep(1) # вывод IP-адреса IP = wifi.ifconfig()[0] print("Успешно подключились. IP адрес: {IP}".format(IP=IP))
Нам осталось лишь описать функции сервера.
Для начала подключим наш модуль с мелодиями, и импортируем оттуда две функции, отвечающие за воспроизведение мелодий:
# подключение модуля с мелодиями from melodies import elochka, antoshka
Немного скорректируем интернет-страницу из прошлого проекта:
# функция возвращает интернет-страницу def html_page(): html_page = """ <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Музыкальный веб-сервер Гиккон</title> </head> <body> <h1>Выберите музыку для воспроизведения:</h1> <p><a href="/antoshka">Антошка</a></p> <p><a href="/elochka">Ёлочка</a></p> </body> </html> """ return html_page
И опишем как сервер будет понимать запросы с сайта. Мы будем пользоваться библиотекой socket
, которую подключали в скрипте boot.py. Socket в переводе с английского - гнездо, розетка, порт. По сути мы должны создать порт, который будем прослушивать. Создадим переменную порта, и укажем служебные параметры, в них мы углубляться не будем, они очень специфичные и используются всегда для такого типа соединений.
# переменная для создания прослушиваемого порта s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Если на этот порт поступает какая-то информация (запросы), мы её обрабатываем и в зависимости от того что пришло принимаем решение, какие действия будут выполняться. Мы будем прослушивать порт 80
на входящие сообщения. После этого мы переведём порт в режим прослушки (приёма) входящих соединений:
s.bind(("", 80)) s.listen(5)
После этого в цикле, когда получаем какое-то соединение, функция accept
нам возвращает кортеж из объекта соединения (conn
) и адреса (откуда пришло соединение, с какого IP-адреса).
while True: conn, addr = s.accept() print("Получено соединение от IP-адреса %s" % str(addr))
Затем мы получаем из объекта соединения данные и проверяем, пришли ли они вообще. Если их нет, соединение закрываем.
data = conn.recv(1024) if not data: # данные не пришли conn.close() continue # не обрабатываем
Далее требуется расшифровать данные, ведь они имеют тип byte
, который неудобен для чтения и строкового представления. После этого, расшифрованные данные (строку) разделим и получим три строки, которые содержат вызванный метод, строку соединения и описание протокола. Из этого нас интересует только строка, но для отладки выведем всё что получили.
udata = data.decode("utf-8") # нужна только первая строка udata = udata.split("\r\n", 1)[0] print("Первая строка данных: %s" % udata) # разделить строку по пробелам method, string, protocol = udata.split(" ", 2) # вывести полученные метод, строку и протокол print(method, string, protocol)
Если полученная строка - это команда с выведенного сайта, то выполняем действия. Если команда “ёлочка”, воспроизведём мелодию Ёлочка. Если команда “антошка”, то воспроизведём мелодию Антошка.
# если строка - это команда "/turn_led", тогда переключить светодиод if string == "/antoshka": antoshka() elif string == "/elochka": elochka()
После того как запрос обработан, нам нужно отправить ответ. В ответ вернём нашу интернет страницу, перед этим передав служебную информацию о соединении, затем соединение можно закрыть.
response = html_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()
На этом функция main()
завершена. Мы создали порт прослушки, указали как обработать полученные данные и вернули ответ.
В конце, уже знакомая конструкция, проверим запущен ли текущий модуль как главный, если да, будем выполнять функцию main()
, а перед этим выведем сообщения, для понимания что мы вошли в главный скрипт (модуль) из модуля загрузки.
if __name__ == "__main__": print("Это главный модуль") main()
Целиком код модуля будет выглядеть следующим образом:
# подключение модуля с мелодиями from melodies import elochka, antoshka # функция возвращает интернет-страницу def html_page(): html_page = """ <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Музыкальный веб-сервер Гиккон</title> </head> <body> <h1>Выберите музыку для воспроизведения:</h1> <p><a href="/antoshka">Антошка</a></p> <p><a href="/elochka">Ёлочка</a></p> </body> </html> """ return html_page def main(): # переменная для создания прослушиваемого порта s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("", 80)) s.listen(5) while True: conn, addr = s.accept() print("Получено соединение от IP-адреса %s" % str(addr)) data = conn.recv(1024) if not data: # данные не пришли conn.close() continue # не обрабатываем udata = data.decode("utf-8") # нужна только первая строка udata = udata.split("\r\n", 1)[0] print("Первая строка данных: %s" % udata) # разделить строку по пробелам method, string, protocol = udata.split(" ", 2) # вывести полученные метод, строку и протокол print(method, string, protocol) # если строка - это команда "/turn_led", тогда переключить светодиод if string == "/antoshka": antoshka() elif string == "/elochka": elochka() response = html_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() if __name__ == "__main__": print("Это главный модуль") main()
Наш сервер готов. Далее нам нужно все три модуля загрузить на контроллер. Модуль boot.py мы загрузим на контроллер и заместим уже имеющийся там модуль boot.py. Модуль melodies.py, который отвечает за воспроизведение музыки загрузим как есть, с тем же именем. И последний модуль загрузим и сделаем его главным. После этого можно перезагрузить контроллер и, зайдя на сайт модуля (посмотреть IP-адрес сайта можно в отладочных сообщениях, которые будут видны если включить режим REPL).
Мы создали веб-сервер без использования сторонних библиотек. Когда мы использовали стороннюю библиотеку micropyserver, она по сути делала все шаги по созданию порта и его прослушки за нас, программирование получалось легче. Однако, если Вам потребуется более гибко настраивать порты и прочие параметры работы с портами, можно воспользоваться библиотекой socket. В любом случае решение остаётся за Вами.