#include "Display.h" #include "Config.h" #include "Log.h" #include #include #include Display::Display() : oled_(128, 32, &Wire, -1) {} bool Display::begin() { Wire.begin(GPIO_SDA, GPIO_SCL); Wire.setClock(400000); // keeps a full 128x32 framebuffer update near 15 ms // An absent optional OLED produces a large burst of ESP-IDF NACK messages. // Probe it once and keep the I2C driver quiet when no display is connected. esp_log_level_set("i2c.master", ESP_LOG_NONE); Wire.beginTransmission(OLED_ADDRESS); if (Wire.endTransmission() != 0) { ok_ = false; Log::printf("OLED", "not detected at I2C address=0x%02X", OLED_ADDRESS); return false; } ok_ = oled_.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS); if (ok_) { oled_.setRotation(OLED_ROTATION); oled_.setTextColor(SSD1306_WHITE); oled_.setTextSize(1); } Log::printf("OLED", "initialization %s, I2C address=0x%02X", ok_ ? "OK" : "FAILED", OLED_ADDRESS); return ok_; } void Display::fit(char *s) { int16_t x, y; uint16_t w, h; while (*s) { oled_.getTextBounds(s, 0, 0, &x, &y, &w, &h); if (w <= 128) break; s[strlen(s) - 1] = '\0'; } } void Display::show(const char *a, const char *b, uint32_t progress, uint32_t progressTotal) { char one[32], two[32]; snprintf(one, sizeof(one), "%s", a ? a : ""); snprintf(two, sizeof(two), "%s", b ? b : ""); // Serial is the primary UI mirror and remains available when OLED is absent. Log::printf("UI", "%s | %s", one, two); if (!ok_) return; fit(one); fit(two); oled_.clearDisplay(); oled_.setCursor(0, 3); oled_.print(one); oled_.setCursor(0, 19); oled_.print(two); if (progressTotal) { if (progress > progressTotal) progress = progressTotal; const uint16_t width = static_cast( (static_cast(progress) * 128U + progressTotal - 1U) / progressTotal); if (width) oled_.drawFastHLine(0, 31, width, SSD1306_WHITE); } oled_.display(); } void Display::formatFrequency(float hz, char *out, size_t n) { float value = hz; const char *suffix = "Hz"; if (hz >= 999950.0f) { value = hz / 1000000.0f; suffix = "M"; } else if (hz >= 1000.0f) { value = hz / 1000.0f; suffix = "k"; } if (suffix[0] == 'M' && fabsf(value - roundf(value)) < 0.0005f) snprintf(out, n, "%.0f%s", value, suffix); else if (value >= 100.0f) snprintf(out, n, "%.1f%s", value, suffix); else if (value >= 10.0f) snprintf(out, n, "%.2f%s", value, suffix); else snprintf(out, n, "%.3f%s", value, suffix); } void Display::formatTestFrequency(uint32_t hz, char *out, size_t n) { if (hz >= 1000000U && hz % 1000000U == 0) snprintf(out, n, "%luM", hz / 1000000U); else if (hz >= 1000U && hz % 1000U == 0) snprintf(out, n, "%luk", hz / 1000U); else if (hz >= 1000U) { const float khz = hz / 1000.0f; if (khz >= 100.0f) snprintf(out, n, "%.1fk", khz); else if (khz >= 10.0f) snprintf(out, n, "%.2fk", khz); else snprintf(out, n, "%.3fk", khz); } else snprintf(out, n, "%lu", hz); } void Display::formatDuration(uint64_t us, char *out, size_t n) { const uint64_t totalSeconds = (us + 999999ULL) / 1000000ULL; const uint64_t minutes = totalSeconds / 60ULL; if (minutes < 60) snprintf(out, n, "%02llu:%02llu", minutes, totalSeconds % 60ULL); else snprintf(out, n, "%llu:%02llu", minutes / 60ULL, minutes % 60ULL); }