Merge pull request #313 from MichaelBell/battery-improvements

Badger2040 Micropython battery improvements
This commit is contained in:
Philip Howard 2022-03-25 16:58:33 +00:00 committed by GitHub
commit 522c83dc19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 625 additions and 460 deletions

View File

@ -323,6 +323,8 @@ namespace pimoroni {
void UC8151::setup(uint8_t speed) { void UC8151::setup(uint8_t speed) {
reset(); reset();
_update_speed = speed;
if(speed == 0) { if(speed == 0) {
command(PSR, { command(PSR, {
RES_128x296 | LUT_OTP | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE RES_128x296 | LUT_OTP | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE
@ -468,6 +470,25 @@ namespace pimoroni {
setup(speed); setup(speed);
} }
uint8_t UC8151::update_speed() {
return _update_speed;
}
uint32_t UC8151::update_time() {
switch(_update_speed) {
case 0:
return 5500;
case 1:
return 2600;
case 2:
return 1000;
case 3:
return 300;
default:
return 5500;
}
}
void UC8151::partial_update(int x, int y, int w, int h, bool blocking) { void UC8151::partial_update(int x, int y, int w, int h, bool blocking) {
// y is given in columns ("banks"), which are groups of 8 horiontal pixels // y is given in columns ("banks"), which are groups of 8 horiontal pixels
// x is given in pixels // x is given in pixels

View File

@ -146,6 +146,8 @@ namespace pimoroni {
bool inverted = false; bool inverted = false;
uint8_t _update_speed = 0;
public: public:
UC8151(uint16_t width, uint16_t height) : UC8151(uint16_t width, uint16_t height) :
width(width), height(height), frame_buffer(new uint8_t[width * height / 8]) { width(width), height(height), frame_buffer(new uint8_t[width * height / 8]) {
@ -199,6 +201,8 @@ namespace pimoroni {
void invert(bool invert); void invert(bool invert);
void update_speed(uint8_t speed); void update_speed(uint8_t speed);
uint8_t update_speed();
uint32_t update_time();
void update(bool blocking = true); void update(bool blocking = true);
void partial_update(int x, int y, int w, int h, bool blocking = true); void partial_update(int x, int y, int w, int h, bool blocking = true);
void off(); void off();

View File

@ -40,7 +40,7 @@ namespace pimoroni {
gpio_set_function(USER, GPIO_FUNC_SIO); gpio_set_function(USER, GPIO_FUNC_SIO);
gpio_set_dir(USER, GPIO_IN); gpio_set_dir(USER, GPIO_IN);
gpio_set_pulls(USER, false, true); gpio_set_pulls(USER, true, false);
gpio_set_function(VBUS_DETECT, GPIO_FUNC_SIO); gpio_set_function(VBUS_DETECT, GPIO_FUNC_SIO);
gpio_set_dir(VBUS_DETECT, GPIO_IN); gpio_set_dir(VBUS_DETECT, GPIO_IN);
@ -195,8 +195,9 @@ namespace pimoroni {
} }
void Badger2040::update_button_states() { void Badger2040::update_button_states() {
uint32_t mask = (1UL << A) | (1UL << B) | (1UL << C) | (1UL << D) | (1UL << E); uint32_t mask = (1UL << A) | (1UL << B) | (1UL << C) | (1UL << D) | (1UL << E) | (1UL << USER);
_button_states = gpio_get_all() & mask; _button_states = gpio_get_all() & mask;
_button_states ^= (1UL << USER); // USER button state is inverted
} }
uint32_t Badger2040::button_states() { uint32_t Badger2040::button_states() {
@ -219,6 +220,10 @@ namespace pimoroni {
uc8151.update_speed(speed); uc8151.update_speed(speed);
} }
uint32_t Badger2040::update_time() {
return uc8151.update_time();
}
void Badger2040::partial_update(int x, int y, int w, int h, bool blocking) { void Badger2040::partial_update(int x, int y, int w, int h, bool blocking) {
uc8151.partial_update(x, y, w, h, blocking); uc8151.partial_update(x, y, w, h, blocking);
} }

View File

@ -30,6 +30,7 @@ namespace pimoroni {
void update(bool blocking=false); void update(bool blocking=false);
void partial_update(int x, int y, int w, int h, bool blocking=false); void partial_update(int x, int y, int w, int h, bool blocking=false);
void update_speed(uint8_t speed); void update_speed(uint8_t speed);
uint32_t update_time();
void halt(); void halt();
void sleep(); void sleep();
bool is_busy(); bool is_busy();

View File

@ -1,6 +1,6 @@
import badger2040
import machine
import time import time
import badger2040
import badger_os
# Global Constants # Global Constants
WIDTH = badger2040.WIDTH WIDTH = badger2040.WIDTH
@ -20,10 +20,6 @@ LEFT_PADDING = 5
NAME_PADDING = 20 NAME_PADDING = 20
DETAIL_SPACING = 10 DETAIL_SPACING = 10
OVERLAY_BORDER = 40
OVERLAY_SPACING = 20
OVERLAY_TEXT_SIZE = 0.6
DEFAULT_TEXT = """mustelid inc DEFAULT_TEXT = """mustelid inc
H. Badger H. Badger
RP2040 RP2040
@ -63,42 +59,6 @@ def truncatestring(text, text_size, width):
# Drawing functions # Drawing functions
# ------------------------------ # ------------------------------
# Draw an overlay box with a given message within it
def draw_overlay(message, width, height, line_spacing, text_size):
# Draw a light grey background
display.pen(12)
display.rectangle((WIDTH - width) // 2, (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 = []
line = ""
appended_line = ""
for word in words:
if len(word) > 0:
appended_line += " "
appended_line += word
if display.measure_text(appended_line, text_size) >= width:
lines.append(line)
appended_line = word
else:
line = appended_line
if len(line) != 0:
lines.append(line)
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], (WIDTH - length) // 2, (HEIGHT // 2) + current_line, text_size)
# Draw the badge, including user text # Draw the badge, including user text
def draw_badge(): def draw_badge():
display.pen(0) display.pen(0)
@ -170,21 +130,19 @@ def draw_badge():
# Program setup # Program setup
# ------------------------------ # ------------------------------
# Global variables
show_overlay = False
# Create a new Badger and set it to update NORMAL # Create a new Badger and set it to update NORMAL
display = badger2040.Badger2040() display = badger2040.Badger2040()
display.led(128)
display.update_speed(badger2040.UPDATE_NORMAL) display.update_speed(badger2040.UPDATE_NORMAL)
# Open the badge file # Open the badge file
try: try:
badge = open("badge.txt", "r") badge = open("badge.txt", "r")
except OSError: except OSError:
badge = open("badge.txt", "w") with open("badge.txt", "w") as f:
badge.write(DEFAULT_TEXT) f.write(DEFAULT_TEXT)
badge.flush() f.flush()
badge.seek(0) badge = open("badge.txt", "r")
# Read in the next 6 lines # Read in the next 6 lines
company = badge.readline() # "mustelid inc" company = badge.readline() # "mustelid inc"
@ -205,63 +163,21 @@ detail2_title = truncatestring(detail2_title, DETAILS_TEXT_SIZE, TEXT_WIDTH)
detail2_text = truncatestring(detail2_text, DETAILS_TEXT_SIZE, detail2_text = truncatestring(detail2_text, DETAILS_TEXT_SIZE,
TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail2_title, DETAILS_TEXT_SIZE)) TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail2_title, DETAILS_TEXT_SIZE))
# Set up the 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_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)
# Button handling function
def button(pin):
global show_overlay
if pin == button_a:
show_overlay = True
return
if pin == button_b:
show_overlay = True
return
if pin == button_c:
show_overlay = True
return
if pin == button_up:
show_overlay = True
return
if pin == button_down:
show_overlay = True
return
# Register the button handling function with the buttons
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)
# ------------------------------ # ------------------------------
# Main program loop # Main program
# ------------------------------ # ------------------------------
draw_badge() draw_badge()
display.update()
while True: while True:
if show_overlay: 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):
draw_overlay("To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt", badger_os.warning(display, "To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt")
WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE)
display.update()
time.sleep(4) time.sleep(4)
draw_badge() draw_badge()
display.update()
show_overlay = False
time.sleep(0.1) 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()

View File

@ -0,0 +1,183 @@
"""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()

View File

@ -4,9 +4,10 @@ import badger2040
rtc = machine.RTC() rtc = machine.RTC()
screen = badger2040.Badger2040() display = badger2040.Badger2040()
screen.update_speed(badger2040.UPDATE_TURBO) display.led(128)
screen.font("gothic") display.update_speed(badger2040.UPDATE_TURBO)
display.font("gothic")
cursors = ["year", "month", "day", "hour", "minute"] cursors = ["year", "month", "day", "hour", "minute"]
set_clock = False set_clock = False
@ -32,10 +33,13 @@ def days_in_month(month, year):
def button(pin): def button(pin):
global last, set_clock, cursor, year, month, day, hour, minute global last, set_clock, cursor, year, month, day, hour, minute
time.sleep(0.05) time.sleep(0.01)
if not pin.value(): if not pin.value():
return return
if button_a.value() and button_c.value():
machine.reset()
adjust = 0 adjust = 0
changed = False changed = False
@ -94,42 +98,42 @@ def draw_clock():
hms = "{:02}:{:02}:{:02}".format(hour, minute, second) hms = "{:02}:{:02}:{:02}".format(hour, minute, second)
ymd = "{:04}/{:02}/{:02}".format(year, month, day) ymd = "{:04}/{:02}/{:02}".format(year, month, day)
hms_width = screen.measure_text(hms, 1.8) hms_width = display.measure_text(hms, 1.8)
hms_offset = int((badger2040.WIDTH / 2) - (hms_width / 2)) hms_offset = int((badger2040.WIDTH / 2) - (hms_width / 2))
h_width = screen.measure_text(hms[0:2], 1.8) h_width = display.measure_text(hms[0:2], 1.8)
mi_width = screen.measure_text(hms[3:5], 1.8) mi_width = display.measure_text(hms[3:5], 1.8)
mi_offset = screen.measure_text(hms[0:3], 1.8) mi_offset = display.measure_text(hms[0:3], 1.8)
ymd_width = screen.measure_text(ymd, 1.0) ymd_width = display.measure_text(ymd, 1.0)
ymd_offset = int((badger2040.WIDTH / 2) - (ymd_width / 2)) ymd_offset = int((badger2040.WIDTH / 2) - (ymd_width / 2))
y_width = screen.measure_text(ymd[0:4], 1.0) y_width = display.measure_text(ymd[0:4], 1.0)
m_width = screen.measure_text(ymd[5:7], 1.0) m_width = display.measure_text(ymd[5:7], 1.0)
m_offset = screen.measure_text(ymd[0:5], 1.0) m_offset = display.measure_text(ymd[0:5], 1.0)
d_width = screen.measure_text(ymd[8:10], 1.0) d_width = display.measure_text(ymd[8:10], 1.0)
d_offset = screen.measure_text(ymd[0:8], 1.0) d_offset = display.measure_text(ymd[0:8], 1.0)
screen.pen(15) display.pen(15)
screen.clear() display.clear()
screen.pen(0) display.pen(0)
screen.thickness(5) display.thickness(5)
screen.text(hms, hms_offset, 40, 1.8) display.text(hms, hms_offset, 40, 1.8)
screen.thickness(3) display.thickness(3)
screen.text(ymd, ymd_offset, 100, 1.0) display.text(ymd, ymd_offset, 100, 1.0)
if set_clock: if set_clock:
if cursors[cursor] == "year": if cursors[cursor] == "year":
screen.line(ymd_offset, 120, ymd_offset + y_width, 120) display.line(ymd_offset, 120, ymd_offset + y_width, 120)
if cursors[cursor] == "month": if cursors[cursor] == "month":
screen.line(ymd_offset + m_offset, 120, ymd_offset + m_offset + m_width, 120) display.line(ymd_offset + m_offset, 120, ymd_offset + m_offset + m_width, 120)
if cursors[cursor] == "day": if cursors[cursor] == "day":
screen.line(ymd_offset + d_offset, 120, ymd_offset + d_offset + d_width, 120) display.line(ymd_offset + d_offset, 120, ymd_offset + d_offset + d_width, 120)
if cursors[cursor] == "hour": if cursors[cursor] == "hour":
screen.line(hms_offset, 70, hms_offset + h_width, 70) display.line(hms_offset, 70, hms_offset + h_width, 70)
if cursors[cursor] == "minute": if cursors[cursor] == "minute":
screen.line(hms_offset + mi_offset, 70, hms_offset + mi_offset + mi_width, 70) display.line(hms_offset + mi_offset, 70, hms_offset + mi_offset + mi_width, 70)
screen.update() display.update()
year, month, day, wd, hour, minute, second, _ = rtc.datetime() year, month, day, wd, hour, minute, second, _ = rtc.datetime()
@ -145,4 +149,4 @@ while True:
if second != last_second: if second != last_second:
draw_clock() draw_clock()
last_second = second last_second = second
time.sleep(0.1) time.sleep(0.01)

View File

@ -1,8 +1,7 @@
import badger2040 import badger2040
import machine
import time import time
import gc import gc
import badger_os
# **** Put the name of your text file here ***** # **** Put the name of your text file here *****
text_file = "book.txt" # File must be on the MicroPython device text_file = "book.txt" # File must be on the MicroPython device
@ -15,7 +14,10 @@ except OSError:
# If the specified file doesn't exist, # If the specified file doesn't exist,
# pre-populate with Wind In The Willows # pre-populate with Wind In The Willows
import witw import witw
open(text_file, "wb").write(witw.data()) with open(text_file, "wb") as f:
f.write(witw.data())
f.flush()
time.sleep(0.1)
del witw del witw
except ImportError: except ImportError:
pass pass
@ -33,9 +35,6 @@ ARROW_HEIGHT = 14
ARROW_PADDING = 2 ARROW_PADDING = 2
TEXT_PADDING = 4 TEXT_PADDING = 4
TEXT_SIZE = 0.5
TEXT_SPACING = int(34 * TEXT_SIZE)
TEXT_WIDTH = WIDTH - TEXT_PADDING - TEXT_PADDING - ARROW_WIDTH TEXT_WIDTH = WIDTH - TEXT_PADDING - TEXT_PADDING - ARROW_WIDTH
FONTS = ["sans", "gothic", "cursive", "serif"] FONTS = ["sans", "gothic", "cursive", "serif"]
@ -71,7 +70,7 @@ def draw_frame():
display.rectangle(WIDTH - ARROW_WIDTH, 0, ARROW_WIDTH, HEIGHT) display.rectangle(WIDTH - ARROW_WIDTH, 0, ARROW_WIDTH, HEIGHT)
display.pen(0) display.pen(0)
display.thickness(ARROW_THICKNESS) display.thickness(ARROW_THICKNESS)
if current_page > 1: if state["current_page"] > 0:
draw_up(WIDTH - ARROW_WIDTH, (HEIGHT // 4) - (ARROW_HEIGHT // 2), draw_up(WIDTH - ARROW_WIDTH, (HEIGHT // 4) - (ARROW_HEIGHT // 2),
ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING) ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING)
draw_down(WIDTH - ARROW_WIDTH, ((HEIGHT * 3) // 4) - (ARROW_HEIGHT // 2), draw_down(WIDTH - ARROW_WIDTH, ((HEIGHT * 3) // 4) - (ARROW_HEIGHT // 2),
@ -83,53 +82,23 @@ def draw_frame():
# ------------------------------ # ------------------------------
# Global variables # Global variables
next_page = True state = {
prev_page = False "last_offset": 0,
change_font_size = False "current_page": 0,
change_font = False "font_idx": 0,
last_offset = 0 "text_size": 0.5,
current_page = 0 "offsets": []
}
badger_os.state_load("ebook", state)
text_spacing = int(34 * state["text_size"])
# Create a new Badger and set it to update FAST # Create a new Badger and set it to update FAST
display = badger2040.Badger2040() display = badger2040.Badger2040()
display.led(128)
display.update_speed(badger2040.UPDATE_FAST) display.update_speed(badger2040.UPDATE_FAST)
# 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)
# Set up the activity LED
led = machine.Pin(badger2040.PIN_LED, machine.Pin.OUT)
offsets = []
# Button handling function
def button(pin):
global next_page, prev_page, change_font_size, change_font
if pin == button_down:
next_page = True
if pin == button_up:
prev_page = True
if pin == button_a:
change_font_size = True
if pin == button_b:
change_font = True
# 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)
# ------------------------------ # ------------------------------
# Render page # Render page
@ -141,7 +110,7 @@ def render_page():
pos = ebook.tell() pos = ebook.tell()
next_pos = pos next_pos = pos
add_newline = False add_newline = False
display.font(FONTS[0]) display.font(FONTS[state["font_idx"]])
while True: while True:
# Read a full line and split it into words # Read a full line and split it into words
@ -180,7 +149,7 @@ def render_page():
if len(line) > 0 and len(next_word) > 0: if len(line) > 0 and len(next_word) > 0:
appended_line += " " appended_line += " "
appended_line += next_word appended_line += next_word
appended_length = display.measure_text(appended_line, TEXT_SIZE) 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? # 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: if appended_length >= TEXT_WIDTH or add_newline:
@ -189,14 +158,14 @@ def render_page():
print(line) print(line)
display.pen(0) display.pen(0)
display.thickness(FONT_THICKNESSES[0]) display.thickness(FONT_THICKNESSES[0])
display.text(line, TEXT_PADDING, (row * TEXT_SPACING) + (TEXT_SPACING // 2) + TEXT_PADDING, TEXT_SIZE) 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 # Clear the line and move on to the next row
line = "" line = ""
row += 1 row += 1
# Have we reached the end of the page? # Have we reached the end of the page?
if (row * TEXT_SPACING) + TEXT_SPACING >= HEIGHT: if (row * text_spacing) + text_spacing >= HEIGHT:
print("+++++") print("+++++")
display.update() display.update()
@ -212,7 +181,7 @@ def render_page():
if add_newline: if add_newline:
print("") print("")
row += 1 row += 1
if (row * TEXT_SPACING) + TEXT_SPACING >= HEIGHT: if (row * text_spacing) + text_spacing >= HEIGHT:
print("+++++") print("+++++")
display.update() display.update()
return return
@ -227,50 +196,68 @@ def render_page():
# Main program loop # Main program loop
# ------------------------------ # ------------------------------
launch = True
changed = False
# Open the book file # Open the book file
ebook = open(text_file, "r") 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: while True:
# Was the next page button pressed? # Was the next page button pressed?
if next_page: if display.pressed(badger2040.BUTTON_DOWN):
current_page += 1 state["current_page"] += 1
# Is the next page one we've not displayed before? changed = True
if current_page > len(offsets):
offsets.append(ebook.tell()) # Add its start position to the offsets list
draw_frame()
render_page()
next_page = False # Clear the next page button flag
# Was the previous page button pressed? # Was the previous page button pressed?
if prev_page: if display.pressed(badger2040.BUTTON_UP):
if current_page > 1: if state["current_page"] > 0:
current_page -= 1 state["current_page"] -= 1
ebook.seek(offsets[current_page - 1]) # Retrieve the start position of the last page if state["current_page"] == 0:
draw_frame() ebook.seek(0)
render_page() else:
prev_page = False # Clear the prev page button flag ebook.seek(state["offsets"][state["current_page"] - 1]) # Retrieve the start position of the last page
changed = True
if change_font_size: if display.pressed(badger2040.BUTTON_A):
TEXT_SIZE += 0.1 state["text_size"] += 0.1
if TEXT_SIZE > 0.8: if state["text_size"] > 0.8:
TEXT_SIZE = 0.5 state["text_size"] = 0.5
TEXT_SPACING = int(34 * TEXT_SIZE) text_spacing = int(34 * state["text_size"])
offsets = [0] state["offsets"] = []
ebook.seek(0) ebook.seek(0)
current_page = 1 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() draw_frame()
render_page() render_page()
change_font_size = False
if change_font: # Is the next page one we've not displayed before?
FONTS.append(FONTS.pop(0)) if state["current_page"] >= len(state["offsets"]):
FONT_THICKNESSES.append(FONT_THICKNESSES.pop(0)) state["offsets"].append(ebook.tell()) # Add its start position to the state["offsets"] list
offsets = [0] badger_os.state_save("ebook", state)
ebook.seek(0)
current_page = 1
draw_frame()
render_page()
change_font = False
time.sleep(0.1) changed = False
display.halt()

View File

@ -1,6 +1,5 @@
import badger2040 import badger2040
import machine import badger_os
import time
# Global Constants # Global Constants
FONT_NAMES = ("sans", "gothic", "cursive", "serif", "serif_italic") FONT_NAMES = ("sans", "gothic", "cursive", "serif", "serif_italic")
@ -65,13 +64,13 @@ def draw_fonts():
for i in range(len(FONT_NAMES)): for i in range(len(FONT_NAMES)):
name = FONT_NAMES[i] name = FONT_NAMES[i]
display.pen(0) display.pen(0)
if i == selected_font: if i == state["selected_font"]:
display.rectangle(0, i * MENU_SPACING, MENU_WIDTH, MENU_SPACING) display.rectangle(0, i * MENU_SPACING, MENU_WIDTH, MENU_SPACING)
display.pen(15) display.pen(15)
display.text(name, MENU_PADDING, (i * MENU_SPACING) + (MENU_SPACING // 2), MENU_TEXT_SIZE) display.text(name, MENU_PADDING, (i * MENU_SPACING) + (MENU_SPACING // 2), MENU_TEXT_SIZE)
display.font(FONT_NAMES[selected_font]) display.font(FONT_NAMES[state["selected_font"]])
display.thickness(2) display.thickness(2)
display.pen(0) display.pen(0)
@ -91,51 +90,36 @@ def draw_fonts():
# ------------------------------ # ------------------------------
# Global variables # Global variables
selected_font = 0 state = {"selected_font": 0}
pressed = False badger_os.state_load("fonts", state)
# Create a new Badger and set it to update FAST # Create a new Badger and set it to update FAST
display = badger2040.Badger2040() display = badger2040.Badger2040()
display.led(128)
display.update_speed(badger2040.UPDATE_FAST) display.update_speed(badger2040.UPDATE_FAST)
# Set up the buttons changed = not badger2040.woken_by_button()
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)
# Button handling function
def button(pin):
global pressed
global selected_font
if not pressed:
if pin == button_up:
selected_font -= 1
if selected_font < 0:
selected_font = len(FONT_NAMES) - 1
pressed = True
return
if pin == button_down:
selected_font += 1
if selected_font >= len(FONT_NAMES):
selected_font = 0
pressed = True
return
# Register the button handling function with the buttons
button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
# ------------------------------ # ------------------------------
# Main program loop # Main program loop
# ------------------------------ # ------------------------------
while True: while True:
draw_frame() if display.pressed(badger2040.BUTTON_UP):
draw_fonts() 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
pressed = False if changed:
while not pressed: draw_frame()
time.sleep(0.1) draw_fonts()
badger_os.state_save("fonts", state)
changed = False
display.halt()

View File

@ -1,11 +1,12 @@
import badger2040 import badger2040
import time
from badger2040 import WIDTH from badger2040 import WIDTH
TEXT_SIZE = 0.45 TEXT_SIZE = 0.45
LINE_HEIGHT = 16 LINE_HEIGHT = 16
display = badger2040.Badger2040() display = badger2040.Badger2040()
display.led(128)
display.pen(0) display.pen(0)
display.rectangle(0, 0, WIDTH, 16) display.rectangle(0, 0, WIDTH, 16)
display.thickness(1) display.thickness(1)
@ -27,7 +28,7 @@ y += LINE_HEIGHT
y += LINE_HEIGHT y += LINE_HEIGHT
display.thickness(2) display.thickness(2)
display.text("Holding USER button:", 0, y, TEXT_SIZE) display.text("Hold USER after:", 0, y, TEXT_SIZE)
display.thickness(1) display.thickness(1)
y += LINE_HEIGHT y += LINE_HEIGHT
display.text("Up / Down - Change font size", 0, y, TEXT_SIZE) display.text("Up / Down - Change font size", 0, y, TEXT_SIZE)
@ -36,5 +37,4 @@ display.text("a - Toggle invert", 0, y, TEXT_SIZE)
y += LINE_HEIGHT y += LINE_HEIGHT
display.update() display.update()
while True: display.halt()
time.sleep(1)

View File

@ -1,9 +1,9 @@
import os import os
import sys import sys
import time import time
import machine
import badger2040 import badger2040
from badger2040 import WIDTH, HEIGHT from badger2040 import HEIGHT
import badger_os
REAMDE = """ REAMDE = """
@ -22,13 +22,14 @@ OVERLAY_TEXT_SIZE = 0.5
TOTAL_IMAGES = 0 TOTAL_IMAGES = 0
# Turn the act LED on as soon as possible
display = badger2040.Badger2040()
display.led(128)
# Try to preload BadgerPunk image # Try to preload BadgerPunk image
try: try:
os.mkdir("images") os.mkdir("images")
except OSError:
pass
try:
import badgerpunk import badgerpunk
with open("images/badgerpunk.bin", "wb") as f: with open("images/badgerpunk.bin", "wb") as f:
f.write(badgerpunk.data()) f.write(badgerpunk.data())
@ -40,6 +41,7 @@ try:
except (OSError, ImportError): except (OSError, ImportError):
pass pass
# Load images
try: try:
IMAGES = [f for f in os.listdir("/images") if f.endswith(".bin")] IMAGES = [f for f in os.listdir("/images") if f.endswith(".bin")]
TOTAL_IMAGES = len(IMAGES) TOTAL_IMAGES = len(IMAGES)
@ -47,50 +49,12 @@ except OSError:
pass pass
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)
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)
image = bytearray(int(296 * 128 / 8)) image = bytearray(int(296 * 128 / 8))
current_image = 0
show_info = True
state = {
# Draw an overlay box with a given message within it "current_image": 0,
def draw_overlay(message, width, height, line_spacing, text_size): "show_info": True
}
# Draw a light grey background
display.pen(12)
display.rectangle((WIDTH - width) // 2, (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], (WIDTH - length) // 2, (HEIGHT // 2) + current_line, text_size)
def show_image(n): def show_image(n):
@ -99,7 +63,7 @@ def show_image(n):
open("images/{}".format(file), "r").readinto(image) open("images/{}".format(file), "r").readinto(image)
display.image(image) display.image(image)
if show_info: if state["show_info"]:
name_length = display.measure_text(name, 0.5) name_length = display.measure_text(name, 0.5)
display.pen(0) display.pen(0)
display.rectangle(0, HEIGHT - 21, name_length + 11, 21) display.rectangle(0, HEIGHT - 21, name_length + 11, 21)
@ -113,7 +77,7 @@ def show_image(n):
y = int((128 / 2) - (TOTAL_IMAGES * 10 / 2) + (i * 10)) y = int((128 / 2) - (TOTAL_IMAGES * 10 / 2) + (i * 10))
display.pen(0) display.pen(0)
display.rectangle(x, y, 8, 8) display.rectangle(x, y, 8, 8)
if current_image != i: if state["current_image"] != i:
display.pen(15) display.pen(15)
display.rectangle(x + 1, y + 1, 6, 6) display.rectangle(x + 1, y + 1, 6, 6)
@ -123,32 +87,41 @@ def show_image(n):
if TOTAL_IMAGES == 0: if TOTAL_IMAGES == 0:
display.pen(15) display.pen(15)
display.clear() display.clear()
draw_overlay("To run this demo, create an /images directory on your device and upload some 1bit 296x128 pixel images.", WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE) badger_os.warning(display, "To run this demo, create an /images directory on your device and upload some 1bit 296x128 pixel images.")
display.update() time.sleep(4.0)
sys.exit() sys.exit()
show_image(current_image) badger_os.state_load("image", state)
changed = not badger2040.woken_by_button()
while True: while True:
if button_up.value(): if display.pressed(badger2040.BUTTON_UP):
if current_image > 0: if state["current_image"] > 0:
current_image -= 1 state["current_image"] -= 1
show_image(current_image) changed = True
if button_down.value(): if display.pressed(badger2040.BUTTON_DOWN):
if current_image < TOTAL_IMAGES - 1: if state["current_image"] < TOTAL_IMAGES - 1:
current_image += 1 state["current_image"] += 1
show_image(current_image) changed = True
if button_a.value(): if display.pressed(badger2040.BUTTON_A):
show_info = not show_info state["show_info"] = not state["show_info"]
show_image(current_image) changed = True
if button_b.value() or button_c.value(): if display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C):
display.pen(15) display.pen(15)
display.clear() display.clear()
draw_overlay("To add images connect Badger2040 to a PC, load up Thonny, and see readme.txt in images/", WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, 0.5) badger_os.warning(display, "To add images connect Badger2040 to a PC, load up Thonny, and see readme.txt in images/")
display.update() display.update()
print(state["current_image"])
time.sleep(4) time.sleep(4)
show_image(current_image) changed = True
time.sleep(0.01) 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()

View File

@ -1,11 +1,12 @@
import badger2040 import badger2040
import time
from badger2040 import WIDTH from badger2040 import WIDTH
TEXT_SIZE = 0.45 TEXT_SIZE = 0.45
LINE_HEIGHT = 16 LINE_HEIGHT = 16
display = badger2040.Badger2040() display = badger2040.Badger2040()
display.led(128)
display.pen(0) display.pen(0)
display.rectangle(0, 0, WIDTH, 16) display.rectangle(0, 0, WIDTH, 16)
display.thickness(1) display.thickness(1)
@ -33,6 +34,4 @@ y += LINE_HEIGHT
display.text("https://pimoroni.com/badger2040", 0, y, TEXT_SIZE) display.text("https://pimoroni.com/badger2040", 0, y, TEXT_SIZE)
display.update() display.update()
display.halt()
while True:
time.sleep(1)

View File

@ -1,20 +1,44 @@
import gc import gc
import os
import time import time
import math import math
import machine import machine
import badger2040 import badger2040
from badger2040 import WIDTH from badger2040 import WIDTH
import launchericons import launchericons
import badger_os
# Reduce clock speed to 48MHz, that's fast enough!
machine.freq(48000000)
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.clear_pressed_to_wake()
else:
# Otherwise restore previously running app
badger_os.state_launch()
# for e.g. 2xAAA batteries, try max 3.4 min 3.0 # for e.g. 2xAAA batteries, try max 3.4 min 3.0
MAX_BATTERY_VOLTAGE = 4.0 MAX_BATTERY_VOLTAGE = 4.0
MIN_BATTERY_VOLTAGE = 3.2 MIN_BATTERY_VOLTAGE = 3.2
display = badger2040.Badger2040()
display.led(128)
page = 0 state = {
font_size = 1 "page": 0,
inverted = False "font_size": 1,
"inverted": False,
"running": "launcher"
}
badger_os.state_load("launcher", state)
display.invert(state["inverted"])
icons = bytearray(launchericons.data()) icons = bytearray(launchericons.data())
icons_width = 576 icons_width = 576
@ -38,46 +62,11 @@ centers = (41, 147, 253)
MAX_PAGE = math.ceil(len(examples) / 3) MAX_PAGE = math.ceil(len(examples) / 3)
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)
# Inverted. For reasons.
button_user = machine.Pin(badger2040.BUTTON_USER, machine.Pin.IN, machine.Pin.PULL_UP)
# 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)
display = badger2040.Badger2040()
def map_value(input, in_min, in_max, out_min, out_max): 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 return (((input - in_min) * (out_max - out_min)) / (in_max - in_min)) + out_min
def get_battery_level():
# 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 int(map_value(vbat, MIN_BATTERY_VOLTAGE, MAX_BATTERY_VOLTAGE, 0, 4))
def draw_battery(level, x, y): def draw_battery(level, x, y):
# Outline # Outline
display.thickness(1) display.thickness(1)
@ -103,17 +92,7 @@ def draw_battery(level, x, y):
def draw_disk_usage(x): def draw_disk_usage(x):
# f_bfree and f_bavail should be the same? _, f_used, _ = badger_os.get_disk_usage()
# 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
display.image( display.image(
bytearray( bytearray(
@ -148,23 +127,23 @@ def render():
display.pen(0) display.pen(0)
display.thickness(2) display.thickness(2)
max_icons = min(3, len(examples[(page * 3):])) max_icons = min(3, len(examples[(state["page"] * 3):]))
for i in range(max_icons): for i in range(max_icons):
x = centers[i] x = centers[i]
label, icon = examples[i + (page * 3)] label, icon = examples[i + (state["page"] * 3)]
label = label[1:].replace("_", " ") label = label[1:].replace("_", " ")
display.pen(0) display.pen(0)
display.icon(icons, icon, icons_width, 64, x - 32, 24) display.icon(icons, icon, icons_width, 64, x - 32, 24)
w = display.measure_text(label, font_sizes[font_size]) w = display.measure_text(label, font_sizes[state["font_size"]])
display.text(label, x - int(w / 2), 16 + 80, font_sizes[font_size]) display.text(label, x - int(w / 2), 16 + 80, font_sizes[state["font_size"]])
for i in range(MAX_PAGE): for i in range(MAX_PAGE):
x = 286 x = 286
y = int((128 / 2) - (MAX_PAGE * 10 / 2) + (i * 10)) y = int((128 / 2) - (MAX_PAGE * 10 / 2) + (i * 10))
display.pen(0) display.pen(0)
display.rectangle(x, y, 8, 8) display.rectangle(x, y, 8, 8)
if page != i: if state["page"] != i:
display.pen(15) display.pen(15)
display.rectangle(x + 1, y + 1, 6, 6) display.rectangle(x + 1, y + 1, 6, 6)
@ -172,89 +151,93 @@ def render():
display.rectangle(0, 0, WIDTH, 16) display.rectangle(0, 0, WIDTH, 16)
display.thickness(1) display.thickness(1)
draw_disk_usage(90) draw_disk_usage(90)
draw_battery(get_battery_level(), WIDTH - 22 - 3, 3) 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.pen(15)
display.text("badgerOS", 3, 8, 0.4) display.text("badgerOS", 3, 8, 0.4)
display.update() display.update()
def launch(file): def wait_for_user_to_release_buttons():
for k in locals().keys(): pr = display.pressed
if k not in ("gc", "file", "machine"): 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):
del locals()[k] time.sleep(0.01)
gc.collect()
try:
__import__(file[1:]) # Try to import _[file] (drop underscore prefix)
except ImportError:
__import__(file) # Failover to importing [_file]
machine.reset() # Exit back to launcher
def launch_example(index): def launch_example(index):
try: wait_for_user_to_release_buttons()
launch(examples[(page * 3) + index][0])
return True file = examples[(state["page"] * 3) + index][0]
except IndexError:
return False 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): def button(pin):
global page, font_size, inverted global changed
changed = True
if button_user.value(): # User button is NOT held down if not display.pressed(badger2040.BUTTON_USER): # User button is NOT held down
if pin == button_a: if pin == badger2040.BUTTON_A:
launch_example(0) launch_example(0)
if pin == button_b: if pin == badger2040.BUTTON_B:
launch_example(1) launch_example(1)
if pin == button_c: if pin == badger2040.BUTTON_C:
launch_example(2) launch_example(2)
if pin == button_up: if pin == badger2040.BUTTON_UP:
if page > 0: if state["page"] > 0:
page -= 1 state["page"] -= 1
render() render()
if pin == button_down: if pin == badger2040.BUTTON_DOWN:
if page < MAX_PAGE - 1: if state["page"] < MAX_PAGE - 1:
page += 1 state["page"] += 1
render() render()
else: # User button IS held down else: # User button IS held down
if pin == button_up: if pin == badger2040.BUTTON_UP:
font_size += 1 state["font_size"] += 1
if font_size == len(font_sizes): if state["font_size"] == len(font_sizes):
font_size = 0 state["font_size"] = 0
render() render()
if pin == button_down: if pin == badger2040.BUTTON_DOWN:
font_size -= 1 state["font_size"] -= 1
if font_size < 0: if state["font_size"] < 0:
font_size = 0 state["font_size"] = 0
render() render()
if pin == button_a: if pin == badger2040.BUTTON_A:
inverted = not inverted state["inverted"] = not state["inverted"]
display.invert(inverted) display.invert(state["inverted"])
render() render()
display.update_speed(badger2040.UPDATE_MEDIUM) if exited_to_launcher or not woken_by_button:
render() wait_for_user_to_release_buttons()
display.update_speed(badger2040.UPDATE_MEDIUM)
render()
display.update_speed(badger2040.UPDATE_FAST) display.update_speed(badger2040.UPDATE_FAST)
# Wait for wakeup button to be released
while button_a.value() or button_b.value() or button_c.value() or button_up.value() or button_down.value():
pass
while True: while True:
if button_a.value(): if display.pressed(badger2040.BUTTON_A):
button(button_a) button(badger2040.BUTTON_A)
if button_b.value(): if display.pressed(badger2040.BUTTON_B):
button(button_b) button(badger2040.BUTTON_B)
if button_c.value(): if display.pressed(badger2040.BUTTON_C):
button(button_c) button(badger2040.BUTTON_C)
if button_up.value(): if display.pressed(badger2040.BUTTON_UP):
button(button_up) button(badger2040.BUTTON_UP)
if button_down.value(): if display.pressed(badger2040.BUTTON_DOWN):
button(button_down) button(badger2040.BUTTON_DOWN)
time.sleep(0.01) if changed:
badger_os.state_save("launcher", state)
changed = False
display.halt()

View File

@ -143,6 +143,7 @@ items_per_page = 0
# Create a new Badger and set it to update FAST # Create a new Badger and set it to update FAST
display = badger2040.Badger2040() display = badger2040.Badger2040()
display.led(128)
display.update_speed(badger2040.UPDATE_FAST) display.update_speed(badger2040.UPDATE_FAST)
# Set up the buttons # Set up the buttons
@ -177,6 +178,13 @@ items_per_page = ((LIST_HEIGHT // ITEM_SPACING) + 1) * list_columns
def button(pin): def button(pin):
global update, current_item, needs_save global update, current_item, needs_save
time.sleep(0.05)
if not pin.value():
return
if button_a.value() and button_c.value():
machine.reset()
if len(list_content) > 0 and not update: if len(list_content) > 0 and not update:
if pin == button_a: if pin == button_a:
if current_item > 0: if current_item > 0:

View File

@ -52,3 +52,4 @@ 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}/help.py _help)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/info.py _info) 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}/qrgen.py _qrgen)
copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/badger_os.py badger_os)

View File

@ -1,14 +1,13 @@
import badger2040 import badger2040
import qrcode import qrcode
import time
# Open the qrcode file # Open the qrcode file
try: try:
text = open("qrcode.txt", "r") text = open("qrcode.txt", "r")
except OSError: except OSError:
text = open("qrcode.txt", "w") with open("qrcode.txt", "w") as text:
text.write("""https://pimoroni.com/badger2040 text.write("""https://pimoroni.com/badger2040
Badger 2040 Badger 2040
* 296x128 1-bit e-ink * 296x128 1-bit e-ink
* six user buttons * six user buttons
@ -18,8 +17,8 @@ Badger 2040
Scan this code to learn Scan this code to learn
more about Badger 2040. more about Badger 2040.
""") """)
text.flush() text.flush()
text.seek(0) text = open("qrcode.txt", "r")
lines = text.read().strip().split("\n") lines = text.read().strip().split("\n")
@ -28,6 +27,7 @@ title_text = lines.pop(0)
detail_text = lines detail_text = lines
display = badger2040.Badger2040() display = badger2040.Badger2040()
display.led(128)
code = qrcode.QRCode() code = qrcode.QRCode()
@ -65,6 +65,4 @@ for line in detail_text:
top += 10 top += 10
display.update() display.update()
display.halt()
while True:
time.sleep(1.0)

View File

@ -16,6 +16,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_pen_obj, Badger2040_pen);
MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_thickness_obj, Badger2040_thickness); MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_thickness_obj, Badger2040_thickness);
MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_pressed_obj, Badger2040_pressed); MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_pressed_obj, Badger2040_pressed);
MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_pressed_to_wake2_obj, Badger2040_pressed_to_wake2);
MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_clear_obj, Badger2040_clear); MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_clear_obj, Badger2040_clear);
MP_DEFINE_CONST_FUN_OBJ_3(Badger2040_pixel_obj, Badger2040_pixel); MP_DEFINE_CONST_FUN_OBJ_3(Badger2040_pixel_obj, Badger2040_pixel);
MP_DEFINE_CONST_FUN_OBJ_KW(Badger2040_line_obj, 4, Badger2040_line); MP_DEFINE_CONST_FUN_OBJ_KW(Badger2040_line_obj, 4, Badger2040_line);
@ -32,6 +33,11 @@ MP_DEFINE_CONST_FUN_OBJ_KW(Badger2040_measure_glyph_obj, 2, Badger2040_measure_g
MP_DEFINE_CONST_FUN_OBJ_3(Badger2040_command_obj, Badger2040_command); MP_DEFINE_CONST_FUN_OBJ_3(Badger2040_command_obj, Badger2040_command);
MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_pressed_to_wake_obj, Badger2040_pressed_to_wake);
MP_DEFINE_CONST_FUN_OBJ_0(Badger2040_clear_pressed_to_wake_obj, Badger2040_clear_pressed_to_wake);
MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_halt_obj, Badger2040_halt);
MP_DEFINE_CONST_FUN_OBJ_0(Badger2040_woken_by_button_obj, Badger2040_woken_by_button);
/***** Binding of Methods *****/ /***** Binding of Methods *****/
STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = { STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Badger2040___del___obj) }, { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Badger2040___del___obj) },
@ -40,6 +46,8 @@ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&Badger2040_update_obj) }, { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&Badger2040_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_partial_update), MP_ROM_PTR(&Badger2040_partial_update_obj) }, { MP_ROM_QSTR(MP_QSTR_partial_update), MP_ROM_PTR(&Badger2040_partial_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_halt), MP_ROM_PTR(&Badger2040_halt_obj) },
{ MP_ROM_QSTR(MP_QSTR_invert), MP_ROM_PTR(&Badger2040_invert_obj) }, { MP_ROM_QSTR(MP_QSTR_invert), MP_ROM_PTR(&Badger2040_invert_obj) },
{ MP_ROM_QSTR(MP_QSTR_led), MP_ROM_PTR(&Badger2040_led_obj) }, { MP_ROM_QSTR(MP_QSTR_led), MP_ROM_PTR(&Badger2040_led_obj) },
{ MP_ROM_QSTR(MP_QSTR_font), MP_ROM_PTR(&Badger2040_font_obj) }, { MP_ROM_QSTR(MP_QSTR_font), MP_ROM_PTR(&Badger2040_font_obj) },
@ -47,6 +55,7 @@ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_thickness), MP_ROM_PTR(&Badger2040_thickness_obj) }, { MP_ROM_QSTR(MP_QSTR_thickness), MP_ROM_PTR(&Badger2040_thickness_obj) },
{ MP_ROM_QSTR(MP_QSTR_pressed), MP_ROM_PTR(&Badger2040_pressed_obj) }, { MP_ROM_QSTR(MP_QSTR_pressed), MP_ROM_PTR(&Badger2040_pressed_obj) },
{ MP_ROM_QSTR(MP_QSTR_pressed_to_wake), MP_ROM_PTR(&Badger2040_pressed_to_wake2_obj) },
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&Badger2040_clear_obj) }, { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&Badger2040_clear_obj) },
{ MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&Badger2040_pixel_obj) }, { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&Badger2040_pixel_obj) },
@ -78,10 +87,14 @@ const mp_obj_type_t Badger2040_type = {
/***** Globals Table *****/ /***** Globals Table *****/
STATIC const mp_map_elem_t badger2040_globals_table[] = { STATIC const mp_rom_map_elem_t badger2040_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_badger2040) }, { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_badger2040) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_Badger2040), (mp_obj_t)&Badger2040_type }, { MP_OBJ_NEW_QSTR(MP_QSTR_Badger2040), (mp_obj_t)&Badger2040_type },
{ MP_ROM_QSTR(MP_QSTR_pressed_to_wake), MP_ROM_PTR(&Badger2040_pressed_to_wake_obj) },
{ MP_ROM_QSTR(MP_QSTR_clear_pressed_to_wake), MP_ROM_PTR(&Badger2040_clear_pressed_to_wake_obj) },
{ MP_ROM_QSTR(MP_QSTR_woken_by_button), MP_ROM_PTR(&Badger2040_woken_by_button_obj) },
{ MP_ROM_QSTR(MP_QSTR_UPDATE_NORMAL), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_NORMAL), MP_ROM_INT(0) },
{ MP_ROM_QSTR(MP_QSTR_UPDATE_MEDIUM), MP_ROM_INT(1) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_MEDIUM), MP_ROM_INT(1) },
{ MP_ROM_QSTR(MP_QSTR_UPDATE_FAST), MP_ROM_INT(2) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_FAST), MP_ROM_INT(2) },
@ -121,4 +134,4 @@ const mp_obj_module_t badger2040_user_cmodule = {
.base = { &mp_type_module }, .base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_badger2040_globals, .globals = (mp_obj_dict_t*)&mp_module_badger2040_globals,
}; };
MP_REGISTER_MODULE(MP_QSTR_badger2040, badger2040_user_cmodule, MODULE_BADGER2040_ENABLED); MP_REGISTER_MODULE(MP_QSTR_badger2040, badger2040_user_cmodule, MODULE_BADGER2040_ENABLED);

View File

@ -1,8 +1,45 @@
#include <cstdio> #include <cstdio>
#include "hardware/watchdog.h"
#include "badger2040.hpp" #include "badger2040.hpp"
#define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) #define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o))
namespace {
struct Badger2040_WakeUpInit {
Badger2040_WakeUpInit()
: state(gpio_get_all() & (0x1f << pimoroni::Badger2040::DOWN)) // Record state of front buttons
{
gpio_set_function(pimoroni::Badger2040::ENABLE_3V3, GPIO_FUNC_SIO);
gpio_set_dir(pimoroni::Badger2040::ENABLE_3V3, GPIO_OUT);
gpio_put(pimoroni::Badger2040::ENABLE_3V3, 1);
gpio_set_function(pimoroni::Badger2040::LED, GPIO_FUNC_SIO);
gpio_set_dir(pimoroni::Badger2040::LED, GPIO_OUT);
gpio_put(pimoroni::Badger2040::LED, 1);
}
bool any() const {
return state > 0;
}
bool get(uint32_t pin) const {
return state & (0b1 << pin);
}
bool get_once(uint32_t pin) {
uint32_t mask = 0b1 << pin;
bool value = state & mask;
state &= ~mask;
return value;
}
void clear() { state = 0; }
private:
uint32_t state;
};
Badger2040_WakeUpInit button_wake_state __attribute__ ((init_priority (101)));
};
extern "C" { extern "C" {
#include "badger2040.h" #include "badger2040.h"
@ -116,9 +153,12 @@ MICROPY_EVENT_POLL_HOOK
#endif #endif
} }
absolute_time_t t_end = make_timeout_time_ms(self->badger2040->update_time());
self->badger2040->update(false); self->badger2040->update(false);
while(self->badger2040->is_busy()) { // Ensure blocking for the minimum amount of time
// in cases where "is_busy" is unreliable.
while(self->badger2040->is_busy() || absolute_time_diff_us(t_end, get_absolute_time()) > 0) {
#ifdef MICROPY_EVENT_POLL_HOOK #ifdef MICROPY_EVENT_POLL_HOOK
MICROPY_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK
#endif #endif
@ -157,9 +197,12 @@ MICROPY_EVENT_POLL_HOOK
#endif #endif
} }
absolute_time_t t_end = make_timeout_time_ms(self->badger2040->update_time());
self->badger2040->partial_update(x, y, w, h); self->badger2040->partial_update(x, y, w, h);
while(self->badger2040->is_busy()) { // Ensure blocking for the minimum amount of time
// in cases where "is_busy" is unreliable.
while(self->badger2040->is_busy() || absolute_time_diff_us(t_end, get_absolute_time()) > 0) {
#ifdef MICROPY_EVENT_POLL_HOOK #ifdef MICROPY_EVENT_POLL_HOOK
MICROPY_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK
#endif #endif
@ -170,7 +213,27 @@ MICROPY_EVENT_POLL_HOOK
return mp_const_none; return mp_const_none;
} }
// halt mp_obj_t Badger2040_woken_by_button() {
return button_wake_state.any() ? mp_const_true : mp_const_false;
}
mp_obj_t Badger2040_halt(mp_obj_t self_in) {
_Badger2040_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Badger2040_obj_t);
// Don't use the Badger halt so we can allow Micropython to be interrupted.
gpio_put(pimoroni::Badger2040::ENABLE_3V3, 0);
self->badger2040->update_button_states();
while (self->badger2040->button_states() == 0) {
#ifdef MICROPY_EVENT_POLL_HOOK
MICROPY_EVENT_POLL_HOOK
#endif
self->badger2040->update_button_states();
}
//watchdog_reboot(0, SRAM_END, 0);
return mp_const_none;
}
// sleep // sleep
mp_obj_t Badger2040_invert(mp_obj_t self_in, mp_obj_t invert) { mp_obj_t Badger2040_invert(mp_obj_t self_in, mp_obj_t invert) {
@ -206,10 +269,25 @@ mp_obj_t Badger2040_thickness(mp_obj_t self_in, mp_obj_t thickness) {
mp_obj_t Badger2040_pressed(mp_obj_t self_in, mp_obj_t button) { mp_obj_t Badger2040_pressed(mp_obj_t self_in, mp_obj_t button) {
_Badger2040_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Badger2040_obj_t); _Badger2040_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Badger2040_obj_t);
self->badger2040->update_button_states(); self->badger2040->update_button_states();
bool wake_state = button_wake_state.get_once(mp_obj_get_int(button));
bool state = self->badger2040->pressed(mp_obj_get_int(button)); bool state = self->badger2040->pressed(mp_obj_get_int(button));
return (state || wake_state) ? mp_const_true : mp_const_false;
}
mp_obj_t Badger2040_pressed_to_wake(mp_obj_t button) {
bool state = button_wake_state.get(mp_obj_get_int(button));
return state ? mp_const_true : mp_const_false; return state ? mp_const_true : mp_const_false;
} }
mp_obj_t Badger2040_pressed_to_wake2(mp_obj_t self_in, mp_obj_t button) {
return Badger2040_pressed_to_wake(button);
}
mp_obj_t Badger2040_clear_pressed_to_wake() {
button_wake_state.clear();
return mp_const_none;
}
// pressed // pressed
// pressed_to_wake // pressed_to_wake
// wait_for_press - implement in terms of MicroPython! // wait_for_press - implement in terms of MicroPython!
@ -467,4 +545,4 @@ mp_obj_t Badger2040_measure_glyph(size_t n_args, const mp_obj_t *pos_args, mp_ma
return mp_obj_new_int(self->badger2040->measure_glyph(c, scale)); return mp_obj_new_int(self->badger2040->measure_glyph(c, scale));
} }
} }

View File

@ -15,6 +15,8 @@ extern mp_obj_t Badger2040_update_speed(mp_obj_t self_in, mp_obj_t speed);
extern mp_obj_t Badger2040_update(mp_obj_t self_in); extern mp_obj_t Badger2040_update(mp_obj_t self_in);
extern mp_obj_t Badger2040_partial_update(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t Badger2040_partial_update(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t Badger2040_halt(mp_obj_t self_in);
extern mp_obj_t Badger2040_invert(mp_obj_t self_in, mp_obj_t invert); extern mp_obj_t Badger2040_invert(mp_obj_t self_in, mp_obj_t invert);
extern mp_obj_t Badger2040_led(mp_obj_t self_in, mp_obj_t brightness); extern mp_obj_t Badger2040_led(mp_obj_t self_in, mp_obj_t brightness);
extern mp_obj_t Badger2040_font(mp_obj_t self_in, mp_obj_t font); extern mp_obj_t Badger2040_font(mp_obj_t self_in, mp_obj_t font);
@ -22,6 +24,7 @@ extern mp_obj_t Badger2040_pen(mp_obj_t self_in, mp_obj_t color);
extern mp_obj_t Badger2040_thickness(mp_obj_t self_in, mp_obj_t thickness); extern mp_obj_t Badger2040_thickness(mp_obj_t self_in, mp_obj_t thickness);
extern mp_obj_t Badger2040_pressed(mp_obj_t self_in, mp_obj_t button); extern mp_obj_t Badger2040_pressed(mp_obj_t self_in, mp_obj_t button);
extern mp_obj_t Badger2040_pressed_to_wake2(mp_obj_t self_in, mp_obj_t button);
extern mp_obj_t Badger2040_clear(mp_obj_t self_in); extern mp_obj_t Badger2040_clear(mp_obj_t self_in);
extern mp_obj_t Badger2040_pixel(mp_obj_t self_in, mp_obj_t x, mp_obj_t y); extern mp_obj_t Badger2040_pixel(mp_obj_t self_in, mp_obj_t x, mp_obj_t y);
@ -37,4 +40,8 @@ extern mp_obj_t Badger2040_glyph(size_t n_args, const mp_obj_t *pos_args, mp_map
extern mp_obj_t Badger2040_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t Badger2040_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t Badger2040_measure_glyph(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t Badger2040_measure_glyph(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t Badger2040_command(mp_obj_t self_in, mp_obj_t reg, mp_obj_t data); extern mp_obj_t Badger2040_command(mp_obj_t self_in, mp_obj_t reg, mp_obj_t data);
extern mp_obj_t Badger2040_pressed_to_wake(mp_obj_t button);
extern mp_obj_t Badger2040_clear_pressed_to_wake();
extern mp_obj_t Badger2040_woken_by_button();