TUNER_MINUS (guessed), --racecondition where delay was skipped, whitespace
[svn42.git] / rf433ctl / rf433ctl.pde
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
3 #include <inttypes.h>
4 #include <OneWire.h>
5 #include <DallasTemperature.h>
6 #include <IRremote.h>
7
8 //********************************************************************//
9
10 #define RF_DATA_OUT_PIN 13
11 #define IR_MOVEMENT_PIN 9
12 #define ONE_WIRE_PIN 8
13 #define PANIC_BUTTON_PIN 7
14 #define PANICLED_PWM_PIN 6
15 #define BLUELED_PWM_PIN 11
16 #define PHOTO_ANALOGPIN 0
17 //movement is reported if during IR_SAMPLE_DURATION at least IR_TRESHOLD ir signals are detectd
18 #define IR_SAMPLE_DURATION 12000
19 #define IR_TRESHOLD 10000
20 //duration PanicButton needs to be pressed before status change occurs (i.e. for two PanicButton Reports, the buttons needs to be pressed 1000 cycles, releases 1000 cycles and again pressed 1000 cycles)
21 #define PB_TRESHOLD 1000
22 #define PHOTO_SAMPLE_INTERVAL 4000
23 #define IRREMOTE_SEND_PIN 3   //hardcoded in library
24 //WARNING IRremote Lib uses TCCR2
25
26 OneWire  onewire(ONE_WIRE_PIN);
27 DallasTemperature dallas_sensors(&onewire);
28 DeviceAddress onShieldTemp = { 0x10, 0xE7, 0x77, 0xD3, 0x01, 0x08, 0x00, 0x3F };
29 IRsend irsend; 
30 #define TEMPC_OFFSET_ARDUINO_GENEREATED_HEAT 
31
32 //********************************************************************//
33 // IR Codes, 32 bit, NEC
34 const int YAMAHA_CODE_BITS = 32;
35 const unsigned long int YAMAHA_CODE_BASE = 0x0000000005EA10000;
36
37 const char YAMAHA_POWER_TOGGLE =0xF8; //Power On/Off
38 const char YAMAHA_POWER_OFF =0x78; //Power Off !!!
39 const char YAMAHA_SLEEP =0xEA; //Toggle Sleep 120/90/60/30min or Off
40
41 const char YAMAHA_CD =0xA8; //Input CD
42 const char YAMAHA_TUNER =0x68; //Input Tuner
43 const char YAMAHA_TAPE =0x18; //Input Toggle Tape/CD
44 const char YAMAHA_DVD_SPDIF =0xE8; //Input Toggle DVD Auto / DVD Analog
45 const char YAMAHA_SAT_SPDIFF =0x2A; //Input Toggle Sat-DTV Auto / Sat-DTV Analog
46 const char YAMAHA_AUX =0xAA;  //Input AUX (mode)
47 const char YAMAHA_VCR =0xF0; //Input VCR
48 const char YAMAHA_EXT51DEC =0xE1; //Input Ext. Decoder On/Off
49
50 const char YAMAHA_TUNER_PLUS =0x08; //Tuner Next Station 1-7  (of A1 - E7)
51 const char YAMAHA_TUNER_MINUS =0x88; //Tuner Prev Station 1-7  (of A1 - E7)
52 const char YAMAHA_TUNER_ABCDE =0x48; //Tuner Next Station Row A-E (of A1 - E7)
53
54 const char YAMAHA_MUTE =0x38;
55 const char YAMAHA_VOLUME_UP =0x58;
56 const char YAMAHA_VOLUME_DOWN =0xD8;
57
58 //const char YAMAHA_FRONT_LEVEL_P =0x01;  //no function
59 //const char YAMAHA_FRONT_LEVEL_M =0x81; //no function
60 //const char YAMAHA_CENTRE_LEVEL_P =0x41;  //no function
61 //const char YAMAHA_CENTRE_LEVEL_M =0xC1; //no function
62 //const char YAMAHA_REAR_LEVEL_P =0x7A; //no function
63 //const char YAMAHA_REAR_LEVEL_M =0xFA; //no function
64 const char YAMAHA_PLUS =0x4A;  //unteres Steuerkreuz: Taste Rechts (Plus)
65 const char YAMAHA_MINUS =0xCA; //unteres Steuerkreuz: Taste Links (Minus)
66 const char YAMAHA_MENU =0x39; // Menu: Settings
67 const char YAMAHA_TEST =0xA1; // Test Sounds
68 const char YAMAHA_TIME_LEVEL =0x19; //Settings for Delay, Subwfs, Right Surround, Left Surround, Center
69 const char YAMAHA_TIME_LEVEL2 =0x61; //(also) Settings for Delay, Subwfs, Right Surround, Left Surround, Center
70 const char YAMAHA_TIME_LEVEL3 =0x99; //(also) Settings for Delay, Subwfs, Right Surround, Left Surround, Center
71
72 const char YAMAHA_EFFECT_TOGGLE =0x6A; //Effect Toggle On/Off
73 const char YAMAHA_PRG_DOWN =0x9A; //Effect/DSP Programm Toggle in down direction
74 const char YAMAHA_PRG_UP =0x1A; //Effect/DSP Programm Toggle in up direction
75 const char YAMAHA_EFFECT1 =0x31; //Effect TV Sports
76 const char YAMAHA_EFFECT2 =0x71; //Effect Rock Concert
77 const char YAMAHA_EFFECT3 =0xB1;  //Effect Disco
78 const char YAMAHA_EFFECT4 =0xD1;  //Mono Movie
79 const char YAMAHA_EFFECT5 =0x91; //Effect Toggle 70mm Sci-Fi / 70mm Spectacle
80 const char YAMAHA_EFFECT6 =0x51; //Effect Toggle 70mm General / 70mm Adventure
81 const char YAMAHA_P5 =0xFB; //P5 PRT (1 Main Bypass)? (1587674115)
82
83
84 void send_yamaha_ir_signal(char codebyte)
85 {
86   unsigned long int code = codebyte & 0xFF;
87   code <<= 8;
88   code |= (0xff ^ codebyte) & 0xFF;
89   code |= YAMAHA_CODE_BASE;
90   irsend.sendNEC(code,YAMAHA_CODE_BITS);
91   Serial.println("Ok");  
92 }
93
94 //********************************************************************//
95
96 typedef struct {
97   byte offset;
98   byte state;
99 } rf_bit_t;
100
101 // offset is number of alphas (0.08ms)
102
103 const rf_bit_t zero_bit[] = { {  4, 1 },
104                               { 16, 0 },
105                               { 20, 1 },
106                               { 32, 0 },
107                               {  0, 0 } };
108
109 const rf_bit_t one_bit[] = { { 12, 1 },
110                              { 16, 0 },
111                              { 28, 1 },
112                              { 32, 0 },
113                              {  0, 0 } };
114
115 const rf_bit_t float_bit[] = { {  4, 1 },
116                                { 16, 0 },
117                                { 28, 1 },
118                                { 32, 0 },
119                                {  0, 0 } };
120
121 const rf_bit_t sync_bit[] = { {   4, 1 },
122                               { 128, 0 },
123                               {   0, 0 } };
124
125 typedef enum { ZERO = 0, ONE , FLOAT , SYNC } adbit_t;
126 typedef byte ad_bit_t;
127 #define WORD_LEN 13
128 typedef ad_bit_t word_t[WORD_LEN];
129
130 const rf_bit_t* bit_defs[] = { zero_bit, one_bit, float_bit, sync_bit };
131
132 byte alpha_cnt = 0;
133 byte bit_cnt = 0;
134 byte chunk_cnt = 0;
135 byte word_cnt = 0;
136 const ad_bit_t* current_word;
137 byte volatile frame_finished = 1;
138
139 #define FRAME_LEN 8
140
141 #define A1_ON  0
142 #define A1_OFF 1
143 #define A2_ON  2
144 #define A2_OFF 3
145
146 #define B1_ON  4
147 #define B1_OFF 5
148 #define B2_ON  6
149 #define B2_OFF 7
150
151 #define C1_ON  8
152 #define C1_OFF 9
153 #define C2_ON  10
154 #define C2_OFF 11
155
156 #define D1_ON  12
157 #define D1_OFF 13
158 #define D2_ON  14
159 #define D2_OFF 15
160
161 const word_t words[]  = { 
162 { ZERO,  ZERO,  FLOAT, FLOAT, ZERO,  ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, FLOAT, SYNC }, // A1_ON
163 { ZERO,  ZERO,  FLOAT, FLOAT, ZERO,  ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO,  SYNC }, // A1_OFF
164 { ZERO,  ZERO,  FLOAT, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, FLOAT, SYNC }, // A2_ON
165 { ZERO,  ZERO,  FLOAT, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO,  SYNC }, // A2_OFF
166
167 { FLOAT, ZERO,  FLOAT, FLOAT, ZERO,  ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, FLOAT, SYNC }, // B1_ON
168 { FLOAT, ZERO,  FLOAT, FLOAT, ZERO,  ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO,  SYNC }, // B1_OFF
169 { FLOAT, ZERO,  FLOAT, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, FLOAT, SYNC }, // B2_ON
170 { FLOAT, ZERO,  FLOAT, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO,  SYNC }, // B2_OFF
171
172 { ZERO,  FLOAT, FLOAT, FLOAT, ZERO,  ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, FLOAT, SYNC }, // C1_ON
173 { ZERO,  FLOAT, FLOAT, FLOAT, ZERO,  ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO,  SYNC }, // C1_OFF
174 { ZERO,  FLOAT, FLOAT, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, FLOAT, SYNC }, // C2_ON
175 { ZERO,  FLOAT, FLOAT, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO,  SYNC }, // C2_OFF
176
177 { FLOAT, FLOAT, FLOAT, FLOAT, ZERO,  ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, FLOAT, SYNC }, // D1_ON
178 { FLOAT, FLOAT, FLOAT, FLOAT, ZERO,  ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO,  SYNC }, // D1_OFF
179 { FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, FLOAT, SYNC }, // D2_ON
180 { FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO, FLOAT, FLOAT, ZERO,  SYNC }  // D2_OFF
181 };
182
183
184 //********************************************************************//
185
186 void start_timer()
187 {
188   // timer 1: 2 ms
189   TCCR1A = 0;                    // prescaler 1:8, WGM = 4 (CTC)
190   TCCR1B = 1<<WGM12 | 1<<CS11;   // 
191   OCR1A = 159;        // (1+159)*8 = 1280 -> 0.08ms @ 16 MHz -> 1*alpha
192 //  OCR1A = 207;        // (1+207)*8 = 1664 -> 0.104ms @ 16 MHz -> 1*alpha
193   TCNT1 = 0;          // reseting timer
194   TIMSK1 = 1<<OCIE1A; // enable Interrupt
195 }
196
197 void stop_timer() // stop the timer
198 {
199   // timer1
200   TCCR1B = 0; // no clock source
201   TIMSK1 = 0; // disable timer interrupt
202 }
203
204 void init_word(const word_t w)
205 {
206   current_word = w;
207   alpha_cnt = 0;
208   chunk_cnt = 0;
209   bit_cnt = 0;
210
211   if(bit_defs[current_word[bit_cnt]][chunk_cnt].state)
212     digitalWrite(RF_DATA_OUT_PIN, LOW); //neue 12V MosFET Verstärkung invertiert Logik !
213   else
214     digitalWrite(RF_DATA_OUT_PIN, HIGH);
215
216   start_timer();
217 }
218
219 ISR(TIMER1_COMPA_vect)
220 {
221   alpha_cnt++;
222   if(alpha_cnt < bit_defs[current_word[bit_cnt]][chunk_cnt].offset)
223     return;
224
225   chunk_cnt++;
226   if(bit_defs[current_word[bit_cnt]][chunk_cnt].offset != 0) {
227     if(bit_defs[current_word[bit_cnt]][chunk_cnt].state)
228       digitalWrite(RF_DATA_OUT_PIN, LOW); //neue 12V MosFET Verstärkung invertiert Logik !
229     else
230       digitalWrite(RF_DATA_OUT_PIN, HIGH);
231     return;
232   }
233   
234   bit_cnt++;
235   if(bit_cnt < WORD_LEN) {
236     alpha_cnt = 0;
237     chunk_cnt = 0;
238     if(bit_defs[current_word[bit_cnt]][chunk_cnt].state)
239       digitalWrite(RF_DATA_OUT_PIN, LOW); //neue 12V MosFET Verstärkung invertiert Logik !
240     else
241       digitalWrite(RF_DATA_OUT_PIN, HIGH);
242     return;
243   }
244   stop_timer();
245   digitalWrite(RF_DATA_OUT_PIN, HIGH);
246
247   word_cnt++;
248   if(word_cnt < FRAME_LEN)
249     init_word(current_word);
250   else
251     frame_finished = 2;
252 }
253
254 //***********//
255
256
257 void send_frame(const word_t w)
258 {
259   if (frame_finished != 1)
260     for(;;) //wait until sending of previous frame finishes
261       if (frame_finished)
262       {
263         delay(150);
264         break;
265       }
266   word_cnt = 0;
267   frame_finished = 0;
268   init_word(w);
269 }
270
271 void check_frame_done()
272 {
273   if (frame_finished==2)
274   {
275     Serial.println("Ok");
276     frame_finished=1;
277     delay(120);
278   }
279 }
280
281 //********************************************************************//
282
283 void printTemperature(DeviceAddress deviceAddress)
284 {
285   dallas_sensors.requestTemperatures();
286   float tempC = dallas_sensors.getTempC(deviceAddress);
287   //Serial.print("Temp C: ");
288   Serial.println(tempC TEMPC_OFFSET_ARDUINO_GENEREATED_HEAT);
289   //Serial.print(" Temp F: ");
290   //Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
291 }
292
293 //********************************************************************//
294
295 unsigned int light_level_mean_ = 0;
296 unsigned int light_sample_time_ = 0;
297
298 void updateLightLevel(unsigned int pin)
299 {
300   light_sample_time_++;
301   if (light_sample_time_ < PHOTO_SAMPLE_INTERVAL)
302     return;
303   light_sample_time_ = 0;
304   
305   unsigned int value = analogRead(pin);
306   if (value == light_level_mean_)
307     return;
308   
309   unsigned int diff = abs(value - light_level_mean_);
310   if (diff > 100)
311     light_level_mean_ = value;
312   else
313       light_level_mean_=(unsigned int) ( ((float) light_level_mean_) * 0.90 + ((float)value)*0.10 );
314 }
315
316 void printLightLevel()
317 {
318   //Serial.print("Photo: ");
319   Serial.println(light_level_mean_);
320 }
321
322 //********************************************************************//
323
324 unsigned long wm_start_=0;
325 bool wait_millis(unsigned long ms)
326 {
327   if (wm_start_ > 0)
328   {
329     if (millis() < wm_start_ || millis() > wm_start_+ ms)
330     {
331       wm_start_=0;
332       return false;
333     }
334     else
335       return true;
336   }
337   else
338   {
339     wm_start_=millis();
340     return true;
341   }
342 }
343
344 unsigned int flash_led_time_=0;
345 unsigned int flash_led_brightness_=255;
346 unsigned int flash_led_delay_=8;
347 unsigned int flash_led_selected_=0;
348 void calculate_led_level()
349 {
350   if (flash_led_time_ == 0 || flash_led_selected_ == 0)
351     return;
352   if (wait_millis(flash_led_delay_))
353     return;
354   flash_led_time_--;
355   int c = abs(sin(float(flash_led_time_) / 100.0)) * flash_led_brightness_;
356   //int d = abs(sin(float(flash_led_time_) / 100.0)) * flash_led_brightness_;
357   if (flash_led_selected_ && (1 << BLUELED_PWM_PIN))
358     analogWrite(BLUELED_PWM_PIN, 255-c);
359   else
360     analogWrite(BLUELED_PWM_PIN,255); //off
361   if (flash_led_selected_ && (1 << PANICLED_PWM_PIN))
362   {
363     if (flash_led_time_)
364       analogWrite(PANICLED_PWM_PIN, c);
365     else
366       analogWrite(PANICLED_PWM_PIN, 255-c);
367   }
368   else
369     analogWrite(PANICLED_PWM_PIN,255); //off
370 }
371
372 void flash_led(unsigned int times, unsigned int brightness_divisor, unsigned int delay_divisor, unsigned int led_selector)
373 {
374   unsigned int new_flash_led_brightness = 255;
375   unsigned int new_flash_led_delay = 8;
376   flash_led_selected_=led_selector;
377   if (times == 0 || led_selector == 0)
378   {
379     analogWrite(PANICLED_PWM_PIN,255); //off
380     analogWrite(BLUELED_PWM_PIN,255); //off
381     return;
382   }
383   if (brightness_divisor > 1) //guard against div by zero
384     new_flash_led_brightness /= brightness_divisor;
385   if (delay_divisor > 1)  //guard against div by zero
386     new_flash_led_delay /= delay_divisor;
387   if (flash_led_time_ == 0 || new_flash_led_brightness > flash_led_brightness_)
388     flash_led_brightness_=new_flash_led_brightness;
389   if (flash_led_time_ == 0 || new_flash_led_delay < flash_led_delay_)
390     flash_led_delay_=new_flash_led_delay;
391   flash_led_time_ += 314*times;
392 }
393
394 //********************************************************************//
395
396 void setup()
397 {
398   pinMode(RF_DATA_OUT_PIN, OUTPUT);
399   digitalWrite(RF_DATA_OUT_PIN, HIGH);
400   pinMode(IR_MOVEMENT_PIN, INPUT);      // set pin to input
401   digitalWrite(IR_MOVEMENT_PIN, LOW);  // turn off pulldown resistors  
402   pinMode(PANIC_BUTTON_PIN, INPUT);      // set pin to input
403   digitalWrite(PANIC_BUTTON_PIN, LOW);  // turn on pulldown resistors 
404   analogWrite(PANICLED_PWM_PIN,255);
405   analogWrite(BLUELED_PWM_PIN,255); //pwm sink(-) instead of pwm + (better for mosfets)
406   pinMode(IRREMOTE_SEND_PIN, OUTPUT);
407   digitalWrite(IRREMOTE_SEND_PIN, HIGH);
408   
409   Serial.begin(9600);
410   
411   onewire.reset();
412   onewire.reset_search();
413   dallas_sensors.begin();
414   //in case we change temp sensor:
415   if (!dallas_sensors.getAddress(onShieldTemp, 0)) 
416     Serial.println("Error: Unable to find address for Device 0"); 
417   dallas_sensors.setResolution(onShieldTemp, 9);  
418 }
419
420 unsigned int ir_time=IR_SAMPLE_DURATION;
421 unsigned int ir_count=0;
422 boolean pb_last_state=0;
423 boolean pb_state=0;
424 boolean pb_postth_state=0;
425 unsigned int pb_time=0;
426
427 void sensorEchoCommand(char command)
428 {
429   Serial.print("Sensor ");
430   Serial.print(command);
431   Serial.print(": ");
432 }
433
434 void loop()
435 {
436   ir_time--;
437   ir_count += (digitalRead(IR_MOVEMENT_PIN) == HIGH);
438
439   if (pb_time < PB_TRESHOLD)
440     pb_time++;
441   pb_state=(digitalRead(PANIC_BUTTON_PIN) == HIGH);
442   
443   if (ir_time == 0)
444   {
445     if (ir_count >= IR_TRESHOLD)
446     {
447       flash_led(1, 8, 1, (1<<BLUELED_PWM_PIN) );
448       Serial.println("movement");
449     }
450     ir_time=IR_SAMPLE_DURATION;
451     ir_count=0;
452   }
453   
454   if (pb_state == pb_last_state && pb_time >= PB_TRESHOLD)
455   {
456     if (pb_state && ! pb_postth_state)
457     {   
458       pb_postth_state=1;
459       Serial.println("PanicButton");
460       flash_led(14, 1, 2, (1<<BLUELED_PWM_PIN)|(1<<PANICLED_PWM_PIN) );
461     }
462     else if (!pb_state)
463       pb_postth_state=0;
464   }
465   else if (pb_state != pb_last_state)
466   {
467     pb_time=0;
468     pb_last_state=pb_state;
469   }
470   
471   updateLightLevel(PHOTO_ANALOGPIN);
472   calculate_led_level();
473   check_frame_done();
474   
475   if(Serial.available()) {
476     char command = Serial.read();
477     
478     if(command == 'A')
479       send_frame(words[A1_ON]);
480     else if(command == 'a')
481       send_frame(words[A1_OFF]);
482     else if(command == 'B')
483       send_frame(words[A2_ON]);
484     else if(command == 'b')
485       send_frame(words[A2_OFF]);
486
487     else if(command == 'C')
488       send_frame(words[B1_ON]);
489     else if(command == 'c')
490       send_frame(words[B1_OFF]);
491     else if(command == 'D')
492       send_frame(words[B2_ON]);
493     else if(command == 'd')
494       send_frame(words[B2_OFF]);
495
496     else if(command == 'E')
497       send_frame(words[C1_ON]);
498     else if(command == 'e')
499       send_frame(words[C1_OFF]);
500     else if(command == 'F')
501       send_frame(words[C2_ON]);
502     else if(command == 'f')
503       send_frame(words[C2_OFF]);
504
505     else if(command == 'G')
506       send_frame(words[D1_ON]);
507     else if(command == 'g')
508       send_frame(words[D1_OFF]);
509     else if(command == 'H')
510       send_frame(words[D2_ON]);
511     else if(command == 'h')
512       send_frame(words[D2_OFF]);
513     else if(command == 'T')
514     {
515       sensorEchoCommand(command);
516       printTemperature(onShieldTemp);
517     }
518     else if(command == 'P')
519     {
520       sensorEchoCommand(command);
521       printLightLevel();
522     }
523     else if (command == '^')
524       flash_led(1, 2, 1, (1 << PANICLED_PWM_PIN));
525     else if (command == '0')
526       send_yamaha_ir_signal(YAMAHA_POWER_OFF);
527     else if (command == '1')
528       send_yamaha_ir_signal(YAMAHA_POWER_TOGGLE);
529     else if (command == '2')
530       send_yamaha_ir_signal(YAMAHA_VOLUME_UP);
531     else if (command == '3')
532       send_yamaha_ir_signal(YAMAHA_VOLUME_DOWN);
533     else if (command == '4')
534       send_yamaha_ir_signal(YAMAHA_MUTE);
535     else if (command == '5')
536       send_yamaha_ir_signal(YAMAHA_CD);
537     else if (command == '6')
538       send_yamaha_ir_signal(YAMAHA_TUNER);
539     else if (command == '7')
540       send_yamaha_ir_signal(YAMAHA_DVD_SPDIF);
541     else if (command == '8')
542       send_yamaha_ir_signal(YAMAHA_MENU);
543     else if (command == '+')
544       send_yamaha_ir_signal(YAMAHA_PLUS);
545     else if (command == '-')
546       send_yamaha_ir_signal(YAMAHA_MINUS);
547     else if (command == 0xa7) // §
548       send_yamaha_ir_signal(YAMAHA_TEST);
549     else if (command == '$')
550       send_yamaha_ir_signal(YAMAHA_TIME_LEVEL);
551     else if (command == '%')
552       send_yamaha_ir_signal(YAMAHA_EFFECT_TOGGLE);
553     else if (command == '&')
554       send_yamaha_ir_signal(YAMAHA_PRG_DOWN);
555     else if (command == '/')
556       send_yamaha_ir_signal(YAMAHA_PRG_UP);
557     else if (command == '(')
558       send_yamaha_ir_signal(YAMAHA_TUNER_PLUS);
559     else if (command == '[')
560       send_yamaha_ir_signal(YAMAHA_TUNER_MINUS);
561     else if (command == ')')
562       send_yamaha_ir_signal(YAMAHA_TUNER_ABCDE);
563     else if (command == '9')
564       send_yamaha_ir_signal(YAMAHA_TAPE);
565     else if (command == '?')
566       send_yamaha_ir_signal(YAMAHA_VCR);
567     else if (command == '=')
568       send_yamaha_ir_signal(YAMAHA_EXT51DEC);
569     else
570       Serial.println("Error: unknown command");
571   }
572 }