Texting your Raspberry Pi
I’ve been working on a side project recently and had an idea for a feature that would involve sending a text message from my phone to the Raspberry Pi. My first thought was setting up some Dynamic DNS and forwarding port 80 to the Pi. But, I didn’t feel great exposing the little guy outside of my firewall. After a quick chat with a developer friend of mine, he suggested I try to leverage AWS.
After looking into it more, it seemed reasonable to have Twilio send a request via a Webhook that would be hosted on AWS API Gateway. The Gateway would then take bits of the request like the message body and put it into the AWS Simple Queue Service (SQS). From there, I could have my Raspberry Pi poll the queue every so often and look for any messages.
I had never used AWS before, so it took me a while to understand the Console, the terms, and how to configure the services. Given that, I’m sure you can find some better ways to do things. Please share them in the comments if you do.
We’ll start by signing up or logging in to AWS. Once you are logged in, you’ll need to create a new queue in SQS. Go into your Console and choose the SQS service.
For the queue configuration, I chose FIFO and then mostly stuck with the defaults. Content-Based Deduplication is important however, unless you want to send a MessageDeduplicationId with every message.
Now we’ll go and set up the Webhook that Twilio will call. Before doing that though, we’ll have to set up a Role for the API Gateway to execute under, so we’ll take a quick detour to the IAM and create a new role. The new role should have the policy AmazonSQSFullAccess so that it can add new messages to the queue.
In addition to the policy, we also need to set up a Trust Relationship so that the role can be called from the API Gateway.
Now we’re ready to configure the API. Head over to the API Gateway service in your Console. The first thing to do is create a new API by clicking the blue ‘Create API’ button. Give it a name and say that it is a New API.
APIs describe a set of resources which each can support multiple methods. I found it easier to debug GET methods as I could easily simulate them in the browser and inspect all the query parameters. You can use POST if you prefer, but the rest of the tutorial will assume GET.
Depending on if you want to have a structured set of APIs, you can create more resources. For this project, I just made any GET of the root resource trigger the API. Here’s the configuration page after I was done.
The first redacted string is the path of your SQS queue. You can find this on your SQS Console page after selecting the queue. It will look something like “/{stringOfNumbers}/{nameOfQueue}.fifo”. Make sure to append “?Action=SendMessage” to the end of the Path override as this is what will actually send the message. The second redacted string is your Execution role which is the ARN we created in the IAM a few steps ago.
Now you have an API that is almost ready to deploy!
We need to grab a few things from the query string that Twilio will send us and transform them a bit to forward along to the SQS. First click on Method Request. Here we’ll specify what data Twilio is passing in the query parameters.
Go back up a level to the diagram and this time click on Integration Request. In this configuration page, we’ll tell the gateway that we want to take some query parameters and rename the keys to map to what the SQS expects.
If you needed to do any more complicated transformations besides using the path, the query parameters, or the request body, you can use AWS Lambda. I think if I want to use the From field, I’ll have to switch to that in the future.
Now we can deploy our API from the Action menu.
When deploying, you’ll be asked for a stage. This is something like beta or prod or whatever meaningful name you want. After the API is deployed you’ll get an Invoke URL. Finally we have what Twilio needs!
Go to Twilio and create a new number. Make sure it has the capability to receive SMS. We haven’t come this far just to fail here! Once you have a number, click it and scroll down to the Messaging section.
We want to configure it with Webhooks/TwiML and set the Webhook for when a message comes in to HTTP GET the Invoke URL from our Gateway API.
We haven’t written a line of code yet and we’ve got text messages getting placed in a queue, all we have to do now is retrieve them. There are a number of ways to do this, but I chose to use Node.js and the aws-sdk npm package. There are tons of in-depth tutorials on how to configure your Raspberry Pi to use Node.js, so I won’t try to cover that here. Once you have it all set up, here is the code I use to retrieve and delete a message from the queue.
var aws = require("aws-sdk/global");
var sqssdk = require("aws-sdk/clients/sqs");
var sqs;var init = function () {
aws.config.loadFromPath("./awsConfig.json");
sqs = new sqssdk({apiVersion: "2012-11-05"})
};var checkForMessages = function () {
var params = {
AttributeNames: [
"All"
],
MaxNumberOfMessages: 10,
MessageAttributeNames: [
"All"
],
QueueUrl: queueUrl,
VisibilityTimeout: 123,
WaitTimeSeconds: 1
};sqs.receiveMessage(params, function(err, data) {
if (err) {
return;
} if (data) {
(data.Messages || []).forEach(function (message) {
console.log(message.Body);
var deleteParams = {
ReceiptHandle: message.ReceiptHandle,
QueueUrl: queueUrl
}; sqs.deleteMessage(deleteParams, function (err, data) {
if (err) {
console.log(err);
}
});
});
}
});
};
That’s it! Please let me know if this helped you or if you ran into any issues.