Store › Forums › Defusable Clock › General Discussion › Adapted firmware (Atmega8, Sparkfun display, 24hrs)
- This topic has 0 replies, 1 voice, and was last updated 12 years, 9 months ago by melvin.
Viewing 1 post (of 1 total)
-
AuthorPosts
-
March 18, 2012 at 4:35 pm #458melvinMember
Dear all,
I just built my first defusable clock (I just used the circuit diagram, not the original kit). Since I only had a Sparkfun 7-Segment display (http://www.sparkfun.com/products/10931) I had to use another pin to control the colon (and I made it blinking every second).
I also adapted the code to use it with ATmega8s because they are cheaper and still offer enough power / memory.
Third adaption is the change to 24hrs display which seems to be a common change as I read.I attached the code in case that anyone is interested in 🙂
Melvin
/*
Defusable Clock Firmware
Copyright (C) 2011 nootropic design, LLC
All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
*/
/*
Additions Melvin Isken (melvin.isken@gmail.com):
- Adapted design for Sparkfun display (http://www.sparkfun.com/products/10931)
- Changed from am/pm to 24h time setting
- enabled use of Atmega8 instead of Atmega328 (switch via define)
*/
#include
//#define ATMEGA328
#define ATMEGA8
#define CLOCK 2
#define LATCH 3
#define DATA 4
#define COLON 13
#define MIN_BUTTON 0
#define HOUR_BUTTON 1
#define DET_BUTTON 2
#define ALARM_BUTTON 3
#define MIN_BUTTON_PIN 9
#define HOUR_BUTTON_PIN 10
#define DET_BUTTON_PIN 12
#define ALARM_BUTTON_PIN 15
#define LED_PM 16
#define LED_ALARM 17
#define LED_TOP 18
#define LED_DET 19
#define BUZZER 11
#define WIRE_1 5
#define WIRE_2 6
#define WIRE_3 7
#define WIRE_4 8
#define TIMER1_SECOND_START 49910
#define COUNTDOWN_DURATION 10
#define SNOOZE_MINUTES 9
#define ALARM_OFF 0
#define ALARM_ON 1
#define ALARM_DET 2
//additional pin for sparkfun led-display (http://www.sparkfun.com/products/10931) to make the colon blinking
#define LED_DP 14
volatile byte hours = 12;
volatile byte minutes = 0;
volatile byte seconds = 0;
//volatile boolean pm = false;
volatile unsigned int countdownSeconds;
volatile boolean ticked = false;
volatile boolean sec_blink = false;
boolean displayCountdown = false;
boolean countdownRunning = false;
byte buttonPins[4] = {MIN_BUTTON_PIN, HOUR_BUTTON_PIN, DET_BUTTON_PIN, ALARM_BUTTON_PIN};
byte buttonState[4] = {HIGH, HIGH, HIGH, HIGH};
unsigned long buttonChange[4] = {0L, 0L, 0L, 0L};
byte alarmHours = 12;
byte alarmMinutes = 0;
//boolean alarmpm = false;
byte alarmMode = ALARM_OFF;
volatile boolean alarmRinging = false;
boolean displayAlarmTime = false;
// Set to true if you want the PM LED on during PM hours. I think it's too bright and
// annoying, so I'm setting this to false by default.
boolean usePMIndicator = false;
byte snoozeHours = 12;
byte snoozeMinutes = 0;
//byte snoozepm = false;
boolean snoozeActivated = false;
boolean blank = false;
volatile byte currentDigit = 0;
void setup() {
pinMode(CLOCK, OUTPUT);
pinMode(LATCH, OUTPUT);
pinMode(DATA, OUTPUT);
pinMode(COLON, OUTPUT);
digitalWrite(COLON, LOW);
pinMode(LED_PM, OUTPUT);
pinMode(LED_ALARM, OUTPUT);
pinMode(LED_TOP, OUTPUT);
pinMode(LED_DET, OUTPUT);
pinMode(LED_DP, OUTPUT);
pinMode(BUZZER, OUTPUT);
pinMode(HOUR_BUTTON_PIN, INPUT);
pinMode(MIN_BUTTON_PIN, INPUT);
pinMode(ALARM_BUTTON_PIN, INPUT);
pinMode(DET_BUTTON_PIN, INPUT);
pinMode(WIRE_1, INPUT);
pinMode(WIRE_2, INPUT);
pinMode(WIRE_3, INPUT);
pinMode(WIRE_4, INPUT);
digitalWrite(HOUR_BUTTON_PIN, HIGH);
digitalWrite(MIN_BUTTON_PIN, HIGH);
digitalWrite(ALARM_BUTTON_PIN, HIGH);
digitalWrite(DET_BUTTON_PIN, HIGH);
digitalWrite(WIRE_1, HIGH);
digitalWrite(WIRE_2, HIGH);
digitalWrite(WIRE_3, HIGH);
digitalWrite(WIRE_4, HIGH);
// Read time from EEPROM
hours = EEPROM.read(0);
minutes = EEPROM.read(1);
seconds = EEPROM.read(2);
//pm = EEPROM.read(3);
alarmHours = EEPROM.read(4);
alarmMinutes = EEPROM.read(5);
//alarmpm = EEPROM.read(6);
alarmMode = EEPROM.read(7);
if ((hours == 0) || (hours > 24) || (alarmHours == 0) || (alarmHours > 24) || (minutes >= 60) || (alarmMinutes >= 60) || (alarmMode > ALARM_DET)) {
// invalid data in EEPROM (first time power up)
hours = 12;
minutes = 0;
seconds = 0;
alarmHours = 12;
alarmMinutes = 0;
//pm = false;
//alarmpm = false;
alarmMode = ALARM_OFF;
}
#ifdef ATMEGA8
TIMSK &= ~(1 << TOIE2);
TCCR2 &= ~((1<ASSR &= ~(1 << AS2);
TIMSK &= ~(1 << OCIE2);
TCCR2 |= (1 << CS22);
TCCR2 &= ~(1 << CS21);
TCCR2 |= (1 << CS20);
TCNT2 = 0;
TIMSK |= (1 << TOIE2);
TIMSK &= ~(1<TCCR1A = 0;
TCCR1B = (1<TIMSK |= (1<
TCNT1 = TIMER1_SECOND_START;
#endif
#ifdef ATMEGA328
// Initialize timers.
// Timer1 is used to keep the clock time
// Timer2 is used for the display multiplexing
// Disable the timer overflow interrupt
TIMSK2 &= ~(1 << TOIE2);
// Set timer2 to normal mode
TCCR2A &= ~((1 << WGM21) | (1 << WGM20));
TCCR2B &= ~(1 << WGM22);
// Use internal I/O clock
ASSR &= ~(1 << AS2);
// Disable compare match interrupt
TIMSK2 &= ~(1 << OCIE2A);
// Prescalar is clock divided by 128
TCCR2B |= (1 << CS22);
TCCR2B &= ~(1 << CS21);
TCCR2B |= (1 << CS20);
// Start the counting at 0
TCNT2 = 0;
// Enable the timer2 overflow interrupt
TIMSK2 |= (1 << TOIE2);
// init timer1
// set prescaler to 1024
TIMSK1 &= ~(1<TCCR1A = 0;
TCCR1B = (1<TIMSK1 |= (1< // With prescalar of 1024, TCNT1 increments 15,625 times per second
// 65535 - 15625 = 49910
TCNT1 = TIMER1_SECOND_START;
#endif
randomSeed(analogRead(0));
}
void loop() {
if (ticked) {
ticked = false;
// write info to EEPROM
EEPROM.write(0, hours);
EEPROM.write(1, minutes);
EEPROM.write(2, seconds);
//EEPROM.write(3, pm);
EEPROM.write(4, alarmHours);
EEPROM.write(5, alarmMinutes);
//EEPROM.write(6, alarmpm);
EEPROM.write(7, alarmMode);
}
if (alarmRinging) {
if (alarmMode == ALARM_ON) {
ringAlarm();
}
if (alarmMode == ALARM_DET) {
for(int i=0;i<4;i++) {
beep(3900, 250, false);
delay(250);
}
displayCountdown = true;
countdownSeconds = COUNTDOWN_DURATION;
countdown();
alarmRinging = false;
}
}
delay(20); // this helps with button debouncing
// check input
if (buttonPressed(ALARM_BUTTON)) {
displayAlarmTime = true;
/*if (alarmpm) {
digitalWrite(LED_PM, HIGH);
} else {
digitalWrite(LED_PM, LOW);
}*/
if (alarmMode == ALARM_OFF) {
digitalWrite(LED_ALARM, LOW);
digitalWrite(LED_DET, LOW);
} else {
digitalWrite(LED_ALARM, HIGH);
if (alarmMode == ALARM_DET) {
digitalWrite(LED_DET, HIGH);
} else {
digitalWrite(LED_DET, LOW);
}
}
} else {
displayAlarmTime = false;
digitalWrite(LED_ALARM, LOW);
digitalWrite(LED_DET, LOW);
}
if (buttonPressedNew(HOUR_BUTTON) || buttonHeld(HOUR_BUTTON, 250)) {
if ((!displayAlarmTime) && (!displayCountdown)) {
hours++;
/*if (hours == 12) {
pm = !pm;
}*/
if (hours == 24) {
hours = 0;
}
/*if (pm) {
digitalWrite(LED_PM, HIGH);
} else {
digitalWrite(LED_PM, LOW);
}*/
}
if (displayAlarmTime) {
// setting the alarm
alarmHours++;
/*if (alarmHours == 12) {
alarmpm = !alarmpm;
}*/
if (alarmHours == 24) {
alarmHours = 0;
}
/*if (alarmpm) {
digitalWrite(LED_PM, HIGH);
} else {
digitalWrite(LED_PM, LOW);
}*/
snoozeHours = alarmHours;
snoozeMinutes = alarmMinutes;
//snoozepm = alarmpm;
}
if (displayCountdown) {
if (countdownSeconds < 5940) {
countdownSeconds += 60;
}
}
} /*else {
if ((!displayAlarmTime) && (!buttonPressed(HOUR_BUTTON))) {
if ((pm) && (usePMIndicator)) {
digitalWrite(LED_PM, HIGH);
} else {
digitalWrite(LED_PM, LOW);
}
}
}*/
if (buttonPressedNew(MIN_BUTTON) || buttonHeld(MIN_BUTTON, 250)) {
if ((!displayAlarmTime) && (!displayCountdown)) {
minutes++;
if (minutes == 60) {
minutes = 0;
}
seconds = 0;
TCNT1 = TIMER1_SECOND_START;
}
if (displayAlarmTime) {
// setting the alarm
alarmMinutes++;
if (alarmMinutes == 60) {
alarmMinutes = 0;
}
snoozeHours = alarmHours;
snoozeMinutes = alarmMinutes;
//snoozepm = alarmpm;
}
if (displayCountdown) {
if (countdownSeconds < 5999) {
countdownSeconds++;
}
}
}
if (buttonPressedNew(DET_BUTTON)) {
if (displayAlarmTime) {
alarmMode++;
if (alarmMode > ALARM_DET) {
alarmMode = ALARM_OFF;
}
if (alarmMode == ALARM_OFF) {
snoozeActivated = false;
}
return;
}
// The DET button has been pressed but not released yet.
displayCountdown = true;
countdownSeconds = COUNTDOWN_DURATION;
}
if (!buttonPressed(DET_BUTTON)) {
if (displayCountdown) {
countdown();
}
}
}
void ringAlarm() {
int frequency = 3900;
int duration = 250; // each beep is .25s
int us = 1000000 / frequency / 2;
int toneLoopCount = (duration * ((float)frequency/1000.0));
int pauseLoopCount = 20000;
while (alarmRinging) {
for(int i=0;iPORTB |= (1 << 3);
if (buttonPressed(ALARM_BUTTON)) {
alarmRinging = false;
snoozeActivated = false;
break;
}
delayMicroseconds(us);
PORTB &= ~(1 << 3);
if (buttonPressed(DET_BUTTON)) {
alarmRinging = false;
snooze();
break;
}
delayMicroseconds(us);
}
for(int i=0;iif (buttonPressed(ALARM_BUTTON)) {
alarmRinging = false;
snoozeActivated = false;
break;
}
if (buttonPressed(DET_BUTTON)) {
alarmRinging = false;
snooze();
break;
}
}
} // while (alarmRinging)
}
void snooze() {
snoozeActivated = true;
// set the snooze time to current time plus 9 minutes
snoozeHours = hours;
//snoozepm = pm;
snoozeMinutes = minutes + SNOOZE_MINUTES;
if (snoozeMinutes >= 60) {
snoozeMinutes -= 60;
snoozeHours++;
/*if (snoozeHours == 12) {
snoozepm = !snoozepm;
}*/
if (snoozeHours == 24) {
snoozeHours = 0;
}
}
}
void countdown() {
int ledCounter = 0;
int ledCounterThreshold = 100000;
byte ledCurrentState = HIGH;
byte defusePin;
byte detPin;
boolean defused = false;
countdownRunning = true;
int fractionalSecond;
// assign random pins
defusePin = random(WIRE_1, (WIRE_4+1));
detPin = defusePin;
while (detPin == defusePin) {
detPin = random(WIRE_1, (WIRE_4+1));
}
digitalWrite(LED_PM, LOW); // turn off the PM LED
// Keep track of how far we are into the current
// second so we can correct later.
fractionalSecond = TCNT1 - TIMER1_SECOND_START;
// Reset back to the last second boundary so we can start the countdown
// immediately and so that the first second isn't truncated
TCNT1 = TIMER1_SECOND_START;
beep(3800, 30);
digitalWrite(LED_DET, ledCurrentState);
while ((countdownSeconds > 0) && (!defused)) {
for(int i=0;i<10000;i++) {
// get input
if (digitalRead(defusePin) == HIGH) {
defused = true;
break;
}
if (digitalRead(detPin) == HIGH) {
countdownSeconds = 0;
break;
}
}
delay(20);
if (ledCounter++ > ledCounterThreshold) {
ledCounter = 0;
if (ledCurrentState == HIGH) {
ledCurrentState = LOW;
} else {
ledCurrentState = HIGH;
}
digitalWrite(LED_DET, ledCurrentState);
}
}
digitalWrite(LED_DET, LOW);
countdownRunning = false;
if (!defused) {
detonate();
} else {
beep(4500, 80);
delay(3000);
displayCountdown = false;
}
// Now to keep the time accurate, add back in the fractional
// second that we took off when we started the countdown sequence.
// Wait until we can add it back to TCNT1 without overflowing.
while (TCNT1 >= (65535 - fractionalSecond));
TCNT1 += fractionalSecond;
}
void detonate() {
for(int i=0;i<8;i++) {
digitalWrite(LED_DET, HIGH);
beep(5000, 50, false);
delay(25);
digitalWrite(LED_DET, LOW);
delay(25);
}
blank = true;
for(int i=0;i<50;i++) {
digitalWrite(random(LED_PM, LED_DET+1), HIGH);
digitalWrite(random(LED_PM, LED_DET+1), HIGH);
for(int j=0;j<5;j++) {
beep(random(100, 300), 10);
}
for(int led=LED_PM;led<=LED_DET;led++) {
digitalWrite(led, LOW);
}
}
delay(2000);
displayCountdown = false;
blank = false;
}
// return true if the button is pressed.
boolean buttonPressed(byte button) {
if (digitalRead(buttonPins[button]) == LOW) {
// the button is currently pressed
if (buttonState[button] == HIGH) {
// if the button was not pressed before, update the state.
buttonChange[button] = millis();
buttonState[button] = LOW;
}
return true;
} else {
// The button is currently not pressed
if (buttonState[button] == LOW) {
// if the button was pressed before, update the state.
buttonChange[button] = millis();
buttonState[button] = HIGH;
}
return false;
}
}
// return true if the button is pressed and it is a new press (not held)
boolean buttonPressedNew(byte button) {
if (digitalRead(buttonPins[button]) == LOW) {
// The button is currently pressed
if (buttonState[button] == HIGH) {
// This is a new press.
buttonChange[button] = millis();
buttonState[button] = LOW;
return true;
}
// This is not a new press.
return false;
} else {
// The button is currently not pressed
if (buttonState[button] == LOW) {
buttonChange[button] = millis();
buttonState[button] = HIGH;
}
return false;
}
}
// return true if the button is pressed and has been held for at least n milliseconds
boolean buttonHeld(byte button, int n) {
if (digitalRead(buttonPins[button]) == LOW) {
// the button is currently pressed
if (buttonState[button] == HIGH) {
// if the button was not pressed before, update the state and return false.
buttonChange[button] = millis();
buttonState[button] = LOW;
return false;
}
if ((millis() - buttonChange[button]) >= n) {
// the button has been pressed for over n milliseconds.
// update the state change time even though the state hasn't changed.
// we update the state change time so we can start the counting over
buttonChange[button] = millis();
return true;
}
// The button is being held, but has not been held for longer than n milliseconds.
return false;
} else {
// The button is currently not pressed
if (buttonState[button] == LOW) {
// if the button was pressed before, update the state.
buttonChange[button] = millis();
buttonState[button] = HIGH;
}
return false;
}
}
void beep(int frequency, int duration) {
beep(frequency, duration, true);
}
void beep(int frequency, int duration, boolean disableDisplayInterrupt) {
int us = 1000000 / frequency / 2;
int loopCount = (duration * ((float)frequency/1000.0));
if (disableDisplayInterrupt) {
#ifdef ATMEGA328
TIMSK2 &= ~(1 << TOIE2);
#endif
#ifdef ATMEGA8
TIMSK &= ~(1 << TOIE2);
#endif
}
for(int i=0;iPORTB |= (1 << 3);
delayMicroseconds(us);
PORTB &= ~(1 << 3);
delayMicroseconds(us);
}
#ifdef ATMEGA328
TIMSK2 |= (1 << TOIE2);
#endif
#ifdef ATMEGA8
TIMSK |= (1 << TOIE2);
#endif
}
// This is the display interrupt to implement multiplexing of the digits.
ISR(TIMER2_OVF_vect) {
byte nDigits = 4;
byte data;
byte digitValue;
byte displayHours, displayMinutes;
TCNT2 = 0;
displayHours = hours;
displayMinutes = minutes;
if (displayAlarmTime) {
displayHours = alarmHours;
displayMinutes = alarmMinutes;
}
if (displayCountdown) {
displayHours = countdownSeconds / 60;
displayMinutes = countdownSeconds % 60;
}
if ((displayHours < 10) && (!displayCountdown)) {
nDigits = 3;
}
if (++currentDigit > (nDigits-1)) {
currentDigit = 0;
}
switch (currentDigit) {
case 0:
digitValue = displayMinutes % 10;
break;
case 1:
digitValue = displayMinutes / 10;
break;
case 2:
digitValue = displayHours % 10;
break;
case 3:
digitValue = displayHours / 10;
break;
}
// Upper 4 bits of data are the value for the current digit.
// They are loaded into shift register outputs QA-QD
data = (digitValue << 4);
// Lower 4 bits 3-0 represent which digit to turn on.
// 3 is most significant digit, 0 is least
// They are loaded into shift register outputs QE-QH
// Digit transistors are active low, so set them all high
data |= 0x0F;
if (!blank) {
// now turn off the bit for digit we want illuminated.
data &= ~(1 << currentDigit);
}
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLOCK, LSBFIRST, data);
digitalWrite(LATCH, HIGH);
//blinking the colon
if(sec_blink){
digitalWrite(LED_DP, HIGH);
}
else {
digitalWrite(LED_DP, LOW);
}
}
// Timer 1 interrupt. This executes every second.
ISR(TIMER1_OVF_vect) {
TCNT1 = TIMER1_SECOND_START;
//blinking the colon
if(sec_blink){
sec_blink = false;
}
else {
sec_blink = true;
}
ticked = true;
seconds++;
if (seconds == 60) {
seconds = 0;
minutes++;
if (minutes == 60) {
minutes = 0;
hours++;
/*if (hours == 12) {
pm = !pm;
}*/
if (hours == 24) {
hours = 0;
}
}
}
if ((!countdownRunning) && (alarmMode != ALARM_OFF)) {
if ((alarmHours == hours) && (alarmMinutes == minutes) && (seconds == 0) /*&& (alarmpm == pm)*/) {
alarmRinging = true;
}
if ((snoozeActivated) && (snoozeHours == hours) && (snoozeMinutes == minutes) && (seconds == 0) /*&& (snoozepm == pm)*/) {
alarmRinging = true;
}
}
if ((countdownRunning) && (countdownSeconds > 0)) {
beep(3800, 30);
countdownSeconds--;
}
}
-
AuthorPosts
Viewing 1 post (of 1 total)
- You must be logged in to reply to this topic.