/**************************************************************************************
** sram_util.c
**
** 1.: SRAM Speicherwerte auslesen und managen
**
** 2.: Software UART für den Empfang der Messwerte mit 600 baud
**
** 19.09.2011 li Speicherbereich 0x200 bis 0x37F, Auswertung u. Speicherung ok.
** 20.09.2011 li 12 Monate zuwenig, 24 Monate besser
** 25.09.2011 li Verbrauch mit Zählerwechsel eingebaut
** 28.09.2011 li 2. Optimierung Software UART funktioniert
** 17.12.2012 li v3 Barchart, Regenmengen ab dem 16.12.2012 mit + 10000 anzeigen
** 09.02.2013 li Temperatur Werte je minute abspeichern, 2Byteje wert
** 13.02.2013 li neue Organisation der Regenwerte, nur noch Tageswerte speichern
** 14.02.2013 li sram_util.h: Namen der öffentlichen Routinen: sram_.....
** 15.02.2013 li Regenmengen Speicherung korrigiert
** 24.02.2013 li Temperatur Ausgabe unter ubuntu wegen neuer requests falsch, korr. 
**-----------------------------------------------------     Speicherbelegung
**       0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
** 0000                    |  |  |     |              Alarm
**                         CF day|     Adresse
**                               |-T-Flag
**
**
** 0010  tout  tgwh  wwas  tvor  trück
** 0020  R_hh  Rheute
** 0030  cadr  cadr  cadr
** 01F0 -- frei
**                                           |- 3 Flags für die Messwerte 0bppppmmmm
**                                           |                              |   |-monat
**                                           |                              |- Patchnummmer (Zählerwechsel)
** 0200  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Jan  gerade Jahre
** 0210  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Feb
** 0220  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Mär
** 0230  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Apr
** 0240  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Mai
** 0250  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Jun
** 0260  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Jul
** 0270  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Aug
** 0280  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Sep
** 0290  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Okt
** 02A0  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Nov
** 02B0  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Dez
** 02C0
** 02D0
** 02E0
** 02F0
** 0300  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Jan  ungerade Jahre
** 0310  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Feb
** 0320  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Mär
** 0330  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Apr
** 0340  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Mai
** 0350  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Jun
** 0360  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Jul
** 0370  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Aug
** 0380  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Sep
** 0390  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Okt
** 03A0  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Nov
** 03B0  x  x  x  x  y  y  y  y  z  z  z  z  fx fy fz jj    cur./last counter Monat Dez
** 03C0
** 03D0
** 03E0
** 03F0
** 0400  pp next free Patch, start mit 1                PATCH = Zähler Wechsel: Zählerstand alt, Zählerstand start neu
** 0410  1111111111  11111111111 2222222222  22222222222
** 0420  3333333333  33333333333 4444444444  44444444444
** 0430  5555555555  55555555555 6666666666  66666666666
** 0440  7777777777  77777777777 8888888888  88888888888
** 0450  9999999999  99999999999 aaaaaaaaaa  aaaaaaaaaaa
** 0460  bbbbbbbbbb  bbbbbbbbbbb cccccccccc  ccccccccccc
** 0470  dddddddddd  ddddddddddd eeeeeeeeee  eeeeeeeeeee
** 0480  ffffffffff  fffffffffff
** 0490  -- frei
** 3FF0  -- frei

** 4000  ww ww                                              max 366 einträge je Tag 2 Byte = 732 Bytes, zB: 2012
** 4010  ..
** 4400  ww ww                                              Regen speicher, (Jahr-2000) % 4  ==  01     zB: 2013
** 4410  ..
** 4800  ww ww                                              Regen speicher mit dem Jahresrest 02        zB: 2014
** 4810  ..
** 4C00  ww ww                                              Regen speicher mit dem Jahresrest 03        zB: 2015
** 4C10  ..
** 4FFF  -----
**
** 5000  frei
**       -- frei
** 8000  tt                                                 7.2.2013: je Minute 5 Werte * 2 Bytes = 60*5*24*2 = 14400 Bytes
** 9000  tt                                                 ergibt hex 0x3840, machen wir 0x4000 daraus
** A000  tt
** B000  tt bis B840
** B840 bis BB40 mit missing value auffüllen
** C000  tt
** D000  tt
** E000  tt
** F000  tt bis F840
** F840 bis FB40 mit missing value auffüllen
** ------------------------------------------------------------------------------------------
** var_array   0      1      2      3      4      5      6      7      8      9     10     11
**             Sonne  Somme              
**             auf    unt
** siehe httpd.h                 
**                                          
*/

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "usart.h"

#include "config.h"
#include "../abc/sram.h"
#include "../abc/basis.h"
#include "httpd.h"                  // für Variablen Array
#include "sram_util.h"
                                    // Soft UART PORT und Time
#define SBIT_TIME 208               // 16000000 Hz und 600 Baud Vorteiler 128
#define SRX_PIN  PINA
#define SRX_BIT  BIT7

#if TEST_SRAM
volatile unsigned int timer2_event;
#endif

typedef struct
{
   unsigned char ss;            // Sekunden
   unsigned char mm;            // Minute
   unsigned char hh;            // Stunde
   unsigned char day;           // Tag
   unsigned char wday;          // Wochentag
   unsigned char mon;           // Monat
   unsigned char year;          // Jahr
   unsigned char dmz;           // MEZ oder MESZ
   unsigned char message_flag;  // time message überwachung
} S_CLOCK;


S_CLOCK volatile s_clock;       // allgemeine clock struktur

unsigned char rgflag = 99;


unsigned char sram_trace=0;     // trace flag, set mit cmd TRACE

                       // 1   2   3   4   5   6   7   8   9   10  11  12
unsigned char motab[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

char text[25];


#define RBUF_SIZE 14            // wegen Counterwerte auf 14

char srx_buffer[RBUF_SIZE];
unsigned char srx_ind = 0;      // receive Index 

unsigned char volatile srx_count = 0;    // software uart read
unsigned char volatile srx_data;         // temp. Bit Speicher
unsigned char volatile srx_bits;         // interne Bit zuordnung
unsigned char volatile srx_rbyte;        // receieved byte
unsigned char volatile srx_rflag = 0;    // 1 wenn rbyte da

unsigned int compute_addr( int varnum , unsigned char mon,  unsigned char year);
void save_regen_tag( unsigned int value );
unsigned int get_day_regen_addr( unsigned char jj, unsigned char mm, unsigned char day );
void set_missing_value( unsigned int, unsigned int);
void save_uint2sram( unsigned int addr, unsigned int v );
unsigned int read_uintFsram( unsigned int addr );
long read_longFsram( unsigned int addr );
void save_long2sram( unsigned int addr, long l );
unsigned char current_temperatur_switch(void);

/*****************************************************************************
** DCF77 Time updates eingetroffen, minuten update?
** wird je Sekunde aufgerufen
** 14.2.2013 li offensichtlich ist der dcf77 Time update nicht sicher
**              damit ist temperatur Speicher falsch .....
*/
void sram_check_time( unsigned char ticks )
{
   s_clock.message_flag += ticks;
   if ( s_clock.message_flag > 60 )
   {
      s_clock.message_flag = 0;
      VA_DCF77_MISMM++;                // var array
      s_clock.mm++;
      if (s_clock.mm == 60)
      {
         s_clock.mm = 0;
         s_clock.hh++;
         if (s_clock.hh == 24)
         {
            s_clock.hh = 0;
            s_clock.day++;
            s_clock.wday++;
            if ( s_clock.wday == 8 )
               s_clock.wday = 1;
            if ( s_clock.year % 4 == 0 && s_clock.year > 0 )
              motab[1] = 29;
            else
              motab[1] = 28;
            if ( s_clock.day >motab[s_clock.mon-1] )
            {
               s_clock.day = 1;
               s_clock.mon += 1;
               if ( s_clock.mon > 12 )
               {
                  s_clock.mon = 1;
                  s_clock.year += 1;
               }
            }
         }
      }
   }
}



/*****************************************************************************
**  Laufender Tag seit dem 1.1.2013
*/
unsigned char current_temperatur_switch(void)
{
   unsigned char j,i;
   unsigned char tage=0;
                               // 1. Schritt = berechnen der Tage des aktuellen Tages
   for( j=13; j <= s_clock.year; j++ )
   {
      if ( j % 4 == 0 )        // Schaltjahr ??
         motab[1] = 29;
      else
         motab[1] = 28;

      if ( j == s_clock.year )
      {
         for ( i=0; i < (s_clock.mon-1) ; i++)
         {
            tage += motab[i];
            tage &= 0x01;
         }
         tage += s_clock.day;
      }
      else
      {
         if ( j % 4 != 0 )      // Schaltjahr ??
            tage += 1;
      }
      tage &= 0x01;
   }
   return( tage & 0x01);
}



/*****************************************************************************
**  Aufruf aus der main loop, siehe main.c
**  100 ms
*/
void sram_clear_loop( void )
{
   unsigned int i;
   unsigned int addr;
                                        // read addresse
   addr = read_uintFsram( SRAM_CURR_CLEAR_ADR );

   for( i=0; i < 7200; i++ )            // loop 4h
   {
      sram_write( addr, '=' );          // wert auf =x setzen d.h > 4000
      addr += 2;                        // das wird in der Grafik ausgeblendet
   }

   sram_write( SRAM_CLEAR_FLAG, 0 );    // reset clear flag
   addr &= 0xC000;                      // zur Sicherheit basis eintragen
   save_uint2sram( SRAM_CURR_CLEAR_ADR, addr );
}



/*****************************************************************************
**  UART Message von gmess_v2 (Heizung) dekodieren
**
**  LmmmmzzzzP   in minuten Sonnen Auf / Untergang
**  DddmmjjwP    Datum: tag monat jahr wochentag
**  ThhmmSP      Zeit: stunde minute MESZ
**  SssP         Sekunde
**  CwwwwP       Temperaturwert + 1024 Aussen
**  GwwwwP       Temperaturwert + 1024 Gewaechshaus
**  VwwwwP       Temperaturwert + 1024 Vorlauf
**  BwwwwP       Temperaturwert + 1024 Rücklauf
**  WwwwwP       Temperaturwert + 1024 Warm Wasser
**  RFnnnP       Regenmesser Ticks ( max. 999 je Stunde, dann reset)
**  <aaP         aa = Alarm Flag, P = Pruefziffer
**  XnnnnnnnnP   Gas Zählerstand ( 2 komma stellen )
**  YnnnnnnnnP   Brauchwasser Anteil ( 2 komma stellen )
**  ZnnnnnnnnP   Stromzählerstand Heizung ( 3 komma stellen )
*/
void sram_decode_message( char *txt )
{
   unsigned char p, c, x, rest;
   int regen, y, z;
   unsigned int addr=0, utemp=0;
   long lx,ly;
   char *s;
   prog_char *atab;
                            // eigener base64 key
                            //           1         2         3         4         5         6
                            // 0123456789012345678901234567890123456789012345678901234567890123
   static char key[] PROGMEM ="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz*=";

   s = text;
                        // maximal 16 Zeichen kopieren
   for (p=0; p < 16; p++)
   {
      *s = *txt;
      if ( *s == '\0' )
         break;
      s++;
      txt++;
   }

   c = text[1];
   s = text;
   s += 2;

   switch ( c )        // 2. Zeichen = Message Kennung
   {
     case 'S':        // Sekunde += 1
      p = berechne_PZ( s, 2 ) + 0x30 ;    // Pruefzeichen
      if ( p == s[2] )                    // check transfer
      {
         x = (unsigned char)a_to_i( s, 2 );
         s_clock.ss = x;
         VA_SOFT_USART_OK++;
      }
      else
         VA_SOFT_USARTERR++;
      break;

     case 'T':        // Zeit
      p = berechne_PZ( s, 5 ) + 0x30 ;                    // Pruefzeichen
      if ( p == s[5] )                                    // check transfer
      {
         s_clock.hh = (unsigned char)a_to_i( s, 2 );      // hh
         s_clock.mm = (unsigned char)a_to_i( s+2, 2 );    // mm
         s_clock.dmz = (unsigned char)a_to_i( s+4, 1 );   // dmz
         s_clock.message_flag = 0;                        // ckeck time event
         VA_SOFT_USART_OK++;
      }
      else
         VA_SOFT_USARTERR++;
      break;

     case 'D':        // Datum
      p = berechne_PZ( s, 7 ) + 0x30 ;    // Pruefzeichen
      if ( p == s[7] )                    // check transfer
      {
         s_clock.day  = (unsigned char)a_to_i( s, 2 );    // Tag
         s_clock.mon  = (unsigned char)a_to_i( s+2, 2 );  // monat
         s_clock.year = (unsigned char)a_to_i( s+4, 2 );  // jahr
         s_clock.wday = (unsigned char)a_to_i( s+6, 1 );  // wochentag

         x = sram_read( SRAM_CLEAR_DAY );         // SRAM CLEAR DAY
         if ( x != s_clock.day )                  // tageswechsel
         {
            c = current_temperatur_switch();                  // laufender Tages switch
            sram_write(SRAM_TEMP_SWITCH, c );     // change t-Flag
            if ( c )                              // Temperatur flag == 1
               save_uint2sram( SRAM_CURR_CLEAR_ADR, 0xC000 );
            else
               save_uint2sram( SRAM_CURR_CLEAR_ADR, 0x8000 );
            sram_write( SRAM_CLEAR_FLAG, 0x01 );   // set clear loop
            sram_write( SRAM_CLEAR_DAY, s_clock.day );
         }
         VA_SOFT_USART_OK++;
      }
      else
         VA_SOFT_USARTERR++;
      break;

     case 'L':                           // Solar Zeiten: use var_array 0 und 1
      p = berechne_PZ( s, 8 ) + 0x30 ;   // Pruefzeichen
      if ( p == s[8] )                   // check transfer
      {
         y = a_to_i( s, 4 );             // Zeit in Minuten Auf oder Untergang
         VA_SONNEN_AUF =  y ;
         y = a_to_i( s+4, 4 );           // Zeit in Minuten Auf oder Untergang
         VA_SONNEN_UNT =  y ;
         VA_SOFT_USART_OK++;
      }
      else
         VA_SOFT_USARTERR++;
      break;

                      // #define A_TEG    BIT0        // .... ...1 Tuer Terasse auf
                      // #define A_TWA    BIT1        // .... ..1. Tuer Waschkueche auf
                      // #define A_TKE    BIT2        // .... .1.. Keller Tuer zu
                      // #define A_BWM    BIT3        // .... 1... Bewegungsmelder
                      // #define A_FEG    BIT4        // ...1 .... Fenster EG auf
                      // #define A_REG    BIT5        // ..1. .... RegenM. tot
                      // #define A_BTOT   BIT6        // .1.. .... BASIS TOT

     case '<':        // Alarm
      p = berechne_PZ( s, 2 ) + 0x30 ;    // Pruefzeichen
      if ( p == s[2] )                    // check transfer
      {
         x = (unsigned char)a_to_i( s, 2 );
         sram_write( SRAM_ALARM_ADR , x );
         VA_SOFT_USART_OK++;
      }
      else
         VA_SOFT_USARTERR++;
      break;

     case 'C':        // Celsius Aussen Temperatur +1024
     case 'G':        // Celsius Gewächshaus +1024
     case 'V':        // Celsius Vorlauf + 1024
     case 'B':        // Celsius Rücklauf + 1024
     case 'W':        // Celsius WarmWasser + 1024
      p = berechne_PZ( s, 4 ) + 0x30 ;    // Pruefzeichen
      if ( p == s[4] )                    // check transfer
      {
         VA_SOFT_USART_OK++;
         if ( c == 'C' )
            addr = 16;             // 0x0010
         else if ( c == 'G' )
            addr = 18;             // 0x0012
         else if ( c == 'V' )
            addr = 22;             // 0x0016
         else if ( c == 'B' )
            addr = 24;             // 0x0018
         else if ( c == 'W' )
            addr = 20;             // 0x0014
         y = a_to_i( s, 4 );             // Temperaturwert
         save_uint2sram( addr, y );
                      // ** 07.2.2013 * Temperaturen speichern geändert
                      // 60 * 24 = 1440 Werte (je Minute ein Wert)
                      // Speicherung als base64 Zahl
                      // 1440 * 2 = 2880 Bytes
                      //
         p = sram_read(SRAM_TEMP_SWITCH); // Temperatur flag adresse
         if ( p & 0x01 )        // Temperatur flag == 1
            addr = 0xC000;
         else
            addr = 0x8000;
         //if ( c == 'V' )                // Vorlauf    f1
         //    +0
         if ( c == 'B' )                  // Rücklauf   f2
            addr += 2880;                 // 0x0B4ß
         else if ( c == 'C' )             // Aussen     f3
            addr += (2880*2);             // 0x1680
         else if ( c == 'W' )             // Warmwasser f4
            addr += (2880*3);             // 0x21c0
         else if ( c == 'G' )             // GwH        f5
            addr += (2880*4);             // 0x2d00

         utemp = s_clock.hh;
         utemp *= 120;                    // 60 * 2 = 120 Byte je Stunde
         utemp += s_clock.mm * 2;         // 2 Bytes je Minute 0-59
         addr  += utemp;

         if ( sram_trace & SRAM_TRACE_MESS2 )        // 8.2.2013
         {
            usart_write("te%c %4x\n",c,addr);
            usart_write("time %i:%i %i\n",s_clock.hh,s_clock.mm,p);
         }
         atab = (prog_char*)&key;
         x = (unsigned char)(y >> 6) & 0x3F;  // /= 64
         x = pgm_read_byte(atab+x);
         sram_write( addr++, x );
         x = (unsigned char)(y & 0x003F);     // rest  64
         x = pgm_read_byte(atab+x);
         sram_write( addr, x );
      }
      else
         VA_SOFT_USARTERR++;
      break;

          // 13.2.2013 neue Organisation der Werte
          // Stundenwechsel: regenwert ist für die alte Stunde zu addieren
     case 'R':                                 // Regenmesser 20.03.2011
      p = berechne_PZ( s, 5 ) + 0x30 ;         // Pruefzeichen
      if ( p == s[5] )                         // check transfer
      {
         regen = a_to_i( s, 5 );               // regenmenge der letzten Stunde
         rest  = (unsigned char)(regen >> 12);
         regen &= 0x0FFF;                      // Filter die menge

         save_uint2sram( 0x0020, regen);       // Save stunden wert

         if ( rgflag != rest )                 // Stunden wechsel ?
         {
            rgflag = rest ;                    // merke neue Stunde

            z = read_uintFsram( 0x0022 );      // read heute
            save_uint2sram( 0x0022, regen+z);  // save heute

            if ( rgflag == 0 )                 // Tageswechsel ?
            {
               z = read_uintFsram( 0x0022 );   // read heute
               save_regen_tag( z );            // über 4 Jahre sichern = gestern
               save_uint2sram( 0x0022, 0);     // regen heute = 0
            }
            save_uint2sram( 0x0020, 0);        // clear stunden wert
         }
         VA_SOFT_USART_OK++;
      }
      else
         VA_SOFT_USARTERR++;
      break;

                                      // Wrap around 12 Monate --------------------
     case 'X':                        // Gas Zählerstand 8.9.2011
     case 'Y':                        // Gas Brauchwasser 8.9.2011
     case 'Z':                        // Strom Zählerstand 8.9.2011
       if ( s_clock.dmz && s_clock.mon )  //  zeit da ???
       {
          p = berechne_PZ( s, 8 ) + 0x30 ;  // Pruefzeichen
          if ( p == s[8] )            // check transfer
          {
             VA_SOFT_USART_OK++;
             addr = compute_addr( 0 , s_clock.mon, s_clock.year );

             if ( sram_trace & SRAM_TRACE_MESS2 )
                usart_write("Wxyz %x\r\n",  addr);

             if ( c == 'X' )
             {
                z = 0;                 // offset
                rest = 0;              // offset zu flag
                utemp = 10;            // korrektur faktor
             }
             else if ( c == 'Y' )
             {
                z = 4;                 // offset
                rest=1;                // offset zu flag
                utemp = 10;            // korrektur faktor
             }
             else if ( c == 'Z' )
             {
                z = 8;                 // offset
                rest= 2;               // offset zu flag
                utemp = 1;             // korrektur faktor
             }

             lx = a_to_i( s, 8 );      // long
             lx *= utemp;              // korrektur auf 3 stellen nach dem komma

             ly = read_longFsram( addr + z );    // read last value
             save_long2sram( addr + z, lx );

             if ( lx < ly )                      // 24.09.2011 Zählerwechsel ??????
             {
                p = sram_read( 0x400 );          // freie Patch area holen
                c = (p+1) & 0x0F;
                if ( c == 0 )
                  c =1;
                sram_write( 0x400, c );          // update

                c = p;                           // patchnummer sichern
                c <<= 4;
                c += s_clock.mon;                // mon von 1-12
                sram_write( addr+rest+12, c );
                sram_write( addr+15, s_clock.year );

                addr = p & 0x0F;                 // patch adresse ermitteln
                addr *= 8;                       // jeweils 8 Byte
                addr += 0x408 ;                  // beginn der area
                                                 // Ende Wert sichern
                save_long2sram( addr, ly );
                                                 // Start Wert sichern
                save_long2sram( addr+4, lx );
             }
             else
             {
                c = sram_read(  addr+15 );       // lese Jahres Merker
                sram_write( addr+15, s_clock.year );
                p = sram_read( addr+rest+12 );   // patch flag lesen

                if ( (c+1) < s_clock.year )      // 2 Jahre alter Eintrag ??
                   p = 0;
                else
                   p &= 0xF0;

                p += s_clock.mon;                // mon von 1-12
                sram_write( addr+rest+12, p );
                sram_write( addr+15, s_clock.year );
             }
          }
          else
             VA_SOFT_USARTERR++;
       }
       break;

     default:
      VA_SOFT_USARTERR++;
      break;
   }
}


/**************************************************************
** save long to sram
*/
void save_long2sram( unsigned int addr, long l )
{
   unsigned char x;

   x = (unsigned char)(l >> 24) & 0xFF;
   sram_write( addr++, x );
   x = (unsigned char)(l >> 16) & 0xFF;
   sram_write( addr++, x );
   x = (unsigned char)(l >> 8) & 0xFF;
   sram_write( addr++, x );
   x = (unsigned char)(l & 0xFF);
   sram_write( addr, x );
}


/**************************************************************
** read long int from sram
*/
long read_longFsram( unsigned int addr )
{
   long l;
   l = sram_read( addr++ );
   l <<= 8;
   l += sram_read( addr++ );
   l <<= 8;
   l += sram_read( addr++ );
   l <<= 8;
   l += sram_read( addr );
   return l;
}

/**************************************************************
** read uint from sram
*/
unsigned int read_uintFsram( unsigned int addr )
{
   unsigned int y;

   y = sram_read( addr );
   y <<= 8;
   y += sram_read( addr+1 );
   return y;
}


/**************************************************************
** save uint to sram
*/
void save_uint2sram( unsigned int addr, unsigned int v )
{
   unsigned char x;

   x = (unsigned char)( v >> 8 ) & 0xFF;
   sram_write( addr, x );
   x = (unsigned char)( v & 0xFF );
   sram_write( addr+1, x );
}


/**************************************************************
** Speichern Tageswert
** 13.02.2013 li neue Organisation des Speichers
*/
void save_regen_tag( unsigned int value )
{
   unsigned char tag,mon,jahr;
   unsigned int addr;


   sram_get_day( text, 1);        // tag von gestern ermitteln

   tag =  a_to_i( text, 2);       // Tag x
   mon =  a_to_i( text+3, 2);     // monat
   jahr = a_to_i( text+8, 2);     // jahr


   if ( s_clock.year && s_clock.dmz )    // Zeit da ??
   {
      addr = get_day_regen_addr(jahr, mon, tag);       // adresse ermitteln
   }
   else
      return;

   if ( sram_trace & SRAM_TRACE_MESS4 )        // 13.2.2013
   {
      usart_write("regen day#:0x%x\n",addr);
   }

   save_uint2sram( addr, value );
}



/**************************************************************
** adresse regen tag
*/
unsigned int get_day_regen_addr( unsigned char jj, unsigned char mm, unsigned char day )
{
   unsigned char i;
   unsigned int addr = 0, jahr;

   if ( jj % 4 == 0 )       // Schaltjahr ??
      motab[1] = 29;
   else
      motab[1] = 28;

   for ( i=0; i < (mm -1); i++)
      addr += motab[i];

   addr += day;

   addr *= 2;

   jahr = jj & 0x03 ;     // speicher Adresse Start ermitteln
   jahr *= 0x0400;

   addr += jahr;
   addr += 0x4000;                  // beginn bei 0x4000

   return addr;
}



/**************************************************************
** Test auf die Alarme
** a = 80: Bit0
** a = 81: BIT1 usw.
** returnwert = 1 = da
*/
unsigned char sram_get_alarm( unsigned char a )
{
   unsigned char av;

   a -= 80;
   av = sram_read( SRAM_ALARM_ADR );
   return( av & (1<<a));
}



/**************************************************************
** Aufbereitung der Varirable VD@00
** Ergebnis = 01.10.2011
** returnwert = string länge = 10
*/
unsigned char sram_get_day( char *pziel, unsigned char b )
{
   unsigned char i=0,j;
   int tage=0;
   char text[12];

   j = s_clock.year ;
   if ( s_clock.year && s_clock.dmz )    // Zeit da ??
   {
      // 1. Schritt = berechnen der Tage des aktuellen Tages

      if ( j % 4 == 0 )        // Schaltjahr ??
         motab[1] = 29;
      else
         motab[1] = 28;

      for ( i=0; i < (s_clock.mon -1) ; i++)
         tage += motab[i];
      tage += s_clock.day;

      if ( b >= tage )
      {
         j--;                  // jahr -1
         if ( j % 4 == 0 )     // Schaltjahr ??
         {
            motab[1] = 29;
            tage += 366;
         }
         else
         {
            motab[1] = 28;
            tage += 365;
         }
      }
      tage -= b;
      for ( i = 0; tage > motab[i]; i++ )
         tage -= motab[i];
   }

   edit99( tage, text, 2 );      // Tag
   edit99( i+1, text+3, 2 );     // Monat
   edit99( j+2000, text+6, 4 );  // Jahr aufbereiten
   text[2] = '.';
   text[5] = '.';
   text[10] = '\0';     // ende
   strcpy( pziel, text);
   return 10;
}



/**************************************************************
** Aufbereitung der Varirable VM@00
** Ergebnis = Sep. 2011
** returnwert = string länge
*/
unsigned char sram_get_monat( char *pziel, unsigned char b )
{
   unsigned char i;
   unsigned int j;
   prog_char *mtab;
   static char montab[] PROGMEM = "JanFebMärAprMaiJunJulAugSepOktNovDez";

   j = s_clock.year;
   i = s_clock.mon;
   if ( b >= i )
   {
      i += 12;
      j -= 1;
   }
   if ( b >= i )
   {
      i += 12;
      j -= 1;
   }
   i -= b;
   i -= 1;  // start bei 0
   i *= 3;  // compute table pos
   mtab = (prog_char*)&montab;
   text[0] = pgm_read_byte(mtab+i++);
   text[1] = pgm_read_byte(mtab+i++);
   text[2] = pgm_read_byte(mtab+i);
   text[3] = '.';
   text[4] = ' ';
   edit99( j+2000, text+5, 4 );  // Jahr aufbereiten
   text[9] = '\0';     // ende
   strcpy( pziel, text);
   return strlen( pziel );
}



/**************************************************************
** Aufbereitung der Varirable varnum nach todo spezifikation
** todo = T  Temperatur wert ohne komma wie gespeichert
** todo = R  Regenmenge wert ohne komma
** todo = C  Counter Ausgaben
** pziel = Ergebnis
** returnwert = string länge
** 17.9.2011 li Applet geht, Temperaturen so wie gespeichert ausgeben
** 25.9.2011 li Verbrauch mit Zählerwechsel eingebaut
** 25.2.2013 li out = value des out form parameters
*/
unsigned char sram_getsram( char todo, char *pziel, int varnum, unsigned int out  )
{
   long x,y,z;
   int regen_korr = 10000 ;
   char *s;
   unsigned char n,p,tag,mon,jahr,flg,sw=1;
   unsigned int rr,r,addr;
                                // 0-15 = Zeit
                                // ab 16 die current daten
   s = &text[0];

   switch ( todo )
   {
     case 'T':                //************************** Temperatur Werte
      if ( varnum < 100 )
      {
         x = read_uintFsram( (varnum*2)+16 );
         edit99( x, text, 5 );
         text[5] = '\0';
         for ( r =0; r < 4; r++ )   // führende nullen durch blank ersetzenn
         {
            if ( *s == '0' )
            {
               *s = ' ';
               s++;
            }
            else
               break;
         }
      }
      else
      {
         // t100 - 123 = f1 area
         // t132 - 155 = f2
         // t164 - 186 = f3
         // t196 - 219 = f4
         // t228 - 251 = f5

         p   = sram_read(SRAM_TEMP_SWITCH ) & 0x01 ;  // T-Flag lesen, wenn 1 dann current ab 0xc000

         varnum -= 100;                // nackte zahl von 0 bis
         flg = out & 0x0F;             // OUT Parameter                  
         rr = varnum & 0x001f;         // 0-31
         r  = varnum & 0x00e0;         // f1 bis f5 auswahl, 0,32,64,96,128
         r *= 90;                      // 90*32 = 2880  = werte f1 bis f5

         addr = 0x8000;                // Basis Adresse heute wenn p=0

         switch(flg)                   // auswahl parameter OUT  0-9
         {
                          // -------- 12 werte im Abstand von 10 Bytes
           case 0:        // gestern
            p ^= 1;       // exor p, wenn p=0 wird p=1, wenn p=1 wird p=0
           case 1:        // heute
            rr *= 120;
            break;
                          // 12 Werte im Abstand von 2 Bytes
           case 2:        // gestern
            p ^= 1;       // exor p, wenn p=0 wird p=1, wenn p=1 wird p=0
           case 6:        // heute
            sw=0; rr *= 24;
            break;        // ab  0:00

           case 3:         // gestern
            p ^= 1;        // exor p, wenn p=0 wird p=1, wenn p=1 wird p=0
           case 7:         // heute
            sw=0; rr *= 24; rr += 720;  break;  // ab  6:00

           case 4:         // gestern
            p ^= 1;        // exor p, wenn p=0 wird p=1, wenn p=1 wird p=0
           case 8:         // heute
            sw=0; rr *= 24; rr += 1440; break;  // ab 12:00

           case 5:         // gestern
            p ^= 1;        // exor p, wenn p=0 wird p=1, wenn p=1 wird p=0
           case 9:         // heute
            sw=0; rr *= 24; rr += 2160; break;  // ab 18:00
         }
         addr += ( r + rr );

         if ( p )
            addr += 0x4000; // p=1 und heute dann +4000 oder wenn p=0 und gestern

         if ( sram_trace & SRAM_TRACE_MESS0 )
            usart_write("VT%3i => 0x%4x\r\n", varnum+100, addr);

         n=0;
         for( p=0; p < 12; p++)
         {
            text[n++] = sram_read(addr++);
            text[n++] = sram_read(addr++);
            if( sw )
              addr += 8;                 // jeden 5 Wert
         }
         text[24] = '\0';
      }
      break;

     case 'R':                //************************** Regen Werte
      x = 0;
      varnum += out;          // 25.2.2013 output parameter 
      if ( varnum & 0x0100 )            // Regen werte je Tag ??  256 bis 511
      {
         r = varnum & 0x00FF;           // 0-255 zulassen

         sram_get_day( text, r);        // r = anzahl der Tage minus current datum

         tag =  a_to_i( text, 2);       // Tag x
         mon =  a_to_i( text+3, 2);     // monat
         jahr = a_to_i( text+8, 2);     // jahr

         if ( jahr == 12 )              // ab dem 16.12.2012 Regen Korr 10000
         {                              // wegen Regenmesser mit anderer Kalibrierung
            if ( mon < 12 )
               regen_korr = 0;
            else
            {
               if ( tag < 16 )
                  regen_korr = 0;
            }
         }
         addr = get_day_regen_addr( jahr, mon, tag );  // gleiche Routine wie Speichern

         if ( sram_trace & SRAM_TRACE_MESS4 )
            usart_write("Rxt 0x%x => 0x%x\r\n", varnum, addr);

         x = read_uintFsram( addr );         // 2 Byte Regen menge

         if ( sram_trace & SRAM_TRACE_MESS4 )
             usart_write("Regen %i\r\n", x);
      }
      else                                   // nuer für Stunde und heute gültig
      {
         if ( varnum == 0 || varnum == 1 )   // current Regen Mengen ??
            x = read_uintFsram( (varnum*2)+0x0020 );    // 0x0020   oder 0x0022
      }
      x += regen_korr;        // ab dem 16.12.2012 10000 dazu wegen applet
      edit99( x, text, 5 );
      text[5] = '\0';
      for ( r =0; r < 4; r++ )   // führende nullen durch blank ersetzenn
      {
         if ( *s == '0' )
         {

            *s = ' ';
            s++;
         }
         else
            break;
      }
      break;
                              //************************** Counter Werte
                              // 0 = Gas 1=GasB 2=Strom 3=not used
                              // .... .... .dmm mmcc
                              //            ||     |- Counter 0-3
                              //            ||------- Monate minus
                              //            |-------- Differenz Bilden
     case 'C':                //**************************
      r = compute_addr( varnum, s_clock.mon, s_clock.year );
      if ( sram_trace & SRAM_TRACE_MESS0 )
         usart_write("Cxc %i => %x\r\n", varnum, r);

      rr = r & 0xFFF0 ;        // compute zeilen Adresse
      p = (unsigned char)(varnum & 0x0003);
      flg = sram_read( rr+12+p);

      if ((flg & 0x0F) == 0 )    // Monat vorhanden im flag ??
      {
         x = 0;
      }
      else
      {
         x =  read_longFsram( r ); // read counter Monat

         if ( varnum & 0x0080 )   // 23.9.2011 Verbrauch ??
         {
            z = 0;
            if ( (flg & 0xF0 ) )  // patch vorhanden ??
            {
               addr = flg & 0xF0;               // patch adresse ermitteln
               addr >>= 1;                      //
               addr += 0x408;                   // beginn der area
               z = read_longFsram( addr+4 );    // read Start counter Wechsel

               x -= z;                          // Differenz = Verbrauch neuer Zähler
               z = x;                           // merken
               x = read_longFsram( addr );      // read Ende alter Zähler

            }
            mon = s_clock.mon -1;
            jahr = s_clock.year;
            if ( mon == 0 )
            {
               mon = 12;
               jahr -= 1;
            }
            r = compute_addr( varnum, mon, jahr );  // Vormonat Adresse
            rr = r & 0xFFF0 ;        // compute zeilen Adresse
            p = (unsigned char)(varnum & 0x0003);
            flg = sram_read( rr+12+p);
            if ((flg & 0x0F) == 0 )    // Monat vorhanden im flag ??
            {
               x = 0;
            }
            else
            {
               if ( sram_trace & SRAM_TRACE_MESS0)
                  usart_write("Cxv %i => %x\r\n", varnum, r);

               y = read_longFsram( r );  // read last counter Vormonat
               x -= y;                   // Differenz = Verbrauch
               x += z;
            }
         }
      }
      edit99( x, text, 8 );
      text[8] = text[7];
      text[7] = text[6];
      text[6] = text[5];
      text[5] = ',';
      text[9] = '\0';
      for ( r =0; r < 4; r++ )   // führende nullen löschen
      {
         if ( *s == '0' )
            s++;
         else
            break;
      }
      break;
   }
   strcpy( pziel, s);
   return strlen( pziel );
}



/**************************************************************
** SRAM Adresse für den counter berechnen
** .... .... dmmm mmcc
**           ||     |- Counter 0-3
**           ||------- Monate minus ( maximal 23, deswegen 5 Bits )
**           |-------- Differenz Bilden
*/
unsigned int compute_addr( int varnum , unsigned char mon,  unsigned char year )
{
   unsigned int adr, r;

   r = varnum & 0x007C;              // Monat Filter
   r >>= 2;                          // anzahl monate zurück
   adr = mon;                        // aktueller Monat
   if ( r >= adr )
   {
      adr += 12;
      year -= 1;
   }
   if ( r >= adr )
   {
      adr += 12;
      year -= 1;
   }
   adr -= r;
   adr -= 1;
   adr *= 16;
   if ( year & 0x01 )
      adr += 0x300;
   else
      adr += 0x200;
   r = (varnum & 0x0003);
   r *= 4;                            // counter liegen je 4 Byte auseinander
   adr += r;
   return adr;
}



/**************************************************************
** Aufbereitung der Zeit
**                    0123456789012345678901
** pziel = Ergebnis   Fr 02.09.11 10:03 MESZ
** returnwert = string länge
*/
unsigned char sram_get_datetime( char *pziel )
{
   unsigned char n;

   for( n = 0; n < 22; n++)      // clear
      text[n] = ' ';

   text[0] = 'D';
   text[1] = 'o';
   if ( s_clock.wday == 1 )
      text[0] = 'M';             // Mon
   else if ( s_clock.wday == 7 )
      text[0] = 'S';             // Son
   else if ( s_clock.wday == 2 )
   {
      text[1] = 'i';             // Die
   }
   else if ( s_clock.wday == 3 )
   {
      text[0] = 'M';
      text[1] = 'i';
   }
   else if ( s_clock.wday == 5 )
   {
      text[0] = 'F';
      text[1] = 'r';
   }
   else if ( s_clock.wday == 6 )
   {
      text[0] = 'S';
      text[1] = 'a';
   }
   edit99( s_clock.day, text+3, 2 );
   text[5] = '.';
   edit99( s_clock.mon, text+6, 2 );
   text[8] = '.';
   edit99( s_clock.year, text+9, 2 );

   edit99( s_clock.hh, text+12, 2 );
   text[14] = ':';
   edit99( s_clock.mm, text+15, 2 );

   text[18] = 'M';
   text[19] = 'E';
   if ( s_clock.dmz == 0x01 )                // .... ..01
   {
      text[20] = 'S';
      text[21] = 'Z';
   }
   else if ( s_clock.dmz == 0x02 )           // .... ..10
   {
      text[20] = 'Z';
   }
   else
   {
      text[20] = '?';
      text[21] = '?';
   }
   text[22] = '\0';
   strcpy( pziel, text);

   return strlen( pziel );
}



/**************************************************************
** Sonnen auf oder Untergang
** oder Tageslänge
** 012345678901234567
** tpy=0:  SA hh:mm SU hh:mm
** tpy=1:  hh:mm
*/
unsigned char sram_get_sonnen( char *pziel, unsigned char typ )
{
   unsigned int sz1, sz2, duration=0;

   sz1 = VA_SONNEN_AUF;
   sz2 = VA_SONNEN_UNT;

   if ( typ == 0)    // Sonnen Aufgangs- und Untergangszeit
   {
      text[0] = 'S';
      text[1] = 'A';
      text[2] = ' ';
      text[8] = ' ';
      text[11] = ' ';
      text[5] = ':';
      text[9] = 'S';
      text[10] = 'U';
      text[14] = ':';
      edit99( sz1 / 60, text+3, 2 );
      edit99( sz1 % 60, text+6, 2 );
      edit99( sz2 / 60, text+12, 2 );
      edit99( sz2 % 60, text+15, 2 );
      if ( sz1 > sz2 )
      {
         text[1] = 'U';
         text[10] = 'A';
      }
      text[17] = '\0';
   }
   else
   {
      if ( sz1 > sz2 )
         duration = sz1-sz2;
      else
         duration = sz2-sz1;
      text[2] = ':';
      edit99( duration / 60, text, 2 );
      edit99( duration % 60, text+3, 2 );
      text[5] = '\0';
   }

   strcpy( pziel, text);
   return strlen( pziel );
}



/*****************************************************************************
**  software uart read, init      300 baud
**  T2 Timer als Zeitmesser
**  PIN Change Interrupt fuer das BIT PB5 = Input Soft UART
**  TIMER2_COMPA für jeds Bit in der Mitte
**  30.09.2011 li SRAM wird nicht mehr gelöscht, bleibt als Speicher erhalten
**  09.02.2013 li missing value fuer tote bereiche eintragen. Grund: Da für
**  die 24h und 6h Zeitauswahl die gleiche webseite benutzt wird, sind in der 24h
**  Auswahl jeweils 72 werte nicht nötig. Die javascript routine wertet für die
**  2hgrafik nur 288 werte aus. Aber die Inputroutine kann ins schleudern kommen
**  wenn ungültige Zeichen vorhanden sind.
*/
void sram_soft_uart_read_init( void )
{
   s_clock.dmz = 0;   // clear
   s_clock.mon = 0;   // clear
                                     // Ueberlaufbereiche Temperatur mit missing value belege
   set_missing_value( 0xB840, 768 );
   set_missing_value( 0xF840, 768 );
                                     // aktuelle Regenwerte aus dem SRAM

                                     // Soft UART init, 600 baud, 8N1, 18,3 msek je Byte
                                     // Interput routinen hier, abfrage auf ein neues Byte in main.c
#ifdef __AVR_ATmega644__
   PCICR |= (1<<PCIE0 );             // PCINT0 erlauben
   PCMSK0 |= BIT7;                   // PORT PA7

   srx_count = 0;                    //
   TCNT2  = 0;
   TCCR2B |= (1 << FOC2A )+ (1<< CS22)+ (1<< CS20);   // Force OutputA prescaler 128
   OCR2A  = SBIT_TIME;
   TCCR2A |= (1<< WGM21);            // CTC
   TIMSK2 = (1 << OCIE2A );          // enable compare match interrupt
#endif

#ifdef __AVR_ATmega168__

   PCICR |= (1<<PCIE0 );             // PCINT0 erlauben
   PCMSK0 |= BIT5;                   // PORT PB5
   srx_count = 0;                    //
   DDRD |= BIT6+BIT7;                // TRACE PIN CHANGE int
   srx_count = 0;                    //

   TCNT2  = 0;
   TCCR2B |= (1 << FOC2A )+ (1<< CS22)+ (1<< CS20);   // Force OutputA prescaler 128
   OCR2A  = SBIT_TIME;
   TCCR2A |= (1<< WGM21);           // CTC
   TIMSK2 = (1 << OCIE2A );         // enable compare match interrupt
 #endif

#if TEST_SRAM
   timer2_event=0;
#endif
}



/*****************************************************************************
** missing value kann ein base64 (eigener key!) wert >4000 sein.
** zur besseren Unterscheidung wird hier =M eingetragen
*/
void set_missing_value( unsigned int von, unsigned int anz )
{
   for ( unsigned int i=0; i < anz; i+=2 )
   {
      sram_write( von, '=');
      sram_write( von+1,'M');
      von += 2;
   }
}



/*****************************************************************************
**  Software UART
**  Interrupt in der Mitte jedes Bits
**  wird beim StartBit synchronisiert
**  25.09.2011 weitere optimierung
*/
ISR ( TIMER2_COMPA_vect )
{
   //PORTD ^= BIT7;
   if ( srx_count )                      // Pin change erkannt ??
   {
      if ( srx_count == 10 )             // 9 Bits + Start Counter 1 = 10
      {
         srx_rbyte = srx_data;           // info sichern
         srx_rflag = 1;                  // byte ready melden       !!!!!!!
         srx_count = 0;                  // warte auf Pin Change Int
      }
      else
      {
         if ( srx_count > 1 )            // Start bit vorbei ?
         {
            if ( SRX_PIN & SRX_BIT )     // RXD Signal = 1 ??
            {
               srx_data |= srx_bits;     // 1 dazu
            }
            srx_bits <<= 1;              // shift Bit
         }
         srx_count++;
      }
   }
#if TEST_SRAM
   timer2_event++;
#endif
}



/*****************************************************************************
**  software uart RXD read, PIN CHANGE INT
*/
ISR ( PCINT0_vect )
{
   if ( srx_count == 0 )           // 0 = warten auf Signal
   {
      if ( (SRX_PIN & SRX_BIT ) == 0 )    // RXD auf 0 (Start Bit) ??
      {
         //PORTD |= BIT6;
         TCNT2 = SBIT_TIME / 2;    // sync auf RXD Signal Mitte
         srx_count = 1;            // start auswertung
         srx_data = 0;             // alle Bits auf 0
         srx_bits = 1;             // Beginn mit Bit0
      }
   }
   //PORTD &= ~BIT6;
}
