魔女帽 庭の隠れ家 竹箒

ダイソーのTape Lightをpico2wで電子制御(前編)

作成日 作成日:2025年8月24日

更新日 更新日:2025年8月24日

はじめに

らしんばんで爆買いしたフィギュアを飾ってみたが…ク ラ イ シ ス ギ!

そんなことを考えながらダイソーを彷徨っていたところ、こんなものが

リモコン操作ができるならpico2wでどうのこうのすれば面白そうと思って無駄に虹色のLEDライトを買った

ということで開けると中はこんな感じ

リモコンはちょくちょく見る安っぽいリモコン
本丸のLEDテープはこんな感じ
裏側は両面テープが貼ってある

左上のよくわからん線は、パッケージの裏側を見る感じこんな感じで使うみたい

実際にACアダプタに挿してみるとちゃんと光った

もちろん付属のリモコンも反応する(別途CR2025ボタン電池を忘れずに買う必要があるが…)

電子制御をしてみよう

ところで、電源ケーブルとの接合部はこんな形になっている

あれ?ジャンパー線させそうじゃね?

普通にささった

テープを見る感じ
黒:5V,
緑赤青:信号線(GND)
っぽいのでとりあえずその通りpico2wからとった電源に繋げてみると

普通に光りました
ということでカソードコモンのRGBLEDとおなじ扱いができるだろうと推察(抵抗は内蔵されているっぽいのでいらない)

てことでこんな感じでmicropythonでコードを書きまして

from machine import Pin, PWM

# 各色のピンにPWMを設定(周波数は1kHzに設定)
red = PWM(Pin(18))
green = PWM(Pin(17))
blue = PWM(Pin(16))

red.freq(1000)
green.freq(1000)
blue.freq(1000)

#LEDを光らせる関数
def RGBLED(R, G, B):
    # 0~255を0~65535に変換し、カソードコモン用に反転
    red.duty_u16(65535 - int(R * 257))
    green.duty_u16(65535 - int(G * 257))
    blue.duty_u16(65535 - int(B * 257))

# LEDを点灯(#ffff00)
RGBLED(255, 255, 0)

実行してみると無事に光りました

コードを以下のように書き換えて…

from machine import Pin, PWM
import time

# 各色のピンにPWMを設定(周波数は1kHzに設定)
red = PWM(Pin(18))
green = PWM(Pin(17))
blue = PWM(Pin(16))

red.freq(1000)
green.freq(1000)
blue.freq(1000)

#LEDを光らせる関数
def RGBLED(R, G, B):
    # 0~255を0~65535に変換し、カソードコモン用に反転
    red.duty_u16(65535 - int(R * 257))
    green.duty_u16(65535 - int(G * 257))
    blue.duty_u16(65535 - int(B * 257))


#LEDの色を徐々に変えていく関数(delay:遅延秒数)
def fade_colors(delay):
    while True:
        # 赤 → 緑
        for i in range(256):
            RGBLED(255 - i, i, 0)
            time.sleep(delay)

        # 緑 → 青
        for i in range(256):
            RGBLED(0, 255 - i, i)
            time.sleep(delay)

        # 青 → 赤
        for i in range(256):
            RGBLED(i, 0, 255 - i)
            time.sleep(delay)

# フェード開始
fade_colors(0.01)

実行してみると動画のようにきれいにグラデーション

550円でこれできるの普通にええなぁ

本題

ところで、私はこのLEDテープを画像の赤線のように貼りたいと思っている(実際には天井につけるんですが撮影の都合上床で)

ですが、この通りにとりあえずセロテープで仮止めしながら貼ろうとすると

どうしても角が無理すぎる

そんなことを考えながらネットサーフィンをしていたところ
以下のサイトを見つけた
https://hutarino.com/lifestyle/interior-coordinate/interior-diy-idea/how-to-use-a-led-tape-lighting/?srsltid=AfmBOooiWyrHcxpxrTXW4uhTbzkM5t982NS6kIQAqB_5ON2S1lE7B-5K

このサイト曰く、ハサミのマークの部分であれば切ってもいいらしい
実際によく見てみると、ハサミのマークが

…これ多分切ってもリード線とかはんだ付けすれば普通に動くのでは?
頭ではわかっていても流石にそれ前提で動くのも怖すぎるのでミスってもいいようにとりあえず一番端っこを切って実験してみることに

切れた。紙ほど簡単には切れないが、ハサミで少し力をいれれば簡単に切れる

え、ここにはんだ付けするん?

と思ってふと裏側の両面テープを剥がしてみると、

銅箔が出てきた、これなら簡単にはんだ付けできそう
と思ってはんだ付けをしようとしたのだが…

はんだが全くつかないんですけど
フラックスを塗布してみたが、同様に駄目だった(フラックス滅多に使わないので使い方がおかしいだけの可能性もある)

とりあえずジャンパー線を押し当ててみて光るか試してみよう(最初からそれやれ)

だが…

光らない…

色々試行錯誤していると、表面のジェル的なものをどかして表面の銅箔に当てれば光ることを発見

ということでカッターナイフを使って銅箔の上側のジェル的なものを除去(結構たいへん)
ジェル的なものを削り取るようなイメージで

ジェル的なものを剥がした表面であればしっかりはんだも乗ってくれた

ということで以下動画を参考に銅箔にはんだ付け(銅箔へのはんだ付け初めてやった)
https://youtu.be/DjpmBLeJdnQ?si=K2LjJBrXSUXgf5bS


こんな感じではんだ付けができました

頼むからついてくれと願いながらpico2wにさすと、
赤緑青としっかり点灯した

ちなみに銅箔の部分かなりとなりとくっつきやすいので注意(1敗)
となりとくっつくと1つしかGNDに接続していないはずなので2つLEDが光ります。絶望です
ちなみにここまで色々試行錯誤して苦戦した結果、2時間ぐらいかかりました



ということでテープをいい感じに切断して仮止め

間の線は結構長めにしないといけないかな?と考えながら適当に転がってた被覆のより線をいい感じの長さに切断しまして

そして表面のジェル的なものをカッターナイフを使って除去し

雑に被覆を剥ぎまして

無事にはんだづけ完了

どきどきの導通チェック

無事RGB点灯!

あとははんだづけした部分を絶縁テープで巻き巻きして…

この殺風景な空間が

LEDライトがつけれた!

ちゃんと照明もつきます


ということで検証時と同じように線を繋いだ後、以下のコードをpico2wに書きまして
(※このコードを書くと、たまにアクセス拒否が起こってpico2w内のデータが取り出せなくなるので、もしもpico2w内に重要なデータを残している方がいればあらかじめバックアップを取っておくこと推奨)(こうなった場合の対処はこのサイトが参考になりますhttps://sanuki-tech.net/and-more/2022/raspberry-pi-pico-resetting-flash-memory/ pico用で書いてありますが、pico2wにも効果あり)


てことで以下のような感じでコードを書きまして

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

# --- 初期設定 ---
red = PWM(Pin(18))
green = PWM(Pin(17))
blue = PWM(Pin(16))
for led in (red, green, blue):
    led.freq(1000)

current_color = [0, 0, 0]
brightness = 1.0
current_mode = "manual"

fade_running = False
flash_running = False

lock = _thread.allocate_lock()

# --- Wi-Fiルータ設定---
ssid = 'ルーターのssid(2.4GHz帯のもの)'
password = 'ルーターのパスワード'

# --- RGB制御 ---
def RGBLED(r, g, b):
    global current_color
    r2 = int(r * brightness)
    g2 = int(g * brightness)
    b2 = int(b * brightness)
    red_val = 65535 if r2 == 0 else 65535 - int(r2 * 257)
    green_val = 65535 if g2 == 0 else 65535 - int(g2 * 257)
    blue_val = 65535 if b2 == 0 else 65535 - int(b2 * 257)
    red.duty_u16(red_val)
    green.duty_u16(green_val)
    blue.duty_u16(blue_val)
    current_color = [r, g, b]

# --- URLデコード ---
def urldecode(s):
    s = s.replace('+', ' ')
    i = 0
    res = ''
    while i < len(s):
        if s[i] == '%' and i + 2 < len(s):
            try:
                res += chr(int(s[i+1:i+3], 16))
                i += 3
            except ValueError:
                res += s[i]
                i += 1
        else:
            res += s[i]
            i += 1
    return res

# --- Wi-Fi接続 ---
ssid = 'A0957FE335CD-2G'
password = 'bgmygtx6kanp6p'
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
print("Connecting Wi-Fi...")
while not wlan.isconnected():
    time.sleep(1)
print("IP:", wlan.ifconfig()[0])

# --- フェード処理(スレッド) ---
def fade_colors():
    global fade_running
    print("fade_colors() started")
    while True:
        for i in range(256):
            with lock:
                if not fade_running:
                    return
                RGBLED(255 - i, i, 0)
            time.sleep(0.01)
        for i in range(256):
            with lock:
                if not fade_running:
                    return
                RGBLED(0, 255 - i, i)
            time.sleep(0.01)
        for i in range(256):
            with lock:
                if not fade_running:
                    return
                RGBLED(i, 0, 255 - i)
            time.sleep(0.01)

# --- フラッシュ処理(スレッド) ---
flash_colors = [
    (255, 0, 0),
    (0, 255, 0),
    (0, 0, 255),
    (255, 255, 0),
    (255, 165, 0),
    (255, 255, 255),
    (0, 0, 0)
]

def flash_loop():
    global flash_running, flash_colors
    print("flash_loop() started")
    index = 0
    while True:
        with lock:
            if not flash_running:
                return
            RGBLED(*flash_colors[index])
            index = (index + 1) % len(flash_colors)
        time.sleep(0.3)

# --- HTTPリクエスト処理 ---
def parse_params(request):
    try:
        lines = request.split('\r\n')
        path = lines[0].split(' ')[1]
        if '?' in path:
            query = path.split('?', 1)[1]
            return {k: v for k, v in (p.split('=') for p in query.split('&') if '=' in p)}
    except Exception as e:
        print("パースエラー:", e)
    return {}

# --- HTMLページ生成 ---
def web_page():
    rc, gc, bc = current_color
    st = f"モード: {current_mode} / RGB: ({rc},{gc},{bc}) / 明るさ: {brightness:.1f}"
    html = f"""<!DOCTYPE html>
<html lang="ja">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>RGB LED コントローラ</title>
<style>
  body {{ font-family: sans-serif; text-align: center; padding: 1em; background: #f0f0f0; }}
  .status {{ margin: 1em auto; padding: 1em; background: white; border-radius: 10px; max-width: 400px; box-shadow: 0 0 5px rgba(0,0,0,0.2); }}
  input[type="color"] {{ width: 100%; height: 50px; border: none; margin: 1em 0; }}
  button {{ padding: 1em; margin: 0.5em; font-size: 1em; border: none; border-radius: 8px; background: #4CAF50; color: white; }}
  button:hover {{ background: #45a049; }}
</style>
</head>
<body>
  <h2>RGB LED コントローラ</h2>
  <div class="status" id="status">{st}</div>
  <input type="color" id="colorPicker" value="#{rc:02x}{gc:02x}{bc:02x}">
  <br><button onclick="sendColor()">色を設定</button><br><br>
  <button onclick="sendCmd('off')">消灯</button>
  <button onclick="sendCmd('fade')">フェード</button>
  <button onclick="sendCmd('flash')">フラッシュ</button><br>
  <button onclick="sendCmd('bright_up')">明るく</button>
  <button onclick="sendCmd('bright_down')">暗く</button>
  <script>
function sendCmd(cmd) {{
  fetch('/?cmd=' + cmd).then(response => response.text()).then(data => updateStatus(data));
}}
function sendColor() {{
  const color = document.getElementById('colorPicker').value;
  fetch('/?pick=' + encodeURIComponent(color)).then(response => response.text()).then(data => updateStatus(data));
}}
function updateStatus(html) {{
  const temp = document.createElement('div');
  temp.innerHTML = html;
  const newStatus = temp.querySelector('#status');
  if (newStatus) {{
    document.getElementById('status').innerHTML = newStatus.innerHTML;
  }}
}}
</script>
</body></html>"""
    return html

# --- コマンド処理 ---
def stop_fade_and_flash():
    global fade_running, flash_running
    with lock:
        fade_running = False
        flash_running = False
    # スレッド停止待ち(簡易的に100~200ms待つ)
    time.sleep(0.2)

def handle_command(p):
    global brightness, current_mode, fade_running, flash_running
    if 'cmd' in p:
        cmd = p['cmd']

        if cmd == 'off':
            stop_fade_and_flash()
            with lock:
                current_mode = 'manual'
            RGBLED(0, 0, 0)

        elif cmd == 'fade':
            stop_fade_and_flash()
            with lock:
                fade_running = True
                current_mode = 'fade'
            try:
                _thread.start_new_thread(fade_colors, ())
                print("starting fade thread...")
            except Exception as e:
                print("スレッド起動失敗:", e)

        elif cmd == 'flash':
            stop_fade_and_flash()
            with lock:
                flash_running = True
                current_mode = 'flash'
            try:
                _thread.start_new_thread(flash_loop, ())
                print("starting flash thread...")
            except Exception as e:
                print("スレッド起動失敗:", e)

        elif cmd == 'bright_up':
            brightness = min(1.0, brightness + 0.1)
            RGBLED(*current_color)

        elif cmd == 'bright_down':
            brightness = max(0.1, brightness - 0.1)
            RGBLED(*current_color)

    elif 'pick' in p:
        raw = urldecode(p['pick'])
        col = raw.lstrip('#')
        if len(col) == 6:
            try:
                r = int(col[0:2], 16)
                g = int(col[2:4], 16)
                b = int(col[4:6], 16)
                stop_fade_and_flash()
                with lock:
                    current_mode = 'manual'
                RGBLED(r, g, b)
            except ValueError:
                print("16進数変換失敗:", col)

# --- Webサーバ起動 ---
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(5)
print("Web server at", addr)

while True:
    cl, addr = s.accept()
    try:
        req = cl.recv(1024).decode()
        print("受信リクエスト:\n", req)
        params = parse_params(req)
        handle_command(params)
        response = web_page()
        cl.send('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n')
        cl.send(response)
    except Exception as e:
        print("通信処理エラー:", e)
    finally:
        cl.close()



実行すると、コンソールに以下のようなlogが出るはず

MPY: soft reboot
Connecting Wi-Fi...
IP: X.X.X.X
Web server at ('0.0.0.0', 80)

このIPアドレスにhttpでブラウザからアクセスする(スマホでもPCでも可)
例えば以下のようなログが出たなら、http://192.168.3.15 にアクセスする

MPY: soft reboot
Connecting Wi-Fi...
IP: 192.168.3.15
Web server at ('0.0.0.0', 80)


そうすると、Webサイトに飛ばされて、遠隔でLEDライトの制御ができる
//後で簡単にwebページの使い方を書く

だが、この状態で消灯を押したとき、画像のような感じで若干赤LEDが光ってしまう

これを解決するために、24番ピンに5Vを220Ωの抵抗を噛ませて流した

そうしたら消灯時に光らなくなった!
そしてとりあえずフィギュアを置いてみると…

なんかまだ全体的に暗い気がする
と思い純正のやつをつけてみると

明らかに明るい
これはpico2wの電圧、電流が足らんということか…

進展があったら続き出します