diff --git a/lib/Utils/src/ColorSerial.cpp b/lib/Utils/src/ColorSerial.cpp new file mode 100644 index 0000000..7910f3e --- /dev/null +++ b/lib/Utils/src/ColorSerial.cpp @@ -0,0 +1,398 @@ +#include "ColorSerial.h" + +// ANSI Escape Codes +const char *ColorSerial::RESET = "\033[0m"; +const char *ColorSerial::BOLD = "\033[1m"; +const char *ColorSerial::UNDERLINE = "\033[4m"; +const char *ColorSerial::ITALIC = "\033[3m"; + +// Standard Colors +const char *ColorSerial::RED = "\033[38;2;255;0;0m"; +const char *ColorSerial::GREEN = "\033[38;2;0;255;0m"; +const char *ColorSerial::BLUE = "\033[38;2;0;120;255m"; +const char *ColorSerial::YELLOW = "\033[38;2;255;255;0m"; +const char *ColorSerial::CYAN = "\033[38;2;0;255;255m"; +const char *ColorSerial::MAGENTA = "\033[38;2;255;0;255m"; +const char *ColorSerial::ORANGE = "\033[38;2;255;165;0m"; +const char *ColorSerial::WHITE = "\033[38;2;255;255;255m"; +const char *ColorSerial::GRAY = "\033[38;2;128;128;128m"; + +// Icons +const char *ColorSerial::ICON_SUCCESS = "✅"; +const char *ColorSerial::ICON_ERROR = "❌"; +const char *ColorSerial::ICON_WARNING = "⚠️"; +const char *ColorSerial::ICON_INFO = "ℹ️"; +const char *ColorSerial::ICON_DEBUG = "🐛"; +const char *ColorSerial::ICON_WIFI = "📡"; +const char *ColorSerial::ICON_WEATHER = "🌡️"; + +// Main Logging Methods +void ColorSerial::success(const char *message, const char *details) +{ + Serial.print(GREEN); + Serial.print(ICON_SUCCESS); + Serial.print(" SUCCESS: "); + Serial.print(RESET); + Serial.print(BOLD); + Serial.print(message); + Serial.print(RESET); + + if (strlen(details) > 0) + { + Serial.print(" "); + Serial.print(GRAY); + Serial.print("("); + Serial.print(details); + Serial.print(")"); + Serial.print(RESET); + } + Serial.println(); +} + +void ColorSerial::error(const char *message, const char *details) +{ + Serial.print(RED); + Serial.print(ICON_ERROR); + Serial.print(" ERROR: "); + Serial.print(RESET); + Serial.print(BOLD); + Serial.print(message); + Serial.print(RESET); + + if (strlen(details) > 0) + { + Serial.print(" "); + Serial.print(RED); + Serial.print(details); + Serial.print(RESET); + } + Serial.println(); +} + +void ColorSerial::warning(const char *message, const char *details) +{ + Serial.print(YELLOW); + Serial.print(ICON_WARNING); + Serial.print(" WARNING: "); + Serial.print(RESET); + Serial.print(BOLD); + Serial.print(message); + Serial.print(RESET); + + if (strlen(details) > 0) + { + Serial.print(" "); + Serial.print(GRAY); + Serial.print("("); + Serial.print(details); + Serial.print(")"); + Serial.print(RESET); + } + Serial.println(); +} + +void ColorSerial::info(const char *message, const char *details) +{ + Serial.print(CYAN); + Serial.print(ICON_INFO); + Serial.print(" INFO: "); + Serial.print(RESET); + Serial.print(BOLD); + Serial.print(message); + Serial.print(RESET); + + if (strlen(details) > 0) + { + Serial.print(" "); + Serial.print(GRAY); + Serial.print("("); + Serial.print(details); + Serial.print(")"); + Serial.print(RESET); + } + Serial.println(); +} + +void ColorSerial::debug(const char *message, const char *details) +{ + Serial.print(MAGENTA); + Serial.print(ICON_DEBUG); + Serial.print(" DEBUG: "); + Serial.print(RESET); + Serial.print(ITALIC); + Serial.print(message); + Serial.print(RESET); + + if (strlen(details) > 0) + { + Serial.print(" "); + Serial.print(GRAY); + Serial.print("["); + Serial.print(details); + Serial.print("]"); + Serial.print(RESET); + } + Serial.println(); +} + +// Data Display Methods +void ColorSerial::data(const char *label, const char *value, const char *unit) +{ + Serial.print(BLUE); + Serial.print(label); + Serial.print(":"); + Serial.print(RESET); + Serial.print(" "); + Serial.print(BOLD); + Serial.print(WHITE); + Serial.print(value); + Serial.print(RESET); + Serial.print(unit); + Serial.println(); +} + +void ColorSerial::data(const char *label, float value, const char *unit) +{ + char buf[20]; + snprintf(buf, sizeof(buf), "%.1f", value); + data(label, buf, unit); +} + +void ColorSerial::data(const char *label, int value, const char *unit) +{ + char buf[20]; + snprintf(buf, sizeof(buf), "%d", value); + data(label, buf, unit); +} + +void ColorSerial::temperature(float temp, const char *label) +{ + const char *color; + if (temp < 0) + color = CYAN; + else if (temp < 10) + color = BLUE; + else if (temp < 20) + color = GREEN; + else if (temp < 30) + color = YELLOW; + else + color = RED; + + Serial.print(BLUE); + Serial.print(label); + Serial.print(": "); + Serial.print(RESET); + Serial.print(color); + Serial.print(temp, 1); + Serial.print("°C"); + Serial.print(RESET); + Serial.println(); +} + +void ColorSerial::wifiStatus() +{ + if (WiFi.status() == WL_CONNECTED) + { + Serial.print(GREEN); + Serial.print(ICON_WIFI); + Serial.print(" WiFi: "); + Serial.print(RESET); + Serial.print(BOLD); + Serial.print("Connected"); + Serial.print(GRAY); + Serial.print(" (IP: "); + Serial.print(WiFi.localIP().toString().c_str()); + Serial.print(")"); + Serial.print(RESET); + Serial.println(); + } + else + { + Serial.print(RED); + Serial.print(ICON_WIFI); + Serial.print(" WiFi: "); + Serial.print(RESET); + Serial.print(BOLD); + Serial.print("Disconnected"); + Serial.print(RESET); + Serial.println(); + } +} + +// Progress Bars +void ColorSerial::progressBar(int percent, const char *label, int width) +{ + int pos = (percent * width) / 100; + const char *color = (percent < 30) ? RED : (percent < 70) ? YELLOW + : GREEN; + + Serial.print(BLUE); + Serial.print(label); + Serial.print(" ["); + Serial.print(RESET); + + for (int i = 0; i < width; i++) + { + if (i < pos) + { + Serial.print(color); + Serial.print("█"); + } + else + { + Serial.print(GRAY); + Serial.print("░"); + } + } + + Serial.print(RESET); + Serial.print(BLUE); + Serial.print("] "); + Serial.print(color); + Serial.print(percent); + Serial.print("%"); + Serial.print(RESET); + Serial.println(); +} + +// Section Headers +void ColorSerial::header(const char *title, char decorator, int width) +{ + int titleLen = strlen(title); + int padding = (width - titleLen - 2) / 2; + + Serial.println(); + + // Top line + Serial.print(MAGENTA); + for (int i = 0; i < width; i++) + { + Serial.print(decorator); + } + Serial.print(RESET); + Serial.println(); + + // Title line + Serial.print(MAGENTA); + Serial.print("║"); + Serial.print(RESET); + + for (int i = 0; i < padding; i++) + { + Serial.print(" "); + } + + Serial.print(BOLD); + Serial.print(title); + Serial.print(RESET); + Serial.println(); + + // Bottom line + Serial.print(MAGENTA); + for (int i = 0; i < width; i++) + { + Serial.print(decorator); + } + Serial.print(RESET); + Serial.println(); +} + +void ColorSerial::subheader(const char *title) +{ + Serial.print(CYAN); + Serial.print("▶ "); + Serial.print(RESET); + Serial.print(BOLD); + Serial.print(title); + Serial.print(RESET); + Serial.println(); +} + +// Weather Specific Private Methods +const char *ColorSerial::getWeatherCondition(int code) +{ + switch (code) + { + case 0: + return "Clear sky"; + case 1: + return "Mainly clear"; + case 2: + return "Partly cloudy"; + case 3: + return "Overcast"; + case 45: + case 48: + return "Fog"; + case 51: + case 53: + case 55: + return "Drizzle"; + case 61: + case 63: + case 65: + return "Rain"; + case 71: + case 73: + case 75: + return "Snow"; + case 95: + case 96: + case 99: + return "Thunderstorm"; + default: + return "Unknown weather"; + } +} + +const char *ColorSerial::getWeatherIcon(int code) +{ + if (code == 0) + return "☀️"; + if (code <= 2) + return "⛅"; + if (code == 3) + return "☁️"; + if (code <= 55) + return "🌧️"; + if (code <= 65) + return "🌧️"; + if (code <= 79) + return "❄️"; + return "🌩️"; +} + +const char *ColorSerial::getWeatherColor(int code) +{ + if (code <= 2) + return YELLOW; + if (code == 3) + return GRAY; + if (code <= 55) + return CYAN; + if (code <= 65) + return BLUE; + if (code <= 79) + return WHITE; + return MAGENTA; +} + +void ColorSerial::weatherCondition(int code, const char *customLabel) +{ + const char *label = (customLabel == nullptr || strlen(customLabel) == 0) ? "Condition" : customLabel; + const char *condition = getWeatherCondition(code); + const char *icon = getWeatherIcon(code); + const char *color = getWeatherColor(code); + + Serial.print(BLUE); + Serial.print(label); + Serial.print(": "); + Serial.print(RESET); + Serial.print(color); + Serial.print(icon); + Serial.print(" "); + Serial.print(condition); + Serial.print(RESET); + Serial.println(); +} \ No newline at end of file diff --git a/lib/Utils/src/ColorSerial.h b/lib/Utils/src/ColorSerial.h new file mode 100644 index 0000000..3cdc1a3 --- /dev/null +++ b/lib/Utils/src/ColorSerial.h @@ -0,0 +1,66 @@ +#ifndef COLORSERIAL_H +#define COLORSERIAL_H + +#include +#include + +class ColorSerial +{ +private: + static const char *getWeatherCondition(int code); + static const char *getWeatherIcon(int code); + static const char *getWeatherColor(int code); + +public: + // ANSI Escape Codes + static const char *RESET; + static const char *BOLD; + static const char *UNDERLINE; + static const char *ITALIC; + + // Standard Colors + static const char *RED; + static const char *GREEN; + static const char *BLUE; + static const char *YELLOW; + static const char *CYAN; + static const char *MAGENTA; + static const char *ORANGE; + static const char *WHITE; + static const char *GRAY; + + // Icons + static const char *ICON_SUCCESS; + static const char *ICON_ERROR; + static const char *ICON_WARNING; + static const char *ICON_INFO; + static const char *ICON_DEBUG; + static const char *ICON_WIFI; + static const char *ICON_WEATHER; + + // Main Logging Methods + static void success(const char *message, const char *details = ""); + static void error(const char *message, const char *details = ""); + static void warning(const char *message, const char *details = ""); + static void info(const char *message, const char *details = ""); + static void debug(const char *message, const char *details = ""); + + // Data Display Methods + static void data(const char *label, const char *value, const char *unit = ""); + static void data(const char *label, float value, const char *unit = ""); + static void data(const char *label, int value, const char *unit = ""); + static void temperature(float temp, const char *label = "Temperature"); + static void wifiStatus(); + + // Progress Bars + static void progressBar(int percent, const char *label = "", int width = 20); + + // Section Headers + static void header(const char *title, char decorator = '═', int width = 40); + static void subheader(const char *title); + + // Weather Specific + static void weatherCondition(int code, const char *customLabel = ""); +}; + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 44a856f..0de1db5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,8 @@ #include +#include #include #include +#include #include #include #include @@ -93,9 +95,26 @@ void setupMatrix() matrix.show(); } -void get_json_data() +void connect_wifi() { - Serial.println("get_json_data()"); + // For Wokwi simulation - use simulated WiFi + Serial.println("Connecting to WiFi..."); + + // In Wokwi, WiFi connects automatically in simulation + WiFi.begin("Wokwi-GUEST", ""); // Wokwi's simulated WiFi + while (WiFi.status() != WL_CONNECTED) + { + delay(250); + Serial.print("."); + } + Serial.println("\nConnected to WiFi!"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); +} + +void json_with_WiFiClient() +{ + Serial.println("json_with_WiFiClient()"); WiFiClient client; const int timeout = 5000; // 5 second timeout @@ -176,21 +195,54 @@ void get_json_data() } } -void connect_wifi() +void json_with_HTTPClient() { - // For Wokwi simulation - use simulated WiFi - Serial.println("Connecting to WiFi..."); + Serial.println("json_with_HTTPClient()"); - // In Wokwi, WiFi connects automatically in simulation - WiFi.begin("Wokwi-GUEST", ""); // Wokwi's simulated WiFi - while (WiFi.status() != WL_CONNECTED) + HTTPClient http; + + http.begin("http://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t_weather=true"); + // http.setTimeout(5000); + + int httpCode = http.GET(); + + if (httpCode != HTTP_CODE_OK) { - delay(250); - Serial.print("."); + Serial.printf("HTTP error: %d\n", httpCode); + http.end(); + return; + } + + Serial.println("connected to api.open-meteo.com :-)"); + Serial.println("=== Full Response Complete ==="); + + String payload = http.getString(); + http.end(); + + Serial.println(payload); + + // JSON parsen + JsonDocument doc; + DeserializationError error = deserializeJson(doc, payload); + + if (!error) + { + JsonObject current = doc["current_weather"]; + JsonObject units = doc["current_weather_units"]; + + float temperature = current["temperature"]; + String temp_unit = units["temperature"]; + + Serial.printf("Temperature: %.1f %s", temperature, temp_unit.c_str()); + Serial.println(); + } + else + { + Serial.println("JSON parse failed"); + Serial.println(error.c_str()); + + return; } - Serial.println("\nConnected to WiFi!"); - Serial.print("IP address: "); - Serial.println(WiFi.localIP()); } void setup() @@ -200,14 +252,16 @@ void setup() // Wait for Serial to be ready (important for some boards) while (!Serial) - { delay(10); - } + + ColorSerial::header("PICO W WEATHER STATION"); + ColorSerial::info("System starting up", "v1.0"); connect_wifi(); if (WiFi.status() == WL_CONNECTED) { - get_json_data(); + json_with_WiFiClient(); + json_with_HTTPClient(); } else {