hirotakaster のすべての投稿

LINE CEK with Arduino ESP32

LINE CEKを使って色々と遊べそうだったから、ちょっと遊んでみた。
 

■ 何にするか…
ESP32があるから、「照明をつけて・消して」というと、LEDの点灯制御をClovaから行えるようにしてみる。いわゆる…IoT的な連携の基本動作といった感じ。
LINE BOTと連携して、ESP32側からLINEに向けてデータを送信する事も出来て、スピーカ・端末・アプリの3者連携は何気に面白いこともできる。
 

■ 全体構成
全体的な接続の流れはこんな感じ。

LINE Clova Wave <=> (LINE社のサーバ) <=> golang(ヲイラのサーバ) <=(MQTT)=> ESP32

LINE CEKと自分のサーバ側(golang)と接続して、端末とのインタフェイスはMQTTで。IFFFTとかでも良いだろうけど、MQTTにしておくと後々で便利というかIoT的(?)な感じ。あとgolangの部分は自分のサーバを使ったけど、nodejs(firebase function)を使うとサーバーレスでサラッと無料で作ることもできる。
いまふと思ったら、firebase function NodeJSの方がクールだったかな…けど、この手のサラッとしたWeb APIというかエンドポイントはgolangで作る方が自分は軽い。まぁ、なんでもいいけど。
 
 
■ モデルのビルド
まずは他のスピーカー類と同じく、言語モデルを作っていく。他で作ったことがある人なら、めっちゃ簡単にサクサク作れると思われ。このLEDのオン・オフのモデルを作る時の自分の考え方としては…

カスタムスロットには、「人が言うオン・オフの言葉」を別々に用意しておく。
インテントは一つだけ用意して、オン・オフ部分の言葉をスロットに割り当てる。

といった感じ。まずはスロットタイプを作る。LED_ON_SLOT/LED_OFF_SLOTの2個を用意しておく。

LED_ON_SLOT

LED_OFF_SLOT

何故にオン・オフで分けたか…というと、言葉の分岐と後で処理を行うgolang部分を楽にするため。
例えば明るくする場合は、照明を付ける、照明をオンにする、照明を点灯する…etc といった色んな表現があって、どの言葉でも「LEDが点灯する」という現象としては同じになる。そこで、LED_ON_SLOTというスロットにひとまとめにしておくことにした。

次はインテント、これは簡単、”照明をつけて・けして”の部分にスロット(LED_ON, LED_OFF)を割り当てるだけ。

ここに2個のスロットをそれぞれ割り当てておくと、”つけて・けして”という現象に対する言葉を、スロットタイプに言葉を増やすことで対応することができる。出来たらビルドをして、あとはテスト。

と…その前に、golang部分のコードはこんな感じ。Json to Goのコンバータで、structはゴソッと作った。フロントは何かWebサーバで終端させて、裏側のgolangにReverse ProxyしてAPとして動作。この手のAPIをサラッと作って遊ぶ時はgolangを使う感じが自分は好き。
あとMQTTサーバは、オープンブローカーで誰でも使える test.mosquitto.org を利用。ここではテストだからOKだけど、マジでやる時はAWS IoT/Google IoT CoreとかMQTTをTLSで終端できるサーバーを利用した方が良い。

package main

import (
 "fmt"
 "log"
 "encoding/json"
 "net/http"
  MQTT "git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git"
)

type ClovaRequest struct {
	Version string `json:"version"`
	Session struct {
		SessionID         string `json:"sessionId"`
		SessionAttributes struct {
		} `json:"sessionAttributes"`
		User struct {
			UserID      string `json:"userId"`
			AccessToken string `json:"accessToken"`
		} `json:"user"`
		New bool `json:"new"`
	} `json:"session"`
	Context struct {
		System struct {
			Application struct {
				ApplicationID string `json:"applicationId"`
			} `json:"application"`
			User struct {
				UserID      string `json:"userId"`
				AccessToken string `json:"accessToken"`
			} `json:"user"`
			Device struct {
				DeviceID string `json:"deviceId"`
				Display  struct {
					Size         string `json:"size"`
					Orientation  string `json:"orientation"`
					Dpi          int    `json:"dpi"`
					ContentLayer struct {
						Width  int `json:"width"`
						Height int `json:"height"`
					} `json:"contentLayer"`
				} `json:"display"`
			} `json:"device"`
		} `json:"System"`
	} `json:"context"`
	Request struct {
		Type   string `json:"type"`
		Intent struct {
			Name  string      `json:"name"`
			Slots interface{} `json:"slots"`
		} `json:"intent"`
	} `json:"request"`
}

type ClovaResponse struct {
	Version           string      `json:"version"`
	SessionAttributes interface{} `json:"sessionAttributes"`
	Response          struct {
		OutputSpeech struct {
			Type   string `json:"type"`
			Values struct {
				Lang  string `json:"lang"`
				Type  string `json:"type"`
				Value string `json:"value"`
			} `json:"values"`
		} `json:"outputSpeech"`
		Directives       interface{} `json:"directives"`
		ShouldEndSession bool        `json:"shouldEndSession"`
	} `json:"response"`
}

func main() {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    var clovaRequest ClovaRequest
    var clovaResponse ClovaResponse

    if err := json.NewDecoder(r.Body).Decode(&clovaRequest); err != nil {
        panic(err)
    }

    // create clova response
    var onoff bool 
    clovaResponse.Version = "1.0"
    clovaResponse.SessionAttributes = nil
    clovaResponse.Response.OutputSpeech.Type = "SimpleSpeech"
    clovaResponse.Response.OutputSpeech.Values.Lang = "ja"
    clovaResponse.Response.OutputSpeech.Values.Type = "PlainText"

        // この部分でスロット名で処理を分岐させる
    if clovaRequest.Request.Intent.Slots.(map[string]interface{})["LED_ON"] != nil {
      clovaResponse.Response.OutputSpeech.Values.Value = "つけたよ"
      onoff = true
    } else if clovaRequest.Request.Intent.Slots.(map[string]interface{})["LED_OFF"] != nil {
      clovaResponse.Response.OutputSpeech.Values.Value = "けしたよ"
      onoff = false
    }
    clovaResponse.Response.Directives = nil
    clovaResponse.Response.ShouldEndSession = false

    outputJson, err := json.Marshal(&clovaResponse)
    if err != nil {
      panic(err)
    }

    w.Header().Set("Content-Type", "application/json")
    fmt.Fprint(w, string(outputJson))

    // sendto MQTT
    go func() {
      ops := MQTT.NewClientOptions().SetClientID("clovagw").AddBroker("tcp://test.mosquitto.org:1883")
      client := MQTT.NewClient(ops)
      if token := client.Connect(); token.Wait() && token.Error() != nil {
        log.Println("Error %s\n", token.Error())
      }

      if onoff {
        client.Publish("clova/led", 0, true, "1")
      } else {
        client.Publish("clova/led", 0, true, "0")
      }
      client.Disconnect(250)
    }()
  })

  log.Fatal(http.ListenAndServe(":8080", nil))
}

そしてESP32側。こっちは clova/led でsubscribeして終端している。ここにClova -> golangを経由してデータが流れてくるといった感じ。これも簡単、pubsubclientのサンプルをそのまま使えば一瞬で出来る。

#include "WiFi.h"
#include "PubSubClient.h"

const char* ssid = "....";
const char* password = ".....";
const char* mqtt_server = "test.mosquitto.org";

#define LED_PIN 12

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

void setup_wifi() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  if ((char)payload[0] == '1') {
    digitalWrite(LED_PIN, HIGH);
  } else {
    digitalWrite(LED_PIN, LOW);
  }
}

void reconnect() {
  while (!client.connected()) {
    String clientId = "ESP32Client";
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      client.subscribe("clova/led");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void loop() {

  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

そんで、ESP32側はこんな感じでLEDが光ってくれる。端末側に距離センサーをつけて、Clova側から距離いくら〜?で身長を測定するような"ものさし"をMQTT経由で送ったり、まぁ...色々となんでも使えると思われ。

Clovaに話しかけなくても、端末側でなにか温度・気圧とか環境状態が変わったら、MQTTでLINEアプリ側にBOT連携して送信したりも。楽しそうかなーと思うのが、LINEアプリ/Clova/端末の3者連携して、LINEのGWをプラットフォームにした空間(部屋)を使って遊べそうな。
LINEのコミュニケーションツールとしての側面はもちろんだけど、ユーザーとモノ・家をつなぐそれぞれプラットフォーム、そしてルーム=部屋=仮想空間として考えると、また違った風景が見えてくる。IoTやM2Mのような、モノとモノや人が介在するという以上に、アプリや仮想空間(ルーム)も関与して、それぞれがプラットフォームを介して相互にやりとり出来るといった感じ。

例えば、振動モーターが付いた端末で、ルーム同士で気に入って仲間になったら、遠隔でブルブルさせてその反応が音としてスピーカーから聞こえるとか。まぁこれはエロい用途にしか使え無さそうだけど。
ちょっと気になるのは、スマートスピーカーを使った目隠し将棋だ。やった事がある人なら分かるけど、駒盤も何も見ないで音(23歩と言って駒を動かしていく)だけで対戦する。観戦者だけはLINEアプリ上で、実際の戦況が見えるといった感じ。「あぁ...完全に位置関係見失っちゃってるよwww」とか、見ている方も何気に面白かったりする。
何にしても、色んな面白いアプリが出てくればなー...という期待な感じ。

倒立振子をサクッと作る

ちょっとPID制御の関係で倒立振子をネタで作った。制作時間は4時間ほど。ネット上のコードのお助けでサクッと。皆さんの英知、素晴らしい。動きはこんな感じ。

 

 


 

 

■ 必要なもの
全て手持ちであった物を使っている。メインで使うものはこんな感じ。MPU6050でロールを取って、それに合わせてモーターを動かす。モーターはTB6612FNGデュアルモータードライブを使って。トータルで3000円弱といった感じかな。

ATMEGA328P 3.3V : $2.1/pcs
TB6612FNG モータードライバ : $1.05/pcs
MPU 6050 : $0.81/pcs
タミヤギアボックス : 615円
タミヤ スリムタイヤ : 373円
タミヤ ユニバーサルプレート : 455円
Lipo 720mAh x 2: $3.0/pcs

このほかにケーブル類、ブレッドボードとダイソーでゲットした両面テープを使った。

■ 組み立て
まずはギアボックスを組み立てて、ユニバーサルプレートに固定する。ギア比は114.7:1を使った。ちなみにモーターにはノイズ対策でコンデンサを適当にくっつけている。

次にダイソーの両面テープでユニバーサルプレートの上に、ブレッドボード固定用の両面テープをくっつける。厚手の両面テープでも3枚重ねにしていい感じにくっつける。

次にブレッドボード上につけたMPU 6050とモータードライバ、ATMEGA328Pをユニバーサルプレート上の両面テープにくっつける。接続はI2CでMPU 6050とATMEGA328Pを接続して、モータードライバはPWMで制御する、といった感じ。まぁ、普通にブレッドボード上に接続してそんな感じ。この前に両面テープを3枚重ねにしたのは、ギアボックスとユニバーサルプレートを固定するプラの高さに合わせていた。

LiPoはギアボックスの下あたりに2個重ねで。1個はモーター駆動用で、もう1個はATMEGA328P/MPU 6050/モータードライバ駆動用に与えている。

これで物は完成。良い感じに重心のバランスを取ってあげるといいかも。まぁ、適当でもいいけど。

■ コード
プログラムはgithub上に上げている。こちら。VScode/PlatformIOで動かせる。

■ 参考
参考にさせていただいたのはこちら。自分のプログラムの中ではカルマンフィルタでMPU 6050の値にフィルタをかけてからPIDの処理をしている。
倒立振子を作る
Arduino-KalmanFilter

chaotic magnetic fidget spinner

磁石を付けたハンドスピナーを作成した。


Maker Faire Tokyo 2018で展示をしたり、togetterでまとめられていた。他はMakezine(日本版)とか、boingboingとかで。

How to Make

作り方は非常に簡単だ。必要な物は、100円均ハンドスピナー、同じく100均で購入できる8個入りのネオジム磁石、そしてペンチがあればOKだ。

まずはペンチを使ってスピナーの外周、金属になっている部分をうまく外す。ニッパーで斜めに金属部分に力を加えるとあっさり取れる。この金属の部分がネオジム磁石とくっつくのがポイントになる。

あとは外周の3点をペンチでカットして、磁石をくっつけるだけだ。数分もあれば出来るだろう。とっても簡単。磁石の所をボンドで固定するのも良い。

動画では磁石の極は外側に向かって全て同じにしている。これで隣のスピナーの頂点にあるネオジムとの強い斥力、隣のスピナーのおもり(金属)部分との弱い引力が隣同士のノードと関連しあってカオティックな動作になる。どれか頂点の磁石の極を反対にしてみると、また違った動きをし始める。引力・斥力と隣のノードとの関係性が効いてくる。後は両面テープをベアリングが格納されている上蓋の所に貼って、床や板にペタッとくっつければ完成だ。


磁石の極を色々と変えてみて動かして遊んでみたり、この手順だと2個x3箇所で磁石を積んだけど、1個にしたり3個積みにしたり、高さとスピナー同士の距離を変えてみたりしても大丈夫。一つのノードが全体に与える影響を簡単に可視化して、実際に手にとって動かすことが出来る。あとは1個の動き(=情報)が全体に拡散していくという、情報の伝播についても考えることが出来るだろう。こっちでも記事になっていたけど、発電・LEDを点灯させたりも出来る(かもしれない)。

Maker Faire Tokyo 2018では104個を使用した。

Article : Introduce Some Chaos Into Your Life With Those Old Fidget Spinners, 使わなくなったハンドスピナーで人生にカオスをもたらす方法, Fidget spinners + magnets: glorious chaotic motion

Maker Faire Bay Area 2018

Exploratoriumに続いてMaker Faire Bay Area

会場全体はこんな感じ。

ざっくり東京ドーム3-4個がスッポリ入る感じの広さ。かなり広い。今年、感じたことは、

– Dark Roomが縮小された。ずーっと3のエリアで凄まじく広い所が今年はEducationになった。
– 8の所にMixed Realityのルームが出来た。
– Microsoftの出展がなくなった。いつも2の所とかにあったのに、今年はなぜか車のLEXUSが置いてあった。
– Droneレース・バトルがなくなった。来年は復活するだろうか。
– NINTENDO LABSがメインスポンサーで部屋を丸々1個使っていた。入場制限で超行列だった。
– 安定のFireと巨大ロボ感

スケジュールとしては、1日目はプレとパーティで来場者は少なめ、2,3日目が一般デーで凄まじく来場者が来る。初日のメインは…出展者とこの日のゲスト限定のパエリアパーティ。このサイズのパエリアが何個もあって、ゲストに振る舞われる。

ビールとパエリアは食べ放題(あまって持っていく人も)、はらっぱに座ったり寝転んで・作品を見ながら飲んで、食べて…といった感じ。まさに自由な空気を一番味わえる時になる。これは他のイベントではあまり無い感じで、自分はこの瞬間が一番好きだったりする。
久々に会った人との話を楽しんだり、自分はよく飲んじゃう方で、ビールを沢山のんでいた。


■ 自分の展示物
自分の所はこんな感じ。

セットアップしたら。あとは見に来る人でめちゃいっぱい。持っていった物は前スレに書いた通り、投影物・フラ・led rodの3点。

フラはいつも、どこからともなくガチのダンサーさんがやってきて広い所で好きに回してくれる。

New video by Hirotaka Niisato / Google Photos

led rodはKinectとか人の動きに合わせて動かすこともできる(実際、以前やった時はUnityとKinect v2連動させて多数動かした。一番近くの人の動きを見て寄って動くとか)。ただトラッキングした数は常に秒間数名は拾っている計算でのべ7万人ほど。凄まじく大量の人で、個別にトラッキングできる状況になかったからランダムで動かした。この辺は、また上手い見せ方があったかも知れない。

New video by Hirotaka Niisato / Google Photos

投影物とフラとled rodが合わさって、こんな遊べる空間にできた。これはキット?売らないのー?とか、ステージで使いたい等のリクエストがあったり。
サーボ・ステッパ・ドライバといった非常にシンプルな構成だから、何気に簡単に作れる。ステッパ・サーボの可動部の3Dモデルはあるから、自分でなんとかしてね:)って感じだったら、ご自由にどーぞ!!ってことでコードも込みで公開するかもだけど、自分の余裕のある時に…かな。

New video by Hirotaka Niisato / Google Photos


■ダークルームの住人達
自分のほかにもダークルームには色んな物がたくさん。今年は屋外が微妙に寒くて、LEDやら光りもの・閉ざされたダークルームは暖かくて過ごしやすかったのか、めちゃ人がたくさん。
ダークルームのMaker・クリエイター達はこんな感じ。

こちらはExploratoriumでも展示していた、音の波長・振幅を手前のトルグでいじって浮かばせてダンスする様子を楽しむもの。初めて見たのはかなり昔だと思うけど、何度見ても素晴らしい。

New video by Hirotaka Niisato / Google Photos

お隣の光るピアノ。もう弾かせてー&ヲレの弾く曲を聴いてくれ!!みたいな感じで、ピアノの音が鳴りやまない事はなくて、いつも曲が流れていて素敵だった。

New video by Hirotaka Niisato / Google Photos

全天球パックマン!!所々で途切れているのがじんわりと良い感じ。

New video by Hirotaka Niisato / Google Photos

こちらは乗って操作できる巨大ネコ。ベースは自動車かな。ちびっこが沢山キャーキャー言って動かしたり登ったりして遊んでいた。こういう巨大な物をドン!!と作って持って来れるのは羨ましくもあったりする。

半天球LEDで、さらに回りには360度でスピーカーが全周についているヘルメット。続々とチビッ子が「やってみて良い?」と被っていた。

こちらも毎年見ているような気もするけど、グラスとヘッドフォンと装着してまるでトリップしているような感じ。ディストピア感が漂ってくる。

Makezineにも載った鈴木さんのホネホネさんも。暗い所だとLEDが良い感じにキレイに見えてめちゃ良い。


■光の世界の住人たち

でかいクマの向こうに見えるのは、メインスポンサーさま、NINTENDO LABS。自分は入れなかった。凄い長さの行列が出来ていて、自分も並んで約1時間…でようやく半分弱まで進んだ。これからもう1時間以上、並んで待っていることは出来なくて挫折した。
Maker Faireは家族で来ている所も多くて、並んでいる自分の眼の前のご家族で子供と嫁さんが「ちょっと、あっちのホールに行ってくるから並んでいてちょうだい」と言われて一人並ばされていたお父さんに、なんとも郷愁を感じてしまった。ここに一緒にあと1時間以上、自分は並んでいる事は出来なかった…

こちらはいきなり出来ていたMixed Reality部屋。去年ってVR部屋とかあったっけ?ダークルームのSVVR(Silicon Valley Virtual Reality)は暗闇の住民だぞ、とか思いつつ中へと。もしかしてHololensが沢山あるのかなー?とか思ったら…

まずは小学校からニワトリになれるVR。小学生がVIVEで作ったもの。その名の通り…学校で飼っているニワトリになって餌を食べたり飛び立ったり。説明しているチビッコがかわゆすぎた:)

ルービック・キューブで各面がディスプレイになっているゲームコンソール。24面の立体ディスプレイといった感じ。

他にもステレオカメラ、Vuforiaを使ったものとか。MRというよりも、VR物は全部ここに集めたんだと思われ。ただ、SVVRはダークルームの住人だったけど。
そして次は定番の巨大ロボや制作物・ファイヤー!!なモノたち。まずは巨大ロボ。去年は水道橋重工とバトルを行ったMegaBots、そして今年は… Prosthesisの登場。

実際に歩いている所は少しだけ見れた。自分の身長だと遠くから…アスファルトの上をロボに人が乗り込んで操作するというのは夢があって良い。そして、去年も見たような、毎年見ているような気がするイカさん:)

お約束とも思えるファイヤーなRabid Transit。2017年のBurning Manでもやっていたみたいね。ググるとよく出てくるけど、Maker Faire Bay Areaに出展して、Burning Manでも出ている…というのは見かける。Burning Manの方は行った事は無いけど、何となく行ってみたい。
見て分かると思うけど、”柵”は無い。周りに消火器は置いてあるものの、好きな距離で見ることが出来る。といっても、自分が撮っているこの距離でも火炎の熱気を感じて、これ以上近づくことが出来ない。この熱気が見えない柵としても機能しているような気がする。

New video by Hirotaka Niisato / Google Photos

そしてこんなモノも…そう、IBMの量子コンピュータQ
パット見で…お寺の大伽藍かと思ったよ。ただ、こういうのもMakerとして持ってくるIBMのキップの良さが素敵だ。

今更ながら欲しくなってきたTinyFPGA。Arduino物はモチロンだけど、いろんなマイコンが売っていたりもする。KickStarterを始めとしたクラウドファウンディングに出している所は、ほぼ見かけるような物ばかり。ただ、ビジネス展とは違って、売る気満々という訳じゃなく、「これはどや!!これ使って何を作る!?」といった感じ。

同じ目的のものがすれ違った瞬間

もちろん、IT的なものや電気を通した物ばかりじゃなく、手芸や自家製の食べ物といったのもある。いわゆる物づくりの本質で、必ずしも光ったり電気的な物やセンサー類を使った物が展示の対象という訳でもない。自分でMakeした物、自分で作ることの楽しみ・経験・体験が尊いんだよね。

食べ物は沢山。そしてワイン・ビールもある。美味しい物を食べて、飲みながら面白い物を見て回って楽しめる。

最後にこちらは、Particleから貰ったお土産。もう少しで出てくる新製品Meshの話とか、6LoWPANやネットワークの伝播の仕組だったり機能の話を聞いていて、面白いなーと思っていたら…

「いま、Meshはプレオーダー出来るよ」
「プレオーダーで、確か3個ずつオーダーしているよ」
「おぉ、ありがとう!!これで何か作る予定なの?」
「一応、MQTT(TLS)とか、もうcontributeしていてね」
「マジか!? ヲマエはhirotakasterか!?」
「うん、マジで自分だよ。」
「あのイケてるlibrary作ったのヲマエか。ヲレはコミュで話しているpeekayで、こっちは…」
「おー、マジっすか!? 会えてめっちゃ嬉しいよ」
「これ持ってけよ」
(以降、キャッキャ)

という事で、Tシャツとかトート・NRF・ステッカー類を貰った。何気に目の前にある現物=Meshが欲しかったけどw
ParticleのMesh(ESP32/nRF52搭載)はマジで期待度が高くて、話を聞いていたらすげーオモシロ機能満載でオススメ感(日本で技適的なあれで使えるかどーか不明だけど…)。エンジニアもオモシロ&優しい人たちだし。
こういう出会いがあったりして、何気に楽しい。

Maker Faire行っても一般向けだから売れないし、行ってもしょうがない…という事はたまに聞いたりする。ビジネス向けの展示会では無いから、そりゃそーだというのもあるんだけど、売ろうと思えば売れるし話になったりもする。クラウドファンディングに出しているプロダクトの出展も沢山あるし。
とは言っても、そもそも何で?という所には「楽しいから」という答えがベースにある。ビジネス目的で行っている訳じゃないからね。この辺は、仕事をしただけ儲かる社会から、文化的な所や精神的な物にも価値を見出していくという、価値と文化が社会でどういう扱いで成長・形成していくのか?という部分や、個々人の価値基準に依存する部分だからね。
「クリエイターやMaker・何か作っているひとはマジで楽しいから行ってみたほうが良いよ!!」とは心から思う。ただ、それは押し付けでも何でもなくて、気になったらどうかな…って感じ。

ただ、”楽しいから”、”ヲレの作ったものを見てみない!?”とかが動機で全然良いんじゃないかなーと。後のことは、あとの事でも良いんだろうし。まず自分でやってみる所から。小さいモノでも大モノでも何でも。

ということで、今年も楽しかった!!来年も楽しみに行くと思われっす:)

Maker Faire Bay Area 2018 – Exploratorium After Dark

Maker Faire Bay Area 2018 報告会も終わって、ようやくblogを書く気になってきた。
報告会はゆるい感じで良かった。飲み・食いものは自分で持ってきてね:)ということで、本当にありがたい事に大量の差し入れで、これは無くならないんじゃ…と思ったけど、きれいに無くなってビックリした。
ちなみに自分は始まる前から、すでに酔っていた。あまりに太ってしまったせいで、ココナッツオイル(MCT)減量を懲りずにはじめいた。昼を食べてなかったせいもあって、空きっ腹にアルコール。すぐ良い感じになってしまった。

という事で、今年も行ってきたMaker Faire Bay Area 2018について。まずはExploratoriumから。

■スケジュール
今年はこんな感じの予定で行ってきた。
5/16:出国
5/17:Exploratorium After Dark
5/18-20:Maker Faire Bay Area 2018
5/21-22:LAでぶらり
5/23-24:帰国

■持っていった物
Slime Lamp

距離もの

LED Hula Hoop

LED ROD

ボツったもの
– 扇風機でLED POV
理由 : 扇風機の電源が100Vのみ(米国は120V)、荷物的に入らないことが判明という2重の理由で

– Typing Machine
理由:荷物的にこれ以上は入らないから却下。

– 作ろうと思っていたアイテム
理由:時間的に見せられるレベルまで作り上げるのはムリだと判断

ということで、トータル4個のアイテムを持っていった。荷物の写真を撮っておくのを忘れたけど、キャリーは23kgギリギリで着替えとかはバックパックに入れて、PCはまた別のバック…ということで、トータルで30kgオーバーの重量だったと思う。30kgの米俵をずっと持ってあるいて移動していると思えば楽勝だ。

■ Exploratorium After Dark

Exploratoriumは知っている人も多いと思うけど、日本でいうところの科学未来館のような感じ。After Darkは毎週木曜日の夜に大人限定のイベントで、エッジの効いたアーチスト・研究者のデモをやったりしているとのこと。ワインやビールのアルコールを飲みながらOK、ということもあって雰囲気も良い感じ。

脈拍に合わせて点灯する版を持っていって、お互いの光を混ぜ合わせて光が伝播する様子を見せて、「こうやって、二人のHeart Beatが混じり合って…」といった、大人の夜に合わせたような感じの見せ方をした。夜も深まって、真っ暗になってきたらフラも出して、好きに回して遊んでもらった。

自分の他にも日本からデカ顔さんも。

exploratoriumでの展示で思ったのは…
– スタッフが凄まじく良い。現場で「こうやった方がうまく見せれるかも…」と思って、ちょっとお願いした事を何でもOKといった感じですぐにやってくれる。素晴らしいすぎた。一緒に良いものをゲストに見せて感動を!!という気持ちが伝わってきた。
– 多分、自分の知る限り、Maker FaireのプレイベントでExploratoriumで開催されたのは初だと思われ。SNSやプロモーションで色んな媒体に出て、チケットは完売だったみたい。その辺も力を入れていて、集客もしっかり。素晴らしい。
– 場所も人もとても良い。場所は近くにSFMOMAがあったり、回って見るには良い感じだと思う。自分の苦手はオシャンティ層が来るのかしら…と思ったら、みんな興味津々、気さくで良い感じ。

長くなってきたから、本編のMaker Faireは次に続く…

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しているだけ。これらは平文で流れているもので、ぼーっと見ていただけで何もしていないし、情報にはマスクをしている。というか全部マスクしている。

exp) 全トピック参照
mosquitto_sub -t “#” -h test.mosquitto.org -d

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万アプリ越えてくるし、たまに「もー、絶対にやらん、やーめったっと!!」とかって思ったりするけど、仕方なくメンテを続けて行きそうな感じ。