Light Sensor to turn lights on

Hi there, just wanted to share a project I have been working on, in case it’s of use to anyone else.

Since I first got emonSD running on my Pi Model B I was keen to explore the OOK feature to control sockets, which have some lights attached, and I played with a couple of software solutions to have the lights come on at sunset. But although it was easy to get the “time” for sunset, the amount of light in the house was affected by the weather - a bright evening meant the lights came on too early, and a dull cloudy evening meant the lights came on too late. So it was time to look at a way of detecting the actual brightness in the room.

I started by testing with a Light Dependent Resistor on a Pi 3, polled via a python script, which sent a message to the MQTT broker on the Pi model B running emonSD, where a NodeRed flow did some checking and then triggered the OOK transmission (later I added Alexa integration).

Although this worked, the value from the LDR was prone to jump around a bit, and this meant quite a lot of the NodeRed flow was spent dealing with the random values. But the concept had been proved, so time for a better hardware solution.

I got a couple of TSL2591 from adafruit, and started by testing it on a Pi Zero, and running in parallel with the LDR on the Pi3. Apart from some newbie challenges with testing the connection to the TSL2591, once I had soldered the 6 pin header to the TSL2591, the connection was solid.

[Pictures of the Pi Zero with TSL2591 light sensor, and wifi dongle connected via USB cable]

So then I focused on the software, aAnd this was an interesting part of the learning experience - creating sufficient logging information to make it easy to see what had happened, and when, and which client had caused it ! The limited logging from the Mosquitto MQTT broker made it hard to see the actual data arriving at the broker, but a simple NodeRed flow that subscribes to all topics, and writes to a file on the Pi, which I can then tail, gave me a real time view of messages arriving at the broker, which helped enormously in problem determination.

Initially the python script on the Pi Zero with the TSL light sensor was using the “simplified” publish.single function to send each reading, and although this worked, I wanted to understand more about the MQTT client, and so I have now re-written to use the separate functions for connect, client and publish. This meant learning about callbacks, and eventually getting the MQTT loop feature to work, which puzzled me for a while.

So as you can tell, i’m a not very experienced with Python yet, so if you see any howlers in the code, please let me know so I can improve.

Python script to poll TSL2591 and send message to MQTT broker
#!/usr/bin/python
# Poll the TSL2591 Light Sensor

import tsl2591
import time
import datetime
import paho.mqtt.publish as publish
import paho.mqtt.client as mqtt

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
    print("Connected with result code "+str(rc))
	
#The callback for when this client publishes to the server.
def on_publish(client, userdata, mid):
	timestamp = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S ')
	if rc[0] != 0:
		print (str(timestamp) +" Publish produced return code "+str(rc[0]))
	print(str(timestamp) +" message published")
def on_disconnect_handler(client, userdata, rc):
    if rc != 0:
        print("Unexpected disconnection.")
def on_log(client, userdata, level, buf):
    print("log: ",buf)
		
#Setup parameters  

interval = 59 #number of seconds to wait between each poll

# Open the log file
fo = open("/home/pi/Light_Sensor/sensor.log", "a+")

print "Light sensor reading every",interval,"seconds"
print "Lux Full IR"

tsl = tsl2591.Tsl2591()  # initialize an instance

mqttc = mqtt.Client(client_id="[email protected]")
mqttc.on_connect = on_connect
mqttc.on_publish = on_publish
mqttc.on_disconnect = on_disconnect_handler
#mqttc.on_log=on_log #enable this to display mqtt logging

mqttc.username_pw_set("xxxxxx", "xxxxxxx")
  
mqttc.connect("192.168.0.x", 1883, 60)
mqttc.loop_start() #Start a loop that reads the buffers and invokes call backs


while True:
	full, ir = tsl.get_full_luminosity()  # read raw values (full spectrum and ir spectrum)
	lux = tsl.calculate_lux(full, ir)  # convert raw values to lux
	timestamp = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S ')
	print timestamp, lux, full, ir
	lux = str(lux)
	fo.write (str(timestamp+lux+'\n'))
#send value via MQTT
	rc=mqttc.publish("TSL2591/light",lux,qos=1)
	time.sleep(interval)

# Close opened file
fo.close()

The real processing is done in NodeRed. The flow subscribes to the topic from the light sensor “TSL2591/light” and then does some checks. If the time is before 4pm, it ignores it, as this is too early to turn the lights on. If the time is after 10pm it ignore it as the lights will be on by now. And if the time is after 11pm, it sends the off command to turn the lights off.

Assuming the time is after 4pm, it checks the value from the light sensor. If the value is below the lower threshold, it turns the lights on. If the value is above the upper threshold, it turns them off, and if it’s in between, it takes no action. This ‘gap’ is important to avoid lights coming on, and then going off on the next cycle, and requires tuning based on where the sensor is located.

Initially the sensor was in the bay window, protected from the room by a curtain, so when the lights were turned on, the sensor was not affected. But as the Pi Zero had a dangly USB power cable, plus a short USB cable with adaptor and wifi dongle, I wanted to move it out the bay window, into somewhere less noticeable. Time for another learning curve! As soon as I brought it into the room, it was affected when the lights were turned on, and the sensor reading shot up above the upper threshold and so it turned the lights off again! This was solved through trial and error, by making the gap big enough.

The flow also uses the rbe node to suppress duplicates. This has the handy side-effect that once it has turned the lights on, if we manually turn the lights off (usually via a voice command to Alexa), the flow doesn’t react, as it still think the lights are on. And this works in reverse.

There is a node that adds a message when the flow starts, again to help when investigating issues - it’s helpful to know when has restarted without digging through syslog.


Node Red flow:poll_TSL2591.txt (1.9 KB)

The next stage is to replace the Pi Zero with an ESP8266, to make the whole sensor smaller. However this may mean porting the python script to a different language, which I suspect will be fun. My ideal goal would be to power the ESP8266 from batteries, and charge the batteries from a small solar panel, which I will happily put back in the bay window, but I fear that may be a ‘Bridge too far’ !