0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MicropythonでESP32のファームウェアアップデートをWi-Fiで行う方法

Last updated at Posted at 2025-11-04

はじめに

MicropythonでESP32のファームウェアアップデートをWi-Fiで行う方法を書きます.今回はサーバーにあるファームウェアをプル方式でダウンロードします.アップデートの方式はA/B パーティション方式です.ファームウェアはMicropythonのサイトのESP32/WROOMにあるOTAサポート版のものを使用します.ESP32にインストールしてあるファームウェアとアップデートに使用するファームウェアが,OTAサポート版でないとアップデートが行えないため注意してください.また,アップデートに使用するファームウェアは.app-binである必要があります.事前にESP32にインストールしておくファームウェアは.binのものにしてください.

環境

◯ESP32

  • ファームウェア(アップデート前):v1.26.0 (2025-08-09)
  • ファームウェア(アップデート後):v1.26.1 (2025-09-11)

ファイル構成

root/ 
    ├─ boot.py
    └─ ota.py

◯サーバー

  • OS:ubuntu-24.04/LTS
  • 実行環境:Python 3.12.3

ファイル構成

user/ 
    └─ OTA/ 
        └─ ESP32_GENERIC-OTA-20250911-v1.26.1.app-bin ⇒ ファームウェア

サンプルコード

ESP32

boot.py

boot.py
import time
from esp32 import Partition
import uos as os
import network
import ota      

def _log_info():
    run = Partition(Partition.RUNNING)
    info = run.info()
    label = info[4] if len(info) >= 5 else None
    label = None
    u = os.uname()

    fw_ver = getattr(u, 'version', None) or getattr(u, 'release', None)
    sta = network.WLAN(network.STA_IF)
    if not sta.active():
        sta.active(True)
    mb = sta.config('mac')
    mac = ':'.join('%02x' % b for b in mb)
    
    print('[BOOT] running=', label, 'fw=', fw_ver, 'mac=', mac)


def _run():
    _log_info()
    Partition.mark_app_valid_cancel_rollback()

    for i in range(3, 0, -1):
        print('[BOOT] starting OTA in', i)
        time.sleep(1)
    try:
        ota.run()
    except Exception as ex:
        print('[BOOT][ERR]', ex)

_run()

ota.py

コードを使用する際は,WIFI_SSIDWIFI_PASSFW_URLを自身のものに変更してください.

ota.py
import time, network, urequests as rq, uhashlib as uh, esp32, machine, esp

# ===== CONFIG =====
WIFI_SSID = "Wi-FiのSSID"
WIFI_PASS = "Wi-Fiのパスワード"
FW_URL    = "http://サーバーのホスト名:任意のポート番号/OTA/ESP32_GENERIC-OTA-20250911-v1.26.1.app-bin"
CHUNK     = 1024


# ===== OTA writer (direct write to inactive OTA partition) =====
BLK = 4096

class OTADirectWriter:
    def __init__(self, total_len=0, sha16=b"\x00"*16):
        self.run  = esp32.Partition(esp32.Partition.RUNNING)
        self.part = self.run.get_next_update()
        if self.part is None:
            raise OSError("No OTA update partition")
        _t, _st, addr, psize, _label, _enc = self.part.info()
        self.addr  = addr
        self.psize = psize
        self.total = total_len or psize
        self.sha16 = sha16 or b"\x00"*16
        self.h     = uh.sha256()
        self.buf   = bytearray(BLK)
        self.boff  = 0
        self.written = 0
        # erase range based on expected total (or full slot if unknown)
        nblk = (self.total + BLK - 1) // BLK
        if hasattr(self.part, "erase_blocks"):
            self.part.erase_blocks(0, nblk)
        else:
            start_sector = addr // BLK
            for i in range(nblk):
                esp.flash_erase(start_sector + i)

    def write(self, data):
        mv = memoryview(data)
        self.h.update(mv)
        off = 0
        while off < len(mv):
            n = min(len(mv) - off, BLK - self.boff)
            self.buf[self.boff:self.boff+n] = mv[off:off+n]
            self.boff += n
            off += n
            if self.boff == BLK:
                bi = self.written // BLK
                self.part.writeblocks(bi, self.buf)
                self.written += BLK
                self.boff = 0

    def finalize(self):
        if self.boff:
            for i in range(self.boff, BLK):
                self.buf[i] = 0xFF
            bi = self.written // BLK
            self.part.writeblocks(bi, self.buf)
            self.written += BLK
            self.boff = 0
        return True

    def switch_and_reboot(self):
        try:
            try:
                esp32.Partition.set_boot(self.part)
            except Exception:
                runp = esp32.Partition(esp32.Partition.RUNNING)
                nxtp = runp.get_next_update()
                if nxtp:
                    esp32.Partition.set_boot(nxtp)
        except Exception:
            pass
        machine.reset()


# ===== HTTP stream =====
def _http_iter(url, sz=CHUNK):
    r = rq.get(url)
    if getattr(r, 'status_code', 200) != 200:
        try:
            r.close()
        except Exception:
            pass
        raise OSError("HTTP {}".format(getattr(r, 'status_code', -1)))
    try:
        while True:
            b = r.raw.read(sz)
            if not b:
                break
            yield b
    finally:
        try:
            r.close()
        except Exception:
            pass


# ===== WiFi =====
def _wifi():
    s = network.WLAN(network.STA_IF)
    if not s.active():
        s.active(True)
    if not s.isconnected():
        s.connect(WIFI_SSID, WIFI_PASS)
        t0 = time.ticks_ms()
        while not s.isconnected() and time.ticks_diff(time.ticks_ms(), t0) < 15000:
            time.sleep_ms(200)
    if not s.isconnected():
        raise OSError("WiFi connect failed")
    return s


# ===== Main =====
def run(fw_url=None):
    print("[OTA] start")
    _wifi()
    url = fw_url or FW_URL
    print("[OTA] GET", url)
    writer = OTADirectWriter(0, b"\x00"*16)
    total = 0
    for part in _http_iter(url, CHUNK):
        writer.write(part)
        total += len(part)
        if (total // CHUNK) % 64 == 0:
            print("[OTA] dl", total)
    ok = writer.finalize()
    print("[OTA] finalize:", ok, "bytes=", total)
    if ok:
        writer.switch_and_reboot()
    return ok

サーバー

コードを用意する必要はありません.HTTPサーバーが動作していて,任意のポートが開いていれば大丈夫です.(今回は例として8000番ポートを使用しました)
今回HTTPサーバーを動作させるに当たり使用したコマンドは以下の通りになります.

python3 -m http.server 8000

実行結果

ESP32

以下の画像はESP32の実行時の出力になります.reset machineの出力を堺に上部がアップデート適用前,下部がアップデート適用後になります.[BOOT]の出力にあるrunning=が現在使用しているパーテーションのラベル名,fw=が現在動作しているファームウェアのバージョンになります.適用前では,ota_0のパーテーションを使用しているのに対し,適用後ではota_1を使用していることが分かります.また,適用前のファームウェアはv1.26.0 on 2025-08-09が動作しているのに対し,適用後ではv1.26.1 on 2025-09-11が動作しており,適用したファームウェアが正常に動作していることが確認できます.
image.png

サーバー

以下の画像はサーバーでコマンドを実行してESP32を動作させた際の出力結果になります.ESP32がHTTPを使用してOTAディレクトリにあるESP32_GENERIC-OTA-20250911-v1.26.1.app-binを取得している事が分かります.
image.png

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?