В статье предлагается один из вариантов для корректного завершения работы операционной системы проигрывателя. Суть заключается в установке ещё одной кнопки, но уже без фиксации, и привязке к ней команды на завершение работы. Так как алгоритм программы игнорирует одновременное нажатие на две и более кнопок, перед выключением питания нужно отжать кнопки фиксированных станций. Затем в течении 2 секунд нажать и удерживать кнопку выключения питания. Для включения питания компьютера нужно выключить блок питания и снова его включить спустя несколько секунд. Это единственное неудобство данного решения.

Есть также вариант использовать кнопку управления питания, расположенную на самой плате. Решение описано здесь. Автор не пробовал этот рецепт.

Реализация

Аппаратная доработка

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

Автор сперва закрепил кнопку типа П2К рядышком с остальными. Красный толкатель использован родной от кнопки питания. Выглядит это таким образом:

Вариант крепления дополнительной кнопки

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

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

Кнопка физически подключается к выводам 29 и 25 порта GPIO. Вывод GPIO-5 (29) соответствует выводу 7 в семантике WPi. В любом случае, вы можете задействовать и другие доступные для управления выводы под эту функцию.

Сборка контроллера и отладка

Если у вас уже настроен автоматический запуск пограммы контроллера, то скорее всего он запущен. Его нужно остановить:

# /etc/init.d/controls stop

Исходный код программы контроллера берём с "Этапа 4" и вносим в него изменения для подключения пятой кнопки:

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

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 5                                     // Общее количество задействованных выводов
 
unsigned int nums = 5;                             // Общее количество задействованных выводов ещё раз
unsigned int pins[] = {1, 4, 5, 6, 7};             // Перечисление выводов GPIO в семантике wPi
unsigned char script_path[50] = "/usr/local/bin/"; // Каталог, где лежат скрипты runN.sh
unsigned int delay_time = 1500;                    // Задержка между опросами линий.
                                                   // Рассчитывать исходя из общего кол-ва кнопок.
 
// === Глобальные переменные ===
 
 // Объявление констант и переменных
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;
}

Изменены эти строки:

# define num 5 
unsigned int nums = 5; 
unsigned int pins[] = {1, 4, 5, 6, 7};
unsigned int delay_time = 1500;

num и nums увеличены на единицу; в массив выводов pins[] добавлен вывод "7"; переменная времени задержки delay_time уменьшена на 500 миллисекунд.

Сохраняем файл как controls.c и собираем его:

# gcc -o controls controls.c -l wiringPi

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

run7.sh
#!/bin/bash
 
# Скрипт запуска команд при активации wiPi входа 7
 
# Подключаем конфиг
. /usr/local/etc/controls.conf
 
/bin/echo -e "Run 7\n" >> $LOG 2>&1
 
# Собственно команда
# /sbin/shutdown -h now
 
exit 0

Дадим права на исполнение:

# chmod +x run7.sh

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

# /etc/init.d/controls start

Отжимаем все кнопки программ, нажимаем и удерживаем на пару секунд кнопку питания. В лог файле /var/local/controls.log , в самом конце, должна появиться запись вида:

Run 7

, что означает, что кнопка была опрошена и был выполнен скрипт run7.sh. Если ничего не произошло отлаживаем таким образом:

Программа контроллер должна быть запущена - именно она конфигурирует линии порта. Нажимаем и удерживаем кнопку питания и в терминале выполняем команду:

# gpio readall  

Должна появиться таблица состояний и напротив 29-го вывода смотрим статус и напряжение на линии:

 +-----+-----+----------+------+---+--OrangePiPC--+---+------+---------+-----+--+
 | BCM | wPi |   Name   | Mode | V | Physical | V | Mode | Name     | wPi | BCM |
 +-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
 .....
 |   5 |   7 |  IO7 PA7 |  IN  | 0 | 29 || 30 |   |      | 0v       |     |     |

Mode должен быть в IN, V - должно быть в нуле при нажатой кнопке. Отпускаем кнопку, снова выполняем gpio readall, в поле V должно перейти в "1". Если вместо IN получилось OUT - проверьте программу и правильность номеров линии в массиве pins[]. Если значение V постоянно находится в каком-то одном уровне - проверьте схему - правильность подключения кнопки.

Когда проблема решена, снова редактируем скрипт run7.sh и раскомментируем строку с командой shutdown

run7.sh
#!/bin/bash
 
# Скрипт запуска команд при активации wiPi входа 7
 
# Подключаем конфиг
. /usr/local/etc/controls.conf
 
/bin/echo -e "Run 7\n" >> $LOG 2>&1
 
# Собственно команда
/sbin/shutdown -h now
 
exit 0

На этом работы завершены. В конце проверяем полный цикл работы устройства.

В процессе эксплуатации данного решения автор обнаружил ещё одно неудобство - невозможно определить работает микрокомпьютер или нет без запуска воспроизведения станции или взгляда на индикаторы платы через перфорацию в задней стенке. На будущее можно предусмотреть на передней панели светодиод, загорающийся при запуске программы контроллера, либо вывести на боковую стенку индикаторы с Ethernet-порта.

А также

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