Obnizの使い方。 NodejsでObnizに天気予報(日本語)を表示

プログラミング

こんにちは!

花粉症で寝込んで仕事も趣味も一時停止してしまった、しずかなかずしです。

コロナ禍であろうと何であろうと、地球は回っています。したがって、例によって今年の花粉症の季節になりましたね。花粉症の皆さんはいかがお過ごしでしょう。私の場合は、花粉症でくらくらして寝込んで、何もかも一時停止しまうことがあります。そんな一時停止状態を再起動すべく、机の中にしばらく眠っていたObnizをゴソゴソと取り出してみました。

Obnizを見つめて、「こいつ、何ができるんだっけ?」と思いながら触り始めたら、ちょっとした工作になったので紹介します。

Obnizとは?

いわゆるIoT(Internet of Things)を実現するための小さな小さなコンピューターがObniz。WiFiでネットにつながり、JavaScriptのプログラミングで好きに動かすことができます。この手の製品は最近は色々と出ていますが、ObnizのいいところはOSのセットアップやプログラミングの環境を準備すること無く、手軽にプログラミングできること。

WebブラウザでJavaScriptを打ち込むとすぐに「なにか」が動きます。プログラム自体は、Obnizで動作するのではなく、JavaScriptを実行しているブラウザで動く、というところが特徴です。Raspberry PiやArduinoなどのIoTのコンピューターを触ったことがある方ならば、この仕組みに新感覚を持つのではないでしょうか。だって、コーディングしたプログラムをObnizという小さなコンピューターにロードする必要がないんですよ!

Obnizは予想外の小型感
Obnizは予想外の小型感

以前、この新感覚をこのブログ、しずかなかずしでも記事にしていました。こちらも合せてご覧ください。

ブラウザでプログラムが動く、と申しましたが、簡単お手軽にブラウザでLチカというのではつまらない。実はこのObniz、Nodejsでも使えます。という訳で、Nodejsでプログラミングしてみましょう。

日本語で1週間の天気を表示

作ったものはこちら。

Obnizの小さなディスプレーに日本語で1週間分の天気予報を表示します。普通にテキストを書くのだと流石にObnizの小さな筐体に日本語フォントが入っていないようですので、予めJavaScriptで「描いて」表示させることになります。

天気予報には、Open WeatherというサービスのAPIをNodejsで呼んで取得しました。

Nodejs+Obniz事始め

これを読んでいる方なら、多分、Nodejsのことを知らない方はいないはず、という前提ではじめます。NodejsはJavaScriptの実行環境ですが、ObnizをブラウザのJavaScriptで使うときとの違いは、SDK(Obnizクラスライブラリ)のロードの仕方くらいでしょうか。Nodejsのパッケージマネージャnpmで簡単にObnizのSDKのダウンロードができますのでやってみましょう。

mkdir obniz
cd obniz
npm init
npm install obniz

といった感じでインストールができます。

Open Weather APIで天気予報取得

Open Weather は世界中の天気予報を提供するサービスです。REST API経由でプログラムから天気予報の情報が取り出せる便利なサービです。価格は、1分間に60回、一月1万回までならタダで使えます。

OpenWeatherの価格ページより転載

呼び出し方もシンプル。URLにパラメータをつけてHTTP GETで呼び出すと、JSON形式の天気情報が送られてきます。

アカウントの登録を行いapi_keyのページから取得したキーをURLのパラメータに設定する必要があります。パラメータは以下のような感じになります。

https://api.openweathermap.org/data/2.5/onecall?lang=ja&units=metric&lat=${lat}&lon=${lon}&exclude=minutely,hourly&appid=${apikey}

${lat}, ${lon}と表記されているところには、緯度経度を入力。例えば、世田谷区の天気がほしければ、${lat}部分に35.636、${lon}部分に139.6343を代入する感じですね。

${apikey}のところはOpen Weatherにログイン後のapi_keyページから取得します。

lat経度
lot緯度
lang天気の情報の言語。日本語ならjaです。
units気温の単位。℃ならmetricを指定します。華氏で取得する場合はimperialを指定します。デフォルトは、ケルビンです。
exclude何の情報を取らないか、を指定します。以下が指定できます。
current: 現在の天気
minutely: 分単位
hourly: 時単位
daily: 日単位
alerts: 警報?

詳しいAPI仕様はこちらから:One Call API。英語です。

という訳で、ちょっと雑に作ったopenweatherの1週間の天気を非同期にJSON形式返してくれる、nodejsのモジュールが以下(openweather.jsというファイルで作りました)。 require('./openweather’).getData().then((obJ)=>{})みたいに使います。

var request = require('request');
var config = require('./config');

module.exports = {
  getData : function () {
    return new Promise(function(resolve, reject) {
      var lat = config.openweather.locations[0].lat;
      var lon = config.openweather.locations[0].lon;
      request.get(
        {uri: `https://api.openweathermap.org/data/2.5/onecall?lang=ja&units=metric&lat=${lat}&lon=${lon}&exclude=minutely,hourly&appid=${config.openweather.apikey}`},
        function(error, response, body)  {
          if (error){
            console.log(error.message);
            reject(new Error(error));
            return;
          }
          // console.log(body);
          resolve(JSON.parse(body));
        }
      );
    });
  },
}

日本語表示のためにcanvasを使う

Obnizのディスプレイに文字を表示するには、obniz.display.print()のようにやるのですが、これだと日本語表示してくれません。JavaScriptのCanvasという仕組みを使って描画したものをObnizに貼り付けてやることで日本語表示も可能です。obniz.display.draw()を使って、Canvasに描いた絵をObnizのディスプレイに表示する、という流れです。

Canvasは、本来ブラウザのHTMLでお絵かきをする仕様ですが、nodejsでも同様のモジュールが用意されているので、そちらを使います。インストールは以下のように行います。

npm install canvas

こちらの記事を参考にしました。

ということで、天気予報をディスプレイに表示するプログラムです。雑に貼り付けてみます。

var config = require('./config');
var ObnizSdk = require('obniz');
const { createCanvas,  registerFont} = require('canvas');
var openweather = require('./openweather');

var weather_obj = null;
var weather_obj_index = 0;

var obniz = new ObnizSdk(config.devid);
obniz.onconnect = async function () {
  obniz.display.clear();

  // registerFont('/usr/share/fonts/truetype/noto/NotoMono-Regular.ttf', {family:'noto'});
  // registerFont('/usr/share/fonts/truetype/takao-mincho/TakaoMincho.ttf', {family:'takao'});

  openweather.getData().then(function(obj) {
    weather_obj = obj;
    console.log(JSON.stringify(obj, null, '\t'));
    display_weather_info(obniz, weather_obj, weather_obj_index);

    setInterval(function() {
      openweather.getData().then(function(obj) {
        console.log('got new weather! - ' + (new Date(Date.now())).toString());
        weather_obj = obj;
        weather_obj_index = 0;
        display_weather_info(obniz, weather_obj, weather_obj_index);
      });
    }, config.openweather.interval);
  })

  obniz.switch.onchange = function(state) {
    if (state !== 'none')
      obniz.display.clear();
      
    if (state === "push") {
      weather_obj_index = 0;
      display_weather_info(obniz, weather_obj, weather_obj_index);
    } else if (state === 'left') {
      weather_obj_index++;
      display_weather_info(obniz, weather_obj, weather_obj_index);
    } else if (state === 'right') {
      weather_obj_index--;
      display_weather_info(obniz, weather_obj, weather_obj_index);
    }
  }
}

obniz.onloop = async function () {
  // called continuously
}
obniz.onclose = async function () {
  // called after connection lost
}

function create_date_string(d) {
  var days = ['日', '月', '火', '水', '木', '金', '土'];
  return `${d.getMonth() + 1}月${d.getDate()}日(${days[d.getDay()]})`;
}

function display_weather_info(obniz, obj, day_index) {
  console.log(`canvas (${obniz.display.width}, ${obniz.display.height})`);

  var length = obj.daily.length + 1;
  var i = (day_index + length) % length;

  const canvas = createCanvas(obniz.display.width, obniz.display.height);
  const ctx = canvas.getContext('2d');

  ctx.fillStyle = "white";
  ctx.font = "20px takao";

  var line_height = 21;
  if (i == 0) { // now
    var l = 20;
    ctx.fillText('今: ', 1, l);
    ctx.fillText(obj.current.weather[0].description, 1, l+=line_height);
    ctx.fillText(obj.current.temp + '℃', 1, l+=line_height);
  } else {
    var l = 20;
    var d = new Date(obj.daily[i-1].dt * 1000);
    ctx.fillText(create_date_string(d), 1, l);
    ctx.fillText(obj.daily[i-1].weather[0].description, 1, l+=line_height);
    ctx.fillText(obj.daily[i-1].temp.day + '℃', 1, l+=line_height);
  }
  
  obniz.display.draw(ctx);
}

Open WeatherのAPIで取得した1週間分のデータをObnizに搭載される左右(?)スイッチでページ送りするプログラムです(上の動画でページ送りの動作がみられます)。obniz.switch.onchangeにハンドラーを登録してその中で右に送ったのか左に送ったのかを判定しています。スイッチを押し込むと「今」の天気が表示されます。

宿題

ここで読者の皆さんに宿題を出しておきます。上の2つのプログラムは、config.jsというファイルがないと動きません。config.jsを作ってみましょう!これが宿題です。

そのファイルには、Open Weatherのapi_keyや天気予報を得たい場所の緯度・経度、加えてObnizのデバイスIDなどが書かれているはずです。

では、頑張って下さい(笑)

リモコンにもなるんだ〜Obnizって!

created by Rinker
obniz
¥935 (2021/09/20 22:23:02時点 Amazon調べ-詳細)