суббота, 21 сентября 2013 г.

GPIO на BeagleBoneBlack: правильный доступ на Python

Про то как помигать светодиодом на платах вроде BeagleBone написано очень много. Но есть несколько проблем из-за которых я и решил написать этот пост.

И так, нужно просто управлять цифровым выходом в программе на Python. Для доступа к GPIO из user-space используется sysfs. Можно прямо из консоли (echo 45 > /sys/class/gpio/export), но это не особо удобно поскольку номера выводов придётся считать вручную. Есть библиотека для Python от adafruit которая реализует эту скучную работу и даёт простой API. Её и будем использовать.
 
Первая проблема: права доступа к /sys.
Вторая проблема: баг в библиотеке от adafruit.

 Решение ниже.

Управление выводами из консоли выглядит примерно так:
echo 45 > /sys/class/gpio/export
Включили GPIO#45. Куда он выведен на плате - курить даташиты, развёрнутый пост про это тут.
После этого в /sys/class/gpio должен появиться каталог gpio45/, являющийся симлинком на /sys/devices/virtual/gpio/gpio45/.
Теперь посредством чтения/записи в соответствующие файлы внутри этого каталога имеем доступ непосредственно к лог.уровням на выводе.
Например, настроим его на выход и запишем туда лог.1:
echo out > /sys/class/gpio/gpio45/direction
echo 1 > /sys/class/gpio/gpio45/value
До сих пор всё хорошо за исключением того что доступ к /sys имеет только root. От имени простого пользователя записать туда данные нельзя. Это никуда не годится.

На форумах можно встретить рекомендации изменять права доступа и настраивать GPIO при загрузке системы, из /etc/rc.local например, но мне это тоже не нравится т.к. появляется 2 места для настройки портов: в инитскриптах и собственно в программе, работающей с этими портами.

Правильнее сделать чтобы создаваемые на sysfs файлы уже имели нужные права для нужного пользователя. Это можно сделать с помощью udev. Напишем правило /etc/udev/rules.d/71-gpio.rules с содержанием:
KERNEL=="gpio*", SUBSYSTEM=="gpio", ACTION=="add", PROGRAM="/bin/sh -c 'chown -R debian:gpio /sys/class/gpio'"
KERNEL=="gpio*", SUBSYSTEM=="gpio", ACTION=="add", PROGRAM="/bin/sh -c 'chown -R debian:gpio /sys/devices/virtual/gpio/'"
Теперь (после перезагрузки т.к. горячая перезагрузка правил udev не сработает для уже загруженного драйвера) при создании файла на sysfs в драйвере gpio будет вызываться смена владельца для 2 каталогов, которые нам и нужны.

Группу gpio создадим:
groupadd gpio
Пользователя debian к ней добавим:
usermod -a -G gpio debian
Перелогиниваемся под пользователем debian и проверяем от его имени все вышеприведённые манипуляции с /sys. Должно работать. Можно писать shell-скрипты для мигания светодиодами или аналогичных бесконечно бесполезных задач :)

Теперь надо получить тот же результат на Python. Используем библиотеку http://learn.adafruit.com/setting-up-io-python-library-on-beaglebone-black/overview.

Простейший скрипт для дёргания выводом каждые пол-секунды:
#!/usr/bin/env python
import Adafruit_BBIO.GPIO as GPIO
import time
P = "P8_11"
GPIO.setup(P, GPIO.OUT)
for i in xrange(100):
    GPIO.output(P, GPIO.HIGH if i % 2 == 0 else GPIO.LOW)
    time.sleep(0.5)
GPIO.cleanup()
Спасибо Adafruit за возможность работать с пинами по их расположению на плате, а не просто по номерам в регистрах процессора. "P8_11" означает 11 пин на гребёнке P8 в BeagleBone Black.

Но тут меня ждала подстава. Данный скрипт работает от root, но не работает от обычного пользователя. Хотя все права верные. Более того, скрипт начинает работать если предварительно вручную из консоли включить нужный пин. С этим пошёл на stackoverflow и на github библиотеки. Но самостоятельно решил проблему раньше. Пока авторы не исправили этот баг есть брутальное решение ниже.

По каким-то причинам при работе от простого пользователя файл /sys/class/gpio/gpio# не успевает создаться до того как будет вызвана запись в него. Вызов open() возвращает ошибку, на которую никому нет дела и всё просто тупо не работает.

Берём исходники этой библиотеки:
git clone https://github.com/adafruit/adafruit-beaglebone-io-python.git
И применяем патч:
--- py_gpio.c    2013-09-17 20:10:08.000000000 +0300
+++ py_gpio.c    2013-09-21 02:54:43.000000000 +0300
@@ -105,10 +105,26 @@

    if (get_gpio_number(channel, &gpio))
        return NULL;
-
-   gpio_export(gpio);
-   gpio_set_direction(gpio, direction);
-   gpio_set_value(gpio, pud);
+  
+   unsigned int count = 1000000;
+   int res = -1;
+   do {
+       res = gpio_export(gpio);
+   } while(res != 0 && count-- > 0);
+   if(count == 0)
+       return NULL;
+   count = 1000000;
+   do {
+       res = gpio_set_direction(gpio, direction);
+   } while(res != 0 && count-- > 0);
+   if(count == 0)
+       return NULL;
+   count = 1000000;
+   do {
+    res = gpio_set_value(gpio, pud);
+   } while(res != 0 && count-- > 0);
+   if(count == 0)
+       return NULL;

    gpio_direction[gpio] = direction; 
Применять патч с помощью patch < name.patch в каталоге adafruit-beaglebone-io-python/source.
Утанавливаем:
python setup.py install
В Debian понадобятся пакеты build-essential python-dev python-setuptools. Если предварительно библиотека была установлена с помощью pip или easy_install, то её следует удалить перед установкой из исходников.

Вот и всё. Теперь ногодрыгание работает в Python от обычного пользователя.

Комментариев нет:

Отправить комментарий