Arduinoの気象観測データを取得

Arduinoで気象観測データサーバを構築し、Raspberry Piでデータを取得、音声出力する。

Arduinoを無線LAN接続にちらりと気象観測データサーバのことを書いたきり、あっという間に3か月以上経ってしまった。サーバへの接続リクエストをトリガにして休止モードから復帰できたらいいな、などと考えていたのだが、勤め人には勉強する時間が乏しく、何よりその能力に欠けていることに気づいた。無念である。ともあれ、お盆休みで到達したところまでを公開するものである。

気象観測データサーバ

サーバは、汎用性を持たせるため、HTTPサーバとしてHTMLデータを出力することにした。下記のスケッチはArduino付属のスケッチ例WebServerをほぼそのまま流用している。MACADDRESSの値は適宜変更のこと。

/*
 * Weather Data Server
 * Based on the Example Scketch "WebServer", which was
 * created 18 Dec 2009
 * by David A. Mellis
 * modified 9 Apr 2012
 * by Tom Igoe,
 * Copyright 2014 FireWheel <firewheel@m40.coreserver.jp>
 * GNU General Public License V3
 * see <http://www.gnu.org/licenses/>
 */
#include <SPI.h>
#include <Ethernet.h>
#include <RHT.h>
#include <MPL.h>
#include <Wire.h>

const unsigned int PORT = 80;
const char CR = 0x0D;
const char LF = 0x0A;
const char QQ = 0x22; // double quotation mark
const String CHARSET = "UTF-8";

byte MAC[] = {0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX};
uint8_t ADDRESS[] = {192, 168, 0, 101};

IPAddress ip(ADDRESS);
EthernetServer server(PORT);

void setup() {
  Ethernet.begin(MAC, ip);
  MPL.begin();
}

void loop() {
  EthernetClient client = server.available();

  if (client) {
    boolean current_line_is_blank  = true;

    while (client.connected()) {
      if (!client.available())
        continue;

      char c = client.read();

      if (c == LF && current_line_is_blank) {
        send_response_message(client);
        break;
      }

      current_line_is_blank
        = (c == LF) ? true
        : (c != CR) ? false
        :             current_line_is_blank
        ;
    }

    delay(1);
    client.stop();
  }
}

void send_response_message(EthernetClient client) {
  String content_type = "Content-Type: text/html; charset=" + CHARSET;
  String meta_charset = "<meta charset=" + quoted(CHARSET) + ">";
  String dd_temperature
    = "<dd id=" + quoted("temperature") + ">" + RHT.thermoString() + "</dd>";
  String dd_humidity
    = "<dd id=" + quoted("humidity")    + ">" + RHT.hygroString()  + "</dd>";
  String dd_atmosphere
    = "<dd id=" + quoted("atmosphere")  + ">" + MPL.atmString()    + "</dd>";

  client.println("HTTP/1.1 200 OK");
  client.println("Connection: close");
  client.println(content_type);
  client.println();
  client.println("<!DOCTYPE html>");
  client.println("<html>");
  client.println("<head>");
  client.println(meta_charset);
  client.println("<title>Weather Data</title>");
  client.println("</head>");
  client.println("<body>");
  client.println("<dl>");
  client.println("<dt>temperature</dt>");
  client.println(dd_temperature);
  client.println("<dt>humidity</dt>");
  client.println(dd_humidity);
  client.println("<dt>atmosphere</dt>");
  client.println(dd_atmosphere);
  client.println("</dl>");
  client.println("</body>");
  client.println("</html>");
}

String quoted (String to_quote) {
  return QQ + to_quote + QQ;
}

気象観測データクライアント

クライアントは使い慣れたPerlで作成した。まず、スクレイピングに必要なURIWeb::Scraper両モジュールをインストールする。Smart::Commentsはデバッグ用なので、お好みでどうぞ。

> sudo apt-get install liburi-perl
> sudo apt-get install libweb-scraper-perl
> sudo apt-get install libsmart-comments-perl

折角Raspberry Piでデータを受けるのだから、結果を音声出力させる。Raspberry Piの設定には明記していなかったが、ダウンロードしたパッケージはホームディレクトリ直下で展開している。

#!/usr/bin/env perl
# weather_data_client.pl - 気象データを取得して出力する
# Copyright 2014 FireWheel <firewheel@m40.coreserver.jp>
# GNU General Public License V3
# see <http://www.gnu.org/licenses/>
use 5.10.0;
use strict;
use warnings;
use utf8;

use URI;
use Web::Scraper;
#use Smart::Comments;

my $URI = q{http://192.168.0.101};
my $COMMAND_FORMAT   = q{~/aquestalkpi/AquesTalkPi %s | aplay >/dev/null 2>&1};
my $TIME_TEXT_FORMAT = q{%d時%d分};
my $WEATHER_TEXT_FORMAT
  = q{"気温 %.1f度、湿度 %.1fパーセント、気圧 %.1fヘクトパスカル"};

my $scraper = scraper {
    process '#temperature', temperature => 'TEXT';
    process '#humidity',    humidity    => 'TEXT';
    process '#atmosphere',  atmosphere  => 'TEXT';
    result 'temperature', 'humidity', 'atmosphere';
};

my $weather_data_ref = $scraper->scrape(URI->new($URI));
my ($hour, $minute)  = (localtime)[2, 1];
### $weather_data_ref
my $time_text    = sprintf $TIME_TEXT_FORMAT, $hour, $minute;
my $weather_text = sprintf $WEATHER_TEXT_FORMAT,
                           $weather_data_ref->{temperature},
                           $weather_data_ref->{humidity},
                           $weather_data_ref->{atmosphere};
my $time_command    = sprintf $COMMAND_FORMAT, $time_text;
my $weather_command = sprintf $COMMAND_FORMAT, $weather_text;
qx{$time_command};
qx{$weather_command};
__END__

cron(8)で15分毎に出力させる、などという使い方も面白いかもしれない。

コメントをどうぞ