Skip to content

Instantly share code, notes, and snippets.

@stonehippo
Last active June 25, 2024 14:46
Show Gist options
  • Save stonehippo/03677c206bf68846328f151ee8322193 to your computer and use it in GitHub Desktop.
Save stonehippo/03677c206bf68846328f151ee8322193 to your computer and use it in GitHub Desktop.
Using CircuitPython modules to work with the Seeed Wio Terminal

Seeed Wio Terminal Circuitpython Modules

note: All of these examples have been tested with the latest version of CircuitPython, which as of this writing was 7.2.4. Some of these examples may require tweaks in older or later versions, due to change in the drivers.

The Seeed Wio Terminal is a nifty connected device development kit. Built around a SAMD51 Cortex-M4 microcontroller and a Realtek RTL8720DN for WiFi and Bluetooth, plus an integrated display and a collection of handy sensors, the Wio Terminal is a great platform for IoT and smart device development.

One of the nice things about the Wio Terminal is the number of options available for developement platforms. You can choose the long-time hardware hacking favorite Arudino, MicroPython or Ardupy, an interesting blend of MicroPython and Arduino. And there's support for my current go-to for Python on hardware, Adafruit's CircuitPython.

Seeed provides some info to get you started with the Wio Terminal and CircuitPython on their wiki. However they don't cover which the hunderds of CircuitPython modules from the library are required to work with the Wio Terminal. While the Wio Terminal has a pretty large amount of flash for a microcontroller, it would be nice to know what the minimal set of modules required would be. And that's what this gist attempts to capture: the list of modules needed to make the Wio Terminal and CircuitPython sing!

Installing Modules

You can get the CircuitPython modules you need in a couple of ways: manually via the CircuitPython library bundle or on the command line with circup.

If you're doing things manually, grab the latest library bundle for your version of CircuitPython (I'm using version 7.2.4 here), unzip it, and drag the following modules into the lib directory on your WIO Terminal:

  • adafruit_bitmap_font
  • adafruit_bus_device
  • adafruit_display_text
  • adafruit_irremote
  • adafruit_lis3dh.mpy
  • simpleio.mpy (this one is optional, but it can make working with some of the peripherals easier)

If you're using circup (which I recommend) you can install all of these modules like this:

circup install adafruit_bitmap_font adafruit_bus_device adafruit_display_text adafruit_irremote adafruit_lis3dh simpleio

Note that this is currently no CircuitPython support for the WiFi and Bluetooth LE functions of the RTL8720D in the WIO Terminal. However, it works via embedded RPC over UART on the Arduino core for this device, so I'm working on seeing if I can make that work in CP, too.

The Modules

General

  • adafruit_bus_device - Handy for working with I2C and SPI peripherals, and a dependency for many of the modules

Display

Support for displayio, the CircuitPython display drawing library, is built into the UF2 image for the Wio Terminal. But if you really want to make the most of it, you will want some additional modules installed.

Accelerometer

  • adafruit_lis3dh

The onboard accelerometer is available at board.GYROSCOPE_SCL and board.GYROSCOPE_SDA, plus an "interrupt" at board.GYROSCOPE_INT (that's in quotes because CircuitPython doesn't really support interrupts).

For basic use with LIS3DH driver module, try something like this:

Note: yeah, it's weird that the pins are called "gyroscope", not "accelerometer", but that's what the schematic called them, so that's how they got defined in CP. But rest assured, the Terminal has an accelerometer but does not have a gyro.

import board
import busio
import adafruit_lis3dh

gyro_i2c = busio.I2C(board.GYROSCOPE_SCL, board.GYROSCOPE_SDA)
accel = adafruit_lis3dh.LIS3DH_I2C(gyro_i2c)

To use the interrupt:

import board
import busio
from digitalio import DigitalInOut
import adafruit_lis3dh

gyro_i2c = busio.I2C(board.GYROSCOPE_SCL, board.GYROSCOPE_SDA)
gyro_int = DigitalInOut(board.GYROSCOPE_INT)
accel = adafruit_lis3dh.LIS3DH_I2C(gyro_i2c, int1=gyro_int)

You can Read The Docs for the full details on the configuration of this driver.

Audio

The PDM microphone in the Wio Terminal can be used with the built-in audiobusio. You can initialize it like this:

import board
import audiobusio

pdm_mic = audiobusio.PDMin(board.I2S_BCLK, board.I2S_SDIN, sample_rate=16000, bit_depth=16)

The sample rate is a mimimum of 16Khz (16,000 Hz) and is approximate. (https://docs.circuitpython.org/en/latest/shared-bindings/audiobusio/index.html#audiobusio.PDMIn).

Buttons

The three buttons across the top of the WIO Terminal are available as board.BUTTON_1 (right-most button), board.BUTTON_2 (middle button), and board.BUTTON_3 (left-most button).

Use them just like any other basic momentary switch in CircuitPython:

import board
from digitalio import DigitalInOut, Pull

b1 = DigitalInOut(board.Button_1)
b1.switch_to_impout(Pull.UP)
print(b1.value) # will be `True` if not pressed, `False` if pressed, thanks to the pullup resistor

In CircuitPython 7.2.4 and above, all of the buttons default to digitalio.Direction.INPUT, so they'll behave like the above example with a little less code:

import board
from digitalio import DigitalInOut

b1 = DigitalInOut(board.Button_1)
print(b1.value) # will be `True` if not pressed, `False` if pressed, no need to call `switch_to_input`

Buzzer

The Wio Terminal buzzer is available at board.BUZZER. To play simple tones, you can use pwmio.PWMOut:

import board
import pwmio

OFF = 0
ON = 2**15

buzzer = pwmio.PWMOut(board.BUZZER, variable_frequency=True)
buzzer.frequency = 440  # set a note, like Middle C
buzzer.duty_cycle = ON  # enable the buzzer to play the note
buzzer.duty_cycle = OFF # turn off the note

If you have the simpleio module installed, you can use that to handle buzzer tasks, too.

import board
import simpleio

buzzer=board.BUZZER

simpleio.tone(buzzer, 440, duration=0.5) # play Middle C for 1/2 second

Five-way Switch

The five-way switch on the front of the device works just like a collection of momentary switches. It has five inputs defined: board.SWITCH_PRESS, board.SWITCH_UP, board.SWITCH_RIGHT, board.SWITCH_DOWN, and board.SWITCH_LEFT. They can be used just like the buttons (see examples above) and at least in CP 7.2.4. and above, they default to digitalio.Direction.INPUT so you can use them with the shorter code version consistently.

import board
from digitalio inport DigitalInOut

sw_center = DigitalInOut(board.SWITCH_PRESS)
sw_right = DigitalInOut(board.SWITCH_RIGHT)
sw_left = DigitalInOut(board.SWITCH_LEFT)
sw_up = DigitalInOut(board.SWITCH_UP)
sw_down = DigitalInOut(board.SWITCH_DOWN)

Infrared Transmitter

You can send via IR receiver with pulseio. If what you're sending is standard remode codes, you'll probably want to use adafruit_irremote, too. It's not bundled with CP, so you'll need to install it.

import board
from pulseio import PulseOut
import adafruit_irremote

ir_transmitter = PulseOut(board.IR, frequency=38000, duty_cycle=2 ** 15)
ir_encoder = adafruit_irremote.GenericTransmit(header=[9000, 4500],
                                            one=[560, 1700],
                                            zero=[560, 560],
                                            trail=0)

ir_encoder.transmit(ir_transmitter, [255, 2, 255, 0])

Code adapted from https://learn.adafruit.com/infrared-ir-receive-transmit-circuit-playground-express-circuit-python/ir-from-cpx-to-cpx.

LED

The onboard LED is available at board.LED. Controlling it with digitalio.DigitalInOut is pretty simple.

import board
from digitalio import DigitalInOut

led = DigitalInOut(board.LED)
led.switch_to_ouput
led.value = True // turn on the LED
led.value = False // turn off the LED

Light Sensor

The light sensor is a board.LIGHT. You'll want to connect to is using analogio.AnalogIn.

import board
from analogio import AnalogIn

light_sensor = AnalogIn(board.LIGHT)
light_sensor.value # read the current light level

Micro SD

Using the SD card reader is fairly straightforward, thanks to the sdcardio library included in CP (if you're using an older version of CP that doesn't have sdcardio, you can use adafruit_sdcard; see https://learn.adafruit.com/micropython-hardware-sd-cards/circuitpython)

import board
import busio
import sdcardio
import storage

sd_spi = busio.SPI(board.SD_SCK, MOSI=board.SD_MOSI, MISO=board.SD_MISO)
sd_cs = board.SD_CS

sd_card = sdcardio.SDCard(sd_spi, sd_cs)
vfs = storage.VfsFat(sd_card)
storage.mount(vfs, "/sd")

Note: Before you can mount the SD card, you'll need to create a directory in the root filesystem on the WIO terminal that you can use for a mount point. If you get an error like RuntimeError: Mount point directory missing, not having creating the mount point first is probably the cause.

The simplest way to do this is via the mounted CIRCUITPY directory; just create the sd directory at the root and you're good to go. If you want to do this programmatically, it's a bit harder, since the CIRCUITPY drive is mounted read-only by default (from the CP side of things). You can use something like this:

import os
if not 'sd' in os.listdir('/'):
    try:
        os.mkdir('/sd')
    except OSError as err:
        if err.errno == 30:
            print("/sd mount point folder does not exist, either create this folder")
            print("from the host computer or configure CIRCUITPY for read/write by")
            print("modifying the boot.py file, inserting the following:")
            print('\nimport storage\nstorage.remount("/",False)')
        else:
            print(str(err))

(code from adafruit/circuitpython#8872 (comment))

WiFi and Bluetooth

There is currently no support for Wifi or Bluetooth via the Realtek chip in CircuitPython.

Getting information about all of the onboard peripherals

You can enumerate all of the onboard peripherals simply:

import board
print(dir(board))
@stonehippo
Copy link
Author

stonehippo commented Jan 27, 2022

@jhoughjr
Copy link

jhoughjr commented Apr 3, 2022

Quite a useless board without networking support. Seems its been two years with no progress.
Also most of these examples yield errors like
main.py output:
Traceback (most recent call last):
File "main.py", line 9, in
TypeError: function takes 3 positional arguments but 4 were given

for the accelerometer to even get that far I had to use busio.I2C, not board.
but only 2 args were actually passed. I like the idea of circuit python, especially the real, but
I guess Ill go back to Arduino C++.
Probably never buying a seeed product again given their abysmal support.

@stonehippo
Copy link
Author

stonehippo commented Apr 3, 2022

@jhoughjr The LIS3DH example was wrong, for sure, which was my fault. I've fixed it, with and without interrupt support. I have confirmed that I can get accelerometer data with or without the interrupt using CP 7.4.2 and the latest version of the adafruit_LIS3DH driver.

I also corrected a minor typo (the variable name) on the microphone example. I plan to flesh out the rest of these examples and will most likely drop the link to the other resource.

I'm not sure what the holdup in on CP support for WiFi/BLE. I suspect that it's a proprietary drive issue with the Realtek chip. However, I would not say this board is useless with CP at all. You can do quite a bit with the on-board peripherals and sensors, external I2C, and the display. It would be great to have WiFi/Bluetooth, and if you really need that, the Adafruit PyPortal is probably a better choice.

@stonehippo
Copy link
Author

stonehippo commented Apr 3, 2022

Ok, I decided to see what's up with RealTek WiFi/BLE support.

Fortunately, it's not a driver issue per se. The WIO Terminal uses an embedded remote procedure call library to talk to the WiFi and BLE functions on the RealTek chip from the SAMD51. This is very much like the way that the Adafruit Airlift works as a co-processor. In fact, Seeed has even implemented an ESP32 emulation layer on the Arduino side to improve compatibility for their Arduino core.

Looking at the CircuitPython board definition for the WIO Terminal, the pins that talk to the RTL8720D are already defined, which is great news because we don't have to go digging around in schematics to find out what's connected to the chip. Both an SPI and UART are connected to the RTL87290D. I think that the SPI is mainly used for flashing the RLT8720 firmware, while the UART is used for the eRPC functions.

I need to take a look at the eRPC code some more, and probably get a look at the CP implementation of the Airlift co-processor driver, but based on what I can see right now, I think it would be possible to implement that SAMD51 side of the eRPC calls for CP, which means that with the latest firmware, we could get WiFi and BLE access on the WIO Terminal in CircuitPython!

Keeping track of my exploration on getting it working in this gist for now.

@Xvery5
Copy link

Xvery5 commented Sep 26, 2022

Quite a useless board without networking support. Seems its been two years with no progress. Also most of these examples yield errors like main.py output: Traceback (most recent call last): File "main.py", line 9, in TypeError: function takes 3 positional arguments but 4 were given

for the accelerometer to even get that far I had to use busio.I2C, not board. but only 2 args were actually passed. I like the idea of circuit python, especially the real, but I guess Ill go back to Arduino C++. Probably never buying a seeed product again given their abysmal support.

that's exactly how I feel, I don't even know what is up with "ArduPy" its been not deleted, but all content removed for months.... better off paying for a real Arduino or a supported circuit python board, even the cheap pico w is Micropython and its not as user friendly as circuit python

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment