Get Started with Data Comms and the Raspberry Pi Pico

The Raspberry Pi Pico would be a great Internet of Things device but for one thing: it has no Internet connectivity. Fortunately, we can fix that with a Super SIM and an add-on cellular module such as Waveshare's Pico SIM7080.

In our tutorial Get Started with SMS Commands and the Raspberry Pi Pico, we combined the Pico, the Waveshare Pico SIM7080 cellular module board, an MCP9808 temperature sensor, and a four-digit, seven-segment LED display into a prototype Internet of Things (IoT) development device.

This device uses Super SIM's SMS Commands API to receive commands over-the-air and, when instructed, to send back information. This works very well for device-to-user communications routed through your cloud, but what if you want the device to be able to reach out to other Internet resources? For that you need a data connection and the ability to make HTTP requests — GET, POST, PUT, etc. — and parse the remote server's response.

This tutorial will take you through the process of adding exactly this functionality to your IoT application.


1. Set up the hardware and software

If you have already completed Get Started with SMS Commands and the Raspberry Pi Pico, you're ready to jump straight to Step 2, below. If not, run through the SMS Commands tutorial's first four steps, which cover the crucial hardware and software setup that you will need to undertake in order to complete this tutorial.

Head there now and then come back here when you've completed Step 4.


2. Prepare the initial Python code

Throughout this tutorial, you'll be pasting code from this page into a text editor, first the code below and then additional functions as you progress through the guide. At each stage, you'll copy the current code from your editor and paste it across to the Pico. The code included here entirely replaces hat from the previous tutorial in the series.

At this point, you should have a Pico with MicroPython installed. It should be fitted to the Waveshare board and connected to your computer by USB cable. You should have fired up Minicom (Mac/Linux) or PuTTY (Windows) and have the MicroPython REPL prompt, >>>. Hit Ctrl-C to exit the running program, if you don't see the prompt.

As a reminder, hit Ctrl-E to enter MicroPython's 'paste mode', paste in code copied from your text editor, and then hit Ctrl-D to start running it.

Alternatively, if you're a Mac or Linux user, you can use the pyboard.py tool to beam it over for you and relay the output to your terminal — details here.

Here's the base code listing. Copy it — click on the copy icon in the top right corner of the listing; it'll appear as you mouse over the code — and paste it into your text editor.

You can find the a complete listing of the code, including all subsequent additions, at our public GitHub repo.

Don't send it over to the Pico just yet — you'll need to complete Step 3 first.

To save scrolling, click here to jump to the rest of the tutorial.

This is the basis of the code you'll work on through the remainder of the guide. What does it do? Much of it is the code you worked on last time, so let's focus on the additions.

To communicate with Internet resources, the modem needs to establish a data connection through the cellular network, and then an HTTP connection to the target server. Lastly, it creates and sends an HTTP request to that server, and reads back the response.

The function open_data_conn() handles the first part, by sending the AT command CNACT=0,1. The 0 is the 'Packet Data Protocol (PDP) context', essentially one of a number of IP channels the modem provides. The 1 is the instruction to enable the data connection.

When the data connection is up, the code calls start_session() to open an HTTP connection to a specific server, which is passed in as an argument. The server is set using AT+SHCONF="URL","<SERVER_DOMAIN>" and the connection then opened with AT+SHCONN.

Requests are made through the function issue_request(). It calls start_session() and then sets up the request: we build the header on the modem (and keep it for future use) and then send AT+SHREQ= with the path to a resource and the value 1 as parameters — the 1 indicates it is a GET request.

The response returned by the modem contains information about the data returned by the server, which is stored on the modem. The code uses this information to get the HTTP status code — to check the request was successful — and the response's length. If the latter is non-zero, the code sends AT+SHREAD= to retrieve that many bytes from the modem's cache. issue_request() extracts any JSON in the response and returns it.

All this is triggered by the receipt of an SMS command, GET, which causes the function process_command_get() to be called. This function calls open_data_conn() and then issue_request(). It parses the received data as JSON and displays the value of a certain field on the LED display using code you worked on in the previous tutorial.

When the code runs, it turns off the Pico's built-in LED. The LED will flash rapidly five times if there was a problem booting the modem.

The LED is turned on when the device is attached to the network. If the LED is flashing slowly, that means it has not yet attached. Please be patient; it will attach shortly.


3. Set up a data source

Before you can run the code, you need to set up the data that will be retrieved. You're going to use Beeceptor as a proxy for the Internet resource your IoT device will be communicating with. In a real-world application, you would sign up to use a specific service and access that, but Beeceptor makes a very handy stand-in. Let's set it up to receive HTTP GET requests from the device.

  1. In a web browser tab, go to Beeceptor .

  2. Enter an endpoint name in the large text field and click Create Endpoint:

  3. On the screen that appears next, click on the upper of the two clipboard icons to copy the endpoint URL:

  4. Keep the tab open.

  5. Jump back to your text editor and locate the process_command_get() function in the Python code. Paste the endpoint URL you got from step 3, in place of YOUR_BEECEPTOR_URL .

  6. Save the file and then transfer it over to the Pico.

  7. Hop back to Beeceptor and click on Mocking Rules (0) in the page shown above and then click Create New Rule .

  8. In the third field, add api/v1/status right after the / that's already there.

  9. Under Response Body, paste the following JSON, your test API's sample output:

  1. The panel should look like this:

  2. Click Save Rule and then close the Mocking Rules panel by clicking the X in the top right corner.

  3. Again, keep the tab open.


4. Try out the code

Switch over to Minicom (or PuTTY). When you see the Listening for commands... message, open a separate terminal tab or window and enter the following command using curl:

You'll need to replace the sections in angle brackets (< and >) with your own information, just as you did last time. Your SIM's SID — or friendly name if you've set one — and specify the auth token generated from your KORE Build API client. To know more about API clients, we've created a quick guide for you here.

This command uses the Super SIM API's SMS Commands API to send a machine-to-machine message to the Pico. The --payload parameter tells the API what the body of the SMS should be: it's whatever comes after the equals sign. In this case, that's GET, the command to which we want the Pico to respond.

The listen() function in your Python code keeps an ear open for incoming SMS messages, which are signalled by the module transmitting a string that includes the characters +CMTI:. If it appears, the code sends a new AT command to the modem to get the message (AT+CMGR) and then awaits a response. When the response comes, the code processes it and extracts the GET — which tells the device to make an HTTP request of that type.

You'll see all this in your terminal window:

You should also see 1234 displayed on the LED — one part of the data received from the API you connected to!

The code listed above will output state messages, but if you want to see the full flow of AT command requests and their responses, you'll need to add a handful of extra lines. To do so, drop in this function:

and add this line right before the final return in the function read_buffer():

The output will now look like this:


5. Post data from the device

Reaching out across the Internet and requesting information is only half of the story: you also want to push data out to the cloud. Our Pico-based IoT demo is well prepared to be a source of information: it includes a temperature sensor which you can read and transmit the result by SMS if you send the command TMP by text message.

Not all data receivers accept input by SMS, however. Most will accept POST requests, though, so let's add the code to the application to support that. There are a number of changes and additions to make.

Add the following code right below the def set_request_header(): function definition:

Replace the existing issue_request() function with this code:

Replace the process_command_get() function with all of the following code:

When you've done that, copy and paste your Beeceptor endpoint URL into the places marked YOUR_BEECEPTOR_URL.

Finally, add the following lines to the listen() function, right below the code that looks for a GET command:

Finally, transfer the updated program to the Pico.


6. Set up a data sink

You can't send data without somewhere to post it, so set that up now. Once more, Beeceptor comes to our assistance: you're going to add a mocking rule as a stand-in for an application server that will take in the data the device posts and return a status message.

  1. Switch to the web browser tab showing Beeceptor .

  2. Click on Mocking Rules (1) and then Create New Rule .

  3. Under Method , select POST .

  4. In the third field, add api/v1/logs right after the / that's already there.

  5. Under Response Body, paste the following JSON:

  1. The panel should look like this:

  2. Click Save Rule and then close the Mocking Rules panel by clicking the X in the top right corner.

  3. Again, keep the tab open.


7. Try out the code — part deux

Switch over to Minicom (or PuTTY). When you see the Listening for commands... message, open a separate terminal tab or window and enter the following command, filling in your details where necessary:

This time, you'll see all this in your terminal window:

You should also see 4567 displayed on the LED — one part of the data received bacl from the API. Speaking of the API, what did it see? Take a look at Beeceptor. It records the receipt of a POST request to /api/v1/logs, and if you click on the entry in the table, then the JSON icon ({:}) above the Request body panel, you'll see the celsius temperature as received data:


8. Next steps

You now have a Raspberry Pi Pico-based IoT device that can send and receive data across the Internet. In this demo, you've used test APIs for getting and posting data, and triggered both by manually sending an SMS command. Why not adapt the code not only to make use of different APIs — perhaps one you might make use of in your production IoT device — but also to do so automatically, at a time appropriate to the application? For example, you might include a regular weather forecast update, or pull in the output from a Slack channel. You might post device status data to your own cloud.

Wherever you take your Raspberry Pi Pico-based IoT device next, we can't wait to see what you build!


Note

When this was written, of course: the Pico W has been release since then.

Last updated

Was this helpful?