LoginSignup
1
2

More than 1 year has passed since last update.

CircuitPythonにてESP32にWebサーバを構築する2つのモード

Last updated at Posted at 2023-03-15

「2つのモード」とは、

一つはAPモード、もう一つはステーションモードのこと。

  • APモード   ESP32をWifiのアクセスポイントにしてローカルWebサーバーを構築する場合に使うモード
  • ステーションモード   ESP32を既設のWifiルータに接続することでインターネットにもアクセスできるWebサーバを構築する場合に使うモード

adafruit_httpserver.serverを使ったCircuitPythonでのコーディング例が少ないので、ここにまとめておく。特にAPモードの例はまったく見つけられなかった。

Wifiの設定

1. APモード

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アドレスとする場合は、下記のコードを追加する。

ステーションモード:固定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サーバのポート番号と合わせておくとよい。

It is possible to use the mDNS protocol to make the server accessible via a hostname in addition to an IP address.

#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のバンドルライブラリを使用する。

HTTPサーバ
# 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サーバ

ローカルWebサーバ(CircuitPython)
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

index.html(Bootstrap使用)
<!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$ &nbsp;</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を実装)

IMG_0018.PNG IMG_1518.png

まとめ

Wifi設定HTTPサーバ設定の2段階で、ESP32にWebサーバを構築できる。

APモードでWebサーバを構築した場合は、BootstrapやJQueryのCDNにアクセスできないため、あらかじめダウンロードしておき、ESP32にコピーを置いて使う必要がある。

当初、MicroWebSrv2をCircuitPythonで使おうと試みたが、簡単には行かず断念した。


以上

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2