前言
作为一个在互联网时代长大的人,你怎么能忍受你的车不支持远程控制呢。
就连我的小电驴也支持手机靠近自动解锁,蓝牙/网络远程控制汽车。但是我的车不支持,所以我不能说过去,所以我开始改造自己,让它支持靠近自动解锁,可以远程控制汽车。
基本情况和整体实现思路
基本需求
一开始我只想实现一个功能:不需要携带车钥匙,靠近车辆自动解锁,熄火后离开车辆自动上锁。
当我萌生这个想法时,我首先想到的是魔法车钥匙来实现我想要的功能。
前期我在 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老板的提醒下,我知道车钥匙按钮默认连接高电平,触发低电平的按键功能。
如图所示:
以上是按钮的接线图。事实上,车钥匙只使用两个引脚。一个引脚直接引向电源负极,另一个引脚在串联电阻后引向芯片。
根据此方案,重新连接后如图所示:
由于车钥匙电源和 ESP32 它已经在一起了,所以现在你只需要将按钮连接到芯片的引脚上 ESP32 ,默认给高电平,触发时给低电平。另一条线不需要连接任何东西,也不需要连接继电器或外部继电器 MOS 管。
对了,上图中的 LED 用来确定车钥匙是否上电。这把车钥匙太简单了,没有任何提示(蜂鸣或LED),我不知道是否正常上电,所以我额外接了一个 LED 方便测试。
控制软件
直接使用控制软件 Arduino 更改官方蓝牙串口示例代码:
//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,为了安全着想,也为了方便供电,模块必须放置在车内,所以这个问题暂时无解,可能需要放弃;或者也可以想办法把车钥匙的天线延长到车外以欺骗车辆的钥匙位置感知功能。
后期计划
添加手机靠近车辆自动解锁,我的想法是可以利用蓝牙的 RSSI 感知手机与模块的距离,达到一定阈值后自动解锁。同时手机离开模块后自动上锁。
当前已实现的手动连接模块触发相应功能也予以保留,不过应该在这个基础上增加可以设置一些参数,例如感应距离,按键触发时间,解锁时是否同步点火或打开窗户(点火后车辆会自动开启空调,可以在夏天自动提前降温,开窗同理)。
应该优化一下配对新的手机,在保证安全的情况下尽可能简化配对新手机的流程、
后期可以添加一个4G模块实现真正的远程控制,不过现在这个功能暂时想不到有实现它的必要,除了夏天在出门前先在家里远程点火给车辆降温外,似乎确实没有其他的优势。
原文发布于我的博客:likehide.com