/* * "DAC-Master(tm)" * * An open-source arduino controller-based wolfson DAC user interface * * Author: Bryan Levin (linux-works) * * Revision: v1.03 */ #define SplashScrnTime 3 // Splash Screen display time, seconds const char Title[] = { "LinuxWorks Labs"}; const char Version[] = { "DACmaster 1.03"}; #include #include #include #include #include #include #include #include /* keyscans for all known keys on MY remote open/close 1302 middle buttons: audio 1380 angle 1381 subtitle 1379 shuffle 1333 program 1311 repeat 1324 dve 1368 prev 1328 next 1329 time/text 1320 search_left 1312 step_right 1313 slow_left 1314 slow_right 1315 play 1330 pause 1337 stop 1336 lower buttons: menu 1307 title 1306 return 1294 display 1364 dvd-mode buttons: power-on 1301 tv-video 165 vol-up 18 vol-down 19 keypad_1 1280 keypad_2 1281 keypad_3 1282 keypad_4 1283 keypad_5 1284 keypad_6 1285 keypad_7 1286 keypad_8 1287 keypad_9 1288 keypad_0 1289 clear 1295 enter 1291 tv-mode buttons: power-on ?? tv/video 165 vol-up 146 vol-down 147 keypad_1 128 keypad_2 129 keypad_3 130 keypad_4 131 keypad_5 132 keypad_6 133 keypad_7 134 keypad_8 135 keypad_9 136 keypad_0 137 clear 1295 enter 139 ***************************** */ /* * IR key scans for sony remotes */ #define KEYSCAN_POWER 149 #define KEYSCAN_ROTATE_THRU_INPUTS 1379 // subtitle #define KEYSCAN_FILT_A 131 //1283 // keypad 4 #define KEYSCAN_FILT_B 132 //1284 // keypad 5 #define KEYSCAN_FILT_C 133 //1285 // keypad 6 #define KEYSCAN_COAX1 134 //1286 // keypad 7 #define KEYSCAN_TOSLINK1 135 //1287 // keypad 8 #define KEYSCAN_CLIP_ON 136 //1288 // keypad 9 #define KEYSCAN_CLIP_OFF 137 //1289 // keypad 0 //ANTICLIP_SEL_OFF #define KEYSCAN_ARROW_UP 1401 #define KEYSCAN_ARROW_DOWN 1402 #define KEYSCAN_ARROW_LEFT 1403 #define KEYSCAN_ARROW_RIGHT 1404 #define KEYSCAN_ARROW_ENTER 1291 // IR decoding #define start_bit 2000 // Start bit threshold (Microseconds) #define bin_1 1000 // Binary 1 threshold (Microseconds) #define bin_0 400 // Binary 0 threshold (Microseconds) #define PULSE_DUR 1000 #define PULSE_2200 2200 #define SONY_IR_PROTO_PULSE_TRAIN_LEN 11 /* * arduino (not atmel!) pins for physical computing i/o */ #define PIN0_RESERVED 0 // pin0 reserved for tx/rx data #define PIN1_RESERVED 1 // pin1 reserved for tx/rx data #define LCD_DB4_PIN 2 // hitachi pin 11 #define LCD_DB5_PIN 3 // hitachi pin 12 #define LCD_DB6_PIN 4 // hitachi pin 13 #define LCD_DB7_PIN 5 // hitachi pin 14 #define LCD_RS_PIN 6 // hitachi pin 4 ('register select') #define LCD_ENABLE_PIN 7 // hitachi pin 6 ('E') #define IR_PIN 8 // IR receiver (sensor pin wired direct) #define PWM_BACKLIGHT_PIN 9 // LCD backlight (optional) #define RELAY_INPUT2_SEL_PIN 10 // low or high #define RELAY_INPUT1_SEL_PIN 11 // low or high #define RELAY_FILTER_SEL_PIN 12 // low, high, high-z #define LED_PIN 13 #define RELAY_ANTICLIP_SEL_PIN 14 // low, high-z // LiquidCrystal display with: // rs on pin 2 // (rw on pin DUMMY) // enable on pin 3 // d4, d5, d6, d7 on pins 4, 5, 6, 7 //LiquidCrystal lcd(2, 3, 3, 4, 5, 6, 7); LiquidCrystal lcd(LCD_RS_PIN, 0, LCD_ENABLE_PIN, LCD_DB4_PIN, LCD_DB5_PIN, LCD_DB6_PIN, LCD_DB7_PIN); // input selector { enum => relay } matrix mapping #define MAX_INPUTS 2 struct _input_sel_mapping { char short_name[10]; char long_name[17]; byte relay1; } input_sel_mapping[MAX_INPUTS] = { { "1 [Coax]", "Popcorn Hour ", 1 } , { "2 [Opto]", "PC (y1) ", 0 } }; /* * eeprom locations (slot numbers in eeprom address space) */ #define EEPROM_MAGIC 0 #define EEPROM_POWER 1 #define EEPROM_INPUT_SEL 2 #define EEPROM_FILTER_SEL 3 #define EEPROM_ANTICLIP_SEL 4 #define EEPROM_BACKLIGHT_LEVEL 5 // current working level #define EEPROM_BACKLIGHT_MIN 6 // our 'dimest' setting #define EEPROM_BACKLIGHT_MAX 7 // our 'brightest' setting // decode the value of eeprom[ EEPROM_INPUT_SEL ] #define INPUT_SEL_COAX1 0 #define INPUT_SEL_TOSLINK1 1 #define FILTER_SEL_A 0 #define FILTER_SEL_B 1 #define FILTER_SEL_C 2 #define ANTICLIP_SEL_OFF 0 #define ANTICLIP_SEL_ON 1 #define DEFAULT_BL_LEVEL 255 /* * globals (that may init from EEPROM at power-up) */ byte power; byte backlight_currently_on=1; // boolean byte backlight_level=DEFAULT_BL_LEVEL; // color-independant 'intensity' // 'selector' switches byte input_selector=0; byte filter_selector=0; byte anticlip_selector=0; byte toplevel_mode=0; // 0=main 'use' mode, 1=edit_vol_window mode /* * lcd physical constants */ #define LCD_PHYS_ROWS 16 #define LCD_PHYS_LINES 2 // char cell 'memory map' locations for start of line1 and line2 on the lcd #define LCD_CURS_POS_L1_HOME 0x80 #define LCD_CURS_POS_L2_HOME 0xC0 #define LCD_CURS_POS_L3_HOME 0x94 #define LCD_CURS_POS_L4_HOME 0xD4 /*********************************** * start of main C code * **********************************/ void setup() { delay(200); // Let everything wake up /* * startup the core function of our system */ init_system(); } void loop() { while(1) { top_scan(); } } void draw_selector_string() { lcd.clear(); // input selector strings if (input_selector == INPUT_SEL_COAX1) { LCD_send_string(input_sel_mapping[0].short_name, LCD_CURS_POS_L1_HOME); LCD_send_string(input_sel_mapping[0].long_name, LCD_CURS_POS_L2_HOME); } else if (input_selector == INPUT_SEL_TOSLINK1) { LCD_send_string(input_sel_mapping[1].short_name, LCD_CURS_POS_L1_HOME); LCD_send_string(input_sel_mapping[1].long_name, LCD_CURS_POS_L2_HOME); } // anticlip icon lcd.command(LCD_CURS_POS_L1_HOME + 10); // position cursor if (anticlip_selector == ANTICLIP_SEL_OFF) { LCD_send_string("[.]", 0); } else if (anticlip_selector == ANTICLIP_SEL_ON) { LCD_send_string("[C]", 0); } else { LCD_send_string("[ ]", 0); } // filter icon lcd.command(LCD_CURS_POS_L1_HOME + 14); // position cursor if (filter_selector == FILTER_SEL_A) { LCD_send_string("Fa", 0); } else if (filter_selector == FILTER_SEL_B) { LCD_send_string("Fb", 0); } else if (filter_selector == FILTER_SEL_C) { LCD_send_string("Fc", 0); } else { LCD_send_string(" ", 0); } } void do_relay_Input_selection() { if (input_selector == INPUT_SEL_COAX1) { pinMode(RELAY_INPUT1_SEL_PIN, OUTPUT); pinMode(RELAY_INPUT2_SEL_PIN, OUTPUT); digitalWrite(RELAY_INPUT2_SEL_PIN, LOW); // low digitalWrite(RELAY_INPUT1_SEL_PIN, HIGH); // high } else if (input_selector == INPUT_SEL_TOSLINK1) { pinMode(RELAY_INPUT1_SEL_PIN, OUTPUT); pinMode(RELAY_INPUT2_SEL_PIN, OUTPUT); digitalWrite(RELAY_INPUT2_SEL_PIN, HIGH); // high digitalWrite(RELAY_INPUT1_SEL_PIN, LOW); // low } } void do_relay_Filter_selection() { if (filter_selector == FILTER_SEL_A) { pinMode(RELAY_FILTER_SEL_PIN, OUTPUT); delay(5); digitalWrite(RELAY_FILTER_SEL_PIN, LOW); // low } else if (filter_selector == FILTER_SEL_B) { pinMode(RELAY_FILTER_SEL_PIN, OUTPUT); delay(5); digitalWrite(RELAY_FILTER_SEL_PIN, HIGH); // high } else if (filter_selector == FILTER_SEL_C) { pinMode(RELAY_FILTER_SEL_PIN, INPUT); // open (high-Z) delay(5); } } void do_relay_Anticlip_selection() { if (anticlip_selector == ANTICLIP_SEL_OFF) { pinMode(RELAY_ANTICLIP_SEL_PIN, OUTPUT); delay(5); digitalWrite(RELAY_ANTICLIP_SEL_PIN, LOW); // lo } else if (anticlip_selector == ANTICLIP_SEL_ON) { pinMode(RELAY_ANTICLIP_SEL_PIN, INPUT); // open (high-Z) delay(5); } } void change_input_selector(byte write_to_eeprom_flag) { // update the input/output selector string names draw_selector_string(); if (write_to_eeprom_flag != 0) { // save the new input selector in EEPROM EEwrite(EEPROM_INPUT_SEL, input_selector); } // engage the right relays based on this new input-selector do_relay_Input_selection(); do_relay_Anticlip_selection(); do_relay_Filter_selection(); } void draw_filter_string() { draw_selector_string(); } void draw_anticlip_string() { draw_selector_string(); } void change_filter_selector(boolean write_to_eeprom_flag) { // update the input/output selector string names draw_filter_string(); if (write_to_eeprom_flag != 0) { // save the new input selector in EEPROM EEwrite(EEPROM_FILTER_SEL, filter_selector); } // engage the right relays based on this new input-selector do_relay_Filter_selection(); } void change_anticlip_selector(boolean write_to_eeprom_flag) { // update the input/output selector string names draw_anticlip_string(); if (write_to_eeprom_flag != 0) { // save the new input selector in EEPROM EEwrite(EEPROM_ANTICLIP_SEL, anticlip_selector); } // engage the right relays based on this new input-selector do_relay_Anticlip_selection(); } void top_scan() { //scan_front_panel_buttons(); // TBD /* * IR keyscan and case-statement routine */ if (toplevel_mode == 0) { // main 'everyday use' mode handle_IR_keys_normal_mode(); } else if (toplevel_mode == 2) { // backlight adjust if (power == 1) { // only allow this if power is on handle_IR_keys_edit_backlight_mode(); } } } void handle_IR_keys_normal_mode() { byte ms,ls; int key; /* * we got a valid IR start pulse! fetch the keycode, now. */ key = get_IR_key(); switch (key) { // no key pressed - quick return case 0: case -1: return; /* * 'immediate mode' method of adjusting brightness (backlight) via 'alternate arrow keys' */ case 1328: // previous if (power == 0) return; // not allowed if power is off if (backlight_level <= 1) { // our lower boundary, don't let user go below this! break; } else if (backlight_level <= 10) { // smaller steps at the extremes backlight_level--; } else if (backlight_level < (255-10)) { // larger steps in the middle backlight_level -= 2; } else { // smaller steps at the extremes backlight_level--; } analogWrite(PWM_BACKLIGHT_PIN, backlight_level); EEwrite(EEPROM_BACKLIGHT_LEVEL, backlight_level); delay(50); //Debounce switch break; case 1329: // next if (power == 0) return; // not allowed if power is off if (backlight_level > 254) { // our upper boundary, don't let user go above this! break; } else if (backlight_level <= 10) { // smaller steps at the extremes backlight_level++; } else if ( backlight_level < (255-10) ) { // larger steps in the middle backlight_level += 2; } else { // smaller steps at the extremes backlight_level++; } analogWrite(PWM_BACKLIGHT_PIN, backlight_level); EEwrite(EEPROM_BACKLIGHT_LEVEL, backlight_level); delay(50); //Debounce switch break; /* * toggle (cycle thru) the backlight display modes */ case 1364: // display if (power == 0) return; // not allowed if power is off if (backlight_currently_on == 1) { for (int bl=backlight_level; bl>=0; bl--) { analogWrite(PWM_BACKLIGHT_PIN, bl); // temporarily turn backlight entirely off (or go to its lowest 'low' setting) delay(2); } backlight_currently_on = 0; // flag it as now off } else { for (int bl=0; bl<=backlight_level; bl++) { analogWrite(PWM_BACKLIGHT_PIN, bl); // restore to normal brightness again delay(2); } backlight_currently_on = 1; // flag it as now on } delay(500); //Debounce switch break; case KEYSCAN_ROTATE_THRU_INPUTS: if (power == 0) return; // not allowed if power is off /* * rotate thru the values, circling back at the end */ if (input_selector == INPUT_SEL_COAX1) input_selector = INPUT_SEL_TOSLINK1; else if (input_selector == INPUT_SEL_TOSLINK1) input_selector = INPUT_SEL_COAX1; change_input_selector(1); delay(500); //Debounce switch break; /* * input selectors (restore last-used vol setting for THAT input) */ case KEYSCAN_COAX1: if (power == 0) return; // not allowed if power is off input_selector = INPUT_SEL_COAX1; change_input_selector(1); delay(500); //Debounce switch break; case KEYSCAN_TOSLINK1: if (power == 0) return; // not allowed if power is off input_selector = INPUT_SEL_TOSLINK1; change_input_selector(1); delay(500); //Debounce switch break; case KEYSCAN_CLIP_ON: /* keypad 9*/ if (power == 0) return; // not allowed if power is off anticlip_selector = ANTICLIP_SEL_ON; change_anticlip_selector(1); delay(500); // Debounce switch break; case KEYSCAN_CLIP_OFF: /* keypad 0 */ if (power == 0) return; // not allowed if power is off anticlip_selector = ANTICLIP_SEL_OFF; change_anticlip_selector(1); delay(500); // Debounce switch break; case KEYSCAN_FILT_A: /* keypad 4 */ if (power == 0) return; // not allowed if power is off filter_selector = FILTER_SEL_A; change_filter_selector(1); delay(500); // Debounce switch break; case KEYSCAN_FILT_B: /* keypad 5 */ if (power == 0) return; // not allowed if power is off filter_selector = FILTER_SEL_B; change_filter_selector(1); delay(500); // Debounce switch break; case KEYSCAN_FILT_C: /* keypad 6 */ if (power == 0) return; // not allowed if power is off filter_selector = FILTER_SEL_C; change_filter_selector(1); delay(500); // Debounce switch break; /* * power button */ case KEYSCAN_POWER: if (power == 0) { // power was in the 'off' or 'standby' state power = 1; // we're not power=on EEwrite(EEPROM_POWER, power); toplevel_mode = 0; signon_msg(); if (backlight_currently_on == 0) { analogWrite(PWM_BACKLIGHT_PIN, 0); // respect user's backlight 'display' setting } change_input_selector(1); // power was ON when user pressed the power button } else { // save the new input selector in EEPROM EEwrite(EEPROM_INPUT_SEL, input_selector); analogWrite(PWM_BACKLIGHT_PIN, backlight_level); // restore to normal brightness again lcd.clear(); LCD_send_string("(Powering Off)", LCD_CURS_POS_L1_HOME); analogWrite(PWM_BACKLIGHT_PIN, 0); // respect user's backlight 'display' setting backlight_currently_on = 1; // flag it as now on for when we finally power-up again power = 0; EEwrite(EEPROM_POWER, power); lcd.clear(); LCD_turn_display_off(); } delay(500); break; /* * go from 'main use mode' to 'edit backlight' mode */ case 1333: /* shuffle */ // 1381 'angle' button if (power == 0) return; // not allowed if power is off toplevel_mode = 2; lcd.clear(); //LCD_cgram_load_boxed_bargraph(); LCD_send_string("Brightness", LCD_CURS_POS_L1_HOME); draw_backlight_labels(); delay(500); // Debounce switch break; // debug, to show logo for a long time (photos) ;) case 1381: // angle button if (power == 0) return; // not allowed if power is off lcd.clear(); signon_msg(); // loop on the IR scanner until we see the enter button while (1) { key = get_IR_key(); if ( (key == 0) || (key == -1) ) continue; if (key == KEYSCAN_ARROW_ENTER) break; } if (backlight_currently_on == 0) { analogWrite(PWM_BACKLIGHT_PIN, 0); // respect user's backlight 'display' setting } change_input_selector(1); delay(200); break; } // switch } int get_IR_key() { int data[SONY_IR_PROTO_PULSE_TRAIN_LEN+1]; byte i; int result = 0; int seed = 1; if (pulseIn(IR_PIN, LOW, PULSE_DUR) < PULSE_2200) { return -1; } for (i=0; i bin_1) { // is it a 1? data[i] = 1; } else { if (data[i] > bin_0) { // is it a 0? data[i] = 0; } else { data[i] = 2; // Flag the data as invalid; I don't know what it is! } } } for (i=0; i 1) { return -1; // Return -1 on invalid data } } for (i=0; i> 4) + '0'; *ls = (val & 0x0f) + '0'; } void bin2ascii(const byte val, byte* ms, byte* ls) { *ms = val / 10 + '0'; *ls = val % 10 + '0'; } byte bcd2bin(const byte val) { return (((val >> 4) * 10) + (val & 0x0f)); } byte bin2bcd(const byte val) { return ((val / 10 * 16) + (val % 10)); } // ==================== LCD Functions =====================================// void clear_line(byte l_num) { if (l_num == 1) { lcd.command(LCD_CURS_POS_L1_HOME); } else { lcd.command(LCD_CURS_POS_L2_HOME); } for (int i=0; i 0, or cursor position if addr == 0 if (addr != 0) { lcd.command(addr); } byte i = 0; while (str[i] != 0) { lcd.write(str[i++]); } } void draw_backlight_labels() { update_backlight_level(); } void update_backlight_level() { byte ls, ms; lcd.command(LCD_CURS_POS_L2_HOME+0); // red bin2ascii(backlight_level / 100, &ms, &ls); //lcd.write(ms); lcd.write(ls); bin2ascii(backlight_level % 100, &ms, &ls); lcd.write(ms); lcd.write(ls); analogWrite(PWM_BACKLIGHT_PIN, backlight_level); } void handle_IR_keys_edit_backlight_mode() { byte ms,ls; int key=0; /* * we got a valid IR start pulse! fetch the keycode, now. */ key = get_IR_key(); switch (key) { // no key pressed - quick return case 0: case -1: return; case 1314: // scan_left if (backlight_level <= 1) { // our lower boundary, don't let user go below this! break; } else if (backlight_level <= 10) { // smaller steps at the extremes backlight_level--; } else if (backlight_level < (255-10)) { // larger steps in the middle backlight_level -= 2; } else { // smaller steps at the extremes backlight_level--; } update_backlight_level(); delay(50); //Debounce switch break; case 1315: // slow_right if (backlight_level >= 255) { // our upper boundary, don't let user go above this! break; } else if (backlight_level <= 10) { // smaller steps at the extremes backlight_level++; } else if ( backlight_level < (255-10) ) { // larger steps in the middle backlight_level += 2; // 8 } else { // smaller steps at the extremes backlight_level++; } update_backlight_level(); delay(50); //Debounce switch break; case 1381: // 'angle' button case 1294: // 'return' case KEYSCAN_ARROW_ENTER: toplevel_mode = 0; // we're done in this edit mode // save our working values to EEPROM EEwrite(EEPROM_BACKLIGHT_LEVEL, backlight_level); /* * redraw the screen in '0' (main) mode */ if (backlight_currently_on == 0) { // if BL was turned off via 'display' then leave it off before we return! analogWrite(PWM_BACKLIGHT_PIN, 0); } // line1 clear_L1(); draw_selector_string(); delay(200); //Debounce switch break; } // switch } // end sketch