AT Commands anno 2021

13 October 2021
# at# web# serial# nrf91

As a developer, I'm often amazed at how hard it can be to get started playing around with hardware, and by extension also IoT. If you plan to send any data through a cellular network, you're not very far off learning that AT commands are not something that stopped being relevant in the 1990s. I believe we can do better.

Boldly go where so many have gone before

AT commands are commands sent to a modem. The modem we'll be talking with is the one that communicates with the cellular network when you want to broadcast your IoT data back to your service. To do this, you need to have an understanding of how your modem works (they all have their quirks), and what commands to send, and in which order. If you search for your modem and "AT commands", you're very likely to end up with a PDF that is 231 pages long, where you bravely cmd+f your way searching for "IP" with 352 hits. Trust me, you are not the first person to do this.

This felt like it could be better, so after crafting a common library for AT commands in TypeScript, I added a glossary along with a free text search on our doc site.

Searching for AT commands

The plan was to make a non-exhaustive list of AT commands that you need to know about to handle standard use cases and help you keep your sanity when having to interact with them. The IP command (AT+CGPADDR) is a so-called SET command and is simple with no real arguments. This is not the usual case.

So you think you can AT

AT commands come with three main "types", being READ, SET and TEST. TEST is something that you usually don't play with, however, READ and SET is widely used, and not necessarily logically. SET (usually) has arguments and allows us to set the state on the device. Some arguments are numbers, and some are strings. Numbers usually represent some sort of ID or functionality as enums. Some enums start at 0. Some start at 1. As you might understand, this is not healthy for any growing developer.

However, this created a spark in my mind, as one of our slogans is "It doesn't have to be complicated". How about we make this... easy. Expanding the AT command library, I added metadata such as parameters, types, and validation. Madness some might say, but I was well beyond reason and wanted to prove this was possible.

Let's take AT#XSOCKET which is a non-native custom command for the nRF9160s Serial LTE modem application made by Nordic Semiconductors. It illustrates everything that is wrong with a command (sorry Nordic, we still love you). We have enumerations that configure the kind of socket operation you're trying to perform. We have enumerations that start at 1 for what kind of socket type you're planning to use. This leaves us with commands such as AT#XSOCKET=1,2,0 which needs interpretation to be used. I started tinkering with a UI that could give a description for all arguments, and make them configurable by the user.

Configuring a horrible AT command

Now you could understand AND configure the command at the same time. I also added the ability to copy it to be used later in your code or terminal leaving the mental jump between reading a PDF and code a thing of the past. I was done, or so I thought.

In comes WebKit

I was happy with my result. I had cleaned something dirty and I felt like I was giving back to the world so somebody after me didn't have to go through all my pain. Then, by pure chance, I popped in to Can I Use (where we frontend developers go to look to see if we can use future stuff or whether Safari is holding us back again). I wandered to the page called web serial. My mind was positively boiling at this point.

You crazy SOB you did it

I can do what now? I can connect to a serial port through my web browser and it's sort of already available? I was grinning, my journey was not done yet.

Plug in baby

Now, there's a myriad of reasons as to why allowing a browser to access your USB serial ports is a bad idea. Not only does it expose it to whatever code that might be running on the web page, but you could with the right commands, create irreversible damage on devices if you don't (or do) know what you're doing. However, it also introduces opportunity.

If your device exposes a modem through the serial port, we can actually perform AT commands on it directly from the browser. I created an ATSerial client in TypeScript (will be available through NPM soon™) which encapsulates a lot of the complexity that is needed when interacting with the Serial API.

After doing this, I incorporated the AT library directly into the ATClient, and the creation was complete. I could now send simple commands, let them be validated by the library (it does that too), and wait for the modem response and let everything be handled as a Promise.

AT library send command

As the glossary AT command docs in the documentation already had all the data I needed from the AT command library, I now could put it all together. Below is a gif that shows me searching for the socket AT command mentioned earlier, selecting the command, configuring it, and sending it directly to the connected Thingy:91 which is flashed with the serial_lte_modem firmware.

Full demo

I've won. mic drop

The future?

As I found this to be a good starting point, I deployed it to our live documentation page and the site is open for anyone to play with, along with a AT getting started. I still haven't published the TypeScript library, and there's a reason for that. The next iteration will include custom functions that have several new included features. The signature is the same, but the response is now a generic response based on the given command.

Send command signature

That means, that when sending a command such as:

const pdpStatus = await atSerial.sendCommand(common.LIST_PDP_STATUS());

You will actually have a fully interpreted and typed response from the device:

Typed response

As well as have typed attributes within the response based on the custom command:

Typed attributes

The custom commands will also contain parameters with actual documentation describing them as well as validating them on the fly using TypeScript:

Custom command signature

The ATClient is also doing an in-depth validation during the execution to ensure that the command is as valid as can be before it hits the device. This way you avoid getting the not so helpful ERROR response from the modem.

The plan is to keep tinkering with the library, adding missing AT commands as well as device configurations to further increase its usefulness. If you got any questions or are curious about more things, pop on by our slack channel and have a talk.

As always, happy coding :)