Differenze tra le versioni di "Ruschino WiFi"

Da raspibo.
Jump to navigation Jump to search
Riga 130: Riga 130:
 
Arduino che interpreta semplici comandi inviati da ESP8266 e pilota il ponte H.
 
Arduino che interpreta semplici comandi inviati da ESP8266 e pilota il ponte H.
  
=== ESP8266 Web server ===
 
Sul modulo wifi ho aggiornato il firmware installando Nodemcu (come riportato in [[ESP8266#Upload_di_un_nuovo_firmware]]).
 
  
Questo firmware permette la pogrammazione dei moduli in linguaggio lua e ha già molte librerie a disposzione.
 
 
Questo è il codice del file  web_serv.lua che crea e gestire le richieste http.
 
 
Seguono il codice della pagina index.htm e init.lua che lancia il server poco dopo il boot del modulo.
 
 
Dopo aver iniziato a scrivere index.htm mi sono accorto che una pagina più lunga di 512 byte non viene inviata al client a causa di una limitazione del firmware.
 
 
Per ovviare al problema ho adottato il codice proposto da Lefteris Stamataros e proposto in un video (vedi riferimenti).
 
 
Il problema si è risolto in modo efficace, ho solo riadattato il tutto per i miei scopi.
 
 
Si inizia configurando il modulo come access point, gli ultimi caratteri sono ricavati dal macaddress questo ci predispone per costruire più robot e riuscire a distinguerne il segnale radio.
 
 
In questa prima parte configuro il modulo come access point:
 
 
<source lang=lua>
 
local str=wifi.ap.getmac();
 
local ssidTemp=string.format("%s%s%s",string.sub(str,10,11),string.sub(str,13,14),string.sub(str,16,17));
 
cfg={};
 
cfg.ssid="192.168.4.1_Ruschino-"..ssidTemp;
 
wifi.ap.config(cfg);
 
cfg.ip="192.168.4.1";
 
cfg.netmask="255.255.255.0";
 
cfg.gateway="192.168.4.1";
 
wifi.ap.setip(cfg);
 
wifi.setmode(wifi.SOFTAP)
 
str=nil;
 
ssidTemp=nil;
 
</source>
 
 
Invio ad arduino una stringa per essere sicuro che le ruote siano ferme. Stampo la stringa sulla porta seriale a cui è collegato Arduino:
 
 
<source lang=lua>
 
collectgarbage();
 
print("R=0&l=0&D=0&d=0&E=0&e=0")
 
<source>
 
 
Preparo una lista delle pagine disponibili sul server web, per aggiunngere una nuova pagina oltre a creare un file va popolata anche la variabile. Da notare che index.htm può essere richiamato sia con http://192.168.4.1/ che come http://192.168.4.1/index.htm perchè entrambe le variabili httpRequest["/"] e httpRequest["/index.htm"] puntano allo stesso file.
 
 
<source lang=lua>
 
local httpRequest={}
 
httpRequest["/"]="index.htm";
 
httpRequest["/index.htm"]="index.htm";
 
httpRequest["/style.css"]="style.css";
 
</source>
 
 
Per ogni file dichiaro una intestazione il mimetype da fornire ai browser dei client per indicarne il contenuto, sarà utile soprattutto se si voglio usare immagini.
 
 
<source lang=lua>
 
local getContentType={};
 
getContentType["/"]="text/html";
 
getContentType["/index.htm"]="text/html";
 
getContentType["/style.css"]="text/css";
 
</source>
 
 
Creo un server in ascolto sulla porta 80 ed attendo le richieste. Se la richiesta corrisponde al nome di un file lo vado a leggere a blocchi di 512 byte e lo invio al client altrimenti restituisco un messaggio di errore.
 
 
<source lang=lua>
 
local filePos=0;
 
if srv then srv:close() srv=nil end
 
srv=net.createServer(net.TCP)
 
srv:listen(80,function(conn)
 
    conn:on("receive", function(conn,request)
 
        --print("[New Request]");
 
        local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP");
 
        if(method == nil)then
 
        _, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP");
 
        end
 
        local formDATA = {}
 
</source>
 
 
Se la pagina viene richiamata passando variabili querystring decodifico l'url e invio le variabili sulla porta seriale ad arduino.
 
 
Per inviare i comandi il codice javascript passa una richiesta XMLHttpRequest richiedendo una pagina inesistente ma passando alcune variabili.
 
 
Con questo metodo non è necessario ricaricare la pagina e il traffico di rete risulta minimo.
 
 
<source lang=lua>
 
        if (vars ~= nil)then
 
            --for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
 
            --    print("["..k.."="..v.."]");
 
            --    formDATA[k] = v
 
            --end 
 
            print(vars)
 
        end
 
        if getContentType[path] then
 
            requestFile=httpRequest[path];
 
        --    print("[Sending file "..requestFile.."]");           
 
            filePos=0;
 
            conn:send("HTTP/1.1 200 OK\r\nContent-Type: "..getContentType[path].."\r\n\r\n");           
 
        else
 
        --    print("[File "..path.." not found]");
 
            conn:send("HTTP/1.1 404 Not Found\r\n\r\n")
 
            conn:close();
 
            collectgarbage();
 
        end
 
    end)
 
</source>
 
 
In questa sezione viene gestito l'invio della pagina web  a blocchi di 512 byte fino al termine del file.
 
 
<source lang=lua>
 
    conn:on("sent",function(conn)
 
        if requestFile then
 
            if file.open(requestFile,r) then
 
                file.seek("set",filePos);
 
                local partial_data=file.read(512);
 
                file.close();
 
                if partial_data then
 
                    filePos=filePos+#partial_data;
 
                    --print("["..filePos.." bytes sent]");
 
                    conn:send(partial_data);
 
                    if (string.len(partial_data)==512) then
 
                        return;
 
                    end
 
                 
 
                end
 
            --else
 
            --    print("[Error opening file"..requestFile.."]");
 
            end
 
        end
 
        --print("[Connection closed]");
 
        conn:close();
 
        collectgarbage();
 
    end)
 
end)
 
</source>
 
 
Codice del file index.htm
 
 
Questa pagina è stata creata per dispositivi mobili, legge gli eventi su uno schemro touch, al momento utilizzarla da pc non è possibile, anche se penso basti poco per riadattarla.
 
 
L'aspetto è minimale, ci sono due quadrati il cui trascinamento sullo schermo regola la potenza erogata verso i motori.
 
 
Motori girano in entrambe le direzioni e riportandoli in posizione centrale le route si fermano.
 
 
Innanzitutto definiamo lo stile della pagina per garantirne la compatibilità con i vari dispositivi.
 
 
<source lang=html>
 
<html>
 
<head>
 
<meta name="viewport" content="initial-scale = 1.0,maximum-scale = 1.0"/>
 
<style>
 
body {padding: 0; margin: 0;}
 
#heade{ border: 3px solid #ff0000; height: 100px; position: fixed; top: 0px; right:  100px; left:  100px}
 
#l{ border: 3px solid #ff0000; margin: auto 200px auto 0px; width: 100px; height: 100px; position: fixed; top: 200px; left:  0px}
 
#r{ border: 3px solid #0000ff; margin: 0px auto auto 255px; width: 100px; height: 100px; position: fixed; top: 200px; right: 0px}
 
.c{ border: -1px solid #000000; width: 100%; position: fixed}
 
</style>
 
</source>
 
 
Dopo aver azzerato variabili passiamo a gestire gli eventi di tocco sullo schermo.
 
 
Vengono letti i dati anche durante lo scorrimento  (è una predisposzione per ampliamenti di codice futuri) e quando solleviamo il dito dallo schermo parte la richiesta in background della pagina test a cui vengono accodati tutti i parametri da passare ad arduino.
 
 
Inoltre vengono spostati i quadrati sulla pagina per dare un feedback visivo al pilota.
 
 
<source lang=lua>
 
<script> 
 
var L=200;
 
var R=200;
 
var ML=0;
 
var MR=0;
 
var DL=0;
 
var DR=0;
 
var EL=0;
 
var ER=0;
 
addEventListener('touchmove', function(e) {
 
e.preventDefault();
 
var touch = e.touches[0];
 
var posY = touch.pageY - 50;
 
var posX = touch.pageX - 50;
 
if (posY>=0 && posY<=400) {
 
    X=posX;
 
    Y=posY;
 
        if(e.touches.length == 1 && posX < 100) {
 
            document.getElementById("l").style.top = posY+'px';
 
            L=Math.round(posY);
 
        } 
 
        if(e.touches.length == 1 && posX > 250) {
 
            document.getElementById("r").style.top = posY+'px';
 
            R=Math.round(posY);
 
        }
 
        if(e.touches.length == 2) { // If two fingers are touching
 
            document.getElementById("l").style.top = posY+'px';
 
            L=Math.round(posY);
 
            document.getElementById("r").style.top = posY+'px';
 
            R=Math.round(posY);
 
        }
 
}       
 
}, false);
 
addEventListener('touchend', function(e) {
 
e.preventDefault();
 
var touch = e.touches[0];
 
var xhttp = new XMLHttpRequest();
 
xhttp.onreadystatechange = function() {
 
}
 
if (L>180 && L<220) {ML=0;EL=0;} //P. centrale 10px toll.
 
if (R>180 && R<220) {MR=0;ER=0;}
 
if (L>220) {EL=1;DL=1;}
 
if (R>220) {ER=1;DR=1;}
 
if (L<180) {EL=1;DL=0;}
 
if (R<180) {ER=1;DR=0;}
 
if (L<180) {ML=Math.round(255-(L*1.30))}
 
if (R<180) {MR=Math.round(255-(R*1.30))}
 
if (L>220) {ML=Math.round(255-((400-L)*1.30))}
 
if (R>220) {MR=Math.round(255-((400-R)*1.30))}
 
//document.getElementById("header").innerHTML= ML+'-'+MR+'_'+L+'-'+R;
 
xhttp.open("GET", "test?R="+MR+"&l="+ML+"&D="+DR+"&d="+DL+"&E="+ER+"&e="+EL, true);
 
xhttp.send();
 
}, false); 
 
</script>
 
</head>
 
<body>
 
<div id=header></div>
 
<div id=l></div>
 
<div id=r></div>
 
<hr class=c style="top: 190px">
 
<hr class=c style="top: 300px">
 
</body>
 
</html>
 
</source>
 
 
Questa è l'interfaccia di controllo vista dal mio telefono. Al momento sono supportati solo gli eventi provenienti da device con touch screen, per il comando da pc l'interfaccia andrà adattata per la gestione evnti del mouse.
 
 
{| style="background:#FBFBFB; width:100%; border:1px solid #a7d7f9; vertical-align:top; color:#000; padding: 5px 10px 10px 8px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius:10px;"
 
|style="text-align: center; width:50%" |[[File:Ruschino web0.png|200px]]
 
|style="text-align: center; width:50%" |[[File:Ruschino web1.png|200px]]
 
|}
 
 
Ed infine init.lua
 
 
Questo file attende tre secondi dopo il boot per lanciare il server web, si tratta di una soluzione pratica per prevenire lo start di uno script che va in errore e ci blocca il modulo wifi.
 
 
Inoltre viene settata la porta seriale per comunicare con arduino.
 
 
<source lang=lua>
 
tmr.alarm(0, 3000, 0, function() dofile('web_serv.lua') end )
 
uart.setup(0,115200,8,0,1,0)
 
</source>
 
  
 
=== Codice arduino ===
 
=== Codice arduino ===
Riga 485: Riga 242:
  
 
* [https://github.com/raspibo/nckicad/tree/master/esempi/04_motor_driver_LD293D Progetto kicad della scheda controllo motori]
 
* [https://github.com/raspibo/nckicad/tree/master/esempi/04_motor_driver_LD293D Progetto kicad della scheda controllo motori]
 
* [http://www.youtube.com/watch?v=5ElOFNiphGA ESP8266 nodemcu LUA TUTORIAL how to solve payload size limit by Lefteris Stamataros]
 
 
* [http://www.instructables.com/id/ESP8266-webserver-serving-multiple-pages/?ALLSTEPS ESP8266 webserver con più pagine]
 
  
 
* [https://github.com/raspibo/Ruschino_WiFi Archivio software del progetto, compresi i files per la stampa 3D]
 
* [https://github.com/raspibo/Ruschino_WiFi Archivio software del progetto, compresi i files per la stampa 3D]
  
* [http://inserthtml.com/demo/mobile-touch/ Pagina web con gestione eventi display touch]
 
  
 
[[Category:Progetti]]
 
[[Category:Progetti]]
 +
[[Category:Ruschino]]

Versione delle 18:42, 12 feb 2016

Ruschino front.jpg

Ruschino
Un robot terrestre, un pò di materiale recuperato, è qualche pezzo di codice

Un robot terrestre, un pò di materiale recuperato, e qualche pezzo di codice

Il progetto nasce per vari motivi:

  • provare a costruire un robot, perchè è un'esperienza nuova e' sempre interessante
  • riciclare un pò di hardware a cui vale la pena dare una nuova vita
  • studiare nuove tecnologie sia hardware che software
  • aggiungere un nuovo gioco alla nostra ormai ricca collezione di progetti che mostriamo durante gli eventi e i robottini sono sempre quelli che hanno più successo
  • affiancare ai robot di Olotoria qualche altro robot per differenziare un po' le esperienze

Partiamo dall'hardware

Lo scoglio più grande è sicuramente hardware quando si pensa ad un robot e l'occasione di poter utilizzare motori con il riduttore, già facilmente assemblabili era troppo ghiotta per poterla lasciare.

Quasi un anno prima grazie ad Olotoria siamo riusciti a recuperare hardware da un vecchio distributore automatico, mi hanno subito colpito i motori con motoriduttore incorporato e ho voluto cercare di utilizzarli per un progetto.

Motori

Sono motori in corrente continua con un riduttore integrato, che hanno molta coppia, non sono particolarmente semplici da alimentare perchè funzionano a 24 volts, questo ha rallentato un po' la realizzazione del progetto(vedremo sotto nella descrizione della parte elettronica qualche informazione in piu').

Ruschino cassetto motori.jpg Ruschino motori.jpg Ruschino motori dett.jpg Ruschino motori dett f.jpg


Hanno una scatola ingranaggi abbastanza grande che obbliga l'utilizzo di ruote di diversi centimetri di diametro, ma con il vantaggio che passandoci dentro due barre filettate formano già un telaio.

L'attacco per i mozzi delle ruote richiede la stampa di alcuni pezzi con la stampante 3d perche' e' a forma di stella. Siccome gli alberi subiranno sollecitazioni all'interno e' comunque necessaria una vite passante che dia solidita' alla struttura.

Per ridurre i disturbi elettrici generati dai motori ho subito saldato ai terminali ed alla carcassa un paio di condensatori (vedi riferimenti).

Ponte H

Per pilotare i motori è necessario un ponte H che sopporti i 24 volts di alimentazione, per questo ho disegnato un board con kicad che utilizza l'integrato L293D ed un paio di transistor per limitare l'utilizzo di pin per il circuuito di comando.

L'integrato richiede due tensioni, una dedicata ai motori(24V), e 5 volts per la logica di comando.

Questo integrato e' abbastanza flessibile, perche' in realta' al suo interno ci sono 4 semiponti, in questo caso i semiponti si utlizzano a coppie.

Questo pero' comporta che per usare due semiponti si necessario utilizzare due pin del microcontrollore, oppure come nel nostro caso u piccolo accorgimento per negare il segnale di un pin e mandarlo in ingresso al secondo semiponte.

04 motor driver L293D.png 04 motor driver L293D top.png 04 motor driver L293D.sch.png L293D ruschino board.png

Schede di controllo

La board è anche disegnata per essere montata al posto di un'altra schedina già presente sulla carcassa dei motoriduttori, in questo modo si sfruttano anche le viti già predisposte.

Per ridurre le dimensioni della board la dissipazione del ponte ad H potrebbe non essere ottimale, vediamo se saranno necessari accorgimenti particolari in seguito.

Per il comando del robot al momento ci sono anche un arduino nano che s'incastra sulla scheda che ho disegnato ed un modulo ESP8266 connesso alla seriale dell'Arduino.

Ruschino motore det.jpg Ruschino board 1.jpg Ruschino board 2.jpg Ruschino ruote 1.jpg

Altro hardware

Per completare l'assemblaggio di base ho aggiunto qualche supporto stampato in 3D e una ballcaster come terzo punto di appoggio, una scatola per contenere le batterie ed i due convertitori dc/dc, due fusibili, l'interruttore generale.

Come ruote ho impilato 4 cd rom da buttare con strisce parasifferi a fare da battistrada.

Completano la lista una manciata di viti e qualche filo per connettere le schede.

Ruschino pezzi 3D.jpg Ruschino dett ruota.jpg Ruschino int ruota.jpg Ruschino ball caster.jpg

Assemblaggio

L'assemblaggio meccanico è piuttosto semplice e non è obbligatorio che sia proprio fatto così volendo è possibile provare altre combinazioni. Riporto qualche foto esplicativa.

La vite lunga che si vede è una barra filettata MA4 che entra perfettamente nei fori del telaio dei motori e gli altri pezzi sono progettati di conseguenza. Per completare una manciata di dadi e qualche rondella.

L'unica nota per quanto riguarda il cubetto della ball caster è che se i dadi dovessero entrare a fatica è possibile spingerli in sede scaldandoli un pò.

IMG 20151111 172650.jpg IMG 20151111 172609.jpg IMG 20151111 172552.jpg


Elettronica

Alimentazioni

Ricapitolando dobbiamo alimentare:

  • Motori a 24Volts DC
  • Arduino nano 5Volts DC
  • Ponte H L293D Volts DC
  • Scheda WiFi ESP8266 3.3Volts DC

Tre tensioni diverse per un piccolo circuito, inoltre 24V e' una tensione un po' alta da generare con le pile, questo mi ha rallentato un po' nella realizzazione, non riuscivo a decidere come fare.

Alla fine ho optato per l'utilizzo di batterie di recupero per arrivare ad una tensione abbastanza alta, elevata a 24V con un convertitore step up.

Secondo convertitore che genera 5Volts direttamente su una presa USB (ne ho trovato un piccolo ed economico) per alimentare sia arduino che il ponte H.

Per il modulo Wifi ho deciso di fare una saldatura volante usando un regolatore di tensione che abbassa i 5V a 3.3V attaccato sul retro del modulo ESP8266 (e' venuto molto compatto ed economico, forse non troppo robusto).

Per sicurezza ho messo due fusibili su cavo piu' vicino possibile alle pile per evitare corti ed incidenti.

Software

Il software è ancora in fase di completamento, ma si basa su due elementi intelligenti: ESP8266 che viene utilizzato come access point, e server web.

Arduino che interpreta semplici comandi inviati da ESP8266 e pilota il ponte H.


Codice arduino

Il programma di arduino è abbastanza semplice, si tratta sostanzialmente di generare due segnali pwm in base alla decodifica della stringa passata dal modulo wifi.

La stringa ha questo formato:

R=255&l=255&D=0&d=1&E=0&e=0
R=potenza del motore destro da 0 a 255
l=potenza del motore destro da 0 a 255
D=direzione del motore destro 0 o 1 
d=direzione del motore sinistro 0 o 1 
E=variabile predisposta per ampliamenti 
e=variabile predisposta per ampliamenti 

Dopo aver decodificato la stringa precedente il robot girera su se stesso a tutta potenza.

 String comando;
 int en12=9;
 int en34=10;
 int dir12=4;
 int dir34=5;
 int board=1;
 int out_bridge_n=0;
 int incomingByte = 0;
 int s_speed_sx=0;
 int s_speed_dx=0;
 boolean direction_sx=0;
 boolean direction_dx=0;
 boolean enable_sx=0; //non utilizzato
 boolean enable_dx=0; //non utilizzato
 int command_name;
 int position;
 char cmd;
 // Calculate based on max input size expected for one command
 #define INPUT_SIZE 30
 void motor_drive(int out_bridge, boolean dir, int speed) {
  if (out_bridge == 34) {
    digitalWrite(dir34, dir);
    analogWrite(en34, speed);  
  } 
  else if (out_bridge == 12) {
    digitalWrite(dir12, dir);
    analogWrite(en12, speed);
  }  
 }  
 void setup() {
  Serial.begin(115200); 
  pinMode(en12, OUTPUT);
  pinMode(dir12, OUTPUT);
  pinMode(en34, OUTPUT);
  pinMode(dir34, OUTPUT); 
  Serial.println("Start");
 }
 void loop() {
  if (Serial.available() > 0) {
    char input[INPUT_SIZE + 1];
    byte size = Serial.readBytes(input, INPUT_SIZE);
    input[size] = 0;
    char* command = strtok(input, "&");
    while (command != 0)
    {
      char* command_value = strchr(command, '=');
      if (command_value != 0)
      {
        *command_value = 0;
        command_name = command[0];
        ++command_value;
        position = atoi(command_value);
      }
      command = strtok(0, "&");
      switch(command_name) {
      case 'l':
        s_speed_sx=position;
        break;
      case 'R':
        s_speed_dx=position;
        break;  
      case 'D':
        direction_dx=position;
        break;
      case 'd':
        direction_sx=position;
        break;  
      case 'E':
        enable_dx=position;
        break;
      case 'e':
        enable_sx=position;
        break;  
      }  
    }
   motor_drive(34,direction_sx,s_speed_sx);
   motor_drive(12,direction_dx,s_speed_dx); 
  }
 }

Varie

Inizialmente non ci avevo pensato, ma avendo configurato il modulo ESP8266 come access point è possibile pilotare il robottino da più device contemporaneamente, nel senso che i comandi vengono inviati in modo asincrono e la loro elaborazione dura pochissimo.

Al momento questo non risulta particolarmente interessante, ma lo potrebbe diventare in futuro, qualora si aggiungessero accessori, inoltre si potrebbe utilizzare questa caratteristica per far pilotare il robot da un bambino con un genitore che in caso il robot sia in pericolo può intervenire prima che ruschino possa ad esempio cadere per le scale.

Riferimenti