Publishing for Bluetooth Asia 2018 developer session: mesh session. This repo is for GenericOnOff server side firmware.
EULA
PLEASE READ MESH_DEMO_TUTORIAL_EULA.TXT BEFORE START DEVELOPMENT.
Overview
This sample demonstrates Bluetooth Mesh functionality on micro:bit, www.microbit.org. It doesn't need provisioning because all the credential material (DevKey, NetKey, AppKey, Key Index, IVI and Unicast address) is pre-configured. This sample work as a GenericOnOff server:
- Pressing Button B on micro:bit will subscribe to the group address from 0xC000 to 0xC003 and back to 0xC000 again like round robin;
Requirements
micro:bit board
Building and Running
0. Download source code, it's zip file, the link is on this page, Download repository
.
1. Following below link to set the development environment up on your Windows computer. http://docs.zephyrproject.org/getting_started/installation_win.html Please make sure git checkout this commit, commit: 60b01f3f5489975098657caa514133c89a472d19 , the detail is here, https://github.com/zephyrproject-rtos/zephyr/commit/60b01f3f5489975098657caa514133c89a472d19
2. copy zip file into ./zephyr/samples/
in the Zephyr tree.
3. unzip the file by "Extract Here".
3. access into the extracted folder and rename prj_bbc_microbit.txt
to prj_bbc_microbit.conf
4. if adopting Windows Command Prompt, use it to access into the unzip folder and type following commands on the console:
mkdir build & cd build
cmake -GNinja -DBOARD=bbc_microbit ..
ninja
if adopting MSYS2, use it to access into the unzip folder and type following commands on the console:
mkdir build & cd build
cmake -GNinja -DBOARD=bbc_microbit ..
ninja
5. connect micro:bit with your computer by USB cable, the board will be enumerated as a massive storage device;
6. drag the hex file (which is in ./zephyr/sample/[unzip folder]/build/zephyr/zephyr.hex
) into
Microbit massive storage device to flash the firmware;
7. micro:bit is ready to work as a GenericOnOff server.
src/main.c
- Committer:
- krenbluetoothsig
- Date:
- 2018-05-16
- Revision:
- 4:a52949a1cf72
- Parent:
- 0:3ed424a8870a
File content as of revision 4:a52949a1cf72:
/* * Copyright (c) 2018 Bluetooth SIG * * SPDX-License-Identifier: Apache-2.0 */ #include <misc/printk.h> #include <bluetooth/bluetooth.h> #include <bluetooth/mesh.h> #include <gpio.h> #include <board.h> #include <soc.h> #include <misc/printk.h> #include <ctype.h> #include <flash.h> #include <gpio.h> #include <pwm.h> #include <display/mb_display.h> #include <bluetooth/mesh.h> #define GROUP_ADDR 0xc000 #define PUBLISHER_ADDR 0x000f #if !defined(NODE_ADDR) #define NODE_ADDR 0x0b02 #endif /* Model Operation Codes */ #define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01) #define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02) #define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03) #define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04) /* Button debounce timeout */ #define BUTTON_DEBOUNCE_DELAY_MS 250 /* LED matrix scroll speed */ #define SCROLL_SPEED K_MSEC(500) /* LED matrix scroll speed */ #define BUZZER_PIN EXT_P0_GPIO_PIN #define BEEP_DURATION K_MSEC(60) /* NVM offset */ #define SEQ_PER_BIT 976 #define SEQ_PAGE (NRF_FICR->CODEPAGESIZE * (NRF_FICR->CODESIZE - 1)) #define SEQ_MAX (NRF_FICR->CODEPAGESIZE * 8 * SEQ_PER_BIT) /* Key definition, it's pre-configured, not need to do provisioning. */ static const u8_t net_key[16] = { 0xf1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, }; static const u8_t dev_key[16] = { 0xf1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, }; static const u8_t app_key[16] = { 0xf1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, }; /* group address string. */ const char *groupAddress2String[] = { "Bedroom", "Living", "Dining", "Garage" }; static struct device *gpio; static struct device *nvm; static struct device *pwm; static struct k_work button_work; static struct k_work subscribe_work; static u32_t time, last_time; const u16_t net_idx; const u16_t app_idx; const u32_t iv_index; u8_t flags; u16_t addr; static u32_t seq; /* group address, initialized */ u16_t groupAddress = GROUP_ADDR; u16_t board_set_target(void) { switch (groupAddress) { case GROUP_ADDR: groupAddress++ ; break; case (GROUP_ADDR + 3): groupAddress = GROUP_ADDR; break; default: groupAddress++; break; } return groupAddress; } void board_seq_update(u32_t seq) { u32_t loc, seq_map; int err; if (seq % SEQ_PER_BIT) { return; } loc = (SEQ_PAGE + ((seq / SEQ_PER_BIT) / 32)); err = flash_read(nvm, loc, &seq_map, sizeof(seq_map)); if (err) { printk("flash_read err %d\n", err); return; } seq_map >>= 1; flash_write_protection_set(nvm, false); err = flash_write(nvm, loc, &seq_map, sizeof(seq_map)); flash_write_protection_set(nvm, true); if (err) { printk("flash_write err %d\n", err); } } static u32_t get_seq(void) { u32_t seq_map, seq = 0; int err, i; for (i = 0; i < NRF_FICR->CODEPAGESIZE / sizeof(seq_map); i++) { err = flash_read(nvm, SEQ_PAGE + (i * sizeof(seq_map)), &seq_map, sizeof(seq_map)); if (err) { printk("flash_read err %d\n", err); return seq; } printk("seq_map 0x%08x\n", seq_map); if (seq_map) { seq = ((i * 32) + (32 - popcount(seq_map))) * SEQ_PER_BIT; if (!seq) { return 0; } break; } } seq += SEQ_PER_BIT; if (seq >= SEQ_MAX) { seq = 0; } if (seq) { seq_map >>= 1; flash_write_protection_set(nvm, false); err = flash_write(nvm, SEQ_PAGE + (i * sizeof(seq_map)), &seq_map, sizeof(seq_map)); flash_write_protection_set(nvm, true); if (err) { printk("flash_write err %d\n", err); } } else { printk("Performing flash erase of page 0x%08x\n", SEQ_PAGE); err = flash_erase(nvm, SEQ_PAGE, NRF_FICR->CODEPAGESIZE); if (err) { printk("flash_erase err %d\n", err); } } return seq; } static void buttonA_pressed(struct k_work *work) { struct mb_display *disp = mb_display_get(); printk("buttonA_pressed\n"); mb_display_print(disp, MB_DISPLAY_MODE_DEFAULT, SCROLL_SPEED, "%s", groupAddress2String[groupAddress - GROUP_ADDR]); } static void buttonB_resubscribe(struct k_work *work) { struct mb_display *disp = mb_display_get(); printk("change new group address to 0x%04x\n", board_set_target()); mb_display_print(disp, MB_DISPLAY_MODE_DEFAULT, SCROLL_SPEED, "%s", groupAddress2String[groupAddress - GROUP_ADDR]); // change model subscription bt_mesh_cfg_mod_sub_overwrite(net_idx, addr, addr, groupAddress, BT_MESH_MODEL_ID_GEN_ONOFF_SRV, NULL); { struct bt_mesh_cfg_hb_sub sub = { .src = PUBLISHER_ADDR, .dst = groupAddress, .period = 0x10, }; bt_mesh_cfg_hb_sub_set(net_idx, addr, &sub, NULL /*&status*/); } } static void button_pressed(struct device *dev, struct gpio_callback *cb, uint32_t pins) { /* * One button press within a 1 second interval sends an on message * More than one button press sends an off message */ time = k_uptime_get_32(); /* debounce the switch */ if (time < last_time + BUTTON_DEBOUNCE_DELAY_MS) { last_time = time; return; } if (pins & BIT(SW0_GPIO_PIN)) { k_work_submit(&button_work); } else { k_work_submit(&subscribe_work); } last_time = time; } static const struct { char note; u32_t period; u32_t sharp; } period_map[] = { { 'C', 3822, 3608 }, { 'D', 3405, 3214 }, { 'E', 3034, 3034 }, { 'F', 2863, 2703 }, { 'G', 2551, 2407 }, { 'A', 2273, 2145 }, { 'B', 2025, 2025 }, }; static u32_t get_period(char note, bool sharp) { int i; if (note == ' ') { return 0; } for (i = 0; i < ARRAY_SIZE(period_map); i++) { if (period_map[i].note != note) { continue; } if (sharp) { return period_map[i].sharp; } else { return period_map[i].period; } } return 1500; } void board_heartbeat(u8_t hops, u16_t feat) { struct mb_display *disp = mb_display_get(); const struct mb_image hops_img[] = { MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }), MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 0, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }), MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 1, 1, 1, 1 }), MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 1, 1, 1, 1 }), MB_IMAGE({ 1, 0, 1, 0, 1 }, { 0, 0, 0, 0, 0 }, { 1, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0 }, { 1, 0, 1, 0, 1 }) }; printk("%u hops\n", hops); if (hops) { hops = min(hops, ARRAY_SIZE(hops_img)); mb_display_image(disp, MB_DISPLAY_MODE_SINGLE, K_SECONDS(2), &hops_img[hops - 1], 1); } } void board_other_dev_pressed(u16_t addr) { struct mb_display *disp = mb_display_get(); printk("board_other_dev_pressed(0x%04x)\n", addr); mb_display_print(disp, MB_DISPLAY_MODE_SINGLE, K_SECONDS(2), "%c", groupAddress2String[groupAddress - GROUP_ADDR][0]); } void board_attention(bool attention) { struct mb_display *disp = mb_display_get(); static const struct mb_image attn_img[] = { MB_IMAGE({ 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0 }, { 0, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0 }), MB_IMAGE({ 0, 0, 0, 0, 0 }, { 0, 1, 1, 1, 0 }, { 0, 1, 1, 1, 0 }, { 0, 1, 1, 1, 0 }, { 0, 0, 0, 0, 0 }), MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 0, 1, 1 }, { 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1 }), MB_IMAGE({ 1, 1, 1, 1, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 0, 0, 0, 1 }, { 1, 1, 1, 1, 1 }), }; if (attention) { mb_display_image(disp, MB_DISPLAY_MODE_DEFAULT | MB_DISPLAY_FLAG_LOOP, K_MSEC(150), attn_img, ARRAY_SIZE(attn_img)); } else { mb_display_stop(disp); } } static void configure_button(void) { static struct gpio_callback button_cb; /* Initialize the button debouncer */ last_time = k_uptime_get_32(); k_work_init(&button_work, buttonA_pressed); k_work_init(&subscribe_work, buttonB_resubscribe); gpio = device_get_binding(SW0_GPIO_NAME); gpio_pin_configure(gpio, SW0_GPIO_PIN, (GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE | GPIO_INT_ACTIVE_LOW)); gpio_pin_configure(gpio, SW1_GPIO_PIN, (GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE | GPIO_INT_ACTIVE_LOW)); gpio_init_callback(&button_cb, button_pressed, BIT(SW0_GPIO_PIN) | BIT(SW1_GPIO_PIN)); gpio_add_callback(gpio, &button_cb); gpio_pin_enable_callback(gpio, SW0_GPIO_PIN); gpio_pin_enable_callback(gpio, SW1_GPIO_PIN); } void board_init(u16_t *addr, u32_t *seq) { struct mb_display *disp = mb_display_get(); printk("SEQ_PAGE 0x%08x\n", SEQ_PAGE); nvm = device_get_binding(FLASH_DEV_NAME); pwm = device_get_binding(CONFIG_PWM_NRF5_SW_0_DEV_NAME); *addr = ( (u16_t)NRF_FICR->DEVICEADDR[0] ) & 0x7fff; printk("FICR 0x%02x\n", *addr); *seq = get_seq(); mb_display_print(disp, MB_DISPLAY_MODE_DEFAULT, SCROLL_SPEED, "%s %s", "Server", groupAddress2String[groupAddress - GROUP_ADDR]); configure_button(); } static void heartbeat(u8_t hops, u16_t feat) { board_heartbeat(hops, feat); } static struct bt_mesh_cfg_srv cfg_srv = { //#if defined(CONFIG_BOARD_BBC_MICROBIT) // .relay = BT_MESH_RELAY_ENABLED, // //.relay = BT_MESH_RELAY_DISABLED, // .beacon = BT_MESH_BEACON_DISABLED, //#else .relay = BT_MESH_RELAY_DISABLED, .beacon = BT_MESH_BEACON_DISABLED, //#endif .frnd = BT_MESH_FRIEND_NOT_SUPPORTED, .default_ttl = 7, /* 3 transmissions with 20ms interval */ .net_transmit = BT_MESH_TRANSMIT(2, 20), .relay_retransmit = BT_MESH_TRANSMIT(3, 20), .hb_sub.func = heartbeat, }; static struct bt_mesh_cfg_cli cfg_cli = { }; static void attention_on(struct bt_mesh_model *model) { printk("attention_on()\n"); board_attention(true); } static void attention_off(struct bt_mesh_model *model) { printk("attention_off()\n"); board_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); BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_srv, NULL, 2 + 2); BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_cli, NULL, 2 + 2); static void gen_onoff_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct mb_display *disp = mb_display_get(); uint8_t value, trans; value = *buf->data++; trans = *buf->data++; printk("gen_onoff_set model 0x%04x, src 0x%04x, value:0x%02x, trans: 0x%02x\n", model->id, ctx->addr, value, trans); if(value){ mb_display_print(disp, MB_DISPLAY_MODE_SINGLE, -1, "%c", groupAddress2String[groupAddress - GROUP_ADDR][0]); } else{ mb_display_stop(disp); } } static void gen_onoff_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { printk("gen_onoff_status modelId 0x%04x, src 0x%04x\n", model->id, ctx->addr); } /* * OnOff Model Server Op Dispatch Table * */ static const struct bt_mesh_model_op gen_onoff_srv_op[] = { //{ BT_MESH_MODEL_OP_GEN_ONOFF_GET, 0, gen_onoff_get }, { BT_MESH_MODEL_OP_GEN_ONOFF_SET, 2, gen_onoff_set }, //{ BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack }, BT_MESH_MODEL_OP_END, }; static struct bt_mesh_model root_models[] = { BT_MESH_MODEL_CFG_SRV(&cfg_srv), BT_MESH_MODEL_CFG_CLI(&cfg_cli), BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub), BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, &gen_onoff_pub_srv, NULL), }; static struct bt_mesh_elem elements[] = { BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE), //BT_MESH_ELEM(0, vnd_models, BT_MESH_MODEL_NONE), }; static const struct bt_mesh_comp comp = { .cid = BT_COMP_ID_LF, .elem = elements, .elem_count = ARRAY_SIZE(elements), }; static void configure(void) { printk("Configuring...\n"); /* Add Application Key */ bt_mesh_cfg_app_key_add(net_idx, addr, net_idx, app_idx, app_key, NULL); /* Bind to vendor model */ bt_mesh_cfg_mod_app_bind(net_idx, addr, addr, app_idx, BT_MESH_MODEL_ID_GEN_ONOFF_SRV, NULL); /* Bind to Health model */ bt_mesh_cfg_mod_app_bind(net_idx, addr, addr, app_idx, BT_MESH_MODEL_ID_HEALTH_SRV, NULL); bt_mesh_cfg_mod_sub_add(net_idx, addr, addr, groupAddress, BT_MESH_MODEL_ID_GEN_ONOFF_SRV, NULL); { struct bt_mesh_cfg_hb_sub sub = { .src = PUBLISHER_ADDR, .dst = groupAddress, .period = 0x10, }; bt_mesh_cfg_hb_sub_set(net_idx, addr, &sub, NULL); printk("Subscribing to heartbeat messages, group address: 0x%04x, %s\n", groupAddress, groupAddress2String[groupAddress - GROUP_ADDR]); } printk("Configuration complete\n"); } static const u8_t dev_uuid[16] = { 0xdd, 0xdd }; static const struct bt_mesh_prov prov = { .uuid = dev_uuid, }; static void bt_ready(int err) { if (err) { printk("Bluetooth init failed (err %d)\n", err); return; } printk("Bluetooth initialized\n"); err = bt_mesh_init(&prov, &comp); if (err) { printk("Initializing mesh failed (err %d)\n", err); return; } printk("Mesh initialized\n"); err = bt_mesh_provision(net_key, net_idx, flags, iv_index, seq, addr, dev_key); if (err) { printk("Provisioning failed (err %d)\n", err); return; } printk("Provisioning completed\n"); configure(); } void main(void) { int err; printk("Initializing...\n"); board_init(&addr, &seq); printk("Unicast address: 0x%04x, seq 0x%06x\n", addr, seq); /* Initialize the Bluetooth Subsystem */ err = bt_enable(bt_ready); if (err) { printk("Bluetooth init failed (err %d)\n", err); } while (1) { } }