Projects

Arduino with Pi

Making with the Nano RP2040 Connect

Liam Davies

Issue 48, July 2021

This article includes additional downloadable resources.
Please log in to access.

Log in

We put this Nano-sized Arduino powerhouse to the test with two easy-to-build projects.

BUILD TIME: a FEW HOURS
DIFFICULTY RATING: Intermediate

It’s always exciting when a new microcontroller is released. The anticipation of what added features and benefits come from the new tech, and what we makers can build it if we get our hands on one.

This month, we were fortunate to be sent the new Nano RP2040 Connect from those clever folk at Arduino. This new board has gained a lot of interest because of the collaboration between Arduino and Raspberry Pi. So, instead of doing a regular review, we’ve gone hands-on, and present you with two projects to build with one. We think that this approach will give you a much better understanding of its capabilities.

The RP2040 Connect

The original Arduino Nano has proven to be, by far, one of the most popular microcontrollers for maker projects because of its versatile and extremely compact form factor. With the ability to slot right into a breadboard and provide the same functionality as the much larger Arduino Uno, there’s not much argument that this little Nano board has revolutionised how many have learnt the ropes of DIY electronics.

However, Arduino just released their brand-new RP2040 Connect, a sensor-packed powerhouse that shapes up to be a worthy successor to the mighty Nano. With the same form factor and pinout, the Connect has access to an entirely new processor, an array of inbuilt sensors, and a Wi-Fi/Bluetooth module.

The RP2040 Connect is the first Arduino ever to use Raspberry Pi-based processing hardware, a 32-bit dual-core Arm Cortex-M0+ running at 133MHz. It’s powerful enough to be capable of running TensorFlow Lite, making running machine learning code possible.

First Impressions

Comparing the Connect to the Nano, the first thing we realised was just how many gadgets Arduino has managed to jam onto the tiny board. There is hardly a square millimetre of the board that isn’t used by tiny SMD components.

The board we received had the breadboard-compatible headers already soldered on, however, Arduino also sells a version with the castellated pins exposed, without headers. Since there are no components on the underside of the board, it can be soldered flush to a PCB for compact projects.

One thing we were slightly disappointed by was the exclusion of a USB-C port, with Arduino opting for the rather outdated Micro-USB port for connection to a computer or power. This means a less durable connector and lower power delivery than USB-C. With the latest Raspberry Pi 4 featuring a USB-C port, we were hoping that Arduino followed with the more modern connection system. However, we do recognise that the Micro-USB port may have been chosen for backwards-compatibility with the Arduino Nano, so it can be slotted directly into existing projects.

Speaking of compatibility, the RP2040 Connect is fully compatible with the existing Raspberry Pi Pico ecosystem! This means support for MicroPython, various lightweight machine learning libraries and even an included OpenMV licence for machine vision.

The RP2040 Connect includes a super fancy six-axis accelerometer that can detect linear and angular acceleration with ranges between +/-2G and +/-16G. It features an AI-powered core that can filter and recognise acceleration events such as double-tapping or fall sensing. Also onboard is a digital microphone with PDM (Pulse Density Modulation) and an authenticator chip for cryptography processing.

Because of its Raspberry Pi Pico-powered CPU, the RP2040 Connect brings a new form of electronics interface to the table, Programmable I/O. This allows small operations to be completed on input and output pins without the use of the main CPU’s processing time, opening up possibilities of multitasking. While the programmable I/O is quite basic in terms of instruction set available, it is possible to accomplish data processing that would be very difficult on a standard microcontroller.

There are also some nice-to-have features that are often seldom seen on other, inexpensive Arduino boards. The Connect includes an inbuilt step-down DC-DC converter, unlike the usual linear regulator most Arduinos are included with. This means that not only is the Connect highly efficient in terms of its power usage, but it also can be provided with a wide range of input voltages, ranging from 5-22V!

Project 1: Sound Meter

Parts Required:Jaycar
1 x RP2040 Connect (With header)Arduino Store: ABX00053
OPTIONAL: Jaycar
1 x RP2040 Connect (Without header)Arduino Store: ABX00052
1 x 30 pin or more Male HeaderHM3211

We’re going to introduce some of the clever gadgets the RP2040 Connect is packed with by building a sound meter to measure the loudness of sounds around it. Because these sensors are a bit more feature-packed than the usual Arduino modules we use for projects, we’re not going to delve super in-depth as it gets confusing pretty quick!

Of course, once you feel confident with coding and modifying the hands-on projects we've made, feel free to look into some of the more advanced features.

We used Arduino’s online editor to program the Connect, which can be accessed at create.arduino.cc/editor/. This is the most convenient way to write and upload code, as the online editor already includes the required libraries to manage connections to the sensors onboard. However, both the older and new 2.0 versions of the Arduino IDE can be used, provided the libraries for WiFiNINA, PDM, and any other libraries we’re using in the sketches are installed. Whatever IDE you’re using, the RP2040 Connect will need to be selected from the Boards Manager and installed.

Our global variables and definitions at the top of the code file are shown here. The WiFiNINA library is needed here despite not using any WiFi functionality of the RP2040, since the RGB light is actually controlled by the WiFiNINA module. The PDM library provides data reading functionality of the digital microphone on board.

Note: Some code for this article is adapted from the example Arduino sketches for the RP2040 Connect.

#include <WiFiNINA.h>
#include <PDM.h>
#define SENSITIVITY 8.0
// Number of audio samples read
volatile int samplesRead;
short sampleBuffer[512];
short averageReading = 0;
short avSamples = 5

The above sensitivity variable is how sensitive the microphone will be to transitioning from the “quiet” colour to the “loud” colour. We’ll use this later in our light code. The other four global variables are necessary for handling audio data collection and averaging it out for creating a stable light colour. You can change the number of avSamples if you want to change the response time (and inversely, stability) of your sound meter.

void setup() {
  Serial.begin(9600);
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  
  PDM.onReceive(onPDMdata);
  if (!PDM.begin(1, 32000)) {
    Serial.println("Failed to start PDM!");
    while (1);
  }
}
void onPDMdata() {
  int bytesAvailable = PDM.available();
  PDM.read(sampleBuffer, bytesAvailable);
  samplesRead = bytesAvailable / 2;
}

This is the setup and audio data reading code we used. After starting a serial connection, we can use the inbuilt constants LEDR and LEDG to set up the red and green LEDs. The group of code below is used for starting up the PDM library, and attaching a “callback” method called onPDMdata.

A callback means that every time valid data is received from the microphone, we run the onPDMdata method that puts the available data into our “sampleBuffer” variable. We can then look through this sampleBuffer array elsewhere to see what microphone data we received.

Code Talk

For readers with a keen eye, you may have noticed that the global variable 'samplesRead' we're referencing from within the onPDMdata callback is marked as volatile in the code.

This is super important since this onPDMdata callback is known as an Interrupt Service Routine (ISR) that can be executed at any time, independent of where the code currently is running at.

By using the keyword 'volatile', we are letting the compiler know the value of this variable may change unexpectedly at any time, and not just from our normal loop() or main().

The compiler - which is the program that turns our human-readable code into machine code - only knows and understands the flow and behavior of the code we have in the loop() or main(), not ISRs. Without the volatile keyword, it would analyse our code, and potentially optimise out or incorrectly cache the variable, and any changes our ISR should make to it won't happen as we expect.

Additionally, it's important to know ISRs have other special limitations and requirements. The main two examples are that (a) they cannot pass parameters, and (b) you cannot hold up the code within them.

The latter point means functions that take quite some time - Serial.print, millis, delay, etc. - should never be used in an ISR.

ISRs should only really be used for increasing variables (like we are doing here), or "raising flags" - a fancy term for setting a variable true or false.

A great example is using an ISR for a button press. When the button is pressed, you shouldn't run the function you want in the ISR - you should just set a variable "buttonPressed = true", and exit the ISR immediately.

Then, in your main() or loop(), process what happens when buttonPressed == true accordingly.

It’s worth noting that the “32000” variable in the line for beginning the PDM communication is the sample rate. We found that the default sample rate of 16000Hz doesn’t work well (if at all) with the RP2040, so we changed it to 32000Hz which works a treat!

void loop() {
  // Wait for samples to be read
  if (samplesRead) {
    // Print samples to the serial 
monitor or plotter
    for (int i = 0; i < samplesRead; i++) {
      Serial.println(sampleBuffer[i]);
      // Make our values read positive
      short sampleVal = abs(sampleBuffer[i]);
      // Average value.
      averageReading = ((averageReading * 
(avSamples - 1)) + sampleVal) / avSamples;
      // Scale and constrain.
      short outputVal = constrain(averageReading 
* SENSITIVITY * (255.0/32700.0), 0, 255);
      // Set the LEDs to write data.
      analogWrite(LEDG, outputVal);
      analogWrite(LEDR, 255 - outputVal);
    }
    //Clear the number of samples 
we read to start again.
    samplesRead = 0;
  }
}

While it looks like a lot of code in the loop function, most of it is actually dedicated to smoothing out the data we’re reading from the microphone. Since we can’t write negative values to our LED, we only want the magnitude of the sound, and thus, we take the absolute value first. We then smooth it out using a weighted averaging function, similar to the digital bubble level in the GlowBit project from Issue 45. Depending on how responsive you want the sound meter, simply change the avSamples variable. Finally, we create out outputVal variable, which scales the input range to the output range, 32700 (the observed maximum range of the microphone) to the 255 maximum of the LEDs. It’s vital that a decimal point is added to avoid the compiler converting the variable to an integer.

Since the sensitivity variable can push the output value to above 255, we must use the constrain function to keep it within the bounds of the analogWrite function. Finally, when writing to the red and green LEDs, we need to remember to invert the values so as the sound gets louder, the red LED increases in intensity as green decreases.

Testing

To upload, make sure the correct COM port is selected and, if you’re using the online editor, the Arduino Create Agent is installed on your computer. The method the RP2040 connect uses to upload code is slightly different than standard Arduino chips. You may notice that your computer detects a new device with mass storage during uploading, which is due to the bootloader structure of the Raspberry-Pi architecture. This should disappear after uploading though. If it doesn’t, double-press the reset button and try uploading again. We found that programs take a lot longer to compile, especially the first time, due to the size and variety of libraries needed to make the Connect tick.

Once uploaded, talk into the microphone or place the Connect near a speaker and play some music. You should see that the LED changes from green to red as the sound becomes louder! If you find the microphone isn’t sensitive enough, increase the sensitivity variable at the top of the sketch. This project is also a cool way to visually see how much wind noise affects microphones. Simply blow into the microphone and you’ll find it instantly turns red! We then put a small “Dead Cat” windshield on it, furry covers that can be slipped over microphones, and it was much harder to turn the light red by blowing on it.

Project 2: Internet-Powered Servo Controller

Let’s put some of the powerful WiFi capabilities of the RP2040 Connect to some use by hosting a small controller website! A servo connected to your board can be simply controlled by navigating to a small website the Connect hosts. First, we need to put together a simple circuit to connect up our servo.

Additional Parts Required:Jaycar
1 x Micro Tower ServoYM2758
3 x Male - Male Jumper Wires*WC6024
1 x 220µF 16V Electrolytic Capacitor*RE6312

* Quantity shown, may be sold in packs. You’ll also need a breadboard and prototyping hardware.

To start, you’ll need to bridge the VUSB jumper on the bottom of the Connect by simply soldering the two gold pads together. By default, the 5V pin is floating, and thus, we won’t be able to power our servo otherwise. Note that this connects the servo directly to the micro-USB’s 5V. Then, just insert the Connect into a breadboard and add connections for ground and 5V. We recommend referring to a pinout as the RP2040 is too packed with gadgets to have pin labels on the topside!

Then, add a 220µF capacitor and jumper wires for the servo header which we’ll add shortly. The 220µF capacitor is present as we need to be able to buffer out any sudden current requirements to avoid overloading the 5V USB port. Finally, a data wire can be connected to Pin 19 to send the signal to the Servo. Just plug in the servo’s power and data jumper cables, and we can get to programming.

The Website

Websites are, fundamentally, very simple methods of serving information to a client. When a client sends a request to fetch a webpage, for example at www.google.com, the server sends back text data that represents the webpage. It’s up to the client to interpret and potentially request other information like images or media from the webserver.

We’re not going to be doing anything that fancy, but building a website page on a capable microcontroller like the RP2040 Connect is a great way to utilise its processing power. And, for letting us access the website without a separate WiFi router, we’re going to set up an access point (AP) on the Connect to enable direct connection.

#include <SPI.h>
#include <WiFiNINA.h>
#include <Servo.h>
char network_name[] = "Servo Controller";
char password[] = "12345678";
int status = WL_IDLE_STATUS;
WiFiServer server(80);
Servo servo;

Our global variables are fairly simple, and here is where you can configure the name and password for your WiFi network. Make sure both the network name and the password are at least 8 characters in length.

void loop() {
  if (status != WiFi.status()) {
    //Print status if we've changed it!
    status = WiFi.status();
    if (status == WL_AP_CONNECTED) {
      Serial.println("Device connected to AP");
    } else {
      Serial.println("Device disconnected 
from AP");
    }
  }
  WiFiClient client = server.available();
  if (client) {
    Serial.println("New client connected!");
    String currentLine = "";
    // While our client is still 
listening and available:
    while (client.connected()) {
      if (client.available()) {
        // Read request data.
        char c = client.read();
        Serial.write(c);
        // If this is the end of the request:
        if (c == 'n') {
          if (currentLine.length() == 0) {
            // Now that we've finished, serve the webpage.
            serveWebpage(client);
            break;
          } else {
            currentLine = "";
          }
          // Carriage return handling.
        } else if (c != 'r') {
...
          currentLine += c;
        }
        
        if (currentLine.endsWith("GET /MIN")) {
          servo.write(0);
        }
        if (currentLine.endsWith("GET /MID")) {
          servo.write(90);
        }
        if (currentLine.endsWith("GET /MAX")) {
          servo.write(180);
        }
      }
    }
    // Stop the client connection.
    client.stop();
    Serial.println("client disconnected");
  }
}

We aren’t going to show the setup code for this program because of limited space. The loop code, however, is important as it controls how the RP2040 handles and responds to requests over the network. Essentially, this loop code monitors when a client first connects and makes a request for the controller page. So that we can see what the client is requesting, we print the text to the Serial console. All HTTP requests end with two newline characters, and if we reach the end of the client’s request, we can then begin to send back our HTTP data.

void serveWebpage(WiFiClient client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-type:text/html");
  client.println();
  client.print("<style>");
  client.print(".container {margin: 0 auto; 
text-align: center; margin-top: 100px;}");
  client.print(".logo {margin: 0 auto; 
text-align: center; margin-top: 50}");
  client.print("<div class='container'>");
  client.print("<button class='min' 
type='submit' onmousedown='location.href="/MIN"'>0 deg</button>");
  client.print("</div>"); client.println();
}

Above is the function for sending through all of the HTTP page text. This is what we mean by webpages are all just text. It’s up the client to interpret the data. In this case, a basic stylesheet is also included to circularise the buttons and centre all elements. Before that though, it’s super important to send a header to tell the client what we’re actually sending and that their request was successful-“HTTP/1.1 200 OK”.

When the client clicks on one of the three buttons, they are “redirected” to one of the three pages MIN, MID, and MAX. Since our server doesn’t actually care what page the client asks for, the page is simply reloaded as normal but the parameters are passed to our code which sets the servo position.

And that’s it! Simply upload your code to the RP2040, join the WiFi network on your phone or computer, and head over to the IP address listed in the Serial console, which is likely 192.168.4.1 or something similar.

Final Thoughts

We had a blast tinkering about with the RP2040 Connect, however, we didn’t get the opportunity to play around with some of the more advanced features. Many of the machine learning capabilities do not have a lot of Arduino code documentation right now, however, hopefully, that will change as the RP2040 Connect hits the market.

While the RP2040 Connect includes a couple of new fancy features like the AI-powered accelerometer, most of the included sensors have been featured on official and unofficial Arduino boards in the past.

What makes the little powerhouse of a board really promising, though, is the minuscule format that Arduino’s designers have managed to squeeze it all into! That, coupled with the upcoming MicroPython support for machine learning libraries, turns the RP2040 Connect into a potential gamechanger in micro-computing.

The ability to add two cores of 133MHz computing power into a tiny, and importantly, an end-user friendly package, is the biggest benefit of Arduino’s newest workshop gadget. We wouldn’t be surprised if it started actively replacing Arduino Nanos in existing and future projects.

As we write this, the Arduino store has sold out of this popular board, however, we know that Arduino is doing everything they can to replenish ASAP. The Connect boards are, of course, available from many other retailers around the world, such as Core Electronics here in Australia.

We can’t wait to see what makers will manage to pull off with it!