Tủ bán hàng có đèn báo từng vị trí

24/08/2026
tu-ban-hang-co-den-bao-tung-vi-tri

Tủ bán hàng có đèn báo từng vị trí

1. Cấu trúc hệ thống

PC / điện thoại

             │

            ▼

  ESP32 TỦ 1 — WEB CHÍNH 192.168.0.50

             │

             ├──── TỦ 1: vị trí 1–256

             │

            └──── ESP32 TỦ 2 192.168.0.51 vị trí 1–256

Mỗi sản phẩm sẽ có thêm thông tin Tủ, và khi bấm CHỌN / LẤY HÀNG, ESP32 Web chính sẽ gửi đúng lệnh đến ESP32 của tủ tương ứng.

Đặc biệt, ta sẽ xử lý luôn tồn kho dùng chung, tránh trường hợp hai tủ cùng sửa products.json rồi dữ liệu bị lệch.

  • Đặt IP cố định cho Tủ 1 và Tủ 2.
  • Tạo ESP32 Tủ 1 làm Web chính.
  • Kết nối Tủ 2 với Tủ 1 qua WiFi/LAN.
  • Thêm trường Tủ vào products.json.
  • Sửa CHỌN → LẤY HÀNG để gửi đúng lệnh đến từng ESP32.
  • Đồng bộ tồn kho.
  • Cuối cùng kiểm tra trường hợp 2 sản phẩm trùng vị trí2 tủ cùng có sản phẩm giống nhau.

                         ROUTER WIFI 192.168.1.1

                                           │

                 ┌─────────┴─────────┐

                 │                                                 │

       ESP32 TỦ 1                                  ESP32 TỦ 2

      192.168.1.50                                   192.168.1.51

             │                                                     │

      MAX7219                                         MAX7219

         TFT                                                  TFT

       Keypad                                            Keypad

        Servo                                               Servo

Tủ 1 = Web chính

Bạn truy cập: http://192.168.1.50

không cần truy cập 192.168.1.51.

Tủ 2 vẫn chạy ESP32 độc lập để điều khiển phần cứng.

2. Quản lý kho

                  PC
                  │
                  ▼
        ┌──────────────────┐
        │ ESP32 TỦ 1       │
        │ WEB MASTER       │
        │ 192.168.1.50     │
        │                  │
        │ products.json    │
        └────────┬─────────┘
                 │ WiFi/LAN
                 │
        ┌────────▼─────────┐
        │ ESP32 TỦ 2       │
        │ SLAVE            │
        │ 192.168.1.51     │
        │                  │
        │ chỉ nhận lệnh    │
        │ điều khiển máy   │
        └──────────────────┘

Tủ 1 quản lý dữ liệu sản phẩm, Tủ 2 nhận lệnh điều khiển.

Như vậy:

  • Upload CSV → chỉ cần upload ở Tủ 1.
  • Edit sản phẩm → Tủ 1.
  • Download CSV → Tủ 1.
  • Tìm kiếm → Tủ 1.
  • Web → Tủ 1.
  • Chọn sản phẩm thuộc Tủ 2 → Tủ 1 gửi lệnh cho Tủ 2.
  • Tủ 2 bật LED/TFT/servo.
  • Tồn kho → Tủ 1 trừ và lưu products.json.

3. Tạo MASTER và SLAVE

  • Tủ 1 = MASTER
  • Tủ 2 = SLAVE
  • Giữ nguyên toàn bộ giao diện web hiện tại.
  • Hai ESP32 vẫn chạy giao diện riêng như hiện tại.
  • MASTER có thể gửi lệnh cho SLAVE.
  • SLAVE nhận lệnh và phản hồi.
  • Kiểm tra bằng PING → PONG trước.
  • Khi PING/PONG ổn định mới chuyển bước 2: thêm cabinet vào products.json.

Kiến trúc trước mắt

                WiFi Router
                     │
          ┌──────────┴──────────┐
          │                     │
     ESP32 TỦ 1             ESP32 TỦ 2
       MASTER                  SLAVE
          │                     │
          │────── PING ────────>│
          │<──── PONG ──────────│
          │                     │
          │──── COMMAND ───────>│
          │<──── RESPONSE ──────│

4. Hoạt động tìm kiếm:

Bàn phím / Web nhập mã
        │
        ▼
MASTER tìm products.json của Tủ 1
        │
        ├── tìm thấy → xử lý Tủ 1
        │
        └── không thấy
                │
                ▼
        gửi SEARCH sang SLAVE
                │
                ▼
        SLAVE trả FOUND
                │
                ▼
        MASTER hiển thị sản phẩm

Điều kiện thuật toán

             SEARCH
                 │
                ▼
        ┌───────────────┐
        │ TỦ 1 / MASTER │
        └───────┬───────┘
                │
          tìm thấy?
          /           \
     YES        NO
      │                 │
     ▼                ▼
dùng Tủ 1       hỏi Tủ 2
                          │
                  FOUND?
                 /             \
               YES           NO
                │                   │
                ▼                 ▼
          dùng Tủ 2       Không tìm

 

5. Tìm kiếm bằng bàn phím

BÀN PHÍM
   ↓
nhập mã SP
   ↓
searchProductAllCabinets(input)
   ↓
┌───────────────────────┐
│               Tìm products.json                │
│                       MASTER                     │
└───────────┬───────────┘
            │ không có
            ↓
       PING SLAVE
            ↓
         PONG
            ↓
    /cabinet/search
            ↓
 FOUND|1|2212001|...
            ↓
 cập nhật currentCode
 currentPosition
 currentName
 currentStock
 currentPrice
            ↓
 TFT / màn hình
 Web

Hai phần như này

WEB /search
      │
      ▼
searchProductAllCabinets()
      │
      ├── Tìm MASTER
      │      └── thấy → hiển thị
      │
      └── không thấy
             │
             ├── PING SLAVE
             │
             └── SEARCH SLAVE
                    └── thấy → hiển thị

 

Phím D
   │
   ▼
searchProductAllCabinets()

 

Kiến trúc cố định

BÀN PHÍM MASTER ──┐
                  │
WEB MASTER ───────┼──> searchProductAllCabinets(code)
                  │
                  ↓
          TÌM products.json MASTER
                  │
          ┌───────┴───────┐
          │               │
        FOUND           NOT FOUND
          │               │
          ↓               ↓
      xử lý SP        PING SLAVE
                          │
                         PONG
                          ↓
                  searchProductOnSlave()
                          │
                         FOUND
                          ↓
                xử lý SP SLAVE

 

Luồng đúng mà chúng ta đã chốt phải là:

Bấm D / Web Search
       ↓
handleSearch()
       ↓
searchProductAllCabinets(code)
       ↓
Tìm products.json MASTER
       ↓
Không có
       ↓
PING SLAVE 1 lần
       ↓
PONG
       ↓
searchProductOnSlave(code)
       ↓
FOUND|...
       ↓
Cập nhật currentCode
currentPosition
currentName
currentStock
currentPrice
       ↓
LED + TFT + Web + Servo

 

Cấu trúc mới chốt là:

Tủ / VT / Tên sản phẩm / Số lượng / Giá

{ "cabinet": 1, "position": 1, "code": "2404066", "name": "MEGA2560 PRO mini CH340", "stock": 1, "price": 310001 },

Khi Master tìm 2212001, kết quả Slave hiện là:

FOUND|2|1|2212001|STM32 STM 32 F103 C8T6 Arm|10|95000
  1. products.json
  2. Đọc/tìm sản phẩm Master
  3. Tìm sản phẩm Slave
  4. Giao thức FOUND|... có thêm cabinet
  5. Hiển thị Web:

     
    Tủ | VT | Tên sản phẩm | Số lượng | Giá
  6. Keypad Master → tìm Master/Slave
  7. Web Master → tìm Master/Slave
  8. Bật LED đúng tủ + vị trí
  9. Giữ nguyên các phần hiện đang hoạt động.

Tìm kiếm có 2 kiểu

Tìm mã SP
    ↓
searchProductFromFile()
    ↓
MASTER tìm trước
    ↓
không thấy
    ↓

searchProductOnSlave()

  ↓
tìm SLAVE

 

Tìm vị trí
    ↓
handleSelect()

 

TÌM MÃ SP
   

handleSelect()
        → currentCabinet = 1

searchProductAllCabinets()
    ├── tìm MASTER → currentCabinet = 1
    └── tìm SLAVE  → searchProductOnSlave()
                      → currentCabinet = 2

Tìm sp tủ 2 từ master thì cũng trừ số lượng

MASTER tìm SP

SLAVE nhận DISPLAY

LCD + LED

currentCode/currentPosition/currentStock được lưu

A có sản phẩm để trừ

 

5. Cấu hình thiết bị

Với code hiện tại của bạn, giới hạn không nằm ở LittleFS trước mà nằm ở RAM khi deserializeJson() đọc toàn bộ products.json vào bộ nhớ.

Một sản phẩm kiểu:

{
  "cabinet": 2,
  "position": 1,
  "code": "2212001",
  "name": "STM32 STM 32 F103 C8T6 Arm",
  "price": 95000,
  "stock": 10
}

Thường chiếm khoảng 100–160 byte trong file JSON, nhưng khi ArduinoJson parse vào RAM thì cần nhiều hơn đáng kể.

Với ESP32 của bạn đang chạy cùng lúc Wi-Fi, WebServer, TFT, MAX7219, Keypad, Servo, HTTPClient và ArduinoJson, tôi khuyên mức thực tế như sau:

Số SP / ESP32 Đánh giá
200–300 Rất an toàn
500 An toàn
700–800 Khá ổn
1.000 Có thể chạy nhưng cần theo dõi RAM
1.500+ Không khuyến nghị với cách đọc JSON hiện tại
2.000+ Dễ thiếu heap/reset/lỗi deserialize

Vì bạn đang dùng 2 tủ, tôi sẽ thiết kế khoảng:

Tủ 1: tối đa khoảng 700–800 SP
Tủ 2: tối đa khoảng 700–800 SP
--------------------------------
Tổng: khoảng 1.400–1.600 SP

mà vẫn có biên an toàn.

Nếu mục tiêu sau này lên 3.000–10.000 sản phẩm, không nên tiếp tục mỗi lần tìm lại:

deserializeJson(doc, file);
JsonArray arr = doc.as<JsonArray>();

toàn bộ file. Lúc đó nên đổi cấu trúc lưu sang nhiều file nhỏ / file index / database dạng key-value, để tìm một sản phẩm mà không phải nạp toàn bộ danh sách vào RAM.

Với hệ thống tủ của bạn hiện tại, tôi nghiêng về đặt giới hạn thiết kế 500–700 sản phẩm mỗi ESP32 để chạy ổn định lâu dài.

mở rộng thành nhiều tủ mà MASTER tìm tuần tự từng ESP32 một, thời gian tìm sẽ tăng theo số tủ.

Ví dụ nếu mỗi lần hỏi một SLAVE mất khoảng 100–300 ms:

 
5 tủ  → khoảng 0.5–1.5 giây
10 tủ → khoảng 1–3 giây
20 tủ → khoảng 2–6 giây

Nếu có tủ mất kết nối và timeout 1–2 giây thì còn chậm hơn nhiều.

Với hệ thống nhiều tủ, nên đổi kiến trúc theo hướng này: mỗi sản phẩm có trường cabinet, và MASTER có một bảng chỉ mục mã SP → số tủ. Khi tìm mã, MASTER biết ngay phải hỏi ESP32 nào, thay vì ping lần lượt tất cả tủ. Khi đó dù 5 hay 50 tủ, thời gian tìm gần như vẫn chỉ là một lần HTTP tới đúng tủ, thường vài chục đến vài trăm ms.

Ví dụ MASTER giữ index nhỏ:

{
  "2212001": 2,
  "1701028": 1,
  "2304016": 5
}

Khi tìm 2304016:

MASTER tra index
→ biết Tủ 5
→ gọi trực tiếp 192.168.1.55
→ Tủ 5 trả sản phẩm

Cách này nhanh hơn rất nhiều.

Với hệ thống của bạn, tôi khuyên nếu mở rộng trên 5–10 tủ, nên chuyển sang kiến trúc:

MASTER
  ├─ lưu index toàn hệ thống
  ├─ biết IP từng tủ
  └─ gửi lệnh trực tiếp đúng tủ

SLAVE 1 → products riêng
SLAVE 2 → products riêng
SLAVE 3 → products riêng
...

Như vậy 20–30 tủ vẫn tìm nhanh, không phải quét tuần tự từng ESP32.

Bình luận
Nội dung này chưa có bình luận, hãy gửi bình luận đầu tiên của bạn.
VIẾT BÌNH LUẬN CỦA BẠN