/* * Ovilus Clone with Touchscreen * OpenParanormal Project * * Maps environmental sensor readings to words in a database * Displays on touchscreen with mode selection * * Hardware: * - ESP32 DevKit * - 2.8" or 3.5" ILI9341 TFT LCD Touchscreen * - BME280 Temperature/Humidity/Pressure Sensor * - Hall Effect Sensor (magnetic field) * - Optional: DFPlayer Mini for audio output * * Wiring: * BME280: * VCC → 3.3V * GND → GND * SDA → GPIO 21 * SCL → GPIO 22 * * Hall Sensor: * VCC → 3.3V * GND → GND * OUT → GPIO 34 * * Libraries needed: * - TFT_eSPI * - Adafruit BME280 * - Adafruit Unified Sensor */ #include #include #include #include TFT_eSPI tft = TFT_eSPI(); Adafruit_BME280 bme; #define HALL_PIN 34 #define WORD_COUNT 100 // Simplified word list (full version has 2048) // Ovilus modes enum Mode { MODE_DICTIONARY, MODE_PHONETIC, MODE_ENERGY }; Mode currentMode = MODE_DICTIONARY; bool active = false; unsigned long lastWordTime = 0; int currentWordIndex = -1; // Simplified word database (you would expand this to 2048 words) const char* wordDatabase[WORD_COUNT] = { "HELLO", "SPIRIT", "HERE", "COLD", "DARK", "LIGHT", "YES", "NO", "DANGER", "HELP", "FEAR", "LOVE", "DEATH", "LIFE", "CHILD", "MOTHER", "FATHER", "HOME", "AWAY", "NEAR", "FAR", "ABOVE", "BELOW", "BEHIND", "FRIEND", "ENEMY", "PAIN", "JOY", "ANGER", "PEACE", "WAR", "FIRE", "WATER", "EARTH", "AIR", "BLOOD", "BONE", "FLESH", "SOUL", "MIND", "HEART", "HAND", "EYE", "VOICE", "WHISPER", "SCREAM", "SILENCE", "NOISE", "DREAM", "NIGHTMARE", "SLEEP", "WAKE", "DAY", "NIGHT", "DAWN", "DUSK", "SUN", "MOON", "STAR", "SHADOW", "GHOST", "DEMON", "ANGEL", "GOD", "DEVIL", "HEAVEN", "HELL", "LIMBO", "LOST", "FOUND", "SEEK", "HIDE", "RUN", "STAY", "COME", "GO", "LEAVE", "REMAIN", "WAIT", "HURRY", "SOON", "NEVER", "ALWAYS", "SOMETIMES", "MAYBE", "CERTAIN", "DOUBT", "TRUST", "LIE", "TRUTH", "SECRET", "REVEAL", "HIDE", "SHOW", "SEE", "BLIND", "HEAR", "DEAF", "SPEAK", "MUTE" }; void setup() { Serial.begin(115200); // Initialize TFT tft.init(); tft.setRotation(1); tft.fillScreen(TFT_BLACK); // Initialize BME280 if (!bme.begin(0x76)) { tft.setTextColor(TFT_RED); tft.setTextSize(2); tft.setCursor(20, 100); tft.println("BME280 ERROR!"); Serial.println("BME280 init failed!"); while(1); } pinMode(HALL_PIN, INPUT); drawMainMenu(); } void loop() { // Check for touch uint16_t touchX, touchY; bool pressed = tft.getTouch(&touchX, &touchY); if (pressed) { handleTouch(touchX, touchY); delay(200); } // If active, check for environmental changes if (active) { checkEnvironment(); } } void drawMainMenu() { tft.fillScreen(TFT_BLACK); // Title tft.setTextColor(TFT_MAGENTA); tft.setTextSize(3); tft.setCursor(50, 10); tft.println("OVILUS"); // Mode buttons drawModeButton(10, 60, 90, 50, "DICT", MODE_DICTIONARY); drawModeButton(110, 60, 90, 50, "PHON", MODE_PHONETIC); drawModeButton(210, 60, 90, 50, "ENRG", MODE_ENERGY); // Start/Stop button uint16_t btnColor = active ? TFT_RED : TFT_GREEN; String btnText = active ? "STOP" : "START"; drawActionButton(80, 130, 160, 60, btnText, btnColor); // Status display drawStatus(); } void drawModeButton(int x, int y, int w, int h, String label, Mode mode) { uint16_t color = (currentMode == mode) ? TFT_YELLOW : TFT_DARKGREY; tft.fillRoundRect(x, y, w, h, 8, color); tft.drawRoundRect(x, y, w, h, 8, TFT_WHITE); tft.setTextColor(TFT_WHITE); tft.setTextSize(2); int textX = x + (w - label.length() * 12) / 2; int textY = y + (h / 2) - 8; tft.setCursor(textX, textY); tft.println(label); } void drawActionButton(int x, int y, int w, int h, String label, uint16_t color) { tft.fillRoundRect(x, y, w, h, 8, color); tft.drawRoundRect(x, y, w, h, 8, TFT_WHITE); tft.setTextColor(TFT_WHITE); tft.setTextSize(3); int textX = x + (w - label.length() * 18) / 2; int textY = y + (h / 2) - 12; tft.setCursor(textX, textY); tft.println(label); } void drawStatus() { tft.fillRect(0, 200, 320, 40, TFT_BLACK); tft.setTextColor(TFT_CYAN); tft.setTextSize(1); tft.setCursor(10, 205); float temp = bme.readTemperature(); float humidity = bme.readHumidity(); int magnetic = analogRead(HALL_PIN); tft.print("T:"); tft.print(temp, 1); tft.print("C H:"); tft.print(humidity, 0); tft.print("% M:"); tft.print(magnetic); // Display last word if (currentWordIndex >= 0) { tft.fillRect(0, 220, 320, 20, TFT_NAVY); tft.setTextColor(TFT_WHITE); tft.setTextSize(2); String word = String(wordDatabase[currentWordIndex]); int wordX = (320 - word.length() * 12) / 2; tft.setCursor(wordX, 222); tft.println(word); } } void handleTouch(uint16_t x, uint16_t y) { // Mode buttons if (y >= 60 && y <= 110) { if (x >= 10 && x <= 100) { currentMode = MODE_DICTIONARY; drawMainMenu(); } else if (x >= 110 && x <= 200) { currentMode = MODE_PHONETIC; drawMainMenu(); } else if (x >= 210 && x <= 300) { currentMode = MODE_ENERGY; drawMainMenu(); } } // Start/Stop button if (x >= 80 && x <= 240 && y >= 130 && y <= 190) { active = !active; drawMainMenu(); } } void checkEnvironment() { // Only generate word every 2-5 seconds to avoid spam unsigned long now = millis(); if (now - lastWordTime < 2000) { return; } // Read environmental sensors float temperature = bme.readTemperature(); float humidity = bme.readHumidity(); float pressure = bme.readPressure() / 100.0F; int magnetic = analogRead(HALL_PIN); // Calculate hash from sensor readings int hash = 0; switch (currentMode) { case MODE_DICTIONARY: // Use all sensors equally hash = (int)((temperature * 10) + humidity + (pressure / 10) + (magnetic / 10)); break; case MODE_PHONETIC: // Emphasize temperature and magnetic hash = (int)((temperature * 20) + (magnetic / 5)); break; case MODE_ENERGY: // Only use magnetic field changes hash = magnetic; break; } // Map hash to word index int wordIndex = abs(hash) % WORD_COUNT; // Only speak if significant change if (wordIndex != currentWordIndex) { currentWordIndex = wordIndex; lastWordTime = now; // Update display drawStatus(); // Log to serial Serial.print("Word: "); Serial.print(wordDatabase[currentWordIndex]); Serial.print(" (T:"); Serial.print(temperature); Serial.print(" H:"); Serial.print(humidity); Serial.print(" P:"); Serial.print(pressure); Serial.print(" M:"); Serial.print(magnetic); Serial.println(")"); } }