//Locking, Automatic Chicken Coop Door
//By Mike Roberts 2024 (based off of original code by Seth Johnson Land To House llc 2016)
//#include <Arduino.h>
#include <ElegantOTA.h>
#include <AsyncTCP.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <ESPAsyncWebServer.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
#include <DallasTemperature.h>
#define CoolRelay 32
#define HeatRelay 33
#define ONE_WIRE_BUS 4
const boolean SerialDisplay = true; // print debug messages or not to serial
//This section is for initializing things:
//const int led = 13;
int tcrt;
const int PWM = 26; //enables PWM on the drv8801 motor controller.
const int DIR = 27; //send high or low to DIR on the motor controller.
const int red = 14; // red LED heating
const int blue = 12; // blue LED cooling
const int coopDoorOpenLed = 13;
const int coopDoorClosedLed = 23;
const int lightSensorPin = 34; //Initialize "lightSensor" This is the photoresistor.
const int lightSensorPin2 = 32;
const int BotSwtch = 18;
const int TopSwtch = 19;
const int TopSwtch2 = 17;
int lightVal = 0;
int lightVal2 = 0;
int BotSwtchState = 0;
int TopSwtchState = 0; //These are the variables that hold the state of the reed switches
int TopSwtch2State = 0;
//int localIPA = 0;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
//Set WiFi Credentials
const char *ssid = "chicken";
const char *password = "********";
// Try and get a static IP address (so we can find the device for web pages)
IPAddress local_IP(192, 168, 8, 88); // change 103 to be unique on your system
IPAddress gateway(192, 168, 8, 1);
IPAddress subnet(255, 255, 255, 0);
// To get the local time we must include DNS servers. Google's used here.
IPAddress primaryDNS(1, 1, 1, 1);
IPAddress secondaryDNS(1, 0, 0, 1);
const char *wl_status_to_string(wl_status_t status);
//WebServer server(80);
AsyncWebServer server(80);
void updateParm1(std::string *htmlString, std::string findPattern, std::string replaceWith)
{
// Copy the string into something we can easily work with locally
std::string tempString = (*htmlString);
// Find the FIRST occurence of the placeholder @parm1
size_t Lightval1Pos = tempString.find(findPattern);
// Replace all the occurences (it might be in multiple places)
while (Lightval1Pos != std::string::npos) {
log_d("Found %s", findPattern.c_str());
tempString.replace(Lightval1Pos, findPattern.length(), replaceWith);
Lightval1Pos = tempString.find(findPattern);
}
// Update the html string passed to us originally
*htmlString = tempString;
}
void updateParm2(std::string *htmlString, std::string findPattern, std::string replaceWith)
{
// Copy the string into something we can easily work with locally
std::string tempString = (*htmlString);
// Find the FIRST occurence of the placeholder @parm1
size_t tempFPos = tempString.find(findPattern);
// Replace all the occurences (it might be in multiple places)
while (tempFPos != std::string::npos) {
log_d("Found %s", findPattern.c_str());
tempString.replace(tempFPos, findPattern.length(), replaceWith);
tempFPos = tempString.find(findPattern);
}
// Update the html string passed to us originally
*htmlString = tempString;
}
void updateParm3(std::string *htmlString, std::string findPattern, std::string replaceWith)
{
// Copy the string into something we can easily work with locally
std::string tempString = (*htmlString);
// Find the FIRST occurence of the placeholder @parm1
size_t topswitchPos = tempString.find(findPattern);
// Replace all the occurences (it might be in multiple places)
while (topswitchPos != std::string::npos) {
log_d("Found %s", findPattern.c_str());
tempString.replace(topswitchPos, findPattern.length(), replaceWith);
topswitchPos = tempString.find(findPattern);
}
// Update the html string passed to us originally
*htmlString = tempString;
}
void updateParm4(std::string *htmlString, std::string findPattern, std::string replaceWith)
{
// Copy the string into something we can easily work with locally
std::string tempString = (*htmlString);
// Find the FIRST occurence of the placeholder @parm1
size_t botswitchPos = tempString.find(findPattern);
// Replace all the occurences (it might be in multiple places)
while (botswitchPos != std::string::npos) {
log_d("Found %s", findPattern.c_str());
tempString.replace(botswitchPos, findPattern.length(), replaceWith);
botswitchPos = tempString.find(findPattern);
}
// Update the html string passed to us originally
*htmlString = tempString;
}
void updateParm5(std::string *htmlString, std::string findPattern, std::string replaceWith)
{
// Copy the string into something we can easily work with locally
std::string tempString = (*htmlString);
// Find the FIRST occurence of the placeholder @parm1
size_t topswitch2Pos = tempString.find(findPattern);
// Replace all the occurences (it might be in multiple places)
while (topswitch2Pos != std::string::npos) {
log_d("Found %s", findPattern.c_str());
tempString.replace(topswitch2Pos, findPattern.length(), replaceWith);
topswitch2Pos = tempString.find(findPattern);
}
// Update the html string passed to us originally
*htmlString = tempString;
}
void motordown() //code for sending door down
{
BotSwtchState = digitalRead(BotSwtch);
while (BotSwtchState != HIGH) {
analogWrite(PWM, 100);
digitalWrite(DIR, LOW);
BotSwtchState = digitalRead(BotSwtch);
}
delay(1000); //wait 1 seconds before turning off the motor to let the locks engage at the bottom
analogWrite(PWM, 0); // now turn off motor
digitalWrite(coopDoorClosedLed, HIGH); // turns on coopDoorClosedLed (green)
digitalWrite(coopDoorOpenLed, LOW); // turns off coopDoorOpenLed (red)
}
void motorup() // this function will run the motor as long as switch 2 has not been triggered
{
BotSwtchState = digitalRead(BotSwtch);
TopSwtchState = digitalRead(TopSwtch);
// TopSwtch2State = digitalRead(TopSwtch2);
while (TopSwtchState != HIGH) {
analogWrite(PWM, 5);
digitalWrite(DIR, HIGH);
TopSwtchState = digitalRead(TopSwtch);
// TopSwtch2State = digitalRead(TopSwtch2);
}
delay(100);
analogWrite(PWM, 0);
digitalWrite(coopDoorClosedLed, LOW); // turns on coopDoorClosedLed (green)
digitalWrite(coopDoorOpenLed, HIGH); // turns off coopDoorOpenLed (red)
}
void setup() //This only runs once.
{
Serial.begin(115200); // initialize the serial communications:
//connect to WiFi
WiFi.mode(WIFI_STA);
WiFi.persistent(false);
WiFi.setAutoReconnect(true);
WiFi.setSleep(false);
WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS);
wl_status_t reply = WiFi.begin(ssid, password);
log_w("Result: %s", wl_status_to_string(reply));
// Just to be sure we have a 404 response on unknown requests
server.onNotFound(
[](AsyncWebServerRequest *request) {
// Send back a plain text message (can be made html if required)
request->send(404, "text/plain", "404 - Page Not Found, oops!");
});
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
// Tell the system that we will be sending back html (not just plain text)
AsyncResponseStream *response = request->beginResponseStream("text/html");
// Format the html as a RAW literal so we don't have to escape all the quotes (and more)
std::string htmlOutput = R"(
<html>
<head>
<script src="https://kit.fontawesome.com/73839aa4ec.js" crossorigin="anonymous"></script>
<body>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<style>
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h2 { font-size: 3.0rem; }
p { font-size: 2.0rem; }
k { font-size: 1.5rem; }
.units { font-size: 1.2rem; }
.ds-labels{
font-size: 1.5rem;
vertical-align:middle;
padding-bottom: 15px;
}
</style>
</body>
</head>
<body>
<h2>Chicken Coop Status Page</h2>
<p>
<i class="fas fa-thermometer-half" style="color:#059e8a;"></i>
<span class="ds-labels">Temperature Fahrenheit</span>
<span id="temperaturef">@tempF</span>
<sup class="units">°F</sup>
</p>
<head>
<title>Chicken Coop Status Page</title>
<style>
body {
background-color: forestgreen;
font-family: Arial, Sans-Serif;
color: yellow;
}
#myValue {
width: 50px;
}
</style>
</head>
<body>
<h1>Light Sensor Value: @Lightval1</h1>
<h1>Top Switch State: @topswitch</h1>
<h1>Bottom Switch State: @botswitch</h1>
<h1>Top Switch 2 State: @topswitch2</h1>
<div style="position: relative">
<k style="position: fixed; bottom: 0; width:100%; text-align: center;"> Revision 4-24-2026
</k>
</div>
</body>
</html>
)";
updateParm1(&htmlOutput, "@Lightval1", std::to_string(lightVal));
updateParm2(&htmlOutput, "@tempF", std::to_string(sensors.getTempFByIndex(0)));
updateParm3(&htmlOutput, "@topswitch", std::to_string(TopSwtchState));
updateParm4(&htmlOutput, "@botswitch", std::to_string(BotSwtchState));
updateParm5(&htmlOutput, "@topswitch2", std::to_string(TopSwtch2State));
// Output the stuffed string (yes, that's a term)
response->printf(htmlOutput.c_str());
// Send back all the text/html for a standard web page
request->send(response);
});
ElegantOTA.begin(&server); // Start ElegantOTA
server.begin();
Serial.println("HTTP server started");
lcd.init(); // set up the LCD's number of columns and rows:
lcd.backlight();
sensors.begin();
pinMode(red, OUTPUT);
pinMode(blue, OUTPUT);
pinMode(CoolRelay, OUTPUT);
pinMode(HeatRelay, OUTPUT);
pinMode(PWM, OUTPUT);
pinMode(DIR, OUTPUT); // set the motor control pins as outputs.
pinMode(coopDoorOpenLed, OUTPUT);
pinMode(coopDoorClosedLed, OUTPUT);
pinMode(BotSwtch, INPUT_PULLUP);
pinMode(TopSwtch, INPUT_PULLUP);
pinMode(TopSwtch2, INPUT_PULLUP);
BotSwtchState = digitalRead(BotSwtch);
TopSwtchState = digitalRead(TopSwtch);
TopSwtch2State = digitalRead(TopSwtch2);
// TestBtnState = digitalRead(TestBtn);
if (BotSwtchState != HIGH) //if BotSwtchState is not high then go to the while statement
{
while (BotSwtchState != HIGH) // this function will run the motor down as long as switch 1 has not been triggered
{
analogWrite(PWM, 100); // turn on motor and set speed to 255
digitalWrite(DIR, LOW); // DIR LOW=down HIGH=up
BotSwtchState = digitalRead(BotSwtch); //read the state of the switch again to make sure that it has not been triggered
TopSwtchState = digitalRead(TopSwtch); //read the state of switch 2 (the top one) and place it in TopSwtchState
}
delay(500);
analogWrite(PWM, 0); // once TopSwtchState has been triggered turn off the motor
digitalWrite(coopDoorClosedLed, HIGH); // turns on coopDoorClosedLed (green)
digitalWrite(coopDoorOpenLed, LOW);
}
}
//this runs over and over
void loop() {
ElegantOTA.loop();
//Code for OTA
sensors.requestTemperatures();
float temperature = sensors.getTempFByIndex(0);
lightVal = analogRead(lightSensorPin); //read the light sensor and place it in lightval
lcd.home();
char buffer[50];
sprintf(buffer, "LSV: %4d", (analogRead(lightSensorPin))); // was A0 which is now 19 try 19 if lightSensorPin doesnt work
lcd.setCursor(0, 0);
lcd.print(buffer);
lcd.setCursor(10, 0);
lcd.print("Temp:"); // sprintf(buffer, "Temp: %f\337F", temperature);
lcd.print(sensors.getTempFByIndex(0));
lcd.print("\337F"); // lcd.print (buffer);
Serial.print(" Light sensor value: ");
Serial.println(analogRead(lightSensorPin));
Serial.print(" ");
Serial.print(sensors.getTempFByIndex(0));
Serial.println("\337F");
if (temperature < 40) //changed temp to 40
{
digitalWrite(red, HIGH);
digitalWrite(blue, LOW);
Serial.println(" Heating");
digitalWrite(CoolRelay, 0);
digitalWrite(HeatRelay, 1);
delay(3000);
} else if (temperature > 40 && temperature < 85) //changed temp to 40
{
digitalWrite(red, LOW);
digitalWrite(blue, LOW);
Serial.println(" All Off");
digitalWrite(CoolRelay, 0);
digitalWrite(HeatRelay, 0);
} else if (temperature > 85) {
digitalWrite(CoolRelay, 1);
digitalWrite(HeatRelay, 0);
digitalWrite(red, LOW);
digitalWrite(blue, HIGH);
Serial.println(" Cooling ");
}
BotSwtchState = digitalRead(BotSwtch);
Serial.print(" Bottom Switch Value: "); // display "Bottom Switch Value:"
Serial.println(digitalRead(BotSwtch));
sprintf(buffer, "Bot Sw: %d", BotSwtchState);
lcd.setCursor(0, 1);
lcd.print(buffer);
TopSwtchState = digitalRead(TopSwtch);
Serial.print(" Top Switch Value: "); // display "Top Switch Value:"
Serial.println(digitalRead(TopSwtch));
sprintf(buffer, "Top Sw: %d", TopSwtchState);
lcd.setCursor(0, 2);
lcd.print(buffer);
TopSwtch2State = digitalRead(TopSwtch2);
Serial.print(" Top Switch 2 Value: "); // display "Top Switch Value:"
Serial.println(digitalRead(TopSwtch2));
sprintf(buffer, "Top Sw2: %d", TopSwtch2State);
lcd.setCursor(0, 3);
lcd.print(buffer);
if (TopSwtchState == HIGH && lightVal < 10) {
delay(30000);
motordown();
} else if (BotSwtchState == HIGH && lightVal > 400) {
delay(5000);
motorup();
}
}
// Update one or more parameters in the web page