After the challenges I had with finding a bluetooth solution for APRS, I more or less gave up on using existing TNC solutions.
During my last installment, I started looking into the Arduino Uno R4 WiFi. It occurred to me that it has an ADC, so I started looking into whether or not anyone had used it as a TNC before. It looked like it had been tried – but with little success.
Since I was vibe-coding (I’ll admit- AI can write better code than me), I challenged ChatGPT with the project. I started back in December 2025 with a pretty broad scope, and it didn’t work well at all. I did get a few things working, such as packet generation (transmitting) and the BLE connection. Since I couldn’t get receive working, I started to get a little frustrated. I eventually tore the setup down – needed to finish my masters and all. I knew once I got moved, I could give it another shot.
A couple months later, I started picking the project back up.
One thing I learned during development of RevLogger was the need for a detailed software requirements as a part of the prompt. Before I started any hardware work, I began fleshing out the software requirements specification (SRS). As I found more and more features that I thought were important, I added as much detail as I could imagine.
After a bit, I reconnected the hardware, and began breaking down my biggest issues. Since receive was the biggest challenge, that’s what I started with. I intentionally broke the project down to JUST a receive development, while dumping other ideas into the SRS.
To satisfy the ADC, I used a couple 10k resistors to create a voltage divider between 5V and 0V, with the ADC input riding at 2.5V. I then coupled my computer audio output through a 100uF non-polarized cap into the ADC input.
One thing I did was start playing with the LED matrix to turn on an LED when a signal is present at the ADC input, and later on, a second light during packet decodes. Both of these were infinitely valuable during troubleshooting. While I was at it, I created a boot-image on the LEDs… hint.. it’s a squirrel.
because I’m using GPT, it took a lot of troubleshooting, and commands like “improve noise performance”, but eventually, i found a solution. the biggest issue was timing within the framing and decode string.
I eventually made a huge breakthrough when I could get some longer packets reliably decoded – this was a HUGE step in the right direction!
Once “we” got decide working, then it became a matter of messing with the framing and timing to get it to be reliable.
Next, it was time to re-tackle Bluetooth. One of my goals was to use this device with IOS and APRS.FI. The kiss over (BLE) protocol is very specific. I had it connected once… and it was time to recreate that.
Once I pulled my old code in and tweaked the service values, it connected! Then there was a need to get the two processors to pass traffic. There were a LOT of incremental builds to grow this capability, but it started working, and APRS.FI would display raw incoming packets.

I used the packet audio cd file (http://www.wa8lmf.net/TNCtest/) to see how it would do. Since this has a 45 minute recording of real world APRS, it was perfect. It wasn’t long before I was not only seeing packets, but able to watch them populate my map!

I was curious how it was really doing compared to commercial solutions, so I contacted NV4B to ask if he could run the CD against some old TNCs because Chris collects stuff. Ingot a response:

First, I needed a way to track decodes, so I added it. The TNC would now count decoded packets. Then I ran the entire 45 minute track. 845+ successful decodes. Wow!!
After this, I started adding a lot more commands to make this device command compatible with kantronics products. I’ve also been working the overall system architecture.
To save code space, the following system architecture is planned:
- Hardware UART reserved for PC connection (like a kantronics)
- I2C will be used to connect a real time clock
- I2C will connect to a “I2C to UART” board, which will connect to a GPS module. This board will host 2 UARTs (though one will be unused for the moment) to allow for expansion, whether it be a weather station or another peripheral. I’ll also need to add firmware support for the weather stuff.
The biggest challenge is the SRAM limitation to avoid duplication (so we don’t digipeat something we already digipeated. I think we got that worked out.
As of now, I’m searching for another TNC to do some live testing with. I’ve decoded packets with an SDR on another computer, but that’s a sketchy setup.
I’m a bit concerned with how the WiFi / web UIs will pan out, but they should be simple enough. There’s also some functionality planned for the other features (foxhunt controller).
I’m also limiting GPS support to GGA, RMC, and one other sentence just to save space, and because these provide the position, time, and constellation data I am interested in.
As of now, here’s how the firmware has evolved to this point:
- v0.1a — First integrated Bell-202 RX/TX core with AX.25/HDLC framing and the known-good transmit path.
- v0.1b — Improved receive TRACK recovery after timing lock was lost.
- v0.1c — Added a parallel fixed-phase safety decoder alongside the tracked decoder.
- v0.1d — Added rolling Bell-202 occupancy logic to improve TRACK retention.
- v0.1e — Added separate development indicators for software channel activity and actual packet/timing detection.
- v0.1f — Fixed compile/scope problems introduced with quiet receive output.
- v0.1g — Changed RX to decode continuously instead of gating the modem from the software carrier detector.
- v0.1h — Ran all 8 fixed timing phases continuously and improved packet-acquisition quality.
- v0.1i — Added 8 confidence-weighted soft/voted receive decoders; initial build exceeded available SRAM.
- v0.1j — Recovered SRAM by shrinking diagnostic snapshots while preserving all 8 receive paths.
- v0.1k — Hardened the software carrier/activity detector against noise without changing or gating the proven decoder; this became the practical RX baseline.
- v0.2a — Added persistent configuration using versioned dual-slot EEPROM storage with CRC protection.
- v0.2b — Added the first framed RA4↔ESP32 IPC framework.
- v0.2c — Fixed Arduino auto-prototype/compiler problems in the IPC implementation.
- v0.2d — Moved IPC onto the internal RA4↔ESP UART path and reduced diagnostic RAM usage enough for reliable compile/runtime.
- v0.2e — Added
, forwarding each unique CRC-valid AX.25 receive frame to the ESP32 without altering the modem.IPC_RX_FRAME
ESP32-S3 bridge revisions
- v0.1 — First attempt at adding TreeRat IPC functionality to the Arduino UNO R4 WiFi USB bridge.
- v0.2 — Improved patch placement and bridge integration, but source matching was still fragile.
- v0.3 — Changed the patcher to regex-based matching; exposed a missing Python
import.re
- v0.4 — Fixed the Python patcher issue, but the build process still masked underlying compile failures.
- v0.5 — Changed the builder to fail immediately on compiler errors, exposing corruption of the Arduino bridge source.
- v0.6 — Rebuilt the patching process around a clean upstream Arduino bridge checkout and successfully preserved normal USB/programming operation.
- v0.7 — Corrected the physical RA4↔ESP IPC UART to ESP GPIO6/GPIO5 / Serial1 and disabled the conflicting stock AT task.
- v0.8 — Added reception and human-readable diagnostic display of
packets from the RA4.IPC_RX_FRAME
Major functionality now proven
At this point we have demonstrated:
- Bell-202 1200-baud receive on A1.
- Bell-202 transmit on A0.
- AX.25 HDLC framing, bit stuffing, NRZI, and FCS handling.
- Real over-the-air TX decoding by another receiver.
- Real-world RX decoding of multiple live/noisy packet frames.
- Continuous 8-phase receive decoding with confidence-weighted paths.
- Software carrier/activity detection separated from actual modem decoding.
- Configurable
.CORSENS
- Persistent configuration surviving power cycles.
- MYCALL, destination, paths, TX delay/tail, persistence, slot time, full-duplex, carrier settings, and COS settings stored in EEPROM.
- Framed RA4↔ESP IPC with CRC and sequence numbers.
- HELLO / heartbeat / CONFIG_GET / CONFIG_SNAPSHOT exchange.
- Normal UNO R4 USB programming and terminal operation preserved while running custom ESP firmware.
- Correct physical RA4↔ESP communication over the board’s internal GPIO6/5 UART.
- Raw CRC-valid AX.25 frames transported successfully from RA4 to ESP.
- Multiple consecutive
packets received intact by the ESP.IPC_RX_FRAME
- ESP AX.25 diagnostic formatting including digipeater
/H-bit information.*
| 0.2e | Proven RA4 baseline: Bell-202 RX/TX, HDLC, AX.25, FCS, IPC RX forwarding |
| v0.2f | Early IPC TX-frame support |
| v0.2h | Added
RXCOUNT RXCOUNT RESET |
| v0.3a–b | Expanded KPC-style command shell |
| v0.3c | Functional CSMA/p-persistence TX scheduling |
| v0.3d | Compile fixes |
| v0.3e | SRAM fix by reducing large debug snapshot usage |
| v0.3f | Finalized/froze
CORTUNE CORSENS |
| v0.3g | Functional
BEACON BCNOW BTEXT UNPROTO |
| v0.3h | Added
COR PACKET COR SQUELCH BEACON AFTER |
| v0.3i | Functional UI digipeater:
DIGIPEAT UIDIGI UIDUPE UIDWAIT UIFLOOD UITRACE |
| v0.3j | Added
DIGITEST |
| v0.3k | Memory housekeeping: removed deep modem-development RAM usage, reduced TX buffer, moved LUT out of SRAM |
| v0.3l | I²C/RTC/UART-bridge foundation branch — shelved pending hardware |
| v0.3m | First functional AX.25 connected-mode engine |
| v0.3n | Connected-mode compile fix |
| v0.3o | Fixed 15-byte AX.25 control-frame TX; connected-mode synthetic tests passed |
| v0.3p | KPC-style
MONITOR MCOM MCON |
| v0.3q | Real
ECHO ON/OFF |
| v0.3r |
PACLEN SENDPAC |
| v0.3s | Added
CMD COMMAND CONNTEST ACK |
| v0.3t | Fixed printable SENDPAC handling with Arduino CR/LF; cleaned buffer-full behavior |
| v0.3u | Functional
MHEARD MHCLEAR MSTAMP MHEADER MRPT |

