QMesh: A LoRa-based Voice Mesh Network

Developing a synchronized, flooded mesh network to carry codec2-encoded voice over LoRa

Similar projects worth following
QMesh sets out to provide an alternative to FM voice/APRS data in amateur radio by creating a system where every device serves as a repeater. Such a system will enable rapidly standing up voice networks for EMCOMM and special event communications. Moreover, the large link budget and non-frequency-duplexed design will allow for compact, low-cost, low-power repeater nodes that can operate in remote locations using a battery and solar panel for power.

Enabling this system is a synchronized, flooded mesh network. This network will carry codec2-encoded voice frames between devices. Semtech's Chirp Spread Spectrum-based waveform, LoRa, will enable to the network to resist the considerable self-interference that occurs with a flooded mesh network.

QMesh is a novel mesh networking protocol designed to provide rapid self-healing and streaming capabilities to mesh networks in order to allow for voice communications. QMesh uses synchronized flooding to provide for self-healing as well as the ability to stream data. QMesh applies two techniques to the LoRa Chirp Spread Spectrum (CSS) waveform in order to make QMesh work: Coded Chirp Spread Spectrum (CCSS) and Chirp Offset Multiple Access (COMA).

Synchronized Flooding

A flooded mesh network is a mesh network where every node repeats they packets they receive. Synchronized flooding is a form of flooding where every node repeats the data at the same time. Synchronized flooding allows for self-healing an isochronous data transfer, enabling meshed voice communications. A major drawback of synchronized flooding, however, is the massive self-interference that occurs from multiple nodes retransmitting at the same time. QMesh uses two techniques, Coded Chirp Spectrum (CCSS), and Chirp Offset Multiple Access (COMA), that reduces the self-interference and allows for reliable communcation.

Coded Chirp Spread Spectrum (CCSS)

Forward Error Correction (FEC) is essential to getting reliable communications in the presence of the large amount of self-interference that occurs with synchronized flooding. As a result, QMesh implements a sophisticated form of FEC on top of LoRa, currently Reed-Solomon-Viterbi (RSV). RSV is a combination of convolutional coding concatenated with Reed-Solomon coding. Used in the past in such applications as interplanetary spacecraft, RSV increases communication reliability for both weak signals as well as against interference.

Chirp Offset Multiple Access (COMA)

Chirp Offset Multiple Access (COMA) is a technique that adds randomized, controlled offsets to CSS transmissions in order to reduce interference and allow the receiver to receive one of the transmissions. COMA adds offsets in two ways: by "wobbling" the center frequency of the transmission (up to +/-25%), and by adding a timing offset of up to one half of a symbol period.

LoRa has two potential ways to “spread out” transmissions in order to increase the likelihood of successful capture. The first technique involves spreading out transmit energy at the chip level. Given that LoRa chirps are not continuous frequency sweeps, but rather a series of discrete chirps, slightly shifting the frequencies of the chips should reduce their interference between them. Likewise, by shifting the chirps in frequency and/or time, it should be similarly possible to reduce the amount of interference between colliding packets and increase the likelihood of successful capture. Figures 2 and 3 show how frequency and time shift reduce overlap between transmissions and increase the likelihood of successful capture.

Frequency Hopping Spread Spectrum (FHSS)

QMesh also supports "slow" (i.e., per-packet) frequency-hopping spread spectrum. Spreading out transmissions across multiple channels provides an extra measure of robustness to QMesh communications.

Please see the GitHub Wiki for additional up-to-date information about QMesh.

Adobe Portable Document Format - 37.92 MB - 02/14/2021 at 16:01


RMHR NerdFest 2021 -- QMesh -- Dan Fay KG5VBY.pdf

Slides presented at RMHR NerdFest. Mostly a shortened version of the DCC 2020 deck, but does have a few updates.

Adobe Portable Document Format - 1.24 MB - 02/14/2021 at 15:58


TAPR DCC 2020 Paper.pdf

Slides presented at TAPR's Digital Communications Conference in September 2020. Contains a discussion of the experimental results that show that QMesh's synchronized flooded protocol can actually work.

Adobe Portable Document Format - 1.75 MB - 12/01/2020 at 03:41


TAPR DCC 2020 -- QMesh -- Dan Fay KG5VBY.pdf

Paper submitted to TAPR's Digital Communications Conference in September 2020. Contains a discussion of the experimental results that show that QMesh's synchronized flooded protocol can actually work.

Adobe Portable Document Format - 1.35 MB - 12/01/2020 at 03:39


Portable Network Graphics (PNG) - 7.60 kB - 07/18/2019 at 19:58


View all 15 files

  • 1 × Custom LoRa RF shield
  • 1 × STM32 NUCLEO-144 board
  • 1 × Antenna SMA-male antenna for the right frequency (433MHz, 915MHz, etc.)
  • 1 × USB power supply Need to supply at least 1A
  • 1 × Amateur radio license Technician or higher in the United States to use this semi-custom, experimental RF hardware setup.

View all 7 components

  • Android Walkie-Talkies

    Dan Fay07/01/2022 at 01:58 0 comments

    A few months ago, I discovered a plethora of Android phones that are designed to work as walkie-talikies (HT's): they have a loud speaker (1W-2W output), a good omnidirectional microphone, a separate, special Push-To-Talk (PTT) button. They also have a port where you can connect a headset, speaker, mic, etc., like a regular HT.

    These devices market themselves for use with Zello. Some of them are dirt cheap, and have pretty low-end features, but there are some higher-end ones with larger screens, faster processors, more RAM/storage, etc. Seeing this as a possible good voice "front end" for QMesh, I purchased one of the low-midrange ones, the Uniwa B8000 (

    It arrived at my house this week, and I decided to check it out. While the specs are fairly low-end (four 1.5GHz ARM Cortex-A53's, 1GB RAM, 8GB storage), it was more than enough to run the Codec2 Walkie Talkie app. The screen is good enough for the intended use, and the physical buttons seemed to work well. 

    Overall, this should work out well for running Codec2 Walkie Talkie and interfacing with a QMesh node over Wi-Fi or Bluetooth.

  • Inverting Bits to Avoid Interference

    Dan Fay08/03/2021 at 12:34 0 comments


    One (experimental) parameter I'm exploring for COMA (Chirp Offset Multiple Access) is to invert the data bits on some transmissions as a way to help keep keep LoRa chirps from overlapping and interfering with each other. The idea here is that LoRa is a chirped version of an m-ary FSK. Basically, it appears that the "breaks" in the chirps correspond to a tone in this m-ary FSK. Moving around the "breaks" in the chirps should thus move around the chips somewhat in space-time.


    There's no direct way for the receiver to know whether the bits have been inverted. Instead, the receiver attempts to decode both the inverted and non-inverted versions of the received packet. The correct one is then determined using the CRC. One issue I did encounter was that the Reed-Solomon decoder would fail on the "bad" packet, and produce a packet with all zeros. The CRC-8 algorithm used would treat this as correct, as the CRC algorithm would deliver a 0x00 value for an all-zeros packet. Since the CRC extracted from this all-zeros packet was 0x00, the CRC check would cause the packet to appear correct. Changing to the CRC-8 parameters to the ones used by CDMA2000 fixed this issue.


    Of course, to really know whether this helps to mitigate interference will require some thorough testing with multiple nodes, which will...take some time.

  • Walsh-Hadamard Codes

    Dan Fay08/03/2021 at 12:21 0 comments

    Up to this point, the various parameters that make up the COMA anti-interference mechanism were chosen randomly at every packet. To prevent overlap, the Mersenne Twister-based pseudo-random number generator was seeded with the address of the node. While this did a good job keeping nodes from interfering from each other, it's probabilistically possible that interfering/overlapping COMA parameters are selected.

    I started experimenting with instead choosing these parameters using Walsh-Hadamard Codes instead. These codes are used in many CDMA systems to find orthogonal pseudo-noise (PN) codes for the different users. Doing some very crude experimenting, it looks like they have a lot of potential as a way to choose the different anti-interference factors that QMesh uses (frequency offset, timing offset, channel, and inverted bits). 

    It does seem to do a pretty good job of selecting parameters that are fairly far apart from each other:


  • ESP32 Serial-to-Wifi Bridge Complete

    Dan Fay06/25/2021 at 18:40 0 comments

    To make administration (monitoring, firmware updates, etc.) easier for outdoor nodes in possibly-inaccessible locations, I decided to develop a "Wireless UART bridge" out of an ESP32. The basic idea is that the ESP32 connects to my home network over Wi-Fi and provides a socket server I can connect to. 

    It was fairly straightforward to do, and it looks like it works well. The source code for it (I did it in the Arduino IDE) is at

    In addition to making remote administration possible, the QMesh "serial client" seems to work a lot better communicating with the board over network sockets than it does over pyserial.

  • UART-Based "Remote" Firmware Update Completed

    Dan Fay06/20/2021 at 05:35 0 comments

    The ability to update a QMesh device's firmware over a UART, which will enable remote firmware update, is complete. I can now update the QMesh firmware over a UART, including both a "wired" USB-to-UART adapter as well as over an HC-05 Bluetooth-to-UART module. Next, I'm working on setting up an ESP32 board to provide a Wi-Fi-to-UART bridge that will allow me to communicate with QMesh boards via a sockets interface.

    Some things I learned along the way:

    1. Using gzip to compress firmware updates is fairly straightforward to do, so long as you have a POSIX-y stuff available like malloc() and various file operators. If you have all of this, working on a gzip'ed file isn't much different from working on a regular file. Do note, however, that zlib uses a significant amount of both RAM and ROM. As a result, I only have zlib built into the bootloader.
    2. Mbed OS makes it fairly easy to implement a bootloader. Since I decided to make a bootloader that was just the QMesh app with some functionality removed, it was fairly large, totaling 150-200KB. I have plenty of program flash, so this isn't a big deal, but it might be if I try to fit everything inside of a smaller device.
    3. You can use the watchdog timer to implement a fallback capability for a botched firmware update. Basically, set the watchdog timer to some relatively short time period that's long enough to allow the main firmware to boot up and disable or pet the watchdog. If the firmware update is botched and it doesn't boot, the watchdog timer will reboot the board back into the bootloader, where a golden firmware image can be loaded instead.
    4. Static analyzers are interesting and fairly useful. The easiest one to do is to just enable clang-tidy in Mbed Studio. It definitely forced me to clean up my C++ code. I also set up codechecker (which runs clang-tidy and the Clang Static Analyzer) as well as Facebook Infer. While these are more cumbersome to use, Facebook Infer did some deeper analysis of my code and managed to find some code where I wasn't closing files that I had already opened.

    Hopefully, once I get the remote firmware stuff done, I'll have multiple solar-powered QMesh nodes that I can access and manage over Wi-Fi. This will make testing and improving the protocol much easier!

  • Protobufs Done, KISS Support Added

    Dan Fay05/22/2021 at 03:56 0 comments

    Finished implementing protobufs as the serialization protocol to be used throughout the system, including over the serial port and in the log and configuration files on the NOR flash.

    In the process of implementing protobufs, I realized that I also needed a framing format to "wrap" the protobufs in. I originally considered SLIP's framing, and then realized that the KISS protocol was an easier-to-parse superset of SLIP, with the bonus of being directly supported by many amateur radio applications.

    As a result, my journey through protobufs has made QMesh a KISS-compatible TNC. This means that, using e.g. an HC-05 (HC-05 info/usage) UART-to-Bluetooth bridge, APRSDroid now works with QMesh. Codec2 Walkie Talkie (Github repo here) should also work with it, but I've been focusing on performance tweaks/enhancements to QMesh before giving it a try. Note that I also added in the ability to fragment KISS frames across multiple QMesh frames if the KISS frames are larger than a single QMesh frame.

    After burning up an STM32 board and an HC-05 board trying to update an outdoor QMesh node, I've been working on developing a wireless, ESP32-based remote administration of the outdoor nodes. Basically, the ESP32 would connect to my Wi-Fi network, and allow me a network-based method to access the UART for testing and remotely updating the firmware (I'm also working on a bootloader to update the "main" firmware).

  • Moving to protobufs

    Dan Fay12/27/2020 at 17:40 0 comments

    I'm currently replacing the JSON-based serial communications setup with Google Protobufs. So far, it seems to be a win: simpler coding, more compact representation, and pretty easy to use.

  • Frequency Hopping Added

    Dan Fay12/01/2020 at 03:52 0 comments

    I decided to play around with the CAD feature on the SX126X a bit, and developed a simple scanning-based frequency hopping scheme. As a result, QMesh can now do a hybrid spread-spectrum scheme (CSS+FHSS).

    Some things I learned along the way:

    1. The CAD has a lot of false positives. Doing a "double CAD" helps reduce this issue. Basically, do a CAD, and if it comes back positive, do a CAD+Rx for the final receive.

    2. The CAD starts the oscillator "fresh". This means that, with a TCXO, the CAD will not start until the programmed TCXO stabilization delay completes. I may consider cutting the stabilization time for CADs, as a bunch of correlators just trying to detect some chirps probably doesn't need the TCXO's full accuracy.

  • TAPR DCC Submission and New Results

    Dan Fay08/16/2020 at 14:35 0 comments

    Just submitted a paper about QMesh to TAPR DCC ( 

    As part of this paper, I tested the packet receive rate (PRR) when two and three nodes are retransmitting. When I use Reed-Solomon-Viterbi (RSV) forward error correction, I'm seeing 99% PRR's. I believe that these results are real, since when I turn off FEC, I instead get 93% (for two retransmitting nodes) and 90% (for three retransmitting nodes). Moreover, this setup is a worst-case scenario where the antennas are right next to each other and are separated by a quarter wavelength. Real-world situations should be less challenging than this, so this is a very good sign.

    At this point, *knocks on wood* the underlying protocol is sufficiently reliable that it's ready to handle streaming voice.

  • Testing Results

    Dan Fay03/06/2020 at 03:25 0 comments

    It looks like the basic principle of QMesh -- that a synchronized, flooded mesh network can achieve a decently-high packet receive rate via the capture effect -- is valid.

    If I place two relay nodes right next to each other, I can get a packet receive rate (PRR) of around 80-100% when the received signals are strong. For marginal signal strengths, the collisions seem to effectively raise the noise floor.

View all 11 project logs

  • 1

    Once the hardware design stabilizes, I'll have better information here. Right now, however, I'm rapidly changing the design of the RF shield.

View all instructions

Enjoy this project?



Ryan Kahn wrote 06/15/2023 at 21:57 point

QQ: how many clients and individual voice transmissions do you estimate can be handled by the system, and how does that scale with the number of nodes?

  Are you sure? yes | no

scoutg wrote 04/16/2022 at 03:39 point

Any progress as of late, the GitHub seems like it's active but I haven't seen anything here

  Are you sure? yes | no

Dan Fay wrote 06/14/2022 at 21:33 point

Yes! What I've been working on is integrating the QMesh network layer with the latest version of Codec2 Walkie-Talkie ( It's an Android app that encodes codec2 voice frames as KISS frames that can be handled by any KISS-compatible TNC. I'm working on using an ESP32 running Espressif's AT firmware to provide a bridge between the TCP/IP socket Wi-Fi interface that Codec2 Walkie Talkie uses and the UART interface that the QMesh devices used.

Once I get it up and running, I'll put up a new post here.

  Are you sure? yes | no

ee334 wrote 12/01/2020 at 17:19 point

Why voice? I need only digital. for example NNTP

  Are you sure? yes | no

Dan Fay wrote 12/27/2020 at 17:38 point

QMesh's waveform/protocol design makes it well-suited for latency-sensitive streaming applications like voice. Its rapid self-healing is useful here for similar reasons. It's also what makes QMesh unique vs. all of the other countless LoRa-based mesh networking protocols being developed out there.

When I've done special event communications (mainly distance running races), PTT voice is a lot easier to use than text.

  Are you sure? yes | no

Per-Oskar O wrote 03/21/2020 at 12:08 point

Congratulations to the successful experiment. Do you think it would be possible to use this to make a lora based walkie talkie? I've been looking into the possibility make a cheap and dirty walkie-talkie using a esp32 and ESPnow. But alas, neither my codingskills nor my electronicsskills are good enough for that project. 

  Are you sure? yes | no

Hsingai Tigris Altaica wrote 05/18/2020 at 03:38 point

Oskar you should look into the WiPhone

  Are you sure? yes | no

Dan Fay wrote 12/01/2020 at 03:44 point

The ultimate goal is some sort of walkie-talkie, perhaps based off of the M17 Project's hardware. In the meantime, however, the goal is to make analog "bridge" repeaters so that people can interface with QMesh using existing FM walkie-talkies like Baofengs.

  Are you sure? yes | no

Dan Fay wrote 07/17/2019 at 12:17 point

Yes, there definitely has been! Please see the project's Github page ( for updates.

  Are you sure? yes | no

Dave wrote 05/28/2019 at 21:18 point

Any progress to report on this effort?  Interested.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates