Руководство по настройке Raspberry Pi и NodeMCU для передачи сообщений в коде Морзе через Wi-Fi и отображения их с помощью светодиода.
Основные шаги:
- Создаем Wi-Fi точку доступа на малине.
- Заливаем прошивку Micropython на NodeMcu.
- Скрипт
morse8266.py
автоматически подключается к Wi-Fi точке доступа малины и ждет сообщения. - С помощью скрипта
morse_rpi.py
отправляем с малины сообщение на NodeMcu.
Как настроить Wi-Fi точку доступа с автозапуском на Raspberry Pi?
Подробная инструкция в этой статье (дойти до пункта Настроим IP-форвардинг).
Код
Код Raspberry Pi morse_rpi.py
Импорт библиотек
import socket
: Импортирует библиотеку для работы с сетевыми сокетами. Используется для отправки сообщений по сети.import argparse
: Импортирует библиотеку для обработки аргументов командной строки. Это позволяет программе принимать параметры от пользователя при запуске.
Словарь MORSE_CODE_DICT
Этот словарь содержит соответствия между символами алфавита (как латинского, так и русского) и их кодами в азбуке Морзе. Каждому символу соответствует строка из точек и тире.
MORSE_CODE_DICT = {
'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.',
'F': '..-.', 'G': '--.', 'H': '....', 'I': '..', 'J': '.---',
'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.', 'O': '---',
'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-',
'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-', 'Y': '-.--',
'Z': '--..', '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....',
'6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----',
', ': '--..--', '.': '.-.-.-', '?': '..--..', '/': '-..-.', '-': '-....-',
'(': '-.--.', ')': '-.--.-', ' ': ' ',
'А': '.-', 'Б': '-...', 'В': '.--', 'Г': '--.', 'Д': '-..', 'Е': '.',
'Ё': '.', 'Ж': '...-', 'З': '--..', 'И': '..', 'Й': '.---', 'К': '-.-',
'Л': '.-..', 'М': '--', 'Н': '-.', 'О': '---', 'П': '.--.', 'Р': '.-.',
'С': '...', 'Т': '-', 'У': '..-', 'Ф': '..-.', 'Х': '....', 'Ц': '-.-.',
'Ч': '---.', 'Ш': '----', 'Щ': '--.-', 'Ъ': '.--.-.', 'Ы': '-.--',
'Ь': '-..-', 'Э': '..-..', 'Ю': '..--', 'Я': '.-.-'
}
Функция text_to_morse(text)
- Цель: Преобразовать текстовое сообщение в код Морзе.
- Процесс:
- Приводит текст к верхнему регистру и разбивает его на слова.
- Для каждого слова и каждого символа в слове ищет соответствующий код Морзе в словаре
MORSE_CODE_DICT
. - Собирает символы Морзе в слова, разделяя их пробелами.
- Возвращает строку, в которой слова в коде Морзе разделены тремя пробелами.
def text_to_morse(text):
morse_code = []
for word in text.upper().split(' '):
morse_letters = []
for char in word:
morse_char = MORSE_CODE_DICT.get(char, '')
if morse_char:
morse_letters.append(morse_char)
morse_word = ' '.join(morse_letters)
morse_code.append(morse_word)
return ' '.join(morse_code) # Три пробела между словами
Функция send_morse_code(message, ip, port)
- Цель: Отправить закодированное в Морзе сообщение на заданный IP-адрес и порт.
- Процесс:
- Преобразует текстовое сообщение в код Морзе с помощью
text_to_morse
. - Создает UDP-сокет.
- Отправляет сообщение на указанный IP-адрес и порт.
- Обрабатывает возможные исключения при отправке.
- Закрывает сокет после отправки.
- Преобразует текстовое сообщение в код Морзе с помощью
def send_morse_code(message, ip, port):
morse_code = text_to_morse(message)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
sock.sendto(morse_code.encode(), (ip, port))
print(f'Отправлено сообщение: "{message}" как Морзе: "{morse_code}"')
except Exception as e:
print(f'Ошибка при отправке сообщения: {e}')
finally:
sock.close()
Функция main()
- Цель: Обработать аргументы командной строки и инициировать отправку сообщения.
- Процесс:
- Создает объект
ArgumentParser
для описания программы и ее аргументов. - Добавляет аргументы:
message
для текста сообщения,--ip
для IP-адреса и--port
для порта. - Парсит аргументы командной строки.
- Объединяет части сообщения в одну строку.
- Вызывает
send_morse_code
с полученными параметрами.
- Создает объект
def main():
parser = argparse.ArgumentParser(description='Отправка сообщений в виде Морзе кодом на ESP.')
parser.add_argument('message', type=str, nargs='+', help='Сообщение для отправки')
parser.add_argument('--ip', type=str, default='192.168.4.11', help='IP адрес ESP устройства')
parser.add_argument('--port', type=int, default=12345, help='UDP порт ESP устройства')
args = parser.parse_args()
message = ' '.join(args.message)
target_ip = args.ip
target_port = args.port
send_morse_code(message, target_ip, target_port)
Код NodeMcu morse8266.py
Импорт библиотек
import network
: Библиотека для работы с сетевыми интерфейсами, такими как Wi-Fi.import socket
: Библиотека для работы с сетевыми сокетами, используемыми для передачи данных.import time
: Библиотека для работы с временем, используется для задержек.from machine import Pin
: Библиотека для работы с пинами микроконтроллера, позволяет управлять светодиодом.import sys
: Библиотека для работы с системными функциями, используется для выхода из программы.
Конфигурация Wi-Fi
- SSID и PASSWORD: Задаются имя сети (SSID) и пароль для подключения к Wi-Fi.
Определения временных интервалов для азбуки Морзе
- UNIT: Основная единица времени, на основе которой определяются длительности точек, тире и промежутков.
- DOT_DURATION, DASH_DURATION: Длительности точки и тире.
- INTRA_CHAR_GAP, INTER_CHAR_GAP, INTER_WORD_GAP: Промежутки между элементами символа, символами и словами соответственно.
# Morse Code Timing Definitions (in seconds)
UNIT = 0.2 # Duration of one unit
DOT_DURATION = UNIT
DASH_DURATION = 3 * UNIT
INTRA_CHAR_GAP = UNIT # Gap between dots and dashes within a character
INTER_CHAR_GAP = 3 * UNIT # Gap between characters
INTER_WORD_GAP = 7 * UNIT # Gap between words
Инициализация Wi-Fi
- Создается объект
station
для управления Wi-Fi интерфейсом. - Активируется интерфейс и производится попытка подключения к заданной сети.
- Выводится на экран состояние подключения. Если подключение не удалось, программа завершает работу.
# Initialize WiFi
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect(SSID, PASSWORD)
Инициализация светодиода
- LED_PIN: Номер пина, к которому подключен светодиод.
- Создается объект
led
для управления светодиодом.
# Initialize LED
LED_PIN = 2 # Change this if your LED is connected to a different pin
led = Pin(LED_PIN, Pin.OUT)
Функция blink(duration)
- Включает светодиод на заданное время, затем выключает его.
def blink(duration):
"""Blink the LED for the specified duration."""
led.on()
time.sleep(duration)
led.off()
Функция parse_and_blink(morse_code)
- Парсит строку, содержащую сообщение в коде Морзе, и мигает светодиодом в соответствии с кодом.
- Поддерживает точки (.), тире (-) и пробелы для разделения символов.
"""
Parse the received Morse code string and blink the LED accordingly. Supports dots (.), dashes (-), and spaces for gaps. """
print(f'Parsing Morse code: "{morse_code}"')
for symbol in morse_code:
if symbol == '.':
blink(DOT_DURATION)
time.sleep(INTRA_CHAR_GAP)
elif symbol == '-':
blink(DASH_DURATION)
time.sleep(INTRA_CHAR_GAP)
elif symbol == ' ':
time.sleep(INTER_CHAR_GAP - INTRA_CHAR_GAP) # Already waited INTRA_CHAR_GAP after symbol
else:
pass
print('Finished parsing Morse code.')
Функция receive_and_process(sock)
- Ожидает и обрабатывает входящие сообщения в коде Морзе через UDP-сокет.
- Устанавливает таймаут на получение данных и обрабатывает исключения.
- Декодирует полученные данные и вызывает
parse_and_blink
для отображения сообщения.
def receive_and_process(sock):
"""Receive and process incoming Morse code messages."""
try:
while True:
sock.settimeout(10.0) # Timeout set to 10 seconds
try:
data, addr = sock.recvfrom(1024) # Buffer size is 1024 bytes
morse_message = data.decode('utf-8').strip()
print(f'Received message from {addr}: "{morse_message}"')
if morse_message:
parse_and_blink(morse_message)
except OSError as e:
print("Socket timeout, no message received.")
except KeyboardInterrupt:
print("Script execution interrupted")
finally:
sock.close()
print("Socket closed")
# Initialize UDP Server
Инициализация UDP-сервера
- UDP_IP и UDP_PORT: Задаются IP-адрес и порт для приема UDP-сообщений.
- Создается и привязывается UDP-сокет.
- Запускается сервер, который ожидает и обрабатывает сообщения.
UDP_IP = '0.0.0.0'
UDP_PORT = 12345
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))
print('UDP server started and waiting for messages...')
receive_and_process(sock)
Основной цикл
- Программа постоянно ожидает входящие сообщения и обрабатывает их, мигая светодиодом в соответствии с полученным кодом Морзе.
Подготовка Raspberry Pi
Установим библиотеку esptool
, чтобы прошивать ESP32:
pip install esptool
Подготовка NodeMcu
Прошивка MicroPython на NodeMcu
Скачаем прошивку MicroPython для NodeMcu, например v1.23.0 (2024-06-02) .bin
Сотрем содержимое флеш-памяти NodeMcu (команда для Windows):
python -m esptool --port COM5 erase_flash
Как найти порт, к которому подключен NodeMcu?
На винде введем в CMD
mode
либо в powershell
[System.IO.Ports.SerialPort]::getportnames()
В данном случае порт COM5
Запишем прошивку на NodeMcu
python -m esptool --port COM5 --baud 460800 write_flash --flash_size=detect 0 ESP8266_GENERIC-20240602-v1.23.0.bin
Получим
Установим mpremote
Установим библиотеку mpremote
, которая используется для работы с MicroPython-устройствами (запись и запуск python-файлов на NodeMcu).
pip install mpremote
Загрузим morse8266.py
на NodeMcu
mpremote connect COM5 fs cp morse8266.py :
Двоеточие обязательно.
Запустим morse8266.py
на NodeMcu
mpremote run morse8266.py
ESP8266 подключится к Wi-FI Raspberry Pi и перейдет в режим «Ожидание передачи».
Узнаем IP-адрес NodeMcu
cat /var/lib/misc/dnsmasq.leases
Получим
1726944373 dc:4f:22:5e:9e:22 192.168.4.11 mpy-esp8266 *
IP-адрес NodeMcu, подключенного по Wi-Fi к Raspberry Pi — 192.168.4.11
Запустим morse_rpi.py
на Raspberry Pi
python morse_rpi.py Привет. Как дела? --ip 192.168.4.11 --port 12345
Получим
Если в функции main()
указан правильный IP-адрес и порт NodeMcu, то в команде можно обойтись без IP-адреса и порта
python morse_rpi.py Привет. Как дела?
Работают обе команды. Пригодится, когда подключено несколько устройств.
Настройка атозапуска morse8266.py
Два варианта автозапуска скрипта.
- Переименуем скрипт:
- Переименуем скрипт
morse8266.py
вmain.py
. MicroPython автоматически выполняетmain.py
послеboot.py
при загрузке устройства.
- Переименуем скрипт
- Использовать
boot.py
для вызова скрипта:- Просто импортируем
morse8266
в файлеboot.py
в NodeMcu.
- Просто импортируем
# boot.py
import morse8266
Результат работы
Передача сообщения Привет. Как дела?
Полный код
Полный код доступен в репозитории на Гитхабе.