Contrôler sa voiture GO par une page web

Dans l’article http://noscollections.ddns.net/carrerago/?p=2879 nous avons vu comment contrôler une voiture à partir d’un mini ordinateur. Nous allons maintenant voir comment contrôler la voiture à partir d’une page Internet.

Ce n’est pas vraiment très difficile, il suffit d’ajouter la fonction de serveur Web au script Python qui gère déjà le pilotage de la Voiture.

Une seul page web suffit pour contrôler une voiture. Il suffit d’avoir une barre de sélection avec un curseur. J’ai trouvé un script idéal sur ce site : https://www.w3schools.com/howto/howto_js_rangeslider.asp . J’ai adapté l’image du curseur en mettant l’image d’une manette,c’est plus sympa dans notre cas.

Ci-dessous le code html.

<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.slidecontainer {
  width: 100%;
}

.slider {
  -webkit-appearance: none;
  width: 100%;
  height: 10px;
  border-radius: 5px;
  background: #d3d3d3;
  outline: none;
  opacity: 0.7;
  -webkit-transition: .2s;
  transition: opacity .2s;
}

.slider:hover {
  opacity: 1;
}

.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 23px;
  height: 57px;
  border: 0;
  background: url('images/manet_2.png');
  cursor: pointer;
}

.slider::-moz-range-thumb {
  width: 23px;
  height: 57px;
  border: 0;
  background: url('manet_2.png');
  cursor: pointer;
}
</style>
</head>
<body>
<h1>Voiture 1 Controle</h1>
<p>Déplacer le curceur pour accelerer.</p>
<div class="slidecontainer">
  <input type="range" min="0" max="50" value="0" step="5" class="slider" id="myRange">
  <p>Value: <span id="demo"></span></p>
</div>

<script>
var slider = document.getElementById("myRange");
var output = document.getElementById("demo");
output.innerHTML = slider.value;

         function flag(couleur){
            var data_file = "order/?flag="+couleur;
            var http_request = new XMLHttpRequest();
            try{
               http_request = new XMLHttpRequest();
            }catch (e){
               try{
                  http_request = new ActiveXObject("Msxml2.XMLHTTP");		
               }catch (e) {	
                  try{
                     http_request = new ActiveXObject("Microsoft.XMLHTTP");
                  }catch (e){
                     alert("Your browser broke!");
                     return false;
                  }			
               }
            }		
            http_request.onreadystatechange = function(){
               if (http_request.readyState == 4  ){
                  var jsonObj = JSON.parse(http_request.responseText);
				  var mavar=jsonObj.data_updated ;
				  mavar=mavar;
                  document.getElementById("data").innerHTML = mavar;
					if ( mavar=="O"){
						loadJSON_2();
					}
               }
            }		
            http_request.open("GET", data_file, true);
            http_request.send();
		 }
slider.oninput = function() {
  output.innerHTML = this.value;
  flag(this.value);
}
</script>

</body>
</html>

Quelques infos :

<input type="range" min="0" max="50" value="0" step="5" class="slider" id="myRange">

Min et max sont les valeurs min et max de la barre donc min doit toujours être à zéro sinon vous pourrez pas vous arrêter. Max peut être mis à 100 pour aller au maximum de la puissance de la voiture, dans mon cas j’ai bridé la puissance à 50% . Step est la finesse de réglage de la puissance. Le plus fin étant 1.

A chaque changement de valeur de la barre, le script envoie la nouvelle valeur à cette adresse : order/?flag=valeur

Coté serveur sur le Raspberry Pi voici le script python.

#!/usr/bin/env python
import RPi.GPIO as GPIO
import time , datetime , json 

from bottle import route, run, template, static_file, request, post , get
import threading

from math import *

from tkinter import *
from tkinter.messagebox import *
from tkinter import ttk
import tkinter as tk
#import bme280

GPIO.setwarnings(False)

@route('/')
def index():
    return template('www/index.html')

@route('/index.html')
def index():
    return template('www/index.html')

@route('/player_1c.html')
def index():
    return template('www/player_1c.html')

@route('/order/')
def index3():
    global auto_order
    global race_flag
    flag=request.query.flag

    if (flag!=""):
        
        y=int(flag)
        pwm1.ChangeDutyCycle(y)
        valpwm.set(str(y))
    
    #data_update()
    data_out="O"
    return data_out

@route('/images/<images>')
def server_static(images):
    return static_file(images, root='www/images')

def serveurweb():
    run(host='0.0.0.0', port=8080)

threading.Thread(target=serveurweb).start()

GPIO.setmode(GPIO.BOARD)
# pin GPIO
piste_1_pin_pwm = 37  # PIN 37

GPIO.setup(piste_1_pin_pwm, GPIO.OUT)  # pin configurée en sortie
pwm1 = GPIO.PWM(piste_1_pin_pwm, 50)  # pwm à une fréquence de 50 Hz
rapport = 0       # rapport cyclique initial de 0%
pwm1.start(rapport) 

def change_power(f):
    #y=int(echelle.get())
    y=int(f)
    pwm1.ChangeDutyCycle(y)
    valpwm.set(str(y))
    #print (f)
    
def bt_stop():
    #remise a zero des relevés
    
    pwm1.ChangeDutyCycle(0)

## Interface Graphique
## -------------------
fenetre = Tk()
# a parametrer dans .ini
menu_width=300

fenetre.minsize(width=400, height=400)
fenetre.maxsize(width=400, height=400)
fenetre.wm_title("Web Speed - Serveur ")

####
p = PanedWindow(fenetre, orient=HORIZONTAL)
p.pack(side=TOP, expand=Y, fill=BOTH, pady=2, padx=2)

Frame1 = Frame(p, borderwidth=2, relief=GROOVE,width=300)
Frame1.pack(side=LEFT, padx=2, pady=2)

Frame_tortue = Frame(p, borderwidth=2, relief=GROOVE,width=650)
Frame_tortue.pack(side=LEFT, padx=2, pady=2)

p.add(Frame1)
p.pack()

nb = ttk.Notebook(Frame1)
# adding Frames as pages for the ttk.Notebook 
# 1er onglet

onglet1 = ttk.Frame(nb)
nb.add(onglet1, text='Commandes')

nb.pack(expand=1, fill="both")

lignes_droites = LabelFrame(onglet1, text="Control", padx=2, pady=2,height=30)
lignes_droites.pack(fill="both", expand="yes")
pa = PanedWindow(lignes_droites, orient=HORIZONTAL)
pa.pack(side=TOP, expand=Y, fill=BOTH, pady=1, padx=1)
pa.add(Button(fenetre, text="Stop", wraplength=menu_width ,command=bt_stop))

lignes_droites = LabelFrame(onglet1, text="Vitesse", padx=2, pady=2,height=30)
lignes_droites.pack(fill="both", expand="yes")
pa = PanedWindow(lignes_droites, orient=HORIZONTAL)
pa.pack(side=TOP, expand=Y, fill=BOTH, pady=1, padx=1)

echelle=Scale(onglet1, orient='horizontal', from_=0, to=40,
      resolution=1, tickinterval=20, length=350,
      label='Voiture 1', command=change_power)
pa.add(echelle)

fenetre.mainloop()

J’ai gardé une petite interface graphique pour que coté serveur (sur l’écran du Raspberry Pi on puisse contrôler la voiture et l’arrêter en cas d’urgence.

Quelques infos sur le script :

Lorsque l’on reçoit une requête dans le « répertoire » order, on vérifie si il y a une variable et on l’interprète pour récupérer la valeur et l’appliquer à la puissance moteur le pwm.

Téléchargement du script python et ses éléments (html, images,…) :

Notez l’adresse IP de votre Raspberry Pi pour saisir la saisir dans votre navigateur Internet. On utilise pas le port standard pour éviter tout conflit si votre Raspberry héberge déjà un serveur web (dans notre script on utilise le port 8080).

Adresse : http://IP_du_PI:8080/player_1c.html

Si vous souhaitez pouvoir contrôler la voiture a partir d’un appareil qui est extérieur à votre réseau (internet), il faut paramétrer votre box pour laisser passer le trafic et le diriger vers le Raspberry. Pour cela regarder les paramètres NAT de votre box.

La vidéo montre le contrôle de la voiture à partir d’un téléphone sur le réseau 4G donc extérieur au réseau. La voiture est bridée à 50% afin d’éviter les sorties de piste lors de la démo vidéo.