Rebecca Jiang
Published

UNIHIKER K10: WiFiManager Phone Setup

UNIHIKER K10 — WiFi Setup via WiFiManager (Phone-based)

BeginnerFull instructions provided1 hour60
UNIHIKER K10: WiFiManager Phone Setup

Things used in this project

Hardware components

USB Cable, USB Type C Plug
USB Cable, USB Type C Plug
×1
DFRobot UNIHIKER K10
×1

Software apps and online services

VS Code
Microsoft VS Code
PlatformIO IDE
PlatformIO IDE
Arduino IDE
Arduino IDE
wifi manager

Story

Read more

Code

Wifi Manager on K10

C/C++
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiManager.h>
#include <time.h>
#include "unihiker_k10.h"

UNIHIKER_K10 k10;
WiFiManager wifiManager;
uint8_t screen_dir = 1; // set landscape orientation (horizontal)

const char* ntpServer = "ntp.aliyun.com";
const long gmtOffsetSec = 8 * 3600;
const int daylightOffsetSec = 0;

unsigned long lastTimeUpdate = 0;
unsigned long buttonPressStart = 0;
const unsigned long longPressMs = 3000;
bool shouldSaveConfig = false;
bool portalRunning = false;

void drawStatus(const String& status, const String& detail = "") {
    // Reworked layout for landscape: short lines, separate rows to avoid truncation
    k10.canvas->canvasClear();
    k10.setScreenBackground(0x000000);

    // title at top-left
    k10.canvas->canvasText("WiFi Manager", 0, 0xFFFFFF);

    // primary status next
    k10.canvas->canvasText(status.c_str(), 1, 0x00FF00);

    // detail on its own line (short)
    if (detail.length()) {
        k10.canvas->canvasText(detail.c_str(), 2, 0x00FFFF);
    }

    // concise instructions arranged for horizontal layout
    if (!portalRunning) {
        k10.canvas->canvasText("Short press A to cfg", 4, 0x00FFFF);
    } else {
        k10.canvas->canvasText("Complete setup on phone", 4, 0x00FFFF);
    }
    k10.canvas->canvasText("Long press A to clear", 5, 0xFF0000);
    k10.canvas->updateCanvas();
}

void drawClock() {
    struct tm timeInfo;
    if (!getLocalTime(&timeInfo)) return;

    char buf[32];
    strftime(buf, sizeof(buf), "%Y-%m-%d", &timeInfo);

    k10.canvas->canvasClear();
    k10.setScreenBackground(0x000000);

    // landscape-friendly lines: each short and on separate row
    k10.canvas->canvasText("Connected", 0, 0x00FF00);
    String ipLine = "IP: " + WiFi.localIP().toString();
    k10.canvas->canvasText(ipLine.c_str(), 1, 0x00FFFF);
    k10.canvas->canvasText(buf, 3, 0xFFFFFF);
    strftime(buf, sizeof(buf), "%H:%M:%S", &timeInfo);
    k10.canvas->canvasText(buf, 4, 0xFF00FF);
    k10.canvas->updateCanvas();
}

void apModeCallback(WiFiManager* wmInstance) {
    (void)wmInstance;
    // short single-line SSID and mode
    drawStatus("AP Mode Active", "SSID: K10_Config");
}

void saveConfigCallback() {
    shouldSaveConfig = true;
    Serial.println("[WiFi] Credentials saved callback");
}

void startConfigPortal() {
    if (portalRunning) return;
    portalRunning = true;

    drawStatus("Starting portal", "Connect SSID: K10_Config");
    Serial.println("[WiFi] Starting config portal");

    wifiManager.setAPCallback(apModeCallback);
    wifiManager.setSaveConfigCallback(saveConfigCallback);
    wifiManager.setConfigPortalTimeout(180);

    bool res = wifiManager.startConfigPortal("K10_Config");
    if (res && WiFi.status() == WL_CONNECTED) {
        Serial.print("[WiFi] Connected after portal, IP: ");
        Serial.println(WiFi.localIP());
        if (shouldSaveConfig) {
            Serial.println("[WiFi] Credentials stored");
            shouldSaveConfig = false;
        }
        drawStatus("Connected", WiFi.localIP().toString());
        configTime(gmtOffsetSec, daylightOffsetSec, ntpServer);
        lastTimeUpdate = 0;
        delay(500);
        drawClock();
    } else {
        Serial.println("[WiFi] Portal exited without connection");
        drawStatus("Setup not completed", "Short press A to retry");
    }

    portalRunning = false;
}

void tryAutoConnectStored(unsigned long timeoutMs = 12000) {
    // kept for possible future use; not used when we clear credentials at startup
    drawStatus("Trying stored network...");
    WiFi.mode(WIFI_STA);
    WiFi.setAutoReconnect(true);
    WiFi.begin();
    unsigned long start = millis();
    while (millis() - start < timeoutMs) {
        if (WiFi.status() == WL_CONNECTED) {
            Serial.print("[WiFi] Connected (stored), IP: ");
            Serial.println(WiFi.localIP());
            drawStatus("Connected", WiFi.localIP().toString());
            configTime(gmtOffsetSec, daylightOffsetSec, ntpServer);
            lastTimeUpdate = 0;
            delay(300);
            drawClock();
            return;
        }
        delay(200);
    }
    Serial.println("[WiFi] Stored network connect timeout");
}

void setup() {
    Serial.begin(115200);
    k10.begin();
    k10.initScreen(screen_dir); // ensure initialization uses landscape
    k10.creatCanvas();

    drawStatus("Initializing");

    // Clear saved WiFi credentials and start portal (keep screen orientation)
    wifiManager.resetSettings();
    WiFi.disconnect(true, true);
    delay(300);
    // re-init screen to ensure orientation after reset
    k10.initScreen(screen_dir);
    drawStatus("Saved WiFi cleared", "Launching portal");
    Serial.println("[WiFi] Cleared saved WiFi credentials; launching portal now");

    // ensure callbacks are registered
    wifiManager.setAPCallback(apModeCallback);
    wifiManager.setSaveConfigCallback(saveConfigCallback);

    // immediately start config portal so user can set SSID/password again
    startConfigPortal();
}

void loop() {
    bool buttonAPressed = k10.buttonA->isPressed();

    if (buttonAPressed) {
        if (buttonPressStart == 0) {
            buttonPressStart = millis();
        }
        if (millis() - buttonPressStart >= longPressMs) {
            // long press: clear saved credentials
            wifiManager.resetSettings();
            WiFi.disconnect(true, true);
            drawStatus("WiFi cleared", "Short press A to configure");
            Serial.println("[WiFi] WiFi settings cleared");
            delay(1200);
            buttonPressStart = 0;
            return;
        }
    } else if (buttonPressStart != 0) {
        unsigned long duration = millis() - buttonPressStart;
        buttonPressStart = 0;
        if (duration < longPressMs) {
            // short press: start config portal
            startConfigPortal();
        }
    }

    if (portalRunning) {
        delay(50);
        return;
    }

    if (WiFi.status() == WL_CONNECTED) {
        if (millis() - lastTimeUpdate > 1000) {
            lastTimeUpdate = millis();
            drawClock();
        }
    } else {
        static unsigned long lastStatus = 0;
        if (millis() - lastStatus > 2000) {
            lastStatus = millis();
            drawStatus("Not connected", "Short press A to configure");
            WiFi.reconnect();
        }
    }

    delay(10);
}

Credits

Rebecca Jiang
2 projects • 3 followers
Mushroom Cloud Maker Space Operation Manager

Comments