rpio-define
Version:
Modern style define GPIO for RaspberryPi.
298 lines (290 loc) • 11.9 kB
JavaScript
import rpio from 'rpio';
export { I2CDevice } from './i2c';
function definePort(type, modifier) {
return function (option) {
option.type = type;
if (typeof modifier == 'function')
modifier(option);
return option;
};
}
export const DigitalOutput = definePort(Boolean);
export const DigitalInput = definePort(Boolean, (opt) => {
if (typeof opt.mode == 'undefined')
opt.mode = 'input';
});
export const Servo = definePort('servo');
export const PWM = definePort('pwm');
/** use example
```javascript
import rpio from 'rpio'
import { defineIO, DigitalOutput, Servo } from 'rpio-define'
rpio.init({ mapping: 'gpio' });
const io = defineIO({
led: DigitalOutput({
pin: 16
}),
motor: Servo({
pin: 12,
})
});
io.led = true; // LED on!
setInterval(() => {
io.led = !io.led; // LED blinking
}, 1000);
```
*/
export function defineIO(descriptors) {
const io = {};
for (const [_key, descriptor] of Object.entries(descriptors)) {
const key = _key;
const type = typeof descriptor.type == 'string'
? descriptor.type.toLowerCase()
: descriptor.type && 'name' in descriptor.type && typeof descriptor.type.name == 'string'
? descriptor.type.name.toLowerCase()
: '';
const isDigitalPortDescriptor = (descriptor) => descriptor.type == Boolean || type == 'digital';
const isServoPortDescriptor = (descriptor) => type == 'servo';
const isPWMPortDescriptor = (descriptor) => type == 'pwm';
if (isDigitalPortDescriptor(descriptor)) {
if (typeof descriptor.mode == 'string') {
const mode = descriptor.mode.toLowerCase();
if (mode == 'output')
descriptor.mode = rpio.OUTPUT;
else if (mode == 'input')
descriptor.mode = rpio.INPUT;
}
if (descriptor.mode == undefined)
descriptor.mode = rpio.OUTPUT;
const isDigitalPortOutputDescriptor = (descriptor) => descriptor.mode == rpio.OUTPUT || descriptor.mode == 'outputopendrain';
const isDigitalPortInputDescriptor = (descriptor) => descriptor.mode == rpio.INPUT || descriptor.mode == 'inputpullup';
if (isDigitalPortOutputDescriptor(descriptor)) {
let value = descriptor.default || false;
if (descriptor.mode == rpio.OUTPUT) {
rpio.mode(descriptor.pin, descriptor.mode, value ? rpio.HIGH : rpio.LOW);
}
else {
rpio.mode(descriptor.pin, rpio.INPUT, rpio.LOW);
if (descriptor.default) {
rpio.mode(descriptor.pin, rpio.OUTPUT, rpio.LOW);
}
}
Object.defineProperty(io, key, {
get() {
return value;
},
set(val) {
value = val;
if (descriptor.mode == rpio.OUTPUT) {
rpio.write(descriptor.pin, value ? rpio.HIGH : rpio.LOW);
}
else if (descriptor.mode == 'outputopendrain') {
rpio.mode(descriptor.pin, value ? rpio.OUTPUT : rpio.INPUT);
}
},
enumerable: true,
});
}
else if (isDigitalPortInputDescriptor(descriptor)) {
if (typeof descriptor.callback == 'function') {
const opt = {
pin: descriptor.pin,
mode: descriptor.mode,
edge: descriptor.edge,
};
if (typeof opt.edge == 'string') {
if (opt.edge == 'rising')
opt.edge = rpio.POLL_HIGH;
else if (opt.edge == 'falling')
opt.edge = rpio.POLL_LOW;
else if (opt.edge == 'both')
opt.edge = rpio.POLL_BOTH;
}
if (typeof opt.mode == 'undefined')
// @ts-ignore
delete opt.mode;
if (typeof opt.edge == 'undefined')
delete opt.edge;
rpio.poll(descriptor.pin, descriptor.callback, descriptor.edge !== undefined ? opt.edge : rpio.POLL_BOTH);
Object.defineProperty(io, key, {
get() {
const value = !!rpio.read(descriptor.pin);
return descriptor.mode == rpio.PULL_UP ? !value : value;
},
enumerable: true,
});
}
else {
rpio.mode(descriptor.pin, rpio.INPUT, descriptor.mode == 'inputpullup' ? rpio.PULL_UP : rpio.PULL_OFF);
Object.defineProperty(io, key, {
get() {
const value = !!rpio.read(descriptor.pin);
return descriptor.mode == rpio.PULL_UP ? !value : value;
},
enumerable: true,
});
}
}
}
/* RaspberryPiにはDAC/ADCは標準搭載されていないらしい・・・。残念。
else if ((descriptor.type == Analog || descriptor.type == Number || type == 'analog')) {
// [Analog] https://github.com/Moddable-OpenSource/moddable/blob/public/documentation/pins/pins.md#analog
const scale = typeof descriptor.scale == 'number' ? descriptor.scale : 1
// ESP32の場合のDigital -> Analog ピン番号読み替え
// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-gpio.c#L27
const analogPin = descriptor.pin > 7 ? {
36: 0,
37: 1,
38: 2,
39: 3,
32: 4,
33: 5,
34: 6,
35: 7,
//8
//9
//4: 10,
//0: 11,
//2: 12,
15: 13,
13: 14,
12: 15,
14: 16,
27: 17,
25: 18,
26: 19
}[descriptor.pin] : descriptor.pin
const port = new Digital(descriptor.pin, Digital.Input)
// 44や971は実測の最大・最小値 いったんこの値でキャリブレーション
let min = 44
let max = 971
Object.defineProperty(io, key, {
get() {
const raw = Analog.read(analogPin)
if (raw > max) max = raw
if (raw < min) min = raw
const value = Math.min(Math.max(raw - min, 0) / (max - min), 1)
return value * scale
},
enumerable: true,
})
}
*/
else if (isServoPortDescriptor(descriptor)) {
let value = descriptor.default || 0;
const option = {
pin: descriptor.pin,
min: descriptor.min,
max: descriptor.max
};
let minAngle = 0;
let maxAngle = 180;
let offsetAngle = 0;
if (typeof option.min == 'undefined')
option.min = 500;
if (typeof option.min == 'object') {
if (typeof option.min.angle == 'number')
minAngle = option.min.angle;
option.min = option.min.pulse;
}
if (typeof option.max == 'undefined')
option.max = 2400;
if (typeof option.max == 'object') {
if (typeof option.max.angle == 'number')
maxAngle = option.max.angle;
option.max = option.max.pulse;
}
if (typeof descriptor.offset == 'number') {
offsetAngle = descriptor.offset;
}
const scale = (option.max - option.min) / (maxAngle - minAngle);
let minPulse = option.min;
if (option.min === undefined)
delete option.min;
if (option.max === undefined)
delete option.max;
rpio.open(descriptor.pin, rpio.PWM);
rpio.pwmSetClockDivider(128); // 1.92MHz / 128 = 150kHz
rpio.pwmSetRange(descriptor.pin, 3000); // 150kHz / 3000 = 50Hz = 20ms
const rate = 20000 / 3000; // (20ms = 20,000us) / 3000 = 6.6us
Object.defineProperty(io, key, {
get() {
return value;
},
set(val) {
value = val;
rpio.pwmSetData(descriptor.pin, ((((val - minAngle + offsetAngle) * scale) + minPulse) / rate) | 0);
},
enumerable: true,
});
// @ts-ignore
io[key] = value;
}
/*
else if (type =='tone' || type == 'pulse') {
const option = {
pin: descriptor.pin
}
Object.defineProperty(io, key, {
value(frequency, duration) {
option.freq = frequency
const port = new Tone(option)
port.write(frequency)
if (duration) {
Timer.delay(duration)
port.close()
} else {
return () => port.close()
}
}
})
}
*/
else if (isPWMPortDescriptor(descriptor)) {
let value = descriptor.default || 0;
const opt = {
pin: descriptor.pin,
hz: 300000
};
const max = descriptor.max || 1;
if (typeof descriptor.hz == 'number')
opt.hz = descriptor.hz;
rpio.open(opt.pin, rpio.PWM);
rpio.pwmSetRange(opt.pin, 1023);
rpio.pwmSetClockDivider(Math.floor((19.2 * 1000000) / opt.hz));
Object.defineProperty(io, key, {
get() {
return value;
},
set(val) {
value = val;
if (max > 1) {
val /= max;
}
if (val <= 1)
val *= 1023;
rpio.pwmSetData(opt.pin, val);
},
enumerable: true,
});
// @ts-ignore
io[key] = value;
}
else if ('get' in descriptor) {
Object.defineProperty(io, key, {
get: descriptor.get,
set: descriptor.set,
enumerable: true,
});
}
else if ('value' in descriptor) {
Object.defineProperty(io, key, {
value: descriptor.value,
enumerable: true,
});
}
}
return io;
}
Object.freeze(defineIO);
export default defineIO;