Introduction: Internet of Things Lighthouse Using Arduino ESP8266 & WS2812 LED

A Lighthouse showing the current temperature outside as read by an IoT thermometer logging to Thingspeak.

This whole idea was brought about by another Raspberry Pi Certified Educator and Computing at Schools Coordinator, Lorraine Underwood. She produced a thermometer that lit up the stairs by utilising an online weather report. See her blog about it here. Neopixel Temperature Stairlights.

I tried her lights and they are great. But. I live in a bungalow and don't have any stairs. Also I already have in the garden a summer house with an NodeMcu ESP8266 with a DB18B20 Thermometer logging the outside temperature to Thingspeak every 5 minutes. That was my first proper go at using the NodeMcu, Arduino and The Internet of Things.

I decided to make something based on the Stairlights that utilised my own data but instead of having blocks of colours that lit up it would sweep the LED colours gradually from cold to hot. All I needed was a bit of inspiration. That came from my locality in South West England, Cornwall. Not far from here is a lighthouse at Godrevy. I thought I could make a lighthouse with the lights sweeping round and changing colour as the temperature altered. An idea was born.

Step 1: What Was Needed.

As I am not very good with Arduino coding I decided to initially code this up using a Raspberry Pi and Python. I haven't got my hands on a PiZero W yet which would also work quite well for this project. So I also have the code for using one of those and an ESP8266.

The big advantage for me regarding the ESP8266 is the single power lead that can also provide the power directly to the LED and there is no requirement to shut down properly. With Arduino you can just pull the lead out. With a Raspberry Pi you do really need to provide a power down button in addition which adds to the complexity of the build and they are nearly £10 and the NodeMcu Lua ESP8266 was only £2.61!

So the list of items I have used was something like this:

  • WS2812x LED String (I used 60 per metre but the more expensive 144 per metre might mean no soldering). No link for these from eBay as the seller is no longer there. But there are loads of others for around £5.
  • NodeMcu Lua ESP8266 WiFi Arduino board. (Link above)
  • Raspberry Pi (used to initially work out how to control the LED only but could be used instead).
  • Wire to connect the LED, Male to Female breadboard jumper wires.
  • Large tube. (Mine was from Skimmed Milk Powder).
  • Yoghurt pot.
  • Cocktail Sticks
  • String
  • White Emulsion Paint
  • Glue Gun
  • Craft Knife
  • PVA
  • Masking Tape
  • Soldering Iron, solder, wire stripper etc.

Step 2: Getting the Code Right

To program this Lighthouse I used a Raspberry Pi and Python. I'm more familiar with Python than Arduino and felt getting it working first and proving that the idea was sound was important. Then when working it would be just a matter of recoding into Arduino.

So with a Raspberry Pi set up with a string of WS2812 LED as per this web site I like and also the Adafruit Raspberry Pi Neopixel guide for the software installation you can code away.

Testing like this ensures that the LED will work and the sequencing makes sense.

The hardest part is getting the colours correct for the temperature scale. Look at the graph above to help this next bit and the code make sense. Starting from the lowest it is all blue values for the LED setting (Red, Green, Blue) = (0, 0, 170).

As the temperature climbs the green values are added and the blue ones subtracted until the midpoint is reached when it is all green (0, 170, 0).

From then on the green is subtracted and the red increased until the maximum point is reached at (170, 0 , 0).

I feel that the LED do not need to be at full brightness so the scale goes from 0 to 170 instead of 255. As the scale of 170 colour levels does not always divide exactly equally between the needed temperature steps, at each point minimum, midpoint and maximum the levels are fixed to the max or min levels in the code.

# This is the code for the Raspberry Pi not the Arduino NodeMcu!

#!/usr/bin/env python</p><p><br>import urllib2,json
from time import sleep</p><p>from neopixel import *

# max is the maximum temperature for your scale
maximum = 30
		
# min is the minimum temperature for your scale
minimum = 0
		
#DC is the change in colour value per degree
DC = (170 / ((0+maximum)+(0-minimum)))
		
# midpoint is the middle of the colour scale values
midpoint = (maximum - (0-minimum)) / 2

# Channel ID and API key for the Summer House Thingspeak Channel
READ_API_KEY='6L97BH43NK9I76ZU'
CHANNEL_ID=229797

# LED strip configuration:
LED_COUNT      = 6	 # Number of LED pixels.
LED_PIN        = 18      # GPIO pin connected to the pixels (must support PWM!).
LED_FREQ_HZ    = 800000  # LED signal frequency in hertz (usually 800khz)
LED_DMA        = 5       # DMA channel to use for generating signal (try 5)
LED_BRIGHTNESS = 255     # Set to 0 for darkest and 255 for brightest
LED_INVERT     = False   # True to invert the signal (when using NPN transistor level shift)

# Define functions which animate LEDs in various ways.
		
def lighthouse(strip, color):
	repeat = 0
	while repeat < 150:			#temperature is only updated every 5 minutes
		gap = 0.2
		strip.setPixelColor(0,color)	# first light on
		strip.show()
		sleep(gap)
		strip.setPixelColor(5,0)	#sixth light off
		strip.show()
		sleep(gap)
		strip.setPixelColor(1,color)	# second light on
		strip.show()
		sleep(gap)
		strip.setPixelColor(0,0)	#first light off
		strip.show()
		sleep(gap)
		strip.setPixelColor(2,color)	# third light on
		strip.show()
		sleep(gap)
		strip.setPixelColor(1,0)	#second light off
		strip.show()
		sleep(gap)
		strip.setPixelColor(3,color)	# fourth light on
		strip.show()
		sleep(gap)
		strip.setPixelColor(2,0)	#third light off
		strip.show()
		sleep(gap)
		strip.setPixelColor(4,color)	# fifth light on
		strip.show()
		sleep(gap)
		strip.setPixelColor(3,0)	#fourth light off
		strip.show()
		sleep(gap)
		strip.setPixelColor(5,color)	# sixth light on
		strip.show()
		sleep(gap)
		strip.setPixelColor(4,0)	#fifth light off
		strip.show()
		sleep(gap)
		repeat += 1			#repeat sequence 150 times

# Main program logic follows:


if __name__ == '__main__':
	# Create NeoPixel object with appropriate configuration.
	strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS)
	# Intialize the library (must be called once before other functions).
	strip.begin()</p><p>	print ('Press Ctrl-C to quit.')
	
	while True:
		# set initial variables for colours
		red = 0
		green = 0
		blue = 0
		
		# Connect to Thingspeak Channel and collect temperature data
		conn = urllib2.urlopen("http://api.thingspeak.com/channels/%s/feeds/last.json?api_key=%s" \
                           % (CHANNEL_ID,READ_API_KEY))
		response = conn.read()
		print "http status code=%s" % (conn.getcode())
		data=json.loads(response)
		
		print "Temperature", data['field1'], "Measured at ", data['created_at']
		conn.close()
		
		# Change data into an integer for colour value calculations
		temp = int(float(data['field1']))


# Between the scales midpoint and maximum the Red values increase from Zero to 170 and
# Green values decrease from 170 to Zero. Blue stays at Zero.


# To test the colours change uncomment next line and adjust the temp value and rerun the program.
# Don't forget to remove or comment it out before proper use.
# temp = 25

		
		if temp >= midpoint:
			blue = 0
			red = (temp - midpoint) * DC
			green = 170- ((temp - midpoint) * DC)
			if red > 170:
				red = 170
				
# Between the midpoint on the scale and the minimum the Green values decrease from 
# 170 to Zero and the Blue values increase from Zero to 170. Red stays at Zero.
					
		if temp >= minimum and temp < midpoint:
			red = 0
			green = temp * DC
			if green > 170:
				green = 170
			blue = 170 - (temp * DC)
			if blue > 170:
				blue = 170
				
		if temp < minimum:
			red = 0
			green = 0
			blue = 170
		
		lighthouse(strip, Color(green,red,blue))

Step 3: Putting It Into the Arduino Code for the NodeMcu ESP8266

So once the code was working the code was translated into Arduino. Here is that code.

#include <Adafruit_Neopixel.h><br>
#include "ThingSpeak.h"

// ***********************************************************************************************************
// This example selects the correct library to use based on the board selected under the Tools menu in the IDE.
// Yun, Ethernet shield, WiFi101 shield, esp8266, and MXR1000 are all supported.
// With Yun, the default is that you're using the Ethernet connection.
// If you're using a wi-fi 101 or ethernet shield (http://www.arduino.cc/en/Main/ArduinoWiFiShield), uncomment the corresponding line below
// ***********************************************************************************************************
//#define USE_WIFI101_SHIELD
//#define USE_ETHERNET_SHIELD</p><p>#if !defined(USE_WIFI101_SHIELD) && !defined(USE_ETHERNET_SHIELD) && !defined(ARDUINO_SAMD_MKR1000) && !defined(ARDUINO_AVR_YUN) && !defined(ARDUINO_ARCH_ESP8266)
#error "Uncomment the #define for either USE_WIFI101_SHIELD or USE_ETHERNET_SHIELD"
#endif

#if defined(ARDUINO_AVR_YUN)
#include "YunClient.h"
YunClient client;
#else
#if defined(USE_WIFI101_SHIELD) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_ARCH_ESP8266)
// Use WiFi
#ifdef ARDUINO_ARCH_ESP8266
#include 
#else
#include 
#include 
#endif


char ssid[] = "XXXXXXXXX";    //  your network SSID (name)
char pass[] = "ZZZZZZZZZ";   // your network password


int status = WL_IDLE_STATUS;
WiFiClient  client;
#elif defined(USE_ETHERNET_SHIELD)
// Use wired ethernet shield
#include 
#include 
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
EthernetClient client;
#endif
#endif</p><p>unsigned long myChannelNumber = 229797;
const char * myReadAPIKey = "6L97BH43NK9I76ZU";

#define NUM_LEDS 6
#define DATA_PIN 13 // LED strip on pin 13 or D7
// Define the array of leds
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, DATA_PIN, NEO_RGB + NEO_KHZ400);

/* Parameters for the limits of the colour scale */
const int maximum = 25;
const int minimum = 3;
int DC = (170 / ((0 + maximum) + (0 - minimum)));
int  midpoint = (maximum - (0 - minimum)) / 2;
int red = 0;
int green = 0;
int blue = 0;

void setup() {
  Serial.begin(9600);
#ifdef ARDUINO_AVR_YUN
  Bridge.begin();
#else
#if defined(ARDUINO_ARCH_ESP8266) || defined(USE_WIFI101_SHIELD) || defined(ARDUINO_SAMD_MKR1000)
  WiFi.begin(ssid, pass);
#else
  Ethernet.begin(mac);
#endif
#endif

  ThingSpeak.begin(client);

  strip.begin();
  strip.show();
}

void loop() {
  // Read the latest value from field 1 of channel Summer House
  float temp = ThingSpeak.readFloatField(myChannelNumber, 1, myReadAPIKey);

  Serial.print("Latest temperature is: ");
  Serial.print(temp);
  Serial.println(" C");

  Serial.print(midpoint);
  Serial.print(DC);

// To test the colours change uncomment next line and adjust the temp value and rerun the program.
// Don't forget to remove or comment it out before proper use.
// temp = 25

  if (temp >= midpoint) {
    Serial.print("Temp > midpoint");

    blue = 0;
    green = 170 - ((temp - midpoint) * DC);
    red = ((temp - midpoint) * DC);
    if (red > 170) {
      red = 170;
    }
  }

  if (temp >= minimum and temp < midpoint){
    Serial.print("Temp in between min and midpoint");
    red = 0;
    green = temp*DC;
    if (green > 170){
      green = 255;
    }
    blue = 170 - (temp*DC);
    if (blue > 170){
      blue = 170;
    }
  }    
  
  Serial.print("Colours are ");
  Serial.print(red);
  Serial.print("\t");
  Serial.print(green);
  Serial.print("\t");
  Serial.print(blue);
  LightHouse(red, green, blue);

}

void LightHouse(uint8_t red, uint8_t green, uint8_t blue) {
    for (int repeat = 0; repeat < 150; repeat++){
        int gap = 200;
        strip.setPixelColor(0, green,red,blue);
        strip.show();
        delay(gap);
        strip.setPixelColor(5, 0,0,0);
        strip.show();
        delay(gap);
        strip.setPixelColor(1, green,red,blue);
        strip.show();
        delay(gap);
        strip.setPixelColor(0, 0,0,0);
        strip.show();
        delay(gap);
        strip.setPixelColor(2, green,red,blue);
        strip.show();
        delay(gap);
        strip.setPixelColor(1, 0,0,0);
        strip.show();
        delay(gap);
        strip.setPixelColor(3, green,red,blue);
        strip.show();
        delay(gap);
        strip.setPixelColor(2, 0,0,0);
        strip.show();
        delay(gap);
        strip.setPixelColor(4, green,red,blue);
        strip.show();
        delay(gap);
        strip.setPixelColor(3, 0,0,0);
        strip.show();
        delay(gap);
        strip.setPixelColor(5, green,red,blue);
        strip.show();
        delay(gap);
        strip.setPixelColor(4, 0,0,0);
        strip.show();
        delay(gap);
    
  }
}

Step 4: Make the Lighthouse Light.

The lights in the Lighthouse were to consist of 6 WS2812 LED placed in a circle. I used three bottle tops glued together as the base to hold them. This just fits nicely inside an upturned yoghurt pot.

The LED strip needs to be cut into individual LED and placed onto the bottle tops. I put a dab of hot glue in the middle just to hold them in place and then used two thin strips of electrical insulation tape to really secure them in place. On my first attempt I found the heat from the soldering iron remelted the hot glue quickly and the LED came unstuck easily.

Place them one right way up then another upside down equally spaced around the caps. The first one in the sequence will have the wires running into it from the bottom then you will need to solder very carefully wires from top of the +V, Din and Gnd from one LED to the next as in the picture above. Repeat for all the LED following the arrows except for the last one which is not connected to the first.

I then got three male to female breadboard jumper leads and gently bent the male end by 90 degrees. This so the leads will tuck underneath the caps making it nice and easy to hot glue the cap to the top of the lighthouse. I also cut away the cap behind where the wires came out of the LED so it make a neat finish.

Once it was all wired up the female end of the leads just connect to the D7 (GPIO 13) for the Din, Gnd to Gnd and +V to the 5v of the NodeMcu. Power up and hopefully watch your lights go round and round.

Step 5: Make the Lighthouse

From the pictures you can get an idea of how the lighthouse was made. Large tub for the base, clear yoghurt pot for the light and something for the roof. Just look around the kitchen and recycling.

Use masking tape to protect the window area when painting as in the pictures. A few coats of paint were needed but as its emulsion it dries very quickly. The nail was used to punch holes through the metal base, now top, of the tub for the cocktail sticks. They were then glued in place from the inside with PVA.

String was glued with the PVA around the sticks to make the railing. A bit of patience here as it is a little fiddly. The final touch is the windows around the outside. I just looked up some pics online until I found a window I liked and then printed a whole load out on a sheet of paper. Cut out and glue.

Step 6: Final Steps

Now all that is needed is to put the lights into the Lighthouse.

I cut a hole in the top carefully with a craft knife and bent the metal flap back. It's difficult to cut it out completely. Run the three jumper wires down through the hole. Use the hot glue gun to stick the LED lights in place. Then glue the yoghurt pot over the LED and the roof on top of that.

For the NodeMcu I used a piece of balsa wood to make a way of attaching to the inside base of the tub. Cardboard would also work. I cut some notches out from the side to make way for the wires and another notch out of the side of the tub for the power lead. Clip it together and a bit of hot glue to secure it all.

Place your masterpiece somewhere nice and power it up.

Hope you enjoy it.

Lights Contest 2017

Participated in the
Lights Contest 2017

Microcontroller Contest 2017

Participated in the
Microcontroller Contest 2017