Coder Social home page Coder Social logo

pigpio-client's Introduction

pigpio-client

Pigpio-client exposes the socket interface APIs of the pigpio library using nodejs. This allows you to connect to a Raspberry Pi, running remotely or local, and manipulate its GPIO pins with javascript. The pigpio socket interface is described more fully here:

New in v1.5.2

  • Add Trigger API

Installing and Running pigpio daemon

A guide for installing and running pigpiod along with other useful information can be found in the wiki

pigpio-client usage example

const pigpio = require('pigpio-client').pigpio({host: 'raspberryHostIP'});  

const ready = new Promise((resolve, reject) => {
  pigpio.once('connected', resolve);
  pigpio.once('error', reject);
});

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

ready.then(async (info) => {
  // display information on pigpio and connection status
  console.log(JSON.stringify(info,null,2));

  // get events from a button on GPIO 17
  const button = pigpio.gpio(17);
  await button.modeSet('input');
  button.notify((level, tick)=> {
    console.log(`Button changed to ${level} at ${tick} usec`)
  });

  // control an LED on GPIO 4
  const led = pigpio.gpio(4);
  await led.modeSet('output');
  await led.write(1);  // turn on LED
  await wait(500);
  await led.write(0);  // turn off
  await wait(500);

  // use waves to blink the LED rapidly (toggle every 100ms)
  await led.waveClear();
  await led.waveAddPulse([[1, 0, 100000], [0, 1, 100000]]);
  const blinkWave = await led.waveCreate();
  led.waveChainTx([{loop: true}, {waves: [blinkWave]}, {repeat: true}]);

  // wait for 10 sec, stop the waves
  await wait(10000);
  await led.waveTxStop();
}).catch(console.error);

All APIs accept error-first callback as an optional last argument, and also return a promise (and thus can be safely used with async/await). Depending on the presence of a callback argument, errors returned by pigpio are delivered in two ways: Methods called without a callback emit 'error' events. Methods called with a callback are supplied an Error object as the first argument returned.
Arguments to callback are: (error, response) unless otherwise noted.

Constructors

PigpioClient.pigpio(options): Construct a pigpio object and connect it to options.host.

Options have the following properties:

  • host: The remote IP address to host running pigpio daemon. Defaults to 'localhost'.
  • port: The port used to configure pigpiod. Default is 8888.
  • pipelining: DEPRECATED. Configures internal socket communications.
  • timeout: The network socket timeout in minutes. Default is 0. Timeout enables automatic retries to connect to the server after any socket error is encountered. During the timeout period, connection will be retried every 5 seconds. An 'error' event will be emitted after the timeout period expires. After connection is established, if keep-alive packets are not received from the server within timeout minutes, the network sockets will be closed and a 'disconnected' event emitted. If timeout is used, it is recommended to set its value to > 1 minute. Also recommended is to use V68 of pigpio library.

pigpio.gpio(gpio_pin) Return a gpio object set to the Broadcom GPIO number specified by gpio_pin. An error will be thrown if gpio_pin is not a valid user GPIO.

pigpio.serialport(rx,tx,dtr) Return a serialport object constructed from GPIO pins for rx, tx and dtr. Rx and Tx may use the same pin for a loop-back. DTR pin is optional. An error will be thrown if the pins are not valid user GPIO. Constructing a serialport object will clear all waveforms.

Events

'connected' Emitted after both command and notification sockets are connected to pigpiod. An 'info' object is passed to the handler. You should wait for this event before attempting to construct other objects - gpio, serialport.

'disconnected' Emitted when the socket connections are closed, for any reason, or when no pigpio keep-alive packet is received within timeout>0 minutes.

'error' Error objects are passed to the 'error' event handler unless a callback was provided when invoking an API. Pigpio errors have the name property set to PigpioError and a code and message property set corresponding the numeric value returned by the socket interface. For pigpio-client specific APIs, the error name is set to PigpioClientError.

Methods

Note: Unless otherwise stated, all references to gpio pin numbers use Broadcom numbering.

pigpio methods

pigpio.getInfo(cb) Returns useful information about rpi hardware and pigpiod.

pigpio.getCurrentTick(cb) Return current timer tick in microseconds. gpioTick

pigpio.readBank1(cb) Returns levels of GPIO bits 31-0. gpioRead_Bits_0_31

pigpio.end(cb) Ends socket communications. Callback is invoked on 'disconnected' event.

pigpio.destroy() DEPRECATED. Invokes socket.destroy() on both network sockets.

pigpio.connect() Re-establishes communication with server after being disconnected.

pigpio.i2cOpen(bus, address, callback) Return a handle for communication with a device attached to a bus on the I2C peripheral. bus==0 uses (Broadcom) gpio 0 and 1, while bus==1 use gpio 2 and 3 for SDA and SCL respectively. address takes values 1-127.

pigpio.i2cClose(handle, callback) Close the I2C device associated with handle.

pigpio.i2cReadDevice(handle, count, callback) Read count bytes from the i2c device referred by handle. Returns tuple (error, count, data). error is 0 on success or an error code. count is the number of bytes read. data is a buffer.

pigpio.i2cWriteDevice(handle, data, callback) Write data to the i2c device referred by handle. data is a byte array or utf8 string. Returns 0 if OK, otherwise an error code.

pigpio.bscI2C(address, data, callback) Transfer data to/from the BSC I2C slave peripheral using gpio 18 (SDA) and 19 (SCL). The data bytes (if any) are written to the BSC transmit FIFO and the bytes in the BSC receive FIFO are returned. The depth of the FIFO is 32 bytes.

This function is not available on the BCM2711 (e.g. as used in the Pi4B).

address is a non-zero 7-bit i2c address the slave device will respond to.
If address is 0, the BSC peripheral is turned off and gpio 18/19 are reset to input mode.

data, optional, is a byte array or utf8 string up to 512 bytes.

The return value is a byte array containing: A byte-count byte followed by 4 status bytes followed by the data bytes read from the RX FIFO.

The EVENT_BSC event is emitted when data is available to read from the BSC I2C device.

gpio basic methods

gpio.modeSet(mode, cb) Sets the gpio mode to be input or output. The mode argument must be string with a value of 'input', 'in', 'output' or 'out'. The optional callback is invoked with either null argument on success or error on failure.

gpio.trigger(len, level, cb) Send a trigger pulse to a GPIO. The GPIO is set to level for len microseconds. The optional callback is invoked with either null argument on success or error on failure.

gpio.modeGet(cb) Returns the mode of gpio as argument to callback. gpioGetMode

gpio.pullUpDown(pud, cb) pud=2: set pullup resistor, pud=1: set pulldown resistor, pud=0: clear resistor setting.gpioSetPullUpDown

gpio.read(cb) Returns the gpio pin level as argument to callback.

gpio.write(level, cb) Sets the gpio output level to on (1) or off (0). If PWM or servo pulses are active on the gpio they are switched off. gpioWrite

gpio.analogWrite(dutyCycle, cb) Set the PWM dutycycle (0-255) on the gpio. Caution: This will stop all waveform generation. gpioPWM.

gpio.hardwarePWM(frequency, dutyCycle, callback) Set the hardware PWM dutycycle (0-1000000) on the gpio. Only works with pins that support hardware PWM (12,13,18,19); max two channels available. PWM frequency is also specified - not likely to work above 30MHz though. Caution: This will stop all waveform generation. gpioHardwarePWM.

gpio.setServoPulsewidth(pulseWidth, cb) Starts servo pulses on gpio. Pulsewidth can be set to 0 (off) and from 500 (most anti-clockwise) to 2500 (most clockwise). Be aware that you can damage your servo when setting a too high pulseWidth. A value of 1500 (mid-point) is a good starting point to check range of your servo. gpioServo

gpio.getServoPulsewidth(cb) Returns the pulsewidth of gpio as argument to callback. gpioGetServoPulsewidth

gpio waveform methods

gpio.waveClear(cb) Clear all waveforms (release DMA control blocks, reset wave IDs). gpioWaveClear

gpio.waveCreate(cb) Returns the wave id of waveform created from previous calls to waveAddPulse or waveAddSerial. gpioWaveCreate

gpio.waveBusy(cb) Returns 1 if wave is still transmitting, otherwise 0. gpioWaveTxBusy

gpio.waveNotBusy(interval, callback) Invokes callback when waveform ends. Polls waveform status at interval msec. Defaults to 25msec.

gpio.waveAddPulse([Pulse_t], cb) Add array of Pulse_t to gpio of current waveform. Pulse_t is a tuple [1, 0, delay] for positive pulse, [0, 1, delay] for negative pulse width = delay. gpioWaveAddGeneric.

gpio.waveChainTx([{loop:x}, {waves: [wids]}, {delay:y}, {repeat:z}], cb) gpioWaveChain
Transmit a chain of waves represented by array of wave IDs [wids]. The chain can have loops, be separated by delays and repeated by inclusion of option objects.

  • loop : x, begins a loop. x can be anything
  • delay: y, insert of delay of y=0 to 65535 microseconds
  • repeat: z, repeat loop z=0 to 65535 times or forever if z=true

gpio.waveSendSync(wid, cb) Synchronizes wid to the currently active waveform. gpioWaveTxSend with mode set to PI_WAVE_MODE_ONE_SHOT_SYNC.

gpio.waveSendOnce(wid, cb) Send the wave id, wid, one time. gpioWaveTxSend with mode set to PI_WAVE_MODE_ONE_SHOT.

gpio.waveTxAt(cb) Return currently active wave id, no wave being transmitted (9999) or wave not found (9998). gpioWaveTxAt

gpio.waveTxStop(cb) Aborts the transmission of the current waveform. This function is intended to stop a waveform started in repeat mode. waveTxStop

gpio.waveDelete(wid, cb) Delete the wave id wid. gpioWaveDelete

Note: waveClear, waveCreate and waveBusy are not gpio specific. These methods are made available to the gpio object for convenience and as a reminder that only a single waveform can be active.
Note: waveBusy and waveNotBusy return status are global indication of waveform state - not specific to gpio!

gpio notification methods

gpio.notify(callback) Registers the notification function callback. callback is invoked whenever the gpio state changes. Arguments to callback are level and tick where tick represents the system's time since boot.

gpio.endNotify(cb) Unregisters the notification on gpio. For convenience, a null tick value is sent - useful for stream objects that wrap the notifier callback.

gpio.glitchSet(steady, cb) Sets a glitch filter (0-300000) on gpio in microseconds. Only effects notifications. gpioGlitchFilter

gpio bit_bang_serial methods

gpio.serialReadOpen(baudRate, dataBits, cb) - gpioSerialReadOpen

gpio.serialRead(count, cb) Returns cb(null, length, buf). buf is Uint8Array of length length. gpioSerialRead

gpio.serialReadClose(cb) gpioSerialReadClose

gpio.serialReadInvert(mode, cb) Mode is 'invert' || 'normal'. gpioSerialReadInvert

waveAddSerial(baud,bits,delay,data,cb) - gpioWaveAddSerial

serialport methods

Experimental, these APIs may change in the future.

serialport.open(baudrate,databits,cb) Argument baudRate must be a number from 50 to 250000. Argument dataBits must be a number from 1 to 32. If the rx gpio is already open for bit-bang serial read the method will close the gpio and then re-open it.

serialport.read(size, cb) size is an optional argument representing the number of bytes to read. If not specified, all the data in pigpio's cyclic buffer is returned (up to 8192 bytes). Returns cb(null, data) where data is a utf8 string. If the serialport is not open, returns cb(null).

serialport.write(data) data is utf8 string or Uint8Buffer. The data is buffered then sent out in chunk sizes that fit the available waveform resources. Returns the number of bytes remaining in the buffer. If the serialport is not open, returns -1. Any pigpio errors occurring during write will be thrown to limit the possibility of data corruption.

serialport.close(cb) Close serialport.

serialport.end(cb) Closes rx gpio for bb_serial_read and changes gpios tx and dtr mode to input.

Environment Variables for Debugging

Environment variables can be set to display messages from pigpio-client to assist in troubleshooting your application. DEBUG='pigpio' will enable status messages. For tracing the all socket communication use: PIGPIO=1.

DEBUG='pigpio' node myApp.js

Issues/Requests/Questions

https://github.com/guymcswain/pigpio-client/issues

Limitations

Only a single instance of serialport is supported. <=1.1.x

pigpio-client's People

Contributors

fliegenklatsch avatar guymcswain avatar philipphaefele avatar stevenaeola avatar urish avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

pigpio-client's Issues

Timing issues with pigpio-client

I've written a timing sensitive application (infrared remote emitter); it sends data on a 37KHz (~37 microsecond) carrier wave. My first go at it, I used https://github.com/fivdi/pigpio (hereafter pigpio.js), which wraps the C bindings to pigpio (hereafter pigpio.c). It works very reliably, but has lots of other unpleasant properties (can only be run as root, and doesn't support concurrent runs).

I moved the application over to pigpio-client, and it initially worked. And then stopped working. And then I'd revert back to pigpio.js, and it would work. And, interestingly enough, if I ran the pigpio.js version, then rolled the application forward to the pigpio-client version, it would work until I restarted pigpiod.c or the pi.

This makes me suspect that there's some setting / configuration pigpio.js does that I'm not doing when I start pigpiod.c without any flags.

As for the application code, the strategy is to build an array of waves, and then send them with: await gpio.waveChainTx([{ loop: false }, { waves }, { delay: 0 }, { repeat: 1 }]). My hope here is to offload all the timing sensitive parts to pigpio.c's internal wave management.

Do you have any advice on how I'd go about debugging this?

Error using waveform

The following code always returns a waveform error on my Raspberry Pi Zero W:

  let wc = await this.gpio.waveClear()
  let wap = await this.gpio.waveAddPulse(pulses)
  var wid = await this.gpio.waveCreate()
  let wso = await this.gpio.waveSendOnce(wid)
  let wdel = await this.gpio.waveDelete(wid)

Pulses is array of 132 tuples.

MyError [pigpioError]: attempt to create an empty waveform
at responseHandler (C:\Users\louis\AppData\Roaming\npm\node_modules\pigpio-client\pigpio-client.js:293:17)
at Socket. (C:\Users\louis\AppData\Roaming\npm\node_modules\pigpio-client\pigpio-client.js:343:30)
at Socket.emit (events.js:210:5)
at addChunk (_stream_readable.js:326:12)
at readableAddChunk (_stream_readable.js:301:11)
at Socket.Readable.push (_stream_readable.js:235:10)
at TCP.onStreamRead (internal/stream_base_commons.js:182:23) {
name: 'pigpioError',
code: 'PI_EMPTY_WAVEFORM',
message: 'attempt to create an empty waveform',
api: 'WVCRE'
}

i2c full features

I'd like to use more comprehensive i2c support, e.g. commands to read and write registers.
Use case is PCA9685 for control of servos.

Was there some reason why this is not a good addition?

(I've started coding it, but don't have much low level i2c experience, so it would be good to know if there are glaring gotchas..)
br,
Simon

timeout on first connect

Hi @guymcswain,

When a host is not available at startup:

if I don't specify pi.timeout, then I get the error event fired.

If I DO specify pi.timeout, then I don't get 'error', 'connected' or 'disconnected', but the pigpio instance stops and releases, and node exits. So there is no way to detect connection failure, and try, for example, an alternative address?

example code (try with and without timeout set....):

let host = '192.168.1.185' || process.env.npm_package_config_host;
let proc = require('process');
// comment out to stop debug
proc.env['DEBUG'] = 'pigpio';
proc.env['PIGPIO'] = '1';
// Connect to the Rpi...
console.log('#### trying to connect to '+host);
let opts = {
    host: host, // the host it will try to connect to port 8888 on....
    timeout: 3, // retry timer in s?
};
const pigpio = require('../pigpio-client').pigpio(opts);  
// a simple wait routine that can be called using await...
function wait(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function keepNodeAlive(){
    while (1){
        await wait(2000);
    }
}
//keepNodeAlive();

const ready = new Promise((resolve, reject) => {
  pigpio.once('connected', resolve);
  pigpio.once('error', reject);
});

pigpio.on('error', (e)=>{ console.log('#### pigpio error (on call)',e); });
pigpio.on('disconnected', (e)=>{ console.log('#### pigpio disconnected',e); });

ready.then(async (info) => {
    // display information on pigpio and connection status
    console.log('#### pigpio connected\n'+JSON.stringify(info,null,2));
    pigpio.end(()=>{ console.log('#### pigpio ended - should exit node now'); })
}).catch((e)=>{
    console.log('#### pigpio error (once call)', e);
});
console.log('#### Waiting for pigpio connection to '+host);

Notifications not working after network disconnect then reconnect.

I also noticed, that button.notify has massive delay. I had to wait like 20-30 seconds to get event change. I ended up checking pin manually, but maybe I am doing something wrong ?
( I used your exact example )
Is it how it works, or I am doing it wrong. Are there any other listeners for edge?

Notify on only one edge

I might just be missing it, but I can't figure out how to set up a notify for only falling or rising edges.

Is this implemented?

opened notification socket with handle=0

I have currently an issue using pigpio-client library inside docker.

My docker container is a privileged container and I am trying to issue a write command to two specific pins over the net. The IP I am using is the docker host IP (172.17.0.1)

request= 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
response= 26 0 0 0 0 0 0 0 0 0 0 0 64 0 0 0
request= 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
opened notification socket with handle= 0
response= 17 0 0 0 0 0 0 0 0 0 0 0 130 32 160 0
{
"host": "172.17.0.1",
"port": 8888,
"pipelining": false,
"commandSocket": true,
"notificationSocket": true,
"pigpioVersion": 64,
"hwVersion": 10494082,
"hardware_type": 3,
"userGpioMask": 268435452
}
request= 0 0 0 0 11 0 0 0 1 0 0 0 0 0 0 0
wrote false to pin11
wrote true to pin13
response= 0 0 0 0 11 0 0 0 1 0 0 0 0 0 0 0
deferred request= 0 0 0 0 13 0 0 0 1 0 0 0 0 0 0 0
response= 0 0 0 0 13 0 0 0 1 0 0 0 0 0 0 0
deferred request= 4 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0
response= 4 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0
deferred request= 4 0 0 0 13 0 0 0 1 0 0 0 0 0 0 0
response= 4 0 0 0 13 0 0 0 1 0 0 0 0 0 0 0

Apparently I cannot get any handle despite the fact I got a valid object with state connected.

lower timeout - for connection monitor

as previously mentioned. Is it possible to have lower timeout so connection is monitored as frequent as possible? Currently if it goes below 2, disconnect event and connect event are triggered for no reason every couple of seconds.

Also is it possible to have timeout on, for example led.write() ? Currently, if it looses connection somewhere in between, and tries to do write(), it waits forever until connection is back. So couple of writes can pileup and take processor, as they will be stacking and waiting for connection to be back.

(I could check if connection is lost, and have variable to check before trying to do led.write(), however connection is not checked frequently enough)

can't set up pulldown resistor

I am not sure if it's me, or pulldown resistor doesn't always work.

this is how my code looks like for setting up GPIOs,
`const slave = {
LED1: pigpio.gpio(config.slave.led_1),
LED2: pigpio.gpio(config.slave.led_2),
LEDext: pigpio.gpio(config.slave.led_ext),
PIR: pigpio.gpio(config.slave.pir)
}

async function settingUp() {
await slave.LED1.modeSet('output')
await slave.LED2.modeSet('output')
await slave.LEDext.modeSet('output')
await slave.PIR.pullUpDown(1)
await slave.PIR.modeSet('input')
return
}

settingUp().then(() => {
console.log('Slave GPIO Setup done...')
return
})
`
First time I got 0, but then after hours it went to 1 and stayed.
I tried changing order of setting up, then it worked again and today stopped.
Not pulling down. I am guessing it's something to do with me not understanding how it should work

SPI functionality

Hi,

I'm trying to connect the MPC3008, but I need SPI for this. Is there an example or something else? Or documentation so that I can add this to the lib?

Kind regards

Can't use more than 11 pigpio.gpio()

This happens when i use 11, everything works normally
before

As soon as i add 1 more, this happens
after

I've tryed implementing it in different ways, i just could not make it work.

pigpiod on different port

I am running pigpiod on a different port to the standard 8888, I am wanting to update my lights using pigs, but cant seem to work out how to get pigs to use the new port?

Thanks

writing and running pigpiod scripts?

Is there a way to create and run scripts in pigpiod?

My use case is to change multiple GPOs at as close to the same time as possible.

e.g. for motor control, the following script would be useful:

let scriptID = pigpio.storeScript('w p2 0 w p4 0 pwm p0 p1 w p2 p3 w p4 p5');

pigpio.runScript(scriptID, [17, 128, 18, 0, 27, 1]);

so setting GPOs 18 & 27 to 0, then setting the pwm duty on 17 to 128, then setting 18->0 & 27->1

these actions would all occur very quickly with no network latency getting in the way....

make install error

after the sudo make install, I get:
Traceback (most recent call last):
File "setup.py", line 3, in
from distutils.core import setup
ModuleNotFoundError: No module named 'distutils.core'
make: *** [Makefile:107: install] Error 1

Correct way to destroy the instance?

Hi,

what is the correct way to completely destroy an instance?

At the moment, I'm calling end(), and then de-referencing the object - but not using a callback because I anticipate that the callback would not be called if the socket(s) had not connected (due to error or just not yet...)?
I'm also calling endNotify() on the two GPIs that were being monitored (probably after end...).
Is there anything else I need to do?

br,
Simon

I2C support

It would be great to have I2C support. For example to connect to PCF8574 port expander or I2C sensors.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.