Node.JS Библиотека предоставляющая доступ работы с GPIO для платформ Raspberry Pi или BeagleBone, а так же разработку и тестирование на компьютерах без GPIO при помощи эмуляции работы библиотеки onoff
gpio-controller является надстройкой над библиотекой onoff расширяющей её функционал:
- объединение пинов в группы
- создание обработчиков событий для групп
- групповое изменение направления ввода/вывода, фронтов прерывания, инверсии GPIO
- эмуляция работы библиотеки для тестирования(mock) функций на компьютерах без GPIO
Цель написания данной библиотеки состояла в том, чтоб немного облегчить разрабтку предоставив функционал для работы с группой пинов. Если Вы обнаружили ошибку, или возникли вопросы - пишите, буду стараться как можно быстрее ответить или исправить ошибку. Хорошего настроения и замечательного дня!
ОС: Linux Node.js versions 12+ English version of documentation coming soon
- Максимальный суммарный ток обоих выводов 3,3В равен 50 мА, напрямую можно подключать устройства только с током потребления меньше 50 мА.
- Максимальный суммарный ток обоих выводов 5В равен 300 мА.
- На выводы GPIO нельзя подавать напряжение выше 3,3В - можно спалить вход или весь процессор.
- Выводы 14 и 15 по умолчанию выполняют альтернативную функцию и являются выводами UART (RXD и TXD), поэтому при включении на них присутствует высокий уровень 3,3В
- Все настраиваемые выводы GPIO (кроме SDA и SCL) по умолчанию являются входами и поэтому имеют высокое сопротивление
- Расположение нумерации GPIO отличается от физической нумерации на сокете
- "out" / "high" / "low" - устанавливает GPIO на выход, если необходимо подать напряжение на пин, например, для светодиода или управления каким-либо устройством: 1 - пин включен и напряжение подается, 0 - пин выключен и напряжени не подается. «high» и «low» — это варианты «out», которые настраивают GPIO как выход с начальным уровнем. 1 или 0 соответственно.
- "in" - устанавливает GPIO на вход c начальным уровнем 1
За основу работы с GPIO была взята библиотка onoff. GpioController дает возможность работать с отдельными пинами, как инстансом класса Gpio данной библиотеки. Библиотека хорошо документирована, предлагаем Вам ознакомиться с документацией по её использованию.
npm i gpio-controller
Typescript:
import { GpioController } from 'gpio-controller';
const mock = GpioController.GPIOACCESS ? false : true;
const gpio = new GpioController({ mock });
Javascript:
const { GpioController } = require('gpio-controller');
const mock = GpioController.GPIOACCESS ? false : true;
const gpio = new GpioController({ mock });
-
GpioController.GPIOACCESS - Определяет, возможен ли доступ к GPIO. Значение true, если текущий процесс имеет разрешения, необходимые для экспорта GPIO в пользовательское пространство. ложно в противном случае. Грубо говоря, если это свойство истинно, текущий процесс должен иметь возможность создавать объекты Gpio.
-
mock - определяет необходимость использования библиотеки onoff или GpioMock. Если разработка ведется на платформе не имеющей GPIO, mock:true позволяет тестировать функционал эмулируя работы библиотеки onoff и настраивать необходимое поведение.
Подключим светодиод на GPIO 23 по схеме(вы можете использовать другую схему подключения или другие устройства, например мультиметр, для проверки подачи напряжения на пин): Сделаем так, чтоб светодиод засветился
import { GpioController } from 'gpio-controller';
const gpio = new GpioController();
const PIN = 14
gpio.setPIN({ gpio: PIN, direction: 'out' });
gpio.switchOn(PIN);
Метод switchOn - изменяет состояние пина подавая на него напряжение. Обратный метод switchOff - заставит погаснуть горящий светодиод
gpio.switchOff(PIN);
А теперь поморгаем несколько раз светодиодом c интервалом в секунду
import { GpioController } from 'gpio-controller';
const gpio = new GpioController();
const PIN = 14
gpio.setPIN({ gpio: PIN, direction: 'out' });
let count = 0;
const interval = setInterval(() => {
count++;
gpio.toggle(PIN);
if (count >= 4) {
clearInterval(interval);
gpio.deletePIN(PIN);
}
}, 1000);
как видите - все достаточно легко. Просто используем метод toggle для изменения состояния пина.
Для опроса пинов, работ с кнопками или другими устройствами подающими напряжение на контроллируемый пин, необходимо установить направление(Direction) в 'in'. Сделаем пример, в котором установим кнопку на GPIO 4, на GPIO 24 установим светодиод и будем включать/выключать светодиод на нажатие кнопки
import { GpioController } from 'gpio-controller';
import { ValueCallback } from 'gpio-controller/gpio/gpio.interface';
const gpio = new GpioController();
const LED = 24;
const BUTTON = 4;
gpio.setPIN({ gpio: LED, direction: 'out' });
gpio.setPIN({ gpio: BUTTON, direction: 'in', edge: 'falling' });
const callback: ValueCallback = (err, value) => {
if (!err) {
gpio.toggle(LED);
}
};
gpio.watchPIN(callback, BUTTON);
при нажатии кнопки происходит вызов callback-функции в которой при помощи toggle меняется состояние пина со светодиодом.
Для чтения и изменения состояния используются методы
// Чтение состояния, возврат Promise 0 или 1
gpio.readSignal(pin: number): Promise<BinaryValue>;
// Изменение состояния, указываем номер пина и необходимое состояние 1 или 0
// Возврат true - состяние установлено / false - ошибка установки состояния
writeSignal(pinNumber: number, signal: BinaryValue): Promise<boolean>
Для примера реализуем функционал toggle из предидущего примера при помощи этих двух методов
import { GpioController } from 'gpio-controller';
import { BinaryValue, ValueCallback } from 'gpio-controller/gpio/gpio.interface';
const gpio = new GpioController();
const LED = 24;
const BUTTON = 4;
gpio.setPIN({ gpio: LED, direction: 'out' });
gpio.setPIN({ gpio: BUTTON, direction: 'in', edge: 'falling' });
const callback: ValueCallback = async (err, value) => {
if (!err) {
const pinValue = await gpio.readSignal(LED) ^ 1;
await gpio.writeSignal(LED, pinValue as BinaryValue);
}
};
gpio.watchPIN(callback, BUTTON);
Direction: out GpioController позволяет объединять пины в группы и работать с массивом пинов изменяя их состояние вызовом одного метода, автоматически инициализировать группу пинов, формировать несколько групп пинов, вешать слушателей на группы с direction 'in'. Если необходимо одновременно менять состояние нескольких пинов, это можно сделать при помощи switchGroupOn Допустим на пинах: 10, 12, 13, 14, 16, 19 - установлены светодиоды. Давайте создадим группу их этих пинов, и затем изменим состояние этих пинов
import { GpioController } from 'gpio-controller';
import { GroupGpio } from 'gpio-controller/gpio/gpio.interface';
const gpio = new GpioController();
// Номера пинов для инициализации
const LEDS = [10, 12, 13, 14, 16, 19];
// ID группы
const groupName = 'ledgroup';
// объект описывающий группу
const ledGroup: GroupGpio = {
groupid: groupName,
direction: 'out',
gpio: LEDS
}
// создадим группу
gpio.addGroup(ledGroup);
// изменим состояние всех пинов группы
gpio.switchGroupOn(groupName);
Вот и все, инициализированно 6 пинов и изменено из состояние, диоды светятся :) точно так же просто можно и "поморгать" этими диодами
import { Blink, GpioController } from 'gpio-controller';
import { GroupGpio } from 'gpio-controller/gpio/gpio.interface';
const gpio = new GpioController();
// Номера пинов для инициализации
const LEDS = [10, 12, 13, 14, 16, 19];
// ID группы
const groupName = 'ledgroup';
// объект описывающий группу
const ledGroup: GroupGpio = {
groupid: groupName,
direction: 'out',
gpio: LEDS
}
// создадим группу
gpio.addGroup(ledGroup);
// моргаем :)
const blink = new Blink(gpio);
blink.setGroup(groupName);
blink.start(1000, 10);
При инициализации GPIO direction: in необходимо учитывать фронт прерывания. edge - параметр, указывающий фронт или фронты генерации прерывания для входного GPIO. Допустимые значения: «none», «rising», «falling» или «both». Независимо от того, поддерживаются ли прерывания входным GPIO, зависит от GPIO. Если прерывания не поддерживаются, свойство edge и метод setGroupEdge использовать не следует.:warning:Прерывания не поддерживаются выходными GPIO❌ Пример создания группы кнопок:
const buttonGroup: GroupGpio = {
groupid: 'buttons',
direction: 'in',
edge: 'both',
gpio: [2, 3, 4],
}
Для примера создадим две группы:
- Группа с пинами для светодиодов [10, 12, 13, 14, 16, 19]
- Группа кнопок из двух штук [4, 8]
- Создадим 1 обработчик событий группы кнопок, при нажатии на кнопку 2 будем зажигать группу светодиодов, при нажатии на кнопку три - гасить светодиоды
import { GpioController } from 'gpio-controller';
import { GroupGpio, PinValueCallback } from 'gpio-controller/gpio/gpio.interface';
const gpio = new GpioController();
const LEDS = [10, 12, 13, 14, 16, 19];
const BUTTONS = [4, 8];
const groupLeds = 'ledgroup';
const groupButtons = 'buttons';
const ledGroup: GroupGpio = {
groupid: groupLeds,
direction: 'out',
gpio: LEDS
}
const buttoGroup: GroupGpio = {
groupid: groupButtons,
direction: 'in',
edge: 'falling',
gpio: BUTTONS,
}
const callback: PinValueCallback = (err, value, pin, group) => {
if (pin == 4) gpio.switchGroupOn(groupLeds);
if (pin == 8) gpio.switchGroupOff(groupLeds);
}
gpio.addGroup(ledGroup);
gpio.addGroup(buttoGroup)
gpio.watchGroup(groupButtons, callback);
функция callback типа PinValueCallback принимает 4 параметра:
- err - ошибка new Error / undefined
- value - (0 / 1) При edge: both нажатая кнопка(falling) возвращает 0, отжатая(rising) кнопка 1
- pin? - номер пина от которого была вызвана функция
- group? - группа в которой сработал пин
при edge: both вызов функции происходит два раза возвращая 0 и 1
GpioMock позволяет работать с библиотекой на платформе не поддерживающей GPIO. Статическое свойство GPIOACCESS позволяет определить возможность работы с GPIO
import { GpioController } from 'gpio-controller';
// Если нет доступа к GPIO используем GpioMock
const mock = GpioController.GPIOACCESS ? false : true;
const gpio = new GpioController({ mock });
свойво mock передаваемое в конструктор определяет использовать GpioMock или нет.
GpioMock дает полный доступ к свойствам и методам, позволяя отслеживать любые изменения, и подходит больше для тестирования функционала.
Пример 1: эмуляция нажатия кнопки для пина 4 и проверка вызова callback
import { Button, GpioController, GpioMock } from 'gpio-controller';
import { ValueCallback } from 'gpio-controller/gpio/gpio.interface';
const gpio = new GpioController({ mock: true });
const callback: ValueCallback = (err, value) => {
if (!err) {
if (value == 0) console.log('Button pressed');
if (value == 1) console.log('Button released');
}
};
gpio.setPIN({ gpio: 4, direction: 'in', edge: 'both' });
gpio.watchPIN(callback, 4);
const pin = gpio.getPIN(4);
const button = new Button(pin as GpioMock);
button.push();
Пример 2 - отслеживаем изменение состояния пина прямым доступом к состоянию:
import { GpioController, GpioMock } from 'gpio-controller';
const gpio = new GpioController({ mock: true });
gpio.setPIN({ gpio: 12, direction: 'out' });
const pin: GpioMock = gpio.getPIN(12) as GpioMock;
console.log('pin value before write:', pin._value);
gpio.writeSignal(12, 1).then(value => {
console.log('pin value after write:', pin._value);
})
В тестах можно найти все примеры работы с данным классом
в работе