ESP32-S3

Maker Motor Driver






N20 Micro Metal Gear Motor with Encoder – 6V 33RPM




จากอุปกรณ์ที่คุณมี
- ESP32-S3
- Maker Drive
- N20 Gear Motor + AB Encoder จำนวน 1 หรือ 2 ตัว
- Battery 7.4V (18650 จำนวน 2 ก้อน)
เราจะทดลองให้มอเตอร์หมุน พร้อมอ่านค่า Encoder แบบ Real-Time
1. ทำความเข้าใจ Maker Drive
Maker Drive เป็นโมดูล H-Bridge แบบ Dual Channel
สามารถควบคุมมอเตอร์ DC ได้ 2 ตัว
แต่ละช่องมี
Motor A
- DIRA
- PWMA
Motor B
- DIRB
- PWMB
หลักการทำงาน
DIR = ทิศทางหมุน
PWM = ความเร็ว
ตัวอย่าง
DIR = HIGH
PWM = 150
=> หมุนไปข้างหน้า
DIR = LOW
PWM = 150
=> หมุนย้อนกลับ
2. ขั้วต่อบน N20 Encoder
มอเตอร์ N20 Encoder ส่วนใหญ่จะมีสาย 6 เส้น
Motor +
Motor -
Encoder VCC
Encoder GND
Encoder A
Encoder B
ตัวอย่างสีที่พบบ่อย
แดง = Motor +
ดำ = Motor -
เหลือง = Encoder A
ขาว = Encoder B
น้ำเงิน = VCC
เขียว = GND
แต่ควรตรวจสอบจาก Datasheet หรือใช้มัลติมิเตอร์วัดก่อน
3. การต่อวงจร
มอเตอร์
N20
Motor+ -------- MA+
Motor- -------- MA-
บน Maker Drive
MA+
MA-
4. ต่อ Encoder เข้ากับ ESP32-S3
Encoder ของ N20 เป็นแบบ Quadrature Encoder
มีสัญญาณ
A
B
ต่อดังนี้
Encoder VCC
→ 3.3V
Encoder GND
→ GND
Encoder A
→ GPIO4
Encoder B
→ GPIO5
สรุป
ESP32-S3
GPIO4 ← Encoder A
GPIO5 ← Encoder B
3.3V ← Encoder VCC
GND ← Encoder GND
5. ต่อ Maker Drive กับ ESP32-S3
ตัวอย่างกำหนดขา
DIRA → GPIO12
PWMA → GPIO13
ต่อสาย
Maker Drive
DIRA ← GPIO12
PWMA ← GPIO13
GND ← GND
สำคัญมาก
ESP32 GND
Maker Drive GND
Battery GND
ต้องต่อร่วมกันทั้งหมด
Common Ground
6. การต่อแบตเตอรี่
ใช้แบตเตอรี่ 18650 จำนวน 2 ก้อน
3.7V + 3.7V
ต่ออนุกรม
7.4V Nominal
8.4V Full Charge
ต่อเข้าที่
Battery +
→ VMOT
Battery -
→ GND
บน Maker Drive
แผนผังรวม
Battery 7.4V
|
|
v
+----------------+
| Maker Drive |
| |
| VMOT GND |
| |
| DIRA PWMA |
+----------------+
| |
| |
GPIO12 GPIO13
| |
+----------------+
| ESP32-S3 |
+----------------+
GPIO4 <--- Encoder A
GPIO5 <--- Encoder B
3V3 ---> Encoder VCC
GND ---> Encoder GND
Maker MA+ ---> Motor+
Maker MA- ---> Motor-
7. หลักการอ่าน Encoder
Encoder จะสร้าง Pulse
ตัวอย่าง
A
__----__----__----__
B
----__----__----__--
ESP32 จะนับ Pulse
Pulse = 100
หมายความว่า
ล้อหมุนไปแล้ว 100 Tick
8. วิธีคำนวณรอบหมุน
สมมุติ Encoder
12 PPR
และ Gear Ratio
1:30
จะได้
12 × 30
= 360 Pulse / รอบล้อ
ถ้านับได้
360 Pulse
แปลว่า
ล้อหมุนครบ 1 รอบ
9. วิธีคำนวณระยะทาง
สมมุติล้อ
เส้นผ่านศูนย์กลาง
32 mm
เส้นรอบวง
π × D
3.1416 × 32
= 100.5 mm
ล้อหมุน 1 รอบ
= 100.5 mm
ถ้า Encoder
360 Pulse
จะได้
1 Pulse
=
100.5 / 360
=
0.279 mm
ดังนั้น
100 Pulse
=
27.9 mm
10. เป้าหมายการทดลองแรก
ทดลอง 3 อย่าง
ทดสอบที่ 1
PWM = 100
หมุน 3 วินาที
หยุด
ทดสอบที่ 2
แสดงค่า Encoder
Pulse Count
บน Serial Monitor
ทดสอบที่ 3
สั่งให้หยุดเมื่อครบ
500 Pulse
เช่น
while(pulseCount < 500)
{
Motor Forward
}
Motor Stop
จากตัวอย่างการต่อวงจรก่อนหน้า
DIRA -> GPIO12
PWMA -> GPIO13
DIRB -> GPIO14
PWMB -> GPIO15
ไฟล์ motordriver_1.0.ino
/*
motordriver_1.0.ino
ESP32-S3 + Maker Drive
Test Motor A and Motor B
DIRA -> GPIO12
PWMA -> GPIO13
DIRB -> GPIO14
PWMB -> GPIO15
*/
const int DIRA = 12;
const int PWMA = 13;
const int DIRB = 14;
const int PWMB = 15;
// PWM Configuration
const int PWM_FREQ = 1000;
const int PWM_RESOLUTION = 8; // 0-255
void setup()
{
Serial.begin(115200);
pinMode(DIRA, OUTPUT);
pinMode(DIRB, OUTPUT);
// ESP32 Core 3.x
ledcAttach(PWMA, PWM_FREQ, PWM_RESOLUTION);
ledcAttach(PWMB, PWM_FREQ, PWM_RESOLUTION);
stopMotor();
Serial.println();
Serial.println("=================================");
Serial.println("Micromouse Motor Driver Test");
Serial.println("Maker Drive + ESP32-S3");
Serial.println("=================================");
}
void loop()
{
Serial.println("Motor Forward");
motorForward(150);
delay(3000);
stopMotor();
delay(1000);
Serial.println("Motor Reverse");
motorReverse(150);
delay(3000);
stopMotor();
delay(2000);
}
void motorForward(int speedValue)
{
digitalWrite(DIRA, HIGH);
digitalWrite(DIRB, HIGH);
ledcWrite(PWMA, speedValue);
ledcWrite(PWMB, speedValue);
}
void motorReverse(int speedValue)
{
digitalWrite(DIRA, LOW);
digitalWrite(DIRB, LOW);
ledcWrite(PWMA, speedValue);
ledcWrite(PWMB, speedValue);
}
void stopMotor()
{
ledcWrite(PWMA, 0);
ledcWrite(PWMB, 0);
}
ผลลัพธ์ที่ควรได้
Motor Forward
(หมุนไปข้างหน้า 3 วินาที)
หยุด 1 วินาที
Motor Reverse
(หมุนย้อนกลับ 3 วินาที)
หยุด 2 วินาที
วนซ้ำ
หากมอเตอร์หมุนทิศทางไม่ถูกต้อง
ให้สลับสาย
MA+ <-> MA-
หรือ
MB+ <-> MB-
โดยไม่ต้องแก้โค้ด
ขั้นต่อไปที่ควรทำคือ motordriver_1.1.ino
- เพิ่ม Encoder A/B
- แสดง Pulse Count บน Serial Monitor
- นับจำนวน Tick ของล้อ
- คำนวณ RPM ของมอเตอร์
สำหรับ motordriver_1.1.ino เราจะเพิ่มความสามารถดังนี้
- ควบคุมมอเตอร์ผ่าน Maker Drive
- อ่าน Encoder A/B
- นับ Pulse
- คำนวณ Tick
- คำนวณ RPM ทุก 1 วินาที
- แสดงผลบน Serial Monitor
การต่อวงจร
Encoder VCC -> 3.3V
Encoder GND -> GND
Encoder A -> GPIO4
Encoder B -> GPIO5
DIRA -> GPIO12
PWMA -> GPIO13
หาก Encoder ของคุณเป็นแบบ Hall Sensor 3.3V สามารถต่อเข้ากับ ESP32-S3 ได้โดยตรง
ไฟล์ motordriver_1.1.ino
/*
motordriver_1.1.ino
ESP32-S3
Maker Drive
N20 Motor + AB Encoder
DIRA -> GPIO12
PWMA -> GPIO13
Encoder A -> GPIO4
Encoder B -> GPIO5
*/
const int DIRA = 12;
const int PWMA = 13;
const int ENCODER_A = 4;
const int ENCODER_B = 5;
volatile long encoderCount = 0;
unsigned long lastRPMTime = 0;
long lastEncoderCount = 0;
const int PWM_FREQ = 1000;
const int PWM_RESOLUTION = 8;
const int MOTOR_SPEED = 150;
/*
ปรับตาม Encoder จริงของคุณ
ตัวอย่าง:
Encoder Disc = 12 PPR
Gear Ratio = 30
12 x 30 = 360 Pulse / Wheel Revolution
*/
const float PULSE_PER_REV = 360.0;
void IRAM_ATTR encoderISR()
{
bool A = digitalRead(ENCODER_A);
bool B = digitalRead(ENCODER_B);
if (A == B)
{
encoderCount++;
}
else
{
encoderCount--;
}
}
void setup()
{
Serial.begin(115200);
pinMode(DIRA, OUTPUT);
pinMode(ENCODER_A, INPUT_PULLUP);
pinMode(ENCODER_B, INPUT_PULLUP);
attachInterrupt(
digitalPinToInterrupt(ENCODER_A),
encoderISR,
CHANGE
);
ledcAttach(PWMA, PWM_FREQ, PWM_RESOLUTION);
Serial.println();
Serial.println("=================================");
Serial.println("Micromouse Encoder Test");
Serial.println("ESP32-S3 + Maker Drive");
Serial.println("=================================");
motorForward(MOTOR_SPEED);
lastRPMTime = millis();
}
void loop()
{
if (millis() - lastRPMTime >= 1000)
{
long currentCount = encoderCount;
long pulseDelta =
currentCount - lastEncoderCount;
float rpm =
(pulseDelta * 60.0) /
PULSE_PER_REV;
Serial.print("Pulse Count : ");
Serial.print(currentCount);
Serial.print(" Delta : ");
Serial.print(pulseDelta);
Serial.print(" RPM : ");
Serial.println(rpm);
lastEncoderCount = currentCount;
lastRPMTime = millis();
}
}
void motorForward(int speedValue)
{
digitalWrite(DIRA, HIGH);
ledcWrite(PWMA, speedValue);
}
void motorReverse(int speedValue)
{
digitalWrite(DIRA, LOW);
ledcWrite(PWMA, speedValue);
}
void stopMotor()
{
ledcWrite(PWMA, 0);
}
ผลลัพธ์บน Serial Monitor
Pulse Count : 356 Delta : 356 RPM : 59.3
Pulse Count : 714 Delta : 358 RPM : 59.7
Pulse Count : 1072 Delta : 358 RPM : 59.7
Pulse Count : 1429 Delta : 357 RPM : 59.5
การตรวจสอบค่า PULSE_PER_REV ที่แท้จริง
เนื่องจาก N20 Encoder แต่ละรุ่นไม่เท่ากัน ให้ทดลองดังนี้
- ทำเครื่องหมายที่ล้อ 1 จุด
- หมุนให้ครบ 1 รอบพอดี
- ดูค่า Pulse Count
ตัวอย่างแสดงว่า
const float PULSE_PER_REV = 420.0;
ค่าที่วัดจริงจะแม่นยำกว่าการคำนวณจาก Datasheet
motordriver_1.2.ino ซึ่งถือเป็นพื้นฐานของระบบ Odometry ที่ใช้จริงใน Micromouse
ความสามารถที่เพิ่มขึ้น
- อ่าน Encoder ซ้ายและขวาพร้อมกัน
- คำนวณ RPM ซ้ายและขวา
- คำนวณความเร็ว mm/s
- คำนวณระยะทางสะสม
- แสดงผลบน Serial Monitor ทุก 1 วินาที
การต่อวงจร
Motor Left
DIRA -> GPIO12
PWMA -> GPIO13
Motor Right
DIRB -> GPIO14
PWMB -> GPIO15
Encoder Left
Encoder A -> GPIO4
Encoder B -> GPIO5
Encoder Right
Encoder A -> GPIO6
Encoder B -> GPIO7
การกำหนดค่าล้อ
ตัวอย่างล้อ Micromouse
const float WHEEL_DIAMETER_MM = 32.0;
เส้นรอบวงล้อ
π × 32
= 100.53 mm
PULSE_PER_REV
ต้องแก้ตาม Encoder จริงของคุณ
ตัวอย่าง
const float PULSE_PER_REV = 360.0;
หากยังไม่ทราบค่า ใช้ 360 ไปก่อน
motordriver_1.2.ino
/*
motordriver_1.2.ino
ESP32-S3
Maker Drive
Dual N20 Encoder Motor
Features
- Dual Encoder
- RPM Left/Right
- Speed mm/s
- Distance mm
*/
const int DIRA = 12;
const int PWMA = 13;
const int DIRB = 14;
const int PWMB = 15;
const int ENCODER_L_A = 4;
const int ENCODER_L_B = 5;
const int ENCODER_R_A = 6;
const int ENCODER_R_B = 7;
volatile long encoderLeft = 0;
volatile long encoderRight = 0;
unsigned long lastTime = 0;
long lastLeftCount = 0;
long lastRightCount = 0;
float totalDistanceLeft = 0;
float totalDistanceRight = 0;
const float PULSE_PER_REV = 360.0;
const float WHEEL_DIAMETER_MM = 32.0;
const float WHEEL_CIRCUMFERENCE =
3.1415926 * WHEEL_DIAMETER_MM;
const int PWM_FREQ = 1000;
const int PWM_RESOLUTION = 8;
void IRAM_ATTR leftEncoderISR()
{
bool A = digitalRead(ENCODER_L_A);
bool B = digitalRead(ENCODER_L_B);
if (A == B)
encoderLeft++;
else
encoderLeft--;
}
void IRAM_ATTR rightEncoderISR()
{
bool A = digitalRead(ENCODER_R_A);
bool B = digitalRead(ENCODER_R_B);
if (A == B)
encoderRight++;
else
encoderRight--;
}
void setup()
{
Serial.begin(115200);
pinMode(DIRA, OUTPUT);
pinMode(DIRB, OUTPUT);
pinMode(ENCODER_L_A, INPUT_PULLUP);
pinMode(ENCODER_L_B, INPUT_PULLUP);
pinMode(ENCODER_R_A, INPUT_PULLUP);
pinMode(ENCODER_R_B, INPUT_PULLUP);
attachInterrupt(
digitalPinToInterrupt(ENCODER_L_A),
leftEncoderISR,
CHANGE
);
attachInterrupt(
digitalPinToInterrupt(ENCODER_R_A),
rightEncoderISR,
CHANGE
);
ledcAttach(PWMA, PWM_FREQ, PWM_RESOLUTION);
ledcAttach(PWMB, PWM_FREQ, PWM_RESOLUTION);
motorForward(150);
lastTime = millis();
Serial.println();
Serial.println("====================================");
Serial.println("Micromouse Odometry Test");
Serial.println("Version 1.2");
Serial.println("====================================");
}
void loop()
{
if (millis() - lastTime >= 1000)
{
long currentLeft = encoderLeft;
long currentRight = encoderRight;
long deltaLeft =
currentLeft - lastLeftCount;
long deltaRight =
currentRight - lastRightCount;
float rpmLeft =
(deltaLeft * 60.0) /
PULSE_PER_REV;
float rpmRight =
(deltaRight * 60.0) /
PULSE_PER_REV;
float speedLeft =
(rpmLeft * WHEEL_CIRCUMFERENCE) / 60.0;
float speedRight =
(rpmRight * WHEEL_CIRCUMFERENCE) / 60.0;
float distanceLeft =
(deltaLeft * WHEEL_CIRCUMFERENCE) /
PULSE_PER_REV;
float distanceRight =
(deltaRight * WHEEL_CIRCUMFERENCE) /
PULSE_PER_REV;
totalDistanceLeft += distanceLeft;
totalDistanceRight += distanceRight;
Serial.println();
Serial.print("L Pulse=");
Serial.print(currentLeft);
Serial.print(" RPM=");
Serial.print(rpmLeft);
Serial.print(" Speed=");
Serial.print(speedLeft);
Serial.print(" mm/s");
Serial.print(" Dist=");
Serial.print(totalDistanceLeft);
Serial.println(" mm");
Serial.print("R Pulse=");
Serial.print(currentRight);
Serial.print(" RPM=");
Serial.print(rpmRight);
Serial.print(" Speed=");
Serial.print(speedRight);
Serial.print(" mm/s");
Serial.print(" Dist=");
Serial.print(totalDistanceRight);
Serial.println(" mm");
lastLeftCount = currentLeft;
lastRightCount = currentRight;
lastTime = millis();
}
}
void motorForward(int pwm)
{
digitalWrite(DIRA, HIGH);
digitalWrite(DIRB, HIGH);
ledcWrite(PWMA, pwm);
ledcWrite(PWMB, pwm);
}
void motorReverse(int pwm)
{
digitalWrite(DIRA, LOW);
digitalWrite(DIRB, LOW);
ledcWrite(PWMA, pwm);
ledcWrite(PWMB, pwm);
}
void stopMotor()
{
ledcWrite(PWMA, 0);
ledcWrite(PWMB, 0);
}
ตัวอย่างผลลัพธ์
L Pulse=360 RPM=60.0 Speed=100.5 mm/s Dist=100.5 mm
R Pulse=358 RPM=59.7 Speed=100.0 mm/s Dist=100.0 mm
L Pulse=720 RPM=60.0 Speed=100.5 mm/s Dist=201.0 mm
R Pulse=716 RPM=59.7 Speed=100.0 mm/s Dist=200.0 mm
Link:https://www.genlogic.co.th/product/1002/maker-drive-simplifying-h-bridge-motor-driver-for-beginner-%E0%B9%82%E0%B8%A1%E0%B8%94%E0%B8%B9%E0%B8%A5%E0%B8%82%E0%B8%B1%E0%B8%9A%E0%B8%A1%E0%B8%AD%E0%B9%80%E0%B8%95%E0%B8%AD%E0%B8%A3%E0%B9%8C%E0%B8%AD%E0%B8%A2%E0%B9%88%E0%B8%B2%E0%B8%87%E0%B8%87%E0%B9%88%E0%B8%B2%E0%B8%A2?srsltid=AfmBOoogULK07Dp9zkpZIVlKCj5YRMPhhVTtJk333Oy_2EnGOu4Vli2d
http://wiki.fluidnc.com/en/hardware/ESP32-S3_Pin_Reference
https://www.robotics.org.za/N20-33RPM-ENC-6V
https://www.hackster.io/amal-shaji/reading-the-encoder-value-of-n20-motor-using-esp32-56575a
