Rajarshi Nigam
Published © MIT

Stranger Things Lights Sign/Costume

Build a SIMPLE text-able Halloween costume/sign in a couple of hours & ~$25 worth of electronics! NO soldering, NO advanced skills, NO Barb!

IntermediateFull instructions provided5 hours7,120

Things used in this project

Hardware components

WS2811 Christmas Lights
Any WS2811 lights will do, I picked these cause they're cheap and Prime eligible. You can even use WS2812/NeoPixel lights if you'd like!
×1
Espressif ESP8266 Development Board
Most of the steps are same with any ESP8266 Wi-Fi board or similar add on for Arduino, I picked this one because it's all integrated and $9! It also accepts 5V so it's easily charged by portable batteries, and although its logic is 3.3V it still works for the above 5V light. You could probably use a Raspberry Pi too, but it'll probably chew through your portable battery faster. Also, the code can be easily modified to not be "connected" and maybe just repeat the same phrase over or blink randomly. This one also only works on 2.4Ghz networks!
×1
Female/Female Jumper Wires
Female/Female Jumper Wires
You just need to connect 3 pairs of wires together. You could use jumper cables you have lying around or even electrical tape if you wanted. A breadboard isn't even required, but you could use that too.
×1

Software apps and online services

Firebase
Firebase provides the realtime aspect for this project, I picked it because it's simple to use and has an amazing no credit card required free tier using a Google account you might already have. Something similar like AWS IoT could also be used, their libraries are easy to get started with.
SMS Messaging API
Twilio SMS Messaging API
Twilio is what allows the costume to receive text messages! No credit card required for the 14 day free trial, it works like a charm, and their developer experience is top notch! You could also use something like Plivo pretty easily.
Zapier
This app connects Twilio to Firebase. You could also use something like AWS Lambda or even host your own backend, but Twilio's more user friendly and easy to get started.

Story

Read more

Code

stranger-lights.ino

C/C++
Copy of repo code for the BACKEND version that uses Firebase
#include <FirebaseArduino.h>
#include <ESP8266WiFi.h>
#include "FastLED.h"

// WIFI settings
#define WIFI_SSID "<<WIFI NETWORK NAME>>"
#define WIFI_PASSWORD "<<WIFI PASSWORD>>"

// Firebase settings
#define FIREBASE_HOST "<<FIREBASE HOST>>.firebaseio.com"
#define FIREBASE_AUTH "<<FIREBASE SECRET>>"
#define FIREBASE_PATH "/data/message"

// the milliseconds to give each letter
#define MILLIS_PER_LETTER 1000

// number of LEDs in the strip
#define NUM_LEDS 50

// the data pin the green wire from the LEDs are connected to
#define DATA_PIN 4

// an array to keep track of the LEDs
CRGB leds[NUM_LEDS];

// the message we will display
String message;

// the time we received the message
unsigned long received;

// we'll use all 26 letters of the alphabet
#define NUM_LETTERS 26

// the LED number (start counting from 0) that we light up to show our message
const int LETTER_LEDS[NUM_LETTERS] = {
 /*A*/  7
,/*B*/  8
,/*C*/  9
,/*D*/  10
,/*E*/  11
,/*F*/  12
,/*G*/  13
,/*H*/  14
,/*I*/  32
,/*J*/  31
,/*K*/  30
,/*L*/  29
,/*M*/  28
,/*N*/  26
,/*O*/  25
,/*P*/  24
,/*Q*/  23
,/*R*/  38
,/*S*/  39
,/*T*/  40
,/*U*/  41
,/*V*/  42
,/*W*/  44
,/*X*/  45
,/*Y*/  46
,/*Z*/  47
};

// how many colors to cycle through for the lights
#define NUM_COLORS 4

void setup() {
  // send print statements at 9600 baud
  Serial.begin(9600);

  // initialize the LEDS
  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);

  // set them all to be off
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();

  // connect to wifi.
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("connecting");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print("connected: ");
  Serial.println(WiFi.localIP());

  // connect to Firebase
  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
  Firebase.stream(FIREBASE_PATH);

  // this message will show until it is overwritten from Firebase and shown if Firebase fails
  message = "the quick brown fox jumps over the lazy dog";
  received = millis();
}


void loop() {
  // if Firebase fails, print to the console
  if (Firebase.failed()) {
    Serial.println("streaming error");
    Serial.println(Firebase.error());
  }

  if (Firebase.available()) {
     FirebaseObject event = Firebase.readEvent();
     String eventType = event.getString("type");
     eventType.toLowerCase();
     
     Serial.print("event: ");
     Serial.println(eventType);
     
     // if there is a new data event
     if (eventType == "put") {
       Serial.print("data: ");
       String data = event.getString("data");
       data.toLowerCase();
       Serial.println(data);

       // remember the message and the time it came in
       message = data;
       received = millis();
     }
  }

  // how many milliseconds have elapsed since the last message came in
  unsigned long elapsed = millis() - received;

  // assuming MILLIS_PER_LETTER, what letter (index) ofthe message should we be on?
  int index = (elapsed/MILLIS_PER_LETTER)%message.length();

  // get the character letter we should print
  char letter = message.charAt(index);

  // if the character is between 'a' and 'z' (no numbers, spaces, or punctuations)
  if(letter >= 'a' && letter <= 'z'){
    // how bright to make this LED from 0 to 1, this is what makes them fade in and out
    // it calculates what percent we are completed with the letter, and makes it fade in from 0-50% and fade out from 50-100%
    // the formula can be visualized here: https://www.desmos.com/calculator/5qk8imeny4
    float brightness = 1-abs((2*(elapsed%MILLIS_PER_LETTER)/((float)MILLIS_PER_LETTER))-1);
    uint8_t value = 255 * brightness;
    
    // get the LED number the letter should be in, assuming our array starts at 'a' and ends at 'z'
    int letter_index = letter-'a';
    int led = LETTER_LEDS[letter_index];

    // get a rotation of colors, so that every NUM_COLORS lights, it loops
    // e.g. red, yellow, green, blue, red, yellow green blue
    uint8_t hue = (letter_index%NUM_COLORS*255)/NUM_COLORS;

    // set that LED to the color
    leds[led] = CHSV(hue, 255, value);
    FastLED.show();
    // set it to black so we don't have to remember the last LED we turned on
    leds[led] = CRGB::Black;
    
    Serial.print(letter);
    Serial.print("\t!");
    Serial.print(led);
    Serial.print("\t=");
    Serial.print(brightness);
    Serial.print("\t@");
    Serial.print(elapsed);
    Serial.println();
  } else {
    // if the letter wasn't a-z then, we just turn off all the leds
    FastLED.show();
  }
}

stranger-lights-standalone.ino

C/C++
Copy of the repo code for the standalone version that creates a Wifi network
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include "FastLED.h"

// WIFI settings
#define WIFI_SSID "STRANGER WIFI"

// Network settings
#define DNS_PORT 53
#define WEB_PORT 80

IPAddress apIP(192, 168, 1, 1);
DNSServer dnsServer;
ESP8266WebServer webServer(WEB_PORT );

#define PAGE \
"<!DOCTYPE html>"\
"<html>"\
"<head>"\
  "<title>Stranger Lights</title>"\
  "<meta name='viewport' content='initial-scale=1, maximum-scale=1'>"\
"</head>"\
"<body style='text-align: center;'>"\
  "<h3>STRANGER LIGHTS</h3>"\
  "<form>"\
    "<p>Enter your message below!</p>"\
    "<input type='text' name='message'><br>"\
    "<input type='submit' value='Send via the Upside Down'>"\
  "</form>"\
"</body>"\
"</html>"


// the milliseconds to give each letter
#define MILLIS_PER_LETTER 1000

// number of LEDs in the strip
#define NUM_LEDS 50

// the data pin the green wire from the LEDs are connected to
#define DATA_PIN 4

// an array to keep track of the LEDs
CRGB leds[NUM_LEDS];

// the message we will display
String message;

// the time we received the message
unsigned long received;

// the default millis per letter
//int millis_per_letter;

// we'll use all 26 letters of the alphabet
#define NUM_LETTERS 26

// the LED number (start counting from 0) that we light up to show our message
const int LETTER_LEDS[NUM_LETTERS] = {
 /*A*/  7
,/*B*/  8
,/*C*/  9
,/*D*/  10
,/*E*/  11
,/*F*/  12
,/*G*/  13
,/*H*/  14
,/*I*/  32
,/*J*/  31
,/*K*/  30
,/*L*/  29
,/*M*/  28
,/*N*/  26
,/*O*/  25
,/*P*/  24
,/*Q*/  23
,/*R*/  38
,/*S*/  39
,/*T*/  40
,/*U*/  41
,/*V*/  42
,/*W*/  44
,/*X*/  45
,/*Y*/  46
,/*Z*/  47
};

// how many colors to cycle through for the lights
#define NUM_COLORS 4

void setup() {
  // send print statements at 9600 baud
  Serial.begin(9600);

  // initialize the LEDS
  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);

  // set them all to be off
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();

  // create the wifi network
  Serial.print("Creating the Network");
  Serial.println(WIFI_AP);
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
  WiFi.softAP(WIFI_SSID);

  // if DNSServer is started with "*" for domain name, it will reply with
  // provided IP to all DNS request
  dnsServer.start(DNS_PORT, "*", apIP);

  // replay to all requests with same HTML
  webServer.onNotFound([]() {
    Serial.println(webServer.uri());
    Serial.print("message: ");
    String data = webServer.arg("message");
    if(data.length()){
      webServer.send(200, "text/html", PAGE);
      data.toLowerCase();
      Serial.println(data);
    
      // remember the message and the time it came in
      message = data;
      received = millis();
    }
    webServer.send(200, "text/html", PAGE);
  });
  webServer.begin();

  // this message will show until it is overwritten
  message = "the quick brown fox jumps over the lazy dog";
  received = millis();
}


void loop() {
  dnsServer.processNextRequest();
  webServer.handleClient();

  // how many milliseconds have elapsed since the last message came in
  unsigned long elapsed = millis() - received;

  // assuming MILLIS_PER_LETTER, what letter (index) ofthe message should we be on?
  int index = (elapsed/MILLIS_PER_LETTER)%message.length();

  // get the character letter we should print
  char letter = message.charAt(index);

  // if the character is between 'a' and 'z' (no numbers, spaces, or punctuations)
  if(letter >= 'a' && letter <= 'z'){
    // how bright to make this LED from 0 to 1, this is what makes them fade in and out
    // it calculates what percent we are completed with the letter, and makes it fade in from 0-50% and fade out from 50-100%
    // the formula can be visualized here: https://www.desmos.com/calculator/5qk8imeny4
    float brightness = 1-abs((2*(elapsed%MILLIS_PER_LETTER)/((float)MILLIS_PER_LETTER))-1);
    uint8_t value = 255 * brightness;
    
    // get the LED number the letter should be in, assuming our array starts at 'a' and ends at 'z'
    int letter_index = letter-'a';
    int led = LETTER_LEDS[letter_index];

    // get a rotation of colors, so that every NUM_COLORS lights, it loops
    // e.g. red, yellow, green, blue, red, yellow green blue
    uint8_t hue = (letter_index%NUM_COLORS*255)/NUM_COLORS;

    // set that LED to the color
    leds[led] = CHSV(hue, 255, value);
    FastLED.show();
    // set it to black so we don't have to remember the last LED we turned on
    leds[led] = CRGB::Black;
    
    Serial.print(letter);
    Serial.print("\t!");
    Serial.print(led);
    Serial.print("\t=");
    Serial.print(brightness);
    Serial.print("\t@");
    Serial.print(elapsed);
    Serial.println();
  } else {
    // if the letter wasn't a-z then, we just turn off all the leds
    FastLED.show();
  }
}

Stranger Lights Backend Version

Arduino Source Code for the BACKEND version that uses Firebase

Stranger Lights Standalone Version

Arduino Source Code for the standalone version that creates a Wifi network

Credits

Rajarshi Nigam

Rajarshi Nigam

2 projects • 20 followers

Comments