radiocli Home Blog GitHub

All posts

Say Your Name

The SDS150 speaks a line-oriented protocol over its serial port: one command per line ending in a carriage return, one reply per line, comma-separated fields, and the first field of a reply repeats the command that caused it. I’ve spent the last two days mapping what it will tell you, the screen, the state, the lists, the settings, and most of that is documented. The part I want to write about is the part that isn’t, and how I got at it, because this radio turns out to be willing to hand you a list of every command it knows, if you ask the right way. When I asked, exactly half of what came back was missing from Uniden’s manual.

A real command says its own name

Send the scanner three letters it doesn’t recognize, ZZZ, and it answers with a bare ERR and nothing else. Send it a command it does know but can’t act on right now, like HLD with no target, and it answers HLD,ERR: it says its own name first, then complains. Send a valid one and you get MDL,SDS150. The pattern is the whole trick. Anything that names itself in a reply exists, whatever it thinks of your arguments, and anything that comes back as a bare ERR does not.

That single behavior makes the command space enumerable. Every command on this radio is exactly three characters, the alphabet is the twenty-six letters plus digits, and unknown names answer immediately instead of timing out, so a sweep runs at the cost of a round trip rather than the cost of a timeout. Cheap enough to try all of them. Credit where it’s due: a protocol that will tell you what it contains, even if nobody at Uniden meant that as a feature, is a more honest protocol than most, and it’s the thing that made everything below possible.

I sent every three-letter name

AAA to ZZZ is 17,576 names. At about 139 milliseconds each that came to roughly 41 minutes of wire time, spread across a few hours of runs and recoveries. Seventy commands answered.

Of those seventy, thirty-five are in the specification and thirty-five are not. Two more exist that a letters-only sweep can never reach, because they end in a digit: the documented GW2 and the undocumented GS2. That puts the real total at seventy-two known commands, thirty-six documented and thirty-six not. Bottom line, the published specification describes exactly half of what this radio answers.

The commands are not scattered evenly. Ten of them start with G and ten with S, seven each with M and P, and six whole letters, B, I, O, X, Y and Z, have nothing behind them at all. That’s not random, it’s vocabulary: the firmware names commands after what they do, G for get, S for set and status, M for menu and model, P for push and power, so the empty letters are just the ones no verb starts with.

How do I know the sweep didn’t miss things? Thirty-five of the thirty-six documented commands are spellable in letters alone, and the sweep found all thirty-five. Zero false negatives against a known set that size is reasonable evidence that nothing answering from a normal scanning state slipped past. It says nothing about commands that only answer from some other mode, and four of the finds refuse with NG rather than ERR, which on the older radios in this lineage meant understood but not valid right now. So there may be a second population this sweep could only ever catch saying no.

Three names out of the seventeen thousand answered with pure silence: KAL, SUS and UDP. KAL is the documented keep-alive, whose entire job is to put traffic on the wire without expecting anything back, so silence there is correct. The fact that the sweep could tell “exists and says nothing” apart from “doesn’t exist” is what let the other two, both undocumented, register as real commands rather than dead air.

The dangerous end is also three letters

POF powers the radio off. No arguments, just the three letters, and it stays off until somebody walks over and presses the button. PRG drops it into a program mode that paints Remote Mode / Keypad Lock on the screen and has no exit anyone has found short of a power cycle. MSM switches the radio into USB mass storage, which does not join the serial port, it replaces it: the instant the command succeeds, the channel you would use to undo it is gone.

Those are the shape a blind sweep sends, three letters and no arguments, so a blacklist went in before the first run: twelve names lifted from the older radios’ vocabulary and guessed to be destructive, things that cleared memory, deleted a channel, formatted the card, entered the bootloader. I sent all twelve later, deliberately, and every one returned a bare ERR. None of them exist on this firmware. The blacklist protected nothing, and withholding them was still the right call, because the way you learn that a “clear all memory” command isn’t there is not by typing it and finding out.

My own side of this wasn’t clean either: POF did get sent blind once, before the blacklist existed, and it powered the radio off mid-sweep. The cause was a line-based reader desynchronizing. Scanner-info replies span many lines, and a reader that takes one line per command hands the leftover lines to the next probe, and the next, quietly attributing every reply to the wrong command until one of them lands on POF. The fix is a single principle: drain the port until it goes quiet, not until the first line arrives.

The prizes

Four undocumented commands earned their keep. GLG reports the current transmission in one line, frequency, modulation, and the system, department and channel names, where the documented route is parsing a whole scanner-info XML document for the same facts. Every field comes back empty when nothing is being received.

GS2 is the one that changed my mind about something I’d written down as settled. It returns the same screen GST does, but it echoes a caller-supplied tag straight back, and GST and STS both refuse an argument outright. Everywhere else in my protocol notes I stated flatly that nothing in a reply says which request produced it, which is true of every documented command and is why two programs sharing one serial port read each other’s answers and neither can tell. GS2 is the exception, and it’s the request identifier every other command needed. Whoever added it understood the problem and solved it cleanly.

PSI is the piece that makes live state practical. The push side of the protocol was documented, but the parameter that sets the push interval never was, which left it unusable. It turns out to be the first argument, and a bare call reads it back. Set to an interval of 1, the radio pushed 296,758 bytes in five seconds, around 60 KB per second of scanner-info documents, and PSI,0 shuts it off. That’s the whole difference between streaming the radio’s state and polling for it, which is the difference between a live meter that’s smooth and one that jitters.

ESN returns the radio’s serial number, no arguments, and it matches the serial recorded on the memory card character for character. It matters because MDL only ever gives you the model, so two SDS150s on one machine are otherwise identical over the wire, and the serial is the only thing that tells them apart from the radio’s own mouth rather than from a USB descriptor.

Guess the shape, not the ancestry

I wrote my guesses down before testing any of them, so the hit rate means something. The names I lifted from the older BCD-series radios this protocol descends from did badly: forty-seven of fifty-five drew a bare ERR, call it a hit rate around two in fifty. The names I guessed from the shape of this protocol did well: where the protocol pairs a polled command with a pushed twin, or a formatted reply with a separator-free twin, I guessed the missing partners and hit two of four. Guessing from what the machine resembles beat guessing from where it came from, and it wasn’t close.

If you take one thing from two days of this, take that. When you’re reverse-engineering something, the family tree tells you less than the thing sitting in front of you. The lineage explains why a command might have survived; the protocol’s own internal patterns tell you what it’s likely to be named. Bet on the rhyme, not the pedigree.

None of this is new to command protocols, either. The Hayes command set that shipped with the Smartmodem in 1981 was line-oriented and carriage-return-terminated, answered OK or ERROR, and from day one the commands in the book were only part of what the hardware would answer. Every modem maker bolted on its own extensions, and cellular modems to this day still speak AT with vendor-specific commands nobody outside the vendor has ever documented. A device protocol whose manual covers half of what the silicon answers is not a Uniden quirk. It’s the normal condition of the genre, and it has been for over forty years.

Ask a machine what it can do and it will tell you. The manual is only the half somebody got around to writing down.