上次说到为什么选择通过扩展NodeJs来实现硬件控制,接下来就看看如何来扩展:
NodeJs的扩展是非常容易的,像几乎所有的教程一下,我们先来个HelloWorld:
// helloworld.cc
#include <node.h> //首先引入node头文件
using namespace v8; //使用V8和,node命名空间
using namespace node;
/**
* 这个就是我们要扩展到NodeJs的c函数了,函数的原型一定得是
* Handle<Value> function_name(const Arguments& args);
* 里面就可以使用C++写各种底层操作了
*/
Handle<Value> sayHelloWorld(const Arguments& args){
HandleScope scope;
printf("Hello World!\n"); //打印一个“Hello World!”
return scope.Close(Undefined()); //返回undefined
}
/**
* 初始化函数,将需要导出的方法绑定到js对象上
* 类似 export.XXX = function(){};
*/
void init(Handle<Object> target) {
NODE_SET_METHOD(target, "say", sayHelloWorld);
}
NODE_MODULE(hello_world, init); //Node require的时候会执行的函数,整个模块的入口
有了C文件,怎么编译呢?NodeJs提供了node-gyp作为模块的编译工具,使用起来也非常简单,所需要的仅仅只有一个Json格式的binding.gyp文件**(里面的注释只是为了说明参数含义,但是会破坏Json结构,实际使用的时候需要去掉)**:
//binding.gyp
{
"targets": [{
"target_name": "helloworld", //模块名称
"sources": [
"helloworld.cc" //源代码列表
]
}]
}
好了,我们所需要的就是这些了。接下来就是编译了:
node-gyp configure #生成Makefile等依赖文件
node-gyp build #编译模块
如果你够幸运的话,应该能在当前目录下的 build/Release/helloworld.node 找到生成的模块。 写个简单的脚本测试一下:
var helloworld = require("./build/Release/helloworld.node");
helloworld.say();
如果没有错的话,应该能看到控制台输出“Hello World!
”
OK,下面是正式的文件:
// wiringpi.cc
#define BUILDING_NODE_EXTENSION
#include <v8.h>
#include <node.h>
#include <node_buffer.h>
#include <wiringPi.h>
#include <wiringShift.h>
using namespace v8;
using namespace node;
#define WIRING_FUNC(NAME) _wiring_##NAME
#define WIRING_DEFINE_CONSTANT(NAME, VALUE) (target)->Set( \
v8::String::NewSymbol(NAME), \
v8::Integer::New(VALUE), \
static_cast<v8::PropertyAttribute>(v8::ReadOnly|v8::DontDelete) \
);
#define WIRING_BIND_METHOD(NAME) NODE_SET_METHOD(target, #NAME, WIRING_FUNC(NAME));
#define WIRING_WRAP_FUNC(NAME) static Handle<Value> WIRING_FUNC(NAME) (const Arguments& args)
#define WIRING_RETURN(RET) return scope.Close(RET);
#define WIRING_RETURN_UNDEFINED WIRING_RETURN(Undefined())
#define WIRING_ARGCHK_LEN(N) if (args.Length() < N) { \
ThrowException(Exception::TypeError(String::New("Need "#N" arguments."))); \
WIRING_RETURN_UNDEFINED \
}
#define WIRING_ARGCHK_INT(N,BIT) if (!args[N]->IsNumber() || !args[N]->IsInt##BIT()) { \
ThrowException(Exception::TypeError(String::Concat(String::New("Argument "#N" type error. need int"#BIT", get "), args[N]->ToString()))); \
WIRING_RETURN_UNDEFINED \
}
#define WIRING_ARGCHK_UINT(N,BIT) if (!args[N]->IsNumber() || !args[N]->IsUint##BIT()) { \
ThrowException(Exception::TypeError(String::Concat(String::New("Argument "#N" type error. need uint"#BIT", get "), args[N]->ToString()))); \
WIRING_RETURN_UNDEFINED \
}
#define WIRING_NUMBER_FUNC_VOID(NAME) WIRING_WRAP_FUNC(NAME) { \
HandleScope scope; \
WIRING_RETURN(Number::New(NAME())) \
}
#define WIRING_NUMBER_FUNC_INT(NAME) WIRING_WRAP_FUNC(NAME) { \
HandleScope scope; \
WIRING_ARGCHK_LEN(1) \
WIRING_ARGCHK_INT(0, 32) \
WIRING_RETURN(Number::New(NAME(args[0]->Int32Value()))) \
}
#define WIRING_UNDEFINED_FUNC_INT(NAME) WIRING_WRAP_FUNC(NAME) { \
HandleScope scope; \
WIRING_ARGCHK_LEN(1) \
WIRING_ARGCHK_INT(0, 32) \
NAME(args[0]->Int32Value()); \
WIRING_RETURN_UNDEFINED \
}
#define WIRING_UNDEFINED_FUNC_UINT(NAME) WIRING_WRAP_FUNC(NAME) { \
HandleScope scope; \
WIRING_ARGCHK_LEN(1) \
WIRING_ARGCHK_UINT(0, 32) \
NAME(args[0]->Uint32Value()); \
WIRING_RETURN_UNDEFINED \
}
#define WIRING_UNDEFINED_FUNC_INT_INT(NAME) WIRING_WRAP_FUNC(NAME) { \
HandleScope scope; \
WIRING_ARGCHK_LEN(2) \
WIRING_ARGCHK_INT(0, 32) \
WIRING_ARGCHK_INT(1, 32) \
NAME(args[0]->Int32Value(), args[1]->Int32Value()); \
WIRING_RETURN_UNDEFINED \
}
#define WIRING_NUMBER_FUNC_INT_INT(NAME) WIRING_WRAP_FUNC(NAME) { \
HandleScope scope; \
WIRING_ARGCHK_LEN(2) \
WIRING_ARGCHK_INT(0, 32) \
WIRING_ARGCHK_INT(1, 32) \
WIRING_RETURN(Number::New(NAME(args[0]->Int32Value(), args[1]->Int32Value()))) \
}
// wiringPi.h:
// Basic wiringPi functions
//extern int wiringPiSetup (void) ;
WIRING_NUMBER_FUNC_VOID(wiringPiSetup)
//extern int wiringPiSetupSys (void) ;
WIRING_NUMBER_FUNC_VOID(wiringPiSetupSys)
//extern int wiringPiSetupGpio (void) ;
WIRING_NUMBER_FUNC_VOID(wiringPiSetupGpio)
//extern int wiringPiSetupPiFace (void) ;
WIRING_NUMBER_FUNC_VOID(wiringPiSetupPiFace)
//extern int wiringPiSetupPiFaceForGpioProg (void) ; // Don't use this - for gpio program only
//extern void (*pinMode) (int pin, int mode) ;
WIRING_UNDEFINED_FUNC_INT_INT(pinMode)
//extern void (*pullUpDnControl) (int pin, int pud) ;
WIRING_UNDEFINED_FUNC_INT_INT(pullUpDnControl)
//extern void (*digitalWrite) (int pin, int value) ;
WIRING_UNDEFINED_FUNC_INT_INT(digitalWrite)
//extern void (*pwmWrite) (int pin, int value) ;
WIRING_UNDEFINED_FUNC_INT_INT(pwmWrite)
//extern void (*setPadDrive) (int group, int value) ;
WIRING_UNDEFINED_FUNC_INT_INT(setPadDrive)
//extern int (*digitalRead) (int pin) ;
WIRING_NUMBER_FUNC_INT(digitalRead)
//extern void (*delayMicroseconds) (unsigned int howLong) ;
WIRING_UNDEFINED_FUNC_UINT(delayMicroseconds)
//extern void (*pwmSetMode) (int mode) ;
WIRING_UNDEFINED_FUNC_INT(pwmSetMode)
//extern void (*pwmSetRange) (unsigned int range) ;
WIRING_UNDEFINED_FUNC_UINT(pwmSetRange)
//
//// Interrupts
//
//extern int (*waitForInterrupt) (int pin, int mS) ;
WIRING_UNDEFINED_FUNC_INT_INT(waitForInterrupt)
//
//// Threads
//
//#define PI_THREAD(X) void *X (void *dummy)
//
//extern int piThreadCreate (void *(*fn)(void *)) ;
//extern void piLock (int key) ;
WIRING_UNDEFINED_FUNC_INT(piLock)
//extern void piUnlock (int key) ;
WIRING_UNDEFINED_FUNC_INT(piUnlock)
//
//// Schedulling priority
//
//extern int piHiPri (int pri) ;
WIRING_NUMBER_FUNC_INT(piHiPri)
//
//
//// Extras from arduino land
//
//extern void delay (unsigned int howLong) ;
WIRING_UNDEFINED_FUNC_UINT(delay)
//extern unsigned int millis (void) ;
WIRING_NUMBER_FUNC_VOID(millis)
// wiringShift.h:
//extern uint8_t shiftIn (uint8_t dPin, uint8_t cPin, uint8_t order) ;
WIRING_WRAP_FUNC(shiftIn) {
HandleScope scope;
WIRING_ARGCHK_LEN(3)
WIRING_ARGCHK_UINT(0, 32)
WIRING_ARGCHK_UINT(1, 32)
WIRING_ARGCHK_UINT(2, 32)
WIRING_RETURN(Number::New(shiftIn(args[0]->Uint32Value(), args[1]->Uint32Value(), args[2]->Uint32Value())))
}
//extern void shiftOut (uint8_t dPin, uint8_t cPin, uint8_t order, uint8_t val) ;
WIRING_WRAP_FUNC(shiftOut) {
HandleScope scope;
int dPin, cPin, order;
Local<Object> bufferObj;
char *buffer = NULL;
size_t length = 0, index = 0;
WIRING_ARGCHK_LEN(4)
WIRING_ARGCHK_UINT(0, 32)
WIRING_ARGCHK_UINT(1, 32)
WIRING_ARGCHK_UINT(2, 32)
dPin = args[0]->Uint32Value();
cPin = args[1]->Uint32Value();
order = args[2]->Uint32Value();
if (Buffer::HasInstance(args[3])) {
bufferObj = args[3]->ToObject();
buffer = Buffer::Data(bufferObj);
length = Buffer::Length(bufferObj);
for(index = 0; index < length; index++){
shiftOut(dPin, cPin, order, buffer[index]);
}
}else{
WIRING_ARGCHK_UINT(3, 32)
shiftOut(dPin, cPin, order, args[3]->Uint32Value());
}
WIRING_RETURN_UNDEFINED
}
extern "C" {
void init(Handle<Object> target) {
WIRING_BIND_METHOD(wiringPiSetup)
WIRING_BIND_METHOD(wiringPiSetupSys)
WIRING_BIND_METHOD(wiringPiSetupGpio)
WIRING_BIND_METHOD(wiringPiSetupPiFace)
WIRING_BIND_METHOD(pinMode)
WIRING_BIND_METHOD(pullUpDnControl)
WIRING_BIND_METHOD(digitalWrite)
WIRING_BIND_METHOD(pwmWrite)
WIRING_BIND_METHOD(setPadDrive)
WIRING_BIND_METHOD(digitalRead)
WIRING_BIND_METHOD(delayMicroseconds)
WIRING_BIND_METHOD(pwmSetMode)
WIRING_BIND_METHOD(pwmSetRange)
WIRING_BIND_METHOD(waitForInterrupt)
WIRING_BIND_METHOD(piLock)
WIRING_BIND_METHOD(piUnlock)
WIRING_BIND_METHOD(piHiPri)
WIRING_BIND_METHOD(delay)
WIRING_BIND_METHOD(millis)
WIRING_BIND_METHOD(shiftIn)
WIRING_BIND_METHOD(shiftOut)
WIRING_DEFINE_CONSTANT("INPUT", INPUT)
WIRING_DEFINE_CONSTANT("OUTPUT", OUTPUT)
WIRING_DEFINE_CONSTANT("PWM_OUTPUT", PWM_OUTPUT)
WIRING_DEFINE_CONSTANT("LOW", LOW)
WIRING_DEFINE_CONSTANT("HIGH", HIGH)
WIRING_DEFINE_CONSTANT("PUD_OFF", PUD_OFF)
WIRING_DEFINE_CONSTANT("PUD_DOWN", PUD_DOWN)
WIRING_DEFINE_CONSTANT("PUD_UP", PUD_UP)
WIRING_DEFINE_CONSTANT("LSBFIRST", LSBFIRST)
WIRING_DEFINE_CONSTANT("MSBFIRST", MSBFIRST)
}
NODE_MODULE(wiringpi, init);
}
以及 binding.gyp:
{
"targets": [{
"target_name": "_wiringpi",
"include_dirs": ["src"],
"sources": [
"wiringpi.cc",
"src/piThread.c",
"src/wiringPiSPI.h",
"src/lcd.h",
"src/wiringSerial.c",
"src/wiringPiSPI.c",
"src/wiringShift.h",
"src/gertboard.h",
"src/wiringPi.c",
"src/softPwm.h",
"src/piHiPri.c",
"src/wiringSerial.h",
"src/wiringPi.h",
"src/lcd.c",
"src/piNes.c",
"src/gertboard.c",
"src/softPwm.c",
"src/wiringPiFace.c",
"src/wiringShift.c",
"src/piNes.h"
]
}]
}
这样,就能在nodejs中调用wiringPi的函数了,接下来,我们就可以专注于nodejs的开发了~(待续)
牛x
怎么一个牛逼了得, Raspberry Pi 小白路过
学过C没用过C,看着这样的代码好恐怖啊。有啥C的好书推荐不?
C Primer Plus http://book.douban.com/subject/1240002/
C Primer Plus http://book.douban.com/subject/1240002/
大神,第一个helloworld的例子有错误 我从昨天下午开始做这个例子,开始是我的mac的node配置有问题,把和配置相关的错误都改了,还不行,但是悲剧的是我还的node配置有问题,就到windows系统里又配置了一次,结果,还是不行,噼里啪啦一直报错~~~ 最后,一直到今天下午,我看了nodejs.org的例子,结果很容易就成功了,再回头看,您代码里有错误: NODE_MODULE(hello_world, init); 这里的“hello_world”应该是“helloworld”
我吐个槽行么, 要不用 nodejs 开发个操作系统得了.
很酷,学习。
我认为绝对是可以的
大神,v8.h, 跟 node.h 你们是从哪里下的呢?
大神,v8.h, 跟 node.h 你们是从哪里下的呢?
你看的是哪个例子呀~
@joeylin 本页最上面那块代码的最后一行: NODE_MODULE(hello_world, init); hello_world多了一个下划线
@jklnode 好的,谢谢你呀,我也测试成功了。我C++不太懂,现在正在学习~可以加下你QQ吗? 希望可以多多交流哈~
哥哥 wiringpi.cc文件的顶部 这句话不能加啊 搞了我好长时间 重复定义了
#define BUILDING_NODE_EXTENSION