AFSK (Audio frequency shift keying) was used primarily for slow – 1200baud – packet radio. Today it is mainly used by HAMs for APRS(tm).

The most common setup is to have radio equipment with TNC (terminal node controller) connected to host PC. Host is running some software that understands incoming packets and reacts to them. If you are a HAM you probably have radio and PC already. What you need is the TNC. General problem I found with TNCs is that they tend to cost too much – 100+EUR price tag is not uncommon for 5-10year old equipment, and new TNC models cost 200+EUR (lots of features not necessary for AFSK operation).

This project is based on BeRTOS and inspired by KI4MCW’s site. KI4MCW’s code served as kind of introduction to BeRTOS operating system. One big exception is that instead of Adruino this project uses STM32VL discovery board.

This little board contains STM32F100 chip – 24Mhz clock, 1x 12bit ADC, 2x 12bit DAC, 3x UART, etc. It is a complete development board with ST-link debugger on the same board. Costs ~10EUR. Since this board contains ADC and DAC, all you really need to complete TNC are few external components (2 resistors, 2 capacitors, 1 trim pot) and the right software for the MCU. The total price is far below 100EUR, and it probably even less than 15EUR. This is practical for massive fill-in digipeater deployment.

The BeRTOS package comes with AFSK modulator and demodulator and simple AX.25 protocol “decoder”. All I had to do it implement KISS protocol and the TNC was finished. KISS Protocol was implemented according to this article – all features are implemented (including message queuing and p-persistence). Many routines are used from STM32 standard library as BeRTOS doesn’t have support for STM32 timers, DAC, and ADC. (It does support ADC, but in a way, that is not usable in this project).

Original AFSK demodulator was later rewritten by OM5AMD to use FIR filters (instead of IIR). New demodulator showed slightly better results in testing (tested against TNC Test CD). Both algorithms can run with opened squelch, but none of them provides (reliable) carrier detect functionality. Carrier detect algorithm is implemented at packet level by looking at incoming bytes and checking fixed bytes on certain positions. This is so-called Digital carrier detect, which is of course not perfect, but worked pretty well in field testing. True carrier detect feature is still on TO-DO list and any ideas are more than welcome :-).

The sources for the whole project are available on GitHub. You will need Arm toolchain to build it. I suggest summoning toolchain, which works well. I have also added compiled binary files to the repository, so you can just upload the code to your board and test.

The project is configured to use USART2 interface (PA2 – TX, PA3 – RX), PA1 as analog signal input, PA4 as analog signal output and PA0 as PTT. Whole project is powered by 5V and all necessary LDOs are already on board.

Serial port is 3.3V, so you are going to need MAX(3)232 (or similar) to interface the board to RS232 port. I personally use cheap USB-to-serial converters, which are based on CP2102 chip (3v3 level, 5volt tolerant)

Analog input needs to be AC coupled and biased to “center” like shown on image. This simple circuit works only when connected directly speaker output. Ideal levels are >1.0Vp-p.
input schematic

TNC connections available on radios usually provide 0.5Vpp signal which is not enough to detect signal correctly! If this is your case, you are going to need opamp to amplify the signal. In my experiments I have used the following circuit. (V_POL = VCC/2, resistors with NA are not populated) MCP602 opamp has been selected, because it works at 3.3volt and is (almost) rail-to-rail. AC coupling and biasing resistors are not necessary, as the biasing is already done by opamp, and ADC input is high-impendance.

Output from the STM32’s DAC is already buffered and requires no external buffer. Signal swing is 3.3Volt p-p, which is probably too high for most radios. Using single trim pot you can adjust the levels to proper values. AC coupling capacitor should be ~10uF (there is error in schematic). This capacitor could probably be omitted if your radio already has AC coupling on the signal input.

Most radios have pull-up on PTT buttons, so NPN transistor is all that you need to drive the PTT. On some radios, like Yaesu VX8, you need extra resistor in series, because the PTT signal is multiplexed with audio signal. The PTT output is routed to the PA0 pin on the Discovery board, which is the same pin that is used for the “USER” button. This is particularly useful when testing, “USER” button works as “PTT” button.

Complete TNC:


TNC on picture is currently used in a digipeater which we (OM5AMX and OM5AMD) setup near our QTH. For the host computer we used decommissioned Routerboard RB411 with OpenWRT installed and APRX digipeater/I-gate software. Radio used is Baofeng UV-3R, with RadioShack HTA 20. Output power is about 15Watts, and selectivity of UV-3R radio was better in tests than Yaesu VX8 (compared by rate of packets/hour).
This digipeater has been running for about 4 months without any trouble. You can find some statistics on – OM5AMX-1

I don’t really want to go into details of software inner workings, or how to program the MCU. If you have any doubt, just ask, or better figure it out yourself ;-).
Any questions or comments are welcome.
73 OM5AMX, Michal