/* * ============================================================================= * Fab Academy 2026 — Week 15 — Yaroslav Artsishevskiy * Web buttons → OLED on the Seeed expansion board * ----------------------------------------------------------------------------- * Same approach as the LED version, but now controlling the OLED screen * on the Seeed XIAO Expansion Board. * * Three buttons in the browser: * [Hello] — show the word "Hello" on the screen * [Blink] — make "Hello" blink on/off once per second * [Clear] — wipe the screen * * The OLED is connected to the XIAO over I2C internally on the expansion * board (SDA = D4, SCL = D5). The U8g2 library handles the I2C automatically. * * Library needed (Arduino IDE → Library Manager → search "U8g2"): * U8g2 by oliver * ============================================================================= */ #include #include "Network.h" #include "WiFi.h" #include #include // U8g2's "text only" mode — simpler and uses less memory // OLED on the expansion board: SSD1306, 128x64, I2C. // This constructor matches the one in the official Seeed wiki. U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE); NetworkUDP udp; WebServer server(80); const char* ssid = "YaroUDP"; const char* password = "12345678"; IPAddress local_IP(192, 168, 4, 1); IPAddress gateway(192, 168, 4, 1); IPAddress subnet(255, 255, 255, 0); const uint16_t udpPort = 1234; // ---- Modes ---------------------------------------------------------------- // Same idea as before: a single variable says what the screen should do. enum Mode { MODE_CLEAR, MODE_HELLO, MODE_BLINK }; Mode currentMode = MODE_CLEAR; // ---- The webpage ---------------------------------------------------------- const char HTML_PAGE[] PROGMEM = R"HTML( YaroOLED

YaroOLED

)HTML"; // ---- Helper functions ----------------------------------------------------- // Draw "Hello" on the screen at a fixed position. void drawHello() { u8x8.clearDisplay(); u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.setCursor(4, 3); // column 4, row 3 (in 8x8 character cells) u8x8.print("Hello"); } // Wipe the screen. void clearScreen() { u8x8.clearDisplay(); } // ---- HTTP handlers -------------------------------------------------------- void handleRoot() { server.send_P(200, "text/html", HTML_PAGE); } void handleHello() { currentMode = MODE_HELLO; Serial.println("HELLO"); server.send(200, "text/plain", "OK"); } void handleBlink() { currentMode = MODE_BLINK; Serial.println("BLINK"); server.send(200, "text/plain", "OK"); } void handleClear() { currentMode = MODE_CLEAR; Serial.println("CLEAR"); server.send(200, "text/plain", "OK"); } // ---- Setup ---------------------------------------------------------------- void setup() { Serial.begin(115200); delay(1000); // OLED setup u8x8.begin(); u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.clearDisplay(); // Wi-Fi access point — same as before Network.begin(); WiFi.mode(WIFI_AP); WiFi.softAPConfig(local_IP, gateway, subnet); WiFi.softAP(ssid, password); udp.begin(udpPort); server.on("/", handleRoot); server.on("/hello", handleHello); server.on("/blink", handleBlink); server.on("/clear", handleClear); server.begin(); Serial.println("Ready"); Serial.print("AP IP: "); Serial.println(WiFi.softAPIP()); } // ---- Loop ----------------------------------------------------------------- // Important: we only redraw the screen when something *changes*, not on every // loop iteration. Constantly redrawing flickers the OLED and slows everything. // // We remember which mode we drew last time. If the mode is the same as last // time, we don't redraw. The only exception is BLINK mode, which has to keep // updating to make the text appear and disappear. void loop() { server.handleClient(); static Mode lastDrawnMode = MODE_CLEAR; static bool blinkVisible = false; static unsigned long lastBlink = 0; switch (currentMode) { case MODE_CLEAR: // Only clear once when entering the mode if (lastDrawnMode != MODE_CLEAR) { clearScreen(); lastDrawnMode = MODE_CLEAR; } break; case MODE_HELLO: // Only draw once when entering the mode if (lastDrawnMode != MODE_HELLO) { drawHello(); lastDrawnMode = MODE_HELLO; } break; case MODE_BLINK: // Toggle "Hello" on/off every 500 ms using millis() if (millis() - lastBlink >= 500) { lastBlink = millis(); blinkVisible = !blinkVisible; if (blinkVisible) drawHello(); else clearScreen(); } lastDrawnMode = MODE_BLINK; break; } // Week 11 UDP listener — kept as it was int packetSize = udp.parsePacket(); if (packetSize) { char incoming[255]; int len = udp.read(incoming, sizeof(incoming) - 1); if (len > 0) incoming[len] = '\0'; Serial.print("Received UDP: "); Serial.println(incoming); } }