PLC Based Automatic Bottle Filling System

PLC Based Automatic Bottle Filling System

Introduction

This project is a semi-automatic bottle filling and capping station designed for Cappy 0.5L orange juice bottles. It utilizes 3D-printed parts, Delta PLC, Arduino Nano, and a combination of stepper, DC, and servo motors to automate the filling and capping process.

It was developed as a university automation project to combine industrial PLC logic with affordable DIY components and serve both educational and prototyping purposes.

Working Principle

The system is split into three core modules:

1. Conveyor 1 (Filling Section)

  • The operator places an empty bottle.
  • Sensor 1 detects the bottle and sends a signal to the PLC.
  • The conveyor stops, and the 12V pump is activated for a fixed time to fill the bottle.
  • Once filled, the conveyor resumes, pushing the bottle to the rotating platform.

2. Rotary Platform (Capping Section)

  • The bottle reaches Sensor 2, which triggers the NEMA 17 stepper motor to rotate 180°.
  • While rotating, a cap drops from the cap holder (gravity-fed, with optional upgrade to vacuum pickup).
  • When the bottle aligns under the capping arm, Sensor 3 activates a servo that lowers the 25mm DC motor onto the cap to screw it in.
  • After capping, the rotary platform rotates another 180°, aligning the bottle with Conveyor 2.

3. Conveyor 2 (Exit Station)

  • Moves the capped bottle to the end of the system for collection.
  • System is ready for the next cycle.
💡
NOTE : While 4 sensors are used for full PLC integration, the system can function with just 1 sensor and timed delays if desired.

Components Used

ComponentDescription
PLCDelta DVP14SS2
MicrocontrollerArduino Nano
Servo MotorMG995
Stepper MotorNEMA 17
Stepper DriverTB6600
Capping Motor25mm 12V 50 RPM DC Motor
Water Pump12V DC Pump
IR Sensors4x MZ80 (5V logic)
Conveyor Motors4x 5V DC Motors (2 per conveyor)
Relays4x 5V–24V logic conversion relays
PLC Relays4x 24V output relays
Voltage Regulators12V, 5V, 4V
Limit SwitchFor stepper control
Start/Stop Buttons2 pushbuttons
Sigma Profiles20x20: 440mm x2, 210mm x2
Bearings + Shafts4x 608 2RS + 4x 8mm chrome rods (124mm)
Filament~800g Esun PLA+ (any PLA works)
ScrewsM5x8mm, M5x10mm, M3x35mm
T-slot NutsFor 20x20 profile

3D Models & Printing

All major structural and functional parts are 3D printed:
Conveyor bodies, Motor mounts, Rotary platform, Sensor holders, Cap dispenser and Capping arm mechanism.

SettingValue
Layer Height0.2 mm
Infill30–35%
SupportsYes (on overhangs)
Nozzle0.4 mm
MaterialPLA/PLA+
Filament Use~800 g

Works with other brands or standard PLA as well.

STL files, grouped by part and function, are hosted on Cults3D:

👉 Download All 3D Files

💡
NOTE: The model is dimensioned for Cappy Orange Juice Bottles (Ø54mm, 150mm height, cap Ø35mm). You’ll need to modify the capper and holder if using other bottles.

Wiring Diagram

In this version of the project, all components are connected to the Arduino Nano, and the PLC is not required for operation. The Delta PLC was originally used for academic demonstration, but the full system can function efficiently with only an Arduino. Forget to connect limit switch to pin 10

The arduino Nano integrated with delta plc.

Code

The Arduino code below manages the stepper motor that rotates the bottle 180° during the capping process, and also moves a servo motor that lowers the capping mechanism. It also includes a homing sequence using a limit switch, and reacts to a sensor that detects the presence of a bottle at the capping position.

NOTE: a limit switch need to be connected to pin 10 for step motor homming

#include <AccelStepper.h>
#include <Servo.h>

#define STEP_PIN 6
#define DIR_PIN 7
#define ENA 8
#define SW1 10
#define SENSOR_PIN 3
#define SERVO_PIN 9

AccelStepper stepper1(1, STEP_PIN, DIR_PIN);
Servo capper;

int max_travel = 12000;
long initial_homing = -1;

enum State { HOMING, MOVE_TO_INITIAL, WAIT_BOTTLE, MOVE_STEP_1, MOVE_STEP_2, CAPPING, MOVE_STEP_3 };
State currentState = HOMING;

unsigned long waitStart = 0;
bool waitStarted = false;

void setup() {
  Serial.begin(9600);
  pinMode(SW1, INPUT_PULLUP);
  pinMode(SENSOR_PIN, INPUT);
  pinMode(ENA, OUTPUT);
  digitalWrite(ENA, LOW);

  stepper1.setMaxSpeed(100.0);
  stepper1.setAcceleration(500.0);

  capper.attach(SERVO_PIN);
  capper.write(0);  // Initial position

  delay(1000);
  homing_1();
}

void loop() {
  switch (currentState) {
    case HOMING:
      currentState = MOVE_TO_INITIAL;
      stepper1.moveTo(170);
      break;

    case MOVE_TO_INITIAL:
      stepper1.run();
      if (stepper1.currentPosition() == stepper1.targetPosition()) {
        currentState = WAIT_BOTTLE;
      }
      break;

    case WAIT_BOTTLE:
      if (digitalRead(SENSOR_PIN) == LOW) {
        stepper1.moveTo(stepper1.currentPosition() + 100);
        currentState = MOVE_STEP_1;
      }
      break;

    case MOVE_STEP_1:
      stepper1.run();
      if (stepper1.currentPosition() == stepper1.targetPosition()) {
        stepper1.moveTo(stepper1.currentPosition() + 100);
        currentState = MOVE_STEP_2;
      }
      break;

    case MOVE_STEP_2:
      stepper1.run();
      if (stepper1.currentPosition() == stepper1.targetPosition()) {
        currentState = CAPPING;
        capper.write(90);
        waitStart = millis();
        waitStarted = true;
      }
      break;

    case CAPPING:
      if (waitStarted && millis() - waitStart >= 5000) {
        capper.write(0);
        stepper1.moveTo(stepper1.currentPosition() + 100);
        currentState = MOVE_STEP_3;
        waitStarted = false;
      }
      break;

    case MOVE_STEP_3:
      stepper1.run();
      if (stepper1.currentPosition() == stepper1.targetPosition()) {
        currentState = WAIT_BOTTLE;
      }
      break;
  }
}

void homing_1() {
  initial_homing = -1;
  while (digitalRead(SW1)) {
    stepper1.moveTo(initial_homing--);
    stepper1.run();
  }

  stepper1.setCurrentPosition(0);
  initial_homing = 1;
  while (!digitalRead(SW1)) {
    stepper1.moveTo(initial_homing++);
    stepper1.run();
    delay(5);
  }

  stepper1.setCurrentPosition(0);
}

Gallery

Demo Video

Subscribe to Omar TPX

Sign up now to get access to the library of members-only issues.
Jamie Larson
Subscribe