長期休暇　～大人の自由研究～　【obniz】で【ドットアニメーション】に挑戦してみた結果

**IoT**ってよく時事問題とかに出るけど実際に何が出来るのか分からない。
**偶然**にも実家に使われていない**obniz**があったので、色々触ってみた。
スターターキットの中で興味を引くものは**「LEDマトリックス」**と**「ブザー」**だったので
**「LEDマトリックス」**を使って何かを表現しようという内容。
#「LEDマトリックス」を使って「ドットアニメーション」を作る
###何はともあれまずは結果↓

https://twitter.com/ProtoYuta/status/1459061626313711626

テキストをそのまま描画する方法だと文字が潰れたり正直イマイチだったので、
ドット単位で点灯させる方法を選択した。
###初心者でも何とか打開出来た方法
<details><summary>コード</summary><div>

```html
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@3.16.0/obniz.js"></script>
</head>
<body>

<div id="obniz-debug"></div>

<script>
  var obniz = new Obniz("OBNIZ_ID_HERE");
  obniz.onconnect = async function () {

  var matrix = obniz.wired("Keyestudio_HT16K33", {
        gnd: 0,
        vcc: 1,
        sda: 2,
        scl: 3      
  });

  await matrix.init(8);
  await matrix.brightness(7);
  await matrix.clear();

  const ctx = await obniz.util.createCanvasContext(
    matrix.width,
    matrix.height
  );
  ctx.fillStyle = "white";  
  

  //ドットアニメーション（晴れ）
  for(let i = 1; i<10; i++) {

    //wait処理  
    const _sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

    await drawShine(ctx,matrix);
    await _sleep(500);
    await drawShine2(ctx,matrix);
    await _sleep(500);

  };
  await drawShine(ctx,matrix);

  //点滅表示　雨
  for(let i = 1; i<5; i++) {

    //wait処理  
    const _sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

    await _sleep(500);
    await drawRain(ctx,matrix);
    await _sleep(1000);
    await drawNull(ctx,matrix);
    await _sleep(500);

  };
  await drawRain(ctx,matrix);

  //点滅表示　曇り
  for(let i = 1; i<5; i++) {

    //wait処理  
    const _sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

    await _sleep(500);
    await drawClowd(ctx,matrix);
    await _sleep(1000);
    await drawNull(ctx,matrix);
    await _sleep(500);

  };
  await drawClowd(ctx,matrix);

  //点滅表示　雪
  for(let i = 1; i<5; i++) {

    //wait処理  
    const _sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

    await _sleep(500);
    await drawSnow(ctx,matrix);
    await _sleep(1000);
    await drawNull(ctx,matrix);
    await _sleep(500);

  };
  await drawSnow(ctx,matrix);

  
  //関数の定義
  //雨マーク
  async function drawRain(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _ctx.fillRect(2,0,4,1)
    _ctx.fillRect(1,1,1,1)
    _ctx.fillRect(6,1,1,1)
    _ctx.fillRect(0,2,1,1)
    _ctx.fillRect(7,2,1,1)
    _ctx.fillRect(0,3,8,1)
    _ctx.fillRect(4,4,1,1)
    _ctx.fillRect(4,5,1,1)
    _ctx.fillRect(2,6,1,1)
    _ctx.fillRect(4,6,1,1)
    _ctx.fillRect(3,7,1,1)

    _matrix.draw(_ctx);
  }

  //点滅用空白
  async function drawNull(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _matrix.draw(_ctx);
  }

  //晴れマーク
  async function drawShine(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _ctx.fillRect(0,0,6,1)
    _ctx.fillRect(0,1,6,1)
    _ctx.fillRect(7,1,1,1)
    _ctx.fillRect(0,2,6,1)
    _ctx.fillRect(0,3,5,1)
    _ctx.fillRect(7,3,1,1)
    _ctx.fillRect(0,4,4,1)
    _ctx.fillRect(0,5,3,1)
    _ctx.fillRect(6,5,1,1)
    _ctx.fillRect(5,6,1,1)
    _ctx.fillRect(1,7,1,1)
    _ctx.fillRect(3,7,1,1)

    _matrix.draw(_ctx);
  }

  //晴れマーク２
  async function drawShine2(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _ctx.fillRect(0,0,6,1)
    _ctx.fillRect(0,1,6,1)
    _ctx.fillRect(7,0,1,1)
    _ctx.fillRect(0,2,6,1)
    _ctx.fillRect(0,3,5,1)
    _ctx.fillRect(7,2,1,1)
    _ctx.fillRect(0,4,4,1)
    _ctx.fillRect(0,5,3,1)
    _ctx.fillRect(5,5,1,1)
    _ctx.fillRect(6,6,1,1)
    _ctx.fillRect(0,7,1,1)
    _ctx.fillRect(2,7,1,1)
    _ctx.fillRect(4,7,1,1)
    _ctx.fillRect(7,4,1,1)

    _matrix.draw(_ctx);
  }

  //曇りマーク
  async function drawClowd(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _ctx.fillRect(2,1,3,1)
    _ctx.fillRect(1,2,1,1)
    _ctx.fillRect(5,2,1,1)
    _ctx.fillRect(1,3,1,1)
    _ctx.fillRect(4,3,3,1)
    _ctx.fillRect(0,4,1,3)
    _ctx.fillRect(3,4,1,1)
    _ctx.fillRect(7,4,1,3)
    _ctx.fillRect(1,7,6,1)

    _matrix.draw(_ctx);
  }

  //雪マーク
  async function drawSnow(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _ctx.fillRect(3,0,2,1)
    _ctx.fillRect(2,1,1,3)
    _ctx.fillRect(5,1,1,3)
    _ctx.fillRect(3,3,2,1)
    _ctx.fillRect(1,4,1,3)
    _ctx.fillRect(6,4,1,3)
    _ctx.fillRect(2,7,4,1)

    _matrix.draw(_ctx);
  }

}

</script>
</body>
</html>
```

</div></details>
関数の中にある**_ctx.fillRect(x,x,x,x)**がLEDマトリックスの点灯位置を表しているようで
引数は横軸座標・縦軸座標・座標からの横軸への幅・座標から縦軸への幅となっている模様。
とは言え設計図も描かずに出来ないので、仕事でよく使うExcelを利用してみた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1792140/c3a01bc2-3c05-ac33-fa27-175b4f25dbb8.png)
**ピクロス**のような設計図を作成して、ドット絵とコードを紐付け。
視覚的に確認しながらコードを作成することが出来た。
###アニメーションを付けた方法
**パラパラ漫画**や**GIFアニメ**の要領で、ドット絵の差分を用意し交互に表示させた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1792140/ac4ac513-d056-21c8-94e5-98c606ee101f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1792140/55a40456-05eb-e255-7f7a-05bf6b7d0d5f.png)
#作った「アニメーション」をどう活かしたのか
###最終的な成果物↓

https://twitter.com/ProtoYuta/status/1459062024151830533

天気のAPIから情報を取得して、
obnizのディスプレイには：**地域・日付・時刻・天気・気温・室温**を表示
LEDディスプレイには取得した天気情報に**合致するアニメーション**を表示
LED信号は天気に対応した色を点灯させた
温度センサーも付けて、室温を取得している
###詳細について
こちらのサイト様を参考にさせてもらいました。

https://laboratory.kazuuu.net/change-the-color-of-the-led-signal-depending-on-the-weather/

<details><summary>コード</summary><div>

```html
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@3.16.0/obniz.js"></script>
</head>
<body>

<script>
  var obniz = new Obniz("OBNIZ_ID_HERE");
  obniz.onconnect = async function () {

  var matrix = obniz.wired("Keyestudio_HT16K33", {
        gnd: 0,
        vcc: 1,
        sda: 2,
        scl: 3      
  });

  await matrix.init(8);
  await matrix.brightness(7);
  await matrix.clear();

  const ctx = await obniz.util.createCanvasContext(
    matrix.width,
    matrix.height
  );
  ctx.fillStyle = "white";

  var light = obniz.wired("Keyestudio_TrafficLight", {
    gnd: 7,
    green: 8,
    yellow: 9,
    red: 10
  });

  var tempsens = obniz.wired("Keyestudio_TemperatureSensor", { signal: 4, vcc: 5, gnd: 6 });
  var API_KEY = 'APIキー'
  var city = '取得する地域名';
  var url = 'http://api.openweathermap.org/data/2.5/forecast?q=' + city + ',jp&units=metric&APPID=' + API_KEY;
  var i = 2;
  var Wdata;
  var ienaka = await tempsens.getWait();
  
  async function sendWeather(data,i) {
      var Week = new Array("日","月","火","水","木","金","土");
      var date = new Date (data.list[i].dt_txt);
      var month = date.getMonth()+1;
      var day = month + "/" + date.getDate() + "/" + Week[date.getDay()] + date.getHours() + ":00";
      var nowWeather = data.list[i].weather[0].main;
      var nowTemp = Math.round(data.list[i].main.temp);

      obniz.display.clear();
      obniz.display.print(city);
      obniz.display.print(day);
      obniz.display.print("室温 🏠 " + Math.floor(ienaka) + "℃");
      switch(nowWeather){
        case "Clear" :
          obniz.display.print("晴れ ☀ " + nowTemp + "℃");
          light.green.on();

          //ドットアニメーション（晴れ）
          for(let i = 1; i<10; i++) {

            //wait処理  
            const _sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

            await drawShine(ctx,matrix);
            await _sleep(500);
            await drawShine2(ctx,matrix);
            await _sleep(500);

          };
          await drawShine(ctx,matrix);

          break;
        case "Rain" :
          obniz.display.print("雨 ☂ " + nowTemp + "℃");
          light.red.on();

          //点滅表示　雨
          for(let i = 1; i<5; i++) {

            //wait処理  
            const _sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

            await _sleep(500);
            await drawRain(ctx,matrix);
            await _sleep(1000);
            await drawNull(ctx,matrix);
            await _sleep(500);

          };
          await drawRain(ctx,matrix);

          break;
        case "Clouds" :
          obniz.display.print("曇り ☁ " + nowTemp + "℃");
          light.yellow.on();

          //点滅表示　曇り
          for(let i = 1; i<5; i++) {

            //wait処理  
            const _sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

            await _sleep(500);
            await drawClowd(ctx,matrix);
            await _sleep(1000);
            await drawNull(ctx,matrix);
            await _sleep(500);

          };
          await drawClowd(ctx,matrix);

          break;
        case "Snow" :
          obniz.display.print("雪 ⛄ " + nowTemp + "℃");
          light.red.on();
          //点滅表示　雪
          for(let i = 1; i<5; i++) {

            //wait処理  
            const _sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

            await _sleep(500);
            await drawSnow(ctx,matrix);
            await _sleep(1000);
            await drawNull(ctx,matrix);
            await _sleep(500);

          };
          await drawSnow(ctx,matrix);

          break;
        default:
          obniz.display.print(nowWeather + " " + nowTemp + "℃");
      }
  }

  //openweathermapへアクセス
  $.ajax({
    url: url,
    dataType: "json",
    type: 'GET',
  })
  //検索成功時にはページに結果を反映
  .done(function(data) {
    sendWeather(data,i);
    Wdata = data;
    console.log(Wdata);
  })
  //検索失敗時にはその旨をディスプレイに表示
  .fail(function(data) {
    obniz.display.clear();
    obniz.display.print("Error");
  });
  obniz.switch.onchange = function(state) {
    if(state == "right"){
      if(i == 8)
        i=0;
      else
        i+=1;
    }else if(state == "left"){
      if(i == 0)
        i=8;
      else
        i-=1;
    }
    //sendWeather関数に取得したデータを引き渡す
    sendWeather(Wdata,i);
  }

  //関数の定義
  //雨マーク
  async function drawRain(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _ctx.fillRect(2,0,4,1)
    _ctx.fillRect(1,1,1,1)
    _ctx.fillRect(6,1,1,1)
    _ctx.fillRect(0,2,1,1)
    _ctx.fillRect(7,2,1,1)
    _ctx.fillRect(0,3,8,1)
    _ctx.fillRect(4,4,1,1)
    _ctx.fillRect(4,5,1,1)
    _ctx.fillRect(2,6,1,1)
    _ctx.fillRect(4,6,1,1)
    _ctx.fillRect(3,7,1,1)

    _matrix.draw(_ctx);
  }

  //点滅用空白
  async function drawNull(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _matrix.draw(_ctx);
  }

  //晴れマーク
  async function drawShine(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _ctx.fillRect(0,0,6,1)
    _ctx.fillRect(0,1,6,1)
    _ctx.fillRect(7,1,1,1)
    _ctx.fillRect(0,2,6,1)
    _ctx.fillRect(0,3,5,1)
    _ctx.fillRect(7,3,1,1)
    _ctx.fillRect(0,4,4,1)
    _ctx.fillRect(0,5,3,1)
    _ctx.fillRect(6,5,1,1)
    _ctx.fillRect(5,6,1,1)
    _ctx.fillRect(1,7,1,1)
    _ctx.fillRect(3,7,1,1)

    _matrix.draw(_ctx);
  }

  //晴れマーク２
  async function drawShine2(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _ctx.fillRect(0,0,6,1)
    _ctx.fillRect(0,1,6,1)
    _ctx.fillRect(7,0,1,1)
    _ctx.fillRect(0,2,6,1)
    _ctx.fillRect(0,3,5,1)
    _ctx.fillRect(7,2,1,1)
    _ctx.fillRect(0,4,4,1)
    _ctx.fillRect(0,5,3,1)
    _ctx.fillRect(5,5,1,1)
    _ctx.fillRect(6,6,1,1)
    _ctx.fillRect(0,7,1,1)
    _ctx.fillRect(2,7,1,1)
    _ctx.fillRect(4,7,1,1)
    _ctx.fillRect(7,4,1,1)

    _matrix.draw(_ctx);
  }

  //曇りマーク
  async function drawClowd(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _ctx.fillRect(2,1,3,1)
    _ctx.fillRect(1,2,1,1)
    _ctx.fillRect(5,2,1,1)
    _ctx.fillRect(1,3,1,1)
    _ctx.fillRect(4,3,3,1)
    _ctx.fillRect(0,4,1,3)
    _ctx.fillRect(3,4,1,1)
    _ctx.fillRect(7,4,1,3)
    _ctx.fillRect(1,7,6,1)

    _matrix.draw(_ctx);
  }

  //雪マーク
  async function drawSnow(_ctx, _matrix) {
    await _matrix.clear();
    await _ctx.clearRect(0, 0, _matrix.width, _matrix.height);
    
    _ctx.fillRect(3,0,2,1)
    _ctx.fillRect(2,1,1,3)
    _ctx.fillRect(5,1,1,3)
    _ctx.fillRect(3,3,2,1)
    _ctx.fillRect(1,4,1,3)
    _ctx.fillRect(6,4,1,3)
    _ctx.fillRect(2,7,4,1)

    _matrix.draw(_ctx);
  }

}

</script>
</body>
</html>

```

</div></details>

#IoT機器を触ってみて
はっきり言って**そんなに簡単ではない**し、業務レベルにするには**費用も嵩みそう**な印象を受けた。
とは言え、**インターネットを介して物理的な領域に干渉出来る**というのは実感出来た。
**触ってみて出来ることを知ったからこそ出るアイデアもあるはずだ。**