SmartMop è un robot autonomo a trazione differenziale, è stato progettato per lavare i pavimenti in modo autonomo. Il robot viaggia evitando ostacoli grazie a un sensore ad ultrasuoni (HC-SR04), nel mentre due spazzole che ruotano e una pompa per il liquido detergente si occupano di pulire.

Componenti:
- Arduino Uno
- Driver motori L293D: per pilotare le 2 ruote motrici (una per lato) con controllo di velocità (PWM) e direzione. È quello che permette avanti/indietro e le sterzate differenziali (girare più veloce da un lato che dall'altro).
- 2 motori per le spazzole (i dischi visibili sotto il telaio) comandati semplicemente in on/off tramite relè.
- Sensore a ultrasuoni HC-SR04: per la rilevazione degli ostacoli
- 2 moduli relè: uno per i due motori delle spazzole, uno per la pompa
- Pompa per il liquido lavapavimenti.
- Batterie dedicate per motori e carichi (separato dalla logica di Arduino)


NB: alimenta ruote, spazzole e pompa con una fonte esterna di energia (le batterie nello schema), non direttamente dai 5V di Arduino per non sovraccaricare la scheda. I due motori delle spazzole ciascuno con il proprio modulo relè, nel codice sono trattati come un unico blocco tramite spazzoleOn()/spazzoleOff() (relè su D10): possono quindi essere collegati in parallelo sullo stesso relè oppure su relè separati pilotati dallo stesso pin, a seconda di come preferisci fare i collegamenti.
Codice:
All'avvio si accendono spazzole e pompa che restano sempre attive durante il funzionamento.
Nel loop (ciclo principale) il robot misura di continuo la distanza con l'ultrasuoni e se la strada è libera (distMin impostata a 20 cm) il robot andrà avanti alla sua velocità.
Se invece rileva un ostacolo (quindi il sensore ad ultrasuoni misura <20cm) si entra nella funzione gestisci(). Questa funzione gestisci() fa si che il dispositivo si ferma, va indietro per un breve tratto (tmpIndietro) e si ferma di nuovo. Prova a girare alternativamente prima da un lato poi dall'altro: la variabile giroDx alterna il lato di primo tentativo ad ogni ostacolo per non "impuntarsi" sempre nella stessa direzione.
Il tentativo di giro (funzione prova) avviene "a piccoli passi": gira per un breve intervallo (tmpPasso), si ferma, ricontrolla la distanza e continua così fino a un tempo massimo (tmpGiro) o finché non trova la via libera.
Se nessuno dei due lati funziona, il robot va indietro ancora ed fa un'inversione di marcia più grande (giro(true) per tmpTot millisecondi, quasi un dietrofront).
Tutte le costanti principali (velocità, tempi di manovra, soglia di distanza) sono raggruppate in alto, cosa utile per calibrare il comportamento in base al montaggio ecc.
Le funzioni principali sono:
- avanti(), indietro(), giro(), stopMot(): pilotano i motori delle ruote
- spazzoleOn()/Off(), pompaOn()/Off(): controllano i due relè
- leggiDist(): legge la distanza dal sensore a ultrasuoni (ritorna -1 se non rileva nulla entro il timeout di 30 ms)
Per tararlo in modo corretto: se il robot urta comunque gli ostacoli provate ad aumentare distMin e se le manovre che tentano di aggirare ostacoli sono troppo brusche o troppo lente modificate velGiro e tmpPasso.
const int x1 = 3;
const int x2 = 5;
const int x3 = 4;
const int x4 = 9;
const int x5 = 6;
const int x6 = 7;
const int rele1 = 10;
const int rele2 = 11;
const int trig = 12;
const int echo = 13;
const int vel = 180;
const int velGiro = 150;
const int distMin = 20;
const unsigned long tmpIndietro = 400;
const unsigned long tmpPasso = 200;
const unsigned long tmpGiro = 900;
const unsigned long tmpTot = 1600;
bool giroDx = true;
void setup()
{
pinMode(x1, OUTPUT);
pinMode(x2, OUTPUT);
pinMode(x3, OUTPUT);
pinMode(x4, OUTPUT);
pinMode(x5, OUTPUT);
pinMode(x6, OUTPUT);
pinMode(rele1, OUTPUT);
pinMode(rele2, OUTPUT);
digitalWrite(rele1, LOW);
digitalWrite(rele2, LOW);
pinMode(trig, OUTPUT);
pinMode(echo, INPUT);
Serial.begin(9600);
Serial.println("avvio");
spazzoleOn();
pompaOn();
delay(500);
}
void loop()
{
long dist = leggiDist();
if (dist > 0 && dist < distMin)
{
Serial.print("ostacolo ");
Serial.print(dist);
Serial.println(" cm");
gestisci();
}
else
{
avanti(vel);
}
}
void gestisci()
{
stopMot();
delay(150);
indietro(vel);
delay(tmpIndietro);
stopMot();
delay(150);
bool lato = giroDx;
giroDx = !giroDx;
if (lato)
Serial.println("provo dx");
else
Serial.println("provo sx");
if (prova(lato))
return;
Serial.println("provo altro lato");
if (prova(!lato))
return;
Serial.println("faccio giro completo");
indietro(vel);
delay(tmpIndietro);
stopMot();
delay(150);
giro(true);
delay(tmpTot);
stopMot();
delay(150);
}
bool prova(bool dx)
{
unsigned long tmp = 0;
while (tmp < tmpGiro)
{
// se dx è true gira dall'altra parte
giro(!dx);
delay(tmpPasso);
stopMot();
tmp = tmp + tmpPasso;
long dist = leggiDist();
if (dist < 0 || dist >= distMin)
{
Serial.println("ok libero");
return true;
}
}
return false;
}
// motori
void avanti(int vel)
{
digitalWrite(x2, HIGH);
digitalWrite(x3, LOW);
digitalWrite(x5, HIGH);
digitalWrite(x6, LOW);
analogWrite(x1, vel);
analogWrite(x4, vel);
}
void indietro(int vel)
{
digitalWrite(x2, LOW);
digitalWrite(x3, HIGH);
digitalWrite(x5, LOW);
digitalWrite(x6, HIGH);
analogWrite(x1, vel);
analogWrite(x4, vel);
}
void giro(bool sx)
{
if (sx)
{
digitalWrite(x2, LOW);
digitalWrite(x3, HIGH);
digitalWrite(x5, HIGH);
digitalWrite(x6, LOW);
}
else
{
digitalWrite(x2, HIGH);
digitalWrite(x3, LOW);
digitalWrite(x5, LOW);
digitalWrite(x6, HIGH);
}
analogWrite(x1, velGiro);
analogWrite(x4, velGiro);
}
void stopMot()
{
digitalWrite(x2, LOW);
digitalWrite(x3, LOW);
digitalWrite(x5, LOW);
digitalWrite(x6, LOW);
analogWrite(x1, 0);
analogWrite(x4, 0);
}
// spazzole
void spazzoleOn()
{
digitalWrite(rele1, HIGH);
}
void spazzoleOff()
{
digitalWrite(rele1, LOW);
}
// pompa
void pompaOn()
{
digitalWrite(rele2, HIGH);
}
void pompaOff()
{
digitalWrite(rele2, LOW);
}
// sensore
long leggiDist()
{
digitalWrite(trig, LOW);
delayMicroseconds(2);
digitalWrite(trig, HIGH);
delayMicroseconds(10);
digitalWrite(trig, LOW);
long tmp = pulseIn(echo, HIGH, 30000);
if (tmp == 0)
return -1;
long dist = tmp * 0.034 / 2;
return dist;
}