alarmclock/matrixclock/matrixclock.ino

234 lines
6 KiB
C++

#include <IRremote.h>
#define SERIAL_DEBUG 0
// Remote controls
#define RC6_CHM 0xFFA25D// CHANNEL -
#define RC6_CH 0xFF629D// CHANNEL
#define RC6_CHP 0xFFE21D// CHANNEL +
#define RC6_PREV 0xFF22DD// |<< PREVIOUS
#define RC6_NEXT 0xFF02FD// >>| NEXT
#define RC6_PLAY 0xFFC23D// PLAY/PAUSE
#define RC6_VM 0xFFE01F// VOLUME -
#define RC6_VP 0xFFA857// VOLUME +
#define RC6_EQ 0xFF906F// EQ
#define RC6_0 0xFF6897// 0
#define RC6_100 0xFF9867// 100+
#define RC6_200 0xFFB04F// 200+
#define RC6_1 0xFF30CF// 1
#define RC6_2 0xFF18E7// 2
#define RC6_3 0xFF7A85// 3
#define RC6_4 0xFF10EF// 4
#define RC6_5 0xFF38C7// 5
#define RC6_6 0xFF5AA5// 6
#define RC6_7 0xFF42BD// 7
#define RC6_8 0xFF4AB5// 8
#define RC6_9 0xFF52AD// 9
#define AMP2540_VCDAUX 0x8FDBCE66
#define AMP2540_TUNERCD 0xC375A98B
#define AMP2540_DVD 0xCC7372A6
#define AMP2540_TAPE 0xE917A076
#define AMP2540_SEARCH 0xE82D76B6
#define AMP2540_RESET 0x04D1A486
#define AMP2540_VM 0xAB95D276
#define AMP2540_VP 0x0217C346
#define MODE_NORMAL 0
#define MODE_SETTIME 1
#define MODE_SETALARM 2
#define PIN_BUTTON 3
#define PIN_BUZZER 8
#define PIN_IR 10
#define PIN_LED_DUTY A0
#define PIN_REG_CLOCK 7
#define PIN_REG_DATA 5
#define PIN_REG_LATCH 6
IRrecv irrecv(PIN_IR);
decode_results ircode;
unsigned long last_ir = 0;
// Matrix state
unsigned char matrix[8] = {0b10000001, 0, 0, 0, 0, 0, 0, 0b10000001};
unsigned char matrix_row_on = 0;
// Interface mode
unsigned char mode = MODE_NORMAL;
// Selected number (0..4: None, Hour, Minute, Second)
unsigned char number_selection = 0;
// Time added to internal clock, to fit real time
unsigned long time_offset = 0;
unsigned char alarm = false;
unsigned long alarm_time = 0;
// Is alarm ringing
unsigned char alarm_ringing = false;
unsigned int led_duty;
unsigned int led_duty_step = 3;
// Bytewise reverse
unsigned char reverse(unsigned char b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
unsigned char fix_pixels(unsigned char b) {
return (b << 1) | (b >> 7);
}
void shift_matrix() {
if(led_duty_step == 0) {
digitalWrite(PIN_REG_LATCH, LOW);
shiftOut(PIN_REG_DATA, PIN_REG_CLOCK, MSBFIRST, ~(1 << matrix_row_on));
shiftOut(PIN_REG_DATA, PIN_REG_CLOCK, MSBFIRST, fix_pixels(matrix[(matrix_row_on+7)%8]));
digitalWrite(PIN_REG_LATCH, HIGH);
matrix_row_on = (matrix_row_on + 1) % 8;
} else if(led_duty_step == 1) {
digitalWrite(PIN_REG_LATCH, LOW);
shiftOut(PIN_REG_DATA, PIN_REG_CLOCK, MSBFIRST, 255);
shiftOut(PIN_REG_DATA, PIN_REG_CLOCK, MSBFIRST, 0);
digitalWrite(PIN_REG_LATCH, HIGH);
}
led_duty_step ++;
}
void draw_time(unsigned long t) {
unsigned char seconds = t/1000%60;
unsigned char minutes = t/60000%60;
unsigned char hours = t/3600000%24;
matrix[1] = reverse(seconds) >> 1;
matrix[2] = matrix[1];
matrix[3] = reverse(minutes) >> 1;
matrix[4] = matrix[3];
matrix[5] = reverse(hours) >> 1;
matrix[6] = matrix[5];
}
void draw_mode() {
switch(mode) {
case MODE_NORMAL: matrix[0] = 0b10000001; break;
case MODE_SETTIME: matrix[0] = 0b10011001; break;
case MODE_SETALARM: matrix[0] = 0b10111101; break;
}
if(alarm) matrix[0] |= 0b01000010;
}
void draw_number_selection() {
switch(number_selection) {
case 1: matrix[1] |= 0b10000001; break;
case 2: matrix[3] |= 0b10000001; break;
case 3: matrix[5] |= 0b10000001; break;
}
}
const unsigned char HEX_ALPHA[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
void send_hex(unsigned long val) {
Serial.write(HEX_ALPHA[(val >> 28) & 0xf]);
Serial.write(HEX_ALPHA[(val >> 24) & 0xf]);
Serial.write(HEX_ALPHA[(val >> 20) & 0xf]);
Serial.write(HEX_ALPHA[(val >> 16) & 0xf]);
Serial.write(HEX_ALPHA[(val >> 12) & 0xf]);
Serial.write(HEX_ALPHA[(val >> 8) & 0xf]);
Serial.write(HEX_ALPHA[(val >> 4) & 0xf]);
Serial.write(HEX_ALPHA[val & 0xf]);
Serial.write('\n');
}
void setup() {
pinMode(PIN_REG_DATA, OUTPUT);
pinMode(PIN_REG_CLOCK, OUTPUT);
pinMode(PIN_REG_LATCH, OUTPUT);
pinMode(PIN_BUZZER, OUTPUT);
pinMode(PIN_BUTTON, INPUT_PULLUP);
pinMode(PIN_LED_DUTY, INPUT);
irrecv.enableIRIn();
pinMode(13, OUTPUT);
digitalWrite(13, LOW);
#if SERIAL_DEBUG
Serial.begin(9600);
#endif
}
void loop() {
if(led_duty_step > led_duty) {
led_duty_step = 0;
led_duty = (1023-analogRead(PIN_LED_DUTY)) / 128;
}
if(irrecv.decode(&ircode)) {
#if SERIAL_DEBUG
send_hex(ircode.value);
#endif
unsigned long val = ircode.value;
if(val == last_ir) {
alarm_ringing = false;
switch(val) {
case AMP2540_VCDAUX: mode = (mode+1) % 3; break;
case AMP2540_VP: change_selected_number(1); break;
case AMP2540_VM: change_selected_number(-1); break;
case AMP2540_SEARCH: number_selection = (number_selection+1) % 4; break;
case AMP2540_TUNERCD: alarm = !alarm; break;
}
last_ir = 0;
} else {
last_ir = val;
}
irrecv.resume();
}
if(alarm && !alarm_ringing) {
unsigned long t = ((millis() + time_offset) / 1000) % 86400;
if(alarm_time / 1000 == t) {
alarm_ringing = true;
}
}
if(alarm_ringing) {
if(millis()%1000 < 100) {
matrix[7] = 0b11111111;
digitalWrite(PIN_BUZZER, HIGH);
} else {
matrix[7] = 0b10000001;
digitalWrite(PIN_BUZZER, LOW);
}
if(digitalRead(PIN_BUTTON) == LOW) {
digitalWrite(PIN_BUZZER, LOW);
alarm_ringing = false;
}
}
draw_mode();
if(mode == MODE_SETALARM)
draw_time(alarm_time);
else
draw_time(millis() + time_offset);
draw_number_selection();
shift_matrix();
}
void change_selected_number(signed int nb) {
switch(mode) {
case MODE_SETTIME: change_time(&time_offset, nb); break;
case MODE_SETALARM: change_time(&alarm_time, nb); break;
}
}
void change_time(unsigned long *t, signed int mul) {
switch(number_selection) {
case 1: *t += mul * 1000; break;
case 2: *t += mul * 60000; break;
case 3: *t += mul * 3600000; break;
}
*t %= 86400000;
}