Skip to content

Add bionebicine_pds_2026 board - #11648

Closed
mmabey wants to merge 1 commit into
adafruit:mainfrom
mmabey:mabey/add-bionebicine-pds-2026-board
Closed

mmabey wants to merge 1 commit into
adafruit:mainfrom
mmabey:mabey/add-bionebicine-pds-2026-board

Conversation

@mmabey

@mmabey mmabey commented Oct 21, 2026

Copy link
Copy Markdown

Summary

Adds a new board, bionebicine_pds_2026, forked from espressif_esp32s3_devkitc_1_n8r8 (ESP32-S3-WROOM-1, N8R8 flash/PSRAM).

Differences from the fork parent:

  • sdkconfig: CONFIG_RTC_CLK_SRC_EXT_CRYS=y. This board has an external 32.768kHz crystal on IO15/IO16, so RTC_SLOW_CLK uses it instead of the internal RC oscillator.
  • pins.c/mpconfigboard.h: IO15/IO16 removed from board.* entirely, since they're claimed by the crystal. MICROPY_HW_NEOPIXEL and the NEOPIXEL alias removed (no onboard addressable LED).
  • mpconfigboard.mk: USB_PRODUCT/USB_MANUFACTURER set to this board's own identity. USB_VID/USB_PID intentionally left as the fork parent's values, because this board's USB is never exposed externally once shipped, so these are only ever seen by internal tooling.

Testing

Built and flashed to real hardware. Confirmed correct boot_out.txt board identity, confirmed over REPL that board.* correctly excludes IO15/IO16/NEOPIXEL while microcontroller.pin still shows the real chip pins (as expected, since those pads physically exist), and confirmed the external crystal starts successfully via a one-off diagnostic-logging build (no fallback-to-internal-RC warning).

Forked from espressif_esp32s3_devkitc_1_n8r8 (same ESP32-S3-WROOM-1,
N8R8 flash/PSRAM config). Differences from the devkit:

- sdkconfig: RTC_CLK_SRC_EXT_CRYS instead of the internal RC oscillator.
  This board wires an external 32.768kHz crystal to IO15/IO16, which the
  devkit doesn't have.
- pins.c/mpconfigboard.h: IO15/IO16 removed from board.* entirely once
  claimed by the crystal; MICROPY_HW_NEOPIXEL and the NEOPIXEL alias
  removed (no onboard addressable LED).
- mpconfigboard.mk: USB_PRODUCT/USB_MANUFACTURER set to this board's own
  identity. USB_VID/USB_PID deliberately left as the fork parent's
  borrowed values because this board's USB is never exposed externally
  once shipped, so they're only ever seen by internal tooling.

@dhalbert dhalbert left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

To be clear, this is a board inside a particular nebulizer product, is that right? Are multiple people going to be using this definition? I am not sure we want to create a board definition that's built every time for such a specialized case.

@mmabey

mmabey commented Oct 21, 2026

Copy link
Copy Markdown
Author

@dhalbert that's a fair pushback. I took this route because of the device's need to use an external crystal for the RTC, and my understanding is that this has to be compiled into the image starting from the board's configuration.

To answer directly, yes, this is for a single product that won't be shared. Given that, I agree I shouldn't have opened this PR. I do wish there was a generic way to turn on support for an external crystal that also allows for an easy way to keep up with the latest CP releases, but I understand that's not realistic.

Sorry for the noise.

@mmabey mmabey closed this Oct 21, 2026
@dhalbert

Copy link
Copy Markdown
Collaborator

Sorry for the noise.

No problem. I agree it would be nice to have some mechanism to maintain a fork that is kept up to date and rebuilt when upstream is updated.

Claude had some ideas:

GitHub has nothing built in that does this. A fork never syncs itself; the "Sync fork" button is manual. Nothing on adafruit's side can notify a fork when a PR merges, so the fork has to poll upstream with a scheduled Action. There are two workable setups.

  1. A board-only repo that builds against upstream (recommended)

Keep only boards/<board_id>/ and one workflow in a small repo; it doesn't need to be a fork. The workflow runs on schedule: (say daily) plus workflow_dispatch:, and:

  1. Runs git ls-remote https://github.com/adafruit/circuitpython refs/heads/main and stops if that SHA was already built. The last SHA can be stored as a release tag name or in the Actions cache.
  2. Clones adafruit, copies the board directory into ports//boards/, and builds. The steps can be copied from .github/workflows/build-board-custom.yml: port setup, submodules, external deps, mpy-cross, then make. tools/board_to_port.py finds the port from the board directory, so the copied board is picked up.
  3. Uploads the firmware as an artifact, or attaches it to a GitHub release, which is easier for the user to grab.

Because nothing gets merged or pushed, there are no conflicts and no token permission problems. A tag-only variant (build when a new 9.x.y tag appears) might suit them better than every merge to main.

  1. A real fork that rebases itself

The fork keeps the board commit on top of upstream main. A scheduled job rebases it, force-pushes, then starts the build. There's a shortcut here: upstream's build-board-custom.yml already supports forks. With branch= it checks out fork/ and builds a single board, so the sync job only has to call gh workflow run build-board-custom.yml -f board=... -f branch=.... That works with the job's built-in GITHUB_TOKEN, even though pushes made with that token don't trigger other workflows. The drawbacks:

  • Upstream commits regularly touch .github/workflows/, and GitHub refuses to let the built-in token push commits that change workflow files. The user would need a personal access token with the workflows scope stored as a secret. This is the most common way fork-sync setups break.
  • A rebase conflict fails the job. That's rare when the board commit only adds a board directory.

Gotchas for either setup

  • Scheduled workflows only run from the repo's default branch. Actions are off by default in forks and have to be enabled in the Actions tab.
  • In a public repo, GitHub disables scheduled workflows after 60 days with no repository activity. Option 2's regular pushes probably count as activity; option 1 pushes nothing, so it would go quiet unless it's a private repo or something else commits now and then. I haven't confirmed whether force-pushes from a bot reset that timer.
  • Private repos use Actions minutes. A single-board build is roughly 10–20 minutes, so daily builds fit within the free 2,000 minutes a month.
  • The common long-term failure is a build failure, not a merge conflict: upstream renames a CIRCUITPY_* flag or changes a board-config convention, and the out-of-tree board stops compiling. GitHub emails the repo owner when a scheduled run fails, which works as an early warning.

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.

2 participants