iPhoneの加速度センサーをPCから見たい!-2

やっとMacなしで加速度センサーの値をPCから見ることに成功。

と言うわけでiPhoneの加速度センサーの状態をPCで見れると色々楽しいんだと思うんですよ

の続き。

The Big5 application for Apple iPhone and iPod touch is a simplified gateway to your web apps.

↑を使って、PCのアプリに加速度センサの値を送信するサンプル。

使い方

  • pythonの動くマシンで、下の方のコードを全部ダウンロードする
  • ソースコードの中の192.168.0.6とか書いてあるところを動かすマシンのIPアドレスに変更する
  • cgiMotionSensor.pyを起動する
  • OPENボタンを押して、allowcube.txtを読み込む
    • ここまではPC上での操作。ここからはiPhoneの操作
  • iPhoneにはBig5をインストールしておく
  • Big5からPCのIPアドレス:8000/motiontest.htmlを読み込む
  • motionボタンを押下。
  • きっとPCのワイヤーフレームiPhoneの傾け方に応じて回転するはず

概要/ソースコード

  • Big5アプリからJavaScriptでローカルのHTTPサーバにXMLHttpRequestする。
  • cgiMotionSensor.py
    • PCで起動しておくGUIのアプリ。
    • HTTPサーバを別スレッドで起動する。
    • …最初はCGIになる予定でこんな名前になっている。
    • 下のボタンは右側からX軸の回転、Y軸の回転、Z軸の回転、奥行きの修正*1ワイヤーフレームのオブジェクトを開くボタン
    • OPENボタンからallowcube.txtを開く
#!/usr/bin/python
# -*- coding: utf-8 -*-

import BaseHTTPServer
import SimpleHTTPServer
import CGIHTTPServer
import threading
import wireFrame
from SimpleXMLRPCServer import *
from math import *

PORT = 8000

frame = wireFrame.wireFrame()
class motionHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_GET(self):
        path = self.path
        strReceiver = "motionreceiver.py"
        l = len(strReceiver)
        i = path.find(strReceiver)
        if( i==-1 ):
            SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
            return
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        self.wfile.write("<html></html>")
        rest = path[l+2:]
        flag = 0
        keyword = ""
        value = ""
        argList = {}
        for ii in range(len(rest)):
            c = rest[ii]
            if c=="&" or ii==len(rest)-1:
                argList[keyword] = value
                flag = 0
                value = ""
                keyword = ""
                continue
            if c=="=":
                flag = 1
                continue
            if flag==0:
                keyword += c
            else:
                value += c
        if 'x' in argList and 'y' in argList and 'z' in argList:
            x = float(argList['x'])
            y = float(argList['y'])
            z = float(argList['z'])

            degy = degrees(atan2(z,y))+180
            degx = -1*degrees(atan2(y,x))+180
            frame.xAxisRollAngle.set(degy)
            frame.zAxisRollAngle.set(degx)
            frame.angleChangedEventCall()
Handler = motionHandler
httpDaemon = threading.Thread(target=SimpleHTTPServer.test,args=(Handler,BaseHTTPServer.HTTPServer))
httpDaemon.setDaemon(True)
httpDaemon.start()

frame.mainloop()

print "end of file"
line,-100,0,0,100,0,0
line,0,-100,0,0,100,0
line,0,0,-100,0,0,100
sphere,100,0,0,10
sphere,0,100,0,10
sphere,0,0,100,10

line,-100,100,-100,100,100,-100
line,100,100,-100,100,-100,-100
line,100,-100,-100,-100,-100,-100
line,-100,-100,-100,-100,100,-100

line,-100,100,-100,-100,100,100,
line,100,100,-100,100,100,100,
line,100,-100,-100,100,-100,100,
line,-100,-100,-100,-100,-100,100,

line,-100,100,100,100,100,100
line,100,100,100,100,-100,100
line,100,-100,100,-100,-100,100
line,-100,-100,100,-100,100,100

text,130,0,0,x
text,0,130,0,y
text,0,0,130,z
  • motiontest.html
    • Big5アプリで開くhtmlファイル。
    • cgiMotionSensor.pyがたとえば192.168.0.6で起動していれば、192.168.0.6:8000/motiontest.htmlをBig5で開く。
<html>
  <head>
	<meta name="viewport" content="width = device-width"></meta>
	<script type="text/javascript" src="http://www.big5apps.com/api/bigfive.js"></script>
	<script src="./motiontest.js" type="text/javascript"></script>
  </head>
  <body>
	<form name="f">
	  <input type="button" onclick="logtest()" value="deviceInfo"/>
	  <input type="button" onclick="accelerationTest()" value="motion"/><br/>
	  x:<input type="text" name="x" value=0/>
		<br/>
	  y:<input type="text" name="y" value=0/>
		<br/>
	  z:<input type="text" name="z" value=0/>
		<br/>
	</form>
  </body>
</html>
  • motiontest.js
function handleDeviceInfo()
{
	alert(Device.model+":"+Device.id);
}
function logtest()
{
	Device.start(handleDeviceInfo);
}
var isBusy = false;
var req = new XMLHttpRequest();
req.onreadystatechange = function(){
	isBusy = false;
	if(req.readyState==4){
		if(req.status==200){
		}else{
			alert("XMLHttpRequest error");
		}
	}
}
function sendAcceleration(x,y,z)
{
	if( isBusy ){ return; }

	val = "x="+x+"&"+"y="+y+"&"+"z="+z;
	req.open("GET","http://192.168.0.6:8000/motionreceiver.py?"+val,true);

	req.send("");
	isBusy = true;
}
function handleAcceleration(x,y,z)
{
	document.f.x.value = x;
	document.f.y.value = y;
	document.f.z.value = z;
	sendAcceleration(x,y,z);
}
var flag = 0;
function accelerationTest()
{
	flag = !flag;
	if( flag ){
		Device.Acceleration.start(handleAcceleration);
	}else{
		Device.Acceleration.stop();
	}
}
#!/usr/bin/env python

import Tkinter
import tkFileDialog
import math

class wireFrame:
    def clickOpenFile(self):
        fileName = tkFileDialog.askopenfilename()
        self.openFile(fileName)
    def cnvMap(self,x,y):
#        return (x-self.width/2,y*-1+self.height/2)
        return (x-self.width/2,(y-self.height/2)*-1,0)

    def cnvCanvas(self,x,y,z):
        interval = self.viewPoint.get()*10 / (self.viewPoint.get()*10 + float(z))
        x = x*interval
        y = y*interval
        return (x+self.width/2,(y-self.height/2)*-1)

    def roll(self,x,y,degree):
        (orgX,orgY) = (x,y)
        orgR = math.sqrt(math.pow(orgX,2)+math.pow(orgY,2))
        if orgR==0:
            return (x,y)
        orgTheta = math.atan2(orgY/orgR,orgX/orgR)
        theta = math.radians(degree)+orgTheta
        x = math.cos(theta)*orgR
        y = math.sin(theta)*orgR
        return (x,y)

    def roll3DbySetting(self,x,y,z):
        x,y = self.roll(x,y,self.zAxisRollAngle.get())
        y,z = self.roll(y,z,self.xAxisRollAngle.get())
        x,z = self.roll(x,z,self.yAxisRollAngle.get())
        return (x,y,z)

    def roll3DbySettingReverse(self,x,y,z):
        x,z = self.roll(x,z,-self.yAxisRollAngle.get())
        y,z = self.roll(y,z,-self.xAxisRollAngle.get())
        x,y = self.roll(x,y,-self.zAxisRollAngle.get())
        return (x,y,z)

    def createObject(self,item):
        '''
        item
                => ('line',startX,startY,startZ,endX,endY,endZ)
                => ('sphere',centerX,centerY,centerZ,radius)
                => ('text',startX,startY,startX,text)
        '''
        if item[0]=='line':
            (itemType,sx,sy,sz,ex,ey,ez) = (item[0],
                                            int(item[1]),int(item[2]),int(item[3]),
                                            int(item[4]),int(item[5]),int(item[6]))
            (rsx,rsy,rsz) = self.roll3DbySetting(sx,sy,sz)
            (rex,rey,rez) = self.roll3DbySetting(ex,ey,ez)
            (lineSx,lineSy) = self.cnvCanvas(rsx,rsy,rsz)
            (lineEx,lineEy) = self.cnvCanvas(rex,rey,rez)
            line = self.canvas.create_line(lineSx,lineSy,
                                           lineEx,lineEy,
                                           fill=self.penColor,width=self.penWidth)
            obj = dict(id=line,itemType=itemType,coords=[sx,sy,sz,ex,ey,ez])
#            self.objects.append(obj)
            self.objects[line] = obj
            return obj
        elif item[0]=='sphere':
            (itemType,x,y,z,r) = (item[0],
                                  int(item[1]),int(item[2]),int(item[3]),int(item[4]))
            (rx,ry,rz) = self.roll3DbySetting(x,y,z)
            (cx,cy) = self.cnvCanvas(rx,ry,rz)
            sphere = self.canvas.create_oval(cx-r,cy-r,cx+r,cy+r,
                                             fill=self.penColor,outline=self.penColor)
            obj = dict(id=sphere,itemType=itemType,coords=[x,y,z,r])
#            self.objects.append(obj)
            self.objects[sphere] = obj
#            print sphere,itemType,x,y,z,r
            return obj
        elif item[0]=='text':
            (itemType,x,y,z,txt) = (item[0],
                                    int(item[1]),int(item[2]),int(item[3]),
                                    item[4])
            (sx,sy,sz) = self.roll3DbySetting(x,y,z)
            (cx,cy) = self.cnvCanvas(sx,sy,sz)
            txtObj = self.canvas.create_text(cx,cy,text=txt)
            obj = dict(id=txtObj,itemType=itemType,coords=[x,y,z],txt=txt)
            self.objects[txtObj] = obj
        else:
            pass
        pass
    def clearCanvas(self):
#         for obj in self.objects:
#             item,itemType,sx,sy,sz,ex,ey,ez = obj
#             self.canvas.delete(item)
        for id in self.objects.keys():
            self.canvas.delete(id)
        self.objects = {}
    def openFile(self,fileName):
        f = open(fileName)
        for row in f:
            row = row.replace('\n','')
            if row=='':continue
            item = row.split(',')
            obj = self.createObject(item)
        f.close()

    def angleChanged(self,event):
        self.angleChangedEventCall()
    def angleChangedEventCall(self):
#        for obj in self.objects:
        for id,obj in self.objects.iteritems():
            itemType = obj['itemType']
            if itemType=='line':
                item,itemType,(sx,sy,sz,ex,ey,ez) = obj['id'],obj['itemType'],obj['coords']
                (rsx,rsy,rsz) = self.roll3DbySetting(sx,sy,sz)
                (rex,rey,rez) = self.roll3DbySetting(ex,ey,ez)
                (lineSx,lineSy) = self.cnvCanvas(rsx,rsy,rsz)
                (lineEx,lineEy) = self.cnvCanvas(rex,rey,rez)
                self.canvas.coords(item,lineSx,lineSy,lineEx,lineEy)
            elif itemType=='sphere':
                item,itemType,(x,y,z,r) = obj['id'],obj['itemType'],obj['coords']
                (rx,ry,rz) = self.roll3DbySetting(x,y,z)
                (cx,cy) = self.cnvCanvas(rx,ry,rz)
                self.canvas.coords(item,cx-r,cy-r,cx+r,cy+r)
            elif itemType=='text':
                item,itemType,(x,y,z),txt = obj['id'],obj['itemType'],obj['coords'],obj['txt']
                (rx,ry,rz) = self.roll3DbySetting(x,y,z)
                (cx,cy) = self.cnvCanvas(rx,ry,rz)
                self.canvas.coords(item,cx,cy)
            
    def __init__(self):
        self.window = Tkinter.Tk()
        self.width = 800
        self.height = 600
        self.penColor = 'red'
        self.penWidth = 3
        self.canvas = Tkinter.Canvas(self.window,bg="white",
                                     width=self.width,height=self.height)
        self.canvas.pack()
        self.xAxisRollAngle = Tkinter.Scale(self.window, from_=0, to=360,
                                            orient=Tkinter.HORIZONTAL)
        self.xAxisRollAngle.bind("<ButtonPress-1>",self.angleChanged)
        self.xAxisRollAngle.bind("<ButtonRelease-1>",self.angleChanged)
        self.xAxisRollAngle.bind("<B1-Motion>",self.angleChanged)
        self.xAxisRollAngle.pack(side=Tkinter.LEFT);

        self.yAxisRollAngle = Tkinter.Scale(self.window, from_=0, to=360,
                                            orient=Tkinter.HORIZONTAL)
        self.yAxisRollAngle.bind("<ButtonPress-1>",self.angleChanged)
        self.yAxisRollAngle.bind("<ButtonRelease-1>",self.angleChanged)
        self.yAxisRollAngle.bind("<B1-Motion>",self.angleChanged)
        self.yAxisRollAngle.pack(side=Tkinter.LEFT);

        self.zAxisRollAngle = Tkinter.Scale(self.window, from_=0, to=360,
                                            orient=Tkinter.HORIZONTAL)
        self.zAxisRollAngle.bind("<ButtonRelease-1>",self.angleChanged)
        self.zAxisRollAngle.bind("<ButtonPress-1>",self.angleChanged)
        self.zAxisRollAngle.bind("<B1-Motion>",self.angleChanged)
        self.zAxisRollAngle.pack(side=Tkinter.LEFT);

        self.viewPoint = Tkinter.Scale(self.window, from_=1, to=100,
                                       orient=Tkinter.HORIZONTAL)
        self.viewPoint.bind("<ButtonRelease-1>",self.angleChanged)
        self.viewPoint.bind("<ButtonPress-1>",self.angleChanged)
        self.viewPoint.bind("<B1-Motion>",self.angleChanged)
        self.viewPoint.set(100)
        self.viewPoint.pack(side=Tkinter.LEFT);

        self.btnOpenFile = Tkinter.Button(self.window,text="open",command=self.clickOpenFile)
        self.btnOpenFile.pack(side=Tkinter.RIGHT)

        self.objects = {}
        
#        self.window.mainloop()
    def mainloop(self):
        self.window.mainloop()

if __name__=='__main__':
    wireFrame()

加速度センサーの返す値。

  • x:平面におき、左側に傾けたときに-1。右側に傾けたときに1。
  • y:垂直に置いて上側を天に向けたときに-1。下側を天に向けたときに1。
  • z:画面を上にして平面に置いたとき、-1。裏にしたときに1。

動作環境

Ubuntu10.04とiPhone3Gでしか確認していません。
他の環境とかだとどうなのかなー。

参考リンク

傾き角度を求める

加速度センサをジョイスティックみたいな方向キー替わりに

*1:触らないことw