Daily Archives: 3/24/2015

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.


[sourcecode lang=”cpp”]
#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()) ;
}
[/sourcecode]

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.


[sourcecode lang=”cpp”]
#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
}
[/sourcecode]