Skip to content

refactor USB detection and support for ESP32-S31 - #82

Merged
Jason2866 merged 9 commits into
factoryfrom
s31_support
May 20, 2026
Merged

refactor USB detection and support for ESP32-S31#82
Jason2866 merged 9 commits into
factoryfrom
s31_support

Conversation

@Jason2866

@Jason2866 Jason2866 commented May 20, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Full support for ESP32‑S31 devices, including a new mutually‑exclusive CLI option to target them.
    • Automatic chip detection and configuration updated to recognize ESP32‑S31 hardware.
    • Firmware, loader/stub, safeboot and secure-image flashing paths enabled for ESP32‑S31.
  • Refactor

    • Improved USB OTG/JTAG detection and connection/reset behavior for more reliable device handling.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Adds ESP32‑S31 end-to-end: CLI flag and detection routing, write-flash configuration, ROM-class implementation and fast-paths, stub/firmware image wiring, and a stub JSON file.

Changes

ESP32-S31 chip support

Layer / File(s) Summary
CLI flag and chip detection wiring
esp_flasher/__main__.py, esp_flasher/common.py
Adds --esp32s31 CLI flag and updates both detect_chip call sites and detect_chip() signature to accept force_esp32s31, routing to esptool.ESP32S31ROM.
Supported-chips mapping & high-level config
esp_flasher/own_esptool.py, esp_flasher/common.py
Registers esp32s31 in SUPPORTED_CHIPS and _chip_to_rom_loader; adds configure_write_flash_args branch selecting esp32s31 model, tasmota32s31-safeboot.bin, and bootloader offset 0x2000.
ROM-level flash operation fast paths
esp_flasher/own_esptool.py
Expands flash_begin(), flash_encrypt_block(), and flash_defl_begin() conditionals to include ESP32S31ROM for ROM-supported encrypted/compressed flash operations.
ESP32S31ROM chip class implementation
esp_flasher/own_esptool.py
Implements ESP32S31ROM(ESP32C5ROM) with S31-specific ROM constants, memory map, efuse/key-manager encryption checks, USB-RAM sizing, SPI pin validation, and reset/MMU behaviors; removes a previous no-op _post_connect override from ESP32P4RC1ROM.
Stub and firmware image wiring
esp_flasher/own_esptool.py, esp_flasher/stubs/*
Adds ESP32S31StubLoader and ESP32S31FirmwareImage, wires esp32s31 through LoadFirmwareImage() and elf2image(), and assigns ESP32S31ROM.STUB_CODE = load_stub(\"esp32s31\").
Stub JSON definition
esp_flasher/stubs/esp32s31.json
Introduces stub metadata with entry, text_start, data_start, bss_start and encoded text/data payloads.

Sequence Diagram

sequenceDiagram
  participant User
  participant CLI
  participant detect_chip
  participant ESP32S31ROM
  participant LoadFirmwareImage
  User->>CLI: run with --esp32s31
  CLI->>detect_chip: call detect_chip(port, force_esp32s31=True,...)
  detect_chip->>ESP32S31ROM: select ESP32S31ROM as loader
  CLI->>LoadFirmwareImage: request bootloader image for esp32s31
  LoadFirmwareImage->>ESP32S31ROM: construct ESP32S31FirmwareImage / stub wiring
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A tiny hop, a new board to find,
ESP32‑S31 now joins the bind.
From CLI to ROM the routes align,
Stub and bootloader snug in time,
Happy flashes, carrots and chime!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ⚠️ Warning The title mentions 'USB detection' which is not substantiated by the changes; the PR primarily adds ESP32-S31 support. Consider revising the title to focus on the primary change: 'Add ESP32-S31 support' or similar, removing the unrelated 'USB detection' reference.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch s31_support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@esp_flasher/__main__.py`:
- Line 37: The new CLI flag args.esp32s31 is never passed into chip detection;
update the calls to detect_chip(...) that currently only pass args.esp8266 and
args.esp32 to also pass args.esp32s31 so the flag affects detection, e.g.,
change detect_chip(args.esp8266, args.esp32) -> detect_chip(args.esp8266,
args.esp32, args.esp32s31) (and any other detect_chip invocations), and ensure
the detect_chip implementation signature and internal logic accept and use the
esp32s31 parameter (or add handling code) so the flag is wired end-to-end.

In `@esp_flasher/own_esptool.py`:
- Around line 3584-3588: The hard_reset implementation for ESP32S31ROM calls an
undefined self.watchdog_reset(), causing AttributeError; replace that call with
a valid reset path—either implement a watchdog_reset method on ESP32S31ROM (or
its base class) with the correct USB hard-reset sequence, or change the branch
to call the existing ESPLoader.hard_reset(self) (or another already-defined USB
reset helper) when uses_usb() is true and secure_download_mode is False so the
code uses a defined reset method instead of self.watchdog_reset().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 13d563cb-e2a6-4d0c-833e-4782241b08ee

📥 Commits

Reviewing files that changed from the base of the PR and between b3e7c23 and fbbcbef.

📒 Files selected for processing (4)
  • esp_flasher/__main__.py
  • esp_flasher/common.py
  • esp_flasher/own_esptool.py
  • esp_flasher/stubs/esp32s31.json

Comment thread esp_flasher/__main__.py
Comment thread esp_flasher/own_esptool.py Outdated
Repository owner deleted a comment from coderabbitai Bot May 20, 2026
@Jason2866

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@esp_flasher/own_esptool.py`:
- Around line 3362-3446: ESP32S31ROM is missing S31-specific USB/JTAG register
constants and it overrides _post_connect() without calling the parent, causing
USB detection and watchdog disabling to use C5 values and be skipped; add
S31-specific constants (e.g. UARTDEV_BUF_NO, USB selector values used by
uses_usb()/uses_usb_jtag_serial(), and the S31 watchdog registers/keys that the
ESP32C5ROM helpers expect such as
DR_REG_LP_WDT_BASE/RTC_CNTL_WDTCONFIG0_REG/RTC_CNTL_WDTWPROTECT_REG/RTC_CNTL_WDT_WKEY
values), update hard_reset()/uses_usb_jtag_serial() usage to reference these new
S31 constants instead of C5 addresses, and modify _post_connect() in ESP32S31ROM
to call super()._post_connect() (then perform any S31-specific steps) so the
parent watchdog-disabling logic runs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b049f60e-83f1-4db9-a535-bf46362ae571

📥 Commits

Reviewing files that changed from the base of the PR and between b3e7c23 and 5b28406.

📒 Files selected for processing (4)
  • esp_flasher/__main__.py
  • esp_flasher/common.py
  • esp_flasher/own_esptool.py
  • esp_flasher/stubs/esp32s31.json

Comment thread esp_flasher/own_esptool.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@esp_flasher/own_esptool.py`:
- Around line 3432-3434: The code added UARTDEV_BUF_NO_USB_JTAG_SERIAL but still
sends all non-OTG resets through ESPLoader.hard_reset(), which bypasses the RTS
settling in ESP32C5ROM.hard_reset(); modify the reset dispatch so that when the
selected USB buffer equals UARTDEV_BUF_NO_USB_JTAG_SERIAL you call
ESP32C5ROM.hard_reset() (or the equivalent S31-specific hard_reset flow) instead
of ESPLoader.hard_reset(), keeping the existing branch for UARTDEV_BUF_NO_USB
(OTG) and the default non-USB path unchanged; reference
UARTDEV_BUF_NO_USB_JTAG_SERIAL, UARTDEV_BUF_NO_USB, ESPLoader.hard_reset, and
ESP32C5ROM.hard_reset to locate and update the conditional.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e8e562d9-eb47-4df9-9425-0672acc4f805

📥 Commits

Reviewing files that changed from the base of the PR and between 5b28406 and 4f80634.

📒 Files selected for processing (1)
  • esp_flasher/own_esptool.py

Comment thread esp_flasher/own_esptool.py Outdated
@Jason2866

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@Jason2866

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
esp_flasher/own_esptool.py (1)

3579-3582: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Call the parent _post_connect() hook.

This override skips ESP32C5ROM._post_connect(), so the inherited watchdog-disabling flow never runs on S31. On USB-JTAG/Serial connections that can let the RTC/SWD watchdog reset the board mid-flash.

🛠️ Suggested fix
     def _post_connect(self):
+        super()._post_connect()
         if self.uses_usb():
             self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@esp_flasher/own_esptool.py` around lines 3579 - 3582, The override of
_post_connect currently skips the parent watchdog-disabling flow; update the
method to call the parent hook (e.g. super()._post_connect() or
ESP32C5ROM._post_connect(self)) and then preserve the USB-specific behavior by
still setting self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK when self.uses_usb() is
true so the inherited watchdog disable runs for S31 and USB-JTAG/Serial
connections before adjusting the RAM block.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@esp_flasher/own_esptool.py`:
- Around line 3579-3582: The override of _post_connect currently skips the
parent watchdog-disabling flow; update the method to call the parent hook (e.g.
super()._post_connect() or ESP32C5ROM._post_connect(self)) and then preserve the
USB-specific behavior by still setting self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK
when self.uses_usb() is true so the inherited watchdog disable runs for S31 and
USB-JTAG/Serial connections before adjusting the RAM block.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 40e2896e-b9f0-4d74-8e62-0737c0dc4a43

📥 Commits

Reviewing files that changed from the base of the PR and between b3e7c23 and 3a90327.

📒 Files selected for processing (4)
  • esp_flasher/__main__.py
  • esp_flasher/common.py
  • esp_flasher/own_esptool.py
  • esp_flasher/stubs/esp32s31.json

@Jason2866

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
esp_flasher/own_esptool.py (1)

3432-3434: ⚠️ Potential issue | 🔴 Critical

Define the S31 UARTDEV_BUF_NO register before reusing the inherited USB helpers.

Line 3595, Line 3608, and Line 4267 still call uses_usb(), but ESP32S31ROM never overrides UARTDEV_BUF_NO. The note on Lines 3580-3582 already says the inherited C5 address bus-faults on S31, so connect/reset/stub-init can still fail immediately on the first USB check. Add the verified S31 UARTDEV_BUF_NO address, or override uses_usb() / uses_usb_jtag_serial() to avoid dereferencing the inherited register until that address is known.

Also applies to: 3580-3597, 3607-3611, 4267-4269

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@esp_flasher/own_esptool.py` around lines 3432 - 3434, The ESP32S31ROM
subclass is calling inherited USB helpers that reference UARTDEV_BUF_NO_USB /
UARTDEV_BUF_NO_USB_JTAG_SERIAL before the S31-specific register is defined;
either add the verified S31 addresses for UARTDEV_BUF_NO_USB and
UARTDEV_BUF_NO_USB_JTAG_SERIAL in the S31 definitions (so inherited
uses_usb()/uses_usb_jtag_serial() can safely read them) or explicitly override
uses_usb() and uses_usb_jtag_serial() in ESP32S31ROM to return False (or
otherwise avoid dereferencing the inherited register) until the correct S31
UARTDEV_BUF_NO_* addresses are known; update the definitions or overrides
referenced by the symbols ESP32S31ROM, UARTDEV_BUF_NO_USB,
UARTDEV_BUF_NO_USB_JTAG_SERIAL, uses_usb(), and uses_usb_jtag_serial().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@esp_flasher/own_esptool.py`:
- Around line 3432-3434: The ESP32S31ROM subclass is calling inherited USB
helpers that reference UARTDEV_BUF_NO_USB / UARTDEV_BUF_NO_USB_JTAG_SERIAL
before the S31-specific register is defined; either add the verified S31
addresses for UARTDEV_BUF_NO_USB and UARTDEV_BUF_NO_USB_JTAG_SERIAL in the S31
definitions (so inherited uses_usb()/uses_usb_jtag_serial() can safely read
them) or explicitly override uses_usb() and uses_usb_jtag_serial() in
ESP32S31ROM to return False (or otherwise avoid dereferencing the inherited
register) until the correct S31 UARTDEV_BUF_NO_* addresses are known; update the
definitions or overrides referenced by the symbols ESP32S31ROM,
UARTDEV_BUF_NO_USB, UARTDEV_BUF_NO_USB_JTAG_SERIAL, uses_usb(), and
uses_usb_jtag_serial().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3a079ca6-032c-492a-b8d5-c3d8efaf9459

📥 Commits

Reviewing files that changed from the base of the PR and between b3e7c23 and 61773bd.

📒 Files selected for processing (4)
  • esp_flasher/__main__.py
  • esp_flasher/common.py
  • esp_flasher/own_esptool.py
  • esp_flasher/stubs/esp32s31.json

@Jason2866 Jason2866 changed the title support for ESP32-S31 refactor USB detection and support for ESP32-S31 May 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@esp_flasher/own_esptool.py`:
- Around line 4258-4260: When rom_loader.uses_usb_otg() is true the code
currently sets self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK but leaves
FLASH_WRITE_SIZE at its default (0x4000), causing write_flash() to chunk stub
uploads at FLASH_WRITE_SIZE while the transport can only handle USB_RAM_BLOCK;
clamp both values on the OTG path by setting self.ESP_RAM_BLOCK =
self.USB_RAM_BLOCK and also reduce FLASH_WRITE_SIZE to at most
self.USB_RAM_BLOCK (e.g. FLASH_WRITE_SIZE = min(FLASH_WRITE_SIZE,
self.USB_RAM_BLOCK)) so write_flash() and stub transfers use the same USB block
size; apply the same change at the other occurrence mentioned (the second
rom_loader.uses_usb_otg() block).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e404fd6f-b856-43f3-a173-129742ebb0d4

📥 Commits

Reviewing files that changed from the base of the PR and between 3a90327 and 67e378b.

📒 Files selected for processing (1)
  • esp_flasher/own_esptool.py

Comment thread esp_flasher/own_esptool.py
@Jason2866

Copy link
Copy Markdown
Owner Author

@coderabbitai is the refactoring to use VID/PID to detect USB mode solid? Is the ESP32-S31 support correct implemented?

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the detect_chip function and VID/PID related code in common.py
echo "=== common.py detect_chip ==="
rg -n "detect_chip|VID|PID|USB|vid|pid|usb" esp_flasher/common.py -A 3 -B 1

echo ""
echo "=== ESP32S31ROM class in own_esptool.py ==="
grep -n "ESP32S31\|esp32s31" esp_flasher/own_esptool.py | head -80

echo ""
echo "=== ESP32S31ROM class body ==="
# Find the line where ESP32S31ROM is defined and show 150 lines
start_line=$(grep -n "class ESP32S31ROM" esp_flasher/own_esptool.py | head -1 | cut -d: -f1)
if [ -n "$start_line" ]; then
  sed -n "${start_line},$((start_line+150))p" esp_flasher/own_esptool.py
fi

echo ""
echo "=== VID/PID in own_esptool.py ==="
rg -n "VID|PID|USB_JTAG|usb_jtag|vid|pid" esp_flasher/own_esptool.py | head -60

echo ""
echo "=== stub json ==="
cat esp_flasher/stubs/esp32s31.json | python3 -c "import json,sys; d=json.load(sys.stdin); print({k: v if k not in ('text','data') else f'<{len(v)} chars>' for k,v in d.items()})"

echo ""
echo "=== __main__.py changes ==="
grep -n "esp32s31\|s31" esp_flasher/__main__.py

echo ""
echo "=== flash_begin and flash_defl_begin S31 entries ==="
rg -n "ESP32S31\|flash_begin\|flash_defl_begin\|flash_encrypt" esp_flasher/own_esptool.py | grep -i "s31\|begin\|encrypt" | head -40

echo ""
echo "=== configure_write_flash_args for S31 ==="
grep -n -A5 -B5 "S31\|s31" esp_flasher/common.py

Length of output: 13224


🏁 Script executed:

#!/bin/bash
echo "=== Full detect_chip function ==="
start=$(grep -n "^def detect_chip" esp_flasher/common.py | head -1 | cut -d: -f1)
sed -n "${start},$((start+80))p" esp_flasher/common.py

echo ""
echo "=== configure_write_flash_args full function ==="
start2=$(grep -n "^def configure_write_flash_args" esp_flasher/common.py | head -1 | cut -d: -f1)
sed -n "${start2},$((start2+120))p" esp_flasher/common.py

echo ""
echo "=== ESP32S31ROM USB/VID/PID methods ==="
# Look at _post_connect and check_spi_connection and hard_reset for S31
start3=$(grep -n "class ESP32S31ROM" esp_flasher/own_esptool.py | head -1 | cut -d: -f1)
end3=$(grep -n "^class ESP32S31StubLoader\|^class ESP32S31Firmware" esp_flasher/own_esptool.py | head -1 | cut -d: -f1)
if [ -n "$start3" ] && [ -n "$end3" ]; then
  sed -n "${start3},$((end3-1))p" esp_flasher/own_esptool.py
fi

echo ""
echo "=== How other chips handle VID/PID (e.g. ESP32C3, ESP32S3) ==="
rg -n "USB_JTAG_SERIAL_PID\|USB_RAM_BLOCK\|_post_connect" esp_flasher/own_esptool.py | head -40

Length of output: 37815


🏁 Script executed:

#!/bin/bash
echo "=== ESP32S31StubLoader body ==="
start=$(grep -n "^class ESP32S31StubLoader" esp_flasher/own_esptool.py | head -1 | cut -d: -f1)
end=$(grep -n "^ESP32S31ROM.STUB_CLASS\|^class " esp_flasher/own_esptool.py | awk -F: -v s="$start" '$1 > s {print $1; exit}')
sed -n "${start},$((end-1))p" esp_flasher/own_esptool.py

echo ""
echo "=== ESP32C5ROM IMAGE_CHIP_ID and USB-related fields ==="
grep -n "IMAGE_CHIP_ID\|USB_JTAG_SERIAL_PID\|USB_RAM_BLOCK\|CHIP_NAME" esp_flasher/own_esptool.py | grep -E "C5|S31|S3[^1]|P4|C6"

echo ""
echo "=== ESP32S31FirmwareImage body ==="
start2=$(grep -n "^class ESP32S31FirmwareImage" esp_flasher/own_esptool.py | head -1 | cut -d: -f1)
end2=$(grep -n "^ESP32S31ROM.BOOTLOADER_IMAGE\|^class " esp_flasher/own_esptool.py | awk -F: -v s="$start2" '$1 > s {print $1; exit}')
sed -n "${start2},$((end2-1))p" esp_flasher/own_esptool.py

echo ""
echo "=== All IMAGE_CHIP_IDs defined ==="
grep -n "IMAGE_CHIP_ID" esp_flasher/own_esptool.py

echo ""
echo "=== Check uses_usb_otg definition ==="
grep -n -A5 "def uses_usb_otg" esp_flasher/own_esptool.py

echo ""
echo "=== Any USB_JTAG_SERIAL_PID overrides per chip ==="
grep -n "USB_JTAG_SERIAL_PID" esp_flasher/own_esptool.py

Length of output: 3007


@Jason2866: Good questions — here's a detailed analysis of both.


1. VID/PID USB Mode Detection — Is it solid?

Yes, the refactoring is solid. The VID/PID logic lives in the shared ESPLoader base class and is inherited unchanged by ESP32S31ROM:

  • get_usb_vid_pid() reads the port's VID/PID via pyserial's list_ports with result caching and clean (None, None) fallback on failure.
  • uses_usb_jtag_serial() checks (0x303A, 0x1001) — a single global PID for USB-JTAG/Serial, shared by all chips.
  • uses_usb_otg() checks (0x303A, IMAGE_CHIP_ID) — chip-specific, so S31 uses PID 0x0020.

All three downstream consumers in ESP32S31ROM are correctly wired:

Location Behaviour Status
_post_connect() Calls super() (C5ROM watchdog disable), then conditionally sets ESP_RAM_BLOCK
hard_reset() Uses rtc_wdt_reset() for USB-OTG, falls back to ESP32C5ROM.hard_reset() otherwise
ESP32S31StubLoader.__init__() Adjusts both ESP_RAM_BLOCK and FLASH_WRITE_SIZE for USB-OTG (matching the S2/S3 stub pattern)

One item to verify: IMAGE_CHIP_ID = 32 doubles as the expected USB-OTG PID (0x0020) via uses_usb_otg(). This value should be confirmed against Espressif's official USB PID assignment for the ESP32-S31. If it's wrong, USB-OTG mode will silently fall back to UART behaviour (no crash), but hard_reset() and FLASH_WRITE_SIZE adjustment would be skipped for USB-OTG connections.


2. ESP32-S31 Implementation — Correct?

Overall yes, the implementation follows the established patterns correctly. A few specific checks:

✅ Correct:

  • configure_write_flash_args checks "ESP32-S31" before "ESP32-S3" — critical, since "ESP32-S3" is a substring of "ESP32-S31". The ordering is right.
  • EFUSE_MAX_KEY = 4 with is_flash_encryption_key_valid() iterating range(5) (0–4), exactly matching the 5 EFUSE_PURPOSE_KEY* register pairs defined. ✅
  • BOOTLOADER_FLASH_OFFSET = 0x2000 matches ofs_bootloader = 0x2000 in configure_write_flash_args. ✅
  • ESP32S31FirmwareImage.set_mmu_page_size() validates {8192, 16384, 32768, 65536} (8KB–64KB), consistent with ESP32-C5. ✅
  • S31 is added to all three ROM fast-path conditional lists (flash_begin, flash_encrypt_block, flash_defl_begin) at lines 949, 980, 1119. ✅
  • Stub JSON parses cleanly with valid entry, text_start, data_start, bss_start fields. ✅

⚠️ Minor inconsistency:

# ESP32S31ROM
def get_flash_voltage(self):
    pass  # implicitly returns None

# ESP32P4ROM (more explicit)
def get_flash_voltage(self):
    raise NotSupportedError(self, "Reading flash voltage")

pass silently returns None rather than raising NotSupportedError. Not a bug, but could produce a confusing silent no-op if the caller checks the return value. Consider aligning with the P4 pattern:

def get_flash_voltage(self):
    raise NotSupportedError(self, "Reading flash voltage")

Summary: The implementation is functionally complete and consistent with how other newer ESP32 chips (C5, P4) are handled. The main thing to double-check is whether IMAGE_CHIP_ID = 32 (USB PID 0x0020) matches what Espressif actually assigns to the ESP32-S31 in USB-OTG mode.

@Jason2866
Jason2866 merged commit 669d656 into factory May 20, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant