未来が家にやってきた！！空中に浮遊している映像を制御するぞ！

みなさんこんにちわ
hurusuです。

今日は、空中映像について触れようと思います。
SFの世界(スターウォーズとか、ガンダムとかナデシコとか)でたまに出てきますよね。空に映像が浮いてて、会話したり触って動かしたり。
あれってすごい浪漫がありますよね。いつかそういう時代が来るのかなと常々思っていましたが、
まさに未来が近づいて来ているようです。

空中映像を実現する技術
-
空中映像の技術として以下のようなものがあるようです。
・AIプレート技術
・再帰反射シート技術
・DCRA技術
・アイリス面型空中結像ディスプレイ
・ホログラム技術
・ライトフィールドディスプレイ
・プラズマ発光技術
・VR、AR

※以下より抜粋
https://www.fuji-keizai.co.jp/market/17087.html


今回の記事では、再帰反射シート技術を使って空中映像を浮かびあがらせてみようと思います。
(本当はDCRAが良かったんだけど、入手方法がなかったから...)

買ったもの
-
再帰反射シートの空中映像開発キットAirWitch(サンプル販売品)・・・11600円(送料込み)
http://airwitch.jp/airwitch_for_iphone.html

こちら株式会社コトさんが出している映像化キットです。
iPhone用なのでそれほど大きくないです。ipad用は3万超えるので流石に手が出せない^^;
ちなみに午後に注文したのですが次の日には自宅に届きました。すごく対応が早い会社さんですね。感謝です。

実際に組み立てた
-
箱を開けた時
![airwitch1.png](https://i.imgur.com/ZDWrwvh.jpg)

完成(1５分くらい)
![airwitch2.png](https://i.imgur.com/y6o8Tv7.jpg)


空中に映像を浮かび上がらせた
-
![airwitch3.png](https://i.imgur.com/TSjasbd.jpg)
画像だとちょっと分かりづらいかもしれませんが、
空中に平面の映像が浮いています。かっこいい！！これが未来や！！

ただね、サイズが小さいのが残念。やはり無理してでもipadサイズを選ぶべきだったか...

空中映像動かしたい！！どうしよう！？
-
みてわかる通り、iphoneを筐体の中(ミラー直下)に配置するので、画面タッチができず操作できません
これはシステム的な問題ではなく、空中映像自体がディスプレイの光源を反射しているのでそもそもそもタッチとかして遮断したら駄目ということでもあります。
ということで、今回はiphoneを直接触らずに画面を操作するために色々トライアンドエラーしてみようと思います。

とり合えず、真っ先に考えたのは以下のデザイン
![designe1.png](https://i.imgur.com/ShMb3R2.jpg)
・ジェスチャー認識センサのLeapMotion
・BLEオンボードのマイコンGenuino101
これを使えば、iphoneとGenuinoをBLEで繋いで制御の伝達ができるはずだ！！

ただ、問題が発生しました。Leapmotion届きません。
Amazon.jpじゃないところで注文したので、中々発送されない...
というわけで、Leapmotion自体は別の機会にして、
今回はTBDで↓の妥協案の構成でいきます。ジェスチャ操作は出来ないけど、
まずはBLEでなんらかの伝達できてしまえば、あとはなんとかなるんじゃないかと。

<妥協案>
センサのところは、去年ちょっと使って放置してた超音波センサ使います。
距離しか取れないので出来ることは少ないですが、とりあえず指を近づけて、なんか動けばいいやということで...
![designe2.png](https://i.imgur.com/H0GB9WT.jpg)

今回のレシピ(メカ)
-
・iphone7 plus
・Genuino 101　 5000円くらい  https://www.switch-science.com/catalog/2670/
・超音波センサ
・ブレッドボード
・ジャンプワイヤ
・USBケーブル A-Mini(B) 249円
・ブレッドボード 1081円
・超音波センサ HC-SR04 https://www.switch-science.com/catalog/1606/ 不具合があるので現在販売停止らしい？
・ACアダプター 9V 1.3A 

今回のレシピ(ソフト)
-
iphone向け
・IDE:XCode9.2
・Language:Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2)
・lib:CoreBluetooth

Genuino101向け
・Arduino IDE 1.8.5 https://www.arduino.cc/en/Main/Software
・Language:Arduino言語
・lib:CurieBLE

ちなみに、Swift・CoreBluetoothは今まで触ったことありませんw
オプショナルとか聞いたことないSwiftの言語仕様に混乱して時間をロストのはここだけの話
あと、画面のアニメーションとかCGとかそういうのも出来ないので、
とりあえずセンサに手を近づけたら表示している画像が切り替わるくらいを今回の目標にしました。



ソース
-
```swift:ViewController.swift
import UIKit
import CoreBluetooth

class ViewController: UIViewController ,CBCentralManagerDelegate ,CBPeripheralDelegate {
    var centralManager: CBCentralManager!
    var serviceUUID : CBUUID!
    var peripheral : CBPeripheral!
    var timer: Timer!
    var distance: Double = 0
    

    //centralManagerの状態変化を取得する
    //CBCentralManagerの状態が変化するとCBCentralManagerDelegateプロトコルの
    //centralManagerDidUpdateStateが呼ばれる@requiredのため、必ず実装が必要

    func centralManagerDidUpdateState(_ central: CBCentralManager) {
//        print ("state: \(central.state)")
        switch central.state {
        case .poweredOn:
            //スキャン開始
//            print("スキャン開始")
            self.centralManager.scanForPeripherals(withServices: nil, options: nil)
            break;
        default:
            break;
        }
    }
    
    //  発見したペリフェラルを出力
/*
    func centralManager(_ central: CBCentralManager,
                        didDiscover peripheral: CBPeripheral,
                        advertisementData: [NSObject : AnyObject],
                        rssi RSSI: NSNumber)
*/
    //  スキャン結果を取得
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        // GenuinoのUUID
        if peripheral.identifier.uuidString == "3035CA98-305A-BFB7-95C4-208AB26FA827"
        {
//            print("Geuino found: \(peripheral.identifier)")
            self.peripheral = peripheral;
            /* 見つかったのでスキャン停止 */
            centralManager.stopScan()
            //接続開始
            self.centralManager.connect(self.peripheral, options: nil)
        }
    }
    //  接続成功時に呼ばれる
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
//        print("Connect success!")
        
        // サービス探索結果を受け取る為にデリゲートをセット
        peripheral.delegate = self
        
        // サービス探索開始
        peripheral.discoverServices(nil)
        
        
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        
        //サービス取得
        guard let services = peripheral.services , services.count > 0 else {
            print("no services")
            return
        }
//        print("\(services.count) 個のサービスを発見！ \(services)")
        
        for service in services {
            // キャラクタリスティック探索開始
            peripheral.discoverCharacteristics(nil, for: service)
        }
    }
    
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if let error = error {
            print("エラー: \(error)")
            return
        }
        
        guard let characteristics = service.characteristics , characteristics.count > 0 else {
            print("no characteristics")
            return
        }
 //       print("\(characteristics.count) 個のキャラクタリスティックを発見！ \(characteristics)")

        for characteristic in characteristics {
        
        //Genuino→iphoneへのキャラクタリスティックの更新通知ができないようなので、諦める
        //peripheral.setNotifyValue(true, for: characteristic)
            
        // Readプロパティを持つのキャラクタリスティックの値を読み出す
         if characteristic.properties.contains(.read) {
                peripheral.readValue(for: characteristic)
            }
        }
    }
    
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        if let error = error {
            print("Notify状態更新失敗...error: \(error)")
        } else {
            print("Notify状態更新成功！characteristic UUID:\(characteristic.uuid), isNotifying: \(characteristic.isNotifying)")
        }
    }
    
    // データの呼び出し結果を取得する
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        var byte: [CUnsignedChar] = [0,0,0,0,0,0,0,0,0,0,0,0];
        // 1バイト取り出す
        characteristic.value?.copyBytes(to: &byte, count: 12)
        print("byte: \(byte)")
        let str : String = String(cString: byte)
        if(Double(str)! == nil)
        {
            print("error")
            return
        } else {
         distance = Double(str)!
        }
        //print("str: \(str)")
        setImageView()
        self.centralManager.cancelPeripheralConnection(peripheral)
    }
    
    //  接続失敗時に呼ばれる
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        print("Connect failed...")
    }
    
    override func viewDidLoad() {
        distance = 0.0
        super.viewDidLoad()
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
        timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
        timer.fire()
    }
    /* タイマをトリガにペリフェラルに繋ぎにいく */
    @objc func update(tm: Timer) {
        
        self.centralManager = CBCentralManager(delegate: self, queue: nil)
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    /* 表示を更新する */
    func setImageView(){
     let viewimage:UIImage //= UIImage(named:"images1.jpg")!
        
        if (distance > 30)
        {
            viewimage = UIImage(named:"images1.jpg")!
        } else if ( distance > 15 ){
            viewimage = UIImage(named:"images3.jpg")!
        } else {
            viewimage = UIImage(named:"images4.jpg")!
        }
        
        // UIImageView 初期化
        let imageView = UIImageView(image:viewimage)
        
        // 画面の横幅を取得
        let screenWidth:CGFloat = view.frame.size.width
        let screenHeight:CGFloat = view.frame.size.height
        
        // 画像の中心を画面の中心に設定
        imageView.center = CGPoint(x:screenWidth/2, y:screenHeight/2)
        
        self.view.addSubview(imageView)
        
    }
}
```

```cpp:skech.ino
#include <CurieBLE.h>

/**************************************
 *  BLE通信向け定義、変数
 *************************************/
#define  BLE_LOCAL_NAME "Advent2017"
#define  BLE_UUID_SERVICE "4200267A-E0F9-4554-924B-374DFF80F108"
#define  BLE_UUID_DISTANCE "81B70873-F22B-46B3-BBBD-5CD3E673245F"

BLEPeripheral blePeripheral;  // BLE Peripheral Device (the board you're programming)
BLEService Advent2017Service(BLE_UUID_SERVICE); // Advent2017
BLECharacteristic distanceCharacteristic(BLE_UUID_DISTANCE, BLERead, 20);


/********************************
 * ピン定義
 * D11 HC-SR04トリガー (in)                                                                                                                                                             
 * D12 HC-SR04エコー   (out)
 ********************************/
#define PIN_ECHO   12
#define PIN_TRIG   11



/********************************
 * 超音波センサ HC-SR04用変数
 ********************************/
struct ST_HC_SR04 {
  int duration;
  float distance;
  float distance_bk;
};
ST_HC_SR04 hc_sr04;

/********************************
 * 障害物までの距離を計測する
 ********************************/
void measure_distance(void)
{
  /**************************************************************************
    トリガ端子を10us以上Highにしてください。
    このセンサモジュールが40kHzのパルスを8回送信して受信します。
    受信すると、出力端子がHighになります。
    出力端子がHighになっている時間がパルスを送信してから受信するまでの時間です。
    出力端子がHighになっている時間の半分を音速で割った数値が距離です。
  ***************************************************************************/
  // トリガをLowにする
  digitalWrite(PIN_TRIG,LOW);
  delayMicroseconds(1);

  // トリガを11μ秒Highにする (センサが40kHzのパルスを8回送信)
  digitalWrite(PIN_TRIG,HIGH);
  delayMicroseconds(11);

  // トリガをLowに落とす
  digitalWrite(PIN_TRIG,LOW);

  // エコーがhighになるまでの時間を計測する(戻り値はパルスの長さ(μ秒))
  hc_sr04.duration = pulseIn(PIN_ECHO,HIGH);

  if (hc_sr04.duration>0) {
    // 時間の半分を音速で割る
    hc_sr04.distance_bk = hc_sr04.distance;
    hc_sr04.distance = hc_sr04.duration/2;    
    hc_sr04.distance = hc_sr04.distance*340*100/1000000; // ultrasonic speed is 340m/s = 34000cm/s = 0.034cm/us
  }
}
/**************************************
 *  void textCharacteristicConnectHandler(BLECentral) 
 *  BLEコネクト時に割り込みで呼ばれます。
 *************************************/
void textCharacteristicConnectHandler(BLECentral& central) {
  // LED 点灯
  digitalWrite(7, HIGH);
}

/**************************************
 *  void textCharacteristicConnectHandler(BLECentral) 
 *  BLEディスコネクト時に割り込みで呼ばれます。
 *************************************/
void textCharacteristicDisconnectHandler(BLECentral& central) {
  // LED 消灯
  digitalWrite(7, LOW);
}
/**************************************
 *  void textCharacteristicConnectHandler(BLECentral) 
 *  BLE書き込み時に割り込みで呼ばれる関数(現在未使用)
 *************************************/
void textCharacteristicWritten(BLECentral& central, BLECharacteristic& characteristic) {
  // do nothinng
}



void setup() {
  /********************************
   * BLE通信設定
   *******************************/
  blePeripheral.setLocalName(BLE_LOCAL_NAME);
  blePeripheral.setAdvertisedServiceUuid(Advent2017Service.uuid());
  blePeripheral.addAttribute(Advent2017Service);
  blePeripheral.addAttribute(distanceCharacteristic);

  //BLEイベント割り込み定義
  blePeripheral.setEventHandler(BLEConnected, textCharacteristicConnectHandler);
  blePeripheral.setEventHandler(BLEDisconnected, textCharacteristicDisconnectHandler);

  //サービス提供開始
  blePeripheral.begin();
  
  Serial.begin(9600);
  while(!Serial) {
    ;
  }
  /********************************
   * Pinの設定: トリガ端子に出力
   * Pinの設定: エコー端子から入力
   *******************************/
  pinMode(PIN_TRIG,OUTPUT);
  pinMode(PIN_ECHO,INPUT);



  /*****************************
   * 障害物への距離計測
   ****************************/
  measure_distance();
  
}


void loop() {

      // 障害物までの距離を計測
      measure_distance();
    
      String  distance = String(hc_sr04.distance);
      char charArray[20] = {};
    
      distance.toCharArray(charArray, distance.length() +1 );
    
      distanceCharacteristic.setValue((unsigned char *)charArray,distance.length());
      Serial.println(hc_sr04.distance);
      delay(500);
}
```
これでとりあえず、センサからの距離(指とか)で映像が切り替わります。そこそこ動く。
たた、コードはとりあえず動きゃいいの精神でトライアンドエラーで１日で適当に作ったのでバグがあったりします。

課題とかバグ(1).
Genuino側で、センサ値をキャラクタリスティックに設定しても、状態更新の通知が発火されてない。
(iphoneのCoreBluetoothのデバッグ中に、キャラクタリスティックの情報を見たときnotifyingがfalseになってたのでそういうことだと思う)
とりあえず、ペリフェラル(Genuino101)の値変化の更新通知を受け取ってのイベントハンドルは出来ないのでタイマでガリガリ取りに行くことにした。
タイマは1000msec周期。500msecにすると、情報がとってこれなくなった。
あと、スキャン-接続-データ取得-ペリフェラル遮断をタイマ周期で何度もやってることが原因なのか、アプリ実行中にCoreBlueToothが頻繁に警告出していた。
これに関しても対処方法調べる時間ないので放置した。
あと、上記に伴い頻繁にコネクションしなおしてしまうためか、動かしてるとたまにGenuinoがしんでる。それにつられるようにアプリも死ぬ。
イベントハンドラの動きががわからないのだが、もしかしたら排他的な話かもしれない。もっと単純なボンミスの可能性もある。

課題とかバグ(2).
Genuino側で、センサ値を格納するキャラクタリスティックに設定したUUIDが有効にならない(UUIDが設定した覚えがない不明な値)。これもわからない。サービスのUUIDは効いてるんだがわからぬ...まぁいいや。

今更LeapMotionが届いたので触ってみる
-
投稿日直前になってLeapMotionが届いた。今更だけど少し触ってみる。
デザインいけてる...けどフリスク感がいなめないw
![designe1.png](https://i.imgur.com/VtuiI2u.jpg)

お試しアプリを試してみた
![designe1.png](https://i.imgur.com/oXpDWDt.jpg)

手の再現率すごいw関節までくっきりwすごい時代になったものだなあ

LeapMotion SDKを使ってみる。とりあえずGenuinoに、シリアル通信で伸ばしている指本数を送ってみる。
-
・開発環境 VS2017　VC++
・LeapMotion SDK 3.2.1

```cpp:advent2017.cpp
#include "stdafx.h"
#include <iostream>
#include <cstring>
#include "Leap.h"
#include "tchar.h"
#include <windows.h>
#include <stdlib.h>
#include <string>

HANDLE hCom;


int ReadSerialport(HANDLE hCom, unsigned char * pdata, int size);
int WriteSerialport(HANDLE hCom, unsigned char * pdata, int size);
BOOL GetSerialportBaud(HANDLE hCom, DWORD * pBaud);
BOOL CloseSerialport(HANDLE hCom);
HANDLE OpenSerialport(LPCTSTR lpFileName);


using namespace Leap;

class SampleListener : public Listener {
  public:
    virtual void onInit(const Controller&);
    virtual void onConnect(const Controller&);
    virtual void onDisconnect(const Controller&);
    virtual void onExit(const Controller&);
    virtual void onFrame(const Controller&);
    virtual void onFocusGained(const Controller&);
    virtual void onFocusLost(const Controller&);
    virtual void onDeviceChange(const Controller&);
    virtual void onServiceConnect(const Controller&);
    virtual void onServiceDisconnect(const Controller&);
    virtual void onServiceChange(const Controller&);
    virtual void onDeviceFailure(const Controller&);
    virtual void onLogMessage(const Controller&, MessageSeverity severity, int64_t timestamp, const char* msg);
};

const std::string fingerNames[] = {"Thumb", "Index", "Middle", "Ring", "Pinky"};
const std::string boneNames[] = {"Metacarpal", "Proximal", "Middle", "Distal"};

void SampleListener::onInit(const Controller& controller) {
  std::cout << "Initialized" << std::endl;
  hCom = OpenSerialport((LPCTSTR)"COM8");
}

void SampleListener::onConnect(const Controller& controller) {
  std::cout << "Connected" << std::endl;
}

void SampleListener::onDisconnect(const Controller& controller) {
  // Note: not dispatched when running in a debugger.
  std::cout << "Disconnected" << std::endl;
}

void SampleListener::onExit(const Controller& controller) {
  std::cout << "Exited" << std::endl;
  CloseSerialport(hCom);
}

void SampleListener::onFrame(const Controller& controller) {


  // Get the most recent frame and report some basic information
  const Frame frame = controller.frame();
#if 0
  std::cout //<< "Frame id: " << frame.id()
           // << ", timestamp: " << frame.timestamp()
            << ", hands: " << frame.hands().count()
            << ", extended fingers: " << frame.fingers().extended().count() << std::endl;
#endif
  WriteSerialport( hCom,  (unsigned char*)std::to_string(frame.fingers().extended().count()).c_str(), 1);


  HandList hands = frame.hands();

  for (HandList::const_iterator hl = hands.begin(); hl != hands.end(); ++hl) {
    // Get the first hand
    const Hand hand = *hl;
    std::string handType = hand.isLeft() ? "Left hand" : "Right hand";
#if 0
    std::cout << std::string(2, ' ') << handType << ", id: " << hand.id()
              << ", palm position: " << hand.palmPosition() << std::endl;
#endif
    // Get the hand's normal vector and direction
    const Vector normal = hand.palmNormal();
    const Vector direction = hand.direction();
#if 0
    // Calculate the hand's pitch, roll, and yaw angles
    std::cout << std::string(2, ' ') <<  "pitch: " << direction.pitch() * RAD_TO_DEG << " degrees, "
              << "roll: " << normal.roll() * RAD_TO_DEG << " degrees, "
              << "yaw: " << direction.yaw() * RAD_TO_DEG << " degrees" << std::endl;
#endif
    // Get the Arm bone
    Arm arm = hand.arm();
#if 0
    std::cout << std::string(2, ' ') <<  "Arm direction: " << arm.direction()
              << " wrist position: " << arm.wristPosition()
              << " elbow position: " << arm.elbowPosition() << std::endl;
#endif
    // Get fingers
    const FingerList fingers = hand.fingers();
    for (FingerList::const_iterator fl = fingers.begin(); fl != fingers.end(); ++fl) {
      const Finger finger = *fl;
#if 0
      std::cout << std::string(4, ' ') <<  fingerNames[finger.type()]
                << " finger, id: " << finger.id()
                << ", length: " << finger.length()
                << "mm, width: " << finger.width() << std::endl;
#endif
      // Get finger bones
      for (int b = 0; b < 4; ++b) {
        Bone::Type boneType = static_cast<Bone::Type>(b);
        Bone bone = finger.bone(boneType);
#if 0
        std::cout << std::string(6, ' ') <<  boneNames[boneType]
                  << " bone, start: " << bone.prevJoint()
                  << ", end: " << bone.nextJoint()
                  << ", direction: " << bone.direction() << std::endl;
#endif
      }
    }
  }

  if (!frame.hands().isEmpty()) {
    std::cout << std::endl;
  }

}

void SampleListener::onFocusGained(const Controller& controller) {
  std::cout << "Focus Gained" << std::endl;
}

void SampleListener::onFocusLost(const Controller& controller) {
  std::cout << "Focus Lost" << std::endl;
}

void SampleListener::onDeviceChange(const Controller& controller) {
  std::cout << "Device Changed" << std::endl;
  const DeviceList devices = controller.devices();

  for (int i = 0; i < devices.count(); ++i) {
    std::cout << "id: " << devices[i].toString() << std::endl;
    std::cout << "  isStreaming: " << (devices[i].isStreaming() ? "true" : "false") << std::endl;
    std::cout << "  isSmudged:" << (devices[i].isSmudged() ? "true" : "false") << std::endl;
    std::cout << "  isLightingBad:" << (devices[i].isLightingBad() ? "true" : "false") << std::endl;
  }
}

void SampleListener::onServiceConnect(const Controller& controller) {
  std::cout << "Service Connected" << std::endl;
}

void SampleListener::onServiceDisconnect(const Controller& controller) {
  std::cout << "Service Disconnected" << std::endl;
}

void SampleListener::onServiceChange(const Controller& controller) {
  std::cout << "Service Changed" << std::endl;
}

void SampleListener::onDeviceFailure(const Controller& controller) {
  std::cout << "Device Error" << std::endl;
  const Leap::FailedDeviceList devices = controller.failedDevices();

  for (FailedDeviceList::const_iterator dl = devices.begin(); dl != devices.end(); ++dl) {
    const FailedDevice device = *dl;
    std::cout << "  PNP ID:" << device.pnpId();
    std::cout << "    Failure type:" << device.failure();
  }
}

void SampleListener::onLogMessage(const Controller&, MessageSeverity s, int64_t t, const char* msg) {
  switch (s) {
  case Leap::MESSAGE_CRITICAL:
    std::cout << "[Critical]";
    break;
  case Leap::MESSAGE_WARNING:
    std::cout << "[Warning]";
    break;
  case Leap::MESSAGE_INFORMATION:
    std::cout << "[Info]";
    break;
  case Leap::MESSAGE_UNKNOWN:
    std::cout << "[Unknown]";
  }
  std::cout << "[" << t << "] ";
  std::cout << msg << std::endl;
}

int main(int argc, char** argv) {
  // Create a sample listener and controller
  SampleListener listener;
  Controller controller;

  // Have the sample listener receive events from the controller
  controller.addListener(listener);

  if (argc > 1 && strcmp(argv[1], "--bg") == 0)
    controller.setPolicy(Leap::Controller::POLICY_BACKGROUND_FRAMES);

  controller.setPolicy(Leap::Controller::POLICY_ALLOW_PAUSE_RESUME);

  // Keep this process running until Enter is pressed
  std::cout << "Press Enter to quit, or enter 'p' to pause or unpause the service..." << std::endl;

  bool paused = false;
  while (true) {
    char c = std::cin.get();
    if (c == 'p') {
      paused = !paused;
      controller.setPaused(paused);
      std::cin.get(); //skip the newline
    }
    else
      break;
  }

  // Remove the sample listener when done
  controller.removeListener(listener);

  return 0;
}


HANDLE OpenSerialport(LPCTSTR lpFileName)
{
	HANDLE hCom = CreateFile(
		lpFileName,						// ポート名を指すバッファへのポインタ
		GENERIC_READ | GENERIC_WRITE,	// アクセスモード（ＲＥＡＤ、ＷＲＩＴＥなど）
		0,								// ポートの共有方法を指定（共有不可：０に設定）
		NULL,							// セキュリティ属
		OPEN_EXISTING,					// ポートを開き方を指定（OPEN_EXISTINGで既存を指定）
		0,								// FILE_FLAG_OVERLAPPED // ポートの属性を指定
		NULL							// テンプレートファイルへのハンドル（常にＮＵＬＬを指定）
	);
	if (hCom == INVALID_HANDLE_VALUE) {
		std::cout << "open error" << std::endl;
	}
	BOOL result;
	result = SetupComm(		//設定
		hCom,	// 通信デバイスのハンドル：CreateFile()で取得したハンドルを指定
		1024,	// 受信バッファーサイズ：　受信のバッファーサイズをバイト単位で指定
		1024	// 送信バッファーサイズ：　送信のバッファーサイズをバイト単位で指定
	);
	return (hCom);
}

BOOL CloseSerialport(HANDLE hCom)
{
	return CloseHandle(hCom);
}

BOOL GetSerialportBaud(HANDLE hCom, DWORD * pBaud)
{
	COMMPROP tmp;
	BOOL result = GetCommProperties(hCom, &tmp);

	*pBaud = tmp.dwSettableBaud;

	return result;
}

int WriteSerialport(HANDLE hCom, unsigned char * pdata, int size)
{

	DWORD NoOfByte = -1;

	BOOL result = WriteFile(hCom, pdata, size, &NoOfByte, NULL);
	return NoOfByte;
}

int ReadSerialport(HANDLE hCom, unsigned char * pdata, int size)
{
	
	BOOL result;
	DWORD dwError;
	COMSTAT comstat;
	result = ClearCommError(hCom, &dwError, &comstat);

	DWORD tmp = size;
	if (tmp > comstat.cbInQue) {
		size = comstat.cbInQue;
	}
	if (size == 0) {
		return 0;
	}

	DWORD NoOfByte = -1;
	result = ReadFile(hCom, pdata, size, &NoOfByte, NULL);

	return NoOfByte;
}
```
これでOK！
ってあらためてみるとこれだと指9本までしか送れないw
まぁいいや。

ということで、今回は空中映像を動かすためのトライアンドエラーの話でした。
いろいろな課題は、またのんびり来年やっていきます。
それでは皆様いいお年を!