CircuitPython Code

Trinket M0 boards can run CircuitPython — a different approach to programming compared to Arduino sketches. In fact, CircuitPython comes factory pre-loaded on Trinket M0. If you’ve overwritten it with an Arduino sketch, or just want to learn the basics of setting up and using CircuitPython, this is explained in the Adafruit Trinket M0 guide.

These directions are specific to the "M0” boards. The original Trinket with an 8-bit AVR microcontroller doesn’t run CircuitPython…for those boards, use the Arduino sketch on the “Arduino code” page of this guide.

Below is CircuitPython code that works similarly (though not exactly the same) as the Arduino sketch shown on a prior page. To use this, plug the Trinket M0 into USB…it should show up on your computer as a small flash drive…then edit the file “main.py” with your text editor of choice. Select and copy the code below and paste it into that file, entirely replacing its contents (don’t mix it in with lingering bits of old code). When you save the file, the code should start running almost immediately (if not, see notes at the bottom of this page).

If Trinket M0 doesn’t show up as a drive, follow the Trinket M0 guide link above to prepare the board for CircuitPython.

"""
This Code uses the:
* Adafruit LCD backpack using MCP23008 I2C expander
* Maxbotic LV-EZ1 Ultrasonic Sensor

Tested with the Trinket M0
The ultrasonic sensor and pin use should be Gemma M0 compatible
This sketch reads the LV-EZ1 by pulse count
Then prints the distance to the LCD and python console

The circuit:
* 5V to Trinket M0 USB or BAT pin, I2C Backpack 5V and EZ1 +5
* GND to Trinket M0 GND pin, I2C Backpack GND and EZ1 GND
* Display I2C Backpack SLK to Trinket GPIO #2
* Display I2C backpack SDA to Trinket GPIO #0
* LV-EZ1 Ultrasonic Sensor PW pin to Trinket GPIO #1
* Backlight can be hard wired by connecting LCD pin 16, 17 or 18 to GND
"""

import time

import adafruit_character_lcd
import board
import busio
import pulseio

ez1pin = board.D1  # Trinket GPIO #1

# i2c LCD initialize bus and class
i2c = busio.I2C(board.SCL, board.SDA)
cols = 16
rows = 2
lcd = adafruit_character_lcd.Character_LCD_I2C(i2c, cols, rows)

# calculated mode or median distance
mode_result = 0

# pulseio can store multiple pulses
# read in time for pin to transition
samples = 18
pulses = pulseio.PulseIn(board.D1, maxlen=samples)

# sensor reads which are in range will be stored here
rangevalue = [0, 0, 0, 0, 0, 0, 0, 0, 0]

# 25ms sensor power up pause
time.sleep(.25)


def tof_cm(time_of_flight):
    """
    EZ1 ultrasonic sensor is measuring "time of flight"
    Converts time of flight into distance in centimeters
    """
    convert_to_cm = 58
    cm = time_of_flight / convert_to_cm

    return cm


def tof_inches(time_of_flight):
    """
    EZ1 ultrasonic sensor is measuring "time of flight"
    Converts time of flight into distance in inches
    """
    convert_to_inches = 147
    inches = time_of_flight / convert_to_inches

    return inches


def find_mode(x):
    """
    find the mode (most common value reported)
    will return median (center of sorted list)
    should mode not be found
    """
    n = len(x)

    max_count = 0
    mode = 0
    bimodal = 0
    counter = 0
    index = 0

    while index < (n - 1):
        prev_count = counter
        counter = 0

        while (x[index]) == (x[index + 1]):
            counter += 1
            index += 1

        if (counter > prev_count) and (counter > max_count):
            mode = x[index]
            max_count = counter
            bimodal = 0

        if counter == 0:
            index += 1

        # If the dataset has 2 or more modes.
        if counter == max_count:
            bimodal = 1

        # Return the median if there is no mode.
        if (mode == 0) or (bimodal == 1):
            mode = x[int(n / 2)]

        return mode


while True:

    # wait between samples
    time.sleep(.5)

    if len(pulses) == samples:
        j = 0  # rangevalue array counter

        # only save the values within range
        # range readings take 49mS
        # pulse width is .88mS to 37.5mS
        for i in range(0, samples):
            tof = pulses[i]  # time of flight - PWM HIGH

            if 880 < tof < 37500:
                if j < len(rangevalue):
                    rangevalue[j] = tof_cm(tof)
                    j += 1

        # clear pulse samples
        pulses.clear()  # clear all values in pulses[]

        # sort samples
        rangevalue = sorted(rangevalue)

        # returns mode or median
        mode_result = int(find_mode(rangevalue))

        # python console prints both centimeter and inches distance
        cm2in = .393701
        mode_result_in = mode_result * cm2in
        print(mode_result, "cm", "\t\t", int(mode_result_in), "in")

        # result must be in char/string format for LCD printing
        digit_string = str(mode_result)

        lcd.clear()
        lcd.message("Range: ")  # write to LCD
        lcd.message("    ")
        lcd.message(digit_string)
        lcd.message("cm")

        time.sleep(2)

Installing Libraries:

The adafruit_character_lcd and adafruit_bus_device libraries must be installed for the above code to run correctly. The latest version of the Adafruit CircuitPython Library Bundle contains both libraries. You want to download the latest stable mpy bundle which will have a filename like this:

adafruit-circuitpython-bundle-x.x.x-mpy-date.zip

The Trinket M0 has limited space, but so in this case we will be selective about which files are copied over to the CIRCUITPY drive. A detailed explanation for installing libraries is available.

Create these two directories and copy the following files from the unzip'd CircuitPython Library Bundle to the CIRCUITPY drive to a new folder called 'lib'. 

  • adafruit_character_lcd
    • ./adafruit_character_lcd/character_lcd.mpy
    • ./adafruit_character_lcd/__init__.py
    • ./adafruit_character_lcd/character_lcd_rgb.mpy
    • ./adafruit_character_lcd/mcp23008.mpy
  • adafruit_bus_device
    • ./adafruit_bus_device/__init__.py
    • ./adafruit_bus_device/i2c_device.mpy
This guide was first published on Oct 01, 2013. It was last updated on Oct 01, 2013. This page (CircuitPython Code) was last updated on Nov 16, 2019.