魔改車鑰匙實現遠程控車:(1)整體思路及控制方案實現

語言: CN / TW / HK

我正在參加「創意開發 投稿大賽」詳情請看:掘金創意開發大賽來了!

前言

作為一個在互聯網時代成長起來的人,怎麼能忍受自己的愛車不支持遠程控制呢。

連我的小電驢都支持手機靠近自動解鎖,藍牙/網絡遠程控車。而我的汽車卻不支持,實在是説不過去,所以萌發了自己改造一下讓它支持靠近自動解鎖,同時能夠遠程控車。

基本情況及整體實現思路

基本需求

最初我想實現的功能只有一個:無需攜帶車鑰匙,靠近車輛自動解鎖,熄火後離開車輛自動上鎖。

當我萌發出這個想法時,第一時間想到的就是魔改車鑰匙來實現我想要的功能。

前期我在 V2 發了一個帖子,原車不支持手機解鎖,但是支持鑰匙感應解鎖,有沒有可能改裝成支持手機解鎖的?試圖尋求有經驗的大佬的靈感,但是收效甚微。後來靈機一動,上某寶搜索了一下,最終找到了大量和我想法一致的改裝套件。這證明我的想法是可行的,並且已經有前人實踐過了。

至於我為什麼不直接在某寶買成品而是要自己實現,無非三個理由:

  1. 淘寶成品略貴,我認為不值這個價錢
  2. 想要實現更多的功能,而套件只提供了固定的一兩個功能,無法自由擴展
  3. 想自己折騰

改裝思路

確定了方案可行,下一步就是實際測試。

鑰匙支持的功能

經過查閲資料,我的車鑰匙支持如下功能:

  1. 靠近車輛後車輛自動解鎖(需要拉主駕門把手激活)
  2. 遠離車輛後車輛自動上鎖
  3. 長按解鎖按鍵可以一鍵打開車窗(汽車未啟動時)
  4. 長按上鎖鍵則反之
  5. 連續短按兩次多功能按鍵開啟尋車
  6. 短按上鎖按鍵後長按多功能按鍵,遠程啟動車輛
  7. 長按多功能按鍵可退出遠程啟動

改裝方案概述

我的改裝思路是,將車鑰匙主板拆出,重新焊接一塊開發板,並由這塊開發板接管控制車鑰匙的電源和按鍵。

然後將改裝後的開發板放入車內,平時車鑰匙電源設置為斷開狀態,在接收到手機的指令或檢測到手機信號強度到一定閾值後給鑰匙供電並配合接通特定按鍵實現感應解鎖。而鎖車則反之。

供電方案選擇

前期測試了我的車支持的取電接口: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. 接受其他設備的配對請求,並且配對需要校驗避免任何人都能連接(現在就是簡單的接收到串口發送的 '1' 即視為校驗通過,因為嚴格意義上來説,除了我自己,沒有人能夠連接 ESP32 的串口,所以這樣就足夠安全了,只是這樣有個問題,無法方便的配對新設備)
  2. 接收已配對的設備連接後發送的指令,分別為:

| 指令 | 動作 | 説明 | | ---- | ---- | ---- | | 1 | 給車鑰匙上電 | 需要上電才能繼續下面的其他操作 | | 2 | 斷開車鑰匙供電 | 無 | | 3 | 上鎖 | 觸發上鎖按鍵後,延遲 10s 斷開車鑰匙供電 | | 4 | 遠程點火 | 模擬觸發短按上鎖按鍵後長按多功能鍵 | | 5 | 遠程熄火 | 模擬觸發長按多功能按鍵 | | 6 | 尋車 | 模擬雙擊多功能按鍵 | | 7 | 觸發多功能按鍵 | 無 | | 8 | 觸發上鎖按鍵 | 無 | | r | 返回各個 IO 口狀態 | 通過藍牙串口返回當前 IO 口狀態 |

最終效果

最終實現效果可以看我在 B站 的這個視頻: 【改裝車鑰匙實現手機控制演示-嗶哩嗶哩】 (視頻沒拍好,可能需要戴上耳機才能聽見發動機的聲音)

視頻裏面只演示了通過手機控制遠程點火這一個功能,但是實際上表中的功能經過測試都是可用的。

缺陷與後續

缺陷

因為這次只是為了驗證手機控制的可行性,所以沒有做太多功能,只做了手動觸發按鍵測試是否可行。

另外,在測試中我發現了兩點比較致命的問題:

  1. 鑰匙在車內時無法使用感應解鎖功能,必須觸發解鎖按鈕才能解鎖車輛
  2. 鑰匙在車內時無法觸發遠程點火功能(上述演示視頻拍攝時,我把測試模塊放在了車外面,沒放車內)

對於問題 1 ,可以通過觸發解鎖按鍵來替代,問題不大。

對於問題 2,為了安全着想,也為了方便供電,模塊必須放置在車內,所以這個問題暫時無解,可能需要放棄;或者也可以想辦法把車鑰匙的天線延長到車外以欺騙車輛的鑰匙位置感知功能。

後期計劃

1.感應解鎖 添加手機靠近車輛自動解鎖,我的想法是可以利用藍牙的 RSSI 感知手機與模塊的距離,達到一定閾值後自動解鎖。同時手機離開模塊後自動上鎖。

2.手動連接 當前已實現的手動連接模塊觸發相應功能也予以保留,不過應該在這個基礎上增加可以設置一些參數,例如感應距離,按鍵觸發時間,解鎖時是否同步點火或打開窗户(點火後車輛會自動開啟空調,可以在夏天自動提前降温,開窗同理)。

3.優化配對 應該優化一下配對新的手機,在保證安全的情況下儘可能簡化配對新手機的流程、

4.真·遠程控制 後期可以添加一個4G模塊實現真正的遠程控制,不過現在這個功能暫時想不到有實現它的必要,除了夏天在出門前先在家裏遠程點火給車輛降温外,似乎確實沒有其他的優勢。

原文發佈於我的博客:likehide.com