用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速度显示、甚至集成简单的游戏等。
