MQTT OBD Vehicle Telemetry

Fancy to see your's vehicle data online in real-time? If so, continue reading!

Things used in this project

Hardware components

×

1

Jumper wires (generic)

×

1

Jumper wires (generic)

Breadboard (generic)

×

1

Breadboard (generic)

u-blox NEO-6M

GPS module

×

1

u-blox NEO-6M

GPS module

ELM327

×

1

ELM327

Story

1) In a nutshell

So today we will connect our car to the internet. Literally. Using Arduino MKR1000 & ELM327 micro-controllers we will send various data from car's engine control unit (RPM, Engine temperature, Velocity.....) to the IBM Watson Cloud using MQTT protocol. Also we will create responsive web-app interface using Node-RED (available on IBM Watson Cloud) and custom iOS application with SwiftUI & CocoaPod's MQTT library.

2) Hardware needed

Core of the project is based on Arduino MKR1000. The advantage of this little device is embedded WiFi shield, that allows us create connection to the IBM Watson Cloud. Next part is ELM327 - micro-controller connected through UART to the Arduino. This little fellow allows us to read ECU's data using PID codes send from Arduino. Since we are connecting vehicle to the internet, it is good manner to see it's location. So for this reason we are also using NEO-6M GPS module.

3) IBM Watson Cloud

Watson Cloud is great solution for processing & visualizing collected data. It has various resources, but what You'll basically need is to register on https://cloud.ibm.com/ and create resource for IoT platform. This resource is used as MQTT broker and will generate URL for broker (server). Inside the platform we will create authentication credentials for two MQTT clients - Arduino & iOS application. Also we can change security of broker to TLS optional, since we are sending data in plain text on port 1883. Next thing is creating Node-RED resource. This will allow us to process data from MQTT clients (publishers/subscribers), but also create a web-interface. After successful creation of Node-RED resource we have to import custom palettes. For interconnection between Node-RED resource & IoT platform resource, import node-red-contrib-scx-ibmiotap. For creating web-app ui import nodered-dashboard. Last, but no least, to interconnect Node-RED with IoT platform, go back to your Node-RED resource in Watson Cloud, select Connections/CreateConnection and choose Connect to your resource of IoT platform.

It is important to mention that we are working with free version of IBM Watson Cloud. But since we are sending small ECU data (JSON format) using MQTT (min. overhead size is just 2 Bytes), Cloud's capacity at 200 MB / monthly means enough space in the end.

On figure 3.1 we can see final Node-RED flow, available at the end of this article. Basically You'll need to modify dark-blue nodes - change MQTT credentials based on your IoT platform. All other blocks can stay the same. Maybe there's question why do we have so many function blocks - the answer is, that they are used to split data based on the MQTT topic - e.g. if we have web-app gauge with engine temperature, we will send only engine temperature there and skip other data (RPM, velocity.......).

After successful importing of flow code, hit the Deploy button. To access created web-app UI replace /red/xxxxxx in your URL with /ui

3) Hardware connection

Since Arduino MKR1000 has only one UART connection by default, connect GPS module RX pin to Arduino pin 0 and TX pin to Arduino pin 1. We will define second UART in.ino code.

4) Arduino software

Code for Arduino is attached in the end of this article. What You'll need to do, is to make sure you have following libraries installed:

WiFi101.h

MQTTClient.h

wiring_private.h

TinyGPS++.h

Next change your Wi-Fi credentials and MQTT credentials based on your IoT platform (broker and client credentials). There is also code for header file premenne.h - make sure you put this file into your project folder, as it obtains functions for retrieving data from vehicle's ECU, with their conversion from HEX to DEC.

5) iOS application

This part is completely optional, and doesn't affect running of your web-app. But if you want to have dedicated application, follow these steps:

  • Create new Xcode project

  • Install Cocoapods using terminal with following commands: sudo gem install cocoapod and pod setup. This process takes a while, so don't worry.

SwiftUI project codes are included in the end of this article, only things You'll have to change are MQTT credentials in ContentView, based on IBM IoT platform. The output of application is displayed on figure 5.1, where we can see 3 horizontal parts:

a) Buttons on top - used for connecting to MQTT broker and publishing/subscribing to topic of our choice (Temperature, RPM.......)

b) MapView - map with annotation mark informing us of vehicle's current location

c) Horizontal-scrolling menu - gauges with ECU's data

Fig. 5.1: iOS application

6) Final output of web-app

Created Node-RED web-app UI obtains two choices from hamburger menu: Static test - used for retrieving only one value from ECU and Dynamic test - retrieving value from ECU every 2 seconds (based on.ino delays - can be changed). Static test UI is displayed on figure below.

Fig. 6.1: Node-RED web-app UI

If You have any questions, do not hesitate to ask.

Schematics

ELM327 + NEO-6M connection to Arduino MRK1000

Code

  • Main .ino file

  • Header file premenne.h

  • Node-RED flow

  • ContentView - main Swift code

  • MapView - Map with annotation mark Swift code

  • Temperature Gauge - Swift code

  • Battery voltage gauge - Swift code

  • Velocity gauge - Swift code

  • RPM gauge - Swift code

MapView - Map with annotation mark Swift code

SwiftFor iOS application

//
//  MapView.swift
//  RingViewRPM
//
//  Created by Kardan on 01/04/2020.
//  Copyright © 2020 Kardan. All rights reserved.
//

import SwiftUI
import MapKit


struct MapView: UIViewRepresentable {
    var predtym:Double
    var nazov:String="sevas"
    var dlzka = 34.011286
    var sirka = -116.166868
    @State var prava:Bool=true
    
    func makeUIView(context: Context) -> MKMapView {
        MKMapView(frame: .zero)
    }
    
    func updateUIView(_ uiView: MKMapView, context: Context) {
        
    
        let coordinate = CLLocationCoordinate2D(
            latitude: dlzka, longitude: sirka)
        let sponka=MKPointAnnotation()
        sponka.coordinate=coordinate
        sponka.title=nazov
        let span = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        uiView.setRegion(region, animated: true)
        let annotations = uiView.annotations
        uiView.removeAnnotations(annotations)
        uiView.addAnnotation(sponka)
        
        
}
    
    
    
}

Last updated