はじめに
以前の記事「
Arduinoを利用した温度・湿度測定webサーバ(暫定版)」の「暫定」の度合いがひどかったので,週末などなどの空き時間を使ってソフトをバージョンアップ.
変更点
将来的に,24時間運転のサーバでデータを定期的に取得して,グラフ化したかったので,コンピュータから読み取って,解析が容易なフォーマットと,人間が読むフォーマットを分けたかった.
Arduinoに届いた,httpのリクエストの「GET なんとか HTTP/1.1」の『なんとか』の文字列を解析して分けられるようにした.下は,実際にアクセスした場合のスクリーンショット.
「http://ホスト名(もしくはIPアドレス)/」でアクセスした場合
「http://ホスト名(もしくはIPアドレス)/ゴミ文字列」でアクセスした場合
「http://ホスト名(もしくはIPアドレス)/ゴミ文字列」でアクセスした場合は,『:』区切りの1行データになるので,sedやawkでも簡単に切り出せる.
ソースコード
プログラムにHTMLを吐き出す機能があるせいで,ソースの中のHTMLのタグ類を,ソースを表示するためのGoogle Code Prettifyが正しく扱えないので一部表示がおかしくなってます.
このページのソースは参考用で,きちんと読みたい方はソースを「
ここ」に置いておきますのでダウンロードしてください.
/*
LM35DZを用いた温度測定(0℃~100℃)
シリアルモニターに摂氏で表示させるのと,httpでのアクセスで答える
参考URL
httpサーバおよび,温度センサ関連
http://openhardware.gridshield.net/home/arduino-snmp-temperature-sensor
http://d.hatena.ne.jp/a10i/20110607/1307453448
http://homepage3.nifty.com/sudamiyako/zk/AVR_ADC/AVR_ADC.html
http://makining.com/okuzawats/dht11-temperature-humidity-20150218
https://learn.adafruit.com/dht/connecting-to-a-dhtxx-sensor
http://mpu.seesaa.net/category/22909809-2.html
http://blog.livedoor.jp/hardyboy/tag/LM35DZ
http://makining.com/okuzawats/dht11-temperature-humidity-20150218
http://qiita.com/masato/items/99e5dac91d13650e90a2
pingのライブラリ
http://playground.arduino.cc/Code/ICMPPing
DHTセンサのライブラリ
https://github.com/adafruit/DHT-sensor-library
*/
/* 固定IP設定の時に,pingで設定を確認する機能を有効にする場合に定義する */
#define USE_PING
/* ネットワーク関係のインクルード */
#include
//#include
//#include
#include
//#include
//#include
//#include
#ifdef USE_PING
#include
#endif /* USE_PING */
/* DHTセンサ関係のインクルード */
#include "DHT.h"
/*
* DHT11関係の定義
*/
#define DHTPIN 2 // DHTが接続されているデジタルのピン番号
// 使っているセンサの種類の定義
#define DHTTYPE DHT11 // DHT 11
//#define DHTTYPE DHT22 // DHT 22 (AM2302)
//#define DHTTYPE DHT21 // DHT 21 (AM2301)
// Connect pin 1 (on the left) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor
// Initialize DHT sensor for normal 16mhz Arduino
DHT dht(DHTPIN, DHTTYPE);
// NOTE: For working with a faster chip, like an Arduino Due or Teensy, you
// might need to increase the threshold for cycle counts considered a 1 or 0.
// You can do this by passing a 3rd parameter for this threshold. It's a bit
// of fiddling to find the right value, but in general the faster the CPU the
// higher the value. The default for a 16mhz AVR is a value of 6. For an
// Arduino Due that runs at 84mhz a value of 30 works.
// Example to initialize DHT sensor for Arduino Due:
//DHT dht(DHTPIN, DHTTYPE, 30);
/*
* LM35(温度センサ)関連の定数の定義
*/
float v=5.0; // MEGA2560やEthernet R3は5.0V駆動
float tempC = 0; // 摂氏値( ℃ )
float offset = 0; // センサの温度ズレを修正する変数
#define LOOPNUMBER 100
#define MEDIAN 50
/*
* 配線関連の定数
*/
int TempSensorPin = 0; // センサ用のアナログI/Fは0番を利用
/*
* ネットワーク関係の定数
*/
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0F, 0x92, 0x1A }; //アドレスは手持ちのarduinoのものに変更すること
boolean useDhcp = false; // DHCPで運用する場合は true に変更
// DHCPでのアドレス取得失敗時の対策や,長時間経過後のアドレス再割当て等は対応していない
/* 以下は固定IP運用の場合の変数なので適宜変更して使用すること */
IPAddress ip(192,168,0,201);
IPAddress dnsServer(192,168,0,1);
IPAddress gatewayAddress(192,168,0,1);
IPAddress netMask(255,255,255,0);
#ifdef USE_PING
SOCKET pingSocket = 0;
char buffer [256];
#endif /* USE_PING */
/*
* デバッグ関連
*/
//#define DEBUG // デバッグ目的で,各種情報をシリアルに出力する場合は有効化
/*
* プログラム本体
*/
#ifdef USE_PING
ICMPPing ping(pingSocket, (uint16_t)random(0, 255));
#endif /* USE_PING */
EthernetServer server(80); // サーバオブジェクトの定義. 引数はポート番号
void setup() {
#ifdef DEBUG
// シリアル通信速度
Serial.begin(9600);
#endif
// DHT11センサの初期化
dht.begin();
// MACアドレスとIPアドレスの設定
// 参考URL http://arduino.cc/en/Reference/EthernetBegin
if (useDhcp) {
#ifdef DEBUG
Serial.println("configure Ethernet using DHCP");
#endif
if (Ethernet.begin(mac)==0) {
#ifdef DEBUG
Serial.println("Failed.");
#endif
} else {
#ifdef DEBUG
Serial.println("Success.");
#endif
}
} else {
#ifdef DEBUG
Serial.println("Manual ip confuguration of ethernet");
#endif
Ethernet.begin(mac, ip, dnsServer, gatewayAddress, netMask);
#ifdef DEBUG
Serial.println("done.");
#endif
#ifdef USE_PING
// ルータのアドレスにping
// 参考URL http://playground.arduino.cc/Code/ICMPPing
ICMPEchoReply echoReply = ping(gatewayAddress, 4);
#ifdef DEBUG
if (echoReply.status == SUCCESS) {
Serial.println("Ping to Gateway address : success");
} else {
Serial.println("Ping to Gateway address : fail");
}
#endif
#endif /* USE_PING */
}
#ifdef DEBUG
// IPアドレスのシリアルへの出力
Serial.println("IP address of Ethernet Interface");
Serial.println(Ethernet.localIP());
//
float temperature = getLMTemperatureEX(TempSensorPin);
float h = getDHTHumidity();
float t = getDHTCTemperature();
float f = getDHTFTemperature();
#endif
// サーバの実行
server.begin();
}
void loop() {
/*
* httpリクエスト解析用
*/
String httpRequestString="";
String httpBuffer="";
// 接続してきたクライアントを示すオブジェクトを生成
EthernetClient client = server.available();
if (client) {
#ifdef DEBUG
Serial.println("Connection established by a client");
#endif
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read(); //一文字読み取り
#ifdef DEBUG
Serial.print(c);
#endif
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
#ifdef DEBUG
Serial.println("");
Serial.println("http request end.");
Serial.print("http request = ");
Serial.println(httpRequestString);
#endif
boolean httpRequestClass = matchRequestString(httpRequestString);
httpRequestString = "";
// センサから値を取得して体感温度を計算
// LM35温度読み取り
//float temperature = getLMTemperature(TempSensorPin);
float temperatureEX = getLMTemperatureEX(TempSensorPin);
// DHT11の温度湿度
float h = getDHTHumidity();
// 摂氏の温度取得
float t = getDHTCTemperature();
// 華氏の温度取得
float f = getDHTFTemperature();
// LM35の温度とDHT11の湿度から,体感温度計算
float hi = dht.convertFtoC(dht.computeHeatIndex(dht.convertCtoF(temperatureEX), h));
// send a standard http response header
client.println("HTTP/1.1 200 OK");
//返答する文字コードの定義
//client.println("Content-Type: text/html; charset=iso-2022-jp"); // JIS
//client.println("Content-Type: text/html; charset=shift_jis"); // SJIS
//client.println("Content-Type: text/html; charset=euc-jp"); // EUC
client.println("Content-Type: text/html; charset=UTF-8"); // UTF-8
//client.println("Content-Type: text/html"); // 定義なし
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println("Refresh: 5"); // refresh the page automatically every 5 sec
client.println();
client.println("");
client.println("");
// httpRequestが人手によるものか否かでHTMLの内容を切り替え
if (httpRequestClass) {
// 人が入力した場合
if (isnan(h) || isnan(t) || isnan(f)) {
#ifdef DEBUG
Serial.println("Failed to read from DHT sensor!");
#endif
client.println("LM35の測定値 : ");
client.println("
");
client.print("摂氏 ");
client.print(temperatureEX);
client.println(" 度");
client.println("
");
client.println("
");
client.println("DHT11はエラーで測定できませんでした");
client.println("
");
} else {
client.println("室内の温度・湿度環境");
client.println("");
client.println("
室内の温度・湿度環境
");
client.println("
");
client.println("
");
client.println("");
client.println("");
client.println("arduinoの出力");
client.println("
");
client.println("LM35測定結果 | ");
client.println("DHT11測定結果 | ");
client.println("体感温度(摂氏) | ");
client.println("
");
client.println("
");
client.println("温度(摂氏) | ");
client.println("温度(摂氏) | ");
client.println("温度(華氏) | ");
client.println("湿度(\%) | ");
client.println("
");
client.println("
");
client.print("");client.print(temperatureEX);client.println(" | ");
client.print("");client.print(t);client.println(" | ");
client.print("");client.print(f);client.println(" | ");
client.print("");client.print(h);client.println(" | ");
client.print("");client.print(hi);client.println(" | ");
client.println("
");
client.println("
");
client.println("");
}
} else {
// 人でない場合
// LM35の測定結果を出力
client.print("LM35:C:");
client.print(temperatureEX);
client.print(":");
// DHTの測定結果を出力
client.print("DHT11:");
// DHTの出力がエラーか否かで切り替え
if (isnan(h) || isnan(t) || isnan(f)) {
#ifdef DEBUG
Serial.println("Failed to read from DHT sensor!");
#endif
client.println("C:0:F:0:H:0:HI:C:0");
} else {
client.print("C:");
client.print(t);
client.print(":F:");
client.print(f);
client.print(":H:");
client.print(h);
client.print(":HI:C:");
client.println(hi);
}
client.println("
");
}
client.println("");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
if (httpBuffer.startsWith("GET ")) {
// GET xxx HTTP/1.1 の行だけを処理対象にする.
// 「xxx」の部分だけを取り出す.
httpRequestString = httpBuffer;
httpRequestString.replace("GET ","");
httpRequestString.replace("HTTP/1.1","");
httpRequestString.replace(" ","");
httpRequestString.replace("\t","");
}
httpBuffer = "";
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
httpBuffer = httpBuffer + c;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
#ifdef DEBUG
Serial.println("client disconnected");
#endif
}
}
//
// DHT11の温度と湿度読み取り関数
float getDHTCTemperature(){
float t = dht.readTemperature(false);
#ifdef DEBUG
// 温度のシリアルへの出力
Serial.println("DHT11 celcious temperature is");
Serial.println(t);
#endif
return t;
}
float getDHTFTemperature(){
float t = dht.readTemperature(true);
#ifdef DEBUG
// 温度のシリアルへの出力
Serial.println("DHT11 Faren temperature is");
Serial.println(t);
#endif
return t;
}
float getDHTHumidity(){
float h = dht.readHumidity();
#ifdef DEBUG
// 湿度のシリアルへの出力
Serial.println("DHT11 Humidity is");
Serial.println(h);
#endif
return h;
}
//
// LM35の温度読み取り関数
//
float getLMTemperatureEX(int pin){
int buff[LOOPNUMBER];
float tmp;
#ifdef DEBUG
Serial.print("Data = ");
#endif
for (int i=0 ; i < LOOPNUMBER ; i++){
buff[i] = analogRead( pin );
#ifdef DEBUG
Serial.print(buff[i]);
Serial.print(" , ");
#endif
}
#ifdef DEBUG
Serial.println("");
#endif
sort(buff,LOOPNUMBER);
#ifdef DEBUG
Serial.print("SortData = ");
for (int i=0 ; i < LOOPNUMBER ; i++){
Serial.print(buff[i]);
Serial.print(" , ");
}
#endif
tmp = ((v * buff[MEDIAN]) / 1023) * 100;
tmp = tmp + offset;
#ifdef DEBUG
// 温度のシリアルへの出力
Serial.println("temperature is");
Serial.println(tmp);
Serial.println("Pin number = ");
Serial.println(pin);
#endif
return tmp;
}
void sort(int a[], int size) {
for(int i=0; i<(size-1); i++) {
for(int o=0; o<(size-(i+1)); o++) {
if(a[o] > a[o+1]) {
int t = a[o];
a[o] = a[o+1];
a[o+1] = t;
}
}
}
}
/*
* httpリクエストの解析関数
*
* 意図:
* 人がブラウザでアクセスした時だけtrueにしたい
* http://ホスト名(もしくはIPアドレス/ でアクセスした時は人がブラウザを使ったと見なす.
* http://ホスト名(もしくはIPアドレス/の後ろにゴミがついていた場合は,マシンが自動で読み出しに来たと見なす.
*
* 仕様 :
* httpクリエスとの文字列がある文字列("/"か"/favicon.ico")に一致したらtrue,それ以外はfalse
*/
#define HTTP_REQUEST_STRING "/"
boolean matchRequestString(String httpRequestString) {
String request_url = HTTP_REQUEST_STRING;
request_url = request_url + "favicon.ico";
int comp_result1 = httpRequestString.compareTo(HTTP_REQUEST_STRING);
int comp_result2 = httpRequestString.compareTo(request_url);
#ifdef DEBUG
Serial.print("defined string = ");
Serial.println(HTTP_REQUEST_STRING);
Serial.print("other string = ");
Serial.println(request_url);
Serial.print("argument of match request string = ");
Serial.println(httpRequestString);
Serial.print("result of compare 1 = ");
Serial.println(comp_result1);
Serial.print("result of compare 2 = ");
Serial.println(comp_result2);
#endif
if ((comp_result1 == 0) || (comp_result2 == 0)) {
// どちらかの結果が1なら,定義したURL文字列と一致した
return true;
} else {
// それ以外の場合は,一致していない
return false;
}
}