Pull to refresh

APRS. AFSK modulator from Flipper Zero

Level of difficultyMedium
Reading time7 min
Views4.9K

There is such an interesting data transfer protocol - APRS. A lot has already been told about him on the Internet. There will be no in-depth theoretical material here. This article will describe how to create your own "pocket" AFSK modulator. In the following articles there will be instructions for going on the air and for creating a simple demodulator. This will allow you to accept APRS packages and display the information on the display right on the street. Everything will be implemented for Flipper Zero. If you don't have this gadget yet, then don't worry and try everything on the great and terrible Arduino. It is very interesting to send information at a distance "with your own hands".

We will not build a "bicycle", we will only train "crutches"

The Internet has everything. And the implementation of AFSK modulators also exists. Only under FZ (Flipper Zero) not yet. This gadget is good compared to my favorite Arduino Nano. For example, the fact that it is a complete device in a case with a display and buttons. Thus, the source code with an open license will be taken and modified taking into account the features of FZ. Here I would like to appeal to more experienced readers. Who are well versed in FreeRTOS. After all, it is she who "spins" on FZ. The original source codes are intended for Arduino. Multitasking is not used there. When developing for Flipper, I encountered an exit from important functions to interrupts. The solution was found. But I'm sure it can be implemented better. Please tell us in the comments how to do it right.

"If someone wants to develop an application, I'm in business" [Gluti. // Rick and Morty. - Season 4. - 2 series.]

You will need to figure out how to create applications for Flipper Zero. This is created for version 0.83.1. https://github.com/flipperdevices/flipperzero-firmware/releases/tag/0.83.1.

fig. 1. Screenshot of the firmware version of the device from the experiment
fig. 1. Screenshot of the firmware version of the device from the experiment

Here, too, we will not dwell in detail. Because everything is already well described in other articles. I will give examples and give links that I used myself. (Many thanks to the authors of these articles for their work done for us!):

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

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

In the article on the second link there is a good example of the implementation of timer triggering. If you use furi_delay_ms(tx_delay);, you will not be able to exit the application at the moment of pause. It's not very convenient. Especially when there are pauses of three minutes. So, timers are necessary.  Thus, the memory allocation/deallocation template from the first article has been improved. This is due to the use of a timer. (As it seemed to me, the second article will be easier to understand. especially for readers who have not yet gone beyond Arduino). 

A good template with memory allocation/release for future applications

https://github.com/NSV47/good_template

Download, unzip and put the directory  /good_template to the catalog flipperzero-firmware/applications_user. Further commands ./fit fap_good_template and ./fbt fap_deploy. And the app on the device.

Everyone says that the most difficult thing is the icon. And it's true. In the repository, you can take my APRS icon. Build firmware. Еransfer to the device. Here is a demonstration:

About transferring a project from one debugging board to another device

When I found out about APRS, I wanted to repeat the success. I came across a project on the Internet that I found very interesting. Here is this project: https://github.com/handiko/Arduino-APRS. Let's use a good template. And we will transfer the functions from the repository of the Github user with the nickname Handiko to it. We will be grateful to him for his great work done. It's better to start with test firmware. So that I can use examples to point out the FreeRTOS feature.

Single tone test 1200 hz

In an infinite loop , it is called set_nada(_1200);, which causes set_nada_1200(void);. In the second function, one of the outputs is turned off and off. For FZ, the use of GPIO is described in the first article. Then our function set_nada(_1200); takes the form:

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);
}

In fact, the same as Handiko. But under the realities of FZ. It's a pity that it doesn't work the way we would like it to:

The simplest buzzer is connected to the output. We hear his clicks in the background. Instead of 1200 Hz sound, the frequency is about 1 Hz. These are the features of using FreeRTOS. It turns out an infinite loop in the FZ application is a little different than in Arduino. Let's rewrite the function void set_nada_1200(SingleToneTest1200HzApp* app). And it should be called not in an infinite loop, but before it:

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);
    }
}

Result:

Problem again. Not so obvious this time. And it took me a couple of evenings after work to solve it. There are some signal distortions, they should not be. This anomaly is visible very well on the waveform:

fig. 2. Waveform of an anomaly in the signal
fig. 2. Waveform of an anomaly in the signal

If you leave it as it is and continue to transfer the code, then everything will look as it should. It is very difficult to interpret the waveform of a modulated message. And the buzzer is generally a toy. It is necessary to deal with this problem at this stage. These are now the features of FreeRTOS that were mentioned at the very beginning. And which need to be rewritten more correctly. When looking for a solution, I came across this article https://habr.com/ru/articles/129445/. Thanks to the kind person who wrote it. Critical sections must be used. Moreover, suspending the scheduler did not help in any way. It was the use of the critical section, which is built on macros, that helped. The article says so. Suspending the scheduler only protects against other tasks. And we fall into an interrupt. Now the function takes the form:

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();
}

Now everything is correct by ear and the oscillogram is in order. Only one thing. While the code is being executed in the critical section, nothing else can be done. For example, exit the application by pressing the back button. In our case, of course, the packages are not that big. You can wait, but the soul of an engineer requires an ideal. And the family of attention. So, the question to more experienced readers is: "How to get rid of this "crutch"?"

Link to the sources of “Single tone test 1200 Hz” (note that this code has not yet implemented access to the timer. But he is not needed here):

https://github.com/NSV47/Single_Tone_Test_1200_Hz_draft 

Handiko has some test firmware in the Arduino_APRS repository. But I will not disassemble them. Because I didn't encounter any serious problems. As the need to use critical sections. I will comment only on a couple of points. Even before I started using critical sections, I didn't pay enough attention to the anomaly on the waveform (well, or by ear with a buzzer). And immediately implemented the APRS_Random_String test. To my surprise, this signal was decoded quite well by the computer (how this can be done I will describe further). Which was decoded in about one case out of 20. At the same time, I sinned on the variable "baud_adj". Because I thought I had something wrong with the pulse duration.

Then I found a solution and used the critical sections. But to my surprise, I couldn't decode it. It seemed that the problem was the inability to use critical sections. And when switching from function to function, an interruption occurs. I tried to compare the original waveforms taken from Arduino with the signal from FZ. And I spent one evening trying to write everything in one function (looks scary. There are more than 3000 lines. But I wanted it to work).

fig. 3. Example of abnormal programming
fig. 3. Example of abnormal programming

And the mistake was in my inattention. In the source code of "APRS_Random_String" in the implementation void send_string_len(const char *in_string, int len) the j+len" index is passed to the "send_char_NRZI(in_string[j + len], HIGH) function. But in the source code "APRS_Hello_world" in the implementation of the same function, the line is passed with the index j. Thus, when ripping off someone else's code, be careful :)

APRS_Hello_world

In fact, it remains only to fill a good template with functions from the source of the same name. The only difference is in accessing GPIO. This imposes on us the need to pass the structure of our application in the form of a parameter to most functions. If you rewrite a "good template" in the image and likeness of the example from the second article, the code will be simplified. I'll show the sources completely:

https://github.com/NSV47/APRS_hello_world_clean

AFSK Decoder

To check the health of the application, it is necessary to decode the message. To do this, you can use AFSK1200 Decoder. To get a signal to the laptop's sound card, I used this connector:

Fig. 4. Pinout Jack 3.5 for connecting Flipper Zero to a computer
Fig. 4. Pinout Jack 3.5 for connecting Flipper Zero to a computer

You will need to select the signal source in the INPUT window and press Start Decoder:

fig. 5. Example of decoded messages
fig. 5. Example of decoded messages

There are other programs that can decode such signals, for example, UISS (instructions https://r4uab.ru/aprs-iss/ ). With this program, I was even better at receiving real APRS signals. Or GNU Radio (instructions https://github.com/handiko/gr-APRS ). I used GNU RADIO in UBUNTU and this instruction.

Results

Now we have managed to create an AVS modulator with our own hands. And figure out how Flipper Zero apps work. We even connected Flipper Zero to the computer. But to reveal the APRS data transmission protocol, we will need a transmitter. For example, I don't have an amateur radio and radio callout license yet. Thus, in one of the following articles we will go on the air in the PMR band (446-446.200 MHz). There is no license required. The power of the transmitter will also be the permitted 500 MW. And then we'll see how far it will be possible to send messages.

Despite some success, I still ask more experienced readers to give answers to the questions that have arisen:

  1. How to protect yourself from the peculiarities of FreeRTOS, so that important code does not interfere with interrupts?

  2. Is it possible to use APRS frequencies (144.800 MHz) for, for example, communication with other radio amateurs (of course, having all the necessary permits, licenses, call signs, etc.) like a WhatsApp chat (well, for example)?

  3. How, after all, can I get an amateur radio license and a call sign now? Maybe someone recently received? 

  4. What happens if you go on the air without a license and a call sign, for example, using the same Baofeng UV-5R? And if you are careful with the power of 1 watt? (I'm curious)

  5. What will happen if you work in the PMR band (446-446.200 MHz), where a license is not required, but with a power of 1 watt?

Thank you for finishing reading. Write, if something does not work out, we will figure it out together.

S. N.

Tags:
Hubs:
Total votes 3: ↑3 and ↓0+3
Comments2

Articles