Удаленное управление. Поймаем кнопку за событие.

И снова вечер и снова делать нечего.
А вот например GPIO. Тут один товарищ описал что такое GPIO в серии статей GPIO для чайников. Из них мы знаем, что GPIO может работать как на вход, так и на выход. Грубо говоря, если я замкну 2 пина, один из которых будет IN, а второй OUT и поменяю значение второго, то значение первого так же изменится. Думаю, схему подобного приводить не стоит, потому что, фактически, это просто соединение прямым проводом двух пинов.

Теперь мысль дальше. Вместо прямого провода мы можем соединить 2 пина кнопкой. Нажимаем кнопку — пины замыкаются, значение IN меняется, отпускаем… ну и так далее. Я соединил кнопкой 2 пина: GPIO3 и GPIO4. Получилась вот такая вот схемка.
Другой, более удобной кнопки у меня, к сожалению нет. Ну да ладно. Осталось только понять когда кнопка нажата, а когда наоборот.
Теперь обратимся к ferro-remote, программке, которую я описывал тут. Можно, например, для нее написать скрипт, который покажет текущее состояние пина 4:
g = fr.client.gpio.export(4, "in") 
print(g:get( ))

Все. Данный скрипт запросит состояние пина и выведет его в консоль.
Малинка висит на 192.168.1.11:12345
сохраним файл как gpio.lua. Запускаем:
$ ./lua_client -s 192.168.1.11:12345 -e gpio.lua 
0

Кнопка отжата. Нажимаем, и запускаем снова
$ ./lua_client -s 192.168.1.11:12345 -e gpio.lua 
1

Ура! Работает!
Но как-то странно все это. Для того, чтоб понять, что сейчас в значении у GPIO4 я вынужден делать удаленный вызов (sick!) с довольно приличной частотой (sick! sick!), чтоб хоть как-то адекватно среагировать на событие. А если клиентов штук 50?
Но, у нас же есть система Linux на raspberry, которая ЗНАЕТ, когда изменилось состояние пина. У GPIO есть такое свойство, имя которому

EDGE.

EDGE может принимать 4 значения:
  • none: не реагировать на изменения
  • rising: реагировать только на «подъем» пина
  • falling: реагировать только на «сброс» пина
  • both: реагировать на оба события
Тут стоит сделать примечание, что не все пины поддерживают данное свойство. Пишу к тому, что у меня есть аналог малинки, на котором из 30 GPIO edge поддерживают только 7.

В lua_client есть возможность подписаться на данное событие. В контексте самого клиента оно называется on_changed.
подписка происходит методом subscribe объекта gpio. Например:
gpio = fr.client.gpio -- алиас для удобства

function value_changed( data ) -- функция-обработчик события
    print( "Value 3 -> 4 has changed to", data.value ) -- текущее состояние выведем
end

function main(  )

    local gin, erri  = gpio.export( 4, "in" ) -- получим 4 для ввода
    local gout, erro = gpio.export( 3, "out" ) -- получим 3 для вывода

    if gin and gout then 
	fr.run( ) -- специальная функция, которая говорит клиенту 
                  -- не завершать работу после функции main, а ждать событий
	assert(gout:set(0)) -- установим значение пина 3 на 0
	assert(gin:set( "edge", "both" )) -- установим значение edge пина 4 на both
	assert(gin:subscribe("on_changed", value_changed, gin )) -- подпишемся, 
                                                                 -- "запомним" gin, чтоб он не был прибит GC 
    else
	print( erri, erro )  -- что-то пошло не так, просто выведем ошибки
    end
end

Сохраним, выполним:
$ ./lua_client -s 192.168.1.11:12345 -e gpio.lua 
Value 3 -> 4 has changed to	0
Value 3 -> 4 has changed to	1
Value 3 -> 4 has changed to	1
Value 3 -> 4 has changed to	1
Value 3 -> 4 has changed to	0
Value 3 -> 4 has changed to	0

Ну прям песня! Теперь я не закидываю малинку запросами с вопросом о состоянии, система сама генерирует событие и присылает мне новое! Процессор на 0, трафик на 0. События.
Кстати, если посмотреть внимательно, то можно заметить, что подряд идут одинаковые значения. Я точно не помню как подобный эффект называется, но суть его в том, что пока я нажимаю на кнопку, она в реальности успевает несколько раз законтачиться и расконтачиться. Система на это все реагирует генерацией события. Чем качественнее кнопка, тем меньше таких вот косяков. У меня — очень некачественная.

Теперь, чтоб было еще интереснее, повесим на GPIO2 диод, как было описано в GPIO для чайников (часть 1) и переделаем немного скрипт, чтоб он устанавливал пину 2 состоние пина 4, когда то изменится.
Итого. Тот же самый скрипт, только добавил открытие GPIO2 и изменение его состояние по изменению GPIO4
gpio = fr.client.gpio

function value_changed( data, diod )
    print( "Value 3 -> 4 has changed to", data.value, data.interval )
    diod:set( data.value ) -- установим состояние диода.
end

function main(  )

    local gin, erri  = gpio.export( 4, "in" )
    local gout, erro = gpio.export( 3, "out" )
    local diod, errd = gpio.export( 2, "out" ) -- откроем диод

    if gin and gout and diod then 
	fr.run( )
	assert(gout:set(0))
	assert(gin:set( "edge", "both" ))
	assert(gin:subscribe("on_changed", value_changed, diod, gin ))
    else
	print( erri, erro, errd )  
    end
end

Теперь скрипт включает и выключает диод, когда я нажимаю или отжимаю кнопку.
PS: Где-то у меня инкрементный енкодер валялся. Думаю, если я его найду, то добавлю пример работы с ним.
  • 0

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

0
А зачем тратить два порта GPIO на кнопку? Не лучше использовать один порт в режиме IN с внутренней подтяжкой к +, а кнопкой просто «сажать» его на землю?
0
Просто для примера.
Но на самом деле я нифига в электронике не соображаю. :-/
А надо бы почитать что.
комментарий был удален
+1
Я точно не помню как подобный эффект называется, но суть его в том, что пока я нажимаю на кнопку, она в реальности успевает несколько раз законтачиться и расконтачиться.
этот эффект называется дребезг контактов
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.