【高先さんの電子工作】第14回：PWMを使ってパッシブブザーを制御する

https://youtu.be/0iv6mZvgEdA


PWMを使ってパッシブブザーを制御するプログラム。

```python:main.py
from machine import Pin, PWM, reset
import socket
import network
import ujson
from utime import sleep, sleep_ms

buzzer = PWM(Pin(15))
frequency = 1000
duration = 250

def tone(Timer):
    """
    パッシブブザーの音を鳴らす。
    """
    if frequency == 0:
        return
    buzzer.freq(frequency)
    buzzer.duty_u16(25000)  #  音量
    sleep_ms(duration)
    buzzer.duty_u16(0)
    
    
class WiFiConnectError(Exception):
    pass

def connect():
    """
    Wi-Fiに接続する。

    Returns:
        ip (str): IPアドレス
    Raises:
        WiFiConnectError: Wi-Fiに接続できなかった場合
    """
    # Wi-Fiの接続情報を取得する
    config = {}
    with open('config.json', 'r') as f:
        config = ujson.load(f)

    # Wi-Fiに接続する
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(config['ssid'], config['password'])

    timeout = 10
    while timeout > 0:
        timeout -= 1
        print('Waiting for connection...')
        if wlan.isconnected():
            ip = wlan.ifconfig()[0]
            print(f'Connected: {ip}')
            return ip
        sleep(1)
    
    raise WiFiConnectError('Wi-Fiに接続できませんでした。')


def open_socket(ip):
    """
    ソケットを開く。このソケットオブジェクトを介してクライアントとの通信を行う。

    Parameters:
        ip: WLAN上で自分自身を表すIPアドレス

    Returns:
        connection(socket.socket): ソケット接続
    """
    address = (ip, 80) # 80ポートでソケット通信を行う
    connection = socket.socket()
    connection.bind(address)
    connection.listen(1) # 同時接続の最大数1
    return connection


def serve(connection):
    """
    WEBサーバーを起動する。

    Parameters:
        connection(socket.socket): ソケット接続
    """
    global frequency
    print('Starting Web Server...')


    while True:
        
        # 接続があるまで待機し、クライアントのソケットオブジェクトを取得する
        client = connection.accept()[0]
        # クライアントからのデータを受信する（最大1024バイト）
        request = client.recv(1024)
        # 受信したバイナリデータを文字列にする
        request = str(request)
        try:
            # リソースのパスを取得する
            # request.split()[0]: HTTPメソッド
            # request.split()[1]: リソースのパス
            request = request.split()[1]
            print(request)
        except IndexError:
            pass

        # スライドバーの値を取得する        
        if '/pitch?value=' in request:
            value = int(request.split('=')[1])
            frequency = value
            rate = int(value * 100 / 2000)
            print(f'{rate}%')
            response = ujson.dumps({'rate': rate})
            client.send("HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n" + response)
            client.close()
            continue

        # HTTPレスポンスを生成する
        response = 'HTTP/1.1 200 OK\r\n'
        response += 'Content-Type: text/html\r\n\r\n'
        response += """
            <!DOCTYPE html>
            <html lang="ja">
            <head>
              <meta charset="UTF-8">
              <title>パッシブブザーを制御する</title>
            </head>
            <body style="display: flex;justify-content: center;align-items: center;height: 100vh;margin: 0;">
              <div id="slider-container" style="text-align: center;">
                <h1 style="font-family: Arial, sans-serif;">50%</h1>
                <input type="range" id="pitchSlider" min="0" max="2000" style="width: 80%;">
              </div>

              <script>
                const pitchSlider = document.getElementById('pitchSlider');
                const h1Element = document.querySelector('h1');

                pitchSlider.addEventListener('change', function() {
                    const pitchValue = pitchSlider.value;
                    console.log('選択された音の高さ: ' + pitchValue);

                    fetch(`/pitch?value=${pitchValue}`)
                        .then(response => {
                            if (!response.ok) {
                                throw new Error('API呼び出しエラー');
                            }
                            return response.json();
                        })
                        .then(data => {
                            console.log(data)
                            h1Element.textContent = `${data.rate}%`;
                        })
                        .catch(error => {
                            console.error('API呼び出しエラー:', error);
                        });
                });
              </script>
            </body>
            </html>
        """
        # HTTPレスポンスを送信する
        client.send(response.encode('utf-8'))
        # クライアントソケットを閉じる
        client.close()


if __name__ == '__main__':
    try:
        # Wi-Fiに接続する
        ip = connect()

        # ソケットを開く
        connection = open_socket(ip)
        
        # 3秒ごとにcallback関数を呼び出す設定をする
        timer = machine.Timer(-1)
        timer.init(period=3000, mode=machine.Timer.PERIODIC, callback=tone)

        # Web Serverを起動する
        serve(connection)
    except WiFiConnectError as e:
        print(f'Error: {e}')
    except Exception as e:
        # 再起動する
        reset()

```

Wi-Fiの接続情報を書き込む設定ファイル。

```json:config.json
{"ssid": "Your SSID", "password": "Your Password"}
```