Alessio Caiazza

Il sapere umano appartiene al mondo.

M17 on your Pi-Star

M17 is one of the most exciting things happening on ham radio, this article covers my patch to turn a Pi-Star into an M17 enabled system.

But first, if you are not familiar with M17, here is the definition from the project website:

M17 is a new digital radio protocol in development as an alternative to those currently available, with freedom in mind. Freedom in the code, protocol, voice codecs, and hardware. The goal is to provide a better option for digital radios in the future.

I’ve started working on the M17 Project; my involvement started with the OpenRTX project, but I contributed to other projects inside the M17 world, as it should be in a healthy open-source ecosystem.

Howto patch your Pi-Star

The Pi-Star upgrade system relies on Git, so I wrote a little script that points the upgrading system to my M17 branches.

The script will modify the dashboard, /usr/local/bin, and /usr/local/sbin. It may fail if you have modifications to these places, so please revert to a stock pi-star before proceeding.

To patch your pi-star, connect with ssh and run the following command.

curl | sudo bash -

This script will update the binaries and the dashboard to support M17. It will add a new m17gateway service and the necessary firewall rules.

To successfully run M17Gateway, your MMDVM modem or HotSpot hat needs a firmware update. For the hotspot, you need at least v1.6.0; for the modem, instead, compile it from the master branch.

Beware that the MMDVMHost included in this patch is backward compatible with the previous version, but the stock binary included with pi-star isn’t forward compatible; you need to downgrade your firmware before uninstalling the M17 patch.

Now that we have a supported firmware it is time to enable the M17 mode from the configuration dashboard.

enabling M17 mode

Enabling it will generate a configuration file in /etc/m17gateway; it is possible to edit it with the full editor available under the expert configuration page. If you only care about the startup reflector, it is possible to change it from the regular configuration page.

startup reflector

The first M17 QSO

Now you are all set; it’s time to test your M17 enabled pi-star.

This is how the dashboard looks like when you have M17 enabled.


There are several options to test this setup. I’ll briefly cover some of them, starting with the option that requires no radio at all.

DroidStar over Internet

DroidStar implements M17 both TX and RX. You can install it on Android, join the iOS app beta, or compile it and run on your computer.

If you connect DroidStar and Pi-Star to the same reflector it will be possible to see the transmissions on the dashboard (and on the hotspot display).

If you have an rtl-srd device, it will be possible to decode the M17 traffic using SDR++.

DroidStar transceiver

With an MMDVM HotSpot board, an USB to TTL adapter, and on USB On The Go (OTG) cable it is possible to turn your Android device into an M17 transceiver using only RF, without internet access.

More details on this experiment are available on the M1HOG wiki.


M17Client is yet another great piece of software from G4KLX. It’s a client designed for use with MMDVM modems and hotspots to allow for use as an M17 radio. This requires access to an M17 capable MMDVM modem or hotspot and of course a suitable radio if using the modem version.

Here is a short video where I’m testing my QSY pull request.


OpenRTX is a Free and Open Source Firmware for Digital Ham Radios, at the time of writing it only supports M17 TX but no RX.

It requires hardware modifications and you can read how much fun Michael DL6MHC had with this in his blog post.

Mobilinkd TNC3

If you have a 9600-baud capable radio, a Mobilinkd TNC3, and an Android device, then you can follow the Mobilinkd M17 Setup Guide from WX9O.

Why I’ve done all of this?

I was building an M17 hotspot from scratch, and once I got all the binaries ready, I realized how many features come for free in pi-star.

After studying the pi-star upgrade system, I decided to patch it to support M17. So I shared my learning on the M17 Discord server, and other hams started using it, reporting bugs, writing articles.

It was no longer only my script, others were using it, so I decided to upstream my changes hoping to have them as part of the standard pi-star distribution.

So I opened a pull request for the dashboard and one for /usr/local/sbin in the hope that we can start a conversation about what is missing to bring M17 to all pi-star users.

73 de IU5BON


( 5GYHJ0)

Hello Tommi, thanks for your comment.

If a page is mostly text, I could do as you suggested, but when pages have a lot of photos or source code blocks (quite common in my blog) then I prefer to keep everything in one page and share the common parts.

Other than that, I personally enjoyed working on the solution to this specific problem.

( 5GP8C2)

My first multilanguage page

This article is also available in english.
Questo articolo è disponibile anche in italiano.

Avevo bisogno di una pagina multilingua, qualcosa di semplice che stesse bene con il mio sito generato staticamente

Ecco i miei requisiti:
  1. deve essere basato esclusivamente si CSS e JavaScript, senza framework,
  2. deve essere di default in inglese, ma supportando una seconda lingua (l'italiano),
  3. deve essere possiible linkare una pagina in una determinata lingua.

L'idea è quella di identificare il body HTML con una classe CSS che descriva il linguaggio visibile, show-en oppure show-it. Dopodichè identificare i contenuti nelle due lingue con le classi lang-en e lang-it.

Con tutto questo pronto, le seguenti 4 righe di CSS faranno in modo che siano visibili solo i contenuti nella lingua desiderata.

I needed a multilangage page, something simple that could fit on my static generated site.

Here are my requirements:

  1. it should only use CSS and plain JavaScript, without frameworks,
  2. it defaults to english, but supports only another languange (italian),
  3. it should be possible to generate a link that shows a specific languange.

The idea was to mark the HTML body with a CSS class describing the expected language, show-en or show-it. And then mark the language specific contents with lang-en or lang-it.

With this in place, the following 4 CSS lines will only show the expected language.

.show-en .lang-it { display: none; }
.show-en .lang-en { display: block; }

.show-it .lang-en { display: none; }
.show-it .lang-it { display: block; }
Bene, vediamo allora la parte HTML, quello che segue è un body di esempio.
Let’s now talk about the HTML. Here follows an example body.
<body class="show-en">
    <div class="lang-it" lang="en">
        This article is also available in <a href="#en" class="switch-lang">english</a>.

    <div class="lang-en" lang="it">
        Questo articolo è disponibile anche in <a href="#it" class="switch-lang">italiano</a>.

    <div class="lang-en" lang="en">
        In this article I'm going to explain how I made a multilanguage page,
        only using CSS and plain JavaScript, without frameworks.
    <div class="lang-it" lang="it">
        In questo articolo spiegherò come ho realizzato una pagina multilingua.
        Utilizzando solo CSS e JavaScript, senza framework.

       // example here
  • Si può notare che il body utilizza la classe show-en,
  • i contenuti localizzati sono identificabili dalle classi lang-en e lang-it,
  • si possono avere contenuto condivisi, privi di localizzazione (come il blocco pre),
  • ci sono due link speciali identificabili dalla classe switch-lang che si occuperanno del cambio di lingua.

Completiamo l'esempio con un po’ di JavaScript.

La prima funzione, setLangFromHash, estrae l'hash dalla URL e controlla se contiene l'indicativo di una lingua.

Siamo solo interessati a #it perché l'inglese è attivo di default.

In aggiunta, l'hash viene suddiviso in base alla presenza del carattere / per permettere l'utilizzo di link interni al documento mantenendo la localizzazione attiva, ed esempio #en/section-1.

  • It it important to note that the body element defaults to show-en class,
  • localized contents are marked with lang-en or lang-it,
  • it is possible to share unlocalized content (like the pre block),
  • there are two special links identified by the switch-lang class that will handle language switching.

Let’s complete the example with some JavaScript.

The first function, setLangFromHash, will extract the URL hash and check if it contains a language code.

We only care about #it because english is the default language and there is nothing to do in that case.

As a bonus, we split the hash on / to allow localized internal links like #en/section-1.

var setLangFromHash = function () {
    var hash = window.location.hash;

    var lang = hash.split("/", 2)[0] || "";
    if (lang == "#it") {
        document.body.classList.replace('show-en', 'show-it');

La seconda funzione, switchLang, si occupa di alternare le classi show-en e show-it nell'elemento body.

The second function, switchLang, will toggle between show-en and show-it on the body element.

var switchLang = function () {
    if (!document.body.classList.replace('show-en', 'show-it')) {
        document.body.classList.replace('show-it', 'show-en');

    return true;
Infine abbiamo bisogno di assemblare queste funzioni nella pagina. Quando tutti gli elemento del DOM sono pronti, possiamo cercare gli elementi identificati dalla classe switch-lang ad assegnargli la funzione switchLang, infine possiamo invocare la funzione setLangFromHash per identificare la lingua corrente nel caso in cui l'utente arrivi da un link esterno.
We finally reached the glue code that connects all the parts. When the DOM is loaded, we search for elements with the switch-lang tag and assign them to the switchLang function, then we invoke the setLangFromHash function to make sure the italian language is detected if a user is coming directly with #it in the URL.
document.addEventListener('DOMContentLoaded', () => {
    // language switching links
    document.querySelectorAll(".switch-lang").forEach(function(a) {
        a.onclick = switchLang;

    // override language based on hash
E con questo abbiamo finito.
And that’s all.
( 5GG8Zo)

Hello. My name is Alessio Caiazza. I'm also known as nolith. I love writing code and technology. I'm passionate about production engineering.

This is where I write my thoughts trying to follow IndieWeb principles.

Staff Backend Engineer, Delivery @ GitLab


IU5BON HamRadio callsign

"Il sapere umano appartiene al mondo."

An IndieWeb Webring 🕸💍