APRS Client on a MCU

I wanted to develop an APRS client on an MCU (ESP-32) instead of a SBC (Raspberry Pi).

This client would use a modem/radio combination (Mobilinkd TNC3/Baofeng UV-5R) to get on the air (144.390 MHz). The client connects to the TNC3 over a Bluetooth Low Energy (BLE) link and is secure against Man-In-The-Middle attacks,

Looking around, I settled on hardware similar to that Amazon’s AWS IoT EduKit workshop uses  https://edukit.workshop.aws/en/ but using APRS instead of the internet—of course.

I choose the (relatively) inexpensive M5Stack Core2 unit (https://m5stack.com/products/m5stack-core2-esp32-iot-development-kit). 
Or I might ultimately build my own using an ESP32 development board.

The video demonstrates the first step in using APRS–position beaconing and registering with a station having an APRS-IS (internet) connection. After the first beacon, you must transmit a beacon every 30 minutes to stay current in the system.

Step 1 for APRS, beacon your position and register with APRS-IS.
Step 2 to follow…

Video Notes:
Next time use a script
It’s APRS-IS not -SI
Be cool. Don’t sound so excited when it works!

DSP Done Dirt Cheap

Created a new GitHub https://github.com/bobh/ESP_DSP and added a Real-Time FIR filter implementation from Espressif’s DSP Library.

Time permitting, I’ll be adding new DSP Library functions and documenting them here and on my YouTube channel @wm6h.

Using the Arduino IDE allows me to add Arduino Libraries for servos, LEDs, etc. along with action figure leitmotifs (WAV audio) for my Holiday displays.

SD Card Support For M5Stack Morse Tutor

Some Morse Tutor projects are adding SD card support (the text file on the SD Card is sent via Morse Code).

I went to gutenberg.org and loaded a large Sherlock Holmes book onto a Samsung 32 GB EVO Plus MicroSDHC UHS-1 Card. Formatted with PC/MAC app SDFORMATTER for Type1 SDHC FAT32

Inserted the card into the M5Stack SD card socket and had no trouble reading/writing it with this code:

#include <M5Stack.h>
//Micro SD / TF Card Test
 M5Stack initializing...OK
Listing directory: /
  DIR : /.Spotlight-V100
  DIR : /.fseventsd
  FILE: /SherlockHolmes.txt  SIZE: 590492
  FILE: /._SherlockHolmes.txt  SIZE: 4096
  FILE: /hello.txt  SIZE: 11
Writing file: /hello.txt
File written
Reading file: /hello.txt
Read from file: Hello world from M5Stack !!

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){

    // Print blank line on screen
    M5.Lcd.printf(" \n  ");
    M5.Lcd.printf(" \n  ");
    Serial.printf("Listing directory: %s\n", dirname);
    M5.Lcd.printf("Listing directory: %s\n", dirname);

    File root = fs.open(dirname);
        Serial.println("Failed to open directory");
        M5.Lcd.println("Failed to open directory");
        Serial.println("Not a directory");
        M5.Lcd.println("Not a directory");

    File file = root.openNextFile();
            Serial.print("  DIR : ");
            M5.Lcd.print("  DIR : ");
                listDir(fs, file.name(), levels -1);
        } else {
            Serial.print("  FILE: ");
            M5.Lcd.print("  FILE: ");
            Serial.print("  SIZE: ");
            M5.Lcd.print("  SIZE: ");
        file = root.openNextFile();

void readFile(fs::FS &fs, const char * path) {
    Serial.printf("Reading file: %s\n", path);
    M5.Lcd.printf("Reading file: %s\n", path);

    File file = fs.open(path);
        Serial.println("Failed to open file for reading");
        M5.Lcd.println("Failed to open file for reading");

    Serial.print("Read from file: ");
    M5.Lcd.print("Read from file: ");
        int ch = file.read();

void writeFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Writing file: %s\n", path);
    M5.Lcd.printf("Writing file: %s\n", path);

    File file = fs.open(path, FILE_WRITE);
        Serial.println("Failed to open file for writing");
        M5.Lcd.println("Failed to open file for writing");
        Serial.println("File written");
        M5.Lcd.println("File written");
    } else {
        Serial.println("Write failed");
        M5.Lcd.println("Write failed");

// the setup routine runs once when M5Stack starts up

void setup() { 
    // initialize the M5Stack object


    // Lcd display
    M5.Lcd.setCursor(0, 10);

    // Page Header
    M5.Lcd.setCursor(0, 05);
    M5.Lcd.printf("           Testing Micro SD Card Functions:\r\n");
    // digitalWrite(TFT_CS, 1);
    // Print blank line on screen
    M5.Lcd.printf(" \n    ");
    M5.Lcd.printf(" \n    ");
    listDir(SD, "/", 0);

    // Print blank line on screen
    M5.Lcd.printf(" \n  ");
    M5.Lcd.printf(" \n  ");
    writeFile(SD, "/hello.txt", "Hello world from M5Stack !!");

    // Print blank line on screen
    M5.Lcd.printf(" \n  ");
    M5.Lcd.printf(" \n  ");

    // Print blank line on screen
    M5.Lcd.printf(" \n  ");
    M5.Lcd.printf(" \n  ");
    readFile(SD, "/hello.txt");

void loop() {

  // put your main code here, to run repeatedly:

A lot wasn’t known but they went anyway

Armstrong and Aldrin did their own test. Just a moment after he became the first human being to step onto the Moon, Armstrong had scooped a bit of lunar dirt into a sample bag and put it in a pocket of his spacesuit—a contingency sample, in the event the astronauts had to leave suddenly without collecting rocks. Back inside the lunar module the duo opened the bag and spread the lunar soil on top of the ascent engine. As they repressurized the cabin, they watched to see if the dirt started to smolder. “If it did, we’d stop pressurization, open the hatch and toss it out,” Aldrin explained. “But nothing happened.”