Пользоваться приложением Microsoft Soundscape (почему-то доступным только для операционной системы iOS), позволяющим слепым ориентироваться в пространстве и узнавать, что находится вокруг них, при помощи голосовых подсказок, будет ещё удобнее, если изготовить внешний шестикнопочный пульт, предложенный тремя авторами Instructables — Jazz Ang, Ana Liu и Venkatesh Potluri.
Строго говоря, Soundscape — разработка не самой корпорации Microsoft, а её подразделения под названием Microsoft Research, занимающегося экспериментами и исследованиями в самых разнообразных областях. Считается, что даже внутренняя атмосфера там совсем иная, чем во всей остальной корпорации.
Назначение кнопок на пульте:
1 — пуск/стоп проигрывания аудио- или видеофайла, а при запущенном и работающем в фоне приложении Soundsdape — также включение/выключение голосовых уведомлений от этой программы.
2 — полное включение/выключение звука, аналог кнопки Mute на пульте телевизора.
3 — переход к следующей дорожке в аудио- или видеопроигрывателе, а в программе Soundscape — переход к следующему голосовому уведомлению.
4 — переход к предыдущей дорожке или голосовому уведомлению.
5 — дублирует кнопку Home.
6 — управление режимом «Виртуальный контроллер» операционной системы iOS (позволяет пользоваться внешними пультами наподобие описываемого).
Как видно из схемы самоделки, составленной в программе Fritzing, кнопки, каждая из которых дополнена подтягивающим резистором на 10 кОм, подключены к выводам 11, 7, 15, 16, 27 и 25 довольно дорогой платы Adafruit Feather nRF52 Bluefruit LE, которая совместима с Arduino IDE и содержит Bluetooth-модуль. При пользовании устройством эта плата питается по стандартному Micro USB-кабелю от пауэрбанка, а при программировании — от ПК. Следует учитывать, что многие пауэрбанки при слишком малом потребляемом токе отключаются автоматически, причём порог отключения зависит от модели.
Собрав конструкцию по схеме, мастера настраивают Arduino IDE для работы с этой платой, как показано на следующих двух скриншотах:
Настроив всё, мастера заливают в плату скетч:
#include
#define CONTROL_PLAY_PAUSE 0x00CD
#define CONTROL_SCAN_NEXT 0x00B5
#define CONTROL_SCAN_PREVIOUS 0x00B6
#define CONTROL_MUTE 0x00E2
#define AC_FORWARD 0x0225
#define AC_BACK 0x0224
#define CONSUMER_BROWSER_HOME 0x0223
BLEDis bledis;
BLEHidAdafruit blehid;
bool hasKeyPressed = false;
//connect pins in the board
int playpauseButtonPin = 11;
int muteButtonPin = 7;
int nextButtonPin = 15;
int backButtonPin = 16;
int homeButtonPin = 27;
int switchControlButtonPin = 25;
void setup()
{
pinMode(playpauseButtonPin, INPUT);
pinMode(muteButtonPin, INPUT);
pinMode(nextButtonPin, INPUT);
pinMode(backButtonPin, INPUT);
pinMode(homeButtonPin, INPUT);
pinMode(switchControlButtonPin, INPUT);
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Bluefruit.begin();
Bluefruit.setTxPower(4); // Check bluefruit.h for supported values
Bluefruit.setName("TESTArroundMeBluefruit52");
// Configure and Start Device Information Service
bledis.setManufacturer("Adafruit Industries");
bledis.setModel("Bluefruit Feather 52");
bledis.begin();
/* Start BLE HID
* Note: Apple requires BLE device must have min connection interval >= 20m
* ( The smaller the connection interval the faster we could send data).
* However for HID and MIDI device, Apple could accept min connection interval
* up to 11.25 ms. Therefore BLEHidAdafruit::begin() will try to set the min and max
* connection interval to 11.25 ms and 15 ms respectively for best performance.
*/
blehid.begin();
/* Set connection interval (min, max) to your perferred value.
* Note: It is already set by BLEHidAdafruit::begin() to 11.25ms - 15ms
* min = 9*1.25=11.25 ms, max = 12*1.25= 15 ms
*/
/* Bluefruit.Periph.setConnInterval(9, 12); */
// Set up and start advertising
startAdv();
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_KEYBOARD);
// Include BLE HID service
Bluefruit.Advertising.addService(blehid);
// There is enough room for the dev name in the advertising packet
Bluefruit.Advertising.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
}
//using this function to control phone
void sendCommand(uint16_t command) {
// Make sure we are connected and bonded/paired
for (uint16_t conn_hdl=0; conn_hdl < BLE_MAX_CONNECTION; conn_hdl++)
{
BLEConnection* connection = Bluefruit.Connection(conn_hdl);
if ( connection && connection->connected() && connection->paired() )
{
// Turn on red LED when we start sending data
digitalWrite(LED_RED, 1);
Serial.println("Sending command...");
// Send key press
blehid.consumerKeyPress(conn_hdl, command);
// Delay a bit between reports
delay(10);
// Send key release
blehid.consumerKeyRelease(conn_hdl);
// Turn off the red LED
digitalWrite(LED_RED, 0);
Serial.println("Command sent!");
}
}
delay(250);
}
//using this function to configure with switch control
void sendSwitchControl(String command) {
for (int i = 0; i < command.length(); i++) {
blehid.keyPress(command.charAt(i));
Serial.write(command.charAt(i));
delay(5);
}
delay(250); // avoid sending the same command again and again
}
void loop()
{
//button to play or pause players
if (digitalRead(playpauseButtonPin) == HIGH) {
sendCommand(CONTROL_PLAY_PAUSE);
}
//button to mute the phone
if (digitalRead(muteButtonPin) == HIGH) {
sendCommand(CONTROL_MUTE);
}
//button to play next media
if (digitalRead(nextButtonPin) == HIGH) {
sendCommand(CONTROL_SCAN_NEXT);
sendCommand(AC_FORWARD);
}
//button to play last media
if (digitalRead(backButtonPin) == HIGH) {
sendCommand(CONTROL_SCAN_PREVIOUS);
sendCommand(AC_BACK);
}
//button to go back home
if (digitalRead(homeButtonPin) == HIGH) {
sendCommand(CONSUMER_BROWSER_HOME);
}
//switch control 'select'
if (digitalRead(switchControlButtonPin) == HIGH) {
sendSwitchControl("SELECT");
if (pressed){
blehid.keyRelease(switchControlButtonPin);
pressed=false;
}
}
delay(5);
}
Со стороны операционной системы iOS мастера настраивают сначала сопряжение с пультом по Bluetooth:
Затем — функции всех кнопок в разделе специальных возможностей:
Убедившись, что всё работает, мастера печатают на 3D-принтере толкатели для кнопок и делятся необходимыми для этого файлами с читателями на Thingiverse под лицензией CC-BY 3.0. Эти детали можно изготовить и другими способами, либо не изготавливать вообще.
На следующих двух видео показано, как мастера испытывают пульт в действии:
Остаётся пересобрать пульт по той же схеме, но пайкой, и поместить результат в корпус, а в iOS установить приложение Soundscape и запустить его в фоне, и устройством можно пользоваться.