#include "EtherShield.h"
#include <avr/io.h>

#define BUFFER_SIZE 500
static uint8_t buf[BUFFER_SIZE+1];
#define STR_BUFFER_SIZE 22
static char strbuf[STR_BUFFER_SIZE+1];

// please modify the following two lines. mac and ip have to be unique
// in your local area network. You can not have the same numbers in
// two devices:
static uint8_t mymac[6] = {0x54,0x55,0x58,0x10,0x00,0x24}; 
static uint8_t myip[4] = {192,168,1,15};
static char baseurl[]="http://192.168.1.15/";
static uint16_t mywwwport =80; // listen port for tcp/www (max range 1-254)

EtherShield es=EtherShield();

// prepare the webpage by writing the data to the tcp send buffer
uint16_t print_webpage(uint8_t *buf);
int8_t analyse_cmd(char *str);

//register dr command r/w
#define STATUS_REG_W 0x06 //000 0011 0
#define STATUS_REG_R 0x07 //000 0011 1
#define MEASURE_TEMP 0x03 //000 0001 1
#define MEASURE_HUMI 0x05 //000 0010 1
#define RESET 0x1e        //000 1111 0

// pins dat - PD2, clk - PD3
#define SETSCK1 PORTD|=(1<<PD3)
#define SETSCK0 PORTD&=~(1<<PD3)
#define SCKOUTP DDRD|=(1<<DDD3)
//
#define SETDAT1 PORTD|=(1<<PD2)
#define SETDAT0 PORTD&=~(1<<PD2)
#define GETDATA (PIND&(1<<PIND2))
//
#define DMODEIN DDRD&=~(1<<DDD2)
#define PULLUP1 PORTD|=(1<<PIND2)
#define DMODEOU DDRD|=(1<<DDD2)

//pulswith long
#define S_PULSLONG delayMicroseconds(3) 
#define S_PULSSHORT delayMicroseconds(1)

// function declaration
char s_measure(unsigned int *p_value, unsigned char mode);

void setup(){
  
   /*initialize enc28j60*/
   es.ES_enc28j60Init(mymac);
   es.ES_enc28j60clkout(2); // change clkout from 6.25MHz to 12.5MHz
   delay(10);
        
	/* Magjack leds configuration, see enc28j60 datasheet, page 11 */
	// LEDA=greed LEDB=yellow
	//
	// 0x880 is PHLCON LEDB=on, LEDA=on
	// enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x880);
	delay(500);
	//
	// 0x990 is PHLCON LEDB=off, LEDA=off
	// enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x990);
	delay(500);
	//
	// 0x880 is PHLCON LEDB=on, LEDA=on
	// enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x880);
	delay(500);
	//
	// 0x990 is PHLCON LEDB=off, LEDA=off
	// enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x990);
	delay(500);
	//
  // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit
  // enc28j60PhyWrite(PHLCON,0b0000 0100 0111 01 10);
  es.ES_enc28j60PhyWrite(PHLCON,0x476);
	delay(100);
        
  //init the ethernet/ip layer:
  es.ES_init_ip_arp_udp_tcp(mymac,myip,80);

}

void loop(){
  uint16_t plen, dat_p;
  int8_t cmd;

  plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);

	/*plen will ne unequal to zero if there is a valid packet (without crc error) */
  if(plen!=0){
	           
    // arp is broadcast if unknown but a host may also verify the mac address by sending it to a unicast address.
    if(es.ES_eth_type_is_arp_and_my_ip(buf,plen)){
      es.ES_make_arp_answer_from_request(buf);
      return;
    }

    // check if ip packets are for us:
    if(es.ES_eth_type_is_ip_and_my_ip(buf,plen)==0){
      return;
    }
    
    if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V){
      es.ES_make_echo_reply_from_request(buf,plen);
      return;
    }
    
    // tcp port www start, compare only the lower byte
    if (buf[IP_PROTO_P]==IP_PROTO_TCP_V&&buf[TCP_DST_PORT_H_P]==0&&buf[TCP_DST_PORT_L_P]==mywwwport){
      if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V){
         es.ES_make_tcp_synack_from_syn(buf); // make_tcp_synack_from_syn does already send the syn,ack
         return;     
      }
      if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V){
        es.ES_init_len_info(buf); // init some data structures
        dat_p=es.ES_get_tcp_data_pointer();
        if (dat_p==0){ // we can possibly have no data, just ack:
          if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V){
            es.ES_make_tcp_ack_from_any(buf);
          }
          return;
        }
        if (strncmp("GET ",(char *)&(buf[dat_p]),4)!=0){
          	// head, post and other methods for possible status codes see:
            // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
            plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>200 OK</h1>"));
            goto SENDTCP;
        }
 	if (strncmp("/ ",(char *)&(buf[dat_p+4]),2)==0){
                plen=print_webpage(buf);
            goto SENDTCP;
         }
        cmd=analyse_cmd((char *)&(buf[dat_p+5]));
        if (cmd==1){
             plen=print_webpage(buf);
        }
SENDTCP:  es.ES_make_tcp_ack_from_any(buf); // send ack for http get
           es.ES_make_tcp_ack_with_data(buf,plen); // send data       
      }
    }
  }
        
}

// The returned value is stored in the global var strbuf
uint8_t find_key_val(char *str,char *key)
{
        uint8_t found=0;
        uint8_t i=0;
        char *kp;
        kp=key;
        while(*str &&  *str!=' ' && found==0){
                if (*str == *kp){
                        kp++;
                        if (*kp == '\0'){
                                str++;
                                kp=key;
                                if (*str == '='){
                                        found=1;
                                }
                        }
                }else{
                        kp=key;
                }
                str++;
        }
        if (found==1){
                // copy the value to a buffer and terminate it with '\0'
                while(*str &&  *str!=' ' && *str!='&' && i<STR_BUFFER_SIZE){
                        strbuf[i]=*str;
                        i++;
                        str++;
                }
                strbuf[i]='\0';
        }
        return(found);
}

int8_t analyse_cmd(char *str)
{
        int8_t r=-1;
     
        if (find_key_val(str,"cmd")){
                if (*strbuf < 0x3a && *strbuf > 0x2f){
                        // is a ASCII number, return it
                        r=(*strbuf-0x30);
                }
        }
        return r;
}


uint16_t print_webpage(uint8_t *buf)
{
        char temp_string[6];
        char humi_string[6];
        int i=0;
        
        uint16_t plen;
        
        getCurrent(temp_string, humi_string);
        
        plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<center><p><h1>Welcome to Arduino Ethernet Shield V1.0  </h1></p> "));
         
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<hr><br><form METHOD=get action=\""));
        plen=es.ES_fill_tcp_data(buf,plen,baseurl);
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("\">"));
 	plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<h1><font color=\"#00FF00\"> "));


        while (temp_string[i]) {
                buf[TCP_CHECKSUM_L_P+3+plen]=temp_string[i++];
                plen++;
        }

 	plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("  &#176C</font></h1><br> ") );
 	plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<h1><font color=\"#0000FF\"> "));


        i = 0;
        while (humi_string[i]) {
                buf[TCP_CHECKSUM_L_P+3+plen]=humi_string[i++];
                plen++;
        }

 	plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("  %RH</font></h1><br> ") );
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<input type=hidden name=cmd value=1>"));
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<input type=submit value=\"Get Temperature\"></form></center>"));
  
        return(plen);
}

void getCurrent(char *temp, char *humi)
{
  const float C1=-4.0;              // for 12 Bit
  const float C2=+0.0405;           // for 12 Bit
  const float C3=-0.0000028;        // for 12 Bit
  const float T1=+0.01;             // for 14 Bit @ 5V
  const float T2=+0.00008;          // for 14 Bit @ 5V
  byte error;
  unsigned int tempval_raw, humival_raw, tempval, humival;
  float t, rh, rh_true;
  
  error=s_measure(&tempval_raw, 0); //measure temperature
  
  if (error==0) {
    error=s_measure(&humival_raw, 1);
  } else {
    temp[0] = 'E';
    temp[1] = 'r';
    temp[2] = 'r';
    temp[3] = 'o';
    temp[4] = 'r';
    temp[5] = '\0';
    humi[0] = '\0';
    return;
  }
  
  tempval = (int) tempval_raw - 40*100;
  
  temp[0] = (int) tempval/1000 + '0';
  temp[1] = (int) tempval%1000/100 + '0';
  temp[2] = '.';
  temp[3] = (int) tempval%100/10 + '0';
  temp[4] = (int) tempval%10 + '0';
  temp[5] = '\0';

  t = (float) tempval_raw*0.01 - 40;
  rh = (float) humival_raw;
  rh_true = C3*rh*rh + C2*rh + C1 + (t-25)*(T1 + T2*rh);
  //        ^_____________          ^______________________
  //         Linearer Teil           Temperaturkompensation
  humival = (int) rh_true * 100;
 
  humi[0] = (int) humival/1000 + '0';
  humi[1] = (int) humival%1000/100 + '0';
  humi[2] = '.';
  humi[3] = (int) humival%100/10 + '0';
  humi[4] = (int) humival%10 + '0';
  humi[5] = '\0';
  return;

}

// writes a byte on the Sensibus and checks the acknowledge
char s_write_byte(unsigned char value)
{
        unsigned char i=0x80;
        unsigned char error=0;
        DMODEOU; 
        while(i){ //shift bit for masking
                if (i & value) {
                        SETDAT1; //masking value with i , write to SENSI-BUS
                }else{ 
                        SETDAT0;
                }
                SETSCK1; //clk for SENSI-BUS
                S_PULSLONG;
                SETSCK0;
                S_PULSSHORT;
                i=(i>>1);
        }
        DMODEIN; //release DATA-line
        PULLUP1;
        SETSCK1; //clk #9 for ack
        S_PULSLONG;
        if (GETDATA){ //check ack (DATA will be pulled down by SHT11)
                error=1;
        }
        S_PULSSHORT;
        SETSCK0;
        return(error); //error=1 in case of no acknowledge
}

// reads a byte form the Sensibus and gives an acknowledge in case of "ack=1"
unsigned char s_read_byte(unsigned char ack)
{
        unsigned char i=0x80;
        unsigned char val=0;
        DMODEIN; //release DATA-line
        PULLUP1;
        while(i){ //shift bit for masking
                SETSCK1; //clk for SENSI-BUS
                S_PULSSHORT;
                if (GETDATA){
                        val=(val | i); //read bit
                }
                SETSCK0;
                S_PULSSHORT;
                i=(i>>1);
        }
        DMODEOU; 
        if (ack){
                //in case of "ack==1" pull down DATA-Line
                SETDAT0;
        }else{
                SETDAT1;
        }
        SETSCK1; //clk #9 for ack
        S_PULSLONG;
        SETSCK0;
        S_PULSSHORT;
        DMODEIN; //release DATA-line
        PULLUP1;
        return (val);
}

// generates a sensirion specific transmission start
// This is the point where sensirion is not I2C standard conform and the
// main reason why the AVR TWI hardware support can not be used.
//       _____         ________
// DATA:      |_______|
//           ___     ___
// SCK : ___|   |___|   |______
void s_transstart(void)
{
        //Initial state
        SCKOUTP;
        SETSCK0;
        DMODEOU; 
        SETDAT1;
        //
        S_PULSSHORT;
        SETSCK1;
        S_PULSSHORT;
        SETDAT0;
        S_PULSSHORT;
        SETSCK0;
        S_PULSLONG;
        SETSCK1;
        S_PULSSHORT;
        SETDAT1;
        S_PULSSHORT;
        SETSCK0;
        S_PULSSHORT;
        //
        DMODEIN; //release DATA-line
        PULLUP1;
}

// communication reset: DATA-line=1 and at least 9 SCK cycles followed by transstart
//      _____________________________________________________         ________
// DATA:                                                     |_______|
//          _    _    _    _    _    _    _    _    _        ___    ___
// SCK : __| |__| |__| |__| |__| |__| |__| |__| |__| |______|  |___|   |______
void s_connectionreset(void)
{
        unsigned char i;
        //Initial state
        SCKOUTP;
        SETSCK0;
        DMODEOU; 
        SETDAT1;
        for(i=0;i<9;i++){ //9 SCK cycles
                SETSCK1;
                S_PULSLONG;
                SETSCK0;
                S_PULSLONG;
        }
        s_transstart(); //transmission start
}

// resets the sensor by a softreset
char s_softreset(void)
{
        s_connectionreset(); //reset communication
        //send RESET-command to sensor:
        return (s_write_byte(RESET)); //return=1 in case of no response form the sensor
}


// makes a measurement (humidity/temperature) with checksum
// p_value returns 2 bytes
// mode: 1=humidity  0=temperature
// return value: 1=write error, 2=crc error, 3=timeout
//
char s_measure(unsigned int *p_value, unsigned char mode)
{
        unsigned error;
        unsigned char i=0;
        s_transstart(); //transmission start
        *p_value=0;
        if(mode){
                mode=MEASURE_HUMI;
        }else{
                mode=MEASURE_TEMP;
        }
        if(s_write_byte(mode)){
                return(1);
        }
        // normal delays: temp i=70, humi i=20
        while(i<240){
                delay(3);
                if (GETDATA==0){
                        i=0;
                        break;
                }
                i++;
        }
        if(i){
                // or timeout 
                return(3);
        }
        i=s_read_byte(1); //read the first byte (MSB)
        *p_value=(i<<8)|s_read_byte(0); //read the second byte (LSB) and end with no-ack
        return(0);
}
