Created
November 29, 2025 20:34
-
-
Save EDISON-SCIENCE-CORNER/8c34a5c1d52d9f8af5e0038e555266b8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <Adafruit_GFX.h> | |
| #include <Adafruit_ST7796S.h> | |
| #include <math.h> | |
| #include <Wire.h> | |
| #include <AHTxx.h> | |
| // ---------------- TFT PINS ---------------- | |
| #define TFT_CS 2 | |
| #define TFT_DC 5 | |
| #define TFT_RST 4 | |
| Adafruit_ST7796S display(TFT_CS, TFT_DC, TFT_RST); | |
| // ---------------- AHT25 SENSOR ------------ | |
| AHTxx aht25(AHTXX_ADDRESS_X38, AHT2x_SENSOR); | |
| // ---------------- ANALOG CO & NO2 SENSOR -------- | |
| int sensorPin = 0; // CO sensor | |
| int sensorPin2 = 1; // NO2 sensor | |
| int sensorValue = 0; | |
| int sensorValue2 = 0; | |
| int coPercent = 0; | |
| int no2Percent = 0; | |
| // ---------------- GAUGE SETTINGS ---------- | |
| int innerRadius = 50; | |
| int outerRadius = 70; | |
| const float PI_RAD = 0.0174533; | |
| int cx[4] = {130, 340, 130, 340}; | |
| int cy[4] = {70, 70, 240, 240}; | |
| const char* labels[4] = {" CO", "NO2", "TEM", "HUM"}; | |
| int prevValues[4] = {-1, -1, -1, -1}; | |
| int valueToAngle(int val) { | |
| return map(constrain(val, 0, 100), 0, 100, 0, 360); | |
| } | |
| // -------- PERFECT GREEN → YELLOW → RED GRADIENT ---------- | |
| uint16_t getGradientColor(int percent) { | |
| percent = constrain(percent, 0, 100); | |
| int r = 0, g = 0, b = 0; | |
| if (percent <= 50) { | |
| b = map(percent, 0, 50, 255, 0); | |
| r = 255; | |
| } else { | |
| g = map(percent, 50, 100, 0, 255); | |
| r = 255; | |
| } | |
| return display.color565(r, g, b); | |
| } | |
| void drawGaugeOutline(int x, int y) { | |
| display.drawCircle(x, y, outerRadius, 0x0000); | |
| display.drawCircle(x, y, innerRadius, 0x0000); | |
| } | |
| void drawGaugeFill(int index, int x, int y, int value, const char* label) { | |
| int prevValue = prevValues[index]; | |
| int angle = valueToAngle(value); | |
| int prevAngle = valueToAngle(prevValue); | |
| uint16_t bgColor = 0xFFFF; | |
| float rad, px, py; | |
| // CLEAR decreasing arc | |
| if (value < prevValue) { | |
| for (int a = angle; a < prevAngle; a++) { | |
| for (int r = innerRadius; r <= outerRadius; r++) { | |
| rad = (a + 90) * PI_RAD; | |
| px = x + cos(rad) * r; | |
| py = y + sin(rad) * r; | |
| display.drawPixel(px, py, bgColor); | |
| } | |
| } | |
| } | |
| // DRAW new arc | |
| if (value > prevValue || prevValue == -1) { | |
| int startAngle = (prevValue == -1) ? 0 : prevAngle; | |
| for (int a = startAngle; a < angle; a++) { | |
| uint16_t color = getGradientColor(map(a, 0, 360, 0, 100)); | |
| for (int r = innerRadius; r <= outerRadius; r++) { | |
| rad = (a + 90) * PI_RAD; | |
| px = x + cos(rad) * r; | |
| py = y + sin(rad) * r; | |
| display.drawPixel(px, py, color); | |
| } | |
| } | |
| } | |
| // CENTER text | |
| display.fillCircle(x, y, innerRadius - 5, 0xFFFF); | |
| display.setCursor(x - 15, y - 10); | |
| display.setTextSize(2); | |
| display.setTextColor(0x0000); | |
| display.print(value); | |
| display.setCursor(x - 20, y + 10); | |
| display.setTextSize(2); | |
| display.setTextColor(0x0000); | |
| display.print(label); | |
| drawGaugeOutline(x, y); | |
| prevValues[index] = value; | |
| } | |
| // ------------------------------------------------------------- | |
| // NEOPIXEL SECTION | |
| // ------------------------------------------------------------- | |
| #include <Adafruit_NeoPixel.h> | |
| #define PIXEL_PIN 6 | |
| #define NUMPIXELS 1 | |
| Adafruit_NeoPixel pixels(NUMPIXELS, PIXEL_PIN, NEO_RGB + NEO_KHZ800); | |
| // ------------------------------------------------------------- | |
| // SETUP | |
| // ------------------------------------------------------------- | |
| void setup() { | |
| Serial.begin(9600); | |
| Wire.begin(); | |
| // AHT25 INIT | |
| if (!aht25.begin(0x38)) { | |
| Serial.println("AHT25 NOT FOUND!"); | |
| while (1); | |
| } | |
| // TFT INIT | |
| display.init(320, 480); | |
| display.setRotation(1); | |
| display.fillScreen(0xFFFF); | |
| // Welcome screen | |
| display.setTextColor(0xF81F); | |
| display.setCursor(60, 140); | |
| display.setTextSize(7); | |
| display.print("AIR BUDDY"); | |
| delay(3000); | |
| display.fillScreen(0xFFFF); | |
| // Draw outlines once | |
| for (int i = 0; i < 4; i++) { | |
| drawGaugeOutline(cx[i], cy[i]); | |
| } | |
| // NeoPixel init | |
| pixels.begin(); | |
| pixels.clear(); | |
| pixels.show(); | |
| } | |
| // ------------------------------------------------------------- | |
| // LOOP | |
| // ------------------------------------------------------------- | |
| void loop() { | |
| // ---------- CO SENSOR ---------- | |
| sensorValue = analogRead(sensorPin); | |
| sensorValue2 = analogRead(sensorPin2); | |
| coPercent = map(sensorValue, 0, 1000, 0, 100); | |
| coPercent = constrain(coPercent, 0, 100); | |
| no2Percent = map(sensorValue2, 0, 1023, 0, 100); | |
| no2Percent = constrain(no2Percent, 0, 100); | |
| // ---------- TEMPERATURE & HUMIDITY ---------- | |
| float temperature = aht25.readTemperature(); | |
| float humidity = aht25.readHumidity(); | |
| int tempVal = constrain((int)temperature, 0, 100); | |
| int humVal = constrain((int)humidity, 0, 100); | |
| // ---------- UPDATE GAUGES ---------- | |
| drawGaugeFill(0, cx[0], cy[0], coPercent, labels[0]); | |
| drawGaugeFill(1, cx[1], cy[1], no2Percent, labels[1]); | |
| drawGaugeFill(2, cx[2], cy[2], tempVal, labels[2]); | |
| drawGaugeFill(3, cx[3], cy[3], humVal, labels[3]); | |
| // ---------- UPDATE LED (YOUR RULE) ---------- | |
| bool alert = false; | |
| if (coPercent > 50) alert = true; | |
| if (no2Percent > 50) alert = true; | |
| if (tempVal > 30) alert = true; | |
| if (alert) { | |
| pixels.setPixelColor(0, pixels.Color(255, 0, 0)); // RED | |
| delay(100); | |
| } else { | |
| pixels.setPixelColor(0, pixels.Color(0, 255, 0)); // GREEN | |
| } | |
| pixels.show(); | |
| delay(2000); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment