Draw to different linux framebuffers with Python?

606 views Asked by At

I would like to draw on a non-standard linux frame buffer with python. I have a Odroid C4 SBC running Ubuntu, with a 3.2 inch LCD screen hat. The LCD is 320x240 16-bit color. I do most of my interaction with a monitor connected through hdmi. The screen uses /dev/fb4 as it's framebuffer, and my monitor is on /dev/fb0. I can directly write to /dev/fb4 in different ways and and stuff shows up on the screen. I'd like to use python to draw simple text and images on the small screen but I'm not sure what modules to use and how to point them at /dev/fb4.

I have found various modules that let you draw to the main framebuffer, like pygame, but I haven't been able to find instructions on how to point them at a different framebuffer.

2

There are 2 answers

0
user3435121 On

Here are some code snippets to help you create your own code.

import mmap # shared memory
import sys  # exit

fbN = 'fb4'   # device created by the device driver

# get width and height
f = open( f"/sys/class/graphics/{fbN}/virtual_size", "r")
wh = f.read()
wa,ha = xy.split( ',')
w = int( wa) # width
h = int( ha) # height
f.close

# get bits per pixel
f = open( f"/sys/class/graphics/{fbN}/bits_per_pixel", "r")
self.bpp = int( f.read())
if not self.bpp in (16,32):
  print( "Unsupported bpp")
  sys.exit()
f.close()

# open framebuffer and map it onto a python bytearray
  fbdev = open( f"/dev/{fbN}", mode='r+b') # open R/W
  fb = mmap.mmap( fbdev.fileno(), w * h * bpp//8, mmap.MAP_SHARED, mmap.PROT_WRITE|mmap.PROT_READ)

# example: write a pixel at position x,y
# the following code support 2 bpp: 32 bits (RGBA) and 16 bits (packed RGB)
x,y = 5,13
if bpp == 32:
  r,g,b,a = 40,80,120,80 # arbitrary color
  p = bytearray.fromhex( '%02x %02x %02x %02x' % ( b,g,r,a))
else:
  r,g,b,a = 10,20,30,1 # arbitrary color
  m = r<<12 + g<<5 + b # pack colors
  p = bytearray.fromhex( '%02x %02x' % ( m>>8, m&0xFF))
pos = (( y * w  + x) * len(p))
fb[pos:pos+len(p)] = p[:] # draw pixel

It's pretty easy to program your own text-to-pixel routine if you use a monospaced bitmapped font (look into /usr/share/consolefonts/). psf file format is documented in https://en.wikipedia.org/wiki/PC_Screen_Font

Note: writing to a framebuffer is a restricted operation. You must add your username to 'video' group.
If you need more info, please detail your question.
Have fun!

0
yingshao xo On

I think I have to give you a more clear python code:

import os
import sys  # for exit
import mmap # shared memory

frame_buffer_number = 'fb0'   # device created by the device driver

# get width and height
f = open(f"/sys/class/graphics/{frame_buffer_number}/virtual_size", "r")
width_and_height = f.read()
width_string, height_string = width_and_height.split(',')
width = int(width_string) # width
height = int(height_string) # height
f.close

# get bits per pixel
f = open(f"/sys/class/graphics/{frame_buffer_number}/bits_per_pixel", "r")
bpp = int(f.read())
if not bpp in (16, 32):
    print("Unsupported bpp")
    sys.exit()
f.close()

# open framebuffer and map it onto a python bytearray
frame_buffer_device = open(f"/dev/{frame_buffer_number}", mode='r+b') # open R/W
frame_buffer_memory_map = mmap.mmap(frame_buffer_device.fileno(), width * height * bpp//8, mmap.MAP_SHARED, mmap.PROT_WRITE | mmap.PROT_READ)

color = 255
frame_buffer_memory_map.write(color.to_bytes(1, byteorder='little') * width * height * 3)

"""
# Copy the image pixel data to the framebuffer
for y in range(height):
    for x in range(width):
        r, g, b = 255, 255, 255

        # Calculate the offset in framebuffer memory
        offset = (y * width + x) * 3

        # Write the pixel data to the framebuffer mmap
        frame_buffer_memory_map[offset + 0] = r
        frame_buffer_memory_map[offset + 1] = g
        frame_buffer_memory_map[offset + 2] = b
"""

# Close the framebuffer device and mmap
frame_buffer_memory_map.close()
frame_buffer_device.close()