以下のページで紹介したLEDMATRIXライブラリを使用すると、NeoPixelマトリックスに文字や図形を簡単に表示できるようになりました。
しかしながら表示できる文字は英字や数字などで漢字や仮名などを表示できないという制限がありました。ここでは、LEDMATRIXを拡張(継承による拡張ではなく書き換えて)日本語表示ができるLEDMATRIXJPを作成したので、それを紹介します。LEDMATRIXJPは日本語フォントとして美咲フォント(の修正版)を使用しています。
LEDMATRIXJPドライバは、ESP32やRasPi PICO (RP2040)などで動作確認をしています。
なお、ESP8266ではヒープ領域が少ないため、美咲フォントをメモリに読み込むことができず、LEDMATRIXJPドライバは使用できませんでした。
美咲フォントの修正
LEDMATRIXJPドライバでは、日本語フォントとして以下のサイトで公開されている美咲フォントを使用しています。長年にわたり開発と自由に利用できるように配布していただいているたま吉さんに感謝します。
公開されているフォントとソフトウェアは、RasPi PICOで動作確認されているようですが、ESP32系では、ヒープ不足で利用できないので、使用するデータ表現を変更し、フォントのメモリ占有量を削減する修正を加えたバージョンを作成したので、LEDMATRIXJPではそれを使用します。
LEDMATRIXJPのライブラリコード
ライブラリのソースをledmatrixjp.pyに示します。開発ボードのlibフォルダの中に入れてください。
"""
LEDMATRIXJP: WS2812B (NeoPixel) Matrix Display driver
for MicroPython
Version 1.0
美咲フォントを使用した日本語表示機能装備
https://github.com/Tamakichi/pico_MicroPython_misakifont
Copyright 2023 K.Kakizaki
LEDMATRIXJPライブラリ: 美咲フォントを使用したNEOPIXEL マトリックスでの日本語表示 (MICROPYTHON)
"""
from machine import Pin
import framebuf
from micropython import const
from neopixel import NeoPixel
from misakifont import MisakiFont
import time
def LEDColor(r, g, b) :
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
class LEDMATRIXJP(framebuf.FrameBuffer):
ASCII = const(0)
MISAKI = const(1)
MIX = const(2)
RED = LEDColor(255, 0, 0)
GREEN = LEDColor(0, 255, 0)
BLUE = LEDColor(0, 0, 255)
CYAN = LEDColor(0, 255, 255)
YELLOW = LEDColor(255, 255, 0)
PURPLE = LEDColor(255, 0, 255)
WHITE = LEDColor(255, 255, 255)
BLACK = LEDColor(0, 0, 0)
SIMPLE=const(0x01)
ZIGZAG=const(0x02)
TOP=const(0x10)
BOTTOM=const(0x20)
RIGHT=const(0x40)
LEFT=const(0x50)
def __init__(self, pin, width, height, skip=0, link=SIMPLE):
self._pin = Pin(pin, Pin.OUT)
self._width = width
self._height = height
self._skip = skip
self._link = link
self._np = NeoPixel(self._pin, width * height + skip)
self._brt = 10
self.buffer = bytearray(self._height * self._width * 2)
super().__init__(self.buffer, self._width, self._height, framebuf.RGB565)
self._font = ASCII
self._mf = None
self.fill(0)
for i in range(0, skip):
self._np[i] = (0,0,0)
self.show()
def color(self, r, g, b):
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
def setPixelColor(self, n, r, g, b):
self._np[n] = (r, g, b)
def setBrightness(self, brt):
if (brt > 255):
brt = 255
if (brt < 0):
brt = 0
self._brt = brt
self.show()
def getBrightness(self):
return self._brt
def text(self, txt, x, y, color):
if self._font == ASCII:
super().text(txt, x, y, color)
elif self._font == MISAKI:
for c in txt:
fnt = self._mf.font(ord(c))
self._draw_font(fnt, x, y, color, 1)
x += 8
else:
for c in txt:
if ord(c) < 0x80:
super().text(c, x, y, color)
else:
fnt = self._mf.font(ord(c))
self._draw_font(fnt, x, y, color, 1)
x += 8
def _draw_font(self, font, x, y, color, size):
for row in range(0, 7):
for col in range(0, 7):
if (0x80 >> col) & font[row]:
self.fill_rect(x + col * size, y + row * size, size, size, color)
def setFont(self, f):
self._font = f
if f != ASCII and self._mf == None:
self._mf = MisakiFont()
def showScrollText(self, text, color, bgcolor=0, y=0, dir=0, ms=50):
text = str(text)
for x in range(self._width,-8*len(text)-1,-1):
self.fill(bgcolor)
self.text(text,x,y,color)
self.show()
time.sleep_ms(ms)
def setScrollText(self, text, color, bgcolor=0, y=0, dir=0, ms=50):
self._text = str(text)
self._color = color
self._bgcolor = bgcolor
self._sty = y
self._stx = self._width
self._stxe = -8*len(self._text)-1
self._dir = dir
self._ms = ms
def runScrollText(self):
if self._stx > self._stxe:
self.fill(self._bgcolor)
self.text(self._text, self._stx, self._sty, self._color)
self.show()
self._stx = self._stx - 1
return True
else:
return False
def size(self):
return (self._width, self._height)
def show(self):
for x in range(0, self._width):
for y in range(0, self._height):
idx = x + y * self._width
v1 = self.buffer[idx*2]
v0 = self.buffer[idx*2+1]
vr = (v0 >> 3) << 3
vg = (((v0 & 0x7) << 3) | (v1 >> 5)) << 2
vb = (v1 & 0x1F) << 3
rate = self._brt/255
if self._link == SIMPLE:
lidx = x + y * self._width
else:
lidx = x * self._height
if x % 2 == 0:
lidx += y
else:
lidx += (self._height - y - 1)
self._np[lidx + self._skip] = (int(vr*rate), int(vg*rate), int(vb*rate))
self._np.write()
使用法と使用上の注意
日本語を表示するための機能以外は基本的に、LEDMATRIXJPライブラリは、LEDMATRIXライブラリと同じ使用法と使用上の注意となります。LEDMATRIXライブラリの記事も併せてごらんください。
デモプログラム
ESP32-PIXELやESP32-KEY-R2, ESP32-SLIM等を対象として作成したLEDMATRIXのデモプログラムです。
LEDMATRIXコンストラクタの第一引数に、LEDの制御用に割り当てたピン番号を指定します。
from ledmatrixjp import LEDMATRIXJP
import time
color = [LEDMATRIXJP.RED, LEDMATRIXJP.GREEN, LEDMATRIXJP.BLUE, LEDMATRIXJP.CYAN, LEDMATRIXJP.YELLOW, LEDMATRIXJP.PURPLE, LEDMATRIXJP.WHITE, ]
# brt = [0,1,2,3,5,10,20,30,50,100]
brt = [0,1,2,3,5,10,20,30]
# lmx = LEDMATRIXJP(15,18,8) # ESP32-PIXEL
# lmx = LEDMATRIXJP(2,8,8)
# lmx = LEDMATRIXJP(23,16,16,link=LEDMATRIXJP.ZIGZAG) # LEDパネル 16x16
lmx = LEDMATRIXJP(23,32,8,link=LEDMATRIXJP.ZIGZAG) # LEDパネル 32x8
# lmx = LEDMATRIXJP(23,32,16,link=LEDMATRIXJP.ZIGZAG) # LEDパネル 16x16を2枚
lmx.setFont(LEDMATRIXJP.MIX)
if lmx.size()[0] <= 8:
scrms = 100
elif lmx.size()[0] <= 20:
scrms = 50
else:
scrms = 20
lmx.setBrightness(0)
lmx.fill(LEDMATRIXJP.GREEN)
for n in brt:
lmx.setBrightness(n)
time.sleep_ms(200)
time.sleep_ms(1000)
lmx.fill(0)
lmx.setBrightness(10)
time.sleep_ms(1000)
if lmx.size()[1] == 18:
str = 'ESP32-PIXEL LEDマトリックス'
else:
str = '日本語表示 LEDマトリックス'
lmx.showScrollText(str, LEDMATRIXJP.YELLOW, ms=scrms)
time.sleep_ms(1000)
for y in range(lmx.size()[1]+1,0,-1):
lmx.fill_rect(0, 0, y*2, y, color[y%len(color)])
lmx.show()
time.sleep_ms(100)
time.sleep_ms(1000)
lmx.showScrollText('気温、しつ度、気圧、加速度、明るさ', LEDMATRIXJP.CYAN, ms=scrms)
time.sleep_ms(1000)
lmx.fill(0)
for y in range(lmx.size()[1],-1,-1):
lmx.line(0, y, lmx.size()[0]-1, 0, color[y%len(color)])
lmx.show()
time.sleep_ms(100)
time.sleep_ms(1000)
lmx.showScrollText('MicroFan マイクロファン', LEDMATRIXJP.RED, ms=scrms)
time.sleep_ms(1000)
for r in range(1,lmx.size()[0]//2):
lmx.ellipse(lmx.size()[0]//2,lmx.size()[1]//2,r,r,LEDMATRIXJP.BLUE,True)
lmx.show()
time.sleep_ms(300)
time.sleep_ms(1000)
lmx.fill(LEDMATRIXJP.BLACK)
str = '> <'
x = lmx.size()[0]//2 - (8*len(str))//2
y = (lmx.size()[1] - 8) // 2
if lmx.size()[0] <= 8:
lmx.text('#',0,0,LEDMATRIXJP.GREEN)
else:
lmx.text(str,x,y,LEDMATRIXJP.CYAN)
lmx.text('#',x+8,y,LEDMATRIXJP.GREEN)
for n in [0,1,1,1,2,2,3,5,7,10]:
lmx.setBrightness(n)
lmx.show()
time.sleep_ms(300)