前段时间在树莓派上控制USB继电器,查了下USB继电器提供了在linux下的驱动和操控代码,可以编译成本地程序。但是目前需求是需要使用web页面来访问控制继电器,如何做到呢?这里我想了两个方案。
方案一:shelljs调用本地程序
这个方案说实话相对比较简单,就是使用shelljs来启动本地应用,同时带着参数。但是经过实际测试,每次都需要2s才能完成调用。可能是我的树莓派B+太老了吧,性能比较一般。但是单独运行本地应用,基本是毫秒级别就完成了。所以这个方案就被否决了。
方案二:使用本地C扩展直接驱动继电器
这个方案是我们今天的主角。nodejs本身支持本地扩展,参照官方文档,将在C语言下的函数调用包装成了在javascript下的函数调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
void SetRelay(const FunctionCallbackInfo<Value>& args) { // 获取v8运行环境 Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); // 判断参数个数是否正确 if (args.Length() < 3) { isolate->ThrowException(Exception::TypeError( String::NewFromUtf8(isolate, "Wrong number of arguments"))); return; } // 验证参数类型是否正确 if (!args[0]->IsNumber() || !args[1]->IsNumber() || !args[2]->IsNumber()) { isolate->ThrowException(Exception::TypeError( String::NewFromUtf8(isolate, "Wrong arguments"))); return; } // 获取所需参数 int deviceId = (int)args[0]->NumberValue(); int portId = (int)args[1]->NumberValue(); int status = (int)args[2]->NumberValue(); int ret = -2; // 进行驱动逻辑操作 libusb_device_handle *devh = NULL; devh = USBRELAY_Open(deviceId); if (devh == NULL) { ret = -1; } else { ret = USBRELAY_SetRelay(devh, portId, status); USBRELAY_Close(devh); } // 包装返回值 Local<Number> num = Number::New(isolate, ret); // 传回返回值 args.GetReturnValue().Set(num); } |
其实整个过程不麻烦,主要代码是要完成从javascript运行环境中得到传递进来的参数,验证参数,再经过C调用,把返回值包裹进v8的返回值对象中,再传回去。
详细代码请参照 https://github.com/sharpbai/UI021-usbrelay-node-modules
这个方案的速度非常快,每次调用几乎立即就生效了!