Getting started

From nothing to a Morse transmission. The first run happens in dev mode on any Linux machine, so you can see the whole flow before a Pi is involved.

1. Add the library

go get github.com/psyb0t/gorpitx

You need Go 1.26 or newer.

2. Write the program

This program sends a Morse message, prints whatever the process writes, and gives up after three seconds.

package main

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"time"

	commonerrors "github.com/psyb0t/common-go/errors"
	"github.com/psyb0t/gorpitx"
)

func main() {
	rpitx := gorpitx.GetInstance()

	args, err := json.Marshal(gorpitx.MORSE{
		Frequency: 14070000, // Hz
		Rate:      20,       // dits per minute
		Message:   "CQ CQ DE N0CALL",
	})
	if err != nil {
		log.Fatal(err)
	}

	stdout := make(chan string, 100)
	stderr := make(chan string, 100)

	rpitx.StreamOutputsAsync(stdout, stderr)

	go func() {
		for line := range stdout {
			fmt.Println("stdout:", line)
		}
	}()

	go func() {
		for line := range stderr {
			fmt.Println("stderr:", line)
		}
	}()

	err = rpitx.Exec(
		context.Background(),
		gorpitx.ModuleNameMORSE,
		args,
		3*time.Second,
	)

	switch {
	case err == nil:
		fmt.Println("transmission finished")
	case errors.Is(err, commonerrors.ErrTimeout):
		fmt.Println("stopped after the timeout")
	default:
		log.Fatal(err)
	}
}

commonerrors is github.com/psyb0t/common-go/errors, which gorpitx already depends on.

3. Run it in dev mode

ENV=dev go run .

With ENV=dev, gorpitx skips the root check and replaces the rpitx program with a shell loop that prints one line a second, so the output looks like this:

stdout: mocking execution of morse 14070000 20 CQ CQ DE N0CALL...
stdout: mocking execution of morse 14070000 20 CQ CQ DE N0CALL...
stdout: mocking execution of morse 14070000 20 CQ CQ DE N0CALL...
2026/01/01 12:00:03 WARN failed to gracefully stop process after timeout err="failed to stop process: terminated [...]"
stopped after the timeout

The WARN line comes from the library’s timeout path. It logs the terminated result of a successful graceful stop as a failure, so you can ignore it.

The loop never ends by itself, so in dev mode every Exec ends through its timeout, a cancelled context or Stop. The arguments in the line are the exact ones the real program would get. Argument validation runs in dev mode too, so a bad frequency fails here the same way it fails on the Pi.

4. Install rpitx on the Pi

Use a Raspberry Pi model that rpitx supports, with Raspberry Pi OS.

sudo apt update
git clone https://github.com/F5OEO/rpitx.git
cd rpitx
./install.sh

Then install the tools the script-based modules call. Skip the ones you do not use.

Module Needs
fsk minimodem, sox
audiosock-broadcast socat, and csdr on the PATH (rpitx’s installer builds csdr)
sudo apt install minimodem sox socat

5. Point gorpitx at rpitx

gorpitx runs <GORPITX_PATH>/<module name>. Set GORPITX_PATH to the directory that holds the rpitx binaries, as an absolute path:

export GORPITX_PATH=/home/pi/rpitx

Do not rely on the default. It is the literal string $HOME/rpitx, and nothing expands $HOME in it. See Configuration.

6. Transmit for real

Build the program and run it as root without ENV. Pass GORPITX_PATH through, because sudo drops most of the environment.

go build -o morse .
sudo GORPITX_PATH=/home/pi/rpitx ./morse

Root is required: GetInstance panics with PIrateRF must be run as root in production mode otherwise. The message names another project, but the check is this one. See Troubleshooting.

Before you key up anything, check that the frequency is one you may transmit on, and put a filter between the Pi’s GPIO pin and any antenna.

Next

  • Running modules covers timeouts, stopping, streaming and the one-at-a-time rule.
  • Modules lists every field for all 12 modules.