Ở bài trước, chúng ta đã có:
ESP32
Động cơ
Driver
Encoder
Cảm biến rơi sản phẩm
Nếu tiếp tục viết chương trình theo kiểu:
if(...)
{
...
}
if(...)
{
...
}
if(...)
{
...
}
Thì chỉ sau vài ngày, chương trình sẽ trở nên rất khó đọc và khó bảo trì.
Các máy bán hàng thương mại không lập trình như vậy.
Họ sử dụng một mô hình gọi là State Machine (Máy trạng thái).
Mục tiêu
Sau bài này bạn sẽ:
Hiểu State Machine là gì.
Biết cách chia chương trình thành từng trạng thái.
Thiết kế phần mềm giống máy bán hàng thương mại.
Viết bộ khung chương trình đầu tiên.
State Machine là gì?
Hãy tưởng tượng một chiếc thang máy.
Nó không thể:
mở cửa
chạy
đóng cửa
cùng một lúc.
Nó luôn ở một trạng thái xác định.
Ví dụ:
Đang chờ
↓
Đóng cửa
↓
Di chuyển
↓
Mở cửa
↓
Chờ
Máy bán nước cũng vậy.
Máy bán nước có bao nhiêu trạng thái?
Phiên bản mini của chúng ta sẽ có khoảng 8 trạng thái.
IDLE
↓
SELECT
↓
PAYMENT
↓
DISPENSE
↓
CHECK
↓
SUCCESS
↓
IDLE
Nếu có lỗi:
CHECK
↓
ERROR
↓
IDLE
Ý nghĩa từng trạng thái
IDLE
Máy đang chờ.
Màn hình hiển thị:
Xin chào
Mời chọn sản phẩm
Động cơ:
Không chạy.
SELECT
Khách chọn:
A1
B2
C3
D4
ESP32 lưu lại:
selectedProduct = A1;
PAYMENT
Máy chờ thanh toán.
Có thể:
QR
NFC
Tiền mặt
Nếu thanh toán thành công:
↓
DISPENSE
DISPENSE
Motor bắt đầu quay.
Motor
↓
Spiral
↓
Lon tiến lên
Encoder bắt đầu đếm.
CHECK
Motor đã dừng.
ESP32 chờ:
IR Sensor
↓
Có lon chưa?
Nếu:
Có.
↓
SUCCESS
Nếu:
Không.
↓
ERROR
SUCCESS
Hiển thị:
Cảm ơn!
Chúc ngon miệng.
Sau vài giây:
↓
IDLE
ERROR
Ví dụ:
Sản phẩm bị kẹt.
Hoặc:
Hết hàng.
Máy sẽ:
Báo lỗi
Hoàn tiền (nếu có)
Gửi log
Sơ đồ hoạt động
+------+
|IDLE |
+------+
|
v
+-------------+
|SELECT |
+-------------+
|
v
+-------------+
|PAYMENT |
+-------------+
|
v
+-------------+
|DISPENSE |
+-------------+
|
v
+-------------+
|CHECK |
+-------------+
| |
Có lon Không
| |
v v
+---------+ +-------+
|SUCCESS | |ERROR |
+---------+ +-------+
\ /
\ /
v v
+------+
|IDLE |
+------+
Khai báo State
Trong Arduino:
enum State
{
IDLE,
SELECT,
PAYMENT,
DISPENSE,
CHECK,
SUCCESS,
ERROR_STATE
};
State currentState = IDLE;
Vòng lặp chính
void loop()
{
switch(currentState)
{
case IDLE:
break;
case SELECT:
break;
case PAYMENT:
break;
case DISPENSE:
break;
case CHECK:
break;
case SUCCESS:
break;
case ERROR_STATE:
break;
}
}
Đây chính là "bộ não" của chiếc máy.
Ví dụ
Khách nhấn:
A2
ESP32:
currentState = SELECT;
Thanh toán thành công.
currentState = PAYMENT;
↓
currentState = DISPENSE;
↓
currentState = CHECK;
↓
currentState = SUCCESS;
↓
currentState = IDLE;
Không dùng delay()
Thay vì:
delay(5000);
Ta dùng:
unsigned long timer;
Ví dụ:
if(millis()-timer>5000)
{
currentState = IDLE;
}
Ưu điểm:
Máy vẫn:
đọc QR
đọc nút bấm
cập nhật màn hình
gửi WiFi
trong khi đang chờ.
Tách chương trình
Không nên viết:
main.cpp
3000 dòng
Hãy chia:
src
├── main.cpp
├── motor.cpp
├── motor.h
├── payment.cpp
├── payment.h
├── display.cpp
├── display.h
├── sensor.cpp
├── sensor.h
├── vending.cpp
└── vending.h
Sau này dự án lớn hơn vẫn rất dễ quản lý.
Nhật ký hoạt động (Log)
Mỗi giao dịch nên lưu:
20:31:20
A1
QR
SUCCESS
Hoặc:
20:35:02
B3
ERROR
NO PRODUCT
Nhật ký giúp:
tìm lỗi
thống kê doanh thu
kiểm tra bảo trì
Mở rộng trong tương lai
Khi đã có State Machine, việc thêm tính năng sẽ rất đơn giản:
IDLE
↓
UPDATE
↓
DOWNLOAD
↓
RESTART
Hoặc:
IDLE
↓
ADMIN MODE
↓
TEST MOTOR
↓
TEST SENSOR
Bạn không cần viết lại toàn bộ chương trình.
Kiến trúc hoàn chỉnh
Người dùng
│
▼
Màn hình cảm ứng
│
▼
State Machine
│
┌─────────┼─────────┐
▼ ▼ ▼
Payment Motor Sensor
│ │ │
└─────────┼─────────┘
▼
Display
│
▼
WiFi / Cloud
Đây là mô hình thường gặp trong các thiết bị IoT hiện đại, giúp mỗi thành phần có một nhiệm vụ rõ ràng và dễ bảo trì.
Bài tập
Tạo
enum Statevới 7 trạng thái như trong bài.Viết
switch(currentState)trongloop().Mỗi trạng thái chỉ cần
Serial.println()tên của nó để kiểm tra luồng hoạt động.Thử mô phỏng một giao dịch hoàn chỉnh:
IDLE → SELECT → PAYMENT → DISPENSE → CHECK → SUCCESS → IDLE.
Thử mô phỏng trường hợp lỗi:
IDLE → SELECT → PAYMENT → DISPENSE → CHECK → ERROR → IDLE.
Tổng kết
Đến bài này, bạn đã chuyển từ việc điều khiển từng linh kiện riêng lẻ sang thiết kế kiến trúc phần mềm cho cả hệ thống.
Bạn đã biết:
State Machine là gì và vì sao nó được dùng trong máy bán hàng tự động.
Cách chia chương trình thành các trạng thái rõ ràng.
Tổ chức mã nguồn theo từng module.
Sử dụng
millis()thay chodelay()để hệ thống luôn phản hồi tốt.
Bài tiếp theo
🥤 Bài 7 — Thiết kế giao diện người dùng (UI/UX) trên màn hình cảm ứng
Chúng ta sẽ xây dựng một giao diện hiện đại theo phong cách Windows/Material Design, với màn hình chọn sản phẩm, giỏ hàng, trạng thái thanh toán, hiệu ứng chuyển cảnh và các nguyên tắc UX giúp khách hàng thao tác nhanh, trực quan và hạn chế nhầm lẫn khi sử dụng máy bán nước tự động.




Không có nhận xét nào:
Đăng nhận xét