#include <Wire.h>
#include <U8g2lib.h>
// ================= DISPLAY =================
U8G2_SSD1306_128X64_NONAME_F_HW_I2C display(U8G2_R0, U8X8_PIN_NONE, 6, 5);
// ================= BIT SWAP FUNCTION =================
uint8_t swapBits(uint8_t b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
// ===== THUNDER ICON =====
const unsigned char icon_thunder_18[] PROGMEM = {
0x00,0x00,0x00,0x00,0x00,0x3f,0xc0,0x00,0x00,0x7d,0xe0,0x00,0x00,0xe0,0x70,0x00,
0x0f,0xc0,0x38,0x00,0x0f,0x80,0x18,0x00,0x1c,0x00,0x1c,0x00,0x38,0x60,0x0e,0x00,
0x7b,0xf8,0x0f,0x00,0xe3,0xf8,0x03,0x80,0xc1,0xf8,0x01,0x80,0xc3,0xf8,0x01,0x80,
0xc0,0x60,0x01,0x80,0xc0,0x6c,0x01,0x80,0xe0,0x3f,0x03,0x80,0xfe,0x3f,0x7f,0x80,
0x3e,0x3f,0xfe,0x00,0x00,0xbf,0x00,0x00,0x01,0x8c,0x00,0x00,0x07,0xf4,0x00,0x00,
0x07,0xe0,0x00,0x00,0x07,0xe0,0x00,0x00,0x07,0xf0,0x00,0x00,0x01,0x80,0x00,0x00,
0x00,0x00,0x00,0x00
};
uint8_t icon_thunder_18_swapped[sizeof(icon_thunder_18)];
// ================= DATE ROTATION =================
unsigned long dateTimer = 0;
int dateStage = 0; // 0=day+date, 1=month, 2=year
String getDayName(int d){
const char* days[] = {"SUN","MON","TUES","WEDNES","THURS","FRI","SATAR"};
return String(days[d]);
}
String getMonthName(int m){
const char* months[] = {"JAN","FEB","MAR","APR","MAY","JUN",
"JUL","AUG","SEP","OCT","NOV","DEC"};
return String(months[m]);
}
// ================= FAKE DATA =================
String weatherDesc = "Sunny";
String savedCity = "Dhaka";
float outdoorTemp = 32;
float indoorTemp = 28;
int indoorHum = 65;
// ===== FAKE STATUS =====
int fakeRSSI = -60; // WiFi strength
int fakeBattery = 75; // Battery %
// ================= TIMER =================
unsigned long lastSecond = 0;
unsigned long textTimer = 0;
int hh = 12, mm = 45, ss = 0;
bool showCity = false;
// ================= SCROLL =================
int scrollX = 0;
String scrollText = "";
// =================================================
// ===== WIFI ICON =====
int getWiFiLevel() {
if(fakeRSSI >= -55) return 4;
if(fakeRSSI >= -65) return 3;
if(fakeRSSI >= -75) return 2;
if(fakeRSSI >= -85) return 1;
return 0;
}
void drawWiFiIcon(int x, int y) {
int level = getWiFiLevel();
for(int i=0;i<4;i++){
if(i < level)
display.drawBox(x + i*3, y - i*2, 2, i*2 + 2);
else
display.drawFrame(x + i*3, y - i*2, 2, i*2 + 2);
}
}
// =================================================
// ===== BATTERY ICON =====
void drawBatteryIcon(int x, int y) {
int w = 14; // reduced width (fits WiFi width)
int h = 7; // reduced height (was ~8 → now -3px)
// Battery body
display.drawFrame(x, y, w, h);
// Battery head (smaller)
display.drawBox(x + w, y + 2, 2, 3);
// Fill level
int fill = map(fakeBattery, 0, 100, 0, w - 2);
display.drawBox(x + 1, y + 1, fill, h - 2);
}
// =================================================
void setup() {
display.begin();
display.setFont(u8g2_font_logisoso20_tr);
scrollText = weatherDesc;
// Convert icon once
for (int i = 0; i < sizeof(icon_thunder_18); i++) {
uint8_t b = pgm_read_byte(&icon_thunder_18[i]);
icon_thunder_18_swapped[i] = swapBits(b);
}
}
// =================================================
void loop() {
// CLOCK
if (millis() - lastSecond >= 1000) {
lastSecond = millis();
ss++;
if (ss >= 60) {
ss = 0;
mm++;
if (mm >= 60) {
mm = 0;
hh++;
if (hh >= 24) hh = 0;
}
}
}
// TEXT ROTATION
if (showCity) {
if (millis() - textTimer > 3000) {
showCity = false;
textTimer = millis();
scrollText = weatherDesc;
scrollX = 0;
}
} else {
if (millis() - textTimer > 4000) {
showCity = true;
textTimer = millis();
scrollText = savedCity;
scrollX = 0;
}
}
// ===== DATE ROTATION =====
//if(millis() - dateTimer > 2400){
// dateStage++;
//if(dateStage > 1) dateStage = 0;
// dateTimer = millis();
//}*/
drawUI();
}
// =================================================
void drawUI() {
display.clearBuffer();
// ===== STATUS BAR =====
drawWiFiIcon(0, 6); // LEFT
drawBatteryIcon(112, 0); // RIGHT
// ===== TOP CENTER DATE =====
struct tm timeinfo;
//getLocalTime(&timeinfo);
timeinfo.tm_wday = 1; // Monday
timeinfo.tm_mday = 06;
timeinfo.tm_mon = 3; // April (0-based)
timeinfo.tm_year = 126; // 2026 (1900+126)
String dateText;
if(dateStage == 0){
// Mon 17
dateText = dateText = getMonthName(timeinfo.tm_mon) + " " +getDayName(timeinfo.tm_wday) + " " + String(timeinfo.tm_mday);
}
//else{
// Apr 2026
// dateText = getMonthName(timeinfo.tm_mon) + " " + String(1900 + timeinfo.tm_year);
// }
// Draw centered
display.setFont(u8g2_font_profont10_tr);
int dtWidth = display.getStrWidth(dateText.c_str());
int dtX = (128 - dtWidth) / 2;
display.setCursor(dtX, 7);
display.print(dateText);
// ===== CLOCK =====
/* char timeStr[9];
sprintf(timeStr, "%02d:%02d:%02d", hh, mm, ss);
display.setFont(u8g2_font_fub20_tf);
int textWidth = display.getStrWidth(timeStr);
int x = (128 - textWidth) / 2;
display.setCursor(x, 32);
display.print(timeStr);
*/
// ===== CLOCK (12H + AM/PM + SMALL SECONDS) =====
// Convert to 12-hour format
// ===== CLOCK (MODIFIED LAYOUT) =====
// Convert to 12-hour format
int displayHour = hh % 12;
if (displayHour == 0) displayHour = 12;
bool isPM = (hh >= 12);
// Main time (HH:MM)
char timeMain[6];
sprintf(timeMain, "%02d:%02d", displayHour, mm);
// Seconds
char secStr[3];
sprintf(secStr, "%02d", ss);
// AM/PM
const char* ampm = isPM ? "PM" : "AM";
// ===== Draw Main Time =====
display.setFont(u8g2_font_fub20_tf);
int mainWidth = display.getStrWidth(timeMain);
// Keep same center alignment as before
int baseX = (128 - (mainWidth + 28)) / 2;
// +28 ≈ space for seconds area (adjust if needed)
display.setCursor(baseX+4, 32);
display.print(timeMain);
// ===== RIGHT SIDE (OLD SECONDS AREA) =====
int secAreaX = baseX + mainWidth + 2;
// AM/PM (TOP)
display.setFont(u8g2_font_spleen6x12_mr);
display.setCursor(secAreaX+6, 20);
display.print(ampm);
// Seconds (BOTTOM)
display.setFont(u8g2_font_spleen6x12_mr);
display.setCursor(secAreaX+6, 32);
display.print(secStr);
///////////////////////////////////
// Divider
display.drawLine(0, 35, 128, 35);
display.drawLine(64, 38, 64, 64);
// ================= LEFT =================
int iconX = 1;
int iconY = 40;
int iconW = 25;
display.drawXBM(iconX, iconY, 25, 25, icon_thunder_18_swapped);
display.setFont(u8g2_font_ncenB08_tr);
int tempX = iconX + iconW + 5;
display.setCursor(tempX, 50);
display.printf("%.0f\xB0""C", outdoorTemp);
// SCROLL TEXT
display.setFont(u8g2_font_6x10_tr);
int textStartX = tempX;
int textEndX = 62;
int textY = 60;
int textWidth2 = display.getStrWidth(scrollText.c_str());
if (textWidth2 <= (textEndX - textStartX)) {
display.setCursor(textStartX, textY);
display.print(scrollText);
} else {
display.setClipWindow(textStartX, textY - 10, textEndX, textY);
display.setCursor(textStartX - scrollX, textY);
display.print(scrollText);
display.setMaxClipWindow();
scrollX++;
if (scrollX > textWidth2 + 10) scrollX = 0;
}
// ================= RIGHT =================
int centerX = 64 + (128 - 64) / 2;
display.setFont(u8g2_font_6x10_tr);
display.setCursor(centerX - 20, 48);
display.print("Indoor");
display.setFont(u8g2_font_ncenB08_tr);
char indoorStr[20];
sprintf(indoorStr, "%.0f\xB0""C / %d%%", indoorTemp, indoorHum);
display.setCursor(centerX - display.getStrWidth(indoorStr)/2, 60);
display.print(indoorStr);
display.sendBuffer();
}Loading
esp32-c3-devkitm-1
esp32-c3-devkitm-1
Loading
ssd1306
ssd1306