Есть такой интересный протокол передачи данных - APRS. Про него в интернете уже много рассказывалось, здесь не будет углубленного теоретического материала. В этой статье будет описано как создать свой собственный «карманный» модулятор AFSK. В последующих статьях будут инструкции по выходу в эфир и по созданию простого демодулятора, который позволит принять пакеты APRS и отобразить информацию на дисплее прямо на улице. Всё будет реализовано для Flipper Zero. Если у Вас еще нет этого гаджета, то не расстраивайтесь и попробуйте всё на великой и ужасной Arduino. Передавать информацию на расстояние «своими руками» очень интересно.

Мы не станем строить «велосипед», только наставим «костылей»

В интернете есть вообще всё. И реализация AFSK модуляторов тоже существует. Только под FZ (Flipper Zero) пока еще нет. Этот гаджет хорош по сравнению с моей любимой Arduino Nano, например, тем, что представляет собой законченное устройство в корпусе с дисплеем и кнопками. Поэтому будут взяты исходные коды с одной из открытых лицензий и доработаны с учетом особенностей FZ. Здесь хочется обратиться к более опытным читателям, которые хорошо разбираются в FreeRTOS, ведь именно она «крутится» на FZ. Оригинальные исходные коды предназначаются для Arduino, где нет многозадачности, а при разработке под FZ я столкнулся с выходом из важных функций формирования сигнала в прерывания. Решение было найдено, но, уверен, что можно реализовать изящнее. Пожалуйста, расскажите в комментариях как сделать правильно.

«Если кто-то захочет разработать приложение, я в деле» [Глути. // Рик и Морти. - 4 сезон. - 2 серия.]

Помимо реализации модулятора AFSK и протокола AX.25 потребуется разобраться с созданием приложений под Flipper Zero. Все про что здесь написано создано для версии 0.83.1 https://github.com/flipperdevices/flipperzero-firmware/releases/tag/0.83.1.

рис. 1. Скриншот версии прошивки девайса из эксперимента
рис. 1. Скриншот версии прошивки девайса из эксперимента

Здесь тоже подробно останавливаться не станем, потому что всё уже неплохо описано в других статьях. Приведу примеры и дам ссылки, которыми сам воспользовался. (Большое спасибо авторам этих статей за их проделанную для нас работу!):

  1. https://amperka.ru/blogs/projects/flipper-zero-app-programming-tutorial

  2. https://yakovlev.me/hello-flipper-zero/

В статье по второй ссылке есть хороший пример реализации срабатывания по таймеру. Сначала я использовал furi_delay_ms(tx_delay);, но находясь на паузе между передачами выйти из приложения не получится. Это не очень удобно, особенно когда паузы по три минуты. Поэтому таймеры просто необходимы. Таким образом шаблон с выделением/освобождением памяти из первой статьи был улучшен использованием таймера из второй статьи. (Как мне показалось, вторая статья будет попроще для понимания, особенно читателям, которые пока что не выходили за пределы Arduino).

Хороший шаблон с выделением/освобождением памяти для будущих приложений

https://github.com/NSV47/good_template

Просто скачайте, разархивируйте и положите каталог /good_template в каталог flipperzero-firmware/applications_user. Далее команды как в статьях ./fbt fap_good_template и ./fbt fap_deploy и приложение уже на девайсе.

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

Собрали, перенесли на девайс. Вот демонстрация:

О переносе проекта с одной отладочной платы на другой девайс

Когда я узнал про APRS и захотел повторить успех, то начал шарить по интернету и наткнулся на проект, который мне показался очень интересным. Вот этот проект: https://github.com/handiko/Arduino-APRS. Давайте возьмем наш хороший шаблон, перенесем в него функции из репозитория пользователя Github с ником Handiko и будем ему бесконечно благодарны за его большую проделанную работу. Лучше пойдем с тестовых прошивок, чтобы я смог на деле указать на особенность FreeRTOS.

Single tone test 1200 hz

В бесконечном цикле вызывается set_nada(_1200);, которая вызывает set_nada_1200(void);. Во второй функции выключается и выключается один из выходов. Для FZ использование GPIO описано в первой статье и тогда наша функция set_nada(_1200); приобретает вид:

void set_nada_1200(SingleToneTest1200HzApp* app)
{
    app->output_value = true;
    furi_hal_gpio_write(app->output_pin, app->output_value);
    furi_delay_us(tc1200);
    app->output_value = false;
    furi_hal_gpio_write(app->output_pin, app->output_value);
    furi_delay_us(tc1200);
}

По сути тоже самое что у Handiko, но под реалии FZ. Жаль только что это работает не так как нам хотелось бы:

К выходу подключен самый простой зуммер, мы слышим его щелчки на фоне. Вместо звука 1200 Гц, частота около 1 Гц. Это как раз те особенности использования FreeRTOS. Получается бесконечный цикл в приложении FZ немного не такой, как в Arduino. Перепишем функцию void set_nada_1200(SingleToneTest1200HzApp* app). И вызывать ее следует не в бесконечном цикле, а до него:

void set_nada_1200(SingleToneTest1200HzApp* app)
{
    uint32_t value = 5000;
    while(value--){
        app->output_value = true;
        furi_hal_gpio_write(app->output_pin, app->output_value);
        furi_delay_us(tc1200);
        app->output_value = false;
        furi_hal_gpio_write(app->output_pin, app->output_value);
        furi_delay_us(tc1200);
    }
}

Результат:

Снова проблема, на этот раз не такая очевидная, и на ее решение у меня ушло пару вечеров после работы. Слышны какие-то искажения сигнала, их быть не должно. На осциллограмме эта аномалия видна очень хорошо:

рис. 2. Осциллограмма аномалии в сигнале
рис. 2. Осциллограмма аномалии в сигнале

Если оставить как есть и продолжить переносить код, то визуально всё будет выглядеть, так как надо. Интерпретировать осциллограмму модулированного сообщения очень сложно, ну зуммер это вообще игрушка. Разобраться с этой проблемой необходимо именно на этом этапе. Это теперь как раз те особенности FreeRTOS, про которые упомянуто в самом начале, и которые надо решить более изящно. При поиске решения мне попалась вот эта статья https://habr.com/ru/articles/129445/. Спасибо доброму человеку, который ее написал. Необходимо использовать критические секции, причем приостановка работы планировщика никак не помогла, помогло именно использование критической секции, построенной на макросах. Ну в статье и написано, что приостановка планировщика защищает только от других тасков, а мы сваливаемся в прерывание. Теперь функция принимает вид:

void set_nada_1200(SingleToneTest1200HzDraftApp* app)
{
    uint32_t value = 5000;
    taskENTER_CRITICAL();
      while(value--){
          app->output_value = true;
          furi_hal_gpio_write(app->output_pin, app->output_value);
          furi_delay_us(tc1200);
          app->output_value = false;
          furi_hal_gpio_write(app->output_pin, app->output_value);
          furi_delay_us(tc1200);
      }
    taskEXIT_CRITICAL();
}

Теперь и на слух всё правильно и на осциллограмме порядок. Только одно но. Пока будет выполняться код в критической секции ничего другого сделать не получится, например, выйти из приложения, нажав кнопку назад. В нашем случае, конечно, пакеты не такие-то уж и большие, можно подождать, но душа инженера требует идеала, а семья внимания, поэтому вопрос к более опытным читателям: «Как избавиться от этого «костыля»?»

Ссылка на исходники Single tone test 1200 hz на всякий случай (обратите внимание в этом коде еще не реализовано обращение к таймеру, но он тут и не нужен):

https://github.com/NSV47/Single_Tone_Test_1200_Hz_draft

У Handiko в репозитории Arduino_APRS есть еще ряд тестовых прошивок. Но разбирать я из не буду, потому что не столкнулся с какими-то серьезными проблемами типа необходимости использования критических секций. Прокомментирую только пару моментов.

Еще до того как я стал использовать критические секции, я не уделил должного внимания аномалии на осциллограмме (ну или на слух с зуммера) и реализовал сразу тест APRS_Random_String. К моему удивлению этот сигнал достаточно неплохо декодировался компьютером (как это можно сделать я опишу дальше). Это меня сбило с толку дальше, когда я реализовал APRS_Hello_World, который декодировался примерно в одном случае из 20. При этом я грешил на переменную «baud_adj», потому что думал что у меня что-то не так с длительностью импульса.

Потом я нашел решение и использовал критические секции, но к моему удивлению декодировать не получалось. Я думал, что из-за того не умею правильно использовать критические секции, при переходе из функции в функцию происходит прерывание. Пытался сравнить оригинальные осциллограммы снятые с Arduino с сигналом с FZ. А один вечер потратил на то, чтобы написать все в одной функции (выглядит страшно, там больше 3000 строк, но мне очень сильно хотелось чтобы это работало).

рис. 3. Пример ненормального программирования
рис. 3. Пример ненормального программирования

А ошибка была просто в моей невнимательности. В исходнике APRS_Random_String в реализации void send_string_len(const char *in_string, int len) в функцию send_char_NRZI(in_string[j + len], HIGH) передается индекс j+len, а вот в исходнике APRS_Hello_world в реализации той же самой функции строчка передается с индексом j. Поэтому сдирая чужой код будьте внимательны:)

APRS_Hello_world

На самом деле остается только наполнить хороший шаблон функциями из одноименного исходника. Разница лишь в обращении к GPIO, что накладывает на нас необходимость передавать в большинство функций структуру нашего приложения в виде параметра. Я думаю, что если переписать хороший шаблон по образу и подобию примера из второй статьи, то код упростится, а я просто покажу исходники полностью:

https://github.com/NSV47/APRS_hello_world_clean

Декодер AFSK

Для проверки работоспособности приложения необходимо декодировать сообщение. Для этого можно использовать AFSK1200 Decoder. Чтобы завести сигнал на звуковую карту ноутбука я воспользовался вот таким разъемом:

рис. 4. Распиновка Jack 3.5 для соединения Flipper Zero с компьютером
рис. 4. Распиновка Jack 3.5 для соединения Flipper Zero с компьютером

Необходимо будет выбрать в окошке INPUT источник сигнала и нажать Start Decoder:

рис. 5. Пример декодированных сообщений
рис. 5. Пример декодированных сообщений

Есть и другие программы, которыми можно декодировать такие сигналы, например, UISS (инструкция https://r4uab.ru/aprs-iss/). С помощью этой программы у меня получалось даже лучше принимать реальные сигналы APRS. Или GNU Radio (инструкция https://github.com/handiko/gr-APRS). Я пользовался в UBUNTU GNU RADIO и этой инструкцией.

Итоги

Теперь у нас получилось создать модулятор AFSK своими руками и разобраться с тем как устроены приложения Flipper Zero. Мы даже соединили Flipper Zero с компьютером. Но чтобы по-настоящему раскрыть протокол передачи данных APRS нам потребуется передатчик. У меня, например, пока что нет лицензии радиолюбителя и радиопозывного, поэтому в одной из следующих статей мы выйдем в эфир в PMR диапазоне (446-446.200 МГц), где лицензия не требуется. Мощность работы передатчика также составит разрешенные 500 мВт. И тогда посмотрим насколько далеко получится передавать сообщения.

Несмотря на некоторый успех, я все-таки прошу более опытных читателей дать ответы на возникшие вопросы:

  1. Как правильно защититься от особенностей работы FreeRTOS, чтобы важный код не мешался с прерываниями?

  2. Можно ли использовать частоты APRS (144.800 МГц) для, например, общения с другими радиолюбителями (конечно же имея все необходимые разрешения, лицензии, позывные и т. п.) наподобие чата в WhatsApp (ну это например)?

  3. Как, всё-таки, сейчас получить лицензию радиолюбителя и позывной? Может быть, кто-то недавно получал? (Потому что я вроде вычитал что сейчас даже и ходить никуда не надо, все можно сделать прямо из дома удаленно).

  4. Что будет если выходить в эфир без лицензии и позывного, например с помощью той же самой Baofeng UV-5R? А если осторожно с мощностью 1 Вт? (Мне просто интересно)

  5. Что будет, если работать в PMR диапазоне (446-446.200 МГц), где лицензия не требуется, но с мощностью 1 Вт?

Спасибо, что дочитали. Пишите, если что вдруг будет не получаться - разберемся вместе.

С. Н.

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


  1. Javian
    18.06.2023 17:29
    +1

    1. Законодательно никак не запрещают, но мешать другим это моветон

    2. https://habr.com/ru/articles/239641/

    3. 5. пока не нажалуются


    1. rus084
      18.06.2023 17:29
      +2

      1 для генерации сигнала лучше завести аппаратный таймер


  1. ZEvS_Poisk
    18.06.2023 17:29
    +2

    Статья отличная! Автору большой респект. В силу своих знаний постараюсь ответить на некоторые вопросы в конце статьи.

    1. Я, конечно, не держал в руках флиппер. Мне думается, что при написании программы, должна быть ассемблерная инструкция, запрещающая прерывания, она должна запрещать прерывания во время работы Вашей программы, и запрещать во все остальные моменты. Да, КО в этой теме похоже я.

    2. При наличии позывного, общаться с другими радиолюбителями Вы вправе, но с оговоркой, что Вы не будете использовать криптографию.

    3. Вообще, нужно сдать экзамен. Он давольно простой. Конечно смотря на какую категорию Вы претендуете. На высшую надо знать азбуку морзе, уметь работать ключём, и читать эфир на слух. На более простые категории надо знать правила использования эфира, и кое что из радиолюбительства. Это может быть закон Ома и/или пример расчета rc цепи для постоянной времени. У моего отца есть позывной. Лично я хочу получить, но все никак не соберусь, хотя аппаратуры уже дофига.

    4. Если не очень большой мощностью, то ничего не будет. Строители, таксисты и прочие псевдослужбы не имеют лицензий и позывных, органы закрывают на это глаза. Ну и за всем не уследишь. При бОЛьших мощностях, уголовных проблем также не будет, могут административно вздрючить, и то маловероятно.

    5. Работайте спокойно. Сейчас не 95й год. И Вы главное не шпиен. Ограничения на пользование эфиром было создано из-за именно охоты за шпиенами, сейчас другие времена и методы и шпиены попадаются на других каналах.


    1. Pisikak Автор
      18.06.2023 17:29

      Спасибо за Ваш ответ! Буду читать про ассемблерные инструкции.


      1. dunaevai
        18.06.2023 17:29
        +1

        Не нужно, в самой furi всё написано, самый простой - FURI_CRITICAL_ENTER()

        , не не забудьте выйти.


        1. Pisikak Автор
          18.06.2023 17:29

          Спасибо!


  1. Astroscope
    18.06.2023 17:29
    +2

    Можно ли использовать частоты APRS (144.800 МГц) для, например, общения с другими радиолюбителями (конечно же имея все необходимые разрешения, лицензии, позывные и т. п.) наподобие чата в WhatsApp (ну это например)?

    APRS, упрощенно, это передача данных о радиолюбительских объектах, а также обмен сообщениями и передача бюллетеней. Обмен сообщениями это, очевидно, один отправитель для одного получателя и ACK о получении, а передача бюллетеней это широковещание любого отправителя неограниченному числу подписанных на бюллетень получателей. То есть да, APRS - это для общения между радиолюбителями, а не только для автоматического обмена данными о местоположении и статусе.

    Что будет если выходить в эфир без лицензии и позывного, например с помощью той же самой Baofeng UV-5R? А если осторожно с мощностью 1 Вт? (Мне просто интересно)

    С учетом легкости получения р/л лицензии и легальной работы на р/л частотах в почти любой стране мира, это просто стыд какой-то, даже не знаю, как и зачем это можно обсуждать всерьез. Тем не менее:

    В некоем условном идеале вас "сдадут" сами радиолюбители, если вы будете пытаться работать на радиолюбительских частотах. У некоторых радиолюбителей могут оказаться самодельные или фабричные устройства для вашего обнаружения, также вас выдаст специфика вашего сигнала - обнаружить вас в виде хобби будет посильным делом для частных лиц, действующих из чистого энтузиазма, и могущих замотивировать соответствующие службы, которым самим по себе вас находить было бы лениво, пусть это и входит в их самые что ни на есть непосредственные обязанности. Если вам это интересно, то я лично знаком с такими энтузиастами и многих знаю заочно - это не досужие домыслы.

    В суровом реале, особенно если вы не будете лезть на р/л частоты и вообще заниматься очевидным хулиганством, с вероятностью 99,9% ничего не будет. Да и на р/л частотах вам скорее всего с помощью обсценной лексики подробно расскажут про вас и вашу почтенную мать, вместо реальных поисков и несколько недружественного личного общения.

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

    Что будет, если работать в PMR диапазоне (446-446.200 МГц), где лицензия не требуется, но с мощностью 1 Вт?

    С вероятностью, практически не отличающейся от 100%, ничего не будет. Формально, чтобы работать без лицензии, вы обязаны использовать немодифицированные сертифицированные PMR-станции - то, что вы ограничиваете мощность до требуемого по стандарту PMR, самого по себе недостаточно. Фактически определить нарушение и доказать его совершение крайне трудно, а без откровенного хулиганства с вашей стороны - решительно никому не нужно.


  1. Astroscope
    18.06.2023 17:29
    +3

    Я знаю, что читать мануалы - скучно. Но библию, все же, пролистать сильно рекомендую.

    AFSK штука очень толернатная, но покуда нам хочется максимизировать количество декодируемых пакетов, необходимо озаботиться о таком параметре как девиация. Не вдаваясь в технические подробности частотной модуляции (FM) примем, что девиация, т.е. изменение частоты несущей при, в идеале, неизменной амплитуде, зависит от громкости модулирующего AFSK сигнала или вообще звука, поэтому для настройки девиации мы будет изменять уровень AFSK тонов, подающихся на микрофонный вход радиостанции. Целевая девиация 1k2 AFSK - 3.0kHz~3.5kHz (для 9k6 GMSK требования существенно жестче). Почему? Если уровень сигнала слишком велик, возникают искажения, и вместо двух чередующихся относительно чистых тонов у нас получается каша из гармоник и интермодуляции - на анализаторе спектра будет видно, что вместо относительно чистого пика над относительно плоским дном у нас растет бесформенный горб на всю ширину аудиоканала. Если уровень сигнала слишком мал, у нас падает SNR. Первое намного хуже, но второе тоже плохо. То есть что-то декодироваться будет в почти любом случае, но мы же хотим сделать все правильно, да? Для установки уровня практически достаточно просто потенциометра (можно многооборотного, но практически необязательно), ну или программной регулировки уровня. Для измерения девиации есть специальные приборы, но можно померять девиацию и без регистрации и SMS.

    Мой твит про APRS.


  1. needsomedata
    18.06.2023 17:29
    +1

    Респект! Уважаю! Сбилдил через https://flipc.org/ - единственное в коде поправил APRS_hello_world_clean на aprs_hello_world_clean, т.к. билдер ругался. Сигнал расшифровывал на iPhone. Подписался на репу.


    1. Pisikak Автор
      18.06.2023 17:29
      +1

      Рад что у Вас всё получилось! И спасибо за исправления


  1. kevit
    18.06.2023 17:29
    +2

    Классный проект! Посмотрите еще https://github.com/xMasterX/flipper-pager (POCSAG)


    1. Pisikak Автор
      18.06.2023 17:29

      Спасибо за интересную ссылку! Я такое люблю