Skip to content

Week 14 Interface and Application Programming

Project Overview

The goal of this project was to get some sort of an output and/or interaction using the ESP32-S3 via a networking interface. The project began by attempting to use Blynk to control the LED, but I later moved on to creating a web server using the ESP32 and custom HTML/CSS for the interface.


Group Assignment

You can find the group assignment on my fabmate Samruddhi's page (You have to scroll to the bottom of the page)

I concur with the findings that Java is great for graphics, sound, and video projects. JavaScript Runs is browser friendly, good for animations and supports interactivity.

Blynk

Image Blynk Interface

Image Adding a new device

Image Setting up the dashboard

Image Setting up Datastream

Image Virtual pin setup

Step 1: Initial Attempt Using Blynk (ESP32-S3)

I started by trying to control the ESP32's onboard LED using the Blynk app on the phone.

Code 1: ESP32 + Blynk (Initial Attempt)

#include <WiFi.h>
#include <BlynkSimpleEsp32.h>

char ssid[] = "your-SSID";
char pass[] = "your-PASSWORD";
char auth[] = "your-BLYNK-AUTH-TOKEN";

void setup()
{
  Serial.begin(115200);
  Blynk.begin(auth, ssid, pass);
}

void loop()
{
  Blynk.run();
}

Issues Encountered

  • The Blynk library (BlynkSimpleEsp32.h) was not found in the Arduino IDE, and this led to the first error during compilation: "BlynkSimpleEsp32.h not found".

Image

  • There was also an issue with the BLYNK_AUTH_TOKEN not being declared in the scope, leading to an error in setting up the Blynk connection.

  • I later found out that the ESP32 favoured an android based hotspot in contrast to an IOS based hotspot, I took it from a friend and had limited access to it, so I could only establish a connection, but not control the working of an LED. I could, however, see the board being online on both Blynk website and phone app.

Moving On to Web Server with ESP32

Since I couldn't proceed with Blynk, I moved on to using the ESP32 as a web server to control the onboard LED.

Code 2: Simple Web Server for LED Control

#include <WiFi.h>
#include <ESPAsyncWebServer.h>

const char* ssid = "your-SSID";
const char* password = "your-PASSWORD";

AsyncWebServer server(80);

void setup(){
  Serial.begin(115200);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi!");

  // Serve HTML page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String html = "<html><body><h1>ESP32 LED Control</h1><button onclick=\"toggleLED()\">Toggle LED</button><script>";
    html += "function toggleLED(){fetch('/toggle');}</script></body></html>";
    request->send(200, "text/html", html);
  });

  // Handle LED control
  server.on("/toggle", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    request->send(200, "text/plain", "LED Toggled");
  });

  // Start server
  server.begin();
}

void loop(){}
Image Image

Click for video

Errors Encountered

  • Device Offline: Initially, after uploading the code, the ESP32 was showing as offline in the Serial Monitor. This was resolved by checking the Wi-Fi credentials and ensuring they were correct.

  • Webpage not responsive: I observed that the webpage served by the ESP32 was functional, but the design was very basic. I wanted to make it more futuristic and aesthetically pleasing.

Step 3: Making the Webpage Futuristic with a PWM Slider for LED Brightness

I decided to add a PWM slider on the webpage to control the brightness of the onboard LED.

Code 3: Futuristic Webpage with PWM Slider

#include <WiFi.h>
#include <ESPAsyncWebServer.h>

const char* ssid = "your-SSID";
const char* password = "your-PASSWORD";
const int LED_PIN = LED_BUILTIN;

AsyncWebServer server(80);

void setup(){
  Serial.begin(115200);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi!");

  pinMode(LED_PIN, OUTPUT);

  // Serve HTML page with PWM slider
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String html = "<html><head><style>body { font-family: Arial, sans-serif; background-color: #1c1c1c; color: white; text-align: center; }</style></head><body>";
    html += "<h1>ESP32 LED Control</h1><input type='range' min='0' max='255' value='128' id='pwmSlider' onchange='updatePWM(this.value)'><br>";
    html += "<span id='pwmValue'>128</span><script>";
    html += "function updatePWM(val){fetch('/setPWM?value='+val); document.getElementById('pwmValue').innerText = val;}</script>";
    html += "</body></html>";
    request->send(200, "text/html", html);
  });

  // Handle PWM value change
  server.on("/setPWM", HTTP_GET, [](AsyncWebServerRequest *request){
    String pwmValue = request->getParam("value")->value();
    int pwm = pwmValue.toInt();
    analogWrite(LED_PIN, pwm);  // Set the LED brightness using PWM
    request->send(200, "text/plain", "PWM set to " + pwmValue);
  });

  // Start server
  server.begin();
}

void loop(){}

Issues Encountered

Slider not controlling brightness: Initially, the slider would not control the brightness of the LED as expected. This was fixed by ensuring that I was using analogWrite() correctly to set the PWM value for the LED.

Step 4: Finding a Workaround for PWM Control Without Using ledcAttachPin

After facing issues with the ledcAttachPin() function, I decided to work around this without using the LED control library.

Workaround Code

I used analogWrite() for controlling the brightness instead of using the ledcAttachPin() function.

#include <WiFi.h>
#include <ESPAsyncWebServer.h>

const char* ssid = "your-SSID";
const char* password = "your-PASSWORD";
const int LED_PIN = LED_BUILTIN;

AsyncWebServer server(80);

void setup(){
  Serial.begin(115200);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi!");

  pinMode(LED_PIN, OUTPUT);

  // Serve HTML page with PWM slider
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String html = "<html><head><style>body { font-family: Arial, sans-serif; background-color: #1c1c1c; color: white; text-align: center; }</style></head><body>";
    html += "<h1>ESP32 LED Control</h1><input type='range' min='0' max='255' value='128' id='pwmSlider' onchange='updatePWM(this.value)'><br>";
    html += "<span id='pwmValue'>128</span><script>";
    html += "function updatePWM(val){fetch('/setPWM?value='+val); document.getElementById('pwmValue').innerText = val;}</script>";
    html += "</body></html>";
    request->send(200, "text/html", html);
  });

  // Handle PWM value change
  server.on("/setPWM", HTTP_GET, [](AsyncWebServerRequest *request){
    String pwmValue = request->getParam("value")->value();
    int pwm = pwmValue.toInt();
    analogWrite(LED_PIN, pwm);  // Set the LED brightness using PWM
    request->send(200, "text/plain", "PWM set to " + pwmValue);
  });

  // Start server
  server.begin();
}

void loop(){}

Issues and Fixes

  • Slider Not Working: I fixed issues with the slider by ensuring the proper use of analogWrite() and setting up the HTML for dynamic updates.

  • Webpage Aesthetics: I used custom CSS to improve the look and feel of the webpage, making it more futuristic and aesthetic.

Processing

I tried making a simple game on processing, that indicates its progress through the controller, via some output device.
The basic principle of the game was to avoid obstacles in the game, and if you hit them the LED connected to the board will light up.
I could not make Processing and Arduino IDE communicate even when using a serial communication.

I also switched boards from the ESP32 to RP2040 to check if that was an issue but still no luck.

Arduino code

Game indication
#define LED_PIN D2  // Built-in LED on Raspberry Pi Pico

void setup() {
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  if (Serial.available()) {
    String command = Serial.readStringUntil('\n');
    command.trim();

    if (command == "HIT") {
      digitalWrite(LED_PIN, HIGH);
      delay(200);  // Brief flash
      digitalWrite(LED_PIN, LOW);
    }
  }
}

Processing code

Game interface
import processing.serial.*;

Serial myPort;
float x = 100;
float y = 200;
float r = 50;
float enemyX = 600;
float enemyY = 200;
boolean hit = false;

void setup() {
  size(800, 400);
  background(0);
  println(Serial.list());  // Print available ports

  // Use the correct port (choose manually or pick from list)
  myPort = new Serial(this, "/dev/cu.usbmodem2101", 115200);
  // Alternatively: myPort = new Serial(this, Serial.list()[0], 115200);

  myPort.clear();  // Clear old serial buffer
  delay(1000);     // Let port settle
}

void draw() {
  background(20);

  // Draw player
  fill(0, 255, 0);
  ellipse(x, y, r, r);

  // Draw enemy
  fill(hit ? color(255, 0, 0) : color(255));
  ellipse(enemyX, enemyY, r, r);

  // Check collision
  if (dist(x, y, enemyX, enemyY) < r) {
    if (!hit) {
      println("Sending HIT to RP2040 🚀");
      myPort.write("HIT\n");
      hit = true;
    }
  } else {
    hit = false;
  }

  // Move enemy
  enemyX -= 3;
  if (enemyX < -r) {
    enemyX = width + r;
    enemyY = random(100, height - 100);
  }
}

void keyPressed() {
  if (key == 'w') y -= 10;
  if (key == 's') y += 10;
  if (key == 'a') x -= 10;
  if (key == 'd') x += 10;
}

Image Processing interface

Image Image Gameplay

Learnings

It is a must to check if you have selected the correct port in your processing code, or else there won't be any communication in the game and the microcontroller. You also need to turn off the serial monitor in Arduino IDE or else you will get an error saying 'Port busy'.


Gameplay video, used a buzzer instead of an LED for better perception.