Skip to content

Instantly share code, notes, and snippets.

@lucasbrigida
Forked from tymarbut/README.md
Created October 14, 2024 06:18
Show Gist options
  • Save lucasbrigida/c89934775effbd0d763e6aae0040a3ac to your computer and use it in GitHub Desktop.
Save lucasbrigida/c89934775effbd0d763e6aae0040a3ac to your computer and use it in GitHub Desktop.
Baofeng (or other radio) data TX/RX for Raspberry Pi

This flow (and associated circuits/hardware) is designed to allow Node-RED to pass messages via radio waves in the same way as it passes messages via MQTT, using commonly-available, inexpensive handheld radios and the Raspberry Pi. The flow has been tested using Baofeng, Wouxun, and Quansheng handheld ham radios. In short, the goal is to allow packet-like transmissions between Node-RED systems over miles, while keeping the hardware costs down (or free for those hams who have surplus Baofengs hanging around). This could be used for a backup to MQTT when the Wifi is unreliable, or simply as a long-distance and network-agnostic message channel.

Prerequisites: Software

First, we need PulseAudio to make and receive sounds with our USB soundcard:

sudo apt-get install pulseaudio -y

Next, you need a piece of software called Minimodem to encode and decode a data stream in analog audio. For Raspbian, it is installed like so:

sudo apt-get install minimodem

For other operating systems, consult the Minimodem webpage.

Prerequisites: Hardware

  • Raspberry Pi
  • Relay board
  • Resistors (10k ohm and 470 ohm to build attenuators)
  • USB soundcard (cheapest available will do)
  • 2x 3.5mm TRS ("headphone") connectors
  • Baofeng/Kenwood 2-pin audio connector (e.g. from Baofeng earbud)

Next, you need to setup your hardware. This involves two discrete circuits: The PTT control and the audio passthrough/attenuation. Forgive this brief digression away from Node-RED and into hardware.

PTT Control

You may know that the Baofeng/Kenwood pinout provides for PTT, by grounding the Mic- to the Spkr-. This can become very tricky, because when you plug your radio directly into other circuitry, such as a USB sound card, there is often enough conductivity between the two "grounds" to trigger the PTT (all the time). So, we'll need a dedicated relay to fully isolate and manage the Spkr-/Mic- connection.

Here is a diagram of (just) the simple relay circuit, using a prefab Pi relay board.

Note that my relay boards want 0=on and 1=off from the GPIO node. If yours is 0=off and 1=on, you will need to modify the "Start PTT" and "Stop PTT" nodes.

Audio Attenuation Done Wrong

Part 2 of the hardware is comprised of two different audio attenuators for passing audio between the radio and the USB soundcard. This is especially complicated by the fact that the radio and the USB soundcard (and standard computer audio devices in general) are built on two different standards of audio hardware in terms of speaker and microphone impedance. So frankly, you might come up with something better than what I did. After trying several different audio attenuation circuit types (L, T, pi, etc.), I concluded that the resistors from Spkr+ and the system ground were ineffective and unnecessary so I removed them. So, this made the circuit very simple, but there is probably a better way.

I took the 2-pin Kenwood connector from a cheapo Baofeng earbud/mic unit (~$3) and the 3.5mm connectors from a standard computer-to-speaker audio cable.

So, here is a diagram of my poorly-constructed but nevertheless functional audio attenuators:

With this audio attenuation setup, I run the Pi on 100% volume and the radio on about 50% volume.

Now, notes on the flow:

By default, it's connected to an MQTT broker on 127.0.0.1 with no username and password, so you'll probably want to set that up.

Minimodem has several types of modulation available, and of course everything you need to know about Minimodem can be found by calling for its help in the terminal:

minimodem -h

In this flow, RTTY is selected because I thought it would provide the highest reliability of any of the available modes, but note that if you're sending a lot of data, or low-latency data, you might want to switch to a faster mode such as Bell 300 or even Bell 1200. Minimodem will do arbitrary Bell speeds, also (e.g Bell 12). I used confidence level of "5" for this flow (like a digital squelch), but this can be changed to make it more or less susceptible to errors. However, if you choose another modulation or confidence value, note that you'll need to make the change to the call to Minimodem in the "restart" node, the "change" node, and the "manual start" node. Yeah, this could have come from a single config variable of some sort, but that seemed unnecessary because mode=RTTY and confidence=5 have been working great for me.

By default, the "Start TX" node injects at start, but I suggest turning this feature off while actually working on the flow. Multiple instances of Minimodem running in the Exec node will cause multiple outputs. I've put a short rate limiter in there to deal with this, but still, you probably don't want multiple instances running anyway.

There are two delays in this flow. I've set them up to be optimal for infrequent RTTY transmissions where PL tones may be in use. However, depending on your application, you may need to increase or decrease particularly the delay between when the PTT is "pressed" and when the data starts.

Finally, note that this whole flow actually works very nicely as a subflow. By replacing the MQTT input/output nodes with subflow input/output nodes inside a subflow, it creates a MQTT-like subflow that transmits out messages handed to it or injects/returns radio-received messages.

Using the Flow

If you've looked at it, it's probably fairly self-explanatory, but to be clear: As set up by default, the flow will receive strings passed to it on the MQTT topic "dataTX", and then transmit them out over the radio frequency your radio is tuned to. Then, it will publish strings received by the radio on MQTT topic "dataRX". It will also transmit text defined in the inject node, and return decoded text in the debug tab. Finally, the MQTT nodes can be replaced with any other node which will send/receive data, such as e-mail, twitter, watch file, GPIO, websocket, etc.

Quick note about radio frequencies

For ham radio operators, remember that your transmissions should include your callsign (at least once every 10 mins), so you'll either need to make sure that's part of your TX data, or add an interveining template node to add your callsign to the message.

For non-licensed operators, first, be aware of where you are allowed to transmit. Sadly, most of the radio spectrum has been regulated, and there is really nowhere where you can transmit anything you want at any power level. However, there are a few frequencies where license-free operation is permitted. You will have to check the laws for your radio region (for license-free frequeencies and for automated transmissions), but it's likely that some of the following frequencies may be available to you:

UHF: FRS or GMRS frequencies. Channel 21 is easy to remember: 462.700mhz. Some other GMRS channels are not round numbers and may require some settings tweaking on your radio to tune. UHF will be best for high-reliability, shorter-distance message passing, such as any routes <5 miles, particularly for transmitters/receivers in motion.

VHF: MURS band: 151.820, 151.880, 151.940, 154.570, and 154.600mhz. These are shared with businesses, so listen for traffic before you pick one. VHF will be better for passing messages over a dozen miles or more. With good antennas, good locations, and good receivers (e.g. Quansheng rather than Baofeng radios), I would expect an upward limit of around 100 miles.

Further usages

It seems that this same circuitry and software could just as well be used on HF bands with many HF rigs - at least the Yaesu ft-857d or similar, which have a PTT circuit and can use the same USB sound card for an audio interface. This would, of course, also allow for SSB operation on VHF/UHF frequencies which I would also expect to significantly increase range for the same power level.

Good luck and 73,

-W7TYY

[{"id":"3e7ccb7e.c18334","type":"mqtt-broker","z":"d63557ec.29caa8","broker":"localhost","port":"1883","clientid":"","usetls":false,"verifyservercert":true,"compatmode":true,"keepalive":"15","cleansession":true,"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":""},{"id":"cb871bc5.3478e8","type":"inject","z":"d63557ec.29caa8","name":"manual TX text","topic":"","payload":"test 0 1 2 3 4 5 6 7 8 9","payloadType":"string","repeat":"","crontab":"","once":false,"x":112,"y":269,"wires":[["52b5b531.ad4a4c","402f3983.bfd0c8","a0ff08a5.5f00f8"]]},{"id":"98f06566.670f98","type":"exec","z":"d63557ec.29caa8","command":"echo ","addpay":true,"append":" | minimodem --tx rtty","useSpawn":false,"name":"minimodem tx","x":607,"y":266,"wires":[["84f3acf1.7b0c5","dcf7e3b3.23082","8429abe7.7bd658"],[],[]]},{"id":"8f1e0680.70e1f8","type":"exec","z":"d63557ec.29caa8","command":" ","addpay":true,"append":"","useSpawn":"","name":"minimodem rx","x":641,"y":578,"wires":[["3899d34f.c7662c","917ca8f8.6e8358","772a0a12.88d5f4"],[],[]]},{"id":"dcf7e3b3.23082","type":"delay","z":"d63557ec.29caa8","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":973,"y":583,"wires":[["c918d17.f36e73"]]},{"id":"a3811815.5c7ee8","type":"inject","z":"d63557ec.29caa8","name":"manual PTT on","topic":"","payload":"0","payloadType":"string","repeat":"","crontab":"","once":false,"x":223,"y":57,"wires":[["c438310f.3bc7d"]]},{"id":"c438310f.3bc7d","type":"rpi-gpio out","z":"d63557ec.29caa8","name":"GPIO40 - PTT (1 = off)","pin":"40","set":true,"level":"1","out":"out","x":1071,"y":44,"wires":[]},{"id":"cd0b9cbd.32f46","type":"inject","z":"d63557ec.29caa8","name":"manual PTT off","topic":"","payload":"1","payloadType":"string","repeat":"","crontab":"","once":false,"x":223,"y":92,"wires":[["c438310f.3bc7d"]]},{"id":"52b5b531.ad4a4c","type":"change","z":"d63557ec.29caa8","name":"start PTT","rules":[{"t":"set","p":"payload","to":"0"}],"action":"","property":"","from":"","to":"","reg":false,"x":835,"y":117,"wires":[["c438310f.3bc7d"]]},{"id":"402f3983.bfd0c8","type":"delay","z":"d63557ec.29caa8","name":"","pauseType":"delay","timeout":"1.2","timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":452,"y":265,"wires":[["98f06566.670f98"]]},{"id":"84f3acf1.7b0c5","type":"delay","z":"d63557ec.29caa8","name":"","pauseType":"delay","timeout":"0.2","timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":776,"y":211,"wires":[["a4129f4a.5bed6"]]},{"id":"a4129f4a.5bed6","type":"change","z":"d63557ec.29caa8","name":"stop PTT","rules":[{"t":"set","p":"payload","to":"1"}],"action":"","property":"","from":"","to":"","reg":false,"x":835,"y":152,"wires":[["c438310f.3bc7d"]]},{"id":"904f56e8.6fb0a8","type":"inject","z":"d63557ec.29caa8","name":"start RX","topic":"","payload":"minimodem --rx -c 5 --rx-one rtty","payloadType":"string","repeat":"","crontab":"","once":true,"x":344,"y":451,"wires":[["3899d34f.c7662c"]]},{"id":"c9157f03.36ea8","type":"mqtt in","z":"d63557ec.29caa8","name":"","topic":"dataTX","broker":"3e7ccb7e.c18334","x":97,"y":198,"wires":[["c4d0134e.3b2ff","402f3983.bfd0c8","52b5b531.ad4a4c","a0ff08a5.5f00f8"]]},{"id":"4cb9f941.b34608","type":"mqtt out","z":"d63557ec.29caa8","name":"","topic":"dataRX","qos":"1","retain":"","broker":"3e7ccb7e.c18334","x":1232,"y":473,"wires":[]},{"id":"c4d0134e.3b2ff","type":"debug","z":"d63557ec.29caa8","name":"incoming MQTT","active":false,"console":"false","complete":"payload","x":259,"y":147,"wires":[]},{"id":"867b5799.7984a8","type":"debug","z":"d63557ec.29caa8","name":"","active":false,"console":"false","complete":"payload","x":1247,"y":435,"wires":[]},{"id":"a0ff08a5.5f00f8","type":"template","z":"d63557ec.29caa8","name":"kill RX during TX","field":"payload","format":"handlebars","template":"echo tx","x":400,"y":351,"wires":[["3899d34f.c7662c","8f1e0680.70e1f8"]]},{"id":"c918d17.f36e73","type":"template","z":"d63557ec.29caa8","name":"restart","field":"payload","format":"handlebars","template":"minimodem --rx -c 5 --rx-one rtty","x":1097,"y":583,"wires":[["3899d34f.c7662c"]]},{"id":"3899d34f.c7662c","type":"rbe","z":"d63557ec.29caa8","name":"","func":"rbe","gap":"","x":663,"y":414,"wires":[["d812a0b3.27ed6"]]},{"id":"2b336afd.d4cc96","type":"inject","z":"d63557ec.29caa8","name":"kill RX","topic":"","payload":"echo tx","payloadType":"string","repeat":"","crontab":"","once":false,"x":344,"y":491,"wires":[["8f1e0680.70e1f8","3899d34f.c7662c"]]},{"id":"917ca8f8.6e8358","type":"function","z":"d63557ec.29caa8","name":"filter","func":"if(msg.payload === \"\" || msg.payload.substr(0,2) === \"tx\")\n{ return null;}\nelse {return msg;}","outputs":1,"noerr":0,"x":828,"y":584,"wires":[["dcf7e3b3.23082"]]},{"id":"772a0a12.88d5f4","type":"function","z":"d63557ec.29caa8","name":"trim","func":"msg.payload = msg.payload.trim();\nreturn msg;","outputs":1,"noerr":0,"x":928,"y":452,"wires":[["e5a4b7a6.1a5b48"]]},{"id":"d812a0b3.27ed6","type":"switch","z":"d63557ec.29caa8","name":"","property":"payload","rules":[{"t":"cont","v":"minimodem --rx -c 5 --rx-one rtty"},{"t":"cont","v":"echo tx"},{"t":"else"}],"checkall":"true","outputs":3,"x":785,"y":419,"wires":[["8f1e0680.70e1f8"],[],[]]},{"id":"e4849f19.1b7b6","type":"comment","z":"d63557ec.29caa8","name":"WARNING - disable inject on start when editing","info":"If you leave this node set to inject on start,\nit will start multiple instances of Minimodem\non deployment, causing duplicate messages.\nTurn off the \"inject on start\" while you're\nworking on this and other flows.","x":283,"y":415,"wires":[]},{"id":"8429abe7.7bd658","type":"debug","z":"d63557ec.29caa8","name":"TX results","active":false,"console":"false","complete":"payload","x":823,"y":264,"wires":[]},{"id":"e5a4b7a6.1a5b48","type":"delay","z":"d63557ec.29caa8","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":1066,"y":452,"wires":[["867b5799.7984a8","4cb9f941.b34608"]]}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment