Wio Terminalの加速度センサ値をMATLABでリアルタイム可視化する

# はじめに
タイトルの通り、Wio Terminalの加速度センサ値を、MATLABに送ってそれをリアルタイムに可視化するやり方についてです。Wio TerminalとMATLABの入ったPCの間はBLEで接続し、ワイヤレスで動作します。

元ネタはSeeed公式サイトの[こちら](https://wiki.seeedstudio.com/Wio-Terminal-Web-Bluetooth/#visualizing-accelerator-data-on-web-site-using-web-bluetooth-apis)で、もともとPC側のアプリケーションがHTMLだったのをMATLABに記述しなおしました。

## こんな感じの動きをします
![wio_accelerometor_movie.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701220/905ca579-6f3d-e979-c057-4fda676d821d.gif)

# Wio Terminalのセットアップ

Wio TerminalでBluetooth接続するにあたっては、ワイヤレス通信用CPU（RTL8720DN）のファームウェアアップデートを行う必要があります。公式ページは[こちら](https://wiki.seeedstudio.com/Wio-Terminal-Bluetooth-Overview/#update-seeed-samd-arduinocores)で、日本語記事は[こちら](https://qiita.com/infinite1oop/items/47235ce4a50e24312ae9)が参考になります。


# コード

### MATLAB側
MATLAB側コードを実行する前に、Wio Terminal側にファームウェアを書き込んだうえで電源を入れておいてください。
Wio Terminal側と問題なく接続できれば、
- Scanning for devices...
- Connecting to device...
- Receiving data...

のメッセージが出た後、加速度センサ値のグラフが表示されます。

ちなみに、ChatGPTで公式のhtmlからMATLABに変換させました。3，4か所ほどの手直しでかなり簡単にできました。
```MATLAB:ble_accel_freerun.m
% Bluetooth Accelerometer Plotter for Wio Terminal

% 1. Upload the sketch to your Wio Terminal
% 2. Run this MATLAB script

clear;
close;
% Set up BLE
serviceUuid = "19B10000-E8F2-537E-4F6C-D104768A1214";
characteristicUuid = "19B10010-E8F2-537E-4F6C-D104768A1214";

% Connect to BLE device
disp("Scanning for devices...");
devices = blelist;
device = [];
numDevices = size(devices, 1);
for i = 1:numDevices
    if any(strcmp(serviceUuid, devices.Advertisement(i).CompleteServiceUUIDs))
        device = devices(i, :);
        break;
    end
end

if isempty(device)
    error("No compatible device found.");
end

disp("Connecting to device...");
b = ble(device.Address{1});
c = characteristic(b, serviceUuid, characteristicUuid);

% Set up plot
xyz_color = ['r','g','b'];
figure;
for i = 1:3
    h(i) = animatedline('Marker', 'o', 'Color', xyz_color(i), 'LineStyle', 'none');
end

title('Bluetooth Accelerometer Plotter for Wio Terminal');
xlabel('Time');
ylabel('Acceleration');
legend('X', 'Y', 'Z');
axis([0, 100, -2, 2]);
grid on;
dataPoints = 100;
accelData = zeros(dataPoints, 3);

% Read data and update plot
disp("Receiving data...");
tic;
i = 0;
while isvalid(b)
    i = i + 1;
    value = read(c, "latest");
    accel = sscanf(native2unicode(value), "%f|%f|%f");
    
    % Update data buffer
    if i <= dataPoints
        accelData(i, :) = accel';
    else
        accelData = [accelData(2:end, :); accel'];
    end
    
    % Update plot
    for j = 1:3
        clearpoints(h(j));
        addpoints(h(j), 1:size(accelData, 1), accelData(:, j));
    end
    drawnow limitrate;
end
```

### Wio Terminal側
Arduino IDEを使う場合には、適当にコードをIDEにコピペして使ってください。
ほぼ[公式](https://wiki.seeedstudio.com/Wio-Terminal-Web-Bluetooth#arduino-set-up-1)のサンプルコードのままですが、画面に加速度センサ値を表示するところだけ追加しました。

[こちら](https://qiita.com/infinite1oop/items/47235ce4a50e24312ae9)の記事にある通り、「Seeed_Arduino_rpcBLE」、「Seeed_Arduino_rpcUnified」などのライブラリが必要です。

```cpp:main.cpp
// Sample code to send Accelerometer values via Bluetooth.
// Original↓
// https://wiki.seeedstudio.com/Wio-Terminal-Web-Bluetooth/#arduino-set-up-1

#include <rpcBLEDevice.h>
#include <BLEServer.h>
#include <LIS3DHTR.h>
 
#define accelerometerService "19b10000-e8f2-537e-4f6c-d104768a1214"
#define firstCharacteristic  "19b10010-e8f2-537e-4f6c-d104768a1214"
#define DESCRIPTOR_UUID      "19b10010"
 
LIS3DHTR<TwoWire> lis;

#include"TFT_eSPI.h"
TFT_eSPI tft;
 
bool deviceConnected = false;
bool oldDeviceConnected = false;
 
BLEServer *pServer = NULL;
BLECharacteristic * pCharacteristic;
 
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      Serial.println("MyServerCallbacks onConnect ");
      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("*********");
      }
    }
};
 
void setup() {
  Serial.begin(115200);
//  while(!Serial){};
 
  lis.begin(Wire1);
  if (!lis) {
    Serial.println("Accelerater Error!");
    while(1);
  }
  lis.setOutputDataRate(LIS3DHTR_DATARATE_25HZ); //Data output rate
  lis.setFullScaleRange(LIS3DHTR_RANGE_2G); //Scale range set to 2g
  Serial.println("Accelerater Initialised!");
 
  Serial.println("Starting BLE work!");
 
  BLEDevice::init("Accelerometer");
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());
 
  BLEService *pService = pServer->createService(accelerometerService);
  pCharacteristic = pService->createCharacteristic(
                                         firstCharacteristic,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );
  pCharacteristic->setAccessPermissions(GATT_PERM_READ | GATT_PERM_WRITE);
  BLEDescriptor *pDescriptor = pCharacteristic->createDescriptor(
                                         DESCRIPTOR_UUID,
                                          ATTRIB_FLAG_VOID | ATTRIB_FLAG_ASCII_Z,
                                         GATT_PERM_READ | GATT_PERM_WRITE,
                                         2
                                         );
  pCharacteristic->setCallbacks(new MyCallbacks());
  pService->start();
 
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(accelerometerService);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
  Serial.println("Characteristic defined! Now you can read it in your phone!");

  // setup for display
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(TFT_BLACK); //Black background
  tft.setTextColor(TFT_WHITE, TFT_BLACK); //White text color 
}
 
void loop() { 
  if (deviceConnected) {
    updateAcceleration();
  }
      // 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;
    }
}
 
void updateAcceleration() {
  float x_values, y_values, z_values;
  x_values = lis.getAccelerationX();
  y_values = lis.getAccelerationY();
  z_values = lis.getAccelerationZ();
 
  String accelerometerData = String(x_values)+"|"+String(y_values)+"|"+String(z_values);
  Serial.println(accelerometerData);
  pCharacteristic->setValue(accelerometerData.c_str());
  pCharacteristic->notify();

  // show xyz values on the display
  char x_str[100], y_str[100], z_str[100];
  dtostrf(x_values, 6, 2, x_str);
  dtostrf(y_values, 6, 2, y_str);
  dtostrf(z_values, 6, 2, z_str);

  tft.drawString(x_str, 70, 60, 6);
  tft.drawString(y_str, 70,110, 6);
  tft.drawString(z_str, 70,160, 6);
  
  delay(20);
}
```

# おわりに
Wio Terminalの加速度センサ値を、MATLABでリアルタイム可視化するやり方について書きました。間違い等ありましたらコメントお願いします。

Wio Terminal、最近あまり見かけなくなりましたが私は好きです。画面でかくてボタン多いところとか。
しばしばセールで4000円台で売ってたりするので、またブームになって欲しいです。