12.09.2024: Макроклавиатурный четверг

Проснулся, позавтракал и, пока не началось, занялся разными делишками. Одним из дел будет вспомнить как вообще шить и программировать контроллер Raspberry Pi Pico. Давненько я этим не занимался.

Ну и, само-собой, первое же включение IDE Thonny превратилось в тот ещё квест.

Нет, саму IDE я запустил без проблем. И контроллер к компьютеру подключил.

Вот только Thonny упорно тычет мне в лицо ошибку: Unable to connect to COM5: port not found.

Чего я только не делал. И контроллер менял, и заливал в него свежий microPython.

Кто не знает, чтобы залить свежий microPython, нужно зайти по ссылке, которая выше по тексту, скачать свежий релиз (там будет файл с расширением uf2), на плате контроллера Raspberry Pi Pico зажать кнопку boot, и не отпуская подключить к USB компьютера. После этого контроллер будет опознаваться как флешка. На эту «флешку» нужно скинуть скачанный uf2-файл. И всё, контроллер прошит.

Увы, но и это не помогло.

А помогло ткнуть Thonny в правый нижний угол и выбрать действующий COM-порт. В моём случае, девятый.

И, поскольку на плате есть RGB-светодиод, то в тестовом скетче прописал, чтобы при инициализации, с пятисекундной задержкой, он светил сначала красным, а как загрузится — зелёным.

# Библиотека для работы с пинами ввода-вывода
from machine import Pin
# Библиотека для работы с временем и массивами
import array, time

import rp2
from rp2 import PIO, StateMachine, asm_pio

# Светодиод в режим выхода на 25 пине
led = Pin(25, Pin.OUT)

# Configure the number of WS2812 LEDs.
NUM_LEDS = 1

@asm_pio(sideset_init=PIO.OUT_LOW, out_shiftdir=PIO.SHIFT_LEFT, autopull=True, pull_thresh=24)
def ws2812():
    T1 = 2
    T2 = 5
    T3 = 3
    label("bitloop")
    out(x, 1)               .side(0)    [T3 - 1] 
    jmp(not_x, "do_zero")   .side(1)    [T1 - 1] 
    jmp("bitloop")          .side(1)    [T2 - 1] 
    label("do_zero")
    nop()                   .side(0)    [T2 - 1]
    
# Create the StateMachine with the ws2812 program, outputting on Pin(23).ws2812引脚
sm = StateMachine(0, ws2812, freq=8000000, sideset_base=Pin(23))

# Start the StateMachine, it will wait for data on its FIFO.
sm.active(1)

# Display a pattern on the LEDs via an array of LED RGB values.
ar = array.array("I", [0 for _ in range(NUM_LEDS)])

time.sleep_ms(20)

print("red")
for j in range(0, 20): 
    for i in range(NUM_LEDS): 
        ar[i] = j<<8 
    sm.put(ar,8) 
    time.sleep_ms(50)
    
time.sleep_ms(5000)

print("green")
for j in range(0, 20): 
    for i in range(NUM_LEDS): 
        ar[i] = j<<16 
    sm.put(ar,8) 
    time.sleep_ms(50)
    
time.sleep_ms(20)
led.value(0)
 
while True:
    # Ждём 1 микросекунду
    time.sleep(1)

Вот такой получился скетч.

Заодно отключил встроенный синий светодиод на пятом пине, чтобы лишнее электричество не кушал.

Работает.

Как сделать обычную макроклавиатуру из контроллера Raspberry Pi Pico

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

microPython ImportError: no module named 'board'

Стал разбираться. Оказалось, что для работы этого модуля нужен не microPython, а circuitPython.

И это плохо, потому, что я не смогу использовать уже написанный код для управления RGB-светодиодом. С одной стороны, для экономии электричества, его можно и выключить. Но вот с другой…

Пошёл качать circuitPython. Прошил его в контроллер Raspberry Pi Pico точно так же как прошивал microPython (т.е. см. выше по тексту).

Вот со скетчем пришлось повозиться.

Во-первых, библиотека, эмулирующая клавиатуру, сама по себе в Thonny не установлена. Установить её через плагины или пакеты — не получилось. Даже когда пытался это сделать из скачанного архива.

Библиотеку скачивал тут.

Затем, распаковать, достать из неё папку adafruit_hid и закинуть в папку lib на флешку, которой прикидывается контроллер Raspberry Pi Pico.

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

Моя рабочая клавиатура это умеет по комбинации Fn+F4, но прожимая её одной рукой, можно случайно порваться. Да и вслепую такое нажать довольно непросто. Но спасибо что хоть так.

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

Удобно? Удобно!

И вроде тестовый скетч всего на несколько строчек. Плёвое дело, казалось бы. Но нет! Началось то самое «во-вторых».

Код клавиши Mute выглядит как 0xAD (или 173, если по нашему, по десятичному). Вот только класс Keycode такой константы не содержит!

Ладно, зато класс Keyboard может кушать сразу код клавиатуры, без всяких прослоек вроде Keycode, но… нет.
Сколько я ему не скармливал 0xAD, 173 и прочие коды мьюта, которые удавалось найти в сети, он на них чихать хотел.

Помогло использование классов ConsumerControl и ConsumerControlCode.

Т.е. всякие «дорого-богатые» клавиши буду использовать через эти классы.

Сейчас скетч выглядит примерно так.

# Библиотека для работы с временем
import time

# Для эмуляции клавиатуры
import digitalio
import board
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
from adafruit_hid.consumer_control import ConsumerControl
from adafruit_hid.consumer_control_code import ConsumerControlCode

# Инициализирую клавиатуру
keyboard = Keyboard(usb_hid.devices)
consumer = ConsumerControl(usb_hid.devices)

# Пины кнопок
btn1_pin = board.GP15
btn1 = digitalio.DigitalInOut(btn1_pin)
btn1.direction = digitalio.Direction.INPUT
# Пусть принимает 3.3 V
btn1.pull = digitalio.Pull.DOWN

time.sleep(5) # Нужно чтобы дистанционный контроллер успел загрузиться

while True:
    if btn1.value:
        print("Кнопка нажата")
        consumer.send(ConsumerControlCode.MUTE) #0xAD
        time.sleep(0.5)
        keyboard.release_all()
        keyboard.release_all() #0xAD
    # Ждём 1 микросекунду
    time.sleep(0.1)

И, я проверил, звук включает/выключает. На этом пришлось прерваться с опытами и стартовать рабочий день.

Поискал как работать с RGB-светодиодом, напаянном на этом моде контроллера, и понял что готовое решение есть только на микроПитоне. Ну или не только, но я дольше искать буду, а оно мне не надо.

Перекинул скетч на другой контроллер, без светодиода.

Как только рабочий день закончился, я засел проектировать мою дистанционную макроклавиатуру. Ну и скетч доработал. Теперь в нём 12 кнопок.

Из них 8 будут нажиматься из Home Assistant контроллером ESP (его программировал ещё в конце августа), а 4 кнопки сделаю физическими.

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

Придумал вот что. Взял «дырчатую» макетку, припаял к ней гребёнки с кроватками.

Так, в случае чего, контроллеры можно запросто снять и заменить.

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

Пины обоих контроллеров закоммутировал между собой напрямую вот по этой схеме.

Цифра перед номером пина — это номер кнопки. И да, первая кнопка у меня типа ноль.

А что? Там и там логическая единица 3.3 V. Оба питаются от USB-порта компьютера. Не вижу смысла колхозить сюда гальваническую развязку.

Пины с GP8 по GP11 (на Raspberry Pi Pico) зарезервированы под «хардварные» кнопки.

ESP8266 запитал от Raspberry Pi Pico.

  • Подал 5 V с пина VSYS (Raspberry Pi Pico) на пин VIN (ESP8266). На фото это красный провод.
  • И пины G («земля») соединил. Чёрный провод.

Поскольку Raspberry Pi Pico всегда подключена по USB, будет удобно работать с макросами. Запускаешь IDE Thonny, и вписываешь что нужно.
А скрипт ESP8266 редактируется прямо в Home Assistant и шьётся «по воздуху».

В общем, то что получилось — нравится. Сразу повесил три макроса: блокировка экрана, разблокировка экрана, включение/выключение звука.

Тайминги проставил такие как в примере.

if btn1.value:
    print("Кнопка 1 нажата")
    #===========МАКРОС==========
    time.sleep(0.5)
    keyboard.press(Keycode.L, Keycode.WINDOWS)
    time.sleep(0.2)
    keyboard.release(Keycode.L, Keycode.WINDOWS)
    #===========================

Т.е. перед срабатыванием макроса, скрипт ждёт пол-секунды.

Этого хватает, чтобы сигнал приходящий от ESP8266 не запускал макрос дважды.

Затем, нажимается комбинация клавиш Wil-L, а через 0.2 сек эти клавиши отпускаются.

Теперь утром, не вставая с постели, я могу включить компьютер, разблокировать экран, а когда он загрузится, запустить рабочее окружение и запитать рабочий стол. И в кресло сяду когда всё будет готово к работе. Разве это не прекрасно?

Так! Осталось припаять кнопки и разместить всё в корпусе так, чтобы нигде ничего не коротнуло.

Напишите комментарий

Введите имя

Введите адрес электронной почты

Введите адрес вашего сайта

Нажмите эту кнопку, чтобы отправить комментарий.

Введите текст комментария