node-red-contrib-pwm 1.0.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 +33 -0
- package/locales/de/pwm.json +14 -0
- package/locales/en/pwm.json +14 -0
- package/locales/es/pwm.json +14 -0
- package/package.json +44 -0
- package/pwm.html +60 -0
- package/pwm.js +95 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# node-red-contrib-rpo-pwm
|
|
2
|
+
|
|
3
|
+
A Node-RED node: **PWM**
|
|
4
|
+
|
|
5
|
+
Converts a duty cycle (0–100 %) into a PWM signal by toggling a 0/1 output at the configured frequency.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install node-red-contrib-rpo-pwm
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or via the Node-RED Palette Manager.
|
|
14
|
+
|
|
15
|
+
## Inputs
|
|
16
|
+
|
|
17
|
+
- **payload** (number): Duty cycle 0–100 %
|
|
18
|
+
- **msg.bypass** (boolean): If true, pass raw value through
|
|
19
|
+
|
|
20
|
+
## Outputs
|
|
21
|
+
|
|
22
|
+
- **Output 1**: 1 (ON) or 0 (OFF) according to PWM cycle
|
|
23
|
+
|
|
24
|
+
## Configuration
|
|
25
|
+
|
|
26
|
+
| Parameter | Description |
|
|
27
|
+
|-----------|-------------|
|
|
28
|
+
| Frequency (Hz) | PWM switching frequency |
|
|
29
|
+
| Min ON time (ms) | Minimum pulse width to avoid very short pulses |
|
|
30
|
+
|
|
31
|
+
## License
|
|
32
|
+
|
|
33
|
+
MIT
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pwm": {
|
|
3
|
+
"label": "PWM",
|
|
4
|
+
"frequency": "Frequenz (Hz)",
|
|
5
|
+
"minOnTime": "Min. Einschaltdauer (ms)",
|
|
6
|
+
"tip": "Payload 0–100 setzt den Tastgrad. msg.bypass=true leitet Rohwert weiter.",
|
|
7
|
+
"input": {
|
|
8
|
+
"payload": "Tastgrad 0–100 % (Zahl)"
|
|
9
|
+
},
|
|
10
|
+
"output": {
|
|
11
|
+
"payload": "1 (EIN) oder 0 (AUS) entsprechend dem PWM-Zyklus"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pwm": {
|
|
3
|
+
"label": "PWM",
|
|
4
|
+
"frequency": "Frequency (Hz)",
|
|
5
|
+
"minOnTime": "Min ON time (ms)",
|
|
6
|
+
"tip": "Payload 0–100 sets duty cycle. msg.bypass=true outputs raw value.",
|
|
7
|
+
"input": {
|
|
8
|
+
"payload": "Duty cycle 0–100 % (number)"
|
|
9
|
+
},
|
|
10
|
+
"output": {
|
|
11
|
+
"payload": "1 (ON) or 0 (OFF) according to PWM cycle"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pwm": {
|
|
3
|
+
"label": "PWM",
|
|
4
|
+
"frequency": "Frecuencia (Hz)",
|
|
5
|
+
"minOnTime": "Tiempo mín. ON (ms)",
|
|
6
|
+
"tip": "Payload 0–100 establece el ciclo de trabajo. msg.bypass=true pasa el valor directo.",
|
|
7
|
+
"input": {
|
|
8
|
+
"payload": "Ciclo de trabajo 0–100 % (número)"
|
|
9
|
+
},
|
|
10
|
+
"output": {
|
|
11
|
+
"payload": "1 (ON) o 0 (OFF) según ciclo PWM"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-red-contrib-pwm",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "PWM generator - converts 0-100% analog value to digital pulse-width signal",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/Wobi848/node-red-contrib-rpo-suite.git"
|
|
8
|
+
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/Wobi848/node-red-contrib-rpo-suite/issues"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"pwm.js",
|
|
14
|
+
"pwm.html",
|
|
15
|
+
"locales",
|
|
16
|
+
"examples"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"node-red",
|
|
20
|
+
"pwm",
|
|
21
|
+
"pulse",
|
|
22
|
+
"duty-cycle",
|
|
23
|
+
"automation"
|
|
24
|
+
],
|
|
25
|
+
"author": "sr.rpo",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"node-red": {
|
|
28
|
+
"version": "1.0.0",
|
|
29
|
+
"nodes": {
|
|
30
|
+
"pwm": "pwm.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=14.0.0"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"test": "mocha \"test/**/*_spec.js\" --exit"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"mocha": "^10.2.0",
|
|
41
|
+
"node-red": "^3.1.0",
|
|
42
|
+
"node-red-node-test-helper": "^0.3.6"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/pwm.html
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('pwm', {
|
|
3
|
+
category: 'rpo',
|
|
4
|
+
color: '#D4600A',
|
|
5
|
+
defaults: {
|
|
6
|
+
"name": {
|
|
7
|
+
"value": ""
|
|
8
|
+
},
|
|
9
|
+
"period": {
|
|
10
|
+
"value": 10
|
|
11
|
+
},
|
|
12
|
+
"periodUnit": {
|
|
13
|
+
"value": "s"
|
|
14
|
+
},
|
|
15
|
+
"invert": {
|
|
16
|
+
"value": false
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
inputs: 1,
|
|
20
|
+
outputs: 1,
|
|
21
|
+
icon: 'font-awesome/fa-signal',
|
|
22
|
+
label: function() { return this.name || 'pwm'; },
|
|
23
|
+
labelStyle: function() { return this.name ? 'node_label_italic' : ''; }
|
|
24
|
+
});
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<script type="text/html" data-template-name="pwm">
|
|
28
|
+
<div class="form-row">
|
|
29
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
30
|
+
<input type="text" id="node-input-name" placeholder="Name">
|
|
31
|
+
</div>
|
|
32
|
+
<div class="form-row">
|
|
33
|
+
<label for="node-input-period"><i class="fa fa-clock-o"></i> Period</label>
|
|
34
|
+
<input type="number" id="node-input-period" min="1" style="width:50%">
|
|
35
|
+
<select id="node-input-periodUnit" style="width:18%; margin-left:4px">
|
|
36
|
+
<option value="ms">ms</option>
|
|
37
|
+
<option value="s">s</option>
|
|
38
|
+
<option value="min">min</option>
|
|
39
|
+
</select>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="form-row">
|
|
42
|
+
<label for="node-input-invert"><i class="fa fa-exchange"></i> Invert</label>
|
|
43
|
+
<input type="checkbox" id="node-input-invert" style="width:auto;margin-top:4px">
|
|
44
|
+
</div>
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<script type="text/html" data-help-name="pwm">
|
|
48
|
+
<p>PWM generator: converts a 0–100% analog value to a digital pulse-width modulation signal.</p>
|
|
49
|
+
<h3>Input</h3>
|
|
50
|
+
<dl class="message-properties">
|
|
51
|
+
<dt>payload <span class="property-type">number</span></dt><dd>Duty cycle 0–100%</dd>
|
|
52
|
+
<dt class="optional">bypass <span class="property-type">boolean</span></dt><dd>Force ON/OFF</dd>
|
|
53
|
+
<dt class="optional">clearBypass <span class="property-type">boolean</span></dt><dd>Return to PWM</dd>
|
|
54
|
+
</dl>
|
|
55
|
+
<h3>Output</h3>
|
|
56
|
+
<dl class="message-properties">
|
|
57
|
+
<dt>payload <span class="property-type">boolean</span></dt><dd>PWM state (fires on state change only)</dd>
|
|
58
|
+
<dt>pwm <span class="property-type">object</span></dt><dd>{ input, output, dutyOn, dutyOff, period, bypass, inverted }</dd>
|
|
59
|
+
</dl>
|
|
60
|
+
</script>
|
package/pwm.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
module.exports = function(RED) {
|
|
2
|
+
function PWMNode(config) {
|
|
3
|
+
RED.nodes.createNode(this, config);
|
|
4
|
+
var node = this;
|
|
5
|
+
|
|
6
|
+
var pRaw = parseFloat(config.period) || 10;
|
|
7
|
+
var pUnit = config.periodUnit || 's';
|
|
8
|
+
node.period = pUnit === 'min' ? pRaw * 60000 : pUnit === 'ms' ? pRaw : pRaw * 1000;
|
|
9
|
+
node.invert = config.invert === true;
|
|
10
|
+
node.input = 0;
|
|
11
|
+
node.bypass = null;
|
|
12
|
+
node.output = false;
|
|
13
|
+
node.timer = null;
|
|
14
|
+
|
|
15
|
+
node.status({ fill:'grey', shape:'dot', text:'OFF 0%' });
|
|
16
|
+
scheduleCycle(node);
|
|
17
|
+
|
|
18
|
+
node.on('input', function(msg) {
|
|
19
|
+
if (msg.clearBypass === true) { node.bypass = null; return; }
|
|
20
|
+
if (msg.bypass !== undefined) {
|
|
21
|
+
node.bypass = (msg.bypass === true || msg.bypass === 'true') ? true : false;
|
|
22
|
+
updateStatus(node);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
var val = parseFloat(msg.payload);
|
|
26
|
+
if (isNaN(val)) { node.warn('Input is not a number: ' + msg.payload); return; }
|
|
27
|
+
node.input = Math.max(0, Math.min(100, val));
|
|
28
|
+
if (node.timer) clearTimeout(node.timer);
|
|
29
|
+
scheduleCycle(node);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
node.on('close', function() {
|
|
33
|
+
if (node.timer) clearTimeout(node.timer);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
function scheduleCycle(n) {
|
|
37
|
+
var pct = n.input / 100;
|
|
38
|
+
var onTime = pct * n.period;
|
|
39
|
+
var offTime = n.period - onTime;
|
|
40
|
+
var bypass = n.bypass;
|
|
41
|
+
|
|
42
|
+
if (bypass !== null) {
|
|
43
|
+
setOutput(n, bypass, onTime, offTime);
|
|
44
|
+
n.timer = setTimeout(function() { scheduleCycle(n); }, n.period);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (pct <= 0) {
|
|
49
|
+
setOutput(n, false, onTime, offTime);
|
|
50
|
+
n.timer = setTimeout(function() { scheduleCycle(n); }, n.period);
|
|
51
|
+
} else if (pct >= 1) {
|
|
52
|
+
setOutput(n, true, onTime, offTime);
|
|
53
|
+
n.timer = setTimeout(function() { scheduleCycle(n); }, n.period);
|
|
54
|
+
} else {
|
|
55
|
+
setOutput(n, true, onTime, offTime);
|
|
56
|
+
n.timer = setTimeout(function() {
|
|
57
|
+
setOutput(n, false, onTime, offTime);
|
|
58
|
+
n.timer = setTimeout(function() { scheduleCycle(n); }, offTime);
|
|
59
|
+
}, onTime);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function setOutput(n, rawState, onTime, offTime) {
|
|
64
|
+
var state = n.invert ? !rawState : rawState;
|
|
65
|
+
if (state === n.output) return;
|
|
66
|
+
n.output = state;
|
|
67
|
+
var msg = {
|
|
68
|
+
payload: state ? 1 : 0,
|
|
69
|
+
pwm: {
|
|
70
|
+
input: n.input,
|
|
71
|
+
output: state,
|
|
72
|
+
dutyOn: Math.round(onTime),
|
|
73
|
+
dutyOff: Math.round(offTime),
|
|
74
|
+
period: n.period,
|
|
75
|
+
bypass: n.bypass !== null ? n.bypass : false,
|
|
76
|
+
inverted: n.invert
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
updateStatus(n);
|
|
80
|
+
n.send(msg);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function updateStatus(n) {
|
|
84
|
+
if (n.bypass !== null) {
|
|
85
|
+
n.status({ fill:'yellow', shape:'dot', text:'BYPASS ' + (n.bypass ? 'ON' : 'OFF') });
|
|
86
|
+
} else if (n.output) {
|
|
87
|
+
n.status({ fill:'green', shape:'dot', text:'ON ' + n.input + '%' });
|
|
88
|
+
} else {
|
|
89
|
+
n.status({ fill:'grey', shape:'dot', text:'OFF ' + n.input + '%' });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
RED.nodes.registerType('pwm', PWMNode);
|
|
95
|
+
};
|