Conversation
Upstream renamed both the GitHub repo (rightup/pyMC_core -> openhop-dev/openhop_core) and the PyPI distribution (pymc-core -> openhop-core). pymc-core is frozen at 1.0.12; all further releases land under the new name, starting at 1.1.1. The rename is near-mechanical: comparing the 1.0.12 and 1.1.1 wheels, the module tree is identical apart from three new files, SX1262Radio has an unchanged __init__ signature, and protocol/identity.py is byte-identical. The [hardware] extra survives the rename. Renamed alongside the imports: - PyMCCoreSession -> OpenHopCoreSession - MockPyMCCoreSession -> MockOpenHopCoreSession - import_pymc_core -> import_openhop_core - require_pymc -> require_openhop Also fixes a dead entry in _RADIO_LOGGER_SUBSTRINGS: the "pyMC" substring never matched anything, since the library's module loggers were named "pymc_core.*" (lowercase) and no logger was literally named "pyMC". It is now "openhop_core", which does match. CHANGELOG entries are left referring to pymc-core, as they were accurate when written. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The 40-pin header sits on /dev/gpiochip0 for CM4, but on /dev/gpiochip15
for CM5 and Pi 5 kernels, where it hangs off the RP1. We hardcoded chip
0 in three places and never passed openhop_core's gpio_chip kwarg, so
affected users had no supported way to point at the right chip — the
only workaround was a udev symlink.
Adds three hardware settings, each with an env override, a Settings >
Hardware control, and a passthrough in create_radio():
gpio_chip MESHCORE_GPIO_CHIP /dev/gpiochipN
use_gpiod_backend MESHCORE_USE_GPIOD_BACKEND polled IRQ edges
en_pins MESHCORE_EN_PINS radio power-enable pins
These are deliberately *not* part of HARDWARE_PRESETS: the chip number
and IRQ backend are properties of the SoC and kernel, not of the radio
board, so picking a board preset must not reset them.
Note use_gpiod_backend does not actually select libgpiod while
python-periphery is installed — openhop_core only swaps in its libgpiod
wrapper when periphery is absent, and its [hardware] extra always
installs periphery. What the flag really changes is edge detection,
from kernel edge interrupts to a polling thread, which is a workaround
for kernels that reject the edge request and leave the radio connected
but deaf. It is documented and labelled ("Poll IRQ") accordingly, and
we do not depend on the gpiod extra, which would never be imported and
needs libgpiod headers to build on the Pi.
Diagnostics, so the next reporter does not have to guess:
- doctor reports the *configured* chip and lists the chips the host
actually has, instead of asserting /dev/gpiochip0.
- pre-flight adds a GPIO_CHIP conflict naming the available chips.
A missing chip short-circuits the pin probes, which would otherwise
emit one redundant conflict per pin for the same root cause.
- conflicts probe pins against the configured chip, and now include
the configured enable pins.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2bd420e0b0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| hardware = runtime_config_from_settings(settings).hardware | ||
| if hardware is not None: | ||
| return hardware |
There was a problem hiding this comment.
Honor the GPIO environment override in doctor
When MESHCORE_GPIO_CHIP=15 meshcore-console doctor is run on a host with an accessible settings database, SettingsStore.load() normally succeeds and this path returns the persisted/default chip (usually 0), so the environment fallback is never reached. Doctor therefore checks /dev/gpiochip0 and may report a failure even though the radio CLI uses load_runtime_config() and will use chip 15; apply the environment override before returning the persisted configuration.
AGENTS.md reference: AGENTS.md:L160-L162
Useful? React with 👍 / 👎.
The GTK app built its radio configuration from the settings database only, so MESHCORE_GPIO_CHIP did nothing there. `doctor` had the same gap: it read the persisted chip and ignored the variable. A tester on CM5 hardware could therefore not point the app at /dev/gpiochip15 without the udev symlink. - Add one `_HARDWARE_ENV_OVERRIDES` table in `meshcore/config.py`. The CLI, the GTK app and `doctor` now apply it through `runtime_config_from_settings()`, so all three agree on the values that reach the radio. The environment wins, which keeps a broken saved configuration recoverable from the command line. - Say in Settings > Hardware which variables are set, because an edit there cannot beat them. - Report the source of the chip number in `doctor`. The "Settings" button on the conflict screen did nothing. `navigate_to` switches the inner page stack, but the conflict screen sits in the outer content stack, so the page changed behind a screen the user never left. The button now leaves that stack first. Add the "uConsole HG AIOv2" board preset, which is the uConsole pinout plus LoRa enable pin 27. Every preset now states `en_pins`, so a switch between boards clears a stale pin. `gpio_chip` stays out of the presets: it follows the kernel, not the board. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#85) Driving the GTK app under Xvfb found two faults that code inspection missed. `navigate_to("settings")` switched the page and then deactivated the nav buttons. Deactivating the last active button makes `_on_nav_button_toggled` switch it back on, which switches the page back with it, so the settings page never appeared. Update the buttons first, as `_on_settings_clicked` already does. This is what made the conflict screen's "Settings" button look dead: the outer stack fix alone landed the user back on the previous page. `_on_hw_preset_changed` wrote every preset field back to its entry except `en_pins`. `_collect_settings` reads the entries, so the AIOv2 preset looked right but saved an empty enable pin. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixes #85.
The bug
The 40-pin header sits on
/dev/gpiochip0for CM4, but on/dev/gpiochip15for CM5 and Pi 5 kernels, where it hangs off the RP1. We hardcoded chip 0 in three places and never passed openhop_core'sgpio_chipkwarg, so affected users had no supported way to point at the right chip — the only workaround was a udev symlink.Changes
Three hardware settings, each with an env override, a Settings > Hardware control, and a passthrough in
create_radio():gpio_chipMESHCORE_GPIO_CHIP/dev/gpiochipNto openuse_gpiod_backendMESHCORE_USE_GPIOD_BACKENDen_pinsMESHCORE_EN_PINSThese are deliberately not in
HARDWARE_PRESETS: the chip number and IRQ backend are properties of the SoC and kernel, not of the radio board, so picking a board preset must not reset them. There's a test pinning that.Diagnostics
So the next reporter doesn't have to guess:
doctorreports the configured chip and lists what the host actually has, instead of asserting/dev/gpiochip0:GPIO_CHIPconflict naming the available chips. A missing chip short-circuits the pin probes, which would otherwise emit one redundant conflict per pin for the same root cause.Note on
use_gpiod_backendIt does not actually select libgpiod while python-periphery is installed. In
GPIOPinManager.__init__theGpiodGPIOclass and theglobals()["GPIO"] = GpiodGPIOswap are nested inside theif backend == "auto"branch, so passingbackend="gpiod"explicitly never installs the wrapper — and the[hardware]extra always pulls in periphery.What the flag really changes is edge detection, from kernel edge interrupts to a polling thread. That's a workaround for kernels that reject the edge request and leave the radio connected but deaf, which is plausibly the more useful half here. It's labelled "Poll IRQ" in the UI and documented accordingly. We do not depend on openhop_core's
gpiodextra: it would never be imported, and it needs libgpiod headers to build on the Pi.Dependency migration (first commit)
Upstream renamed both the repo (
rightup/pyMC_core→openhop-dev/openhop_core) and the PyPI distribution (pymc-core→openhop-core).pymc-coreis frozen at 1.0.12; further releases land under the new name, starting at 1.1.1.Comparing the wheels, the rename is near-mechanical: the module tree is identical apart from three new files,
SX1262Radio.__init__is unchanged, andprotocol/identity.pyis byte-identical. Internal names renamed alongside the imports (PyMCCoreSession→OpenHopCoreSession, etc.). CHANGELOG entries are left referring topymc-core, as they were accurate when written.Also fixes a dead entry in
_RADIO_LOGGER_SUBSTRINGS: the"pyMC"substring never matched anything, since the library's module loggers were namedpymc_core.*(lowercase) and no logger was literally namedpyMC.Testing
gpio_chip=15,en_pins=16,17,use_gpiod_backend=True; natural width stays 58px, so no runaway-resize regression.Follow-ups (not in scope here)
doctor's spidev check is still hardcoded to/dev/spidev1.0despite now havingbus_id/cs_idin hand — wrong for the waveshare preset, but doesn't affect the Change gpiochip0 to gpiochip15 #85 reporters.GPIOPinManagerwarnings (including "Could not setup EN pin") still don't reach the radio-error path. Adding it to_RADIO_LOGGER_SUBSTRINGSwould surface exactly the failures reporters hit blind, but broadens what becomes a toast.🤖 Generated with Claude Code