a digital scan of a 35mm film image of a processing sketch running on an LCD
Skip to Content

Blender != CAD

Assignment: Spend a handful of hours evaluating your assigned 3D modeling software (Blender)

Blender doesn’t seem like a reasonable substitute for Alibre or Solidworks… I’m not even sure CAD suites are on the radar of competing products for the Blender Dev team. Instead, they’re positioning Blender as an alternative to Maya or 3Ds Max, and extending functionality into game design while basic CAD functionality (measurements, assemblies, alignment) is neglected.

Still, it represents a huge amount of open-source work towards the basic infrastructure required by both 3D Modeling / Animation tools and 3D Drafting / Design tools. Several efforts have been made to implement CAD functionality within Blender, but none of them seem mature enough for production work (or even sustained dabbling).

Of all the small side-projects and forks attempting to take Blender in a more CAD-like direction, the only one with any enduring momentum seems to be BlenderCAD — which proved too unstable for completing even basic work.

This pretty much describes the experience:

I wish the developers luck, though, since the lack of serious open-source CAD tools is a shame. Something Blender-based is probably the last great hope.

February 11 2010 at 2 PM

And Computer

Artist and Computer

Artist and Computer, some thoughts from 1976.

Via Kevin O’Neill.

February 8 2010 at 11 PM

Interstitial Wasteland

Homework #1. Create a program that behaves like a UNIX text processing program (such as cat, grep, tr, etc.). Your program should take text as input (any text, or a particular text of your choosing) and output a version of the text that has been filtered and/or munged. Be creative, insightful, or intentionally banal.

Choose one text that you created with your program to read in class.

Bonus: Use the program that you created in tandem with another UNIX command line utility.

I try to avoid destroying data. I draw upon the usual set of justifications: Storage is only getting cheaper, an empty HD occupies the same physical space as a full HD, yadda yadda.

Whether or not this policy is for the best, it’s left me with over a million lines of instant messenger conversation logs from my earlier years — mundane, personal conversations between myself and a handful of friends. (Running from about Jr. High to end end of High School.) If not of much general interest, the contents of these (often painfully angst-ridden) logs are a personally surreal thing to revisit.

In response to the first assignment, I wanted to draw from this well of text in some way. I’m particularly interested in the idea of accidental art — the daily, unintended collisions with things that might be formally construed as art.

I wrote a quick algorithm to put my variation of the Infinite monkey theorem to the test. Can enough angsty teens, given enough time to type, eventually produce something resembling a traditionally recognized “great work”?

I decided to pit my adolescent conversations against T.S. Eliot’s The Waste Land. I wasn’t interested in simply recreating the poem verbatim, instead I used the first and last words of each line as anchors between the poem and my logs, and anything in the middle would be filled in from my conversations based on nearby words.

So, the algorithm takes the first and last word from each line of the poem, and then looks for matches in my conversation logs. If it finds a match for both words in my logs, then it will walk forward from the first word, and backward from the last word, to create a line of text which starts and ends identically to a line in The Waste Land.

Finally, if the resulting line is too long, it will cut words out of the middle until the length of the output line matches the length of the line in The Waste Land. Currently, lines that are shorter than their equivalents in the original poem are just printed as-is. (It would be nice to find a reasonable way to beef these up to size.)

When matches aren’t found, the line is dropped. Only about 60% of the poem could be reconstructed from my million lines of conversation text. (e.g., words like “abolie” never turned up in my logs, and therefore were not available to reconstruct that line of The Waste Land.)

The code is happy to work with any plain text files. Supply it with a model text (in this case, Eliot’s poem) and a source text (in this case, my conversation logs), and it will do its best to shape the source text into the model text.

The first argument to the program is the source text, and the second is the model text. For example, from the command line, this would use aim.txt as the source and wasteland.txt as the model, and save the results to a text file named aim-wasteland.txt:
python interstitial-wasteland.py aim.txt wasteland.txt >> aim-wasteland.txt

It takes a while to run, and you won’t get decent results unless the source text is huge.

Here’s the full output: interstitial-wasteland-output.txt

And the output with the original poem in parallel: interstitial-wasteland-output-parallel.txt

A small excerpt of the raw output:

I… wasn’t going us, he’s DEAD!

April one is designed for inbreeding,
Memory Lane and stirring
Winter Olympic Games. of recovering
Earth, yet we and feeding
And let me tell for like an hour..by
Individuals can be dangerous though
I”m confused http://winter.squaw.com/html/squawcambig.html

-What are the current problems problems?- it grows
Out man,

The same excerpt in parallel with the model text:

I. THE BURIAL OF THE DEAD
I… wasn’t going us, he’s DEAD!

April is the cruellest month, breeding
April one is designed for inbreeding,

Memory and desire, stirring
Memory Lane and stirring

Winter kept us warm, covering
Winter Olympic Games. of recovering

Earth in forgetful snow, feeding
Earth, yet we and feeding

And drank coffee, and talked for an hour.
And let me tell for like an hour..by

In the mountains, there you feel free.
Individuals can be dangerous though

I read, much of the night, and go south in the winter.
I”m confused http://winter.squaw.com/html/squawcambig.html

What are the roots that clutch, what branches grow
-What are the current problems problems?- it grows

Out of this stony rubbish? Son of man,
Out man,

And the source code:

  1. import sys
  2. args = sys.argv
  3.  
  4. # I hard coded these for my local testing.
  5. #args = [‘self’, ‘aim.txt’, ‘wasteland.txt’]
  6.  
  7. # Set to true if you want extra output for debugging.
  8. # TK turn this into a command line parameters.
  9. verbose = 0
  10.  
  11. # Set to true if you want to show the original line above the munged one.
  12. # TK turn this into a command line parameters.
  13. print_original = 0
  14.  
  15. if verbose: print args
  16. if verbose: print ‘Take the text from ‘ + args[1] + ’ and model it after ‘ + args[2]
  17.  
  18. # Pull the filenames from stdin.
  19. source_file_name = args[1]
  20. model_file_name = args[2]
  21.  
  22. # Open each file. (Error handling would be good here…)
  23. source_file = open(source_file_name, ‘r’)
  24. model_file = open(model_file_name, ‘r’)
  25.  
  26. # Read each line of each file into a list.
  27. source_lines = source_file.readlines()
  28. model_lines = model_file.readlines()
  29.  
  30.  
  31. # Removes usernames from the start of a line, e.g. removes "OBRIGADO:"
  32. def anonymize(line):
  33.   if ’:’ in line:
  34.     colon_index = line.index(’:’) + 1
  35.     anonymous_line = line[colon_index:len(line)]
  36.     return anonymous_line.strip()
  37.    
  38.   return line
  39.  
  40. # Clean up line breaks.
  41. def remove_breaks(line):
  42.   line = line.replace(\n,)
  43.   line = line.replace(\r,)
  44.   return line
  45.  
  46.  
  47. # Gives index of element containing word.
  48. # Less strict than .index(string) since it finds partial matches.
  49. def word_at(string, list):
  50.   index = 0
  51.   for item in list:
  52.     if string in item:
  53.       return index
  54.       break
  55.     index += 1
  56.        
  57.   return -1
  58.  
  59.  
  60. # Go through the model and look for matches to the first and last words.
  61. index = 0
  62. for line in model_lines:
  63.   # Make sure it’s not a blank line.
  64.   line = line.strip()
  65.  
  66.   # Put in line breaks if it is blank.
  67.   if len(line) == 0:
  68.     print
  69.  
  70.   # Otherwise, start processing.
  71.   if len(line) > 1:
  72.     # Place each word in a list.
  73.     line_list = line.split(’ ‘)
  74.     first_word = line_list[0];
  75.     last_word = line_list[-1];
  76.    
  77.     if verbose: print ‘––––––––––––’    
  78.     if verbose: print ‘Line ‘ + str(index) + ’ starts with "’ + first_word + ‘" ends with "’ + last_word + ‘"’
  79.  
  80.     # Find first instance of first word in source file.
  81.     for first_word_line in source_lines:
  82.       if first_word in first_word_line:
  83.        
  84.         # We found the starting word, now find the ending word.
  85.         for last_word_line in source_lines:
  86.           if last_word in last_word_line:
  87.            
  88.             # We have both a starting and ending word match!
  89.            
  90.             # Clean up, remove line breaks and attribution.
  91.             # TK problem if match was in name?
  92.             first_word_line = anonymize(remove_breaks(first_word_line))
  93.             last_word_line = anonymize(remove_breaks(last_word_line))      
  94.            
  95.             # For the first line, save from the word forward.
  96.             first_line_list = first_word_line.split(’ ‘)
  97.             first_word_index = word_at(first_word, first_line_list)
  98.             first_line_list = first_line_list[first_word_index:len(first_line_list)]
  99.            
  100.             # For the last line, save from the word backward.
  101.             last_line_list = last_word_line.split(’ ‘)            
  102.             last_word_index = word_at(last_word, last_line_list)
  103.             last_line_list = last_line_list[0:last_word_index + 1]
  104.            
  105.             # TK remove blank stuff.
  106.             complete_line_list = first_line_list + last_line_list
  107.             if verbose: print complete_line_list
  108.            
  109.             # Construct a sentence as close to the original length as possible.
  110.             model_line_length = len(line_list);
  111.            
  112.             # remove words until we have the desired length.
  113.             # TK single word line problems?
  114.             while len(complete_line_list) > model_line_length:
  115.               # Pop from the middle.
  116.               complete_line_list.pop(int(len(complete_line_list) / 2))
  117.            
  118.             complete_line = ’ ‘.join(complete_line_list)
  119.            
  120.             # Print the original above the munged line.
  121.             if print_original: print line
  122.            
  123.             print complete_line
  124.            
  125.             # Print add some line breaks for readability.
  126.             if print_original: print
  127.            
  128.             break
  129.        
  130.         break
  131.        
  132.   index += 1

February 5 2010 at 3 PM

Egg Dissolve

The mechanisms class was charged with creating egg-cracking Rube Goldberg machines for the first week's assignment. Rather than breaking the egg shell through brute force, we tossed around the idea of dissolving the egg shell entirely.

Luckily there was some muriatic acid lying around the shop, so we ran a quick experiment. The shell dissolves, but it takes about 20 minutes, and what's left isn't a broken egg, but a layer of internal membrane which keeps the de-shelled egg intact. Interesting, but not well suited to the purposes of our machine-to-be, so we shelved the idea. (Maybe it was too clever by half to begin with.)

(Science music by Boards of Canada.)

January 28 2010 at 12 PM

Triangle Soup

A quick assignment for GLART, drawing an animated field of triangles.

Here’s the code:

  1.         package mika;
  2.         import java.awt.event.MouseEvent;
  3.         import javax.media.opengl.*;
  4.         import jocode.*;
  5.  
  6.         /**
  7.          * DemoBasicGeometry.java
  8.          * <P>
  9.          * Demonstrate six types of geometry using glBegin()…glEnd()
  10.          * <P>
  11.          * napier at potatoland dot org
  12.          */
  13.  
  14.         public class Week1 extends JOApp {
  15.  
  16.           // Set the mouse position in a way that’s
  17.           // useful for translating objects at 0 Z
  18.           public float screenCursorX;
  19.           public float screenCursorY;
  20.           public float tempValue;
  21.           public float sizeMult;
  22.  
  23.           /**
  24.            * Start the application, Run() initializes the OpenGL context, calls setup(),
  25.            * handles mouse and keyboard input, and calls draw() in a loop.
  26.            */
  27.           public static void main(String args[]) {
  28.             // create the app
  29.             Week1 demo = new Week1();
  30.  
  31.             // set title, window size
  32.             windowTitle = "Hello World";
  33.             displayWidth = 1440;
  34.             displayHeight = 900;
  35.  
  36.             // start running: will call init(), setup(), draw(), mouse functions
  37.             demo.run();
  38.           }
  39.  
  40.           /**
  41.            * Initialize settings. Will be called once when app starts. Called by
  42.            * JOApp.init().
  43.            */
  44.           @Override
  45.           public void setup() {
  46.             // set a background color
  47.             gl.glClearColor(0f, 0f, 0f, 1f);
  48.  
  49.             // Move over to my second monitor for testing…
  50.             // TODO Disable this IRL
  51.             frame.setLocation(-1440, 150);
  52.           }
  53.  
  54.           /**
  55.            * Render one frame. Called by the JOApp.display() callback function.
  56.            */
  57.           @Override
  58.           public void draw() {
  59.  
  60.             // Clear screen and depth buffer
  61.             gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
  62.  
  63.             // Select The Modelview Matrix (controls model orientation)
  64.             gl.glMatrixMode(GL.GL_MODELVIEW);
  65.  
  66.             gl.glEnable(GL.GL_BLEND);
  67.             gl.glBlendFunc(GL.GL_SRC0_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
  68.             // gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE);
  69.  
  70.             // Reset the Modelview matrix
  71.             // this resets the coordinate system to center of screen
  72.             gl.glLoadIdentity();
  73.  
  74.             // Where is the ‘eye’
  75.             glu.gluLookAt(0f, 0f, 10f, // eye position
  76.                 6f, 1f, 0f, // target to look at
  77.                 0f, 1f, 0f); // which way is up
  78.  
  79.             gl.glColor4f(1f, 1f, 1f, 0.5f);
  80.             // gl.glColor3f(1f, 1f, 1f); // color will affect all the following verts
  81.  
  82.             sizeMult = 3;
  83.             tempValue = (tempValue + .02f) % 1000;
  84.  
  85.             for (int i = 0; i < 200; i++) {
  86.               gl.glRotatef(tempValue, tempValue, tempValue, tempValue);
  87.  
  88.               gl.glTranslatef(.1f, .1f, .1f);
  89.  
  90.               gl.glBegin(GL.GL_TRIANGLES);
  91.               {
  92.                 gl.glVertex3f(0f, 0f, 0f); // top
  93.                 gl.glVertex3f(-0.5f * sizeMult, -1f * sizeMult, 0f); // lower left
  94.                 gl.glVertex3f(0.5f * sizeMult, -1f * sizeMult, 0f); // lower right
  95.               }
  96.               gl.glEnd();
  97.             }
  98.  
  99.             // reset vertex color to white
  100.             gl.glColor3f(1f, 1f, 1f);
  101.           }
  102.  
  103.           @Override
  104.           public void mouseMoved(MouseEvent _event) {
  105.             // Call the parent method since it actually gives us the
  106.             // Better just to copy the whole method?
  107.             super.mouseMoved(_event);
  108.             screenCursorX = cursorX / (displayWidth / 10f);
  109.             screenCursorY = cursorY / (displayHeight / 10f) - 3f;
  110.           }
  111.  
  112.         }
January 27 2010 at 3 PM

Window Drop

Video: Susan Prentiss

I spent an unreasonable portion of my childhood in the back seat of a car, staring out the window. Rainy days, in particular, allowed for one of the more confounding means of passing the time: attempting to predict and understand the movements of water drops on the window.


Attachments

January 26 2010 at 11 AM

Market Penetration

The 4 Big Myths of Profile Pictures: a straight-faced analysis of how profile pictures of various types correlate with "success" on a dating website. The text's self-help tone is a bit cheeky, but the conclusions are based on several thousand profiles worth of data, and the charts and graphs are pure gold:



January 24 2010 at 10 PM

Buzz Pot: A Variable Detent Potentiometer

In the course of developing Brain Radio with Arturo and Sofy, we saw the need for a means of tuning between an arbitrary, and potentially unstable number of channels. For the sake of context, Brain Radio is a head-mounted EEG-based broadcast system — everyone with a headset can tune into anyone else with a headset, and listen to sounds synthesized by that person's brain waves.

How, exactly, would the tuning process take place? We knew a few things:

  • We wanted to use a dial to leverage associations with radio / tuning / broadcast / analog.
  • We would need some kind of tactile feedback, since the dial would be mounted on the headset, outside of the wearer's field of view.
  • The number of available stations / channels would vary — if three people were in range, the dial would need to be able to tune to three positions. If more channels came online or dropped out, the interface would have to adapt.

What we really wanted was a potentiometer with detents, to make it easy to click-click-click from station to station. Detents also summon a tactile delight rivaled only by toggle switches and large mechanical levers. They inflate the sense of intention associated with an action: They make you feel like you know what you're doing.

The catch, of course, is that detented potentiometers have a finite number of detents, and they're set at the factory. If we bought five-detent pots and ended up with six radio channels to tune between, we were SOL. What we needed was a variable detent potentiometer.

Google turned up some shady, product-less patent filings. And the PComp list confirmed that no such device existed — and then suggested something very savvy: use something else to generate the tactile feedback. How about a vibrator motor...

So I did exactly that. A vibrator motor, some hot glue, a potentiometer, and some code all collided to create the buzz pot:

The video doesn't really communicate the physical feedback coming through the pot when channel thresholds are crossed, but in practice it works pretty well. The number of channels is easily changed in software — and the resistance range of the potentiometer is simply divided up so a given range of values represent a single channel.

For example, a three channel setting would put channel 1 between analog values 0 and 341, channel 2 between 341 and 682, and then channel 3 between 682 and 1023. When the pot passes from one channel range to the next, the microcontroller flips on the transistor controlling power to the vibrator motor for a fraction of a second, sending a mechanical buzz through the pot that lets the user feel when they've changed channels, even if they can't see what they're doing.

Development was relatively simple. Hot glue held up surprisingly well to the vibration motor.

Buzz pot motor mountedBuzz pot bottom view

The basic test configuration includes a seven-segment LED display so I could verify when the channels changed.

Buzz pot test configuration

Here's the schematic. The seven-segment display adds some complexity... it's really just for troubleshooting purposes (sending the channel status out over serial would be much simpler). Even then, a shift-register would allow for more sensible use of the Arduino's pins if this were more than a proof of concept. The TIP 120 between the Arduino and the vibration motor is definitely overkill, I just put it in place since Brain Radio was going to have discrete power sources and it would have made sense to put the motor on the non-Arduino power supply. (Some of the graphics in the schematic were adapted from the Fritzing project.)

Buzz Pot Schematic

And finally, the code:

  1. // Buzz Pot
  2. // Eric Mika, 2009
  3. // Provides tactile feedback for a potentiometer to denote changes
  4. // from one value range to another. Ideal for situations where an unknown
  5. // number of values must be set by a single potentiometer.
  6. // To do:
  7. // 1. Debounce the thresholds.
  8. // 2. Handle fringe-case runtime channel count changes.
  9.  
  10. int vibratorPin = 9; // Turns the vibration motor on and off through a transistor.
  11. int potPin = 0; // Reads the potentiometer.
  12. int potValue = 0; // Stores the potentiometer value.
  13.  
  14. int channels = 10; // Number of values selectable by the pot.
  15. int currentChannel = 0; // Starting value.
  16. int lastChannel = 0;
  17.  
  18. int vibDuration = 200; // How long to turn the motor on when thresholds are crossed.
  19. unsigned long vibStart = 0; // Keep track of time so we know when to turn off the motor.
  20.  
  21. // Map digital pins to their respective LEDs in the 7 segment display.
  22. // Could use a shift register instead to save pins.
  23. int dispA = 2;
  24. int dispB = 3;
  25. int dispC = 4;
  26. int dispD = 5;
  27. int dispE = 6;
  28. int dispF = 7;
  29. int dispG = 8;
  30.  
  31. void setup() {
  32.   // Set up 7 segment display pins.
  33.   pinMode(dispA, OUTPUT);
  34.   pinMode(dispB, OUTPUT);
  35.   pinMode(dispC, OUTPUT);
  36.   pinMode(dispD, OUTPUT);
  37.   pinMode(dispE, OUTPUT);
  38.   pinMode(dispF, OUTPUT);
  39.   pinMode(dispG, OUTPUT);    
  40.  
  41.   // Set up vibrator pin.
  42.   pinMode(vibratorPin, OUTPUT);  
  43. }
  44.  
  45. void loop() {
  46.   // Read the analog input into a variable, correct for value inversion.
  47.   potValue = 1023 - analogRead(potPin);
  48.  
  49.   // If 10 channels, return a number between 0 and 9...
  50.   // Constrain to catch rounding errors at the top end.
  51.   currentChannel = constrain(potValue / (1023 / channels), 0, channels - 1);
  52.  
  53.   // Show the current channel number on the 7 segment display.
  54.   displayDigit(currentChannel);
  55.  
  56.   // Vibrate if we change channels.
  57.   if (lastChannel != currentChannel) {
  58.     vibStart = millis();
  59.   }
  60.  
  61.   // Keep vibrating for the full duration...
  62.   if ((millis() - vibStart) <= vibDuration) {
  63.     digitalWrite(vibratorPin, HIGH);
  64.   }
  65.   else {
  66.      digitalWrite(vibratorPin, LOW);
  67.   }
  68.  
  69.   lastChannel = currentChannel;
  70. }
  71.  
  72. // Shows a number on the 7 segment display.
  73. // It's a common anode model, so LOW is actually on.
  74. void displayDigit(int digit) {
  75.   switch (digit) {
  76.   case 0:
  77.     digitalWrite(dispA, LOW);
  78.     digitalWrite(dispB, LOW);
  79.     digitalWrite(dispC, LOW);
  80.     digitalWrite(dispD, LOW);
  81.     digitalWrite(dispE, LOW);
  82.     digitalWrite(dispF, LOW);
  83.     digitalWrite(dispG, HIGH);      
  84.     break;
  85.   case 1:
  86.     digitalWrite(dispA, HIGH);
  87.     digitalWrite(dispB, LOW);
  88.     digitalWrite(dispC, LOW);
  89.     digitalWrite(dispD, HIGH);
  90.     digitalWrite(dispE, HIGH);
  91.     digitalWrite(dispF, HIGH);
  92.     digitalWrite(dispG, HIGH);
  93.     break;
  94.   case 2:
  95.     digitalWrite(dispA, LOW);
  96.     digitalWrite(dispB, LOW);
  97.     digitalWrite(dispC, HIGH);
  98.     digitalWrite(dispD, LOW);
  99.     digitalWrite(dispE, LOW);
  100.     digitalWrite(dispF, HIGH);
  101.     digitalWrite(dispG, LOW);
  102.     break;
  103.   case 3:
  104.     digitalWrite(dispA, LOW);
  105.     digitalWrite(dispB, LOW);
  106.     digitalWrite(dispC, LOW);
  107.     digitalWrite(dispD, LOW);
  108.     digitalWrite(dispE, HIGH);
  109.     digitalWrite(dispF, HIGH);
  110.     digitalWrite(dispG, LOW);
  111.     break;
  112.   case 4:
  113.     digitalWrite(dispA, HIGH);
  114.     digitalWrite(dispB, LOW);
  115.     digitalWrite(dispC, LOW);
  116.     digitalWrite(dispD, HIGH);
  117.     digitalWrite(dispE, HIGH);
  118.     digitalWrite(dispF, LOW);
  119.     digitalWrite(dispG, LOW);
  120.     break;
  121.   case 5:
  122.     digitalWrite(dispA, LOW);
  123.     digitalWrite(dispB, HIGH);
  124.     digitalWrite(dispC, LOW);
  125.     digitalWrite(dispD, LOW);
  126.     digitalWrite(dispE, HIGH);
  127.     digitalWrite(dispF, LOW);
  128.     digitalWrite(dispG, LOW);
  129.     break;
  130.   case 6:
  131.     digitalWrite(dispA, LOW);
  132.     digitalWrite(dispB, HIGH);
  133.     digitalWrite(dispC, LOW);
  134.     digitalWrite(dispD, LOW);
  135.     digitalWrite(dispE, LOW);
  136.     digitalWrite(dispF, LOW);
  137.     digitalWrite(dispG, LOW);
  138.     break;      
  139.   case 7:
  140.     digitalWrite(dispA, LOW);
  141.     digitalWrite(dispB, LOW);
  142.     digitalWrite(dispC, LOW);
  143.     digitalWrite(dispD, HIGH);
  144.     digitalWrite(dispE, HIGH);
  145.     digitalWrite(dispF, HIGH);
  146.     digitalWrite(dispG, HIGH);
  147.     break;
  148.   case 8:
  149.     digitalWrite(dispA, LOW);
  150.     digitalWrite(dispB, LOW);
  151.     digitalWrite(dispC, LOW);
  152.     digitalWrite(dispD, LOW);
  153.     digitalWrite(dispE, LOW);
  154.     digitalWrite(dispF, LOW);
  155.     digitalWrite(dispG, LOW);
  156.     break;
  157.   case 9:
  158.     digitalWrite(dispA, LOW);
  159.     digitalWrite(dispB, LOW);
  160.     digitalWrite(dispC, LOW);
  161.     digitalWrite(dispD, LOW);
  162.     digitalWrite(dispE, HIGH);
  163.     digitalWrite(dispF, LOW);
  164.     digitalWrite(dispG, LOW);
  165.     break;
  166.   default:      
  167.     digitalWrite(dispA, HIGH);
  168.     digitalWrite(dispB, HIGH);
  169.     digitalWrite(dispC, HIGH);
  170.     digitalWrite(dispD, HIGH);
  171.     digitalWrite(dispE, HIGH);
  172.     digitalWrite(dispF, HIGH);
  173.     digitalWrite(dispG, HIGH);
  174.   }
  175. }

December 29 2009 at 11 AM