PCとBLEデバイス間で無線通信してみる

#目次
・はじめに
・Windows10（フォームアプリケーション＿.NET Framework＿C#）の作成
・BLEデバイス（ESP32＿Arduino）の作成
・通信する
#はじめに
　WindowsでBLE（BluetoothLowEnergy）通信ができないかC#のフォームアプリを作ってみる。
　Windows10側はC#を使う。フォームアプリをVisualStudio2019で作成する。
　BLEデバイスはESP32とArduinoで作成する。
　双方向で数値を送受信する。Bluetoothは簡単そうだが、BLEではどうなるか。
　Windowsについては、フォームアプリのほかに適切なものがいろいろある（UWPというらしい…）のだが、これ以外詳しくないのでこれでやる。
　・【Notify】でのBLEデバイスからの不定期受信
　・【Write】でのBLEデバイスへの書き込み
ができた。実際の結果はこちら
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">WindowsとESP32の間で無線通信。BluetoothLowEnergy（BLE）による送受信をする。<br>Windows10（フォームアプリケーション）からESP32に対し、受信【Notify】と書込み【Write】ができるようになったぞ。<br>またQiitaにでも書こう。 <a href="https://t.co/16DX25msf6">pic.twitter.com/16DX25msf6</a></p>&mdash; いっちー (@get_itchy_feet) <a href="https://twitter.com/get_itchy_feet/status/1246827386882023427?ref_src=twsrc%5Etfw">April 5, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#Windows10（フォームアプリケーション_.NET Framework_C#）の作成
　まずは【Microsoft Visual Studio Community 2019】から新しいプロジェクト【Windowsフォームアプリケーション .NET Framework】を作成する。使用する言語は【C#】となる。
　BLEを扱えるようにするために【Microsoft.Windows.SDK.Contracts】というアドオンが必要みたいだ。下記のサイトを参考に入れてみる。
https://tomosoft.jp/design/?p=41123
　画面も簡単に作る。ラベル二つにボタンが一つ、それにタイマー。それぞれ以下の通り。
　　・ラベル：labelNotifyData　【Notify】による受信した数値を表示。数は二つ。
　　・ラベル：labelWriteData　書き込みを行う数値を表示。
　　・ボタン：buttonWrite　ボタンをクリックすると【Write】による書き込みを行う。
　　・タイマー：timer1　ラベルの文字の更新に使用する。intervalが100ms、enabledをtrueに。
　BLEでデバイスから送られる数値は二つ。1秒ごと送信され、一つ目は1つずつ値が増えていく、二つ目は【Write】により書き込んだ数値がそのまま返ってくる。これを【Notify】で受信する。
　BLEデバイス書き込みはボタンのクリックで行う。クリックすると【Write】で書き込みを行う。数値はボタンクリックごとに一つづつふえる。
　また、タイマーを用い100msごとにラベルの文字を更新する。
<img width=60% src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184089/85025f42-8406-2fc5-875f-e2d8c8ba6ea6.png>
　コードの書き方もいろんなところを参考にコピペして作り上げた。主に参考にしたのは以下のサイト
https://qiita.com/Dr10_TakeHiro/items/7446d68cbffeae7c7184
https://qiita.com/gebo/items/41da7474936845d77d06
　起動と同時にスキャンを行い、目的のBLEデバイスと接続する。
　相手側のBLEデバイスのUUIDは以下の通り
　　・BLEデバイスのサービスUUID　"00002220-0000-1000-8000-00805F9B34FB"
　　・【Write】のキャラクタリスティックUUID　"00002222-0000-1000-8000-00805F9B34FB"
　　・【Notify】のキャラクタリスティックUUID　"00002221-0000-1000-8000-00805F9B34FB"
　以下のようにコードを書いてみる。Tryとかはよくわからんから省略する。

```C#:FormsAppBleQiitaTest.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.Advertisement;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using System.Runtime.InteropServices.WindowsRuntime;        //Asbuffer

namespace FormsAppBleQiitaTest
{
    public partial class Form1 : Form
    {
        static BluetoothLEAdvertisementWatcher watcher;
        public Form1()
        {
            InitializeComponent();
            StartBle();                 //起動と同時にBleデバイスとの接続を試みる。
        }
        static public string textLog;
        static public string textWriteData;
        //ラベルの文字をタイマーで更新する
        private void timer1_Tick(object sender, EventArgs e)
        {
            labelNotifyData.Text = textLog;
            labelWriteData.Text = textWriteData;
        }
        //スキャン他　起動時に呼び出し
        public static async void StartBle()
        {
            watcher = new BluetoothLEAdvertisementWatcher();
            watcher.Received += Watcher_Received;
            watcher.ScanningMode = BluetoothLEScanningMode.Active;
            watcher.Start();
            textWriteData = "Write : 0";
        }
        static bool isBleFind = false;
        static bool isBleNotify = false;
        static bool isBleWrite = false;
        public static async void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
        {
            var bleServiceUUIDs = args.Advertisement.ServiceUuids;
            textLog = "MAC:" + args.BluetoothAddress.ToString();
            foreach (var uuidone in bleServiceUUIDs)
            {
                textLog = uuidone.ToString();
                if (uuidone.ToString() == "00002220-0000-1000-8000-00805f9b34fb")       //目的のサービスUUIDを探す
                {
                    textLog = "Find";
                    isBleFind = true;
                }
                if (isBleFind == true)
                {
                    watcher.Stop();       //目的のサービスUUIDをみつけたらスキャン終了
                    textLog = "Stop";
                    BluetoothLEDevice dev = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
                    GattDeviceService service = dev.GetGattService(new Guid("00002220-0000-1000-8000-00805f9b34fb"));
                    var characteristicsRx = service.GetCharacteristics(new Guid("00002221-0000-1000-8000-00805f9b34fb"));
                    CHARACTERISTIC_UUID_RX = characteristicsRx.First();
                    if (CHARACTERISTIC_UUID_RX.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify))
                    {
                        CHARACTERISTIC_UUID_RX.ValueChanged += characteristicBleDevice;
                        await CHARACTERISTIC_UUID_RX.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
                        isBleNotify = true;
                    }
                    var characteristicsTx = service.GetCharacteristics(new Guid("00002222-0000-1000-8000-00805f9b34fb"));
                    CHARACTERISTIC_UUID_TX = characteristicsTx.First();
                    if (CHARACTERISTIC_UUID_TX.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Write))
                    {
                        isBleWrite = true;
                    }
                    break;
                }
            }
        }
        public static byte[] RxData;
        public static GattCharacteristic CHARACTERISTIC_UUID_RX;
        public static GattCharacteristic CHARACTERISTIC_UUID_TX;
        //Notifyによる受信時の処理
        public static void characteristicBleDevice(GattCharacteristic sender, GattValueChangedEventArgs eventArgs)
        {
            byte[] data = new byte[eventArgs.CharacteristicValue.Length];
            Windows.Storage.Streams.DataReader.FromBuffer(eventArgs.CharacteristicValue).ReadBytes(data);
            RxData = data;
            textLog = "Notfy : " + RxData[0].ToString() + "," + RxData[1].ToString();
            return;
        }
        //終了時の処理
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (isBleFind == true) CHARACTERISTIC_UUID_RX.Service.Dispose();
        }
        //ボタンがクリックされたときの処理
        byte[] TXdata = { 0 };
        private void buttonWrite_Click(object sender, EventArgs e)
        {
            if (isBleWrite == true)
            {
                var result = CHARACTERISTIC_UUID_TX.WriteValueAsync(TXdata.AsBuffer());
                if (TXdata[0] < 100) TXdata[0]++;
                else TXdata[0] = 0;
                textWriteData = "Write : " + TXdata[0].ToString();
            }
        }
    }
}
```
　
#BLEデバイス（ESP32）の作成
　BLEデバイス側を用意する。
　ESP32はマルツパーツで【SP32-WROOM-32搭載開発ボード】を購入する。
https://www.marutsu.co.jp/pc/i/1525354/
　準備や使い方はいろいろネットに載っている。
　以下のようなコードを書き込む。サンプルコードを少しいじった程度だが。
　コードの内容は、
・1秒ごとにNotifyで送信する。送信する数値は二つ。
・数値のうち最初の一つは1秒ごとに一つづつ増えていく（1 → 2 → 3 ・・・）。
・もう一つはPCからWriteで受信した数値をそのまま返す。
・設定するUUIDはWindows側のコードの説明を参照
・デバイス名は「BleDevice」

```C++:BleQiita.ino

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;

int bleRX,bleTX;
uint8_t notifySend[2] = {0,0};

#define SERVICE_UUID           "00002220-0000-1000-8000-00805F9B34FB"
#define CHARACTERISTIC_UUID_RX "00002222-0000-1000-8000-00805F9B34FB"
#define CHARACTERISTIC_UUID_TX "00002221-0000-1000-8000-00805F9B34FB"

class MyServerCallbacks: public BLEServerCallbacks {
	void onConnect(BLEServer* pServer) {
		deviceConnected = true;
	};

	void onDisconnect(BLEServer* pServer) {
		deviceConnected = false;
	}
};

class MyCallbacks: public BLECharacteristicCallbacks {
	void onWrite(BLECharacteristic *pCharacteristic) {
		std::string rxValue = pCharacteristic->getValue();

		if (rxValue.length() > 0) {
			Serial.println("*********");
			Serial.print("Received Value: ");
			for (int i = 0; i < rxValue.length(); i++)
			Serial.print(rxValue[i]);

			Serial.println();
			Serial.println("rxValue[0]");

			bleRX = rxValue[0];
		}
	}
};

void setup() {
  Serial.begin(115200);

	// Create the BLE Device
	BLEDevice::init("BleDevice");

	// Create the BLE Server
	BLEServer *pServer = BLEDevice::createServer();
	pServer->setCallbacks(new MyServerCallbacks());

	// Create the BLE Service
	BLEService *pService = pServer->createService(SERVICE_UUID);

	// Create a BLE Characteristic
	pCharacteristic = pService->createCharacteristic(
		CHARACTERISTIC_UUID_TX,
		BLECharacteristic::PROPERTY_NOTIFY
	);

	pCharacteristic->addDescriptor(new BLE2902());

	BLECharacteristic *pCharacteristic = pService->createCharacteristic(
		CHARACTERISTIC_UUID_RX,
		BLECharacteristic::PROPERTY_WRITE
 	);

	pCharacteristic->setCallbacks(new MyCallbacks());

	// Start the service
	pService->start();

	// Start advertising
    BLEAdvertising *pAdvertising = pServer->getAdvertising();
    pAdvertising->addServiceUUID(SERVICE_UUID);
	pServer->getAdvertising()->start();
	Serial.println("Waiting a client connection to notify");
}

void loop() {
    // notify changed value
    if (deviceConnected) {
        if(bleRX < 100) bleTX++;
        else bleTX = 0;
        notifySend[0] = bleTX;
        notifySend[1] = bleRX;
        pCharacteristic->setValue(notifySend, 2);
        pCharacteristic->notify();
        delay(1000); 
　　// bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
    }
    // disconnecting
    if (!deviceConnected && oldDeviceConnected) {
        delay(500); // give the bluetooth stack the chance to get things ready
        pServer->startAdvertising(); // restart advertising
        Serial.println("start advertising");
        oldDeviceConnected = deviceConnected;
    }
    // connecting
    if (deviceConnected && !oldDeviceConnected) {
        // do stuff here on connecting
        oldDeviceConnected = deviceConnected;
    }
}
```

#通信する
　BLEデバイスの電源を入れ、Windows側でフォームアプリを起動させると送受信を始める。
　ただし、Windows側でデバイスのペアリングを済ませておく必要がある。
　【設定】→【Bluetoothとその他のデバイス】→【Bluetoothまたはその他のデバイスを追加する】→【Bluetooth】で「BleDevice」を追加する。

　なんとかできたようだ。