Статьи

Начало работы с выводами Raspberry Pi GPIO в Node.js

IoTWeek_Gray

Это неделя Интернета вещей в SitePoint! Всю неделю мы публикуем статьи, ориентированные на пересечение интернета и физического мира, поэтому следите за последними обновлениями в теге IoT .

Интернет вещей сейчас в моде. Есть так много идей, которые мы можем воплотить в реальность в области физических вычислений, и нас легко втянуть в идею программирования мира, в котором мы живем! Если у вас есть Raspberry Pi и макет, что дальше?

В этой статье мы рассмотрим, как получить доступ к выводам GPIO на Raspberry Pi с помощью Node.js. С помощью выводов GPIO вы можете напрямую программировать оборудование. API JavaScript делают это без проблем. API-интерфейсы являются абстракциями к распространенным методам и доступны из любого места. Интерпретатор Node.js выполняется в одном процессе, который открывает способы написания этого кода таким образом, чтобы он был тестируемым. Самым интересным для меня является то, что вы можете писать модульные тесты, ставить точки останова и исследовать код, как и любую другую программу JavaScript, все с вашего компьютера.

Давайте начнем.

Что такое GPIO?

GPIO расшифровывается как универсальный ввод / вывод . Это контакты, которые находятся на боковой стороне Raspberry Pi, рядом с желтым гнездом видеовыхода. Ниже показано, как они выглядят.

Физическая схема GPIO

Источник: Raspberry Pi

Думайте о них как о том, как вы соединяетесь с внешним миром от Пи. Это позволяет вам писать программы, которые не запускаются на экране компьютера. Каждый контакт действует как переключатель, который вы включаете или выключаете. Вы можете получать входные данные из физического мира или отправлять выходные данные. Базовые платы поставляются с 26 контактами, и 9 из них представляют собой контакты питания или заземления. Контакты заземления находятся в конце каждой цепи, через которую должен протекать ток. Более свежие платы Raspberry Pi поставляются с дополнительным набором из 14 контактов.

Если вас интересует более подробная информация о выводах GPIO, эта онлайн-диаграмма предоставит вам все необходимое, чтобы понять, для чего предназначен каждый вывод. Существует огромное количество выводов для ввода / вывода и заземления. Эти контакты являются основой физических вычислений. В зависимости от вашей цели, вы можете использовать столько, сколько вам нужно.

Дразнить fs !

Я знаю, что вы думаете, что, черт возьми, fs и почему меня это волнует? В Unix-подобных операционных системах файл устройства — это драйвер, который выглядит как файл. С точки зрения непрофессионалов, драйвер устройства — это файл! Угадай, что? GPIO API — это оболочки, которые читают или записывают в файл устройства. API файловой системы — это концепции, которые могут быть вам уже знакомы. Если вы никогда не работали с файлами в Node.js, я рекомендую просмотреть модуль fs и файловые системы в Node.js. fs — сокращение от «файловая система» и позволяет вам читать или писать в старый файл. Здесь нет ничего фантастического, все, что мы делаем, это, например, writeFile() и позволяем GPIO обрабатывать все остальное. Хитрость заключается в том, чтобы знать, что писать в какой файл.

Существует небольшой удобный пакет npm под названием mock-fs , который поможет нам с юнит-тестами. С помощью этой библиотеки можно придумать любой файл в файловой системе и смоделировать его в памяти. Что настолько радикально, так это то, что мы имеем дело только с файлами, и это все, что нам нужно сделать. В Unix-подобной системе GPIO ведет себя как любой другой простой старый файл. Это дает нам свободу в том, как мы можем подойти к этому решению.

Суть библиотеки mock-fs — функция mock({}) . Он принимает один параметр, который является объектом JavaScript. Внутри этого параметра можно придумать любой файл, который вы захотите. Прелесть здесь в том, что все это запоминается, поэтому вы можете сходить с ума от юнит-тестов. Интерпретатор работает в одном процессе, это означает, что можно переопределить модуль fs во время выполнения. JavaScript — это динамический язык, поэтому мы можем смоделировать любой модуль, доступный текущему процессу.

Интерфейс GPIO имеет больше смысла, когда вы пишете на нем хорошие модульные тесты. Что мне нравится, так это то, что вы можете получить автоматизированное тестовое покрытие и хорошее чистое решение. Модульные тесты улучшают читабельность кода, потому что они ясно показывают, для чего предназначен API.

Так что давайте запачкаем руки.

Модульное тестирование всего

Итак, давайте откроем булавку с «out» и протестируем это:

 it('opens a pin with out', function (done) { mock({ '/sys/class/gpio/gpio23/direction': '' }); gpio.open(16, 'out', function () { const direction = fs.readFileSync('/sys/class/gpio/gpio23/direction').toString(); should(direction).equal('out'); done(); }); }); 

Реализация этого теста должна отобразить физический вывод 16 на вывод 23 BCM в GPIO. Номера BCM — это номера контактов Broadcom, которые ядро ​​увидит в драйвере устройства. Драйвер устройства GPIO дает вам общее представление о том, где находятся файлы устройства. Как показано, чтобы открыть булавку, вы записываете строку «out» в /direction . Это говорит GPIO, что мы ожидаем записи на этот вывод. После завершения убедитесь, что файл имеет то, что ему нужно. mock происходит из библиотеки mock-fs , а fs является стандартной файловой системой в Node.js. Ядро говорит, где путь — версия 3.18.x и выше находится в /sys/class/gpio .

Чтобы написать на булавку на доске и проверить это, можно сделать:

 it('writes to a pin with a high value', function (done) { mock({ '/sys/class/gpio/gpio23/value': '0' }); gpio.write(16, 5, function () { const value = fs.readFileSync('/sys/class/gpio/gpio23/value').toString(); should(value).equal('1'); done(); }); }); 

Есть сходства между gpio.open() и gpio.write() . При записи это записывает в файл /value . Для проверки работоспособности я написал сверхвысокое значение 5, но мы ожидаем, что в тесте только 1. GPIO принимает только высокое или низкое значение, как двоичный файл.

Я взял подробности реализации от pi-gpio . Эта библиотека дает вам хороший обзор того, куда идет каждый вывод. Вы также можете посмотреть файлы устройств в ядре. В любом случае, моя цель — чтобы вы хорошо разбирались в основах, чтобы вы могли получить четкую картину.

Давайте немного сойдем с ума, как насчет достижения точки останова в моем модульном тесте? Я использую WebStorm, чтобы сделать это, опять же, использую то, что вам удобно:

Точка останова внутри WebStorm

При программировании звука смысл состоит в том, чтобы сократить цикл обратной связи, необходимый для поиска ошибок. Модульные тесты — хороший способ затянуть петлю и получить немедленную обратную связь.

Ради простоты я пишу в один контакт. Остальная часть GPIO суммируется аналогичным образом. Откройте булавку и скажите ей, что вы хотите с ней сделать. Читайте или пишите на булавку, все, что вам нужно сделать. Низкоуровневые API — это файлы устройств, поэтому вы можете выбрать, как программировать каждый вывод.

Блинк Демо

Чтобы конкретизировать каждый модульный тест, давайте рассмотрим некоторые общие переменные:

 var sysFsPath = '/sys/class/gpio/gpio'; var pinMapping = { '16': 23 }; 

Выше я определил отображение контактов в GPIO и путь к драйверу устройства. Код ниже смотрит на код, который открывается и записывает на контакт:

 function open(pinNumber, direction, callback) { const path = sysFsPath + pinMapping[pinNumber] + '/direction'; fs.writeFile(path, direction, (callback || noOp)); } function write(pinNumber, value, callback) { const path = sysFsPath + pinMapping[pinNumber] + '/value'; value = !!value ? '1' : '0'; fs.writeFile(path, value, 'utf8', callback); } function noOp() {} 

Как показано, все, что нужно сделать, это writeFile() в файл устройства. noOp является фиктивным обратным вызовом в случае, если обратного вызова нет. С этой деталью реализации я получаю прохождение тестов и заверения в том, что это сработает. value при записи гарантирует, что оно будет установлено на высокое или низкое значение ( '0' или '1' ).

В финале демонстрация работающего блинкера с использованием API, показанного выше:

 gpio.open(16, 'out', function () { var on = 0; var blinker = setInterval(function () { gpio.write(16, on, function () { on = (on + 1) % 2; console.log('ON = ' + on); }); }, 1000); setTimeout(function () { clearInterval(blinker); }, 12000); }); 

setInterval() вызывается каждую секунду, в setInterval() вызове я говорю ему переключать контакт с модулем. blinker имеет интервал, setTimeout() использует его, чтобы очистить его через 12 секунд. Обратный вызов в setTimeOut() завершает работу и завершает программу.

Чтобы запустить пример кода, введите:

 sudo npm start 

(Для доступа к GPIO на Raspberry Pi необходимы права администратора)

Я надеюсь, что GPIO выглядит более интуитивно понятным из этой демонстрации. Ожидается открытый штифт с направлением. Затем вы пишете на булавку и позволяете GPIO обрабатывать остальные мелочи.

Вывод

Подход, основанный на тестировании, идеально подходит для IoT. В IoT ваше воображение — это предел. Raspberry Pi может быть развернут в любой точке мира — с помощью физических вычислений вы не хотите отправлять аппаратное оборудование на полпути по всему миру для отладки кода. Благодаря подходу, основанному на тестировании, существует немедленная обратная связь и подтверждение рабочих решений. Вы более продуктивны и можете затянуть петлю обратной связи.

Что мне нравится в API GPIO, так это то, что его можно превратить в обертку вокруг модуля fs . Это дает вам полную свободу в написании чистого и тестируемого кода.

Остальная часть демонстрационного примера на GitHub .