モブ沢工房

プログラミングとかLinux関連(特にOSSのグラフィックツール関連)とかレトロゲームとか3Dプリンタやら日曜大工等、色々。

超久々にRaspberryPi pico wでhttpサーバなLチカ!

超久々ですが、RaspberryPi Pico Wを入手できたため、micropythonにて公式サイトから拾えるraspberry-pi-pico-python-sdk.pdfにあるサンプルを参考にhttpサーバでLチカしてみました。 単にLチカしてもアレなので多少工夫を加えてあります

  • Thonnyで開発したのですが、そのままだと再起動するたびにEADDRINUSEが出て引っこ抜く事になるためにsetsocketoptなどしてあります
  • その他、丁寧にfinally節でcloseしてます
  • ledピンはPin("LED", Pin.OUT)で得るのが無難なようです
  • 何故かルータがいきなり初手でリクエストなしでアクセスしてきて開きっぱなしにするため、timeoutを付けました (不気味…)
  • サーバっぽくos.fork()で処理したかったんだけどunixにしか無いんですなコレ…multiprocessingとかthreadはmicropythonで出来るのかどうか知らないので、手っ取り早くタイムアウトだけにしました

使い方は、本家のようにurlに/light/on で点灯、 /light/offで消灯ですが、それに加え、 /light/0x0f などとすると、8ビットの数値のビットパターンで、0だと「短く点灯」1だと「普通に点灯」で点滅により16進数を表現します。

さらに、/quitでサーバをシャットダウンしてwifiを切断します。

さすがmicroとはいえpythonだけあって色々簡単に出来て、面白いですね😆

なお、Thonnyでインタプリタを設定することでPico Wに直で開発中のコードを送り込んで実行することが可能なのですが、ubuntu 20.04.6で入るThonny 3.2.7にはPico Wの項目がありません…(当たり前かw)

しかしながら、これはesp32を指定すれば問題なく実行出来ました。まぁpythonだし大丈夫だろ…ということで😁

import network
import socket
import time
import os

from machine import Pin

def send_response(cl, msg):
    global html
    response = html % msg

    cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
    cl.send(response)
    
led = Pin("LED", Pin.OUT)

ssid = 'your ssid'
password = 'your password'

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)

html = """<!DOCTYPE html>
<html>
    <head> <title>Pico W</title> </head>
    <body> <h1>Pico W</h1>
        <p>%s</p>
    </body>
</html>
"""

max_wait = 10
while max_wait > 0:
    if wlan.status() < 0 or wlan.status() >= 3:
        break
    max_wait -= 1
    print('waiting for connection...')
    time.sleep(1)

if wlan.status() != 3:
    raise RuntimeError('network connection failed')
else:
    print('connected')
    status = wlan.ifconfig()
    print( 'ip = ' + status[0] )

addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]

s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # To avoid EADDRINUSE
s.bind(addr)
s.settimeout(5) # Some router accesses without request and connect forever.
s.listen(1)

print('listening on', addr)

# Listen for connections

unit = 0.5 # led blink unit in second
try:
    while True:
        try:
            cl = None # for finally block.
            cl, addr = s.accept()
            print('client connected from', addr)
            ipaddr,port = addr
            print("waiting request...")
            request = cl.recv(1024)
            print(request)

            request = str(request)
            reqtail = request.find(' HTTP/1.1')
            if reqtail < 0:
                print("no request body")
                continue
            reqbody = request[6:reqtail].strip()
            print(reqbody)
            
            led_status = None
            if reqbody == '/quit':
                print("quit server.")
                send_response(cl, "Server shutdown.")
                break
            elif reqbody == '/light/on':
                led_status = True
            elif reqbody == '/light/off':
                led_status = False
            elif reqbody.startswith('/light/0x'):
                led_status = reqbody[7:11]
                
            if led_status is None:
                print("invalid request.")
                stateis = "invalid"
            elif led_status == True:
                led.value(1)
                stateis = "led on"
            elif led_status == False:
                led.value(0)
                stateis = "led off"
            else:
                # burst blink mode.
                try:
                    value = int(led_status, 16)
                    stateis = "blink : %s" % led_status
                    for i in range(8):
                        led.value(1)
                        cv = (value >> i) & 1
                        if cv == 0:
                            time.sleep(unit/3)
                        else:
                            time.sleep(unit)
                        led.value(0)
                        time.sleep(unit)
                            
                except ValueError:
                    stateis = "Failed to convert : %s" % led_status              

            send_response(cl, stateis)

        except OSError as e:
            pass
        finally:
            if cl is not None:
                print('connection closed')
                cl.close()
                cl = None

finally:
    s.close()
    wlan.disconnect()