5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Raspberry Piのセンサー情報をJavaでグラフ化し、Webブラウザーで確認する

Last updated at Posted at 2018-04-08

#はじめに
Raspberry Pi 3 に接続した温度・湿度センサー、および気圧センサーの情報を継続してデータベースに記録し、それをグラフ化して他の PC やスマホの Web ブラウザー上に表示させてみました。プログラミング言語は Java を使用しています。

History Graph

温度・湿度センサーおよび気圧センサーは、DHT11、BMP180を使用しました。

DHT11
(温度・湿度センサー)
BMP180
(気圧・温度センサー)
DHT11 BMP180

#準備
ハードウェア、およびソフトウェア環境は、以下の記事で紹介しているものを使用しています。

Raspberry Piのセンサー情報をグラフ化し、Webブラウザーで確認できる環境を準備する

環境の準備が整ったら、以下の配線図のようにセンサーを取り付けます。抵抗はデータシート上では 5KΩ が指定されていましたが、手持ちになかったので 10KΩ で代用ました(特に問題はないようです)。

配線図

(参考資料)DHT11データシートBMP180データシート

#データベースの作成
以下の操作は、すべて pi ユーザーから実施しています。

まず始めに、温湿度および気圧のデータを記録していくためのデータベースを作成します。データベースは上のリンク先の記事でインストールした MySQL を使用しています。

以下の例ではデータベース名として raspberrypi を指定していますが、任意の名前を指定することができます(出力の一部は省略しています)。

$ mysql -p
Enter password:

MariaDB [(none)]> CREATE DATABASE raspberrypi CHARACTER SET utf8;
MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| raspberrypi        |
+--------------------+

データベースが作成されたら、その中にテーブルを作成します。以下の例ではテーブル atmosphere を作成し、カラムとして id、datetime(日時)、temperature(温度)、humidity(湿度)、pressure(気圧)を作成しています。こちらも分かりやすい任意の名前を指定することができます。

MariaDB [(none)]> use raspberrypi
MariaDB [raspberrypi]> CREATE TABLE atmosphere( id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY, datetime DATETIME NOT NULL, temperature FLOAT(4,1), humidity FLOAT(4,1), pressure FLOAT(5,1) );

MariaDB [raspberrypi]> show tables;
+-----------------------+
| Tables_in_raspberrypi |
+-----------------------+
| atmosphere            |
+-----------------------+

MariaDB [raspberrypi]> desc atmosphere;
+-------------+------------+------+-----+---------+----------------+
| Field       | Type       | Null | Key | Default | Extra          |
+-------------+------------+------+-----+---------+----------------+
| id          | int(10)    | NO   | PRI | NULL    | auto_increment |
| datetime    | datetime   | NO   |     | NULL    |                |
| temperature | float(4,1) | YES  |     | NULL    |                |
| humidity    | float(4,1) | YES  |     | NULL    |                |
| pressure    | float(5,1) | YES  |     | NULL    |                |
+-------------+------------+------+-----+---------+----------------+

MariaDB [raspberrypi]> exit

#データの記録
Tomcat の webapps ディレクトリ配下に、データを記録するためのプログラムや、サーブレットを配置するためのディレクトリを作成します。

mkdir -p ~/apache-tomcat-9.0.6/webapps/atmosphere/WEB-INF/classes

センサーから情報を読み取り、データベースに記録していくための以下のソースコードを、この classes ディレクトリーに作成します。

  • RecordingTask.java
  • DHT11.java
  • DHT11Result.java
  • BMP180.java

4 つのファイルのうち下の 3 つのファイルは、各センサーから情報を読み取るためのものです。これらについては、それぞれ以下の紹介記事からコピーして下さい。

DHT11.java / DHT11Result.java
Raspberry Pi 3 & DHT11からJavaで温度・湿度を読み取る

BMP180.java
Raspberry Pi 3 & BMP180からJavaで気圧・温度を読み取る

RecordingTask.java
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.Properties;

import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;

public class RecordingTask {

    private static String sqlDataBase      ;
    private static String sqlTable         ;
    private static String sqlColDateTime   ;
    private static String sqlColTemperature;
    private static String sqlColHumidity   ;
    private static String sqlColPressure   ;

    private static String sqlUser    ;
    private static String sqlPassword;

    // {0} table name
    // {1} column name(datetime)
    // {2} column name(temperature)
    // {3} column name(humidity)
    // {4} column name(pressuer)
    // {5} temperature
    // {6} humidity
    // {7} puressure
    private static final String sqlTemplate =
        "INSERT INTO {0} ( {1}, {2}, {3}, {4} ) VALUES ( NOW(), {5,number,#0.0}, {6,number,#0.0}, {7,number,#0.00} );";

    public static void main( String... args ) {

        getProperties();

        Connection conn = null;
        String url = "jdbc:mysql://localhost/" + sqlDataBase;

        try {
            Class.forName( "com.mysql.jdbc.Driver" ).newInstance();
            conn = DriverManager.getConnection( url, sqlUser, sqlPassword );

            Statement stmt = conn.createStatement();

            // DHT11 ---

            DHT11 dht11 = new DHT11( 15 ); // use GPIO pin 15

            DHT11Result result;
            while ( true ) {
                result = dht11.read();

                if ( result.isValid() ) {
                    // System.out.printf("Temperature: %.1f C\n" , result.temperature );
                    // System.out.printf("Humidity:    %.1f %%\n", result.humidity);
                    break;
                }
            }

            // BMP180 ---

            BMP180 bmp180 = new BMP180( BMP180.ULTRAHIGHRES );

            double pressure = bmp180.readPressure();
            // System.out.printf( "Pressure   : %.2f hPa\n", pressure );

            // write data to database
            String sql = MessageFormat.format( sqlTemplate,
                             sqlTable, sqlColDateTime, sqlColTemperature, sqlColHumidity, sqlColPressure,
                             result.temperature, result.humidity, pressure );
            // System.out.println( sql );

            int num = stmt.executeUpdate( sql );

            stmt.close();
        } catch ( ClassNotFoundException e ) {
            e.printStackTrace();
        } catch ( SQLException e ) {
            e.printStackTrace();
        } catch ( Exception e ) {
            e.printStackTrace();
        } finally {
            try {
                if ( conn != null ) {
                    conn.close();
                }
            } catch ( SQLException e ) {
                e.printStackTrace();
            }
        }
    }

    static void getProperties() {
        Path path = Paths.get( "atmosphere.properties" );
        try ( BufferedReader reader = Files.newBufferedReader( path, StandardCharsets.UTF_8 ) ) {
            Properties properties = new Properties();
            properties.load( reader );

            sqlDataBase       = properties.getProperty( "mysql.db" );
            sqlTable          = properties.getProperty( "mysql.db.table" );
            sqlColDateTime    = properties.getProperty( "mysql.db.table.datetime" );
            sqlColTemperature = properties.getProperty( "mysql.db.table.temperature" );
            sqlColHumidity    = properties.getProperty( "mysql.db.table.humidity" );
            sqlColPressure    = properties.getProperty( "mysql.db.table.puressure" );
            sqlUser           = properties.getProperty( "mysql.user" );
            sqlPassword       = properties.getProperty( "mysql.password" );
        } catch ( IOException e ) {
            e.printStackTrace();
        }
    }
}

データベースへ書き込む値の指定は、以下の部分で行っています。他のセンサーを使用する場合は、読み取った値を result.temperature、result.humidity、pressure の部分に double 型で指定すれば OK です。

            String sql = MessageFormat.format( sqlTemplate,
                             sqlTable, sqlColDateTime, sqlColTemperature, sqlColHumidity, sqlColPressure,
                             result.temperature, result.humidity, pressure );

また、同じ場所にプロパティファイル atmosphere.properties を作成しておきます。このファイルには、作成したデータベース、テーブル、およびカラムに指定した名前を登録します。あわせて MySQL にアクセスするためのユーザーおよびパスワードも指定しておきます。

atmosphere.properties
mysql.db=raspberrypi
mysql.db.table=atmosphere
mysql.db.table.datetime=datetime
mysql.db.table.temperature=temperature
mysql.db.table.humidity=humidity
mysql.db.table.puressure=pressure
mysql.user=pi
mysql.password=raspberry
jfreechart.chartfile=/home/pi/apache-tomcat-9.0.6/webapps/atmosphere/atmosphere.png

最後の jfreechart.chartfile= は、サーブレットが作成するグラフイメージを置く場所とファイル名を指定しています。

ここまで準備ができたら、ソースコードをコンパイルして実行します。

$ cd ~/apache-tomcat-9.0.6/webapps/atmosphere/WEB-INF/classes
$ javac RecordingTask.java
$ sudo java -classpath $CLASSPATH RecordingTask

プログラムの実行後、センサーから取得された情報がデータベースに書き込まれていることを確認します。

$ mysql -p
Enter password:

MariaDB [(none)]> use raspberrypi

MariaDB [raspberrypi]> select * from atmosphere;
+----+---------------------+-------------+----------+----------+
| id | datetime            | temperature | humidity | pressure |
+----+---------------------+-------------+----------+----------+
|  1 | 2018-04-07 19:26:15 |        24.2 |     50.0 |    994.4 |
+----+---------------------+-------------+----------+----------+

MariaDB [raspberrypi]> exit

なおテストで作成したレコードを削除したい場合や、テーブル、データベースそのものを削除したい場合は、以下のSQL文を使用します。削除は特に確認もなくサクッと削除されますので注意して下さい。

機能 SQL文
テーブル内のレコードをすべて削除 USE database_name
TRUNCATE TABLE table_name;
テーブルの削除 USE database_name
DROP TABLE table_name;
データベースの削除 DROP DATABASE database_name;

cron から定期的にこのプログラムを実行して、センサー情報をデータベースに蓄積します。ディレクトリ ~/apache-tomcat-9.0.6/webapps/atmosphere に以下のシェルスクリプト atmosphere.sh を作成し、アクセス権を設定しておきます。

atmosphere.sh
#!/bin/sh

CATALINA_HOME="/home/pi/apache-tomcat-9.0.6"
export CLASSPATH=.:/opt/pi4j/lib/*:$CATALINA_HOME/lib/*

cd $CATALINA_HOME/webapps/atmosphere/WEB-INF/classes
sudo java -classpath $CLASSPATH RecordingTask
$ cd ~/apache-tomcat-9.0.6/webapps/atmosphere
$ chmod 755 atmosphere.sh

このシェルスクリプトを crontab で設定すれば準備完了です。crontab を最初に起動した場合使用するエディタを聞かれますが、特に理由がない限り nano がお勧めです。

$ crontab -e
no crontab for pi - using an empty one

Select an editor.  To change later, run 'select-editor'.
  1. /bin/ed
  2. /bin/nano        <---- easiest
  3. /usr/bin/vim.tiny

Choose 1-3 [2]: 2

エディタで開かれたファイルの最後に以下を追加します。この設定は毎時 0 分にシェルスクリプトを起動します。今回作成したプログラムは、毎時 0 分にデータが記録される前提で作成されています。

0 * * * * /bin/bash /home/pi/apache-tomcat-9.0.6/webapps/atmosphere/atmosphere.sh

設定した内容は crontab -l で確認できます。

#グラフの作成
再び classes ディレクトリに戻って、データベースからデータを読み込み、グラフイメージを作成するサーブレット AtmosphereChart.java を作成します。

AtmosphereChart.java
import java.io.File;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;
import java.text.MessageFormat;
import java.awt.BasicStroke;
import java.awt.Font;
import java.awt.Color;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.RequestDispatcher;

import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.title.TextTitle;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.CategoryMarker;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.Layer;

public class AtmosphereChart extends HttpServlet {

    private String sqlDataBase      ;
    private String sqlTable         ;
    private String sqlColDateTime   ;
    private String sqlColTemperature;
    private String sqlColHumidity   ;
    private String sqlColPressure   ;

    private String sqlUser    ;
    private String sqlPassword;

    private String chartFile;

    private static final String PERIOD_1_DAY    = "1";
    private static final String PERIOD_2_DAYS   = "2";
    private static final String PERIOD_3_DAYS   = "3";
    private static final String PERIOD_7_DAYS   = "7";
    private static final String PERIOD_30_DAYS  = "30";
    private static final String PERIOD_90_DAYS  = "90";
    private static final String PERIOD_180_DAYS = "180";
    private static final String PERIOD_360_DAYS = "360";

    // {0} table name {1} column name(datetime) {2} period(minute)
    private static final String sqlTemplate =
        "SELECT * FROM {0} WHERE {1} > ( SELECT MAX( {1} ) FROM {0} ) - INTERVAL {2,number,#} - 1 MINUTE ";

    private static final HashMap<String, String> sqlConditions;
    static {
        sqlConditions = new HashMap<>();
        sqlConditions.put( PERIOD_1_DAY   , ";" );
        sqlConditions.put( PERIOD_2_DAYS  , ";" );
        sqlConditions.put( PERIOD_3_DAYS  , "AND MOD( HOUR( {1} ), 2 ) = 0;" );
        sqlConditions.put( PERIOD_7_DAYS  , "AND MOD( HOUR( {1} ), 3 ) = 0;" );
        sqlConditions.put( PERIOD_30_DAYS , "AND HOUR( {1} ) = 12;" );
        sqlConditions.put( PERIOD_90_DAYS , "AND MOD( DAYOFMONTH( {1} ), 2 ) = 1 AND HOUR( {1} ) = 12;" );
        sqlConditions.put( PERIOD_180_DAYS, "AND MOD( DAYOFMONTH( {1} ), 4 ) = 1 AND HOUR( {1} ) = 12;" );
        sqlConditions.put( PERIOD_360_DAYS, "AND MOD( DAYOFMONTH( {1} ), 8 ) = 1 AND HOUR( {1} ) = 12;" );
    }

    private static final String path = Thread.currentThread().getContextClassLoader().getResource( "" ).getPath();
    private static final Font labelFont     = new Font( Font.SANS_SERIF, Font.PLAIN, 12 );
    private static final Font tickLabelFont = new Font( Font.SANS_SERIF, Font.PLAIN, 10 );

    private java.util.logging.Logger logger;

    @Override
    protected void doGet( HttpServletRequest request, HttpServletResponse response )
                   throws ServletException, IOException {
        /*
            logger = java.util.logging.Logger.getLogger( "debug" );
            try {
                java.util.logging.FileHandler fh = new java.util.logging.FileHandler( "/home/pi/debug.log" );
                fh.setFormatter( new java.util.logging.SimpleFormatter() );
                logger.addHandler( fh );
            } catch ( IOException e ) {
            }
        */

        String[] datetime    = null;
        double[] temperature = null;
        double[] humidity    = null;
        double[] pressure    = null;
        ArrayList<String> markerLabels = new ArrayList<>();

        int size = 0;

        String period = request.getParameter( "period" );
        if ( period == null ) period = PERIOD_1_DAY;

        getProperties();

        // Read data from database

        Connection conn = null;
        String url = "jdbc:mysql://localhost/" + sqlDataBase;

        try {
            Class.forName( "com.mysql.jdbc.Driver" ).newInstance();
            conn = DriverManager.getConnection( url, sqlUser, sqlPassword );

            Statement stmt = conn.createStatement();

            String sql = makeSQLStatement( period );
            // logger.log( java.util.logging.Level.INFO, "SQL statement : " + sql );

            ResultSet rs = stmt.executeQuery( sql );

            rs.last();
            size = rs.getRow();
            rs.beforeFirst();

            datetime    = new String[ size ];
            temperature = new double[ size ];
            humidity    = new double[ size ];
            pressure    = new double[ size ];

            int i = 0;
            while( rs.next() ) {
                datetime   [ i ] = MessageFormat.format(
                                       "{0,date,MM-dd} {1,time,HH:mm}",
                                       rs.getDate( sqlColDateTime ), rs.getTime( sqlColDateTime ) );
                temperature[ i ] = rs.getDouble( sqlColTemperature );
                humidity   [ i ] = rs.getDouble( sqlColHumidity );
                pressure   [ i ] = rs.getDouble( sqlColPressure );

                if ( isMarkerLabel( period, datetime[ i ] ) ) {
                    markerLabels.add( datetime[ i ] );
                }

                i ++ ;
            }

            rs.close();
            stmt.close();
        } catch ( ClassNotFoundException e ) {
            logger.log( java.util.logging.Level.WARNING, "", e );
        } catch ( SQLException e ) {
            logger.log( java.util.logging.Level.WARNING, "", e );
        } catch ( Exception e ) {
            logger.log( java.util.logging.Level.WARNING, "", e );
        } finally {
            try {
                if ( conn != null ) {
                    conn.close();
                }
            } catch ( SQLException e ) {
                logger.log( java.util.logging.Level.WARNING, "", e );
            }
        }

        // Draw Chart

        LineAndShapeRenderer renderer;
        CategoryAxis cAxis;
        NumberAxis   nAxis;

        DefaultCategoryDataset dataT = new DefaultCategoryDataset();
        DefaultCategoryDataset dataH = new DefaultCategoryDataset();
        DefaultCategoryDataset dataP = new DefaultCategoryDataset();

        for( int i = 0; i < size; i ++ ) {
            dataT.addValue( ( Number )temperature[ i ], "Temperature", datetime[ i ] );
            dataH.addValue( ( Number )humidity   [ i ], "Humidity"   , datetime[ i ] );
            dataP.addValue( ( Number )pressure   [ i ], "Pressure"   , datetime[ i ] );
        }

        // Temperature
        JFreeChart chart = ChartFactory.createLineChart(
            "Sensor Data History Graph - " + period + ( period.equals( "1" ) ? " day" : " days" ),
            "Time",
            "Temperature (C)",
            dataT,
            PlotOrientation.VERTICAL,
            true,
            false,
            false
        );
        chart.setBackgroundPaint( Color.WHITE );
        TextTitle subtitle = new TextTitle(
            size != 0 ?
            MessageFormat.format( "Last data({0})  Temperature: {1} C  Humidity: {2} %  Pressure: {3} hPa",
                                  datetime   [ size - 1 ],
                                  temperature[ size - 1 ],
                                  humidity   [ size - 1 ],
                                  pressure   [ size - 1 ] ) :
            "No data to plot on the graph."
        );
        chart.addSubtitle( subtitle );

        CategoryPlot plot = chart.getCategoryPlot();

        cAxis = plot.getDomainAxis();
        cAxis.setCategoryLabelPositions( CategoryLabelPositions.UP_90 );
        cAxis.setLabelFont( labelFont );
        cAxis.setTickLabelFont( tickLabelFont );

        renderer = ( LineAndShapeRenderer )plot.getRenderer();
        nAxis = ( NumberAxis )plot.getRangeAxis();

        customizeChart( renderer, nAxis, 0.0, 50.0, Color.RED );

        // Add Humidity
        renderer = new LineAndShapeRenderer();
        nAxis = new NumberAxis( "Humidity (%)" );

        customizeChart( renderer, nAxis, 0.0, 100.0, Color.BLUE );

        plot.setRenderer( 1, renderer );    // renderer #1
        plot.setDataset( 1, dataH );        // dataset  #1
        plot.setRangeAxis( 1, nAxis );      // range axis #1
        plot.mapDatasetToRangeAxis( 1, 1 ); // map dataset #1 to range axis #1

        // Add Pressure
        renderer = new LineAndShapeRenderer();
        nAxis = new NumberAxis( "Pressure (hPa)" );

        customizeChart( renderer, nAxis, 950.0, 1050.0, Color.GREEN );

        plot.setRenderer( 2, renderer );    // renderer #2
        plot.setDataset( 2, dataP );        // dataset  #2
        plot.setRangeAxis( 2, nAxis );      // range axis #2
        plot.mapDatasetToRangeAxis( 2, 2 ); // map dataset #2 to range axis #2

        for ( String markerLabel : markerLabels ) {
            CategoryMarker marker = new CategoryMarker( markerLabel );
            marker.setPaint( Color.WHITE );
            marker.setDrawAsLine( true );
            marker.setStroke( new BasicStroke( 1.0f ) );
            plot.addDomainMarker( marker, Layer.BACKGROUND );
        }

        try {
            ChartUtilities.saveChartAsPNG( new File( chartFile ), chart, 800, 600 );
        } catch ( IOException e ) {
            logger.log( java.util.logging.Level.WARNING, "", e );
        }

        RequestDispatcher disp = request.getRequestDispatcher( "/result.html" );
        disp.forward( request, response );
    }

    void getProperties() {
        Path propPath = Paths.get( path + "atmosphere.properties" );
        try ( BufferedReader reader = Files.newBufferedReader( propPath, StandardCharsets.UTF_8 ) ) {
            Properties properties = new Properties();
            properties.load( reader );

            sqlDataBase       = properties.getProperty( "mysql.db" );
            sqlTable          = properties.getProperty( "mysql.db.table" );
            sqlColDateTime    = properties.getProperty( "mysql.db.table.datetime" );
            sqlColTemperature = properties.getProperty( "mysql.db.table.temperature" );
            sqlColHumidity    = properties.getProperty( "mysql.db.table.humidity" );
            sqlColPressure    = properties.getProperty( "mysql.db.table.puressure" );
            sqlUser           = properties.getProperty( "mysql.user" );
            sqlPassword       = properties.getProperty( "mysql.password" );
            chartFile         = properties.getProperty( "jfreechart.chartfile" );
        } catch ( IOException e ) {
            logger.log( java.util.logging.Level.WARNING, "", e );
        }
    }

    String makeSQLStatement( String period ) {
        String conditions = sqlConditions.get( period );
        if ( conditions == null ) conditions = ";";

        return MessageFormat.format( sqlTemplate + conditions,
                                     sqlTable, sqlColDateTime, Integer.parseInt( period ) * 24 * 60 );
    }

    boolean isMarkerLabel( String period, String datetime ) {

        switch ( period ) {
            case PERIOD_2_DAYS:
            case PERIOD_3_DAYS:
            case PERIOD_7_DAYS:
                return datetime.indexOf( "00:00" ) >= 0;

            case PERIOD_30_DAYS:
                return datetime.indexOf( "-01" ) >= 0 || datetime.indexOf( "-11" ) >= 0 ||
                       datetime.indexOf( "-21" ) >= 0;

            case PERIOD_90_DAYS:
            case PERIOD_180_DAYS:
            case PERIOD_360_DAYS:
                return datetime.indexOf( "-01" ) >= 0;

            default: // assume "1"
                return datetime.indexOf( "00:00" ) >= 0 || datetime.indexOf( "12:00" ) >= 0;
        }
    }

    void customizeChart( LineAndShapeRenderer renderer, NumberAxis axis,
                         double lower, double upper, Color color ) {

        renderer.setSeriesPaint( 0, color );
        renderer.setSeriesStroke( 0, new BasicStroke( 1.0f ) );
        renderer.setSeriesShapesVisible( 0, true );

        axis.setRange( lower, upper);
        axis.setLabelFont( labelFont );
        axis.setTickLabelFont( tickLabelFont );
        axis.setTickLabelPaint( color );
    }
}

ソースコードをコンパイルして class ファイルを作成します。

$ cd ~/apache-tomcat-9.0.6/webapps/atmosphere/WEB-INF/classes
$ javac AtmosphereChart.java

JFreeChart を使用したプログラミングについては、以下の記事を参考にさせていただきました。

(参考記事)

JFreeChartでグラフ作成

このサーブレットの名前や呼び出し方などを定義するために ~/apache-tomcat-9.0.6/webapps/atmosphere/WEB-INF の下に以下のファイル web.xml を作成します。

web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0">

  <!-- servlet definition -->
  <servlet>
    <servlet-name>AtmosphereChart</servlet-name>
    <servlet-class>AtmosphereChart</servlet-class>
  </servlet>

  <!-- mapping between servlet and UTL -->
  <servlet-mapping>
    <servlet-name>AtmosphereChart</servlet-name>
    <url-pattern>/AtmosphereChart</url-pattern>
  </servlet-mapping>

</web-app>

最後に 2 つの HTML ファイル index.html および result.html を ~/apache-tomcat-9.0.6/webapps/atmosphere の下に配置して下さい。

index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="refresh" content="0; URL='./AtmosphereChart'">
</head>
</html>
result.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Cache-Control" content="no-cache">
    <meta http-equiv="Expires" content="0">
    <style>
        .period {
            font-family:sans-serif;
            padding: 20px;
        }
        .period .form-title {
            font-weight: bold;
            font-size:14px;
        }
        .period label {
            font-size:12px;
        }
        .period input[type=submit] {
            appearance: none;
            outline: none;
            border: none;
            color: #fff;
            background-color: #7294f2;
            height: 24px;
            width: 52px;
            margin-left: 20px;
        }
        .period input[type=submit]:hover{
            background-color: #8af;
            cursor:pointer;
        }
    </style>
    <title>History Graph</title>
</head>
<body><center>
    <table border="0">
    <tr><td><img src="./atmosphere.png"></td></tr>
    <tr><td>
        <form class="period" method="get" action="./AtmosphereChart">
            <fieldset>
            <legend class="form-title">
            Select period to drow graph for Temperature, Humidity and Pressure.
            </legend>
            <center>
            <label><input type="radio" name="period" value="1" checked> 1 day</label>
            <label><input type="radio" name="period" value="2"> 2 days</label>
            <label><input type="radio" name="period" value="3"> 3 days</label>
            <label><input type="radio" name="period" value="7"> 7 days</label>
            <label><input type="radio" name="period" value="30"> 30 days</label>
            <label><input type="radio" name="period" value="90"> 90 days</label>
            <label><input type="radio" name="period" value="180"> 180 days</label>
            <label><input type="radio" name="period" value="360"> 360 days</label>
            <input type="submit" value="OK">
            </center>
            </fieldset>
        </form>
    </td></tr>
    </table>
</center></body>
</html>

以上ですべての設定の完了です。~/apache-tomcat-9.0.6/webapps/atmosphere/ の下には、以下のファイルが作成されています。

atmosphere
├── atmosphere.sh
├── index.html
├── result.html
└── WEB-INF
    ├── classes
    │   ├── AtmosphereChart.class
    │   ├── AtmosphereChart.java
    │   ├── atmosphere.properties
    │   ├── BMP180.class
    │   ├── BMP180.java
    │   ├── DHT11$1.class
    │   ├── DHT11.class
    │   ├── DHT11.java
    │   ├── DHT11Result.class
    │   ├── DHT11Result.java
    │   ├── DHT11$SignalTransition.class
    │   ├── RecordingTask.class
    │   └── RecordingTask.java
    └── web.xml

#グラフの表示
URLに以下を指定してグラフを表示させます。

http://[ipアドレス または localhost]:8080/atmosphere/

最新のデータから過去 1 日分のデータがグラフ化されます。表示する期間を変えたい場合は、ページの下にあるラジオボタンから期間を選んで OK ボタンをクリックします。グラフが煩雑にならないように、選択した期間によって以下のルールに従ってデータをピックアップしてグラフ化しています。

選択した期間 ピックアップするデータ
1 Day / 2 Days 毎時
3 Days 2 時間ごと(偶数時)
7 Days 3 時間ごと(0時、3時、6時・・・)
30 Days 毎日12時
90 Days 1 日置き(1日、3日、5日・・・)の12時
180 Days 4 日置き(1日、5日、9日・・・)の12時
360 Days 8 日置き(1日、9日、17日・・・)の12時

したがって例えば 90 Days を選択しても、90 日間分のデータがないと表示されるデータが少なかったり、上記の条件に合致しているデータがないと、折れ線グラフが表示されないこともあります。

既知の問題として、期間を選んで OK をクリックしてもグラフが更新されない場合があります。これはブラウザのキャッシュに残ったイメージが表示されるためのようですが(Raspberry Pi 上では正しくイメージファイルが更新されています)、この場合はもう一度期間を選んで OK をクリックし直してみて下さい。

5
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?