12
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-04-11

#目次
・はじめに
・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デバイスへの書き込み
ができた。実際の結果はこちら

#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ごとにラベルの文字を更新する。

 コードの書き方もいろんなところを参考にコピペして作り上げた。主に参考にしたのは以下のサイト
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とかはよくわからんから省略する。

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」

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」を追加する。

 なんとかできたようだ。

12
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?