【完整版】零延遲、無誤觸。從零開始自製 Android 專用「通用物理觸發按鈕」專業指南

【完整版】零延遲、無誤觸。從零開始自製 Android 專用「通用物理觸發按鈕」專業指南

|工具

這篇文章將為深受雨天誤觸、喚醒延遲所苦的外送員,或是追求 Android 設備操作效率極致的極客們,詳細介紹這款由現場實戰經驗孕育而成的「通用物理觸發按鈕」完整製作步驟。

製作時的 X 推文 1 製作時的 X 推文 2 製作時的 X 推文 3 製作時的 X 推文 4 製作時的 X 推文 5

1. 概念:為何走向「自製」之路?

本指南專注於實用的軟硬體建構步驟,但關於這款「終極實體按鈕」誕生前,在雨中路上經歷的多次失敗與試錯過程,我已記錄在下方的 note 文章中。若您對為何市售產品行不通、以及開發過程中那段土法煉鋼的故事感興趣,歡迎一併參考。

市面上販售的廉價藍牙按鈕或媒體控制器,在業務等級的嚴苛環境(雨天、連續運作、需要瞬間決策的操作)下,存在著致命的弱點:

  • 喚醒延遲: 由於省電功能,在緊急需要按下時,第一次點擊往往無法被識別。

  • 排版干擾: 在特定應用(如 Uber Eats)中,市售產品的輸入訊號可能會引發預期之外的操作。

  • 彈跳效應(Chattering)與進水: 物理密封性不足,在雨天的高濕度環境下或水滴影響下,會頻繁發生誤操作(彈跳效應)。

為了從根本解決這些課題,本專案採用**使用 Arduino 相容微控制器並以 C++ 自行開發韌體,讓 OS 將其識別為「具備最佳行為模式的媒體遙控器」**的方法。藉此建構出一個流暢、無延遲且可靠的操作環境。

2. 必備零件(BOM:物料清單)

以下是在秋葉原電子零件行或 Amazon 皆可取得、經實戰驗證過的零件清單。選材皆以穩定性為優先考量。

零件名稱型號 / 詳細規格最低參考價格(含稅)取得管道選定理由
微控制器基板Seeed Studio XIAO nRF528401,800日圓千石電商超小型、支援 Bluetooth 5.0、內建電池充放電電路。此用途的最佳解。
Amazon
電池鋰聚合物充電電池 3.7v 110mAh880日圓千石電商可容納於 XIAO 的尺寸內,並確保足夠的驅動時間。
Amazon
機械開關Kailh Choc Brown(茶軸)60日圓/個遊舍工房採用低平、且具明確點擊感的茶軸。能有效防止誤觸。
遊舍工房
鍵帽chocfox CFX 1U keycap550日圓/10個遊舍工房專為 Choc 開關設計,扁平且易於按壓。
遊舍工房
配線材廢棄 USB 線等內部的線芯0日圓廢材利用細緻且柔軟,適合在極小空間內佈線。
外殼3D 列印成品等-自製也可使用百元商店的容器自製,我是使用 3D 印表機製作。

3. 必備工具與消耗品

由於屬於精密作業,準備合適的工具是成功的關鍵。

  • 烙鐵: 建議選擇具溫度調節功能的。烙鐵頭不要太細,熱傳導效果較佳。

  • 焊錫: goot 高密度實裝用 SD-81 (0.6mm)\ Amazon - 固定器具: 支撐架 軟管臂型\ Amazon - 黏性凝膠雙面膠: Can Do 製(厚度 1.8mm)※我的 3D 列印外殼尺寸是按照 1.8mm 厚度設計的,若您自行製作外殼,則不一定需要此膠,或可採取其他方式固定基板與電池。

4. 硬體實裝:確實組裝的奧義

此步驟的失誤會直接導致功能失效。特別是焊接的基本功與電池處理,請務必謹慎小心。

4-1. 配線準備

剝開廢棄充電線(USB 線等)的外皮,取出內部細如髮絲的導線(芯線)。截取所需長度,將兩端披覆層剝除 2~3mm,預先沾上一點點焊錫(預錫)。

4-2. 開關與基板的焊接

連接 XIAO nRF52840 與機械開關。

  1. 連接位置:

    • 按鈕 1(主按鈕):開關的一側接腳連至 XIAO 的 D0,另一側連至 GND

    • 按鈕 2(副按鈕):開關的一側接腳連至 XIAO 的 D1,另一側連至 GND

      (※因為 GND 是共用的,開關側可以像雛菊鏈一樣串聯起來)

  2. 焊接訣竅(預熱):

    焊錫無法附著的主因通常是「熱量不足」。請將烙鐵頭同時接觸基板焊盤與導線,預熱 1~2 秒後再送入焊錫。當焊錫如富士山般均勻擴散即為成功。若呈現圓球狀,則代表熱量或助焊劑不足。

4-3. 電池連接(警告:嚴防短路)

電池直接焊接至基板背面的電池接點(BAT+BAT-)。

  • 嚴禁短路: 切割鋰電池配線時,絕對不要用斜口鉗同時切斷正負極線。 這會透過刀刃造成短路,引發起火風險。請務必一條一條切斷,並將另一條線用絕緣膠帶包覆保護。

  • 連接順序: 為安全起見,先焊接負極(黑線)至基板的 BAT-,隨後再焊接正極(紅線)至 BAT+

焊接01

焊接02

焊接03

4-4. 組裝與固定(防斷線對策)

在微型電子製作中,最常見的問題就是「導線根部斷線」。

焊接完成後,請不要隨意搬動基板與配線。

為了防止斷線,焊接完成後請立即使用準備好的 1.8mm 厚凝膠雙面膠,將基板、電池與機械開關一併固定在 3D 列印外殼內。此膠帶的厚度與彈性兼具了衝擊吸收與優化配置的功能。

※1.8mm 厚凝膠雙面膠是為了收納進我自製的外殼,若採用自定義環境建構,可以使用其他固定方式。 ※建議立即用膠帶固定,因為在進行後續步驟時移動基板,極易導致配線斷裂。

5. 軟體:燒錄韌體

注入靈魂,讓微控制器發揮「高效藍牙按鈕」的功能。

5-1. Arduino IDE 的設定與燒錄

請至 https://www.google.com/search?q=https://www.arduino.cc/en/software 安裝 Arduino IDE,並加入必要的開發板套件(Seeed nRF52 mbed-enabled Boards)。

288

啟動安裝好的 IDE,並使用 USB 線將 Seeed Studio XIAO nRF52840 連接至電腦。

  1. 搜尋開發板: 從選單開啟 Select Other Board and Port,搜尋並選擇 Seeed Studio XIAO nRF52840

    (※注意:Seeed Studio XIAO 系列有「無印(SAMD21)」、「ESP32C3」等名稱相似的別款基板。請務必指定「nRF52840」,選錯將無法燒錄)

    開發板選擇: 搜尋後選單會出現 Seeed Studio XIAO nRF52840,請選取。注意區分 Plus 或 Seeed 等版本差異。

  2. 選擇連接埠:

    Tools -> Port 選擇與 Seeed Studio XIAO nRF52840 連接的埠號(如 COM Port)。※mac 版的 tools 不在視窗內,而是在上方任務列。

    燒錄:確認開發板與連接埠準備就緒後,請將以下程式碼貼入中央的文字輸入區。

#include <bluefruit.h>

// --- 自訂設定 ---
#define DEVICE_NAME "D-Remote" // 可更改為 11 個字元以內的喜好名稱
#define BTN1_PIN 0             // D0 (音量加大 / 長按功能)
#define BTN2_PIN 1             // D1 (音量減小)
// ----------------------

BLEDis bledis;
BLEBas blebas; // 電池服務
BLEHidGeneric blehid(1, 0, 0);

static uint16_t inputReportLen[] = { 1 };
#define REPORT_ID_CONSUMER_CONTROL 1

// 媒體遙控器設計圖
uint8_t const hidReportDescriptor[] = {
  0x05, 0x0C,                   // Usage Page (Consumer Devices)
  0x09, 0x01,                   // Usage (Consumer Control)
  0xA1, 0x01,                   // Collection (Application)
  0x85, REPORT_ID_CONSUMER_CONTROL, // Report ID (1)
  0x15, 0x00,                   // Logical Minimum (0)
  0x25, 0x01,                   // Logical Maximum (1)
  0x75, 0x01,                   // Report Size (1 bit)
  0x95, 0x03,                   // Report Count (3)
  0x09, 0xE9,                   // Usage 1: Volume Increment (Bit 0)
  0x09, 0xEA,                   // Usage 2: Volume Decrement (Bit 1)
  0x09, 0xCD,                   // Usage 3: Play/Pause       (Bit 2)
  0x81, 0x02,                   // Input (Data, Variable, Absolute)
  0x75, 0x01,                   // Report Size (1 bit)
  0x95, 0x05,                   // Report Count (5) - Padding
  0x81, 0x03,                   // Input (Constant, Variable, Absolute)
  0xC0                          // End Collection
};

// 計算真實電池剩餘百分比的函式
uint8_t getBatteryPercentage() {
  // 開啟電池電壓讀取開關 (VBAT_ENABLE)
  pinMode(VBAT_ENABLE, OUTPUT);
  digitalWrite(VBAT_ENABLE, LOW);
  delay(5); // 等待電壓穩定

  analogReference(AR_INTERNAL); // 基準電壓 3.6V
  analogReadResolution(12);     // 12bit 解析度
  int raw = analogRead(PIN_VBAT); // 讀取電壓

  // 讀取完畢後關閉開關以防止放電
  digitalWrite(VBAT_ENABLE, HIGH);
  pinMode(VBAT_ENABLE, INPUT);

  // 根據 XIAO nRF52840 的精確電阻比 (1MΩ 與 510kΩ) 進行電壓計算(mV)
  // 計算公式: 讀取值 * (基準電壓/解析度) * (分壓比修正)
  float mv = raw * (3600.0 / 4096.0) * (1510.0 / 510.0);

  // 符合鋰離子電池特性 (4200mV=100%, 3300mV=0%)
  if (mv >= 4200.0) return 100;
  if (mv <= 3300.0) return 0;
  return (uint8_t)((mv - 3300.0) / (4200.0 - 3300.0) * 100.0);
}

void setup() {
  pinMode(BTN1_PIN, INPUT_PULLUP);
  pinMode(BTN2_PIN, INPUT_PULLUP);

  Bluefruit.begin();
  Bluefruit.setTxPower(8);
  Bluefruit.setName(DEVICE_NAME);
  Bluefruit.setAppearance(BLE_APPEARANCE_HID_GAMEPAD);

  bledis.setManufacturer("HI-JA");
  bledis.setModel("Delivery Controller");
  bledis.begin();

  blebas.begin();
  blebas.write(getBatteryPercentage()); // 啟動時發送真實電量

  blehid.setReportLen(inputReportLen);
  blehid.setReportMap(hidReportDescriptor, sizeof(hidReportDescriptor));
  blehid.begin();

  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_GAMEPAD);
  Bluefruit.Advertising.addService(blehid);
  Bluefruit.Advertising.addService(blebas);
  Bluefruit.Advertising.addName();
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);
  Bluefruit.Advertising.setFastTimeout(30);
  Bluefruit.Advertising.start(0);
}

void loop() {
  bool raw1 = (digitalRead(BTN1_PIN) == LOW);
  bool raw2 = (digitalRead(BTN2_PIN) == LOW);

  static int state = 0;
  static unsigned long pressTime = 0;
  static uint8_t lastReport = 0;
  uint8_t newReport = 0;
  unsigned long now = millis();

  // --- 同時按下判斷狀態機 ---
  if (state == 0) {
    if (raw1 || raw2) {
      state = 1;
      pressTime = now;
    }
  } else if (state == 1) {
    if (!raw1 && !raw2) {
      state = 0;
    } else if (now - pressTime > 40) {
      if (raw1 && raw2) state = 4;
      else if (raw1) state = 2;
      else if (raw2) state = 3;
    }
  } else {
    if (state == 2 && !raw1) state = 0;
    if (state == 3 && !raw2) state = 0;
    if (state == 4 && (!raw1 && !raw2)) state = 0;
  }

  if (state == 2) newReport = 0x01;
  else if (state == 3) newReport = 0x02;
  else if (state == 4) newReport = 0x04;
  else newReport = 0x00;

  if (newReport != lastReport) {
    blehid.inputReport(0, &newReport, 1);
    lastReport = newReport;
  }

  // --- 長按功能 ---
  static unsigned long d0StartTime = 0;
  static bool d0Long10s = false;
  static bool d0Long15s = false;

  if (state == 2 && raw1) {
    if (d0StartTime == 0) d0StartTime = now;
    unsigned long duration = now - d0StartTime;

    if (duration >= 15000 && !d0Long15s) {
      d0Long15s = true;
      uint8_t releaseReport = 0;
      blehid.inputReport(0, &releaseReport, 1);
      delay(100);
      nrf_gpio_cfg_sense_input(g_ADigitalPinMap[BTN1_PIN], NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
      NRF_POWER->SYSTEMOFF = 1;
    }
    else if (duration >= 10000 && !d0Long10s && !d0Long15s) {
      d0Long10s = true;
      NVIC_SystemReset();
    }
  } else {
    d0StartTime = 0;
    d0Long10s = false;
    d0Long15s = false;
  }

  // 每 60 秒更新一次真實電池電量
  static unsigned long lastBatteryTime = 0;
  if (now - lastBatteryTime > 60000) {
    lastBatteryTime = now;
    blebas.write(getBatteryPercentage());
  }
  delay(5);
}

  • 貼上程式碼後,點擊畫面左上角的(右箭頭圖示)執行燒錄。

    出現 # 讀取條並完成後即可拔除 USB。燒錄期間請勿拔掉。

    若出現燒錄錯誤,可能是設定或貼上過程有誤,請複製錯誤碼並諮詢 Gemini 或 ChatGPT。

5-2. 疑難排解:連接埠未識別或死機時

開發過程中,基板無法被電腦識別(連接埠消失)是常有的事。

  • 基本重置: 使用鑷子等快速短接基板上的「RST」接點 2 次(雙擊),即可進入 Bootloader 模式,通常就能被識別。

  • 最終手段(完全死機時): 若重置操作完全沒反應,可能是基板已經完全當機。這種情況下,按重置按鈕無效。唯有使用「保持電池連接狀態並靜置,直至電量完全放盡」這種土法,才能強制斷電重啟。

5-3. 按鈕的操作規範與規格

燒錄的程式碼中實作了 40ms 的狀態機,可最大限度排除彈跳效應(誤觸)。並控制電壓讀取功能 (VBAT_ENABLE),向手機傳送準確的電池剩餘電量。

【按鈕動作規格】

  • 按鈕 0 (D0) 短按: 音量加大

  • 按鈕 1 (D1) 短按: 音量減小

  • 按鈕 0 + 按鈕 1 同時按下: 播放 / 暫停

  • 按鈕 0 長按 10 秒: 清除藍牙配對資訊(重置)

  • 按鈕 0 長按 15 秒: 關閉電源

【電池續航力】

在不進行任何輸入操作的待機狀態下,可維持 約 20~30 天。實際在繁重的外送業務中使用時,續航會隨著操作頻率而縮短。

燒錄正常完成並開始運作後,基板的藍色 LED 燈會閃爍,進入待配對狀態。

6. Android 端設定:將普通按鈕變成最強觸發器

硬體完成並與手機藍牙配對後,接著要在 Android OS 端攔截這些輸入,並將其轉換為所需動作。若不進行此設定,這只會是一個單純改變音量的按鈕。

6-1. 事前準備:開啟開發者選項

為了使用 Android 的進階系統權限,首先啟用開發者模式。

  1. 開啟 Android 設定關於手機

  2. 版本號碼 連續點擊 7 次,開啟開發者模式。

  3. 開啟 設定系統開發者選項,並將**「USB 偵錯」「無線偵錯」**設為開啟。

6-2. 導入 FRep2 與詳細巨集建立(影像辨識點擊)

如果您想使用「只在畫面上出現特定按鈕(影像)時才判斷點擊」的高階巨集,而非 Key Mapper 的固定位置點擊,則必須在 Key Mapper 設定前,先在 FRep2 完成巨集設定。

(※若單純只想讓固定位置進行點擊,可跳過此步驟直接至「6-3. 導入 Shizuku」)

① 安裝 FRep2 並切換至「精密模式」(必須)

要在 FRep2 中透過影像搜尋執行硬體點擊,必須從預設的「簡單模式」切換至「精密模式」,並賦予畫面擷取權限。

  1. 從 Play 商店安裝 「FRep2」

  2. 存取 FRep2 官方網站的設定指南( https://strai.x0.com/frep2-ja/setup )。

  3. 在頁面內的 【精密模式設定・畫面擷取權限授予】 中,根據您的作業環境(Windows / Mac / Linux / Android 單機)下載對應的 [畫面擷取權限賦予工具]

  4. 依照官網指示執行工具(此過程會使用「USB 偵錯」或「無線偵錯」),賦予權限並讓服務以精密模式運作。

② 事前準備(截取目標螢幕截圖)

開啟您想操作的應用程式(如 Uber Eats),並儲存顯示著您想點擊按鈕的畫面截圖

(※對於基於安全限制無法截圖的應用程式,無法使用影像辨識點擊。)

③ 新建巨集與顯示設定

  1. 啟動 FRep2,從主畫面點擊 紀錄新規

  2. 從右側設定項目中開啟 【顯示設定】,點擊 在任何應用程式顯示 並選擇 倉庫(不顯示)

    (※因為是透過捷徑在背景呼叫,無需顯示操作面板。)

④ 影像辨識程式設計

  1. 點擊選單 【程式】 內的 編輯播放內容

  2. 點擊畫面上的 按鈕,選擇 影像辨識從影像檔案

    (※此處若無法選擇影像,請從手機的「設定」>「應用程式」開啟 FRep2,並賦予其對影像檔案的存取權限。)

  3. 開啟先前準備好的截圖,點擊右下角的 套用

⑤ 指定辨識目標框

  1. 畫面會出現一個藍色方形框。此藍框為「辨識目標」。

  2. 將框的中心對準您想點擊的位置(例如 Uber Eats 拒絕「×」記號的中心等)。

  3. 重要:辨識框「越小」,處理速度越快。 若是拒絕按鈕,將框調整為細長狀,只框住 X 記號中心附近即可。指定完成後點擊 套用

⑥ 辨識設定與搜尋範圍優化(高速化關鍵)

  1. 在下個畫面的 【辨識設定】 中點擊 顏色相似度,將其變更為 搜尋:相似度

  2. 勾選 點擊辨識區域中心

  3. 點擊 搜尋範圍:螢幕全體,會出現可輸入 4 個數值的圖表。

  4. 點擊右上方的「顯示影像(手指照片的圖示)」。

    影像數值會隨液晶尺寸變化,請勿參考,務必使用您手上的手機進行尺寸調整。

  5. 先前設定藍框的部分會變得明亮。

    若是 Uber Eats 的拒絕按鈕等,位置會隨彈窗變動的按鈕,請依照其變動的最大範圍擴大明亮區域

    (※此處搜尋範圍越小,處理速度會呈現壓倒性的快。請控制在最小限度。)

  6. 點擊 套用 進行設定。

  7. 【進階設定】 中,若不需儲存截圖,請取消勾選。

  8. 點擊畫面右下角的 關閉 結束影像搜尋設定。

  9. 編輯畫面最初的「待機 5.0」是不必要的浪費時間,可刪除。

⑦ 儲存設定與確認

  1. 點擊畫面右上方的**「磁碟片圖示」**儲存設定。

  2. 透過左上角的返回箭頭(或漢堡選單)點擊 紀錄,回到設定列表。

  3. 點擊上方欄位的 標準面板,變更為 倉庫

  4. 若有顯示剛才建立的巨集,FRep2 端就準備就緒了。

6-3. 導入並啟動 Shizuku(確保系統權限與提升響應速度)

為了讓通常需要 Root 權限才能進行的系統操作(如透過 ADB 的硬體螢幕點擊)成為可能,我們常駐 Shizuku。

(※使用 FRep2 的影像辨識點擊時,不設定 Shizuku 也能運作,但 Shizuku 環境下的響應速度極快,強烈建議導入)

  1. 從 Google Play 商店安裝 「Shizuku」

    (※注意:部分裝置如日本版 Pixel 9a 等,可能在 Play 商店找不到 Shizuku。為確保安全,請務必至官方 GitHub 發布頁 https://github.com/RikkaApps/Shizuku/releases 直接下載 APK 安裝。不建議從不明第三方網站下載。)

  2. 通信環境準備(重要): Shizuku 啟動使用「無線偵錯」,因此必須處於可連線 Wi-Fi,或可連線至其他手機進行網路共享的環境。請在此時連接 Wi-Fi 或熱點。

    (※請放心,此連線僅用於啟動 Shizuku。啟動成功後即可斷開。不會持續消耗共享熱點手機的數據流量。)

  3. 確認開發者選項: 為確保萬一,請確認手機 設定系統開發者選項 已出現,且「無線偵錯」為開啟狀態。(若未出現,請依照前述步驟對 關於手機版本號碼 連點 7 次。)

  4. 啟動 Shizuku App,點擊「透過無線偵錯啟動」區塊下的 配對

  5. 畫面會跳轉至開發者選項的無線偵錯頁面,點擊 使用配對碼進行裝置配對

  6. 將出現的 6 位數代碼輸入至 Shizuku 的通知(或彈窗)中。

  7. 回到 Shizuku App,點擊 啟動。上方顯示「Shizuku 正在執行中」即為成功。確認啟動後,即可解除 Wi-Fi 或熱點連線。(※手機重啟後,可能需要再次執行此啟動程序。)

6-4. Key Mapper 設定(輸入重新映射與動作分配)

這是偵測物理按鈕輸入並轉換為任意動作的核心設定。最後在此處將所有動作連結。

※進行此設定時,需要實際按鍵以紀錄訊號,請務必先將自製的藍牙按鈕與手機配對。

  1. 從 Play 商店安裝並啟動 「Key Mapper」

  2. 首次啟動時,依照指示賦予「協助工具」權限,並許可 Shizuku 權限

  3. 點擊畫面下方的 按鈕(或 + new key map),開啟上方列有 TriggerActions 的規則建立畫面。

  4. 紀錄觸發器(啟動動機):

    1. 點擊畫面下方的紅色 tap to record trigger (或 Record Trigger)。

    2. 在畫面變成「Press your keys」的期間,按下自製物理按鈕的「按鈕 1(例如音量加大)」。

    3. 畫面顯示該藍牙裝置名稱與「Volume Up」等訊號即成功。

      (※重點:送出的訊號雖然是「音量加大」,但透過 Key Mapper,可以將其轉換為您指定的其他動作執行,而不實際增加手機音量。)

  5. 設定動作(兩種途徑):

    移動至畫面上方的 Actions 分頁,點擊畫面下方的 Add Action。清單會出現,請依照用途選擇以下任一方法:

    • 【方法 A】連結至 FRep2 巨集(影像辨識點擊 / 使用捷徑)

      透過物理按鈕呼叫「6-2」中預先建立的 FRep2 巨集。

      1. 從動作清單選擇 Launch app shortcut

      2. 出現捷徑清單,選擇 FRep2 捷徑

      3. 開啟 FRep2 設定畫面。因為建立的巨集位於「倉庫」,將清單變更為 倉庫(不顯示) 並選擇對應巨集。

      4. 播放項目選擇 從頭開始播放

        (※此方法不設定 Shizuku 也能運作,但在 Shizuku 有效環境下響應更快。)

    • 【方法 B】利用 Shell command(ADB 硬體點擊)

      ※此處以 Uber Eats 的「拒絕」動作為例說明。其他用途請參考「6-5. 自訂」。

      1. 從動作清單選擇 Shell command

      2. 開啟設定畫面,先在 Description 欄位輸入標題(例如:Uber 拒絕等)。

      3. 接著將以下內容複製並貼入 Script 欄位:

        # 1. 取得螢幕尺寸
        size=$(wm size | awk '{print $NF}'); w=${size%x*}; h=${size#*x}
        x=$((w * 86 / 100))
        
        # 2. 座標計算(因為會多次使用,設為變數)
        y1=$((h * 565 / 1000))
        y2=$((h * 528 / 1000))
        y3=$((h * 491 / 1000))
        y4=$((h * 454 / 1000))
        y5=$((h * 417 / 1000))
        
        # 3. 執行「不移動的滑動」(75 毫秒 = 0.075 秒按壓)
        # 使用 swipe 而非 tap,透過讓起點與終點相同來達成「長按點擊」效果
        input swipe $x $y1 $x $y1 75; sleep 0.2
        input swipe $x $y2 $x $y2 75; sleep 0.2
        input swipe $x $y3 $x $y3 75; sleep 0.2
        input swipe $x $y4 $x $y4 75; sleep 0.2
        input swipe $x $y5 $x $y5 75
        
      4. 將下方的 Execution Mode 設定變更為 ADB

        (※重要:若未與 Shizuku 連結,ADB 模式將無法運作。另外,Uber Eats 等 App 在「Standard」模式下無法點擊。)

      5. 設定完成後,點擊右下角的 Done 儲存。

      6. 回到動作設定畫面,再次點擊右下角的 Done 儲存。

  6. 取消音量變更(超重要)與儲存:

    在規則設定畫面下方,或「設定 (Settings)」中,確認 「Bypass(繞過)」 已打勾(生效)。這能確保**「按下觸發按鈕時,手機本身的音量不會改變」**。確認無誤後,點擊右下角的 Done 儲存設定。

  7. 確認 Key Mapper 啟動(重要):

    為了讓 Key Mapper 正確運作,兩個啟動開關都必須為 ON

    • 個別巨集啟動: 儲存的巨集列表右上方的 Enabled 是否為 ON。

    • Key Mapper 本身啟動: 主畫面上方 Running ↔️ Paused 是否為 Running

      兩者都為 ON 時,按下物理按鈕才會有反應。

請依相同步驟分別建立「按鈕 2(音量減小)」或「同時按下(播放/暫停)」的規則。

6-5. 自訂:打造個人專屬的最強工具

對於初學者而言,設定到這裡應該已經對整體機制有了概念。以下是依用途進行自訂的建議:

  • 變更觸發器或動作: 在設定「觸發器」時改用不同按鈕,或在設定「動作」時改為開啟應用程式(例如啟動 Google 地圖),請嘗試更換動作清單。

  • Shell command 的座標調整: 本次方法 B 的程式碼中,是以螢幕尺寸的 %(百分比) 計算 X, Y 座標。只要更改數值就能自由變更點擊位置,因為是指定百分比,即使更換手機也能輕鬆適應。當然也支援直接指定像素 (px)。

  • 處理速度的優勢: 比起 Key Mapper 標準的 X, Y 座標點擊功能,使用 Shell command 指定座標點擊的速度明顯較快。

  • 關於接單動作: 應用點擊或滑動設定,理論上也可以「接單」,但因為誤接風險過高,本指南不推薦,也不提供設定範例。若有需要請自行承擔風險自訂。

  • 推薦設定: 強烈建議將「回到導航 App(Google 地圖等)」的動作設定在另一個按鈕上,非常方便。

7. 運用注意事項與極限(防水性方面)

本裝置雖針對嚴苛的現場環境設計,但構造上有極限。

並不完全防水。

塗佈基板專用防水噴霧或指甲油,可提升基板本身防護性。但由於充電用的 USB Type-C 端子是外露的,無法做到全密封。

若在雨天使用,務必將其設置在機車的把手保護套內,或以有遮蔽的環境下運用為前提。端子部位一旦浸水會瞬間短路而毀損。

8. 原創 3D 列印用 STL 數據檔(100 日圓)販售說明

本文介紹的機殼 3D 列印數據(STL 檔案) 已在 BOOTH 上架。這是為了讓電路板與電池能以毫米級精度完美契合而專門設計的數據。

如果您擁有 3D 列印機,歡迎下載數據並自行列印使用。

【購買前注意事項】 此殼是依據本指南指定的零件構成(特別是 Kailh Choc Brown 與 1.8mm 厚凝膠膠帶)最佳化設計的。若您的選材不同,可能無法順利組裝,購買前請特別留意。

9. 無限可能:邁向下一場 Hack

這顆按鈕最初是為了能在雨天、不看螢幕的瞬間,處理外送中的「接單」、「拒單」操作而設計的。但獲得通用的「物理觸發器」後,應用範圍將無限擴展。

  • AI 即時呼叫: 作為能瞬間啟動 ChatGPT 等語音輸入模式或 API 的物理開關。

  • IoT / 智慧家庭控制: 與 Tasker 等聯動,作為遠端控制特定家電或觸發 Webhook 的媒介。

  • 遊戲: 透過獨有的輸入延遲對策程式碼,作為特定遊戲專用的客製控制器。

雨天嚴苛的路上,或深夜桌前焊接與程式除錯的試錯過程。希望這顆最終完成的小按鈕,能解決各位現場的難題,成為各位「遊玩」的新火種。

感謝您閱讀這份土法煉鋼的紀錄到最後。如果這些資訊能成為改變您環境的契機,或點燃新的 Hack 火花,那便物盡其用了。那麼,我們在下一個點子見吧!