Collected changes.
authorwerner <werner>
Tue, 27 Jul 2010 10:08:22 +0000 (10:08 +0000)
committerwerner <werner>
Tue, 27 Jul 2010 10:08:22 +0000 (10:08 +0000)
ChangeLog
heating-control.c [new file with mode: 0644]
mkdiff
sha1sum.c
sks-stats.sh

index ef8dbbf..8a7a183 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2010-07-27  Werner Koch  <wk@g10code.com>
+
+       * sha1sum.c (unescapefname): Fix unescaping.
+
+2009-09-02  Werner Koch  <wk@g10code.com>
+
+       * mkdiff: Fix sort(1) syntax.
+
 2006-11-21  Werner Koch  <wk@g10code.com>
 
        * scrutmime.c (message_cb): Don't exit for HTML but my name in the
diff --git a/heating-control.c b/heating-control.c
new file mode 100644 (file)
index 0000000..7b48b55
--- /dev/null
@@ -0,0 +1,2174 @@
+/* heating-control.c - heating control for an ATmega32.
+   (c) Holger Buss
+   Copyright (C) 2010 Werner Koch
+   
+   FIXME:  Figure out copyright and license stuff.
+
+   Original notice:
+   *********************************************
+   Project : Heizung ATmega32
+   Version : 2.0
+   Date    : 09.12.2003  // AT90C8535
+   Date    : 08.01.2006  // ATMEGA32
+   Date    : 29.11.2006  // Fehler im Taktgenerator (Uhr ging falsch)
+   Author  : (c) Holger Buss
+   http://www.mikrocontroller.com --> Bauanleitungen
+   Chip type           : ATmega32
+   Program type        : Application
+   Clock frequency     : 8.000000 MHz
+   Memory model        : Small
+   External SRAM size  : 0
+   Data Stack size     : 512
+   *********************************************
+
+
+   2010-02-08 wk  Almost complete rewrite to allow building with
+                  avr-gcc.  Changed the source language to English as
+                  a convenience to non-German speaking hackers.  The
+                  LCD code has been written from scratch.
+
+*/
+
+
+/*#define USE_TURN_PUSH*/
+/*#define NO_LCD*/
+
+/* Clock frequency in Hz. */
+#define F_CPU 8000000UL
+
+#include <stdio.h>
+#include <util/delay.h>
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+#include <avr/eeprom.h>
+#include <avr/sleep.h>
+
+
+#define DIM(v)              (sizeof(v)/sizeof((v)[0]))
+
+
+/* Display definitions.  */
+#define LCD_DDR          DDRC         /* DDR for the LCD pins.   */
+#define LCD_PORT         PORTC        /* Port for the LCD pins.  */
+#define LCD_PIN          PINC         /* Pin for the LCD pins.   */
+#define LCD_RS_PIN       0            /* Pin # for RS.           */
+#define LCD_RW_PIN       1            /* Pin # for RW.           */
+#define LCD_E_PIN        2            /* Pin # for Enable line.  */
+#define LCD_DATA0_PIN    4            /* Pin # for data bit 0.   */
+#define LCD_DATA1_PIN    5            /* Pin # for data bit 1.   */
+#define LCD_DATA2_PIN    6            /* Pin # for data bit 2.   */
+#define LCD_DATA3_PIN    7            /* Pin # for data bit 3.   */
+#define LCD_DATA_MASK    0b11110000   /* Mask for the data pins. */ 
+#define LCD_PIN_MASK     0b11110111   /* Mask for all used pins. */ 
+
+/* UART defs.  */
+#define BAUD       9600ul
+#define UBRR_VAL   ((F_CPU+8*BAUD)/(16*BAUD)-1)
+#define BAUD_REAL  (F_CPU/(16*(UBRR_VAL+1)))
+#define BAUD_ERROR ((1000*BAUD_REAL)/BAUD)
+#if (BAUD_ERROR < 990 || BAUD_ERROR > 1010)
+# error computed baud rate out of range
+#endif
+
+
+/* Time data.  */
+typedef struct
+{
+  unsigned int time;
+  unsigned char mode;
+} timer_data_t;
+
+timer_data_t ee_timer[65] EEMEM;
+
+static inline unsigned int
+get_timer_time (int idx)
+{
+  return (idx < 0 || idx >= DIM(ee_timer))
+    ? 0 : eeprom_read_word (&ee_timer[idx].time);
+}
+
+static inline unsigned char
+get_timer_mode (int idx)
+{
+  return (idx < 0 || idx >= DIM(ee_timer))
+    ? 0 : eeprom_read_byte (&ee_timer[idx].mode);
+}
+
+static inline void
+put_timer_time (int idx, uint16_t value)
+{
+  if (idx >= 0 && idx < DIM (ee_timer))
+    eeprom_update_word (&ee_timer[idx].time, value);
+}
+
+static inline void
+put_timer_mode (int idx, uint8_t value)
+{
+  if (idx >= 0 && idx < DIM (ee_timer))
+    eeprom_update_byte (&ee_timer[idx].mode, value);
+}
+
+
+/* Toggled by the shift menu between 0 and 32. */
+unsigned char ee_shift_offset EEMEM;
+static inline uint8_t
+get_shift_offset (void)
+{
+  return eeprom_read_byte (&ee_shift_offset);
+}
+static inline void
+put_shift_offset (uint8_t value)
+{
+  eeprom_update_byte (&ee_shift_offset, value);   
+}
+
+/* The number of characteristics we support; they are mapped one to
+   one to the operating modes.  */
+#define N_CHARACTERISTICS 3
+
+/* The maximum number of consumption log entries.  */
+#define MAX_LOGGING 250
+
+/* A ring buffer to store the consumption.  */
+unsigned char ee_consumption_buffer[MAX_LOGGING] EEMEM;
+static inline uint8_t
+get_consumption (int idx)
+{
+  return idx >= 0 && idx < MAX_LOGGING
+    ? eeprom_read_byte (&ee_consumption_buffer[idx]) : 0;
+}
+static inline void
+put_consumption (int idx, uint8_t value)
+{
+  if (idx >= 0 && idx < MAX_LOGGING)
+    eeprom_update_byte (&ee_consumption_buffer[idx], value);
+}
+
+/* A ring buffer to store the temperatures.  */
+unsigned char ee_temperature_buffer[MAX_LOGGING] EEMEM;
+static inline uint8_t
+get_temperature (int idx)
+{
+  return idx >= 0 && idx < MAX_LOGGING
+    ? eeprom_read_byte (&ee_temperature_buffer[idx]) : 0;
+}
+static inline void
+put_temperature (int idx, uint8_t value)
+{
+  if (idx >= 0 && idx < MAX_LOGGING)
+    eeprom_update_byte (&ee_temperature_buffer[idx], value);
+}
+
+/* The index to the next to use log item. */
+unsigned char ee_history_index EEMEM;
+static inline uint8_t
+get_history_index (void)
+{
+  return eeprom_read_byte (&ee_history_index);
+}
+static inline void
+put_history_index (uint8_t value)
+{
+  eeprom_update_byte (&ee_history_index, value);
+}
+
+signed int  ee_t_boiler_min[N_CHARACTERISTICS] EEMEM;
+static inline int16_t
+get_t_boiler_min (int idx)
+{
+  return (idx >= 0 && idx < N_CHARACTERISTICS)
+    ? eeprom_read_word (&ee_t_boiler_min[idx]) : 0;
+}
+static inline void
+inc_t_boiler_min (int idx, int16_t value)
+{
+  if (idx >= 0 && idx < N_CHARACTERISTICS)
+    {
+      int16_t val = eeprom_read_word (&ee_t_boiler_min[idx]);
+      val += value;
+      eeprom_write_word (&ee_t_boiler_min[idx], val);
+    }
+}
+
+signed int  ee_t_boiler_max[N_CHARACTERISTICS] EEMEM;
+static inline int16_t
+get_t_boiler_max (int idx)
+{
+  return (idx >= 0 && idx < N_CHARACTERISTICS)
+    ? eeprom_read_word (&ee_t_boiler_max[idx]) : 0;
+}
+static inline void
+inc_t_boiler_max (int idx, int16_t value)
+{
+  if (idx >= 0 && idx < N_CHARACTERISTICS)
+    {
+      int16_t val = eeprom_read_word (&ee_t_boiler_max[idx]);
+      val += value;
+      eeprom_write_word (&ee_t_boiler_max[idx], val);
+    }
+}
+
+/* Definitions of the characteristics curves.  */
+signed int  ee_t_curve_high[N_CHARACTERISTICS] EEMEM;
+static inline int16_t
+get_t_curve_high (int idx)
+{
+  return (idx >= 0 && idx < N_CHARACTERISTICS)
+    ? eeprom_read_word (&ee_t_curve_high[idx]) : 0;
+}
+static inline void
+inc_t_curve_high (int idx, int16_t value)
+{
+  if (idx >= 0 && idx < N_CHARACTERISTICS)
+    {
+      int16_t val = eeprom_read_word (&ee_t_curve_high[idx]);
+      val += value;
+      eeprom_write_word (&ee_t_curve_high[idx], val);
+    }
+}
+
+signed int  ee_t_curve_low[N_CHARACTERISTICS] EEMEM;
+static inline int16_t
+get_t_curve_low (int idx)
+{
+  return (idx >= 0 && idx < N_CHARACTERISTICS)
+    ? eeprom_read_word (&ee_t_curve_low[idx]) : 0;
+}
+static inline void
+inc_t_curve_low (int idx, int16_t value)
+{
+  if (idx >= 0 && idx < N_CHARACTERISTICS)
+    {
+      int16_t val = eeprom_read_word (&ee_t_curve_low[idx]);
+      val += value;
+      eeprom_write_word (&ee_t_curve_low[idx], val);
+    }
+}
+
+signed int  ee_t_pump_on[N_CHARACTERISTICS] EEMEM;
+static inline int16_t
+get_t_pump_on (int idx)
+{
+  return (idx >= 0 && idx < N_CHARACTERISTICS)
+    ? eeprom_read_word (&ee_t_pump_on[idx]) : 0;
+}
+static inline void
+inc_t_pump_on (int idx, int16_t value)
+{
+  if (idx >= 0 && idx < N_CHARACTERISTICS)
+    {
+      int16_t val = eeprom_read_word (&ee_t_pump_on[idx]);
+      val += value;
+      eeprom_write_word (&ee_t_pump_on[idx], val);
+    }
+}
+
+/* A flag and a value to detect whether the eeprom has been setup.  */
+unsigned char e2_init_marker EEMEM;
+#define E2_INIT_MARKER_OKAY 0x3a
+
+
+/* The current time measured in minutes.  */
+volatile unsigned int current_time;
+
+
+/* Constants for the operation modes.  The values are assumed to be
+   ascending starting from 0.  They are used as an index; the value
+   for deactivated is treated especially, though.  They also index the
+   custom glyphs we use.*/
+enum {
+  NIGHT_MODE       = 0,
+  DAY_MODE         = 1,
+  ABSENT_MODE      = 2,
+  DEACTIVATED_MODE = 3
+};
+
+/* Flag indicating a workday.  */
+#define WORKDAY     0x4000
+#define TIMEFLAG2   0x8000  /* Fixme: Purpose is unknown.  */
+
+/* The current operation mode.  */
+unsigned char operation_mode;
+
+
+unsigned int burner_time;
+unsigned int total_time;
+
+/* The names of the weekdays.  */
+static const PROGMEM char weekday_names[7][3] = 
+  { "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So" };
+
+
+/* Flags for scheduled actions.  */
+volatile struct
+{
+  unsigned char menu:1;
+  unsigned char minute:1;
+  unsigned char day:1;
+  unsigned char send_data:1;
+  unsigned char send_lcd:1;
+  unsigned char refresh_lcd:1;
+  unsigned char output:1;
+  unsigned char run_main:1;
+} actionflags;
+
+
+volatile struct
+{
+  unsigned char show_target_temp:1;
+} actionflags2;
+
+
+/* The hardware keys.  */
+#define KEY_1       bit_is_set (PINA, PINA7)
+#define KEY_2       bit_is_set (PINA, PINA6)
+#define KEY_3       bit_is_set (PINA, PINA5)
+#define KEY_4       bit_is_set (PINA, PINA4)
+#define KEY_5       bit_is_set (PINA, PINA3)
+
+/* We map the hardware keys to one virtual key with these values.  */
+enum
+  {
+    VK_NONE = 0,
+    VK_MENU = 1,
+    VK_UP   = 3,
+    VK_DOWN = 4,
+    VK_ENTER = 5,
+    VK_MODE = 6
+  };
+
+/* The current value of our virtual key.  */
+unsigned char current_key;
+
+/* The indentifier of the current submenu.  */
+unsigned char current_submenu;
+
+
+/* The measured temperatures and an average value.  */
+signed int  outside_temperature;
+signed int  boiler_temperature;
+signed long avg_temperature;        
+
+#define LED_NIGHT        (PORTB)
+#define LED_NIGHT_BIT    (0)
+#define LED_ABSENT       (PORTB)
+#define LED_ABSENT_BIT   (1)
+#define RELAY_BOILER     (PORTB)
+#define RELAY_BOILER_BIT (2)
+#define RELAY_PUMP       (PORTB)
+#define RELAY_PUMP_BIT   (3)
+#define RELAY_DAY        (PORTB)
+#define RELAY_DAY_BIT    (4)
+
+/* The hysteresis of the boiler temperature measured in 0.1 degress.  */
+#define BOILER_HYSTERESIS 15
+
+/* The desired temperatur of the boiler.  */
+signed int boiler_temperature_target = 0; 
+
+
+#define MK_TIME(h,m)  ((h)*60+(m))
+
+
+static void lcd_load_user_glyphs (void);
+
+void lcd_delay_ms(uint8_t ms) {  _delay_ms (ms); }
+#define delay_ms(ms)      _delay_ms ((ms))
+
+/* Despite what the Displaytech docs and several examples say, the
+   disassembly of the original code of this program shows that 42
+   cycles are used.  We use the provided delay loop which runs at 3
+   cycles and ignore that gcc does need all the 7 cycles for the setup
+   (rcall, ldi, ret).  */
+#define _lcd_e_delay()   do { _delay_loop_1 (15); } while (0)
+#define _lcd_e_high()    do { LCD_PORT  |=  _BV(LCD_E_PIN); } while (0)
+#define _lcd_e_low()     do { LCD_PORT  &= ~_BV(LCD_E_PIN); } while (0)
+#define _lcd_e_toggle()  do {                  \
+                            _lcd_e_high ();    \
+                            _lcd_e_delay ();   \
+                            _lcd_e_low ();     \
+                         } while (0)
+#define _lcd_rw_high()   do { LCD_PORT |=  _BV(LCD_RW_PIN); } while (0)
+#define _lcd_rw_low()    do { LCD_PORT &= ~_BV(LCD_RW_PIN); } while (0)
+#define _lcd_rs_high()   do { LCD_PORT |=  _BV(LCD_RS_PIN); } while (0)
+#define _lcd_rs_low()    do { LCD_PORT &= ~_BV(LCD_RS_PIN); } while (0)
+#define _lcd_waitbusy()  do { while (_lcd_read (1) & 0x80); } while (0)
+
+/* Send a command to the LCD.  */
+#define lcd_command(cmd) do {                      \
+                              _lcd_waitbusy ();    \
+                              _lcd_write (cmd, 1); \
+                         } while (0)
+
+/* Write a data byte to the display.  */
+#define lcd_putc(c)      do {                      \
+                              _lcd_waitbusy ();    \
+                              _lcd_write (c, 0);   \
+                         } while (0)
+
+
+/* Clear the display.  */
+#define lcd_clear()  lcd_command (0x01);
+
+/* Got to the home position.  */
+#define lcd_home()   lcd_command (0x02);
+
+
+static uint8_t
+_lcd_read (uint8_t read_ctrl)
+{
+  uint8_t value = 0;
+
+  PORTB |= 4;
+  if (read_ctrl)
+    _lcd_rs_low (); 
+  else
+    _lcd_rs_high (); 
+  _lcd_rw_high ();
+
+  /* Configure data pins as input.  */
+  LCD_DDR &= ~LCD_DATA_MASK;
+
+  /* Read high nibble.  */
+  _lcd_e_high ();
+  _lcd_e_delay ();
+  if (bit_is_set (LCD_PIN, LCD_DATA0_PIN))
+    value |= 0x10;
+  if (bit_is_set (LCD_PIN, LCD_DATA1_PIN))
+    value |= 0x20;
+  if (bit_is_set (LCD_PIN, LCD_DATA2_PIN))
+    value |= 0x40;
+  if (bit_is_set (LCD_PIN, LCD_DATA3_PIN))
+    value |= 0x80;
+  _lcd_e_low ();
+
+  _lcd_e_delay ();
+
+  /* Read low nibble */
+  _lcd_e_high ();
+  _lcd_e_delay ();
+  if (bit_is_set (LCD_PIN, LCD_DATA0_PIN))
+    value |= 0x01;
+  if (bit_is_set (LCD_PIN, LCD_DATA1_PIN))
+    value |= 0x02;
+  if (bit_is_set (LCD_PIN, LCD_DATA2_PIN))
+    value |= 0x04;
+  if (bit_is_set (LCD_PIN, LCD_DATA3_PIN))
+    value |= 0x08;
+  _lcd_e_low ();
+
+  /* Set data bits to output and set them to high. */
+  LCD_PORT |= LCD_DATA_MASK;
+  LCD_DDR  |= LCD_DATA_MASK;
+
+  _lcd_rw_low ();
+  PORTB &= ~4;
+  return value;
+}
+
+
+static void
+_lcd_write (uint8_t value, uint8_t write_ctrl)
+{
+  uint8_t data;
+
+  if (write_ctrl)
+    _lcd_rs_low ();
+  else
+    _lcd_rs_high ();
+  _lcd_rw_low ();
+
+  /* Configure data pins as output.  */
+  LCD_DDR |= LCD_DATA_MASK;
+
+  /* Write high nibble.  */
+  data = 0;
+  if ((value & 0x80))
+    data |= _BV (LCD_DATA3_PIN);
+  if ((value & 0x40))
+    data |= _BV (LCD_DATA2_PIN);
+  if ((value & 0x20))
+    data |= _BV (LCD_DATA1_PIN);
+  if ((value & 0x10))
+    data |= _BV (LCD_DATA0_PIN);
+  LCD_PORT &= ~LCD_DATA_MASK;
+  LCD_PORT |= data;
+  _lcd_e_toggle ();
+
+  /* Write low nibble.  */
+  data = 0;
+  if ((value & 0x08))
+    data |= _BV (LCD_DATA3_PIN);
+  if ((value & 0x04))
+    data |= _BV (LCD_DATA2_PIN);
+  if ((value & 0x02))
+    data |= _BV (LCD_DATA1_PIN);
+  if ((value & 0x01))
+    data |= _BV (LCD_DATA0_PIN);
+  LCD_PORT &= ~LCD_DATA_MASK;
+  LCD_PORT |= data;
+  _lcd_e_toggle ();
+
+  /* Set data bits to high. */
+  LCD_PORT |= LCD_DATA_MASK;
+}
+
+
+
+/* Init the LCD to 4 bit mode.  */
+void
+lcd_init (void)
+{
+  /* Configure all used pins as output and clear them.  */
+  LCD_PORT &= ~LCD_PIN_MASK;
+  LCD_DDR |= LCD_PIN_MASK;
+
+  /* RS is cleared to send a command; cmd = 0x03.
+     Command must be repeated two times.  */
+  lcd_delay_ms (15);
+  LCD_PORT |= (_BV (LCD_DATA1_PIN) | _BV (LCD_DATA0_PIN));
+  _lcd_e_toggle ();
+  lcd_delay_ms (5);
+  _lcd_e_toggle ();
+  lcd_delay_ms (1);
+  _lcd_e_toggle ();
+  lcd_delay_ms (1);
+
+  /* Select 4 bit mode.  */
+  LCD_PORT &= ~LCD_PIN_MASK;
+  LCD_PORT |= _BV (LCD_DATA1_PIN);
+  _lcd_e_toggle ();
+  lcd_delay_ms (1);
+
+  /* Set function: 4bit,     2 lines,  5x7.     */
+  /*               (bit 4=0) (bit 3=1) (bit2=0) */ 
+  lcd_command (0x20 | 8 );
+
+  /* Display off.  */
+  lcd_command (0x08);
+  /* Display clear.  */
+  lcd_command (0x01);
+  /* Entry mode set: increase cursor, display is not shifted */
+  /*                 (bit 1)          ( bit 0)               */
+  lcd_command (0x04 | 2 );
+
+  /* Display is now ready. Switch it on.  */
+
+  /* Display on/off: Display on, cursor off, blinking off.  */
+  /*                 (bit 2)     (bit 1)     (bit 0)        */
+  lcd_command (0x08 | 4 );
+
+  lcd_load_user_glyphs ();
+}
+
+
+/* Load the our special glyphs.  */
+static void
+lcd_load_user_glyphs (void)
+{
+  static const PROGMEM char glyphs[5][8] =
+    {
+      { /* glyph 0 - moon */
+        0b0000000,
+        0b0000000,
+        0b0001110,  
+        0b0011110,
+        0b0011110,
+        0b0001110,
+        0b0000000,
+        0b0000000
+      },
+      { /* glyph 1 - sun */
+        0b0000100,
+        0b0010101,
+        0b0001110,
+        0b0010001,
+        0b0010001,
+        0b0001110,
+        0b0010101,
+        0b0000100
+      },
+      { /* glyph 2 - circle */
+        0b0000000,
+        0b0000000,
+        0b0001110,
+        0b0010001,
+        0b0010001,
+        0b0001110,
+        0b0000000,
+        0b0000000
+      },
+      { /* glyph 3 - up arrow */
+        0b0000000,
+        0b0000100,
+        0b0001110,
+        0b0010101,
+        0b0000100,
+        0b0000100,
+        0b0000100,
+        0b0000000
+      },
+      { /* glyph 4 - down arrow */
+        0b0000000,
+        0b0000100,
+        0b0000100,
+        0b0000100,
+        0b0010101,
+        0b0001110,
+        0b0000100,
+        0b0000000
+      }
+    };
+  unsigned char idx, g, row;
+
+  for (idx=0; idx < DIM (glyphs); idx++)
+    {
+      lcd_command ((0x80 | idx)); /* Set DDRAM address.  */
+      g = (0x40 | (idx * 8));     /* First Set CGRAM command. */
+      for (row=0; row < 8; row++)
+        {
+          lcd_command (g++);
+          lcd_putc (pgm_read_byte (&glyphs[idx][row]));
+        }
+    }
+}
+
+
+/* Set the next data write position to X,Y.  */
+void
+lcd_gotoxy (uint8_t x, uint8_t y)
+{
+  lcd_command (0x80 | ((y? 0x40:0) + x));
+}
+
+
+uint8_t
+lcd_getc (void)
+{
+  _lcd_waitbusy ();
+  return _lcd_read (0);
+}
+
+void
+lcd_puts (const char *s)
+{
+  uint8_t c;
+  
+  while ((c = *s++))
+    lcd_putc (c);
+}
+
+
+#define lcd_puts_P(s)  _lcd_puts_P (PSTR ((s)))
+void
+_lcd_puts_P (const char *progmem_s)
+{
+  uint8_t c;
+  
+  while ((c = pgm_read_byte (progmem_s++)))
+    lcd_putc (c);
+}
+
+
+
+\f
+#define LCD      0
+#define SERIAL  1
+#define LEADING_ZERO  -1 
+#define lcd_int(w, a, p)   format_int (LCD,    (w), (a), (p))
+#define uart_int(w, a, p)  format_int (SERIAL, (w), (a), (p))
+
+void
+uart_putc (char c)
+{
+  while (!bit_is_set (UCSRA, UDRE))
+    ;
+  UDR = c;
+}                                              
+
+
+void
+uart_puts (const char *s)
+{
+  uint8_t c;
+  
+  while ((c = *s++))
+    {
+      if (c == '\n')
+        uart_putc ('\r');
+      uart_putc (c);
+    }
+}
+
+#define uart_puts_P(s)  _uart_puts_P (PSTR ((s)))
+void
+_uart_puts_P (const char *progmem_s)
+{
+  uint8_t c;
+  
+  while ((c = pgm_read_byte (progmem_s++)))
+    {
+      if (c == '\n')
+        uart_putc ('\r');
+      uart_putc (c);
+    }
+}
+
+
+void
+do_putchar (char c)
+{
+  if (actionflags.output == LCD)
+    lcd_putc (c);
+  else
+    uart_putc (c);
+}                                              
+
+
+void
+format_int (char output, signed int value, signed char width,
+            signed char precision)
+{
+  unsigned int table[] = {0,1,10,100,255};
+  signed char i;
+  unsigned char pos, zero;  
+   
+  actionflags.output = output;
+  
+  if (width < 0)                         
+    {
+      width = - width;
+      if (value < 0) 
+        {
+          value = -value;
+          do_putchar ('-');
+        }
+      else
+        do_putchar('+');                    
+    }
+
+  if (precision > width || width > 3)
+    {
+      for (i=0; i < width; i++)
+        do_putchar ('?');
+      return;                            
+    }
+  
+  if (value >= table[width + 1])     
+    {
+      for (i=0; i < width; i++)
+        do_putchar ('?');
+      return;                            
+    }
+  
+  zero = (precision < 0);
+  
+  for (i=width; i > 0; i--)
+    {
+      if (i == precision)
+        {
+          do_putchar ('.');
+          zero = 1;
+        } 
+      pos = '0' + (value / table[i]);
+      
+      if ((pos == '0') && !zero && i != 1)
+        do_putchar ((i == precision+1)? '0':' ');
+      else
+        {
+          zero = 1;                        
+          do_putchar (pos);
+          value %= table[i];            
+        }
+    }   
+}
+
+
+/* Read the value from the ADC SOURCE.  */
+unsigned int
+read_adc (unsigned char source)
+{
+  ADMUX = source;
+  ADCSRA |= _BV(ADSC);  /* ADC Start Conversion.  */
+  while (bit_is_clear (ADCSRA, ADIF)) /* Wait for Interrupt Flag.  */
+    ;
+  ADCSRA |= _BV(ADIF);  /* Clear ADC Interrupt Flag.  */
+  /* Read the value to scale it. */
+  return ((ADCW*25)/4)-2250; 
+}
+
+
+/* Read the temperature sensors.  */
+void
+read_t_sensors (unsigned char first_time)
+{
+  signed int value;
+
+  /* First the outside temerature.  */
+  value = read_adc (1); /* Read ADC1 using AREF. */
+  if (first_time)
+    outside_temperature = value;
+
+  /* Slowly adjust our idea of the outside temperature to the
+     readout. */
+  if (actionflags.minute) 
+    {
+      if (outside_temperature > value)
+        outside_temperature--;
+      else if (outside_temperature < value)
+        outside_temperature++;
+      actionflags.minute = 0;
+    }
+
+  /* Second the boiler temperature.  */
+  value = read_adc (2); /* Read ADC2 using AREF. */
+  if (boiler_temperature > value)
+    boiler_temperature--;
+  else if (boiler_temperature < value)
+    boiler_temperature++;
+
+  if (first_time)
+    boiler_temperature = value;
+}
+
+
+/* 1ms ticker interrupt service routine. */
+ISR (TIMER2_COMP_vect)
+{
+  static unsigned int clock; /* Milliseconds of the current minute.  */
+  static unsigned int tim;
+  
+  if (++clock == 60000) 
+   {
+     /* One minute has passed.  Bump the current time.  */
+     current_time++; 
+     clock = 0; 
+     actionflags.minute = 1;
+     
+     if (!(current_time % 1440)) 
+       actionflags.day = 1;
+
+     if (current_time >= 1440*7) 
+       current_time = 0;
+     
+     /* Update the average temperature.  */
+     avg_temperature += outside_temperature / 10;
+   } 
+
+  if (++tim == 2000)
+    { 
+      /* Two seconds passed.  */
+      if (bit_is_clear (RELAY_BOILER, RELAY_BOILER_BIT))
+        burner_time++;
+      total_time++;
+      tim = 0;
+      actionflags2.show_target_temp = !actionflags2.show_target_temp;
+   }
+
+  /* Run the main loop very 35ms.  */
+  if (!(clock % 35))
+    {
+      actionflags.run_main = 1;
+
+      /* Request an LCD refresh every 350ms.  */
+      if (!(clock % 350))
+        actionflags.refresh_lcd = 1;
+    }      
+}
+
+
+/* UART transmit interrupt service routine.  */
+ISR (USART_TXC_vect)
+{
+  /* Nothing to do.  */
+}
+
+
+/* UART receive interrupt service routine.  */
+ISR (USART_RXC_vect)
+{
+  unsigned char c = UDR;
+  
+  switch (c)
+    {
+    case 'T':                        actionflags.send_data = 1; break;
+    case '2': current_key = VK_UP;   actionflags.send_lcd = 1; break;
+    case '1': current_key = VK_DOWN; actionflags.send_lcd = 1; break;
+    case '3': current_key = VK_MODE; break;
+    case ' ':                        actionflags.send_lcd = 1; break;
+    case '\r':current_key = VK_ENTER; actionflags.send_lcd = 1; break; 
+    } 
+  
+}
+
+
+/* External interrupt 1 service routine.  This is only used with the
+   "turn-push" button option.  */
+#ifdef USE_TURN_PUSH
+ISR (INT1_vect)
+{
+  unsigned char key;
+  
+  key = KEY_2;
+  
+  if (MCUCR==0x08) /* Falling edge */
+    {
+      current_key = !key? VK_DOWN : VK_UP;
+      MCUCR = 0x0C;
+    }
+  else /* Rising edge.  */
+    {  
+      MCUCR = 0x08;  
+      current_key = !key? VK_UP : VK_DOWN;
+    }
+  GIFR |= 0x80;
+}                      
+#endif /*USE_TURN_PUSH*/
+
+
+/* Poll the keyboard and debounce the up and down keys.  */
+#define DEBOUNCE_COUNT 7
+#define DEBOUNCE(a,b)  do {                                   \
+                         if (!(a)++ || (a) == DEBOUNCE_COUNT) \
+                           current_key = (b);                 \
+                         if ((a) == DEBOUNCE_COUNT)           \
+                           (a) = 3;                           \
+                       } while (0)
+void
+poll_keyboard (void)
+{
+  static char key1, key2, key3, key4, key5;
+  
+  current_key = VK_NONE;
+
+#ifdef USE_TURN_PUSH
+  
+#else
+  if (KEY_1)
+    DEBOUNCE (key1, VK_MENU);
+  else
+    key1 = 0;
+
+  if(KEY_2)
+    DEBOUNCE (key2, VK_DOWN);
+  else 
+    key2 = 0;
+  
+  if (KEY_3)
+    DEBOUNCE (key3, VK_UP);
+  else
+    key3 = 0;
+#endif
+
+  if (KEY_4)
+    DEBOUNCE (key4, VK_ENTER);
+  else
+    key4 = 0;
+
+  if (KEY_5)
+    DEBOUNCE (key5, VK_MODE);
+  else
+    key5 = 0;
+}                   
+#undef DEBOUNCE
+#undef DEBOUNCE_COUNT
+
+
+unsigned char
+get_operation_mode (unsigned int time)
+{
+  unsigned char result = DEACTIVATED_MODE;
+  unsigned char i;
+  uint16_t t;
+  uint8_t shiftoff;
+   
+  for (i = 0; i < 32; i++) 
+    {  
+      shiftoff = get_shift_offset ();
+      t = get_timer_time (i + shiftoff);
+      if ((t & WORKDAY))
+        {
+          if (time  < (6*1440)  /* Monday to Friday.  */
+              && ((t & ~WORKDAY) % 1440) == (time % 1440))
+            { 
+              result = get_timer_mode (i + shiftoff);
+              break;
+            } /* FiXME:  original remark:  please test. */
+        }
+      else if (t == time) 
+        { 
+          result = get_timer_mode (i + shiftoff);
+          break; 
+        } 
+    }
+
+  return result;
+}
+
+
+void
+status_menu (void)
+{  
+  static unsigned char index, blink;
+
+  if (actionflags.menu)
+    { 
+      lcd_gotoxy (2, 0);
+      lcd_puts_P ("             %");
+      actionflags.menu = 0;
+    }                  
+
+  if (actionflags.refresh_lcd)
+    {
+      lcd_gotoxy (0, 0); _lcd_puts_P (weekday_names[current_time/1440]);
+   
+      lcd_gotoxy (3 ,0); lcd_int ((current_time % 1440) / 60, 2 , LEADING_ZERO);
+      
+      lcd_putc (blink? ':' : ' ');
+      blink = !blink;
+     
+      lcd_int ((current_time % 1440) % 60, 2 ,LEADING_ZERO);
+      lcd_gotoxy (0, 1); 
+      if (actionflags2.show_target_temp)
+        { 
+          lcd_puts_P ("Ts="); 
+          lcd_int (boiler_temperature_target/10, -2, 0); 
+        }                                                         
+      else
+        {
+          lcd_puts_P ("Ta=");
+          lcd_int (outside_temperature/10, -2, 0); 
+        }                                                         
+      lcd_putc ('\xdf'); lcd_putc(' ');
+
+      lcd_puts_P("Tk=");
+      lcd_int (boiler_temperature/10, -2, 0);
+      lcd_putc ('\xdf'); lcd_putc('C');
+      lcd_gotoxy(12,0);
+      lcd_int ( total_time? ((long) burner_time * 100) / total_time:0, 3, 0);
+
+      lcd_gotoxy(10,0);
+      switch (operation_mode)
+        {
+        case DAY_MODE:   
+        case NIGHT_MODE:  
+        case ABSENT_MODE:
+          lcd_putc (operation_mode);
+          break;
+        case DEACTIVATED_MODE: 
+          lcd_putc (' ');
+          break;
+        }
+      
+      actionflags.refresh_lcd = 0; 
+    } 
+
+
+  
+  switch (index)
+      { 
+      case 0:
+        if (current_key == VK_UP || current_key == VK_DOWN)
+          {
+            current_submenu = 255;
+            actionflags.menu = 1;
+          }
+        break;
+        
+      case 1: /* Change the hour.  */
+        lcd_gotoxy (2,0); lcd_putc ('\x7e');  
+        if (current_key == VK_UP)
+          current_time += 60; 
+        else if (current_key == VK_DOWN && current_time > 60)
+          current_time -= 60; 
+        break;
+
+      case 2: /* Change the minute.  */
+         lcd_gotoxy (2,0); lcd_putc (' ');  
+         lcd_gotoxy (8,0); lcd_putc ('\x7f');
+         if (current_key == VK_UP)
+           current_time ++; 
+         else if (current_key == VK_DOWN && current_time)
+           current_time --;
+         break;
+
+      case 3: /* Change the day.  */
+         lcd_gotoxy (8,0); lcd_putc (' ');
+         lcd_gotoxy (2,0); lcd_putc ('\x7f');
+         if (current_key == VK_UP) 
+           {
+             if (current_time < 6*1440)
+               current_time += 1440;
+             else
+               current_time %= 1440;
+           }
+         else if (current_key == VK_DOWN)
+           {
+             if (current_time > 1440)
+               current_time -= 1440;
+             else 
+               current_time += 6*1440;
+           }
+         break;
+      }
+    
+    if (current_key == VK_ENTER) 
+      {
+        if (index < 3)
+          index++;
+        else 
+          { 
+            index = 0;      
+            actionflags.menu = 1;
+          }
+      }                     
+}
+
+
+unsigned char 
+edit_timer (unsigned char pos)
+{
+  static unsigned int tmp_wert, time, tag_time;
+  static unsigned char tmp_mode, index;
+  uint16_t t;
+  
+
+  pos += get_shift_offset ();
+  
+  if (actionflags.menu)
+    {
+      actionflags.menu = 0;
+      lcd_gotoxy(2,0); lcd_puts_P ("              ");
+
+      lcd_gotoxy (0, 0);
+      t = get_timer_time (pos);
+      if (t & WORKDAY)
+        lcd_puts_P ("WT");
+      else
+        _lcd_puts_P (weekday_names[t % (1440*7) / 1440]);
+      
+      lcd_gotoxy (0, 1); lcd_puts_P ("Mode   ");
+      time = t & ~(WORKDAY|TIMEFLAG2);
+      tmp_mode = get_timer_mode (pos);
+      tmp_wert = time;                            
+      tag_time = (time / 1440)*1440; 
+      if (t & WORKDAY)
+        tag_time = WORKDAY;
+    }
+   
+  lcd_gotoxy (7 ,0);
+  lcd_int ((tmp_wert % 1440) / 60, 2, LEADING_ZERO);
+  lcd_putc(':');
+  lcd_int ((tmp_wert % 1440) % 60, 2, LEADING_ZERO);
+
+  lcd_gotoxy(7,1);
+  switch (tmp_mode)
+    {
+    case DAY_MODE:
+      lcd_puts_P ("Tag      "); 
+      lcd_gotoxy (14, 0);
+      lcd_putc (tmp_mode);
+      break;
+
+    case NIGHT_MODE:
+      lcd_puts_P ("Nacht    "); 
+      lcd_gotoxy (14, 0);
+      lcd_putc (tmp_mode);
+      break;
+
+    case ABSENT_MODE:
+      lcd_puts_P ("Abwesend "); 
+      lcd_gotoxy (14, 0);
+      lcd_putc (tmp_mode);
+      break;
+
+    case DEACTIVATED_MODE:
+      lcd_puts_P ("DEAKTIV  "); 
+      lcd_gotoxy (14, 0);
+      lcd_putc ('-');
+      break;
+    }
+  
+  switch (index)
+    {
+    case 0:
+      lcd_gotoxy (6, 0); lcd_putc ('\x7e');  
+      if (current_key == VK_UP)
+        tmp_wert += 60; 
+      else if (current_key == VK_DOWN)
+        tmp_wert -= 60;
+      break;
+      
+    case 1: 
+      lcd_gotoxy (6,0); lcd_putc(' ');  
+      lcd_gotoxy (12,0); lcd_putc('\x7f');
+      if (current_key == VK_UP)
+        tmp_wert ++; 
+      else if (current_key == VK_DOWN)
+        tmp_wert --;
+      break;
+      
+    case 2: 
+      lcd_gotoxy (12,0); lcd_putc (' ');
+      lcd_gotoxy (6,1); lcd_putc ('\x7e');
+      if (current_key == VK_UP)
+        {
+          if (tmp_mode < 3)
+            tmp_mode++;
+          else
+            tmp_mode = 0;
+        }
+      else if (current_key == VK_DOWN)
+        {
+          if (tmp_mode > 0)
+            tmp_mode--;
+          else
+            tmp_mode = 3;
+        }
+      break;
+    }
+  
+  if (current_key == VK_ENTER) 
+    {
+      if (index < 2)
+        index++;
+      else 
+        { 
+          put_timer_mode (pos, tmp_mode);
+          put_timer_time (pos, (tmp_wert % 1440) | tag_time);
+          index = 0;      
+          actionflags.menu = 1;
+          return 1;
+        }
+    }                     
+   return 0;
+}
+
+
+void
+show_timer (char x, char y, unsigned char pos)
+{
+  unsigned int time;  
+  uint8_t mode;
+  
+  pos += get_shift_offset ();
+  
+  time = get_timer_time (pos) & ~(WORKDAY|TIMEFLAG2);
+  mode = get_timer_mode (pos);
+  
+  lcd_gotoxy (x,y);
+     
+  if (mode == DEACTIVATED_MODE)
+    lcd_puts_P (" --:--"); 
+  else 
+    {  
+      lcd_putc (mode);
+      lcd_int ((time % 1440) / 60, 2, LEADING_ZERO);
+      lcd_putc(':');
+      lcd_int ((time % 1440) % 60, 2, LEADING_ZERO); 
+    }
+}
+
+
+void 
+timer_menu (void)
+{                 
+  static unsigned char pos, index, edit;
+  static unsigned char clearlcd = 1;
+  uint16_t atime;
+
+  if (actionflags.menu) 
+   { 
+     actionflags.menu = 0;
+     edit = 0; 
+     clearlcd = 1;
+   };
+  
+  if (edit == 2) /* Edit timer.  */
+    {
+      if (edit_timer (pos+index))
+        edit = 0;   
+    }
+  else /* Select timer.  */
+    {
+      if (clearlcd)
+        {  
+          lcd_gotoxy(0,0);
+          atime = get_timer_time (pos);
+          if (atime & WORKDAY)
+            lcd_puts_P ("WT");                           
+          else 
+            _lcd_puts_P (weekday_names[atime % (1440*7) / 1440]);
+      
+          lcd_gotoxy(0,1); lcd_int((get_shift_offset ()/32)+1, 1, 0);
+          
+          show_timer (3 ,0, pos);
+          show_timer (10,0, pos+1);
+          show_timer (3 ,1, pos+2);
+          show_timer (10,1, pos+3);
+          clearlcd = 0;   
+          lcd_gotoxy (2,0); lcd_putc(' ');
+          lcd_gotoxy (9,0); lcd_putc(' '); 
+          lcd_gotoxy (1,1); lcd_putc(' '); lcd_putc (' ');
+          lcd_gotoxy (9,1); lcd_putc(' '); 
+        }                  
+      
+      clearlcd = 1; 
+  
+      if (edit == 0)
+        {
+          if (current_key == VK_UP)
+            {
+              if (pos < 28)
+                pos += 4; 
+              else
+                {
+                  pos = 0; 
+                  current_submenu = 255; 
+                }
+            }
+          else if (current_key == VK_DOWN)
+            {
+              if (pos > 0)
+                pos -= 4;
+              else
+                pos = 28;
+            }
+          else
+            clearlcd = 0;    
+        }
+      else
+        {
+          if (current_key == VK_UP)
+            {
+              if (index < 3)
+                index++;
+              else 
+                index = 0;
+            }
+          else if (current_key == VK_DOWN)
+            {
+              if (index > 0)
+                index--;
+              else
+                index = 3; 
+            }
+          else
+            clearlcd = 0;
+
+          switch (index)
+            {
+            case 0: lcd_gotoxy (2, 0); break;
+            case 1: lcd_gotoxy (9, 0); break;
+            case 2: lcd_gotoxy (2, 1); break;
+            case 3: lcd_gotoxy (9, 1); break;
+            }
+          lcd_putc ('\x7e'); 
+        }
+      
+      if (current_key == VK_ENTER) 
+        { 
+          if (edit == 0) 
+            edit = 1;
+          else if (edit == 1) 
+            {  
+              edit = 2;               
+              actionflags.menu = 1;
+              current_key = VK_NONE;
+              edit_timer (pos+index);
+            }   
+          clearlcd = 1;        
+        }
+    }   
+}
+
+
+void
+shift_menu (void)
+{
+  if (actionflags.menu)
+  {
+    /* lcd_gotoxy(0,0); lcd_puts_P("Timer - Gruppe   "); */
+    lcd_gotoxy (0,1); lcd_puts_P ("setzen");
+    actionflags.menu = 0;
+  }                  
+                                            
+  lcd_gotoxy (10,1); lcd_int (get_shift_offset () / 32 +1, 1, 0);
+  
+  if ((current_key == VK_UP) || (current_key == VK_DOWN)) 
+    put_shift_offset (get_shift_offset ()? 0 : 32);
+  
+  if (current_key == VK_ENTER) 
+    current_submenu = 255;
+}
+
+
+void
+temperature_menu (void)
+{
+  static unsigned char kennlinie, index;
+  
+  if (actionflags.menu)
+    {
+      lcd_gotoxy (0, 0); lcd_puts_P ("  \x4   \xdf bei    \xdf");
+      lcd_gotoxy (0, 1); lcd_puts_P ("  \x5   \xdf bei    \xdf");
+      actionflags.menu = 0;
+    }                  
+  
+  lcd_gotoxy (0,0); lcd_putc (kennlinie+1);
+  
+  lcd_gotoxy (4,0);  lcd_int (get_t_boiler_max (kennlinie) / 10, 2, 0); 
+  lcd_gotoxy (4,1);  lcd_int (get_t_boiler_min (kennlinie) / 10, 2, 0); 
+  lcd_gotoxy (12,0); lcd_int (get_t_curve_high (kennlinie) / 10, -2, 0); 
+  lcd_gotoxy (12,1); lcd_int (get_t_curve_low  (kennlinie) / 10, -2, 0); 
+  switch (index)
+    {
+    case 0:
+      lcd_gotoxy(11,1); lcd_putc(' ');
+      if (current_key == VK_UP)
+        {
+          if (kennlinie < 2)
+            kennlinie++; 
+          else 
+            {
+              kennlinie=0;
+              current_submenu = 255; 
+            }
+        }
+      
+      if (current_key == VK_DOWN)
+        {
+          if (kennlinie > 0) 
+            kennlinie--;
+          else
+            kennlinie = 2;
+        }
+      break;
+      
+    case 1:
+      lcd_gotoxy (7,0); lcd_putc ('\x7f');
+      if (current_key == VK_UP)
+        inc_t_boiler_max (kennlinie, 10);
+      if (current_key == VK_DOWN) 
+        inc_t_boiler_max (kennlinie, -10);
+      break;
+
+    case 2:
+      lcd_gotoxy (7,0); lcd_putc(' ');  
+      lcd_gotoxy (11,0); lcd_putc('\x7e');
+      if (current_key == VK_UP)
+        inc_t_curve_high (kennlinie, 10);
+      if (current_key == VK_DOWN)
+        inc_t_curve_high (kennlinie, -10);
+      break;
+      
+    case 3:
+      lcd_gotoxy (11,0); lcd_putc (' ');  
+      lcd_gotoxy (7,1); lcd_putc ('\x7f');
+      if (current_key == VK_UP)
+        inc_t_boiler_min (kennlinie, 10);
+      if (current_key == VK_DOWN)
+        inc_t_boiler_min (kennlinie, -10);
+      break;
+      
+    case 4:
+      lcd_gotoxy (7,1); lcd_putc (' ');  
+      lcd_gotoxy (11,1); lcd_putc ('\x7e');
+      if (current_key == VK_UP)
+        inc_t_curve_low (kennlinie, 10);
+      if (current_key == VK_DOWN)
+        inc_t_curve_low (kennlinie, -10);
+      break;
+    }
+  
+  if (current_key == VK_ENTER)
+    {
+      if (index <4)
+        index++; 
+      else 
+        index = 0;
+    }
+}
+
+
+void
+pump_menu (void)
+{                          
+  static unsigned char tmp_mode;
+  int celsius;
+
+  if (actionflags.menu)                          
+    {  
+      actionflags.menu = 0;
+      
+      lcd_gotoxy (0,0); lcd_puts_P ("Pumpe aus       ");
+      lcd_gotoxy (0,1); lcd_puts_P ("      Tk <    \xdf" "C");
+      
+      switch (tmp_mode)
+        { 
+        case NIGHT_MODE:
+          lcd_gotoxy( 12,0); lcd_putc (tmp_mode);
+          lcd_gotoxy (0,1); lcd_puts_P ("Nacht");
+          break; 
+     
+        case DAY_MODE:
+          lcd_gotoxy (12,0); lcd_putc (tmp_mode);
+          lcd_gotoxy (0,1); lcd_puts_P ("Tag");
+          break; 
+     
+        case ABSENT_MODE:
+          lcd_gotoxy (12,0); lcd_putc (tmp_mode);
+          lcd_gotoxy (0,1); lcd_puts_P ("Abw.");
+          break; 
+        }
+    }                  
+  
+  lcd_gotoxy (11,1);
+  celsius = get_t_pump_on (tmp_mode);
+  lcd_int (celsius/10, 3, 0);
+  switch (tmp_mode)
+    {
+    case NIGHT_MODE:
+    case DAY_MODE:
+    case ABSENT_MODE:
+      if (current_key == VK_UP && celsius < 850 )
+        inc_t_pump_on (tmp_mode, 10);
+      else if (current_key == VK_DOWN && celsius > 10)
+        inc_t_pump_on (tmp_mode, -10);
+      break;
+    }
+  
+  if (current_key == VK_ENTER) 
+    { 
+      if (tmp_mode < 2)
+        tmp_mode++;
+      else 
+        {
+          tmp_mode = 0;
+          current_submenu = 255; 
+        }
+      actionflags.menu = 1;
+    }   
+}
+
+
+void
+history_menu (void)
+{                          
+  static unsigned char index, tmp_verb[5], tmp_temp[5];
+  unsigned char i; 
+  
+  if (actionflags.menu)                          
+    {  
+      actionflags.menu = 0;
+      index = get_history_index () - 1;          
+      
+      for (i = 0; i < 5; i++)
+        {
+          if (index >= MAX_LOGGING)
+            index = MAX_LOGGING -1;
+          tmp_verb[i] = get_consumption (index);
+          tmp_temp[i] = get_temperature (index);
+          index--;
+        }  
+    }
+
+  if (actionflags.refresh_lcd)
+    {
+      lcd_gotoxy(0,0);
+      for (i=0; i < 5; i++)
+        {
+          if (tmp_verb[i] <= 100)
+            lcd_int (tmp_verb[i], 3, 0);
+          else
+            lcd_puts_P (" - ");
+        }
+      lcd_putc ('%');
+      lcd_gotoxy (0,1);                                         
+      for (i=0; i < 5; i++)
+        {
+          if (tmp_temp[i] <= 100)
+            lcd_int (tmp_temp[i], -2, 0);
+          else 
+            lcd_puts_P (" - ");
+        }
+      lcd_putc ('\xdf');
+    }
+  
+  switch (current_key)
+    {
+    case VK_UP:
+    case VK_DOWN:
+    case VK_ENTER:
+      current_submenu = 255; 
+      actionflags.menu = 1;   
+      break;
+   }   
+}
+
+
+void
+select_menu (void)
+{
+  static unsigned char index;
+  
+  if (actionflags.refresh_lcd)
+    {  
+      lcd_gotoxy (0, 1); lcd_puts_P ("                 ");
+      lcd_gotoxy (0, 0); 
+      switch (index) 
+        { 
+        case 0: 
+          lcd_puts_P ("Status           ");      
+          break;
+        case 1: 
+          lcd_puts_P ("Timer - Setup    ");
+          break;
+        case 2: 
+          lcd_puts_P ("Schalt - Gruppe  ");
+          break;
+        case 3: 
+          lcd_puts_P ("Kennlinien       ");
+          break;
+        case 4: 
+          lcd_puts_P ("Wasserpumpe      ");
+          break;
+        case 5: 
+          lcd_puts_P ("Verbrauch \xf5" "ber   ");
+          lcd_gotoxy (0, 1); 
+          lcd_puts_P ("5 Tage");
+          break;
+        }         
+    }                                        
+
+  switch (current_key)
+    {
+    case VK_UP:
+      if (index < 5)
+        index++;
+      else 
+        index = 0;
+      break;
+
+    case VK_DOWN:
+      if (index > 0)
+        index--;
+      else
+        index = 5;
+      break;
+  
+    case VK_ENTER:
+      current_submenu = index;
+      actionflags.menu = 1;
+      break;
+    } 
+}
+
+
+void 
+get_controlpoint (void)
+{  
+  signed long target, dK, dT;
+
+  if (outside_temperature < get_t_curve_high (operation_mode))
+    {
+      target = get_t_boiler_max (operation_mode);
+    }
+  else if (outside_temperature > get_t_curve_low (operation_mode))
+    {
+      target = get_t_boiler_min (operation_mode);
+    }
+  else
+    {
+      dK = (get_t_boiler_max (operation_mode)
+            - get_t_boiler_min (operation_mode));
+      dT = (get_t_curve_low (operation_mode)
+            - get_t_curve_high (operation_mode));
+      target = (get_t_boiler_max (operation_mode)
+                - ((outside_temperature
+                    - get_t_curve_high (operation_mode)) * dK) / dT);
+    }
+  boiler_temperature_target = target; 
+}
+
+
+void
+switch_boiler_relay (int on)
+{
+  if (!on)  /* Inverse logic.  */
+    RELAY_BOILER |= _BV(RELAY_BOILER_BIT);
+  else
+    RELAY_BOILER &= ~_BV(RELAY_BOILER_BIT);
+}
+
+void
+switch_pump_relay (int on)
+{
+  if (!on)  /* Inverse logic.  */
+    RELAY_PUMP |= _BV(RELAY_PUMP_BIT);
+  else
+    RELAY_PUMP &= ~_BV(RELAY_PUMP_BIT);
+}
+
+void
+switch_day_relay (int on)
+{
+  if (on) 
+    RELAY_DAY |= _BV(RELAY_DAY_BIT);
+  else
+    RELAY_DAY &= ~_BV(RELAY_DAY_BIT);
+}
+
+void
+switch_night_led (int on)
+{
+  if (on) 
+    LED_NIGHT |= _BV(LED_NIGHT_BIT);
+  else
+    LED_NIGHT &= ~_BV(LED_NIGHT_BIT);
+}
+
+void
+switch_absent_led (int on)
+{
+  if (on) 
+    LED_ABSENT |= _BV(LED_ABSENT_BIT);
+  else
+    LED_ABSENT &= ~_BV(LED_ABSENT_BIT);
+}
+
+
+void
+relay_control (void)
+{
+  static unsigned char old_operation_mode, delay_counter;
+
+  if (boiler_temperature
+      > boiler_temperature_target + BOILER_HYSTERESIS)
+    switch_boiler_relay (0);
+  else if (boiler_temperature < boiler_temperature_target)
+    switch_boiler_relay (1);
+  
+  if (old_operation_mode == operation_mode)
+    {
+      if ((boiler_temperature < get_t_pump_on (operation_mode))
+          && (boiler_temperature_target < get_t_pump_on (operation_mode)))
+        {
+          /* Boiler temperature dropped below the limit. */
+          switch_pump_relay (0);
+        }
+      else if ((boiler_temperature_target
+                > get_t_pump_on (operation_mode) + 10)
+               && (boiler_temperature > get_t_pump_on (operation_mode)))
+        {
+          switch_pump_relay (1);
+        }
+    }   
+  else /* Operation mode changed - take action after a delay.  */
+    {
+      if (--delay_counter == 0)
+        old_operation_mode = operation_mode;
+    }
+}
+
+/* Process the main menu.  */
+void 
+run_menu (void)
+{      
+  if (current_key == VK_MODE) 
+    { 
+      if (operation_mode < ABSENT_MODE)
+        operation_mode++;
+      else 
+        operation_mode = 0; 
+      
+      actionflags.menu= 1;
+    }
+  else if (current_key == VK_MENU) 
+    {
+      current_submenu = 255;
+      actionflags.menu= 1;
+    }
+  
+  switch (current_submenu)
+    {
+    case 0: status_menu (); break;
+    case 1: timer_menu ();  break;
+    case 2: shift_menu (); break;
+    case 3: temperature_menu (); break;
+    case 4: pump_menu (); break;             
+    case 5: history_menu(); break;   
+    default: select_menu (); break;
+    }
+}
+
+
+/* Check the values in the eeprom.  */
+void
+init_eeprom (void)
+{
+  unsigned char i;
+  unsigned int aword = 0;
+  unsigned char abyte = 0;
+  if (eeprom_read_byte (&e2_init_marker) == E2_INIT_MARKER_OKAY)
+    return;
+
+  lcd_clear ();
+  lcd_puts_P ("Init EEPROM");
+  lcd_gotoxy (0, 1);
+  for (i = 0; i < 64; i++)
+    {
+      delay_ms (15);
+      switch (i % 4)
+        { 
+        case 0:
+          aword = MK_TIME (7,00);
+          abyte = DAY_MODE;
+          break;
+        case 1:
+          aword = MK_TIME (9,00);
+          abyte = ABSENT_MODE;
+          break;
+        case 2:
+          aword = MK_TIME (17,00);
+          abyte = DEACTIVATED_MODE;
+          break;
+        case 3:
+          aword = MK_TIME (22,00);
+          abyte = NIGHT_MODE;
+          lcd_putc ('.');
+          break;
+        }   
+      
+      if ((i%32) < 4)
+        aword |= WORKDAY;
+      else if ((i%32)<12)
+        aword += (((i%32-4)/4)+5) * 1440;
+      else     
+        {
+          aword += (((i%32-12)/4)) * 1440;
+          abyte = DEACTIVATED_MODE;
+        }
+      
+      put_timer_time (i, aword);
+      put_timer_mode (i, abyte);
+    }
+      
+  eeprom_update_word (&ee_t_boiler_max[DAY_MODE], 750);
+  eeprom_update_word (&ee_t_boiler_min[DAY_MODE], 300);
+  eeprom_update_word (&ee_t_curve_high[DAY_MODE], -100);
+  eeprom_update_word (&ee_t_curve_low[DAY_MODE], 200);
+  eeprom_update_word (&ee_t_pump_on[DAY_MODE], 390);
+
+  eeprom_update_word (&ee_t_boiler_max[NIGHT_MODE], 500);
+  eeprom_update_word (&ee_t_boiler_min[NIGHT_MODE], 300);
+  eeprom_update_word (&ee_t_curve_high[NIGHT_MODE], -150);
+  eeprom_update_word (&ee_t_curve_low[NIGHT_MODE], 200);
+  eeprom_update_word (&ee_t_pump_on[NIGHT_MODE], 450);
+
+  eeprom_update_word (&ee_t_boiler_max[ABSENT_MODE], 600);
+  eeprom_update_word (&ee_t_boiler_min[ABSENT_MODE], 300);
+  eeprom_update_word (&ee_t_curve_high[ABSENT_MODE], -150);
+  eeprom_update_word (&ee_t_curve_low[ABSENT_MODE], 200);
+  eeprom_update_word (&ee_t_pump_on[ABSENT_MODE], 440);          
+    
+  put_shift_offset (0);   
+    
+  for(i = 0 ; i < MAX_LOGGING ;i++)
+    {
+      /* Fixme:  Replace using a block fucntion. */
+      eeprom_update_byte (&ee_consumption_buffer[i], 0xFF);   
+      eeprom_update_byte (&ee_temperature_buffer[i], 0xFF);
+    }
+  put_history_index (0);    
+    
+  eeprom_write_byte (&e2_init_marker, E2_INIT_MARKER_OKAY);
+}
+
+
+/* Store the oil/gas/pellets consumption in a history file.  */
+void 
+store_consumption (void)
+{
+  uint8_t idx = get_history_index ();
+
+  put_consumption (idx, ((long) burner_time * 100) / total_time);
+  put_temperature (idx, avg_temperature / 1440);
+  avg_temperature = 0;
+  burner_time = 0;
+  total_time = 0;
+  if (++idx >= MAX_LOGGING)
+    idx = 0; 
+  put_history_index (idx);
+}
+
+
+
+/*
+    Entry point
+ */
+int
+main (void)
+{
+  operation_mode = DAY_MODE;
+  actionflags.menu = 1;
+
+  /* Port A: Pins 0..7 to input. 
+     PINA.7 = KEY-1
+     PINA.6 = KEY-2
+     PINA.5 = KEY-3
+     PINA.4 = KEY-4
+     PINA.3 = KEY-5
+     PINA.2 = ADC2 = PA-2
+     PINA.1 = ADC1 = PA-1
+     PINA.0 = ADC0 = AIN-1
+  */
+  PORTA = 0x00;
+  DDRA  = 0x00;
+
+  /* Port B: Pins 0..4 to output, pins 5..7 to input.
+     PINB.7  = SCK
+     PINB.6  = MISI
+     PINB.5  = MOSI
+     PORTB.4 = RELAY_DAY
+     PORTB.3 = RELAY_PUMP
+     PORTB.2 = RELAY_BOILER
+     PORTB.1 = LED_ABSENT
+     PORTB.0 = LED_NIGHT
+  */
+  PORTB = 0x00;
+  DDRB  = 0x1f;
+
+  /* Port C: Pins 0..7 to input. 
+     PINC.7 = LCD_DATA3_PIN
+     PINC.6 = LCD_DATA2_PIN
+     PINC.5 = LCD_DATA1_PIN
+     PINC.4 = LCD_DATA0_PIN
+     PINC.3 = DIGIN1 ("PC3" on the PCB)
+     PINC.2 = LCD_EN
+     PINC.1 = LCD_RW
+     PINC.0 = LCD_RS
+  */
+  PORTC = 0x00;
+  DDRC  = 0x00;
+
+  /* Port D: Pins 0..7 to input.
+     PIND.7 = 
+     PIND.6 = 
+     PIND.5 = 
+     PIND.4 = 
+     PIND.3 = 
+     PIND.2 = 
+     PIND.1 = 
+     PIND.0 = 
+  */
+  PORTD = 0x00;
+  DDRD  = 0x00;
+
+
+  /* Init timer/counter 0 to:
+   * Clock source: system clock.
+   * Clock value: timer 0 stopped.
+   * Mode: normal top = 0xff.
+   * OC0 output: disconnected.
+   */
+  TCCR0 = 0x02;
+  TCNT0 = 0x64;
+  OCR0  = 0x00;
+
+  /* Init timer/counter 1 to:
+   * Clock source: system clock.
+   * Clock value: timer 1 stopped
+   * Mode: normal top = 0xffff
+   * OC1A output: disconnected.
+   * OC1B output: disconnected.
+   * Noise canceler: off.
+   * Input capture on falling edge.
+   */
+  TCCR1A = 0x00;
+  TCCR1B = 0x00;
+  TCNT1H = 0x00;
+  TCNT1L = 0x00;
+  OCR1AH = 0x00;
+  OCR1AL = 0x00;
+  OCR1BH = 0x00;
+  OCR1BL = 0x00;
+
+  /* Init timer/counter 2 to:
+   * Clock source: system clock.
+   * Clock value: 125.000 kHz.
+   * Mode: normal top = 0xff.
+   * OC2 output: disconnected.
+   */
+  ASSR  = 0x00;
+  TCCR2 = 0x0c;
+  TCNT2 = 0x00;
+  OCR2  = 124;     /*  1ms  */
+
+  /* Init external interrupts:
+   * INT0: off.
+   * INT1: on, falling edge.
+   * INT2: off.
+   */
+#ifdef USE_TURN_PUSH
+  GICR  |= 0x80;
+  MCUCR  = 0x08;
+  MCUCSR = 0x00;
+  GIFR   = 0x80;
+#endif
+
+  /* Init timer/counter interrupts.  */
+  TIMSK = 0x80;
+
+  /* Set the UART: 8n1, async, rc and tx on, rx and tx ints enabled.
+     Baud rate set to the value computed from BAUD.  */
+  UCSRA = 0x00;
+  UCSRB = _BV(RXCIE) | _BV(TXCIE) | _BV(RXEN) | _BV(TXEN);
+  UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0);
+  UBRRH = (UBRR_VAL >> 8);
+  UBRRL = (UBRR_VAL & 0xff);
+
+  /* Init the analog comparator:
+   * Analog comparator: off (ACSR.7 = 1)
+   * Input capture by timer/counter 1: off.
+   * Analog comparator output: off
+   */
+  ACSR  = 0x80;
+  SFIOR = 0x00;
+
+  /* Init the ADC:
+   * Enable, Single conversion.
+   * Prescaler = 64 (at 8Mhz => 125kHz).
+   */
+  ADCSRA = 0x86;
+
+  /* Prepare the LCD.  */
+  uart_puts_P ("init lcd ...\n");
+  lcd_init ();
+  uart_puts_P ("init lcd done\n");
+
+
+  /* Reset the eeprom if the enter key is pressed while booting.  */
+  if (KEY_4)
+    eeprom_write_byte (&e2_init_marker, 0xff);
+
+  /*           1234567890123456 */
+  lcd_gotoxy (0, 0);
+  lcd_puts_P ("I.Busker,H.Buss,");
+  lcd_gotoxy (0,1);
+  lcd_puts_P ("W.Koch");
+
+  delay_ms (1500);
+
+  uart_puts_P ("init eeprom...\n");
+  init_eeprom ();
+  uart_puts_P ("init eeprom done\r\n");
+
+  read_t_sensors (1); 
+
+  /* Enable interrupts.  */
+  sei ();
+
+  /* Main loop.  */
+  for (;;)
+   {
+     static unsigned char tmp_mode;      
+
+     while (!actionflags.run_main)
+       {
+         set_sleep_mode(SLEEP_MODE_IDLE);
+         cli();
+         if (!actionflags.run_main)
+           {
+             sleep_enable();
+             sei();
+             sleep_cpu();
+             sleep_disable();
+           }
+         sei();
+       }
+     actionflags.run_main = 0;
+
+     tmp_mode = get_operation_mode (current_time);
+     if (tmp_mode != DEACTIVATED_MODE) 
+       operation_mode = tmp_mode;
+           
+     run_menu ();
+
+     get_temperature (0);
+     get_controlpoint ();
+     relay_control ();
+     poll_keyboard ();              
+     
+     switch_day_relay (operation_mode == DAY_MODE);
+     switch_night_led (operation_mode == NIGHT_MODE);
+     switch_absent_led (operation_mode == ABSENT_MODE);
+     
+     if (actionflags.day) 
+       { 
+         store_consumption ();
+         actionflags.day = 0;
+       }
+            
+     if (actionflags.send_lcd)
+       {                          
+         uint8_t i;
+         
+         actionflags.send_lcd = 0;
+
+         run_menu ();
+
+         lcd_gotoxy (0, 0);
+         for (i = 0; i < 16; i++)
+           uart_putc (lcd_getc ());
+         uart_puts_P ("\n");
+         lcd_gotoxy (0, 1);
+         for (i = 0; i < 16; i++)
+           uart_putc (lcd_getc ());
+         uart_puts_P ("\n");
+       }
+            
+     if (actionflags.send_data)
+       {                          
+         unsigned char tmp_buffer;
+         uint8_t consumption;
+         
+         actionflags.send_data = 0;
+
+         tmp_buffer = get_history_index () - 1;
+         
+         actionflags.output = SERIAL;
+         while (tmp_buffer != get_history_index ())
+           {
+             if (tmp_buffer >= MAX_LOGGING) 
+               tmp_buffer = MAX_LOGGING-1;
+             if (tmp_buffer == get_history_index ())
+               break;
+             
+             consumption = get_consumption (tmp_buffer);
+             if (consumption <= 100) 
+               {
+                 uart_int (consumption, 3, 0);
+                 do_putchar(',');
+                 uart_int (get_temperature (tmp_buffer), -2 , 0);
+                 do_putchar('\r');
+                 do_putchar('\n');
+               }
+             tmp_buffer--; 
+           } 
+       }
+   }
+}
+
+
+/*
+Local Variables:
+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"
+End:
+ avrdude -c usbasp -pm32 -U flash:write:heating-control.hex
+*/
diff --git a/mkdiff b/mkdiff
index 5299b16..6d123c1 100644 (file)
--- a/mkdiff
+++ b/mkdiff
@@ -32,13 +32,13 @@ fi
 set -e
 
 curr_ver=$(ls $pack-${vprf}*.tar.${ext} 2>/dev/null | sed "s/^$pack-\(.*\)\.tar\.${ext}/\1/"\
-          | sort -r -t '.' -n +0 -1 +1 -2 +2 | head -1 )
+          | sort -r -t '.' -n -k 1,1 -k 2,2 -k 3,3 -k 4,4 | head -1 )
 if [ ! -f $pack-$curr_ver.tar.$ext ]; then
     echo "mkdiff: no current version of package $pack found" >&2
     exit 1
 fi
 prev_ver=$(ls $pack-${vprf}*.tar.${ext} 2>/dev/null | sed "s/^$pack-\(.*\)\.tar\.${ext}/\1/"\
-          | sort -r -t '.' -n +0 -1 +1 -2 +2 | head -2 | tail -1 )
+          | sort -r -t '.' -n -k 1,1 -k 2,2 -k 3,3 -k 4,4 | head -2 | tail -1 )
 if [ "$prev_ver" = "$curr_ver"  ]; then
     echo "mkdiff: no previous version of package $pack found" >&2
     exit 1
@@ -118,7 +118,7 @@ rm $tmp_name
 
 if [ $opt_sign = yes ]; then
 echo "Signing and compressing patch file"
-gpg --clearsign --not-dash-escaped -u 1CE0C630 \
+gpg2 --clearsign --not-dash-escaped -u 1CE0C630 \
     < $diff_name | bzip2 --best > $diff_name.bz2
 else
 echo "compressing patch file"
@@ -153,7 +153,7 @@ if ! diff -urP "$pack-$prev_ver/" "$pack-$curr_ver/" >/dev/null ; then
 fi
 
 if [ $opt_sign = yes ]; then
-if ! bzcat $diff_name.bz2 | gpg --batch --verify ; then
+if ! bzcat $diff_name.bz2 | gpg2 --batch --verify ; then
     exit 1
 fi
 fi
index 90eaedd..dcdec79 100644 (file)
--- a/sha1sum.c
+++ b/sha1sum.c
@@ -755,9 +755,9 @@ unescapefname (char *fname)
             case '\\': *d++ = '\\'; break;
             case 'n': *d++ = '\n'; break;
             case 'r': *d++ = '\r'; break;
-            case 'f': *d++ = '\r'; break;
-            case 'v': *d++ = '\r'; break;
-            case 'b': *d++ = '\r'; break;
+            case 'f': *d++ = '\f'; break;
+            case 'v': *d++ = '\v'; break;
+            case 'b': *d++ = '\b'; break;
             default: *d++ = '\\'; *d++ = *s; break;
             }
         }
index a1cba5b..5d63c40 100755 (executable)
@@ -14,7 +14,7 @@
 #if [ -f "$failedfile.now" ]; then
 #   rm "$failedfile.now" 2>/dev/null
 #   ( echo "List of failed hosts:"
-#     cat  "$failedfile" ) |  mail -s "keys.gnupg.net status change detected" wk@gnupg.org
+#     cat  "$failedfile" ) |  mail -s "keys.gnupg.net status change detected" keystats-gnupg-net@gnupg.org
 #fi
 #
 
@@ -36,8 +36,10 @@ function get_stats () {
    echo "retrieving $host:$port using $hostip" >&2
 
   echo "Server $hostip $port"
-  ( wget -qO - -T 3 -t 1 "http://$host:$port/pks/lookup?op=stats" || echo ) | \
-     awk -v failed="${failed_hosts_file}" -v hostip="$hostip" -v hostn="$host" '
+  # Note that versions of wget < 1.10 can't override 'Host:'.
+  (wget -qO - -T 30 -t 3 --no-cache --header "Host: $host:$port" \
+     "http://$hostip:$port/pks/lookup?op=stats" || echo) |\
+    awk -v failed="${failed_hosts_file}" -v hostip="$hostip" -v hostn="$host" '
 /<\/table>/           {in_settings = 0; in_peers = 0; in_daily = 0}
 /<h2>Settings<\/h2>/  {in_settings = 1 }
 /<h2>Gossip Peers<\/h2>/ {in_peers = 1 }
@@ -115,7 +117,15 @@ echo '</tbody>
 
 function print_footer () {
 print_footer2
-echo '</body>
+
+echo '<br><br><br><br><br><hr>
+<div align="left"><font size="-1">
+Notifications on status changes are automatically posted to the
+<a href="http://lists.gnupg.org/mailman/listinfo/keystats-gnupg-net"
+>keystats-gnupg-net</a> mailing list.  You are welcome to subscribe
+to this announce-only list.
+</font></div>
+</body>
 </html>'
 }