micro:bitでCOCOA探知器を作ってみよう！！ ～ 社会貢献できるかな ～

# はじめに

次のブログを読んで、「自分が何か貢献できることは無いか？」と思って、Bluetooth搭載のmicro:bitでCOCOA探知器を開発してみます。
COCOAの6割普及に貢献できるといいのですが。[^1] [^2]


> [「批判の文化が日本を技術後進国にしているかもしれないという話」
（https://simplearchitect.hatenablog.com/entry/2020/06/22/083821）](https://simplearchitect.hatenablog.com/entry/2020/06/22/083821)


"驚愕のド素人開発"ですので、ググりまくって、開発を進めましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/b7247b62-e22a-5153-4672-34ffa3bbfafb.png)
(追記 2020/07/25 ココアの写真)

## （追記 2020/07/22）COCOA で夏休み自由研究

夏休みが始まりましたね。時期をずらしたり、半日登校にしたりと、地域によって対応は異なるようですね。カリキュラムを消化することに重点が置かれ、夏休みの自由研究とかは、提出自体が自由になっちゃうんですかね。
でも、COCOAを教材にすれば、いろいろな研究ができそうですよ。自粛でつちかった自主性を自由研究に生かしてください。

### 事例
【ご注意】調査・実験等での不特定多数の人との直接的な接触は避けましょう。

* アプリのしくみは？
* アプリが開発された目的は？
* アプリの普及と効果の関係は？
* アプリの開発に携わったのはどんな人たち？
* アプリはどのように開発された？
* アプリを入れた人、入れていない人の理由は？
* 個人情報やプライバシーの扱いは？
* アプリを普及させるためには？


#【驚愕のド素人開発 - 1日目】

## 下調べ

COCOAのしくみを確認します。
https://qiita.com/ken_hamada/items/d9a3ef65ae20f26c7f07

接触確認には、 Bluetooth Low Energy を使っているようです。
仕様書：https://www.blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf

micro:bitについて、おさらいします。
https://ht-deko.com/arduino/microbit.html

COCOAアプリが発信するアドバタイジング・データを`Central`としてmicro:bitでスキャンし、`Complete 16-bit Service UUID Section` が、`0xFD6F`であることを確認できれば、良いようです。

## 開発環境

micro:bitで、Bluetooth Low Energyの組み込み開発を行うために、次の３つを試しました。

1. [Arduino IDE - BLEPeripheral ライブラリ](https://github.com/sandeepmistry/arduino-BLEPeripheral)
2. [Mynewt - Nimble](https://mynewt.apache.org/latest/network/index.html)
3. [Zephyr - Bluetooth](https://docs.zephyrproject.org/latest/guides/bluetooth/index.html)

結論だけ、申し上げますと、この中で、`Central`として機能し、メモリ(RAM)が16kb以内に収まったのは、**Zephyr**のサンプルプログラムだけでした。

※ Zephyrの開発環境構築に関しては、次のページを参照してください(Windows 10)。
https://qiita.com/jp-96/items/6e32f6b36a5e2d5d2418

#【驚愕のド素人開発 - 2日目】

## COCOAのインストール
まずは、新型コロナウイルス接触確認アプリ「COCOA」をスマホにインストールしました。
手元のスマホは、Androidです。
https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/kenkou_iryou/covid19_qa_kanrenkigyou_00009.html

## Zephyrのサンプルプログラム - Central の動作確認

Zephyrには、micro:bitでも動作する[Central](https://github.com/zephyrproject-rtos/zephyr/tree/master/samples/bluetooth/central)というサンプルプラグラムがありますので、まずは、その動作を確認してみます。

```プロンプトコマンド.cmd
c:\zephyr\venv\scripts\activate
cd c:\zephyr\zephyrproject\zephyr\
west build -p auto -b bbc_microbit samples\bluetooth\central
west flash
```

```
(venv) c:\zephyr\zephyrproject\zephyr>west build -p auto -b bbc_microbit samples\bluetooth\central
[3/7] Linking C executable zephyr\zephyr_prebuilt.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:       94812 B       256 KB     36.17%
            SRAM:       15413 B        16 KB     94.07%
        IDT_LIST:         152 B         2 KB      7.42%
[7/7] Linking C executable zephyr\zephyr.elf

(venv) c:\zephyr\zephyrproject\zephyr>west flash
-- west flash: rebuilding
[0/1] cmd.exe /C "cd /D C:\zephyr\zephyrproject\zephyr\bui...\flash && "C:\Program Files\CMake\bin\cmake.exe" -E echo "

-- west flash: using runner pyocd
-- runners.pyocd: Flashing file: C:/zephyr/zephyrproject/zephyr/build/zephyr/zephyr.hex
0000760:WARNING:common:STLink and CMSIS-DAPv2 probes are not supported because no libusb library was found.
[====================] 100%
0008832:INFO:loader:Erased 95232 bytes (93 sectors), programmed 95232 bytes (93 pages), skipped 0 bytes (0 pages) at 12.28 kB/s

(venv) c:\zephyr\zephyrproject\zephyr>
```

例えば、Tera Termでmicro:bitに接続すると、スキャンしたBLEデバイスが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/4c520784-ded9-de58-d48d-77d992def2c1.png)

## スニペットによるソースコードレビュー

サンプルプログラム`Central`のソースコードの内容を確認してみます。

### main()関数

`main()`関数で、`connected()`関数と`disconnected()`関数をコールバックに設定(`bt_conn_cb_register()`)し、`start_scan()`関数でスキャンを開始しています。

```main.c

static struct bt_conn_cb conn_callbacks = {
		.connected = connected,
		.disconnected = disconnected,
};

void main(void)
{
	int err;

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

	printk("Bluetooth initialized\n");

	bt_conn_cb_register(&conn_callbacks);

	start_scan();
}
```

### start_scan()関数
`bt_le_scan_start()`関数でコールバック関数として`device_found()`関数を指定しており、BLEデバイスが見つかる度に`device_found()`関数が呼び出されるようにしています。

```main.c
static void start_scan(void)
{
	int err;

	/* This demo doesn't require active scan */
	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
	if (err) {
		printk("Scanning failed to start (err %d)\n", err);
		return;
	}

	printk("Scanning successfully started\n");
}
```

### device_found()関数

見つかったBLEデバイスの情報が次の引数で渡されてきます。

| # | 引数                      | 内容 |
|---|---------------------------|------|
| 1 | const bt_addr_le_t *addr  | Advertiser LE address and type. |
| 2 | int8_t rssi               | Strength of advertiser signal. |
| 3 | uint8_t type              | Type of advertising response from advertiser. |
| 4 | struct net_buf_simple *ad | Buffer containing advertiser data. |

サンプルプログラムでは、`type`が`BT_GAP_ADV_TYPE_ADV_IND`や`BT_GAP_ADV_TYPE_ADV_DIRECT_IND`である接続可能なBLEデバイスを対象とし、その`addr`を文字列化して(`bt_addr_le_to_str()`)、表示しています。
また、`rssi`が`-70`以上の強度であれば、スキャンを停止し(`bt_le_scan_stop()`)、接続を試みています(`bt_conn_le_create()`)。

```main.c
static struct bt_conn *default_conn;

static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
			 struct net_buf_simple *ad)
{
	char addr_str[BT_ADDR_LE_STR_LEN];
	int err;

	if (default_conn) {
		return;
	}

	/* We're only interested in connectable events */
	if (type != BT_GAP_ADV_TYPE_ADV_IND &&
	    type != BT_GAP_ADV_TYPE_ADV_DIRECT_IND) {
		return;
	}

	bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
	printk("Device found: %s (RSSI %d)\n", addr_str, rssi);

	/* connect only to devices in close proximity */
	if (rssi < -70) {
		return;
	}

	if (bt_le_scan_stop()) {
		return;
	}

	err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
				BT_LE_CONN_PARAM_DEFAULT, &default_conn);
	if (err) {
		printk("Create conn to %s failed (%u)\n", addr_str, err);
		start_scan();
	}
}
```

## COCOAアプリは、どれ？

[仕様書(p.5)](https://www.blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf)には、次のように記述されています。しかし、サンプルプログラムでは、`type`が`BT_GAP_ADV_TYPE_ADV_IND`や`BT_GAP_ADV_TYPE_ADV_DIRECT_IND`であるBLEデバイスを接続対象としていますので、`BT_GAP_ADV_TYPE_ADV_NONCONN_IND`であるCOCOAアプリのBLEデバイスの情報は、Tera Termに表示されません。

```
During the Bluetooth broadcast, advertisements are to be non-connectable undirected of type ADV_NONCONN_IND
(Section 2.3.1.3 of 5.2 Core Spec). 
```

### 対象をBT_GAP_ADV_TYPE_ADV_NONCONN_INDのみにする

サンプルプログラムを変更していきます。
まずは、`type`が`BT_GAP_ADV_TYPE_ADV_NONCONN_IND`のみを対象とし、接続を行わないようにします。

```main.c
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
			 struct net_buf_simple *ad)
{
	char addr_str[BT_ADDR_LE_STR_LEN];
	
	/* We're only interested in non-connectable events */
	if (type != BT_GAP_ADV_TYPE_ADV_NONCONN_IND) {
		return;
	}

	bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
	printk("Device found: %s (RSSI %d)\n", addr_str, rssi);

}
```

これを実行すると、`Tera Term`では、次のように表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/24287d64-ac54-9bbe-ce07-d8cf87f844a6.png)

### Complete 16-bit Service UUID Section - 0xFD6F
では、この中でCOCOAアプリのBLEデバイスは、どれでしょうか。それは、アドバタイジング・データの`Complete 16-bit Service UUID Section`が、`0xFD6F`であるBLEデバイスです[(仕様書 p.4)](https://www.blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf)。
`struct net_buf_simple *ad`引数の値を直接確認してもよいのですが、そのデータ構造を分解してくれるヘルパー関数`bt_data_parse()`がありますので、それを使います。

### COCOAアプリのBLEデバイスを表示

まずは、新たに`adv_data_found()`コールバック関数を追加し、アドバタイジング・データの`Complete 16-bit Service UUID Section`が、`0xFD6F`であるかどうかを判定します。

| # | 引数                  | 説明 |
|---|----------------------|------|
| 1 | struct bt_data *data | 分解されたアドバタイジング・データ  |
| 2 | void *user_data      | 呼び出し元とやり取りできる参照変数（ポインタ） |

```main.c
static bool adv_data_found(struct bt_data *data, void *user_data)
{
	int *hit = (int *)user_data;
	if(data->type == BT_DATA_UUID16_ALL) {
	   if ((data->data[0] == 0x6F) && (data->data[1] == 0xFD)) {
		   // Complete 16-bit Service UUID Section — The UUID is 0xFD6F
		   *hit=1;
	   }
	   return false;
	}
	return true;
}
```

そして、`device_found()`関数では、`bt_data_parse()`ヘルパー関数で、`struct net_buf_simple *ad`引数の内容を分解し、`adv_data_found()`コールバック関数で判定させます。
`hit`が1であれば(0でなければ)、BLEデバイス情報を表示します。

```main.c
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
			 struct net_buf_simple *ad)
{
	char addr_str[BT_ADDR_LE_STR_LEN];
	int hit = 0;

	/* We're only interested in non-connectable events */
	if (type != BT_GAP_ADV_TYPE_ADV_NONCONN_IND) {
		return;
	}
	bt_data_parse(ad, adv_data_found, &hit);
	if (hit) {
		bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
		printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
	}
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/78468374-1f29-b867-f753-68e7fe4ec795.png)

## アドレスの変化

数分放置していると異なるアドレスが取得されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/91338f06-9be1-5f12-7a39-01d8da5aca5e.png)


これは、[仕様書(p.5)](https://www.blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf)にあるように、ランダムで、10分から20分で変更されるアドレスだからです。近くに複数のCOCOAアプリが存在すると、どのアドレスが、どのデバイスのものかは、他のユーザーには、わかりません。わかるのは、自分自身のアプリと感染者登録した際のサーバーだけのようです。

```Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf
• The advertiser address type shall be Random Non-resolvable.
• On platforms supporting the Bluetooth Random Private Address with a randomized rotation timeout interval, the
advertiser address rotation period shall be a random value that is greater than 10 minutes and less than 20 minutes. 
```

この仕様を実際にmicro:bitを動かして、確認していただければ、個人情報だのプライバシーだのに関しての安全性について、安心していただけるかと思います。

**理解できない方は、ごめんなさい。ITを活用せずに、マスクと感で、感染拡大防止に努め、政府に苦言を発信していてください。**

```
中西氏はこう指摘する。

「このアプリも誰もが使うものですから、何より使いやすく、使う人の心理的なハードルの低いものにしないといけません。
　　日本人はプライバシーについて敏感なので、その部分の不安を残したままでは利用者は
　　　なかなか増えないことは目に見えています。この不安を解消し、アプリを役立つものにしていくためには、
　　　　政府が国民に向かってきちんとプライバシー保護に関して説明しないといけません。
　　　　　『個人情報は絶対に目的以外で使用しないので、どうぞアプリを使ってください』と
　　　　　　強くお願いするべきなのに、マスコミからの追及を気にしているのか、政府の腰が引けている気がしてなりません。
　　　　　　　アプリをみんなに使ってもらおうという積極的な姿勢が感じられないのです」
```

引用：https://news.yahoo.co.jp/articles/8ed9df99680526c924cb0e9d31a37eadae83a53b

#【驚愕のド素人開発 - 3日目】

micro:bitによるCOCOA探知器のプロトタイプは、サンプルプログラムを修正するだけでできました。
ここからは、エラー処理とか、流儀とかは、気にせずに開発に進みましょう。

## 外観のデザイン
探知器といえば、ドラゴンボールレーダーですかね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/69c7f2cf-3f48-f6c4-632e-11a097e7eedb.png)
出典： http://manekineko-k.com/blog/wp03/wp-content/uploads/2013/09/doragon.gif

## アプリの仕様

|項目|内容|備考|
|---|---|---|
| 名称 |COCOA探知器||
| デバイス |BBC micro:bit||
| 開発環境 |Zephyr|Windows 10|
| 対象年齢 |5歳以上||
| 受信データ仕様 |Exposure Notification|スキャンのみ|
| 強度 |rssi値|閾値:-90|
| LED表示 |10秒間 アニメーション、5秒間 検知結果表示|繰り返し|
| 検知結果表示 |検知したCOCOAアプリの数だけ、rssi>=-90なら中心から１列目の四角のドットを、rssi<-90なら2列目のドットを、ランダムな位置に重ならないように点灯させる。||


## ソースコード
主な修正点は、検知結果表示部分です。
`clear_dis()`関数で、検知結果変数をクリアし、`set_dis()`関数で、`addr`と`rssi`をもとに、検知結果変数の表示位置に設定し、`disp_dis()`関数で、検知結果変数の内容を表示しています。

```main.c
/* main.c - Application main entry point */

#include <zephyr.h>
#include <sys/printk.h>
#include <sys/byteorder.h>
#include <sys/time_units.h>
#include <power/reboot.h>

#include <bluetooth/bluetooth.h>

#include <display/mb_display.h>

static const struct mb_image radar[] = {
	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 }),
	MB_IMAGE(
		{ 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0 }),
};

static const struct mb_image ng[] = {
	MB_IMAGE(
		{ 1, 0, 0, 0, 1 },
		{ 0, 1, 0, 1, 0 },
		{ 0, 0, 1, 0, 0 },
		{ 0, 1, 0, 1, 0 },
		{ 1, 0, 0, 0, 1 }),
	MB_IMAGE(
		{ 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0 }),
};

struct disp_led {
	const int rowindex;
	const int colbit;
	int on;
};

static struct disp_led dis1[] = {
	{1, BIT(1), 0},
	{1, BIT(2), 0},
	{1, BIT(3), 0},
	{2, BIT(1), 0},
	{2, BIT(3), 0},
	{3, BIT(1), 0},
	{3, BIT(2), 0},
	{3, BIT(3), 0},
};
static const int dis1_size = ARRAY_SIZE(dis1);

static struct disp_led dis2[] = {
	{0, BIT(0), 0},
	{0, BIT(1), 0},
	{0, BIT(2), 0},
	{0, BIT(3), 0},
	{0, BIT(4), 0},
	{1, BIT(0), 0},
	{1, BIT(4), 0},
	{2, BIT(0), 0},
	{2, BIT(4), 0},
	{3, BIT(0), 0},
	{3, BIT(4), 0},
	{4, BIT(0), 0},
	{4, BIT(1), 0},
	{4, BIT(2), 0},
	{4, BIT(3), 0},
	{4, BIT(4), 0},
};
static const int dis2_size = ARRAY_SIZE(dis2);

static void disp_animate(int32_t duration, const struct mb_image *img, uint8_t img_count, uint8_t loop)
{
	struct mb_display *disp = mb_display_get();
	for(uint8_t j=0;j<loop;j++){
		for(uint8_t i=0;i<img_count;i++)
		{
			mb_display_image(disp, MB_DISPLAY_MODE_SINGLE, SYS_FOREVER_MS,
				&img[i], 1);
			k_sleep(K_MSEC(duration));
		}
	}
	mb_display_stop(disp);
}

static void clear_dis()
{
	for(int i=0;i<dis1_size;i++){
		dis1[i].on = 0;
	}
	for(int i=0;i<dis2_size;i++){
		dis2[i].on = 0;
	}
}

static void set_dis(const bt_addr_le_t *addr, int8_t rssi )
{
	if (rssi>=-90){
		int h = (addr->a.val[0]) % dis1_size;
		for(int i=0;i<dis1_size;i++){
			int idx = (i+h)%dis1_size;
			if (dis1[idx].on){
				continue;
			}
			dis1[idx].on = 1;
			return;
		}
	}
	int h = (addr->a.val[0]) % dis2_size;
	for(int i=0;i<dis2_size;i++){
		int idx = (i+h)%dis2_size;
		if (dis2[idx].on){
			continue;
		}
		dis2[idx].on = 1;
		return;
	}
}

static void disp_dis()
{
	struct mb_image hits[] = {
		{},
		{},
	};
	for(int i=0;i<dis1_size;i++){
		if (dis1[i].on){
			hits[0].row[dis1[i].rowindex] |= dis1[i].colbit;
		}
	}
	for(int i=0;i<dis2_size;i++){
		if (dis2[i].on){
			hits[0].row[dis2[i].rowindex] |= dis2[i].colbit;
		}
	}
	disp_animate(250, hits, ARRAY_SIZE(hits), 10);
}

static bool adv_data_found(struct bt_data *data, void *user_data)
{
    int *hit = (int *)user_data;
    if(data->type == BT_DATA_UUID16_ALL) {
       if (sys_get_le16(data->data) == 0xFD6F) {
           // Complete 16-bit Service UUID Section — The UUID is 0xFD6F
           *hit=1;
       }
       return false;
    }
    return true;
}

int count;

static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
			 struct net_buf_simple *ad)
{
	char addr_str[BT_ADDR_LE_STR_LEN];
	int hit;
	hit=0;
	bt_data_parse(ad, adv_data_found, &hit);
	if (hit) {
		count++;
		bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
		printk("[%d] COCOA found: %s (RSSI %d)\n", count, addr_str, rssi);
		set_dis(addr, rssi);
	}
}

void main(void)
{
	int err;
	
	err = bt_enable(NULL);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}
	printk("Bluetooth initialized\n");
	
	clear_dis();
	
	count = 0;
	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
	if (err) {
		printk("Scanning failed to start (err %d)\n", err);
		disp_animate(500, ng, ARRAY_SIZE(ng), 5);
	} else {
		printk("Scanning successfully started\n");
		disp_animate(200, radar, ARRAY_SIZE(radar), 10);
		
		bt_le_scan_stop();		
		printk("Stop Scanning: %d\n", count);
		
		disp_dis();
	}
	sys_reboot(SYS_REBOOT_WARM);
}
```

```prj.conf
CONFIG_ISR_STACK_SIZE=1024

CONFIG_BT=y
CONFIG_BT_CENTRAL=y

CONFIG_GPIO=y
CONFIG_DISPLAY=y
CONFIG_MICROBIT_DISPLAY=y

CONFIG_REBOOT=y
```

## 実証実験

実際に、電車に乗って、車内でスキャンしてみました。そしたら、真っ赤になったので、こんなにCOCOAがインストールされているのかと疑問になりました。導入率は、20%にも達していないはずですので。

そこで、ログを確認したところ、次のようにスキャンされていることがわかりました。

```
[1] COCOA found: 09:80:9c:85:e4:0f (random) (RSSI -66)
[2] COCOA found: 07:26:f9:77:05:1e (random) (RSSI -62)
[3] COCOA found: 32:b3:df:32:61:8e (random) (RSSI -77)
[4] COCOA found: 14:fc:d8:46:cf:17 (random) (RSSI -62)
[5] COCOA found: 09:80:9c:85:e4:0f (random) (RSSI -66)
[6] COCOA found: 4b:69:4d:4d:f2:19 (random) (RSSI -55)
[7] COCOA found: 07:26:f9:77:05:1e (random) (RSSI -63)
[8] COCOA found: 32:b3:df:32:61:8e (random) (RSSI -71)
[9] COCOA found: 14:fc:d8:46:cf:17 (random) (RSSI -64)
[10] COCOA found: 09:80:9c:85:e4:0f (random) (RSSI -60)
[11] COCOA found: 4b:69:4d:4d:f2:19 (random) (RSSI -53)
[12] COCOA found: 07:26:f9:77:05:1e (random) (RSSI -64)
[13] COCOA found: 32:b3:df:32:61:8e (random) (RSSI -71)
[14] COCOA found: 14:fc:d8:46:cf:17 (random) (RSSI -60)
[15] COCOA found: 09:80:9c:85:e4:0f (random) (RSSI -59)
[16] COCOA found: 4b:69:4d:4d:f2:19 (random) (RSSI -53)
[17] COCOA found: 07:26:f9:77:05:1e (random) (RSSI -65)
[18] COCOA found: 32:b3:df:32:61:8e (random) (RSSI -73)
[19] COCOA found: 14:fc:d8:46:cf:17 (random) (RSSI -60)
[20] COCOA found: 09:80:9c:85:e4:0f (random) (RSSI -58)
[21] COCOA found: 4b:69:4d:4d:f2:19 (random) (RSSI -55)
[22] COCOA found: 07:26:f9:77:05:1e (random) (RSSI -65)
[23] COCOA found: 32:b3:df:32:61:8e (random) (RSSI -71)
[24] COCOA found: 09:80:9c:85:e4:0f (random) (RSSI -58)
[25] COCOA found: 14:fc:d8:46:cf:17 (random) (RSSI -60)
[26] COCOA found: 07:26:f9:77:05:1e (random) (RSSI -69)
[27] COCOA found: 4b:69:4d:4d:f2:19 (random) (RSSI -55)
[28] COCOA found: 32:b3:df:32:61:8e (random) (RSSI -71)
[29] COCOA found: 09:80:9c:85:e4:0f (random) (RSSI -58)
[30] COCOA found: 14:fc:d8:46:cf:17 (random) (RSSI -60)
[31] COCOA found: 07:26:f9:77:05:1e (random) (RSSI -71)
[32] COCOA found: 4b:69:4d:4d:f2:19 (random) (RSSI -55)
[33] COCOA found: 32:b3:df:32:61:8e (random) (RSSI -71)
[34] COCOA found: 14:fc:d8:46:cf:17 (random) (RSSI -60)
[35] COCOA found: 07:26:f9:77:05:1e (random) (RSSI -71)
[36] COCOA found: 09:80:9c:85:e4:0f (random) (RSSI -62)
[37] COCOA found: 4b:69:4d:4d:f2:19 (random) (RSSI -53)
[38] COCOA found: 32:b3:df:32:61:8e (random) (RSSI -72)
```

1回のスキャンで、同一デバイスを何度も見つけてしまうようです。集計すると、実際には５つでした。改良の余地がありますね。

```
07:26:f9:77:05:1e	8
09:80:9c:85:e4:0f	8
14:fc:d8:46:cf:17	7
32:b3:df:32:61:8e	8
4b:69:4d:4d:f2:19	7
```


# おわりに

* [新型コロナウイルス接触確認アプリ（COCOA) COVID-19 Contact-Confirming Application](https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/cocoa_00138.html)のしくみと、[Bluetooth Low Energy (BLE) の Exposure Notification プロファイル（サービス）の仕様書](https://www.blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf)を確認しました。
* micro:bitの開発環境（フレームワーク）としてZephyrを選定し、サンプルプログラムのCentral(samples\bluetooth\central)の動作確認を行いました。
* サンプルプログラムを元に、デバイスのスキャンの中で、アドバタイジング・データからExposure Notification サービスを検知できるように改造しました。
* LEDディスプレイでアニメーションを行いながら、COCOAアプリの検知（スキャン）を行い、検知結果をドット数で表示するようにしました。
* 10秒間のスキャンで、同一デバイスを複数回検出してしまうことを実証実験で確認しました。改良の余地があります。

# おまけ - 小学4年生の子どもとお父さんの会話

子「お父さん、完成したね。」
父「なんとかできたね。電波は目に見えないから、こういうツールがあると、目に見えるからちょっとうれしいよね。」
子「近くにCOCOAが何人いるかわかるからね。」
父「どんなことに役立てられると思う？」
子「役に立たない！」
父「・・・」

子「でも、しくみがわかった気がする。」
父「どういうところ？」
子「ずっとスキャンしていると、そのうち、番号が変わるところ。」

父「どうして変わるのかな？」
子「番号をランダムにして、どのCOCOAかわかりにくいようにするためだよね。」
父「正解！普通は、固定の番号が割り当てられているけど、COCOAは、10分から20分で変化するランダムな番号を発信しているみたいだね。しかも、デバイスを特定するための情報や濃厚接触を判断するための情報は暗号化されているから、他の人には全く分からないんだよ。暗号化された情報には、個人情報や位置情報も含んでいないしね。」

子「マスクはみんなしてるけど、COCOAはあまり入れていないね。」
父「あー、個人情報だのプライバシーだのと言い訳をする人がいるからね。[‘政府の“デジタル音痴”が止まらない‘](https://news.yahoo.co.jp/articles/8ed9df99680526c924cb0e9d31a37eadae83a53b)って記事があるけど、経済活動を再開したい経団連のトップの方でさえも、あまり理解できていないんじゃないのかな。」
子「micro:bitで作って、しくみを理解すればいいじゃん。」
父「小学生向けのmicro:bitなら、だれでも入手可能だし、オープンソースで全部できるしね。これからの時代、トップの方には、そろそろ、デジタル音痴を解消して欲しいよね。」

子「でも、LEDが止まるときがあるよ。」
父「今回、開発に利用したZephyrの不具合なのかな。Zephyrはオープンソースだから、その内部まで踏み込んだテストをしたいね。」
父「[COVID-19 Radar Japan](https://github.com/Covid-19Radar/Covid19Radar)というオープンソースをベースに開発されているCOCOAでも、初歩的と言われる不具合があって、iPhoneのアプリでは、エラーで落ち続けちゃうらしくって、COCOAのことを、["驚愕のド素人開発"](https://cdn-ak.f.st-hatena.com/images/fotolife/s/simplearchitect/20200622/20200622073804.png)なんて表現する人も現れたんだよ。」
子「リセットすればいいじゃん。ゲームでもバグったらリセットする。」
父「iPhoneにはアプリを強制的にリセットする機能がないみたいだね。丸ごと全部をリセットするのは面倒だしね。」
子「そんなんも用意されていないなら、iPhoneも["驚愕のド素人開発"](https://cdn-ak.f.st-hatena.com/images/fotolife/s/simplearchitect/20200622/20200622073804.png)だね。」

～ おしまい ～

#(追記 2020/07/23) タイトルを良くして欲しい。

山崎 大作（日経メディカル）様へ
記事の内容はとても良いです。タイトルをもっと建設的な方向に持っていってほしいですね。


インタビュー◎世界経済フォーラム第四次産業革命日本センターの藤田卓仙氏に聞く
[接触確認アプリはこのままでは意義は少ない](https://medical.nikkeibp.co.jp/leaf/mem/pub/report/t344/202007/566484.html)

```
海外と比べても個人のプライバシーに配慮した形をとっており、位置情報や電話番号を取得しない他、基本的に個人を特定する情報を利用しないのが特徴で、接触者のデータはそれぞれのユーザーのスマホ内で管理している。
```

#(追記 2020/08/03) みんなが入れてるかを見える化しよう！

[内閣副大臣が語る｢コロナ接触確認アプリ“COCOA”｣をめぐる4つの事実
西田宗千佳 ITジャーナリスト
Aug. 03, 2020, 06:30 AM](https://www.businessinsider.jp/post-217766)

```
みなさんインストールすれば、安心が広がりますし、リスクもコントロールできるようになります。“みんな入れてるんだ”となれば、お店などに入りやすくなりますよね（平副大臣）
```

#(追記 2020/08/19) 「COCOA」から接触通知が！

実際のCOCOAアプリでもアプリそのものや、その運用に関する課題も少しずつ見えてきたようですね。
https://diamond.jp/articles/-/246083

【実録】コロナ接触確認アプリ「COCOA」から接触通知が！見えてきた課題とは
澤田 翔
ライフ・社会 ＤＯＬ特別レポート
2020.8.19 5:00


```
もっとも接触の通知を知るのに2日かかってしまったのは大きな課題だ。
```

[^1]: [コロナ接触確認アプリ「6割目標ではない」「義務化ない」、内閣府副大臣](https://news.yahoo.co.jp/articles/6bc0ac1c10b2db18d984729faef6bb5ff33ba8b0)
[^2]: [COCOA利用率が4割＋6割の外出自粛で感染半減——日本大学生産工学部がシミュレーション](https://k-tai.watch.impress.co.jp/docs/news/1268613.html)