Bluetooth Mesh Light CTL (color tuned light) sample

Is there a sample app that uses the Zephyr Light CTL Bluetooth Mesh model?  I've tried to add that model to the light_ctrl sample app, but get a crash during configuration so I must be doing something wrong. 

The onoff_level_lighting_vnd app claims to support that model but it doesn't seem to be using the Zephyr lighting models. 

Thanks

Parents
  • Hi Jack, 
    I think you are right. There isn't a sample for that model right now. Please let us know the issue you are seeing we can try to take a look. 
    We have limited capacity in the holidays period but will be back to full staff by beginning of the year. 

  • Thanks, here goes.

    I started with the VSCode plugin light_ctrl sample using version 2.8.0 of the toolchain and libraries. Build machines with Windows and Linux tried.  Target is nRF52840DK. 

    The unmodified sample builds, runs, and provisions and functions correctly with the nRF Mesh Android app. 

    After the modifications below, the code compiles and runs, but during initialization I get an assert showing on the console (see below), and an "Initial Configuration Failed" message from the app.  

    Note that the assert states that temp srv wasn't properly initialized.  On debugging, however, it appeared that the tmp srv model was properly initialized and the light CTL model was not, however I have a low degree of confidence in this assessment. 

    model_handler.c was modified from the sample as follows:

    Add headers

    #include <bluetooth/mesh/light_ctrl_srv.h>
    #include <bluetooth/mesh/light_ctl_srv.h>

    Add placeholder handlers

    static void temp_srv_set(struct bt_mesh_light_temp_srv *srv,
                             struct bt_mesh_msg_ctx *ctx,
                             const struct bt_mesh_light_temp_set *set,
                             struct bt_mesh_light_temp_status *status)
    {
        // Implement setting the temperature on your hardware
    }

    static void temp_srv_get(struct bt_mesh_light_temp_srv *srv,
                             struct bt_mesh_msg_ctx *ctx,
                             struct bt_mesh_light_temp_status *status)
    {
        // Implement reading the current temperature state from your hardware
    }

    static const struct bt_mesh_light_temp_srv_handlers temp_srv_handlers = {
        .set = temp_srv_set,
        .get = temp_srv_get,
    };

    static struct bt_mesh_light_temp_srv light_temp_srv =
        BT_MESH_LIGHT_TEMP_SRV_INIT(&temp_srv_handlers);

    static struct bt_mesh_light_ctl_srv light_ctl_srv =

        BT_MESH_LIGHT_CTL_SRV_INIT(&lightness_srv_handlers, &temp_srv_handlers);

    Add light ctl and light temp srv models:

    static struct bt_mesh_elem elements[] = {
        BT_MESH_ELEM(1,
                     BT_MESH_MODEL_LIST(
                         BT_MESH_MODEL_CFG_SRV,
                         BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
                         BT_MESH_MODEL_LIGHTNESS_SRV(&my_ctx.lightness_srv),
                         BT_MESH_MODEL_SCENE_SRV(&scene_srv),
                         BT_MESH_MODEL_SENSOR_SRV(&sensor_srv),
                         BT_MESH_MODEL_LIGHT_CTL_SRV(&light_ctl_srv)
                                        ),
                     BT_MESH_MODEL_NONE),
        BT_MESH_ELEM(2,
                     BT_MESH_MODEL_LIST(
                         BT_MESH_MODEL_LIGHT_TEMP_SRV(&light_ctl_srv.temp_srv)
                     ),
                     BT_MESH_MODEL_NONE),
        BT_MESH_ELEM(3,
                     BT_MESH_MODEL_LIST(
                         BT_MESH_MODEL_LIGHT_CTRL_SRV(&light_ctrl_srv)
                                       ),
                    BT_MESH_MODEL_NONE)
    };

    prj.conf changes


    CONFIG_BT_MESH_MODEL_EXTENSION_LIST_SIZE=33

    CONFIG_BT_MESH_LIGHT_CTL_SRV=y

    CONFIG_CORTEX_M_DEBUG_MONITOR_HOOK=y

    CONFIG_SEGGER_DEBUGMON=y

    Console output:

    *** Booting Mesh Light Fixture v2.8.0-c57f55d0686e ***
    *** Using nRF Connect SDK v2.8.0-a2386bfc8401 ***
    *** Using Zephyr OS v3.7.99-0bc3393fb112 ***
    Initializing...
    [00:00:00.008,941] <inf> fs_nvs: 8 Sectors of 4096 bytes
    [00:00:00.008,972] <inf> fs_nvs: alloc wra: 0, fb0
    [00:00:00.008,972] <inf> fs_nvs: data wra: 0, 44
    [00:00:00.009,124] <inf> bt_sdc_hci_driver: SoftDevice Controller build revision:
    fe 2c f9 6a 7f 36 22 2e a0 79 c0 40 be 2c 03 20 |.,.j.6". .y.@.,.
    40 c2 f3 32 |@..2
    [00:00:00.013,305] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002)
    [00:00:00.013,336] <inf> bt_hci_core: HW Variant: nRF52x (0x0002)
    [00:00:00.013,366] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 254.63788 Build 573996906
    [00:00:00.013,763] <inf> bt_hci_core: No ID address. App must call settings_load()
    Bluetooth initialized
    [00:00:00.439,453] <inf> bt_hci_core: Identity: EC:2C:E8:3F:5C:0E (random)
    [00:00:00.439,514] <inf> bt_hci_core: HCI: version 6.0 (0x0e) revision 0x104e, manufacturer 0x0059
    [00:00:00.439,544] <inf> bt_hci_core: LMP: version 6.0 (0x0e) subver 0x104e
    [00:00:00.442,474] <inf> bt_mesh_provisionee: Device UUID: 5c0b7b01-159d-4da6-a3f4-84feea62c259
    Mesh initialized
    Successfully enabled LC server
    [00:02:35.433,380] <inf> bt_mesh_main: Primary Element: 0x0020
    New light transition-> Lvl: 0, Time: 0, Delay: 0
    [00:02:35.443,359] <err> bt_mesh_light_ctl_srv: Light CTL srv[32]: Temp. srv not properly initialized
    ASSERTION FAIL @ WEST_TOPDIR/zephyr/kernel/work.c:687
    [00:02:35.443,878] <err> os: r0/a1: 0x00000004 r1/a2: 0x000002af r2/a3: 0x00000003
    [00:02:35.443,908] <err> os: r3/a4: 0x00000004 r12/ip: 0x0001b4a4 r14/lr: 0x0004443b
    [00:02:35.443,908] <err> os: xpsr: 0x01000000
    [00:02:35.443,939] <err> os: s[ 0]: 0x00000000 s[ 1]: 0x0004c9d3 s[ 2]: 0x00000000 s[ 3]: 0x00000000
    [00:02:35.443,969] <err> os: s[ 4]: 0x0005e348 s[ 5]: 0x2000dd34 s[ 6]: 0x00000000 s[ 7]: 0x0004bc3d
    [00:02:35.443,969] <err> os: s[ 8]: 0x20006ea8 s[ 9]: 0x0004bc7f s[10]: 0xffffffff s[11]: 0x2000dd34
    [00:02:35.444,030] <err> os: s[12]: 0x00000000 s[13]: 0x00044431 s[14]: 0x0005e348 s[15]: 0x00066cdc
    [00:02:35.444,030] <err> os: fpscr: 0x000002af
    [00:02:35.444,030] <err> os: Faulting instruction address (r15/pc): 0x0004bc6a
    [00:02:35.444,091] <err> os: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0
    [00:02:35.444,122] <err> os: Current thread: 0x20006ea8 (sysworkq)
    [00:00:00.000,427] <err> qspi_nor: JEDEC id [ff ff ff] expect [c2 28 17]

  • Elfving, thanks for the update.  Just to review (it's been a long thread), I'm looking for an example of CTL working with other relevant models (lightness, on/off, temperature, maybe some others).  If someone could just take the light_ctrl example (or any other example) and add CTL, that would be ideal.  I'm just trying to get CTL to work some way.

    The code that we've been discussing was a graft of the Zephyr test suite with the light_ctrl example code and, as I said earlier, is a mess.  Everything else I have tried works worse than that one though.  I don't know if that's the best baseline, but there we are.

    It's not extremely time-critical, we have plenty of other things to do, but if we could get something in the next few weeks it would be helpful.  We definitely need to get this working in the end.

    Also, we have switched from nRF52840 to nRF54L15 in the meantime, and the behavior is similar.  The build files, etc. I have been sending are still from the 840 build.

    Thanks,
    Jack

  • Hi,

    So this issue could be connected to that CONFIG_BT_MESH_MAX_CONN should be set to less than CONFIG_BT_MAX_CONN if you'd use BLE for other purposes in addition to mesh, like SMP. Although it doesn't immediately make sense to me why putting CONFIG_BT_MAX_CONN to 1 should make things work, try lowering CONFIG_BT_MESH_MAX_CONN.

    Of course, CONFIG_BT_EXT_ADV_MAX_ADV_SET should be increased by one for each additional advertising set you want to use. If you didn't know exactly how many sets you are using, you could simply increase it one by one and see when it works (ideally for optimum memory use, you should add only that as many as you are using in the application). If this was set to 1 currently, that could also be something that helps us here.

    ji_584 said:

    The code that we've been discussing was a graft of the Zephyr test suite with the light_ctrl example code and, as I said earlier, is a mess.  Everything else I have tried works worse than that one though.  I don't know if that's the best baseline, but there we are.

    Understood. I can see if I can get something neater for you later on, though for the time being the project you sent is what we're looking at. You haven't added any other BLE connection to this, like SMP right?

    Regards,

    Elfving

  • Edit: corrected variable name CONFIG_BT_MAX_CONN below

    Elfving,
    It seems to compile, provision and configure if and only if 
    CONFIG_BT_MESH_MAX_CONN == 1
    Increasing CONFIG_BT_EXT_ADV_MAX_ADV_SET and/or CONFIG_BT_MAX_CONN didn't seem to change this.
    Is there a drawback to this setting of CONFIG_BT_MESH_MAX_CONN?

    Also, can you tell me the best resources for learning the effects and interactions of these configuration variables?  I know how to look up the name to get a description, but the context is sometimes lacking.
    I still haven't functionally tested the model once it doesn't throw errors, so I'll do that. It seems like the nRF Mesh app doesn't have a color temperature control, so I'll set up a dimmer example.

    Thanks,
    Jack
  • Thanks for the patience Jack,

    I'm waiting for a response from the relevant R&D team. Just one question.

    ji_584 said:
    It seems to compile, provision and configure if and only if 
    CONFIG_BT_MESH_MAX_CONN == 1

    This is interesting. But that is in addition to setting CONFIG_BT_MAX_CONN to 1, right?

    Regards,

    Elfving

  • Elfving,

    No, I last set: 

    CONFIG_BT_MAX_CONN=5
    CONFIG_BT_MESH_MAX_CONN=1
    CONFIG_BT_EXT_ADV_MAX_ADV_SET=10
    CONFIG_BT_BUF_CMD_TX_COUNT=5
    which doesn't produce an error when configuring. 
    Other values of MAX_CONN and MESH_MAX_CONN are also ok as long as (I think) MAX_CONN > MESH_MAX_CONN.
    If I set MESH_MAX_CONN to 2 or greater I get problems.
    Jack
Reply Children
  • Hi Jack, I am deeply sorry about the wait.

    I have had one from the relevant R&D team add Light CTL to the light_ctrl example here. 

    It is worth noting that this sample could be tested more. And one thing he for instance finds missing here is that transitions for Temperature are not printed in console. It is useful to add such printing (to show demonstration of simultaneous transitions for Lightness and Temperature).

    Regards,

    Elfving

  • Hi Elfving,

    I appreciate all your help. Understood about the testing situation. I'll check the new example out and let you know how it goes. 

    Jack

  • I was able to provision with the Android app without error.  It seems they added an assert to keep CONFIG_BT_MESH_MAX_CONN = 1.  I haven't tested the actual functionality yet.

  • Hi,
    It's been a while for this ticket, let me know if I need to open a new one.

    I have recently renewed effort in this area, and I have been unable to get a client to actually set the temperature.  I used the example ctl srv app posted above: 
    https://github.com/omkar3141/sdk-nrf/blob/5c2911b2970616609d03437c1992d311f3f302e7/samples/bluetooth/mesh/light_ctrl/src/model_handler.c

    and the dimmer example app modified to include a ctl client.

    I provisioned with the Android nRF Mesh, setting the app key for the relevant models on both server and client, and publish address of 0xFFFF on the client side models.   No error messages are observed, the client appears to be responding to the button press with calls to bt_mesh_light_ctl_set_unack(), the lightness level changes on the server, but there is no indication that the temp level changes.  I had to fix a few config file errors due to changing API requirements.  Hardware is two nrf54l15dk.

    Any advice on getting this working?

    Thanks,
    Jack

    Here is the model_handler.c I tested, derived from the sample dimmer app:

    /*
    * Copyright (c) 2023 Nordic Semiconductor ASA
    *
    * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
    */
    
    /**
    * @file
    * @brief Model handler for the Light Dimmer Controller.
    *
    */
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/mesh/proxy.h>
    #include <bluetooth/mesh/models.h>
    #include <bluetooth/mesh/light_ctl_cli.h>
    #include <dk_buttons_and_leds.h>
    #include "model_handler.h"
    #include <time.h>
    
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(dimmer_sample, LOG_LEVEL_INF);
    
    #define SHORT_PRESS_THRESHOLD_MS 300
    #define LONG_PRESS_TIMEOUT_SEC K_SECONDS(20)
    #define DELTA_MOVE_STEP 1000
    #define SCENE_COUNT 4
    
    /* Color temperature mapping constants */
    #define CTL_TEMP_MIN 2700 /* Warm white at low lightness */
    #define CTL_TEMP_MAX 6500 /* Cool white at high lightness */
    #define CTL_DELTA_UV 0 /* No delta UV adjustment */
    
    enum dim_direction { DIM_DOWN, DIM_UP };
    
    /* Forward declarations */
    static void ctl_status_handler(struct bt_mesh_light_ctl_cli *cli,
    struct bt_mesh_msg_ctx *ctx,
    const struct bt_mesh_light_ctl_status *status);
    static void temp_status_handler(struct bt_mesh_light_ctl_cli *cli,
    struct bt_mesh_msg_ctx *ctx,
    const struct bt_mesh_light_temp_status *status);
    
    /* CTL client handlers */
    static const struct bt_mesh_light_ctl_cli_handlers ctl_handlers = {
    .ctl_status = ctl_status_handler,
    .temp_status = temp_status_handler,
    };
    
    /** Context for a single Light Dimmer instance. */
    struct dimmer_ctx {
    /** Generic OnOff client instance for this switch */
    struct bt_mesh_onoff_cli onoff_client;
    /** Generic Level client instance for this switch */
    struct bt_mesh_lvl_cli lvl_client;
    /** Light CTL client instance for this switch */
    struct bt_mesh_light_ctl_cli ctl_client;
    /* Relative start time of the latest press of the button */
    int64_t press_start_time;
    /* Dimmer direction */
    enum dim_direction direction;
    /* Long press work (Dimmer mode) */
    struct k_work_delayable long_press_start_work;
    struct k_work_delayable long_press_stop_work;
    /* Current lightness level for temperature mapping */
    uint16_t current_lightness;
    };
    
    /** Context for a single scene button instance */
    struct scene_btn_ctx {
    /** Scene client instance for this button */
    struct bt_mesh_scene_cli client;
    /** The currently recalled scene*/
    uint16_t current_scene;
    /** Time when button was pressed */
    int64_t press_start_time;
    };
    
    #if IS_ENABLED(CONFIG_BT_MESH_NLC_PERF_CONF)
    static const uint8_t cmp2_elem_offset[1] = { 0 };
    
    static const struct bt_mesh_comp2_record comp_rec[2] = {
    {/* Basic Scene Selector NLC Profile 1.0.1*/
    .id = BT_MESH_NLC_PROFILE_ID_BASIC_SCENE_SELECTOR,
    .version.x = 1,
    .version.y = 0,
    .version.z = 1,
    .elem_offset_cnt = 1,
    .elem_offset = cmp2_elem_offset,
    .data_len = 0
    },
    {/* Dimming Control NLC Profile 1.0.1 */
    .id = BT_MESH_NLC_PROFILE_ID_DIMMING_CONTROL,
    .version.x = 1,
    .version.y = 0,
    .version.z = 1,
    .elem_offset_cnt = 1,
    .elem_offset = cmp2_elem_offset,
    .data_len = 0
    }
    };
    
    static const struct bt_mesh_comp2 comp_p2 = {
    .record_cnt = 2,
    .record = comp_rec
    };
    #endif
    
    static void dimmer_start(struct k_work *work)
    {
    struct dimmer_ctx *ctx = CONTAINER_OF(k_work_delayable_from_work(work), struct dimmer_ctx,
    long_press_start_work);
    
    struct bt_mesh_lvl_move_set move_set = {
    .delta = (ctx->direction == DIM_UP) ? DELTA_MOVE_STEP :
    (int16_t)(-1 * DELTA_MOVE_STEP),
    .transition = &(struct bt_mesh_model_transition){ .time = 500 },
    };
    
    LOG_INF("Start dimming %s...", (ctx->direction == DIM_UP ? "UP" : "DOWN"));
    
    /* Use traditional level control for continuous dimming - CTL will be set on release */
    if (bt_mesh_model_pub_is_unicast(ctx->lvl_client.model)) {
    bt_mesh_lvl_cli_move_set(&ctx->lvl_client, NULL, &move_set, NULL);
    } else {
    bt_mesh_lvl_cli_move_set_unack(&ctx->lvl_client, NULL, &move_set);
    }
    
    k_work_reschedule(&ctx->long_press_stop_work, LONG_PRESS_TIMEOUT_SEC);
    }
    
    static void dimmer_stop(struct k_work *work)
    {
    struct dimmer_ctx *ctx = CONTAINER_OF(k_work_delayable_from_work(work), struct dimmer_ctx,
    long_press_stop_work);
    
    struct bt_mesh_lvl_move_set move_set = {
    .delta = 0,
    .transition = NULL,
    };
    
    if (bt_mesh_model_pub_is_unicast(ctx->lvl_client.model)) {
    bt_mesh_lvl_cli_move_set(&ctx->lvl_client, NULL, &move_set, NULL);
    } else {
    bt_mesh_lvl_cli_move_set_unack(&ctx->lvl_client, NULL, &move_set);
    }
    }
    
    static struct dimmer_ctx dimmer = {
    .lvl_client = BT_MESH_LVL_CLI_INIT(NULL),
    .onoff_client = BT_MESH_ONOFF_CLI_INIT(NULL),
    .ctl_client = BT_MESH_LIGHT_CTL_CLI_INIT(&ctl_handlers),
    .press_start_time = 0,
    .direction = DIM_DOWN,
    .current_lightness = 0
    };
    
    /* Helper function to map lightness level to color temperature */
    static uint16_t lightness_to_temperature(uint16_t lightness)
    {
    /* Linear mapping: 0 lightness = 2700K, 65535 lightness = 6500K */
    uint32_t temp_range = CTL_TEMP_MAX - CTL_TEMP_MIN;
    uint32_t temp = CTL_TEMP_MIN + ((uint32_t)lightness * temp_range) / 65535;
    return (uint16_t)temp;
    }
    
    /* Helper function to set both lightness and temperature */
    static int set_light_ctl(uint16_t lightness, const struct bt_mesh_model_transition *transition)
    {
    uint16_t temperature = lightness_to_temperature(lightness);
    
    struct bt_mesh_light_ctl_set set = {
    .params = {
    .light = lightness,
    .temp = temperature,
    .delta_uv = CTL_DELTA_UV,
    },
    .transition = transition
    };
    
    dimmer.current_lightness = lightness;
    
    LOG_INF("Setting CTL: lightness=%u, temp=%uK, delta_uv=%d", lightness, temperature, CTL_DELTA_UV);
    
    /* Use unacknowledged message for group addresses or to avoid blocking */
    return bt_mesh_light_ctl_set_unack(&dimmer.ctl_client, NULL, &set);
    }
    
    /* CTL status handlers */
    static void ctl_status_handler(struct bt_mesh_light_ctl_cli *cli,
    struct bt_mesh_msg_ctx *ctx,
    const struct bt_mesh_light_ctl_status *status)
    {
    LOG_INF("CTL status: light=%u, temp=%u", status->current_light, status->current_temp);
    dimmer.current_lightness = status->current_light;
    }
    
    static void temp_status_handler(struct bt_mesh_light_ctl_cli *cli,
    struct bt_mesh_msg_ctx *ctx,
    const struct bt_mesh_light_temp_status *status)
    {
    LOG_INF("CTL temp status: temp=%u, delta_uv=%d",
    status->current.temp, status->current.delta_uv);
    }
    
    static void dimmer_button_handler(struct dimmer_ctx *ctx, bool pressed)
    {
    int err;
    
    if (pressed) {
    ctx->direction = (ctx->direction == DIM_UP) ? DIM_DOWN : DIM_UP;
    
    ctx->press_start_time = k_uptime_get();
    err = k_work_reschedule(&ctx->long_press_start_work,
    K_MSEC(SHORT_PRESS_THRESHOLD_MS));
    
    if (err < 0) {
    LOG_ERR("Scheduling dimming to start failed: %d", err);
    }
    } else {
    if ((k_uptime_get() - ctx->press_start_time) >= SHORT_PRESS_THRESHOLD_MS) {
    /** Button was released after a long time.
    * Stop dimming immediately.
    */
    err = k_work_reschedule(&ctx->long_press_stop_work, K_NO_WAIT);
    
    if (err < 0) {
    LOG_INF("Scheduling dimming to stop failed: %d", err);
    }
    
    } else {
    /** Button was released after a short time.
    * Cancel dimming operation and toggle light on/off with CTL.
    */
    k_work_cancel_delayable(&ctx->long_press_start_work);
    
    uint16_t target_lightness = (ctx->direction == DIM_UP) ? 65535 : 0;
    LOG_INF("Toggle light %s with CTL (lightness=%u)",
    (ctx->direction == DIM_UP ? "ON" : "OFF"), target_lightness);
    
    err = set_light_ctl(target_lightness, NULL);
    if (err) {
    LOG_ERR("Failed to send CTL set message: %d", err);
    
    /* Fallback to basic on/off control */
    struct bt_mesh_onoff_set set = {
    .on_off = ctx->direction,
    };
    
    if (bt_mesh_model_pub_is_unicast(ctx->onoff_client.model)) {
    err = bt_mesh_onoff_cli_set(&ctx->onoff_client, NULL, &set, NULL);
    } else {
    err = bt_mesh_onoff_cli_set_unack(&ctx->onoff_client, NULL, &set);
    }
    
    if (err) {
    LOG_ERR("Failed to send OnOff fallback message: %d", err);
    }
    }
    }
    }
    }
    
    static struct scene_btn_ctx scene_btn = {
    .current_scene = 1,
    .press_start_time = 0
    };
    
    static void scene_button_handler(struct scene_btn_ctx *ctx, bool pressed)
    {
    if (pressed) {
    ctx->press_start_time = k_uptime_get();
    } else {
    int err;
    
    if (k_uptime_get() - ctx->press_start_time > SHORT_PRESS_THRESHOLD_MS) {
    /* Long press, store scene */
    LOG_INF("Storing scene %d", ctx->current_scene);
    err = bt_mesh_scene_cli_store_unack(&ctx->client, NULL,
    ctx->current_scene);
    
    if (err) {
    LOG_ERR("Failed to send Scene Store message: %d", err);
    }
    } else {
    /* Short press, recall next scene */
    ctx->current_scene = (ctx->current_scene >= SCENE_COUNT) ?
    1 : ctx->current_scene + 1;
    LOG_INF("Recalling scene %d", ctx->current_scene);
    err = bt_mesh_scene_cli_recall_unack(&ctx->client, NULL,
    ctx->current_scene, NULL);
    
    if (err) {
    LOG_ERR("Failed to send Scene Recall message: %d", err);
    }
    }
    }
    }
    
    static void button_handler_cb(uint32_t pressed, uint32_t changed)
    {
    if (!bt_mesh_is_provisioned()) {
    LOG_ERR("Node is not provisioned");
    return;
    }
    
    if (changed & BIT(0)) {
    dimmer_button_handler(&dimmer, pressed & BIT(0));
    }
    if (changed & BIT(1)) {
    scene_button_handler(&scene_btn, pressed & BIT(1));
    }
    }
    
    /* Set up a repeating delayed work to blink the DK's LEDs when attention is
    * requested.
    */
    static struct k_work_delayable attention_blink_work;
    static bool attention;
    
    static void attention_blink(struct k_work *work)
    {
    static int idx;
    const uint8_t pattern[] = {
    #if DT_NODE_EXISTS(DT_ALIAS(sw0))
    BIT(0),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw1))
    BIT(1),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw2))
    BIT(2),
    #endif
    #if DT_NODE_EXISTS(DT_ALIAS(sw3))
    BIT(3),
    #endif
    };
    
    if (attention) {
    dk_set_leds(pattern[idx++ % ARRAY_SIZE(pattern)]);
    k_work_reschedule(&attention_blink_work, K_MSEC(30));
    } else {
    dk_set_leds(DK_NO_LEDS_MSK);
    }
    }
    
    static void attention_on(const struct bt_mesh_model *mod)
    {
    attention = true;
    k_work_reschedule(&attention_blink_work, K_NO_WAIT);
    }
    
    static void attention_off(const struct bt_mesh_model *mod)
    {
    /* Will stop rescheduling blink timer */
    attention = false;
    }
    
    static const struct bt_mesh_health_srv_cb health_srv_cb = {
    .attn_on = attention_on,
    .attn_off = attention_off,
    };
    
    static struct bt_mesh_health_srv health_srv = {
    .cb = &health_srv_cb,
    };
    
    BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);
    
    static struct bt_mesh_elem elements[] = {
    BT_MESH_ELEM(1,
    BT_MESH_MODEL_LIST(BT_MESH_MODEL_CFG_SRV,
    BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
    BT_MESH_MODEL_ONOFF_CLI(&dimmer.onoff_client),
    BT_MESH_MODEL_LVL_CLI(&dimmer.lvl_client),
    BT_MESH_MODEL_LIGHT_CTL_CLI(&dimmer.ctl_client),
    BT_MESH_MODEL_SCENE_CLI(&scene_btn.client)),
    BT_MESH_MODEL_NONE),
    };
    
    static const struct bt_mesh_comp comp = {
    .cid = CONFIG_BT_COMPANY_ID,
    .elem = elements,
    .elem_count = ARRAY_SIZE(elements),
    };
    
    const struct bt_mesh_comp *model_handler_init(void)
    {
    #if IS_ENABLED(CONFIG_BT_MESH_NLC_PERF_CONF)
    if (bt_mesh_comp2_register(&comp_p2)) {
    printf("Failed to register comp2\n");
    }
    #endif
    
    static struct button_handler button_handler = {
    .cb = button_handler_cb,
    };
    
    dk_button_handler_add(&button_handler);
    k_work_init_delayable(&attention_blink_work, attention_blink);
    k_work_init_delayable(&dimmer.long_press_start_work, dimmer_start);
    k_work_init_delayable(&dimmer.long_press_stop_work, dimmer_stop);
    
    return &comp;
    }



  • Hi Jack, 

    That's okay, we can keep it here.

    I'll look into it, but I might have to get back to you on this next week.

    Regards,

    Elfving

Related