材料

  • 树莓派2代B型
  • 步进电机28BYJ-48-5V
  • ULN2003步进电机驱动板

安装node&npm

$ wget http://nodejs.org/dist/v7.1.0/node-v7.1.0-linux-armv7l.tar.xz
$ tar xvfJ node-v7.1.0-linux-armv7l.tar.xz
$ sudo mv node-v7.1.0-linux-armv7l /opt
$ sudo update-alternatives --install "/usr/bin/node" "node" "/opt/node-v7.1.0-linux-armv7l/bin/node" 1
$ sudo update-alternatives --set node /opt/node-v7.1.0-linux-armv7l/bin/node
$ sudo update-alternatives --install "/usr/bin/npm" "npm" "/opt/node-v7.1.0-linux-armv7l/bin/npm" 1

安装rpio

$ npm install rpio
$ cat >/etc/udev/rules.d/20-gpiomem.rules <<EOF
SUBSYSTEM=="bcm2835-gpiomem", KERNEL=="gpiomem", GROUP="gpio", MODE="0660"
EOF

最终代码

var rpio = require('rpio');
var motorObj = null;
var STATUS = {
    NOT_READY: -1,
    STARTED: 1,
    STOPED: 0
}

/**
 * [Motor 步进电机构造函数]
 * @param {[type]} pins [指定使用的针脚]
 */
function Motor(pins) {
    this.pins = pins || [11, 12, 15, 16];
    this.status = STATUS.NOT_READY;
    this.high = 0;

    this._initGpio();
    return this;
}
Motor.STATUS = STATUS;

Motor.prototype = {
    /**
     * [_initGpio 初始化步进电机]
     */
    _initGpio: function() {
        var self = this,
            flag = 0;
        this.pins.forEach(function(pin) {
            //rpio.close(pin);
            rpio.open(pin, rpio.OUTPUT);
            flag++;
        });
        motorObj = this;

        var t = setInterval(function() {
            if (flag == 4) {
                rpio.write(self.pins[0], rpio.HIGH);
                self.status = Motor.STATUS.STOPED;
                clearInterval(t);
            }
        }, 10);
    },
    /**
     * [_run 电机运转逻辑]
     */
    _run: function() {
        var self = this,
            pins = this.pins;

        var _f = function() {
            var interval = 4 * self.speed,
                pin2High = (self.high + ((1 - self.direction) * 2 + 1)) % 4;

            rpio.write(pins[self.high], rpio.LOW);
            rpio.write(pins[pin2High], rpio.HIGH);
            self.high = pin2High;
            self._t = setTimeout(_f, interval);
        }
        _f();
    },
    _unexportPins: function() {
        var pins = this.pins;
        rpio.close(pins[0]);
        rpio.close(pins[1]);
        rpio.close(pins[2]);
        rpio.close(pins[3]);
    },
    /**
     * [start 启动电机]
     * @param  {[int]} speed     [速度]
     * @param  {[mixed]} direction [转向]
     */
    start: function(speed, direction) {
        if (this.status == Motor.STATUS.STARTED) {
            return this;
        }

        var self = this;
        this.loop = 20;
        if (this.status == Motor.STATUS.NOT_READY && this.loop) {
            this._st = setTimeout(function() {
                self.loop--;
                self.start(speed, direction);
            }, 20);
        } else if (this.status == Motor.STATUS.STOPED) {
            clearTimeout(this._st);

            this.setSpeed(speed);
            this.setDirection(direction);

            this._run();
            this.status = Motor.STATUS.STARTED;
        }

        return this;
    },

    /**
     * [stop 电机停转]
     */
    stop: function() {
        if (this.status != Motor.STATUS.STARTED) {
            return this;
        }

        clearTimeout(this._t);
        this.status = Motor.STATUS.STOPED;
        return this;
    },

    /**
     * [off 停止电机]
     */
    off: function() {
        this.stop();
        this._unexportPins();
    },

    /**
     * [setSpeed description]
     * @param {[int]} speed [速度档, 1~10, 1 最快]
     */
    setSpeed: function(speed) {
        this.speed = Math.round(Math.min(10, Math.max(1, speed || 5)));
        return this;
    },

    /**
     * [setDirection 设置运转方向]
     * @param {[mixed]} d [方向,0||'0'||'reverse'反转(逆时钟),其他值正转]
     */
    setDirection: function(d) {
        this.direction = (d === 0 || d === '0' || d == 'reverse') ? 0 : 1;
        return this;
    },

    /**
     * [turn 反转电机运转方向]
     */
    turn: function() {
        this.direction = 1 - this.direction;
        return this;
    },

    /**
     * [faster 加快速度]
     * @param  {[type]} v [增加的档位差,默认增加一档]
     */
    faster: function(v) {
        this.setSpeed(this.speed - (v || 1));
        return this;
    },

    /**
     * [faster 减慢速度]
     * @param  {[type]} v [减慢的档位差,默认减慢一档]
     */
    slower: function(v) {
        this.setSpeed(this.speed + (v || 1));
        return this;
    }
}

process.on('exit', function() {
    motorObj && motorObj.status == Motor.STATUS.STARTED && motorObj.off();
});



var app = new Motor();
app.start(10, 0)

以上代码参考pi-motor,原项目使用pi-gpio

附录

  • 树莓派针脚
    • 树莓派针脚分为GPIO针脚(GPIO PIN)和物理针脚(physical PIN)两种表示方式,参考
    • 物理针脚
    • GPIO针脚
  • 为什么选rpio
    • 性能更高,[参考](pi-gpio很久没有维护,而且需要安装gpio-admin, gpio-admin也很久没人维护,安装比较麻烦,且有兼容问题)
    • 有在维护
    • 安装方便