jperkin/node-rpio

Trying to communicate to Blinkt on GPIO 23/24

Closed this issue · 1 comments

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

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