Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets

Komponenten und Materialien

Arduino UNO × 1
Arduino MKR GSM 1400 × 1
Druckwandler (0-150 psi) × 1
Adafruit DHT22 Temperatur- & Feuchtigkeitssensor × 1
DS3231 RTC Modul × 1
Adafruit Logic Level Converter (BSS138, 4-Kanal I2C) × 1
Adafruit 3,7V LiPo Akku (2000 mAh+) × 2
Step-Up Spannungswandler × 1
Starterkit (LEDs, Widerstände, Kabel) × 1
Breadboard × 1

Apps und Online-Dienste

Projektziel

Entwicklung eines kostengünstigen Prototyps zur Drucküberwachung an Industrieanlagen an abgelegenen Standorten mittels Mobilfunkdaten.

Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets
Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets
Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets
Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets
Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets

Projektdurchlauf

Schritt 1: Druckwandler → Arduino Uno

Der Druckwandler wandelt Druck in ein analoges elektrisches Signal um. Der Arduino Uno wandelt dieses Signal in Druckwerte (psi) um.

Schritt 2: Uno → MKR GSM 1400 via Serial

Serielle Kommunikation zwischen zwei Arduinos:

Datenübertragungs-Häufigkeit (Uno → MKR GSM 1400):

Hinweise: Der Uno-Code könnte für mehrere Eingänge (Temperatur, Feuchtigkeit, Batteriespannung) mit eigenen Schwellwerten erweitert werden. Die Umwandlung des Analogsignals in psi basiert auf diesem YouTube-Video. Der Arduino Forum Post "Serial Input Basics" half bei der seriellen Kommunikation.

Uno-Code: "InstrumentReader" (mit Kommentaren im Anhang)

Schritt 3: MKR GSM 1400 → Arduino Cloud via Mobilfunk

Der MKR GSM 1400 verarbeitet Seraldaten vom Uno und sendet sie via Mobilfunk an die Arduino Cloud.

Wichtig: Im MKR-Code wird Serial1.read statt Serial.read verwendet, da MKR-Boards TX/RX über Serial1 ansprechen. Siehe Arduino Reference.

Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets

Arduino Cloud Variablen:

Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets

MKR-Code: "CommunicationsDevice" (mit Kommentaren im Anhang)

Schritt 4: Arduino Cloud → Google Sheets via Webhook

Datenübertragung per Webhook. Kern ist die doPost-Funktion im Google Sheets Script.

Webhook-Einrichtung (Start in Google Sheets!):

  1. Neue Google Sheets Datei erstellen
  2. Tools → Scripteditor
  3. Code mit doPost-Funktion schreiben (siehe GoogleSheetsScript.js)
  4. Veröffentlichen → Als Web-App bereitstellen
  5. Dialog: (1) Projektversion: "Neu" wählen, (2) Ausführen als: "Ich", (3) Zugriff: "Jeder, auch anonym"
  6. Deploy klicken → "Current web app URL" kopieren → in Arduino Cloud Webhook-Tab einfügen (URL bleibt bei Updates gleich)

Großer Teil des JavaScript-Codes basiert auf dem Projekt Arduino IoT Cloud Google Sheets Integration.

Schritt 5: Google Sheets – Daten parsen

Einzelwerte aus dataStringCloud extrahieren und eindeutige Datensätze anzeigen. Test-Sheet mit Farbcodierung: Google Sheets Beispiel

Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets

Schritt 6: Google Sheets – Benachrichtigungen senden

Zwei Funktionen in GoogleSheetsScript.js:

Trigger für sendEmail einrichten:

  1. Google Sheets → Tools → Scripteditor
  2. Bearbeiten → Aktuelle Projekt-Trigger
  3. Trigger hinzufügen → sendEmail auswählen
  4. Zeitgesteuert (z. B. jede Minute) → ermöglicht Benachrichtigung bei Geräteausfall
Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets

Batterielaufzeit

~24 Stunden. Optimierbar durch Abschalten der Displays oder Entfernen nicht-essentieller Sensoren (DHT22, DS3231).

Datenverbrauch

~0,7 MB/Tag. Optimierbar durch Reduktion von Datenmenge (nur Druck statt aller Werte) oder Frequenz (stündlich statt 30-minütlich).

Projektkosten

Gesamt: ~241 $

Hardware / Werkzeuge

Bezugsquellen: Arduino Online Store, Adafruit, Amazon, Harbor Freight, Home Depot.

Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets

Fazit

Danke fürs Review! Fragen, Feedback, Kommentare, Vorschläge sind willkommen.

Code

InstrumentReader – Sketch für Arduino Uno

// Sketch 1 of 2 // Arduino Uno
// Datenerfassung & Übertragung an MKR 1400 via Serial
// DHT22 benötigt 2 Libraries (nur eine wird im Code aufgerufen):
// (1) DHT Sensor Library: https://github.com/adafruit/DHT-sensor-library
// (2) Adafruit Unified Sensor: https://github.com/adafruit/Adafruit_Sensor
#include <SPI.h>
#include <Wire.h>
#include <DS3231.h>  // RTC
#include <DHT.h>     // DHT22
#include "U8glib.h"   // Velleman 128x64 OLED SPI Display
// Hinweis: Standard U8glib funktionierte nicht; Hersteller-Library nötig:
// https://www.velleman.eu/support/downloads/?code=VMA437
// Syntax: https://github.com/olikraus/u8glib/wiki/userreference

RTClib RTC;
#define DHTPIN 11
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
U8GLIB_SH1106_128X64 u8g(3, 4, 6, 7);  // CLK:3, MOSI:4, CS:6, DC:7
int y_pos = 0;
int i = 0;  // Messzähler
const int ledPin = 9;      // Sendeanzeige
const int ledPin2 = 8;     // Manuelle Sendeanzeige
const int buttonPin = A1;
int buttonState = 0;
int transmitFrequency = 30;  // Minuten
String pTransmitDateTime = "";
int transmitCounter = 0;
int pTransmitMinute = 0;
int ptriggerTransmitAlertIndicator;
float cRuntimeAtTriggerStart = 0.0;
float dtLastTrigger = 0.0;
int triggerCounter = 0;
int triggerTransmitAlertCounter = 0;

float lowTrigger = 20.0;
float highTrigger = 40.0;
float dtLastTriggerLimit = 2.0;

void setup() {
  Serial.begin(9600);
  Wire.begin();
  dht.begin();
  pinMode(ledPin, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(buttonPin, INPUT);
  u8g.setRot180();  // Display drehen falls nötig
}

void loop() {
  delay(5000);
  DateTime now = RTC.now();
  float cRuntime = millis() / 60000;
  float p = getPressure();
  buttonState = digitalRead(buttonPin);
  
  if (buttonState == 1) {
    digitalWrite(ledPin2, HIGH);
    delay(30000);  // Warten bis MKR1400 bereit
  } else {
    digitalWrite(ledPin2, LOW);
  }

  float h = dht.readHumidity();
  float t = dht.readTemperature(true);  // true = Fahrenheit
  int transmitIndicator = 0;
  
  if (now.minute() % transmitFrequency == 0 && now.minute() != pTransmitMinute) {
    transmitIndicator = 1;
    pTransmitMinute = now.minute();
    pTransmitDateTime = String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second());
  }

  int triggerStatus = 0;
  if (p <= lowTrigger || p >= highTrigger) {
    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) {
    triggerPushTransmitAlertIndicator = 1;
    flashLED(5, 500);
    delay(5000);
  }
  ptriggerTransmitAlertIndicator = triggerTransmitAlertIndicator;

  // Datenstring erstellen
  String 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) + ">";

  String cDateTime = String(now.month()) + "/" + String(now.day()) + "/" + String(now.year()) +
    " " + String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second());
  String cHumTemp = "H:" + String(h) + "% T:" + String(t) + "degF";
  String 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++;
  }
  delay(100);

  // Display aktualisieren
  u8g.firstPage();
  do {
    draw(cDateTime, cHumTemp, cP, i, transmitCounter, now.minute(), transmitFrequency, pTransmitMinute);
  } while (u8g.nextPage());
  delay(1000);
  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.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("Remainder:"); u8g.setPrintPos(70, 48); u8g.print(DnowMinute % DtransmitFrequency);
}

float getPressure() {
  int sensorVal = analogRead(A2);
  float voltage = (sensorVal * 5.0) / 1023.0;
  // Bei 0 psi: Analog Input = 100 → Voltage = 100 * (5/1023) = 0.4889
  float m = ((150 - 0) / (4.5 - 0.4889));
  float b = 150 - (m * 4.5);
  float pressure_psi = (m * voltage) + b;
  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);
  }
}

GoogleSheetsScript.js

// 2 Funktionen:
// doPost - Empfängt Daten vom Arduino Cloud Webhook (läuft bei neuen Cloud-Daten)
// sendEmail - Sendet E-Mails basierend auf Sheet "Data" (läuft minütlich per Trigger)

// Basiert auf: https://create.arduino.cc/projecthub/Arduino_Genuino/arduino-iot-cloud-google-sheets-integration-71b6bc
// GitHub: https://github.com/arduino/arduino-iot-google-sheet-script/blob/master/Code.gs

var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("RawData");
var sd = ss.getSheetByName("Data");
var sref = ss.getSheetByName("References");
var MAX_ROWS = 1440;  // Max Datenzeilen (12h bei 30s Intervall)
var HEADER_ROW = 1;
var TIMESTAMP_COL = 1;

function doPost(e) {
  var cloudData = JSON.parse(e.postData.contents);
  console.log(cloudData);
  var values = cloudData.values;
  console.log(values);
  
  var incLength = values.length;
  var incNames = [];
  var incValues = [];
  for (var i = 0; i < incLength; i++) {
    incNames[i] = values[i].name;
    incValues[i] = values[i].value;
  }
  console.log(incNames);
  console.log(incValues);

  var timestamp = values[0].updated_at;
  var date = new Date(Date.parse(timestamp));
  
  if (date.getYear() > 2018) {
    if (sheet.getRange(HEADER_ROW + 1, 1).getValue() != '') {
      var now = new Date();
      var COMM_TIME = 120;  // 120 Sekunden Toleranz
      if (now.getTime() - date.getTime() > COMM_TIME * 1000) {
        return;  // Veraltete Nachricht verwerfen
      }
    }
    
    // Header schreiben
    sheet.getRange(HEADER_ROW, 1).setValue('timestamp');
    for (var i = 0; i < incLength; i++) {
      var lastCol = sheet.getLastColumn();
      if (lastCol == 1) {
        sheet.getRange(HEADER_ROW, lastCol + 1).setValue(incNames[i]);
      } else {
        var found = 0;
        for (var col = 2; col <= lastCol; col++) {
          if (sheet.getRange(HEADER_ROW, col).getValue() == incNames[i]) {
            found = 1; break;
          }
        }
        if (found == 0) {
          sheet.getRange(HEADER_ROW, lastCol + 1).setValue(incNames[i]);
        }
      }
    }
    
    var lastCol = sheet.getLastColumn();
    var lastRow = sheet.getLastRow();
    if (lastRow > MAX_ROWS + HEADER_ROW - 1) sheet.deleteRow(lastRow);
    sheet.insertRowAfter(HEADER_ROW);
    
    var range = sheet.getRange('A2:Z2');
    range.setFontColor('#000000');
    range.setFontSize(10);
    range.setFontWeight('normal');
    
    sheet.getRange(HEADER_ROW + 1, TIMESTAMP_COL).setValue(date).setNumberFormat("yyyy-MM-dd HH:mm:ss");
    
    for (var col = 1 + TIMESTAMP_COL; col <= lastCol; col++) {
      for (var i = 0; i < incLength; i++) {
        var currentName = sheet.getRange(HEADER_ROW, col).getValue();
        if (currentName == incNames[i]) {
          if (incValues[i] == true) incValues[i] = 1;
          else if (incValues[i] == false) incValues[i] = 0;
          sheet.getRange(HEADER_ROW + 1, col).setValue(incValues[i]);
        }
      }
      if (sheet.getRange(HEADER_ROW + 1, col).getValue() == '') {
        sheet.getRange(HEADER_ROW + 1, col).setValue("-");
      }
    }
  }
}

function sendEmail() {
  var emailAddress = sd.getRange("V3").getValue();
  var lastPressure = sd.getRange("K3").getValue();
  var lastUpdate = sd.getRange("A3").getValue();
  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);
    sd.getRange("L5").setValue(-1);
    sd.getRange("L6").setValue(triggerAlertCount + 1);
    MailApp.sendEmail(emailAddress, "Status Change Alert - Outside Setpoints", message);
  }
  if (triggerMonitor == 1 && triggerAlertCount <= 4) {
    sd.getRange("L3").setValue(lastUpdate);
    sd.getRange("L5").setValue(1);
    sd.getRange("L6").setValue(triggerAlertCount + 1);
    MailApp.sendEmail(emailAddress, "Status Change Alert - Normal", message);
  }
  if (emailSentNoUpdate == 0 && dtLastDeviceUpdate1 > 60 && dtLastDeviceUpdate2 > 60) {
    sd.getRange("T3").setValue(1);
    sd.getRange("U3").setValue(0);
    sd.getRange("T4").setValue(new Date().getTime());
    MailApp.sendEmail(emailAddress, "Alert - Over 60 minutes Since Last Device Update", message);
  }
  if (emailSentNoUpdate == 1 && dtLastDeviceUpdate1 < 60) {
    sd.getRange("T3").setValue(0);
    sd.getRange("U3").setValue(1);
    sd.getRange("U4").setValue(new Date().getTime());
    MailApp.sendEmail(emailAddress, "Alert - Device Started Updating", message);
  }
}

CommunicationsDevice – Sketch für MKR GSM 1400

Code im Anhang des Originalprojekts verfügbar.

Schaltplan

Echtzeit-IoT-Drucküberwachung mit MKR GSM, Arduino Cloud & Google Sheets

Herstellungsprozess

  1. ADLINK & Google Cloud kooperieren für nahtlose IoT-Lösungen
  2. Raspberry Pi & HDC2010: Kompletter I2C Temperatur- & Feuchtigkeitssensor Setup-Guide
  3. BMP180 I2C Barometer-Sensor – Raspberry Pi Integrationsanleitung
  4. Python/MicroPython Sensor Logger: Sensordaten nahtlos in Google Sheets speichern
  5. Adafruit 10DOF IMU Daten von Raspberry Pi 2 an Azure Event Hubs senden (Windows 10 IoT Core)
  6. Präzise Temperatur- & Feuchtigkeitsüberwachung mit SHT15 unter Windows 10 IoT Core
  7. UnifiedWater v1 – Komplettes Arduino Wasserqualitäts-Sensorkit
  8. Smart IoT-Glas bauen: Füllstand überwachen mit ESP8266, Arduino & Ultraschall
  9. Sensorturm mit Arduino Cloud: Temperatur, Bewegung & Gase überwachen
  10. DIY Arduino Smartwatch: Eigene Apple-Watch-Alternative bauen