屋内環境モニタ

最終修正日:2021/1/8

ESP32を採用したプロジェクトを何か進めたいと思っています。
気象モニタは止めました。では何を目指すか!やはり工場内の環境モニタがいいかなと思いました。

工場内の環境モニタとなると、雰囲気センサの類いを扱うべきです。また、電源も、自律したいのでソーラパネルも装備しておきたいです。工場内の蛍光灯から得られエネルギーで運営できるのではないかなと感じています。

工場内で環境モニタしたい項目を検討しました。気温、湿度は常套として、気圧、埃度合、CO2濃度、明るさ、騒音辺りが計測出来れば良いかなと思っています。また、I2Cで接続出来るセンサであれば増設も容易に出来るようにして置けば良いのかと思いました。

センサの選択ですが、

表示器ですが、

電源ですが、自己完結すべくソーラ発電機を用意しておきたいです。バッテリ充電コントローラを介してリチュームイオン電池から電源取得するような回路構成とします。
屋内で自己完結するだけの電池容量、太陽光発電容量をきちんと求める必要がありますが、とりあえずたたき台を使って成り立つかどうか確認した上で、消費電流を求めた方がいいのかなと思います。

消費電流等の情報
@ ESP
A シャープ埃センサモジュール
B 温度センサBME280
C 照度センサTSL2561
D CO2センサCCS811
E マイクモジュール
F LED
G OLED

製品の説明

ハードウェア仕様
CPUモジュール ESP32 38pinタイプ開発モジュール ESP32-DEVKITC-32U 技適 外部アンテナ(U.FL)
バッテリ充電コントローラ TP-4056 他にDFROBOTのモジュールも使えそう
ソーラバッテリ 5.5Vタイプの太陽光発電モジュール ハウジングに設置できるようなサイズであること
ダイオード 逆流防止用のダイオードを用意する  1N4007(1000V1A)相当 UF2010(1000V2A)
バッテリケース 18650 1本用
バッテリ 18650タイプ 10000mAh 3.7V
アンテナ 外部アンテナアダプタ SMA
温度モジュール BME280 端子の並びに注意 VCC/GND/SCL/SDA
照度モジュール TSL2561 端子の並びに注意 VCC/GND/SCL/SDA
OLCDモジュール I2C  SSD1306 128 ×64 端子の並びに注意 VCC/GND/SCL/SDA
ハウジング  


グラフ表示出来るようにスケッチを検討しました。Chart.jsを利用して、1日分のデータを確保し、表示要求があったときに提供出来るようなモノがいいかなと考えました。

iPhoneでも表示出来る事を確認しました。

表示設計として、通常はモニタ画面ですが、設定画面と、OTA用のUpdate画面を用意しておきたいと考えました。

ソースコードもレタッチしました。

必要なライブラリは、使用するセンサ毎に対応が必要になります。

https://github.com/adafruit/Adafruit-GFX-Library
https://github.com/adafruit/Adafruit_SSD1306
https://github.com/finitespace/BME280/blob/master/src/BME280I2C.hAdafruit_BME280.hはうまく動作しませんでした
https://github.com/adafruit/TSL2561-Arduino-Library

/*
 * 20201015 T.Wanibe
 * 『Indoor environment monitor』としてプロジェクトを更新
 * Chart.jsを作り込み強化
 * フラッシュの使用量が多少増えているが、まだまだ大丈夫そう
 * 1日分のデータバッファをサイクリックに使うことにした。そのため初期値をキチンと定義することにした。
 * データが無い場合はNULLにすることでグラフプロットが無くなることが判っているので対応した。
 * RootのHTML表示スタイルを更新した。
 * 時刻表示を追加して見ました。起動時にNTPサーバにアクセスし、時刻取得。ESP32内部のRTCに登録してOLCDに時刻表示する
 * ESP32のGPIOの一部のpinにはArduinoに用意されているanalogWrite()がありません。DACが存在するからかもしれません。
 * その代わりなのかLED_PWMという機能が用意されているようです。https://garretlab.web.fc2.com/arduino/lab/ledc/
 * この機能が利用できるPinは16個のようです。今回PO3(GPIO4)、PO4(GPIO2)が対象pinのようですので、改造してみました。
 * PWMによるLEDの明るさ調整ですが、単純に直線的な輝度制御は難しいですね
 * LEDの配置等を変更
 * OTAを維持したまま、モードをST+APにして普段はPCとの間でトランシーバモード、一方、ファーム更新はSTモードで実現出来ないか確認する
 * Adafruit_BME280.hがESP32でうまく動作しないため、ライブラリを変更し、BME280I2C.hを採用した
 * WebServer  on()の扱いをリファレンスで確認する必要があります。
 * https://garretlab.web.fc2.com/arduino/esp32/reference/libraries/WebServer/WebServer_on.html
 * この関数の為、WiznetのWebServer関数との整合性がとれない。 ESP32とSTM32の共存は難しそう
 * この記述はESP8266/32のみ
 * ※HTMLの記述にてString型を駆使して記述するが、その手法も多岐にわたる。好みの方法を確立しておく必要がある
 * 最大1310720バイトのフラッシュメモリのうち、スケッチが809234バイト(61%)を使っています。
 * 最大327680バイトのRAMのうち、グローバル変数が43520バイト(13%)を使っていて、ローカル変数で284160バイト使うことができます。
 * バイナリファイルは787kBですので上記スケッチサイズに合致します。
 * ブラウザからの転送はあっという間に完了します。
 */
#include <WiFi.h>
#include <time.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//#include <Adafruit_Sensor.h>
//#include <Adafruit_BME280.h>
#include <BME280I2C.h>
#include "arduino_secrets.h"                                            //機密データを「Secret」タブ/arduino_secrets.hに入力してください
#define HTTPport        80
#define SCREEN_WIDTH    128                                             // OLED display width, in pixels
#define SCREEN_HEIGHT   64                                              // OLED display height, in pixels
#define OLED_RESET      -1                                              // Reset pin # (or -1 if sharing Arduino reset pin)
#define NUMFLAKES       10                                              // アニメーション例の雪片の数
#define LOGO_HEIGHT     16
#define LOGO_WIDTH      16
#define OLCDAres        0x3C                                            //0x3Cか0x3D
#define LED             13                                              //ボード上のLEDは使えないのでGPIO13にLEDを接続
#define AIport          A0
#define PO1             17
#define PO2             16
#define PO3             4
#define PO4             2
#define PI1             39
#define PI2             34
#define PI3             35
#define PI4             33
//#define BMEID           0x58
#define LEDC_CHANNEL_0  0                                               // use first channel of 16 channels (started from zero)
#define LEDC_CHANNEL_1  1                                               // use first channel of 16 channels (started from zero)
const char*     host            = "esp32";
char            ssid[]          = SECRET_SSID;                          //ネットワークSSID(名前)
char            pass[]          = SECRET_PASS;                          //ネットワークパスワード(WPAの使用、またはWEPのキーとして使用)
String          myID            = "admin";
String          myPASS          = "password";
//variabls for blinking an LED with Millis
unsigned long   previousMillis  = 0;                                    //LEDが最後に更新されたときに保存されます
const long      interval        = 1000;                                 //点滅する間隔(ミリ秒)
int             ledState        = LOW;                                  //LEDの設定に使用されるledState
unsigned        status;
//Station用
IPAddress       ip(192, 168, 0, 33);
IPAddress       gateway(192, 168, 0, 1);
IPAddress       netmask(255, 255, 255, 0);
//SoftAP用
IPAddress       ap_ip(192, 168, 250, 33);
IPAddress       ap_gateway(192, 168, 250, 1);
const char*     ap_ssid         = "ESP_AP";                             //このSSIDが公開される。
const char*     ap_password     = "password";                           //
char            buf1[128];
char            buf2[128];
char            buf3[128];
String          buf11           = "ABC";
String          buf22           = "DEF";
float           gDataBuf[3];
float           gDayBuf[24][3];                                         //24時間分3要素データ
float           temp(NAN),hum(NAN),pres(NAN);                           // hum(NAN)は無効
int             brightness      = 0;
int             fadeAmount      = 25;
//const char*     ntpServer     ="ntp.jst.mfeed.ad.jp";                 //日本のNTPサーバー選択
const char*     ntpServer       ="192.168.0.206";                       //LocalTimeServer
const long      gmtOffset_sec = 9 * 3600;                               //9時間の時差を入れる
const int       daylightOffset_sec = 0;                                 //夏時間はないのでゼロ
struct          tm      timeinfo;
WebServer       WS(HTTPport);
//I2C(SDA、SCLピン)に接続されたSSD1306ディスプレイの宣言
Adafruit_SSD1306 OLCD(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
BME280I2C       bmp;                                                    // Default : forced mode, standby time = 1000 ms
                                                                        // Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off,
//-------------- Style
String style =
"<style>\n"
        "\t#file-input,input{width:100%;height:44px;border-radius:4px;margin:10px auto;font-size:15px}\n"
        "\tinput{background:#f1f1f1;border:0;padding:0 15px}body{background:#3498db;font-family:sans-serif;font-size:14px;color:#777}\n"
        "\t#file-input{padding:0;border:1px solid #ddd;line-height:44px;text-align:left;display:block;cursor:pointer}\n"
        "\t#bar,#prgbar{background-color:#f1f1f1;border-radius:10px}#bar{background-color:#3498db;width:0%;height:10px}\n"
        "\tform{background:#fff;max-width:258px;margin:75px auto;padding:30px;border-radius:5px;text-align:center}\n"
        "\t.btn{background:#3498db;color:#fff;cursor:pointer}\n"
"</style>\n";
//-------------- Login page
String loginIndex = 
"<form name=loginForm>\n"
        "\t<h1>ESP32 Login</h1>\n"
        "\t<input name=userid placeholder='User ID'>\n"
        "\t<input name=pwd placeholder=Password type=Password>\n"
"<input type=submit onclick=check(this.form) class=btn value=Login>\n</form>\n"
"<script>\n"
        "\tfunction check(form) {\n"
                "\t\tif(form.userid.value== '" + myID + "' && form.pwd.value== '" + myPASS +"' )\n"
                        "\t\t\t{window.open('/serverIndex')}\n"
                "\t\telse\n"
                        "\t\t\t{alert('Error Password or Username')}\n"
        "\t}\n"
"</script>\n" + style;
//-------------- Server Index Page
String serverIndex = 
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>\n"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>\n"
        "\t<input type='file' name='update' id='file' onchange='sub(this)' style=display:none>\n"
        "\t<label id='file-input' for='file'>   Choose file...</label>\n"
        "\t<input type='submit' class=btn value='Update'>\n"
        "\t<br><br>\n"
        "\t<div id='prg'></div>\n"
        "\t<br><div id='prgbar'><div id='bar'></div></div><br>\n"
"</form>\n"
"<script>\n"
        "\tfunction sub(obj){\n"
                "\t\tvar fileName = obj.value.split('\\\\');\n"
                "\t\tdocument.getElementById('file-input').innerHTML = '   '+ fileName[fileName.length-1];\n"
        "\t};\n"
        "\t$('form').submit(function(e){\n"
                "\t\te.preventDefault();\n"
                "\t\tvar form = $('#upload_form')[0];\n"
                "\t\tvar data = new FormData(form);\n"
                "\t\t$.ajax({\n"
                        "\t\t\turl: '/update',\n"
                        "\t\t\ttype: 'POST',\n"
                        "\t\t\tdata: data,\n"
                        "\t\t\tcontentType: false,\n"
                        "\t\t\tprocessData:false,\n"
                        "\t\t\txhr: function() {\n"
                                "\t\t\t\tvar xhr = new window.XMLHttpRequest();\n"
                                "\t\t\t\txhr.upload.addEventListener('progress', function(evt) {\n"
                                        "\t\t\t\t\tif (evt.lengthComputable) {\n"
                                                "\t\t\t\t\t\tvar per = evt.loaded / evt.total;\n"
                                                "\t\t\t\t\t\t$('#prg').html('progress: ' + Math.round(per*100) + '%');\n"
                                                "\t\t\t\t\t\t$('#bar').css('width',Math.round(per*100) + '%');\n"
                                        "\t\t\t\t\t}\n"
                                "\t\t\t\t}, false);\n"
                                "\t\t\t\treturn xhr;\n"
                        "\t\t\t},\n"
                        "\t\t\tsuccess:function(d, s) {\n"
                                "\t\t\t\tconsole.log('success!')\n"
                        "\t\t\t},\n"
                        "\t\t\terror: function (a, b, c) {\n"
                        "\t\t\t}\n"
                "\t\t});\n"
        "\t});\n"
"</script>\n" + style;
//------------ fileNotFound
String fileNotFound = 
"<html><head>\n"
        "\t<title>ESP32 : 404 Not Found</title>\n"
"</head><body>\n"
        "\t<center><h1>ESP32 404 : Not Found</h1></center>\n"
        "\t<p>The requested URL was not found on this server.</p>\n"
"</body></html>\n";
//------------
void handleGetData() {
        //float a           = analogRead(A0);
        //String adcValue = String(a);
        gDataBuf[0]     =       analogRead(A0);
        gDataBuf[1]     =       temp;
        gDataBuf[2]     =       pres;
        WS.send(200, "text/html", SendHTML(gDataBuf));         //Send ADC value only to client ajax request
}
//------------
void handleNotFound(){
        //WS.send(404, "text/plain", "Not found");
        WS.send(404, "text/html",fileNotFound);
}
//------------
String SendHTML(float *tempSensor){
        String ptr =
        "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
        "<html lang='ja'><head><META http-equiv='Content-Type' content='text/html; charset=utf-8'>\n"
        "<META http-equiv='Content-Style-Type' content='text/css'>\n"
                "\t<title>IndoorEnvironmentMonitor</title>\n"
                "\t<SCRIPT SRC='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.bundle.js'></SCRIPT>\n"
                "\t<SCRIPT SRC='https://cdn.rawgit.com/chartjs/Chart.js/master/samples/utils.js'></SCRIPT>\n"
                "\t<style>\n"
                        "\t\tcanvas{\n"
                        "\t\t\t-moz-user-select: none;\n"
                        "\t\t\t-webkit-user-select: none;\n"
                        "\t\t\t-ms-user-select: none;\n"
                        "\t\t}\n"
                        "\t\thtml { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"
                        "\t\tbody{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n"
                        "\t\tp {font-size: 24px;color: #444444;margin-bottom: 10px;}\n"
                "\t</style>\n"
        "</head>\n"
        "<BODY BGCOLOR='#ffffff'>\n"
        "<CENTER><TABLE WIDTH='750' BORDER='0' CELLSPACING='0' CELLPADDING='0'>\n"
                "\t<TR>\n"
                        "\t\t<TD COLSPAN='3' VALIGN='TOP'>\n"
                        "\t\t<H2><CENTER>IndoorEnvironmentMonitor</CENTER></H2>\n"
                        "\t\t</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%'>\n"
                        "\t\t<BR CLEAR='ALL'></TD>\n"
                        "\t\t<TD WIDTH='33%'></TD>\n"
                        "\t\t<TD WIDTH='34%'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%'>\n"
                        "\t\t<BR CLEAR='ALL'></TD>\n"
                        "\t\t<TD WIDTH='33%'></TD>\n"
                        "\t\t<TD WIDTH='34%'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>データ取得時刻:</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(buf3) +"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'></TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>気圧(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(tempSensor[2],0)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[Pa]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>気温(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(tempSensor[1],1)+"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[℃]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>湿度(BME280):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP'></TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[%]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>浮遊粒子(ESP32):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP'></TD>\n" 
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[mg/m3]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>CO2濃度(ESP32):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP'></TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[ppm]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>照度(TSL2561):</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP'></TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[lux]</TD>\n"
                "\t</TR>\n"
                "\t<TR>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='RIGHT'>騒音:</TD>\n"
                        "\t\t<TD WIDTH='33%' VALIGN='TOP' ALIGN='CENTER'>" +String(tempSensor[0],0) +"</TD>\n"
                        "\t\t<TD WIDTH='34%' VALIGN='TOP'>[dBm]</TD>\n"
                "\t</TR>\n"
        "</TABLE>\n"
        "<div style='width:75%;'><canvas id='canvas' width='750' height='400' style='display: block; width: 750px; height: 400px;'></canvas></div>\n"
        "</CENTER>\n"
        "\t<script>\n"
                "\t\tvar randomScalingFactor = function() {\n"
                        "\t\t\treturn 18 + Math.random() * 10;\n"
                "\t\t};\n"
                "\t\tvar data_00 = ["+  (gDayBuf[0][2] ==0.0 ? String('\0') : String(gDayBuf[0][2],0))+ ","+    (gDayBuf[1][2] ==0.0 ? String('\0') : String(gDayBuf[1][2],0))+ ","+
                                        (gDayBuf[2][2] ==0.0 ? String('\0') : String(gDayBuf[2][2],0))+ ","+    (gDayBuf[3][2] ==0.0 ? String('\0') : String(gDayBuf[3][2],0))+ ","+
                                        (gDayBuf[4][2] ==0.0 ? String('\0') : String(gDayBuf[4][2],0))+ ","+    (gDayBuf[5][2] ==0.0 ? String('\0') : String(gDayBuf[5][2],0))+ ","+
                                        (gDayBuf[6][2] ==0.0 ? String('\0') : String(gDayBuf[6][2],0))+ ","+    (gDayBuf[7][2] ==0.0 ? String('\0') : String(gDayBuf[7][2],0))+ ","+
                                        (gDayBuf[8][2] ==0.0 ? String('\0') : String(gDayBuf[8][2],0))+ ","+    (gDayBuf[9][2] ==0.0 ? String('\0') : String(gDayBuf[9][2],0))+ ","+
                                        (gDayBuf[10][2]==0.0 ? String('\0') : String(gDayBuf[10][2],0))+","+    (gDayBuf[11][2]==0.0 ? String('\0') : String(gDayBuf[11][2],0))+","+
                                        (gDayBuf[12][2]==0.0 ? String('\0') : String(gDayBuf[12][2],0))+","+    (gDayBuf[13][2]==0.0 ? String('\0') : String(gDayBuf[13][2],0))+","+
                                        (gDayBuf[14][2]==0.0 ? String('\0') : String(gDayBuf[14][2],0))+","+    (gDayBuf[15][2]==0.0 ? String('\0') : String(gDayBuf[15][2],0))+","+
                                        (gDayBuf[16][2]==0.0 ? String('\0') : String(gDayBuf[16][2],0))+","+    (gDayBuf[17][2]==0.0 ? String('\0') : String(gDayBuf[17][2],0))+","+
                                        (gDayBuf[18][2]==0.0 ? String('\0') : String(gDayBuf[18][2],0))+","+    (gDayBuf[19][2]==0.0 ? String('\0') : String(gDayBuf[19][2],0))+","+
                                        (gDayBuf[20][2]==0.0 ? String('\0') : String(gDayBuf[20][2],0))+","+    (gDayBuf[21][2]==0.0 ? String('\0') : String(gDayBuf[21][2],0))+","+
                                        (gDayBuf[22][2]==0.0 ? String('\0') : String(gDayBuf[22][2],0))+","+    (gDayBuf[23][2]==0.0 ? String('\0') : String(gDayBuf[23][2],0))+","+
                "];\n"
                "\t\tvar data_01 = ["+  (gDayBuf[0][1] ==0.0 ? String('\0') : String(gDayBuf[0][1],1))+ ","+    (gDayBuf[1][1] ==0.0 ? String('\0') : String(gDayBuf[1][1],1))+ ","+
                                        (gDayBuf[2][1] ==0.0 ? String('\0') : String(gDayBuf[2][1],1))+ ","+    (gDayBuf[3][1] ==0.0 ? String('\0') : String(gDayBuf[3][1],1))+ ","+
                                        (gDayBuf[4][1] ==0.0 ? String('\0') : String(gDayBuf[4][1],1))+ ","+    (gDayBuf[5][1] ==0.0 ? String('\0') : String(gDayBuf[5][1],1))+ ","+
                                        (gDayBuf[6][1] ==0.0 ? String('\0') : String(gDayBuf[6][1],1))+ ","+    (gDayBuf[7][1] ==0.0 ? String('\0') : String(gDayBuf[7][1],1))+ ","+
                                        (gDayBuf[8][1] ==0.0 ? String('\0') : String(gDayBuf[8][1],1))+ ","+    (gDayBuf[9][1] ==0.0 ? String('\0') : String(gDayBuf[9][1],1))+ ","+
                                        (gDayBuf[10][1]==0.0 ? String('\0') : String(gDayBuf[10][1],1))+","+    (gDayBuf[11][1]==0.0 ? String('\0') : String(gDayBuf[11][1],1))+","+
                                        (gDayBuf[12][1]==0.0 ? String('\0') : String(gDayBuf[12][1],1))+","+    (gDayBuf[13][1]==0.0 ? String('\0') : String(gDayBuf[13][1],1))+","+
                                        (gDayBuf[14][1]==0.0 ? String('\0') : String(gDayBuf[14][1],1))+","+    (gDayBuf[15][1]==0.0 ? String('\0') : String(gDayBuf[15][1],1))+","+
                                        (gDayBuf[16][1]==0.0 ? String('\0') : String(gDayBuf[16][1],1))+","+    (gDayBuf[17][1]==0.0 ? String('\0') : String(gDayBuf[17][1],1))+","+
                                        (gDayBuf[18][1]==0.0 ? String('\0') : String(gDayBuf[18][1],1))+","+    (gDayBuf[19][1]==0.0 ? String('\0') : String(gDayBuf[19][1],1))+","+
                                        (gDayBuf[20][1]==0.0 ? String('\0') : String(gDayBuf[20][1],1))+","+    (gDayBuf[21][1]==0.0 ? String('\0') : String(gDayBuf[21][1],1))+","+
                                        (gDayBuf[22][1]==0.0 ? String('\0') : String(gDayBuf[22][1],1))+","+    (gDayBuf[23][1]==0.0 ? String('\0') : String(gDayBuf[23][1],1))+","+
                "];\n"
                "\t\tvar data_02 = ["+  (gDayBuf[0][0] ==0.0 ? String('\0') : String(gDayBuf[0][0],0))+ ","+    (gDayBuf[1][0] ==0.0 ? String('\0') : String(gDayBuf[1][0],0))+ ","+
                                        (gDayBuf[2][0] ==0.0 ? String('\0') : String(gDayBuf[2][0],0))+ ","+    (gDayBuf[3][0] ==0.0 ? String('\0') : String(gDayBuf[3][0],0))+ ","+
                                        (gDayBuf[4][0] ==0.0 ? String('\0') : String(gDayBuf[4][0],0))+ ","+    (gDayBuf[5][0] ==0.0 ? String('\0') : String(gDayBuf[5][0],0))+ ","+
                                        (gDayBuf[6][0] ==0.0 ? String('\0') : String(gDayBuf[6][0],0))+ ","+    (gDayBuf[7][0] ==0.0 ? String('\0') : String(gDayBuf[7][0],0))+ ","+
                                        (gDayBuf[8][0] ==0.0 ? String('\0') : String(gDayBuf[8][0],0))+ ","+    (gDayBuf[9][0] ==0.0 ? String('\0') : String(gDayBuf[9][0],0))+ ","+
                                        (gDayBuf[10][0]==0.0 ? String('\0') : String(gDayBuf[10][0],0))+","+    (gDayBuf[11][0]==0.0 ? String('\0') : String(gDayBuf[11][0],0))+","+
                                        (gDayBuf[12][0]==0.0 ? String('\0') : String(gDayBuf[12][0],0))+","+    (gDayBuf[13][0]==0.0 ? String('\0') : String(gDayBuf[13][0],0))+","+
                                        (gDayBuf[14][0]==0.0 ? String('\0') : String(gDayBuf[14][0],0))+","+    (gDayBuf[15][0]==0.0 ? String('\0') : String(gDayBuf[15][0],0))+","+
                                        (gDayBuf[16][0]==0.0 ? String('\0') : String(gDayBuf[16][0],0))+","+    (gDayBuf[17][0]==0.0 ? String('\0') : String(gDayBuf[17][0],0))+","+
                                        (gDayBuf[18][0]==0.0 ? String('\0') : String(gDayBuf[18][0],0))+","+    (gDayBuf[19][0]==0.0 ? String('\0') : String(gDayBuf[19][0],0))+","+
                                        (gDayBuf[20][0]==0.0 ? String('\0') : String(gDayBuf[20][0],0))+","+    (gDayBuf[21][0]==0.0 ? String('\0') : String(gDayBuf[21][0],0))+","+
                                        (gDayBuf[22][0]==0.0 ? String('\0') : String(gDayBuf[22][0],0))+","+    (gDayBuf[23][0]==0.0 ? String('\0') : String(gDayBuf[23][0],0))+","+
                "];\n"
                "\t\tvar config = {\n"
                        "\t\t\ttype: 'line',\n"
                        "\t\t\tdata: {\n"
                                "\t\t\t\tlabels: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12' , '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'],\n"
                                "\t\t\t\tdatasets: [{\n"
                                        "\t\t\t\t\tlabel: '気圧[Pa]',\n"
                                        "\t\t\t\t\tdata: data_00,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.red,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tcubicInterpolationMode: 'monotone',\n"
                                        "\t\t\t\t\tyAxisID: 'y1'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '気温[℃]',\n"
                                        "\t\t\t\t\tdata: data_01,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.blue,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tyAxisID: 'y2'\n"
                                "\t\t\t\t}, {\n"
                                        "\t\t\t\t\tlabel: '騒音[dBm]',\n"
                                        "\t\t\t\t\tdata: data_02,\n"
                                        "\t\t\t\t\tborderColor: window.chartColors.green,\n"
                                        "\t\t\t\t\tbackgroundColor: 'rgba(0, 0, 0, 0)',\n"
                                        "\t\t\t\t\tfill: false,\n"
                                        "\t\t\t\t\tlineTension: 0,\n"
                                        "\t\t\t\t\tyAxisID: 'y3'\n"
                                "\t\t\t\t}]\n"
                        "\t\t\t},\n"
                        "\t\t\toptions: {\n"
                                "\t\t\t\tresponsive: true,\n"
                                "\t\t\t\ttitle: {\n"
                                        "\t\t\t\t\tdisplay: true,\n"
                                        "\t\t\t\t\ttext: '今日の測定結果'\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\tlegend: {                               //凡例\n"
                                        "\t\t\t\t\tdisplay: true\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\ttooltips: {                             //ツールチップ\n"
                                        "\t\t\t\t\tenabled: true,\n"
                                        "\t\t\t\t\tmode: 'index'\n"
                                "\t\t\t\t},\n"
                                "\t\t\t\tscales: {\n"
                                        "\t\t\t\t\txAxes: [{\n"
                                                "\t\t\t\t\t\tdisplay: true,\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '時刻 [時]'\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t}],\n"
                                        "\t\t\t\t\tyAxes: [{\n"
                                                "\t\t\t\t\t\tid:'y1',\n"
                                                "\t\t\t\t\t\tdisplay: true,\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.red,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '圧力 [Pa]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.red,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 120000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y2',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.blue,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '温度 [℃]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.blue,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 60\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t},{\n"
                                                "\t\t\t\t\t\tid:'y3',\n"
                                                "\t\t\t\t\t\tscaleLabel: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.green,\n"
                                                        "\t\t\t\t\t\t\tdisplay: true,\n"
                                                        "\t\t\t\t\t\t\tlabelString: '騒音[dBm]'\n"
                                                "\t\t\t\t\t\t},\n"
                                                "\t\t\t\t\t\tticks: {\n"
                                                        "\t\t\t\t\t\t\tfontColor: window.chartColors.green,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMin: 0,\n"
                                                        "\t\t\t\t\t\t\tsuggestedMax: 6000\n"
                                                "\t\t\t\t\t\t}\n"
                                        "\t\t\t\t\t}]\n"
                                "\t\t\t\t}\n"
                        "\t\t\t}\n"
                "\t\t};\n"
                "\t\twindow.onload = function() {\n"
                        "\t\t\tvar ctx = document.getElementById('canvas').getContext('2d');\n"
                        "\t\t\twindow.myLine = new Chart(ctx, config);\n"
                "\t\t};\n"
        "\t</script>\n</BODY>\n"
        "</html>\n";
        return ptr;
}
//------------ BMPprint
void printValues() {
        BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
        BME280::PresUnit presUnit(BME280::PresUnit_Pa);
        bmp.read(pres, temp, hum, tempUnit, presUnit);
        sprintf(buf1,"Temperature:%.2f%1s",temp,String(tempUnit == BME280::TempUnit_Celsius ? 'C' :'F'));
        sprintf(buf2,"Pressure: %.0f Pa",pres); 
        buf11   = String(buf1);
        buf22   = String(buf2);
        Serial.println(buf1);
        Serial.println(buf2);
        OLCD.setCursor(0, 40);
        OLCD.fillRect(0,40,128,16,SSD1306_BLACK);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 40);
        OLCD.println(buf1);
        OLCD.println(buf2);
        OLCD.display();
}
//------------ printTime
int printTime() {
        getLocalTime(&timeinfo);
        sprintf(buf3,"%04d/%02d/%02d %02d:%02d:%02d",timeinfo.tm_year+1900,timeinfo.tm_mon+1,timeinfo.tm_mday,timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
        Serial.println(buf3);
        OLCD.setCursor(0, 56);
        OLCD.fillRect(0,56,128,8,SSD1306_BLACK);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 56);
        OLCD.println(buf3);
        return timeinfo.tm_hour;
}
//------------ デバッグ用
void printLocalTime(){
        struct tm timeinfo;
        if (!getLocalTime(&timeinfo)) {
                Serial.println(F("Failed to obtain time"));
                return;
        }
        Serial.println(&timeinfo, "%Y %m %d %a %H:%M:%S");              //日本人にわかりやすい表記へ変更
}      
//------------ setup function
void setup(void) {
        pinMode(LED,OUTPUT);    digitalWrite(LED,LOW);                  //Onboard LED port Direction output
        pinMode(PO1,OUTPUT);    digitalWrite(PO1,LOW);
        pinMode(PO2,OUTPUT);    digitalWrite(PO2,LOW);
        pinMode(PO3,OUTPUT);    digitalWrite(PO3,LOW);
        //pinMode(PO4,OUTPUT);  digitalWrite(PO4,LOW);
        //ledcSetup(LEDC_CHANNEL_0,1000,8);                               //double ledcSetup(uint8_t chan, double freq, uint8_t bit_num);
        //ledcAttachPin(PO3, LEDC_CHANNEL_0);                             //void ledcAttachPin(uint8_t pin, uint8_t chan);
        ledcSetup(LEDC_CHANNEL_1,1000,8);
        ledcAttachPin(PO4, LEDC_CHANNEL_1);
        pinMode(PI1,INPUT);
        pinMode(PI2,INPUT);
        pinMode(PI3,INPUT);
        pinMode(PI4,INPUT);
        /*gDayBuf[24][3]  =       {
                {0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},
                {0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},{10.0,20.0,30.0},{0.0,0.0,0.0},
                {0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},
                {0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0}
        };*/
        //
        Serial.begin(115200);
        //SSD1306_SWITCHCAPVCC =内部で3.3Vから表示電圧を生成
        Wire.setClock(400000);                                          // クロック速度を最も速くなるように設定して、通信を改善します(高速モード)
        if(!OLCD.begin(SSD1306_SWITCHCAPVCC, OLCDAres)) {               // 128x64のスレーブアドレスオリジナル値は0x3Dでしたが手元の元は0x3Cでした。
                Serial.println(F("SSD1306 allocation failed"));
                for(;;);                                                // Don't proceed, loop forever
        }
        status = bmp.begin();
        if (!status) {
                Serial.println(F("Could not find BME280 sensor!"));
        }
        // bme.chipID(); // Deprecated. See chipModel().
        switch(bmp.chipModel()){
                case BME280::ChipModel_BME280:
                        Serial.println(F("Found BME280 sensor! Success."));
                        break;
                case BME280::ChipModel_BMP280:
                        Serial.println(F("Found BMP280 sensor! No Humidity available."));
                        break;
                default:
                        Serial.println(F("Found UNKNOWN sensor! Error!"));
        }
        // バッファをクリアします
        OLCD.clearDisplay();
        // アクセスポイント(無線LAN親機) + ステーション(無線LAN子機)
        WiFi.mode(WIFI_AP_STA);
        // まず既存のアクセスポイント(ネットワーク)に接続する
        //WiFi.config(ip,gateway,netmask);                              //固定IPにする場合に使用
        WiFi.begin(ssid, pass);
        // Wait for connection
        Serial.println(F(""));
        while (WiFi.status() != WL_CONNECTED) {                         //接続するまでループするが、すごく電流を喰うようでchipが熱くなる。要対策。
                delay(1000);
                Serial.print(F("."));
        }
        Serial.println(F(""));
        Serial.print(F("Connected to "));Serial.println(ssid);
        IPAddress myIP =WiFi.localIP();
        Serial.print(F("IP address: "));Serial.println(myIP);
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 0);
        OLCD.print(F("LoginID:"));OLCD.println(myID);
        OLCD.print(F("LoginPASS:"));OLCD.println(myPASS);
        OLCD.print(F("IP:"));OLCD.println(myIP);
        //OLCD.display();
        // SoftAPを開始する
        WiFi.softAPConfig(ap_ip,ap_gateway,netmask);
        WiFi.softAP(ap_ssid, ap_password);
        //IPAddress ap_ip = WiFi.softAPIP();
        OLCD.setTextSize(1);OLCD.setTextColor(SSD1306_WHITE);OLCD.setCursor(0, 40);
        OLCD.print(F("SSID:"));OLCD.println(ap_ssid);
        OLCD.print(F("PASS:"));OLCD.println(ap_password);
        OLCD.print(F("IP:"));OLCD.println(ap_ip);
        OLCD.display();
        Serial.print(F("SoftAPのIPアドレス(LAN側): "));Serial.println(myIP);
        //init and get the time
        configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);                       //void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3);
        printLocalTime();
        //ホスト名解決にmdnsを使用する
        if (!MDNS.begin(host)) {                                //https://esp32.local
                Serial.println(F("Error setting up MDNS responder!"));
                OLCD.setCursor(0, 32);OLCD.print(F("Error setting up MDNS responder!"));
                while (1) {
                        delay(1000);
                }
        }
        Serial.println(F("mDNS responder started"));
        //serverIndexに格納されているインデックスページを返す
        WS.on("/login", HTTP_GET, []() {
                WS.sendHeader("Connection", "close");
                WS.send(200, "text/html", loginIndex);
                OLCD.fillRect(0,32,128,8,SSD1306_BLACK);
                OLCD.setCursor(0, 32);
                OLCD.setTextColor(SSD1306_WHITE);
                OLCD.print(F("Connection"));
                OLCD.display();
        });
        WS.on("/serverIndex", HTTP_GET, []() {
                WS.sendHeader("Connection", "close");
                WS.send(200, "text/html", serverIndex);
                OLCD.fillRect(0,32,128,8,SSD1306_BLACK);
                OLCD.setCursor(0, 32);
                OLCD.setTextColor(SSD1306_WHITE);
                OLCD.print(F("UpdateReady"));
                OLCD.display();
        });
        WS.on("/", HTTP_GET, []() {
                WS.sendHeader("Connection", "close");
                gDataBuf[0]     =       analogRead(A0);
                gDataBuf[1]     =       temp;
                gDataBuf[2]     =       pres;
                WS.send(200, "text/html", SendHTML(gDataBuf));
                //OLCD.setCursor(0, 32);
                OLCD.fillRect(0,32,128,8,SSD1306_BLACK);
               OLCD.display();
        });
        //ファームウェアファイルのアップロードの処理
        WS.on("/update", HTTP_POST, []() {
                WS.sendHeader("Connection", "close");
                OLCD.setCursor(0, 32);
                OLCD.drawRect(0,32,128,8,SSD1306_BLACK);
                OLCD.display();
                WS.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
                ESP.restart();
        }, []() {
                HTTPUpload& upload = WS.upload();
                if (upload.status == UPLOAD_FILE_START) {
                        Serial.printf("Update: %s\n", upload.filename.c_str());
                        OLCD.setCursor(0, 32);
                        OLCD.drawRect(0,32,128,8,SSD1306_BLACK);
                        OLCD.setTextColor(SSD1306_WHITE);
                        OLCD.print(F("Updating..."));
                        OLCD.display();
                        if (!Update.begin(UPDATE_SIZE_UNKNOWN)) {       //利用可能な最大サイズから始める
                                Update.printError(Serial);
                        }
                } else if (upload.status == UPLOAD_FILE_WRITE) {
                        //ESPへのファームウェアのフラッシュ
                        if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
                                Update.printError(Serial);
                        }
                } else if (upload.status == UPLOAD_FILE_END) {
                        if (Update.end(true)) {                         //サイズを現在の進行状況に設定する場合はtrue
                                Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
                        } else {
                                Update.printError(Serial);
                        }
                }
        });
        //WS.on("/", handleRoot);                                       //This is display page
        //WS.on("/", handleGetData);                                    //To get update of ADC Value only
        WS.on("/GetData", handleGetData);                               //To get update of ADC Value only
        //WS.on("/readPres", handlePres);                               //To get update of ADC Value only
        //WS.on("/readTemp", handleTemp);                               //To get update of ADC Value only
        WS.onNotFound(handleNotFound);
        WS.begin();
}
//------------
int count       = 0;
int preSet      = -1;
void loop(void) {
        WS.handleClient();
        delay(100);
        printValues();
        int h = printTime();                                            //0-23
        //ループして遅延なく点滅する
        unsigned long currentMillis = millis();
        if(preSet != h){
                //1時間おきに時刻を確認し、データをメモリに確保
                preSet  = h;
                gDayBuf[h][0]   =       analogRead(A0);
                gDayBuf[h][1]   =       temp;
                gDayBuf[h][2]   =       pres;
                digitalWrite(PO3, true);
        }
        if (currentMillis - previousMillis >= interval) {
                //最後にLEDを点滅させた時間を保存します
                previousMillis = currentMillis;
                //LEDがオフの場合はオンにし、逆の場合も同様です。
                ledState = not(ledState);
                //変数のledStateでLEDを設定します。
                digitalWrite(LED, ledState);
                switch(count){
                        case 0:
                                digitalWrite(PO1, true);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 1:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, true);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                        case 2:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, true);
                                //digitalWrite(PO4, false);
                                break;
                        case 3:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, true);
                                break;
                        default:
                                digitalWrite(PO1, false);
                                digitalWrite(PO2, false);
                                //digitalWrite(PO3, false);
                                //digitalWrite(PO4, false);
                                break;
                }
                //ledcWrite(LEDC_CHANNEL_0, brightness);
                ledcWrite(LEDC_CHANNEL_1, 255 - brightness);
                brightness = brightness + fadeAmount;
                if (brightness <= 0){
                        brightness = 1;
                        fadeAmount = abs(fadeAmount);
                        digitalWrite(PO3, false);
                }else if(brightness >= 255){
                        brightness = 254;
                        fadeAmount = -abs(fadeAmount);
                }
                count = (count + 1) % 5;
                Serial.println(count);
        }
}

照度センサTSL2561及び空気品質センサCCS-811を入手しました。屋内環境モニタに組み込んでみました。


環境モニタユニットが市場に出てきたことを確認しました。ラトックシステムズのWi-Fi 環境センサー RS-WFEVS1です。
https://www.ratocsystems.com/products/subpage/wfevs1.html

このユニットに採用されているCO2センサーはセンシリオン社のPA(光音響感知原理)方式センサSCD40が搭載されているようです。

このセンサチップが載ったモジュールを見つけられていませんので、RS-WFEVS1の情報から推測します。

7つの環境情報(CO2、PM1/2.5/4/10、VOC、温度、湿度、気圧、UV)を取得し、専用のWebServerのデータ転送、そのデータをiPhoneやAndroidタブレットでモニタするという商品の様です。セッティング等専用アプリケーションで行い、ローカルな環境で使う事は出来ない様で必ずインターネットに接続した状態で使うみたいです。本体価格は2万円程度ですが、毎月の使用料が必要になるかどうかが気になるところです。少なくともデータはLocalに置く術は無いようです。

SCD40がどの程度の安定度があるのか?CCS-811と同じなのか気になるところです。少なくとも原理は異なるようです。

データは 1分毎に計測し、5分分のデータが貯まったところでクラウドへの送信するようです。
温度データの測定範囲が気になりますね。5 〜 60℃となっています。室内でも0度以下になるケースは有るように思うのですが。


戯言(nonsense)に戻る


問い合わせ頁の表示


免責事項

本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。

本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。

商標・登録商標

Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。