nihsok が 2023年06月12日02時40分03秒 に編集
初版
タイトルの変更
1000円台で作るスマートスイッチ
タグの変更
RaspberryPiPicoW
IoT
スマートスイッチ
サーボモーター
Wi-Fi
タイマー
HTTP
micropython
メイン画像の変更
記事種類の変更
製作品
ライセンスの変更
(GPL-3.0+) GNU General Public License, version 3
本文の変更
はじめに = 物理ボタンを遠隔操作するためのガジェットは市販のもの(SwitchBot ボット、+Style スイッチなど)だと数千円もするが、マイコン+モーターの単純な構成なら低コストで実現できそうである。なるべく安価に作成して、気軽に導入できるようにしたい。 先行例 - ESP32でWeb上の情報に基づきサーボモーターを動かす (https://elchika.com/article/c6cf26f3-8751-41af-9cd4-0b8a655c8de5/ ) - ESP32でローカルサーバーの情報に基づきサーボモーターを動かす (https://elchika.com/article/2e305a2c-5ef0-4826-8f70-a4643898a389/) 今回やりたいこと ![キャプションを入力できます](https://camo.elchika.com/892f05ec08c7ee11538a37530f7e12b4009e6166/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66336238363735322d663630612d343866342d613963622d3331306133646133386665322f32303838373064372d663637352d346361642d393435352d663563666266623331353261/) - Raspberry Pi Pico Wは常時待ち受け状態にしてHTTPサーバーを立て、スマホなどのブラウザ操作によりサーボモーターを動かす。 - モーターの角度などのパラメータもブラウザから調整できるようにする。 - 任意の時間がたった後に動作するようなタイマーも設定する。 ハードウェア = 分量外もあるが、1700円くらい? - マイコンボード:Raspberry Pi Pico W (秋月電子で1210円 https://akizukidenshi.com/catalog/g/gM-17947/ ) - サーボモーター:FS-90 (マルツで396円 https://www.marutsu.co.jp/pc/i/2228273/ ) - 壁への固定:[3M コマンドタブ](https://www.command.jp/3M/ja_JP/command-jp/products/~/コマンド-タブ-はりかえ用-CM3TN-S-20-パック-箱/?N=5924736+3288418710+3294529191&rt=rud) (十分な粘着力がありつつ、引き延ばすと簡単にはがせる。セットで300円弱) - 電源:micro USBアダプタとケーブル - はんだ適量 このほか作業用にPC、はんだごて、ニッパー等 組み立て - サーボモーターの赤い線をVBUS (40) 、茶色い線をGND (38) 、オレンジの線をPWM(今回は34)に接続。 サーボモーターをスイッチの枠に設置。モーターの力が最も伝わるような配置を考える。**モーターのトルクは足りているが浮き上がって力が伝わらないことが往々にしてある**ので、いろいろ試行錯誤する。今回はスイッチの枠を外して壁にねじどめされている基部に接着した。また、サーボモーターの腕はやや引いた状態で0度とした。 ![設置状態の写真](https://camo.elchika.com/88d07a0d3003b82a39e7623d1ca573db52a2021c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66336238363735322d663630612d343866342d613963622d3331306133646133386665322f33353639613138392d653238622d346432372d386438312d383632383934646432623831/) Webページの様子 - ![何も指定していないとき](https://camo.elchika.com/6a0dcb47aed2595440ce61c8f936af9a48cbdfb6/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66336238363735322d663630612d343866342d613963622d3331306133646133386665322f61663032393032392d376335652d346435362d393535382d666465666562633938643764/) ![実行後の表示](https://camo.elchika.com/f09e9bbe0aba634acd4a83fd6e794b1253926f7e/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66336238363735322d663630612d343866342d613963622d3331306133646133386665322f34326435353062612d356362642d343131662d383936652d623539353731356165643231/) ソフトウェア = 動作するまでの初期設定等は他の資料を参照のこと。 ```python:main.py import re from machine import PWM,Pin,Timer import socket import time import re import wifi import sg90 wifi.connect('SSID','PASSWD') #適宜 html = """<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>Pico Switch</title> </head> <body> <form oninput="display.value=Number(deg.value);"> <p><input type="number" name="timer" min="0" value="%s">分後に<input type="submit" name="action" value="実行"></p> <p>角度:<output name="display">%s</output>°<input type="range" name="deg" min="-90" max="90" step="1" value="%s"></p> <p>押す長さ:<input type="number" name="keep" min="1" list="numlist" value="%s">秒</p> <datalist id="numlist"> <option value="1"></option> <option value="2"></option> <option value="3"></option> <option value="4"></option> <option value="5"></option> <option value="6"></option> <option value="7"></option> <option value="8"></option> <option value="9"></option> <option value="10"></option> </datalist> <input type="submit" name="action" value="適用"> </form> %s </body> </html> """ addr = socket.getaddrinfo('0.0.0.0',80)[0][-1] s = socket.socket() s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) s.bind(addr) s.listen(1) servo = PWM(Pin(28)) servo.freq(50) servo.duty_u16(sg90.set_degree(0)) timer=Timer() while True: try: cl, addr = s.accept() request = str(cl.recv(1024)) result = re.search(r'deg=-?\d+',request) degree = 0 if result is None else result.group(0)[4:] result = re.search(r'keep=\d+',request) keep = 1 if result is None else result.group(0)[5:] result = re.search(r'timer=\d+',request) count = 0 if result is None else result.group(0)[6:] result = re.search(r'action=(%E5%AE%9F%E8%A1%8C|%E9%81%A9%E7%94%A8)',request) action = None if result is None else result.group(0)[7:] if action=='%E5%AE%9F%E8%A1%8C': message='実行しました。' elif action=='%E9%81%A9%E7%94%A8': message='適用しました。' else: message='' cl.send('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') cl.send(html % (count,degree,degree,keep,message)) cl.close() def func(timer): servo.duty_u16(sg90.set_degree(int(degree))) time.sleep(int(keep)) servo.duty_u16(sg90.set_degree(0)) if action=='%E5%AE%9F%E8%A1%8C': timer.deinit() timer.init(mode=Timer.ONE_SHOT,period=60000*int(count),callback=func) except OSError as e: cl.close() ``` ```python:wifi.py import network import time def connect(ssid,passwd,retry=10): wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid,passwd) time.sleep(3) print(wlan.ifconfig()) ``` ```python:sg90.py def set_degree(degree): if degree < -90 : degree = -90 if degree > 90 : degree = 90 return int( ( degree * 0.0475 / 90 + 0.0725 ) * 65535 ) ``` まとめ = タイマーが使えるのが意外と便利というのは使い始めてからわかった。しばらく使用してみて、問題点が見つかれば修正する予定。 今後やってみるとよさそうなアイディア - 複数タイマー - Bluetooth対応 - 家の外からの遠隔操作 - 3Dプリンターでかっこいいケースを作る - バッテリーでの動作