Skip to content

Post about building a Matter light bulb#626

Open
franc0is wants to merge 2 commits intomasterfrom
fbo/matter-light-bulb-post
Open

Post about building a Matter light bulb#626
franc0is wants to merge 2 commits intomasterfrom
fbo/matter-light-bulb-post

Conversation

@franc0is
Copy link
Copy Markdown
Member

No description provided.

@franc0is franc0is requested a review from a team as a code owner March 24, 2026 20:38
@franc0is franc0is force-pushed the fbo/matter-light-bulb-post branch 2 times, most recently from c4e68e4 to 0784754 Compare March 24, 2026 20:40
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Mar 24, 2026

Deploying interrupt with  Cloudflare Pages  Cloudflare Pages

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

View logs

@noahp noahp force-pushed the fbo/matter-light-bulb-post branch from 0784754 to f6a5b82 Compare April 2, 2026 19:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment on lines +9 to +12
#include <zephyr/drivers/pwm.h>
#include <zephyr/shell/shell.h>
#include <zephyr/logging/log.h>

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +38
/* 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);
}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
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}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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}

Copilot uses AI. Check for mistakes.
Comment on lines +482 to +483
/* Light endpoint ID must match the ZAP file (endpoint 2) */
static constexpr EndpointId kLightEndpointId = 2;
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
/* 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;

Copilot uses AI. Check for mistakes.

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.
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Typo: “tends of thousands” should be “tens of thousands”.

Suggested change
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.

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +50
python3 "$GENERATE_PY" \
--no-prettify-output \
--templates "$APP_TEMPLATES" \
--zcl "$ZCL_JSON" \
--output-dir "$ZAP_GENERATED" \
"$ZAP_FILE" 2>&1 | grep -E "^[✍🕐]" || true
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +52 to +57
python3 "$GENERATE_PY" \
--no-prettify-output \
--templates "$IDL_TEMPLATES" \
--zcl "$ZCL_JSON" \
--output-dir "$ZAP_DIR" \
"$ZAP_FILE" 2>&1 | grep -E "^[✍🕐]" || true
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +22

#define NUM_LEDS ARRAY_SIZE(pwm_leds)

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +60
if (level > 0) {
light_on = true;
}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
if (level > 0) {
light_on = true;
}
light_on = (level > 0);

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +114
uint8_t level = (uint8_t)strtoul(argv[1], NULL, 10);

light_set_level(level);
return 0;
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
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.

3 participants