Gabungkan semua koneksi — USB, Bluetooth, WiFi, MQTT — menjadi satu aplikasi Flutter pintar.
Buat Smart Environment Monitor — system yang bisa berkomunikasi via multiple koneksi, auto-switch connection, dan menyimpan data history.
Arsitektur lengkap: ESP32 multi-connection → Flutter App → Local DB
Satu abstraksi untuk mengelola semua jenis koneksi.
Dart/// Abstraksi koneksi — semua tipe koneksi implementasi ini
abstract class DeviceConnection {
String get name;
bool get isConnected;
Future<bool> connect();
void disconnect();
void sendCommand(Map<String, dynamic> data);
Stream<Map<String, dynamic>> get dataStream;
}
/// USB Serial connection
class UsbConnection implements DeviceConnection {
@override String get name => 'USB Serial';
// ... implementasi dari Minggu 5-6
}
/// Bluetooth BLE connection
class BleConnection implements DeviceConnection {
@override String get name => 'Bluetooth BLE';
// ... implementasi dari Minggu 9-10
}
/// WiFi WebSocket connection
class WsConnection implements DeviceConnection {
@override String get name => 'WiFi WebSocket';
// ... implementasi dari Minggu 13
}
/// MQTT connection
class MqttConnection implements DeviceConnection {
@override String get name => 'MQTT';
// ... implementasi dari Minggu 14
}
Dartimport 'package:flutter/material.dart';
enum ConnectionType { usb, ble, wifi, mqtt }
class ConnectionManager extends ChangeNotifier {
final Map<ConnectionType, DeviceConnection> _connections = {};
ConnectionType? _activeType;
DeviceConnection? _active;
ConnectionType? get activeType => _activeType;
DeviceConnection? get active => _active;
bool get isConnected => _active?.isConnected ?? false;
ConnectionManager() {
_connections[ConnectionType.usb] = UsbConnection();
_connections[ConnectionType.ble] = BleConnection();
_connections[ConnectionType.wifi] = WsConnection();
_connections[ConnectionType.mqtt] = MqttConnection();
}
/// Connect via tipe tertentu
Future<bool> connectVia(ConnectionType type) async {
// Disconnect yang lama
_active?.disconnect();
final conn = _connections[type]!;
final ok = await conn.connect();
if (ok) {
_active = conn;
_activeType = type;
notifyListeners();
}
return ok;
}
/// Auto-detect: coba koneksi dari yang paling baik
Future<bool> autoConnect() async {
// Prioritas: MQTT > WiFi > BLE > USB
final priority = [
ConnectionType.mqtt,
ConnectionType.wifi,
ConnectionType.ble,
ConnectionType.usb,
];
for (final type in priority) {
final ok = await connectVia(type);
if (ok) return true;
}
return false;
}
/// Kirim command via koneksi aktif
void send(Map<String, dynamic> data) {
_active?.sendCommand(data);
}
/// Stream data dari koneksi aktif
Stream<Map<String, dynamic>>? get dataStream =>
_active?.dataStream;
void disconnectAll() {
for (final conn in _connections.values) {
conn.disconnect();
}
_active = null;
_activeType = null;
notifyListeners();
}
}
C++ (Arduino)#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
// ======= PINS =======
#define LED_PIN 2
#define RELAY_PIN 4
#define LDR_PIN 34
#define DHT_PIN 15
// ======= WiFi =======
const char* AP_SSID = "ESP32_SmartEnv";
const char* AP_PASS = "12345678";
const char* STA_SSID = "WiFi_Rumah";
const char* STA_PASS = "password123";
// ======= MQTT =======
const char* MQTT_SERVER = "192.168.1.100";
const int MQTT_PORT = 1883;
// ======= BLE =======
#define SERVICE_UUID "12345678-1234-1234-1234-123456789abc"
#define CHAR_SENSOR_UUID "12345678-1234-1234-1234-123456789ab1"
#define CHAR_CONTROL_UUID "12345678-1234-1234-1234-123456789ab2"
// ======= Objects =======
AsyncWebServer httpServer(80);
AsyncWebSocket ws("/ws");
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
BLECharacteristic* sensorChar;
BLECharacteristic* controlChar;
bool bleClientConnected = false;
unsigned long lastSensorRead = 0;
// ======= Sensor data struct =======
struct SensorData {
float suhu;
float kelembaban;
int cahaya;
bool ledState;
bool relayState;
} sensorData;
// ======= Build JSON from sensor data =======
String buildJson() {
JsonDocument doc;
doc["suhu"] = sensorData.suhu;
doc["kelembaban"] = sensorData.kelembaban;
doc["cahaya"] = sensorData.cahaya;
doc["led"] = sensorData.ledState;
doc["relay"] = sensorData.relayState;
doc["uptime"] = millis() / 1000;
String json;
serializeJson(doc, json);
return json;
}
// ======= Handle command (from any connection) =======
void handleCommand(const char* json) {
JsonDocument doc;
deserializeJson(doc, json);
if (doc.containsKey("led")) {
sensorData.ledState = doc["led"].as<bool>();
digitalWrite(LED_PIN, sensorData.ledState ? HIGH : LOW);
}
if (doc.containsKey("relay")) {
sensorData.relayState = doc["relay"].as<bool>();
digitalWrite(RELAY_PIN, sensorData.relayState ? HIGH : LOW);
}
// Broadcast perubahan ke semua koneksi
broadcastAll();
}
// ======= Broadcast ke semua koneksi aktif =======
void broadcastAll() {
String json = buildJson();
// Serial
Serial.println(json);
// WebSocket
ws.textAll(json);
// BLE
if (bleClientConnected && sensorChar) {
sensorChar->setValue(json.c_str());
sensorChar->notify();
}
// MQTT
if (mqtt.connected()) {
mqtt.publish("sensor/data", json.c_str());
}
}
// ======= Setup semua koneksi =======
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(RELAY_PIN, OUTPUT);
// 1. WiFi AP+STA mode
WiFi.mode(WIFI_AP_STA);
WiFi.softAP(AP_SSID, AP_PASS);
WiFi.begin(STA_SSID, STA_PASS);
Serial.printf("AP IP: %s\n",
WiFi.softAPIP().toString().c_str());
// 2. WebSocket
ws.onEvent([](AsyncWebSocket *s, AsyncWebSocketClient *c,
AwsEventType type, void *arg, uint8_t *data, size_t len) {
if (type == WS_EVT_DATA) {
handleCommand((char*)data);
}
});
httpServer.addHandler(&ws);
httpServer.begin();
// 3. MQTT
mqtt.setServer(MQTT_SERVER, MQTT_PORT);
mqtt.setCallback([](char* topic, byte* payload,
unsigned int len) {
char buf[256];
memcpy(buf, payload, len);
buf[len] = '\0';
handleCommand(buf);
});
// 4. BLE setup
BLEDevice::init("ESP32_SmartEnv");
BLEServer* bleServer = BLEDevice::createServer();
BLEService* bleService =
bleServer->createService(SERVICE_UUID);
sensorChar = bleService->createCharacteristic(
CHAR_SENSOR_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY);
sensorChar->addDescriptor(new BLE2902());
controlChar = bleService->createCharacteristic(
CHAR_CONTROL_UUID,
BLECharacteristic::PROPERTY_WRITE);
// BLE control callback (simplified)
bleService->start();
BLEAdvertising* adv = BLEDevice::getAdvertising();
adv->start();
Serial.println("All protocols ready!");
}
void loop() {
// MQTT reconnect
if (WiFi.status() == WL_CONNECTED && !mqtt.connected()) {
mqtt.connect("ESP32_SmartEnv");
mqtt.subscribe("control/#");
}
mqtt.loop();
ws.cleanupClients();
// Serial command
if (Serial.available()) {
String cmd = Serial.readStringUntil('\n');
handleCommand(cmd.c_str());
}
// Baca sensor periodik
if (millis() - lastSensorRead > 1000) {
lastSensorRead = millis();
sensorData.cahaya = analogRead(LDR_PIN);
// DHT read (pseudo — gunakan library DHT)
// sensorData.suhu = dht.readTemperature();
// sensorData.kelembaban = dht.readHumidity();
sensorData.suhu = 25.0 + random(-20, 20) / 10.0;
sensorData.kelembaban = 60.0 + random(-50, 50) / 10.0;
broadcastAll();
}
}
Folder Structurelib/
├── main.dart
├── models/
│ └── sensor_data.dart # Data class
├── services/
│ ├── connection_manager.dart # Abstract + manager
│ ├── usb_connection.dart # USB Serial
│ ├── ble_connection.dart # BLE
│ ├── ws_connection.dart # WebSocket
│ ├── mqtt_connection.dart # MQTT
│ └── db_service.dart # Local database
├── providers/
│ └── app_provider.dart # ChangeNotifier
└── pages/
├── home_page.dart # Dashboard
├── controls_page.dart # LED / Relay kontrol
├── history_page.dart # Data chart
└── settings_page.dart # Connection setting
Dart — main.dartimport 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => ConnectionManager(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Smart Environment Monitor',
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: const Color(0xFF0f172a),
appBarTheme: const AppBarTheme(
backgroundColor: Color(0xFF1e293b),
),
),
home: const HomePage(),
);
}
}
| # | Task | Target |
|---|---|---|
| 1 | ESP32 multi-protocol firmware | Serial + BLE + WS + MQTT aktif bersamaan |
| 2 | ConnectionManager + Provider | Auto-detect dan switch koneksi |
| 3 | Dashboard page | Real-time sensor cards + mini chart |
| 4 | Controls page | LED + Relay toggle via koneksi aktif |
| 5 | History page | Chart + tabel data tersimpan (SQLite/Hive) |
| 6 | Settings page | Pilih koneksi, atur IP broker, scan BLE |