A Flutter application designed to communicate with and control an ESP32 microcontroller over WiFi.
This application provides a user interface to send control commands to an ESP32 device and monitor input pin states. It establishes communication over WiFi, sending data to the ESP32 web server to control GPIO pins and receiving real-time updates from connected sensors or switches.
- WiFi-based communication with ESP32
- Control multiple GPIO pins from the Flutter app
- Monitor status of 5 input pins in real-time
- Read sensor data or switch states from the ESP32
- Clean, responsive UI for mobile devices
- Cross-platform support (iOS, Android)
- Flutter (v3.0.0 or higher)
- An ESP32 development board
- Arduino IDE with ESP32 board support
-
Flutter App Setup
- Clone this repository
- Run
flutter pub getto install dependencies - Configure the ESP32 IP address in the app configuration
-
ESP32 Setup
- Flash the ESP32 with the code provided below
- Update WiFi credentials in the ESP32 code
Upload the following code to your ESP32 using Arduino IDE:
#include <WiFi.h>
#include <WebServer.h> // Use the standard WebServer library
// --- Wi-Fi Credentials ---
const char* ssid = "YOUR_WIFI_SSID"; // Replace with your Wi-Fi network name
const char* password = "YOUR_WIFI_PASSWORD"; // Replace with your Wi-Fi password
// --- Web Server Setup ---
WebServer server(80); // Create a web server object on port 80
// --- GPIO Pins for Output Control (Existing Example) ---
// Make sure these pins are safe to use as outputs on your specific ESP32 board
const int outputPins[] = {2, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22}; // Example GPIOs for output
const int numOutputPins = sizeof(outputPins) / sizeof(outputPins[0]); // Should match Flutter app (13)
// --- GPIO Pins for Input Reading (NEW) ---
// Choose 5 pins suitable for input. Pins 34-39 are input-only and good choices.
// Other pins like 23, 25, 26, 27, 32, 33 are also often available.
// Avoid pins already used for output or special functions (e.g., UART0 TX/RX).
const int inputPins[] = {34, 35, 36, 39, 23}; // Example: Using 4 input-only pins + GPIO23
const int numInputPins = sizeof(inputPins) / sizeof(inputPins[0]); // Should be 5
// --- Handler for incoming data to CONTROL outputs ---
void handleUpdate() {
// Check if the request method is POST and if there's plain text data
if (server.method() == HTTP_POST && server.hasArg("plain")) {
String receivedData = server.arg("plain"); // Get the plain text body
Serial.print("Received data for output control: ");
Serial.println(receivedData);
// --- Data Validation ---
if (receivedData.length() == numOutputPins) { // Check if the length matches expected (13)
bool validData = true;
for (int i = 0; i < receivedData.length(); i++) {
if (receivedData[i] != '0' && receivedData[i] != '1') {
validData = false;
break;
}
}
if (validData) {
// --- Process the received data ---
// Control GPIO pins based on the received string
for (int i = 0; i < numOutputPins; i++) {
// Ensure index is within bounds for outputPins array
if (i < (sizeof(outputPins) / sizeof(outputPins[0]))) {
int pinState = (receivedData[i] == '1') ? HIGH : LOW;
digitalWrite(outputPins[i], pinState);
Serial.printf("Set Output Pin %d (%d) to %s\n", i + 1, outputPins[i], (pinState == HIGH) ? "HIGH" : "LOW");
}
}
server.send(200, "text/plain", "Output data received successfully!");
} else {
Serial.println("Error: Invalid characters in output data.");
server.send(400, "text/plain", "Invalid output data format: Only '0' or '1' allowed.");
}
} else {
Serial.print("Error: Invalid output data length. Expected ");
Serial.print(numOutputPins);
Serial.print(", Got ");
Serial.println(receivedData.length());
server.send(400, "text/plain", "Invalid output data length. Expected 13 characters.");
}
} else {
Serial.println("Error: Invalid request format for /update.");
server.send(400, "text/plain", "Invalid request for /update. Use POST with plain text body.");
}
}
// --- Handler to GET input pin states (NEW) ---
void handleGetStates() {
if (server.method() != HTTP_GET) {
server.send(405, "text/plain", "Method Not Allowed");
return;
}
String pinStates = ""; // String to hold the pin states (e.g., "10110")
Serial.print("Reading input pins: ");
for (int i = 0; i < numInputPins; i++) {
int state = digitalRead(inputPins[i]); // Read the state of the current input pin
pinStates += (state == HIGH) ? '1' : '0'; // Append '1' for HIGH, '0' for LOW
Serial.printf("Pin %d (%d) = %d; ", i + 1, inputPins[i], state);
}
Serial.println(); // New line after printing states
Serial.print("Sending states: ");
Serial.println(pinStates);
// Send the combined state string back to the client (Flutter app)
server.send(200, "text/plain", pinStates);
}
// --- Handler for root URL (Optional) ---
void handleRoot() {
String html = "<html><body><h1>ESP32 Server Running</h1>";
html += "<p>Send POST requests to /update (expects ";
html += String(numOutputPins);
html += " '0' or '1' chars) to control outputs.</p>";
html += "<p>Send GET requests to /getStates to read ";
html += String(numInputPins);
html += " input pin states.</p>";
html += "</body></html>";
server.send(200, "text/html", html);
}
// --- Handler for Not Found ---
void handleNotFound() {
server.send(404, "text/plain", "Not Found");
}
void setup() {
Serial.begin(115200); // Start serial communication for debugging
Serial.println("\nESP32 Web Server Starting...");
// --- Configure OUTPUT Pins (Existing Example) ---
Serial.println("Configuring output pins...");
for (int i = 0; i < numOutputPins; i++) {
pinMode(outputPins[i], OUTPUT);
digitalWrite(outputPins[i], LOW); // Start with all pins LOW
}
Serial.println("Output pins configured.");
// --- Configure INPUT Pins (NEW) ---
Serial.println("Configuring input pins...");
for (int i = 0; i < numInputPins; i++) {
// Use INPUT_PULLUP if your switches connect the pin to GND when active.
// Use INPUT if you have external pull-up/pull-down resistors or the sensor actively drives the pin high/low.
pinMode(inputPins[i], INPUT_PULLUP);
// pinMode(inputPins[i], INPUT); // Alternative if using external resistors or active sensors
Serial.printf("Input Pin %d (%d) configured.\n", i + 1, inputPins[i]);
}
Serial.println("Input pins configured.");
// --- Connect to Wi-Fi ---
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP()); // Print the ESP32's IP address
// --- Setup Web Server Routes ---
server.on("/", HTTP_GET, handleRoot); // Handler for the root path
server.on("/update", HTTP_POST, handleUpdate); // Handler for controlling outputs
server.on("/getStates", HTTP_GET, handleGetStates); // Handler for reading inputs (NEW)
server.onNotFound(handleNotFound); // Handler for 404 errors
// --- Start Server ---
server.begin();
Serial.println("HTTP server started. Listening for requests...");
}
void loop() {
// Handle incoming client requests
server.handleClient();
// You can add other non-blocking tasks here if needed
// delay(10); // Small delay can sometimes help stability, but avoid long delays
}The application supports bidirectional communication with the ESP32:
-
Output Control: The Flutter app sends a string of '0's and '1's to the ESP32's
/updateendpoint via HTTP POST request. Each character corresponds to an output GPIO pin state (0 = OFF, 1 = ON). -
Input Monitoring: The app can send GET requests to
/getStatesendpoint to retrieve the current states of the 5 input pins. The ESP32 returns a 5-character string where each character represents an input pin state (0 = LOW, 1 = HIGH).
For reliable operation:
- Ensure proper external circuits for input pins (pull-up/pull-down resistors as needed)
- The ESP32 code uses INPUT_PULLUP mode by default for switch/button inputs
- For sensors that actively drive pins high/low, modify the code to use INPUT mode
This project follows clean architecture principles with:
- Data layer: Handles API communication with the ESP32
- Domain layer: Contains business logic and entities
- Presentation layer: Manages UI components and user interactions
This project is licensed under the MIT License - see the LICENSE file for details.