From afea765b71bfbbd722141c8deef19753368b807d Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 14 Jul 2022 14:43:51 +0100 Subject: [PATCH] Inky Frame: MicroPython Examples. Co-authored-by: thirdr --- micropython/examples/inky_frame/README.md | 63 ++++++ .../examples/inky_frame/WIFI_CONFIG.py | 3 + .../inky_frame/inky_frame_daily_activity.py | 138 +++++++++++++ .../examples/inky_frame/inky_frame_news.py | 188 ++++++++++++++++++ .../inky_frame/inky_frame_placekitten.py | 68 +++++++ .../inky_frame/inky_frame_quote_of_the_day.py | 169 ++++++++++++++++ .../inky_frame/inky_frame_random_joke.py | 88 ++++++++ .../inky_frame/inky_frame_xkcd_daily.py | 74 +++++++ 8 files changed, 791 insertions(+) create mode 100644 micropython/examples/inky_frame/README.md create mode 100644 micropython/examples/inky_frame/WIFI_CONFIG.py create mode 100644 micropython/examples/inky_frame/inky_frame_daily_activity.py create mode 100644 micropython/examples/inky_frame/inky_frame_news.py create mode 100644 micropython/examples/inky_frame/inky_frame_placekitten.py create mode 100644 micropython/examples/inky_frame/inky_frame_quote_of_the_day.py create mode 100644 micropython/examples/inky_frame/inky_frame_random_joke.py create mode 100644 micropython/examples/inky_frame/inky_frame_xkcd_daily.py diff --git a/micropython/examples/inky_frame/README.md b/micropython/examples/inky_frame/README.md new file mode 100644 index 00000000..9f9feb97 --- /dev/null +++ b/micropython/examples/inky_frame/README.md @@ -0,0 +1,63 @@ +# Pico Enviro+ MicroPython Examples + +- [PicoGraphics](#picographics) +- [Examples](#examples) + - [Daily Activity](#daily-activity) + - [News](#news) + - [PlaceKitten](#placekitten) + - [Quote of the Day](#quote-of-the-day) + - [Random Joke](#random-joke) + - [XKCD Daily](#xkcd-daily) + +## PicoGraphics + +You can draw on Inky Frame using our tiny PicoGraphics display library. +- [PicoGraphics MicroPython function reference](../../modules/picographics) + +## Examples + +The 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). + +You'll also need to install the `micropython-urllib.urequest` library using Thonny's 'Tools' > 'Manage Packages' or `common/lib/urllib` which contains a compiled `.mpy` version that uses less RAM. You should place this directory in `lib` on your Pico W. + +Finally for examples loading images, you'll need `sdcard.mpy` from `common/lib`. You should place this file in `lib` on your Pico W. + +### Daily Activity +[inky_frame_daily_activity.py](inky_frame_daily_activity.py) + +### News +[inky_frame_news.py](inky_frame_news.py) + +Display headlines from BBC news. + +### PlaceKitten +[inky_frame_placekitten.py](inky_frame_placekitten.py) + +Download a random (from a small subset) image from PlaceKitten. + +### Quote of the Day +[inky_frame_quote_of_the_day.py](inky_frame_quote_of_the_day.py) + +Load the WikiQuotes Quote of the Day and display it. + +### Random Joke +[inky_frame_random_joke.py](inky_frame_random_joke.py) + +Load a random joke from JokeAPI.dev and display it. + +Jokes are rendered into images "offline" by our feed2image service for two reasons: + +1. Saves the Pico W having to process them +2. JokeAPI.dev needs TLS1.3 which Pico W does not support! + +For bugs/contributions or to complain about a joke, see: https://github.com/pimoroni/feed2image + +### XKCD Daily +[inky_frame_xkcd_daily.py](inky_frame_xkcd_daily.py) + +Download and display the daily webcomic from https://xkcd.com/ + +The webcomic is rendered "offline" by our feed2image service since xkcd.com requires TLS1.3! + +For bugs/contributions see: https://github.com/pimoroni/feed2image + diff --git a/micropython/examples/inky_frame/WIFI_CONFIG.py b/micropython/examples/inky_frame/WIFI_CONFIG.py new file mode 100644 index 00000000..b1356062 --- /dev/null +++ b/micropython/examples/inky_frame/WIFI_CONFIG.py @@ -0,0 +1,3 @@ +SSID = "YOUR_WIFI_SSID" +PSK = "YOUR_WIFI_PASSWORD" +COUNTRY = "YOUR_COUNTRY_CODE" diff --git a/micropython/examples/inky_frame/inky_frame_daily_activity.py b/micropython/examples/inky_frame/inky_frame_daily_activity.py new file mode 100644 index 00000000..606eb13f --- /dev/null +++ b/micropython/examples/inky_frame/inky_frame_daily_activity.py @@ -0,0 +1,138 @@ +import gc +import WIFI_CONFIG +from network_manager import NetworkManager +import uasyncio +import ujson +from urllib import urequest +from picographics import PicoGraphics, DISPLAY_INKY_FRAME +from machine import Pin +from pimoroni_i2c import PimoroniI2C +from pcf85063a import PCF85063A + + +I2C_SDA_PIN = 4 +I2C_SCL_PIN = 5 +HOLD_VSYS_EN_PIN = 2 + +# set up and enable vsys hold so we don't go to sleep +hold_vsys_en_pin = Pin(HOLD_VSYS_EN_PIN, Pin.OUT) +hold_vsys_en_pin.value(True) + +# intialise the pcf85063a real time clock chip +i2c = PimoroniI2C(I2C_SDA_PIN, I2C_SCL_PIN, 100000) +rtc = PCF85063A(i2c) + +# Length of time between updates in Seconds. +# Frequent updates will reduce battery life! +UPDATE_INTERVAL = 60 * 1 + +# API URL +URL = "https://www.boredapi.com/api/activity" + + +def status_handler(mode, status, ip): + print(mode, status, ip) + + +network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler) + +gc.collect() +graphics = PicoGraphics(DISPLAY_INKY_FRAME) +WIDTH, HEIGHT = graphics.get_bounds() +graphics.set_font("cursive") +gc.collect() + + +def display_quote(text, ox, oy, scale, wordwrap): + # Processing text is memory intensive + # so we'll do it one char at a time as we draw to the screen + line_height = 8 * scale + html = False + html_tag = "" + word = "" + space_width = graphics.measure_text(" ", scale=scale) + x = ox + y = oy + for char in text: + if char in "[]": + continue + if char == "<": + html = True + html_tag = "" + continue + if char == ">": + html = False + continue + if html: + if char in "/ ": + continue + html_tag += char + continue + if char in (" ", "\n") or html_tag == "br": + w = graphics.measure_text(word, scale=scale) + if x + w > wordwrap or char == "\n" or html_tag == "br": + x = ox + y += line_height + + graphics.text(word, x, y, scale=scale) + word = "" + html_tag = "" + x += w + space_width + continue + + word += char + + # Last word + w = graphics.measure_text(word, scale=scale) + if x + w > wordwrap: + x = ox + y += line_height + + graphics.text(word, x, y, scale=scale) + + +rtc.enable_timer_interrupt(True) + +while True: + # Connect to WiFi + uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) + + # Clear the screen + graphics.set_pen(1) + graphics.clear() + graphics.set_pen(0) + + # Grab the data + socket = urequest.urlopen(URL) + j = ujson.load(socket) + socket.close() + + text = [j['activity'], j['type'], j['participants']] + + # Page lines! + graphics.set_pen(3) + graphics.line(0, 65, WIDTH, 65) + for i in range(2, 13): + graphics.line(0, i * 35, WIDTH, i * 35) + + # Page margin + graphics.set_pen(4) + graphics.line(50, 0, 50, HEIGHT) + graphics.set_pen(0) + + # Main text + graphics.set_pen(4) + graphics.text("Activity Idea", 55, 30, WIDTH - 20, 2) + graphics.set_pen(0) + graphics.set_font("bitmap8") + display_quote(text[0], 55, 170, 5, WIDTH - 20) + + graphics.set_pen(2) + graphics.text("Activity Type: " + text[1], 55, HEIGHT - 45, WIDTH - 20, 2) + graphics.text("Participants: " + str(text[2]), 400, HEIGHT - 45, WIDTH - 20, 2) + + graphics.update() + + # Time to have a little nap until the next update + rtc.set_timer(UPDATE_INTERVAL) + hold_vsys_en_pin.init(Pin.IN) diff --git a/micropython/examples/inky_frame/inky_frame_news.py b/micropython/examples/inky_frame/inky_frame_news.py new file mode 100644 index 00000000..f2ab1206 --- /dev/null +++ b/micropython/examples/inky_frame/inky_frame_news.py @@ -0,0 +1,188 @@ +from picographics import PicoGraphics, DISPLAY_INKY_FRAME +from network_manager import NetworkManager +import uasyncio +from urllib import urequest +import WIFI_CONFIG +import gc +import qrcode +from machine import Pin +from pimoroni_i2c import PimoroniI2C +from pcf85063a import PCF85063A + +I2C_SDA_PIN = 4 +I2C_SCL_PIN = 5 +HOLD_VSYS_EN_PIN = 2 + +# set up and enable vsys hold so we don't go to sleep +hold_vsys_en_pin = Pin(HOLD_VSYS_EN_PIN, Pin.OUT) +hold_vsys_en_pin.value(True) + +# Uncomment one URL to use (Top Stories, World News and technology) +# URL = "http://feeds.bbci.co.uk/news/rss.xml" +# URL = "http://feeds.bbci.co.uk/news/world/rss.xml" +URL = "http://feeds.bbci.co.uk/news/technology/rss.xml" + +# Length of time between updates in Seconds. +# Frequent updates will reduce battery life! +UPDATE_INTERVAL = 60 * 1 + +graphics = PicoGraphics(DISPLAY_INKY_FRAME) +WIDTH, HEIGHT = graphics.get_bounds() +graphics.set_font("bitmap8") +code = qrcode.QRCode() + +# intialise the pcf85063a real time clock chip +i2c = PimoroniI2C(I2C_SDA_PIN, I2C_SCL_PIN, 100000) +rtc = PCF85063A(i2c) + + +def status_handler(mode, status, ip): + print(mode, status, ip) + + +network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler) + + +def read_until(stream, char): + result = b"" + while True: + c = stream.read(1) + if c == char: + return result + result += c + + +def discard_until(stream, c): + while stream.read(1) != c: + pass + + +def parse_xml_stream(s, accept_tags, group_by, max_items=3): + tag = [] + text = b"" + count = 0 + current = {} + while True: + char = s.read(1) + if len(char) == 0: + break + + if char == b"<": + next_char = s.read(1) + + # Discard stuff like ") + continue + + # Detect ") # Discard ]> + gc.collect() + + elif next_char == b"/": + current_tag = read_until(s, b">") + top_tag = tag[-1] + + # Populate our result dict + if top_tag in accept_tags: + current[top_tag.decode("utf-8")] = text.decode("utf-8") + + # If we've found a group of items, yield the dict + elif top_tag == group_by: + yield current + current = {} + count += 1 + if count == max_items: + return + tag.pop() + text = b"" + gc.collect() + continue + + else: + current_tag = read_until(s, b">") + tag += [next_char + current_tag.split(b" ")[0]] + text = b"" + gc.collect() + + else: + text += char + + +def measure_qr_code(size, code): + w, h = code.get_size() + module_size = int(size / w) + return module_size * w, module_size + + +def draw_qr_code(ox, oy, size, code): + size, module_size = measure_qr_code(size, code) + graphics.set_pen(1) + graphics.rectangle(ox, oy, size, size) + graphics.set_pen(0) + for x in range(size): + for y in range(size): + if code.get_module(x, y): + graphics.rectangle(ox + x * module_size, oy + y * module_size, module_size, module_size) + + +def get_rss(): + try: + stream = urequest.urlopen(URL) + output = list(parse_xml_stream(stream, [b"title", b"description", b"guid", b"pubDate"], b"item")) + return output + + except OSError as e: + print(e) + return False + + +rtc.enable_timer_interrupt(True) + +while True: + # Connect to WiFi + uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) + + # Gets Feed Data + feed = get_rss() + + # Clear the screen + graphics.set_pen(1) + graphics.clear() + graphics.set_pen(0) + + # Title + graphics.text("Headlines from BBC News:", 10, 10, 300, 2) + + # Draws 3 articles from the feed if they're available. + if feed: + graphics.set_pen(4) + graphics.text(feed[0]["title"], 10, 40, WIDTH - 150, 3 if graphics.measure_text(feed[0]["title"]) < 650 else 2) + graphics.text(feed[1]["title"], 130, 180, WIDTH - 140, 3 if graphics.measure_text(feed[1]["title"]) < 650 else 2) + graphics.text(feed[2]["title"], 10, 320, WIDTH - 150, 3 if graphics.measure_text(feed[2]["title"]) < 650 else 2) + + graphics.set_pen(3) + graphics.text(feed[0]["description"], 10, 110 if graphics.measure_text(feed[0]["title"]) < 650 else 90, WIDTH - 150, 2) + graphics.text(feed[1]["description"], 130, 250 if graphics.measure_text(feed[1]["title"]) < 650 else 230, WIDTH - 145, 2) + graphics.text(feed[2]["description"], 10, 395 if graphics.measure_text(feed[2]["title"]) < 650 else 375, WIDTH - 150, 2) + + code.set_text(feed[0]["guid"]) + draw_qr_code(490, 40, 100, code) + code.set_text(feed[1]["guid"]) + draw_qr_code(10, 180, 100, code) + code.set_text(feed[2]["guid"]) + draw_qr_code(490, 320, 100, code) + + else: + graphics.set_pen(4) + graphics.text("Error: Unable to get feed :(", 10, 40, WIDTH - 150, 4) + + graphics.update() + + # Time to have a little nap until the next update + rtc.set_timer(UPDATE_INTERVAL) + hold_vsys_en_pin.init(Pin.IN) diff --git a/micropython/examples/inky_frame/inky_frame_placekitten.py b/micropython/examples/inky_frame/inky_frame_placekitten.py new file mode 100644 index 00000000..8c6f9366 --- /dev/null +++ b/micropython/examples/inky_frame/inky_frame_placekitten.py @@ -0,0 +1,68 @@ +import gc +import uos +import random +import machine +import jpegdec +import uasyncio +import sdcard +import WIFI_CONFIG +from urllib import urequest +from network_manager import NetworkManager +from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY + +""" +random placekitten (from a very small set) + +You *must* insert an SD card into Inky Frame! +We need somewhere to save the jpg for display. +""" + +gc.collect() # We're really gonna need that RAM! + + +def status_handler(mode, status, ip): + print(mode, status, ip) + + +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)) + + +graphics = PicoGraphics(DISPLAY) + +WIDTH, HEIGHT = graphics.get_bounds() +FILENAME = "/sd/placekitten.jpg" +ENDPOINT = "http://placekitten.com/{0}/{1}" + + +sd_spi = machine.SPI(0, sck=machine.Pin(18, machine.Pin.OUT), mosi=machine.Pin(19, machine.Pin.OUT), miso=machine.Pin(16, machine.Pin.OUT)) +sd = sdcard.SDCard(sd_spi, machine.Pin(22)) +uos.mount(sd, "/sd") +gc.collect() # Claw back some RAM! + +url = ENDPOINT.format(WIDTH, HEIGHT + random.randint(0, 10)) + +socket = urequest.urlopen(url) + +# Stream the image data from the socket onto disk in 1024 byte chunks +# the 600x448-ish jpeg will be roughly ~24k, we really don't have the RAM! +data = bytearray(1024) +with open(FILENAME, "wb") as f: + while True: + if socket.readinto(data) == 0: + break + f.write(data) +socket.close() +gc.collect() # We really are tight on RAM! + + +jpeg = jpegdec.JPEG(graphics) +gc.collect() # For good measure... + +graphics.set_pen(1) +graphics.clear() + +jpeg.open_file(FILENAME) +jpeg.decode() + +graphics.update() diff --git a/micropython/examples/inky_frame/inky_frame_quote_of_the_day.py b/micropython/examples/inky_frame/inky_frame_quote_of_the_day.py new file mode 100644 index 00000000..76269889 --- /dev/null +++ b/micropython/examples/inky_frame/inky_frame_quote_of_the_day.py @@ -0,0 +1,169 @@ +import gc +import time +import ujson +import uasyncio +import WIFI_CONFIG +from urllib import urequest +from network_manager import NetworkManager +from picographics import PicoGraphics, DISPLAY_INKY_FRAME + + +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): + print(text) + text = text.split("\n") + author = text[8].split("|")[2][5:-4] + text = text[6][2:] + gc.collect() + return text, author + + +def status_handler(mode, status, ip): + print(mode, status, ip) + + +network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler) + +gc.collect() +graphics = PicoGraphics(DISPLAY_INKY_FRAME) +WIDTH, HEIGHT = graphics.get_bounds() +graphics.set_font("bitmap8") +gc.collect() + + +BADCHARS = { + "’": "'", + "—": "", + "…": "..." +} + + +def display_quote(text, ox, oy, scale, wordwrap): + # Processing text is memory intensive + # so we'll do it one char at a time as we draw to the screen + line_height = 9 * scale + html_tag = "" + word = "" + extra_text = "" + space_width = graphics.measure_text(" ", scale=scale) + x = ox + y = oy + i = -1 + while True: + if len(extra_text) == 0: + i += 1 + if i >= len(text): + break + + if len(extra_text) > 0: + char = extra_text[0] + extra_text = extra_text[1:] + else: + char = text[i] + + if char in BADCHARS: + word += BADCHARS[char] + continue + + # Unpick stuff like [[word]] and [[disambiguation|word]] + # and [[w:wikipedia_page|word]] + # test cases: July 8th 2022, July 12th 2022 + if char == "[": + if text[i:i + 2] == "[[": + link = False + if text[i + 2:i + 4] == "w:": + link = True + i += 2 + end = text[i:].index("]]") + if "|" in text[i + 2:i + end]: + parts = text[i + 2:i + end].split("|") + word = parts[1] + if not link: + extra_text = " (" + parts[0] + ")" + else: + word = text[i + 2:i + end] + i += end + 1 + continue + + if char == "&": + if text[i:i + 5] == "&": + word += "&" + i += 4 + continue + + if char == "<": + j = i + text[i:].index(">") + html_tag = text[i + 1:j].replace("/", "").strip() + i = j + continue + + if char in (" ", "\n") or html_tag == "br": + w = graphics.measure_text(word, scale=scale) + if x + w > wordwrap or char == "\n" or html_tag == "br": + x = ox + y += line_height + + graphics.text(word, x, y, scale=scale) + word = "" + html_tag = "" + x += w + space_width + continue + + word += char + + # Last word + w = graphics.measure_text(word, scale=scale) + if x + w > wordwrap: + x = ox + y += line_height + + graphics.text(word, x, y, scale=scale) + + +while True: + gc.collect() + + 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)) + socket = urequest.urlopen(url) + j = ujson.load(socket) + socket.close() + + text = j['expandtemplates']['wikitext'] + del j + gc.collect() + + text, author = parse_qotd(text) + + print(text) + + graphics.set_pen(1) + graphics.clear() + graphics.set_pen(0) + graphics.text("QoTD - {2} {3} {0:04d}".format(*date), 10, 10, scale=3) + + display_quote(text, 10, 40, 2, wordwrap=WIDTH - 20) + + graphics.text(author, 10, HEIGHT - 20, scale=2) + + graphics.update() + gc.collect() + + last_date = "{3} {2}, {0}".format(*date) + + time.sleep(60) diff --git a/micropython/examples/inky_frame/inky_frame_random_joke.py b/micropython/examples/inky_frame/inky_frame_random_joke.py new file mode 100644 index 00000000..cb1b474f --- /dev/null +++ b/micropython/examples/inky_frame/inky_frame_random_joke.py @@ -0,0 +1,88 @@ +import gc +import uos +import random +import machine +import jpegdec +import WIFI_CONFIG +import uasyncio +from network_manager import NetworkManager +from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY +from urllib import urequest + + +gc.collect() # We're really gonna need that RAM! + + +def status_handler(mode, status, ip): + print(mode, status, ip) + + +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)) + + +graphics = PicoGraphics(DISPLAY) + +WIDTH, HEIGHT = graphics.get_bounds() +FILENAME = "random-joke.jpg" + +JOKE_IDS = "https://pimoroni.github.io/feed2image/jokeapi-ids.txt" +JOKE_IMG = "https://pimoroni.github.io/feed2image/jokeapi-{}-600x448.jpg" + +import sdcard # noqa: E402 - putting this at the top causes an MBEDTLS OOM error!? +sd_spi = machine.SPI(0, sck=machine.Pin(18, machine.Pin.OUT), mosi=machine.Pin(19, machine.Pin.OUT), miso=machine.Pin(16, machine.Pin.OUT)) +sd = sdcard.SDCard(sd_spi, machine.Pin(22)) +uos.mount(sd, "/sd") +gc.collect() # Claw back some RAM! + +# We don't have the RAM to store the list of Joke IDs in memory. +# the first line of `jokeapi-ids.txt` is a COUNT of IDs. +# Grab it, then pick a random line between 0 and COUNT. +# Seek to that line and ...y'know... there's our totally random joke ID + +socket = urequest.urlopen(JOKE_IDS) + +# Get the first line, which is a count of the joke IDs +number_of_lines = int(socket.readline().decode("ascii")) +print("Total jokes {}".format(number_of_lines)) + +# Pick a random joke (by its line number) +line = random.randint(0, number_of_lines) +print("Getting ID from line {}".format(line)) + +for x in range(line): # Throw away lines to get where we need + socket.readline() + +# Read our chosen joke ID! +random_joke_id = int(socket.readline().decode("ascii")) +socket.close() + +print("Random joke ID: {}".format(random_joke_id)) + +url = JOKE_IMG.format(random_joke_id) + +socket = urequest.urlopen(url) + +# Stream the image data from the socket onto disk in 1024 byte chunks +# the 600x448-ish jpeg will be roughly ~24k, we really don't have the RAM! +data = bytearray(1024) +with open(FILENAME, "wb") as f: + while True: + if socket.readinto(data) == 0: + break + f.write(data) +socket.close() +del data +gc.collect() # We really are tight on RAM! + + +jpeg = jpegdec.JPEG(graphics) +gc.collect() # For good measure... + +graphics.set_pen(1) +graphics.clear() + +jpeg.open_file(FILENAME) +jpeg.decode() + +graphics.update() diff --git a/micropython/examples/inky_frame/inky_frame_xkcd_daily.py b/micropython/examples/inky_frame/inky_frame_xkcd_daily.py new file mode 100644 index 00000000..9b1d1d29 --- /dev/null +++ b/micropython/examples/inky_frame/inky_frame_xkcd_daily.py @@ -0,0 +1,74 @@ +import gc +import uos +import random +import machine +import jpegdec +import uasyncio +import sdcard +import WIFI_CONFIG +from urllib import urequest +from network_manager import NetworkManager +from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY + +""" +xkcd daily + +You *must* insert an SD card into Inky Frame! +We need somewhere to save the jpg for display. + +Fetches a pre-processed XKCD daily image from: +https://pimoroni.github.io/feed2image/xkcd-daily.jpg + +See https://xkcd.com/ for more webcomics! +""" + +gc.collect() # We're really gonna need that RAM! + + +def status_handler(mode, status, ip): + print(mode, status, ip) + + +network_manager = NetworkManager("GB", status_handler=status_handler) +uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK)) + + +graphics = PicoGraphics(DISPLAY) + +WIDTH, HEIGHT = graphics.get_bounds() +FILENAME = "/sd/xkcd-daily.jpg" +ENDPOINT = "https://pimoroni.github.io/feed2image/xkcd-daily.jpg" + + +sd_spi = machine.SPI(0, sck=machine.Pin(18, machine.Pin.OUT), mosi=machine.Pin(19, machine.Pin.OUT), miso=machine.Pin(16, machine.Pin.OUT)) +sd = sdcard.SDCard(sd_spi, machine.Pin(22)) +uos.mount(sd, "/sd") +gc.collect() # Claw back some RAM! + + +url = ENDPOINT.format(WIDTH, HEIGHT + random.randint(0, 10)) + +socket = urequest.urlopen(url) + +# Stream the image data from the socket onto disk in 1024 byte chunks +# the 600x448-ish jpeg will be roughly ~24k, we really don't have the RAM! +data = bytearray(1024) +with open(FILENAME, "wb") as f: + while True: + if socket.readinto(data) == 0: + break + f.write(data) +socket.close() +gc.collect() # We really are tight on RAM! + + +jpeg = jpegdec.JPEG(graphics) +gc.collect() # For good measure... + +graphics.set_pen(1) +graphics.clear() + +jpeg.open_file(FILENAME) +jpeg.decode() + +graphics.update()