ESP8266 后驱遥控车(初版 2026-08-25 · 最后更新 2026-09-11 01:25)
拆开后按本文原样接。固件:Documents/Mac管家/wifi_boat/
刷写:arduino-cli compile --fqbn esp8266:esp8266:nodemcuv2 --upload -p /dev/cu.usbserial-110 /Users/xiaobai/Documents/Mac管家/wifi_boat
串口号每次可能变,先
ls /dev/cu.usbserial-*看实际是几,再填进-p。
手机连热点 wifi-boat / 密码 12345678 → http://192.168.4.1/
左侧竖滑=速度,右侧竖滑=转向急缓,中间九宫格方向,前灯后灯夹在前进两边。
倒车后灯自动亮,前进/停/转弯后灯灭。无语音。
硬件:NodeMCU ESP8266(CP2102)、L298N、两只后驱 N20 6V 150rpm、12V 电池、前后小 LED。
更新日志
| 时间 | 做了什么 |
|---|---|
| 2026-08-25 | 初版。数字开关全速,网页 8 个按钮,转向靠「一侧轮子停死」 |
| 2026-09-10 20:35 | 确认板子端口为 /dev/cu.usbserial-110(旧笔记里的 -10 已失效) |
| 2026-09-10 20:45 | 加 PWM 调速:ENA/ENB 改 analogWrite,网页加速度滑块 1~100%,新增 /api/cmd?c=speed&v=N。另加 150ms 启动斜坡,换向时先清零占空比防 L298N 上下桥对冲 |
| 2026-09-10 21:00 | 重写差速转向:内轮 35% / 外轮 100%(原来内轮 0% 会蹭地顿挫),掉头 ±70%,倒车 80%。加串口测试指令,用 pyserial 实测全部占空比通过 |
| 2026-09-11 00:50 | 网页 v5 改版:速度/转向改两侧竖滑,方向居中九宫格,前灯后灯移到前进左右;新增 /api/state 实时轮速条与转弯半径显示;转向急缓可在网页上调(c=turn&v=20~55),不用重新刷机 |
| 2026-09-11 01:05 | 修方向键无响应:方向键改表单 + 隐藏 iframe 提交(不依赖 JS),所有响应加 Connection: close 防 ESP8266 socket 被轮询占满,轮询 400ms→1000ms 且页面隐藏时暂停,页面加 JS 错误显示 |
| 2026-09-11 01:15 | 加跑偏修正:网页「跑偏修正」滑块 -20~+20(负补左轮、正补右轮),串口 trim N;同时加 turn N、wifi 诊断指令 |
| 2026-09-11 01:20 | 热点改 802.11g:ESP8266 默认 11n 广播,部分 iPhone 连不上;测试脚本移入 Mac管家/wifi_boat/tools/,项目里新增 CHANGELOG.md |
| 2026-09-11 01:25 | 修强制门户不弹窗:补齐各家系统的探测地址(iOS library/test/success.html、Android mobile/status.php、Win11 redirect 等 10 个),并撤掉网页响应上的 Connection: close(只保留在 /api/*) |
强制门户(连上自动弹界面)怎么工作
手机连上 Wi-Fi 后会自动访问一个固定地址探测网络。只要返回的内容不是它预期的,系统就判定「这个网需要登录」,把我们的控制页弹出来:
| 系统 | 探测地址 | 它期待的 |
|---|---|---|
| iOS | /hotspot-detect.html、/library/test/success.html |
正文 Success |
| Android / Chrome | /generate_204、/mobile/status.php |
HTTP 204 |
| Windows | /ncsi.txt、/connecttest.txt、/redirect |
Microsoft NCSI |
代码里这些地址一律返回控制页(WebControl.cpp 里那个 portal lambda),所以一定判成"需要登录"。
另外 DNS 开了通配(dns_.start(53,"*",apIp_)),任何域名都指到 192.168.4.1,探测必然落到我们这儿。
不弹窗时怎么办(iOS 会缓存"这个网不用弹"的判断):
- 设置 → Wi-Fi → 点 wifi-boat 的 ⓘ → 忽略此网络,重新输密码连一次 ← 这招最灵
- 还是不弹就直接在 Safari 打开 http://192.168.4.1/ ,加到主屏幕后跟 App 一样
- 确认连没连上:串口发
wifi,看已连设备是不是 ≥1
排查:手机连不上 wifi-boat
热点本身起没起来,用串口查(115200 发 wifi):
[WIFI] 模式=AP SSID=wifi-boat IP=192.168.4.1 已连设备=0 广播=2
已连设备 是 0 就说明手机没连上来。按顺序试:
- 关掉蜂窝数据再连。iOS 发现这个网没有互联网,会自动切回 4G,表现就是"连不上"
- 连的时候会弹「此网络无互联网连接」,要点「仍要使用」,别点取消
- 设置 → Wi-Fi → 点 wifi-boat 右边的 ⓘ → 忽略此网络,再重新输密码连一次
- 还不行就关掉私有无线局域网地址(同一个 ⓘ 页面里)
- 密码是
12345678,必须 8 位以上,短了 ESP8266 直接拒绝 - 固件已经把无线模式从 11n 降到 11g(
WebControl.cpp里WiFi.setPhyMode)
排查:按了没反应
按顺序查,90% 停在第 1 步:
- L298N 有没有接 12V 电池?
板子只插 USB 时,ESP8266 照常输出 PWM、网页轮速条照常动,但马达一根线都不会转——L298N 的电机电源是独立的,不吃 USB 的电。 - L298N 上那个红色/绿色 LED 亮不亮? 不亮就是 12V 没进来。
- +5V 跳帽插了吗? 中间那个。拔了逻辑 5V 不起,L298N 不工作。
- ENA / ENB 跳帽拔了吗? 没拔就一直全速,8266 控不了。
- 上面都正常 → 看网页:按「前进」按钮会不会变蓝、轮速条动不动。
- 会变蓝 = 指令到了,问题在电机那侧(12V / 接线 / 跳帽)
- 不变蓝 = 网页没发到,看页面底部有没有红色「离线」
电源(先接这个)
12V 电池:
| 线 | 接到 |
|---|---|
| 红 + | L298N 螺丝孔 +12V |
| 黑 − | L298N 螺丝孔 GND |
| 黄 | 充电 NTC,空着 |
L298N 跳帽:
- 中间 +5V 跳帽:插上(12V → 板上 7805 → 逻辑 5V,灯会红)
- ENA、ENB 跳帽:拔掉(改由 8266 控制)
- 底下 +5V 螺丝孔:给 8266 供电时再用,不接电池
给 8266 供电(二选一,12V 不准进 8266):
- L298N +5V → NodeMCU VIN,GND 已共地 → 可拔 USB
- 或 USB 充电宝 / 电脑,只供电不控车
8266 ↔ L298N(信号 + 共地)
NodeMCU 丝印 → L298N:
| ESP | GPIO | L298N | 作用 |
|---|---|---|---|
| GND | — | GND | 必须共地 |
| D5 | 14 | ENA | 左使能(全速 HIGH) |
| D1 | 5 | IN1 | 左方向 |
| D2 | 4 | IN2 | 左方向 |
| D6 | 12 | ENB | 右使能 |
| D7 | 13 | IN3 | 右方向 |
| D8 | 15 | IN4 | 右方向 |
不要用 D3/D4/D0 当电机脚(烧录/LED/拉高不稳)。D8=GPIO15 启动时要保持低,接 IN4 可以。
电机 ↔ L298N
| 位置 | 线色(装车时) | L298N |
|---|---|---|
| 左后 | 白 + 灰 | OUT1 / OUT2 |
| 右后 | 黄/蓝(或紫) | OUT3 / OUT4 |
转反了:只对调那一侧两根 OUT,不要改 8266 脚。
直连电池能转、过 298 不转:逻辑 5V 没起来或 ENA/ENB 没打开。12V 已接时插上 +5V 跳帽,ENA/ENB 可先插跳帽测电机,再拔掉改接 8266。
灯光(小 LED,禁止 12V 直插 GPIO)
| 灯 | ESP | 接法 |
|---|---|---|
| 前灯 | D0 (GPIO16) | D0 → 220Ω → LED 正极,负极 → GND |
| 后灯 | D3 (GPIO0) | D3 → 220Ω → LED 正极,负极 → GND |
网页「前灯/后灯」开关。后退自动开后灯。D3 是烧录脚,下次刷机失败先拔后灯再刷。
不要接
- 12V → VIN / 5V / 3V3 / D0–D8
- 电机直插 GPIO
- 电池黄线接到任何电源孔
- 喇叭/语音(已取消,RX 空着)
调速(2026-09-10 加)
ENA/ENB 改走 PWM(config.h 里调):
| 参数 | 值 | 说明 |
|---|---|---|
PWM_FREQ_HZ |
1000 | L298N 是双极型,别超 5kHz |
PWM_MAX_DUTY |
1023 | 8266 analogWrite 满量程 = 全速 |
PWM_MIN_DUTY |
300 | 低于此值减速电机只嗡嗡不转,滑块 1% 也保底 300 |
RAMP_MS |
150 | 占空比 0→满的爬升时间,抑制起步打滑和电流冲击 |
- 指令:
/api/cmd?c=speed&v=70 - 改速度不用重发方向指令,正在跑会立刻生效,且保持当前转向姿态
- 100% 就是原来的全速,软件没法更快(见文末「提速边界」)
转向(2026-09-10 重写)
只有两个后轮 = 差速转向,转弯半径由两轮速度差决定:
R = (轮距 / 2) × (v外 + v内) / (v外 − v内)
| 动作 | 内轮 | 外轮 | 实测占空比 | 转弯半径 | 说明 |
|---|---|---|---|---|---|
| 左转 | 左 35% | 右 100% | 358 / 1023 | ≈1.04×轮距 | 平滑弧线,保住 68% 推力 |
| 右转 | 右 35% | 左 100% | 1023 / 358 | ≈1.04×轮距 | 同上 |
| 掉头 | 左反 70% | 右正 70% | −716 / 716 | ≈0 | 原地自旋,只用于掉头 |
| 后退 | 80% | 80% | 818 / 818 | — | 倒车限速 |
旧做法是内轮 0%(停死)——车被停住那只轮子拖着蹭地,顿挫、掉一半推力、还磨胎。
改成 35% 后内侧继续滚,转向依然很急,但顺滑得多。
想更急 → TURN_INNER_PCT 往 20 调;想更飘 → 往 50 调。
SPIN_PCT 不给 100%,双轮反向满速电流翻倍,L298N 和电池都扛不住。
低速时转向会变宽:滑块 50% 时内外比约 46%,30% 时约 59%。
因为PWM_MIN_DUTY兜底生效了(内轮算下来低于 300 会被抬到 300)。这是有意保留的——不然内轮直接停转。
串口测试指令(115200)
不用连网页也能测:
f 前进 b 后退 l 左转 r 右转 u 掉头 s 停止
speed 70 设速度 turn 35 转向急缓
trim -8 跑偏修正(负=补左轮 正=补右轮)
stat 看每侧 dir / 目标占空比 / 实际占空比
wifi 看热点状态和已连设备数
测试脚本在项目里:Mac管家/wifi_boat/tools/
python3 tools/test_motor.py # 自动跑完全部动作,打印实测占空比
python3 tools/demo_drive.py # 接 12V 电池后跑真车演示序列
python3 tools/demo_drive.py 50 # 50% 速度演示
提速边界(重要)
6V 电机吃的是 12V 电池经 L298N 后的 ~9.5V(L298N 压降约 2.5V),已超速 58%。
所以软件层面 100% 占空比就是天花板,再想快只有硬件:
| 方案 | 提升 | 备注 |
|---|---|---|
| 换 MOSFET 驱动(TB6612 / DRV8871) | +15~25% | 抢回 2V 压降 |
| 换高转速 N20(150→300rpm) | +100% | 扭矩减半,小车载得动 |
| 换更大轮子 | 线速度 ∝ 轮径 | 扭矩需求上升 |
| 加电池电压 | ❌ 别做 | 6V 电机会烧 |
网页按钮
速度滑块 + 前进 / 左转 / 右转 / 后退 / 掉头 / 停止 / 前灯 / 后灯
- 左转:左轮 35%、右轮 100%(见上文「转向」)
- 右转:右轮 35%、左轮 100%
- 掉头:左反、右正,各 70%
- 停止立刻切断 ENA/ENB,不走斜坡
转弯代码(src/motor/Motor.cpp):
void Motor::left() {
/* 左转:左轮是内侧,慢;右轮外侧,快 */
setSides(1, TURN_INNER_PCT, 1, TURN_OUTER_PCT);
}
void Motor::right() {
setSides(1, TURN_OUTER_PCT, 1, TURN_INNER_PCT);
}
void Motor::uturn() {
setSides(-1, SPIN_PCT, 1, SPIN_PCT);
}