Skip to content

Instant messaging over a long distance using LoRa & singlechannel LoRa gateway

License

Notifications You must be signed in to change notification settings

comradeFreeman/loramobgate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

56 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LoRaMobGate Project

Проект предназначен для обеспечения коммуникации пользователей на большом расстоянии путём подключения с помощью USB к смартфону/ноутбуку/компьютеру ("Хост") специального Устройства, оснащённого радиомодулем LoRa. В совокупности с Хостом оно одновременно является одноканальным LoRa-шлюзом для конечных устройств Интернета Вещей (IoT).

В описании проекта главным звеном является Устройство, а Хост рассматривается как обычная вычислительная мощность к нему в придачу, поэтому далее в повествовании под Устройством будет подразумеваться связка этих двух компонентов.

Device

Архитектура системы включает в себя:

  • Устройство (Device). Состоит из микроконтроллера (МК), радиомодуля и обвязки для V-USB.
  • Хост (Host). Любая ЭВМ, оснащённая USB-портом в режиме host-mode. Практически любой современный смартфон удовлетворяет этому требованию.
  • Сеть (Network). Образуется Устройствами при помощи ПО на Хосте. Данное ПО включает в себя облегчённый стек сетевых протолов, который отчасти копирует TCP/IP и приспособлен для потребностей проекта.
  • API-сервер. Выполняет вспомогательную функции: маршрутизация пакетов Сети через сеть Интернет, когда она доступна, связывание сегментов Сети, находящихся на расстояних, на которых связь через радиоканал невозможна. Architecture_ru

Возможности:

  • Децентрализованная коммуникационная сеть большого радиуса действия.
  • Собственный облегчённый стек протоколов.
  • Приложение для всех популярных операционных систем (на текущий момент есть сценарий сборки исполняемого файла только под ОС Android, под Windows - в процессе), представляющее собой мессенджер. Помимо явных функций мессенджера, имеет ПО для обеспечения работы LoRa-шлюза.
  • Дешёвое в плане элементной базы устройство на китайском модуле AI-Thinker Ra-01SC/Ra-02 и МК Atmega168PA.

Устройство (Device)

Устройство построено на базе МК Atmega168PA и радиомодуля Ra-01SC/Ra-02. Для связи с Хостом используется реализация V-USB с тактовой частотой 16 МГц. Для взаимодействия с модулем задействован интерефейс SPI и библиотека LoRaRF. Для согласования уровней (радиомодуль работает от 3.3В) вся схема питается от 3.55-3.7В, получаемых от USB-порта через два последовательно включённых светодиода. Несмотря на то, что согласно документации для работы МК на частотах от 10-20МГц требуется напряжение питания 4.5-5.5В, экспериментально установлено, что устройство функционирует корректно и от такого пониженного напряжения. Прерывание с наивысшим приоритетом INT0 используется для работы с V-USB, а INT1 - для обратного вызова с модуля при приёме сообщения из эфира. Schematic PCB_3D

Устройство имеет перечень реализованных команд, всего 63 команды: 51 к радиомодулю, 12 - к МК. Забегая наперёд уточню, что для команды кодируются одним байтом и команды к радиомодулю занимают диапазон 100-199, а команды к МК - диапазон 200-255.

В EEPROM память МК записываются следующие данные:

Название Предназначение
devAddr Уникальный сетевой адрес Устройства
loraSyncWord Фраза синхронизации для взаимодействия на L1 LoRa
country Код страны по ISO3166-1. Введён на случай наличия ограничений по выходной мощности передатчика в конкретной стране (в коде пока не имплементировано).
privateKey Закрытый ключ Устройства, применяется для шифрования в мессенджере (более подробно - дальше).

Обмен с Хостом осуществляется с помощью определённого формата пакета, приведённого ниже. а) Host -> Device

1 byte 1 byte 1 byte 2 bytes ARG1LEN bytes ... ARGnLEN bytes PYDC bytes
OPCODE ARGC PYDST PYDC ARG1 ... ARGn PAYLOAD
Код операции Кол-во аргументов Индекс начала полезной нагрузки Длина полезной нагрузки Аргумент 1 ... Аргумент n Полезная нагрузка
  • Начало PAYLOAD надо размещать на байтах, кратных 8, для упрощения чтения через V-USB

б) Device -> Host

1 byte 2 bytes PYDC bytes
OPCODE PYDC PAYLOAD

Сеть и ПО Хоста

Обмен между Устройствами осуществляется с помощью разработанного облегчённого стека протоколов, функционально частично повторяющего стек TCP/IP. net_packet_ru Работа Сети и взаимодействие с Устройством реализованы программно в коде и разбиты на классы, каждый из которых выполняет свою определённую роль, схематически их связь можно представить следующим образом:

classes_diagram

  • UsbConnection. Предназначен для поиска устройства на шине, установки и поддержания соединения с ним и обмена данными в обе стороны.

  • Keys. Этот класс отвечает за всё, что связано с ключами, шифрованием, дешифрованием и т.д. Как уже упоминалось ранее, шифрование используется только в мессенджере, для этого используется криптография на эллиптических кривых для генерации пар «закрытый ключ-публичный ключ», протокол ECDH для согласования ключей и генерации ключа чата и алгоритм Salsa20 для собственно шифрования данных. Класс отвечает за хранение закрытого ключа устройства, публичных ключей других устройств, генерацию ключа чата.

  • Network. Отвечает за события сети (как ни странно), обмен ключами и построение дерева соседей. К событиям сети относятся следующие события:

    • Информация о соседях (L3NEIGHBORINFO). Раз в некоторый промежуток времени Устройство рассылает широковещательный пакет, в который помещает свою таблицу соседей. Все, кто примут этот пакет, обновляют своё дерево соседей. Таким образом на каждом Устройстве через некоторое время формируется дерево соседей на некоторое количество прыжков. В коде дерево реализовано с использованием модуля anytree. (на текущений момент на начальной стадии)
    • Запрос публичного ключа (L3KEYRQ). Выше (в классе Keys) было сказано, что для генерации ключа чата используется ECDH, который требует, чтобы у каждого из двух абонентов был публичных ключ другой стороны. А что делать, если у кого-то его нет? Тогда класс Keys при попытке шифрования и не нахождении нужного публичного ключа автоматически формирует широковещательный сетевой пакет с запросом о публичном ключе, связанном с Устройством назначения, которое впоследствии отправляется в эфир Устройством. Первое же Устройство, которое найдёт в своей базе ключей нужное, присылает его к отправителю (тоже широковещательный пакет) в пакете типа L3KEYEX. Таким образом другие Устройства в сети тоже узнают этот ключ и аналогичных запросов станет меньше.
    • Обмен публичными ключами (L3KEYEX). При приёме пакета с одноимённым типом запроса, все Устройства обновляют свою базы ключей, добавляя новую пару «устройство - публичный ключ», и пересылают пакет дальше.
  • InternetConnection. Отвечает за соединение с сетью Интернет и через него проходят запросы в API на сервере, также не будем заострять на нем внимание.

  • UsbPacket и NetPacket реализуют в коде форматы, упоминавшиеся выше. Среди полей последнего следуют выделить AppID и ContentType, являющиеся перечислениями. Можно принять, что последний является подтипом в пределах первого типа данных.

    Перечисление AppID Значение
    UNKNOWN 0
    MESSENGER 1
    NETWORK 2
    LORA 3
    INVALID 15
    Перечисление ContentType Значение
    UNKNOWN 0
    TEXT 10
    VOICE 11
    IMAGE 12
    AUDIO 13
    MESSAGE_STATUS 14
    MESSAGE_OPTIONS 15
    EMERGENCY 19
    L3NEIGHBORINFO 20
    L3KEYEX 21
    L3KEYRQ 22
    L3CNFRP 23
    LORA 30
    INVALID 255
  • Device. Класс является агрегирующим классом, объединяющим все вышеупомянутые классы и содержащим в себе почти всю логику работы устройства - от инициализации по USB до очереди расшифрованных сообщений мессенджера, плюс методы для ещё большего уровня абстракции.

API-сервер

Для разгрузки радиоканала, когда у устройств есть доступ к Интернету, и возможности объединять отдельные сегменты Сети на языке Python был написан API-сервер, для этого была использована библиотека fastapi. Большинство конечных точек требуют авторизации по токену. API-сервер не хранит принятых данных, а только их ретранслирует, и поэтому для их хранения используется БД Redis и ORM библиотека redis-om. Почти все конечные точки требуют авторизации по токену для работы, который можно получить, обратившись к конечной точке /token, отправив на неё POST-запросом форму, "пользователь - пароль", где "пользователь" - сетевой адрес устройства, а "пароль" - закрытый ключ, считываемый из EEPROM памяти Устройства. Эти данные будут сверены API-сервером со своей БД, которая содержит полный список сетевых адресов и хешей закрытых ключей всех когда-либо выпущенных Устройств.

Тип запроса Конечная точка Назначение
POST /token Получение токена
GET /users/me Информация о текущем Устройстве
GET /device/packets Получить входящие пакеты
POST /device/packets Отправить пакеты на сервер
GET /device/packets/count Количество входящих пакетов
POST /network/neighbors Отправить информацию о соседях на сервер
GET /network/stats Общая статистика Сети

Внимание! Для того, чтобы работал поиск пакетов в redis-om, требуется не обычный сервер Redis, Redis-Stack-Server!

Также API-сервер может принимать от устройств информацию о соседях и строить дерево связей (на текущий момент не реализовано!), чтобы осуществлять так называемую «умную» маршрутизацию пакетов - то есть передавать пакет через посредников и желательно через сеть Интернет, пример показан ниже. Пусть А хочет прислать пакет Устройства В, но оно далеко. Тогда он поочерёдно посылает его в эфир и на API-сервер. API-сервер знает топологию сегмента Сети и при следующем опросе Устройством Б конечной точки /device/packets/ в ответ добавит также и пакет от Устройства А до В, который будет ретранслирован через радиоканал. smart_routing_ru

LoRa-шлюз

Шлюз будет реализован программно. Суть его работы будет заключаться в следующем. Если Устройство получает некий пакет из эфира и в ходе проверки оказывается, что это пакет от конечного устройства LoRa (AppID.LoRa), то такой пакет отправляется API-серверу, если к нему есть доступ, иначе отравляется другим Устройствам по радиоканалу. API-сервер обращается к API TheThingsNetwork и пересылает пакет ему. Каждое устройство при первом обращении к API-серверу запускает на последнем процедуру регистрации Устройства как Шлюза.

Приложение-мессенджер

Для использования ранее разработанного ПО хоста, реализации возможности взаимодействия устройств между собой, коммуникации их владельцев и работы LoRaWAN-шлюза была выбрана концепция применения-мессенджера, которое будет предоставлять соответствующий пользовательский интерфейс, похожий на обычный мессенджер вроде Telegram, Viber, WhatsApp. Технически приложение использует фреймворк Kivy и коллекцию виджетов в стиле Material Design v2 KivyMD, а для сборки в APK (Android) и IPA (iOS, пока не реализован) файлы используется проект Buildozer. В дальнейшем проект будет дополнен сценариями сборки под ОС Windows, Linux и macOS. Разработанное приложение имеет следующие экраны:

  • Перечень чатов. Этот экран показывается пользователю при старте применения. Содержит список чатов пользователя. На верхней панели размещена кнопка вызова бокового меню (1), индикация доступа к сети Интернет (2), индикация связи с устройством (4) и поиск чатов (5) по фрагменту названия чата или содержимому последнего сообщения.

disp_1

Единица чата состоит из рисунка чата (6), названия чата (7), текста последнего сообщения (8) и его даты (9). Боковое меню содержит: рисунок текущего пользователя (1), его ник (2) и сетевой адрес устройства (3). Кроме этого есть три кнопки перехода на другие экраны: редактирование текущего пользователя (4), создание нового чата (5) и настроек применения (6).

disp_1x

  • Чат. Этот экран предназначен для отображения сообщений выбранного на предыдущем экране чата. Верхняя панель содержит: кнопку возврата (1) на главный экран перечня чатов, название чата (2), кнопку перемотки (3) до последнего сообщения, кнопку вызова подменю чата (4). Элемент сообщения состоит из «облачки» (5), текста сообщения (6) и времени (7). Кроме этого сообщения разных дней разделены плашкой даты (9) и сообщение может содержать информацию о том, на какое сообщение ссылается текущее (8) или от кого переслано сообщение (на рисунок не показано). Также по нажатию на сообщение над ним открывается подменю списка действий с сообщением, действия по умолчанию: ответить, переслать, скопировать и удалить (с диалогом подтверждения).

disp_2

Подменю списка действий с чатом содержит пункты: поиск, поиск по дате, изменение названия чата и удаление чата (с диалоговым окном подтверждения). При смене названия чата автоматически генерируется новая картинка чата, состоящая из монохромного фона и цветной первой буквы названия чата.

  • Создание чата. На этом экране пользователю предлагается ввести адрес устройства собеседника и по желанию название чата.

disp_3

  • Настройки приложения. Этот экран содержит настройки графического интерфейса и работы ПО хоста.
  • Редактирование текущего пользователя. На этом экране можно изменить ник и рисунок текущего пользователя.

Использование

На текущий момент при сборке прошивки Устройства с помощью Arduino-Makefile по непонятной причине на полученной прошивке не будет работать приём. Рекомендуется собирать код при помощи Arduino IDE. Также на данный момент в Makefile не работает прошивка Устройства через USBASP.

ПО Устройства можно собрать при помощи Arduino-Makefile, выполнив в директории исходного кода команду make.

Для этого у Вас должны быть установлены Arduino IDE и avrdude

ПО Хоста можно использовать отдельно от приложения, оно является лишь одним из возможных фронтендов.

git clone https://github.com/comradeFreeman/loramobgate.git
cd loramobgate/messenger
python3 -m venv venv
source venv/bin/activate
pip install Cython wheel
pip install -r requirements.txt

Запуск только ПО Хоста:

cd device
python

Python 3.10.6 (main, Mar 10 2023, 10:55:28) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> from loramobgate import Device
>>> from queue import Queue
>>> messenger_queue = Queue()
>>> device = Device(messenger_queue = messenger_queue)
>>> device.dev_info
{'dev_addr': 3722304989, 'lora_sync_word': 52, 'country': 804, 'chip': <Chip.SX126X: 6>}

Запуск мессенджера:

python main.py

ВНИМАНИЕ! Для того, чтобы всё заработало на Windows, нужно вручную установить драйвер libusb! Для этого требуется запустить messenger/zadig-2.4.exe, выбрать из выпадающего списка loramobgate, чуть ниже в правом списке выбрать libusb-win32 и нажать кнопку Install Driver / Reinstall Driver.