Community project

Dual-Server Drone Monitoring

Adnan khiar aditya rahmat

Published August 22, 2026

ESP32
Photo of Dual-Server Drone MonitoringGenerated with AI

This 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
Wiring diagram for Dual-Server Drone Monitoring

Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.

Parts list

Bill of materials
ComponentQtyNotes
ILI9341 TFT Touchscreen2.8 inch1240x320 SPI TFT display using the ILI9341 LCD controller with an XPT2046 resistive touch controller sharing the SPI bus
NEO-6M GPS ModuleNEO-6M1u-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 Keypad4x41A 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 SensorR3071Pemindai sidik jari yang menjadi syarat kedua sebelum dashboard dibuka.
Modul level shifter BSS138 4 kanal4-channel BSS1381modul kecil yang menurunkan dan menaikkan sinyal data antara sensor 5 V dan ESP32 3,3 V.
LED merah 5 mmMerah 5 mm1Lampu biasa merah yang menyala saat baterai server perlu diisi atau tidak terbaca.
LED kuning 5 mmKuning 5 mm1Lampu biasa kuning yang menunjukkan baterai server sedang berada di tingkat menengah.
LED hijau 5 mmHijau 5 mm1Lampu biasa hijau yang menunjukkan server menerima daya dan baterai dalam kondisi baik.
LED hijau 5 mmHijau 5 mm1Lampu biasa yang menyala bila drone pertama sedang online di server.
LED hijau 5 mmHijau 5 mm1Lampu biasa yang menyala bila drone kedua sedang online di server.
LED hijau 5 mmHijau 5 mm1Lampu biasa yang menyala bila drone ketiga sedang online di server.
LED hijau 5 mmHijau 5 mm1Lampu biasa yang menyala bila drone keempat sedang online di server.
LED hijau 5 mmHijau 5 mm1Lampu biasa yang menyala bila drone kelima sedang online di server.
LED hijau 5 mmHijau 5 mm1Lampu biasa yang menyala bila drone keenam sedang online di server.
Resistor220 Ω1Resistor pembatas arus untuk LED baterai merah.
Resistor220 Ω1Resistor pembatas arus untuk LED baterai kuning.
Resistor220 Ω1Resistor pembatas arus untuk LED baterai hijau.
Resistor220 Ω1Resistor pembatas arus untuk LED drone 1.
Resistor220 Ω1Resistor pembatas arus untuk LED drone 2.
Resistor220 Ω1Resistor pembatas arus untuk LED drone 3.
Resistor220 Ω1Resistor pembatas arus untuk LED drone 4.
Resistor220 Ω1Resistor pembatas arus untuk LED drone 5.
Resistor220 Ω1Resistor pembatas arus untuk LED drone 6.
74HC595 Shift Register74HC59518-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 Register74HC59518-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 steps
  1. Matikan 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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.
  9. 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.
  10. 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
PinConnectionType
3V3tft_touch VCCpower
GNDtft_touch GNDground
GPIO 11tft_touch MOSIspi
GPIO 13tft_touch MISOspi
GPIO 12tft_touch SCKspi
GPIO 14tft_touch TFT_CSdigital
GPIO 15tft_touch TFT_DCdigital
GPIO 16tft_touch TFT_RSTdigital
GPIO 17tft_touch TOUCH_CSdigital
3V3gps VCCpower
GNDgps GNDground
GPIO 21gps TXuart
GPIO 18gps RXuart
5Vfingerprint VCCpower
GNDfingerprint GNDground
GPIO 1keypad R1digital
GPIO 2keypad R2digital
GPIO 3keypad R3digital
GPIO 4keypad R4digital
GPIO 5keypad C1digital
GPIO 6keypad C2digital
GPIO 7keypad C3digital
GPIO 40keypad C4digital
EXTfingerprint TXModul level shifter BSS138 4 kanal HV1uart
EXTfingerprint RXModul level shifter BSS138 4 kanal HV2uart
3V3fingerprint_level_shifter LVpower
5Vfingerprint_level_shifter HVpower
GNDfingerprint_level_shifter GNDground
GPIO 38fingerprint_level_shifter LV1data
GPIO 39fingerprint_level_shifter LV2data
EXTres_battery_red BLED merah 5 mm ANODEdigital
GNDled_battery_red CATHODEground
EXTres_battery_yellow BLED kuning 5 mm ANODEdigital
GNDled_battery_yellow CATHODEground
EXTres_battery_green BLED hijau 5 mm ANODEdigital
GNDled_battery_green CATHODEground
EXTres_drone_1 BLED hijau 5 mm ANODEdigital
GNDled_drone_1 CATHODEground
EXTres_drone_2 BLED hijau 5 mm ANODEdigital
GNDled_drone_2 CATHODEground
EXTres_drone_3 BLED hijau 5 mm ANODEdigital
GNDled_drone_3 CATHODEground
EXTres_drone_4 BLED hijau 5 mm ANODEdigital
GNDled_drone_4 CATHODEground
EXTres_drone_5 BLED hijau 5 mm ANODEdigital
GNDled_drone_5 CATHODEground
EXTres_drone_6 BLED hijau 5 mm ANODEdigital
GNDled_drone_6 CATHODEground
3V3led_shift_1 VCCpower
GNDled_shift_1 GNDground
GPIO 41led_shift_1 DSdigital
GPIO 42led_shift_1 SH_CPdigital
GPIO 47led_shift_1 ST_CPdigital
GNDled_shift_1 OEground
3V3led_shift_1 MRpower
3V3led_shift_2 VCCpower
GNDled_shift_2 GNDground
GNDled_shift_2 OEground
3V3led_shift_2 MRpower
EXTled_shift_1 Q0-Q7Resistor Adata
EXTres_battery_yellow A74HC595 Shift Register Q0-Q7digital
EXTres_battery_green A74HC595 Shift Register Q0-Q7digital
EXTres_drone_1 A74HC595 Shift Register Q0-Q7digital
EXTres_drone_2 A74HC595 Shift Register Q0-Q7digital
EXTres_drone_3 A74HC595 Shift Register Q0-Q7digital
EXTres_drone_4 A74HC595 Shift Register Q0-Q7digital
EXTres_drone_5 A74HC595 Shift Register Q0-Q7digital
EXTled_shift_2 SH_CP74HC595 Shift Register SH_CPdigital
EXTled_shift_2 ST_CP74HC595 Shift Register ST_CPdigital
EXTled_shift_2 DS74HC595 Shift Register Q0-Q7digital
EXTled_shift_2 Q0-Q7Resistor Adata

Firmware

ESP32
main.cppDeploy to device
#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.

Open in Schematik