GlowTie

by Raxathor in Circuits > Wearables

1288 Views, 12 Favorites, 0 Comments

GlowTie

Screenshot 2023-09-05 120434.png
GlowTie
IMG_1710.jpg
IMG_1704.jpg

In the ever-evolving world of fashion and wearable technology, there emerges a brilliant fusion of style and innovation that captures the essence of modern sophistication – the Glowtie. This remarkable accessory is far more than just a simple necktie; it is a glowing testament to the marriage of traditional elegance and cutting-edge technology.

The Glowtie is a dazzling embodiment of the future of fashion, a captivating conversation piece that seamlessly integrates LEDs and advanced materials to create a unique and captivating visual experience. It radiates an unmistakable aura of style and individuality, drawing attention to its wearer in any setting. Whether you're attending a gala event, making a statement at a tech conference, or simply seeking a remarkable addition to your daily attire, the Glowtie is poised to redefine your sartorial choices.

Discover how the Glowtie illuminates not only your ensemble but also your imagination, as it blurs the lines between fashion and innovation in a world where self-expression knows no bounds.

This project and files are inspired by Stephen Hawes.

https://github.com/sphawes/glowtie

https://stephenhawes.com/glowtie/

https://youtu.be/q1nSY2faLgM?si=AOlhQc2qNZf0J7bW

Supplies

IMG_1691.jpg

Let's commence by delving into the components listed in the Bill of Materials

  • 1x ESP12-E / ESP12-F
  • 13x WS2812B LEDs
  • 3x 10K resistors
  • 3x 1K resistors
  • 1x 100-ohm resistor
  • 1x TP4056 IC
  • 1x Green LED (0805)
  • 1x Red LED (0805)
  • 1x DW01 Battery Protection IC
  • 1x FS8205A MOSFET IC
  • 1x AMS117 3.3V
  • 2x 0.1uF capacitors
  • 2x 10uF capacitors
  • 1x 100uF capacitor
  • 1x SMD Slide Switch
  • 1x Micro USB port
  • 1x LiPo Battery 3.7v
  • 1x 3D printed parts

ensemble of some of the tools used

  • Tweezers
  • Soldering Iron
  • Solder Paste with syringe
  • Hot Air Gun
  • FTDI USB to TTL adapter
  • Breadboards
  • Isopropyl Alcohol
  • Brush
  • Holding Hands

Design - Schematic

Schematic4.png
Schematic3.png
Schematic2.png

The project revolves around the ESP12-E/ESP12-F microcontroller as its core. The WS2812B LED, commonly known as a NeoPixel, operates as an individually addressable RGB LED module. Its operation revolves around a single data line, allowing for seamless integration into various lighting applications. Data is serially transmitted to the WS2812B, with each LED in the chain receiving its color and brightness information. Upon receiving the data, the LED interprets the signal and illuminates it in the specified color and intensity. This precise control over each LED's color and brightness enables captivating visual effects, animations, and dynamic lighting displays, making the WS2812B an ideal choice for projects ranging from decorative lighting to digital art installations and wearable tech.

In this setup, the WS2812B LEDs are daisy-chained together, with their VSS and GND pins connected to the respective battery voltage and ground. Additionally, the DIN (Data In) of the first LED is linked to one of the GPIO pins on the ESP 12E, while the DOUT (Data Out) of this LED is connected to the DIN of the subsequent LED, creating a sequential connection for all the LEDs in the chain. The schematic also employs the use of TP4056 circuitry. TP4056 is a highly versatile and widely used single-cell lithium-ion (Li-ion) or lithium-polymer (LiPo) battery charger IC. Its primary function is to efficiently and safely charge rechargeable Li-ion or LiPo batteries from various power sources, such as USB ports or external DC supplies. The TP4056 employs a straightforward charging algorithm with built-in safeguards, making it a popular choice for various portable and battery-powered devices.

The TP4056 operates in two primary charging phases: constant current (CC) and constant voltage (CV). During the CC phase, the charger provides a fixed charging current to the battery, which is determined by the value of an external resistor connected to the IC. This current gradually increases the battery voltage. Once the battery voltage reaches a specific threshold (usually around 4.2 volts for Li-ion batteries), the TP4056 transitions into the CV phase. In this phase, it maintains a constant voltage across the battery terminals, allowing the charging current to decrease gradually. This prevents overcharging, as the charging current tapers off as the battery reaches its full capacity. The TP4056 also incorporates multiple safety features, including overcharge protection, over-discharge protection, and thermal protection, ensuring the battery remains within safe operating conditions during the charging process. Overall, the TP4056 offers a simple yet reliable solution for single-cell Li-ion or LiPo battery charging applications.

Explore the GitHub page for downloading the KiCad Schematic files https://github.com/raxathor/GlowTie.

Design - Board Layout

Board3.png
Board1.png
Board2.png

Creating the board layout for the Glowtie in KiCad is a meticulous and essential step in bringing this captivating wearable technology to life. In the intricate dance of design, every element must harmonize seamlessly, from the placement of LEDs to the routing of delicate traces. The LEDs, strategically positioned for maximum luminosity and visual impact, must be intricately connected to the ESP12-E/ESP12-F microcontroller, ensuring the synchronized flow of data and power. The layout should not only guarantee the structural integrity of the board but also optimize the performance of this illuminating accessory. As the Glowtie embraces the union of elegance and innovation, its board layout in KiCad becomes the canvas on which the art of fashion meets the science of technology, promising to captivate and inspire all who encounter its radiant charm. Refer to the files in the GitHub repo https://github.com/raxathor/GlowTie

The Edge cuts layer is intricately fashioned to mirror the proportions and shape of a bowtie, a nod to the GlowTie's namesake. While a ground pour is incorporated into the PCB, it might not be essential. If you opt to employ it, it's crucial to ensure that there's no copper fill beneath the ESP12F's antenna region, as this could lead to future Wi-Fi connectivity complications. Additionally, the placement of the slide switch and the USB micro port on the bottom of the GlowTie allows for convenient power on/off functionality and charging through the USB micro port. The overall dimensions of the board are 127 mm * 57.15 mm.

Follow these steps to make the board layout in KiCad:

1. Open KiCad: Launch KiCad and open your project. Ensure you've completed the schematic design phase before moving on to the board layout.

2. Open PCBNew: In KiCad, the tool for PCB layout is called PCBNew. You can access it from the KiCad main menu by selecting "PCBNew" or by clicking on its icon if available in your KiCad version.

3. Load Netlist: If you haven't already, you need to load the netlist generated from your schematic design. To do this:

  - In PCBNew, go to "Tools" > "Update PCB from Schematic."

  - Browse and select the netlist file associated with your project.

  - Click "Read Current Netlist."

4. Initial Footprint Assignment: PCBNew will try to automatically associate components from the schematic with footprints in your library. Review these assignments by clicking on the "Annotate" button and ensure all components are correctly matched with their footprints. Make any necessary corrections.

5. Place Components: Now, it's time to place your components on the PCB canvas. Follow these steps:

  - Click on the "Place" button.

  - Begin placing components on the board, following your design's requirements and guidelines.

  - Use the "M" key to rotate components if needed.

  - Pay attention to component spacing, alignment, and orientation.

6. Route Traces: After placing components, you'll need to route traces to connect them. Here's how:

  - Click on the "Route" button.

  - Start routing traces by clicking on a pad and dragging to another pad.

  - Use the "R" key to interactively change layers while routing.

  - Keep track of your design's rules, such as trace width and clearance, as specified in your design constraints.

7. Add Copper Pours (Optional): You can create copper pours for power and ground planes:

  - Click on the "Add Filled Zones" button.

  - Draw polygons to define your copper pours for each layer (e.g., ground and power planes).

  - Set the net for each copper pour to the corresponding power or ground net.

8. Silkscreen and Designators: Add silkscreen labels and component designators to your board to help identify components and their orientation.

9. Final Checks: Before finalizing your board layout:

  - Run a Design Rules Check (DRC) to ensure your design complies with manufacturing rules and constraints.

  - Check for any unconnected nets or unrouted connections.

10. Generate Gerber and Drill Files: Follow the steps mentioned in STEP 3 to generate Gerber and drill files for your PCB.

11. Review and Save: Carefully review your board layout and save your work. Ensure that you've saved both the PCB layout file and the project file.

12. Documentation: Consider creating design documentation or assembly instructions to accompany your PCB design when sending it to a manufacturer.

Once you've completed these steps, your PCB layout in KiCad is ready for fabrication. Make sure to thoroughly check your design before sending it to a PCB manufacturer to avoid potential errors in the physical board.

Generating Gerbers

Gerbers.png

Generating Gerber files in KiCad is a crucial step in preparing your printed circuit board (PCB) design for fabrication. Gerber files are the industry-standard format used by PCB manufacturers to create your physical board. Here's a step-by-step guide on how to generate Gerber files in KiCad:


1. Open Your KiCad Project: Ensure that your KiCad project is open, and your PCB design is ready for production.

2. Open PCBNew: In KiCad, the PCB design tool is called PCBNew. Open it by clicking on the PCBNew icon or selecting it from the KiCad main menu.

3. Check Your Design: Before generating Gerbers, double-check your design for any errors, such as unconnected traces, missing components, or design rule violations.

4. Set Plot Parameters:

  a. In PCBNew, go to "File" > "Plot."

  b. In the "Plot" dialog box, make sure the following settings are configured correctly:

   - Plot format: Gerber (rs274x).

 - Output directory: Choose the directory where you want to save your Gerber files.

   - Plot all layers: Ensure that all layers required for manufacturing are selected.

   - Options: Verify that "Use Protel filename extensions" is unchecked.

  c. Click the "Plot" button.

5. Generate the Gerber Files:

  a. After clicking "Plot," PCBNew will generate the Gerber files in the specified directory.

  b. A series of Gerber files will be created, each representing a different layer of your PCB, such as copper layers, silkscreen, solder mask, etc.

6. View the Gerber Files: You can use a Gerber viewer (e.g., GerbView) to verify the generated Gerber files. Make sure they accurately represent your PCB design.

7. Generate the Drill Files:

  a. To generate the drill files, go to "File" > "Drill File."

  b. In the "Drill File" dialog, verify the settings, including the output directory.

  c. Click "Drill."

8. View the Drill Files: Use a Gerber viewer to confirm that the drill files are correctly generated and contain the required drill holes and markings.

9. Compress Files (Optional): You can create a ZIP archive containing all the Gerber and drill files for easy submission to a PCB manufacturer.

10. Documentation (Optional): It's a good practice to include a readme file in your submission with important information about your design, such as layer names and any special instructions.

Now, you have successfully generated Gerber files and drill files for your KiCad PCB design, making it ready for fabrication at a PCB manufacturing facility of your choice. Always ensure that your files meet the manufacturer's specifications before submitting them for production.


I opted for PCB Power, a manufacturing facility specializing in printed circuit boards located in Gujrat, India, for the fabrication of my boards. Their website, which can be found at https://www.pcbpower.com/, was my chosen platform. I decided to go with a sleek black color for the board and selected a thickness of 1.6mm, a choice that not only adds to its aesthetic appeal but is also highly recommended.

Assembly & Soldering

IMG_1694.jpg
IMG_1692.jpg
IMG_1701.jpg

The precision of SMD assembly ensures that these components are meticulously placed on the PCB.

SMD (Surface Mount Device) assembly using solder paste and a hot air gun is a specialized technique employed in the electronics industry to populate printed circuit boards (PCBs) with surface-mount components. This process offers a higher level of precision and versatility when compared to traditional soldering methods, making it indispensable in modern electronics manufacturing.

Solder Paste Application:

  - The first step in this assembly method involves applying solder paste to the PCB's solder pads. Solder paste is a mixture of tiny solder particles suspended in flux. Automated dispensing machines or stencils are commonly used to accurately deposit the solder paste onto the designated areas. This step is crucial, as it ensures that the right amount of solder is in place for each component.

Component Placement:

  - Once the solder paste is applied, SMD components, such as resistors, capacitors, ICs, and microcontrollers, are meticulously placed on the PCB using automated pick-and-place machines. These machines precisely position each component onto its designated solder pad with micron-level accuracy.

Reflow Soldering with Hot Air Gun:

  - After component placement, the PCB is subjected to a controlled heating process, typically performed with a hot air gun or within a reflow oven. The solder paste reflows, melting and bonding the components to the PCB pads. The hot air gun method is especially versatile for small-scale or prototype assembly, as it offers greater flexibility and control over the soldering process.

After the process is done do not forget to short the terminals with some soldering lead above the Red LED as that is what provides voltage to the WS2812B LEDs.


SMD assembly using solder paste and a hot air gun exemplifies the marriage of precision and efficiency.


Programming

Programming.png
IMG_1697.jpg
programming3.png

Programming the ESP12E module using an FTDI USB to serial adapter and the Arduino IDE is a straightforward process. Here's a step-by-step guide:

You'll Need:

  1. FTDI USB to Serial Adapter
  2. Arduino IDE installed on your computer.
  3. Appropriate USB cables.


1. Connect the Hardware:

  - Connect the FTDI adapter to your computer via a USB cable.

  - Connect the FTDI adapter to the ESP12E module as follows:

   - FTDI 3.3V (or 5V, depending on your ESP12E version) to ESP12E VCC.

   - FTDI GND to ESP12E GND.

   - FTDI TX to ESP12E RX.

   - FTDI RX to ESP12E TX.

   - Optionally, you can connect the DTR/RTS pin of the FTDI adapter to the ESP12E's GPIO0 pin for automatic programming mode, but this is not always necessary.


2. Install the ESP8266 Board in Arduino IDE:

  - Open the Arduino IDE.

  - Go to "File" > "Preferences."

  - In the "Additional Boards Manager URLs" field, enter the following URL:

   ```http://arduino.esp8266.com/stable/package_esp8266com_index.json```

  - Click "OK" to close the Preferences window.

  - Go to "Tools" > "Board" > "Boards Manager."

  - Search for "esp8266" and install the "esp8266" board package.

3. Select the ESP8266 Board:

  - Go to "Tools" > "Board" and select the appropriate ESP8266 board (e.g., "NodeMCU 1.0 (ESP-12E Module)").

4. Set Upload Parameters:

  - In "Tools," set the following parameters:

   - "CPU Frequency": 80 MHz.

   - "Flash Size": Depending on your ESP12E module, select the appropriate flash size (usually 4MB).

   - "Port": Select the COM port that corresponds to your FTDI adapter.

5. Write and Upload Your Sketch:

  - Write or open the given Arduino sketch in the GitHub repo

  - Ensure that your code includes the necessary libraries for the ESP8266.

  - Click the "Upload" button (right arrow icon) in the Arduino IDE.

  - The Arduino IDE will compile your sketch and upload it to the ESP12E module via the FTDI adapter.

6. Monitor Serial Output (Optional):

  - You can open the Serial Monitor in the Arduino IDE (Tools > Serial Monitor) to view the serial output from your ESP12E for debugging purposes.


That's it! Your ESP12E module should now be programmed with your Arduino sketch. Make sure to disconnect GPIO0 from GND (if connected) and reset your ESP12E module to run the uploaded code.

Brief About the Firmware

This firmware controls the behavior of the Glowtie, which displays various lighting patterns and colors. Here's the code and its breakdown.

#include <Adafruit_NeoPixel.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>


#define PIN            12
#define NUMPIXELS      13


#define SN "PizzaBowtie"


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


ADC_MODE(ADC_VCC);


const char *ssid = SN;
const char *password = "pleaseletmein";


ESP8266WebServer server(80);


int const battCacheSize = 30;
int battCachePosition = 0;
int battCache[battCacheSize] = {3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300, 3300};
int summedCache = 99000;
float avgBatt = 3300;
int newBatt = 0;


int delayVal = 8;
int btMode = 0;
int rVal = 0;
int gVal = 0;
int bVal = 0;
int brightness = 150;
int checkBattTimer = 0;
int prevMode = 0;


int ba = 0;
int bb = 0;
int bc = 0;
int bd = 0;
int bi = 0;


int pa = 0;
int pi = 0;


int ii = 0;


unsigned long prevMillis = millis();


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


  pixels.begin();


  //storing 4 eeprom values. the 0th is the saved mode, then the 1-3th are r, g, and b values, respectively. 4 bytes per int with 4 ints is 16 bytes:
  EEPROM.begin(512);


  btMode = EEPROM.read(0);
  rVal = EEPROM.read(4);
  gVal = EEPROM.read(8);
  bVal = EEPROM.read(12);


  //CHECKING BATTERY


  newBatt = ESP.getVcc();
  if (newBatt <= 3100) {
    btMode = 1;
    Serial.println("Battery low, going into low battery mode.");
  }


  //Animation
  for (int j = 0; j < 300; j++) {
    burst(50, 20, 0);
    delay(10);
  }


  //WIFI JAZZ
  Serial.print("Creating Glowtie netowork...");
  if (WiFi.softAP(ssid)) {
    Serial.println("Successfully started access point.");
  }
  else {
    Serial.println("Could not start access point.");
  }


  IPAddress myIP = WiFi.softAPIP();
  Serial.print("IP address: ");
  Serial.println(myIP);


  server.on("/", handleRoot);
  server.begin();


  //END WIFI STUFF


  checkBattTimer = millis();


}


void loop() {
  //yield();
  server.handleClient();
  //Checking battery value


  if (millis() >= checkBattTimer + 5000) {
    newBatt = ESP.getVcc();
    Serial.println(newBatt);
    if (newBatt <= 3100) {
      btMode = 1;
    }
    checkBattTimer = millis();
  }


  //OFF MODE
  if (btMode == 0) {
    if (prevMode != 0) {
      off();
      prevMode = 0;
    }
  }


  //LOW BATTERY MODE
  else if (btMode == 1) {
    if (prevMode != 1) {
      lowBatt();
      prevMode = 1;
    }
  }


  //SOLID COLOR MODE
  else if (btMode == 2) {
    if (prevMode != 2) {
      solid();
      prevMode = 2;
    }
  }


  //INFINITY MODE
  else if (btMode == 3) {
    if (prevMode != 3) {
      prevMillis = millis() - 71;
      prevMode = 3;
      ii = 0;
    }
    if (millis() > prevMillis + 70) {
      prevMillis = millis();
      infin(rVal, gVal, bVal);
    }
  }


  //PULSE MODE
  else if (btMode == 4) {
    if (prevMode != 4) {
      pi = 0;
      prevMode = 4;
    }
    if (millis() > prevMillis + 7) {
      prevMillis = millis();
      pulse(rVal, gVal, bVal);
    }
  }


  //BURST MODE
  else if (btMode == 5) {
    if (prevMode != 5) {
      bi = 0;
      prevMode = 5;
    }
    if (millis() > prevMillis + 10) {
      //Burst mode
      prevMillis = millis();
      burst(rVal, gVal, bVal);
    }
  }
}


void handleRoot() {
  //off();


  if (server.args() > 0) {
    int r = server.arg("red").toInt();
    int g = server.arg("green").toInt();
    int b = server.arg("blue").toInt();
    String btModeS = server.arg("mode");
    btMode = btModeS.toInt();


    int red = r / 3;
    int green = g / 3;
    int blue = b / 3;


    rVal = red;
    gVal = green;
    bVal = blue;


    EEPROM.write(0, btMode);
    EEPROM.write(4, rVal);
    EEPROM.write(8, gVal);
    EEPROM.write(12, bVal);
    EEPROM.commit();
  }


  //String html ="<!DOCTYPE html> <html> <head> <title>Glowtie</title> <style> body { background-color: #000000; font-family: \"Helvetica\"; color: #ffffff; } .slider{ width:90%; } #colorUpdate{ background-color: black; width:200px; height:40px; margin:auto; text-align: center; padding:20px; border-radius: 10px; } </style> <meta name=\"viewport\" content=\"width=device-width, height=device-height, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0\" /> <script> console.log(\"Inline JS running.\"); function updateColor(){ var red = document.forms['settings'].red.value; var green = document.forms['settings'].green.value; var blue = document.forms['settings'].blue.value; var rgb = blue | (green << 8) | (red << 16); var color = \"#\" + (0x1000000 + rgb).toString(16).slice(1); document.getElementById(\"colorUpdate\").style.backgroundColor = color; } </script> </head> <body> <h1 id=\"colorUpdate\">Glowtie</h1> <form action=\"\" name=\"settings\" id=\"settings\" method=\"post\"> <!--Color: <input class=\"jscolor\" value=\"ffffff\" name=\"color\">--> Red:<br /> <input type=\"range\" class=\"slider\" min=\"0\" max=\"255\" value=\"127\" name=\"red\" onchange=\"updateColor()\"><br /> Green<br /> <input type=\"range\" class=\"slider\" min=\"0\" max=\"255\" value=\"127\" name=\"green\" onchange=\"updateColor()\"><br /> Blue<br /> <input type=\"range\" class=\"slider\" min=\"0\" max=\"255\" value=\"127\" name=\"blue\" onchange=\"updateColor()\"><br /> <input type=\"radio\" name=\"mode\" value=\"0\" checked> Off<br /> <input type=\"radio\" name=\"mode\" value=\"2\"> Solid<br /> <input type=\"radio\" name=\"mode\" value=\"3\"> Infinity<br /> <input type=\"radio\" name=\"mode\" value=\"4\"> Breathe<br /> <input type=\"radio\" name=\"mode\" value=\"5\"> Burst<br /><br /> <button onclick=\"document.forms['settings'].submit()\" type=\"button\">Send to Glowtie!</button> </form> </body> </html>";
  String html = "<!DOCTYPE html> <html> <head> <title>Glowtie</title> <style> body { background-color: #000000; font-family: \"Helvetica\"; color: #ffffff; } .slider { -webkit-appearance: none; /* Override default CSS styles */ appearance: none; width: 100%; /* Full-width */ margin:auto; margin-bottom:40px; margin-top:10px; height: 25px; /* Specified height */ background:black; /* Grey background */ outline: none; /* Remove outline */ opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */ -webkit-transition: .2s; /* 0.2 seconds transition on hover */ transition: opacity .2s; border:3px solid white; } .slider::-webkit-slider-thumb { -webkit-appearance: none; /* Override default look */ appearance: none; width: 50px; /* Set a specific slider handle width */ height: 50px; /* Slider handle height */ background: white; /* Green background */ cursor: pointer; /* Cursor on hover */ border-radius: 5px; border: 1px solid black; } #sliderCont { width:70%; margin:auto; } #colorUpdate{ background-color: black; width:200px; height:40px; margin:auto; text-align: center; padding:20px; border-radius: 10px; font-family:'Courier New', Courier, monospace } .container { display: block; position: relative; padding-left: 80px; padding-top:5px; margin-bottom: 30px; cursor: pointer; font-size: 22px; font-family: \"Courier New\"; font-weight: bold; color:white; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } /* Hide the browser's default radio button */ .container input { position: absolute; opacity: 0; cursor: pointer; } /* Create a custom radio button */ .checkmark { position: absolute; top: 0; left: 0; height: 40px; width: 70px; background-color: white; border-radius: 10px; } /* On mouse-over, add a grey background color */ .container:hover input ~ .checkmark { background-color: #ccc; } /* When the radio button is checked, add a blue background */ .container input:checked ~ .checkmark { background-color: gold; } /* Create the indicator (the dot/circle - hidden when not checked) */ .checkmark:after { content: \"\"; position: absolute; display: none; } /* Show the indicator (dot/circle) when checked */ .container input:checked ~ .checkmark:after { display: block; } /* Style the indicator (dot/circle) */ .container .checkmark:after { top: 15px; left: 15px; width: 40px; height: 10px; border-radius: 3px; background: black; } </style> <meta name=\"viewport\" content=\"width=device-width, height=device-height, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0\" /> <script> console.log(\"Inline JS running.\"); function updateColor(){ var red = document.forms['settings'].red.value; var green = document.forms['settings'].green.value; var blue = document.forms['settings'].blue.value; var rgb = blue | (green << 8) | (red << 16); var color = \"#\" + (0x1000000 + rgb).toString(16).slice(1); document.getElementById(\"colorUpdate\").style.backgroundColor = color; } </script> </head> <body> <h1 id=\"colorUpdate\">Glowtie</h1> <form action=\"\" name=\"settings\" id=\"settings\" method=\"post\"> <!--Color: <input class=\"jscolor\" value=\"ffffff\" name=\"color\">--> <div id=\"sliderCont\"> RED<br /> <input type=\"range\" class=\"slider\" min=\"0\" max=\"255\" value=\"127\" name=\"red\" onchange=\"updateColor()\"><br /> GREEN<br /> <input type=\"range\" class=\"slider\" min=\"0\" max=\"255\" value=\"127\" name=\"green\" onchange=\"updateColor()\"><br /> BLUE<br /> <input type=\"range\" class=\"slider\" min=\"0\" max=\"255\" value=\"127\" name=\"blue\" onchange=\"updateColor()\"><br /> </div> <br /> <div style=\"width:200px;margin:auto;\"> <!-- <input class=\"radio\" type=\"radio\" name=\"mode\" value=\"0\" checked> Off<br /> <input class=\"radio\" type=\"radio\" name=\"mode\" value=\"2\"> Solid<br /> <input class=\"radio\" type=\"radio\" name=\"mode\" value=\"3\"> Infinity<br /> <input class=\"radio\" type=\"radio\" name=\"mode\" value=\"4\"> Breathe<br /> <input class=\"radio\" type=\"radio\" name=\"mode\" value=\"5\"> Burst<br /><br />--> <label class=\"container\">Off <input type=\"radio\" checked=\"checked\" name=\"mode\" value=\"0\"> <span class=\"checkmark\"></span> </label> <label class=\"container\">Solid <input type=\"radio\" name=\"mode\" value=\"2\"> <span class=\"checkmark\"></span> </label> <label class=\"container\">Infinity <input type=\"radio\" name=\"mode\" value=\"3\"> <span class=\"checkmark\"></span> </label> <label class=\"container\">Breathe <input type=\"radio\" name=\"mode\" value=\"4\"> <span class=\"checkmark\"></span> </label> <label class=\"container\">Burst <input type=\"radio\" name=\"mode\" value=\"5\"> <span class=\"checkmark\"></span> </label> <button style=\"width:150px;height:50px;background-color:gold;color:black;position:relative;margin-left:25px;margin-top:20px;font-family: 'Courier New', Courier, monospace;font-size:20px;border:0px;border-radius:10px;\" onclick=\"document.forms['settings'].submit()\" type=\"button\">UPDATE</button> </div> </form> </body> </html>";


  server.send(200, "text/html", html);


}


void updateAvgBatt() {
  newBatt = ESP.getVcc();
  summedCache = summedCache - battCache[battCachePosition];
  summedCache = summedCache + newBatt;
  battCache[battCachePosition] = newBatt;


  if (battCachePosition == (battCacheSize - 1)) {
    battCachePosition = 0;
  }
  else {
    battCachePosition++;
  }
  avgBatt = summedCache / battCacheSize;


}


void off() {
  pixels.setPixelColor(0, pixels.Color(0, 0, 0));
  pixels.setPixelColor(1, pixels.Color(0, 0, 0));
  pixels.setPixelColor(2, pixels.Color(0, 0, 0));
  pixels.setPixelColor(3, pixels.Color(0, 0, 0));
  pixels.setPixelColor(4, pixels.Color(0, 0, 0));
  pixels.setPixelColor(5, pixels.Color(0, 0, 0));
  pixels.setPixelColor(6, pixels.Color(0, 0, 0));
  pixels.setPixelColor(7, pixels.Color(0, 0, 0));
  pixels.setPixelColor(8, pixels.Color(0, 0, 0));
  pixels.setPixelColor(9, pixels.Color(0, 0, 0));
  pixels.setPixelColor(10, pixels.Color(0, 0, 0));
  pixels.setPixelColor(11, pixels.Color(0, 0, 0));
  pixels.setPixelColor(12, pixels.Color(0, 0, 0));
  pixels.show();
}


void lowBatt() {
  pixels.setPixelColor(0, pixels.Color(0, 0, 0));
  pixels.setPixelColor(1, pixels.Color(0, 0, 0));
  pixels.setPixelColor(2, pixels.Color(0, 0, 0));
  pixels.setPixelColor(3, pixels.Color(0, 0, 0));
  pixels.setPixelColor(4, pixels.Color(0, 0, 0));
  pixels.setPixelColor(5, pixels.Color(0, 0, 0));
  pixels.setPixelColor(6, pixels.Color(50, 0, 0));
  pixels.setPixelColor(7, pixels.Color(0, 0, 0));
  pixels.setPixelColor(8, pixels.Color(0, 0, 0));
  pixels.setPixelColor(9, pixels.Color(0, 0, 0));
  pixels.setPixelColor(10, pixels.Color(0, 0, 0));
  pixels.setPixelColor(11, pixels.Color(0, 0, 0));
  pixels.setPixelColor(12, pixels.Color(0, 0, 0));


  pixels.show();
}


void solid() {
  pixels.setPixelColor(0, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(1, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(2, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(3, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(4, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(5, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(6, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(7, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(8, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(9, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(10, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(11, pixels.Color(rVal, gVal, bVal));
  pixels.setPixelColor(12, pixels.Color(rVal, gVal, bVal));


  pixels.show();
}


void infin(int red, int green, int blue) {


  if (ii == 0) {
    pixels.setPixelColor(6, pixels.Color(0, 0, 0));
    pixels.setPixelColor(0, pixels.Color(red, green, blue));
    pixels.show();
  }
  else if (ii == 1) {
    pixels.setPixelColor(1, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(0, pixels.Color(0, 0, 0));
    pixels.show(); // This sends the updated pixel color to the hardware.
  }
  else if (ii == 2) {
    pixels.setPixelColor(2, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(1, pixels.Color(0, 0, 0));
    pixels.show();
  }
  else if (ii == 3) {
    pixels.setPixelColor(3, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(2, pixels.Color(0, 0, 0));
    pixels.show();
  }
  else if (ii == 4) {
    pixels.setPixelColor(4, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(3, pixels.Color(0, 0, 0));
    pixels.show();
  }
  else if (ii == 5) {
    pixels.setPixelColor(5, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(4, pixels.Color(0, 0, 0));
    pixels.show();
  }
  else if (ii == 6) {
    pixels.setPixelColor(6, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(5, pixels.Color(0, 0, 0));
    pixels.show();
  }
  else if (ii == 7) {
    pixels.setPixelColor(12, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(6, pixels.Color(0, 0, 0));
    pixels.show();
  }
  else if (ii == 8) {
    pixels.setPixelColor(11, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(12, pixels.Color(0, 0, 0));
    pixels.show();
  }
  else if (ii == 9) {
    pixels.setPixelColor(10, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(11, pixels.Color(0, 0, 0));
    pixels.show();
  }
  else if (ii == 10) {
    pixels.setPixelColor(9, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(10, pixels.Color(0, 0, 0));
    pixels.show();
  }
  else if (ii == 11) {
    pixels.setPixelColor(8, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(9, pixels.Color(0, 0, 0));
    pixels.show();
  }
  else if (ii == 12) {
    pixels.setPixelColor(7, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(8, pixels.Color(0, 0, 0));
    pixels.show();
  }
  else if (ii == 13) {
    pixels.setPixelColor(6, pixels.Color(red, green, blue)); // Moderately bright green color.
    pixels.setPixelColor(7, pixels.Color(0, 0, 0));
    pixels.show();
  }


  if (ii > 12) {
    ii = 0;
  }
  else {
    ii = ii + 1;
  }


}


void pulse(int red, int green, int blue) {
  if (pi >= 200) {
    pi = 0;
  }


  //Brightness value counter
  if (pi <= 50) {
    pa = pa + 1;
  }
  if (pi > 50 && pa > 0) {
    pa = pa - 1;
  }


  float fA = float(pa);


  pixels.setPixelColor(6, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));


  pixels.setPixelColor(5, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));
  pixels.setPixelColor(0, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));
  pixels.setPixelColor(7, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));
  pixels.setPixelColor(12, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));


  pixels.setPixelColor(1, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));
  pixels.setPixelColor(4, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));
  pixels.setPixelColor(8, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));
  pixels.setPixelColor(11, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));


  pixels.setPixelColor(2, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));
  pixels.setPixelColor(3, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));
  pixels.setPixelColor(9, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));
  pixels.setPixelColor(10, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));


  pixels.show();
  pi = pi + 1;
}


void burst(int red, int green, int blue) {
  if (bi >= 300) {
    bi = 0;
  }


  // a column
  if (bi <= 50) {
    ba = ba + 1;
  }
  if (bi > 50 && ba > 0) {
    ba = ba - 1;
  }


  // b column
  if (bi >= 25 && bi <= 75) {
    bb = bb + 1;
  }
  if (bi > 75 && bb > 0) {
    bb = bb - 1;
  }


  //c column
  if (bi >= 50 && bi <= 100) {
    bc = bc + 1;
  }
  if (bi > 100 && bc > 0) {
    bc = bc - 1;
  }


  //d column
  if (bi >= 75 && bi <= 125) {
    bd = bd + 1;
  }
  if (bi > 125 && bd > 0) {
    bd = bd - 1;
  }


  float fA = float(ba);
  float fB = float(bb);
  float fC = float(bc);
  float fD = float(bd);


  pixels.setPixelColor(6, pixels.Color(fA / 50 * red, fA / 50 * green, fA / 50 * blue));


  pixels.setPixelColor(5, pixels.Color(fB / 50 * red, fB / 50 * green, fB / 50 * blue));
  pixels.setPixelColor(0, pixels.Color(fB / 50 * red, fB / 50 * green, fB / 50 * blue));
  pixels.setPixelColor(7, pixels.Color(fB / 50 * red, fB / 50 * green, fB / 50 * blue));
  pixels.setPixelColor(12, pixels.Color(fB / 50 * red, fB / 50 * green, fB / 50 * blue));


  pixels.setPixelColor(1, pixels.Color(fC / 50 * red, fC / 50 * green, fC / 50 * blue));
  pixels.setPixelColor(4, pixels.Color(fC / 50 * red, fC / 50 * green, fC / 50 * blue));
  pixels.setPixelColor(8, pixels.Color(fC / 50 * red, fC / 50 * green, fC / 50 * blue));
  pixels.setPixelColor(11, pixels.Color(fC / 50 * red, fC / 50 * green, fC / 50 * blue));


  pixels.setPixelColor(2, pixels.Color(fD / 50 * red, fD / 50 * green, fD / 50 * blue));
  pixels.setPixelColor(3, pixels.Color(fD / 50 * red, fD / 50 * green, fD / 50 * blue));
  pixels.setPixelColor(9, pixels.Color(fD / 50 * red, fD / 50 * green, fD / 50 * blue));
  pixels.setPixelColor(10, pixels.Color(fD / 50 * red, fD / 50 * green, fD / 50 * blue));


  pixels.show();
  bi = bi + 1;
}


Libraries and Dependencies:

  • The code uses several libraries for different functionalities:
  • `Adafruit_NeoPixel`: This library is used to control NeoPixel LEDs, which are RGB LEDs that can display various colors.
  • `ESP8266WiFi` and `WiFiClient`: These libraries are used for handling Wi-Fi connectivity.
  • `ESP8266WebServer`: This library allows you to create a web server on the ESP8266 for controlling the Glowtie via a web interface.
  • `EEPROM`: This library is used for reading and writing data to the ESP8266's EEPROM memory.


Constants and Variables:

  • The code defines various constants and variables to control the behavior of the Glowtie, including LED pin, Wi-Fi settings, battery monitoring, LED colors, brightness, and different lighting modes (off, low battery, solid color, infinity, pulse, burst).


Setup Function:

  • The `setup()` function initializes the serial communication, NeoPixel LEDs, and EEPROM.
  • It also checks the battery voltage and sets the Glowtie to low battery mode if the voltage is below a threshold.
  • The Wi-Fi access point is created for controlling the Glowtie through a web interface.
  • A web server is started, and the IP address of the Glowtie is printed to the serial monitor.


Loop Function:

  • The `loop()` function continuously runs and handles different lighting modes based on the `btMode` variable.
  • It also periodically checks the battery voltage and switches to low battery mode if necessary.
  • The available lighting modes include off, low battery, solid color, infinity, pulse, and burst.


Web Interface:

  • The `handleRoot()` function serves as the web interface for controlling the Glowtie.
  • It reads RGB color values and the selected mode from a web form and updates the Glowtie's settings accordingly.
  • The web interface allows users to change the LED color and select different lighting modes.


LED Animation Functions:

  • Several functions (`off()`, `lowBatt()`, `solid()`, `infin()`, `pulse()`, and `burst()`) are used to control the LED patterns and colors based on the selected mode.


The code provides the Glowtie with the capability to change its LED color and lighting pattern through a web interface, making it a customizable and interactive wearable LED accessory. Users can control the Glowtie's appearance by accessing the web interface and adjusting the settings.

3D Printing & GlowTie Assembly

IMG_1698.jpg
Glowtie Assembly

Solder, connect & ensure the LiPo battery is connected with the proper polarity before initiating the 3D printer. If you do not have a 3D printer available, consider utilizing 3D printing services offered in your local area."

3D printed parts for enclosures offer a versatile and cost-effective solution for housing various electronic devices, prototypes, or mechanical components. These customized enclosures are created using additive manufacturing technology, allowing for precise and intricate designs, find the .stl files attached for the bumper and frame with the .step file.

Interfacing With WiFi

GlowTie Interfacing with WiFi

To establish a connection with the GlowTie, follow these steps: 

1. Activate the slide switch and access the Wi-Fi settings on your mobile phone, ensuring that mobile data is disabled.

2. Locate an SSID named "PizzaBowTie" and connect to it. Take note of the provided IP address.

3. Enter the IP address into your mobile phone's browser to enjoy the ability to interface with and alter the GlowTie's colors.

Voila!

GlowTie
IMG_1704.jpg
IMG_1702.jpg

Congratulations

Enjoy your creation by attaching the GlowTie to your shirt, engaging with its interface, and reveling in the brilliant and vibrant colors it offers.