用NodeJs来控制硬件(基于Raspberry Pi)(三)
发布于 2年前 作者 yourke 7765 次浏览

上次说到为什么选择通过扩展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的开发了~(待续)

15 回复

怎么一个牛逼了得, Raspberry Pi 小白路过

学过C没用过C,看着这样的代码好恐怖啊。有啥C的好书推荐不?

大神,第一个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
回到顶部