2
0

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 5 years have passed since last update.

TI SensorTagをブラウザから使う(1):with Chromeブラウザ

Last updated at Posted at 2019-03-24

TIが販売している小型で電池駆動のSensorTagを使ってみます。
 http://www.tij.co.jp/tool/jp/CC2650STK

計10個のセンサが付いているにもかかわらず、低価格です。

  • デジタル・マイク
  • 磁気センサ
  • 湿度
  • 圧力
  • 加速度計
  • ジャイロスコープ
  • 磁力計
  • 物体の温度
  • 周囲温度

しかも、デバッガボードCC-DEVPACK-DEBUGを使うと、ファームウェアのカスタマイズができます。これも低価格です。
 https://service.macnica.co.jp/library/120701

いろいろできそうなので、以降、2回の投稿に渡っていじってみます。

1回目は、WebBluetooth APIを使ってブラウザから接続し、センサ情報を表示します。WebBluetooth APIが対応しているブラウザであれば大丈夫なのですが、今回は定番のChromeを使います。
Android、Windows10で動くことを確認しました。(Windows10のChromeで動いたのは驚きでした)

2回目は、LINE ThingsのBLE機能を使って接続します。単に、LINE Thingsの勉強のためです。
ですが、2回目は、デバッガボードCC-DEVPACK-DEBUGを所有している方のみが対象です。LINE Thingsに対応させるためにファームウェアのカスタマイズが必要だからです。

#WebBluetooth APIを使ったSensorTag用クラスを作成する

単に、HTMLとJavascriptを書いて、HTTPSからブラウズできる場所に置くだけです。
WebBluetooth APIを使うので、HTTPSである必要があります。

まずは、SensorTagを扱いやすくするためのクラスを作成します。
WebBluetooth APIを存分に使っています。

以下を参考にさせていただきました。

sensortag.js
'use stricts';

const UUID_SERVICE_LINETHINGS = "【LINE Thingsのサービス探索用serviceUUID】"; //1回目では使いません。 

const UUID_SERVICE_IR_TEMPERATURE = 'f000aa00-0451-4000-b000-000000000000';
const UUID_CHAR_IR_TEMPERATURE_DATA = 'f000aa01-0451-4000-b000-000000000000';
const UUID_CHAR_IR_TEMPERATURE_ENABLE = 'f000aa02-0451-4000-b000-000000000000';
const UUID_CHAR_IR_TEMPERATURE_PERIOD = 'f000aa03-0451-4000-b000-000000000000';

const UUID_SERVICE_HUMIDITY = 'f000aa20-0451-4000-b000-000000000000';
const UUID_CHAR_HUMIDITY_DATA = 'f000aa21-0451-4000-b000-000000000000';
const UUID_CHAR_HUMIDITY_ENABLE = 'f000aa22-0451-4000-b000-000000000000';
const UUID_CHAR_HUMIDITY_PERIOD = 'f000aa23-0451-4000-b000-000000000000';

const UUID_SERVICE_BAROMETRIC_PRESSURE = 'f000aa40-0451-4000-b000-000000000000';
const UUID_CHAR_BAROMETRIC_PRESSURE_DATA = 'f000aa41-0451-4000-b000-000000000000';
const UUID_CHAR_BAROMETRIC_PRESSURE_ENABLE = 'f000aa42-0451-4000-b000-000000000000';
const UUID_CHAR_BAROMETRIC_PRESSURE_PERIOD = 'f000aa44-0451-4000-b000-000000000000';

const UUID_SERVICE_MOVEMENT = 'f000aa80-0451-4000-b000-000000000000';
const UUID_CHAR_MOVEMENT_DATA = 'f000aa81-0451-4000-b000-000000000000';
const UUID_CHAR_MOVEMENT_ENABLE = 'f000aa82-0451-4000-b000-000000000000';
const UUID_CHAR_MOVEMENT_PERIOD = 'f000aa83-0451-4000-b000-000000000000';

const UUID_SERVICE_OPTICAL = 'f000aa70-0451-4000-b000-000000000000';
const UUID_CHAR_OPTICAL_DATA = 'f000aa71-0451-4000-b000-000000000000';
const UUID_CHAR_OPTICAL_ENABLE = 'f000aa72-0451-4000-b000-000000000000';
const UUID_CHAR_OPTICAL_PERIOD = 'f000aa73-0451-4000-b000-000000000000';

const UUID_SERVICE_IO = 'f000aa64-0451-4000-b000-000000000000';
const UUID_CHAR_IO_DATA = 'f000aa65-0451-4000-b000-000000000000';
const UUID_CHAR_IO_CONFIG = 'f000aa66-0451-4000-b000-000000000000';

const UUID_SERVICE_SIMPLE_KEYS = BluetoothUUID.canonicalUUID(0xffe0);
const UUID_CHAR_SIMPLE_KEYS_DATA = BluetoothUUID.canonicalUUID(0xffe1);

class SensorTag{
    constructor(){
		this.bluetoothDevice = null;
        this.characteristics = new Map();
    }

    is_opened(){
        return this.bluetoothDevice ? true : false;
    }

    open(){
		return this.requestDevice(UUID_SERVICE_LINETHINGS);
    }

    close() {
		if (!this.is_opened())
			throw "Bluetooth Device is not opened";

		return Promise.resolve()
		.then(() =>{
			if (this.bluetoothDevice.gatt.connected) {
				console.log('Execute : disconnect');
                this.bluetoothDevice.gatt.disconnect();
                this.bluetoothDevice = null;
				this.characteristics.clear();
			} else {
                this.bluetoothDevice = null;
				this.characteristics.clear();
				throw "Bluetooth Device is already disconnected";
			}
		});
	}

    setup(){
		console.log('Execute : setup');

		return this.bluetoothDevice.gatt.connect()
		.then(server => {
            return this.setup_ir_tempeature(server);
        })
		.then(server => {
            return this.setup_humidity(server);
        })
        .then(server => {
            return this.setup_barometric_pressure(server);
        })
		.then(server => {
            return this.setup_movement(server);
        })
		.then(server => {
            return this.setup_optical(server);
        })
		.then(server => {
            return this.setup_io(server);
        })
		.then(server => {
            return this.setup_simple_keys(server);
        })
		.then(server =>{
			console.log('setup done');
			return this.bluetoothDevice.name;
		});
    }

    set_enable_movement(xg, yg, zg, xa, ya, za, mag, wakeon, arange){
        if (!this.is_opened())
            return;

        var bits = 0;
        bits |= (xg ? 0x01 : 0x00) << 13;
        bits |= (yg ? 0x01 : 0x00) << 14;
        bits |= (zg ? 0x01 : 0x00) << 15;
        bits |= (xa ? 0x01 : 0x00) << 10;
        bits |= (ya ? 0x01 : 0x00) << 11;
        bits |= (za ? 0x01 : 0x00) << 12;
        bits |= (mag ? 0x01 : 0x00) << 9;
        bits |= (wakeon ? 0x01 : 0x00) << 8;
        bits |= (arange & 0x03) << 6;
        this.paRange = Math.pow(2, (arange & 0x03) + 1);
        return this.characteristics.get(UUID_CHAR_MOVEMENT_ENABLE).writeValue(Uint8Array.from([(bits >> 8) & 0xff, bits & 0xff]));
    }

    set_enable(uuid, enable){
        if (!this.is_opened())
            return;

        return this.characteristics.get(uuid).writeValue(Uint8Array.from([enable ? 0x01 : 0x00]));
    }

    set_period(uuid, period){
        return this.characteristics.get(uuid).writeValue(Uint8Array.from([period & 0xff]));
    }

    set_callback(callback){
        this.callback = callback;
    }

    onDataChanged(event){
        console.log('onDataChanged');
        let characteristic = event.target;

        switch(characteristic.uuid){
            case UUID_CHAR_IR_TEMPERATURE_DATA:{
                var ambientTemperature = characteristic.value.getUint16(2, true) / 128.0;
                var objectTemperature = characteristic.value.getUint16(0, true) / 128.0;
                if( this.callback ){
                    this.callback({
                        type: 'temperature',
                        ambient_temperature: ambientTemperature,
                        object_temperature: objectTemperature
                    });
                }
                break;
            }
            case UUID_CHAR_HUMIDITY_DATA:{
                var temperature  = -40 + ((165  * characteristic.value.getUint16(0, true)) / 65536.0);
                var humidity  = characteristic.value.getUint16(2, true) * 100 / 65536.0;
                if( this.callback ){
                    this.callback({
                        type: 'humidity',
                        temperature: temperature,
                        humidity : humidity 
                    });
                }
                break;
            }
            case UUID_CHAR_BAROMETRIC_PRESSURE_DATA:{
                var flTempBMP;
                var flPressure;
              
                flTempBMP = (characteristic.value.getUint16(0, true) | (characteristic.value.getUint8(2) << 16))/ 100.0;
                flPressure = (characteristic.value.getUint16(3, true) | (characteristic.value.getUint8(5) << 16)) / 100.0;
                if( this.callback ){
                    this.callback({
                        type: 'pressure',
                        pressure: flPressure,
                        temperature: flTempBMP
                    });
                }

                break;
            }
            case UUID_CHAR_MOVEMENT_DATA:{
                // 250 deg/s range
                var xG = characteristic.value.getInt16(0, true) / 128.0;
                var yG = characteristic.value.getInt16(2, true) / 128.0;
                var zG = characteristic.value.getInt16(4, true) / 128.0;

                // we specify 8G range in setup
                var xA = characteristic.value.getInt16(6, true) / (32768.0 / this.paRange);
                var yA = characteristic.value.getInt16(8, true) / (32768.0 / this.paRange);
                var zA = characteristic.value.getInt16(10, true) / (32768.0 / this.paRange);

                // magnetometer (page 50 of http://www.invensense.com/mems/gyro/documents/RM-MPU-9250A-00.pdf)
                var xM = characteristic.value.getInt16(12, true);
                var yM = characteristic.value.getInt16(14, true);
                var zM = characteristic.value.getInt16(16, true);
                if( this.callback ){
                    this.callback({
                        type: 'movement',
                        xG: xG,
                        yG: yG,
                        zG: zG,
                        xA: xA,
                        yA: yA,
                        zA: zA,
                        xM: xM,
                        yM: yM,
                        zM: zM,
                    });
                }
                break;
            }
            case UUID_CHAR_OPTICAL_DATA:{
                var rawLux = characteristic.value.getUint16(0, true);

                var exponent = (rawLux & 0xF000) >> 12;
                var mantissa = (rawLux & 0x0FFF);
              
                var flLux = mantissa * Math.pow(2, exponent) / 100.0;
                if( this.callback ){
                    this.callback({
                        type: 'optical',
                        luminance : flLux
                    });
                }

                break;
            }
            case UUID_CHAR_SIMPLE_KEYS_DATA:{
                if( this.callback ){
                    this.callback({
                        type: 'keys',
                        keys : characteristic.value.getUint8(0)
                    });
                }

                break;

            }
            default:
                console.log('Unkown data', characteristic);
                break;
        }
    }

    onDisconnect(event){
		console.log('onDisconnect');
	}

    requestDevice(service_uuid){
        return new Promise((resolve, reject) =>{
            liff.init(data => resolve(data), error => reject(error) );
        })
        .then(data =>{
            console.log('Execute : requestDevice(liff)');
            return this.liffCheckAvailablityAndDo()
            .then( () =>{
                return liff.bluetooth.requestDevice();
            })
            .then(device =>{
                console.log("requestDevice OK");
                this.characteristics.clear();
                this.bluetoothDevice = device;
                this.bluetoothDevice.addEventListener('gattserverdisconnected', (event) => {
                    this.onDisconnect(event);
                });
                return this.bluetoothDevice.name;
            });
        })
        .catch(error =>{
            console.log('Execute : requestDevice(normal)');
            return navigator.bluetooth.requestDevice({
                filters: [{services:[ /* service_uuid */ BluetoothUUID.canonicalUUID(0x180f) ]}], // 1回目ではとりあえずこんな感じ
                  optionalServices: [
                      UUID_SERVICE_IR_TEMPERATURE,
                      UUID_SERVICE_HUMIDITY,
                      UUID_SERVICE_BAROMETRIC_PRESSURE,
                      UUID_SERVICE_MOVEMENT,
                      UUID_SERVICE_OPTICAL,
                      UUID_SERVICE_IO,
                      UUID_SERVICE_SIMPLE_KEYS
                ]
            })
            .then(device => {
                console.log("requestDevice OK");
                this.characteristics.clear();
                this.bluetoothDevice = device;
                this.bluetoothDevice.addEventListener('gattserverdisconnected', (event) => {
                    this.onDisconnect(event)
                });
                return this.bluetoothDevice.name;
            });
        });
    }

    async liffCheckAvailablityAndDo() {
        console.log('calling liffCheckAvailablityAndDo');

        await liff.initPlugins(['bluetooth']);
        for( var i = 0 ; i < 10 ; i++ ){
            var isAvailable = await liff.bluetooth.getAvailability();
            if( isAvailable )
                return;
            await wait_async(3000);
        }
        throw 'error liff.bluetooth.getAvailability';
    }

	setCharacteristic(service, characteristicUuid) {
		return service.getCharacteristic(characteristicUuid)
		.then(characteristic => {
			console.log('setCharacteristic : ' + characteristicUuid);
			this.characteristics.set(characteristicUuid, characteristic);
			return service;
		});
	}

    startNotify(uuid) {
		console.log('Execute : startNotifications');
        var characteristic = this.characteristics.get(uuid);
		if( characteristic === undefined )
			throw "Not Connected";

		characteristic.addEventListener('characteristicvaluechanged', (event) =>{
			this.onDataChanged(event);
		});
		return characteristic.startNotifications();
	}

	stopNotify(uuid){
		console.log('Execute : stopNotifications');
		var characteristic = this.characteristics.get(uuid);
		if( characteristic === undefined )
			throw "Not Connected";

		return characteristic.stopNotifications();
    }
    
    setup_ir_tempeature(server){
        return server.getPrimaryService(UUID_SERVICE_IR_TEMPERATURE)
        .then(service =>{
            return Promise.all([
                this.setCharacteristic(service, UUID_CHAR_IR_TEMPERATURE_DATA),
                this.setCharacteristic(service, UUID_CHAR_IR_TEMPERATURE_ENABLE),
                this.setCharacteristic(service, UUID_CHAR_IR_TEMPERATURE_PERIOD)
            ]);
        })
        .then(values =>{
            return this.startNotify(UUID_CHAR_IR_TEMPERATURE_DATA);
        })
        .then(()=>{
            return server;
        });
    }

    setup_humidity(server){
        return server.getPrimaryService(UUID_SERVICE_HUMIDITY)
        .then(service =>{
            return Promise.all([
                this.setCharacteristic(service, UUID_CHAR_HUMIDITY_DATA),
                this.setCharacteristic(service, UUID_CHAR_HUMIDITY_ENABLE),
                this.setCharacteristic(service, UUID_CHAR_HUMIDITY_PERIOD)
            ]);
        })
        .then(values =>{
            return this.startNotify(UUID_CHAR_HUMIDITY_DATA);
        })
        .then(()=>{
            return server;
        });
    }

    setup_barometric_pressure(server){
        return server.getPrimaryService(UUID_SERVICE_BAROMETRIC_PRESSURE)
        .then(service =>{
            return Promise.all([
                this.setCharacteristic(service, UUID_CHAR_BAROMETRIC_PRESSURE_DATA),
                this.setCharacteristic(service, UUID_CHAR_BAROMETRIC_PRESSURE_ENABLE),
                this.setCharacteristic(service, UUID_CHAR_BAROMETRIC_PRESSURE_PERIOD)
            ]);
        })
        .then(values =>{
            return this.startNotify(UUID_CHAR_BAROMETRIC_PRESSURE_DATA);
        })
        .then(()=>{
            return server;
        });
    }

    setup_movement(server){
        return server.getPrimaryService(UUID_SERVICE_MOVEMENT)
        .then(service =>{
            return Promise.all([
                this.setCharacteristic(service, UUID_CHAR_MOVEMENT_DATA),
                this.setCharacteristic(service, UUID_CHAR_MOVEMENT_ENABLE),
                this.setCharacteristic(service, UUID_CHAR_MOVEMENT_PERIOD)
            ]);
        })
        .then(values =>{
            return this.startNotify(UUID_CHAR_MOVEMENT_DATA);
        })
        .then(()=>{
            return server;
        });
    }

    setup_optical(server){
        return server.getPrimaryService(UUID_SERVICE_OPTICAL)
        .then(service =>{
            return Promise.all([
                this.setCharacteristic(service, UUID_CHAR_OPTICAL_DATA),
                this.setCharacteristic(service, UUID_CHAR_OPTICAL_ENABLE),
                this.setCharacteristic(service, UUID_CHAR_OPTICAL_PERIOD)
            ]);
        })
        .then(values =>{
            return this.startNotify(UUID_CHAR_OPTICAL_DATA);
        })
        .then(()=>{
            return server;
        });
    }

    setup_io(server){
        return server.getPrimaryService(UUID_SERVICE_IO)
        .then(service =>{
            return Promise.all([
                this.setCharacteristic(service, UUID_CHAR_IO_DATA),
                this.setCharacteristic(service, UUID_CHAR_IO_CONFIG)
            ]);
        })
        .then(()=>{
            return server;
        });
    }

    setup_simple_keys(server){
        return server.getPrimaryService(UUID_SERVICE_SIMPLE_KEYS)
        .then(service =>{
            return Promise.all([
                this.setCharacteristic(service, UUID_CHAR_SIMPLE_KEYS_DATA)
            ]);
        })
        .then(values =>{
            return this.startNotify(UUID_CHAR_SIMPLE_KEYS_DATA);
        })
        .then(()=>{
            return server;
        });
    }
}

function wait_async(timeout){
	return new Promise((resolve, reject) =>{
		setTimeout(resolve, timeout);
	});
}

UUID_SERVICE_LINETHINGS は1回目では使わないので、とりあえず変更する必要はありません。

クラスの実装を一部補足します。

  • open()

まず何よりこのメソッドを呼び出します。

 liff.init(data => resolve(data), error => reject(error) )

これは、LINE Thingsを使うためのLINEアプリ(LIFF)から呼ばれたかどうかを判別するためのコードです。1回目の記事ではChromeからの呼び出しなのでエラーとなり、catchの処理に移ります。

 navigator.bluetooth.requestDevice

引数に指定したサービスUUIDを持ったBLEデバイスを検索(Scan)します。素のSensorTagではバッテリーサービス(0x180f)を持っているので、それを指定しています。

  • setup()

センサー情報を読み出すためのCharacteristicを検索するとともに、センサー情報のNotifyを有効にします。
CharacteristicのUUIDは決まっているので、それを各Primary Serviceで指定しています。

  • set_enable()

センサー計測を有効にします。setup()ではNotifyができる状態になっただけです。センサー情報の取得の有効化・無効化はこのset_enable()で切り替えます。

  • set_period()

センサー情報のNotify間隔を指定します。デフォルトは1秒間隔になっています。

  • set_callback()

これは、センサー情報の取得はReadではなくNotifyで受信するようにしたいため、このメソッドに受信するコールバック関数を指定します。
SensorTagからNotifyされてくるデータを各センサーのタイプに合わせてコンバートします。
(※気圧のコンバートのための計算式がわかりませんでした。。。。MovementのEnableマスクも怪しい。。。)

あとは、このクラスを使ってブラウザに表示させます。Vueを使っています。

start.js
'use strict';

var sensortag = new SensorTag();

var vue_options = {
    el: "#top",
    data: {
        movement: {},
        temperature: {},
        humidity: {},
        pressure: {},
        optical: {},
        temperature_enable : false,
        movement_enable : false,
        humidity_enable : false,
        pressure_enable : false,
        optical_enable : false,
        button_title: '接続',
        left_key: null,
        right_key: null,

        progress_title: '',
    },
    computed: {
    },
    methods: {
        start: async function(){
            if( !sensortag.is_opened() ){
                try{
                    this.button_title = '接続中';
                    sensortag.set_callback(this.on_receive);
                    await sensortag.open();
                    await sensortag.setup();
                    this.button_title = '切断';
                }catch(error){
                    console.log(error);
                    alert(error);
                    this.button_title = '接続';
                }
            }else{
                try{
                    this.button_title = '切断中';
                    sensortag.close();
                    this.button_title = '接続';
                }catch(error){
                    console.log(error);
                    this.button_title = '切断';
                }
            }
        },
        set_enable: async function(type){
            if( type == 'movement'){
                if( !this.movement_enable )
                    await sensortag.set_enable_movement(true, true, true, true, true, true, true, false, 3);
                else
                    await sensortag.set_enable_movement(false, false, false, false, false, false, false, false, 0);
            }else{
                var uuid;
                var value;
                switch(type){
                    case 'temperature':
                        uuid = UUID_CHAR_IR_TEMPERATURE_ENABLE;
                        value = !this.temperature_enable;
                        break;
                    case 'humidity':
                        uuid = UUID_CHAR_HUMIDITY_ENABLE;
                        value = !this.humidity_enable;
                        break;
                    case 'pressure':
                        uuid = UUID_CHAR_BAROMETRIC_PRESSURE_ENABLE;
                        value = !this.pressure_enable;
                        break;
                    case 'optical':
                        uuid = UUID_CHAR_OPTICAL_ENABLE;
                        value = !this.optical_enable;
                        break;
                }
                await sensortag.set_enable(uuid, value);
            }
        },
        on_receive: function(data){
            console.log(data);
            switch( data.type ){
                case 'temperature':
                    this.temperature = data;
                    break;
                case 'humidity':
                    this.humidity = data;
                    break;
                case 'pressure':
                    this.pressure = data;
                    break;
                case 'movement':
                    this.movement = data;
                    break;
                case 'optical':
                    this.optical = data;
                    break;
                case 'keys':
                    if( data.keys & 0x01 )
                        this.left_key = true;
                    else
                        this.left_key = false;
                    if( data.keys & 0x02 )
                        this.right_key = true;
                    else
                        this.right_key = false;
                    break;
            }
        }
    },
    created: function(){
    },
    mounted: function(){
    }
};
var vue = new Vue( vue_options );

以下は、HTMLファイルです。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: https://ssl.gstatic.com 'unsafe-eval' 'unsafe-inline'; style-src * 'unsafe-inline'; media-src *; img-src * data: content: blob:;">
  <meta name="format-detection" content="telephone=no">
  <meta name="msapplication-tap-highlight" content="no">
  <meta name="apple-mobile-web-app-capable" content="yes" />
  <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">

  <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
  <!-- Latest compiled and minified CSS -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  <!-- Optional theme -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
  <!-- Latest compiled and minified JavaScript -->
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

  <title>SensortTag</title>

  <script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
  <script src="https://unpkg.com/vue"></script>
</head>
<body>
    <div id="top" class="container">
        <h1>SensorTag</h1>

        <button class="btn btn-default" v-on:click="start()">{{button_title}}</button><br>
        <br>
        <input type="checkbox" v-model="temperature_enable" v-on:click="set_enable('temperature')">
        IR Temperature Sensor<br>
        <label>ambient_temperature</label> {{temperature.ambient_temperature}}<br>
        <label>object_temperature</label> {{temperature.object_temperature}}<br>
        <br>
        <input type="checkbox" v-model="humidity_enable" v-on:click="set_enable('humidity')">
        Humidity Sensor<br>
        <label>temperature</label> {{humidity.temperature}}<br>
        <label>humidity</label> {{humidity.humidity}}<br>
        <br>
        <input type="checkbox" v-model="pressure_enable" v-on:click="set_enable('pressure')">
        Barometric Pressure Sensor<br>
        <label>pressure</label> {{pressure.pressure}}<br>
        <label>temperature</label> {{pressure.temperature}}<br>
        <br>
        <input type="checkbox" v-model="optical_enable" v-on:click="set_enable('optical')">
        Optical Sensor<br>
        <label>luminance</label> {{optical.luminance}}<br>
        <br>
        Keys<br>
        <label>left_key</label> {{left_key}}<br>
        <label>right_key</label> {{right_key}}<br>
        <br>
        <input type="checkbox" v-model="movement_enable" v-on:click="set_enable('movement')">
        Movement Sensor<br>
        <label>xG</label> {{movement.xG}}<br>
        <label>yG</label> {{movement.yG}}<br>
        <label>zG</label> {{movement.zG}}<br>
        <label>xA</label> {{movement.xA}}<br>
        <label>yA</label> {{movement.yA}}<br>
        <label>zA</label> {{movement.zA}}<br>
        <label>xM</label> {{movement.xM}}<br>
        <label>yM</label> {{movement.yM}}<br>
        <label>zM</label> {{movement.zM}}<br>

        <div class="modal fade" id="progress">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <h4 class="modal-title">{{progress_title}}</h4>
                    </div>
                    <div class="modal-body">
                        <center><progress max="100" /></center>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script src="js/sensortag.js"></script>
    <script src="js/start.js"></script>
</body>

#ChromeブラウザからSensorTagに接続する

以上のコンテンツをWebサーバに配置して、Chromeから開いてみましょう。
必ず、HTTPSでアクセスしてください。

image.png

SensorTagの電源ボタンを1回押して、緑色のLEDが点滅するのを確認して、「接続」ボタンを押下します。
そうすると、以下のようなデバイスの選択画面が出てきます。
以下の画面ではすでに1度接続しているときのものですが、初めて接続する場合には、不明なデバイスとして表示されるかもしれません。

image.png

対象デバイスを選択してから「ペア設定」ボタンを押下します。
数秒してからボタンが「切断」に変われば接続成功です。
各センサ名のチェックボックスをOnにすることで、1秒ごとに表示が変わるのがわかります。
Keysも、SensorTagの両脇のボタンを押すと表示が切り替わります。

image.png

以上です。

(2回目はLINE Thingsに対応させます。)
TI SensorTagをブラウザから使う(2):with LINE Things

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?