This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

BLE mesh and FOTA upgrade

I am working with nRF Connect SDK using an nRF52840 DK. And I want to realise both BLE mesh and FOTA upgrade. FOTA upgrade example SMP Server Sample works properly for Bluetooth Low Energy (without any mesh). And now I want to integrate this FOTA upgrade into BLE mesh application. Please, tell me, how can I do it in the best way?
I had some attempts to realise it, however I faced some difficulties. I don't know how to stop mesh advertisement, which started by bt_mesh_prov_enable, and run ordinary BLE advertising by bt_le_adv_start. bt_le_adv_stop and bt_mesh_prov_disable didn't stop mesh advertisement, since calling bt_le_adv_start function after them returned EALREADY error code. As I think, I could merely set a flag to upgrade (after pressing a button or some command) and reboot microcontroller, and depending on this flag run the FOTA upgrade sample. But I think that there's a more straightforward way to realise this functionality. Can you explain to me how to merge BLE mesh example and FOTA upgrade properly?

Parents Reply Children
  • Thanks for your reply! It sounds frustrating.

    But is it possible to stop BLE mesh advertising and run ordinary BLE advertising by some trigger kinda a button or a command? Do you want to say that it's impossible to realise? Why doesn't bt_mesh_prov_disable function stop BLE mesh to allow me to run bt_le_adv_start after it and advertise SMP Service with UUID 8D53DC1D-1DB7-4CD3-868B-8A527460AA84 and etc.? And is it an appropriate workaround to reboot the microcontroller with running BLE mesh and run usual FOTA upgrade after it without BLE mesh? It won't be FOTA upgrade through BLE mesh, certainly, rather it will be direct BLE connection and upgrade.

    I merely thought that others realised the same functionality and it's a typical problem, which already was solved. That's why I would like to know the best practice how to realise FOTA upgrade in the case of BLE mesh.

    How is it expected to realise FOTA upgrade of microcontrollers within a BLE mesh?

  • Hi,

    Sorry for the confusion, it was a misuderstanding of the question.

    Yes, It is possible to realise both BLE Mesh and FOTA. You can disable the mesh stack, and start your own BLE operations for a time period to do this.

    RAlexeev said:
    Why doesn't bt_mesh_prov_disable function stop BLE mesh to allow me to run bt_le_adv_start after it and advertise SMP Service with UUID 8D53DC1D-1DB7-4CD3-868B-8A527460AA84 and etc.?

    I'm not sure why it doesn't disable the mesh. Have you debgged the code and see if the are any error messages retrning when you call bt_mesh_prov_disable? 

    RAlexeev said:
    And is it an appropriate workaround to reboot the microcontroller with running BLE mesh and run usual FOTA upgrade after it without BLE mesh?

    Yes, it this should be fine doing this.

  • Thanks for your answers.

    Yes, it this should be fine doing this.

    It's great, but I consider it as the last option, because it's not a pretty elegant solution.

    Yes, It is possible to realise both BLE Mesh and FOTA. You can disable the mesh stack, and start your own BLE operations for a time period to do this.

    Great! However, I faced some errors and it didn't work properly. In the case of unprovisioned node, I merely don't understand why bt_mesh_prov_disable() returns success and at the same time in debug log I see an error "bt_mesh_proxy: Failed to stop advertising (err -5)". But in the case, if a node is provisioned, then I can't disable BLE mesh and advertise SMP Service at all.

    As an example, if I modify your example Bluetooth: Mesh Light, adding a feature of disabling provisioning bearers and stopping advertisements after bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT) kinda like this:

    err = bt_mesh_prov_disable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
    if (err) {
        LOG_ERR("Failed to disable \"BT_MESH_PROV_ADV | BT_MESH_PROV_GATT\" (err %d)", err);
        return;
    } else {
        LOG_INF("\"BT_MESH_PROV_ADV | BT_MESH_PROV_GATT\" disabled");
    }
    
    err = bt_le_adv_stop();
    if (err) {
        LOG_ERR("Failed to stop advertising (err %d)", err);
    } else {
        LOG_INF("Stop advertising");
    }

    then bt_mesh_prov_disable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT) and bt_le_adv_stop() return successfully, but at the same time I have such warning and error in logs during calling those functions:

    [00:00:02.497,253] <wrn> bt_hci_core: opcode 0x200a status 0x0c
    [00:00:02.497,253] <err> bt_mesh_proxy: Failed to stop advertising (err -5)

    However, when I call these functions in the opposite order, firstly - bt_le_adv_stop(), and secondly - bt_mesh_prov_disable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT), then I don't have those warning and error in logs. Should I call bt_mesh_prov_disable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT) at all or not?

    But if I call just bt_le_adv_stop() without bt_mesh_prov_disable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT), then after calling bt_le_adv_start() nRF Connect for Mobile app shows "Mesh Provisioning Service". But if I call bt_mesh_prov_disable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT), then I don't have "Mesh Provisioning Service" in a list of services.

    Everything above was for the cases, when I didn't provision a node. In the case when I provisioned the node, and after it I tried to stop BLE mesh and start ordinary advertisement by calling these functions: bt_le_adv_stop() and bt_le_adv_start(), both of them return success result, but in reality the node doesn't start ordinary advertisement, and I see some grey records in nRF Connect for Mobile app with names "N/A (Bluetooth Mesh)" and multiple addresses. If I also call bt_mesh_prov_disable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT), then this function returns error code 69 regardless of it was called before or after bt_le_adv_stop().

    Thus, when the node is unprovisioned, then I can call bt_le_adv_stop() and bt_mesh_prov_disable() to stop a mesh and bt_le_adv_start() after it to start ordinary advertisement of SMP Service with UUID 8D53DC1D-1DB7-4CD3-868B-8A527460AA84. Is it the right way or not? It seems that it works. But what should I do in the case of the provisioned node? Tell me, please, how to start an ordinary advertisement, when the node is already provisioned? And is it enough to call bt_le_adv_stop() and bt_mesh_prov_disable() to disable BLE mesh or should I call something else also?

    I would be appreciated if you assist me in this issue before the weekends.

  • Hi,

    The problem might be that you are calling bt_mesh_prov_disable(), you should call bt_mesh_suspend() instead.

  • I tried the proposed solutions. But unfortunately, they also don't work and it outputs the same errors and even some others.
    I wrote a simplified example, which switches between BLE mesh and usual BLE, to demonstrate my problem. I built it both with nRF Connect SDK v1.3.0 and master branch. The example is consisted of BLE mesh network, running initially, and pressing a button 1 on an nRF52840 DK you can stop BLE mesh and start usual BLE and vice versa.

    #include <zephyr.h>
    #include <device.h>
    #include <drivers/gpio.h>
    #include <sys/__assert.h>
    #include <string.h>
    
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/conn.h>
    #include <bluetooth/gatt.h>
    #include <bluetooth/mesh/models.h>
    #include <bluetooth/mesh/dk_prov.h>
    #include <dk_buttons_and_leds.h>
    
    #include "model_handler.h"
    
    #define LOG_LEVEL LOG_LEVEL_DBG
    #include <logging/log.h>
    LOG_MODULE_REGISTER(my_ble_sample);
    
    #define DEVICE_NAME             CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN         (sizeof(DEVICE_NAME) - 1)
    
    
    static const struct bt_data ad[] = {
        BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
        BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
    };
    
    
    typedef enum
    {
        MODE_BLE_MESH     = 0x00,
        MODE_NOT_BLE_MESH = 0x01,
        NUMBER_OF_BLE_MODES
    } ble_mode_t;
    
    static struct k_delayed_work change_ble_mode_work;
    
    static void button_pressed(uint32_t button_state, uint32_t has_changed);
    static void change_ble_mode(struct k_work *work);
    static void connected(struct bt_conn *conn, uint8_t err);
    static void disconnected(struct bt_conn *conn, uint8_t reason);
    static void start_ble(void);
    static void stop_ble(void);
    static void start_ble_mesh(void);
    static void stop_ble_mesh();
    static void bt_ready(int err);
    
    
    static void button_pressed(uint32_t button_state, uint32_t has_changed)
    {
        if (has_changed & DK_BTN1_MSK) {  /* Check a particular button whether its state has changed */
            if ((button_state & DK_BTN1_MSK) != 0) {  /* The button is pressed */
                k_delayed_work_submit(&change_ble_mode_work, K_NO_WAIT);
                return;
            }
        }
    }
    
    
    static void change_ble_mode(struct k_work *work)
    {
        static ble_mode_t current_ble_mode = MODE_BLE_MESH;
    
        switch(current_ble_mode) {
            case MODE_BLE_MESH:
                LOG_DBG("Change mode from MODE_BLE_MESH to MODE_NOT_BLE_MESH.");
                stop_ble_mesh();
                start_ble();
                break;
            case MODE_NOT_BLE_MESH:
                LOG_DBG("Change mode from MODE_NOT_BLE_MESH to MODE_BLE_MESH.");
                stop_ble();
                start_ble_mesh();
                break;
        }
    
        current_ble_mode = (current_ble_mode + 1) % NUMBER_OF_BLE_MODES;
    }
    
    
    static void connected(struct bt_conn *conn, uint8_t err)
    {
        if (err) {
            LOG_ERR("Connection failed (err 0x%02x)", err);
        } else {
            LOG_INF("Connected");
        }
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
        LOG_INF("Disconnected (reason 0x%02x)", reason);
        start_ble();
    }
    
    static struct bt_conn_cb conn_callbacks = {
        .connected = connected,
        .disconnected = disconnected,
    };
    
    static void start_ble(void)
    {
        int err;
    
        err = bt_le_adv_stop();
        if (err) {
            LOG_ERR("Failed to stop advertising (err %d)", err);
        } else {
            LOG_INF("Stop advertising");
        }
    
        err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
        if (err) {
            LOG_ERR("Failed to start advertising (err %d)", err);
            return;
        } else {
            LOG_INF("Start advertising");
        }
    
        bt_conn_cb_register(&conn_callbacks);
    }
    
    
    static void stop_ble(void)
    {
        int err;
    
        bt_conn_cb_register(NULL);
    
        err = bt_le_adv_stop();
        if (err) {
            LOG_ERR("Failed to stop advertising (err %d)", err);
        } else {
            LOG_INF("Stop advertising");
        }
    }
    
    
    static void start_ble_mesh(void)
    {
        int err;
    
        if (bt_mesh_is_provisioned()) {
            err = bt_mesh_resume();
            if (err) {
                LOG_ERR("Failed to resume BLE mesh (err %d)", err);
            } else {
                LOG_INF("Resume BLE mesh");
            }
        } else {
            err = bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
            if (err) {
                LOG_ERR("Failed to enable BLE mesh provisioning (err %d)", err);
            } else {
                LOG_INF("Enable BLE mesh provisioning");
            }
        }
    }
    
    
    static void stop_ble_mesh()
    {
        int err;
        
        if (bt_mesh_is_provisioned()) {
            err = bt_mesh_suspend();
            if (err) {
                LOG_ERR("Failed to suspend BLE mesh (err %d)", err);
            } else {
                LOG_INF("Suspend BLE mesh");
            }
        } else {
            err = bt_mesh_prov_disable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
            if (err) {
                LOG_ERR("Failed to disable BLE mesh provisioning (err %d)", err);
            } else {
                LOG_INF("Disable BLE mesh provisioning");
            }
        }    
    }
    
    
    static void bt_ready(int err)
    {
        if (err) {
            LOG_ERR("Bluetooth init failed (err %d).", err);
            return;
        } else {
            LOG_INF("Bluetooth initialized.");
        }
    
        err = bt_mesh_init(bt_mesh_dk_prov_init(), model_handler_init());
        if (err) {
            LOG_ERR("Initializing mesh failed (err %d)\n", err);
            return;
        } else {
            LOG_INF("Initialize BLE mesh");
        }
    
        if (IS_ENABLED(CONFIG_SETTINGS)) {
            settings_load();
        }
    
        /* This will be a no-op if settings_load() loaded provisioning info */
        err = bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
        if (err) {
            LOG_ERR("Failed to enable BLE mesh provisioning (err %d)", err);
        } else {
            LOG_INF("Enable BLE mesh provisioning");
        }
    
        k_delayed_work_init(&change_ble_mode_work, change_ble_mode);
    }
    
    
    void main(void)
    {
        int err;
    
        err = dk_leds_init();
        if (err) {
            LOG_ERR("LEDs init error %d", err);
            return;
        }
    
        err = dk_buttons_init(button_pressed);
        if (err) {
            LOG_ERR("Buttons init error %d", err);
            return;
        }
    
        err = bt_enable(bt_ready);
        if (err) {
            LOG_ERR("Bluetooth init failed (err %d).", err);
            return;
        }
    }
    

    #include <bluetooth/bluetooth.h>
    #include <bluetooth/mesh/models.h>
    #include <dk_buttons_and_leds.h>
    #include "model_handler.h"
    
    
    /** Configuration server definition */
    static struct bt_mesh_cfg_srv cfg_srv = {
        .relay = IS_ENABLED(CONFIG_BT_MESH_RELAY),
        .beacon = BT_MESH_BEACON_ENABLED,
        .frnd = IS_ENABLED(CONFIG_BT_MESH_FRIEND),
        .gatt_proxy = IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY),
        .default_ttl = 7,
    
        /* 3 transmissions with 20ms interval */
        .net_transmit = BT_MESH_TRANSMIT(2, 20),
        .relay_retransmit = BT_MESH_TRANSMIT(2, 20),
    };
    
    /* Set up a repeating delayed work to blink the DK's LEDs when attention is
     * requested.
     */
    static struct k_delayed_work attention_blink_work;
    
    static void attention_blink(struct k_work *work)
    {
        static int idx;
        const uint8_t pattern[] = {
            BIT(0) | BIT(1),
            BIT(1) | BIT(2),
            BIT(2) | BIT(3),
            BIT(3) | BIT(0),
        };
        dk_set_leds(pattern[idx++ % ARRAY_SIZE(pattern)]);
        k_delayed_work_submit(&attention_blink_work, K_MSEC(30));
    }
    
    static void attention_on(struct bt_mesh_model *mod)
    {
        k_delayed_work_submit(&attention_blink_work, K_NO_WAIT);
    }
    
    static void attention_off(struct bt_mesh_model *mod)
    {
        k_delayed_work_cancel(&attention_blink_work);
        dk_set_leds(DK_NO_LEDS_MSK);
    }
    
    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(&cfg_srv),
                               BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub)),
            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)
    {
        k_delayed_work_init(&attention_blink_work, attention_blink);
    
        return &comp;
    }
    

    And you can find the whole project here:

    example.zip

    Run the nRF52840 DK.
    Let's consider the first case when BLE mesh node is not provisioned.


    *** Booting Zephyr OS build v2.3.0-rc1-ncs1  ***
    [00:00:00.003,143] <inf> mcuboot: Starting bootloader
    [00:00:00.009,552] <inf> mcuboot: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
    [00:00:00.019,805] <inf> mcuboot: Boot source: none
    [00:00:00.025,421] <inf> mcuboot: Swap type: none
    [00:00:00.378,479] <inf> mcuboot: Bootloader chainload address offset: 0xc000
    [00:00:00.386,291] <inf> mcuboot: Jumping to the first image slot
    *** Booting Zephyr OS build v2.3.0-rc1-ncs1  ***
    [00:00:00.002,899] <inf> fs_nvs: 2 Sectors of 4096 bytes
    [00:00:00.002,899] <inf> fs_nvs: alloc wra: 0, ff0
    [00:00:00.002,929] <inf> fs_nvs: data wra: 0, 0
    [00:00:00.005,706] <inf> bt_hci_core: No ID address. App must call settings_load()
    [00:00:00.005,706] <inf> my_ble_sample: Bluetooth initialized.
    [00:00:00.005,767] <inf> my_ble_sample: Initialize BLE mesh
    [00:00:00.021,392] <inf> my_ble_sample: Enable BLE mesh provisioning


    Initially, everything seems fine, BLE mesh started. You can provision the node using the nRF Mesh mobile app. And you can connect and disconnect.

    [00:01:03.898,437] <inf> bt_mesh_main: Primary Element: 0x0002
    [00:01:11.455,322] <wrn> bt_mesh_transport: No matching TX context for ack



    Okay, the next step is that you disconnect from the node and press a button 1 to change a BLE mode. You can see that node is suspended, but advertising hasn't started.

    [00:01:31.023,345] <dbg> my_ble_sample.change_ble_mode: Change mode from MODE_BLE_MESH to MODE_NOT_BLE_MESH.
    [00:01:31.023,620] <inf> my_ble_sample: Suspend BLE mesh
    [00:01:31.023,834] <inf> my_ble_sample: Stop advertising
    [00:01:31.023,986] <err> my_ble_sample: Failed to start advertising (err -22)


    You press the button 1 again to get it back to BLE mesh mode.

    [00:03:02.516,021] <dbg> my_ble_sample.change_ble_mode: Change mode from MODE_NOT_BLE_MESH to MODE_BLE_MESH.
    [00:03:02.516,021] <inf> my_ble_sample: Stop advertising
    [00:03:02.517,089] <inf> my_ble_sample: Resume BLE mesh


    Everything has worked without any errors. So, now you try to connect to this BLE mesh node from the nRF Mesh mobile app (Android).

    In the case of a master branch, I have such assertion and fatal error:
    ASSERTION FAIL [client] @ WEST_TOPDIR/zephyr/subsys/bluetooth/mesh/proxy.c:750
            No client for connection
    [00:00:54.936,340] <err> os: r0/a1:  0x00000004  r1/a2:  0x000002ee  r2/a3:  0x200012d0
    [00:00:54.936,370] <err> os: r3/a4:  0x00000002 r12/ip:  0x6f697463 r14/lr:  0x000286d1
    [00:00:54.936,370] <err> os:  xpsr:  0x41000000
    [00:00:54.936,370] <err> os: Faulting instruction address (r15/pc): 0x0003d368
    [00:00:54.936,370] <err> os: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0
    [00:00:54.936,401] <err> os: Current thread: 0x20001b58 (unknown)
    [00:00:55.052,429] <err> fatal_error: Resetting system


    In the case of v1.3.0, I just can't connect to the node. It tried to connect the first time, but without any result and after it the node is not scannable from the app.

    But if try to connect to the node from the nRF Connect for Mobile (Android), then it connects fine without fatal error and rebooting. But it generates another error permanently:

    [00:00:30.454,193] <wrn> bt_mesh_proxy: Failed to advertise using Network ID (err -12)



    Another way, start from the beginning. You don't provision the node at all and it's unprovisioned. And after flashing and running the node, you merely press the button 1 to switch to normal BLE.
    Initial output is the same:
    *** Booting Zephyr OS build v2.3.0-rc1-ncs1  ***
    [00:00:00.003,143] <inf> mcuboot: Starting bootloader
    [00:00:00.009,552] <inf> mcuboot: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
    [00:00:00.019,836] <inf> mcuboot: Boot source: none
    [00:00:00.025,451] <inf> mcuboot: Swap type: none
    [00:00:00.378,936] <inf> mcuboot: Bootloader chainload address offset: 0xc000
    [00:00:00.386,779] <inf> mcuboot: Jumping to the first image slot
    *** Booting Zephyr OS build v2.3.0-rc1-ncs1  ***
    [00:00:00.002,899] <inf> fs_nvs: 2 Sectors of 4096 bytes
    [00:00:00.002,899] <inf> fs_nvs: alloc wra: 0, ff0
    [00:00:00.002,899] <inf> fs_nvs: data wra: 0, 0
    [00:00:00.005,676] <inf> bt_hci_core: No ID address. App must call settings_load()
    [00:00:00.005,676] <inf> my_ble_sample: Bluetooth initialized.
    [00:00:00.005,737] <inf> my_ble_sample: Initialize BLE mesh
    [00:00:00.021,331] <inf> my_ble_sample: Enable BLE mesh provisioning



    You press the button 1:
    [00:01:41.167,907] <dbg> my_ble_sample.change_ble_mode: Change mode from MODE_BLE_MESH to MODE_NOT_BLE_MESH.
    [00:01:41.168,212] <inf> my_ble_sample: Disable BLE mesh provisioning
    [00:01:41.168,548] <wrn> bt_hci_core: opcode 0x200a status 0x0c
    [00:01:41.168,579] <err> bt_mesh_proxy: Failed to stop advertising (err -5)
    [00:01:41.168,609] <inf> my_ble_sample: Stop advertising
    [00:01:41.168,792] <err> my_ble_sample: Failed to start advertising (err -22)


    You can see errors, which I mentioned in my previous messages. And BLE advertisement doesn't work.
    After it you press the button 1 again to get it back to BLE mesh mode:

    [00:03:02.380,554] <dbg> my_ble_sample.change_ble_mode: Change mode from MODE_NOT_BLE_MESH to MODE_BLE_MESH.
    [00:03:02.380,554] <inf> my_ble_sample: Stop advertising
    [00:03:02.381,256] <inf> my_ble_sample: Enable BLE mesh provisioning


    And you try to add the node in the nRF Mesh mobile app (Android).


    In the case of the master branch, I have such assertion and fatal error:
    ASSERTION FAIL [client] @ WEST_TOPDIR/zephyr/subsys/bluetooth/mesh/proxy.c:631
            No client for connection
    [00:00:47.184,997] <err> os: r0/a1:  0x00000004  r1/a2:  0x00000277  r2/a3:  0x200012d0
    [00:00:47.184,997] <err> os: r3/a4:  0x00000002 r12/ip:  0x6f697463 r14/lr:  0x00028671
    [00:00:47.185,028] <err> os:  xpsr:  0x41000000
    [00:00:47.185,028] <err> os: Faulting instruction address (r15/pc): 0x0003d368
    [00:00:47.185,028] <err> os: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0
    [00:00:47.185,058] <err> os: Current thread: 0x20001b58 (unknown)
    [00:00:47.321,014] <err> fatal_error: Resetting system



    And in the case of v1.3.0, the nRF Mesh mobile app is trying to "connect" to the node for a while and stops without any result. And the node becomes not scannable from the app also.

    I sincerely ask you to provide at least a small workable example of switching between BLE mesh and usual BLE. As you can see from my logs, switching between BLE mesh and usual BLE produces various errors. Maybe you know how to overcome them.

    And please, give a bit more detailed answer to allow me to reproduce your idea. I am so eagerly awaiting to realise my BLE mesh and OTA DFU.

Related