2015年5月9日土曜日

Arduinoを利用した温度・湿度測定webサーバ(改版)


はじめに


以前の記事「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(" "); client.println(""); client.println(""); client.println(""); client.println(" "); client.println(" "); client.println(""); client.println(""); client.println(""); client.println(""); client.println(" "); client.println(" "); client.print(""); client.print(""); client.print(""); client.print(""); client.print(""); client.println(" "); client.println("
arduinoの出力
LM35測定結果DHT11測定結果体感温度(摂氏)
温度(摂氏)温度(摂氏)温度(華氏)湿度(\%)
");client.print(temperatureEX);client.println("");client.print(t);client.println("");client.print(f);client.println("");client.print(h);client.println("");client.print(hi);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; } }

0 件のコメント: