Skip to content

15. Interface and Application Programming

Group assignment:

  • Compare as many tool options as possible.
  • Document your work on the group work page and reflect on your individual page what you learned.

This is my group page.

Individual assignment

  • Write an application for the embedded board that you made. that interfaces a user with an input and/or output device(s)

The UI I made

For this week I decided to make a mini version of my interactive IR frame that I was going to be using for my touch screen magic morrir.

How it works

How this works is, I am using IR trasmitters and IR receivers to make a grid across a 3x3 LED gird. The IR leds then send a beam across to where the receivers are and if the beam is disrupted then it turns on the LED that the finger is hovering over. This is very simular to Andrew Puky’s there for I will be using his documenting to help me.

3D Printing

My first step of this was to 3D print a mini frame. I designed my frame in Fusion360. Below is a picture of my frame in fusion and of it after prinint it. Here is the file

Adding Compnents

Then after the 3D print was done, I needed to add the components in. So every side that was a square was for a IR receiver and everyside with a circle was for a IR transmitter. Together this created a grid.

This is an image of all of the components in the spots and as you can see they fit perfectly.

Wiring the Components

The next step was wiring the compnents. This was a frustrating moment for me because I had two big realizations. The first was the pin out for the IR Receivers. Below is the image of my wiring of the whole thing before.

In that photo I wired the IR Receivers based on the pinout that amazon gave mw which is below.

As you can see it shows that its output pin, GND pin and then VCC pin. When I sent my code it wasnt working. I spent 2 days on it and I couldn’t figure out why it wasn’t working. And I tested everything to the point that I took apart the the whole thing to and just try 1 receiver and transmitter. It was still not working. Finally I came to the conclusion that it was the IR receivers. I almost bought new ones but then I looked up the pinout online. I did this because I had these other receivers that looked exaclty like it and I remember it was different wiring. Then I was running out of ideas and I found the pinout below and decieded to give it a try.

I just realized that it was the same thing and I just read the pinout wrong.

Once I tried the new pinout it worked perfectly. Below is the video of it working.

Below is the code for that that can also be downloaded here

#include <WiFi.h>
#include <WebServer.h>

// ====== YOUR WIFI INFO ======
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

// ====== IR SENSOR PIN ======
const int irSensorPin = D2;   // Use a digital pin like D2 (GPIO2)

// ====== WEB SERVER ======
WebServer server(80);

// ====== HTML PAGE ======
String htmlPage = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>IR Beam Sensor (Digital)</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      margin-top: 50px;
      background-color: #f2f2f2;
    }
    #box {
      width: 150px;
      height: 150px;
      margin: 20px auto;
      background-color: grey;
      border-radius: 12px;
      transition: background-color 0.3s;
      box-shadow: 0 0 10px rgba(0,0,0,0.2);
    }
  </style>
</head>
<body>
  <h2>IR Beam Sensor (Digital)</h2>
  <div id="box"></div>
  <p>Status: <span id="statusText">Reading...</span></p>

  <script>
    async function updateBox() {
      try {
        const res = await fetch("/status");
        const data = await res.text();
        const box = document.getElementById("box");
        const text = document.getElementById("statusText");
        if (data === "BLOCKED") {
          box.style.backgroundColor = "green";
          text.innerText = "Beam Blocked";
        } else {
          box.style.backgroundColor = "grey";
          text.innerText = "Clear";
        }
      } catch (e) {
        console.error(e);
      }
    }
    setInterval(updateBox, 300);
    updateBox();
  </script>
</body>
</html>
)rawliteral";

// ====== SERVER HANDLERS ======
void handleRoot() {
  server.send(200, "text/html", htmlPage);
}

void handleStatus() {
  int sensorValue = digitalRead(irSensorPin);
  // Adjust logic depending on your sensor: some output LOW when blocked, some HIGH
  String state = (sensorValue == LOW) ? "BLOCKED" : "CLEAR";
  server.send(200, "text/plain", state);
}

// ====== SETUP ======
void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println();
  Serial.println("Starting IR Beam Sensor (Digital)...");

  pinMode(irSensorPin, INPUT);

  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("\nWiFi Connected!");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  // Start web server
  server.on("/", handleRoot);
  server.on("/status", handleStatus);
  server.begin();
  Serial.println("Web server started.");
}

// ====== LOOP ======
void loop() {
  server.handleClient();
}

Then after that worked then I added another column. I decided to add one column at a time because Mr. Dubick always told us to start simple.

This is a video of adding a second row. As you can see in the end of the video that when I covered both then both got triggered.Below is the cold for just 2 and here is the download for it.

#include <WiFi.h>
#include <WebServer.h>

// ====== YOUR WIFI INFO ======
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

// ====== IR SENSOR PINS ======
const int irSensor1Pin = D2;  // Digital pin D2 (GPIO2)
const int irSensor2Pin = D3;  // Digital pin D3 (GPIO3) for second beam

// ====== WEB SERVER ======
WebServer server(80);

// ====== HTML PAGE ======
String htmlPage = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>IR Beam Sensors (Digital)</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      margin-top: 50px;
      background-color: #f2f2f2;
    }
    .box {
      width: 150px;
      height: 150px;
      margin: 20px auto;
      background-color: grey;
      border-radius: 12px;
      transition: background-color 0.3s;
      box-shadow: 0 0 10px rgba(0,0,0,0.2);
    }
  </style>
</head>
<body>
  <h2>IR Beam Sensors</h2>
  <p>Sensor 1:</p>
  <div id="box1" class="box"></div>
  <p>Sensor 2:</p>
  <div id="box2" class="box"></div>
  <p>Status 1: <span id="statusText1">Reading...</span></p>
  <p>Status 2: <span id="statusText2">Reading...</span></p>

  <script>
    async function updateBoxes() {
      try {
        const res = await fetch("/status");
        const data = await res.json();  // now we return JSON
        // Sensor 1
        const box1 = document.getElementById("box1");
        const text1 = document.getElementById("statusText1");
        if (data.sensor1 === "BLOCKED") {
          box1.style.backgroundColor = "green";
          text1.innerText = "Beam Blocked";
        } else {
          box1.style.backgroundColor = "grey";
          text1.innerText = "Clear";
        }
        // Sensor 2
        const box2 = document.getElementById("box2");
        const text2 = document.getElementById("statusText2");
        if (data.sensor2 === "BLOCKED") {
          box2.style.backgroundColor = "green";
          text2.innerText = "Beam Blocked";
        } else {
          box2.style.backgroundColor = "grey";
          text2.innerText = "Clear";
        }
      } catch (e) {
        console.error(e);
      }
    }
    setInterval(updateBoxes, 300);
    updateBoxes();
  </script>
</body>
</html>
)rawliteral";

// ====== SERVER HANDLERS ======
void handleRoot() {
  server.send(200, "text/html", htmlPage);
}

void handleStatus() {
  int val1 = digitalRead(irSensor1Pin);
  int val2 = digitalRead(irSensor2Pin);

  // Adjust logic depending on your sensor: LOW = blocked
  String state1 = (val1 == LOW) ? "BLOCKED" : "CLEAR";
  String state2 = (val2 == LOW) ? "BLOCKED" : "CLEAR";

  // Send as JSON
  String json = "{";
  json += "\"sensor1\":\"" + state1 + "\",";
  json += "\"sensor2\":\"" + state2 + "\"";
  json += "}";
  server.send(200, "application/json", json);
}

// ====== SETUP ======
void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println();
  Serial.println("Starting 2 IR Beam Sensors (Digital)...");

  pinMode(irSensor1Pin, INPUT);
  pinMode(irSensor2Pin, INPUT);

  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("\nWiFi Connected!");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  // Start web server
  server.on("/", handleRoot);
  server.on("/status", handleStatus);
  server.begin();
  Serial.println("Web server started.");
}

// ====== LOOP ======
void loop() {
  server.handleClient();
}

Finally this is it with all 3 and it is deteching the finger alright. When I add this to my finaly project then I will fix this but overeall it worked very well. And once again below is the code for it and here is the download.

#include <WiFi.h>
#include <WebServer.h>

// ====== WIFI INFO ======
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

// ====== IR SENSOR PINS ======
const int irSensor1Pin = D2;  // Digital pin D2
const int irSensor2Pin = D3;  // Digital pin D3
const int irSensor3Pin = D4;  // Digital pin D4

// ====== WEB SERVER ======
WebServer server(80);

// ====== HTML PAGE ======
String htmlPage = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>IR Beam Sensors (3 Columns)</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      background-color: #f2f2f2;
      margin: 30px;
    }
    .container {
      display: flex;
      justify-content: center;
      gap: 30px;
    }
    .column {
      display: flex;
      flex-direction: column;
      gap: 15px;
      align-items: center;
    }
    .box {
      width: 120px;
      height: 120px;
      background-color: grey;
      border-radius: 12px;
      transition: background-color 0.3s;
      box-shadow: 0 0 10px rgba(0,0,0,0.2);
    }
  </style>
</head>
<body>
  <h2>IR Beam Sensors</h2>
  <div class="container">
    <div class="column">
      <p>Sensor 1</p>
      <div id="box1" class="box"></div>
      <p>Status 1: <span id="statusText1">Reading...</span></p>
    </div>
    <div class="column">
      <p>Sensor 2</p>
      <div id="box2" class="box"></div>
      <p>Status 2: <span id="statusText2">Reading...</span></p>
    </div>
    <div class="column">
      <p>Sensor 3</p>
      <div id="box3" class="box"></div>
      <p>Status 3: <span id="statusText3">Reading...</span></p>
    </div>
  </div>

  <script>
    async function updateBoxes() {
      try {
        const res = await fetch("/status");
        const data = await res.json();

        // Sensor 1
        const box1 = document.getElementById("box1");
        const text1 = document.getElementById("statusText1");
        box1.style.backgroundColor = (data.sensor1 === "BLOCKED") ? "green" : "grey";
        text1.innerText = (data.sensor1 === "BLOCKED") ? "Beam Blocked" : "Clear";

        // Sensor 2
        const box2 = document.getElementById("box2");
        const text2 = document.getElementById("statusText2");
        box2.style.backgroundColor = (data.sensor2 === "BLOCKED") ? "green" : "grey";
        text2.innerText = (data.sensor2 === "BLOCKED") ? "Beam Blocked" : "Clear";

        // Sensor 3
        const box3 = document.getElementById("box3");
        const text3 = document.getElementById("statusText3");
        box3.style.backgroundColor = (data.sensor3 === "BLOCKED") ? "green" : "grey";
        text3.innerText = (data.sensor3 === "BLOCKED") ? "Beam Blocked" : "Clear";

      } catch (e) {
        console.error(e);
      }
    }
    setInterval(updateBoxes, 300);
    updateBoxes();
  </script>
</body>
</html>
)rawliteral";

// ====== SERVER HANDLERS ======
void handleRoot() {
  server.send(200, "text/html", htmlPage);
}

void handleStatus() {
  int val1 = digitalRead(irSensor1Pin);
  int val2 = digitalRead(irSensor2Pin);
  int val3 = digitalRead(irSensor3Pin);

  String state1 = (val1 == LOW) ? "BLOCKED" : "CLEAR";
  String state2 = (val2 == LOW) ? "BLOCKED" : "CLEAR";
  String state3 = (val3 == LOW) ? "BLOCKED" : "CLEAR";

  String json = "{";
  json += "\"sensor1\":\"" + state1 + "\",";
  json += "\"sensor2\":\"" + state2 + "\",";
  json += "\"sensor3\":\"" + state3 + "\"";
  json += "}";

  server.send(200, "application/json", json);
}

// ====== SETUP ======
void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("\nStarting 3 IR Beam Sensors...");

  pinMode(irSensor1Pin, INPUT);
  pinMode(irSensor2Pin, INPUT);
  pinMode(irSensor3Pin, INPUT);

  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("\nWiFi Connected!");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/status", handleStatus);
  server.begin();
  Serial.println("Web server started.");
}

// ====== LOOP ======
void loop() {
  server.handleClient();
}

UI Aspect

The interface is created entirely in HTML, CSS, and JavaScript, and is hosted by the ESP32 itself. When a user connects to the ESP32’s IP address over Wi-Fi, they see a page with three sensor panels, each showing live status information.

Each sensor is represented by a colored box and a text label. - The color changes dynamically based on the beam state:

- Grey – Beam clear (no obstruction)

- Green – Beam blocked (object detected)

The display refreshes automatically every 300 ms using JavaScript’s fetch() to request the latest readings from the ESP32.

Layout

The webpage displays three columns — one for each IR sensor — to mirror the physical arrangement of the sensors on the board.

Each column contains: - The sensor name (Sensor 1, Sensor 2, Sensor 3) - A color-changing indicator box - A text of the status showing “Beam Blocked” or “Clear” in real time

This simple layout provides an immediate visual indication of the sensor states without requiring any serial monitor or external software.

Reflection

Group

Through the group peoject I learned the differences between Python(Tkinter) and JavaScript + HTML Web App. I also learned when to use each one. If I were to use one on my final project I would choose to use the Python(Tkninter) because for my final project I need to use an app to control where the finger is.

Individual

This week was challenging because it was a big step on how to make my finally project touch. For a while I was stuck on how todo this but now that this week is finally compleate, I feel like I know what I need todo for my final project..


Last update: November 15, 2025