From a02834be9e70e07111107514324dde2e0555a1f6 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 17 Jan 2023 12:43:45 +0000 Subject: [PATCH] Badger2040: Port more examples. --- .../examples/badger2040w/examples/clock.py | 12 +- .../examples/badger2040w/examples/fonts.py | 129 ++++++++ .../examples/badger2040w/examples/help.py | 40 +++ .../examples/badger2040w/examples/image.py | 119 +++++++ .../examples/badger2040w/examples/info.py | 45 +++ .../examples/badger2040w/examples/list.py | 311 ++++++++++++++++++ .../examples/badger2040w/examples/qrgen.py | 138 ++++++++ .../examples/badger2040w/images/README.txt | 3 + .../badger2040w/images/badgerpunk.jpg | Bin 0 -> 9264 bytes .../examples/badger2040w/uf2-manifest.txt | 4 +- 10 files changed, 795 insertions(+), 6 deletions(-) create mode 100644 micropython/examples/badger2040w/images/README.txt create mode 100644 micropython/examples/badger2040w/images/badgerpunk.jpg diff --git a/micropython/examples/badger2040w/examples/clock.py b/micropython/examples/badger2040w/examples/clock.py index 7d52e4c6..48297997 100644 --- a/micropython/examples/badger2040w/examples/clock.py +++ b/micropython/examples/badger2040w/examples/clock.py @@ -5,15 +5,17 @@ import badger2040w display = badger2040w.Badger2040W() +display.set_update_speed(3) WIDTH, HEIGHT = display.get_bounds() -display.connect() +try: + display.connect() + if display.isconnected(): + ntptime.settime() +except RuntimeError: + pass # no WiFI -# We're going to keep the badger on, so slow down the system clock if on battery -display.set_update_speed(3) - -ntptime.settime() rtc = machine.RTC() display.set_font("gothic") diff --git a/micropython/examples/badger2040w/examples/fonts.py b/micropython/examples/badger2040w/examples/fonts.py index e69de29b..30699ad8 100644 --- a/micropython/examples/badger2040w/examples/fonts.py +++ b/micropython/examples/badger2040w/examples/fonts.py @@ -0,0 +1,129 @@ +import badger2040w +import badger_os + +# Global Constants +FONT_NAMES = ( + ("sans", 0.7, 2), + ("gothic", 0.7, 2), + ("cursive", 0.7, 2), + ("serif", 0.7, 2), + ("serif_italic", 0.7, 2), + ("bitmap6", 3, 1), + ("bitmap8", 2, 1), + ("bitmap14_outline", 1, 1) +) + +WIDTH = badger2040w.WIDTH +HEIGHT = badger2040w.HEIGHT + +MENU_TEXT_SIZE = 0.5 +MENU_SPACING = 16 +MENU_WIDTH = 84 +MENU_PADDING = 5 + +TEXT_INDENT = MENU_WIDTH + 10 + +ARROW_THICKNESS = 3 +ARROW_WIDTH = 18 +ARROW_HEIGHT = 14 +ARROW_PADDING = 2 + + +# ------------------------------ +# Drawing functions +# ------------------------------ + +# Draw a upward arrow +def draw_up(x, y, width, height, thickness, padding): + border = (thickness // 4) + padding + display.line(x + border, y + height - border, + x + (width // 2), y + border) + display.line(x + (width // 2), y + border, + x + width - border, y + height - border) + + +# Draw a downward arrow +def draw_down(x, y, width, height, thickness, padding): + border = (thickness // 2) + padding + display.line(x + border, y + border, + x + (width // 2), y + height - border) + display.line(x + (width // 2), y + height - border, + x + width - border, y + border) + + +# Draw the frame of the reader +def draw_frame(): + display.set_pen(15) + display.clear() + display.set_pen(12) + display.rectangle(WIDTH - ARROW_WIDTH, 0, ARROW_WIDTH, HEIGHT) + display.set_pen(0) + draw_up(WIDTH - ARROW_WIDTH, (HEIGHT // 4) - (ARROW_HEIGHT // 2), + ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING) + draw_down(WIDTH - ARROW_WIDTH, ((HEIGHT * 3) // 4) - (ARROW_HEIGHT // 2), + ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING) + + +# Draw the fonts and menu +def draw_fonts(): + display.set_font("bitmap8") + for i in range(len(FONT_NAMES)): + name, size, thickness = FONT_NAMES[i] + display.set_pen(0) + if i == state["selected_font"]: + display.rectangle(0, i * MENU_SPACING, MENU_WIDTH, MENU_SPACING) + display.set_pen(15) + + display.text(name, MENU_PADDING, (i * MENU_SPACING) + int((MENU_SPACING - 8) / 2), WIDTH, MENU_TEXT_SIZE) + + name, size, thickness = FONT_NAMES[state["selected_font"]] + display.set_font(name) + + y = 0 if name.startswith("bitmap") else 10 + + display.set_pen(0) + for line in ("The quick", "brown fox", "jumps over", "the lazy dog.", "0123456789", "!\"£$%^&*()"): + display.text(line, TEXT_INDENT, y, WIDTH, size) + y += 22 + + display.update() + + +# ------------------------------ +# Program setup +# ------------------------------ + +# Global variables +state = {"selected_font": 0} +badger_os.state_load("fonts", state) + +# Create a new Badger and set it to update FAST +display = badger2040w.Badger2040W() +display.led(128) +display.set_update_speed(badger2040w.UPDATE_FAST) + +changed = not badger2040w.woken_by_button() + +# ------------------------------ +# Main program loop +# ------------------------------ + +while True: + if display.pressed(badger2040w.BUTTON_UP): + state["selected_font"] -= 1 + if state["selected_font"] < 0: + state["selected_font"] = len(FONT_NAMES) - 1 + changed = True + if display.pressed(badger2040w.BUTTON_DOWN): + state["selected_font"] += 1 + if state["selected_font"] >= len(FONT_NAMES): + state["selected_font"] = 0 + changed = True + + if changed: + draw_frame() + draw_fonts() + badger_os.state_save("fonts", state) + changed = False + + display.halt() diff --git a/micropython/examples/badger2040w/examples/help.py b/micropython/examples/badger2040w/examples/help.py index e69de29b..8e08a8c8 100644 --- a/micropython/examples/badger2040w/examples/help.py +++ b/micropython/examples/badger2040w/examples/help.py @@ -0,0 +1,40 @@ +import badger2040w +from badger2040w import WIDTH + +TEXT_SIZE = 0.45 +LINE_HEIGHT = 20 + +display = badger2040w.Badger2040W() +display.led(128) + +# Clear to white +display.set_pen(15) +display.clear() + +display.set_font("bitmap8") +display.set_pen(0) +display.rectangle(0, 0, WIDTH, 16) +display.set_pen(15) +display.text("badgerOS", 3, 4, WIDTH, 1) +display.text("help", WIDTH - display.measure_text("help", 0.4) - 4, 4, WIDTH, 1) + +display.set_font("sans") +display.set_pen(0) + +TEXT_SIZE = 0.62 +y = 20 + int(LINE_HEIGHT / 2) + +display.set_font("sans") +display.text("Up/Down - Change page", 0, y, WIDTH, TEXT_SIZE) +y += LINE_HEIGHT +display.text("a, b or c - Launch app", 0, y, WIDTH, TEXT_SIZE) +y += LINE_HEIGHT +display.text("a & c - Exit app", 0, y, WIDTH, TEXT_SIZE) +y += LINE_HEIGHT + +display.update() + +# Call halt in a loop, on battery this switches off power. +# On USB, the app will exit when A+C is pressed because the launcher picks that up. +while True: + display.halt() diff --git a/micropython/examples/badger2040w/examples/image.py b/micropython/examples/badger2040w/examples/image.py index e69de29b..f44478d4 100644 --- a/micropython/examples/badger2040w/examples/image.py +++ b/micropython/examples/badger2040w/examples/image.py @@ -0,0 +1,119 @@ +import os +import sys +import time +import badger2040w +from badger2040w import HEIGHT, WIDTH +import badger_os +import jpegdec + + +REAMDE = """ +Images must be 296x128 pixel JPEGs + +Create a new "images" directory via Thonny, and upload your .jpg files there. +""" + +OVERLAY_BORDER = 40 +OVERLAY_SPACING = 20 +OVERLAY_TEXT_SIZE = 0.5 + +TOTAL_IMAGES = 0 + + +# Turn the act LED on as soon as possible +display = badger2040w.Badger2040W() +display.led(128) + +jpeg = jpegdec.JPEG(display.display) + +# Try to preload BadgerPunk image +try: + os.mkdir("/images") + with open("/images/readme.txt", "w") as f: + f.write(REAMDE) + f.flush() +except (OSError, ImportError): + pass + +# Load images +try: + IMAGES = [f for f in os.listdir("/images") if f.endswith(".jpg")] + TOTAL_IMAGES = len(IMAGES) +except OSError: + pass + + +state = { + "current_image": 0, + "show_info": True +} + + +def show_image(n): + file = IMAGES[n] + name = file.split(".")[0] + jpeg.open_file("/images/{}".format(file)) + jpeg.decode() + + if state["show_info"]: + name_length = display.measure_text(name, 0.5) + display.set_pen(0) + display.rectangle(0, HEIGHT - 21, name_length + 11, 21) + display.set_pen(15) + display.rectangle(0, HEIGHT - 20, name_length + 10, 20) + display.set_pen(0) + display.text(name, 5, HEIGHT - 10, WIDTH, 0.5) + + for i in range(TOTAL_IMAGES): + x = 286 + y = int((128 / 2) - (TOTAL_IMAGES * 10 / 2) + (i * 10)) + display.set_pen(0) + display.rectangle(x, y, 8, 8) + if state["current_image"] != i: + display.set_pen(15) + display.rectangle(x + 1, y + 1, 6, 6) + + display.update() + + +if TOTAL_IMAGES == 0: + display.set_pen(15) + display.clear() + badger_os.warning(display, "To run this demo, create an /images directory on your device and upload some 1bit 296x128 pixel images.") + time.sleep(4.0) + sys.exit() + + +badger_os.state_load("image", state) + +changed = not badger2040w.woken_by_button() + + +while True: + if display.pressed(badger2040w.BUTTON_UP): + if state["current_image"] > 0: + state["current_image"] -= 1 + changed = True + if display.pressed(badger2040w.BUTTON_DOWN): + if state["current_image"] < TOTAL_IMAGES - 1: + state["current_image"] += 1 + changed = True + if display.pressed(badger2040w.BUTTON_A): + state["show_info"] = not state["show_info"] + changed = True + if display.pressed(badger2040w.BUTTON_B) or display.pressed(badger2040w.BUTTON_C): + display.set_pen(15) + display.clear() + badger_os.warning(display, "To add images connect Badger2040 to a PC, load up Thonny, and see readme.txt in images/") + display.update() + print(state["current_image"]) + time.sleep(4) + changed = True + + if changed: + badger_os.state_save("image", state) + show_image(state["current_image"]) + changed = False + + # Halt the Badger to save power, it will wake up if any of the front buttons are pressed + display.halt() diff --git a/micropython/examples/badger2040w/examples/info.py b/micropython/examples/badger2040w/examples/info.py index e69de29b..7cfff345 100644 --- a/micropython/examples/badger2040w/examples/info.py +++ b/micropython/examples/badger2040w/examples/info.py @@ -0,0 +1,45 @@ +import badger2040w +from badger2040w import WIDTH + +TEXT_SIZE = 0.45 +LINE_HEIGHT = 16 + +display = badger2040w.Badger2040W() +display.led(128) + +# Clear to white +display.set_pen(15) +display.clear() + +display.set_font("bitmap8") +display.set_pen(0) +display.rectangle(0, 0, WIDTH, 16) +display.set_pen(15) +display.text("badgerOS", 3, 4, WIDTH, 1) +display.text("info", WIDTH - display.measure_text("help", 0.4) - 4, 4, WIDTH, 1) + +display.set_font("sans") +display.set_pen(0) + +y = 16 + int(LINE_HEIGHT / 2) + +display.text("Made by Pimoroni, powered by MicroPython", 0, y, WIDTH, TEXT_SIZE) +y += LINE_HEIGHT +display.text("Dual-core RP2040, 133MHz, 264KB RAM", 0, y, WIDTH, TEXT_SIZE) +y += LINE_HEIGHT +display.text("2MB Flash (1MB OS, 1MB Storage)", 0, y, WIDTH, TEXT_SIZE) +y += LINE_HEIGHT +display.text("296x128 pixel Black/White e-Ink", 0, y, WIDTH, TEXT_SIZE) +y += LINE_HEIGHT +y += LINE_HEIGHT + +display.text("For more info:", 0, y, WIDTH, TEXT_SIZE) +y += LINE_HEIGHT +display.text("https://pimoroni.com/badger2040w", 0, y, WIDTH, TEXT_SIZE) + +display.update() + +# Call halt in a loop, on battery this switches off power. +# On USB, the app will exit when A+C is pressed because the launcher picks that up. +while True: + display.halt() diff --git a/micropython/examples/badger2040w/examples/list.py b/micropython/examples/badger2040w/examples/list.py index e69de29b..f74298ad 100644 --- a/micropython/examples/badger2040w/examples/list.py +++ b/micropython/examples/badger2040w/examples/list.py @@ -0,0 +1,311 @@ +import binascii + +import badger2040w +import badger_os + +# **** Put your list title here ***** +list_title = "Checklist" +list_file = "checklist.txt" + + +# Global Constantsu +WIDTH = badger2040w.WIDTH +HEIGHT = badger2040w.HEIGHT + +ARROW_THICKNESS = 3 +ARROW_WIDTH = 18 +ARROW_HEIGHT = 14 +ARROW_PADDING = 2 + +MAX_ITEM_CHARS = 26 +TITLE_TEXT_SIZE = 0.7 +ITEM_TEXT_SIZE = 0.6 +ITEM_SPACING = 20 + +LIST_START = 40 +LIST_PADDING = 2 +LIST_WIDTH = WIDTH - LIST_PADDING - LIST_PADDING - ARROW_WIDTH +LIST_HEIGHT = HEIGHT - LIST_START - LIST_PADDING - ARROW_HEIGHT + + +# Default list items - change the list items by editing checklist.txt +list_items = ["Badger", "Badger", "Badger", "Badger", "Badger", "Mushroom", "Mushroom", "Snake"] +save_checklist = False + +try: + with open("checklist.txt", "r") as f: + raw_list_items = f.read() + + if raw_list_items.find(" X\n") != -1: + # Have old style checklist, preserve state and note we should resave the list to remove the Xs + list_items = [] + state = { + "current_item": 0, + "checked": [] + } + for item in raw_list_items.strip().split("\n"): + if item.endswith(" X"): + state["checked"].append(True) + item = item[:-2] + else: + state["checked"].append(False) + list_items.append(item) + state["items_hash"] = binascii.crc32("\n".join(list_items)) + + badger_os.state_save("list", state) + save_checklist = True + else: + list_items = [item.strip() for item in raw_list_items.strip().split("\n")] + +except OSError: + save_checklist = True + +if save_checklist: + with open("checklist.txt", "w") as f: + for item in list_items: + f.write(item + "\n") + + +# ------------------------------ +# Drawing functions +# ------------------------------ + +# Draw the list of items +def draw_list(items, item_states, start_item, highlighted_item, x, y, width, height, item_height, columns): + item_x = 0 + item_y = 0 + current_col = 0 + for i in range(start_item, len(items)): + if i == highlighted_item: + display.set_pen(12) + display.rectangle(item_x, item_y + y - (item_height // 2), width // columns, item_height) + display.set_pen(0) + display.text(items[i], item_x + x + item_height, item_y + y, WIDTH, ITEM_TEXT_SIZE) + draw_checkbox(item_x, item_y + y - (item_height // 2), item_height, 15, 0, 2, item_states[i], 2) + item_y += item_height + if item_y >= height - (item_height // 2): + item_x += width // columns + item_y = 0 + current_col += 1 + if current_col >= columns: + return + + +# Draw a upward arrow +def draw_up(x, y, width, height, thickness, padding): + border = (thickness // 4) + padding + display.line(x + border, y + height - border, + x + (width // 2), y + border) + display.line(x + (width // 2), y + border, + x + width - border, y + height - border) + + +# Draw a downward arrow +def draw_down(x, y, width, height, thickness, padding): + border = (thickness // 2) + padding + display.line(x + border, y + border, + x + (width // 2), y + height - border) + display.line(x + (width // 2), y + height - border, + x + width - border, y + border) + + +# Draw a left arrow +def draw_left(x, y, width, height, thickness, padding): + border = (thickness // 2) + padding + display.line(x + width - border, y + border, + x + border, y + (height // 2)) + display.line(x + border, y + (height // 2), + x + width - border, y + height - border) + + +# Draw a right arrow +def draw_right(x, y, width, height, thickness, padding): + border = (thickness // 2) + padding + display.line(x + border, y + border, + x + width - border, y + (height // 2)) + display.line(x + width - border, y + (height // 2), + x + border, y + height - border) + + +# Draw a tick +def draw_tick(x, y, width, height, thickness, padding): + border = (thickness // 2) + padding + display.line(x + border, y + ((height * 2) // 3), + x + (width // 2), y + height - border) + display.line(x + (width // 2), y + height - border, + x + width - border, y + border) + + +# Draw a cross +def draw_cross(x, y, width, height, thickness, padding): + border = (thickness // 2) + padding + display.line(x + border, y + border, x + width - border, y + height - border) + display.line(x + width - border, y + border, x + border, y + height - border) + + +# Draw a checkbox with or without a tick +def draw_checkbox(x, y, size, background, foreground, thickness, tick, padding): + border = (thickness // 2) + padding + display.set_pen(background) + display.rectangle(x + border, y + border, size - (border * 2), size - (border * 2)) + display.set_pen(foreground) + display.line(x + border, y + border, x + size - border, y + border) + display.line(x + border, y + border, x + border, y + size - border) + display.line(x + size - border, y + border, x + size - border, y + size - border) + display.line(x + border, y + size - border, x + size - border, y + size - border) + if tick: + draw_tick(x, y, size, size, thickness, 2 + border) + + +# ------------------------------ +# Program setup +# ------------------------------ + +changed = not badger2040w.woken_by_button() +state = { + "current_item": 0, +} +badger_os.state_load("list", state) +items_hash = binascii.crc32("\n".join(list_items)) +if "items_hash" not in state or state["items_hash"] != items_hash: + # Item list changed, or not yet written reset the list + state["current_item"] = 0 + state["items_hash"] = items_hash + state["checked"] = [False] * len(list_items) + changed = True + +# Global variables +items_per_page = 0 + +# Create a new Badger and set it to update FAST +display = badger2040w.Badger2040W() +display.led(128) +display.set_font("sans") +if changed: + display.set_update_speed(badger2040w.UPDATE_FAST) +else: + display.set_update_speed(badger2040w.UPDATE_TURBO) + +# Find out what the longest item is +longest_item = 0 +for i in range(len(list_items)): + while True: + item = list_items[i] + item_length = display.measure_text(item, ITEM_TEXT_SIZE) + if item_length > 0 and item_length > LIST_WIDTH - ITEM_SPACING: + list_items[i] = item[:-1] + else: + break + longest_item = max(longest_item, display.measure_text(list_items[i], ITEM_TEXT_SIZE)) + + +# And use that to calculate the number of columns we can fit onscreen and how many items that would give +list_columns = 1 +while longest_item + ITEM_SPACING < (LIST_WIDTH // (list_columns + 1)): + list_columns += 1 + +items_per_page = ((LIST_HEIGHT // ITEM_SPACING) + 1) * list_columns + + +# ------------------------------ +# Main program loop +# ------------------------------ + +while True: + if len(list_items) > 0: + if display.pressed(badger2040w.BUTTON_A): + if state["current_item"] > 0: + page = state["current_item"] // items_per_page + state["current_item"] = max(state["current_item"] - (items_per_page) // list_columns, 0) + if page != state["current_item"] // items_per_page: + display.update_speed(badger2040w.UPDATE_FAST) + changed = True + if display.pressed(badger2040w.BUTTON_B): + state["checked"][state["current_item"]] = not state["checked"][state["current_item"]] + changed = True + if display.pressed(badger2040w.BUTTON_C): + if state["current_item"] < len(list_items) - 1: + page = state["current_item"] // items_per_page + state["current_item"] = min(state["current_item"] + (items_per_page) // list_columns, len(list_items) - 1) + if page != state["current_item"] // items_per_page: + display.update_speed(badger2040w.UPDATE_FAST) + changed = True + if display.pressed(badger2040w.BUTTON_UP): + if state["current_item"] > 0: + state["current_item"] -= 1 + changed = True + if display.pressed(badger2040w.BUTTON_DOWN): + if state["current_item"] < len(list_items) - 1: + state["current_item"] += 1 + changed = True + + if changed: + badger_os.state_save("list", state) + + display.set_pen(15) + display.clear() + + display.set_pen(12) + display.rectangle(WIDTH - ARROW_WIDTH, 0, ARROW_WIDTH, HEIGHT) + display.rectangle(0, HEIGHT - ARROW_HEIGHT, WIDTH, ARROW_HEIGHT) + + y = LIST_PADDING + 12 + display.set_pen(0) + display.text(list_title, LIST_PADDING, y, WIDTH, TITLE_TEXT_SIZE) + + y += 12 + display.set_pen(0) + display.line(LIST_PADDING, y, WIDTH - LIST_PADDING - ARROW_WIDTH, y) + + if len(list_items) > 0: + page_item = 0 + if items_per_page > 0: + page_item = (state["current_item"] // items_per_page) * items_per_page + + # Draw the list + display.set_pen(0) + draw_list(list_items, state["checked"], page_item, state["current_item"], LIST_PADDING, LIST_START, + LIST_WIDTH, LIST_HEIGHT, ITEM_SPACING, list_columns) + + # Draw the interaction button icons + display.set_pen(0) + + # Previous item + if state["current_item"] > 0: + draw_up(WIDTH - ARROW_WIDTH, (HEIGHT // 4) - (ARROW_HEIGHT // 2), + ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING) + + # Next item + if state["current_item"] < (len(list_items) - 1): + draw_down(WIDTH - ARROW_WIDTH, ((HEIGHT * 3) // 4) - (ARROW_HEIGHT // 2), + ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING) + + # Previous column + if state["current_item"] > 0: + draw_left((WIDTH // 7) - (ARROW_WIDTH // 2), HEIGHT - ARROW_HEIGHT, + ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING) + + # Next column + if state["current_item"] < (len(list_items) - 1): + draw_right(((WIDTH * 6) // 7) - (ARROW_WIDTH // 2), HEIGHT - ARROW_HEIGHT, + ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING) + + if state["checked"][state["current_item"]]: + # Tick off item + draw_cross((WIDTH // 2) - (ARROW_WIDTH // 2), HEIGHT - ARROW_HEIGHT, + ARROW_HEIGHT, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING) + else: + # Untick item + draw_tick((WIDTH // 2) - (ARROW_WIDTH // 2), HEIGHT - ARROW_HEIGHT, + ARROW_HEIGHT, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING) + else: + # Say that the list is empty + empty_text = "Nothing Here" + text_length = display.measure_text(empty_text, ITEM_TEXT_SIZE) + display.text(empty_text, ((LIST_PADDING + LIST_WIDTH) - text_length) // 2, (LIST_HEIGHT // 2) + LIST_START - (ITEM_SPACING // 4), WIDTH, ITEM_TEXT_SIZE) + + display.update() + display.set_update_speed(badger2040w.UPDATE_TURBO) + changed = False + + display.halt() diff --git a/micropython/examples/badger2040w/examples/qrgen.py b/micropython/examples/badger2040w/examples/qrgen.py index e69de29b..451e430d 100644 --- a/micropython/examples/badger2040w/examples/qrgen.py +++ b/micropython/examples/badger2040w/examples/qrgen.py @@ -0,0 +1,138 @@ +import badger2040w +import qrcode +import time +import os +import badger_os + +# Check that the qrcodes directory exists, if not, make it +try: + os.mkdir("/qrcodes") +except OSError: + pass + +# Check that there is a qrcode.txt, if not preload +try: + text = open("/qrcodes/qrcode.txt", "r") +except OSError: + text = open("/qrcodes/qrcode.txt", "w") + text.write("""https://pimoroni.com/badger2040 +Badger 2040 +* 296x128 1-bit e-ink +* six user buttons +* user LED +* 2MB QSPI flash + +Scan this code to learn +more about Badger 2040. +""") + text.flush() + text.seek(0) + +# Load all available QR Code Files +try: + CODES = [f for f in os.listdir("/qrcodes") if f.endswith(".txt")] + TOTAL_CODES = len(CODES) +except OSError: + pass + + +print(f'There are {TOTAL_CODES} QR Codes available:') +for codename in CODES: + print(f'File: {codename}') + +display = badger2040w.Badger2040W() + +code = qrcode.QRCode() + +state = { + "current_qr": 0 +} + + +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) + display.set_pen(15) + display.rectangle(ox, oy, size, size) + display.set_pen(0) + for x in range(size): + for y in range(size): + if code.get_module(x, y): + display.rectangle(ox + x * module_size, oy + y * module_size, module_size, module_size) + + +def draw_qr_file(n): + display.led(128) + file = CODES[n] + codetext = open("/qrcodes/{}".format(file), "r") + + lines = codetext.read().strip().split("\n") + code_text = lines.pop(0) + title_text = lines.pop(0) + detail_text = lines + + # Clear the Display + display.set_pen(15) # Change this to 0 if a white background is used + display.clear() + display.set_pen(0) + + code.set_text(code_text) + size, _ = measure_qr_code(128, code) + left = top = int((badger2040w.HEIGHT / 2) - (size / 2)) + draw_qr_code(left, top, 128, code) + + left = 128 + 5 + + display.text(title_text, left, 20, badger2040w.WIDTH, 2) + + top = 40 + for line in detail_text: + display.text(line, left, top, badger2040w.WIDTH, 1) + top += 10 + + if TOTAL_CODES > 1: + for i in range(TOTAL_CODES): + x = 286 + y = int((128 / 2) - (TOTAL_CODES * 10 / 2) + (i * 10)) + display.set_pen(0) + display.rectangle(x, y, 8, 8) + if state["current_qr"] != i: + display.set_pen(15) + display.rectangle(x + 1, y + 1, 6, 6) + display.update() + + +badger_os.state_load("qrcodes", state) +changed = not badger2040w.woken_by_button() + +while True: + if TOTAL_CODES > 1: + if display.pressed(badger2040w.BUTTON_UP): + if state["current_qr"] > 0: + state["current_qr"] -= 1 + changed = True + + if display.pressed(badger2040w.BUTTON_DOWN): + if state["current_qr"] < TOTAL_CODES - 1: + state["current_qr"] += 1 + changed = True + + if display.pressed(badger2040w.BUTTON_B) or display.pressed(badger2040w.BUTTON_C): + display.set_pen(15) + display.clear() + badger_os.warning(display, "To add QR codes, connect Badger2040W to a PC, load up Thonny, and see qrgen.py.") + time.sleep(4) + changed = True + + if changed: + draw_qr_file(state["current_qr"]) + badger_os.state_save("qrcodes", state) + changed = False + + # Halt the Badger to save power, it will wake up if any of the front buttons are pressed + display.halt() diff --git a/micropython/examples/badger2040w/images/README.txt b/micropython/examples/badger2040w/images/README.txt new file mode 100644 index 00000000..c9a6e267 --- /dev/null +++ b/micropython/examples/badger2040w/images/README.txt @@ -0,0 +1,3 @@ +Images must be 296x128 pixel JPEGs + +Create a new "images" directory via Thonny, and upload your .jpg files there. diff --git a/micropython/examples/badger2040w/images/badgerpunk.jpg b/micropython/examples/badger2040w/images/badgerpunk.jpg new file mode 100644 index 0000000000000000000000000000000000000000..73f32be9525032d80bfb9bcb2b967daa1f496c36 GIT binary patch literal 9264 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