micro:bit v1にてnRF Connect SDKでBLE Peripheral

# micro:bit v1にてnRF Connect SDKでBLE Peripheral

## 概要
nRF Connect SDKのBoard taget:`bbc_microbit`のサンプルのpongで、片方がCentalもう一方がPeripheralになるBLE通信してるっぽいので、それを参考に、Peripheralを簡単実装して、スマホからnRF Connect for Mobileアプリで、キャラクタリスティクスの読み書きをしてみる。

- https://github.com/mt08xx/microbit_v1_ble_periperal_example

## 動機
- たしか、v1.5がうちに4つ、v2.2が1つあったハズ...
- - nRF Connect SDKにボード設定(`bbc_microbit`)がある
    - hello_worldサンプルが実行・デバッグできる。
    - bbc_microbit用のサンプルがある
    - `pong`で、2台のmicrobitで対戦ゲームができるらしい => 実際できた

- センサーでも読みだして、お知らせするようなBLEデバイス(Peripheral)でも、できるんでは？

## 環境
- [micro:bit V1.5](https://tech.microbit.org/hardware/1-5-revision/)
    - nRF51822 (FLASH 256KB/RAM16KB) ... メモリがきつい。。 nrf51822はRAM32KBのもあるのだが、micro:bit v1では、16KB
- nRF Connect SDK v2.4.3
    - 最新v2.7.0でビルドしたら、RAMに　はまらなかったような。
    - ただのBLE使うんだったら、v2.4.xあたりで大丈夫なはず。
- VSCode
    - [Serial Monitor](https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-serial-monitor)を入れて、接続して、printkの出力を確認
    - [cortex-debug](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug)で、`pyocd`で、デバッグもできるぞ (nRF Connect SDKは通常jlinkを使用)

- スマホアプリ [nRF Connect for Mobile](https://www.nordicsemi.com/Products/Development-tools/nrf-connect-for-mobile)
    - 動作確認用　Connectして、READ↓ / WRITE↑ する

## 動作
- 1秒ごとにカウンターが＋１され、printk で出力される
- セントラルと接続時
    - セントラルからREADされると、カウンターの値を返す。
    - セントラルからWRITEされると、カウンターの値を更新する


## コード

- prog.confは、`pong`からそのまま流用
    - ※ちなみに`CONFIG_BT_CENTRAL=n`にすれば、FLASHもRAMもうちょっと空きができます。

    ```conf:prj.conf
    CONFIG_ISR_STACK_SIZE=1024
    CONFIG_BT=y
    CONFIG_BT_CENTRAL=y
    CONFIG_BT_PERIPHERAL=y
    CONFIG_BT_GATT_CLIENT=y
    CONFIG_BT_DEVICE_NAME="MICROBITv1"
    CONFIG_GPIO=y
    CONFIG_DISPLAY=y
    CONFIG_MICROBIT_DISPLAY=y
    CONFIG_PWM=y
    CONFIG_PWM_NRF5_SW=y
    
    # This feature isn't needed as this sample is intended to be used
    # between zephyr devices only.
    CONFIG_BT_GATT_CACHING=n
    ```
    <br>
- src/main.c

    ```c:src/main.c
    #include <zephyr/kernel.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/device.h>
    #include <string.h>
    #include <zephyr/drivers/pwm.h>
    #include <zephyr/debug/stack.h>
    
    #include <zephyr/display/mb_display.h>
    
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/bluetooth/hci.h>
    
    /*
      - 動作
    	- 1秒ごとにカウンターが＋１され、printk で出力される
        - セントラルと接続時
    		- セントラルからREADされると、カウンターの値を返す。
    		- セントラルからWRITEされると、カウンターの値を更新する
    */
    uint32_t count = 0;
    
    //----------------------------------------------------------------------
    //サービスとキャラクタリスティクスのUUID
    /** @brief Service UUID of Example Custom Service **/
    // 1605b267-ca22-4f4e-b7ea-0955ad54ca00
    #define BT_UUID_EXAMPLE_SERVICE_VAL \
    	BT_UUID_128_ENCODE(0x1605b267, 0xca22, 0x4f4e, 0xb7ea, 0x0955ad54ca00)
    
    /** @brief Characteristic UUID of EXAMPLE - Read/Write **/
    // ebbff71b-8a65-4596-be37-d845e2e7667e
    #define BT_UUID_EXAMPLE_CHAR_VAL \
    	BT_UUID_128_ENCODE(0xebbff71b, 0x8a65, 0x4596, 0xbe37, 0xd845e2e7667e)
    
    #define BT_UUID_EXAMPLE_SERVICE	BT_UUID_DECLARE_128(BT_UUID_EXAMPLE_SERVICE_VAL)
    #define BT_UUID_EXAMPLE_CHAR	BT_UUID_DECLARE_128(BT_UUID_EXAMPLE_CHAR_VAL)
    #define EXAMPLE_CHAR_NAME	"ExampleChar"
    //セントラルからREADされたときに呼ばれる関数
    static ssize_t read_EXAMPLE(struct bt_conn* conn,
    	const struct bt_gatt_attr* attr,
    	void* buf,
    	uint16_t len,
    	uint16_t offset)
    {
    	uint8_t buffer[4];
    	uint16_t buffer_len;
    	printk("%s\n", __FUNCTION__);
    
    	buffer_len = 4;
    	buffer[0] = (uint8_t)(count >> 24);
    	buffer[1] = (uint8_t)(count >> 16);
    	buffer[2] = (uint8_t)(count >> 8);
    	buffer[3] = (uint8_t)count;
    	return bt_gatt_attr_read(conn, attr, buf, len, offset,
    		buffer,
    		buffer_len);
    }
    //セントラルからWRITEされたときに呼ばれる関数
    static ssize_t write_EXAMPLE(struct bt_conn* conn,
    	const struct bt_gatt_attr* attr,
    	const void* buf,
    	uint16_t len,
    	uint16_t offset,
    	uint8_t flags)
    {
    	const char* const buffer = buf;
    	ARG_UNUSED(conn);	//avoid warning
    	printk("%s\n", __FUNCTION__);
    	uint32_t value = 0;
    	for (int i = 0; i < len; i++)
    	{
    		value = (value << 8) + buffer[i];
    		printk("%02x ", buffer[i]);
    	}
    	printk("\n");
    	count = value;
    
    	return len;
    }
    
    //こんな感じで定義する
    /* EXAMPLE BLE Service Declaration */
    BT_GATT_SERVICE_DEFINE(EXAMPLE_svc,
    	BT_GATT_PRIMARY_SERVICE(BT_UUID_EXAMPLE_SERVICE),
    	// Read/Write
    	BT_GATT_CHARACTERISTIC(BT_UUID_EXAMPLE_CHAR,
    		BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP,
    		BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
    		read_EXAMPLE, write_EXAMPLE, NULL),
    	BT_GATT_CUD(EXAMPLE_CHAR_NAME, BT_GATT_PERM_READ),
    );
    
    //----------------------------------------------------------------------
    //セントラルと接続したら呼ばれる関数
    static void connected(struct bt_conn* conn, uint8_t err)
    {
    	if (err) {
    		printk("Connection failed (err %u)\n", err);
    		return;
    	}
    	printk("Connected\n");
    }
    //セントラルから切断したら呼ばれる関数
    static void disconnected(struct bt_conn* conn, uint8_t reason)
    {
    	printk("Disconnected (reason %u)\n", reason);
    }
    //こんな感じで設定する
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected = connected,
    	.disconnected = disconnected,
    };
    
    //----------------------------------------------------------------------
    // アドバタイズ
    static const struct bt_data ad[] = {
    	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_EXAMPLE_SERVICE_VAL),
    };
    static const struct bt_data sd[] = {
    	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_EXAMPLE_SERVICE_VAL),
    };
    
    //----------------------------------------------------------------------
    int main(void)
    {
    	int err;
    	err = bt_enable(NULL);
    	err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
    	while (1)
    	{
    		printk("%08x\n", count++);
    		k_msleep(1000);
    	}
    	return 0;
    }
    ```


## その他
- UUID
    - https://duckduckgo.com で、`uuid`を検索すると、生成してくれる。
    <br>
- 使用メモリ
    - `-Os` にてビルド => RAM: <font color='red'>**16044**</font> / 16384
        ```
        [246/246] Linking C executable zephyr\zephyr.elf
        Memory region         Used Size  Region Size  %age Used
                   FLASH:      127492 B       256 KB     48.63%
                     RAM:       16044 B        16 KB     97.92%
                IDT_LIST:          0 GB         2 KB      0.00%
        ```
    - `-O2` にてビルド => RAM: <font color='red'>**16068**</font> / 16384
        ```
        [246/246] Linking C executable zephyr\zephyr.elf
        Memory region         Used Size  Region Size  %age Used
                   FLASH:      138968 B       256 KB     53.01%
                     RAM:       16068 B        16 KB     98.07%
                IDT_LIST:          0 GB         2 KB      0.00%
        ```
    - `-Og` にてビルド => RAM: <font color='red'>**16356**</font> / 16384
        ```
        [247/247] Linking C executable zephyr\zephyr.elf
        Memory region         Used Size  Region Size  %age Used
                   FLASH:      140080 B       256 KB     53.44%
                     RAM:       16356 B        16 KB     99.83%
                IDT_LIST:          0 GB         2 KB      0.00%
        ```
      <br>


- cortex-debugでの設定
    - launch.json - デバッグから、Create launch.jsonで。
    	```json:launch.json
    	{
    		// Use IntelliSense to learn about possible attributes.
    		// Hover to view descriptions of existing attributes.
    		// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    		"version": "0.2.0",
    		"configurations": [
    			{
    				"name": "Cortex Debug",
    				"cwd": "${workspaceFolder}",
    				"executable": "./build/zephyr/zephyr.elf", //実行ファイルを指定
    				"request": "launch",
    				"type": "cortex-debug",
    				"runToEntryPoint": "main",
    				"servertype": "pyocd",　　 //pyocd
    			}
    		]
    	}
    	```
        <br>
    -  settings.json - CTRL+,で設定開く. ツールのパスと、プリフィックスを指定
    	```json:settings.json
    	{
            ...
    		"cortex-debug.armToolchainPath": "C:/ncs/toolchains/31f4403e35/opt/zephyr-sdk/arm-zephyr-eabi/bin",
    		"cortex-debug.armToolchainPrefix": "arm-zephyr-eabi",
    		"cortex-debug.pyocdPath": "C:/ncs/toolchains/31f4403e35/opt/bin/Scripts/pyocd"
    	}

- あとで電力測定してみたいのだが、Interfaceチップ(KL26)で電気食ってる気がする..