2015/12/17

姿勢監視チェッカー作ってみた

by JINS MEME
JINS MEME Advent Calendar 2015 17日目の記事です。
姿勢監視.gif

JinsMEME × PhilipsHue

JinsMEMEの加速度センサーをインプットとして使って、アウトプット先をPhilipsHueのLEDライトにして、猫背になったら赤く点滅し、正しい姿勢になるとアラートなしの通常のライトに戻るというのを作ってみました。


デスクワーカーのために

ホワイトカラー、デスクワーカーのために。
PCでの長時間仕事をするとなるとどうしても、背中が曲がってきがちです。
猫背になると赤く点滅させるという物理的な現象を発生させ、まわりの人の迷惑にならないように、意識します。
またライトがちかちかして鬱陶しいので、反射的に良い姿勢を維持しようとするのでよいです。猫背にも前傾猫背や後傾猫背など左右のねじれなどがあると思いますが、今回は前傾になった際に加速度センサーx軸のある一定の敷居値を超えたらという判定でアラートしています。
日常的に姿勢を観察し、正しい姿勢を維持することによって、腰痛の予防や、日々の姿勢を見直すきっかけになればいいなと思っています。

仕組み

JINS MEME→iOS→Philips Hue
JINS MEMEからiOSにBluetoothでデータを送信、
iOS側データを取得、iOSからwifi経由でPhilips Hueを操作
言語はSwiftで書き、Hueを操作するためのSDKを利用しています。
The hue Apple SDK by Philips
https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX

必要なもの、環境

実装 

作成したAppCilentIDとClientSecretを以下の****の場所にセットします。
AppDelegate.swift
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        MEMELib.setAppClientId("*********************", clientSecret: "*********************")
        return true
    }
  • SwiftからObjective-Cのクラスを利用するためにBridging-Header.hを作成 HueSDK_iOSとSDKWizardの中のPHLoadingViewController,PHBridgeSelectionViewController,PHBridgePushLinkViewController プラス Meme用にMEMELibを記述
Bridging-Header.h
#ifndef Bridging_Header_h
#define Bridging_Header_h

#import "PHLoadingViewController.h"
#import "PHBridgeSelectionViewController.h"
#import "PHBridgePushLinkViewController.h"
#import 

#import 


#endif /* Bridging_Header_h */

Hue bridgeとの接続

詳細に関してはこちらをご覧ください。
http://www.developers.meethue.com/documentation/apple-api-guide

猫背になったら、アラート

MEMEからのリアルタイムデータ取得の部分と、Hueのライト変更箇所を抜粋
ライセンスはMITです。ご自由にどうぞ。
MMDataViewController.swift
    var memeRealTimeData:MEMERealTimeData?

    func memeRealTimeModeDataReceived(data: MEMERealTimeData!) {

        self.memeRealTimeData = data
        if let d = self.memeRealTimeData as MEMERealTimeData?{

            // 猫背判定
            let accX = d.accX
            if  (Int(accX) <= -3){

                // red
                self.updateLight(0, isAlert: true, isRandom: false)
            }
         }
    }

    func updateLight(hueValue:Int, isAlert:Bool, isRandom:Bool){

        let cache = PHBridgeResourcesReader.readBridgeResourcesCache()
        let bridgeSendAPI = PHBridgeSendAPI()

        for light in cache!.lights!.values {
            // don't update state of non-reachable lights
            if light.lightState!.reachable == 0 {
                continue
            }

            let lightState = PHLightState()
            if light.type == DIM_LIGHT {
                // Lux bulbs just get a random brightness
                lightState.brightness = Int(arc4random()) % 254
            } else {

                // アラートの場合
                if isAlert {
                    // 点滅中
                    if lightState.alert == ALERT_SELECT {
                        break
                    }
                    lightState.alert = ALERT_SELECT
                }

                // ランダム点灯
                if isRandom {
                    lightState.hue = Int(arc4random()) % 65535
                }else{
                    lightState.hue = hueValue
                }

                lightState.brightness = 254
                lightState.saturation = 254
            }

            // Send lightstate to light
            bridgeSendAPI.updateLightStateForId(light.identifier, withLightState: lightState, completionHandler: { (errors: [AnyObject]!) -> () in

                if errors != nil {
                    let message = String(format: NSLocalizedString("Errors %@", comment: ""), errors)
                    print("Response: \(message)")
                }
            })
        }
    }

ビルド実行

初期ロード時にHueとの接続、Scanして、MEMEと接続。
MEMEをかけた状態で、前傾姿勢になってみてください。

今後の展開

今回もJINS MEMEで遊んでみました。JINS MEME × Something(Thing to Thing)というのはいくらでもあります。
今回はローカルでのアラート点灯ですが、インターネット越しにリモートのデバイスを動かすようなことも可能で実装済みですので、公開していきたいと考えています。
AWS IoTでデバイス間の状態を検知して、M2M(Machine to Machine)の世界を実現できるのではないかと、わくわくしています。

ではまた。

2015/12/13

【Swift】NSXMLParserで文字列にアルファベットや数字、マルチバイト文字が含まれる場合、複数に分かれてパースされる件

SwiftでXMLをパースする際に、
NSXMLParserを使用するのですが、以下のような要素をパースする際に

aaa あいうえお abcd 春夏秋冬 123
マルチバイト+アルファベット文字列が要素内であったとすると、
それぞれ分かれて、一回目aaaで二回目あいうえお、三回目abcdの形で取得できてしまって、
一気に取れないのが不便。
フラグとかはないので手動で配列に貯めてから、閉じタグを読み込んだ時にreduceで再帰的に足して取得する

実装

NSXMLParserDelegateを指定
    var isTitle:Bool=false
    var parsedStrArr:Array<String>!
    var entries : NSMutableArray!
    var tmpEntry : NSMutableArray!

    override func viewDidLoad() {
        super.viewDidLoad()
        loadRss()
    }

    func loadRss() {

        // Sample feed url
        let rUrl: NSURL = NSURL(string: "https://hoge.co.jp/feed/?cat=5")!
        let rssUrl = rUrl

        parser = NSXMLParser(contentsOfURL: rssUrl)!
        parser.delegate = self

        //以下の1行の処理でparse関連の処理が行われる。
        let success:Bool = parser.parse()
        if success {
            print("パース成功")
        } else {
            print("パース失敗")
        }
    }

    // NSXMLParserDelegate
    // ここから下はParse関連処理

    func parserDidStartDocument(parser: NSXMLParser){
        entries = NSMutableArray()
    }

    // 開始タグを読み込んだ時よばれる - Start
    func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [NSObject : AnyObject]) {

        parsedStrArr = []

        if (elementName == "title"){
          // 配列初期化
            tmpEntry = NSMutableArray()
            entries.addObject(tmpEntry)
            isTitle = true;
        }
    }

    //閉じタグを読み込んだ時よばれる - End
    func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {

        if (elementName == "title"){
            // reduceによる再帰処理で文字列を足す
            tmpEntry.addObject(parsedStrArr.reduce(String()) { $0 + $1 })
            isTitle = false
        }
    }

    //タグ以外のテキストを読み込んだ時(タグとタグ間の文字列)
    func parser(parser: NSXMLParser, foundCharacters string: String?) {

        if(isTitle && tmpEntry != nil){
            // タイトル内文字列を追加していく
            parsedStrArr.append(string!)
        }
    }

    func parserDidEndDocument(parser: NSXMLParser){
        // ここで最終処理 entries
    }

参考

[iOS] NSXMLParser で文字列が1つのエレメントで複数に分かれてパースされる
http://www.kuma-de.com/blog/2012-07-20/3782

2015/12/09

メガネ美女子が流し目でシャッター by JINS MEME

JINS MEME Advent Calendar 2015 9日目の記事です。
釣っぽいタイトルですが、釣りではありません実現可能です。
前回はRICHO THETA Sのシャッターを流し目で押しました。
今回はJINS MEME をつけて流し目すると、iPhoneのカメラでシャッターを自動で押します。
その写真をInstagramやTwitterやFacebookに投稿します。

メガネ女子に使ってほしい!!!!

との熱烈な要望がありましたので、やっちゃいました。
是非メガネ女子なかた、要望あればAppStoreに出しますよ!
インスタで流し目選手権できたらいいな〜


JINS MEMEってなに?

見た目はいたって普通のメガネ。
ジャイロセンサーなどがあり、視線移動などのデータが取れます。
一日目の記事が非常にわかりやすく網羅されているかと思います。
http://qiita.com/mito_log/items/020a87996ed2b9d793e6


なんで流し目でシャッターなの?

流し目セルフィーとでも言い換えられますね。
TOP画の通り、流し目の方が、優雅でメガネ女子が美しく映えるからです
どこかしら余裕があり、風流があり、、知的でSな雰囲気がなんともたまりません。
bijyo.jpg
※写真はイメージです。これぞ、まさに流し目。
ウィンクしたら、シャッターとかがいいですが、まだ片目だけのまばたき検知はできない?(動作検証したところ片目だけ、まばたきをしても検知できない)ので、
今後、できるようにしてもらいたいところです。JINSさん!


仕組み

JINS MEMEからリアルタイムに流し目(視線移動、右or左)検知データを取得、
iPhone側のフロントカメラを起動しておいて、流し目トリガーで撮影といったシンプルな流れです。
その後、撮影した静止画をFacebook, Twitter, Instagramに投稿をそれぞれ選択可能。

流れ

その1. アプリ起動後、メガネ女子が流し目をする

メガネjyoshi.jpg

その2. iPhone側でシャッターが自動で切られ、画像保存

これはiPhoneを固定しておいてもいいかと。

その3. 下からメニューがせり上がってくる。

IMG_7830 (2).PNG
インスタグラムの欄を選択

その4. Instagramで投稿

IMG_7831 (2).PNG
そのまま投稿したいところですが、
インスタグラムのAPIで直接投稿するようなものは用意されていません。
ですので撮影した写真をそのままInstagramのアプリに渡します。


必要なもの、環境


実装 

言語はiOSアプリ開発に使用されるSwift(2.0)で以前の記事でサンプルを書きましたがそちらをベースに実装します。
http://qiita.com/you_matz/items/4f0f83289cb5f4ee5980
JINS MEMEとの接続などはこちらをベースに
実装は以下のgistを確認ください。
流し目でシャッター音はなりますが、なしの実装も可能ではありますが審査でrejectされるおそれがありますよ。
https://gist.github.com/manchan/d270366af589034290d5


反応はいいの? すぐ撮影できるの?

今回はメガネからデータ送信→iOSで取得なのですぐです。
JINS MEMEはiOSデバイスとBluetooth通信で約20Hzの周期、つまり0.05秒毎にリアルタイムデータ送信なので、
wifi経由で操作するTHETA Sの時とは違って、即撮影が可能です。


おわりに

以上でメガネ美女子が流し目でシャッター by JINS MEMEは終わりです。
今回もJINS MEMEを使って遊んでみました。

MEME女子の方いましたら是非アプリ化しますので、ご連絡くださいw
もちろん、それ以外の方も、iPhoneを固定した場所に置いて、
JINS MEMEを使って流し目で定点セルフィーしたいなどご要望があれば、ご連絡ください。
これからもJINS MEMEで遊んでいきます。
この投稿を読んでJINS MEME欲しい!おもしろそう!なにかやりたい!と思ってくれる方がいれば幸いです。
ではまた。

2015/12/07

【JINS MEME × THETA S】流し目 or まばたきシャッターの実装

JINS MEME Advent Calendar 2015 7日目の記事です。昨日の記事は@mito_logさんによる JINS MEMEのデータをxx秒おきにGoogle SpreadSheetに流すでした。

今回はJinsMEME ESの3点眼位センサーをインプットとして使って、アウトプット先を最近購入したTHETA Sで遊んでみようかなと思い、実装してみました。
JINS MEMEは、どこを見ているかという視線判定とまばたきの早さ、強さというデータが取れるのが魅力的です。
今回は流し目(右か左)に視線移動するとTHETA Sのシャッターを押すという誰得なことをやります。

JINS MEMEってなに?

一日目の記事が非常にわかりやすく網羅されているかと思います。
http://qiita.com/mito_log/items/020a87996ed2b9d793e6

THETA Sってなに?

全天球の360度の写真、動画が取れるカメラデバイスです。
RICOH THETA Advent Calendarの一日目の記事を参考にしてください。
http://panora.tokyo/3945/
どんな写真をとっても360度で撮れるので、おもしろいです。
写真を見たほうがわかりやすいので、京都の紅葉の写真と市内一望の写真をあげていますのでこちらを見てグリグリ触って確認してみてください。下の写真にフォーカスを当てて、キーボードの上下左右を押しても360度、見ることができますよ!
京都は今年最後の紅葉風景です。 また来年。 #theta360 - Spherical Image - RICOH THETA

京都市内一望 #theta360 - Spherical Image - RICOH THETA



なんで流し目でシャッターなの?
  • THETA Sはボタンを押したらすぐ写真がとれるのですが、どうしても指が写ってしまう。
  • 上記の紅葉の写真みたいにスマホアプリからシャッター押せますがどうしてもうつむきがちになって顔が映らない
  • JinsMEMEでまばたき検出したら撮影でもいいが、連写しすぎちゃうので。まばたきの速さと強さで強くor弱くまばたきすればシャッターというのは実装しましたが、わかりやすいのは視線移動かなと思ったので。
という理由から流し目したら、シャッターを押すということに決めました。
それでもまぁまぁな頻度で流し目はするので、右向いて左向いたらシャッターとかでもいいと思います。

仕組み

ざっくり言うとJinsMEMEからiOSにBluetoothでデータを送信、
iOS側データを取得、iOSからwifi経由でTHETA Sを操作
THETAはHTTPサーバとして動作し、APIリクエストを送って操作ができます。
詳細はこちら。現時点(2015年12月6日)での
RICHO THETA API v2 Reference
https://developers.theta360.com/ja/docs/v2/api_reference/
言語はSwiftで以前の記事でサンプルを書きましたがそちらをベースに実装します。
http://qiita.com/you_matz/items/4f0f83289cb5f4ee5980

必要なもの、環境

実装 

下準備

こちらからJins Memeのサンプルをダウンロード
https://github.com/manchan/JinsMeme-Swift-Sample
JinsMEME developerサイトからアプリを作成
https://developers.jins.com/ja/login/?goto=/apps/create/
作成したAppCilentIDとClientSecretを以下の****の場所にセットします。
AppDelegate.swift
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        MEMELib.setAppClientId("*********************", clientSecret: "*********************")
        return true
    }
THETA Sのシャッターを押すために、
HTTPネットワークライブラリであるAlamofireを利用します。
use_frameworks!
pod 'Alamofire', '~> 2.0'

THETA Sとのセッションの開始

デバイスとのセッションを開始し、sessionIdを取得します。
MMDataViewController.swift
import Alamofire

    var ssid:String?

    override func viewDidLoad() {
        super.viewDidLoad()
        sessionStart()
    }

    // セッション開始
    private func sessionStart() {

        let parameters = [
            "name": "camera.startSession",
            "parameters": []
        ]

        Alamofire.request(.POST, "http://192.168.1.1/osc/commands/execute", parameters: parameters, encoding: .JSON)
            .responseJSON { _, _, result in

                switch result {
                case .Success(let data):
                    let data_dic = data as! NSDictionary
                    if let ssid = data_dic["results"]!["sessionId"] as? String{
                        self.ssid = ssid
                    }
                case .Failure(let data, let error):
                    print("Request failed with error: \(error)")
                }
        }
    }

THETA Sで静止画撮影

静止画撮影するためのメソッドです。
セッション開始して、取得したsessionIdを利用します。
撮影に成功すると、takePicture Success!!とアラートを出します。
MMDataViewController.swift
    // 静止画撮影
    private func takePicture(){

        let parameters = [
            "name": "camera.takePicture",
            "parameters": [
                "sessionId": self.ssid!
            ]
        ]

        Alamofire.request(.POST, "http://192.168.1.1/osc/commands/execute", parameters: parameters, encoding: .JSON)
            .responseJSON { _, _, result in

                switch result {
                case .Success(let data):
                    let data_dic = data as! NSDictionary
                    print("takePicture Success!!")
                    UIAlertView(title: "撮影test", message: "takePicture Success!!", delegate: nil, cancelButtonTitle: "OK").show()
                case .Failure(let data, let error):
                    print("Request failed with error: \(error)")
                }
        }
    }

JINS MEMEからリアルタイムデータの取得,視線移動からシャッター

memeRealTimeModeDataReceivedメソッドから取得したリアルタイムデータを利用します。
tableViewのcellForRowAtIndexPathデリゲートメソッドから視線を右に動かした箇所を抜粋
MMDataViewController.swift
            case 6:

                /****************
                視線が右に動いたかどうかを示す整数値
                0:なし
                1:移動検知ー小
                2:移動検知ー中
                3:逆移動検知ー大
                4:逆移動検知ー特大
                ****************/


                label = "Eye Move Right";
                value = NSString(format: "%d", data.eyeMoveRight) as String

                if value != "0"{

                    self.takePicture()
                    let alertController = UIAlertController(title: "右移動", message: "", preferredStyle: .Alert)
                    let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action:UIAlertAction!) in
                    }
                    alertController.addAction(cancelAction)
                    let OKAction = UIAlertAction(title: "OK", style: .Default) { (action:UIAlertAction!) in
                    }
                    alertController.addAction(OKAction)

                    self.presentViewController(alertController, animated: true, completion:nil)

                }

                break
視線が右に動いた場合、シャッターを押し、右移動というアラートを出しています。
これで、JINS MEME×THETA Sで静止画を撮影するということが可能となります。

ビルド実行

THETA Sの電源を入れ、iPhoneのWifi設定でTHETAのWifiに接続します。
その後、ビルド実行して、JINS MEMEデバイスとの接続で完了。
視線移動して、試してみてください。

シャッターを無音にしたい場合(2015/12/13追記)

_shutterVolumeを0にすることで、シャッター音なしも可能となります。
デフォルトで音量は100になっています。


MMDataViewController.swift
    func setShutterVolumeZero() {

        let parameters = [
            "name": "camera.setOptions",
            "parameters": [
                "sessionId": self.ssid!,
                "options": [
                     "_shutterVolume":0
                ]
            ]
        ]

        Alamofire.request(.POST, commandExecURL, parameters: parameters, encoding: .JSON)
            .responseJSON { _, _, result in

                switch result {
                case .Success(let data):
                    let data_dic = data as! NSDictionary
                    print(data_dic)
                case .Failure(let data, let error):
                    print("Request failed with error: \(error)")
                }
        }
    }

番外編(弱まばたきでシャッター)

まばたきのスピードでゆっくりまばたきしたら、撮影しますか?というアラートを出して、
OKを押すと、シャッターを押します。アラートは別になしでもいいかと思います。
MMDataViewController.swift
            case 8:

                /****************
                まばたきのスピード(Millisecond)
                ****************/

                label = "Blink Speed";
                value = NSString(format: "%d", data.blinkSpeed) as String
                // 50以下であれば
                if Int(value) != 0 &&  Int(value) < 50 {
                    let alertController = UIAlertController(title: "撮影しますか?", message: "", preferredStyle: .Alert)
                    let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action:UIAlertAction!) in
                    }
                    alertController.addAction(cancelAction)
                    let OKAction = UIAlertAction(title: "OK", style: .Default) { (action:UIAlertAction!) in
                        if let sid = self.ssid as String? {
                            self.takePicture()
                        }
                    }
                    alertController.addAction(OKAction)
                    self.presentViewController(alertController, animated: true, completion:nil)

                }
                break

ぶっちゃけ反応どうなの?

視線を右に動かしてから、シャッターまでやはりタイムラグはあります。
そこはJINS MEME→iOS→THETA SでiOS経由なのでしょうがない部分ではあります。
そこがJINS MEMEのBluetooth→THETAとかに直接コマンド送れれば...もっと反応は早くなるでしょう。でも視線移動から体感一秒以内なので十分なスピードです。

おわりに

以上で【JINS MEME × THETA S】流し目シャッターの実装は終わりです。
まだまだJINS MEME × ハード連携は組み合わせはいくらでもあると思うので、これからもやっていきたいなと思っています。この投稿を読んでJINS MEME or THETA Sが欲しい!おもしろそうと思ってくれる方がいれば幸いです。
別でAppleWatchからTHETAをコントロールするようなことはしたので、またRICHO THETA Advent Calendarの方で書きます。