Saturday, August 13, 2022

Micropython - Modifying fbconsole.py to support keystroke injection with os.dupterm

 This is something I needed to make an interactive micropython terminal without using the serial connection to interact with it.  E.g.  a separate library handles keyborad scanning using the pi pico pins and when a keypress is registered, a callback function is used to pass the dupterm instance.  Since I want to use fbconsole to display the console on a screen, it made sense to add the needed bits to the fbconsole library to support passing in the keystrokes:


Usage:

In our display module:

display = ssd1306.SSD1306_I2C(128, 64, i2c_list[0], 0x3c)

scr = FBConsole(display)
os.dupterm(scr)

And for a callback function, we can use something like this:

STREAM = oled_fb.scr
keeb.activate(STREAM.inject) # setup the callback

To pass in single or multiple characters, you can use the inject method.  You'll see them appear in the terminal when the function is called:

STREAM.inject('1 + 1')
STREAM.inject(b'\r')


fbconsole.py Modifications:

Within fbconsole.py, we add a couple imports, constants, and define self._data in __init__:

import framebuf
import uio
import os

_MP_STREAM_POLL = const(3)
_MP_STREAM_POLL_RD = const(0x0001)
class FBConsole(uio.IOBase):
def __init__(self, fb, bgcolor=0, fgcolor=-1, width=-1, height=-1, readobj=None):
self._data = bytearray()
self.readobj = readobj

And methods in the class:

def inject(self, data):
self._data += data

if hasattr(os, 'dupterm_notify'):
os.dupterm_notify(None)

def read(self, sz=None):
d = self._data
self._data[:] = b''
return d

def ioctl(self, op, arg):
if op == _MP_STREAM_POLL:
if self._data:
return _MP_STREAM_POLL_RD

And finally modify the readinto method. 

# def readinto(self, buf, nbytes=0):
# if self.readobj != None:
# return self.readobj.readinto(buf, nbytes)
# else:
# return None

def readinto(self, buf):
if not self._data:
return None
b = min(len(buf), len(self._data))
buf[:b] = self._data[:b]
self._data = self._data[b:]
return b

Finally, in addition to regular numbers, letters, and symbols, you'll need a few ascii codes to pass in enter, backspace, etc as well as ctrl+key cobmos:


ASCII_CODES = {
'_bksp': b'\b',
'_entr': b'\r',
'_tab': b'\t',
}

CTRLED = {
'a': b'\x01', # ctrl + a
'b': b'\x02',
'c': b'\x03',
'd': b'\x04',
}

I'll do a followup article with more info on ctrl and other special characters.  We should be able to pass in arrows to move the cursor, etc. 

Finally, this article will be updated with a link to the complete code soon. 

Reference: