M
MeshTRX
All articles

The button that pretended to work

6 min read

Three complaints from the group in a row turned out to be about the same thing, though they looked different: "changing the channel for everyone does not work", "the lock icon on messages is confusing", "the radio received it but the app shows nothing". None of them was a radio bug. All three lived in the gap where the device and the person understand the situation differently — and all three would have sailed through any automated test.

The button that pretended to work

"Could not change the channel on all nodes. Is this function somehow tied to encryption?" — followed by a second message: "same here, it does not work for me either".

It was not tied to encryption. The firmware had a check that looks like plain common sense: if the requested channel equals the current one, there is nothing to do, so return. For a single radio that is true. But the "change for everyone" command is addressed to the neighbours, not to yourself, and it has to go on the air regardless of where you happen to be standing.

The interface finished the job. In the app, picking a channel moves your own radio immediately. So the person first picks the channel — the radio moves — and then presses "change for everyone" while already sitting on that channel. Exactly the state in which the firmware decided there was nothing to do. The command never went out. Not sometimes, not in poor signal — never.

The instructive part is that each half is sensible on its own. The firmware avoided pointless work. The app avoided making the user confirm the same choice twice. The bug appeared at the seam, and a seam is precisely what you cannot see while looking at one side of it.

Now a matching channel cancels only your own hop, not the broadcast, and the button in the app opens its own channel list marking where the radio currently stands.

The lock that read backwards

When a key is set, the radio ignores unencrypted traffic by default. That is deliberate: a single radio without a key defeats encryption entirely while everyone still believes the conversation is private. Sometimes you do want the open air, though, so a switch appeared — and with it a marker on messages that arrived unencrypted. The marker was an open padlock.

"The little lock on messages from open chats should be changed — it looks closed, as if the message were encrypted."

In a message list an icon the height of a line is a few pixels. An open padlock differs from a closed one by the shackle, and at that size the shackle is simply invisible. Worse, a padlock next to a message reads as a badge of quality: there is a lock, so it must be protected. The icon said the exact opposite of what was meant.

We replaced it with words: "unencrypted". Eleven characters instead of an icon, and nothing left to guess.

A false alarm I set off myself

The third story is about the test, not the product, which is why it belongs here.

Checking the new marker, I sent an unencrypted message from one board to another. The receive log showed the packet arrived, parsed, text intact. In the app: nothing. Half an hour went into hunting for a break in the chain from radio to Bluetooth to phone — packet length, the new trailing byte, the parsing on the app side.

There was no break. In the test I had addressed the message to FFFF, out of habit from other protocols where that is the broadcast address. In MeshTRX broadcast is zeros. The radio honestly checked: not for me and not for everyone, and dropped it. Everything worked correctly; the test was wrong.

The lesson is dull but worth repeating: before looking for a bug in the system, check the tool you are looking with.

Why the bench misses this

The project has a bench: two boards, a phone, some sixty checks. It catches a great deal and saves hours. It would not have found a single one of the three stories above.

The reason is the same in all three cases. The bench reaches the firmware by its own routes — through test console commands, not through the app. The routes are similar but not identical. Once that cost us dearly: for several days text sent from a phone went on the air unencrypted, because sending text has two paths — through a queue and directly — the bench used the queue, encryption had been added only there, and the phone sent directly. People reported that the link stopped working with a key set, while the bench was all green.

The bench answers the question "does what we intended work". It does not answer "did we intend the right thing", and certainly not "will a person understand it".

What does help

A real device within reach. Since a genuine phone joined the checks — one whose screen can be captured and read as words — bugs of this class take minutes to find: the message either carries the "unencrypted" caption or it does not, and there is nothing left to argue about.

And the group. "It does not work" cannot be checked; "could not change the channel for everyone, one of my nodes has encryption, the other is open" can be, and that one took twenty minutes including the release. Half the value of a message from the group is not the complaint but the detail around it.

What the radio can do today is in the documentation. What is in progress and what is deferred is on the project page. Observations from the air are always welcome in the Telegram group.