node-sense-hat中RTIMULib2的getData()作用域失效及定时读取问题
树莓派Sense Hat传感器读取问题与解决
问题背景
我使用Node.js的node-sense-hat包读取树莓派2上Sense Hat的传感器数据并保存到文件。单次读取或无限循环读取可正常工作,但需要在读取间添加延迟以确保测量可靠。
首次尝试:使用sleep包导致回调失效
尝试用sleep包添加延迟,但会让整个Node进程休眠,回调函数无法执行:
const libsensehat = require("node-sense-hat"); const libsleep = require("sleep"); var sleep = libsleep.sleep; var IMU = new libsensehat.Imu.IMU; function sensor_read_callback(err, data) { ...process data... } while(true) { IMU.getData(sensor_read_callback); sleep(1000); // 单位:ms }
这段代码无法写入数据,因为Node休眠时回调无法执行。
第二次尝试:setInterval触发回调错误
改用setInterval()后,出现Node.js错误:
const libsensehat = require("node-sense-hat"); var IMU = new libsensehat.Imu.IMU; var sensor_read_interval_handle; function sensor_read_callback(err, data) { ...process data... } sensor_read_interval_handle = setInterval(IMU.getData, 1000, sensor_read_callback); // 单位:ms }
错误信息:
internal/validators.js:223 throw new ERR_INVALID_CALLBACK(callback); ^ TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received undefined at setInterval (timers.js:210:3) at Object.<anonymous> (/root/uwrf-rocket-club-flight-recorder/sense-hat.js:92:31) at Module._compile (internal/modules/cjs/loader.js:1114:14) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1143:10) at Module.load (internal/modules/cjs/loader.js:979:32) at Function.Module._load (internal/modules/cjs/loader.js:819:12) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:75:12) at internal/main/run_main_module.js:17:47 { code: 'ERR_INVALID_CALLBACK' }
将IMU.getData替换为console.log后可正常输出,说明问题出在node-sense-hat的RTIMULib2上。
最终解决:bind绑定作用域
发现RTIMULib2的getData()需要处于父对象作用域才能访问,使用bind绑定IMU后代码正常工作:
const libsensehat = require("node-sense-hat"); var IMU = new libsensehat.Imu.IMU; var sensor_read_interval_handle; function sensor_read_callback(err, data) { ...process data... } sensor_read_interval_handle = setInterval(IMU.getData.bind(IMU), 1000, sensor_read_callback); // 单位:ms }
我的问题
- 如何按固定间隔(调试后约20ms)读取Sense Hat的数据?
- RTIMULib2出现此行为的原因是什么?是否是IMU对象作用域变化导致绑定失效或进程退出?
注:使用Node.js v14.21.3,因node-sense-hat不兼容新版本Node(v19.8.1)。
问题解答
1. 按20ms固定间隔读取数据的实现
你已验证的bind方案可直接调整间隔为20ms使用,注意回调内的处理逻辑需用异步方式(比如异步文件写入),避免阻塞读取节奏:
const libsensehat = require("node-sense-hat"); const fs = require('fs'); const IMU = new libsensehat.Imu.IMU; let sensorReadInterval; function sensorReadCallback(err, data) { if (err) { console.error('读取传感器数据失败:', err); return; } // 异步写入文件,避免阻塞 fs.appendFile('sensor-data.log', JSON.stringify(data) + '\n', (writeErr) => { if (writeErr) console.error('写入文件失败:', writeErr); }); } // 设置20ms间隔读取 sensorReadInterval = setInterval(IMU.getData.bind(IMU), 20, sensorReadCallback); // 如需停止读取,调用:clearInterval(sensorReadInterval);
另外,也可使用setTimeout递归调用,保证每次读取完成后再等待20ms,避免因回调耗时导致间隔不准:
const libsensehat = require("node-sense-hat"); const fs = require('fs'); const IMU = new libsensehat.Imu.IMU; const READ_INTERVAL = 20; function sensorReadCallback(err, data) { if (err) { console.error('读取传感器数据失败:', err); } else { // 异步处理数据 fs.appendFile('sensor-data.log', JSON.stringify(data) + '\n', (writeErr) => { if (writeErr) console.error('写入文件失败:', writeErr); }); } // 读取完成后,触发下一次读取 setTimeout(() => IMU.getData(sensorReadCallback), READ_INTERVAL); } // 启动首次读取 IMU.getData(sensorReadCallback);
2. RTIMULib2出现该行为的原因
这是JavaScript函数作用域与this绑定的问题,和进程退出无关:
- 当你直接把
IMU.getData传给setInterval时,函数会脱离IMU对象的上下文,此时函数内部的this不再指向IMU实例。 - RTIMULib2的
getData方法内部依赖this访问IMU实例的内部状态或资源,当this指向错误(比如默认指向全局对象或undefined),方法无法正常获取回调参数,最终导致传入的回调被识别为undefined,触发ERR_INVALID_CALLBACK错误。 - 使用
IMU.getData.bind(IMU)会强制将函数内部的this绑定到IMU实例上,确保方法执行时能访问正确的上下文,因此可以正常工作。
内容的提问来源于stack exchange,提问作者deepthink
相关产品推荐
相关产品推荐

