IoT圧力センサー:MKR GSM + Arduino Cloud + Google Sheets
コンポーネントと消耗品
> |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
アプリとオンラインサービス
> |
| |||
| ||||
| ||||
|
このプロジェクトについて
目的
このプロジェクトの目的は、セルラーデータを使用して遠隔地の産業機器への圧力を監視するための低コストデバイスのプロトタイプを作成することでした。
プロジェクトウォークスルー
以下は、圧力トランスデューサからGoogleスプレッドシートに添付されたスクリプトによって生成された電子メール通知へのデータの流れに続くプロジェクトのステップバイステップのウォークスルーです。
ステップ1:Unoへの圧力トランスデューサー
圧力トランスデューサは、圧力をアナログ電気信号に変換します。
Arduino Unoは、圧力トランスデューサからのアナログ信号を圧力(psi)に変換します。
ステップ2:シリアル経由でUnoからMKR GSM1400へ
シリアル経由で2つのArduinoデバイス間で通信する場合:
- デバイス#1のRXをデバイス#2のTXに接続します
- デバイス#1のTXをデバイス#2のRXに接続します
- デバイスには共通のアースが必要です
データ送信頻度(宇野からMKR GSM 1400)
- 通常: 30分ごとに(transmitFrequency)、UnoはデータをMKR GSM 1400にシリアル印刷し、MKR GSM1400はデータをクラウドに送信します。
- 高/低トリガー: 圧力が40psi(highTrigger)または20 psi(lowTrigger)を超え、2分(dtLastTriggerLimit)を超えてそこにとどまる場合、UnoはデータをMKR GSM 1400にシリアル印刷し、クラウドにデータを送信します。
- DEMAND POLL: UnoのピンA1がHighに押されると、データをMKR GSM 1400にシリアル印刷し、MKR GSM1400がデータをクラウドに送信します。注:ピンA1は、宇野のスケッチでは「buttonPin」という名前です。 UnoのピンA1を高く押す方法は2つあります。 (1)ブレッドボードに押しボタンがあります。 (2)MKR GSM 1400のピンA3がハイの場合、ピンA1をハイにプッシュします。ピンA3はArduinoクラウドの入力によって制御されるため、定期的にスケジュールされたデータ送信を待たずに、いつでもリモートで現在の圧力を取得できます。
メモ
- Unoのスケッチを変更して、温度、湿度、バッテリー電圧などの複数の入力を、現在のバージョンの圧力に加えて、高い設定値と低い設定値で監視できるようにすることができます。
- 圧力トランスデューサからのアナログ信号を圧力(psi)に変換するために使用されるコードは、次のYouTubeビデオに記載されている手順に基づいています:https://www.youtube.com/watch?v =AB7zgnfkEi4 >
- 「シリアル入力の基本」をカバーするArduinoフォーラムの次の投稿は、シリアルデータを使用して1つのデバイスから別のデバイスに通信するコードを作成する際に非常に役立ちました:https://forum.arduino.cc/index。 php?topic =288234.0
このプロジェクトでArduinoUnoに使用されているコードには、重要な情報を説明するコメントが添付されています。
ファイル名: "InstrumentReader"
ステップ3:セルラー経由でArduinoクラウドにMKR GSM1400を送信
MKR GSM 1400は、Arduino Unoからのシリアルデータを処理し、セルラーデータを使用してデータをArduinoクラウドに送信します。
MKR GSM 1400のコードでは、Serial.readではなくSerial1.readが表示されることに注意してください。 Arduino Webサイトの参考資料には、適切な説明があります。下の画像の表は、MKRボードのTX / RXピンがSerial1を介してアクセスされることを示しています。
https://www.arduino.cc/reference/en/language/functions/communication/serial/
Arduinoクラウド
このプロジェクトは、Arduinoクラウドで2つの変数を使用してセットアップされています。下の画像は、これらの変数がArduinoクラウドのダッシュボードにどのように表示されるかを示しています。
「dataStringCloud」という名前の最初の変数は、事実上、デバイスからのすべてのデータのパッケージです。このアプローチは、Googleスプレッドシートのデータ処理を簡素化するために、値ごとに1つの変数ではなく採用されました。個々の変数名では、同じままの値と更新されなかった値の違いを区別するのは困難でした。このパッケージのデータはGoogleスプレッドシートで解析されます。
「pinCloud」という名前の2番目の変数は、ArduinoクラウドからMKR GSM1400を制御するために使用されます。スケッチには、pinCloudの値に基づいてアクションを制御するスイッチ機能があります。 pinCloud =1の場合、ピンA1がHighに押され、ボード上のLEDが点灯します。 pinCloud =2の場合、ピンA3がハイにプッシュされ、ArduinoUnoが上記のように現在のデータを送信します。
このプロジェクトでArduinoMKR GSM 1400に使用されるコードには、重要な情報を説明するコメントが添付されています。
ファイル名: "CommunicationsDevice"
ステップ4:Webhook経由でArduinoクラウドからGoogleスプレッドシートへ
データは、Webhookを使用してArduinoクラウドからGoogleスプレッドシートに転送されます。
Webhookのコアは、Googleスプレッドシートファイルのスクリプトで記述されたdoPost関数です。
Webhookの設定方法の概要は次のとおりです。 プロセスはGoogleスプレッドシートで開始されることに注意してください。最後までArduinoクラウドに到達しません。 AからBに移動するには、Bから開始します。
- 新しいGoogleスプレッドシートファイルを作成する
- ツールバーの[ツール]をクリックし、ドロップダウンで[スクリプトエディター]を選択します
- doPost関数を使用してコードを記述します (このプロジェクトに添付されているGoogleSheetsScript.jsを参照してください)
- ツールバーの[公開]をクリックし、ドロップダウンで[ウェブアプリとしてデプロイ...]を選択します
- ダイアログボックスには3つのフィールドが表示されます。
- (1)プロジェクトバージョン: 常にドロップダウンを使用して「新規」を選択してください。最初の更新後、デフォルトで現在のバージョンの#になります。ドロップダウンを使用して[新規]を選択しない場合、変更は有効になりません。
- (2)アプリを次のように実行します: 「私([email protected])」
- (3)アプリにアクセスできるユーザー: 「誰でも、匿名でも」
- 3つのフィールドの値を確認した後、デプロイを押します
- 「現在のWebアプリのURL」を含む2番目のダイアログボックスが表示されます。これは、ArduinoクラウドのWebhookタブにコピーして貼り付けるURLです。注目に値するのは、このURLは、プロジェクトのバージョンに関係なく同じままです。
- [OK]をクリックすると、完了です!
このプロジェクトで使用されているJavaScriptコードの大部分は、「Arduino IoT Cloud GoogleSheetsIntegration」という名前の別のプロジェクトで使用されているコードをモデルにしています。プロジェクトへのリンクは以下のとおりです。ぜひチェックしてみてください。
https://create.arduino.cc/projecthub/Arduino_Genuino/arduino-iot-cloud-google-sheets-integration-71b6bc?ref=part&ref_id=64347&offset=9
ステップ5:Googleスプレッドシートを使用してデータを解析する
Googleスプレッドシートを使用して、dataStringCloudから個々の値を解析し、Arduinoクラウドから転送された一意のレコードを表示します
以下のリンクは、デバイスの最近のテストに使用されたGoogleスプレッドシートファイルへのリンクです。このファイルのセルは、各シートの凡例に示されているように、それらがどのように入力されたかに基づいて色分けされています。
https://docs.google.com/spreadsheets/d/1XwCir2Llw8RvGPGgZI3Yk6U5a3LeIfUACNuO1Gr_LFQ/edit?usp=sharing
ステップ6:Googleスプレッドシートを使用して通知を送信する
上記の手順4で参照したこのプロジェクト(GoogleSheetsScript.js)のJavaScriptファイルには2つの関数があることに気付いたかもしれません。
- doPost関数-ArduinoCloudWebhookからデータを送信します。 Arduinoクラウドに新しいデータがあるときに実行されます。
- sendEmail関数-プロジェクトのGoogleスプレッドシートファイルの「データ」という名前のシートから抽出された値に基づいてメールを送信します。トリガー設定の設定に基づいて、1分ごとに実行されます。
sendEmail関数のトリガーを設定する手順
- Googleスプレッドシートファイルを開く
- ツールバーの[ツール]をクリックします
- ドロップダウンで[スクリプトエディタ]を選択します
- スクリプトエディタウィンドウから次の手順に進みます:
- ツールバーの[編集]をクリックします
- ドロップダウンで[現在のプロジェクトのトリガー]を選択します
- G Suite DeveloperHubウィンドウから次の手順に進みます。
- ウィンドウの右下隅にある[トリガーの追加]を選択します
- 表示されるダイアログボックスで、sendEmail関数を実行するための選択を行います。
- 注:時間駆動ベースでトリガーを実行すると、デバイスの更新が停止したときに電子メール通知を生成できます。
バッテリー寿命
〜24時間
これは、ディスプレイをオフにするか削除することで最適化できます。もう1つのオプションは、DHT22やDS3231などの必須ではないセンサーを削除することです。
データ使用量
〜0.7メガバイト/日
これは、データ送信のサイズまたは頻度を減らすことによって最適化できます。例:縮小サイズを小さくするには、圧力、温度、湿度、および時間ではなく、圧力のみを送信します。頻度を減らすために、30分ごとではなく、1時間ごとに更新するだけです。
プロジェクトコスト
合計=$ 241
- Arduino MKR GSM 1400($ 70)
- Arduino Uno($ 22)
- 2 x 3.7V LiPoバッテリー($ 30)
- 2 x LEDディスプレイ($ 29)
- 耐候性プラスチックボックス($ 22)
- 圧力センサー($ 19)
- 温度/湿度センサー-DHT22($ 10)
- RTCモジュール-DS3231($ 5)
- 電圧ステップアップコンバーター($ 5)
- ロジックレベルコンバーター($ 4)
- その他-LED、抵抗器、配線など($ 25)
ハードウェア/ツール
このプロジェクトで使用されたすべてのハードウェアとツールは、以下から購入しました。
- Arduinoオンラインストア(https://store.arduino.cc/)
- Adafruit(https://www.adafruit.com/)
- アマゾン(https://www.amazon.com/)
- ハーバーフレイト
- ホームデポ
結論として...
このプロジェクトのレビューに時間を割いていただきありがとうございます。
ありとあらゆる質問/フィードバック/コメント/提案は大歓迎です/ありがたいです。
コード
- InstrumentReader-ArduinoUnoのスケッチ
- GoogleSheetsScript.js
InstrumentReader-ArduinoUnoのスケッチ Arduino
// Sketch 1 of 2 // Arduino Uno //データはこのデバイスによって収集され、シリアル経由でMKR1400に送信されます// DHT22センサーには2つのライブラリが必要ですが、コードで呼び出されるのは1つだけです。 //(1):DHTセンサーライブラリ:https://github.com/adafruit/DHT-sensor-library //(2):Adafruit統合センサーライブラリ:https://github.com/adafruit/Adafruit_Sensor #include#include #include // RTC module#include // DHT22#include "U8glib.h" // Velleman 128 x 64 OLEDSPIディスプレイ//注:別の標準U8glibライブラリをこの表示に使用しようとすると、機能しませんでした。メーカーが推奨するライブラリを使用した場合は機能しました。 //ライブラリ:https://www.velleman.eu/support/downloads/?code =VMA437 //構文:https://github.com/olikraus/u8glib/wiki/userreference&https://github.com/ olikraus / u8glib / wiki / thelloworld //#include // Arduino UnoRTClibRTCのイーサネットシールドのSDカードに保存するオプション; #define DHTPIN 11 // DHTセンサーに接続されたデジタルピン#defineDHTTYPE DHT22 / / DHT 22(AM2302)、AM2321DHT dht(DHTPIN、DHTTYPE); U8GLIB_SH1106_128X64 u8g(3、4、6、7); //(CLK / SCK:3、MOSI:4、CS:6、DC(A0):7)// u8g(sck、mosi、cs、a0 [、reset])int y_pos =0; //グローバル変数// const int chipSelect =10; // ArduinoUnoのイーサネットシールドのSDカードに保存するオプション// floatfileSizeSD =0.0; // Arduino Unoint i =0のイーサネットシールドでSDカードに保存するオプション; // Unoによって取得された読み取り値の数をカウントします(プログラムのループ数と同じ)const int ledPin =9; //送信インジケーター(データ送信が発生すると点滅)const int ledPin2 =8; //送信インジケーターを押します(ブレッドボードの手動ボタンで高く押すか、クラウドからアクティブ化されたMKR 1400から出力します)const int buttonPin =A1; int buttonState =0; inttransmitFrequency =30; // 2番目のデバイスにデータを送信するためのシリアル印刷dataStringの頻度(分)String pTransmitDateTime =""; inttransmitCounter =0; int pTransmitMinute =0; int ptriggerTransmitAlertIndicator; float cRuntimeAtTriggerStart =0.0; float dtLastTrigger =0.0; int triggerCounter =0.0; int triggerTransmitAlertCounter =0; //トリガーを制御するための入力変数floatlowTrigger =20.0; float highTrigger =40.0; float dtLastTriggerLimit =2.0; //この期間条件が満たされると、アラートが生成されますvoid setup(void){Serial.begin(9600); Wire.begin(); dht.begin(); pinMode(ledPin、OUTPUT); pinMode(ledPin2、OUTPUT); pinMode(buttonPin、INPUT); u8g.setRot180(); //必要に応じて画面を反転します(回転させるためにこの行のコメントを追加/削除します)// ArduinoUno用のEthernetShieldのSDカードに保存するオプション//Serial.print( "Initializing SD card ..."); // if(!SD.begin(chipSelect))//カードが存在し、初期化できるかどうかを確認します// {// Serial.println( "カードが失敗したか、存在しません"); // while(1); //これ以上何もしない//} // Serial.println( "card initialize。");} void loop(void){delay(5000); DateTime now =RTC.now(); float cRuntime =millis()/ 60000; float p =getPressure(); // Serial.println(p); buttonState =digitalRead(buttonPin); // Serial.print( "Button:"); // Serial.println(buttonState); if(buttonState ==1){digitalWrite(ledPin2、HIGH); delay(30000); // Uno:buttonPinがMKR1400:pingPinによってHIGHにプッシュされた場合に、MKR1400がデータを受信する準備ができるようにするための遅延} else {digitalWrite(ledPin2、LOW); } float h =dht.readHumidity(); float t =dht.readTemperature(true); // t =dht.readTemperature(true)->摂氏Fの場合の温度&t =dht.readTemperature()->摂氏の場合の温度inttransmitIndicator =0; if(now.minute()%transmitFrequency ==0 &&now.minute()!=pTransmitMinute){transmitIndicator =1; pTransmitMinute =now.minute(); pTransmitDateTime =String(now.hour())+ String( ":")+ String(now.minute())+ String( ":")+ String(now.second()); } int triggerStatus =0; if(p <=lowTrigger || p> =highTrigger){//注:このifステートメントの条件で参照される変数は、高い設定値と低い設定値に対して評価されます//評価される変数をすばやく変更できます-これは変数が指定されている場所のみtriggerStatus =1; triggerCounter ++; } else {triggerCounter =0; } if(triggerStatus ==1 &&triggerCounter ==1){cRuntimeAtTriggerStart =cRuntime; } dtLastTrigger =cRuntime-cRuntimeAtTriggerStart; int triggerTransmitAlertIndicator =0; if((dtLastTrigger> dtLastTriggerLimit)&&triggerStatus ==1){triggerTransmitAlertIndicator =1; triggerTransmitAlertCounter ++; } else {triggerTransmitAlertCounter =0; } if(triggerTransmitAlertCounter> 0 &&triggerTransmitAlertCounter%10 ==0){flashLED(2,500); } int triggerPushTransmitAlertIndicator =0; if((triggerTransmitAlertIndicator ==1 &&triggerTransmitAlertCounter ==1)|| ptriggerTransmitAlertIndicator!=triggerTransmitAlertIndicator)// if(TriggerStatusはAlert&Count =1の指定された最小時間に存在し、これが指定された最小時間を超えた最初のループであることを意味しますtime //またはtriggerAlertステータスが変更されます-これにより、TriggerStatusが0に戻り、トリガー条件が満たされない場合にプッシュアラートが生成されます。){triggerPushTransmitAlertIndicator =1; flashLED(5,500); delay(5000); } ptriggerTransmitAlertIndicator =triggerTransmitAlertIndicator; //現在のインジケーターが前のインジケーターに保存されます。次のループで、ここで転送された値は、新しい値に基づいて生成された値と比較されます。 //文字列を作成しますStringdataString ="";文字列cDateTime ="";文字列cHumTemp ="";文字列cP =""; dataString + ="<" + String(i)+ "、" + String(triggerTransmitAlertIndicator)+ "、" + String(dtLastTrigger、0)+ "、" + String(buttonState)+ "、" + String(now.month ())+ "、" + String(now.day())+ "、" + String(now.year())+ "、" + String(now.hour())+ "、" + String(now .minute())+ "、" + String(now.second())+ "、" + String(h)+ "、" + String(t)+ "、" + String(p)+ ">"; cDateTime + =String(now.month())+ "/" + String(now.day())+ "/" + String(now.year())+ "" + String(now.hour())+ ":" + String(now.minute())+ ":" + String(now.second()); cHumTemp + ="H:" + String(h)+ "%T:" + String(t)+ "degF"; cP + ="P:" + String(p)+ "psi"; if(transmitIndicator ==1 || triggerPushTransmitAlertIndicator ==1 || buttonState ==1){char dataArray [100]; dataString.toCharArray(dataArray、100); Serial.println(dataArray); flashLED(10,500); transmitCounter ++; } // Serial.print( "T:"); // Serial.println(triggerStatus); delay(100); //メッセージ全体が到着するまで少し待ちます// picture loop u8g.firstPage(); do {draw(cDateTime、cHumTemp、cP、i、transmitCounter、now.minute()、transmitFrequency、pTransmitMinute); } while(u8g.nextPage()); delay(1000); // writeToSD(dataString); // Arduino Uno i ++のイーサネットシールドでSDカードに保存するオプション;} void draw(String DcDateTime、String DcHumTemp、String DcP、int Di、int DtransmitCounter、int DnowMinute、int DtransmitFrequency、int DpTransmitMinute){u8g.begin(); u8g.setFont(u8g_font_5x7); // u8g_font_micro // u8g_font_5x7 // u8g_font_5x8 // u8g_font_6x10 u8g.setFontPosTop(); u8g.setPrintPos(0,0); u8g.print(DcDateTime); u8g.setPrintPos(0,8); u8g.print(2); u8g.setPrintPos(10,8); u8g.print(DcHumTemp); u8g.setPrintPos(0,16); u8g.print( "3#:"); u8g.setPrintPos(30,16); u8g.print(Di); u8g.setPrintPos(50,16); u8g.print(DcP); u8g.setPrintPos(0,24); u8g.print( "4 #t:"); u8g.setPrintPos(30,24); u8g.print(DtransmitCounter); u8g.setPrintPos(50,24); u8g.print( "tFreq:"); u8g.setPrintPos(83,24); u8g.print(DtransmitFrequency); u8g.setPrintPos(0,32); u8g.print(5); u8g.setPrintPos(10,32); u8g.print( "nowMinute:"); u8g.setPrintPos(70,32); u8g.print(DnowMinute); u8g.setPrintPos(0,40); u8g.print(6); u8g.setPrintPos(10,40); u8g.print( "pTransmitMinute:"); u8g.setPrintPos(95,40); u8g.print(DpTransmitMinute); u8g.setPrintPos(0,48); u8g.print(7); u8g.setPrintPos(10,48); u8g.print( "残り:"); u8g.setPrintPos(70,48); u8g.print(DnowMinute%DtransmitFrequency);} float getPressure(){int SensorVal =analogRead(A2); // Serial.print( "Sensor Value:"); // Serial.print(sensorVal);フロート電圧=(sensorVal * 5.0)/1023.0; // Serial.print( "ボルト:"); // Serial.print(電圧); //圧力=0の場合、アナログ入力=100 //アナログ入力から電圧への変換:アナログ入力=100->電圧=100 *(5/1023)=0.4889 float m =((150-0)/(4.5- 0.4889)); float b =150-(m * 4.5); // Serial.print( "m ="); // Serial.print(m); // Serial.print( "b ="); // Serial.print( b); float pressure_psi =((m * Voltage)+ b); // Serial.print( "Pressure ="); // Serial.print(pressure_psi); // Serial.println( "psi"); // delay(200); return pressure_psi;} void flashLED(int num、int t){for(int z =1; z <=num; z ++){digitalWrite(ledPin、HIGH); delay(t); digitalWrite(ledPin、LOW); delay(t); }} // ArduinoUno用イーサネットシールドのSDカードに保存するオプション// void writeToSD(String dataToWrite)// {////ファイルを開きます。一度に開くことができるファイルは1つだけであることに注意してください。////したがって、別のファイルを開く前にこのファイルを閉じる必要があります。 //ファイルdataFile =SD.open( "datalog4.txt"、FILE_WRITE); // fileSizeSD =dataFile.size(); //ファイルサイズをバイト単位で返します// fileSizeSD =fileSizeSD / 1000000; //バイトをMBに変換します。 1 MB =1e6バイト////ファイルが使用可能な場合は、次のように書き込みます。//if(dataFile)// {// dataFile.println(dataToWrite); // dataFile.close(); // //シリアルポートにも出力します:// // Serial.println(dataToWrite); //} // //ファイルが開いていない場合は、エラーをポップアップします:// else // {// Serial.println( "error open datalog1.txt"); //} //}
GoogleSheetsScript.js JavaScript
//このコードには2つの関数があります// doPost関数-ArduinoCloudWebhookからデータを送信します。 Arduinoクラウドに新しいデータがある場合に実行されます。//sendEmail関数-プロジェクトのGoogleスプレッドシートファイルの「データ」という名前のシートから抽出された値に基づいてメールを送信します。トリガー設定の設定に基づいて、1分ごとに実行されます。手順については、プロジェクトハブの投稿のsendEmail関数の部分を参照してください。//このコードの大部分(sendEmail関数を除く)は、Arduino ProjectHubの次のプロジェクトをモデルにしています//https://create.arduino。 cc / projecthub / Arduino_Genuino / arduino-iot-cloud-google-sheets-integration-71b6bc?f =1 //これは、ProjectHubで上記のプロジェクトに使用されたGoogleスクリプトを含むGitHubリポジトリへのリンクです。 //このリンクはProjectHubのプロジェクトの説明からコピーされました。//https://github.com/arduino/arduino-iot-google-sheet-script/blob/master/Code.gs//アクティブになりますspreasheetvar ss =SpreadsheetApp.getActiveSpreadsheet(); // RawDataという名前のシートを取得するsheet =ss.getSheetByName( "RawData"); var sd =ss.getSheetByName( "Data"); var sref =ss.getSheetByName( "References"); var MAX_ROWS =1440; //表示するデータ行の最大数// 3600s / cloud_int(30s)* num_ore(12h)=(60 * 60 * 12)/ 30 =(3600 * 12)/ 30 =30秒の更新で12時間に1440回の読み取り間隔//(60 * 24)/ 15 =15分の更新間隔で24時間に96回の測定値// 15日* 96回の測定値/日=1440回の測定値// 90日* 96回の測定値/日=8640回の測定値// 365日* 96読み取り/日=35040 readingsvar HEADER_ROW =1; // headervarの行インデックスTIMESTAMP_COL =1; //タイムスタンプの列インデックスcolumnfunctiondoPost(e){var cloudData =JSON.parse(e.postData.contents); //これはIoTCloud console.log(cloudData);からのすべての情報を含むjsonオブジェクトです。 // var webhook_id =cloudData.webhook_id; //これら3つを実際には使用していません// var device_id =cloudData.device_id; // var things_id =cloudData.thing_id; var values =cloudData.values; //これはjsonオブジェクトの配列ですconsole.log(values); //値の配列から名前と値を格納します//各入力プロパティには次のものがあります://列名になる名前//列ヘッダーの下の行に書き込まれる値var incLength =values.length; var incNames =[]; var incValues =[]; for(var i =0; i2018){//到着するすべてのメッセージを破棄します'late' if(sheet.getRange(HEADER_ROW + 1、1).getValue()!=''){// HEADER_ROW + 1 =行#2と列#1->これは最新のタイムスタンプの場所ですシート内//最新のタイムスタンプが空白でない場合=( '')、現在の時刻を受信データのタイムスタンプと比較します//最新のタイムスタンプが空白の場合、スクリプトが初めてである可能性があります実行されます。 //この場合、このifステートメントをスキップして、列ヘッダーとデータへの書き込みに進みます。 //データが遅れて到着するかどうかは関係ありません(現在の時刻と着信データのタイムスタンプ)。 var now =new Date(); //ここでvarCOMM_TIME =120; //注:より多くのメッセージを通過できるように120に変更され、以前は5秒に設定され、正常に機能しました//クラウドとアプリ間の通信時間の大まかな過大評価if(now.getTime()-date.getTime()> COMM_TIME * 1000){//現在の時刻とタイムスタンプの差が5秒を超える場合は、データを破棄します。 //このIfステートメントの条件がtrueと評価されると、returnステートメントのために関数が停止します。戻る; // "returnステートメントは関数の実行を停止し、その関数から値を返します。" }} //このセクションは、着信プロパティの名前に基づいてヘッダー行に値を書き込みます//つまり、このセクションは列名を作成します//ヘッダー行と最初のセルに名前を割り当てます列=タイムスタンプsheet.getRange(HEADER_ROW、1).setValue( 'timestamp'); for(var i =0; i 2です//列1はタイムスタンプ列です。 if(lastCol ==1){//タイムスタンプ列であるlastCol ==1の後の列で始まる列名を書き込みます// incNamesはすべての着信プロパティの名前を含む配列です// if lastCol ==1 、incNames配列のi番目の位置から列#2のヘッダー行に値を書き込みます=lastCol + 1 sheet.getRange(HEADER_ROW、lastCol + 1).setValue(incNames [i]); } else {// lastCol!=1の場合に評価されます//名前がすでにヘッダーにあるかどうかを確認しますvarfound =0; for(var col =2; col <=lastCol; col ++){//列2から開始し、lastColまでのすべての列を反復処理して、囲まれたifステートメントを評価しますif(sheet.getRange(HEADER_ROW、col).getValue( )==incNames [i]){//このIfステートメントの条件はヘッダーの行と列の値を比較します#=colは着信プロパティ名の配列の 'i番目の値と比較します//このifステートメントは評価されますfor each iteration of the for loop that it is enclosed in. // The condition is evaluated for all of the columns from column #2 to the last column. // It is checking to see if the 'i'th value in the incNames array exists in any of the columns in the header row. // If the 'i'th value in the incNames array finds a match to any of the values in the header row, set found =1 &exit the for loop with the break statment. found =1;壊す; // "The break statement breaks the loop and continues executing the code after the loop" } // close if statement evaluated for each iteration of the for loop that it is enclosed in. } // close for loop to check the 'i'th value in the incNames array to the values in the header row. if (found ==0) { // This If statemnt will be evaluated after the preceeding for loop has completed. // If found ==0 it means that the 'i'th value in the incNames array did not match any of the existing values in the header row. // If found ==0, write the 'i'th value in the incNames array to the column after the last column. // If new properties are added to the incoming data over time, the existing columns will not be impacted. The new property will be added to the column after the last column. sheet.getRange(HEADER_ROW, lastCol+1).setValue(incNames[i]); } // close if statement } // close else, since this is the end of the code block inside the main for loop, the next i will be evaluated up to i =incLength // The block of code inside this for loop is evaluated for each value at location i in the incNames array. // The values of i range from 0 to incLength (the number of values in the names array) // In JavaScript the index in arrays starts at 0. In other words, the 1st value in the array is at location =0. } // close main for loop used to write column names (assigning values from names array to header row) // redefine last coloumn and last row since new names could have been added var lastCol =sheet.getLastColumn(); var lastRow =sheet.getLastRow(); // delete last row to maintain constant the total number of rows if (lastRow> MAX_ROWS + HEADER_ROW - 1) { sheet.deleteRow(lastRow); } // insert new row after deleting the last one sheet.insertRowAfter(HEADER_ROW); // reset style of the new row, otherwise it will inherit the style of the header row var range =sheet.getRange('A2:Z2'); //range.setBackground('#ffffff'); range.setFontColor('#000000'); range.setFontSize(10); range.setFontWeight('normal'); // write the timestamp sheet.getRange(HEADER_ROW+1, TIMESTAMP_COL).setValue(date).setNumberFormat("yyyy-MM-dd HH:mm:ss"); // write values in the respective columns for (var col =1+TIMESTAMP_COL; col <=lastCol; col++) { // for loop to assign the value from incValues to the approrpriate column // This block of code was replaced by an if statement checking for blank values after the incoming data is populated. // Copy previous values // This is to avoid empty cells if not all properties are updated at the same time // sheet.getRange(HEADER_ROW+1, col).setValue(sheet.getRange(HEADER_ROW+2, col).getValue()); for (var i =0; i 2018), used to eliminate dupicate messages from the Arduino Cloud} // close doPost functionfunction sendEmail (){ var emailAddress =sd.getRange("V3").getValue(); var lastPressure =sd.getRange("K3").getValue(); // pressure at last update var lastUpdate =sd.getRange("A3").getValue(); // datetime of last update var ssLink ="https://docs.google.com/spreadsheets/d/1XwCir2Llw8RvGPGgZI3Yk6U5a3LeIfUACNuO1Gr_LFQ/edit#gid=1123486497"; var triggerAlertCount =sd.getRange("L6").getValue(); var triggerMonitor =sd.getRange("M3").getValue(); var dtLastStatusChange =sd.getRange("P3").getValue(); var dtLastDeviceUpdate1 =sd.getRange("S3").getValue(); var dtLastDeviceUpdate2 =sd.getRange("S4").getValue(); var emailSentNoUpdate =sd.getRange("T3").getValue(); var emailSentStartedReading =sd.getRange("U3").getValue(); var message ="Last Device Update:" + "\n" + lastUpdate + "\n\n" + " Last Pressure:" + "\n\t" + lastPressure.toFixed(2) + " psi" + "\n\n" + " Link to Spreadsheet:" + "\n\t" + ssLink; if(triggerMonitor ==0){ sd.getRange("L5").setValue(0); sd.getRange("L6").setValue(0); } if(triggerMonitor ==-1 &&triggerAlertCount <=4){ sd.getRange("L3").setValue(lastUpdate); // emailSent sd.getRange("L5").setValue(-1); sd.getRange("L6").setValue(triggerAlertCount + 1); var subject ="Status Change Alert - Outside Setpoints"; MailApp.sendEmail(emailAddress, subject, message); } if(triggerMonitor ==1 &&triggerAlertCount <=4){ sd.getRange("L3").setValue(lastUpdate); // emailSent sd.getRange("L5").setValue(1); sd.getRange("L6").setValue(triggerAlertCount + 1); var subject ="Status Change Alert - Normal"; MailApp.sendEmail(emailAddress, subject, message); } if(emailSentNoUpdate ==0 &&dtLastDeviceUpdate1> 60 &&dtLastDeviceUpdate2> 60){ sd.getRange("T3").setValue(1); // emailSentNoUpdate sd.getRange("U3").setValue(0); // emailSentStartedReading sd.getRange("T4").setValue(now.getTime()); // emailSentNoUpdate var subject ="Alert - Over 60 minutes Since Last Device Update"; MailApp.sendEmail(emailAddress, subject, message); } if(emailSentNoUpdate ==1 &&dtLastDeviceUpdate1 <60){ // removed the following from the condition:&&dtLastDeviceUpdate2> 60 sd.getRange("T3").setValue(0); // emailSentNoUpdate sd.getRange("U3").setValue(1); // emailSentStartedReading sd.getRange("U4").setValue(now.getTime()); // emailSentStartedReading var subject ="Alert - Device Started Updating"; MailApp.sendEmail(emailAddress, subject, message); }}
CommunicationsDevice - Sketch for MKR 1400
回路図
製造プロセス
- ADLINKはGoogleCloudと提携して、IoT対応ソリューションを提供します
- 基本的なIoT– RaspberryPIHDC2010の方法
- BMP180I2Cデジタル気圧センサー
- Googleスプレッドシートを使用したPython / MicroPythonセンサーロガー
- Raspberry Pi2上のWindows10 IoT Core –Adafruitセンサーデータ
- Windows 10 IoTCoreおよびSHT15
- UnifiedWater v1
- IOT-ESP8266、Arduino、超音波センサーを使用したスマートジャー
- Arduinoクラウドセンサータワー
- Arduino Apple Watch
- AlexaとArduinoIoTCloudを使用したテレビのフルコントロール