Last week, I worked on implementing Bluetooth with a local input. Building on that experience, this week I aimed to create a local output over Wi-Fi. Initially, I attempted to use a Pi Pico W because I enjoy coding in Python. However, after about an hour and a half, I encountered issues with the Thonny IDE on my computer, which seemed to have been damaged. As a result, I switched to the ESP32-C3 Xiao, especially since I'm already using the ESP32-CAM for my final project. Sticking with C++ seemed more efficient under the circumstances.
My first task was to connect the Xiao to the internet. The Wi-Fi at the Harvard Reef posed a challenge because it requires more than just a basic Wi-Fi connection to access Harvard Secure. I eventually utilized the maker space in the SEC, which has its own Wi-Fi network. After connecting the Xiao to my computer, I worked on establishing a connection to the Wi-Fi to run a server. Despite some initial difficulties, including repeatedly pressing the B and R buttons, I managed to establish a successful connection. My first functional output was a simple blinking light.
Initially, my interface featured a straightforward on/off switch.
Despite the voltage mismatch between the 3.3V and 5V, the coding library instructed me to proceed, and it worked without issues. With Claude's help, I enhanced the UI to make interactions more visually appealing. Additionally, I mounted the LED strip onto a 3D-printed owl's head after our club meeting at Harvard, which gave the project a unique and aesthetic touch.
The tool that I used is an HTML/CSS web server that the esp32 was producing. The web server updates the microcontrollers state by listening for get requests and then processing them. Other options could include things like flask if micropython is used to establish a database. Things like firebase are also possible to build a web app and use as a backedn. It is also possible to conigure the code to pull things from cloud storage buckets like AWS S3, azure blob storage, or google cloud storage by putting in credentials. It was a pretty sick feeling making something that is able to connect to Wi-Fi. I think that this lesson unlocked a huge skillset going forward.
The final setup involved attaching the LED strip to the owl’s head, creating an engaging and interactive display. Below is the code I used for the project:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bluetooth to Wi-Fi Project Reflection</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<div class="container">
<h1 class="text-center">Bluetooth to Wi-Fi Project</h1>
<div class="card">
<h3>Project Overview:</h3>
<p>Last week, I worked on implementing Bluetooth with a local input. Building on that experience, this week I aimed to create a local output over Wi-Fi. Initially, I attempted to use a Pi Pico W because I enjoy coding in Python. However, after about an hour and a half, I encountered issues with the Thonny IDE on my computer, which seemed to have been damaged. As a result, I switched to the ESP32-C3 Xiao, especially since I'm already using the ESP32-CAM for my final project. Sticking with C++ seemed more efficient under the circumstances.</p>
</div>
<div class="card">
<h3>Connecting to Wi-Fi:</h3>
<p>My first task was to connect the Xiao to the internet. The Wi-Fi at the Harvard Reef posed a challenge because it requires more than just a basic Wi-Fi connection to access Harvard Secure. I eventually utilized the maker space in the SEC, which has its own Wi-Fi network. After connecting the Xiao to my computer, I worked on establishing a connection to the Wi-Fi to run a server. Despite some initial difficulties, including repeatedly pressing the B and R buttons, I managed to establish a successful connection. My first functional output was a simple blinking light.</p>
</div>
<div class="card">
<h3>Interface Development:</h3>
<p>Initially, my interface featured a straightforward on/off switch. However, I wanted to enhance its functionality, so I decided to incorporate an LED strip. With assistance from Anh, I discovered the Adafruit Neopixel library and successfully tested the LED. The connections I made were as follows:</p>
<ul>
<li><strong>XIAO:</strong> GND to <strong>LED:</strong> GND</li>
<li><strong>XIAO:</strong> 3.3V to <strong>LED:</strong> 5V</li>
<li><strong>XIAO:</strong> GPIO10 to <strong>LED:</strong> Din</li>
</ul>
<p>Despite the voltage mismatch between the 3.3V and 5V, the coding library instructed me to proceed, and it worked without issues. With Claude's help, I enhanced the UI to make interactions more visually appealing. Additionally, I mounted the LED strip onto a 3D-printed owl's head after our club meeting at Harvard, which gave the project a unique and aesthetic touch.</p>
</div>
<div class="card">
<h3>Final Implementation:</h3>
<p>The final setup involved attaching the LED strip to the owl’s head, creating an engaging and interactive display. Below is the code I used for the project:</p>
<pre><code>
// Example C++ code for ESP32-C3 Xiao with Neopixel
#include <WiFi.h>
#include <Adafruit_NeoPixel.h>
// Replace with your network credentials
const char *ssid = "MAKERSPACE";
const char *password = "12345678";
// NeoPixel setup
#define LED_PIN 10 // The pin your LED strip is connected to
#define LED_COUNT 30 // Change this to match your strip's LED count
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Set web server port number to 80
WiFiServer server(80);
// Variable to store the HTTP request
String header;
// Current state variables
String stripState = "off";
int currentMode = 0; // 0=solid, 1=rainbow, 2=breathing
uint32_t currentColor = strip.Color(255, 0, 0); // Start with red
void setup() {
Serial.begin(115200);
delay(1000);
// Initialize NeoPixel strip
strip.begin();
strip.setBrightness(50); // Set initial brightness (0-255)
strip.show(); // Initialize all pixels to 'off'
Serial.println("Initializing...");
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
// Rainbow animation function
void rainbow(int wait) {
for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
for(int i=0; i= 0; brightness--) {
strip.setBrightness(brightness);
for(int i=0; i= 0) {
stripState = "on";
currentMode = 0; // Default to solid color mode
} else if (header.indexOf("GET /led/off") >= 0) {
stripState = "off";
strip.clear();
strip.show();
} else if (header.indexOf("GET /mode/solid") >= 0) {
currentMode = 0;
} else if (header.indexOf("GET /mode/rainbow") >= 0) {
currentMode = 1;
} else if (header.indexOf("GET /mode/breathing") >= 0) {
currentMode = 2;
} else if (header.indexOf("GET /color/red") >= 0) {
currentColor = strip.Color(255, 0, 0);
} else if (header.indexOf("GET /color/green") >= 0) {
currentColor = strip.Color(0, 255, 0);
} else if (header.indexOf("GET /color/blue") >= 0) {
currentColor = strip.Color(0, 0, 255);
} else if (header.indexOf("GET /color/white") >= 0) {
currentColor = strip.Color(255, 255, 255);
}
// Modern Apple-style webpage
client.println("<!DOCTYPE html><html>");
client.println("<head>");
client.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
client.println("<style>");
client.println("* { margin: 0; padding: 0; box-sizing: border-box; -webkit-font-smoothing: antialiased; }");
client.println("body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background: #000; color: #fff; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; }");
client.println(".container { max-width: 600px; width: 90%; text-align: center; padding: 20px; }");
client.println("h1 { font-size: 2.5rem; font-weight: 700; margin-bottom: 2rem; background: linear-gradient(90deg, #fff, #888); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }");
client.println("h2 { font-size: 1.5rem; color: #888; margin: 1.5rem 0; }");
client.println(".status { font-size: 1.2rem; color: #888; margin-bottom: 2rem; padding: 1rem; border-radius: 12px; background: rgba(255,255,255,0.1); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }");
client.println(".button { display: inline-block; padding: 1rem 2rem; font-size: 1rem; text-decoration: none; border-radius: 30px; transition: all 0.3s ease; margin: 0.5rem; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }");
client.println(".button-on { background: linear-gradient(135deg, #06c, #0091ff); color: white; box-shadow: 0 5px 15px rgba(0,102,204,0.4); }");
client.println(".button-off { background: rgba(255,255,255,0.1); color: #fff; border: 1px solid rgba(255,255,255,0.2); }");
client.println(".button:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(0,0,0,0.3); }");
client.println(".button-red { background: linear-gradient(135deg, #ff0000, #cc0000); }");
client.println(".button-green { background: linear-gradient(135deg, #00ff00, #00cc00); }");
client.println(".button-blue { background: linear-gradient(135deg, #0000ff, #0000cc); }");
client.println(".button-white { background: linear-gradient(135deg, #ffffff, #cccccc); color: #000; }");
client.println(".glow { position: absolute; width: 100px; height: 100px; background: #06c; filter: blur(50px); opacity: 0.5; border-radius: 50%; z-index: -1; }");
client.println(".control-section { margin: 2rem 0; padding: 1rem; border-radius: 12px; background: rgba(255,255,255,0.05); }");
client.println("</style>");
client.println("</head>");
client.println("<body>");
client.println("<div class='glow' style='top: 20%; left: 20%;'></div>");
client.println("<div class='glow' style='top: 60%; left: 70%;'></div>");
client.println("<div class='container'>");
client.println("<h1>LED Strip Control</h1>");
// Power controls
client.println("<div class='status'>Status: " + stripState + "</div>");
client.println("<div class='control-section'>");
if (stripState=="off") {
client.println("<a href=\"/led/on\"><button class=\"button button-on\">Turn On</button></a>");
} else {
client.println("<a href=\"/led/off\"><button class=\"button button-off\">Turn Off</button></a>");
}
client.println("</div>");
// Mode controls
client.println("<div class='control-section'>");
client.println("<h2>Mode</h2>");
client.println("<a href=\"/mode/solid\"><button class=\"button " + String(currentMode == 0 ? "button-on" : "button-off") + "\">Solid</button></a>");
client.println("<a href=\"/mode/rainbow\"><button class=\"button " + String(currentMode == 1 ? "button-on" : "button-off") + "\">Rainbow</button></a>");
client.println("<a href=\"/mode/breathing\"><button class=\"button " + String(currentMode == 2 ? "button-on" : "button-off") + "\">Breathing</button></a>");
client.println("</div>");
// Color controls
client.println("<div class='control-section'>");
client.println("<h2>Color</h2>");
client.println("<a href=\"/color/red\"><button class=\"button button-red\">Red</button></a>");
client.println("<a href=\"/color/green\"><button class=\"button button-green\">Green</button></a>");
client.println("<a href=\"/color/blue\"><button class=\"button button-blue\">Blue</button></a>");
client.println("<a href=\"/color/white\"><button class=\"button button-white\">White</button></a>");
client.println("</div>");
client.println("</div>");
client.println("</body></html>");
client.println();
break;
} else {
currentLine = "";
}
} else if (c != '\r') {
currentLine += c;
}
}
}
header = "";
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}
</code></pre>