Chili Ripeness Detection on a CNC Gantry

Chili Ripeness Detection on a CNC Gantry

A three-axis CNC gantry that rolls over a 2 × 6 m chili bed, photographs all 27 plants and tells you which ones are ready to pick.

Chili prices in Indonesia swing hard (cayenne hit around Rp 120,000/kg in March 2025), yet deciding when a plant is ready still comes down to someone walking the rows and judging which fruit looks red enough. It's slow, it's subjective, and it gets worse the bigger the farm.

This thesis hands that walk to a machine. Inside a smart greenhouse at Universitas Sriwijaya, an aluminium gantry carries a camera over every plant in the bed, a Raspberry Pi runs a YOLOv11 model on each photo, and a web dashboard turns the fruit counts into a harvest call per plant. The gantry, its electronics and firmware, the model and the dashboard were all designed and built from scratch.

Results

0.47 mm

Mean positioning error along the 6 m axis

94.3%

Detection precision in the greenhouse

25 / 27

Harvest calls matching a hand count

11.8 min

To scan all 27 plants

The smart greenhouse from outside
The three planter beds before planting
Fruiting chili plants in the main bed

The  greenhouse

The system lives in an 8 × 10 m greenhouse on the Faculty of Engineering campus in Indralaya: a C75 light-steel frame, a 250-micron UV polyethylene roof, and 50-mesh insect-net walls that let air through but keep pests out. Inside are three raised planter beds of 2 × 6 m, each laid out for 27 plants on a 3 × 9 grid, plus a small control room with 220 V power.

The plants were grown for it too. Kaliber cayenne seeds were sown on 16 December 2025, raised in polybags through the vegetative and flowering stages, and moved into the main bed with their root balls at 164 days old, already carrying fruit at every stage of ripeness.

Three  axes  over  a  2  ×  6  m  bed

The X axis runs the full 6 m of the bed. Two 20×60 aluminium columns ride V-wheels along 20×20 rails fixed to both edges of the bed, and each column has its own NEMA17 (X1 and X2) driving a GT2 belt, so both sides travel together.

Bridging the columns is a 2 m 20×60 cross-slide for the Y axis. A 5 mm steel carriage plate rolls along it on five V-wheels and carries five more on its back face for the Z riser: a 1 m aluminium post driven by an 8 mm lead screw with a 2 mm pitch, so the camera holds its height without slipping when the motor is idle.

X and Y use GT2 belts on 20-tooth pulleys at 1/16 microstepping, which works out to 80 steps per millimetre. Drag chains carry the cables and the irrigation hose along with the carriage, and a limit switch at the end of every axis gives the machine a hard zero. It homes Z first, then Y, then X.

3D design of the gantry over the planter bed
Gantry columns X1 and X2
The Y-axis rail
Gantry column with corner bracket, motor and panel box
Cross-slide carriage with pulley, belt and drag chain
The Z-axis camera riser
Panel box with the ESP32, DRV8825 drivers and relays

Electronics

Raspberry Pi 4

The brain. Runs a FastAPI service that takes session commands from the dashboard, sequences every move, runs the YOLOv11 model and streams video and status to the browser.

ESP32 #1: motion

On the Pi's USB. Drives four DRV8825 stepper drivers (X1, X2, Y, Z), the two MG90S servos of the pan-tilt head, a VL53L1X time-of-flight sensor over I²C, and the relays for the solenoid valve and pump.

ESP32 #2: soil

On the Pi's UART. Reads capacitive soil-moisture probes buried along each third of the bed for the watering logic.

Camera head

A 1080p Logitech C922 on the pan-tilt servos at the top of the Z riser, so the framing can be set for each plant before every shot.

How the Pi, both ESP32s, drivers, servos and sensors connect
ESP32 #1 schematic with four DRV8825 drivers

One  scan,  start  to  finish

A scan starts on the dashboard, which creates the session and hands its ID to the Pi. If another session is already running the Pi refuses with a 409, so two jobs can never fight over the motors. Otherwise it enables the drivers and homes all three axes.

Then it walks the grid. At each of the 27 plants the gantry moves into position, the servos set the camera angle, and the system waits for the frame to stop swaying before it captures. The photo is uploaded to the dashboard and run through YOLOv11 on the Pi; the counts per class go back over HTTP while a Server-Sent Event updates the browser live. A failed move or capture triggers an emergency stop instead of a half-finished run.

After the last plant, any plant with more than five ripe fruits is flagged as ready to harvest, the gantry homes again and the drivers switch off.

It  waters,  too

The same gantry runs a watering pass. It first sweeps the bed with the downward-facing ToF sensor to find the height of the tallest plants, and reads the soil-moisture probes. Both feed a type-2 fuzzy controller that decides how long the solenoid valve should stay open, and the gantry then stops along the bed, opening the valve for that long at each stop.

Motion  tests

0.47 mm

Mean X error over 27 points (max 0.8 mm, tolerance ±5 mm)

0.33 mm

Mean Y error over 27 points (max 0.5 mm)

≤ 0.25 mm

Skew between the X1 and X2 motors along the rail

0.03 mm

Worst homing deviation across 10 runs per axis

Seven  days  in  the  greenhouse

The finished system ran a full scan every afternoon at around 17:00 from 1 to 7 July 2026. Six of the seven sessions covered all 27 plants. On 5 July the network between the Pi and the dashboard dropped after plant 11 and the session stopped; the gantry and the model had been working normally up to that point.

Before scanning, every fruit on every plant was counted by hand. Against that count the system found 863 of 950 fruits with 52 false detections (94.3% precision, 90.8% recall), and its harvest call matched reality on 25 of 27 plants. Both misses were plants sitting right on the five-ripe-fruit threshold.

A full scan takes 709 seconds on average, within a few seconds across three runs. Homing takes 5.3 s, a move to the next row 5.8 s and capture plus inference 4.1 s, but each move across to the next column takes 28.6 s. The bottleneck is moving the frame, not running the model.

One plant as the gantry camera sees it
The same plant with YOLOv11 detections

What's  next

The field run points at three improvements. The Pi should buffer results locally and reconnect on its own, so a network blip pauses a session instead of ending it. The column moves are where the time goes, so they're the place to tune speed and acceleration. And midday sun overexposes the camera and shifts the chili colours, which is why scans ran in the late afternoon; a diffuser on the camera housing would open up the rest of the day.

Built  with

  • Raspberry Pi 4
  • ESP32
  • NEMA17
  • DRV8825
  • VL53L1X
  • MG90S
  • Logitech C922
  • Aluminium T-slot
  • GT2 belt
  • Lead screw
  • Python
  • FastAPI
  • YOLOv11
  • Next.js
  • PostgreSQL

Related  Work