Select Page

Iridium Satellite Communication with Arduino

When deploying a microcontroller-based IoT solution or just doing experiments gathering data in the field, you often need a way to get the data back to your home base or to the cloud somehow. You’ve probably used WiFi, maybe dabbled with cellular solutions, but what about satellite communication? Sometimes satellite is the only option if you are in a remote area. Transmitting data via satellite has been possible for quite some time using a satellite modem like the RockBLOCK 9603 from Rock Seven Communications. I recently used this technology to build a solution for a customer, and it was SO FUN I wanted to write up something about it because there’s not very much info out there.

Here is the basic architecture of the solution. Data is transmitted to one of the 66 Iridium satellites and then downlinked to Rock Seven where it will then be sent over the Internet to your server via HTTP or even email. I’m using a Node-RED server as the destination endpoint.

One barrier is cost: the RockBLOCK 9603 costs $250, you’ll pay a monthly fee to keep the modem activated, and each message costs money. I’ll give more details on cost later, but if you have a mission critical system that needs to be monitored or controlled in a remote area, it can be easy to justify the costs. From a development perspective, the good news is that there is an excellent Arduino library called IridiumSBD that makes it very easy to send Iridium short burst data messages (full documentation here). You will also want to keep in mind that messages need to be short (few hundred bytes at most) and that sending a message can take several minutes. We’re not streaming video with this stuff.


After I learned this technology, I decided to make a simple development board to make it easier to deal with the wiring and make it more portable. The RockBLOCK modem has a 10-pin Molex “PicoBlade” connector, but it was fairly easy to solder to the board. I used a SAMD21 microcontroller (the SparkFun SAMD21 mini breakout board is a great choice) because it is more powerful than a simple Arduino Uno and it has more hardware serial interfaces. I need one serial interface for a GPS module and one for the satellite modem. You might want one for serial communication to some other device, too! It’s good to have lots of hardware serial interfaces. I have grown tired of using software serial, as it can introduce problems and I don’t have time for that.

The board also has an Adafruit GPS breakout board for plug-n-play GPS. I just put female headers on the board so I can redeploy the SAMD21 and GPS boards in other solutions someday. I also added an OLED display for output and a few LEDs, along with some buttons and a pot for input.

Rock Seven Account

To use the modem, it has to be registered with Rock Seven Communications. You have to pay a monthly line rental fee to keep it active on the network, and you have to buy credits that are used up as you send messages. Rock Seven is a British company, so all the prices are in GBP. Line rental costs £12 per month (click for conversion to U.S. dollars), and 100 credits costs £13. It costs one credit for every 50 bytes in a message. A message sent from a modem can be up to 340 bytes, and that would cost you 7 credits. As a system designer, I was highly motivated to keep my messages under 50 bytes!

You also use your Rock Seven account to tell Rock Seven what to do with the messages when they get them from the Iridium constellation. This is called a “delivery group”. I’m having Rock Seven do an HTTP POST to my Node-RED server. Note that there doesn’t seem to be any security; I can’t specify a username and password for my server, so “security by obscurity” is the simple approach. That’s why I’m not showing you my AWS server name in this image. All the info you need about the integration between Rock Seven and your application can be found here.

Communication Experiments

For my experimentation I use an external antenna but the default configuration is to use a built-in patch antenna which works fine. You will need a clear view of the sky — satellite communication typically won’t work indoors (although I have successfully sent messages from indoors very near a window). For power, I’m using a portable 5V supply.

My Arduino program just lets you input a number from 1-100 using a potentiometer to show how data can be transmitted. I’m also transmitting the GPS coordinates and the time of the sensor reading. Keep in mind that sending a message can take several minutes. The IridiumSBD library can call a callback function you specify so you can do things during the long transmission time, like control an LED and print output. Once you tell the library to send a message, it will keep trying to contact an Iridium satellite, as it may take a while for one of the 66 satellites to fly over your area. The default timeout in the library is 5 minutes. If my program fails to send the message in 5 minutes, the red LED comes on. If successful, the green LED is lit.

The payload of my message to the satellite is 16 bytes: 6 bytes for the timestamp (year, month, day, hour, minute, second), 4 bytes each for latitude and longitude, and two bytes for the data value. The Rock Seven management console lets you see the messages received by Rock Seven and forwarded to your destination server. The payload is sent as hexadecimal, so my 16 byte message is actually sent as 32 HEX characters to my server.

Processing Messages

After receiving the data over the Internet from Rock Seven, you can do whatever you want with it. I am processing the messages with a Node-RED server and displaying the received value on a web dashboard.

Along with the payload, Rock Seven delivers metadata, including the time at which the satellite received the message. I compare this to the time that the microncrontroller started sending the message so we know how long the process really took. In this case, it took 3 minutes and 48 seconds to successfully transmit to a satellite. The data also provides an approximate GPS location where it thinks your modem is. This is the CEP parameter in the metadata. This can be off by several kilometers, so if you are interested in actual location, you will need to use a real GPS module, like I am.

Here is the JavaScript code in the function node “parse data”. The decoded data is passed along to the dashboard display widgets.

var data =;

var p = 0;
var year = parseInt(data.substr(p,2), 16);
p += 2;
var month = parseInt(data.substr(p,2), 16);
p += 2;
var day = parseInt(data.substr(p,2), 16);
p += 2;
var hour = parseInt(data.substr(p,2), 16);
p += 2;
var minute = parseInt(data.substr(p,2), 16);
p += 2;
var second = parseInt(data.substr(p,2), 16);
p += 2;

var latString = data.substr(p,8);
var lat = parseInt(latString, 16);
// can be negative, so check for sign bit
if (lat & 0x80000000) {
    lat = lat - 0x100000000;
lat = lat / 10000000.0;
p += 8;

var lonString = data.substr(p,8);
var lon = parseInt(lonString, 16);
// can be negative, so check for sign bit
if (lon & 0x80000000) {
    lon = lon - 0x100000000;
lon = lon / 10000000.0;
p += 8;

var valueString = data.substr(p,4);
var value = parseInt(valueString, 16);

// time that the message was created and ready to send
var msgTime = new Date(year+2000, month-1, day, hour, minute, second);
msg.payload.message_time = msgTime;

// time that the message was actually successfully transmitted to satellite
var txTime =  new Date("20" + msg.payload.transmit_time);
msg.payload.transmit_time = txTime;

var txMilliseconds = txTime.getTime() - msgTime.getTime();

var txMinutes = Math.floor(txMilliseconds / 60000);
var txSeconds = (txMilliseconds % 60000) / 1000;
var txTimeString = txMinutes + ":" + txSeconds;
msg.payload.txTime_ms = txMilliseconds;
msg.payload.txTime = txTimeString;

msg.payload.decoded_data = {
   year: year,
   month: month,
   day: day,
   hour: hour,
   minute: minute,
   second: second,
   lat: lat,
   lon: lon,
   value: value
return msg;


GitHub repo with the code used in this project and the board design files

RockBLOCK 9603 modem documentation

GitHub repo for IridiumSBD library

Detailed documentation for IridiumSBD library

Going Further

There are some things I haven’t tried yet. A satellite modem can actually receive messages from your server via Rock Seven. I also think it is important to design a security solution. A shared secret between the modem and Internet application may be sufficient, but deploying secrets to IoT devices has its own complexity. I could send a security token to the modem securely because sending a message to a modem requires Rock Seven credentials. The modem could then present the security token in each message, and the endpoint processor could require it. I think this would be reasonably secure, but the security token would take up valuable bytes in each message.


Using a Rock Seven Iridium modem is a lot of fun and it can be an important part of an IoT solution when you don’t have other connectivity options. It’s not cheap, you need a clear view of the sky, and it can take several minutes to send a short message. But if you can design a solution within those constraints like I have, it’s completely viable. And special thanks to Mikal Hart for writing the awesome Iridium SBD library!

Arduino Audio Hacker Realtime Voice Changer

Here’s another fun but simple project using the Audio Hacker shield for Arduino. This is a realtime voice changer, which is an improvement upon the original voice changer example I provided with the Audio Hacker library.

This project uses a technique called granular synthesis to change the pitch of the input. Granular synthesis is rather complex, but it involves dividing the sample up into small fragments called “grains”. When playing back a sample, if we want the pitch higher, we play the grain at a higher speed, but we play it over and over again until it takes the same amount of time as the grain played at original speed. Likewise, to lower pitch, we play each grain at a slower speed, but move onto the next grain sooner so that the overall sample has the same duration.

This realtime voice changer only lowers pitch. Raising pitch would require a lag to record something and play snippits of it faster. Lowering the pitch is accomplished by recording the input and then simultaneously playing it slower. That is, the “play head” moves slower than the “record head”. Occasionally, the play head needs to skip ahead over some of the input and catch up to the record head. This way, the playback takes the same amount of time as the recording, making it seem realtime. It is realtime, we are just playing only part of what was input (slowly) and skipping the rest so that the overall time is the same.

The example is in the Audio Hacker Library examples folder so you can load it into the Arduino IDE with

File->Examples->Audio Hacker->RealtimeVoiceChanger

As always, get the Audio Hacker library from GitHub:

Arduino Audio Hacker Realtime Reverser

Here’s a fun project using the Audio Hacker shield for Arduino – a realtime audio reverser! This program records the audio input to the Audio Hacker’s memory but plays it back in reverse. The “play head” jumps ahead a bit, then plays recorded audio backwards, then jumps ahead to the next snippet, plays it backward, and so on. There is a bit of lag between the input signal and reversed output, but this is unavoidable: we have to record something before playing it backward. There’s no way to play something backward in perfect realtime. This audio snippets are about a half-second long, so the lag is pretty small.

It’s great to hook up to a TV signal and just reverse all the audio. Using this effect, spoken English sounds like Russian to me!


The example is in the Audio Hacker Library examples folder so you can load it into the Arduino IDE with

File->Examples->Audio Hacker->RealtimeReverser

As always, get the Audio Hacker library from GitHub:


Arduino Shield for CAT M1 and NB-IoT Modems

LTE CAT M1 (sometimes called LTE-M) and NB-IoT are both exciting new cellular technologies targeting IoT applications. While there are many modems being built with this new technology, there are not many choices for hobbyists. There are a few Arduino shields and breakout boards based on the SIMCOM SIM7000 modem, and a few with u-blox SARA modems.

Nimbelink is a cellular technology provider that takes the unique approach of offering a variety of different modems as interchangeable modules. No matter what the actual cellular modem is, the 20-pin Nimbelink Skywire modules with the familiar “XBEE” footprint all have the same pinout and electrical characteristics. The interchangeable nature of these modem modules allows product designers to future-proof their products, allowing new modules to be plugged in later. Another huge benefit is that the Nimbelink modules are already certified on their respective cellular networks (Verizon, AT&T, etc.) so you don’t have to.

Nimbelink has a development kit for use by product developers, but it’s rather expensive. I wanted to try out a Nimbelink CAT M1 modem without the dev kit, and since there are so many hobbyists using Arduinos out there, I wanted to provide a nice Arduino library for the modem. I chose the Nimbelink module based on the Sequans Monarch CAT M1 modem and got to work designing an Arduino shield to hold it.

Skywire Shield with Nimbelink Skywire module with Sequans Monarch CAT M1 modem

Hardware Design

The shield is quite simple, and really just provides an appropriate switch-mode power supply for the modem. The input power for the shield can be 5-12V, and provides a stable 3.8V at up to 1.7A to the modem. I used a TI TPS5402 for this. Jumper settings allow you to connect the modem’s UART to the Arduino hardware UART on pins 0 and 1 or to software serial on digital pins 2 and 3. I typically use software serial at 19200 so that I can use the Arduino serial interface for debugging. The buttons labeled “Func 1” and “Func 2” are connected to digital pins 4 and 5.

Software Interface

What about actually using the modem in software? Every modem has its own AT command set, and although there are similarities, a developer must know the details of the modem they are using. Luckily, the excellent TinyGSM Arduino library provides an Arduino Client interface for a variety of underlying modems. The Arduino Client interface is widely used by networking libraries to abstract away the underlying hardware. That is, when you code to the Client API, it doesn’t matter if the hardware underneath is an Ethernet shield, an ESP8266, a cellular modem, or whatever. Examples of libraries that code to this interface and therefore work with many kinds of hardware are ArduinoHttpClient for web clients and PubSubClient for MQTT clients. Even HTTPS and MQTT over TLS work! Security is important.

TinyGSM did not have an implementation for the Sequans modem, so I wrote one. It’s in my forked repo of TinyGSM (it has not been merged into the parent yet). To use the TinyGSM examples with the Nimbelink module with a Sequans modem, simply add this to your source code:


I also bought a Nimbelink module with the Quectel BG96 CAT M1 and NB-IoT modem on it. Luckily, TinyGSM already has an Client interface implementation for this modem. After plugging it into my shield and simply changing my HTTP and MQTT test clients to use this modem, everything just worked! That is, I was able to change the modem to a different manufacturer (and completely different AT command set) and make my code work with a one line change:


Skywire Shield with Nimbelink Skywire module with Quectel BG96 CAT M1/NB-IoT modem

So the lesson here is that with thoughtful design of software and hardware abstractions, one can achieve a lot of flexibility.

  • the Arduino Client interface defines a standard software API for network clients so that an implementation for any hardware can be written and expose its functions through this same interface
  • useful libraries like ArduinoHttpClient and PubSubClient code to this interface so that HTTP and MQTT are easily used with lots of hardware
  • and finally, Nimbelink has defined a standard Skywire module pinout so that it’s easy to swap out the hardware

This is all getting pretty easy, right?! I hope you agree.

I haven’t decided if I’ll make this shield available as a product because there has to be some real demand for that to be worth it. Let me know if you are interested in an easy way to get started with modern cellular technology using Nimbelink modules.

Schematic and board design files at GitHub: skywire-cat-m1-modem-shield
Arduino library for controlling the Sequans Monarch and Quectel BG96 modems: TinyGSM

Building an IVR with Twilio and Node-RED

Node-RED Library page: node-red-contrib-twilio-ivr

Project source code at GitHub: node-red-contrib-twilio-ivr

Twilio is fast becoming an important part of Internet’s communication infrastructure. What is Twilio? Twilio is a developer platform that allows you to add capabilities like voice, video, and messaging to your applications. This project uses the Twilio Programmable Voice APIs to bridge the gap between the telephone system and the Internet. It connects the mysterious, traditionally closed world of telephony with the open Internet. If you like building things on the Internet, now you can integrate with those things using the phone system, whether it be via voice calls, text messages, or even wireless devices with Twilio SIM cards. Like many API services, Twilio has a pay-as-you-go model, so I’ve been able to explore all this great functionality for very little money. In this project I will show you how you can build an IVR (Interactive Voice Response) system with Twilio and one of my favorite technologies, Node-RED. And I have a great example IVR that you can call yourself to explore!

Twilio + Node-RED = POWER!

In case you don’t know what an IVR is, it’s that thing when you call a customer service phone number and are presented with a menu system that helps you (or tries to). A caller interacts with the IVR by pressing keypad numbers and speaking commands. To start using Twilio in this way, you need to buy a phone number from Twilio for your callers to call. As a developer, you present “content” to the caller by using a Twilio markup language called TwiML which instructs Twilio how to present your IVR content to whomever calls your number. It’s really just like building a web server that returns HTML that is presented by a web browser. But in this case, we build an HTTP server that returns TwiML to Twilio which audibly presents that content to the caller. The caller’s phone is the browser! Twilio handles the job of connecting telephone callers to your HTTP server.

You can build a TwiML server using any web server technology, like PHP on an Apache server, etc. I chose to use Node-RED because I use it for lots of IoT processing flows. Most importantly, by building a library of nodes specifically designed for building a Twilio IVR, I (and now you) can build sophisticated IVRs by dragging and dropping nodes into a Node-RED flow. And since Node-RED has many libraries available for integration into just about anything (including a way to invoke Twilio APIs), your IVR can integrate with databases, other servers, social media, and anything else in Internet-land. I think Node-RED is the perfect tool for creating IVR flows.

Note that Twilio has their own product like this called Twilio Studio, but I built my system before they announced their product. Besides, mine is free and I think you can build much larger and more sophisticated IVRs with my solution :)

Node-RED IVR Components

Here is an overview of the main components of an IVR built with this system. Instructions for setting all this up for your own IVR is later in this article.

Twilio IVR Library

To make all this easy, I built a library of Node-RED nodes that create TwiML for the response. Some nodes are simple and have a one-to-one correspondence with TwiML markup, like the “play” node for playing MP3 files and the “say” node for speaking text. These nodes create TwiML using the <Play> and <Say> tags. Other nodes are for higher-level IVR concepts, like the “menu” node. It lets you create a menu that is spoken to the caller and arranges for the caller input to route to the correct part of the IVR flow.

Twilio IVR Core Flow

Much of the heavy lifting of the IVR is done by a predefined Node-RED flow called Twilio IVR Core. Your IVR will use this Node-RED flow and you won’t need to change it. The main part is the Universal Router. Twilio always invokes the Universal Router HTTP endpoint (which is /router) which figures out which route to follow through the IVR based on the user’s input or routing information specified by the previously invoked route (more on this later). Routes are a key concept in the IVR. The are a particular path that the caller is on, and have names like /main-menu or /account or /agent.

Example IVR: “Customer Service Hell”

The easiest way to demonstrate the many features you can implement in an IVR was to build a great example. The Node-RED flow “Customer Service Hell” is an example that uses many features. It is designed to frustrate any caller with a maze of menu choices and frustrating interactions. You can call this IVR at (612) 999-2812. (if you are international, dial +1-612-999-2812). Please note that it costs us a small amount of money every time you call, so don’t abuse it, but by all means use it to learn and maybe get a good laugh. Here is a huge image of this Node-RED flow that you can use to follow along. Be sure to look at the route called /nightmare-hold. If you are planning on implementing a Twilio IVR, I suggest you start with this flow by import it into your Node-RED implementation (more on setup later).

Twilio Configuration

Of course you have to tell Twilio to invoke your Node-RED server: in the Twilio console, specify your server endpoints in the configuration for your Twilio phone number. Here is an example of how this should look. Node-RED can be configured to protect HTTP endpoints with username/password credentials, so you need to specify these as well. Note that the error webhook does not need the credentials (and doesn’t work if you provide them).

How It All Works

Twilio invokes the IVR over HTTP to get the TwiML response for Twilio to present to the caller. A response to the caller may request input from the caller in the form of a menu or may prompt the caller for other input like an account number or invite the caller to speak a command. The Universal Router controls the flow of a call. First, it finds the caller’s session or creates a new session if the call is new. The session keeps track of anything we want to remember since we last returned TwiML to Twilio. (It’s just like a web server session where we keep track of info before responding to the browser.)

Next, the router determines which route to invoke. For example, if a menu was presented to the caller during the last invocation, then the caller’s input must be matched to the correct route to invoke. The session automatically keeps track of any menu that was presented so that the caller’s input can be used to route correctly. For example, if the last menu told the caller to press “1” for accounting, then if they pressed “1”, we route to the /accounting route. Another important thing to note is that if a call is new, the router will invoke a route called /begin, so your IVR needs to define this route as the starting point for a new call.

Next, the router actually invokes the desired call route. Each route in the IVR begins with a Node-RED HTTP endpoint. Even though Twilio does not invoke these HTTP endpoints directly (Twilio always invokes /router), the Universal Router invokes routes via HTTP. The responsibility of a route is to return the TwiML that is to be returned to Twilio. The outer layer of TwiML (the <Response> and </Response> tags) are taken care of by the router.

Examples of Common Route Patterns

Presenting a Menu

A common pattern in an IVR is to present the caller with a menu of choices. Here is the part of the IVR that implements the /main-menu route, as well as the starting point for the whole IVR, /begin. You can see that if a call is new it says a welcome message before connecting to the /main-menu route.

The pattern for a menu-based route is to start with a gather-begin node which tells Twilio that we want user input. See the Twilio documentation for the <Gather> TwiML element. You can configure the gather-begin node to expect DTMF (touch tones) or speech input, or both. You can also provide speech hints to help Twilio recognize speech input. For example if you are presenting a menu that allows the caller to say “representative”, then specify the string “representative” as a speech hint in the gather-begin node to help Twilio recognize the caller’s utterance.

The menu node creates the TwiML that reads a menu to the caller. This node provides lots of flexibility. For each menu item, the item can be spoken (“Say”) or an audio file can be played (“Play”). The word “For” can be prepended to the name of the item if that makes sense. The caller can press a number to choose the item (“Press” option) or can press or say it (“Press or Say” option). You can also specify speech that can be used to choose the item. For example in the second item below, the caller can press 2, say “two” or say “account” to choose the item. Finally, each item specifies which route in the IVR to invoke if chosen. The Universal Router will inspect the input from the caller (whether spoken or entered) and determine the route to invoke.

Also, don’t forget to use a gather-end node before returning to the router. This adds the ending tag </Gather> to the TwiML response.

Using the set-route node

Although menus are a common pattern, not every route in an IVR presents the caller with a menu of choices. Sometimes you want to prompt the caller for input (like an account number) or prompt them to make a recording of some kind. An example of this is the /account route in the Customer Service Hell IVR. Click the image below to see this route. Note that we still need the gather-begin and gather-end nodes since we are asking for input.

Dynamically building a menu

One more pattern that you may find a use for is the ability to build a menu dynamically instead of specifying it all in a single menu node. Below is a particularly insidious part of our Customer Service Hell IVR. After prompting the caller for a 4-digit PIN, it transposes the digits that they entered and tells the caller that their PIN is wrong. The IVR keeps track of the number of times the caller has failed (in the call session), and after multiple failures we add a menu choice for the caller and invite them to “shock the monkey” by pressing ‘*’. (Relax, PETA, it’s just a sound effect.) This ability to dynamically build the “content” returned to the caller is very powerful.

System Setup

Here are the steps to take if you want to actually build and IVR. I suggest you get the example “Customer Service Hell” IVR working first so you can see how everything works together.

First it is assumed you have Node-RED installed and running. If you don’t, then get started at the Node-RED website.

In your Node-RED installation, install the Twilio IVR library:

npm install node-red-contrib-twilio-ivr

Next, you’ll need to add a Node-RED flow with the Twilio IVR Core. You can get this flow from the Node-RED Library entry for this project. In Node-RED, use the Import option on the menu and import from the clipboard. Past the Twilio IVR Core content from the clipboard.

To run the Customer Service Hell IVR, create another flow in Node-RED and import from the clipboard just as you did for the Twilio IVR Core. Past the content of the IVR flow into Node-RED. This IVR relies on audio files which are retrieved by Twilio when you tell Twilio to play an audio file using a “play” or “menu” node. You can download the audio files from this link and you will need to host them on a server. You will specify the base URL of all the audio files in the configuration of your Node-RED “play” and “menu” nodes.

Don’t forget to configure your Twilio account to point your phone number at your Node-RED server (this is described earlier in the article).

Final Notes

You will need to make one change in the Twilio IVR Core flow: the Universal Route contains an HTTP node called “invoke route” which makes the HTTP call to the route endpoints. If your server is protected with a username/password you will need to specify them. You will also need to tell this node whether it needs to use SSL/TLS when connecting.