Да, я каждый день на созвонах в Zoom. Да, у меня есть ребёнок, который часто вмешивается в эти звонки. Алгоритм выдачи рекламы Instagram, ты победил, мне нужна эта кнопка.
Но у меня есть предубеждения. В Instagram рекламируется проект с Kickstarter. К тому же, я не хочу делать свой вклад в доходы от рекламы Facebook, даже нажимая на этот ролик. Выражение Дженни Оделл Бесплатных часов не бывает полностью описывает мою точку зрения на качество продуктов в рекламе Instagram. Кроме того, мой лимит на финансирование проектов с Kickstarter практически исчерпался.
Я поддерживал множество проектов на Kickstarter, и иногда результат этих проектов напоминал мне гамбургер, который Майкл Дуглас получил в фильме С меня хватит!.
Так что давайте соберём такую кнопку сами.
Первое, о чём стоит задуматься: какую кнопку мне будет приятно нажимать?
Я люблю клавиатуры с переключателями Cherry MX. Существует три типа механических переключателей: линейные, тактильные и кликающие. Линейный простейший тип переключателя, перемещающийся вверх-вниз почти без обратной связи. У тактильных переключателей в середине хода есть выступ, позволяющий почувствовать, когда произошло нажатие клавиши. А кликающие переключатели имеют более сильную тактильную обратную связь И производят слышимый клик при нажатии.
В обычной ситуации мы бы купили тестер переключателей, чтобы разобраться, какой тип нам отзывается, а ещё опросили бы своих коллег, какой звук должна издавать клавиатура, чтобы они нас не убили. Но мы посередине пандемии COVID, поэтому коллег рядом нет! Выберем переключатель Cherry MX Blue с удобной тактильной обратной связью, который при этом чрезвычайно громкий. На сайте Cherry MX этот переключатель называют кликающим и заметным, но это ещё очень слабо сказано.
Выглядит красиво, но можно придумать и кое-что получше. Если мне приятно нажимать переключатель Cherry MX Blue, то не будет ли ещё приятнее нажимать комически большой Cherry MX Blue?
И это Novelkeys Big Switch.
Он в 4 раза больше по каждой из размерностей и в 64 раза больше по объёму, чем обычный переключатель. У него даже есть огромный колпачок!
К сожалению Большой Переключатель не продаётся в корпусе, поэтому мне пришлось воспользоваться 3D-печатью. Красивый корпус нашёлся на Thingiverse: NovelKeys Big Switch Case. Всегда стоит поискать ремиксы на случай, если кто-нибудь решил усовершенствовать исходный дизайн. В данном случае нашёлся ремикс, в который добавлен отсек для Pro Micro, а переключатель устанавливается более плотно, поэтому я напечатал его.
Теперь, когда у нас есть корпус, нужна плата, которую мы вставим в него и прикрепим к переключателю.
У Pro Micro есть чип ATmega32U4, позволяющий эмулировать устройство USB HID, например, USB-клавиатуру. К тому же, эта плата имеет маленький размер.
В нижней части Большого Переключателя есть два металлических контакта.
При нажатии клавиши внутри переключателя происходит замыкание цепи между этими двумя контактами.
Взглянем на расположение контактов Pro Micro:
Можно подключить GND к одному металлическому контакту, а Pin 2 ко второму. Pin 2 это контакт цифрового ввода-вывода, который считывает HIGH, когда клавиша нажата, и LOW, когда нет.
Было бы ещё здорово иметь какой-нибудь наглядный индикатор состояния Mute, поэтому можно добавить светодиод.
Я заказал светодиод размером 10 мм:
И резистор на 220 ом:
Длинная нога светодиодов подключается к PWR, а короткая к GND. Мы вставим резистор между длинной ногой и другим контактом, чтобы снизить величину тока. Я выбрал Pin 9 в нижней части платы. Короткую ногу я соединил с GND. Мне показалась полезной эта страница о светодиодах и резисторах.
Между платой и переключателем я припаял такой провод 20 AWG:
В результате получилась вот такая конструкция:
Просто запихнём всё это в наш напечатанный корпус:
Теперь нужно написать код.
Я начал с кода, который Sparkfun написала для создания огромной Кнопки Сохранения, и немного его изменил.
Принцип заключается в следующем: при нажатии клавиши она посылает сочетание горячих клавиш Zoom включения и отключения звука (на Mac это Cmd-Shift-A). Нужно будет изменить настройки Zoom, чтобы это сочетание клавиш распознавалось, даже когда Zoom находится не в фокусе. Поставим флажок Enable Global Shortcut:
Также мы хотим включать или выключать светодиод после каждого нажатия на клавишу. Я решил, что светодиод будет аналогом лампочки В эфире когда синий светодиод горит, звук у меня включен и люди слышат, что я говорю.
Но если просто включать-выключать светодиод при каждом нажатии на клавишу, то как сохранять синхронизацию с состоянием Mute в самом Zoom?
Удобство Pro Micro заключается в том, что она имеет и последовательное подключение. Обычно оно используется для печати отладочной информации в Arduino IDE, но мы можем использовать его для обеспечения синхронизации с состоянием включения звука в Zoom.
Вот код, который мы загружаем в саму Pro Micro:
#include "Keyboard.h"// OS parameterstypedef enum { LINUX, WINDOWS, MAC} os_types;// Change this to your operating systemconst os_types OS = MAC;// Pinsconst int btn_pin = 2;const int led_pin = 9;// Constantsconst int debounce_delay = 50; // ms// Globalsint btn_state = HIGH;int btn_prev = HIGH;unsigned long last_debounce_time = 0;int os_ctrl;int led_state = LOW;void setup() { Serial.begin(57600); // opens serial port, sets data rate to 57600 bps // Set up LED and button pins pinMode(btn_pin, INPUT_PULLUP); // Set the button as an input pinMode(led_pin, OUTPUT); digitalWrite(led_pin, led_state); // Begin keyboard Keyboard.begin(); // Switch to correct control/command key switch(OS){ case LINUX: case WINDOWS: os_ctrl = KEY_LEFT_CTRL; break; case MAC: os_ctrl = KEY_LEFT_GUI; break; default: os_ctrl = KEY_LEFT_CTRL; break; } // Get initial timestamp Serial.println("started"); }void loop() { // Read current state of the button int btn_read = digitalRead(btn_pin); // Remember when the button changed states if ( btn_read != btn_prev ) { last_debounce_time = millis(); } // Wait before checking the state of the button again if ( millis() > (last_debounce_time + debounce_delay) ) { if ( btn_read != btn_state ) { btn_state = btn_read; if ( btn_state == LOW ) { // Send cmd+shift+a Keyboard.press(KEY_LEFT_SHIFT); Keyboard.press(os_ctrl); Keyboard.press('a'); delay(100); Keyboard.releaseAll(); Serial.println("pressed"); if (led_state == LOW) { led_state = HIGH; } else { led_state = LOW; } digitalWrite(led_pin, led_state); } } } // Remember the previous button position for next loop() btn_prev = btn_read; if (Serial.available() > 0) { String incomingString = Serial.readStringUntil('\n'); if (incomingString == "muted") { led_state = LOW; } else if (incomingString == "unmuted") { led_state = HIGH; } digitalWrite(led_pin, led_state); } }
Дальше мы можем добавить Applescript, сообщающий о текущем состоянии Zoom. Я нашёл Zoom-плагин для устройства Streamdeck, содержавший исходный Applescript, и изменил его так, чтобы он сообщал о том, открыт ли Zoom, и каково состояние его звука. Также я модифицировал скрипт, чтобы он выводил JSON.
set zoomStatus to "closed"set muteStatus to "disabled"tell application "System Events"if exists (window 1 of process "zoom.us") thenset zoomStatus to "open"tell application process "zoom.us"if exists (menu bar item "Meeting" of menu bar 1) thenset zoomStatus to "call"if exists (menu item "Mute audio" of menu 1 of menu bar item "Meeting" of menu bar 1) thenset muteStatus to "unmuted"elseset muteStatus to "muted"end ifend ifend tellend ifend tellcopy "{\"mute\":\"" & (muteStatus as text) & "\",\"status\":\"" & (zoomStatus as text) & "\"}" to stdout
Если теперь мы запустим его во время звонка в Zoom, то он будет выводить примерно такое:
$ osascript get-zoom-status.scpt{"mute":"muted","status":"call"}
Затем я написал небольшое приложение на Node, используемое в качестве посредника между Pro Micro и этим скриптом:
const { exec } = require('child_process');const SerialPort = require('serialport');const Readline = require('@serialport/parser-readline');const port = new SerialPort('/dev/tty.usbmodemHIDPC1', { baudRate: 57600});var checkStatus = function() { console.log('Checking status...'); exec('osascript get-zoom-status.scpt', (error, stdout, stderr) => { if (error) { console.error(`exec error: ${error}`); return; } var status = JSON.parse(stdout); if (status.mute == 'unmuted') { port.write('unmuted'); } else { port.write('muted'); } });}const parser = port.pipe(new Readline({ delimiter: '\r\n' }))parser.on('data', function (data) { if (data == "pressed") { console.log('Button pressed.'); checkStatus(); }})checkStatus();setInterval(checkStatus, 30000);
Этот скрипт выполняет две задачи. При нажатии кнопки он отправляет плате Pro Micro через последовательный порт команду нажато, при этом запускается Applescript для определения текущего состояния звука в Zoom. Затем он отправляет плате Pro Micro команду звук выключен или звук включен, благодаря чему светодиод переключается в соответствующее состояние. Также я создал таймер, запускающий скрипт через каждые 30 секунд на случай, если я случайно отключу или включу звук через интерфейс Zoom, а не через кнопку, в противном случае состояние будет обновляться только при нажатии кнопки.
Вот как выглядит кнопка при использовании в звонке через Zoom:
Пожалуйста, поддержите мой Kickstarter шучу, у меня нет никакого Kickstarter, но, надеюсь, теперь вы сможете создать такую кнопку сами.
На правах рекламы
Закажите сервер и сразу начинайте работать! Создание VDS любой конфигурации в течение минуты. Эпичненько :)