amc:ss2025:group-f:start
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
amc:ss2025:group-f:start [2025/07/28 20:21] – [3.6 Google Apps Script and Google sheets] 33784_students.hsrw | amc:ss2025:group-f:start [2025/07/29 22:36] (current) – paul-christian.thoma | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== | ====== | ||
+ | **Applied Measurement and Control 2025** | ||
+ | |||
Paul-Christian Thoma(32436)-Elham Mohammadi(32475)-Deniz-Zeynep Adem(33784) | Paul-Christian Thoma(32436)-Elham Mohammadi(32475)-Deniz-Zeynep Adem(33784) | ||
Line 16: | Line 18: | ||
By combining local sensing, cloud-based logic, and remote monitoring tools, this system provides a scalable, modular approach to smart irrigation in home or laboratory environments. | By combining local sensing, cloud-based logic, and remote monitoring tools, this system provides a scalable, modular approach to smart irrigation in home or laboratory environments. | ||
- | {{ amc: | + | {{ : |
**Figure 1.** Automated Plant Watering System Prototype | **Figure 1.** Automated Plant Watering System Prototype | ||
Line 123: | Line 125: | ||
The following diagrams, created using Tinkercad, illustrate the complete hardware connections for both the Sensor and Pump nodes in the system. | The following diagrams, created using Tinkercad, illustrate the complete hardware connections for both the Sensor and Pump nodes in the system. | ||
- | {{ amc: | + | {{ :amc: |
**Figure 2.** Schematic Diagram of Sensor Setup (designed by Deniz-Zeynep Adem) | **Figure 2.** Schematic Diagram of Sensor Setup (designed by Deniz-Zeynep Adem) | ||
| | ||
- | {{ amc: | + | {{ :amc: |
**Figure 3.** Schematic Diagram of Pump Setup (designed by Deniz-Zeynep Adem) | **Figure 3.** Schematic Diagram of Pump Setup (designed by Deniz-Zeynep Adem) | ||
Line 138: | Line 140: | ||
| | ||
+ | |||
<code cpp> | <code cpp> | ||
#include < | #include < | ||
Line 151: | Line 154: | ||
// Google Apps Script webhook URL for sending data to Google Sheets | // Google Apps Script webhook URL for sending data to Google Sheets | ||
- | const char GOOGLE_SHEET_URL[] = " | + | const char GOOGLE_SHEET_URL[] = " |
// MQTT Topics | // MQTT Topics | ||
Line 185: | Line 188: | ||
// Format data as JSON payload | // Format data as JSON payload | ||
- | String | + | String |
+ | if (status == "Dry" | ||
+ | action = "Pump is ON 💧"; | ||
+ | } else if (status == "Moist") { | ||
+ | action = "OK 👍"; | ||
+ | } else { | ||
+ | action = "Alert ⚠️ Maintenance Needed"; | ||
+ | } | ||
+ | |||
+ | String jsonPayload = "{\" | ||
+ | " | ||
+ | " | ||
Serial.println(" | Serial.println(" | ||
Line 213: | Line 228: | ||
if (millis() - lastRead > readInterval) { | if (millis() - lastRead > readInterval) { | ||
int value = analogRead(sensorPin); | int value = analogRead(sensorPin); | ||
- | String status = value > threshold ? " | + | String status = value > threshold ? " |
// Publish raw value and status to MQTT topics | // Publish raw value and status to MQTT topics | ||
Line 229: | Line 244: | ||
// If soil is dry, send water command | // If soil is dry, send water command | ||
- | if (status | + | if (status.startsWith(" |
mqtt.publish(COMMAND_TOPIC, | mqtt.publish(COMMAND_TOPIC, | ||
} | } | ||
Line 246: | Line 261: | ||
#include < | #include < | ||
#include < | #include < | ||
- | #include < | + | #include < |
+ | #include < | ||
- | // WiFi credentials | + | // Wi-Fi credentials |
- | const char* ssid = "YOUR_WIFI_SSID"; | + | const char* ssid = "iotlab"; |
- | const char* password = "YOUR_WIFI_PASSWORD"; | + | const char* password = "iotlab18"; |
- | // MQTT broker | + | // MQTT broker |
const char* mqtt_server = " | const char* mqtt_server = " | ||
+ | const char* control_topic = " | ||
- | WiFiClient espClient; | + | // Google Apps Script Web App URL |
- | PubSubClient client(espClient); | + | const char* google_script_url = " |
- | Servo pumpServo; | + | WiFiClient espClient; |
- | const int servoPin = 5; // Pin connected to the servo | + | PubSubClient client(espClient); |
+ | |||
+ | Servo pumpServo; | ||
+ | const int servoPin = 5; | ||
- | // Function to connect to WiFi | ||
void setup_wifi() { | void setup_wifi() { | ||
+ | Serial.print(" | ||
WiFi.begin(ssid, | WiFi.begin(ssid, | ||
while (WiFi.status() != WL_CONNECTED) { | while (WiFi.status() != WL_CONNECTED) { | ||
Line 271: | Line 291: | ||
} | } | ||
- | // Callback function that handles incoming MQTT messages | + | void postToGoogleSheet(const String& status, const String& action) { |
+ | if (WiFi.status() == WL_CONNECTED) { | ||
+ | HTTPClient http; | ||
+ | http.begin(google_script_url); | ||
+ | http.addHeader(" | ||
+ | |||
+ | | ||
+ | String payload = " | ||
+ | |||
+ | int httpResponseCode = http.POST(payload); | ||
+ | Serial.print(" | ||
+ | Serial.println(httpResponseCode); | ||
+ | http.end(); | ||
+ | } else { | ||
+ | Serial.println(" | ||
+ | } | ||
+ | } | ||
void callback(char* topic, byte* payload, unsigned int length) { | void callback(char* topic, byte* payload, unsigned int length) { | ||
- | String msg = ""; | + | String msg = ""; |
- | for (int i = 0; i < length; i++) msg += (char)payload[i]; | + | for (unsigned |
- | msg.trim(); | + | |
+ | } | ||
+ | msg.trim(); | ||
- | Serial.print(" | + | Serial.print(" |
Serial.print(topic); | Serial.print(topic); | ||
- | Serial.print(": | + | Serial.print(" |
Serial.println(msg); | Serial.println(msg); | ||
- | | + | if (String(topic) == control_topic) { |
- | | + | if (msg == " |
- | if (msg == " | + | pumpServo.write(180); |
- | pumpServo.write(180); | + | Serial.println(" |
+ | postToGoogleSheet(" | ||
} else if (msg == " | } else if (msg == " | ||
- | pumpServo.write(90); | + | pumpServo.write(90); |
+ | Serial.println(" | ||
+ | postToGoogleSheet(" | ||
} | } | ||
} | } | ||
} | } | ||
- | // Function to (re)connect to MQTT and subscribe to topic | ||
void reconnect() { | void reconnect() { | ||
while (!client.connected()) { | while (!client.connected()) { | ||
- | Serial.print(" | + | Serial.print(" |
- | if (client.connect(" | + | if (client.connect(" |
Serial.println(" | Serial.println(" | ||
- | client.subscribe(" | + | client.subscribe(control_topic); |
} else { | } else { | ||
Serial.print(" | Serial.print(" | ||
- | Serial.println(client.state()); | + | Serial.print(client.state()); |
- | delay(2000); | + | delay(2000); |
} | } | ||
} | } | ||
Line 308: | Line 349: | ||
void setup() { | void setup() { | ||
- | Serial.begin(115200); | + | Serial.begin(115200); |
- | setup_wifi(); | + | setup_wifi(); |
- | pumpServo.attach(servoPin); | + | pumpServo.attach(servoPin); |
- | pumpServo.write(90); | + | pumpServo.write(90); |
- | + | client.setServer(mqtt_server, | |
- | client.setServer(mqtt_server, | + | client.setCallback(callback); |
- | client.setCallback(callback); | + | |
} | } | ||
void loop() { | void loop() { | ||
- | if (!client.connected()) reconnect(); | + | if (!client.connected()) |
- | client.loop(); | + | |
+ | } | ||
+ | client.loop(); | ||
} | } | ||
</ | </ | ||
Line 409: | Line 451: | ||
- | Step-by-Step Guide to Connect ESP32 to Google Sheets | + | |
+ | The saved data can be accessed through this link: | ||
+ | |||
+ | '' | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | //**Step-by-Step Guide to Connect ESP32 to Google Sheets**// | ||
**1. Create a Google Sheet** | **1. Create a Google Sheet** | ||
* Open [Google Sheets](https:// | * Open [Google Sheets](https:// | ||
- | * Name the sheet something like **"Soil Moisture Log"**. | + | * Name the sheet something like **"Automated Plant Watering Data"**. |
* Rename the first sheet tab to **" | * Rename the first sheet tab to **" | ||
* Add column headers in Row 1: | * Add column headers in Row 1: | ||
Line 448: | Line 498: | ||
**4. Integrate the Web App URL into Your ESP32 Code** | **4. Integrate the Web App URL into Your ESP32 Code** | ||
- | In your ESP32 Arduino code, update the `GOOGLE_SHEET_URL`: | + | In your ESP32 Arduino code, update the `GOOGLE_SHEET_URL` |
'' | '' | ||
Line 457: | Line 507: | ||
'' | '' | ||
- | // | ||
**5. Upload and Run the ESP32 Code** | **5. Upload and Run the ESP32 Code** | ||
- | Upload your finalized ESP32 code via Arduino IDE or PlatformIO. | + | Upload your finalized ESP32 code via Arduino IDE. |
* **The ESP32 will:** | * **The ESP32 will:** | ||
Line 485: | Line 534: | ||
**Google Apps Script Codes:** | **Google Apps Script Codes:** | ||
+ | <code cpp> | ||
+ | function doPost(e) { | ||
try { | try { | ||
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(" | const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(" | ||
const data = JSON.parse(e.postData.contents); | const data = JSON.parse(e.postData.contents); | ||
+ | |||
const status = data.status || ""; | const status = data.status || ""; | ||
const moisture = data.moisture || ""; | const moisture = data.moisture || ""; | ||
const action = data.action || ""; | const action = data.action || ""; | ||
const timestamp = new Date(); | const timestamp = new Date(); | ||
- | | + | |
+ | | ||
sheet.insertRows(2, | sheet.insertRows(2, | ||
- | sheet.getRange(2, | + | sheet.getRange(2, |
- | sheet.getRange(2, | + | sheet.getRange(2, |
- | sheet.getRange(2, | + | sheet.getRange(2, |
- | sheet.getRange(2, | + | sheet.getRange(2, |
- | // Optional: | + | |
+ | // Send emails if there is an alert | ||
if (action.toLowerCase().includes(" | if (action.toLowerCase().includes(" | ||
- | | + | |
- | const subject = " | + | `Soil Alert!\nStatus: ${status}\nMoisture: |
- | const message = `An alert has been logged in your system:\n\n` + | + | |
- | `Status: ${status}\nMoisture: | + | |
- | MailApp.sendEmail(emailAddress, | + | |
} | } | ||
+ | |||
return ContentService.createTextOutput(" | return ContentService.createTextOutput(" | ||
- | } catch (error) { | + | } catch (err) { |
- | Logger.log(" | + | return ContentService.createTextOutput(" |
- | return ContentService.createTextOutput(" | + | |
| | ||
} | } | ||
+ | } | ||
+ | |||
+ | </ | ||
====== | ====== | ||
Line 523: | Line 576: | ||
Below is a video showcasing the node-Red flow and showing the code and possible errors that came up and how they were fixed. Starting with the discussion part of this documentation. | Below is a video showcasing the node-Red flow and showing the code and possible errors that came up and how they were fixed. Starting with the discussion part of this documentation. | ||
- | https:// | + | {{ : |
**Figure 4.** Documentation of the data in note-red (designed by Paul-Christian Thoma) | **Figure 4.** Documentation of the data in note-red (designed by Paul-Christian Thoma) | ||
Line 535: | Line 587: | ||
The project successfully demonstrates an automated plant watering system based on real-time soil moisture monitoring and MQTT communication. Separating control logic from the sensor node to Node-RED allows flexible and remote decision-making. Integration with Google Sheets and email notifications adds monitoring and alert capabilities. | The project successfully demonstrates an automated plant watering system based on real-time soil moisture monitoring and MQTT communication. Separating control logic from the sensor node to Node-RED allows flexible and remote decision-making. Integration with Google Sheets and email notifications adds monitoring and alert capabilities. | ||
- | The setup of the hardware is quick and easy especially since the water pump had a build in relay. As long as the right amount of power in this case 9V can be supplied to the pump. Most of the effort was to programm | + | The setup of the hardware is quick and easy especially since the water pump had a build in relay. As long as the right amount of power in this case 9V can be supplied to the pump. Most of the effort was to program |
- | When setting up the email-Alert extra steps where needed to allow node-red to send an email. For example creating an app password for the gmail email and enabling 2FA authentication. If feasible it might be an option to rely on sms messaging or a telegram bot for communicating the alert. A possible advantage would be real time communication with no delay. Additionally it was necessary to include a command to prevent the alert from being triggered multiple times during the same event. Otherwise if the pump would malfunction once the alert would continously be send every 10 seconds until it was fixed which would lead to the inbox being filled with e-mails. | + | |
+ | When setting up the email-Alert extra steps where needed to allow node-red to send an email. For example creating an app password for the gmail email and enabling 2FA authentication. If feasible it might be an option to rely on sms messaging or a telegram bot for communicating the alert. | ||
+ | |||
+ | A possible advantage would be real time communication with no delay. Additionally it was necessary to include a command to prevent the alert from being triggered multiple times during the same event. Otherwise if the pump would malfunction once the alert would continously be send every 10 seconds until it was fixed which would lead to the inbox being filled with e-mails. | ||
As of right now the system is only suitable for indoor application of plants inside a living room. Since the current setup is not weatherproof/ | As of right now the system is only suitable for indoor application of plants inside a living room. Since the current setup is not weatherproof/ | ||
+ | |||
To make it self sustainable it could be considered to use a PV-module for supplying power and charging the battery which is powering the pump. It should be connected to a charging circuit to prevent overcharging of the battery. To safe energy a deep sleep function could be integrated so the sensor is not constantly reading values but rather every couple hours since the moisture will most likely not rapidly change. | To make it self sustainable it could be considered to use a PV-module for supplying power and charging the battery which is powering the pump. It should be connected to a charging circuit to prevent overcharging of the battery. To safe energy a deep sleep function could be integrated so the sensor is not constantly reading values but rather every couple hours since the moisture will most likely not rapidly change. | ||
When setting it up outside a stable connection to a local WiFi network needs to be possible. | When setting it up outside a stable connection to a local WiFi network needs to be possible. | ||
Line 559: | Line 616: | ||
* **Wi-Fi Dependency: | * **Wi-Fi Dependency: | ||
- | * **Lack of Enclosure or Waterproofing: | + | |
Line 567: | Line 624: | ||
- | {{youtube> | + | {{youtube> |
+ | |||
+ | {{youtube> | ||
+ | |||
+ | {{youtube> | ||
amc/ss2025/group-f/start.1753726913.txt.gz · Last modified: 2025/07/28 20:21 by 33784_students.hsrw