はじめに
以前の記事「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("
"); 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; } }arduinoの出力 "); client.println(""); 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.print(" "); client.println("");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("
0 件のコメント:
コメントを投稿