Back when we ran High Frontier: The Drake Objective in 2024 I used a Raspberry Pi to turn Telegram messages into the "sad computer voice" prevalent in sci-fi movies.
This was a surprise hit in the game but there were a few problems...
- One of our players is very hard of hearing - so unable to engage with much pure audio content
- It's really easy to miss random out of the blue messages from a walkie-talkie unless you're actively engaging with it in the moment. Doubly so if somebody is talking to you in person at the time. We were asked to repeat messages all the time.
- It used voice activation on the walkie-talkie doing the transmitting which wasn't totally reliable and it would occasionally clip the start of messages despite making a "beep" before talking
All of these were related to me just bashing it together in the fortnight before the event while also working on other stuff.
I started on the update then quickly decided to drop the transcription and constrain the project to just fixing the acknowledged problems before doing something that could suck a load of time and effort.
The frequency range of audio transmitted by PMR walkie-talkies is limited and it can be hard for a person paying attention to distinguish speech sometimes so I surmised automated transcription would do more poorly, even the cloud based 'AI' version of it.
My
original version had a single Telegram bot that was part of a group chat with a simple command interface. The event GMs could chat in the group normally but anything prefixed with the command "/say" would be turned into speech and sent to the PMR. The bot was just one of Telegram's example bots and Microsoft's Azure speech to text example smushed together, but it worked well. With no WiFi on-site I relied on a 4G dongle I had kicking around that "just worked" in the Raspberry Pi.
Everything looks like a nail
As Telegram worked well for the GM group and I had a load of Android tablets with LTE modems from other events, the obvious way to give the players a text channel to interact with MU|TH|UR through was just another group chat in Telegram.
While I had physical carry cases for the tablets the interface couldn't be skinned to be "diegetic" with the time I had. They just had some Android Tablets and Telegram but this is not an immersion destroying thing and to do anything else would have been a big pile of work. A custom Android app or Progressive Web App is something that's in my wish list for the future but wasn't realistic for this event.
So with two chat groups that created a requirement for two bots if I wanted to "separate control messages from user messages" which is a good design pattern even though this isn't exactly a highly sensitive application.
Two bots, one script
All the main Telegram bot Python examples are for one bot, which is fair enough. I am not a Python programmer and quickly found that the 'blocking but async event driven' default way of writing bots with the common library is inimical to running two bots at once. Well not exactly but you then can't use the 'convenience' methods and have to engage with the Telegram API at a lower level. Several people have asked for a multiple bot example but the library maintainer's response was of the vaguely dismissive "read the docs/now draw the rest of the owl" variety. Likewise lots of other responses kicking around on a search have pseudocode partial examples that just didn't run even when I tried to fill in the blanks.

So, sledgehammer to crack a nut time and I quickly taught myself how to use the multiprocessing library in Python. Wasteful but it's only a Telegram bot talking to webhooks on a system with one job to do. The two bots talk to each other via a multiprocessor Queue: player messages are passed to the GM bot and GM messages intended for distribution are passed to the player bot. Not unexpectedly there seems to be a lot of subtlety about whether certain things are thread safe in Python when using the async.io method the Telegram library uses itself. I think using the multiprocessing library and its own inter-process communication method insulates me from that but like I say I'm no Python programmer, my usual playground is C++ and FreeRTOS.
While I was doing this I added a tiny bit more subtlety to the control. Messages prefixed "/say" would go to the player group chat as well as being broadcast as speech. Messages prefixed "/send" would just go to the group chat. This was a speculative feature but ended up being useful.
Push to talk
There's no standard for connecting things to PMR walkie talkies but they do share a kind of common pattern. I used the same circuit as I did for MU|TH|UR v1 to match impedance/levels and added in a relay that grounds the 'tip' connection when you want to transmit.
From this it was simply a case of triggering a GPIO pin before 'speaking' to latch the relay and then release it afterwards.
The end result meant triggering the PMR was now much more reliable and receiving PMRs received the messages more consistently.
When 3G is no longer enough
With not long to go I had it all working and plugged in the dongle to do a bit of a final test. Which then stubbornly refused to work. I spent far too long trying to troubleshoot this, cursing bitrot and version creep in Raspbian but when it occurred to me to try the dongle in my laptop it didn't work there either.
Despite being listed as "4G" in some specs that show up when you search it turns out the dongle I'd been using for ages was actually some variant of 3G that the big switchoff made non-functional. Well it did come from e-waste, which is where it'll go back to. I've got an otherwise usable phone with the same problem.
This prompted a quick panic order of a
Waveshare SIM7600G-H dongle from PiHut. There's a certain clunkiness to it as it's intended for industrial/hobbyist use rather than consumers but that at least means no weird gimped branded up firmware and at least some technical information on their Wiki.
Getting it working was definitely not a consumer job, sending AT commands over a USB serial port and then having to reconfigure Network Manager in Raspbian to not set the DNS servers: another frustrating half day rabbithole but with it configured it connects quickly and reliably. The external antenna probably helps.
As it's also possible to use this via an onboard UART I may see if I can use this from embedded hardware like an ESP32 but that's a project for another time.
Sticking it in a box
The new dongle and external antenna meant there was no way the original enclosure where I repurposed an external mains socket box was going to work any more.
So I settled on using a 9l "Really Useful Box" and 3D printed a mounting plate for it all. There's also a fascia with a status LED, a button to shutdown/restart it and some places to cable-tie the cables in place to stop them getting yanked out of the internals. The box isn't properly waterproof long term but will stand use outdoors for the couple of days at a time that we need it.
All the external leads are hugely long. It's powered from a 12v lead-acid battery which needs tucking away somewhere safe and the PMR used to transmit needs some height to help with coverage and ends up tied to a pole high up.
Sometimes the wrong thing is the right thing
Once I'd reached this point of it working reliably and was thinking about how we'd use it in the game it popped into my head we could use What3Words to send the player group directions.
I'm no fan of this attempt to do corporate capture of something there should be an open standard for (there is one but it is a tad clunky) and the failings of their word choice algorithm for safety critical situations have been explored extensively by people smarter than me. However in this case, smacking the W3W app onto the tablets the players were already carrying and then sending locations into the chat as a clickable link that opened the app was a fair choice and we'd used it a little in the past.
Until the event I didn't even get to test it, and when we started using it the "/send" option where MU|TH|UR didn't read out the words was needed because otherwise it was a tide of unintelligible blah when we forgot.
Much as I have reservations about W3W it worked mostly OK for this, only once sending them in completely the wrong direction which was probably a GM mistake, and I think we'll be using this again next year.
Want to build your own?
I've now backed this all up and once I've had time to write an "installer" of sorts I'll update the previous
instructions on GitHub.