Raspberry Pi снискали огромную популярность по всему миру. Однако многие из любителей-разработчиков сталкиваются с проблемой доступа к устройству из интернета. Как правило приходится колдовать с DDNS или докупать статический IP адрес. Оба варианта предполагают настройка роутера, что не для всех и не всегда доступно. Кроме того – откртытие прямого доступа к устройству из интрента несет в себе определнные риски безопасности.

Крайне распространенным способом решения этой проблемы является использование промежуточного сервера, через который сервер и клиент подключаются друг к другую.
Схема крайне широко используется в различных пакетах удаленного доступа: VNCViewer, AmmyyAdmin и т.д.

Вариант 1-DDNS

image

Вариант 2 – статический IP адрес

image

Вариант 3 – использование промежуточного сервера

image

Эту схему подключения можно реализовать через протокол MQTT over Websockets, но мне было инетесно реализовать что-то значительно более простое, JSON-ориентированое, расширяемое, без ограничений протокола на размер сообщения и с более очевидной моделью безопасности.

Выбор протокола был очевиден. WebSocket уже давно поддерживается браузерами и большинством платформ и языков программирования.

Модель шины данных тривиальна:

WebSocket cервер поддерживает неограниченное количество каналов. Каждый канал может быть как открытым, так и защищенным паролем. Канал существует только пока у нему подключен хотя-бы один клиент. Никакие данные на сервере не принципиально не хранятся. Пароль задается первым клиентом подключившемся к каналу. Клиенты без пароля/с неправильным паролем в канал допущены не будут.

Канал является контейнером для сообщений. Сообщения бывают трех типов: адресные сообщения (команды), которые предназначены для одного или нескольких получателей, ответные сообщения и широковещательные сообщения которые будут получены всеми клиентами подключенными к каналу.

image

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

Сообщения нигде не сохраняются, не логируются и передаются по защищенному протоколу wss.
Сообщения – это типизированные JSON объекты. Бинарная информация передается как Base64 encoded string.

Код проекта доступен на GitHub, а действующий хаб вы можете найти здесь.
Проект состоит из WebSocket сервера на .net, web-приложения-клиента, которая подключается к серверу и python приложения для raspberry pi.

Веб-приложение — далеко не единственно возможный способ взаимодействия с websocket-сервером. Например, можно легко написать облачный клиент, который будет «слушать» события веб камеры и сохранять картинки в какое-нибудь облачное хранилище.

Архитектура позволяет подключать неограниченное число raspberry pi и клиентов на один канал.
Например если вы захотите построить некую систему безопасности/управления климатом/умным домом – вы можете все raspberry «посадить» на один канал и отправлять команды сразу всем или нектором инстансам.

Широковещательные события «broadcast» получат все участники сети.

Несколько слов о возможностях приложения


  1. Удаленный доступ к файловой системе. Можно смотреть, скачивать и загружать картинки, звуковые файлы и прочие файлы. Довольно удобно, чтобы не таскать весь свой многотерабатный архив фоток на телефоне. Ну или если нужно что-то поискать в архивах.
  2. PiCamera позволяет получать изображение как по запросу так и в режиме веб камеры. Наряду с получением звука с микрофона может использоваться как дешевое решение для безопасности. Требует подключенной pi-камеры.
  3. Отправка звукозаписей на raspberry или получение записи с микрофона. Можно использовать как для каких-то задач безопасности, так и для голосового взаимодействия с котами или детьми. Требует подключения к телевизору/колонке и микрофону соответсвенно.
  4. Включение/выключение/получение фокуса телевизора. CEC протокол позволяет управлять телевизором через HDMI кабель. Необходимо установить cec-client на raspberry.
  5. Отображение звуковой и текстовой информации на телевизоре. Эта функция включает включение и получение фокуса в телевизоре и отображение текстового сообщения, сопровождаемое звуковым сигналом. Удобно, если надо удаленно заставить детей делать уроки.
  6. Удаленное отображение картинки. Если вы хотитет сделать сюрприз своим родым и внезапно вывести на семейный телевизор фотографию ваших приключений – то эта функция как раз для этого. Выбираете любую картинку ПК или телефона и отправляете на raspberry.
  7. Получение данных о влажности, температур. Raspberry рассылает Broadcast сообщение с заданной перодичнсотью. Помимо влажности и температуры сообщение содержит информацию о температуре и загрузук процессора, загрузке диска и памяти. Совместно удаленным управлением портами ввода-вывода можно использовать в качестве дистанционного управления климатом в помещении. Для измерения температуры необходим датчик DHT11 ($1).
  8. Управление портами ввода-вывода. Позволяет как считать информацию с выбранных GPIO портов, так и вывести на них сигнал. Можно получать статус переключаетелей, зажигать лампочки, звонить в звоночки, а через реле управлять какими-то более серьезными приборами.
  9. Удаленный запуск комманд позволяет выолнять на raspberry любые shell команды. Например запустить просмотр фоотоальбома или фильма. Если такая возможность кажется вам небезопасной – эту функцию можно отключить.


image
To be continued…

Комментарии (8)


  1. AlexandreFrolov
    10.04.2019 09:27

    Решил подобную задачу с помощью OpenVPN. В датацентре расположены серверы OpenVPN (шлюзы), на Raspberry Pi устанавливается клиент OpenVPN.

    Приложение обращается к Raspberri Pi через шлюзы, проксирующие запросы с помощью nginx по сети OpenVPN. При этом для Raspberry Pi выделяются статические IP из сети OpenVPN. На роутере можно использовать NAT, внешние динамические IP. Никакая дополнительная настройка роутеров не требуется.

    Работает достаточно надежно даже при использовании беспроводной 4G. При включении и временных перебоях в связи канал автоматически восстанавливается.


  1. jaiprakash
    10.04.2019 10:25

    В варианте с DDNS нужен "белый" IP, как показала практика, а он есть далеко не всегда.


    1. kt97679
      10.04.2019 22:44

      Без белого IP может помочь github.com/gjedeer/tuntox


      1. jaiprakash
        10.04.2019 23:00

        Глубоко не разбирался, но выглядит как туннель, т. е. вариант №3.


        1. kt97679
          11.04.2019 00:25

          Да, только не нужно организовывать свой выделенный сервер.


  1. dsoastro
    10.04.2019 20:23

    Я использую autossh на rpi (устанавливает ssh соединение до сервера и поддерживает его). Что-то типа autossh -M <port на rpi> -o «PubkeyAuthentication=yes» -o «ServerAliveInterval 60» -o «ServerAliveCountMax 3» -R localhost:<port_на_сервере>:localhost:22 <login на сервере>@адрес_сервера -i <путь_до_ключа>. Эту команду ставим в автозагрузку (/etc/rc.local), либо пишем unit для systemd.

    На телефоне ставим termux, с него ssh на сервер, оттуда ssh -p <port_на_сервере> pi@localhost.

    Либо можно поставить nginx на сервере, прокси-пассить его на shellinabox на сервере же, и тогда получаем терминал сервера через браузер. Из терминала по ssh заходим на rpi


  1. keylase
    11.04.2019 08:33

    Без всяких .net-ов github.com/zhaojh329/rttys


    1. keylase
      11.04.2019 08:35

      ну и собственно клиент github.com/zhaojh329/rtty