IoT4Car : Capturer et analyser la télémétrie véhicule avec Arduino MKR1000
Composants et fournitures
| Arduino MKR1000 | ×1 |
| Convertisseur de niveau logique bidirectionnel SparkFun | ×1 |
| SparkFun OBD-II UART | ×1 |
| Câble SparkFun OBD-II vers DB9 | ×1 |
Applications et services en ligne
- Arduino IDE
- freeboard.io (tableau de bord)
- dweet.io (plateforme IoT)
À propos du projet
Contexte
En conduisant, en regardant votre tableau de bord, vous êtes-vous déjà dit qu'il serait utile de collecter ces données pour les analyser ? Ces données recèlent des trésors cachés. Pour les particuliers : habitudes de conduite, vitesse, consommation moyenne, nombre de feux rouges, temps d'attente. Pour les entreprises : surveillance temps réel de flottes, état des véhicules, répartition de la charge, efficacité carburant, localisation – le tout remonté vers un système central via le cloud. Avec le machine learning, on peut prédire les coûts et analyser le comportement des conducteurs. L'IoT se généralise, ces applications ne sont plus loin. Avec les cartes Arduino MKR ciblées IoT, vous pouvez construire un dispositif qui dialogue avec votre voiture et envoie les données télémétriques dans le cloud. N'est-ce pas génial ?
Dialoguer avec un véhicule
Qu'est-ce que l'OBD-II ?
L'OBD (On-Board Diagnostics) est le système d'auto-diagnostic embarqué du véhicule. Introduit aux USA en 1994, obligatoire sur tous les véhicules 1996+. L'OBD-II (2e génération) a 5 protocoles de signalisation, dont le bus CAN (Controller Area Network), obligatoire aux USA depuis 2008. On accède aux données via l'interface 16 broches OBD-II.
Mon contrôleur : Arduino MKR WiFi 1000 pour sa capacité WiFi. L'Arduino MKR GSM 1400 serait encore mieux (couverture GSM plus large), mais le WiFi fonctionne avec un hotspot téléphone.
La carte d'interprétation : SparkFun OBD-II UART Board. Elle interface le bus OBD-II de la voiture, fournit une interface série avec jeux de commandes ELM327, supporte tous les standards OBD-II majeurs dont CAN. Puce STN1110 : convertit les messages entre protocoles OBD-II et UART.
Attention niveau de tension : La carte OBD-II sort du 5V, l'Arduino MKR1000 tolère 3,3V max sur ses E/S. Un convertisseur de niveau (level shifter) bidirectionnel 5V↔3,3V est indispensable (SparkFun Logic Level Converter).
Câblage : Arduino MKR broches 13 (Rx) et 14 (Tx) → carte OBD-II UART Tx/Rx via le level shifter. Masse commune. Un écran LCD 1602 est aussi connecté pour affichage temps réel (câblage standard, non détaillé ici).
Serial1, pas Serial ! Sur l'Arduino MKR, le port série sur broches 13/14 est Serial1. Serial désigne le port USB (communication PC). N'oubliez pas d'initialiser Serial1.begin(9600) dans setup() et d'utiliser Serial1.println(message) pour envoyer les commandes.
Commandes OBD
Les commandes sont des codes hexadécimaux en ASCII. Les 2 premiers chiffres = mode de service (10 services dans SAE J1979). Pour la surveillance temps réel, on utilise le mode 01 (données courantes).
Le PID (Parameter ID) suit le mode. PIDs utilisés :
010D– Vitesse véhicule010C– Régime moteur (RPM)012F– Niveau carburant0105– Température liquide de refroidissement
Décodage des données
Après envoi, l'Arduino écoute Serial1 (délai 200 ms recommandé). La réponse suit le format : >1 PID_Répété Données.
Exemples :
010D→ réponse>1 0D 00: vitesse = 0x00 km/h010C→ réponse>1 0C 09 8C: RPM = 0x098C / 4 = 611 tr/min012F→ réponse>1 2F 1D: carburant = 0x1D / 255 × 100 = 11%0105→ réponse>1 05 79: température = 0x79 - 40 = 81°C
Les vraies données commencent au 6e caractère (index 5). Remplacez le moniteur série par l'écran LCD pour une utilisation portable/autonome.
Envoi des données dans le cloud
L'atout du MKR1000 vs UNO : l'accès Internet. Pour l'automobile, le WiFi est rare en extérieur – j'utilise mon téléphone en hotspot personnel.
Exemple avec dweet.io + freeboard.io. Dweet.io reçoit les données via API, freeboard.io les visualise. Configuration : créer un dashboard freeboard, source de données liée à votre « thing » dweet.io (ici « mkr1000 »), widgets jauge pour chaque variable.
Code d'envoi HTTP :
void httpRequest() {
client.stop();
if (client.connect(server, 80)) {
Serial.println("Connected");
String data = "POST /dweet/for/mkr1000?RPM=";
data.concat(vRPM);
data.concat("&Speed="); data.concat(vSpeed);
data.concat("&Fuel="); data.concat(vFuel);
data.concat("&Temp="); data.concat(vTemp);
client.println(data);
client.println("Host: https://www.dweet.io");
client.println("Connection: close");
client.println();
} else {
lcd.clear(); lcd.setCursor(0,0); lcd.println("Connection failed");
}
}
Conclusion
Testé sur ma voiture, ça fonctionne. Je travaille sur un PCB intégré. Peut-être plus de tutos et une vidéo démo à l'avenir. Attention en déboguant en conduisant !
L'Arduino MKR WiFi 1000 suffit pour cette application. Avec plus de cartes, j'essaierais le MKR GSM 1400. Libre à vous d'utiliser d'autres cartes IoT avec ce tutoriel.
Ce projet est fun et éducatif. Merci de votre lecture, questions et commentaires bienvenus.
Code
IoT4Car_code (C/C++)
/* OBDII-UART-Serial version 9
Dialogue véhicule via OBDII-UART, affichage LCD, upload freeboard IoT
Auteur: zhaoshentech | MAJ: 2018-08-27
v3: getResponse() corrigé, ajout getRPM()
v4: ajout getSpeed()
v5: ajout module LCD vitesse/RPM
v6: version WiFi
v7: sans WiFi, sans Serial (autonome)
v8: ajout carburant + température, réorganisation affichage
v9: version WiFi, sans Serial, upload vitesse/RPM/carburant/temp
*/
////////////////////////////////////////////////////////
// WiFi
////////////////////////////////////////////////////////
#include <SPI.h>
#include <WiFi101.h>
char ssid[] = "VOTRE_WIFI_SSID";
char pass[] = "VOTRE_WIFI_MDP";
char server[] = "www.dweet.io";
unsigned long lastConnectionTime = 0;
const unsigned long postingInterval = 10L * 1000L; // 10 secondes
WiFiClient client;
int status = WL_IDLE_STATUS;
////////////////////////////////////////////////////////
// LCD
////////////////////////////////////////////////////////
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
////////////////////////////////////////////////////////
// Variables
////////////////////////////////////////////////////////
char rxData[20]; char rxIndex = 0; char inChar = 0;
String message;
int vSpeed = 0, vRPM = 0, vFuel = 0, vTemp = 0;
void setup() {
lcd.begin(16, 2); lcd.clear();
if (WiFi.status() == WL_NO_SHIELD) { lcd.println("WiFi not ready"); while(true); }
while (status != WL_CONNECTED) {
lcd.clear(); lcd.setCursor(0, 0); lcd.println("Connecting WiFi...");
status = WiFi.begin(ssid, pass);
delay(5000);
}
lcd.setCursor(0, 1); lcd.println("Connected!");
Serial1.begin(9600);
resetBuffer();
}
void loop() {
while (status != WL_CONNECTED) {
lcd.clear(); lcd.setCursor(0, 0);
Serial.println("Connecting to Wifi");
lcd.println("Connect WiFi...");
status = WiFi.begin(ssid, pass);
delay(5000);
}
getSpeed(); getRPM(); getFuel(); getCoolTemp();
if (millis() - lastConnectionTime > postingInterval) {
httpRequest();
lastConnectionTime = millis();
}
}
void getRPM() {
message = "010C";
Serial1.println(message);
delay(200);
for (int i = 8; i < 16; ++i) { lcd.setCursor(i, 0); lcd.write(' '); }
lcd.setCursor(8, 0);
getResponse();
vRPM = ((strtol(&rxData[6], 0, 16) * 256) + strtol(&rxData[9], 0, 16)) / 4;
lcd.print(vRPM); lcd.print(" rpm");
}
void getSpeed() {
message = "010D";
Serial1.println(message);
delay(200);
for (int i = 0; i < 8; ++i) { lcd.setCursor(i, 0); 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() {
message = "012F";
Serial1.println(message);
delay(200);
for (int i = 0; i < 8; i++) { lcd.setCursor(i, 1); lcd.write(' '); }
lcd.setCursor(0, 1);
getResponse();
vFuel = strtol(&rxData[6], 0, 16);
vFuel = 1.0 * vFuel / 255 * 100;
lcd.print(vFuel); lcd.print(" %");
}
void getCoolTemp() {
message = "0105";
Serial1.println(message);
delay(200);
for (int i = 8; i < 16; i++) { lcd.setCursor(i, 1); lcd.write(' '); }
lcd.setCursor(8, 1);
getResponse();
vTemp = strtol(&rxData[6], 0, 16);
vTemp = vTemp - 40;
lcd.print(vTemp); lcd.write(0xDF); lcd.print("C");
}
void getResponse() {
while (Serial1.available() > 0) {
if (Serial1.peek() == '\r') {
inChar = Serial1.read();
rxData[rxIndex] = '\0';
rxIndex = 0;
} else {
inChar = Serial1.read();
rxData[rxIndex++] = inChar;
}
}
}
void resetBuffer() {
for (int i = 0; i < 20; i++) rxData[i] = 0;
}
void httpRequest() {
client.stop();
if (client.connect(server, 80)) {
Serial.println("Connected");
String data = "POST /dweet/for/mkr1000?RPM=";
data.concat(vRPM);
data.concat("&Speed="); data.concat(vSpeed);
data.concat("&Fuel="); data.concat(vFuel);
data.concat("&Temp="); data.concat(vTemp);
client.println(data);
client.println("Host: https://www.dweet.io");
client.println("Connection: close");
client.println();
} else {
lcd.clear(); lcd.setCursor(0,0); lcd.println("Connection failed");
}
}
Schématiques
Câblage : Arduino MKR WiFi 1000 + SparkFun OBD-II UART + SparkFun Logic Level Shifter + LCD 1602 (fichier Fritzing disponible).
Processus de fabrication
- Guide complet des plaques d'immatriculation : histoire, design et avenir
- Système de freinage antiblocage (ABS) : sécurité sur routes glissantes
- Comprendre un groupe motopropulseur : comment il propulse votre véhicule
- Comprendre l'alternateur : comment il alimente votre voiture et signes de défaillance
- Système d'Évitement d'Obstacles par Intelligence Artificielle (Q-Learning) sur Arduino
- Manette de jeu USB Arduino DIY – Construisez votre propre gamepad haute performance
- Comprendre la masse châssis : objectif, fonctionnement et importance
- Disposition du châssis automobile : position du moteur et configurations de transmission expliquées
- Le câblage automobile décrypté : notions clés pour une performance fiable