Course: EDNS 151 Cornerstone Design (Fall 2025)
Project: Hardware inventory management system for small to medium shops
Goal: Reduce waste and reordering by automatically identifying, organizing, and tracking hardware
I built the backend around two JSON databases: one to track inventory by bin and another to store part metadata (SKU, name, weight). A set of helper functions updates these files based on sensor inputs and user actions.
For hardware interfacing, I wrote the code to read load cell data, drive the motor, and capture frames from the OV9281 camera. The vision system uses OpenCV with a CNN-based approach to compare training images to live frames for part identification.
I helped design the electrical integration between subsystems, ensuring the Raspberry Pi, motor, load cells, and camera were powered correctly and wired for reliable data transfer. This wiring tied the physical ID station and storage bins into the software stack so inventory updates could flow to the web interface.
Physical layout of the identification station and storage bins.
I validated each software component independently: load cell readings via rolling averages, motor control through forward/reverse test scripts, and live camera streaming on the Pi. At the time of the report, the camera interface and data pipeline were functional and the CNN model was the remaining development task before full system integration.
Representative snippets from the Raspberry Pi CV stack used in the project:
Load cell trimmed-mean filtering (HX711):
def read_raw(name, samples=10, timeout=1.0):
setup()
if name not in SENSORS:
raise ValueError(f"Unknown load cell name '{name}'")
dt_pin = SENSORS[name]["DT"]
sck_pin = SENSORS[name]["SCK"]
vals = []
for _ in range(samples):
v = _read_hx711_once(dt_pin, sck_pin, timeout=timeout)
if v is not None:
vals.append(v)
time.sleep(0.005)
if not vals:
return None
vals.sort()
trimmed = vals[1:-1] if len(vals) >= 5 else vals
return mean(trimmed)
Object isolation before feature extraction:
diff = cv2.absdiff(img_gray, empty_gray)
diff_blur = cv2.GaussianBlur(diff, (5, 5), 0)
_, mask = cv2.threshold(diff_blur, 25, 255, cv2.THRESH_BINARY)
kernel = np.ones((3, 3), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=2)
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
Low-latency MJPEG streaming from the Pi camera:
def _mjpeg_stream(draw_overlay=False, q=80):
while True:
with _lock:
frame = None if _latest_frame is None else _latest_frame.copy()
fps = _metrics["fps"]
if frame is None:
time.sleep(0.01)
continue
if draw_overlay:
cv2.putText(frame, f"FPS: {fps}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255), 2, cv2.LINE_AA)
payload = _encode_jpeg(frame, quality=q)
if payload is None:
continue
yield (b"--frame\\r\\n"
b"Content-Type: image/jpeg\\r\\n"
b"Content-Length: " + str(len(payload)).encode() + b"\\r\\n\\r\\n" +
payload + b"\\r\\n")
Teammates:
← Back to Projects