パソコンからワイヤレスでRaspberry Pi Pico Wに音楽を演奏させてみた。

未分類

Raspberry Pi Pico W(受信側)のプログラム。
Picoでは、PWM(パルス幅変調)を使って擬似的に音を鳴らすのが一般的です。

import network, socket
from machine import Pin, PWM
import time

# Wi-Fi接続情報
ssid = 'あなたのSSID'
password = 'あなたのパスワード'

# Wi-Fi接続の初期化
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)

# IPアドレスの固定設定
# 1つ目の192.168.XX.XXの部分には、Pico Wで使用するIPアドレス
# 2つ目の192.168.XX.XXの部分には、Wi-FiルーターのIPアドレス
sta_if.ifconfig(('192.168.XX.XX', '255.255.255.0', '192.168.XX.XX', '8.8.8.8')) 

# Wi-Fiに接続
sta_if.connect(ssid, password)

# Wi-Fi接続が確立するまで待機
while not sta_if.isconnected():
    time.sleep(0.1)


# ソケットの準備
# ここではポート8080をリッスン(受信待機)するように設定
addr = socket.getaddrinfo(sta_if.ifconfig()[0], 8080)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)

# ブザーをPWM制御で鳴らす準備
buzzer = PWM(Pin(16))  # GPIO16に接続したブザー

# 音階周波数の定義
REST=    0
G3,Ab3,A3,Bb3,B3 =  196,208,220,233,247
C4, Db4, D4, Eb4, E4, F4, Gb4, G4, Ab4, A4, Bb4, B4 = 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494
C5, Db5, D5, Eb5, E5, F5, Gb5, G5, Ab5, A5, Bb5, B5 = 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988
C6, Db6, D6, Eb6, E6, F6, Gb6, G6, Ab6, A6, Bb6, B6 =1047,1109,1175,1245,1319,1397,1480,1568,1661,1760,1865,1976

# 音を鳴らす関数
def tone(frequency, duration):
    if frequency == REST:
        # 休符の場合は音を鳴らさずに待機
        time.sleep(duration)
        return
    buzzer.freq(frequency)       # 周波数を設定
    buzzer.duty_u16(30000)       # デューティ比を設定(音を鳴らす)
    time.sleep(duration)         # 指定時間鳴らす
    buzzer.duty_u16(0)           # 音を止める
    time.sleep(0.002)            # 少し間を空ける

# 勇者のテーマを再生する関数
def yusya():
    BPM = 208              # 曲のテンポ
    BEAT = 60 / BPM        # 1拍の長さ(秒)

    # メロディ(音階と長さの組み合わせ)
    melody = [
        # イントロ部分
        [C5, BEAT/2], [D5, BEAT/2], [E5, BEAT/2], [C5, BEAT/2],
        [C5, BEAT/2], [D5, BEAT/2], [E5, BEAT/2], [C5, BEAT/2],

        # メインメロディ
        [G4, BEAT], [C5, BEAT], [E5, BEAT], [G5, BEAT],
        [A5, BEAT], [G5, BEAT], [E5, BEAT], [C5, BEAT],

        [A4, BEAT], [C5, BEAT], [E5, BEAT], [G5, BEAT],
        [F5, BEAT], [E5, BEAT], [C5, BEAT], [A4, BEAT],

        # サビ
        [F4, BEAT], [A4, BEAT], [C5, BEAT], [F5, BEAT],
        [E5, BEAT], [D5, BEAT], [C5, BEAT], [A4, BEAT],

        [G4, BEAT], [B4, BEAT], [D5, BEAT], [G5, BEAT],
        [F5, BEAT], [E5, BEAT], [C5, BEAT], [G4, BEAT],

        # エンディング
        [C5, BEAT*2], [D5, BEAT*2], [E5, BEAT*2],
        [C5, BEAT*2], [G4, BEAT*2], [C5, BEAT*2], [REST, BEAT]
    ]

    # メロディを順番に再生
    for m in melody:
        tone(m[0], m[1])

# 接続待機
print('Waiting for CALL...')

while True:
    # クライアントからの接続を受け付ける
    cl, addr = s.accept()
    print('Connected from', addr)

    # 受信データを取得
    data = cl.recv(1024)
    print('Received:', data)
    
    # "CALL"というデータが届いたらメロディ再生
    if data == b'CALL':
        yusya()

    # クライアントとの接続を閉じる
    cl.close()

PC(送信側)のプログラムです。

# main.py

import socket
import time

# 接続先のIPアドレスとポート番号
target_ip = '192.168.XX.XX'  # 受信側(Pico Wなど)のIPアドレスに書き換えてください
target_port = 8080           # 受信側プログラムで待ち受けているポート番号

print("Enterキーを押すと'CALL'を送信します。終了はCtrl+Cで。")

try:
    while True:
        # ユーザーの入力待ち(Enterキーで送信)
        key = input("Enterを押して送信> ")
        if key == "":
            try:
                # ソケット通信の準備
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

                # 受信側へ接続
                s.connect((target_ip, target_port))

                # 文字列 "CALL" を送信
                s.send(b'CALL')

                # ソケットを閉じる
                s.close()
                print("送信完了!")
            except Exception as e:
                # 送信に失敗した場合のエラーメッセージ
                print("送信失敗:", e)
        
        # ちょっとだけ待機(CPU負荷を減らす)
        time.sleep(0.1)

except KeyboardInterrupt:
    # Ctrl+Cでプログラムを終了するときのメッセージ
    print("\n終了します。")

参考にしたサイト

【IoT工作】Raspberry Pi Pico Wでワイヤレス呼び鈴を作ろう!
Pico Wとブレッドボードでワイヤレス呼び鈴を自作!配線写真・MicroPythonコードを丸ごと掲載し、初心者でも30分で完成する手順を解説しています。

PC(送信側)のプログラムです。送信側に音楽のデータを持たせました

Raspberry Pi Pico W(受信側)のプログラム。

import network, socket
import ujson as json
from machine import Pin, PWM
import time

# Wi-Fi接続情報
ssid = 'あなたのSSID'
password = 'あなたのパスワード'

# Wi-Fi初期化
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.ifconfig(('192.168.XX.XX', '255.255.255.0', '192.168.XX.XX', '8.8.8.8'))
sta_if.connect(ssid, password)

while not sta_if.isconnected():
    time.sleep(0.1)

print('✅ Wi-Fi接続完了:', sta_if.ifconfig())

# 音階→周波数の辞書
NOTE_FREQ = {
    "REST":0,
    "G3":196, "Ab3":208, "A3":220, "Bb3":233, "B3":247,
    "C4":262, "Db4":277, "D4":294, "Eb4":311, "E4":330, "F4":349, "Gb4":370, "G4":392, "Ab4":415, "A4":440, "Bb4":466, "B4":494,
    "C5":523, "Db5":554, "D5":587, "Eb5":622, "E5":659, "F5":698, "Gb5":740, "G5":784, "Ab5":831, "A5":880, "Bb5":932, "B5":988,
    "C6":1047,"Db6":1109,"D6":1175,"Eb6":1245,"E6":1319,"F6":1397,"Gb6":1480,"G6":1568,"Ab6":1661,"A6":1760,"Bb6":1865,"B6":1976
}

# PWMブザー設定
buzzer = PWM(Pin(16))

def play_note(note, duration):
    freq = NOTE_FREQ.get(note, 0)
    if freq == 0:
        time.sleep(duration)
    else:
        buzzer.freq(freq)
        buzzer.duty_u16(3000)
        time.sleep(duration)
        buzzer.duty_u16(0)
        time.sleep(0.02)

# サーバー待機
addr = socket.getaddrinfo(sta_if.ifconfig()[0], 8080)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
print('🎵 メロディサーバー待機中...')

while True:
    cl, addr = s.accept()
    print('🎶 接続:', addr)

    # 受信データ(長い曲でも受信できるようにループで集める!)
    data = b''
    while True:
        chunk = cl.recv(1024)
        if not chunk:
            break
        data += chunk

    print('🎼 受信完了! データサイズ:', len(data))

    # JSONパースして再生
    try:
        melody = json.loads(data)
        for note, duration in melody:
            play_note(note, duration)
    except Exception as e:
        print('⚠️ エラー:', e)

    cl.close()

PC(送信側)のプログラムです。送信側に音楽のデータを持たせました

import socket
import json

# BPMからBEATを計算
BPM = 208
BEAT = 60 / BPM

melody = [
    ["C6", BEAT/2],["Bb5", BEAT/2],["C6", BEAT/2],["Bb5", BEAT/2],["C6", BEAT/2],["Bb5", BEAT/2],["C6", BEAT/2],["D6", BEAT/2],["REST", BEAT/2],["G5", BEAT*3/4],["REST", BEAT/4],
    ["C5", BEAT/2],["D5", BEAT/2],["G5", BEAT/2],["Bb5", BEAT/2],["A5", BEAT/2],["REST", BEAT/2],["F5", BEAT*3/4],["REST", BEAT/4],
    ["Bb4", BEAT/2],["C5", BEAT/2],["Bb4", BEAT/2],["C5", BEAT/2],["D5", BEAT],
    ["C5", BEAT/2],["D5", BEAT/2],["C5", BEAT/2],["D5", BEAT/2],["G5", BEAT/2],["F5", BEAT/2],["C5", BEAT],["Bb4", BEAT/2],["Bb4", BEAT],
    ["G4", BEAT],["A4", BEAT],["Bb4", BEAT],["C5", BEAT],["D5", BEAT],["Eb5", BEAT],["Gb5", BEAT],["G5", BEAT],["A5", BEAT],["Bb5", BEAT],["C6", BEAT],["F5", BEAT],["Eb6", BEAT],["Eb6", BEAT*2],["D6", BEAT],
    ["C6", BEAT/2],["Bb5", BEAT/2],["C6", BEAT/2],["D6", BEAT/2],["REST", BEAT/2],["G5", BEAT*3/4],["REST", BEAT/4],
    ["C5", BEAT/2],["D5", BEAT/2],["G5", BEAT/2],["Bb5", BEAT/2],["A5", BEAT/2],["REST", BEAT/2],["F5", BEAT*3/4],["REST", BEAT/4],
    ["Bb4", BEAT/2],["C5", BEAT/2],["Bb4", BEAT/2],["C5", BEAT/2],["D5", BEAT],
    ["C5", BEAT/2],["D5", BEAT/2],["G5", BEAT/2],["F5", BEAT/2],["Bb4", BEAT/2],["Bb4", BEAT/2],["C5", BEAT],["Bb4", BEAT/2],["Bb4", BEAT],
    ["G4", BEAT],["A4", BEAT],["Bb4", BEAT],["C5", BEAT],["D5", BEAT],["Eb5", BEAT],["F5", BEAT],["G5", BEAT],["A5", BEAT],["Bb5", BEAT],["A5", BEAT*2],
    ["D5", BEAT],["D5", BEAT/2],["D5", BEAT/2],["D6", BEAT/2],["A5", BEAT],["A5", BEAT],["G5", BEAT/2],["G5", BEAT],["REST", BEAT]
]

# JSON文字列にしてバイト列に変換
data = json.dumps(melody).encode()

# 送信先のIPとポート
target_ip = '192.168.XX.XX'  # Pico Wの固定IPアドレス
target_port = 8080

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((target_ip, target_port))
    s.sendall(data)  # sendallを使うのがベスト!
    s.close()
    print("🎶 メロディ送信完了!")
except Exception as e:
    print("送信失敗:", e)

コメント