// Install from the Library Manager: // "LiquidCrystal I2C" // Board: "ESP32 Dev Module" #include #include #include #include #include // Your Wi-Fi network's name and password const char* WIFI_NAME = "Network_Name"; const char* WIFI_PASSWORD = "Password"; // The Central Bank's daily rates list const char* RATES_URL = "https://www.tcmb.gov.tr/kurlar/today.xml"; // Rates come out on workdays at 15:30; // a look every half hour is enough (ms) const unsigned long FETCH_EVERY = 30UL * 60 * 1000; // If they fail, try again after this const unsigned long RETRY_AFTER = 60000; // Time between screen pages (ms) const unsigned long PAGE_EVERY = 4000; // The ESP32's I2C pins const int SDA_PIN = 21; const int SCL_PIN = 22; // Ask about the password after this (ms) const unsigned long WIFI_HINT_AFTER = 15000; // I2C address: 0x27 on most, 0x3F on some LiquidCrystal_I2C lcd(0x27, 16, 2); // The last selling rates read; 0: none float usd = 0; float eur = 0; float gbp = 0; // The rates' date, e.g. "30.10.2026" String rateDate = ""; unsigned long nextFetch = 0; unsigned long lastPage = 0; int page = 0; // Writes a line and blanks the rest // (no flicker) void printLine(int row, const String& text) { lcd.setCursor(0, row); lcd.print(text); for (int n = text.length(); n < 16; n++) { lcd.print(' '); } } // Finds a currency's selling rate // (ForexSelling); 0 if it is missing float sellingRate(const String& xml, const char* code) { int at = xml.indexOf(String("CurrencyCode=\"") + code + "\""); if (at < 0) return 0; int start = xml.indexOf("", at); if (start < 0) return 0; start += 14; int end = xml.indexOf('<', start); return xml.substring(start, end).toFloat(); } // Downloads the list; if it fails, the // old rates stay on screen bool fetchRates() { WiFiClientSecure client; // The certificate is not checked: only // a public list is being read client.setInsecure(); HTTPClient http; http.begin(client, RATES_URL); // Ask for it plain: to the ESP32's own // request TCMB answers with gzip http.setAcceptEncoding("identity"); int status = http.GET(); if (status != 200) { Serial.print("Could not get rates, HTTP "); Serial.println(status); http.end(); return false; } String xml = http.getString(); http.end(); // The list gives its date at the top: // Tarih="30.10.2026" int at = xml.indexOf("Tarih=\""); if (at >= 0) { int end = xml.indexOf('"', at + 7); rateDate = xml.substring(at + 7, end); } float newUsd = sellingRate(xml, "USD"); if (newUsd == 0) { // Not the rates list: its start shows // what went wrong Serial.print("Unexpected answer ("); Serial.print(xml.length()); Serial.print(" bytes): "); Serial.println(xml.substring(0, 160)); return false; } usd = newUsd; eur = sellingRate(xml, "EUR"); gbp = sellingRate(xml, "GBP"); Serial.print(rateDate); Serial.print(" USD "); Serial.print(usd, 4); Serial.print(" EUR "); Serial.print(eur, 4); Serial.print(" GBP "); Serial.println(gbp, 4); return usd > 0; } // "USD/TL 49.8644": exactly 16 chars String rateLine(const char* code, float rate) { char text[17]; snprintf(text, sizeof text, "%s/TL %8.4f", code, rate); return String(text); } void showPage() { if (usd == 0) { printLine(0, "No rates yet"); printLine(1, "Will try again"); return; } if (page == 0) { printLine(0, rateLine("USD", usd)); printLine(1, rateLine("EUR", eur)); } else { printLine(0, rateLine("GBP", gbp)); printLine(1, "TCMB " + rateDate); } } void setup() { Serial.begin(115200); Wire.begin(SDA_PIN, SCL_PIN); lcd.init(); lcd.backlight(); lcd.print("Waiting for WiFi"); Serial.print("Connecting to "); Serial.println(WIFI_NAME); WiFi.begin(WIFI_NAME, WIFI_PASSWORD); unsigned long start = millis(); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); if (millis() - start > WIFI_HINT_AFTER) { // No luck: check name, password lcd.setCursor(0, 1); lcd.print("Name/password?"); } } Serial.println(); Serial.println("Connected"); printLine(0, "Getting rates"); printLine(1, ""); } void loop() { // At start-up, then every half hour if ((long)(millis() - nextFetch) >= 0) { bool ok = fetchRates(); nextFetch = millis() + (ok ? FETCH_EVERY : RETRY_AFTER); showPage(); } // Two pages in turn: USD and EUR, then // GBP and the rates' date if (millis() - lastPage > PAGE_EVERY) { lastPage = millis(); page = 1 - page; showPage(); } }