powerpic
Replacement board for a Casio CA-53W
/** @file alarmclock.c
*
* This mode implements a configurable alarm and an hourly chime.
*
*/
#include <xc.h>
#include "lib/mode.h"
#include "lib/events.h"
#include "lib/tick.h"
#include "lib/alarm.h"
#include "lib/display.h"
#include "lib/keypad.h"
#include "lib/buttons.h"
#include "lib/buzzer.h"
#include "modes/alarmclock.h"
#define LOG_TAG "mode.alarmclock"
#include "lib/logging.h"
// Status of daily alarm. 0 is disabled
static unsigned char daily_alarm_enabled = 0;
// Time object that holds our daily alarm.
static time_t daily_alarm = {
.hour = 0x16,
.minute = 0x20,
.second = 0x00
};
// Status of daily alarm. 0 is disabled
static unsigned char hourly_alarm_enabled = 0;
// Time object to hold the next hourly alarm.
static time_t hourly_alarm;
// Draw the daily alarm time to the display.
void daily_alarm_draw (void);
void daily_alarm_edit_draw (void);
void alarmclock_edit_start (void);
void hourly_alarm_update (void);
void
alarmclock_init (void)
{
// Initialize some default values for the hourly alarm
}
void
alarmclock_start (void)
{
// Tickrate is disabled by default and that's what we want
// Draw 'AL' in corner
display_secondary_string(1, "AL");
// Draw the daily alarm
display_primary_clear(0);
daily_alarm_draw();
if (daily_alarm_enabled)
{
// If the daily alarm is enabled, draw the wave
display_misc(DISPLAY_MISC_WAVE);
}
if (hourly_alarm_enabled)
{
// If the hourly chime is enabled, draw the bell
display_misc(DISPLAY_MISC_BELL);
}
}
signed char
alarmclock_run (unsigned int event)
{
if (EVENT_TYPE(event) == EVENT_TICK)
{
// We don't want any tick events
}
if (EVENT_TYPE(event) == EVENT_ALARM)
{
// We don't do anything about alarm events.
// Need an alarm clock daemon
}
if (EVENT_TYPE(event) == EVENT_KEYPAD)
{
if (EVENT_DATA(event) == '*')
{
// Multiply key is used to toggle hourly chime
if (hourly_alarm_enabled)
{
hourly_alarm_enabled = 0;
display_misc_clear(DISPLAY_MISC_BELL);
alarm_del_event(ALARMCLOCK_EVENT_CHIME);
}
else
{
hourly_alarm_enabled = 1;
display_misc(DISPLAY_MISC_BELL);
hourly_alarm_update();
alarm_set_time(&hourly_alarm, ALARMCLOCK_EVENT_CHIME);
}
}
if (EVENT_DATA(event) == '4')
{
// 4 key is used to toggle daily alarm
if (daily_alarm_enabled)
{
daily_alarm_enabled = 0;
display_misc_clear(DISPLAY_MISC_WAVE);
alarm_del_event(ALARMCLOCK_EVENT_DAILY);
}
else
{
daily_alarm_enabled = 1;
display_misc(DISPLAY_MISC_WAVE);
alarm_set_time(&daily_alarm, ALARMCLOCK_EVENT_DAILY);
}
}
}
if (EVENT_TYPE(event) == EVENT_BUTTON)
{
if (EVENT_DATA(event) == BUTTON_MODE_PRESS)
{
// Switch modes on mode press
return 1;
}
if (EVENT_DATA(event) == BUTTON_ADJ_PRESS)
{
// Adj button is used to edit the daily alarm.
// Set the run thread to be our edit thread
alarmclock_mode.run = &alarmclock_edit;
// Set tickrate to our blink rate
tick_rate_set_ms(500);
alarmclock_edit_start();
}
}
return 0;
}
void
alarmclock_stop (void)
{
// Clear AM/PM for next mode
display_misc_clear(DISPLAY_MISC_AM);
display_misc_clear(DISPLAY_MISC_PM);
// Clear colon for next mode
display_period_clear(DISPLAY_PERIOD_COLON);
}
static unsigned char daily_alarm_is_beeping = 0;
void
alarmclockd (unsigned int event)
{
if (EVENT_TYPE(event) == EVENT_ALARM)
{
if (EVENT_DATA(event) == ALARMCLOCK_EVENT_CHIME)
{
LOG_DEBUG("Chime alarm event event");
// Beep Block Beep
buzzer_tone(4500, 100, 75);
__delay_ms(100);
buzzer_tone(4500, 100, 75);
// Update hourly alarm to new time
hourly_alarm_update();
// Register alarm
alarm_set_time(&hourly_alarm, ALARMCLOCK_EVENT_CHIME);
}
if (EVENT_DATA(event) == ALARMCLOCK_EVENT_DAILY)
{
LOG_DEBUG("Daily alarm event");
// 20 sec long alarm
// Disabled with any button press
// NOTE: Since our buzzer and delays block right now, we just give
// a few beeps. (This means you can't silence it)
// daily_alarm_is_beeping = 1;
buzzer_tone(3200, 100, 75);
__delay_ms(50);
buzzer_tone(3200, 100, 75);
__delay_ms(50);
buzzer_tone(3200, 100, 75);
// Set another alarm for our daily time, this should be
// automatically set for tomorrow since the time has passed.
alarm_set_time(&daily_alarm, ALARMCLOCK_EVENT_DAILY);
}
}
if (daily_alarm_is_beeping &&
((EVENT_TYPE(event) == EVENT_KEYPAD) ||
(EVENT_TYPE(event) == EVENT_BUTTON)))
{
// Daily alarm is beeping, any button or keypress silences it.
LOG_DEBUG("Silencing daily alarm");
daily_alarm_is_beeping = 0;
}
}
static unsigned char edit_blink = 0;
static unsigned char edit_position = 0;
static unsigned char edit_tod = 0;
void
alarmclock_edit_start (void)
{
edit_blink = 1;
edit_position = 1;
// We temporarily convert the time to 12 hour format
if (0x00 == daily_alarm.hour)
{
// Midnght - 12:00 AM
daily_alarm.hour = 0x12;
edit_tod = 0;
}
else if (0x12 == daily_alarm.hour)
{
// Noon - 12:00 PM
edit_tod = 1;
}
else if (0x12 < daily_alarm.hour)
{
// PM
daily_alarm.hour -= 0x12;
edit_tod = 1;
}
else
{
// AM
edit_tod = 0;
}
// And clear the alarm enabled wave
display_misc_clear(DISPLAY_MISC_WAVE);
}
signed char
alarmclock_edit (unsigned int event)
{
if (EVENT_TYPE(event) == EVENT_TICK)
{
// Draw our alarm time
daily_alarm_edit_draw();
// Blink character that is currently being edited
// and am/pm sign
if (edit_blink)
{
// We are blinking meaning we are drawing an underscore in place
// of the current place being edited
display_primary_character((signed char)edit_position, '_');
// And clearing the AM/PM
display_misc_clear(DISPLAY_MISC_AM);
display_misc_clear(DISPLAY_MISC_PM);
edit_blink = 0;
}
else
{
edit_blink = 1;
}
}
if (EVENT_TYPE(event) == EVENT_KEYPAD)
{
unsigned char keypress = EVENT_DATA(event);
if ((keypress >= '0') && (keypress <= '9'))
{
keypress -= 48;
// Set alarm digit based on edit_position
switch (edit_position)
{
case 1: // Hour Tens
// Only acknowledge buttons 0-1
if (0 == keypress)
{
daily_alarm.hour &= 0x0F;
edit_position = 2;
}
else if (1 == keypress)
{
// Clear hour ones place to zero if it's over 2
if ((daily_alarm.hour & 0x0F) > 2)
{
daily_alarm.hour = 0x10;
}
else
{
daily_alarm.hour =
(unsigned char)(
(daily_alarm.hour & 0x0F) | (1 << 4)
);
}
edit_position = 2;
}
break;
case 2: // Hour Ones
if (1 == (daily_alarm.hour >> 4))
{
// Only acknowledge buttons 0-2 if hour is 1
if (3 > keypress)
{
daily_alarm.hour =
((daily_alarm.hour & 0xF0) | (keypress));
edit_position = 4;
}
}
else
{
// Acknowledge 1-9
daily_alarm.hour = ((daily_alarm.hour & 0xF0) | (keypress));
edit_position = 4;
}
break;
case 4: // Minute Tens
if (6 > keypress)
{
// Only acknowledge buttons 0-5
daily_alarm.minute =
(unsigned char)(
(daily_alarm.minute & 0x0F) | (keypress << 4)
);
edit_position = 5;
}
break;
case 5: // Minute Ones
daily_alarm.minute =
((daily_alarm.minute & 0xF0) | (keypress));
edit_position = 1;
break;
}
daily_alarm_edit_draw();
}
if (keypress == '.')
{
// switch AM/PM
if (edit_tod)
{
display_misc_clear(DISPLAY_MISC_PM);
display_misc(DISPLAY_MISC_AM);
edit_tod = 0;
}
else
{
display_misc_clear(DISPLAY_MISC_AM);
display_misc(DISPLAY_MISC_PM);
edit_tod = 1;
}
}
}
if (EVENT_TYPE(event) == EVENT_BUTTON)
{
if (EVENT_DATA(event) == BUTTON_MODE_PRESS)
{
// Mode button press advances edit position
switch (edit_position)
{
case 1: // Hour Tens
edit_position = 2;
break;
case 2: // Hour Ones
edit_position = 4;
break;
case 4: // Minute Tens
edit_position = 5;
break;
case 5: // Minute Ones
edit_position = 1;
break;
}
}
if (EVENT_DATA(event) == BUTTON_ADJ_PRESS)
{
// Adj button is used to confirm alarm and return to display mode
// Convert back to 24 hour format
if (edit_tod)
{
// PM time
// Handle 12:00 PM (Noon)
if (0x12 != daily_alarm.hour)
{
daily_alarm.hour += 0x12;
}
}
else
{
// AM
// Handle 12:00 AM (Midnight)
if (0x12 == daily_alarm.hour)
{
daily_alarm.hour = 0x00;
}
}
if (daily_alarm_enabled)
{
// Alarm is enabled, register alarm
// Delete any alarms with the old time
alarm_del_event(ALARMCLOCK_EVENT_DAILY);
alarm_set_time(&daily_alarm, ALARMCLOCK_EVENT_DAILY);
// Enable indicator
display_misc(DISPLAY_MISC_WAVE);
}
// Set the run thread to be our main run thread
alarmclock_mode.run = &alarmclock_run;
// Disable ticks
tick_disable();
// There might be a 0 in the hour tens place if hour < 10
display_primary_clear(1);
daily_alarm_draw();
}
}
return 0;
}
void
hourly_alarm_update (void)
{
datetime_time_now(&hourly_alarm);
if (0x23 == hourly_alarm.hour)
{
// Handle rollover
hourly_alarm.hour = 0x00;
}
else if (0x09 == hourly_alarm.hour)
{
// Handle jump from 0x09 to 0x10
hourly_alarm.hour = 0x10;
}
else if (0x19 == hourly_alarm.hour)
{
// handle jump from 0x19 to 0x20
hourly_alarm.hour = 0x20;
}
else
{
hourly_alarm.hour += 1;
}
// Clear minutes
hourly_alarm.minute = 0;
// Clear seconds
hourly_alarm.second = 0;
}
void
daily_alarm_draw (void)
{
// Draw alarm time
//
// (Pos 1-2) Display hour
if (0x00 == daily_alarm.hour)
{
// Midnght - 12:00 AM
display_primary_character(1, 1);
display_primary_character(2, 2);
display_misc(DISPLAY_MISC_AM);
}
else if (0x12 == daily_alarm.hour)
{
// Noon - 12:00 PM
display_primary_character(1, 1);
display_primary_character(2, 2);
display_misc(DISPLAY_MISC_PM);
}
else if (0x12 < daily_alarm.hour)
{
// PM
if ((daily_alarm.hour >> 4) - 1)
{
// Only display 1's
display_primary_character(1, ((daily_alarm.hour >> 4) - 1));
}
display_primary_character(2, ((daily_alarm.hour & 0x0F) - 2));
display_misc(DISPLAY_MISC_PM);
}
else
{
// AM
if ((daily_alarm.hour >> 4))
{
// Don't display 0's
display_primary_character(1, (daily_alarm.hour >> 4));
}
display_primary_character(2, (daily_alarm.hour & 0x0F));
display_misc(DISPLAY_MISC_AM);
}
// (3) Display colon
display_period(DISPLAY_PERIOD_COLON);
// (4-5) Display minute
display_primary_character(4, (daily_alarm.minute >> 4));
display_primary_character(5, (daily_alarm.minute & 0x0F));
}
void
daily_alarm_edit_draw (void)
{
// (Pos 1-2) Display hour
display_primary_character(1, (daily_alarm.hour >> 4));
display_primary_character(2, (daily_alarm.hour & 0x0F));
// (3) Display colon
display_period(DISPLAY_PERIOD_COLON);
// (4-5) Display minute
display_primary_character(4, (daily_alarm.minute >> 4));
display_primary_character(5, (daily_alarm.minute & 0x0F));
if (edit_tod)
{
// PM time of day
display_misc(DISPLAY_MISC_PM);
}
else
{
// AM time of day
display_misc(DISPLAY_MISC_AM);
}
}
// EOF //