จัดไปวัยรุ่น! เรื่องมีอยู่ว่า พี่กำลังเล่นกับ Mozzi ซึ่งเป็น ไลบรารีสำหรับสร้างเสียง (audio synthesis library) อยู่ดีๆ ก็คิดได้ว่า ถ้าจะใช้มันให้เต็มประสิทธิภาพจริงๆ นี่ต้องสร้างคอนโทรลเลอร์เองซะด้วยสิ
แล้วถ้าเราแค่ เสียบคีย์บอร์ด หรือ MIDI controller ไปเลยล่ะ? มันจะง่ายกว่าเยอะเลยนี่นา... อะไรนะ?! พี่ไม่รู้มาก่อนเลยว่ามันทำได้ด้วย!
ภาพรวมโปรเจค
โปรเจคนี้คือคลาสมาสเตอร์ในเรื่อง Bus Interfacing Forensics เลยทีเดียว แม้ ESP8266 จะเป็นโมดูลไร้สายเป็นหลัก แต่เราจะแปลงร่างมันให้เป็น USB Host ทรงพลังที่สามารถคุยกับ MIDI controller ระดับอุตสาหกรรม, อุปกรณ์ HID และฮาร์ดแวร์เกมมิ่งได้ ความท้าทายอยู่ที่การทำให้ ทรานซีฟเวอร์ MAX3421E ทำงานประสานกับขา bootstrap ของ ESP8266 ที่มีข้อจำกัด ซึ่งต้องอาศัยการปรับแต่งฮาร์ดแวร์แบบละเอียดยิบ และการปะผุด라이เวอร์ในระดับไลบรารี เพื่อให้ระบบมั่นคง
เลือก USB Host ของคุณ
ต้องขอบคุณอุปกรณ์จาก Circuit@Home ที่ทำให้เราทำสิ่งนี้ได้ หมายเหตุ: เว็บไซต์ Circuitathome ถูกแทนที่แล้ว นี่คือลิงก์ต้นฉบับจาก Internet Archive.
ในบทสอนนี้เราจะใช้ USB Host Mini (เวอร์ชั่นโคลน) ชิปบนบอร์ด (MAX3421E) จะทำงานที่ 3.3V เท่านั้น

ถ้าน้องอยากใช้ ไมโครคอนโทรลเลอร์ 5V (เช่น Arduino Uno / Nano) ให้ใช้ USB Host Shield แทนนะ

ก่อนจะเสียบ USB host เข้ากับไมโครคอนโทรลเลอร์ของเรา เราต้องแก้ไขปัญหาบางอย่าง ไม่งั้นมันจะไม่ทำงานแน่นอน
ลงลึกเทคนิค: แก้ปัญหาเรื่องไฟเลี้ยง
ถ้าน้องซื้อ USB Host Mini โคลน มา มันมักจะไม่มีจัมเปอร์สำหรับ VBUS นั่นหมายความว่า อุปกรณ์ USB ของน้องจะได้ไฟเลี้ยงแค่ 3.3V ซึ่งมันใช้กับอุปกรณ์ของพี่ไม่ได้เลย เพราะทุกตัวต้องการ 5V
น้องจะต้องตัดเส้นวงจร (trace) นี้เพื่อแก้ปัญหา พี่ใช้ ไขควงเล็ก ทำ แต่แนะนำว่าให้ใช้ คัตเตอร์เล็กๆ ดีกว่า
พี่ก็ไม่อยากทำแบบนี้หรอกนะ แต่พี่หาวิธีที่ดีกว่านี้ไม่ได้ นอกจากซื้อของแท้ :-)
VBUS Power Forensics (การเปลี่ยนผ่านสู่ 5V):
- จุดอ่อน 3.3V: USB Host Mini โคลนส่วนใหญ่จะต่อสาย VBUS (เส้นไฟเลี้ยง USB) ผิดไปที่เรล 3.3V เนื่องจากอุปกรณ์ USB มาตรฐาน (คีย์บอร์ด, MIDI) ทำงานบนลอจิก/ไฟเลี้ยง 5V พวกมันจึงไม่สามารถเริ่มทำงานหรือไฟตกได้
- การแยกเส้นวงจรแบบละเอียด: การแก้ไขต้องอาศัยการตัดเส้น VBUS ให้ขาดออกจาก LDO 3.3V อย่างแม่นยำ จากนั้นให้เชื่อม VBUS ไปที่ขา RAW 5V อินพุต บอร์ดก็จะสามารถจ่ายกระแสที่จำเป็นให้กับอุปกรณ์พ่วงที่กินไฟสูงได้ โดยไม่กระทบกับสัญญาณลอจิก 3.3V ของ MAX3421E
สู้งานนะน้อง! ห้ามช็อตนะตัวนี้
ต่อ USB Host เข้าไปเลยวัยรุ่น
โอเค หวังว่าน้องยังไม่ได้ทำบอร์ดพังนะ! ถึงเวลาต่อสายแล้ววว
พี่ใช้ Wemos Mini D1 กับ USB Host Mini อุปกรณ์ของพี่ วิธีต่อตามนี้เลย
พี่ไม่ได้ต่อขา INT นะ มันก็ทำงานได้ปกติดีโดยไม่ต้องใช้
- D3 -- SS
- D7 -- MISO
- D6 -- MOSI
- D5 -- SCK
- RST -- RST
วิเคราะห์ปัญหาความขัดแย้งของ SPI Bus กับ Boot-Strap:
- ปัญหาของ GPIO15 (D8): บน Wemos D1 Mini นะ GPIO15 (D8) ทำหน้าที่เป็น SPI SS (Slave Select) มาตรฐาน แต่ว่า! GPIO15 เนี่ยมันเป็น Boot-Strap Pin ด้วย ซึ่งต้องถูกดึงให้เป็น LOW ตอนเปิดเครื่อง ถ้าน้องไปต่อ SS ของ USB Host เข้ากับ D8 บ่อยๆ มันจะทำให้ ESP8266 เข้าไปอยู่ในสถานะบูตที่ไม่ถูกต้อง หรือโหมดอัพโหลดเฟิร์มแวร์แทน
- การเปลี่ยนเส้นทางไปที่ GPIO0 (D3): โปรเจกต์นี้เลี่ยงปัญหาด้วยการย้ายสัญญาณ SS ไปที่ GPIO0 (D3) แทน การย้ายตำแหน่งแบบนักสืบสายช่างนี้ทำให้ ESP8266 บูตเข้าโปรแกรม SPI ได้ถูกต้อง พร้อมกับยังควบคุมจังหวะการเลือกชิป (chip-select) ของ USB transceiver ได้เต็มที่อีกด้วย
แก้ไขไลบรารี USB Host
น้องอาจจะสังเกตเห็นแล้วว่า SS (Chip Select/CS) ถูกต่อที่ D3 แทนที่จะเป็น D8
พี่เคยเจอปัญหาที่ USB Host ดันทำให้ Wemos Mini ของพี่เข้าโหมด อัพโหลดเฟิร์มแวร์ ซะงั้น ซึ่งมันจะ ทำให้สเก็ตช์รันไม่ได้
เพื่อที่จะ เปลี่ยนพิน SS เราต้องไปแก้โค้ดในไลบรารีสักบรรทัดนึง
ดาวน์โหลด USB Host Shield Library 2.0 มาให้เรียบร้อย

เปิดไฟล์ UsbCore.h ในโฟลเดอร์ libraries/USB_Host_Shield_Library_2.0 แล้วเปลี่ยนบรรทัดนี้
#elif defined(ESP8266)typedef MAX3421e<P15, P5> MAX3421E; // ESP8266 boards
เป็นบรรทัดนี้แทน
#elif defined(ESP8266)typedef MAX3421e<P0, P5> MAX3421E; // ESP8266 boards
สังเกตนะ เราใช้ P0 แทนที่จะเป็น D3 เพราะว่า GPIO0 ก็คือ D3 บน Wemos Mini นั่นเอง
การแพตช์ไดรเวอร์ในระดับไลบรารี:
- การสืบสวนใน UsbCore.h: เพื่อให้รองรับการแมปพินที่ไม่มาตรฐาน ต้องแพตช์โค้ดแกนของ
USB_Host_Shield_Library_2.0โดยการเปลี่ยนMAX3421etypedef จากP15เป็นP0ไดรเวอร์จะถูกกำหนดเป้าหมายใหม่ให้จัดการกับการกำหนดเส้นทาง SS ใหม่ในระดับรีจิสเตอร์ฮาร์ดแวร์
ดาวน์เกรด ESP8266 Core
โอเค ใกล้เสร็จแล้ว ถ้าน้องลองคอมไพล์ตัวอย่างจากไลบรารีดู มันจะไม่ทำงานแน่นอน
หมายเหตุ : ปัญหานี้เพิ่งถูกแก้ไปไม่นาน ดังนั้นขั้นตอนนี้จำเป็นก็ต่อเมื่อเวอร์ชันของ USB Host Shield Library 2.0 ที่น้องใช้เป็น 1.3.2 หรือต่ำกว่า เท่านั้น
สาเหตุคือไลบรารีไม่เข้ากับ Arduino ESP8266 Core เวอร์ชัน 2.5 น้องต้องดาวน์เกรดมันกลับไปเป็น 2.4.2
ไปที่ Tools / Boards / Boards Manager แล้วเปลี่ยนเวอร์ชันเป็น 2.4.2

วิศวกรรมและการนำไปใช้: การจัดการเวอร์ชัน ESP8266 Core
- การดาวน์เกรดสู่ 2.4.2: ESP8266 Arduino Core บางเวอร์ชันมีการเปลี่ยนแปลงที่ทำให้การทำงานของ SPI พัง โปรเจกต์นี้ระบุว่าเวอร์ชัน 2.4.2 เป็นพื้นฐานที่เสถียรสำหรับการเริ่มต้น MAX3421E และการนับ descriptor แบบรับประกันได้
ทดสอบ USB Host ของคุณให้ชัวร์
ใน USB Host Shield Library 2.0 มีตัวอย่างให้เล่นเพียบเลยครับน้อง! อยากรู้ว่าเซ็ตอัพถูกต้องไหม ให้ลองรัน USB_desc ตัวอย่างนี้ก่อนเลย

- อัพโหลด โค้ด USB_desc เข้าไป
- เปิด Serial Monitor (ตั้งค่าเป็น 115200)
- เสียบอุปกรณ์ USB เข้าไป
- กดรีเซ็ตบอร์ด (ESP8266) ซะ
หมายเหตุ: ตัวอย่างนี้ยังไม่รองรับการ Hot-plug นะจ๊ะ (อย่างน้อยก็ในตัวอย่าง) เพราะงั้นทุกครั้งที่ ถอดหรือเสียบ อุปกรณ์ใหม่ ต้อง กดรีเซ็ตบอร์ด ใหม่ด้วย
ถ้าทุกอย่างโอเค น้องควรจะเห็นข้อความประมาณนี้โผล่มา:
01
--
Device descriptor:
Descriptor Length: 12
Descriptor type: 01
USB version: 0200
Device class: FF
Device Subclass: FF
Device Protocol: FF
Max.packet size: 08
Vendor ID: 045E
Product ID: 028E
Revision ID: 0114
Mfg.string index: 01
Prod.string index: 02
Serial number index: 03
Number of conf.: 01
Configuration descriptor:
Total length: 0099
Num.intf: 04
Conf.value: 01
Conf.string: 00
Attr.: A0
Max.pwr: FA
Interface descriptor:
Intf.number: 00
Alt.: 00
Endpoints: 02
Intf. Class: FF
Intf. Subclass: 5D
Intf. Protocol: 01
Intf.string: 00
Unknown descriptor:
Length: 11
Type: 21
Contents: 0001012581140000000013010800000705
Endpoint descriptor:
Endpoint address: 81
Attr.: 03
Max.pkt size: 0020
Polling interval: 04
Endpoint descriptor:
Endpoint address: 01
Attr.: 03
Max.pkt size: 0020
Polling interval: 08
Interface descriptor:
Intf.number: 01
Alt.: 00
Endpoints: 04
Intf. Class: FF
Intf. Subclass: 5D
Intf. Protocol: 03
Intf.string: 00
Unknown descriptor:
Length: 1B
Type: 21
Contents: 000101018240010220168300000000000016030000000000000705
Endpoint descriptor:
Endpoint address: 82
Attr.: 03
Max.pkt size: 0020
Polling interval: 02
Endpoint descriptor:
Endpoint address: 02
Attr.: 03
Max.pkt size: 0020
Polling interval: 04
Endpoint descriptor:
Endpoint address: 83
Attr.: 03
Max.pkt size: 0020
Polling interval: 40
Endpoint descriptor:
Endpoint address: 03
Attr.: 03
Max.pkt size: 0020
Polling interval: 10
Interface descriptor:
Intf.number: 02
Alt.: 00
Endpoints: 01
Intf. Class: FF
Intf. Subclass: 5D
Intf. Protocol: 02
Intf.string: 00
Unknown descriptor:
Length: 09
Type: 21
Contents: 000101228407000705
Endpoint descriptor:
Endpoint address: 84
Attr.: 03
Max.pkt size: 0020
Polling interval: 10
Interface descriptor:
Intf.number: 03
Alt.: 00
Endpoints: 00
Intf. Class: FF
Intf. Subclass: FD
Intf. Protocol: 13
Intf.string: 04
Unknown descriptor:
Length: 06
Type: 41
Contents: 000101034A20
Addr:1(0.0.1)
วิศวกรรมและการนำไปใช้: ไดรเวอร์สำหรับอุปกรณ์หลายประเภท
- HID/Boot Protocol: โชว์ฟีเจอร์การดักจับ Raw Scan Codes จากคีย์บอร์ดและแพ็กเก็ตการเคลื่อนไหวของเมาส์ได้ชัดเจน
- MIDI Buffer Parsing: แยก Event พวก Note ON (0x90) และ Note OFF (0x80) ออกมาจาก USB bulk transfer buffer โดยตรง ทำให้ ESP8266 ของเรากลายเป็นโหนดซินธิไซเซอร์ MIDI แบบไร้สายได้เลย
- XBOX 360 Forensics: ใช้ไดรเวอร์พิเศษอย่าง XBOXUSB ในการอ่านค่าจาก Hat-Switch (ช่วง -32768 ถึง 32768) และสั่งให้คอนโทรลเลอร์สั่นผ่าน USB bus ได้ด้วย
- Interrupt-Free Operation: แม้ MAX3421E จะมีพิน INT (Interrupt) แต่โปรเจคนี้เลือกใช้ สถาปัตยกรรมการ Polling ความเร็วสูง แทน โดยการเรียก
Usb.Task()ในลูปหลัก ทำให้ ESP8266 จัดการแพ็กเก็ต USB ที่ใช้เวลาไม่ถึงมิลลิวินาทีได้ โดยไม่ต้องไปยุ่งยากกับ ISR-driven SPI transactions ให้วุ่นวาย สบายๆ
ตัวอย่างจอย XBOX 360
ถ้าน้องมี จอย XBOX360 for PC ล่ะก็ ใช้ตัวอย่าง XBOX/XBOXUSB ไปเลย
- L2/R2 จะสั่นเมื่อกด
- L2/R2 : 0/255
- Stick (Hat) X/Y : -32768 / 32768
- ควบคุมไฟ LED ได้ด้วยนะเว้ย! (ลองกดปุ่ม pad / back / start / xbox ดูสิ)
ตัวอย่างคีย์บอร์ด
เปิดตัวอย่าง HID/USBHIDBootKbd ขึ้นมา
ตัวอย่างนี้จะตรวจจับการกด/ปล่อยปุ่ม และปุ่มพิเศษ (SHIFT/CTRL อะไรพวกนั้น)
DN >07<
ASCII: d
UP >07<
ตัวอย่างเมาส์
อันนี้พี่ยังเซ็ตให้เมาส์ใช้งานได้ไม่สำเร็จเลย ตัวอย่างอยู่ที่ HID/USBHIDBootMouse นะ
ตัวอย่าง MIDI Controller
เปิดตัวอย่าง USBH_MIDI/USBH_MIDI_dump ดู
MIDI ตัวส่วนใหญ่จะใช้พื้นที่บัฟเฟอร์ (bufMidi) แค่ส่วนนิดเดียวเอง
00001679: 64: 09 90 2F 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000174A: 64: 08 80 2F 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- bufMidi[0] : Note ON (9) / Note OFF (8)
- bufMidi[2] : โน้ต
- bufMidi[3] : ความแรง (Velocity)
สรุปสั้นๆ
การใช้อุปกรณ์ USB เปิดโลกให้ไมโครคอนโทรลเลอร์ของเรามากโข พี่ว่าเราแค่แตะผิวน้ำของสิ่งที่ทำได้กับเจ้า USB Host ตัวนี้เท่านั้นแหละ
- ใช้ USB Hub ต่ออุปกรณ์หลายชิ้นพร้อมกันได้
- ทำไมพินบนบอร์ด USB Host ถึงเยอะจัง? ก็เพราะมันมี GPIO ไงน้อง! จะต่อ ปุ่มกด / ไฟ LED หรือแม้แต่ จอ LCD เข้าไปก็ยังได้!
- ต่อ แฟลชไดรฟ์ USB ใช้งานก็ได้นะ
บทส่งท้าย
USB-Host คือสะพานเชื่อมระหว่างโลก ไมโครคอนโทรลเลอร์ฝังตัว กับ ระบบอุปกรณ์รอบข้างของคอมพ์ เลยล่ะ ถ้าน้องเข้าใจเรื่อง การจัดการไฟ VBUS และ การแก้ปัญหาความขัดแย้งตอนบูต แล้วล่ะก็ จะหยิบจับอุปกรณ์ USB อะไรมาใช้สร้างสรรค์โปรเจกต์เจ๋งๆ ไม่ว่าจะเป็นซินธิไซเซอร์, คอนโซลเกมสั่งทำ หรือระบบ HMI สำหรับ IoT ก็ทำได้หมด จัดไปวัยรุ่น!
สุดยอดฝีมือ: เอาชนะบัส USB ผ่านการวิเคราะห์ฮาร์ดแวร์แบบฉลาดๆ