Build a Smart AI Supermarket Shelf That Watches Its Own Stock!
A tiny robot shelf that knows exactly what you take, how much is left, and even lights up to say "hello!" — powered by Arduino, RFID, and weight sensors. No supermarket needed, just your desk!
What We're Building 🛒✨
Real supermarkets are starting to use "smart shelves" — shelves that can tell when a product is picked up, how many are left, and when it's time to restock, all without a human checking by hand! In this project, we'll build our very own miniature version using Arduino. Our shelf will:
Detect Removed Products
An RFID reader instantly knows which tagged product was taken off the shelf.
Weigh Every Slot
A tiny load cell under each slot tracks exactly how much stock remains.
Warn About Low Stock
When weight drops below a safe level, the shelf lights up red and sends an alert.
Light Itself Up
Full-color LEDs glow green when stocked and pulse red when a slot needs a refill.
Show Product Info
A little OLED screen displays the name, price, and quantity of the last item scanned.
Update a Live Dashboard
All this data streams to a touch-friendly HTML dashboard you can show on any screen.
How It Works ⚙️
Think of the shelf as having three "senses" working together, just like a tiny robot brain:
- Touch sense (RFID): Every product has a little RFID sticker. When you tap it near the reader while placing it back or picking it up, the Arduino instantly recognizes exactly which product it is.
- Weight sense (Load Cells + HX711): Each shelf slot rests on a small load cell. The HX711 amplifier converts tiny weight changes into numbers the Arduino can read, so it always knows how many items are on that slot.
- Decision + Output: The Arduino combines both readings. If weight falls below a threshold (like "only 2 items left"), it turns that slot's LEDs red, shows a warning on the OLED, and sends a "LOW STOCK" message out over serial to the dashboard.
Parts You'll Need 🧰
This project uses a 3-slot mini shelf as our demo, which you can expand later. Total cost is roughly $35-55 depending on where you shop.
| Component | Qty | Purpose |
|---|---|---|
| Arduino Mega 2560 | ×1 | Main brain — needs many pins for 3 RFID + 3 weight sensors |
| MFRC522 RFID Reader Module | ×3 | Detects which product is placed/removed on each slot |
| RFID Tags/Stickers (13.56MHz) | ×6+ | Stuck onto each product |
| Mini Load Cell (0-5kg) + HX711 Amp | ×3 | Weighs each shelf slot |
| WS2812B Addressable LED Strip | ×1 (30 LEDs) | Automatic shelf lighting, color-coded per slot |
| 0.96" I2C OLED Display (SSD1306) | ×1 | Shows product name, price & stock count |
| Breadboard + Jumper Wires | 1 set | Wiring everything together, no soldering needed |
| 5V 3A External Power Supply | ×1 | Powers the LED strip and sensors safely |
| Cardboard / foam board / 3D-printed shelf frame | 1 set | The physical mini shelf body |
| Small plastic cups or trays (per slot) | ×3 | Sits on the load cell to hold products steady |
Circuit & Wiring 🔌
Here's how each module connects to the Arduino Mega. We're showing wiring for Slot 1 — repeat the RFID and HX711 pattern for Slots 2 and 3 using the extra pins listed.
RFID Reader (MFRC522) — SPI shared bus
| MFRC522 Pin | Arduino Mega Pin | Wire Color |
|---|---|---|
| SDA (SS) | Slot 1: D9 · Slot 2: D8 · Slot 3: D7 | Orange |
| SCK | D52 (shared) | Yellow |
| MOSI | D51 (shared) | Blue |
| MISO | D50 (shared) | Green |
| RST | Slot 1: D5 · Slot 2: D4 · Slot 3: D3 | Purple |
| 3.3V | 3.3V rail | Red |
| GND | GND rail | Black |
Load Cell + HX711 Amplifier (per slot)
| HX711 Pin | Arduino Mega Pin | Wire Color |
|---|---|---|
| DT (Data) | Slot 1: A0 · Slot 2: A1 · Slot 3: A2 | White |
| SCK (Clock) | Slot 1: A3 · Slot 2: A4 · Slot 3: A5 | Green |
| VCC | 5V rail | Red |
| GND | GND rail | Black |
LED Strip, OLED & Power
| Part | Pin | Arduino Mega Pin |
|---|---|---|
| WS2812B Strip | DIN (Data In) | D6 |
| WS2812B Strip | V+ / GND | External 5V supply (share GND with Arduino!) |
| OLED Display | SDA | D20 (SDA) |
| OLED Display | SCL | D21 (SCL) |
| OLED Display | VCC / GND | 5V / GND rail |
Step-by-Step Build Guide 🪛
Build the Shelf Frame
Cut cardboard or foam board into a small 2-tier shelf, or use a 3D-printed frame. Leave 3 flat slots wide enough for small snack boxes or toy products.
- Keep each slot the same size so weight readings stay consistent
- Leave a gap behind each slot to hide wiring
Mount the Load Cells
Screw or glue one load cell flat under each slot, then place a small tray or cup on top so products sit steady and centered.
- Make sure the load cell isn't touching the frame anywhere else, or readings will be inaccurate
Wire the RFID Readers
Mount one MFRC522 reader at the front edge of each slot, facing where a product will be tapped. Connect using the SPI wiring table above.
Attach the LED Strip
Run the WS2812B strip along the front edge of each shelf tier so each slot's section can glow its own color.
Add the OLED Display
Mount the OLED at the top corner of the shelf, like a little price tag screen, and wire it to the I2C pins.
Stick RFID Tags on Products
Attach one RFID sticker to the bottom of each toy product or snack box. Note down each tag's unique ID (the code will help you find this).
Upload the Code & Calibrate
Upload the Arduino sketch below, open the Serial Monitor, and follow the calibration steps to teach each load cell what an empty slot and a full slot weigh.
Connect the Live Dashboard
Open the HTML dashboard page in Chrome, click "Connect Shelf," and watch your smart shelf's data appear live on screen!
Arduino Code 💻
This sketch reads all 3 RFID readers and load cells, controls the LEDs, updates the OLED, and streams JSON data over serial for the dashboard. It uses the MFRC522, HX711, Adafruit_NeoPixel, and Adafruit_SSD1306 libraries — install them from the Arduino Library Manager first.
// ===== Smart AI Supermarket Shelf — Mini Makers Lab =====
#include <SPI.h>
#include <MFRC522.h>
#include <HX711.h>
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// ---- Pin setup ----
const int RFID_SS[3] = {9, 8, 7};
const int RFID_RST[3] = {5, 4, 3};
const int HX_DT[3] = {A0, A1, A2};
const int HX_SCK[3] = {A3, A4, A5};
const int LED_PIN = 6;
const int LEDS_PER_SLOT = 10;
const int NUM_LEDS = LEDS_PER_SLOT * 3;
MFRC522 rfid[3] = { MFRC522(RFID_SS[0], RFID_RST[0]),
MFRC522(RFID_SS[1], RFID_RST[1]),
MFRC522(RFID_SS[2], RFID_RST[2]) };
HX711 scale[3];
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_SSD1306 display(128, 64, &Wire, -1);
// ---- Product database: RFID tag UID -> product info ----
struct Product { String uid; String name; float price; float unitWeight; };
Product catalog[] = {
{"A1B2C3D4", "Choco Bar", 1.50, 45.0},
{"E5F6A7B8", "Juice Box", 2.00, 120.0},
{"C9D0E1F2", "Cereal Pack", 3.25, 180.0}
};
// ---- Calibration & thresholds (set during Step 7) ----
float calibrationFactor[3] = {-7050.0, -7050.0, -7050.0};
float lowStockGrams[3] = {90.0, 240.0, 360.0}; // ~2 items left
String slotProduct[3] = {"Empty", "Empty", "Empty"};
void setup() {
Serial.begin(9600);
SPI.begin();
strip.begin();
strip.show();
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED not found!");
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
for (int i = 0; i < 3; i++) {
rfid[i].PCD_Init();
scale[i].begin(HX_DT[i], HX_SCK[i]);
scale[i].set_scale(calibrationFactor[i]);
scale[i].tare(); // zero out with empty tray
}
showMessage("Smart Shelf Ready!");
Serial.println("{\"event\":\"boot\",\"status\":\"ready\"}");
}
void loop() {
for (int slot = 0; slot < 3; slot++) {
checkRFID(slot);
checkWeight(slot);
}
delay(400);
}
void checkRFID(int slot) {
if (!rfid[slot].PICC_IsNewCardPresent() || !rfid[slot].PICC_ReadCardSerial()) return;
String uid = "";
for (byte i = 0; i < rfid[slot].uid.size; i++) {
uid += String(rfid[slot].uid.uidByte[i], HEX);
}
uid.toUpperCase();
Product p = findProduct(uid);
slotProduct[slot] = p.name;
float grams = scale[slot].get_units(5);
int itemsLeft = (p.unitWeight > 0) ? max(0, (int)(grams / p.unitWeight)) : 0;
showProductInfo(p.name, p.price, itemsLeft);
sendUpdate(slot, p.name, grams, itemsLeft, grams < lowStockGrams[slot]);
rfid[slot].PICC_HaltA();
}
void checkWeight(int slot) {
float grams = scale[slot].get_units(5);
bool low = grams < lowStockGrams[slot];
setSlotColor(slot, low ? strip.Color(255, 40, 40) : strip.Color(40, 220, 90));
if (low) {
sendUpdate(slot, slotProduct[slot], grams, 0, true);
}
}
void setSlotColor(int slot, uint32_t color) {
int start = slot * LEDS_PER_SLOT;
for (int i = start; i < start + LEDS_PER_SLOT; i++) strip.setPixelColor(i, color);
strip.show();
}
Product findProduct(String uid) {
for (auto &p : catalog) if (p.uid == uid) return p;
return {uid, "Unknown Item", 0, 0};
}
void showProductInfo(String name, float price, int qty) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println(name);
display.print("Price: $"); display.println(price);
display.print("In stock: "); display.println(qty);
display.display();
}
void showMessage(String msg) {
display.clearDisplay();
display.setCursor(0, 20);
display.println(msg);
display.display();
}
void sendUpdate(int slot, String name, float grams, int qty, bool low) {
Serial.print("{\"slot\":"); Serial.print(slot);
Serial.print(",\"product\":\""); Serial.print(name);
Serial.print("\",\"weight\":"); Serial.print(grams);
Serial.print(",\"qty\":"); Serial.print(qty);
Serial.print(",\"lowStock\":"); Serial.print(low ? "true" : "false");
Serial.println("}");
}
calibrationFactor until the displayed grams match reality.Touch-Screen Inventory Dashboard (HTML) 📺
This page runs in Chrome and uses the Web Serial API to read the JSON messages straight from your Arduino over USB. Show it fullscreen on any touchscreen monitor (including one plugged in over HDMI) for a real "store display" feel. Save this as dashboard.html and open it in Chrome or Edge.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Smart Shelf Dashboard</title>
<style>
body{ font-family:sans-serif; background:#1b1533; color:#fff; margin:0; padding:24px; }
h1{ text-align:center; }
#connectBtn{ display:block; margin:0 auto 24px; padding:14px 28px; font-size:1.1rem;
border:none; border-radius:12px; background:#ff6b6b; color:#fff; cursor:pointer; }
.grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:20px; max-width:900px; margin:0 auto; }
.card{ background:#2b2140; border-radius:18px; padding:20px; text-align:center;
border:3px solid #4CD37B; transition:border-color .3s; }
.card.low{ border-color:#FF5252; }
.card h2{ margin:6px 0; }
.qty{ font-size:2.4rem; font-weight:bold; }
.status{ font-size:.9rem; opacity:.8; }
</style>
</head>
<body>
<h1>🛒 Smart Shelf — Live Inventory</h1>
<button id="connectBtn">🔌 Connect Shelf</button>
<div class="grid" id="grid">
<div class="card" id="slot0"><h2>Slot 1</h2><div class="qty">--</div><div class="status">Waiting...</div></div>
<div class="card" id="slot1"><h2>Slot 2</h2><div class="qty">--</div><div class="status">Waiting...</div></div>
<div class="card" id="slot2"><h2>Slot 3</h2><div class="qty">--</div><div class="status">Waiting...</div></div>
</div>
<script>
let port, reader, buffer = "";
document.getElementById("connectBtn").addEventListener("click", async () => {
try {
port = await navigator.serial.requestPort();
await port.open({ baudRate: 9600 });
readLoop();
} catch (err) {
alert("Could not connect: " + err);
}
});
async function readLoop() {
const textDecoder = new TextDecoderStream();
port.readable.pipeTo(textDecoder.writable);
reader = textDecoder.readable.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += value;
let lines = buffer.split("\n");
buffer = lines.pop();
lines.forEach(handleLine);
}
}
function handleLine(line) {
try {
const data = JSON.parse(line.trim());
if (data.slot === undefined) return;
const card = document.getElementById("slot" + data.slot);
card.querySelector(".qty").textContent = data.qty ?? "--";
card.querySelector("h2").textContent = data.product || ("Slot " + (data.slot+1));
card.querySelector(".status").textContent = data.lowStock ? "⚠️ Low stock!" : "✅ Well stocked";
card.classList.toggle("low", !!data.lowStock);
} catch (e) { /* ignore incomplete lines */ }
}
</script>
</body>
</html>
Testing & Troubleshooting 🧪
🏷️ RFID not reading
Double-check 3.3V power (RFID modules do NOT like 5V!) and that SS/RST pins match the code for each slot.
⚖️ Weight jumping around
Make sure the load cell is only touching the shelf at its two mounting points — nothing else should press on it.
💡 LEDs not lighting
Confirm the external 5V supply is connected and shares a common ground with the Arduino.
📺 Dashboard won't connect
Web Serial only works in Chrome or Edge, and only one program can use the serial port at a time — close Serial Monitor first.
Frequently Asked Questions ❓
What is a smart supermarket shelf project?
A smart supermarket shelf is a mini robotics project that uses sensors like RFID readers and weight sensors to automatically detect when a product is picked up, track how much stock is left, and update a digital display in real time — just like the smart shelves being tested in real stores.
Do I need to know how to code to build this project?
No! This guide includes the complete, ready-to-upload Arduino code and HTML dashboard code. You just need to copy the code, wire the parts as shown, and upload it using the free Arduino IDE.
What components do I need for the smart shelf project?
You need an Arduino Mega 2560, MFRC522 RFID modules, HX711 load cell amplifiers with mini load cells, an addressable NeoPixel LED strip, a 0.96 inch OLED display, RFID tags, and basic wiring supplies like jumper wires and a breadboard.
How does the weight sensor detect low stock?
Each shelf slot sits on a small load cell connected to an HX711 amplifier. The Arduino continuously reads the weight and compares it to a known weight-per-item value. When the total weight drops below a set threshold, it means only a few items are left, so the Arduino triggers a low-stock alert.
Can this project connect to a real screen or dashboard?
Yes! The Arduino sends live product and stock data over a serial connection. The included HTML dashboard page uses the Web Serial API in Chrome to read that data and display a live, touch-friendly inventory screen on any monitor, including one connected over HDMI.
Is this project safe for kids to build?
Yes, with adult supervision. The project runs on safe low-voltage 5V electronics, uses no soldering if you choose breadboard-friendly modules, and is a great introduction to sensors, circuits, and coding for young makers.

Comments
Post a Comment