HSP3Dish.jsでセンサー値を取得

目次

今回やること

 HSP3Dish.js でJavaScriptを実行する方法については、前回説明しました。 今回はその続きとして、JavaScript側で取得した値をHSP3側で受け取り、使ってみる方法について書いてみます。

 まずはdevicemotionイベントを使用して加速度を取得してみます。 また、加速度(重力を含む)、角速度の取得に加え、deviceorientationイベントを用いてデバイスの向きも取得します。 あ。requestPermissionを使っていないので、iOS (Safari)では動きません。Androidスマホでお試しください。

 今回の内容は、HSP3.7β9、β10が対象です。

センサ値の取得(JavaScript)

 JavaScriptでセンサーデータを取得する方法を確認します。

HTMLとJavaScriptでの実装例がこちらです。
実装例:sensor01.html

 こちらの実装例はiOS (Safari)では動作しません。動作させるためにはrequestPermissionを入れる必要があるのですが、今回は調べてないので見送りです。

 動かしてみると、小数点1桁までしか値が取得できていません。ブラウザの制限でしょうか?このあたりについても調べきれてないので今回は見送り。このままいきます。

 JavaScriptで作成したプログラム部分はこのようになっています。

JavaScript

// 値の受け取り用グローバル変数
var acc  = { x: 0, y: 0, z: 0};
var accg = { x: 0, y: 0, z: 0};
var rot    = { alpha: 0, beta: 0, gamma: 0};
var orient = { alpha: 0, beta: 0, gamma: 0};

// デバイスモーションイベントのリスナーを追加
window.addEventListener('devicemotion', function(event) {
    // 加速度
    var acceleration = event.acceleration;
    acc.x = acceleration.x;
    acc.y = acceleration.y;
    acc.z = acceleration.z;

    // 加速度 (重力を含む)
    var accelerationIncludingGravity = event.accelerationIncludingGravity;
    accg.x = accelerationIncludingGravity.x;
    accg.y = accelerationIncludingGravity.y;
    accg.z = accelerationIncludingGravity.z;

    // 角速度
    var rotationRate = event.rotationRate;
    rot.alpha = rotationRate.alpha;
    rot.beta  = rotationRate.beta;
    rot.gamma = rotationRate.gamma;
});

// デバイスオリエンテーションイベントのリスナーを追加
window.addEventListener('deviceorientation', function(event) {
    // デバイスの方向
    orient.alpha = event.alpha;
    orient.beta  = event.beta;
    orient.gamma = event.gamma;
});

 変数 accaccgrotorient にセンサーの値を格納します。 イベントリスナーを設定し、センサーの値が更新されるたびに値を取得する処理を実行しています。

stringToUTF8Array関数を使った値の受け取り

 JavaScript側からHSP3へ値を渡すには、stringToUTF8Array 関数を使用します。 この関数はHSP3Dish.js内で定義されており、以下のように使用します。 (そういえばセーブの話をした際にもstringToUTF8Arrayは使ったんでしたね。復習です。)

JavaScript

stringToUTF8Array(文字列, Module.HEAP8, HSP3側の受け取り用変数のポインタ, HSP側の変数のバッファサイズ)
パラメータ説明
文字列HSP3側に渡したい文字列を指定します。
Module.HEAP8固定値(意味は分かりません。)
HSP3側の受け取り用変数のポインタHSP3側で値を受け取る変数のポインタを指定します。実行直前にvarptr命令で取得して使います。
HSP側の変数のバッファサイズsdim命令で確保したサイズ以下か、varsize命令で確認したサイズ以下を指定します。sdim命令で事前に確保したサイズ以上を指定していけません。

 HSP3の変数のポインタを渡す必要があるので、HSP3側での実装が必要です。

HSP3

sdim strBox, 64
ptrBox  = varptr( strBox )
exec "stringToUTF8Array('文字列', Module.HEAP8, " + ptrBox + ", 64)"

 この方法では文字列のみ受け渡し可能です。 数値を渡す場合は、文字列に変換したものを受け取り、HSP3側で double関数などを使用して数値に変換する必要があります。 ちょっと面倒ですね。

HSP3でのセンサーデータ取得

 ここからは、加速度を取得する方法を例にHSP3Dish.jsでの実装例を作ってみます。

 JavaScript側の準備をします。加速度を取得するスクリプトをHTMLファイルに追加します。 ヘルパーでHTMLファイルを出力後に以下の内容を追記します。 hsp3dish.jsを呼び出した後ぐらいがいいと思います。

HTML

<script async type="text/javascript" src="hsp3dish.js"></script>
<script>
  var acc  = { x: 0, y: 0, z: 0};
  function updateSensorAcc(ptrX, ptrY, ptrZ) {
      if( !!ptrX && (acc.x !== null) ){ stringToUTF8Array(acc.x.toFixed(5), Module.HEAP8, ptrX, 64); }
      if( !!ptrY && (acc.y !== null) ){ stringToUTF8Array(acc.y.toFixed(5), Module.HEAP8, ptrY, 64); }
      if( !!ptrZ && (acc.z !== null) ){ stringToUTF8Array(acc.z.toFixed(5), Module.HEAP8, ptrZ, 64); }
  }
  window.addEventListener('devicemotion', function(event) {
      // 加速度
      var acceleration = event.acceleration;
      acc.x = acceleration.x;
      acc.y = acceleration.y;
      acc.z = acceleration.z;
  });
</script>

 HSP3側の実装は、updateSensorAcc関数を呼び出すだけです。

HSP3

#include "hsp3dish.as"

; 読み込み用バッファ
sdim strAcceleration_x, 64
sdim strAcceleration_y, 64
sdim strAcceleration_z, 64

exec_text = ""
;-------------------------------------------------------------------------------
*main
	redraw 1
	exec exec_text
	redraw 0 : color 10,10,10 : boxf : color 255, 255, 255 : pos 0,0

	;	取得値を数値に変換
	; 取得した値は文字列なので、使いやすいように数値に変換します。
	acceleration_x = double( strAcceleration_x )
	acceleration_y = double( strAcceleration_y )
	acceleration_z = double( strAcceleration_z )

	;	描画
	mes "acc X = " + acceleration_x
	mes "acc Y = " + acceleration_y
	mes "acc Z = " + acceleration_z

	;	センサー値を取得
	ptrAcc_x  = varptr( strAcceleration_x )
	ptrAcc_y  = varptr( strAcceleration_y )
	ptrAcc_z  = varptr( strAcceleration_z )
	exec_text = "updateSensorAcc( " + ptrAcc_x  + ", " + ptrAcc_y  + ", " + ptrAcc_z  + ");"

	goto *main

 ほとんどJavaScript側で実装してしまったので、HSP3側はシンプルに記述できました。

サンプル

 同じ要領で、加速度(重力含む)、角速度も取得してみます。ついでにデバイスの向きについても取得してみます。 実装例です。

例:sensor02.html

 スマートフォン(Android)などのセンサーが内蔵されたデバイスで実行してください。 デスクトップPCのブラウザにはセンサーがないため、反応しません。iOS(Safari)も非対応です。

 デバイスを動かすことで、棒グラフや数値が動的に変化する様子を確認できるはずです。

sensor.jsについて

 サンプルの中身を見ていきます。 まずはJavaScript側の実装から。加速度や角速度、デバイスの向きなどを取得するためのスクリプトが必要です。

JavaScript側で取得するセンサー情報は、次のものです。

  • 加速度 … devicemotionイベントのaccelerationプロパティ
  • 加速度(重力含む) … devicemotionイベントのaccelerationIncludingGravityプロパティ
  • 角速度 … devicemotionイベントのrotationRateプロパティ
  • デバイスの向き … deviceorientationイベントのeventオブジェクト

 これらを取得するJavaScriptのプログラムを書く必要があります。長くなりそうですね。毎度HTMLに何行も追記するのは面倒です。 これに限らずスクリプトは長くなりがちなので別ファイルに書くべきでしょう。今回は sensor.js という名前のファイルにまとめてHTML側から呼び出す形にします。

JavaScript

// sensor.js

// 値の受け取り用グローバル変数
var acc  = { x: 0, y: 0, z: 0};
var accg = { x: 0, y: 0, z: 0};
var rot    = { alpha: 0, beta: 0, gamma: 0};
var orient = { alpha: 0, beta: 0, gamma: 0};

function updateSensorAcc(ptrX, ptrY, ptrZ) {
    if( !!ptrX && (acc.x !== null) ){ stringToUTF8Array(acc.x.toFixed(5), Module.HEAP8, ptrX, 64); }
    if( !!ptrY && (acc.y !== null) ){ stringToUTF8Array(acc.y.toFixed(5), Module.HEAP8, ptrY, 64); }
    if( !!ptrZ && (acc.z !== null) ){ stringToUTF8Array(acc.z.toFixed(5), Module.HEAP8, ptrZ, 64); }
}

function updateSensorAccg(ptrX, ptrY, ptrZ) {
    if( !!ptrX && (accg.x !== null) ){ stringToUTF8Array(accg.x.toFixed(5), Module.HEAP8, ptrX, 64); }
    if( !!ptrY && (accg.y !== null) ){ stringToUTF8Array(accg.y.toFixed(5), Module.HEAP8, ptrY, 64); }
    if( !!ptrZ && (accg.z !== null) ){ stringToUTF8Array(accg.z.toFixed(5), Module.HEAP8, ptrZ, 64); }
}
function updateSensorRot(ptrA, ptrB, ptrG) {
    if( !!ptrA && (rot.alpha !== null) ){ stringToUTF8Array(rot.alpha.toFixed(5), Module.HEAP8, ptrA, 64); }
    if( !!ptrB && (rot.beta  !== null) ){ stringToUTF8Array(rot.beta.toFixed(5),  Module.HEAP8, ptrB, 64); }
    if( !!ptrG && (rot.gamma !== null) ){ stringToUTF8Array(rot.gamma.toFixed(5), Module.HEAP8, ptrG, 64); }
}
function updateSensorOrient(ptrA, ptrB, ptrG) {
    if( !!ptrA && (orient.alpha !== null) ){ stringToUTF8Array(orient.alpha.toFixed(5), Module.HEAP8, ptrA, 64); }
    if( !!ptrB && (orient.beta  !== null) ){ stringToUTF8Array(orient.beta.toFixed(5),  Module.HEAP8, ptrB, 64); }
    if( !!ptrG && (orient.gamma !== null) ){ stringToUTF8Array(orient.gamma.toFixed(5), Module.HEAP8, ptrG, 64); }
}


// デバイスモーションイベントのリスナーを追加
window.addEventListener('devicemotion', function(event) {
    // 加速度
    var acceleration = event.acceleration;
    acc.x = acceleration.x;
    acc.y = acceleration.y;
    acc.z = acceleration.z;

    // 加速度 (重力を含む)
    var accelerationIncludingGravity = event.accelerationIncludingGravity;
    accg.x = accelerationIncludingGravity.x;
    accg.y = accelerationIncludingGravity.y;
    accg.z = accelerationIncludingGravity.z;

    // 角速度
    var rotationRate = event.rotationRate;
    rot.alpha = rotationRate.alpha;
    rot.beta  = rotationRate.beta;
    rot.gamma = rotationRate.gamma;
});

// デバイスオリエンテーションイベントのリスナーを追加
window.addEventListener('deviceorientation', function(event) {
    // デバイスの方向
    orient.alpha = event.alpha;
    orient.beta  = event.beta;
    orient.gamma = event.gamma;
});

 以下は、HTMLファイル側からsensor.jsを読み込む例です。 <script src="sensor.js"></script> の部分で読み込んでいます。 htmlを出力した後に、この要領で修正を行ってください。

HTML

…
    </script>
    <script async type="text/javascript" src="hsp3dish.js"></script>
    <script src="sensor.js"></script>
  <p><font size="2" color="#404040">powered by …</font></p>
  </body>
</html>

 この方法を使うと、HTML側での修正は1行だけで済み、センサー値取得機能のアップデートも簡単に行えます。

hsp3での実装例

 センサー値を取得する関数が準備できたら、HSP3側で値を受け取るスクリプトを作成します。

HSP3

; スマホ向けHSP3Dishのサンプル
#include "hsp3dish.as"
#include "d3m.hsp"

#module

;	バー
#deffunc drawBar double p_max, double p_val
	r = ginfo_r
	g = ginfo_g
	b = ginfo_b
	px = ginfo_cx
	py = ginfo_cy
	h = 10
	w = 120
	
	color 200,200,200
	boxf px - w, py, px + w, py + h
	
	v = p_val / p_max
	ww = v * w
	if ww >= 0 {
		color 250, 0, 0
	} else {
		color 0, 0, 250
	}
	boxf px, py, ww + px, py + h
	pos px, py+h
	color r,g,b
	return

; 画像を使用したフォント表示準備
; mod_picfont.asを流用
#deffunc picfont int _p1, int _p2, int _p3, int _p4, int _p5
	sx=_p2:if sx=0 : sx=16
	sy=_p3:if sy=0 : sy=16
	mode=_p4:id=_p1
	ofsx=_p5
	celdiv id, sx, sy;, sx/2, sy/2
	return

; 画像を使用したフォント表示
#deffunc picfprt str _p1
	x=ginfo_cx:xs=x
	y=ginfo_cy
	i=0:gmode mode,sx,sy
	st=_p1

	repeat
	a1=peek(st,i):i++:if a1=0 : break
	if a1 = 13 {
		a1 = peek(st,i)
		if a1=10 : i++
		x=xs : y+=sy : continue	; 改行
	} else {
		pos x,y
		celput id, a1
	}
	x+=sx+ofsx
	loop

	pos xs,y+sy
	return
#global


;-------------------------------------------------------------------------------

; プラットフォームを確認
getreq idPlatform, SYSREQ_PLATFORM

; フォントを準備
widFont = 1
celload  "fontchr.png", widFont
picfont widFont,16,16,2,-4	; Dishだとgmode 2が効いてない。


; 読み込み用バッファ
sdim strAcceleration_x, 64
sdim strAcceleration_y, 64
sdim strAcceleration_z, 64
sdim strAccelerationIncludingGravity_x, 64
sdim strAccelerationIncludingGravity_y, 64
sdim strAccelerationIncludingGravity_z, 64
sdim strRotationRate_alpha, 64
sdim strRotationRate_beta,  64
sdim strRotationRate_gamma, 64
sdim strOrientation_alpha, 64
sdim strOrientation_beta,  64
sdim strOrientation_gamma, 64


exec_text = ""
	
;-------------------------------------------------------------------------------
*main
	redraw 1
	if idPlatform = PLATFORM_WEBGL {
		; hsp3dish.js(WebGL/JavaScript)版
		exec exec_text
	} else {
		; Windows版など他環境
		await 16
	}
	redraw 0 : color 10,10,10 : boxf : color 255, 255, 255 : pos 0,0
	picfprt "" + d3getfps() + " fps"

	;	取得値を数値に変換
	; 取得した値は文字列なので、使いやすいように数値に変換します。
	acceleration_x                 = double( strAcceleration_x )
	acceleration_y                 = double( strAcceleration_y )
	acceleration_z                 = double( strAcceleration_z )
	accelerationIncludingGravity_x = double( strAccelerationIncludingGravity_x )
	accelerationIncludingGravity_y = double( strAccelerationIncludingGravity_y )
	accelerationIncludingGravity_z = double( strAccelerationIncludingGravity_z )
	rotationRate_alpha             = double( strRotationRate_alpha )
	rotationRate_beta              = double( strRotationRate_beta )
	rotationRate_gamma             = double( strRotationRate_gamma )
	orientation_alpha              = double( strOrientation_alpha )
	orientation_beta               = double( strOrientation_beta )
	orientation_gamma              = double( strOrientation_gamma )


	;	描画
	cx = ginfo_winx / 2
	pos cx, 10

	m = 10.0
	s = "acceleration_x" : v = acceleration_x : gosub *l_draw
	s = "acceleration_y" : v = acceleration_y : gosub *l_draw
	s = "acceleration_z" : v = acceleration_z : gosub *l_draw
	s = "accelerationIncludingGravity_x" : v = accelerationIncludingGravity_x : gosub *l_draw
	s = "accelerationIncludingGravity_y" : v = accelerationIncludingGravity_y : gosub *l_draw
	s = "accelerationIncludingGravity_z" : v = accelerationIncludingGravity_z : gosub *l_draw
	m = 30.0
	s = "rotationRate_alpha" : v = rotationRate_alpha : gosub *l_draw
	s = "rotationRate_beta " : v = rotationRate_beta  : gosub *l_draw
	s = "rotationRate_gamma" : v = rotationRate_gamma : gosub *l_draw
	m = 360.0
	s = "orientation_alpha " : v = orientation_alpha  : gosub *l_draw
	m = 90.0
	s = "orientation_beta  " : v = orientation_beta   : gosub *l_draw
	s = "orientation_gamma " : v = orientation_gamma  : gosub *l_draw


	;	値を取得
	; JavaScriptを使用して、各種センサーの値を取得します。
	; 次の名用のスクリプトを実行します。updateSensor~関数は、sensor.jsファイル内で定義しています。
	;
	;   }
	;     updateSensorAcc(    ptrX, ptrY, ptrZ )
	;     updateSensorAccg(   ptrX, ptrY, ptrZ )
	;     updateSensorRot(    ptrA, ptrB, ptrG )
	;     updateSensororient( ptrA, ptrB, ptrG )
	;   }
	ptrAcc_x  = varptr( strAcceleration_x )
	ptrAcc_y  = varptr( strAcceleration_y )
	ptrAcc_z  = varptr( strAcceleration_z )
	ptrAccg_x = varptr( strAccelerationIncludingGravity_x )
	ptrAccg_y = varptr( strAccelerationIncludingGravity_y )
	ptrAccg_z = varptr( strAccelerationIncludingGravity_z )
	ptrRot_a  = varptr( strRotationRate_alpha )
	ptrRot_b  = varptr( strRotationRate_beta  )
	ptrRot_g  = varptr( strRotationRate_gamma )
	ptrOrient_a = varptr( strOrientation_alpha )
	ptrOrient_b = varptr( strOrientation_beta  )
	ptrOrient_g = varptr( strOrientation_gamma )
	
	exec_text = "{"
	exec_text+= "updateSensorAcc( "    + ptrAcc_x  + ", " + ptrAcc_y  + ", " + ptrAcc_z  + ");"
	exec_text+= "updateSensorAccg( "   + ptrAccg_x + ", " + ptrAccg_y + ", " + ptrAccg_z + ");"
	exec_text+= "updateSensorRot( "    + ptrRot_a  + ", " + ptrRot_b  + ", " + ptrRot_g  + ");"
	exec_text+= "updateSensorOrient( " + ptrOrient_a + ", " + ptrOrient_b + ", " + ptrOrient_g + ");"
	exec_text+= "}"

	goto *main

;------------------------------
;	奇麗に配置して値を描画
;------------------------------
*l_draw
	x1 = ginfo_cx
	y1 = ginfo_cy
	pos cx - 120
	picfprt "" + v
	pos x1, y1
	picfprt s
	drawBar m, v

	pos , ginfo_cy+4+5
	
	return

 値の取得は簡単になったのですが、データを表示する部分が少し複雑になっています。 数字が動いているだけでは、センサー値が正常に取得されているかがわかりません。そのため、棒グラフを使って視覚的に変化を表示しています。

 また、項目名称と取得したセンサー値を確認できるようにするため、文字で表示するようにしています。mes 命令ではパフォーマンスに問題があるため、画像フォントを使用しています。

その他への活用

 今回は加速度センサーや角速度センサーを用いてデータを取得しましたが、 ブラウザのAPIには、GPS位置情報を取得する機能などもあります。 その他にも、ブラウザならではの機能を活用する方法が存在します。(するはず。)

 今後、HSP3で他のブラウザ機能を使えるか試してみたいところですが、 手間がかかるため、本音は誰か調べてほしい…。

最新バージョン(HSP3.7β10)の紹介

 この記事を書いている間に、先日(2025/1/27)HSP3.7β10が公開されました。 この最新バージョンでは、加速度(ginfo_accel*)とジャイロセンサー値(ginfo_gyro*)の取得に対応しました。 加速度かジャイロが必要な場合は、このページで紹介したやり方よりもginfo_accel*命令かginfo_gyro*命令の利用を推奨します。 exec命令を使わないで済むならそれに越したことはありません。

HTML
var sensor_button = 1;

とすることで使えるようになるようです。こっちはiOS (Safari)にも対応しているみたいです。

関連記事

  1. HSP3Dish.jsでJavaScript 対象環境 execでJavaScriptを実行する HSP3Dish.jsにおけるスクリプトの最小構...
  2. HSP3Dish.jsでGPS座標を取得 Geolocation APIとは? Geolocation APIの基本的な使い方 エラー処理 位...