Skip to content

Week 14 Documentation for Fab Lab

Project Overview

The goal of this project is to control an onboard LED on an ESP32-S3 via a web interface. The project began by attempting to use Blynk to control the LED, but we later moved on to creating a web server using the ESP32 and custom HTML/CSS for the interface.


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 we 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

Working LED local host 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: We observed that the webpage served by the ESP32 was functional, but the design was very basic. We 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 we were 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, we decided to work around this without using the LED control library.

Workaround Code

We 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(){}
PWM futuristic slider Click for video

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

  // Choose the correct port manually if needed
  // Example: myPort = new Serial(this, Serial.list()[1], 115200);
  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