Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 20:42 22 Nov 2024 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: 5078
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: 855
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: 4219
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: 6764
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: 5078
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: 4219
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: 855
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: 5078
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.

© JAQ Software 2024