Inspiration

Our inspiration came from a simple problem that we kept on seeing throughout our lives: people often do not sort their trash correctly, either because they are in a rush or because they do not feel motivated to do it properly. We noticed that even when the right bins are available, trash still ends up in the wrong place. At the same time, there is very little incentive to make waste disposal engaging or rewarding. Since gamified experiences are so effective at capturing people’s attention these days, we wanted to turn throwing away trash into something fun, interactive, effortless, and more sustainable for our environment. That idea led us to Dumpster Fire 🔥, a smart waste-sorting system that makes proper disposal faster, easier, and more exciting 😏.

What it does

When you throw something into the trash can, the system decides whether it belongs in recyclable, compost, or waste and routes it into the right compartment. Vision runs on a laptop (USB or built-in camera via OpenCV): we watch for a sustained change in frame brightness—so a hand or object entering the bin triggers a capture—then send the frame to Google Gemini with a strict JSON schema for predicted_class, disposal_category, and an estimated USD value for the item.

The Raspberry Pi runs a small async WebSocket server (default port 8765) that exposes get_distance (for optional proximity gating) and execute_sort, which maps each label to a predefined servo choreography (three servos on an Adafruit PCA9685 I²C driver at address 0x40). The laptop orchestrator connects as a WebSocket client, sends the sort command after classification, and can POST the JPEG to our FastAPI backend (POST /internal/drops) with a shared Bearer secret.

On the web side, users sign in with Auth0; data lives in Postgres (we host via Supabase). When a physical drop is ingested, the backend can estimate value again with Gemini and, when a single authenticated Plinko session is connected, push a drop event over /ws/plinko so the front end can animate Plinko and surface scores—turning “correct bin” into something you can see and compete on.

How we built it

Ironically, we built it with trash and a dream. Since we could not 3D print anything, we used what was available in my trash bin including cardboard boxes, old trash bins, and leftover screws that we unscrewed from different devices that we did not use anymore.

We first modeled the project in SolidWorks to get an idea of what our project should look like and then we got to work by finding the right materials. We "drilled" holes by consistently stabbing where we wanted the screws using a pocket knife and we used servos to create our joint mechanism. Additionally, we used clamps and even a screwdriver head (which is still currently in our mechanism) to hold other servos together when we were missing the right parts, and tested many different items (including using our Chinese takeout container that we ate 2 hours before building).

On the electronics side, three hobby servos plug into a 16-channel PCA9685 board, driven from Python with adafruit-circuitpython-servokit. The Pi daemon initializes safe default angles, then serves JSON-RPC-style messages over WebSockets using a small shared protocol (ready, get_distance, execute_sort, sort_result, errors) so the laptop and Pi stay in sync.

The laptop “orchestrator” (server/) is a Python loop: OpenCV grabs frames, an EWMA lighting monitor decides when something changed enough to count as a throw, Gemini returns structured disposal labels, and we either run sort motion on the Pi (normal split setup) or, for bench demos, optionally run local servo_test*.py scripts. We wired environment variables for WS_URL, GEMINI_API_KEY, optional PROXIMITY_POLL, and DROP_API_URL (defaults to our Railway deployment) so the same code works in the lab, at a hackathon table, or in CI with a mock Pi WebSocket.

The SPA (React + Vite) talks to FastAPI for REST and derives the Plinko WebSocket URL from the API base; the backend uses Alembic migrations on Postgres. We added pytest e2e tests that spin up a fake Pi and stub HTTP ingest so we could regression-test the full “classify → sort → notify web” path without carrying hardware everywhere.

Challenges we ran into

Literally everything about this project was a challenge. One of the first things we ran into was that our mini servos were getting worked so hard that we burned through multiple of them in one sitting just trying to move the lid. Because of that, we had to keep rethinking the mechanism, find ways to reduce how much stress we were putting on the servos, and be super careful while testing so we did not keep frying more. On top of that, building the actual structure was hard because we did not have the normal tools or materials you would usually want for something like this, so we had to build everything from scratch. One chalenge we had was that the cardboard chasis kept flexing which caused our main ramp to constantly sag which we used to our advantage by implementing a drop and push system. Due to the fact that we couldn't get our hands on any real fasteners, we had to utilize masking tap (a lot of it...) to bring our dream project to life. We also had many hardware issues with the raspberry pi which almost made our main hardware&software coders crash out such as connectivity. The wiring was also difficult too as we did not have many plugs so we had to extend our wires from one wall to another. We ended up improvising a lot and using random materials that you would not normally expect, like straight cardboard, screwdriver heads, and whatever else we could make work.

On the software side, tuning the lighting trigger (EWMA alpha, delta threshold, hold time) took iteration: too sensitive and we classified empty frames; too dull and we missed drops. Split-brain debugging across Pi WebSocket, laptop camera, and a deployed API meant chasing firewall rules, wrong WS_URL, and Bearer mismatches between DROP_API_KEY and DEVICE_INGEST_SECRET. Plinko push only fires when exactly one distinct user session is connected, which is correct for demos but was confusing until we read the router logic.

We first built a CNN model for a trash classifier but it did not work too well, so we ended up using Google Gemini for vision instead. The website side was also a struggle because we tried different database options like MongoDB, and none of them were really working for us, so we ended up switching to Supabase (Postgres) with a clearer relational model for users, drops, and leaderboards. There were definitely multiple moments where we fully crashed out because the mechanism was not working, but in the end we got it to work through a lot of testing, rebuilding, and constantly optimizing the joints and overall design.

Accomplishments that we're proud of

Getting our mechanism to work was a breath of fresh air. Once we got our joints moving, it took a while to connect everything together, and once we connected the laptop orchestrator to the Raspberry Pi over WebSockets and saw sorting plus Plinko fire end-to-end, it felt luxurious. We are proud that we shipped a documented, testable pipeline: shared protocol types, mock-Pi e2e tests, and deployment notes so someone else could reproduce the stack without guessing. We are also proud of how much we improvised on hardware under real constraints and still closed the loop from physical throw → vision → actuation → cloud ingest → UI.

What we learned

We learned that if we ever use servos again, we should always opt for the stronger ones to avoid them being completely overworked. Additionally, next time we should use 3D printers—as when we were picking up tape, we noticed that a lot of people were using 3D printers and we thought that was not allowed. We also gained more hardware skills such as using a Raspberry Pi 5 for the first time, running an async WebSocket service on it, and pairing that with a separate vision machine so heavy ML did not have to run on the board.

We learned that LLM vision with a strict JSON contract can replace a brittle custom CNN for a hackathon timeline, as long as you handle parse failures and logging. We also learned that our own habits throughout this were our testing dataset because we made so much trash during this process that we ended up dumping it into our trash can and it was able to correctly sort everything for us (including five cans of beer and Celsius—so you could say our project ran on that… literally). Overall, it was a great learning experience. 10/10 would do again.

What's next for Dumpster Fire!

If we had more time, we would harden sensing: real ultrasonic or ToF on the Pi with calibrated thresholds instead of relying primarily on laptop lighting triggers, and add mechanical dampening or gear reduction so smaller servos are not always on the edge of stall torque. We would 3D-print guided chutes and indexed mounts for repeatability, expand automated tests toward full Auth0 + browser flows, and refine Plinko fairness and multi-user rules (queue drops, tie-breakers, and clearer session UX). Longer term, we would like on-device or edge classification options for lower latency and offline demos, while keeping the same WebSocket contract so the Pi firmware stays stable.

Built With

Share this project:

Updates