用Espruino和JavaScript打造电动滑板遥控器:从硬件选型到固件开发全解析
1. 项目概述:为什么选择Espruino来造一个滑板遥控器?
几年前,当我第一次尝试给我的长板加装电驱套件时,最头疼的不是电机和电池,而是那个手感廉价、功能单一的成品遥控器。它要么延迟高得吓人,要么续航短得可怜,最关键的是,它完全“不属于”我的滑板。作为一个喜欢折腾的开发者,我萌生了自己动手做一个的念头。市面上主流的方案是Arduino搭配C/C++,或者是ESP32搭配MicroPython,但我最终选择了 Espruino ——一个允许你用 JavaScript 来编写嵌入式固件的微控制器平台。这个决定让整个开发过程变得异常“友好”。
你可能好奇,为什么是JavaScript?在嵌入式领域,C语言是当之无愧的王者,性能极致,但对新手和快速迭代来说,门槛不低。Espruino的核心价值在于,它将Web开发中那套熟悉的、基于事件驱动的编程模型带到了硬件层面。你不需要纠结于内存管理、指针这些底层细节,可以更专注于业务逻辑:比如“当摇杆被推动时,通过无线电发送一个油门值”。这对于需要快速验证想法、注重开发效率的DIY项目来说,简直是神器。本次要做的电动滑板遥控器,就是一个绝佳的实践案例:它需要读取传感器(霍尔传感器或摇杆)、驱动显示屏、管理电池电量,并通过无线模块与滑板上的接收器通信。用Espruino,你可以像写一个网页交互脚本一样,把这些功能串联起来。
这个项目适合谁呢?首先是有一定动手能力的创客或电子爱好者,你想深入理解无线遥控系统是如何工作的。其次是前端或全栈开发者,你想跨出舒适区,用自己熟悉的JavaScript语言触碰物理世界,这会是一次极其有趣的体验。当然,它也适合那些对成品遥控器不满意,渴望拥有一个完全自定义、从外观到交互都由自己掌控的滑板玩家。整个项目会涉及硬件选型、焊接组装、电路设计基础以及Espruino编程,我会把每个环节的“为什么”和“怎么做”都掰开揉碎讲清楚,尤其是那些官方文档里不会提的坑和技巧。
2. 核心硬件选型与设计思路解析
自己造遥控器,硬件是骨架。选型不是堆砌最贵的部件,而是在成本、可靠性、易用性和项目需求之间找到最佳平衡点。我的设计目标是: 低延迟、高可靠性、长续航、可编程性强 。下面我们来拆解每一个关键部件背后的考量。
2.1 微控制器:为何是Espruino MDBT42Q?
遥控器的大脑,我选择了 Espruino MDBT42Q 。市面上可选的主控很多,比如经典的Arduino Nano(ATmega328P),或者功能更强的ESP32。选择MDBT42Q,主要是基于以下几点综合判断:
- 内置蓝牙与JavaScript引擎 :MDBT42Q是一块基于nRF52832芯片的板子,它集成了低功耗蓝牙(BLE)射频模块。这意味着无线通信功能是原生的,无需额外模块,简化了设计和布线。更重要的是,它预装了Espruino固件,开机即是一个JavaScript解释器。
- 开发效率与调试便利性 :用JavaScript开发,你可以通过USB连接电脑,在Web IDE或命令行里实时执行代码、查看变量、调试逻辑,就像在浏览器控制台里调试网页一样。这种即时反馈对于调试传感器读数、无线数据包等动态过程至关重要,远比“编写-编译-上传-重启-查看”的传统嵌入式流程高效。
- 充足的IO与性能 :nRF52832拥有足够的GPIO引脚来连接屏幕、传感器、开关,其ARM Cortex-M4内核的性能足以流畅处理本项目的逻辑、驱动OLED以及运行BLE协议栈,不会有性能瓶颈。
注意 :Espruino官方也有其他板型,如Puck.js、Espruino WiFi等。选择MDBT42Q是因为它引脚引出完整,便于在面包板或洞洞板上搭建原型,且价格相对适中。如果你手头有ESP32开发板,也可以刷入Espruino固件来替代,但初次配置会稍麻烦一些。
2.2 油门信号输入:霍尔传感器 vs. 游戏摇杆
这是遥控器的核心输入设备,决定了油门控制的精度和手感。原文提到了两种方案: 线性比率式霍尔效应传感器 (如DRV5055)和 游戏摇杆 。
-
线性霍尔传感器(推荐方案) :
- 原理 :传感器输出一个与所处磁场强度成正比的模拟电压。我们将一块小磁铁固定在遥控器的滑块或扳机上,当用户推动时,磁铁靠近或远离传感器,引起磁场变化,从而输出变化的电压。控制器读取这个电压值,就能精确得知油门位置。
- 优势 : 无接触、无磨损、寿命极长、精度高、手感平滑线性 。这是商用高端遥控器的常见方案,能提供非常跟手的控制体验。
- 选型要点 :DRV5055A2QLPG是一款常见的3.3V供电、模拟量输出的霍尔传感器。购买时务必注意其灵敏度(mV/mT)和供电电压范围是否与你的系统(3.3V)匹配。同时需要准备一块尺寸合适的钕铁硼磁铁。
-
游戏摇杆(备选/快速原型方案) :
- 原理 :通常是两个电位器(分别对应X/Y轴)和一个按键。我们只使用其中一个轴(如Y轴)作为油门信号。
- 优势 : 极易获取、接线简单(通常只有VCC, GND, VRX, VRY, SW五个引脚)、成本低廉 。适合快速验证想法或预算极其有限的情况。
- 劣势 :电位器属于机械接触式元件,存在物理磨损,长期使用后可能出现跳动、噪音或线性度变差的问题。手感上也通常不如精心调校的霍尔方案。
我的建议与最终选择 :为了项目的长期可靠性和专业度, 强烈推荐使用霍尔传感器方案 。虽然初期需要设计一个机械结构来固定磁铁和传感器(比如3D打印一个滑块导轨),但这部分投入是值得的。在本项目中,我将以DRV5055为例进行详细讲解。如果你暂时用摇杆入门,也完全可行,大部分代码逻辑是通用的,只是读取的引脚和数值范围需要调整。
2.3 人机交互:OLED显示屏与按键
遥控器需要给用户反馈,最基本的就是显示当前状态。
-
OLED显示屏(SSD1306, 128x32) :选择I2C接口的0.96英寸OLED屏,原因如下:
- 省电 :OLED是自发光,显示深色像素时几乎不耗电,非常适合电池设备。
- 高对比度 :即使在阳光下也有不错的可视性。
- 接口简单 :I2C总线只需连接SDA(数据)、SCL(时钟)、VCC(3.3V)、GND四根线,节省IO口。
- 分辨率够用 :128x32像素足以显示速度、电量、连接状态、模式等关键信息。
-
按键 :原文建议至少一个轻触开关和一个拨动开关。
- 轻触开关 :用作“功能键”,例如短按切换显示页面,长按开关机或进入配对模式。
- 拨动开关 :用作“硬件开关”,最安全的用途是控制整个遥控器的电源通断。即使用软件实现了关机,一个物理断电开关也能彻底杜绝待机耗电,避免下次想玩时电池已耗尽。
2.4 供电系统:电池管理与安全
遥控器需要移动供电,安全、高效是首要原则。
-
电池 :选用单节 18650锂离子电池 。其理由很充分:能量密度高、放电能力强、规格统一易获取、有成熟的充电和保护方案。容量建议选择2600mAh以上,以确保数周的续航。
-
充电/保护板 :这是 绝对不能省略 的部分!你需要一块集成了TP4056充电芯片和DW01A保护芯片的小板子。它的作用是:
- 充电管理 :提供稳定的5V转4.2V充电流程,支持恒流恒压充电。
- 过充/过放保护 :当电池电压高于4.25V或低于2.5V左右时,自动切断电路,防止电池损坏甚至发生危险。
- 短路保护 。
- 接线 :电池的正负极焊接到保护板的B+和B-;保护板的输出端(OUT+和OUT-)则作为整个遥控器系统的电源正负极。
-
电压检测电路(关键细节) :为了在OLED上显示电池电量,我们需要测量电池电压。但Espruino的模拟输入引脚只能承受最大3.3V电压,而18650电池满电电压约4.2V。直接连接会烧毁芯片!因此必须使用一个 电压分压电路 。通常用两个电阻串联,例如一个100kΩ(R1)和一个47kΩ(R2)的电阻。电池电压接在串联电阻两端,从两个电阻中间连接ADC引脚。这样,ADC读取的电压
V_adc = V_bat * (R2 / (R1 + R2))。计算一下:4.2V * (47k / (100k+47k)) ≈ 1.34V,安全地落在3.3V以内。在代码中,我们再通过公式V_bat = V_adc * ((R1+R2) / R2)反算出真实电池电压。
3. 电路连接与硬件组装实操指南
理论清楚了,现在开始动手。这一步需要耐心和细心,良好的硬件基础是稳定运行的保障。
3.1 绘制连接示意图(虽无原理图,但心中有图)
虽然原文没有提供原理图,但我们可以根据功能模块梳理出清晰的接线表。假设我们使用霍尔传感器方案。
| 元件 | 引脚 | 连接到 Espruino MDBT42Q 引脚 | 说明 |
|---|---|---|---|
| OLED (SSD1306) | VCC | 3.3V | 电源正极 |
| GND | GND | 电源地 | |
| SDA | P0.30 (SDA) | I2C数据线 | |
| SCL | P0.31 (SCL) | I2C时钟线 | |
| 霍尔传感器 (DRV5055) | VCC | 3.3V | 电源正极 |
| GND | GND | 电源地 | |
| VOUT | P0.02 (A0) | 模拟信号输出,接ADC引脚 | |
| 拨动开关 | 一端 | 电池保护板OUT+ | 接入主电源正极 |
| 另一端 | 整个系统VCC总线 | 控制总电源通断 | |
| 轻触开关 | 一端 | P0.15 (配置内部上拉) | 作为数字输入 |
| 另一端 | GND | 按下时接地,触发低电平 | |
| 电压分压电路 | R1 (100k)一端 | 电池保护板OUT+ (Vbat) | 接电池正极 |
| R1, R2连接点 | P0.03 (A1) | 分压后电压测量点 | |
| R2 (47k)一端 | GND | 接地 |
接线要点与实操心得 :
- 电源走线 :建议用较粗的导线(如AWG22)连接电池和保护板,以及从开关到主VCC总线。大电流路径电阻要小。
- 信号线 :I2C的SDA和SCL线最好并列走线,避免过长。如果遇到屏幕显示不稳定,可以尝试在SDA和SCL线上各加一个4.7kΩ的上拉电阻到3.3V(有些模块已集成)。
- 接地 :务必确保 所有元件的GND引脚最终都连接到同一个“地” ,即电池保护板的OUT-。混乱的接地是噪声和不稳定性的主要来源。建议在洞洞板或PCB上布置一条粗壮的“地线总线”。
- 焊接 :确保焊点圆润光滑,无虚焊、短路。焊接Espruino引脚时,电烙铁温度不宜过高(350°C左右),并确保接地良好,防止静电击穿芯片。
3.2 结构组装与固定技巧
硬件电路不能“飞线”裸奔,需要一个外壳来保护和组织。
-
外壳设计与获取 :你可以使用现成的塑料项目盒子钻孔改造,也可以像我一样进行 3D打印 。设计外壳时需要考虑:
- 所有元件的定位孔(屏幕、开关、USB充电口)。
- 霍尔传感器的固定位置以及磁铁滑块的滑动轨道。这个轨道需要光滑,阻力适中,可以用一小段铝型材或精心设计的塑料轨道。
- 电池仓的形状和固定方式。
- 上下盖的固定方式(螺丝柱或卡扣)。
- 可以在Thingiverse等网站搜索“electric skateboard remote”找现成模型修改,这是最快捷的方式。
-
元件固定 :
- 屏幕 :不建议直接用热熔胶,夏天易软化。可以使用少量 环氧树脂胶 或 双面泡棉胶 ,后者有一定缓冲作用。
- 主控板与电池 :在壳体内壁设计卡槽或使用尼龙扎带固定。电池务必用绝缘胶带包裹好电极后再固定,防止短路。
- 开关与传感器 :确保开关拨动顺畅,传感器位置精准。霍尔传感器应使用胶水牢牢固定在其安装座上。
-
总装 :先将所有元件在外壳内大致摆位,理清线材。连接好所有导线后,仔细检查一遍再通电测试。最后合上盖子,拧紧螺丝。一个专属于你的遥控器硬件部分就诞生了。
4. Espruino固件开发详解
硬件准备就绪,现在注入灵魂。我们将用JavaScript编写遥控器的所有逻辑。请先确保你的电脑已安装Espruino Web IDE或配置好命令行工具。
4.1 开发环境搭建与基础代码结构
首先,通过USB线将MDBT42Q连接电脑。打开Espruino Web IDE,选择正确的串口,点击连接。连接成功后,左侧控制台会显示“>`”提示符。
一个健壮的遥控器固件应该采用模块化、事件驱动的结构。下面是一个基础框架:
// ====== 硬件引脚定义 ======
var PIN = {
hallSensor: A0, // 霍尔传感器模拟输入
batVoltage: A1, // 电池电压检测(接分压中点)
btnFunction: D15, // 功能按键 (P0.15)
led: LED1, // 板载LED,用于指示
};
// ====== 全局变量与状态 ======
var state = {
throttle: 0, // 当前油门值,范围 -1000 到 1000 (或 0-1000)
batteryPercent: 100,
isConnected: false,
displayPage: 0, // 当前显示页面索引
};
// ====== 初始化函数 ======
function initHardware() {
console.log("Initializing hardware...");
// 1. 配置功能按键(内部上拉,按下为低电平)
pinMode(PIN.btnFunction, "input_pullup");
setWatch(function(e) {
onButtonPressed(e);
}, PIN.btnFunction, { repeat: true, edge: "falling", debounce: 50 });
// 2. 初始化I2C并连接OLED屏幕
I2C1.setup({ scl: D31, sda: D30, bitrate: 400000 });
var g = require("SSD1306").connect(I2C1, function() {
console.log("OLED Ready");
g.clear();
g.drawString("Remote Booting...", 0, 0);
g.flip();
});
global.G = g; // 将图形对象设为全局,方便调用
// 3. 初始化模拟输入(ADC)
// Espruino默认已启用,无需额外设置
// 4. 初始化蓝牙(BLE)
initBluetooth();
console.log("Hardware init done.");
}
// ====== 主循环与核心逻辑 ======
function mainLoop() {
// 1. 读取传感器
readThrottle();
// 2. 读取电池电压(不需要每次循环都读,比如每10次读一次)
// 3. 更新显示
updateDisplay();
// 4. 通过BLE发送数据(如果已连接)
if (state.isConnected) {
sendDataOverBLE();
}
}
// 设置一个定时器,每50ms执行一次主循环(20Hz更新率)
setInterval(mainLoop, 50);
// ====== 程序入口 ======
initHardware();
console.log("Electric Skateboard Remote Firmware Started.");
代码解析与注意事项 :
setWatch:这是Espruino处理按键事件的优雅方式。它设置了一个“监视器”,当引脚电平发生指定变化(edge: "falling"下降沿,即按下瞬间)时触发回调函数。debounce: 50是 防抖参数 ,至关重要,可以滤除按键机械抖动产生的误触发信号。require("SSD1306"):这是Espruino为SSD1306驱动提供的内置模块,简化了屏幕操作。- 主循环频率 :
setInterval(mainLoop, 50)即20Hz。对于遥控器,20-50Hz的更新率足以保证操控跟手。更高的频率会增加功耗,需要权衡。
4.2 核心功能模块实现
现在我们来填充框架中的几个核心函数。
1. 读取油门值 ( readThrottle )
var THROTTLE_DEADZONE = 50; // 死区范围,避免中间位置漂移
function readThrottle() {
// 读取ADC原始值 (0-1.0 对应 0-3.3V)
var analogValue = analogRead(PIN.hallSensor);
// 根据你的传感器和磁铁安装方式,将电压映射到油门范围
// 示例:假设静止时电压为0.5V,最大推力时电压为0.1V,最大拉力时电压为0.9V
var neutralVoltage = 0.5;
var range = 0.4; // 从中性点到最大值的电压变化量
var rawThrottle = (analogValue - neutralVoltage) / range; // 范围约为 -1.0 到 1.0
// 应用死区
if (Math.abs(rawThrottle) < (THROTTLE_DEADZONE / 1000.0)) {
rawThrottle = 0;
}
// 限制范围并转换为整数(例如 -1000 到 1000)
rawThrottle = Math.max(-1.0, Math.min(1.0, rawThrottle));
state.throttle = Math.round(rawThrottle * 1000);
// 可选:添加指数曲线或平滑滤波,让操控手感更细腻
// state.throttle = applyExpoCurve(state.throttle);
}
实操心得:校准是关键!
neutralVoltage和range这两个值需要 实地校准 。在磁铁处于你定义的“中立位”时,读取analogValue并赋值给neutralVoltage。然后将磁铁推到最大位置,读取电压,计算与中立位的差值作为range。这个校准过程最好做一个简单的配置模式,通过按键触发并显示当前电压值到屏幕。
2. 读取电池电量 ( readBatteryVoltage )
var R1 = 100; // 单位:千欧姆
var R2 = 47; // 单位:千欧姆
var VOLTAGE_DIVIDER_RATIO = (R1 + R2) / R2; // 分压比
var BATTERY_FULL = 4.2; // 满电电压
var BATTERY_EMPTY = 3.2; // 保护板截止电压,建议略高于实际截止电压
function readBatteryVoltage() {
var adcVoltage = analogRead(PIN.batVoltage); // 读取的是分压后的电压
var realVoltage = adcVoltage * VOLTAGE_DIVIDER_RATIO;
// 简单线性计算电量百分比(仅供参考,锂电池放电曲线非绝对线性)
var percent = (realVoltage - BATTERY_EMPTY) / (BATTERY_FULL - BATTERY_EMPTY) * 100;
state.batteryPercent = Math.max(0, Math.min(100, Math.round(percent)));
return realVoltage;
}
注意 :锂电池电量计算是个复杂问题,电压法只是一个粗略估计。更准确的方法是库仑计(测量进出电荷),但电路复杂。对于遥控器,电压估算法已完全够用。可以在主循环中每5秒调用一次此函数,避免频繁ADC读取。
3. 驱动OLED显示 ( updateDisplay )
function updateDisplay() {
var g = global.G;
if (!g) return;
g.clear();
g.setFontVector(20); // 使用大号字体显示主要信息
// 根据显示页面切换内容
switch(state.displayPage) {
case 0: // 页面0:主要信息
g.drawString("THR:" + state.throttle, 0, 0);
g.setFontBitmap();
g.drawString("BAT:" + state.batteryPercent + "%", 0, 25);
g.drawString(state.isConnected ? "CONN" : "NO CONN", 70, 25);
break;
case 1: // 页面1:电压/调试信息
var volt = readBatteryVoltage();
g.setFontVector(16);
g.drawString(volt.toFixed(2) + "V", 0, 0);
g.setFontBitmap();
g.drawString("ADC:" + analogRead(PIN.hallSensor).toFixed(3), 0, 20);
break;
}
g.flip(); // 将缓冲区内容刷到屏幕
}
// 按键事件处理:切换显示页面
function onButtonPressed(e) {
var duration = e.time - e.lastTime; // 粗略计算按下时长
if (duration > 0.8) { // 长按
// 进入配对模式或关机
enterPairingMode();
} else { // 短按
state.displayPage = (state.displayPage + 1) % 2; // 在0和1之间切换
updateDisplay();
}
}
4. 低功耗蓝牙(BLE)通信 ( initBluetooth 和 sendDataOverBLE ) 这是实现无线遥控的核心。我们将使用BLE的“自定义服务”(Custom Service)和“特征值”(Characteristic)来传输数据。
var bleServiceUUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"; // 自定义服务UUID
var txCharUUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"; // 发送特征 (遥控器->滑板)
var rxCharUUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"; // 接收特征 (滑板->遥控器,可选)
function initBluetooth() {
NRF.setAdvertising([], {
name: "ESK8-Remote-" + NRF.getAddress().substr(-5),
showName: true,
connectable: true,
scannable: true,
discoverable: true,
});
NRF.setServices({
[bleServiceUUID]: {
[txCharUUID]: {
value: [], // 初始值空
maxLen: 20,
writable: false,
readable: true,
notify: true, // 启用通知,滑板端可以订阅此特征值的变化
description: "Throttle Data",
},
// 可以添加更多特征值,如接收滑板回传的速度、温度等
}
}, { advertise: [bleServiceUUID] });
// 监听连接事件
NRF.on('connect', function(addr) {
console.log("Connected to", addr);
state.isConnected = true;
digitalWrite(PIN.led, 1); // LED亮表示已连接
});
NRF.on('disconnect', function(addr) {
console.log("Disconnected");
state.isConnected = false;
digitalWrite(PIN.led, 0); // LED灭表示未连接
});
}
function sendDataOverBLE() {
if (!state.isConnected) return;
// 将油门、电池电量等数据打包成一个数组缓冲区
// 协议设计示例:前2个字节为油门值(有符号16位整数),第3个字节为电量百分比
var buffer = new ArrayBuffer(3);
var view = new DataView(buffer);
view.setInt16(0, state.throttle, true); // true 表示小端字节序
view.setUint8(2, state.batteryPercent);
// 更新特征值,已连接的中央设备(滑板)会收到通知
NRF.updateServices({
[bleServiceUUID]: {
[txCharUUID]: {
value: buffer,
notify: true,
}
}
});
}
BLE协议设计要点 :这里定义了一个简单的3字节协议。在实际项目中,你可能需要传输更多数据,比如遥控器ID、校验和、按钮状态等。务必设计一个清晰、可扩展的数据结构。 notify: true 使得滑板端无需主动轮询,一旦遥控器更新数据,滑板能立即收到,这是实现低延迟的关键。
4.3 代码上传与固化
在Web IDE中编写调试完所有代码后,需要将其保存到Espruino的“闪存”中,实现上电自启动。
- 连接设备 :在Web IDE中点击“Connect”。
- 发送代码 :将完整的代码粘贴到右侧代码编辑器,点击“Send to Espruino”。此时代码在RAM中运行。
- 测试 :操作摇杆/滑块,观察屏幕显示和变量输出是否正常。用手机BLE扫描工具(如
nRF Connect)搜索设备名,尝试连接并查看服务/特征值。 - 保存固化 :在左侧控制台输入命令
save()并回车。这会将当前内存中的代码保存到Flash。下次断电重启后,设备会自动运行这些代码。 - 安全备份 :点击Web IDE的“Save”按钮,将代码保存到本地电脑。
重要警告 :
save()命令会覆盖之前的保存内容。如果新代码有致命错误导致设备无法启动,你可能需要“擦除”Flash。方法是:断开USB,按住MDBT42Q上的按钮(如果有的話,通常是BTN1),再插入USB,待红灯快速闪烁后松开,此时设备进入“引导加载程序”模式,再在IDE中点击“Connect”并发送E.setBootCode()等命令清空。具体操作请查阅Espruino官方文档关于你所用板型的恢复方法。 务必在代码稳定后再执行save()。
5. 系统调试、优化与问题排查
即使按照步骤操作,第一次也难免遇到问题。这里汇总了常见坑点和解决方案。
5.1 上电无反应或异常
- 检查电源 :用万用表测量电池保护板输出电压是否正常(~3.7V-4.2V)。检查拨动开关是否接触良好。
- 检查接线 :重点检查VCC和GND是否接反、虚焊。尤其是OLED和Espruino的供电。
- 观察指示灯 :MDBT42Q上电后通常有电源指示灯(常亮)和状态LED(可能闪烁)。如果毫无反应,可能是主板损坏或电源问题。
5.2 屏幕不显示或花屏
- 检查I2C地址 :SSD1306的I2C地址通常是0x3C。在控制台输入
I2C1.scan()查看是否能扫描到设备。 - 检查接线 :确认SDA、SCL是否接对,接触是否良好。尝试加上拉电阻(4.7kΩ到3.3V)。
- 供电不足 :屏幕启动瞬间电流可能较大,如果电源线太细或电池电量低,可能导致初始化失败。尝试外接稳定3.3V电源测试。
5.3 油门读数不稳定或跳动
- 电源噪声 :电机、无线模块等都可能引入噪声。在霍尔传感器的VCC和GND之间并联一个 0.1uF的陶瓷电容 ,可以很好地滤除高频噪声。
- ADC参考电压 :确保Espruino的ADC参考电压稳定。如果使用电池直接供电,电压会随着放电缓慢下降,影响ADC精度。可以在代码中定期读取一个已知的、稳定的内部参考电压来进行软件补偿,但对于本项目,影响微乎其微。
- 软件滤波 :在
readThrottle函数中加入软件滤波算法,如移动平均滤波或一阶低通滤波。var throttleHistory = new Array(5).fill(0); function readThrottleWithFilter() { var raw = // ... 原始的ADC读取和映射计算 throttleHistory.shift(); // 移除最旧的值 throttleHistory.push(raw); var filtered = throttleHistory.reduce((a,b)=>a+b) / throttleHistory.length; state.throttle = Math.round(filtered * 1000); }
5.4 BLE无法连接或数据不更新
- 设备未广播 :在手机BLE扫描工具中查看是否能发现“ESK8-Remote-xxxxx”设备。如果没有,检查
initBluetooth函数是否被执行,或者尝试在控制台手动执行NRF.restart()。 - 服务/特征值未发现 :连接后,在
nRF Connect中查看是否列出了我们定义的服务UUID和特征值UUID。确保UUID字符串格式正确。 - 数据未通知 :确认在
sendDataOverBLE中,更新特征值时设置了notify: true。同时,滑板端(中央设备)必须 订阅(Subscribe) 该特征值的通知,才能自动接收数据。 - 连接不稳定 :检查天线周围是否有金属屏蔽。确保遥控器和滑板接收器之间的距离在合理范围内(开阔地通常10-20米没问题)。
5.5 续航时间短
- 测量待机电流 :使用万用表电流档串联在电池和保护板之间,在遥控器待机(BLE保持广播)时测量电流。正常应在几百微安到几毫安之间。如果达到几十毫安,说明有地方漏电。
- 优化软件 :
- 降低屏幕刷新率,或者在不操作时关闭屏幕背光(如果支持)。
- 降低主循环频率,比如从20Hz降到10Hz。
- 在BLE连接后,可以适当降低广播功率(
NRF.setAdvertising中的power参数)。 - 实现真正的休眠:当一段时间无操作后,让Espruino进入深度睡眠模式(
deepSleep),仅通过按键中断唤醒。这需要更复杂的电源电路设计和代码,是进阶优化方向。
完成以上所有步骤,你的自定义电动滑板遥控器就应该能够稳定工作了。从一堆散件到一个可以精准控制滑板加速减速的无线设备,这个过程中获得的硬件知识、嵌入式编程思维和解决问题的能力,远比一个成品遥控器更有价值。这个项目是一个完美的起点,你可以在此基础上继续扩展,比如增加震动反馈、GPS速度显示、甚至集成简单的游戏等。
更多推荐

所有评论(0)