diff --git a/.github/workflows/hil-circuitpython.yml b/.github/workflows/hil-circuitpython.yml index 3de7a97..0e538da 100644 --- a/.github/workflows/hil-circuitpython.yml +++ b/.github/workflows/hil-circuitpython.yml @@ -42,7 +42,7 @@ jobs: CIRCUITPYTHON_VERSION: ${{ matrix.CIRCUITPYTHON_VERSION}} steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v7 - name: Set Env Vars run: | diff --git a/.github/workflows/hil-micropython.yml b/.github/workflows/hil-micropython.yml index 1af3ab5..504cf8a 100644 --- a/.github/workflows/hil-micropython.yml +++ b/.github/workflows/hil-micropython.yml @@ -48,7 +48,7 @@ jobs: MPY_BOARD: ${{matrix.MPY_BOARD}} steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v7 - name: Set Environment Variables run: | diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index c27ceb0..9a61f0c 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -41,9 +41,9 @@ jobs: --header 'Content-Type: application/json' \ --header 'X-Session-Token: ${{ secrets.NOTEHUB_SESSION_TOKEN }}' \ --data '{"req":"note.add","file":"build_results.qi","body":{"result":"building"}}' - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install pipenv diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index a5e9620..6535946 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -12,11 +12,11 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 # setuptools-scm needs full git history - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.x' - name: Install dependencies diff --git a/.github/workflows/update-notecard-schema.yml b/.github/workflows/update-notecard-schema.yml index 461ec67..c6e3c7c 100644 --- a/.github/workflows/update-notecard-schema.yml +++ b/.github/workflows/update-notecard-schema.yml @@ -11,10 +11,10 @@ jobs: update-notecard-api: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 - name: Set up Python 3.12 - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.12 diff --git a/README.md b/README.md index 85de162..8bb28f6 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ library with: - [RaspberryPi](examples/notecard-basics/rpi_example.py) - [CircuitPython](examples/notecard-basics/cpy_example.py) - [MicroPython](examples/notecard-basics/mpy_example.py) +- [Notecard Outboard Firmware Update](examples/outboard-dfu/circuit-python/code.py) ## Contributing diff --git a/examples/outboard-dfu/README.md b/examples/outboard-dfu/README.md new file mode 100644 index 0000000..ad03e65 --- /dev/null +++ b/examples/outboard-dfu/README.md @@ -0,0 +1,51 @@ +# Notecard Outboard Firmware Update Example + +This example demonstrates [Notecard Outboard Firmware Update][odfu], which lets +you update a host MCU's firmware over-the-air from Notehub with no host code +required to perform the download. The Notecard receives the new firmware image +and reprograms the host over the DFU signals on a Notecarrier F. + +The [`circuit-python/code.py`](circuit-python/code.py) sample: + +1. Configures the Notecard to enable Outboard Firmware Update for the host + (`card.dfu`, `card.aux`, and `dfu.status`). +2. Blinks the built-in LED with a recognizable pattern. This is the "payload" + you update over-the-air. + +This example targets the [Blues Swan][swan] on a +[Notecarrier F][notecarrier-f]. Cygnet does not support CircuitPython. + +## Prerequisites + +- A [Blues Swan][swan] running CircuitPython +- A [Notecarrier F][notecarrier-f] with a [Notecard][notecard] +- A [Notehub](https://notehub.io) account and project +- The `notecard` package copied into the `lib/notecard` directory of your + device (copy the contents of this repo's `notecard/` directory), as described + in the repo [README](../../README.md) + +## Running the example + +1. Set the `ProductUID` on your Notecard (via the [in-browser terminal][repl] + or the [Notecard CLI][cli]) so it reports to your Notehub project, or set + `productUID` in `code.py`. +2. Copy `circuit-python/code.py` to your Swan (CircuitPython auto-runs `code.py` + on boot). The LED will blink and the Notecard will report firmware version + `1.0.0` to Notehub. + +## Demonstrating an over-the-air update + +1. In `code.py`, change `BLINK_DELAY` (for example to `0.1` for a fast blink) + and bump `FIRMWARE_VERSION` (for example to `2.0.0`). +2. Build a firmware image and upload it to Notehub, then apply the DFU action + to the host. See the [Outboard Firmware Update guide][odfu] for the full + build-and-upload walkthrough. +3. Once the update is applied, the LED blink speed changes and Notehub reports + the new firmware version — confirming the over-the-air update succeeded. + +[odfu]: https://dev.blues.io/notehub/host-firmware-updates/notecard-outboard-firmware-update/ +[swan]: https://shop.blues.com/collections/feather-mcu/products/swan +[notecard]: https://shop.blues.io/collections/notecard +[notecarrier-f]: https://shop.blues.io/collections/notecarrier/products/notecarrier-f +[repl]: https://dev.blues.io/terminal/ +[cli]: https://dev.blues.io/tools-and-sdks/notecard-cli/ diff --git a/examples/outboard-dfu/circuit-python/code.py b/examples/outboard-dfu/circuit-python/code.py new file mode 100644 index 0000000..816f282 --- /dev/null +++ b/examples/outboard-dfu/circuit-python/code.py @@ -0,0 +1,74 @@ +"""note-python CircuitPython Outboard Firmware Update example. + +This file contains a complete working sample for demonstrating Notecard +Outboard Firmware Update from CircuitPython. It configures the Notecard to +enable Outboard Firmware Update for the host, then blinks the built-in LED with +a recognizable pattern that serves as the "payload" you update over-the-air. + +To demonstrate an update: change BLINK_DELAY (and bump FIRMWARE_VERSION) below, +build a new firmware image, upload it to Notehub, and apply the DFU action to +the host. The blink speed (and reported version) will change once the update +is applied. + +This example targets the Blues Swan on a Notecarrier F. Cygnet does not support +CircuitPython. +""" +import time +import board +import digitalio +import notecard +from notecard import card as card_helper +from notecard import dfu as dfu_helper +from notecard import hub as hub_helper + +# The unique Product Identifier for your device. Claim one in a Notehub project. +productUID = "com.your-company.your-project" + +# The firmware version reported to Notehub via dfu.status. Bump this (and change +# BLINK_DELAY) when you build a new image to update to. +FIRMWARE_VERSION = "1.0.0" + +# Seconds the LED stays on/off. Change this to make the update visually obvious. +BLINK_DELAY = 0.5 + + +def configure_outboard_dfu(card): + """Put the Notecard online and enable Outboard Firmware Update for the host. + + Outboard Firmware Update requires the Notecard to be in "continuous" or + "periodic" mode. On a Notecarrier F the DFU signals are routed over the + Notecard's shared AUX pins, so card.dfu uses mode "aux" and card.aux is set + to "off" to free those pins for DFU. + """ + hub_helper.set(card, product=productUID, mode="continuous", + sn="circuitpython-notecard") + + # Enable Outboard Firmware Update and tell the Notecard the host MCU type. + card_helper.dfu(card, name="stm32", on=True, mode="aux") + + # Free the AUX pins so they can be used for Outboard Firmware Update. + card_helper.aux(card, mode="off") + + # Enable host DFU and report the running firmware version to Notehub. + dfu_helper.status(card, on=True, version=FIRMWARE_VERSION) + + +def main(): + """Enable Outboard DFU, then blink the built-in LED as the update payload.""" + i2c = board.I2C() + card = notecard.OpenI2C(i2c, 0, 0, debug=True) + + configure_outboard_dfu(card) + + led = digitalio.DigitalInOut(board.LED) + led.direction = digitalio.Direction.OUTPUT + + print("Running firmware version {}. Hit CTRL-C to stop.".format(FIRMWARE_VERSION)) + while True: + led.value = True + time.sleep(BLINK_DELAY) + led.value = False + time.sleep(BLINK_DELAY) + + +main()