Voiture télécommandé par Internet

Ayant retrouvé ma chère et tendre voiture téléguidée fièrement acquis 7 ans plus tôt, il ne faisait aucun doute qu’elle allait subir quelques modifications sous peu. C’est maintenant chose faite, elle est devenue pilotable de n’importe quel endroit du monde. Tout ça mis en oeuvre très facilement grâce à un téléphone sous Android !

En effet, plutôt qu’une carte embarquée Linux (Ou autre) onéreuse, j’ai décidé d’utiliser un téléphone portable sous Android. Les avantages :

  • Il dispose d’un GPS, des accéléromètres, d’une caméra, d’une puce 3g, du wifi, du bluetooth (Tout dépend du modèle, mais ceux-là sont relativement commun)
  • Développement simple sous Android avec Java
  • Son prix ! Si vous ne prenez pas le tout dernier modèle bien entendu, on peut facilement trouver des occasions sur eBay, et c’est encore moins cher si l’écran est fissuré
  • Facilité de communication avec un module Arduino

Le but de ce projet était que n’importe qui, à partir d’une page web, puisse contrôler la voiture. Pour se faire, petit schéma :

Les servomoteurs de la voiture sont contrôlé par un module Arduino, qui est connecté au téléphone Android se chargeant de la réception des commandes en socket. Il existe plusieurs techniques pour faire communiquer un arduino et un système Android, l’une un peu coûteuse (Et de très bonne qualité, préférez cette voie là) grâce à l’usb Host, le bluetooth/wifi(Coûteux, le sans fil est dommage quand les deux dispositifs sont à 5 cm) et l’autre qui ne vous coûtera rien en contrepartie d’un ping un peu élevé, j’ai nommé ‘ l’audio ‘ (Et c’est la méthode que j’utilise). En gros, les deux voies (Oui, en stereo) audio du téléphone sont connectés à deux entrées analogique de l’arduino. Il envoie successivement des salves à droite, puis à gauche. Le temps entre chaque salve de gauche à droite détermine une valeur pour la direction, et entre chaque salve de droite à gauche est déterminé la valeur de vitesse.

Théoriquement, le téléphone se connecte ensuite au serveur (Car on ne connait pas son adresse IP, c’est donc le téléphone qui doit se connecter au serveur) et le client se connecte sur celui-ci via une interface web.

Dans mon cas, j’ai fait le procédé inverse (Par facilité), le téléphone portable est connecté en wifi sur le serveur, et le client y accède via une redirection de port.

Source de l’interface java du client :

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.applet.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
 
public class CarServer extends Applet implements KeyListener,ActionListener {
	/**
	 *
	 */
	private static final long serialVersionUID = 1L;
	Font bigFont;
	Color redColor;
	Color greenColor;
	Color bgColor;
 
	int bas = 0;
	int haut = 0;
	int gauche = 0;
	int droite = 0;
	int vitesse = 3;
	int volant = 3;
	int fvitesse = 0;
	int fvolant = 0;
 
	int connecte = 0;
	Button btnCo;
	Button btnLo;
	TextField nameField;
 
	String IP = "";
 
     public void init(){
    	 bigFont = new Font("Arial",Font.BOLD,10);
    	 redColor = Color.red;
    	 greenColor = Color.green;
    	 setBackground(Color.white);
    	 setLayout(new FlowLayout());
    	 nameField = new TextField("IP DU SERVEUR",20); // Renseignez l'IP du serveur
    	 btnCo = new Button("LET'S TO THIS");
    	 btnLo = new Button("Local");
    	 add(nameField);
    	 add(btnLo);
    	 add(btnCo);
    	 btnCo.addActionListener(this);
    	 btnLo.addActionListener(this);
    	 nameField.addActionListener(this);
    	 nameField.addKeyListener(this);
     }
 
     public void stop(){ 
 
     }
 
     public void Gupdate(){
    	 repaint();
     }
 
     //Merveilleuse interface graphique
 
     public void paint(Graphics g){
    	 g.setFont(bigFont);
    	 if(connecte == 0){
    		 g.drawString("En attente de connexion...",20,250);
    	 }else if(connecte == 1){
    		 g.drawString("Connecté, c'est cool  ",20,250);
    	 }if(connecte == 2){
    		 g.drawString("Oh non, perte de la voiture... Peut-être un accident",20,250);
    	 }
 
    	 g.drawString("Volant : "+Integer.toString(volant),55,80);
    	 g.drawString("Vitesse : "+Integer.toString(vitesse),55,90);
    	 g.fillRect(55, 115, 60, 110);
    	 if(volant > 3){
    		 g.setColor(redColor);
    	 }else if(volant < 3){
    		 g.setColor(greenColor);
    	 }else{
    		 g.setColor(Color.black);
    	 }
    	 g.fillRect(40, 110, 10, 20);
    	 g.fillRect(120, 110, 10, 20);
 
    	 if(vitesse > 3){
    		 g.setColor(redColor);
    	 }else if(vitesse < 3){
    		 g.setColor(greenColor);
    	 }else{
    		 g.setColor(Color.black);
    	 }
    	 g.fillRect(40, 210, 10, 20);
    	 g.fillRect(120, 210, 10, 20);
     }
 
     //La classe du socket !
 
     public class ServerCar extends Thread {
     	public void run() {
 
     		Socket socket = null;
     		DataOutputStream dataOutputStream = null;
     		//DataInputStream dataInputStream = null;
     		try {
     			socket = new Socket(IP, 8887); // On se connecte
     			//dataInputStream = new DataInputStream(socket.getInputStream()); // Si jamais On veut recevoir des données
     			dataOutputStream = new DataOutputStream(socket.getOutputStream());
 
     			long startTime = System.currentTimeMillis();
         		long estimatedTime = System.currentTimeMillis() - startTime;
     			while(true){
     				connecte = 1;
     				estimatedTime = System.currentTimeMillis() - startTime;
         			if(estimatedTime > 50){
         				startTime = System.currentTimeMillis();
         				Gupdate();
         				if(haut == 1){
         					if(vitesse == 5){
 
         					}else if(vitesse < 5){
         						vitesse++;
         						}else if(vitesse > 3){
         							vitesse--;
         						}
         				}else if(bas == 1){
         					if(vitesse > 1){
         						vitesse--;
         					}
         				}else{
         					if(vitesse > 3){
         						vitesse--;
         					}else if(vitesse < 3){
         						vitesse++;
         					}
         				}
 
         				if(gauche == 1){
         					if(volant == 5){
 
         					}else if(volant < 5){
         						volant++;
         					}else if(volant > 3){
         						volant--;
         					}
         				}else if(droite == 1){
         					if(volant > 1){
         						volant--;
         					}
         				}else{
         					if(volant > 3){
         						volant--;
         					}else if(volant < 3){
         						volant++;
         					}
         				}
 
         				if(vitesse == 5){
         					fvitesse = 100;
         				}else if(vitesse == 4){
         					fvitesse = 140;
         				}else if(vitesse == 3){
         					fvitesse = 200;
         				}else if(vitesse == 2){
         					fvitesse = 250;
         				}else if(vitesse == 1){
         					fvitesse = 300;
         				}
 
         				if(volant == 1){
         					fvolant = 100;
         				}else if(volant == 2){
         					fvolant = 140;
         				}else if(volant == 3){
         					fvolant = 200;
         				}else if(volant == 4){
         					fvolant = 250;
         				}else if(volant == 5){
         					fvolant = 300;
         				}
         				dataOutputStream = new DataOutputStream(socket.getOutputStream());
         				dataOutputStream.writeUTF(fvitesse+"/"+fvolant); //On envoit tout ça à la voiture
         				System.out.println(fvitesse+"/"+fvolant);
     			}
 
     			//
     			}
     		} catch (UnknownHostException e) {
     			// TODO Auto-generated catch bloc
     			e.printStackTrace();
     		} catch (IOException e) {
     			// TODO Auto-generated catch block
     			e.printStackTrace();
     			connecte = 2;
     		}
     	}
     }
 
     public void keyPressed(KeyEvent e) {
         if(e.getKeyCode() == 38){
         	haut = 1;
         	System.out.println("Haut !");
         }else if(e.getKeyCode() == 40){
         	bas = 1;
         }else if(e.getKeyCode() == 37){
         	gauche = 1;
         }else if(e.getKeyCode() == 39){
         	droite = 1;
         }
     }
 
     public void keyReleased(KeyEvent e) {
     	if(e.getKeyCode() == 38){
         	haut = 0;
         }else if(e.getKeyCode() == 40){
         	bas = 0;
         }else if(e.getKeyCode() == 37){
         	gauche = 0;
         }else if(e.getKeyCode() == 39){
         	droite = 0;
         }
     }
 
	@Override
	public void actionPerformed(ActionEvent evt) {
		// TODO Auto-generated method stub
		if (evt.getSource() == btnCo){
			new ServerCar().start();
		}else if (evt.getSource() == btnLo){
			nameField.setText("192.168.1.49");
		}
		IP = nameField.getText();
	}
 
	@Override
	public void keyTyped(KeyEvent arg0) {
		// TODO Auto-generated method stub
	}
}

A la reception, code source du côté Android :
package com.artandgj.carandroid;
 
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
 
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
 
public class CarAndroidActivity extends Activity {
 
	private static final String TAG = "ThreadMessaging";
	public Handler mMainHandler, mChildHandler;
	public long servo_dir = 200;
    	public long servo_v = 100;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button btn_start = (Button)findViewById(R.id.start);
    	Button btn_stop = (Button)findViewById(R.id.stop);
        final Thread soundthread = new Thread(new SoundThread());
 
        btn_start.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){
            	soundthread.start();
            	new ChildThread().start();
            }
        });
        btn_stop.setOnClickListener(new View.OnClickListener(){
           	public void onClick(View v){
           		mChildHandler.getLooper().quit();
           		soundthread.destroy();
           	}
        });
    }
 
    class ChildThread extends Thread {
        public void run() {
 
        	ServerSocket serverSocket = null;
            Socket socket = null;
            DataInputStream dataInputStream = null;
            DataOutputStream dataOutputStream = null;
            String temp = null;
 
            try {
             serverSocket = new ServerSocket(8887);
             System.out.println("Listening :8887");
            } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
            }
 
            while(true){
            	try {
            		socket = serverSocket.accept();
            		dataInputStream = new DataInputStream(socket.getInputStream());
            		//dataOutputStream = new DataOutputStream(socket.getOutputStream());
            		System.out.println("ip: " + socket.getInetAddress());
            		// System.out.println("message: " + dataInputStream.readUTF());
            		while(true){
            			dataInputStream = new DataInputStream(socket.getInputStream());
        				temp = dataInputStream.readUTF().toString();
        				String str[]= temp.split("/");
        				servo_dir = new Long(str[0]);
        				servo_v = new Long(str[1]);
        				Log.i(TAG, servo_v + " - " + servo_dir);
            		}
            		//dataOutputStream.writeUTF("Hello!");
            	} catch (IOException e) {
            		// TODO Auto-generated catch block
            		e.printStackTrace();
            	}
            	finally{
            		if( socket!= null){
            			try {
            				socket.close();
            				System.out.println("Fermeture");
            			} catch (IOException e) {
            				// TODO Auto-generated catch block
            				e.printStackTrace();
            			}
            		}
            		if( dataInputStream!= null){
            			try {
            				dataInputStream.close();
            			} catch (IOException e) {
            				// TODO Auto-generated catch block
            				e.printStackTrace();
            			}
            		}
 
            		if( dataOutputStream!= null){
            			try {
            				dataOutputStream.close();
            			} catch (IOException e) {
            				// TODO Auto-generated catch block
            				e.printStackTrace();
            			}
            		}
            	}
            }
        }
    }
 
    public class SoundThread implements Runnable {
 
    //Communication avec l'Arduino par la sortie sonore
 
    	AndroidAudioDevice device = new AndroidAudioDevice();
 
        @Override
        public void run() {
        	final float frequency = 20;
            float increment = (float)(2*Math.PI) * frequency / 44100; // angular increment for each sample
            float angle = 0;
            float samples[] = new float[1024];
 
            int tour = 0;
            //int fois = 100;
            boolean continu = true;
 
            angle = 0;
    		for( int i = 0; i < samples.length; i++ ){
                samples[i] = (float)Math.sin( angle );
                angle += increment;
            }
 
    		long startTime = System.currentTimeMillis();
    		long estimatedTime = System.currentTimeMillis() - startTime;
 
    		Looper.prepare();
    		mChildHandler = new Handler() {
                public void handleMessage(Message msg) {
                	servo_dir = Long.parseLong((String) msg.obj);
                }
            };
 
            while(continu){
            	if(tour == 0){
            		estimatedTime = System.currentTimeMillis() - startTime;
            		if(estimatedTime > servo_dir){
	            		device.SetStereo(1);
	                    device.writeSamples(samples);
	                    startTime = System.currentTimeMillis();
	                    tour = 1;
	                    Log.i(TAG, "Tac");
            		}
            	}else{
            		estimatedTime = System.currentTimeMillis() - startTime;
            		if(estimatedTime > servo_v){
	            		device.SetStereo(0);
	                    device.writeSamples(samples);
	                    startTime = System.currentTimeMillis();
	                    tour = 0;
            		}
            	}
            }
            Looper.loop();
        }
    }
 
}

Classe Android pour le son :

package com.artandgj.carandroid;
 
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
 
public class AndroidAudioDevice{
   AudioTrack track;
   short[] buffer = new short[1024];
 
   public AndroidAudioDevice(){
      int minSize =AudioTrack.getMinBufferSize( 44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT);
      track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100,
                                        AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT,
                                        minSize, AudioTrack.MODE_STREAM);
      track.play();
   }	   
 
   public void writeSamples(float[] samples)
   {
      fillBuffer( samples );
      track.write( buffer, 0, samples.length );
   }
 
   private void fillBuffer( float[] samples )
   {
      if( buffer.length < samples.length )
         buffer = new short[samples.length];
 
      for( int i = 0; i < samples.length; i++ )
         buffer[i] = (short)(samples[i] * Short.MAX_VALUE);;
   }
 
   public void SetStereo(int son){
	   if(son == 0){
		   track.setStereoVolume(0, 1);
	   }else{
		   track.setStereoVolume(1, 0);
	   }
   }
}

Et le code du côté de l’arduino :

#include <Servo.h> 
 
const int analogR = A5;
const int analogL = A4;
const int analogOutPin = 9;
 
int valeursR[10];
int totalR = 0;
int etatR = 0;
 
int valeursL[10];
int totalL = 0;
int etatL = 0;
 
int pETAT = 0;
int ETAT = 0;
 
unsigned long time;
unsigned long vtime;
 
int sensorValueR = 0;
int sensorValueL = 0;
 
Servo SVitesse;
Servo SDirection;
 
void setup() {
  Serial.begin(9600);
  time = micros();
  SDirection.attach(10);
  SVitesse.attach(11);
 
  SVitesse.write(90);
  SDirection.write(80);
}
 
void loop() {
  /*Serial.print(0xff, BYTE); // Sync byte
  Serial.print((sensorValue >>  & 0xff, BYTE);
  Serial.print(sensorValue & 0xff, BYTE);*/
 
  sensorValueR = analogRead(analogR)*15;
  sensorValueL = analogRead(analogL)*15;  
 
  for (int i=0; i <= 8; i++){
    valeursR[i] = valeursR[i+1];
    valeursL[i] = valeursL[i+1];
  }
 
  if(sensorValueR > 40){
    valeursR[9] = 1;
  }else{
    valeursR[9] = 0;
  }
 
  if(sensorValueL > 40){
    valeursL[9] = 1;
  }else{
    valeursL[9] = 0;
  }
 
  totalR = 0;
  for (int i=0; i <= 9; i++){
    totalR += valeursR[i];
  }
 
  totalL = 0;
  for (int i=0; i <= 9; i++){
    totalL += valeursL[i];
  }
 
  pETAT = ETAT;
 
  if(totalR == 0){
    if(etatR == 1){
      etatR = 0;
    }
  }else if(totalR == 10){
    if(etatR == 0){
      etatR = 1;
      ETAT = 0;
      check();
    }
  }
 
  if(totalL == 0){
    if(etatL == 1){
      etatL = 0;
    }
  }else if(totalL == 10){
    if(etatL == 0){
      etatL = 1;
      ETAT = 1;
      check();
    }
  }     
 
  delayMicroseconds(1);
}
 
void check(){
  if(pETAT != ETAT){
    if(pETAT == 0){
      vtime = micros() - time;
      vtime = map(vtime, 60000, 300000, 0, 20);
 
      if(vtime < 5){
        SDirection.write(110);
      }else if(vtime > 5 && vtime < 8){
        //SDirection.write(100);
      }else if(vtime > 8 && vtime < 13){
        SDirection.write(90);
      }else if(vtime > 13 && vtime < 18){
        //SDirection.write(80);
      }else if(vtime > 17){
        SDirection.write(70);
      }else{
        SDirection.write(80);
      }
 
      time = micros();
    }else{
      //Vitesse
      vtime = micros() - time;
      vtime = map(vtime, 60000, 300000, 0, 20);
 
      if(vtime < 5){
        SVitesse.write(70);
      }else if(vtime > 5 && vtime < 8){
        //SVitesse.write(80);
      }else if(vtime > 8 && vtime < 13){
        SVitesse.write(90);
      }else if(vtime > 13 && vtime < 18){
        //SVitesse.write(100);
      }else if(vtime > 17){
        SVitesse.write(110);
      }else{
        SVitesse.write(90);
      }
 
      time = micros();
    }
  }
}

A savoir que les codes sources de ce projet là ne sont pas très très ” optimisés “, elles sont plus là pour inspirer (Haha).

Pour le fun, une photo de ' l'arduino embarqué '

Au menu de la prochaine mise à jour, faire le retour caméra et rendre la voiture autonome grâce au GPS :)

23 thoughts on “Voiture télécommandé par Internet

  1. Bonjour
    Une méthode moins onéreuse encore :
    - dessouder les LED IR de vos anciennes télécommandes (j’ai utilisé 2)
    - les assembler sur un petit adaptateur stéréo et le “plugger” sur le port casque d’un smartphone ou tablette android.
    - Carte Arduino (j’ai un ADK rev 3) munie d’un récepteur IR (à 1.50 Euros)
    - Adapter l’excellent code de ce site pour envoyer quelques PCM : AudioTrack(AudioManager.STREAM_MUSIC, 48000,
    AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_8BIT,
    bufSize, AudioTrack.MODE_STATIC);
    Le robot exécutera les manoeuvres selon la fréquence… Voilà
    J’adore votre petit buggi et merci pour votre excellent code…..

  2. You post interesting content here. Your website deserves much more
    visitors. It can go viral if you give it initial boost,
    i know useful service that can help you, just search
    in google: svetsern traffic tips

  3. We do have faith in each of the aspects you have accessible to your post. They’re convincing which enable it to undoubtedly operate. Even now, the particular threads are extremely brief to start. Is it possible you you need to expand them somewhat out of next period? Information submit.. oto koltuk döşeme kumaşı

Leave a Reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>