りなっくすとらずぱい!

Raspberry Pi初心者に向けた各コマンドの説明、プログラムの作り方について紹介しています!

Rasperry PiにNode.jsをインストールする (2 - 簡単なプログラミング)

f:id:ibuquicallig:20190520034529p:plain

この記事ではNode.jsでの超簡単なプログラムの例と解説をしています。

この記事でできること

  • npmコマンドでモジュールをインストールすることができる。
  • require()でモジュールを読み込むことができる。
  • モジュールを組み合わせて定期実行処理天気予報を生成する簡単なスクリプトを作ることができる。

前の記事

プロジェクトの作成

まずはNode.jsのプロジェクトを作成していきます。前回使用したプロジェクトを使ってもいいのですが新しく作りましょう。

npm init

$ mkdir node-trainning
$ cd node-trainning
$ npm init
# ...

$ ls
package.json

作成が完了したら今回使用するモジュールをインストールしていきます。

各モジュールのインストール

今回使うモジュールは以下の4つです。

  • node-cron
  • random-words
  • figlet
  • weather-js

node-cronでは、処理を1秒ごと、30分ごとのように定期実行ことができるようになるモジュールです。名前にもある通りLinuxのcrontabコマンドで設定できるアレと同じような感じです。文法も同じとなっているのでcrontabを知っている人はそのまま使えますね!

random-wordsでは、そのまんまランダムな英語の単語を取得できます。figletでは、指定した文字列をアスキーアート風に出力できます。

weather-jsでは、指定した都市の現在・未来の天気と気温などの情報をJSON形式で取得できます。出力言語を簡単に設定できるので使いますが、呼び出しているMicrosoftのAPIはすでに公開が終了している様子です。

が、なぜか現在でも使用できるみたいです(笑)。

これらのモジュールを以下のコマンドで一気にインストールします。

$ npm install --save node-cron figlet random-words weather-js

定期実行スクリプトを作成する

それではプログラムを作っていきます。まずは指定した間隔で、ランダムな英単語をアスキーアート風に出力するあまり実用性のない(!?)プログラムを作ってきます。

ファイル名はcron_echo.jsとでもしておきます。プロジェクト内でvim cron_echo.jsでエディタを開いておいてください。

モジュールをロード

インストールしたモジュールを使用するにはまずは、require()を使用してモジュールを取得、変数に格納します。node-cronrandom-wordsfigletを使用したいので、スクリプトの先頭にはこのように書きます。

const cron        = require('node-cron');
const randomWords = require('random-words');
const figlet      = require('figlet');

これでnode-cronモジュールの関数を使いたければ、cron.functionName()で実行できるようになります。モジュールの基本的な使い方については、npmの各ページにおおよそ書いてあります。

node-cron部分

まずはnode-cronで定期実行を予定する部分を作成していきます。先ほど定義したcron変数を使用して、cron.schedule('', function(){})を実行します。一個目の引数には予定を記述し、二個目の引数には実行したい関数を指定します。予定の文法は以下の通りで、crontabと同じです。

 # ┌────────────── 秒
 # │ ┌──────────── 分
 # │ │ ┌────────── 時
 # │ │ │ ┌──────── 日
 # │ │ │ │ ┌────── 月
 # │ │ │ │ │ ┌──── 曜日
 # │ │ │ │ │ │
 # │ │ │ │ │ │
 # * * * * * *     (*はすべての、という意味になる)

すべてアスタリスクを指定すると毎秒実行されます。いかに少し例を挙げておきます。

cron.schedule('0 0 12 * * *'), () => {
  console.log('毎日12時00分00秒に実行されます。');
});

cron.schedule('* 1-10 * * * *'), () => {
  console.log('毎時01分 - 10分の間、1分刻みで実行されます。');
});

cron.schedule('*/2 * * * * *'), () => {
  console.log('2秒ごとに実行されます。');
});

結構いろんな指定の仕方ができるのでマニュアルやcrontabコマンドのmanなどを一度目を通しておくといいです。

今回は2秒間隔で処理を実行したいので以下のようにします。もちろん時間の間隔はお好きなようでかまいません!

const cron        = require('node-cron');
const randomWords = require('random-words');
const figlet      = require('figlet');

cron.schedule('*/2 * * * * *'), () => {
});

まだnode cron_echo.jsを実行しても何も起きないので、文字列を表示する処理を次に書いてきます。

文字出力部分

randomWordsで生成した文字列をそのままfigletに渡してアスキーアート化、最終的にconsole.log()で出力します。

const cron        = require('node-cron');
const randomWords = require('random-words');
const figlet      = require('figlet');

cron.schedule('*/2 * * * * *'), () => {
  const word = randomWords();  // ランダムな単語を生成
  figlet(word, (err, result) => {
    console.log(result);
  });
});

こんな感じですが、2点改善点があります。

1つめは、1度しか使わない変数(word)が存在していることです。そのまま変数を使用している場所に、代入している処理を書いた方がコードがスッキリします。他にもいろいろ利点はありますが、一度しか使用しない変数は宣言しないようにしましょう。

2つめは、アロー関数内でエラーハンドリングがなされていないことです。第一引数にエラーオブジェクトがあるのでそれを利用します。今回は超簡単な処理ですが、エラー発生時にログが何も表示されない状態は絶対に避けるようにしましょう。

これらを改善すると、以下のようなコードとなります。

const cron        = require('node-cron');
const randomWords = require('random-words');
const figlet      = require('figlet');

cron.schedule('*/2 * * * * *'), () => {
  figlet(randomWords(), (err, result) => {
    if (err) return console.log(err);  // エラー発生時

    console.log(result);
  });
});

実行結果

エディタを終了して、node cron_echo.jsと実行するとスクリプトが開始します。

$ node cron_echo.js

   __ _  __ _ ___
  / _` |/ _` / __|
 | (_| | (_| \__ \
  \__, |\__,_|___/
  |___/
                _ _   _
 __      ___ __(_) |_(_)_ __   __ _
 \ \ /\ / / '__| | __| | '_ \ / _` |
  \ V  V /| |  | | |_| | | | | (_| |
   \_/\_/ |_|  |_|\__|_|_| |_|\__, |
                              |___/
   __
  / _|_ __ ___  _ __ ___
 | |_| '__/ _ \| '_ ` _ \
 |  _| | | (_) | | | | | |
 |_| |_|  \___/|_| |_| |_|

^C

Ctrl + Cで処理を終了します。これで割としょうもないスクリプトが完成しました😎😎😎

実行間隔を変えてみたり、figletのオプションを使用してスタイルを変更したりもうちょっといじることができますのでチャンレンジしてみてください!

天気予報スクリプトを作成する

モジュールを変数化して、その中の関数を使用するという流れが大体つかめたかと思います。次はもう少し実用的な処理を作成してみようと思います。

weather-jsを使用して、指定した都市の現在の天気と明日の天気を表示します。以下のような感じの結果が返ってきます。

$ node weather_forecast.js 大阪
大阪府
現在の天気は 晴れ所により曇り で、気温は 18℃ です。
明日の天気は 曇り所により晴れ で、気温は 19℃/25℃(低/高) です。

$ node weather_forecast.js
都市名を指定してください。

$ node weather_forecast.js abcdefg
指定した都市が見つかりません。

第二引数で指定している内容を検索対象としています。指定しない場合・一致する都市がない場合はメッセージのみ表示します。

モジュールをロード

一つ目と同じようにweather-jsを読み込みます。今回のファイル名はweather-forecast.jsとします。

const weather = require('weather-js');

次に、コマンドライン上で実行するときに指定した都市(大阪等)を取得します。Node.jsで保持している変数process.argv[i]からアクセスできます。[i]に2以降を指定すると引数を取得できます。

node weather_forecast.js 大阪 京都 奈良 ...
[0]  [1]                 [2]  [3]  [4]  ...

この辺の引数やオプションの取得・設定なども便利なモジュールがあるようなので発展としてあとから参照してみてください。

さて、コードは以下のようにします。追加で引数が指定されなかった場合のエラー表示処理も加えておきます。

const weather = require('weather-js');

const loc = process.argv[2];

if (loc == null) return console.log('都市名を指定してください。'); 

return console.log()をすることで、ログ出力しながら処理を以降の処理を行わないようにすることができます。今回は特に戻り値を使用しないのでこの実装でもOKです。

これでコマンドラインからの引数の取得と値チェックは完了です。

weather-js部分

次に肝心かなめの天気の情報取得部分を作成していきます。以下のリンク先が基本的なマニュアルとなっています。

weather.find()の第1引数に検索文字列などのオプションを設定して、第2引数に取得した情報を使用するコールバック関数を設定します。

weather.find({search: loc, degreeType: 'C', lang: 'ja-JP'}, (err, result) => {
  console.log(JSON.stringify(result, null, 2));
});

degreeTypeCを設定すると摂氏(℃)で気温を取得できます。またマニュアルにはないのですがlangja-JPを指定すると日本語で値を取得できます。

とりあえずコンソール上に取得できた情報を表示させてみます。

$ node weather_forecast.js 大阪
[
  {
    "location": {...},
    "current": {...},
    "forecast": [...]
  }
]

帰ってきた配列には、JSON形式でlocation(指定した都市の情報)、current(現在の気象状態)、forecast(現在を含む1週間の予報)が格納されています。指定した都市名が重複している場合には複数件データが取得できますが、とりあえず最初にヒットするものだけを表示します。

今回使用しようと思う値は、都市名現在の天気現在の気温明日の天気明日の最低気温明日の最高気温の6個です。それぞれ以下のようにたどっていくと取得できます。

パス
都市名 current.observationpoint
現在の天気 current.skytext
現在の気温 current.temperature
明日の天気 forecast[1].skytextday
明日の最低気温 forecast[1].low
明日の最高気温 forecast[1].high

resultの配列の最初の値を使用したいので、それぞれ頭にresult[0].をつけると正しく取得できるようになります。後ほどこれらの値を使用しますが、まずはエラーチェックを実装します。

エラーチェック

以下のようにします。エラーではないですがマッチする都市が存在しない場合も考慮しましょう。

weather.find({search: loc, degreeType: 'C', lang: 'ja-JP'}, (err, result) => {
  if (err) return console.log(err);
  if (result.length == 0) return console.log('指定した都市が見つかりません。');
});

マッチしない場合には空の配列が返ってくるのでarray.length0の場合は、と考えることができます。

この部分はisError(err, result)という感じでエラーチェック部分として関数化してしまってもいいかもしれないですね!

成形部分

最後に、天気情報を表示させます。先ほどのJSONのパスの一覧を見て作ってみます。

weather.find({search: loc, degreeType: 'C', lang: 'ja-JP'}, (err, result) => {
  if (err) return console.log(err);
  if (result.length == 0) return console.log('指定した都市が見つかりません。');

  const current  = result[0].current;
  const tomorrow = result[0].forecast[1];

  console.log(current.observationpoint);
  console.log('現在の天気は %s で、気温は %s℃ です。', current.skytext, current.temperature);
  console.log('明日の天気は %s で、気温は %s℃/%s℃(低/高) です。', tomorrow.skytextday, tomorrow.low, tomorrow.high);
});

少し横に長くなっていますがこんな感じです。result[0]からアクセスするとさらに長くなってしまうので、いったん別の変数に格納しています。

console.log()にはそのまま文字列などを表示するほかに、文字列置換を使用しても表示することができます。

埋め込みたい部分に%s(String)や%d(整数値)を指定して、続く引数に前から順番に値を指定することで使用できます。

最終的なコード

最終的には以下のようになります。

const weather = require('weather-js');

const loc = process.argv[2];

if (loc == null) return console.log('都市名を指定してください。');

weather.find({search: loc, degreeType: 'C', lang: 'ja-JP'}, (err, result) => {
  if (err) return console.log(err);
  if (result.length == 0) return console.log('指定した都市が見つかりません。');

  const current  = result[0].current;
  const tomorrow = result[0].forecast[1];

  console.log(current.observationpoint);
  console.log('現在の天気は %s で、気温は %s℃ です。', current.skytext, current.temperature);
  console.log('明日の天気は %s で、気温は %s℃/%s℃(低/高) です。', tomorrow.skytextday, tomorrow.low, tomorrow.high);
});

実行結果

$ node weather_forecast.js 大阪
大阪府
現在の天気は 晴れ所により曇り で、気温は 18℃ です。
明日の天気は 曇り所により晴れ で、気温は 19℃/25℃(低/高) です。

$ node weather_forecast.js
都市名を指定してください。

$ node weather_forecast.js abcdefg
指定した都市が見つかりません。

発展

取得できる情報はまだまだいっぱいあるので、それらを追加で表示させてみてください。例えば現在の湿度はresult[0].current.humidityで取得できます。

天気予報も一週間分取得できるのでループ処理で全部コンソール上に表示する処理なんかもできると思います。

ちょっと複雑になるかもしれませんが、引数で都市名を指定しない場合、プロンプトで入力させるという実装もできそうです。コマンドラインからの入力を取得するには以下のモジュールで実現できそうです。

最後に

以上が、かなり単純な内容でしたがNode.jsでのモジュールの使い方や処理の書き方となります。npmを通じて簡単に機能を実装できるので素早く開発できました。

私も以前はPHPやPythonでサーバー処理を実装していたのですが、それらの処理を全部Node.jsで置き換えることができた(言語が統一できた)のでかなり学習コストが下がったと思います。

Raspberry PiのGPIO周りも、いろいろな方法で値の読み書きをすることができますが、その辺のモジュールも公開されている方がいます。いずれこの部分も掘り下げていきたいと思いま😎😎

次の記事

Raspberry PiといえばGPIOピンで電子工作という感もありますが、DHT11という気温と湿度を計測できるモジュールがあるので、それをNode.js上で動かしてみたりした記事が以下となっています。

ほかのNode.jsで動くアプリの作成(webサーバー)などは記事が完成しましたら随時ここにリンクを追加していきます。

参考

以下のサイトの情報を引用・参考にしました。