The Nano RP2040 Connect board can be programmed using the popular Python® programming language. The board is supported by upstream MicroPython and OpenMV's fork of MicroPython, where MicroPython is an implementation of the Python® language, designed to run on microcontrollers.
In this article, you will find a lot of sample scripts that will work directly with your Nano RP2040 Connect, such as general GPIO control, reading onboard sensors and Wi-Fi/BLE communication!
To install the OpenMV IDE and load scripts to your board, you can refer to Getting started with OpenMV and Nano RP2040 Connect.
To install upstream MicroPython and load scripts to your board, you will need to follow a few simple steps:
1. Connect your board to your computer via USB.
2. Download and install the Thonny Editor (or other preferred editors).
3. Download the
.uf2
file from the Nano RP2040 Connect's nightly build page4. Place a jumper wire between the REC and GND pins on the board, then press the RESET button. This will open mass storage.
5. Drag and drop the
.uf2
file into mass storage. This will install MicroPython on your board.6. In the Thonny Editor, navigate to Run > Select Interpreter.
7. Select MicroPython(generic) and from port, select your device (in this case, it is COM17).
8. Write a Python® script (or select any example from the list below), and click on the Green Play Button (F5) to run it on your board.
Congratulations! You can now run MicroPython scripts on your Nano RP2040 Connect board!
You can visit the docs for MicroPython to get a better overview of the MicroPython API.
Below you will find a lot of useful examples that can be loaded to your Nano RP2040 Connect board. Many of these examples were extracted from the OpenMV repository, where you can find many useful examples for other boards as well.
In this article, you will only find examples for the Nano RP2040 Connect board. For more information on how to use delays, read and write to pins, please refer to the Python® with Arduino main article.
The pinout for the Nano RP2040 Connect and the RP2040 microcontroller varies greatly. For example, if we are to use
D2
according to the Arduino pinout, we would actually need to use pin 25.1# Defining "D2" on the Arduino Nano RP2040 Connect2p0 = Pin(25, Pin.OUT)
Before you start using the board's pins, it might be a good idea to check out the table below to understand the relationship between Arduino's pinout and the RP2040's pinout.
Arduino | RP2040 | Usage |
---|---|---|
TX | GPIO0 | UART/TX |
RX | GPIO1 | UART/RX |
D2 | GPIO25 | GPIO |
D3 | GPIO15 | GPIO |
D4 | GPIO16 | GPIO |
D5 | GPIO17 | GPIO |
D6 | GPIO18 | GPIO |
D7 | GPIO19 | GPIO |
D8 | GPIO20 | GPIO |
D9 | GPIO21 | GPIO |
D10 | GPIO5 | GPIO |
D11 | GPIO7 | SPI/COPI |
D12 | GPIO4 | SPI/CIPO |
D13 | GPIO6 | SPI/SCK |
D14/A0 | GPIO26 | ADC/RP2040 |
D15/A1 | GPIO27 | ADC/RP2040 |
D16/A2 | GPIO28 | ADC/RP2040 |
D17/A3 | GPIO29 | ADC/RP2040 |
D18/A4 | GPIO12 | I2C |
D19/A5 | GPIO13 | I2C |
D20/A6 | GPIO36 | ADC/NINA-W102 |
D21/A7 | GPIO35 | ADC/NINA-W102 |
Note that pins A4, A5 are used as an I2C bus and are not recommended to be used as analog pins.
To read the analog pins on the Nano RP2040 Connect, we can choose from the following pins:
26
27
28
29
12
(I2C bus, not recommended to use)13
(I2C bus, not recommended to use)36
(Connected to the NINA module)35
(Connected to the NINA module)To define them, we need to import the
machine
module, and define the pin as follows: 1import machine2
3adc_pin = machine.Pin(29)4adc = machine.ADC(adc_pin)
To read the analog pin, simply use:
1reading = adc.read_u16() #16-bit resolution (0-65535)
The below script will read the
A3
pin on the Nano RP2040 Connect and print the value in the terminal.1import machine2import time3
4adc_pin = machine.Pin(29) # A35adc = machine.ADC(adc_pin)6
7while True:8 reading = adc.read_u16() 9 print("ADC: ",reading)10 time.sleep_ms(500)
Prints the accelerometer and gyroscope values in the Serial Monitor.
1import time2from lsm6dsox import LSM6DSOX3
4from machine import Pin, I2C5lsm = LSM6DSOX(I2C(0, scl=Pin(13), sda=Pin(12)))6
7while (True):8 print('Accelerometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*lsm.read_accel()))9 print('Gyroscope: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*lsm.read_gyro()))10 print("")11 time.sleep_ms(100)
Below example can be used with OpenMV's frame buffer window (top right corner).
1import image, audio, time2from ulab import numpy as np3from ulab import scipy as sp4
5CHANNELS = 16FREQUENCY = 320007N_SAMPLES = 32 if FREQUENCY == 16000 else 648SCALE = 29SIZE = (N_SAMPLES * SCALE) // CHANNELS10
11raw_buf = None12fb = image.Image(SIZE+(50*SCALE), SIZE, image.RGB565, copy_to_fb=True)13audio.init(channels=CHANNELS, frequency=FREQUENCY, gain_db=16)14
15def audio_callback(buf):16 # NOTE: do Not call any function that allocates memory.17 global raw_buf18 if (raw_buf == None):19 raw_buf = buf20
21# Start audio streaming22audio.start_streaming(audio_callback)23
24def draw_fft(img, fft_buf):25 fft_buf = (fft_buf / max(fft_buf)) * SIZE26 fft_buf = np.log10(fft_buf + 1) * 2027 color = (0xFF, 0x0F, 0x00)28 for i in range(0, len(fft_buf)):29 img.draw_line(i*SCALE, SIZE, i*SCALE, SIZE-int(fft_buf[i]) * SCALE, color, SCALE)30
31def draw_audio_bar(img, level, offset):32 blk_size = (SIZE//10)33 color = (0xFF, 0x00, 0xF0)34 blk_space = (blk_size//4)35 for i in range(0, int(round(level/10))):36 fb.draw_rectangle(SIZE+offset, SIZE - ((i+1)*blk_size) + blk_space, 20 * SCALE, blk_size - blk_space, color, 1, True)37
38while (True):39 if (raw_buf != None):40 pcm_buf = np.frombuffer(raw_buf, dtype=np.int16)41 raw_buf = None42
43 if CHANNELS == 1:44 fft_buf = sp.signal.spectrogram(pcm_buf)45 l_lvl = int((np.mean(abs(pcm_buf[1::2])) / 32768)*100)46 else:47 fft_buf = sp.signal.spectrogram(pcm_buf[0::2])48 l_lvl = int((np.mean(abs(pcm_buf[1::2])) / 32768)*100)49 r_lvl = int((np.mean(abs(pcm_buf[0::2])) / 32768)*100)50
51 fb.clear()52 draw_fft(fb, fft_buf)53 draw_audio_bar(fb, l_lvl, 0)54 draw_audio_bar(fb, l_lvl, 25*SCALE)55 if CHANNELS == 2:56 draw_audio_bar(fb, r_lvl, 25 * SCALE)57 fb.flush()58
59# Stop streaming60audio.stop_streaming()
Scans for devices connected to the I2C buses:
1import time2from machine import Pin, I2C3
4i2c_list = [None, None]5i2c_list[0] = I2C(0, scl=Pin(13), sda=Pin(12), freq=100_000)6i2c_list[1] = I2C(1, scl=Pin(7), sda=Pin(6), freq=100_000)7
8for bus in range(0, 2):9 print("\nScanning bus %d..."%(bus))10 for addr in i2c_list[bus].scan():11 print("Found device at address %d:0x%x" %(bus, addr))
To read data and write data through TX and RX pins, you can use
uart.write()
and uart.read()
.1from machine import UART, Pin2import time3
4uart = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))5
6while True:7 uart.write('hello') # writes 5 bytes8 val = uart.read(5) # reads up to 5 bytes9 print(val) # prints data10 time.sleep(1)
Below are examples on wireless connectivity, using the NINA-W102 module onboard the Nano RP2040 Connect.
In order to use these examples, you may have to upgrade your firmware. You can find instructions on how to in Upgrading Nano RP2040 Connect NINA firmware.
Turn your board into an access point:
1# Wi-Fi AP Mode Example2#3# This example shows how to use Wi-Fi in Access Point mode.4import network, socket, sys, time, gc5
6SSID ='My_Nano_RP2040_Connect' # Network SSID7KEY ='1234567890' # Network key (must be 10 chars)8HOST = '' # Use first available interface9PORT = 8080 # Arbitrary non-privileged port10
11# Init wlan module and connect to network12wlan = network.WLAN(network.AP_IF)13wlan.active(True)14wlan.config(essid=SSID, key=KEY, security=wlan.WEP, channel=2)15print("AP mode started. SSID: {} IP: {}".format(SSID, wlan.ifconfig()[0]))16
17def recvall(sock, n):18 # Helper function to recv n bytes or return None if EOF is hit19 data = bytearray()20 while len(data) < n:21 packet = sock.recv(n - len(data))22 if not packet:23 raise OSError("Timeout")24 data.extend(packet)25 return data26
27def start_streaming(server):28 print ('Waiting for connections..')29 client, addr = server.accept()30
31 # set client socket timeout to 5s32 client.settimeout(5.0)33 print ('Connected to ' + addr[0] + ':' + str(addr[1]))34
35 # FPS clock36 clock = time.clock()37 while (True):38 try:39 # Read data from client40 data = recvall(client, 1024)41 # Send it back42 client.send(data)43 except OSError as e:44 print("start_streaming(): socket error: ", e)45 client.close()46 break47
48while (True):49 try:50 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)51 # Bind and listen52 server.bind([HOST, PORT])53 server.listen(1)54
55 # Set server socket to blocking56 server.setblocking(True)57 while (True):58 start_streaming(server)59 except OSError as e:60 server.close()61 print("Server socket error: ", e)
To scan available networks:
1# Scan Example2
3# This example shows how to scan for Wi-Fi networks.4
5import time, network6
7wlan = network.WLAN(network.STA_IF)8wlan.active(True)9
10print("Scanning...")11while (True):12 scan_result = wlan.scan()13 for ap in scan_result:14 print("Channel:%d RSSI:%d Auth:%d BSSID:%s SSID:%s"%(ap))15 print()16 time.sleep_ms(1000)
Making an HTTP request (in this case to google):
Remember to enter your network name and password inside the SSID and KEY variables.
1import network, socket2
3# AP info4SSID='' # Network SSID5KEY='' # Network key6
7PORT = 808HOST = "www.google.com"9
10# Init wlan module and connect to network11print("Trying to connect. Note this may take a while...")12
13wlan = network.WLAN(network.STA_IF)14wlan.active(True)15wlan.connect(SSID, KEY)16
17# We should have a valid IP now via DHCP18print("Wi-Fi Connected ", wlan.ifconfig())19
20# Get addr info via DNS21addr = socket.getaddrinfo(HOST, PORT)[0][4]22print(addr)23
24# Create a new socket and connect to addr25client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)26client.connect(addr)27
28# Set timeout29client.settimeout(3.0)30
31# Send HTTP request and recv response32client.send("GET / HTTP/1.1\r\nHost: %s\r\n\r\n"%(HOST))33print(client.recv(1024))34
35# Close socket36client.close()
Remember to enter your network name and password inside the SSID and KEY variables.
Obtain accurate time and date from the Internet:
1# NTP Example2#3# This example shows how to get the current time using NTP4
5import network, usocket, ustruct, utime6
7# AP info8SSID='' # Network SSID9KEY='' # Network key10
11TIMESTAMP = 220898880012
13# Init wlan module and connect to network14print("Trying to connect... (may take a while)...")15
16wlan = network.WLAN()17wlan.active(True)18wlan.connect(SSID, key=KEY, security=wlan.WPA_PSK)19
20# We should have a valid IP now via DHCP21print(wlan.ifconfig())22
23# Create new socket24client = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM)25client.bind(("", 8080))26#client.settimeout(3.0)27
28# Get addr info via DNS29addr = usocket.getaddrinfo("pool.ntp.org", 123)[0][4]30
31# Send query32client.sendto('\x1b' + 47 * '\0', addr)33data, address = client.recvfrom(1024)34
35# Print time36t = ustruct.unpack(">IIIIIIIIIIII", data)[10] - TIMESTAMP37print ("Year:%d Month:%d Day:%d Time: %d:%d:%d" % (utime.localtime(t)[0:6]))
In the terminal, we should see it in this format:
1Year:2021 Month:8 Day:10 Time: 7:56:30
This example allows us to connect to our board via our phone, and control the built-in LED. We recommend using the nRF Connect applications.
After loading the script below, your board should be listed as "Nano RP2040 Connect" in the list of available devices. You need to pair in order to control the built-in LED.
1import bluetooth2import random3import struct4import time5from ble_advertising import advertising_payload6from machine import Pin7from micropython import const8
9LED_PIN = 610
11_IRQ_CENTRAL_CONNECT = const(1)12_IRQ_CENTRAL_DISCONNECT = const(2)13_IRQ_GATTS_WRITE = const(3)14
15_FLAG_READ = const(0x0002)16_FLAG_WRITE = const(0x0008)17_FLAG_NOTIFY = const(0x0010)18_FLAG_INDICATE = const(0x0020)19
20_SERVICE_UUID = bluetooth.UUID(0x1523)21_LED_CHAR_UUID = (bluetooth.UUID(0x1525), _FLAG_WRITE)22_LED_SERVICE = (_SERVICE_UUID, (_LED_CHAR_UUID,),)23
24class BLETemperature:25 def __init__(self, ble, name="NANO RP2040"):26 self._ble = ble27 self._ble.active(True)28 self._ble.irq(self._irq)29 ((self._handle,),) = self._ble.gatts_register_services((_LED_SERVICE,))30 self._connections = set()31 self._payload = advertising_payload(name=name, services=[_SERVICE_UUID])32 self._advertise()33
34 def _irq(self, event, data):35 # Track connections so we can send notifications.36 if event == _IRQ_CENTRAL_CONNECT:37 conn_handle, _, _ = data38 self._connections.add(conn_handle)39 elif event == _IRQ_CENTRAL_DISCONNECT:40 conn_handle, _, _ = data41 self._connections.remove(conn_handle)42 # Start advertising again to allow a new connection.43 self._advertise()44 elif event == _IRQ_GATTS_WRITE:45 Pin(LED_PIN, Pin.OUT).value(int(self._ble.gatts_read(data[-1])[0]))46 47 def _advertise(self, interval_us=500000):48 self._ble.gap_advertise(interval_us, adv_data=self._payload)49
50if __name__ == "__main__":51 ble = bluetooth.BLE()52 temp = BLETemperature(ble)53 54 while True:55 time.sleep_ms(1000)
In this article we have gone through a selection of scripts that will help you control your Nano RP2040 Connect board, via the OpenMV IDE. Feel free to check out our Python® with Arduino boards article, where you can find guides to other boards, useful links to learn Python® and more.