1
1

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 1 year has passed since last update.

micro:bitのジャイロセンサーをBLEで読み取り、C#へ渡す

Last updated at Posted at 2023-05-11

処理フロー

[MakeCode]
micro:bitに内蔵されているジャイロセンサーの値を、BLE(Bluetooth Low Energy)を使用してPCへ送信します.

[JavaScript]
接続処理をし、受け取ったセンサー値をクリップボードに書き込みます.

[C#]
クリップボードからセンサー値を読み取り、画面上のキャラクターを操作します.

言語間の値の受け渡し方法に悩んだのですが、"クリップボードを経由する"という無理やりな方法で実行しています.

MakeCode

センサーの値をBluetoothで送信するだけですので、非常にシンプルなコードになっています.

screenshot

MakeCodeのJavaScript版のコードは以下です.

bluetooth.onBluetoothConnected(function () {
    basic.showIcon(IconNames.Yes)
})
bluetooth.onBluetoothDisconnected(function () {
    basic.showIcon(IconNames.No)
})
input.onButtonPressed(Button.A, function () {
    basic.showString("  ")
    basic.showString(control.deviceName())
    basic.showIcon(IconNames.Square)
})
basic.showIcon(IconNames.Square)
bluetooth.startTemperatureService()
bluetooth.startLEDService()
bluetooth.startUartService()
basic.forever(function () {
    bluetooth.uartWriteNumber(input.rotation(Rotation.Roll))
})

JavaScript

BLE接続・センサー値読み取り・クリップボードへの書き込み処理.
ネットにはmicro:bitとPCが1対1のサンプルしかなかったため、4対1で対応させるのに苦労しました.

<script>
const microbit1 = new BlueJelly();
const microbit2 = new BlueJelly();
const microbit3 = new BlueJelly();
const microbit4 = new BlueJelly();

window.onload = function () {
  microbit1.setUUID("micro:bit1", BlueJelly.MICROBIT_UART_SERVICE, BlueJelly.MICROBIT_TX_CHARACTERISTIC);
  microbit2.setUUID("micro:bit2", BlueJelly.MICROBIT_UART_SERVICE, BlueJelly.MICROBIT_TX_CHARACTERISTIC);
  microbit3.setUUID("micro:bit3", BlueJelly.MICROBIT_UART_SERVICE, BlueJelly.MICROBIT_TX_CHARACTERISTIC);
  microbit4.setUUID("micro:bit4", BlueJelly.MICROBIT_UART_SERVICE, BlueJelly.MICROBIT_TX_CHARACTERISTIC);
}

microbit1.onScan = function (deviceName) {
  document.getElementById('status').innerHTML = deviceName;
}
microbit1.onRead = function (data){
  let value = "";
  for(let i = 0; i < data.byteLength; i++){
    value = value + String.fromCharCode(data.getInt8(i));
  }
  value = Number(value);
  console.log(value);
  document.getElementById('data_text').innerHTML = value;
}

microbit2.onScan = function (deviceName) {
  document.getElementById('status2').innerHTML = deviceName;
}
microbit2.onRead = function (data){
  let value = "";
  for(let i = 0; i < data.byteLength; i++){
    value = value + String.fromCharCode(data.getInt8(i));
  }
  value = Number(value);
  console.log(value);
  document.getElementById('data_text2').innerHTML = value;
}

microbit3.onScan = function (deviceName) {
  document.getElementById('status3').innerHTML = deviceName;
}
microbit3.onRead = function (data, uuid){
  let value = "";
  for(let i = 0; i < data.byteLength; i++){
    value = value + String.fromCharCode(data.getInt8(i));
  }
  value = Number(value);
  console.log(value);
  document.getElementById('data_text3').innerHTML = value;
}

microbit4.onScan = function (deviceName) {
  document.getElementById('status4').innerHTML = deviceName;
}
microbit4.onRead = function (data, uuid){
  let value = "";
  for(let i = 0; i < data.byteLength; i++){
    value = value + String.fromCharCode(data.getInt8(i));
  }
  value = Number(value);
  console.log(value);
  document.getElementById('data_text4').innerHTML = value;
}

document.getElementById('startNotifications').addEventListener('click', function() {
      microbit1.startNotify('micro:bit1');
});

document.getElementById('startNotifications2').addEventListener('click', function() {
      microbit2.startNotify('micro:bit2');
});

document.getElementById('startNotifications3').addEventListener('click', function() {
      microbit3.startNotify('micro:bit3');
});

document.getElementById('startNotifications4').addEventListener('click', function() {
      microbit4.startNotify('micro:bit4');
});

var observer = new MutationObserver(function(){
  var MB1 = document.getElementById('data_text');
  var MB2 = document.getElementById('data_text2');
  var MB3 = document.getElementById('data_text3');
  var MB4 = document.getElementById('data_text4');
  var content = MB1.innerHTML + "\n" + MB2.innerHTML + "\n" + MB3.innerHTML + "\n" + MB4.innerHTML
  navigator.clipboard.writeText(content)
  });
const elem = document.getElementById('data_text4');
const config = { 
  attributes: true, 
  childList: true, 
  characterData: true 
};
observer.observe(elem, config);
</script>

ライブラリ"BlueJelly.js"を少し改変して、使用しています.

bluejelly.js
var BlueJelly = function(){
  this.bluetoothDevice = null;
  this.dataCharacteristic = null;
  this.hashUUID ={};
  this.hashUUID_lastConnected;

  this.onScan = function(deviceName){console.log("onScan");};
  this.onConnectGATT = function(uuid){console.log("onConnectGATT");};
  this.onRead = function(data, uuid){console.log("onRead");};
  this.onWrite = function(uuid){console.log("onWrite");};
  this.onStartNotify = function(uuid){console.log("onStartNotify");};
  this.onStopNotify = function(uuid){console.log("onStopNotify");};
  this.onDisconnect = function(){console.log("onDisconnect");};
  this.onClear = function(){console.log("onClear");};
  this.onReset = function(){console.log("onReset");};
  this.onError = function(error){console.log("onError");};
}

BlueJelly.prototype.setUUID = function(name, serviceUUID, characteristicUUID){
  console.log('Execute : setUUID');
  console.log(this.hashUUID);

  this.hashUUID[name] = {'serviceUUID':serviceUUID, 'characteristicUUID':characteristicUUID};
}

BlueJelly.prototype.scan = function(uuid){
  return (this.bluetoothDevice ? Promise.resolve() : this.requestDevice(uuid))
  .catch(error => {
    console.log('Error : ' + error);
    this.onError(error);
  });
}

BlueJelly.prototype.requestDevice = function(uuid) {
  console.log('Execute : requestDevice');
  return navigator.bluetooth.requestDevice({
	  filters: [{
        namePrefix: 'BBC micro:bit',
      }],
      optionalServices: [this.hashUUID[uuid].serviceUUID]})
  .then(device => {
    this.bluetoothDevice = device;
    this.bluetoothDevice.addEventListener('gattserverdisconnected', this.onDisconnect);
    this.onScan(this.bluetoothDevice.name);
  });
}

BlueJelly.prototype.connectGATT = function(uuid) {
  if(!this.bluetoothDevice)
  {
    var error = "No Bluetooth Device";
    console.log('Error : ' + error);
    this.onError(error);
    return;
  }
  if (this.bluetoothDevice.gatt.connected && this.dataCharacteristic) {
    if(this.hashUUID_lastConnected == uuid)
      return Promise.resolve();
  }
  this.hashUUID_lastConnected = uuid;

  console.log('Execute : connect');
  return this.bluetoothDevice.gatt.connect()
  .then(server => {
    console.log('Execute : getPrimaryService');
    return server.getPrimaryService(this.hashUUID[uuid].serviceUUID);
  })
  .then(service => {
    console.log('Execute : getCharacteristic');
    return service.getCharacteristic(this.hashUUID[uuid].characteristicUUID);
  })
  .then(characteristic => {
    this.dataCharacteristic = characteristic;
    this.dataCharacteristic.addEventListener('characteristicvaluechanged',this.dataChanged(this, uuid));
    this.onConnectGATT(uuid);
  })
  .catch(error => {
      console.log('Error : ' + error);
      this.onError(error);
    });
}

BlueJelly.prototype.dataChanged = function(self, uuid) {
  return function(event) {
    self.onRead(event.target.value, uuid);
  }
}

BlueJelly.prototype.read= function(uuid) {
  return (this.scan(uuid))
  .then( () => {
    return this.connectGATT(uuid);
  })
  .then( () => {
    console.log('Execute : readValue');
    return this.dataCharacteristic.readValue();
  })
  .catch(error => {
    console.log('Error : ' + error);
    this.onError(error);
  });
}

BlueJelly.prototype.write = function(uuid, array_value) {
  return (this.scan(uuid))
  .then( () => {
    return this.connectGATT(uuid);
  })
  .then( () => {
    console.log('Execute : writeValue');
    data = Uint8Array.from(array_value);
    return this.dataCharacteristic.writeValue(data);
  })
  .then( () => {
    this.onWrite(uuid);
  })
  .catch(error => {
    console.log('Error : ' + error);
    this.onError(error);
  });
}

BlueJelly.prototype.startNotify = function(uuid) {
  return (this.scan(uuid))
  .then( () => {
    return this.connectGATT(uuid);
  })
  .then( () => {
    console.log('Execute : startNotifications');
    this.dataCharacteristic.startNotifications()
  })
  .then( () => {
    this.onStartNotify(uuid);
  })
  .catch(error => {
    console.log('Error : ' + error);
    this.onError(error);
  });
}

BlueJelly.prototype.disconnect= function() {
  if (!this.bluetoothDevice) {
    var error = "No Bluetooth Device";
    console.log('Error : ' + error);
    this.onError(error);
    return;
  }

  if (this.bluetoothDevice.gatt.connected) {
    console.log('Execute : disconnect');
    this.bluetoothDevice.gatt.disconnect();
  } else {
   var error = "Bluetooth Device is already disconnected";
   console.log('Error : ' + error);
   this.onError(error);
   return;
  }
}

BlueJelly.prototype.clear= function() {
   console.log('Excute : Clear Device and Characteristic');
   this.bluetoothDevice = null;
   this.dataCharacteristic = null;
   this.onClear();
}

BlueJelly.prototype.reset= function() {
  console.log('Excute : reset');
  this.disconnect();
  this.clear();
  this.onReset();
}


Object.defineProperty(BlueJelly, 'MICROBIT_BASE_UUID', {value: "e95d0000-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_SERVICESGENERIC_ACCESS', {value: "00001800-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_DEVICE_NAME', {value: "00002a00-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_APPEARANCE', {value: "00002a01-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS', {value: "00002a04-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_GENERIC_ATTRIBUTE', {value: "00001801-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_SERVICE_CHANGED', {value: "2a05", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_DEVICE_INFORMATION', {value: "0000180a-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MODEL_NUMBER_STRING', {value: "00002a24-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_SERIAL_NUMBER_STRING', {value: "00002a25-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_HARDWARE_REVISION_STRING', {value: "00002a27-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_FIRMWARE_REVISION_STRING', {value: "00002a26-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MANUFACTURER_NAME_STRING', {value: "00002a29-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_ACCELEROMETER_SERVICE', {value: "e95d0753-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_ACCELEROMETER_DATA', {value: "e95dca4b-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_ACCELEROMETER_PERIOD', {value: "e95dfb24-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MAGNETOMETER_SERVICE', {value: "e95df2d8-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MAGNETOMETER_DATA', {value: "e95dfb11-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MAGNETOMETER_PERIOD', {value: "e95d386c-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MAGNETOMETER_BEARING', {value: "e95d9715-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MAGNETOMETER_CALIBRATION', {value: "e95db358-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_BUTTON_SERVICE', {value: "e95d9882-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_BUTTON_A_STATE', {value: "e95dda90-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_BUTTON_B_STATE', {value: "e95dda91-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_IO_PIN_SERVICE', {value: "e95d127b-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_PIN_DATA', {value: "e95d8d00-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_PIN_AD_CONFIGURATION', {value: "e95d5899-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_PIN_IO_CONFIGURATION', {value: "e95db9fe-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_PWM_CONTROL', {value: "e95dd822-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_LED_SERVICE', {value: "e95dd91d-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_LED_MATRIX_STATE', {value: "e95d7b77-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_LED_TEXT', {value: "e95d93ee-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_SCROLLING_DELAY', {value: "e95d0d2d-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_EVENT_SERVICE', {value: "e95d93af-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MICROBIT_REQUIREMENTS', {value: "e95db84c-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MICROBIT_EVENT', {value: "e95d9775-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_CLIENT_REQUIREMENTS', {value: "e95d23c4-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_CLIENT_EVENT', {value: "e95d5404-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_DFU_CONTROL_SERVICE', {value: "e95d93b0-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_DFU_CONTROL', {value: "e95d93b1-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_TEMPERATURE_SERVICE', {value: "e95d6100-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_TEMPERATURE', {value: "e95d9250-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_TEMPERATURE_PERIOD', {value: "e95d1b25-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_UART_SERVICE', {value: "6e400001-b5a3-f393-e0a9-e50e24dcca9e", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_TX_CHARACTERISTIC', {value: "6e400002-b5a3-f393-e0a9-e50e24dcca9e", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_RX_CHARACTERISTIC', {value: "6e400003-b5a3-f393-e0a9-e50e24dcca9e", writable: false});

HTML/CSS

screenshot

[JavaScript(画像がある方が分かりやすいので、こちらに記載します.)]
④左足のボタンがトリガーとなっており、④の接続が完了すると、4つのセンサーの値をクリップボードへコピーし続けます.

index.html
<!doctype html>

<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="BlueJelly">
    <meta name="viewport" content="width=640, maximum-scale=1.0, user-scalable=yes">
    <title>micro:bit接続</title>
    <link href="https://fonts.googleapis.com/css?family=Lato:100,300,400,700,900" rel="stylesheet" type="text/css">
    <link rel="stylesheet" href="style.css">
    <script type="text/javascript" src="bluejelly.js"></script>
  </head>

<body>
    <div id="a">
    <div class="contents margin"><b>①右手</b><div id="status"> </div><br>
        <button id="startNotifications" class="button">接続開始</button>
        <div id="data_text"> </div>
    </div>
    </div>

    <div id="b">
      <div class="contents margin"><b>②右足</b><div id="status3"> </div><br>
          <button id="startNotifications3" class="button">接続開始</button>
          <div id="data_text3"> </div>
      </div>
      </div>

    <div id="c">
    <div class="contents margin"><b>③左手</b><div id="status2"> </div><br>
        <button id="startNotifications2" class="button">接続開始</button>
        <div id="data_text2"> </div>
    </div>
    </div>
	
    <div id="d">
	  <div class="contents margin"><b>④左足</b><div id="status4"> </div><br>
        <button id="startNotifications4" class="button">接続開始</button>
        <div id="data_text4"> </div>
    </div>
  </div>
</div>
</body>
</html>
style.css

@charset "utf-8";
/* CSS Document */

/*************************** RESET ***************************/

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {
	margin: 0;
	padding: 0;
	border: 0;
	outline: 0;
	font-size: 100%;
	vertical-align: baseline;
	background: transparent;
}
/*************************** RESET ***************************/


html, body {
	height: 100%;
	width: 100%;
	font-family: 'Lato', Verdana, "Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
	min-width: 640px;
	position: relative;
  background-color: #c6dfc6;
}

hr{
  border-width: 1px;
  border-color: #fff;
  border-style: solid;
  margin: 20px 0px 20px 0px;
}

.title{
  width: 100%;
  background-color: #fff;
  padding: 20px 0px 20px 0px;
}

.contents{
  padding: 20px 0px 50px 0px;
}

.footer{
  width: 100%;
  font-size:12px;
  position: absolute;
  bottom: 0px;
  height: 60px;
  color: #9a9a9a;
}

.margin{
  border-left:  70px;
  border-right: 70px;
  border-style: solid;
  box-sizing: border-box;
  border-color: rgba(0, 0, 0, 0);
}

.button{
  border-width: 1px;
  border-color: #e6e6e6;
  border-style: solid;
  background-color: #fff;
  padding: 5px 20px 5px 20px;
  color: #4d4d4d;
  font-size: 14px;
  font-weight: bold;
  margin: 0px;
}

.button:hover{
  border-color: #ccc;
}
.button:active{
  background-color: #f0f0f0;
}

#chart{
	margin-bottom: 20px;
}

#data_text, #data_text2, #data_text3, #data_text4{
  color:#0000ff;
  font-size:50px;
  font-weight: 300;
  line-height: normal;
}

#status, #status2, #status3, #status4{
	color:#0000ff;
	font-size:14px;
  font-weight: 300;
}

a { text-decoration: none; }
a:link { color: #9a9a9a; }
a:visited { color: #9a9a9a; }

#a, #c {
  float: left;
  margin:auto;
  text-align:center;
  width: 50%;
  }

#b, #d {
  float: left;
  margin:auto;
  text-align:center;
  width: 50%;
  }

C#

Windowsフォームアプリケーションで開発します.
WebViewで接続画面を埋め込んでいます.

screenshot
Form1.cs
private void timer1_Tick(object sender, EventArgs e)
{
    webView21.Focus();
    Data = Clipboard.GetText();

    string[] Get_Data;
    Get_Data = Data.Split('\n');

    string right_hand = Get_Data[0];
    string left_hand = Get_Data[1];
    string right_foot = Get_Data[2];
    string left_foot = Get_Data[3];
}

以上のコードで変数にセンサー値が格納されます.

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?