0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ボタンを押すとPCで音が鳴るカラクリ

0
Last updated at Posted at 2026-01-08

1.開発の動機

妻から声をかけられたが、ヘッドホンで大音量で音(推しのASMR配信)を聞いていたので応答できず怒られた。
殴る、蹴る、棒でつつく、等の方法を提案されたが、痛いのは嫌なので細工で乗り切ることにした。
具体的にはChatGPTでスクリプトを書かせてそれを配線する作業となる。簡単。

2.用意したもの

ハードウェア
RaspberryPi PICO W
プッシュスイッチ(モーメンタリ)
100均のUSB-MicroBケーブル
ビニル被覆ワイヤー 少量
ブレッドボード、QIコネクタと圧着工具
ソフトウェア環境等
Windows11
MicroPython
Thonny
利用するもの
ChatGPT 5.2 Thinking

充分な検証をせずマネしないでください
また、詳しいことを訊かれても筆者は知識が無いのでお答えできません
ASMR配信を聴く場合は背後等に注意しましょう

3.やりたいこと

(1)PICO Wの任意のGPIOをGNDに落とす(ONにする)
(2)デスクトップPCに通知が飛び、チャイムが鳴る
(3)怒られずに済む ←重要

3-1.ChatGPTに投げたプロンプト

今回あなたは組込製品のソフトウェアの専門家です。下記のような要望があり、指定されたハードウェア上で動作するスクリプトをPythonで記述してください。
1) やりたいこと ボタンを押すと指定されたPCのWindowsに通知が出て音が鳴る。 WEB会議や動画視聴の際、棒で背中をつつくとかしなくて良くなる。

2) ハードウェア 
・ボタン側 RaspberryPi PICO W
・表示する側 Windows11が動作しているPC

3) 使用言語 Python

4)詳細仕様 ・ボタンを押す→Windows11のPCでアラート音が鳴る(ウェブ会議の相手に聞こえるのは問題ないものとする) 
・ネットワーク環境は、PICO WからネットワークへはWi-Fiを使用し、PCはLANケーブルでネットワークに接続しており、自分の所属するネットワーク内だけにメッセージが届けば良いものとする。
・通知音の音源は当方で用意するので、スクリプト内で設定できるように変数として構成し、初期値はDummy.wavとする 
・PICO Wに設定するAP情報とパスワードは、当方で入力するので、スクリプト内で設定できるように変数として構成し、AP名の初期値はAP12345678とし、パスワードはABCD1234とする 

5)環境説明
・LANネットワークアドレス周り 192.168.0.0/24 DGW 192.168.0.254 DNS 192.168.0.7 

6) 不明な点については事前に確認をお願いします。では、よろしくお願いします。

3-2.作成されたプロンプト PICO W側

※途中でLEDの表示・点滅機能を利用しようと思い立ったので追加改修しています

# main.py (Raspberry Pi Pico W / MicroPython)
# ボタン押下で LAN内へ「急用」通知要求をUDP送信する
# 改修:
#  1) 内蔵LED: 待機中は常時点灯、通報時は5秒間点滅
#  2) 連続動作防止: 1回押した後はCOOLDOWN_SECの間は無効化

import time
import socket
import network
from machine import Pin

# ===== ユーザー設定(要望の初期値) =====
WIFI_SSID = "AP12345678"
WIFI_PASSWORD = "ABCD1234"

# 送信先:まずはブロードキャスト推奨(LAN内のWindowsにだけ届く)
DEST_IP = "192.168.0.255"
DEST_PORT = 5005

# 簡易認証(同じLANの別端末が誤送信しないようにしたい場合)
SHARED_KEY = "CHANGE_ME"   # 使わないなら空文字 "" にしてOK

# ===== 連続動作防止(改修: 変更可能な変数) =====
COOLDOWN_SEC = 5  # 1回押した後に作動しない時間(秒)

# ===== LED制御(改修) =====
ALERT_BLINK_SEC = 5.0   # 通報時に点滅する時間(秒)
BLINK_PERIOD_MS = 200   # 点滅周期(ms) 200ms -> 約2.5Hz

# ボタン
BUTTON_PIN = 15
DEBOUNCE_MS = 80

# ===== Wi-Fi接続 =====
def wifi_connect():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if wlan.isconnected():
        return wlan

    print("Connecting Wi-Fi...")
    wlan.connect(WIFI_SSID, WIFI_PASSWORD)

    t0 = time.ticks_ms()
    while not wlan.isconnected():
        if time.ticks_diff(time.ticks_ms(), t0) > 20000:
            raise RuntimeError("Wi-Fi connect timeout")
        time.sleep_ms(200)

    print("Wi-Fi connected:", wlan.ifconfig())
    return wlan

# ===== UDP送信 =====
def send_urgent(sock):
    # メッセージ形式: key|text
    # textは固定「急用」
    msg = "{}|{}".format(SHARED_KEY, "急用")
    sock.sendto(msg.encode("utf-8"), (DEST_IP, DEST_PORT))

# ===== LED点滅(改修) =====
def blink_led_for(led: Pin, seconds: float, period_ms: int):
    end_ms = time.ticks_add(time.ticks_ms(), int(seconds * 1000))
    while time.ticks_diff(end_ms, time.ticks_ms()) > 0:
        led.toggle()
        time.sleep_ms(period_ms)
    # 終了後は待機状態(常時点灯)へ戻す
    led.value(1)


def main():
    wifi_connect()

    # 内蔵LED(Pico Wは "LED" で参照可能)
    led = Pin("LED", Pin.OUT)
    led.value(1)  # 待機中:常時点灯(改修)

    # UDPソケット(ブロードキャスト送信用)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

    button = Pin(BUTTON_PIN, Pin.IN, Pin.PULL_UP)

    last_state = button.value()
    cooldown_until_ms = time.ticks_ms()  # 次に作動可能になる時刻

    print("Ready. Press button to send '急用'.")

    while True:
        state = button.value()

        # 作動禁止期間中は入力を無視(改修)
        now = time.ticks_ms()
        if time.ticks_diff(cooldown_until_ms, now) > 0:
            # 待機中LEDは常時点灯
            led.value(1)
            last_state = state
            time.sleep_ms(10)
            continue

        # 押下検出:HIGH→LOW
        if last_state == 1 and state == 0:
            # デバウンス
            time.sleep_ms(DEBOUNCE_MS)
            if button.value() == 0:
                try:
                    # 送信(UDPなので取りこぼし対策で2回)
                    send_urgent(sock)
                    time.sleep_ms(50)
                    send_urgent(sock)
                    print("Sent: 急用")

                    # 通報時:LEDをALERT_BLINK_SEC秒間点滅(改修)
                    blink_led_for(led, ALERT_BLINK_SEC, BLINK_PERIOD_MS)

                    # 連続動作防止:COOLDOWN_SEC秒間無効化(改修)
                    cooldown_until_ms = time.ticks_add(time.ticks_ms(), int(COOLDOWN_SEC * 1000))

                except Exception as e:
                    print("Send error:", e)
                    # エラー時も見た目は待機(常時点灯)
                    led.value(1)

                # ボタンが離されるまで待つ(チャタリング/連打対策)
                while button.value() == 0:
                    time.sleep_ms(10)

        last_state = state
        time.sleep_ms(10)


if __name__ == "__main__":
    main()

3-2.Windows11側の準備

(1)フォルダの準備

C:\pico_urgent

(2)venvの準備

PowerShellを起動し、下記のコマンドを実行して実行フォルダ内にvenv環境を作る

python -m venv C:\pico_urgent\venv
(3)venv環境の確認
Test-Path C:\pico_urgent\venv\Scripts\python.exe
Test-Path C:\pico_urgent\venv\Scripts\pythonw.exe

両方ともTrueになればOK

(4)venv側のpipを更新する
C:\pico_urgent\venv\Scripts\python.exe -m pip install --upgrade pip
(5)スクリプトファイルを格納する
receiver_sound_only.py
(6)スクリプトをChatGPTからコピペ
# receiver_sound_only.py (Windows 11 / Python)
# Pico W からの UDP を受けて、指定WAVを鳴らす(通知は出さない)
# 目的: 「音だけ出ればOK」

import os
import socket
import threading
import time

# ===== ユーザー設定 =====
LISTEN_IP = "0.0.0.0"
LISTEN_PORT = 5005

SOUND_WAV = "Dummy.wav"  # 初期値: Dummy.wav(スクリプトと同じフォルダに置くのが確実)

# Pico 側と合わせる(使わないなら "" でOK)
SHARED_KEY = "CHANGE_ME"

# 連続受信時の暴発防止(秒): 0 にすると無制限
MIN_INTERVAL_SEC = 0.5


def resolve_wav_path(path: str) -> str:
    if os.path.isabs(path):
        return path
    here = os.path.dirname(os.path.abspath(__file__))
    return os.path.join(here, path)


def play_wav(path: str) -> None:
    # WAV再生: winsound は標準ライブラリ
    import winsound

    if os.path.exists(path):
        # 非同期再生(鳴らしながら受信待ち可能)
        winsound.PlaySound(path, winsound.SND_FILENAME | winsound.SND_ASYNC)
    else:
        # ファイルが見つからない場合はビープ
        winsound.MessageBeep()


_last_play_ts = 0.0
_lock = threading.Lock()


def handle_message(data: bytes, addr) -> None:
    global _last_play_ts

    text = data.decode("utf-8", errors="ignore").strip()

    # 形式: key|急用
    if "|" in text:
        key, msg = text.split("|", 1)
    else:
        key, msg = "", text

    if SHARED_KEY and key != SHARED_KEY:
        return

    if msg.strip() != "急用":
        return

    # 連続再生抑止
    now = time.time()
    with _lock:
        if MIN_INTERVAL_SEC > 0 and (now - _last_play_ts) < MIN_INTERVAL_SEC:
            return
        _last_play_ts = now

    wav = resolve_wav_path(SOUND_WAV)
    play_wav(wav)
    print("Played WAV for '急用' from", addr)


def main():
    wav = resolve_wav_path(SOUND_WAV)
    print("=== Pico Urgent Receiver (sound only) ===")
    print("Listen:", LISTEN_IP, LISTEN_PORT)
    print("WAV:", wav)
    print("MIN_INTERVAL_SEC:", MIN_INTERVAL_SEC)

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind((LISTEN_IP, LISTEN_PORT))

    while True:
        data, addr = sock.recvfrom(2048)
        # 念のため別スレッドで処理
        threading.Thread(target=handle_message, args=(data, addr), daemon=True).start()


if __name__ == "__main__":
    main()

4.タスクスケジューラに登録

4-1.

タスク スケジューラ ライブラリ → タスクの作成

4-2.

「全般」タブ → 名前(わかりやすければ何でも良い。英数字推奨)、「最上位の特権で実行する」をON

4-3.

「トリガー」タブ → 新規 → 「タスクの開始」を「ログオン時」に設定

4-4.

ここがキモなので注意してください

「操作」タブ

プログラム/スクリプト → C:\pico_urgent\venv\Scripts\pythonw.exe

引数の追加 → "C:\pico_urgent\receiver_sound_only.py"

開始 → c:\pico_urgent

4-5.

「設定」タブ

「タスクが既に実行中の場合に適用される規則」を「新しいインスタンスを開始しない」に設定

4-6.

登録されているのが確認できたら、いったんサインアウトして再ログオン

5.RaspberryPi PICO Wの組立

ブレッドボードで組立てました。

ケースにしまいたかったので100均で適当なUSBのケーブルを買ってきてB側を剥いて+5Vが来ているところを探します。探せたらケースにケーブルとちょうどいいサイズの穴を開けて通しましょう。

5-1.電源

+5V → GP40
GND → GNDならどこでも良い。(自分は38を使った)

5-2.プッシュスイッチ

検出側 → GP15
反対側 → GNDならどこでも良い

6.試験

Youtubeでも流しながらプッシュスイッチを押す

LEDの動作

点灯→点滅

PCの動作

指定したチャイム(Dummy.wav)が鳴る

最後に

ASMRは背後に気をつけましょう。
よいヘッドホンライフを!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?