【高先さんの電子工作】第15回：PWMを使ってRGB LEDを制御する

https://youtu.be/-InSZLLggIA


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


red = PWM(Pin(13))
green = PWM(Pin(14))
blue = PWM(Pin(15))
red.freq(1000)
green.freq(1000)
blue.freq(1000)
r = 0
g = 0
b = 0


def interval_mapping(x, in_min, in_max, out_min, out_max):
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min


def color_to_duty(rgb_value):
    rgb_value = int(interval_mapping(rgb_value,0,255,0,65535))
    return rgb_value


def color_set(red_value,green_value,blue_value):
    red.duty_u16(color_to_duty(red_value))
    green.duty_u16(color_to_duty(green_value))
    blue.duty_u16(color_to_duty(blue_value))
    

def callback(Timer):
    color_set(r,g,b)


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 r
    global g
    global b
    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 '/led?' in request:
            params = request.split('?')[1].split('&')
            r = int(params[0].split('=')[1])
            g = int(params[1].split('=')[1])
            b = int(params[2].split('=')[1])
            response = ujson.dumps({'message': 'ok'})
            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>
            <head>
                <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
                <title>PWMでRGB LEDを制御する</title>
            </head>
            <body style="display: flex;justify-content: center;align-items: center;height: 100vh;margin: 0;">
                <div style="text-align: center;">
                    <h1 style="font-family: Arial, sans-serif;"></h1>
                    <input type="color" id="colorPicker" onchange="getColorValue()">
                </div>


                <script>
                    function getColorValue() {
                        // input要素から選択された色を取得
                        var colorPicker = document.getElementById("colorPicker");
                        var selectedColor = colorPicker.value;

                        // 色をRGB値に変換
                        var hex = selectedColor.slice(1); // 先頭の#を削除
                        var bigint = parseInt(hex, 16);
                        var r = (bigint >> 16) & 255;
                        var g = (bigint >> 8) & 255;
                        var b = bigint & 255;

                        // APIを呼び出す
                        fetch(`/led?r=${r}&g=${g}&b=${b}`, {
                            method: 'GET',
                            headers: {
                                'Content-Type': 'application/json'
                            }
                        })
                        .then(response => {
                            if (!response.ok) {
                                throw new Error('API呼び出しエラー');
                            }
                            return response.json();
                        })
                        .then(data => {
                            console.log(data);
                            var rgbValue = document.querySelector('h1');
                            rgbValue.textContent = "RGB: (" + r + ", " + g + ", " + b + ")";
                        })
                        .catch(error => {
                            console.error('API呼び出しエラー:', error);
                        });
                    }
                </script>
            </body>
            </html>
        """
        # HTTPレスポンスを送信する
        client.send(response)
        # クライアントソケットを閉じる
        client.close()


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

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

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

```

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