Monday, February 20, 2017


 I show how to collect detailed CAN bus data from a Tesla Model S, and display it in the car's own web browser!

Complete Raspberry Pi with CAN bus shield and 12V buck converter: http://copperhilltech.com/raspberry-pi-3-system-with-can-bus-interface/

Tesla diagnostic connector: https://www.panjo.com/buy/tesla-diagnostic-cable-for-ms-mx-sept-2015-and-up-bare-wire-version-308125

RPi CAN bus tutorial: http://www.cowfishstudios.com/blog/canned-pi-part1

Get node.js installed on the RPi: http://thisdavej.com/beginners-guide-to-installing-node-js-on-a-raspberry-pi/

I used socket.io, which is a module that installs into node.js: https://www.npmjs.com/package/socket.io

Diagnostic connector pinout (scroll through the images):
http://store.evtv.me/proddetail.php?prod=TeslaCANKit





Tesla CAN bus reverse engineering: https://skie.net/skynet/downloads

Basic sequence to setup the server on the Pi:
sudo ip link set can0 type can bitrate 500000 listen-only on
sudo ip link set can0 up

candump -cae can0,0:0,#FFFFFFFF

candump -cae can0,0:0,#FFFFFFFF | grep 106 | awk -Winteractive '{ print $10}' | node app.js

I will post the node.js code on my blog:


Support Applied Science on Patreon:
https://www.patreon.com/AppliedScience


app.js:

var readline = require('readline');
var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});
pipedata = '';
rl.on('line', function(line){
    console.log(line);
pipedata = line;
});

var http = require('http'),
    fs = require('fs'),
    // NEVER use a Sync function except at start-up!
    index = fs.readFileSync(__dirname + '/index.html');

// Send index.html to all requests
var app = http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.end(index);
});

// Socket.io server listens to our app
var io = require('socket.io').listen(app);

// Send current time to all connected clients
function sendTime() {
//    io.emit('time', { time: new Date().toJSON() });
    io.emit('time', pipedata);
}

// Send current time every 10 secs
setInterval(sendTime, 100);

// Emit welcome message on connection
io.on('connection', function(socket) {
    // Use socket to communicate with this particular client only, sending it it's own id
    socket.emit('welcome', { message: 'Welcome!', id: socket.id });

    socket.on('i am client', console.log);
});

app.listen(8080);


index.html:


<!doctype html>

<html>

    <head>

        <script src='/socket.io/socket.io.js'></script>

        <script>

            var socket = io();



            socket.on('welcome', function(data) {

                addMessage(data.message);



                // Respond with a message including this clients' id sent from the server

                socket.emit('i am client', {data: 'foo!', id: data.id});

            });

            socket.on('time', function(data) {

                addMessage(data);

            });

            socket.on('error', console.error.bind(console));

            socket.on('message', console.log.bind(console));



            function addMessage(message) {

              //  var text = document.createTextNode(message);

                  document.getElementById('messages').textContent = message;

        //     el = document.createElement('li'),

               //     messages = document.getElementById('messages');



              //  el.appendChild(text);

              //  messages.appendChild(el);

            }

        </script>



<style>

p.sansserif { font-family: Arial, Helvtica, sans-serif; font-size:300%;}

</style>



    </head>

    <body>

        <p class = "sansserif" id='messages'>Hello</p>

    </body>

</html>




20 comments:

  1. Thanks for sharing.
    Could you by any chance also share som snippets of canbus data?
    I have done some development for the center console browser before, and would like to play around with it.

    I think D3 would be a nice fit for this kind of project of streaming data, and live updates. D3 is an open source javascript library for manipulating documents/graphics based on data. It has great documentation, lots of examples and a solid community.

    85D owner from Tromsø/Norway.

    ReplyDelete
  2. Thanks for this! I've just purchased the hardware and cannot wait to see what you are able to make of this.

    ReplyDelete
  3. Um, what happened to the semiconductor fabrication tools that you were making a while back?

    ReplyDelete
  4. Thank for share, for me this is new knowledge. My basic is nutrition.

    ReplyDelete
  5. Any idea what those spikes on the scope display are coming from? Surely that can't be a feature of CAN? It almost looks like the scope's ground wire is not connected.

    ReplyDelete
    Replies
    1. The scope ground was definitely connected, however I agree that those spikes look pretty gnarly. The scope was being powered from 110VAC from an inexpensive 12V inverter with a three-prong plug. So, it is likely the scope chassis was grounded to the car's earth, and there were possibly parallel ground paths. I noticed that the scope was able to decode the data with no problem, so I ignored the spikes.

      Delete
  6. Not to be too picky but i think you said odb2 should be obd2 (on board idagnostics..)

    ReplyDelete
  7. This is so cool. I hoped for someone to do this. Looking forward to your updates! Maybe you can sell a ready-to-use kit sometime in the future? I would buy that in a heartbeat!

    ReplyDelete
  8. What version of the Model S did you go with, Ben?

    ReplyDelete
    Replies
    1. P100D :) The next video on this topic should show some really extreme automotive physics experiments.

      Delete
  9. Have you looked into a grafana and influxdb setup?

    ReplyDelete
  10. This comment has been removed by the author.

    ReplyDelete
  11. Hi Ben, is there a private way to contact you?

    ReplyDelete
  12. Connector is widely availible. Here is Mouser page of connector shell http://www.mouser.com/search/ProductDetail.aspx?r=571-173851-2

    pins: http://www.mouser.com/search/ProductDetail.aspx?r=571-173630-6

    ReplyDelete
    Replies
    1. Thanks! This may be for pre-2015 cars. Mouser shows this as a 12-position connector, and the 2015 and later cars have a larger 18 position connector.

      Delete
  13. Hello Ben. Firstly, I would like to thank you for sharing this example.
    I have a question though - why did you use those pipes "grep 106 | awk -Winteractive '{ print $10}'" before executing node app?
    What do they mean?
    I am asking because I can't get any data to be printed on the webpage, but candump works while executed standalone.

    ReplyDelete
    Replies
    1. Grep 106 - this shows only candump packets with ID 106. (it just looks for lines of text that contain 106, and drops all others)
      awk -Winteractive '{ print $10}' - any packet containing 106 is dumped into awk that pulls out the 10th field in the line. In this case, it's the last hex byte of data from the can 106 packet.

      The reason you may not see any data, is because you don't have any canbus ID 106 packets.

      Delete
  14. This comment has been removed by the author.

    ReplyDelete
  15. I had some issues with buffering, on raspbian. Here's what I had to do to get the output to run smoothly.

    candump -cae vcan0,0:0,#FFFFFFFF | grep 0D1 --line-buffered | mawk -W interactive '{ print $4; system("")}' | node app.js

    ReplyDelete
    Replies
    1. I started building an app to take all this can bus data and quickly reached the limitations of piping raw data into the node server.

      Instead you can use this to use node to spawn a child process to capture data. It's much more responsive

      var spawn = require('child_process').spawn,
      candump = spawn('candump',['vcan0']);

      var io = require('socket.io').listen(app);
      candump.stdout.on('data', function (data) {
      $pipedat = data.toString().split("\n"); // data comes in multiple lines
      $pipedat.forEach(function (e) {
      if (e !== "") { // last line is usually empty, ignore
      $o = e.split(" "); // split line data by spaces (alt to awk)
      $msgline = [$o[4],$o.slice(9).join("")]; // output in "123 DEADBEEF12345678" format
      //console.log('\033[2J'); // cls
      $rt = nodelib.addMessage($msgline[0],$msgline[1]);
      io.emit('data', $rt);
      //console.log($rt.engine.RPM);
      }
      });
      });
      app.listen(8088);

      Delete