#はじめに
Raspberry Pi 3 に接続した温度・湿度センサー、および気圧センサーの情報を継続してデータベースに記録し、それをグラフ化して他の PC やスマホの Web ブラウザー上に表示させてみました。プログラミング言語は Java を使用しています。
温度・湿度センサーおよび気圧センサーは、DHT11、BMP180を使用しました。
DHT11 (温度・湿度センサー) |
BMP180 (気圧・温度センサー) |
---|---|
#準備
ハードウェア、およびソフトウェア環境は、以下の記事で紹介しているものを使用しています。
環境の準備が整ったら、以下の配線図のようにセンサーを取り付けます。抵抗はデータシート上では 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で気圧・温度を読み取る
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 にアクセスするためのユーザーおよびパスワードも指定しておきます。
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 を作成し、アクセス権を設定しておきます。
#!/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 を作成します。
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 を使用したプログラミングについては、以下の記事を参考にさせていただきました。
(参考記事)
このサーブレットの名前や呼び出し方などを定義するために ~/apache-tomcat-9.0.6/webapps/atmosphere/WEB-INF の下に以下のファイル 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 の下に配置して下さい。
<!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>
<!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 をクリックし直してみて下さい。