Car Driving Simulation using Socket.IO

Car Driving Simulation using Socket.IO

For our Embedded system course here at Kathmandu University, we needed to develop a mini project and we were making a remote control rover system. Before making the real rover, I wanted to try out a simulation. So, I built a car driving simulation using Socket.IO.

I found a lot of chat room applications tutorial using socket.io, one from socket.io itself. Now the job was to translate the messages into the signals to move the car. So, I have decided to write up this little demo, in which we will be creating this car driving simulation using socket.io.

The end product will have a remote, just like the one, of your TV and a car from top view projection in which you can move and rotate the car, with the remote.

So, lets start with the basics.

And if you are in a hurry, here is the GitHub repo link of the project.

What is socket.io

Socket.IO is a JavaScript library for realtime web applications. It enables realtime, bi-directional communication between web clients and servers.

As stated in the homepage of socket.io, ”Socket.IO enables real-time, bidirectional and event-based communication. It works on every platform, browser or device, focusing equally on reliability and speed.”

Setting up the environment

I use VS Code and its my favorite code editor. Which one is yours?

Which is your favorite code editor?

View Results

Loading ... Loading ...

We will be using two packages:

In your project folder directory, open up the terminal and type:

npm init

Leave the values blank, or add your name and description – it’s your choice.

Then we need to install the above packages: express & socket.io.

npm install --save express socket.io

Once, your package.json file looks something like this, lets start building the thing.

Building the app

Starting with the backend, create a new file, here we will be naming it server.js.

This is the final version of what the server.js would look like.

server.js

const express = require('express');
const app = express();
var path = require('path');
const http = require('http').createServer(app);
const io = require('socket.io')(http);

app.use(express.static("public"));

app.get('/', (req, res) =>{
    res.sendFile(path.join(__dirname + '/public/index.html'));
})

app.get('/remote', (req, res) =>{
    res.sendFile(path.join(__dirname + '/public/remote.html'));
})

io.on('connection', function (socket) {

    socket.on('move', function (data) {
       io.emit('move', data);
    });
    socket.on('disconnect', () => {
        console.log('User has disconnected.');
    })
 });

http.listen( 3000, ()=>{
    console.log('App is listening...')
})

Breaking Down the Code:

If you have used express before there is not much new here. We have created some variables, loaded the modules, and served the files.

The thing about socket.io is

const io = require('socket.io')(http);

which sets up a new server instance of socket.io.

And we are setting up the socket namespace connection, we are listening to connection event for incoming sockets and in the callback, we are emitting the message to move the car based upon whatever data is being sent to the server. Basically, the remote will be sending the data to the server and server would be emitting it to the front end, where the front end JavaScript based on the data would move the car.

Then in our directory, inside the public folder we create two files index.html and remote.html for our car page and the remote page.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <style>
        .car{
            height: 40px;
            width: 20px;
            position: absolute;
        }
    </style>
        <link rel="stylesheet" href="/css/car.css?x38250">
    <title>Car</title>
</head>
<body>
    
    <div class="car">
        <img class="the-car" src="/images/car.png?x38250" alt="">
    </div>
 
    <script src="/js/car.js?x38250"></script>
    <script src="/socket.io/socket.io.js?x38250"></script>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="/js/script.js?x38250"></script>
</body>
</html>

remote.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/css/remote.css?x38250">
    <title>Remote</title>
</head>
<body>
    <div class="container">
        <div class="remote-box">
            <h4>Remote</h4>
            <div class="top-box">
                <span class="outer-cover" id="top">
                    <img src="/images/icons/up.svg?x38250" alt="Top">
                </span>
            </div>
            <div class="middle-box">
                <span class="outer-cover" id="left">
                    <img src="/images/icons/left.svg?x38250" alt="Left">
                </span>
                <span class="outer-cover" id="right">
                    <img src="/images/icons/right.svg?x38250" alt="Right">
                </span>
            </div>
            <div class="middle-box">
                <span class="outer-cover" id="rotateLeft">
                    <img src="/images/icons/rotate_left.svg?x38250" alt="Left">
                </span>
                <span class="outer-cover" id="rotateRight">
                    <img src="/images/icons/rotate_right.svg?x38250" alt="Right">
                </span>
            </div>
            <div class="bottom-box">
                <span class="outer-cover" id="bottom">
                    <img src="/images/icons/down.svg?x38250" alt="Bottom">
                </span>
            </div>

            
        </div>
    </div>
    <script src="/socket.io/socket.io.js?x38250"></script>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="/js/remote.js?x38250"></script>
</body>
</html>

So, these are the basic HTML files where we have loaded respective CSS, JS and socket.io JavaScript.

Here, remote.js is the file which is loaded in the remote page and based upon the button press event, it emits the data to the server. For example, if a button with id ‘top’ gets pressed, during this event, a bool variable, which is by default false, would be true. And during the event, ‘move’: ‘top’ message would be emitted to the server, which is then sent to the car page.

remote.js

document.addEventListener("DOMContentLoaded", function() {
  var socket  = io.connect();
  var top = false;
  var right = false;
  var left = false;
  var bottom = false;
  var rotateLeft = false;
  var rotateRight = false;

  var topButton  = document.getElementById('top');
  var leftButton  = document.getElementById('left');
  var rightButton  = document.getElementById('right');
  var bottomButton  = document.getElementById('bottom');
  var rotateLeftButton  = document.getElementById('rotateLeft');
  var rotateRightButton  = document.getElementById('rotateRight');

  topButton.onmousedown = function(e){ top = true; };
  topButton.onmouseup = function(e){ top = false; };

  leftButton.onmousedown = function(e){ left = true; };
  leftButton.onmouseup = function(e){ left = false; };

  rightButton.onmousedown = function(e){ right = true; };
  rightButton.onmouseup = function(e){ right = false; };

  bottomButton.onmousedown = function(e){ bottom = true; };
  bottomButton.onmouseup = function(e){ bottom = false; };

  rotateLeftButton.onmousedown = function(e){ rotateLeft = true; };
  rotateLeftButton.onmouseup = function(e){ rotateLeft = false; };

  rotateRightButton.onmousedown = function(e){ rotateRight = true; };
  rotateRightButton.onmouseup = function(e){ rotateRight = false; };

    function mainLoop() {
        if ( top ) {
          socket.emit('move', 'top');
        }
        if ( left ) {
        socket.emit('move', 'left');
        }
        if ( right ) {
            socket.emit('move', 'right');
        }
        if ( bottom ) {
          socket.emit('move', 'bottom');
        }
        if ( rotateLeft ) {
          socket.emit('move', 'rLeft');
        }
        if ( rotateRight ) {
          socket.emit('move', 'rRight');
        }
       setTimeout(mainLoop, 25);
    }
    mainLoop();
 });

Then in the car page or index.html, car.js is used for positioning of the car, while script.js is used to move the car.

car.js

let car = document.querySelector('.car');
let moveBy = 10;
        
let midx = Math.floor(window.innerWidth / 2);
let midy = Math.floor(window.innerHeight / 2);

window.addEventListener('load', () => {
    car.style.position = 'absolute';
    car.style.left = 0;
    car.style.top = 0;
    car.style.left = parseInt(car.style.left) + midx + 'px';
    car.style.top = parseInt(car.style.top) + midy + 'px';

    myGameArea.start();
});


var myGameArea = {
    canvas : document.createElement("canvas"),
    start : function() {
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;
		this.canvas.id="demo";
        this.context = this.canvas.getContext("2d");
        document.body.insertBefore(this.canvas, document.body.childNodes[0]);
    }
}

deg = 0;

script.js

$(function () {    
    var socket = io();
    socket.on('move', function(data){
        switch (data) {
            case 'left':
                car.style.left = parseInt(car.style.left) - moveBy + 'px';
                break;
            case 'right':
                car.style.left = parseInt(car.style.left) + moveBy + 'px';
                break;
            case 'top':
                car.style.top = parseInt(car.style.top) - moveBy + 'px';
                break;
            case 'bottom':
                car.style.top = parseInt(car.style.top) + moveBy + 'px';
                break;
            case 'rLeft':
                deg -= 5;
                car.style.webkitTransform = 'rotate('+deg+'deg)'; 
                car.style.mozTransform    = 'rotate('+deg+'deg)'; 
                car.style.msTransform     = 'rotate('+deg+'deg)'; 
                car.style.oTransform      = 'rotate('+deg+'deg)'; 
                car.style.transform       = 'rotate('+deg+'deg)'; 
                break;
            case 'rRight':
                deg += 5;
                car.style.webkitTransform = 'rotate('+deg+'deg)'; 
                car.style.mozTransform    = 'rotate('+deg+'deg)'; 
                car.style.msTransform     = 'rotate('+deg+'deg)'; 
                car.style.oTransform      = 'rotate('+deg+'deg)'; 
                car.style.transform       = 'rotate('+deg+'deg)'; 
                break;
        }
    });
});

Here we listen for the socket, and once it is received, based upon the data, we steer the car using JavaScript.

Then, some CSS files for the stylings.

car.css

body{
    margin: 0;
}

canvas {
    border:1px solid #d3d3d3;
    background:url('background.jpg');
    margin: 0;
    padding: 0;
}

.the-car{
    width: 45px;
    height: 80px;
}

remote.css

.container{
    width: 100%;
    /* height: 100vh; */
    display: flex;
    justify-content: center;
    align-items: center;
}

.remote-box{
    border: 2px solid rgb(135, 140, 140);
    background-color: rgba(146, 145, 145, 0.342);
    border-radius: 10px;
    text-align: center;
}

.outer-cover{
    background-color: rgb(173, 211, 206);
    padding: 10px;
    display: flex;
    width: 25px;
    justify-content: center;
    align-items: center;
    border-radius: 50%;
    cursor: pointer;
    margin: 0 auto;
}
.outer-cover:hover{
    background-color: rgb(134, 160, 156);
}

.top-box{
    margin: 0px 0px 20px 0px;
}

.middle-box{
    padding: 10px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    min-width: 150px;
}

.bottom-box{
    margin: 20px 0px 20px 0px;
}

This is what my final file directory looked like.

And as for the icons and images, anything can be used or it is available in my GitHub, along with all the code.

For running the GitHub code from above link, firstly install Node.js and then,

> git clone https://github.com/awanshrestha/car-simulation-socket.git
> cd car-simulation-socket
> npm install
> node server.js
  1. Open http://localhost:3000 on your browser to see the car.
  2. The remote will be at http://localhost:3000/remote

And for the final product we would have our car and the remote.

The Car
The Remote

So, how is it? An socket.io project which is not an another chat application.

Leave a Comment

Your email address will not be published. Required fields are marked *