nRF connect SDK:BLE Meshが分かり易くなった

#BLE Mesh
従来のBluetooth/Bluetooth LEが1対1(Peer to Peer/P2P)で通信するのに対して、1対多および多対多のフラッド通信を行うことで複数の端末と同時にやり取りをする通信方式です。また、自分宛ではないメッセージをホップすることで従来のP2P通信では届かない遠くのデバイスに対してもメッセージを届けることが可能な通信方式です。
この通信方式で現状最も普及しているのはおそらくZigbeeではないかと思われます。例えばPhilipsのHueなんかもZigbeeですね。ちなみにこれを書いている時点(2022年1月)では、日本においてBluetooth Meshを使用した製品は存在しません。
##かつては鬼門だったBLE Mesh
BLE Meshの製品がまだ日本で存在していない理由は色々あるのだろうとは思いますが、なんだかんだでBluetoothにおいて一番シェアの高いNordicにおいてはSDKの存在が鬼門だったのではないかと思います。実はnRF SDKのころからBLE Meshは存在していたのですが、nRF SDK for meshという別のSDKで提供されていました。

https://www.nordicsemi.com/Products/Development-software/nRF5-SDK-for-Mesh/Download

SDKが分かれているということは当然それだけで済むはずもなく、nRF SDKとは提供されているAPIが完全に別物になっています。Meshに関わる部分は動作が異なるので違うAPIになるのは当たり前ですが、基本的な部分であるNRF_LOG_INFOなどのログを吐き出すAPIやボタンを押すAPI(Callback)などの関数の作り方も全く違うので、ぶっちゃけていうとnRF SDKで習得したことは**何一つ役に立ちません！**
私も何度かこのSDKでMeshに挑戦したのですが、挫折しまくって結局何も作れませんでした・・・。
##が！
nRF connect SDKではBLEもBLE Meshも両方とも一つのSDKでサポートしています。ということは・・・当たり前ですがBLEに関わる部分以外は共通になったのです。これは大きい！
##余談
BLE Meshが本格的に稼働し始めたこともあり、かつBluetooth LEではないBluetoothはClassicと呼ぶようになった今、BLE MeshではないBLEはBLE Classicと呼ぶらしいです。(検索ではまともにヒットしません)
#共通化されたmain.c
ただ単にプロジェクトからコピーしただけです(笑)が、このファイルの中身を触ることは基本的にないと思ってよさそうです。

```main.c
/*
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

/** @file
 *  @brief Nordic mesh sensor observer sample
 */
#include <bluetooth/bluetooth.h>
#include <bluetooth/mesh/models.h>
#include <bluetooth/mesh/dk_prov.h>
#include <dk_buttons_and_leds.h>
#include "model_handler.h"

static void bt_ready(int err)
{
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}

	printk("Bluetooth initialized\n");

	dk_leds_init();
	dk_buttons_init(NULL);

	err = bt_mesh_init(bt_mesh_dk_prov_init(), model_handler_init());
	if (err) {
		printk("Initializing mesh failed (err %d)\n", err);
		return;
	}

	if (IS_ENABLED(CONFIG_SETTINGS)) {
		settings_load();
	}

	/* This will be a no-op if settings_load() loaded provisioning info */
	bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);

	printk("Mesh initialized\n");
}

void main(void)
{
	int err;

	printk("Initializing...\n");

	err = bt_enable(bt_ready);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
	}
}
```

##モデルの定義
nRF connect SDKにおいてはBLE Mesh使用時のmain.cは共通化されているため、どこで差別化されているのかというと、

```
	err = bt_mesh_init(bt_mesh_dk_prov_init(), model_handler_init());
```

で呼び出しているmodel_handler_init()の中で実際に使用するModelsを定義しています。これがmodel_handler.cになります。
同じモデルは各エレメントに1つまでです。例えばGeneric OnOff Server/Clientを一つのエレメントに2つ以上持たせることはできません。
###共通部分
model_handler.cにもベースとなる部分があって、それが以下になります。

```model_handler.c
/*
 * Copyright (c) 2020 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <bluetooth/bluetooth.h>
#include <bluetooth/mesh/models.h>
#include <dk_buttons_and_leds.h>
#include "model_handler.h"

/* 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,
			     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;
}
```

なお、この部分はいわゆるプロビジョニングをした時の点滅処理でして、全てのサンプルプロジェクトで共通になってはいますが例えばLEDの点滅パターンを変えるなどの変更は可能です。