In this previous post, I gave an overview of how I collect ambient data using a Raspberry Pi and a couple of Raspberry Pico Ws. In this post, I’m going to give more detail on how to set up the Pico to take readings on demand via the network.

Equipment

If you want to try this at home, you’ll need:

  • A Raspberry Pico W with MicroPython installed
  • A soldering iron and paraphernalia (or another way of connecting the sensor to the Pico W such as breadboard and jumpers)
  • A DHT-22 or BME280 sensor
  • A wifi network

Attaching a sensor to your Pico W

The MicroPython code I wrote has support for two sensors: the DHT-22 and the BME280. The DHT-22 measures temperature and humidity, while the BME280 additionally measures air pressure. Currently, my code only supports these sensors.

Pico W with BME280, connected via a breadboard and jumpers

A rather dusty Pico W with BME280 attached

A Pico W with DHT-22 soldered directly to the board

A Pico W with DHT-22 soldered directly to the board

There are plenty of guides to wiring up these sensors, so I suggest you consult one of them and then come back here. For example:

Important: You’ll need to install MicroPython as laid out in the guides. For the BME280, you’ll have to install the library, also described in the guide. For the DHT-22, it’s preinstalled when you install MicroPython. Actually accessing the sensors is taken care of by the code below so you don’t need to read that part of the guides.

Installing the code

Get the code from GitHub

Grab the repository from GitHub. You need to have the code on your machine before you can load it onto the Pico.

git clone https://github.com/johnheaven/pico-ambient-data-api.git

What the code does

If you add a settings.py file as described below, the code should just work - so this is just an overview in case you’re interested.

Most of the details are abstracted out into two objects: get_ambient_data and mini_server.

get_ambient_data is an object that yields readings one at a time. So it can be instantiated with a particular sensor type, and then it returns readings for that particular sensor. All the code is tucked away in a method within the class.

mini_server is also an object. It takes care of opening a connection and listening for requests. It tries its best to make a connection, trying out all the networks set in settings.py. If it fails miserably (i.e. all networks tried the specified number of times), it’s supposed to wait 10 minutes then restart the machine - which should start the whole process again. But I don’t think this actually works.

Routes are added to the mini_server object. Routes are the URIs that the mini server responds to, and the handlers are functions that you pass in as arguments. These have to be wrapped (similar to how a partial function works, but we don’t have functools in MicroPython so have to improvise) because we need to pass arguments, but we can’t call them at the time we pass them in.

The two standard routes are /data and /find. /data returns all the data and is actually all we really need.

There’s one route that is a little hidden: this is the 404 response. This is passed into the server object when it is instantiated in exactly the same way as the other routes.

Find your device’s UUID for settings.py

You need your device’s UUID so that you can enter settings that are specific to it. This is handy so that you can put all your settings for all your Picos in one file and always copy the whole repository onto all your Picos. The Pico will then use the settings that match its UUID.

The only way to find (the string representation of) the UUID at the moment is to use the REPL that starts in Thonny or VS Code when you attach the Pico, then type in the following:

import binascii, machine
binascii.b2a_base64(machine.unique_id()).decode('utf-8').strip('\n')

Copy that to your clipboard, then move on to the next step. Don’t forget that you’ll need to find the UUID for each Pico and enter it separately. (You can do this successively — of course each Pico doesn’t need to have all the settings for all the others, but it’s helpful that they don’t mind because then you don’t need to worry about overwriting settings on one Pico with those of the other.)

Enter settings for your network, sensor type, Pico ID etc.

Duplicate example.settings.py to settings.py, then change the settings appropriately. You do this by:

  1. Adding a dictionary for each Pico using the template in the example file
  2. At the bottom, add an item within global_settings dictionary, with the key being the UUID that you just copied from the REPL.

What the settings mean

  • sensor_type: the type of sensor attached to your Pico. Currently, only dht22 and bme280 are supported.
  • pico_id: a name for your sensor that shows up in the JSON file, so we can differentiate between the sensors. (Actually we can do that with the UUID too, but pico_id is a human-friendly identification.)
  • secrets: A list of dictionaries. Each dictionary is contains an SSID and password, which get tried successively to connect to the network. There’s no guarantee which order they will be tried in, because dictionaries are unordered.

Now save this, and load your code onto your Pico W.

Getting the code onto your Pico

Now you’re ready to upload the code to your Pico. I use VS Code for this:

  • Open the base directory for your project in VS Code (e.g. cd to the directory in the Terminal, then code .)
  • If you don’t already have it installed, install the Pico-W-Go extension
  • On the blue bar at the bottom of the screen, click ‘All commands’ then select ‘Upload project to Pico’

Find the IP address and test the API

If everything works (which it never does first time…) then your Pico should restart and connect to the network. After a while, it will show the IP address it’s connected to in the REPL in the window at the bottom of the VS Code workspace.

Enter the IP address in a browser. You should get a 404 message, and the page title should be the Pico ID (not the UUID) you entered in settings.py.

Try adding the IP address followed by /data. After a few seconds, you should get a JSON object with a single reading along with information about the Pico and sensor.

JSON data returned via the /data route. Pressure is null because it is not supported by this sensor - the dht22

JSON data returned via the /data route. Pressure is null because it is not supported by this sensor - the dht22

We can try out the /find route too.

The ‘find’ route returns the UUID

The ‘find’ route returns the UUID

Finally, if we try any other route, we get a 404 and see the name of the device.

The 404/resource not found route

The 404/resource not found route

And that’s it for now! What you actually do with this data is another matter… this solution doesn’t do any logging. We’ll cover that in a later post. If you’re curious, check out my ambient data logger on GitHub.