hirotakaster のすべての投稿

Particle Pub/Sub

Particle Meshというデバイスが10月にいよいよ発送される。

Meshという名前のとおり、Bluetoothでメッシュネットワークを構築して、WiFi/LTEでインターネットに抜けていく。メッシュネットワークはThread(6LoWPAN: IPv6)で構成される。各端末のスペックはリンク先を見れば分かるとおり、何気にすごい。Bluetoothのみの端末Xeon、WiFi/Bluetoothで構成されるArgon、LTE/Bluetoothで構成されるBoron(2G/3G版もあり)という3種類。BluetoothはNordic nRF52840、WiFiはESP32という最近流行りの構成な感じ。そして現在のところ、最初のオーダーは送料無料で安い!!
 
 
■ ギテキというもの…
このParticle、昔はSpark CoreというArduinoコンパチ風な端末(ギテキあり)を出していて日本国内でも使えた。そして次に出してきたPhotonという端末が主力になっている。ただ、このPhotonはギテキが無いくて国内では使えない。自分はこのSpark CoreのときにMQTT ライブラリをcontributeしたせいで、次のような現象が起きている。

1. Spark Core(ギテキあり)にライブラリを公開
2. ライブラリがめちゃ使われる
3. Particle Photon(ギテキ無し)が主流になる
4. Spark Coreはほぼディスコン状態
5. ライブラリのメンテ・サポートを続ける…

ギテキ無しの端末のライブラリをメンテ・サポートを続けているという状態だ。現在のところ約12万アプリがライブラリを利用している。恐らくIoT的な側面で考えると、かなり使われている方だと思う。まぁ国内で使われなくても、世界中で使われるとそんな感じかなと。たまに、「2Gの端末で何かおかしいんだけど?」と聞かれるけど、そもそも日本には2Gは無いしデバックをお願いしていたりもする。

ここで思ったのが次のMeshだ。ギテキ無いんじゃね?みたいな(お約束)。ざっくりこんな素敵な構成が出来るのに…

コミュニティに上がっている最新情報で、実際に動いているネットワークのトポロジなんかを見るとわりと感動するものがある。国内で使えるよう、どこか持ってきてくれないかしら…まぁ、自分は10個ほどプレオーダーしちゃってるんだけどね。どうせ、またメンテしないといけないし(白目)。

this pic from Particle community site.
 
 
■ WebからOTA、IDE環境、NodeJS、iOS/Android…etc
このParticleがイケているのが、周辺環境が恐ろしく充実しているという点もある。Web IDE上でコードを書いてWiFi経由でアプリを流し込めて、端末やライブラリの管理を行える。ログもWeb上で確認できる。これがSpark Core(2013年)時点から使えているのだけど、何気にこれが良い感じだったりする。

さらに面白いのがMQTTのようなPublish/Subscribeの環境が利用できる。正確に言うとMQTTではなく、CoAPをDTLS上で動かして実現している。このWebIDEはよくあるAzure,Google Cloud、Webhookにデータを流すように利用することができて、大抵のクラウド環境との疎通もParticle上を経由してデータのやりとりをすることが出来る。あとSIMの管理とかもあったりする。

つまり…端末をネットワークに接続すれば、あとはすべてWeb上からコードを書いて、ログを見てデバックして(もちろん、PCにUSB経由でシリアルも出力)、NodeJSやWebAPIを使って自前のシステムを作ったり、iOS/AndroidのアプリもOKと…かなり充実した環境になっている。

NodeJSはこんな感じでParticle CloudとのPub/Subをサクッと書ける。

var Particle = require('particle-api-js');
var particle = new Particle();
var token;

// データのPublish
particle.publishEvent({ name:"banjo_normal/get", data: "test val", isPrivate: false, auth: 'auth token' });

// SubscriveしたEvent情報ほ処理
particle.getEventStream({ deviceId: 'banjo_normal', auth: 'auth token' }).then(
	function(stream) {
		stream.on('event', cloudEventHandler);
	},
	function(err) {
		console.log("error starting event listener", err);
		process.exit(1);
	});

cloudEventHandler = function(data) {
	console.log("Event", data);
}

そして端末側はこんな感じ。A0からアナログデータを読んで、Publishする。コードはArduino likeに書ける(C++)。

#include "application.h"

String deviceName = "";

void subscribeHandler(const char *event, const char *data) {
    Serial.print(event);
    Serial.print(", data: ");
    if (data)
        Serial.println(data);
    else
        Serial.println("NULL");
        
    // publish sensor data to "[device_name]/put/val" endpoint
    int val = digitalRead(A0);
    char buffer[128];
    sprintf(buffer, "%d", val);
    Particle.publish(deviceName + "/put/val", buffer);

}

void namehandler(const char *topic, const char *data) {
    // get device name
    deviceName = String(data);
    Particle.unsubscribe();
    Serial.println("This device name is : " +deviceName);
    
    // subscribe devicename/data endpoint
    Particle.subscribe(deviceName + "/get", subscribeHandler);
}

void setup() {
    Serial.begin(9600);

    // get device name from cloud
    Particle.subscribe("particle/device/name", namehandler);
    Particle.publish("particle/device/name");
}

void loop() {
}

自分がcontributeしているMQTT(TLS)を使うと直接外部のクラウド(Azure, Cloud Core, IoT Core…etc)と接続して通信を行うことが出来る。逆にParticleのPub/Subはサービスとしてエレガントになっていて、端末の起動・停止といった情報をとって既に用意されているSDKを使って開発を進められることが出来る。外部との疎通はどちらを使っても大丈夫になる。

このPub/Subは現在のところ、MQTT・Particleのどちらを使おうとしてもサーバーを経由したメッセージのやりとりになる。次のMeshになるとThread(6LoWPAN)上のメッシュネットワークに閉じたメッセージングと、今までと同様のインターネット上のゲートウェイに投げるメッセージの両方が使えるようになる。
これはかなり面白い。インターネットへの疎通がなくてもメッシュネットワークを構成して、WPAN上でのメッセージングが出来る。また、自身はインターネットに接続していなくても、メッシュネットワーク経由で上位のエッジ(Argon, Boron)を経由してインターネットに抜けることが出来る。
人が多くて混雑した所での利用、大量の端末・制御を使いたいときは有効に働くと思われ。
 
 
■ ギテキ、うまいのそれ?
当初のSpark Core, Particle Photonのfirmwareは色々あって、UDPパケットを投げると端末が停止したり、メモリリーク、とあるクラスを使うとメモリフラグメント、端末の認証(priavate 証明書)を差し替えるとネットワークに接続不能になったり…いまでも謎の現象が起きたりもする。この辺はgithub上のソースコードが丸々上がっていて、自分で書き換えたりもできる。あと自分で作ったlibをcontributeして使ってもらうことも。まぁ、最初のMesh firmwareはちゃんと動かないとは思うけど、将来的な期待感がめちゃ高い。

ただ、問題はギテキ、これに尽きる…

NASA SDO Data

NASAは太陽の画像データを公開している。SDO(Solar Dynamics Observatory)というサイトで、膨大なデータを公開している。これらのデータはこの辺のパスを降りていくと見ることが出来る。

例えばこんな磁力線がバンバンな写真とか、ゾクゾクしてくるものがある。

この画像データを使って動画として見たくなる…という気持ちが働いて、対象のデータを指定するとある一定日時の分だけクロールしてくるのをgolangで作った。

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"strings"
	"time"
)

const baseurl string = "https://sdo.gsfc.nasa.gov/assets/img/browse/"
// 保存先
const filepath string = "/tmp/img/"

// 取ってくるファイルサイズとデータ型
const targetimg string = "512_HMI171pfss.jpg"

// 何日分とってくるか
const daycount int = 100

// 開始日時
const startday string = "2018/02/01"

func saveImg(url string, filename string) {
	response, err := http.Get(url)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()

	file, err := os.Create(filename)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	io.Copy(file, response.Body)
}

func crawlurl(url string) {
	var client = http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		panic(err)
	}

	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

    // 対象のファイルがあるかチェック
	re := regexp.MustCompile("<a href=\"(.*?)_" + targetimg + "\".*?>")
	group := re.FindAllStringSubmatch(string(body), -1)
	for _, c := range group {
		// get target url
		fmt.Println(url + c[1] + "_" + targetimg)
		
		// save img to local
		go saveImg(url+c[1]+"_"+targetimg, filepath+"/"+strings.Replace(c[1], "_", "", 1)+".jpg")
		
		// 5 second delay
		time.Sleep(5 * time.Second)
	}
}

func main() {
	for i := 0; i < daycount; i++ {
		t, _ := time.Parse("2006/01/02", startday)
		t = t.AddDate(0, 0, i)
		crawlurl(baseurl + t.Format("2006/01/02/"))
	}
}

あまりバンバンcrawlすると迷惑になっちゃうと思うから、ちょっぴりdelayを入れておく。対象のファイルは毎日存在する訳でも決まった時間に出来るわけでも無さそうだから(ただ、ほぼ毎日8時間おき位で作られている)、directory indexから取ってくるのを見て、存在したら取ってくる感じで。画像の権利的なところはこちら。2048のサイズまで使える。ただ、大きいサイズはそれだけ容量を消費するのと動画に変換するときの負荷が高くなるから、とりあえず最小の512サイズで。golangでサラッとした処理を作るときの便利さはハンパない。

これでファイルを取得してきたら、avconvで動画に変換する。

# ファイルを連番に変更して
ls *.jpg | awk '{ printf "mv %s %03d.jpg\n", $0, NR }' | sh 
# 動画に変換
avconv  -r 10 -start_number 1 -i "%03d.jpg" solar.mp4

これで完了。mp4のファイルが出来上がる。こちらは2018年2月〜9月頭まで、約半年分のものを動画にした感じ。

Solar AIA 094/335/193

Solar AIA&HMIB

Ardiunoで簡単にPID処理

よくArduinoでPID処理を行う時に使うサンプル例で、A0とPWM(3)を直結させて、ターゲットになる出力になるまで評価するといった方法がある。PIDってなんぞや?というのは、この記事がとても分かりやすいと思われ。

ArduinoのライブラリにはPIDで検索するとそれっぽく使えるライブラリが出てくる。

PID_v1
FastPID

どれもPID成分を決めると簡単に計算をしてくれる。とわ言え、時間軸に対して比例・積分・微分をしてパラメータ(ゲイン)の掛け合わせと足し算だけだから、自分でやっても簡単に出来る。以下はそれぞれPIDの成分を変えた時、2.5Vのターゲットに対してどのくらいで収束するか?グラフにしたものになる。

P=4(青), 8(オレンジ), 16(グレー), 32(黄色), I=D=0

P=32, I=10(青), 100(オレンジ), 200(グレー), 400(黄色)

P=32, I=10, D=0.8(青), 0.4(オレンジ), 0.2(グレー), 0.1(黄色), 0.01(緑)

PI固定でD成分のパラメーターは横軸(20)以降の2.5Vを中心とした分散(偏差)が最小になるような値=0.01までは出した。0以上、0.01未満くらいの所で、さらに値をチューニングすれば値を追い込めるとは思うけど、そこまではやっていない。ほとんど同じようには見えるものの、D=0.01のときになる。使ったコードはこちら、非常に簡単。LEDとCdsを使ったモノもコードにあげている。こちらはArduinoで使えるライブラリを使ったモノも。

本当は理論値に近づくようにシュミレーションして、評価として実機に流しながらベストな値を求めるのが正しいんだろう。他には機械学習で一番良さそうな値を見つけに行くというのもありだと思われ。

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でまとめられていた。

How to Make

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

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

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

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


磁石の極を色々と変えてみて動かして遊んでみたり、この手順だと2個x3箇所で磁石を積んだけど、1個にしたり3個積みにしたり、高さとスピナー同士の距離を変えてみたりしても大丈夫。簡単に作って、一つのノードが全体に与える影響(カオス・バタフライエフェクト)を簡単に可視化して、実際に手にとって動かすことが出来る。こっちでも記事になっていたけど、発電・LEDを点灯させたりも出来る。

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

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);
  }
}