Build a Mini Shopping Trolley That Bills, Guards & Bags Itself!
Meet Trolly — a mini trolley that pops its lid open the moment you scan an item's RFID tag, silently weighs everything to catch sneaky unscanned items, and at checkout, opens its front door to drop your shopping straight into a sealed bag.
🧾 Say hi to Trolly, your smart shopping trolley!
What Is a Smart Billing Trolley, Anyway?
Trolly rethinks the checkout line! Instead of scanning everything at a register, each item gets scanned right as you shop. An RFID reader identifies the item and adds it to your running bill, a servo pops the lid open so you can drop it in, and a hidden weight sensor keeps watch the whole time to make sure nothing gets added without being scanned. At the end, one button press for "payment" opens the front door and sends everything into a bag, ready to go.
What You'll Need
Gather these parts before you start building Trolly!
Arduino Uno
The brain running billing, weight checks, and every servo.
RC522 RFID Reader + Tags
Identifies each product the moment it's scanned.
SG90 Servo (Top Lid)
Pops open so you can drop the scanned item in.
SG90 Servo (Front Door)
Opens at checkout to release items into a bag.
SG90 Servo (Bag Seal)
Twists a tie closed once the bag is filled.
Load Cell + HX711 Amplifier
Weighs the basket to catch unscanned items.
Push Button ("Pay Now")
Simulates completing payment at the exit point.
0.96" I2C OLED Display
Shows the running bill total and system status.
LEDs (Green + Red)
Green for all-clear, red for a theft alert.
Small Buzzer
Beeps for scans, alerts, and payment complete.
Jumper Wires + Breadboard
Connects every sensor, servo, and display.
The Circuit Diagram
The RFID reader, load cell, three servos, LEDs, and OLED all connect to one Arduino Uno.
Step-by-Step Build Instructions
We'll build the scanning system first, then the theft-check, then checkout. Work with an adult on wiring and calibration!
Mount the RFID reader and lid servo
Place the RFID reader at the trolley's rim where items get scanned, and mount the lid servo so it can swing the top lid open and closed.
Install the load cell under the basket
Mount the load cell beneath the trolley's basket floor, connected through the HX711 amplifier, so it can weigh everything inside at all times.
💡 Tip: Calibrate the load cell with a known weight before trusting its readings — the same process used in a kitchen scale project.Set up your product database
Assign each toy product an RFID tag, and note down its price and weight — you'll enter these into the code.
Add the front door and bag seal servos
Mount the front door servo at the base of the trolley so it can swing open toward a waiting bag, and the bag seal servo with a small arm that can twist a tie closed.
Add the "Pay Now" button, LEDs, and buzzer
Mount the button near the trolley handle to simulate the checkout point, and place the LEDs and buzzer somewhere visible for status feedback.
Mount the OLED and finish wiring
Add the OLED where it's easy to read your running total, then double-check every wire against the circuit diagram.
Upload the code and test
Upload the code, scan a tagged item, drop it through the open lid, then press "Pay Now" and watch the front door dispense and seal your bag!
The Arduino Code
This code needs the MFRC522, HX711, Servo, and Adafruit_SSD1306 libraries. Add your own product tags and prices, then click Upload.
// 🛒🤖 Trolly the Mini Smart Shopping Trolley — Arduino Robotics Project // Scans items to bill, weighs to prevent theft, and auto-bags at checkout #include <SPI.h> #include <MFRC522.h> #include <HX711.h> #include <Servo.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SS_PIN 10 #define RST_PIN 9 MFRC522 rfid(SS_PIN, RST_PIN); HX711 scale; const int hxDT = 7, hxSCK = 8; float calibrationFactor = 420.0; // set this during calibration float expectedWeight = 0; float weightTolerance = 15.0; // grams of allowed variation Servo lidServo, doorServo, sealServo; const int lidPin = 3, doorPin = 5, sealPin = 6; const int payButtonPin = 2; const int buzzerPin = 4; const int greenLED = A0, redLED = A1; Adafruit_SSD1306 display(128, 64, &Wire, -1); // ---- Simple product database ---- struct Product { byte uid[4]; String name; float price; float weight; }; Product products[3] = { {{0x11, 0x22, 0x33, 0x44}, "Apple", 0.50, 150}, {{0x55, 0x66, 0x77, 0x88}, "Milk", 1.20, 1000}, {{0x99, 0xAA, 0xBB, 0xCC}, "Bread", 2.00, 500} }; float billTotal = 0; void setup() { SPI.begin(); rfid.PCD_Init(); scale.begin(hxDT, hxSCK); scale.set_scale(calibrationFactor); scale.tare(); lidServo.attach(lidPin); lidServo.write(0); doorServo.attach(doorPin); doorServo.write(0); sealServo.attach(sealPin); sealServo.write(0); pinMode(payButtonPin, INPUT_PULLUP); pinMode(buzzerPin, OUTPUT); pinMode(greenLED, OUTPUT); pinMode(redLED, OUTPUT); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); showBill(); } void loop() { if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) { scanItem(); } checkForTheft(); if (digitalRead(payButtonPin) == LOW) { completeCheckout(); } } // Opens the lid, adds the item to the bill, and updates expected weight void scanItem() { for (int i = 0; i < 3; i++) { if (matchesUID(products[i].uid)) { billTotal += products[i].price; expectedWeight += products[i].weight; lidServo.write(90); tone(buzzerPin, 1400, 150); delay(1500); // time to drop the item in lidServo.write(0); showBill(); } } rfid.PICC_HaltA(); rfid.PCD_StopCrypto1(); } // Compares the scanned tag's UID to a product's stored UID bool matchesUID(byte* uid) { for (byte i = 0; i < 4; i++) { if (rfid.uid.uidByte[i] != uid[i]) return false; } return true; } // Compares actual basket weight to expected weight from scanned items void checkForTheft() { float currentWeight = scale.get_units(5); float difference = abs(currentWeight - expectedWeight); if (difference > weightTolerance) { digitalWrite(redLED, HIGH); digitalWrite(greenLED, LOW); } else { digitalWrite(redLED, LOW); digitalWrite(greenLED, HIGH); } } // Opens the front door, dispenses into the bag, seals it, then resets void completeCheckout() { showMessage("Payment Complete!"); doorServo.write(100); // open front door delay(1000); sealServo.write(90); // twist the bag tie closed delay(600); tone(buzzerPin, 1800, 400); delay(1500); doorServo.write(0); sealServo.write(0); billTotal = 0; expectedWeight = 0; scale.tare(); showBill(); } // Shows the running bill total on the OLED void showBill() { display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 10); display.println("Your Bill:"); display.setTextSize(2); display.setCursor(0, 30); display.print("$"); display.println(billTotal, 2); display.display(); } // Shows a short status message on the OLED void showMessage(String msg) { display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 25); display.println(msg); display.display(); }
How Does Trolly Actually Work?
Here are the big robotics ideas hiding inside this project:
Scan-to-Bill Logic
Each product's RFID tag is matched against a small database, so scanning instantly looks up the right price and weight — no manual entry needed.
Expected vs. Actual Weight
Every scan adds to an "expected weight" total. The load cell continuously compares that to the real weight — if they don't match, something was added without being scanned.
A Two-Door Design
The lid only lets items in during shopping, while the front door only opens after checkout — separating "adding items" from "removing items" is what makes the system trustworthy.
Structs for Organized Data
The Product struct bundles a tag's UID, name, price, and weight together — much tidier than tracking four separate arrays.
🧑🔬 Safety First!
- Build with an adult, especially when wiring the load cell and calibrating it.
- Use only toy or craft products for testing — this is a learning model, not a certified retail checkout system.
- Keep fingers clear of the lid, door, and seal servos while they're moving.
- The bag "sealing" servo should only twist a soft tie, never anything sharp or heated.
Frequently Asked Questions
What if the weight check gives false alarms?
Increase weightTolerance slightly to allow for small variations in how a load cell reads, and make sure your load cell was properly calibrated with a known test weight first.
How do I add more products?
Add a new entry to the products[] array with that item's RFID UID, name, price, and weight, and increase the array size and loop limit in scanItem() to match.
Can items be removed and re-added mid-shop?
This basic version only tracks additions. As a challenge, you could scan the same tag twice to mean "remove," subtracting its price and weight from the running totals instead.
What age group is this project good for?
Because it combines RFID, load cell calibration, and multiple servos, this is an advanced project best suited for kids around age 11+ working closely with an adult.

Comments
Post a Comment