LoginSignup
6
3

More than 3 years have passed since last update.

M5Stackのビーコン化に挑戦

Last updated at Posted at 2020-12-20

この記事は プロトアウトスタジオの アドベントカレンダー2020 の6日目の記事です!

やったこと

M5Stackのビーコン化に挑戦してみました。

もう少し説明しますと、
M5Stack Gray のジャイロセンサーの値を別の M5Stack Gray へBLE通信で送ってみました。
なお、Twitterで「BLT通信」とはつぶやいてるのにはご容赦ください。おそらくBLEとLTEが混ざったようです。サンドイッチでもありませんw



ついでに、電波強度(RSSI、TxPower)も取得して、距離を測ろうと試みしたが、以下のツイートのように正確な距離を測定することはできませんでした。
私のやり方がおかしいのか、周りの電波に影響されているのか分かりませんが、現状の私のスキルでは、ビーコンは正確な距離ではなく、アバウトに近いづいてきたかどうか判定するケースに使おうと思います。

とはいえ、せっかくここまでしましたので、備忘録をかねて、Qiitaに書きます。

なお、Twitterで今度は「LTE通信」とつぶやいてますね。通信のことをよく分かってないのがモロバレです。


発信(ビーコン)側

ビーコンについてのコードは、このサイトのコードをほぼそのまま使用しました。
M5StackとM5StickC間のarduino BLE通信

コード

BLEServer.ino

// <M5Stack.h>をincludeする前に、IMUモジュールを#defineしておく
#define M5STACK_MPU6886 
#include <Arduino.h>
#include <M5Stack.h>
#include <WiFi.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

uint16_t Dvalue = 0;

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

#define SERVICE_UUID        "7780" //適当なID(上のような長いUUIDだとうまく認識されなかったので適当に)
#define CHARACTERISTIC_UUID "7781" //適当なID

float gyroX ; //M5StackのジャイロセンサーX軸の値

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

void setup()
{
  M5.begin();
  M5.Power.begin();
  M5.IMU.Init();
  WiFi.mode(WIFI_OFF); 
  Serial.print("");
  // Create the BLE Device
  BLEDevice::init("M5STACK1G"); //適当な名前

  // Create the BLE Server
  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,
                      BLECharacteristic::PROPERTY_READ   |
                      BLECharacteristic::PROPERTY_WRITE  |
                      BLECharacteristic::PROPERTY_NOTIFY |
                      BLECharacteristic::PROPERTY_INDICATE
                    );

  // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
  // Create a BLE Descriptor
  pCharacteristic->addDescriptor(new BLE2902());

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

  // Start advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(false);
  pAdvertising->setMinPreferred(0x0);  // set value to 0x00 to not advertise this parameter
  BLEDevice::startAdvertising();

}

void loop()
{
  M5.update();
  uint8_t buf[2]; //要素数2

  M5.IMU.getGyroData(&gyroX); //ジャイロデータ取得

    M5.Lcd.clear(BLACK);
    M5.Lcd.setCursor(10, 0);
    M5.Lcd.setTextSize(5);
    M5.Lcd.setTextColor(YELLOW);
    M5.Lcd.println("GyroX:");
    M5.Lcd.setTextColor(RED);
    M5.Lcd.println(String(gyroX));
    Serial.println(gyroX);
    delay(300);
    Dvalue=gyroX; 

    if (deviceConnected) {
        memset(buf,0,sizeof buf);  
        buf[0]=(uint8_t)(Dvalue & 0xff);
        buf[1]=(uint8_t)((Dvalue >>8) & 0xff);
        // 数値は0~500少しまであるので、2バイト使います

        pCharacteristic->setValue(buf,sizeof buf);
        pCharacteristic->notify();
        value++;
        delay(3); // 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
        oldDeviceConnected = deviceConnected;
    }
    // connecting
    if (deviceConnected && !oldDeviceConnected) {
        // do stuff here on connecting
        oldDeviceConnected = deviceConnected;
    }
}

受信側

ビーコンについてのコードは、発信側と同じく、こちらのサイトのコードをほぼそのまま使用しました。
M5StackとM5StickC間のarduino BLE通信

RSSI値とTXPower値は、BLEAdvertisedDevice の getRSSI() 、getTXPower()で呼び出しで取得できます。以下のサイトを参考にしました。
M5StackでBLEタグのRSSIを取得する
M5StickC非公式日本語リファレンス

ビーコンの距離についてはこちらのサイトを参考にしました
RSSI と TxPower からビーコンとの距離および近接度(Proximity)を推定する

コード

BLECLient.ino
#include <Arduino.h>
#include <M5Stack.h>
#include "BLEDevice.h"

// The remote service we wish to connect to.
static BLEUUID serviceUUID("7780");
// The characteristic of the remote service we are interested in.
static BLEUUID    charUUID("7781");

static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;

uint16_t val; 

static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,  uint8_t* pData,  size_t length,  bool isNotify) {
    val=0;
    val=(uint16_t)(pData[1]<<8 | pData[0]); //ペリフェラルから受信した値(2バイト)を数値に変換
    Serial.println(val);

}

class MyClientCallback : public BLEClientCallbacks {
  void onConnect(BLEClient* pclient) {
  }

  void onDisconnect(BLEClient* pclient) {
    connected = false;
    Serial.println("onDisconnect");
  }
};

bool connectToServer() {
    Serial.print("Forming a connection to ");
    Serial.println(myDevice->getAddress().toString().c_str());

    BLEClient*  pClient  = BLEDevice::createClient();
    Serial.println(" - Created client");

    pClient->setClientCallbacks(new MyClientCallback());

    // Connect to the remove BLE Server.
    pClient->connect(myDevice);  // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
    Serial.println(" - Connected to server");

    // Obtain a reference to the service we are after in the remote BLE server.
    BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
    if (pRemoteService == nullptr) {
      Serial.print("Failed to find our service UUID: ");
      Serial.println(serviceUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    Serial.println(" - Found our service");

    // Obtain a reference to the characteristic in the service of the remote BLE server.
    pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
    if (pRemoteCharacteristic == nullptr) {
      Serial.print("Failed to find our characteristic UUID: ");
      Serial.println(charUUID.toString().c_str());
      pClient->disconnect();
      return false;
    }
    Serial.println(" - Found our characteristic");

    // Read the value of the characteristic.
    if(pRemoteCharacteristic->canRead()) {
      //サンプルだと下の行を実行するタイミングでリブートかかるのでコメント化。
      //notifyCallbackのほうでCharacteristicから取得するように変更
      //std::string value = pRemoteCharacteristic->readValue();
      //Serial.print("The characteristic value was: ");
      //Serial.println(value.c_str());
    }

    if(pRemoteCharacteristic->canNotify())
      pRemoteCharacteristic->registerForNotify(notifyCallback);

    connected = true;

}
/**
 * Scan for BLE servers and find the first one that advertises the service we are looking for.
 */
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
 /**
   * Called for each advertising BLE server.
   */
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("BLE Advertised Device found: ");
    Serial.println(advertisedDevice.toString().c_str());
    Serial.println(advertisedDevice.getRSSI());
    Serial.println(advertisedDevice.getTXPower());

    M5.Lcd.setTextSize(2);
    M5.Lcd.setTextColor(WHITE);

    //M5.Lcd.print(advertisedDevice.getName().c_str());
    M5.Lcd.print("RSSI:");
    M5.Lcd.print(advertisedDevice.getRSSI());

    M5.Lcd.print(" / TxPower:");
    M5.Lcd.println(advertisedDevice.getTXPower());

    float RSSI = advertisedDevice.getRSSI();
    float tx = advertisedDevice.getTXPower();
    //距離を求めてるつもりだけど自信なし
    float distance = pow(10.0, (tx - RSSI) / 20.0); 

    M5.Lcd.setTextColor(YELLOW);
    M5.Lcd.print("Distance:");
    M5.Lcd.println(distance);

    // We have found a device, let us now see if it contains the service we are looking for.

    if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {

      BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect = true;
      doScan = true;

    } // Found our server
  } // onResult
}; // MyAdvertisedDeviceCallbacks


void setup() {
  M5.begin();
  Serial.begin(1500000);
  Serial.println("Starting Arduino BLE Client application...");
  BLEDevice::init("");

  // Retrieve a Scanner and set the callback we want to use to be informed when we
  // have detected a new device.  Specify that we want active scanning and start the
  // scan to run for 5 seconds.
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());

  pBLEScan->setInterval(1349);
  pBLEScan->setWindow(449);
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5, false);
} // End of setup.


// This is the Arduino main loop function.
void loop() {

  M5.Lcd.fillScreen(BLACK); //この辺の表示は接続確認後、にしたほうがいいかも
  M5.Lcd.setCursor(10, 0);
  M5.Lcd.setTextSize(3);
  M5.Lcd.setTextColor(YELLOW);
  M5.Lcd.print("GyroX:");
  M5.Lcd.setTextColor(RED);   
  M5.Lcd.println(val);


  // If the flag "doConnect" is true then we have scanned for and found the desired
  // BLE Server with which we wish to connect.  Now we connect to it.  Once we are 
  // connected we set the connected flag to be true.
  if (doConnect == true) {
    if (connectToServer()) {
      Serial.println("We are now connected to the BLE Server.");
    } else {
      Serial.println("We have failed to connect to the server; there is nothin more we will do.");
    }
    doConnect = false;
  }

  // If we are connected to a peer BLE Server, update the characteristic each time we are reached
  // with the current time since boot.
  if (connected) {
    String newValue = "Time since boot: " + String(millis()/1000);
    Serial.println("Setting new characteristic value to \"" + newValue + "\"");

    M5.Lcd.setTextSize(1);
    M5.Lcd.setTextColor(BLUE);
    M5.Lcd.print("newValue:");
    M5.Lcd.println(newValue);

    // Set the characteristic's value to be the array of bytes that is actually a string.
    pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());


  }else if(doScan){
    BLEDevice::getScan()->start(0);  // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino
  }

  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->start(1);

  delay(1000); // Delay a second between loops.
}
6
3
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
6
3