Адвент-календарь «Let it Glow», день #11: Весёлая мембрана!
Это день #11 Адвент-календаря «Let it Glow»!
Сегодня у нас ещё один управляющий компонент для игры. То, что лежит в вашей коробочке — похожее на переднюю панель банкомата — это мембранная клавиатура (именно она объясняет странное название сегодняшней статьи!).
Эти гибкие друзья — отличный способ создавать интересные проекты с паролями, выбором программ и многим другим. Мы выбрали именно версию 4x1, потому что её гораздо проще программировать, чем большие варианты.
К проектам!
Содержимое коробки #11
В этой коробке вы найдёте:
1x мембранная клавиатура на 4 кнопки
1x 5-контактная удлинённая гребёнка-пин
5x перемычки «папа-папа»
Занятия сегодняшнего дня
Сегодня мы покажем, как подключить мембранную клавиатуру, включая то, как соединены её контакты, а затем напишем несколько программ для работы с кнопками — в том числе секретную систему паролей для списка подарков!
Сегодня мы затронем несколько новых тем, так что приготовьтесь учиться!
Что такое мембранная клавиатура?
Эти клавиатуры немного похожи на четыре обычных кнопки, но с несколько иным подходом к контактам. Они компактны, тонки и имеют удобную клейкую основу для крепления к проектам.
Нам очень нравится работать с пронумерованными клавишами — с их помощью мы можем создавать программы с паролями и делать наши проекты в духе Джеймса Бонда!
У клавиатуры 5 контактов — 1 контакт строки и 4 контакта столбцов. Вам не нужно особо беспокоиться о строках и столбцах: просто считайте, что контакт строки подаёт 3,3 В на одну сторону клавиш, а остальные — это GPIO-пины, которые настраиваются как входы и определяют нажатия клавиш.
Когда вы нажимаете клавишу, контакт строки с 3,3 В соединяется с контактом клавиши и посылает сигнал HIGH на GPIO-пин, к которому он подключён.
Мы специально выбрали мембранную клавиатуру 4x1 для этого календаря, так как её проще подключать, программировать и понимать — код для неё остаётся простым и знакомым.
Сборка схемы
Как всегда, убедитесь, что Pico отключён от USB-кабеля при работе со схемой.
Подготовка макетной платы
Уберите слайдер со вчерашнего дня, но оставьте RGB-гирлянду — она нам понадобится в некоторых заданиях сегодня.
Ваша стартовая точка должна выглядеть вот так:
Установка клавиатуры
Сначала вставьте 5-контактную гребёнку в женский разъём клавиатуры. Она войдёт не до конца — не форсируйте, если остался зазор.
Затем вставьте другую сторону гребёнки в макетную плату. Мы разместили её примерно там, где был слайдер, вот так:
Теперь — провода. Можно ожидать, что контакты идут в том же порядке, что и клавиши, но, к сожалению, это не так! Это может немного запутать при подключении.
Вот карта контактов клавиатуры, GPIO-пинов, которые мы для них используем, и соответствующих клавиш:
Контакт клавиатуры |
1 |
2 |
3 |
4 |
5 |
GPIO Pico |
3V3 |
10 |
11 |
12 |
13 |
Клавиша |
3,3 В |
2 |
1 |
4 |
3 |
Надеемся, это поможет разобраться. Например, клавиша 1 на клавиатуре подключена к 3-му контакту, для которого мы будем использовать GPIO 11.
Можно почти не обращать внимания на последнюю строку и просто сосредоточиться на том, какой контакт клавиатуры к какому GPIO-пину подключён. Итак, подключаем:
Подключите контакт 1 (левый) к 3,3 В (физический пин 36)
Подключите контакт 2 к GPIO10 (физический пин 14)
Подключите контакт 3 к GPIO11 (физический пин 15)
Подключите контакт 4 к GPIO12 (физический пин 16)
Подключите контакт 5 к GPIO13 (физический пин 17)
Вот как должна выглядеть ваша макетная плата на данном этапе:
Установка блочного светодиода
Мы также вернём наш надёжный блочный светодиод со дня #2. Устанавливаем его на то же место, но с другим GPIO-пином:
Светодиод установлен в верхнем левом углу макетной платы (см. изображение ниже)
Резистор установлен между левой ножкой светодиода (анод +) и через центральный разрыв
Соедините ножку резистора с GPIO6 (физический пин 9)
Соедините правую ножку светодиода (катод -) с GND (физический пин 8)
Вот как должна выглядеть готовая схема:
Задание 1: Базовый тест клавиатуры
Давайте запустим супер-простой тест, чтобы убедиться, что кнопки регистрируются правильно.
Скрипт ниже настраивает контакты столбцов как входы, затем запускает простой цикл while с несколькими операторами if для проверки нажатий.
Попробуйте этот код и убедитесь, что всё работает правильно:
from machine import Pin
import time
# Set up column pins (inputs)
key1 = Pin(11, Pin.IN, Pin.PULL_DOWN)
key2 = Pin(10, Pin.IN, Pin.PULL_DOWN)
key3 = Pin(13, Pin.IN, Pin.PULL_DOWN)
key4 = Pin(12, Pin.IN, Pin.PULL_DOWN)
while True:
if key1.value() == 1:
print("Button 1")
if key2.value() == 1:
print("Button 2")
if key3.value() == 1:
print("Button 3")
if key4.value() == 1:
print("Button 4")
time.sleep(0.3)
Задание 2: Выбор цвета LED-гирлянды
Как насчёт проекта для установки разных цветов нашей RGB LED-гирлянды, где клавиатура позволяет менять цвета когда угодно?
Отлично подходит для смены настроения подсветки или декорации в зависимости от того, как идёт ваш день! Мы основываемся на статичном коде «чётных и нечётных» со вчерашнего дня.
Для этого мы введём кое-что новое в работе с функциями — передачу аргументов!
Что такое аргументы в функциях?
Мы уже показывали вам функции — блоки кода, которые мы можем вызывать и использовать в нашей программе когда угодно, удобно для вещей, которые мы хотим повторять.
Мы можем передавать информацию в эти функции с помощью аргументов, которые позволяют запускать один и тот же код функции, но с переданным в неё аргументом.
Например, следующая базовая функция устанавливает цвет красным, но здесь нет аргументов, поэтому мы не можем изменить этот цвет при каждом вызове:
def myfunction():
strand.fill((255,0,0))
Мы можем добавить аргумент в функцию и дать ему имя. Здесь мы называем аргумент colour. Обратите внимание, что строка strand.fill использует тот же аргумент colour в скобках:
def myfunction(colour):
strand.fill((colour))
Теперь при вызове функции мы можем добавить нужный цвет в скобках, что позволяет менять цвет при каждом вызове:
myfunction(255,0,0)
Вы можете добавить в функцию столько аргументов, сколько хотите — просто разделяйте их запятой. В примере кода ниже используются два аргумента, чтобы при каждом вызове задавать два цвета для наших светодиодов.
Код
Помимо аргументов, всё остальное в скрипте ниже должно быть вам знакомо. У нас есть цикл while, который ожидает нажатий кнопок, и когда кнопка нажата, он вызывает функцию и передаёт ей два аргумента (цветовые переменные).
Функция запускает цикл for, который устанавливает цвета, затем программа снова ожидает нажатий кнопок.
Мы использовали клавишу 4 для выключения (OFF), и клавиши 1, 2 и 3 для разных цветов.
Попробуйте:
from machine import Pin
import time
from neopixel import NeoPixel
# Set up column pins (inputs)
key1 = Pin(11, Pin.IN, Pin.PULL_DOWN)
key2 = Pin(10, Pin.IN, Pin.PULL_DOWN)
key3 = Pin(13, Pin.IN, Pin.PULL_DOWN)
key4 = Pin(12, Pin.IN, Pin.PULL_DOWN)
# LED details
GPIOnumber = 2
LEDcount = 15
# Define the strand pin number and number of LEDs
strand = NeoPixel(Pin(GPIOnumber), LEDcount)
# Colour variables
off = 0,0,0
green = 0,255,0
blue = 0,0,255
white = 255,255,255
yellow = 255,255,0
pink = 255,0,255
aqua = 0,255,255
# LED index list
ledindex = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
# Turn off all LEDs before program start
strand.fill((0,0,0))
strand.write()
time.sleep(1)
# Function with two arguments for colours
def blinky1(colour1,colour2):
for led in ledindex:
if (led % 2) == 0: #If the LED index is even
strand[led] = (colour1)
else: # If not (odd numbers)
strand[led] = (colour2)
strand.write()
while True:
time.sleep(0.1)
if key1.value() == 1:
blinky1(aqua,green)
elif key2.value() == 1:
blinky1(white,blue)
elif key3.value() == 1:
blinky1(yellow,pink)
elif key4.value() == 1:
blinky1(off,off)
Задание 3: Нажми для мигания
Другой компонент, который мы использовали с несколькими входами — DIP-переключатель со дня #5. Мы запускали с ним пример выбора программы, однако для определённых проектов клавиатура может быть лучшим выбором.
DIP-переключатель лучше подходит для чего-то, что вы хотите установить на более длительный период. Клавиатура больше подходит для вещей, которые вы хотите менять или контролировать постоянно — например, цвета или скорость.
Мы создадим короткий простой скрипт, предлагающий четыре разные скорости мигания для нашего блочного светодиода через каждую клавишу, но будем использовать несколько вложенных циклов while вместо операторов if, что позволит создать проект, требующий удержания клавиши для работы.
Это просто ещё один способ поиграть и показать гибкость этих вещей.
Код
У нас есть цикл while, который работает бесконечно, и внутри него вложенные циклы while. Каждый цикл while ожидает нажатия клавиши, и пока она нажата, устанавливает переменную под названием flash в разное значение.
У нас есть функция (program1), которую затем вызываем. Она использует значение переменной flash как задержку между включением и выключением блочного светодиода.
Всё просто — попробуйте и удержите палец на одной из клавиш:
from machine import Pin
import time
# Set up column pins (inputs)
key1 = Pin(11, Pin.IN, Pin.PULL_DOWN)
key2 = Pin(10, Pin.IN, Pin.PULL_DOWN)
key3 = Pin(13, Pin.IN, Pin.PULL_DOWN)
key4 = Pin(12, Pin.IN, Pin.PULL_DOWN)
# Set up our LED
blockLED = Pin(6, Pin.OUT)
# Create the flash variable
flash = 0
# Create our simple function
def program1():
blockLED.value(1) # LED ON
time.sleep(flash)
blockLED.value(0) # LED off
time.sleep(flash)
while True:
while key1.value() == 1:
flash = 1
program1()
while key2.value() == 1:
flash = 0.5
program1()
while key3.value() == 1:
flash = 0.1
program1()
while key4.value() == 1:
flash = 0.05
program1()
Задание 4: Никаких лишних нажатий!
Предыдущее задание требовало удерживать кнопки, но иногда это плохо…
Проблема с клавиатурами/кнопками при использовании для одиночного нажатия/ввода заключается в риске того, что программа обнаружит более одного ввода/нажатия, если пользователь держит палец слишком долго. Попробуйте это на первом задании — программа будет снова и снова выводить одну и ту же кнопку.
Мы добавили короткую задержку 0.3 для устранения дребезга в том задании, но это не идеальное решение. Существует метод в MicroPython под названием «прерывания», который можно использовать, однако он немного сложен и, мы думаем, это может быть слишком много для ваших первых двенадцати дней программирования.
Вместо этого мы используем простой подход, который нам нравится, потому что его легко читать и понимать, и он работает очень хорошо. Если работает — значит, работает!
Ждём когда все LOW
Когда мы нажимаем клавишу, мы посылаем на входной пин сигнал HIGH. Если мы держим палец слишком долго, код продолжает видеть этот сигнал HIGH и вызывает многократные нажатия.
Что мы можем сделать — заставить наш код проверять, что все четыре клавиши отпущены (то есть LOW или 0), прежде чем мы допустим какой-либо дальнейший ввод. Мы делаем это, создав переменную 0 или 1 (state) и используя её в нашем операторе if.
Это работает так (сначала быстро посмотрите код ниже):
Если state равно 0, мы позволяем нашему оператору if проверять нажатия кнопок. Если кнопка нажата, устанавливаем state в 1
При state = 1 (то есть кнопка только что была нажата), наш первый оператор if больше не может запускаться и проверять входы (так как требует state = 0)
Оператор elif установит state обратно в 0 только если все четыре клавиши LOW (0) и state равно 1
Как только вы отпустите только что нажатую клавишу, все клавиши теперь low и state равно 1, позволяя нашему оператору elif выполниться и установить state обратно в 0, то есть первый оператор if снова может проверять нажатия
Как проверить что все клавиши LOW
Стоит быстро упомянуть кое-что в коде ниже, чего вы раньше не видели. Наш оператор elif проверяет, что все клавиши LOW (0) вот так:
elif state == 1 and key1.value() == key2.value() == key3.value() == key4.value() == 0:
Конкретно говорим об этой части:
key1.value() == key2.value() == key3.value() == key4.value() == 0:
Это довольно говорящий само за себя код — вы просто связываете вместе (через ==) всё, что хотите проверить на одинаковое значение, и указываете желаемое значение в конце. В нашем примере мы хотим, чтобы все они имели значение 0 (LOW).
Вы также можете убрать значение, что говорит коду, что вы хотите, чтобы они все были одинаковыми, но вас не волнует, какое именно значение, вот так:
key1.value() == key2.value() == key3.value() == key4.value():
Код
Скопируйте это в Thonny и попробуйте удержать палец на клавише. Видите — дублей нет!
from machine import Pin
import time
# Set up column pins (inputs)
key1 = Pin(11, Pin.IN, Pin.PULL_DOWN)
key2 = Pin(10, Pin.IN, Pin.PULL_DOWN)
key3 = Pin(13, Pin.IN, Pin.PULL_DOWN)
key4 = Pin(12, Pin.IN, Pin.PULL_DOWN)
state = 0
while True:
time.sleep(0.1) # Short delay
# If state = 0, allow checking for keypress
if state == 0:
if key1.value() == 1:
print("Button 1")
state = 1
elif key2.value() == 1:
print("Button 2")
state = 1
elif key3.value() == 1:
print("Button 3")
state = 1
elif key4.value() == 1:
print("Button 4")
state = 1
# Only runs if state = 1 AND all keys are LOW
elif state == 1 and key1.value() == key2.value() == key3.value() == key4.value() == 0:
state = 0
Задание 5: Секретный список подарков (тссс!)
В этом задании происходит довольно много всего!
Мы сделаем программу, которая запрашивает у пользователя 4-значный пароль, а затем сравнивает введённое с сохранённым паролем. Если пароль верный, программа печатает наш секретный список подарков. Здесь мы также используем описанный выше метод для блокировки дублирующихся нажатий.
В этом примере кода происходит много всего, давайте обозначим, что он делает. Помните, обычно есть несколько способов достичь одного и того же с помощью кода — мы постарались показать что-то, что легко читается, разбирается и понимается, но не стесняйтесь комментировать ниже со своими скриптами/методами.
Наша программа делает следующее:
Создаёт два списка: один с нашим паролем (passcode) и пустой, в который мы будем сохранять нажатия кнопок (userentry)
Проверяет длину (len) списка userentry с помощью цикла while
Если она меньше 4 (пользователь ещё не закончил ввод) и state равно 0, проверяет следующее нажатие клавиши
Если клавиша нажата, мы используем функцию для добавления (append) этой клавиши в наш список userentry, выводим звёздочку (*) и меняем state на 1
Если длина уже равна 4 символам, пользователь завершил ввод, этот цикл while игнорируется и код переходит к операторам if и else ниже него
Если введённый код совпадает с нашим паролем, мы включаем светодиод и выводим список подарков
Если нет (else) — выводим сообщение об ошибке
Программа завершается после успеха или неудачи в любом случае
Давайте быстро разберём новые вещи, которые мы будем использовать в этом примере…
Append
Наш код начинается с пустого списка, который мы заполняем 4 числами, введёнными пользователем, но как это сделать? Мы используем метод append.
Наш список называется userentry и начинается так:
userentry = []
Чтобы добавить следующее нажатое число (нашу переменную под названием key) в этот список, мы используем строку в формате имя_списка.append(что_добавить).
Рабочий пример выглядит так. Добавлять можно в любой список — пустой или нет:
userentry.append(key)
Len
Мы используем len для возврата количества элементов в списке. Просто используем «len» с именем списка в скобках.
У нас есть цикл while, который проверяет длину нашего списка userentry вот так, чтобы проверить, введены ли пользователем все 4 символа («пока длина меньше 4»):
while len(userentry) < 4:
Глобальные переменные
В одной из наших функций мы используем эту строку:
global state
Если вы хотите изменить существующую переменную внутри функции, нам нужно использовать global. Если мы этого не делаем, она будет считаться локальной переменной (только частью функции) и не обновится для остальной части программы.
То же самое применяется, если вы создаёте переменную внутри функции с таким же именем, как одна вне функции — она будет считаться локальной только для функции, если не использовать global.
Трюки с print
Мы используем два новых трюка с функцией print:
end=»»
Мы используем это в нашем print, чтобы следующий print оставался на той же строке. Например, если мы запустим код ниже со стандартными строками print:
print("*")
print("*")
print("*")
Он выведет вот так:
*
*
*
Однако если мы добавим end=»» после запятой, вот так:
print("*", end="")
print("*", end="")
print("*", end="")
Вывод остаётся на той же строке — удобно для аутентичного стиля ввода пароля:
***
\n
Когда мы используем \n в начале нашей строки текста, она создаёт новую строку перед выводом.
Вот пример без этого:
print("turkey")
print("turkey")
print("turkey")
print("turkey")
Который выводит:
turkey
turkey
turkey
turkey
И вот пример, где вторая строка использует \n перед текстом:
print("turkey")
print("\nturkey")
print("turkey")
print("turkey")
Который выводит вот так:
turkey
turkey
turkey
turkey
Код
Внимательно прочитайте код ниже и пояснения выше, а затем попробуйте. Комментариев много, но вы всегда можете удалить эти строки если предпочитаете:
from machine import Pin
import time
# Set up column pins (inputs)
key1 = Pin(11, Pin.IN, Pin.PULL_DOWN)
key2 = Pin(10, Pin.IN, Pin.PULL_DOWN)
key3 = Pin(13, Pin.IN, Pin.PULL_DOWN)
key4 = Pin(12, Pin.IN, Pin.PULL_DOWN)
# Set up our LED
blockLED = Pin(6, Pin.OUT)
# Create list of presents
presents = ["Train set","Furby","Boomerang","YoYo"]
# Set your passcode in a list
passcode = [1,2,3,4]
# Empty list for the entered password
userentry = []
# Create state variable
state = 0
# Create keypress variable
key = 0
# Append function
def appendkey():
global state
userentry.append(key)
print("*", end="")
state = 1
# Delay + print function
def myprint(mytext):
print(mytext)
time.sleep(0.5)
## Start our program ##
print("") # Empty line
print("Welcome to the Secret Present List system")
time.sleep(1)
print("Enter the passcode to continue: ", end="")
# While userentry length is less than 4
while len(userentry) < 4:
time.sleep(0.1)
if state == 0:
if key1.value() == 1:
key = 1
appendkey()
elif key2.value() == 1:
key = 2
appendkey()
elif key3.value() == 1:
key = 3
appendkey()
elif key4.value() == 1:
key = 4
appendkey()
# If state is 1 and all keys are LOW
elif state == 1 and key1.value() == key2.value() == key3.value() == key4.value() == 0:
state = 0
else:
pass # Do nothing
# Program only gets this far if userentry is 4 characters long
# If the passcode is correct
if userentry == passcode:
blockLED.value(1) # LED on
print("\n") #Empty newline
print("----------------------")
print("*** ACCESS GRANTED ***")
myprint("----------------------")
myprint("Secret present list:")
# Print each present from our list
for i in presents:
myprint(i)
myprint("----------------------")
blockLED.value(0) # LED off
# If the keyed code is incorrect
else:
myprint("\n------------------")
myprint("INCORRECT PASSCODE")
myprint("ACCESS DENIED")
time.sleep(1)
День #11 завершён!
Надеемся, вы за-мем-бра-нете всё это… понимаете шутку?
Несмотря на то, что это относительно простое устройство ввода (в том виде, как мы его используем), надеемся, вам понравились разные способы применения этой мембранной клавиатуры. Просто наличие пронумерованных клавиш позволяет создавать захватывающие проекты с кодами доступа и многое другое.
Если вы позже найдёте более крупные клавиатуры, например варианты 8x8, всё нужно будет программировать немного по-другому, так как тогда контакты строк и столбцов должны будут взаимодействовать друг с другом… но это стоит рассмотреть в другой день.
Подведём итоги — сегодня вы:
Узнали, что такое мембранная клавиатура
Создали схему с мембранной клавиатурой (включая запутанный порядок контактов!)
Узнали об аргументах в функциях
Использовали вложенные циклы while для активации кнопок только при удержании
Нашли способ избежать лишних нажатий клавиш
Смело исследуйте **прерывания*, если чувствуете себя храбрым!*
Узнали, как проверить несколько состояний пинов одновременно (пример с LOW)
Узнали метод append для добавления в список
Узнали о len для возврата длины элемента
Узнали о глобальных переменных
Познакомились с новыми трюками print (end=»» и \n)
Завтра последний день :(
Оставьте всё на месте и отдохните, готовясь к нашему финальному компоненту завтра. Хорошего вечера!
Для создания изображений схем на макетной плате на этой странице использовался `Fritzing <https://fritzing.org/>`_.