Automated Precision Fish Feeder with WiFi 3D Printer File Image 1
Automated Precision Fish Feeder with WiFi 3D Printer File Image 2
Automated Precision Fish Feeder with WiFi 3D Printer File Image 3
Automated Precision Fish Feeder with WiFi 3D Printer File Image 4
Automated Precision Fish Feeder with WiFi 3D Printer File Image 5
Automated Precision Fish Feeder with WiFi 3D Printer File Image 6
Automated Precision Fish Feeder with WiFi 3D Printer File Image 7
Automated Precision Fish Feeder with WiFi 3D Printer File Image 8
Automated Precision Fish Feeder with WiFi 3D Printer File Image 9
Automated Precision Fish Feeder with WiFi 3D Printer File Image 10
Automated Precision Fish Feeder with WiFi 3D Printer File Thumbnail 1
Automated Precision Fish Feeder with WiFi 3D Printer File Thumbnail 2
Automated Precision Fish Feeder with WiFi 3D Printer File Thumbnail 3
Automated Precision Fish Feeder with WiFi 3D Printer File Thumbnail 4
Automated Precision Fish Feeder with WiFi 3D Printer File Thumbnail 5
Automated Precision Fish Feeder with WiFi 3D Printer File Thumbnail 6
Automated Precision Fish Feeder with WiFi 3D Printer File Thumbnail 7
Automated Precision Fish Feeder with WiFi 3D Printer File Thumbnail 8
Automated Precision Fish Feeder with WiFi 3D Printer File Thumbnail 9
Automated Precision Fish Feeder with WiFi 3D Printer File Thumbnail 10

Automated Precision Fish Feeder with WiFi

Flying Gyroscope avatarFlying Gyroscope

March 24, 2025

printables-icon

Description

Welcome

Previous versions of this feeder have kept my dwarf freshwater shrimp happy and healthy during vacations.  This contest has given me the motivation to finally implement some improvements.  Changing to a servo motor and writing the graphical interface are the most noteworthy changes.

My feeder does not use a cloud service, but you do need an internet connection to synchronize time with an NTP server.  

This feeder works best with foods larger than a millimeter or two: granules, pellets, wafers, sticks, …  Flake and very fine foods will not work well.  I recommend watching how your food is dispensed with several manual feedings.  

Key Benefits

  • Automatically feed while you are away
  • Assign any feeding time, any number of times per day
  • Each feeding gives a precise portion
    • Mix and match foods in different slots
  • Simple control and monitoring through web interface
    • Every function is available through simple button clicks
  • Powered by USB-C
  • Full recovery after a power loss

Which File?

There are folders for large and small feeding trays, then files for each rim thickness.  All of the printed parts for a feeder are bundled into the 3mf file.  They do not contain any print settings, just 3D models.   

  • Large uses 5 slots with a thick tray
    • Stores the most food
    • 4 usable slots + starting slot
    • Use program Code_Large_5_Slot.ino
  • Small uses 7 slots with a thin tray
    • 6 usable slots + starting slot
    • Use program Code_Small_7_Slot.ino
  • 2-13mm fits rimless tanks up to 0.5 inch
    • The small version of this feeder is featured in all the photos
  • 12-27mm fits rimmed tanks from 0.5 inch to 1.0 inch
  • 26-39mm fits rimmed tanks from 1.0 to 1.5 inch

Supplies

Printing

There are no special temperature or strength requirements, so any filament should work.  I made mine with PLA.  I test all my prints with a 0.4mm nozzle, 0.2mm layers, and conservative speeds and accelerations.  

The bottom of the base (in the printed orientation) needs to be flat and smooth.  This means using a smooth surface and having a bed mesh with minimal deviations.  The top of the tray (in the printed orientation) needs to be smooth and flat.  Problems with these two surfaces may result in food getting lost in the gap between the base and tray.  

Assembly

I have three points to make about assembly.  First, the long screw (M3x12mm for the small feeder, M3x25mm for the large feeder) attaches the tray and coupler together. Everything else uses M3x8mm screws.  

Second, the servo and tray position need to be aligned.  Manually rotate the servo clockwise until it stops.  Then take off the servo horn and press it into place with the alignment shown below.  The starting position may not line up exactly, but this is addressed later with calibration.

Third, the microcontroller needs to be inserted so that the buttons and LED face away from the aquarium.  You should be able to press the hardware reset button (with a small pointy object) through a hole in the wall.  If you accidentally use the wrong orientation, you will need to flip it around and re-solder the wires on the microcontroller.  

Wiring

Soldering is going to be the best way to connect everything in the electronics compartment.  I chose components that would make wiring easy - you only need to keep track of three wires. 

  • Orange/Yellow wire = Signal.  Connect servo and A1 (GPIO17).
  • Red wire = Positive.  Connect servo and 5V.
  • Brown/Black wire = Ground.  Connect servo and GND.
  • Capacitor
    • Longer lead = Positive.  Connect to red wire.
    • Shorter lead on the side with a white stripe = Negative.  Connect to black wire.

The feeder is powered through the USB-C port on the microcontroller.  

Code

This is my first internet-of-things program, so I am sure there is room for improvement.  If you want to look through the entire code, I wrote descriptive comments at the start of every function.  To summarize, the program will:

  • Monitor time and status variables
  • Control the servo to feed
  • Store and retrieve status variables and logs
  • Connect to the internet and sync time
  • Run a webserver that parses user clicks
  • Output status and read user input through the Serial Monitor

Remember that the program version must match the feeder size!  There is a simple but very important change to the variable numTray.   

The only part of code that you need to consider and edit is the following near the top:

#define SERVER "us.pool.ntp.org"  // server for syncing time
// https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
#define MYTZ "EST5EDT,M3.2.0,M11.1.0"  // timezone and DST info
const char ssid[] = "wifi_SSID";  // wifi name
const char pass[] = "plain_text_passwd";  // wifi password

Choose the NTP time server for your country, and look up the code for your time zone at the github link: https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv.  Then, enter your wifi name and password inside the quotation marks.   

Program Behavior

In case you do not read code for fun, I can spell out some of the program's behavior.  The slots on the small feeder are labeled 0 through 6, and the slots on the large feeder are labeled 0 through 4.  0 corresponds to the blank starting slot.  

If you need to reset the feeding tray to the starting position, use the graphical interface or Serial Monitor.  Restarting the microcontroller does not reset the tray.  This is important for power recovery because it makes sure that the feeder can return to the correct slot after booting up.  The reset button on the back of the feeder (next to the status LED) forces a hardware restart but does not reset the tray position. 

Feeding times are set by the hour, and feeding occurs at the top of the hour.  If the entire hour passes without power, no feeding occurs.  The automated program does not try to make up for a skipped feeding once power returns.  It is stressful on your aquarium ecosystem when airstones and filters lose power.  Unless you have baby fry, it is very safe to skip one feeding.  

When the feeder advances the tray, it wiggles back and forth another time to make sure all food is dislodged.  If a specific feeding time has multiple entries, feeding will only occur once.

During startup, the program will wait for a successful wifi connection and time sync before continuing.  This is necessary because the feeder does not have a real-time-clock (RTC) module and needs to check the internet for time.  I skipped the RTC to keep cost down and simplify wiring.  Plus, the wifi chip enables the wireless webserver. 

If there is an internet (time sync) error or wifi error after the initial startup, the program will try to reconnect and resync for one minute.  If it cannot, the program will continue with other code and try again on the next main loop.  The internal timers on a microcontroller are susceptible to drift, but only by seconds after a whole day.  The feeder can safely function even with these errors.

Program the Microcontroller

If you are new to Aduino, try running the blink program by following the Adafruit learning guide: https://learn.adafruit.com/adafruit-qt-py-esp32-s2/arduino-neopixel-blink.  This was my first program as a beginner.  Do note that the user interface has gotten a facelift since that guide was published.  

Use Arduino IDE to upload the code.  The default installation will not have everything you need.  Add https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json to the additional boards manager.  This updates the list of available boards.  Next, use the board manager to install ESP32 by Espressif Systems.  

Finally, use the library manager to add ESP32Servo by Kevin Harrington, John K. Bennett and Adafruit NeoPixel by Adafruit.  Now you can compile and upload.  

First Startup

The LED should briefly show red and blue during startup.  Start the Serial Monitor at 115200 baud rate and wait up to 20 seconds for a status update.  (Or, press “Enter” with a blank line to force an update.)  Verify the time, and note the IP address.  You should see output like the text below.  After the first startup you will not need to use the Serial Monitor - all functions are available through the graphical interface.  

============= User Config ================
1.  Servo calibration
2.  Set feeding time
3.  Manual feed
4.  Reset feeder
5.  Reset feeder (and skip today)
6.  Skip all feeding today
============= Program Update =============
SSID: XXX
IP Address: XXX.XXX.XXX.XXX
Signal strength (RSSI): -49 dBm
Startup time:  2025  Mar 8  12:37:24
Current time:  2025  Mar 8  13:3:39
Last manual reset:  2025  Mar 8  12:32:13
============== Tray Status ==============
Current tray position:  2
Feeding log ...
Slot 1: 2025  Mar 8  13:0:0  (Automated Feed)
Slot 2: 2025  Mar 8  13:3:0  (Manual Feed)
Slot 3: 
Slot 4: 
Slot 5: 
Slot 6:  

Calibration

After your feeder has booted up for the first time, you need to calibrate the servo angles so that each slot lines up correctly over the hole.  Calibration is saved to memory so you only need to do it once.  Cheap servos are not precision devices and may not yield 180° of rotation.  Since I am unfamiliar with tuning pulse widths, I modeled for 175° of rotation in CAD and developed this calibration routine.  

Unless you love command lines, I recommend using the calibration buttons on the graphical interface.  The values represent offsets from the mathematically calculated value.  Decrease the value for less rotation, and increase the value for more rotation.  (More rotation means moving farther away from the starting slot).  Click the “Test” button to move to that slot and test the current alignment.  

The program will move the tray to an adjacent slot (if necessary) before testing the new offset value.  During testing, I ran into an issue where the position would not properly update with small changes.

Start with the first slot.  Try assigning a small (2-5) or zero offset to the first slot, and then adjust the screw on top of the feeder tray to finalize the alignment.  After the first slot is calibrated and the adjustment screw is tightened, you can then align the other slots.  

Web Interface

Connect to the same wifi as the feeder, then type the feeder's IP address into a web browser.  You can find the IP address by checking your router, or by looking at a status update through the Serial Monitor.

Look at the screenshot in the picture gallery to see available commands and logs.  Be careful with the refresh button!  If you refresh with the web browser button, a duplicate referral link will get processed, and the last button click will run again.  Instead, use the refresh button inside the webpage to see updated results.  The graphical interface will auto-refresh after button clicks.

Resetting Memory

Reprogramming the microcontroller does not erase the flash memory.  Run this short program to erase memory:

#include <nvs_flash.h>
void setup() {
  nvs_flash_erase(); // erase the NVS partition and...
  nvs_flash_init(); // initialize the NVS partition.
  while(true);
}
void loop() {}

Thanks for visiting, and enjoy your aquariums!

Minor update:  Cleaned up the code a little.  Added startup time and reset logging.  Serial Monitor updates print out more information.  

Minor update:  Updated code to add calibration to the graphical interface.  Every function is now on the gui.  

Major update:  Added a large size that uses 5 slots and a thicker tray, making room for more and larger food.

Major update:  Corrected the number of trays in each code, and added some clarifications in the description.  Thanks EC.