kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Merge pull request #702 from pimoroni/docs/inky73
Inky 7.3 tweaks & docsrefactor/micropython-cmake v1.19.16
commit
faf4efac34
|
@ -54,7 +54,7 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
datetime_t PCF85063A::get_datetime() {
|
||||
static uint8_t result[7] = {0};
|
||||
uint8_t result[7] = {0};
|
||||
|
||||
i2c->read_bytes(address, Registers::SECONDS, result, 7);
|
||||
|
||||
|
@ -72,7 +72,7 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
void PCF85063A::set_datetime(datetime_t *t) {
|
||||
static uint8_t data[7] = {
|
||||
uint8_t data[7] = {
|
||||
bcd_encode((uint)t->sec),
|
||||
bcd_encode((uint)t->min),
|
||||
bcd_encode((uint)t->hour),
|
||||
|
|
|
@ -50,6 +50,10 @@ namespace pimoroni {
|
|||
}
|
||||
}
|
||||
|
||||
void PicoGraphics::set_thickness(uint t) {
|
||||
thickness = t;
|
||||
}
|
||||
|
||||
void PicoGraphics::set_clip(const Rect &r) {
|
||||
clip = bounds.intersection(r);
|
||||
}
|
||||
|
@ -284,6 +288,25 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
void PicoGraphics::thick_line(Point p1, Point p2, uint thickness) {
|
||||
int32_t ht = thickness / 2;
|
||||
int32_t t = (int32_t)thickness;
|
||||
|
||||
// fast horizontal line
|
||||
if(p1.y == p2.y) {
|
||||
int32_t start = std::min(p1.x, p2.x);
|
||||
int32_t end = std::max(p1.x, p2.x);
|
||||
rectangle(Rect(start, p1.y - ht, end - start, t));
|
||||
return;
|
||||
}
|
||||
|
||||
// fast vertical line
|
||||
if(p1.x == p2.x) {
|
||||
int32_t start = std::min(p1.y, p2.y);
|
||||
int32_t length = std::max(p1.y, p2.y) - start;
|
||||
rectangle(Rect(p1.x - ht, start, t, length));
|
||||
return;
|
||||
}
|
||||
|
||||
// general purpose line
|
||||
// lines are either "shallow" or "steep" based on whether the x delta
|
||||
// is greater than the y delta
|
||||
|
@ -298,8 +321,7 @@ namespace pimoroni {
|
|||
int32_t x = p1.x;
|
||||
int32_t y = p1.y << 16;
|
||||
while(s--) {
|
||||
int32_t ht = thickness / 2;
|
||||
rectangle({x - ht, (y >> 16) - ht, ht * 2, ht * 2});
|
||||
rectangle({x - ht, (y >> 16) - ht, t, t});
|
||||
y += sy;
|
||||
x += sx;
|
||||
}
|
||||
|
@ -311,8 +333,7 @@ namespace pimoroni {
|
|||
int32_t y = p1.y;
|
||||
int32_t x = p1.x << 16;
|
||||
while(s--) {
|
||||
int32_t ht = thickness / 2;
|
||||
rectangle({(x >> 16) - ht, y - ht, ht * 2, ht * 2});
|
||||
rectangle({(x >> 16) - ht, y - ht, t, t});
|
||||
y += sy;
|
||||
x += sx;
|
||||
}
|
||||
|
|
|
@ -251,7 +251,7 @@ namespace pimoroni {
|
|||
virtual void set_pen(uint8_t r, uint8_t g, uint8_t b) = 0;
|
||||
virtual void set_pixel(const Point &p) = 0;
|
||||
virtual void set_pixel_span(const Point &p, uint l) = 0;
|
||||
virtual void set_thickness(uint t) = 0;
|
||||
void set_thickness(uint t);
|
||||
|
||||
virtual int get_palette_size();
|
||||
virtual RGB* get_palette();
|
||||
|
@ -304,7 +304,6 @@ namespace pimoroni {
|
|||
PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer);
|
||||
void set_pen(uint c) override;
|
||||
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
void set_thickness(uint t) override;
|
||||
|
||||
void set_pixel(const Point &p) override;
|
||||
void set_pixel_span(const Point &p, uint l) override;
|
||||
|
@ -321,7 +320,6 @@ namespace pimoroni {
|
|||
PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer);
|
||||
void set_pen(uint c) override;
|
||||
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
void set_thickness(uint t) override;
|
||||
|
||||
void set_pixel(const Point &p) override;
|
||||
void set_pixel_span(const Point &p, uint l) override;
|
||||
|
@ -364,7 +362,6 @@ namespace pimoroni {
|
|||
|
||||
void set_pen(uint c) override;
|
||||
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
void set_thickness(uint t) override {};
|
||||
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
int create_pen_hsv(float h, float s, float v) override;
|
||||
|
||||
|
@ -397,7 +394,6 @@ namespace pimoroni {
|
|||
PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer);
|
||||
void set_pen(uint c) override;
|
||||
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
void set_thickness(uint t) override {};
|
||||
int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override;
|
||||
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
int create_pen_hsv(float h, float s, float v) override;
|
||||
|
@ -431,7 +427,6 @@ namespace pimoroni {
|
|||
PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer);
|
||||
void set_pen(uint c) override;
|
||||
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
void set_thickness(uint t) override {};
|
||||
int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override;
|
||||
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
int create_pen_hsv(float h, float s, float v) override;
|
||||
|
@ -457,7 +452,6 @@ namespace pimoroni {
|
|||
PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer);
|
||||
void set_pen(uint c) override;
|
||||
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
void set_thickness(uint t) override {};
|
||||
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
int create_pen_hsv(float h, float s, float v) override;
|
||||
void set_pixel(const Point &p) override;
|
||||
|
@ -480,7 +474,6 @@ namespace pimoroni {
|
|||
PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer);
|
||||
void set_pen(uint c) override;
|
||||
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
void set_thickness(uint t) override {};
|
||||
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
int create_pen_hsv(float h, float s, float v) override;
|
||||
void set_pixel(const Point &p) override;
|
||||
|
@ -497,7 +490,6 @@ namespace pimoroni {
|
|||
PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer);
|
||||
void set_pen(uint c) override;
|
||||
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
void set_thickness(uint t) override {};
|
||||
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
int create_pen_hsv(float h, float s, float v) override;
|
||||
void set_pixel(const Point &p) override;
|
||||
|
@ -570,7 +562,6 @@ namespace pimoroni {
|
|||
PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver<uint8_t> &direct_display_driver);
|
||||
void set_pen(uint c) override;
|
||||
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
void set_thickness(uint t) override {};
|
||||
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
int create_pen_hsv(float h, float s, float v) override;
|
||||
void set_pixel(const Point &p) override;
|
||||
|
|
|
@ -18,10 +18,6 @@ namespace pimoroni {
|
|||
color = std::max(r, std::max(g, b)) >> 4;
|
||||
}
|
||||
|
||||
void PicoGraphics_Pen1Bit::set_thickness(uint t) {
|
||||
thickness = t;
|
||||
}
|
||||
|
||||
void PicoGraphics_Pen1Bit::set_pixel(const Point &p) {
|
||||
// pointer to byte in framebuffer that contains this pixel
|
||||
uint8_t *buf = (uint8_t *)frame_buffer;
|
||||
|
|
|
@ -18,10 +18,6 @@ namespace pimoroni {
|
|||
color = std::max(r, std::max(g, b));
|
||||
}
|
||||
|
||||
void PicoGraphics_Pen1BitY::set_thickness(uint t) {
|
||||
thickness = t;
|
||||
}
|
||||
|
||||
void PicoGraphics_Pen1BitY::set_pixel(const Point &p) {
|
||||
// pointer to byte in framebuffer that contains this pixel
|
||||
uint8_t *buf = (uint8_t *)frame_buffer;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
- [Random Joke](#random-joke)
|
||||
- [SD Card Test](#sd-card-test)
|
||||
- [XKCD Daily](#xkcd-daily)
|
||||
- [Dithering](#dithering)
|
||||
|
||||
## PicoGraphics
|
||||
|
||||
|
@ -91,3 +92,8 @@ The webcomic is rendered "offline" by our feed2image service since xkcd.com requ
|
|||
|
||||
For bugs/contributions see: https://github.com/pimoroni/feed2image
|
||||
|
||||
### Dithering
|
||||
[inky_frame_dithering.py](inky_frame_dithering.py)
|
||||
|
||||
A basic example showing automatic dithering in action, as PicoGraphics tries to use Inky Frame's limited colour palette to match arbitrary colours.
|
||||
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
# An offline image gallery that switches between five jpg images
|
||||
# Copy them into your Pico's flash using Thonny.
|
||||
# If you want to use your own images they must be 600 x 448 pixels or smaller
|
||||
# and saved as *non-progressive* jpgs
|
||||
# Copy them into the root of your Pico's flash using Thonny.
|
||||
|
||||
from pimoroni import ShiftRegister
|
||||
from picographics import PicoGraphics, DISPLAY_INKY_FRAME
|
||||
# If you want to use your own images they must be the screen dimensions (or smaller)
|
||||
# and saved as *non-progressive* jpgs.
|
||||
|
||||
# Make sure to uncomment the correct size for your display!
|
||||
|
||||
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
|
||||
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
|
||||
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_7 as DISPLAY # 7.3"
|
||||
from machine import Pin
|
||||
import jpegdec
|
||||
from pimoroni import ShiftRegister
|
||||
|
||||
# you can change your file names here
|
||||
IMAGE_A = "jwst1.jpg"
|
||||
|
@ -16,7 +21,7 @@ IMAGE_D = "jwst4.jpg"
|
|||
IMAGE_E = "jwst5.jpg"
|
||||
|
||||
# set up the display
|
||||
display = PicoGraphics(display=DISPLAY_INKY_FRAME)
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
# Inky Frame uses a shift register to read the buttons
|
||||
SR_CLOCK = 8
|
||||
|
@ -42,14 +47,7 @@ hold_vsys_en_pin = Pin(HOLD_VSYS_EN_PIN, Pin.OUT)
|
|||
hold_vsys_en_pin.value(True)
|
||||
|
||||
# Create a new JPEG decoder for our PicoGraphics
|
||||
j = jpegdec.JPEG(display)
|
||||
|
||||
# setup
|
||||
activity_led.on()
|
||||
# update the image on Inky every time it's powered up
|
||||
# comment these lines out if running on battery power
|
||||
# button_a_led.on()
|
||||
# display_image(IMAGE_A)
|
||||
j = jpegdec.JPEG(graphics)
|
||||
|
||||
|
||||
def display_image(filename):
|
||||
|
@ -61,9 +59,16 @@ def display_image(filename):
|
|||
j.decode(0, 0, jpegdec.JPEG_SCALE_FULL)
|
||||
|
||||
# Display the result
|
||||
display.update()
|
||||
graphics.update()
|
||||
|
||||
|
||||
# setup
|
||||
activity_led.on()
|
||||
# update the image on Inky every time it's powered up
|
||||
# comment these lines out if running on battery power
|
||||
# button_a_led.on()
|
||||
# display_image(IMAGE_A)
|
||||
|
||||
while True:
|
||||
button_a_led.off()
|
||||
button_b_led.off()
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
# An offline image gallery that switches between five jpg images
|
||||
# on your SD card (copy them across by plugging your SD into a computer).
|
||||
# If you want to use your own images they must be 600 x 448 pixels or smaller
|
||||
|
||||
# If you want to use your own images they must be the screen dimensions (or smaller)
|
||||
# and saved as *non-progressive* jpgs
|
||||
|
||||
# Make sure to uncomment the correct size for your display!
|
||||
|
||||
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
|
||||
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
|
||||
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_7 as DISPLAY # 7.3"
|
||||
from pimoroni import ShiftRegister
|
||||
from picographics import PicoGraphics, DISPLAY_INKY_FRAME
|
||||
from machine import Pin, SPI
|
||||
import jpegdec
|
||||
import sdcard
|
||||
import uos
|
||||
|
||||
|
||||
# you can change your file names here
|
||||
IMAGE_A = "sd/jwst1.jpg"
|
||||
IMAGE_B = "sd/jwst2.jpg"
|
||||
|
@ -18,7 +24,7 @@ IMAGE_D = "sd/jwst4.jpg"
|
|||
IMAGE_E = "sd/jwst5.jpg"
|
||||
|
||||
# set up the display
|
||||
display = PicoGraphics(display=DISPLAY_INKY_FRAME)
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
# Inky Frame uses a shift register to read the buttons
|
||||
SR_CLOCK = 8
|
||||
|
@ -49,14 +55,7 @@ hold_vsys_en_pin = Pin(HOLD_VSYS_EN_PIN, Pin.OUT)
|
|||
hold_vsys_en_pin.value(True)
|
||||
|
||||
# Create a new JPEG decoder for our PicoGraphics
|
||||
j = jpegdec.JPEG(display)
|
||||
|
||||
# setup
|
||||
activity_led.on()
|
||||
# update the image on Inky every time it's powered up
|
||||
# comment these lines out if running on battery power
|
||||
# button_a_led.on()
|
||||
# display_image(IMAGE_A)
|
||||
j = jpegdec.JPEG(graphics)
|
||||
|
||||
|
||||
def display_image(filename):
|
||||
|
@ -68,9 +67,16 @@ def display_image(filename):
|
|||
j.decode(0, 0, jpegdec.JPEG_SCALE_FULL)
|
||||
|
||||
# Display the result
|
||||
display.update()
|
||||
graphics.update()
|
||||
|
||||
|
||||
# setup
|
||||
activity_led.on()
|
||||
# update the image on Inky every time it's powered up
|
||||
# comment these lines out if running on battery power
|
||||
# button_a_led.on()
|
||||
# display_image(IMAGE_A)
|
||||
|
||||
while True:
|
||||
button_a_led.off()
|
||||
button_b_led.off()
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
|
||||
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
|
||||
from picographics import PicoGraphics, DISPLAY_INKY_FRAME_7 as DISPLAY # 7.3"
|
||||
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
|
||||
WIDTH, HEIGHT = graphics.get_bounds()
|
||||
|
||||
graphics.set_pen(1)
|
||||
graphics.clear()
|
||||
|
||||
w = int(WIDTH / 8)
|
||||
|
||||
# Solid Colours
|
||||
|
||||
for p in range(8):
|
||||
graphics.set_pen(p)
|
||||
graphics.rectangle(w * p, 0, w, 50)
|
||||
|
||||
# "Greydient"
|
||||
|
||||
for x in range(WIDTH):
|
||||
g = int(x / float(WIDTH) * 255)
|
||||
graphics.set_pen(graphics.create_pen(g, g, g))
|
||||
for y in range(30):
|
||||
graphics.pixel(x, 60 + y)
|
||||
|
||||
# Rainbow Gradient
|
||||
|
||||
for x in range(WIDTH):
|
||||
h = x / float(WIDTH)
|
||||
graphics.set_pen(graphics.create_pen_hsv(h, 1.0, 1.0))
|
||||
for y in range(100):
|
||||
graphics.pixel(x, 100 + y)
|
||||
|
||||
# Block Colours & Text
|
||||
|
||||
graphics.set_pen(graphics.create_pen(128, 128, 0))
|
||||
graphics.rectangle(0, 210, 200, 100)
|
||||
graphics.set_pen(graphics.create_pen(200, 200, 200))
|
||||
graphics.text("Hello", 10, 220)
|
||||
graphics.text("Hello", 10, 240, scale=4.0)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(0, 128, 128))
|
||||
graphics.rectangle(200, 210, 200, 100)
|
||||
graphics.set_pen(graphics.create_pen(200, 200, 200))
|
||||
graphics.text("Hello", 210, 220)
|
||||
graphics.text("Hello", 210, 240, scale=4.0)
|
||||
|
||||
graphics.set_pen(graphics.create_pen(128, 0, 128))
|
||||
graphics.rectangle(400, 210, 200, 100)
|
||||
graphics.set_pen(graphics.create_pen(200, 200, 200))
|
||||
graphics.text("Hello", 410, 220)
|
||||
graphics.text("Hello", 410, 240, scale=4.0)
|
||||
|
||||
# Red, Green and Blue gradients
|
||||
|
||||
for x in range(WIDTH):
|
||||
g = int(x / float(WIDTH) * 255)
|
||||
for y in range(20):
|
||||
graphics.set_pen(graphics.create_pen(g, 0, 0))
|
||||
graphics.pixel(x, 320 + y)
|
||||
graphics.set_pen(graphics.create_pen(0, g, 0))
|
||||
graphics.pixel(x, 350 + y)
|
||||
graphics.set_pen(graphics.create_pen(0, 0, g))
|
||||
graphics.pixel(x, 380 + y)
|
||||
|
||||
graphics.update()
|
|
@ -0,0 +1,82 @@
|
|||
import time
|
||||
import uasyncio
|
||||
import WIFI_CONFIG
|
||||
import inky_frame
|
||||
from network_manager import NetworkManager
|
||||
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
|
||||
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
|
||||
from picographics import PicoGraphics, DISPLAY_INKY_FRAME_7 as DISPLAY # 7.3"
|
||||
|
||||
# Sync the Inky (always on) RTC to the Pico W so that "time.localtime()" works.
|
||||
inky_frame.pcf_to_pico_rtc()
|
||||
|
||||
# Avoid running code unless we've been triggered by an event
|
||||
# Keeps this example from locking up Thonny when we want to tweak the code
|
||||
if inky_frame.woken_by_rtc() or inky_frame.woken_by_button():
|
||||
graphics = PicoGraphics(DISPLAY)
|
||||
WIDTH, HEIGHT = graphics.get_bounds()
|
||||
|
||||
graphics.set_pen(1)
|
||||
graphics.clear()
|
||||
|
||||
# Look, just because this is an RTC demo,
|
||||
# doesn't mean we can't make it rainbow.
|
||||
for x in range(WIDTH):
|
||||
h = x / WIDTH
|
||||
p = graphics.create_pen_hsv(h, 1.0, 1.0)
|
||||
graphics.set_pen(p)
|
||||
graphics.line(x, 0, x, HEIGHT)
|
||||
|
||||
graphics.set_pen(0)
|
||||
graphics.rectangle(0, 0, WIDTH, 14)
|
||||
graphics.set_pen(1)
|
||||
graphics.text("Inky Frame", 1, 0)
|
||||
graphics.set_pen(0)
|
||||
|
||||
def status_handler(mode, status, ip):
|
||||
print(mode, status, ip)
|
||||
|
||||
year, month, day, hour, minute, second, dow, _ = time.localtime()
|
||||
|
||||
# Connect to the network and get the time if it's not set
|
||||
if year < 2023:
|
||||
connected = False
|
||||
network_manager = NetworkManager(WIFI_CONFIG.COUNTRY, status_handler=status_handler, client_timeout=60)
|
||||
|
||||
t_start = time.time()
|
||||
try:
|
||||
uasyncio.get_event_loop().run_until_complete(network_manager.client(WIFI_CONFIG.SSID, WIFI_CONFIG.PSK))
|
||||
connected = True
|
||||
except RuntimeError:
|
||||
pass
|
||||
t_end = time.time()
|
||||
|
||||
if connected:
|
||||
inky_frame.set_time()
|
||||
|
||||
graphics.text("Setting time from network...", 0, 40)
|
||||
graphics.text(f"Connection took: {t_end-t_start}s", 0, 60)
|
||||
else:
|
||||
graphics.text("Failed to connect!", 0, 40)
|
||||
|
||||
# Display the date and time
|
||||
year, month, day, hour, minute, second, dow, _ = time.localtime()
|
||||
|
||||
date_time = f"{year:04}/{month:02}/{day:02} {hour:02}:{minute:02}:{second:02}"
|
||||
|
||||
graphics.set_font("bitmap8")
|
||||
|
||||
text_scale = 8 if WIDTH == 800 else 6
|
||||
text_height = 8 * text_scale
|
||||
|
||||
offset_left = (WIDTH - graphics.measure_text(date_time, scale=text_scale)) // 2
|
||||
offset_top = (HEIGHT - text_height) // 2
|
||||
|
||||
graphics.set_pen(graphics.create_pen(50, 50, 50))
|
||||
graphics.text(date_time, offset_left + 2, offset_top + 2, scale=text_scale)
|
||||
graphics.set_pen(1)
|
||||
graphics.text(date_time, offset_left, offset_top, scale=text_scale)
|
||||
|
||||
graphics.update()
|
||||
|
||||
inky_frame.sleep_for(2)
|
|
@ -25,6 +25,7 @@ Pico Graphics replaces the individual drivers for displays- if you're been using
|
|||
- [Get Bounds](#get-bounds)
|
||||
- [Text](#text)
|
||||
- [Changing The Font](#changing-the-font)
|
||||
- [Changing The Thickness](#changing-the-thickness)
|
||||
- [Drawing Text](#drawing-text)
|
||||
- [Basic Shapes](#basic-shapes)
|
||||
- [Line](#line)
|
||||
|
@ -311,6 +312,17 @@ These are aligned horizontally (x) to their left edge, but vertically (y) to the
|
|||
* `serif_italic`
|
||||
* `serif`
|
||||
|
||||
#### Changing The Thickness
|
||||
|
||||
Vector (Hershey) fonts are drawn with individual lines. By default these are 1px thick, making for very thin and typically illegible text.
|
||||
|
||||
To change the thickness of lines used for Vector fonts, use the `set_thickness` method:
|
||||
|
||||
```python
|
||||
display.set_thickness(n)
|
||||
```
|
||||
|
||||
Drawing thick text involves setting a lot more pixels and may slow your drawing down considerably. Be careful how and where you use this.
|
||||
|
||||
#### Drawing Text
|
||||
|
||||
|
@ -371,7 +383,13 @@ To draw a straight line at any angle between two specified points:
|
|||
display.line(x1, y1, x2, y2)
|
||||
```
|
||||
|
||||
The X1/Y1 and X2/Y2 coordinates describe the start and end of the line respectively.
|
||||
The X1/Y1 and X2/Y2 coordinates describe the start and end of the line respectively.
|
||||
|
||||
If you need a thicker line, for an outline or UI elements you can supply a fifth parameter - thickness - like so:
|
||||
|
||||
```python
|
||||
display.line(x1, y1, x2, y2, thickness)
|
||||
```
|
||||
|
||||
#### Circle
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_text_obj, 1, ModPicoGraphics_text);
|
|||
MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_measure_text_obj, 1, ModPicoGraphics_measure_text);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_polygon_obj, 2, ModPicoGraphics_polygon);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_triangle_obj, 7, 7, ModPicoGraphics_triangle);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_line_obj, 5, 5, ModPicoGraphics_line);
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_line_obj, 5, 6, ModPicoGraphics_line);
|
||||
|
||||
// Sprites
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_spritesheet_obj, ModPicoGraphics_set_spritesheet);
|
||||
|
|
|
@ -503,6 +503,10 @@ mp_obj_t ModPicoGraphics_set_font(mp_obj_t self_in, mp_obj_t font) {
|
|||
mp_int_t ModPicoGraphics_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
|
||||
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t);
|
||||
(void)flags;
|
||||
if((PicoGraphicsPenType)self->graphics->pen_type == PEN_INKY7) {
|
||||
// Special case for Inky Frame 7.3" which uses a PSRAM framebuffer not accessible as a raw buffer
|
||||
mp_raise_ValueError("No local framebuffer.");
|
||||
}
|
||||
bufinfo->buf = self->graphics->frame_buffer;
|
||||
bufinfo->len = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, self->graphics->bounds.w, self->graphics->bounds.h);
|
||||
bufinfo->typecode = 'B';
|
||||
|
@ -512,6 +516,11 @@ mp_int_t ModPicoGraphics_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *buf
|
|||
mp_obj_t ModPicoGraphics_set_framebuffer(mp_obj_t self_in, mp_obj_t framebuffer) {
|
||||
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t);
|
||||
|
||||
if((PicoGraphicsPenType)self->graphics->pen_type == PEN_INKY7) {
|
||||
// Special case for Inky Frame 7.3" which uses a PSRAM framebuffer not accessible as a raw buffer
|
||||
mp_raise_ValueError("No local framebuffer.");
|
||||
}
|
||||
|
||||
if (framebuffer == mp_const_none) {
|
||||
m_del(uint8_t, self->buffer, self->graphics->bounds.w * self->graphics->bounds.h);
|
||||
self->buffer = nullptr;
|
||||
|
@ -749,10 +758,6 @@ mp_obj_t ModPicoGraphics_create_pen_hsv(size_t n_args, const mp_obj_t *args) {
|
|||
mp_obj_t ModPicoGraphics_set_thickness(mp_obj_t self_in, mp_obj_t pen) {
|
||||
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t);
|
||||
|
||||
if(self->graphics->pen_type != PicoGraphics::PEN_1BIT) {
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Thickness not supported!"));
|
||||
}
|
||||
|
||||
self->graphics->set_thickness(mp_obj_get_int(pen));
|
||||
|
||||
return mp_const_none;
|
||||
|
@ -1034,16 +1039,27 @@ mp_obj_t ModPicoGraphics_triangle(size_t n_args, const mp_obj_t *args) {
|
|||
}
|
||||
|
||||
mp_obj_t ModPicoGraphics_line(size_t n_args, const mp_obj_t *args) {
|
||||
enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2 };
|
||||
enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_thickness };
|
||||
|
||||
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t);
|
||||
|
||||
self->graphics->line(
|
||||
{mp_obj_get_int(args[ARG_x1]),
|
||||
mp_obj_get_int(args[ARG_y1])},
|
||||
{mp_obj_get_int(args[ARG_x2]),
|
||||
mp_obj_get_int(args[ARG_y2])}
|
||||
);
|
||||
if(n_args == 5) {
|
||||
self->graphics->line(
|
||||
{mp_obj_get_int(args[ARG_x1]),
|
||||
mp_obj_get_int(args[ARG_y1])},
|
||||
{mp_obj_get_int(args[ARG_x2]),
|
||||
mp_obj_get_int(args[ARG_y2])}
|
||||
);
|
||||
}
|
||||
else if(n_args == 6) {
|
||||
self->graphics->thick_line(
|
||||
{mp_obj_get_int(args[ARG_x1]),
|
||||
mp_obj_get_int(args[ARG_y1])},
|
||||
{mp_obj_get_int(args[ARG_x2]),
|
||||
mp_obj_get_int(args[ARG_y2])},
|
||||
mp_obj_get_int(args[ARG_thickness])
|
||||
);
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
# Inky Frame (MicroPython) <!-- omit in toc -->
|
||||
|
||||
Most of your interaction with Inky Frame will be via the PicoGraphics library, but we have created an `inky_frame` module to help you read the onboard A, B, C, D and E buttons and control the LEDs.
|
||||
|
||||
- [Pico Graphics](#pico-graphics)
|
||||
- [Colour \& Dithering](#colour--dithering)
|
||||
- [Images \& JPEGs](#images--jpegs)
|
||||
- [Buttons \& LEDs](#buttons--leds)
|
||||
- [Status LEDs](#status-leds)
|
||||
- [Battery Power \& RTC](#battery-power--rtc)
|
||||
- [Function Reference](#function-reference)
|
||||
- [Wakeup States](#wakeup-states)
|
||||
- [RTC](#rtc)
|
||||
- [Other](#other)
|
||||
- [RAM Usage](#ram-usage)
|
||||
- [Accessing The Framebuffer](#accessing-the-framebuffer)
|
||||
|
||||
|
||||
## Pico Graphics
|
||||
|
||||
You can draw on Inky Frame using our PicoGraphics display library.
|
||||
- [PicoGraphics MicroPython function reference](../../modules/picographics)
|
||||
|
||||
### Colour & Dithering
|
||||
|
||||
Inky Frame supports eight native colours, those that it's physically capable of displaying. These are:
|
||||
|
||||
* `BLACK` = 0
|
||||
* `WHITE` = 1
|
||||
* `GREEN` = 2
|
||||
* `BLUE` = 3
|
||||
* `RED` = 4
|
||||
* `YELLOW` = 5
|
||||
* `ORANGE` = 6
|
||||
* `TAUPE` = 7 (only used for dither on 4.0" and 5.7" displays †)
|
||||
|
||||
:info: † - the "taupe" colour (also known as clear) is the colour used to reset the display to a default, clear state. We misuse this as an extra colour on 4.0" and 5.7", but the 7.3" display clear colour is a sort of muddy, greenish gradient that's not consistent enough. You can stil use it, by all means, but it wont be considered for dithering.
|
||||
|
||||
These colours are available as constants in the `inky_frame` module so you don't have to remember which number corresponds to which, eg:
|
||||
|
||||
```python
|
||||
display.set_pen(inky_frame.YELLOW)
|
||||
```
|
||||
|
||||
You can use colours outside of these eight by using `create_pen(r, g, b)` or `create_pen_hsv(h, s, v)`. eg:
|
||||
|
||||
```python
|
||||
my_colour = display.create_pen(255, 128, 0)
|
||||
display.set_pen(my_colour)
|
||||
```
|
||||
|
||||
PicoGraphics will do its best to match your colour choice by mixing the available colours with a dither pattern. This works best on large areas of colour such as backgrounds, big UI elements or chunky text. See the `inky_frame_dithering.py` example for a demonstration.
|
||||
|
||||
:info: Due to the lack of light blue or pink native colours, Inky Frame isn't very good at reproducing pink, purple, violet or light blue, cyan and teal.
|
||||
|
||||
|
||||
### Images & JPEGs
|
||||
|
||||
You can use the `jpegdec` (JPEG decoding) module to display JPEG files on Inky, but you should be aware of some caveats:
|
||||
|
||||
1. JPEGs are compressed and lossy, a small JPEG displayed on Inky might show random specks of colour where you don't expect them as PicoGraphics tries its best to dither noise to the nearest available colours.
|
||||
2. JPEGs are, by default, dithered to the 7 (or 8 on 4.0 and 5.7) available colours. This uses "ordered dither," which is fast, but not very pretty.
|
||||
3. You can turn off dithering with `jpeg.decode(dither=False)` for a posterised effect.
|
||||
|
||||
## Buttons & LEDs
|
||||
|
||||
Inky Frame has five user buttons labelled A to E, with LEDs. The buttons are connected via a shift-register and available in the `inky_frame` module as `button_a`, `button_b`, `button_c`, `button_d` and `button_e`.
|
||||
|
||||
Each button has some convenient methods for checking if it's pressed:
|
||||
|
||||
* `raw()` - get the current state of the button with no debounce (returns `True` the first time it's called if the corresponding button)
|
||||
* `read()` - read the current button state, observing debounce (50ms)
|
||||
|
||||
Additionally each button has some methods for controlling its associated LED:
|
||||
|
||||
* `led_on()` - turn the LED on
|
||||
* `led_off()` - turn the LED off
|
||||
* `led_toggle()` - toggle the LED
|
||||
* `led_brightness(0.5)` - set the LED brightness (from 0.0 to 1.0)
|
||||
|
||||
### Status LEDs
|
||||
|
||||
In addition to the button LEDs there are two status LEDs, `busy` and `wifi`, which are available as:
|
||||
|
||||
* `led_busy` - connected to pin `LED_BUSY`
|
||||
* `led_wifi` - connected to pin `LED_WIFI`
|
||||
|
||||
These are instances of the `pimoroni.PWMLED` class, and have the following methods:
|
||||
|
||||
* `on()` - turn the LED on
|
||||
* `off()` - turn the LED off
|
||||
* `toggle()` - toggle the LED
|
||||
* `brightness(0.5)` - set the LED brightness (from 0.0 to 1.0)
|
||||
|
||||
:info: `toggle()` is provided for compatibility with the `Pin()` class. It's a little odd, since it changes the LED brightness to `1.0 - current_brightness`.
|
||||
|
||||
## Battery Power & RTC
|
||||
|
||||
When running on battery power, Inky Frame's buttons cause it to wake from a power-off state and start running your code. Additionally an onboard PCF85063A real-time clock runs continuously from battery and can wake up your Frame automatically.
|
||||
|
||||
The Inky Frame library includes a number of convenience functions to set the clock and sleep your device. If you want accurate time you must check if the clock is set - usually a simple check against the year works - connect to a network, and set via NTP (Network Time Protocol).
|
||||
|
||||
Inky Frame technically has *two* real-time clocks, the external RTC we've added - which remains continuously powered by battery - and the internal RTC of the Pico W. For convenience and compatibility with other code examples we recommend using the external RTC only to set the time on the internal one which makes functions like `time.localtime()` work as you'd expect.
|
||||
|
||||
```python
|
||||
import time
|
||||
import machine
|
||||
import inky_frame
|
||||
|
||||
inky_frame.pcf_to_pico_rtc() # Sync Inky RTC time to Pico's RTC
|
||||
|
||||
year, month, day, dow, hour, minute, second, _ = machine.RTC().datetime()
|
||||
|
||||
if year < 2023:
|
||||
# Connect to network
|
||||
inky_frame.set_time() # Sets both the Inky and Pico RTC
|
||||
|
||||
print(time.localtime())
|
||||
```
|
||||
|
||||
### Function Reference
|
||||
|
||||
#### Wakeup States
|
||||
|
||||
For your convenience these wakeup state functions also check the *current* state of their associated event, this allows you to run code from Thonny with a button held down - for example - to test how your deployed code will behave on battery-
|
||||
|
||||
* `inky_frame.woken_by_rtc()` - Returns `True` if the RTC caused a wakeup, or if the RTC ALARM is currently raised.
|
||||
* `inky_frame.woken_by_button()` - Returns `True` if a button caused a wakeup, or if a button is currently pressed.
|
||||
* `inky_frame.woken_by_ext_trigger()` Returns `True` if the external trigger caused a wakeup, or if the trigger is currently asserted.
|
||||
|
||||
#### RTC
|
||||
|
||||
* `inky_frame.set_time()` - Attempt to run `ntptime.settime()` and set the time on both RTCs
|
||||
* `inky_frame.pcf_to_pico_rtc()` - Sync from Inky's RTC to the Pico W's RTC
|
||||
* `inky_frame.pico_rtc_to_pcf()` - Sync from Pico W's RTC to Inky's RTC (sometime useful since Thonny sets the Pico W RTC automatically)
|
||||
* `inky_frame.sleep_for(minutes)` - Set the RTC alarm for a number of minutes and cut the power. This will completely power off the Pico W, but leave the Inky RTC running to wake it back up.
|
||||
|
||||
:info: You can access all Inky RTC (PCF85063A) functions via `inky_frame.rtc`
|
||||
|
||||
#### Other
|
||||
|
||||
* `inky_frame.turn_off()` - Cut the power to the Pico W (on battery only), only an alarm event or a button press can wake it back up.
|
||||
|
||||
## RAM Usage
|
||||
|
||||
Both Inky 4.0" and 5.7" use only the Pico's onboard RAM. It's quite cozy. The frame buffers are 3-bits-per-pixel, making Inky 4.0"
|
||||
|
||||
On 7.3" we had to add a PSRAM chip to act as the display's framebuffer. Right now it acts exclusively as a framebuffer, but that frees up some Pico RAM to work with so you can do more with that 7.3" panel.
|
||||
|
||||
The Inky frame buffer sizes are as follows:
|
||||
|
||||
* 4.0" - 640x400 - 96,000 bytes.
|
||||
* 5.7" - 600x448 - 100,800 bytes.
|
||||
* 7.3" - 800x480 - 144,000 bytes.
|
||||
|
||||
Since MicroPython on a Pico W has only 166k that would have left *just* 22k on 7.3", instead you get (almost) all 166k to play with since the PicoGraphics instance itself uses only 8544 bytes!
|
||||
|
||||
### Accessing The Framebuffer
|
||||
|
||||
PicoGraphics has undocumented support for accessing its raw framebuffer using `memoryview(graphics)`.
|
||||
|
||||
This is useful for copying raw binary images (effectively valid Inky frame buffers saved to a file) avoiding JPEG compression and so forth. For some dicussion about why and how you might do this, see: https://github.com/pimoroni/pimoroni-pico/issues/681
|
||||
|
||||
:warning: This *does not work* for Inky 7.3, since there is no framebuffer in memory. PicoGraphics will raise a `ValueError: No local framebuffer.` if you try. We aim to fix this with some hardfault handling sorcery.
|
|
@ -1,32 +1,129 @@
|
|||
from pimoroni import ShiftRegister
|
||||
from machine import Pin
|
||||
from pimoroni import ShiftRegister, PWMLED
|
||||
from machine import Pin, I2C, RTC
|
||||
from wakeup import get_shift_state, reset_shift_state
|
||||
from micropython import const
|
||||
import pcf85063a
|
||||
import ntptime
|
||||
import time
|
||||
|
||||
BLACK = const(0)
|
||||
WHITE = const(1)
|
||||
|
||||
SR_CLOCK = 8
|
||||
SR_LATCH = 9
|
||||
SR_OUT = 10
|
||||
GREEN = const(2)
|
||||
BLUE = const(3)
|
||||
RED = const(4)
|
||||
YELLOW = const(5)
|
||||
ORANGE = const(6)
|
||||
TAUPE = const(7)
|
||||
|
||||
LED_A = 11
|
||||
LED_B = 12
|
||||
LED_C = 13
|
||||
LED_D = 14
|
||||
LED_E = 15
|
||||
SR_CLOCK = const(8)
|
||||
SR_LATCH = const(9)
|
||||
SR_OUT = const(10)
|
||||
|
||||
LED_BUSY = 6
|
||||
LED_WIFI = 7
|
||||
LED_A = const(11)
|
||||
LED_B = const(12)
|
||||
LED_C = const(13)
|
||||
LED_D = const(14)
|
||||
LED_E = const(15)
|
||||
|
||||
LED_BUSY = const(6)
|
||||
LED_WIFI = const(7)
|
||||
|
||||
HOLD_VSYS_EN = const(2)
|
||||
|
||||
RTC_ALARM = const(2)
|
||||
EXTERNAL_TRIGGER = const(1)
|
||||
EINK_BUSY = const(0)
|
||||
|
||||
SHIFT_STATE = get_shift_state()
|
||||
|
||||
reset_shift_state()
|
||||
|
||||
i2c = I2C(0)
|
||||
rtc = pcf85063a.PCF85063A(i2c)
|
||||
i2c.writeto_mem(0x51, 0x00, b'\x00') # ensure rtc is running (this should be default?)
|
||||
rtc.enable_timer_interrupt(False)
|
||||
|
||||
vsys = Pin(HOLD_VSYS_EN)
|
||||
vsys.on()
|
||||
|
||||
|
||||
def woken_by_rtc():
|
||||
mask = (1 << RTC_ALARM)
|
||||
return bool(sr.read() & mask) or bool(SHIFT_STATE & mask)
|
||||
|
||||
|
||||
def woken_by_ext_trigger():
|
||||
mask = (1 << EXTERNAL_TRIGGER)
|
||||
return bool(sr.read() & mask) or bool(SHIFT_STATE & mask)
|
||||
|
||||
|
||||
def woken_by_button():
|
||||
return bool(sr.read() & 0b11111000) or bool(SHIFT_STATE & 0b11111000)
|
||||
|
||||
|
||||
def pico_rtc_to_pcf():
|
||||
# Set the PCF85063A to the time stored by Pico W's RTC
|
||||
year, month, day, dow, hour, minute, second, _ = RTC().datetime()
|
||||
rtc.datetime((year, month, day, hour, minute, second, dow))
|
||||
|
||||
|
||||
def pcf_to_pico_rtc():
|
||||
# Set Pico W's RTC to the time stored by the PCF85063A
|
||||
t = rtc.datetime()
|
||||
# BUG ERRNO 22, EINVAL, when date read from RTC is invalid for the Pico's RTC.
|
||||
try:
|
||||
RTC().datetime((t[0], t[1], t[2], t[6], t[3], t[4], t[5], 0))
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def sleep_for(minutes):
|
||||
year, month, day, hour, minute, second, dow = rtc.datetime()
|
||||
|
||||
# if the time is very close to the end of the minute, advance to the next minute
|
||||
# this aims to fix the edge case where the board goes to sleep right as the RTC triggers, thus never waking up
|
||||
if second >= 55:
|
||||
minute += 1
|
||||
|
||||
minute += minutes
|
||||
|
||||
while minute >= 60:
|
||||
minute -= 60
|
||||
hour += 1
|
||||
|
||||
if hour >= 24:
|
||||
hour -= 24
|
||||
|
||||
rtc.clear_alarm_flag()
|
||||
rtc.set_alarm(0, minute, hour)
|
||||
rtc.enable_alarm_interrupt(True)
|
||||
|
||||
turn_off()
|
||||
|
||||
# Simulate sleep while on USB power
|
||||
while minutes > 0:
|
||||
time.sleep(60)
|
||||
minutes -= 1
|
||||
|
||||
|
||||
def turn_off():
|
||||
time.sleep(0.1)
|
||||
vsys.off()
|
||||
|
||||
|
||||
def set_time():
|
||||
# Set both Pico W's RTC and PCF85063A to NTP time
|
||||
ntptime.settime()
|
||||
pico_rtc_to_pcf()
|
||||
|
||||
|
||||
class Button:
|
||||
def __init__(self, sr, idx, led, debounce=50):
|
||||
self.sr = sr
|
||||
self.startup_state = bool(SHIFT_STATE & (1 << idx))
|
||||
self.led = Pin(led, Pin.OUT) # LEDs are just regular IOs
|
||||
self.led = PWMLED(led)
|
||||
self.led.off()
|
||||
self._idx = idx
|
||||
self._debounce_time = debounce
|
||||
|
@ -39,6 +136,12 @@ class Button:
|
|||
def led_off(self):
|
||||
self.led.off()
|
||||
|
||||
def led_brightness(self, brightness):
|
||||
self.led.brightness(brightness)
|
||||
|
||||
def led_toggle(self):
|
||||
self.led.toggle()
|
||||
|
||||
def read(self):
|
||||
if self.startup_state:
|
||||
self.startup_state = False
|
||||
|
@ -70,5 +173,5 @@ button_c = Button(sr, 5, LED_C)
|
|||
button_d = Button(sr, 4, LED_D)
|
||||
button_e = Button(sr, 3, LED_E)
|
||||
|
||||
led_busy = Pin(LED_BUSY, Pin.OUT)
|
||||
led_wifi = Pin(LED_WIFI, Pin.OUT)
|
||||
led_busy = PWMLED(LED_BUSY)
|
||||
led_wifi = PWMLED(LED_WIFI)
|
||||
|
|
|
@ -224,3 +224,30 @@ class ShiftRegister:
|
|||
|
||||
def is_set(self, mask):
|
||||
return self.read() & mask == mask
|
||||
|
||||
|
||||
# A basic wrapper for PWM with regular on/off and toggle functions from Pin
|
||||
# Intended to be used for driving LEDs with brightness control & compatibility with Pin
|
||||
class PWMLED:
|
||||
def __init__(self, pin, invert=False):
|
||||
self._invert = invert
|
||||
self._led = PWM(Pin(pin, Pin.OUT))
|
||||
self._led.freq(1000)
|
||||
self._brightness = 0
|
||||
self.brightness(0)
|
||||
|
||||
def brightness(self, brightness):
|
||||
brightness = min(1.0, max(0.0, brightness))
|
||||
self._brightness = brightness
|
||||
if self._invert:
|
||||
brightness = 1.0 - brightness
|
||||
self._led.duty_u16(int(65535 * brightness))
|
||||
|
||||
def on(self):
|
||||
self.brightness(1)
|
||||
|
||||
def off(self):
|
||||
self.brightness(0)
|
||||
|
||||
def toggle(self):
|
||||
self.brightness(1 - self._brightness)
|
||||
|
|
Ładowanie…
Reference in New Issue