How to Solve Complex Problems

Yesterday I ran into a complex problem and spent quite some time on finding a possible solution. But no chance.

Today I thought about it again and wanted to write an e-mail to a colleague in order to request some support. While writing the problem down in the e-mail, out of nothing, I suddenly saw the reason of the problem and it was crystal clear.

It’s often like that. I think these things really help to understand and solve bigger problems:

  • Divide the problem into smaller pieces and approach the solution step by step.
  • If you can’t see the solution, take a piece of paper (or write in a text file, whatever you prefer) and describe the problem. Don’t assume anything and describe the whole thing from ground up. This will often help to better understand the problem and you’ll probably be able to solve it yourself.

Construction of a Wood Case for Arduino

After some first experiments with the Arduino platform and really liking it, I got the idea to construct my own little wood housing for an Arduino. It contains a 9V battery, Arduino Leonardo board, two push buttons, two LEDs, a 2×16 character LCD display with potentiometer for brightness regulation, as well as a power switch and an LED as power indicator.

This setup is good for some little fun programs, like lottery number generation, crystal ball fun, small games, etc.

The box is made of 4mm thick wood plates and measures about 11x11x4cm (length/width/height). That’s not too big and still much nicer than a plain circuit without any housing ;-) .

Constructing this box was real fun and well, I’ve learned quite something. I’m going to show some pics and what the box looked like in the end.

Picture 1: That’s the base where the Arduino board will be mounted on and one side element. The side element contains screw nuts so that the base can be closed and opened with screws later on. Opening is important because there’s a battery inside the box and I also may want to change the program code from time to time.

box view 1

Picture 2: This just shows how the base can be closed. The screw keeps the whole thing together. Note that I made all these parts with these mortise joints. For the base it would have probably been easier to make it flat, however, as it fitted quite well it still works, so I left it that way.

box pic 2

Picture 3: That’s the backside of the top panel. It contains the LCD screen, buttons, and LEDs. The screen is fixed with small screws, and the other parts will be kept in place with hot plastic glue later (worked quite well).

box pic 3

Picture 4: This is the base again. This time with Arduino Leonardo and some more electronic stuff on a perf board. I used the Leonardo board without headers, so it takes less place. The electronics are some resistors for LEDs and buttons, a piezo buzzer to make some noise and sockets where the upper panel can be connected. It’s soldered directly to the Arduino board via some wires. The whole thing is pretty compact and fits nicely on the base.

box pic 4

Picture 5: Now side panels are glued onto the top panel. LEDs and buttons have already been fixed with hot plastic before. And on the right side you can see space prepared for a 9V battery block.

box pic 5

Picture 6: Base connected to the upper part. Battery is already inside and the whole thing is ready to be closed with the four screws.

box pic 6

Picture 7: Closed box from below.

box pic 7

Picture 8: Final picture, the finished box.

box pic 8

So that’s my homemade little wood computer ;-) . Hope you like it and maybe got inspired to construct your own.

Modified Gameplay for Simon Says (2)

Here comes a little improvement to the code that I’ve posted yesterday (see other post).

After playing a “faster” game of Simon Says this morning and winning at the first attempt, I thought it would be nice being able to combine a higher MOVES_TO_WIN value with faster gameplay. So I adjusted the code in order to make it possible to keep buttons BLUE and YELLOW or BLUE and GREEN pressed at boot-up at the same time. BLUE and YELLOW will start a high speed game with 30 moves to win, and BLUE combined with GREEN will be a slow game with 30 moves to win.

To achieve this, I modified the button checking code in function loop as follows:

/* changing replay speed in case GREEN or YELLOW button has been pressed */
/* and setting a higher MOVES_TO_WIN number when BLUE button was pressed */
if((button & LED_GREEN) > 0)
  replayDelay = 300;
else if((button & LED_YELLOW) > 0)
  replayDelay = 75;

if((button & LED_BLUE) > 0)
  MOVES_TO_WIN = 30;

You can download the complete new version here: simon_mod2.ino.zip

Modified Gameplay for Simon Says

Now that I managed to load sketches on Sparkfun’s Simon Says game (see my other post), I created my own modifications in order to make gameplay a bit more interesting.

I took the NibbleSynth code from http://typorrhea.wordpress.com/2011/10/08/sparkfun-simon-says-nibblesynth as basis, because I wanted to have this music playing feature in my version, too.

These are the modifications that I added to the game:

  • When pressing the red button on power-up, the game will go to NibbleSynth mode.
  • When pressing the green button on power-up, the game will be slower: half speed.
  • When pressing the yellow button on power-up, the game will be faster: double speed (quite funny actually).
  • When pressing the blue button, you’ll have to repeat 30 steps without mistake in order to win (usually it is 13 steps). That’s much harder.
  • After the game ends and the winner or loser sound has been played, the game will show the number of correctly repeated steps. That’s quite handy when playing against other people and comparing scores. The number of LEDs that will light up is the number of steps that you correctly repeated. Example: if you had 9 correct steps, 4 LEDs will light up 2 times and 1 LED will light up 1 time.

Here comes the source code. Feel free to load it onto your Simon Says game or take it as basis for your own modifications. You can also download the zipped source code: simon_mod.ino.zip.

/**
 * 6-19-2007
 * Copyright 2009, Spark Fun Electronics
 * Nathan Seidle
 * nathan at sparkfun.com
 * 
 * The original code has been extended with "NibbleSynth" as published
 * in this blog:
 * http://typorrhea.wordpress.com/2011/10/08/sparkfun-simon-says-nibblesynth
 *
 * Other changes have been done by me. Holding a button on power-on has
 * different effects:
 * RED: game goes to NibbleSynth mode -> you can play music
 * GREEN: game play is half speed
 * YELLOW: game play is double speed (funny!)
 * BLUE: really hard game, you'll need 30 steps to win (instead of 13)
 *
 * My changes are also published on my blog under:
 * http://skyreacher.net?p=208
 *
 * Released under the Creative Commons Attribution Share-Alike 3.0 License
 * http://creativecommons.org/licenses/by-sa/3.0
 *
 * Simon Game ported for the ATmega168
 *
 * Fixes and cleanup by Joshua Neal 
 *
 * Generates random sequence, plays music, and displays button lights.
 *
 * Simon tones from Wikipedia
 * - A (red, upper left) - 440Hz - 2.272ms - 1.136ms pulse
 * - a (green, upper right, an octave higher than A) - 880Hz - 1.136ms,
 *   0.568ms pulse
 * - D (blue, lower left, a perfect fourth higher than the upper left) 
 *   587.33Hz - 1.702ms - 0.851ms pulse
 *   G (yellow, lower right, a perfect fourth higher than the lower left) - 
 *   784Hz - 1.276ms - 0.638ms pulse
 *
 * The tones are close, but probably off a bit, but they sound all right.
 *   
 * The old version of SparkFun simon used an ATmega8. An ATmega8 ships
 * with a default internal 1MHz oscillator.  You will need to set the
 * internal fuses to operate at the correct external 16MHz oscillator.
 *
 * Original Fuses:
 * avrdude -p atmega8 -P lpt1 -c stk200 -U lfuse:w:0xE1:m -U hfuse:w:0xD9:m
 *
 * Command to set to fuses to use external 16MHz: 
 * avrdude -p atmega8 -P lpt1 -c stk200 -U lfuse:w:0xEE:m -U hfuse:w:0xC9:m
 *
 * The current version of Simon uses the ATmega168. The external osciallator
 * was removed to reduce component count.  This version of simon relies on the
 * internal default 1MHz osciallator. Do not set the external fuses.
 */

#include 
#include 

/* Uncomment one of the following, corresponding to the board you have. */
//#define BOARD_REV_6_25_08
// #define BOARD_REV_4_9_2009
#define BOARD_REV_PTH

#ifdef BOARD_REV_PTH

#define CHIP_ATMEGA168

#define LED_RED		(1 << 0)
#define LED_GREEN	(1 << 1)
#define LED_BLUE	(1 << 2)
#define LED_YELLOW	(1 << 3)

/* LED pin definitions */
#define LED_RED_PIN			2
#define LED_RED_PORT		PORTB
#define LED_GREEN_PIN		3
#define LED_GREEN_PORT		PORTD
#define LED_BLUE_PIN		5
#define LED_BLUE_PORT		PORTB
#define LED_YELLOW_PIN		5
#define LED_YELLOW_PORT		PORTD

/* Button pin definitions */
#define BUTTON_RED_PIN		1
#define	BUTTON_RED_PORT		PINB
#define BUTTON_GREEN_PIN	2
#define	BUTTON_GREEN_PORT	PIND
#define BUTTON_BLUE_PIN		4
#define	BUTTON_BLUE_PORT	PINB
#define BUTTON_YELLOW_PIN	6
#define	BUTTON_YELLOW_PORT	PIND

/* Buzzer pin definitions */
#define BUZZER1		4
#define BUZZER1_PORT	PORTD
#define BUZZER2		7
#define BUZZER2_PORT	PORTD

#endif  /* BOARD_REV_PTH */

#ifdef BOARD_REV_6_25_08

#define CHIP_ATMEGA168

#define LED_RED		(1 << 0)
#define LED_GREEN	(1 << 1)
#define LED_BLUE	(1 << 2)
#define LED_YELLOW	(1 << 3)

/* LED pin definitions */
#define LED_RED_PIN		3
#define LED_RED_PORT		PORTC
#define LED_GREEN_PIN		2
#define LED_GREEN_PORT		PORTD
#define LED_BLUE_PIN		0
#define LED_BLUE_PORT		PORTC
#define LED_YELLOW_PIN		5
#define LED_YELLOW_PORT		PORTD

/* Button pin definitions */
#define BUTTON_RED_PIN		2
#define	BUTTON_RED_PORT		PINC
#define BUTTON_GREEN_PIN	5
#define	BUTTON_GREEN_PORT	PINC
#define BUTTON_BLUE_PIN		1
#define	BUTTON_BLUE_PORT	PINC
#define BUTTON_YELLOW_PIN	6
#define	BUTTON_YELLOW_PORT	PIND

/* Buzzer pin definitions */
#define BUZZER1		3
#define BUZZER1_PORT	PORTD
#define BUZZER2		4
#define BUZZER2_PORT	PORTD

#endif  /* BOARD_REV_6_25_08 */

#ifdef BOARD_REV_4_9_2009

#define CHIP_ATMEGA168

/* LED pin definitions */
#define LED_BLUE_PIN		5
#define LED_BLUE_PORT		PORTB
#define LED_YELLOW_PIN		5
#define LED_YELLOW_PORT		PORTD
#define LED_RED_PIN		2
#define LED_RED_PORT		PORTB
#define LED_GREEN_PIN		2
#define LED_GREEN_PORT		PORTD

/* Button pin definitions */
#define BUTTON_RED_PIN		0
#define	BUTTON_RED_PORT		PINB
#define BUTTON_GREEN_PIN	1
#define	BUTTON_GREEN_PORT	PINB
#define BUTTON_BLUE_PIN		7
#define	BUTTON_BLUE_PORT	PIND
#define BUTTON_YELLOW_PIN	6
#define	BUTTON_YELLOW_PORT	PIND

/* Buzzer pin definitions */
#define BUZZER1		3
#define BUZZER1_PORT	PORTD
#define BUZZER2		4
#define BUZZER2_PORT	PORTD

#endif  /* BOARD_REV_4_9_2009 */

/* Define game parameters */
#define TIME_LIMIT	5000		/* 5000ms = 5 sec */
#define SHOW_NUMBER_DELAY   800  /* delay used when showing score after game ends */

#define sbi(port_name, pin_number)   (port_name |= 1< 256)
  {
    TIFR0 = (1< 0) {
    delay_us(1000);
  }
}

/* Light the given set of LEDs */
void 
set_leds(uint8_t leds)
{
  if ((leds & LED_RED) != 0) {
    sbi(LED_RED_PORT, LED_RED_PIN);
  } 
  else {
    cbi(LED_RED_PORT, LED_RED_PIN);
  }
  if ((leds & LED_GREEN) != 0) {
    sbi(LED_GREEN_PORT, LED_GREEN_PIN);
  } 
  else {
    cbi(LED_GREEN_PORT, LED_GREEN_PIN);
  }
  if ((leds & LED_BLUE) != 0) {
    sbi(LED_BLUE_PORT, LED_BLUE_PIN);
  } 
  else {
    cbi(LED_BLUE_PORT, LED_BLUE_PIN);
  }
  if ((leds & LED_YELLOW) != 0) {
    sbi(LED_YELLOW_PORT, LED_YELLOW_PIN);
  } 
  else {
    cbi(LED_YELLOW_PORT, LED_YELLOW_PIN);
  }
}


#ifdef BOARD_REV_6_25_08
void
init_gpio(void)
{
  /* 1 = output, 0 = input */
  DDRB = 0b11111111; 
  DDRC = 0b00001001; /* LEDs and Buttons */
  DDRD = 0b00111110; /* LEDs, buttons, buzzer, TX/RX */

  PORTC = 0b00100110; /* Enable pull-ups on buttons 0,2,3 */
  PORTD = 0b01000000; /* Enable pull-up on button 1 */
}
#endif  /* BOARD_REV_6_25_08 */
#ifdef BOARD_REV_4_9_2009
void
init_gpio(void)
{
  /* 1 = output, 0 = input */
  DDRB = 0b11111100; /* button 2,3 on PB0,1 */
  DDRD = 0b00111110; /* LEDs, buttons, buzzer, TX/RX */

  PORTB = 0b00000011; /* Enable pull-ups on buttons 2,3 */
  PORTD = 0b11000000; /* Enable pull-up on button 0,1 */
}
#endif  /* BOARD_REV_4_9_2009 */

#ifdef BOARD_REV_PTH
void
init_gpio(void)
{
  /* 1 = output, 0 = input */
  DDRB = 0b11101101; /* LEDs and Buttons */
  DDRC = 0b11111111; /* LEDs and Buttons */
  DDRD = 0b10111011; /* LEDs, buttons, buzzer, TX/RX */

  PORTB = 0b00010010; /* Enable pull-ups on buttons 1,4 */
  //PORTC = 0b00100110; /* Enable pull-ups on buttons 0,2,3 */
  PORTD = 0b01000100; /* Enable pull-up on button 1 */    
}
#endif

void 
ioinit(void)
{
  init_gpio();	

  //Set Timer 0 Registers to Default Setting to over-ride the timer initialization made in the init() function of the Arduino Wiring libary (Wiring.c in the hardware/core/arduino folder)
  TCCR0A = 0;
  TIMSK0 = 0;
  /* Init timer 0 for delay_us timing (1,000,000 / 1 = 1,000,000) */
  //TCCR0B = (1< 70; x--) {
    for (y = 0; y < 3; y++) {
      sbi(BUZZER2_PORT, BUZZER2);
      cbi(BUZZER1_PORT, BUZZER1);

      delay_us(x);

      cbi(BUZZER2_PORT, BUZZER2);
      sbi(BUZZER1_PORT, BUZZER1);

      delay_us(x);
    }
  }
}

/* Play the winner sound and lights */
void
play_winner(void)
{
  set_leds(LED_GREEN|LED_BLUE);
  winner_sound();
  set_leds(LED_RED|LED_YELLOW);
  winner_sound();
  set_leds(LED_GREEN|LED_BLUE);
  winner_sound();
  set_leds(LED_RED|LED_YELLOW);
  winner_sound();
}

/* Plays the current contents of the game moves */
void
play_moves(void)
{
  uint8_t move;

  for (move = 0; move < nmoves; move++) {
    toner(moves[move], replayDelay);
    delay_ms(replayDelay);
  }
}

/* Adds a new random button to the game sequence, by sampling the timer */
void
add_to_moves(void)
{
  uint8_t new_button;

  /* Use the lower 2 bits of the timer for the random value */
  new_button = 1 << (TCNT2 & 0x3);

  moves[nmoves++] = new_button;
}

/* Toggle buzzer every buzz_delay_us, for a duration of buzz_length_ms. */
void 
buzz_sound(uint16_t buzz_length_ms, uint16_t buzz_delay_us)
{
  uint32_t buzz_length_us;

  buzz_length_us = buzz_length_ms * (uint32_t)1000;
  while (buzz_length_us > buzz_delay_us*2) {
    buzz_length_us -= buzz_delay_us*2;

    /* toggle the buzzer at various speeds */
    cbi(BUZZER1_PORT, BUZZER1);
    sbi(BUZZER2_PORT, BUZZER2);
    delay_us(buzz_delay_us);

    sbi(BUZZER1_PORT, BUZZER1);
    cbi(BUZZER2_PORT, BUZZER2);
    delay_us(buzz_delay_us);
  }
}

/*
 * Light an LED and play tone
 *
 * red, upper left:     440Hz - 2.272ms - 1.136ms pulse
 * green, upper right:  880Hz - 1.136ms - 0.568ms pulse
 * blue, lower left:    587.33Hz - 1.702ms - 0.851ms pulse
 * yellow, lower right: 784Hz - 1.276ms - 0.638ms pulse
 */
 void
toner(uint8_t which, uint16_t buzz_length_ms)
{
  set_leds(which);
  switch (which) {
  case LED_RED:
    buzz_sound(buzz_length_ms, 1136); 
    break;

  case LED_GREEN:
    buzz_sound(buzz_length_ms, 568); 
    break;

  case LED_BLUE:
    buzz_sound(buzz_length_ms, 851); 
    break;

  case LED_YELLOW:
    buzz_sound(buzz_length_ms, 638); 
    break;
  }

  /* Turn off all LEDs */
  set_leds(0);
}

// half-steps up the scale... I *think* it starts @ C3... could be wrong :)  
uint16_t freq_ms [] =
{
  0,
3431,
3239,
3058,
2886,
2724,
2571,
2427,
2290,
2162,
2040,
1926,
1818,
1716,
1619,
1528
};

void
synth_toner(uint8_t which, uint16_t buzz_length_ms)
{
  if(which==0) {return;}
  set_leds(which);

  buzz_sound(buzz_length_ms,freq_ms[which]); 

  /* Turn off all LEDs */
  set_leds(0);
}

/* Show an "attract mode" display while waiting for user to press button. */
void
attract_mode(void)
{
  while (1) {
    set_leds(LED_RED);
    delay_ms(100);
    if (check_button() != 0x00)
      return;

    set_leds(LED_BLUE);
    delay_ms(100);
    if (check_button() != 0x00) 
      return;

    set_leds(LED_GREEN);
    delay_ms(100);
    if (check_button() != 0x00) 
      return;

    set_leds(LED_YELLOW);
    delay_ms(100);
    if (check_button() != 0x00) 
      return;
  }
}


/* Wait for a button to be pressed.  Returns one of led colors (LED_RED, etc.)
 * if successful, 0 if timed out */
uint8_t
wait_for_button(void)
{
  uint16_t time_limit = TIME_LIMIT;
  uint8_t released = 0;
  uint8_t old_button;

  while (time_limit > 0) {
    uint8_t button;

    /* Implement a small bit of debouncing */
    old_button = button;
    button = check_button();

    /* 
     		 * Make sure we've seen the previous button
     		 * released before accepting new buttons
     		 */
    if (button == 0) 
      released = 1;
    if (button == old_button && released == 1) {
      /* Make sure just one button is pressed */
      if (button == LED_RED || 
        button == LED_BLUE ||
        button == LED_GREEN || 
        button == LED_YELLOW) {
        return button;
      }
    }

    delay_ms(1);

    time_limit--; 
  }
  return 0; 	/* Timed out */
}

void synthMode(void)
{
  play_winner();
  while (1) {
    synth_toner(check_button(),50);
  }
}

void show_score() {
  /* show score: number of LEDs lighting up is the number of correct moves */
  set_leds(0);
  delay_ms(2500);
  
  // show sequence of fours...
  while(nmoves > 4) {
    show_number(4);
    nmoves -= 4;
  }
  
  // and finally the remaining rest
  show_number(nmoves - 1);
  
  delay_ms(1500);
}

void show_number(uint8_t number) {
  /* show the given number for some amount of time */
  switch(number) {
    case 1: set_leds(0b00000001); break;
    case 2: set_leds(0b00000011); break;
    case 3: set_leds(0b00000111); break;
    case 4: set_leds(0b00001111);
  }
  delay_ms(SHOW_NUMBER_DELAY);
  set_leds(0);
  delay_ms(SHOW_NUMBER_DELAY);
}

/* Play the game.   Returns 0 if player loses, or 1 if player wins. */
int
game_mode(void)
{
  nmoves = 0;
  while (nmoves < MOVES_TO_WIN) {
    uint8_t move;

    /* Add a button to the current moves, then play them back */
    add_to_moves(); 
    play_moves(); 

    /* Then require the player to repeat the sequence. */
    for (move = 0; move < nmoves; move++) {
      uint8_t choice = wait_for_button();

      /* If wait timed out, player loses. */
      if (choice == 0)
        return 0;

      toner(choice, replayDelay); /* show choice with same speed as replay, so it sounds the same */

      /* If the choice is incorect, player loses. */
      if (choice != moves[move]) {
        return 0;
      }
    }

    /* Player was correct, delay before playing moves */
    delay_ms(1000);
  }

  /* player wins */
  nmoves++; /* must increase by one so that right number is shown later */
  return 1;
}

void setup()
{
}

void loop()
{

  /* Setup IO pins and defaults */
  ioinit();
  uint8_t button = check_button();
  
  if(button == LED_RED) {
    synthMode();
  } else {
    /* changing replay speed in case GREEN or YELLOW button has been pressed */
    /* and setting a higher MOVES_TO_WIN number when BLUE button was pressed */
    if(button == LED_GREEN)
      replayDelay = 300;
    else if(button == LED_YELLOW)
      replayDelay = 75;
    else if(button == LED_BLUE)
      MOVES_TO_WIN = 30;
    
    play_winner();
    /* Main loop */
    while (1) {
      /* Wait for user to start game */
      attract_mode();

      /* Indicate the start of game play */
      set_leds(LED_RED|LED_GREEN|LED_BLUE|LED_YELLOW);
      delay_ms(1000);
      set_leds(0);
      delay_ms(250);

      /* Play game and handle result */
      if (game_mode() != 0) {
        /* Player won, play winner tones */
        play_winner();
        /* and show score */
        show_score();
      } else {
        /* Player lost, play loser tones */
        play_loser();
        /* and show score */
        show_score(); 
      }
    }
  }
}

Uploading new code to Sparkfun’s Simon Says game under OS X

I’ve started playing around with electronics and microprocessors recently. My first soldering project was Sparkfun’s Simon Says game – pretty fun and just the right thing in order to get some soldering experience.

Next thing I wanted to do was to figure out how to upload a new program sketch to the ATmega328 processor with my Mac. Here comes a short summary on how this worked:

  1. First I soldered a 6 pins male header into the board’s FTDI Basic interface.
     
  2. Then I got Sparkfun’s USB to FTDI connection cable with 3.3V and attached it to the game board (black wire goes to BLK…) on the one side and to my Mac on the other side.
    USB cable connected to FTDI interface
  3. Configuration on the Mac works pretty much as described in this Sparkfun tutorial. I already had the Arduino IDE installed and just had to set the board type to Lilypad /w ATmega328.
  4. The one thing that was missing now was a driver for this FTDI interface. I got it from http://www.ftdichip.com/Drivers/VCP.htm where I downloaded version 2.2.18 for Mac OS X / x64 (64-bit). Opened the downloaded image file and installed the contained package and that’s it. Now I had the new option /dev/tty.usbserial-AE01A70D available under Tools->Serial Port in the Arduino IDE. Selected that one and uploaded a sketch successfully to the board.

The first sketch I uploaded onto my Simon Says game was the “NibbleSynth” program that I found on this blog: http://typorrhea.wordpress.com/2011/10/08/sparkfun-simon-says-nibblesynth/. It worked and is pretty fun to play around with! Thanks for sharing it.

Note: When loading a sketch on the Simon Says board, you’ll overwrite the original program. You get the game code from Sparkfun’s tutorial page, however, this will not include the hidden easter egg that was on the chip initially (it played a funny melody when holding a button while powering it on).

iCal Duplicates

I use the plain and simple iCal program on my Mac to keep track of private events. So far so good. To have my events with me, I sync the calendar with my iPhone, which – at least in theory – works as simple as activating a checkbox in iTunes and simply syncing the phone.

I don’t know why, but iCal is a pretty stupid program and doesn’t recognize duplicate entries at all. When syncing calendars it often happens that afterwards you’ll see lots of duplicates entries and it’s pretty annoying to remove them manually.

Today I found a pretty nice little program called iCal Dupe Deleter which allows to delete such duplicates with a simple click. I tried it out and it works like a charm, so I can absolutely recommend it. You can download it from http://www.nhoj.co.uk/icaldupedeleter for free (“donationware”). I tried it out on OS X 10.6.8 (Snow Leopard).