From 7b127d5f1943d310e23b5a0dde621ba3c6bb49d7 Mon Sep 17 00:00:00 2001 From: Gee Bartlett Date: Fri, 18 Nov 2022 14:30:22 +0000 Subject: [PATCH] Driver/st7567 (#581) Add ST7567/Pico GFX examples --- micropython/examples/gfx_pack/README.md | 37 +++ micropython/examples/gfx_pack/calc.py | 54 ++++ micropython/examples/gfx_pack/sunrise.py | 280 +++++++++++++++++++++ micropython/examples/gfx_pack/zoo_facts.py | 196 +++++++++++++++ 4 files changed, 567 insertions(+) create mode 100644 micropython/examples/gfx_pack/calc.py create mode 100644 micropython/examples/gfx_pack/sunrise.py create mode 100644 micropython/examples/gfx_pack/zoo_facts.py diff --git a/micropython/examples/gfx_pack/README.md b/micropython/examples/gfx_pack/README.md index fb0aaeb9..8f012f82 100644 --- a/micropython/examples/gfx_pack/README.md +++ b/micropython/examples/gfx_pack/README.md @@ -5,11 +5,15 @@ - [Basic Examples](#basic-examples) - [Balls Demo](#balls-demo) - [Button Test](#button-test) + - [Calc](#calc) - [Rainbow](#rainbow) - [Snake](#snake) - [Advanced Examples](#advanced-examples) - [CO2](#co2) - [Thermometer](#thermometer) +- [Wireless Examples](#wireless-examples) + - [Sunrise / Sunset](#sunrise--sunset) + - [Zoo Facts](#zoo-facts) ## About Pico GFX Pack @@ -43,6 +47,12 @@ LCD demo with a bunch of bouncy balls! Shows how to read the buttons, display text and change the colour of the RGBW backlight. +### Calc + +[calc.py](calc.py) + +This example draws a nice sine wave on the display, reminiscent of the graphical calculators from the 90s. + ### Rainbow [rainbow.py](rainbow.py) @@ -86,3 +96,30 @@ To use the Pico's internal temperature sensor in place of the BME68x breakout, j - :link: [BME680 breakout store page](https://shop.pimoroni.com/products/bme680-breakout) - :link: [BME688 breakout store page](https://shop.pimoroni.com/products/bme688-breakout) + +## Wireless Examples + +These wireless examples need `network_manager.py` and `WIFI_CONFIG.py` from the `common` directory to be saved to your Pico W. Open up `WIFI_CONFIG.py` in Thonny to add your wifi details (and save it when you're done). + +- [micropython/examples/common](../../examples/common) + +### Sunrise / Sunset + +[sunrise.py](sunrise.py) + +This sunrise / sunset simulator displays information from the Sunrise Sunset API, and also shows how to use a 16x16 animated sprite. +Find out more about Sunrise Sunset API here: https://sunrise-sunset.org/api + +### Zoo Facts + +[zoo_facts.py](zoo_facts.py) + +Downloads a list of five zoo animals and displays their vital statistics. +Find out more about Zoo Animal API here: https://zoo-animal-api.herokuapp.com/ + +- A = Next animal +- B = Last animal +- D = Show stats +- E = Fetch a different 5 animals + + diff --git a/micropython/examples/gfx_pack/calc.py b/micropython/examples/gfx_pack/calc.py new file mode 100644 index 00000000..b8bbfd4a --- /dev/null +++ b/micropython/examples/gfx_pack/calc.py @@ -0,0 +1,54 @@ +''' +calc.py +Graphical demo for the GFX Pack with Raspberry Pi Pico +This example draws a nice sine wave on the display, +reminiscent of the graphical calculators from the 90s. +''' + +from math import radians, sin +from gfx_pack import GfxPack + +gp = GfxPack() +display = gp.display +WIDTH, HEIGHT = display.get_bounds() +display.set_backlight(1.0) # Turns on the white component of the backlight +gp.set_backlight(0, 0, 0) # Leaves the other backlight colours off + + +def draw_axis(): + display.line(0, 32, 128, 32) + display.line(64, 8, 64, 56) + for i in range(8): + display.line(64, (i * 12) + 8, 67, (i * 12) + 8) + display.line((i * 16), 32, (i * 16), 34) + + +def draw_sine(offset=0, vlines=False): + for x in range(128): + angle = 720 / 128 * x + y = int((sin(radians(angle + offset)) * 24) + 32) + display.pixel(x, y) + if vlines: + if x % 2 == 0: + display.line(x, 32, x, y) + + +def draw_text(): + display.text("Remember A-Level Maths!", 0, 0, WIDTH, 1) + display.text("y=sin(x) x=(-360:360)", 0, 58, WIDTH, 1) + + +# Clear display +display.set_pen(0) +display.clear() + +# Set pen to black +display.set_pen(15) + +# Draw the details in the PicoGraphics frame buffer +draw_axis() +draw_sine(0, True) +draw_text() + +# Update the display +display.update() diff --git a/micropython/examples/gfx_pack/sunrise.py b/micropython/examples/gfx_pack/sunrise.py new file mode 100644 index 00000000..af6532e9 --- /dev/null +++ b/micropython/examples/gfx_pack/sunrise.py @@ -0,0 +1,280 @@ +''' +sunrise.py +This example is for the Pico W with GFX Pack. +This displays information from the Sunrise Sunset API: +Find out more here - https://sunrise-sunset.org/api +Also shows how to use a 16x16 animated sprite. +''' +import WIFI_CONFIG +import time +from math import radians, sin, cos +from random import randint +from gfx_pack import GfxPack +from network_manager import NetworkManager +import ntptime +import urequests +import uasyncio +import machine + + +# Helper class so that the different formats of time can be converted into one comparable format +# Makes up for the lack of a datetime module in MicroPython +class TimeObj: + def __init__(self): + self.secs = 0 + self.mins = 0 + self.hours = 0 + self.PM = False + + # Returns time variables as a tuple (h, m, s) + def get_time(self): + pm_hrs = 0 + if self.PM: + pm_hrs = 12 + return (self.hours + pm_hrs, self.mins, self.secs) + + # Returns time variables as a single string + def get_str(self): + h, m, s = self.get_time() + return "{0:02d}:{1:02d}:{2:02d}".format(h, m, s) + + # Set time variables from the sunrise-sunset API + def parse_api(self, apiStr): + strsplit = apiStr.split() + if strsplit[1] == 'PM': + self.PM = True + timesplit = strsplit[0].split(':') + self.hours = int(timesplit[0]) + self.mins = int(timesplit[1]) + self.secs = int(timesplit[2]) + + # Set time variables form + def parse_localtime(self, localtpl): + yr, mo, dy, self.hours, self.mins, self.secs, un1, un2 = localtpl + + # Returns number of seconds since midnight + def get_total_secs(self): + seconds = 0 + if self.PM: + seconds += 43200 # seconds in the first 12 hours + seconds += (self.hours * 3600) + seconds += (self.mins * 60) + seconds += self.secs + return seconds + + +# Instances of TimeObj helper class defined above +sunrise_obj = TimeObj() +sunset_obj = TimeObj() +currenttime = TimeObj() + +# Coordinates for Sheffield-on-Sea, UK +lng = -1.4659 +lat = 53.3829 +# Coordinates for LA, USA +# lng = -118.2437 +# lat = 34.0522 +URL = 'https://api.sunrise-sunset.org/json?lat={0}&lng={1}&date=today'.format(lat, lng) +rtc = machine.RTC() + +gp = GfxPack() +display = gp.display +sys_status = "Starting" +WIDTH, HEIGHT = display.get_bounds() +display.set_backlight(1) # turn on the white component of the backlight + +# Generate hill heights +hills1 = [randint(10, 18), randint(10, 18), randint(10, 18), randint(10, 18), randint(10, 18)] +hills2 = [randint(10, 18), randint(10, 18)] +hills3 = [randint(10, 18), randint(10, 18)] + +# Sprite information for sun icon +sun = [ + [ + 0b0000000100000000, + 0b0000000000000000, + 0b0010011111001000, + 0b0000100000100000, + 0b0001000000010000, + 0b0010000000001000, + 0b0010010001001000, + 0b0010000100001000, + 0b0010000000001000, + 0b0010000000001000, + 0b0001001110010000, + 0b0000100000100000, + 0b0010011111001000, + 0b0000000000000000, + 0b0000000100000000, + 0b0000000000000000 + ], + [ + 0b0000000000000000, + 0b0100000000000100, + 0b0000011111000000, + 0b0000100000100000, + 0b0001000000010000, + 0b0010010001001000, + 0b0010010001001000, + 0b1010000100001010, + 0b0010000000001000, + 0b0010010001001000, + 0b0001001110010000, + 0b0000100000100000, + 0b0000011111000000, + 0b0100000000000100, + 0b0000000000000000, + 0b0000000000000000 + ], + [ + 0b0000000100000000, + 0b0100000000000100, + 0b0010011111001000, + 0b0000100000100000, + 0b0001000000010000, + 0b0010010001001000, + 0b0010010001001000, + 0b1010000100001010, + 0b0010000000001000, + 0b0010010001001000, + 0b0001001110010000, + 0b0000100000100000, + 0b0010011111001000, + 0b0100000000000100, + 0b0000000100000000, + 0b0000000000000000 + ] +] + + +def get_data(): + # open the json file + print(f'Requesting URL: {URL}') + r = urequests.get(URL) + # open the json data + j = r.json() + print('Data obtained!') + r.close() + return j + + +def get_sunrise(): + sun_json = get_data() + sunrise = sun_json['results']['sunrise'] + sunrise_obj.parse_api(sunrise) + sunset = sun_json['results']['sunset'] + sunset_obj.parse_api(sunset) + + +def display_status(): + global sys_status + display.set_pen(0) + display.clear() + display.set_pen(15) + display.text(sys_status, 0, 0, WIDTH, 1) + display.update() + + +def status_handler(mode, status, ip): + # reports wifi connection status + global sys_status + print(mode, status, ip) + sys_status = 'Mode: {0} Connected: {1} IP: {2}'.format(mode, status, ip) + display_status() + display.update() + print('Connecting to wifi...') + if status is not None: + if status: + print('Wifi connection successful!') + else: + print('Wifi connection failed!') + + +def calc_circle_points(ori_x, ori_y, r, deg): + rads = radians(deg - 180) + x = int(cos(rads) * r) + ori_x + y = int(sin(rads) * r) + ori_y + return x, y + + +def to_seconds(hour, mins, secs, isPM=False): + seconds = 0 + if isPM: + seconds += 43200 # Seconds in the first 12 hours + seconds += (hour * 3600) + seconds += (mins * 60) + seconds += secs + return seconds + + +def draw_hills(): + display.set_pen(12) + for x in range(5): + display.circle(30 * x, 64, hills1[x]) + display.set_pen(9) + for x in range(2): + display.circle(60 * x + 15, 64, hills2[x]) + display.set_pen(3) + for x in range(2): + display.circle(60 * x + 30 + 15, 64, hills3[x]) + + +def draw_text(): + display.set_pen(15) + display.text("Sun Rise-Set API demo", 0, 0, WIDTH, 1) + display.text("Sunrise: {0}".format(sunrise_obj.get_str()), 0, 8, WIDTH, 1) + display.text("Sunset: {0}".format(sunset_obj.get_str()), 0, 16, WIDTH, 1) + display.text("{0}".format(currenttime.get_str()), 0, 24, WIDTH, 1) + + +def draw_sun(sunrise, sunset, time, cycle): + sunlight_range = sunset - sunrise + angle = int((180 / sunlight_range) * (time - sunrise)) % 360 + pos_x, pos_y = calc_circle_points(64 - 16, 54, 50, angle) + display.set_pen(15) + if angle > 180: + gp.set_backlight(0, 0, 255) + elif angle < 90: + r = 255 + g = ((angle / 100) * 90) + b = 0 + gp.set_backlight(r, g, b) + elif angle > 90: + r = 255 + g = 100 - (((angle - 90) / 100) * 90) + b = 0 + gp.set_backlight(r, g, b) + for y in range(16): + for x in range(16): + if sun[cycle][y] & (1 << x): + display.pixel(x + pos_x, int((y + pos_y))) + + +try: + network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler) + uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) +except Exception as e: + print(f'Wifi connection failed! {e}') + +get_sunrise() +ntptime.settime() +currenttime.parse_localtime(time.localtime()) + +count = 0 # Counter for animation +display.set_backlight(0.5) # Dim white backlight to help colours show + +while True: + # Update current time class instance from RTC + currenttime.parse_localtime(time.localtime()) + count += 1 + + display.set_pen(0) + display.clear() + + # Uncomment for the animation + # draw_sun(0, 180, count % 180, count % 3) + draw_sun(sunrise_obj.get_total_secs(), sunset_obj.get_total_secs(), currenttime.get_total_secs(), count % 3) + draw_hills() + draw_text() + display.update() + time.sleep(0.2) diff --git a/micropython/examples/gfx_pack/zoo_facts.py b/micropython/examples/gfx_pack/zoo_facts.py new file mode 100644 index 00000000..b3f9eb48 --- /dev/null +++ b/micropython/examples/gfx_pack/zoo_facts.py @@ -0,0 +1,196 @@ +''' +zoo_facts.py +This example is for the Pico W with GFX Pack. +It uses the Zoo Animal API to download a list of 5 animals, +then displays them on the GFX Pack +A = Next animal +B = Last animal +D = Show stats +E = Fetch a different 5 animals +Find out more about Zoo Animal API here: +https://zoo-animal-api.herokuapp.com/ +''' +import WIFI_CONFIG +import time +from gfx_pack import GfxPack, SWITCH_A, SWITCH_B, SWITCH_D, SWITCH_E +from network_manager import NetworkManager +import urequests +import uasyncio + +URL = 'https://zoo-animal-api.herokuapp.com/animals/rand/5' + +gp = GfxPack() +display = gp.display + +WIDTH, HEIGHT = display.get_bounds() +display.set_backlight(0.5) # turn off the white component of the backlight +animals = [] +animal_number = 0 +stat_page = False +sys_status = "STATUS" + + +# Data class for containing the animal facts +class Animal: + def __init__(self): + self.name = "" + self.latin_name = "" + self.animal_type = "" + self.habitat = "" + self.diet = "" + self.length_max = "" + self.weight_max = "" + self.lifespan = "" + + def process_json(self, json): + print(json['name']) + self.name = json['name'] + self.latin_name = json['latin_name'] + self.animal_type = json['animal_type'] + self.habitat = json['habitat'] + self.diet = json['diet'] + self.length_max = json['length_max'] + self.weight_max = json['weight_max'] + self.lifespan = json['lifespan'] + + +def get_data(): + # open the json file + print(f'Requesting URL: {URL}') + r = urequests.get(URL) + # open the json data + j = r.json() + print('Data obtained!') + r.close() + return j + + +def get_animals(): + global sys_status + animals = [] + sys_status = 'Getting Animals' + display_status() + display.update() + json_data = get_data() + for index in range(len(json_data)): + new_animal = Animal() + new_animal.process_json(json_data[index]) + animals.append(new_animal) + return animals + + +def display_status(): + global sys_status + display.set_pen(0) # Set pen to white + display.clear() + display.set_pen(15) + display.text(sys_status, 0, 0, WIDTH, 1) + display.update() + + +def status_handler(mode, status, ip): + # reports wifi connection status + global sys_status + print(mode, status, ip) + sys_status = 'Mode: {0} Connected: {1} IP: {2}'.format(mode, status, ip) + display_status() + display.update() + print('Connecting to wifi...') + if status is not None: + if status: + print('Wifi connection successful!') + else: + print('Wifi connection failed!') + + +def display_animal(animal, stat_page): + display.set_pen(0) # Set pen to white + display.clear() + display.set_pen(15) + if stat_page is False: + display.text('Animal Info {0}'.format(animal_number), 0, 0, WIDTH, 1) + display.text('Name: {0}'.format(animal.name[:19]), 0, 10, WIDTH, 1) + display.text('Latin: {0}'.format(animal.latin_name[:19]), 0, 20, WIDTH, 1) + display.text('Type: {0}'.format(animal.animal_type[:19]), 0, 30, WIDTH, 1) + display.text('Habitat: {0}'.format(animal.habitat[:19]), 0, 40, WIDTH, 1) + display.text('Diet: {0}'.format(animal.diet[:19]), 0, 50, WIDTH, 1) + + else: + display.text('Animal Stats {0}'.format(animal_number), 0, 0, WIDTH, 1) + display.text('Max Length: {0}'.format(animal.length_max), 0, 10, WIDTH, 1) + display.text('Max Weight: {0}'.format(animal.weight_max), 0, 20, WIDTH, 1) + display.text('Lifespan: {0}'.format(animal.lifespan), 0, 30, WIDTH, 1) + display.update() + + +try: + network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler) + uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) +except Exception as e: + print(f'Wifi connection failed! {e}') + + +# From CPython Lib/colorsys.py +def hsv_to_rgb(h, s, v): + if s == 0.0: + return v, v, v + i = int(h * 6.0) + f = (h * 6.0) - i + p = v * (1.0 - s) + q = v * (1.0 - s * f) + t = v * (1.0 - s * (1.0 - f)) + i = i % 6 + if i == 0: + return v, t, p + if i == 1: + return q, v, p + if i == 2: + return p, v, t + if i == 3: + return p, q, v + if i == 4: + return t, p, v + if i == 5: + return v, p, q + + +# some variables to keep track of rainbow background +h = 0 + +display.set_font("bitmap8") + +animals = get_animals() +display.set_backlight(0) +while True: + + h += 1 + r, g, b = [int(255 * c) for c in hsv_to_rgb(h / 360.0, 1.0, 1.0)] # rainbow magic + gp.set_backlight(r, g, b) # Set backlight to a converted HSV value + display.set_pen(0) # Set pen to white + display.clear() + display.set_pen(15) # Set pen to black + # Draw text + display_animal(animals[animal_number], stat_page) + + if gp.switch_pressed(SWITCH_B): + animal_number += 1 + if animal_number > 4: + animal_number = 0 + display_animal(animals[animal_number], stat_page) + time.sleep(0.4) + + elif gp.switch_pressed(SWITCH_A): + animal_number -= 1 + if animal_number < 0: + animal_number = 4 + display_animal(animals[animal_number], stat_page) + time.sleep(0.4) + + elif gp.switch_pressed(SWITCH_D): + stat_page = not stat_page + display_animal(animals[animal_number], stat_page) + time.sleep(0.4) + + elif gp.switch_pressed(SWITCH_E): + animals = get_animals() + display_animal(animals[animal_number], stat_page)