Skip to content

Watch a board's lamps, so a declared LED is a light that flashes #600

Description

@A13xB0

Where it stands

A board profile declares its lamps and their pins. Nothing watches them, so the interface draws a lamp as an outline rather than as an unlit lamp — deliberately, because "off" and "not modelled" are different facts and the second is about us. The board view says the same in words: no watcher on this pin · not modelled.

mb_input.c in the QEMU fork opens its file with the exact sentence this issue is about:

A board's lamps are outputs somebody watches; its buttons are inputs somebody drives, and until now nothing could drive them.

The inputs half was built. The outputs half was not.

Why it is worth having

A lamp is the cheapest signal a board gives. Firmware flashes one on boot, on transmit, on a fault — and on a board with no display it is the only thing a person can see. Today a run of a Heltec V3 tells you nothing about whether its TX lamp ever lit, which is a question with an answer we throw away.

The shape, which is already established

The mechanism exists and is used for the front-end module, whose enable line is an ordinary GPIO the firmware drives:

qdev_connect_gpio_out_named(gpio, ESP32_GPIO_OUT, fem_pin,
                            qdev_get_gpio_in_named(radio, "sx1262-fem", 0));

A lamp is the same thing pointed at a different consumer:

  1. hw/misc/mb_lamp.c — a device with one named GPIO input per declared lamp pin. On a level change it writes an eight-byte message to the socket the buttons already use, tagged 'L' with the pin and the level. Same fixed width, so every other reader skips it.
  2. hw/xtensa/esp32s3.c — a machine property for the lamp pins, wiring ESP32_GPIO_OUT for each into the device. About twenty lines beside the block that wires mb-input.
  3. peripheral/buttons.go — the sender grows a read loop and keeps the levels. The socket is already bidirectional; nothing reads it back today.
  4. engine, state, the board view — the levels reach NodeStat, and the lamp is drawn lit, with the flash visible because the level is a level and not an event.

The nRF52 boards need the Renode equivalent, which is the same idea against a different platform file.

What makes it a separate change

The fork is its own repository and ships as a pinned binary (QEMU_RELEASE in packaging/emulator-pins.env). This needs a change there, a tagged release building for three platforms, and a pin bump here — so it cannot ride along with a UI pull request, and the Go half is not worth writing before the data exists.

Also worth doing while there

The battery meter is modelled — ADC1 channel from the pin, the raw encoded through the inverse of ESP-IDF's calibration curve — but it is set once at boot from Battery.VoltageAt(1) and held there. A cell that never drains is fine for a boot and wrong for a long run, and the board view now says so on that row: "Set at boot and held there: nothing drains it as the run goes on." Driving it from the energy model as the run proceeds is the natural companion to this.

Where it was found

Building the board view (#598), which draws every declared part and has to say honestly which of them anything observes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Q9HbD44EKWWTRYgxbFGxf6

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions