目录
-
- 一、依照本测试的电子常识(不定期补充)
- 二、ESP32开发板
- 三、TB6612双路直流电机驱动模块
- 四、小车接线
-
- [1. 供电总结构](#1. 供电总结构)
- [2. 左边驱动板 TB6612_1(控制左前轮、左后轮)](#2. 左边驱动板 TB6612_1(控制左前轮、左后轮))
- [3. 右边驱动板 TB6612_2(控制右前轮、右后轮)](#3. 右边驱动板 TB6612_2(控制右前轮、右后轮))
- [3. 极简总框图](#3. 极简总框图)
- 五、烧录
一、依照本测试的电子常识(不定期补充)
- MCU(Microcontroller Unit) :单片机 / 微控制器
ESP32,就是一块 MCU。 - GPIO(General-Purpose Input/Output) :通用数字引脚/通用输入输出引脚
ESP32 上那些 D2、D4、D5、D13... 所有 D 开头的引脚就是,只不过是简写了。 - 电源、电池、模块供电 :绝对不能接反,接反必烧
- 电机两根线:不分正负,接反只是反转,不烧
- 串联 :电压相加,电流不变
- 并联 :电压不变,容量相加
- 共用地线(GND/Ground/负极) :所有模块ESP32、TB6612、电源、传感器GND 必须连在一起(共地)
- ESP32 输出电压 :ESP32 只能输出 3.3V,不能直接控制 高于电压的 设备,必须用驱动板中转。
- 注意:电机驱动模块 并不是升压、不是加压,它只是 "开关 + 通道"。
比如:TB6612 输入多少 V 电机电压,输出就是多少 V,不会升高电压!驱动模块上VM 引脚上接了多少V电压,电机两端就是多少V电压,不过还是要注意驱动模块和电机最高能承受多少电压。
- 注意:电机驱动模块 并不是升压、不是加压,它只是 "开关 + 通道"。
- PWM调速 :PWM并不是真实电压 ,是快速开关模拟出来的
- 高/低电平 :ESP32 就是靠 不断输出(GPIO) 高 / 低 来告诉 TB6612去驱动电机 转 / 不转 / 正转 / 反转 / 速度多少。
- esp32的高电平 = 3.3V = 有电 = 1
- esp32的低电平 = 0V = 没电 = 0
- ESP32开发板供电/输电 :
- 外部供电:通常接 5V(USB / 电池),板载稳压芯片会把 5V 降到 3.3V 给 ESP32 芯片用
- 所有 GPIO 引脚输出电压:只有 3.3V(高电平 = 3.3V,低电平 = 0V),没有 5V 输出
- 引脚输入耐压:只能接≤3.3V 的信号,直接接 5V 会烧引脚
二、ESP32开发板

引脚解释
非 D 类引脚(电源、使能、串口)
| 丝印 | 功能说明 |
|---|---|
| VIN | 电源输入引脚,可接 5V 电源(USB/外部 5V),板载稳压为 3.3V 给 ESP32 供电 |
| GND | 电源地(负极),所有 GND 引脚内部连通 |
| EN | 芯片使能引脚,高电平(3.3V)运行,低电平(0V)复位/休眠 |
| 3V3 | 3.3V 稳压输出,可给外部传感器/模块供电(限小电流负载) |
| RX0 | GPIO3,UART0 RX(串口0接收,用于烧录/调试日志) |
| TX0 | GPIO1,UART0 TX(串口0发送,用于烧录/调试日志) |
D 开头引脚(支持 PWM)
| 丝印 | 功能说明 |
|---|---|
| D2 | GPIO2,通用数字 IO,支持 PWM/ADC/I2C 等 |
| D4 | GPIO4,通用数字 IO,支持 PWM/ADC/I2C 等 |
| D5 | GPIO5,通用数字 IO,支持 PWM/SPI 等,上电电平影响启动模式 |
| D12 | GPIO12,通用数字 IO,上电电平影响启动模式,谨慎用作关键输入,支持 PWM |
| D13 | GPIO13,通用数字 IO,支持 PWM/ADC/SPI 等 |
| D14 | GPIO14,通用数字 IO,支持 PWM/ADC/SPI/I2C 等 |
| D15 | GPIO15,通用数字 IO,上电电平影响启动模式,谨慎用作关键输入,支持 PWM |
| D16 | GPIO16,通用数字 IO,可作 UART2 RX,支持 PWM |
| D17 | GPIO17,通用数字 IO,可作 UART2 TX,支持 PWM |
| D18 | GPIO18,通用数字 IO,支持 PWM/SPI/I2C 等 |
| D19 | GPIO19,通用数字 IO,支持 PWM/SPI/I2C 等 |
| D21 | GPIO21,通用数字 IO,I2C SDA(数据总线),支持 PWM |
| D22 | GPIO22,通用数字 IO,I2C SCL(时钟总线),支持 PWM |
| D23 | GPIO23,通用数字 IO,支持 PWM/SPI 等 |
| D25 | GPIO25,通用数字 IO,支持 ADC/PWM/I2S 等 |
| D26 | GPIO26,通用数字 IO,支持 ADC/PWM 等 |
| D27 | GPIO27,通用数字 IO,支持 ADC/PWM 等 |
| D32 | GPIO32,通用数字 IO,支持 ADC/PWM 等 |
| D33 | GPIO33,通用数字 IO,支持 ADC/PWM 等 |
补充:部分引脚上电电平影响启动模式
ESP32 接通电源(上电)的瞬间,部分引脚(如 D5/D12/D15)的电平高低(0V 或 3.3V),会决定 ESP32 进入哪种启动模式,这是 ESP32 硬件设计的固有特性。
| 模式 | 触发条件(以上电时引脚电平为准) | 用途 |
|---|---|---|
| 正常运行模式 | GPIO0(BOOT)= 高电平、GPIO12(D12)= 高电平/悬空 | 日常运行烧录好的程序(你小车代码的正常工作模式) |
| 下载/烧录模式 | GPIO0(BOOT)= 低电平 | 烧录新程序到 ESP32(需要按住 BOOT 按键上电) |
| SD 卡启动模式 | GPIO12(D12)= 低电平 | 从外接 SD 卡读取程序运行(极少用到) |
D 开头引脚(不支持 PWM,仅输入)
| 丝印 | 功能说明 |
|---|---|
| D34 | GPIO34,仅输入引脚,支持 ADC,不可输出电平 |
| D35 | GPIO35,仅输入引脚,支持 ADC,不可输出电平 |
| VP | GPIO36(SENSOR_VP),仅输入引脚,支持 ADC,不可输出电平 |
| VN | GPIO39(SENSOR_VN),仅输入引脚,支持 ADC,不可输出电平 |
特殊引脚
EN 引脚与按键说明
EN 是 Enable(使能)引脚,板上既有按键也有外部焊盘。
- 电平逻辑:高电平(3.3V)时芯片正常运行,低电平(0V)时芯片复位或进入休眠。
- 按键作用 :按下 EN 按键会把引脚拉低,让 ESP32 重启(复位),松开后恢复高电平,芯片继续运行。
- 外部使用:可以外接开关或其他 MCU,来远程复位 ESP32 或实现低功耗唤醒。
注意:不要长期把 EN 引脚拉低,否则芯片会一直处于复位状态,无法正常工作。
BOOT 引脚与按键说明
BOOT 是 Download(下载)引脚,对应芯片的 GPIO0,板上有按键和外部焊盘(部分板丝印为 DOWNLOAD)。
-
电平逻辑:正常运行时为高电平,进入下载模式时需要拉低。
-
按键作用:专门用来进入固件下载模式(一般IDE会自动处理),操作步骤是:
- 先按住 BOOT 按键不放;
- 短按一下 EN 按键(让芯片复位);
- 松开 EN 按键,继续按住 BOOT 按键 1~2 秒后松开;
- 此时 ESP32 进入 UART 下载模式,电脑就可以通过串口给它烧录新程序。
-
外部使用:可以外接按键或自动下载电路,让烧录操作更方便。
注意:正常运行时 BOOT 引脚保持高电平,拉低只用于烧录程序,不会影响正在运行的代码。
三、TB6612双路直流电机驱动模块
TB6612FNG 是东芝推出的直流电机驱动芯片,做成模块后可以同时控制 2 个直流电机,支持正反转、PWM 调速、刹车和停止功能。
TB6612FNG 是东芝这款电机驱动芯片的完整型号,FNG 是型号后缀,代表封装与版本信息。
- TB6612:芯片系列与功能编号(双路 H 桥直流电机驱动)
- F:封装类型(SSOP,小外形封装)
- N:引脚数 / 封装规格(24 引脚 SSOP)
- G:版本 / 批次标识(东芝内部版本代号)
简单说:FNG 就是告诉你 "这颗芯片是 SSOP24 封装的 TB6612 版本"。
特点
- 可同时驱动 2 路直流电机
- 电机供电电压:2.2V ~ 13.5V
- 每路电机连续电流:1.2A,峰值 3.2A
- 支持 PWM 调速(0~100%占空比)
- 内置:过流保护、过热保护
- 效率高、发热量很小
- 支持 3.3V / 5V 逻辑电平,兼容 ESP32、STM32、Arduino

引脚解释
| 引脚丝印 | 功能类型 | 详细说明 | 电压/接线 |
|---|---|---|---|
| VM | 电机电源输入 | 给电机供电,范围 2.2V~13.5V(常用6~12V) | 接电机电源正极 |
| VCC | 逻辑电源输入 | 给芯片内部逻辑供电,2.7V~5.5V(与MCU一致,3.3V/5V) | 接MCU 3.3V/5V |
| GND | 逻辑地 | 芯片逻辑地,必须与MCU、电机电源共地 | 接系统负极 |
| PGND1/PGND2 | 功率地 | 电机驱动大电流地,内部与GND连通 | 接电源负极 |
| STBY | 待机控制 | 高电平(H)= 正常工作;低电平(L)= 待机(关输出) | 接MCU GPIO(默认下拉,需主动拉高) |
| AIN1 | 通道A方向输入1 | 控制电机A正反转 | 接MCU GPIO |
| AIN2 | 通道A方向输入2 | 控制电机A正反转 | 接MCU GPIO |
| PWMA | 通道A PWM调速 | 接收PWM信号控制电机A转速(占空比越大越快) | 接MCU PWM引脚 |
| BIN1 | 通道B方向输入1 | 控制电机B正反转 | 接MCU GPIO |
| BIN2 | 通道B方向输入2 | 控制电机B正反转 | 接MCU GPIO |
| PWMB | 通道B PWM调速 | 接收PWM信号控制电机B转速 | 接MCU PWM引脚 |
| AO1 | 通道A输出1 | 电机A接线端1 | 接电机A一端 |
| AO2 | 通道A输出2 | 电机A接线端2 | 接电机A另一端 |
| BO1 | 通道B输出1 | 电机B接线端1 | 接电机B一端 |
| BO2 | 通道B输出2 | 电机B接线端2 | 接电机B另一端 |
VCC(Voltage Common Collector) :驱动模块自己上面也有芯片(类似ESP32开发板上的esp32芯片),芯片只能用小功率来驱动,给芯片内部逻辑 / 大脑供电(小功率)。
VM(Voltage Motor) :给电机供电(大功率)
电机A控制逻辑(AIN1/AIN2)
| AIN1 | AIN2 | PWMA | 电机A状态 |
|---|---|---|---|
| H | L | PWM | 正转(速度由PWM决定) |
| L | H | PWM | 反转 |
| H | H | × | 刹车(短接电机两端) |
| L | L | × | 停止(自由滑行) |
电机 B(BIN1/BIN2)逻辑完全相同。
与 L298N双路直流电机驱动模块 对比
| 项目 | TB6612FNG | L298N |
|---|---|---|
| 驱动类型 | 双 H 桥 MOSFET 驱动 | 双 H 桥 双极型晶体管驱动 |
| 可驱动电机数量 | 2 路直流电机 / 1 路 4 线步进电机 | 2 路直流电机 / 1 路 4 线步进电机 |
| 电机工作电压 | 2.2V ~ 13.5V | 6V ~ 46V |
| 单路持续电流 | 1.2A | 2A |
| 单路峰值电流 | 3.2A | 3A |
| 逻辑电压 | 3.3V / 5V 都支持 | 5V |
| 效率 | 高(MOS 管,压降小) | 低(晶体管,压降大) |
| 发热情况 | 几乎不发热 | 很大,必须加散热片 |
| 功耗 | 低,省电 | 高,耗电快 |
| 控制方式 | IN1/IN2 方向 + PWM 调速 | IN1/IN2 方向 + PWM 调速 |
| 功能 | 正转、反转、刹车、滑行 | 正转、反转、刹车 |
| 体积 | 很小,贴片式模块 | 大,厚重 |
| 价格 | 便宜 | 便宜 |
| 适合场景 | 智能小车、机器人、小电机 | 大功率电机、大扭矩、12V 以上系统 |
补充:H 桥
一种可以让电机正转、反转、刹车的电路结构 。因此双 H 桥就是一个芯片 / 模块里集成了两套 H 桥,可以同时独立控制 2 个电机。
所以:
- TB6612 = 双 H 桥驱动
- L298N = 双 H 桥驱动
它们都能驱动 2 个直流电机,适合两轮小车。
另外还有四H桥驱动模块,常见驱动模块有:L293D、DRV8833 四电机版、四路电机驱动板等。
四、小车接线
| 接线类别 | 连接起点 | 连接终点 | 接线顺序 | 线数/拆分方式 | 功能说明 |
|---|---|---|---|---|---|
| 动力供电(正极) | 锂电池正极 | ESP32 DEV Board - VIN | 1 | 1股(总正极线拆分) | 给ESP32主板供电 |
| 动力供电(正极) | 锂电池正极 | TB6612 FNG 1 - VM | 2 | 1股(总正极线拆分) | 左驱动板电机动力供电 |
| 动力供电(正极) | 锂电池正极 | TB6612 FNG 2 - VM | 3 | 1股(总正极线拆分) | 右驱动板电机动力供电 |
| 控制供电(3.3V) | ESP32 - 3.3V引脚 | TB6612 FNG 1 - VCC | 4 | 1股(3.3V线拆分) | 左驱动板控制信号供电 |
| 控制供电(3.3V) | ESP32 - 3.3V引脚 | TB6612 FNG 1 - STBY | 5 | 1股(3.3V线拆分) | 左驱动板待机使能(必须接) |
| 控制供电(3.3V) | ESP32 - 3.3V引脚 | TB6612 FNG 2 - VCC | 6 | 1股(3.3V线拆分) | 右驱动板控制信号供电 |
| 控制供电(3.3V) | ESP32 - 3.3V引脚 | TB6612 FNG 2 - STBY | 7 | 1股(3.3V线拆分) | 右驱动板待机使能(必须接) |
| 左前轮控制 | ESP32 - 18引脚 | TB6612 FNG 1 - AIN1 | 8 | 单独1根线 | 左前轮正/反转控制 |
| 左前轮控制 | ESP32 - 19引脚 | TB6612 FNG 1 - AIN2 | 9 | 单独1根线 | 左前轮正/反转控制 |
| 左前轮控制 | ESP32 - 23引脚 | TB6612 FNG 1 - PWMA | 10 | 单独1根线 | 左前轮转速调节(PWM) |
| 左后轮控制 | ESP32 - 22引脚 | TB6612 FNG 1 - BIN1 | 11 | 单独1根线 | 左后轮正/反转控制 |
| 左后轮控制 | ESP32 - 21引脚 | TB6612 FNG 1 - BIN2 | 12 | 单独1根线 | 左后轮正/反转控制 |
| 左后轮控制 | ESP32 - 25引脚 | TB6612 FNG 1 - PWMB | 13 | 单独1根线 | 左后轮转速调节(PWM) |
| 右前轮控制 | ESP32 - 14引脚 | TB6612 FNG 2 - AIN1 | 14 | 单独1根线 | 右前轮正/反转控制 |
| 右前轮控制 | ESP32 - 12引脚 | TB6612 FNG 2 - AIN2 | 15 | 单独1根线 | 右前轮正/反转控制 |
| 右前轮控制 | ESP32 - 13引脚 | TB6612 FNG 2 - PWMA | 16 | 单独1根线 | 右前轮转速调节(PWM) |
| 右后轮控制 | ESP32 - 5引脚 | TB6612 FNG 2 - BIN1 | 17 | 单独1根线 | 右后轮正/反转控制 |
| 右后轮控制 | ESP32 - 4引脚 | TB6612 FNG 2 - BIN2 | 18 | 单独1根线 | 右后轮正/反转控制 |
| 右后轮控制 | ESP32 - 2引脚 | TB6612 FNG 2 - PWMB | 19 | 单独1根线 | 右后轮转速调节(PWM) |
| 左前轮电机 | TB6612 FNG 1 - A01 | 左前轮电机 - 引脚1 | 20 | 单独1根线 | 左前轮电机动力输出 |
| 左前轮电机 | TB6612 FNG 1 - A02 | 左前轮电机 - 引脚2 | 21 | 单独1根线 | 左前轮电机动力输出 |
| 左后轮电机 | TB6612 FNG 1 - B01 | 左后轮电机 - 引脚1 | 22 | 单独1根线 | 左后轮电机动力输出 |
| 左后轮电机 | TB6612 FNG 1 - B02 | 左后轮电机 - 引脚2 | 23 | 单独1根线 | 左后轮电机动力输出 |
| 右前轮电机 | TB6612 FNG 2 - A01 | 右前轮电机 - 引脚1 | 24 | 单独1根线 | 右前轮电机动力输出 |
| 右前轮电机 | TB6612 FNG 2 - A02 | 右前轮电机 - 引脚2 | 25 | 单独1根线 | 右前轮电机动力输出 |
| 右后轮电机 | TB6612 FNG 2 - B01 | 右后轮电机 - 引脚1 | 26 | 单独1根线 | 右后轮电机动力输出 |
| 右后轮电机 | TB6612 FNG 2 - B02 | 右后轮电机 - 引脚2 | 27 | 单独1根线 | 右后轮电机动力输出 |
| 接地(负极) | 锂电池负极 | ESP32 - 任意GND引脚 | 28 | 单独1根线 | 总接地起点,所有设备共地 |
| 接地(负极) | ESP32 - 另一GND引脚 | TB6612 FNG 1 - GND | 29 | 单独1根线 | 左驱动板接地 |
| 接地(负极) | TB6612 FNG 1 - 另一GND | TB6612 FNG 2 - GND | 30 | 单独1根线 | 右驱动板接地 |
1. 供电总结构
bash
锂电池(+) ──┬──> ESP32 VIN
├──> TB6612_1 VM
└──> TB6612_2 VM
ESP32 3.3V ─┬──> TB6612_1 VCC
├──> TB6612_1 STBY
├──> TB6612_2 VCC
└──> TB6612_2 STBY
锂电池(-) ───> ESP32 GND ──> TB6612_1 GND ──> TB6612_2 GND
2. 左边驱动板 TB6612_1(控制左前轮、左后轮)
bash
ESP32 18 → AIN1
ESP32 19 → AIN2
ESP32 23 → PWMA
AO1、AO2 → 左前轮电机
ESP32 22 → BIN1
ESP32 21 → BIN2
ESP32 25 → PWMB
BO1、BO2 → 左后轮电机
3. 右边驱动板 TB6612_2(控制右前轮、右后轮)
bash
ESP32 14 → AIN1
ESP32 12 → AIN2
ESP32 13 → PWMA
AO1、AO2 → 右前轮电机
ESP32 5 → BIN1
ESP32 4 → BIN2
ESP32 2 → PWMB
BO1、BO2 → 右后轮电机
3. 极简总框图
bash
锂电池 (+)
│
┌──────────┼──────────┐
│ │ │
▼ ▼ ▼
ESP32 ~ VIN TB6612(1)~VM TB6612(2)~VM
ESP32 ~ 3.3V
│
┌────────────┼────────────┬────────────┐
│ │ │ │
▼ ▼ ▼ ▼
TB6612(1)~VCC TB6612(1)~STBY TB6612(2)~VCC TB6612(2)~STBY
锂电池 (-)
│
▼
ESP32 ~ GND
│
▼
TB6612(1) ~ GND
│
▼
TB6612(2) ~ GND
五、烧录
- Arduino IDE (官网下载最新版:https://www.arduino.cc/en/software)
- 国内开发板管理器镜像地址(来源为网络搜索) :https://jihulab.com/esp-mirror/espressif/arduino-esp32/-/raw/gh-pages/package_esp32_index_cn.json
- 安装Arduino IDE
- 国内用户添加国内开发板管理器镜像地址
- 安装ESP32开发板适配包(ESP32 by Espressif Systems)【乐鑫(ESP32 芯片原厂)维护】
- Arduino IDE 本身只支持 Arduino 官方板卡(如 Uno、Nano),要烧录 ESP32 代码,必须通过「开发板管理器」安装 ESP32 的适配包。如果不装 ESP32 专属的驱动(适配包),IDE 根本不认识 ESP32,自然没法给它烧录代码。
- 注意:不要安装Arduino ESP32 Boards by Arduino,只有 Arduino Nano ESP32 这一款(就是 Arduino 自己出的那个小 ESP32 板子)才可以用。
- 选择对应开发板、选择对应数据端口
- 编译烧录
- 打开串口监控器选择对应代码日志波特率,查看测试日志
测试代码
cpp
// 左前
#define AIN1_LF 18
#define AIN2_LF 19
#define PWMA_LF 23
// 左后
#define BIN1_LR 22
#define BIN2_LR 21
#define PWMB_LR 25
// 右前
#define AIN1_RF 14
#define AIN2_RF 12
#define PWMA_RF 13
// 右后
#define BIN1_RR 5
#define BIN2_RR 4
#define PWMB_RR 2
void setup() {
Serial.begin(115200);
pinMode(AIN1_LF, OUTPUT);
pinMode(AIN2_LF, OUTPUT);
pinMode(PWMA_LF, OUTPUT);
pinMode(BIN1_LR, OUTPUT);
pinMode(BIN2_LR, OUTPUT);
pinMode(PWMB_LR, OUTPUT);
pinMode(AIN1_RF, OUTPUT);
pinMode(AIN2_RF, OUTPUT);
pinMode(PWMA_RF, OUTPUT);
pinMode(BIN1_RR, OUTPUT);
pinMode(BIN2_RR, OUTPUT);
pinMode(PWMB_RR, OUTPUT);
delay(1000);
// 开始逐个测试轮子
Serial.println("=== 左前轮 转 2秒 ===");
digitalWrite(AIN1_LF, HIGH);
digitalWrite(AIN2_LF, LOW);
analogWrite(PWMA_LF, 180);
delay(2000);
stopAll();
Serial.println("=== 左后轮 转 2秒 ===");
digitalWrite(BIN1_LR, HIGH);
digitalWrite(BIN2_LR, LOW);
analogWrite(PWMB_LR, 180);
delay(2000);
stopAll();
Serial.println("=== 右前轮 转 2秒 ===");
digitalWrite(AIN1_RF, HIGH);
digitalWrite(AIN2_RF, LOW);
analogWrite(PWMA_RF, 180);
delay(2000);
stopAll();
Serial.println("=== 右后轮 转 2秒 ===");
digitalWrite(BIN1_RR, HIGH);
digitalWrite(BIN2_RR, LOW);
analogWrite(PWMB_RR, 180);
delay(2000);
stopAll();
Serial.println("=== 测试完成 ===");
}
void loop() {
// 这里空着,只跑一次测试
}
void stopAll() {
digitalWrite(AIN1_LF, LOW);
digitalWrite(AIN2_LF, LOW);
digitalWrite(BIN1_LR, LOW);
digitalWrite(BIN2_LR, LOW);
digitalWrite(AIN1_RF, LOW);
digitalWrite(AIN2_RF, LOW);
digitalWrite(BIN1_RR, LOW);
digitalWrite(BIN2_RR, LOW);
analogWrite(PWMA_LF, 0);
analogWrite(PWMB_LR, 0);
analogWrite(PWMA_RF, 0);
analogWrite(PWMB_RR, 0);
delay(100);
}
简易WIFI网页遥控版本
cpp
#include <WiFi.h>
#include <WebServer.h>
// ====================== 改成你家的 WiFi ======================
const char* ssid = "xxx";
const char* password = "xxx";
// ============================================================
#define AIN1_LF 18
#define AIN2_LF 19
#define PWMA_LF 23
#define BIN1_LR 22
#define BIN2_LR 21
#define PWMB_LR 25
#define AIN1_RF 14
#define AIN2_RF 12
#define PWMA_RF 13
#define BIN1_RR 5
#define BIN2_RR 4
#define PWMB_RR 2
WebServer server(80);
void stopAll() {
digitalWrite(AIN1_LF, 0); digitalWrite(AIN2_LF, 0); analogWrite(PWMA_LF, 0);
digitalWrite(BIN1_LR, 0); digitalWrite(BIN2_LR, 0); analogWrite(PWMB_LR, 0);
digitalWrite(AIN1_RF, 0); digitalWrite(AIN2_RF, 0); analogWrite(PWMA_RF, 0);
digitalWrite(BIN1_RR, 0); digitalWrite(BIN2_RR, 0); analogWrite(PWMB_RR, 0);
}
void forward() {
stopAll();
digitalWrite(AIN1_LF, 1); digitalWrite(AIN2_LF, 0); analogWrite(PWMA_LF, 180);
digitalWrite(BIN1_LR, 1); digitalWrite(BIN2_LR, 0); analogWrite(PWMB_LR, 180);
digitalWrite(AIN1_RF, 1); digitalWrite(AIN2_RF, 0); analogWrite(PWMA_RF, 180);
digitalWrite(BIN1_RR, 1); digitalWrite(BIN2_RR, 0); analogWrite(PWMB_RR, 180);
server.send(200, "text/plain", "ok");
}
void backward() {
stopAll();
digitalWrite(AIN1_LF, 0); digitalWrite(AIN2_LF, 1); analogWrite(PWMA_LF, 180);
digitalWrite(BIN1_LR, 0); digitalWrite(BIN2_LR, 1); analogWrite(PWMB_LR, 180);
digitalWrite(AIN1_RF, 0); digitalWrite(AIN2_RF, 1); analogWrite(PWMA_RF, 180);
digitalWrite(BIN1_RR, 0); digitalWrite(BIN2_RR, 1); analogWrite(PWMB_RR, 180);
server.send(200, "text/plain", "ok");
}
void left() {
stopAll();
digitalWrite(AIN1_LF, 0); digitalWrite(AIN2_LF, 1); analogWrite(PWMA_LF, 150);
digitalWrite(BIN1_LR, 0); digitalWrite(BIN2_LR, 1); analogWrite(PWMB_LR, 150);
digitalWrite(AIN1_RF, 1); digitalWrite(AIN2_RF, 0); analogWrite(PWMA_RF, 150);
digitalWrite(BIN1_RR, 1); digitalWrite(BIN2_RR, 0); analogWrite(PWMB_RR, 150);
server.send(200, "text/plain", "ok");
}
void right() {
stopAll();
digitalWrite(AIN1_LF, 1); digitalWrite(AIN2_LF, 0); analogWrite(PWMA_LF, 150);
digitalWrite(BIN1_LR, 1); digitalWrite(BIN2_LR, 0); analogWrite(PWMB_LR, 150);
digitalWrite(AIN1_RF, 0); digitalWrite(AIN2_RF, 1); analogWrite(PWMA_RF, 150);
digitalWrite(BIN1_RR, 0); digitalWrite(BIN2_RR, 1); analogWrite(PWMB_RR, 150);
server.send(200, "text/plain", "ok");
}
void stopCar() {
stopAll();
server.send(200, "text/plain", "ok");
}
void handleRoot() {
String html = R"HTML(
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小车遥控</title>
<style>
* {
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
body {
text-align: center;
margin-top: 40px;
}
button {
width: 220px;
height: 110px;
font-size: 36px;
margin: 10px;
border-radius: 10px;
background: #4CAF50;
color: white;
border: none;
}
</style>
</head>
<body>
<h1>小车遥控</h1>
<button
touchstart="fetch('/forward')"
touchend="fetch('/stop')"
mousedown="fetch('/forward')"
mouseup="fetch('/stop')"
>
前进
</button><br>
<button
touchstart="fetch('/backward')"
touchend="fetch('/stop')"
mousedown="fetch('/backward')"
mouseup="fetch('/stop')"
>
后退
</button><br>
<button
touchstart="fetch('/left')"
touchend="fetch('/stop')"
mousedown="fetch('/left')"
mouseup="fetch('/stop')"
>
左转
</button>
<button
touchstart="fetch('/right')"
touchend="fetch('/stop')"
mousedown="fetch('/right')"
mouseup="fetch('/stop')"
>
右转
</button><br>
<button onclick="fetch('/stop')">停止</button>
<script>
// 给按钮绑定触摸事件
document.querySelectorAll('button').forEach(btn => {
btn.addEventListener('touchstart', (e) => {
e.preventDefault();
eval(btn.getAttribute('touchstart'));
});
btn.addEventListener('touchend', (e) => {
e.preventDefault();
eval(btn.getAttribute('touchend'));
});
});
</script>
</body>
</html>
)HTML";
server.send(200, "text/html; charset=utf-8", html);
}
void setup() {
Serial.begin(115200);
delay(500);
pinMode(AIN1_LF, OUTPUT);
pinMode(AIN2_LF, OUTPUT);
pinMode(PWMA_LF, OUTPUT);
pinMode(BIN1_LR, OUTPUT);
pinMode(BIN2_LR, OUTPUT);
pinMode(PWMB_LR, OUTPUT);
pinMode(AIN1_RF, OUTPUT);
pinMode(AIN2_RF, OUTPUT);
pinMode(PWMA_RF, OUTPUT);
pinMode(BIN1_RR, OUTPUT);
pinMode(BIN2_RR, OUTPUT);
pinMode(PWMB_RR, OUTPUT);
stopAll();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi 连接成功!");
Serial.print("控制地址:");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/forward", forward);
server.on("/backward", backward);
server.on("/left", left);
server.on("/right", right);
server.on("/stop", stopCar);
server.begin();
}
void loop() {
server.handleClient();
}
esp32小车测试