Conversation
c4e68e4 to
0784754
Compare
Deploying interrupt with
|
| Latest commit: |
f6a5b82
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://70e46452.interrupt.pages.dev |
| Branch Preview URL: | https://fbo-matter-light-bulb-post.interrupt.pages.dev |
0784754 to
f6a5b82
Compare
There was a problem hiding this comment.
Pull request overview
Adds a long-form blog post describing how to build a Matter dimmable light on the nRF54LM20 DK, along with accompanying example firmware snippets and helper scripts for composing/generating ZAP artifacts.
Changes:
- Add ZAP fragment compose/decompose utilities and a generation wrapper script.
- Add a Zephyr-based light module (PWM + shell commands) and a board device-tree overlay for PWM LED pins.
- Add a new Jekyll post documenting the full build and implementation walkthrough.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| example/matter-light-bulb/zap_generate.sh | Adds a shell pipeline to compose ZAP fragments and run Matter ZAP codegen. |
| example/matter-light-bulb/zap_decompose.py | Adds a utility to split a monolithic .zap into reusable fragments. |
| example/matter-light-bulb/zap_compose.py | Adds a utility to merge fragments into a generated .zap and endpoints list. |
| example/matter-light-bulb/nrf54lm20dk_nrf54lm20a_cpuapp.overlay | Adds PWM LED + pinctrl configuration for the target DK. |
| example/matter-light-bulb/light.h | Declares a simple C API for light on/off + brightness control. |
| example/matter-light-bulb/light.c | Implements PWM-based LED driving and Zephyr shell commands. |
| _posts/2026-03-31-matter-light-bulb.md | Publishes the “Matter light bulb from scratch” post with code excerpts and build steps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #include <zephyr/drivers/pwm.h> | ||
| #include <zephyr/shell/shell.h> | ||
| #include <zephyr/logging/log.h> | ||
|
|
There was a problem hiding this comment.
strtoul() is used below but <stdlib.h> isn’t included, which can trigger a build failure under C99/C11 (implicit declaration). Add the proper standard header.
| /* Map level 1–254 to pulse width (use uint64 to avoid overflow) */ | ||
| pulse = (uint32_t)(((uint64_t)pwm_leds[i].period * light_level) / 254); | ||
| } | ||
| pwm_set_pulse_dt(&pwm_leds[i], pulse); | ||
| } |
There was a problem hiding this comment.
pwm_set_pulse_dt() returns an error code, but it’s ignored here. If the driver rejects an out-of-range pulse (e.g., due to an unexpected level), failures will be silent and the light state will desync from Matter. Capture and log/handle the return value.
| set(ZAP_GEN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/build/zap) | ||
| if(NOT EXISTS ${ZAP_GEN_DIR}/matter_echo.zap) | ||
| execute_process( | ||
| COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/tools/zap_generate.sh ${ZAP_GEN_DIR} |
There was a problem hiding this comment.
The post’s CMake snippet references ${CMAKE_CURRENT_SOURCE_DIR}/tools/zap_generate.sh, but in this repo the script is at example/matter-light-bulb/zap_generate.sh and there is no example/matter-light-bulb/tools/ directory. Update the snippet (or the file layout) so readers can follow it verbatim.
| COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/tools/zap_generate.sh ${ZAP_GEN_DIR} | |
| COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/zap_generate.sh ${ZAP_GEN_DIR} |
| /* Light endpoint ID must match the ZAP file (endpoint 2) */ | ||
| static constexpr EndpointId kLightEndpointId = 2; |
There was a problem hiding this comment.
This comment/snippet says the light endpoint is "endpoint 2", but earlier the post describes the application endpoint as endpoint 1, and the provided zap_compose.py assigns endpoint IDs sequentially starting at 0 (root) then 1 (first fragment). This mismatch is likely to break copy/paste for readers; align the endpoint ID consistently.
| /* Light endpoint ID must match the ZAP file (endpoint 2) */ | |
| static constexpr EndpointId kLightEndpointId = 2; | |
| /* Light endpoint ID must match the ZAP file (endpoint 1) */ | |
| static constexpr EndpointId kLightEndpointId = 1; |
|
|
||
| Phew, I learned a lot about Matter putting this together. In the end, building a Matter light bulb is straightforward! Indeed, had I not gotten sucked into the ZAP file side-quest, it would have taken no more than an hour or two. | ||
|
|
||
| This firmware build stands on the shoulders of giants. Below our 200 lines of application code lies tends of thousands of lines of C++ implementing the Matter spec. The resulting firmware binary is almost 1MB! So while this was relatively little work, it is a complex firmware. |
There was a problem hiding this comment.
Typo: “tends of thousands” should be “tens of thousands”.
| This firmware build stands on the shoulders of giants. Below our 200 lines of application code lies tends of thousands of lines of C++ implementing the Matter spec. The resulting firmware binary is almost 1MB! So while this was relatively little work, it is a complex firmware. | |
| This firmware build stands on the shoulders of giants. Below our 200 lines of application code lies tens of thousands of lines of C++ implementing the Matter spec. The resulting firmware binary is almost 1MB! So while this was relatively little work, it is a complex firmware. |
| python3 "$GENERATE_PY" \ | ||
| --no-prettify-output \ | ||
| --templates "$APP_TEMPLATES" \ | ||
| --zcl "$ZCL_JSON" \ | ||
| --output-dir "$ZAP_GENERATED" \ | ||
| "$ZAP_FILE" 2>&1 | grep -E "^[✍🕐]" || true |
There was a problem hiding this comment.
Because this python invocation is in a pipeline and ends with "|| true", the script will report success even if generate.py fails. Consider enabling pipefail and preserving generate.py’s exit status (while still optionally filtering output) so failures stop the script.
| python3 "$GENERATE_PY" \ | ||
| --no-prettify-output \ | ||
| --templates "$IDL_TEMPLATES" \ | ||
| --zcl "$ZCL_JSON" \ | ||
| --output-dir "$ZAP_DIR" \ | ||
| "$ZAP_FILE" 2>&1 | grep -E "^[✍🕐]" || true |
There was a problem hiding this comment.
Same issue as above: piping through grep and ending with "|| true" masks generate.py failures. The second generation step should also propagate errors so ZAP generation can’t silently fail.
|
|
||
| #define NUM_LEDS ARRAY_SIZE(pwm_leds) | ||
|
|
There was a problem hiding this comment.
NUM_LEDS uses ARRAY_SIZE(), but this translation unit doesn’t include the header that defines it (it relies on transitive includes). Add the appropriate Zephyr util header (or define a local ARRAY_SIZE) to make this file self-contained.
| if (level > 0) { | ||
| light_on = true; | ||
| } |
There was a problem hiding this comment.
When level is set to 0, apply_pwm() will turn the LEDs off, but light_on remains unchanged. That can make light_get()/status report ON while brightness is 0. Consider explicitly setting light_on = (level > 0) to keep state consistent.
| if (level > 0) { | |
| light_on = true; | |
| } | |
| light_on = (level > 0); |
| uint8_t level = (uint8_t)strtoul(argv[1], NULL, 10); | ||
|
|
||
| light_set_level(level); | ||
| return 0; |
There was a problem hiding this comment.
The shell input is parsed with strtoul() and then cast to uint8_t, so out-of-range values will wrap (e.g., 256 → 0) and 255 can produce an out-of-range PWM pulse. Validate/clamp the value to 0–254 (per the help text) and return an error on invalid input.
No description provided.