Community project
Dual-Server Drone Monitoring
Generated with AIThis project builds a dual-server drone monitoring station using an ESP32 microcontroller as the command center for tracking and controlling multiple drone units. The system combines a touchscreen display, GPS positioning, biometric security, and wireless networking to create a complete fleet management interface with real-time status monitoring and device control capabilities.
The guide provides a complete parts list, wiring diagram showing connections between the ILI9341 display, NEO-6M GPS module, R307 fingerprint sensor, 4x4 keypad, shift registers, and status LEDs, plus step-by-step assembly instructions and Arduino firmware. Builders will learn how to integrate multiple communication protocols, implement fingerprint-based login security, display live drone telemetry on the touchscreen, and manage a networked fleet of up to 24 connected devices.
Wiring diagram
Interactive · read-only
Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.
Parts list
Bill of materials| Component | Qty | Notes |
|---|---|---|
| ILI9341 TFT Touchscreen2.8 inch | 1 | 240x320 SPI TFT display using the ILI9341 LCD controller with an XPT2046 resistive touch controller sharing the SPI bus |
| NEO-6M GPS ModuleNEO-6M | 1 | u-blox NEO-6M based GPS receiver module. Outputs NMEA sentences (GGA, RMC, etc.) over UART at 9600 baud by default. Provides latitude, longitude, altitude, speed, and time. The NEO-6M module itself is a 3.3V-class device; many GY-NEO6MV2 breakout boards accept 5V on their VCC header through an onboard regulator, but UART I/O remains 3.3V-domain and must not be driven above 3.6V. Features an on-board patch antenna footprint and an SMA/IPEX connector for external active antenna (preferred for faster lock acquisition). Supply current ~45 mA in acquisition, ~11 mA in tracking. |
| 4x4 Matrix Keypad4x4 | 1 | A 16-button (4 rows × 4 columns) membrane matrix keypad that uses 8 digital I/O lines (4 row + 4 column) to scan all keys. No dedicated power rail is required — rows and columns are driven directly by GPIO. Compatible with the Arduino Keypad library. |
| R307 UART Fingerprint SensorR307 | 1 | Pemindai sidik jari yang menjadi syarat kedua sebelum dashboard dibuka. |
| Modul level shifter BSS138 4 kanal4-channel BSS138 | 1 | modul kecil yang menurunkan dan menaikkan sinyal data antara sensor 5 V dan ESP32 3,3 V. |
| LED merah 5 mmMerah 5 mm | 1 | Lampu biasa merah yang menyala saat baterai server perlu diisi atau tidak terbaca. |
| LED kuning 5 mmKuning 5 mm | 1 | Lampu biasa kuning yang menunjukkan baterai server sedang berada di tingkat menengah. |
| LED hijau 5 mmHijau 5 mm | 1 | Lampu biasa hijau yang menunjukkan server menerima daya dan baterai dalam kondisi baik. |
| LED hijau 5 mmHijau 5 mm | 1 | Lampu biasa yang menyala bila drone pertama sedang online di server. |
| LED hijau 5 mmHijau 5 mm | 1 | Lampu biasa yang menyala bila drone kedua sedang online di server. |
| LED hijau 5 mmHijau 5 mm | 1 | Lampu biasa yang menyala bila drone ketiga sedang online di server. |
| LED hijau 5 mmHijau 5 mm | 1 | Lampu biasa yang menyala bila drone keempat sedang online di server. |
| LED hijau 5 mmHijau 5 mm | 1 | Lampu biasa yang menyala bila drone kelima sedang online di server. |
| LED hijau 5 mmHijau 5 mm | 1 | Lampu biasa yang menyala bila drone keenam sedang online di server. |
| Resistor220 Ω | 1 | Resistor pembatas arus untuk LED baterai merah. |
| Resistor220 Ω | 1 | Resistor pembatas arus untuk LED baterai kuning. |
| Resistor220 Ω | 1 | Resistor pembatas arus untuk LED baterai hijau. |
| Resistor220 Ω | 1 | Resistor pembatas arus untuk LED drone 1. |
| Resistor220 Ω | 1 | Resistor pembatas arus untuk LED drone 2. |
| Resistor220 Ω | 1 | Resistor pembatas arus untuk LED drone 3. |
| Resistor220 Ω | 1 | Resistor pembatas arus untuk LED drone 4. |
| Resistor220 Ω | 1 | Resistor pembatas arus untuk LED drone 5. |
| Resistor220 Ω | 1 | Resistor pembatas arus untuk LED drone 6. |
| 74HC595 Shift Register74HC595 | 1 | 8-bit serial-in, parallel-out shift register with output latch. DS/SH_CP/ST_CP connect to MCU GPIO; Q0-Q7 are external outputs and should not consume MCU pins. |
| 74HC595 Shift Register74HC595 | 1 | 8-bit serial-in, parallel-out shift register with output latch. DS/SH_CP/ST_CP connect to MCU GPIO; Q0-Q7 are external outputs and should not consume MCU pins. |
Assembly
10 stepsMatikan daya dan siapkan papan
Letakkan ESP32-S3 DevKitC di meja yang tidak menghantarkan listrik dan jangan hubungkan USB dulu. Semua bagian yang memakai kabel ke board harus berbagi GND agar sinyalnya bisa dibaca dengan benar.
- Tip: Gunakan breadboard dan kabel jumper pendek supaya kabel tidak mudah lepas.
- ⚠ Jangan memasang atau mencabut kabel ketika USB tersambung karena salah sentuh dapat merusak board.
Pasang layar sentuh
Hubungkan layar 2,8 inci: VCC ke 3V3 (power), GND ke GND (ground), MOSI ke GPIO11 (data layar), MISO ke GPIO13 (data sentuh), SCK ke GPIO12 (clock), TFT_CS ke GPIO14 (pemilih layar), TFT_DC ke GPIO15 (perintah/data layar), TFT_RST ke GPIO16 (reset layar), dan TOUCH_CS ke GPIO17 (pemilih sentuhan).
- Tip: Pada beberapa layar MOSI disebut SDI dan SCK disebut CLK.
- ⚠ Pastikan VCC layar masuk ke 3V3, bukan 5V — tegangan salah dapat merusak layar.
Pasang GPS
Hubungkan modul GPS: VCC ke 3V3 (power), GND ke GND (ground), TX modul ke GPIO21 (lokasi masuk ke server), dan RX modul ke GPIO18 (data kembali ke GPS).
- Tip: Letakkan antena GPS dekat jendela atau di luar ruangan ketika menguji agar modul dapat menemukan satelit.
- ⚠ Jangan menukar TX dan RX; bila tertukar, lokasi tidak akan muncul di dashboard.
Pasang sensor sidik jari dengan pengaman tegangan
Hubungkan R307: VCC ke 5V (power) dan GND ke GND (ground). Pada modul BSS138, hubungkan LV ke 3V3 (power), HV ke 5V (power), dan GND ke GND (ground). Hubungkan TX R307 ke HV1 (data), LV1 ke GPIO38 (data masuk), RX R307 ke HV2 (data), lalu LV2 ke GPIO39 (data keluar).
- Tip: Sensor hanya mengenali jari yang sudah terdaftar di memorinya; bersihkan permukaannya sebelum dipakai.
- ⚠ Jangan menyambungkan TX sensor langsung ke ESP32 — sinyal 5 V dapat merusak pin 3,3 V tanpa modul BSS138.
Pasang keypad password
Hubungkan kabel keypad berdasarkan labelnya: R1 ke GPIO1 (baris tombol), R2 ke GPIO2 (baris tombol), R3 ke GPIO3 (baris tombol), R4 ke GPIO4 (baris tombol), C1 ke GPIO5 (kolom tombol), C2 ke GPIO6 (kolom tombol), C3 ke GPIO7 (kolom tombol), dan C4 ke GPIO40 (kolom tombol).
- Tip: Masukkan PIN lalu tekan #; tekan * bila ingin menghapus angka yang salah.
- ⚠ Jangan menebak urutan kabel pita keypad; periksa tulisan R1 hingga C4 pada keypad Anda.
Pasang dua IC pengendali LED
Pasang dua IC 74HC595 pada breadboard. Untuk IC pertama, VCC ke 3V3 (power), GND ke GND (ground), DS ke GPIO41 (data), SH_CP ke GPIO42 (clock), ST_CP ke GPIO47 (menyalakan data bersama), OE ke GND (mengaktifkan keluaran), dan MR ke 3V3 (mencegah reset). Pada IC kedua, VCC ke 3V3 (power), GND ke GND (ground), SH_CP ke SH_CP IC pertama (clock bersama), ST_CP ke ST_CP IC pertama (sinyal bersama), serta DS ke Q7' keluaran serial IC pertama (data berantai).
- Tip: Cari tanda titik atau lekukan pada badan IC; itu menandai ujung pin 1.
- ⚠ Jangan memasukkan IC terbalik atau memberi 5V pada VCC 74HC595, karena keluaran 5V dapat merusak pin ESP32 dan LED.
Pasang sembilan LED dua kaki biasa
Pasang setiap LED dua kaki biasa dengan resistor 220 Ω sendiri: Q0, Q1, Q2 IC pertama ke resistor lalu ke kaki panjang LED merah, kuning, hijau baterai (status daya); Q3 sampai Q7 IC pertama ke resistor lalu ke kaki panjang LED drone 1 sampai 5 (jumlah drone); Q0 IC kedua ke resistor lalu ke kaki panjang LED drone 6. Sambungkan seluruh kaki pendek LED ke GND (ground).
- Tip: Kaki LED yang panjang biasanya positif; sisi badan LED yang datar menandai kaki pendek/negatif. Tiga LED baterai dipasang sebagai satu kelompok, lalu sisakan ruang sebelum enam LED drone seperti model casing.
- ⚠ Setiap LED wajib memiliki resistor sendiri — LED tanpa resistor dapat rusak. Jangan mengganti LED ini dengan LED ring; rancangan ini untuk LED dua kaki biasa.
Cetak dan susun casing server
Buka file `data/server_enclosure.scad` di OpenSCAD dan cetak bagian `front`, `body`, `left_door`, `right_door`, serta `rear_door`. Masukkan pin engsel dari filamen 1,75 mm atau kawat lurus 1,5 mm setelah mencoba satu engsel dahulu. Pasang tiga LED baterai pada kelompok kiri atas dan enam LED drone pada kelompok kanan atas; panel bawah depan terisi penuh, dengan lubang fingerprint dan lubang docking di bidang miring.
- Tip: Ubah `LED_HOLE_D` menjadi 3.2 mm untuk LED 3 mm atau sekitar 5.2 mm untuk LED 5 mm sebelum mencetak. Ukur layar fisik dahulu lalu sesuaikan `DISPLAY_W` dan `DISPLAY_H` bila perlu.
- ⚠ Jangan memaksa modul layar atau LED ke lubang yang kekecilan karena plastik dapat retak. Lubang docking bukan jalur baterai langsung; gunakan hanya konektor dan rangkaian pengisian yang sudah dipilih.
Tempatkan penguat Wi-Fi berbaterai
Tempatkan penguat/router Wi-Fi portabel bertenaga USB 5V/2A atau power bank pada tray lepas-pasang di dalam casing atau di luar casing. Penguat terhubung ke server dan drone melalui Wi-Fi (data nirkabel), bukan kabel LAN ke ESP32. Untuk beberapa penguat yang saling meneruskan jangkauan, pakai perangkat yang benar-benar mendukung mesh/backhaul nirkabel dengan satu jaringan terenkripsi; repeater biasa tidak boleh dirangkai sembarang.
- Tip: Buka file `data/wifi_extender_mesh_plan.md` untuk diagram jaringan, daftar status layar extender, dan aturan sambungan antar-penguat.
- ⚠ Jangan menghubungkan baterai drone atau power bank langsung ke pin 3V3/5V ESP32. Jangan memakai rangkaian penguat berantai sebelum Anda memastikan perangkatnya mendukung mesh.
Nyalakan dan gunakan server
Periksa sekali lagi bahwa tidak ada kabel 3V3 atau 5V yang menyentuh GND. Hubungkan Server Utama ke USB, lalu tekan Deploy di Schematik. Setelah masuk menggunakan PIN dan sidik jari, sentuh PANGGIL SEMUA untuk mengirim perintah terenkripsi ke semua drone/node. Tombol A pada keypad mengirim kode Morse SOS untuk LED drone saat modul drone ditambahkan nanti.
- Tip: Buka `include/drone_template.h`, `src/fleet_protocol.h`, dan `data/wifi_extender_mesh_plan.md` sebagai file pendukung saat menambahkan drone atau penguat berikutnya.
- ⚠ Jangan membagikan kunci AES kepada pihak yang tidak dipercaya; kunci itu dipakai untuk memastikan hanya server resmi yang dapat mengirim perintah.
Pin assignments
Board wiring reference| Pin | Connection | Type |
|---|---|---|
| 3V3 | tft_touch VCC | power |
| GND | tft_touch GND | ground |
| GPIO 11 | tft_touch MOSI | spi |
| GPIO 13 | tft_touch MISO | spi |
| GPIO 12 | tft_touch SCK | spi |
| GPIO 14 | tft_touch TFT_CS | digital |
| GPIO 15 | tft_touch TFT_DC | digital |
| GPIO 16 | tft_touch TFT_RST | digital |
| GPIO 17 | tft_touch TOUCH_CS | digital |
| 3V3 | gps VCC | power |
| GND | gps GND | ground |
| GPIO 21 | gps TX | uart |
| GPIO 18 | gps RX | uart |
| 5V | fingerprint VCC | power |
| GND | fingerprint GND | ground |
| GPIO 1 | keypad R1 | digital |
| GPIO 2 | keypad R2 | digital |
| GPIO 3 | keypad R3 | digital |
| GPIO 4 | keypad R4 | digital |
| GPIO 5 | keypad C1 | digital |
| GPIO 6 | keypad C2 | digital |
| GPIO 7 | keypad C3 | digital |
| GPIO 40 | keypad C4 | digital |
| EXT | fingerprint TX → Modul level shifter BSS138 4 kanal HV1 | uart |
| EXT | fingerprint RX → Modul level shifter BSS138 4 kanal HV2 | uart |
| 3V3 | fingerprint_level_shifter LV | power |
| 5V | fingerprint_level_shifter HV | power |
| GND | fingerprint_level_shifter GND | ground |
| GPIO 38 | fingerprint_level_shifter LV1 | data |
| GPIO 39 | fingerprint_level_shifter LV2 | data |
| EXT | res_battery_red B → LED merah 5 mm ANODE | digital |
| GND | led_battery_red CATHODE | ground |
| EXT | res_battery_yellow B → LED kuning 5 mm ANODE | digital |
| GND | led_battery_yellow CATHODE | ground |
| EXT | res_battery_green B → LED hijau 5 mm ANODE | digital |
| GND | led_battery_green CATHODE | ground |
| EXT | res_drone_1 B → LED hijau 5 mm ANODE | digital |
| GND | led_drone_1 CATHODE | ground |
| EXT | res_drone_2 B → LED hijau 5 mm ANODE | digital |
| GND | led_drone_2 CATHODE | ground |
| EXT | res_drone_3 B → LED hijau 5 mm ANODE | digital |
| GND | led_drone_3 CATHODE | ground |
| EXT | res_drone_4 B → LED hijau 5 mm ANODE | digital |
| GND | led_drone_4 CATHODE | ground |
| EXT | res_drone_5 B → LED hijau 5 mm ANODE | digital |
| GND | led_drone_5 CATHODE | ground |
| EXT | res_drone_6 B → LED hijau 5 mm ANODE | digital |
| GND | led_drone_6 CATHODE | ground |
| 3V3 | led_shift_1 VCC | power |
| GND | led_shift_1 GND | ground |
| GPIO 41 | led_shift_1 DS | digital |
| GPIO 42 | led_shift_1 SH_CP | digital |
| GPIO 47 | led_shift_1 ST_CP | digital |
| GND | led_shift_1 OE | ground |
| 3V3 | led_shift_1 MR | power |
| 3V3 | led_shift_2 VCC | power |
| GND | led_shift_2 GND | ground |
| GND | led_shift_2 OE | ground |
| 3V3 | led_shift_2 MR | power |
| EXT | led_shift_1 Q0-Q7 → Resistor A | data |
| EXT | res_battery_yellow A → 74HC595 Shift Register Q0-Q7 | digital |
| EXT | res_battery_green A → 74HC595 Shift Register Q0-Q7 | digital |
| EXT | res_drone_1 A → 74HC595 Shift Register Q0-Q7 | digital |
| EXT | res_drone_2 A → 74HC595 Shift Register Q0-Q7 | digital |
| EXT | res_drone_3 A → 74HC595 Shift Register Q0-Q7 | digital |
| EXT | res_drone_4 A → 74HC595 Shift Register Q0-Q7 | digital |
| EXT | res_drone_5 A → 74HC595 Shift Register Q0-Q7 | digital |
| EXT | led_shift_2 SH_CP → 74HC595 Shift Register SH_CP | digital |
| EXT | led_shift_2 ST_CP → 74HC595 Shift Register ST_CP | digital |
| EXT | led_shift_2 DS → 74HC595 Shift Register Q0-Q7 | digital |
| EXT | led_shift_2 Q0-Q7 → Resistor A | data |
Firmware
ESP32#include <Arduino.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#include <BLEDevice.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <XPT2046_Touchscreen.h>
#include <Keypad.h>
#include <Adafruit_Fingerprint.h>
#include <TinyGPS++.h>
#include "fleet_protocol.h"
// Struktur data perangkat yang terhubung
struct Device {
char id[20];
bool drone;
bool online;
int rssi;
float lat, lon;
bool camera, audio;
uint32_t lastSeen;
};
// Deklarasi fungsi
void writeLedBits(uint16_t bits);
uint8_t onlineDrones();
void setIndicators();
void header(const char *text);
void loginScreen();
void dashboard();
IPAddress broadcastIP();
void broadcast(const char *message);
void callAll();
void morseAll();
void updateDevice(char *message);
void receivePackets();
void handleKeys();
void scanFinger();
void handleTouch();
constexpr int TFT_CS = 14;
constexpr int TFT_DC = 15;
constexpr int TFT_RST = 16;
constexpr int TOUCH_CS = 17;
constexpr int GPS_RX_PIN = 21;
constexpr int GPS_TX_PIN = 18;
constexpr int FINGER_RX_PIN = 38;
constexpr int FINGER_TX_PIN = 39;
constexpr int LED_SHIFT_DATA = 41;
constexpr int LED_SHIFT_CLOCK = 42;
constexpr int LED_SHIFT_LATCH = 47;
constexpr uint32_t AUTO_LOCK_MS = 15UL * 60UL * 1000UL;
constexpr char SERVER_ID[] = "SERVER_1";
constexpr char WIFI_SSID[] = "CONTROL_CENTER_WIFI";
constexpr char WIFI_PASSWORD[] = "CHANGE_THIS_WIFI_PASSWORD";
constexpr char LOGIN_PASSWORD[] = "2468";
Device devices[24];
uint8_t deviceCount = 0;
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
XPT2046_Touchscreen touch(TOUCH_CS);
HardwareSerial gpsSerial(1), fingerSerial(2);
TinyGPSPlus gps;
Adafruit_Fingerprint finger(&fingerSerial);
WiFiUDP udp;
const byte ROWS = 4, COLS = 4;
byte rows[ROWS] = {1, 2, 3, 4};
byte cols[COLS] = {5, 6, 7, 40};
char keymap[ROWS][COLS] = {{'1','2','3','A'}, {'4','5','6','B'}, {'7','8','9','C'}, {'*','0','#','D'}};
Keypad keypad = Keypad(makeKeymap(keymap), rows, cols, ROWS, COLS);
bool passwordOK = false, fingerOK = false, open = false;
String entered;
uint32_t lastActivity = 0, lastBeacon = 0;
void writeLedBits(uint16_t bits) {
digitalWrite(LED_SHIFT_LATCH, LOW);
// IC kedua menerima byte tinggi lebih dulu; Q0 IC kedua adalah LED drone ke-6.
shiftOut(LED_SHIFT_DATA, LED_SHIFT_CLOCK, MSBFIRST, (uint8_t)(bits >> 8));
shiftOut(LED_SHIFT_DATA, LED_SHIFT_CLOCK, MSBFIRST, (uint8_t)(bits & 0xFF));
digitalWrite(LED_SHIFT_LATCH, HIGH);
}
uint8_t onlineDrones() {
uint8_t count = 0;
for (uint8_t i = 0; i < deviceCount; i++) if (devices[i].online && devices[i].drone) count++;
return count;
}
void setIndicators() {
uint16_t bits = 0;
// Bit 0=merah, 1=kuning, 2=hijau; bit 3 sampai 8=drone 1 sampai 6.
if (WiFi.isConnected()) bits |= (1U << 2);
else bits |= (1U << 1);
uint8_t drones = onlineDrones();
for (uint8_t i = 0; i < drones && i < 6; i++) bits |= (1U << (3 + i));
writeLedBits(bits);
}
void header(const char *text) {
tft.fillRect(0, 0, 320, 28, ILI9341_DARKCYAN);
tft.setTextSize(2); tft.setTextColor(ILI9341_WHITE); tft.setCursor(6, 6); tft.print(text);
}
void loginScreen() {
tft.fillScreen(ILI9341_BLACK); header("SERVER TERKUNCI");
tft.setTextSize(2); tft.setTextColor(ILI9341_WHITE); tft.setCursor(12, 48); tft.print("PASSWORD + SIDIK JARI");
tft.setTextSize(1); tft.setCursor(12, 84); tft.print("PIN: ");
for (uint8_t i = 0; i < entered.length(); i++) tft.print('*');
tft.setCursor(12, 108); tft.printf("Password: %s", passwordOK ? "OK" : "menunggu");
tft.setCursor(12, 124); tft.printf("Sidik jari: %s", fingerOK ? "OK" : "tempelkan jari");
}
void dashboard() {
tft.fillScreen(ILI9341_BLACK); header("SERVER UTAMA");
tft.setTextSize(1); tft.setTextColor(ILI9341_CYAN); tft.setCursor(8, 34);
tft.printf("Wi-Fi:%s Drone:%u", WiFi.isConnected() ? "OK" : "PUTUS", onlineDrones());
tft.setCursor(8, 48);
if (gps.location.isValid()) tft.printf("GPS %.4f, %.4f", gps.location.lat(), gps.location.lng());
else tft.print("GPS: menunggu sinyal satelit");
tft.fillRoundRect(8, 62, 190, 34, 5, ILI9341_RED); tft.setTextSize(2); tft.setTextColor(ILI9341_WHITE); tft.setCursor(18, 72); tft.print("PANGGIL SEMUA");
tft.fillRoundRect(210, 62, 100, 34, 5, ILI9341_DARKGREY); tft.setTextSize(1); tft.setCursor(235, 75); tft.print("LOGOUT");
tft.setTextColor(ILI9341_YELLOW); tft.setCursor(8, 108); tft.print("Tombol A: kirim kode Morse SOS");
tft.setTextColor(ILI9341_WHITE);
for (uint8_t i = 0; i < deviceCount && i < 8; i++) {
tft.setCursor(8, 123 + i * 13);
tft.printf("%s %s %ddBm KAM:%s MIK:%s", devices[i].id, devices[i].online ? "ONLINE" : "OFFLINE", devices[i].rssi, devices[i].camera ? "YA" : "-", devices[i].audio ? "YA" : "-");
}
setIndicators();
}
IPAddress broadcastIP() {
IPAddress ip = WiFi.localIP(), mask = WiFi.subnetMask();
return IPAddress(ip[0] | ~mask[0], ip[1] | ~mask[1], ip[2] | ~mask[2], ip[3] | ~mask[3]);
}
void broadcast(const char *message) {
if (!WiFi.isConnected()) return;
udp.beginPacket(broadcastIP(), FLEET_PORT); udp.print(message); udp.endPacket();
}
// Perintah jaringan dipertahankan dalam bahasa teknis agar firmware drone cocok.
void callAll() { char message[80]; snprintf(message, sizeof(message), "CALL_ALL|%s|%lu", SERVER_ID, (unsigned long)millis()); broadcast(message); }
void morseAll() { broadcast("MORSE_ALL|SERVER_1|...---..."); }
void updateDevice(char *message) {
char *tag = strtok(message, "|");
if (!tag || strcmp(tag, "HELLO")) return;
char *id = strtok(nullptr, "|"), *kind = strtok(nullptr, "|"), *lat = strtok(nullptr, "|"), *lon = strtok(nullptr, "|");
char *cam = strtok(nullptr, "|"), *mic = strtok(nullptr, "|");
if (!id || !kind || !lat || !lon || !cam || !mic) return;
uint8_t index = deviceCount;
for (uint8_t i = 0; i < deviceCount; i++) if (!strcmp(devices[i].id, id)) { index = i; break; }
if (index == deviceCount && deviceCount < 24) deviceCount++;
if (index >= 24) return;
strncpy(devices[index].id, id, 19); devices[index].id[19] = 0;
devices[index].drone = !strcmp(kind, "DRONE"); devices[index].online = true; devices[index].rssi = WiFi.RSSI();
devices[index].lat = atof(lat); devices[index].lon = atof(lon); devices[index].camera = atoi(cam); devices[index].audio = atoi(mic); devices[index].lastSeen = millis();
}
void receivePackets() {
int length;
while ((length = udp.parsePacket()) > 0) {
char buffer[192]; int readCount = udp.read(buffer, min(length, 191));
if (readCount > 0) { buffer[readCount] = 0; updateDevice(buffer); }
}
}
void handleKeys() {
char key = keypad.getKey(); if (!key) return; lastActivity = millis();
if (open) { if (key == 'A') { morseAll(); dashboard(); } return; }
if (key >= '0' && key <= '9' && entered.length() < 12) entered += key;
else if (key == '*') entered = "";
else if (key == '#') { passwordOK = (entered == LOGIN_PASSWORD); entered = ""; }
if (passwordOK && fingerOK) open = true;
if (open) dashboard(); else loginScreen();
}
void scanFinger() {
if (open || fingerOK || finger.getImage() != FINGERPRINT_OK) return;
if (finger.image2Tz() == FINGERPRINT_OK && finger.fingerFastSearch() == FINGERPRINT_OK) {
fingerOK = true; lastActivity = millis(); if (passwordOK) open = true;
if (open) dashboard(); else loginScreen();
}
}
void handleTouch() {
if (!open || !touch.touched()) return;
TS_Point point = touch.getPoint(); int x = map(point.y, 380, 3700, 0, 320), y = map(point.x, 250, 3800, 0, 240);
lastActivity = millis();
if (y >= 62 && y <= 96 && x >= 8 && x <= 198) callAll();
if (y >= 62 && y <= 96 && x >= 210) { passwordOK = false; fingerOK = false; open = false; entered = ""; loginScreen(); }
delay(180);
}
void setup() {
Serial.begin(115200);
pinMode(LED_SHIFT_DATA, OUTPUT); pinMode(LED_SHIFT_CLOCK, OUTPUT); pinMode(LED_SHIFT_LATCH, OUTPUT); writeLedBits(0);
SPI.begin(12, 13, 11); tft.begin(); tft.setRotation(1); touch.begin(); touch.setRotation(1);
gpsSerial.begin(9600, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
fingerSerial.begin(57600, SERIAL_8N1, FINGER_RX_PIN, FINGER_TX_PIN); finger.verifyPassword();
WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); udp.begin(FLEET_PORT);
// Bluetooth hanya menyiarkan ID server; telemetri utama memakai Wi-Fi.
BLEDevice::init(SERVER_ID); BLEDevice::getAdvertising()->start();
lastActivity = millis(); loginScreen(); setIndicators();
}
void loop() {
while (gpsSerial.available()) gps.encode(gpsSerial.read());
receivePackets(); handleKeys(); scanFinger(); handleTouch();
bool changed = false;
for (uint8_t i = 0; i < deviceCount; i++) if (devices[i].online && millis() - devices[i].lastSeen > DEVICE_TIMEOUT_MS) { devices[i].online = false; changed = true; }
if (changed && open) dashboard();
if (millis() - lastBeacon > 2000 && WiFi.isConnected()) { lastBeacon = millis(); broadcast("BEACON|SERVER_1"); }
if (open && millis() - lastActivity > AUTO_LOCK_MS) { passwordOK = false; fingerOK = false; open = false; entered = ""; loginScreen(); }
static uint32_t indicatorTick = 0;
if (millis() - indicatorTick > 500) { indicatorTick = millis(); setIndicators(); }
}“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.
Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.