rpi-io 2.1.3 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,7 +1,13 @@
1
1
  # Changelog
2
- ## [2.1.3] - 2026-06-04
2
+ ## [2.2.0] - 2026-007-06
3
+ ### Added
4
+ - Static function RIO.lineIsSupported(*line*)
5
+ - Static function RIO.lineIsAvailable(*line*)
6
+
7
+ ## [2.1.5] - 2026-06-05
3
8
  ### Changed
4
9
  - License MIT replaced by EUPL
10
+ - Announcement as rpisquare.com module
5
11
 
6
12
  ## [2.1.1] - 2026-03-26
7
13
  ### Changed
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # rpi-io
2
- ![Static Badge](https://img.shields.io/badge/rpi--io-_2.1.x_-FF5500?style=flat) ![Static Badge](https://img.shields.io/badge/Nodejs-%3E_23-66cc33?logo=nodedotjs&logoColor=white) ![Static Badge](https://img.shields.io/badge/NPM-%3E_10-CC3534?logo=npm&logoColor=white) ![Static Badge](https://img.shields.io/badge/Raspberry_Pi-Zero2_4B_5B-C51A4A?logo=raspberrypi&logoColor=white) ![Static Badge](https://img.shields.io/badge/OS-Bookworm_Trixie-0D7AB9?style=flat)
2
+ ![Static Badge](https://img.shields.io/badge/rpi--io-_2.2.x_-FF5500?style=flat) ![Static Badge](https://img.shields.io/badge/Nodejs-%3E_23-66cc33?logo=nodedotjs&logoColor=white) ![Static Badge](https://img.shields.io/badge/NPM-%3E_10-CC3534?logo=npm&logoColor=white) ![Static Badge](https://img.shields.io/badge/Raspberry_Pi-Zero2_4B_5B-C51A4A?logo=raspberrypi&logoColor=white) ![Static Badge](https://img.shields.io/badge/OS-Bookworm_Trixie-0D7AB9?style=flat)
3
3
 
4
- Current version is 2.1.3. More details in [CHANGELOG.md](CHANGELOG.md).
4
+ Current version is 2.2.0. Latest update includes new static utility functions. More details in [CHANGELOG.md](CHANGELOG.md).
5
5
 
6
6
  **rpi-io** is a lite [ESM](https://nodejs.org/api/esm.html#modules-ecmascript-modules) module for **Node.js** to control **Raspberry Pi** GPIO: access (in, out), input event detection and [PWM](https://en.wikipedia.org/wiki/Pulse-width_modulation) peripheral control.
7
7
 
@@ -15,6 +15,7 @@ Current version is 2.1.3. More details in [CHANGELOG.md](CHANGELOG.md).
15
15
  - Input/Output - [libgpiod](https://libgpiod.readthedocs.io/en/stable/) v1.6.3 (*Bookworm*) and v2.2.1 (*Trixie*)
16
16
  - PWM - [sysfs](https://en.wikipedia.org/wiki/Sysfs) interface.
17
17
 
18
+ **rpi-io** is a key module of the solution [rpisquare.com](https://rpisquare.com) to control peripherals of remote Raspberry Pi devices.
18
19
 
19
20
  ## Prerequisites
20
21
 
@@ -339,7 +340,7 @@ import {RIO, sleep, ctrlC, } from "rpi-io"
339
340
  - [API reference guide](documentation/api.md)
340
341
  - [Benchmark](documentation/benchmark.md)
341
342
 
342
- ## Licence
343
+ ## License
343
344
 
344
345
  [EUPL](https://interoperable-europe.ec.europa.eu/collection/eupl)
345
346
 
@@ -186,12 +186,47 @@ RIO.closeAll()
186
186
 
187
187
  Function to return current model of RPi.
188
188
 
189
+ #### Example
190
+
189
191
  ```javascript
190
192
  import {RIO} from "rpi-io"
191
193
  console.log("model:", RIO.model())
192
194
  // Returns '5B', '4B', '3B', 'Zero2', 'Zero' or '' when unknown.
193
195
  ```
194
196
 
197
+
198
+
199
+ ### RIO.lineIsSupported(line)
200
+
201
+ Function to test that RIO supports a GPIO line number
202
+
203
+ #### Example
204
+
205
+ ```js
206
+ import {RIO} from "rpi-io"
207
+
208
+ console.log("line 1 is supported:", RIO.lineIsSupported(1)) // displays false
209
+ console.log"line 17 is supported:", RIO.lineIsSupported(17)) // displays true
210
+ ```
211
+
212
+
213
+
214
+ ### RIO.lineIsAvailable(line)
215
+
216
+ Function to test that RIO supports a GPIO line number and that is is not yet used
217
+
218
+ ```js
219
+ import {RIO} from "rpi-io"
220
+
221
+ RIO.closeAll() // Release any open instance
222
+ console.log"line 17 is supported:", RIO.lineIsAvailable(17)) // displays true
223
+ const led = new RIO(17, "output", {value: 0})
224
+ log("line 17 is available:", RIO.lineIsAvailable(17)) // displays false
225
+ RIO.closeAll()
226
+ ```
227
+
228
+
229
+
195
230
  ## Utilities
196
231
 
197
232
  ### log(args)
package/esm/main.mjs CHANGED
@@ -14,10 +14,9 @@ export {traceCfg, log, warn, sleep, ctrlC, lineConfig, lineNumber}
14
14
  const require = createRequire(import.meta.url)
15
15
  const ADDON = require("../build/Release/gpio.node")
16
16
  const CHIPNAME = "/dev/gpiochip0"
17
- const RPi_GPIO_STD = [4, 5, 6, 16, 17, 20, 21, 22, 23, 24, 25, 26, 27]
17
+ const RPi_GPIO_STD = [5, 6, 16, 17, 20, 21, 22, 23, 24, 25, 26, 27]
18
18
  const RPi_GPIO_PWM = [12, 13, 18, 19]
19
19
  const RPI_GPIO_ALL = [...RPi_GPIO_STD, ...RPi_GPIO_PWM]
20
- const RPI_CHIP = "gpiochip0"
21
20
  const PWM_CHIP = "pwmchip0"
22
21
 
23
22
  // -------------------------------------------------------------------
@@ -345,6 +344,28 @@ export class RIO {
345
344
  return ""
346
345
  }
347
346
  }
347
+
348
+ /** ------------------------------------------------------------------
349
+ * @function RIO.lineIsSupported
350
+ * @description Check that line number is supported by RIO
351
+ * @param {Number} line
352
+ * @return {Boolean}
353
+ */
354
+ static lineIsSupported(line) {
355
+ return RPI_GPIO_ALL.indexOf(line) !== -1
356
+ }
357
+
358
+ /** ------------------------------------------------------------------
359
+ * @function RIO.lineIsAvailable
360
+ * @description Check that line number is supported and
361
+ * not yet used in a RIO instance
362
+ * @param {Number} line
363
+ * @return {Boolean}
364
+ */
365
+ static lineIsAvailable(line) {
366
+ if (!RIO.lineIsSupported(line)) return false
367
+ return !RIO.instances.has(line)
368
+ }
348
369
  }
349
370
 
350
371
  // -------------------------------------------------------------------
@@ -0,0 +1,115 @@
1
+ // -------------------------------------------------------------------
2
+ // RPI-IO: ULN2003 STEP MOTORS CONTROL
3
+ // -------------------------------------------------------------------
4
+
5
+ import {RIO} from "./main.mjs";
6
+
7
+ export class ULN2003 {
8
+
9
+ static RPi_GPIO_STD = [5, 6, 16, 17, 20, 21, 22, 23, 24, 25, 26, 27]
10
+ static RPi_GPIO_PWM = [12, 13, 18, 19]
11
+
12
+ /** ------------------------------------------------------------------
13
+ * @function isValidGpio
14
+ * @description Check if pin a valid multipurpose GPIO
15
+ * @param {Number} pin
16
+ * @return {Boolean}
17
+ */
18
+ static static isValidGpio(pin) {
19
+ return [...ULN2003.RPi_GPIO_STD, ...ULN2003.RPi_GPIO_PWM].GPIOS.includes(pin);
20
+ }
21
+
22
+ static MODE = ["half", "full"]
23
+ static STEPS = {
24
+ half: [
25
+ [1, 0, 0, 0],
26
+ [1, 1, 0, 0],
27
+ [0, 1, 0, 0],
28
+ [0, 1, 1, 0],
29
+ [0, 0, 1, 0],
30
+ [0, 0, 1, 1],
31
+ [0, 0, 0, 1],
32
+ [1, 0, 0, 1]
33
+ ],
34
+ full: [
35
+ [1, 0, 0, 1],
36
+ [1, 1, 0, 0],
37
+ [0, 1, 1, 0],
38
+ [0, 0, 1, 1]
39
+ ]
40
+ }
41
+
42
+
43
+ /** ------------------------------------------------------------------
44
+ * @method constructor
45
+ * @param {Array} pins - Array of 4 GPIO BCM port number e.g. [21,22,24,25]
46
+ * @param {Object} opt - Delay between steps and sequencing mode. See below
47
+ */
48
+ constructor(pins, opt) {
49
+
50
+ // Init options with default values and check validity
51
+ const defopt = {
52
+ // Delay between two steps in 1 ≤ ms ≤ 1000
53
+ delayMs: 10,
54
+ // Sequencing mode: 'full' or 'half' (default)
55
+ seqMode: ULN2003.MODE[0]
56
+ }
57
+ opt = {...defopt, ...opt}
58
+ opt.seqMode = opt.seqMode.toLowerCase()
59
+
60
+ // Check if pins
61
+ pins.forEach(pin => {
62
+ if (!ULN2003.isValidGpio(pin)) {
63
+ throw new Error("Invalid pin number: " + pin)
64
+ }
65
+ })
66
+
67
+
68
+ rpiIoInstances.forEach(instance => {
69
+ if (!instance instanceof RIO) throw new Error("Invalid instance")
70
+ if (instance.mode !== "output") throw new Error("Invalid instance mode")
71
+ })
72
+
73
+ // Reset option values if not valid
74
+ if (ULN2003.MODE.indexOf(opt.seqMode) === -1) opt.seqMode = defopt.seqMode
75
+ if (typeof opt.delayMs !== "number" || opt.delayMs < 1 || opt.delayMs > 1000) opt.delayMs = defopt.delayMs
76
+
77
+
78
+ // Throw error if list of rpi-io instances is not valid: wrong number, wrong instance, wrong mode
79
+ if (!Array.isArray(rpiIoInstances) || rpiIoInstances.length !== 4) throw new Error("Invalid instance list.")
80
+ rpiIoInstances.forEach(instance => {
81
+ if (!instance instanceof RIO) throw new Error("Invalid instance")
82
+ if (instance.mode !== "output") throw new Error("Invalid instance mode")
83
+ })
84
+
85
+ // No option error here
86
+
87
+ this.coils = rpiIoInstances
88
+ this.sequence = ULN2003.STEPS[opt.seqMode]
89
+ this.len = this.sequence.length
90
+ this.delay = opt.delayMs
91
+ this.A = rpiIoInstances[0]
92
+ this.B = rpiIoInstances[1]
93
+ this.C = rpiIoInstances[2]
94
+ this.D = rpiIoInstances[0]
95
+ this.inc = 0
96
+ this.index = 0
97
+ }
98
+ }
99
+
100
+ const toto = new ULN2003()
101
+
102
+
103
+ /** ------------------------------------------------------------------
104
+ * @method uln2003
105
+ * @description To control step motors based on ULN20003-like drivers.
106
+ * This function used 4 rpi-io output instances to control
107
+ * step motors back and forth.
108
+ * @param {Object} pins
109
+ * @param {Number} steps - Nombre de pas (entier > 0)
110
+ * @param {'fwd'|'rev'} dir - Direction : 'fwd' = avant, 'rev' = arrière
111
+ */
112
+
113
+ // -------------------------------------------------------------------
114
+ // EoF
115
+ // -------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rpi-io",
3
- "version": "2.1.3",
3
+ "version": "2.2.0",
4
4
  "description": "Nodejs module to control Raspberry Pi GPIO",
5
5
  "keywords": [
6
6
  "raspberry",
@@ -0,0 +1,103 @@
1
+ // -------------------------------------------------------------------
2
+ // TEST - ULN 2003
3
+ // -------------------------------------------------------------------
4
+ import {RIO, traceCfg, log, sleep, ctrlC, lineNumber} from "../esm/main.mjs"
5
+
6
+ (async () => {
7
+ traceCfg(2)
8
+
9
+ // Init outputs
10
+ const pins = {
11
+ A: new RIO(21, 'output', {value: 0}),
12
+ B: new RIO(22, 'output', {value: 0}),
13
+ C: new RIO(24, 'output', {value: 0}),
14
+ D: new RIO(25, 'output', {value: 0}),
15
+ }
16
+
17
+ const HALF_STEP = [
18
+ [1, 0, 0, 0],
19
+ [1, 1, 0, 0],
20
+ [0, 1, 0, 0],
21
+ [0, 1, 1, 0],
22
+ [0, 0, 1, 0],
23
+ [0, 0, 1, 1],
24
+ [0, 0, 0, 1],
25
+ [1, 0, 0, 1],
26
+ ]
27
+
28
+ const FULL_STEP = [
29
+ [1, 0, 0, 1],
30
+ [1, 1, 0, 0],
31
+ [0, 1, 1, 0],
32
+ [0, 0, 1, 1],
33
+ ]
34
+
35
+ /** ------------------------------------------------------------------
36
+ * @function write
37
+ * @param {Number} port
38
+ * @param {Number} value
39
+ */
40
+ const write = (port, value) => {
41
+ pins[port].write(value)
42
+ }
43
+
44
+ const reset = () => {
45
+ for (const [port, instance] of Object.entries(pins)) {
46
+ instance.write(0)
47
+ }
48
+ }
49
+
50
+ const test = async () => {
51
+ for (const [port, instance] of Object.entries(pins)) {
52
+ instance.write(1)
53
+ await sleep(500)
54
+ instance.write(0)
55
+ await sleep(500)
56
+ }
57
+ }
58
+
59
+ // Close ports on ctrl+c
60
+ ctrlC(() => {
61
+ reset()
62
+ RIO.closeAll();
63
+ })
64
+
65
+ /** --------------------------------------------------------------
66
+ * @function stepMotor
67
+ * @param {Object} opts
68
+ * @param {Number} opts.steps - Nombre de pas (entier > 0)
69
+ * @param {'fwd'|'rev'} opts.dir - Direction : 'fwd' = avant, 'rev' = arrière
70
+ * @param {number} opts.delayMs - Délai entre deux pas en millisecondes (≥ 1)
71
+ * @param {'half'|'full'} [opts.mode='half'] - Mode de séquençage
72
+ * @returns {Promise<void>}
73
+ */
74
+ async function stepMotor({steps, dir = 'fwd', delayMs = 10, mode = 'half'}) {
75
+ const sequence = mode === 'half' ? HALF_STEP : FULL_STEP;
76
+ const len = sequence.length;
77
+ const inc = dir === 'fwd' ? 1 : -1;
78
+ let index = 0;
79
+
80
+ for (let i = 0; i < steps; i++) {
81
+ const [a, b, c, d] = sequence[index];
82
+ write('A', a);
83
+ write('B', b);
84
+ write('C', c);
85
+ write('D', d);
86
+ index = ((index + inc) % len + len) % len;
87
+ await sleep(delayMs); // sleep() natif rpi-io
88
+ }
89
+
90
+ // Désactivation des bobines
91
+ write('A', 0);
92
+ write('B', 0);
93
+ write('C', 0);
94
+ write('D', 0);
95
+ }
96
+
97
+
98
+ await stepMotor({ steps: 1600, dir: 'rev', delayMs: 8 })
99
+
100
+ })()
101
+ // -------------------------------------------------------------------
102
+ // EoF
103
+ // -------------------------------------------------------------------
@@ -0,0 +1,16 @@
1
+ // -------------------------------------------------------------------
2
+ // TEST - line support and availability
3
+ // -------------------------------------------------------------------
4
+ import {RIO, traceCfg, log} from "../esm/main.mjs"
5
+
6
+ traceCfg(2)
7
+ log("line 1 is supported:", RIO.lineIsSupported(1))
8
+ log("line 17 is supported:", RIO.lineIsSupported(17))
9
+ log("line 17 is available:", RIO.lineIsAvailable(17))
10
+ const led = new RIO(17, "output", {value: 0})
11
+ log("new instance with line 17")
12
+ log("line 17 is available:", RIO.lineIsAvailable(17))
13
+ RIO.closeAll()
14
+ // -------------------------------------------------------------------
15
+ // EoF
16
+ // -------------------------------------------------------------------
package/test/model.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // -------------------------------------------------------------------
2
- // TEST - Simple write
2
+ // TEST - Retrive RPi model
3
3
  // -------------------------------------------------------------------
4
4
  import {RIO} from "../esm/main.mjs"
5
5