rpi-io 1.1.5 → 1.2.1
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 +16 -9
- package/esm/dev.mjs +6 -6
- package/esm/rio.mjs +182 -94
- package/esm/utl.mjs +2 -2
- package/history.md +13 -4
- package/package.json +4 -1
- package/scr/test.js +19 -13
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.
|
|
@@ -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
|
|
|
@@ -403,7 +406,7 @@ console.log(Rio.os())
|
|
|
403
406
|
|
|
404
407
|
To check if device is supported by **rpi-io** (device, model, os, middleware). It returns *true/false* and displays details in console.
|
|
405
408
|
|
|
406
|
-
#### Rio.
|
|
409
|
+
#### Rio.logCfg(level)
|
|
407
410
|
To set the level of console used in the module:
|
|
408
411
|
|
|
409
412
|
- 0: no console
|
|
@@ -414,9 +417,9 @@ To set the level of console used in the module:
|
|
|
414
417
|
|
|
415
418
|
```javascript
|
|
416
419
|
import {Rio} from "rpi-io";
|
|
417
|
-
Rio.
|
|
420
|
+
Rio.logCfg(0)
|
|
418
421
|
Rio.isSystemSupported() // Nothing displayed in console
|
|
419
|
-
Rio.
|
|
422
|
+
Rio.logCfg(2)
|
|
420
423
|
Rio.isSystemSupported() // See console below
|
|
421
424
|
// 08:15:16.506 🔎 👍 Hardware is Raspberry Pi
|
|
422
425
|
// 08:15:16.539 🔎 👍 Raspberry Pi 5 Model B Rev 1.0 is supported
|
|
@@ -430,9 +433,13 @@ Rio.isSystemSupported() // See console below
|
|
|
430
433
|
|
|
431
434
|
The following table summarizes GPIO *set* average times with rpi-io v1.1.0 and various hardware and OS.
|
|
432
435
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
|
436
|
-
|
|
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
|
+
|
|
443
|
+
|
|
437
444
|
---
|
|
438
445
|
|
package/esm/dev.mjs
CHANGED
|
@@ -9,11 +9,11 @@ let debug = {
|
|
|
9
9
|
// -------------------------------------------------------------------
|
|
10
10
|
// FUNCTIONS
|
|
11
11
|
/** ------------------------------------------------------------------
|
|
12
|
-
* @function
|
|
12
|
+
* @function nowStr
|
|
13
13
|
* @description Return timestamp as formatted string
|
|
14
14
|
* @return {String}
|
|
15
15
|
*/
|
|
16
|
-
export const
|
|
16
|
+
export const nowStr = () => {
|
|
17
17
|
const date = new Date()
|
|
18
18
|
return date.toLocaleTimeString() + "." + date.getMilliseconds().toLocaleString('en', {
|
|
19
19
|
minimumIntegerDigits: 3,
|
|
@@ -29,7 +29,7 @@ export const now = () => {
|
|
|
29
29
|
*/
|
|
30
30
|
export const log = function () {
|
|
31
31
|
if (debug.log)
|
|
32
|
-
console.log.apply(console, Array.prototype.concat.apply([
|
|
32
|
+
console.log.apply(console, Array.prototype.concat.apply([nowStr(), "🔎 "], arguments))
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/** ------------------------------------------------------------------
|
|
@@ -38,15 +38,15 @@ export const log = function () {
|
|
|
38
38
|
*/
|
|
39
39
|
export const trap = function () {
|
|
40
40
|
if (debug.trap)
|
|
41
|
-
console.log.apply(console, Array.prototype.concat.apply([
|
|
41
|
+
console.log.apply(console, Array.prototype.concat.apply([nowStr(), "⚠️", "\x1b[38;2;255;80;0m", ...arguments, "\x1b[0m"]))
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/** ------------------------------------------------------------------
|
|
45
|
-
* @function
|
|
45
|
+
* @function logCfg
|
|
46
46
|
* @description Set log configuration
|
|
47
47
|
* @param {Number} level 0: no log, 1: trap only, 2: trap and log
|
|
48
48
|
*/
|
|
49
|
-
export const
|
|
49
|
+
export const logCfg = level => {
|
|
50
50
|
debug = {
|
|
51
51
|
log: false,
|
|
52
52
|
trap: false
|
package/esm/rio.mjs
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import {spawn} from "child_process";
|
|
7
7
|
import {writeFileSync, readFileSync} from "fs"
|
|
8
8
|
import {exeFile, exeShell, wait} from "./utl.mjs"
|
|
9
|
-
import {
|
|
9
|
+
import {logCfg, log, trap} from "./dev.mjs"
|
|
10
10
|
|
|
11
11
|
// -------------------------------------------------------------------
|
|
12
12
|
// CLASS RIO AND FURTHER
|
|
@@ -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")
|
|
@@ -544,10 +628,14 @@ Rio.isSystemSupported = () => {
|
|
|
544
628
|
log("👍 Systems requirements for rpi-io are met")
|
|
545
629
|
return true
|
|
546
630
|
}
|
|
631
|
+
/** ------------------------------------------------------------------
|
|
632
|
+
* @function Rio.logCfg
|
|
633
|
+
*/
|
|
634
|
+
Rio.logCfg = logCfg
|
|
547
635
|
/** ------------------------------------------------------------------
|
|
548
636
|
* @function Rio.log
|
|
549
637
|
*/
|
|
550
|
-
Rio.log =
|
|
638
|
+
Rio.log = log
|
|
551
639
|
// -------------------------------------------------------------------
|
|
552
640
|
// EoF
|
|
553
641
|
// -------------------------------------------------------------------
|
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,17 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.2.1 - Nov 10, 2025
|
|
2
2
|
===================
|
|
3
|
-
|
|
3
|
+
* Change utility API function name from `Rio.log` to `Rio.logCfg`.
|
|
4
4
|
|
|
5
|
-
1.
|
|
5
|
+
1.2.0 - Nov 07, 2025
|
|
6
|
+
===================
|
|
7
|
+
* New option parameter for method `monitor` to take care of bias and bounces.
|
|
8
|
+
* Fixes and optimization
|
|
9
|
+
|
|
10
|
+
1.1.0 - Oct 31, 2025
|
|
6
11
|
===================
|
|
7
12
|
* Added support libgpiod 2.2.1
|
|
8
|
-
* Tested with *Trixie*
|
|
13
|
+
* Tested with *Trixie*
|
|
14
|
+
|
|
15
|
+
1.0.0 - Oct 23, 2025
|
|
16
|
+
===================
|
|
17
|
+
Initial release: *Bookworm*, libgpiod 1.63
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rpi-io",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Nodejs module to control Raspberry Pi GPIO",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"raspberry",
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
"libgpiod",
|
|
9
9
|
"nodejs",
|
|
10
10
|
"esm",
|
|
11
|
+
"output",
|
|
12
|
+
"input",
|
|
13
|
+
"monitor",
|
|
11
14
|
"pwm",
|
|
12
15
|
"bookworm",
|
|
13
16
|
"trixie"
|
package/scr/test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// -------------------------------------------------------------------
|
|
2
2
|
// RPi GPIO CONTROL TEST SCRIPT
|
|
3
3
|
// -------------------------------------------------------------------
|
|
4
|
-
import {log, trap,
|
|
4
|
+
import {log, trap, nowStr} from "../esm/dev.mjs"
|
|
5
5
|
import {ctrlC} from "../esm/utl.mjs"
|
|
6
6
|
import {Rio} from "../esm/rio.mjs"
|
|
7
7
|
|
|
@@ -12,7 +12,7 @@ import {Rio} from "../esm/rio.mjs"
|
|
|
12
12
|
* @param {String} txt
|
|
13
13
|
*/
|
|
14
14
|
function info(cmd, txt) {
|
|
15
|
-
console.log(
|
|
15
|
+
console.log(nowStr(), "🔎", "\x1b[38;2;0;160;16m", cmd, "\x1b[0m", txt)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/** ------------------------------------------------------------------
|
|
@@ -35,7 +35,7 @@ function isNumber(str) {
|
|
|
35
35
|
// TOP LEVEL TEST
|
|
36
36
|
// -------------------------------------------------------------------
|
|
37
37
|
(function () {
|
|
38
|
-
Rio.
|
|
38
|
+
Rio.logCfg(2)
|
|
39
39
|
ctrlC(Rio.stopMonitoring)
|
|
40
40
|
|
|
41
41
|
if (process.argv.length < 3) {
|
|
@@ -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(() => {
|