TODO: Port examples and images to Raspberry Pi Pico W
The Internet of Things (IoT) is the convergence of internet and real world. IoT embedded devices typically have limited resources, but they are also becoming more performant with each generation. This allows an interpreted language like Python, which is less efficient but more convenient than C, to run on a microcontroller.
This workshop teaches the basics of embedded programming on the latest IoT hardware, with CircuitPython.
This workshop is aimed at interested people with basic programming experience in Python.
Participants need a laptop with MacOS, Windows or Linux, and one USB/USB-C port. IoT hardware including sensors is available on loan.
The workshop requires a Wi-Fi network that is accessible without a portal. Alternatively, a personal smartphone can be used as a hotspot.
The easiest way to program microcontrollers — https://circuitpython.org/
To program a CircuitPython microcontroller, plug it in via USB.
It shows up as a USB drive called CIRCUITPY.
(If not, see hardware setup.)
CircuitPython works with any text editor, e.g. Mu Editor, VS Code, or nano.
$ nano /Volumes/CIRCUITPY/code.py
To see output you'll need a serial monitor like PuTTY on Windows or screen on MacOS, Linux (or tio).
$ screen /dev/tty.u<TAB> 115200
If there is no output, use CTRL-D to reload
Hello, World!
Code done running.
Or press any other key to enter the REPL
>>>
Download the library bundle ZIP file from https://circuitpython.org/libraries
You will selectively copy files from the ZIP to your microcontroller later on.
Plug in your board via USB and open the CIRCUITPY drive.
Copy required libraries from the bundle to the lib folder.
Copy your code to a file named code.py on the drive.
$ cp hello/code.py /Volumes/CIRCUITPY/code.py
Now you are ready to try GPIO & sensors.
We use a Pico W microcontroller with Grove sensors and actuators.
- https://www.adafruit.com/product/5526 (Raspberry Pi Pico W)
- https://www.adafruit.com/product/3879 (USB C to USB Micro cable for data transfer)
- https://www.adafruit.com/product/4175 (USB A to USB C adapter, optional)
https://circuitpython.org/board/raspberry_pi_pico_w/
To get the Pico W into ROM bootloader mode
- Press and hold the BOOTSEL button
- Unplug, then plug in the Pico W via USB
Now the board should show up as a USB device, e.g. /dev/cu.usbmodem01 on MacOS or COM3 on Windows.
Download the board specific .UF2 file from https://circuitpython.org/board/raspberry_pi_pico_w/
Drop it on the USB drive named RPI-RP2 and wait until the drive disconnects.
Now the board should show up as a USB drive named CIRCUITPY.
- https://learn.adafruit.com/pico-w-wifi-with-circuitpython/installing-circuitpython#flash-resetting-uf2-3128829 (Flash resetting UF2)
- https://learn.adafruit.com/welcome-to-circuitpython/troubleshooting
- TODO (Schematic)
- https://www.seeedstudio.com/Grove-Button-p-766.html
- https://www.seeedstudio.com/Grove-Red-LED-p-1142.html
- https://www.seeedstudio.com/Grove-Rotary-Angle-Sensor-p-770.html
- https://www.seeedstudio.com/Grove-Temperature-Humidity-Sensor-DHT1-p-745.html
- https://www.seeedstudio.com/Grove-4-pin-Male-Jumper-to-Grove-4-pin-Conversion-Cable-5-PCs-per-Pack.html
Control a LED or any other digital actuator.
/CIRCUITPY
└── code.py # copied from below
import board
import digitalio
import time
actuator = digitalio.DigitalInOut(board.LED) # or board.GP1 (Grove), GP3 (Grove), GP8 (badge LED)
actuator.direction = digitalio.Direction.OUTPUT
while True:
actuator.value = True
time.sleep(1)
actuator.value = False
time.sleep(1)
# No output, but LED should blink
Read a button or any other digital sensor.
/CIRCUITPY
└── code.py # copied from below
import board
import digitalio
import time
sensor = digitalio.DigitalInOut(board.GP6) # or board.GP1 (Grove), GP3 (Grove)
sensor.direction = digitalio.Direction.INPUT
sensor.pull = digitalio.Pull.UP
while True:
print(sensor.value)
time.sleep(0.1)
False
False
True
...
Read a DHT11 sensor using the adafruit_dht library.
/CIRCUITPY
├── code.py # copied from below
└── lib # libraries from bundle
└── adafruit_dht.mpy
import adafruit_dht
import board
import time
sensor = adafruit_dht.DHT11(board.D18)
while True:
try:
temp = sensor.temperature
humi = sensor.humidity
print("{:.2f} °C, {:.2f} %".format(temp, humi))
except RuntimeError as e:
print("Oops, reading the sensor did not work.")
time.sleep(5)
23.00 °C, 42.00 %
...
Search the library bundle docs for a sensor or actuator name.
Connect to the Internet using Wi-Fi.
/CIRCUITPY
└── code.py # copied from below
import wifi
WIFI_SSID = "MY_SSID" # TODO
WIFI_PASS = "MY_PASSWORD" # TODO
print("Connecting to Wi-Fi \"{0}\"...".format(WIFI_SSID))
wifi.radio.connect(WIFI_SSID, WIFI_PASS) # waits for IP address
print("Connected, IP address = {0}".format(wifi.radio.ipv4_address))
Connecting to Wi-Fi "MY_SSID"...
Connected, IP address = 192.168.0.23
Code done running.
Once a device is connected to the Internet, it can send data to a cloud backend.
To see how this works, try the HTTP post or MQTT publish examples.
Post data to the https://thingspeak.com/ cloud backend using HTTPS.
Create a free ThingSpeak account to get a Write API Key.
/CIRCUITPY
├── code.py # copied from below
└── lib # libraries from bundle
└── adafruit_requests.mpy
import ssl
import time
import wifi
import socketpool
import adafruit_requests
WIFI_SSID = "MY_SSID" # TODO
WIFI_PASS = "MY_PASSWORD" # TODO
CLOUD_KEY = "****************" # TODO, ThingSpeak Write API Key
CLOUD_URL = "https://api.thingspeak.com/update.json"
print("Connecting to Wi-Fi \"{0}\"...".format(WIFI_SSID))
wifi.radio.connect(WIFI_SSID, WIFI_PASS) # waits for IP address
print("Connected, IP address = {0}".format(wifi.radio.ipv4_address))
socket = socketpool.SocketPool(wifi.radio)
context = ssl.create_default_context()
https = adafruit_requests.Session(socket, context)
while True:
value = 23.0 # e.g. from sensor
json_data = {
"api_key": CLOUD_KEY,
"field1": value,
}
print("Posting to {0}\n> {1}".format(CLOUD_URL, json_data))
response = https.post(CLOUD_URL, json=json_data)
print("< {0}".format(response.json()))
time.sleep(30) # s
Connecting to Wi-Fi "MY_SSID"...
Connected, IP address = 192.168.0.42
Posting to https://api.thingspeak.com/update.json
> {'field1': 23.0, 'api_key': '****************'}
< {'field1': 23.0, 'channel_id': 555, 'created_at': '2022-08-30T13:37:00Z', ...
Now, try to merge in the DHT11 example to send real sensor values.
Or learn more about Internet protocols and HTTP.
Publish data to the https://thingspeak.com/ cloud backend using MQTT.
/CIRCUITPY
├── code.py # copied from below
└── lib # libraries from bundle
└── adafruit_minimqtt
├── __init__.py
├── adafruit_minimqtt.mpy
└── matcher.mpy
from random import randint
import ssl
import time
import wifi
import socketpool
import adafruit_minimqtt.adafruit_minimqtt as minimqtt
WIFI_SSID = "MY_SSID" # TODO
WIFI_PASS = "MY_PASSWORD" # TODO
# See https://ch.mathworks.com/help/thingspeak/mqtt-basics.html
MQTT_HOST = "mqtt3.thingspeak.com"
MQTT_PORT = 8883
# https://thingspeak.com/devices/mqtt > Add a device
MQTT_CLNT = "***********************" # TODO, Client ID
MQTT_USER = "***********************" # TODO, Username
MQTT_PASS = "***********************" # TODO, Password
THSP_CHAN = "000000" # TODO, ThingSpeak Channel ID
print("Connecting to Wi-Fi \"{0}\"...".format(WIFI_SSID))
wifi.radio.connect(WIFI_SSID, WIFI_PASS) # waits for IP address
print("Connected, IP address = {0}".format(wifi.radio.ipv4_address))
pool = socketpool.SocketPool(wifi.radio)
context = ssl.create_default_context()
def handle_connect(client, userdata, flags, rc):
print("Connected to {0}".format(client.broker))
def handle_publish(client, userdata, topic, pid):
print("Published to {0} with PID {1}".format(topic, pid))
mqtt_client = minimqtt.MQTT(
broker = MQTT_HOST,
port = MQTT_PORT,
client_id = MQTT_CLNT,
username = MQTT_USER,
password = MQTT_PASS,
socket_pool = pool,
ssl_context = context)
mqtt_client.on_connect = handle_connect
mqtt_client.on_publish = handle_publish
print("\nConnecting to {0}...".format(MQTT_HOST))
mqtt_client.connect()
while True:
value = 23 # e.g. from sensor
mqtt_topic = "channels/" + THSP_CHAN + "/publish"
mqtt_payload = "field1=" + str(value)
mqtt_client.publish(mqtt_topic, mqtt_payload)
time.sleep(5)
Learn more about the MQTT messaging protocol.