「2つのモード」とは、
一つはAPモード、もう一つはステーションモードのこと。
- APモード ESP32をWifiのアクセスポイントにしてローカルWebサーバーを構築する場合に使うモード
- ステーションモード ESP32を既設のWifiルータに接続することでインターネットにもアクセスできるWebサーバを構築する場合に使うモード
adafruit_httpserver.server
を使ったCircuitPythonでのコーディング例が少ないので、ここにまとめておく。特にAPモードの例はまったく見つけられなかった。
Wifiの設定
1. APモード
import wifi
ssid = 'XXXXX' #公開するSSID
psk = 'YYYYYY' #パスワード
wifi.radio.start_ap(ssid=ssid, password=psk)
print(wifi.radio.ipv4_address_ap)
# 192.168.4.1
スマホやPCから上記で指定したSSIDに接続する。
passwordは省略できるが、セキュリティが弱くなるので省略はお勧めしない。
APのIPアドレスは192.168.4.1
。これを変更する方法は分からない。
スマホやPCをこのSSIDに接続すると、192.168.4.2〜
のIPアドレスが割り当てられる。
2. ステーションモード
import wifi
ssid = 'XXXXX' #接続するSSID
psk = 'YYYYYY' #パスワード
wifi.radio.connect(ssid=ssid, password=psk)
print(wifi.radio.ipv4_address)
# 192.168.xx.xx
IPアドレスは接続したWifiルーターによって払い出されたIPアドレスとなる。
もし、固定IPアドレスとする場合は、下記のコードを追加する。
import wifi
+ import ipaddress
+ ipv4 = ipaddress.IPv4Address("192.168.xx.xx")
+ netmask = ipaddress.IPv4Address("255.255.255.0")
+ gateway = ipaddress.IPv4Address("192.168.xx.1")
+ wifi.radio.set_ipv4_address(ipv4=ipv4, netmask=netmask, gateway=gateway)
ssid = 'XXXXX' #接続するSSID
psk = 'YYYYYY' #パスワード
wifi.radio.connect(ssid=ssid, password=psk)
print(wifi.radio.ipv4_address)
# 192.168.xx.xx
3. ホスト名の設定
APモード、ステーションモードのどちらの場合でも、mDNSプロトコルによるホスト名を設定することができる。
ポート番号は、この次に設定するHTTPサーバのポート番号と合わせておくとよい。
#wifi.radio.start_ap(ssid=ssid, password=psk)
# or
wifi.radio.connect(ssid=ssid, password=psk)
mdns_server = mdns.Server(wifi.radio)
mdns_server.hostname = "esp32s3board" #下記注記
mdns_server.advertise_service(service_type="_http", protocol="_tcp", port=80)
- ここで指定した
文字列.local
が実際のホスト名となる(上の例だとesp32s3board.local
)。
また、hostnameの設定を省略すると、cpy-xxyyzz.local
(xxyyzzはMACアドレスの下3バイト)となる。
ホスト名でアクセスできるので活用したい。
% ping esp32s3board.local
PING esp32s3board.local (192.168.4.1): 56 data bytes
64 bytes from 192.168.4.1: icmp_seq=2 ttl=255 time=6.971 ms
64 bytes from 192.168.4.1: icmp_seq=3 ttl=255 time=4.253 ms
64 bytes from 192.168.4.1: icmp_seq=4 ttl=255 time=8.206 ms
^C
--- esp32s3board.local ping statistics ---
5 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 4.253/6.477/8.206/3.634 ms
APモード、もしくは ステーションモードでWifiに接続できたので、次はHTTPサーバを設定する。
HTTPサーバ
adafruitのバンドルライブラリを使用する。
# already connected to WiFi
import socketpool
from adafruit_httpserver.server import HTTPServer
from adafruit_httpserver.request import HTTPRequest
from adafruit_httpserver.response import HTTPResponse
from adafruit_httpserver.methods import HTTPMethod
from adafruit_httpserver.mime_type import MIMEType
pool = socketpool.SocketPool(wifi.radio)
server = HTTPServer(pool)
@server.route("/")
def base(request: HTTPRequest):
with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response:
#response.send_file("index.html")
response.send("・・・・") #html
@server.route("/", method=HTTPMethod.POST)
def buttonpress(request: HTTPRequest):
raw_text = request.raw_request.decode("utf8")
print(raw_text)
# POSTされた情報に対応した処理を行う
with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response:
response.send("・・・・") #html
host_address = str(wifi.radio.ipv4_address) #ステーションモードの場合
#host_address = str(wifi.radio.ipv4_address_ap) # APモードの場合
server.start(host=host_address, port=8080) #ポート番号省略時は 80
while True:
try:
server.poll()
except OSError as error:
print(error)
continue
実装例
GPIO3に接続したWS2812Cを光らせる色を制御するローカルWebサーバ
import os
import time
import wifi
import socketpool
import board
import microcontroller
from adafruit_httpserver.server import HTTPServer
from adafruit_httpserver.request import HTTPRequest
from adafruit_httpserver.response import HTTPResponse
from adafruit_httpserver.methods import HTTPMethod
from adafruit_httpserver.mime_type import MIMEType
import neopixel
import random
from adafruit_datetime import datetime
# neopixel on board
pixel_pin = board.IO3
num_pixels = 1
OFF = (0, 0, 0)
RED = (255, 0, 0)
YELLOW = (255, 150, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
WHITE = (255, 255, 255)
COLORS = [RED, YELLOW, GREEN, CYAN, BLUE, PURPLE, WHITE]
pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.3, auto_write=True)
pixels[0] = OFF
last_color = None
#wifi ssid & psk
ssid = 'XXXXX' #公開するSSID
psk = 'YYYYYY' #パスワード
wifi.radio.start_ap(ssid=ssid, password=psk)
time.sleep(1)
print(f'WiFi AP mode Started! SSID is {ssid}')
pool = socketpool.SocketPool(wifi.radio)
server = HTTPServer(pool)
print(f'Web Server Started! Please access to http://{wifi.radio.ipv4_address_ap}')
document_root = '/www_root'
def webpage(filename, root):
filepath = root + '/' + filename
with open(filepath, 'r') as f:
html = f.read()
html = html.replace('$TEMP$', f'{microcontroller.cpu.temperature:.1f}')
html = html.replace('$NOW$', f'{datetime.now()}')
return html
@server.route("/")
def base(request: HTTPRequest):
with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response:
response.send(webpage('index.html', document_root))
@server.route("/", method=HTTPMethod.POST)
def buttonpress(request: HTTPRequest):
global last_color
raw_text = request.raw_request.decode("utf8")
print(raw_text)
last_color = None
if "RED" in raw_text:
last_color = RED
if "GREEN" in raw_text:
last_color = GREEN
if "BLUE" in raw_text:
last_color = BLUE
if "WHITE" in raw_text:
last_color = WHITE
if "YELLOW" in raw_text:
last_color = YELLOW
if "OFF" in raw_text:
last_color = OFF
if last_color is not None:
pixels[0] = last_color
elif "RANDOM" in raw_text:
for _ in range(15):
while (color := random.choice(COLORS)) == last_color:
pass
last_color = color
pixels[0] = color
time.sleep(0.1)
with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response:
response.send(webpage('index.html', document_root))
server.start(host=str(wifi.radio.ipv4_address_ap), root_path=document_root)
while True:
try:
server.poll()
except OSError as error:
print(error)
continue
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Local Web Server Demo</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<script src="js/popper.min.js"></script>
<script src="js/bootstrap.bundle.min.js"></script>
</head>
<body>
<p class="text-center bg-primary text-info h1">RGB-LED Color Controller</p>
<div id="wrap" style="display: block;">
<p class="h6">This is RGB-LED color controller on ESP32 original board using a local HTTP server with CircuitPython. </p>
<div class="container-fluid d-grid gap-2">
<div id="loader" style="display: block;">
<div class="spinner-border spinner-border text-muted" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<form accept-charset="utf-8" method="POST">
<button type="submit" class="btn btn-primary w-100" name="RGB" value="BLUE">Blue</button>
</form>
<form accept-charset="utf-8" method="POST">
<button type="submit" class="btn btn-success w-100" name="RGB" value="GREEN">Green</button>
</form>
<form accept-charset="utf-8" method="POST">
<button type="submit" class="btn btn-danger w-100" name="RGB" value="RED">Red</button>
</form>
<form accept-charset="utf-8" method="POST">
<button type="submit" class="btn btn-warning w-100" name="RGB" value="YELLOW">Yellow</button>
</form>
<form accept-charset="utf-8" method="POST">
<button type="submit" class="btn btn-outline-dark w-100" name="RGB" value="WHITE">White</button>
</form>
<form accept-charset="utf-8" method="POST">
<button type="submit" class="btn btn-secondary w-100" name="RGB" value="OFF">Off</button>
</form>
<form accept-charset="utf-8" method="POST">
<button type="submit" class="btn btn-info w-100" name="RGB" value="RANDOM">Random</button>
</form>
</form>
</div>
<hr>
<p class="lead text-center">CPU Temperature : $TEMP$ ゚C</p>
<p class="text-primary text-end h7">at $NOW$ </p>
</div>
<script>
window.onload = () => {
const loader = document.getElementById('loader');
loader.style.display = "none";
}
window.onsubmit = () => {
const loader = document.getElementById('loader');
loader.style.display = "block";
}
</script>
</body>
</html>
スマホから接続したときの画面と自作ボード(裏面にESP32S3を実装)
まとめ
Wifi設定とHTTPサーバ設定の2段階で、ESP32にWebサーバを構築できる。
APモードでWebサーバを構築した場合は、BootstrapやJQueryのCDNにアクセスできないため、あらかじめダウンロードしておき、ESP32にコピーを置いて使う必要がある。
当初、MicroWebSrv2をCircuitPythonで使おうと試みたが、簡単には行かず断念した。
以上