Sunday, 24 August 2014

Augmented Reality

It has been a long time i didn't update my blog. I've done some big and small project between this periods and i gonna post it here gradually :)


Today I'm gonna share about a image processing applications. This is one of my assignment during my diploma studies. I'm using Processing to write this application and most of the codes are referring from this website. I just do some modifications on the code, add more features, and implement it with the live camera view. You will need to download the NyArtoolkit and also GsVideo for Processing to perform this function.

The original code should display the cube on the markers detected in the live camera. I modified the program to recognized few markers and display different patterns on each of it at one time.  If you wish to displace more and different pattern you just need to Google for different pattern or shape code and put it into the Processing. Also, in this Augmented reality application, I added few rows of codes so that i can edit the scales of the pattern shown in the screen in the live camera. 

Below are the code i'm using for this application with the demo video:

video

// Processing + NyARToolkit + GSVideo = Argumeneted reality
 
import java.io.*; // for the loadPatternFilenames() function
import processing.opengl.*; // for OPENGL rendering
import jp.nyatla.nyar4psg.*; // the NyARToolkit Processing library
import codeanticode.gsvideo.*; // the GSVideo library
 
// the full path to the camera_para.dat file
String camPara = "C:/Users/kit/Documents/Processing/libraries/nyar4psg/data/camera_para.dat";
// the full path to the .patt pattern files
String patternPath = "C:/Users/kit/Documents/Processing/libraries/nyar4psg/patternMaker/examples/ARToolKit_Patterns";
// the dimensions at which the AR will take place.
int arWidth = 640;
int arHeight = 360;
// the number of pattern markers (from the complete list of .patt files) that will be detected, here the first 10 from the list.
int numMarkers = 10;
float scales = 1.0;
float mS = 0.2;
 
GSCapture cam;
MultiMarker nya;
float[] scaler = new float[numMarkers];
color[] colors = new color[numMarkers];
PVector rot, speed; 

void setup() {
  size(1280, 720, OPENGL); // the sketch size
  cam = new GSCapture(this, 1280, 720); // initializing the webcam capture at a specific resolution 
  cam.start(); // start capturing
  noStroke(); // turn off stroke for the rest of this sketch
  // create a new MultiMarker at a specific resolution (arWidth x arHeight), with the default camera calibration and coordinate system
  nya = new MultiMarker(this, arWidth, arHeight, camPara, NyAR4PsgConfig.CONFIG_DEFAULT);
  // set the delay after which a lost marker is no longer displayed. by default set to something higher, but here manually set to immediate.
  nya.setLostDelay(1);
  String[] patterns = loadPatternFilenames(patternPath);
  rot = new PVector(random(TWO_PI), random(TWO_PI), random(TWO_PI)); // random x, y, z rotation
  speed = new PVector(random(-mS, mS), random(-mS, mS), random(-mS, mS)); // random x, y, z speed (within maxSpeed boundaries)
  // for the selected number of markers, add the marker for detection
  for (int i=0; i<numMarkers; i++) {
    nya.addARMarker(patternPath + "/" + patterns[i], 80);
    scaler[i] = random(0.8, 1.9); // scaled a little smaller or bigger
    colors[i] = color(random(255), random(255), random(255), 160); // random color, always at a transparency of 160
   
  }
}
 
void draw() {
  if (cam.available()) {
    cam.read();
    background(cam); // a background call is needed for correct display of the marker results
    // create a copy of the cam image at the resolution of the AR detection to avoid error
    PImage cSmall = cam.get();
    cSmall.resize(arWidth, arHeight);
    nya.detect(cSmall); // detect markers in the image
    
   drawShapes();
   println("Scale : " + scales);
  }
}


 
// this function draws correctly placed 3D boxes on top of detected markers
void drawShapes() {
  // set the AR perspective uniformly, this general point-of-view is the same for all markers
  nya.setARPerspective();
  // set the text alignment (full centered) and size (big)
  textAlign(CENTER, CENTER);
  textSize(20);

  for (int i=0; i<numMarkers; i++) {
    // checking exist marker
    if ((!nya.isExistMarker(i))) { continue; }
    
    if(i==0) //MARKER NUMBER 0
    {
    // get the Matrix for this marker and use it (through setMatrix)
    setMatrix(nya.getMarkerMatrix(i));
    scale(1, -1); // turn things upside down, for text display
    scale(scales); //decide the size of the boxes
    translate(0, 0, 20); // translate the box by half (20, draw frm marker) of it's size (40,draw in air)
    //move up 20 from the base marker
    lights(); // turn on some lights,makes the box color nicer
    stroke(0); // give the box a black stroke/line
    fill(colors[i]); // fill color
    box(40); // the box
    noLights(); // turn off the lights
    translate(0, 0, 20.1); // translate to just slightly above the box (to prevent OPENGL uglyness)
    noStroke(); 
    fill(255, 50);
    rect(-20, -20, 40, 40); // display a transparent white rectangle right above the box
    translate(0, 0, 0.1); // translate to just slightly above the rectangle (to prevent OPENGL uglyness)
    fill(0);
    text("" + i, -20, -20, 40, 40); // display the ID of the box in black text centered in the rectangle
    }
    
    if (i == 3) //MARKER NUMBER 3
    {
     // get the Matrix for this marker and use it (through setMatrix)
    setMatrix(nya.getMarkerMatrix(i));
    scale(scales); //decide the size of the boxes
    translate(0, 0, 20); // translate the sphere by half (20, draw frm marker) of it's size (40,draw in air)
    //move up 20 from the base marker
    lights(); // turn on some lights,makes the box color nicer
    stroke(colors[i]); //no line needed
    fill(255,160); // fill color
    sphere(20); // the box
    //noStroke(); 
    }
    
     if (i == 5) //MARKER NUMBER 5
    { 
     colorMode(RGB, 1); //set color mode to 1 only for easy fill color for the matrix
     rot.add(speed); // always keep rotating
     // get the Matrix for this marker and use it (through setMatrix)
    setMatrix(nya.getMarkerMatrix(i));
    translate(0, 0, 30); // translate the sphere by half (20, draw frm marker) of it's size (40,draw in air)
    // rotate the cube in 3 dimensions
    rotateX(rot.x);
    rotateY(rot.y);
    rotateZ(rot.z);
    scale(scales*10);
 
    // a cube made out of 6 quads
    // the 1 range can be used for both the color and the coordinates as a result of color range and scale (see earlier)
    beginShape(QUADS);
 
    fill(0, 1, 1); vertex(-1,  1,  1);
    fill(1, 1, 1); vertex( 1,  1,  1);
    fill(1, 0, 1); vertex( 1, -1,  1);
    fill(0, 0, 1); vertex(-1, -1,  1);
  
    fill(1, 1, 1); vertex( 1,  1,  1);
    fill(1, 1, 0); vertex( 1,  1, -1);
    fill(1, 0, 0); vertex( 1, -1, -1);
    fill(1, 0, 1); vertex( 1, -1,  1);
 
    fill(1, 1, 0); vertex( 1,  1, -1);
    fill(0, 1, 0); vertex(-1,  1, -1);
    fill(0, 0, 0); vertex(-1, -1, -1);
    fill(1, 0, 0); vertex( 1, -1, -1);
 
    fill(0, 1, 0); vertex(-1,  1, -1);
    fill(0, 1, 1); vertex(-1,  1,  1);
    fill(0, 0, 1); vertex(-1, -1,  1);
    fill(0, 0, 0); vertex(-1, -1, -1);
 
    fill(0, 1, 0); vertex(-1,  1, -1);
    fill(1, 1, 0); vertex( 1,  1, -1);
    fill(1, 1, 1); vertex( 1,  1,  1);
    fill(0, 1, 1); vertex(-1,  1,  1);
 
    fill(0, 0, 0); vertex(-1, -1, -1);
    fill(1, 0, 0); vertex( 1, -1, -1);
    fill(1, 0, 1); vertex( 1, -1,  1);
    fill(0, 0, 1); vertex(-1, -1,  1);
 
    endShape();
    colorMode(RGB, 255);
    }
  }
  // reset to the default perspective
  perspective();
}
// this function loads .patt filenames into a list of Strings based on a full path to a directory (relies on java.io)
String[] loadPatternFilenames(String path) {
  File folder = new File(path);
  FilenameFilter pattFilter = new FilenameFilter() {
    public boolean accept(File dir, String name) {
      return name.toLowerCase().endsWith(".patt");
    }
  };
  return folder.list(pattFilter);
}


void keyPressed() {
  if (key == 'z' && scales < 2.0) {
   // scales = 1.5;
   scales = scales + 0.1;
  }
  else if(key == 'x' && scales > 0.7) {
    scales = scales -0.1;
    //scales = 0.8;
  }
}

Thursday, 26 December 2013

Sorry about the video of the Bluetooth control robot, I'm too busy with my final year project recently :p Just dug out the video from some old files, enjoy:) Youtube LinkBluetooth Controlled robot video

Monday, 15 April 2013

Use Android smartphone to control two wheels robot with Arduino via Bluetooth

3) Android programming part using Processing

For the programming for my android smart phone, I've tried using few ways to establish the bluetooth connection between Arduino and my smart phone. I've tried to use few library to do the programming part, like sweetbt and btserial library, but these two libraries don’t function well as my smart phone could not successfully connect to the device via Bluetooth. The btserial library can be download at this website but the .jar file is seem to be missing and waiting for rebuild.

So I tried to change to eclipse, although I manage to download the application into my smart phone(sony Xperia X10i), but the coding for eclipse is much more harder compared to processing as I totally don’t have the basic for android programming it will be very hard for me to use the eclipse. So I try to find another alternative way to do the android programming. But lastly, I managed to find a library named Ketai, for processing, which works perfectly to communicate with the Arduino Bluetooth shield via Bluetooth, and the coding is much easier than eclipse. 

You can download the Ketai library from this website and place it in your sketch "library" file : https://code.google.com/p/ketai/downloads/list

Before download the codes into your android smartphone via processing, pls do enable the sketch permission of BLUETOOTH and BLUETOOTH ADMIN via the "Android" tab. If you forgot to tick this 2 things, the apps downloaded into your smart phone cannot be launched.


I've edited the code base on this original codes, and it is very helpful for me:)
Below will be the final coding of the android processing code with implemented Ketai library function:

//required for BT enabling on startup
import android.content.Intent;
import android.os.Bundle;
import ketai.net.bluetooth.*;
import ketai.ui.*;
import ketai.net.*;
PFont fontMy;                                    //declaring font
boolean bReleased = true;       //no permament sending when finger is tap
KetaiBluetooth bt;                  // Create object from BtSerial class
boolean isConfiguring = true;
String info = "";
KetaiList klist;
ArrayList devicesDiscovered = new ArrayList(); //store in array the discovered device
boolean rectOver = false;
int rec = 0;

// The following code is required to enable bluetooth at startup.
void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
bt = new KetaiBluetooth(this);//create the BtSerial object that will handle the connection
}

void onActivityResult(int requestCode, int resultCode, Intent data)
 {
bt.onActivityResult(requestCode, resultCode, data);
}//to show the discovered device

void setup()
{
size(displayWidth, displayHeight); //size of my phone screen
smooth();
frameRate(10);                        //the frame rate of my screen
orientation(PORTRAIT);        //vertical
bt.start();                                 //start listening for BT connections
isConfiguring = true;               //at my phone start select device…
fontMy = createFont("SansSerif", 20); //font size
textFont(fontMy);
}

void draw()
{
//at app start select device
if (isConfiguring)

{
ArrayList names;

//create the BtSerial object that will handle the connection
//with the list of paired devices
klist = new KetaiList(this, bt.getPairedDeviceNames());

isConfiguring = false;                         //stop selecting device
}

else
{
color a = color(255,0,0);                      //the color for each button of the interface
color b = color(255,0,0);
color c = color(255,0,0);
color d = color(255,0,0);
color e = color(165,8,27);
color f = color(165,8,27);
color g = color(165,8,27);
color h = color(165,8,27);
color i = color(18,21,121);
color j = color(255,255,255);
color k = color(255,255,255);
color l = color(0,0,0);
update(mouseX, mouseY);                 //update our finger point at where of the screen
background(45,12,137);                      //background color

if((mousePressed)&&(rectOver)&&(rec==1))  //when we click the button’s color change  
                                                                              accordingly with different color code
{      c = color(10,237,26);
}
else if ((mousePressed)&&(rectOver)&&(rec==2))
{       d = color(10,237,26);
}
else if ((mousePressed)&&(rectOver)&&(rec==3))
{       a = color(10,237,26);
}
else if ((mousePressed)&&(rectOver)&&(rec==4))
{       b = color(10,237,26);
}
else if ((mousePressed)&&(rectOver)&&(rec==5))
{       j = color(74,182,252);
}
else if ((mousePressed)&&(rectOver)&&(rec==6))
{      k = color(74,182,252);
}
else if ((mousePressed)&&(rectOver)&&(rec==7))
{      e = color(10,237,26);
}
else if ((mousePressed)&&(rectOver)&&(rec==8))
{      f = color(10,237,26);
}
else if ((mousePressed)&&(rectOver)&&(rec==9))
{      g = color(10,237,26);
}
else if ((mousePressed)&&(rectOver)&&(rec==10))
{      h = color(10,237,26);
}
else if ((mousePressed)&&(rectOver)&&(rec==11))
{      l = color(255,255,255);
}


if ((rec == 2) && (rectOver)&&(mousePressed) && (bReleased == true)) { 
// If our finger is on the square,
    byte[] data = {'w'};           // send w to arduino when we click the button 2
    bt.broadcast(data);              //send with bt
    bReleased = false;               // send data for once until next time we click the button again
  }
if ((rec == 1) && (rectOver)&&(mousePressed) && (bReleased == true)) { 
    byte[] data = {'s'};           // send s to arduino when we click the button 1
    bt.broadcast(data);              //send with bt
    bReleased = false;
  }
  if ((rec == 3) && (rectOver)&&(mousePressed) && (bReleased == true)) {
    byte[] data = {'a'};           // send a to arduino when we click the button 3
    bt.broadcast(data);              //send with bt
    bReleased = false;
  }
  if ((rec == 4) && (rectOver)&&(mousePressed) && (bReleased == true)) { 
    byte[] data = {'d'};           // send d to arduino when we click the button 4
    bt.broadcast(data);              //send with bt
    bReleased = false;
  }
  if ((rec == 5) && (rectOver)&&(mousePressed) && (bReleased == true)) { 
    byte[] data = {'l'};           // send l to arduino when we click the button 5(ellipse)
    bt.broadcast(data);              //send with bt
    bReleased = false;
  }

  if ((rec == 6) && (rectOver)&&(mousePressed) && (bReleased == true)) { 
    byte[] data = {'k'};           // send k to arduino when we click the button 6(ellipse)
    bt.broadcast(data);              //send with bt
    bReleased = false;
  }
    if ((rec == 7) && (rectOver)&&(mousePressed) && (bReleased == true)) { 
    byte[] data = {'q'};           // send q to arduino when we click the button 7
    bt.broadcast(data);              //send with bt
    bReleased = false;
  }
    if ((rec == 8) && (rectOver)&&(mousePressed) && (bReleased == true)) { 
    byte[] data = {'e'};           // send e to arduino when we click the button 8
    bt.broadcast(data);              //send with bt
    bReleased = false;
  }
    if ((rec == 9) && (rectOver)&&(mousePressed) && (bReleased == true)) { 
    byte[] data = {'z'};           // send z to arduino when we click the button 9
    bt.broadcast(data);              //send with bt
    bReleased = false;
  }
    if ((rec == 10) && (rectOver)&&(mousePressed) && (bReleased == true)) { 
    byte[] data = {'c'};           // send c to arduino when we click the button 10
    bt.broadcast(data);              //send with bt
    bReleased = false;
  }


      if ((rec == 11) && (rectOver)&&(mousePressed) && (bReleased == true)) { 
    byte[] data = {'t'};                 // send t to arduino when we click the button 3
    bt.broadcast(data);              //send with bt
    bReleased = false;
  }
 
 
if((rectOver)&&(mousePressed == false)&& (bReleased == false))
  {                    //when our finger move up from the button, send stop command to arduino
    byte[] data = {' '};             
    bt.broadcast(data);
    bReleased = true;
  }

  fill(a);                                                //fill each area of button with the color declared above
  stroke(162); //the shape covered with a grey color line
  triangle(200,250,250,150,300,250);              //draw the triangle with the coordinates
   fill(b);
   triangle(200,600,250,700,300,600);
   fill(c);
   triangle(150,375,50,425,150,475);
    fill(d);
    triangle(350,375,450,425,350,475);
    fill(e);
    triangle(425,240,325,290,400,340);  //325
    fill(f);
    triangle(400,510,325,560,425,610);  //325
    fill(g);
    triangle(75,240,175,290,100,340); //75
    fill(h);
    triangle(100,510,175,560,75,610); //75
   fill(i);
   stroke(172);
rect(380,80,70,70);                  //draw 2 rectangle below the sphere(ellipse)
rect(380,700,70,70);
stroke(0);
fill(j);
ellipse(415,115,85,85);            //draw sphere with diameter 85
fill(k);
ellipse(415,735,85,85);
fill(l);
stroke(255);
ellipse(25,25,30,30);
}

//to print received data and show on screen
fill(255);
noStroke();
textAlign(LEFT);
text(info, 20, 104);
noFill();
}

void update(int x, int y) {       //to control the flag when we click a button
 if ( overRect(200, 150, 100, 100) ) {
    rectOver = true;                   //left right
    rec = 3;
  }
  else if
  ( overRect(50, 375, 100, 100) ) {
    rectOver = true;                   //up down
    rec = 1;
  }
  else if
  ( overRect(350, 375, 100, 100) ) {
    rectOver = true;
        rec = 2;                            //up down
  }
  else if
  ( overRect(200, 600, 100, 100) ) {
    rectOver = true;                   //left right
        rec = 4;
  }
    else if
  ( overRect(372,72,85,85) ) {
    rectOver = true;
        rec = 5;                            //for ellipse 1
  }
    else if
  ( overRect(372,692,85,85) ) {
    rectOver = true;
        rec = 6;                            //for ellipse 2
  }
      else if
  ( overRect(325, 240, 100, 100) ) {
    rectOver = true;
        rec = 7;                            //up left
  }
      else if
  ( overRect(325, 510, 100, 100) ) {
    rectOver = true;
        rec = 8;                            //up right
  }
      else if
  ( overRect(75, 240, 100, 100) ) {
    rectOver = true;
        rec = 9;                            //btm left
  }
      else if
  ( overRect(75, 510, 100, 100) ) {
    rectOver = true;
        rec = 10;                          //btm right
  }
        else if
  ( overRect(10, 10, 30, 30) ) {
    rectOver = true;
        rec = 11;                          //stop
  }
  else
  {
    rectOver = false;                 //nothing s touched on screen
  }
}

boolean overRect(int x, int y, int width, int height)  { // to scan we touch which area
  if (mouseX >= x && mouseX <= x+width &&
      mouseY >= y && mouseY <= y+height)             //to see if the mouse cursor inside rect
  {
    return true;
  } else {
    return false;
  }
}

void onKetaiListSelection(KetaiList klist)
{
String selection = klist.getSelection();            //select the device to connect
bt.connectToDeviceByName(selection);        //connect to the device
klist = null;                                                      //dispose of bluetooth list for now
}

//Call back method to manage data received
void onBluetoothDataEvent(String who, byte[] data)
{
if (isConfiguring)
return;
//received
info += new String(data);
if(info.length() > 150)             //clean the words on screen if string to long
info = "";
}//END of Android processing coding


Video will be uploaded soon :)

Friday, 5 April 2013

Use Android smartphone to control two wheels robot with Arduino via Bluetooth

2) Arduino Programming Part

For the Arduino programming part, I'm using is Arduino Leonardo. The difference of programming coding of Leonardo with Uno is the "serial" command. If i'm not mistaken, for Leonardo to communicate via Bluetooth the command will be "Serial1.xx" as below. Meanwhile for Uno will be just "Serial.xx".

Before going into Arduino programming part, you have to configure your own Bluetooth module. For example: set the baudrate, name of the Bluetooth device, and password to let your Android device connect to your Bluetooth shield. I'm doing all this setting by using a software called hyperterminal. You can try to configure your bluetooth module first because everyone are using different shield & module so I'm not gonna emphasize on it here, you can always try to google it :) (Bluetooth module configuration). Also, make sure your computer had installed the correct driver for your own Bluetooth Dongle.

Bluetooth Shield, Module and Dongle that I'm using

Below are the coding for my Arduino Leonardo: 


#include <Servo.h> //include servo function
#define BUFFERSIZE 127
uint8_t inBuffer[BUFFERSIZE];
int inLength; // length of data in the buffer
int numLoop = 0; // number of times we looped
char val;

Servo myservo;  //for servo function
int motor_LF=2; //declaring pin used for the motor
int motor_LR=3;
int motor_RF=4;
int motor_RR=5;

int motor_LPWM = 13; //declaring pwm pin
int motor_RPWM = 11;

void setup() {
  Serial1.begin(38400); //baud rate I set for bluetooth module is 38400
  myservo.attach(9); //my servo at pin 9 as actuator
  pinMode(motor_LF, OUTPUT); //set pin as output
  pinMode(motor_LR, OUTPUT);
  pinMode(motor_RF, OUTPUT);
  pinMode(motor_RR, OUTPUT);
  pinMode(motor_LPWM, OUTPUT);
  pinMode(motor_RPWM, OUTPUT);
 
  motor_reset(); // call function to reset the motor
}
void loop() {
  // read string if available
  if (Serial1.available()) {
    inLength =  0;
    while (Serial1.available()) {
      val = Serial1.read();
    }
   
    Serial1.print("Arduino received: "); //send the text to the android device
    Serial1.write(val);
    Serial1.println();
  }
 
   if (val == 'k') //K
    { myservo.write(90); } //write the angle of servo should move
   
    if (val == 'l') //L
    { myservo.write(30); }
   
      if (val == 'm') ///M
    { myservo.write(178); }
   
    if (val == 'w') //W – Forward motion of motor
    {
      digitalWrite(motor_LF, HIGH);
      digitalWrite(motor_LR, LOW);
      digitalWrite(motor_RF, HIGH);
      digitalWrite(motor_RR, LOW);
      analogWrite(motor_LPWM, 255); //sending pwm pulses
      analogWrite(motor_RPWM, 255); //255 will be highest
    }
   
    if (val == 's') //S – Reverse motion
    {
      digitalWrite(motor_LF, LOW);
      digitalWrite(motor_LR, HIGH );
      digitalWrite(motor_RF, LOW);
      digitalWrite(motor_RR, HIGH);
      analogWrite(motor_LPWM, 255);
      analogWrite(motor_RPWM, 255);
}

    if (val == 'q') //Q - Left Forward
    {
      digitalWrite(motor_LF, HIGH);
      digitalWrite(motor_LR, LOW);
      digitalWrite(motor_RF, HIGH);
      digitalWrite(motor_RR, LOW);
      analogWrite(motor_LPWM, 128); //slower thn Right PWM but same direction to turn left
      analogWrite(motor_RPWM, 255);
    }
   
    if (val == 'e') //E - Right Forward
    {
      digitalWrite(motor_LF, HIGH);
      digitalWrite(motor_LR, LOW);
      digitalWrite(motor_RF, HIGH);
      digitalWrite(motor_RR, LOW);
      analogWrite(motor_LPWM, 255);
      analogWrite(motor_RPWM, 128); //slower thn left PWM but same direction to turn right
    }
        if (val == 'z') //Z - Left backward
    {
      digitalWrite(motor_LF, LOW);
      digitalWrite(motor_LR, HIGH);
      digitalWrite(motor_RF, LOW);
      digitalWrite(motor_RR, HIGH);
      analogWrite(motor_LPWM, 128);
      analogWrite(motor_RPWM, 255);
    }
        if (val == 'c') //C - Right backward
    {
      digitalWrite(motor_LF, LOW);
      digitalWrite(motor_LR, HIGH);
      digitalWrite(motor_RF, LOW);
      digitalWrite(motor_RR, HIGH);
      analogWrite(motor_LPWM, 255);
      analogWrite(motor_RPWM, 128);
    }
   
    if (val == 'a') //A - Spin Left
    {
      digitalWrite(motor_LF, LOW);
      digitalWrite(motor_LR, HIGH);
      digitalWrite(motor_RF, HIGH);
      digitalWrite(motor_RR, LOW);
      analogWrite(motor_LPWM, 255); //move in same speed but different direction to spin
      analogWrite(motor_RPWM, 255);
    }
   
    if (val == 'd') //D - Spin Right
    {
      digitalWrite(motor_LF, HIGH);
      digitalWrite(motor_LR, LOW);
      digitalWrite(motor_RF, LOW);
      digitalWrite(motor_RR, HIGH);
      analogWrite(motor_LPWM, 255);
      analogWrite(motor_RPWM, 255);
    }

    if (val == ' ') //Space – Brake function
    {
      digitalWrite(motor_LF, LOW); //make all the pin LOW to stop the motor
      digitalWrite(motor_LR, LOW);
      digitalWrite(motor_RF, LOW);
      digitalWrite(motor_RR, LOW);
      analogWrite(motor_LPWM, 255); //both 255 to make the motor stop immediately
      analogWrite(motor_RPWM, 255);
    }
   
    if (val == 't') //T – Reset the motor
    {
      motor_reset();
    }
 
}

void motor_reset()  //reset function
{
  digitalWrite(motor_LF, LOW);
  digitalWrite(motor_LR, LOW);
  digitalWrite(motor_RF, LOW);
  digitalWrite(motor_RR, LOW);
  analogWrite(motor_LPWM, 0);
  analogWrite(motor_RPWM, 0);
}//END of arduino code


Parts of the coding credits to
Nadrew Chong Jia Ying