Ethernet IO-web server

From Pinguino
Jump to: navigation, search

Example for functions Ethernet.init Ethernet.read Ethernet.write Ethernet.requestAnalysis


/*********************************************
Author:    Andre Gentric

Date: Jul 5 2014

Description:

Application to demonstrate the capabilities of ENC28J60/ETHERNET lib.
It is based on the http://tuxgraphics.org/electronics DIY projects.
Thanks to Guido Socher.

This requests a enc28j60 module, easily found on the online market.
Tested with a 47j53-A board.

The 47j53-A communicates with the enc28j60 module thru the SPI/MSSP module.
The enc28j60 is powered from an external 3.3V and is linked to a PC thru a 
Dlink router, connected itself to a DSL modem.
Therefore we can also communicate with web servers, if necessary, by programming  
the 47j53-A as a client.

We use a DS18B20 as temperature sensor powered with external 3.3V and a MPX4115AP 
as pressure sensor powered with external 5V.

It is important to get 3.3 and 5V from an external source in order to keep a 
fixed and stable voltage to ensure good analog measurements (ref. MPX4115AP).  
Effectively taking 3.3V from the Pinguino board we notice the voltage on the  
18f47j53 chip decreases from 3.21V to 3.06V.

On the PC the web browser (chrome or chromium in our case) is listening at 
192.168.0.10 according our enc28j60 programming.
As we want to prevent our i/o data to be modified without authorization we have 
introduced a password. 

So to start the i/o data displaying we have to enter as:

URL on your browser : http://192.168.0.10/ 

then we enter your password as it is in the program, here secret that we can change. 

This is to modify according your programmed choices.

In this demo we propose :
- 4 digital outputs RD0 to RD3. On each of these pins you can connect either LED,
 either transistor / relay 
- 2 digital inputs RD4 and RD5 to enter, for instance, button controls
- 2 analog inputs RA0 and RA1 - voltage limited to 3.3V

For the DS18B20 use refer to the Pinguino examples and 1wire library. Here the 
output is connected to pin #6.

The MPX4115AP output divided thru a voltage divider circuit(22K & 33K resistors) 
is connected to pin #8 (above RA0)

Notice when we press Home, Get Temperature or Get Pressure button led on pin#24
switches on during 500 ms.

*********************************************/

#include <stdlib.h>

#define SPIINIT

// 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] = {0x47,0x54,0x43,0x10,0x20,0x30}; 
static uint8_t myip[4]  = {192,168,0,10};

static char baseurl[]   = "http://192.168.0.10/"; // you can select something else
static uint16_t mywwwport = 80; // listen port for tcp/www (max range 1-254)

#define BUFFER_SIZE 800
static uint8_t buf[BUFFER_SIZE+1];
static volatile uint8_t stepcounter=0;

// global string buffer
#define STR_BUFFER_SIZE 24
static char strbuf[STR_BUFFER_SIZE+1];
static uint16_t gPlen;

// name of this switch (title on the main webpage)
static char label[30]="Welcome to the IO-web server"; // must not be longer than 20 char

// password (only a-z,0-9,_ characters): put your own pwd
static char password[9]="secret"; // must not be longer than 9 char

// Max amount of switches (0..3) to display on the webpage:
#define MAX_RELAY 4

// the positions on PORTD where the leds/relays are connected:
// based upon use of 47j53-A Pinguino board:
static uint8_t relay2port[MAX_RELAY] = {24,25,26,27}; // RD0, RD1, RD2, RD3
#define MAX_D_INPUT 2
static uint8_t d_input2port[MAX_D_INPUT] = {28,29};

// prepare the webpage by writing the data to the tcp send buffer
uint16_t http200ok(void);
uint16_t print_webpage_entry(uint8_t *buf);
uint16_t print_webpage(uint8_t *buf);
uint16_t print_webpage_io(void);
int8_t analyse_get_url(char *str);

uint8_t verify_password(char *str);
void usage(void);
uint8_t find_key_val(char *str,char *strbuf, uint8_t maxlen,char *key);

// get current temperature and pressure
#define ONEWIREBUS	6	// according your DS18B20 schematics - pin# connected to DQ line
void getCurrentTemp (char *temp);
void getCurrentPr (char *pr);

// 
uint8_t verify_password(char *str)
{
        // the first characters of the received string are
        // a simple password/cookie:
        if (strncmp(password,str,strlen(password))==0){
                return(1);
        }
        return(0);
}

void usage(void){
  gPlen=fill_tcp_data(buf,http200ok(),"<p>Usage: http://host_or_ip/password</p>\n");
}

// search for a string of the form key=value in
// a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\r\n
//
// The returned value is stored in strbuf. You must allocate
// enough storage for strbuf, maxlen is the size of strbuf.
// I.e the value it is declated with: strbuf[5]-> maxlen=5
uint8_t find_key_val(char *str,char *strbuf, uint8_t maxlen,char *key)
{
  uint8_t found=0;
  uint8_t i=0;
  char *kp;
  kp=key;
  while(*str &&  *str!=' ' && *str!='\n' && 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!='\n' && *str!='&' && i<maxlen-1){
      *strbuf=*str;
      i++;
      str++;
      strbuf++;
    }
    *strbuf='\0';
  }
  // return the length of the value
  return(i);
}

// convert a single hex digit character to its integer value
unsigned char h2int(char c)
{
        if (c >= '0' && c <='9'){
                return((unsigned char)c - '0');
        }
        if (c >= 'a' && c <='f'){
                return((unsigned char)c - 'a' + 10);
        }
        if (c >= 'A' && c <='F'){
                return((unsigned char)c - 'A' + 10);
        }
        return(0);
}

// decode a url string e.g "hello%20joe" or "hello+joe" becomes "hello joe"
void urldecode(char *urlbuf)
{
        char c;
        char *dst;
        dst=urlbuf;
        while ((c = *urlbuf)) {
                if (c == '+') c = ' ';
                if (c == '%') {
                        urlbuf++;
                        c = *urlbuf;
                        urlbuf++;
                        c = (h2int(c) << 4) | h2int(*urlbuf);
                }
                *dst = c;
                dst++;
                urlbuf++;
        }
        *dst = '\0';
}


// takes a string of the form confirm?pw=xxx&rst=1 and analyse it
// return values: -3 no valid password
//                 1 passpword OK
int8_t analyse_get_url(char *str)
{
  uint8_t loop=15;
  int8_t i;
  uint8_t on=0;
  uint8_t port, r;
    if (strncmp("confirm",str,7)==0){
      if (find_key_val(str,strbuf,STR_BUFFER_SIZE,"pw")){
        urldecode(strbuf);
        if (verify_password(strbuf)==0){
          return(-3);
        }else{
          return(1);
        }
      }else{
        return(-3);
      }
	  }

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


  // str is now something like password/?sw=rdx&a=1 or just end of url
  if (find_key_val(str,strbuf,STR_BUFFER_SIZE,"sw")){
    if (strbuf[0] != 'r'){
      return(0);
    }
    if (strbuf[1] != 'd'){
      return(0);
    }
    if (strbuf[2] < 0x3a && strbuf[2] > 0x2f){
    // is a ASCII number, return it
      port=strbuf[2]-0x30;
    }else{
      return(0);
    }
    if (find_key_val(str,strbuf,STR_BUFFER_SIZE,"a")){
      if (strbuf[0] == '1') {
        digitalWrite(relay2port[port],HIGH);// transistor on
        return(6);
      }
      else if (strbuf[0] == '0') {
        digitalWrite(relay2port[port],LOW);// transistor off
        return(6);
      }
    }
  } //fin "sw"
    return(-1);
}

uint16_t http200ok(void)
{
  uint16_t pl;
  pl= fill_tcp_data (buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>200 OK</h1>");
  return(pl);
}

// prepare the webpage by writing the data to the tcp send buffer
uint16_t print_webpage_entry(uint8_t *buf)
{
        uint16_t plen;
        plen=fill_tcp_data(buf,0,"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n");
        plen=fill_tcp_data(buf,plen,"<p>Entry to Home site:<br>");
        plen=fill_tcp_data(buf,plen,"<form action=/confirm method=get>");
        plen=fill_tcp_data(buf,plen,"\nPassword: <input type=password size=10 name=pw><br>\n<input type=submit value=\"Home\"></form></p>");
        plen=fill_tcp_data(buf,plen,"<hr><br>version 1.0, pinguino.cc\n");
        return(plen);
}

// Home page - temperature & pressure measurements & access to IO view and control
uint16_t print_webpage(uint8_t *buf)
{
  char temp_string[10], pr_string[10];
  int i=0;

  uint16_t pl;

  getCurrentTemp(temp_string);
  getCurrentPr(pr_string);
  
  pl = fill_tcp_data (buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
  pl = fill_tcp_data (buf, pl, "<center><p><h1>Welcome to Pinguino Ethernet Interface (ENC28J60)</h1></p> ");

  pl = fill_tcp_data (buf, pl, "<hr><br><form METHOD=get action=\"");
  pl = fill_tcp_data (buf, pl, baseurl);
  pl = fill_tcp_data (buf, pl, "\">");
  pl = fill_tcp_data (buf, pl, "<h2> Current Temperature is </h2> ");
  pl = fill_tcp_data (buf, pl, "<h1><font color=\"#00FF00\"> ");

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

  pl = fill_tcp_data (buf, pl, "  &#176C</font></h1><br> " );
  pl = fill_tcp_data (buf, pl, "<input type=hidden name=cmd value=1>");
  pl = fill_tcp_data (buf, pl, "<input type=submit value=\"Get Temperature\"></form>");

  pl = fill_tcp_data (buf, pl, "<hr><br><form METHOD=get action=\"");
  pl = fill_tcp_data (buf, pl, baseurl);
  pl = fill_tcp_data (buf, pl, "\">");
  pl = fill_tcp_data (buf, pl, "<h2> Current Pressure is </h2> ");
  pl = fill_tcp_data (buf, pl, "<h1><font color=\"#00FF00\"> ");
  
  i = 0;
  while (pr_string[i]) {
    buf[TCP_CHECKSUM_L_P+3+pl]=pr_string[i++];
    pl++;
  }

  pl = fill_tcp_data (buf, pl, "  hPa</font></h1><br> " );
  pl = fill_tcp_data (buf, pl, "<input type=hidden name=cmd value=2>");
  pl = fill_tcp_data (buf, pl, "<input type=submit value=\"Get Pressure\"></form>");

  pl = fill_tcp_data (buf, pl, "<hr><br><form METHOD=get action=\"");
  pl = fill_tcp_data (buf, pl, baseurl);

  pl = fill_tcp_data (buf, pl, "\">");
  pl = fill_tcp_data (buf, pl, "<input type=hidden name=cmd value=3>");
  pl = fill_tcp_data (buf, pl, "<input type=submit value=\"Get IO view & ctrl\"></form>");
  pl = fill_tcp_data (buf, pl, "</center><hr> <p> <a href=\"http://www.pinguino.cc\">www.pinguino.cc<a>");
  return(pl);
}
// IO server webpage
uint16_t print_webpage_io(void)
{
  uint16_t pl;
  char numstr[6];
  uint8_t switchnum=0; 

  pl = fill_tcp_data (buf, 0, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
  pl=fill_tcp_data(buf,pl,"<h2>");
  pl=fill_tcp_data(buf,pl,label);
  pl=fill_tcp_data(buf,pl,"</h2>\n<pre>");

  pl = fill_tcp_data (buf, pl, "<hr><br><form METHOD=get action=\"");
  pl = fill_tcp_data (buf, pl, baseurl); //
  pl = fill_tcp_data (buf, pl, "\">");
  pl = fill_tcp_data (buf, pl, "<input type=hidden name=cmd value=4>");
  pl = fill_tcp_data (buf, pl, "<input type=submit value=\"Refresh\"></form>");

  while(switchnum<MAX_RELAY){
    itoa(switchnum,strbuf,10); // convert integer to string
    pl=fill_tcp_data(buf,pl,"\nRD");
    pl=fill_tcp_data(buf,pl,strbuf); // switch number in ascii
    pl=fill_tcp_data(buf,pl," output: ");
    if (digitalRead(relay2port[switchnum])){
      pl=fill_tcp_data(buf,pl,"ON ");
    }else{
      pl=fill_tcp_data(buf,pl,"OFF");
    }
    pl=fill_tcp_data(buf,pl," [<a href=?sw=rd");
    pl=fill_tcp_data(buf,pl,strbuf); // switch number in ascii
    pl=fill_tcp_data(buf,pl,"&a=");
    if (digitalRead(relay2port[switchnum])){
      pl=fill_tcp_data(buf,pl,"0");
    }else{
      pl=fill_tcp_data(buf,pl,"1");
    }
    pl=fill_tcp_data(buf,pl,">change</a>]");
    pl=fill_tcp_data(buf,pl,"\n");
    switchnum++;
  }
  pl=fill_tcp_data(buf,pl,"\ndigital input RD4: ");
  if (digitalRead(d_input2port[0])){
    pl=fill_tcp_data(buf,pl,"1");
  }else{
    pl=fill_tcp_data(buf,pl,"0");
  }
  pl=fill_tcp_data(buf,pl,"\ndigital input RD5: ");
  if (digitalRead(d_input2port[1])){
    pl=fill_tcp_data(buf,pl,"1");
  }else{
    pl=fill_tcp_data(buf,pl,"0");
  }
  pl=fill_tcp_data(buf,pl,"\nanalog input ADC0: ");
  itoa(analogRead(A0),numstr,10); // convert integer to string
  pl=fill_tcp_data(buf,pl,numstr);
  pl=fill_tcp_data(buf,pl,"\nanalog input ADC1: ");
  itoa(analogRead(A1),numstr,10); // convert integer to string
  pl=fill_tcp_data(buf,pl,numstr);

  pl = fill_tcp_data (buf, pl, "<hr><br><form METHOD=get action=\"");
  pl = fill_tcp_data (buf, pl, baseurl); //
  pl = fill_tcp_data (buf, pl, "\">");
  pl = fill_tcp_data (buf, pl, "<input type=hidden name=cmd value=5>");
  pl = fill_tcp_data (buf, pl, "<input type=submit value=\"Home\"></form>");

  pl = fill_tcp_data (buf, pl, "</center><hr> <p> <a href=\"http://www.pinguino.cc\">www.pinguino.cc<a>");

  pl=fill_tcp_data(buf,pl,"</pre><hr>");
  return(pl);
}

void spint0() {
        if (stepcounter>1){
                stepcounter=0;
                digitalWrite(25,0);// transistor off
        }
        if (stepcounter>0){
                stepcounter++;
        }
}

// Generate an interrup about ever 1/2s form the 12.5MHz system clock
// Since we have that 1024 prescaler we do not really generate a second
// (1.00000256000655361677s) 

void setup(){
  int8_t i;
  for (i=0; i < MAX_RELAY; i++)
  { 
    pinMode(relay2port[i],OUTPUT);
    digitalWrite(relay2port[i],LOW);
  }
  for (i=0; i < MAX_D_INPUT; i++)
    pinMode(d_input2port[i],INPUT);
  Ethernet.init(mymac,myip,mywwwport); //eth_init
  OnTimer0(spint0, INT_MILLISEC, 500);
}

void loop(){
  uint16_t pl, dat_p;
  int8_t cmd;
  
  pl = Ethernet.read(BUFFER_SIZE, buf);//enc28j60PacketReceive
// pl will ne unequal to zero if there is a valid packet (without crc error)
  dat_p= Ethernet.requestAnalysis(buf,pl);//packetloop_icmp_tcp
  if(dat_p==0) return; // no http request

  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
    dat_p = http200ok();
    Ethernet.write(buf,dat_p); // send web page data
    return;
  }
  if (strncmp("/ ",(char *)&(buf[dat_p+4]),2)==0){
    dat_p=print_webpage_entry(buf);
    Ethernet.write(buf,dat_p); // send web page data
    return;
  }
  cmd=analyse_get_url((char *)&(buf[dat_p+5]));
                                // for possible status codes see:
                                // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

  if (cmd==-3){
    pl=fill_tcp_data(buf,0,"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Wrong password</h1>");
    dat_p=fill_tcp_data(buf,pl,"<a href=/>&gt;&gt;retry</a>\n");
    Ethernet.write(buf,dat_p); // send web page data
    return;
  }

  if (cmd==-1){
    dat_p=fill_tcp_data(buf,0,"HTTP/1.0 401 Unauthorized\r\nContent-Type: text/html\r\n\r\n<h1>401 Unauthorized</h1>");
    Ethernet.write(buf,dat_p); // send web page data
    return;
  }

  if (cmd==1 || cmd==2 || cmd==5){
    digitalWrite(25,1);// transistor on
    stepcounter=1; // trigger counter to clear transistor 1 sec later
    dat_p=print_webpage(buf);
    Ethernet.write(buf,dat_p); // send web page data
    return;
  }
  if (cmd==3) {
    dat_p = print_webpage_io();
    Ethernet.write(buf,dat_p); // send web page data
    return;
  }
  if (cmd == 4 || cmd == 6) {
  // just display the status:
    dat_p=print_webpage_io();
    Ethernet.write(buf,dat_p); // send web page data
    return;
  }

  if (cmd==10){
    Ethernet.write(buf,gPlen); // send web page data
    return;
  }

}
void getCurrentTemp (char *temp)
{
	TEMPERATURE t;
	
	if (DS18B20.read(ONEWIREBUS, SKIPROM, RES12BIT, &t))
	{
		if(t.sign) Sprintf(temp,"-%d.%d", t.integer, t.fraction);
  else Sprintf(temp,"%d.%d", t.integer, t.fraction);
	}
}

void getCurrentPr (char *pr)
{
int s;  // variable to store the value coming from the sensor
int ip; // rounded pressure (hPa)
float a0=1664; //ratio Vout/Vout pont diviseur experimental value
float a1=4095; // digital output for 3.21V = Valim uC measured with a multimeter
float a2=0.63817; //ratio Valim uC / Valim MPX4115AP 3.21V / 5.03V 
float b=95, c=0.9;
float p;
  s = analogRead(A0);
  p = (a0 * a2 * s / a1 + b) / c;
  ip= (int) (p+0.5);

  Sprintf(pr,"%d", ip);
}