Make the Solenoid Keyboard.

Solenoid Keyboard(Typing Machine) is funny item. I would describe how to make the this works.

– Typing Board(solenoid typing machine)


1. Simple solenoid test with Arduino Uno
First of all, it have to control the solenoid from Arduino. Here is just 1 solenoid version fritzing circuit. (note)This push type solenoid have 8gf power on 5V from datasheets.

– Parts list.
1. Arduino Uno or Mega
2. 5V solenoid ROB-11015
3. Panjit 1S3 Schottky Barrier Rectifier Diode
4. NPN Transistor 2SC3558-Y
5. 5.6K carbon resistor

– Arduino Sketch

void setup(){
  pinMode(2, OUTPUT);
}

void loop(){
  digitalWrite(2, LOW);
  delay(1000);
  digitalWrite(2, HIGH);
  delay(100);
}

2. set on the keyboard
Test the many solenoid with same circuit. Modify Arduino sketch for many solenoid pin out control.

Next, make the base board for up-setting on the keyboard and solenoid. It have to be checked the Macbook Air keyboard size and solenoid pushing height before make the base-board. Then I used the 2-wood/alminium board/some screw.

Set the solenoid on the base board. I use the 10x100mm Aluminum Frame for solenoid and base board connectivity, bend the Aluminum Frame 90 degree and cut(checking the solenoid length).
When this work, it’s very important checking the keyboard/solenoid setting position and solenoid push length before the setting the solenoid on the base board. This solenoid push power is only 8gf with 5V. Therefore if top of the solenoid position is too near or far to the keyboard, solenoid could not type the keyboard. Solenoid position setting is point of the this typing machine.

Arduino source code is here.

#define SOLENOID_1_PIN_START 2
#define SOLENOID_1_PIN_END 11

#define SOLENOID_2_PIN_START 22
#define SOLENOID_2_PIN_END 51

#define KEYBORAD_5_KEY    22
#define KEYBORAD_4_KEY    23
#define KEYBORAD_3_KEY    24
#define KEYBORAD_2_KEY    25
#define KEYBORAD_1_KEY    26

#define KEYBORAD_Q_KEY    27
#define KEYBORAD_W_KEY    28
#define KEYBORAD_E_KEY    29
#define KEYBORAD_R_KEY    30
#define KEYBORAD_T_KEY    31

#define KEYBORAD_A_KEY    51
#define KEYBORAD_S_KEY    50
#define KEYBORAD_D_KEY    49
#define KEYBORAD_F_KEY    48
#define KEYBORAD_G_KEY    47

#define KEYBORAD_Z_KEY    46
#define KEYBORAD_X_KEY    45
#define KEYBORAD_C_KEY    44
#define KEYBORAD_V_KEY    43
#define KEYBORAD_SPACE_KEY    42

#define KEYBORAD_6_KEY    11
#define KEYBORAD_7_KEY    10
#define KEYBORAD_8_KEY    9
#define KEYBORAD_9_KEY    8
#define KEYBORAD_0_KEY    7

#define KEYBORAD_Y_KEY    6
#define KEYBORAD_U_KEY    5
#define KEYBORAD_I_KEY    4
#define KEYBORAD_O_KEY    3
#define KEYBORAD_P_KEY    2

#define KEYBORAD_H_KEY    41
#define KEYBORAD_J_KEY    40
#define KEYBORAD_K_KEY    39
#define KEYBORAD_L_KEY    38

#define KEYBORAD_B_KEY    37
#define KEYBORAD_N_KEY    36
#define KEYBORAD_M_KEY    35
#define KEYBORAD_ENTER_KEY    34


void setup() {
  for (int i = SOLENOID_1_PIN_START; i <= SOLENOID_1_PIN_END; i++) {
    pinMode(i, OUTPUT);
  }

  for (int i = SOLENOID_2_PIN_START; i <= SOLENOID_2_PIN_END; i++) {
    pinMode(i, OUTPUT);
  }
  
  Serial.begin(9600);
}

void keyput(int key) {
  digitalWrite(key, LOW);
  delay(100);
  digitalWrite(key, HIGH);
  delay(50);
  digitalWrite(key, LOW);
}

void loop() {   
  int input = Serial.read();
  if(input != -1 ){
    if (input == '5')  keyput(KEYBORAD_5_KEY);
    if (input == '4')  keyput(KEYBORAD_4_KEY);
    if (input == '3')  keyput(KEYBORAD_3_KEY);
    if (input == '2')  keyput(KEYBORAD_2_KEY);
    if (input == '1')  keyput(KEYBORAD_1_KEY);
    if (input == 'q')  keyput(KEYBORAD_Q_KEY);
    if (input == 'w')  keyput(KEYBORAD_W_KEY);
    if (input == 'e')  keyput(KEYBORAD_E_KEY);
    if (input == 'r')  keyput(KEYBORAD_R_KEY);
    if (input == 't')  keyput(KEYBORAD_T_KEY);
    if (input == 'a')  keyput(KEYBORAD_A_KEY);
    if (input == 's')  keyput(KEYBORAD_S_KEY);
    if (input == 'd')  keyput(KEYBORAD_D_KEY);
    if (input == 'f')  keyput(KEYBORAD_F_KEY);
    if (input == 'g')  keyput(KEYBORAD_G_KEY);
    if (input == 'z')  keyput(KEYBORAD_Z_KEY);
    if (input == 'x')  keyput(KEYBORAD_X_KEY);
    if (input == 'c')  keyput(KEYBORAD_C_KEY);
    if (input == 'v')  keyput(KEYBORAD_V_KEY);
    if (input == ' ')  keyput(KEYBORAD_SPACE_KEY);
    if (input == '6')  keyput(KEYBORAD_6_KEY);
    if (input == '7')  keyput(KEYBORAD_7_KEY);
    if (input == '8')  keyput(KEYBORAD_8_KEY);
    if (input == '9')  keyput(KEYBORAD_9_KEY);
    if (input == '0')  keyput(KEYBORAD_0_KEY);
    if (input == 'y')  keyput(KEYBORAD_Y_KEY);
    if (input == 'u')  keyput(KEYBORAD_U_KEY);
    if (input == 'i')  keyput(KEYBORAD_I_KEY);
    if (input == 'o')  keyput(KEYBORAD_O_KEY);
    if (input == 'p')  keyput(KEYBORAD_P_KEY);

    if (input == 'h')  keyput(KEYBORAD_H_KEY);
    if (input == 'j')  keyput(KEYBORAD_J_KEY);
    if (input == 'k')  keyput(KEYBORAD_K_KEY);
    if (input == 'l')  keyput(KEYBORAD_L_KEY);

    if (input == 'b')  keyput(KEYBORAD_B_KEY);
    if (input == 'n')  keyput(KEYBORAD_N_KEY);
    if (input == 'm')  keyput(KEYBORAD_M_KEY);
    if (input == '\n')  keyput(KEYBORAD_ENTER_KEY);
  }
}

toioで遊んでキャッキャしてみる

toioのフルキットが届いたから、遊んで行こう。まずはフルキットの壮観な感じでドーン!!

■ とりあえず遊んでみる。
まずは何にしても動かしてみよーってことで、ゲズンロイドの”めだま生物”を動かしてみる。

どこを向いても目玉が向いておいかけてくる。まるで何かの生き物のよう…というか、工作生物という設定でこの子達は全て生き物ってことで、こんどはシャクトリー。

ぶっちゃけ…ずーっと見ていても飽きない。まるで何かの意思を持った人工生物(ここでは工作生物)のように動き続ける。ぼーっと見ていると思うのだけど、この2個のキューブが絶対位置を知りながら、協調動作をしているというのが何気にすごい。大抵は外部のセンサーで位置を補足するんだけど、自己位置同定をして動いている。専用マットの外に出ようとすると、検知して方向を変える。なんかスゲーよ…

このめだめ生物を応用して、目玉をとってLEDライトを付けて、ターゲットに”くつ”を使うと…

延々と靴をLEDで照らしつづけることが出来る。これは…何か面白い用途というか、可能性を感じる。そう、何かに使えそうなのよね。例えば、キューブにもう少し大きい箱を被せて、影絵をずっと照らし続けるとかでも面白そう。

んで、次にネタでダイソーで買ってきたサイリウムと粘土を使ってレースコースを作って…

部屋を真っ暗にすると、幻想的な雰囲気でtoioレースが楽しめる。というか、この時点ではまだ操作が上手くなくって、カクカクしまくっているwww

■ チビッコとガチバトル
そして今度はチビッコとクラフトファイターバトル。

もちろんガチで戦っている。これもダイソーでゲットしてきたキャラを搭載させて、アルティメットバトルの様相になってきている。必殺技とバランスが何気にキーになってくる。自分も、「てめー!!」とか普通に言っている…そう、自分も激しく負けず嫌いだから、この手の勝負&バトルになると相手がチビッコでもマジになる。

シャクトリーの工作(といっても紙を切って貼り付けるだけで超簡単)とかを一通り遊んだら、自分に勝つまで止めないとか恐ろしい事を言い始めてきて、それでも負けないでバトルをしていた最終戦。

負けてしまった…これで開放される。約2時間ほど、あっという間だった。このまま延々と全部やるまで止めない!!とか言われたらどうしよう…とか思っちゃったもん。

そして後日…都内の某所にて飲みの場で持って行って飲んでいたら、さらにカヲスな環境に。

こんな状況でも青いマットの上でtoioのキューブはちゃんと安定的に動作してくれた。”うに”のした、ヱビスビールの横にキューブが光って見える。もちろんだけど、自分も激しく酔っていた。

■ 少し技術的な視点で

やっぱり何か技術的な視点でも見たくなってくる。といっても、モノの持つ力が強くって、触って遊んでいると技術的にどーか?はどーでも良くなってくる。toioは専用のコンソールと動くキューブがBLEで通信して、iPhone/Android的なアプリは今のところは無い。
ただ、
「BLEなら見れるだろう」->
「スマホからキューブに接続出来るだろう」->
「勝手アプリやらが作れてtoioで遊べちゃうだろう」->
「ひゃっはー!!」
と思って…思わずsniffer

WireSharkでtoioが通信しているのを全力でsnifferして見て、何か面白いことが無いかなーと思って見てみたら…位置情報らしき物が秒間30以上で通知されていたり、モーターを動かす情報が飛んでいたり、凄いスピードで流れていく。キューブの操作や動きがリアルタイムに感じて、応答性がめちゃ良いのはこのレスポンシビリティの良さか…とか思いつつ、データを見ていたけど、全然分からん!!ってことで…次はiPhoneのLight Blueからキューブに接続してみた



Light Blueから接続できて、色んなサービスが見えてきた。センサー情報、バッテリー情報が見れたり、ブザーやモーターに値を入れたら音を鳴らしたり動かすことが出来た。そして、notificationをオンにしたら、キューブの動きと連動してデータが取得できるサービスが何個かあって、恐らくこれが位置情報をやりとりしているモノだろう…と思って、キューブを動かしながらデータをぼーっと見ていた。キューブがマット上の印字を読み取って絶対座標の同定を行っているようで(参考)、Bluetoothの動き的に予想したのは次のとおり。

1) ほんの少しの動きでもnotificationが飛んでくるから、精度はかなり良さそう。
2) マット内にゼロ点 or マット外に仮想のx-yのゼロ点(データを受け取るコンソール側で変換)が多分あるはず。
3) ヘッダ、位置情報(x-y?)、精度/印字のID(?)、位置情報の小数点以下(?)、Checksum…etcといったデータ構造かな。

とかとか思ったり、カンで多分これかな〜と思って久々にswiftでBTつなげてiPhoneアプリを適当に作って見てみたら、なんとなく位置情報が取れてるっぽい。

コードはこんな感じ(適当)。

import UIKit
import CoreBluetooth

class ViewController: UIViewController {

    let toioCubeUUID = "toioの-U-U-I-D"
    let toioCubeIDDetectionUUID = "id detectの-U-U-I-D"
    
    var centralManager: CBCentralManager!
    var peripheral: CBPeripheral!
    var serviceUUID : CBUUID!
    var charcteristicUUID: CBUUID!
    var layer: CAShapeLayer! = nil
    
    override func viewDidLoad() {
        super.viewDidLoad()
        centralManager = CBCentralManager(delegate: self, queue: nil)
        serviceUUID = CBUUID(string: toioCubeUUID)
        charcteristicUUID = CBUUID(string: toioCubeIDDetectionUUID)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    private func drawCircle(xpoint: Int, ypoint : Int) {

        let path = UIBezierPath(arcCenter: CGPoint(x: xpoint, y: ypoint), radius: 10,  startAngle: 0.0, endAngle: CGFloat(Double.pi * 2.0), clockwise: true)
        if (layer != nil) {
            layer.removeFromSuperlayer()
        }
        layer = CAShapeLayer()
        layer.fillColor = UIColor.orange.cgColor
        layer.path = path.cgPath
        
        self.view.layer.addSublayer(layer)
    }
}

extension ViewController: CBCentralManagerDelegate {
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case CBManagerState.poweredOn:
            let services: [CBUUID] = [serviceUUID]
            centralManager?.scanForPeripherals(withServices: services, options: nil)
        default:
            break
        }
    }
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,
                        advertisementData: [String : Any], rssi RSSI: NSNumber) {
        self.peripheral = peripheral
        centralManager?.stopScan()
        central.connect(peripheral, options: nil)
    }
    
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        peripheral.delegate = self
        peripheral.discoverServices([serviceUUID])
    }
}

extension ViewController: CBPeripheralDelegate {
    
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if error != nil {
            print(error.debugDescription)
            return
        }
        peripheral.discoverCharacteristics([charcteristicUUID], for: (peripheral.services?.first)!)
    }
    
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        
        if error != nil {
            print(error.debugDescription)
            return
        }
        
        peripheral.setNotifyValue(true, for: (service.characteristics?.first)!)
    }
    
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        
        if error != nil {
            print(error.debugDescription)
            return
        }
        
        updateWithData(data: characteristic.value!)
    }
    
    private func updateWithData(data : Data) {
        let toioData = data.withUnsafeBytes {
            [UInt8](UnsafeBufferPointer(start: $0, count: data.count))
        }
        
        // 0x03 : cube not set-on the mat
        if (toioData.first != nil) && toioData.first == 0x03 {
            print("Data: \(toioData)")
            
        // 0x01 : get id detection(x-y coordinate)
        } else if (toioData.first != nil) && 0x01 == 0x01 {
            let y : UInt16 = UInt16(toioData[5]) + UInt16(toioData[6]) << 8
            let x : UInt16 = UInt16(toioData[3]) + UInt16(toioData[4]) << 8
            print("x-y :  \(x) , \(y)")
            drawCircle(xpoint: Int(x), ypoint: Int(y))
        }
    }
}

本当にこれで正しいかどうかは分からないけど、何となく雰囲気的には座標が取れて動いているっぽい。更にモーターやブザー(音)を動かしたりする勝手SDKを誰が作っちゃいそーな予感がしている。

このtoioを色々触ってみて思ったのが...技術は抜きに面白い!!ということだった。プログラミングのエッセンスを少し入れると、生き物のように動く。しかも、外部センサー無しに自分の位置を自分で分かった上で。これが良い感じで、外部のKinectのような赤外線・カメラで画像認識・超音波...etcを使うと、電源どーするよ?外部センサー連動&フィードバックどーするよ?とか、何気に面倒なことが起きる。これを考えなくてもOK!!というのは、何気に良い感じだと思われ。
思わずGoogle Homeを乗せてみたらあっさり動いたし、モーターも小型サイズなのにめちゃ強い。ということは...他のセンサー連動で、声や音声を認識して動いたり...といった事もアリな気がするよ。

MQTTとセキュリティ

ふらっと見ていたら、こんな記事を発見した。

刑務所のドア開放や電車の不正操作も? 無防備なMQTTのM2M接続

2分くらいで読める内容だからチェックしてみた方が良いと思われ。自分も端末向けにMQTT(TLS)を出していて、現時点で平文とTLS(暗号/認証)で利用されているアプリケーションの比率は…

MQTT(平文) 44.94K
MQTT(TLS) 1.69K

約26倍もの利用で平文の方が圧倒的に多い。これは、それぞれのライブラリを使っているアプリの数で、実際に端末に配布されている数はもっと多いだろうから、凄く多くの端末が平文で通信を行っているということになる。別に平文を使ってはいけない!!という訳じゃなく、このほぼIoT的なシステムでデファクト気味になっているMQTTを、IoT的な端末で安全に使う方法を少し書いておこう。

1) GWを使う構成
そもそもMQTTは産業向けのプロトコルで使われる想定だったようで、インターネットから閉ざされた閉空間またはセキュリティが担保されたルータやスイッチ配下の端末間ネットワークを構成するために利用される、という方法がメインだったろう。こういったネットワークの場合、内部に悪意のあるモノが無いという前提のもと、平文でやりとりされるのもアリ。
(このような方法に似たもので、Load BalancerでTLS(443)をインターネット側から終端して、バックエンドの通信は平文で行うことでTLSの負荷を軽くする、といった方法はよくある)

MQTT Broker上ではメッセージを終端して、外部との通信はセキュアに行うといったもの。
まぁ、この場合は平文でもOKかなと。ただ、内部に侵入された時も守りたい!!という時は暗号化/認証すればOK。まぁ侵入された時点でノックアウトされているようなもんだけど。

2) インターネット上のサーバに直接接続
これは端末が直接WiFiやルータと接続して、外部と通信を行うものになる。これは当然、暗号/認証をTLSのようなもので行ってセキュアに通信を行う必要がある。ルータ以下はセキュアに守られていても、インターネット上に平文の通信を流すというのは行ってはいけないですね。

3) せめて暗号化
これは、本文のメッセージを平文でインターネット上に流して使いたい…というワガママさんに、どうしても&しょうがなく…といったケースでは仕方なく(本当はダメ)。
この場合の問題点は、認証をどうするよ…ということだ。データを暗号化しても認証を行わないと、不正にデータを取られたり予想外の攻撃を受けたりする。よく証明書を利用すれば、”暗号化されてOK!!”というのを聞いたりするけど、証明書の利用はもちろん暗号化も一つだけど、”相手を認証する”という部分もある。”相手が誰か証明”するってことね。例えば、誰かと話そうと思っている時、その誰かは本当にその人ですか?という時に証明書を取り出して、”自分が本当にその人ですよ”と証明をする。丁度、パスポートや免許で自分の証明をするのと同じような感じ。
この部分的に暗号化を行うとデータ自体は守られそうだけど、接続する時にどうやって認証するのか?というリスクが発生する。MQTT BrokerとID/Passwordで認証すれば?と思うかもしれないけど、このときTLSを利用していないとID/Passwordが平文で流れて見事にヤバい、というか被害が悪化する。これは接続する時はIDだけを変えて、データを垂れ流す時だけだろうか…気合でPub/Subでアプリケーション間で認証するのは出来なくは無いけど。

■ 本当に暗号&認証って必要?
「暗号化とか認証とかめんどくせーし、やってられないし、いいよ平文で見れても気にしないし」という人も居ると思われ。鍵なんか使って暗号化しなくても大丈夫!!ネットを使う人が盗聴とか攻撃を一切しない幸せな世の中だと良いね!!、と自分自身思っているけど、理想と現実は違う。現実は攻撃・盗聴・搾取…etc何でもありで痕跡も消して…というカヲス状態だ。便利なネットだけど、光には影が出来る、というのに似ている。正しいことは、一方からの風景にしか過ぎない。

セキュリティとか必要ないし〜と思う人には、実際にリスクを目の前に見せたりもするんだけど、それで対応するかどうかは…そこ次第。リスクをよくよく理解した上で対応しない、という選択肢もアリ。問題を分かっているという事と、知らないことは大きな差があるからね。まぁ普通は流しているデータを見られてもOK!!という人はあまり居ないと思うのと、本当に理解していたら大抵は後回しでも対応するんだけどね。ま、別に他人事だしどーでもいいけど。

ということで、冒頭の記事が本当なのか、オープンになっているMQTTサーバに接続して、全トピックをSubscribeしてみることにしよう。これはハックでもクラックでも無い。MQTTとして普通にSubscribeしているだけ。これらは平文で流れているもので、ぼーっと見ていただけで何もしていないし、情報にはマスクをしている。というか全部マスクしているw

1) AWSの情報が流れている…
{“arn:XXX:XXX:XX-XXXX-1:xxxxxxx:XXXXXXXXX/XXXX/XXXXXXXXXXXXXX/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx”,….

2) WiFiに接続する証明書が流れている
{“XXXXX”:”XXXXXXXXXXXXXX”,”XXXX”:”XXXXXXXX”}
以下証明書のPEM

3) 某社のIDと入管記録
{“XXXXX” : “XXXXX” ,
“XXXXXX” : “XXX XXX XXX XXX ” ,
“XXXXXX_XXXX” : “XXXX-XX-XX XX:XX:XX” ,
“XXXXXX_XXXX” : “XXXX-XX-XX XX:XX:XX”}

4) 某デバイスや車の位置情報
{latitude”:xx.xxxxxxx,”longitude”:xxx.xxxxxxxxx”,….}

6) その他もろもろ(メアド、環境センサのデータ、 端末名、MAC、IP、PC(Mac/Win)、スマホのデータ、WiFiのデータ、ドアの開閉状態…etc)

あと、この辺の落とし穴として…MQTTサーバだと1883(平文)、8883(TLS)になっていて、8883の方に接続していて暗号化をしていても、1883(平文)の方にもサクットそのまま接続できて、8883側に流しているデータを1883側でSubscribeできる。つまり…オープンなMQTTサーバにヤバいデータは流すな!!ということに尽きるし、余計なポートは閉じれ!!といった感じ。
この皆さんの問題点はデータが見れる他に、トピック名も分かるから偽装のデータを投げたり、当該トピックに大量に流して溢れさせたり(大抵、この手の端末はスペックが低くて大量に投げると止まる)、外部から端末制御…といった、冒頭の記事で言っている内容と同じことが出来る。

こういう平分で全部見れちゃうぜー!!とか眺めていると、もう暗号化しなくてオール平文でも良いんじゃね?って気が、0.0001nmくらい感じてくる。感じるだけで、自分はやらないけどね。
結果…暗号&認証は必要でつ

続・mbedTLSの組込で分かったこと

現在、Particle Photon/Electron向けにMQTT/CoAP/TLS周辺のcommunity libraryを公開している。

利用しているアプリケーションの総数は上記のようになっている。わりと使っているアプリも多くてメンテを何度辞めようか…と思いながらも続けている。それぞれの用途は名前の通り、次のような機能になる。

MQTT : 素のMQTT
MQTT-TLS : TLS向けのMQTT
TlsTcpClient: TLS(TCP)クライアント
coap : 素のCoAP

TLSのベースはmbedTLSの現時点での最新版(2.6.0)を利用している。それぞれの説明はgithub上にて。まえに”mbedTLSの組込で分かったこと“にも記載したけど、今回もmbedTLSを実装して分かったことや、端末の特性上つらいポイントを少し残しておく。

■ 容量との果てしない戦い
ひたすらTLSの容量を削っていく。現時点でのTLS library単体のサイズは50kbytes台になっている。これをアプリに組み込むと…

TlsTcpClient : 63Kbyte

MQTT-TLS : 61Kbyte

証明書は2048bitでPEM形式でソースコードに埋め込んで利用する。その分やアプリサイズを引くと50Kbyte台になる。以前、商用のTLSライブラリは大体50Kbyte程度で、そのくらいまで落とすのも出来るはず…と言ったけど、本当にそういう事に。対応しているCipher SuiteはTLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256だけになってしまった。寂しい…繋げる環境に依ってビルドを変えてターゲットを絞っていく感じを想定して、とりあえず最小にしている。

なぜ容量を減らすか?

これは非常に簡単な理由からで、TLSで容量を沢山使うと、他のlibraryやアプリの実装に容量を使えなくなってしまうから。issueやcommunity上で、「容量でかい・メモリの限界」と言われまくってきたけど、もうこれだけ減らせば十分でしょう。
メモリや容量が大きいと…アプリが書けない、他のlibraryが使えない、TLSのネゴシエーション中の処理でmalloc errorで落ちる、といった様々な問題が起きる。そもそも接続以前に、ネゴシエーション中に落ちるのは心が折れる。
アルゴリズムや実装の選定はmbedTLSの設定(config.h)で追加・除去が簡単に出来る。この設定を調整して実装の選定をしてく。

■ ソースコード修正/関数追加
Particleのビルドは arm-none-eabi-gcc で行われて基本的にはC++で記載していく。ただ、mbedTLSはCだからC++にしていく作業が必要になる。これは拡張子を変えて、キャストを変えたり、関数を変えたり…といった地味な作業になる。

そして必要な関数を用意する。
例えば、TLSで利用する証明書には有効期限という概念がある。ある時刻を過ぎれば証明書は失効する。その証明書が正しいかどうか?を判別するには、端末で時間をちゃんと保持しておく必要がある。ただし、time/gettimeofdayのような関数はもちろんParticleの環境には存在しない。そして、この手の端末だと追加で電池を搭載していないから(電源を切ってもPCとかが日時を保持しているのは、ハードウェアクロック(RTC: Real Time Clock)と電池が搭載されていて端末の電源を落としても時刻を保持してくれている)、起動してネットワークに接続した後に時間を同期して保持&読み出せる必要がある。

これらを用意して証明書をverifyする時に有効期限をちゃんと検証(verify)できるようにする。具体的にはmbedtls/timing.cpp内に、こんな感じのラッパを用意して叩けるようにしておく。

#include "rtc_hal.h"
#include "timer_hal.h"
extern "C" int _gettimeofday( struct timeval *tv, void *tzvp )
{
    uint32_t t = HAL_Timer_Milliseconds();  // get uptime
    tv->tv_sec = HAL_RTC_Get_UnixTime();    // get rtc time before Particle.syncTime()
    tv->tv_usec = ( t % 1000 )*1000;        // get remaining microseconds
    return 0;
} // end _gettimeofday()

そして起動してネットワークに接続後に時刻の同期を行うようにしておく。わりと苦肉の策で、秒については時刻を同期したタイミングで。ただしマイクロ秒については同期しても取れないから、仕方なくuptimeから取得してくることにした(なう)。verifyの時に有効期限を切ってOKにしてしまえば…というのも考えられるけど、さすがにそれはダメかなと。

ここで更に問題になるのが…uint32_tだ。そう、いわゆる2038年問題というやつだ。この秒を扱っているHAL_RTC_Get_UnixTime()の戻りはtime_t(uint32_t)で定義されている。これをuint64_tにすれば問題無しな訳だけど、さらに根っこのfirmware側とMCU側のSDKまで波及しているから、ここだけ修正してもなんともならないという訳ですな。まだこれは20年も先の話なわけだけど、サーバ側だけならまだしも、この手の組込み物だと近くなってきたらドキドキ感が上がってくると思われ(まだ20年も先だけど)。

■ オイコラ技適どーするよ?
これはメンテ上で発生する問題で地味に切ない。現時点でParticleが提供している(していた)端末は次の通り。

– Core(WiFi:技適:日本国内OK、ただしもう入手不能&売ってない)
– Photon(WiFi:技適なし)
– Electron(SIM Cellular 2G/3G:技適なし)

ベースになるライブラリMQTTは、Coreの技適がOKの時に作ってPublishした。その後、Photon、Electronという2つの端末が出てきて主力になっている。ただし、Coreは既に終了している。つまり…
技適がOKの時に作ったライブラリで、その後は技適が無い端末しか無い状態でメンテナンスする」という事が発生する。さらには、「2Gでも動くの?」という質問も来たりする。国内は無いからもちろん海外からだけだけど。そもそも2Gは日本国内には無いだが…

Electronは持ってないし、しょうがないから「検証よろしくね!!」「diffちょうだい!!」という事にしているけど、この辺はなんとかならないかなーと思ったりする。

地味にまだ使っているひと、まだまだ素のMQTTはもう4万アプリ越えてくるし、たまに「もー、絶対にやらん、やーめったっと!!」とかって思ったりするけど、仕方なくメンテを続けて行きそうな感じ。

Alexaで開発する流れ(日本語UI)

Alexaで日本語で開発できるようになったから、サクット作る流れをメモで残しておく。詳細はAmazonの資料だけど、見てもなかなか分からなかったり、謎な部分のお助けになれば。

■ 利用するもの
登録はやっておきましょう。
– Alexaの管理画面(日本版) : https://alexa.amazon.co.jp
– Alexaの管理画面(こっちは米国版) : https://alexa.amazon.com
– Amazonの開発者ポータル : https://developer.amazon.com/edw/home.html#/
– AWS Lambda : https://console.aws.amazon.com/lambda/

ここではサンプルで使われるLambdaのサンプルにもある「色をAlexaに言わせるスキル」の簡易版を作る。どういったものか?と言うと、Alexaに向かって、
自分:「好きな色は赤です。」
Alexa:「あなたの好きな色は赤ですね。」
と色を反芻して言ってくれるもので、基礎的だけど初歩としては簡単な1歩になると思われ。ということで進めていく。

■ Amazonの開発者ポータルで情報を入力する
1. Alexaの開発者ポータルにログイン

2. 新しいスキルを作っていくボタンをぽちる

3.1. スキルの情報を入れていく

言語で日本語を選択できる。スキルの種類については、こちらを参照。自分が使いやすくて感動したのはスマートホームスキル。

3.2. スキルの情報をいれた結果

適当に名前を入れる。呼び出し名は「Alexa、なんちゃらかんちゃら」とスキルを起動するときの”なんちゃらかんちゃら”の部分。名前と呼び出し名を一緒にしておくと、分かりやすくて良いと思われ。もちろん、日本語名が使える。
そして、アプリケーションIDが発行される。この”アプリケーションID“のIDはあとでLambdaで利用する。

4.1.対話モデルを作っていく

スキルビルダーをポチって画面遷移

4.2.スキルビルダー画面

ここで対話モデルを作っていく。ザックリだと、

1. Alexaに話しかける話の構造(Intent)
2. 話しかけた結果から取得できる変数の設定(Slot)
3. 変数に割り当てることができる値の定義(SlotTypeに値を割り当て)

といった感じ。ここで設定した名前が後でAWS Lambdaのプログラム側で利用できるようになる。例えば、次から設定する流れだと、

1.1. Intentの名前を決める(MyColorsIntent)
1.2. 話しかける内容をつくる(“好きな色は {Color} です”と話かけられるようにする。ここで {Color} は可変の値。)
2.1. 変数(Slot)の設定を行う。
2.1. Slotのタイプを作る(Color_ITEM_LIST)
3.1. 変数に割当られる値の定義(緑、赤、青…etc)を入れていく

つまり、Alexaに話かける内容はここで決める。「怪しい {Color} は何ですか?」「{Color}をもう一度言ってくれますか」といった話を入れていったりする。
なにはともあれ、”Intentの+”ボタンをクリック。

4.3.Intentの名前を決める

ここでは”MyColorsIntent”という名前にする。この名前はあとでLambdaで利用する。この名称に日本語は利用できない。

4.4.話しかける内容をつくる

「好きな色は {Color} です。」と入れて”+”をクリック。この時、{}の前後に半角スペースを1個いれる。これを入れないとモデルを作る時にエラーになる。

4.5.Slotの設定を行う

右側のタブに”Color”という項目が追加されるので、その下にある”Choose a slot type…”をクリックするとリストが出てくる。ここで、新規に”COLOR_ITEM_LIST”を追記&作成して”+”をクリック。
割り当てたSlot(変数)には型が必要で、デフォルトで利用できるもので、AMAZON.City, AMAZON.DATE…etcが並んでいる。TEXTやStringといった文字列が無いのが辛い(少し昔はあったけど、無くなってしまった)。自作スキルでデフォルトで利用できる型があればそれを使った方がOK。大抵の場合は使いたい型が無いと思うので、自分で型を作ることになると思われ。

4.6.Slotに割当られる値の定義

「赤、緑、青…etc」を入れて”+”を押して、Slotに割当られる値を定義していく。ここで割り当てた値が、あとで作るLambdaで利用することが出来る。
設定関係はここまで。

5.作ったモデルを見てみる

“Code Editor”をクリックすると、作ったモデルのJSONの表現が見れる。ここで直接、モデルを記載していってもOK。

6.モデルの保存とビルド

“Save Model” -> “Build Model”で作ったモデルをビルドしておく。終わったら”Configuration”ボタンをクリックして、作ったスキルの設定をしていく。

7.Alexaの設定画面

ここでAWS Lambda(プログラム側)との連携の設定をする。サービスエンドポイントはAWS Lambda(HTTPS側で自前サーバも可)で、デフォルトのテキストボックスの所にLambda側のARNエンドポイントを入れる。まだLambad側は作っていないので、とりあえずAlexa側の設定はここまで。次にLambdaでプログラム側の設定をしていく。

■ LambdaでAlexaスキルを作っていく
1. Lambdaの関数を作る。

AWS Lambdaの詳しい説明はググればOK。ザックリだと、サーバを作らなくてもアプリでスケールしてくれるPaaSといった感じ。何はともあれ「関数の作成」をクリック。

2. 関数の作成

テキストボックスの所に”Alexa”と入れると、Alexaで利用できる関数一覧が出てくるので、”Alexa-skills-kit-color-expert-python”をクリック。

3. 関数の設定

作成する関数の”名前”、ロールは既存のロールで、”service-role/alexa-skill”を選択。
ページの一番下にある”関数の作成”ボタンをクリック。

4. 関数の作成完了

これでLambda側の関数が作成できた。サンプル通りで非常に簡単。次に赤枠にある部分のARNという部分をコピーしておいて、実際にAlexa側と連携させてテストをする。

■ Amazonの開発者ポータルでスキルのテスト
1. AlexaとLambdaの連携

作ったLamdaのARNを”デフォルト”という部分に入れて(arn:lambda:…という文字列)、”次へ”をクリック

2. Alexa上でテスト

Alexaのテスト画面。”無効”となっているため、クリックして有効にする。

3. サービスシュミレーター

少し画面のしたに行くと”サービスシュミレーター”があるから「好きな色は赤です」と入れて、”色を呼ぶを呼び出す”をクリックすると結果が表示される。

スキルの開発の流れはここまでで終わり。シュミレータでテストを行ったり、いちいちAlexaに話しかけるのが面倒な時はボイスシュミレーターで話させたりも出来る。
あとは自分でアプリを作ったり、音声で遊んだり。外部連携をLambdaでやって、カメラと連動させて「いまのお家の状態を写真を撮ってメールで送って」といったセキュリティデバイス化させたり、LINEと連動させたりするのも面白いと思われ。
スキルタイプで、カスタムは一般的な対話で遊べるけど、スマートホームAPIは使ってみるとかなり面白いと思う。自分はスマートホームAPIでデバイス連携をさせた時、簡単で美しい流れにちょっぴり感動してしまった。

あとプログラムを書くのが苦手で、とりあえずRSSフィードだけ読ませてみたい…という時はフラッシュブリーフィングでサクット作るのもアリだと思われ。”マイチャンネル”みたいな扱いとしても使える。

■ その他
1. Lambdaの応答の日本語化
サンプルのColor-me-expertは日本語を戻していない。ただ、普通にPythonのプログラムで応答の文字列の部分を日本語にしてutf-8(# coding:utf-8)をコードの頭に追加すれば、日本語を利用することができる。以下はサンプル中に日本語をいれたもの(超簡単)。
日本語とは関係ないけど、ここでの注意点は”lambda_handler(event, context):”でAlexaアプリのIDを指定して、作ったAlexaスキルからしか実行できないように制限をしている。

# coding:utf-8

from __future__ import print_function


# --------------- Helpers that build all of the responses ----------------------

def build_speechlet_response(title, output, reprompt_text, should_end_session):
    return {
        'outputSpeech': {
            'type': 'PlainText',
            'text': output
        },
        'card': {
            'type': 'Simple',
            'title': "SessionSpeechlet - " + title,
            'content': "SessionSpeechlet - " + output
        },
        'reprompt': {
            'outputSpeech': {
                'type': 'PlainText',
                'text': reprompt_text
            }
        },
        'shouldEndSession': should_end_session
    }


def build_response(session_attributes, speechlet_response):
    return {
        'version': '1.0',
        'sessionAttributes': session_attributes,
        'response': speechlet_response
    }


# --------------- Functions that control the skill's behavior ------------------

def get_welcome_response():
    """ If we wanted to initialize the session to have some attributes we could
    add those here
    """

    session_attributes = {}
    card_title = "ようこそ"
    speech_output = "これは好きな色のサンプルアプリでーす "
    reprompt_text = "好きな色を言ってね。 "
    should_end_session = False
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))


def handle_session_end_request():
    card_title = "おわりです"
    speech_output = "サンプルを使ってもらってありがと" 
    should_end_session = True
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))


def create_favorite_color_attributes(favorite_color):
    return {"favoriteColor": favorite_color}


def set_color_in_session(intent, session):
    card_title = intent['name']
    session_attributes = {}
    should_end_session = False

    # この Color がSlotで設定した名前
    # intent['slots']['Color']['value'] で値を取得できる。
    if 'Color' in intent['slots']:
        favorite_color = intent['slots']['Color']['value']
        session_attributes = create_favorite_color_attributes(favorite_color)
        speech_output = "あなたの好きな色は " + \
                        favorite_color + \
                        "ですね。 " \
                        "他に好きな色は?"
        reprompt_text = "好きな色を言ってね。 "
    else:
        speech_output = "色が分からなかったよ。もういちど言って " 
        reprompt_text = "色が分からなかったよ。 " \
                        "好きな色は赤ですとかって言って "
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))


def get_color_from_session(intent, session):
    session_attributes = {}
    reprompt_text = None

    if session.get('attributes', {}) and "favoriteColor" in session.get('attributes', {}):
        favorite_color = session['attributes']['favoriteColor']
        speech_output = "あなたの好きな色は " + favorite_color + \
                        "です。終わります。"
        should_end_session = True
    else:
        speech_output = "私はあなたが好きな色が分からないです。 " \
                        "好きな色は赤ですとかって言って。"
        should_end_session = False

    return build_response(session_attributes, build_speechlet_response(
        intent['name'], speech_output, reprompt_text, should_end_session))


# --------------- Events ------------------

def on_session_started(session_started_request, session):
    """ Called when the session starts """

    print("on_session_started requestId=" + session_started_request['requestId']
          + ", sessionId=" + session['sessionId'])


def on_launch(launch_request, session):
    """ Called when the user launches the skill without specifying what they
    want
    """

    print("on_launch requestId=" + launch_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # Dispatch to your skill's launch
    return get_welcome_response()


def on_intent(intent_request, session):
    """ Called when the user specifies an intent for this skill """

    print("on_intent requestId=" + intent_request['requestId'] +
          ", sessionId=" + session['sessionId'])

    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']

    # この部分でIntentで設定した名前で処理を分岐することができる
    if intent_name == "MyColorIsIntent":
        return set_color_in_session(intent, session)
    elif intent_name == "WhatsMyColorIntent":
        return get_color_from_session(intent, session)
    elif intent_name == "AMAZON.HelpIntent":
        return get_welcome_response()
    elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent":
        return handle_session_end_request()
    else:
        raise ValueError("Invalid intent")


def on_session_ended(session_ended_request, session):
    """ Called when the user ends the session.

    Is not called when the skill returns should_end_session=true
    """
    print("on_session_ended requestId=" + session_ended_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # add cleanup logic here


# --------------- Main handler ------------------

def lambda_handler(event, context):
    print("event.session.application.applicationId=" +
          event['session']['application']['applicationId'])

    """
    ここの部分でAlexaアプリのID(applicationId)を設定しておく。このlambdaの関数がAlexaの当該アプリからしか実行できないようにしておく必要あり。
    """
    if (event['session']['application']['applicationId'] != "amzn1.ask.skill.XXXXXXXXXXXXXXXXXXXXXXXX"):
        raise ValueError("Invalid Application ID")

    if event['session']['new']:
        on_session_started({'requestId': event['request']['requestId']},
                           event['session'])

    if event['request']['type'] == "LaunchRequest":
        return on_launch(event['request'], event['session'])
    elif event['request']['type'] == "IntentRequest":
        return on_intent(event['request'], event['session'])
    elif event['request']['type'] == "SessionEndedRequest":
        return on_session_ended(event['request'], event['session'])


2. Alexaの管理画面
作ったアプリはAlexaのスキルで確認できる。

これはAlexa側のスキル管理画面。この右上に、自分がいれたスキル一覧が分かるボタンがあって、見てみると。

作ったスキル”色を呼ぶ”が入っている。スキルの追加は、スキル一覧から選んで有効にする必要があるが、開発版のタグ(devJP)が付いて、正規のアプリとパット見で区別して分かるのは良い感じ。

2.1. 自作スキルをAlexaに追加するには?
米国版のスキルだと開発するとすぐAlexaのスキル一覧に表示されて分かるのだけど、日本版では「スキルのベータテスト」から追加する流れのようだ(2017/11/15現在)。

開発者コンソールで、アイコンや色々な情報を入れてリリース申請の直前まで行える状態になると、このボタンが押せるようになる。

すると次のような画面になる。

ここでAlexaに登録している人のメアド等を入れて”テストを開始”ボタンを押すと招待URLが送信される。メールの中に”JP customers: To get started, follow this link:”という方のURLリンクがあるから(別の方はUS側のリンク)、そっちをクリックするとスキル一覧に追加されて、実機Echoでテストを進めることが出来る。

3. Lambda利用料金
AWSは従量課金で沢山使うほど課金される。基本的に何かをやろうとすると有料で、Alexaスキルを作ってみたいけど有料なのはなぁ…と思うかもしれない。
自分はAmazonの回し者でも何でもないけど、Lambdaは超安いです。Alexaスキルをさらっと作って動かして開発するくらいなら、課金額は10円もかからないと思われ。サンプルを動かすくらいなら、1円もかからない。気軽に動かしてみよっとー、って感じで全然大丈夫なはず。

4. リリース
実際にスキルを一般にリリースするには、公開情報の設定(スキルのアイコンや説明文)などの設定が必要になる。開発版なら、自分のスキル一覧上で使えるから特に気にする必要はないけど。
設定を適当に入れてリリースすれば一般利用者にすぐ使って貰えるか?と思ったら…Amazonによるリリース判定が待っていた。申請内容はこちら
スキルの説明文や、Alexaに話しかける内容をちゃんと作らないと、あっさりNGが来る。申請すればすぐ通るんじゃん!!と思ってやってみたら、全くそんな事が無かった(まぁ、ちゃんと作らないとダメというのは当然と言えば、当然だけど)。

上モノフェス

上モノフェスが10/8, 9に長野 上田市で開催されて行ってきた。

場所はサントミューゼというオシャレ空間でした。ぼーっと原っぱに座って寝ているだけでも何か満たされそうな感じ。

自分の展示空間は暗室独り占めー!!ひゃーっほい。

実はこの暗室空間はコンサートホールとエントランスの間にある踊り場の空間で、設置当日に準備をしてもらって感謝です。こういう滅多に使えない場所を使えるというのは実験をしたくなってくる。

投影する方向は、開けたらすぐというコンサートホールの扉に決めた。まさか、こういう所でKinect v2/XtionでDepthを取って投影したりサーボやステッパを動かして、LEDフラを回しまくる!!という非日常は無いだろうと…

諸々のものを設置。物としては、Kinect v2/Xtion、プロジェクタで投影もの、PCx2、LEDフラx2、ステッパ&サーボx9の最近製作しているものを持って来ました!!といった感じ。この青いLEDバーもゴッツリ動く(Kinect v2連動でUnityから制御)。

予想外だったのが…めっちゃ人。マジでめっちゃ人たくさん。はい、凄かったっす。たまに脱走して戻ってきたら、この暗室に自分が入れない…という状態になっているほど人たくさん。「こういう謎のオモシロ展で、人がどれくらいかなぁ…」と思っていたら、すげーかった。時間があっと言うまに過ぎて、他の皆さんのを見て回る余裕が無いくらい凄かった。あんまり他の所の写真を撮れてなかった…

1日目が終わって、ふと前スレのDepth輪郭に手を入れて見ようか…と思って、声をバーっと出すと投影しているのが分身/分裂するようにしてみたら…チビッコ達がワーキャー!!と声を上げてくれて、展示の3要素と思っている「光る(LED群)・大きい(バーが全体的に動く)・音がなる(人間の声)」が揃った。対応の忙しさに自ら火に油を注いでしまったか…と思ったけど、チビッコがキャーキャーしてむしろ楽しかった。

分裂しているのがブレてよく撮れて無いっすね。背丈ほどのフラを持って、声を出して、謎の動きをするバーの前のチビッコという謎のこの状態の写真が何気に面白い。

新聞に掲載されて、「新聞でこの光るのを見て、何かと思って来てみた」という年配の方とか、わざわざ来てくれた方とかいたり有り難かった。
とにかく、めっちゃ沢山のお客さんで凄かった。運営・手配して頂いたみなさん、ありがとうございます&次回or来年も楽しみにしています。

depth contour man

これはMaker Faire Bay Area 2017でダークエリアでプロジェクタを使って何か投影したいな…と思って、フライトの前日くらいにサクッとノリで作った。元々はDroneへの投影で使ったプログラムを流用して、少し変化をさせたものでXtionを使った。Depthをとって、輪郭抽出、密度を変えたり3Dの距離に応じてメッシュ化していたりもするけど、超シンプルにしてみた。Depth周りをやったことがある人なら、そっすね…1時間くらいでサクッと作れちゃうやつです(多分)。



超気合いを入れた凝ったものより、サクッと作った物が何故かウケが良くなる…というのは良くあることで、何故かこれを見て踊る・動く・声をだす(上モノフェスでは、さらっと改良をして声の大きさで分身させるようにするエフェクトを入れたり)、楽しんで貰えて何より。自分も、こういうシンプルな物の方が分かりやすくてウケるというのが理解できた。



これは投影する前の状態をキャプチャーし続けていたもの。ずーっとDepthデータも保存していたら何かで使えて面白いかも…と今更思っていたり。

ブースの前はこんな感じ。ずーっと人だかりだった。

わりと好きな1枚。ハートマークを作って遊んでいた。人が重なると千手観音みたいなことも出来るのよね。

前スレのフラとPETS、菊池さん、itogさんのも含めて光モノ展な感じ。左のLEDマトリックスも綺麗。この3人であの超大量のお客さん達をよく対応できたと感慨深いものがあったりする。

LED hula hoop

年初に光るフラフープを作っていた。写真で見ると幻想的になる。


Maker Faire Bay Area 2017


Maker Faire Tokyo 2017


Maker Faire Tokyo 2017


Maker Faire Bay Area 2017

単に光るフラフープはあるけど、これはネットワーク(WiFi)で接続して色を制御することができる。Bluetoothを使わなかったのはスマホとかからBluetoothで接続して利用するより、WebAPI(小さいサーバがフラフープの中で動いている)としてアクセス可能にした方が、PCやスマホからHTTPリクエストをするだけで簡単に使えるという利点の方が高いと思ったからだ。つまり…

「WiFiに接続して色を変えて遊べるフラフープ」という着地点になる。

利用したのはESP、LiPo、LED Stripsというシンプルな構成で、フラフープの中に丸っと組み込めばOKだろう、と安易に思って取り掛かることに。
というので問題になったのが、フラフープの径だった。外径が20mm、内径が12mmのフラフープをゲットして、組み込むことになるのだけど、このサイズに適合する端末と電池を考えるという問題になった。

LEDは入る。問題はESPとLiPoだ。ESPはアンテナに注意して削って小さくして収めることができた。LiPoは最初は単三電池x3のフラフープ用のホルダーを設計してみたけど、単三x3だとフラフープの中に入れた時に電池の長さ(単三x3分)で光らない影になる部分が出てくる。そこでフラに入って容量も大きい電池を探すことになった。

結局利用したのは、Trustfireのこちら。これをフラフープの内部に収まるように設計したホルダーに入れて、ESPとLEDと結線したら良い感じに。

ESPの方は内部でWebサーバを動かして、WiFiに接続したら他の端末からWebAPIを叩いて色やパターンを投入して変えられるようにした。LEDの総数は50個程度だから簡単だけど、これをなるべく即時で部分的に色を変化させる(1-20は赤、21-40は青、41-50は緑)、グラデーション変化をネットワーク経由で与えようと思うと少し考える必要が出てくる。出来るだけ通信の回数・サイズ(帯域)を抑えつつ効果が出せるように。
そこで出来るだけ簡素なAPIリクエストでガラッと色を変えられるように仕様を考えて、サクサクと色(RGB 255/255/255)を、全体・部分・パターンでサクサクと変えられるようにした。

フル充電だと光りながら2-3時間くらいは頑張ってくれる。電池容量的にそんなに持つはず無いだろうと思うかもしれない。もちろん、LEDが常に全力で点灯しっぱなしだと30分くらいで電池が切れてしまう。ただし、見栄えとして光る部分/光らない部分やグラデーションをつけたりするから常にフルで光っぱなしという訳では無い=>利用する環境次第で現実的に運用すると結構持つ、ということになる。

案外これは良い感じかもと思ったのが、LEDと端末、LiPoはフラフープの外径の硬質プラスチックで保護されるから、相当強烈な衝撃を加えても大丈夫。LEDの発熱も全く大丈夫。と言っても、Maker Faire Bay Areaでちびっ子達にバシバシぶん投げられたり、アスファルトに叩きつけられたりして、中の配線がちぎれて1個壊れてしまった…多分数千〜万人には触られたとは思うけど、パターンで光るプログラムも投入していたから完全に放置でOKという楽な部分はあった。そして壊れたから補強と対策をやって、Maker Faire Tokyo、上モノフェスの方は放置しておいても大丈夫だった。

こちらはMaker Faire Bay Areaで回していた人たち。首で回すというのは初めてみた。


現地のダンサーさんがやたら気に入って踊っていた。「売って」と言われたけど残念な事に、今売ってしまうと展示が出来なくなっちゃうのですよ>< あとでカメラマンも連れてきてバシバシ撮っていた。

微動だけで回していた人とかすごい。

露光で回しながら何か撮ったりとか、夜・暗室のイベントとかで遊んで使えると思われ。ちなみに…自分はほとんど回すことが出来ないっす。

lighting paper lantern



よく光る提灯や上下に稼働する物はあるけど、提灯自体が稼働して動いたら面白いんじゃ…と思ってカッと作った。

最初はIKEAの紙提灯をみた時に作れるのでは?と思って、何個かゲットしてきたのが最初だった。

これで稼働させると次のようになる。




これを利用すると縮む時に微妙な動きになる。というのは提灯は中の紙巻が螺旋状になっているため、力を加えるとどうしても斜めから縮む・広がるといった動作になる。ちなみに中の構造は次のようになっている。

非常に簡単だ。よく利用するParticleにモータードライバ、LEDにモーターと電源でLipoが入っている。色の変化はネットワーク経由でMQTTでRGBを制御、提灯の伸びる・縮まるといった動作はモーターで巻き上げる事で制御している。大きくなる時は自重で下がるように重さを調整している。
モーターを2個利用してよりパワーアップ、巻き上げを面で行えるようにしても、どうしても螺旋状の不均一な巻き上げ・伸びを解決できなかったから、別の提灯を利用することにした。

コストパフォーマンスが良くて、巻き上げ・伸びを綺麗にできる物はないか…と思っていたら、ダイソーの提灯を発見した。これは中の構造が螺旋状ではなく、同心円状になっていて均一な縮み・伸びが実現できそうだ。

動かして見るとこの通り良い感じ。



上下に稼働するのに加えて、形状も変化して大量に並べて光らせて遊んだら、わりと楽しいと思われ。

mbedTLS implementation to Particle

Now I contribute/maintenance 2 TLS client library to the Particle, TlsTcpClient, MQTT-TLS. Many developer request is “Please update to the light-weight library”, then I update to the light version!!
Initital TLS version(0.0.1) size is 111Kbyte flash image, too big:-< here is TlsTcpClient sample sapplication build.

Now latest version MQTT-TLS(0.1.3), TlsTcpClient(0.1.15) 40Kbyte over size down 70Kbyte:) I think only TLS library real size maybe 60-70kbyte.

8 Cipher Suites are included in this library, TLS_RSA_WTIH_AES_256_[GCM|CBC]_SHA384、TLS_RSA_WTIH_AES_128_[GCM|CBC]_SHA256、TLS_RSA_PSK_WTIH_AES_256_[GCM|CBC]_SHA384、TLS_RSA_PSK_WTIH_AES_128_[GCM|CBC]_SHA256
here is MQTT-TLS tls 1.2 negotiation flow(my MQTT server port is 1883).

It’s hard to lightweight TLS library for think about the selection of the TLS protocol/cipher suites and algorithm, application have to include 2048bit root certificate as a result it waste 2048bit flash area. I think maybe more lightweight TLS library(about 50kbyte) could be. If more issue/request coming, I will try to update the library:)

—–

mbedTLSの組込で分かったこと に続いて…TLSをメンテしたり、色んなリクエストに対応しつつTLSの組込という事をしている。この2つTlsTcpClient, MQTT-TLS

めっちゃ軽量化した。111kbyteから70kbyteに、40kbyteの軽量化。これだけ空けばアプリも十分書けるでしょう。ちょっとヤベーなーと思って軽量化した理由のひとつに、”USING TWILIO SYNC WITH MQTT ON A PARTICLE PHOTON“で使っちゃってるやん…完全にネタで作ったのにDocに入れられると、危機感を覚えてマジメになるというか何というか。twilio以外にもmathworksとかsamsung, losant, 本家のIBMに…etc だったりMQTT使って便利だぜ!!みたいなのが使われていたし、マジメに対応しなきゃなーと思っていたりはしていた。

ただ、「軽量化して!!」というリクエストは受けていて、完全スルーしていたから良い機会だし、セキュリティを考えつつ軽量化をした。Cipher Suiteは固いものが8つ。まぁ大丈夫でしょう。

■ サイズとの戦い、軽量化!!軽量化!!軽量化!!
TLSは基本(サイズ的に)大きい。普通のPCでもメモリに16Gbyteとか余裕で搭載する時代に、たった数百〜数kbyteを巡って軽量化を考えるのは地味な修行になる。

例えば、2048bitのRoot CAをアプリに内包する必要があるけど、それだけで2kbyteもの領域とメモリが強制的に消費されてしまう。さらにサーバー証明書を使うとなると、さらに2048bitのメモリの領域が消えて行く。現時点の70Kbyteのサンプルアプリだと、約3%もの領域が何もしなくてもflash領域で使われて消えてしまう…これはぶっちゃけ辛い。

例えば、無理やり方法でやってみたのが、
1.端末のEEPROMにデータを退避させておく。
2.librayrで必要なメモリの一部でEEPROM/SRAMを利用。
といった、使える領域は何でも使ってみる戦術をやってみたりした。ただ、EEPROMは回数制限があるし、あまりにも変態的な使い方になるから、正攻法で普通に実装した。

またリクエストがあれば軽量化をすると思う。もう少し頑張れば50kbyte台も行けると思うし、特定のCipher Suiteに限定すれば更に軽くすることも出来るから。