close-circle
Close
0%
0%

Wi-Fi christmas lights

Connecting some old fairy lights to an ESP8266 for absolutely no reason whatsoever

Similar projects worth following
close
It's Christmas (sort of) and I wanted to upgrade some old fairy lights. A simple, cheap and quick hack with some interesting Lua learning points.

This is a super simple project but provides a good introduction to the ESP8266 and Lua. A "Hello World" of Wi-Fi projects.

Build instructions

  1. Flash the nodeMCU firmware to your ESP8266. In order for this project to work, you will need the PWM, HTTP and WiFi modules enabled. The firmware in the documents section has everything you need.
  2. Butcher your lack-luster battery powered christmas lights as explained in "the circuit" below. I stuck all my components on a bit of proto board.
  3. Upload the lua script and HTML page to your ESP8266. I use ESPlorer to handle this side of things. Set your baud rate to 115200 when using the firmware.
  4. Plug in the module and connect to the Wi-Fi access point.
  5. Navigate to 192.168.4.1 (the default IP address served up by nodeMCU in AP mode).
  6. Make christmas lights flash!

As an optional extra, I installed my lights inside an Altoids tin (of course) and attached a second string of lights. If you go down this route, make sure you protect any wires from shorting on the case.

The circuit

Super simple! Hook up pin 1 of the nodemcu to a mosfet transistor and put this between the fairy lights and the ground pin. That's all there is to it! If you have no emotional attachments to the lights, just chop the wires off where they enter the battery pack and solder them to the correct ESP pins.

Still confused? Here's a hand drawn gloomy circuit schematic.

The code

This is the more interesting part of the project and a more detailed explanation is in the project logs below. However, here are a few things I learnt:

  1. NodeMCU has a low buffer limit which restricts the amount of HTML you can dump in a single file;
  2. It is possible to monitor the results of an HTML form input pretty easily (although this was frustrating to start with as I couldn't work out where in the payload they were found).

The webpage

A reasonably straightforward HTML form with four sliders for the different settings:

firmware.bin

nodeMCU firmware

octet-stream - 500.00 kB - 12/17/2016 at 11:11

download-circle
Download

Christmas Lights.lua

Main LUA script

lua - 4.48 kB - 12/17/2016 at 11:11

download-circle
Download

mainPage.html

HTML source for webpage

HyperText Markup Language (HTML) - 1.37 kB - 12/17/2016 at 11:11

download-circle
Download

  • 1 × Nodemcu Can just use normal ESP8266 module instead
  • 1 × 2N7000 through hole transistor Why not go SMT? Mad props!
  • 1 × A string of uninspiring battery powered fairy lights I ordered hundreds for my wedding a few years ago
  • 1 × Perf board and hookup wire Dig inside the parts bin

  • The Code!

    Alex Hunt12/17/2016 at 11:07 0 comments

      Here's the code I am currently running. The broad structure is:

      1. Create an open access point for users to connect to;
      2. Declare the variables needed to control the LED flashing speed etc.
      3. Create a table with the locations of various additional files needed. See this excellent tutorial here for how to serve up big files. I have copied most of this code but with a few small tweaks.
      4. Start the PWM on pin 1.
      5. Loop through all of the submitted data from the HTML form (stored in _GET in this example).
      6. Use the data to set the PWM value and alarm cycles.

      The only slightly strange bit of the codes is the lines that follow this pattern:

      partial_data = string.gsub(partial_data,"xxBVxx",ledPWM)

      In the HTML files, each of the values for the form inputs is as follows:

      <input class="slider-width" type="range" name="Brightness" min="0" max="1023" value="xxBVxx" onChange="form.submit()">
      The nodemcu code goes through the HTML source and replaces xxBVxx with the correct PWM value. This ensures that the webpage served up has the sliders in the correct position.
      --Close any servers already running
      if srv then srv:close() srv=nil end
      
      wifi.setmode(wifi.SOFTAP)
      cfg={}
      cfg.ssid="AlexsXmasLights"
      wifi.ap.config(cfg)
      
      --Variables for flashing lights
      led1 = 1
      ledPWM=500
      flashSpeedOn=500
      flashSpeedOff=500
      glowOn="On"
      glowSpeed=5
      gpio.mode(led1, gpio.OUTPUT)
      value = true
      glow=true
      scan=true
      
      --Tables for file locations
      local httpRequest={}
      httpRequest["/"]="mainPage.html";
      httpRequest["/index.html"]="mainPage.html";
      local getContentType={};
      getContentType["/"]="text/html";
      getContentType["/index.htm"]="text/html";
      
      local filePos=0;
      
      
      --Start the PWM stuff at full
      pwm.setup(led1,1000,ledPWM);
      pwm.start(led1);
      
      tmr.alarm(0, 500, 1, function ()
          if(glowSpeed>0)then
              if(glow==true)then
                  ledPWM=ledPWM+1
              else
                  ledPWM=ledPWM-1
              end
              if(ledPWM==1000)then
                  glow=false
              end
              if(ledPWM==1)then
                  glow=true
              end
              pwm.setduty(led1,ledPWM);
              tmr.interval(0, glowSpeed)
          else
              value = not value
              if(value==true) then
                  pwm.setduty(led1,ledPWM);
                  tmr.interval(0, flashSpeedOn)
              else
                  pwm.setduty(led1,0);
                  tmr.interval(0, flashSpeedOff)
              end
          end
      end)
      
      srv=net.createServer(net.TCP)
      srv:listen(80,function(conn)
          conn:on("receive", function(client,request)
              local buf = "";
              scan=false
              local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP");
              if(method == nil)then
                  _, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP");
              end
              local _GET = {}
              if (vars ~= nil)then
                  for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
                      _GET[k] = v
                  end
              end
            
              --Loop through info submitted
              for key,value in pairs(_GET) do 
                  if(key=="FlashSpeedOn") then
                      flashSpeedOn = tonumber(value);
                      if(flashSpeedOn>0) then
                          tmr.start(0)
                      else
                          tmr.stop(0)
                          pwm.setduty(led1,ledPWM);
                      end
                  end
                  if(key=="glowSpeed") then
                      glowSpeed = tonumber(value)
                  end
                  if(key=="FlashSpeedOff") then
                      flashSpeedOff = tonumber(value);
                      if(flashSpeedOff==0) then
                          tmr.stop(0)
                          pwm.setduty(led1,led1023);
                      end
                  end
                  if(key=="Brightness") then
                      ledPWM = tonumber(value);
                  end
                  if(key=="glowOn") then
                      scan=true
                      glow=true
                  end
               end
              if getContentType[path] then
                  requestFile=httpRequest[path];
                  print("[Sending file "..requestFile.."]");            
                  filePos=0;
                  conn:send("HTTP/1.1 200 OK\r\nContent-Type: "..getContentType[path].."\r\n\r\n");            
              else
                  print("[File "..path.." not found]");
                  conn:send("HTTP/1.1 404 Not Found\r\n\r\n")
                  conn:close();
                  collectgarbage();
              end
          end)
          conn:on("sent",function(conn)
              local next1 = 0;
              if requestFile then
                  if file.open(requestFile,r) then
                      file.seek("set",filePos);
                      local partial_data=file.read(512);
                      if(partial_data~=nil) then
                          if (string.len(partial_data)==512) then
                              next1 = 1
                          end
                          partial_data = string.gsub(partial_data,"xxBVxx",ledPWM)
                          partial_data = string.gsub(partial_data,"xxFONVxx",flashSpeedOn)
                          partial_data = string.gsub(partial_data,"xxFOFFVxx",flashSpeedOff)
                          partial_data = string.gsub(partial_data,"xxGSxx",glowSpeed)
                          file.close();
                          if partial_data then
                              filePos=filePos+(512-string...
    Read more »

View project log

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates