Binary Clock, Part 2

Atoms, Electrons, Photons
[vc_row][vc_column][vc_column_text]

The long awaited part 2 of this blog post has finally arrived! Though I’ve been tinkering on this project for the past two years, I decided to write it up to coincide with the outrageous arrest of 14 year old tinkerer Ahmed Mohamed who was hand cuffed because his teacher thought his electronic clock project looked like a bomb. This binary clock project of mine ended up being a personal electronic circuit design introduction course. You can download all the relevant files here if you want to make your own.<br />I forgot what the original inspiration was but what I’m ending up with is a working binary clock on a custom printed circuit board. Ultimately, this project will involve fiber optics inside polished cement for a unique time piece but we’re not there yet… Here’s what I learned so far:

Bit shifters

A binary clock needs 20 individual blinky things and the arduino has less than that, so I needed to figure out a way to create more individually addressable outputs. The solution I found is a the 74HC595N chip that can turn two inputs into 8. In fact, you can wire them in series and they can provide you with any number of outputs in multiples of 8. I decided to use one to drive the hours display, one for the minutes, and one for the seconds.

There are tons of tutorials for them so it was fairly straight forward to get it working.

Keeping time

While you can make a timer with just an arduino, it is not very accurate and it has no way to keep the clock going if you unplug the power. I used a rtc1307 chip which is designed for just this purpose. It keeps time accurately and uses a small battery to continue keeping time when the power is disconnected.

Again, there is an arduino library available and a good amount of tutorials out there so it wasn’t too hard to test it and incorporate it in the build.

Removing the arduino

Eventually, since I wanted to end up with a single circuit board, I didn’t want to have to plug anyting into an arduino. Once again, the internet is a wonderful resource which allowed me to figure out how put only the arduino components I needed onto a bread board. I can upload the code onto the chip by putting it on an arduino, and then pull it off of there to mount it directly onto the breadboard.

Code

The clock can be set to one of 4 modes: display time, set hours, set minutes or set seconds. There are two buttons. One button toggles between all the different modes, while the other increments the count of the hours, minutes, and seconds when they are in their respective mode.

#include <Time.h>
#include <Wire.h>
#include <DS1307RTC.h>

//Pin connected to ST_CP of 74HC595
const int latchPin = 8;
//Pin connected to SH_CP of 74HC595
const int clockPin = 12;
////Pin connected to DS of 74HC595
const int dataPin = 11;

//Pins for setting the time
const int button0Pin = 5;
const int button1Pin = 6;

// Variables for debounce
int button0State;
int button1State;
int previousButton0State = LOW;
int previousButton1State = LOW;

long lastDebounce0Time = 0; // the last time the output pin was toggled
long lastDebounce1Time = 0; // the last time the output pin was toggled
long debounceDelay = 50; // the debounce time; increase if the output flickers
int button0Mode = 0;

// object to communicate with RTC
tmElements_t tm;

void setup() {
// Setup pins for the shift register
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);

// Setup pins for manual time setting
pinMode(button0Pin, INPUT);
pinMode(button1Pin, INPUT);
}

void loop() {

RTC.read(tm);
// button 0 can be in display time, hour set, minute set, and second set modes.
//
if( button0Mode == 0){
timeDisplayMode();
}
else if( button0Mode == 1){
setHourMode();
}
else if( button0Mode == 2){
setMinuteMode();
}
else if( button0Mode == 3){
setSecondMode();
}

//
// mode switching
//
// Just after a button is pushed or released, there is noise where the value returned is 0/1 random.
// debouncing consists of reading the incomming value and, if it has changed, storing the time the change was noticed.
// It then continues to check and if after a certain amount of time (delay), it reads the input value and it is still the changed value
// noticed before, then it means that an actual state change happened.
int reading0 = digitalRead(button0Pin);
// this only happens when the input value is different from the last time the value was read
if (reading0 != previousButton0State) {
lastDebounce0Time = millis();
}
// we only enter this loop if the returned value hasn’t changed in a while, which means we are not in the noisy transition
if ((millis() – lastDebounce0Time) > debounceDelay) {
// if we are in here, it means we got two similar readings.
if (reading0 != button0State) {
// if the two similar readings we got are different from the stored state, we must have changed
button0State = reading0;
if(reading0 == HIGH){
button0Mode = (button0Mode+1)%4;
}
}
}
previousButton0State = reading0;

delay(100);

}

void timeDisplayMode(){
int sc = tm.Second;
int mn = tm.Minute;
int hr = tm.Hour;
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(sc,3));
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(mn,3));
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(hr,2));
digitalWrite(latchPin, HIGH);
}

void setHourMode(){
int reading1 = digitalRead(button1Pin);
if (reading1 != previousButton1State) {
// reset the debouncing timer
lastDebounce1Time = millis();
}
if ((millis() – lastDebounce1Time) > debounceDelay) {
if (reading1 != button1State) {
button1State = reading1;
if(reading1 == HIGH){
tm.Hour = (tm.Hour+1)%24;
RTC.write(tm);
}
}
}
previousButton1State = reading1;

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(0,3));
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(0,3));
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(tm.Hour,2));
digitalWrite(latchPin, HIGH);
}

void setMinuteMode(){
int reading1 = digitalRead(button1Pin);
if (reading1 != previousButton1State) {
// reset the debouncing timer
lastDebounce1Time = millis();
}
if ((millis() – lastDebounce1Time) > debounceDelay) {
if (reading1 != button1State) {
button1State = reading1;
if(reading1 == HIGH){
tm.Minute = (tm.Minute+1)%60;
RTC.write(tm);
}
}
}
previousButton1State = reading1;

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(0,3));
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(tm.Minute,3));
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(0,2));
digitalWrite(latchPin, HIGH);
}

void setSecondMode(){
int reading1 = digitalRead(button1Pin);
if (reading1 != previousButton1State) {
// reset the debouncing timer
lastDebounce1Time = millis();
}
if ((millis() – lastDebounce1Time) > debounceDelay) {
if (reading1 != button1State) {
button1State = reading1;
if(reading1 == HIGH){
tm.Second = (tm.Second+1)%60;
RTC.write(tm);
}
}
}
previousButton1State = reading1;

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(tm.Second,3));
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(0,3));
shiftOut(dataPin, clockPin, MSBFIRST, setTimeBits(0,2));
digitalWrite(latchPin, HIGH);
}

// create binary value for each digit of the hour/minute/second number
// offset represents how many bits are used for the tens.
int setTimeBits(int n, int offset){
int n1 = n%10;
int n0 = (n-n1)/10;
return n0 | n1<<offset;
}

Schematic and board

Once the breadboard was working, I set out to sketch the circuit in Fritzing. While it’s not quite as intimidating as EAGLE cad, I ended up using the latter after running into some limitations with the former (I don’t remember what they were). There was a lot for me to learn there but, in the end, it’s conceptually pretty simple: all the pieces have to be connected together correctly. It’s just another way to represent the circuit. Once that was done, I started with the board. I laid out all the components and let the software automatically figure out how to create the correct traces.
One cool thing is that if you choose the correct electronic components in the software, all the size and shapes are properly represented when you are designing the board. It’s a huge pain in the ass to sort through all the libraries of components, though, specially when you don’t know what all the specs mean.

 

The eagle cad files are included in the download file at the top of this page.

Manufacturing the board

Super simple: just go online and find a service that will manufacture them. For this project, I used oshpark.com and dirtypcbs.com, which allow you to upload your designs right out of EAGLE cad. After a few weeks, you get your board in the mail, ready for you to solder the components on. I order my components from mouser.com, which allows you to save a collection of various components into a project specific list. Again, finding the right components amongst the tens of thousands they have available is really time consuming and annoying. But now, I have my parts list so I never have to go through that again if I want to solder up new versions of the board. The list of parts in included in the download file at the top of this page.

The ugly truth

If you were paying attention, you no doubt noticed in the preceding paragraph that I used two board manufacturers. That is because the first board layouts I had printed actually had shorts. I suppose it’s probably not that uncommon, but it’s really frustrating to upload your designs, order the boards, wait for them to be delivered, spend all this time soldering the board to find out it doesn’t work, and then it can be challenging to figure out where the wires are getting crossed. In the end, I spent about $150 on boards and parts that ended up not working. I guess that’s the cost of learning… My first two board designs were ordered through Oshpark, and the minimum order was 3, for about $50. The third order was done on dirtypcbs and was $25 for 10 boards. They feel cheaper and took forever to get delivered but you sure can’t beat the price.

 

From broken laptop to cool utility monitor

Art, Atoms, Electrons

Broken things are solutions looking for problems. They hold magic undiscovered potential. I hate throwing away broken things because I’m never sure I won’t be able to later re-purpose some part of them into something else. On a seemingly unrelated note, I’m often in need of plugging some random computer or video device in to a screen for testing and, up until now, I have had to swap cables on my desktop monitors or pull out some bulky crappy old monitor from the garage and find some temporary place for it while I use it. What I really want is a small lightweight monitor I can easily access and occasionally plug a Raspberry Pi or my linux server into, something that’s closer in size to a laptop screen than a big bulky desktop monitor and that will not take up desk space.

Cue in broken hardware

As luck would have it, my stupid cat likes to sleep on laptops. They provide a nice warm place and a soothing fan sound. Usually, the only repercussion of this fact is that I sometimes wake up in the morning with hair on the keyboard and the Google helpfully informing me that
thanks_google
In one particular instance however, I was using a hackintoshed Asus 1201N that was clearly not designed to support the weight of a house cat; I know because it never woke up from its night of feline suffocation. I took it apart to see what I could find but never did find the needle in that haystack; probably a shorted motherboard… In the closet of broken stuff it went.

A killer and his victim

Displays

I’m always thinking about creating different displays. I’m pretty bored with the standard monitor design and I’m always interested in challenges to the rectangle-in-a-sleek-shiny-enclosure status-quo which led me to learn that I could probably find hardware compatible with my broken laptop’s LCD panel. I took the laptop apart, got the part number of the LCD panel and eventually found the right board on ebay for about $30 (I searched for “HSD121PHW1 controller”, HSD121PHW1 being the panel’s model number). Two weeks later, the padded envelope with the telltale Chinese characters on it showed up in my mail mailbox and I immediately plugged it in to test it. Lo and behold, it worked!

From parts to a thing

Okay, so now, I actually have the parts I need to make that small monitor I’ve been wanting: a small LCD panel and a driver board I can plug any HDMI or DVI display into. The next step is putting it together in a cool, cheap and convenient way. I had been peripherally interested by Plexiglas for another project so I decided to try it as a mounting surface for this. I measured the width of the screen and driver board, as well as the height of the screen with and without the adjustment buttons, and got a couple pieces cut at my local plastic store (Santa Monica Plastics). I drilled some holes, mounted the various parts on the back using standoffs, and sandwiched them by screwing in the smaller piece of plexi in the front using longer standoffs.

^[strange]+6\s[regx]+5\s[obseion]+9$

Electrons

I find regular expressions obtuse, maddening, and at the same time, strangely compelling. For those who don’t know, “regular expressions” (usually referred to as “regex”) is a common way for most computer programs to search text for particular patterns of characters, like for example a date or an address. Regex gives you access to atoms that represent individual character (“a”, “6”, “#”, etc…) or general character types (digits, symbols, uppercase…), as well as a concise grammar defining how these characters appear. Regex is a set of small blocks with specific rules that define how to put all these blocks together to represent something bigger.

A quick example

How to match a date like 2014/12/24?
Digits are represented like this: \d so the following regex will represent any character from 0 to 9:
\d
What if you are looking for a series of 4 digits? You could do this:
\d\d\d\d
But it can get a little heavy if you are looking for 200 digits, so you can use the following notation:
\d{4}
Okay, so now you have matched the first 4 digits, how about matching the “/” character? In this case, since it’s just a specific character, just go ahead and type it in:
\d{4}/
Then match 2 digits, another “/” and two more digits:
\d{4}/\d{2}/\d{2}

It’s a mental puzzle that requires you to visualize how patterns can be broken down into concise nuggets of representative abstraction.

Regulex

The following web site creates a diagram out of a regex expression to give you a useful visual graphic depiction of the logic.

Regexpress

This next one uses similar visuals but allows you to build the expression by stringing along icons that represent the patterns you are looking for.

Regviz

Here’s another one that doesn’t use fancy charts and graphics but it does allow you yo see the matches in real time with text you specify.

Dexter’s Head

Art, Electrons

Quick little test of photogrammetry software. Nothing revolutionary here… It’s simply something that I’ve been interested in for a long time and haven’t gotten around to working with. Until now, that is, since I have been capturing and modeling real environments for live projections.

From this:

19 still images.
contactSheet

to this:

Click and drag in the window to tumble the geometry.
[kento_3dmv width=”600″ height=”400″ source=”https://relentlessplay.com/wp-content/uploads/2015/01/dextershead/dextershead.obj” ]

Maya Frustum Visualizer

Electrons

Why Maya doesn’t offer this functionality out of the box is a mystery to me. I suspect there is actually a way to do it but it’s so buried I can’t find it. I needed it for a recent job so I’m putting it out there for public consumption. Anyway, it’s simple: just select a camera and run the script to visualize the frustum. It should update if you change the camera transforms, film back and FOV. Use it, change it, break it, fix it, improve it at your leisure…

 """A Maya Python script that builds frustum geometry based on the selected camera. Thomas Hollier 2015. hollierthomas@gmail.com""" 
import maya.cmds as cmds
import math, sys

#--------- Gather relevant camera attributes
import maya.cmds as cmds
import math, sys

#--------- Gather relevant camera attributes
camera = cmds.ls(selection=True)

if not cmds.objectType(camera, isType="camera"):
	print "ERROR: You need to select a camera."
	sys.exit(0)

focalLength = cmds.getAttr(camera[0]+".focalLength")
horizontalAperture = cmds.getAttr(camera[0]+".cameraAperture")[0][0]
verticalAperture = cmds.getAttr(camera[0]+".cameraAperture")[0][1]
nearClipping = cmds.getAttr(camera[0]+".nearClipPlane")
farClipping = cmds.getAttr(camera[0]+".farClipPlane")

print "---- Camera Attributes:\n\tfocal length: %s\n\thorizontal aperture: %s" % (focalLength, horizontalAperture)

#--------- compute FOV just for kicks, and to verify numbers match
adjacent = focalLength
opposite = horizontalAperture*.5*25.4

print "---- Right Triangle Values:\n\tadjacent: %s\n\topposite: %s" % (adjacent, opposite)

horizontalFOV = math.degrees(math.atan(opposite/adjacent))*2

print "\tcomputed horizontal FOV: %s" % (horizontalFOV)

#--------- calculate ratios
plane = horizontalAperture*25.4
nearScaleValue = nearClipping*plane/focalLength
farScaleValue = farClipping*plane/focalLength

print "---- Lens:\n\tprojection ratio: %s" % (plane/focalLength)

#--------- build geometry
myCube = cmds.polyCube(w=1, h=1, d=farClipping-nearClipping, sy=1, sx=1, sz=1, ax=[0,1,0], ch=1, name=camera[0].replace("Shape", "Frustrum"))
 
cmds.setAttr(myCube[0]+".translateZ", nearClipping+(farClipping-nearClipping)*.5)
cmds.makeIdentity(apply=True, t=1, r=1, s=1, n=0, pn=1);
cmds.setAttr(myCube[0]+".rotatePivotZ", 0)
cmds.setAttr(myCube[0]+".scalePivotZ", 0)
cmds.setAttr(myCube[0]+".rotateY", 180)

#--------- use expressions to update frustum geo as FOV and apertures are changed 
scaleX = "%s.scaleZ*%s.farClipPlane*%s.horizontalFilmAperture*25.4/%s.focalLength" % (myCube[0],camera[0],camera[0],camera[0])
scaleY = "%s.scaleZ*%s.farClipPlane*%s.verticalFilmAperture*25.4/%s.focalLength" % (myCube[0],camera[0],camera[0],camera[0])

cmds.move(0,0,0, myCube[0]+".f[2]", absolute=True)
cmds.scale(nearScaleValue, 0, 1, myCube[0]+".f[2]", pivot=[0,0,0])
cmds.expression(s="%s.scaleX = %s;%s.scaleY = %s;" % (myCube[0],scaleX,myCube[0],scaleY), n="%s_Expr" % myCube[0])
cmds.parent(myCube, camera, relative=True)


Self Illuminating Responsive Tintype Frame

Art, Atoms, Electrons

The area of a tintype image that gets exposed to the most light essentially becomes a pure silver coating. While the reflective properties of silver make the final image a bit darker than traditional photographic prints, it also gives it a really compelling silvery metallic reflective feel. As a way to highlight those unique qualities, I decided to experiment with hiding LEDs in a box frame to illuminate the image from inside. I also wanted to find a way to intensify the lighting and make the image come alive as viewers got close to it.

Framed!

I inherited some antique oak wood floor pieces and made quick work of it on the old chop saw.

I built some walls behind the front of the frame and attached some LED string lights on it.

I mounted an 8×10 tintype on a black backing board cut to fit at the bottom of the frame’s walls.

Lights OFF / Lights ON

Controls

The next step was to figure out the kind of sensor needed to make the lights come on as a viewer approached the frame. I figured the sensor required would need a range of at least a few meters and be able to return the specific distance to the closest obstructing object. I am not a distance sensor expert so I used the internet to help. In the end, I settled for a Maxsonar EZ0 LV. It’s cheap, it’s got a range of a few meters and it’s got both serial, analog and PWM outputs. I hooked it up to a teensy board and confirmed the sensor was returning appropriate distance values. On the lighting front, I was planning on controlling the brightness of the LED string with the teensy, but since the LED string requires a 12V supply and the teensy outputs only 5V, I used a MOSFET driven by the teensy’s output to modulate the LED’s power supply.

The electronically savvy amongst you will notice that I’m currently using 2 power plugs: a 12V one for the LED and a 5V USB supply for the teensy, which is stupid. I tried to use a voltage regulator to convert the 12V to a 5V supply but somehow it created much noisier reading on the distance sensor… I must have missed a capacitor somewhere. I will try using an Arduino Nano which can take a 12V supply directly.

Code

God, I love the internet!!! I read somewhere that the sensor’s PWM signal was much cleaner so I started with that but I eventually found out that it also is much much slower and wasn’t able to keep up with the rate of change I was looking for. In the end, I used the analog signal and tried to filter it as best I could.

/*
Using the Analog signal from a Maxsonar LV EZ0 to drive a 
12V LED strip through a MOSFET. 
Take 20 samples, sort and select the median value.
*/

const int anPin = 9;
int anVolt, cm;
const int samples = 20;
int ranges[samples];
int highestReading;

float time;
float previousIntensity;
float maxDist;
float minDist;
int minVal, maxVal;

int mosfetPin = 9;           // the pin that the MOSFET is attached to

void setup()  { 
  // declare pin 9 to be an output:
  pinMode(led, OUTPUT);
  Serial.begin(9600);
  time = 0;
  maxDist = 450;
  minDist = 10;
  minVal = 5;
  maxVal = 255;
  previousIntensity = 0;
} 

void loop(){
  // print time spent since last loop
  Serial.println(millis()-time);
  time = millis(); 
  
  // sample sensor value
  for(int i = 0; i &lt; samples ; i++)
  {
    anVolt = analogRead(anPin)/2;
    ranges[i] = anVolt;
    delayMicroseconds(50);
  }
  isort(ranges,samples);
  
  // convert to centimeters
  Serial.println(samples/2);
  cm = 2.54 * ranges[samples/2];

  // shape the curve of the result
  float intensity = (cm - minDist)/(maxDist - minDist);
  intensity = 1-constrain(intensity,0,1);
  intensity = intensity*intensity;
  intensity = previousIntensity*.995 + intensity*.005;
  previousIntensity = intensity;

  // set the brightness of pin 9:
  float val= intensity*(maxVal-minVal)+minVal;
  analogWrite(mosfetPin, val);
}

//Sorting function
void isort(int *a, int n){
// *a is an array pointer function
  for (int i = 1; i &lt; n; ++i)
  {
    int j = a[i];
    int k;
    for (k = i - 1; (k &gt;= 0) &amp;&amp; (j &lt; a[k]); k--)
    {
      a[k + 1] = a[k];
    }
    a[k + 1] = j;
  }
}

Next Steps

Once I get my paws on that Arduino Nano board, I can rework my circuit and get the soldering iron out to make my electronics more permanent. I have also ordered a HC-SR04 Distance Sensor to see how it compares to the Maxsonar in terms of accuracy, speed and noise. Also, I need to make the frame a little bit deeper so the light can spread out a bit further across the image.

Which one is cooler?

The ominous cyclopean look or the cute and friendly Wall-E look?

Creating a Spotify appliance from an old smartphone

Atoms, Electrons

A problem:

If you have a teenager, you will no doubt be familiar with the dilemma of letting them listen to music at night while keeping them somewhat sheltered from the irresistible pull of the bright rectangle of light that seem to so efficiently hijack their attention. In my day, it was simple: the tape deck played music and that’s all it did. These days, with smart phones, it feels like it’s all or nothing. The tape deck comes with a movie theater, a video game arcade, and a place to hang out with friends, and they’re all open and available 24 hours a day. So, like any concerned parent, we take the phone away at night but since now days, it’s the place they get their music, it means they can’t listen to music anymore which is kind of sad.

Something annoying:

You know what I hate? The fact that our consumer culture dictates that when something is broken, we throw it away and buy something new rather than fixing it. I had a smartphone and after two years, the internal phone speaker broke, which meant I had to have it on speaker or plugged into an earpiece to hear people talking to me. I actually attempted to fix the problem: I ordered the part on ebay and spend 30 minutes opening the phone but I failed. Mind you, the smartphone was still a computer with over 1000 times the speed and memory of my first computer, a nice bright and sharp touch screen, wifi, etc… Other than the speaker problem, it worked perfectly. In the end, though, since I couldn’t fix the problem, I reluctantly gave in and bought a new phone. Grrrrr!


Ready for the trash? Not so fast!

So, I made this:

It’s actually really simple. I tried to restrict the functionality of the phone and create an object designed with the single purpose of enjoying spotify without the potential distraction of the rest of the internet getting in the way. First off, with the zip card out, your smartphone becomes a small tablet.


I removed all the video games and Netflix and Youtubes and Hulus and Snapchats and Facebooks and Vines and Instagrams and circles and Pinterests and Ellos and MySpace. I kept only Spotify and installed a program called Autostart which automatically launches an app after the phone boots and prevents a user from quitting the app.


I then built a frame into which I could mount the phone and poured a trademark Hollier polished cement base. I used autostart to automatically launch Spotify to whenever the phone turns on and thus transformed a crappy old orphaned phone into a custom one-of-a-kind Spotify appliance. Being mounted in a frame and set into a solid base transforms it into something you set and walk away like a radio rather than something you interact and fiddle with like a phone.

How to get a 9 year old interested in programming

Electrons, Giggles

Osx has a nifty command line speech synthesizer called “say”. It allows you to type some text and hear it “spoken” by the ubiquitous synthesized robotic voice. First, open the terminal and show him how it works by typing:
say "hello, my name is Robert"
Then show him that you can type different sentences and let him play with that until he gets the hang of it. Make sure to include enough potty humor to ensure sufficient hilarity:
say "Even though I am a computer, I sometimes talk about farts."
That should get his attention. You can also include question marks and nonsensical words for extra extra fun:
say "Are you some kind of flarpy nunckenbarf?"
The next step is to introduce the concept of variables:
friend="John"
say "$friend is a complete idiot"
friend="My hamburger"
say "$friend is a complete idiot"

By this time, tears of laughter should be streaming down his face, but don’t let it stop you. This is where the comedic potential really starts paying off with the introduction of the “for” loop.
for friend in "alfred" "max"
do
say "$friend is a fizzlebutt"
done

And then show him how to pause between the sentences
for friend in "alfred" "max"
do
say "$friend is a fizzlebutt"
sleep 1
done

Finally, once he finally peels himself off the floor and catches his breath, you apply the final coup-de-grace with the help of the “if” statement:
for friend in "alfred" "max" "frank" "dave"
do
if [ "$friend" != "max" ]

then
say "$friend is a total moron"
sleep 1
else
say "$friend has bad breath"
sleep 1
fi
done

After you share these intoxicatingly powerful instruments of distraction with your son, I suggest you steer clear of the technology teacher at the school.