UNPKG

@eeemarv/io-spi

Version:

Promise Based Native Node.js Addon for SPI devices on Linux

377 lines (254 loc) 9.15 kB
# Node Native SPI Addon ![TypeScript](https://img.shields.io/badge/types-included-blue.svg) A high-performance Node.js native addon for SPI communication on Linux, leveraging direct spidev.h APIs. Features * Promise-based transfers with configurable per-transfer settings. * Dynamic reconfiguration of mode, speed_hz, and bits_per_word. * Full support for Linux SPI parameters (e.g., delay_usecs, cs_change). * Getters for current device settings. * Built-in TypeScript type declarations. ## Concurrency & Multiple Chip Select (CS) ### Non-Blocking API All transfers are __asynchronous__ by design: ```js import SPIDevice from '@eeemarv/io-spi'; spi = new SPIDevice('/dev/spidev0.0'); // Fire-and-forget transfer spi.transfer([txBuffer]) .then([rxBuffer] => console.log('Done!')) .catch(err => console.error('Error:', err)); ``` ### Key notes * Transfers are queued at the OS level but don't block Node.js's event loop. * Each transfer() call returns a native Promise (no manual threading required). --- ### Multiple Chip Select (CS) Pins To control multiple SPI slaves, create separate instances per CS ```js // Each CS line gets its own instance // each instance contains its own configuration: // mode, max_speed_hz and bits_per_word const spiCS0 = new SPIDevice('/dev/spidev0.0'); // Uses CS0 const spiCS1 = new SPIDevice('/dev/spidev0.1',{ max_speed_hz: 500_000 }); // Uses CS1 const txBufferForDevice0 = Buffer.from([0x55, 0xAA]); const txBufferForDevice1 = Buffer.from([0x66, 0xBB]); // Concurrent operations Promise.all([ spiCS0.transfer([txBufferForDevice0]), spiCS1.transfer([txBufferForDevice1]) ]).then(([[rxBufferfromDevice0], [rxBufferFromDevice1]]) => { console.log(rxBufferfromDevice0); console.log(rxBufferfromDevice1); }).catch((error) => { console.error(error); }); ``` ### Hardware Setup * Ensure each slave has a dedicated CS line (e.g., CS0, CS1). * Kernel must expose multiple /dev/spidevX.Y devices (check `ls /dev/spidev* -l`). __Example Wiring (Raspberry Pi):__ Pi (Master) | Peripheral 0 | Peripheral 1 ---|---|--- MOSI (GPIO10) | MOSI | MOSI MISO (GPIO9) | MISO | MISO SCLK (GPIO11) | SCLK | SCLK CE0 (GPIO8) | CS | - CE1 (GPIO7) | - | CS ## Installation ```bash npm install @eeemarv/io-spi # OR yarn add @eeemarv/io-spi ``` ## Prerequisites * Linux (e.g., Raspberry Pi, Orange Pi) with SPI kernel support. * Node.js v20+ * Build tools: ```bash sudo apt-get install build-essential python3 ``` ## Usage ### Import ```js const SPIDevice = require('@eeemarv/io-spi'); // OR import SPIDevice from '@eeemarv/io-spi'; ``` ### Initialize ```js const spi = new SPIDevice('/dev/spidev0.0', { mode: 3, // SPI mode (default 0) max_speed_hz: 500_000, // Clock speed (default 1_000_000 or 1MHz) bits_per_word: 16 // Bits per word (default 8) }); ``` Or configure dynamically: ```js spi.setMode(2); // Switch to mode 2 spi.setMaxSpeedHz(250_000); // Reduce speed to 250kHz spi.setBitsPerWord(8); console.log(spi.getMode()); // e.g., 2 ``` ### Transfer Data #### Simple Transfer (Uses Device Defaults) ```javascript spi.transfer([ Buffer.from([0x01, 0x02]) ]).then(([result]) => { console.log(result); // Buffer with received data }).catch((error) => { console.log(error); }); ``` #### Advanced Transfer (Per-Transfer Settings) ```javascript spi.transfer([ Buffer.from([0x01, 0x02]), // Uses device defaults { // Overrides settings for this transfer tx_buf: Buffer.from([0x03, 0x04]), // required speed_hz: 500000, // Temporary speed change delay_usecs: 100, // Delay after transfer (microseconds) cs_change: 1 // Toggle CS after this transfer }, Buffer.from([0x05, 0x06]) // Reverts to device defaults ]).then((results) => { console.log(results); // `results` is an array of Buffers (one per transfer) }).catch((error) => { console.log(error); }); ``` ## API Reference ### new SPIDevice(path[, options]) * path (string): SPI device path (e.g., /dev/spidev0.0). * options (object): * mode: SPI mode 0-3 (CPOL/CPHA), more rare modes are also supported. Defaults to 0. * max_speed_hz (number): Clock speed in Hz. Defaults to 1_000_000 (1Mhz) * bits_per_word (number): Bits per word. Defaults to 8 ### Methods Method | Description ---|--- transfer(transfers) | Returns a Promise<Buffer[]> for all transfers. Each transfer can override settings (see below). setMode(mode) | Sets SPI mode. Throws if invalid. getMode() | Returns current mode. setMaxSpeedHz(hz) | Sets clock speed (Hz). getMaxSpeedHz() | Returns current speed. setBitsPerWord(bits) | Sets bits per word (usually 8). getBitsPerWord() | Returns current bits per word. ### Transfer Object Parameters Each transfer can specify: Parameter | Type | Description ---|---|--- `tx_buf` | Buffer | Data to send. Required. `speed_hz` | number | Temporary clock speed (overrides max_speed_hz). `delay_usecs` | number | Delay after transfer (microseconds). `cs_change` | number (0,1) | Toggle chip select after this transfer. default is 0. See [Linux spidev.h](https://github.com/torvalds/linux/blob/master/include/uapi/linux/spi/spidev.h) for full documentation of all parameters. Parameters `tx_nbits`, `rx_nbits` and `word_delay_usecs` can also be used, but these are not widely implemented. ## TypeScript Support This package includes built-in TypeScript type declarations via `index.d.ts`. If you're using TypeScript, you'll get autocompletion and type checking automatically: ```ts import SPIDevice from '@eeemarv/io-spi'; const spi = new SPIDevice('/dev/spidev0.0', { max_speed_hz: 1_000_000, mode: 0 }); ``` Type definitions include * Constructor options * transfer() method with buffer/object overloads * Getter/setter methods for mode, speed, and bits-per-word No need to install @types/... — types are bundled with the package. ## Examples ### Loopback Test With this test you can see if the SPI device works without the involvement of a slave device. Connect the MOSI pin directly to the MISO pin, run the test and see if the data matches. ```bash node examples/loopback.js ``` The default device is `/dev/spidev0.0` but can be changed with the `--device` flag. Other flags for max_speed_hz (`--speed=<number>`), mode (`--mode=<0,1,2 or 3>`) and bits_per_word (`--bits=<8,16 or 32>`) can be set. ### MFRC522 The RC522 module (with MFRC522 NXP chip) can communicate with the contactless Mifare tags. This test performs a self test and then scans for tag UIDs (4, 7 or 10 bytes). ```bash node examples/loopback.js ``` The default device is `/dev/spidev0.0` and can be changed with the `--device` flag. The `max_speed_hz` of 10Mhz can be change with the `--speed` flag (`--speed=<number>`). If the self test fails (in case of a clone MFRC522), it can be disabled with `--no-self-test`. ## Troubleshooting ### Enable SPI Check out if SPI is enabled. To list all available SPI devices: ```bash ls -l /dev/spi* ``` Check out the manual of your SBC on how to enable the SPI devices. If you have `raspi-config` (common on the Raspberry Pi), run ```bash sudo raspi-config ``` Navigate to Interface options > Enable SPI. On the Orange Pi, run `orangepi-config` ```bash sudo orangepi-config ``` Navigate to System > Hardware > Toggle hardware configuration. ### Permission Denied To allow non-root users to access the SPI device (e.g. `/dev/spidev0.0`, `/dev/spidev1.1`) without sudo, you need to modify the device permissions and group ownership permanently. #### 1. Create a Dedicated Group for SPI Access ```bash sudo groupadd spi ``` #### 2. Add Your User to the Group ```bash sudo usermod -aG spi $(whoami) # Replace $(whoami) with the target username ``` (Log out and back in for the group change to take effect.) #### 3. Set a udev Rule to Change SPI Device Permissions Ubuntu, Debian and Raspbian use udev to manage device permissions. Create a new rule: ```bash sudo nano /etc/udev/rules.d/90-spi.rules ``` Add this line to grant read/write access to the `spi` group: ```bash SUBSYSTEM=="spidev", GROUP="spi", MODE="0660" ``` #### 4. Reload udev Rules & Trigger Changes ```bash sudo udevadm control --reload-rules sudo udevadm trigger ``` #### 5. Verify the Changes Check the SPI device permissions: ```bash ls -l /dev/spidev* ``` Expected output: ```bash crw-rw---- 1 root spi 153, 0 Jun 17 10:14 /dev/spidev1.1 ``` Now, users in the `spi` group can access it without sudo. ### Invalid Arguments Verify parameters match spidev.h constraints (e.g., valid mode). ### Build Issues Rebuild with `node-gyp rebuild --verbose` ### Hardware connection problems For good connection, especially at speeds above 1Mhz, be sure * to keep wires or paths short * to keep wires or paths bundled together * to take ground (and power) from pins the closed to the MISO, MOSI, CS and CLCK pins in order to provide a ground return path * to thoroughly clean up flux after soldering * that you are invoking the right spi device. E.g. on the Orange Pi Zero 3 only /dev/spidev1.1 is available on the pinout. ## License MIT