#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
// ---------------- WIFI (WOKWI) ---------------- //
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// ---------------- CLOUD RL ---------------- //
String serverURL = "https://xxxxx.ngrok-free.app/predict";
// ---------------- PINS ---------------- //
#define LATCH_PIN 5
#define CLOCK_PIN 18
#define DATA_PIN 19
#define SERVO_PIN 23
// ---------------- OBJECTS ---------------- //
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo gateServo;
// ---------------- VARIABLES ---------------- //
int totalSlots = 20;
int slotStatus[20];
unsigned long lastReconnect = 0;
// ---------------- WIFI CONNECT ---------------- //
void connectWiFi() {
Serial.println("\nš Connecting to Wokwi WiFi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nā
WiFi Connected!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
// ---------------- READ SLOTS ---------------- //
void readSlots() {
digitalWrite(LATCH_PIN, LOW);
delayMicroseconds(5);
digitalWrite(LATCH_PIN, HIGH);
for (int i = 0; i < totalSlots; i++) {
slotStatus[i] = digitalRead(DATA_PIN);
digitalWrite(CLOCK_PIN, HIGH);
delayMicroseconds(5);
digitalWrite(CLOCK_PIN, LOW);
}
}
// ---------------- COUNT FREE ---------------- //
int getFreeSlots() {
int freeCount = 0;
for (int i = 0; i < totalSlots; i++) {
if (slotStatus[i] == 0)
freeCount++;
}
return freeCount;
}
// ---------------- CLOUD RL ---------------- //
String getCloudDecision(int freeSlots) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("ā WiFi Lost");
return "ERROR";
}
HTTPClient http;
http.setTimeout(5000);
http.begin(serverURL);
http.addHeader("Content-Type", "application/json");
// ā
CORRECT RL FORMAT
String json = "{\"state\":[" + String(freeSlots) + ",0,0,0]}";
Serial.println("\nš¤ Sending: " + json);
int code = http.POST(json);
if (code > 0) {
String res = http.getString();
http.end();
Serial.println("š Response: " + res);
if (res.indexOf("\"action\":1") >= 0)
return "OPEN";
else
return "CLOSE";
}
http.end();
Serial.println("ā Cloud Error");
return "ERROR";
}
// ---------------- GATE CONTROL ---------------- //
void controlGate(String decision) {
if (decision == "OPEN") {
gateServo.write(90);
Serial.println("š Gate OPEN");
}
else {
gateServo.write(0);
Serial.println("ā Gate CLOSE");
}
}
// ---------------- LCD ---------------- //
void updateLCD(int freeSlots, String status) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Free: ");
lcd.print(freeSlots);
lcd.setCursor(0, 1);
lcd.print("Gate: ");
lcd.print(status);
}
// ---------------- SETUP ---------------- //
void setup() {
Serial.begin(115200);
pinMode(LATCH_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(DATA_PIN, INPUT);
lcd.init();
lcd.backlight();
gateServo.attach(SERVO_PIN);
gateServo.write(0);
connectWiFi();
}
// ---------------- LOOP ---------------- //
void loop() {
readSlots();
int freeSlots = getFreeSlots();
String decision = getCloudDecision(freeSlots);
// fallback safety only (NO spam logs)
if (decision == "ERROR") {
decision = (freeSlots > 5) ? "OPEN" : "CLOSE";
}
controlGate(decision);
updateLCD(freeSlots, decision);
Serial.print("Free Slots: ");
Serial.print(freeSlots);
Serial.print(" | Decision: ");
Serial.println(decision);
delay(2000);
}