jperkin / node-rpio

Raspberry Pi GPIO library for node.js
857 stars 123 forks source link

Trying to communicate to Blinkt on GPIO 23/24 #117

Closed chriskinsman closed 4 years ago

chriskinsman commented 4 years ago

I have found all of the existing blinkt libraries rely upon wiring-pi which appears broken in Node >=12.

Trying to fork and get one of the existing libraries blinkt-kit working using node-rpio instead.

Looks like a straightforward changeover however I can't seem to get any response from the blinkt LEDs.

The protocol appears to be that they put a bit on GPIO 23 and then move GPIO 24 high and then low to trigger a clock cycle.

I am wondering if with the increase in RPIO speed if my issue is perhaps that I need to leave the clock high longer?

In any case here is the short modified source:

"use strict";

const rpio = require("rpio");

const DAT = 23;
const CLK = 24;

const DEFAULT_RED = 0;
const DEFAULT_GREEN = 0;
const DEFAULT_BLUE = 0;
const DEFAULT_BRIGHTNESS = 0.2;
const DEFAULT_PIXELS = 8;

class Blinkt {
    constructor({ dat = DAT, clk = CLK, clearOnExit = false } = {}) {
        this.dat = dat;
        this.clk = clk;

        rpio.open(this.dat, rpio.OUTPUT, rpio.LOW);
        rpio.open(this.clk, rpio.OUTPUT, rpio.LOW);

        this.blinktPixels = Array.from(new Array(DEFAULT_PIXELS), () => [DEFAULT_RED, DEFAULT_GREEN, DEFAULT_BLUE, 0]);

        if (clearOnExit) {
            this.setClearOnExit();
        }
    }

    getBrightness(brightness = DEFAULT_BRIGHTNESS) {
        return parseInt(31.0 * brightness, 10) & 0b11111;
    }

    getPixel({ r = DEFAULT_RED, g = DEFAULT_GREEN, b = DEFAULT_BLUE, brightness = DEFAULT_BRIGHTNESS } = {}) {
        return [parseInt(r, 10) & 255, parseInt(g, 10) & 255, parseInt(b, 10) & 255, this.getBrightness(brightness)];
    }

    setPixel({ pixel = 0, r, g, b, brightness = DEFAULT_BRIGHTNESS } = {}) {
        this.blinktPixels[pixel] = this.getPixel({ r, g, b, brightness });
    }

    getAll() {
        return this.blinktPixels;
    }

    setAll({ r, g, b, brightness } = {}) {
        this.blinktPixels.forEach((_, pixel) => this.setPixel({ pixel, r, g, b, brightness }));
    }

    setBrightness({ pixel, brightness = DEFAULT_BRIGHTNESS }) {
        if (typeof pixel !== "undefined") {
            this.blinktPixels[pixel][3] = this.getBrightness(brightness);
        } else {
            this.blinktPixels.forEach(pixel => (pixel[3] = this.getBrightness(brightness)));
        }
    }

    clear() {
        this.setAll({ r: 0, g: 0, b: 0, brightness: 0 });
        this.show();
    }

    cleanup() {
        this.clear();
        process.exit();
    }

    setClearOnExit(value = true) {
        if (this.clearOnExit) return;
        this.clearOnExit = true;
        process.on("exit", () => this.cleanup());
        process.on("SIGINT", () => this.cleanup());
    }

    writeData(bit) {
        rpio.write(this.dat, bit);
        rpio.write(this.clk, rpio.HIGH);
        rpio.sleep(0);
        rpio.write(this.clk, rpio.LOW);
        rpio.sleep(0);
    }

    writeByte(byte) {
        for (let i = 0; i < DEFAULT_PIXELS; i++) {
            const bit = (byte & (1 << (7 - i))) > 0 === true ? rpio.HIGH : rpio.LOW;
            this.writeData(bit);
        }
    }

    writeDataNTimes(bit, cycles) {
        for (let i = 0; i < cycles; i++) this.writeData(bit);
    }

    // Emit exactly enough clock pulses to latch the small dark die APA102s which are weird
    // for some reason it takes 36 clocks, the other IC takes just 4 (number of pixels/2)

    eof() {
        this.writeDataNTimes(0, 36);
    }

    sof() {
        this.writeDataNTimes(0, 32);
    }

    show() {
        this.sof();
        this.blinktPixels.forEach(pixel => {
            const [red, green, blue, brightness] = pixel;
            this.writeByte(0b11100000 | brightness);
            this.writeByte(blue);
            this.writeByte(green);
            this.writeByte(red);
        });
        this.eof();
    }
}

module.exports = {
    Blinkt
};

Any ideas on why rpio may not be working in this case?

BTW have checked and the user is a member of the gpio group and I have made the recommended change to /boot/config.txt

Node version 12.18.0 Pi 3

node-gyp rebuild during npm i does show a number of warnings:

make: Entering directory '/home/pi/projects/blinkt-kit/node_modules/rpio/build'
  CXX(target) Release/obj.target/rpio/src/rpio.o
In file included from ../src/rpio.cc:17:
../../nan/nan.h: In function ‘void Nan::AsyncQueueWorker(Nan::AsyncWorker*)’:
../../nan/nan.h:2294:62: warning: cast between incompatible function types from ‘void (*)(uv_work_t*)’ {aka ‘void (*)(uv_work_s*)’} to ‘uv_after_work_cb’ {aka ‘void (*)(uv_work_s*, int)’} [-Wcast-function-type]
     , reinterpret_cast<uv_after_work_cb>(AsyncExecuteComplete)
                                                              ^
In file included from ../../nan/nan.h:56,
                 from ../src/rpio.cc:17:
../src/rpio.cc: At global scope:
/home/pi/.cache/node-gyp/12.18.0/include/node/node.h:608:43: warning: cast between incompatible function types from ‘void (*)(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE)’ {aka ‘void (*)(v8::Local<v8::Object>)’} to ‘node::addon_register_func’ {aka ‘void (*)(v8::Local<v8::Object>, v8::Local<v8::Value>, void*)’} [-Wcast-function-type]
       (node::addon_register_func) (regfunc),                          \
                                           ^
/home/pi/.cache/node-gyp/12.18.0/include/node/node.h:642:3: note: in expansion of macro ‘NODE_MODULE_X’
   NODE_MODULE_X(modname, regfunc, NULL, 0)  // NOLINT (readability/null_usage)
   ^~~~~~~~~~~~~
../src/rpio.cc:495:1: note: in expansion of macro ‘NODE_MODULE’
 NODE_MODULE(rpio, setup)
 ^~~~~~~~~~~
  CC(target) Release/obj.target/rpio/src/bcm2835.o
../src/bcm2835.c: In function ‘bcm2835_gpio_pad’:
../src/bcm2835.c:487:3: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
   if (bcm2835_pads == MAP_FAILED)
   ^~
../src/bcm2835.c:490:5: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
     volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group;
     ^~~~~~~~
../src/bcm2835.c: In function ‘bcm2835_gpio_set_pad’:
../src/bcm2835.c:500:3: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
   if (bcm2835_pads == MAP_FAILED)
   ^~
../src/bcm2835.c:503:5: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
     volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group;
     ^~~~~~~~
  SOLINK_MODULE(target) Release/obj.target/rpio.node
  COPY Release/rpio.node
make: Leaving directory '/home/pi/projects/blinkt-kit/node_modules/rpio/build'

A simple program like this:

const { Blinkt } = require('../src/blinkt');

const blinkt = new Blinkt({ clearOnExit: true });

console.log('starting');
blinkt.setAll(128,128,128, 0.8);
blinkt.show();

setTimeout(() => {
    console.log('exiting');
    process.exit(0);
}, 5000);

Completes but with a Segmentation Fault

chriskinsman commented 4 years ago

My issue with respect to physical vs logical GPIO pins. Closing.