บังคับรถหุ่นยนต์ ESP32 ด้วยจอยเกมคอนโซล ผ่าน Bluepad32
ใช้ Bluepad32 ให้ ESP32 รับจอยเกมคอนโซลผ่าน Bluetooth แล้วขับรถสองล้อด้วย TB6612FNG ESP32 รุ่นแรกรองรับจอยทุกแบบ ส่วน S3/C3/C6 ได้เฉพาะ BLE และต้องแยกไฟมอเตอร์
- Bluepad32 ทำให้ ESP32 เป็นเครื่องรับจอยเกม Bluetooth ได้โดยตรง ไม่ต้องมีมือถือหรือแอปคั่นกลาง
- ESP32 รุ่นแรกรองรับจอยทั้ง Bluetooth Classic และ BLE ส่วน S3/C3/C6 ไม่มี Bluetooth Classic จึงใช้ได้เฉพาะจอย BLE
- สติ๊กอนาล็อกให้ค่าราว −512 ถึง 512 ผสมแกนหน้า-หลังกับซ้าย-ขวาเป็นความเร็วล้อซ้ายและขวา แล้วตัดค่าเกินทิ้ง
- แยกแบตมอเตอร์ออกจากไฟเลี้ยง ESP32 แต่ต่อกราวด์ร่วมกัน และหยุดมอเตอร์ทันทีเมื่อจอยหลุด
จอยเกมคอนโซลที่วางอยู่บ้านใช้บังคับรถหุ่นยนต์ ESP32 ได้โดยตรง ด้วยไลบรารี Bluepad32 ที่ทำให้ ESP32 รับจอย Bluetooth ได้เหมือนเครื่องเกม เงื่อนไขสำคัญมีสองข้อ: ESP32 รุ่นแรกเท่านั้นที่รองรับจอยทุกแบบ (รุ่น S3/C3/C6 ได้เฉพาะจอย BLE) และไฟมอเตอร์ต้องแยกจากไฟเลี้ยงบอร์ด
บทนี้เป็นคู่มือวิธีสร้าง ยังไม่ใช่บันทึกงานที่ทำเสร็จแล้ว ตัวเลขในตัวอย่างใช้แสดงวิธีคำนวณ เช็กสเปกมอเตอร์และแบตของคุณเองทุกครั้ง
เลือกบอร์ดให้ตรงกับจอย
จอยแต่ละแบบคุยกับเครื่องรับคนละโปรโตคอล บางแบบใช้ Bluetooth Classic (BR/EDR) บางแบบใช้ BLE เอกสาร Bluepad32 ระบุว่า ESP32 รุ่นแรก, Pico W และ Pico 2 W รองรับ BR/EDR ส่วน ESP32-S3, C3, C5, C6 และ H2 ไม่รองรับ
| ESP32 รุ่นแรก | ESP32-S3 / C3 / C6 | |
|---|---|---|
| Bluetooth Classic (BR/EDR) | มี | ไม่มี |
| BLE | มี | มี |
| จอย PS3 / PS4 / PS5 | ได้ | ไม่ได้ |
| Switch Pro, Joy-Con, Wii Remote | ได้ | ไม่ได้ |
| จอยตระกูล 8BitDo | ได้ | ไม่ได้ |
| Xbox Wireless รุ่น 1914 (เฟิร์มแวร์ v5.15 ขึ้นไป) | ได้ | ได้ |
| Switch 2 Pro, Switch 2 Joy-Con | ได้ | ได้ |
| Steam Controller, Stadia (เฟิร์มแวร์ Bluetooth) | ได้ | ได้ |
ถ้ายังไม่มีบอร์ด เลือก ESP32 รุ่นแรก จอยไหนในบ้านก็ใช้ได้ ดูความต่างอื่นของแต่ละรุ่นได้ในเทียบรุ่น ESP32
ตั้งค่า Arduino IDE
Bluepad32 แจกเป็น board package แยก ไม่ใช่ไลบรารีธรรมดา
- ใน Preferences เพิ่ม URL ของ board index สองอัน: ของ ESP32 ทางการ และ
package_esp32_bluepad32_index.jsonจาก reporicardoquesada/esp32-arduino-lib-builder(ลิงก์เต็มอยู่ในหน้าเอกสาร Arduino ของ Bluepad32) - Tools → Board → Boards Manager ติดตั้ง
esp32และesp32_bluepad32 - เลือกบอร์ดจาก Tools → Board → ESP32 + Bluepad32 Arduino
- เปิดตัวอย่าง File → Examples → Bluepad32_ESP32 → Controller อัปโหลด แล้วกดปุ่มจับคู่บนจอย ดูค่าที่พิมพ์ออก Serial Monitor
ทำข้อ 4 ให้ผ่านก่อนต่อมอเตอร์ จะได้รู้ว่าดันสติ๊กขึ้นแล้วค่า axisY() ออกบวกหรือลบ
ต่อวงจร: แยกไฟมอเตอร์
ใช้ไดรเวอร์มอเตอร์ TB6612FNG คุมมอเตอร์ DC สองตัว ข้อมูลจาก datasheet ของ Toshiba
- VM (ไฟมอเตอร์) ใช้งานจริง 2.5–13.5 V สูงสุดห้ามเกิน 15 V
- VCC (ไฟลอจิก) 2.7–5.5 V ต่อ 3V3 ของ ESP32 ได้
- กระแสต่อช่อง 1.2 A เฉลี่ย 3.2 A ช่วงสั้น (พัลส์ 10 ms) และในตารางช่วงใช้งานระบุ 1.0 A ที่ VM ≥ 4.5 V
- PWM ได้ถึง 100 kHz มีวงจรตัดเมื่อร้อนเกิน
- ขา STBY ต้องดึงเป็น HIGH มอเตอร์ถึงจะหมุน
| TB6612FNG | ต่อกับ |
|---|---|
| VM | ขั้วบวกแบตมอเตอร์ |
| VCC | ESP32 3V3 |
| GND | กราวด์แบตมอเตอร์ และ GND ของ ESP32 |
| AIN1 / AIN2 / PWMA | GPIO25 / 26 / 27 (ล้อซ้าย) |
| BIN1 / BIN2 / PWMB | GPIO32 / 33 / 4 (ล้อขวา) |
| STBY | GPIO21 |
| AO1-AO2 / BO1-BO2 | มอเตอร์ซ้าย / ขวา |
ทำไมต้องแยกไฟ: ตอนมอเตอร์ออกตัวหรือติดขัด กระแสพุ่งสูงและแรงดันตก ถ้าบอร์ดใช้ไฟเส้นเดียวกัน ESP32 อาจรีเซ็ตกลางทาง ให้ ESP32 ใช้ไฟจาก USB power bank หรือวงจรแปลงไฟแยก (วิธีจ่ายไฟ ESP32) ส่วนมอเตอร์ใช้แบตของมันเอง แล้วต่อกราวด์สองฝั่งเข้าด้วยกัน ถ้าลืมกราวด์ร่วม สัญญาณจาก ESP32 จะไม่มีจุดอ้างอิง มอเตอร์จะไม่ตอบสนองหรือกระตุก
ตัวอย่างเช็กแบตกับไดรเวอร์ แบตลิเธียมสองเซลล์อนุกรมเต็มที่ 2 × 4.2 = 8.4 V อยู่ในช่วง 2.5–13.5 V แต่ต้องเช็กว่ามอเตอร์ทนแรงดันนี้ได้ด้วย และกระแสตอนล้อติด (stall current) ของมอเตอร์แต่ละตัวต้องไม่เกินราว 1 A ต่อช่อง ถ้าสเปกมอเตอร์บอกสูงกว่านี้ ต้องใช้ไดรเวอร์ที่รับกระแสได้มากกว่า ก่อนใช้แบตลิเธียมอ่านความปลอดภัยของแบต LiPo และ Li-ion
ผสมสติ๊กเป็นความเร็วล้อ
เอกสาร Bluepad32 ระบุว่าสติ๊กอนาล็อกถูกปรับให้อยู่ในช่วงราว −512 ถึง 512 ส่วนไกปืนอยู่ที่ 0–1023 รถสองล้อขับแบบรถถัง (differential drive) ใช้สติ๊กซ้ายอันเดียว: แกน Y คือเดินหน้า-ถอยหลัง แกน X คือเลี้ยว
- ล้อซ้าย = เดินหน้า + เลี้ยว
- ล้อขวา = เดินหน้า − เลี้ยว
ตัวอย่างทีละขั้น ดันสติ๊กขึ้นค่อนไปทางขวา Serial อ่านได้ axisY = −400, axisX = 150 (บนจอยส่วนใหญ่ดันขึ้นค่าเป็นลบ ให้เช็กกับจอยของคุณ)
- เดินหน้า = −(−400) = 400, เลี้ยว = 150
- ล้อซ้าย = 400 + 150 = 550 → ตัดให้เหลือ 512
- ล้อขวา = 400 − 150 = 250
- แปลงเป็น PWM 8 บิต (0–255): ซ้าย 512 × 255 ÷ 512 = 255, ขวา 250 × 255 ÷ 512 ≈ 124
- ผล: ล้อซ้ายหมุนเต็มที่ ล้อขวาราวครึ่งเดียว รถโค้งไปทางขวา
สติ๊กไม่เคยคืนศูนย์พอดี ค่าจะค้างอยู่หลักสิบ ตั้ง dead zone เช่น ±40 ให้ค่าที่เล็กกว่านี้นับเป็นศูนย์ รถจะไม่คลานเองตอนปล่อยมือ
โค้ด
#include <Bluepad32.h>
const int AIN1 = 25, AIN2 = 26, PWMA = 27; // ล้อซ้าย
const int BIN1 = 32, BIN2 = 33, PWMB = 4; // ล้อขวา
const int STBY = 21;
const int DEAD = 40;
ControllerPtr pad = nullptr;
void onConnected(ControllerPtr c) { if (!pad) pad = c; }
void onDisconnected(ControllerPtr c) { if (pad == c) pad = nullptr; }
void drive(int in1, int in2, int pwm, int v) { // v: -512..512
v = constrain(v, -512, 512);
digitalWrite(in1, v > 0);
digitalWrite(in2, v < 0);
ledcWrite(pwm, abs(v) * 255 / 512);
}
void stopAll() { drive(AIN1, AIN2, PWMA, 0); drive(BIN1, BIN2, PWMB, 0); }
void setup() {
for (int p : {AIN1, AIN2, BIN1, BIN2, STBY}) pinMode(p, OUTPUT);
ledcAttach(PWMA, 20000, 8); // 20 kHz พ้นช่วงที่หูได้ยิน
ledcAttach(PWMB, 20000, 8);
digitalWrite(STBY, HIGH);
BP32.setup(&onConnected, &onDisconnected);
}
void loop() {
BP32.update();
if (!pad || !pad->isConnected()) { stopAll(); delay(10); return; } // จอยหลุด = หยุด
if (pad->hasData()) {
int fwd = -pad->axisY(), turn = pad->axisX();
if (abs(fwd) < DEAD) fwd = 0;
if (abs(turn) < DEAD) turn = 0;
drive(AIN1, AIN2, PWMA, fwd + turn);
drive(BIN1, BIN2, PWMB, fwd - turn);
}
delay(10);
}
ledcAttach(pin, freq, resolution) เป็น API ของ Arduino-ESP32 รุ่น 3.x ถ้าคอมไพล์แล้วไม่รู้จักฟังก์ชันนี้ แปลว่า core เป็นรุ่นเก่า ให้ใช้ ledcSetup กับ ledcAttachPin แทน ถ้าดันขึ้นแล้วรถถอย ลบเครื่องหมายลบหน้า axisY() ออก ถ้าเลี้ยวกลับด้าน สลับสายมอเตอร์ข้างนั้น
หยุดมอเตอร์ทุกครั้งที่จอยหลุด รถที่วิ่งค้างตามคำสั่งสุดท้ายจะชนของหรือตกโต๊ะ ทดสอบครั้งแรกโดยยกล้อให้ลอยจากพื้น
ตัวถังพิมพ์ 3 มิติ
พิมพ์แผ่นฐานที่มีรูยึดมอเตอร์ ช่องใส่แบตที่ถอดชาร์จได้ และกล่องบอร์ดแยกจากแบต ใช้ heat-set insert ที่จุดที่ต้องขันสกรูซ้ำ (วิธีใส่ insert) วางแบตไว้ต่ำและกลางรถ รถจะไม่หงายตอนออกตัวแรง
ข้อผิดพลาดที่เจอบ่อย
- ใช้บอร์ด S3/C3/C6 กับจอย PlayStation หรือ Switch Pro จับคู่ไม่ได้เพราะไม่มี Bluetooth Classic
- ไฟมอเตอร์กับบอร์ดใช้เส้นเดียว ESP32 รีเซ็ตตอนมอเตอร์ออกตัว จอยหลุดกลางทาง
- ลืมต่อกราวด์ร่วม มอเตอร์ไม่หมุนหรือกระตุก
- ลืมดึง STBY เป็น HIGH ทุกอย่างต่อถูกแต่ล้อไม่หมุน
- ไม่มี dead zone รถคลานเองตอนปล่อยสติ๊ก
- ไม่ตัดค่าเกิน 512 ค่าล้นแล้วความเร็วกระโดด
- มอเตอร์กินกระแสเกินไดรเวอร์ ชิปร้อนและตัดการทำงานเป็นช่วง ๆ
แหล่งข้อมูล
คำถามที่พบบ่อย
ทำไมจอย PS4 ต่อกับบอร์ด ESP32-S3 ไม่ได้
จอย PlayStation ทุกรุ่นที่ Bluepad32 รองรับใช้ Bluetooth Classic (BR/EDR) แต่ ESP32-S3, C3 และ C6 ไม่มี BR/EDR ต้องใช้ ESP32 รุ่นแรก หรือเปลี่ยนเป็นจอยที่อยู่ในรายการ BLE
ใช้แบตก้อนเดียวเลี้ยงทั้งมอเตอร์และบอร์ดได้ไหม
ได้ถ้ามีวงจรแปลงไฟแยกให้บอร์ดและตัวเก็บประจุพอ แต่สำหรับรถคันแรก แยกแหล่งไฟง่ายกว่าและตัดปัญหาบอร์ดรีเซ็ตตอนมอเตอร์ออกตัว ไม่ว่าแบบไหนต้องต่อกราวด์ร่วมกัน
ใช้ Bluepad32 ในงานขายได้ไหม
ตัว Bluepad32 เป็น Apache 2.0 แต่ใช้ BTstack ซึ่งฟรีเฉพาะโปรเจกต์โอเพนซอร์ส งานปิดซอร์สเชิงพาณิชย์บน ESP32 ต้องติดต่อ BTstack เรื่องสัญญาอนุญาต
วงจรและโค้ดในบทความทดสอบกับบอร์ดตัวอย่าง บอร์ดต่างรุ่นอาจใช้ขาต่างกัน ตรวจ pinout ของบอร์ดที่ใช้ก่อนต่อไฟเสมอ