Lab.Vatli365
← Cấp độ 2 · Tín hiệu tương tự và PWM

Bài 11 · Công nghệ · Tin học · Toán

Cần điều khiển joystick hai trục
hai biến trở đặt vuông góc, và bài toán vùng chết

Joystick trông như một linh kiện mới, nhưng bên trong chỉ là hai biến trở của Bài 12 đặt vuông góc nhau cộng một nút nhấn của Bài 2. Cái mới nằm ở chỗ khác: cần điều khiển không bao giờ về đúng giữa, và chương trình phải sống chung với điều đó.

Thời lượng
1 tiết (45 phút)
Chi phí một nhóm
≈ 15 nghìn đồng (chưa tính bo)
Cấp độ
2 / 12
Trạng thái
Đã có phần cứng, đang hoàn thiện
01

Mục tiêu và yêu cầu cần đạt

Bài 12 đọc một biến trở. Bài này đọc hai, cộng thêm một nút — nghe như chỉ là phép cộng. Nhưng có một thứ hoàn toàn mới: số đọc không bao giờ đứng yên, kể cả khi không ai chạm vào cần. Xử lí sự không đứng yên đó là nội dung chính.

Sau bài này học sinh làm được

  • Giải thích joystick chỉ là hai biến trở đặt vuông góc cộng một nút nhấn.
  • Đo được dao động tự nhiên của số đọc khi để yên cần.
  • Chọn được vùng chết từ số liệu đo chứ không chép của nhóm khác.
  • Viết hàm đổi một số đọc thành ba trạng thái −1, 0, 1.
  • Nhận biết bốn hướng và trạng thái nhấn cần.
Gắn với chương trình
  • Toán 10 — toạ độ trên mặt phẳng, khoảng giá trị, sai số ngẫu nhiên.
  • Tin học 10 — hàm có giá trị trả về, cấu trúc rẽ nhánh nhiều nhánh.
  • Công nghệ 10 — thiết bị nhập dạng tương tự.

Trích nguyên văn yêu cầu cần đạt từ Phụ lục Thông tư 32/2018 trước khi đưa vào kế hoạch bài dạy.

02

Lí thuyết

Mở nắp ra chẳng có gì mới

Hai trục quay vuông góc, mỗi trục xoay con chạy của một biến trở. Đẩy cần đi đâu thì hình chiếu của chuyển động lên mỗi trục làm biến trở tương ứng đổi giá trị. Ấn cần xuống thì chạm một nút nhấn nằm dưới.

Tức joystick là Bài 12 nhân đôi cộng Bài 2 — phần lớn linh kiện "phức tạp" hoá ra chỉ là thứ đã biết ghép lại.

Vì sao số đọc không bao giờ đứng yên

Để yên cần mà in liên tục, số nhảy quanh một giá trị: 1890, 1902, 1887, 1895… Ba nguyên nhân chồng lên nhau — lò xo không đưa cần về đúng một điểm, tiếp xúc trong biến trở có điện trở thay đổi, và bản thân ADC cũng có nhiễu.

So sánh trực tiếp với một mốc thì kết quả chớp qua lại giữa "trái" và "giữa" hàng chục lần mỗi giây. Đó không phải lỗi lập trình mà là tính chất của mọi phép đo thật.

Vùng chết là câu trả lời

Dùng hai mốc thay vì một: dưới mốc thấp là một hướng, trên mốc cao là hướng ngược, ở giữa coi như đứng yên. Khoảng giữa ấy phải rộng hơn dao động tự nhiên của số đọc.

Cách chọn: để yên cần vài giây, ghi giá trị nhỏ nhất và lớn nhất, lấy nửa hiệu rồi cộng chút dự phòng. Đây là lần đầu học sinh đo để chọn tham số thay vì nhận một con số cho sẵn.

Cấp 3V3, không phải 5 V

Chân module ghi +5V vì vốn dành cho bo 5 V. Cấp 5 V thì đầu ra hai biến trở có thể lên 5 V, vượt mức chân ESP32 chịu được và làm hỏng chân. Cấp 3V3 thì số đọc trải đúng 0–4095 mà không có rủi ro nào.

03

Linh kiện

Linh kiệnMô tả, lưu ý khi muaSố lượng · giá
ESP32 DevKit V1Bo 30 chân, cổng micro-USB hoặc Type-C1 · 90–150k
Module joystick hai trụcLoại năm chân GND, +5V, VRx, VRy, SW1 · 12–18k
LED 5 mm và điện trở 220 ΩĐể thấy kết quả ngay trên mạch1 · 700đ
Dây nối cái–đựcModule joystick dùng chân cắm đực5 · 3k
04

Lắp mạch và sơ đồ nối dây

Đỏ · nguồn dương Đen · GND Vàng · SCL Xanh dương · SDA Trắng · tín hiệu Xanh lá · 1-Wire
KhốiNối dâyNguồn
Trục XVRx → GPIO34Bắt buộc nhóm ADC1
Trục YVRy → GPIO35Bắt buộc nhóm ADC1
Nút cầnSW → GPIO32Dùng điện trở kéo lên trong chip, nhấn đọc ra 0
Nguồn module+5V → 3V3 của ESP32 · GND → GNDCấp 3V3 chứ không phải 5 V — xem hộp cảnh báo
B11.1 Ảnh thực

Module joystick cắm năm dây vào ESP32, kèm LED trên breadboard. Nhãn VRx, VRy, SW trên module đọc được rõ.

Khung giữ chỗ — thay bằng ảnh chụp thật khi có
B11.2 Hình vẽ

Mặt cắt joystick: hai biến trở đặt vuông góc, mỗi cái gắn với một trục quay của cần; bên dưới là một nút nhấn thường mở.

Khung giữ chỗ — thay bằng ảnh chụp thật khi có
B11.3 Hình vẽ

Mặt phẳng toạ độ 0–4095 theo cả hai trục, ở giữa vẽ một hình vuông tô nhạt ghi "vùng chết", bốn phía ngoài ghi TRÁI, PHẢI, LÊN, XUỐNG.

Khung giữ chỗ — thay bằng ảnh chụp thật khi có

Năm dây

  1. GND của module → GND của ESP32.
  2. +5V của module → 3V3 của ESP32.
  3. VRx → GPIO34.
  4. VRy → GPIO35.
  5. SW → GPIO32.
  6. LED: GPIO23 → điện trở 220 Ω → chân dài, chân ngắn → GND.
Vì sao bắt buộc GPIO34 và GPIO35

ESP32 có hai nhóm ADC. Nhóm thứ hai ngừng đọc được khi Wi-Fi bật, mà runtime của lộ trình này giữ Wi-Fi bật suốt để nhận lệnh nạp mã. Vì vậy cả hai trục đều phải nằm trong nhóm ADC1 — tức các chân từ GPIO32 đến GPIO39. Cắm nhầm sang chân khác thì số đọc đứng im ở 0 và rất khó đoán ra lí do.

GPIO34 và GPIO35 chỉ vào được, không ra được

Hai chân này không có mạch ra và cũng không có điện trở kéo lên bên trong. Với joystick thì không sao vì ta chỉ đọc. Nhưng đừng thử dùng chúng cho LED ở bài khác — sẽ không sáng và không có thông báo lỗi nào.

05

Mã nguồn

Toàn bộ mã trong bài chạy trên MicroPython 1.28 cho ESP32. Số đọc giữa của mỗi module một khác — mọi con số trong bài chỉ là ví dụ, học sinh phải đo lại trên chính bo của mình.

Bước 0 — chỉ in hai số
# ------------------------------------------------------------
#  Bai 11 - Buoc 0: chi in hai so, chua ket luan gi
# ------------------------------------------------------------
from machine import ADC, Pin
import time

x = ADC(Pin(34)); x.atten(ADC.ATTN_11DB)
y = ADC(Pin(35)); y.atten(ADC.ATTN_11DB)

while True:
    print(x.read(), y.read())
    time.sleep_ms(200)

Đẩy cần đi bốn phía và quan sát. Học sinh sẽ tự rút ra: một số đổi khi đẩy ngang, số kia đổi khi đẩy dọc, đẩy chéo thì cả hai cùng đổi. Cả lớp cũng sẽ nhận ra ngay chuyện số không đứng yên — và đó là câu hỏi mở đầu tự nhiên nhất cho mục Lí thuyết.

Chương trình đo — tự tìm vùng chết cho bo của mình
# Do vung chet: de yen can 5 giay roi doc lai bang so lieu
from machine import ADC, Pin
import time

x = ADC(Pin(34)); x.atten(ADC.ATTN_11DB)

nho_nhat = 4095
lon_nhat = 0
moc = time.ticks_ms()

while time.ticks_diff(time.ticks_ms(), moc) < 5000:
    v = x.read()
    if v < nho_nhat:
        nho_nhat = v
    if v > lon_nhat:
        lon_nhat = v
    time.sleep_ms(10)

print("Nho nhat:", nho_nhat)
print("Lon nhat:", lon_nhat)
print("Giua nen dat:", (nho_nhat + lon_nhat) // 2)
print("Vung chet toi thieu:", (lon_nhat - nho_nhat) // 2 + 50)

Năm giây để yên cần, máy tự tính hộ giá trị giữa và vùng chết tối thiểu. Ghi lại hai con số này rồi chép vào chương trình chính. Mỗi nhóm sẽ có một cặp số khác nhau — đó là điều bình thường và rất nên nói rõ.

Chương trình chính — bốn hướng có vùng chết
# ------------------------------------------------------------
#  Bai 11 - Bon huong co vung chet
# ------------------------------------------------------------
from machine import ADC, Pin
import time

# ===== CẤU HÌNH =====
CHAN_X = 34
CHAN_Y = 35
CHAN_NUT = 32
GIUA = 1900               # so doc khi de yen - DO LAI tren bo cua minh
VUNG_CHET = 600           # rong hon dao dong tu nhien luc de yen
NGHI = 150                # ms
# ====================

x = ADC(Pin(CHAN_X)); x.atten(ADC.ATTN_11DB)
y = ADC(Pin(CHAN_Y)); y.atten(ADC.ATTN_11DB)
nut = Pin(CHAN_NUT, Pin.IN, Pin.PULL_UP)

def huong(gia_tri):
    if gia_tri < GIUA - VUNG_CHET:
        return -1
    if gia_tri > GIUA + VUNG_CHET:
        return 1
    return 0

while True:
    hx = huong(x.read())
    hy = huong(y.read())

    if hx == -1:
        print("TRAI")
    elif hx == 1:
        print("PHAI")
    elif hy == -1:
        print("XUONG")
    elif hy == 1:
        print("LEN")

    if nut.value() == 0:
        print("NHAN CAN")

    time.sleep_ms(NGHI)

Hàm huong() nén cả một dải 4096 giá trị xuống còn ba trạng thái. Đây là một ý tưởng quan trọng: phần lớn chương trình điều khiển không cần biết con số chính xác, chỉ cần biết "về phía nào".

Em mô tả bằng tiếng Việt, hệ thống dựng thuật toán và chương trình, rồi nạp thẳng xuống ESP32 qua Wi-Fi.

06

Hiệu chuẩn và kiểm thử

Đo dao động tự nhiên của hai trục

Chạy chương trình đo hai lần, một lần cho trục X và một lần cho trục Y (đổi Pin(34) thành Pin(35)). Ghi vào bảng.

TrụcNhỏ nhấtLớn nhấtGiá trị giữaVùng chết tối thiểu
X…………
Y…………

Đo bốn cực trị

Đẩy cần hết cỡ về từng phía và ghi số đọc. Bảng này cho biết dải làm việc thật của module, và thường không đối xứng — một chi tiết hay để bàn về sai số của linh kiện rẻ.

Vị trí cầnSố đọc XSố đọc YNhận xét
Hết cỡ trái………
Hết cỡ phải………
Hết cỡ lên………
Hết cỡ xuống………
Để yên………

Thử vùng chết quá hẹp

Đặt VUNG_CHET xuống 20 rồi chạy lại, để yên cần và quan sát. Máy sẽ in ra hướng liên tục dù không ai chạm vào. Cho học sinh thấy hiện tượng này trước khi trả về giá trị đo được — nó biến vùng chết từ một quy tắc thành một nhu cầu.

Danh sách kiểm trước khi coi là xong

  • Để yên cần thì không có dòng hướng nào được in.
  • Bốn hướng đều nhận đúng và không lẫn sang nhau.
  • Nhấn cần xuống thì in ra thông báo, thả ra thì thôi.
  • Hai bảng số liệu đã điền đủ bằng số đo thật.
  • Học sinh giải thích được vì sao vùng chết của nhóm mình khác nhóm bên.
07

Kinh nghiệm và cạm bẫy

Bài này dạy một thói quen quan trọng hơn cả joystick

Đến đây là lần đầu học sinh phải đo trước, chọn tham số sau. Nếu cả lớp chép chung một con số vùng chết, bài mất hẳn giá trị. Cách giữ được ý nghĩa: yêu cầu mỗi nhóm dán số đo của mình lên bảng, rồi cùng nhìn xem các nhóm khác nhau bao nhiêu.

Bốn lỗi hay gặp
  • Một trục luôn đọc ra 0. Cắm vào chân ngoài nhóm ADC1, hoặc cắm nhầm VRy vào chân không phải ADC.
  • Hai trục đổi vai. Cắm ngược VRx và VRy; đổi lại hai dây là xong, không cần sửa mã.
  • Hướng bị ngược. Module xoay 180° so với hình. Đảo dấu so sánh trong hàm huong(), hoặc đơn giản là xoay module lại.
  • Nút cần không nhận. Quên Pin.PULL_UP — chân SW không có điện trở kéo lên trên module.
Chỗ này cần một chuyện thật của chính thầy cô

Các hộp trên viết từ quá trình dựng và chạy thử bài. Sau buổi dạy đầu tiên, xin thầy cô thay bằng tình huống đã thực sự xảy ra ở lớp mình.

08

Ứng dụng trong dạy học

Toán 10Tin học 10Công nghệ 10STEM

Toán — toạ độ và sai số ngẫu nhiên gặp nhau

Hai số đọc là một cặp toạ độ thật, và tập hợp các điểm đo được khi để yên cần là một đám mây quanh tâm. Học sinh có thể vẽ đám mây ấy lên giấy kẻ ô, rồi thấy vùng chết chính là hình vuông bao lấy đám mây. Khái niệm "sai số ngẫu nhiên" trở nên nhìn thấy được.

Tin học — hàm trả về giá trị

huong() là ví dụ gọn cho một hàm nhận vào một số và trả ra một trong ba khả năng. Học sinh thường viết đi viết lại cùng một chuỗi if cho cả hai trục; đây là lúc chỉ ra rằng gói nó thành hàm vừa ngắn hơn vừa dễ sửa hơn.

Bài học STEM 5 hoạt động — "Tay cầm điều khiển cho trò chơi của lớp"

Hoạt động 1 — Xác định vấn đề. Lớp muốn làm một trò chơi nhỏ hiển thị trên máy tính, nhưng gõ bàn phím thì không có cảm giác điều khiển.

Tiêu chí sản phẩm: nhận đủ bốn hướng và nút bấm; để yên tay thì nhân vật phải đứng im hoàn toàn trong 30 giây; độ trễ cảm nhận được phải nhỏ.

Hoạt động 2 — Kiến thức nền. ADC và nhóm ADC1; vùng chết; hàm trả về; nhịp lặp ảnh hưởng tới độ trễ.

Hoạt động 3 — Đề xuất và chọn phương án. Hai tiêu chí đối chọi nhau: vùng chết rộng thì đứng im tốt nhưng cần phải đẩy xa mới ăn; nhịp lặp nhanh thì đỡ trễ nhưng in ra quá nhiều. Các nhóm phải tìm điểm cân bằng và giải thích được.

Hoạt động 4 — Chế tạo và thử. Đo, chọn tham số, thử đúng 30 giây để yên tay.

Hoạt động 5 — Báo cáo và điều chỉnh. Đổi tay cầm giữa các nhóm; nhóm nào để yên mà vẫn trôi thì phải đo lại chứ không được đoán.

Hướng mở rộng
  • Ghép servo của cấp độ 6 để cần điều khiển quay được một cánh tay thật.
  • Dùng độ lệch khỏi tâm làm tốc độ chứ không chỉ làm hướng — một trục analog thật sự.
  • Ghép ESP-NOW ở cấp độ 7 để tay cầm điều khiển xe không dây.
  • Hướng thi Khoa học kĩ thuật: tay cầm hỗ trợ người khó thao tác chuột.