このページの目次
ショップ
取扱説明書
- 準備中
製品概要
PICOLAB-MUSICは、PICO用オーディオ実験用拡張ボードです。

PICOLABシリーズの拡張ボード群は、CircuitPythonやMicroPython を使用したプログラミングでの利用を主眼として、Raspberry Pi PICOの拡張ボードとして開発されました。PICOLAB-MUSICは、タッチスイッチによる鍵盤や、D級アンプ・スピーカーを備えており、CircuitPythonとsynthioライブラリを使用して楽器の作成や実験を行うことができます。
- Raspberry Pi PICO/PICO W用の多機能拡張ボードです。PICO 2では利用できません。
- CircuitPythonでのプログラミングに重点を置いて開発されています。
- PICOのプログラム実験で利用される主要な入出力を搭載しており、PICOLAB-MUSICだけで様々な実験を行うことができます。また、拡張端子を使用して、様々なセンサーを追加で利用できます。
- 出力として、TFTディスプレイ、カラーLEDx13、D級アンプ・スピーカーを搭載しています。
- TFTディスプレイは320x170ピクセルです。
- 入力として、タクトスイッチx2、タッチスイッチx13を搭載しています。
- ストレージとして、マイクロSDカードソケットを搭載しています。
- 基板上部に各種のセンサー等を接続できるI2C端子を装備しています。
- Raspberry Pi PICO/PICO W等のMCUボードは付属しませんので別途お買い求めください。

入出力とセンサー
- 出力
- TFT 320x170 (SPI)
- カラーLED x 13
- D級アンプとスピーカー(PWMサウンド)
- 入力
- タクトスイッチ x2
- タッチスイッチ x13
- ストレージ (SPI)
- マイクロSDカード ソケット
- 拡張コネクタ
- スライドボリューム
- I2C拡張コネクタ (接続機器はオプション)
- 温度、湿度センサー
- 照度センサー
- 加速度、ジャイロセンサー
- レーザー距離センサー
- 人感センサー
CircuitPythonでのプログラム例
CircuitPythonのシンセサイザー機能 synthio を使用した電子ピアノのプログラム例です。
- タッチキーになっている鍵盤を押すとその音が鳴ります。
- ポリフォニー機能を持ち、ドミソなどの複数の鍵盤を押すと複数の音が出ます。
- 左のSW1を押しながら鍵盤を押すと1オクターブ高い、SW2を押しながら鍵盤を押すと1オクターブ低い音が鳴ります。
- 鍵盤を押すとその上部のLEDが点灯します。
- すみません。TFT表示は使っていません。。。
恐ろしい時代になりました。私は1行もプログラムを書いていません。
指示をしたらGemini君が書いてくれました。といっても、一筋縄ではいきませんでしたが。
synthioを使用したプログラム例は少なく、また、バージョンの違いで機能がかなり変わってきているので、現在のバージョンで適切に動くプログラムを書かせるのに少々苦労しました。細かい指示を繰り返しプログラムを作成させる必要がありました。とは言っても、多くの場合には、ここでエラーが出るなどを文句(状況報告)をすると勝手に再調査して書き直してくれたんですけどね。ただ、なかなか音が出るようにならないので、何度もそろそろ自分で書こうと思いもしました。
それでも、くじけずにGemini君に頑張ってもらって音が出た時は、やった!という感じでしたね。
import board
import audiopwmio
import audiomixer
import synthio
import re
import touchio
import time
import neopixel
import sys
import digitalio
# --- タッチセンサーとノート、LEDのマッピング ---
# GP_ピン番号: {"note": "ノート名", "led_idx": 対応するWS2812Bのインデックス, "is_sharp": シャープ音ならTrue} の形式で定義します。
TOUCH_SENSOR_MAPPING = {
board.GP0: {"note": "C4", "led_idx": 0, "is_sharp": False},
board.GP1: {"note": "C#4", "led_idx": 1, "is_sharp": True},
board.GP2: {"note": "D4", "led_idx": 2, "is_sharp": False},
board.GP3: {"note": "D#4", "led_idx": 3, "is_sharp": True},
board.GP6: {"note": "E4", "led_idx": 4, "is_sharp": False},
board.GP7: {"note": "F4", "led_idx": 5, "is_sharp": False},
board.GP8: {"note": "F#4", "led_idx": 6, "is_sharp": True},
board.GP9: {"note": "G4", "led_idx": 7, "is_sharp": False},
board.GP10: {"note": "G#4", "led_idx": 8, "is_sharp": True},
board.GP11: {"note": "A4", "led_idx": 9, "is_sharp": False},
board.GP12: {"note": "A#4", "led_idx": 10, "is_sharp": True},
board.GP13: {"note": "B4", "led_idx": 11, "is_sharp": False},
board.GP22: {"note": "C5", "led_idx": 12, "is_sharp": False},
}
# --- WS2812B LED設定 ---
NEOPIXEL_PIN = board.GP21
NUM_PIXELS = len(TOUCH_SENSOR_MAPPING)
PIXEL_BRIGHTNESS = 0.05
PIXEL_COLOR_NATURAL = (255, 255, 0) # 黄色
PIXEL_COLOR_SHARP = (0, 255, 0) # 緑色
pixels = neopixel.NeoPixel(NEOPIXEL_PIN, NUM_PIXELS, brightness=PIXEL_BRIGHTNESS, auto_write=False)
pixels.fill((0, 0, 0))
pixels.show()
if NUM_PIXELS != len(TOUCH_SENSOR_MAPPING):
print("エラー: NUM_PIXELSとTOUCH_SENSOR_MAPPINGの要素数が一致しません!")
sys.exit(1)
# --- オクターブシフトボタン設定 ---
OCTAVE_DOWN_PIN = board.GP27
OCTAVE_UP_PIN = board.GP26
octave_down_button = digitalio.DigitalInOut(OCTAVE_DOWN_PIN)
octave_down_button.direction = digitalio.Direction.INPUT
octave_up_button = digitalio.DigitalInOut(OCTAVE_UP_PIN)
octave_up_button.direction = digitalio.Direction.INPUT
current_octave_shift = 0
# --- オーディオ出力ピンの定義 ---
AUDIO_PIN = board.GP20
# --- オーディオ設定 ---
SAMPLE_RATE = 22050
audio = audiopwmio.PWMAudioOut(AUDIO_PIN)
mixer = audiomixer.Mixer(voice_count=len(TOUCH_SENSOR_MAPPING), sample_rate=SAMPLE_RATE, channel_count=1)
synth = synthio.Synthesizer(sample_rate=SAMPLE_RATE, channel_count=1)
audio.play(mixer)
mixer.play(synth, voice=0)
# --- MIDIノート番号計算 ---
NOTE_PARSE_REGEX = re.compile(r"([A-G]#?)([0-9])")
def note_to_midi_number(note_name_str):
match = NOTE_PARSE_REGEX.match(note_name_str)
if not match:
raise ValueError(f"不正なノート名フォーマット: '{note_name_str}'. 'C4' や 'C#4' のような形式を期待します。")
octave = int(match.group(2))
note = match.group(1)
notes_midi_offset = {
"C": 0, "C#": 1, "D": 2, "D#": 3, "E": 4, "F": 5,
"F#": 6, "G": 7, "G#": 8, "A": 9, "A#": 10, "B": 11
}
if note not in notes_midi_offset:
raise ValueError(f"認識できないノート部分: '{note}' in '{note_name_str}'.")
midi_note_number = (octave + 1) * 12 + notes_midi_offset[note]
return midi_note_number
try:
midi_note_base_numbers = {}
for pin_obj, mapping_data in TOUCH_SENSOR_MAPPING.items():
note_name = mapping_data["note"]
midi_note_base_numbers[note_name] = note_to_midi_number(note_name)
print("\n--- 計算された基本MIDIノート番号 ---")
for note_name, midi_num in midi_note_base_numbers.items():
print(f"{note_name}: {midi_num}")
print("---------------------------------")
except ValueError as e:
print(f"MIDIノート番号計算エラー: {e}")
sys.exit(1)
# --- タッチセンサー(Capacitive Touch)設定 ---
touch_sensors = {}
print("\n--- タッチセンサー設定 ---")
for pin_obj, mapping_data in TOUCH_SENSOR_MAPPING.items():
note_name = mapping_data["note"]
led_idx = mapping_data["led_idx"]
is_sharp = mapping_data["is_sharp"]
try:
sensor = touchio.TouchIn(pin_obj)
touch_sensors[pin_obj] = {
"object": sensor,
"note": note_name,
"led_idx": led_idx,
"is_sharp": is_sharp,
"is_pressed": False
}
print(f"{pin_obj} ({note_name}, LED#{led_idx}, {'#' if is_sharp else ' '}) をタッチセンサーとして設定しました。")
except ValueError as e:
print(f"警告: {pin_obj} はタッチセンサーとして設定できませんでした ({e})。このピンはスキップされます。")
continue
if not touch_sensors:
print("エラー: 設定できたタッチセンサーが一つもありません。Picoが対応しているか確認してください。")
sys.exit(1)
print("\n電子ピアノの準備ができました!")
print("設定されたGPピンをタッチして音を鳴らしてください。")
# --- メインループ ---
active_midi_notes = set()
active_led_states = {}
while True:
# --- オクターブシフトボタンのチェック ---
temp_octave_shift = 0
if not octave_down_button.value: # GP27が押されている
temp_octave_shift = -12
elif not octave_up_button.value: # GP26が押されている
temp_octave_shift = 12
if temp_octave_shift != current_octave_shift:
current_octave_shift = temp_octave_shift
if active_midi_notes:
synth.release_all()
active_midi_notes.clear()
print(f"オクターブシフト: {current_octave_shift/12:+.0f}オクターブ")
current_pressed_midi_notes = set()
next_led_states = {}
# 全てのタッチセンサーの状態をチェック
for pin_obj, sensor_data in touch_sensors.items():
sensor = sensor_data["object"]
note_name = sensor_data["note"]
led_idx = sensor_data["led_idx"]
is_sharp = sensor_data["is_sharp"]
if sensor.value:
midi_note = midi_note_base_numbers[note_name] + current_octave_shift
midi_note = max(0, min(127, midi_note))
current_pressed_midi_notes.add(midi_note)
next_led_states[led_idx] = PIXEL_COLOR_SHARP if is_sharp else PIXEL_COLOR_NATURAL
if not sensor_data["is_pressed"]:
sensor_data["is_pressed"] = True
else:
if sensor_data["is_pressed"]:
sensor_data["is_pressed"] = False
# 音の処理
newly_active_midi_notes = current_pressed_midi_notes - active_midi_notes
# ここを修正: current_pressed_notes -> current_pressed_midi_notes
newly_inactive_midi_notes = active_midi_notes - current_pressed_midi_notes
for midi_note in newly_active_midi_notes:
synth.press([midi_note])
active_midi_notes.add(midi_note)
for midi_note in newly_inactive_midi_notes:
synth.release([midi_note])
active_midi_notes.remove(midi_note)
if not active_midi_notes: # active_midi_notes が空になったら
synth.release_all() # 全ての音をリリース (念のため)
# LEDの処理
for idx in list(active_led_states.keys()):
if idx not in next_led_states:
pixels[idx] = (0, 0, 0)
del active_led_states[idx]
for idx, color in next_led_states.items():
if idx not in active_led_states or active_led_states[idx] != color:
pixels[idx] = color
active_led_states[idx] = color
pixels.show()
time.sleep(0.01)
回路図

関連製品
PICOLAB-SENS
PICOLABシリーズのフラグシップとなるPICO拡張多機能実験ボードで、プログラミングで多用される多くの入出力やセンサーを装備しており、このボードだけで多様な実験を行うことができます。
PICOLAB-BASE
PICOLAB-SENSからセンサー群とD級アンプ・スピーカーが取り除かれた廉価版です。
PICOLAB-HDMI
HDMIコネクタを備えており、CircuitPythonやArduinoとpicodviライブラリを使用してモニタ表示を利用したゲームやデジタルサイネージなどの作成や実験を行うことができます。
