diff --git a/micropython/examples/pico_inky/WIFI_CONFIG.py b/micropython/examples/pico_inky/WIFI_CONFIG.py new file mode 100644 index 00000000..b54d8867 --- /dev/null +++ b/micropython/examples/pico_inky/WIFI_CONFIG.py @@ -0,0 +1,2 @@ +SSID = "" +PSK = "" diff --git a/micropython/examples/pico_inky/bored_api.py b/micropython/examples/pico_inky/bored_api.py new file mode 100644 index 00000000..fac9a055 --- /dev/null +++ b/micropython/examples/pico_inky/bored_api.py @@ -0,0 +1,75 @@ +import WIFI_CONFIG +from network_manager import NetworkManager +import time +import uasyncio +import ujson +from urllib import urequest +from picographics import PicoGraphics, DISPLAY_INKY_PACK +from pimoroni import Button + + +""" +Simple demo to get a random activity from BoredAPI.com +""" + +WIFI_COUNTRY = "GB" # Changeme! + + +button_a = Button(12) +button_b = Button(13) +button_c = Button(14) + +graphics = PicoGraphics(DISPLAY_INKY_PACK) +graphics.set_font("bitmap8") + +WIDTH, HEIGHT = graphics.get_bounds() +ENDPOINT = "https://www.boredapi.com/api/activity" + + +def status_handler(mode, status, ip): + graphics.set_update_speed(2) + graphics.set_pen(15) + graphics.clear() + graphics.set_pen(0) + graphics.text("Network: {}".format(WIFI_CONFIG.SSID), 10, 10, scale=2) + status_text = "Connecting..." + if status is not None: + if status: + status_text = "Connection successful!" + else: + status_text = "Connection failed!" + + graphics.text(status_text, 10, 30, scale=2) + graphics.text("IP: {}".format(ip), 10, 60, scale=2) + graphics.update() + + +network_manager = NetworkManager(WIFI_COUNTRY, status_handler=status_handler) + + +def update(): + uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) + + url = ENDPOINT + print("Requesting URL: {}".format(url)) + j = ujson.load(urequest.urlopen(url)) + + print(j) + + graphics.set_update_speed(1) + graphics.set_pen(15) + graphics.clear() + graphics.set_pen(0) + graphics.text(j["activity"], 10, 10, wordwrap=WIDTH - 20, scale=2) + graphics.text(j["type"], 10, 108, scale=2) + + graphics.update() + + +# Run continuously. +# Be friendly to the API you're using! +while True: + update() + + while not button_a.is_pressed: + time.sleep(0.1) diff --git a/micropython/examples/pico_inky/clock.py b/micropython/examples/pico_inky/clock.py new file mode 100644 index 00000000..46b4ecba --- /dev/null +++ b/micropython/examples/pico_inky/clock.py @@ -0,0 +1,170 @@ +import time +import machine +from picographics import PicoGraphics, DISPLAY_INKY_PACK + + +# Buttons +button_a = machine.Pin(12, machine.Pin.IN, pull=machine.Pin.PULL_UP) +button_b = machine.Pin(13, machine.Pin.IN, pull=machine.Pin.PULL_UP) +button_c = machine.Pin(14, machine.Pin.IN, pull=machine.Pin.PULL_UP) + +# Display +graphics = PicoGraphics(DISPLAY_INKY_PACK) +WIDTH, HEIGHT = graphics.get_bounds() +graphics.set_update_speed(3) +graphics.set_font("gothic") + +# RTC +rtc = machine.RTC() + +cursors = ["hour", "minute", "year", "month", "day", "finish"] +set_clock = False +cursor = 0 +last = 0 + + +def days_in_month(month, year): + if month == 2 and ((year % 4 == 0 and year % 100 != 0) or year % 400 == 0): + return 29 + return (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[month - 1] + + +# Button handling function +def button(pin): + global last, set_clock, cursor, year, month, day, hour, minute + + adjust = 0 + changed = False + + time.sleep(0.01) + if pin.value(): + return + + if pin == button_b and not set_clock: + cursor = 0 + set_clock = True + draw_clock() + return + + if set_clock: + if pin == button_b: + cursor += 1 + cursor %= len(cursors) + changed = True + + if pin == button_a: + adjust = 1 + changed = True + + if pin == button_c: + adjust = -1 + changed = True + + if cursors[cursor] == "finish": + if adjust != 0: + set_clock = False + changed = True + if not set_clock: + rtc.datetime((year, month, day, 0, hour, minute, second, 0)) + + if cursors[cursor] == "year": + year += adjust + year = max(year, 2022) + day = min(day, days_in_month(month, year)) + + if cursors[cursor] == "month": + month += adjust + month = min(max(month, 1), 12) + day = min(day, days_in_month(month, year)) + + if cursors[cursor] == "day": + day += adjust + day = min(max(day, 1), days_in_month(month, year)) + + if cursors[cursor] == "hour": + hour += adjust + hour %= 24 + + if cursors[cursor] == "minute": + minute += adjust + minute %= 60 + + if changed: + draw_clock() + + +button_a.irq(trigger=machine.Pin.IRQ_FALLING, handler=button) +button_b.irq(trigger=machine.Pin.IRQ_FALLING, handler=button) +button_c.irq(trigger=machine.Pin.IRQ_FALLING, handler=button) + + +def draw_clock(): + hms = "{:02}:{:02}:{:02}".format(hour, minute, second) + ymd = "{:04}/{:02}/{:02}".format(year, month, day) + + hms_width = graphics.measure_text(hms, 1.8) + hms_offset = int((WIDTH / 2) - (hms_width / 2)) + h_width = graphics.measure_text(hms[0:2], 1.8) + mi_width = graphics.measure_text(hms[3:5], 1.8) + mi_offset = graphics.measure_text(hms[0:3], 1.8) + + ymd_width = graphics.measure_text(ymd, 1.0) + ymd_offset = int((WIDTH / 2) - (ymd_width / 2)) + y_width = graphics.measure_text(ymd[0:4], 1.0) + m_width = graphics.measure_text(ymd[5:7], 1.0) + m_offset = graphics.measure_text(ymd[0:5], 1.0) + d_width = graphics.measure_text(ymd[8:10], 1.0) + d_offset = graphics.measure_text(ymd[0:8], 1.0) + + graphics.set_pen(15) + graphics.clear() + graphics.set_pen(0) + + # No "thickness" setting in PG so, uh, fake it! + graphics.text(hms, hms_offset, 40, scale=1.8) + graphics.text(hms, hms_offset, 41, scale=1.8) + graphics.text(hms, hms_offset + 1, 40, scale=1.8) + graphics.text(hms, hms_offset - 1, 40, scale=1.8) + + # Double up the text to fill out the lines + graphics.text(ymd, ymd_offset, 100, scale=1.0) + graphics.text(ymd, ymd_offset, 101, scale=1.0) + + if set_clock: + if cursors[cursor] == "year": + graphics.line(ymd_offset, 120, ymd_offset + y_width, 120) + + if cursors[cursor] == "month": + graphics.line(ymd_offset + m_offset, 120, ymd_offset + m_offset + m_width, 120) + + if cursors[cursor] == "day": + graphics.line(ymd_offset + d_offset, 120, ymd_offset + d_offset + d_width, 120) + + if cursors[cursor] == "hour": + graphics.line(hms_offset, 70, hms_offset + h_width, 70) + + if cursors[cursor] == "minute": + graphics.line(hms_offset + mi_offset, 70, hms_offset + mi_offset + mi_width, 70) + + done_width = graphics.measure_text("done", scale=0.5) + graphics.text("done", WIDTH - done_width - 5, HEIGHT - 15, scale=0.5) + if cursors[cursor] == "finish": + graphics.line(WIDTH - done_width - 5, HEIGHT - 5, WIDTH - 5, HEIGHT - 5) + + graphics.update() + + +year, month, day, wd, hour, minute, second, _ = rtc.datetime() + +if (year, month, day) == (2021, 1, 1): + rtc.datetime((2022, 2, 28, 0, 12, 0, 0, 0)) + +last_second = second + +while True: + if not set_clock: + year, month, day, wd, hour, minute, second, _ = rtc.datetime() + if second != last_second: + draw_clock() + last_second = second + time.sleep(0.01) diff --git a/micropython/examples/pico_inky/network_manager.py b/micropython/examples/pico_inky/network_manager.py new file mode 100644 index 00000000..74026363 --- /dev/null +++ b/micropython/examples/pico_inky/network_manager.py @@ -0,0 +1,85 @@ +import rp2 +import network +import machine +import uasyncio + + +class NetworkManager: + _ifname = ("Client", "Access Point") + + def __init__(self, country="GB", client_timeout=30, access_point_timeout=5, status_handler=None): + rp2.country(country) + self._ap_if = network.WLAN(network.AP_IF) + self._sta_if = network.WLAN(network.STA_IF) + + self._mode = network.STA_IF + self._client_timeout = client_timeout + self._access_point_timeout = access_point_timeout + self._status_handler = status_handler + self.UID = ("{:02X}" * 8).format(*machine.unique_id()) + + def isconnected(self): + return self._sta_if.isconnected() or self._ap_if.isconnected() + + def ifaddress(self): + if self._sta_if.isconnected(): + return self._sta_if.ifconfig()[0] + if self._ap_if.isconnected(): + return self._ap_if.ifconfig()[0] + return '0.0.0.0' + + def disconnect(self): + if self._sta_if.isconnected(): + self._sta_if.disconnect() + if self._ap_if.isconnected(): + self._ap_if.disconnect() + + async def wait(self, mode): + while not self.isconnected(): + self._handle_status(mode, None) + await uasyncio.sleep_ms(1000) + + def _handle_status(self, mode, status): + if self._status_handler is not None: + self._status_handler(self._ifname[mode], status, self.ifaddress()) + + async def client(self, ssid, psk): + if self._sta_if.isconnected(): + return + + self._ap_if.disconnect() + self._ap_if.active(False) + self._ap_if.deinit() + + self._sta_if = network.WLAN(network.STA_IF) + self._sta_if.active(True) + self._sta_if.connect(ssid, psk) + + try: + await uasyncio.wait_for(self.wait(network.STA_IF), self._client_timeout) + self._handle_status(network.STA_IF, True) + + except uasyncio.TimeoutError: + self._sta_if.active(False) + self._handle_status(network.STA_IF, False) + raise RuntimeError("WIFI Client Failed") + + async def access_point(self): + if self._ap_if.isconnected(): + return + + self._sta_if.disconnect() + self._sta_if.active(False) + self._sta_if.deinit() + + self._ap_if = network.WLAN(network.AP_IF) + self._ap_if.active(True) + + try: + await uasyncio.wait_for(self.wait(network.AP_IF), self._access_point_timeout) + self._handle_status(network.AP_IF, True) + + except uasyncio.TimeoutError: + self._sta_if.active(False) + self._handle_status(network.AP_IF, False) + raise RuntimeError("WIFI AP Failed") diff --git a/micropython/examples/pico_inky/placekitten.py b/micropython/examples/pico_inky/placekitten.py new file mode 100644 index 00000000..08ddea9f --- /dev/null +++ b/micropython/examples/pico_inky/placekitten.py @@ -0,0 +1,86 @@ +import WIFI_CONFIG +from network_manager import NetworkManager +import uasyncio +from urllib import urequest +from picographics import PicoGraphics, DISPLAY_INKY_PACK +import jpegdec +import random + + +""" +Grab a random image from PlaceKitten.com +and display it on Inky Pack. + +Optionally cache to flash, if you need the RAM +for something else. +""" + +WIFI_COUNTRY = "GB" # Changeme! + + +graphics = PicoGraphics(DISPLAY_INKY_PACK) + +WIDTH, HEIGHT = graphics.get_bounds() +FILENAME = "placekitten.jpg" +ENDPOINT = "http://placekitten.com/{0}/{1}" + + +def status_handler(mode, status, ip): + graphics.set_update_speed(3) + graphics.set_pen(15) + graphics.clear() + graphics.set_pen(0) + graphics.text("Network: {}".format(WIFI_CONFIG.SSID), 10, 10, scale=2) + status_text = "Connecting..." + if status is not None: + if status: + status_text = "Connection successful!" + else: + status_text = "Connection failed!" + + graphics.text(status_text, 10, 30, scale=2) + graphics.text("IP: {}".format(ip), 10, 60, scale=2) + graphics.update() + + +graphics.set_font("bitmap8") +graphics.set_update_speed(1) + +network_manager = NetworkManager(WIFI_COUNTRY, status_handler=status_handler) +uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) + + +url = ENDPOINT.format(WIDTH, HEIGHT + random.randint(0, 10)) +print("Requesting URL: {}".format(url)) +socket = urequest.urlopen(url) + +# Load the image data into RAM (if you have enough!) +data = bytearray(1024 * 10) +socket.readinto(data) +socket.close() + + +""" +# Stream the image data from the socket onto disk in 1024 byte chunks +# if you're doing something else RAM intensive you might want to use this! +data = bytearray(1024) +with open(FILENAME, "wb") as f: + while True: + if socket.readinto(data) == 0: + break + f.write(data) +socket.close() +""" + +jpeg = jpegdec.JPEG(graphics) +jpeg.open_RAM(data) +jpeg.decode(0, 0) + +graphics.set_pen(15) +graphics.rectangle(0, HEIGHT - 14, WIDTH, 14) + +graphics.set_pen(0) +graphics.text(url, 5, HEIGHT - 9, scale=1) + +graphics.set_update_speed(1) +graphics.update() diff --git a/micropython/examples/pico_inky/quote_of_the_day.py b/micropython/examples/pico_inky/quote_of_the_day.py new file mode 100644 index 00000000..579e5daa --- /dev/null +++ b/micropython/examples/pico_inky/quote_of_the_day.py @@ -0,0 +1,88 @@ +import WIFI_CONFIG +from network_manager import NetworkManager +import time +import uasyncio +import ujson +from urllib import urequest +from picographics import PicoGraphics, DISPLAY_INKY_PACK + + +""" +Grab the quote of the day from Wikipedia. +""" + +WIFI_COUNTRY = "GB" # Changeme! + +graphics = PicoGraphics(DISPLAY_INKY_PACK) + +WIDTH, HEIGHT = graphics.get_bounds() +ENDPOINT = "https://en.wikiquote.org/w/api.php?format=json&action=expandtemplates&prop=wikitext&text={{{{Wikiquote:Quote%20of%20the%20day/{3}%20{2},%20{0}}}}}" +MONTHNAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] + + +last_date = "" + + +def parse_qotd(text): + text = text.split("\n") + return ( + text[6][2:].replace("[[", "").replace("]]", "").replace("
", '\n').replace("
", '\n'), # Quote + text[8].split("|")[2][5:-4] # Author + ) + + +def status_handler(mode, status, ip): + graphics.set_update_speed(2) + graphics.set_pen(15) + graphics.clear() + graphics.set_pen(0) + graphics.text("Network: {}".format(WIFI_CONFIG.SSID), 10, 10, scale=2) + status_text = "Connecting..." + if status is not None: + if status: + status_text = "Connection successful!" + else: + status_text = "Connection failed!" + + graphics.text(status_text, 10, 30, scale=2) + graphics.text("IP: {}".format(ip), 10, 60, scale=2) + graphics.update() + + +network_manager = NetworkManager(WIFI_COUNTRY, status_handler=status_handler) + +while True: + graphics.set_font("bitmap8") + graphics.set_update_speed(1) + + uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) + + date = list(time.localtime())[:3] + date.append(MONTHNAMES[date[1] - 1]) + + if "{3} {2}, {0}".format(*date) == last_date: + time.sleep(60) + continue + + url = ENDPOINT.format(*date) + print("Requesting URL: {}".format(url)) + j = ujson.load(urequest.urlopen(url)) + + text = j['expandtemplates']['wikitext'] + + text, author = parse_qotd(text) + + print(text) + + graphics.set_pen(15) + graphics.clear() + graphics.set_pen(0) + graphics.text("QoTD - {2} {3} {0:04d}".format(*date), 10, 10, scale=2) + graphics.text(text, 10, 30, wordwrap=WIDTH - 20, scale=1) + graphics.text(author, 10, 108, scale=1) + + graphics.update() + + last_date = "{3} {2}, {0}".format(*date) + + time.sleep(60)