3 min read

python3脚本输出文本内容到/dev/fb0

在终端/dev/fb0每秒显示当前时间。

#隐藏控制台光标

# 开启
echo 1 > /sys/class/graphics/fbcon/cursor_blink
# 关闭
echo 0 > /sys/class/graphics/fbcon/cursor_blink

#text2fb0.py

import time
import datetime
import pytz
from PIL import Image, ImageDraw, ImageFont
import numpy

#pip3 install pillow pytz numpy

def get_time():
    # 获取当前时间
    tz = pytz.timezone('Asia/Shanghai')
    t = datetime.datetime.now(tz)
    return t

def get_time_str():
    # 获取当前时间字符串
    t = get_time()
    return t.strftime('%Y-%m-%d %H:%M:%S')

def draw_text_on_framebuffer(text):
    # 创建一个空白图像
    fb = Framebuffer(0)
    image = Image.new('RGB',size=fb.size, color=(0, 0, 0))
    draw = ImageDraw.Draw(image)
    
    # 在图像上绘制文本
    text_position = (10, 10)  # 文本位置
    text_color = (255, 255, 255)  # 白色文本
    font_size = 48  # 字体大小
    font = ImageFont.truetype("/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", font_size)  # 使用指定大小的字体,fc-list查看本机字体
    draw.text(text_position, text, font=font, fill=text_color)
    
    #cx = fb.size[0] // 2
    #cy = fb.size[1] // 2
    #draw.rectangle((cx - 10, cy -10, cx + 10,  cy + 10), "white") 
    fb.show(image)

def _read_and_convert_to_ints(filename):
    with open(filename, "r") as fp:
        content = fp.read()
        tokens = content.strip().split(",")
        return [int(t) for t in tokens if t]


def _converter_argb(image: Image):
    return bytes([x for r, g, b in image.getdata() for x in (255, r, g, b)])


def _converter_rgb565(image: Image):
    return bytes([x for r, g, b in image.getdata()
                for x in ((g & 0x1c) << 3 | (b >> 3), r & 0xf8 | (g >> 3))])


def _converter_1_argb(image: Image):
    return bytes([x for p in image.getdata()
                for x in (255, p, p, p)])


def _converter_1_rgb(image: Image):
    return bytes([x for p in image.getdata()
                for x in (p, p, p)])


def _converter_1_rgb565(image: Image):
    return bytes([(255 if x else 0) for p in image.getdata()
                for x in (p, p)])


def _converter_rgba_rgb565_numpy(image: Image):
    flat = numpy.frombuffer(image.tobytes(), dtype=numpy.uint32)
    # note,  this is assumes little endian byteorder and results in
    # the following packing of an integer:
    # bits 0-7: red, 8-15: green, 16-23: blue, 24-31: alpha
    flat = ((flat & 0xf8) << 8) | ((flat & 0xfc00) >> 5) | ((flat & 0xf80000) >> 19)
    return flat.astype(numpy.uint16).tobytes()


def _converter_no_change(image: Image):
    return image.tobytes()

# anything that does not use numpy is hopelessly slow
_CONVERTER = {
    ("RGBA", 16): _converter_rgba_rgb565_numpy,
    ("RGB", 16): _converter_rgb565,
    ("RGB", 24): _converter_no_change,
    ("RGB", 32): _converter_argb,
    ("RGBA", 32): _converter_no_change,
    # note numpy does not work well with mode="1" images as
    # image.tobytes() loses pixel color info
    ("1", 16): _converter_1_rgb565,
    ("1", 24): _converter_1_rgb,
    ("1", 32): _converter_1_argb,
}


class Framebuffer(object):

    def __init__(self, device_no: int):
        self.path = f"/dev/fb{device_no}"
        config_dir = f"/sys/class/graphics/fb{device_no}"
        self.size = tuple(_read_and_convert_to_ints(
            config_dir + "/virtual_size"))
        self.stride = _read_and_convert_to_ints(config_dir + "/stride")[0]
        self.bits_per_pixel = _read_and_convert_to_ints(
            config_dir + "/bits_per_pixel")[0]
        assert self.stride == self.bits_per_pixel // 8 * self.size[0]

    def __str__(self):
        args = (self.path, self.size, self.stride, self.bits_per_pixel)
        return "%s  size:%s  stride:%s  bits_per_pixel:%s" % args

    # Note: performance is terrible even for medium resolutions
    def show(self, image: Image):
        converter = _CONVERTER[(image.mode, self.bits_per_pixel)]
        assert image.size == self.size
        out = converter(image)
        with open(self.path, "wb") as fp:
            fp.write(out)

    def on(self):
        pass

    def off(self):
        pass

#主程序
if __name__ == "__main__":
    # 循环输出时间到/dev/fb0。每秒一次
    while True:
        draw_text_on_framebuffer(get_time_str())
        time.sleep(1)

#参考地址

https://raspi.muth.org/framebuffer.html