System Integration

Assignment:

  • Design and document the system integration for your final project

#.Assignment:

System Integration Documentation: Linear Autofeeder Project

For my final project, I set out to design and document the system integration of a Linear Autofeeder. The goal was to create a feeder that moves along a rail, stops at intervals to dispense food, and continues its path, repeating the process automatically based on user input.

1. MY Approach

I broke the project down into three main parts to simplify development and ensure successful integration:

  • Electronics: Power and control the system with necessary sensors and actuators.
  • Mechanical: Provide structure, movement capability, and a reliable feeding mechanism.
  • Programming: Orchestrate the system logic control motion, feeding cycles, and user interaction.

Project Goals

  • Build a reliable system that moves accurately along a rail.
  • Add a controlled food dispensing mechanism using an auger.
  • Include a simple interface (rotary encoder + LCD) for manual adjustments.
  • Ensure smooth communication between all components.

2. System Overview

The system is divided into three main parts:

  1. Electronics
    • Microcontroller (XIAO ESP32C3): The brain of the system, handling all decisions and communication.
    • Stepper Motors (NEMA 17):
      • Motor 1: Moves the feeder along the rail.
      • Motor 2: Turns the auger to dispense food.
    • Motor Drivers (TB6600): Help the microcontroller control the motors with precision.
    • Sensors & Interfaces:
      • Limit Switch: Stops the system at the end of the rail.
      • Rotary Encoder: Lets users change settings easily.
      • 20Γ—4 LCD Display: Shows status and settings.
    • Power Regulation:
      • AMS1117 Regulators: Keep voltages stable and safe for components.
  2. Mechanical Components
    • Rail & Carriage:
      • Aluminum Rail: Provides a stable path for movement.
      • 3D-Printed Carriage: Carries the feeder mechanism.
    • Feeding Mechanism:
      • Hopper: Holds the food before it's dispensed.
      • Auger (3D-Printed): Rotates to release food in measured portions.
    • Enclosure: Keeps everything protected and organized.
  3. Firmware (Software)
    • Motor Movement:
      • Tracks steps for precise motion.
    • User Interface:
      • Rotary encoder input is handled using interrupts for a fast response.
      • A menu appears on the LCD for setting feed intervals and speed.
    • Safety Features:
      • Software endstops to avoid going past the track.
      • Emergency stop using the encoder button.

3. Designs of the project

A. Electronics

To bring the Linear Autofeeder to life, I designed an electronic system that controls the motors, reads user input, and displays information. The main components include a microcontroller, stepper motors, drivers, sensors, a display, and voltage regulators.

image

I started by reviewing the pinout of the XIAO ESP32C3, which I chose as the main microcontroller for the project. This helped me plan how each component would connect and ensured I used the available pins efficiently.

image

Next, I selected the key components for the project, including two NEMA 17 stepper motors with their TB6600 drivers, an LCD display, a rotary encoder, and a limit switch. These components were chosen to handle movement, user input, and safety functions within the system.

image

After selecting the components, I created a schematic in KiCad to show how everything would be connected. I also added power sources with different voltage levels: 12V for the stepper drivers, 5V for the encoder, and 3.3V for the XIAO ESP32C3. To manage this, I used AMS1117 voltage regulators to step down the voltages as needed for each component.

image

In KiCad PCB Editor, I routed the connections between components based on the schematic and arranged them to fit neatly on the board. After finalizing the layout, I generated the design files to create a custom PCB for my project.

image

Here is the PCB layout view, showing the final arrangement and routing of all components before fabrication.

image

After completing the PCB design, I generated SVG files for the board. Since I was using a Roland MonoFab PCB milling machine, I needed to convert the design into compatible G-code. To do this, I used ModsProject, which allowed me to generate the toolpaths needed for milling.

image

I then opened the generated G-code in VPanel, the control software for the Roland MonoFab. After setting the origin and securing the copper board, I started the PCB milling process.

The video shows the Roland MonoFab milling the PCB, accurately carving the traces from the copper board.

image

B. Mechanical Components

I designed the mechanical parts to support the feeder’s movement and food dispensing. This includes an aluminum rail for smooth travel, a 3D-printed carriage to hold the mechanism, a hopper to store the food, a 3D-printed auger to release it, and an enclosure to protect everything. I aimed to use as many digital fabrication machines as possible from what we learned during Fab Academy. For the rail supports (legs), I used the CNC machine; for the auger and carriage, I used the 3D printer; and for the enclosure, I used the laser cutter.

image

To visualize the complete system, I designed the entire project in SolidWorks, including all mechanical and structural parts. This helped me check how everything would fit together. This is an exploded view of the assembly, showing all the components clearly.

image

The assembly

image

For the legs, I used the CNC machine to cut the parts from MDF, as shown in the picture above. This provided strong and stable support for the rail system.

image

After cutting, I sanded the CNC parts to make the surfaces smooth and ready for assembly.

image

I then assembled the CNC-cut legs with the aluminum rail tube, forming the main support structure for the feeder.

image

For the auger, hopper, and carriage, I saved the designs as STL files, sliced them, and used the 3D printer to fabricate each part. The video above shows the hopper being printed during the process.

For the enclosure, I saved the designed parts as DXF files and used the laser cutter to cut them from 5 mm MDF. The process is shown in the video above.

C. Firmware (Software)

The final part of the project was writing the firmware, which was the most challenging for me due to my limited coding experience. To make the logic easier to understand, I created a flowchart of the system using Mermaid. For debugging and troubleshooting, I relied on AI tools for guidance and support throughout the process.

image

image

As shown in the flowcharts above, I mapped out the logic of the system step by step to guide the firmware development and make the structure easier to follow.

For the coding, I used the Arduino IDE to write and upload the firmware. The full code is shown below.

πŸ“„ Arduino Code for the Linear Autofeeder Project


#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <AccelStepper.h>

// LCD Configuration
LiquidCrystal_I2C lcd(0x27, 20, 4); // I2C address 0x27, 20x4 LCD

// Pin Definitions - TESTED AND WORKING
#define RAIL_STEP_PIN D6    // for feeder linear position motor
#define RAIL_DIR_PIN D7     // for feeder linear position motor
#define FEEDER_STEP_PIN D10  // for food extraction motor
#define FEEDER_DIR_PIN D8    // for food extraction motor
#define ENCODER_CLK D2
#define ENCODER_DT D1
#define ENCODER_SW D3
#define ENDSTOP_PIN D0      // for homing limit switch (pulled up)

// Stepper Motor Configuration
AccelStepper railStepper(AccelStepper::DRIVER, RAIL_STEP_PIN, RAIL_DIR_PIN);
AccelStepper feederStepper(AccelStepper::DRIVER, FEEDER_STEP_PIN, FEEDER_DIR_PIN);

// Motor Parameters - SIMPLIFIED 200 SPEED FOR ALL
#define MOTOR_SPEED 200           // All motors: 200 steps/sec
#define MOTOR_ACCELERATION 100    // Acceleration for rail motor
#define STEPS_PER_MM 80           // Steps per millimeter for rail motor
#define FEEDER_FEED_SPEED 200      // Feeder: 200 steps/sec (simplified, reliable)
#define STEPS_PER_REVOLUTION 800  // Both motors: 800 steps = 1 full revolution

// System Constants - UPDATED FOR 800 STEPS/REV
#define MAX_POSITIONS 3
#define MIN_FEED_TIME 1
#define MAX_FEED_TIME 60
#define DEBOUNCE_DELAY 50
#define LONG_PRESS_TIME 2000
#define MAX_RAIL_POSITION 8000    // Increased for 800 steps/rev (was 3000)
#define HOMING_SPEED 100          // Slower homing speed for safety
#define HOMING_TIMEOUT 30000      // Homing timeout in milliseconds

// EEPROM Addresses
#define EEPROM_POSITIONS 0
#define EEPROM_FEED_TIMES 20
#define EEPROM_SETTINGS_SAVED 50
#define EEPROM_HOME_POSITION 60

// System Variables
volatile int encoderPos = 0;
volatile bool encoderChanged = false;
int lastEncoderPos = 0;
bool lastButtonState = HIGH;
unsigned long buttonPressTime = 0;
unsigned long lastDebounceTime = 0;

// Homing Variables
bool isHomed = false;
long homePosition = 0;
bool homingInProgress = false;
unsigned long homingStartTime = 0;

// System State
enum SystemState {
  STARTUP,
  HOMING,
  MAIN_MENU,
  SET_POSITIONS,
  SET_FEED_TIMES,
  FEEDING_MODE,
  RUNNING_CYCLE,
  MANUAL_CONTROL
};

SystemState currentState = STARTUP;
int menuIndex = 0;
int editingPosition = 0;
bool isEditing = false;

// Feeding System Variables - UPDATED DEFAULT POSITIONS
long feedingPositions[MAX_POSITIONS] = {1600, 4000, 6400}; // Updated for 800 steps/rev
int feedingTimes[MAX_POSITIONS] = {5, 5, 5}; // Default feed times in seconds
int currentFeedingPosition = 0;
bool feedingActive = false;
unsigned long feederStartTime = 0;
bool feederRunning = false;
bool railMoving = false;

// Menu Structure
const char* mainMenuItems[] = {
  "1.Start Feeding",
  "2.Set Positions", 
  "3.Set Feed Times",
  "4.Manual Control",
  "5.Home System"
};

// Function declarations
void loadSettings();
void saveSettings();
void encoderISR();
void handleEncoder();
void handleButton();
void handleShortPress();
void handleLongPress();
void startHoming();
void handleHoming();
void displayMainMenu();
void selectMenuItem();
void displayHomingRequired();
void displaySetPositions();
void displaySetFeedTimes();
void displayFeedingMode();
void displayManualControl();
void displaySystemInfo();
void handleManualControl();
void handleManualMovement(int delta);
void toggleFeeder();
void startFeedingCycle();
void handleRunningCycle();
void displayFeedingCountdown();
void moveToFeedingPosition(long targetPosition);
void startFeeder();
void stopFeeder();
void stopAllMotors();
void stopFeeding();
void completeFeedingCycle();

void setup() {
  // Initialize serial FIRST
  Serial.begin(9600);
  delay(1000); // Give serial time to initialize
  Serial.println("Animal Feed System Starting...");
  
  // Initialize LCD
  lcd.init();
  lcd.backlight();
  lcd.clear();
  
  // Initialize pins AFTER serial is stable
  pinMode(ENCODER_CLK, INPUT_PULLUP);
  pinMode(ENCODER_DT, INPUT_PULLUP);
  pinMode(ENCODER_SW, INPUT_PULLUP);
  pinMode(ENDSTOP_PIN, INPUT_PULLUP);
  
  // Initialize motor pins
  pinMode(RAIL_STEP_PIN, OUTPUT);
  pinMode(RAIL_DIR_PIN, OUTPUT);
  pinMode(FEEDER_STEP_PIN, OUTPUT);
  pinMode(FEEDER_DIR_PIN, OUTPUT);
  
  // Set initial states - ALL LOW
  digitalWrite(RAIL_STEP_PIN, LOW);
  digitalWrite(RAIL_DIR_PIN, LOW);
  digitalWrite(FEEDER_STEP_PIN, LOW);
  digitalWrite(FEEDER_DIR_PIN, LOW);
  
  // Attach interrupts for encoder
  attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), encoderISR, FALLING);
  
  // Initialize stepper motors - ALL 200 SPEED
  railStepper.setMaxSpeed(MOTOR_SPEED);      // 200 max
  railStepper.setAcceleration(MOTOR_ACCELERATION);
  
  feederStepper.setMaxSpeed(FEEDER_FEED_SPEED); // 200 max  
  feederStepper.setAcceleration(MOTOR_ACCELERATION);
  
  Serial.println("Motors configured - ALL at 200 steps/sec");
  
  // Load settings from EEPROM
  loadSettings();
  
  // Display welcome message
  lcd.setCursor(0, 0);
  lcd.print("Animal Feed System");
  lcd.setCursor(0, 1);
  lcd.print("SIMPLIFIED - 200sps");
  lcd.setCursor(0, 2);
  lcd.print("Focus: MOTOR SPIN");
  lcd.setCursor(0, 3);
  lcd.print("Initializing...");
  delay(3000);
  
  // Start homing sequence
  currentState = HOMING;
  startHoming();
  
  Serial.println("Setup complete - Starting homing");
}

void loop() {

  handleEncoder();
  handleButton();

  //...
               

To read the Full Arduino Code

⬇️ Download Full Arduino Code

Linear Autofeeder Quick Start Guide

What is the Linear Autofeeder?

The Linear Autofeeder is an automatic feeding system that travels along a rail to feed your animals at multiple locations. Think of it as a smart robot that moves food where it's needed, when it's needed without you having to be there.

What it does:

  • Moves automatically to 3 different feeding locations
  • Dispenses precise amounts of food at each spot
  • Runs on a schedule you set up once
  • Remembers your settings even after power outages
  • Works reliably day after day with minimal maintenance

How the System Works

The Simple Process:

  1. Homing: System finds its starting position automatically
  2. Programming: You set where to feed and for how long
  3. Automatic Operation: System runs feeding cycles on its own
  4. Repeat: Continues reliably until you change settings

  • Rail System: The track that the feeder travels along
  • Control Box: LCD screen and dial for programming
  • Motors: Move the system and dispense food
  • Sensors: Know when system reaches each position

Quick Reference

Dial Actions Control Dial (Encoder):

Action What It Does
TURN Navigate menus, adjust values, move system
PRESS Select option, confirm choice, toggle feeder
HOLD Go back, exit to main menu, emergency stop

Screen Symbols:

Symbol Meaning
> Currently selected menu item
[5s] Value being edited (brackets)
H:OK System is homed and ready
H:NO System needs homing first
LIVE System moving in real-time
MOVE Motor currently running
STOP Motor has stopped

Common Screens:

  1. Main Menu = Choose what to do
  2. Set Positions = Program where to feed
  3. Set Feed Times = Program how long to feed
  4. Feeding Mode = Ready to start cycle
  5. Manual Control = Test system manually

Complete Setup Sequence

Your First Time Setup:

Step 1: Power On
  • System automatically homes
  • Wait for main menu
Step 2: Set Position 1
  1. TURN β†’ Select "2.Set Positions"
  2. PRESS β†’ Enter position setup
  3. PRESS β†’ Start editing Pos 1
  4. TURN β†’ Watch system move to desired location
  5. PRESS β†’ Save position
Step 3: Set Position 2
  1. TURN β†’ Select Pos 2
  2. PRESS β†’ Start editing
  3. TURN β†’ Move to second feeding location
  4. PRESS β†’ Save position
Step 4: Set Position 3
  1. TURN β†’ Select Pos 3
  2. PRESS β†’ Start editing
  3. TURN β†’ Move to third feeding location
  4. PRESS β†’ Save position
  5. HOLD β†’ Return to main menu
Step 5: Set Feed Times
  1. TURN β†’ Select "3.Set Feed Times"
  2. PRESS β†’ Enter time setup
  3. For each position:
    • PRESS β†’ Start editing
    • TURN β†’ Adjust seconds (5-15 is good to start)
    • PRESS β†’ Save time
  4. HOLD β†’ Return to main menu
Step 6: Start Feeding
  1. TURN β†’ Select "1.Start Feeding"
  2. PRESS β†’ Enter feeding mode
  3. PRESS β†’ Start automatic cycle
  4. Watch it work!

Daily Operation

To Feed Your Animals:

  1. TURN dial β†’ Select "Start Feeding"
  2. PRESS dial β†’ Enter feeding mode
  3. PRESS dial β†’ Begin cycle
  4. Walk away β†’ System runs automatically

Emergency Stop:

  • PRESS dial anytime during feeding = Immediate stop
  • HOLD dial = Return to main menu

Testing

Before assembling all the parts, I tested the code separately to ensure each function worked correctly. The video above shows the code testing process in action.

After testing the code, I assembled all the mechanical and electronic parts to see how the complete system would work together. I was pleased to find that the Linear Autofeeder functioned successfully as planned.

Design files