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) {
reset();
_update_speed = speed;
if(speed == 0) {
command(PSR, {
RES_128x296 | LUT_OTP | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE
@ -468,6 +470,25 @@ namespace pimoroni {
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) {
// y is given in columns ("banks"), which are groups of 8 horiontal pixels
// x is given in pixels

View File

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

View File

@ -40,7 +40,7 @@ namespace pimoroni {
gpio_set_function(USER, GPIO_FUNC_SIO);
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_dir(VBUS_DETECT, GPIO_IN);
@ -195,8 +195,9 @@ namespace pimoroni {
}
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 ^= (1UL << USER); // USER button state is inverted
}
uint32_t Badger2040::button_states() {
@ -219,6 +220,10 @@ namespace pimoroni {
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) {
uc8151.partial_update(x, y, w, h, blocking);
}

View File

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

View File

@ -1,6 +1,6 @@
import badger2040
import machine
import time
import badger2040
import badger_os
# Global Constants
WIDTH = badger2040.WIDTH
@ -20,10 +20,6 @@ LEFT_PADDING = 5
NAME_PADDING = 20
DETAIL_SPACING = 10
OVERLAY_BORDER = 40
OVERLAY_SPACING = 20
OVERLAY_TEXT_SIZE = 0.6
DEFAULT_TEXT = """mustelid inc
H. Badger
RP2040
@ -63,42 +59,6 @@ def truncatestring(text, text_size, width):
# 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
def draw_badge():
display.pen(0)
@ -170,21 +130,19 @@ def draw_badge():
# Program setup
# ------------------------------
# Global variables
show_overlay = False
# 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:
badge = open("badge.txt", "w")
badge.write(DEFAULT_TEXT)
badge.flush()
badge.seek(0)
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"
@ -205,63 +163,21 @@ 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))
# 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()
display.update()
while True:
if show_overlay:
draw_overlay("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()
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()
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()
screen = badger2040.Badger2040()
screen.update_speed(badger2040.UPDATE_TURBO)
screen.font("gothic")
display = badger2040.Badger2040()
display.led(128)
display.update_speed(badger2040.UPDATE_TURBO)
display.font("gothic")
cursors = ["year", "month", "day", "hour", "minute"]
set_clock = False
@ -32,10 +33,13 @@ def days_in_month(month, year):
def button(pin):
global last, set_clock, cursor, year, month, day, hour, minute
time.sleep(0.05)
time.sleep(0.01)
if not pin.value():
return
if button_a.value() and button_c.value():
machine.reset()
adjust = 0
changed = False
@ -94,42 +98,42 @@ def draw_clock():
hms = "{:02}:{:02}:{:02}".format(hour, minute, second)
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))
h_width = screen.measure_text(hms[0:2], 1.8)
mi_width = screen.measure_text(hms[3:5], 1.8)
mi_offset = screen.measure_text(hms[0:3], 1.8)
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 = screen.measure_text(ymd, 1.0)
ymd_width = display.measure_text(ymd, 1.0)
ymd_offset = int((badger2040.WIDTH / 2) - (ymd_width / 2))
y_width = screen.measure_text(ymd[0:4], 1.0)
m_width = screen.measure_text(ymd[5:7], 1.0)
m_offset = screen.measure_text(ymd[0:5], 1.0)
d_width = screen.measure_text(ymd[8:10], 1.0)
d_offset = screen.measure_text(ymd[0:8], 1.0)
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)
screen.pen(15)
screen.clear()
screen.pen(0)
screen.thickness(5)
screen.text(hms, hms_offset, 40, 1.8)
screen.thickness(3)
screen.text(ymd, ymd_offset, 100, 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":
screen.line(ymd_offset, 120, ymd_offset + y_width, 120)
display.line(ymd_offset, 120, ymd_offset + y_width, 120)
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":
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":
screen.line(hms_offset, 70, hms_offset + h_width, 70)
display.line(hms_offset, 70, hms_offset + h_width, 70)
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()
@ -145,4 +149,4 @@ while True:
if second != last_second:
draw_clock()
last_second = second
time.sleep(0.1)
time.sleep(0.01)

View File

@ -1,8 +1,7 @@
import badger2040
import machine
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
@ -15,7 +14,10 @@ except OSError:
# If the specified file doesn't exist,
# pre-populate with Wind In The Willows
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
except ImportError:
pass
@ -33,9 +35,6 @@ ARROW_HEIGHT = 14
ARROW_PADDING = 2
TEXT_PADDING = 4
TEXT_SIZE = 0.5
TEXT_SPACING = int(34 * TEXT_SIZE)
TEXT_WIDTH = WIDTH - TEXT_PADDING - TEXT_PADDING - ARROW_WIDTH
FONTS = ["sans", "gothic", "cursive", "serif"]
@ -71,7 +70,7 @@ def draw_frame():
display.rectangle(WIDTH - ARROW_WIDTH, 0, ARROW_WIDTH, HEIGHT)
display.pen(0)
display.thickness(ARROW_THICKNESS)
if current_page > 1:
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),
@ -83,53 +82,23 @@ def draw_frame():
# ------------------------------
# Global variables
next_page = True
prev_page = False
change_font_size = False
change_font = False
last_offset = 0
current_page = 0
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)
# 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
@ -141,7 +110,7 @@ def render_page():
pos = ebook.tell()
next_pos = pos
add_newline = False
display.font(FONTS[0])
display.font(FONTS[state["font_idx"]])
while True:
# 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:
appended_line += " "
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?
if appended_length >= TEXT_WIDTH or add_newline:
@ -189,14 +158,14 @@ def render_page():
print(line)
display.pen(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
line = ""
row += 1
# Have we reached the end of the page?
if (row * TEXT_SPACING) + TEXT_SPACING >= HEIGHT:
if (row * text_spacing) + text_spacing >= HEIGHT:
print("+++++")
display.update()
@ -212,7 +181,7 @@ def render_page():
if add_newline:
print("")
row += 1
if (row * TEXT_SPACING) + TEXT_SPACING >= HEIGHT:
if (row * text_spacing) + text_spacing >= HEIGHT:
print("+++++")
display.update()
return
@ -227,50 +196,68 @@ def render_page():
# 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 next_page:
current_page += 1
if display.pressed(badger2040.BUTTON_DOWN):
state["current_page"] += 1
# Is the next page one we've not displayed before?
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
changed = True
# Was the previous page button pressed?
if prev_page:
if current_page > 1:
current_page -= 1
ebook.seek(offsets[current_page - 1]) # Retrieve the start position of the last page
draw_frame()
render_page()
prev_page = False # Clear the prev page button flag
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 change_font_size:
TEXT_SIZE += 0.1
if TEXT_SIZE > 0.8:
TEXT_SIZE = 0.5
TEXT_SPACING = int(34 * TEXT_SIZE)
offsets = [0]
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)
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()
render_page()
change_font_size = False
if change_font:
FONTS.append(FONTS.pop(0))
FONT_THICKNESSES.append(FONT_THICKNESSES.pop(0))
offsets = [0]
ebook.seek(0)
current_page = 1
draw_frame()
render_page()
change_font = False
# 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)
time.sleep(0.1)
changed = False
display.halt()

View File

@ -1,6 +1,5 @@
import badger2040
import machine
import time
import badger_os
# Global Constants
FONT_NAMES = ("sans", "gothic", "cursive", "serif", "serif_italic")
@ -65,13 +64,13 @@ def draw_fonts():
for i in range(len(FONT_NAMES)):
name = FONT_NAMES[i]
display.pen(0)
if i == selected_font:
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) + (MENU_SPACING // 2), MENU_TEXT_SIZE)
display.font(FONT_NAMES[selected_font])
display.font(FONT_NAMES[state["selected_font"]])
display.thickness(2)
display.pen(0)
@ -91,51 +90,36 @@ def draw_fonts():
# ------------------------------
# Global variables
selected_font = 0
pressed = False
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)
# Set up the buttons
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)
changed = not badger2040.woken_by_button()
# ------------------------------
# Main program loop
# ------------------------------
while True:
draw_frame()
draw_fonts()
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
pressed = False
while not pressed:
time.sleep(0.1)
if changed:
draw_frame()
draw_fonts()
badger_os.state_save("fonts", state)
changed = False
display.halt()

View File

@ -1,11 +1,12 @@
import badger2040
import time
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)
@ -27,7 +28,7 @@ y += LINE_HEIGHT
y += LINE_HEIGHT
display.thickness(2)
display.text("Holding USER button:", 0, y, TEXT_SIZE)
display.text("Hold USER after:", 0, y, TEXT_SIZE)
display.thickness(1)
y += LINE_HEIGHT
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
display.update()
while True:
time.sleep(1)
display.halt()

View File

@ -1,9 +1,9 @@
import os
import sys
import time
import machine
import badger2040
from badger2040 import WIDTH, HEIGHT
from badger2040 import HEIGHT
import badger_os
REAMDE = """
@ -22,13 +22,14 @@ 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")
except OSError:
pass
try:
import badgerpunk
with open("images/badgerpunk.bin", "wb") as f:
f.write(badgerpunk.data())
@ -40,6 +41,7 @@ try:
except (OSError, ImportError):
pass
# Load images
try:
IMAGES = [f for f in os.listdir("/images") if f.endswith(".bin")]
TOTAL_IMAGES = len(IMAGES)
@ -47,50 +49,12 @@ except OSError:
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))
current_image = 0
show_info = True
# 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 = []
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)
state = {
"current_image": 0,
"show_info": True
}
def show_image(n):
@ -99,7 +63,7 @@ def show_image(n):
open("images/{}".format(file), "r").readinto(image)
display.image(image)
if show_info:
if state["show_info"]:
name_length = display.measure_text(name, 0.5)
display.pen(0)
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))
display.pen(0)
display.rectangle(x, y, 8, 8)
if current_image != i:
if state["current_image"] != i:
display.pen(15)
display.rectangle(x + 1, y + 1, 6, 6)
@ -123,32 +87,41 @@ def show_image(n):
if TOTAL_IMAGES == 0:
display.pen(15)
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)
display.update()
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()
show_image(current_image)
badger_os.state_load("image", state)
changed = not badger2040.woken_by_button()
while True:
if button_up.value():
if current_image > 0:
current_image -= 1
show_image(current_image)
if button_down.value():
if current_image < TOTAL_IMAGES - 1:
current_image += 1
show_image(current_image)
if button_a.value():
show_info = not show_info
show_image(current_image)
if button_b.value() or button_c.value():
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()
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()
print(state["current_image"])
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 time
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)
@ -33,6 +34,4 @@ y += LINE_HEIGHT
display.text("https://pimoroni.com/badger2040", 0, y, TEXT_SIZE)
display.update()
while True:
time.sleep(1)
display.halt()

View File

@ -1,20 +1,44 @@
import gc
import os
import time
import math
import machine
import badger2040
from badger2040 import WIDTH
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
MAX_BATTERY_VOLTAGE = 4.0
MIN_BATTERY_VOLTAGE = 3.2
display = badger2040.Badger2040()
display.led(128)
page = 0
font_size = 1
inverted = False
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
@ -38,46 +62,11 @@ centers = (41, 147, 253)
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):
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):
# Outline
display.thickness(1)
@ -103,17 +92,7 @@ def draw_battery(level, x, y):
def draw_disk_usage(x):
# 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
_, f_used, _ = badger_os.get_disk_usage()
display.image(
bytearray(
@ -148,23 +127,23 @@ def render():
display.pen(0)
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):
x = centers[i]
label, icon = examples[i + (page * 3)]
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[font_size])
display.text(label, x - int(w / 2), 16 + 80, 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[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 page != i:
if state["page"] != i:
display.pen(15)
display.rectangle(x + 1, y + 1, 6, 6)
@ -172,89 +151,93 @@ def render():
display.rectangle(0, 0, WIDTH, 16)
display.thickness(1)
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.text("badgerOS", 3, 8, 0.4)
display.update()
def launch(file):
for k in locals().keys():
if k not in ("gc", "file", "machine"):
del locals()[k]
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 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):
try:
launch(examples[(page * 3) + index][0])
return True
except IndexError:
return False
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 page, font_size, inverted
global changed
changed = True
if button_user.value(): # User button is NOT held down
if pin == button_a:
if not display.pressed(badger2040.BUTTON_USER): # User button is NOT held down
if pin == badger2040.BUTTON_A:
launch_example(0)
if pin == button_b:
if pin == badger2040.BUTTON_B:
launch_example(1)
if pin == button_c:
if pin == badger2040.BUTTON_C:
launch_example(2)
if pin == button_up:
if page > 0:
page -= 1
render()
if pin == button_down:
if page < MAX_PAGE - 1:
page += 1
render()
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 == button_up:
font_size += 1
if font_size == len(font_sizes):
font_size = 0
if pin == badger2040.BUTTON_UP:
state["font_size"] += 1
if state["font_size"] == len(font_sizes):
state["font_size"] = 0
render()
if pin == button_down:
font_size -= 1
if font_size < 0:
font_size = 0
if pin == badger2040.BUTTON_DOWN:
state["font_size"] -= 1
if state["font_size"] < 0:
state["font_size"] = 0
render()
if pin == button_a:
inverted = not inverted
display.invert(inverted)
if pin == badger2040.BUTTON_A:
state["inverted"] = not state["inverted"]
display.invert(state["inverted"])
render()
display.update_speed(badger2040.UPDATE_MEDIUM)
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)
# 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:
if button_a.value():
button(button_a)
if button_b.value():
button(button_b)
if button_c.value():
button(button_c)
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 button_up.value():
button(button_up)
if button_down.value():
button(button_down)
if display.pressed(badger2040.BUTTON_UP):
button(badger2040.BUTTON_UP)
if display.pressed(badger2040.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
display = badger2040.Badger2040()
display.led(128)
display.update_speed(badger2040.UPDATE_FAST)
# Set up the buttons
@ -177,6 +178,13 @@ items_per_page = ((LIST_HEIGHT // ITEM_SPACING) + 1) * list_columns
def button(pin):
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 pin == button_a:
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}/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)

View File

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

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_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_3(Badger2040_pixel_obj, Badger2040_pixel);
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_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 *****/
STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = {
{ 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_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_led), MP_ROM_PTR(&Badger2040_led_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_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_pixel), MP_ROM_PTR(&Badger2040_pixel_obj) },
@ -78,10 +87,14 @@ const mp_obj_type_t Badger2040_type = {
/***** 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_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_MEDIUM), MP_ROM_INT(1) },
{ 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 },
.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 "hardware/watchdog.h"
#include "badger2040.hpp"
#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" {
#include "badger2040.h"
@ -116,9 +153,12 @@ MICROPY_EVENT_POLL_HOOK
#endif
}
absolute_time_t t_end = make_timeout_time_ms(self->badger2040->update_time());
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
MICROPY_EVENT_POLL_HOOK
#endif
@ -157,9 +197,12 @@ MICROPY_EVENT_POLL_HOOK
#endif
}
absolute_time_t t_end = make_timeout_time_ms(self->badger2040->update_time());
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
MICROPY_EVENT_POLL_HOOK
#endif
@ -170,7 +213,27 @@ MICROPY_EVENT_POLL_HOOK
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
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) {
_Badger2040_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Badger2040_obj_t);
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));
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;
}
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_to_wake
// 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));
}
}
}

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_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_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);
@ -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_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_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_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();