/* * OpenParanormal Multi-Tool * Professional High-Tech Paranormal Interface * * Design: Dark cyberpunk aesthetic with paranormal elements * - Matrix-style green/cyan accents on black * - Glowing indicators and pulsing animations * - Technical readouts with mystical icons * - Professional data visualization * * Hardware: * - ESP32 DevKit * - 3.5" ILI9486 TFT LCD Touchscreen (480x320) * - BME280 (Temp/Humidity/Pressure) * - Hall Effect Sensor (Magnetic) * - TEA5767 FM Radio Module * - PIR Motion Sensor * - 12-LED WS2812B Ring * - Speaker + Amplifier * * Features: * - Animated boot sequence * - Mode selection with icons * - Real-time sensor graphs * - Pulsing threat indicators * - Hex grid backgrounds * - Scan line effects */ #include #include #include #include #include TFT_eSPI tft = TFT_eSPI(); Adafruit_BME280 bme; Adafruit_NeoPixel ring(12, 25, NEO_GRB + NEO_KHZ800); // Color Palette - High-Tech Paranormal #define COLOR_BG 0x0000 // Pure black #define COLOR_PRIMARY 0x07FF // Cyan (tech) #define COLOR_SECONDARY 0x07E0 // Green (matrix/paranormal) #define COLOR_ACCENT 0xF81F // Magenta (anomaly) #define COLOR_WARNING 0xFD20 // Orange #define COLOR_CRITICAL 0xF800 // Red #define COLOR_TEXT 0xCE79 // Light grey #define COLOR_DIM 0x4208 // Dark grey (grid lines) #define COLOR_GLOW 0x07FF // Cyan glow effect // Pins #define HALL_PIN 34 #define PIR_PIN 33 #define LED_RING_PIN 25 // Device Modes enum Mode { MODE_MAIN, MODE_EMF, MODE_SPIRIT_BOX, MODE_OVILUS, MODE_REM_POD, MODE_SCANNER }; Mode currentMode = MODE_MAIN; unsigned long lastUpdate = 0; unsigned long scanlinePos = 0; bool anomalyDetected = false; float emfHistory[48] = {0}; // 48 data points for graph int historyIndex = 0; // Animation variables int pulsePhase = 0; bool bootComplete = false; void setup() { Serial.begin(115200); // Initialize display tft.init(); tft.setRotation(3); // Landscape (480x320) tft.fillScreen(COLOR_BG); // Initialize sensors Wire.begin(); if (!bme.begin(0x76)) { Serial.println("BME280 init failed!"); } pinMode(HALL_PIN, INPUT); pinMode(PIR_PIN, INPUT); // Initialize LED ring ring.begin(); ring.setBrightness(50); ring.show(); // Boot sequence bootSequence(); // Draw main menu drawMainMenu(); } void loop() { unsigned long now = millis(); // Animated scanline effect (runs continuously) if (now - lastUpdate > 50) { drawScanline(); pulsePhase = (pulsePhase + 5) % 360; lastUpdate = now; } // Update LED ring with ambient animation updateLEDRing(); // Check for touch input uint16_t touchX, touchY; bool pressed = tft.getTouch(&touchX, &touchY); if (pressed) { handleTouch(touchX, touchY); delay(200); // Debounce } // Update current mode display switch (currentMode) { case MODE_MAIN: // Main menu is static, just animate break; case MODE_EMF: updateEMFMode(); break; case MODE_SPIRIT_BOX: updateSpiritBoxMode(); break; case MODE_OVILUS: updateOvilusMode(); break; case MODE_REM_POD: updateREMPodMode(); break; case MODE_SCANNER: updateScannerMode(); break; } delay(10); } // ========== BOOT SEQUENCE ========== void bootSequence() { tft.fillScreen(COLOR_BG); // Draw hex grid background drawHexGrid(); // Animated title tft.setTextColor(COLOR_PRIMARY); tft.setTextSize(4); for (int i = 0; i < 12; i++) { tft.setCursor(80 + i*2, 100); tft.print("OPENPARANORMAL"); delay(50); } delay(300); // Subtitle with glow effect tft.setTextSize(2); tft.setTextColor(COLOR_SECONDARY); tft.setCursor(140, 150); tft.print("MULTI-SPECTRUM DETECTOR"); delay(500); // Initialize systems text tft.setTextSize(1); tft.setTextColor(COLOR_TEXT); const char* systems[] = { "> Initializing EMF sensors...", "> Calibrating environmental array...", "> Loading frequency scanner...", "> Syncing quantum resonance detector...", "> Establishing baseline readings...", "> System ready. Standing by..." }; int y = 200; for (int i = 0; i < 6; i++) { tft.setCursor(50, y); tft.setTextColor(COLOR_SECONDARY); tft.print(systems[i]); // LED ring pulse for (int j = 0; j < 12; j++) { ring.setPixelColor(j, ring.Color(0, 50, 50)); } ring.show(); delay(100); ring.clear(); ring.show(); delay(200); y += 15; } delay(1000); // Flash to main menu tft.fillScreen(COLOR_BG); bootComplete = true; } // ========== MAIN MENU ========== void drawMainMenu() { currentMode = MODE_MAIN; tft.fillScreen(COLOR_BG); // Draw subtle hex grid drawHexGrid(); // Header with glowing border tft.fillRect(0, 0, 480, 50, COLOR_BG); tft.drawRect(0, 0, 480, 50, COLOR_PRIMARY); tft.drawRect(1, 1, 478, 48, COLOR_PRIMARY); tft.setTextSize(3); tft.setTextColor(COLOR_PRIMARY); tft.setCursor(80, 15); tft.print("PARANORMAL TOOLKIT"); // Animated subtitle tft.setTextSize(1); tft.setTextColor(COLOR_SECONDARY); tft.setCursor(160, 60); tft.print("SELECT DETECTION MODE"); // Draw mode buttons with paranormal icons drawModeButton(20, 90, 140, 100, "EMF", "METER", 0); drawModeButton(170, 90, 140, 100, "SPIRIT", "BOX", 1); drawModeButton(320, 90, 140, 100, "OVILUS", "SCAN", 2); drawModeButton(20, 200, 140, 100, "REM", "POD", 3); drawModeButton(170, 200, 140, 100, "FULL", "SCANNER", 4); // Status bar at bottom drawStatusBar(); } void drawModeButton(int x, int y, int w, int h, const char* line1, const char* line2, int iconType) { // Outer glow tft.drawRoundRect(x-1, y-1, w+2, h+2, 8, COLOR_GLOW); // Main button tft.fillRoundRect(x, y, w, h, 8, 0x0841); // Very dark blue tft.drawRoundRect(x, y, w, h, 8, COLOR_PRIMARY); tft.drawRoundRect(x+1, y+1, w-2, h-2, 8, COLOR_PRIMARY); // Icon (simple geometric representation) int iconX = x + w/2; int iconY = y + 25; tft.setTextColor(COLOR_SECONDARY); switch(iconType) { case 0: // EMF - Lightning bolt tft.drawLine(iconX-5, iconY-10, iconX, iconY, COLOR_ACCENT); tft.drawLine(iconX, iconY, iconX-3, iconY, COLOR_ACCENT); tft.drawLine(iconX-3, iconY, iconX+5, iconY+10, COLOR_ACCENT); break; case 1: // Spirit Box - Radio waves tft.drawCircle(iconX, iconY, 8, COLOR_SECONDARY); tft.drawCircle(iconX, iconY, 12, COLOR_SECONDARY); tft.drawCircle(iconX, iconY, 16, COLOR_SECONDARY); break; case 2: // Ovilus - Eye symbol tft.drawEllipse(iconX, iconY, 15, 10, COLOR_ACCENT); tft.fillCircle(iconX, iconY, 5, COLOR_ACCENT); break; case 3: // REM Pod - Proximity rings tft.drawCircle(iconX, iconY, 6, COLOR_WARNING); tft.drawCircle(iconX, iconY, 11, COLOR_WARNING); tft.drawCircle(iconX, iconY, 16, COLOR_WARNING); tft.fillCircle(iconX, iconY, 3, COLOR_WARNING); break; case 4: // Scanner - Radar sweep tft.drawCircle(iconX, iconY, 15, COLOR_PRIMARY); tft.drawLine(iconX, iconY, iconX+12, iconY-8, COLOR_PRIMARY); break; } // Text tft.setTextSize(2); tft.setTextColor(COLOR_TEXT); int text1W = strlen(line1) * 12; int text2W = strlen(line2) * 12; tft.setCursor(x + (w - text1W)/2, y + h - 40); tft.print(line1); tft.setCursor(x + (w - text2W)/2, y + h - 20); tft.print(line2); } // ========== EMF MODE ========== void updateEMFMode() { static unsigned long lastRead = 0; if (millis() - lastRead < 100) return; lastRead = millis(); // Read EMF sensor int emfValue = analogRead(HALL_PIN); // Add to history emfHistory[historyIndex] = emfValue; historyIndex = (historyIndex + 1) % 48; // Clear graph area tft.fillRect(20, 80, 440, 180, COLOR_BG); // Draw graph with glow effect for (int i = 0; i < 47; i++) { int x1 = 20 + i * 9; int x2 = 20 + (i+1) * 9; int y1 = 260 - (emfHistory[i] / 20); int y2 = 260 - (emfHistory[(i+1)] / 20); // Glow line (thicker, dimmer) tft.drawLine(x1, y1, x2, y2, COLOR_DIM); // Main line tft.drawLine(x1, y1, x2, y2, COLOR_SECONDARY); } // Draw value tft.setTextSize(4); tft.setTextColor(COLOR_PRIMARY); tft.setCursor(200, 100); tft.fillRect(200, 100, 100, 40, COLOR_BG); tft.print(emfValue); // Threat level tft.setTextSize(2); if (emfValue < 500) { tft.setTextColor(COLOR_SECONDARY); tft.setCursor(200, 150); tft.print("BASELINE"); } else if (emfValue < 1500) { tft.setTextColor(COLOR_WARNING); tft.setCursor(200, 150); tft.print("ELEVATED"); } else { tft.setTextColor(COLOR_CRITICAL); tft.setCursor(200, 150); tft.print("ANOMALY!"); anomalyDetected = true; } } void drawEMFMode() { currentMode = MODE_EMF; tft.fillScreen(COLOR_BG); drawHexGrid(); // Header tft.fillRect(0, 0, 480, 60, COLOR_BG); tft.drawRect(0, 0, 480, 60, COLOR_PRIMARY); tft.setTextSize(3); tft.setTextColor(COLOR_PRIMARY); tft.setCursor(120, 15); tft.print("EMF DETECTOR"); // Back button tft.fillRoundRect(10, 270, 80, 40, 8, COLOR_DIM); tft.drawRoundRect(10, 270, 80, 40, 8, COLOR_TEXT); tft.setTextSize(2); tft.setTextColor(COLOR_TEXT); tft.setCursor(20, 280); tft.print("< BACK"); drawStatusBar(); } // ========== SPIRIT BOX MODE ========== void drawSpiritBoxMode() { currentMode = MODE_SPIRIT_BOX; tft.fillScreen(COLOR_BG); drawHexGrid(); // Header with scan effect tft.fillRect(0, 0, 480, 60, COLOR_BG); tft.drawRect(0, 0, 480, 60, COLOR_SECONDARY); tft.drawRect(1, 1, 478, 58, COLOR_SECONDARY); tft.setTextSize(3); tft.setTextColor(COLOR_SECONDARY); tft.setCursor(100, 15); tft.print("SPIRIT BOX"); // Frequency display tft.setTextSize(4); tft.setTextColor(COLOR_PRIMARY); tft.setCursor(150, 100); tft.print("98.5 MHz"); // Control buttons drawControlButton(20, 170, 100, 50, "SCAN", COLOR_SECONDARY); drawControlButton(130, 170, 100, 50, "REV", COLOR_PRIMARY); drawControlButton(240, 170, 100, 50, "FAST", COLOR_WARNING); drawControlButton(350, 170, 100, 50, "VOL+", COLOR_ACCENT); // Back button tft.fillRoundRect(10, 270, 80, 40, 8, COLOR_DIM); tft.drawRoundRect(10, 270, 80, 40, 8, COLOR_TEXT); tft.setTextSize(2); tft.setTextColor(COLOR_TEXT); tft.setCursor(20, 280); tft.print("< BACK"); drawStatusBar(); } void updateSpiritBoxMode() { // Add scanning animation static int scanPos = 0; scanPos = (scanPos + 2) % 480; // Draw scanning line tft.drawFastVLine(scanPos, 70, 90, COLOR_SECONDARY); tft.drawFastVLine((scanPos + 1) % 480, 70, 90, COLOR_DIM); } // ========== OVILUS MODE ========== void drawOvilusMode() { currentMode = MODE_OVILUS; tft.fillScreen(COLOR_BG); drawHexGrid(); // Mystical header tft.fillRect(0, 0, 480, 60, COLOR_BG); tft.drawRect(0, 0, 480, 60, COLOR_ACCENT); tft.drawRect(1, 1, 478, 58, COLOR_ACCENT); tft.setTextSize(3); tft.setTextColor(COLOR_ACCENT); tft.setCursor(140, 15); tft.print("OVILUS SCAN"); // Mode buttons drawControlButton(30, 80, 130, 50, "DICT", COLOR_PRIMARY); drawControlButton(170, 80, 130, 50, "PHON", COLOR_SECONDARY); drawControlButton(310, 80, 130, 50, "ENRG", COLOR_ACCENT); // Word display area (large, centered) tft.drawRoundRect(40, 150, 400, 80, 8, COLOR_ACCENT); tft.drawRoundRect(41, 151, 398, 78, 8, COLOR_ACCENT); tft.setTextSize(4); tft.setTextColor(COLOR_ACCENT); tft.setCursor(160, 175); tft.print("HELLO"); // Environmental readings (small, bottom) tft.setTextSize(1); tft.setTextColor(COLOR_TEXT); tft.setCursor(100, 245); tft.print("TEMP: 72F HUM: 45% MAG: 234"); // Back button tft.fillRoundRect(10, 270, 80, 40, 8, COLOR_DIM); tft.drawRoundRect(10, 270, 80, 40, 8, COLOR_TEXT); tft.setTextSize(2); tft.setTextColor(COLOR_TEXT); tft.setCursor(20, 280); tft.print("< BACK"); drawStatusBar(); } void updateOvilusMode() { // Pulsing word effect static int pulse = 0; pulse = (pulse + 5) % 360; int brightness = 128 + (int)(127 * sin(pulse * PI / 180)); uint16_t color = tft.color565(brightness, 0, brightness); // Update word with pulse tft.setTextSize(4); tft.setTextColor(color, COLOR_BG); tft.setCursor(160, 175); tft.print("HELLO"); } // ========== REM POD MODE ========== void drawREMPodMode() { currentMode = MODE_REM_POD; tft.fillScreen(COLOR_BG); drawHexGrid(); // Header tft.fillRect(0, 0, 480, 60, COLOR_BG); tft.drawRect(0, 0, 480, 60, COLOR_WARNING); tft.setTextSize(3); tft.setTextColor(COLOR_WARNING); tft.setCursor(140, 15); tft.print("REM POD"); // Central proximity indicator (radar style) int centerX = 240; int centerY = 150; tft.drawCircle(centerX, centerY, 40, COLOR_DIM); tft.drawCircle(centerX, centerY, 60, COLOR_DIM); tft.drawCircle(centerX, centerY, 80, COLOR_DIM); // Status tft.setTextSize(2); tft.setTextColor(COLOR_SECONDARY); tft.setCursor(180, 240); tft.print("MONITORING"); // Back button tft.fillRoundRect(10, 270, 80, 40, 8, COLOR_DIM); tft.drawRoundRect(10, 270, 80, 40, 8, COLOR_TEXT); tft.setTextSize(2); tft.setTextColor(COLOR_TEXT); tft.setCursor(20, 280); tft.print("< BACK"); drawStatusBar(); } void updateREMPodMode() { // Animated radar sweep static int angle = 0; angle = (angle + 5) % 360; int centerX = 240; int centerY = 150; int radius = 80; // Clear old sweep tft.fillCircle(centerX, centerY, radius, COLOR_BG); // Redraw circles tft.drawCircle(centerX, centerY, 40, COLOR_DIM); tft.drawCircle(centerX, centerY, 60, COLOR_DIM); tft.drawCircle(centerX, centerY, 80, COLOR_DIM); // Draw sweep line int x = centerX + (int)(radius * cos(angle * PI / 180)); int y = centerY + (int)(radius * sin(angle * PI / 180)); tft.drawLine(centerX, centerY, x, y, COLOR_WARNING); } // ========== FULL SCANNER MODE ========== void drawScannerMode() { currentMode = MODE_SCANNER; tft.fillScreen(COLOR_BG); drawHexGrid(); // Header tft.setTextSize(2); tft.setTextColor(COLOR_PRIMARY); tft.setCursor(150, 10); tft.print("FULL SPECTRUM SCAN"); // Multi-sensor display tft.setTextSize(1); tft.setTextColor(COLOR_TEXT); // EMF tft.setCursor(10, 40); tft.print("EMF:"); tft.setTextColor(COLOR_SECONDARY); tft.setCursor(60, 40); tft.print("127 mG"); // Temperature tft.setTextColor(COLOR_TEXT); tft.setCursor(10, 60); tft.print("TEMP:"); tft.setTextColor(COLOR_SECONDARY); tft.setCursor(60, 60); tft.print("72.3 F"); // Motion tft.setTextColor(COLOR_TEXT); tft.setCursor(10, 80); tft.print("MOTION:"); tft.setTextColor(COLOR_SECONDARY); tft.setCursor(80, 80); tft.print("NONE"); // Graph area tft.drawRect(10, 100, 460, 140, COLOR_PRIMARY); // Back button tft.fillRoundRect(10, 270, 80, 40, 8, COLOR_DIM); tft.drawRoundRect(10, 270, 80, 40, 8, COLOR_TEXT); tft.setTextSize(2); tft.setTextColor(COLOR_TEXT); tft.setCursor(20, 280); tft.print("< BACK"); drawStatusBar(); } void updateScannerMode() { // Update all sensor readings in real-time // (Similar to EMF mode but with multiple graphs) } // ========== UI HELPERS ========== void drawControlButton(int x, int y, int w, int h, const char* label, uint16_t color) { tft.fillRoundRect(x, y, w, h, 8, 0x0841); tft.drawRoundRect(x, y, w, h, 8, color); tft.setTextSize(2); tft.setTextColor(color); int textW = strlen(label) * 12; tft.setCursor(x + (w - textW)/2, y + (h - 16)/2); tft.print(label); } void drawHexGrid() { // Draw subtle hexagonal grid pattern for (int y = 0; y < 320; y += 30) { for (int x = 0; x < 480; x += 26) { int offsetY = (x / 26) % 2 == 0 ? 0 : 15; drawHexagon(x, y + offsetY, 12, COLOR_DIM); } } } void drawHexagon(int x, int y, int size, uint16_t color) { // Draw a small hexagon for (int i = 0; i < 6; i++) { int x1 = x + (int)(size * cos(i * 60 * PI / 180)); int y1 = y + (int)(size * sin(i * 60 * PI / 180)); int x2 = x + (int)(size * cos((i + 1) * 60 * PI / 180)); int y2 = y + (int)(size * sin((i + 1) * 60 * PI / 180)); tft.drawLine(x1, y1, x2, y2, color); } } void drawScanline() { // Animated scanline effect (CRT style) scanlinePos = (scanlinePos + 2) % 320; // Draw scanline tft.drawFastHLine(0, scanlinePos, 480, COLOR_DIM); // Erase old scanline if (scanlinePos > 2) { tft.drawFastHLine(0, scanlinePos - 2, 480, COLOR_BG); } } void drawStatusBar() { // Bottom status bar with system info tft.fillRect(0, 310, 480, 10, COLOR_BG); tft.drawFastHLine(0, 310, 480, COLOR_PRIMARY); tft.setTextSize(1); tft.setTextColor(COLOR_TEXT); // Battery tft.setCursor(10, 312); tft.print("PWR: 87%"); // Time tft.setCursor(200, 312); tft.print("UPTIME: 00:12:34"); // Status tft.setTextColor(COLOR_SECONDARY); tft.setCursor(400, 312); tft.print("ACTIVE"); } void updateLEDRing() { // Ambient pulsing effect on LED ring int brightness = 20 + (int)(15 * sin(pulsePhase * PI / 180)); if (anomalyDetected) { // Red pulsing if anomaly for (int i = 0; i < 12; i++) { ring.setPixelColor(i, ring.Color(brightness * 2, 0, 0)); } anomalyDetected = false; // Reset } else { // Cyan ambient for (int i = 0; i < 12; i++) { ring.setPixelColor(i, ring.Color(0, brightness, brightness)); } } ring.show(); } // ========== TOUCH HANDLING ========== void handleTouch(uint16_t x, uint16_t y) { Serial.print("Touch: "); Serial.print(x); Serial.print(", "); Serial.println(y); if (currentMode == MODE_MAIN) { // Main menu buttons if (y >= 90 && y <= 190) { if (x >= 20 && x <= 160) { drawEMFMode(); } else if (x >= 170 && x <= 310) { drawSpiritBoxMode(); } else if (x >= 320 && x <= 460) { drawOvilusMode(); } } else if (y >= 200 && y <= 300) { if (x >= 20 && x <= 160) { drawREMPodMode(); } else if (x >= 170 && x <= 310) { drawScannerMode(); } } } else { // Back button (all modes) if (x >= 10 && x <= 90 && y >= 270 && y <= 310) { drawMainMenu(); } } }