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
}
}
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).
Au menu de la prochaine mise à jour, faire le retour caméra et rendre la voiture autonome grâce au GPS
Bravo, le projet est super
tres belle oeuvre de reflexion .
je vais becoup
Super! j’adore l’idée d’utiliser un smartphone avec tous ses capteurs déjà intégrés Bravo ça donne des idées tiens
Très bien!!!! j’aimerai bien vous avoir comme mon proffesseur de programmation en base de donner et java
ok pareille
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…..
That’s an ineillegtnt answer to a difficult question xxx
Apcoapietirn for this information is over 9000-thank you!
Now that’s subtle! Great to hear from you.
Bonjour,
Très bon projet !
Je recherche un personne comme vous capable de m’aider pas à pas dans ce type de projet (je début dans l’arduino).
Si vous pourriez me contactez : maxime64121@gmail.com
Merci,
Cordialement, Maxime.
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
If you are interested in topic: earn extra income in india
- you should read about Bucksflooder first
I see your blog needs some fresh content. Writing manually is time consuming, but
there is solution for this. Just search for: Masquro’s strategies
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şı
Fortunately, there is a new AI bot that can write the content fo website, and it’s fully optimized to increase your ranking as well.
You can see the magic of AI in a video here =>> https://zeep.ly/zePEY
To the artandgj.com administrator, Your posts are always well researched and well written.
Hi artandgj.com administrator, Thanks for the detailed post!
Hi artandgj.com admin, You always provide useful information.
Dear artandgj.com administrator, You always provide great examples and case studies.
Dear artandgj.com administrator, Your posts are always well-supported by facts and figures.
Dear artandgj.com admin, You always provide valuable information.
Dear artandgj.com admin, You always provide in-depth analysis and understanding.
Hello artandgj.com admin, You always provide great examples and case studies.