処理フロー
[MakeCode]
micro:bitに内蔵されているジャイロセンサーの値を、BLE(Bluetooth Low Energy)を使用してPCへ送信します.
[JavaScript]
接続処理をし、受け取ったセンサー値をクリップボードに書き込みます.
[C#]
クリップボードからセンサー値を読み取り、画面上のキャラクターを操作します.
言語間の値の受け渡し方法に悩んだのですが、"クリップボードを経由する"という無理やりな方法で実行しています.
MakeCode
センサーの値をBluetoothで送信するだけですので、非常にシンプルなコードになっています.
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"を少し改変して、使用しています.
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
[JavaScript(画像がある方が分かりやすいので、こちらに記載します.)]
④左足のボタンがトリガーとなっており、④の接続が完了すると、4つのセンサーの値をクリップボードへコピーし続けます.
<!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>
@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で接続画面を埋め込んでいます.
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];
}
以上のコードで変数にセンサー値が格納されます.