This week was spent implementing a command for submitting SMS messages. Given that I already had encoding working, it shouldn't have taken more than a day. Unfortunately, things don't always go as planned. Sometimes in more stupid, embarrassing ways than others.
There are four characters in this story:
atd, a program that accepts high
level commands, converts them to a form that the modem can understand, and can
interpret responses from the modem;
atc, a CLI frontend for
program which lets me act like the modem, and send responses to
modemtalk, a small program that lets me talk to the modem directly, through
stdin and stdout.
When I started testing the submit command, I found that it would work when I
atsim to send
atd what I expected the modem to send, but it wouldn't
work when I tested against the actual modem. At one level, this was expected; I
concluded that my mental model of the modem's behavior was doing was incorrect.
However, when I tried to use
modemtalk to see what the modem was actually
doing, it was in-line with my mental modem. Immediately after, I ran
again, tested it, and it worked properly. I killed it, started it, and again, it
When the modem starts up, it spits out a bunch of information, even before
commands are sent to it. My initial suspicion was that this output was somehow
corrupting the internal state of
atd, causing issues. I ended up rewriting the
code that sends configuration commands to the modem, hoping that would solve the
issue. It did not. I'm happy I did this however, because the code is just nicer
Looking deeper, the core problem seemed to be the following:
atd expects to
get a prompt from the modem, after which it could input the SMS message and send
it on it's merry way. Even
atd was sending the
AT+CMGS command to the modem,
the prompt was not arriving.
To help debug, I started adding a bunch of print statements to get the content
of the modem input and output buffers, and found another issue: I expected
responses from the modem to be terminated by
\r\n, but instead they were only
\n. This was a massive, vigorously waved red flag that
something was wrong with the TTY configuration (modem communication happens
through a TTY interface).
atd use the exact same function to
configure the TTY, so to confirm that they were doing the same thing, I went
line by line the functions, making sure that all the flags being set were the
same. They indeed were, which left me even more confused. I reverted to my
hypothesis that somehow the initial output that the modem spit out was somehow
atd as to make the carriage returns disappear from the buffers, and
somehow losing the SMS prompt. To fix the state without restarting the modem, I
modemtalk, and input a character so that the modem would stop
listening for an SMS message, and go back to listening for commands. Then I
atd, and it would work flawlessly. Of course if I restarted the
atd would be broken again.
This morning, getting desperate, I did a more thorough check of the TTY
configuration functions again, and found my problem. While the configuration
function was the same in both
atd, I wasn't actually calling
the function in
atd. This meant that if I ran
atd on modem startup, the TTY
would be misconfigured until running
modemtalk, which actually configured it
properly. In hindsight, this should have been the first thing I checked, but
didn't even consider because all was well until I added SMS. More specifically,
it broke because the kernel would wait until encountering a newline to send it
atd. The prompt doesn't end with a newline, so it would just never send.
It seems to work well now, so I can say that
atd finally has the ability
to send texts!
The next steps will be saving received texts, and the user interface.