산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

IoT 압력 센서:MKR GSM + Arduino Cloud + Google Sheets

구성품 및 소모품

Arduino UNO
× 1
Arduino MKR GSM 1400
× 1
압력 변환기(0-150psi)
× 1
Adafruit DHT22 온도 및 습도 센서
× 1
DS3231 RTC 모듈
× 1
Adafruit 논리 레벨 변환기 - BSS138 - 4채널 I2C 안전 양방향
× 1
Adafruit 3.7V LiPo 배터리(2000mAh 이상)
× 2
전압 승압 변환기
× 1
다양한 LED 및 저항기가 포함된 기본 스타터 키트
× 1
브레드보드(일반)
× 1

앱 및 온라인 서비스

Google 스프레드시트
Arduino IoT 클라우드
Arduino 웹 편집기
Arduino IDE

이 프로젝트 정보

목표

이 프로젝트의 목적은 셀룰러 데이터를 사용하여 원격 위치에 있는 산업 장비의 압력을 모니터링하는 저가 장치의 프로토타입을 만드는 것이었습니다.

프로젝트 둘러보기

다음은 압력 변환기에서 Google 시트에 첨부된 스크립트에 의해 생성된 이메일 알림으로의 데이터 흐름에 따라 프로젝트의 단계별 안내입니다.

1단계:Uno로의 압력 변환기

압력 변환기는 압력을 아날로그 전기 신호로 변환합니다.

Arduino Uno는 압력 변환기의 아날로그 신호를 압력(psi)으로 변환합니다.

2단계:직렬을 통해 MKR GSM 1400으로 Uno

직렬을 통해 두 개의 Arduino 장치 간에 통신할 때:

<울>
  • 장치 #1의 RX를 장치 #2의 TX에 연결
  • <울>
  • 장치 #1의 TX를 장치 #2의 RX에 연결
  • <울>
  • 기기에는 공통점이 있어야 합니다.
  • 데이터 전송 주파수(Uno to MKR GSM 1400)

    <울>
  • 정상: 30분마다(transmitFrequency) Uno는 데이터를 클라우드로 전송할 MKR GSM 1400으로 데이터를 직렬 인쇄합니다.
  • <울>
  • 높음/낮음 트리거: 압력이 40psi(highTrigger)를 초과하거나 20psi(lowTrigger) 미만으로 올라가고 2분 이상 유지되면(dtLastTriggerLimit) Uno는 데이터를 클라우드로 전송할 MKR GSM 1400에 데이터를 직렬 인쇄합니다. <울>
  • 수요 투표: Uno의 핀 A1이 높게 푸시되면 데이터를 클라우드로 전송할 MKR GSM 1400으로 데이터를 직렬 인쇄합니다. 참고:핀 A1의 이름은 Uno용 스케치에서 "buttonPin"입니다. Uno의 A1 핀을 높게 올리는 방법은 2가지가 있습니다. (1) 브레드보드에 푸시버튼이 있습니다. (2) MKR GSM 1400의 핀 A3이 하이이면 핀 A1을 하이로 푸시합니다. A3 핀은 Arduino Cloud의 입력으로 제어되므로 정기적으로 예약된 데이터 전송을 기다릴 필요 없이 원격으로 현재 압력을 언제든지 얻을 수 있습니다.
  • 메모

    <울>
  • Uno에 대한 스케치는 현재 버전의 압력 외에도 온도, 습도, 배터리 전압 등과 같은 여러 입력을 높고 낮은 설정값으로 모니터링할 수 있도록 수정될 수 있습니다.
  • <울>
  • 압력 변환기의 아날로그 신호를 압력(psi)으로 변환하는 데 사용되는 코드는 다음 YouTube 동영상에 제공된 지침을 기반으로 합니다. https://www.youtube.com/watch?v=AB7zgnfkEi4
  • <울>
  • "직렬 입력 기본 사항"을 다루는 Arduino 포럼의 다음 게시물은 직렬 데이터를 사용하여 한 장치에서 다른 장치로 통신하는 코드를 작성할 때 매우 유용했습니다. https://forum.arduino.cc/index. php?topic=288234.0
  • 이 프로젝트에서 Arduino Uno에 사용된 코드에는 주요 정보를 설명하는 주석이 첨부되어 있습니다.

    파일 이름:"InstrumentReader"

    3단계:MKR GSM 1400에서 셀룰러를 통해 Arduino 클라우드로

    MKR GSM 1400은 Arduino Uno의 직렬 데이터를 처리하고 셀룰러 데이터를 사용하여 데이터를 Arduino Cloud로 전송합니다.

    MKR GSM 1400용 코드에서 Serial.read가 아닌 Serial1.read를 볼 수 있다는 점은 주목할 가치가 있습니다. Arduino 웹 사이트의 참조 자료에 좋은 설명이 나와 있습니다. 아래 이미지의 표는 MKR 보드의 TX/RX 핀이 Serial1을 통해 액세스되는 것을 보여줍니다.

    https://www.arduino.cc/reference/en/language/functions/communication/serial/

    아두이노 클라우드

    이 프로젝트는 Arduino Cloud에서 2개의 변수로 설정됩니다. 아래 이미지는 이러한 변수가 Arduino Cloud의 대시보드에 어떻게 표시되는지 보여줍니다.

    "dataStringCloud"라는 이름의 첫 번째 변수는 사실상 장치의 모든 데이터 패키지입니다. 이 접근 방식은 Google 스프레드시트에서 데이터 처리를 단순화하기 위해 각 값에 대해 하나의 변수를 사용하는 것과 반대로 사용되었습니다. 개별 변수 이름을 사용하면 동일하게 유지되는 값과 업데이트되지 않은 값의 차이를 구별하기가 어려웠습니다. 이 패키지의 데이터는 Google 스프레드시트에서 구문 분석됩니다.

    "pinCloud"라는 두 번째 변수는 Arduino Cloud에서 MKR GSM 1400을 제어하는 ​​데 사용됩니다. 스케치에는 pinCloud의 값에 따라 동작을 제어하는 ​​스위치 기능이 있습니다. pinCloud =1일 때 핀 A1이 높게 눌러 보드의 LED가 켜집니다. pinCloud =2일 때 핀 A3이 높게 푸시되어 위에서 설명한 대로 Arduino Uno가 현재 데이터를 전송합니다.

    이 프로젝트에서 Arduino MKR GSM 1400에 사용된 코드에는 주요 정보를 설명하는 주석이 첨부되어 있습니다.

    파일 이름:"CommunicationsDevice"

    4단계:웹훅을 통해 Arduino 클라우드에서 Google 스프레드시트로

    데이터는 웹훅을 사용하여 Arduino Cloud에서 Google Sheets로 전송됩니다.

    Webhook의 핵심은 Google Sheets 파일의 스크립트로 작성된 doPost 함수입니다.

    다음은 웹훅을 설정하는 방법에 대한 간략한 요약입니다. 프로세스는 Google 스프레드시트에서 시작됩니다. 끝날 때까지 Arduino Cloud에 도달할 수 없습니다. A에서 B로 이동하려면 B에서 시작하십시오.

    <울>
  • 새 Google 스프레드시트 파일 만들기
  • <울>
  • 도구 모음에서 "도구"를 클릭하고 드롭다운에서 "스크립트 편집기"를 선택합니다.
  • <울>
  • doPost 함수로 코드 작성 (이 프로젝트에 첨부된 GoogleSheetsScript.js 참조)
  • <울>
  • 도구 모음에서 "게시"를 클릭하고 드롭다운에서 "웹 앱으로 배포..."를 선택합니다.
  • <울>
  • 3개의 필드가 있는 대화 상자가 나타납니다.
  • <울>
  • (1) 프로젝트 버전: 항상 드롭다운을 사용하여 "새로 만들기"를 선택하십시오. 첫 번째 업데이트 후에는 현재 버전의 #으로 기본 설정됩니다. 드롭다운을 사용하여 "새로 만들기"를 선택하지 않으면 변경 사항이 적용되지 않습니다.
  • <울>
  • (2) 다음과 같이 앱을 실행합니다. "나([email protected])"
  • <울>
  • (3) 앱에 대한 액세스 권한이 있는 사용자: "누구나, 익명이라도"
  • <울>
  • 3개 필드의 값을 확인한 후 배포 누르기
  • <울>
  • "현재 웹 앱 URL"과 함께 두 번째 대화 상자가 나타납니다. 이것은 Arduino 클라우드의 웹훅 탭에 복사하여 붙여넣을 URL입니다. 주목할 점은 이 URL은 프로젝트 버전에 관계없이 동일하게 유지된다는 것입니다.
  • <울>
  • 확인을 클릭하면 완료됩니다!
  • 이 프로젝트에 사용된 JavaScript 코드의 상당 부분은 "Arduino IoT Cloud Google Sheets Integration"이라는 다른 프로젝트에서 사용된 코드를 모델로 합니다. 프로젝트 링크는 아래에 있습니다. 확인하는 것이 좋습니다.

    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 Cloud에서 전송된 고유 레코드 표시

    아래 링크는 최근 기기 테스트에 사용된 Google Sheets 파일입니다. 이 파일의 셀은 각 시트의 범례에 표시된 대로 채워진 방식에 따라 색상이 지정됩니다.

    https://docs.google.com/spreadsheets/d/1XwCir2Llw8RvGPGgZI3Yk6U5a3LeIfUACnuO1Gr_LFQ/edit?usp=sharing

    6단계:Google 스프레드시트를 사용하여 알림 보내기

    위의 4단계에서 참조한 이 프로젝트의 JavaScript 파일(GoogleSheetsScript.js)에 두 개의 함수가 있다는 것을 눈치채셨을 것입니다.

    <울>
  • doPost 함수 - Arduino Cloud Webhook에서 데이터를 전송합니다. Arduino Cloud에 새로운 데이터가 있을 때 실행됩니다.
  • <울>
  • sendEmail 함수 - 프로젝트의 Google 스프레드시트 파일에 있는 "Data"라는 시트에서 추출한 값을 기반으로 이메일을 보냅니다. 트리거 설정의 설정에 따라 1분에 한 번씩 실행됩니다.
  • sendEmail 기능에 대한 트리거 설정 단계

    <울>
  • Google 스프레드시트 파일 열기
  • <울>
  • 도구 모음에서 "도구" 클릭
  • <울>
  • 드롭다운에서 "스크립트 편집기" 선택
  • <울>
  • 스크립트 편집기 창에서 다음으로 이동합니다.
  • <울>
  • 도구 모음에서 '수정' 클릭
  • <울>
  • 드롭다운에서 "현재 프로젝트의 트리거" 선택
  • <울>
  • G Suite 개발자 허브 창에서 다음으로 이동합니다.
  • <울>
  • 창 오른쪽 하단의 "트리거 추가" 선택
  • <울>
  • 표시되는 대화 상자에서 sendEmail 기능을 실행하도록 선택합니다.
  • <울>
  • 참고:시간 기반으로 트리거를 실행하면 기기 업데이트가 중지될 때 이메일 알림을 생성할 수 있습니다.
  • 배터리 수명

    ~24시간

    디스플레이를 끄거나 제거하여 최적화할 수 있습니다. 또 다른 옵션은 DHT22 및 DS3231과 같은 비필수 센서를 제거하는 것입니다.

    데이터 사용량

    ~0.7MB/일

    이것은 데이터 전송의 크기나 빈도를 줄임으로써 최적화될 수 있습니다. 예:크기를 줄이려면 압력, 온도, 습도 및 시간이 아닌 압력만 보내십시오. 빈도를 줄이기 위해 30분이 아닌 매시간 업데이트합니다.

    프로젝트 비용

    총계 =$241

    <울>
  • 아두이노 MKR GSM 1400($70)
  • <울>
  • 아두이노 우노($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/)
  • <울>
  • 아다프루트(https://www.adafruit.com/)
  • <울>
  • 아마존(https://www.amazon.com/)
  • <울>
  • 항만 화물
  • <울>
  • 홈 디포
  • 결론적으로...

    시간을 내어 이 프로젝트를 검토해 주셔서 감사합니다.

    모든 질문/피드백/댓글/제안을 환영합니다/감사합니다.

    <섹션 클래스="섹션 컨테이너 섹션 축소 가능" id="코드">

    코드

    <울>
  • InstrumentReader - Arduino Uno용 스케치
  • GoogleSheetsScript.js
  • InstrumentReader - Arduino UnoArduino용 스케치
    // Sketch 1 of 2// Arduino Uno // 이 장치에서 데이터를 수집하고 직렬을 통해 MKR 1400으로 전송합니다.// DHT22 센서에는 2개의 라이브러리가 필요하지만 코드에서는 하나만 호출됩니다. // (1):DHT 센서 라이브러리:https://github.com/adafruit/DHT-sensor-library // (2):Adafruit 통합 센서 라이브러리:https://github.com/adafruit/Adafruit_Sensor #include #include #include  // RTC 모듈#include  // DHT22#include "U8glib.h" // Velleman 128 x 64 OLED SPI 디스플레이 // 참고:다른 표준 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 UnoRTClib용 이더넷 실드의 SD 카드에 저장하는 옵션 RTC;#define DHTPIN 11 // DHT 센서에 연결된 디지털 핀#define DHTTYPE 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; // Arduino Uno용 이더넷 쉴드의 SD 카드에 저장하는 옵션 // float fileSizeSD =0.0; // Arduino Unoint용 이더넷 쉴드의 SD 카드에 저장하는 옵션 i =0; // Uno에 의해 취해진 판독의 수를 셉니다(프로그램의 # 루프와 같음) const int ledPin =9; // 전송 지시자(데이터 전송이 발생하면 깜박임) const int ledPin2 =8; // 푸시 전송 표시기(브레드보드의 수동 버튼에 의해 높게 푸시되거나 클라우드에서 활성화된 MKR 1400의 출력) const int buttonPin =A1;int buttonState =0;int transmissionFrequency =30; // 두 번째 장치로 데이터를 보내기 위한 직렬 인쇄 dataString의 빈도(분)String pTransmitDateTime ="";int transmissionCounter =0;int pTransmitMinute =0;int ptriggerTransmitAlertIndicator;float cRuntimeAtTriggerStart =0.0;float dtLastTrigger =0.0;int triggerCounter =;int triggerTransmitAlertCounter =0; // Trigger를 제어하기 위한 입력 변수float lowTrigger =20.0;float highTrigger =40.0;float dtLastTriggerLimit =2.0; // 이 시간 동안 조건이 충족되면 경고가 생성됩니다.void setup (void) { Serial.begin(9600); Wire.begin(); dht.begin(); 핀모드(LED핀, 출력); 핀모드(LED핀2, 출력); 핀모드(버튼핀, 입력); u8g.setRot180(); // 필요한 경우 화면을 뒤집습니다(회전하려면 이 줄에 주석 추가/제거) // Arduino Uno용 이더넷 쉴드의 SD 카드에 저장하는 옵션 // Serial.print("Initializing SD card..."); // if (!SD.begin(chipSelect)) // 카드가 존재하고 초기화 가능한지 확인 // { // Serial.println("Card failed, or not present"); // 동안 (1); // 더 이상 아무것도 하지 않음 // } // Serial.println("card initialized.");}void loop (void) { delay(5000); 지금 날짜 시간 =RTC.now(); float cRuntime =millis()/60000; float p =getPressure();// Serial.println(p); buttonState =digitalRead(buttonPin);// Serial.print("버튼:");// Serial.println(buttonState); if(버튼 상태 ==1) { digitalWrite(ledPin2, HIGH); 지연(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() --> 도 C이면 temp int transmissionIndicator =0; if(now.minute() % transmissionFrequency ==0 &&now.minute() !=pTransmitMinute) { transmissionIndicator =1; pTransmitMinute =now.minute(); pTransmitDateTime =String(now.hour())+String(":")+String(now.minute())+String(":")+String(now.second()); } int 트리거 상태 =0; if(p <=lowTrigger || p>
    =highTrigger) { // 참고:이 if 문의 조건에서 참조된 변수는 높은 설정값과 낮은 설정값에 대해 평가됩니다. // 평가되는 변수를 빠르게 변경합니다. 변수가 지정된 유일한 위치 triggerStatus =1; 트리거카운터++; } else { 트리거 카운터 =0; } if(triggerStatus ==1 &&triggerCounter ==1) { cRuntimeAtTriggerStart =cRuntime; } dtLastTrigger =cRuntime - cRuntimeAtTriggerStart; int triggerTransmitAlertIndicator =0; if((dtLastTrigger> dtLastTriggerLimit) &&triggerStatus ==1) { triggerTransmitAlertIndicator =1; 트리거 전송 경고 카운터 ++; } 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; 플래시LED(5,500); 지연(5000); } ptriggerTransmitAlertIndicator =triggerTransmitAlertIndicator; // 현재 표시기는 이전 표시기에 저장됩니다. 다음 루프에서 여기에 전송된 값은 새 값을 기반으로 생성된 값과 비교됩니다. // 문자열 생성 String dataString =""; 문자열 cDateTime =""; 문자열 chHumTemp =""; 문자열 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.second())+", "+문자열(h)+","+문자열(t)+","+문자열(p)+">"; cDateTime +=String(now.month())+"/"+String(now.day())+"/"+String(now.year())+" "+String(now.hour())+ ":"+String(now.minute())+":"+String(now.second()); cHumTemp +="H:"+문자열(h)+"% T:"+문자열(t)+"degF"; cP +="P:"+문자열(p)+"psi"; if(transmitIndicator ==1 || triggerPushTransmitAlertIndicator ==1 || buttonState ==1) { char dataArray[100]; dataString.toCharArray(dataArray, 100); 직렬.println(데이터배열); 플래시LED(10,500); 전송 카운터++; } // Serial.print("T:");// Serial.println(triggerStatus); 지연(100); // 전체 메시지가 도착할 때까지 잠시 기다립니다. // 그림 루프 u8g.firstPage(); do { draw(cDateTime,cHumTemp, cP,i,transmitCounter,now.minute(),transmitFrequency,pTransmitMinute); } 동안(u8g.nextPage()); 지연(1000); // writeToSD(dataString); // Arduino Uno용 이더넷 쉴드의 SD 카드에 저장하는 옵션 i++;}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("센서 값:");// 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( 비); float pressure_psi =((m*voltage)+ b);// Serial.print(" 압력 =");// Serial.print(pressure_psi);// Serial.println(" psi"); // 지연(200); 반환 압력_psi;} 무효 flashLED(int num, int t){ for (int z =1; z <=num; z++) { digitalWrite(ledPin, HIGH); 지연(t); 디지털 쓰기(LED 핀, LOW); 지연(t); }}// Arduino Uno용 이더넷 쉴드의 SD 카드에 저장하는 옵션 //void writeToSD(String dataToWrite) //{ // // 파일을 엽니다. 한 번에 하나의 파일만 열 수 있으므로 // // 다른 파일을 열기 전에 이 파일을 닫아야 합니다. // 파일 dataFile =SD.open("datalog4.txt", FILE_WRITE); // fileSizeSD =dataFile.size(); // 파일 크기를 바이트 단위로 반환 // fileSizeSD =fileSizeSD / 1000000; // 바이트를 MB로 변환합니다. 1MB =1e6바이트 // // 파일을 사용할 수 있으면 다음과 같이 작성합니다. // if (dataFile) // { // dataFile.println(dataToWrite); // dataFile.close(); // // 직렬 포트에도 출력:// // Serial.println(dataToWrite); // } // // 파일이 열려 있지 않으면 오류 팝업:// else // { // Serial.println("error opening datalog1.txt"); // } //}
    GoogleSheetsScript.js자바스크립트
    // 이 코드에는 2개의 함수가 있습니다. // doPost 함수 - Arduino Cloud Webhook에서 데이터를 전송합니다. Arduino Cloud.// sendEmail 함수에 새 데이터가 있을 때 실행됩니다. - 프로젝트의 Google Sheets 파일에서 "Data"라는 시트에서 추출한 값을 기반으로 이메일을 보냅니다. 트리거 설정의 설정에 따라 1분에 한 번씩 실행됩니다. 지침은 프로젝트 허브 게시물의 sendEmail 기능 부분을 참조하세요.// 이 코드의 대부분은 (sendEmail 기능을 제외하고) Arduino 프로젝트 허브// https://create.arduino의 다음 프로젝트를 모델로 합니다. cc/projecthub/Arduino_Genuino/arduino-iot-cloud-google-sheets-integration-71b6bc?f=1// 프로젝트 허브에서 위에서 언급한 프로젝트에 사용된 Google 스크립트가 있는 GitHub 저장소에 대한 링크입니다. // 이 링크는 프로젝트 허브의 프로젝트 설명에서 복사되었습니다.// https://github.com/arduino/arduino-iot-google-sheet-script/blob/master/Code.gs// get active 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 판독값var HEADER_ROW =1; // headervar의 행 인덱스 TIMESTAMP_COL =1; // 타임스탬프의 열 인덱스 columnfunction doPost(e) { var cloudData =JSON.parse(e.postData.contents); // IoT Cloud에서 들어오는 모든 정보를 포함하는 json 객체입니다. console.log(cloudData); //var webhook_id =cloudData.webhook_id; // 실제로 이 세 가지를 사용하지 않음 //var device_id =cloudData.device_id; //var thing_id =cloudData.thing_id; var 값 =cloudData.values; // 이것은 json 객체의 배열입니다. console.log(values); // values ​​배열에서 이름과 값을 저장합니다. // 들어오는 각 속성에는 다음이 있습니다. // 열 이름이 될 이름 // 열 헤더 아래의 행에 기록될 값 var incLength =values.length; var incNames =[]; var inc값 =[]; for (var i =0; i  2018) { // 도착한 모든 메시지를 버립니다. 'late' if (sheet.getRange(HEADER_ROW+1, 1).getValue() !='') { // HEADER_ROW + 1 =행 #2 &열 #1 --> 이것은 가장 최근 타임스탬프의 위치입니다. 시트에서 // 가장 최근 타임스탬프가 비어 있지 않으면 =(''), 현재 시간을 수신 데이터의 타임스탬프와 비교합니다. // 가장 최근 타임스탬프가 비어 있으면 스크립트가 처음일 가능성이 높습니다. 실행됩니다. // 이 경우 이 if 문을 건너뛰고 열 헤더 및 데이터 쓰기를 진행합니다. // 데이터가 늦게 도착하는지 여부는 중요하지 않습니다(현재 시간 대 들어오는 데이터의 타임스탬프). var now =new Date(); // 이제 var COMM_TIME =120; // 참고:더 많은 메시지를 전달할 수 있도록 120으로 변경, 이전에는 5초로 설정하고 정상적으로 작동했습니다. // 클라우드와 앱 간의 통신 시간을 대략적으로 과대평가한 경우 if (now.getTime() - date.getTime()> COMM_TIME * 1000) { // 현재 시간과 타임스탬프의 차이가 5초보다 크면 데이터를 버립니다. // 이 If 문의 조건이 true로 평가되면 return 문으로 인해 함수가 중지됩니다. 반품; // "return 문은 함수의 실행을 중지하고 해당 함수의 값을 반환합니다." } } // 이 섹션은 들어오는 속성의 이름을 기반으로 헤더 행에 값을 씁니다. // 즉, 이 섹션은 열 이름을 생성합니다. // 헤더 행과 첫 번째 셀에 이름을 할당합니다. 열 =타임스탬프 sheet.getRange(HEADER_ROW, 1).setValue('타임스탬프'); 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인 경우 평가 // 이름이 이미 헤더에 있는지 확인 var found =0; for (var col =2; col <=lastCol; col++) { // 2열에서 시작하여 lastCol까지 모든 열을 반복하여 if 문을 평가하는 if (sheet.getRange(HEADER_ROW, col).getValue( ) ==incNames[i]) { // the condition of this If statement compares the value in the header row &column # =col to the 'i'th value in the array of incoming property names // This if statement is evaulated 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

    회로도


    제조공정

    1. ADLINK는 Google Cloud와 협력하여 IoT 지원 솔루션을 제공합니다.
    2. 기본 IoT – RaspberryPI HDC2010 방법
    3. BMP180 I2C 디지털 기압 센서
    4. Google 스프레드시트가 포함된 Python/MicroPython 센서 로거
    5. Raspberry Pi 2의 Windows 10 IoT Core – Adafruit 센서 데이터
    6. Windows 10 IoT 코어 및 SHT15
    7. UnifiedWater v1
    8. IOT - ESP8266, Arduino 및 초음파 센서를 사용하는 스마트 항아리
    9. Arduino 클라우드 센서 타워
    10. Arduino Apple Watch