[SOLVED] Problema SRF04 che si blocca!

Ciao a tutti.
Sono relativemente nuovo di Arduino (meno come esperienza di programmazione e in parte anche di hardware;-)) e volevo segnalarvi un problema che ho avuto con l'SRF04 (sensore ultrasuoni a 4 pin). Ho cercato nel forum e non ho trovato altri con questa soluzione (ne hanno parlato nei forum in inglese, infatti ho postato anche lì la soluzione, in inglese ovviamente) per cui vi descrivo il problema e la soluzione.

Ho ricevuto il mio primo SRF04 oggi, e tutto contento ho iniziato a fare qualche prova con i vari esempio trovati in giro. Tutto ok, tranne per il fatto che non appena l'SRF04 non "vede" più alcun eco di ritorno, si impalla, ossia il pulseIn() restituisce sempre 0 per cui non misura più nulla! Apparentemente sembrava un problema di timeout, ma anche impostando il parametro del timeout nella pulseIn() (ho messo 50000 microsecondi) non c'è stato nulla da fare: se va in timeout si blocca e l'unico rimedio è staccare un attimo l'alimentazione dell'SRF04 e ricollegarla.
Questa cosa in altri post avevo letto che era stata fatta programmativamente collegando un transistor ad un pin dell'Arduino e con questo pilotare lo "spegnimento" del sensore per qualche millisecondo, ma non mi piacciono molto le soluzioni così "tarocche" per cui mi sono incaponito a cercaare una alternativa.

E l'ho trovata finalmente (guardate l'ora...), e SENZA ALCUNA MODIFICA HARDWARE.

Quando rilevo che il sensore è bloccato (ossia leggo 0 dalla pulseIn) la soluzione è stata reimpostare l'echo pin a OUTPUT, lo metto quindi io in LOW, e dopo un breve tempo (100ms) reimposto il pin su INPUT, quindi rileggo il sensore.
Ecco uno spezzone di codice per spiegarmi meglio:

// ... this is the reading loop
    long pulseDuration;
    long distance;
    int tries = 0;
    do
    {
        pulseDuration = pulseIn( echoPort, HIGH, 50000);
        distance = 0.034 * pulseDuration / 2;
        if ( pulseDuration == 0 ) {
            delay(100);
            pinMode(echoPort, OUTPUT);
            digitalWrite(echoPort, LOW);
            delay(100);
            pinMode(echoPort, INPUT);
        }
    } while (pulseDuration == 0 && ++tries < 3);
    if (pulseDuration == 0)
      // Out of range
    else
      // Read ok!

Spero sia utile agli altri sventurati che come me hanno preso l'SRF04 "tarocco" (d'ora in avanti solo SRF05!)..:wink:

EDIT: il codice si può ulteriormente semplificare, senza cicli, con una semplice if visto che l'"impallamento" del sensore si risolve al primo digitalWrite():

    pulseDuration = pulseIn( echoPort, HIGH, 50000);
    if ( pulseDuration == 0 ) {
        pinMode(echoPort, OUTPUT);
        digitalWrite(echoPort, LOW);
        delay(100);
        pinMode(echoPort, INPUT);
        delay(100);
        pulseDuration = pulseIn( echoPort, HIGH, 50000);
    }
    if (pulseDuration == 0)
    {
      // Out of range
      // ...
    }
    else
    {
      // Read ok!
      distance = 0.034 * pulseDuration / 2;
      // ...
    }

Ulteriore (e penso finale) correzione/miglioramento, ora penso sia a punto.
Per cui vi posto l'intero codice dello sketch, fatene quello che volete, io ho risolto il mio problema e spero che possa essere utile anche ad altro :slight_smile:
Ho collegato un led rosso al pin 12 e un led verde al 13: il primo (rosso) segnala quando un ostacolo è a meno di 15 cm, il secondo (verde) quando la distanza è maggiore della portata (3 mt). Se mettete anche un led (e relativa resistenza ovviamente) sul pin 2 (echo port) potete avere riscontro visivo dei ping effettuati (nel mio sketch lampeggia 2 volte al secondo, pari alla frequenza di scansione).

//Parametri per SR04 Sensore ultrasuoni
const int triggerPort = 3;
const int echoPort = 2;
const int ledRed = 12;
const int ledGreen = 13;
const int iMaxDistance = 300; // Centimetri
const int iReadInterval = 500; // millisecondi

void setup() {
    Serial.begin(9600);
    // Programmo i pin
    pinMode(triggerPort, OUTPUT);
    pinMode(echoPort, INPUT);
    pinMode(ledRed, OUTPUT);
    pinMode(ledGreen, OUTPUT);
}

void loop() {

    // Leggo la distanza
    long distance = readDistance(triggerPort, echoPort);
    if ( distance > 0 && distance < 38000 ) {
        Serial.print("Dist: ");
        Serial.print(distance);
        Serial.println(" cm");
        digitalWrite(ledGreen, LOW);
        if(distance < 15)
            digitalWrite(ledRed, HIGH);
        else
            digitalWrite(ledRed, LOW);
    }
    else
    {
        // è fuori dalla portata del sensore
        Serial.println("Dist: ---");
        digitalWrite(ledGreen, HIGH);
        digitalWrite(ledRed, LOW);
    }
    // Aspetta un poco
    delay(iReadInterval);
}

long readDistance(int triggerPort, int echoPort) {
    ping(triggerPort);
    long pulseDuration = pulseIn( echoPort, HIGH);
    if ( pulseDuration == 0) {
        // Tenta di recuperare l'eventuale blocco dell'SR04
        pinMode(echoPort, OUTPUT);
        digitalWrite(echoPort, LOW);
        delay(10);
        pinMode(echoPort, INPUT);
        delay(10);
    }
    long distance = 0.034 * pulseDuration / 2;
    return distance;
}

void ping(int triggerPort) {
    //porta bassa l'uscita del trigger
    digitalWrite( triggerPort, LOW );
    delayMicroseconds(2); // Pull down
    //invia un impulso su trigger
    digitalWrite( triggerPort, HIGH );
    delayMicroseconds(10); // Ping width
    digitalWrite( triggerPort, LOW );  
}