Pages: [1]   Go Down
Author Topic: Basic Analog Pin reporting using ENCENC28J60 EthernetShield  (Read 1486 times)
0 Members and 1 Guest are viewing this topic.
Indonesia
Offline Offline
Jr. Member
**
Karma: 0
Posts: 70
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,
I'll share this to others who just start playing with ENC28J60 ethernetshield.
Reasons :
1. I received so many help/enlighment from others
2. This Shield is not Official , so there is no "standardize" Library for it.

Credits goes to :
1. Andy , http://blog.thiseldo.co.uk/?p=344 , For his ENC28J60 Arduino Library.
2. Trystan , http://openenergymonitor.org/emon/node/88.
3. PaulS, http://arduino.cc/forum/index.php?action=profile;u=16084 , Tonns of enlighment.
4. Gus , http://projectgus.com/2010/07/eeprom-access-with-arduino/

Ok , Here we go.

Important note : I made a small modification of Andy's library. It's a good library, but do not "end user" to make change of HOSTNAME (need to be hardcoded at sketch level). I don't want this, so I made small modification.
Here it is :

Code:
Modification to --> ip_arp_udp_tcp.c
1. Look for --> #if defined (WWW_client) , around line# 1310
2. Look for --> //GET , around line# 1316
3. Commen out all :
len=fill_tcp_data_p(bufptr,0,PSTR("GET "));
len=fill_tcp_data_p(bufptr,len,client_urlbuf);
if( client_urlbuf_var )
len=fill_tcp_data(bufptr,len,client_urlbuf_var);
// I would prefer http/1.0 but there is a funny
// bug in some apache webservers which causes
// them to send two packets (fragmented PDU)
// if we don't use HTTP/1.1 + Connection: close
len=fill_tcp_data_p(bufptr,len,PSTR(" HTTP/1.1\r\nHost: "));
len=fill_tcp_data_p(bufptr,len,client_hoststr);
len=fill_tcp_data_p(bufptr,len,PSTR("\r\nUser-Agent: EtherShield/1.6\r\nAccept: text/html\r\nConnection: close\r\n\r\n"));

4. Replace with :
len=fill_tcp_data(bufptr,0,client_urlbuf_var);


Arduino sketch.
A. Main Sketch
Code:
/*
WE USE MODIFIED VERSION OF Andy's Library ver 1.6
*/
#include <EtherShield.h>
//#include <EEPROM.h>
#include<string.h>

//Structured EEPROM--START
#include <avr/eeprom.h>

#define eeprom_read_to(dst_p, eeprom_field, dst_size) eeprom_read_block(dst_p, (void *)offsetof(__eeprom_data, eeprom_field), MIN(dst_size, sizeof((__eeprom_data*)0)->eeprom_field))
#define eeprom_read(dst, eeprom_field) eeprom_read_to(&dst, eeprom_field, sizeof(dst))
#define eeprom_write_from(src_p, eeprom_field, src_size) eeprom_write_block(src_p, (void *)offsetof(__eeprom_data, eeprom_field), MIN(src_size, sizeof((__eeprom_data*)0)->eeprom_field))
#define eeprom_write(src, eeprom_field) { typeof(src) x = src; eeprom_write_from(&x, eeprom_field, sizeof(x)); }
#define MIN(x,y) ( x > y ? y : x )
 
//const int buflen = 32;
 
/*
 * __eeprom_data is the magic name that maps all of the data we are
 * storing in our EEPROM
 */
struct __eeprom_data {
byte magic;
byte mac[6];
byte ip[4];
byte gw[4];
byte srv[4];
char host[40];
char callfile[20];
byte conflen[10];
char nodeid[10];
char nodepin[10];
};

//Structured EEPROM--STOP

//----ETH VARS-------------------------------
#define PORT 80                   // Ndak bisa di rubah
byte magic;
byte mac[6] = {0x54,0x55,0x58,0x10,0x00,0x25};
byte ip[4] = {192,168,10,231};
byte gw[4] = {192,168,10,1};
byte srv[4] = { 192,168,10,232 }; // Get pachube ip by DNS call
char host[40]="telemetry.bino.int";
char callfile[20]="/post.php?"  ;    // Set your own feed ID here
byte conflen[10]={0,0,0,0,0,0,0,0,0,0};
char nodeid[10]="mynodeid";
char nodepin[10]="mynodepin";
byte mymagic=0x0a;

uint8_t resend=0;
int8_t dns_state=0;
uint32_t timetosend=millis();
uint16_t dat_p;
long lastDnsRequest = 0L;
int plen = 0;

EtherShield es=EtherShield();

#define BUFFER_SIZE 550
uint8_t buf[BUFFER_SIZE+1];
//-------------VARS to SEND------------------------------
int sendvarlen = 6;
char sendvarname[6][5]={"s0","s1","s2","s3","s4","s5"};
char sendvarval[6][10]={"0","0","0","0","0","0"};
//-------------------------------------------------
//----FOR Menuing----
boolean inmenu = false;
boolean waitentry=false;

void setup(){
  Serial.begin(9600);
  eeprom_read(magic, magic);
  if (magic != mymagic)
  {
    delay(3000);
    Serial.print("FIRST BLOOD !!!");
    magic = mymagic;
    saveall();
  }
  readall();

  delay(100);
 
  es.ES_enc28j60Init(mac);

  //init the ethernet/ip layer:
  es.ES_init_ip_arp_udp_tcp(mac, ip, PORT);

  // init the web client:
  es.ES_client_set_gwip(gw);  // e.g internal IP of dsl router
  es.ES_client_set_wwwip(srv);
}


void loop()
{
  //Do this in any condition
  dns_state=2;
  // handle ping and wait for a tcp packet - calling this routine powers the sending and receiving of data
  // If we have IP address for server and its time then request data
  ethReady();
  //---------------------------

  if (!inmenu)
  {
    if (Serial.available())
    {
      if (Serial.read() == 'c')
      {
        printmenu();
      }
    }
    else
    {
      domainproc();
    }
  }
  else
  {
    if (Serial.available())
    {
      domenu();
    }   
  }
}

void domainproc()
{
  if(millis() - timetosend > 1000)  // every 10 seconds
  {
    timetosend = millis();
    readsensor();
    ethSend();
  }
}



void readsensor()
{
  for (int i = 0 ; i < sendvarlen ; i++)
  {

    itoa(analogRead(i),sendvarval[i],10);
  }
}

Oppss ... I need to post 2 other part seperately
Logged

Indonesia
Offline Offline
Jr. Member
**
Karma: 0
Posts: 70
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

B. Any Ethernet bussiness
Code:
void ethSetup()
{
  //Serial.println("Setup Ethernet");
  /*initialize enc28j60*/
  es.ES_enc28j60Init(mac);

  //init the ethernet/ip layer:
  es.ES_init_ip_arp_udp_tcp(mac, ip, PORT);

  // init the web client:
  es.ES_client_set_gwip(gw);  // e.g internal IP of dsl router
  es.ES_client_set_wwwip(srv);
  timetosend = millis();
}

void ethReady()
{
  plen=0;
  plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);
  dat_p=es.ES_packetloop_icmp_tcp(buf,plen);
}

void ethSend()
{
      char HTTPCALL[chkvarlen() + 150];
      strcpy(HTTPCALL, "GET ");
      strcat(HTTPCALL, callfile);
      strcat(HTTPCALL, "nodeid=");
        strcat(HTTPCALL, nodeid);
        strcat(HTTPCALL, "&");
      strcat(HTTPCALL, "nodepin=");
        strcat(HTTPCALL, nodepin);
        strcat(HTTPCALL, "&");
      for (int z = 0 ; z < sendvarlen ; z++)
      {
        strcat(HTTPCALL,sendvarname[z]);
        strcat(HTTPCALL,"=");
        strcat(HTTPCALL,sendvarval[z]);
        if (z < sendvarlen-1)
        {
          strcat(HTTPCALL,"&");
        }
      }
      strcat(HTTPCALL, " HTTP/1.1\r\nHost: ");
      strcat(HTTPCALL, host);
      strcat(HTTPCALL, "\r\nUser-Agent: EtherShield/1.6\r\nAccept: text/html\r\nConnection: close\r\n\r\n");
      //-------END----Struct the HTTPCALL

      Serial.println("HTTP Data To Send");
     
      Serial.println(HTTPCALL);
     
      // note the use of PSTR - this puts the string into code space and is compulsory in this call
      // second parameter is a variable string to append to HTTPPATH, this string is NOT a PSTR
      //es.ES_client_browse_url(PSTR(HTTPPATH), NULL, PSTR(HOSTNAME), &browserresult_callback);
      //es.ES_client_browse_url(PSTR(HTTPPATH), strtimetosend, PSTR(HOSTNAME), &browserresult_callback);
      //es.ES_client_browse_url(PSTR(HTTPPATH), strtimetosend, PSTR(""), &browserresult_callback);
      es.ES_client_browse_url(callfile, HTTPCALL, host, &browserresult_callback);
}

int chkvarlen()
{
  int retval=0;
  for (int i=0; i<sendvarlen; i++)
  {
    retval = retval + strlen(sendvarname[i]);
    retval = retval + strlen(sendvarval[i]);
  }
  retval = retval + (2 * sendvarlen);
   
  int ii = strlen(callfile) + strlen(host) + strlen(nodeid) + strlen(nodepin);

  retval = retval + ii;


  return retval;
}

void browserresult_callback(uint8_t statuscode,uint16_t datapos){
  if (datapos != 0)
  {
    /*
    // now search for the csv data - it follows the first blank line
    // I'm sure that there is an easier way to search for a blank line - but I threw this together quickly
    // and it works for me.
    uint16_t pos = datapos;
    while (buf[pos])    // loop until end of buffer (or we break out having found what we wanted)
    {
      while (buf[pos]) if (buf[pos++] == '\n') break;   // find the first line feed
      if (buf[pos] == 0) break; // run out of buffer
      if (buf[pos++] == '\r') break; // if it is followed by a carriage return then it is a blank line (\r\n\r\n)
    }
    if (buf[pos])  // we didn't run out of buffer
    {
      pos++;  //skip over the '\n' remaining
      Serial.println((char*)&buf[pos]);
    }
    */
  }
}


Logged

Indonesia
Offline Offline
Jr. Member
**
Karma: 0
Posts: 70
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

C. Configuration menu Bussiness.
Code:
void printmenu()
{
  Serial.println("Configuration");
  Serial.println("---------------");

  Serial.print("1. MAC Address ");
    Serial.print("[");
    for (int i=0; i<6; i++)
    {
      Serial.print(mac[i],HEX);
      if (i<5)
      {
        Serial.print(".");
      }
    }
    Serial.println("]");
   
  Serial.print("2. IP Address [");
    for (int i=0; i<4; i++)
    {
      Serial.print(ip[i],DEC);
      if (i<3)
      {
        Serial.print(".");
      }
    }
    Serial.println("]");

  Serial.print("3. Gateway [");
    for (int i=0; i<4; i++)
    {
      Serial.print(gw[i],DEC);
      if (i<3)
      {
        Serial.print(".");
      }
    }
    Serial.println("]");

  Serial.print("4. Server IP [");
    for (int i=0; i<4; i++)
    {
      Serial.print(srv[i],DEC);
      if (i<3)
      {
        Serial.print(".");
      }
    }
    Serial.println("]");

  Serial.print("5. Server Name [");
    Serial.print(host);
    Serial.println("]");

  Serial.print("6. File to Call[");
    Serial.print(callfile);
    Serial.println("]");

  Serial.print("7. Node ID [");
    Serial.print(nodeid);
    Serial.println("]");

  Serial.print("8. Node PIN [");
    Serial.print(nodepin);
    Serial.println("]");

  Serial.println("s. Save");

  Serial.println("x. Exit ");

  Serial.println("---------------");

  Serial.print("Option : ");
 
  inmenu = true;
}

void domenu()
{
  switch (Serial.read())
  {
    case '1':
      domenu1();
      break;
    case '2':
      domenu2();
      break;
    case '3':
      domenu3();
      break;
    case '4':
      domenu4();
      break;
    case '5':
      domenu5();
      break;
    case '6':
      domenu6();
      break;
    case '7':
      domenu7();
      break;
    case '8':
      domenu8();
      break;
    case 's' :
      domenu_save();
      break;
    case 'x' :
      domenu_exit();
      break;
  }
  if (inmenu)
  {
    printmenu();
  }
}
void domenu1()
{
  Serial.println("1");
  Serial.print("Enter MAC Address : ");
  waitentry=true;
  while (waitentry)
  {
    if (Serial.available()>0)
    {
      delay(10);
      for (int x=0 ; x<6; x++)
      {
        int i = 0;
        char inchar[3]="";
        while (Serial.available() && i<3)
        {
          char serchar = Serial.read();
          Serial.print(serchar);
          if (serchar == 13 || serchar == '.')
          {
            delay(10);
            break;
          }
          else
          {
            inchar[i]=serchar;
            i++;
          }
        }
        mac[x] = ( htoi(inchar[0]) * 16 ) + htoi(inchar[1]);
      }
      Serial.println();
      waitentry = false;
    }
  } 
}
int htoi (char c) {  //does not check that input is valid
/*
By : mspguy
at : http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1292893820
*/
    if (c<='9')
        return c-'0';
    if (c<='F')
        return c-'A'+10;
    if (c<='f')
        return c-'a'+10;
    return 0;
}
void domenu2()
{
  Serial.println("2");
  Serial.print("Enter IP Address : ");
  waitentry=true;
  while (waitentry)
  {
    if (Serial.available()>0)
    {
      delay(10);
      for (int x=0 ; x<4; x++)
      {
        int i = 0;
        char inchar[4]="";
        while (Serial.available() && i<4)
        {
          char serchar = Serial.read();
          Serial.print(serchar);
          if (serchar == 13 || serchar == '.')
          {
            delay(10);
            break;
          }
          else
          {
            inchar[i]=serchar;
            i++;
          }
        }
        ip[x] = atoi(inchar);
      }
      Serial.println();
      waitentry = false;
    }
  } 
}
void domenu3()
{
  Serial.println("3");
  Serial.print("Enter  Gateway IP Address : ");
  waitentry=true;
  while (waitentry)
  {
    if (Serial.available()>0)
    {
      delay(10);
      for (int x=0 ; x<4; x++)
      {
        int i = 0;
        char inchar[4]="";
        while (Serial.available() && i<4)
        {
          char serchar = Serial.read();
          Serial.print(serchar);
          if (serchar == 13 || serchar == '.')
          {
            delay(10);
            break;
          }
          else
          {
            inchar[i]=serchar;
            i++;
          }
        }
        gw[x] = atoi(inchar);
      }
      Serial.println();
      waitentry = false;
    }
  } 
}
void domenu4()
{
  Serial.println("4");
  Serial.print("Enter Server IP Address : ");
  waitentry=true;
  while (waitentry)
  {
    if (Serial.available()>0)
    {
      delay(10);
      for (int x=0 ; x<4; x++)
      {
        int i = 0;
        char inchar[4]="";
        while (Serial.available() && i<4)
        {
          char serchar = Serial.read();
          Serial.print(serchar);
          if (serchar == 13 || serchar == '.')
          {
            delay(10);
            break;
          }
          else
          {
            inchar[i]=serchar;
            i++;
          }
        }
        srv[x] = atoi(inchar);
      }
      Serial.println();
      waitentry = false;
    }
  } 
}


void domenu5()
{
  Serial.println("5");
  Serial.print("Enter Server Hostname :");
  int i = 0;
  char commandbuffer[40];
  delay(30);
  waitentry=true;
  while (waitentry)
  {
    if (Serial.available()>0)
    {
      delay(30);
      while (Serial.available() && i<40)
      {
        commandbuffer[i++] = Serial.read();
      }
      if (i>0)
      {
        memset(host, '\0', sizeof(host));

        commandbuffer[i++]='\0';
        strcpy(host, commandbuffer);
        Serial.println(host);
      }
      waitentry = false;
    }
  } 
}
void domenu6()
{
  Serial.println("6");
  Serial.print("Enter File to call :");
  int i = 0;
  char commandbuffer[20];
  delay(30);
  waitentry=true;
  while (waitentry)
  {
    if (Serial.available()>0)
    {
      delay(30);
      while (Serial.available() && i<20)
      {
        commandbuffer[i++] = Serial.read();
      }
      if (i>0)
      {
        memset(callfile, '\0', sizeof(callfile));

        commandbuffer[i++]='\0';
        strcpy(callfile, commandbuffer);
        Serial.println(callfile);
      }
      waitentry = false;
    }
  } 
}
void domenu7()
{
  Serial.println("7");
  Serial.print("Enter Our Node ID : ");
  int i = 0;
  char commandbuffer[10];
  delay(30);
  waitentry=true;
  while (waitentry)
  {
    if (Serial.available()>0)
    {
      delay(30);
      while (Serial.available() && i<10)
      {
        commandbuffer[i++] = Serial.read();
      }
      if (i>0)
      {
        memset(nodeid, '\0', sizeof(nodeid));

        commandbuffer[i++]='\0';
        strcpy(nodeid, commandbuffer);
        Serial.println(nodeid);
      }
      waitentry = false;
    }
  } 
}
void domenu8()
{
  Serial.println("8");
  Serial.print("Enter Our Node PIN : ");
  int i = 0;
  char commandbuffer[10];
  delay(30);
  waitentry=true;
  while (waitentry)
  {
    if (Serial.available()>0)
    {
      delay(30);
      while (Serial.available() && i<10)
      {
        commandbuffer[i++] = Serial.read();
      }
      if (i>0)
      {
        memset(nodepin, '\0', sizeof(nodepin));

        commandbuffer[i++]='\0';
        strcpy(nodepin, commandbuffer);
        Serial.println(nodepin);
      }
      waitentry = false;
    }
  } 
}



void domenu_save()
{
  Serial.println("SAVE");
  saveall();
}
void domenu_exit()
{
  Serial.println("EXIT !!");
  Serial.println();
  Serial.println();
  inmenu = false;
  Serial.flush();
}

void saveall()
{
  eeprom_write(magic, magic);
  eeprom_write_from(mac, mac,6);
  eeprom_write_from(ip, ip,4);
  eeprom_write_from(gw, gw,4);
  eeprom_write_from(srv, srv,4);
  eeprom_write_from(host, host,strlen(host)+1);
  conflen[0]=strlen(host);
  eeprom_write_from(callfile, callfile,strlen(callfile)+1);
  conflen[1]=strlen(callfile);
 
  eeprom_write_from(nodeid,nodeid,strlen(nodeid)+1);
  conflen[2]=strlen(nodeid);
 
  eeprom_write_from(nodepin,nodepin,strlen(nodepin)+1);
  conflen[3]=strlen(nodepin);



  eeprom_write_from(conflen, conflen,10);

}
void readall()
{
  eeprom_read_to(conflen, conflen,10);
  eeprom_read(magic, magic);
  eeprom_read_to(mac, mac,6);
  eeprom_read_to(ip, ip,4);
  eeprom_read_to(gw, gw,4);
  eeprom_read_to(srv, srv,4);
  memset(host, '\0', sizeof(host));
  eeprom_read_to(host, host,conflen[0]);
  memset(callfile, '\0', sizeof(callfile));
  eeprom_read_to(callfile, callfile,conflen[1]);
 
  memset(nodeid, '\0', sizeof(nodeid));
  eeprom_read_to(nodeid, nodeid,conflen[2]);

  memset(nodepin, '\0', sizeof(nodepin));
  eeprom_read_to(nodepin, nodepin,conflen[3]);

}

Thats all.

Now I need to learn on WebServer feature of Andy's library.
My USB port always dead when I Plug my EthernetShield.
So I think it'll be better if the configuration is using web based configuration form.
Hopefully I don't crack my lame brain down.

Sincerely
-bino-
« Last Edit: February 04, 2011, 10:32:23 pm by binooetomo » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey Bino,

Thanks for your post, but I'm trying to make a really simple example to show how to use the Arduino as a Client to post data to a database using PHP/MySQL leveraging your post by basically making it bare-bones.

I have run it trying to do a few things but I some Errors that I don't really know much about as I am not an full-fledge programmer.

I get one error:
  Invalid conversion from 'void(*)(uint8_t,uint16_t)' to 'void(*)(uint8_t,uint16_t,uint16_t)' when trying to compile.

So for fun, I added a blank uint16_t in the callback function, just to make it compile.  In anycase, when I upload to the Arduino, I can see that the HTTPCALL does function, but I have no idea whether I am actually connecting or sending data to my PHP script. (I check the db to see if it arrives, to no avail).

How can I make the below code work? What am I missing? Did I take out too much?

How can I see whether it is connecting to my PHP script?  Why did I get that error?

SO MANY QUESTIONS! hahah....

Anyway, your help and guidance would be greatly appreciated! 

CHEERS!

Code:
/*
WE USE MODIFIED VERSION OF Andy's Library ver 1.6
*/

#include <etherShield.h>

#include <string.h>


//----ETH VARS-------------------------------


#define PORT 80           // Ndak bisa di rubah

byte magic;
byte mac[6] = {0x54,0x55,0x58,0x10,0x00,0x25};
byte ip[4] = {192,168,1,203}; // ip of arduino
byte gw[4] = {192,168,1,245};  //ip of router
byte srv[4] = { 192,168,1,66 }; // ip of server
char host[13]="192.168.1.66";   // server name?
char callfile[21]="/ethernet_to_db.php?"  ;    // place to put crap
byte mymagic=0x0a;

int8_t dns_state=0;
uint32_t timetosend=millis();
uint16_t dat_p;
//long lastDnsRequest = 0L;
int plen = 0;

EtherShield es= EtherShield();

#define BUFFER_SIZE 550
uint8_t buf[BUFFER_SIZE+1];
//--------------------------------------------

void setup(){
  Serial.begin(9600);

 
  es.ES_enc28j60Init(mac);

  //init the ethernet/ip layer:
  es.ES_init_ip_arp_udp_tcp(mac, ip, PORT);
o a
  // init the web client:
  es.ES_client_set_gwip(gw);  // e.g internal IP of dsl router
  es.ES_client_set_wwwip(srv);
 
 
  timetosend = millis();
   
 
}


void loop()
{
  //Do this in any condition
  dns_state=2;
  // handle ping and wait for a tcp packet - calling this routine powers the sending and receiving of data
  // If we have IP address for server and its time then request data
  ethReady();
  //---------------------------


  if(millis() - timetosend > 1000)  // every 10 seconds
  {
    timetosend = millis();
 
    ethSend();
  }
}



void ethReady()
{
  plen=0;
  plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);
  dat_p=es.ES_packetloop_icmp_tcp(buf,plen);

}

void ethSend()
{
      char HTTPCALL[150];
      strcpy(HTTPCALL, "GET ");
      strcat(HTTPCALL, callfile);
      strcat(HTTPCALL, "value=HELLO");
        //strcat(HTTPCALL, nodeid);
        //strcat(HTTPCALL, "&");
      //strcat(HTTPCALL, "nodepin=");
        //strcat(HTTPCALL, nodepin);
       // strcat(HTTPCALL, "&");

      strcat(HTTPCALL, " HTTP/1.0\r\nHost: ");
      strcat(HTTPCALL, host);
      strcat(HTTPCALL, "\r\nUser-Agent: EtherShield/1.6\r\nAccept: text/html\r\nConnection: close\r\n\r\n");
      //-------END----Struct the HTTPCALL

      Serial.println("HTTP Data To Send");
     
      Serial.println(HTTPCALL);
     
      // note the use of PSTR - this puts the string into code space and is compulsory in this call
      // second parameter is a variable string to append to HTTPPATH, this string is NOT a PSTR
      //es.ES_client_browse_url(PSTR(HTTPPATH), NULL, PSTR(HOSTNAME), &browserresult_callback);
      //es.ES_client_browse_url(PSTR(HTTPPATH), strtimetosend, PSTR(HOSTNAME), &browserresult_callback);
      //es.ES_client_browse_url(PSTR(HTTPPATH), strtimetosend, PSTR(""), &browserresult_callback);

//The actual code to send data to PHP script:
      es.ES_client_browse_url(callfile, HTTPCALL, host, &browserresult_callback);
       
}

void browserresult_callback(uint8_t statuscode,uint16_t datapos){
  if (datapos != 0)
  {
   //removed since all was commented out anyway
   
  }
}



Logged

Pages: [1]   Go Up
Jump to: