Arduino Kitchen Timer

Arduino Kitchen Timer is made of 4-digit 7-segment LED, and 12-key keypad, 8-bit shift register 74HC164, and some resistors, decoupling capacitor, and Piezo speaker for the sound output.

* Do not imagine I am using this timer in my kitchen because I don’t. :) It’s just the name of device…

Feature:
Kitchen Timer. Maximum duration is 99 minutes and 99 sec. Beep when it’s done.

Parts:
1 x Keypad 12 Button
1 x 7-Segment Yellow 4 Digit LED
1 x 74AC164
8 x 470 ohm Resistor (LED)
* 220 ohm may work but it’s a little bit brighter. I used 470 ohm resistor array.
3 x 4.7K ohm Resistor (Keypad pullup resistors)
1 x Piezo Speaker taken out from old PC.

Pin Assignment:
Digital 02-05 Common Cathode of LED
Digital 06-12 Keypad Scanning
Digital 13 Piezo Speaker Output
Analog 0 74AC164 Data
Analog 1 74AC164 Clock
Analog 3-5 Not Used.

Video and Semantic in eagle format will be added soon.
Porting to Arduino Kitchen Timer Shield is on the way, but I will replace the keypad with just three or four buttons.

#include   <GenKeypad.h> //My keypad library
#include   <MsTimer2.h> //download this library at http://www.arduino.cc/playground/Main/MsTimer2

#define SHIFT_DATA 15     // 74AC164 data pin
#define SHIFT_CLOCK  14   // clock pin
#define SPEAKER_PIN 13

int NumPins[4] = {2, 3, 4, 5};
int SS = 0;
int MM = 0;
int start = 0;
char input[4] = {0, 0, 0, 0};
int inputIndex = 0;
int length = 15; // the number of notes
char notes[] = "ccccccccccccccc";
int beats[] = { 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1 };

int tempo = 300;
int beeping = 0;

/**
   Keypad 
 **/
GenKeypad keypad;

//Key Input Callback Functions 

void keyUP(char key)
{
  //Serial.print("keyUP: ");  
  //Serial.println(key);  
}  

int atoi(char c)
{
  if (c == 0)
    return 0;
  else
    return (c - '0');
}

void keyDOWN(char key)
{
  Serial.print("keyDOWN: ");
  Serial.println(key);  

  if (key == '#' && start == 0)
  {
    //Convert input chars into MM:SS
    MM = atoi(input[0]) * 10 + atoi(input[1]);
    SS = atoi(input[2]) * 10 + atoi(input[3]);

    if (MM + SS > 0)
      start = 1;
    return;
  }
  else if (key == '#' && start == 1)
  {
    start = 0;
    return;
  }

  if (key == '*')
  {
    if (beeping == 0)
    {
      for (int i = 0; i < 4; i++)
        input[i] = 0;
    }

    inputIndex = 0;
    start = 0;
    beeping = 0;
    digitalWrite(SPEAKER_PIN, LOW);
  }
  else
  {
    shiftDigit(input);
    input[3] = key;      

    inputIndex++;
    inputIndex %= 4;
  }
}  

void shiftDigit(char input[4])
{
  input[0] = input[1];
  input[1] = input[2];
  input[2] = input[3];
}

void keyPRESSING(char key)
{
  //Serial.print("keyPressed: ");  
  //Serial.println(key);  
}

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

  //Digital pins parameter order matters!!
  keypad.setRowPins(11, 6, 7, 9);  //These pins are used as OUTPUT pins.
  keypad.setColPins(10, 12, 8);    //These pins are used as INPUT pins.  
  keypad.setFunction(GenKeypad::EVENT_KEY_UP,       keyUP);
  keypad.setFunction(GenKeypad::EVENT_KEY_DOWN,     keyDOWN);
  keypad.setFunction(GenKeypad::EVENT_KEY_PRESSING, keyPRESSING);

  pinMode(SPEAKER_PIN, OUTPUT);

  pinMode(SHIFT_DATA, OUTPUT);
  pinMode(SHIFT_CLOCK, OUTPUT);

  pinMode(NumPins[0], OUTPUT);
  pinMode(NumPins[1], OUTPUT);
  pinMode(NumPins[2], OUTPUT);
  pinMode(NumPins[3], OUTPUT);

  digitalWrite(NumPins[0], HIGH);
  digitalWrite(NumPins[1], HIGH);
  digitalWrite(NumPins[2], HIGH);
  digitalWrite(NumPins[3], HIGH);

  // writing default values (from the datasheet)

  MsTimer2::set(1000, secPulse);
  MsTimer2::start();
}

int digitSS = 0;
int digitMM = 0;
int digit = 0;

  byte font[10] = {
	//abcdefg.
    0b11111100,
    0b01100000,
    0b11011010,
    0b11110010,
    0b01100110,
    0b10110110,
    0b10111110,
    0b11100100,
    0b11111110,
    0b11100110
  };

byte c2font(char c)
{
  const byte blank = 0b00000000;

  if (c == 0)
    return blank;
  else
    return font[c-'0'];
}

void setChars(char input[4], int delaymsec)
{
  digitalWrite(NumPins[0], LOW);
  shiftOut(SHIFT_DATA, SHIFT_CLOCK, LSBFIRST, c2font(input[3]));
  delay(delaymsec);
  digitalWrite(NumPins[0], HIGH);

  digitalWrite(NumPins[1], LOW);
  shiftOut(SHIFT_DATA, SHIFT_CLOCK, LSBFIRST, c2font(input[2]));
  delay(delaymsec);
  digitalWrite(NumPins[1], HIGH);

  digitalWrite(NumPins[2], LOW);
  shiftOut(SHIFT_DATA, SHIFT_CLOCK, LSBFIRST, c2font(input[1]));
  delay(delaymsec);
  digitalWrite(NumPins[2], HIGH);

  digitalWrite(NumPins[3], LOW);
  shiftOut(SHIFT_DATA, SHIFT_CLOCK, LSBFIRST, c2font(input[0]));
  delay(delaymsec);
  digitalWrite(NumPins[3], HIGH);

  delay(delaymsec);
}

void beep()
{
  if (start)
  {
    digitalWrite(SPEAKER_PIN, HIGH);
    beeping = 1;
    start = 0;
  }
  else
    digitalWrite(SPEAKER_PIN, LOW);
}

void secPulse()
{
  if (start)
  {
    SS--;
    if (SS == 0 && MM == 0)
    {
      beep();
      start = 0;
      return;
    }

    if (SS == -1 && MM > 0)
    {
      MM--;
      SS = 59;
    }

  }
}
void playTone(int tone, int duration) {
  for (long i = 0; i < duration * 1000L; i += tone * 2)
  {
    digitalWrite(SPEAKER_PIN, HIGH);
    delayMicroseconds(tone);
    digitalWrite(SPEAKER_PIN, LOW);
    delayMicroseconds(tone);
  }
}

void playNote(char note, int duration) {
  char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C', 'F', 'G'};
  int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 473, 478};

  // play the tone corresponding to the note name
  for (int i = 0; i < 10; i++) {
    if (names[i] == note)
    {
      playTone(tones[i], duration);
    }
  }
}

char * clearLeadingZero(char input[4])
{
  if (input[0] == '0')
  {
    input[0] = 0;
    if (input[1] == '0')
    {
      input[1] = 0;
      if (input[2] == '0')
        input[2] = 0;
    }
  }

  return input;
}

void processLCD()
{
  static char chs[4] = {'0', '0', '0', '0'};

  if (start)
  {
    chs[3] = SS%10 + '0';
    chs[2] = SS/10 + '0';
    chs[1] = MM%10 + '0';
    chs[0] = MM/10 + '0';

    setChars(clearLeadingZero(chs), 2);
  }
  else
  {
    setChars(input, 2);
  }
}

void loop()
{
  int someKeyDown = keypad.scanKeys(0);
  keypad.handlePostEvents(someKeyDown);      

  processLCD();

  if (beeping = 1)
  {
     beeping++;
     beeping %=3;

     for (int i = 0; i < length; i++)
     {
       someKeyDown = keypad.scanKeys(0);
       keypad.handlePostEvents(someKeyDown);      

       processLCD();
       if (beeping != 0)
         playNote(notes[i], beats[i] * tempo/2);

       // pause between notes
       delay(tempo / 2);
     }
  }
}

NTSC on Arduino

I switched to NTSC signal. I think it’s much simpler and easier, but it’s monochrome.
I’ve managed to get around 40 pixels width and working to increase vertical resolution.
It’s a very good idea to use constant length of interrupt to send NTSC signal apart from your pixel manipulation.

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1188261175/0

I am trying to build VGA (analog RGB) signal output on Arduino.
I found out that H/V Sync can be done by the built-in timer interrupt.

1. Arduino Pin-out assignment.

02: Red
03: Green
04: Blue
(05: SyncFlag)
06: VSync
07: HSync
10-13: SPI for EEPROM

(On going memo)

* Memory?
Frame buffer: 640 x 480 pixels x 3 bytes per pixel = 900KB
External EEPROM 2MB AT45DB161D may be needed.

* Find out the maximum transmission rate.

* Double buffering with two Atmega chips and one 2MB AT45DB161D? possible?

* Any suggestions?

References:
http://avga.prometheus4.com/index.php?p=2-0 (AVR but not written for Arduino)
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1228264461/all (Implementation issues discussed here.)
H/V Sync code using ISR timer: http://olb7.free.fr/arduino/syncInterrupt.pde
EEPROM:

http://blog.blockos.org/?p=27

http://todotani.cocolog-nifty.com/blog/2009/07/arduino-4cf4.html

Breadboard Arduino

You can build DIY arduino in your breadboard. The best site I found is: http://itp.nyu.edu/physcomp/Tutorials/ArduinoBreadboard

I have not managed the USB part yet, but I am sure you can add the Arduino Serial USB Board

So far, the LED blinking works good on my breadboard.
Eventually I am planning to do some VGA signal experiment on the breadboard.

Digit Counter with 7-segment LED + Arduino

It just counts 0 to 9 with 7-segment LED. Only digit 1 side has been used since the digit 2 is burned out…
Adjust output pins for your environment. For simplicity, you need 7 pins for output in this example.
I am sure I can reduce the number of pins with demux. That’s gonna be the next project.

Parts: Arduino Duemilanove, Breadboard, a register and LTD-5523AB (Datasheet)

The LED-facing-upright LTD-5523AB pin-order is:

18, 17, 16, 15, 14, 13, 12, 11, 10pin 
[------------LTD-5523AB------------]
     | - A - |       | - A - |
     F       B       F       B
     | - G - |       | - G - |
     E       C       E       C
     | - D - |       | - D - |
[------------LTD-5523AB------------]
01, 02, 03, 04, 05, 06, 07, 08, 09pin

[7-segment LED for digit 1]
A = 16pin, B = 15pin, C = 03pin, D = 02pin, E = 01pin, F = 18pin, DP = 04pin
14pin is common cathode for digit 1 (it means it should be GNDed all the time.)

For example, to make the digit 3, just turn on A, B, C, D and G.

[7segment.pde]
//Output Pins
//* Adjust this for your pin outputs.
int ledPin[8] = {4, 5, 6, 7, 9, 10, 11, 12};  

//Segment to Pin
//* Adjust this for your 7 segment LED spec.
//                 A,  B, C, D, E,  F,  G, DP
int Seg2Pin[8] = {11, 10, 5, 6, 7, 9, 12, 4};

//Digit to Segment
// * You don't need modify this unless you want to change digits.
int Dec2Seg[11] =
{
//A(125), B(64), C(32), D(16), E(8), F(4), G(2), DP(1)
  0x07E, //Digit 0
  0x00C, //Digit 1
  0x0B6, //Digit 2
  0x09E, //Digit 3
  0x0CC, //Digit 4
  0x0DA, //Digit 5
  0x0FB, //Digit 6
  0x00E, //Digit 7
  0x0FF, //Digit 8
  0x0DF, //Digit 9
  0x100 //DP
};

void setup()                    // run once, when the sketch starts
{
  for (int i = 0; i < 8; i++)
    pinMode(ledPin[i], OUTPUT);      // sets the digital pin as output
}

void clearAllSegments()
{
  //Turn off all segments
  for (int i = 0; i  0)
  {
    seg /= 2;
    if (seg & 0x1)
      digitalWrite(Seg2Pin[pin], HIGH);   // sets the LED on
    pin++;
  }
}

void loop()                     // run over and over again
{
  //Count 0 to 9
  for( int digit = 0; digit <= 10; digit++)
  {
    drawDigit(digit);
    delay(1000);                  // waits for a second
    clearAllSegments();
  }
}

Update 1: I change the 13pin to 9pin for output since 13pin has some builtin register.
Update 2: I implemented a bit mask to select segments.n

Scanner + Printer = Copy Machine

Multi-functional printer is just a combination of printer and scanner.
So, why don’t you make DIY copy machine in Linux if you have a printer and scanner?
Make sure your scanner is supported in SANE in Linux, and install scanbuttond for scanner’s button control.
scanbuttond seems no longer in active development, but it works OK in CentOS.

I added these commands in /usr/local/etc/buttonpressed.sh as follows:

In the button pressed event,

scanimage --mode lineart --resolution 300 -x 215 -y 297 > test.pnm
pnmtops -scale 1000 -dpi 300 test.pnm | lpr -PHL-2040
convert test.pnm /some/archive/`date + %Y%m%d%H%M`.pdf

So, whenever I press the button, it automatically starts scanning, then prints it out and archives as PDF.
The only thing that bugs me is the slow scanning time…

How to move SVN repository to other server.

On old SVN Server side:

# svnadmin dump your_repository_dir > repo.dmp

* This will take more than minutes.

On new SVN Server side:

# svnadmin create repo
# svnadmin load repo < repo.dmp

Note that taking a dump of repository does NOT dump the configuration files in repository.
So make you copy the conf directory.

References:

http://www.digitalmediaminute.com/article/2251/how-to-move-a-subversion-repository

http://2tbsp.com/node/88

http://svnbook.red-bean.com/en/1.1/ch05s03.html#svn-ch-5-sect-3.5

Posted in Linux. Tags: , , . Leave a Comment »

How to move /home to other drive.

Be careful to check if your /home is mounted or just a directory.
Thanks to IBM, following this instruction suffice for everybody.

http://www.ibm.com/developerworks/linux/library/l-partplan.html

I finally made the /home in software RAID 1… Next thing I need a UPS for the machine.

Posted in Linux. Tags: , , . Leave a Comment »

Xen Paravirtualized CentOS domU on CentOS 5.3 dom0

How to install paravirtualized CentOS 5.3 x64 as domU

# virt-install --paravirt \
               --name centosx64 \
               --ram 512 \
               --file /home/xen/images/centosx64.img \
               --file-size 10 \
               --nographics \
               --location http://mirrors.kernel.org/centos/5.3/os/x86_64/

It means Memory: 512MB, Disk Image: 10GB and No Graphics
Check the comment if you want to make use of pre-downloaded iso image or dvd.

If you want to add Desktop environment after instrallation, just add these.

# yum groupinstall "X Window System"
# yum groupinstall "GNOME Desktop Environment"

Reference: http://www.cyberciti.biz/tips/rhel-centos-xen-virtualization-installation-howto.html

CentOS 5.3 + xen-3.4-testing

$ su -
$ cd /usr/src
$ hg clone http://xenbits.xensource.com/xen-3.4-testing.hg
$ cd xen-3.4-testing.hg
$ make world
$ make install
$ ./install.sh

* eth0 is not detected with this kernel at this point.
So, I have to build the kernel…

Reference: http://bderzhavets.wordpress.com/2009/04/17/setup-xen-34-centos-53-dom0-64-bit/

CentOS 5.3 + linux-2.6.27 kernel

Anyway, I wasn’t happy with the XEN 3.3 with the xen kernel 2.6.18-128.1.10.el5xen (got these by yum)
Let’s update the newer kernel and Xen.

So, download the xen kernel 2.6.27, then apply patches, and compile. Let’s see if it works with Xen 3.4…

$ git clone http://xenbits.xen.org/git-http/xenclient/linux-2.6.27.git
$ cd linux-2.6.27
$ git clone http://xenbits.xen.org/git-http/xenclient/linux-2.6.27-pq.git .git/patches
$ guilt-push -a

(These commands above are taken from Xen-devel ML.)
I have been looking for the best Linux distro + Xen, but I hope CentOS 5.3 will be the one.

$ make CONFIG_DEBUG_SECTION_MISMATCH=y

Error 1:
drivers/built-in.o: In function `acpi_ec_read’:
/usr/src/linux-2.6.27/drivers/acpi/ec.c:390: undefined reference to `in_query_wmi_event_data’

make: *** [.tmp_vmlinux1] Error 1

Patch 1: It’s fun to see this kind of comment “HACK ALERT!!” But just comment out this hack…

diff –git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 2359480..b2c0221 100644
— a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -387,6 +387,7 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data)
/* HACK ALERT
* Please refer to wmi.c for an explanation on why we added this hack.
*/
+/*
if ( in_query_wmi_event_data == TRUE ) {
if ( address == 0×2b ) {
wmi_event_data_index = 0;
@@ -398,6 +399,7 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data)
wmi_event_data_index++;
}
}
+*/

return result;
}

$ make
$ make install
$ make modules_install
$ DEPMOD 2.6.27.19-5.1
$ mkinitrd /boot/initrd-2.6.27.19-5.1.img 2.6.27.19-5.1

Then, edit /boot/grub/menu.lst accordingly.