Skip to content

Instantly share code, notes, and snippets.

@EDISON-SCIENCE-CORNER
Created November 29, 2025 20:34
Show Gist options
  • Select an option

  • Save EDISON-SCIENCE-CORNER/8c34a5c1d52d9f8af5e0038e555266b8 to your computer and use it in GitHub Desktop.

Select an option

Save EDISON-SCIENCE-CORNER/8c34a5c1d52d9f8af5e0038e555266b8 to your computer and use it in GitHub Desktop.
#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