Week 14 - Interface and Application Programming


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:

SectionFunction
StatusShows clock time, alarm state, light mode, and countdown
Set Clock TimeChanges the software clock time
AlarmSets alarm hour, minute, and ON/OFF state
Ambient LightChanges the NeoPixel lighting mode
CountdownSets, starts, pauses, and resets the countdown
Quick ControlsStops 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:

ModeDescription
OFFTurns the LEDs off
WARMWarm orange light
BEIGE BREATHSlowly rising and falling beige light
BLUE NIGHTDim blue night light
RAINBOWAnimated rainbow lighting
FOCUS WHITEBright 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:

  1. Uploaded the code to the ESP32-C3.
  2. Opened the Serial Monitor.
  3. Connected my computer to the DESKO_Control Wi-Fi network.
  4. Opened http://192.168.4.1 in a browser.
  5. Tested setting the clock time.
  6. Tested changing the alarm.
  7. Tested each ambient light mode.
  8. Tested countdown start, pause, and reset.
  9. 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.