Merge pull request #860 from pimoroni/patch-bye-bye-badger
Badger2040/2040W: Remove old/incompatible examples.
|
@ -1,104 +1,3 @@
|
|||
# Badger 2040 Examples <!-- omit in toc -->
|
||||
|
||||
:warning: This code has been deprecated in favour of a dedicated Badger 2040 project: https://github.com/pimoroni/badger2040
|
||||
|
||||
- [Function Examples](#function-examples)
|
||||
- [Battery](#battery)
|
||||
- [Button Test](#button-test)
|
||||
- [LED](#led)
|
||||
- [Pin interrupt](#pin-interrupt)
|
||||
- [Application Examples](#application-examples)
|
||||
- [Badge](#badge)
|
||||
- [Checklist](#checklist)
|
||||
- [Clock](#clock)
|
||||
- [E-Book](#e-book)
|
||||
- [Fonts](#fonts)
|
||||
- [Image](#image)
|
||||
- [QR gen](#qr-gen)
|
||||
- [Launcher](#launcher)
|
||||
- [Conway](#conway)
|
||||
|
||||
|
||||
## Function Examples
|
||||
|
||||
### Battery
|
||||
[battery.py](battery.py)
|
||||
|
||||
An example of how to read the battery voltage and display a battery level indicator.
|
||||
|
||||
### Button Test
|
||||
[button_test.py](button_test.py)
|
||||
|
||||
An example of how to read Badger2040's buttons and display a unique message for each.
|
||||
|
||||
### LED
|
||||
[led.py](led.py)
|
||||
|
||||
Blinks Badger's LED on and off.
|
||||
|
||||
### Pin interrupt
|
||||
[pin_interrupt.py](pin_interrupt.py)
|
||||
|
||||
An example of drawing text and graphics and using the buttons.
|
||||
|
||||
## Application Examples
|
||||
|
||||
### Badge
|
||||
[badge.py](badge.py)
|
||||
|
||||
Create your own name badge! This application looks for two files on your MicroPython drive:
|
||||
* `badge.txt` - A text file containing 6 lines, corresponding to the 6 different pieces of text on the badge
|
||||
* `badge-image.bin` - A 104x128px 1-bit colour depth image to display alongside the text. You can use `examples/badger2040/image_converter/convert.py` to convert them:
|
||||
|
||||
```shell
|
||||
python3 convert.py --binary --resize image_file_1.png image_file_2.png image_file_3.png
|
||||
```
|
||||
|
||||
### Checklist
|
||||
[list.py](list.py)
|
||||
|
||||
A checklist application, letting you navigate through items and tick each of them off.
|
||||
|
||||
* `checklist.txt` - A text file containing the titles of items for the list.
|
||||
|
||||
### Clock
|
||||
[clock.py](clock.py)
|
||||
|
||||
A simple clock showing the time and date, that uses the E Ink's fast speed to update every second.
|
||||
|
||||
### E-Book
|
||||
[ebook.py](ebook.py)
|
||||
|
||||
A mini text file e-reader. Comes pre-loaded with an excerpt of The Wind In the Willows.
|
||||
|
||||
### Fonts
|
||||
[fonts.py](fonts.py)
|
||||
|
||||
A demonstration of the various fonts that can be used in your programs.
|
||||
|
||||
### Image
|
||||
[image.py](image.py)
|
||||
|
||||
An image gallery. Displays and lets you cycle through any images stored within the MicroPython device's `/images` directory. Images must be 296x128 pixels with 1-bit colour depth. You can use `examples/badger2040/image_converter/convert.py` to convert them:
|
||||
|
||||
```shell
|
||||
python3 convert.py --binary --resize image_file_1.png image_file_2.png image_file_3.png
|
||||
```
|
||||
|
||||
### QR gen
|
||||
[qrgen.py](qrgen.py)
|
||||
|
||||
Displays and lets you cycle through multiple QR codes, with configuration stored in text files within the MicroPython device's `/qrcodes` directory.
|
||||
|
||||
- `/qrcodes/qrcode.txt` - A text file containing 9 lines. The first line should be a URL which will be converted into and displayed as a QR code. Up to 8 more lines of information can be added, which will be shown as plain text to the right of the QR code.
|
||||
- `/qrcodes/*.txt` - additional text files can be created using the same format. All text files can be cycled through.
|
||||
|
||||
### Launcher
|
||||
[launcher.py](launcher.py)
|
||||
|
||||
A launcher-style application, providing a menu of other applications that can be loaded, as well as information such as battery level.
|
||||
|
||||
### Conway
|
||||
[conway.py](conway.py)
|
||||
|
||||
Conway's classic Game of Life, implemented on the Badger. Note: this application is *not* linked from the Launcher by default - it can be run directly using Thonny or your MicroPython editor of choice, or you can modify the Launcher to add it (you'll want to update `launchericons.png` as well)
|
||||
|
|
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 36 KiB |
|
@ -1,6 +0,0 @@
|
|||
try:
|
||||
open("main.py", "r")
|
||||
except OSError:
|
||||
with open("main.py", "w") as f:
|
||||
f.write("import _launcher")
|
||||
f.flush()
|
Before Width: | Height: | Size: 9.7 KiB |
|
@ -1,183 +0,0 @@
|
|||
import time
|
||||
import badger2040
|
||||
import badger_os
|
||||
|
||||
# Global Constants
|
||||
WIDTH = badger2040.WIDTH
|
||||
HEIGHT = badger2040.HEIGHT
|
||||
|
||||
IMAGE_WIDTH = 104
|
||||
|
||||
COMPANY_HEIGHT = 30
|
||||
DETAILS_HEIGHT = 20
|
||||
NAME_HEIGHT = HEIGHT - COMPANY_HEIGHT - (DETAILS_HEIGHT * 2) - 2
|
||||
TEXT_WIDTH = WIDTH - IMAGE_WIDTH - 1
|
||||
|
||||
COMPANY_TEXT_SIZE = 0.6
|
||||
DETAILS_TEXT_SIZE = 0.5
|
||||
|
||||
LEFT_PADDING = 5
|
||||
NAME_PADDING = 20
|
||||
DETAIL_SPACING = 10
|
||||
|
||||
DEFAULT_TEXT = """mustelid inc
|
||||
H. Badger
|
||||
RP2040
|
||||
2MB Flash
|
||||
E ink
|
||||
296x128px"""
|
||||
|
||||
BADGE_IMAGE = bytearray(int(IMAGE_WIDTH * HEIGHT / 8))
|
||||
|
||||
try:
|
||||
open("badge-image.bin", "rb").readinto(BADGE_IMAGE)
|
||||
except OSError:
|
||||
try:
|
||||
import badge_image
|
||||
BADGE_IMAGE = bytearray(badge_image.data())
|
||||
del badge_image
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Utility functions
|
||||
# ------------------------------
|
||||
|
||||
# Reduce the size of a string until it fits within a given width
|
||||
def truncatestring(text, text_size, width):
|
||||
while True:
|
||||
length = display.measure_text(text, text_size)
|
||||
if length > 0 and length > width:
|
||||
text = text[:-1]
|
||||
else:
|
||||
text += ""
|
||||
return text
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Drawing functions
|
||||
# ------------------------------
|
||||
|
||||
# Draw the badge, including user text
|
||||
def draw_badge():
|
||||
display.pen(0)
|
||||
display.clear()
|
||||
|
||||
# Draw badge image
|
||||
display.image(BADGE_IMAGE, IMAGE_WIDTH, HEIGHT, WIDTH - IMAGE_WIDTH, 0)
|
||||
|
||||
# Draw a border around the image
|
||||
display.pen(0)
|
||||
display.thickness(1)
|
||||
display.line(WIDTH - IMAGE_WIDTH, 0, WIDTH - 1, 0)
|
||||
display.line(WIDTH - IMAGE_WIDTH, 0, WIDTH - IMAGE_WIDTH, HEIGHT - 1)
|
||||
display.line(WIDTH - IMAGE_WIDTH, HEIGHT - 1, WIDTH - 1, HEIGHT - 1)
|
||||
display.line(WIDTH - 1, 0, WIDTH - 1, HEIGHT - 1)
|
||||
|
||||
# Uncomment this if a white background is wanted behind the company
|
||||
# display.pen(15)
|
||||
# display.rectangle(1, 1, TEXT_WIDTH, COMPANY_HEIGHT - 1)
|
||||
|
||||
# Draw the company
|
||||
display.pen(15) # Change this to 0 if a white background is used
|
||||
display.font("serif")
|
||||
display.thickness(3)
|
||||
display.text(company, LEFT_PADDING, (COMPANY_HEIGHT // 2) + 1, COMPANY_TEXT_SIZE)
|
||||
|
||||
# Draw a white background behind the name
|
||||
display.pen(15)
|
||||
display.thickness(1)
|
||||
display.rectangle(1, COMPANY_HEIGHT + 1, TEXT_WIDTH, NAME_HEIGHT)
|
||||
|
||||
# Draw the name, scaling it based on the available width
|
||||
display.pen(0)
|
||||
display.font("sans")
|
||||
display.thickness(4)
|
||||
name_size = 2.0 # A sensible starting scale
|
||||
while True:
|
||||
name_length = display.measure_text(name, name_size)
|
||||
if name_length >= (TEXT_WIDTH - NAME_PADDING) and name_size >= 0.1:
|
||||
name_size -= 0.01
|
||||
else:
|
||||
display.text(name, (TEXT_WIDTH - name_length) // 2, (NAME_HEIGHT // 2) + COMPANY_HEIGHT + 1, name_size)
|
||||
break
|
||||
|
||||
# Draw a white backgrounds behind the details
|
||||
display.pen(15)
|
||||
display.thickness(1)
|
||||
display.rectangle(1, HEIGHT - DETAILS_HEIGHT * 2, TEXT_WIDTH, DETAILS_HEIGHT - 1)
|
||||
display.rectangle(1, HEIGHT - DETAILS_HEIGHT, TEXT_WIDTH, DETAILS_HEIGHT - 1)
|
||||
|
||||
# Draw the first detail's title and text
|
||||
display.pen(0)
|
||||
display.font("sans")
|
||||
display.thickness(3)
|
||||
name_length = display.measure_text(detail1_title, DETAILS_TEXT_SIZE)
|
||||
display.text(detail1_title, LEFT_PADDING, HEIGHT - ((DETAILS_HEIGHT * 3) // 2), DETAILS_TEXT_SIZE)
|
||||
display.thickness(2)
|
||||
display.text(detail1_text, 5 + name_length + DETAIL_SPACING, HEIGHT - ((DETAILS_HEIGHT * 3) // 2), DETAILS_TEXT_SIZE)
|
||||
|
||||
# Draw the second detail's title and text
|
||||
display.thickness(3)
|
||||
name_length = display.measure_text(detail2_title, DETAILS_TEXT_SIZE)
|
||||
display.text(detail2_title, LEFT_PADDING, HEIGHT - (DETAILS_HEIGHT // 2), DETAILS_TEXT_SIZE)
|
||||
display.thickness(2)
|
||||
display.text(detail2_text, LEFT_PADDING + name_length + DETAIL_SPACING, HEIGHT - (DETAILS_HEIGHT // 2), DETAILS_TEXT_SIZE)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Program setup
|
||||
# ------------------------------
|
||||
|
||||
# Create a new Badger and set it to update NORMAL
|
||||
display = badger2040.Badger2040()
|
||||
display.led(128)
|
||||
display.update_speed(badger2040.UPDATE_NORMAL)
|
||||
|
||||
# Open the badge file
|
||||
try:
|
||||
badge = open("badge.txt", "r")
|
||||
except OSError:
|
||||
with open("badge.txt", "w") as f:
|
||||
f.write(DEFAULT_TEXT)
|
||||
f.flush()
|
||||
badge = open("badge.txt", "r")
|
||||
|
||||
# Read in the next 6 lines
|
||||
company = badge.readline() # "mustelid inc"
|
||||
name = badge.readline() # "H. Badger"
|
||||
detail1_title = badge.readline() # "RP2040"
|
||||
detail1_text = badge.readline() # "2MB Flash"
|
||||
detail2_title = badge.readline() # "E ink"
|
||||
detail2_text = badge.readline() # "296x128px"
|
||||
|
||||
# Truncate all of the text (except for the name as that is scaled)
|
||||
company = truncatestring(company, COMPANY_TEXT_SIZE, TEXT_WIDTH)
|
||||
|
||||
detail1_title = truncatestring(detail1_title, DETAILS_TEXT_SIZE, TEXT_WIDTH)
|
||||
detail1_text = truncatestring(detail1_text, DETAILS_TEXT_SIZE,
|
||||
TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail1_title, DETAILS_TEXT_SIZE))
|
||||
|
||||
detail2_title = truncatestring(detail2_title, DETAILS_TEXT_SIZE, TEXT_WIDTH)
|
||||
detail2_text = truncatestring(detail2_text, DETAILS_TEXT_SIZE,
|
||||
TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail2_title, DETAILS_TEXT_SIZE))
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Main program
|
||||
# ------------------------------
|
||||
|
||||
draw_badge()
|
||||
|
||||
while True:
|
||||
if display.pressed(badger2040.BUTTON_A) or display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C) or display.pressed(badger2040.BUTTON_UP) or display.pressed(badger2040.BUTTON_DOWN):
|
||||
badger_os.warning(display, "To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt")
|
||||
time.sleep(4)
|
||||
|
||||
draw_badge()
|
||||
|
||||
display.update()
|
||||
|
||||
# If on battery, halt the Badger to save power, it will wake up if any of the front buttons are pressed
|
||||
display.halt()
|
|
@ -1,183 +0,0 @@
|
|||
"""Keep track of app state in persistent flash storage."""
|
||||
|
||||
import os
|
||||
import gc
|
||||
import time
|
||||
import json
|
||||
import machine
|
||||
import badger2040
|
||||
|
||||
|
||||
def get_battery_level():
|
||||
# Battery measurement
|
||||
vbat_adc = machine.ADC(badger2040.PIN_BATTERY)
|
||||
vref_adc = machine.ADC(badger2040.PIN_1V2_REF)
|
||||
vref_en = machine.Pin(badger2040.PIN_VREF_POWER)
|
||||
vref_en.init(machine.Pin.OUT)
|
||||
vref_en.value(0)
|
||||
|
||||
# Enable the onboard voltage reference
|
||||
vref_en.value(1)
|
||||
|
||||
# Calculate the logic supply voltage, as will be lower that the usual 3.3V when running off low batteries
|
||||
vdd = 1.24 * (65535 / vref_adc.read_u16())
|
||||
vbat = (
|
||||
(vbat_adc.read_u16() / 65535) * 3 * vdd
|
||||
) # 3 in this is a gain, not rounding of 3.3V
|
||||
|
||||
# Disable the onboard voltage reference
|
||||
vref_en.value(0)
|
||||
|
||||
# Convert the voltage to a level to display onscreen
|
||||
return vbat
|
||||
|
||||
|
||||
def get_disk_usage():
|
||||
# f_bfree and f_bavail should be the same?
|
||||
# f_files, f_ffree, f_favail and f_flag are unsupported.
|
||||
f_bsize, f_frsize, f_blocks, f_bfree, _, _, _, _, _, f_namemax = os.statvfs("/")
|
||||
|
||||
f_total_size = f_frsize * f_blocks
|
||||
f_total_free = f_bsize * f_bfree
|
||||
f_total_used = f_total_size - f_total_free
|
||||
|
||||
f_used = 100 / f_total_size * f_total_used
|
||||
f_free = 100 / f_total_size * f_total_free
|
||||
|
||||
return f_total_size, f_used, f_free
|
||||
|
||||
|
||||
def state_running():
|
||||
state = {"running": "launcher"}
|
||||
state_load("launcher", state)
|
||||
return state["running"]
|
||||
|
||||
|
||||
def state_clear_running():
|
||||
running = state_running()
|
||||
state_modify("launcher", {"running": "launcher"})
|
||||
return running != "launcher"
|
||||
|
||||
|
||||
def state_set_running(app):
|
||||
state_modify("launcher", {"running": app})
|
||||
|
||||
|
||||
def state_launch():
|
||||
app = state_running()
|
||||
if app is not None and app != "launcher":
|
||||
launch("_" + app)
|
||||
|
||||
|
||||
def state_delete(app):
|
||||
try:
|
||||
os.remove("/state/{}.json".format(app))
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def state_save(app, data):
|
||||
try:
|
||||
with open("/state/{}.json".format(app), "w") as f:
|
||||
f.write(json.dumps(data))
|
||||
f.flush()
|
||||
except OSError:
|
||||
import os
|
||||
try:
|
||||
os.stat("/state")
|
||||
except OSError:
|
||||
os.mkdir("/state")
|
||||
state_save(app, data)
|
||||
|
||||
|
||||
def state_modify(app, data):
|
||||
state = {}
|
||||
state_load(app, state)
|
||||
state.update(data)
|
||||
state_save(app, state)
|
||||
|
||||
|
||||
def state_load(app, defaults):
|
||||
try:
|
||||
data = json.loads(open("/state/{}.json".format(app), "r").read())
|
||||
if type(data) is dict:
|
||||
defaults.update(data)
|
||||
return True
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
|
||||
state_save(app, defaults)
|
||||
return False
|
||||
|
||||
|
||||
def launch(file):
|
||||
state_set_running(file[1:])
|
||||
|
||||
gc.collect()
|
||||
|
||||
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
|
||||
def quit_to_launcher(pin):
|
||||
if button_a.value() and button_c.value():
|
||||
machine.reset()
|
||||
|
||||
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=quit_to_launcher)
|
||||
button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=quit_to_launcher)
|
||||
|
||||
try:
|
||||
try:
|
||||
__import__(file[1:]) # Try to import _[file] (drop underscore prefix)
|
||||
except ImportError:
|
||||
__import__(file) # Failover to importing [_file]
|
||||
|
||||
except ImportError:
|
||||
# If the app doesn't exist, notify the user
|
||||
warning(None, "Could not launch: " + file[1:])
|
||||
time.sleep(4.0)
|
||||
except Exception as e:
|
||||
# If the app throws an error, catch it and display!
|
||||
print(e)
|
||||
warning(None, str(e))
|
||||
time.sleep(4.0)
|
||||
|
||||
# If the app exits or errors, do not relaunch!
|
||||
state_clear_running()
|
||||
machine.reset() # Exit back to launcher
|
||||
|
||||
|
||||
# Draw an overlay box with a given message within it
|
||||
def warning(display, message, width=badger2040.WIDTH - 40, height=badger2040.HEIGHT - 40, line_spacing=20, text_size=0.6):
|
||||
if display is None:
|
||||
display = badger2040.Badger2040()
|
||||
display.led(128)
|
||||
|
||||
# Draw a light grey background
|
||||
display.pen(12)
|
||||
display.rectangle((badger2040.WIDTH - width) // 2, (badger2040.HEIGHT - height) // 2, width, height)
|
||||
|
||||
# Take the provided message and split it up into
|
||||
# lines that fit within the specified width
|
||||
words = message.split(" ")
|
||||
|
||||
lines = []
|
||||
current_line = ""
|
||||
for word in words:
|
||||
if display.measure_text(current_line + word + " ", text_size) < width:
|
||||
current_line += word + " "
|
||||
else:
|
||||
lines.append(current_line.strip())
|
||||
current_line = word + " "
|
||||
lines.append(current_line.strip())
|
||||
|
||||
display.pen(0)
|
||||
display.thickness(2)
|
||||
|
||||
# Display each line of text from the message, centre-aligned
|
||||
num_lines = len(lines)
|
||||
for i in range(num_lines):
|
||||
length = display.measure_text(lines[i], text_size)
|
||||
current_line = (i * line_spacing) - ((num_lines - 1) * line_spacing) // 2
|
||||
display.text(lines[i], (badger2040.WIDTH - length) // 2, (badger2040.HEIGHT // 2) + current_line, text_size)
|
||||
|
||||
display.update()
|
|
@ -1,163 +0,0 @@
|
|||
import badger2040
|
||||
from machine import Pin, ADC
|
||||
import time
|
||||
|
||||
# Global Constants
|
||||
# for e.g. 2xAAA batteries, try max 3.4 min 3.0
|
||||
MAX_BATTERY_VOLTAGE = 4.0
|
||||
MIN_BATTERY_VOLTAGE = 3.2
|
||||
|
||||
WIDTH = badger2040.WIDTH
|
||||
HEIGHT = badger2040.HEIGHT
|
||||
|
||||
BATT_WIDTH = 200
|
||||
BATT_HEIGHT = 100
|
||||
BATT_BORDER = 10
|
||||
BATT_TERM_WIDTH = 20
|
||||
BATT_TERM_HEIGHT = 50
|
||||
BATT_BAR_PADDING = 10
|
||||
BATT_BAR_HEIGHT = BATT_HEIGHT - (BATT_BORDER * 2) - (BATT_BAR_PADDING * 2)
|
||||
BATT_BAR_START = ((WIDTH - BATT_WIDTH) // 2) + BATT_BORDER + BATT_BAR_PADDING
|
||||
BATT_BAR_END = ((WIDTH + BATT_WIDTH) // 2) - BATT_BORDER - BATT_BAR_PADDING
|
||||
|
||||
NUM_BATT_BARS = 4
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Utility functions
|
||||
# ------------------------------
|
||||
|
||||
|
||||
def map_value(input, in_min, in_max, out_min, out_max):
|
||||
return (((input - in_min) * (out_max - out_min)) / (in_max - in_min)) + out_min
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Drawing functions
|
||||
# ------------------------------
|
||||
|
||||
# Draw the frame of the reader
|
||||
def draw_battery(level, resolution):
|
||||
display.pen(15)
|
||||
display.clear()
|
||||
|
||||
display.thickness(1)
|
||||
|
||||
# Draw the battery outline
|
||||
display.pen(0)
|
||||
display.rectangle(
|
||||
(WIDTH - BATT_WIDTH) // 2, (HEIGHT - BATT_HEIGHT) // 2, BATT_WIDTH, BATT_HEIGHT
|
||||
)
|
||||
|
||||
display.rectangle(
|
||||
(WIDTH + BATT_WIDTH) // 2,
|
||||
(HEIGHT - BATT_TERM_HEIGHT) // 2,
|
||||
BATT_TERM_WIDTH,
|
||||
BATT_TERM_HEIGHT,
|
||||
)
|
||||
|
||||
display.pen(15)
|
||||
display.rectangle(
|
||||
(WIDTH - BATT_WIDTH) // 2 + BATT_BORDER,
|
||||
(HEIGHT - BATT_HEIGHT) // 2 + BATT_BORDER,
|
||||
BATT_WIDTH - BATT_BORDER * 2,
|
||||
BATT_HEIGHT - BATT_BORDER * 2,
|
||||
)
|
||||
|
||||
# Add a special check for no battery
|
||||
if level < 1:
|
||||
X = WIDTH // 2
|
||||
Y = HEIGHT // 2
|
||||
|
||||
display.pen(0)
|
||||
display.thickness(1)
|
||||
thickness = (BATT_BORDER * 3) // 2
|
||||
start_extra = thickness // 3
|
||||
end_extra = (thickness * 2) // 3
|
||||
for i in range(0, thickness):
|
||||
excess = i // 2
|
||||
display.line(
|
||||
X - (BATT_HEIGHT // 2) + i - excess - start_extra,
|
||||
Y - (BATT_HEIGHT // 2) - excess - start_extra,
|
||||
X + (BATT_HEIGHT // 2) + i - excess + end_extra,
|
||||
Y + (BATT_HEIGHT // 2) - excess + end_extra,
|
||||
)
|
||||
display.pen(15)
|
||||
for i in range(0 - thickness, 0):
|
||||
display.line(
|
||||
X - (BATT_HEIGHT // 2) + i,
|
||||
Y - (BATT_HEIGHT // 2),
|
||||
X + (BATT_HEIGHT // 2) + i,
|
||||
Y + (BATT_HEIGHT // 2),
|
||||
)
|
||||
else:
|
||||
# Draw the battery bars
|
||||
display.pen(0)
|
||||
length = (
|
||||
BATT_BAR_END - BATT_BAR_START - ((NUM_BATT_BARS - 1) * BATT_BAR_PADDING)
|
||||
) // NUM_BATT_BARS
|
||||
current_level = 0.0
|
||||
normalised_level = level / resolution
|
||||
for i in range(NUM_BATT_BARS):
|
||||
current_level = (1.0 * i) / NUM_BATT_BARS
|
||||
if normalised_level > current_level:
|
||||
pos = i * (length + BATT_BAR_PADDING)
|
||||
display.rectangle(
|
||||
BATT_BAR_START + pos,
|
||||
(HEIGHT - BATT_BAR_HEIGHT) // 2,
|
||||
length,
|
||||
BATT_BAR_HEIGHT,
|
||||
)
|
||||
|
||||
display.update()
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Program setup
|
||||
# ------------------------------
|
||||
|
||||
# Create a new Badger and set it to update FAST
|
||||
display = badger2040.Badger2040()
|
||||
display.update_speed(badger2040.UPDATE_FAST)
|
||||
|
||||
# Set up the ADCs for measuring battery voltage
|
||||
vbat_adc = ADC(badger2040.PIN_BATTERY)
|
||||
vref_adc = ADC(badger2040.PIN_1V2_REF)
|
||||
vref_en = Pin(badger2040.PIN_VREF_POWER)
|
||||
vref_en.init(Pin.OUT)
|
||||
vref_en.value(0)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Main program loop
|
||||
# ------------------------------
|
||||
|
||||
last_level = -1
|
||||
|
||||
while True:
|
||||
# Enable the onboard voltage reference
|
||||
vref_en.value(1)
|
||||
|
||||
# Calculate the logic supply voltage, as will be lower that the usual 3.3V when running off low batteries
|
||||
vdd = 1.24 * (65535 / vref_adc.read_u16())
|
||||
vbat = (
|
||||
(vbat_adc.read_u16() / 65535) * 3 * vdd
|
||||
) # 3 in this is a gain, not rounding of 3.3V
|
||||
|
||||
# Disable the onboard voltage reference
|
||||
vref_en.value(0)
|
||||
|
||||
# Print out the voltage
|
||||
print("Battery Voltage = ", vbat, "V", sep="")
|
||||
|
||||
# Convert the voltage to a level to display onscreen
|
||||
level = int(
|
||||
map_value(vbat, MIN_BATTERY_VOLTAGE, MAX_BATTERY_VOLTAGE, 0, NUM_BATT_BARS)
|
||||
)
|
||||
|
||||
# Only draw if the battery level has changed significantly
|
||||
if level != last_level:
|
||||
draw_battery(level, NUM_BATT_BARS)
|
||||
last_level = level
|
||||
|
||||
time.sleep(1)
|
|
@ -1,67 +0,0 @@
|
|||
import badger2040
|
||||
import machine
|
||||
import time
|
||||
|
||||
display = badger2040.Badger2040()
|
||||
display.update_speed(badger2040.UPDATE_TURBO)
|
||||
display.pen(15)
|
||||
display.clear()
|
||||
display.update()
|
||||
|
||||
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
# the User button (boot/usr on back of board) is inverted from the others
|
||||
button_user = machine.Pin(badger2040.BUTTON_USER, machine.Pin.IN, machine.Pin.PULL_UP)
|
||||
|
||||
|
||||
message = None
|
||||
message_y = 60
|
||||
|
||||
|
||||
def button(pin):
|
||||
global message
|
||||
if message is not None:
|
||||
return
|
||||
if pin == button_a:
|
||||
message = "Button a"
|
||||
return
|
||||
if pin == button_b:
|
||||
message = "Button b"
|
||||
return
|
||||
if pin == button_c:
|
||||
message = "Button c"
|
||||
return
|
||||
if pin == button_up:
|
||||
message = "Button Up"
|
||||
return
|
||||
if pin == button_down:
|
||||
message = "Button Down"
|
||||
return
|
||||
if pin == button_user:
|
||||
message = "Button Usr"
|
||||
return
|
||||
|
||||
|
||||
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
|
||||
button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
button_user.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
|
||||
|
||||
while True:
|
||||
if message is not None:
|
||||
display.pen(15)
|
||||
display.clear()
|
||||
display.pen(0)
|
||||
display.thickness(4)
|
||||
display.text(message, 6, message_y, 1.4)
|
||||
for _ in range(2):
|
||||
display.update()
|
||||
message = None
|
||||
time.sleep(0.1)
|
|
@ -1,154 +0,0 @@
|
|||
import time
|
||||
import machine
|
||||
import badger2040
|
||||
|
||||
# We're going to keep the badger on, so slow down the system clock if on battery
|
||||
badger2040.system_speed(badger2040.SYSTEM_SLOW)
|
||||
|
||||
rtc = machine.RTC()
|
||||
display = badger2040.Badger2040()
|
||||
display.led(128)
|
||||
display.update_speed(badger2040.UPDATE_TURBO)
|
||||
display.font("gothic")
|
||||
|
||||
cursors = ["year", "month", "day", "hour", "minute"]
|
||||
set_clock = False
|
||||
cursor = 0
|
||||
last = 0
|
||||
|
||||
# Set up the buttons
|
||||
button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
|
||||
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
|
||||
|
||||
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
|
||||
|
||||
time.sleep(0.01)
|
||||
if not pin.value():
|
||||
return
|
||||
|
||||
if button_a.value() and button_c.value():
|
||||
machine.reset()
|
||||
|
||||
adjust = 0
|
||||
changed = False
|
||||
|
||||
if pin == button_b:
|
||||
set_clock = not set_clock
|
||||
changed = True
|
||||
if not set_clock:
|
||||
rtc.datetime((year, month, day, 0, hour, minute, second, 0))
|
||||
|
||||
if set_clock:
|
||||
if pin == button_c:
|
||||
cursor += 1
|
||||
cursor %= len(cursors)
|
||||
|
||||
if pin == button_a:
|
||||
cursor -= 1
|
||||
cursor %= len(cursors)
|
||||
|
||||
if pin == button_up:
|
||||
adjust = 1
|
||||
|
||||
if pin == button_down:
|
||||
adjust = -1
|
||||
|
||||
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 set_clock or changed:
|
||||
draw_clock()
|
||||
|
||||
|
||||
# Register the button handling function with the buttons
|
||||
button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
|
||||
|
||||
def draw_clock():
|
||||
hms = "{:02}:{:02}:{:02}".format(hour, minute, second)
|
||||
ymd = "{:04}/{:02}/{:02}".format(year, month, day)
|
||||
|
||||
hms_width = display.measure_text(hms, 1.8)
|
||||
hms_offset = int((badger2040.WIDTH / 2) - (hms_width / 2))
|
||||
h_width = display.measure_text(hms[0:2], 1.8)
|
||||
mi_width = display.measure_text(hms[3:5], 1.8)
|
||||
mi_offset = display.measure_text(hms[0:3], 1.8)
|
||||
|
||||
ymd_width = display.measure_text(ymd, 1.0)
|
||||
ymd_offset = int((badger2040.WIDTH / 2) - (ymd_width / 2))
|
||||
y_width = display.measure_text(ymd[0:4], 1.0)
|
||||
m_width = display.measure_text(ymd[5:7], 1.0)
|
||||
m_offset = display.measure_text(ymd[0:5], 1.0)
|
||||
d_width = display.measure_text(ymd[8:10], 1.0)
|
||||
d_offset = display.measure_text(ymd[0:8], 1.0)
|
||||
|
||||
display.pen(15)
|
||||
display.clear()
|
||||
display.pen(0)
|
||||
display.thickness(5)
|
||||
display.text(hms, hms_offset, 40, 1.8)
|
||||
display.thickness(3)
|
||||
display.text(ymd, ymd_offset, 100, 1.0)
|
||||
|
||||
if set_clock:
|
||||
if cursors[cursor] == "year":
|
||||
display.line(ymd_offset, 120, ymd_offset + y_width, 120)
|
||||
if cursors[cursor] == "month":
|
||||
display.line(ymd_offset + m_offset, 120, ymd_offset + m_offset + m_width, 120)
|
||||
if cursors[cursor] == "day":
|
||||
display.line(ymd_offset + d_offset, 120, ymd_offset + d_offset + d_width, 120)
|
||||
|
||||
if cursors[cursor] == "hour":
|
||||
display.line(hms_offset, 70, hms_offset + h_width, 70)
|
||||
if cursors[cursor] == "minute":
|
||||
display.line(hms_offset + mi_offset, 70, hms_offset + mi_offset + mi_width, 70)
|
||||
|
||||
display.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)
|
|
@ -1,215 +0,0 @@
|
|||
import math
|
||||
import time
|
||||
from random import random
|
||||
|
||||
import machine
|
||||
|
||||
import badger2040
|
||||
|
||||
# Overclock the RP2040 to run the sim faster
|
||||
badger2040.system_speed(badger2040.SYSTEM_TURBO)
|
||||
|
||||
# ------------------------------
|
||||
# Program setup
|
||||
# ------------------------------
|
||||
|
||||
# Global constants
|
||||
CELL_SIZE = 6 # Size of cell in pixels
|
||||
INITIAL_DENSITY = 0.3 # Density of cells at start
|
||||
|
||||
# Create a new Badger and set it to update TURBO
|
||||
screen = badger2040.Badger2040()
|
||||
screen.led(128)
|
||||
screen.update_speed(badger2040.UPDATE_TURBO)
|
||||
|
||||
restart = False # should sim be restarted
|
||||
|
||||
# ------------------------------
|
||||
# Button functions
|
||||
# ------------------------------
|
||||
|
||||
|
||||
# Button handling function
|
||||
def button(pin):
|
||||
global restart
|
||||
# if 'a' button is pressed, restart the sim
|
||||
if pin == button_a:
|
||||
restart = True
|
||||
|
||||
|
||||
# Set up button
|
||||
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
|
||||
# ------------------------------
|
||||
# Screen functions
|
||||
# ------------------------------
|
||||
|
||||
|
||||
# Remove everything from the screen
|
||||
def init_screen():
|
||||
screen.update_speed(badger2040.UPDATE_NORMAL)
|
||||
screen.pen(15)
|
||||
screen.clear()
|
||||
screen.update()
|
||||
screen.update_speed(badger2040.UPDATE_TURBO)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Classes
|
||||
# ------------------------------
|
||||
|
||||
# Define a 'cell'
|
||||
class Cell:
|
||||
def __init__(self):
|
||||
self._alive = False
|
||||
|
||||
def make_alive(self):
|
||||
self._alive = True
|
||||
|
||||
def make_dead(self):
|
||||
self._alive = False
|
||||
|
||||
def is_alive(self):
|
||||
return self._alive
|
||||
|
||||
|
||||
# Define the whole board
|
||||
class Board:
|
||||
def __init__(self):
|
||||
self._rows = math.floor(badger2040.WIDTH / CELL_SIZE)
|
||||
self._columns = math.floor(badger2040.HEIGHT / CELL_SIZE)
|
||||
self._grid = [[Cell() for _ in range(self._columns)] for _ in range(self._rows)]
|
||||
|
||||
self._initialise_board()
|
||||
|
||||
# Draw the board to the screen
|
||||
def draw_board(self):
|
||||
row_idx = 0
|
||||
column_idx = 0
|
||||
|
||||
for row in self._grid:
|
||||
column_idx = 0
|
||||
for cell in row:
|
||||
if cell.is_alive():
|
||||
screen.pen(0)
|
||||
else:
|
||||
screen.pen(15)
|
||||
screen.rectangle(
|
||||
row_idx * CELL_SIZE, column_idx * CELL_SIZE, CELL_SIZE, CELL_SIZE
|
||||
)
|
||||
column_idx += 1
|
||||
row_idx += 1
|
||||
|
||||
screen.update()
|
||||
|
||||
# Generate the first iteration of the board
|
||||
def _initialise_board(self):
|
||||
for row in self._grid:
|
||||
for cell in row:
|
||||
if random() <= INITIAL_DENSITY:
|
||||
cell.make_alive()
|
||||
|
||||
# Get the neighbour cells for a given cell
|
||||
def get_neighbours(self, current_row, current_column):
|
||||
# Cells either side of current cell
|
||||
neighbour_min = -1
|
||||
neighbour_max = 2
|
||||
neighbours = []
|
||||
|
||||
for row in range(neighbour_min, neighbour_max):
|
||||
for column in range(neighbour_min, neighbour_max):
|
||||
neighbour_row = current_row + row
|
||||
neighbour_column = current_column + column
|
||||
# Don't count the current cell
|
||||
if not (
|
||||
neighbour_row == current_row and neighbour_column == current_column
|
||||
):
|
||||
# It's a toroidal world so go all the way round if necessary
|
||||
if (neighbour_row) < 0:
|
||||
neighbour_row = self._rows - 1
|
||||
elif (neighbour_row) >= self._rows:
|
||||
neighbour_row = 0
|
||||
|
||||
if (neighbour_column) < 0:
|
||||
neighbour_column = self._columns - 1
|
||||
elif (neighbour_column) >= self._columns:
|
||||
neighbour_column = 0
|
||||
|
||||
neighbours.append(self._grid[neighbour_row][neighbour_column])
|
||||
return neighbours
|
||||
|
||||
# Calculate the next generation
|
||||
def create_next_generation(self):
|
||||
to_alive = []
|
||||
to_dead = []
|
||||
changed = False
|
||||
|
||||
for row in range(len(self._grid)):
|
||||
for column in range(len(self._grid[row])):
|
||||
# Get all the neighours that are alive
|
||||
alive_neighbours = []
|
||||
for neighbour_cell in self.get_neighbours(row, column):
|
||||
if neighbour_cell.is_alive():
|
||||
alive_neighbours.append(neighbour_cell)
|
||||
|
||||
current_cell = self._grid[row][column]
|
||||
# Apply the Conway GoL rules (B3/S23)
|
||||
if current_cell.is_alive():
|
||||
if len(alive_neighbours) < 2 or len(alive_neighbours) > 3:
|
||||
to_dead.append(current_cell)
|
||||
if len(alive_neighbours) == 3 or len(alive_neighbours) == 2:
|
||||
to_alive.append(current_cell)
|
||||
else:
|
||||
if len(alive_neighbours) == 3:
|
||||
to_alive.append(current_cell)
|
||||
|
||||
for cell in to_alive:
|
||||
if not cell.is_alive():
|
||||
# The board has changed since the previous generation
|
||||
changed = True
|
||||
cell.make_alive()
|
||||
|
||||
for cell in to_dead:
|
||||
if cell.is_alive():
|
||||
# The board has changed since the previous generation
|
||||
changed = True
|
||||
cell.make_dead()
|
||||
|
||||
return changed
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Main program loop
|
||||
# ------------------------------
|
||||
|
||||
|
||||
def main():
|
||||
global restart
|
||||
|
||||
init_screen()
|
||||
board = Board()
|
||||
board.draw_board()
|
||||
time.sleep(0.5)
|
||||
|
||||
while True:
|
||||
# The 'a' button has been pressed so restart sim
|
||||
if restart:
|
||||
init_screen()
|
||||
restart = False
|
||||
board = Board()
|
||||
board.draw_board()
|
||||
time.sleep(0.5)
|
||||
# The board didn't update since the previous generation
|
||||
if not board.create_next_generation():
|
||||
screen.update_speed(badger2040.UPDATE_NORMAL)
|
||||
board.draw_board()
|
||||
screen.update_speed(badger2040.UPDATE_TURBO)
|
||||
time.sleep(5)
|
||||
restart = True
|
||||
# Draw the next generation
|
||||
else:
|
||||
board.draw_board()
|
||||
|
||||
|
||||
main()
|
|
@ -1,263 +0,0 @@
|
|||
import badger2040
|
||||
import time
|
||||
import gc
|
||||
import badger_os
|
||||
|
||||
# **** Put the name of your text file here *****
|
||||
text_file = "book.txt" # File must be on the MicroPython device
|
||||
|
||||
|
||||
try:
|
||||
open(text_file, "r")
|
||||
except OSError:
|
||||
try:
|
||||
# If the specified file doesn't exist,
|
||||
# pre-populate with Wind In The Willows
|
||||
import witw
|
||||
with open(text_file, "wb") as f:
|
||||
f.write(witw.data())
|
||||
f.flush()
|
||||
time.sleep(0.1)
|
||||
del witw
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
gc.collect()
|
||||
|
||||
# Global Constants
|
||||
WIDTH = badger2040.WIDTH
|
||||
HEIGHT = badger2040.HEIGHT
|
||||
|
||||
ARROW_THICKNESS = 3
|
||||
ARROW_WIDTH = 18
|
||||
ARROW_HEIGHT = 14
|
||||
ARROW_PADDING = 2
|
||||
|
||||
TEXT_PADDING = 4
|
||||
TEXT_WIDTH = WIDTH - TEXT_PADDING - TEXT_PADDING - ARROW_WIDTH
|
||||
|
||||
FONTS = ["sans", "gothic", "cursive", "serif"]
|
||||
FONT_THICKNESSES = [2, 1, 1, 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.pen(15)
|
||||
display.clear()
|
||||
display.pen(12)
|
||||
display.rectangle(WIDTH - ARROW_WIDTH, 0, ARROW_WIDTH, HEIGHT)
|
||||
display.pen(0)
|
||||
display.thickness(ARROW_THICKNESS)
|
||||
if state["current_page"] > 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)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Program setup
|
||||
# ------------------------------
|
||||
|
||||
# Global variables
|
||||
state = {
|
||||
"last_offset": 0,
|
||||
"current_page": 0,
|
||||
"font_idx": 0,
|
||||
"text_size": 0.5,
|
||||
"offsets": []
|
||||
}
|
||||
badger_os.state_load("ebook", state)
|
||||
|
||||
text_spacing = int(34 * state["text_size"])
|
||||
|
||||
|
||||
# Create a new Badger and set it to update FAST
|
||||
display = badger2040.Badger2040()
|
||||
display.led(128)
|
||||
display.update_speed(badger2040.UPDATE_FAST)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Render page
|
||||
# ------------------------------
|
||||
|
||||
def render_page():
|
||||
row = 0
|
||||
line = ""
|
||||
pos = ebook.tell()
|
||||
next_pos = pos
|
||||
add_newline = False
|
||||
display.font(FONTS[state["font_idx"]])
|
||||
|
||||
while True:
|
||||
# Read a full line and split it into words
|
||||
words = ebook.readline().split(" ")
|
||||
|
||||
# Take the length of the first word and advance our position
|
||||
next_word = words[0]
|
||||
if len(words) > 1:
|
||||
next_pos += len(next_word) + 1
|
||||
else:
|
||||
next_pos += len(next_word) # This is the last word on the line
|
||||
|
||||
# Advance our position further if the word contains special characters
|
||||
if '\u201c' in next_word:
|
||||
next_word = next_word.replace('\u201c', '\"')
|
||||
next_pos += 2
|
||||
if '\u201d' in next_word:
|
||||
next_word = next_word.replace('\u201d', '\"')
|
||||
next_pos += 2
|
||||
if '\u2019' in next_word:
|
||||
next_word = next_word.replace('\u2019', '\'')
|
||||
next_pos += 2
|
||||
|
||||
# Rewind the file back from the line end to the start of the next word
|
||||
ebook.seek(next_pos)
|
||||
|
||||
# Strip out any new line characters from the word
|
||||
next_word = next_word.strip()
|
||||
|
||||
# If an empty word is encountered assume that means there was a blank line
|
||||
if len(next_word) == 0:
|
||||
add_newline = True
|
||||
|
||||
# Append the word to the current line and measure its length
|
||||
appended_line = line
|
||||
if len(line) > 0 and len(next_word) > 0:
|
||||
appended_line += " "
|
||||
appended_line += next_word
|
||||
appended_length = display.measure_text(appended_line, state["text_size"])
|
||||
|
||||
# Would this appended line be longer than the text display area, or was a blank line spotted?
|
||||
if appended_length >= TEXT_WIDTH or add_newline:
|
||||
|
||||
# Yes, so write out the line prior to the append
|
||||
print(line)
|
||||
display.pen(0)
|
||||
display.thickness(FONT_THICKNESSES[0])
|
||||
display.text(line, TEXT_PADDING, (row * text_spacing) + (text_spacing // 2) + TEXT_PADDING, state["text_size"])
|
||||
|
||||
# Clear the line and move on to the next row
|
||||
line = ""
|
||||
row += 1
|
||||
|
||||
# Have we reached the end of the page?
|
||||
if (row * text_spacing) + text_spacing >= HEIGHT:
|
||||
print("+++++")
|
||||
display.update()
|
||||
|
||||
# Reset the position to the start of the word that made this line too long
|
||||
ebook.seek(pos)
|
||||
return
|
||||
else:
|
||||
# Set the line to the word and advance the current position
|
||||
line = next_word
|
||||
pos = next_pos
|
||||
|
||||
# A new line was spotted, so advance a row
|
||||
if add_newline:
|
||||
print("")
|
||||
row += 1
|
||||
if (row * text_spacing) + text_spacing >= HEIGHT:
|
||||
print("+++++")
|
||||
display.update()
|
||||
return
|
||||
add_newline = False
|
||||
else:
|
||||
# The appended line was not too long, so set it as the line and advance the current position
|
||||
line = appended_line
|
||||
pos = next_pos
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Main program loop
|
||||
# ------------------------------
|
||||
|
||||
launch = True
|
||||
changed = False
|
||||
|
||||
# Open the book file
|
||||
ebook = open(text_file, "r")
|
||||
if len(state["offsets"]) > state["current_page"]:
|
||||
ebook.seek(state["offsets"][state["current_page"]])
|
||||
else:
|
||||
state["current_page"] = 0
|
||||
state["offsets"] = []
|
||||
|
||||
while True:
|
||||
# Was the next page button pressed?
|
||||
if display.pressed(badger2040.BUTTON_DOWN):
|
||||
state["current_page"] += 1
|
||||
|
||||
changed = True
|
||||
|
||||
# Was the previous page button pressed?
|
||||
if display.pressed(badger2040.BUTTON_UP):
|
||||
if state["current_page"] > 0:
|
||||
state["current_page"] -= 1
|
||||
if state["current_page"] == 0:
|
||||
ebook.seek(0)
|
||||
else:
|
||||
ebook.seek(state["offsets"][state["current_page"] - 1]) # Retrieve the start position of the last page
|
||||
changed = True
|
||||
|
||||
if display.pressed(badger2040.BUTTON_A):
|
||||
state["text_size"] += 0.1
|
||||
if state["text_size"] > 0.8:
|
||||
state["text_size"] = 0.5
|
||||
text_spacing = int(34 * state["text_size"])
|
||||
state["offsets"] = []
|
||||
ebook.seek(0)
|
||||
state["current_page"] = 0
|
||||
changed = True
|
||||
|
||||
if display.pressed(badger2040.BUTTON_B):
|
||||
state["font_idx"] += 1
|
||||
if (state["font_idx"] >= len(FONTS)):
|
||||
state["font_idx"] = 0
|
||||
state["offsets"] = []
|
||||
ebook.seek(0)
|
||||
state["current_page"] = 0
|
||||
changed = True
|
||||
|
||||
if launch and not changed:
|
||||
if state["current_page"] > 0 and len(state["offsets"]) > state["current_page"] - 1:
|
||||
ebook.seek(state["offsets"][state["current_page"] - 1])
|
||||
changed = True
|
||||
launch = False
|
||||
|
||||
if changed:
|
||||
draw_frame()
|
||||
render_page()
|
||||
|
||||
# Is the next page one we've not displayed before?
|
||||
if state["current_page"] >= len(state["offsets"]):
|
||||
state["offsets"].append(ebook.tell()) # Add its start position to the state["offsets"] list
|
||||
badger_os.state_save("ebook", state)
|
||||
|
||||
changed = False
|
||||
|
||||
display.halt()
|
|
@ -1,134 +0,0 @@
|
|||
import badger2040
|
||||
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 = badger2040.WIDTH
|
||||
HEIGHT = badger2040.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.pen(15)
|
||||
display.clear()
|
||||
display.pen(12)
|
||||
display.rectangle(WIDTH - ARROW_WIDTH, 0, ARROW_WIDTH, HEIGHT)
|
||||
display.pen(0)
|
||||
display.thickness(ARROW_THICKNESS)
|
||||
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.font("bitmap8")
|
||||
display.thickness(1)
|
||||
for i in range(len(FONT_NAMES)):
|
||||
name, size, thickness = FONT_NAMES[i]
|
||||
display.pen(0)
|
||||
if i == state["selected_font"]:
|
||||
display.rectangle(0, i * MENU_SPACING, MENU_WIDTH, MENU_SPACING)
|
||||
display.pen(15)
|
||||
|
||||
display.text(name, MENU_PADDING, (i * MENU_SPACING) + int((MENU_SPACING - 8) / 2), MENU_TEXT_SIZE)
|
||||
|
||||
name, size, thickness = FONT_NAMES[state["selected_font"]]
|
||||
display.font(name)
|
||||
display.thickness(thickness)
|
||||
|
||||
y = 0 if name.startswith("bitmap") else 10
|
||||
|
||||
display.pen(0)
|
||||
for line in ("The quick", "brown fox", "jumps over", "the lazy dog.", "0123456789", "!\"£$%^&*()"):
|
||||
display.text(line, TEXT_INDENT, y, size)
|
||||
y += 22
|
||||
|
||||
display.thickness(1)
|
||||
|
||||
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 = badger2040.Badger2040()
|
||||
display.led(128)
|
||||
display.update_speed(badger2040.UPDATE_FAST)
|
||||
|
||||
changed = not badger2040.woken_by_button()
|
||||
|
||||
# ------------------------------
|
||||
# Main program loop
|
||||
# ------------------------------
|
||||
|
||||
while True:
|
||||
if display.pressed(badger2040.BUTTON_UP):
|
||||
state["selected_font"] -= 1
|
||||
if state["selected_font"] < 0:
|
||||
state["selected_font"] = len(FONT_NAMES) - 1
|
||||
changed = True
|
||||
if display.pressed(badger2040.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()
|
|
@ -1,42 +0,0 @@
|
|||
import badger2040
|
||||
from badger2040 import WIDTH
|
||||
|
||||
TEXT_SIZE = 0.45
|
||||
LINE_HEIGHT = 20
|
||||
|
||||
display = badger2040.Badger2040()
|
||||
display.led(128)
|
||||
|
||||
display.pen(0)
|
||||
display.rectangle(0, 0, WIDTH, 16)
|
||||
display.thickness(1)
|
||||
display.pen(15)
|
||||
display.text("badgerOS", 3, 8, 0.4)
|
||||
display.text("help", WIDTH - display.measure_text("help", 0.4) - 4, 8, 0.4)
|
||||
|
||||
display.pen(0)
|
||||
|
||||
TEXT_SIZE = 0.62
|
||||
y = 20 + int(LINE_HEIGHT / 2)
|
||||
|
||||
display.thickness(2)
|
||||
display.font("sans")
|
||||
display.text("Up/Down - Change page", 0, y, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
display.text("a, b or c - Launch app", 0, y, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
display.text("a & c - Exit app", 0, y, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
|
||||
y += 8
|
||||
display.text("Up/Down & User - Font size", 0, y, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
display.text("a & User - Toggle invert", 0, y, 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()
|
|
@ -1,127 +0,0 @@
|
|||
import os
|
||||
import sys
|
||||
import time
|
||||
import badger2040
|
||||
from badger2040 import HEIGHT
|
||||
import badger_os
|
||||
|
||||
|
||||
REAMDE = """
|
||||
Images must be 296x128 pixel with 1bit colour depth.
|
||||
|
||||
You can use examples/badger2040/image_converter/convert.py to convert them:
|
||||
|
||||
python3 convert.py --binary --resize image_file_1.png image_file_2.png image_file_3.png
|
||||
|
||||
Create a new "images" directory via Thonny, and upload the .bin 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 = badger2040.Badger2040()
|
||||
display.led(128)
|
||||
|
||||
# Try to preload BadgerPunk image
|
||||
try:
|
||||
os.mkdir("images")
|
||||
import badgerpunk
|
||||
with open("images/badgerpunk.bin", "wb") as f:
|
||||
f.write(badgerpunk.data())
|
||||
f.flush()
|
||||
with open("images/readme.txt", "w") as f:
|
||||
f.write(REAMDE)
|
||||
f.flush()
|
||||
del badgerpunk
|
||||
except (OSError, ImportError):
|
||||
pass
|
||||
|
||||
# Load images
|
||||
try:
|
||||
IMAGES = [f for f in os.listdir("/images") if f.endswith(".bin")]
|
||||
TOTAL_IMAGES = len(IMAGES)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
image = bytearray(int(296 * 128 / 8))
|
||||
|
||||
state = {
|
||||
"current_image": 0,
|
||||
"show_info": True
|
||||
}
|
||||
|
||||
|
||||
def show_image(n):
|
||||
file = IMAGES[n]
|
||||
name = file.split(".")[0]
|
||||
open("images/{}".format(file), "r").readinto(image)
|
||||
display.image(image)
|
||||
|
||||
if state["show_info"]:
|
||||
name_length = display.measure_text(name, 0.5)
|
||||
display.pen(0)
|
||||
display.rectangle(0, HEIGHT - 21, name_length + 11, 21)
|
||||
display.pen(15)
|
||||
display.rectangle(0, HEIGHT - 20, name_length + 10, 20)
|
||||
display.pen(0)
|
||||
display.text(name, 5, HEIGHT - 10, 0.5)
|
||||
|
||||
for i in range(TOTAL_IMAGES):
|
||||
x = 286
|
||||
y = int((128 / 2) - (TOTAL_IMAGES * 10 / 2) + (i * 10))
|
||||
display.pen(0)
|
||||
display.rectangle(x, y, 8, 8)
|
||||
if state["current_image"] != i:
|
||||
display.pen(15)
|
||||
display.rectangle(x + 1, y + 1, 6, 6)
|
||||
|
||||
display.update()
|
||||
|
||||
|
||||
if TOTAL_IMAGES == 0:
|
||||
display.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 badger2040.woken_by_button()
|
||||
|
||||
|
||||
while True:
|
||||
if display.pressed(badger2040.BUTTON_UP):
|
||||
if state["current_image"] > 0:
|
||||
state["current_image"] -= 1
|
||||
changed = True
|
||||
if display.pressed(badger2040.BUTTON_DOWN):
|
||||
if state["current_image"] < TOTAL_IMAGES - 1:
|
||||
state["current_image"] += 1
|
||||
changed = True
|
||||
if display.pressed(badger2040.BUTTON_A):
|
||||
state["show_info"] = not state["show_info"]
|
||||
changed = True
|
||||
if display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C):
|
||||
display.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()
|
|
@ -1,42 +0,0 @@
|
|||
import badger2040
|
||||
from badger2040 import WIDTH
|
||||
|
||||
TEXT_SIZE = 0.45
|
||||
LINE_HEIGHT = 16
|
||||
|
||||
display = badger2040.Badger2040()
|
||||
display.led(128)
|
||||
|
||||
display.pen(0)
|
||||
display.rectangle(0, 0, WIDTH, 16)
|
||||
display.thickness(1)
|
||||
display.pen(15)
|
||||
display.text("badgerOS", 3, 8, 0.4)
|
||||
display.text("info", WIDTH - display.measure_text("help", 0.4) - 4, 8, 0.4)
|
||||
|
||||
display.pen(0)
|
||||
|
||||
y = 16 + int(LINE_HEIGHT / 2)
|
||||
|
||||
display.text("Made by Pimoroni, powered by MicroPython", 0, y, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
display.text("Dual-core RP2040, 133MHz, 264KB RAM", 0, y, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
display.text("2MB Flash (1MB OS, 1MB Storage)", 0, y, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
display.text("296x128 pixel Black/White e-Ink", 0, y, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
y += LINE_HEIGHT
|
||||
|
||||
display.thickness(2)
|
||||
display.text("For more info:", 0, y, TEXT_SIZE)
|
||||
display.thickness(1)
|
||||
y += LINE_HEIGHT
|
||||
display.text("https://pimoroni.com/badger2040", 0, y, 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()
|
|
@ -1,241 +0,0 @@
|
|||
import gc
|
||||
import time
|
||||
import math
|
||||
import badger2040
|
||||
from badger2040 import WIDTH
|
||||
import launchericons
|
||||
import badger_os
|
||||
|
||||
# Reduce clock speed to 48MHz
|
||||
badger2040.system_speed(badger2040.SYSTEM_NORMAL)
|
||||
|
||||
changed = False
|
||||
exited_to_launcher = False
|
||||
woken_by_button = badger2040.woken_by_button() # Must be done before we clear_pressed_to_wake
|
||||
|
||||
if badger2040.pressed_to_wake(badger2040.BUTTON_A) and badger2040.pressed_to_wake(badger2040.BUTTON_C):
|
||||
# Pressing A and C together at start quits app
|
||||
exited_to_launcher = badger_os.state_clear_running()
|
||||
else:
|
||||
# Otherwise restore previously running app
|
||||
badger_os.state_launch()
|
||||
|
||||
# for e.g. 2xAAA batteries, try max 3.4 min 3.0
|
||||
MAX_BATTERY_VOLTAGE = 4.0
|
||||
MIN_BATTERY_VOLTAGE = 3.2
|
||||
|
||||
display = badger2040.Badger2040()
|
||||
display.led(128)
|
||||
|
||||
state = {
|
||||
"page": 0,
|
||||
"font_size": 1,
|
||||
"inverted": False,
|
||||
"running": "launcher"
|
||||
}
|
||||
|
||||
badger_os.state_load("launcher", state)
|
||||
|
||||
display.invert(state["inverted"])
|
||||
|
||||
icons = bytearray(launchericons.data())
|
||||
icons_width = 576
|
||||
|
||||
examples = [
|
||||
("_clock", 0),
|
||||
("_fonts", 1),
|
||||
("_ebook", 2),
|
||||
("_image", 3),
|
||||
("_list", 4),
|
||||
("_badge", 5),
|
||||
("_qrgen", 8),
|
||||
("_info", 6),
|
||||
("_help", 7),
|
||||
]
|
||||
|
||||
font_sizes = (0.5, 0.7, 0.9)
|
||||
|
||||
# Approximate center lines for buttons A, B and C
|
||||
centers = (41, 147, 253)
|
||||
|
||||
MAX_PAGE = math.ceil(len(examples) / 3)
|
||||
|
||||
|
||||
def map_value(input, in_min, in_max, out_min, out_max):
|
||||
return (((input - in_min) * (out_max - out_min)) / (in_max - in_min)) + out_min
|
||||
|
||||
|
||||
def draw_battery(level, x, y):
|
||||
# Outline
|
||||
display.thickness(1)
|
||||
display.pen(15)
|
||||
display.rectangle(x, y, 19, 10)
|
||||
# Terminal
|
||||
display.rectangle(x + 19, y + 3, 2, 4)
|
||||
display.pen(0)
|
||||
display.rectangle(x + 1, y + 1, 17, 8)
|
||||
if level < 1:
|
||||
display.pen(0)
|
||||
display.line(x + 3, y, x + 3 + 10, y + 10)
|
||||
display.line(x + 3 + 1, y, x + 3 + 11, y + 10)
|
||||
display.pen(15)
|
||||
display.line(x + 2 + 2, y - 1, x + 4 + 12, y + 11)
|
||||
display.line(x + 2 + 3, y - 1, x + 4 + 13, y + 11)
|
||||
return
|
||||
# Battery Bars
|
||||
display.pen(15)
|
||||
for i in range(4):
|
||||
if level / 4 > (1.0 * i) / 4:
|
||||
display.rectangle(i * 4 + x + 2, y + 2, 3, 6)
|
||||
|
||||
|
||||
def draw_disk_usage(x):
|
||||
_, f_used, _ = badger_os.get_disk_usage()
|
||||
|
||||
display.image(
|
||||
bytearray(
|
||||
(
|
||||
0b00000000,
|
||||
0b00111100,
|
||||
0b00111100,
|
||||
0b00111100,
|
||||
0b00111000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000001,
|
||||
)
|
||||
),
|
||||
8,
|
||||
8,
|
||||
x,
|
||||
4,
|
||||
)
|
||||
display.pen(15)
|
||||
display.rectangle(x + 10, 3, 80, 10)
|
||||
display.pen(0)
|
||||
display.rectangle(x + 11, 4, 78, 8)
|
||||
display.pen(15)
|
||||
display.rectangle(x + 12, 5, int(76 / 100.0 * f_used), 6)
|
||||
display.text("{:.2f}%".format(f_used), x + 91, 8, 0.4)
|
||||
|
||||
|
||||
def render():
|
||||
display.pen(15)
|
||||
display.clear()
|
||||
display.pen(0)
|
||||
display.thickness(2)
|
||||
|
||||
max_icons = min(3, len(examples[(state["page"] * 3):]))
|
||||
|
||||
for i in range(max_icons):
|
||||
x = centers[i]
|
||||
label, icon = examples[i + (state["page"] * 3)]
|
||||
label = label[1:].replace("_", " ")
|
||||
display.pen(0)
|
||||
display.icon(icons, icon, icons_width, 64, x - 32, 24)
|
||||
w = display.measure_text(label, font_sizes[state["font_size"]])
|
||||
display.text(label, x - int(w / 2), 16 + 80, font_sizes[state["font_size"]])
|
||||
|
||||
for i in range(MAX_PAGE):
|
||||
x = 286
|
||||
y = int((128 / 2) - (MAX_PAGE * 10 / 2) + (i * 10))
|
||||
display.pen(0)
|
||||
display.rectangle(x, y, 8, 8)
|
||||
if state["page"] != i:
|
||||
display.pen(15)
|
||||
display.rectangle(x + 1, y + 1, 6, 6)
|
||||
|
||||
display.pen(0)
|
||||
display.rectangle(0, 0, WIDTH, 16)
|
||||
display.thickness(1)
|
||||
draw_disk_usage(90)
|
||||
vbat = badger_os.get_battery_level()
|
||||
bat = int(map_value(vbat, MIN_BATTERY_VOLTAGE, MAX_BATTERY_VOLTAGE, 0, 4))
|
||||
draw_battery(bat, WIDTH - 22 - 3, 3)
|
||||
display.pen(15)
|
||||
display.text("badgerOS", 3, 8, 0.4)
|
||||
|
||||
display.update()
|
||||
|
||||
|
||||
def wait_for_user_to_release_buttons():
|
||||
pr = display.pressed
|
||||
while pr(badger2040.BUTTON_A) or pr(badger2040.BUTTON_B) or pr(badger2040.BUTTON_C) or pr(badger2040.BUTTON_UP) or pr(badger2040.BUTTON_DOWN):
|
||||
time.sleep(0.01)
|
||||
|
||||
|
||||
def launch_example(index):
|
||||
wait_for_user_to_release_buttons()
|
||||
|
||||
file = examples[(state["page"] * 3) + index][0]
|
||||
|
||||
for k in locals().keys():
|
||||
if k not in ("gc", "file", "badger_os"):
|
||||
del locals()[k]
|
||||
|
||||
gc.collect()
|
||||
|
||||
badger_os.launch(file)
|
||||
|
||||
|
||||
def button(pin):
|
||||
global changed
|
||||
changed = True
|
||||
|
||||
if not display.pressed(badger2040.BUTTON_USER): # User button is NOT held down
|
||||
if pin == badger2040.BUTTON_A:
|
||||
launch_example(0)
|
||||
if pin == badger2040.BUTTON_B:
|
||||
launch_example(1)
|
||||
if pin == badger2040.BUTTON_C:
|
||||
launch_example(2)
|
||||
if pin == badger2040.BUTTON_UP:
|
||||
if state["page"] > 0:
|
||||
state["page"] -= 1
|
||||
render()
|
||||
if pin == badger2040.BUTTON_DOWN:
|
||||
if state["page"] < MAX_PAGE - 1:
|
||||
state["page"] += 1
|
||||
render()
|
||||
else: # User button IS held down
|
||||
if pin == badger2040.BUTTON_UP:
|
||||
state["font_size"] += 1
|
||||
if state["font_size"] == len(font_sizes):
|
||||
state["font_size"] = 0
|
||||
render()
|
||||
if pin == badger2040.BUTTON_DOWN:
|
||||
state["font_size"] -= 1
|
||||
if state["font_size"] < 0:
|
||||
state["font_size"] = 0
|
||||
render()
|
||||
if pin == badger2040.BUTTON_A:
|
||||
state["inverted"] = not state["inverted"]
|
||||
display.invert(state["inverted"])
|
||||
render()
|
||||
|
||||
|
||||
if exited_to_launcher or not woken_by_button:
|
||||
wait_for_user_to_release_buttons()
|
||||
display.update_speed(badger2040.UPDATE_MEDIUM)
|
||||
render()
|
||||
|
||||
display.update_speed(badger2040.UPDATE_FAST)
|
||||
|
||||
while True:
|
||||
if display.pressed(badger2040.BUTTON_A):
|
||||
button(badger2040.BUTTON_A)
|
||||
if display.pressed(badger2040.BUTTON_B):
|
||||
button(badger2040.BUTTON_B)
|
||||
if display.pressed(badger2040.BUTTON_C):
|
||||
button(badger2040.BUTTON_C)
|
||||
|
||||
if display.pressed(badger2040.BUTTON_UP):
|
||||
button(badger2040.BUTTON_UP)
|
||||
if display.pressed(badger2040.BUTTON_DOWN):
|
||||
button(badger2040.BUTTON_DOWN)
|
||||
|
||||
if changed:
|
||||
badger_os.state_save("launcher", state)
|
||||
changed = False
|
||||
|
||||
display.halt()
|
|
@ -1,15 +0,0 @@
|
|||
# Blinky badger fun!
|
||||
|
||||
import badger2040
|
||||
import time
|
||||
|
||||
badger = badger2040.Badger2040()
|
||||
|
||||
while True:
|
||||
# increase badger.led brightness from 0 to 255 and back down to zero
|
||||
for i in range(0, 255):
|
||||
badger.led(i)
|
||||
time.sleep_ms(10)
|
||||
for i in range(255, 0, -1):
|
||||
badger.led(i)
|
||||
time.sleep_ms(10)
|
|
@ -1,315 +0,0 @@
|
|||
import binascii
|
||||
|
||||
import badger2040
|
||||
import badger_os
|
||||
|
||||
# **** Put your list title here *****
|
||||
list_title = "Checklist"
|
||||
list_file = "checklist.txt"
|
||||
|
||||
|
||||
# Global Constants
|
||||
WIDTH = badger2040.WIDTH
|
||||
HEIGHT = badger2040.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.pen(12)
|
||||
display.rectangle(item_x, item_y + y - (item_height // 2), width // columns, item_height)
|
||||
display.pen(0)
|
||||
display.text(items[i], item_x + x + item_height, item_y + y, 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.pen(background)
|
||||
display.rectangle(x + border, y + border, size - (border * 2), size - (border * 2))
|
||||
display.pen(foreground)
|
||||
display.thickness(thickness)
|
||||
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 badger2040.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 = badger2040.Badger2040()
|
||||
display.led(128)
|
||||
if changed:
|
||||
display.update_speed(badger2040.UPDATE_FAST)
|
||||
else:
|
||||
display.update_speed(badger2040.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(badger2040.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(badger2040.UPDATE_FAST)
|
||||
changed = True
|
||||
if display.pressed(badger2040.BUTTON_B):
|
||||
state["checked"][state["current_item"]] = not state["checked"][state["current_item"]]
|
||||
changed = True
|
||||
if display.pressed(badger2040.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(badger2040.UPDATE_FAST)
|
||||
changed = True
|
||||
if display.pressed(badger2040.BUTTON_UP):
|
||||
if state["current_item"] > 0:
|
||||
state["current_item"] -= 1
|
||||
changed = True
|
||||
if display.pressed(badger2040.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.pen(15)
|
||||
display.clear()
|
||||
|
||||
display.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.pen(0)
|
||||
display.thickness(3)
|
||||
display.text(list_title, LIST_PADDING, y, TITLE_TEXT_SIZE)
|
||||
|
||||
y += 12
|
||||
display.pen(0)
|
||||
display.thickness(2)
|
||||
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.pen(0)
|
||||
display.thickness(2)
|
||||
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.pen(0)
|
||||
display.thickness(ARROW_THICKNESS)
|
||||
|
||||
# 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), ITEM_TEXT_SIZE)
|
||||
|
||||
display.update()
|
||||
display.update_speed(badger2040.UPDATE_TURBO)
|
||||
changed = False
|
||||
|
||||
display.halt()
|
|
@ -1,55 +0,0 @@
|
|||
function (convert_image TARGET IMAGE)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/../modules/${IMAGE}.py
|
||||
|
||||
COMMAND
|
||||
cd ${CMAKE_CURRENT_LIST_DIR}/assets && python3 ../../../../examples/badger2040/image_converter/convert.py --out_dir ${CMAKE_CURRENT_BINARY_DIR}/../modules --py ${IMAGE}.png
|
||||
|
||||
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/assets/${IMAGE}.png
|
||||
)
|
||||
target_sources(${TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/../modules/${IMAGE}.py)
|
||||
endfunction()
|
||||
|
||||
function (convert_raw TARGET SRC DST)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py
|
||||
|
||||
COMMAND
|
||||
cd ${CMAKE_CURRENT_LIST_DIR}/assets && python3 ../../../../examples/badger2040/image_converter/data_to_py.py ${CMAKE_CURRENT_LIST_DIR}/assets/${SRC} ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py
|
||||
|
||||
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/assets/${SRC}
|
||||
)
|
||||
target_sources(${TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py)
|
||||
endfunction()
|
||||
|
||||
function (copy_module TARGET SRC DST)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py
|
||||
|
||||
COMMAND
|
||||
cp ${SRC} ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py
|
||||
|
||||
DEPENDS ${SRC}
|
||||
)
|
||||
|
||||
target_sources(${TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/../modules/${DST}.py)
|
||||
endfunction()
|
||||
|
||||
convert_image(usermod_badger2040 badge_image)
|
||||
convert_image(usermod_badger2040 badgerpunk)
|
||||
convert_image(usermod_badger2040 launchericons)
|
||||
|
||||
convert_raw(usermod_badger2040 289-0-wind-in-the-willows-abridged.txt witw)
|
||||
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/assets/boot.py boot)
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/launcher.py _launcher)
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/clock.py _clock)
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/fonts.py _fonts)
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/ebook.py _ebook)
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/image.py _image)
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/list.py _list)
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/badge.py _badge)
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/help.py _help)
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/info.py _info)
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/qrgen.py _qrgen)
|
||||
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/badger_os.py badger_os)
|
|
@ -1,65 +0,0 @@
|
|||
import badger2040
|
||||
import machine
|
||||
|
||||
display = badger2040.Badger2040()
|
||||
|
||||
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
|
||||
display.thickness(10)
|
||||
display.pen(0)
|
||||
display.line(0, 5, 295, 5)
|
||||
display.line(0, 123, 295, 123)
|
||||
|
||||
display.thickness(1)
|
||||
for x in range(14):
|
||||
display.line(x * 20, 10, x * 20, 118)
|
||||
|
||||
display.line(0, 0, 295, 127)
|
||||
display.line(0, 127, 295, 0)
|
||||
|
||||
display.font("sans")
|
||||
display.thickness(5)
|
||||
display.text("Hello World", 10, 30, 1.0)
|
||||
display.pen(7)
|
||||
display.text("Hello World", 10, 60, 1.0)
|
||||
display.pen(11)
|
||||
display.text("Hello World", 10, 90, 1.0)
|
||||
|
||||
display.update()
|
||||
|
||||
dirty = False
|
||||
pressed = None
|
||||
|
||||
|
||||
def button(pin):
|
||||
global dirty, pressed
|
||||
if pin == button_a:
|
||||
pressed = "Button A"
|
||||
dirty = True
|
||||
return
|
||||
if pin == button_b:
|
||||
pressed = "Button B"
|
||||
dirty = True
|
||||
return
|
||||
|
||||
|
||||
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
||||
|
||||
# This breaks Thonny, since it's no longer possible to Stop the code
|
||||
# need to press the reset button on the board...
|
||||
# It will also crash your USB bus, probably, your whole bus...
|
||||
# @micropython.asm_thumb
|
||||
# def lightsleep():
|
||||
# wfi()
|
||||
|
||||
while True:
|
||||
if dirty:
|
||||
display.pen(15)
|
||||
display.clear()
|
||||
display.pen(0)
|
||||
display.text(pressed, 10, 60, 2.0)
|
||||
display.update()
|
||||
dirty = False
|
||||
# machine.lightsleep() # Currently imposible to wake from this on IRQ
|
|
@ -1,155 +0,0 @@
|
|||
import badger2040
|
||||
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
|
||||
|
||||
# Load all available QR Code Files
|
||||
try:
|
||||
CODES = [f for f in os.listdir("/qrcodes") if f.endswith(".txt")]
|
||||
except OSError:
|
||||
CODES = []
|
||||
|
||||
# create demo QR code file if no QR code files exist
|
||||
if len(CODES) == 0:
|
||||
try:
|
||||
new_qr_code_filename = "qrcode.txt"
|
||||
with open(f"/qrcodes/{new_qr_code_filename}", "w") as text:
|
||||
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()
|
||||
|
||||
# Set the CODES list to contain the new_qr_code_filename (created above)
|
||||
CODES = [new_qr_code_filename]
|
||||
|
||||
except OSError:
|
||||
CODES = []
|
||||
|
||||
TOTAL_CODES = len(CODES)
|
||||
|
||||
display = badger2040.Badger2040()
|
||||
|
||||
code = qrcode.QRCode()
|
||||
|
||||
|
||||
state = {
|
||||
"current_qr": 0
|
||||
}
|
||||
|
||||
|
||||
def set_state_current_index_in_range():
|
||||
badger_os.state_load("qrcodes", state)
|
||||
if state["current_qr"] >= len(CODES):
|
||||
state["current_qr"] = len(CODES) - 1 # set to last index (zero-based). Note: will set to -1 if currently 0
|
||||
if state["current_qr"] < 0: # check that the index is not negative, thus still out of range
|
||||
state["current_qr"] = 0
|
||||
badger_os.state_save("qrcodes", state)
|
||||
|
||||
|
||||
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.pen(15)
|
||||
display.rectangle(ox, oy, size, size)
|
||||
display.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]
|
||||
|
||||
try:
|
||||
with open(f"/qrcodes/{file}", "r") as codetext:
|
||||
lines = codetext.read().strip().split("\n")
|
||||
except OSError:
|
||||
lines = ["", "", "", "", "", "", "", "", "", ""]
|
||||
|
||||
code_text = lines.pop(0)
|
||||
title_text = lines.pop(0)
|
||||
detail_text = lines
|
||||
|
||||
# Clear the Display
|
||||
display.pen(15) # Change this to 0 if a white background is used
|
||||
display.clear()
|
||||
display.pen(0)
|
||||
|
||||
code.set_text(code_text)
|
||||
size, _ = measure_qr_code(128, code)
|
||||
left = top = int((badger2040.HEIGHT / 2) - (size / 2))
|
||||
draw_qr_code(left, top, 128, code)
|
||||
|
||||
left = 128 + 5
|
||||
|
||||
display.thickness(2)
|
||||
display.text(title_text, left, 20, 0.5)
|
||||
display.thickness(1)
|
||||
|
||||
top = 40
|
||||
for line in detail_text:
|
||||
display.text(line, left, top, 0.4)
|
||||
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.pen(0)
|
||||
display.rectangle(x, y, 8, 8)
|
||||
if state["current_qr"] != i:
|
||||
display.pen(15)
|
||||
display.rectangle(x + 1, y + 1, 6, 6)
|
||||
display.update()
|
||||
|
||||
|
||||
set_state_current_index_in_range()
|
||||
changed = not badger2040.woken_by_button()
|
||||
|
||||
while True:
|
||||
if TOTAL_CODES > 1:
|
||||
if display.pressed(badger2040.BUTTON_UP):
|
||||
if state["current_qr"] > 0:
|
||||
state["current_qr"] -= 1
|
||||
changed = True
|
||||
|
||||
if display.pressed(badger2040.BUTTON_DOWN):
|
||||
if state["current_qr"] < TOTAL_CODES - 1:
|
||||
state["current_qr"] += 1
|
||||
changed = True
|
||||
|
||||
if display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C):
|
||||
display.pen(15)
|
||||
display.clear()
|
||||
badger_os.warning(display, "To add QR codes, connect Badger2040 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()
|
|
@ -1,3 +0,0 @@
|
|||
SSID = ""
|
||||
PSK = ""
|
||||
COUNTRY = "" # Change to your local two-letter ISO 3166-1 country code
|
Before Width: | Height: | Size: 7.7 KiB |
|
@ -1,7 +0,0 @@
|
|||
mustelid inc
|
||||
H. Badger
|
||||
RP2040
|
||||
2MB Flash
|
||||
E ink
|
||||
296x128px
|
||||
/badges/badge.jpg
|
|
@ -1,172 +0,0 @@
|
|||
import time
|
||||
import badger2040w
|
||||
import badger_os
|
||||
import jpegdec
|
||||
|
||||
# Global Constants
|
||||
WIDTH = badger2040w.WIDTH
|
||||
HEIGHT = badger2040w.HEIGHT
|
||||
|
||||
IMAGE_WIDTH = 104
|
||||
|
||||
COMPANY_HEIGHT = 30
|
||||
DETAILS_HEIGHT = 20
|
||||
NAME_HEIGHT = HEIGHT - COMPANY_HEIGHT - (DETAILS_HEIGHT * 2) - 2
|
||||
TEXT_WIDTH = WIDTH - IMAGE_WIDTH - 1
|
||||
|
||||
COMPANY_TEXT_SIZE = 0.6
|
||||
DETAILS_TEXT_SIZE = 0.5
|
||||
|
||||
LEFT_PADDING = 5
|
||||
NAME_PADDING = 20
|
||||
DETAIL_SPACING = 10
|
||||
|
||||
BADGE_PATH = "/badges/badge.txt"
|
||||
|
||||
DEFAULT_TEXT = """mustelid inc
|
||||
H. Badger
|
||||
RP2040
|
||||
2MB Flash
|
||||
E ink
|
||||
296x128px
|
||||
/badges/badge.jpg
|
||||
"""
|
||||
|
||||
# ------------------------------
|
||||
# Utility functions
|
||||
# ------------------------------
|
||||
|
||||
|
||||
# Reduce the size of a string until it fits within a given width
|
||||
def truncatestring(text, text_size, width):
|
||||
while True:
|
||||
length = display.measure_text(text, text_size)
|
||||
if length > 0 and length > width:
|
||||
text = text[:-1]
|
||||
else:
|
||||
text += ""
|
||||
return text
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Drawing functions
|
||||
# ------------------------------
|
||||
|
||||
# Draw the badge, including user text
|
||||
def draw_badge():
|
||||
display.set_pen(0)
|
||||
display.clear()
|
||||
|
||||
# Draw badge image
|
||||
jpeg.open_file(badge_image)
|
||||
jpeg.decode(WIDTH - IMAGE_WIDTH, 0)
|
||||
|
||||
# Draw a border around the image
|
||||
display.set_pen(0)
|
||||
display.line(WIDTH - IMAGE_WIDTH, 0, WIDTH - 1, 0)
|
||||
display.line(WIDTH - IMAGE_WIDTH, 0, WIDTH - IMAGE_WIDTH, HEIGHT - 1)
|
||||
display.line(WIDTH - IMAGE_WIDTH, HEIGHT - 1, WIDTH - 1, HEIGHT - 1)
|
||||
display.line(WIDTH - 1, 0, WIDTH - 1, HEIGHT - 1)
|
||||
|
||||
# Uncomment this if a white background is wanted behind the company
|
||||
# display.set_pen(15)
|
||||
# display.rectangle(1, 1, TEXT_WIDTH, COMPANY_HEIGHT - 1)
|
||||
|
||||
# Draw the company
|
||||
display.set_pen(15) # Change this to 0 if a white background is used
|
||||
display.set_font("serif")
|
||||
display.text(company, LEFT_PADDING, (COMPANY_HEIGHT // 2) + 1, WIDTH, COMPANY_TEXT_SIZE)
|
||||
|
||||
# Draw a white background behind the name
|
||||
display.set_pen(15)
|
||||
display.rectangle(1, COMPANY_HEIGHT + 1, TEXT_WIDTH, NAME_HEIGHT)
|
||||
|
||||
# Draw the name, scaling it based on the available width
|
||||
display.set_pen(0)
|
||||
display.set_font("sans")
|
||||
name_size = 2.0 # A sensible starting scale
|
||||
while True:
|
||||
name_length = display.measure_text(name, name_size)
|
||||
if name_length >= (TEXT_WIDTH - NAME_PADDING) and name_size >= 0.1:
|
||||
name_size -= 0.01
|
||||
else:
|
||||
display.text(name, (TEXT_WIDTH - name_length) // 2, (NAME_HEIGHT // 2) + COMPANY_HEIGHT + 1, WIDTH, name_size)
|
||||
break
|
||||
|
||||
# Draw a white backgrounds behind the details
|
||||
display.set_pen(15)
|
||||
display.rectangle(1, HEIGHT - DETAILS_HEIGHT * 2, TEXT_WIDTH, DETAILS_HEIGHT - 1)
|
||||
display.rectangle(1, HEIGHT - DETAILS_HEIGHT, TEXT_WIDTH, DETAILS_HEIGHT - 1)
|
||||
|
||||
# Draw the first detail's title and text
|
||||
display.set_pen(0)
|
||||
display.set_font("sans")
|
||||
name_length = display.measure_text(detail1_title, DETAILS_TEXT_SIZE)
|
||||
display.text(detail1_title, LEFT_PADDING, HEIGHT - ((DETAILS_HEIGHT * 3) // 2), WIDTH, DETAILS_TEXT_SIZE)
|
||||
display.text(detail1_text, 5 + name_length + DETAIL_SPACING, HEIGHT - ((DETAILS_HEIGHT * 3) // 2), WIDTH, DETAILS_TEXT_SIZE)
|
||||
|
||||
# Draw the second detail's title and text
|
||||
name_length = display.measure_text(detail2_title, DETAILS_TEXT_SIZE)
|
||||
display.text(detail2_title, LEFT_PADDING, HEIGHT - (DETAILS_HEIGHT // 2), WIDTH, DETAILS_TEXT_SIZE)
|
||||
display.text(detail2_text, LEFT_PADDING + name_length + DETAIL_SPACING, HEIGHT - (DETAILS_HEIGHT // 2), WIDTH, DETAILS_TEXT_SIZE)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Program setup
|
||||
# ------------------------------
|
||||
|
||||
# Create a new Badger and set it to update NORMAL
|
||||
display = badger2040w.Badger2040W()
|
||||
display.led(128)
|
||||
display.set_update_speed(badger2040w.UPDATE_NORMAL)
|
||||
display.set_thickness(2)
|
||||
|
||||
jpeg = jpegdec.JPEG(display.display)
|
||||
|
||||
# Open the badge file
|
||||
try:
|
||||
badge = open(BADGE_PATH, "r")
|
||||
except OSError:
|
||||
with open(BADGE_PATH, "w") as f:
|
||||
f.write(DEFAULT_TEXT)
|
||||
f.flush()
|
||||
badge = open(BADGE_PATH, "r")
|
||||
|
||||
# Read in the next 6 lines
|
||||
company = badge.readline() # "mustelid inc"
|
||||
name = badge.readline() # "H. Badger"
|
||||
detail1_title = badge.readline() # "RP2040"
|
||||
detail1_text = badge.readline() # "2MB Flash"
|
||||
detail2_title = badge.readline() # "E ink"
|
||||
detail2_text = badge.readline() # "296x128px"
|
||||
badge_image = badge.readline() # /badges/badge.jpg
|
||||
|
||||
# Truncate all of the text (except for the name as that is scaled)
|
||||
company = truncatestring(company, COMPANY_TEXT_SIZE, TEXT_WIDTH)
|
||||
|
||||
detail1_title = truncatestring(detail1_title, DETAILS_TEXT_SIZE, TEXT_WIDTH)
|
||||
detail1_text = truncatestring(detail1_text, DETAILS_TEXT_SIZE,
|
||||
TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail1_title, DETAILS_TEXT_SIZE))
|
||||
|
||||
detail2_title = truncatestring(detail2_title, DETAILS_TEXT_SIZE, TEXT_WIDTH)
|
||||
detail2_text = truncatestring(detail2_text, DETAILS_TEXT_SIZE,
|
||||
TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail2_title, DETAILS_TEXT_SIZE))
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Main program
|
||||
# ------------------------------
|
||||
|
||||
draw_badge()
|
||||
|
||||
while True:
|
||||
if display.pressed(badger2040w.BUTTON_A) or display.pressed(badger2040w.BUTTON_B) or display.pressed(badger2040w.BUTTON_C) or display.pressed(badger2040w.BUTTON_UP) or display.pressed(badger2040w.BUTTON_DOWN):
|
||||
badger_os.warning(display, "To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt")
|
||||
time.sleep(4)
|
||||
|
||||
draw_badge()
|
||||
|
||||
display.update()
|
||||
|
||||
# If on battery, halt the Badger to save power, it will wake up if any of the front buttons are pressed
|
||||
display.halt()
|
|
@ -1,94 +0,0 @@
|
|||
import time
|
||||
import machine
|
||||
import ntptime
|
||||
import badger2040w
|
||||
|
||||
|
||||
display = badger2040w.Badger2040W()
|
||||
display.set_update_speed(2)
|
||||
display.set_thickness(4)
|
||||
|
||||
WIDTH, HEIGHT = display.get_bounds()
|
||||
|
||||
try:
|
||||
display.connect()
|
||||
if display.isconnected():
|
||||
ntptime.settime()
|
||||
except (RuntimeError, OSError):
|
||||
pass # no WiFI
|
||||
|
||||
rtc = machine.RTC()
|
||||
|
||||
display.set_font("gothic")
|
||||
|
||||
|
||||
def draw_clock():
|
||||
global second_offset, second_unit_offset
|
||||
|
||||
hms = "{:02}:{:02}:{:02}".format(hour, minute, second)
|
||||
ymd = "{:04}/{:02}/{:02}".format(year, month, day)
|
||||
|
||||
hms_width = display.measure_text(hms, 1.8)
|
||||
hms_offset = int((WIDTH / 2) - (hms_width / 2))
|
||||
|
||||
ymd_width = display.measure_text(ymd, 1.0)
|
||||
ymd_offset = int((WIDTH / 2) - (ymd_width / 2))
|
||||
|
||||
display.set_pen(15)
|
||||
display.clear()
|
||||
display.set_pen(0)
|
||||
|
||||
display.text(hms, hms_offset, 40, 0, 1.8)
|
||||
display.text(ymd, ymd_offset, 100, 0, 1.0)
|
||||
|
||||
display.set_update_speed(2)
|
||||
display.update()
|
||||
display.set_update_speed(3)
|
||||
|
||||
hms = "{:02}:{:02}:".format(hour, minute)
|
||||
second_offset = hms_offset + display.measure_text(hms, 1.8)
|
||||
hms = "{:02}:{:02}:{}".format(hour, minute, second // 10)
|
||||
second_unit_offset = hms_offset + display.measure_text(hms, 1.8)
|
||||
|
||||
|
||||
def draw_second():
|
||||
global second_offset, second_unit_offset
|
||||
|
||||
display.set_pen(15)
|
||||
display.rectangle(second_offset, 8, 75, 56)
|
||||
display.set_pen(0)
|
||||
|
||||
if second // 10 != last_second // 10:
|
||||
s = "{:02}".format(second)
|
||||
display.text(s, second_offset, 40, 0, 1.8)
|
||||
display.partial_update(second_offset, 8, 75, 56)
|
||||
|
||||
s = "{}".format(second // 10)
|
||||
second_unit_offset = second_offset + display.measure_text(s, 1.8)
|
||||
|
||||
else:
|
||||
s = "{}".format(second % 10)
|
||||
display.text(s, second_unit_offset, 40, 0, 1.8)
|
||||
display.partial_update(second_unit_offset, 8, 75 - (second_unit_offset - second_offset), 56)
|
||||
|
||||
|
||||
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
|
||||
last_minute = minute
|
||||
draw_clock()
|
||||
|
||||
|
||||
while True:
|
||||
year, month, day, wd, hour, minute, second, _ = rtc.datetime()
|
||||
if second != last_second:
|
||||
if minute != last_minute:
|
||||
draw_clock()
|
||||
last_minute = minute
|
||||
else:
|
||||
draw_second()
|
||||
last_second = second
|
||||
time.sleep(0.01)
|
|
@ -1,244 +0,0 @@
|
|||
import badger2040w
|
||||
import gc
|
||||
import badger_os
|
||||
|
||||
# **** Put the name of your text file here *****
|
||||
text_file = "/books/289-0-wind-in-the-willows-abridged.txt" # File must be on the MicroPython device
|
||||
|
||||
gc.collect()
|
||||
|
||||
# Global Constants
|
||||
WIDTH = badger2040w.WIDTH
|
||||
HEIGHT = badger2040w.HEIGHT
|
||||
|
||||
ARROW_THICKNESS = 3
|
||||
ARROW_WIDTH = 18
|
||||
ARROW_HEIGHT = 14
|
||||
ARROW_PADDING = 2
|
||||
|
||||
TEXT_PADDING = 4
|
||||
TEXT_WIDTH = WIDTH - TEXT_PADDING - TEXT_PADDING - ARROW_WIDTH
|
||||
|
||||
FONTS = ["sans", "gothic", "cursive", "serif"]
|
||||
THICKNESSES = [2, 1, 1, 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)
|
||||
if state["current_page"] > 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)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Program setup
|
||||
# ------------------------------
|
||||
|
||||
# Global variables
|
||||
state = {
|
||||
"last_offset": 0,
|
||||
"current_page": 0,
|
||||
"font_idx": 0,
|
||||
"text_size": 0.5,
|
||||
"offsets": []
|
||||
}
|
||||
badger_os.state_load("ebook", state)
|
||||
|
||||
text_spacing = int(34 * state["text_size"])
|
||||
|
||||
|
||||
# Create a new Badger and set it to update FAST
|
||||
display = badger2040w.Badger2040W()
|
||||
display.led(128)
|
||||
display.set_update_speed(badger2040w.UPDATE_FAST)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Render page
|
||||
# ------------------------------
|
||||
|
||||
def render_page():
|
||||
row = 0
|
||||
line = ""
|
||||
pos = ebook.tell()
|
||||
next_pos = pos
|
||||
add_newline = False
|
||||
display.set_font(FONTS[state["font_idx"]])
|
||||
display.set_thickness(THICKNESSES[state["font_idx"]])
|
||||
|
||||
while True:
|
||||
# Read a full line and split it into words
|
||||
words = ebook.readline().split(" ")
|
||||
|
||||
# Take the length of the first word and advance our position
|
||||
next_word = words[0]
|
||||
if len(words) > 1:
|
||||
next_pos += len(next_word) + 1
|
||||
else:
|
||||
next_pos += len(next_word) # This is the last word on the line
|
||||
|
||||
# Advance our position further if the word contains special characters
|
||||
if '\u201c' in next_word:
|
||||
next_word = next_word.replace('\u201c', '\"')
|
||||
next_pos += 2
|
||||
if '\u201d' in next_word:
|
||||
next_word = next_word.replace('\u201d', '\"')
|
||||
next_pos += 2
|
||||
if '\u2019' in next_word:
|
||||
next_word = next_word.replace('\u2019', '\'')
|
||||
next_pos += 2
|
||||
|
||||
# Rewind the file back from the line end to the start of the next word
|
||||
ebook.seek(next_pos)
|
||||
|
||||
# Strip out any new line characters from the word
|
||||
next_word = next_word.strip()
|
||||
|
||||
# If an empty word is encountered assume that means there was a blank line
|
||||
if len(next_word) == 0:
|
||||
add_newline = True
|
||||
|
||||
# Append the word to the current line and measure its length
|
||||
appended_line = line
|
||||
if len(line) > 0 and len(next_word) > 0:
|
||||
appended_line += " "
|
||||
appended_line += next_word
|
||||
appended_length = display.measure_text(appended_line, state["text_size"])
|
||||
|
||||
# Would this appended line be longer than the text display area, or was a blank line spotted?
|
||||
if appended_length >= TEXT_WIDTH or add_newline:
|
||||
|
||||
# Yes, so write out the line prior to the append
|
||||
print(line)
|
||||
display.set_pen(0)
|
||||
display.text(line, TEXT_PADDING, (row * text_spacing) + (text_spacing // 2) + TEXT_PADDING, WIDTH, state["text_size"])
|
||||
|
||||
# Clear the line and move on to the next row
|
||||
line = ""
|
||||
row += 1
|
||||
|
||||
# Have we reached the end of the page?
|
||||
if (row * text_spacing) + text_spacing >= HEIGHT:
|
||||
print("+++++")
|
||||
display.update()
|
||||
|
||||
# Reset the position to the start of the word that made this line too long
|
||||
ebook.seek(pos)
|
||||
return
|
||||
else:
|
||||
# Set the line to the word and advance the current position
|
||||
line = next_word
|
||||
pos = next_pos
|
||||
|
||||
# A new line was spotted, so advance a row
|
||||
if add_newline:
|
||||
print("")
|
||||
row += 1
|
||||
if (row * text_spacing) + text_spacing >= HEIGHT:
|
||||
print("+++++")
|
||||
display.update()
|
||||
return
|
||||
add_newline = False
|
||||
else:
|
||||
# The appended line was not too long, so set it as the line and advance the current position
|
||||
line = appended_line
|
||||
pos = next_pos
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Main program loop
|
||||
# ------------------------------
|
||||
|
||||
launch = True
|
||||
changed = False
|
||||
|
||||
# Open the book file
|
||||
ebook = open(text_file, "r")
|
||||
if len(state["offsets"]) > state["current_page"]:
|
||||
ebook.seek(state["offsets"][state["current_page"]])
|
||||
else:
|
||||
state["current_page"] = 0
|
||||
state["offsets"] = []
|
||||
|
||||
while True:
|
||||
# Was the next page button pressed?
|
||||
if display.pressed(badger2040w.BUTTON_DOWN):
|
||||
state["current_page"] += 1
|
||||
|
||||
changed = True
|
||||
|
||||
# Was the previous page button pressed?
|
||||
if display.pressed(badger2040w.BUTTON_UP):
|
||||
if state["current_page"] > 0:
|
||||
state["current_page"] -= 1
|
||||
if state["current_page"] == 0:
|
||||
ebook.seek(0)
|
||||
else:
|
||||
ebook.seek(state["offsets"][state["current_page"] - 1]) # Retrieve the start position of the last page
|
||||
changed = True
|
||||
|
||||
if display.pressed(badger2040w.BUTTON_A):
|
||||
state["text_size"] += 0.1
|
||||
if state["text_size"] > 0.8:
|
||||
state["text_size"] = 0.5
|
||||
text_spacing = int(34 * state["text_size"])
|
||||
state["offsets"] = []
|
||||
ebook.seek(0)
|
||||
state["current_page"] = 0
|
||||
changed = True
|
||||
|
||||
if display.pressed(badger2040w.BUTTON_B):
|
||||
state["font_idx"] += 1
|
||||
if (state["font_idx"] >= len(FONTS)):
|
||||
state["font_idx"] = 0
|
||||
state["offsets"] = []
|
||||
ebook.seek(0)
|
||||
state["current_page"] = 0
|
||||
changed = True
|
||||
|
||||
if launch and not changed:
|
||||
if state["current_page"] > 0 and len(state["offsets"]) > state["current_page"] - 1:
|
||||
ebook.seek(state["offsets"][state["current_page"] - 1])
|
||||
changed = True
|
||||
launch = False
|
||||
|
||||
if changed:
|
||||
draw_frame()
|
||||
render_page()
|
||||
|
||||
# Is the next page one we've not displayed before?
|
||||
if state["current_page"] >= len(state["offsets"]):
|
||||
state["offsets"].append(ebook.tell()) # Add its start position to the state["offsets"] list
|
||||
badger_os.state_save("ebook", state)
|
||||
|
||||
changed = False
|
||||
|
||||
display.halt()
|
|
@ -1,129 +0,0 @@
|
|||
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()
|
|
@ -1,41 +0,0 @@
|
|||
import badger2040w
|
||||
from badger2040w import WIDTH
|
||||
|
||||
TEXT_SIZE = 0.45
|
||||
LINE_HEIGHT = 20
|
||||
|
||||
display = badger2040w.Badger2040W()
|
||||
display.led(128)
|
||||
display.set_thickness(2)
|
||||
|
||||
# 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()
|
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.6 KiB |
|
@ -1,119 +0,0 @@
|
|||
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()
|
|
@ -1,44 +0,0 @@
|
|||
import badger2040w
|
||||
from badger2040w import WIDTH
|
||||
|
||||
TEXT_SIZE = 1
|
||||
LINE_HEIGHT = 15
|
||||
|
||||
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_pen(0)
|
||||
|
||||
y = 16 + int(LINE_HEIGHT / 2)
|
||||
|
||||
display.text("Made by Pimoroni, powered by MicroPython", 5, y, WIDTH, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
display.text("Dual-core RP2040, 133MHz, 264KB RAM", 5, y, WIDTH, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
display.text("2MB Flash (1MB OS, 1MB Storage)", 5, y, WIDTH, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
display.text("296x128 pixel Black/White e-Ink", 5, y, WIDTH, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
y += LINE_HEIGHT
|
||||
|
||||
display.text("For more info:", 5, y, WIDTH, TEXT_SIZE)
|
||||
y += LINE_HEIGHT
|
||||
display.text("https://pimoroni.com/badger2040w", 5, 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()
|
|
@ -1,312 +0,0 @@
|
|||
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")
|
||||
display.set_thickness(2)
|
||||
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()
|
|
@ -1,48 +0,0 @@
|
|||
import badger2040w as badger2040
|
||||
from badger2040w import WIDTH
|
||||
import network
|
||||
|
||||
TEXT_SIZE = 1
|
||||
LINE_HEIGHT = 16
|
||||
|
||||
# Display Setup
|
||||
display = badger2040.Badger2040W()
|
||||
display.led(128)
|
||||
|
||||
# Connects to the wireless network. Ensure you have entered your details in WIFI_CONFIG.py :).
|
||||
display.connect()
|
||||
net = network.WLAN(network.STA_IF).ifconfig()
|
||||
|
||||
# Page Header
|
||||
display.set_pen(15)
|
||||
display.clear()
|
||||
display.set_pen(0)
|
||||
|
||||
display.set_pen(0)
|
||||
display.rectangle(0, 0, WIDTH, 20)
|
||||
display.set_pen(15)
|
||||
display.text("badgerOS", 3, 4)
|
||||
display.text("Network Details", WIDTH - display.measure_text("Network Details") - 4, 4)
|
||||
display.set_pen(0)
|
||||
|
||||
y = 35 + int(LINE_HEIGHT / 2)
|
||||
|
||||
if net:
|
||||
display.text("> LOCAL IP: {}".format(net[0]), 0, y, WIDTH)
|
||||
y += LINE_HEIGHT
|
||||
display.text("> Subnet: {}".format(net[1]), 0, y, WIDTH)
|
||||
y += LINE_HEIGHT
|
||||
display.text("> Gateway: {}".format(net[2]), 0, y, WIDTH)
|
||||
y += LINE_HEIGHT
|
||||
display.text("> DNS: {}".format(net[3]), 0, y, WIDTH)
|
||||
else:
|
||||
display.text("> No network connection!", 0, y, WIDTH)
|
||||
y += LINE_HEIGHT
|
||||
display.text("> Check details in WIFI_CONFIG.py", 0, y, WIDTH)
|
||||
|
||||
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()
|
|
@ -1,215 +0,0 @@
|
|||
import badger2040w as badger2040
|
||||
from badger2040w import WIDTH
|
||||
import machine
|
||||
from urllib import urequest
|
||||
import gc
|
||||
import qrcode
|
||||
import badger_os
|
||||
|
||||
# URLS to use (Entertainment, Science and Technology)
|
||||
URL = ["http://feeds.bbci.co.uk/news/entertainment_and_arts/rss.xml",
|
||||
"http://feeds.bbci.co.uk/news/science_and_environment/rss.xml",
|
||||
"http://feeds.bbci.co.uk/news/technology/rss.xml"]
|
||||
|
||||
code = qrcode.QRCode()
|
||||
|
||||
state = {
|
||||
"current_page": 0,
|
||||
"feed": 2
|
||||
}
|
||||
|
||||
badger_os.state_load("news", state)
|
||||
|
||||
# Display Setup
|
||||
display = badger2040.Badger2040W()
|
||||
display.led(128)
|
||||
display.set_update_speed(2)
|
||||
|
||||
# Setup buttons
|
||||
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
|
||||
|
||||
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 <?xml vers...
|
||||
if next_char == b"?":
|
||||
discard_until(s, b">")
|
||||
continue
|
||||
|
||||
# Detect <![CDATA
|
||||
elif next_char == b"!":
|
||||
s.read(1) # Discard [
|
||||
discard_until(s, b"[") # Discard CDATA[
|
||||
text = read_until(s, b"]")
|
||||
discard_until(s, b">") # 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)
|
||||
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)
|
||||
|
||||
|
||||
# A function to get the data from an RSS Feed, this in case BBC News.
|
||||
def get_rss(url):
|
||||
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
|
||||
|
||||
|
||||
# Connects to the wireless network. Ensure you have entered your details in WIFI_CONFIG.py :).
|
||||
display.connect()
|
||||
|
||||
print(state["feed"])
|
||||
feed = get_rss(URL[state["feed"]])
|
||||
|
||||
|
||||
def draw_page():
|
||||
|
||||
# Clear the display
|
||||
display.set_pen(15)
|
||||
display.clear()
|
||||
display.set_pen(0)
|
||||
|
||||
# Draw the page header
|
||||
display.set_font("bitmap6")
|
||||
display.set_pen(0)
|
||||
display.rectangle(0, 0, WIDTH, 20)
|
||||
display.set_pen(15)
|
||||
display.text("News", 3, 4)
|
||||
display.text("Page: " + str(state["current_page"] + 1), WIDTH - display.measure_text("Page: ") - 4, 4)
|
||||
display.set_pen(0)
|
||||
|
||||
display.set_font("bitmap8")
|
||||
|
||||
# Draw articles from the feed if they're available.
|
||||
if feed:
|
||||
page = state["current_page"]
|
||||
display.set_pen(0)
|
||||
display.text(feed[page]["title"], 2, 30, WIDTH - 130, 2)
|
||||
code.set_text(feed[page]["guid"])
|
||||
draw_qr_code(WIDTH - 100, 25, 100, code)
|
||||
|
||||
else:
|
||||
display.set_pen(0)
|
||||
display.rectangle(0, 60, WIDTH, 25)
|
||||
display.set_pen(15)
|
||||
display.text("Unable to display news! Check your network settings in WIFI_CONFIG.py", 5, 65, WIDTH, 1)
|
||||
|
||||
display.update()
|
||||
|
||||
|
||||
draw_page()
|
||||
|
||||
while 1:
|
||||
|
||||
changed = False
|
||||
|
||||
if button_down.value():
|
||||
if state["current_page"] < 2:
|
||||
state["current_page"] += 1
|
||||
changed = True
|
||||
|
||||
if button_up.value():
|
||||
if state["current_page"] > 0:
|
||||
state["current_page"] -= 1
|
||||
changed = True
|
||||
|
||||
if button_a.value():
|
||||
state["feed"] = 0
|
||||
state["current_page"] = 0
|
||||
feed = get_rss(URL[state["feed"]])
|
||||
badger_os.state_save("news", state)
|
||||
changed = True
|
||||
|
||||
if button_b.value():
|
||||
state["feed"] = 1
|
||||
state["current_page"] = 0
|
||||
feed = get_rss(URL[state["feed"]])
|
||||
badger_os.state_save("news", state)
|
||||
changed = True
|
||||
|
||||
if button_c.value():
|
||||
state["feed"] = 2
|
||||
state["current_page"] = 0
|
||||
feed = get_rss(URL[state["feed"]])
|
||||
badger_os.state_save("news", state)
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
draw_page()
|
|
@ -1,139 +0,0 @@
|
|||
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/badger2040w
|
||||
Badger 2040 W
|
||||
* 296x128 1-bit e-ink
|
||||
* 2.4GHz wireless
|
||||
* five user buttons
|
||||
* user LED
|
||||
* 2MB QSPI flash
|
||||
|
||||
Scan this code to learn
|
||||
more about Badger 2040 W.
|
||||
""")
|
||||
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 Badger 2040 W to a PC, load up Thonny, and add files to /qrcodes directory.")
|
||||
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()
|
|
@ -1,106 +0,0 @@
|
|||
# This example grabs current weather details from Open Meteo and displays them on Badger 2040 W.
|
||||
# Find out more about the Open Meteo API at https://open-meteo.com
|
||||
|
||||
import badger2040w as badger2040
|
||||
from badger2040w import WIDTH
|
||||
import urequests
|
||||
import jpegdec
|
||||
|
||||
# Set your latitude/longitude here (find yours by right clicking in Google Maps!)
|
||||
LAT = 53.38609085276884
|
||||
LNG = -1.4239983439328177
|
||||
TIMEZONE = "auto" # determines time zone from lat/long
|
||||
|
||||
URL = "http://api.open-meteo.com/v1/forecast?latitude=" + str(LAT) + "&longitude=" + str(LNG) + "¤t_weather=true&timezone=" + TIMEZONE
|
||||
|
||||
# Display Setup
|
||||
display = badger2040.Badger2040W()
|
||||
display.led(128)
|
||||
display.set_update_speed(2)
|
||||
|
||||
jpeg = jpegdec.JPEG(display.display)
|
||||
|
||||
# Connects to the wireless network. Ensure you have entered your details in WIFI_CONFIG.py :).
|
||||
display.connect()
|
||||
|
||||
|
||||
def get_data():
|
||||
global weathercode, temperature, windspeed, winddirection, date, time
|
||||
print(f"Requesting URL: {URL}")
|
||||
r = urequests.get(URL)
|
||||
# open the json data
|
||||
j = r.json()
|
||||
print("Data obtained!")
|
||||
print(j)
|
||||
|
||||
# parse relevant data from JSON
|
||||
current = j["current_weather"]
|
||||
temperature = current["temperature"]
|
||||
windspeed = current["windspeed"]
|
||||
winddirection = calculate_bearing(current["winddirection"])
|
||||
weathercode = current["weathercode"]
|
||||
date, time = current["time"].split("T")
|
||||
|
||||
r.close()
|
||||
|
||||
|
||||
def calculate_bearing(d):
|
||||
# calculates a compass direction from the wind direction in degrees
|
||||
dirs = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']
|
||||
ix = round(d / (360. / len(dirs)))
|
||||
return dirs[ix % len(dirs)]
|
||||
|
||||
|
||||
def draw_page():
|
||||
# Clear the display
|
||||
display.set_pen(15)
|
||||
display.clear()
|
||||
display.set_pen(0)
|
||||
|
||||
# Draw the page header
|
||||
display.set_font("bitmap6")
|
||||
display.set_pen(0)
|
||||
display.rectangle(0, 0, WIDTH, 20)
|
||||
display.set_pen(15)
|
||||
display.text("Weather", 3, 4)
|
||||
display.set_pen(0)
|
||||
|
||||
display.set_font("bitmap8")
|
||||
|
||||
if temperature is not None:
|
||||
# Choose an appropriate icon based on the weather code
|
||||
# Weather codes from https://open-meteo.com/en/docs
|
||||
# Weather icons from https://fontawesome.com/
|
||||
if weathercode in [71, 73, 75, 77, 85, 86]: # codes for snow
|
||||
jpeg.open_file("/icons/icon-snow.jpg")
|
||||
elif weathercode in [51, 53, 55, 56, 57, 61, 63, 65, 66, 67, 80, 81, 82]: # codes for rain
|
||||
jpeg.open_file("/icons/icon-rain.jpg")
|
||||
elif weathercode in [1, 2, 3, 45, 48]: # codes for cloud
|
||||
jpeg.open_file("/icons/icon-cloud.jpg")
|
||||
elif weathercode in [0]: # codes for sun
|
||||
jpeg.open_file("/icons/icon-sun.jpg")
|
||||
elif weathercode in [95, 96, 99]: # codes for storm
|
||||
jpeg.open_file("/icons/icon-storm.jpg")
|
||||
jpeg.decode(13, 40, jpegdec.JPEG_SCALE_FULL)
|
||||
display.set_pen(0)
|
||||
display.text(f"Temperature: {temperature}°C", int(WIDTH / 3), 28, WIDTH - 105, 2)
|
||||
display.text(f"Wind Speed: {windspeed}kmph", int(WIDTH / 3), 48, WIDTH - 105, 2)
|
||||
display.text(f"Wind Direction: {winddirection}", int(WIDTH / 3), 68, WIDTH - 105, 2)
|
||||
display.text(f"Last update: {date}, {time}", int(WIDTH / 3), 88, WIDTH - 105, 2)
|
||||
|
||||
else:
|
||||
display.set_pen(0)
|
||||
display.rectangle(0, 60, WIDTH, 25)
|
||||
display.set_pen(15)
|
||||
display.text("Unable to display weather! Check your network settings in WIFI_CONFIG.py", 5, 65, WIDTH, 1)
|
||||
|
||||
display.update()
|
||||
|
||||
|
||||
get_data()
|
||||
draw_page()
|
||||
|
||||
# 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()
|
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.0 KiB |
|
@ -1,3 +0,0 @@
|
|||
Images must be 296x128 pixel JPEGs
|
||||
|
||||
Create a new "images" directory via Thonny, and upload your .jpg files there.
|
Before Width: | Height: | Size: 9.0 KiB |
|
@ -1,183 +0,0 @@
|
|||
import gc
|
||||
import os
|
||||
import time
|
||||
import math
|
||||
import badger2040w as badger2040
|
||||
import badger_os
|
||||
import jpegdec
|
||||
|
||||
APP_DIR = "/examples"
|
||||
FONT_SIZE = 2
|
||||
|
||||
changed = False
|
||||
exited_to_launcher = False
|
||||
woken_by_button = badger2040.woken_by_button() # Must be done before we clear_pressed_to_wake
|
||||
|
||||
if badger2040.pressed_to_wake(badger2040.BUTTON_A) and badger2040.pressed_to_wake(badger2040.BUTTON_C):
|
||||
# Pressing A and C together at start quits app
|
||||
exited_to_launcher = badger_os.state_clear_running()
|
||||
badger2040.reset_pressed_to_wake()
|
||||
else:
|
||||
# Otherwise restore previously running app
|
||||
badger_os.state_launch()
|
||||
|
||||
|
||||
display = badger2040.Badger2040W()
|
||||
display.set_font("bitmap8")
|
||||
display.led(128)
|
||||
|
||||
jpeg = jpegdec.JPEG(display.display)
|
||||
|
||||
state = {
|
||||
"page": 0,
|
||||
"running": "launcher"
|
||||
}
|
||||
|
||||
badger_os.state_load("launcher", state)
|
||||
|
||||
examples = [x[:-3] for x in os.listdir("/examples") if x.endswith(".py")]
|
||||
|
||||
# Approximate center lines for buttons A, B and C
|
||||
centers = (41, 147, 253)
|
||||
|
||||
MAX_PAGE = math.ceil(len(examples) / 3)
|
||||
|
||||
WIDTH = 296
|
||||
|
||||
|
||||
def map_value(input, in_min, in_max, out_min, out_max):
|
||||
return (((input - in_min) * (out_max - out_min)) / (in_max - in_min)) + out_min
|
||||
|
||||
|
||||
def draw_disk_usage(x):
|
||||
_, f_used, _ = badger_os.get_disk_usage()
|
||||
|
||||
display.set_pen(15)
|
||||
display.image(
|
||||
bytearray(
|
||||
(
|
||||
0b00000000,
|
||||
0b00111100,
|
||||
0b00111100,
|
||||
0b00111100,
|
||||
0b00111000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000001,
|
||||
)
|
||||
),
|
||||
8,
|
||||
8,
|
||||
x,
|
||||
4,
|
||||
)
|
||||
display.rectangle(x + 10, 3, 80, 10)
|
||||
display.set_pen(0)
|
||||
display.rectangle(x + 11, 4, 78, 8)
|
||||
display.set_pen(15)
|
||||
display.rectangle(x + 12, 5, int(76 / 100.0 * f_used), 6)
|
||||
display.text("{:.2f}%".format(f_used), x + 91, 4, WIDTH, 1.0)
|
||||
|
||||
|
||||
def render():
|
||||
display.set_pen(15)
|
||||
display.clear()
|
||||
display.set_pen(0)
|
||||
|
||||
max_icons = min(3, len(examples[(state["page"] * 3):]))
|
||||
|
||||
for i in range(max_icons):
|
||||
x = centers[i]
|
||||
label = examples[i + (state["page"] * 3)]
|
||||
icon_label = label.replace("_", "-")
|
||||
icon = f"{APP_DIR}/icon-{icon_label}.jpg"
|
||||
label = label.replace("_", " ")
|
||||
jpeg.open_file(icon)
|
||||
jpeg.decode(x - 26, 30)
|
||||
display.set_pen(0)
|
||||
w = display.measure_text(label, FONT_SIZE)
|
||||
display.text(label, int(x - (w / 2)), 16 + 80, WIDTH, FONT_SIZE)
|
||||
|
||||
for i in range(MAX_PAGE):
|
||||
x = 286
|
||||
y = int((128 / 2) - (MAX_PAGE * 10 / 2) + (i * 10))
|
||||
display.set_pen(0)
|
||||
display.rectangle(x, y, 8, 8)
|
||||
if state["page"] != i:
|
||||
display.set_pen(15)
|
||||
display.rectangle(x + 1, y + 1, 6, 6)
|
||||
|
||||
display.set_pen(0)
|
||||
display.rectangle(0, 0, WIDTH, 16)
|
||||
draw_disk_usage(90)
|
||||
display.set_pen(15)
|
||||
display.text("badgerOS", 4, 4, WIDTH, 1.0)
|
||||
|
||||
display.update()
|
||||
|
||||
|
||||
def wait_for_user_to_release_buttons():
|
||||
while display.pressed_any():
|
||||
time.sleep(0.01)
|
||||
|
||||
|
||||
def launch_example(index):
|
||||
wait_for_user_to_release_buttons()
|
||||
|
||||
file = examples[(state["page"] * 3) + index]
|
||||
file = f"{APP_DIR}/{file}"
|
||||
|
||||
for k in locals().keys():
|
||||
if k not in ("gc", "file", "badger_os"):
|
||||
del locals()[k]
|
||||
|
||||
gc.collect()
|
||||
|
||||
badger_os.launch(file)
|
||||
|
||||
|
||||
def button(pin):
|
||||
global changed
|
||||
changed = True
|
||||
|
||||
if pin == badger2040.BUTTON_A:
|
||||
launch_example(0)
|
||||
if pin == badger2040.BUTTON_B:
|
||||
launch_example(1)
|
||||
if pin == badger2040.BUTTON_C:
|
||||
launch_example(2)
|
||||
if pin == badger2040.BUTTON_UP:
|
||||
if state["page"] > 0:
|
||||
state["page"] -= 1
|
||||
render()
|
||||
if pin == badger2040.BUTTON_DOWN:
|
||||
if state["page"] < MAX_PAGE - 1:
|
||||
state["page"] += 1
|
||||
render()
|
||||
|
||||
|
||||
if exited_to_launcher or not woken_by_button:
|
||||
wait_for_user_to_release_buttons()
|
||||
display.set_update_speed(badger2040.UPDATE_MEDIUM)
|
||||
render()
|
||||
|
||||
display.set_update_speed(badger2040.UPDATE_FAST)
|
||||
|
||||
while True:
|
||||
if display.pressed(badger2040.BUTTON_A):
|
||||
button(badger2040.BUTTON_A)
|
||||
if display.pressed(badger2040.BUTTON_B):
|
||||
button(badger2040.BUTTON_B)
|
||||
if display.pressed(badger2040.BUTTON_C):
|
||||
button(badger2040.BUTTON_C)
|
||||
|
||||
if display.pressed(badger2040.BUTTON_UP):
|
||||
button(badger2040.BUTTON_UP)
|
||||
if display.pressed(badger2040.BUTTON_DOWN):
|
||||
button(badger2040.BUTTON_DOWN)
|
||||
|
||||
if changed:
|
||||
badger_os.state_save("launcher", state)
|
||||
changed = False
|
||||
|
||||
display.halt()
|
|
@ -1,181 +0,0 @@
|
|||
import machine
|
||||
import micropython
|
||||
from picographics import PicoGraphics, DISPLAY_INKY_PACK
|
||||
import network
|
||||
from network_manager import NetworkManager
|
||||
import WIFI_CONFIG
|
||||
import uasyncio
|
||||
import time
|
||||
import gc
|
||||
import wakeup
|
||||
|
||||
|
||||
BUTTON_DOWN = 11
|
||||
BUTTON_A = 12
|
||||
BUTTON_B = 13
|
||||
BUTTON_C = 14
|
||||
BUTTON_UP = 15
|
||||
BUTTON_USER = None # User button not available on W
|
||||
|
||||
BUTTON_MASK = 0b11111 << 11
|
||||
|
||||
SYSTEM_VERY_SLOW = 0
|
||||
SYSTEM_SLOW = 1
|
||||
SYSTEM_NORMAL = 2
|
||||
SYSTEM_FAST = 3
|
||||
SYSTEM_TURBO = 4
|
||||
|
||||
UPDATE_NORMAL = 0
|
||||
UPDATE_MEDIUM = 1
|
||||
UPDATE_FAST = 2
|
||||
UPDATE_TURBO = 3
|
||||
|
||||
LED = 22
|
||||
ENABLE_3V3 = 10
|
||||
BUSY = 26
|
||||
|
||||
WIDTH = 296
|
||||
HEIGHT = 128
|
||||
|
||||
SYSTEM_FREQS = [
|
||||
4000000,
|
||||
12000000,
|
||||
48000000,
|
||||
133000000,
|
||||
250000000
|
||||
]
|
||||
|
||||
BUTTONS = {
|
||||
BUTTON_DOWN: machine.Pin(BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN),
|
||||
BUTTON_A: machine.Pin(BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN),
|
||||
BUTTON_B: machine.Pin(BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN),
|
||||
BUTTON_C: machine.Pin(BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN),
|
||||
BUTTON_UP: machine.Pin(BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN),
|
||||
}
|
||||
|
||||
WAKEUP_MASK = 0
|
||||
|
||||
|
||||
def woken_by_button():
|
||||
return wakeup.get_gpio_state() & BUTTON_MASK > 0
|
||||
|
||||
|
||||
def pressed_to_wake(button):
|
||||
return wakeup.get_gpio_state() & (1 << button) > 0
|
||||
|
||||
|
||||
def reset_pressed_to_wake():
|
||||
wakeup.reset_gpio_state()
|
||||
|
||||
|
||||
def pressed_to_wake_get_once(button):
|
||||
global WAKEUP_MASK
|
||||
result = (wakeup.get_gpio_state() & ~WAKEUP_MASK & (1 << button)) > 0
|
||||
WAKEUP_MASK |= (1 << button)
|
||||
return result
|
||||
|
||||
|
||||
def system_speed(speed):
|
||||
try:
|
||||
machine.freq(SYSTEM_FREQS[speed])
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
|
||||
class Badger2040W():
|
||||
def __init__(self):
|
||||
self.display = PicoGraphics(DISPLAY_INKY_PACK)
|
||||
self._led = machine.PWM(machine.Pin(LED))
|
||||
self._led.freq(1000)
|
||||
self._led.duty_u16(0)
|
||||
self._update_speed = 0
|
||||
|
||||
def __getattr__(self, item):
|
||||
# Glue to redirect calls to PicoGraphics
|
||||
return getattr(self.display, item)
|
||||
|
||||
def update(self):
|
||||
t_start = time.ticks_ms()
|
||||
self.display.update()
|
||||
t_elapsed = time.ticks_ms() - t_start
|
||||
|
||||
delay_ms = [4700, 2600, 900, 250][self._update_speed]
|
||||
|
||||
if t_elapsed < delay_ms:
|
||||
time.sleep((delay_ms - t_elapsed) / 1000)
|
||||
|
||||
def set_update_speed(self, speed):
|
||||
self.display.set_update_speed(speed)
|
||||
self._update_speed = speed
|
||||
|
||||
def led(self, brightness):
|
||||
brightness = max(0, min(255, brightness))
|
||||
self._led.duty_u16(int(brightness * 256))
|
||||
|
||||
def invert(self, invert):
|
||||
raise RuntimeError("Display invert not supported in PicoGraphics.")
|
||||
|
||||
def thickness(self, thickness):
|
||||
raise RuntimeError("Thickness not supported in PicoGraphics.")
|
||||
|
||||
def halt(self):
|
||||
time.sleep(0.05)
|
||||
enable = machine.Pin(ENABLE_3V3, machine.Pin.OUT)
|
||||
enable.off()
|
||||
while not self.pressed_any():
|
||||
pass
|
||||
|
||||
def pressed(self, button):
|
||||
return BUTTONS[button].value() == 1 or pressed_to_wake_get_once(button)
|
||||
|
||||
def pressed_any(self):
|
||||
for button in BUTTONS.values():
|
||||
if button.value():
|
||||
return True
|
||||
return False
|
||||
|
||||
@micropython.native
|
||||
def icon(self, data, index, data_w, icon_size, x, y):
|
||||
s_x = (index * icon_size) % data_w
|
||||
s_y = int((index * icon_size) / data_w)
|
||||
|
||||
for o_y in range(icon_size):
|
||||
for o_x in range(icon_size):
|
||||
o = ((o_y + s_y) * data_w) + (o_x + s_x)
|
||||
bm = 0b10000000 >> (o & 0b111)
|
||||
if data[o >> 3] & bm:
|
||||
self.display.pixel(x + o_x, y + o_y)
|
||||
|
||||
def image(self, data, w, h, x, y):
|
||||
for oy in range(h):
|
||||
row = data[oy]
|
||||
for ox in range(w):
|
||||
if row & 0b1 == 0:
|
||||
self.display.pixel(x + ox, y + oy)
|
||||
row >>= 1
|
||||
|
||||
def status_handler(self, mode, status, ip):
|
||||
print(mode, status, ip)
|
||||
self.display.set_pen(15)
|
||||
self.display.clear()
|
||||
self.display.set_pen(0)
|
||||
if status:
|
||||
self.display.text("Connected!", 10, 10, 300, 0.5)
|
||||
self.display.text(ip, 10, 30, 300, 0.5)
|
||||
else:
|
||||
self.display.text("Connecting...", 10, 10, 300, 0.5)
|
||||
self.update()
|
||||
|
||||
def isconnected(self):
|
||||
return network.WLAN(network.STA_IF).isconnected()
|
||||
|
||||
def ip_address(self):
|
||||
return network.WLAN(network.STA_IF).ifconfig()[0]
|
||||
|
||||
def connect(self):
|
||||
if WIFI_CONFIG.COUNTRY == "":
|
||||
raise RuntimeError("You must populate WIFI_CONFIG.py for networking.")
|
||||
self.display.set_update_speed(2)
|
||||
network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=self.status_handler)
|
||||
uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK))
|
||||
gc.collect()
|
|
@ -1,188 +0,0 @@
|
|||
"""Keep track of app state in persistent flash storage."""
|
||||
|
||||
import os
|
||||
import gc
|
||||
import time
|
||||
import json
|
||||
import machine
|
||||
import badger2040w as badger2040
|
||||
|
||||
|
||||
def get_battery_level():
|
||||
return 0
|
||||
# Battery measurement
|
||||
vbat_adc = machine.ADC(badger2040.PIN_BATTERY)
|
||||
vref_adc = machine.ADC(badger2040.PIN_1V2_REF)
|
||||
vref_en = machine.Pin(badger2040.PIN_VREF_POWER)
|
||||
vref_en.init(machine.Pin.OUT)
|
||||
vref_en.value(0)
|
||||
|
||||
# Enable the onboard voltage reference
|
||||
vref_en.value(1)
|
||||
|
||||
# Calculate the logic supply voltage, as will be lower that the usual 3.3V when running off low batteries
|
||||
vdd = 1.24 * (65535 / vref_adc.read_u16())
|
||||
vbat = (
|
||||
(vbat_adc.read_u16() / 65535) * 3 * vdd
|
||||
) # 3 in this is a gain, not rounding of 3.3V
|
||||
|
||||
# Disable the onboard voltage reference
|
||||
vref_en.value(0)
|
||||
|
||||
# Convert the voltage to a level to display onscreen
|
||||
return vbat
|
||||
|
||||
|
||||
def get_disk_usage():
|
||||
# f_bfree and f_bavail should be the same?
|
||||
# f_files, f_ffree, f_favail and f_flag are unsupported.
|
||||
f_bsize, f_frsize, f_blocks, f_bfree, _, _, _, _, _, f_namemax = os.statvfs("/")
|
||||
|
||||
f_total_size = f_frsize * f_blocks
|
||||
f_total_free = f_bsize * f_bfree
|
||||
f_total_used = f_total_size - f_total_free
|
||||
|
||||
f_used = 100 / f_total_size * f_total_used
|
||||
f_free = 100 / f_total_size * f_total_free
|
||||
|
||||
return f_total_size, f_used, f_free
|
||||
|
||||
|
||||
def state_running():
|
||||
state = {"running": "launcher"}
|
||||
state_load("launcher", state)
|
||||
return state["running"]
|
||||
|
||||
|
||||
def state_clear_running():
|
||||
running = state_running()
|
||||
state_modify("launcher", {"running": "launcher"})
|
||||
return running != "launcher"
|
||||
|
||||
|
||||
def state_set_running(app):
|
||||
state_modify("launcher", {"running": app})
|
||||
|
||||
|
||||
def state_launch():
|
||||
app = state_running()
|
||||
if app is not None and app != "launcher":
|
||||
launch(app)
|
||||
|
||||
|
||||
def state_delete(app):
|
||||
try:
|
||||
os.remove("/state/{}.json".format(app))
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def state_save(app, data):
|
||||
try:
|
||||
with open("/state/{}.json".format(app), "w") as f:
|
||||
f.write(json.dumps(data))
|
||||
f.flush()
|
||||
except OSError:
|
||||
import os
|
||||
try:
|
||||
os.stat("/state")
|
||||
except OSError:
|
||||
os.mkdir("/state")
|
||||
state_save(app, data)
|
||||
|
||||
|
||||
def state_modify(app, data):
|
||||
state = {}
|
||||
state_load(app, state)
|
||||
state.update(data)
|
||||
state_save(app, state)
|
||||
|
||||
|
||||
def state_load(app, defaults):
|
||||
try:
|
||||
data = json.loads(open("/state/{}.json".format(app), "r").read())
|
||||
if type(data) is dict:
|
||||
defaults.update(data)
|
||||
return True
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
|
||||
state_save(app, defaults)
|
||||
return False
|
||||
|
||||
|
||||
def launch(file):
|
||||
state_set_running(file)
|
||||
|
||||
gc.collect()
|
||||
|
||||
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
||||
|
||||
def quit_to_launcher(pin):
|
||||
if button_a.value() and button_c.value():
|
||||
machine.reset()
|
||||
|
||||
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=quit_to_launcher)
|
||||
button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=quit_to_launcher)
|
||||
|
||||
try:
|
||||
__import__(file)
|
||||
|
||||
except ImportError:
|
||||
# If the app doesn't exist, notify the user
|
||||
warning(None, f"Could not launch: {file}")
|
||||
time.sleep(4.0)
|
||||
except Exception as e:
|
||||
# If the app throws an error, catch it and display!
|
||||
print(e)
|
||||
warning(None, str(e))
|
||||
time.sleep(4.0)
|
||||
|
||||
# If the app exits or errors, do not relaunch!
|
||||
state_clear_running()
|
||||
machine.reset() # Exit back to launcher
|
||||
|
||||
|
||||
# Draw an overlay box with a given message within it
|
||||
def warning(display, message, width=badger2040.WIDTH - 20, height=badger2040.HEIGHT - 20, line_spacing=20, text_size=0.6):
|
||||
print(message)
|
||||
|
||||
if display is None:
|
||||
display = badger2040.Badger2040W()
|
||||
display.led(128)
|
||||
|
||||
# Draw a light grey background
|
||||
display.set_pen(12)
|
||||
display.rectangle((badger2040.WIDTH - width) // 2, (badger2040.HEIGHT - height) // 2, width, height)
|
||||
|
||||
width -= 20
|
||||
height -= 20
|
||||
|
||||
display.set_pen(15)
|
||||
display.rectangle((badger2040.WIDTH - width) // 2, (badger2040.HEIGHT - height) // 2, width, height)
|
||||
|
||||
# Take the provided message and split it up into
|
||||
# lines that fit within the specified width
|
||||
words = message.split(" ")
|
||||
|
||||
lines = []
|
||||
current_line = ""
|
||||
for word in words:
|
||||
if display.measure_text(current_line + word + " ", text_size) < width:
|
||||
current_line += word + " "
|
||||
else:
|
||||
lines.append(current_line.strip())
|
||||
current_line = word + " "
|
||||
lines.append(current_line.strip())
|
||||
|
||||
display.set_pen(0)
|
||||
|
||||
# Display each line of text from the message, centre-aligned
|
||||
num_lines = len(lines)
|
||||
for i in range(num_lines):
|
||||
length = display.measure_text(lines[i], text_size)
|
||||
current_line = (i * line_spacing) - ((num_lines - 1) * line_spacing) // 2
|
||||
display.text(lines[i], (badger2040.WIDTH - length) // 2, (badger2040.HEIGHT // 2) + current_line, badger2040.WIDTH, text_size)
|
||||
|
||||
display.update()
|
|
@ -1,108 +0,0 @@
|
|||
import rp2
|
||||
import network
|
||||
import machine
|
||||
import uasyncio
|
||||
|
||||
|
||||
class NetworkManager:
|
||||
_ifname = ("Client", "Access Point")
|
||||
|
||||
def __init__(self, country="GB", client_timeout=60, access_point_timeout=5, status_handler=None, error_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._error_handler = error_handler
|
||||
self.UID = ("{:02X}" * 8).format(*machine.unique_id())
|
||||
|
||||
def isconnected(self):
|
||||
return self._sta_if.isconnected() or self._ap_if.isconnected()
|
||||
|
||||
def config(self, var):
|
||||
if self._sta_if.active():
|
||||
return self._sta_if.config(var)
|
||||
else:
|
||||
if var == "password":
|
||||
return self.UID
|
||||
return self._ap_if.config(var)
|
||||
|
||||
def mode(self):
|
||||
if self._sta_if.isconnected():
|
||||
return self._ifname[0]
|
||||
if self._ap_if.isconnected():
|
||||
return self._ifname[1]
|
||||
return None
|
||||
|
||||
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 callable(self._status_handler):
|
||||
self._status_handler(self._ifname[mode], status, self.ifaddress())
|
||||
|
||||
def _handle_error(self, mode, msg):
|
||||
if callable(self._error_handler):
|
||||
if self._error_handler(self._ifname[mode], msg):
|
||||
return
|
||||
raise RuntimeError(msg)
|
||||
|
||||
async def client(self, ssid, psk):
|
||||
if self._sta_if.isconnected():
|
||||
self._handle_status(network.STA_IF, True)
|
||||
return
|
||||
|
||||
self._ap_if.disconnect()
|
||||
self._ap_if.active(False)
|
||||
|
||||
self._sta_if.active(True)
|
||||
self._sta_if.config(pm=0xa11140)
|
||||
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)
|
||||
self._handle_error(network.STA_IF, "WIFI Client Failed")
|
||||
|
||||
async def access_point(self):
|
||||
if self._ap_if.isconnected():
|
||||
self._handle_status(network.AP_IF, True)
|
||||
return
|
||||
|
||||
self._sta_if.disconnect()
|
||||
self._sta_if.active(False)
|
||||
|
||||
self._ap_if.ifconfig(("10.10.1.1", "255.255.255.0", "10.10.1.1", "10.10.1.1"))
|
||||
self._ap_if.config(password=self.UID)
|
||||
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)
|
||||
self._handle_error(network.AP_IF, "WIFI Client Failed")
|
|
@ -1 +0,0 @@
|
|||
import launcher # noqa F401
|
|
@ -1,113 +0,0 @@
|
|||
# Badger 2040 W MicroPython Examples <!-- omit in toc -->
|
||||
|
||||
:warning: This code has been deprecated in favour of a dedicated Badger 2040 project: https://github.com/pimoroni/badger2040
|
||||
|
||||
- [About Badger 2040 W](#about-badger-2040-w)
|
||||
- [Badger 2040 W and PicoGraphics](#badger-2040-w-and-picographics)
|
||||
- [Examples](#examples)
|
||||
- [Badge](#badge)
|
||||
- [Clock](#clock)
|
||||
- [Ebook](#ebook)
|
||||
- [Fonts](#fonts)
|
||||
- [Help](#help)
|
||||
- [Image](#image)
|
||||
- [Info](#info)
|
||||
- [List](#list)
|
||||
- [Net Info](#net-info)
|
||||
- [News](#news)
|
||||
- [Qrgen](#qrgen)
|
||||
- [Weather](#weather)
|
||||
- [Other Resources](#other-resources)
|
||||
|
||||
|
||||
## About Badger 2040 W
|
||||
|
||||
Badger 2040 W is a programmable E Paper/eInk/EPD badge with 2.4GHz wireless connectivity, powered by Raspberry Pi Pico W. It can go into a deep sleep mode between updates to preserve battery.
|
||||
|
||||
- :link: [Badger 2040 W store page](https://shop.pimoroni.com/products/badger-2040-w)
|
||||
|
||||
Badger 2040 W ships with MicroPython firmware pre-loaded, but you can download the most recent version at the link below (you'll want the `pimoroni-badger2040w` .uf2). If you download the `-with-examples` file, it will come with examples built in.
|
||||
|
||||
- [MicroPython releases](https://github.com/pimoroni/pimoroni-pico/releases)
|
||||
- [Installing MicroPython](../../../setting-up-micropython.md)
|
||||
|
||||
## Badger 2040 W and PicoGraphics
|
||||
|
||||
The easiest way to start displaying cool stuff on Badger is by using our `badger2040w` module (which contains helpful functions for interacting with the board hardware) and our PicoGraphics library (which contains a bunch of functions for drawing on the E Ink display).
|
||||
|
||||
- [Badger 2040 W function reference](../../modules/badger2040w/README.md)
|
||||
- [PicoGraphics function reference](../../modules/picographics/README.md)
|
||||
|
||||
## Examples
|
||||
|
||||
Find out more about how to use these examples in our Learn guide:
|
||||
|
||||
- [Getting Started with Badger 2040 W](https://learn.pimoroni.com/article/getting-started-with-badger-2040-w)
|
||||
|
||||
### Badge
|
||||
[badge.py](examples/badge.py)
|
||||
|
||||
Customisable name badge example.
|
||||
|
||||
### Clock
|
||||
[clock.py](examples/clock.py)
|
||||
|
||||
Clock example with (optional) NTP synchronization and partial screen updates.
|
||||
|
||||
### Ebook
|
||||
[ebook.py](examples/ebook.py)
|
||||
|
||||
View text files on Badger.
|
||||
|
||||
### Fonts
|
||||
[fonts.py](examples/fonts.py)
|
||||
|
||||
View all the built in fonts.
|
||||
|
||||
### Help
|
||||
[help.py](examples/help.py)
|
||||
|
||||
How to navigate the launcher.
|
||||
|
||||
### Image
|
||||
[image.py](examples/image.py)
|
||||
|
||||
Display .jpegs on Badger.
|
||||
|
||||
### Info
|
||||
[info.py](examples/info.py)
|
||||
|
||||
Info about Badger 2040 W.
|
||||
|
||||
### List
|
||||
[list.py](examples/list.py)
|
||||
|
||||
A checklist to keep track of to-dos or shopping.
|
||||
|
||||
### Net Info
|
||||
[net_info.py](examples/net_info.py)
|
||||
|
||||
Show IP address and other wireless connection details.
|
||||
|
||||
### News
|
||||
[news.py](examples/news.py)
|
||||
|
||||
View BBC news headlines.
|
||||
|
||||
### Qrgen
|
||||
[qrgen.py](examples/qrgen.py)
|
||||
|
||||
Display QR codes and associated text.
|
||||
|
||||
### Weather
|
||||
[weather.py](examples/weather.py)
|
||||
|
||||
Display current weather data from the [Open-Meteo weather API](https://open-meteo.com/)
|
||||
|
||||
## Other Resources
|
||||
|
||||
Here are some cool Badger 2040 W community projects and resources that you might find useful / inspirational! Note that code at the links below has not been tested by us and we're not able to offer support with it.
|
||||
|
||||
- :link: [Send messages to Badger via webform](https://github.com/techcree/Badger2040W/tree/main/webform)
|
||||
- :link: [3D printed Badger 2040 W enclosure](https://kaenner.de/badger2040w)
|
||||
- :link: [Badger Pixel Client for a Raspberry Pi Pixel Server](https://github.com/penguintutor/badger-pixel-client)
|
|
@ -1,10 +0,0 @@
|
|||
*.py
|
||||
lib/*.py
|
||||
examples/*.jpg
|
||||
examples/*.py
|
||||
images/*.jpg
|
||||
images/*.txt
|
||||
badges/*.txt
|
||||
badges/*.jpg
|
||||
books/*.txt
|
||||
icons/*.jpg
|