Andrew's FabAcademy Journey

Inventory Tracking System for Storerooms

Prepared by Andrew | Last Updated: May 27, 2025, 04:23 AM CAT

This Fab Academy project automates tool and item management in storerooms, addressing inefficiencies and loss due to misplaced or untracked tools.

Problem

Tools in storerooms are often misplaced or untracked, leading to inefficiencies and loss. Manual tracking is error-prone and time-consuming.

Students_line
Slow_Queue
Loan_Register

Solution

The Inventory Tracking System automates tool management by:

This system enhances efficiency, reduces loss, and ensures reliability with a manual fallback if the RFID reader fails.

Prior Work

Fab Academy Skills

  • System Integration: Combined hardware (PCB, enclosure) and software (Python) for a standalone system.
  • Diagram

Hardware Design

  • Microcontroller: Raspberry Pi 3B+ for processing and GUI management.
  • Pi 3B
  • Input Device: RF Ideas RDR-7081AKU USB RFID reader and RFID-RC522 for redundancy.
  • RDR-7081AKU?
  • Output Device: Tkinter GUI on Raspberry Pi; optional 16x2 I2C LCD for redundancy.

Fabrication

  • Enclosure: 3D-printed 150x100x50 mm PLA enclosure Solidworks for Raspberry Pi and RDR-7081AKU.
  • All enclosed
  • PCB: I Milled PCB for power and I2C connections.
  • Custom PCB

Software Development

  • Tool and Borrower Registration: Python script on Raspberry Pi reads RFID UIDs, logs borrower IDs, and stores data in CSV (tool_id, borrower_id, timestamp, action).
  • Tracking Logic:
    • Check-out: Scan tool → Scan borrower ID or manual entry → Log “checked out”.
    • Display status on Tkinter GUI and optional LCD.
  • Error Handling: Validates UIDs, alerts via GUI for invalid scans.

Implementation Steps

  • Step 1: Set Up the Raspberry Pi 3 Environment
    • Install Windows Subsystem for Linux to in stall Ubuntu in order to run Pi Os and python applications/li> WSL
    • Run Ubuntu to work on raspberry installations
    • Ubuntu
    • Install Raspberry Pi 3 Operating System

    • Pi Os

    • Writing to SD CARD

    • Writing Os to sdcard

    Time: 06:50 AM CAT, May 27, 2025

    • Installed LXDE Remote Desktop to host the Raspberry Pi 3 desktop remotely, ensuring keyboard and mouse connectivity.
    • LXDE Remote Desktop
    • Update the system and install Python 3.13.3.
    • python-3133
    • Install required Python libraries (pyserial for serial communication, keyboard for USB HID input).
    • pyserial
    • Verify installations to confirm Python version and library presence.
    • Version 3.12
      Pyserial keyboard
    • Duration: ~15-20 minutes.
  • Step 2: Prepare the Hardware Connections

    Time: 07:10 AM CAT, May 27, 2025

    • Connect the RDR-7081AKU USB RFID reader to a USB port on the Raspberry Pi 3.
    • Connect the RP2040 to the Raspberry Pi 3 via USB (serial port).
    • Connection pins Rp2040 to RaspberryPi 3
    • Rp2040 to Pi
    • Wire the RFID-RC522 to the RP2040 with SPI pins.
    • RC522 to RP2040
    • Connect the 16x2 I2C LCD to the Raspberry Pi with I2C pins.
    • Pi to LCD Pin connections
    • Test the serial connection to confirm RP2040 detection.
    • Detecting RP2040
    • Test I2C connection
    • Duration: ~10-15 minutes.
  • Step 3: Set Up the CSV File and Initial Data

    Time: 07:25 AM CAT, May 27, 2025

    • Create a transactions.csv file in the working directory.
    • Add header and initial data (e.g., sample tool transactions).
    • Create an initial inventory dictionary with sample tools (e.g., Hammer: 5, Screwdriver: 10).
    • Transactions log
    • Duration: ~10 minutes.
  • Step 4: Write the Tkinter GUI Code

    Time: 07:35 AM CAT, May 27, 2025

    • Use Visual Studio Code to edit and test the Python Tkinter GUI code, confirming successful display of the GUI.
    • Testing Tkinter Code on Vscode
    • Paste the Python Tkinter GUI code into the Raspberry Pi terminal to create or run the inventory tracking script.
    • Inventory_gui.py
    • Duration: ~30-40 minutes.
  • Step 5: Program the RP2040

    Time: 08:15 AM CAT, May 27, 2025

    • Install Micropython on the Raspberry Pi 3 RP2040
    • RP2040 Com7
    • Use Thonny to program the RP2040 with sketches for controlling the RFID-RC522.
    • Main
                                      import machine
      import time
      from mfrc522 import MFRC522
      
      # Pin definitions
      SDA = machine.Pin(5, machine.Pin.OUT)
      SCK = machine.Pin(2)
      MOSI = machine.Pin(3)
      MISO = machine.Pin(4)
      RST = machine.Pin(6, machine.Pin.OUT)
      
      spi = machine.SPI(0, sck=SCK, mosi=MOSI, miso=MISO, baudrate=1000000)
      rdr = MFRC522(sck=2, mosi=3, miso=4, rst=6, cs=5)
      
      def read_uid():
          (stat, tag_type) = rdr.request(rdr.REQIDL)
          if stat == rdr.OK:
              (stat, raw_uid) = rdr.anticoll()
              if stat == rdr.OK:
                  uid = ":".join(f"{x:02X}" for x in raw_uid)
                  return uid
          return None
      
      print("Ready to scan cards. Bring a tag close...")
      
      while True:
          uid = read_uid()
          if uid:
              print("Detected tag UID:", uid)
              time.sleep(2)  # Pause to avoid double scanning
          else:
              time.sleep(0.1)
      
                                  


    • MFRC522 code
    •                             from machine import Pin, SPI
      from os import uname
      
      
      class MFRC522:
      
          OK = 0
          NOTAGERR = 1
          ERR = 2
      
          REQIDL = 0x26
          REQALL = 0x52
          AUTHENT1A = 0x60
          AUTHENT1B = 0x61
      
          def __init__(self, sck, mosi, miso, rst, cs):
              self.sck = Pin(sck, Pin.OUT)
              self.mosi = Pin(mosi, Pin.OUT)
              self.miso = Pin(miso)
              self.rst = Pin(rst, Pin.OUT)
              self.cs = Pin(cs, Pin.OUT)
      
              self.rst.value(0)
              self.cs.value(1)
      
              board = uname()[0]
      
              if board in ('WiPy', 'LoPy', 'FiPy'):
                  self.spi = SPI(0)
                  self.spi.init(SPI.MASTER, baudrate=1000000, pins=(self.sck, self.mosi, self.miso))
              elif board in ('esp8266', 'esp32'):
                  self.spi = SPI(1, baudrate=1000000, polarity=0, phase=0,
                                 sck=self.sck, mosi=self.mosi, miso=self.miso)
              else:
                  # RP2040 and other generic platforms
                  self.spi = SPI(0, baudrate=1000000, polarity=0, phase=0,
                                 sck=self.sck, mosi=self.mosi, miso=self.miso)
      
              self.rst.value(1)
              self.init()
      
          def _wreg(self, reg, val):
              self.cs.value(0)
              self.spi.write(bytes([0x7E & (reg << 1)]))
              self.spi.write(bytes([val]))
              self.cs.value(1)
      
          def _rreg(self, reg):
              self.cs.value(0)
              self.spi.write(bytes([0x80 | (0x7E & (reg << 1))]))
              val = self.spi.read(1)
              self.cs.value(1)
              return val[0]
      
          def _sflags(self, reg, mask):
              self._wreg(reg, self._rreg(reg) | mask)
      
          def _cflags(self, reg, mask):
              self._wreg(reg, self._rreg(reg) & (~mask))
      
          def _tocard(self, cmd, send):
              recv = []
              bits = irq_en = wait_irq = n = 0
              stat = self.ERR
      
              if cmd == 0x0E:
                  irq_en = 0x12
                  wait_irq = 0x10
              elif cmd == 0x0C:
                  irq_en = 0x77
                  wait_irq = 0x30
      
              self._wreg(0x02, irq_en | 0x80)
              self._cflags(0x04, 0x80)
              self._sflags(0x0A, 0x80)
              self._wreg(0x01, 0x00)
      
              for c in send:
                  self._wreg(0x09, c)
              self._wreg(0x01, cmd)
      
              if cmd == 0x0C:
                  self._sflags(0x0D, 0x80)
      
              i = 2000
              while True:
                  n = self._rreg(0x04)
                  i -= 1
                  if not (i != 0 and not (n & 0x01) and not (n & wait_irq)):
                      break
      
              self._cflags(0x0D, 0x80)
      
              if i:
                  if (self._rreg(0x06) & 0x1B) == 0x00:
                      stat = self.OK
      
                      if n & irq_en & 0x01:
                          stat = self.NOTAGERR
                      elif cmd == 0x0C:
                          n = self._rreg(0x0A)
                          lbits = self._rreg(0x0C) & 0x07
                          bits = (n - 1) * 8 + lbits if lbits != 0 else n * 8
                          n = max(1, min(n, 16))
                          for _ in range(n):
                              recv.append(self._rreg(0x09))
                  else:
                      stat = self.ERR
      
              return stat, recv, bits
      
          def _crc(self, data):
              self._cflags(0x05, 0x04)
              self._sflags(0x0A, 0x80)
      
              for c in data:
                  self._wreg(0x09, c)
      
              self._wreg(0x01, 0x03)
      
              i = 0xFF
              while True:
                  n = self._rreg(0x05)
                  i -= 1
                  if not (i != 0 and not (n & 0x04)):
                      break
      
              return [self._rreg(0x22), self._rreg(0x21)]
      
          def init(self):
              self.reset()
              self._wreg(0x2A, 0x8D)
              self._wreg(0x2B, 0x3E)
              self._wreg(0x2D, 30)
              self._wreg(0x2C, 0)
              self._wreg(0x15, 0x40)
              self._wreg(0x11, 0x3D)
              self.antenna_on()
      
          def reset(self):
              self._wreg(0x01, 0x0F)
      
          def antenna_on(self, on=True):
              if on and ~(self._rreg(0x14) & 0x03):
                  self._sflags(0x14, 0x03)
              else:
                  self._cflags(0x14, 0x03)
      
          def request(self, mode):
              self._wreg(0x0D, 0x07)
              (stat, recv, bits) = self._tocard(0x0C, [mode])
              if stat != self.OK or bits != 0x10:
                  stat = self.ERR
              return stat, bits
      
          def anticoll(self):
              ser_chk = 0
              ser = [0x93, 0x20]
              self._wreg(0x0D, 0x00)
              (stat, recv, bits) = self._tocard(0x0C, ser)
              if stat == self.OK and len(recv) == 5:
                  for i in range(4):
                      ser_chk ^= recv[i]
                  if ser_chk != recv[4]:
                      stat = self.ERR
              else:
                  stat = self.ERR
              return stat, recv
      
          def select_tag(self, ser):
              buf = [0x93, 0x70] + ser[:5]
              buf += self._crc(buf)
              (stat, recv, bits) = self._tocard(0x0C, buf)
              return self.OK if stat == self.OK and bits == 0x18 else self.ERR
      
          def auth(self, mode, addr, sect, ser):
              return self._tocard(0x0E, [mode, addr] + sect + ser[:4])[0]
      
          def stop_crypto1(self):
              self._cflags(0x08, 0x08)
      
          def read(self, addr):
              data = [0x30, addr]
              data += self._crc(data)
              (stat, recv, _) = self._tocard(0x0C, data)
              return recv if stat == self.OK else None
      
          def write(self, addr, data):
              buf = [0xA0, addr] + self._crc([0xA0, addr])
              (stat, recv, bits) = self._tocard(0x0C, buf)
              if not (stat == self.OK and bits == 4 and (recv[0] & 0x0F) == 0x0A):
                  return self.ERR
      
              buf = data[:16] + self._crc(data[:16])
              (stat, recv, bits) = self._tocard(0x0C, buf)
              if not (stat == self.OK and bits == 4 and (recv[0] & 0x0F) == 0x0A):
                  return self.ERR
      
              return self.OK
      
                              
    • Verify the RP2040 initializes MFRC and writes ready to scan.
    • Ready to scan cards
    • Duration: ~20-30 minutes.
  • Step 6: Test the Integration

    Time: 08:45 AM CAT, May 27, 2025

    • Run the Tkinter GUI script
    • Scan an RFID tag with the RDR-7081AKU and verify UID display and LCD outputIt works
    • Test RFID-RC522 scanning as a fallbackDisplays Triggers RP2040 on LCD but does not scan cards
    • Send a manual message via the GUI to test LCD displayNo response
    • heck the transactions.csv file for updated entriesAvailable
    • Duration: ~20-30 minutes.
  • Bill of Materials (BOM)

    • New Components:
      • Raspberry Pi, wires ($47.10).
      • RFID tags multicolor(20) ~$10.
      • PLA filament (~900g) ~$16.99.
      • MicroSD card (8 GB) ~$8.98.
      • LCD ~$12.99.
      • PCB (custom) ~$10.
      • Misc (headers, M3 bolts and nuts, M3 self tapping screws) ~$4.
      • RDR-7081AKU ($124.99)
    • Total New Cost: ~$235.05 (The cost is higher because of the RDR-7081AKU).

    Timeline (Completed)

      Timeline

    Deliverables

    • Hardware: Custom PCB with Raspberry Pi, RDR-7081AKU, RFID-RC522, and optional LCD.
    • Fabrication: 3D-printed PLA enclosure (150x100x50 mm), 20 laser-cut acrylic RFID tags.
    • Software: Python code for RFID scanning, Tkinter GUI, and CSV logging.
    • Documentation: HTML webpage with schematics, code, photos, and video.
    • Demo: Video showing tool check-out/check-in with borrower registration.

    Questions to be Answered

    • Will the RDR-7081AKU reliably scan all 20 tags in a busy storeroomLooking at the test it can manage as it doesnt miss a scan
    • Is the 150x130x92 mm enclosure sufficient for the Raspberry Pi and RDR-7081AKU? In terms of space its sufficient but I need to modify to allow cables to fot in for assembly
    • Can the optional LCD enhance redundancy without complicating the design?
    • Still to test as I need to establish why its not scanning
    • Will the manual entry redundancy handle all edge cases?Yes, its available if all scans fail to scan, the GUI will log transactions on the CSV

    Evaluation

    • Functionality:
      • RDR-7081AKU scans and logs transactions correctly (tested with 10 tools, 5 borrowers).
      • Manual entry updates inventory and logs transactions.
      • GUI reflects real-time changes.
    • Design Quality:
      • Enclosure fits all components with accessible USB ports.
      • PCB routes power cleanly, tested with multimeter.
    • Performance:
      • RFID scan speed 2 seconds.
      • GUI updates within 2 seconds of actions.
    • Documentation:
      • Submitted schematics (KiCAD), code (Python), 3D/STL files, photos/videos.
      • Submit schematics (KiCAD), code (Python), 3D/STL files, photos/videos.
      • Files for Download:

        Zip Files for Downloads

        Inventry Management Project files for Download

      • I have always wanted a system like this one and FabAcademy has really opened my eyes that such systems can be self made when you have the right tools. I have leart that concepts such as machine learning machine leaning, digital fabrication can all be combined to eqip us to do alomst anything. I have learnt skills in each unit being design, fabrication, electronics, programming, integration, dessemination, interllectual property and income generation.