Using the Parallax PING))) ultrasonic distance sensor

I intended to play around with some of the NRF24L01 radio modules I have around, but my brain didn’t feel up to it after a day of debugging. So, instead i dusted off a Parallax PING))) sensor that I’ve had around for a long time. I thought it might be fun to see if I could use the OLED display to display the distance measured by the sensor. While watching Agents of Shield, I came up with the following tiny bit of code, demo’ed below.


#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); 

//          _          
//    ___  (_)__  ___ _
//   / _ \/ / _ \/ _ `/
//  / .__/_/_//_/\_, / 
// /_/          /___/  
//
// A simple rangefinder program using the Parallax PING sensor
// Writes display out to my tiny OLED display...

const int pingPin = 7 ;

char dbuf[10] ;

void 
draw()
{
    u8g.setDefaultBackgroundColor();
    u8g.drawBox(0, 0, 128, 64);
    u8g.setDefaultForegroundColor(); 
    u8g.setFont(u8g_font_10x20);
    u8g.drawStr(14, 42, dbuf);
    u8g.drawStr(94, 42, "cm");
}

void
setup()
{
    Serial.begin(9600);
}

void
loop() 
{
    long duration ;

    pinMode(pingPin, OUTPUT);
    digitalWrite(pingPin, LOW);         // Make sure we drive the pin low...
    delayMicroseconds(2);
    digitalWrite(pingPin, HIGH);        // Pulse high for 5us
    delayMicroseconds(5);
    digitalWrite(pingPin, LOW);         // Finish the pulse...

    pinMode(pingPin, INPUT);            // Get ready to read...
    duration = pulseIn(pingPin, HIGH);  // Wait for the echo pulse to drop back to low

    // The speed of sound is 29 us per cm... j

    float cm = duration / (29. * 2.) ;
    dtostrf(cm, 8, 2, dbuf) ;

    u8g.firstPage() ;
    do {
        draw() ;
    } while (u8g.nextPage()) ;
}

Addendum: I’ve been snapping short videos with the Vine app, and then posting them to Twitter and Facebook. Irritatingly, these don’t seem to actually play properly when I embed them back in my website. I’ll figure out a better workflow next time.

Addendum2: Goofing around a little, it appeared that this sensor worked out until about 70cm or so, and then snaps to 340cm. Not sure what that is about: the sensor should behave more accurately than that. I’ll experiment more tomorrow. I should also note that these Parallax sensors are fairly pricey: list price is almost $30. I like the Parallax guys a lot, but that’s pretty spendy. You can get much cheaper modules on Ebay, with costs as low as around $1.50. I haven’t got any of these, but they are worth trying, although various sources on the web suggest that you might get what you pay for. Try googling for “HC-SR04” for more information.

My version of minilife2 for the Arduino…

Without a lot more explanation, I did a bit more work on my implementation of Conway’s life, reducing the overhead so I can implement the full 128×64 bit resolution of the OLED display. I also hardcoded an initial pattern: the classic Gosper glider gun. It runs at about 1.625 frames/second, so a new glider is emitted every 18 seconds or so. Eventually, they wrap around and destroy the glider gun by colliding with it. I can think of a few more optimizations that are easy, but I doubt they are very important.


#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); 

#define XSIZE           (128)
#define XMASK           (127)
#define XSIZE_UINT8     (XSIZE/8)
#define YSIZE           (64)
#define YMASK           (63)

// We use the extra lines as buffer space...
uint8_t c[YSIZE][XSIZE_UINT8] ;

#define MODX(x)         (((x)+XSIZE)&XMASK)
#define MODY(y)         (((y)+YSIZE)&YMASK)

#define ISSET(a, x) \
        (a[MODX(x)>>3] & (0x1<<(MODX(x)&7)))

#define SET(a, x) \
        a[MODX(x)>>3] |= (0x1<<(MODX(x)&7))

#define SETXY(a, x, y) \
        a[y][x>>3] |= (0x1<<((x)&7))

#define CLR(a, x) \
        a[MODX(x)>>3] &= ~(0x1<<(MODX(x)&7))


void
initrandom()
{
    int i, j ;
    for (j=0; j<YSIZE; j++)
        for (i=0; i<XSIZE_UINT8; i++)
            c[j][i] = random(256) ;
}

void
initgun()
{
    memset(c, 0, sizeof(c)) ;
    SETXY(c, 70, 28); SETXY(c, 68, 29); SETXY(c, 70, 29); SETXY(c, 58, 30);
    SETXY(c, 59, 30); SETXY(c, 66, 30); SETXY(c, 67, 30); SETXY(c, 80, 30);
    SETXY(c, 81, 30); SETXY(c, 57, 31); SETXY(c, 61, 31); SETXY(c, 66, 31);
    SETXY(c, 67, 31); SETXY(c, 80, 31); SETXY(c, 81, 31); SETXY(c, 46, 32);
    SETXY(c, 47, 32); SETXY(c, 56, 32); SETXY(c, 62, 32); SETXY(c, 66, 32);
    SETXY(c, 67, 32); SETXY(c, 46, 33); SETXY(c, 47, 33); SETXY(c, 56, 33);
    SETXY(c, 60, 33); SETXY(c, 62, 33); SETXY(c, 63, 33); SETXY(c, 68, 33);
    SETXY(c, 70, 33); SETXY(c, 56, 34); SETXY(c, 62, 34); SETXY(c, 70, 34);
    SETXY(c, 57, 35); SETXY(c, 61, 35); SETXY(c, 58, 36); SETXY(c, 59, 36);
}

void 
setup()
{
#ifdef TIME_ME
    Serial.begin(9600) ;
#endif

    randomSeed(analogRead(A0));
    initgun() ;
}

void
draw()
{
    u8g.setDefaultBackgroundColor() ;
    u8g.drawBox(0, 0, 128, 64) ;
    u8g.setDefaultForegroundColor() ;
    u8g.drawXBM(0, 0, XSIZE, YSIZE, (const uint8_t *) c) ;
}

void 
line(const uint8_t *a, const uint8_t *b, const uint8_t *c,
        uint8_t *out)
{
    int i ;
    for (i=0; i<XSIZE; i++) {
        int sum = 0 ;
        if (ISSET(a, i-1)) sum ++ ;
        if (ISSET(a, i  )) sum ++ ;
        if (ISSET(a, i+1)) sum ++ ;
        if (ISSET(b, i-1)) sum ++ ;
        if (ISSET(b, i+1)) sum ++ ;
        if (ISSET(c, i-1)) sum ++ ;
        if (ISSET(c, i  )) sum ++ ;
        if (ISSET(c, i+1)) sum ++ ;

        if (ISSET(b, i)) {
            if (sum == 2 || sum == 3)
                SET(out, i) ;
            else
                CLR(out, i) ;
        } else {
            if (sum == 3)
                SET(out, i) ;
            else
                CLR(out, i) ;
        }
    }
}

void
loop()
{
    int j, op ;
    uint8_t obuf[2][XSIZE_UINT8] ;
    uint8_t otmp[XSIZE_UINT8] ;

#ifdef TIME_ME
    unsigned long time_start, time_stop ;

    time_start = millis() ;
#endif

    line(c[YSIZE-1], c[0], c[1], obuf[0]) ;
    line(c[0], c[1], c[2],       obuf[1]) ;
    op = 0 ;

    // We need this for the wrap around.

    memcpy(otmp, c[0], XSIZE_UINT8) ;

    for (j=2; j<YSIZE-1; j++) {
        memcpy(c[j-2], obuf[op], XSIZE_UINT8) ;
        line(c[MODY(j-1)], c[j], c[MODY(j+1)], obuf[op]) ;
        op = 1 - op ;
    }

    // We still have three lines we need to copy back...
    // And one more buffer to compute with line()

    memcpy(c[YSIZE-3], obuf[op], XSIZE_UINT8) ;
    line(c[YSIZE-2], c[YSIZE-1], otmp, obuf[op]) ;
    op = 1 - op ;
    memcpy(c[YSIZE-2], obuf[op], XSIZE_UINT8) ;
    op = 1 - op ;
    memcpy(c[YSIZE-1], obuf[op], XSIZE_UINT8) ;
    
    // compute one generation..

    u8g.firstPage() ;
    do {
        draw() ;
    } while (u8g.nextPage()) ;

#ifdef TIME_ME
    time_stop = millis() ;

    Serial.print(time_stop - time_start) ;
    Serial.println() ;
#endif
}