魔改車鑰匙實現遠端控車:(1)整體思路及控制方案實現
我正在參加「創意開發 投稿大賽」詳情請看:掘金創意開發大賽來了!
前言
作為一個在網際網路時代成長起來的人,怎麼能忍受自己的愛車不支援遠端控制呢。
連我的小電驢都支援手機靠近自動解鎖,藍芽/網路遠端控車。而我的汽車卻不支援,實在是說不過去,所以萌發了自己改造一下讓它支援靠近自動解鎖,同時能夠遠端控車。
基本情況及整體實現思路
基本需求
最初我想實現的功能只有一個:無需攜帶車鑰匙,靠近車輛自動解鎖,熄火後離開車輛自動上鎖。
當我萌發出這個想法時,第一時間想到的就是魔改車鑰匙來實現我想要的功能。
前期我在 V2 發了一個帖子,原車不支援手機解鎖,但是支援鑰匙感應解鎖,有沒有可能改裝成支援手機解鎖的?試圖尋求有經驗的大佬的靈感,但是收效甚微。後來靈機一動,上某寶搜尋了一下,最終找到了大量和我想法一致的改裝套件。這證明我的想法是可行的,並且已經有前人實踐過了。
至於我為什麼不直接在某寶買成品而是要自己實現,無非三個理由:
- 淘寶成品略貴,我認為不值這個價錢
- 想要實現更多的功能,而套件只提供了固定的一兩個功能,無法自由擴充套件
- 想自己折騰
改裝思路
確定了方案可行,下一步就是實際測試。
鑰匙支援的功能
經過查閱資料,我的車鑰匙支援如下功能:
- 靠近車輛後車輛自動解鎖(需要拉主駕門把手啟用)
- 遠離車輛後車輛自動上鎖
- 長按解鎖按鍵可以一鍵開啟車窗(汽車未啟動時)
- 長按上鎖鍵則反之
- 連續短按兩次多功能按鍵開啟尋車
- 短按上鎖按鍵後長按多功能按鍵,遠端啟動車輛
- 長按多功能按鍵可退出遠端啟動
改裝方案概述
我的改裝思路是,將車鑰匙主機板拆出,重新焊接一塊開發板,並由這塊開發板接管控制車鑰匙的電源和按鍵。
然後將改裝後的開發板放入車內,平時車鑰匙電源設定為斷開狀態,在接收到手機的指令或檢測到手機訊號強度到一定閾值後給鑰匙供電並配合接通特定按鍵實現感應解鎖。而鎖車則反之。
供電方案選擇
前期測試了我的車支援的取電介面:USB充電口、點菸器、後視鏡預留取電介面、OBD取電、保險盒取電。
經過測試,USB口、點菸器、預留介面均不支援熄火後繼續供電,故放棄使用。
僅剩下 OBD 和 保險盒 取電,但是保險盒取電過於麻煩,故放棄。
而 OBD 支援熄火持續供電,且只需要使用轉接頭即可直接轉接成 USB 口,使用起來很方便。
故最終選擇 OBD 供電作為供電方案。
材料準備與技術選擇
確定了改裝思路,接下來就是選擇改裝材料。
線材,焊接工具等周邊材料不過多贅述。
主要是需要選定一塊合適的開發板。
基於我的需求,這塊開發板應該至少支援藍芽、至少有四個可用的 IO 介面(分別用於控制車鑰匙電源、車鑰匙上鎖鍵、車鑰匙解鎖鍵、車鑰匙多功能按鍵)、價格應該儘可能的低。
最初我打算選擇 Arduino NANO 配合藍芽模組,但是這樣成本過高,已經快接近某寶成品模組的價格了,後來在B大佬的推薦下,我購買了 ESP32C3 開發板,並且搭配 MicroPython 進行開發。ESP32C3的優勢在於價格極其便宜,一塊僅需9.9還包郵。(在我寫這篇文章的時候,它已經漲價了)、同時支援藍芽(BLE)和WiFi、整體體積很小。
但是,經過一段時間的熟悉和測試,我發現 ESP32C3 僅支援 BLE 不支援經典藍芽,而且 MicroPython 對於 BLE 的支援也不完善,不支援配對裝置,這就導致我無法驗證裝置真偽,因為現在智慧手機的 MAC 都是隨機的,除非你和手機完成配對,否則無法得到手機的真實 MAC。
因此我開始將目光轉向經典藍芽,後來還是在B大佬的推薦下,我選擇了 ESP32 開發板,優勢依然是便宜,20元多一點包郵,並且支援經典藍芽和BLE雙模,同時也支援WIFI。
但是這次我沒有選擇 MicroPython 作為開發平臺,而是選擇了 Arduino。理由也很簡單,MicroPython 不支援 ESP32 的經典藍芽……
可行性研究測試
硬體焊接
拆開車鑰匙主機板後,結構非常簡單,一眼就能看出各個元器件的作用。
首先將主機板的正負極供電分別焊接引出一條線,接下來把需要的按鍵也分別引出線就OK了。
有一點需要注意的是,由於按鍵焊點特別的小,非常不容易焊接,好在這塊主機板上貼心的打了非常多的測試用的觸點,所以我直接把線焊在了檢測點上。
此時出現了一個問題,按鍵應該焊接那兩條線?
我首先想到的是白嫖某寶店家的成熟方案:
只不過我搜遍了某寶,幾乎都是同樣的焊接方式,但是他們都是四腳的按鍵,而我的是六腳按鍵啊,這能一樣嗎?
看來白嫖不成只能自己琢磨了,經過我用放大鏡仔細研究,發現其實這個按鍵只接了兩腳,那麼問題就不大了,不用關心它的實現原理,只要無腦把這兩個腳引出即可。因為不管幾腳的開關,一般來說都是保持常開,按下按鍵後接通。
那麼,我要如何實現模擬按鍵的接通與斷開呢?
還是在B大佬的推薦下,我購買了 NMOS 管,但是買的時候沒注意看引數,買的 MOS 管是貼片 MOS 管,非常的小,壓根沒法焊接……還好我買 MOS 管時為了以防萬一,順手買了兩個繼電器。既然 MOS 管無法使用,那就使用繼電器吧。
反正核心原理就是要在接收到 ESP32 的訊號後接通從按鍵引出的兩條線。
焊接並排線完成後如圖:
(原諒我第一次焊接,全焊的糊成一團了)
硬體連線方案
經過直接測試,上述焊接和排線都沒有問題。
但是,當我把 ESP32 接上時卻出問題了: ESP32 帶不動這個繼電器! 一旦啟動繼電器 ESP32 就直接過載重啟……
接下來怎麼辦?嘗試倒騰比車鑰匙觸點還小的 MOS 管?還是外接一個電源模組?
或許,可以直接放棄接通按鍵兩條引腳的方案,改為直接給連線晶片一端的引腳高電平以模擬按下按鍵?
說幹就幹,快速寫了一個 demo 後開始測試,結果發現並不行。
後來還是在B大佬的提醒下,我才知道原來這個車鑰匙按鍵是預設接通高電平,給低電平才觸發按鍵功能。
具體如圖示:
上述為按鍵的接線圖,實際上車鑰匙只用了兩個引腳,其中一個引腳直接引向電源負極,另外一個在串聯電阻後引向晶片的某個引腳。
ps: 上面圖畫反了,電阻是串聯在引向晶片這條線的。
按照這個方案,重新連線後如圖:
確定了按鍵實際上是給低電平觸發後,連線起來就簡單多了,因為車鑰匙電源和 ESP32 已經共地,所以現在只需要把按鍵引向晶片這個引腳連線上 ESP32 ,預設給高電平,需要觸發時給低電平即可,另外一條線不需要連線任何東西,也不需要再外接繼電器或 MOS 管。
對了,上圖中的 LED 是用來確定車鑰匙是否上電的,這個車鑰匙太質樸了,沒有任何提示(蜂鳴或者LED),我無法得知是否正常上電,所以我額外接了一個 LED 方便測試。
控制軟體
控制軟體直接使用的 Arduino 官方的藍芽串列埠示例程式碼改的:
```c //This example code is in the Public Domain (or CC0 licensed, at your option.) //By Richard Li - 2020 // //This example creates a bridge between Serial and Classical Bluetooth (SPP with authentication) //and also demonstrate that SerialBT have the same functionalities of a normal Serial
include "BluetoothSerial.h"
if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
error Bluetooth is not enabled! Please run make menuconfig
to and enable it
endif
if !defined(CONFIG_BT_SPP_ENABLED)
error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
endif
define LED_CONNECT 2
define LED_POWER 15
define PIN_POWER 13
define PIN_LOCK 12
define PIN_LOOP 14
boolean isConnectDevice = false;
BluetoothSerial SerialBT; boolean confirmRequestPending = true;
void BTConfirmRequestCallback(uint32_t numVal) { confirmRequestPending = true; Serial.println(numVal); }
void BTAuthCompleteCallback(boolean success) { confirmRequestPending = false; if (success) { Serial.println("Pairing success!!"); } else { Serial.println("Pairing failed, rejected by user!!"); } }
// Bt_Status callback function void Bt_Status (esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
if (event == ESP_SPP_SRV_OPEN_EVT) { Serial.println ("Client Connected"); digitalWrite(LED_CONNECT, HIGH); isConnectDevice = true; confirmRequestPending = false; // Do stuff if connected }
else if (event == ESP_SPP_CLOSE_EVT ) { Serial.println ("Client Disconnected"); digitalWrite(LED_CONNECT, LOW); isConnectDevice = false; // Do stuff if not connected } }
void get_start() { digitalWrite(LED_POWER, HIGH); digitalWrite(PIN_POWER, HIGH); }
void cut_power() { digitalWrite(LED_POWER, LOW); digitalWrite(PIN_POWER, LOW); }
void lock_car() { digitalWrite(PIN_LOCK, LOW); delay(500); digitalWrite(PIN_LOCK, HIGH); delay(10000); digitalWrite(LED_POWER, LOW); digitalWrite(PIN_POWER, LOW); }
void luncher_car() { digitalWrite(PIN_LOCK, LOW); delay(300); digitalWrite(PIN_LOCK, HIGH); delay(1000); digitalWrite(PIN_LOOP, LOW); delay(8000); digitalWrite(PIN_LOOP, HIGH); }
void shut_down_car() { digitalWrite(PIN_LOOP, LOW); delay(8000); digitalWrite(PIN_LOOP, HIGH); }
void find_my_car() { digitalWrite(PIN_LOOP, LOW); delay(200); digitalWrite(PIN_LOOP, HIGH); delay(300); digitalWrite(PIN_LOOP, LOW); delay(200); digitalWrite(PIN_LOOP, HIGH); }
void click_loop() { digitalWrite(PIN_LOOP, LOW); delay(200); digitalWrite(PIN_LOOP, HIGH); }
void click_lock() { digitalWrite(PIN_LOCK, LOW); delay(200); digitalWrite(PIN_LOCK, HIGH); }
void read_status() { int power_value = digitalRead(PIN_POWER); int loop_value = digitalRead(PIN_LOOP); int lock_value = digitalRead(PIN_LOCK);
char s[200]; sprintf(s, "power %d, loop %d, lock %da\n", power_value, loop_value, lock_value); SerialBT.print(s); }
void setup() { Serial.begin(115200);
pinMode(LED_CONNECT, OUTPUT); pinMode(LED_POWER, OUTPUT);
pinMode(PIN_POWER, OUTPUT); pinMode(PIN_LOCK, OUTPUT); pinMode(PIN_LOOP, OUTPUT);
digitalWrite(PIN_LOCK, HIGH); digitalWrite(PIN_LOOP, HIGH);
SerialBT.enableSSP(); SerialBT.onConfirmRequest(BTConfirmRequestCallback); SerialBT.onAuthComplete(BTAuthCompleteCallback); // Define the Bt_Status callback SerialBT.register_callback (Bt_Status); SerialBT.begin("equationl's Auto"); //Bluetooth device name Serial.println("The device started, now you can pair it with bluetooth!"); }
void loop() { if (confirmRequestPending) { if (Serial.available()) { int dat = Serial.read(); if (dat == '1') { SerialBT.confirmReply(true); } else { SerialBT.confirmReply(false); } } } else { if (Serial.available()) { SerialBT.write(Serial.read()); } if (SerialBT.available()) { int msg = SerialBT.read(); Serial.write(msg); if (msg == '1') { get_start(); } if (msg == '2') { cut_power(); } if (msg == '3') { lock_car(); } if (msg == '4') { luncher_car(); } if (msg == '5') { shut_down_car(); } if (msg == '6') { find_my_car(); } if (msg == '7') { click_loop(); } if (msg == '8') { click_lock(); } if (msg == 'r') { read_status(); } } delay(5); } }
```
程式碼很簡單,我就不一一介紹了,大概說一下這段程式碼實現的功能:
- 接受其他裝置的配對請求,並且配對需要校驗避免任何人都能連線(現在就是簡單的接收到串列埠傳送的 '1' 即視為校驗通過,因為嚴格意義上來說,除了我自己,沒有人能夠連線 ESP32 的串列埠,所以這樣就足夠安全了,只是這樣有個問題,無法方便的配對新裝置)
- 接收已配對的裝置連線後傳送的指令,分別為:
| 指令 | 動作 | 說明 | | ---- | ---- | ---- | | 1 | 給車鑰匙上電 | 需要上電才能繼續下面的其他操作 | | 2 | 斷開車鑰匙供電 | 無 | | 3 | 上鎖 | 觸發上鎖按鍵後,延遲 10s 斷開車鑰匙供電 | | 4 | 遠端點火 | 模擬觸發短按上鎖按鍵後長按多功能鍵 | | 5 | 遠端熄火 | 模擬觸發長按多功能按鍵 | | 6 | 尋車 | 模擬雙擊多功能按鍵 | | 7 | 觸發多功能按鍵 | 無 | | 8 | 觸發上鎖按鍵 | 無 | | r | 返回各個 IO 口狀態 | 通過藍芽串列埠返回當前 IO 口狀態 |
最終效果
最終實現效果可以看我在 B站 的這個視訊: 【改裝車鑰匙實現手機控制演示-嗶哩嗶哩】 (視訊沒拍好,可能需要戴上耳機才能聽見發動機的聲音)
視訊裡面只演示了通過手機控制遠端點火這一個功能,但是實際上表中的功能經過測試都是可用的。
缺陷與後續
缺陷
因為這次只是為了驗證手機控制的可行性,所以沒有做太多功能,只做了手動觸發按鍵測試是否可行。
另外,在測試中我發現了兩點比較致命的問題:
- 鑰匙在車內時無法使用感應解鎖功能,必須觸發解鎖按鈕才能解鎖車輛
- 鑰匙在車內時無法觸發遠端點火功能(上述演示視訊拍攝時,我把測試模組放在了車外面,沒放車內)
對於問題 1 ,可以通過觸發解鎖按鍵來替代,問題不大。
對於問題 2,為了安全著想,也為了方便供電,模組必須放置在車內,所以這個問題暫時無解,可能需要放棄;或者也可以想辦法把車鑰匙的天線延長到車外以欺騙車輛的鑰匙位置感知功能。
後期計劃
1.感應解鎖 新增手機靠近車輛自動解鎖,我的想法是可以利用藍芽的 RSSI 感知手機與模組的距離,達到一定閾值後自動解鎖。同時手機離開模組後自動上鎖。
2.手動連線 當前已實現的手動連線模組觸發相應功能也予以保留,不過應該在這個基礎上增加可以設定一些引數,例如感應距離,按鍵觸發時間,解鎖時是否同步點火或開啟窗戶(點火後車輛會自動開啟空調,可以在夏天自動提前降溫,開窗同理)。
3.優化配對 應該優化一下配對新的手機,在保證安全的情況下儘可能簡化配對新手機的流程、
4.真·遠端控制 後期可以新增一個4G模組實現真正的遠端控制,不過現在這個功能暫時想不到有實現它的必要,除了夏天在出門前先在家裡遠端點火給車輛降溫外,似乎確實沒有其他的優勢。
原文釋出於我的部落格:likehide.com
- 安卓與串列埠通訊-校驗篇
- 安卓與串列埠通訊-實踐篇
- 為 Kotlin 的函式新增作用域限制(以 Compose 為例)
- 安卓與串列埠通訊-基礎篇
- Compose For Desktop 實踐:使用 Compose-jb 做一個時間水印助手
- 初探 Compose for Wear OS:實現一個簡易選擇APP
- Compose太香了,不想再寫傳統 xml View?教你如何在已有View專案中混合使用Compose
- 在安卓中壓縮GIF的幾種方法(附例項程式碼)
- 魔改車鑰匙實現遠端控車:(1)整體思路及控制方案實現
- 跟我一起使用 compose 做一個跨平臺的黑白棋遊戲(3)狀態與遊戲控制邏輯
- 跟我一起使用 compose 做一個跨平臺的黑白棋遊戲(2)介面佈局
- 以不同的形式在安卓中建立GIF動圖
- 跟我一起使用 compose 做一個跨平臺的黑白棋遊戲(4)移植到compose-jb實現跨平臺
- 羨慕大勞星空頂?不如跟我一起使用 Jetpack compose 繪製一個星空背景(帶流星動畫)
- 魔改車鑰匙實現遠端控車:(4)基於compose和經典藍芽編寫一個控制APP