各種センサの情報を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での情報表示に活用してみてください。
不明点などありましたら、お問合せください。
この記事が気に入ったらサポートをしてみませんか?