Lizard Lights: Arduino Controlled
Joshua HThis projected was created to help my brother monitor and time his Bearded Dragon's lights, temperature, and humidity. Unfortunately this project got put off for awhile, but thankfully it is all finished and just needs to be installed. I didn't have this place to document so I mainly have pictures of the finished project.
I used an Arduino Uno R3 with an ethernet shield. Coupled with 4 relays I am able to control 4 outlets. Currently the project allows for toggling on and off each outlet through the web as well as a timer based system. We monitor the temperature and humidity and allow an additional light to be turned on if it gets too cold.
Using DHT11 sensors for accurate enough humidity and temperature readings. they were fairly cheap and bought in a pack of 5 from eBay. The code is pretty simple. Including a couple different libraries and reusing code for NTP I was able to get most of the functionality without writing something from scratch.
I used this handy power rail that allowed for a more modular way to add or remove my main power lines. Each tab can be twisted off to add more or remove extras. For added safety I included a little push button breaker in case of a short or something bad happening. I have a cheap wall charger that has a 2 amp and a 1 amp USB port. I power the arduino with one and the relay board with the other. This was key in helping keep a stable relay board.
/*
* LLFinal
* Lizard Lights Final
*
* Digital Pin Wiring:
* pin 2 - Sensor 1 (Input) pin 6 - Relay 1 (Output)
* pin 3 - Sensor 2 (Input) pin 7 - Relay 2 (Output)
* pin 4 - Sensor 3 (Input) pin 8 - Relay 3 (Output)
* pin 5 - Sensor 4 (Input) pin 9 - Relay 4 (Output)
*
* Written By: Joshua Hostetler
* Date: 9/29/2013
* Description: Turn on and off four total lights for bearded dragon.
* Read from temperature and humidity sensors to determine
* amount of light and base off of time of day.
*/
#include <Arduino.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SPI.h>
#include "DHT.h"
#define DHTTYPE DHT11
#define DEBUG false
#define Serial if(DEBUG)Serial
//--- ethernet and ntp init info
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0xDA, 0xA6 };
IPAddress ip(10, 0, 0, 7);
IPAddress gateway(10, 0, 0, 1);
IPAddress subnet(255, 255, 255, 0);
// NTP Server:
IPAddress timeServer(10, 0, 0, 1);
unsigned int localPort = 8888;
//--- Timezone info for ntp time
const int timeZone = -5;
//--- Digital Pins
const int leftSide = 2;
const int middleLog = 3;
const int rightSide = 4;
const int Platform = 5;
const int ccflBulb = 6;
const int dayBulb = 7;
const int bLight1 = 8;
const int bLight2 = 9;
//--- Control Url
String readUrl;
//--- Relay state - relays are active low
const int ON = 0;
const int OFF = 1;
//--- on off time for day lights
const int onTime = 480;
const int offTime = 1350;
//--- Set up sensors
DHT dhtLeft(leftSide, DHTTYPE);
DHT dhtLog(middleLog, DHTTYPE);
DHT dhtRight(rightSide, DHTTYPE);
DHT dhtPlatform(Platform, DHTTYPE);
//--- Set up ethernet
EthernetUDP Udp;
EthernetServer server(80);
void setup()
{
//--- initialize pins so relays are inactive on startup
digitalWrite(ccflBulb, OFF);
digitalWrite(dayBulb, OFF);
digitalWrite(bLight1, OFF);
digitalWrite(bLight2, OFF);
//--- Set up the Relays as outputs
pinMode(ccflBulb, OUTPUT);
pinMode(dayBulb, OUTPUT);
pinMode(bLight1, OUTPUT);
pinMode(bLight2, OUTPUT);
//--- Initialize sensors
dhtLeft.begin();
dhtLog.begin();
dhtRight.begin();
dhtPlatform.begin();
//--- open the serial port
Serial.begin(9600);
Ethernet.begin(mac, ip, gateway, subnet);
Serial.print("IP address is ");
Serial.println(Ethernet.localIP());
Udp.begin(localPort);
Serial.println("waiting for sync");
setSyncProvider(getNtpTime);
setSyncInterval(600); //in seconds - 10min
server.begin();
//--- Create Alarms and Timers
Alarm.alarmRepeat(8, 00, 00, MorningAlarm);
Alarm.alarmRepeat(22, 30, 00, EveningAlarm);
Alarm.timerRepeat(300, DayStatus);
Alarm.timerOnce(10, initStart);
}
void loop()
{
listenForEthernetClients();
Alarm.delay(2000);
}
//--- Alarm functions - turn lights on and off here
void MorningAlarm()
{
digitalWrite(ccflBulb, ON);
digitalWrite(dayBulb, ON);
digitalWrite(bLight1, OFF);
digitalWrite(bLight2, OFF);
}
void EveningAlarm()
{
digitalWrite(ccflBulb, OFF);
digitalWrite(dayBulb, OFF);
digitalWrite(bLight1, ON);
digitalWrite(bLight2, ON);
}
void DayStatus()
{
int curTime = (hour()*60)+minute();
if(curTime >= onTime && curTime <= offTime){
CheckSensorsAM();
} else {
CheckSensorsPM();
}
}
void initStart()
{
int curTime = (hour()*60)+minute();
if(curTime >= onTime && curTime <= offTime) {
MorningAlarm();
} else {
EveningAlarm();
}
}
void CheckSensorsAM()
{
float Tem;
float coolSide = 0;
float warmSide = 0;
Tem = dhtLeft.readTemperature();
if (isnan(Tem)) {
Serial.println("Failed to read from DHT");
} else {
coolSide += Tem;
}
Tem = dhtLog.readTemperature();
if (isnan(Tem)) {
Serial.println("Failed to read from DHT");
} else {
coolSide += Tem;
}
Tem = dhtRight.readTemperature();
if (isnan(Tem)) {
Serial.println("Failed to read from DHT");
} else {
warmSide += Tem;
}
Tem = dhtPlatform.readTemperature();
if (isnan(Tem)) {
Serial.println("Failed to read from DHT");
} else {
warmSide += Tem;
}
coolSide = coolSide/2;
warmSide = warmSide/2;
if (coolSide <= 25) {
digitalWrite(bLight1, ON);
} else if (coolSide >= 37) {
digitalWrite(bLight1, OFF);
digitalWrite(bLight2, OFF);
}
if (warmSide <= 26){
digitalWrite(bLight2, ON);
} else if (warmSide >= 43) {
digitalWrite(bLight1, OFF);
digitalWrite(bLight2, OFF);
}
}
void CheckSensorsPM()
{
float Tem;
float coolSide = 0;
float warmSide = 0;
Tem = dhtLeft.readTemperature();
if (isnan(Tem)) {
Serial.println("Failed to read from DHT");
} else {
coolSide += Tem;
}
Tem = dhtLog.readTemperature();
if (isnan(Tem)) {
Serial.println("Failed to read from DHT");
} else {
coolSide += Tem;
}
Tem = dhtRight.readTemperature();
if (isnan(Tem)) {
Serial.println("Failed to read from DHT");
} else {
warmSide += Tem;
}
Tem = dhtPlatform.readTemperature();
if (isnan(Tem)) {
Serial.println("Failed to read from DHT");
} else {
warmSide += Tem;
}
coolSide = coolSide/2;
warmSide = warmSide/2;
if (coolSide <= 21) {
digitalWrite(bLight1, ON);
} else if (coolSide >= 32) {
digitalWrite(bLight1, OFF);
}
if (warmSide <= 21){
digitalWrite(bLight2, ON);
} else if (warmSide >= 32) {
digitalWrite(bLight2, OFF);
}
}
//--- listen for incoming clients
void listenForEthernetClients() {
float Hum, Tem;
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
if (readUrl.length() < 100) {
readUrl += c;
}
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println("Refresh: 30"); // refresh the page automatically every 30 sec
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
// output time
client.print(hour());
client.print(":");
if(minute() < 10)
client.print('0');
client.print(minute());
client.print(":");
if(second() < 10)
client.print('0');
client.print(second());
client.println("<br />");
client.println("<br />");
// output the state of each relay
client.print("CCFL Bulb");
client.print(" is ");
if (digitalRead(ccflBulb) == 1){
client.println("OFF");
} else {
client.println("ON");
}
client.println("<br />");
client.print("Black Light 1");
client.print(" is ");
if (digitalRead(bLight1) == 1){
client.println("OFF");
} else {
client.println("ON");
}
client.println("<br />");
client.print("Black Light 2");
client.print(" is ");
if (digitalRead(bLight2) == 1){
client.println("OFF");
} else {
client.println("ON");
}
client.println("<br />");
client.print("Day Bulb");
client.print(" is ");
if (digitalRead(dayBulb) == 1){
client.println("OFF");
} else {
client.println("ON");
}
client.println("<br />");
client.println("<br />");
// out the humidity and temperature of each sensor
Hum = dhtLeft.readHumidity();
Tem = dhtLeft.readTemperature();
if (isnan(Tem) || isnan(Hum)) {
client.println("Failed to read from DHT");
} else {
client.print("Humidity Left Side: ");
client.print(Hum);
client.print(" %\t");
client.print("Temperature Left Side: ");
client.print(Tem);
client.println(" *C");
}
client.println("<br />");
Hum = dhtLog.readHumidity();
Tem = dhtLog.readTemperature();
if (isnan(Tem) || isnan(Hum)) {
client.println("Failed to read from DHT");
} else {
client.print("Humidity by Log: ");
client.print(Hum);
client.print(" %\t");
client.print("Temperature by Log: ");
client.print(Tem);
client.println(" *C");
}
client.println("<br />");
Hum = dhtRight.readHumidity();
Tem = dhtRight.readTemperature();
if (isnan(Tem) || isnan(Hum)) {
client.println("Failed to read from DHT");
} else {
client.print("Humidity Right Side: ");
client.print(Hum);
client.print(" %\t");
client.print("Temperature Right Side: ");
client.print(Tem);
client.println(" *C");
}
client.println("<br />");
Hum = dhtPlatform.readHumidity();
Tem = dhtPlatform.readTemperature();
if (isnan(Tem) || isnan(Hum)) {
client.println("Failed to read from DHT");
} else {
client.print("Humidity by Platform: ");
client.print(Hum);
client.print(" %\t");
client.print("Temperature by Platform: ");
client.print(Tem);
client.println(" *C");
}
client.println("<br />");
client.println("</html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disonnected");
}
controlRelay();
}
//--- turn relay on or off
void controlRelay()
{
if (readUrl.indexOf("?Day") > 0) {
digitalWrite(ccflBulb, ON);
digitalWrite(dayBulb, ON);
digitalWrite(bLight1, OFF);
digitalWrite(bLight2, OFF);
} else if (readUrl.indexOf("?Night") > 0) {
digitalWrite(ccflBulb, OFF);
digitalWrite(dayBulb, OFF);
digitalWrite(bLight1, ON);
digitalWrite(bLight2, ON);
} else if (readUrl.indexOf("?BL1On") > 0) {
digitalWrite(bLight1, ON);
} else if (readUrl.indexOf("?BL1Off") > 0) {
digitalWrite(bLight1, OFF);
} else if (readUrl.indexOf("?BL2On") > 0) {
digitalWrite(bLight2, ON);
} else if (readUrl.indexOf("?BL2Off") > 0) {
digitalWrite(bLight2, OFF);
} else if (readUrl.indexOf("?ccflOn") > 0) {
digitalWrite(ccflBulb, ON);
} else if (readUrl.indexOf("?ccflOff") > 0) {
digitalWrite(ccflBulb, OFF);
} else if (readUrl.indexOf("?dayOn") > 0) {
digitalWrite(dayBulb, ON);
} else if (readUrl.indexOf("?dayOff") > 0) {
digitalWrite(dayBulb, OFF);
}
readUrl="";
}
/*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
time_t getNtpTime()
{
while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.println("Transmit NTP Request");
sendNTPpacket(timeServer);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Receive NTP Response");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response :-(");
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}