В предыдущей статье мы начали строить систему сбора данных о погоде на базе контроллера micro::bit и радиомодуля LoRa и остановились на сборке. Сегодня займемся программированием радиомодулей, рассмотрим программы для периферийных узлов и создадим сайт, на котором будут отображаться полученные системой данные. Для нашей системы сбора данных мы подготовили несколько программ. Для шлюза в интернет для Raspberry Pi программы на Python (версии 2.x и 3.x), а также JavaScript: e32-get-config.py — чтение конфигурации модуля LoRa EBYTE серии E32; e32-set-loranet-nodes-config.py — запись новой конфигурации в модуль LoRa EBYTE серии E32; e32-loranet-get-all.py — опрос периферийных узлов с целью получения от них результатов измерений по радиоканалу;  index.js — сайт для просмотра результатов измерений на базе Node.js и Express Для периферийных узлов 1, 2 и 3 для micro::bit: microbit-lora-net-host1.hex microbit-lora-net-host2.hex microbit-lora-net-host3.hex Все исходные тексты программ для Raspberry Pi, а также загрузочные коды для micro::bit, упомянутые в этой статье, можно загрузить здесь. Радиомодули LoRa EBYTE представляют собой довольно сложные и многофункциональные устройства, которые можно настраивать, записывая в них через UART байты конфигурации. Если вы приобрели новые модули, то прежде, чем их использовать, было бы неплохо определить их текущую конфигурацию, а затем задать новую, подходящую для вашей системы сбора данных.  Как минимум нужно задать рабочую частоту, уровень выходной мощности передатчика и режим работы (прозрачный или фиксированный). В фиксированном режиме работы следует указать номер канала и адрес получателя. Детальное описание параметров конфигурации и режимов работы можно найти в технической спецификации (даташите, data sheet) модуля на сайте. Мы рассмотрим только самые важные параметры, имеющие отношение к нашей системе сбора данных. Для чтения текущей конфигурации радиомодулей E32 мы подготовили программу e32-get-config.py. При запуске эта программа выводит на консоль Raspberry Pi байты конфигурации в том виде, как они были получены, а также расшифровку значений: Как видите, программа вернула шесть байт конфигурации: 0xc0000e1a0fc7, а также расписала значения отдельных параметров, извлеченных из полученных данных.  Расскажем о том, как закодированы параметры. В табл. 1 приведено назначение каждого из шести байт конфигурации. Видно, что некоторые параметры занимают по одному байту (байты 0 и 4), некоторые — сразу два байта (байты 1 и2), а некоторые кодируются отдельными битами (байты 3 и 5). Табл. 1. Байты конфигурации E32 Номер байта  Элемент Описание 0 Заголовок команды 0xC0 — при выключении питания модуля параметры сохраняются; 0xC2  — при выключении питания модуля параметры не сохраняются (удобно для тестирования); 1 Адрес, старший байт Значения от 0x00 до 0xFF 2 Адрес, младший байт Значения от 0x00 до 0xFF 3 Формат данных UART, скорость передачи данных Биты 7, 6 — режим работы порта UART Биты 5, 4, 3 — скорость передачи данных через UART Биты 2, 1, 0 — скорость передачи данных по радиоканалу 4 Номер канала передачи данных Значения от 0x00 до 0x1F 5 Дополнительные параметры конфигурации Бит 7 — прозрачный или фиксированный режим передачи данных Бит 6 — используется ли подтягивающий резистор (1 — используется, 0 – не используется, схема с открытым коллектором) Биты 5, 4, 3 — задержка пробуждения модуля при получении данных по радиоканалу Бит 2 — если равен 1, используется режим упреждающей коррекции ошибок FEC (Forward Error Correction), если 0 — этот режим отключен Биты 1, 0 — выходная мощность радиомодуля Первый байт содержит 0xC0. Это означает, что при включении электропитания модуля конфигурация сохраняется с памяти модуля.  Байты 1 и 2 содержат значение 0x000e. Это адрес модуля. Назначение модулям индивидуальных адресов позволяет выполнять адресную отправку пакетов данных. В данном случае, чтобы модуль получил данные, их надо отправлять, указывая адрес 0x000e (работает только в так называемом фиксированном режиме). По умолчанию адрес модуля установлен в нулевое значение. Обратите внимание на байт 4, который задает номер канала передачи данных для фиксированного режима. Вы можете задавать различные номера каналов для разных подсистем, работающих на одной территории, и при этом данные будут получены только теми модулями, которые настроены на канал отправки данных. Таким образом, в фиксированном режиме можно направлять данные нужным радиомодулям, задавая при передаче их адрес и номер канала. Займемся теперь байтом конфигурации с номером 3 (табл. 2). Табл. 2. Байт конфигурации с номером 3 Бит 7 Бит 6 Режим работы порта UART 0 0 8N1 (установлен по умолчанию) 0 1 8O1 1 0 8E1 1 1 8N1 Здесь 8N1 означает, что в пакете 8 бит данных, нет служебного бита проверки четности, используется один стоп-бит в конце пакета. Режим 8O1 аналогичен предыдущему, но используется контроль по нечетности. В режиме 8E1 то же самое, но применяется контроль по четности. Наша программа вернула режим UART, равный 0, что означает использование 8N1. Биты 5, 4 и 3 байта 3 задают скорость передачи данных в bps (табл. 3). Табл. 3. Скорость передачи данных UART в bps Бит 5 Бит 4 Бит 3 Скорость передачи данных в bps 0 0 0 1200 0 0 1 2400 0 1 0 4800 0 1 1 9600 (установлена по умолчанию) 1 0 0 19200 1 0 1 38400 1 1 0 57600 1 1 1 115200 Программа e32-get-config.py вернула скорость UART как 3, что означает использование скорости передачи данных 9600 bps. Биты 2, 1 и 0 байта 3 задают скорость передачи данных по радиоканалу в bps (табл. 4). Табл. 4. Скорость передачи данных по радиоканалу в bps Бит 5 Бит 4 Бит 3 Скорость передачи данных в bps 0 0 0 300 0 0 1 1200 0 1 0 2400 (установлена по умолчанию) 0 1 1 4800 1 0 0 9600  1 0 1 19200 1 1 0 19200 1 1 1 19200 В нашем случае программа e32-get-config.py показала скорость передачи по радиоканалу, равную 2. Это соответствует 2400 bps. Байт конфигурации с номером 5 содержит множество различных параметров. Как видно из табл. 1, бит 7 определяет режим передачи данных — прозрачный или фиксированный. Программа e32-get-config.py определила, что в модуле используется фиксированный режим. Бит 6 имеет отношение к схемотехнике контроллера. Если он равен 1 (как в нашем случае), то радиомодуль использует свой подтягивающий резистор, а если 0 — то подтягивающего резистора нет. Использование битов 5, 4, 3 байта с номером 5 зависят от так называемого режима работы радиомодуля. Этот режим задается при помощи контактов M0 и M1 радиомодуля. Установкой нужного режима возможно переключение модуля в «спящий» режим, при этом пробуждение произойдет при получении радиосигнала. Биты 5, 4 и 3 задают задержу при пробуждении.  В нашей системе сбора данных мы используем только нормальный режим, когда на контакты M0 и M1 подается нулевое напряжение. При необходимости подробное описание остальных режимов вы найдете в технической документации на радиомодуль. Бит 2 показывает, используется ли упреждающая коррекция ошибок FEC (Forward Error Correction). Если он равен 1, этот режим используется, если 0 — данный режим отключен. Программа e32-get-config.py показала, что в тестируемом модуле алгоритм FEC включен, что способствует устойчивой связи на больших расстояниях. И, наконец, биты 1 и 0 пятого байта конфигурации задают выходную мощность радиомодуля (табл. 5). Табл. 5. Выходная мощность радиомодуля Бит 1 Бит 0 Выходная мощность для E32-915T30D, E32-868T30D и E32-433T30D Выходная мощность для E32-915T20D, E32-868T20D и E32-433T20D 0 0 30 dBm или 1 Вт (установлена по умолчанию) 20 dBm или 100 мВт (установлена по умолчанию) 0 1 27 dBm или 501 мВт 17 dBm или 50 мВт 1 0 24 dBm или 251 мВт 14 dBm или 25 мВт 1 1 21 dBm или 126 мВт 10 dBm или 10 мВт Байт 4 конфигурации задает номер канала (табл. 6). Чтобы определить частоту по номеру канала, используйте формулы, приведенные ниже в таблице. Табл. 6. Номер канала Модуль Номер канала по умолчанию Формула вычисления частоты по номеру канала Channel Рабочий диапазон частот, МГц E32-433T20D, E32-433T30D 23 (0x17) 410 МГц + Channel * 1 МГц 410-441 E32-868T20D, E32-868T30D 6 (0x6) 862 МГц + Channel * 1 МГц 862-893 E32-915T20D,E32-915T30D 15 (0xF) 900 МГц + Channel * 1 МГц 900-931 Например, для модулей E32-868T20D и E32-868T30D можно установить частоту 869 МГц, если задать номер канала, равный 7 (862+7=869). Исходный код программы чтения конфигурации радиомодулей серии E32 представлен в листинге 1.  В самом начале программа импортирует модуль RPi.GPIO, необходимый для работы с контактами разъема GPIO Raspberry Pi. Кроме этого, для работы с радиомодулем через UART потребуется модуль serial, модули time и sys: Далее программа инициализирует порт GPIO при помощи функции gpio_init, получает текущую конфигурацию функцией e32_get_config, выводит данные конфигурации через функцию print_e32_config, а затем сбрасывает порт GPIO: Функция gpio_init показана ниже: Прежде всего, она устанавливает режим использования номеров каналов GPIO.BCM (еще можно указывать номера разъема GPIO в режиме GPIO.BOARD). В переменные M0 и M1 мы записываем значения 22 и 27. Это номера каналов GPIO22 (вывод 15 разъема GPIO) и GPIO27 (вывод 13 разъема GPIO), контакты которых на разъеме GPIO Raspberry Pi подключены к соответствующим контактам EBYTE LoRa. Напомним, что контакты 13 и 15 разъема GPIO мы подключили к контактам M1 и M0 радиомодуля.  Устанавливаем значения соответствующих переменных: Обратите также внимание на использование функции GPIO.setwarnings с параметром False. Она отключает появление предупреждающего сообщения, если программа прервала свою работу, но вы запустили ее снова. С помощью метода GPIO.setup функция переключает каналы M0 и M1 в режим вывода, а затем с помощью метода GPIO.output устанавливает на контактах M0 и M1 уровень напряжения 3,3 В, соответствующий логической единице: Далее функция делает задержку в работе программы на одну секунду с помощью метода time.sleep. На следующем шаге функция gpio_init открывает устройство serial, очищает его буфер и возвращает объект для выполнения операций ввода и вывода: Обратите внимание, что здесь устанавливается скорость обмена 9600 bps. Эта скорость должна соответствовать скорости UART радиомодуля. Функция e32_get_config читает конфигурацию радиомодуля и возвращает ее вызывающей программе: Она записывает в порт UART микрокомпьютера Raspberry Pi три байта 0xC1. По этой команде радиомодуль возвращает шесть байт конфигурации. Наша программа читает их методом ser.read и возвращает в вызывающую программу после небольшой задержки. Полученная конфигурация передается функции print_e32_config в качестве параметра для разбора полученных данных и вывода их на консоль. В зависимости от версии Python полученные байты конфигурации преобразуются в шестнадцатеричную текстовую строку различными способами: Чтобы извлечь значения из битов отдельных байтов конфигурации используются операции сдвига и операция логического И. Например, здесь извлекается номер радиоканала: С помощью программы настройки конфигурации радиомодулей E32 (листинг 2) мы будем задавать адрес, номер канала, частоту и уровень мощности.  В массиве NODE_CFG записаны конфигурации для всех четырех узлов нашей системы сбора данных. Отредактируйте значение частоты и уровня мощности так, чтобы это соответствовало законодательству вашей страны. При запуске передайте программе e32-set-loranet-nodes-config.py номер узла от 1 до 4. Программа извлечет соответствующие байты конфигурации, покажет текущую конфигурацию на консоли функцией get_node_config. Далее после инициализации GPIO функцией gpio_init программа установит новую конфигурацию, вызвав функцию set_config: Новая конфигурация устанавливается функцией set_config при отправке в модуль LoRa через UART соответствующей строки массива NODE_CFG: Далее функция set_config показывает на консоли обновленную конфигурацию. Программа e32-loranet-get-all.py (листинг 3) отправляет команду получения данных по очереди на каждый узел нашей системы, получает данные и сохраняет их в JSON-файле.  Список адресов и каналов находится в массиве NODE_ADDR_CHAN: После инициализации порта GPIO функцией gpio_init программа получает данные с помощью функции get_nodes_data, а затем  записывает их в файл функцией save_nodes_data_to_file: Отправка команды в виде текстовой строки «getData» для каждого узла выполняется функцией send_cmd с помощью функции write: Если данные были отправлены успешно, функция получает результаты измерений от периферийного узла, декодирует их и возвращает: Узлы передают данные в виде строки, причем значения разделены символом точка с запятой. Значения, полученных от всех периферийных узлов, записываются в файл JSON с помощью функции save_nodes_data_to_file. Обратите внимание, что расшифровку названия параметров выполняет функция format_node_data. С ее помощью можно задать различный набор названий параметров для каждого узла. Программа сбора данных запускается без параметров и показывает на консоли сформированные данные JSON: Чтобы программа периодически запускала сбор данных, создайте для ее запуска задание Crontab. Для этого создайте файл crontab_lora.txt: Здесь обновление данных будет выполняться каждые 15 минут, вы можете задать свое значение. Далее запустите задание: Теперь займемся программами для micro::bit для периферийных узлов. Вы можете загрузить коды программ, пригодные для Microsoft MakeCode, с сайта GitHub. Номер в именах файлов задает периферийный узел, для которого эта программа предназначена: microbit-lora-net-host1.hex, microbit-lora-net-host2.hex, microbit-lora-net-host3.hex. Чтобы посмотреть и отредактировать программы, откройте в браузере Chrome сайт Microsoft MakeCode. Далее перетащите мышкой один из файлов, например, microbit-lora-net-host1.hex, прямо в окно браузера. Откроется редактор MakeCode и программа будет в него уже загружена. Программы для узлов 1 и 2 отличаются только текстовой строкой, которую они записывают в OLED-дисплей в начале своей работы. Соответствующий блок при начале показан на рис. 1. Здесь выполняется инициализация и сброс OLED-дисплея, после чего на его экран выводится строка «E32 Loranet Host 1» для первого узла и «E32 Loranet Host 2» для второго узла. Устанавливается размер буферов передачи данных для UART. На светодиодной матрице micro::bit зажигается цифра 1 (2 для второго узла). Для работы с дисплеем используется расширение oled-ssd1306. Если вы будете создавать программу «с нуля», установите это расширение, выбрав слева в палитре редактора MakeCode строку Расширения. Введите в строке поиска название нужного вам расширения. Таже при создании программы установите расширение pxt-lora. Введите адрес в упомянутой выше строки поиска и подтвердите установку. Расширение pxt-lora написано автором этой статьи и поддерживает следующие модули: E30-170T20D, E32-170T30D, E32-433T20D, E32-433T27D, E32-433T30D, E32-868T20D, E32-868T30D, E32-915T20D и E32-915T30D. Когда периферийный узел получает команду getData, он вызывает функцию getWeatherData и отправляет полученные от нее результаты измерений на адрес 14 (0xe) по 15 каналу, т.е. на узел шлюза в интернет. Соответствующий блок показан на рис. 2. На OLED-дисплей выводится принятая команда. После успешной отправки на светодиодной матице micro::bit появляется ромбик (на короткое время). Что касается функции getWeatherData, то ее задача заключается в получении данных от погодной станции BME-280. Эти данные преобразуются в текстовые строки и соединяются через символ точки с запятой. Если вы будете создавать программу для третьего узла заново, то установите расширение для измерителя освещенности с названием BH1750. Также, конечно, нужно установить расширение pxt-lora. Третий узел не оснащен дисплеем OLED, поэтому при начале работы программа инициализирует только радиомодуль, устанавливает размеры буферов UART, а также адрес 35 (0x23) на шине для измерителя освещенности BH1750 FVI GY-30 (рис. 4). На светодиодной матрице micro::bit зажигается цифра 3 — номер периферийного узла. Когда третий узел получает команду «getData», он вызывает одноименную функцию и на короткое время выводит ромбик вместо цифры 3 на светодиодную матрицу micro::bit. Данные, полученные от этой функции, отправляются в центральный шлюз по адресу 14 на канале 15 (рис. 5). Функция getData (рис. 6) получает значение температуры процессора и освещенности в люксах, а затем преобразует в данные текстовую строку с разделителем в виде точки с запятой. Итак, наша система сбора данных отправляет по очереди команду getData на все периферийные узлы, а полученные значения сохраняет в виде JSON файла. Теперь на базе Node.js и Express сделаем простейший сайт, чтобы можно было наблюдать за результатами измерений через браузер.  Чтобы установить Node.js в Raspberry Pi OS Lite, вы можете воспользоваться процедурой для Debian, описанной здесь. Установку нужно выполнять с правами пользователя root следующим образом: После установки проверьте версии Node.js и npm: Выполните инициализацию проекта: При инициализации вам будет задан ряд вопросов. Вы можете выбрать здесь ответы по умолчанию или указать свои данные, например, при запросе описания приложения description, точки входа entry point и автора author. После подтверждения введенных данных в каталоге приложения будет создан файл package.json. Далее нужно установить Express в каталог приложения и сохранить его в списке зависимостей файла package.json: Создайте файл index.js (листинг 4) и разместите его в каталоге /home/pi/loranet/lora_site/.  Здесь предполагается, что сайт будет работать в локальной сети по адресу 192.168.0.19 и на порту 8000. Вам, возможно, нужно будет указать другой адрес, свободный в вашей сети. Запустите Web-сервер такой командой: Если теперь открыть сайт по адресу http://192.168.0.19:8000, то в окне браузера появятся данные, полученные от периферийных узлов (рис. 7). Для доступа к этому сайту из интернета настройте на своем роутере проброс порта 8000. Способ настройки зависит от модели вашего роутера, в интернете можно найти множество инструкций на эту тему. Чтобы снизить вероятность взлома узла шлюза, рекомендуем сменить пароль пользователя pi и установить fail2ban: Вы также можете настроить брандмауэр, чтобы ограничить доступ извне. Еще будет нелишним сделать так, чтобы при использовании команды sudo нужно было вводить пароль. Для этого отредактируйте файл: Измените следующую строку, заменив в ней NOPASSWD на PASSWD: Должно получиться так: Если вам удалось собрать и запустить систему сбора данных, описанную в этой статье, можете усовершенствовать или дополнить ее. Например, помимо погодной станции подключите к периферийным узлам сенсоры движения, контакты, датчики газа и воды и тому подобные устройства. Можно дистанционно через интернет включать, например, освещение или кондиционер, или реализовывать другие элементы умного дома. Вы даже можете попробовать сделать на ее основе охранное устройство, хотя для серьезной охраны лучше все же установить промышленную систему. Автор: Александр Фролов. НЛО прилетело и оставило здесь промокод для читателей нашего блога:— 15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.   Маркетолог