0. はじめに
「本書の電子工作パーツを Raspberry Pi Pico シリーズで利用する」にて、本書の公式パーツセットを Raspberry Pi Pico シリーズ(以後 Pico)で利用する方法を紹介しました。Wifi 機能のない Raspberry Pi Pico で実行できる演習は、 本書で Wifi 接続を用いない 1 章から 8 章の内容でした。
一方、2023年3月、Wifi 機能付きの Raspberry Pi Pico W (以下 Pico W)が日本で販売開始されました。 さらに、2025年3月にはその高機能版である Raspberry Pi Pico 2 W (以下 Pico 2 W)が日本で販売開始されました。 Pico W や Pico 2 W を用いると、 Wifi 接続を用いる 9 章および 10 章の演習をある程度再現できるようになります。 具体的には、本ページでは下記の内容を取り扱います。
- 9章の内容: PC やスマートフォンのブラウザで、Pico W / Pico 2 W に接続された LED / I2C温度センサ / RGB フルカラー LED / DCモータ / サーボモータ を制御
- 10章の内容: PCやスマートフォンのブラウザで、Pico W / Pico 2 W を搭載したキャタピラ式模型を制御
なお、Wifi 経由での Pico W / Pico 2 W の制御には欠点もあり、回路への制御信号の送信が、ブラウザのページ再読み込みのタイミングでしか行えないため、ブラウザに対して行ったアクションへのレスポンスが悪い、という問題があります。
さて、本ページでは以上の内容の実現方法を解説していきます。
その内容に進む前に、「本書の電子工作パーツを Raspberry Pi Pico シリーズで利用する」を参考に、 下記の内容を進めておいてください。Pico W や Pico 2 W でも、Pico 用の演習をすべて実行することができます。Pico W や Pico 2 W を購入できるサイトもリンク先で紹介されています。
- Pico W / Pico 2 W への MicroPython 環境のインストール (自分が用いているバージョンの Pico の UF2 ファイルを用いることに注意)
- Windows などへの、開発環境 Thonny のインストール
- Pico W / Pico 2 W で Python プログラムを実行する方法の習得
1. ブラウザのボタンによるLEDの点灯
まずは、PCやスマートフォンのブラウザから、Pico W / Pico 2 W に接続された LED のオン / オフを切り替える演習を行ってみましょう。9.3 章の内容です。必要な回路は下図の通りです。 見てわかる通り、LED以外に I2C 接続の小型 LCD も接続されています。これは、Pico W / Pico 2 W に割り当てられた IP アドレスやエラーメッセージを表示するためのものです。この LCD を接続しなくても演習は実行可能ですが、その場合、Pico W / Pico 2 W からのメッセージを Windows などの PC 上で見るしかなくなります。LCD を接続すること、すなわち Windows などの PC なしでも Pico W / Pico 2 W を動作させられるようにすることを強く推奨します。さて、この回路に対して実行するプログラムは以下の通りです。なお、本ページで紹介するプログラムはすべてこちらのページの解説を参考に作成しました。
import time import sys import network import socket from time import sleep from machine import Pin, I2C led = Pin(16, Pin.OUT) i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) i2c.scan() ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' ##### for LCD def setup_st7032(): c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c])) sleep(0.2) i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x0d, 0x01])) sleep(0.001) def clear(): global position global line position = 0 line = 0 i2c.writeto_mem(address_st7032, register_setting, bytes([0x01])) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line i2c.writeto_mem(address_st7032, register_setting, bytes([0xc0])) sleep(0.001) def write_string(s): for c in list(s): write_char(ord(c)) def write_char(c): global position byte_data = check_writable(c) if position == display_chars: clear() elif position == chars_per_line*(line+1): newline() i2c.writeto_mem(address_st7032, register_display, bytes([byte_data])) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 address_st7032 = 0x3e register_setting = 0x00 register_display = 0x40 contrast = 32 # 0から63のコントラスト。30から40程度を推奨 chars_per_line = 8 # LCDの横方向の文字数 display_lines = 2 # LCDの行数 display_chars = chars_per_line*display_lines position = 0 line = 0 setup_st7032() ##### end of LCD wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) html = """<!DOCTYPE html><html> <head><meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <title>1 - LEDの点灯制御</title> <style>button { width: auto; height: auto; background: %s; font-weight: normal; font-size: 14pt; font-family: Arial, Helvetica, sans-serif; font-size: 14px; color: #ffffff; padding: 10px 20px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: 1px solid #003366; -moz-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); -webkit-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); text-shadow: 0px -1px 0px rgba(000,000,000,0.7), 0px 1px 0px rgba(255,255,255,0.3); } </style></head> <p>下のボタンを押すごとに、LEDの点灯状態が変化します。</p> <div align="center"> <form><button name="led" value="toggle" type="submit">LED</button></form> </div> </body></html> """ # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') write_string('waiting for conn') #showing on LCD time.sleep(1) # Handle connection error if wlan.status() != 3: write_string('conn. failed') # showing on LCD raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) write_string(status[0]) #showing on LCD # Open socket addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind(addr) except OSError as e: print(e) s.close() s = None write_string('socket error') #showing on LCD sys.exit() s.listen(1) print('listening on', addr) ledState = 0 buttonColor = '#003366' # Listen for connections, serve client while True: try: cl, addr = s.accept() #print('client connected from', addr) request = cl.recv(1024) #print("request:") #print(request) request = str(request) led_toggle = request.find('led=toggle') #print( 'led_toggle = ' + str(led_toggle)) if led_toggle == 8: #print("led toggle") ledState = 1 - ledState led.value(ledState) if ledState == 0: buttonColor = '#003366' else: buttonColor = '#26a1ff' response = html % buttonColor cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') cl.send(response) cl.close() except OSError as e: print('connection closed') except KeyboardInterrupt as e: break s.close() s = Noneこのプログラムを実行するための注意をいくつか記します。本ページのプログラムにはすべて下記のように、用いている Wifi の SSID (アクセスポイント名) とそのパスワードを記す項目があります。 ここを皆さんの環境における SSID とパスワードに変更しないと本ページのプログラムは全て動作しませんのでご注意ください。なお、2.4GHzの周波数帯の Wifi アクセスポイントにしか接続できませんのでご注意ください。
ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD'また、本ページの回路を LCD なしで実行する場合、プログラム中の下記の部分を消し、
##### for LCD (省略) ##### End of LCD下記の内容で差し替える必要があります。この変更も、本ページのすべてのプログラムで共通です。
def write_string(s): passさらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。
記述できたら Pico W / Pico 2 W でプログラムを実行してみましょう。LCD 上に(または Thonny のコンソール上に)Pico W に割り当てられた IP アドレス(例えば、 192.168.1.40 のようなもの)が表示されれば、実行が成功した可能性が高いでしょう。下図のような状態です。 そうしたら、Pico W / Pico 2 W と同じネットワーク内に存在する PC やスマートフォンのブラウザでアドレス(例えば 192.168.1.40 の場合、 http://192.168.1.40/ )にアクセスしてみましょう。下図のようなページが現れるはずです。 ブラウザ上のボタンを押すことで Pico W / Pico 2 W に接続された LED の点灯/消灯が切り替わります。LED 点灯時の画面の様子は下記の通りで、ボタンの色が変わります。これは、本書の Raspberry Pi 版のプログラムと同じ挙動です。ただし、上で既に述べたように、回路のアクションはブラウザ上のページの再読み込みのタイミングでしか起こりませんので、反応が遅いという問題がありますのでご了承ください。これは、本ページの以下のプログラム全てに当てはまります。 なお、ここでの解説がよくわからないという場合、本書 9 章の記述を読み直してみることを推奨します。本ページは、本書読者の方を対象としているため、本書と重複する内容の記述は最低限にとどめています。
2. ブラウザへの温度センサの値の表示
PCやスマートフォンのブラウザに、Pico W / Pico 2 W に接続された I2C 温度センサの値を表示する演習を行ってみましょう。9.4 章の内容です。必要な回路は下図の通りです。 そして、この回路上で実行すべきプログラムは下記の通りです。import time import sys import network import socket from time import sleep from machine import Pin, I2C ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) i2c.scan() ##### for temperature sensor def read_adt7410(): word_data = int.from_bytes(i2c.readfrom_mem(address_adt7410, register_adt7410, 4), 'little') data = (word_data & 0xff00)>>8 | (word_data & 0xff)<<8 data = data>>3 # 13ビットデータ if data & 0x1000 == 0: # 温度が正または0の場合 temperature = data*0.0625 else: # 温度が負の場合、 絶対値を取ってからマイナスをかける temperature = ( (~data&0x1fff) + 1)*-0.0625 return temperature address_adt7410 = 0x48 register_adt7410 = 0x00 ##### end of temperature sensor ##### for LCD def setup_st7032(): c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c])) sleep(0.2) i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x0d, 0x01])) sleep(0.001) def clear(): global position global line position = 0 line = 0 i2c.writeto_mem(address_st7032, register_setting, bytes([0x01])) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line i2c.writeto_mem(address_st7032, register_setting, bytes([0xc0])) sleep(0.001) def write_string(s): for c in list(s): write_char(ord(c)) def write_char(c): global position byte_data = check_writable(c) if position == display_chars: clear() elif position == chars_per_line*(line+1): newline() i2c.writeto_mem(address_st7032, register_display, bytes([byte_data])) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 address_st7032 = 0x3e register_setting = 0x00 register_display = 0x40 contrast = 32 # 0から63のコントラスト。30から40程度を推奨 chars_per_line = 8 # LCDの横方向の文字数 display_lines = 2 # LCDの行数 display_chars = chars_per_line*display_lines position = 0 line = 0 setup_st7032() ##### end of LCD wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) # refresh per 5 sec. html = """<!DOCTYPE html><html> <head><meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <meta http-equiv="refresh" content="5"> <title>2 - I2C温度センサ(ADT7410)による温度の取得</title> </head> <div align="center"> <p>温度: %s</p> </div> </body></html> """ # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') write_string('waiting for conn') #showing on LCD time.sleep(1) # Handle connection error if wlan.status() != 3: write_string('conn. failed') # showing on LCD raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) write_string(status[0]) #showing on LCD # Open socket addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind(addr) except OSError as e: print(e) s.close() s = None write_string('socket error') #showing on LCD sys.exit() s.listen(1) print('listening on', addr) # Listen for connections, serve client while True: try: cl, addr = s.accept() #print('client connected from', addr) request = cl.recv(1024) #print("request:") #print(request) request = str(request) inputValue = read_adt7410() inputStr = '{:.1f}'.format(inputValue) response = html % inputStr cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') cl.send(response) cl.close() except OSError as e: print('connection closed') except KeyboardInterrupt as e: break s.close() s = Noneなお、「1. ブラウザのボタンによるLEDの点灯」で述べたように、YOUR_WIFI_SSID と YOUR_WIFI_PASSWORD を自分の環境のものに変更することと、 LCD なしでプログラムを実行したい場合は「# for LCD ~ # End of LCD」の部分の差し替えが必要ですのでご注意ください。
さらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。
以上のプログラムを実行し、ブラウザでアクセスすると下記のようなページが開きます。 5秒おきにページが再読み込みされ、センサの値がページ上で更新されるようになっています。 これは、プログラム中の下記の部分で設定されています。あまり頻繁に通信が行われるのも良くないですので、この部分を小さな値に変更することはお勧めしません。
<meta http-equiv="refresh" content="5">
3. ブラウザのボタンによるRGBフルカラーLED の制御
PCやスマートフォンのブラウザで、Pico W / Pico 2 W に接続された RGBフルカラーLED の色を制御する演習を行ってみましょう。9.5 章の内容です。なお、Rapsberry Pi を対象とした本書では、RGBフルカラーLEDの色の制御に、ブラウザのスライダを用いました。 しかし、Pico W / Pico 2 W ではスライダを用いるのは難しそうに思えましたので(専門的に言えば、JavaScript で取得したスライダの値を回路に反映する方法が思いつかない)、簡易的にボタンによる制御を行うことにしました。
必要な回路は下図の通りです。 そして、この回路上で実行すべきプログラムは下記の通りです。
import time import sys import network import socket from time import sleep from machine import Pin, PWM, I2C pwm1 = PWM(Pin(16)) pwm2 = PWM(Pin(17)) pwm3 = PWM(Pin(18)) pwm1.freq(100) pwm2.freq(100) pwm3.freq(100) pwm1.duty_u16(0) pwm2.duty_u16(0) pwm3.duty_u16(0) # アノードコモンの場合、下記の3行を有効に #pwm1.duty_u16(65535) #pwm2.duty_u16(65535) #pwm3.duty_u16(65535) i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) i2c.scan() ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' ##### for LCD def setup_st7032(): c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c])) sleep(0.2) i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x0d, 0x01])) sleep(0.001) def clear(): global position global line position = 0 line = 0 i2c.writeto_mem(address_st7032, register_setting, bytes([0x01])) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line i2c.writeto_mem(address_st7032, register_setting, bytes([0xc0])) sleep(0.001) def write_string(s): for c in list(s): write_char(ord(c)) def write_char(c): global position byte_data = check_writable(c) if position == display_chars: clear() elif position == chars_per_line*(line+1): newline() i2c.writeto_mem(address_st7032, register_display, bytes([byte_data])) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 address_st7032 = 0x3e register_setting = 0x00 register_display = 0x40 contrast = 32 # 0から63のコントラスト。30から40程度を推奨 chars_per_line = 8 # LCDの横方向の文字数 display_lines = 2 # LCDの行数 display_chars = chars_per_line*display_lines position = 0 line = 0 setup_st7032() ##### end of LCD wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) html = """<!DOCTYPE html><html> <head><meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <title>3 - RGBフルカラーLEDの制御</title> <style>button { width: auto; height: auto; background: #003366; font-weight: normal; font-size: 14pt; font-family: Arial, Helvetica, sans-serif; font-size: 14px; color: #ffffff; padding: 10px 20px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: 1px solid #003366; -moz-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); -webkit-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); text-shadow: 0px -1px 0px rgba(000,000,000,0.7), 0px 1px 0px rgba(255,255,255,0.3); } </style></head> <p>ボタンをクリックすることで、RGBフルカラーLEDの色が変わります。</p> <p>どの行のボタンが赤、青、緑の何色に対応するかは用いるRGBフルカラーLEDの種類によって異なります。</p> <div align="center"> <form>色 1 <button name="col1_0" value="on" type="submit" style="background: %s;"> 0 </button> <button name="col1_1" value="on" type="submit" style="background: %s;"> 1 </button> <button name="col1_2" value="on" type="submit" style="background: %s;"> 2 </button> <button name="col1_3" value="on" type="submit" style="background: %s;"> 3 </button> <button name="col1_4" value="on" type="submit" style="background: %s;"> 4 </button> </form> <br /><br /> <form>色 2 <button name="col2_0" value="on" type="submit" style="background: %s;"> 0 </button> <button name="col2_1" value="on" type="submit" style="background: %s;"> 1 </button> <button name="col2_2" value="on" type="submit" style="background: %s;"> 2 </button> <button name="col2_3" value="on" type="submit" style="background: %s;"> 3 </button> <button name="col2_4" value="on" type="submit" style="background: %s;"> 4 </button> </form> <br /><br /> <form>色 3 <button name="col3_0" value="on" type="submit" style="background: %s;"> 0 </button> <button name="col3_1" value="on" type="submit" style="background: %s;"> 1 </button> <button name="col3_2" value="on" type="submit" style="background: %s;"> 2 </button> <button name="col3_3" value="on" type="submit" style="background: %s;"> 3 </button> <button name="col3_4" value="on" type="submit" style="background: %s;"> 4 </button> </form> <br /><br /> </div> </body></html> """ # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') write_string('waiting for conn') #showing on LCD time.sleep(1) # Handle connection error if wlan.status() != 3: write_string('conn. failed') # showing on LCD raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) write_string(status[0]) #showing on LCD # Open socket addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind(addr) except OSError as e: print(e) s.close() s = None write_string('socket error') #showing on LCD sys.exit() s.listen(1) print('listening on', addr) onCol = '#26a1ff' offCol = '#003366' col1_strs = (onCol, offCol, offCol, offCol, offCol) col2_strs = (onCol, offCol, offCol, offCol, offCol) col3_strs = (onCol, offCol, offCol, offCol, offCol) colall_strs = col1_strs + col2_strs + col3_strs buttonValMax = 4 # Listen for connections, serve client while True: try: cl, addr = s.accept() #print('client connected from', addr) request = cl.recv(1024) #print("request:") #print(request) request = str(request) col1_0_on = request.find('col1_0=on') col1_1_on = request.find('col1_1=on') col1_2_on = request.find('col1_2=on') col1_3_on = request.find('col1_3=on') col1_4_on = request.find('col1_4=on') col2_0_on = request.find('col2_0=on') col2_1_on = request.find('col2_1=on') col2_2_on = request.find('col2_2=on') col2_3_on = request.find('col2_3=on') col2_4_on = request.find('col2_4=on') col3_0_on = request.find('col3_0=on') col3_1_on = request.find('col3_1=on') col3_2_on = request.find('col3_2=on') col3_3_on = request.find('col3_3=on') col3_4_on = request.find('col3_4=on') if col1_0_on == 8: col1_strs = (onCol, offCol, offCol, offCol, offCol) duty = 0 if col1_1_on == 8: col1_strs = (offCol, onCol, offCol, offCol, offCol) duty = 1 if col1_2_on == 8: col1_strs = (offCol, offCol, onCol, offCol, offCol) duty = 2 if col1_3_on == 8: col1_strs = (offCol, offCol, offCol, onCol, offCol) duty = 3 if col1_4_on == 8: col1_strs = (offCol, offCol, offCol, offCol, onCol) duty = 4 if col2_0_on == 8: col2_strs = (onCol, offCol, offCol, offCol, offCol) duty = 0 if col2_1_on == 8: col2_strs = (offCol, onCol, offCol, offCol, offCol) duty = 1 if col2_2_on == 8: col2_strs = (offCol, offCol, onCol, offCol, offCol) duty = 2 if col2_3_on == 8: col2_strs = (offCol, offCol, offCol, onCol, offCol) duty = 3 if col2_4_on == 8: col2_strs = (offCol, offCol, offCol, offCol, onCol) duty = 4 if col3_0_on == 8: col3_strs = (onCol, offCol, offCol, offCol, offCol) duty = 0 if col3_1_on == 8: col3_strs = (offCol, onCol, offCol, offCol, offCol) duty = 1 if col3_2_on == 8: col3_strs = (offCol, offCol, onCol, offCol, offCol) duty = 2 if col3_3_on == 8: col3_strs = (offCol, offCol, offCol, onCol, offCol) duty = 3 if col3_4_on == 8: col3_strs = (offCol, offCol, offCol, offCol, onCol) duty = 4 # アノードコモンの場合、下記の行を有効に #duty = buttonValMax - duty if col1_0_on == 8 or col1_1_on == 8 or col1_2_on == 8 or col1_3_on == 8 or col1_4_on == 8: pwm1.duty_u16(int(duty*65535/buttonValMax)) if col2_0_on == 8 or col2_1_on == 8 or col2_2_on == 8 or col2_3_on == 8 or col2_4_on == 8: pwm2.duty_u16(int(duty*65535/buttonValMax)) if col3_0_on == 8 or col3_1_on == 8 or col3_2_on == 8 or col3_3_on == 8 or col3_4_on == 8: pwm3.duty_u16(int(duty*65535/buttonValMax)) colall_strs = col1_strs + col2_strs + col3_strs response = html % colall_strs cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') cl.send(response) cl.close() except OSError as e: print('connection closed') except KeyboardInterrupt as e: break s.close() s = Noneなお、「1. ブラウザのボタンによるLEDの点灯」で述べたように、YOUR_WIFI_SSID と YOUR_WIFI_PASSWORD を自分の環境のものに変更することと、 LCD なしでプログラムを実行したい場合は「# for LCD ~ # End of LCD」の部分の差し替えが必要ですのでご注意ください。
さらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。
以上のプログラムを実行じ、ブラウザで LCD 上のアドレスにアクセスすると、下記のようなページが開きます。スライダの代わりに、ボタンによってRGBフルカラーLEDの3つの色の明るさを調整する仕組みです。
4. ブラウザのボタンによる DC モーターの速度制御
PCやスマートフォンのブラウザで、Pico W / Pico 2 W に接続された DC モーターの回転速度を制御する演習を行ってみましょう。9.6 章の内容です。なお、Rapsberry Pi を対象とした本書では、DC モーターの速度制御に、ブラウザのタッチイベントを用いました。 しかし、Pico W / Pico 2 W ではタッチイベントを用いるのは難しそうに思えましたので(専門的に言えば、JavaScript で取得したタッチイベントの情報を回路に反映する方法が思いつかない)、簡易的にボタンによる制御を行うことにしました。
必要な回路は下図の通りです。
import time import sys import network import socket from time import sleep from machine import Pin, PWM, I2C pwm1 = PWM(Pin(16)) pwm2 = PWM(Pin(17)) pwm1.freq(100) pwm2.freq(100) pwm1.duty_u16(0) pwm2.duty_u16(0) i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) i2c.scan() ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' ##### for LCD def setup_st7032(): c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c])) sleep(0.2) i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x0d, 0x01])) sleep(0.001) def clear(): global position global line position = 0 line = 0 i2c.writeto_mem(address_st7032, register_setting, bytes([0x01])) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line i2c.writeto_mem(address_st7032, register_setting, bytes([0xc0])) sleep(0.001) def write_string(s): for c in list(s): write_char(ord(c)) def write_char(c): global position byte_data = check_writable(c) if position == display_chars: clear() elif position == chars_per_line*(line+1): newline() i2c.writeto_mem(address_st7032, register_display, bytes([byte_data])) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 address_st7032 = 0x3e register_setting = 0x00 register_display = 0x40 contrast = 32 # 0から63のコントラスト。30から40程度を推奨 chars_per_line = 8 # LCDの横方向の文字数 display_lines = 2 # LCDの行数 display_chars = chars_per_line*display_lines position = 0 line = 0 setup_st7032() ##### end of LCD wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) html = """<!DOCTYPE html><html> <head><meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <title>4 - ボタンによるDCモーターの速度制御</title> <style>button { width: auto; height: auto; background: #003366; font-weight: normal; font-size: 14pt; font-family: Arial, Helvetica, sans-serif; font-size: 14px; color: #ffffff; padding: 10px 20px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: 1px solid #003366; -moz-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); -webkit-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); text-shadow: 0px -1px 0px rgba(000,000,000,0.7), 0px 1px 0px rgba(255,255,255,0.3); } </style></head> <p>押したボタンにより、モーターが回転を開始します。中央のボタンをクリックしないとモーターは止まりません。</p> <div align="center"> <form><button name="vel_n2" value="on" type="submit" style="background: %s;"> -2 </button> <button name="vel_n1" value="on" type="submit" style="background: %s;"> -1 </button> <button name="vel_0" value="on" type="submit" style="background: %s;"> 0 </button> <button name="vel_p1" value="on" type="submit" style="background: %s;"> +1 </button> <button name="vel_p2" value="on" type="submit" style="background: %s;"> +2 </button> </form> <br /><br /> </div> </body></html> """ # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') write_string('waiting for conn') #showing on LCD time.sleep(1) # Handle connection error if wlan.status() != 3: write_string('conn. failed') # showing on LCD raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) write_string(status[0]) #showing on LCD # Open socket addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind(addr) except OSError as e: print(e) s.close() s = None write_string('socket error') #showing on LCD sys.exit() s.listen(1) print('listening on', addr) onCol = '#26a1ff' offCol = '#003366' vel_strs = (offCol, offCol, onCol, offCol, offCol) duty_max = 0.7 # Listen for connections, serve client while True: try: cl, addr = s.accept() #print('client connected from', addr) request = cl.recv(1024) #print("request:") #print(request) request = str(request) vel_n2_on = request.find('vel_n2=on') vel_n1_on = request.find('vel_n1=on') vel_0_on = request.find('vel_0=on') vel_p1_on = request.find('vel_p1=on') vel_p2_on = request.find('vel_p2=on') if vel_n2_on == 8: vel_strs = (onCol, offCol, offCol, offCol, offCol) pwm2.duty_u16(0) pwm1.duty_u16(int(65535*duty_max)) if vel_n1_on == 8: vel_strs = (offCol, onCol, offCol, offCol, offCol) pwm2.duty_u16(0) pwm1.duty_u16(int(65535*duty_max/2)) if vel_0_on == 8: vel_strs = (offCol, offCol, onCol, offCol, offCol) pwm1.duty_u16(0) pwm2.duty_u16(0) if vel_p1_on == 8: vel_strs = (offCol, offCol, offCol, onCol, offCol) pwm1.duty_u16(0) pwm2.duty_u16(int(65535*duty_max/2)) if vel_p2_on == 8: vel_strs = (offCol, offCol, offCol, offCol, onCol) pwm1.duty_u16(0) pwm2.duty_u16(int(65535*duty_max)) response = html % vel_strs cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') cl.send(response) cl.close() except OSError as e: print('connection closed') except KeyboardInterrupt as e: break s.close() s = Noneなお、「1. ブラウザのボタンによるLEDの点灯」で述べたように、YOUR_WIFI_SSID と YOUR_WIFI_PASSWORD を自分の環境のものに変更することと、 LCD なしでプログラムを実行したい場合は「# for LCD ~ # End of LCD」の部分の差し替えが必要ですのでご注意ください。
さらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。
以上のプログラムを実行し、LCD上のアドレスにブラウザでアクセスすると、下記のようなページが開きます。タッチの代わりに、ボタンでモーターの回転の向きと速度を調整する仕組みです。
5. ブラウザのボタンによるサーボモーターの角度制御
PCやスマートフォンのブラウザで、Pico W / Pico 2 W に接続されたサーボモーターの角度を制御する演習を行ってみましょう。9.7 章(付録 PDF)の内容です。なお、Rapsberry Pi を対象とした本書では、サーボモーターの角度制御に、ブラウザのスライダを用いました。 しかし、Pico W / Pico 2 W ではスライダを用いるのは難しそうに思えましたので(専門的に言えば、JavaScript で取得したスライダの値を回路に反映する方法が思いつかない)、簡易的にボタンによる制御を行うことにしました。
必要な回路は下図の通りです。
import time import sys import network import socket from time import sleep from machine import Pin, PWM, I2C def servo_duty_hwpwm_web(val): val_min = 0 val_max = 4 servo_min = 0.035 # 最小デューティ比3.5% servo_max = 0.1 # 最大デューティ比10% if val < val_min: val = val_min duty = (servo_min-servo_max)*(val-val_min)/(val_max-val_min) + servo_max # 一般的なサーボモーターはこちらを有効に #duty = (servo_max-servo_min)*(val-val_min)/(val_max-val_min) + servo_min return duty pwm1 = PWM(Pin(16)) pwm1.freq(50) pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(2))) i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) i2c.scan() ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' ##### for LCD def setup_st7032(): c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c])) sleep(0.2) i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x0d, 0x01])) sleep(0.001) def clear(): global position global line position = 0 line = 0 i2c.writeto_mem(address_st7032, register_setting, bytes([0x01])) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line i2c.writeto_mem(address_st7032, register_setting, bytes([0xc0])) sleep(0.001) def write_string(s): for c in list(s): write_char(ord(c)) def write_char(c): global position byte_data = check_writable(c) if position == display_chars: clear() elif position == chars_per_line*(line+1): newline() i2c.writeto_mem(address_st7032, register_display, bytes([byte_data])) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 address_st7032 = 0x3e register_setting = 0x00 register_display = 0x40 contrast = 32 # 0から63のコントラスト。30から40程度を推奨 chars_per_line = 8 # LCDの横方向の文字数 display_lines = 2 # LCDの行数 display_chars = chars_per_line*display_lines position = 0 line = 0 setup_st7032() ##### end of LCD wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) html = """<!DOCTYPE html><html> <head><meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <title>7 - サーボモーターの制御</title> <style>button { width: auto; height: auto; background: #003366; font-weight: normal; font-size: 14pt; font-family: Arial, Helvetica, sans-serif; font-size: 14px; color: #ffffff; padding: 10px 20px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: 1px solid #003366; -moz-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); -webkit-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); text-shadow: 0px -1px 0px rgba(000,000,000,0.7), 0px 1px 0px rgba(255,255,255,0.3); } </style></head> <p>押したボタンにより、サーボモーターの位置が決まります。</p> <div align="center"> <form><button name="pos0" value="on" type="submit" style="background: %s;"> 0 </button> <button name="pos1" value="on" type="submit" style="background: %s;"> 1 </button> <button name="pos2" value="on" type="submit" style="background: %s;"> 2 </button> <button name="pos3" value="on" type="submit" style="background: %s;"> 3 </button> <button name="pos4" value="on" type="submit" style="background: %s;"> 4 </button> </form> <br /><br /> </div> </body></html> """ # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') write_string('waiting for conn') #showing on LCD time.sleep(1) # Handle connection error if wlan.status() != 3: write_string('conn. failed') # showing on LCD raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) write_string(status[0]) #showing on LCD # Open socket addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind(addr) except OSError as e: print(e) s.close() s = None write_string('socket error') #showing on LCD sys.exit() s.listen(1) print('listening on', addr) onCol = '#26a1ff' offCol = '#003366' pos_strs = (offCol, offCol, onCol, offCol, offCol) # Listen for connections, serve client while True: try: cl, addr = s.accept() #print('client connected from', addr) request = cl.recv(1024) #print("request:") #print(request) request = str(request) pos0_on = request.find('pos0=on') pos1_on = request.find('pos1=on') pos2_on = request.find('pos2=on') pos3_on = request.find('pos3=on') pos4_on = request.find('pos4=on') if pos0_on == 8: pos_strs = (onCol, offCol, offCol, offCol, offCol) pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(0))) if pos1_on == 8: pos_strs = (offCol, onCol, offCol, offCol, offCol) pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(1))) if pos2_on == 8: pos_strs = (offCol, offCol, onCol, offCol, offCol) pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(2))) if pos3_on == 8: pos_strs = (offCol, offCol, offCol, onCol, offCol) pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(3))) if pos4_on == 8: pos_strs = (offCol, offCol, offCol, offCol, onCol) pwm1.duty_u16(int(65535*servo_duty_hwpwm_web(4))) response = html % pos_strs cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') cl.send(response) cl.close() except OSError as e: print('connection closed') except KeyboardInterrupt as e: break s.close() s = Noneなお、「1. ブラウザのボタンによるLEDの点灯」で述べたように、YOUR_WIFI_SSID と YOUR_WIFI_PASSWORD を自分の環境のものに変更することと、 LCD なしでプログラムを実行したい場合は「# for LCD ~ # End of LCD」の部分の差し替えが必要ですのでご注意ください。
さらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny の「STOP (停止)」ボタンから一旦停止する必要があることもご注意ください。
以上のプログラムを実行し、LCD上のアドレスにブラウザでアクセスすると、下記のようなページが開きます。スライダの代わりに、ボタンでサーボモーターの位置を調整する仕組みです。
6. ブラウザのボタンによるキャタピラ式模型の制御
PCやスマートフォンのブラウザで、Pico W / Pico 2 W に接続されたキャタピラ式模型を制御する演習を行ってみましょう。10 章の内容です。ここまでの演習同様、「前進」、「後退」、「右旋回」、「左旋回」、「静止」という 5 つのボタンによる簡易的な制御ですのでご了承ください。
import time import sys import network import socket from time import sleep from machine import Pin, PWM, I2C pwm1 = PWM(Pin(16)) pwm2 = PWM(Pin(17)) pwm3 = PWM(Pin(18)) pwm4 = PWM(Pin(19)) pwm1.freq(100) pwm2.freq(100) pwm3.freq(100) pwm4.freq(100) pwm1.duty_u16(0) pwm2.duty_u16(0) pwm3.duty_u16(0) pwm4.duty_u16(0) i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) i2c.scan() ssid = 'YOUR_WIFI_SSID' password = 'YOUR_WIFI_PASSWORD' ##### for LCD def setup_st7032(): c_lower = (contrast & 0xf) c_upper = (contrast & 0x30)>>4 i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x39, 0x14, 0x70|c_lower, 0x54|c_upper, 0x6c])) sleep(0.2) i2c.writeto_mem(address_st7032, register_setting, bytes([0x38, 0x0d, 0x01])) sleep(0.001) def clear(): global position global line position = 0 line = 0 i2c.writeto_mem(address_st7032, register_setting, bytes([0x01])) sleep(0.001) def newline(): global position global line if line == display_lines-1: clear() else: line += 1 position = chars_per_line*line i2c.writeto_mem(address_st7032, register_setting, bytes([0xc0])) sleep(0.001) def write_string(s): for c in list(s): write_char(ord(c)) def write_char(c): global position byte_data = check_writable(c) if position == display_chars: clear() elif position == chars_per_line*(line+1): newline() i2c.writeto_mem(address_st7032, register_display, bytes([byte_data])) position += 1 def check_writable(c): if c >= 0x06 and c <= 0xff : return c else: return 0x20 # 空白文字 address_st7032 = 0x3e register_setting = 0x00 register_display = 0x40 contrast = 32 # 0から63のコントラスト。30から40程度を推奨 chars_per_line = 8 # LCDの横方向の文字数 display_lines = 2 # LCDの行数 display_chars = chars_per_line*display_lines position = 0 line = 0 setup_st7032() ##### end of LCD wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(ssid, password) html = """<!DOCTYPE html><html> <head><meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <title>5 - クローラーの操作</title> <style>button { width: auto; height: auto; background: #003366; font-weight: normal; font-size: 14pt; font-family: Arial, Helvetica, sans-serif; font-size: 14px; color: #ffffff; padding: 10px 20px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; border: 1px solid #003366; -moz-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); -webkit-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.5); text-shadow: 0px -1px 0px rgba(000,000,000,0.7), 0px 1px 0px rgba(255,255,255,0.3); } </style></head> <p>押したボタンにより、クローラーが移動します。中央のボタンをクリックしないとクローラーは止まりません。</p> <div align="center"> <table border="0"> <tr align="center"> <td></td> <td><form><button name="forward" value="on" type="submit" style="background: %s;"> ↑ </button></form></td> <td></td> </tr> <tr align="center"> <td><form><button name="rot_l" value="on" type="submit" style="background: %s;"> ← </button></form></td> <td><form><button name="stop" value="on" type="submit" style="background: %s;"> 〇 </button></form></td> <td><form><button name="rot_r" value="on" type="submit" style="background: %s;"> → </button></form></td> </tr> <tr align="center"> <td></td> <td><form><button name="backward" value="on" type="submit" style="background: %s;"> ↓ </button></form></td> <td></td> </tr> </table> <br /><br /> </div> </body></html> """ # Wait for connect or fail max_wait = 20 while max_wait > 0: if wlan.status() < 0 or wlan.status() >= 3: break max_wait -= 1 print('waiting for connection...') write_string('waiting for conn') #showing on LCD time.sleep(1) # Handle connection error if wlan.status() != 3: write_string('conn. failed') # showing on LCD raise RuntimeError('network connection failed') else: print('Connected') status = wlan.ifconfig() print( 'ip = ' + status[0] ) write_string(status[0]) #showing on LCD # Open socket addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind(addr) except OSError as e: print(e) s.close() s = None write_string('socket error') #showing on LCD sys.exit() s.listen(1) print('listening on', addr) onCol = '#26a1ff' offCol = '#003366' move_strs = (offCol, offCol, onCol, offCol, offCol) duty_max = 0.7 # Listen for connections, serve client while True: try: cl, addr = s.accept() #print('client connected from', addr) request = cl.recv(1024) #print("request:") #print(request) request = str(request) forward_on = request.find('forward=on') rot_l_on = request.find('rot_l=on') stop_on = request.find('stop=on') rot_r_on = request.find('rot_r=on') backward_on = request.find('backward=on') if forward_on == 8: move_strs = (onCol, offCol, offCol, offCol, offCol) pwm2.duty_u16(0) pwm1.duty_u16(int(65535*duty_max)) pwm4.duty_u16(0) pwm3.duty_u16(int(65535*duty_max)) if rot_l_on == 8: move_strs = (offCol, onCol, offCol, offCol, offCol) pwm1.duty_u16(0) pwm2.duty_u16(int(65535*duty_max)) pwm4.duty_u16(0) pwm3.duty_u16(int(65535*duty_max)) if stop_on == 8: move_strs = (offCol, offCol, onCol, offCol, offCol) pwm1.duty_u16(0) pwm2.duty_u16(0) pwm3.duty_u16(0) pwm4.duty_u16(0) if rot_r_on == 8: move_strs = (offCol, offCol, offCol, onCol, offCol) pwm2.duty_u16(0) pwm1.duty_u16(int(65535*duty_max)) pwm3.duty_u16(0) pwm4.duty_u16(int(65535*duty_max)) if backward_on == 8: move_strs = (offCol, offCol, offCol, offCol, onCol) pwm1.duty_u16(0) pwm2.duty_u16(int(65535*duty_max)) pwm3.duty_u16(0) pwm4.duty_u16(int(65535*duty_max)) response = html % move_strs cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') cl.send(response) cl.close() except OSError as e: print('connection closed') except KeyboardInterrupt as e: break s.close() s = Noneなお、「1. ブラウザのボタンによるLEDの点灯」で述べたように、YOUR_WIFI_SSID と YOUR_WIFI_PASSWORD を自分の環境のものに変更することと、 LCD なしでプログラムを実行したい場合は「# for LCD ~ # End of LCD」の部分の差し替えが必要ですのでご注意ください。
さらに、プログラムの書き換え時は、Pico W / Pico 2 W 上で動作しているプログラムを Thonny から一旦停止する必要があることもご注意ください。
以上のプログラムを実行し、LCD上のアドレスにブラウザでアクセスすると、下記のようなページが開きます。タッチの代わりに、ボタンでキャタピラ式模型の移動方法を変更する仕組みです。
補足情報「Raspberry Pi Pico W を使って Wifi 経由で回路を制御してみよう」はとても参考になりました。この記事を参考にPicoを使い始めましたが、簡単な制御はPicoで十分だと実感しました。
返信削除プログラムはwlan = network.WLAN(network.STA_IF)というSTAモード設定で書かれていますが、wlan = network.WLAN(network.AP_IF)というAPモードにすれば、わざわざWifiルータを中継してPicoを制御しなくても直接Picoを制御できそうですが、あえてSTAモードにしているのは何か不都合なことが生じるのでしょうか。教えていただけるとありがたいです。よろしくお願いいたします。
コメントありがとうございます。
削除STAモードでプログラムが書かれているのは、
参考にした下記のサイトでSTAモードを用いていたからです。
https://core-electronics.com.au/guides/raspberry-pi-pico-w-create-a-simple-http-server/
恐らく、APモードでプログラムを書いた場合でも問題なく
動作するのではないかと思います。
ただし、いま手元に Pico W がないので試すことができないのですが、
AP モードに変更するために多少のコードの変更は必要かもしれません。