Introduction
This week I worked on the interface for my final project, DESKO.
For this assignment, I created a web interface that allows DESKO to be controlled from a computer, phone, or tablet. Instead of only using the physical buttons on the device, the user can connect to DESKO’s own Wi-Fi network and open a local control panel in a browser.
Hero Shot Video
Group Assignment
Group assignment link: https://week-14-group-assignment-4c9ef1.fabcloud.io
What I Made
I made a local web interface for DESKO.
The ESP32-C3 creates its own Wi-Fi network:
const char* WIFI_SSID = "DESKO_Control";
const char* WIFI_PASSWORD = "12345678";
After connecting to this network, the user opens:
http://192.168.4.1
The page then shows the DESKO control panel.
Interface Features
The web interface includes these controls:
| Section | Function |
|---|---|
| Status | Shows clock time, alarm state, light mode, and countdown |
| Set Clock Time | Changes the software clock time |
| Alarm | Sets alarm hour, minute, and ON/OFF state |
| Ambient Light | Changes the NeoPixel lighting mode |
| Countdown | Sets, starts, pauses, and resets the countdown |
| Quick Controls | Stops the alarm/buzzer or turns LEDs off |
The interface was designed to be simple because it is meant to be used quickly from a phone or computer.
How It Works
The ESP32-C3 works as a Wi-Fi access point and web server. The browser sends requests to the ESP32-C3, and the ESP32-C3 updates the clock variables, LEDs, buzzer, or display.
The communication structure is:
Phone / Computer
|
| browser request
|
DESKO Wi-Fi network
|
| local web server
|
ESP32-C3
|
| output control
|
E-paper display, NeoPixels, buzzer
The main server setup is:
void setupWebInterface() {
WiFi.mode(WIFI_AP);
WiFi.softAP(WIFI_SSID, WIFI_PASSWORD);
server.on("/", handleWebPage);
server.on("/setTime", handleSetTimeWeb);
server.on("/setAlarm", handleSetAlarmWeb);
server.on("/ambient", handleAmbientWeb);
server.on("/countdown", handleCountdownWeb);
server.on("/countdownToggle", handleCountdownToggleWeb);
server.on("/countdownReset", handleCountdownResetWeb);
server.on("/stopAlarm", handleStopAlarmWeb);
server.on("/ledOff", handleLedOffWeb);
server.begin();
}
For this part I was stuck on how I was going to start the setup so got help from ChatGPT.
In the main loop, the web server is updated continuously:
void loop() {
server.handleClient();
updateSoftwareClock();
if (btnMode.pressed()) handleModeButton();
if (btnUp.pressed()) handleUpButton();
if (btnOk.pressed()) handleOkButton();
handleMinuteDisplayUpdate();
handleAlarm();
handleCountdown();
handleStopwatchDisplay();
applyAmbientLighting();
if (displayDirty) updateDisplay();
}
This lets the physical buttons and the web interface work in the same program.
Web Page Design
The web page is generated directly inside the ESP32-C3 code. I used HTML and CSS strings inside the handleWebPage() function.
The page has a card-based layout:
page += "<h1>DESKO</h1>";
page += "<p class='sub'>Desk clock control panel</p>";
Each control is placed inside a card. For example, the alarm section uses a form:
page += "<div class='card'><h2>Alarm</h2>";
page += "<form action='/setAlarm' method='GET'>";
page += "<input type='number' name='hour' min='0' max='23'>";
page += "<input type='number' name='minute' min='0' max='59'>";
page += "<select name='enabled'>";
page += "<button type='submit'>Set Alarm</button>";
page += "</form></div>";
When the user presses a button, the browser sends a GET request to the ESP32-C3.
Clock Control
The clock time can be changed from the web interface. The /setTime route reads the hour and minute values and updates the software clock.
void handleSetTimeWeb() {
if (server.hasArg("hour")) {
currentHour = constrain(server.arg("hour").toInt(), 0, 23);
}
if (server.hasArg("minute")) {
currentMinute = constrain(server.arg("minute").toInt(), 0, 59);
}
resetClockSeconds();
displayDirty = true;
redirectHome();
}
After changing the time, displayDirty becomes true, so the e-paper display refreshes with the new time.
Alarm Control
The alarm can also be set from the browser. The user can choose the hour, minute, and whether the alarm is ON or OFF.
void handleSetAlarmWeb() {
if (server.hasArg("hour")) {
alarmHour = constrain(server.arg("hour").toInt(), 0, 23);
}
if (server.hasArg("minute")) {
alarmMinute = constrain(server.arg("minute").toInt(), 0, 59);
}
if (server.hasArg("enabled")) {
alarmEnabled = server.arg("enabled").toInt() == 1;
}
alarmRinging = false;
lastTriggeredAlarmMinute = -1;
stopBuzzer();
displayDirty = true;
redirectHome();
}
This connects directly to the alarm logic in the final project code. When the alarm time approaches, the wake-up light starts gradually. When the alarm time is reached, the buzzer rings and the lights turn warm and bright.
Ambient Light Control
The ambient light section controls the NeoPixels. The available modes are:
| Mode | Description |
|---|---|
| OFF | Turns the LEDs off |
| WARM | Warm orange light |
| BEIGE BREATH | Slowly rising and falling beige light |
| BLUE NIGHT | Dim blue night light |
| RAINBOW | Animated rainbow lighting |
| FOCUS WHITE | Bright warm white light |
The selected mode is sent through the /ambient route.
void handleAmbientWeb() {
if (server.hasArg("mode")) {
int mode = constrain(server.arg("mode").toInt(), 0, AMBIENT_COUNT - 1);
ambientMode = (AmbientMode)mode;
if (ambientMode != AMBIENT_OFF) {
lastNonOffAmbient = ambientMode;
} else {
ledsOff();
}
displayDirty = true;
}
redirectHome();
}
This worked well because the LED response was immediate and easy to see during testing.
Countdown Control
The countdown page lets the user set a countdown duration between 1 and 120 minutes. The countdown can be started, paused, and reset from the browser.
void handleCountdownWeb() {
if (server.hasArg("minutes")) {
countdownSetMinutes = constrain(server.arg("minutes").toInt(), 1, 120);
countdownTotalSeconds = countdownSetMinutes * 60UL;
countdownRemainingSeconds = countdownTotalSeconds;
countdownRunning = false;
countdownFinishedRinging = false;
stopBuzzer();
showCountdownPixels();
displayDirty = true;
}
redirectHome();
}
The countdown is also connected to the NeoPixels. As time passes, the LEDs visually represent the remaining time.
void handleCountdownToggleWeb() {
if (countdownFinishedRinging) {
countdownFinishedRinging = false;
countdownRemainingSeconds = countdownTotalSeconds;
stopBuzzer();
}
countdownRunning = !countdownRunning;
lastCountdownTick = millis();
displayDirty = true;
redirectHome();
}
Quick Controls
I added two quick controls because they are useful during normal use and testing.
The first one stops the alarm or countdown buzzer:
void handleStopAlarmWeb() {
alarmRinging = false;
countdownFinishedRinging = false;
stopBuzzer();
ledsOff();
displayDirty = true;
redirectHome();
}
The second one turns the LEDs off:
void handleLedOffWeb() {
ambientMode = AMBIENT_OFF;
ledsOff();
displayDirty = true;
redirectHome();
}
These buttons make the interface more practical because the user can stop the device quickly without using the physical buttons.
Testing
I tested the interface in these steps:
- Uploaded the code to the ESP32-C3.
- Opened the Serial Monitor.
- Connected my computer to the
DESKO_ControlWi-Fi network. - Opened
http://192.168.4.1in a browser. - Tested setting the clock time.
- Tested changing the alarm.
- Tested each ambient light mode.
- Tested countdown start, pause, and reset.
- Tested stopping the buzzer and turning LEDs off.
Problems and Fixes
Problem 1 - I needed the interface to work without a router
At first, I considered connecting the ESP32-C3 to an existing Wi-Fi network. However, this would make the project depend on another network.
I fixed this by making the ESP32-C3 create its own access point.
WiFi.mode(WIFI_AP);
WiFi.softAP(WIFI_SSID, WIFI_PASSWORD);
This made DESKO easier to use because the user can connect directly to it.
Problem 2 - The display needed to update after web changes
When values changed from the web interface, the e-paper display also needed to refresh. I fixed this by setting:
displayDirty = true;
after web actions such as setting the clock, alarm, ambient mode, and countdown.
Problem 3 - Alarm and countdown needed a quick stop button
During testing, it was annoying to stop the buzzer only from the physical buttons. I added a web route called /stopAlarm so the alarm or countdown buzzer can be stopped from the browser.
Final Result
The final result is a working browser interface for DESKO. It can control the clock time, alarm, ambient light, countdown, buzzer, and LEDs from a phone or computer.
This week was useful for my final project because it made DESKO feel more complete. The physical buttons still work, but the web interface gives the user another way to control the same device.