風をよむ風鈴を作ってみた

# 風をよむ
風をよむって未来を予想って感じですよね
同名の番組が世界情勢を伝える番組ですし
ちなみに慣用句には風をよむってなかったでした

# 作ったもの
[![Image from Gyazo](https://i.gyazo.com/8241212355d287849b230ed172eca4f8.gif)](https://gyazo.com/8241212355d287849b230ed172eca4f8)

## 使ったもの
* [obniz](https://obniz.io/ja) ハードv2.0.0
* [node[(node.org/ja) v12.3.1
* nodeライブラリ package.jsonの内容一部

```json
  "dependencies": {
    "axios": "^0.19.0",
    "obniz": "^2.2.0",
    "simplenoise": "^1.0.1"
  }
```

* [OpenWeatherMap API](https://openweathermap.org/forecast5) 3時間ごとの天気など取得できる

## ソースコード

```javascript
"use strict";

const axios = require("axios");

const WEATHER_API = "http://api.openweathermap.org/data/2.5/forecast";
const CITY_ID = "1850147"; //tokyo
const APP_ID = process.env.WEATHER_APP_ID;

const Obniz = require("obniz");
let obniz = new Obniz(process.env.OBNIZ_ID);

const noise = require("simplenoise");
noise.seed(Math.random());

obniz.onconnect = async function() {
  // 初回に風速を取得
  let windSp = await getNextWindSpeed();
  console.log("get wind speed : " + windSp);
  obniz.display.clear();
  obniz.display.print("get wind speed : " + windSp);

  // １時間に1回風速を取得，更新する
  setInterval(async function() {
    windSp = await getNextWindSpeed();
    console.log("update wind speed : " + windSp);
    obniz.display.clear();
    obniz.display.print("update wind speed : " + windSp);
  }, 3600000); //１時間

  let servo = obniz.wired("ServoMotor", { gnd: 0, vcc: 1, signal: 2 });
  // 基本角
  // servo.angle(90);

  let count = 0;
  // 1.5秒ごとにサーボを動かす
  obniz.repeat(async function() {
    // console.log(windSp);
    let noiseVal = noise.simplex2(0, count);
    // 風が強ければ値の変化の大きさも大きく
    // 風の大きさ5が大きい想定
    // サーボ120が垂直想定
    let servoVal = 90 - noiseVal * 80 * (windSp / 4);
    console.log(servoVal);
    servo.angle(servoVal);
    // 風が強ければ値の変化も早く
    count += windSp;
  }, 1500);
};

// 一番直近の風速度を返す 浮動小数 風速かな？
async function getNextWindSpeed() {
  const d = await getNextWeather();
  if (!d) return -1;
  if (!d["wind"]) return -2;
  if (!d["wind"]["speed"]) return -3;
  return d["wind"]["speed"];
}

// 一番直近の天気データを返す
async function getNextWeather() {
  const res = await axios.get(WEATHER_API, {
    params: {
      id: CITY_ID,
      appid: APP_ID
    }
  });
  // console.log(res);

  // 現在時刻取得
  let date = new Date();
  let nowUnix = date.getTime() / 1000;

  // データから3時間ごとのリストを抽出
  // 現在の日付の0時からデータが存在するので，現在時刻より後ろで一番近いデータを抽出
  let list = res.data.list;
  for (let i = 0; i < list.length; i++) {
    let dUnix = list[i].dt - 32400; //utcと東京の誤差
    // let d = new Date(list[i].dt_txt);
    // データのリストの順番は時刻昇順と信じて
    if (nowUnix < dUnix) {
      // 3時間ごとのデータなのでだいたい3時間後
      return list[i];
    }
  }
  return null;
}
```

一時間ごとに約3時間後の風力を取得してその風力によってサーボモータを動かす振り幅などを決めています
1.5秒間隔でサーボの次の角度の値を決めて回転しています

## パーリンノイズの利用
パーリンノイズをわかりやすく解説してくれているサイト
https://postd.cc/understanding-perlin-noise/

波形や炎，水，雲の生成なんかにも利用されているようで，今回の風を擬似的に生成するのに使用してみました
グラフを作ってみたのが以下です

![random と noise.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76572/505fe66f-f6ab-903d-04dd-cdfafdc954c8.png)

今回のサーボモータに置き換えると縦の振幅がサーボの角度で，横軸が時間とします
パーリンノイズでは振幅はもちろんのこと横方向の変化速度というか波長の調節もできます
風速によりこの振幅の最大値と波長も変えていきます
ちなみに記事最初に載せた動画は風速4の状態です

![風速による振幅と波長の違い.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76572/c862f406-ef75-3af5-7297-5354969d91d5.png)

上記のぐらふでは0〜180°のサーボを利用しているので，90°を中心に±90の変化をさせています
風速が弱いと波長が伸びるようにしていたのですが，いざそのアルゴリズムをグラフ化するともうちょっと伸ばしてもいい気がしてきますね

# まとめ
台風が話題になったときに東京にて利用してみました
台風ってだけ聞いてどうなるんだろうと思ってたところそんなに風鈴がならずで，結果的に東京にはそんなに強い風が来なかったでした
いちよう風をよめたのかなと思います
と，台風と風鈴ってことでより夏っぽくなれたかなと

しかし，サーボの駆動部分が見えているとやはり音よりそこに目がいって，その動きで強そうってなるのでうまくパッケージングしたいですね

音付きの動画をyoutubeにアップしたのでそちらもご覧いただければと思います
https://www.youtube.com/watch?v=2N4i2rjbFcM


今度は逆に普通の風鈴の煽られ具合をセンシングしたいですね