亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

IoT4Car

组件和用品

Arduino MKR1000
× 1
SparkFun 逻辑电平转换器 - 双向
× 1
SparkFun OBD-II UART
× 1
SparkFun OBD-II 转 DB9 电缆
× 1

应用和在线服务

Arduino IDE
freeboard
dweet

关于这个项目

背景

开车时,看看仪表盘,你有没有想过收集仪表读数并做一些分析?这些数据可能包含隐藏的宝藏。对于个人而言,它可以反映您的驾驶习惯,可以告诉您您的速度、您的平均 mpg、您有多少个红绿灯,以及您在每个十字路口的等待时间。对于公司而言,这些数据对于车队管理中的实时监控至关重要。车辆状况、工作负荷分配、汽油效率,甚至车辆位置都可以通过云反馈到中央控制系统。公司可以使用机器学习将数据输入训练模型,以预测成本,甚至分析驾驶员的特征。随着物联网的广泛普及,上述应用也离我们不远了。使用面向物联网应用的 Arduino MKR 板,您可以构建一个与您的汽车对话并将遥测数据上传到云端的设备。是不是很酷?

与车辆交谈

我们需要一个接口来访问车辆系统。我们可以在哪里入侵汽车?答案是OBD-II接口。

什么是 OBD-II?

车载诊断 (OBD) 是车辆的内置自诊断系统,我们可以通过它与汽车进行通信。它于 1994 年首次在美国推出,并成为所有 1996 年和更新的美国车辆的要求。其他国家,包括加拿大、欧盟部分地区、日本、澳大利亚和巴西,也通过了类似的立法。 OBD-II(第二代)有五种信令协议,控制器局域网(CAN总线)就是其中之一。从2008年开始,美国所有的汽车都要求实现CAN总线。Youtube上对CSS Electronics提供的OBDII有很好的介绍。在本项目中,我们将通过 16 针 OBD-II 接口访问数据。

我的控制器

Arduino 是爱好者、制造商和专业人士的绝佳平台。它有多种针对不同应用的板。由于其 WiFi 功能,我在这里使用 Arduino MKR WiFi 1000 板。您也可以使用您喜欢的其他板。我会推荐 Arduino MKR GSM 1400,因为 GSM 覆盖的区域比 WiFi 覆盖的范围要广得多。但别担心,即使有 WiFi,我们也可以在路边上网。我会告诉你解决方法。

口译委员会

虽然 Arduino 本身有大量的 I/O 和众多的库,但我们仍然需要一个可以将 OBD 协议翻译成 Arduino 可以识别的语言的板。我使用的板子是 SparkFun OBD-II UART 板。

该板允许您连接汽车的 OBD-II 总线。它使用 ELM327 命令集为您提供串行接口,并支持所有主要的 OBD-II 标准,例如 CAN。该板包含一个 STN1110 芯片,它是一个 OBD 到 UART 的解释器,可用于在当前使用的任何 OBD-II 协议和 UART 之间转换消息。

但是需要指出的是,解释板的I/O电压为5V,直接连接可能会损坏Arduino MKR板的I/O。 Arduino MKR WiFI 1000 运行在较低的电压下,其 I/O 电压为 3.3 V。因此,需要一个电平转换器将信号从 5 V 转换为 3.3 V,反之亦然。下面是我使用的电平转换的图像。

连接起来

连接电路非常容易。只需通过电平转换器将 Arduino MRK 引脚 13 Rx 和引脚 14 Tx 连接到 OBD-II UART 板 Tx 和 Rx 引脚。当然,您需要将两块板的地连接在一起。

为了方便调试和演示,我还连接了一块 LCD 1602 屏幕到 Arduino 以实时显示数据。 LCD 到 Arduino 的接线可以在本教程中找到,因此这里不再详述。

下面是面包板连接的图像。绿线是连接 Arduino 和 OBD-II UART 板的线,而黄线是连接 Arduino 和 LCD 的线。原理图也可以在附件中找到。

由于面包板面积有限,真正的连接有点混乱,但它遵循上面的原理图。我在图片中包含了微型 USB 和 ODB-II 转 DB9 电缆。

Serial1 不是Serial

好的,是时候对我们的 Arduino MKR 板进行编程了。由于我的 Arduino MKR 板通过 UART 与解释板通信,因此无需安装 3rd 方库。向解释板发送命令就像与串行监视器通信一样。我唯一要强调的是,与 Pin 13 和 Pin 14 相关联的串口是 Serial 1 ! Arduino MKR 板 串口 是指用于与计算机通信的 USB 端口。不要忘记初始化 Serial 1 setup() 函数中的端口。

 Serial1.begin(9600); 

并使用 Serial 1 将命令推送到解释板。

 Serial1.println(message); 

消息

如您所见,我使用变量“message”来存储命令。 OBD 命令由用 ASCII 字符编写的十六进制代码组成。前两个十六进制数表示要使用的服务模式。最新的 OBD-II 标准 SAE J1979 中描述了 10 种诊断服务。由于我们对实时监控感兴趣,所以我们只会使用01 用于显示此项目中当前数据的代码。

服务模式后的任何十六进制数字代表实现特殊功能的参数 ID(PID)。下面是 01 服务模式下 PID 的屏幕截图。更多信息可以在维基百科中找到。

在这个项目中,我将演示如何获取汽车速度、发动机 RPM、燃油油位和发动机冷却液温度。这四个功能的OBD命令是:

  • 010D // 车速
  • 010C // 发动机转速
  • 012F // 油位
  • 0105 // 冷却液温度。

解码数据

命令发出后,Arduino MKR 板将侦听串行 1 端口的任何响应。发出命令后最好延迟 200 毫秒。我使用以下代码接收响应。

void getResponse(void){ while(Serial1.available()> 0) { // 首先检查我们是否收到了消息的结尾字符('\r')。 if(Serial1.peek() =='\r'){ // 到达消息末尾,清空串口缓冲区 inChar =Serial1.read(); rxData[rxIndex] ='\0'; // 重置缓冲区索引,以便下一个字符返回到字符串的开头 rxIndex =0; } // 如果我们没有得到消息字符的结尾,只需将新字符添加到字符串中 else{ // 从串口获取新字符:inChar =Serial1.read(); // 向字符串添加新字符,并增加索引变量: rxData[rxIndex++] =inChar; } }} 

解释板的响应遵循格式

">1 个重复的 PID 数据"

例如,在上面的屏幕截图中,我发送“010D”来获取汽车速度。响应是“>1 0D 00”。前 5 个字符表示汽车收到命令并返回 PID 0x0D。最后两位数返回速度数据 0x00。

然后我发送“010C”来获取引擎RPM,响应“>1 0C”显示命令的确认,数据0x098C是16进制引擎RPM值的4倍。 0x098C / 4 =611 dec,因此发动机 RPM 为 611 rpm。

之后,我发出命令“012F”来获取油位,我得到数据 0x1D。燃油油位计算为 0x1D / 255 * 100 =11% dec。

最后一个命令是“0105”,它给我冷却液温度 0x79。实际温度是 0x79 - 40 =81 °C dec。然后命令序列自身重复。

如您所见,响应行在两个十六进制数字之间有空格,前 5 个数字只是重复命令。因此,真实数据从第6个字符开始(第一个从0索引开始)。

在编程和调试中,串口监视器是有帮助的,但在实际应用中,液晶屏更便携,满足物联网电源要求。只需将串口监视器更换为液晶屏,即可实时监控您的汽车数据。下面是在我自己的车上使用该项目的照片。

云我们的数据

Arduino MKR 相对于 UNO 的优势在于其互联网可访问性。针对物联网应用,Arduino MKR将使行业更加智能和互联。在汽车应用中,MKR WiFi 1000可能不是最好的板子,因为WiFi信号在室外环境中很少见,但我用手机作为个人热点,所以没有问题。

还有很多其他云平台可以存储、查看和后期处理数据。你可以选择任何你喜欢的。我将以 dweet.io 和 freeboard.io 为例。 Dweet.io 提供了可以发送数据的 API。 Freeboard.io 具有获取 dweet.io 数据并将其可视化的句柄。 dweet.io和freebboard.io的设置教程有很多,不再赘述。如果你有兴趣,这里有一些例子,例子1,例子2。

下图展示了数据推送代码,用于说明如何创建dweet命令。

void httpRequest() { client.stop(); // 创建要发送到干舷的数据字符串 if (client.connect(server, 80)){ Serial.println("Connected");字符串数据 ="POST /dweet/for/mkr1000?RPM=";数据连接(vRPM); // 上传引擎 RPM data.concat("&Speed="); data.concat(vSpeed); // 上传车速 data.concat("&Fuel=");数据连接(vFuel); // 上传燃油油位 data.concat("&Temp=");数据连接(vTemp); // 上传冷却液温度 client.println(data); client.println("主机:https://www.dweet.io"); client.println("连接:关闭"); // 连接结束 client.println(); } else { lcd.clear(); lcd.setCursor(0,0); lcd.println("连接失败"); }} 

在 freeboard.io 上,我们需要创建一个新的仪表板,并在该仪表板中创建一个新的数据源。将此数据源链接到您在代码中定义的 dweet.io 内容。就我而言,它是 mkr1000。创建一个新的 Gauge 小部件,我们将使用它来显示数据。给它一个名字,并将它链接到我们的一个变量。下面是我的仪表板的屏幕截图。它显示速度、RPM、燃油液位和冷却液温度。

结论

我在自己的车上试过这些板子,效果很好。我正在设计包含集成电路中所有功能的 PCB。希望以后我会写更多的教程。我也可能包含一个视频演示。抱歉,这次我不能像开车一样拍摄视频。而且你在街上开车调试代码时也要小心!

Arduino MKR WiFi 板对于这个应用来说已经足够了。如果我有更多的板,我想我可以尝试 MKR GSM 1400 板。随意在本教程中使用其他 IoT 板,并告诉我您的反馈。

在这个项目上工作既有趣又富有教育意义。我很享受调试问题的感觉。我也很高兴在网上分享我所知道的。感谢您的阅读。如果您有任何问题或意见,请告诉我。

代码

  • IoT4Car_code
IoT4Car_codeC/C++
该程序将使用 OBDII-UART 板与车辆对话,并将结果显示在 LCD 上,并上传到干舷物联网平台
/** OBDII-UART-Serial version 9* 该程序将使用 OBDII 与车辆对话-UART 板,* 并在 LCD 上显示结果,并上传到干舷物联网平台* * 作者:zhaoshentech* 更新:2018-08-27* * 更新:* v3:修改了 getResponse() 函数,以便缓冲区接收正确响应。* 添加 getRPM() 以从车辆获取发动机 RPM。* v4:添加 getSpeed() 函数以获取车辆速度 * v5:添加 LCD 模块并在显示屏上显示速度和 RPM LCD* v6:是wifi版本* v7:是非wifi、非串口版本。去掉串口初始化,* 这样板子不用电脑也能工作。* v8:是非wifi,非串口版本。添加燃油液位和冷却液温度。* 重新排列显示位置。* v9:是 wifi,非串行版本。 Upolad 速度、RPM、燃油液位和冷却液温度* * LCD 电路连接:* LCD RS 引脚到数字引脚 12* LCD 启用引脚到数字引脚 11* LCD D4 引脚到数字引脚 5* LCD D5 引脚到数字引脚 4* LCD D6 针到数字针 3 * LCD D7 针到数字针 2* LCD R/W 针到地* 10 K 电位计:* 结束到 +5V 和地* 雨刷到 LCD VO 针(针 3)*///// ////////////////////////////////////////////////// ////// WiFi相关//////////////////////////////////////// ///////////////#include#includechar ssid[] ="你的WIFI SSID"; // wifi IDchar pass[] ="你的WIFI PSWD"; // wifi passwordchar server[] ="www.dweet.io"; // freeboard 和 dweet Settingsunsigned long lastConnectionTime =0; // 跟踪上次连接 timeconst unsigned long postingInterval =10L * 1000L; // 每 10 秒发布一次数据 WiFiClient 客户端; //初始化wifi客户端int status =WL_IDLE_STATUS; // WiFi无线电状态//包括LDC libaray#include const int rs =12, en =11, d4 =5, d5 =4, d6 =3, d7 =2;LiquidCrystal lcd(rs, en, d4, d5, d6, d7);// 这是一个字符缓冲区,将存储来自串口的数据:char rxData[20];char rxIndex =0;char inChar =0;String message;// 变量保持速度和 RPM 数据:int vSpeed =0;int vRPM =0;int vFuel =0;int vTemp =0;void setup() { // 设置 LCD 的列数和行数:lcd.begin( 16,2);液晶显示器(); // 检查屏蔽是否存在: if (WiFi.status() ==WL_NO_SHIELD) { lcd.println("WiFi not ready");同时(真); } // 尝试连接到 WiFi 网络:while (status !=WL_CONNECTED) { lcd.clear(); lcd.setCursor(0, 0); lcd.println("正在连接WiFi..."); status =WiFi.begin(ssid, pass); // 等待 5 秒连接:delay(5000); } lcd.setCursor(0, 1); lcd.println("已连接!"); // Serial1 是与车辆通话的实际端口 Serial1.begin(9600); resetBuffer();}void loop() { while ( status !=WL_CONNECTED) { lcd.clear(); lcd.setCursor(0,0); // 连接到 WPA/WPA2 Wi-Fi 网络 Serial.println("Connecting to Wifi"); lcd.println("连接WiFi..."); status =WiFi.begin(ssid, pass); // 等待 10 秒连接延迟 (5000); } getSpeed();获取转速();获取燃料(); getCoolTemp(); if (millis() - lastConnectionTime> PostingInterval) { httpRequest(); lastConnectionTime =毫秒(); }}// getRPM 数据向Serial1 端口发送“010C”命令// 并调用getResponse() 来收集数据。然后它在串行监视器上打印// RPM 数据。void getRPM(void){ message ="010C"; Serial1.println(消息);延迟(200); //清除当前行 for (int i =8; i <16; ++i) { lcd.setCursor(i, 0); // 0 行,i 列 lcd.write(' '); } lcd.setCursor(8,0); // LCD 屏幕的第一行后半部分 //等待响应 getResponse(); // RPM 响应除以 4 给出正确的值。 vRPM =((strtol(&rxData[6],0,16)*256) + strtol(&rxData[9],0,16))/4;液晶打印(vRPM); lcd.print(" rpm");}void getSpeed(void){ message ="010D"; Serial1.println(消息);延迟(200); //清除当前行: for (int i =0; i <8; ++i) { lcd.setCursor(i, 0); // 0 行,i 列 lcd.write(' '); } lcd.setCursor(0,0);//液晶屏第一行前半部分//等待汽车响应 getResponse(); vSpeed =strtol(&rxData[6], 0, 16); //以km/h为单位 vSpeed =vSpeed * 0.621371; // 以mph为单位 lcd.print(vSpeed); lcd.print(" mph");}void getFuel(void){ message ="012F"; Serial1.println(消息);延迟(200); // 清除当前行: for (int i =0; i <8; i++){ lcd.setCursor(i, 1); // 第一行,第 i 列 lcd.write(' '); } lcd.setCursor(0, 1); // LCD 屏幕的第二行前半部分 //等待来自汽车的响应 getResponse(); vFuel =strtol(&rxData[6], 0, 16); // 规模为 255 //vFuel =244; // 调试使用 vFuel =1.0* vFuel / 255 *100; // 以 100 为刻度 lcd.print(vFuel); lcd.print("%"); //Serial.println(vFuel); // 调试用法}void getCoolTemp(void){ message ="0105"; Serial1.println(消息);延迟(200); // 清除当前行: for (int i =8; i <16; i++){ lcd.setCursor(i, 1); // 第一行,第 i 列 lcd.write(' '); } lcd.setCursor(8, 1); // LCD 屏幕的第二行后半部分 //等待来自汽车的响应 getResponse(); vTemp =strtol(&rxData[6], 0, 16); // 以 C 为单位但偏移 40 度 vTemp =vTemp - 40; // 偏移 0 lcd.print(vTemp); // 打印度 C lcd.write(0xDF); lcd.print("C");}// getResponse 函数将来自 UART 的传入数据收集到 rxData 缓冲区// 并在响应传输时退出。 //一旦检测到回车字符串//,rxData缓冲区就以null结尾(这样我们可以把它当作一个字符串)//并且rxData索引被重置为0,以便可以复制下一个字符串。 void getResponse(void ){ while(Serial1.available()> 0) { // 首先检查我们是否收到了消息结尾字符('\r')。 if(Serial1.peek() =='\r'){ // 到达消息末尾,清空串口缓冲区 inChar =Serial1.read(); rxData[rxIndex] ='\0'; // 重置缓冲区索引,以便下一个字符返回到字符串的开头 rxIndex =0; } // 如果我们没有得到消息字符的结尾,只需将新字符添加到字符串中 else{ // 从串口获取新字符:inChar =Serial1.read(); // 向字符串添加新字符,并增加索引变量: rxData[rxIndex++] =inChar; } }}void resetBuffer(void){ for (int i =0; i <20; i++){ rxData[i] =0; }}void httpRequest() { client.stop(); // 创建要发送到干舷的数据字符串 if (client.connect(server, 80)){ Serial.println("Connected");字符串数据 ="POST /dweet/for/mkr1000?RPM=";数据连接(vRPM); // 上传引擎 RPM data.concat("&Speed="); data.concat(vSpeed); // 上传车速 data.concat("&Fuel=");数据连接(vFuel); // 上传燃油油位 data.concat("&Temp=");数据连接(vTemp); // 上传冷却液温度 client.println(data); client.println("主机:https://www.dweet.io"); client.println("连接:关闭"); // 连接结束 client.println(); } else { lcd.clear(); lcd.setCursor(0,0); lcd.println("连接失败"); }}

示意图

连接 Arduino MKR WiFi 1000、SparkFun OBD-II UART 板、SparkFun Logic Level Shifter 和 LCD 1602 frizling_schematics_M8kF26dafQ.fzz

制造工艺

  1. 牌照
  2. 防抱死制动系统
  3. 什么是动力总成?
  4. 什么是交流发电机?
  5. 使用人工智能避障
  6. Arduino 游戏控制器
  7. 云服装
  8. Pixie:基于 Arduino 的 NeoPixel 手表
  9. Arduino 供电的水壶
  10. 什么是底盘接地?
  11. 车辆底盘布局
  12. 了解车辆接线