Adapted firmware (Atmega8, Sparkfun display, 24hrs)

Store Forums Defusable Clock General Discussion Adapted firmware (Atmega8, Sparkfun display, 24hrs)

Viewing 1 post (of 1 total)
  • Author
    Posts
  • #458
    melvin
    Member

    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;i PORTB |= (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;i if (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;i PORTB |= (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--;
    }
    }
Viewing 1 post (of 1 total)
  • You must be logged in to reply to this topic.