Infinity Mirror

by Kristofer Berrett

With the TESPuino-S3 or the TESPuino-32 and a few simple materials, you can build your own Infinity Mirror, with customizable lighting features to create an amazing optical illusion.

List of Materials

The following materials were used in this project. I’ve also included links to the specific products I used.

  • TESPuino-32

  • Shadow Box

  • Reflective Window Tint

  • Mirror

  • NeoPixel Strip

  • Power Supply

Instructions:

How to Build the Infinity Mirror

The first task with this project was to build the box itself. For this, I used a off the shelf shadow box from hobby lobby. There are a lot of options for a shadow box. I went with one that was deeper as opposed to a shallow one so that the effect of the infinity mirror was enhanced. The more space that light has to travel, the deeper the box will appear.

Construction of the box was fairly simple. I first removed the front pane of glass from the shadow box and, following the instructions provided in the window tint packaging, added window tint to the inside of the glass. This allows the light that comes from the inside of the box to bounce and reflect between the mirror and the pane of glass.

Next I had to cut the LEDs down to size. My initial plan was to have four light strip layers. I started by cutting four strips, with each one being long enough to run around the entire box. However, after attempting to install the light strip, it was clear that making the corners tight would be an issue. Instead, I cut one foot sections of the light strip and installed them so that they were tight into the corners. The back of the light strip is adhesive, but to make sure that they didn’t move, I added hot glue to tack them down.

After that it was a simple job of soldering the one-foot sections together in the corners. Well simple for Levi Hancock. I got him to help me with this part.

Once all the lights were in place, I had to drill a hole in the side of the box to the outside to run power and data from the TESPuino and power supply.

Soldering the end of one strip to the beginning of another.

Linking the signals at the corner.

A hole cut to allow data, power and ground signals to the LED strip.

Coding

I performed the coding in the arduino IDE. To program the TESPuino with the arduino IDE we use the Espressif pre-loaded boards, it works great. For this project I set up the IDE by using the “ESP32S3 Dev Module” board in the Arduino IDE as I was using the TESPuino-32 module. Once you’ve loaded this board, there are a host of examples that you can use to start a project. I used the “Simple WiFi Server” as a starting point, and then added the control for the neopixel. The code is pretty rudimentary, but with a little more effort, it would be pretty simple to add animations or change effects. Feel free to take a look below or copy and use.

  • /*

    WiFi Web Server Infinity Mirror Control

    created for arduino 25 Nov 2012

    by Tom Igoe

    ported for sparkfun esp32

    31.01.2017 by Jan Hendrik Berlin

    adapted to ESPuino

    12.01.2023 by Kris Berrett

    */

    #include <WiFi.h>

    #include <Adafruit_NeoPixel.h>

    #define NUMPIXELS 272

    #define PIN 12

    #define SHORTDELAY 5 // Time in milliseconds

    #define MEDDELAY 125 // Time in milliseconds

    #define LONGDELAY 500 // Time in Milliseconds

    Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

    const char* ssid = "T2-Eng";

    const char* password = "t2testing";

    WiFiServer server(80);

    void setup()

    {

    Serial.begin(115200);

    pinMode(12, OUTPUT); // set the LED pin mode

    delay(10);

    // We start by connecting to a WiFi network

    Serial.println();

    Serial.println();

    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();

    }

    int value = 0;

    void loop() {

    WiFiClient client = server.available(); // listen for incoming clients

    if (client) { // if you get a client,

    Serial.println("New Client."); // print a message out the serial port

    String currentLine = ""; // make a String to hold incoming data from the client

    while (client.connected()) { // loop while the client's connected

    if (client.available()) { // if there's bytes to read from the client,

    char c = client.read(); // read a byte, then

    Serial.write(c); // print it out the serial monitor

    if (c == '\n') { // if the byte is a newline character

    // if the current line is blank, you got two newline characters in a row.

    // that's the end of the client HTTP request, so send a response:

    if (currentLine.length() == 0) {

    // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)

    // and a content-type so the client knows what's coming, then a blank line:

    client.println("HTTP/1.1 200 OK");

    client.println("Content-type:text/html");

    client.println();

    // the content of the HTTP response follows the header:

    client.print("Click <a href=\"/Purple\">here</a> to turn the neopixels purple.<br>");

    client.print("Click <a href=\"/BlueYellow\">here</a> for a blue and yellow animation.<br>");

    client.print("Click <a href=\"/Red\">here</a> to turn the neopixel Red.<br>");

    client.print("Click <a href=\"/AtOnce\">here</a> to turn the pixels on all at once.<br>");

    client.print("Click <a href=\"/Vertical\">here</a> for vertical animation<br>");

    client.print("Click <a href=\"/Off\">here</a> to turn the neopixels off.<br>");

    // The HTTP response ends with another blank line:

    client.println();

    // break out of the while loop:

    break;

    } else { // if you got a newline, then clear currentLine:

    currentLine = "";

    }

    } else if (c != '\r') { // if you got anything else but a carriage return character,

    currentLine += c; // add it to the end of the currentLine

    }

    // Check to see the client request

    if (currentLine.endsWith("GET /Purple")) {

    for (int i = 0; i < NUMPIXELS; i++) {

    pixels.setPixelColor(i, pixels.Color (64, 0, 45));

    pixels.show();

    delay(SHORTDELAY);

    }

    }

    if (currentLine.endsWith("GET /BlueYellow")) {

    for (int j = 0; j < 100; j++) {

    for (int i = 0; i < NUMPIXELS; i++) {

    if (i % 2 == 0) {

    pixels.setPixelColor(i, pixels.Color(245, 133, 2));

    }

    else {

    pixels.setPixelColor(i, pixels.Color(2, 55, 227));

    }

    }

    pixels.show();

    delay(MEDDELAY);

    for (int i = 0; i < NUMPIXELS; i++) {

    if (i % 2 != 0) {

    pixels.setPixelColor(i, pixels.Color(245, 133, 2));

    }

    else {

    pixels.setPixelColor(i, pixels.Color(2, 55, 227));

    }

    }

    pixels.show();

    delay(MEDDELAY);

    }

    }

    if (currentLine.endsWith("GET /Red")) {

    for (int i = 0; i < NUMPIXELS; i++) {

    pixels.setPixelColor(i, pixels.Color (64, 0, 0));

    pixels.show();

    delay(SHORTDELAY);

    }

    }

    if (currentLine.endsWith("GET /AtOnce")) {

    for (int i = 0; i < NUMPIXELS; i++) {

    pixels.setPixelColor(i, pixels.Color (0, 64, 0));

    }

    pixels.show(); // Send the updated pixel colors to the hardware.

    }

    if (currentLine.endsWith("GET /Vertical")) {

    int value = random(205, 272);

    for (int i = 272; i >= 205; i--) {

    pixels.setPixelColor(i, pixels.Color (2, 156, 227)); // Blue

    }

    pixels.show(); // Send the updated pixel colors to the hardware.

    delay(LONGDELAY);

    for (int i = 272; i >= 137; i--) {

    if (i >= 205) {

    pixels.setPixelColor(i, pixels.Color (227, 171, 2)); // Orange

    }

    else {

    pixels.setPixelColor(i, pixels.Color (2, 156, 227)); // Blue

    }

    }

    pixels.show();

    delay(LONGDELAY);

    for (int i = 272; i >= 69; i--) {

    if ((i >= 205) || (i <= 136)) {

    pixels.setPixelColor(i, pixels.Color (2, 156, 227)); // Blue

    }

    else {

    pixels.setPixelColor(i, pixels.Color (227, 171, 2)); // Orange

    }

    }

    pixels.show();

    delay(LONGDELAY);

    for (int i = 272; i >= 0; i--) {

    if ((i >= 205) || ((i >= 69) && (i <= 136))) {

    pixels.setPixelColor(i, pixels.Color (227, 171, 2)); // Orange

    }

    else {

    pixels.setPixelColor(i, pixels.Color (2, 156, 227)); // Blue

    }

    }

    pixels.show();

    delay(LONGDELAY);

    for (int i = 272; i >= 0; i--) {

    pixels.setPixelColor(i, pixels.Color (0, 0, 0));

    }

    pixels.show();

    }

    if (currentLine.endsWith("GET /Off")) {

    for (int i = 0; i < NUMPIXELS; i++) {

    pixels.setPixelColor(i, pixels.Color (0, 0, 0));

    }

    pixels.show(); // Send the updated pixel colors to the hardware

    }

    }

    }

    // close the connection:

    client.stop();

    Serial.println("Client Disconnected.");

    }

    }

Testing

Once I loaded software on the Tespuino, it was pretty simple to make it work. I opened the serial monitor and got the IP address of the Tespuino.  I made sure I was on the same network as the Tespuino, and then went to the address given and found the webpage that I had set in the arduino sketch. When I pressed the button on the screen, I’d get the appropriate response from the Infinity Mirror. Check out the video for more details.

Conclusions

All in all, this was a pretty simple build, and the effect of the infinity mirror is pretty cool to see on your desk. This is a great build that will introduce you to programing the TESPuino with the Arduino IDE.

Next
Next

Game Controller