Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 08:48 14 Jul 2025 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Quick hack of a cat feeder. ESP01

Author Message
Gizmo

Admin Group

Joined: 05/06/2004
Location: Australia
Posts: 5116
Posted: 05:26am 16 Oct 2024
Copy link to clipboard 
Print this post

Occasionally I need to go away for a day or two. The dog goes to the doggy hotel for a couple nights, and the cat stays at home. Usually I put down enough cat food for the duration, but he eats it all in one go, then finds somewhere on the carpet to vomit it all up.

I was given a broken cat feeder. It has multiple issues, and instead of trying to fix it, I decided to hack it with a arduino.



Opened it up and removed the main control board. The feeder uses a motor to spin a paddle wheel to basically throw out some cat food. I used a ESP01 plus relay board as the brains, mounted on a piece of veroboard with a couple caps, power socket, etc. Its powered by a 9v adaptor.



Spent a couple hours with ChatGPT to come up with the basic code, then modified it to add a few features. It connects to my wifi, and serves a web page to let you configure the feeder. I have two feeding times, and a duration to spin the motor for. It connects to a NTP server to get the actual time. Settings are saved in EEPROM.

The cats name is Alf




#include <EEPROM.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <time.h>  // NTP time library

// EEPROM Addresses for storing data
const int EEPROM_SIZE = 10;  // Enough space for 4 integers (start hour, start minute, start second, and duration)

// WiFi credentials
const char* ssid = "Removed";
const char* password = "Removed";

IPAddress local_IP(192, 168, 178, 50);  // Set your desired IP address here
IPAddress gateway(192, 168, 178, 1);     // Set your gateway (usually the router's IP)
IPAddress subnet(255, 255, 255, 0);    // Subnet mask
IPAddress dns(192, 168, 178, 1);  

// NTP Server
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 36000;    // Set to your time zone's GMT offset (in seconds)
const int daylightOffset_sec = 0; // Set to 0 if no daylight savings

// Pin configuration
const int relayPin = 0;  // GPIO0 for relay control

// Web server running on port 80
ESP8266WebServer server(80);

// Variables to store relay control time and duration
int startHour1 = 0, startMinute1 = 0, startSecond = 0;
int startHour2 = 0, startMinute2 = 0;
int durationSeconds = 5;
bool relayOn = false;
bool manualTest = false;  // Flag for manual test

void setup() {
 Serial.begin(115200);
 pinMode(relayPin, OUTPUT);
 digitalWrite(relayPin, HIGH);  // Relay off by default
 
 EEPROM.begin(EEPROM_SIZE);
 loadTimerSettings();  // Load saved timer settings from EEPROM

 // Connect to WiFi
 if (!WiFi.config(local_IP, gateway, subnet, dns)) {
   Serial.println("Failed to configure Static IP");
 }

 WiFi.begin(ssid, password);
 WiFi.hostname("CatFeeder");
 Serial.print("Connecting to WiFi");
 while (WiFi.status() != WL_CONNECTED) {
   delay(500);
   Serial.print(".");
 }
 Serial.println("\nConnected to WiFi!");
 Serial.print("IP address: ");
 Serial.println(WiFi.localIP());
 Serial.print("Hostname: ");
 Serial.println(WiFi.hostname());

 // Initialize NTP
 configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

 server.on("/", handleRoot);
 server.on("/setTime", handleSetTime);
 server.on("/manualTest", handleManualTest);  // Manual test button
 
 server.begin();
 Serial.println("Web server started.");
}

void loop() {
 server.handleClient();

 // Get the current time
 time_t now = time(nullptr);
 struct tm* currentTime = localtime(&now);

 int currentHour = currentTime->tm_hour;
 int currentMinute = currentTime->tm_min;
 int currentSecond = currentTime->tm_sec;

 // Check if the relay should be on
 if (((currentHour == startHour1 && currentMinute == startMinute1 && currentSecond == 0) || (currentHour == startHour2 && currentMinute == startMinute2 && currentSecond == 0)) && !relayOn && !manualTest) {
   digitalWrite(relayPin, LOW);  // Turn on the relay
   relayOn = true;
   startSecond=currentSecond;
   Serial.println("Relay turned on!");
 }
 
 // Turn off the relay after the specified duration
 if (relayOn && (currentSecond - startSecond) >= durationSeconds ) {
   digitalWrite(relayPin, HIGH);  // Turn off the relay
   relayOn = false;
   manualTest = false;  // Reset manual test flag after operation
   Serial.println("Relay turned off!");
 }
}

void loadTimerSettings() {
 startHour1 = EEPROM.read(0);
 startMinute1 = EEPROM.read(1);
 startHour2 = EEPROM.read(2);
 startMinute2 = EEPROM.read(3);
 durationSeconds = EEPROM.read(4) | (EEPROM.read(5) << 8);  // Combine the two bytes of duration
 Serial.println("Timer settings loaded from EEPROM.");
}

void saveTimerSettings() {
 EEPROM.write(0, startHour1);
 EEPROM.write(1, startMinute1);
 EEPROM.write(2, startHour2);
 EEPROM.write(3, startMinute2);
 EEPROM.write(4, durationSeconds & 0xFF);         // Lower byte of duration
 EEPROM.write(5, (durationSeconds >> 8) & 0xFF);  // Higher byte of duration
 EEPROM.commit();
 Serial.println("Timer settings saved to EEPROM.");
}

void handleRoot() {
 time_t now = time(nullptr);
 struct tm* currentTime = localtime(&now);

 // Format current time as HH:MM:SS
 String currentTimeStr = String(currentTime->tm_hour) + ":" +
                         String(currentTime->tm_min) + ":" +
                         String(currentTime->tm_sec);

 // Format scheduled time for the relay as HH:MM:SS
 String scheduledTimeStr1 = String(startHour1) + ":" + String(startMinute1) + ":00";
 String scheduledTimeStr2 = String(startHour2) + ":" + String(startMinute2) + ":00";

 // HTML content
 String html = "<html><body><h1>Alf Feeder</h1><hr/>";
 html += "<p>Current Time: " + currentTimeStr + "</p>";
 html += "<p>Feed Scheduled Time 1 : " + scheduledTimeStr1 + "</p>";
 html += "<p>Feed Scheduled Time 2 : " + scheduledTimeStr2 + "</p>";
 html += "<p>Relay Duration: " + String(durationSeconds) + " seconds</p>";
 
 html += "<fieldset><legend>Settings</legend><form action=\"/setTime\" method=\"GET\">";
 html += "Feed Time 1: <input type=\"number\" name=\"hour1\" min=\"0\" max=\"23\" value=\"" + String(startHour1) +"\">H : <input type=\"number\" name=\"minute1\" min=\"0\" max=\"59\" value=\"" + String(startMinute1) +"\">M<br>";
 html += "Feed Time 2: <input type=\"number\" name=\"hour2\" min=\"0\" max=\"23\" value=\"" + String(startHour2) +"\">H : <input type=\"number\" name=\"minute2\" min=\"0\" max=\"59\" value=\"" + String(startMinute2) +"\">M<br>";
 html += "Duration (seconds): <input type=\"number\" name=\"duration\" min=\"0\" max=\"3600\" value=\"" + String(durationSeconds) + "\"><br>";
 html += "<input type=\"submit\" value=\"Set Time\">";
 html += "</form></fieldset>";
 
 html += "<form action=\"/manualTest\" method=\"GET\">";
 html += "<input type=\"submit\" value=\"Test Relay\">";
 html += "</form>";
 
 html += "</body></html>";
 server.send(200, "text/html", html);
}
// Handle setTime page
void handleSetTime() {
 if (server.hasArg("hour1") && server.hasArg("minute1") && server.hasArg("hour2") && server.hasArg("minute2")  && server.hasArg("duration")) {
   startHour1 = server.arg("hour1").toInt();
   startMinute1 = server.arg("minute1").toInt();
   
   startHour2 = server.arg("hour2").toInt();
   startMinute2 = server.arg("minute2").toInt();
   durationSeconds = server.arg("duration").toInt();
   saveTimerSettings();
   server.send(200, "text/html", "Time set! Go <a href=\"/\">back</a>.");
   Serial.print("Time 1 set to: ");
   Serial.print(startHour1);
   Serial.print(":");
   Serial.print(startMinute1);
   Serial.print(" Time 2 set to: ");
   Serial.print(startHour2);
   Serial.print(":");
   Serial.print(startMinute2);


   Serial.print(" for ");
   Serial.print(durationSeconds);
   Serial.println(" seconds.");
 } else {
   server.send(400, "text/html", "Invalid request.");
 }
}

// Handle manual test button
void handleManualTest() {
 if (!relayOn) {
   digitalWrite(relayPin, LOW);  // Turn on the relay
   relayOn = true;
   manualTest = true;  // Set manual test flag
   startSecond = second();
    // Start the manual timer
   server.send(200, "text/html", "Manual test activated! Relay on for set duration. Go <a href=\"/\">back</a>.");
   Serial.println("Manual test: Relay turned on!");
 } else {
   server.send(200, "text/html", "Relay already active. Go <a href=\"/\">back</a>.");
 }
}

int second() {
 time_t now = time(nullptr);
 struct tm* currentTime = localtime(&now);
 return currentTime->tm_sec;
}




The best time to plant a tree was twenty years ago, the second best time is right now.
JAQ
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 1362
Posted: 05:34am 16 Oct 2024
Copy link to clipboard 
Print this post

A couple of hours?!?!?!    
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5064
Posted: 07:06am 16 Oct 2024
Copy link to clipboard 
Print this post

Nice work Gizmo !!

I am going to study your C code. I think this could also be a nice project for a WebMite (I think it has the commands/functions to do this).
But this (total cost around 3-4 dollar ?) is cheaper than the Webmite alone....

Volhout
PicomiteVGA PETSCII ROBOTS
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7876
Posted: 07:40am 16 Oct 2024
Copy link to clipboard 
Print this post

That's a really cool project. :)
Actually, I think it's the first time I've seen the 01 used in a "proper" project rather than just a breadboard demo.




Going off on one of my rambles....

I've just been playing with a node MCU board, which uses the ESP-12E (a modern version with more IO). I loaded it with ESP BASIC as I just couldn't figure out node MCU and I hate C. It's interesting, to say the least. ESP BASIC has some odd quirks and quite a few limitations but it's very usable. Some bits are genius:

a = pin(pi, 7) will read the state of  GPIO7
pin(po, 7) = 1 will set GPIO7 to 1
pin(pw, 7) = 50 will set PWM on GPIO7 to 50
a = pin(ai) will read the (only) analogue input

No setpin needed, it's done on the fly.

Quotes in strings can be | or " so print "I said |Hello|" prints
I said "Hello"
as does |I said "Hello"|

Unfortunately you have to write "print" in full. Arrays are only single-dimensional.

All programming is via a web browser. There can be a serial interface to the chip but it's not needed - and I don't think you can load programs through it anyway. It's quite a nice IDE in many ways.

It will run on a ESP8266-01S but the real-time debugger and variables dump are omitted. I'm not sure if he's still developing for that platform now though.
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
Gizmo

Admin Group

Joined: 05/06/2004
Location: Australia
Posts: 5116
Posted: 08:14am 16 Oct 2024
Copy link to clipboard 
Print this post

  PhenixRising said  A couple of hours?!?!?!    


Yeah I've learned to embrace ChatGPT for stuff like this. I just ask a series of questions, it generates code, then I ask it to add another feature, it adds the code, etc. It does the bulk of the work, then I go and debug it, make modifications, etc.

In this case, its code was for only one event a day, I added extra code for 2 events. Its EEPROM code was wrong, fixed that. I also tidied up the HTML page a little, added the test relay button and code. And lastly it was pulling the relay output high, but it needed to be low, to activate the relay.

If I had to do everything from scratch, it would be a couple days at least.

Glenn
The best time to plant a tree was twenty years ago, the second best time is right now.
JAQ
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5064
Posted: 08:20am 16 Oct 2024
Copy link to clipboard 
Print this post

Hi Gizmo,

Are you using a Fritzbox router ? 192.168.178.xx

Volhout
PicomiteVGA PETSCII ROBOTS
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 1362
Posted: 11:30am 16 Oct 2024
Copy link to clipboard 
Print this post

  Gizmo said  
  PhenixRising said  A couple of hours?!?!?!    


Yeah I've learned to embrace ChatGPT for stuff like this. I just ask a series of questions, it generates code, then I ask it to add another feature, it adds the code, etc. It does the bulk of the work, then I go and debug it, make modifications, etc.

In this case, its code was for only one event a day, I added extra code for 2 events. Its EEPROM code was wrong, fixed that. I also tidied up the HTML page a little, added the test relay button and code. And lastly it was pulling the relay output high, but it needed to be low, to activate the relay.

If I had to do everything from scratch, it would be a couple days at least.

Glenn


Same here but with the ESP32. I don't have a ChatGPT subscription and so it turns out that there is a daily allowance. I switched to Copilot and there's no limit when it comes to code-assist.
I have since realised that it only pulls-in sample code from the web because I have found the exact same stuff...but it's certainly quick.

Got my knickers in a twist because my fresh install was the latest Core 3+ Arduino and a few things, such as timer-related functions have changed and are no-longer compatible. Switched back to Core 2+ and everything is fine.
Now I have learned enough that I can switch back to Core 3+.

The problem was that; ChatGPT and Copilot were grabbing the Core 2+ stuff and I just happened to be needing the timer functions.

I am racing through this stuff with surprising ease and can now come-up with peripheral for the Mites. I now have the ESP32 as my Bluetooth device, connected to a PicoMite and it's rock-solid. Don't really need WiFi yet but that and Ethernet seem pretty straightforward, as does ESP-NOW.

Fun, fun, fun  
 
Gizmo

Admin Group

Joined: 05/06/2004
Location: Australia
Posts: 5116
Posted: 11:34am 16 Oct 2024
Copy link to clipboard 
Print this post

  Volhout said  Hi Gizmo,

Are you using a Fritzbox router ? 192.168.178.xx

Volhout


I did once. When I bought a new router, I decided to keep the same number scheme.

Glenn
The best time to plant a tree was twenty years ago, the second best time is right now.
JAQ
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025