Очередной этап проекта - проектирование и реализация средств управления устройством посредством кнопок на передней панели, чтобы само устройство (в части управления) не зависело от компьютера.

В качестве органов управления доступны 4 кнопки с зависимой фиксацией. Зависимая фиксация - это когда нажатие одной кнопки приводит к "отжатию" остальных. Эти кнопки можно использовать для реализации любой функции. Автор выбрал точно такую-же функцию, как и была в оригинальном радио - переключение каналов, - каждой кнопке программируется своя Интернет-станция. Пока кнопка нажата, станция играет; когда все кнопки отжаты - не играет ни одна станция. Если нажаты несколько клавиш (конструктив это позволяет) - ничего не меняется.

Краткое описание конструкции

Функциональная схема выглядит так:

Функциональная схема работы

Кнопки подключаются к линиям порта GPIO микрокомпьютера. Специальная программа-контроллер входов (controls) через драйвер WiringPi опрашивает эти линии. При активации той или иной линии порта низким уровнем, контроллер запускает внешний bash-скрипт, привязанный к номеру линии. В скрипте могут быть описаны любые команды, но для нашей задачи, в скриптах прописаны команды управления медиаплейером Mopidy посредством консольного клиента mpc и протокол MPD. Команда поступает через сеть, через MPD-интерфейс плейера Mopidy и последний совершает действия.

Реализация

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

Шаг 1. Установка mpc

Для управления плейером Mopidy через командную строку установим программу mpc:

# apt-get install mpc

Настроим Mopidy для удалённого управления через протокол MPD.
Остановим Mopidy, если он ещё запущен:

/etc/init.d/mopidy stop

Откроем файл /etc/mopidy/mopidy.conf и настроим секцию mpd

[mpd]
enabled = true
hostname = 0.0.0.0
port = 6600
password = 1234
max_connections = 20
connection_timeout = 60
zeroconf = Mopidy MPD server on $hostname
command_blacklist =
  listall
  listallinfo

Параметр enabled должно быть true - это включит функционал. Установим параметр password в 1234 - это пароль для подключения по протоколу mpd. Вы можете установить свой.

Теперь создадим в каталоге /var/lib/mopidy/playlists файл с расширением .m3u, куда пропишем радиостанции по одной в строку. Для примера создадим файл radio.m3u с таким содержимым:

http://icecast.vgtrk.cdnvideo.ru/vestifm_mp3_64kbps
http://live.shtorm.fm:8000/mp3_gold.m3u
http://185.53.169.128:8000/192
http://50.7.96.138:8115/stream

Запускаем Mopidy:

/etc/init.d/mopidy start

Через вэб-интерфейс переходим в Playlists, выбираем наш плейлист и в правой колонке должны отобразиться станции. Пробуем включить какой-нибудь поток. Если всё работает, переходим к проверке управления через mpc.

Общий формат команд таков:

mpc -h пароль@хост команда параметр

Примеры:
Останавливаем воспроизведение:

# mpc -h 1234@127.0.0.1 stop 

Очищаем очередь воспроизведения:

# mpc -h 1234@127.0.0.1 clear

Загружаем наш плей-лист "radio":

# mpc -h 1234@127.0.0.1 load radio

Запускаем на воспроизведение вторую станцию из списка:

# mpc -h 1234@127.0.0.1 play 2

Если нужно, регулируем громкость:

# mpc -h 1234@127.0.0.1 volume 80

Если вышла ошибка error: incorrect password - проверьте правильность пароля, указанного в секции mpd в /etc/mopidy/mopidy.conf

На этом, взаимодействие с Mopidy по протоколу MPC можно считать настроенным. Переходим к настройке контроллера.

Шаг 2. Установка библиотек WiringPi

К сожалению, для процессора A20 (H3) ещё не написали библиотек для Python, где бы была реализована поддержка прерываний. Программа контроллера входов на Python потребляет значительное количество ресурсов, поэтому автор реализовал программу на языке Си с простым опросом линий. "Мостом" между Си и аппаратурой процессора OrangePi PC служит библиотека WiringPi, а точнее её адаптированный форк - WiringOP-H3 от zhaolei. Необходимо её установить в операционной системе микрокомпьютера и провести тест GPIO.

Перед работой доустановим некоторые пакеты, необходимые для сборки программ из исходников:

# apt-get install autoconf automake autotools-dev m4 make binutils cpp cpp-4.6 gcc gcc-4.6 gcc-4.6-base libc-dev-bin libc6-dev libmpc2 libmpfr4 linux-libc-dev manpages manpages-dev zip unzip

Скачиваем исходник библиотеки с https://github.com/zhaolei/WiringOP в виде архива и ложим в какой-нибудь каталог. Далее распаковываем его и входим в каталог:

# cd /root/src
# unzip WiringOP-h3.zip
# cd WiringOP-h3

Даём права на исполнение для build и производим сборку:

# chmod +x ./build
# ./build

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

# gpio readall

Выйдет что-то вроде такой таблицы:

 +-----+-----+----------+------+---+--OrangePiPC--+---+------+---------+-----+--+
 | BCM | wPi |   Name   | Mode | V | Physical | V | Mode | Name     | wPi | BCM |
 +-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
 |     |     |     3.3v |      |   |  1 || 2  |   |      | 5v       |     |     |
 |   2 |  -1 |    SDA.0 |      |   |  3 || 4  |   |      | 5V       |     |     |
 |   3 |  -1 |    SCL.0 |      |   |  5 || 6  |   |      | 0v       |     |     |
 |   4 |   6 | IO6 PA06 |  OUT | 0 |  7 || 8  |   |      | TxD3     |     |     |
 |     |     |       0v |      |   |  9 || 10 |   |      | RxD3     |     |     |
 |  17 |  -1 |     RxD2 |      |   | 11 || 12 | 0 | OUT  | IO1 PD14 | 1   | 18  |
 |  27 |  -1 |     TxD2 |      |   | 13 || 14 |   |      | 0v       |     |     |
 |  22 |  -1 |     CTS2 |      |   | 15 || 16 | 0 | OUT  | IO4 PC04 | 4   | 23  |
 |     |     |     3.3v |      |   | 17 || 18 | 0 | OUT  | IO5 PC07 | 5   | 24  |
 |  10 |  -1 |     MOSI |      |   | 19 || 20 |   |      | 0v       |     |     |
 |   9 |  -1 |     MISO |      |   | 21 || 22 |   |      | RTS2     |     |     |
 |  11 |  -1 |     SCLK |      |   | 23 || 24 |   |      | SPI-CE0  |     |     |
 |     |     |       0v |      |   | 25 || 26 |   |      | CE1      |     |     |
 |   0 |  -1 |    SDA.1 |      |   | 27 || 28 |   |      | SCL.1    |     |     |
 |   5 |   7 |  IO7 PA7 |  OUT | 0 | 29 || 30 |   |      | 0v       |     |     |
 |   6 |   8 |  IO8 PA8 |  OUT | 0 | 31 || 32 | 0 | OUT  | IO9 PG08 | 9   | 12  |
 |  13 |  10 | IO10 PA9 |  OUT | 0 | 33 || 34 |   |      | 0v       |     |     |
 |  19 |  12 | IO12PA10 |  OUT | 0 | 35 || 36 | 0 | OUT  | IO13PG09 | 13  | 16  |
 |  26 |  14 | IO14PA20 |  OUT | 0 | 37 || 38 | 0 | OUT  | IO15PG06 | 15  | 20  |
 |     |     |       0v |      |   | 39 || 40 | 0 | OUT  | IO16PG07 | 16  | 21  |
 +-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
 | BCM | wPi |   Name   | Mode | V | Physical | V | Mode | Name     | wPi | BCM |
 +-----+-----+----------+------+---+--OrangePIPC--+------+----------+-----+-----+

Немного разберёмся что к чему:

  • Physical - номера линий физического порта, т.е. номерация пинов GPIO,
  • V - уровень, который присутствует в данный момент на линии,
  • Mode - режим, в котором работает линия: вход или выход,
  • Name - именование линии в контексте RaspBerryPi,
  • wPi - номера линий в контексте работы WiringPi. Именно в этом формате будет взаимодействие в программе-контроллере,
  • BCM - номера линий самого процессора в контексте функционального назначения выводов.

В колонке wPi, где вместо номера линии присутствует значение "-1" означает, что библиотека не смогла проинициализировать данную линию и она недоступна для управления (например занята другим приложением). Подробнее об этом можно почитать на этой странице сайта автора WiringPi.

Как видно по значениям OUT в колонке Mode, доступных пользователю линий остаётся не так уж много.

Шаг 3. Сборка контроллера и установка в системе

Следующий шаг - сборка программы-контроллера, которая будет опрашивать линии порта и по определённой логике запускать внешний процесс в виде bash-скрипта. Исходный код программы помещён ниже. Кратенько, программа работает следующим образом:

  1. Инициализация GPIO и каждой линий порта.
  2. Опрос и сохранение состояния каждой линии в массиве.
  3. Пауза для устранения дребезга контактов и разгрузки процессора.
  4. Обработка логики смены уровней на линиях и выполнение действий.
  5. Сохранение текущего состояния линий в массиве для следующего сравнения.
  6. Переход на шаг 2.

У алгоритма есть тонкости:

  1. Кнопки используются с зависимой фиксацией! Так если нажал на одну - и она остаётся в этом положении до тех пор, пока не будет нажата другая. При использовании кнопок без фиксации следует дорабатывать программу.
  2. Низкий уровень "0" на линии является активным.
  3. Скрипты выполняются однократно при переходе линии из состояния "1" → "0".
  4. При переходе из состояния "активна хоть одна линия" в состояние "не активна ни одна линия" программа однократно выполняет скрипт run0.sh. Это нужно для отключения воспроизведения любой станции. Если в своей разработке будете использовать кнопки с зависимой фиксацией - просто не сильно (не до срабатывания фиксатора) нажмите на пару кнопок, чтобы остальные отжались. Таким образом ни одна кнопка не будет нажата.
  5. Если активны несколько линий, программа ничего не предпринимает и работает как работала до этого - нажатие игнорируется.
  6. Сразу после запуска программы, происходит запуск run0.sh, а затем runN.sh, если какая-то из кнопок была уже нажата.
  7. Наличие и правильность внешних скриптов не проверяется программой - это должен обеспечить пользователь.

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

Исходный код:

controls.c
// Вариант контроллера кнопок с зависимой фиксацией или тумблеров для OrangePi
// Версия 2.0
// @ Команда Зея.org http://nix.zeya.org/ 
// Распространяется на условиях "Общественное достояние"
 
#include <stdio.h>    // Подключение библиотеки стандартного ввода-вывода
#include <wiringPi.h> // Подключение библиотеки поддержки чипа
#include <stdlib.h>   // Подключение библиотеки работы с функциями system
#include <string.h>   // Подключение библиотеки работы со строковыми функциями
 
 
// === Настраиваемые пользовательские параметры === 
# define num 4                                     // Общее количество задействованных выводов
 
unsigned int nums = 4;                             // Общее количество задействованных выводов ещё раз
unsigned int pins[] = {1, 4, 5, 6};                // Перечисление выводов GPIO в семантике wPi
unsigned char script_path[50] = "/usr/local/bin/"; // Каталог, где лежат скрипты runN.sh
unsigned int delay_time = 2000;                    // Задержка между опросами линий.
                                                   // Рассчитывать исходя из общего кол-ва кнопок.
 
// === Глобальные переменные ===
 
 // Объявление констант и переменных
unsigned int curr_state[num];       // массив текущих состояний
unsigned int old_state[num];        // массив предыдущих состояний
unsigned int trigg_0;               // триггер события всех неактивных входов
unsigned int act_inputs = 0;        // переменная количества активных входов
 
unsigned int i;                     // счётчик
 
 
 
// === ФУНКЦИИ ===
 
// Функция запуска внешнего процесса
void run_script(unsigned int act_butt_num)
{
  unsigned char command[50];
  sprintf(command, "%s" "%s" "%u" "%s", script_path, "run", act_butt_num, ".sh");  // Формируем команду в виде текста 
  system (command);                 // Производим запуск дочернего процесса
  return;
}
 
// Функция инициализации GPIO
void init_gpio()
{
  wiringPiSetup();              // Выбираем систему нумерации выводов - wiringPi, смотреть столбец wPi вывода gpio readall
  for (i = 0; i < nums; i++)    // Перебираем в цикле все выводы и устанавливаем для каждого параметры
   {
      pinMode(pins[i], INPUT);            // Установка функции порта на ввод
      pullUpDnControl(pins[i], PUD_UP);   // Подключение подтягивающего внутр. резистора с шины питания
   } 
  return;
}
 
// Функция опроса входов
void scan_inputs()
{
 act_inputs = 0;                             // Сброс счётчика активных входов
 for (i = 0; i < nums; i++)                  // Перебираем в цикле все входы
  {
    curr_state[i] = digitalRead(pins[i]);    // Заносим состояние каждого входа в массив текущих состояний
    if (curr_state[i]==0)                    // Если вход активен - инкремент счётчика
     {
        act_inputs = act_inputs++;
     }
  }
 return; 
}
 
// Функция копирования массивов состояний из нового в старый
void copy_states()
{
  for (i = 0; i < nums; i++)
   {
     old_state[i] = curr_state[i];
   }
  return;
}
 
// Инициализация массива предыдущего состояния
// После запуска ни один вход не активен
void old_state_init()
{
  for (i = 0; i < nums; i++)
   {
      old_state[i] = 1;
   }
  return;
}
 
 
// Функция сравнения состояний и выполнения действий - логика действий
void logic()
{
  if (act_inputs == 0)    // === Если нет активных входов...
   {
      if (trigg_0 == 1)     // ...и если триггер события неактивности входов установлен...
       {
          return;           // ...завершить функцию
       } 
      else                  // ...иначе взвести триггер и выполнить скрипт нулевого действия
       {
          trigg_0 = 1; // ...
          run_script(0);
          return;
       } 
   }
  else if (act_inputs > 1)     // === Если есть более двух активных входов - обновить состояние и выйти
   {
     copy_states();
     return;
   }
  else if (act_inputs == 1)    // === Если есть один активный вход - начать сравнение
   {
     trigg_0 = 0;             // Сброс триггера нулевой активности
     for (i = 0; i < nums; i++) 
 
     // Если состояние изменились на "включен"
     if (curr_state[i] == 0 && old_state[i] == 1)
       {
           old_state[i] = curr_state[i];
           run_script(pins[i]);
       }
     // Если состояние изменились на "отключен"
     else if (curr_state[i] == 1 && old_state[i] == 0)
       {
           old_state[i] = curr_state[i];
       }
    }
  return;
 
}
 
 
// === Контроллер ===
int main(void)
{
//  printf ("start\n"); // отладка
 
  init_gpio();      // Инициализация GPIO
  old_state_init(); // Первоначальная инициализация массива старых состояний
  scan_inputs();    // Совершаем первый опрос входов
  while (1)
   {
     logic();             // обрабатываем состояния и выполняем действия
     scan_inputs();       // сканируем входы 
     delay (delay_time);  // задержка между опросами
   }
  return 0;
}

Пользователю необходимо подставить свои значения в переменные:

  • num и nums - это общее количество используемых линий. В авторском варианте задействовано 4 кнопки - это 4 линии. Значения num и nums должны совпадать.
  • В массиве pins[] нужно через запятую перечислить используемые линии в формате wPi - т.е. смотреть нужно на вывод gpio readall в колонку wPi. При подключении проводов смотреть какому номеру wPi соответствует физический номер линии. Номера wPi будут использоваться в именах внешних скриптов.
  • В переменную script_path[50] нужно установить путь, где будут размещаться внешние скрипты.
  • delay_time нужно выставить интервал опроса линий в миллисекундах. Чем это значение меньше, тем больше нагружается процессор, если выставить больше - возрастёт время реакции на нажатие. Например, если в конструкции задействовано 10 кнопок и выставлена задержка в 2 секунды, то опрос всех кнопок завершится через 20 секунд, что достаточно долго для обработки события.

Исходный код скачивается и сохраняется как файл controls.c в каком-нибудь каталоге. Далее в текстовом редакторе устанавливаются вышеописанные переменные, а затем нужно произвести сборку:

# gcc -o controls controls.c -l wiringPi

Для проверки работоспособности можно создать в каталоге, указанном в переменной script_path[50] скрипты с именами runN.sh , где N - номер линии порта в формате wPi, активация которой вызовет этот скрипт. Пример: активация линии 5 вызовет скрипт run5.sh, активация 16 линии - run16.sh и тд.

Более полный пример: задача вызвать команду echo "Hello world" при нажатии на кнопку, подключенную к физической линии 29 порта GPIO:

  1. 29-я линии соответствует 7-му выводу в формате wPi.
  2. Создаём скрипт run0.sh, куда вносим команду exit 0 с соответствующим окружением и заголовками - это для обработки ситуации, когда ни одна кнопка не нажата. Делаем его исполняемым.
  3. Создаём скрипт run7.sh, куда вносим команду echo "Hello world" с соответствующим окружением и заголовками. Делаем его исполняемым.
  4. Прописываем "7" в массив pins[].
  5. Прописываем "1" в num и nums (если кнопка всего одна).
  6. Компилируем программу.
  7. Проверяем: запускаем программу, должен отработать run0.sh, а при замыкании линий 29 и 25 (GND) между собой на время более 2 секунд вызовет скрипт run7.sh, который в свою очередь выведет "Hello world".

Вернёмся к установке программы.
В текущем каталоге, где собиралась программа, появится файл controls. Копируем его в каталог /usr/local/bin/

Для упрощения конфигурирования глобальные параметры в скриптах определены в отдельный конфигурационный файл. Это параметр пути лог-файла и идентификационных данных для подключения к Mopidy по протоколу MPD.

Создадим такой конфиг с именем controls.conf, и положим его в /usr/local/etc/

controls.conf
# Конфигурационный файл для дочерних скриптов
# контроллера кнопок
 
PLAYER="1234@127.0.0.1"
LOG="/var/local/controls.log"

Создадим пустой файл для лога:

# touch /var/local/controls.log

В этот файл будет помещаться вся информация о работе скриптов и mpc.

Теперь переходим в /usr/local/bin/ и создаём по скрипту с командами, для каждой линии:

run0.sh
#!/bin/bash
 
# Скрипт запуска команд для случая,
# когда не активна ни одна кнопка.
 
# Подключаем конфиг
. /usr/local/etc/controls.conf
 
# Собственно команда
 
/bin/echo -e "Run 0\n" >> $LOG 2>&1
 
/usr/bin/mpc -h $PLAYER stop >> $LOG 2>&1
 
exit 0
run1.sh
#!/bin/bash
 
# Скрипт запуска команд при активации wiPi входа 1
 
# Подключаем конфиг
. /usr/local/etc/controls.conf
 
# /bin/echo -e "Run 0\n"
 
# exit 0
 
# Собственно команда
 
/bin/echo -e "Run 1\n" >> $LOG 2>&1
 
/usr/bin/mpc -h $PLAYER stop >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER clear >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER load radio >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER play 2 >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER volume 80 >> $LOG 2>&1
 
exit 0
run4.sh
#!/bin/bash
 
# Скрипт запуска команд при активации wiPi входа 4
 
# Подключаем конфиг
. /usr/local/etc/controls.conf
 
# /bin/echo -e "Run 0\n"
 
# exit 0
 
# Собственно команда
 
/bin/echo -e "Run 4\n" >> $LOG 2>&1
 
/usr/bin/mpc -h $PLAYER stop >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER clear >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER load radio >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER play 3 >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER volume 80 >> $LOG 2>&1
 
exit 0
run5.sh
#!/bin/bash
 
# Скрипт запуска команд при активации wiPi входа 5
 
# Подключаем конфиг
. /usr/local/etc/controls.conf
 
# /bin/echo -e "Run 0\n"
 
# exit 0
 
# Собственно команда
 
/bin/echo -e "Run 5\n" >> $LOG 2>&1
 
/usr/bin/mpc -h $PLAYER stop >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER clear >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER load radio >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER play 4 >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER volume 80 >> $LOG 2>&1
 
exit 0
run6.sh
#!/bin/bash
 
# Скрипт запуска команд при активации wiPi входа 6
 
# Подключаем конфиг
. /usr/local/etc/controls.conf
 
# /bin/echo -e "Run 0\n"
 
# exit 0
 
# Собственно команда
 
/bin/echo -e "Run 6\n" >> $LOG 2>&1
 
/usr/bin/mpc -h $PLAYER stop >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER clear >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER load radio >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER play 1 >> $LOG 2>&1
/usr/bin/mpc -h $PLAYER volume 80 >> $LOG 2>&1
 
exit 0

Скрипты, кроме run0.sh, отличаются только номером станции в плей-листе. Лишние команды из скриптов можете удалить - автор использовал их для отладки.
Каждый раз во время запуска скрипта, серверу Mopidy последовательно даются команды:

  • stop - для остановки текущего воспроизведения,
  • clear - для отмены выбора текущего плей-листа,
  • load radio - для повторной загрузки этого же листа,
  • play X - для запуска воспроизведения Интернет-станции с адреса на первой строке с номером X плей-листа. Счёт начинается с 1 и увеличивается вниз.
  • volume 80 - для подстройки громкости. У разных станций она разная и для выравнивания уровня между переключениями станций, настройка будет полезна.

Команды для перезагрузки плей-листа необходимы, поскольку без них mopidy "забывает" что воспроизводилось. Скрипты можно запустить вручную, чтобы проверить как они работают.
После команд имеется конструкция:

>> $LOG 2>&1

, задача которой стандартный вывод и вывод об ошибках переслать в лог-файл. При желании, если нет надобности в отладке, можно перенаправлять вывод в /dev/null, для чего в конфигурационном файле /usr/local/etc/control.conf , вместо

LOG="/var/local/controls.log"

поставить:

LOG="/dev/null"

Сразу отметим, что запуск программы контроллера должен происходить от имени root`а, в связи с чем дочерние процессы также получат эти права. Поэтому, если конструкция работает в публичной сети, позаботьтесь о том, чтобы скрипты были доступны для модификации только root`у. Также неплохо было бы настроить фаирвол.

После того, как нужные скрипты созданы, дадим права на исполнение:

# chmod +x *.sh

Шаг 4. Аппаратная доработка

Внимание!
Чтобы не повредить входные цепи порта GPIO, следуйте рекомендациям:
1. Любые манипуляции с линиям порта GPIO - отключение или подключение внешних цепей, обязательно производите при отключенном из розетки блоке питания, а также отключенных периферийных устройствах.
2. Первым подключается сигнал GND, а затем все остальные периферийные линии.
3. Перед работой желательно сравнять потенциал статического электричества коснувшись контакта GND платы микрокомпьютера: металлического экрана usb или Ethernet-разъёма.

Ниже приведена принципиальная схема подключения кнопок к GPIO. При нажатии на кнопку на входе процессора формируется логическое "0" и программа контроллер начинает обработку. Назначение резисторов следует подчеркнуть отдельно. Дело в том, что после включения микрокомпьютера и до загрузки модуля ядра gpio_sunxi, на произвольных линиях GPIO формируются логические уровни "1". Если подключить кнопки без резисторов, можно перегрузить линии порта, в случае когда кнопка оказалась нажатой на момент включения микрокомпьютера. Сопротивление резистора выбрано исходя из вытекающего тока не более 2 мА и стабильного формирования логического "0" при замыкании контактов кнопки.

Схема подключения кнопок к микрокомпьютеру

Контакт GND нужно брать тоже с разъёма GPIO - это защитит порты в случае появления потенциала между общим земляным проводом и "землёй" микрокомпьютера. Соответствие подключаемых кнопок портам выбираете вы сами, нужно лишь учитывать, чтобы линия была доступна для конфигурирования со стороны WiringPi (статус не должен быть "-1" или "пустой").

Программа-контроллер, при запуске подключает подтягивающий резистор между шиной +3,3В и линией GPIO для формирования логической "1" на входе. Цепь при этом выглядит так:

Цепь порта

Более подробную схему порта можно посмотреть здесь.

Резисторы смонтированы на оригинальной монтажной плате в подходящих местах. Соединения до кнопок выполнены изолированным проводом, а дорожки от кнопок на плате перерезаны.

Вид со стороны дорожек
Вид со стороны деталей

Для подключения проводов к контактам GPIO взяты пружинистые контакты с разъёмов старой отечественной техники.

Перед подачей питания на микрокомпьютер, внимательно проверьте правильность схемы.

Полная схема устройства на данном этапе выглядит так:

Полная схема с учётом подключения кнопок. (Нажмите для увеличения)

В формате PDF: Полная схема с учётом подключения кнопок. Этап 4. (53 Кбайт)

Шаг 5. Отладка взаимодействия

Запускаем программу контроллера:

# cd /usr/local/bin
# ./controls

В отдельном окне терминала наблюдаем за тем, что пишется в лог-файл. После запуска программы в логе запишется:

Run 0

volume: 80%   repeat: off   random: off   single: off   consume: off

Run 0 говорит о том, что был выполнен запуск скрипта Run0.sh
После нажатия кнопки, подключенной к выводу GPIO4 - вывод 7 (или вывод 6 в семантике wPi) будет записано:

Run 6

volume: 80%   repeat: off   random: off   single: off   consume: off
volume: 80%   repeat: off   random: off   single: off   consume: off
loading: radio
//http://icecast.vgtrk.cdnvideo.ru/vestifm_mp3_64kbps//
[playing] #1/4   0:00/0:00 (0%)
volume: 80%   repeat: off   random: off   single: off   consume: off
//http://icecast.vgtrk.cdnvideo.ru/vestifm_mp3_64kbps//
[playing] #1/4   0:00/0:00 (0%)
volume: 80%   repeat: off   random: off   single: off   consume: off

Нажимаем на другую кнопку, смотрим что запишется в лог.

Если всё работает как надо, настраиваем автозапуск контроллера. Программа контроллера - это не полноценный демон, поэтому проще её запускать и отправлять в фон, а для закрытия использовать killall
Создаём скрипт запуска такого вида:

controls
#! /bin/sh
### BEGIN INIT INFO
# Provides:          controls
# Required-Start:    $all
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:
# Short-Description: Run button controller if it exist
### END INIT INFO
 
 
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/usr/local/etc
 
. /lib/init/vars.sh
. /lib/lsb/init-functions
 
do_start() {
        if [ -x /usr/local/bin/controls ]; then
                [ "$VERBOSE" != no ] && log_begin_msg "Running button controller (/usr/local/bin/controls)"
                sleep 5s
                /usr/local/bin/controls &
                ES=$?
                [ "$VERBOSE" != no ] && log_end_msg $ES
                return $ES
        fi
}
 
case "$1" in
    start)
        do_start
        ;;
    restart|reload|force-reload)
        echo "Error: argument '$1' not supported" >&2
        exit 3
        ;;
    stop)
        killall controls
        ;;
    *)
        echo "Usage: $0 start|stop" >&2
        exit 3
        ;;
esac

Обратите внимание, что контроллер запускается с пятисекундной задержкой (sleep 5s) - это нужно для полноценного запуска Mopidy. Без задержки не всегда выполняется первая команда.

Сохраняем как controls, даём права на выполнение и копируем в /etc/init.d/

# chown root:root controls
# chmod +x controls
# cp controls /etc/init.d/

Далее производим установку в уровни запуска

# insserv -v controls

В каталоге /etc/rc2.d/ должна появиться символическая ссылка на /etc/init.d/controls

Пробуем запустить и остановить программу и контролировать наличие процесса в памяти:

# /etc/init.d/controls start
# ps -e -H
# /etc/init.d/controls stop

Ну и под конец работ пробуйте перезагрузить ОС и понаблюдать за результатом: если в момент включения была нажата кнопка, то после загрузки ОС, должна включиться станция, соответствующая этой кнопке.

Работы по созданию аппаратного управления завершены, осталось решить проблему управления питанием микрокомпьютера - чтобы микрокомпьютер можно было корректно выключить и также включить. На данном этапе выключение желательно производить через shutdown -h now через консоль, либо эту команду "подключить" к одной из кнопок. Однако для включения микрокомпьютера нужно сперва снять питание +5В, т.е. обесточить блок питания, а затем снова его включить - начнётся нормальный процесс загрузки.

Ну а дальнейшее развитие проекта зависит от фантазии. Например, можно реализовать управление Mopidy через пульт дистанционного управления.

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

Источники информации

А также

Печать/экспорт