RC Car controlled Wirelesly

Hey Guys,

I'm pretty news to the whole Arduino scene but I find it really interesting. I just need some pointers on how to accomplish my projects. So before I ask my questions, let me just say sorry if they are "noob" questions and if I seem vague in my explanations. Don't hesitate to ask me to clarify if you guys don't understand.

So here goes. My project looks like this, I bought a RC Car and stripped everything in there and would like to be able to control the car using the Yun Wifi.

1- What is the best way to control the car, through the Web Server ? I don't know if it's the best way, it looks laggy.

2- If the web server is the best how do I create a web interface and send the commands to the yun?

Thank you

PS: If I posted in the wrong thread sorry about it. Just move it.

web server is indeed laggy at the moment (it's not its fault actually, in another thread there is a more complete explanation) I'd go with yunserver and yunclient instead, that makes you talk more directly to the sketch

Thank you for the help, and by yunserver yunclient you mean SSH and then Telnet ???

At your choice. If you call server.listenOnLocalhost() then yes If you call server.noListenOnLocalhost() it will start listening on port 5555 (or a port of your choice) on yun's public IPs

I'm really sorry to come back with this but I still don't get how to talk to the arduino, without latency. I've been reading alot lately on that and what I can see is people seem to create a WebServer and talk to the arduino this way. Is that the best way to do it ? If not how do I create a UI with a foward and backward button and maybe 2 other button to speed up the car and talk directly on the Arduino?

Right now I'm able to connect SSH and Telnet to the board and send commands like F to make it go foward and it works really good.

If any of you don't feel like explaining a link to a complete tut would be good enough for me.

Thank you again and sorry to be bothering you guys.

Just to be sure: are you saying you set up a YunServer with noListenOnLocalhost(), tried to connect to the yun and it failed?
I’ve just coded this

#include <YunServer.h>
#include <Bridge.h>
#include <YunClient.h>

YunServer server(5678);

void setup() {
  Serial.begin(9600);

  // Bridge startup
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  Bridge.begin();
  digitalWrite(13, HIGH);

  server.noListenOnLocalhost();
  server.begin();
}

void loop() {
  YunClient client = server.accept();

  // There is a new client?
  if (client) {
    String command = "none";
    while (command.indexOf("stop") != 0) {
      if (client.available() > 0) {
        command = client.readString();
        client.print("Received: ");
        client.print(command);
      }
    }
    client.stop();
  }
}

I can then open a terminal and write

federico@smilzo:~$ telnet 192.168.240.1 5678
Trying 192.168.240.1...
Connected to 192.168.240.1.
Escape character is '^]'.
Hello world!
Received: Hello world!
Say hi!   
Received: Say hi!
stop
Received: stop
Connection closed by foreign host.

I might have explain myself wrong.

Right now I can control my car using this code.

#include <Console.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 
// Or, create it with a different I2C address (say for stacking)
// Adafruit_MotorShield AFMS = Adafruit_MotorShield(0x61); 

// Select which 'port' M1, M2, M3 or M4. In this case, M1
Adafruit_DCMotor *myMotor = AFMS.getMotor(1);
// You can also make another motor on port M2
Adafruit_DCMotor *myOtherMotor = AFMS.getMotor(2);

const int ledPin = 13; // the pin that the LED is attached to
int incomingByte;      // a variable to read incoming serial data into

void setup() {
  Serial.begin(9600);           // set up Serial library at 9600 bps
  Serial.println("Adafruit Motorshield v2 - DC Motor test!");

  AFMS.begin();  // create with the default frequency 1.6KHz
  //AFMS.begin(1000);  // OR with a different frequency, say 1KHz

  // Set the speed to start, from 0 (off) to 255 (max speed)
  myMotor->setSpeed(150);
  myMotor->run(FORWARD);
  // turn on motor
  myMotor->run(RELEASE);

  //OTHER MOTOR
  myOtherMotor->setSpeed(150);
  myOtherMotor->run(FORWARD);
  // turn on motor
  myOtherMotor->run(RELEASE);

  // initialize serial communication:
  Bridge.begin();
  Console.begin();

  while (!Console){
    ; // wait for Console port to connect.
  }
  Console.println("You're connected to the Console!!!!");
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  uint8_t i;

  // see if there's incoming serial data:
  if (Console.available() > 0) {
    // read the oldest byte in the serial buffer:
    incomingByte = Console.read();
    // if it's a capital H (ASCII 72), turn on the LED:
    if (incomingByte == 'F' || incomingByte == 'f') {
      myMotor->run(FOWARD);
      myMotor->setSpeed(100); 
    }
    if (incomingByte == 'L' || incomingByte == 'l') {
      myOtherMotor->run(FORWARD);
      myOtherMotor->setSpeed(100);
      delay(1000);
      myOtherMotor->setSpeed(130);
      delay(1000);
      myOtherMotor->setSpeed(140);
      delay(2000);
      myOtherMotor->setSpeed(160);
      delay(2000);   
    }
    if (incomingByte == 'R' || incomingByte == 'r') {
      myOtherMotor->run(BACKWARD);
      myOtherMotor->setSpeed(225);
    }
    if (incomingByte == 'C' || incomingByte == 'c') {
      myOtherMotor->run(RELEASE);
    }
    // if it's an L (ASCII 76) turn off the LED:
    if (incomingByte == 'B' || incomingByte == 'b') {
      myMotor->run(BACKWARD);
      for (i=0; i<255; i++) {
        myMotor->setSpeed(i);  
        delay(10);
      }
    }
    // 
    if (incomingByte == 'S' || incomingByte == 's') {
      myMotor->run(RELEASE);
      myOtherMotor->run(RELEASE);
    }
  }    
}

So on my Android tablet I have a ssh client I connect using ssh root@192.168.240.1, then I telnet localhost 6571

I send F and my car goes FOWARD. So that part works.

Now what I would like is to create an interface, instead of writing F in a SSH client, that has buttons to make the car go foward.

Hope I’m a little bit clearer now.

If you expose your sketch with a "public" YunServer as in my previous example, you can open a socket from your android to your yun and have your sketch wait for a YunClient connection instead than using Console. Sockets are quite easy to manage and IMHO it's the most reactive way for controlling a car.

Thank you for your help Frederico, I'm not finished and don't understand everything just yet but I think you just send me on the right track.

Thank you again.

Keep us posted: remote controlling is tough, if you report us your progress we will all benefit

Sure thing Frederico I’ll be more then happy to make ppl benefit by my project.

One question about the code. And again might be me the problem here.

#include <YunServer.h>
#include <Bridge.h>
#include <YunClient.h>

YunServer server(5678);

void setup() {
  Serial.begin(9600);

  // Bridge startup
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  Bridge.begin();
  digitalWrite(13, HIGH);

  server.noListenOnLocalhost();
  server.begin();
}

void loop() {
  YunClient client = server.accept();

  // There is a new client?
  if (client) {
    String command = "none";
    while (command.indexOf("stop") != 0) {
      if (client.available() > 0) {
        command = client.readString();
        Serial.println(command);
        if (command == "OFF") {
          Serial.println("OFF");
          digitalWrite(13, LOW);
        }
        if (command == "ON") {
          digitalWrite(13, HIGH);
        }
        client.print("Received: ");
        client.print(command);
      }
    }
    client.stop();
  }
}

Why is it that I never go in the if (command == “OFF”) or “ON” statement?
If I try comand.equals(“OFF”) or even compareTo doesn’t work

UPDATE: Weird thing if I do if (command.indexOf (“OFF”) == 0) {

that works.

Because command contains a newline (or carriage return + newline, it depends on the OS)

Yeah you are right. Adding command.trim(); worked like a charm.

Sorry for that question!! :)

Hey Frederico,

Ok this is how far I got, right now I'm listening on Socket 5678 like you showed me. I created a little c# project that send stuff over the socket and that works fine, the arduino get's all my commands. But their is a 2 seconds delay between when I press the up arrow and when the car starts turning. So what I would like to know is what could do te delay ? Should I try coding all in c++ ?

Thank you again Frederico

I don't think the problem is in your c# code (but I'll stress test it anyway, just in case) It's probably on the arduino side even if 2 seconds for such a thing is really a lot. Do you have source code to share?

Sure thing. Here’s my Arduino code

#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"
#include <Bridge.h>
#include <Console.h>
#include <Wire.h>
#include <YunServer.h>
#include <YunClient.h>

Adafruit_MotorShield AFMS = Adafruit_MotorShield();
// Select which 'port' M1, M2, M3 or M4. In this case, M1
Adafruit_DCMotor *frontWheels = AFMS.getMotor(2);
// You can also make another motor on port M2
Adafruit_DCMotor *rearWheels = AFMS.getMotor(1);

const int ledPin = 13; // the pin that the LED is attached to
int incomingByte;      // a variable to read incoming serial data into

// We open the socket 5678 to communicate.
YunServer server(5678);

void setup() {
  Serial.begin(9600);

  // Bridge startup
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  Bridge.begin();
  digitalWrite(ledPin, HIGH);

  AFMS.begin();  // create with the default frequency 1.6KHz

  // Set the speed to start, from 0 (off) to 255 (max speed)
  frontWheels->setSpeed(0);
  frontWheels->run(FORWARD);
  // turn on motor
  frontWheels->run(RELEASE);

  //OTHER MOTOR
  rearWheels->setSpeed(0);
  rearWheels->run(FORWARD);
  // turn on motor
  rearWheels->run(RELEASE);

  server.noListenOnLocalhost();
  server.begin();
}

void loop() {
  uint8_t i;
  YunClient client = server.accept();

  // There is a new client?
  if (client) {
    String command = "none";
    digitalWrite(ledPin, LOW);

    while (!command.equals("stop")) {
      if (client.available() > 0) {
        command = client.readString();
        command.trim();

        Serial.println(command);

        if ( command.equals("back") ) {
          rearWheels->run(RELEASE);
          rearWheels->setSpeed(255);
          rearWheels->run(BACKWARD);
        }
        else if (command.equals("forward") ) {
          rearWheels->run(RELEASE);
          rearWheels->setSpeed(255);
          rearWheels->run(FORWARD);
        }
        else if ( command.equals("brake") ) {
          rearWheels->run(RELEASE);
          frontWheels->run(RELEASE);
        }
        else if ( command.equals("left") ) {
          frontWheels->run(RELEASE);
          frontWheels->setSpeed(150);
          frontWheels->run(FORWARD);
        }
        else if ( command.equals("right") ) {
          frontWheels->run(RELEASE);
          frontWheels->setSpeed(150);
          frontWheels->run(BACKWARD);
        }
      }
    }
    digitalWrite(ledPin, HIGH);
    client.stop();
  }
}

And my C# code just incase

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace RCController
{
    public partial class Form1 : Form
    {
        byte[] msg;
        byte[] bytes = new byte[256];
        Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Down)
            {
                msg = Encoding.UTF8.GetBytes("back");

                try
                {
                    // Blocks until send returns. 
                    int i = s.Send(msg);
                }
                catch (SocketException z)
                {
                    Console.WriteLine(z.Message);
                }
            }
            else if (e.KeyCode == Keys.Up)
            {
                msg = Encoding.UTF8.GetBytes("forward");

                try
                {
                    // Blocks until send returns. 
                    int i = s.Send(msg);
                }
                catch (SocketException z)
                {
                    Console.WriteLine(z.Message);
                }
            }
            else if (e.KeyCode == Keys.Left)
            {
                msg = Encoding.UTF8.GetBytes("left");

                try
                {
                    // Blocks until send returns. 
                    int i = s.Send(msg);
                }
                catch (SocketException z)
                {
                    Console.WriteLine(z.Message);
                }
            }
            else if (e.KeyCode == Keys.Right)
            {
                msg = Encoding.UTF8.GetBytes("right");

                try
                {
                    // Blocks until send returns. 
                    int i = s.Send(msg);
                }
                catch (SocketException z)
                {
                    Console.WriteLine(z.Message);
                }
            }
            else if (e.KeyCode == Keys.ShiftKey)
            {
                msg = Encoding.UTF8.GetBytes("brake");

                try
                {
                    // Blocks until send returns. 
                    int i = s.Send(msg);
                }
                catch (SocketException z)
                {
                    Console.WriteLine(z.Message);
                }
            }
        }

        private void connexionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            IPAddress[] IPs = Dns.GetHostAddresses("192.168.240.1");

            s.Connect(IPs[0], 5678);

            msg = Encoding.UTF8.GetBytes("This is a test");
            try
            {
                // Blocks until send returns. 
                int i = s.Send(msg);
            }
            catch (SocketException z)
            {
                Console.WriteLine(z.Message);
            }

        }

        private void deconnexionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            msg = Encoding.UTF8.GetBytes("stop");
            try
            {
                // Blocks until send returns. 
                int i = s.Send(msg);
            }
            catch (SocketException z)
            {
               Console.WriteLine(z.Message);
            }
            s.Disconnect(true);
        }
    }
}

Thank you for the help

You see anything wrong with the code Frederico?

I see that you put some debugging prints, may you post the output?

(both C# and Arduino)

My problem isn't with the output of my command cause that works perfectly, my problem is with the 2 seconds delay between my soft and the arduino giving the command. I tried different approach in my arduino code like, changing from a string to convert to a Int and switching my if to a switch.. case and the effect is still there. I even tried changing my string to a char and the 2 seconds delay is always there.

PS: Something else that I tried is using telnet as my command giver instead of my c# program and the delay remains.

FYI : adding a delay at the end, delay(500); and even changing the Serial speed to 115200 didn't change a thing.

I'm running out of ideas, if anyone has something else to try let me know.

Thanks