Initial Commit
This commit is contained in:
parent
2d95ee1255
commit
a14910c0d1
2 changed files with 217 additions and 0 deletions
85
README.md
Normal file
85
README.md
Normal file
|
@ -0,0 +1,85 @@
|
|||
# 🌡️ ThermoMood Display
|
||||
|
||||
**A circular mood visualizer based on skin temperature — perfect for your desk or cubicle.**
|
||||
Touch the thermocouple, and the GC9A01A round display reveals a mood with color and snarky commentary.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Features
|
||||
|
||||
- Displays Fahrenheit temperature at the bottom of a circular screen
|
||||
- Mood is selected based on skin temperature ranges
|
||||
- Background color changes dynamically with mood
|
||||
- Funny, sarcastic, cubicle-safe mood labels
|
||||
- Word-wrapped, auto-centered text
|
||||
- Smooth average filtering to avoid noisy updates
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Hardware Requirements
|
||||
|
||||
| Component | Details |
|
||||
|----------|---------|
|
||||
| **Microcontroller** | Arduino Nano ESP32 |
|
||||
| **Display** | 1.28" Round TFT GC9A01A (240x240) |
|
||||
| **Thermocouple** | Type K Thermocouple |
|
||||
| **Converter Module** | MAX6675 Thermocouple Amplifier |
|
||||
| **Wiring** | 4-Wire SPI (Display) + 3-Wire SPI (MAX6675) |
|
||||
|
||||
---
|
||||
|
||||
## 📷 Pinout (Example Wiring)
|
||||
|
||||
| Signal | Arduino Nano ESP32 | Module |
|
||||
|--------|--------------------|--------|
|
||||
| **SO** (Thermocouple) | `D4` | MAX6675 SO |
|
||||
| **CS** (Thermocouple) | `D5` | MAX6675 CS |
|
||||
| **SCK** | `D6` | MAX6675 SCK |
|
||||
| **TFT DC** | `D7` | GC9A01A DC |
|
||||
| **TFT CS** | `D10` | GC9A01A CS |
|
||||
| **TFT RST** | `D8` | GC9A01A RST |
|
||||
| **TFT SCK/MOSI** | Default SPI | GC9A01A SCK/MOSI |
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Mood Mapping (Temperature → Mood)
|
||||
|
||||
| Temp Range (°F) | Example Moods |
|
||||
|-----------------|----------------|
|
||||
| `< 85` | "Are you even alive?", "CRASHING OUT" |
|
||||
| `85–87` | "Quietly spiraling", "Flatline on enthusiasm" |
|
||||
| `88–89` | "Classic cubicle-core", "Emotionally beige" |
|
||||
| `90–91` | "Deadline panic", "Running on fumes" |
|
||||
| `92–93` | "Unraveling", "Caffeinated and dangerous" |
|
||||
| `94–95` | "TIME FOR YOU TO GO HOME", "Corporate martyr detected" |
|
||||
| `> 96` | "Go touch grass", "Return to sender (overheated)" |
|
||||
|
||||
---
|
||||
|
||||
## 💻 Libraries Required
|
||||
|
||||
Install these via the Arduino Library Manager:
|
||||
|
||||
- `Adafruit_GFX`
|
||||
- `Adafruit_GC9A01A`
|
||||
- `MAX6675` (Adafruit version recommended)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 To Run
|
||||
|
||||
1. Wire up your components as listed.
|
||||
2. Upload `ThermoMood_Display.ino` using Arduino IDE (board: **Arduino Nano ESP32**).
|
||||
3. Touch the thermocouple — your mood will appear!
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contributions Welcome
|
||||
|
||||
Want to add emoji, animations, or finger detection? Submit a pull request or suggest ideas!
|
||||
|
||||
---
|
||||
|
||||
## 🧠 License
|
||||
|
||||
This project is open source under the MIT License.
|
132
ThermoMood_Display.ino
Normal file
132
ThermoMood_Display.ino
Normal file
|
@ -0,0 +1,132 @@
|
|||
// ThermoMood Display Integration (Circular Layout, Mood-Centric)
|
||||
// Uses MAX6675 thermocouple and GC9A01A round TFT to display temperature-based moods
|
||||
|
||||
#include <max6675.h>
|
||||
#include <SPI.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_GC9A01A.h>
|
||||
|
||||
// Thermocouple SPI pin definitions
|
||||
#define SO_PIN D4
|
||||
#define CS_PIN D5
|
||||
#define SCK_PIN D6
|
||||
MAX6675 thermocouple(SCK_PIN, CS_PIN, SO_PIN);
|
||||
|
||||
// TFT display pin definitions
|
||||
#define TFT_DC D7
|
||||
#define TFT_CS D10
|
||||
#define TFT_RST D8
|
||||
Adafruit_GC9A01A tft(TFT_CS, TFT_DC, TFT_RST);
|
||||
|
||||
// Track last displayed stable temperature
|
||||
float lastDisplayedTemp = -999.0;
|
||||
|
||||
// Return background color based on mood content
|
||||
uint16_t getMoodColor(const char* mood) {
|
||||
if (strstr(mood, "alive") || strstr(mood, "Frozen")) return GC9A01A_NAVY;
|
||||
if (strstr(mood, "spiraling") || strstr(mood, "low")) return GC9A01A_BLUE;
|
||||
if (strstr(mood, "meh") || strstr(mood, "core")) return GC9A01A_DARKGREY;
|
||||
if (strstr(mood, "deadline") || strstr(mood, "present")) return GC9A01A_ORANGE;
|
||||
if (strstr(mood, "existential") || strstr(mood, "panic")) return GC9A01A_TEAL;
|
||||
if (strstr(mood, "TIME FOR YOU") || strstr(mood, "martyr")) return GC9A01A_RED;
|
||||
if (strstr(mood, "CRASHING OUT") || strstr(mood, "overheated")) return GC9A01A_MAROON;
|
||||
return GC9A01A_PURPLE;
|
||||
}
|
||||
|
||||
// Choose mood text based on input temperature (Fahrenheit)
|
||||
const char* getMood(float tempF) {
|
||||
if (tempF < 85) {
|
||||
const char* moods[] = {"Are you even alive?", "Frozen in apathy", "Intern from the underworld", "Just cold hands or cold heart?", "CRASHING OUT"}; return moods[random(0, 5)];
|
||||
} else if (tempF < 88) {
|
||||
const char* moods[] = {"Quietly spiraling", "Coffee might help, but not really", "Low-power mode engaged", "Social battery: low", "Flatline on enthusiasm"}; return moods[random(0, 5)];
|
||||
} else if (tempF < 90) {
|
||||
const char* moods[] = {"Classic cubicle-core", "Meh. Just meh.", "Functioning introvert mode", "Emotionally beige", "Dead inside, but on time"}; return moods[random(0, 5)];
|
||||
} else if (tempF < 92) {
|
||||
const char* moods[] = {"One deadline away from losing it", "Focused panic detected", "Productivity-induced suffering", "Almost emotionally present", "Running on fumes"}; return moods[random(0, 5)];
|
||||
} else if (tempF < 94) {
|
||||
const char* moods[] = {"Buzzing with existential dread", "Running on caffeine and vibes", "Possibly unraveling", "Caffeinated and dangerous", "Deadline ninja"}; return moods[random(0, 5)];
|
||||
} else if (tempF < 96) {
|
||||
const char* moods[] = {"Overclocked human processor", "Might spontaneously combust", "TIME FOR YOU TO GO HOME", "Stop. Breathe. Panic again.", "Corporate martyr detected"}; return moods[random(0, 5)];
|
||||
} else {
|
||||
const char* moods[] = {"Go touch grass. Immediately.", "Check for open flames", "Thermally unstable coworker", "CRASHING OUT", "Return to sender (overheated)"}; return moods[random(0, 5)];
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600); // Start serial for debugging
|
||||
delay(500);
|
||||
tft.begin(); // Initialize TFT display
|
||||
tft.setRotation(0);
|
||||
tft.fillScreen(GC9A01A_BLACK); // Start with black screen
|
||||
randomSeed(analogRead(A0)); // Seed RNG for mood randomness
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static float lastStableTemp = -999;
|
||||
static float tempSum = 0;
|
||||
static int sampleCount = 0;
|
||||
|
||||
float tempF = thermocouple.readFahrenheit(); // Read current temperature
|
||||
tempSum += tempF;
|
||||
sampleCount++;
|
||||
|
||||
// Average temp every 10 readings to reduce noise
|
||||
if (sampleCount >= 10) {
|
||||
float avgTemp = tempSum / sampleCount;
|
||||
tempSum = 0;
|
||||
sampleCount = 0;
|
||||
|
||||
// Only update display if temperature changes significantly
|
||||
if (abs(avgTemp - lastStableTemp) >= 1.0) {
|
||||
lastStableTemp = avgTemp;
|
||||
const char* mood = getMood(avgTemp);
|
||||
uint16_t moodColor = getMoodColor(mood);
|
||||
|
||||
Serial.print("Temp: "); Serial.print(avgTemp); Serial.print(" F | Mood: "); Serial.println(mood);
|
||||
|
||||
tft.fillScreen(moodColor); // Set background to mood color
|
||||
tft.drawCircle(120, 120, 119, GC9A01A_BLACK); // Thin border ring
|
||||
tft.drawCircle(120, 120, 118, GC9A01A_BLACK);
|
||||
|
||||
// Draw mood message, word-wrapped in center
|
||||
tft.setTextColor(GC9A01A_WHITE);
|
||||
tft.setTextSize(2);
|
||||
int y = 100;
|
||||
char buffer[128];
|
||||
strncpy(buffer, mood, sizeof(buffer));
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
char* line = strtok(buffer, " ");
|
||||
String currentLine = "";
|
||||
|
||||
while (line != NULL) {
|
||||
if (currentLine.length() + strlen(line) + 1 < 15) {
|
||||
currentLine += line;
|
||||
currentLine += " ";
|
||||
} else {
|
||||
int x = (240 - currentLine.length() * 12) / 2;
|
||||
tft.setCursor(x, y);
|
||||
tft.print(currentLine);
|
||||
y += 20;
|
||||
currentLine = String(line) + " ";
|
||||
}
|
||||
line = strtok(NULL, " ");
|
||||
}
|
||||
if (currentLine.length() > 0) {
|
||||
int x = (240 - currentLine.length() * 12) / 2;
|
||||
tft.setCursor(x, y);
|
||||
tft.print(currentLine);
|
||||
}
|
||||
|
||||
// Draw temperature at bottom
|
||||
tft.setTextColor(GC9A01A_WHITE);
|
||||
tft.setTextSize(2);
|
||||
char tempStr[10];
|
||||
sprintf(tempStr, "%d F", (int)avgTemp);
|
||||
int tempWidth = strlen(tempStr) * 12;
|
||||
int tempX = (240 - tempWidth) / 2;
|
||||
tft.setCursor(tempX, 210);
|
||||
tft.print(tempStr);
|
||||
}
|
||||
}
|
||||
delay(500); // Sample delay
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue