最新のオリジナルMicroPython(v1.24.0 preview)をM5Stack(Core2/Fire/M5Atom[S3]/M5Stamp[S3])に最適化してリビルドしました!これから機能を充実させてゆきます。
現状ではどのMicroPythonでも使えない機能があるため、今のところ機能が豊富なloboris版をM5Stack(M5Sstick/M5Atom/M5Stamp)に最適化してリビルドしました!
from machine import I2S
spk = I2S(0, sck=Pin(12), ws=Pin(0), sd=Pin(2), mode=I2S.TX,
bits=16, format=I2S.MONO_RIGHT, rate=44100, ibuf=1024) 音飛びする際はibufの値を増やしてください。
psramがない状態でbtstack(Bluetooth)と同時に使うときはbuffsizeを4以下に設定しないとハングします。
from machine import I2S
spk = I2S(I2S.NUM0,
bck=Pin(19), ws=Pin(33), sdout=Pin(22),
mode=I2S.MASTER_TX,
dataformat=I2S.B16, channelformat=I2S.ALL_RIGHT,
samplerate=48000, dmacount=2,
dmalen=1024)
再生
import audio
audio.play_mp3('ファイル名.mp3') ファイルディスクリプタの指定も出来ます
オプション(省略可、値は初期値)
port_id=0,
buffsize=1940, リードバッファ(socketストリームからの再生時など、音切れする際は増やします)
bg_play=False, stacksize=4096, priority=2, core=1, バックグラウンド再生の指定
bit=32, I2Sで扱うビットサイズ(音質向上用)
stream=False, True にするとヘッダを読み込まない
end_mute=False, 再生後の消音(ノイズ対策)
button=None, button_on=False 停止ボタン、停止ボタンON条件
audio.check_play() 再生確認
audio.stop() 再生停止
audio.interrupt_in() 子スレッドのファイルアクセスを停止
audio.interrupt_out() 子スレッドのファイルアクセスを再開
import audio
spk.play_mp3('ファイル名.mp3')
オプション(省略可) buffsize=8, stacksize=4096
audio.check_play() 再生確認
audio.stopwave() 再生停止
DAC使用の際はspkの部分がdacになります
バックグラウンド再生中でも親スレッドでファイルアクセスが出来ます!
競合が発生しないように改良していますが、ハードウェアでファイルシステムとリソースを共有しているデバイスがあると競合が発生します(M5StackのSDカードとLCDの関係など)
詳しくは
Partitions API、
SPI Flash API を参照してください。
この場合は interrupt_in() により子スレッドのファイルアクセスを停止することで競合を避けることが出来ます。下記ご注意ください。
mp3ファイルはモノラルに変換してください。
変換ツールはEcoDecoTooL114を使っています(Fraunhofer/CBR/128kbps)
WAVEファイルを再生するIndex
import audio
audio.play_wave('ファイル名.wav') ファイルディスクリプタの指定も出来ます
オプション(省略可、値は初期値)
port_id=0,
buffsize=1024, バッファーサイズ
bg_play=False, stacksize=4096, priority=2, core=1, バックグラウンド再生の指定
bit=32, I2Sで扱うビットサイズ(音質向上用)
repeat=0, リピート再生回数
end_mute=False 再生後の消音(ノイズ対策)
button=None, button_on=False 停止ボタン、停止ボタンON条件
audio.check_play() 再生確認
audio.stop() 再生停止
バックグラウンド再生については
MP3ファイルを再生するを参照してください。
SDカードスロットとLCDはインターフェース(VSPI)を共有しています。
SDカードとLCDへのアクセスが集中した状態ではLCDへの表示が遅れたり再生音が途切れることがあります。バッファーを増やすことで改善されます。
dac.wavplay('/sd/ファイル名.wav', サンプルレート補正, バッファ数(省略可))
WAVデータはDMAを使ってDAコンバータに転送されます。
DMAのサイズはバッファ数2(バッファ長1024Byte)と固定されているため、
バッファ数を指定できるようにオプションを追加しています。
32を指定した場合、バッファサイズは32KBになります。
弊害として再生開始までの時間が長くなります。
初期化
from machine import I2S
framerate = 12000
mic = I2S(0, ws=Pin(0), sd=Pin(34), mode=I2S.RX|I2S.PDM,
bits=16, format=I2S.ALL_RIGHT, rate=framerate*4, ibuf=1024)
sleep_ms(500)
from machine import I2S
framerate = 12000
mic = I2S(0, ws=Pin(40), sd=Pin(41), mode=I2S.RX|I2S.PDM,
bits=16, format=I2S.ALL_RIGHT, rate=framerate*2, ibuf=1024)
sleep_ms(500)
from machine import I2S
samples = bytearray(2*8192)
mic = I2S(I2S.NUM0,
ws=Pin(0), sdin=Pin(34),
mode=I2S.MASTER_RX_PDM,
dataformat=I2S.B16, channelformat=I2S.RIGHT_LEFT,
samplerate=10000, dmacount=2,
dmalen=128) 任意のサンプルレート
mic.set_clk(samplerate=10000, dataformat=I2S.B16,channel=I2S.MONO)
numread = audio.readinto(samples, timeout=I2S.portMAX_DELAY) 録音実行
録音
音に反応して自動で録音します。録音開始に必要な音量はローパス、マイクレベルで設定します。
録音データはファイルにも保存できます(レスポンスが落ちます)
import wave
buff = bytearray(framerate*10+44)
rec_size= wave.mic_recording_auto(mic, 録音データの格納場所(メモリ / ファイル名)buff,
フレームレート12000, ローパス8, マイクレベル6)
オプション(省略可、値は初期値)
chunk_size=6000, 録音の最小単位(フレームレートの半分を指定すれば0.5秒)
block_size=48000, 音声認識処理される最小単位ブロック単位(通常フレームレートの4倍)
sc=3, nc=2, debug=False, cv=False, shift=0
ボタンAを押している間に録音してwaveファイルを作成します(0.5秒単位)
メモリに記録しているので高速動作します。メモリ不足の場合は直接ファイルに書き込む必要があります。
import struct
WAVE_FORMAT_PCM = 0x0001
nchannels = 1
sampwidth = 2
samples = bytearray(framerate*10+44)
mv = memoryview(samples)
datalength=0
print('Wait...'
while buttonA.value():
pass
print('Rec', end='')
for i in range(20):
if buttonA.value():
break
datalength += mic.readinto(mv[44+datalength:44+datalength+framerate//2])
print(".", end='')
print(' ')
mv[0:4] = struct.pack('4s', b'RIFF')
mv[4:40] = struct.pack('<L4s4sLHHLLHH4s',
36 + datalength, b'WAVE',
b'fmt ', 16, WAVE_FORMAT_PCM,
nchannels, framerate,
nchannels * framerate * sampwidth,
nchannels * sampwidth,
sampwidth * 8, b'data')
mv[40:44] = struct.pack('<L', datalength)
wave = open('test.wav','wb')
wave.write(mv[0:44+datalength])
wave.close()
mic.deinit()
MicroPythonでグラフィックを使ってみる。
液晶ディスプレイを使う [ originalリビルド版 ]Index
初期化
import st7789
spi = machine.SPI(2, sck=Pin(17), mosi=Pin(21))
tft = st7789.ST7789(2, 128, 128, モジュールを改変しているので第1引数は2になります(本来はSPI)
baudrate=24000000, モジュールを改変しているのでbaudrateはここで指定します。(本来はSPIで指定)
reset=Pin(34, Pin.OUT), dc=Pin(33, Pin.OUT), cs=Pin(15, Pin.OUT),
backlight=Pin(16, Pin.OUT), color_order=st7789.BGR, rotation=2)
tft.init()
tft.offset(2, 0)
tft.on()
tft.fill(st7789.BLACK)
tft.jpg(f'MicroPython.jpg', 23, 23, st7789.FAST)
import st7789
spi = machine.SPI(2)
tft = st7789.ST7789(2, 320, 240, モジュールを改変しているので第1引数は2になります(本来はSPI)
baudrate=40000000, モジュールを改変しているのでbaudrateはここで指定します。(本来はSPIで指定)
reset=Pin(33, Pin.OUT), dc=Pin(15, Pin.OUT), cs=Pin(5, Pin.OUT),
rotation=0, color_order=st7789.BGR)
tft.init()
tft.fill(st7789.BLACK)
tft.jpg(f'MicroPython.jpg', 23, 23, st7789.FAST)
pmu.setLCDBrightness(1000) バックライトの照度の指定が必要です。
import st7789
spi = machine.SPI(2)
tft = st7789.ST7789(2, 320, 240, モジュールを改変しているので第1引数は2になります(本来はSPI)
baudrate=40000000, モジュールを改変しているのでbaudrateはここで指定します。(本来はSPIで指定)
reset=Pin(33, Pin.OUT), dc=Pin(27, Pin.OUT), cs=Pin(14, Pin.OUT),
backlight=Pin(32, Pin.OUT), rotation=0, color_order=st7789.BGR)
tft.init()
tft.on()
tft.fill(st7789.BLACK)
tft.jpg(f'MicroPython.jpg', 23, 23, st7789.FAST)
import st7789
spi = machine.SPI(2, sck=Pin(13), mosi=Pin(15))
tft = st7789.ST7789(2, 135, 240, モジュールを改変しているので第1引数は2になります(本来はSPI)
baudrate=20000000, モジュールを改変しているのでbaudrateはここで指定します。(本来はSPIで指定)
reset=Pin(12, Pin.OUT), dc=Pin(14, Pin.OUT), cs=Pin(5, Pin.OUT),
backlight=Pin(27, Pin.OUT), rotation=1)
tft.init()
tft.offset(40, 54)
tft.on()
tft.fill(st7789.BLACK)
tft.jpg(f'MicroPython.jpg', 23, 23, st7789.FAST)
描画などの使い方はこちらを参照してください。→
ST7789 Driver for MicroPython
M5Stack Core2 / FireはLCDとSDが同じSPIバスに接続されているので、共用出来るように"russhughes/st7789_mpy"を修正しています。
マルチプラットフォームなのでM5Stackのハードウェアに特化していないのだと思われます。
液晶ディスプレイを使う [ originalリビルド版[S3] ]Index
初期化
import s3lcd
bus = s3lcd.SPI_BUS(2, mosi=21, sck=15, dc=42, cs=14, pclk=27000000, swap_color_bytes=True)
custom_rotations = (
(128, 128, 2, 1, False, False, False),
(128, 128, 1, 2, True, True, False),
(128, 128, 2, 1, False, True, True),
(128, 128, 1, 2, True, False, True),
)
try:
tft.deinit()
except:
pass
tft=s3lcd.ESPLCD(bus, 128, 128,
inversion_mode=True, color_space=s3lcd.BGR,
reset=48, rotations=custom_rotations,
rotation=2, dma_rows=32, options=0,
)
tft.init()
tft.fill(s3lcd.BLACK)
import custom_file
tft.jpg(custom_file.MICROPYTHON_LOGO, 23, 23)
tft.show()
import lp5562
bl=lp5562.LP5562(i2c)
bl.enable()
bl.setBrightness(3,255)
描画などの使い方はこちらを参照してください。
→
ESP_LCD MicroPython driver for ESP32-S3 Devices with ST7789 or compatible displays.
液晶ディスプレイを使う (framebuf)Index
初期化
import sh1107
oled = sh1107.SH1107_SPI(64, 128, spi, dc=Pin(27), res=Pin(33), cs=Pin(14), seg_remap=1)
上下反転
oled.line(0, 0, 64, 128, 1) 端から端まで線を引く
oled.text('Micropython', 0, 0, 1, 1)
'表示文字',X座標,Y座標,1=白,1=右90°回転
oled.scroll(-8, 0)
oled.show()
画面リフレッシュ
oled.bitmap('i2is.bmp')
'ビットマップファイル名' 128x64ドット 単色
oled.show()
画面リフレッシュ
液晶ディスプレイを使う [ loborisリビルド版 ]Index
初期化
import display
from machine import Pin
ips_check = Pin(33,Pin.IN,Pin.PULL_DOWN) IPS液晶の識別
if ips_check.value():
display_type = tft.M5STACK_IPS
else:
display_type = tft.M5STACK
Pin(33,Pin.OUT)
tft = display.TFT()
tft.init(display_type, width=240, height=320, mosi=23, miso=19, clk=18, cs=14, dc=27,
rst_pin=33, rot=tft.LANDSCAPE, bgr=True, splash=False, backl_pin=32, backl_on=1,
speed=60000000, color_bits=tft.COLOR_BITS24)
import display
tft=display.TFT()
tft.init(tft.M5STICKC, width=160, height=80, mosi=15, miso=14, clk=13, cs=5, dc=23,
rst_pin=18, rot=tft.LANDSCAPE_FLIP, bgr=True, splash=False,
speed=27000000)
縦画面の場合 width=160, height=80, rot=tft.LANDSCAPE_FLIP
ST7735S Datasheet
import display
tft=display.TFT()
tft.init(tft.M5STICKCP, width=240, height=135, mosi=15, miso=14, clk=13, cs=5, dc=23,
rst_pin=18, rot=tft.LANDSCAPE, bgr=True, splash=False,
speed=27000000)
ST7789V2 Datasheet
描画
tft.set_bg(tft.WHITE)
tft.set_fg(tft.BLACK)
tft.clear()
tft.rect(1, 1, 158, 78) 端から端まで線を引く
tft.font(tft.FONT_Ubuntu) フォント指定
tft.text(0, 0, 'test', tft.BLACK) テキストの表示
tft.image(0, 0, 'イメージファイル名')
tft.image(0, 0, '', type=tft.JPG, buf=バッファ)
バッファに格納されたイメージデータを表示できるようにオプションを追加しています。
※ バッファを指定する際はtypeの指定が必須。
jpegのデーコードはESP32のマスクROMに格納されたTiny JPEG Decompressorで行います。デコードされたデータは16x16ドットのブロックごとにDMA転送されます。
FONT_Default 13
FONT_Small 8x12
FONT_Ubuntu 16
FONT_DejaVu18/24
FONT_Comic 20x24
FONT_Minya 20x24
FONT_Tooney 32x37
FONT_DefaultSmall 10x10
FONT_7seg 18x31
詳しくは…
display・loboris/MicroPython_ESP32_psRAM_LoBo Wiki・GitHub
液晶ディスプレイを使う [ loborisリビルド版 + LovyanGFX ] (2020/08/07-)Index
lovyan03さん製作の超高速グラフィックライブラリ
LovyanGFXをMicroPythonでも使えるようにしました。
displayモジュールと共存出来ません、別途リビルドが必要です。
MicroPython[loboris+lovyan03リビルド版] ダウンロード (実験バージョン)
初期化
import lgfx
lgfx.init()
model=lgfx.getBoard()
lgfx.setColorDepth(lgfx.COLOR_BITS24)
if model==lgfx.M5Stack:
pass
elif model==lgfx.M5StickC:
lgfx.setRotation(lgfx.LANDSCAPE_FLIP)
elif model==lgfx.M5StickCPlus:
lgfx.setRotation(lgfx.LANDSCAPE_FLIP)
LovyanGFXでイメージデータを表示する
lgfx.drawJpgFile('イメージファイル名', 0, 0) x, yの位置指定が出来ます
lgfx.drawJpg(data, 0, 0) バッファに格納されたイメージデータ,x, yの位置指定が出来ます
lgfx.fillScreen()
lgfx.setColor(lgfx.WHITE)
lgfx.drawRect(0, 0, 240, 135, lgfx.CYAN)
lgfx.drawLine(0, 0, 50, 100, lgfx.color888(0xA0, 0xB0, 0xC0))
lgfx.setFont(lgfx.Font4)
lgfx.setTextColor(lgfx.BLACK)
lgfx.setFont(lgfx.Yellowtail_32)
lgfx.setTextSize(0.6, 0.6)
lgfx.drawString('hello, world',0, 100)
lgfx.setCursor(5, 5)
print(lgfx.getCursorX())
print(lgfx.getCursorY())
print(lgfx.textWidth('hello, world'))
print(lgfx.getTextSizeX())
print(lgfx.getTextSizeY())
lgfx.write('hello, world')
lgfx.setTextColor(lgfx.RED)
lgfx.print('hello, world')
lgfx.print(0.5)
lgfx.println('hello, world')
イメージデータを連続表示する (2019/04/09-)Index
M5Stack [ loborisリビルド版 ]
上の動画は内蔵フラッシュメモリーに保存した画像ファイル(jpeg)を連続表示して実現しています。
全画面(320×240)にjpeg画像1枚を表示する時間を測定すると約0.1ミリ秒でした。
全画面表示はLCD(ILI9341)のアクセススピードより、画像データ保管場所からの転送スピードに影響されます。
動画をムービーメーカーで7FPSに変換 →
Free Video to JPG Converterで画像に変換(フレーム毎) →
XnConvertでトリミング/リサイズ/再圧縮(10KB以内、320×240[Offset 56,11]、圧縮率=25/DCT=Float/Smooth=0/Sub=4,1,1/EXIFなし/ハフマンなし) →
FlexRena84でファイル名調整 → 同じ画像を間引く
4,1,1
クロマサブサンプリングを知る
MicroPythonでファイルシステムを使ってみる。
SDカードをマウントするIndex
初期化
import os
sd = machine.SDCard(slot=2, freq=40000000, cs=Pin(4))
os.mount(sd, '/sd')
LCDの初期化前に実行します。SDを初期化したときはLCD初期化の spi = machine.SPI(2) を実行しません。
import uos
uos.sdconfig(uos.SDMODE_SPI, clk=18, mosi=23, miso=19, cs=4, maxspeed=40)
uos.mountsd()
maxspeedはSPIインターフェースのクロック速度を指定します。(初期値40、省略できます)
import uos
uos.sdconfig(uos.SDMODE_SPI, clk=0, mosi=26, miso=36, cs=2, maxspeed=14)
uos.mountsd()
GPIOマトリックスにより任意のピンを割り当てます。maxspeedは14Mでないとアクセスできませんでした。
実際の転送速度はSDカードとの相性によるようです。
SPIインターフェースを使う際のトラブル&対策Index
ESP32モジュールにはHSPI、VSPI(ESP32非C/SだとそれぞれSPI2_HOST=1、SPI3_HOST=2)の2系統のSPIインターフェースが用意されています。(FlashとpsRAM用は別)
M5Stack Core2 / Fire / Basic
SDカードスロットとLCDはインターフェース(VSPI)を共有しています。外部用SPI端子もVSPIに接続されます。
SDカードとLCDを同時にアクセスする際には注意が必要。
MicroPython [ loboris ] でのSPIとDMA
SPIはDMAも使用しておりMicroPython [ loboris ] では現状下記に固定されています。
このためLCDと外部SPI機器は同時に使用出来ず、次のエラーが発生します。
spi_master: spi_bus_initialize(96): dma channel already in use
[SPI_UTILS]: spi initialization failed with rc=0x103
MicroPythonでセンサーを使ってみる。
ジャイロ、加速度、地磁気センサを使うIndex
なるべく共通化できるようにプログラムを記述しています。設定値は各センサーの仕様によります。
import mpu9250
import mpu6500
mpu = mpu6500.MPU6500(i2c,
accel_fs = mpu6500.ACCEL_FS_SEL_16G,
gyro_fs = mpu6500.GYRO_FS_SEL_500DPS,
accel_sf = mpu6500.SF_G,
gyro_sf = mpu6500.SF_DEG_S
)
imu = mpu9250.MPU9250(i2c, mpu6500=mpu)
M5Stack FIRE 初期版(PSRAMが認識しないもの)など一部機種の9軸センサーは地磁気センサーのI2Cアドレスが他のM5Stackと異なります。次の修正が必要です。
mpu6500.MPU6500.whoami=0x71
ak8963.AK8963.whoami=18
mag = ak8963.AK8963(i2c,address=0x0e)
mpu = mpu6500.MPU6500(i2c,
)
imu = mpu9250.MPU9250(i2c, mpu6500=mpu, ak8963=mag)
import mpu6886
imu = mpu6886.MPU6886(i2c,
accel_fs = mpu6886.ACCEL_FS_SEL_2G,
gyro_fs = mpu6886.GYRO_FS_SEL_500DPS,
accel_sf = mpu6886.SF_G,
gyro_sf = mpu6886.SF_DEG_S
)
import sh200q
i2c = machine.I2C(sda=21, scl=22)
imu = sh200q.SH200Q(i2c,
accel_fs = sh200q.ACCEL_FS_SEL_4G,
gyro_fs = sh200q.GYRO_FS_SEL_500DPS,
accel_sf = sh200q.SF_G,
gyro_sf = sh200q.SF_DEG_S,
)
imu.acceleration 加速[計算値] (xyzのタプル形式)
imu.acceleration_raw 加速[出力値] (xyzのタプル形式)
imu.gyro ジャイロ[計算値] (xyzのタプル形式)
imu.offset オフセットの取得設定 (xyzのタプル/リスト形式)
imu.whoami
imu.temp
imu.sh200q_ADCReset()
imu.sh200q_Reset()
Madgwickフィルタを使う [ originalリビルド版 / loborisリビルド版 ]Index
Madgwickフィルタを使う事によりロール、ピッチ、ヨー角が高精度に取得できます。
import madgwick
madgwick.begin(50) 50Hz:周波数が高い方が精度があがります ループ内の処理に合わせて決めます。
st=time.ticks_ms()
while True:
diff=time.ticks_diff(time.ticks_ms(), st)
if 20: 20ms:設定した周波数に対応したインターバル
sleep_ms(diff)
st=time.ticks_ms()
gyro=imu.gyro
acc=imu.acceleration
madgwick.updateIMU(gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2])
mag=imu.magnetic
madgwick.update(gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], mag[0], mag[1], mag[2])
print(madgwick.getRoll(), madgwick.getPitch(), madgwick.getYaw()) ロール、ピッチ、ヨーの取得
MicroPythonでBluetoothを使ってみる。
Bluetooth(Classic)を使う [ loborisリビルド版 + btstack ] (2019/08〜)Index
Bluetoothに関する情報は少ないのでMicroPythonに実装するのは苦労しました。モジュールさえ作ってしまえばあとの扱いは簡単です。
いまのところ
btstackとの組み合わせがとても安定してます。btstackは登場からかなり経っていることもあり情報もそこそこあってソースコードも読みやすく、サンプルコードも充実しています。でもAPI構成(
BLE ANCS Client API)が独自なのでMicroPythonのモジュールにするのはちょっと面倒です。
公式MicroPython(ESP32)がBluetooth(BLE)に対応しました。クラシックには対応してないようです。だんだんbtstackを使う必要性が薄れてくるかもしれません。
"bnep_lwip.c"
#define LWIP_TIMER_INTERVAL_MS 25
下記追加
#define TCP_MSS 536
#define TCP_SND_BUF (2 * TCP_MSS)
#define TCP_SND_QUEUELEN ((4 * TCP_SND_BUF + (TCP_MSS - 1))/(TCP_MSS))
"btstack_run_loop_freertos.c"
"btstack_run_loop_freertos.h"
static void btstack_run_loop_freertos_execute(void)から
int btstack_run_noloop_freertos_execute(uint32_t timeout)をつくる
下記の箇所は変更
//uint32_t timeout_ms = portMAX_DELAY; ←コメントアウト
uint32_t timeout_ms = timeout; ←変更
//xTaskNotifyWait(pdFALSE, 0xffffffff, NULL, pdMS_TO_TICKS(timeout_ms)); ←コメントアウト
BaseType_t res = xTaskNotifyWait(pdFALSE, 0xffffffff, NULL, pdMS_TO_TICKS(timeout_ms)); ←変更
return res; ←追加
sixaxis (DUALSHOCK3)
DUALSHOCK3のデータを直接取得することが出来ます。
import sixaxis
import utime
import ubinascii
print(ubinascii.hexlify(sixaxis.read_mac(),':').decode()) ホスト側MACアドレスの表示
def hid_handler(arg):
global sixaxis
if arg[0]==sixaxis.L2CAP_DATA_PACKET: ボタンの状態変化でCallBackルーチンが呼び出される
print(arg[2][2:]) DUALSHOCK3からの受信データ
sixaxis.setup(handler=hid_handler, mac_address='xx:xx:xx:xx:xx:xx')
引数(すべて省略可能)
handler = CallBackルーチンの指定
mac_address = ホスト側MACアドレスを変更する際に指定
all_events = Trueを指定すると全てのイベント通知でCallBackルーチンを呼び出す。Falseはボタンの変化時のみ呼び出し。デフォルト[False]
log_level = ログレベルをHCI_DUMP_LOG_LEVEL_ERROR,〜INFO,〜DEBUGから選択。デフォルト[〜ERROR]
dump = Trueを指定するとBTSTACKのデバッグダンプが出力される。デフォルト[False]
wait = DUALSHOCK3のバージョンによりWaitが必要だったため追加。デフォルト[10ms]
sixaxis.power_control(sixaxis.POWER_ON)
while True:
sixaxis.run_noloop_execute(10) 必ずメインループで実行 (10msのあいだbtstackの処理を実行)
packet=sixaxis.read_buffer() スティックの状態とsixaxisの値はread_bufferで取得
if packet is not None:
print(packet) DUALSHOCK3からの受信データ
utime.sleep(0.001)
spp[Server] (Serial Port Profile)
仮想シリアルポートとして動作します。PC側の設定は着信(デバイスが接続を開始する)
import spp
import utime
import ubinascii
print(ubinascii.hexlify(spp.read_mac(),':').decode()) ホスト側MACアドレスの表示
def spp_handler(arg):
global spp
if arg[0]==spp.RFCOMM_DATA_PACKET:
print(arg[2]) 受信データ
spp.send(arg[2]) データ送信(エコーバック)
spp.send(arg[2]) データ送信(エコーバック)
elif arg[0]==spp.HCI_EVENT_PACKET:
if arg[2][0]==spp.RFCOMM_EVENT_CHANNEL_OPENED:
print('OPENED')
elif arg[2][0]==spp.RFCOMM_EVENT_CHANNEL_CLOSED:
print('CLOSED')
elif arg[2][0]==spp.HCI_EVENT_USER_CONFIRMATION_REQUEST:
value=arg[2][8] + (arg[2][9]<<8) + (arg[2][10]<<16) + (arg[2][11]<< 24
)
print('CONFIRMATION_REQUEST ', value)
spp.setup(spp_handler)
第1引数 CallBackルーチンの指定
name = サービス名を変更する際に指定。デフォルト[SPP Server]
cod = class of deviceの値を指定。デフォルト[0x007a020c]
mac_address = ホスト側MACアドレスを変更する際に指定
all_events = Trueを指定すると全てのイベント通知でCallBackルーチンを呼び出す。Falseはボタンの変化時のみ呼び出し。デフォルト[False]
log_level = ログレベルをHCI_DUMP_LOG_LEVEL_ERROR,〜INFO,〜DEBUGから選択。デフォルト[〜ERROR]
dump = Trueを指定するとBTSTACKのデバッグダンプが出力される。デフォルト[False]
spp.power_control(spp.POWER_ON)
while True:
spp.run_noloop_execute(10) 必ずメインループで実行 (10msのあいだbtstackの処理を実行)
utime.sleep(0.001)
spp[Client] (Serial Port Profile)
仮想シリアルポートとして動作します。PC側の設定は発信(コンピュータが接続を開始する)
import spp_cl
import utime
import ubinascii
print(ubinascii.hexlify(spp_cl.read_mac(),':').decode()) ホスト側MACアドレスの表示
def spp_handler(arg):
global spp_cl
if arg[0]==spp_cl.RFCOMM_DATA_PACKET:
print(arg[2]) 受信データ
elif arg[0]==spp_cl.HCI_EVENT_PACKET:
if arg[2][0]==spp_cl.GAP_EVENT_INQUIRY_RESULT:
packet=arg[2]
for i in range(len(packet) // 2):
packet[i],packet[-1-i] = packet[-1-i],packet[i]
addr=ubinascii.hexlify(packet[10:16],':').decode()
cod=ubinascii.hexlify(packet[6:9]).decode()
print("DEVICE ADDR["+addr+"] COD["+cod+"]")
if arg[2][0]==spp_cl.GAP_EVENT_INQUIRY_COMPLETE:
print('CONNECT')
if arg[2][0]==spp_cl.RFCOMM_EVENT_CHANNEL_OPENED:
if arg[2][2]:
print('OPEN ERROR')
else:
print('OPENED')
spp_cl.send('test') データ送信(テスト)
elif arg[2][0]==spp_cl.RFCOMM_EVENT_CHANNEL_CLOSED:
print('CLOSED')
elif arg[2][0]==spp_cl.HCI_EVENT_USER_CONFIRMATION_REQUEST:
value=arg[2][8] + (arg[2][9]<<8) + (arg[2][10]<<16) + (arg[2][11]<< 24
)
print('CONFIRMATION_REQUEST ', value)
spp_cl.setup(spp_handler, address='xx:xx:xx:xx:xx:xx')
第1引数 CallBackルーチンの指定
address = 接続するSPPサーバーのアドレスを指定省略する。デフォルト[指定しない]
cod = class of deviceの値を指定。デフォルト[0x007a020c]
mac_address = ホスト側MACアドレスを変更する際に指定
all_events = Trueを指定すると全てのイベント通知でCallBackルーチンを呼び出す。Falseはボタンの変化時のみ呼び出し。デフォルト[False]
log_level = ログレベルをHCI_DUMP_LOG_LEVEL_ERROR,〜INFO,〜DEBUGから選択。デフォルト[〜ERROR]
dump = Trueを指定するとBTSTACKのデバッグダンプが出力される。デフォルト[False]
spp_cl.power_control(spp_cl.POWER_ON)
while True:
spp_cl.run_noloop_execute(10) 必ずメインループで実行 (10msのあいだbtstackの処理を実行)
utime.sleep(0.001)
hid (Wii Remote/Other)
WiiリモコンなどのHIDタイプのゲームコントローラからデータを直接取得することが出来ます。
import hid
import utime
def hid_handler(arg):
global hid
if arg[0]==hid.L2CAP_DATA_PACKET: ボタンの状態変化でCallBackルーチンが呼び出される
print('L2CAP_DATA_PACKET ', arg[2])
hid.setup(hid_handler, 'xx-xx-xx-xx-xx-xx')
第1引数 CallBackルーチンの指定
第2引数 接続するHIDデバイスのアドレスを指定
all_events = Trueを指定すると全てのイベント通知でCallBackルーチンを呼び出す。Falseはボタンの変化時のみ呼び出し。デフォルト[False]
log_level = ログレベルをHCI_DUMP_LOG_LEVEL_ERROR,〜INFO,〜DEBUGから選択。デフォルト[〜ERROR]
dump = Trueを指定するとBTSTACKのデバッグダンプが出力される。デフォルト[False]
hid.power_control(hid.POWER_ON)
while True:
hid.run_noloop_execute(10) 必ずメインループで実行 (10msのあいだbtstackの処理を実行)
utime.sleep(0.001)
hid[Slave] (Keyboard/Mouse)
HIDのスレーブ動作をします。キーボード、マウスとして機能します。
import hid_slv
hid_slv.setup(descriptor=bytearray(descriptor))
descriptor = レポートディスクリプタのバイナリリストを指定。指定しないとKeyboardになります。
name = サービス名を変更する際に指定。デフォルト[BTstack HID Keyboard]
cod = class of deviceの値を指定。デフォルト[0x2540]
log_level = ログレベルをHCI_DUMP_LOG_LEVEL_ERROR,〜INFO,〜DEBUGから選択。デフォルト[〜ERROR]
dump = Trueを指定するとBTSTACKのデバッグダンプが出力される。デフォルト[False]
hid_slv.power_control(spp_cl.POWER_ON)
while True:
hid_slv.run_noloop_execute(10)
if not pb.value():
hid_slv.send(b'\xa1\x00\x00\x04\x00\x00\x00\x00\x00')
hid_slv.send(b'\xa1\x00\x00\x00\x00\x00\x00\x00\x00')
キーボードの場合、2バイト目がModifier、4〜9目バイトがキーコード(6キー同時押し)
キーコードとキーの割り当てarmory-keyboard
descriptor=(
0x09, 0x06,
0xa1, 0x01,
0x75, 0x01,
0x95, 0x08,
0x05, 0x07,
0x19, 0xe0,
0x29, 0xe7,
0x15, 0x00,
0x25, 0x01,
0x81, 0x02,
0x95, 0x01,
0x75, 0x08,
0x81, 0x03,
0x95, 0x05,
0x75, 0x01,
0x05, 0x08,
0x19, 0x01,
0x29, 0x05,
0x91, 0x02,
0x95, 0x01,
0x75, 0x03,
0x91, 0x03,
0x95, 0x06,
0x75, 0x08,
0x15, 0x00,
0x25, 0x65,
0x05, 0x07,
0x19, 0x00,
0x29, 0x65,
0x81, 0x00,
0xc0 )
hciパケットをAndroidでキャプチャして解析
通知 [Androidシステム バグレポート#xの生成中]
通知 [Androidシステム バグレポート#xの記録完了]
バグレポートを共有するにはタップします
バグレポートには、システムのさまざまなログファイルのデータが含まれており、他人に知られたくないデータ(アプリの使用状況、位置情報など)が含まれている場合もあります。バグレポートの共有は、信頼できる人やアプリとのみ行ってください。
内部共通ストレージに下記のファイルが作成されます
bugreport-xxxxx_xxxxxxxxxxxxxxxxxx-yyyy-mm-dd-HH-MM-SS.zip
Bluetooth(BLE)を使う [ originalリビルド版 ] (2023/04〜)Index
ESP32S3からClassicは非対応になりBLEのみ対応となりました。
APIもNimBLEベースになっています。
BLE Keyboard
AtomS3をBLE Keyboardにする例です。
Heerkog氏の
hid_services.pyを組み込んでいます。
from machine import Pin
button = Pin(41, Pin.IN)AtomS3のボタンを設定します。
import time
from hid_services import Keyboard
kb = Keyboard("Keyboard")
kb.start()
while True:
if not button.value():
if kb.get_state() is Keyboard.DEVICE_CONNECTED:
kb.set_keys(0x1A, 0x00, 0x00, 0x00, 0x00) "w"を送信
kb.notify_hid_report()
time.sleep_ms(2)
kb.set_keys()
kb.notify_hid_report()
time.sleep_ms(200)
elif kb.get_state() is Keyboard.DEVICE_IDLE:
kb.start_advertising()
i = 10
while i > 0 and kb.get_state() is Keyboard.DEVICE_ADVERTISING:
time.sleep(3)
i -= 1
if kb.get_state() is Keyboard.DEVICE_ADVERTISING:
kd.stop_advertising()
if kb.get_state() is Keyboard.DEVICE_CONNECTED:
time.sleep_ms(20)
else:
time.sleep(2)
オリジナルUUIDの生成
こちらで生成できます。version1を指定
https://www.uuidgenerator.net/version1
MicroPythonでネットワークを使ってみる。
Wi-Fi アクセスポイントに接続するIndex
import time
import network
import setting SSID,PASSWARDを保存した設定ファイル
if 'ap_if' not in locals():
ap_if = network.WLAN(network.STA_IF)
if not ap_if.isconnected():
ap_if.active(True)
ap_if.connect(setting.MY_SSID, setting.MY_PASSWARD)
while not ap_if.isconnected(): 接続待ち
print(".", end='')
time.sleep(0.1)
FTPサーバーに接続するIndex
[ originalリビルド版 ]
import ftp
ftp.ftpserver()
robert-hh氏の
FTP-Server-for-ESP8266-ESP32-and-PYBDを組み込んでいます。
簡易バージョンです。クライアントがセッションを閉じると終了。1 つのセッションのみがサポートされます。
MicroPythonでCPU制御を使ってみる。
DeepSleepを使うIndex
DeepSleep中は消費電力を節約できます。
import machine
指定期間(msec)DeepSleepした後に自動復帰
machine.deepsleep(3000)
任意の秒数(msec)を指定
指定のボタンを押したときにDeepSleepから復帰
rtc = machine.RTC()
rtc.wake_on_ext0(39, 0) 復帰に使うGPIO番号とレベル(0=LowActive / 1=HighActive)を指定
machine.deepsleep()
次のメソッドでDeepSleepからの復帰を確認できます。
machine.wake_reason()
machine.wake_description()
動作クロックを変更するIndex
動作クロックには2MHz, 80Mhz, 160MHz, 240MHzを指定できます。
import machine
machine.freq(240) 任意の動作クロックを指定、省略した場合は現在のクロックを返します。
MicroPythonでデバイスを使ってみる。
MicroPythonからパワーマネジメントIC AXP192にアクセスする。
import axp192
i2c = machine.I2C(sda=21, scl=22)
pm=axp192.AXP192(i2c)
pm.ScreenBreath(brightness) バックライト精度調整
pm.EnableCoulombcounter()
pm.DisableCoulombcounter()
pm.StopCoulombcounter()
pm.ClearCoulombcounter()
pm.GetCoulombchargeData()
pm.GetCoulombdischargeData()
pm.GetCoulombData()
pm.GetVbatData()
pm.GetVinData()
pm.GetIinData()
pm.GetVusbinData()
pm.GetIusbinData()
pm.GetIchargeData()
pm.GetIdischargeData()
pm.GetTempData()
pm.GetPowerbatData()
pm.GetVapsData()
pm.SetSleep()
pm.GetWarningLeve()
pm.GetBtnPress()
pm.GetBatState()
pm.GetBatVoltage() バッテリー電圧
pm.GetBatCurrent()
pm.GetVinVoltage()
pm.GetVinCurrent()
pm.GetVBusVoltage() USB電圧
pm.GetVBusCurrent()
pm.GetTempInAXP192()
pm.GetBatPower()
pm.GetBatChargeCurrent()
pm.GetAPSVoltage()
pm.GetBatCoulombInput()
pm.GetBatCoulombOut()
pm.GetWarningLevel()
pm.SetCoulombClear()
pm.SetLDO2(True/False)
pm.ShutDown() シャットダウン ← 独自に追加
MicroPythonからリアルタイムクロックBM8563にアクセスする。
import bm8563
i2c = machine.I2C(sda=21, scl=22)
rtc=bm8563.BM8563(i2c)
rtc.GetBm8563Time() (年, 月, 日, 時, 分, 秒, 週)が戻ります。
rtc.GetTime() (時, 分, 秒)が戻ります。
rtc.SetTime(Hours, Minutes, Seconds) 時間を設定します。
rtc.GetData() (年, 月, 日, 週)が戻ります。
rtc.SetData(Year, Month, Date, WeekDay) 日付を設定します。
import camera
Pin(18, Pin.OUT,value=0)
sleep_ms(500);
camera.init(0, format=camera.RGB565,framesize=camera.FRAME_VGA, quality=0,
d0=3, d1=42, d2=46, d3=48, d4=4, d5=17, d6=11, d7=13,
vsync=10, href=14, pclk=40, pwdn=-1, reset=-1, xclk=21, siod=12, sioc=9,
xclk_freq=camera.XCLK_20MHz, fb_size=2, fb_location=camera.PSRAM)
camera.mirror(0)
buf = camera.capture_jpeg()
bufに取得したjpegイメージデータが格納される
モジュール内の関数
capture
framesize, quality, flip, mirror, contrast, saturation, brightness, speffect, whitebalance
deinit
M5AtomS3R Camera Kit (8MB PSRAM)で扱いやすいようにモジュールを改良しています。
import camera
Pin(18, Pin.OUT,value=0)
sleep_ms(500);
camera.init(0, fb_size=1)
camera.mirror(0)
buf = camera.capture()
bufに取得したjpegイメージデータが格納される
モジュール内の関数
capture
framesize, quality, flip, mirror, contrast, saturation, brightness, speffect, whitebalance
deinit
import camera
camera.init([,reset] [,xclk] [,siod] [,sioc] [,d7] [,d6] [,d5] [,d4] [,d3] [,d2] [,d1] [,d0]
[,vsync] [,href] [,pclk] [,freq] [,timer] [,channel] [,pixformat]
[,framesize] [,quality] [,fb_count]) イニシャライズ
camera.sensor_set([,pixformat] [,framesize] [,contrast] [,brightness] [,saturation]
[,gainceiling] [,quality] [,colorbar] [,whitebal] [,gain_ctrl] [,exposure_ctrl]
[,hmirror] [,vflip] [,aec2] [,awb_gain] [,agc_gain] [,aec_value] [,special_effect]
[,wb_mode] [,ae_level] [,dcw] [,bpc] [,wpc] [,raw_gma] [,lenc])
fb=camera.get_fb() フレームバッファ構造体のポインタを返す。
img=camera.buffer(fb) imgに取得したイメージデータが格納される
width=camera.width(fb) イメージ幅
height=camera.height(fb) イメージ高さ
camera.return_fb(fb) 取得イメージ解放(データの処理後は必ず解放)
camera.deinit()
公開準備中…
pixformat = RGB565, YUV422, GRAYSCALE, JPEG, RGB888
framesize = QQVGA, QQVGA2, QCIF, HQVGA, QVGA, CIF, VGA, SVGA, XGA, SXGA, UXGA
contrast = -2 - 2 コントラスト
brightness = -2 - 2 輝度
saturation = -2 - 2 彩度
gainceiling = GAINCEILING_2X - 128X
quality = 0 - 63
colorbar = True, False
whitebal = True, False オートホワイトバランス
gain_ctrl = True, False
exposure_ctrl = True, False 露出
hmirror = True, False 水平反転
vflip = True, False 垂直反転
aec2 = True, False
awb_gain = True, False
agc_gain = 0 - 30
aec_value = 0 - 1200
special_effect = NORMAL, ANTIQUE, MONO, NEGATIVE, BLUISH, GRENNISH, REDDISH, MONO_NEGATIVE
wb_mode = 0 - 4
ae_level = -2 - 2
dcw = True, False
bpc = True, False
wpc = True, False
raw_gma = True, False
lenc = True, False
OV2640ピンアサイン
1 | STROBE/NC | 2 | AGND | 3 | SIO_D(22) | 4 | AVDD(3.3V) |
5 | SIO_C(23) | 6 | RESET(15) | 7 | VSYNC(25) | 8 | PWDN |
9 | HREF(26) | 10 | DVDD(1.8V) | 11 | DOVDD(2.8V) | 12 | Y9(D7=19) |
13 | XCLK(27) | 14 | Y8(D6=36) | 15 | DGND | 16 | Y7(D5=18) |
17 | PCLK(21) | 18 | Y6(D4=39) | 19 | Y2(D0=32) | 20 | Y5(D3=5) |
21 | Y3(D1=35) | 22 | Y4(D2=34) | 23 | Y1 | 24 | Y0 |
DualShock/2を使うIndex
DualShock/2はSPIインターフェースで通信できます。
DualShock/2の資料はこちらが詳しくとても分かりやすいです。
プレイステーション2専用ローリングスイッチ技術資料
初期化
M5Stack [ loborisリビルド版 ] (2018/06/24)
spi = SPI(
spihost=SPI.VSPI,
baudrate = 250000, polarity = 1,
phase = 0,
bits = 8, firstbit = SPI.LSB,
duplex = True,
sck = Pin(18, Pin.OUT), mosi = Pin(23, Pin.OUT), miso = Pin(19, Pin.IN),
cs = Pin(26, Pin.OUT))
MicroPythonでWebAPIを使ってみる。 [ originalリビルド版 ]
Google Cloud Speech-to-Text をWebAPIで使う (2024/01〜)Index
import requests_direct as requests requests_directはカスタムrequestsモジュール
import json
url = "https://speech.googleapis.com/v1/speech:recognize?key=" + API_KEY
head1 = json.dumps({
"config" : {
"encoding" : "LINEAR16",
"sampleRateHertz" : 12000, 録音データのサンプルレート
"languageCode" : "ja-JP"
}
})
head2 = json.dumps({
"audio" : {
"content" : ""
}
})
tail = "\"}}"
head = head1[0:len(head1)-1] + "," + head2[1:len(head2)-3]
response = requests.post(url, head=head, wavedata=mv[0:44+datalength], tail=tail, chunk_size=4096)
results = json.loads(response.text)
if results.get('results') is not None:
for result in results["results"]:
talk_string = result["alternatives"][0]["transcript"]
print(talk_string,"confidence:" , result["alternatives"][0]["confidence"])
waveデータの生成は
マイクを使うを参照してください。
ChatGPT AIチャットボットをWebAPIで使う (2023/04〜)Index
import urequests as requests
import ujson as json
content=None Noneにすると前回の会話をリセット
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}
data = {
"model": "gpt-3.5-turbo",
"messages": [
{"role": "system", "content": "あなたはの使命は地球環境の維持です。"}, AIのキャラクター?設定
{"role": "system", "content": "あなたの友達はイブです。"},systemは複数指定可能
{"role": "user", "content": "一緒に踊りましょう"}, AIへのユーザーからのメッセージ
]
}
if content is not None:
data['messages'][0].update({"role": "assistant", "content": content}) 前回の会話の継続
response = requests.post(
"https://api.openai.com/v1/chat/completions",
headers=headers, data=json.dumps(data).encode("utf-8"))
response_data = json.loads(response.text)
choices = response_data['choices']
for choice in choices:
if choice['finish_reason'] == 'stop':
content = choice['message']['content']
break
print(content) AIの返答を表示
VOICEVOXをWebAPIで使う (2023/04〜)Index
VOICEVOXサーバーの起動 (Windows)
AppData\Local\programs\VOICEVOX\run.exe --host VOICEVOXサーバーのアドレス
VOICEVOXサーバーへのリクエスト
import urequests as requests
import webapi サポートモジュール
address="VOICEVOXサーバーのアドレス:50021/"
res1 = requests.post(address+'audio_query?text='+webapi.encode_url_string(content)+'&speaker=1&outputStereo=false')
res2 = requests.post(address+'synthesis?speaker=1&outputStereo=false',data = res1.text.encode("utf-8"))
キャラクターボイスを変えるときはspeakerの値を変えます。
webapi.play_wav(res2.raw, spk) インスタンス変数を参照しているので要注意
spkの初期化は
MP3ファイルを再生するを参照してください。