refactor: split monolithic .ino into modular structure + improve boot UX
## Code Structure - Split 2172-line .ino into 7 modules: - config.h - structs, enums, constants - globals.h - extern declarations, function prototypes - display.cpp - OLED display functions, Thanos dissolve effect - ntp_client.cpp - NTP sync, DST calculation - wifi_manager.cpp - WiFi connection management - weather.cpp - Open-Meteo API (async) - web_server.cpp - Web interface handlers - clock_ntp_ota_v1.9.ino - setup/loop, EEPROM, OTA (153 lines) ## Boot UX Improvements - Remove meaningless "READY" screen - Replace cryptic counter (0-7) with "WiFi..." + animated dots - Show SSID + IP on connected screen - Use yellow zone (Y 48-63) for status text - Fix text centering on all boot screens - Initialize lastBlinkTime/lastModeSwitch to prevent first-frame glitch ## Layout (dual-color display) - Blue zone (Y 0-47): main content - Yellow zone (Y 48-63): status/version - Centering formula: width = n×5 + (n-1)×1 for size 1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
c3bfaa26bc
commit
4a7e889052
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* config.h - Configuration structures and constants
|
||||
* TJ-56-654 Weather Clock v1.9.2
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
// Firmware version
|
||||
#define FIRMWARE_VERSION "1.9.2"
|
||||
|
||||
// OLED I2C Configuration
|
||||
#define I2C_SDA 0 // GPIO0 (I2C Data) - SWAPPED!
|
||||
#define I2C_SCL 2 // GPIO2 (I2C Clock) - SWAPPED!
|
||||
#define SCREEN_WIDTH 128
|
||||
#define SCREEN_HEIGHT 64
|
||||
#define OLED_RESET -1 // No reset pin
|
||||
#define OLED_ADDRESS 0x3C
|
||||
|
||||
// Configuration structure with validation
|
||||
#define CONFIG_MAGIC 0xC10CC10C // Magic number to validate EEPROM data
|
||||
|
||||
struct Config {
|
||||
uint32_t magic = CONFIG_MAGIC; // Magic number for validation
|
||||
char ssid[32] = ""; // Empty - configured via WiFiManager captive portal
|
||||
char password[64] = ""; // Empty - configured via WiFiManager captive portal
|
||||
long timezone_offset = 0; // Base UTC offset in seconds (0=Lisbon/London, 3600=Paris/Berlin)
|
||||
bool dst_enabled = true; // Auto DST: +1 hour during summer (European rules: last Sun Mar-Oct)
|
||||
int brightness = 4; // 0-7
|
||||
char ntp_server[64] = "pool.ntp.org";
|
||||
unsigned long ntp_interval = 3600; // NTP update interval in seconds (default: 1 hour)
|
||||
bool hour_format_24 = true; // true=24h, false=12h
|
||||
char hostname[32] = "tj56654-clock";
|
||||
|
||||
// Weather settings
|
||||
float latitude = 37.19; // Portimao, Portugal
|
||||
float longitude = -8.54;
|
||||
char city_name[32] = "Portimao";
|
||||
bool weather_enabled = true;
|
||||
unsigned long weather_interval = 1800; // 30 minutes in seconds
|
||||
|
||||
// Display settings
|
||||
uint8_t display_rotation_sec = 5; // Seconds per screen
|
||||
bool show_weather = true;
|
||||
bool show_sunrise_sunset = true;
|
||||
uint8_t display_orientation = 2; // 0=0°, 1=90°, 2=180°, 3=270°
|
||||
};
|
||||
|
||||
// Exponential backoff retry configuration (for NTP/Weather)
|
||||
struct RetryConfig {
|
||||
uint8_t maxRetries = 3; // Give up after 3 tries
|
||||
uint8_t currentRetry = 0;
|
||||
unsigned long nextRetryTime = 0;
|
||||
unsigned long maxBackoffMs = 8000; // Max backoff 8 seconds
|
||||
|
||||
unsigned long getBackoffDelay() {
|
||||
unsigned long delay = 1000UL * (1UL << currentRetry); // 1s, 2s, 4s, 8s...
|
||||
return (delay > maxBackoffMs) ? maxBackoffMs : delay;
|
||||
}
|
||||
|
||||
void scheduleRetry() {
|
||||
if (currentRetry < maxRetries) {
|
||||
nextRetryTime = millis() + getBackoffDelay();
|
||||
currentRetry++;
|
||||
} else {
|
||||
nextRetryTime = 0; // Max retries reached, stop
|
||||
}
|
||||
}
|
||||
|
||||
bool isRetryTime() {
|
||||
return nextRetryTime > 0 && millis() >= nextRetryTime;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
currentRetry = 0;
|
||||
nextRetryTime = 0;
|
||||
}
|
||||
|
||||
bool maxRetriesReached() {
|
||||
return currentRetry >= maxRetries;
|
||||
}
|
||||
};
|
||||
|
||||
// WiFi retry configuration - infinite retries with longer backoff
|
||||
struct WiFiRetryConfig {
|
||||
uint8_t currentRetry = 0;
|
||||
unsigned long nextRetryTime = 0;
|
||||
static const unsigned long MAX_BACKOFF_MS = 300000; // Max 5 minutes between retries
|
||||
|
||||
unsigned long getBackoffDelay() {
|
||||
// 5s, 10s, 20s, 40s, 80s, 160s, 300s (max)
|
||||
unsigned long delay = 5000UL * (1UL << currentRetry);
|
||||
return (delay > MAX_BACKOFF_MS) ? MAX_BACKOFF_MS : delay;
|
||||
}
|
||||
|
||||
void scheduleRetry() {
|
||||
nextRetryTime = millis() + getBackoffDelay();
|
||||
if (currentRetry < 10) currentRetry++; // Cap at 10 to prevent overflow
|
||||
}
|
||||
|
||||
bool isRetryTime() {
|
||||
return nextRetryTime > 0 && millis() >= nextRetryTime;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
currentRetry = 0;
|
||||
nextRetryTime = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Weather fetch state machine
|
||||
enum WeatherState {
|
||||
WEATHER_IDLE,
|
||||
WEATHER_REQUESTING,
|
||||
WEATHER_SUCCESS,
|
||||
WEATHER_FAILED
|
||||
};
|
||||
|
||||
// Async NTP state machine
|
||||
enum NTPState {
|
||||
NTP_IDLE,
|
||||
NTP_REQUEST_SENT,
|
||||
NTP_WAITING,
|
||||
NTP_SUCCESS,
|
||||
NTP_FAILED
|
||||
};
|
||||
|
||||
// Async WiFi state machine
|
||||
enum WiFiConnectionState {
|
||||
WIFI_CONN_IDLE,
|
||||
WIFI_CONN_CONNECTING,
|
||||
WIFI_CONN_CONNECTED,
|
||||
WIFI_CONN_FAILED,
|
||||
WIFI_CONN_SKIP_ASYNC // Skip async, go straight to WiFiManager
|
||||
};
|
||||
|
||||
// Weather data cache
|
||||
struct WeatherData {
|
||||
float temperature = 0.0;
|
||||
int weathercode = -1; // WMO weather code
|
||||
int humidity = 0;
|
||||
float windspeed = 0.0;
|
||||
unsigned long lastUpdate = 0;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
// Sunrise/Sunset cache
|
||||
struct SunTimes {
|
||||
int sunriseMinutes = 0; // Minutes since midnight
|
||||
int sunsetMinutes = 0;
|
||||
int lastDay = -1; // Day of year
|
||||
char sunrise[6] = "--:--"; // HH:MM format
|
||||
char sunset[6] = "--:--";
|
||||
};
|
||||
|
||||
// Dissolve transition constants
|
||||
const unsigned long DISSOLVE_DURATION = 2000; // 2 sec total (1s dissolve out + 1s dissolve in)
|
||||
const unsigned long DISSOLVE_FRAME_INTERVAL = 100; // 100ms per frame
|
||||
|
||||
// NTP timeout
|
||||
const unsigned long NTP_TIMEOUT_MS = 5000; // 5 second timeout
|
||||
|
||||
// WiFi timeout
|
||||
const unsigned long WIFI_TIMEOUT_MS = 10000; // 10 second timeout
|
||||
|
||||
#endif // CONFIG_H
|
||||
Reference in New Issue
Block a user