As I wanted good brightness warm white light plus an option of "ambiance lighting", I decided to install:
For the white light:
- One 10W COB LED stripe
http://www.banggood.com/10W-COB-LED-Lamp-Light-Bulb-1000LM-Warm-Pure-White-for-DIY-12-24V-p-937811.html - 4 pcs (total 1 meter) warm white 5050 LED stripe (many vendors)
I have found the tape glue on the back side of these LED stripes to not be reliable over time, so I spent a messy half-hour removing the adhesive using isopropanol. It was not easy, so if anyone has a better way to do it - please let me know! After soldering wires to them, I hotglued the LED stripes in place as shown.
I wanted the four channels (white, red, green, blue) controlled individually, with dimmer, driven from an ESP8266. The white circuit draws around 1,5 amp at 12V, so the best suited FET I found in my drawers to switch that was an IRF540 (N-channel). This is not a logic-level FET, so cannot be driven by the ESP directly, so 2N2222's are used to drive the FETs.
I selected an ESP-03, which has enough GPIO pins. I used GPIO2 on one of the channels. GPIO0 and GPIO2 must either be floating or pulled high at power-on for the ESP to start up correctly. This is why the 2N2222 connected to GPIO2 has an extra pullup resistor.
My electronics board is powered by a 230VAC/12VDC adapter, and a small step-down module provides 3,3VDC for the ESP.
The firmware on the ESP is the integer version of NodeMCU.
https://github.com/nodemcu/nodemcu-firm ... v_20150406
The lamp is controlled via an EasyIoT server running on my Raspberry Pi 2: http://iot-playground.com/
The EasyIoT server generates a web page that can be accessed from any device inside our home wifi network:
The Lua code running on the ESP is:
------------------------------------------------------------------------------
-- Lua driver for RGBW dimmer node
-- Written to work as an EasyIoT Virutal Node
-- http://iot-playground.com/forum/general/102-nodemcu-dimmer-switch-temp-sensor-without-arduino
-- https://github.com/DennisSc/easyIoT-nodeMCU
-- Based on QuinLED_ESP8266_V0.4
-- http://blog.quindorian.org/2014/12/esp8266-wifi-led-dimmer-part-1-of-x.html
--
-- Signals are inverted
--
-- ArnieO, 16 april 2015
------------------------------------------------------------------------------
-- PARAMETERS
CHANR=6 -- GPIO12
CHANG=7 --GPIO13
CHANB=5 --GPIO14
CHANW=4 --GPIO2
--SSID & PW
SSID = "YourSSID"
PW = "YourNetworkPassword"
IPADR = "192.168.0.172"
IPROUTER = "192.168.0.1"
-- Start with all lights off
pwm.setup(CHANR, 500, 1023)
pwm.setup(CHANG, 500, 1023)
pwm.setup(CHANB, 500, 1023)
pwm.setup(CHANW, 500, 0)
pwm.start(CHANR)
pwm.start(CHANG)
pwm.start(CHANB)
pwm.start(CHANW)
CHAN=0
-- Inverted values: 1023 = OFF
LEDR_current=1023
LEDG_current=1023
LEDB_current=1023
LEDW_current=0
LED_current=1023
Fadetime=1000
Stepcounter=0
PosStepcounter=0
DimTimer=0
--StepcounterG=0
--PosStepcounterG=0
--DimTimerG=0
--StepcounterB=0
--PosStepcounterB=0
--DimTimerB=0
wifi.setmode(wifi.STATION)
wifi.sta.setip({ip=IPADR,netmask="255.255.255.0",gateway=IPROUTER})
wifi.sta.config(SSID,PW)
srv=net.createServer(net.TCP)
srv:listen(43333,function(conn)
conn:on("receive",function(conn,payload)
print("\nInput: "..payload)
-- Inverting the received value: 1023 -> 0, 0 -> 1023
LED_target=tonumber(string.sub(payload, 13) )
LED_target = 1023 - LED_target
print("Led_target= "..LED_target)
if string.find(payload,"LEDR") then
dimmerchannel = "R"
LED_current = LEDR_current
LEDR_target = LED_target
LEDR_current = LEDR_target
CHAN = CHANR
elseif string.find(payload,"LEDG") then
dimmerchannel = "G"
LED_current = LEDG_current
LEDG_target = LED_target
LEDG_current = LEDG_target
CHAN = CHANG
elseif string.find(payload,"LEDB") then
dimmerchannel = "B"
LED_current = LEDB_current
LEDB_target = LED_target
LEDB_current = LEDB_target
CHAN = CHANB
elseif string.find(payload,"LEDW") then
dimmerchannel = "W"
LED_current = LEDW_current
LEDW_target = LED_target
LEDW_current = LEDW_target
CHAN = CHANW
end
print("Received LED"..dimmerchannel.." Target Value: "..LED_target)
Stepcounter=(LED_target)-(LED_current)
if (Stepcounter) < 0 then
PosStepcounter= - (Stepcounter)
else PosStepcounter=(Stepcounter)
end
-- if (PosStepcounter) == 0 then
-- PosStepcounter=1
-- end
if (PosStepcounter) == 0 then
PosStepcounter=(PosStepcounter)+1
else PosStepcounter=(PosStepcounter)
end
DimTimer=(Fadetime)/(PosStepcounter)
if (DimTimer) == 0 then
DimTimer=1
end
print ("Dimmer channel "..dimmerchannel..":")
print ("Fadetime: "..Fadetime)
print ("Stepcounter: "..Stepcounter)
print ("PosStepcounter: "..PosStepcounter)
print ("DimTimer: "..DimTimer)
print ("LED_current: "..LED_current)
print ("LED_target: "..LED_target)
tmr.alarm(0, (DimTimer), 1, function()
if LED_current < LED_target then
LED_current = (LED_current + 1)
pwm.setduty(CHAN, LED_current)
elseif LED_current > LED_target then
LED_current = (LED_current - 1)
pwm.setduty(CHAN, LED_current)
elseif LED_current == LED_target then
tmr.stop(0)
end
end)
end)
end)
print ("EasyIoT dimmer/switch sensor for nodeMCU")
print ("Based on QuinLED_ESP8266_V0.4_")
The starting point for this code was shamelessly stolen from the forum over at http://iot-playground.com/ - then modified some.
All PWM values are inverted, so that OFF is 1023 and ON is 0. The reason for this is that the 2N2222 drivers will switch the FETS on when the GPIOs are low and vice versa. The fade time is one second.
At power-up, the Lua code initializes so that the white light is fully on, the RGB LEDs off. In this way, the wall switch in our bedroom will work almost as with a normal lamp - except that once power is on, the lamp can be controlled from the closest smartphone or tablet.
The white LEDs are connected to J2, the RGBs to J3/J4.
I soldered this on a 5x7 cm perfboard hotglued to the lamp base. The ESP-03 is placed in a socket so that it can be programmed separately then plugged in.
The automation on the EasyIoT server is four very similar programs, this is for the white channel:
// dimmer node N4S0 WHITE
const String ESP8266_IP_ADDRESS = "192.168.0.172";
const String NODE_ADDRESS = "N4S0";
const String NODE_COLOR = "W"; // Set dimmer color channel here: R, G or B - or W
int myVal;
/*
This code is running one time when program is enabled
*/
public void Setup()
{
EventHelper.ModuleChangedHandler((o, m, p) =>
{
if (m.Domain == "Virtual" && m.Address == NODE_ADDRESS)
{
Console.WriteLine("\r\n"+ m.Domain +" "+ m.Address +" in program id "+ Program.ProgramId.ToString() +" property "+ p.Property +" value "+ p.Value);
Console.WriteLine("Program was triggered by change of "+ p.Property +" for node id "+ m.Address);
switch (p.Property)
{
case "Sensor.DimmerLevel":
myVal = ConvertRange(0, 100, 0, 1023, Convert.ToInt32(p.Value));
Console.WriteLine("Mapped "+ p.Property +" value of "+ p.Value +" to pwm value "+ myVal + "\r\n");
sendToServer("LED" + NODE_COLOR + "_target=" + myVal.ToString());
break;
case "Sensor.DigitalValue":
if (myVal == 1)
myVal = 1023;
Console.WriteLine("triggering \"Sensor.DimmerLevel\" event...\r\n");
//sendToServer("LED" + NODE_COLOR + "_target="+myVal);
ModuleHelper.SetProperty(m.Domain, m.Address, "Sensor.DimmerLevel", (Convert.ToInt32(p.Value)*100).ToString());
EventHelper.SetEvent(m.Domain, m.Address, "Sensor.DimmerLevel");
break;
default:
break;
} //end switch
}//end if
return true;
});//end EventHelper
}
/*
This code is running periodicaly when program is enabled.
Cron job determine running period.
*/
public void Run()
{}
private void sendToServer(String message)
{
try
{
Console.WriteLine("TCP client command:" + message + "\r\n");
Int32 port = 43333;
System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient( ESP8266_IP_ADDRESS, port);
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
System.Net.Sockets.NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
// Close everything.
stream.Close();
client.Close();
}
catch(Exception e)
{
Console.WriteLine(e.StackTrace + "\r\n");
}
}
private static int ConvertRange(
int originalStart, int originalEnd, // original range
int newStart, int newEnd, // desired range
int value) // value to convert
{
double scale = (double)(newEnd - newStart) / (originalEnd - originalStart);
return (int)(newStart + ((value - originalStart) * scale));
}
Photos with low light intensity:
With the lamp cover on (unfortunately some shadow from the power supply):