项目背景
UWB数字钥匙是全新的无钥匙进入系统,在接近车辆时,根据距离车辆会自动首先开启迎宾灯,然后随着距离的接近,会自动调整座椅位置等,然后靠近车门时,则自动解锁;当远离车辆时,也能够自动根据位置变化来自动锁车。解决车辆电子钥匙中继攻击的安全漏洞。UWB钥匙的优点在于,它是一项具有近距离精确空间感知能力的技术;准确性高,感应智能手机位置的准确性约为现有产品的 5 倍。
本设计主要是基于UWB定位技术实现智能车钥匙的测距功能,结合TencentosTiny实时操作系统完成初步的功能实现。由于时间紧迫,这里只实现了UWB智能车钥匙的部分功能,后期再慢慢完善。
硬件设计
我采用的是研创物联家的DWM1000-SMA芯片分别与小熊派STM32L431和比赛用的最新一代物联网开发板连接实现测距功能。
开发板不断的通过SPI读取DWM1000的时序和回传消息,计算出两个板的距离然后再通过ESP8266上传到云端。
当实测距离小于1m时,将一个信号量置1,大于1m后置0
TencentOS Tiny开发板
小熊派开发板
ESP8266模块
DWM1000-SMA
UWB技术
UWB是Ultra Wide-Band缩写,UWB技术中文名为超宽带无线通信技术,它来源于很久之前的脉冲通信技术,是一种无载波通信技术,UWB不使用载波,而是使用短的能量脉冲序列,并通过正交频分调制或直接排序将脉冲扩展到一个频率范围内。这是我论文里的调查部分,可以看到UWB在功耗,定位,安全,精度等方面都优于其它技术。
UWB基本测距原理
TOF:Time Of Flight飞行时间测距法,主要利用信号在两个异步收发机(Transceiver)之间飞行时间来测量节点间的距离。双向飞行时间法(TW-TOF, two way-time of flight)每个模块从启动开始即会生成一条独立的时间戳 。模块A的发射机在其时间戳上的Ta1发射请求性质的脉冲信号,模块B在Tb2时刻发射一个响应性质的信号,被模块A在自己的时间戳Ta2时刻接收。有次可以计算出脉冲信号在两个模块之间的飞行时间,从而确定飞行距离S。
但是单纯的TOF算法有一个比较严格的约束:发送设备和接收设备必须始终同步。这是一个比较棘手的问题,但是一种Double-sided Two-way Ranging的算法巧妙的避开了这个问题,它即利用了TOF测距的优良特点,同时又极大的去除了TOF的同步问题,从而为TOF的实用化扫清了道路。
软件开发
UWB驱动文件的移植
decadriver
文件夹,是DW1000中的寄存器定义及各种操作,与平台无关,属于通用文件,无需进行大的修改;platform
文件夹下:
- deca_mutex.c:提供平台相关的互斥锁操作适配实现,裸机中用开关中断实现,RTOS中用互斥量实现;
- deca_slepp.c:提供平台相关的延时函数实现,用系统提供的
tos_task_delay
延时函数; - deca_spi.c:提供平台相关的SPI通信函数实现,直接使用系统的API实现即可,可以参考一下官方例程库中的demo_apps lpspi_loopback
3. dw1000.c
和dw1000.h
文件,这两个文件主要为了定义一些DW1000所使用的控制引脚,实现DW1000中断引脚所连接的EXTI功能初始化,DW1000复位功能等。
4. example_ds_twr_init.c
该文件将程序打包为一个程序入口,以方便系统调用
这部分主要参考了MCULover666大佬的博客:DW1000开发笔记(三)基于STM32 HAL库裸机工程移植DW1000官方驱动
测距程序入口
代码语言:javascript复制void DEMO_SSTWR_INITIATOR( void )
{
uint32_t status;
uint8_t tagAddress = tagAddr[1];
uint8_t anchorAddress = 0;
float32_t distance[4] = {0};
UWB_SetRxAfterTxDelay(UWB_POLL_TX_TO_RESP_RX_DLY_UUS);
UWB_SetRxTimeout(UWB_RESP_RX_TIMEOUT_UUS);
while (1) {
for (uint8_t i = 0; i < 4; i ) {
anchorAddress = anchorAddr[i];
status = UWB_Ranging(tagAddress, &anchorAddress, &distance[i]);
if (status == KS_OK) {
printf(" [X] [OK] .7f mrn", anchorAddress, distance[i]);
}
else {
printf(" [X] [ER] .7f mrn", anchorAddress, distance[i]);
}
}
printf("rn");
tos_task_delay(100);
}
}
测距主程序
代码语言:javascript复制uint32_t UWB_Ranging( const uint8_t tagAddress, uint8_t *anchorAddress, float32_t *distance )
{
uint32_t status;
uint8_t buffer[RANGING_LENS 6] = {0}; // PACKET TIME (4) CHECK SUM (2)
uint8_t address[2] = {tagAddress, *anchorAddress};
uint8_t type;
uint16_t lens;
int32_t t_init, t_resp;
// *distance = 0;
kSerial_Pack(buffer, address, NULL, 0, KS_NTYPE);
UWB_SendPacket(buffer, RANGING_LENS 2, UWB_RANGING | UWB_IMMEDIATE | UWB_TX_RESPONSE);
status = UWB_RecvPacket(buffer, RANGING_LENS 6, UWB_RX_CONTINUE);
if (status == KS_OK) {
status = kSerial_Unpack(buffer, address, NULL, &lens, &type);
if (status == KS_OK) {
if ((address[1] == tagAddress) || (address[1] == 0)) {
if (*anchorAddress == 0) {
*anchorAddress = address[0];
}
else if (address[0] != *anchorAddress) {
return KS_ERROR;
}
t_init = UWB_GetInitiatorTime();
t_resp = UWB_GetReplyTime(&buffer[RANGING_LENS]);
*distance = UWB_GetDistance(t_init, t_resp);
return KS_OK;
}
}
}
return KS_ERROR;
}
MQTT上云
代码语言:javascript复制void mqttclient_task(void)
{
int error;
int k=0;
int distences = 1;
mqtt_client_t *client = NULL;
mqtt_message_t msg;
k_event_flag_t match_flag;
char host_ip[20];
memset(&msg, 0, sizeof(msg));
#ifdef USE_ESP8266
esp8266_sal_init(esp8266_port);
esp8266_join_ap("dashengbeyond", "88888888");
#endif
#ifdef USE_EC600S
ec600s_sal_init(HAL_UART_PORT_0);
#endif
mqtt_log_init();
client = mqtt_lease();
tos_event_create(&report_result_event, (k_event_flag_t)0u);
/* Domain Format: <your product ID>.iotcloud.tencentdevices.com */
tos_sal_module_parse_domain("YSR3V1LY80.iotcloud.tencentdevices.com",host_ip,sizeof(host_ip));
/*
These infomation is generated by mqtt_config_gen.py tool in "TencentOS-tinytools" directory.
*/
mqtt_set_port(client, "1883");
mqtt_set_host(client, host_ip);
mqtt_set_client_id(client, "YSR3V1LY80dev01");
mqtt_set_user_name(client, "YSR3V1LY80dev01;21010406;12365;4294967295");
mqtt_set_password(client, "cdb70911879d695b7ef68cd6caa74f6848b5a245;hmacsha1");
mqtt_set_clean_session(client, 1);
error = mqtt_connect(client);
MQTT_LOG_D("mqtt connect error is %#0x", error);
error = mqtt_subscribe(client, "$thing/down/property/YSR3V1LY80/dev01", QOS0, tos_topic_handler);
MQTT_LOG_D("mqtt subscribe error is %#0x", error);
while (1) {
memset(&msg, 0, sizeof(msg));
if (distences>=150)
{
k=1;
tos_task_delay(3000);
}
if (distences > 100) {
g_pinSet = 1;//识别钥匙标志位
GPIO_PinWrite(BOARD_USER_LED_GPIO,BOARD_USER_LED_GPIO_PIN,LOGIC_LED_ON);
}
if (distences < 100) {
g_pinSet = 0;
GPIO_PinWrite(BOARD_USER_LED_GPIO,BOARD_USER_LED_GPIO_PIN,LOGIC_LED_OFF);
}
snprintf(report_buf, sizeof(report_buf), REPORT_DATA_TEMPLATE, distences);
msg.qos = QOS0;
msg.payload = (void *) report_buf;
error = mqtt_publish(client, "$thing/up/property/YSR3V1LY80/dev01", &msg);
MQTT_LOG_D("mqtt publish error is %#0x", error);
tos_event_pend(&report_result_event,
report_success|report_fail,
&match_flag,
TOS_TIME_FOREVER,
TOS_OPT_EVENT_PEND_ANY | TOS_OPT_EVENT_PEND_CLR);
if (match_flag == report_success) {
printf("report to Tencent IoT Explorer successrn");
}else if (match_flag == report_fail){
printf("report to Tencent IoT Explorer failrn");
}
tos_task_delay(1000);
}
}
程序运行
串口打印
云端数据
这里我是想加一个远程开锁的,但是时间不够了,我对MQTT还不是很熟悉,后面再接着完善吧
微信小程序(腾讯连连)
演示视频
总结与展望
第一次接触实时操作系统,什么都需要去学习,时间很紧,做的很粗超,还有一些功能没有实现,再加上过年舒服了好长时间,直到开学才开始加班做,有点累,但这两周过的很充实。因为开始的晚,有很多大家已经解决的问题我还在问,感觉有被讨厌,但真的很感谢大家给我的帮助。能参加这次比赛真的很开心。
后面我会接着做这个方案,比如一些很实用的功能:
- 远程开车门
- 多标签识别--多把钥匙看是谁开的车
- 多基站三维定位--识别人在车内还是车外
最后感谢TencentOS Tiny AIoT应用创新大赛给了我这次学习的机会,再次感谢各位大佬给我提供的帮助,感谢!