Shanel Wu
Published © GPL3+

Automatic Chicken Coop Door (feat. takeout containers)

A DIY motorized chicken (and duck) coop door that adapts to sunrise/sunset times, and can optionally be remote-controlled.

IntermediateShowcase (no instructions)Over 1 day666
Automatic Chicken Coop Door (feat. takeout containers)

Things used in this project

Hardware components

SparkFun ESP32 Thing
SparkFun ESP32 Thing
×1
SparkFun Motor Driver - Dual TB6612FNG (1A)
SparkFun Motor Driver - Dual TB6612FNG (1A)
×1
SparkFun Standard Gearmotor - 30 RPM (3-12V)
×1
SparkFun Power Supply - 12V/5V (2A) with ATX breakout
I had to make my own kit because SparkFun was out of stock, so you can also get the power supply and ATX breakout board + components separately.
×1
Solderless Breadboard Half Size
Solderless Breadboard Half Size
×1
Reused plastic takeout containers (w/ lids)
×3
20in (?) drawer slides, 1 pair
Need to double-check the length.
×1
Scrap wood: piece of 2x4, pieces of plywood
2x4 piece is approximately 8"/20cm tall, with a hole drilled out to house the motor. The thin sheet of wood was cut to the shape of the 2x4. Plywood pieces: approx. 6 x 8in / 15 x 20cm for the base of the enclosure, and 12 x 24in / 30 x 60cm for the door.
×1
1/8" nylon rope
×1

Software apps and online services

Arduino IDE
Arduino IDE
Sunrise Sunset API
NetBurner The Network Time Protocol (NTP) API
Google Apps Script

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)
Drill / Driver, Cordless
Drill / Driver, Cordless
Reciprocating Saw
For cutting the door opening into the shed wall
Circular Saw
Cutting wood pieces

Story

Read more

Schematics

Preliminary physical design

Schematic (roughly to scale) of the overall mechanics and key dimensions of the door, created prior to building anything.

Code

[chickenCoop_V2.ino] Main program

Arduino
Main program that includes all of the other .INO files.
/* Main program for chicken/duck coop management system
 * To keep this file's length under control, ONLY include:
 *  - include statements
 *  - macro #definitions
 *  - setup() and loop() functions
 * ALL OTHER CODE CONTAINED IN FUNCTIONS IN OTHER FILES
 */

#include <WiFi.h>
#include <TB6612_ESP32.h>
#include <ESPAsyncWebServer.h>
#include <WebSocketsServer.h>
#include <SPIFFS.h>
#include <HTTPClient.h>
#include "time.h" // https://randomnerdtutorials.com/esp32-date-time-ntp-client-server-arduino/
#include "ArduinoJson.h"

#include "auth.h" // secrets header (.h) file

// motor set-up from https://github.com/sparkfun/SparkFun_TB6612FNG_Arduino_Library
#define AIN1 17
#define AIN2 4
#define PWMA 16
#define STBY 18
#define OFFSET_A 1 // Value can be 1 or -1
#define MANUAL_UP 19    // GPIO for manual motor control
#define MANUAL_DOWN 23  // 2 pins, one for each dir
//#define EMERGENCY_STOP 36 // red button on breadboard, emergency stop
#define OPEN_DR 255
#define CLOSE_DR -255   // motor.drive() vals for open/close

// date-time constants
#define MILLIS 1000
#define HOUR_SEC 3600
#define HOUR_MILLIS HOUR_SEC*MILLIS // 1 hour
#define MINUTE_SEC 60
#define MINUTE_MILLIS 60000 // 1 minute

// data/state macros
#define TZ -7 // timezone GMT-7, Mountain Time (Denver)
#define DATETIME_RDY currentDateTime[0]
#define CURRENT_YEAR currentDateTime[1]
#define CURRENT_MONTH currentDateTime[2]
#define CURRENT_DATE currentDateTime[3]
#define CURRENT_HOUR currentDateTime[4]
#define CURRENT_MINUTE currentDateTime[5]
#define DST_FLAG currentDateTime[6]

#define SUNRISE_RDY sunriseVals[0]
#define SUNRISE_HOUR sunriseVals[1]
#define SUNRISE_MINUTE sunriseVals[2]
#define SUNSET_RDY sunsetVals[0]
#define SUNSET_HOUR sunsetVals[1]
#define SUNSET_MINUTE sunsetVals[2]

#define STATE_NIGHT   0
#define STATE_SUNRISE 1
#define STATE_DAY     2
#define STATE_SUNSET  3

/* USER SETTINGS */
bool doorStatus = false; // true = open, false = closed
bool autoMode = false;
  // true = auto mode, false = manual mode
char flockStatus = 'c'; // flock status, changed by user in UI
  // 'c' = in the coop
  // 'r' = in the run
  // 'y' = in the yard 
// DEFAULT settings - user can adjust motor times and offsets in UI
uint8_t motorInterval_open = 25;
uint8_t motorInterval_close = 15;
uint8_t offset_open = 40;
uint8_t offset_close = 30;

bool googleEnabled = true;
  // when Google connection enabled, ESP will send POST 
  // requests with system status/user setting changes

/* STATE MACHINE GLOBALS */
unsigned long currentTime; // check millisecond time / 1000
unsigned int prevTime; // when the hour was last checked
unsigned int updateInterval;

// motor state machine
Motor motor = Motor(AIN1, AIN2, PWMA, OFFSET_A, STBY, 5000, 8, 1);
uint8_t motorTime; // how many seconds the motor has been running
unsigned int motorStartTime; // when the motor was started
unsigned int motorInterval;
bool motorOn = false;
bool motorDir = true; // true = opening, false = closing

// sunrise/sunset state machine;
char state = 'R'; // state is 'R' in setup/reset state, 
  // setup() will set it to 0-3 depending on time of day

/* CONNECTIVITY / WEB INTEGRATIONS */
// data arrays: sunriseVals/sunsetVals, currentDateTime
// ready byte = 1 when data available, 0 if empty or error

uint8_t sunriseVals[3]; // format: [ready, hour, min]
uint8_t sunsetVals[3];   

// time stuff from NTP
const char* ntpServer = "pool.ntp.org";
int currentDateTime[7]; 
/* format: [ready, year, month, date, hour, min, DST]
- ready: 1 if array data is available, 0 if empty or error
- year: 20XX
- month: 1-12
- date: 1-31 dep on month
- hour: 0-23 for 12AM to 11PM
- min: 0-59 
- DST: (daylight savings flag) +1 if daylight savings, 0 if not, -1 if error
*/

// who's connected
bool clientConnected = false; 
  // when client connected, update every minute for client UI
  // otherwise, just update every hour
//char WSOutBuffer[50];
String message;

// HTTP port for server
const uint8_t port = 80;
AsyncWebServer server(port);
// Websocket for server/client data updates
const uint8_t WSport = 81;
WebSocketsServer webSocket(WSport);

void setup() {
//  Serial.println(millis());
//  Serial.begin(115200);
  pinMode(MANUAL_UP, INPUT_PULLUP);
  pinMode(MANUAL_DOWN, INPUT_PULLUP);
  message.reserve(50);
//  pinMode(EMERGENCY_STOP, INPUT);
  
  if (!SPIFFS.begin()) {
//    Serial.println("error occured while mounting SPIFFS");
    return;
  }

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    currentTime = millis()/1000;
  }
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);

  // loading page and assets
  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(SPIFFS, "/index.html", "text/html", false);
  });
  server.on("/script.js", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(SPIFFS, "/script.js", "text/javascript");
  });
  
  server.begin();

  // Init NTP and sunrise/sunset API connections
  long gmtOffset_sec = TZ*HOUR_SEC; // GMT-7 is Mountain time
  int daylightOffset_sec = HOUR_SEC;
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  updateLocalTime();
  getSunTimes();
//  Serial.printf("System time - %u ms\n", millis());
//  printLocalTime();
//  printSunTimes();

  // Init sunrise-sunset state machine: 
  // determine which state to enter
  if (CURRENT_HOUR > SUNSET_HOUR || CURRENT_HOUR < SUNRISE_HOUR-1) {
//    Serial.println("entering night stage");
    state = STATE_NIGHT; // night time, door should be closed
  } else if (CURRENT_HOUR == SUNRISE_HOUR-2) {
//    Serial.println("entering sunrise stage");
    state = STATE_SUNRISE; // sunrise, door should be open(ing)
  } else if (CURRENT_HOUR > SUNRISE_HOUR-2 && CURRENT_HOUR < SUNSET_HOUR) {
//    Serial.println("entering day stage");
    state = STATE_DAY; // day time, door should be open
  } else if (CURRENT_HOUR == SUNSET_HOUR) {
//    Serial.println("entering sunset stage");
    state = STATE_SUNSET; // sunset, door should be close(ing)
  } 
  if (googleEnabled) {
    message = F("ESP has booted into state ");
    message += int(state);
    postToGoogle(message);
  }
}

void loop() {
//  if (digitalRead(EMERGENCY_STOP)) {
//    motorOn = stopDoor();
//  }
  currentTime = millis()/1000;
  wifiLoop();
  webSocket.loop();
  dayNightLoop();
  
  // motor state machine, only runs if motorOn = true
  if (motorOn) {
    // stop the door automatically after motorInterval
    //  in auto mode: always and mark door open/close
    //  in manual mode: if the door starts off closed and has been opening
    //    OR if the door starts off open and has been closing
    //    (doorState != motorDir)
    if (motorTime >= motorInterval) {
      motorOn = stopDoor();
      updateDoorStatus();
      if (autoMode && doorStatus) {
        flockStatus = 'r';      
        broadcastChange('f');
      } 
      return;
    } else if (currentTime - motorStartTime >= motorTime) {
      // either  auto or manual mode, if the motor was turned on, keep time
      // on how long it runs for
      motorTime++;
      broadcastChange('d');
    }
  } else { // manual motor controls - physical buttons
    if (digitalRead(MANUAL_UP) && digitalRead(MANUAL_DOWN)) {
      // no manual commands
      stopDoor();
    } else if ((!digitalRead(MANUAL_UP)) && digitalRead(MANUAL_DOWN)) {
      openDoor();
    } else if (digitalRead(MANUAL_UP) && (!digitalRead(MANUAL_DOWN))) {
      closeDoor();
    }
  }
}

void dayNightLoop() {
  // sunrise/sunset state machine
  switch (state) {
    case STATE_NIGHT: // night time, wait for sunrise
      updateInterval = (clientConnected) ? MINUTE_SEC : HOUR_SEC;
      if (DATETIME_RDY && SUNRISE_RDY) {
        if (CURRENT_HOUR == SUNRISE_HOUR-2) {
          // if it's less than 2 hours to sunrise, switch to state 2
          state = STATE_SUNRISE;
          broadcastChange('s');
          return;
        }
      }
      if (currentTime - prevTime > updateInterval) {
        prevTime = currentTime;
        updateLocalTime();
        // update sunrise/sunset at midnight local time
        if (CURRENT_HOUR == 0) {
          getSunTimes();
          updateGoogle('c'); // post auto/manual
        }
      }
      break;
    case STATE_SUNRISE: // sunrise: open door
      updateInterval = MINUTE_SEC;
      if (currentTime - prevTime > updateInterval) {
        prevTime = currentTime;
        updateLocalTime();
      }
      if (timeToOpen()) { // no offset: CURRENT_MINUTE >= SUNRISE_MINUTE 
//        if (googleEnabled) {
//          postToGoogle("time to auto open");
//        }
        if (autoMode) { 
          startDoor(true, motorInterval_open);
//          motorInterval = motorInterval_open;
//          motorStartTime = currentTime;
//          motorTime = 0;
//          motorOn = openDoor();
//          updateDoorStatus();
        }
        state = STATE_DAY;
        broadcastChange('s');
        return;
      }
      break;
    case STATE_DAY: // day time, wait for sunset
      updateInterval = (clientConnected) ? MINUTE_SEC : HOUR_SEC;
      if (DATETIME_RDY && SUNSET_RDY) {
        if (CURRENT_HOUR == SUNSET_HOUR) {
          // if it's less than 1 hour to sunset, switch to state 2
          state = STATE_SUNSET;
          broadcastChange('s');
          return;
        }
      }
      if (currentTime - prevTime > updateInterval) {
        updateLocalTime();
      }
      break;
    case STATE_SUNSET: // sunset: close door, reset sunrise/sunset times
      updateInterval = MINUTE_SEC;
      if (currentTime - prevTime > updateInterval) {
        prevTime = currentTime;
        updateLocalTime();
      }
      if (timeToClose()) {
//        if (googleEnabled) {
//          message = F("time to auto close");
//          postToGoogle(message);
//        }
        if (autoMode) {
          startDoor(false, motorInterval_close);
//          motorInterval = motorInterval_close;
//          motorStartTime = currentTime;
//          motorTime = 0;
//          motorOn = closeDoor();
//          updateDoorStatus();
        }
        state = STATE_NIGHT;
        broadcastChange('s');
        return;
      }
      break;
    default:
      break;
  }
}

void wifiLoop() {
  static unsigned int lastConnectAttempt;
  if (currentTime - lastConnectAttempt >= MINUTE_SEC) {
    if (WiFi.status() != WL_CONNECTED) {
      WiFi.disconnect();
      WiFi.begin(ssid, password);
      lastConnectAttempt = currentTime;
    }
  }
}

[auth.h] Personal authentication details TEMPLATE

Arduino
Nope, I'm not giving you my home Wi-Fi password or Google API key or exact GPS coordinates. But I'll provide this version of my auth.h file with placeholder information for you to fill in with your own details, if you decide to try using this code.
const char* ssid = "honeyIShrunkTheWiFi";     // your local Wi-Fi network
const char* password = "networkPassword";     // your Wi-Fi password

const char* sunRiseSetAPI = "https://api.sunrise-sunset.org/json?lat=40.014984&lng=-105.270546";       // these are coordinates to Boulder High School! Replace with your latitude and longitude.

const char* googleScript = ""; // If you want to integrate Google services like the calendar, or a spreadsheet to publish debugging data.

[dateTime.ino] Date/Time handling

Arduino
Functions that handle the system's date/time updating. Includes calling both NTP and sunrise-sunset API's, appropriately unpacking the received data, and comparing if it's time to open/close.
/* date/time API calling and parsing/printing data
 * - NTP and sunrise-sunset
 * - look at NTP documentation
 */
// helper functions for sunrise and sunset states w/ offsets
bool timeToOpen() {
  // if it is [offset_open] minutes til sunrise
  uint8_t openHour, openMin;
  if (offset_open > SUNRISE_MINUTE) { // eg 45 minutes before 6:20
    openMin = 60 - (offset_open - SUNRISE_MINUTE);
    openHour = SUNRISE_HOUR-1;
  } else {
    openMin = SUNRISE_MINUTE - offset_open;
    openHour = SUNRISE_HOUR;
  }
  if (CURRENT_HOUR >= openHour && CURRENT_MINUTE >= openMin) {
    return true;
  } else {
    return false;
  }
}

bool timeToClose() {
  // if it is [offset_close] minutes after sunset
  uint8_t closeHour, closeMin;
  closeMin = SUNSET_MINUTE + offset_close;
  if (closeMin > 59) { // eg 20 minutes before 6:45
    closeMin = closeMin % 60;
    closeHour = SUNSET_HOUR+1;
  } else {
    closeHour = SUNSET_HOUR;
  }
  if (CURRENT_HOUR >= closeHour && CURRENT_MINUTE >= closeMin) {
    return true;
  } else {
    return false;
  }
}

void updateLocalTime() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
//    Serial.println(F("Failed to obtain time"));
    DATETIME_RDY = 0;
    return;
  }
  DATETIME_RDY = 1;
  CURRENT_YEAR = timeinfo.tm_year+1900;
  CURRENT_MONTH = timeinfo.tm_mon+1;
  CURRENT_DATE = timeinfo.tm_mday;
  CURRENT_HOUR = timeinfo.tm_hour;
  CURRENT_MINUTE = timeinfo.tm_min;
  DST_FLAG = timeinfo.tm_isdst;
  
  prevTime = currentTime;
  broadcastChange('t');
}

void getSunTimes() {
  // https://www.dfrobot.com/blog-917.html
  HTTPClient http;
  http.begin(sunRiseSetAPI);
  int httpCode = http.GET();
  if (httpCode > 0) {
    String payload = http.getString();
    http.end();
    StaticJsonDocument<520> doc;
    DeserializationError err = deserializeJson(doc, payload);

    if (err) {
//      Serial.println("Parsing failed");
//      Serial.println(err.c_str());
      return;
    }
    String sunrise = doc["results"]["sunrise"];
    String sunset = doc["results"]["sunset"];
    parseUTCString(sunrise, sunriseVals);
    parseUTCString(sunset, sunsetVals);
    broadcastChange('n');
  }
}

// unpack the string from sunrise-sunset API
// input format: "HH:MM:SS XM" in UTC or "H:MM:SS XM"
// output: [ready, hour, min] in 24-hour Mountain time
void parseUTCString(String UTCString, uint8_t (&buffArray)[3]) {
  uint8_t hour, minute;
  uint8_t firstColon = UTCString.indexOf(':');
  if (firstColon == 1 || firstColon == 2) {
    hour = UTCString.substring(0, firstColon).toInt();
    minute = UTCString.substring(firstColon+1, firstColon+3).toInt();
    char AMPM = (UTCString.charAt(firstColon+7));
    uint8_t DST;
    if (DST_FLAG > 0) { // if daylight savings
      DST = 1;} else {DST = 0;}
    if (AMPM == 'A') { // for AM, subtract GMT offset
      hour = ((hour % 12)+24+TZ+DST) % 24;
    } else if (AMPM == 'P') {
      hour = ((hour % 12)+12+TZ+DST) % 24;
    }
    buffArray[0] = 1;
    buffArray[1] = hour;
    buffArray[2] = minute;
    return;
  }
  buffArray[0] = 0;
  buffArray[1] = -1;
  buffArray[2] = -1; // error when hour/min is negative
}

void buildTimeString() {
  message = F("time: ");
  message += (CURRENT_HOUR > 12) ? CURRENT_HOUR - 12 : CURRENT_HOUR;
  message += ':';
  if (CURRENT_MINUTE < 10) {
    message += '0';
  }
  message += CURRENT_MINUTE;
  message += ' ';
  message += (CURRENT_HOUR > 12) ? F("PM") : F("AM");
}

[doorControl.ino] Door control logic

Arduino
See the main program (chicken_coop_V2.ino) for definitions of globals: motorInterval, motorStartTime, currentTime, motorTime, etc. See [note file here] for definition of broadcastChange() function.
/* File: doorControl.ino
 * Short desc: Handles auto/manual controls for the door motor.
 * IN AUTO MODE:
 *	- runs sunrise/sunset state machines
 *	- when opening/closing door, runs motor state machine 
 *	  with fixed motor interval
 *  - user can adjust motor interval in UI
 * IN MANUAL MODE:
 *	- lets user run motor up/down manually
 *		> web client buttons
 *		> physical press buttons
 *	- lets user manually set open/close status if hook was used
 */

void startDoor(bool dir, uint8_t interval) {
  motorInterval = interval;
  motorStartTime = currentTime;
  motorTime = 0;
  if (dir) {
    motorOn = openDoor();
  } else {
    motorOn = closeDoor();
  }
  updateDoorStatus();
}

// start running motor to open door
bool openDoor() {
  motorDir = true;
  motor.drive(OPEN_DR);
  return true;
}

// start closing door
bool closeDoor() {
  motorDir = false;
  motor.drive(CLOSE_DR); 
  return true;
}

bool stopDoor() {
	motor.brake();
	motorTime = 0; // reset motor time counter
	return false;
}

// when done running the motor, update door status
void updateDoorStatus() {
  if (!motorOn) {
    if (motorDir) { 
      doorStatus = true;
    } else {
      doorStatus = false;
    }
  }
  broadcastChange('d');
}

void updateAutoMode() {
  autoMode = !autoMode;
  motorOn = stopDoor();
  broadcastChange('c');
}

[clientServer.ino] Web interface

Arduino
Handles user interactions with the webpage interface served by the ESP32 to the local Wi-Fi network. The web interface establishes a websocket between itself and the connecting client, sending the user data on the system status, and receiving commands from the user. A lot of this file is just the result of me figuring out some custom "protocol" to minimize the data that needed to be sent over the websocket, while still being kinda plain English. I think the maximum packet size sent is 5 bytes?
/* Callback function for websocket events. */
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
  switch (type) {
    case WStype_DISCONNECTED: // if a client disconnects
//      Serial.printf("[%u] Client disconnected :(\n", num);
      if (webSocket.connectedClients() == 0) { // if no more clients
        clientConnected = false;
      }
      break;
    case WStype_CONNECTED: { // if a new websocket client connects
        if (!clientConnected) { // update clientConnected
          clientConnected = true;
        }
        IPAddress ip = webSocket.remoteIP(num);
//        Serial.printf("[%u] Connected from %d.%d.%d.%d\n", num, ip[0], ip[1], ip[2], ip[3]);
        updateWS('f');
        updateWS('d');
        updateWS('m');
        updateWS('e');
        updateWS('c');
        updateWS('g');
        updateWS('n');
        break;
      }
    case WStype_TEXT: { // if new text data is received
        //        Serial.printf("[%u] sent: %s\n", num, payload);
        if (payload[0] == 'o') {
          // clicked open button
          startDoor(true, 30);
        } else if (payload[0] == 'c') {
          // clicked close button
          startDoor(false, 30);
        } else if (payload[0] == 'h') {
          // released open/close button
          motorOn = stopDoor();
          updateDoorStatus();
        } else if (payload[0] == 's') {
          if (payload[1] == 'o') {
            // clicked manual set open button
            doorStatus = true;
          } else if (payload[1] == 'c') {
            // clicked manual set close button
            doorStatus = false;
          }
          broadcastChange('d');
        } else if (payload[0] == 'f') {
          flockStatus = payload[1];
          broadcastChange('f');
          if (googleEnabled) {
            String message = String((char*)payload);
            message += webSocket.remoteIP(num).toString();
            postToGoogle(message);
          }
        } else if (payload[0] == 'a') {
          // clicked manual button
          updateAutoMode();
        } else if (payload[0] == 'g') {
          // toggle Google POST requests
          googleEnabled = !googleEnabled;
          broadcastChange('g');
        } else if (payload[0] == 'm') {
          // user changed motorInterval thru client interface
          // client will send "m[o/c][seconds]"
          char* param = (char*) payload + 2;
          if (payload[1] == 'o') {
            motorInterval_open = String(param).toInt();
          } else {
            motorInterval_close = String(param).toInt();
          }
          broadcastChange('m');
        } else if (payload[0] == 'e') {
          // user adjusted open/close offsets
          char* param = (char*) payload + 2;
          if (payload[1] == 'o') {
            offset_open = String(param).toInt();
          } else {
            offset_close = String(param).toInt();
          }
          broadcastChange('e');
        } else if (googleEnabled) {
          message = F("client message [");
          message += webSocket.remoteIP(num).toString() + "]: ";
          message += String((char*)payload);
          postToGoogle(message);
        }
        break;
      }
      default:
        break;
  }
}

/* send a POST request to the Google cloud service
   src: https://github.com/arduino-libraries/ArduinoHttpClient/blob/master/src/HttpClient.cpp
*/
int postToGoogle(String data) {
  HTTPClient http;
  http.begin(googleScript);
  http.addHeader(F("Content-Type"), F("text/plain"), true);
  int httpResponseCode = http.POST(data);
  http.end();
  return httpResponseCode;
}

void broadcastChange(char code) {
  static const bool debug = true;
  switch(code) {
    case 'g': // googleEnabled
        if (!googleEnabled) {
          updateGoogle(code);
        }
    case 'c': // autoMode
    case 'm': // motorInterval
    case 'f': // flockStatus
    case 'd': // doorStatus, motorOn, or motorDir
    case 'e': // offset adjustment
    case 'n': // sun times / date-time
        if (clientConnected) {
          updateWS(code);
        }
        if (googleEnabled) {
          updateGoogle(code);
        }
      break;    
    case 't': // time only
      if (clientConnected) {
        updateWS(code);
      }
      if (debug && googleEnabled) {
        updateGoogle(code);
      }
      break;
    case 's': // state
      if (debug && googleEnabled) {
        updateGoogle(code);
      }
      break;
    default:
      break;  
  }
}

void updateWS(char code) {
//  String message;
  switch(code) {
    case 'd': // doorStatus, motorOn, or motorDir
      if (motorOn) {
        if (motorDir) {
          message = F("opening ");
        } else {
          message = F("closing ");
        }
        message += int(motorTime);
      } else if (doorStatus) {
        message = F("open");
      } else {
        message = F("closed");
      }
      webSocket.broadcastTXT(message);
      break;
    case 'f': // flockStatus
      message = F("flock ");
      message += flockStatus;
      webSocket.broadcastTXT(message);
      break; 
    case 't': // update time only
      if (DATETIME_RDY) {
        buildTimeString();
        webSocket.broadcastTXT(message);
      }
      break;
    case 'n': // sun times / date (only sent once per client session)
    /* if there are any new date/time or sunrise/sunset time 
     *  data ready, send to connected clients
     */
      if (DATETIME_RDY) {
        message = F("date: ");
        message += String(CURRENT_MONTH) + '/' + String(CURRENT_DATE) + '/' + CURRENT_YEAR;
        webSocket.broadcastTXT(message);
        buildTimeString();
        webSocket.broadcastTXT(message);
      }    
      if (SUNRISE_RDY && SUNSET_RDY) {
        message = F("sunrise/sunset: ");
        message += String(SUNRISE_HOUR) + ':';// + minute_R + F(" AM");
        if (SUNRISE_MINUTE < 10) {
          message += '0';
        }
        message += String(SUNRISE_MINUTE) + F(" AM/");
        message += String(SUNSET_HOUR - 12) + ':';
        if (SUNSET_MINUTE < 10) {
          message += '0';
        }
        message += String(SUNSET_MINUTE) + F(" PM");
        webSocket.broadcastTXT(message);
      }
      break;
    case 'c': // autoMode
      if (autoMode) {
        message = F("control AUTO");
      } else {
        message = F("control MANUAL");
      }
      webSocket.broadcastTXT(message);
      break;
    case 'm': // motorInterval
      message = F("mo ");
      message += motorInterval_open;
      webSocket.broadcastTXT(message);
      message = F("mc ");
      message += motorInterval_close;
      webSocket.broadcastTXT(message);
      break;
    case 'e': // offsets
      message = F("eo ");
      message += offset_open;
      webSocket.broadcastTXT(message);
      message = F("ec ");
      message += String(offset_close);
      webSocket.broadcastTXT(message);
      break;
    case 'g': // googleEnabled
      if (googleEnabled) {
        message = F("google ON");
      } else {
        message = F("google OFF");
      }
      webSocket.broadcastTXT(message);
      break;
    case 's': // state
      message = F("state ");
      message += int(state);
      webSocket.broadcastTXT(message);
      break;
    default:
      break;  
  }
}

void updateGoogle(char code) {
  switch(code) {
    case 'd': // doorStatus, motorOn, or motorDir
      if (!motorOn) {
        if (doorStatus) {
          message = F("do");
        } else {
          message = F("dc");
        }      
        postToGoogle(message);
      }
      break;
    case 't': // sun times / date-time
    case 'n':
      break;
    case 'c': // autoMode
      if (autoMode) {
        message = F("ca");
      } else {
        message = F("cm");
      }
      postToGoogle(message);
      if (autoMode) {
        updateGoogle('e');
        updateGoogle('m');
      }
      break;
    case 'm': // motorInterval
      message = F("mo: ");
      message += String(motorInterval_open);
      message += F("  mc: ");
      message += String(motorInterval_close);
      postToGoogle(message);
      break;
    case 'e': // offsets
      message = F("eo: ");
      message += String(offset_open);
      message += F("  ec: ");
      message += String(offset_close);
      postToGoogle(message);
      break;
    case 'f': // flockStatus
      message = F("flock: ");
      message += String(flockStatus);
      postToGoogle(message);
      break; 
    case 'g': // googleEnabled
      if (googleEnabled) {
        message = F("Hello from the ESP");
      } else {
        message = F("Go away Google");
      }
      postToGoogle(message);
      break;
    case 's': // state
      message = F("state ");
      message += int(state);  
      postToGoogle(message);
      break;
    default:
      break;  
  }
}

All Arduino Code

Firmware for programming the ESP32 in the Arduino IDE.

Credits

Shanel Wu
2 projects • 3 followers
Contact
Thanks to Jefita (R.I.P.) and Jaguar, Griselda (R.I.P.) and Panther, Conchita y Pepita, and Lupita.

Comments

Please log in or sign up to comment.