microbit-v2にMicropythonをインストールする
2021/5/10
LCD表示スクリプトを追加した。
2021/5/6
初版
microbit-v2 MicroPython Install
microbit-v2 MicroPython Install
概要
microbit-v2にMicropythonをインストールする方法について記載する
webブラウザーから「micro:bit python editor」にアクセスしてプログラムを作成する方法があるが、ここではmicrobit-v2のボードにmicropythonインタープリタを書き込んで使用する。
(ホストPCとしてはubuntuを想定している)
注意:
microbit-v1のmicropythonの書き込み/実行には以下のエディタを使用すること。
・micro:bit python editor
事前準備
(1)Thonny(pythonエディタ)
以下の手順でインストールする:
bash <(wget -O - https://thonny.org/installer-for-linux)
(2)picocom(通信ソフト)
以下の手順でインストールする:
sudo apt-get install picocom
ビルド手順
以下の手順でmicrobit-v2用のmicropythonをビルドする:
mkdir mb2_mp
cd mb2_mp
git clone https://github.com/microbit-foundation/micropython-microbit-v2.git
cd micropython-microbit-v2
git submodule update --init
make -C lib/micropython/mpy-cross
cd src
gedit Makefile
# Makefileを自分のビルド環境にあわせて変更する
Makefile
以下の部分を自分の実行パスに合わせて変更する:
(実行パスは、which rm,which mkdirなどを実行すると分かる)
<省略>
RM = /usr/bin/rm
MKDIR = /usr/bin/mkdir
PYTHON3 ?= python3
<省略>
続き:
make
# makeが完了すると以下のようにMICROBIT.hexが生成される:
ls
MICROBIT.hex addlayouttable.py codal.patch codal_port
Makefile build codal_app
# miicrobit-v2ボードを接続してファームウェアを描き込む
cp MICROBIT.hex /media/USER/MICROBIT/
以上でfirmwareがボードに書き込まれる。
・USERは環境依存なので、自分の環境に合わせること。
動作確認
picocomを使いボードとシリアルで通信する。
以下、通信例:
$ picocom /dev/ttyACM0 -b115200
MPY: soft reboot
MicroPython v1.15-64-g1e2f0d280 on 2021-05-06; micro:bit v2.0.0-beta.5 with nRF52833
Type "help()" for more information.
>>> import os
>>> os.uname()
(sysname='microbit', nodename='microbit', release='2.0.0-beta.5', version='micro:bit v2.0.0-beta.5+c28e6bd-dirty on 2021-05-06; MicroPython v1.15-64-g1e2f0d280 on 2021-05-06', machine='micro:bit with nRF52833')
>>> import gc
>>> gc.collect()
>>> gc.mem_free()
62272
>>> list(5 * x + y for x in range(10) for y in [4, 2, 1])
[4, 2, 1, 9, 7, 6, 14, 12, 11, 19, 17, 16, 24, 22, 21, 29, 27, 26, 34, 32, 31, 39, 37, 36, 44, 42, 41, 49, 47, 46]
>>>
>>> dir()
['pin_logo', 'panic', 'pin2', 'pin3', 'pin0', 'pin1', 'pin6', 'compass', 'pin4', 'pin5', 'pin7', 'sleep', 'temperature', 'pin9', 'pin8', 'ws2812_write', 'SoundEvent', 'pin13', 'pin12', 'pin11', 'pin10', 'running_time', 'spi', 'pin15', 'pin14', 'display', 'accelerometer', 'pin16', 'pin19', '__thonny_helper', 'audio', '__thonny_relevant', 'button_a', 'button_b', 'Sound', 'os', 'speaker', 'pin_speaker', 'pin20', 'i2c', '__name__', 'uart', 'set_volume', 'Image', 'microphone', 'gc', 'reset']
>>> help('modules')
__main__ machine os uerrno
antigravity math radio urandom
audio microbit speech ustruct
builtins micropython this usys
gc music uarray utime
love neopixel ucollections
Plus any modules on the filesystem
>>>
#オンライン・ヘルプ
>>> help()
Welcome to MicroPython on the micro:bit!
Try these commands:
display.scroll('Hello')
running_time()
sleep(1000)
button_a.is_pressed()
What do these commands do? Can you improve them? HINT: use the up and down
arrow keys to get your command history. Press the TAB key to auto-complete
unfinished words (so 'di' becomes 'display' after you press TAB). These
tricks save a lot of typing and look cool!
Explore:
Type 'help(something)' to find out about it. Type 'dir(something)' to see what
it can do. Type 'dir()' to see what stuff is available. For goodness sake,
don't type 'import this'.
Control commands:
CTRL-C -- stop a running program
CTRL-D -- on a blank line, do a soft reset of the micro:bit
CTRL-E -- enter paste mode, turning off auto-indent
For a list of available modules, type help('modules')
For more information about Python, visit: http://python.org/
To find out about MicroPython, visit: http://micropython.org/
Python/micro:bit documentation is here: https://microbit-micropython.readthedocs.io/
>>>
サンプルスクリプト
helloWorld.py
from microbit import *
while True:
display.scroll('Hello, World!')
display.show(Image.HEART)
sleep(2000)
blink.py
from microbit import *
while True:
pin0.write_digital(0)
sleep(500)
pin0.write_digital(1)
sleep(500)
・接続しているLED(pin0)が点滅する。
dimmer.py
from microbit import *
min_power = 50
max_power = 1023
power_step = (max_power - min_power) / 9
brightness = 0
def set_power(brightness):
display.show(str(brightness))
if brightness == 0:
pin0.write_analog(0)
else:
pin0.write_analog(brightness * power_step + min_power)
set_power(brightness)
while True:
if button_a.was_pressed():
brightness -= 1
if brightness < 0:
brightness = 0
set_power(brightness)
elif button_b.was_pressed():
brightness += 1
if brightness > 9:
brightness = 9
set_power(brightness)
sleep(100)
・ボタンBを押すと接続しているLEC(pin0)の光量が増す。
・ボタンAを押すと接続しているLEC(pin0)の光量が減る。
linghtsensor.py
from microbit import *
while True:
if display.read_light_level() < 100:
display.show(Image.HEART)
else: display.clear()
sleep(2000)
・光をLED-Matrixに当てると表示されているLEDのハートマークが消える。
(強い光を当てないと光センサーが動作しないようだ)
conway.py
'''
Conway's Game Of Life for the micro:bit
Press button A or tap the micro:bit to generate a fresh layout.
'''
import microbit
import random
arena1 = bytearray(7 * 7)
arena2 = bytearray(7 * 7)
def show():
img = microbit.Image(5,5)
for y in range(5):
for x in range(5):
img.set_pixel(x, y, arena1[8 + y * 7 + x]*9)
microbit.display.show(img)
# do 1 iteration of Conway's Game of Life
def conway_step():
global arena1, arena2
for i in range(5 * 5): # loop over pixels
i = 8 + (i // 5) * 7 + i % 5
# count number of neighbours
num_neighbours = (arena1[i - 8] +
arena1[i - 7] +
arena1[i - 6] +
arena1[i - 1] +
arena1[i + 1] +
arena1[i + 6] +
arena1[i + 7] +
arena1[i + 8])
# check if the centre cell is alive or not
self = arena1[i]
# apply the rules of life
if self and not (2 <= num_neighbours <= 3):
arena2[i] = 0 # not enough, or too many neighbours: cell dies
elif not self and num_neighbours == 3:
arena2[i] = 1 # exactly 3 neighbours around an empty cell: cell is born
else:
arena2[i] = self # stay as-is
# swap the buffers (arena1 is now the new one to display)
arena1, arena2 = arena2, arena1
while True:
# randomise the start
for i in range(5 * 5): # loop over pixels
i = 8 + (i // 5) * 7 + i % 5
arena1[i] = random.randrange(2) # set the pixel randomly
show()
microbit.sleep(1) # need to yield to update accelerometer (not ideal...)
# loop while button a is not pressed
while not microbit.button_a.is_pressed() and microbit.accelerometer.get_z() < -800:
conway_step()
show()
microbit.sleep(150)
neopixel_random.py
"""
neopixel_random.py
Repeatedly displays random colours onto the LED strip.
This example requires a strip of 8 Neopixels (WS2812) connected to pin0.
"""
from microbit import *
import neopixel
from random import randint
# Setup the Neopixel strip on pin0 with a length of 8 pixels
#np = neopixel.NeoPixel(pin0, 8)
np = neopixel.NeoPixel(pin0, 64)
while True:
#Iterate over each LED in the strip
for pixel_id in range(0, len(np)):
red = randint(0, 60)
green = randint(0, 60)
blue = randint(0, 60)
# Assign the current LED a random red, green and blue value between 0 and 60
np[pixel_id] = (red, green, blue)
# Display the current pixel data on the Neopixel strip
np.show()
#sleep(100)
・使用するneopixelの数に応じてsleep時間は調整すること。
firefly.py
# A micro:bit Firefly.
# By Nicholas H.Tollervey. Released to the public domain.
import radio
import random
from microbit import display, Image, button_a, sleep
# Create the "flash" animation frames. Can you work out how it's done?
flash = [Image().invert()*(i/9) for i in range(9, -1, -1)]
# The radio won't work unless it's switched on.
radio.on()
# Event loop.
while True:
# Button A sends a "flash" message.
if button_a.was_pressed():
radio.send('flash') # a-ha
# Read any incoming messages.
incoming = radio.receive()
if incoming == 'flash':
# If there's an incoming "flash" message display
# the firefly flash animation after a random short
# pause.
sleep(random.randint(50, 350))
display.show(flash, delay=100, wait=False)
# Randomly re-broadcast the flash message after a
# slight delay.
if random.randint(0, 9) == 0:
sleep(500)
radio.send('flash') # a-ha
・2つのmicrobitを用意して上のプログラムを描き込み実行する。
・Aボタンを押すとbluetooth経由で信号を受信して対向のmicrobitのLEDが光る。
(対向のmicrobitはv2でなくても良い)
performanceTest.py
import time
def performanceTest():
msec = time.ticks_ms
endTime = msec() + 10000
count = 0
while msec() < endTime:
count += 1
print("Count: ", count)
performanceTest()
以下が出力される:(出力例)
Count: 669283
LCD表示スクリプト(2021/5/10)
以下は
「DEPRECATED LIBRARY micropython-adafruit-rgb-display」のソースをmicrobit-v2のmicropythonに合わせて修正したものになる:
(使用しているLCDは以下のもの)
1.8inch colorful display module for micro:bit, 160x128
rgb.py
import utime
import ustruct
def color565(r, g, b):
return (r & 0xf8) << 8 | (g & 0xfc) << 3 | b >> 3
class DummyPin:
"""A fake gpio pin for when you want to skip pins."""
OUT = 0
IN = 0
PULL_UP = 0
PULL_DOWN = 0
OPEN_DRAIN = 0
ALT = 0
ALT_OPEN_DRAIN = 0
LOW_POWER = 0
MED_POWER = 0
HIGH_PWER = 0
IRQ_FALLING = 0
IRQ_RISING = 0
IRQ_LOW_LEVEL = 0
IRQ_HIGH_LEVEL = 0
def __call__(self, *args, **kwargs):
return False
init = __call__
value = __call__
out_value = __call__
toggle = __call__
high = __call__
low = __call__
on = __call__
off = __call__
mode = __call__
pull = __call__
drive = __call__
irq = __call__
class Display:
_PAGE_SET = None
_COLUMN_SET = None
_RAM_WRITE = None
_RAM_READ = None
_INIT = ()
_ENCODE_PIXEL = ">H"
_ENCODE_POS = ">HH"
_DECODE_PIXEL = ">BBB"
def __init__(self, width, height):
self.width = width
self.height = height
self.init()
def init(self):
"""Run the initialization commands."""
for command, data in self._INIT:
self._write(command, data)
def _block(self, x0, y0, x1, y1, data=None):
"""Read or write a block of data."""
self._write(self._COLUMN_SET, self._encode_pos(x0, x1))
self._write(self._PAGE_SET, self._encode_pos(y0, y1))
if data is None:
size = ustruct.calcsize(self._DECODE_PIXEL)
return self._read(self._RAM_READ,
(x1 - x0 + 1) * (y1 - y0 + 1) * size)
self._write(self._RAM_WRITE, data)
def _encode_pos(self, a, b):
"""Encode a postion into bytes."""
return ustruct.pack(self._ENCODE_POS, a, b)
def _encode_pixel(self, color):
"""Encode a pixel color into bytes."""
return ustruct.pack(self._ENCODE_PIXEL, color)
def _decode_pixel(self, data):
"""Decode bytes into a pixel color."""
return color565(*ustruct.unpack(self._DECODE_PIXEL, data))
def pixel(self, x, y, color=None):
"""Read or write a pixel."""
if color is None:
return self._decode_pixel(self._block(x, y, x, y))
if not 0 <= x < self.width or not 0 <= y < self.height:
return
self._block(x, y, x, y, self._encode_pixel(color))
def fill_rectangle(self, x, y, width, height, color):
"""Draw a filled rectangle."""
x = min(self.width - 1, max(0, x))
y = min(self.height - 1, max(0, y))
w = min(self.width - x, max(1, width))
h = min(self.height - y, max(1, height))
self._block(x, y, x + w - 1, y + h - 1, b'')
chunks, rest = divmod(w * h, 512)
pixel = self._encode_pixel(color)
if chunks:
data = pixel * 512
for count in range(chunks):
self._write(None, data)
if rest:
self._write(None, pixel * rest)
def fill(self, color=0):
"""Fill whole screen."""
self.fill_rectangle(0, 0, self.width, self.height, color)
def hline(self, x, y, width, color):
"""Draw a horizontal line."""
self.fill_rectangle(x, y, width, 1, color)
def vline(self, x, y, height, color):
"""Draw a vertical line."""
self.fill_rectangle(x, y, 1, height, color)
def blit_buffer(self, buffer, x, y, width, height):
"""Copy pixels from a buffer."""
if (not 0 <= x < self.width or
not 0 <= y < self.height or
not 0 < x + width <= self.width or
not 0 < y + height <= self.height):
raise ValueError("out of bounds")
self._block(x, y, x + width - 1, y + height - 1, buffer)
class DisplaySPI(Display):
def __init__(self, spi, dc, cs=None, rst=None, width=1, height=1):
self.spi = spi
self.cs = cs
self.dc = dc
self.rst = rst
if self.rst is None:
self.rst = DummyPin()
if self.cs is None:
self.cs = DummyPin()
#self.cs.init(self.cs.OUT, value=1)
#self.dc.init(self.dc.OUT, value=0)
#self.rst.init(self.rst.OUT, value=1)
self.cs.write_digital(1)
self.dc.write_digital(0)
self.rst.write_digital(1)
self.reset()
super().__init__(width, height)
def reset(self):
#self.rst(0)
self.rst.write_digital(0)
utime.sleep_ms(50)
#self.rst(1)
self.rst.write_digital(1)
utime.sleep_ms(50)
def _write(self, command=None, data=None):
if command is not None:
#self.dc(0)
#self.cs(0)
self.dc.write_digital(0)
self.cs.write_digital(0)
self.spi.write(bytearray([command]))
#self.cs(1)
self.cs.write_digital(1)
if data is not None:
#self.dc(1)
#self.cs(0)
self.dc.write_digital(1)
self.cs.write_digital(0)
self.spi.write(data)
#self.cs(1)
self.cs.write_digital(1)
def _read(self, command=None, count=0):
#self.dc(0)
#self.cs(0)
self.dc.write_digital(0)
self.cs.write_digital(0)
if command is not None:
self.spi.write(bytearray([command]))
if count:
data = self.spi.read(count)
#self.cs(1)
self.cs.write_digital(1)
return data
・ピン制御の部分をmicrobitのmicropythonに合わせて修正した。
st7735.py
from rgb import DisplaySPI, color565
import ustruct
_NOP=const(0x00)
_SWRESET=const(0x01)
_RDDID=const(0x04)
_RDDST=const(0x09)
_SLPIN=const(0x10)
_SLPOUT=const(0x11)
_PTLON=const(0x12)
_NORON=const(0x13)
_INVOFF=const(0x20)
_INVON=const(0x21)
_DISPOFF=const(0x28)
_DISPON=const(0x29)
_CASET=const(0x2A)
_RASET=const(0x2B)
_RAMWR=const(0x2C)
_RAMRD=const(0x2E)
_PTLAR=const(0x30)
_COLMOD=const(0x3A)
_MADCTL=const(0x36)
_FRMCTR1=const(0xB1)
_FRMCTR2=const(0xB2)
_FRMCTR3=const(0xB3)
_INVCTR=const(0xB4)
_DISSET5=const(0xB6)
_PWCTR1=const(0xC0)
_PWCTR2=const(0xC1)
_PWCTR3=const(0xC2)
_PWCTR4=const(0xC3)
_PWCTR5=const(0xC4)
_VMCTR1=const(0xC5)
_RDID1=const(0xDA)
_RDID2=const(0xDB)
_RDID3=const(0xDC)
_RDID4=const(0xDD)
_PWCTR6=const(0xFC)
_GMCTRP1=const(0xE0)
_GMCTRN1=const(0xE1)
class ST7735(DisplaySPI):
"""
A simple driver for the ST7735-based displays.
>>> from machine import Pin, SPI
>>> import st7735
>>> display = st7735.ST7735(SPI(1), dc=Pin(12), cs=Pin(15), rst=Pin(16))
>>> display = st7735.ST7735R(SPI(1, baudrate=40000000), dc=Pin(12), cs=Pin(15), rst=Pin(16))
>>> display.fill(0x7521)
>>> display.pixel(64, 64, 0)
"""
_COLUMN_SET = _CASET
_PAGE_SET = _RASET
_RAM_WRITE = _RAMWR
_RAM_READ = _RAMRD
_INIT = (
(_SWRESET, None),
(_SLPOUT, None),
(_MADCTL, b'\x08'), # bottom to top refresh
(_COLMOD, b'\x05'), # 16bit color
(_INVCTR, b'0x00'), # line inversion
# 1 clk cycle nonoverlap, 2 cycle gate rise, 3 sycle osc equalie,
# fix on VTL
(_DISSET5, b'\x15\x02'),
# fastest refresh, 6 lines front porch, 3 line back porch
(_FRMCTR1, b'\x00\x06\x03'),
(_PWCTR1, b'\x02\x70'), # GVDD = 4.7V, 1.0uA
(_PWCTR2, b'\x05'), # VGH=14.7V, VGL=-7.35V
(_PWCTR3, b'\x01\x02'), # Opamp current small, Boost frequency
(_PWCTR6, b'\x11\x15'),
(_VMCTR1, b'\x3c\x38'), # VCOMH = 4V, VOML = -1.1V
(_GMCTRP1, b'\x09\x16\x09\x20\x21\x1b\x13\x19'
b'\x17\x15\x1e\x2b\x04\x05\x02\x0e'), # Gamma
(_GMCTRN1, b'\x08\x14\x08\x1e\x22\x1d\x18\x1e'
b'\x18\x1a\x24\x2b\x06\x06\x02\x0f'),
(_CASET, b'\x00\x02\x00\x81'), # XSTART = 2, XEND = 129
(_RASET, b'\x00\x02\x00\x81'), # YSTART = 2, YEND = 129
(_NORON, None),
(_DISPON, None),
)
_ENCODE_PIXEL = ">H"
_ENCODE_POS = ">HH"
def __init__(self, spi, dc, cs, rst=None, width=128, height=128):
super().__init__(spi, dc, cs, rst, width, height)
class ST7735R(ST7735):
_INIT = (
(_SWRESET, None),
(_SLPOUT, None),
(_MADCTL, b'\xc8'),
(_COLMOD, b'\x05'), # 16bit color
(_INVCTR, b'\x07'),
(_FRMCTR1, b'\x01\x2c\x2d'),
(_FRMCTR2, b'\x01\x2c\x2d'),
(_FRMCTR3, b'\x01\x2c\x2d\x01\x2c\x2d'),
(_PWCTR1, b'\x02\x02\x84'),
(_PWCTR2, b'\xc5'),
(_PWCTR3, b'\x0a\x00'),
(_PWCTR4, b'\x8a\x2a'),
(_PWCTR5, b'\x8a\xee'),
(_VMCTR1, b'\x0e'),
(_INVOFF, None),
(_GMCTRP1, b'\x02\x1c\x07\x12\x37\x32\x29\x2d'
b'\x29\x25\x2B\x39\x00\x01\x03\x10'), # Gamma
(_GMCTRN1, b'\x03\x1d\x07\x06\x2E\x2C\x29\x2D'
b'\x2E\x2E\x37\x3F\x00\x00\x02\x10'),
)
def __init__(self, spi, dc, cs, rst=None, width=128, height=160):
super().__init__(spi, dc, cs, rst, width, height)
def init(self):
super().init()
cols = ustruct.pack('>HH', 0, self.width - 1)
rows = ustruct.pack('>HH', 0, self.height - 1)
for command, data in (
(_CASET, cols),
(_RASET, rows),
(_NORON, None),
(_DISPON, None),
):
self._write(command, data)
・特に修正はない。
displayLCD.py
from microbit import *
from st7735 import ST7735, ST7735R
from rgb import color565
spi.init(mode=3, baudrate=300000000, sclk=pin13, mosi=pin15, miso=pin14)
#spi.init(0)
lcd = ST7735R(spi, dc=pin12, cs=pin16, rst=pin8)
lcd.fill(0)
for x in range(128):
for y in range(160):
lcd.pixel(x,y,color565(x*2, y*3//2, 255-x-y*3//4))
・転送速度が遅いようで描画速度が遅い。
(設定の問題だと思うが画面端にゴミが表示される)
・他のmicropythonと異なり最初からSPIのインスタンスが用意されているので、コーディング時は、そこに注意する必要がある。(メモリの制約と推測する)
main.py
電源オン時に自動的にmain.pyが実行されるので、直接実行したいプログラムをmain.pyに置くことができる。
直接実行したいプログラムがxxxx.pyの場合は、
main.pyを以下のようにしても良い。
main.py
# main.py
import xxxx
参照URL
micro:bit-v2_MicroPython関連:
MicroPython on the micro:bit via CODAL
Hello, World! (v2-doc)日本語版
micro:bit v2 用 MicroPython で新機能を試してみる
Hello, World! (v2-doc)
Out of box experience(factory reset)
Python guide(examples)
micropython/examples
Thonny関連:
Thonny (Python IDE for beginners)
Thonnyインストール方法(PicoボードにMicropython/CircuitPythonをインストールする)
(別の)MicroPython関連:
TFT LCD(ST7735R)をMicroPythonで動かしてみた
DEPRECATED LIBRARY micropython-adafruit-rgb-display
MakeCode(python)関連:(参考)
(the difference between) MakeCode Python and MicroPython
micro:bit v2(マイクロビット)に関する技術情報を集約
以上