Controlling a quadrocopter with the Arduino Ethernet Shield

Hey guys,

I'm trying to build a quadrocopter and control it with my Computer over wifi.

My setup:

  • Arduino with Ethernet Shield mounted on the quadrocopter
  • Ethernet Shield connected via LAN to a wifi router also mounted on the quadrocopter
  • Computer connected to quadrocopters wifi
  • Xbox Controller connected to computer

My problem is to get a save (bidirectional) connection to the quadrocopter, so I can fly with it without worrying about loosing the connection.
My goal is to send commands like speed, direction etc. to the Arduino and to get back a feedback like pitch, roll etc.

My Arduino server code:

void loop()
{
 EthernetClient client = server.available(); //if server got a client, create a client to handle it
  if (client && client. connected) {  //check if client is ready and connected
      if (client.available()) {  //check for incoming data
        data = client.readStringUntil('|'); //the data has alway this format: XXX;XXX;XXX;XXX;XXX;|
        data.trim();
        client.flush();

        for(int i = 0; i < 5; i++){  //split the string and convert it to integers
          lastcomma = comma;
          comma = data.indexOf(';', comma + 1);
          if(comma < 1){
            comma = data.length();
          }
          substrg[i] = data.substring(lastcomma, comma);
          substrg[i].toCharArray(buffer, 32);
          int values[i] = atoi(buffer);
          Serial.print(values[i]); 
          Serial.print("\t");
          comma++;
          if( i == dataToRecieve-1){ 
            comma = 0; 
            lastcomma = 0;
          }
        }
        
        P = values[0]; P = P/100; //get understandable names for the variables
        I = values[1]; I = I/100;
        D = values[2]; D = D/100;     
        ARM = values[3];
        SPEED = values[4] + 20;
        
        FRONT.SetTunings(P, I, D); //set new PID tunings
        BACK.SetTunings(P, I, D);

        int kalXint = kalAngleX;
        int kalYint = kalAngleY;
        int xOutint = 0;

        client.print(kalXint);  //send a message back to the computer
        client.print(";");        // format is XXX;XXX;XXX;|
        client.print(kalYint);
        client.print(";");
        client.print(xOutint);
        client.print(";");
        client.print("|");
      }
    }
  }
}

I'm running an processing script on the computer, which is doing pretty much the same, except it runs as client.

Anyone an idea how to solve this problem?

Thanks...

My problem is to get a save (bidirectional) connection to the quadrocopter, so I can fly with it without worrying about loosing the connection.

I suspect that the issue is that you don't understand the nature of client/server interaction.

There is not a continuous connection, using TCP/IP. The connection exists only long enough to transfer ONE GET request/one response.

You could get a continuous connection, using UDP.

But the TCP/IP protocoll checks for droped packets and the UDP doesn't. So I thougth it is better to use TCP/IP...

Maybe I can give the code a "command structure". Instead of sending all the values every loop, it would send the values only when it's needed.
This structure would look like this: command:value| (f.ex.: command = kalmanX, value = 180°)
Does this make sense?

Does this make sense?

More than trying to force the client/server architecture to do something it is not intended to do.

But the TCP/IP protocoll checks for droped packets and the UDP doesn't. So I thougth it is better to use TCP/IP...

There are, of course, tradeoffs. What can you do about a dropped packet in the time you have to deal with a packet? Not much. If the packets arrive often enough, a dropped packet isn't the end of the world.

Then I'll try to use an UDP connection...

PaulS:
There is not a continuous connection, using TCP/IP. The connection exists only long enough to transfer ONE GET request/one response.

Speaking about networking in general, TCP/IP will let you go back and forth as many times as you like. It sounds like you are describing HTTP, which is get/response oriented, but you can use a different protocol like telnet or even make up your own TCP/IP protocol and do as much back and forth as you want within a single persistent TCP socket.

That said, I don't know if the Ethernet Arduino shield or library will let you use non-HTTP TCP protocols?

That said, I don't know if the Ethernet Arduino shield or library will let you use non-HTTP TCP protocols?

Yes, it will. I've maintained a TCP connection with a PC for 8 hours at 15 packets/second.

I think in the past the below http code was doing ~15 transactions per second when the fast data was selected. The page has a counter so the updates can be timed. YMMV

// zoomkat's meta refresh data frame test page 5/25/13
// use http://192.168.1.102:84 in your brouser for main page
// http://192.168.1.102:84/data static data page
// http://192.168.1.102:84/datastart meta refresh data page
// for use with W5100 based ethernet shields
// set the refresh rate to 0 for fastest update
// use STOP for single data updates

#include <SPI.h>
#include <Ethernet.h>

const int analogInPin0 = A0;
const int analogInPin1 = A1;
const int analogInPin2 = A2;
const int analogInPin3 = A3;
const int analogInPin4 = A4;
const int analogInPin5 = A5;

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 102 }; // arduino ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port
unsigned long int x=0; //set refresh counter to 0
String readString; 

//////////////////////

void setup(){
  Serial.begin(9600);
    // disable SD SPI if memory card in the uSD slot
  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);

  Ethernet.begin(mac, ip, gateway, gateway, subnet);
  server.begin();
  Serial.println("meta refresh data frame test 5/25/13"); // so I can keep track of what is loaded
}

void loop(){
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
         if (readString.length() < 100) {
          readString += c; 
         } 
        //check if HTTP request has ended
        if (c == '\n') {

          //check get atring received
          Serial.println(readString);

          //output HTML data header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();

          //generate data page
          if(readString.indexOf("data") >0) {  //checks for "data" page
            x=x+1; //page upload counter
            client.print("<HTML><HEAD>");
            //meta-refresh page every 1 seconds if "datastart" page
            if(readString.indexOf("datastart") >0) client.print("<meta http-equiv='refresh' content='1'>"); 
            //meta-refresh 0 for fast data
            if(readString.indexOf("datafast") >0) client.print("<meta http-equiv='refresh' content='0'>"); 
            client.print("<title>Zoomkat's meta-refresh test</title></head><BODY>
");
            client.print("page refresh number: ");
            client.print(x); //current refresh count
            client.print("

");
            
              //output the value of each analog input pin
            client.print("analog input0 is: ");
            client.print(analogRead(analogInPin0));
            
            client.print("
analog input1 is: ");
            client.print(analogRead(analogInPin1));
                        
            client.print("
analog input2 is: ");
            client.print(analogRead(analogInPin2));
            
            client.print("
analog input3 is: ");
            client.print(analogRead(analogInPin3));
                                    
            client.print("
analog input4 is: ");
            client.print(analogRead(analogInPin4));
            
            client.print("
analog input5 is: ");
            client.print(analogRead(analogInPin5));
            client.println("
</BODY></HTML>");
           }
          //generate main page with iframe
          else
          {
            client.print("<HTML><HEAD><TITLE>Zoomkat's frame refresh test</TITLE></HEAD>");
            client.print("Zoomkat's Arduino frame meta refresh test 5/25/13");
            client.print("

Arduino analog input data frame:
");
            client.print("&nbsp;&nbsp;<a href='http://192.168.1.102:84/datastart' target='DataBox' title=''yy''>META-REFRESH</a>");
            client.print("&nbsp;&nbsp;&nbsp;&nbsp;<a href='http://192.168.1.102:84/data' target='DataBox' title=''xx''>SINGLE-STOP</a>");
            client.print("&nbsp;&nbsp;&nbsp;&nbsp;<a href='http://192.168.1.102:84/datafast' target='DataBox' title=''zz''>FAST-DATA</a>
");
            client.print("<iframe src='http://192.168.1.102:84/data' width='350' height='250' name='DataBox'>");
            client.print("</iframe>
</HTML>");
          }
          delay(1);
          //stopping client
          client.stop();
          //clearing string for next read
          readString="";
        }
      }
    }
  }
}

I'm trying to build a quadrocopter and control it with my Computer over wifi.
...
My goal is to send commands like speed, direction etc. to the Arduino and to get back a feedback like pitch, roll etc.

Personally, I'd say don't even try. I doubt wifi will have quick enough responsiveness for adequate control of a flyer. Unless maybe the flyer has an extreme amount of autonomous smarts. Also, with wifi, range will be very limited. You'd probably do better with longer-range XBee modules or maybe RFM22 radios. I'm sure their responsiveness will also be much better. 100-200mW transmit power.

As far as it getting out of range, I've thought about this with my [ground] robots. A flyer is easy since, if it loses contact, you can have it go into an increasing-spiral circling pattern. With my robots, I thought to have the robot remember its past few directional commands from the base station, and if signal is lost, then backtrack the commands in reverse.

I think it is fast enough. I used a joystick connected to the PC, and it was very fast on the Arduino end at 15 updates/second.

Unlike zoomkat's code, mine used client.connect(), transferred packets bidirectionally for 8 hours, then client.stop().

But with the wireless router, the Arduino, and assorted control stuff onboard, weight may be an issue.

SurferTim:
But with the wireless router, the Arduino, and assorted control stuff onboard, weight may be an issue.

I wondered about this. Why not use a wifi shield instead of Ethernet+router?

tylernt:

SurferTim:
But with the wireless router, the Arduino, and assorted control stuff onboard, weight may be an issue.

I wondered about this. Why not use a wifi shield instead of Ethernet+router?

Transmit power. My router has a 500mw radio onboard, and provides power for the Arduino over a usb port, and allows a telnet-like connection to the Arduino usb port.

edit: Transmitting with that power setting is illegal in many countries. Many places have a 100mw transmit limit.