addrutil: Re-indent.
[wk-misc.git] / heating-control.c
1 /* heating-control.c - heating control for an ATmega32.
2    (c) Holger Buss
3    Copyright (C) 2010 Werner Koch
4
5    FIXME:  Figure out copyright and license stuff.
6
7    All new code (Werner Koch) shall be under the GPLv3+.
8
9
10    Original notice:
11    *********************************************
12    Project : Heizung ATmega32
13    Version : 2.0
14    Date    : 09.12.2003  // AT90C8535
15    Date    : 08.01.2006  // ATMEGA32
16    Date    : 29.11.2006  // Fehler im Taktgenerator (Uhr ging falsch)
17    Author  : (c) Holger Buss
18    http://www.mikrocontroller.com --> Bauanleitungen
19    Chip type           : ATmega32
20    Program type        : Application
21    Clock frequency     : 8.000000 MHz
22    Memory model        : Small
23    External SRAM size  : 0
24    Data Stack size     : 512
25    *********************************************
26
27
28    2010-02-08 wk  Almost complete rewrite to allow building with
29                   avr-gcc.  Changed the source language to English as
30                   a convenience to non-German speaking hackers.  The
31                   LCD code has been written from scratch.
32
33    2010-09-05 wk  Change meaning of the day relay - now used to lit the
34                   display.
35
36    2010-09-20 wk  Change UART output to an easer to parse format.  Use
37                   AT like commands to request data.
38
39   */
40
41
42 /*#define USE_TURN_PUSH*/
43 /*#define NO_LCD*/
44
45 /* Clock frequency in Hz. */
46 #define F_CPU 8000000UL
47
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <util/delay.h>
51 #include <avr/io.h>
52 #include <avr/pgmspace.h>
53 #include <avr/interrupt.h>
54 #include <avr/eeprom.h>
55 #include <avr/sleep.h>
56
57
58 #define DIM(v)               (sizeof(v)/sizeof((v)[0]))
59
60
61 /* Display definitions.  */
62 #define LCD_DDR          DDRC         /* DDR for the LCD pins.   */
63 #define LCD_PORT         PORTC        /* Port for the LCD pins.  */
64 #define LCD_PIN          PINC         /* Pin for the LCD pins.   */
65 #define LCD_RS_PIN       0            /* Pin # for RS.           */
66 #define LCD_RW_PIN       1            /* Pin # for RW.           */
67 #define LCD_E_PIN        2            /* Pin # for Enable line.  */
68 #define LCD_DATA0_PIN    4            /* Pin # for data bit 0.   */
69 #define LCD_DATA1_PIN    5            /* Pin # for data bit 1.   */
70 #define LCD_DATA2_PIN    6            /* Pin # for data bit 2.   */
71 #define LCD_DATA3_PIN    7            /* Pin # for data bit 3.   */
72 #define LCD_DATA_MASK    0b11110000   /* Mask for the data pins. */
73 #define LCD_PIN_MASK     0b11110111   /* Mask for all used pins. */
74
75 /* UART defs.  */
76 #define BAUD       9600ul
77 #define UBRR_VAL   ((F_CPU+8*BAUD)/(16*BAUD)-1)
78 #define BAUD_REAL  (F_CPU/(16*(UBRR_VAL+1)))
79 #define BAUD_ERROR ((1000*BAUD_REAL)/BAUD)
80 #if (BAUD_ERROR < 990 || BAUD_ERROR > 1010)
81 # error computed baud rate out of range
82 #endif
83
84
85 /* Time data.  */
86 typedef struct
87 {
88   unsigned int time;
89   unsigned char mode;
90 } timer_data_t;
91
92 timer_data_t ee_timer[65] EEMEM;
93
94 static inline unsigned int
95 get_timer_time (int idx)
96 {
97   return (idx < 0 || idx >= DIM(ee_timer))
98     ? 0 : eeprom_read_word (&ee_timer[idx].time);
99 }
100
101 static inline unsigned char
102 get_timer_mode (int idx)
103 {
104   return (idx < 0 || idx >= DIM(ee_timer))
105     ? 0 : eeprom_read_byte (&ee_timer[idx].mode);
106 }
107
108 static inline void
109 put_timer_time (int idx, uint16_t value)
110 {
111   if (idx >= 0 && idx < DIM (ee_timer))
112     eeprom_update_word (&ee_timer[idx].time, value);
113 }
114
115 static inline void
116 put_timer_mode (int idx, uint8_t value)
117 {
118   if (idx >= 0 && idx < DIM (ee_timer))
119     eeprom_update_byte (&ee_timer[idx].mode, value);
120 }
121
122
123 /* Toggled by the shift menu between 0 and 32. */
124 unsigned char ee_shift_offset EEMEM;
125 static inline uint8_t
126 get_shift_offset (void)
127 {
128   return eeprom_read_byte (&ee_shift_offset);
129 }
130 static inline void
131 put_shift_offset (uint8_t value)
132 {
133   eeprom_update_byte (&ee_shift_offset, value);
134 }
135
136 /* The number of characteristics we support; they are mapped one to
137    one to the operating modes.  */
138 #define N_CHARACTERISTICS 3
139
140 /* The maximum number of consumption log entries.  */
141 #define MAX_LOGGING 250
142
143 /* A ring buffer to store the consumption.  */
144 unsigned char ee_consumption_buffer[MAX_LOGGING] EEMEM;
145 static inline uint8_t
146 get_consumption (int idx)
147 {
148   return idx >= 0 && idx < MAX_LOGGING
149     ? eeprom_read_byte (&ee_consumption_buffer[idx]) : 0;
150 }
151 static inline void
152 put_consumption (int idx, uint8_t value)
153 {
154   if (idx >= 0 && idx < MAX_LOGGING)
155     eeprom_update_byte (&ee_consumption_buffer[idx], value);
156 }
157
158 /* A ring buffer to store the temperatures.  */
159 unsigned char ee_temperature_buffer[MAX_LOGGING] EEMEM;
160 static inline uint8_t
161 get_temperature (int idx)
162 {
163   return idx >= 0 && idx < MAX_LOGGING
164     ? eeprom_read_byte (&ee_temperature_buffer[idx]) : 0;
165 }
166 static inline void
167 put_temperature (int idx, uint8_t value)
168 {
169   if (idx >= 0 && idx < MAX_LOGGING)
170     eeprom_update_byte (&ee_temperature_buffer[idx], value);
171 }
172
173 /* The index to the next to use log item. */
174 unsigned char ee_history_index EEMEM;
175 static inline uint8_t
176 get_history_index (void)
177 {
178   return eeprom_read_byte (&ee_history_index);
179 }
180 static inline void
181 put_history_index (uint8_t value)
182 {
183   eeprom_update_byte (&ee_history_index, value);
184 }
185
186 signed int  ee_t_boiler_min[N_CHARACTERISTICS] EEMEM;
187 static inline int16_t
188 get_t_boiler_min (int idx)
189 {
190   return (idx >= 0 && idx < N_CHARACTERISTICS)
191     ? eeprom_read_word (&ee_t_boiler_min[idx]) : 0;
192 }
193 static inline void
194 inc_t_boiler_min (int idx, int16_t value)
195 {
196   if (idx >= 0 && idx < N_CHARACTERISTICS)
197     {
198       int16_t val = eeprom_read_word (&ee_t_boiler_min[idx]);
199       val += value;
200       eeprom_write_word (&ee_t_boiler_min[idx], val);
201     }
202 }
203
204 signed int  ee_t_boiler_max[N_CHARACTERISTICS] EEMEM;
205 static inline int16_t
206 get_t_boiler_max (int idx)
207 {
208   return (idx >= 0 && idx < N_CHARACTERISTICS)
209     ? eeprom_read_word (&ee_t_boiler_max[idx]) : 0;
210 }
211 static inline void
212 inc_t_boiler_max (int idx, int16_t value)
213 {
214   if (idx >= 0 && idx < N_CHARACTERISTICS)
215     {
216       int16_t val = eeprom_read_word (&ee_t_boiler_max[idx]);
217       val += value;
218       eeprom_write_word (&ee_t_boiler_max[idx], val);
219     }
220 }
221
222 /* Definitions of the characteristics curves.  */
223 signed int  ee_t_curve_high[N_CHARACTERISTICS] EEMEM;
224 static inline int16_t
225 get_t_curve_high (int idx)
226 {
227   return (idx >= 0 && idx < N_CHARACTERISTICS)
228     ? eeprom_read_word (&ee_t_curve_high[idx]) : 0;
229 }
230 static inline void
231 inc_t_curve_high (int idx, int16_t value)
232 {
233   if (idx >= 0 && idx < N_CHARACTERISTICS)
234     {
235       int16_t val = eeprom_read_word (&ee_t_curve_high[idx]);
236       val += value;
237       eeprom_write_word (&ee_t_curve_high[idx], val);
238     }
239 }
240
241 signed int  ee_t_curve_low[N_CHARACTERISTICS] EEMEM;
242 static inline int16_t
243 get_t_curve_low (int idx)
244 {
245   return (idx >= 0 && idx < N_CHARACTERISTICS)
246     ? eeprom_read_word (&ee_t_curve_low[idx]) : 0;
247 }
248 static inline void
249 inc_t_curve_low (int idx, int16_t value)
250 {
251   if (idx >= 0 && idx < N_CHARACTERISTICS)
252     {
253       int16_t val = eeprom_read_word (&ee_t_curve_low[idx]);
254       val += value;
255       eeprom_write_word (&ee_t_curve_low[idx], val);
256     }
257 }
258
259 signed int  ee_t_pump_on[N_CHARACTERISTICS] EEMEM;
260 static inline int16_t
261 get_t_pump_on (int idx)
262 {
263   return (idx >= 0 && idx < N_CHARACTERISTICS)
264     ? eeprom_read_word (&ee_t_pump_on[idx]) : 0;
265 }
266 static inline void
267 inc_t_pump_on (int idx, int16_t value)
268 {
269   if (idx >= 0 && idx < N_CHARACTERISTICS)
270     {
271       int16_t val = eeprom_read_word (&ee_t_pump_on[idx]);
272       val += value;
273       eeprom_write_word (&ee_t_pump_on[idx], val);
274     }
275 }
276
277 /* A flag and a value to detect whether the eeprom has been setup.  */
278 unsigned char e2_init_marker EEMEM;
279 #define E2_INIT_MARKER_OKAY 0x3a
280
281
282 /* The current time measured in minutes.  */
283 volatile unsigned int current_time;
284
285 /* /\* IR interrupt counter.  *\/ */
286 /* volatile unsigned int ir_int_counter; */
287
288
289 /* Constants for the operation modes.  The values are assumed to be
290    ascending starting from 0.  They are used as an index; the value
291    for deactivated is treated especially, though.  They also index the
292    custom glyphs we use.*/
293 enum {
294   NIGHT_MODE       = 0,
295   DAY_MODE         = 1,
296   ABSENT_MODE      = 2,
297   DEACTIVATED_MODE = 3
298 };
299
300 /* Flag indicating a workday.  */
301 #define WORKDAY     0x4000
302 #define TIMEFLAG2   0x8000  /* Reserved flag.  */
303
304 /* The current operation mode.  */
305 unsigned char operation_mode;
306
307 /* The display lit counter (seconds). */
308 unsigned char lit_timer;
309
310 volatile unsigned int burner_time;
311 volatile unsigned int total_time;
312
313 /* The names of the weekdays.  */
314 static const PROGMEM char weekday_names[7][3] =
315   { "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So" };
316
317
318 /* Flags for scheduled actions.  */
319 volatile struct
320 {
321   unsigned char menu:1;
322   unsigned char minute:1;
323   unsigned char day:1;
324   unsigned char refresh_lcd:1;
325   unsigned char output:1;
326   unsigned char run_main:1;
327   unsigned char monitor_mode:1;
328 } actionflags;
329
330
331 volatile struct
332 {
333   unsigned char show_left_temp:2;
334   unsigned char show_right_temp:1;
335   unsigned char reset_status_menu:1;
336   unsigned char send_hist:1;
337   unsigned char send_data:1;
338   unsigned char send_conf:1;
339 } actionflags2;
340
341 volatile struct
342 {
343   unsigned char status_change:1;
344 } actionflags3;
345
346
347 /* The hardware keys.  */
348 #define KEY_1       bit_is_set (PINA, PINA7)
349 #define KEY_2       bit_is_set (PINA, PINA6)
350 #define KEY_3       bit_is_set (PINA, PINA5)
351 #define KEY_4       bit_is_set (PINA, PINA4)
352 #define KEY_5       bit_is_set (PINA, PINA3)
353
354 /* We map the hardware keys to one virtual key with these values.  */
355 enum
356   {
357     VK_NONE = 0,
358     VK_MENU = 1,
359     VK_UP   = 3,
360     VK_DOWN = 4,
361     VK_ENTER = 5,
362     VK_MODE = 6
363   };
364
365 /* The current value of our virtual key.  */
366 unsigned char current_key;
367
368 /* The identifier of the current submenu.  */
369 unsigned char current_submenu;
370
371 /* The current atcommand if any and a flag indicating that an AT
372    command is currently processed.  During the time of procewssing an
373    AT command all input is ignored.  */
374 volatile char atcommand[16];
375 volatile char run_atcommand;
376
377
378 /* The measured temperatures and an average value.  */
379 signed int  real_temperature[2];
380 signed int  outside_temperature;
381 signed int  boiler_temperature;
382 signed long avg_temperature;
383
384 #define LED_NIGHT        (PORTB)
385 #define LED_NIGHT_BIT    (0)
386 #define LED_ABSENT       (PORTB)
387 #define LED_ABSENT_BIT   (1)
388 #define RELAY_BOILER     (PORTB)
389 #define RELAY_BOILER_BIT (2)
390 #define RELAY_PUMP       (PORTB)
391 #define RELAY_PUMP_BIT   (3)
392 #define RELAY_LIT        (PORTB)
393 #define RELAY_LIT_BIT    (4)
394
395
396 /* The hysteresis of the boiler temperature measured in 0.1 degress.
397    For example: 25 means that a target boiler temperature of 50 degree
398    Celsius may oscillate between 47.5 and 52.5. */
399 #define BOILER_HYSTERESIS 20
400
401 /* The desired temperatur of the boiler.  */
402 signed int boiler_temperature_target;
403
404
405 #define MK_TIME(h,m)  ((h)*60+(m))
406
407
408 static void lcd_load_user_glyphs (void);
409
410 void lcd_delay_ms(uint8_t ms) {  _delay_ms (ms); }
411 #define delay_ms(ms)      _delay_ms ((ms))
412
413 /* Despite what the Displaytech docs and several examples say, the
414    disassembly of the original code of this program shows that 42
415    cycles are used.  We use the provided delay loop which runs at 3
416    cycles and ignore that gcc does need all the 7 cycles for the setup
417    (rcall, ldi, ret).  */
418 #define _lcd_e_delay()   do { _delay_loop_1 (15); } while (0)
419 #define _lcd_e_high()    do { LCD_PORT  |=  _BV(LCD_E_PIN); } while (0)
420 #define _lcd_e_low()     do { LCD_PORT  &= ~_BV(LCD_E_PIN); } while (0)
421 #define _lcd_e_toggle()  do {                  \
422                             _lcd_e_high ();    \
423                             _lcd_e_delay ();   \
424                             _lcd_e_low ();     \
425                          } while (0)
426 #define _lcd_rw_high()   do { LCD_PORT |=  _BV(LCD_RW_PIN); } while (0)
427 #define _lcd_rw_low()    do { LCD_PORT &= ~_BV(LCD_RW_PIN); } while (0)
428 #define _lcd_rs_high()   do { LCD_PORT |=  _BV(LCD_RS_PIN); } while (0)
429 #define _lcd_rs_low()    do { LCD_PORT &= ~_BV(LCD_RS_PIN); } while (0)
430 #define _lcd_waitbusy()  do { while (_lcd_read (1) & 0x80); } while (0)
431
432 /* Send a command to the LCD.  */
433 #define lcd_command(cmd) do {                      \
434                               _lcd_waitbusy ();    \
435                               _lcd_write (cmd, 1); \
436                          } while (0)
437
438 /* Write a data byte to the display.  */
439 #define lcd_putc(c)      do {                      \
440                               _lcd_waitbusy ();    \
441                               _lcd_write (c, 0);   \
442                          } while (0)
443
444
445 /* Clear the display.  */
446 #define lcd_clear()  lcd_command (0x01);
447
448 /* Got to the home position.  */
449 #define lcd_home()   lcd_command (0x02);
450
451
452 static uint8_t
453 _lcd_read (uint8_t read_ctrl)
454 {
455   uint8_t value = 0;
456
457   if (read_ctrl)
458     _lcd_rs_low ();
459   else
460     _lcd_rs_high ();
461   _lcd_rw_high ();
462
463   /* Configure data pins as input.  */
464   LCD_DDR &= ~LCD_DATA_MASK;
465
466   /* Read high nibble.  */
467   _lcd_e_high ();
468   _lcd_e_delay ();
469   if (bit_is_set (LCD_PIN, LCD_DATA0_PIN))
470     value |= 0x10;
471   if (bit_is_set (LCD_PIN, LCD_DATA1_PIN))
472     value |= 0x20;
473   if (bit_is_set (LCD_PIN, LCD_DATA2_PIN))
474     value |= 0x40;
475   if (bit_is_set (LCD_PIN, LCD_DATA3_PIN))
476     value |= 0x80;
477   _lcd_e_low ();
478
479   _lcd_e_delay ();
480
481   /* Read low nibble */
482   _lcd_e_high ();
483   _lcd_e_delay ();
484   if (bit_is_set (LCD_PIN, LCD_DATA0_PIN))
485     value |= 0x01;
486   if (bit_is_set (LCD_PIN, LCD_DATA1_PIN))
487     value |= 0x02;
488   if (bit_is_set (LCD_PIN, LCD_DATA2_PIN))
489     value |= 0x04;
490   if (bit_is_set (LCD_PIN, LCD_DATA3_PIN))
491     value |= 0x08;
492   _lcd_e_low ();
493
494   /* Set data bits to output and set them to high. */
495   LCD_PORT |= LCD_DATA_MASK;
496   LCD_DDR  |= LCD_DATA_MASK;
497
498   _lcd_rw_low ();
499   return value;
500 }
501
502
503 static void
504 _lcd_write (uint8_t value, uint8_t write_ctrl)
505 {
506   uint8_t data;
507
508   if (write_ctrl)
509     _lcd_rs_low ();
510   else
511     _lcd_rs_high ();
512   _lcd_rw_low ();
513
514   /* Configure data pins as output.  */
515   LCD_DDR |= LCD_DATA_MASK;
516
517   /* Write high nibble.  */
518   data = 0;
519   if ((value & 0x80))
520     data |= _BV (LCD_DATA3_PIN);
521   if ((value & 0x40))
522     data |= _BV (LCD_DATA2_PIN);
523   if ((value & 0x20))
524     data |= _BV (LCD_DATA1_PIN);
525   if ((value & 0x10))
526     data |= _BV (LCD_DATA0_PIN);
527   LCD_PORT &= ~LCD_DATA_MASK;
528   LCD_PORT |= data;
529   _lcd_e_toggle ();
530
531   /* Write low nibble.  */
532   data = 0;
533   if ((value & 0x08))
534     data |= _BV (LCD_DATA3_PIN);
535   if ((value & 0x04))
536     data |= _BV (LCD_DATA2_PIN);
537   if ((value & 0x02))
538     data |= _BV (LCD_DATA1_PIN);
539   if ((value & 0x01))
540     data |= _BV (LCD_DATA0_PIN);
541   LCD_PORT &= ~LCD_DATA_MASK;
542   LCD_PORT |= data;
543   _lcd_e_toggle ();
544
545   /* Set data bits to high. */
546   LCD_PORT |= LCD_DATA_MASK;
547 }
548
549
550
551 /* Init the LCD to 4 bit mode.  */
552 void
553 lcd_init (void)
554 {
555   /* Configure all used pins as output and clear them.  */
556   LCD_PORT &= ~LCD_PIN_MASK;
557   LCD_DDR |= LCD_PIN_MASK;
558
559   /* RS is cleared to send a command; cmd = 0x03.
560      Command must be repeated two times.  */
561   lcd_delay_ms (15);
562   LCD_PORT |= (_BV (LCD_DATA1_PIN) | _BV (LCD_DATA0_PIN));
563   _lcd_e_toggle ();
564   lcd_delay_ms (5);
565   _lcd_e_toggle ();
566   lcd_delay_ms (1);
567   _lcd_e_toggle ();
568   lcd_delay_ms (1);
569
570   /* Select 4 bit mode.  */
571   LCD_PORT &= ~LCD_PIN_MASK;
572   LCD_PORT |= _BV (LCD_DATA1_PIN);
573   _lcd_e_toggle ();
574   lcd_delay_ms (1);
575
576   /* Set function: 4bit,     2 lines,  5x7.     */
577   /*               (bit 4=0) (bit 3=1) (bit2=0) */
578   lcd_command (0x20 | 8 );
579
580   /* Display off.  */
581   lcd_command (0x08);
582   /* Display clear.  */
583   lcd_command (0x01);
584   /* Entry mode set: increase cursor, display is not shifted */
585   /*                 (bit 1)          ( bit 0)               */
586   lcd_command (0x04 | 2 );
587
588   /* Display is now ready. Switch it on.  */
589
590   /* Display on/off: Display on, cursor off, blinking off.  */
591   /*                 (bit 2)     (bit 1)     (bit 0)        */
592   lcd_command (0x08 | 4 );
593
594   lcd_load_user_glyphs ();
595 }
596
597
598 /* Load the our special glyphs.  */
599 static void
600 lcd_load_user_glyphs (void)
601 {
602   static const PROGMEM char glyphs[5][8] =
603     {
604       { /* glyph 0 - moon */
605         0b0000000,
606         0b0000000,
607         0b0001110,
608         0b0011110,
609         0b0011110,
610         0b0001110,
611         0b0000000,
612         0b0000000
613       },
614       { /* glyph 1 - sun */
615         0b0000100,
616         0b0010101,
617         0b0001110,
618         0b0010001,
619         0b0010001,
620         0b0001110,
621         0b0010101,
622         0b0000100
623       },
624       { /* glyph 2 - circle */
625         0b0000000,
626         0b0000000,
627         0b0001110,
628         0b0010001,
629         0b0010001,
630         0b0001110,
631         0b0000000,
632         0b0000000
633       },
634       { /* glyph 3 - up arrow */
635         0b0000000,
636         0b0000100,
637         0b0001110,
638         0b0010101,
639         0b0000100,
640         0b0000100,
641         0b0000100,
642         0b0000000
643       },
644       { /* glyph 4 - down arrow */
645         0b0000000,
646         0b0000100,
647         0b0000100,
648         0b0000100,
649         0b0010101,
650         0b0001110,
651         0b0000100,
652         0b0000000
653       }
654     };
655   unsigned char idx, g, row;
656
657   for (idx=0; idx < DIM (glyphs); idx++)
658     {
659       lcd_command ((0x80 | idx)); /* Set DDRAM address.  */
660       g = (0x40 | (idx * 8));     /* First Set CGRAM command. */
661       for (row=0; row < 8; row++)
662         {
663           lcd_command (g++);
664           lcd_putc (pgm_read_byte (&glyphs[idx][row]));
665         }
666     }
667 }
668
669
670 /* Set the next data write position to X,Y.  */
671 void
672 lcd_gotoxy (uint8_t x, uint8_t y)
673 {
674   lcd_command (0x80 | ((y? 0x40:0) + x));
675 }
676
677
678 uint8_t
679 lcd_getc (void)
680 {
681   _lcd_waitbusy ();
682   return _lcd_read (0);
683 }
684
685 void
686 lcd_puts (const char *s)
687 {
688   uint8_t c;
689
690   while ((c = *s++))
691     lcd_putc (c);
692 }
693
694
695 #define lcd_puts_P(s)  _lcd_puts_P (PSTR ((s)))
696 void
697 _lcd_puts_P (const char *progmem_s)
698 {
699   uint8_t c;
700
701   while ((c = pgm_read_byte (progmem_s++)))
702     lcd_putc (c);
703 }
704
705
706
707 \f
708 #define LCD      0
709 #define SERIAL  1
710 #define LEADING_ZERO  -1
711 #define lcd_int(w, a, p)   format_int (LCD,    (w), (a), (p))
712 #define uart_int(w, a, p)  format_int (SERIAL, (w), (a), (p))
713
714 void
715 uart_putc (char c)
716 {
717   while (!bit_is_set (UCSRA, UDRE))
718     ;
719   UDR = c;
720 }
721
722
723 void
724 uart_puts (const char *s)
725 {
726   uint8_t c;
727
728   while ((c = *s++))
729     {
730       if (c == '\n')
731         uart_putc ('\r');
732       uart_putc (c);
733     }
734 }
735
736 #define uart_puts_P(s)  _uart_puts_P (PSTR ((s)))
737 void
738 _uart_puts_P (const char *progmem_s)
739 {
740   uint8_t c;
741
742   while ((c = pgm_read_byte (progmem_s++)))
743     {
744       if (c == '\n')
745         uart_putc ('\r');
746       uart_putc (c);
747     }
748 }
749
750
751 void
752 do_putchar (char c)
753 {
754   if (actionflags.output == LCD)
755     lcd_putc (c);
756   else
757     uart_putc (c);
758 }
759
760
761 void
762 format_int (char output, signed int value, signed char width,
763             signed char precision)
764 {
765   unsigned int table[] = {0,1,10,100,1000,10000,32767};
766   signed char i;
767   unsigned char pos, zero;
768   char nowidth = 0;
769
770   actionflags.output = output;
771
772   if (width < 0)
773     {
774       width = - width;
775       if (value < 0)
776         {
777           value = -value;
778           do_putchar ('-');
779         }
780       else
781         do_putchar ('+');
782     }
783   else if (!width)
784     {
785       width = 5;
786       nowidth = 1;
787       if (value < 0)
788         {
789           value = -value;
790           do_putchar ('-');
791         }
792     }
793
794   if (precision > width || width > 5)
795     {
796       for (i=0; i < width; i++)
797         do_putchar ('?');
798       return;
799     }
800
801   if (value >= table[width + 1])
802     {
803       for (i=0; i < width; i++)
804         do_putchar ('?');
805       return;
806     }
807
808   zero = (precision < 0);
809
810   for (i=width; i > 0; i--)
811     {
812       if (i == precision)
813         {
814           do_putchar ('.');
815           zero = 1;
816         }
817       pos = '0' + (value / table[i]);
818
819       if ((pos == '0') && !zero && i != 1)
820         {
821           if (!nowidth)
822             do_putchar ((i == precision+1)? '0':' ');
823         }
824       else
825         {
826           zero = 1;
827           do_putchar (pos);
828           value %= table[i];
829         }
830     }
831 }
832
833
834 /* Read the value from the ADC SOURCE.  */
835 unsigned int
836 read_adc (unsigned char source)
837 {
838   ADMUX = source;
839   ADCSRA |= _BV(ADSC);  /* ADC Start Conversion.  */
840   while (bit_is_clear (ADCSRA, ADIF)) /* Wait for Interrupt Flag.  */
841     ;
842   ADCSRA |= _BV(ADIF);  /* Clear ADC Interrupt Flag.  */
843   /* Read the value to scale it. */
844   return ((ADCW*25)/4)-2250;
845 }
846
847
848 /* Read the temperature sensors.  */
849 void
850 read_t_sensors (unsigned char first_time)
851 {
852   signed int value;
853
854   /* First the outside temerature.  */
855   value = read_adc (1); /* Read ADC1 using AREF. */
856   real_temperature[0] = value;
857   if (first_time)
858     outside_temperature = value;
859
860
861   real_temperature[1] = 0;  /* Fixme: Take from third sensor.  */
862
863   /* Slowly adjust our idea of the outside temperature to the
864      readout. */
865   if (actionflags.minute)
866     {
867       if (outside_temperature > value)
868         outside_temperature--;
869       else if (outside_temperature < value)
870         outside_temperature++;
871       actionflags.minute = 0;
872     }
873
874   /* Second the boiler temperature.  */
875   value = read_adc (2); /* Read ADC2 using AREF. */
876   if (boiler_temperature > value)
877     boiler_temperature--;
878   else if (boiler_temperature < value)
879     boiler_temperature++;
880
881   if (first_time)
882     boiler_temperature = value;
883 }
884
885
886 /* 1ms ticker interrupt service routine. */
887 ISR (TIMER2_COMP_vect)
888 {
889   static unsigned int clock; /* Milliseconds of the current minute.  */
890   static unsigned int tim;
891
892   clock++;
893
894   if (!(clock % 2000))
895     {
896       if (actionflags2.show_left_temp < 2)
897         actionflags2.show_left_temp++;
898       else
899         actionflags2.show_left_temp = 0;
900       actionflags2.show_right_temp = !actionflags2.show_right_temp;
901
902       if (actionflags.monitor_mode && !(clock % 10000))
903         actionflags2.send_data = 1;
904     }
905
906   if (clock == 60000)
907    {
908      /* One minute has passed.  Bump the current time.  */
909      current_time++;
910      clock = 0;
911      actionflags.minute = 1;
912
913      if (!(current_time % 1440))
914        actionflags.day = 1;
915
916      if (current_time >= 1440*7)
917        current_time = 0;
918
919      /* Update the average temperature.  */
920      avg_temperature += outside_temperature / 10;
921    }
922
923   if (++tim == 2000)
924     {
925       /* Two seconds passed.  */
926       if (bit_is_set (RELAY_BOILER, RELAY_BOILER_BIT))
927         burner_time++;
928       total_time++;
929       tim = 0;
930
931       if (lit_timer > 1)
932         lit_timer -= 2;
933    }
934
935   /* Run the main loop every 35ms.  */
936   if (!(clock % 35))
937     {
938       actionflags.run_main = 1;
939
940       /* Request an LCD refresh every 350ms.  */
941       if (!(clock % 350))
942         actionflags.refresh_lcd = 1;
943     }
944
945 }
946
947
948 /* UART transmit interrupt service routine.  */
949 ISR (USART_TXC_vect)
950 {
951   /* Nothing to do.  */
952 }
953
954
955 /* UART receive interrupt service routine.  */
956 ISR (USART_RXC_vect)
957 {
958   static uint8_t state;
959   static uint8_t bufidx;
960   uint8_t c = UDR;
961
962   switch (state)
963     {
964     case 0:
965       if (c == '\r')
966         state = 1;
967       break;
968     case 4:
969       if (run_atcommand)
970         break;
971       state = 1;
972       /*FALLTHRU*/
973     case 1:
974       if (c == 'A' || c == 'a')
975         state = 2;
976       else if (c == '\n')
977         ;
978       else
979         state = 0;
980       break;
981     case 2:
982       if (c == 'T' || c == 't')
983         {
984           state = 3;
985           bufidx = 0;
986         }
987       else if (c == '/') /* Repeat last command.  */
988         {
989           run_atcommand = 1;
990           state = 4;
991         }
992       else
993         state = 0;
994       break;
995     case 3:
996       if (c == '\r')
997         {
998           atcommand[bufidx] = 0;
999           run_atcommand = 1;
1000           state = 4;
1001         }
1002       else if (bufidx < sizeof atcommand - 1)
1003         atcommand[bufidx++] = c;
1004       break;
1005     }
1006 }
1007
1008
1009 /* Triggered by the infrared sensor.  */
1010 /* ISR (INT0_vect) */
1011 /* { */
1012 /*   ir_int_counter++; */
1013 /*   GIFR   |= 0x40; */
1014 /* } */
1015
1016
1017 /* External interrupt 1 service routine.  This is only used with the
1018    "turn-push" button option.  */
1019 #ifdef USE_TURN_PUSH
1020 ISR (INT1_vect)
1021 {
1022   unsigned char key;
1023
1024   key = KEY_2;
1025
1026   if (MCUCR==0x08) /* Falling edge */
1027     {
1028       current_key = !key? VK_DOWN : VK_UP;
1029       MCUCR = 0x0C;
1030     }
1031   else /* Rising edge.  */
1032     {
1033       MCUCR = 0x08;
1034       current_key = !key? VK_UP : VK_DOWN;
1035     }
1036   GIFR |= 0x80;
1037 }
1038 #endif /*USE_TURN_PUSH*/
1039
1040
1041 /* Poll the keyboard and debounce the up and down keys.  */
1042 #define DEBOUNCE_COUNT 7
1043 #define DEBOUNCE(a,b)  do {                                   \
1044                          if (!(a)++ || (a) == DEBOUNCE_COUNT) \
1045                            current_key = (b);                 \
1046                          if ((a) == DEBOUNCE_COUNT)           \
1047                            (a) = 3;                           \
1048                        } while (0)
1049 void
1050 poll_keyboard (void)
1051 {
1052   static char key1, key2, key3, key4, key5;
1053
1054   current_key = VK_NONE;
1055
1056 #ifdef USE_TURN_PUSH
1057
1058 #else
1059   if (KEY_1)
1060     DEBOUNCE (key1, VK_MENU);
1061   else
1062     key1 = 0;
1063
1064   if(KEY_2)
1065     DEBOUNCE (key2, VK_DOWN);
1066   else
1067     key2 = 0;
1068
1069   if (KEY_3)
1070     DEBOUNCE (key3, VK_UP);
1071   else
1072     key3 = 0;
1073 #endif
1074
1075   if (KEY_4)
1076     DEBOUNCE (key4, VK_ENTER);
1077   else
1078     key4 = 0;
1079
1080   if (KEY_5)
1081     DEBOUNCE (key5, VK_MODE);
1082   else
1083     key5 = 0;
1084
1085   if (current_key != VK_NONE)
1086     lit_timer = 20; /* Set to n seconds. */
1087 }
1088 #undef DEBOUNCE
1089 #undef DEBOUNCE_COUNT
1090
1091
1092 unsigned char
1093 get_operation_mode (unsigned int time)
1094 {
1095   unsigned char result = DEACTIVATED_MODE;
1096   unsigned char i;
1097   uint16_t t;
1098   uint8_t shiftoff;
1099
1100   for (i = 0; i < 32; i++)
1101     {
1102       shiftoff = get_shift_offset ();
1103       t = get_timer_time (i + shiftoff);
1104       if ((t & WORKDAY))
1105         {
1106           if (time  < (6*1440)  /* Monday to Friday.  */
1107               && ((t & ~WORKDAY) % 1440) == (time % 1440))
1108             {
1109               result = get_timer_mode (i + shiftoff);
1110               break;
1111             } /* FiXME:  original remark:  please test. */
1112         }
1113       else if (t == time)
1114         {
1115           result = get_timer_mode (i + shiftoff);
1116           break;
1117         }
1118     }
1119
1120   return result;
1121 }
1122
1123
1124 void
1125 status_menu (void)
1126 {
1127   static unsigned char index, blink;
1128
1129   if (actionflags2.reset_status_menu)
1130     {
1131       actionflags2.reset_status_menu = 0;
1132       index = blink = 0;
1133     }
1134
1135   if (actionflags.menu)
1136     {
1137       lcd_gotoxy (2, 0);
1138       lcd_puts_P ("             %");
1139       actionflags.menu = 0;
1140     }
1141
1142   if (actionflags.refresh_lcd)
1143     {
1144       lcd_gotoxy (0, 0); _lcd_puts_P (weekday_names[current_time/1440]);
1145
1146       lcd_gotoxy (3 ,0); lcd_int ((current_time % 1440) / 60, 2 , LEADING_ZERO);
1147
1148       lcd_putc (blink? ':' : ' ');
1149       blink = !blink;
1150
1151       lcd_int ((current_time % 1440) % 60, 2 ,LEADING_ZERO);
1152       lcd_gotoxy (0, 1);
1153       switch (actionflags2.show_left_temp)
1154         {
1155         case 0:
1156           lcd_puts_P ("Ta=");
1157           lcd_int (real_temperature[0]/10, -2, 0);
1158           break;
1159         case 1:
1160           lcd_puts_P ("Tb=");
1161           lcd_int (real_temperature[1]/10, -2, 0);
1162           break;
1163         default:
1164           lcd_puts_P ("T*=");
1165           lcd_int (outside_temperature/10, -2, 0);
1166           break;
1167         }
1168       lcd_putc ('\xdf'); lcd_putc(' ');
1169
1170       switch (actionflags2.show_right_temp)
1171         {
1172         case 0:
1173           lcd_puts_P ("Ts=");
1174           lcd_int (boiler_temperature_target/10, -2, 0);
1175           break;
1176         default:
1177           lcd_puts_P("Tk=");
1178           lcd_int (boiler_temperature/10, -2, 0);
1179           break;
1180         }
1181       lcd_putc ('\xdf'); lcd_putc('C');
1182
1183       lcd_gotoxy(12,0);
1184       lcd_int ( total_time? ((long) burner_time * 100) / total_time:0, 3, 0);
1185
1186       lcd_gotoxy(10,0);
1187       switch (operation_mode)
1188         {
1189         case DAY_MODE:
1190         case NIGHT_MODE:
1191         case ABSENT_MODE:
1192           lcd_putc (operation_mode);
1193           break;
1194         case DEACTIVATED_MODE:
1195           lcd_putc (' ');
1196           break;
1197         }
1198
1199       actionflags.refresh_lcd = 0;
1200     }
1201
1202
1203
1204   switch (index)
1205       {
1206       case 0:
1207         if (current_key == VK_UP || current_key == VK_DOWN)
1208           {
1209             current_submenu = 255;
1210             actionflags.menu = 1;
1211           }
1212         break;
1213
1214       case 1: /* Change the hour.  */
1215         lcd_gotoxy (2,0); lcd_putc ('\x7e');
1216         if (current_key == VK_UP)
1217           current_time += 60;
1218         else if (current_key == VK_DOWN && current_time > 60)
1219           current_time -= 60;
1220         break;
1221
1222       case 2: /* Change the minute.  */
1223          lcd_gotoxy (2,0); lcd_putc (' ');
1224          lcd_gotoxy (8,0); lcd_putc ('\x7f');
1225          if (current_key == VK_UP)
1226            current_time ++;
1227          else if (current_key == VK_DOWN && current_time)
1228            current_time --;
1229          break;
1230
1231       case 3: /* Change the day.  */
1232          lcd_gotoxy (8,0); lcd_putc (' ');
1233          lcd_gotoxy (2,0); lcd_putc ('\x7f');
1234          if (current_key == VK_UP)
1235            {
1236              if (current_time < 6*1440)
1237                current_time += 1440;
1238              else
1239                current_time %= 1440;
1240            }
1241          else if (current_key == VK_DOWN)
1242            {
1243              if (current_time > 1440)
1244                current_time -= 1440;
1245              else
1246                current_time += 6*1440;
1247            }
1248          break;
1249       }
1250
1251     if (current_key == VK_ENTER)
1252       {
1253         if (index < 3)
1254           index++;
1255         else
1256           {
1257             index = 0;
1258             actionflags.menu = 1;
1259           }
1260       }
1261 }
1262
1263
1264 unsigned char
1265 edit_timer (unsigned char pos)
1266 {
1267   static unsigned int tmp_wert, time, tag_time;
1268   static unsigned char tmp_mode, index;
1269   uint16_t t;
1270
1271
1272   pos += get_shift_offset ();
1273
1274   if (actionflags.menu)
1275     {
1276       actionflags.menu = 0;
1277       lcd_gotoxy(2,0); lcd_puts_P ("              ");
1278
1279       lcd_gotoxy (0, 0);
1280       t = get_timer_time (pos);
1281       if (t & WORKDAY)
1282         lcd_puts_P ("WT");
1283       else
1284         _lcd_puts_P (weekday_names[t % (1440*7) / 1440]);
1285
1286       lcd_gotoxy (0, 1); lcd_puts_P ("Mode   ");
1287       time = t & ~(WORKDAY|TIMEFLAG2);
1288       tmp_mode = get_timer_mode (pos);
1289       tmp_wert = time;
1290       tag_time = (time / 1440)*1440;
1291       if (t & WORKDAY)
1292         tag_time = WORKDAY;
1293     }
1294
1295   lcd_gotoxy (7 ,0);
1296   lcd_int ((tmp_wert % 1440) / 60, 2, LEADING_ZERO);
1297   lcd_putc(':');
1298   lcd_int ((tmp_wert % 1440) % 60, 2, LEADING_ZERO);
1299
1300   lcd_gotoxy(7,1);
1301   switch (tmp_mode)
1302     {
1303     case DAY_MODE:
1304       lcd_puts_P ("Tag      ");
1305       lcd_gotoxy (14, 0);
1306       lcd_putc (tmp_mode);
1307       break;
1308
1309     case NIGHT_MODE:
1310       lcd_puts_P ("Nacht    ");
1311       lcd_gotoxy (14, 0);
1312       lcd_putc (tmp_mode);
1313       break;
1314
1315     case ABSENT_MODE:
1316       lcd_puts_P ("Abwesend ");
1317       lcd_gotoxy (14, 0);
1318       lcd_putc (tmp_mode);
1319       break;
1320
1321     case DEACTIVATED_MODE:
1322       lcd_puts_P ("DEAKTIV  ");
1323       lcd_gotoxy (14, 0);
1324       lcd_putc ('-');
1325       break;
1326     }
1327
1328   switch (index)
1329     {
1330     case 0:
1331       lcd_gotoxy (6, 0); lcd_putc ('\x7e');
1332       if (current_key == VK_UP)
1333         tmp_wert += 60;
1334       else if (current_key == VK_DOWN)
1335         tmp_wert -= 60;
1336       break;
1337
1338     case 1:
1339       lcd_gotoxy (6,0); lcd_putc(' ');
1340       lcd_gotoxy (12,0); lcd_putc('\x7f');
1341       if (current_key == VK_UP)
1342         tmp_wert ++;
1343       else if (current_key == VK_DOWN)
1344         tmp_wert --;
1345       break;
1346
1347     case 2:
1348       lcd_gotoxy (12,0); lcd_putc (' ');
1349       lcd_gotoxy (6,1); lcd_putc ('\x7e');
1350       if (current_key == VK_UP)
1351         {
1352           if (tmp_mode < 3)
1353             tmp_mode++;
1354           else
1355             tmp_mode = 0;
1356         }
1357       else if (current_key == VK_DOWN)
1358         {
1359           if (tmp_mode > 0)
1360             tmp_mode--;
1361           else
1362             tmp_mode = 3;
1363         }
1364       break;
1365     }
1366
1367   if (current_key == VK_ENTER)
1368     {
1369       if (index < 2)
1370         index++;
1371       else
1372         {
1373           put_timer_mode (pos, tmp_mode);
1374           put_timer_time (pos, (tmp_wert % 1440) | tag_time);
1375           index = 0;
1376           actionflags.menu = 1;
1377           return 1;
1378         }
1379     }
1380    return 0;
1381 }
1382
1383
1384 void
1385 show_timer (char x, char y, unsigned char pos)
1386 {
1387   unsigned int time;
1388   uint8_t mode;
1389
1390   pos += get_shift_offset ();
1391
1392   time = get_timer_time (pos) & ~(WORKDAY|TIMEFLAG2);
1393   mode = get_timer_mode (pos);
1394
1395   lcd_gotoxy (x,y);
1396
1397   if (mode == DEACTIVATED_MODE)
1398     lcd_puts_P (" --:--");
1399   else
1400     {
1401       lcd_putc (mode);
1402       lcd_int ((time % 1440) / 60, 2, LEADING_ZERO);
1403       lcd_putc(':');
1404       lcd_int ((time % 1440) % 60, 2, LEADING_ZERO);
1405     }
1406 }
1407
1408
1409 void
1410 timer_menu (void)
1411 {
1412   static unsigned char pos, index, edit;
1413   static unsigned char clearlcd = 1;
1414   uint16_t atime;
1415
1416   if (actionflags.menu)
1417    {
1418      actionflags.menu = 0;
1419      edit = 0;
1420      clearlcd = 1;
1421    };
1422
1423   if (edit == 2) /* Edit timer.  */
1424     {
1425       if (edit_timer (pos+index))
1426         edit = 0;
1427     }
1428   else /* Select timer.  */
1429     {
1430       if (clearlcd)
1431         {
1432           lcd_gotoxy(0,0);
1433           atime = get_timer_time (pos);
1434           if (atime & WORKDAY)
1435             lcd_puts_P ("WT");
1436           else
1437             _lcd_puts_P (weekday_names[atime % (1440*7) / 1440]);
1438
1439           lcd_gotoxy(0,1); lcd_int((get_shift_offset ()/32)+1, 1, 0);
1440
1441           show_timer (3 ,0, pos);
1442           show_timer (10,0, pos+1);
1443           show_timer (3 ,1, pos+2);
1444           show_timer (10,1, pos+3);
1445           clearlcd = 0;
1446           lcd_gotoxy (2,0); lcd_putc(' ');
1447           lcd_gotoxy (9,0); lcd_putc(' ');
1448           lcd_gotoxy (1,1); lcd_putc(' '); lcd_putc (' ');
1449           lcd_gotoxy (9,1); lcd_putc(' ');
1450         }
1451
1452       clearlcd = 1;
1453
1454       if (edit == 0)
1455         {
1456           if (current_key == VK_UP)
1457             {
1458               if (pos < 28)
1459                 pos += 4;
1460               else
1461                 {
1462                   pos = 0;
1463                   current_submenu = 255;
1464                 }
1465             }
1466           else if (current_key == VK_DOWN)
1467             {
1468               if (pos > 0)
1469                 pos -= 4;
1470               else
1471                 pos = 28;
1472             }
1473           else
1474             clearlcd = 0;
1475         }
1476       else
1477         {
1478           if (current_key == VK_UP)
1479             {
1480               if (index < 3)
1481                 index++;
1482               else
1483                 index = 0;
1484             }
1485           else if (current_key == VK_DOWN)
1486             {
1487               if (index > 0)
1488                 index--;
1489               else
1490                 index = 3;
1491             }
1492           else
1493             clearlcd = 0;
1494
1495           switch (index)
1496             {
1497             case 0: lcd_gotoxy (2, 0); break;
1498             case 1: lcd_gotoxy (9, 0); break;
1499             case 2: lcd_gotoxy (2, 1); break;
1500             case 3: lcd_gotoxy (9, 1); break;
1501             }
1502           lcd_putc ('\x7e');
1503         }
1504
1505       if (current_key == VK_ENTER)
1506         {
1507           if (edit == 0)
1508             edit = 1;
1509           else if (edit == 1)
1510             {
1511               edit = 2;
1512               actionflags.menu = 1;
1513               current_key = VK_NONE;
1514               edit_timer (pos+index);
1515             }
1516           clearlcd = 1;
1517         }
1518     }
1519 }
1520
1521
1522 void
1523 shift_menu (void)
1524 {
1525   if (actionflags.menu)
1526   {
1527     /* lcd_gotoxy(0,0); lcd_puts_P("Timer - Gruppe   "); */
1528     lcd_gotoxy (0,1); lcd_puts_P ("setzen");
1529     actionflags.menu = 0;
1530   }
1531
1532   lcd_gotoxy (10,1); lcd_int (get_shift_offset () / 32 +1, 1, 0);
1533
1534   if ((current_key == VK_UP) || (current_key == VK_DOWN))
1535     put_shift_offset (get_shift_offset ()? 0 : 32);
1536
1537   if (current_key == VK_ENTER)
1538     current_submenu = 255;
1539 }
1540
1541
1542 void
1543 temperature_menu (void)
1544 {
1545   static unsigned char kennlinie, index;
1546
1547   if (actionflags.menu)
1548     {
1549       lcd_gotoxy (0, 0); lcd_puts_P ("  \x3   \xdf bei    \xdf");
1550       lcd_gotoxy (0, 1); lcd_puts_P ("  \x4   \xdf bei    \xdf");
1551       actionflags.menu = 0;
1552     }
1553
1554   lcd_gotoxy (0,0); lcd_putc (kennlinie);
1555
1556   lcd_gotoxy (4,0);  lcd_int (get_t_boiler_max (kennlinie) / 10, 2, 0);
1557   lcd_gotoxy (4,1);  lcd_int (get_t_boiler_min (kennlinie) / 10, 2, 0);
1558   lcd_gotoxy (12,0); lcd_int (get_t_curve_low (kennlinie) / 10, -2, 0);
1559   lcd_gotoxy (12,1); lcd_int (get_t_curve_high  (kennlinie) / 10, -2, 0);
1560
1561   switch (index)
1562     {
1563     case 0:
1564       lcd_gotoxy(11,1); lcd_putc(' ');
1565       if (current_key == VK_UP)
1566         {
1567           if (kennlinie < N_CHARACTERISTICS-1)
1568             kennlinie++;
1569           else
1570             {
1571               kennlinie = 0;
1572               current_submenu = 255;
1573             }
1574         }
1575
1576       if (current_key == VK_DOWN)
1577         {
1578           if (kennlinie > 0)
1579             kennlinie--;
1580           else
1581             kennlinie = N_CHARACTERISTICS-1;
1582         }
1583       break;
1584
1585     case 1:
1586       lcd_gotoxy (7,0); lcd_putc ('\x7f');
1587       if (current_key == VK_UP)
1588         inc_t_boiler_max (kennlinie, 10);
1589       if (current_key == VK_DOWN)
1590         inc_t_boiler_max (kennlinie, -10);
1591       break;
1592
1593     case 2:
1594       lcd_gotoxy (7,0); lcd_putc(' ');
1595       lcd_gotoxy (11,0); lcd_putc('\x7e');
1596       if (current_key == VK_UP)
1597         inc_t_curve_low (kennlinie, 10);
1598       if (current_key == VK_DOWN)
1599         inc_t_curve_low (kennlinie, -10);
1600       break;
1601
1602     case 3:
1603       lcd_gotoxy (11,0); lcd_putc (' ');
1604       lcd_gotoxy (7,1); lcd_putc ('\x7f');
1605       if (current_key == VK_UP)
1606         inc_t_boiler_min (kennlinie, 10);
1607       if (current_key == VK_DOWN)
1608         inc_t_boiler_min (kennlinie, -10);
1609       break;
1610
1611     case 4:
1612       lcd_gotoxy (7,1); lcd_putc (' ');
1613       lcd_gotoxy (11,1); lcd_putc ('\x7e');
1614       if (current_key == VK_UP)
1615         inc_t_curve_high (kennlinie, 10);
1616       if (current_key == VK_DOWN)
1617         inc_t_curve_high (kennlinie, -10);
1618       break;
1619     }
1620
1621   if (current_key == VK_ENTER)
1622     {
1623       if (index < 4)
1624         index++;
1625       else
1626         index = 0;
1627     }
1628 }
1629
1630
1631 void
1632 pump_menu (void)
1633 {
1634   static unsigned char tmp_mode;
1635   int celsius;
1636
1637   if (actionflags.menu)
1638     {
1639       actionflags.menu = 0;
1640
1641       lcd_gotoxy (0,0); lcd_puts_P ("Pumpe aus       ");
1642       lcd_gotoxy (0,1); lcd_puts_P ("      Tk <    \xdf" "C");
1643
1644       switch (tmp_mode)
1645         {
1646         case NIGHT_MODE:
1647           lcd_gotoxy( 12,0); lcd_putc (tmp_mode);
1648           lcd_gotoxy (0,1); lcd_puts_P ("Nacht");
1649           break;
1650
1651         case DAY_MODE:
1652           lcd_gotoxy (12,0); lcd_putc (tmp_mode);
1653           lcd_gotoxy (0,1); lcd_puts_P ("Tag");
1654           break;
1655
1656         case ABSENT_MODE:
1657           lcd_gotoxy (12,0); lcd_putc (tmp_mode);
1658           lcd_gotoxy (0,1); lcd_puts_P ("Abw.");
1659           break;
1660         }
1661     }
1662
1663   lcd_gotoxy (11,1);
1664   celsius = get_t_pump_on (tmp_mode);
1665   lcd_int (celsius/10, 3, 0);
1666
1667   switch (tmp_mode)
1668     {
1669     case NIGHT_MODE:
1670     case DAY_MODE:
1671     case ABSENT_MODE:
1672       if (current_key == VK_UP && celsius < 850 )
1673         inc_t_pump_on (tmp_mode, 10);
1674       else if (current_key == VK_DOWN && celsius > 10)
1675         inc_t_pump_on (tmp_mode, -10);
1676       break;
1677     }
1678
1679   if (current_key == VK_ENTER)
1680     {
1681       if (tmp_mode < 2)
1682         tmp_mode++;
1683       else
1684         {
1685           tmp_mode = 0;
1686           current_submenu = 255;
1687         }
1688       actionflags.menu = 1;
1689     }
1690 }
1691
1692
1693 void
1694 history_menu (void)
1695 {
1696   static unsigned char index, tmp_verb[5], tmp_temp[5];
1697   unsigned char i;
1698
1699   if (actionflags.menu)
1700     {
1701       actionflags.menu = 0;
1702       index = get_history_index () - 1;
1703
1704       for (i = 0; i < 5; i++)
1705         {
1706           if (index >= MAX_LOGGING)
1707             index = MAX_LOGGING -1;
1708           tmp_verb[i] = get_consumption (index);
1709           tmp_temp[i] = get_temperature (index);
1710           index--;
1711         }
1712     }
1713
1714   if (actionflags.refresh_lcd)
1715     {
1716       lcd_gotoxy(0,0);
1717       for (i=0; i < 5; i++)
1718         {
1719           if (tmp_verb[i] <= 100)
1720             lcd_int (tmp_verb[i], 3, 0);
1721           else
1722             lcd_puts_P (" - ");
1723         }
1724       lcd_putc ('%');
1725       lcd_gotoxy (0,1);
1726       for (i=0; i < 5; i++)
1727         {
1728           if (tmp_temp[i] <= 100)
1729             lcd_int (tmp_temp[i], -2, 0);
1730           else
1731             lcd_puts_P (" - ");
1732         }
1733       lcd_putc ('\xdf');
1734     }
1735
1736   switch (current_key)
1737     {
1738     case VK_UP:
1739     case VK_DOWN:
1740     case VK_ENTER:
1741       current_submenu = 255;
1742       actionflags.menu = 1;
1743       break;
1744    }
1745 }
1746
1747
1748 void
1749 select_menu (void)
1750 {
1751   static unsigned char index;
1752
1753   if (actionflags.refresh_lcd)
1754     {
1755       lcd_gotoxy (0, 1); lcd_puts_P ("                 ");
1756       lcd_gotoxy (0, 0);
1757       switch (index)
1758         {
1759         case 0:
1760           lcd_puts_P ("Status           ");
1761           break;
1762         case 1:
1763           lcd_puts_P ("Timer - Setup    ");
1764           break;
1765         case 2:
1766           lcd_puts_P ("Schalt - Gruppe  ");
1767           break;
1768         case 3:
1769           lcd_puts_P ("Kennlinien       ");
1770           break;
1771         case 4:
1772           lcd_puts_P ("Wasserpumpe      ");
1773           break;
1774         case 5:
1775           lcd_puts_P ("Verbrauch \xf5" "ber   ");
1776           lcd_gotoxy (0, 1);
1777           lcd_puts_P ("5 Tage");
1778           break;
1779         }
1780     }
1781
1782   switch (current_key)
1783     {
1784     case VK_UP:
1785       if (index < 5)
1786         index++;
1787       else
1788         index = 0;
1789       break;
1790
1791     case VK_DOWN:
1792       if (index > 0)
1793         index--;
1794       else
1795         index = 5;
1796       break;
1797
1798     case VK_ENTER:
1799       current_submenu = index;
1800       actionflags.menu = 1;
1801       break;
1802     }
1803 }
1804
1805
1806 void
1807 get_controlpoint (void)
1808 {
1809   signed long target, dK, dT;
1810
1811   if (outside_temperature < get_t_curve_low (operation_mode))
1812     {
1813       target = get_t_boiler_max (operation_mode);
1814     }
1815   else if (outside_temperature > get_t_curve_high (operation_mode))
1816     {
1817       target = get_t_boiler_min (operation_mode);
1818     }
1819   else
1820     {
1821       dK = (get_t_boiler_max (operation_mode)
1822             - get_t_boiler_min (operation_mode));
1823       dT = (get_t_curve_high (operation_mode)
1824             - get_t_curve_low (operation_mode));
1825       target = (get_t_boiler_max (operation_mode)
1826                 - ((outside_temperature
1827                     - get_t_curve_low (operation_mode)) * dK) / dT);
1828     }
1829   boiler_temperature_target = target;
1830 }
1831
1832
1833 void
1834 switch_lit_relay (int on)
1835 {
1836   if (on)
1837     RELAY_LIT |= _BV(RELAY_LIT_BIT);
1838   else
1839     RELAY_LIT &= ~_BV(RELAY_LIT_BIT);
1840 }
1841
1842 void
1843 switch_night_led (int on)
1844 {
1845   if (on)
1846     LED_NIGHT |= _BV(LED_NIGHT_BIT);
1847   else
1848     LED_NIGHT &= ~_BV(LED_NIGHT_BIT);
1849 }
1850
1851 void
1852 switch_absent_led (int on)
1853 {
1854   if (on)
1855     LED_ABSENT |= _BV(LED_ABSENT_BIT);
1856   else
1857     LED_ABSENT &= ~_BV(LED_ABSENT_BIT);
1858 }
1859
1860
1861 void
1862 relay_control (void)
1863 {
1864   static unsigned char old_operation_mode, delay_counter;
1865
1866   if (boiler_temperature > boiler_temperature_target + BOILER_HYSTERESIS)
1867     {
1868       if (bit_is_set (RELAY_BOILER, RELAY_BOILER_BIT))
1869         {
1870           RELAY_BOILER &= ~_BV(RELAY_BOILER_BIT);
1871           if (actionflags.monitor_mode)
1872             actionflags3.status_change = 1;
1873         }
1874     }
1875   else if (boiler_temperature < boiler_temperature_target - BOILER_HYSTERESIS)
1876     {
1877       if (bit_is_clear (RELAY_BOILER, RELAY_BOILER_BIT))
1878         {
1879           RELAY_BOILER |= _BV(RELAY_BOILER_BIT);
1880           if (actionflags.monitor_mode)
1881             actionflags3.status_change = 1;
1882         }
1883     }
1884
1885   if (old_operation_mode == operation_mode)
1886     {
1887       if ((boiler_temperature < get_t_pump_on (operation_mode))
1888           && (boiler_temperature_target < get_t_pump_on (operation_mode)))
1889         {
1890           /* Boiler temperature dropped below the limit. */
1891           if (bit_is_set (RELAY_PUMP, RELAY_PUMP_BIT))
1892             {
1893               RELAY_PUMP &= ~_BV(RELAY_PUMP_BIT);
1894               if (actionflags.monitor_mode)
1895                 actionflags3.status_change = 1;
1896             }
1897         }
1898       else if ((boiler_temperature_target
1899                 > get_t_pump_on (operation_mode) + 10)
1900                && (boiler_temperature > get_t_pump_on (operation_mode)))
1901         {
1902           if (bit_is_clear (RELAY_PUMP, RELAY_PUMP_BIT))
1903             {
1904               RELAY_PUMP |= _BV(RELAY_PUMP_BIT);
1905               if (actionflags.monitor_mode)
1906                 actionflags3.status_change = 1;
1907             }
1908         }
1909     }
1910   else /* Operation mode changed - take action after a delay.  */
1911     {
1912       if (--delay_counter == 0)
1913         {
1914           old_operation_mode = operation_mode;
1915           if (actionflags.monitor_mode)
1916             actionflags3.status_change = 1;
1917         }
1918     }
1919 }
1920
1921 /* Process the main menu.  */
1922 void
1923 run_menu (void)
1924 {
1925   if (current_key == VK_MODE)
1926     {
1927       if (operation_mode < ABSENT_MODE)
1928         operation_mode++;
1929       else
1930         operation_mode = 0;
1931
1932       actionflags.menu= 1;
1933     }
1934   else if (current_key == VK_MENU)
1935     {
1936       /* Instead of showing the menu we now directly switch to the
1937          status menu.  */
1938       current_submenu = 0;
1939       actionflags.menu= 1;
1940       actionflags2.reset_status_menu= 1;
1941     }
1942
1943   switch (current_submenu)
1944     {
1945     case 0: status_menu (); break;
1946     case 1: timer_menu ();  break;
1947     case 2: shift_menu (); break;
1948     case 3: temperature_menu (); break;
1949     case 4: pump_menu (); break;
1950     case 5: history_menu(); break;
1951     default: select_menu (); break;
1952     }
1953 }
1954
1955
1956 /* Check the values in the eeprom.  */
1957 void
1958 init_eeprom (void)
1959 {
1960   unsigned char i;
1961   unsigned int aword = 0;
1962   unsigned char abyte = 0;
1963
1964   if (eeprom_read_byte (&e2_init_marker) == E2_INIT_MARKER_OKAY)
1965     return;
1966
1967   lcd_clear ();
1968   lcd_puts_P ("Init EEPROM");
1969   lcd_gotoxy (0, 1);
1970   for (i = 0; i < 64; i++)
1971     {
1972       delay_ms (15);
1973       switch (i % 4)
1974         {
1975         case 0:
1976           aword = MK_TIME (7,00);
1977           abyte = DAY_MODE;
1978           break;
1979         case 1:
1980           aword = MK_TIME (9,00);
1981           abyte = DEACTIVATED_MODE;
1982           break;
1983         case 2:
1984           aword = MK_TIME (17,00);
1985           abyte = DEACTIVATED_MODE;
1986           break;
1987         case 3:
1988           aword = MK_TIME (22,00);
1989           abyte = NIGHT_MODE;
1990           lcd_putc ('.');
1991           break;
1992         }
1993
1994       if ((i%32) < 4)
1995         aword |= WORKDAY;
1996       else if ((i%32)<12)
1997         aword += (((i%32-4)/4)+5) * 1440;
1998       else
1999         {
2000           aword += (((i%32-12)/4)) * 1440;
2001           abyte = DEACTIVATED_MODE;
2002         }
2003
2004       put_timer_time (i, aword);
2005       put_timer_mode (i, abyte);
2006     }
2007
2008   eeprom_update_word (&ee_t_curve_low[DAY_MODE], -100);
2009   eeprom_update_word (&ee_t_boiler_max[DAY_MODE], 750);
2010   eeprom_update_word (&ee_t_curve_high[DAY_MODE], 180);
2011   eeprom_update_word (&ee_t_boiler_min[DAY_MODE], 380);
2012   eeprom_update_word (&ee_t_pump_on[DAY_MODE], 400);
2013
2014   eeprom_update_word (&ee_t_curve_low[NIGHT_MODE], -150);
2015   eeprom_update_word (&ee_t_boiler_max[NIGHT_MODE], 600);
2016   eeprom_update_word (&ee_t_curve_high[NIGHT_MODE], 180);
2017   eeprom_update_word (&ee_t_boiler_min[NIGHT_MODE], 380);
2018   eeprom_update_word (&ee_t_pump_on[NIGHT_MODE], 450);
2019
2020   eeprom_update_word (&ee_t_boiler_max[ABSENT_MODE], 600);
2021   eeprom_update_word (&ee_t_boiler_min[ABSENT_MODE], 350);
2022   eeprom_update_word (&ee_t_curve_low[ABSENT_MODE], -150);
2023   eeprom_update_word (&ee_t_curve_high[ABSENT_MODE], 200);
2024   eeprom_update_word (&ee_t_pump_on[ABSENT_MODE], 440);
2025
2026   put_shift_offset (0);
2027
2028   for(i = 0 ; i < MAX_LOGGING ;i++)
2029     {
2030       /* Fixme:  Replace using a block fucntion. */
2031       eeprom_update_byte (&ee_consumption_buffer[i], 0xFF);
2032       eeprom_update_byte (&ee_temperature_buffer[i], 0xFF);
2033     }
2034   put_history_index (0);
2035
2036   eeprom_write_byte (&e2_init_marker, E2_INIT_MARKER_OKAY);
2037 }
2038
2039
2040 /* Store the oil/gas/pellets consumption in a history file.  */
2041 void
2042 store_consumption (void)
2043 {
2044   uint8_t idx = get_history_index ();
2045
2046   put_consumption (idx, ((long) burner_time * 100) / total_time);
2047   put_temperature (idx, avg_temperature / 1440);
2048   avg_temperature = 0;
2049   burner_time = 0;
2050   total_time = 0;
2051   if (++idx >= MAX_LOGGING)
2052     idx = 0;
2053   put_history_index (idx);
2054 }
2055
2056
2057 static void
2058 do_colon_linefeed (void)
2059 {
2060   do_putchar (':');
2061   do_putchar ('\r');
2062   do_putchar ('\n');
2063 }
2064
2065
2066 /* Run an AT command found in ATCOMMAND.  Returns 0 on success or true
2067    on error. */
2068 static char
2069 do_atcommand (void)
2070 {
2071   static char cmd[16];
2072   static char echo_mode;
2073   uint8_t i, c;
2074
2075   for (i=0; (c=atcommand[i]) && i < sizeof cmd -1; i++)
2076     cmd[i] = c;
2077   cmd[i] = c;
2078
2079   if (echo_mode)
2080     {
2081       uart_puts_P ("AT");
2082       uart_puts (cmd);
2083       uart_puts_P ("\r\n");
2084     }
2085
2086   if (!*cmd)
2087     ;
2088   else if (*cmd == 'i' || *cmd == 'I')
2089     uart_puts_P ("Heating Control (AT&H for help)\r\n");
2090   else if (*cmd == 'e' || *cmd == 'E')
2091     echo_mode = (cmd[1] == '1');
2092   else if (*cmd == '&')
2093     {
2094       if (cmd[1] == 'h' || cmd[1] == 'H')
2095         {
2096           uart_puts_P (" ATI  - info\r\n"
2097                        " ATEn - switch local echo on/off\r\n"
2098                        " AT&H - this help page\r\n"
2099                        " AT&V - status and current temperature\r\n"
2100                        " AT+H - list history\r\n"
2101                        " AT+C - list configuration\r\n"
2102                        " AT+Ln - switch display light on/off\r\n"
2103                        " AT+Mn - switch monitor mode on/off\r\n"
2104                        " AT+TIME=<min> - set system time to MIN\r\n");
2105         }
2106       else if (cmd[1] == 'v' || cmd[1] == 'V')
2107         actionflags2.send_data = 1;
2108       else
2109         return 1;
2110     }
2111   else if (*cmd == '+')
2112     {
2113       if (cmd[1] == 'h' || cmd[1] == 'H')
2114         actionflags2.send_hist = 1;
2115       else if (cmd[1] == 'l' || cmd[1] == 'L')
2116         lit_timer = cmd[2] == '1'? 20 : 0;
2117       else if ((cmd[1] == 'c' || cmd[1] == 'C') && !cmd[2])
2118         actionflags2.send_conf = 1;
2119       else if (cmd[1] == 'm' || cmd[1] == 'M')
2120         actionflags.monitor_mode = (cmd[2] == '1');
2121       else if (cmd[1] == 'T' && cmd[2] == 'I' && cmd[3] == 'M'
2122                && cmd[4] == 'E' && cmd[5] == '=' && cmd[6])
2123         current_time = atoi (cmd+6);
2124       else
2125         return 1;
2126     }
2127   else
2128     return 1;
2129   return 0; /* Okay.  */
2130 }
2131
2132
2133 /*
2134     Entry point
2135  */
2136 int
2137 main (void)
2138 {
2139   operation_mode = DAY_MODE;
2140   actionflags.menu = 1;
2141
2142   /* Port A: Pins 0..7 to input.
2143      PINA.7 = KEY-1
2144      PINA.6 = KEY-2
2145      PINA.5 = KEY-3
2146      PINA.4 = KEY-4
2147      PINA.3 = KEY-5
2148      PINA.2 = ADC2 = PA-2
2149      PINA.1 = ADC1 = PA-1
2150      PINA.0 = ADC0 = AIN-1
2151   */
2152   PORTA = 0x00;
2153   DDRA  = 0x00;
2154
2155   /* Port B: Pins 0..4 to output, pins 5..7 to input.
2156      PINB.7  = SCK
2157      PINB.6  = MISI
2158      PINB.5  = MOSI
2159      PORTB.4 = RELAY_LIT
2160      PORTB.3 = RELAY_PUMP
2161      PORTB.2 = RELAY_BOILER
2162      PORTB.1 = LED_ABSENT
2163      PORTB.0 = LED_NIGHT
2164   */
2165   PORTB = 0x00;
2166   DDRB  = 0x1f;
2167
2168   /* Port C: Pins 0..7 to input.
2169      PINC.7 = LCD_DATA3_PIN
2170      PINC.6 = LCD_DATA2_PIN
2171      PINC.5 = LCD_DATA1_PIN
2172      PINC.4 = LCD_DATA0_PIN
2173      PINC.3 = DIGIN1 ("PC3" on the PCB)
2174      PINC.2 = LCD_EN
2175      PINC.1 = LCD_RW
2176      PINC.0 = LCD_RS
2177   */
2178   PORTC = 0x00;
2179   DDRC  = 0x00;
2180
2181   /* Port D: Pins 0..7 to input.
2182      PIND.7 =
2183      PIND.6 =
2184      PIND.5 =
2185      PIND.4 =
2186      PIND.3 =
2187      PIND.2 = IR (SFH506, pin3)
2188      PIND.1 = TXD
2189      PIND.0 = RXD
2190   */
2191   PORTD = 0x00;
2192   DDRD  = 0x00;
2193
2194
2195   /* Init timer/counter 0 to:
2196    * Clock source: system clock.
2197    * Clock value: timer 0 stopped.
2198    * Mode: normal top = 0xff.
2199    * OC0 output: disconnected.
2200    */
2201   TCCR0 = 0x02;
2202   TCNT0 = 0x64;
2203   OCR0  = 0x00;
2204
2205   /* Init timer/counter 1 to:
2206    * Clock source: system clock.
2207    * Clock value: timer 1 stopped
2208    * Mode: normal top = 0xffff
2209    * OC1A output: disconnected.
2210    * OC1B output: disconnected.
2211    * Noise canceler: off.
2212    * Input capture on falling edge.
2213    */
2214   TCCR1A = 0x00;
2215   TCCR1B = 0x00;
2216   TCNT1H = 0x00;
2217   TCNT1L = 0x00;
2218   OCR1AH = 0x00;
2219   OCR1AL = 0x00;
2220   OCR1BH = 0x00;
2221   OCR1BL = 0x00;
2222
2223   /* Init timer/counter 2 to:
2224    * Clock source: system clock.
2225    * Clock value: 125.000 kHz.
2226    * Mode: normal top = 0xff.
2227    * OC2 output: disconnected.
2228    */
2229   ASSR  = 0x00;
2230   TCCR2 = 0x0c;    /* Set precaler to clk/64, select CTC mode.  */
2231   TCNT2 = 0x00;    /* Clear timer/counter register.  */
2232   OCR2  = 124;     /* Compare value for 1ms.  */
2233
2234   /* Init external interrupts.  */
2235   /* GICR  |= 0x40;  /\* Enable Int0.  *\/ */
2236   /* MCUCR |= 0x02;  /\* Trigger on falling edge.  *\/ */
2237   /* GIFR  |= 0x40;  /\* Clear Int0 flag.  *\/ */
2238 #ifdef USE_TURN_PUSH
2239   GICR  |= 0x80;  /* Enable Int1.  */
2240   MCUCR |= 0x08;  /* Trigger on falling edge.  */
2241   GIFR  |= 0x80;  /* Clear Int1 flag.  */
2242 #endif
2243
2244   /* Init timer/counter interrupts.  */
2245   TIMSK = 0x80;
2246
2247   /* Set the UART: 8n1, async, rc and tx on, rx and tx ints enabled.
2248      Baud rate set to the value computed from BAUD.  */
2249   UCSRA = 0x00;
2250   UCSRB = _BV(RXCIE) | _BV(TXCIE) | _BV(RXEN) | _BV(TXEN);
2251   UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0);
2252   UBRRH = (UBRR_VAL >> 8);
2253   UBRRL = (UBRR_VAL & 0xff);
2254
2255   /* Init the analog comparator:
2256    * Analog comparator: off (ACSR.7 = 1)
2257    * Input capture by timer/counter 1: off.
2258    * Analog comparator output: off
2259    */
2260   ACSR  = 0x80;
2261   SFIOR = 0x00;
2262
2263   /* Init the ADC:
2264    * Enable, Single conversion.
2265    * Prescaler = 64 (at 8Mhz => 125kHz).
2266    */
2267   ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1);
2268
2269   /* Prepare the LCD.  */
2270   lcd_init ();
2271
2272   /* Reset the eeprom if the enter key is pressed while booting.  */
2273   if (KEY_4)
2274     eeprom_write_byte (&e2_init_marker, 0xff);
2275
2276   /*           1234567890123456 */
2277   lcd_gotoxy (0, 0);
2278   lcd_puts_P ("I.Busker,H.Buss,");
2279   lcd_gotoxy (0,1);
2280   lcd_puts_P ("W.Koch");
2281
2282   delay_ms (1500);
2283
2284   init_eeprom ();
2285
2286   read_t_sensors (1);
2287
2288   /* Enable interrupts.  */
2289   sei ();
2290
2291   uart_puts_P ("?: system ready\r\n");
2292
2293   /* Main loop.  */
2294   for (;;)
2295    {
2296      static unsigned char tmp_mode;
2297
2298      while (!actionflags.run_main)
2299        {
2300          set_sleep_mode(SLEEP_MODE_IDLE);
2301          cli();
2302          if (!actionflags.run_main)
2303            {
2304              sleep_enable();
2305              sei();
2306              sleep_cpu();
2307              sleep_disable();
2308            }
2309          sei();
2310        }
2311      actionflags.run_main = 0;
2312
2313      tmp_mode = get_operation_mode (current_time);
2314      if (tmp_mode != DEACTIVATED_MODE)
2315        operation_mode = tmp_mode;
2316
2317      run_menu ();
2318      read_t_sensors (0);
2319      get_controlpoint ();
2320      relay_control ();
2321      poll_keyboard ();
2322
2323      switch_lit_relay (lit_timer);
2324      switch_night_led (operation_mode == NIGHT_MODE);
2325      switch_absent_led (operation_mode == ABSENT_MODE);
2326
2327      if (actionflags.day)
2328        {
2329          store_consumption ();
2330          actionflags.day = 0;
2331        }
2332
2333      if (run_atcommand)
2334        {
2335          actionflags.output = SERIAL;
2336          if (do_atcommand ())
2337            uart_puts_P ("ERROR\r\n");
2338          else
2339            uart_puts_P ("OK\r\n");
2340          run_atcommand = 0;
2341        }
2342
2343      if (actionflags2.send_hist || actionflags2.send_data
2344          || actionflags2.send_conf || actionflags3.status_change)
2345        {
2346          unsigned int tmptime = current_time;
2347
2348          actionflags.output = SERIAL;
2349
2350          actionflags3.status_change = 0;
2351
2352          uart_putc ('s');
2353          uart_putc (':');
2354          uart_int (tmptime / 1440, 0, 0);
2355          uart_putc (':');
2356          uart_int ((tmptime % 1440) / 60, 2, LEADING_ZERO);
2357          uart_putc (':');
2358          uart_int ((tmptime % 1440) % 60, 2, LEADING_ZERO);
2359          uart_putc (':');
2360          uart_int (tmptime, 0, 0);
2361          uart_putc (':');
2362          uart_int (operation_mode, 0, 0);
2363          uart_putc (':');
2364          uart_putc (get_shift_offset ()?'1':'0');
2365          uart_putc (':');
2366          uart_putc ((RELAY_BOILER & _BV (RELAY_BOILER_BIT))?'1':'0');
2367          uart_putc (':');
2368          uart_putc ((RELAY_PUMP & _BV (RELAY_PUMP_BIT))?'1':'0');
2369          uart_putc (':');
2370          uart_putc (':');
2371          uart_putc (':');
2372          uart_putc (':');
2373          uart_putc (':');
2374          uart_int (total_time, 0, 0);
2375          uart_putc (':');
2376          uart_int (burner_time, 0, 0);
2377          do_colon_linefeed ();
2378
2379          if (actionflags2.send_data)
2380            {
2381              actionflags2.send_data = 0;
2382
2383              uart_putc ('t');
2384              uart_putc (':');
2385              uart_int (boiler_temperature_target, 0, 0);
2386              uart_putc (':');
2387              uart_int (boiler_temperature, 0, 0);
2388              uart_putc (':');
2389              uart_int (outside_temperature, 0, 0);
2390              uart_putc (':');
2391              uart_putc (':');
2392              uart_putc (':');
2393              uart_int (real_temperature[0], 0, 0);
2394              uart_putc (':');
2395              uart_int (real_temperature[1], 0, 0);
2396              do_colon_linefeed ();
2397
2398            }
2399
2400          if (actionflags2.send_hist)
2401            {
2402              uint8_t tmpidx;
2403              uint8_t consumption;
2404
2405              actionflags2.send_hist = 0;
2406
2407              uart_putc ('h');
2408              do_colon_linefeed ();
2409
2410              tmpidx = get_history_index () - 1;
2411              while (tmpidx != get_history_index ())
2412                {
2413                  if (tmpidx >= MAX_LOGGING)
2414                    tmpidx = MAX_LOGGING-1;
2415                  if (tmpidx == get_history_index ())
2416                    break;
2417
2418                  consumption = get_consumption (tmpidx);
2419                  if (consumption <= 100)
2420                    {
2421                      uart_putc ('h');
2422                      uart_putc (':');
2423                      uart_int (tmpidx, 0, 0);
2424                      uart_putc (':');
2425                      uart_int (consumption, 0, 0);
2426                      uart_putc (':');
2427                      uart_int (get_temperature (tmpidx), 0, 0);
2428                      do_colon_linefeed ();
2429                    }
2430                  tmpidx--;
2431                }
2432            }
2433
2434          if (actionflags2.send_conf)
2435            {
2436              actionflags2.send_conf = 0;
2437            }
2438        }
2439    }
2440 }
2441
2442
2443 /*
2444 Writing:
2445  avrdude -c usbasp -pm32 -U flash:w:heating-control.hex
2446 Fuse bits:
2447  lfuse = 0xEF (8MHz crystal)
2448  hfuse = 0xD1 (ie. disable JTAG)
2449 avrdude -c usbasp -pm32 -v -B 4 -U lfuse:w:0xEF:m
2450 avrdude -c usbasp -pm32 -v -B 4 -U hfuse:w:0xD1:m
2451
2452 Local Variables:
2453 compile-command: "avr-gcc -Wall -Wno-pointer-sign -g -mmcu=atmega32 -Os -o heating-control.elf heating-control.c -lc ; avr-objcopy -O ihex -j .text -j .data  heating-control.elf heating-control.hex"
2454 End:
2455 */