diff --git a/examples/backgroundforscreen.jpg b/examples/backgroundforscreen.jpg new file mode 100644 index 0000000..7ff87ac Binary files /dev/null and b/examples/backgroundforscreen.jpg differ diff --git a/examples/backgroundforscreen.png b/examples/backgroundforscreen.png new file mode 100644 index 0000000..1119f45 Binary files /dev/null and b/examples/backgroundforscreen.png differ diff --git a/examples/display_jpg.py b/examples/display_jpg.py new file mode 100644 index 0000000..468f6fb --- /dev/null +++ b/examples/display_jpg.py @@ -0,0 +1,18 @@ +from explorer import Explorer2350 +import jpegdec + +board = Explorer2350() + +display = board.display + +# Create a new JPEG decoder for our PicoGraphics +j = jpegdec.JPEG(display) + +# Open the JPEG file +j.open_file("backgroundforscreen.jpg") + +# Decode the JPEG +j.decode(0, 0, jpegdec.JPEG_SCALE_FULL, dither=True) + +# Display the result +display.update() diff --git a/examples/display_png.py b/examples/display_png.py new file mode 100644 index 0000000..54ffa3c --- /dev/null +++ b/examples/display_png.py @@ -0,0 +1,24 @@ +# Display a PNG image on Explorer 2350 + +from explorer import Explorer2350 +import pngdec +import gc + +board = Explorer2350() + +display = board.display + +# run garbage collection - displaying large PNGs is resource intensive +gc.collect() + +# Create a new JPEG decoder for our PicoGraphics +p = pngdec.PNG(display) + +# Open the PNG file +p.open_file("backgroundforscreen.png") + +# Decode the PNG +p.decode(0, 0) + +# Display the result +display.update() diff --git a/examples/explorer-sensor-stick-demo.py b/examples/explorer-sensor-stick-demo.py new file mode 100644 index 0000000..b457e78 --- /dev/null +++ b/examples/explorer-sensor-stick-demo.py @@ -0,0 +1,36 @@ +# Display readings from the multi-sensor stick on the Explorer screen +from explorer import Explorer2350 +from breakout_ltr559 import BreakoutLTR559 +from lsm6ds3 import LSM6DS3 +from breakout_bme280 import BreakoutBME280 +import time + +board = Explorer2350() + +ltr = BreakoutLTR559(board.i2c) +lsm = LSM6DS3(board.i2c) +bme = BreakoutBME280(board.i2c) + +display = board.display + +# lets set up some pen colours to make drawing easier +WHITE = display.create_pen(255, 255, 255) +BLACK = display.create_pen(0, 0, 0) +RED = display.create_pen(255, 0, 0) +GREY = display.create_pen(125, 125, 125) + +while True: + lux, _, _, _, _, _, prox = ltr.get_reading() + ax, ay, az, gx, gy, gz = lsm.get_readings() + temperature, pressure, humidity = bme.read() + display.set_pen(BLACK) + display.clear() + display.set_pen(WHITE) + if lux is not None: + display.text(f"Lux: {lux:.0f}\nProx: {prox:.0f}", 0, 0, 320, 3) + if ax is not None: + display.text(f"Accelerometer:\nX: {ax:.0f}, Y: {ay:.0f}, \nZ: {az:.0f}\nGyro:\nX: {gx:.0f}, Y: {gy:.0f}, \nZ: {gz:.0f}", 0, 45, 320, 3) + if temperature is not None: + display.text(f"Temperature: {temperature:.2f}°C,\nHumidity: {humidity:.0f}%,\nPressure: {pressure/100:.0f}hPa", 0, 180, 320, 3) + display.update() + time.sleep(0.1) \ No newline at end of file diff --git a/examples/explorer.py b/examples/explorer.py new file mode 100644 index 0000000..4d18a61 --- /dev/null +++ b/examples/explorer.py @@ -0,0 +1,132 @@ +import gc +from machine import Pin, PWM +from pimoroni_i2c import PimoroniI2C +from servo import Servo +from picographics import PicoGraphics, DISPLAY_EXPLORER + +# IO Pin Constants +GP0 = 0 +GP1 = 1 +GP2 = 2 +GP3 = 3 +GP4 = 4 +GP5 = 5 + +A0 = 40 +A1 = 41 +A2 = 42 +A3 = 43 +A4 = 44 +A5 = 45 + +GPIOS = (GP0, GP1, GP2, GP3, GP4, GP5, A0, A1, A2, A3, A4, A5) +ADCS = (A0, A1, A2, A3, A4, A5) + + +# Index Constants +SERVO_1 = 0 +SERVO_2 = 1 +SERVO_3 = 2 +SERVO_4 = 3 + +SWITCH_A = 0 +SWITCH_B = 1 +SWITCH_C = 2 +SWITCH_X = 3 +SWITCH_Y = 4 +SWITCH_Z = 5 + +# Count Constants +NUM_GPIOS = 6 +NUM_ADCS = 6 +NUM_SERVOS = 4 +NUM_SWITCHES = 6 + + +class Explorer2350(): + SWITCH_A_PIN = 16 + SWITCH_B_PIN = 15 + SWITCH_C_PIN = 14 + SWITCH_X_PIN = 17 + SWITCH_Y_PIN = 18 + SWITCH_Z_PIN = 19 + + I2C_SDA_PIN = 20 + I2C_SCL_PIN = 21 + + USER_SW_PIN = 22 + + SERVO_1_PIN = 9 + SERVO_2_PIN = 8 + SERVO_3_PIN = 7 + SERVO_4_PIN = 6 + + PWM_AUDIO_PIN = 12 + AMP_EN_PIN = 13 + + AMP_CORRECTION = 4 + DEFAULT_VOLUME = 0.2 + + def __init__(self, init_servos=True): + # Free up hardware resources + gc.collect() + + self.display = PicoGraphics(display=DISPLAY_EXPLORER) + + # Set up the servos, if the user wants them + self.servos = None + if init_servos: + self.servos = [Servo(self.SERVO_1_PIN - i) for i in range(4)] + + # Set up the i2c for Qw/st and Breakout Garden + self.i2c = PimoroniI2C(self.I2C_SDA_PIN, self.I2C_SCL_PIN, 100000) + + # Set up the amp enable + self.__amp_en = Pin(self.AMP_EN_PIN, Pin.OUT) + self.__amp_en.off() + + self.audio_pwm = PWM(Pin(self.PWM_AUDIO_PIN)) + self.__volume = self.DEFAULT_VOLUME + + # Set up the user switch + self.__switch = Pin(self.USER_SW_PIN, Pin.IN, Pin.PULL_DOWN) + + def switch_pressed(self): + return self.__switch.value() + + def play_tone(self, frequency): + try: + self.audio_pwm.freq(frequency) + except ValueError: + self.play_silence() + raise ValueError("frequency of range. Expected greater than 0") + + corrected_volume = (self.__volume ** 4) # Correct for RC Filter curve + self.audio_pwm.duty_u16(int(32768 * corrected_volume)) + self.unmute_audio() + + def play_silence(self): + self.audio_pwm.freq(44100) + + corrected_volume = (self.__volume ** 4) # Correct for RC Filter curve + self.audio_pwm.duty_u16(int(32768 * corrected_volume)) + self.unmute_audio() + + def stop_playing(self): + self.audio_pwm.duty_u16(0) + self.mute_audio() + + def volume(self, volume=None): + if volume is None: + return self.__volume + + if volume < 0.01 or volume > 1.0: + raise ValueError("volume out of range. Expected 0.0 to 1.0") + + self.__volume = volume + + def mute_audio(self): + self.__amp_en.off() + + def unmute_audio(self): + self.__amp_en.on() \ No newline at end of file diff --git a/examples/multiple_servos.py b/examples/multiple_servos.py new file mode 100644 index 0000000..74a6b22 --- /dev/null +++ b/examples/multiple_servos.py @@ -0,0 +1,58 @@ +import time +import math +from explorer import Explorer2350 + +""" +Demonstrates how to control all of the servos on Explorer. +""" + +# Create a new Explorer +board = Explorer2350() + +# Enable all servos (this puts them at the middle) +for s in board.servos: + s.enable() +time.sleep(2) + +# Go to min +for s in board.servos: + s.to_min() +time.sleep(2) + +# Go to max +for s in board.servos: + s.to_max() +time.sleep(2) + +# Go back to mid +for s in board.servos: + s.to_mid() +time.sleep(2) + +SWEEPS = 3 # How many sweeps of the servo to perform +STEPS = 10 # The number of discrete sweep steps +STEPS_INTERVAL = 0.5 # The time in seconds between each step of the sequence +SWEEP_EXTENT = 90.0 # How far from zero to move the servo when sweeping + +# Do a sine sweep +for j in range(SWEEPS): + for i in range(360): + value = math.sin(math.radians(i)) * SWEEP_EXTENT + for s in board.servos: + s.value(value) + time.sleep(0.02) + +# Do a stepped sweep +for j in range(SWEEPS): + for i in range(0, STEPS): + for s in board.servos: + s.to_percent(i, 0, STEPS, 0.0 - SWEEP_EXTENT, SWEEP_EXTENT) + time.sleep(STEPS_INTERVAL) + for i in range(0, STEPS): + for s in board.servos: + s.to_percent(i, STEPS, 0, 0.0 - SWEEP_EXTENT, SWEEP_EXTENT) + time.sleep(STEPS_INTERVAL) + +# Disable the servos +for s in board.servos: + s.disable() \ No newline at end of file diff --git a/examples/single_servo.py b/examples/single_servo.py new file mode 100644 index 0000000..035080a --- /dev/null +++ b/examples/single_servo.py @@ -0,0 +1,54 @@ +import time +import math +from explorer import Explorer2350, SERVO_1 + +""" +Demonstrates how to control a single servo on Explorer. +""" + +# Create a new Explorer2350 +board = Explorer2350() + +# Access the servo from Explorer and enable it (this puts it at the middle) +s = board.servos[SERVO_1] + +print(board.servos) +s.enable() +time.sleep(2) + +# Go to min +s.to_min() +time.sleep(2) + +# Go to max +s.to_max() +time.sleep(2) + +# Go back to mid +s.to_mid() +time.sleep(2) + + +SWEEPS = 3 # How many sweeps of the servo to perform +STEPS = 10 # The number of discrete sweep steps +STEPS_INTERVAL = 0.5 # The time in seconds between each step of the sequence +SWEEP_EXTENT = 90.0 # How far from zero to move the servo when sweeping + + +# Do a sine sweep +for j in range(SWEEPS): + for i in range(360): + s.value(math.sin(math.radians(i)) * SWEEP_EXTENT) + time.sleep(0.02) + +# Do a stepped sweep +for j in range(SWEEPS): + for i in range(0, STEPS): + s.to_percent(i, 0, STEPS, 0.0 - SWEEP_EXTENT, SWEEP_EXTENT) + time.sleep(STEPS_INTERVAL) + for i in range(0, STEPS): + s.to_percent(i, STEPS, 0, 0.0 - SWEEP_EXTENT, SWEEP_EXTENT) + time.sleep(STEPS_INTERVAL) + +# Disable the servo +s.disable() \ No newline at end of file diff --git a/examples/tone_song.py b/examples/tone_song.py new file mode 100644 index 0000000..e0c823d --- /dev/null +++ b/examples/tone_song.py @@ -0,0 +1,131 @@ +import time +from explorer import Explorer2350 + +""" +This example shows you how you can use Explorer's audio output +with a speaker to play different notes and string them together into a bleepy tune. + +It uses code written by Avram Piltch - check out his Tom's Hardware article! +https://www.tomshardware.com/uk/how-to/buzzer-music-raspberry-pi-pico + +Press "User" button to start or stop the song. +""" + + +# This handy list converts notes into frequencies, which you can use with the inventor.play_tone function +TONES = { + "B0": 31, + "C1": 33, + "CS1": 35, + "D1": 37, + "DS1": 39, + "E1": 41, + "F1": 44, + "FS1": 46, + "G1": 49, + "GS1": 52, + "A1": 55, + "AS1": 58, + "B1": 62, + "C2": 65, + "CS2": 69, + "D2": 73, + "DS2": 78, + "E2": 82, + "F2": 87, + "FS2": 93, + "G2": 98, + "GS2": 104, + "A2": 110, + "AS2": 117, + "B2": 123, + "C3": 131, + "CS3": 139, + "D3": 147, + "DS3": 156, + "E3": 165, + "F3": 175, + "FS3": 185, + "G3": 196, + "GS3": 208, + "A3": 220, + "AS3": 233, + "B3": 247, + "C4": 262, + "CS4": 277, + "D4": 294, + "DS4": 311, + "E4": 330, + "F4": 349, + "FS4": 370, + "G4": 392, + "GS4": 415, + "A4": 440, + "AS4": 466, + "B4": 494, + "C5": 523, + "CS5": 554, + "D5": 587, + "DS5": 622, + "E5": 659, + "F5": 698, + "FS5": 740, + "G5": 784, + "GS5": 831, + "A5": 880, + "AS5": 932, + "B5": 988, + "C6": 1047, + "CS6": 1109, + "D6": 1175, + "DS6": 1245, + "E6": 1319, + "F6": 1397, + "FS6": 1480, + "G6": 1568, + "GS6": 1661, + "A6": 1760, + "AS6": 1865, + "B6": 1976, + "C7": 2093, + "CS7": 2217, + "D7": 2349, + "DS7": 2489, + "E7": 2637, + "F7": 2794, + "FS7": 2960, + "G7": 3136, + "GS7": 3322, + "A7": 3520, + "AS7": 3729, + "B7": 3951, + "C8": 4186, + "CS8": 4435, + "D8": 4699, + "DS8": 4978 +} + +# Put the notes for your song in here! +SONG = ("F6", "F6", "E6", "F6", "F5", "P", "F5", "P", "C6", "AS5", "A5", "C6", "F6", "P", "F6", "P", "G6", "FS6", "G6", "G5", "P", "G5", "P", "G6", "F6", "E6", "D6", "C6", "P", "C6", "P", "D6", "E6", "F6", "E6", "D6", "C6", "D6", "C6", "AS5", "A5", "AS5", "A5", "G5", "F5", "G5", "F5", "E5", "D5", "C5", "D5", "E5", "F5", "G5", "AS5", "A5", "G5", "A5", "F5", "P", "F5") + +NOTE_DURATION = 0.150 # The time (in seconds) to play each note for. Change this to make the song play faster or slower + + +# Create a new Explorer +board = Explorer2350() + +# Set the volume of the speaker output +board.volume(0.25) + +# Play the song +for i in range(len(SONG)): + if SONG[i] == "P": + # This is a "pause" note, so stop the motors + board.play_silence() + else: + # Get the frequency of the note and play it + board.play_tone(TONES[SONG[i]]) + + time.sleep(NOTE_DURATION) + +board.stop_playing() diff --git a/examples/weather_station_bme280.py b/examples/weather_station_bme280.py index f79c367..b45cc8c 100644 --- a/examples/weather_station_bme280.py +++ b/examples/weather_station_bme280.py @@ -1,16 +1,17 @@ -# This example lets you plug a BME280 breakout into your Pico Explorer and make a little indoor weather station, with barometer style descriptions. +# Plug a BME280 breakout into Explorer 2350 and make a little indoor weather station, with barometer style descriptions. import time from breakout_bme280 import BreakoutBME280 -from machine import I2C, Pin -from picographics import PicoGraphics, DISPLAY_EXPLORER +from explorer import Explorer2350 +import jpegdec -# set up the hardware -display = PicoGraphics(display=DISPLAY_EXPLORER) -i2c = I2C(0, sda=Pin(20), scl=Pin(21)) -bme = BreakoutBME280(i2c, address=0x76) +# Set up the hardware +board = Explorer2350() +display = board.display -# lets set up some pen colours to make drawing easier +bme = BreakoutBME280(board.i2c, address=0x76) + +# set up some pen colours to make drawing easier TEMPCOLOUR = display.create_pen(0, 0, 0) # this colour will get changed in a bit WHITE = display.create_pen(255, 255, 255) BLACK = display.create_pen(0, 0, 0) @@ -95,8 +96,16 @@ def describe_humidity(humidity): while True: - display.set_pen(BLACK) - display.clear() + # Create a new JPEG decoder for our PicoGraphics + j = jpegdec.JPEG(display) + + # Attempt to display a fancy background, if the file is present + try: + j.open_file("backgroundforscreen.jpg") + j.decode(0, 0, jpegdec.JPEG_SCALE_FULL, dither=True) + except: + display.set_pen(BLACK) + display.clear() # read the sensors temperature, pressure, humidity = bme.read() @@ -105,18 +114,18 @@ def describe_humidity(humidity): # draw a thermometer/barometer thingy display.set_pen(GREY) - display.circle(190, 190, 40) - display.rectangle(180, 45, 20, 140) + display.circle(250, 175, 40) + display.rectangle(240, 30, 20, 140) # switch to red to draw the 'mercury' display.set_pen(RED) - display.circle(190, 190, 30) + display.circle(250, 175, 30) thermometerheight = int(120 / 30 * temperature) if thermometerheight > 120: thermometerheight = 120 if thermometerheight < 1: thermometerheight = 1 - display.rectangle(186, 50 + 120 - thermometerheight, 10, thermometerheight) + display.rectangle(245, 155 - thermometerheight, 10, thermometerheight) # drawing the temperature text display.set_pen(WHITE) @@ -127,17 +136,17 @@ def describe_humidity(humidity): display.text(describe_temperature(temperature), 10, 60, 240, 3) # and the pressure text - display.text("pressure:", 10, 90, 240, 3) - display.text('{:.0f}'.format(pressurehpa) + 'hPa', 10, 110, 240, 5) - display.text(describe_pressure(pressurehpa), 10, 140, 240, 3) + display.text("pressure:", 10, 85, 240, 3) + display.text('{:.0f}'.format(pressurehpa) + 'hPa', 10, 105, 240, 5) + display.text(describe_pressure(pressurehpa), 10, 135, 240, 3) # and the humidity text - display.text("humidity:", 10, 170, 240, 3) - display.text('{:.0f}'.format(humidity) + '%', 10, 190, 240, 5) - display.text(describe_humidity(humidity), 10, 220, 240, 3) + display.text("humidity:", 10, 160, 240, 3) + display.text('{:.0f}'.format(humidity) + '%', 10, 180, 240, 5) + display.text(describe_humidity(humidity), 10, 210, 240, 3) # time to update the display display.update() - # waits for 1 second and clears to BLACK - time.sleep(1) + # wait for a short time to stop Thonny from freaking out + time.sleep(0.1) \ No newline at end of file