見出し画像

各種センサの情報をwebixを使ってWeb表示

RaspberryPiへのWebixライブラリ適用例の紹介記事ですが、別の記事作成でしばらく紹介できませんでした。再開します。今回は、RaspberryPiに市販ボードを実装し、各種センサの情報を定期的に収集し、RaspberryPi上のデータベースに格納(ロギング)し、Web画面から期間を指定して情報を一覧表示したり、グラフ表示する機能です。
web画面は、webixというライブラリを使用してJavascriptで実装しています。サーバ側のデータ処理部分は、PHP言語で実装し、rest_api方式を採用しています。尚、各種センサのデータ収集は、I2Cインタフェースを使用するために市販ボードが提供しているライブラリを使用し、python言語で実装しました。LED点灯処理やスイッチ情報の読取りは、GPIOインタフェースであることや、ruby言語のコーディングになれていたことから、こちらは、ruby言語で記述しました。さらに複雑な構成になっているのですが、周期処理(cronによる起動)は、一度、PHPをコール(cronからcurlコマンドで起動)し、PHPからrubyスクリプトをコール(起動)しています。
rubyスクリプト内には、pythonスクリプトを起動し、IC2センサ情報を収集したら、ファイルにJSONフォーマットで上書き保存する方式をとりました。
PHP言語内で連想配列に変換しやすく(データベースに格納処理などを簡単に記載する)ために、JSONフォーマットで保存する実装としています。
尚、RaspberryPiのApache2の実行ユーザは、apacheユーザでないことや、python実行時のユーザとの関係から、追加したユーザsunsunに変更しました。(sunsunユーザは、sudoコマンドが実行できる設定も必要です)
結果としてPHPを起動すると、sunsunユーザで実行され、sudoコマンドを利用してrubyを実行し、その中でもsudoコマンドでpythonを実行しています。
pythonスクリプトでは、/home/sunsun/work/BME280_result.txtにセンサデータをJSON形式で保存させます。
以下、例です。日付と温度、湿度、気圧、照度を記述しています。

{"date":"2024/04/12 02:20:03","temp":"25.900","humid":"36.900","atmos":"1020.900","illumin":"113.000"}

bme280_read.pyは、以下のとおりです。cgsensorを事前にインストールしています。

#!/usr/bin/env python3
"""
BME280センサーコード v1.02
測定を行い、結果を読み出して表示する.
Indoor Corgi, https://www.indoorcorgielec.com
GitHub: https://github.com/IndoorCorgi/cgsensor

必要環境:
1) Raspberry Pi OS, Python3
2) I2Cインターフェース
  Raspberry PiでI2Cを有効にして下さい
  https://www.indoorcorgielec.com/resources/raspberry-pi/raspberry-pi-i2c/

3) 拡張基板
  RPZ-IR-Sensor: https://www.indoorcorgielec.com/products/rpz-ir-sensor/

4) cgsensorパッケージ
  sudo python3 -m pip install -U cgsensor

TSL2572センサーコード

測定を行い、結果を読み出して表示する.
Indoor Corgi, https://www.indoorcorgielec.com
GitHub: https://github.com/IndoorCorgi/cgsensor

必要環境:
1) Raspberry Pi OS, Python3
2) I2Cインターフェース
  Raspberry PiでI2Cを有効にして下さい
  https://www.indoorcorgielec.com/resources/raspberry-pi/raspberry-pi-i2c/

3) 拡張基板
  RPZ-IR-Sensor: https://www.indoorcorgielec.com/products/rpz-ir-sensor/

4) cgsensorパッケージ
  sudo python3 -m pip install -U cgsensor
"""

import cgsensor  # インポート
import datetime

bme280 = cgsensor.BME280(i2c_addr=0x76)  # BME280制御クラスのインスタンス, i2c_addrは0x76/0x77から選択
bme280.forced()  # Forcedモードで測定を行い, 結果をtemperature, pressure, humidityに入れる

tsl2572 = cgsensor.TSL2572()   # TSL2572制御クラスのインスタンス
tsl2572.single_auto_measure()  # 条件を自動で調整しながら1回測定を行い, luxに結果を入れる

# 書き込むファイルのパスを宣言する
file_name = "/home/sunsun/work/BME280_result.txt"
try:
    dt_now = datetime.datetime.now()
    file = open(file_name, 'w')
    file.write("{\"date\":\""+dt_now.strftime('%Y/%m/%d %H:%M:%S"')+",") #測定日時
    file.write("\"temp\":\"%.3f\"," % bme280.temperature)            #温度度
    file.write("\"humid\":\"%.3f\"," % bme280.humidity)               #湿度%
    file.write("\"atmos\":\"%.3f\"," % bme280.pressure)              #気圧hPa
    file.write("\"illumin\":\"%.3f\"}" % tsl2572.illuminance)        #照度lux
except Exception as e:
    print(e)
finally:
    file.close()

このpythonスクリプトをrubyからコール(実行)しています。
RPZ-IR-Sensor_read.rb
rsp = `sudo python3 /home/sunsun/work/bme280_read.py`で実行できます。
GPIO読取りライブラリとしてpi_piperを事前にインストールしています。

# RPZ-IR-Sensor_read.rb
# crontab(root)で2min単位に実行
#
#文字コードUTF-8
require 'logger'
require 'date'
require 'pi_piper'
include PiPiper

logfilename = '/home/sunsun/work/RPZ-IR-Sensor_read.log'
logger = Logger.new(logfilename,5,100000)
logger.level  = Logger::INFO

logger.info('RPZ-IR-Sensor_read start ver 1.01')

ARGV.each_with_index do |arg, i|
  #puts "ARGV[#{i}]:#{arg}"
end


#LED緑
pin17_green = PiPiper::Pin.new(:pin => 17, :direction => :out)
pin17_green.on


#LED黄色18
#LED青色22
#LED白色27

#SW1(赤) GPIO5
#SW2(黒) GPIO6
pin5_SW1 = Pin.new pin:5, direction: :in, pull: :up
pin6_SW2 = Pin.new pin:6, direction: :in, pull: :up


pin5_SW1.read
pin5_SW1_level = pin5_SW1.value
pin6_SW2.read
pin6_SW2_level = pin6_SW2.value

#puts "SW1:P5="+pin5_SW1_level.to_s
#puts "SW2:P6="+pin6_SW2_level.to_s

now_time = DateTime.now.strftime("%Y/%m/%d %H:%M:%S")
now_time_s = DateTime.now.strftime("%H%M")
now_time_M = DateTime.now.strftime("%M")
#puts "now time="+now_time


rsp = `sudo python3 /home/sunsun/work/bme280_read.py`
python_exec_result = File.read('/home/sunsun/work/BME280_result.txt')
#puts python_exec_result
logger.info('python exec='+python_exec_result)

pin17_green.off

このRPZ-IR-Sensor_read.rbは、PHPから実行します。
BT0100_I2C_data_logging.phpをcurlコマンドで実行する処理をcronに定義しています。(以下の例は10分毎に実行する例です)

*/10 * * * * curl -s "http://localhost/webix01/rest_api/BT0100/BT0100_I2C_data_logging.php?userid=admin"

BT0100_I2C_data_logging.phpでは、rubyを実行(間接的に、python実行)し、結果ファイルBME280_result.txtを読取り、データベースに格納します。
10分周期で実行するので、データベースには、10分周期の測定データが格納されます。あらかじめ、i2cloggerというテーブルを作成しておく必要があります。

CREATE TABLE `ic2logger` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id:自動連番',
  `date`  datetime DEFAULT NULL COMMENT 'date',
  `temp`  varchar(32) NOT NULL DEFAULT '' COMMENT '温度',
  `humid` varchar(32) NOT NULL DEFAULT '' COMMENT '湿度',
  `atmos` varchar(32) NOT NULL DEFAULT '' COMMENT '気圧',
  `illumin` varchar(32) NOT NULL DEFAULT '' COMMENT '照度',
  `created_userid` varchar(16) NOT NULL DEFAULT 'admin' COMMENT '作成ユーザ',
  `updated_userid` varchar(16) NOT NULL DEFAULT 'admin' COMMENT '更新ユーザ',
  `created_on` datetime DEFAULT NULL COMMENT '作成日時',
  `updated_on` datetime DEFAULT NULL COMMENT '更新日時',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci  COMMENT='IC2ロガー';

以下は、phpのソースです。BT0100_I2C_data_logging.php

 <?php
//======================================================================
//File Name       : BT0100_I2C_data_logging.php
//Encoding        : UTF-8
//Creation Date   : 2024-04-11
// 
//Copyright © 2024 sunsunfarm. All rights reserved.
// 
//This source code or any portion thereof must not be  
//reproduced or used in any manner whatsoever.
//====================================================================== 
// I2C各種データを読み出して、DBにロギングする(実行は、cronで)
//crontab */10 * * * * curl -s "http://localhost/webix01/rest_api/BT0100/BT0100_I2C_data_logging.php?userid=admin"


    $FUNC_INFO = "BT0100";
    $VER_INFO ="V01L01";
    $myfilename = basename(__FILE__);   //自分自身のファイル名取得

    $userid = 'admin';
    if($_SERVER["REQUEST_METHOD"] != "GET"){
      header("HTTP/1.0 404 Not Found");
      return;
    }

    if(isset($_GET['userid'])){
        $userid = $_GET['userid'];
    }
    $logheader = 'userid='.$userid.', '.$myfilename.':';//ログ出力時のヘッダー情報(自ファイル名,ログインIDを付与)
   //共通関数の組み込み
    include('../../commonlib/svr_common_lib_v2.php');
    $config_obj = get_config_obj();

    $base_folder_info_name = "/home/sunsun/work/";
    $sever_on_cmd = "sudo /usr/bin/ruby ".$base_folder_info_name."RPZ-IR-Sensor_read.rb ";
    $result_filename = "BME280_result.txt";
    //
    //メインルーチン
    //rubyを実行して、結果ファイルを読み出す
    //実行コマンド取得
    // RasbeyPiでは、apache2をsunsun userで起動しているので、スクリプトなどは、/home/sunsun/workに配置する
    //
    error_log($logheader."sever_on_cmd  =".$sever_on_cmd );
    $cmd_resp = exec($sever_on_cmd);	
    
    $results = file($base_folder_info_name.$result_filename);
    //error_log($logheader."result=".$results[0] );
    $data_list = json_decode($results[0],true);	//JSON形式を配列に変換
    error_log($logheader."data_list=".var_export($data_list,true ) );
    //date,temp,humid,atmos,illumin
    
    //データベース接続する(MariaDB)
    $dbh = mariadb_connect($config_obj,'app01','webix-server_redmine001');

    //検索するSQL文準備
	$insert_sql1 = "INSERT INTO i2clogger(date,temp,humid,atmos,illumin,created_userid,updated_userid,created_on,updated_on)  VALUES(?,?,?,?,?,?,?,?,?)";
	$insert_stmt =  $dbh->prepare($insert_sql1);
	
	$insert_stmt->bindValue(1,$data_list["date"]);
	$insert_stmt->bindValue(2,$data_list["temp"]);
	$insert_stmt->bindValue(3,$data_list["humid"]);
	$insert_stmt->bindValue(4,$data_list["atmos"]);
	$insert_stmt->bindValue(5,$data_list["illumin"]);
	$insert_stmt->bindValue(6,$userid);	//created_userid
	$insert_stmt->bindValue(7,$userid);	//updated_userid
	$insert_stmt->bindValue(8,date("Y-m-d H:i:s"));
	$insert_stmt->bindValue(9,date("Y-m-d H:i:s"));
	$insert_stmt->execute();
	error_log($logheader.' insert i2clogger:'.$data_list["date"]);
    $dbh = null;
    $resp = "ok";
    $error_code = 0;
    $json_data = json_encode(compact("resp","error_code"),JSON_UNESCAPED_UNICODE);
    echo $json_data;
?>

周期的にデータベースにセンサ情報が格納されるので、今後は、webアプリで読み出す機能の実装です。
画面は、以下のようなデザインにしました。(今回は、PC向けの画面のみ紹介します)

表示期間のデフォルトは、当日を含む、月初めからを期間としました。
カレンダマークをクリックすると、カレンダ表示されるので、日付を変更できます。webixのdatepickerというコンポーネントを使用しています。ロケールを日本にする
webix.i18n.setLocale("ja-JP");
ことで、カレンダも日本語表記になります。便利ですね。

datepickerのカスタマイズもいろいろ可能です。詳細は、webixのマニュアルを参照してください。

期間を指定して、検索ボタンをクリックすると、データベースを検索して各種センサの情報を一覧表示します。

以下の例は、2024/4/12の1日で収集している情報が表示されています。
(当日なので、まだデータは、少ないです)

一覧表示は、datatabelというコンポーネントで簡単に表示できますが、時間ごとの情報変化を把握しにくいので、今回、グラフ表示もさせてみました。
グラフは、chartコンポーネントを使用します。

このコンポーネントには、いろいろなグラフスタイルがあるので、詳細は、マニュアルを見てください。今回は、X軸に日時、縦軸に各種センサを数値をプロットし、折れ線グラフで表示します。また、センサは、4つ(温度、湿度、気圧、照度)あるので、それぞれ、個別にwindowを定義して表示しています。今回は、サンプル紹介なので、実装を簡単にしていますが、X軸の表記をどのようにするかは、検討が必要です。データ量が多くなったときに、表示を間引かないと文字が重なってしまいます。
データ数に応じて、間引く処理は記述しましたが、データが多くなった時点で見直しが必要です。
以下表示例です。



温度と気圧が徐々に下がっていることがよくわかります。
一覧だけでは、わかりにくいですね。
今回の画面を構成したソースとデータベースを検索するソースを紹介します。
(画面は、まだスマホ画面を実装していないので、PCモードだけ参考にしてください。)
HD0010_logger_lists.php

<?php
//======================================================================
//File Name       : HD0010_logger_lists.php
//Encoding        : UTF-8
//Creation Date   : 2024-04-11
// 
//Copyright © 2024 sunsunfarm. All rights reserved.
// 
//This source code or any portion thereof must not be  
//reproduced or used in any manner whatsoever.
//====================================================================== 
	$TITLE_INFO ="ロガー";
	$VER_INFO ="V01L01";
	$JOB_INFO = "HD0010";
	$myfilename = basename(__FILE__);	//自分自身のファイル名取得
    define('SUB_FOLDER','/webix01');    //サブフォルダを指定したURL 
    define('ROOT_PATH','/var/www/html/webix01'); //ソースを保存しているパス(動作環境に応じて記述する必要あり)
	$userid = '';
	$logheader = 'userid='.$userid.', '.$myfilename.':';//ログ出力時のヘッダー情報(自ファイル名,ログインIDを付与)
	if($_SERVER["REQUEST_METHOD"] != "GET"){
	  error_log($logheader.'  REQUEST_METHOD no GET');
	  header("HTTP/1.0 404 Not Found");
	  return;
	}
	$error_flag = -1;
	$accesskey = 0;
	$userclient ="pc";
	$mypermission = 0;
	$backgcolor = "#3498DB";
	if(isset($_GET['accesskey'])){
		 if(is_numeric($_GET['accesskey'])){
			$accesskey =$_GET['accesskey'];
			$error_flag = 1;
		}
	}
	else{
		$error_flag = -1;
	  	error_log($logheader.'  accesskey not found');
	  	header("HTTP/1.0 404 Not Found");
	  	exit;
	}
	if(isset($_GET['userid'])){
		$userid = $_GET['userid'];
		$error_flag = 1;
	}
	else{
		$error_flag = -1;
	  	error_log($logheader.'  userid not found');
	  	header("HTTP/1.0 404 Not Found");
	  	exit;
	}
	if(isset($_GET['userclient'])){
		$userclient = $_GET['userclient'];
	}
	if(isset($_GET['backgcolor'])){
		$backgcolor = $_GET['backgcolor'];
		error_log($logheader.'  $backgcolor ='.$backgcolor);
	}
	if(isset($_GET['permission'])){
		if(is_numeric($_GET['permission'])){
			$mypermission = intval($_GET['permission']);
		}
	}
	$logheader = 'userid='.$userid.', '.$myfilename.':';//ログ出力時のヘッダー情報(自ファイル名,ログインIDを付与)
	include('../../commonlib/svr_common_lib_v2.php');	//
	$config_obj = get_config_obj();
	if(Chk_AccessKey($accesskey)){
	}
	else{
		error_log($logheader.'  accesskey check error');
	  	header("HTTP/1.0 404 Not Found");
	  	exit;
	}
?>

<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<title><?php echo $TITLE_INFO.' HD0010 ('.$VER_INFO.')' ?></title> 
		<script src="/webix_GPL_1020/webix.js" type="text/javascript" charset="utf-8"></script>
		<link href="/webix_GPL_1020/skins/compact.css?<?php echo date('Ymd-H'); ?>" rel="stylesheet" type="text/css">
		<link href="<?php  echo SUB_FOLDER; ?>/commonlib/webix_custom_css.css?<?php echo date('Ymd-H'); ?>" rel="stylesheet" type="text/css">
		<link rel="icon" href="<?php  echo SUB_FOLDER; ?>/image/webix_64.ico">
		<script src="<?php  echo SUB_FOLDER; ?>/commonlib/object-assign.js"></script>
		<script src="<?php  echo SUB_FOLDER; ?>/commonlib/moment-with-locales.js"></script>
    	<script src="<?php  echo SUB_FOLDER; ?>/commonlib/webix_common_lib.js"></script>
		<link rel="stylesheet" href="/webix_GPL_1020/css/materialdesignicons.min.css" type="text/css" charset="utf-8">
	<style>
	.blue .webix_el_box{
  		color: #0000ff;
	}
	.blue .webix_control.webix_el_text{
  		color: #0000ff;
	}
	.red .webix_el_box{
  		color: #ff0000;
	}
	.red  .webix_control.webix_el_text{
  		color: #ff0000;
	}
	.invalid_mess{font-size:8px}
	.red  .webix_control.webix_el_text{
  		color: #ff0000;
	}
	.orange_bgcolor button.webix_button{
		background: #FF8856;
		color:#FFFFFF;
		border:1px solid #FF8856;
	}
	.gray_bgcolor {
		background: #efefef;
		
	}
	.highlight{
        background-color:#FFAAAA;
    }
    .teal_bgcolor .webix_button {
 		background-color: #008080;
  		 color: #FFFFFF;
	}
	.green input{
		background-color:#B1FF91;
		border-color:green;
	}
	.pink input{
		background-color:#FFB6FE;
		border-color:red ;
	} 
  	</style>
  	</head>	
	<body>
		<script type="text/javascript" charset="utf-8">		
		webix.i18n.setLocale("ja-JP");
		var my_local_session = webix.storage.local.get('login');
<?php

	include_once('../../commonlib/CM0010_goto_menu_action.php');
	include_once('../../commonlib/CM0030_sendprm_set_request.php');
	include_once('../../commonlib/CM0060_common_validate_check.php');
    include_once('../../commonlib/CM0070_prompt_parameter_setting.php');
    include_once('../../commonlib/CM0080_screen_control.php');
	if(isset($_GET['open_mode'])){
		$open_mode = $_GET['open_mode'];
		if($open_mode == '_blank'){
			echo '	var menu_btn_name = "閉じる";'."\n";
		}
		else{
			echo '	var menu_btn_name = "メニュー";'."\n";
		}
	}
	else{
			echo '	var menu_btn_name = "メニュー";'."\n";
	}
	echo '	var my_permission ='.$mypermission.";\n";
    echo "	var HD0010_backgcolor = '".$backgcolor."';\n";
?>

<?php
	if($userclient != "pc"){
?>
	webix.ui.fullScreen();
<?php
}
?>

webix.i18n.setLocale("ja-JP");

function HD0010_select_ic2logger_lists(limit = ""){
	var fl_startdate = $$("fl_startdate").getValue();
	var fl_enddate = $$("fl_enddate").getValue();
	
	if(fl_startdate == null && fl_enddate == null ){
		webix.message({type:"error", text:"いずれかの日付を指定してください。"});
		return;
	}

	$$("HD0010_table").clearAll();			//リストを一度クリア
	CM0080_datatable_filter_clear("HD0010_table");

	var access_key = Get_AccessKey();
	var send_prm  = Prepare_send_prm(my_local_session, access_key);
	if(fl_startdate != null) send_prm.fl_startdate = moment(fl_startdate).format("YYYY-MM-DD 00:00:00");
	if(fl_enddate != null) send_prm.fl_enddate = moment(fl_enddate).format("YYYY-MM-DD 23:59:59");
	var xhr =webix.ajax().sync().get("<?php  echo SUB_FOLDER; ?>/rest_api/HD0010/HD0012_ic2logger_db_selectlists.php",send_prm);
	var resp =  JSON.parse(xhr.responseText);
	if(resp.resp =="ok"){
		$$("HD0010_table").parse(resp.var_lists);
		var v_count =$$("HD0010_table").count();
		$$("select_count").setValue(String(v_count));
		if(v_count == 0 ){				
			$$("comment").setValue("検索結果は、0件です。");
			webix.message({type:"error", text:"検索結果は、0件です。"});
		}				
		else if(v_count >= 1000){
			$$("comment").setValue("検索結果は、1000件以上で、"+String(v_count)+"件のみ表示");
			webix.message({type:"error", text:"検索結果は、1000件を超過しました。"});
		}
		else{
			$$("comment").setValue("");
		}
	}
	else{
		webix.message({type:"error", text:"検索でエラーが発生しました。code="+resp.error_code});
	}
}

var HD0010_temp_win_collection = [
{rows:[
    {
      view:"chart",
      id:"temp_chart",
      type:"line",
      value:"#temp#",
      barWidth:30,
      radius:0,
      height:300,
      width:500,
      xAxis:{
        template: function(obj) {
          //const temp = obj.humanTs;
          //const format = webix.Date.dateToStr("%m/%d<br>%H:%m");
          //obj.date = format(temp);
          //obj.date = moment(obj.date).format("MM/DD<br>HH:mm");
          return obj.date_str;
        }
      },
      yAxis:{
      	title:"温度℃"
      },
      data:[]
    },
    {view:"label",value: ""},
        {margin:5,
        cols:[
          {width:450},
            {view:"button", value: "閉じる", align:"right", width: 100,
             click:function(){
                    $$('HD0010_temp_chart_win').hide();
                }
            }
        ]
    }
  ]
}
];

var HD0010_humid_win_collection = [
{rows:[
    {
      view:"chart",
      id:"humid_chart",
      type:"line",
      value:"#humid#",
      barWidth:30,
      radius:0,
      height:300,
      width:500,
      xAxis:{
        template: function(obj) {
          return obj.date_str;
        }
      },
      yAxis:{
      	title:"湿度%"
      },
      data:[]
    },
    {view:"label",value: ""},
        {margin:5,
        cols:[
          {width:450},
            {view:"button", value: "閉じる", align:"right", width: 100,
             click:function(){
                    $$('HD0010_humid_chart_win').hide();
                }
            }
        ]
    }
  ]
}
];

var HD0010_atmos_win_collection = [
{rows:[
    {
      view:"chart",
      id:"atmos_chart",
      type:"line",
      value:"#atmos#",
      barWidth:30,
      radius:0,
      height:300,
      width:500,
      xAxis:{
        template: function(obj) {
          return obj.date_str;
        }
      },
      yAxis:{
      	title:"気圧hPa"
      },
      data:[]
    },
    {view:"label",value: ""},
        {margin:5,
        cols:[
          {width:450},
            {view:"button", value: "閉じる", align:"right", width: 100,
             click:function(){
                    $$('HD0010_atmos_chart_win').hide();
                }
            }
        ]
    }
  ]
}
];
var HD0010_illumin_win_collection = [
{rows:[
    {
      view:"chart",
      id:"illumin_chart",
      type:"line",
      value:"#illumin#",
      barWidth:30,
      radius:0,
      height:300,
      width:500,
      xAxis:{
        template: function(obj) {
          return obj.date_str;
        }
      },
      yAxis:{
      	title:"照度lux"
      },
      data:[]
    },
    {view:"label",value: ""},
        {margin:5,
        cols:[
          {width:450},
            {view:"button", value: "閉じる", align:"right", width: 100,
             click:function(){
                    $$('HD0010_illumin_chart_win').hide();
                }
            }
        ]
    }
  ]
}
];

webix.ui({
  view:"window", 
  id:"HD0010_temp_chart_win",
  move:true,
  head:"温度℃", 
  left:200, top:200,
  width:800,
  height:600,
  body:{
       view:"form"
       ,id:"HD0010_temp_win_form"
       ,elements:HD0010_temp_win_collection
       ,width:600
  }
});
webix.ui({
  view:"window", 
  id:"HD0010_humid_chart_win",
  move:true,
  head:"湿度%", 
  left:200, top:200,
  width:800,
  height:600,
  body:{
       view:"form"
       ,id:"HD0010_humid_win_form"
       ,elements:HD0010_humid_win_collection
       ,width:600
  }
});

webix.ui({
  view:"window", 
  id:"HD0010_atmos_chart_win",
  move:true,
  head:"気圧hPa", 
  left:200, top:200,
  width:800,
  height:600,
  body:{
       view:"form"
       ,id:"HD0010_atmos_win_form"
       ,elements:HD0010_atmos_win_collection
       ,width:600
  }
});

webix.ui({
  view:"window", 
  id:"HD0010_illumin_chart_win",
  move:true,
  head:"照度lux", 
  left:200, top:200,
  width:800,
  height:600,
  body:{
       view:"form"
       ,id:"HD0010_illumin_win_form"
       ,elements:HD0010_illumin_win_collection
       ,width:600
  }
});

function HD0010_get_chart_lists(){
    var chart_lists = [];
    var cnt = 0;
 	$$("HD0010_table").eachRow(function(row){ 
		cnt += 1;
	});
	if(cnt <20){
	    $$("HD0010_table").eachRow(function(row){ 
 			const record = $$("HD0010_table").getItem(row);
 			record.date_str = moment(record.date).format("MM/DD<br>HH:mm");
			cnt += 1;
 			chart_lists.push(record);
		});
	}
	else if(cnt <100){
	    $$("HD0010_table").eachRow(function(row){ 
 			const record = $$("HD0010_table").getItem(row);
 			var mm =  moment(record.date).format("mm");
 			if(mm == "00"){
 				record.date_str = moment(record.date).format("MM/DD<br>HH:mm");
 			}
 			else{
 				record.date_str = "";
 			}
			cnt += 1;
 			chart_lists.push(record);
		});
	}
	else{
	    $$("HD0010_table").eachRow(function(row){ 
 			const record = $$("HD0010_table").getItem(row);
 			var HH =  moment(record.date).format("HH:mm");
 			if(HH == "00:00" || HH == "06:00"|| HH == "12:00"|| HH == "18:00"){
 				record.date_str = moment(record.date).format("MM/DD<br>HH:mm");
 			}
 			else{
 				record.date_str = "";
 			}
			cnt += 1;
 			chart_lists.push(record);
		});
	}
	return chart_lists;
}
	
	//検索条件フォーム構成リスト
	var form_collection = [
		{ margin:5,
			cols:[
				{ view:"label", height:50, template:"<span style='font-weight:bold; font-size:180%;'>測定一覧[気温・湿度・気圧・照度](HD0010)</span>",width:890},
			]
		},
<?php
	if($userclient == "pc"){
?>
		{ margin:5, 
			cols:[
				{view:"datepicker",	label:"表示期間",id:"fl_startdate"	,name:"fl_startdate",labelWidth:80,width:210,bottomPadding:15,
					format:webix.Date.dateToStr("%Y/%m/%d"),labelAlign:"right" ,editable: true,value: moment().startOf('month').format('YYYY/MM/DD')
				},
				{view:"datepicker"	,label:"~ ",id:"fl_enddate"	,name:"fl_enddate",labelWidth:50,width:180,bottomPadding:15,
					format:webix.Date.dateToStr("%Y/%m/%d"),labelAlign:"right" ,editable: true,value: moment().format('YYYY/MM/DD')
				},
				{ view:"button", value: "検索", align:"center", width: 110,id:"HD0010_search_btn",name:"HD0010_search_btn",
					click:function(){
						HD0010_select_ic2logger_lists();
					}
				},
				// メニューに戻るボタン実装
				{ view:"button",  value: menu_btn_name, align:"center", width: 110, css:"menu",
					click:function(){
						Goto_Menu(my_local_session["userclient"]);	//メニュー画面へ遷移
					}
				}
			]
		},
		{ margin:5, 
			cols:[
				{ view:"button", value: "温度", align:"center", width: 110,id:"HD0010_temp_btn",name:"HD0010_temp_btn",
					click:function(){
						var chart_lists = HD0010_get_chart_lists();
						$$("temp_chart").clearAll();
						$$("temp_chart").parse(chart_lists);
						$$("temp_chart").refresh();
						$$("HD0010_temp_chart_win").show();
					}
				},
				{ view:"button", value: "湿度", align:"center", width: 110,id:"HD0010_humid_btn",name:"HD0010_humid_btn",
					click:function(){
						var chart_lists = HD0010_get_chart_lists();
						$$("humid_chart").clearAll();
						$$("humid_chart").parse(chart_lists);
						$$("humid_chart").refresh();
						$$("HD0010_humid_chart_win").show();
					}
				},
				{ view:"button", value: "気圧", align:"center", width: 110,id:"HD0010_atmos_btn",name:"HD0010_atmos_btn",
					click:function(){
						var chart_lists = HD0010_get_chart_lists();
						$$("atmos_chart").clearAll();
						$$("atmos_chart").parse(chart_lists);
						$$("atmos_chart").refresh();
						$$("HD0010_atmos_chart_win").show();
					}
				},
				{ view:"button", value: "照度", align:"center", width: 110,id:"HD0010_illumin_btn",name:"HD0010_illumin_btn",
					click:function(){
						var chart_lists = HD0010_get_chart_lists();
						$$("illumin_chart").clearAll();
						$$("illumin_chart").parse(chart_lists);
						$$("illumin_chart").refresh();
						$$("HD0010_illumin_chart_win").show();
					}
				},
			]
		},
<?php
}
else{
?>
		{ margin:5, 
			cols:[
				{ view:"button", value: "検索", align:"center", width: 110,id:"HD0010_search_btn",name:"HD0010_search_btn",
					click:function(){
						HD0010_select_ic2logger_lists();
					}
				},
				// メニューに戻るボタン実装
				{ view:"button",  value: menu_btn_name, align:"center", width: 110, css:"menu",
					click:function(){
						Goto_Menu(my_local_session["userclient"]);	//メニュー画面へ遷移
					}
				}
			]
		},
<?php
}
?>
	];
	var grid1 = {
		id: "HD0010_table",
		name: "HD0010_table",
		type:"clean",
		view:"datatable",
		width:800,
		resizeColumn:true,
		resizeRow: { headerOnly:true },
		columns:[
            { id:"id"     ,header:["id"     ,{content:"textFilter"}],width:100,sort:"int", css:{"text-align":"right"}},
            { id:"date"   ,header:["日時"   ,{content:"textFilter"}],width:150,sort:"int", css:{"text-align":"right"},
             	format:function(value){
   					var str1 = String(value);
   					if(str1 == "" || str1 == 0){
   						return "";
   					}
   					else{
        	    		return moment(value).format("YYYY/MM/DD HH:mm");
        			}
        		}
             },
            { id:"temp"   ,header:["温度℃" ,{content:"textFilter"}],width:100,sort:"int", css:{"text-align":"right"}},
            { id:"humid"  ,header:["湿度%" ,{content:"textFilter"}],width:100,sort:"int", css:{"text-align":"right"}},
            { id:"atmos"  ,header:["気圧hPa",{content:"textFilter"}],width:100,sort:"int", css:{"text-align":"right"}},
            { id:"illumin",header:["照度lux",{content:"textFilter"}],width:100,sort:"int", css:{"text-align":"right"}},
		],
		css:"webix_data_border webix_header_border",
		resizeColumn:true, resizeRow:true,
		leftSplit:2,
		scroll:"xy",
		select:"row",
		datatype:"json",
		data:[]
	};

<?php
	if($userclient == "pc"){
?>
	var form1_height = 200;
<?php
}
else{
?>
	var form1_height = 200;
<?php
}
?>
	
	//画面描画
	webix.ui({
		padding: 10,
		rows:[ { view:"form", id: "form1", elements : form_collection, margin:3, select:true ,height:form1_height},
			   { margin:5,cols: [
			      { view:"text", label:"検索結果(件)", labelWidth:100,labelAlign:"right",name:"select_count",id:"select_count",value:0,width:160, inputAlign:"right",readonly:true },
		          { view:"label",label:"", width:300, name:"comment", id:"comment" }
		       ]},
		grid1,
		]
	});

$$("fl_startdate").attachEvent("onEnter",function(ev){
    var a = $$("fl_startdate").getValue();
    if(a!=''){
    	$$('HD0010_search_btn').focus();
    }
});
$$("fl_enddate").attachEvent("onEnter",function(ev){
    var a = $$("fl_enddate").getValue();
    if(a!=''){
    	$$('HD0010_search_btn').focus();
    }
});




</script>
</body>
</html>

サーバ側PHP HD0012_ic2logger_db_selectlists.php

<?php
//======================================================================
//File Name       : HD0012_ic2logger_db_selectlists.php
//Encoding        : UTF-8
//Creation Date   : 2024-04-11
// 
//Copyright © 2024 sunsunfarm. All rights reserved.
// 
//This source code or any portion thereof must not be  
//reproduced or used in any manner whatsoever.
//====================================================================== 
    header("Content-Type: text/javascript; charset=utf-8");
    if($_SERVER["REQUEST_METHOD"] != "GET"){
        header("HTTP/1.0 404 Not Found");
        return;
    }

    $db_select_limit = 1000;
    //検索条件用パラメータ(初期値)
    $fl_startdate = ''; 
    $fl_enddate = "";
    $userid ='';
    $accesskey =0;
    
    $error_flag = -1;
    //GET情報からパラメータ取得
    if(isset($_GET['accesskey'])){
        if(is_numeric($_GET['accesskey'])){
            $accesskey =$_GET['accesskey'];
        }
    }
    else{
        $error_flag = -1;
        $result_code = "ng";
        $error_code = -1;
        $json_data = json_encode(compact("result_code","error_code"),JSON_UNESCAPED_UNICODE);
        echo $json_data;
        exit;
    }
    if(isset($_GET['userid'])){
        $userid = $_GET['userid'];
    }
    else{
        $error_flag = -1;
        $result_code = "ng";
        $error_code = -2;
        $json_data = json_encode(compact("result_code","error_code"),JSON_UNESCAPED_UNICODE);
        echo $json_data;
        exit;
    }
    $myfilename = basename(__FILE__);
    $logheader = 'userid='.$userid.', '.$myfilename.':';
    include('../../commonlib/svr_common_lib_v2.php');
    $config_obj = get_config_obj();


    //アクセスキーチェック
    if(Chk_AccessKey($accesskey)){
        $error_flag = 1;
    }
    else{
        $result_code = "ng";
        $error_code = -3;
        $json_data = json_encode(compact("result_code","error_code"),JSON_UNESCAPED_UNICODE);
        echo $json_data;
        exit;
    }
    
    if(isset($_GET['fl_startdate'])){
        $fl_startdate = $_GET['fl_startdate'];
    }
    if(isset($_GET['fl_enddate'])){
        $fl_enddate = $_GET['fl_enddate'];
    }
    //検索用SQL文作成
    function create_select_sql($fl_startdate ,$fl_enddate ,$db_select_limit)
    {

        $select_sql1 = " SELECT date,temp,humid,atmos,illumin from i2clogger ";
        $where_str_array = array();
        if($fl_startdate != ""){
            $where_str_array[] =" date >= '".$fl_startdate."'";
        }
        if($fl_enddate  != ""){
            $where_str_array[] =" date <= '".$fl_enddate."'";
        }
		if(count($where_str_array) > 0){
			$where_str = implode(" and ",$where_str_array);
			$select_sql1 =  $select_sql1." where ".$where_str;
		}
        $select_sql3 =" ORDER BY date LIMIT ".strval($db_select_limit);
        return $select_sql1.$select_sql3;
    }
    //
    //メインルーチン
    //
    if($error_flag == -1){
        //正しい遷移でない場合は、強制的にログイン画面に遷移
        header("HTTP/1.0 404 Not Found");
        return;
    }

    //データベース接続する(MariaDB)
    $dbh = mariadb_connect($config_obj,'app01','webix_webix');
    if($dbh == false){
    	//エラー応答
    	error_log($logheader.' DB connect error');
    	$resp = "ng";
		$error_code = -2;
 		$json_data = json_encode(compact("resp","error_code","count","var_lists"),JSON_UNESCAPED_UNICODE);
		echo $json_data;
		exit;
    }

    $select_sql = create_select_sql($fl_startdate ,$fl_enddate ,$db_select_limit);
    //error_log($logheader.' SQL ='.$select_sql);

	//SQL文の実行
	$stmt = $dbh->query($select_sql );
	try {
		$var_lists =  $stmt->fetchAll(PDO::FETCH_ASSOC);  //フィールド名だけで保存(index情報なし)
		$count = count($var_lists);
		for($i=0;$i<$count;$i++){
			$var_lists[$i]["id"] = $i+1;
		}
		$resp = "ok";
		$error_code = 0;
	}catch (Exception $e) {
		error_log($logheader.'  捕捉した例外: '.$e->getMessage());	//例外発生時の処理(エラー情報をログに格納してエラー応答)
		$resp = "ng";
		$count = 0;
		$error_code = -1;
        $json_data = json_encode(compact("resp","error_code"),JSON_UNESCAPED_UNICODE);
        echo $json_data;
   	}
	$stmt = null;
	$dbh = null;
	$json_data = json_encode(compact("resp","error_code","count","var_lists"),JSON_UNESCAPED_UNICODE);
	echo $json_data;
?>

以下は、4つのグラフを表示した例です。

RaspberryPiで簡単にセンサ情報を収集してwebサービスで情報公開できます。性能的にも問題ありません。ぜひ、いろいろな情報収集とWebでの情報表示に活用してみてください。
不明点などありましたら、お問合せください。

この記事が気に入ったらサポートをしてみませんか?