以下のページで紹介した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)