読者です 読者をやめる 読者になる 読者になる

Raspberry PiとArduinoを使って、温度、湿度、土壌測定

もうぼちぼち、梅雨入りをし、湿気に悩まされる季節になってきたかと思います。

この時期になると、湿気と温度から不快指数を求めたくもなります。

今回は、Raspberry PiとArduinoを使って、温度、湿度、土壌測定してみました。

こんな感じに接続
f:id:haru-komugi:20150620224516j:plain

こんな感じにグラフ表示
f:id:haru-komugi:20150620222308p:plain

必要な機材

本体のRaspberry Piwww.amazon.co.jp

温度・湿度センサー(ちょと高い)
https://strawberry-linux.com/catalog/items?code=52001

土壌センサーから値を取得するためのArduinowww.amazon.co.jp

土壌センサー(安い)www.amazon.co.jp

湿度・温度の測定

こちらを参考にしてくださいませ。
基本的には usbrh プログラムを作れればよいかと思います。mugimugi.hatenablog.com

土壌センサーの配線

配線はシンプルで、電源と、アースと、読み込むアナログ端子を接続するだけです。
f:id:haru-komugi:20150620222816j:plain
f:id:haru-komugi:20150620222905j:plain
f:id:haru-komugi:20150620222925j:plain

Arduinoプログラム

Arduinoに書き込むプログラムはこちら(ほとんど、サンプルプログラムのままです)
この辺はRaspberry PiよりもWindowsで書き込んだほうがラクな気がします。

/*
  AnalogReadSerial
  Reads an analog input on pin 0, prints the result to the serial monitor.
  Attach the center pin of a potentiometer to pin A0, and the outside pins to +5V and ground.

 This example code is in the public domain.
 */

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // print out the value you read:
  Serial.println(sensorValue);
  delay(1000);        // delay in between reads for stability
}

Raspberry PiからArduinoのシリアルデーターを読む

こちらを参考
LINUX間通信の準備

ttyAMA0.c

File Edit Options Buffers Tools C Help
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <time.h>

#define DEV_NAME    "/dev/ttyACM0"
//#define DEV_NAME    "/dev/ttyUSB0"
#define BAUD_RATE    B9600
#define BUFF_SIZE    4096

void serial_init(int fd)
{
  struct termios tio;
  memset(&tio,0,sizeof(tio));
  tio.c_cflag = CS8 | CLOCAL | CREAD;
  tio.c_cc[VTIME] = 100;

  cfsetispeed(&tio,BAUD_RATE);
  cfsetospeed(&tio,BAUD_RATE);

  tcsetattr(fd,TCSANOW,&tio);
}


int main(int argc,char *argv[]){

  int fd;
  int i,j;
  int len;
  int cnt = 0;
  unsigned char buffer[BUFF_SIZE],in_data[BUFF_SIZE];
  fd = open(DEV_NAME,O_RDWR);
  if(fd<0){
    printf("open failed\n");
    perror(argv[1]);
    exit(1);
  }
  serial_init(fd);
  j = 0;
  //  printf("start\n",argv[0]);
  while(1){

    len=read(fd,buffer,BUFF_SIZE);
    if(len==0){
      continue;
    }
    if(len < 0){
      printf("%s: ERROR\n",argv[0]);
      perror("");
      exit(2);
    }

    for(i=0; i<len; i++){
      //  printf("%02X ",buffer[i]);
      in_data[j++] = buffer[i];
    }
    if((in_data[j - 1] == 0x0a) || (in_data[j - 1] == 0)){
      in_data[j - 1] = 0x0a;
      in_data[j] = 0;
      if( cnt == 2 ){
        //        printf("\n read-data=%s\n",&in_data[0]);
printf("%s",&in_data[0]);
        exit(2);
      }
      cnt += 1;
      write(fd,&in_data[0],strlen(&in_data[0]));
      j = 0;
    }
  }
}

なんでかArduinoから得られるデーターが、途中のバイトもそのまま表示されるので、1回目の取得データーをスルーして、2回目のデーターを取得表示するようにしています。

コンパイル

$ gcc ttyAMA0.c -o ttyAMA0

実行

$ ttyAMA0

グラフ表示プログラム

グラフ描画のソースコードはこちら

<?php

//グラフ表示
if( $_GET['m'] == "" ){

  echo <<<HTML
<html>
<head>
  <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
  <script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
  <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
  <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css">
<style type="text/css">
#temperature{
  color:#3366cc;
}
#humidity{
  color:#dc3912;
}
#discomfort{
  color:#ff9900;
}
#soil{
  color:#109618;
}
</style>
  <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
  <script type="text/javascript"
          src="https://www.google.com/jsapi?autoload={
            'modules':[{
              'name':'visualization',
              'version':'1',
              'packages':['corechart']
            }]
          }"></script>
  <script type="text/javascript">
$(function(){


  //初期化関数
  var init = function(){

    initAjax();
    setInterval(function(){

      initAjax();

    },1000 * 60 * 5  );

  };

  //小数点○桁目を四捨五入
  var myRound = function(val, precision){

    digit = Math.pow(10, precision);
    val = val * digit;
    val = Math.round(val);
    val = val / digit;

    return val;

  };

  //初期Ajaxリクエスト
  var initAjax = function(){

    //APIから測定データー取得
    $.getJSON( "./index.php?m=api" , function(json){

      //タイトル挿入
      $("#resultLatestDate").text(json[0]["mdatetime"].split(" ")[0] );
      $("#resultLatestSoil").text( myRound( json[0]["soil"] , 1 ) );
      $("#resultLatestTemperature").text( myRound( json[0]["temperature"] , 1 ) );
      $("#resultLatestHumidity").text( myRound( json[0]["humidity"] , 1 ) );
      var discomfortIndex = 0.81 * json[0]["temperature"] + 0.01 * json[0]["humidity"] * ( 0.99 * json[0]["temperature"] - 14.3 ) + 46.3;
      $("#resultLatestDiscomfortIndex").text( parseInt( discomfortIndex ,10 ) );

      //表の作成
      var len = json.length;
      var html = "";
      for( var i = 0 ; i < len ; i ++ ){

        html += "<tr><td>" + json[i].mdatetime.split(" ")[1] + "</td><td>" + myRound( json[i].discomfort , 1 )+ "</td><td>" + myRound( json[i].temperature , 1 ) + "</td><td\
>" + myRound( json[i].humidity , 1 ) + "</td><td>" + myRound( json[i].soil , 1  ) + "</td></tr>";

      }
      $( "#resultTable tbody" ).html( html );

      //グラフの描画
      drawChart(json);

    });

  };

  //グラフの描画
  var drawChart = function(json){

    //配列の作成
    var dataArr = [ ['date', 'Temperature', 'Humidity' , 'Discomfort' , 'soil' ] ];
    var i = json.length;
    while( i -- ){

      dataArr.push( [ json[i].mdatetime.split(" ")[1] , parseFloat( json[i].temperature ) , parseFloat( json[i].humidity ) , parseFloat( json[i].discomfort ) , parseFloat( \
json[i].soil ) ] );

    }

    //Googleチャートでグラフの描画
    var data = google.visualization.arrayToDataTable( dataArr);
    var options = {
      curveType: 'function',
      legend: 'none'
    };
    var chart = new google.visualization.LineChart(document.getElementById('curve_chart'));
    chart.draw(data, options);

  }

  //ライブラリ読み込み時実行
  google.setOnLoadCallback(init);

});
</script>
</head>
<body>
<div class="container">
  <h2 id="resultLatestDate"></h2>
  <div class="row">
    <div class="col-sm-3"><h3 id="discomfort">Discomfort:<span id="resultLatestDiscomfortIndex"></span></h3></div>
    <div class="col-sm-3"><h3 id="soil">Soil:<span id="resultLatestSoil"></span>%</h3></div>
    <div class="col-sm-3"><h3 id="humidity">Humidity:<span id="resultLatestHumidity"></span>%</h3></div>
    <div class="col-sm-3"><h3 id="temperature">Temperature:<span id="resultLatestTemperature"></span></h3></div>
  </div>
  <div id="map_weather"></div>
  <div class="row">
    <div id="curve_chart"></div>
    <br/>
  </div>
  <div id="result" class="row">
    <table id="resultTable" class="table table-striped">
      <thead>
      <tr class="warning">
        <th>hh:mm:ss</th>
        <th>discomfort(%)</th>
        <th>temperature(C)</th>
        <th>humidity(%)</th>
        <th>soil(%)</th>
      </thead>
      <tbody></tbody>
    </table>
  </div>
</div>
</body>
</html>
HTML;

}
//API
elseif( $_GET['m'] == "api" ){

  $link = mysql_connect('localhost', 'root', '');
  $db = mysql_select_db('raspberry', $link);

  //グラフ描画用データーの取得
  if( $_GET['type'] == "" ){

    //MySQLからデーター取得
    $sql = "
SELECT
  *
FROM
  usbrh_data
WHERE
  mdate = CURRENT_DAT
ORDER BY
  id DESC
;";
    $result = mysql_query( $sql );
    $json_arr = array();
    while( $val = mysql_fetch_assoc( $result ) ){
      array_push( $json_arr, $val );
    }
    header("Content-Type: application/javscript; charset=utf-8");
    echo json_encode( $json_arr );
    exit;

  }
  //測定データーをMySQLに挿入(cronなどを使って定期的にこちらを叩きます)
  elseif( $_GET['type'] == "insert" ){

    //湿気・温度測定
    $result_arr =explode( " " , exec("usbrh") );
    $temperature = e( $result_arr[0] );
    $humidity    = e( $result_arr[1] );
    $discomfort  = e( 0.81 * $temperature + 0.01 * $humidity * ( 0.99 * $temperature - 14.3 ) + 46.3 );

    //土壌測定
    $soil = e( 100 - ( intval( exec("ttyAMA0") ) / 1024 * 100 ) );

    //挿入
    $sql = "
INSERT INTO
  usbrh_data
(
  soil        ,
  discomfort  ,
  temperature ,
  humidity    ,
  mdate       ,
  mdatetime   ,
  print
)VALUES(
  '{$soil}'        ,
  '{$discomfort}'  ,
  '{$temperature}' ,
  '{$humidity}'    ,
  CURRENT_DATE()   ,
  NOW()            ,
  '1'
);";
    $result = mysql_query( $sql );
    exit;

  }

}

//エスケープ関数
function e( $val ){

  return mysql_real_escape_string($val);

}

データー格納用のMySQL

CREATE TABLE IF NOT EXISTS `usbrh_data` (
  `id` int(11) NOT NULL,
  `soil` float NOT NULL,
  `discomfort` float NOT NULL,
  `temperature` float NOT NULL,
  `humidity` float NOT NULL,
  `mdate` date NOT NULL,
  `mdatetime` datetime NOT NULL,
  `print` int(11) NOT NULL DEFAULT '1'
) ENGINE=MyISAM 

参考リンク

不快指数の計算方法
www.infiniteloop.co.jp