Порты GPIO являются одними из главных преимуществ Raspberry Pi, недаром в более поздних версиях платы их количество было увеличено с 26 до 40. К сожалению, большинство подключаемых к ним плат (сенсорные экраны, светодиодные матрицы, платы расширения и т.д.), в целях совместимости со всеми моделями «малинки», используют только первые 26 контактов, оставляя остальные 14 «за бортом». Но мы не дадим пропасть добру! В данной статье я расскажу, как сделать кнопку выключения или перезагрузки на двух неиспользуемых портах. На самом деле функциональность кнопки может быть любой, зависит от вашей фантазии и потребностей.





Это мой первый опыт работы с GPIO, так что тут важно было понять логику. Почти любой контакт GPIO (есть несколько зарезервированных) можно установить в одно из двух состояний: «выход» (он же OUT или логическая 1) или «вход» (IN или логический 0). Напряжение на выходе составляет 3,3В. Работать с портами GPIO можно как непосредственно из терминала, так и из любого языка программирования. Предельно подробно это описано здесь.



Для своей кнопки я решил использовать два самых правых контакта в верхнем ряду. Разъём #38 (GPIO20) будет установлен на «выход», а разъём #40 (GPIO21) — на «вход». Далее цикл, повторяющийся раз в секунду, будет «слушать» разъём #40 и, как только на него поступит сигнал, используемые порты «очистятся» и будет запущена консольная команда для выключения. Как уже говорилось выше, команда может быть и любой другой. Ради интереса я выполнил эту задачу двумя способами: на Python и bash-скриптом. Ниже код обоих вариантов:

shutdown.py
import RPi.GPIO as GPIO
from time import sleep
import os

GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)  # Turn off warnings output
GPIO.setup(38, GPIO.OUT) # Set pin #38 (GPIO20) to output
GPIO.setup(40, GPIO.IN)  # Set pin #40 (GPIO21) to input

while True:
    buttonIn = GPIO.input(40)
    if buttonIn == True:
        print 'System shuts down'
        GPIO.cleanup()
        os.system("sudo shutdown -h now")
        break
    sleep(1)        


shutdown.sh
#! /bin/bash

# Set up GPIO20 and set to output
echo "20" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio20/direction
echo "1" > /sys/class/gpio/gpio20/value

# Set up GPIO21 and set to input
echo "21" > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio21/direction

while ( true ) 
do
    # check if the pin is connected to GND and, if so, halt the system
    if [ $(</sys/class/gpio/gpio21/value) == 1 ]
    then
        echo "20" > /sys/class/gpio/unexport
        echo "21" > /sys/class/gpio/unexport
        shutdown -h now "System halted by a GPIO action"
    fi 
    sleep 1
done




Повторюсь, оба скрипта выполняют одно и то же, для работы кнопки нужен один из них (любой). Их также можно скачать:


В случае со скриптом на Python есть один нюанс: для работы необходим класс RPi.GPIO, который необходимо скачать и установить отдельно. Делается это так:

wget http://pypi.python.org/packages/source/R/RPi.GPIO/RPi.GPIO-0.5.11.tar.gz
tar zxf RPi.GPIO-0.5.11.tar.gz
cd RPi.GPIO-0.5.11
sudo python setup.py install


Собственно, последнее, что осталось сделать — это добавить скрипт в автозагрузку. Способов сделать это тоже несколько, я выбрал cron. Для этого запускается команда sudo crontab -e и в открывшемся файле добавляется одна из следующих строк:

@reboot python /home/pi/lentyay/poweroff.py &
@reboot sudo /home/pi/lentyay/shutdown.sh &




На этом всё. От себя скажу, что пользоваться такой кнопкой очень удобно.

Комментарии (27)


  1. svlasov
    02.04.2015 13:06
    +9

    Есть более оптимальный вариант на Python без цикла:

    import subprocess
    import RPi.GPIO as GPIO
    GPIO.setmode(GPIO.BCM)
    
    # GPIO3 (pin 5)
    GPIO.setup(3, GPIO.IN, pull_up_down=GPIO.PUD_UP)
    
    GPIO.wait_for_edge(3, GPIO.FALLING)
    subprocess.call(['shutdown -h now "System halted by GPIO action"'], shell=True)
    
    GPIO.cleanup()
    


    Замыкаются пины 5 и 6 (GPIO3 и земля).


    1. newkamikaze Автор
      02.04.2015 13:32

      Спасибо, попробую. Правда, как я писал, хочется использовать «последние» пины, так что попробую на 39-м и 40-м.


      1. Krypt
        02.04.2015 15:50
        +1

        Вы можете попробовать взять один из «верхних» GPIO с гребёнки и землю подцепить куда-нибудь ещё. 2 порта для кнопки не нужны


  1. lexfrei
    02.04.2015 14:42

    А зачем выключать RPi? Ест она примерно ничего, включать всё равно кнопкой не выйдет. Я не пристаю, просто на официальном форуме часто встаёт этот вопрос и ответа не него всё ещё нет.
    А почему бы не переписать на С++? На сколько я знаю, из него GPIO тоже доступен и ничего лишнего для бинарника доставлять не придётся.


    1. kAIST
      02.04.2015 14:54
      +1

      Область применения raspberry (и подобных штук), не ограничивается лежанием дома и работой 24х7.
      Я, например, сейчас делаю девайс, который работает от аккумулятора, и как то жаль «в холостую» расходовать аккумулятор. Проблемы с выключением у меня правда нет, так как есть дисплей с тачскрином. Проблема включения решается либо включателем на разрыв питания, хотя можно и заморочиться на кнопку )


      1. newkamikaze Автор
        02.04.2015 15:08

        Да, я тоже планирую сделать из «малинки» портативный фотик с инфракрасной камерой. Работать он тоже будет от пауэрбанка. Если брать на природу, то разумно было бы выключать его между снимками. Данная кнопка — как раз для этого.

        А на C++, не переписал, так как совсем его не знаю. Python тоже не знаю, но начал изучать.

        Кстати, подумалось, можно ли удалённо «эмулировать» нажатие кнопки (через Webiopi, например), переназначив пины?


        1. kAIST
          02.04.2015 15:16

          Да для таких операций на С++ вообщем то смысла особого нет переписывать — производительность высокая не нужна, а python является лишь «клеем» для низкоуровневых операций.

          А смысл какой что то «эмулировать», переназначать и пр, если то же выключение можно сделать и другими способами, без кнопки )


    1. qwerty1023
      02.04.2015 16:53

      А как он тогда выключается, что его включить нельзя? Глянул по быстрому схему и документацию на BCM2835, но нигде не могу найти распиновку микросхемы. Заинтересовала нога D15, подписанная как «RUN». Возможно передернув там состояние, можно перезапустить схему.


      1. gentux
        02.04.2015 17:54

        если эти выводы закоротить, при запущенной системе — горячая перезагрузка (как на PC кнопка Reset)
        если эти выводы закоротить, при выкл системе (командой halt) — система запуститься (как на PC кнопка Power)
        http://raspberrypi.ru/blog/readblog/562.html


      1. nochkin
        02.04.2015 17:56

        Если я не путаю, то на Raspberry Pi незапаянный разъём P6 это Reset. С него можно перезапустить систему.


    1. DarkByte
      02.04.2015 17:28

      После перехода с B на B+ словил очень неприятный баг. Если выключить малину не правильно, то ФС при старте уже не примонтируется и нужно вытаскивать флешку и делать ей fsck. Единственный совет, который удалось найти — «купите совместимую с малиной флешку», попробовал три разных, две из которых в списке совместимых значатся как рабочие — не помогло.


      1. lexfrei
        03.04.2015 17:07

        А можно больше деталей? У меня 3 малины «в парке», стопка разных флешек и никаких проблем.


        1. DarkByte
          03.04.2015 22:06

          Сейчас под рукой малины нет, но ошибка примерно следующая:

          Kernel panic-not syncing:
          VFS: unable to mount root fs on unknown- block(179,2)

          Если делать reboot или shutdown, то всё нормально, а если halt или сброс по питанию, то почти наверняка не загрузится, а fsck найдёт ошибки в разделе. Но стоит раздел корректно размонтировать перед перезагрузкой и никаких ошибок нет. На модели «B» таких проблем не было (были другие :D), по питанию дёргалась чуть ли ни каждый день, ничего не сыпалось, всегда загружалось нормально. apt-get upgrade и rpi-update ничего нового не видят.


          1. nikitosk
            20.04.2015 13:58

            у меня после очередного выключения малины она больше не включилась, в качестве системы там стоял xbian, флешка была разбита на 3 раздела FAT,Btrfs — системный и swap, дак вот теперь флешку не отформатировать совершенно ни чем нельзя, точнее она форматируется но после передергивания опять в том же состоянии, файлы с виндового раздела, тоже удаляются но после размонтирования\монтирования опять все на месте. Флешка Transcend 16GB ей и месяца не было. Что с ней можно еще сделать кроме, как выкинуть, подскажите.
            Купил новую флеху с неизвестным названием на 8GB c ней система не стартует вообще, у меня подозрение уже: может малина сдохла? Ее можно как нибудь проверить без флешки на работоспособность?


            1. Lol4t0
              20.04.2015 17:51

              Подключаете UART и смотрите лог загрузки. elinux.org/RPi_Serial_Connection


              1. nikitosk
                21.04.2015 09:54

                Спасибо, попробовал подключиться, молчание, постоянно горит индикатор RX на USB TTL адаптере, хоть с карточкой хоть без.
                Такое может быть?


                1. Lol4t0
                  21.04.2015 11:30

                  Возможно, uboot не пишет в консоль. Можно посмотреть доку и настроить его так, чтобы он это делал elinux.org/RPi_U-Boot


  1. Cobolorum
    02.04.2015 15:17
    +5

    За циклы сканирования для ожидания нажатия надо расстреливать.
    Для обработки внешних событий микроконтроллера существуют прерывания, т.ч. и от внешних сигналов.
    Если вы так любите питон смотри сюда raspberrywebserver.com/gpio/using-interrupt-driven-gpio.html


    1. kAIST
      02.04.2015 16:17
      +1

      Насколько вижу по исходникам, эти «прерывания» эмулируются запуском отдельного потока, который делает практически тоже самое, что и код в первом комментарии. Так что наоборот, будет больший оверхед.


      1. lorc
        02.04.2015 16:29
        +1

        поэтому лучше всего написать маленький модуль ядра который просто зареквестит нужный gpio, повешает на него обработчик прерывания, а в обработчике вызовет функцию ctrl_alt_del() (да, в ядре есть такая функция, посылает SIGINT иниту)


        1. kAIST
          02.04.2015 16:40
          +2

          Тут человек python только осваивает, а вы предлагаете модуль ядра писать ))
          Хотя дело не в этом, бывает что такие одноразовые костыли в итоге использовать удобней.

          Приведу пример из того что делаю сейчас:
          Для девайса сделал контроллер тачскрина на МК, который тупо по UART шлет координаты. На заморочки с USB-HID в плане софта и железа ушло бы много времени.
          Далее скрипт на raspberry принимает эти цифры, вычисляет нужную точку (с МК идут сырые данные) и через xdotool делает клик. Тут можно было написать простенький модуль ядра, но дошло что по UART нужно передавать не только это.
          Да и зачем такие сложности в устройстве, которое существует в единственном экземпляре и только для себя ))


          1. lorc
            02.04.2015 17:00
            +1

            Ну например для понднятия скилов :)

            А почему xdotool кстати? Есть же python-uinput. Получите самый настоящий инпут девайс, неотличимый от настоящего. А так да, писать tty discipline driver — это ещё то развлечение… И всё равно без юзерспейса не обойтись.


  1. fundorin
    02.04.2015 23:42
    +1

    Мне кажется, есть смысл делать выключение на долгое нажатие. Во избежание случайных срабатываний.


    1. newkamikaze Автор
      03.04.2015 00:07

      Пока фальстартов небыло, но кнопкой я пользуюсь всего несколько дней. До этого долго просто замыкал проводки. На самом деле есть несколько чисто механических решений. Например, утопленная кнопка или откидывающаяся защитная крыжечка на кнопке. Я такой прикрыл на аппе для квадрокоптера кнопку отключения моторов


      1. fundorin
        03.04.2015 00:11

        Если механическое, то слайдер будет надёжнее утопленной кнопки. Правда, за корпус придётся придерживать при выключении. А дребезг контактов как-то обрабатывается или он тут не нужен?


  1. Lol4t0
    03.04.2015 00:03

    А Raspberry Pi не поддерживает ACPI?

    Вообще я ожидал прочитать статью про прерывания, управление режимами работы процессора, ACPI, модули ядра…

    В любом случае с этим надо разбираться, чтобы научить его включаться по кнопочке.


    1. lorc
      05.04.2015 16:27
      +1

      Нет, на АРМах нету ACPI. Точнее нету одного определенного стандарта. Каждый производитель лепит свой ROM-код для таких функций как уход в спящий режим, запуск и остановка дополнительных ядер и т.д. На некоторых платах питание вообще выключается прямой просьбой к PMIC выключить напряжение. Эдакое харакири.

      Было бы круто, если бы поддерживался EFI, но такого нет, к сожалению.