#include <WiFi.h>
#include <HTTPClient.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
// Define HC-SR04 Pins
#define TRIG_PIN 13 // Connects to ESP32 GPIO 13
// IMPORTANT: Confirm this matches your Wokwi wiring.
// Based on previous analysis, ECHO output (after voltage divider) was GPIO 12.
#define ECHO_PIN 27 // Connects to ESP32 GPIO 12 (Remember the voltage divider!)
// WiFi credentials (Wokwi simulation)
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// Google Apps Script Web App URL
// NOTE: Ensure this is the EXACT URL from your deployed web app
// with "Execute as: Me" and "Who has access: Anyone".
// It should end with /exec.
const char* serverName = "https://script.google.com/macros/s/AKfycbyoDdDMraTwneJa1TmCT5h9wZ57vJr2LUxsmqMTHBBb71T0tkzW-sFTjKwJVXTey1kNcQ/exec"; // <-- Replace with your actual URL
// NTP setup
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 7 * 3600, 60000); // GMT+7, update every 60s
unsigned long lastSendTime = 0;
const unsigned long sendInterval = 15000; // every 15 seconds
// Function to read distance from HC-SR04 sensor
float bacaJarakCM() {
// Clears the TRIG_PIN
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
// Sets the TRIG_PIN on HIGH state for 10 micro seconds
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// Reads the ECHO_PIN, returns the sound wave travel time in microseconds
long duration = pulseIn(ECHO_PIN, HIGH);
// Calculate the distance (speed of sound = 0.0343 cm/us)
// Distance = (Duration * Speed of Sound) / 2 (to sound and back)
float distance = (duration * 0.0343) / 2;
Serial.print("Jarak: ");
Serial.print(distance);
Serial.println(" cm");
return distance; // Return distance as a float
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("Memulai program...");
// Configure HC-SR04 pins
pinMode(TRIG_PIN, OUTPUT); // Sets the TRIG_PIN as an Output
pinMode(ECHO_PIN, INPUT); // Sets the ECHO_PIN as an Input
WiFi.begin(ssid, password);
Serial.print("Menghubungkan ke WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500); // Use 500ms delay during connection attempts
Serial.print(".");
}
Serial.println("\nTerhubung ke WiFi!"); // Use \n for newline
timeClient.begin();
// Initial time update might take a moment after WiFi connects
timeClient.update();
Serial.println("NTP time initialized.");
// randomSeed(analogRead(0)); // Not needed for actual sensor reading
}
// This is the updated loop function that uses GET requests and handles the 302 redirect
void loop() {
// Check if it's time to send data (every 15 seconds)
if (millis() - lastSendTime >= sendInterval) {
lastSendTime = millis(); // Update the last send time
float jarak = bacaJarakCM(); // Get the distance from the actual sensor
// Check if WiFi is connected before sending
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http; // Create an HTTPClient object
// Update time from NTP server
timeClient.update();
String waktu = timeClient.getFormattedTime(); // Get formatted time
// --- Construct the initial URL with data as GET parameters ---
// Data will be sent like: ?distance=XXX.XX×tamp=HH:MM:SS
String initialUrl = String(serverName) + "?distance=" + String(jarak) + "×tamp=" + waktu;
Serial.println("Mengirim data ke Google Sheets...");
Serial.println("Initial URL (GET): " + initialUrl); // Print the initial URL being used
// --- Step 1: Send the initial GET request ---
http.begin(initialUrl); // Start connection to the initial URL with parameters
// No need for Content-Type header with GET parameters
int httpResponseCode = http.GET(); // Send the GET request
Serial.println("Initial HTTP Response code: " + String(httpResponseCode));
// --- Step 2 & 3: Check for 302 redirect and get the new location ---
// HTTP_CODE_FOUND is a constant defined in HTTPClient.h for status code 302
if (httpResponseCode == HTTP_CODE_FOUND) {
String redirectUrl = http.getLocation(); // Get the URL from the 'Location' header
Serial.println("Received 302 redirect to: " + redirectUrl);
// Close the current connection before starting a new one
http.end();
// --- Step 4: Send a new GET request to the redirected URL ---
Serial.println("Retrying GET to redirected URL...");
// Note: The redirected URL from Google Apps Script /exec often points
// to the base /echo endpoint. The parameters sent in the *initial* GET
// request to /exec are typically preserved and available to the script
// when it executes via the /echo endpoint. So we just GET the redirect URL.
http.begin(redirectUrl); // Start connection to the new, redirected URL
int redirectResponseCode = http.GET(); // Send a GET request to the redirected URL
Serial.println("Redirected HTTP Response code: " + String(redirectResponseCode));
// Check the response code from the redirected request
if (redirectResponseCode > 0) { // Codes > 0 usually indicate success/response
String response = http.getString(); // Get the response body
Serial.println("Data sent successfully via redirect!");
Serial.println("Response: " + response);
// Expected successful response from script might be {"status":"success", ...}
} else {
// Handle errors from the redirected request
Serial.println("Failed to send data to redirected URL, error: " + String(redirectResponseCode));
Serial.println("Error details: " + http.errorToString(redirectResponseCode));
}
} else if (httpResponseCode > 0) {
// Handle successful response codes other than 302 (e.g., 200 OK)
// This might happen if Google changes redirect behavior or for different deployment types
String response = http.getString();
Serial.println("Data berhasil dikirim (no redirect)!");
Serial.println("HTTP Response code: " + String(httpResponseCode));
Serial.println("Response: " + response);
} else {
// Handle initial request errors (e.g., connection failed, 404, 401 if deployment/URL is wrong)
Serial.println("Gagal kirim data, kode error: " + String(httpResponseCode));
Serial.println("Error details: " + http.errorToString(httpResponseCode));
}
// Ensure the HTTP connection is closed if it wasn't already handled by a redirect
if (httpResponseCode != HTTP_CODE_FOUND) {
http.end(); // Close initial connection if not redirected
}
} else {
// Handle WiFi disconnected state
Serial.println("WiFi terputus");
// Optional: add WiFi.begin(ssid, password); here to try and reconnect
}
}
// The delay was in the other script, but our current loop uses millis() for timing.
// Adding a small delay here won't hurt and prevents hammering the CPU if interval is short,
// but the 15-second interval logic is already controlling send frequency.
// delay(100); // Optional: small delay
}