// --- T-test F-funcion OS-operating system ---
// --- made by KSINIUS inspired by GABCIUS ---
// --- TFOS-1.4 BUBLY (RESTORED LOGO & LOADING) ---
#include <Arduino.h>
#include <U8g2lib.h>
#include <IRremote.h>
#define LDR_P A0
#define TMP_P A2
#define IR_S A4
#define SDA_P A11
#define SCL_P A12
#define OLED_V 13
#define SEC_P 12
#define MAT_P 52
#define GRA_P 47
#define OVR_P 22
#define PWR_P 23
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, SCL_P, SDA_P, U8X8_PIN_NONE);
const int BUBBLE_COUNT = 5;
int bubX[BUBBLE_COUNT], bubY[BUBBLE_COUNT], bubR[BUBBLE_COUNT];
int slideX = 0, currentX = 0;
unsigned long irCode = 0, lastIrTime = 0, lastActivity = 0;
int hist[128];
float mx = -99.0, mn = 99.0, realLgt = 0, realTemp = 0;
bool gMode = false, lGrp = HIGH, saverEnabled = true, lastOvrState = HIGH;
const unsigned long saverTimeout = 40000;
const unsigned long wireframeDelay = 30000;
int bounceX = 30, bounceY = 20, dirX = 2, dirY = 2;
const int logoW = 38, logoH = 28;
int pOut[] = {A1, A3, A5, A13, A14, 51, 53, 11};
// --- Custom UI Functions ---
void drawThermoIcon(int x, int y, float t) {
u8g2.drawFrame(x + 1, y, 3, 7); u8g2.drawCircle(x + 2, y + 8, 2);
int fill = map(constrain((int)t, 0, 50), 0, 50, 0, 6);
u8g2.drawVLine(x + 2, y + 7 - fill, fill);
if (t > 0) u8g2.drawPixel(x + 2, y + 8);
}
void drawRadioTower(int x, int y) {
u8g2.drawLine(x + 3, y + 10, x + 3, y + 3); u8g2.drawLine(x + 1, y + 10, x + 5, y + 10);
if (millis() - lastIrTime < 500) {
u8g2.drawCircle(x + 3, y + 3, 2, U8G2_DRAW_UPPER_RIGHT | U8G2_DRAW_UPPER_LEFT);
u8g2.drawCircle(x + 3, y + 3, 4, U8G2_DRAW_UPPER_RIGHT | U8G2_DRAW_UPPER_LEFT);
}
}
void drawWeatherIcon(int x, int y, float lgt) {
if (lgt >= 75) {
u8g2.drawDisc(x + 3, y + 3, 2); u8g2.drawLine(x+3, y-1, x+3, y); u8g2.drawLine(x+3, y+6, x+3, y+7);
u8g2.drawLine(x-1, y+3, x, y+3); u8g2.drawLine(x+6, y+3, x+7, y+3);
u8g2.drawLine(x+1, y+1, x, y); u8g2.drawLine(x+5, y+1, x+6, y);
u8g2.drawLine(x+1, y+5, x, y+6); u8g2.drawLine(x+5, y+5, x+6, y+6);
} else if (lgt >= 55) {
u8g2.drawCircle(x+2, y+4, 2); u8g2.drawCircle(x+4, y+3, 2); u8g2.drawCircle(x+5, y+4, 2);
} else {
u8g2.drawCircle(x + 3, y + 3, 3); u8g2.setDrawColor(0); u8g2.drawDisc(x + 1, y + 3, 2); u8g2.setDrawColor(1);
}
}
void drawGraphUI(int ox, float t) {
u8g2.setFont(u8g2_font_4x6_tf);
u8g2.drawStr(ox + 2, 12, "100"); u8g2.drawStr(ox + 4, 23, "75");
u8g2.drawStr(ox + 4, 36, "50"); u8g2.drawStr(ox + 4, 50, "25");
u8g2.drawStr(ox + 6, 60, "0");
u8g2.drawLine(ox + 16, 8, ox + 16, 60); u8g2.drawLine(ox + 16, 60, ox + 125, 60);
for (int i = 22; i < 125; i += 8) {
u8g2.drawPixel(ox + i, 10); u8g2.drawPixel(ox + i, 21);
u8g2.drawPixel(ox + i, 34); u8g2.drawPixel(ox + i, 48);
}
u8g2.setCursor(ox + 20, 5); u8g2.print("NOW:"); u8g2.print(t, 1);
u8g2.setCursor(ox + 60, 5); u8g2.print("MX:"); u8g2.print(mx, 1);
u8g2.setCursor(ox + 95, 5); u8g2.print("MN:"); u8g2.print(mn, 1);
for (int i = 0; i < 107; i++) { u8g2.drawLine(ox + 17 + i, hist[20 + i], ox + 18 + i, hist[21 + i]); }
}
void setup() {
pinMode(OLED_V, OUTPUT); digitalWrite(OLED_V, LOW);
pinMode(PWR_P, OUTPUT); digitalWrite(PWR_P, LOW);
for(int p : pOut) { pinMode(p, OUTPUT); digitalWrite(p, LOW); }
pinMode(SEC_P, INPUT_PULLUP); pinMode(MAT_P, INPUT_PULLUP);
pinMode(GRA_P, INPUT_PULLUP); pinMode(OVR_P, INPUT_PULLUP);
u8g2.begin();
for(int i=0; i<BUBBLE_COUNT; i++) { bubX[i] = random(0, 120); bubY[i] = random(16, 50); bubR[i] = random(1, 3); }
for(int i=0; i<128; i++) hist[i] = 60;
// 1. ORIGINAL LOADING SCREEN
int sz[] = {0, 1, 3, 5, 3, 1};
for(int c=0; c<2; c++) {
for(int f=0; f<6; f++) {
u8g2.clearBuffer();
u8g2.drawBox(45, 18, 38, 5); u8g2.drawBox(61, 18, 6, 28); u8g2.drawBox(61, 30, 16, 5);
for(int i=0; i<4; i++) { int cur = sz[(f - i + 6) % 6]; if(cur > 0) u8g2.drawFrame(46 + (i * 12) - (cur/2), 52 - (cur/2), cur, cur); }
u8g2.sendBuffer(); delay(35);
}
}
// 2. LAYERED SLIDE BOOT WITH ORIGINAL TF LOGO
for (int s = 0; s <= 30; s += 2) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_4x6_tf);
u8g2.drawStr(82, 10, "v1.4 BUBLY"); // Corner Version
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(54 + s, 42, "OS"); // OS slides out from behind F
u8g2.setDrawColor(0);
u8g2.drawBox(0, 15, 78, 40); // Mask for the slide
u8g2.setDrawColor(1);
// Original TF Block Logo
u8g2.drawBox(45, 18, 38, 5); u8g2.drawBox(61, 18, 6, 28); u8g2.drawBox(61, 30, 16, 5);
u8g2.sendBuffer();
delay(15);
}
u8g2.setContrast(255); delay(100); u8g2.setContrast(128);
delay(1500);
IrReceiver.begin(IR_S); lastActivity = millis();
}
void loop() {
unsigned long now = millis();
bool b1 = (digitalRead(SEC_P) == LOW); bool b2 = (digitalRead(MAT_P) == LOW);
bool b3 = (digitalRead(GRA_P) == LOW); bool curOvr = digitalRead(OVR_P);
if (curOvr == LOW && lastOvrState == HIGH) { saverEnabled = !saverEnabled; lastActivity = now; delay(200); }
lastOvrState = curOvr;
if (IrReceiver.decode()) { irCode = IrReceiver.decodedIRData.decodedRawData; lastIrTime = now; lastActivity = now; IrReceiver.resume(); }
if (b1 || b2 || b3) lastActivity = now;
realLgt = constrain(map(analogRead(LDR_P), 0, 1023, 100, 0), 0, 100);
int rT = analogRead(TMP_P);
realTemp = (rT > 1021 || rT < 2) ? -99.0 : (1.0 / (log(((float)rT * 10000.0 / (1023.0 - rT)) / 10000.0) / 3950.0 + 1.0 / 298.15) - 273.15);
if (realTemp > -50.0) {
if (mx == -99.0 || realTemp > mx) mx = realTemp;
if (mn == 99.0 || realTemp < mn) mn = realTemp;
for (int i=0; i<127; i++) hist[i] = hist[i+1];
hist[127] = map(constrain((int)realTemp, 0, 100), 0, 100, 60, 8);
}
unsigned long idleTime = now - lastActivity;
bool isSaving = (saverEnabled && (idleTime > saverTimeout));
bool isWireframe = (saverEnabled && (idleTime > (saverTimeout + wireframeDelay)));
if (isWireframe) u8g2.setContrast(5); else if (isSaving) u8g2.setContrast(150);
else u8g2.setContrast((realLgt >= 75) ? 225 : (realLgt >= 55 ? 110 : 20));
if (b3 && lGrp == HIGH) { gMode = !gMode; slideX = gMode ? -128 : 0; }
lGrp = b3;
if (currentX != slideX) currentX += (currentX > slideX) ? -16 : 16;
u8g2.clearBuffer();
if (isSaving) {
if (isWireframe) { u8g2.drawFrame(bounceX, bounceY, 38, 5); u8g2.drawFrame(bounceX+16, bounceY, 6, 28); u8g2.drawFrame(bounceX+16, bounceY+12, 16, 5); }
else { u8g2.drawBox(bounceX, bounceY, 38, 5); u8g2.drawBox(bounceX+16, bounceY, 6, 28); u8g2.drawBox(bounceX+16, bounceY+12, 16, 5); }
bounceX += dirX; bounceY += dirY;
if (bounceX <= 0 || bounceX >= (128 - logoW)) dirX = -dirX;
if (bounceY <= 0 || bounceY >= (64 - logoH)) dirY = -dirY;
} else if (b1) {
u8g2.drawBox(0, 0, 128, 64); u8g2.setDrawColor(0);
u8g2.setFont(u8g2_font_ncenB10_tr); u8g2.drawStr(25, 22, "MADE BY:"); u8g2.drawStr(30, 42, "KSINIUS");
u8g2.setFont(u8g2_font_6x10_tf); u8g2.drawStr(5, 58, "INSPIRED BY GABCIUS");
u8g2.setFont(u8g2_font_4x6_tf); u8g2.drawStr(2, 63, "v1.4 BUBLY");
u8g2.setDrawColor(1);
} else if (b2) {
u8g2.setFont(u8g2_font_4x6_tf); for(int i=0; i<50; i++) { u8g2.setCursor(random(128), random(64)); u8g2.print((char)random(33,126)); }
} else {
for (int i = 0; i < BUBBLE_COUNT; i++) {
u8g2.drawCircle(bubX[i] + currentX, bubY[i], bubR[i]);
if (now % 3 == 0) bubY[i]--;
if (bubY[i] < 14) { bubY[i] = 51; bubX[i] = random(0, 128); }
}
// Main UI TF Logo
u8g2.drawBox(currentX+2, 1, 14, 2); u8g2.drawBox(currentX+8, 1, 2, 9); u8g2.drawBox(currentX+8, 5, 6, 2);
u8g2.setFont(u8g2_font_6x10_tf); u8g2.setCursor(currentX + 60, 10); u8g2.print("S:"); u8g2.print(saverEnabled ? "ON" : "OFF");
drawRadioTower(currentX + 96, 1); drawThermoIcon(currentX + 107, 1, realTemp); drawWeatherIcon(currentX + 117, 2, realLgt);
u8g2.setCursor(currentX + 4, 28); u8g2.print("TEMP: "); u8g2.print(realTemp, 1); u8g2.print("C");
u8g2.setCursor(currentX + 72, 28); u8g2.print("LGT: "); u8g2.print((int)realLgt); u8g2.print("%");
u8g2.drawFrame(currentX + 4, 33, 52, 7); u8g2.drawBox(currentX + 5, 34, map(constrain((int)realTemp, 0, 100), 0, 100, 0, 50), 5);
u8g2.drawFrame(currentX + 72, 33, 52, 7); u8g2.drawBox(currentX + 73, 34, map(constrain((int)realLgt, 0, 100), 0, 100, 0, 50), 5);
u8g2.drawHLine(currentX, 13, 128); u8g2.drawHLine(currentX, 52, 128);
u8g2.setCursor(currentX + 4, 63); u8g2.print("IR: 0x"); u8g2.print(irCode, HEX);
drawGraphUI(currentX + 128, realTemp);
}
u8g2.sendBuffer();
}