NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Project Help and Ideas » Vicious Alarm Clock

April 12, 2009
by wayward
wayward's Avatar

Hi fellow Nerdiates (and Nerdsperts),

this is something I've been working on over the last month or so (almost half the time since I got my NerdKit, so it is definitely my Major Project™). I wouldn't bother with the schematics as the circuit is fairly simple for those who know what the individual parts do, and if there is demand, I can certainly post more detailed descriptions, part numbers, code, etc. For now, a photo:

photo of a beginner's idea of a cool project

Before everyone jumps and accuses me that I have strayed far from what comes with NerdKits: the only part that you would absolutely need to buy for this is a bigger breadboard. Other things can either be salvaged from ye olde electronics equipment, obtained from your fellow nerds, or ordered as free samples. Your mileage may vary.

Vicious Alarm Clock is going to be an alarm clock to end all alarm clocks, as far as my girlfriend and I are concerned. We're both very hard sleepers and can go to great lengths to ignore attempts at becoming aware of our surroundings (I have been known to get up, open a terminal, issue a command to kill the music playing process blaring Bob Dylan from the speakers, close the terminal and go back to sleep, briefly scanning the newsfeeds in the browser along the way). I call it "vicious" because it is going to work on two levels:

  • play a highly annoying cacophony of sounds, of random frequency and random duration;
  • require a coherent cognitive response from the victim in order to stop.

The big idea is to hook the microcontroller to the LCD and a couple of buttons on one side, to display the time and obtain rudimentary input, and to a real-time clock on the other side. When the alarm sets off, you can silence it immediately for the time being. The LCD will display a few simple arithmetical questions of the type: 3+7 > 11-2 ? which you will need to keep answering successfully over a short period of time, say one minute, for the alarm to fully deactivate. (Think entering a passcode after you open an alarm-secured door.) Of course, this is rather cruel and may not be a huge success with everybody, but it's a nice way of learning electronics. Make a torture device for your girlfriend.

About the RTC: there are many of those available as free samples. I went with Texas Instruments' bq3287EMT since it has a backup battery and a 32.768KHz oscillator built-in, so all I have to do is interface it with the μC. (Not done yet.) I'll write more about that should there be interest.

LCD stuff: I soldered the LCD wires to an IC socket to make use of the bigger breadboard and optimize the layout of the components a bit along the way (note the location of the contrast trimmer — pins 1, 2 and 3 on the socket are connected to Vcc, CONTRAST and GND pins of the HD44780 interface (see NerdKits Guide pp. 28)). More interestingly, the IC next to it is 74LS174, a package of six D flip-flops wired up together using short blue wires to act as a shift register. This effectively enables me to use only two wires from the μC to drive the LCD, a rather ingenious design, wholly described here.

The number on the LCD is a random 16-bit number obtained by augmenting the noise from the analog-to-digital converter. Naturally, I will need good randomness, since hard sleepers could eventually get accustomed even to predictable out-of-tune sounds. :)

There, if it sounds interesting to you, please let me know. I'll keep you posted.

Zoran

April 12, 2009
by wayward
wayward's Avatar

If someone wants to build the two-wire LCD interface mentioned in the previous post, here's my modified lcd.c that seems to work fine. Drop it in the directory of your project and modify the Makefile and #include directives in your C code to refer to it instead of the original. Alternatively, you can just save a backup of the original lcd.c and replace it with this one, but if you are anything close to being as forgetful as I am, I advise against. :)

// lcd.c
// for NerdKits
//
// modified for Myke Predko's two-wire interface
// (http://www.rentron.com/Myke1.htm)

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include <stdio.h>

#include "lcd.h"
#include "delay.h"

/* To simplify things, we require both pins to belong to the same port.
 * You can certainly do it otherwise but you're pretty much on your own then.
 */
#define LCD_PORT          C
#define LCD_CLOCK_PIN     1
#define LCD_DATA_PIN      2

/* Do not change these unless you're changing the architecture
   to something other than AVR.
*/
#define _CONCAT(A, B, C) A ## B ## C
#define _DIR_REGISTER(port) _CONCAT(DDR, port, )
#define _DATA_REGISTER(port) _CONCAT(PORT, port, )
#define _PIN_NAME(port, pin) _CONCAT(P, port, pin)
#define DIR_REGISTER    _DIR_REGISTER(LCD_PORT)
#define DATA_REGISTER   _DATA_REGISTER(LCD_PORT)
#define CLOCK_PIN  _PIN_NAME(LCD_PORT, LCD_CLOCK_PIN)
#define DATA_PIN   _PIN_NAME(LCD_PORT, LCD_DATA_PIN)

void drive_clock(int state) {
  if (state)
    DATA_REGISTER |= (1 << CLOCK_PIN);
  else
    DATA_REGISTER &= ~(1 << CLOCK_PIN);
  delay_us(1);  // Delay to allow the 74LS174 to settle
}

void drive_data(int state) {
  if (state)
    DATA_REGISTER |= (1 << DATA_PIN);
  else
    DATA_REGISTER &= ~(1 << DATA_PIN);
  delay_us(1);  // Delay to allow the 74LS174 to settle
}

void push(int value) {
  drive_data(value);
  drive_clock(0);
  drive_clock(1);
}

void empty_shift_register() {
  // 74LS174 has a reset pin, but then this wouldn't be a two-wire interface.
  // Therefore we shift in six zeros.
  int i;

  drive_data(0);
  for (i = 0; i < 6; i++) {
    drive_clock(0);
    drive_clock(1);
  }
  drive_data(1);
}

void lcd_flush_nibble(char c) {
  int i;
  for (i = 0; i < 4; i++)
    push((c >> i) & 1);

  // NOTE: only 2 or 3 work in the delays here.
  // (So says Mike of NerdKits and I trust him.)
  drive_data(1); delay_us(2);  // use desired delay minus one
  drive_data(0); delay_us(2);  // (drive_data() has a 1us delay of its own)
}

void lcd_write_cmd_nibble(char c) {
  empty_shift_register();
  push(1);  // E high
  push(0); // RS low
  lcd_flush_nibble(c);
}

void lcd_write_cmd_byte(char c) {
  empty_shift_register();
  push(1);  // E high
  push(0);  // RS low
  lcd_flush_nibble( (c >> 4) & 0x0f );

  empty_shift_register();
  push(1);  // E high
  push(0); // RS low
  lcd_flush_nibble( c & 0x0f );

  delay_us(80);
}

void lcd_write_data_byte(char c) {
  empty_shift_register();
  push(1);  // E high
  push(1);  // RS high
  lcd_flush_nibble( (c >> 4) & 0x0f );

  empty_shift_register();
  push(1);  // E high
  push(1);  // RS high
  lcd_flush_nibble( c & 0x0f );

  delay_us(80);
}

void lcd_clear_and_home() {
  lcd_write_cmd_byte(0x01);
  delay_ms(50);
  lcd_write_cmd_byte(0x02);
  delay_ms(50);
}

void lcd_home() {
  lcd_write_cmd_byte(0x02);
  delay_ms(50);
}

void lcd_write_data(char c) {
  lcd_write_data_byte(c);
}

// lcd_write_int16
void lcd_write_int16(int16_t in) {
  uint8_t started = 0;

  uint16_t pow = 10000;

  if(in < 0) {
    lcd_write_data('-');
    in = -in;
  }

  while(pow >= 1) {
    if(in / pow > 0 || started || pow==1) {
      lcd_write_data((uint8_t) (in/pow) + '0');
      started = 1;
      in = in % pow;
    }
    pow = pow / 10;
  }
}

// lcd_write_int16_centi
// assumes that its measured in centi-whatevers
void lcd_write_int16_centi(int16_t in) {
  uint8_t started = 0;

  uint16_t pow = 10000;

  if(in < 0) {
    lcd_write_data('-');
    in = -in;
  }

  while(pow >= 1) {
    if(in / pow > 0 || started || pow==1) {
      lcd_write_data((uint8_t) (in/pow) + '0');
      started = 1;
      in = in % pow;
    }

    if(pow == 100) {
      if(!started) {
        lcd_write_data('0');
      }
      lcd_write_data('.');
      started = 1;
    }

    pow = pow / 10;
  }

}

void lcd_write_string(const char *x) {
  // assumes x is in program memory
  while(pgm_read_byte(x) != 0x00)
    lcd_write_data(pgm_read_byte(x++));
}

void lcd_line_two() {
  // set position 40, which is the first character
  // on the second line
  lcd_write_cmd_byte(0x80 | 0x40); 
}

// lcd_init()
void lcd_init() {
  // set pin driver directions (output on CLOCK_PIN and DATA_PIN)
  DIR_REGISTER |= (1 << CLOCK_PIN) | (1 << DATA_PIN);

  drive_clock(1);

  // wait 100msec
  delay_ms(100);

  // do reset
  lcd_write_cmd_nibble(0x03);
  delay_ms(6);
  lcd_write_cmd_nibble(0x03);
  delay_us(250);
  lcd_write_cmd_nibble(0x03);
  delay_us(250);

  // write 0010 (data length 4 bits)
  lcd_write_cmd_nibble(0x02);
  // set to 2 lines, font 5x8
  lcd_write_cmd_byte(0x28);
  // disable LCD
  //lcd_write_byte(0x08);
  // enable LCD
  lcd_write_cmd_byte(0x0c);
  // clear display
  lcd_write_cmd_byte(0x01);
  delay_ms(5);
  // enable LCD
  lcd_write_cmd_byte(0x0c);
  // set entry mode
  lcd_write_cmd_byte(0x06);

  // set cursor/display shift
  lcd_write_cmd_byte(0x14);

  // clear and home
  lcd_clear_and_home();
}

int lcd_putchar(char c, FILE *stream) {
  lcd_write_data(c);
  return 0;
}
April 12, 2009
by ranger
ranger's Avatar

Your wiring is beautiful. Just in case you didn't know ;)

Great idea with the LCD hookup; I have some of those laying around, just might hook that up... won't have to keep finding which darn wire came loose.

Anyways, the alarm clock... reminds me of a cross between that "mail goggles" app for gmail and the alarm clocks that get up and roll away when activated. Nice work.

April 13, 2009
by wayward
wayward's Avatar

Hehe, I was going to apologize for the wiring actually :) It seems a little wasteful to go cutting the wires to exact lengths :) But I imagine most of these can be reused (especially the shorter ones).

Thanks for the feedback. I'm going to connect the RTC now. Wish me luck.

April 13, 2009
by ranger
ranger's Avatar

Might be a little wasteful, but it looks good.

How'd it go?

April 16, 2009
by wayward
wayward's Avatar

I did wire up the RTC, but I'm not reading anything sensible from it yet (any RAM location I try to access returns 0x0c). Tinkering with timings ahead. :)

May 23, 2009
by wayward
wayward's Avatar

New developments.

It's Alive!

The clock is operational. I added three push buttons which serve no actual purpose yet, but will be used to navigate the configuration menu (adjust alarm/time/date etc.) and, more importantly, communicate with the alarm clock when it decides that you've slept enough. (There's another push button grounding PB0 when held; I used to have a jumper there which uses less space but is a pain to use, and I admit, I reprogram this thing every few lines of code).

I am thinking what is the best thing to have in order to stop the alarm, requires presence of mind, and can be done with three push buttons. Math questions? A simple game of reflexes? Pattern memorization? Post your ideas!

May 24, 2009
by wayward
wayward's Avatar

I have a question regarding buttons :) These push buttons aren't ideal; there is likely some noise when they are pressed and depressed, which might result in the pin value going from, say, high (I have pull-ups on them) through a series of high-low states to finally stabilize on low. This would happen rather quickly, I understand, but still I want to safeguard against the MCU picking up one of those intermediary high states as the button being depressed. Is there some canonical way to make sure that the button is fully depressed? Should I set a timer and calculate "duty cycle" on the pin after I first get a low on it over a certain time period, then conclude that the button is pressed if the duty is above 50%? Or is there a better way?

Thanks!

May 25, 2009
by mrobbins
(NerdKits Staff)

mrobbins's Avatar

Hi wayward,

The clock looks great!

The issue you are raising with the buttons/switches being noisy in their transitions is an issue called "debouncing" -- literally because you want to remove the effect of the switch contacts physically bouncing around. You're on the right track with the timer although I don't think that "duty cycle" would be the best way to approach it. A simpler way is to keep track of time and to not allow your logic code to consider any button state changes until the button has maintained a particular high or low level for say 10 milliseconds. Or, probably the super-simple way is to just "go to sleep" with delay_ms(10) after any transition, so you'd just ignore any bouncing (or anything at all) that happened in that time, if that still leaves you enough time to do what you want in the rest of your code.

Mike

May 25, 2009
by wayward
wayward's Avatar

mrobbins,

thanks for setting me straight on "duty cycle" -- I learn new terms and use them left and right until I figure them out. Now "debouncing" opens new horizons on Google :) I'll see what ways there are to do it, pick one that doesn't require much (if any) external circuitry, and does not waste cycles, then post it here for other Nerdiates.

Cheers!

May 25, 2009
by wayward
wayward's Avatar

There's a good debouncing tutorial and an example code for AVR. I'm putting it here for anyone interested. I'll do something like shown there, only I'll keep the timer running only for the duration of the uncertainty period. I am trying to keep power consumption to a minimum.

June 02, 2009
by wayward
wayward's Avatar

AVR Vicious Alarm Clock now lives on Launchpad.

July 02, 2009
by jbremnant
jbremnant's Avatar

That debouncing technique is interesting. wayward, props to you for the projects and all the info you post along with it. At first I thought 10ms was rather arbitrary time to wait until the bouncing settles. However, that pdf you linked goes through interesting heuristics of bouncing behavior for variety of switches - 10ms seems plenty of time to wait before checking the states again.

July 03, 2009
by wayward
wayward's Avatar

Thanks, jbremnant. I'll keep posting here whatever interesting things I find. For now, though, the only modification I did on my project -- hardware-side -- was adding one pull-up resistor to the chip select pin on the timer IC. :) All the development is in the code.

July 04, 2009
by sgmaniac1255
sgmaniac1255's Avatar

i'm afraid there is one major flaw in you design, (and this is coming from a fellow heavy sleeper) if it were me that "monstrosity" (no affiance, just saying what i would be thinking in the morning :P ) was trying to wake up.... it might find its self in the next county.... through several windows :)...

i would recommend heavy duty bolts along with something that is wire cutter and or hammer proof to power the speakers

July 04, 2009
by wayward
wayward's Avatar

Well, that is a problem, among several others. For example, why bother when I have my Linux box do the same in a few keystrokes? :) But thanks for your input. I am considering building it into my wall, behind a bulletproof glass.

August 30, 2009
by Keith726
Keith726's Avatar

Wow - this is a great project! Good luck with the alarm clock. I, too will rewire my LCD and solder the leads to a socket - GREAT idea. Will you post revised lcd.h for the new 4-line lcd that now ships with the Nerdkit? Can you please post the code that you use to access the bq3287 chip? I'm a n00b, and I would love to have an example of how to use the MCU to interface with another complex chip.

August 30, 2009
by wayward
wayward's Avatar

Hey Keith726,

actually, I've been thinking to do a write-up on everything that I have done so far, explain design choices, tricks of the trade that I learned, etc. I am not fully satisfied with how the clock works (still need to connect a sound source to it :D), but why not? I'll write something in the next few days and post it here.

Thanks for giving me a nudge to get off my lazy chair.

September 09, 2009
by wayward
wayward's Avatar

I've been asked by a few people about the two-wire LCD interface, so I did a little write-up with diagrams, source code and Eagle board of you want to try it out for yourself. I haven't tested the source code on any displays other than my 2x24 LCD and I haven't etched the PCB myself. Let me know if any of it works, or doesn't!

schematic for the 2-wire interface

September 10, 2009
by mcai8sh4
mcai8sh4's Avatar

Hi wayward, great instructions, I'll be trying this out when time permits.

I notice the lcd.c lcd.h files are not on the server though.

Nice work!!

September 11, 2009
by wayward
wayward's Avatar

Thanks mcai, fixed that.

October 24, 2009
by pbfy0
pbfy0's Avatar

Could you please post the VAC source itself (main program)?

October 31, 2009
by wayward
wayward's Avatar

pbfy0: https://launchpad.net/avac is where the code is, navigate to Bazaar repository and do a checkout.

May 02, 2010
by wayward
wayward's Avatar

Some people have complained that the link to the two-wire LCD interface isn't working, which is true because I was linking to my laptop. Here's the same write-up on Google Sites. Just ignore AdSense.

May 05, 2010
by n3ueaEMTP
n3ueaEMTP's Avatar

Zoran, I would love to interface the bq3287 chip with my nerd kit. Could you pass along whatever code/wiring diagrams you are willing to share please? Thanks

Chris B. n3ueaEMTP

Post a Reply

Please log in to post a reply.

Did you know that hobby servos are controlled through a particular kind of PWM (Pulse Width Modulation) signal? Learn more...