rpi-io 1.1.4 → 1.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/README.md +27 -10
- package/esm/rio.mjs +176 -92
- package/esm/utl.mjs +2 -2
- package/history.md +9 -4
- package/package.json +9 -3
- package/scr/test.js +16 -10
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# rpi-io
|
|
2
|
-
    
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
**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.
|
|
@@ -24,7 +24,7 @@ By default rpi-io does not require specific configuration.
|
|
|
24
24
|
|
|
25
25
|
If you want to use [hardware-based PWM](https://pinout.xyz/pinout/pwm) peripherals, some configuration is required:
|
|
26
26
|
|
|
27
|
-
- Open the file `/boot/firmware/config.txt` and add the required [dtoverlay](https://www.raspberrypi.com/documentation/computers/configuration.html#part3.1) configuration for PWM as
|
|
27
|
+
- Open the file `/boot/firmware/config.txt` and add the required [dtoverlay](https://www.raspberrypi.com/documentation/computers/configuration.html#part3.1) configuration for PWM as showed in the examples below.
|
|
28
28
|
|
|
29
29
|
```shell
|
|
30
30
|
# Examples of PWM configuration to add to/boot/firmware/config.txt
|
|
@@ -228,7 +228,7 @@ let currentValue = myInput.get();
|
|
|
228
228
|
|
|
229
229
|
|
|
230
230
|
|
|
231
|
-
#### monitor(edge, callback)
|
|
231
|
+
#### monitor(edge, callback, options)
|
|
232
232
|
|
|
233
233
|
To monitor activities on some *in* GPIO and receive events in a callback function.
|
|
234
234
|
|
|
@@ -246,6 +246,9 @@ myButton.monitor("both", event => {
|
|
|
246
246
|
|
|
247
247
|
- **edge** *{String}* 'rising', 'falling' or 'both'. Any other value stops instance monitoring.
|
|
248
248
|
- **callback** *{Function}* Callback function with one Object parameter for input events on instance e.g. `event = {edge: 'rising,' time: 'Thu Oct 30 2025 18:12:35 GMT+0100'}`
|
|
249
|
+
- **options** *{Object}* Default value is `{bias: 'disable', bounce: 0}`
|
|
250
|
+
- `bias` To configure according your input connection. Supported value ares 'disable', 'pull-down' and 'pull-up'.
|
|
251
|
+
- `bounce` Delay in ms to filter callback on rebounce. Value between 0 and 1000. 0 means no filter.
|
|
249
252
|
|
|
250
253
|
##### Detected errors
|
|
251
254
|
|
|
@@ -273,7 +276,7 @@ console.log("servo position = 0°");
|
|
|
273
276
|
|
|
274
277
|
##### Parameters
|
|
275
278
|
|
|
276
|
-
All time parameters are defined in
|
|
279
|
+
All time parameters are defined in nanoseconds (ns).
|
|
277
280
|
|
|
278
281
|
- **period** *{Number}* Time period in which [duty cycle](https://en.wikipedia.org/wiki/Pulse-width_modulation) varies.
|
|
279
282
|
- **duty** *{Number}* Initial duty cycle.
|
|
@@ -331,6 +334,16 @@ servo.pwmDuty(1500000)
|
|
|
331
334
|
|
|
332
335
|
|
|
333
336
|
### Utility functions
|
|
337
|
+
#### Rio.stopMonitoring()
|
|
338
|
+
|
|
339
|
+
To stop monitoring subprocess when leaving your app script or in case of failure. This prevents having a runaway subprocess that could block a new call to the `monitor()` method.
|
|
340
|
+
|
|
341
|
+
##### Example
|
|
342
|
+
|
|
343
|
+
```javascript
|
|
344
|
+
Rio.stopMonitoring()
|
|
345
|
+
```
|
|
346
|
+
|
|
334
347
|
#### Rio.config(gpio)
|
|
335
348
|
|
|
336
349
|
To return configuration of some GPIO
|
|
@@ -379,7 +392,7 @@ console.log(Rio.version())
|
|
|
379
392
|
|
|
380
393
|
#### Rio.os()
|
|
381
394
|
|
|
382
|
-
To retrieve
|
|
395
|
+
To retrieve Raspberry Pi OS info.
|
|
383
396
|
|
|
384
397
|
##### Example
|
|
385
398
|
|
|
@@ -418,11 +431,15 @@ Rio.isSystemSupported() // See console below
|
|
|
418
431
|
|
|
419
432
|
## Benchmark
|
|
420
433
|
|
|
421
|
-
The following table summarizes GPIO *set* average times with rpi-io v1.1.0 and various
|
|
434
|
+
The following table summarizes GPIO *set* average times with rpi-io v1.1.0 and various hardware and OS.
|
|
435
|
+
|
|
436
|
+
💡 If you can, stick to *Bookworm*: **rpi-io** has stability and performance issues with Trixie and libgpiod v2.2.1. I did not test libgpiod v1.6.3 on Trixie.
|
|
437
|
+
|
|
438
|
+
| | RPi 5B | RPi 4B | RPi Zero2 |
|
|
439
|
+
|----------|---------|----------|------------|
|
|
440
|
+
| Bookworm | 1.37 ms | 4.56 ms | 9.07 ms |
|
|
441
|
+
| Trixie | 3.82 ms | 11.29 ms | not tested |
|
|
442
|
+
|
|
422
443
|
|
|
423
|
-
| | RPi 5B | RPi 4B | RPi Zero2 |
|
|
424
|
-
| -------- | ------- | ---------- | ---------- |
|
|
425
|
-
| Bookworm | 1.37 ms | 4.56 ms | 9.07 ms |
|
|
426
|
-
| Trixie | 3.82 ms | not tested | not tested |
|
|
427
444
|
---
|
|
428
445
|
|
package/esm/rio.mjs
CHANGED
|
@@ -33,12 +33,8 @@ export class Rio {
|
|
|
33
33
|
static PWM_CHIP = "pwmchip0"
|
|
34
34
|
static TYPES = ["in", "out", "pwm"]
|
|
35
35
|
|
|
36
|
-
//
|
|
37
|
-
static
|
|
38
|
-
// Object list of monitored inputs
|
|
39
|
-
static monitored = {}
|
|
40
|
-
// Current monitoring process
|
|
41
|
-
static monitoringProcess = null
|
|
36
|
+
// Map of instances
|
|
37
|
+
static instances = new Map()
|
|
42
38
|
|
|
43
39
|
// ---------------------------------------------------------------
|
|
44
40
|
// GENERIC METHODS: constructor, disable
|
|
@@ -51,13 +47,18 @@ export class Rio {
|
|
|
51
47
|
*/
|
|
52
48
|
constructor(gpio, type) {
|
|
53
49
|
|
|
54
|
-
this.chip = Rio.RPI_CHIP
|
|
55
50
|
this.gpio = gpio
|
|
56
51
|
this.gpioStr = this.gpio.toString()
|
|
57
52
|
this.gpioName = "GPIO" + this.gpioStr
|
|
58
53
|
this.type = type
|
|
59
54
|
this.os = Rio.os().name
|
|
60
55
|
this.versionLib = Rio.version().substring(0, 4)
|
|
56
|
+
// Monitoring properties
|
|
57
|
+
this.watch = {
|
|
58
|
+
edge: "stop",
|
|
59
|
+
callback: false,
|
|
60
|
+
process: null
|
|
61
|
+
}
|
|
61
62
|
|
|
62
63
|
// OS is not supported
|
|
63
64
|
if (Rio.TESTED_OS.indexOf(this.os) === -1)
|
|
@@ -78,34 +79,19 @@ export class Rio {
|
|
|
78
79
|
if (Rio.RPI_GPIOS.indexOf(gpio) === -1)
|
|
79
80
|
throw new Error("This gpio is not supported: " + gpio)
|
|
80
81
|
|
|
81
|
-
// gpio is already
|
|
82
|
-
if (Rio.
|
|
83
|
-
throw new Error("This gpio is already
|
|
82
|
+
// gpio is already defined
|
|
83
|
+
if (Rio.instances.has(gpio))
|
|
84
|
+
throw new Error("This gpio is already defined: " + gpio)
|
|
84
85
|
|
|
85
86
|
// this.read, this.write, monArgs depending on gpiolib version
|
|
86
87
|
switch (this.versionLib) {
|
|
87
88
|
case "v1.6":
|
|
88
|
-
this.write = (value
|
|
89
|
+
this.write = (value) => {
|
|
89
90
|
exeFile("gpioset", [Rio.RPI_CHIP, this.gpioStr + "=" + value])
|
|
90
91
|
}
|
|
91
92
|
this.read = () => {
|
|
92
93
|
return exeFile("gpioget", [Rio.RPI_CHIP, this.gpioStr])
|
|
93
94
|
}
|
|
94
|
-
this.monArgs = gpios => {
|
|
95
|
-
let args = []
|
|
96
|
-
args.push("-b") // line-buffered: output is flushed after every line
|
|
97
|
-
args.push("--format=%o %e") // gpio, edge. Others: seconds(%s) and nanoseconds (%n)
|
|
98
|
-
args.push(Rio.RPI_CHIP)
|
|
99
|
-
args = args.concat(gpios)
|
|
100
|
-
return args
|
|
101
|
-
}
|
|
102
|
-
this.monData = data => {
|
|
103
|
-
const [gpio, edge] = data.toString().trim().split(/\s+/)
|
|
104
|
-
return {
|
|
105
|
-
gpio: gpio,
|
|
106
|
-
edge: edge
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
95
|
break
|
|
110
96
|
case "v2.2":
|
|
111
97
|
this.write = (value, time = 0) => {
|
|
@@ -114,19 +100,6 @@ export class Rio {
|
|
|
114
100
|
this.read = () => {
|
|
115
101
|
return exeFile("gpioget", ["--numeric", this.gpioName])
|
|
116
102
|
}
|
|
117
|
-
this.monArgs = gpios => {
|
|
118
|
-
gpios = gpios.map(gpio => "GPIO" + gpio)
|
|
119
|
-
let args = []
|
|
120
|
-
args = args.concat(gpios)
|
|
121
|
-
return args
|
|
122
|
-
}
|
|
123
|
-
this.monData = data => {
|
|
124
|
-
const [time, edge, gpio] = data.toString().trim().split(/\s+/)
|
|
125
|
-
return {
|
|
126
|
-
gpio: gpio.substring(5,7),
|
|
127
|
-
edge: edge
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
103
|
break
|
|
131
104
|
}
|
|
132
105
|
|
|
@@ -148,21 +121,26 @@ export class Rio {
|
|
|
148
121
|
this.pathPwm = "/sys/class/pwm/" + this.pwm + "/"
|
|
149
122
|
this.pathChannel = this.pathPwm + "pwm" + this.channel + "/"
|
|
150
123
|
}
|
|
124
|
+
// Everything OK => Add this to instance list
|
|
125
|
+
Rio.instances.set(gpio, this)
|
|
126
|
+
log("new gpio instance created:", this.gpio)
|
|
151
127
|
}
|
|
152
128
|
|
|
153
129
|
/** --------------------------------------------------------------
|
|
154
130
|
* @function disable
|
|
155
|
-
* @description Remove instance from Rio.
|
|
131
|
+
* @description Remove instance from Rio.instances
|
|
156
132
|
* Set this.gpio = -1
|
|
157
133
|
*/
|
|
158
134
|
disable() {
|
|
159
|
-
|
|
160
|
-
if (this.gpio === -1 || i === -1) {
|
|
161
|
-
log("gpio", this.gpio, "is already disabled")
|
|
162
|
-
} else {
|
|
163
|
-
Rio.used_gpios.splice(i, 1)
|
|
164
|
-
log("gpio", this.gpio, "has been disabled")
|
|
135
|
+
if (Rio.instances.has(this.gpio)) {
|
|
165
136
|
this.gpio = -1
|
|
137
|
+
this.watch.edge = "stop"
|
|
138
|
+
this.watch.callback = false
|
|
139
|
+
this.watch.process !== null ? this.watch.process.kill("SIGTERM") : false
|
|
140
|
+
Rio.instances.delete(this.gpio)
|
|
141
|
+
log("gpio", this.gpio, "has been disabled")
|
|
142
|
+
} else {
|
|
143
|
+
log("gpio", this.gpio, "is already disabled")
|
|
166
144
|
}
|
|
167
145
|
}
|
|
168
146
|
|
|
@@ -223,13 +201,112 @@ export class Rio {
|
|
|
223
201
|
return result
|
|
224
202
|
}
|
|
225
203
|
|
|
204
|
+
/** --------------------------------------------------------------
|
|
205
|
+
* @function monArgs
|
|
206
|
+
* @description Utility to take care gpiomon arguments according to libgpiod version
|
|
207
|
+
* @param {Array} gpios
|
|
208
|
+
* @param {Object} opt
|
|
209
|
+
* @return {Array}
|
|
210
|
+
*/
|
|
211
|
+
#monArgs = (gpios, opt) => {
|
|
212
|
+
let args = []
|
|
213
|
+
switch (this.versionLib) {
|
|
214
|
+
case "v1.6":
|
|
215
|
+
args.push("-b") // line-buffered: output is flushed after every line
|
|
216
|
+
args.push("--bias=" + opt.bias)
|
|
217
|
+
args.push("--format=%o %e") // gpio, edge. Others: seconds(%s) and nanoseconds (%n)
|
|
218
|
+
args.push(Rio.RPI_CHIP)
|
|
219
|
+
return args.concat(gpios)
|
|
220
|
+
case "v2.2":
|
|
221
|
+
opt.bias === "disable" ? opt.bias = "disabled" : false
|
|
222
|
+
gpios = gpios.map(gpio => "GPIO" + gpio)
|
|
223
|
+
args.push("--bias")
|
|
224
|
+
args.push(opt.bias)
|
|
225
|
+
return args.concat(gpios)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** --------------------------------------------------------------
|
|
230
|
+
* @function monData
|
|
231
|
+
* @description Utility to take care of gpiomon stdout data
|
|
232
|
+
* @param {String} data
|
|
233
|
+
* @return {Object}
|
|
234
|
+
*/
|
|
235
|
+
#monData = data => {
|
|
236
|
+
switch (this.versionLib) {
|
|
237
|
+
case "v1.6":
|
|
238
|
+
const [gpioV1, edgeV1] = data.toString().trim().split(/\s+/)
|
|
239
|
+
return {
|
|
240
|
+
gpio: gpioV1,
|
|
241
|
+
edge: edgeV1
|
|
242
|
+
}
|
|
243
|
+
case "v2.2":
|
|
244
|
+
const [timeV2, edgeV2, gpioV2] = data.toString().trim().split(/\s+/)
|
|
245
|
+
return {
|
|
246
|
+
time: timeV2,
|
|
247
|
+
gpio: gpioV2.substring(5, 7),
|
|
248
|
+
edge: edgeV2
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/** --------------------------------------------------------------
|
|
253
|
+
* @function monCallback
|
|
254
|
+
* @description Utility to trigger callback from gpiomon stdout data
|
|
255
|
+
* @param {String} data
|
|
256
|
+
*/
|
|
257
|
+
#monCallback = data => {
|
|
258
|
+
const eventData = this.#monData(data)
|
|
259
|
+
const now = new Date()
|
|
260
|
+
const delta = Math.max(1, now - this.monitoredEvent.latest) // delta is always > 0ms
|
|
261
|
+
const edge = eventData.edge === "1" ? "rising" : "falling"
|
|
262
|
+
|
|
263
|
+
// Bounce detected
|
|
264
|
+
if (delta <= this.bounceTime && edge === this.monitoredEvent.edge) {
|
|
265
|
+
log("bounce detected on gpio", this.gpio, edge, delta + "ms")
|
|
266
|
+
}
|
|
267
|
+
// Trigger callback according to edge requirement
|
|
268
|
+
else {
|
|
269
|
+
if (this.watch.enabled.indexOf(edge) !== -1)
|
|
270
|
+
this.watch.callback(edge, now)
|
|
271
|
+
// log("monitored event on gpio", this.gpio, edge,)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Update latest monitored event
|
|
275
|
+
this.monitoredEvent.latest = now
|
|
276
|
+
this.monitoredEvent.edge = edge
|
|
277
|
+
}
|
|
278
|
+
|
|
226
279
|
/** --------------------------------------------------------------
|
|
227
280
|
* @function monitor
|
|
228
281
|
* @description Wait for edge events
|
|
229
282
|
* @param {String} edge {'stop', 'rising', 'falling' 'both'}
|
|
230
283
|
* @param {Function} callback (edge, time)
|
|
284
|
+
* @param {Object} opt - opt.bias {'disable','pull-up','pull-down'}
|
|
285
|
+
* - opt.bounce 0-1000
|
|
231
286
|
*/
|
|
232
|
-
monitor(edge, callback) {
|
|
287
|
+
monitor(edge, callback, opt) {
|
|
288
|
+
|
|
289
|
+
// Default and clean parameters
|
|
290
|
+
const defaultCallback = (edge, time) => {
|
|
291
|
+
log("gpio", this.gpio, "event:", edge, time)
|
|
292
|
+
}
|
|
293
|
+
const defopt = {
|
|
294
|
+
bias: "disable",
|
|
295
|
+
bounce: 0
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
callback === undefined ? callback = defaultCallback : false
|
|
299
|
+
opt = {...defopt, ...opt}
|
|
300
|
+
opt.bounce < 0 ? opt.bounce = 0 : false
|
|
301
|
+
opt.bounce > 1000 ? opt.bounce = 1000 : false
|
|
302
|
+
this.bounceTime = opt.bounce
|
|
303
|
+
this.monitoredEvent = {
|
|
304
|
+
latest: new Date(null),
|
|
305
|
+
edge: "none"
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const biases = ["disable", "pull-up", "pull-down"]
|
|
309
|
+
biases.indexOf(opt.bias) === -1 ? opt.bias = "disable" : false
|
|
233
310
|
|
|
234
311
|
if (this.gpio === -1)
|
|
235
312
|
throw new Error("This instance is disabled")
|
|
@@ -237,47 +314,51 @@ export class Rio {
|
|
|
237
314
|
if (this.type !== "in")
|
|
238
315
|
throw new Error("Monitor is dedicated to 'in' instances")
|
|
239
316
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
edge: edge,
|
|
243
|
-
callback: callback
|
|
244
|
-
}
|
|
317
|
+
if (["stop", "rising", "falling", "both"].indexOf(edge) === -1)
|
|
318
|
+
throw new Error("Edge " + edge + "is not supported")
|
|
245
319
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
Rio.monitoringProcess.kill("SIGTERM")
|
|
249
|
-
Rio.monitoringProcess = null
|
|
250
|
-
}
|
|
320
|
+
if (typeof callback !== "function")
|
|
321
|
+
throw new Error("Callback is not a function")
|
|
251
322
|
|
|
252
|
-
//
|
|
253
|
-
if (
|
|
254
|
-
delete Rio.monitored[this.gpio]
|
|
255
|
-
log("gpio", this.gpio, "is no longer being monitored")
|
|
256
|
-
}
|
|
323
|
+
// First time the instance is monitored or has been stopped
|
|
324
|
+
if (this.watch === "stop") {
|
|
257
325
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (that.edge === "rising" || that.edge === "both")
|
|
269
|
-
that.callback("rising", new Date())
|
|
270
|
-
} else {
|
|
271
|
-
if (that.edge === "falling" || that.edge === "both")
|
|
272
|
-
that.callback("falling", new Date())
|
|
273
|
-
}
|
|
326
|
+
if (edge === "stop") {
|
|
327
|
+
trap("cannot stop an unstarted monitoring on gpio", this.gpio)
|
|
328
|
+
}
|
|
329
|
+
// Edge is in ["rising", "falling", "both"]
|
|
330
|
+
else {
|
|
331
|
+
this.watch = {
|
|
332
|
+
edge: edge,
|
|
333
|
+
enabled: edge === "both" ? ["rising", "falling"] : [edge],
|
|
334
|
+
callback: callback,
|
|
335
|
+
process: spawn("gpiomon", this.#monArgs([this.gpio], opt))
|
|
274
336
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
337
|
+
this.watch.process.stdout.on("data", this.#monCallback)
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Instance is already monitored
|
|
341
|
+
else {
|
|
342
|
+
// Stop: Kill process and delete monitored data
|
|
343
|
+
if (edge === "stop") {
|
|
344
|
+
this.watch.process !== null ? this.watch.process.kill("SIGTERM") : false
|
|
345
|
+
this.watch.edge = "stop"
|
|
346
|
+
this.watch.enabled = []
|
|
347
|
+
this.watch.callback = false
|
|
348
|
+
this.watch.process = null
|
|
349
|
+
Rio.instances.set(this.gpio, this)
|
|
350
|
+
log("monitoring of gpio", this.gpio, "has been stopped")
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Kill process, update edge and callback, define new process
|
|
354
|
+
else {
|
|
355
|
+
this.watch.process !== null ? this.watch.process.kill("SIGTERM") : false
|
|
356
|
+
this.watch.edge = edge
|
|
357
|
+
this.watch.enabled = edge === "both" ? ["rising", "falling"] : [edge]
|
|
358
|
+
this.watch.callback = callback
|
|
359
|
+
this.watch.process = spawn("gpiomon", this.#monArgs([this.gpio], opt))
|
|
360
|
+
this.watch.process.stdout.on("data", this.#monCallback)
|
|
361
|
+
}
|
|
281
362
|
}
|
|
282
363
|
}
|
|
283
364
|
|
|
@@ -321,9 +402,9 @@ export class Rio {
|
|
|
321
402
|
|
|
322
403
|
// Check duty cycle
|
|
323
404
|
if (duty < this.dutyMin)
|
|
324
|
-
throw new Error("PWM duty"
|
|
405
|
+
throw new Error("PWM duty " + duty + " is lower than duty min " + this.dutyMin)
|
|
325
406
|
if (duty > this.dutyMax)
|
|
326
|
-
throw new Error("PWM duty"
|
|
407
|
+
throw new Error("PWM duty" + duty + " is upper than duty max" + this.dutyMax)
|
|
327
408
|
|
|
328
409
|
log("channel", this.channel, "normalized PWM data (period, duty, duty min, duty max):", this.period, this.duty, this.dutyMin, this.dutyMax)
|
|
329
410
|
|
|
@@ -450,10 +531,15 @@ Rio.version = () => {
|
|
|
450
531
|
* @description Stop monitoring of all inputs
|
|
451
532
|
*/
|
|
452
533
|
Rio.stopMonitoring = () => {
|
|
453
|
-
Rio.
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
534
|
+
Rio.instances.forEach((that) => {
|
|
535
|
+
if (that.watch.process !== null) {
|
|
536
|
+
that.watch.process.kill("SIGTERM")
|
|
537
|
+
that.watch.edge = "stop"
|
|
538
|
+
that.watch.callback = false
|
|
539
|
+
that.watch.process = null
|
|
540
|
+
log("monitoring of gpio", that.gpio, "has been stopped")
|
|
541
|
+
}
|
|
542
|
+
})
|
|
457
543
|
}
|
|
458
544
|
|
|
459
545
|
/** ------------------------------------------------------------------
|
|
@@ -505,8 +591,6 @@ Rio.os = () => {
|
|
|
505
591
|
*/
|
|
506
592
|
Rio.isSystemSupported = () => {
|
|
507
593
|
|
|
508
|
-
const info = Rio.cpuInfo()
|
|
509
|
-
|
|
510
594
|
// Test hardware
|
|
511
595
|
if (/Raspberry Pi/i.test(Rio.cpuInfo()) === false) {
|
|
512
596
|
trap("not a Raspberry Pi hardware")
|
package/esm/utl.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// -------------------------------------------------------------------
|
|
2
2
|
// RPi GPIO CONTROL - UTILITIES
|
|
3
3
|
// -------------------------------------------------------------------
|
|
4
|
-
import {log,trap} from "./dev.mjs"
|
|
4
|
+
import {log, trap} from "./dev.mjs"
|
|
5
5
|
import {execSync, execFileSync, spawnSync} from "child_process"
|
|
6
6
|
|
|
7
7
|
// -------------------------------------------------------------------
|
|
@@ -64,7 +64,7 @@ export const exeShell = cmd => {
|
|
|
64
64
|
* @param {Array} args
|
|
65
65
|
* @return {String} stdout or ""
|
|
66
66
|
*/
|
|
67
|
-
export const exeFile = (file, args) => {
|
|
67
|
+
export const exeFile = (file, args = []) => {
|
|
68
68
|
try {
|
|
69
69
|
return execFileSync(file, args, {
|
|
70
70
|
stdio: "pipe",
|
package/history.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.2.0 - Nov 07, 2025
|
|
2
2
|
===================
|
|
3
|
-
|
|
3
|
+
* New option parameter for method `monitor` to take care of bias and bounces.
|
|
4
|
+
* Fixes and optimization
|
|
4
5
|
|
|
5
|
-
1.1.0 - Oct 31 2025
|
|
6
|
+
1.1.0 - Oct 31, 2025
|
|
6
7
|
===================
|
|
7
8
|
* Added support libgpiod 2.2.1
|
|
8
|
-
* Tested with *Trixie*
|
|
9
|
+
* Tested with *Trixie*
|
|
10
|
+
|
|
11
|
+
1.0.0 - Oct 23, 2025
|
|
12
|
+
===================
|
|
13
|
+
Initial release: *Bookworm*, libgpiod 1.63
|
package/package.json
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rpi-io",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Nodejs module to control
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Nodejs module to control Raspberry Pi GPIO",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"raspberry",
|
|
7
7
|
"gpio",
|
|
8
|
+
"libgpiod",
|
|
8
9
|
"nodejs",
|
|
9
10
|
"esm",
|
|
10
|
-
"
|
|
11
|
+
"output",
|
|
12
|
+
"input",
|
|
13
|
+
"monitor",
|
|
14
|
+
"pwm",
|
|
15
|
+
"bookworm",
|
|
16
|
+
"trixie"
|
|
11
17
|
],
|
|
12
18
|
"homepage": "https://github.com/gdorbes/rpi-io#readme",
|
|
13
19
|
"bugs": {
|
package/scr/test.js
CHANGED
|
@@ -84,19 +84,25 @@ function isNumber(str) {
|
|
|
84
84
|
if (gpio > 0) {
|
|
85
85
|
const btn = new Rio(gpio, "in")
|
|
86
86
|
btn.get()
|
|
87
|
-
/* btn.monitor("both", (event, time) => {
|
|
88
|
-
log("event", event)
|
|
89
|
-
}) */
|
|
90
87
|
}
|
|
91
88
|
break
|
|
92
89
|
case "monitor":
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
90
|
+
const gpio1 = isNumber(process.argv[3])
|
|
91
|
+
const gpio2 = isNumber(process.argv[4])
|
|
92
|
+
let btn1, btn2
|
|
93
|
+
if (gpio1 > 0) {
|
|
94
|
+
btn1 = new Rio(gpio1, "in")
|
|
95
|
+
btn1.monitor("rising")
|
|
99
96
|
}
|
|
97
|
+
if (gpio2 > 0) {
|
|
98
|
+
btn2 = new Rio(gpio2, "in")
|
|
99
|
+
btn2.monitor("falling", () => {
|
|
100
|
+
log(gpio2, "falling")
|
|
101
|
+
}, {bias: "pull-down"})
|
|
102
|
+
}
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
btn1.monitor("stop")
|
|
105
|
+
}, 6000)
|
|
100
106
|
break
|
|
101
107
|
case "benchmark":
|
|
102
108
|
const gpioOut = isNumber(process.argv[3])
|
|
@@ -138,7 +144,7 @@ function isNumber(str) {
|
|
|
138
144
|
setTimeout(() => {
|
|
139
145
|
servo.pwmDuty(1500000)
|
|
140
146
|
log("servo position = 90°")
|
|
141
|
-
},2000)
|
|
147
|
+
}, 2000)
|
|
142
148
|
|
|
143
149
|
// after 4s -> duty=1.5 ms
|
|
144
150
|
setTimeout(() => {
|