peterhinch / micropython-font-to-py

A Python 3 utility to convert fonts to Python source capable of being frozen as bytecode
MIT License
368 stars 67 forks source link

Use bigger font and draw in framebuffer #25

Closed JPFrancoia closed 4 years ago

JPFrancoia commented 4 years ago

Hi,

I have a 2.7 inches epaper screen, from waveshare. I use the driver that comes from here: https://github.com/mcauser/micropython-waveshare-epaper/blob/master/epaper2in7.py

I use it like this (this is my main.py):

from machine import Pin, SPI
import time
import epaper2in7
import framebuf
import pics

# SPIV on ESP32
sck = Pin(18)
miso = Pin(19)
mosi = Pin(23)
dc = Pin(22)
cs = Pin(5)
rst = Pin(21)
busy = Pin(4)
# spi = SPI(2, baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso)
spi = SPI(2, baudrate=1000000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso)

print("We're in main")

w = 176
h = 264
x = 0
y = 0

e = epaper2in7.EPD(spi, cs, dc, rst, busy)
e.init()

print("Screen ready")

buf = bytearray(w * h // 8)
fb = framebuf.FrameBuffer(buf, w, h, framebuf.MONO_HLSB)
black = 1
white = 0

fb.fill(white)
# fb.text("Hello World", 30, 0, white)

e.display_frame(buf)

fb.blit(pics.fb_thermometer, 10, 2)
fb.blit(pics.fb_humidity, 10, 62)
fb.blit(pics.fb_pressure, 10, 122)
fb.blit(pics.fb_light, 10, 182)

fb.text("23°C", 70, 20, black)

e.display_frame(buf)

print("Image printed")

while True:
    pass

This is just a test example but it's not hard to understand. I have 5 framebuffers: 4 are images (60x60 pixels), 1 is the "main" one. I blit the 4 images onto the main framebuffer. This works.

Now, I would like to display the temperature near the thermometer image. I managed to write some text next to the image, but the font is way too small. I know each character is 8x8 by default, but I'd need something like 20px in height, otherwise the temperature isn't readable.

I would like to know if I can use the Writer class to do that. My device isn't a subclass of framebuffer ), so I was wondering what's the way to go here. Is it possible to simply write the text to a framebuffer, and blit it too on the main framebuffer?

Cheers

peterhinch commented 4 years ago

You'll need to create a device driver for a notional display based on this doc. Such a driver would be a (very simple) subclass of Framebuf with width and height bound variables and a show method (possibly null). This should enable the Writer class to render text to it.

JPFrancoia commented 4 years ago

Ok, so based on this example here: https://github.com/peterhinch/micropython-font-to-py/blob/master/writer/writer_demo.py, this would look a bit like this?


import framebuf
from writer import Writer
import freesans20

class NotionalDisplay(framebuf.FrameBuffer):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def show():
        ...

my_display = NotionalDisplay(176, 264)
wri = Writer(my_display, freesans20)
Writer.set_textpos(ssd, 0, 0)  # verbose = False to suppress console output
wri.printstring('Sunday\n')
wri.printstring('12 Aug 2018\n')
wri.printstring('10.30am')
my_display.show()
peterhinch commented 4 years ago

Yes, but your constructor needs to instantiate the base class like this.

JPFrancoia commented 4 years ago

Ok, thanks for your answer. I'll try when I have a bit of free time and come back to you with the results :)

JPFrancoia commented 4 years ago

Ok, I got it to work with the following code:

from machine import Pin, SPI
import time
import epaper2in7
import framebuf
import pics

from writer import Writer
import freesans20

# SPIV on ESP32
sck = Pin(18)
miso = Pin(19)
mosi = Pin(23)
dc = Pin(22)
cs = Pin(5)
rst = Pin(21)
busy = Pin(4)
# spi = SPI(2, baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso)
spi = SPI(2, baudrate=1000000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso)

print("We're in main")

w = 176
h = 264
x = 0
y = 0

e = epaper2in7.EPD(spi, cs, dc, rst, busy)
e.init()

print("Screen ready")

buf = bytearray(w * h // 8)
fb = framebuf.FrameBuffer(buf, w, h, framebuf.MONO_HLSB)
black = 1
white = 0

fb.fill(white)
# fb.text("Hello World", 30, 0, white)

e.display_frame(buf)

fb.blit(pics.fb_thermometer, 10, 2)
fb.blit(pics.fb_humidity, 10, 62)
fb.blit(pics.fb_pressure, 10, 122)
fb.blit(pics.fb_light, 10, 182)

fb.text("23°C", 70, 20, black)

e.display_frame(buf)

class NotionalDisplay(framebuf.FrameBuffer):
    def __init__(self, width, height, buffer):
        self.width = width
        self.height = height
        self.buffer = buffer
        self.mode = framebuf.MONO_HLSB
        super().__init__(self.buffer, self.width, self.height, self.mode)

    def show(self):
        ...

my_display = NotionalDisplay(176, 264, buf)
wri = Writer(my_display, freesans20)

# verbose = False to suppress console output
Writer.set_textpos(my_display, 0, 0)

wri.printstring('Sunday\n')
print("Sunday printed")
wri.printstring('12 Aug 2018\n')
print("August printed")
wri.printstring('10.30am')
print("time printed")
my_display.show()

e.display_frame(buf)

while True:
    pass

It looks like this:

IMG_20200112_145332

(Note that my code is not clean or optimized, it's just a proof of concept).

I legit thought I'd have to fight more to get it to work.

Thanks for your help.

JPFrancoia commented 4 years ago

Repo for all the code: https://github.com/JPFrancoia/esp32_devkit_waveshare_eink_screen

peterhinch commented 4 years ago

I'm glad you got it working, the display looks good.