9 Eylül 2016 Cuma

PIC18F4520 & ENC28J60 İLE WEB SERVER UYGULAMASI
















Merhabalar,

  Projemizde PIC mikrodenetleyici kullanarak bir web sayfasını ENC28J60 aracılığıyla internet üzerinden yayınlayacağız. Yayınladığımız web sayfası üzerinden röle kontrolü yapabileceğiz. Nem, sıcaklık, LDR ve hava kalite sensörlerimizden okuduğumuz verileri bu sayfada yayınlayacağız. Yani bir otomasyon web sunucusu yapmış olacağız. Projemizde kullanılacak donanımlar şunlardır;
 - PIC18F4520 Mikrodenetleyici,
 - ENC28J60 Ethernet LAN Modülü,
 - DHT22 Nem ve Sıcaklık Sensör Modülü,
 - MQ-135 Hava Kalitesi Ölçüm Modülü,
 - Röle Modülü,
 - LDR,
 - 16x2 LCD Ekran.
    Bu donanımlardan 16x2 LCD ekranı zaten daha önceki birçok projemizde kullandık. Diğerlerinin özelliklerine kısaca değineceğiz.





PIC18F4520 mikrodenetleyicisi PIC18FXXX serisine ait 8 bit mikrodenetleyicidir. Pin yapısı daha önce birçok projemizde kullandığımız 40 bacağa sahip PIC16F877A ile aynıdır. PIC18FXXX serisinde yüksek performanslı RISC işlemci kullanılmaktadır. 32KB program hafızası ve 1536 byte veri hafızasına sahiptir. 13 tane ADC kanalı bulunmaktadır. Mikrochip web sayfasından detaylı bilgi edinebilirsiniz.





ENC28J60 ethernet LAN modülü, üzerinde ethernet girişi bulunan ve  ethernet girişini SPI arayüzüne çeviren bir karttır. Üzerinde  Microchip'in ENC28J60 entegresi bulunan bu modül SPI aracılığıyla  birçok mikrodenetleyiciyle haberleşebilir. Bu modül ile pic mikrodenetleyicimizi internete bağlayabilecek ve internete bağlı bir cihazla mikrodenetleyicimize erişebileceğiz.




 DHT22 Nem ve Sıcaklık Sensör Modülü, üzerinde AM2302 sensörü bulunan bir karttır. Bu modülü bir önce ki yayınladığımız projede detaylı bir şekilde incelemiştik. Detaylı bilgi için DHT22 İLE SICAKLIK VE NEM ÖLÇÜMÜ başlıklı yayınımızı inceleyebilirsiniz.
MQ-135 Hava Kalitesi Ölçüm Modülü, ev- iş yeri gibi kapalı alanların hava kalitesine etki eden zehirli gazları tespit etmeye yarayan karttır. Üzerinde bulunan SnO2 bileşiğinden yapılmış sensör ünitesi temiz havada düşük iletkenliğe sahiptir. Hava kirlenince sensör ünitesinin iletkenliği artar. Modülün analog çıkışında ki sinyal mikrodenetleyicinin ADC birimi ile okuyacağız. Elde edilen ADC değerine bazı matematiksel işlemlerden geçirerek hava kalitesini ppm olarak elde edeceğiz.


  
  Röle Modülü, 5V ile kontakların kontrol edilebildiği 4 ayrı kanaldan oluşan röle kartıdır. Kartın üzerindeki röleler optokuplör ve transistör ile tetiklenmekdedir. Herhangi bir giriş pinine lojik '0' (0V) verildiğinde ilgili röle çeker, lojik 1 (5V) verildiğinde ise bırakır.





LDR (light dependent resistör) bir foto dirençdir. Foto dirençlerin değeri ışığa bağımlı olarak değişir. LDR, üzerine düşen ışık şiddeti artıkça direnç değeri azalır. Işık ile direç arasındaki ilişki lineer değil paraboliktir. LDR ile 10K' lık bir direnci birbirine seri bağlayıp voltaj bölücü yapacağız. Voltajın bölündüğü orta kısımdan mikrodenetleyicinin ADC birimi ile okuma yapacağız. 



Mikrodenetleyici ile modüllerin bağlantısı aşağıda ki şemada yer almaktadır.



















  DHT22 modülü için microC PRo for PIC derleyicisinde kullanabileceğimiz bir kütüphane oluşturduk. Projemizin programında da bu kütüphaneyi kullanacağız. AM2302-DHT22 kütüphane dosyalarına buradan ulaşabilirsiniz. İndirdiğiniz .mpkg uzantılı dosya kütüphanemizdir. Kütüphaneyi mikroC PRO for PIC derleyicisine yükleyebilmeniz için Mikro Elektronika'nın Package Manager programına ihtiyacınız olacak. Package Manager programını buradan indirebilirsiniz. Programı kurduktan sonra .mpkg uzantılı kütüphane dosyasını açtığınızda aşağıdaki gibi bir arayüzle karşılaşacaksınız.

 Install package butonuna tıklayarak yüklemeyi gerçekleştirebilirsiniz. Yükleme bittikten sonra microC PRo for PIC derleyicisinde library manager içerisinde AM2302-DHT22 kütüphanesine ulaşabilirsiniz. Artık bu kütüphaneyi DHT22 sensörü kullandığınız farklı projelerinizde kolayca kullanabilirsiniz.
  Yazdığımız mikrodenetleyici programı aşağıdadır.
#include  "__EthEnc28j60.h"

// duplex config flags
#define Spi_Ethernet_HALFDUPLEX     0x00  // half duplex
#define Spi_Ethernet_FULLDUPLEX     0x01  // full duplex

// mE ehternet NIC pinout
sfr sbit SPI_Ethernet_Rst at RC0_bit;
sfr sbit SPI_Ethernet_CS  at RC1_bit;
sfr sbit SPI_Ethernet_Rst_Direction at TRISC0_bit;
sfr sbit SPI_Ethernet_CS_Direction  at TRISC1_bit;
// end ethernet NIC definitions

//************  LCD module connections  ******************
sbit LCD_RS at RB5_bit;
sbit LCD_EN at RB4_bit;
sbit LCD_D4 at RB3_bit;
sbit LCD_D5 at RB2_bit;
sbit LCD_D6 at RB1_bit;
sbit LCD_D7 at RB0_bit;

sbit LCD_RS_Direction at TRISB5_bit;
sbit LCD_EN_Direction at TRISB4_bit;
sbit LCD_D4_Direction at TRISB3_bit;
sbit LCD_D5_Direction at TRISB2_bit;
sbit LCD_D6_Direction at TRISB1_bit;
sbit LCD_D7_Direction at TRISB0_bit;

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

// AM2302 module connections
sbit AM2302_pin_in  at RD0_bit;
sbit AM2302_pin_out at RD0_bit;
sbit AM2302_Pin_Direction at TRISD0_bit;
// END AM2302 module connections

/************************************************************
 * ROM constant strings
 */
const unsigned char httpHeader[] = "HTTP/1.1 200 OK\nContent-type: " ;  // HTTP header
const unsigned char httpMimeTypeHTML[] = "text/html\n\n" ;              // HTML MIME type
const unsigned char httpMimeTypeScript[] = "text/plain\n\n" ;           // TEXT MIME type
unsigned char httpMethod[] = "GET /";
/*
 * web page, splited into 2 parts :
 * when coming short of ROM, fragmented data is handled more efficiently by linker
 *
 * this HTML page calls the boards to get its status, and builds itself with javascript
 */
const   char    *indexPage =                   // Change the IP address of the page to be refreshed
"<meta http-equiv=\"refresh\" content=\"10;url=http://192.168.1.50\">\
<HTML><HEAD><title>mikrohex</title></HEAD><BODY>\
<h1>PIC18F4520 + ENC28J60 Web Server</h1>\
<a href=/>Reload</a>\
<script src=/s></script>\
<table><tr><td valign=top><table border=1 style=\"font-size:20px ;font-family: terminal ;\">\
<tr><th colspan=2>DHT22-MQ135-LDR</th></tr>\
<tr><td bgcolor=#00ffcc>SICAKLIK</td><td><script>document.write(SCT)</script>.<script>document.write(SCK)</script>°C</td></tr>\
<tr><td bgcolor=#00ffcc>NEM</td><td>%<script>document.write(NEMT)</script>.<script>document.write(NEMK)</script></td></tr>\
<tr><td bgcolor=#00ffcc>HAVA</td><td><script>document.write(AN2)</script> ppm</td></tr>\
<tr><td bgcolor=#00ffcc>IŞIK</td><td><script>document.write(AN1)</script></td></tr>\
" ;

const   char    *indexPage2 =  "</table></td><td>\
<table border=1 style=\"font-size:20px ;font-family: terminal ;\">\
<tr><th colspan=3>PORTD</th></tr>\
<script>\
var str,i;\
str=\"\";\
for(i=0;i<4;i++)\
{str+=\"<tr><td bgcolor=yellow>RÖLE \"+i+\"</td>\";\
if(~PORTD&(1<<(i+4))){str+=\"<td bgcolor=red>ON\";}\
else {str+=\"<td bgcolor=#cccccc>OFF\";}\
str+=\"</td><td><a href=/t\"+i+\">AÇ/KAPAT</a></td></tr>\";}\
document.write(str) ;\
</script>\
</table></td></tr></table>\
This is HTTP request #<script>document.write(REQ)</script></BODY></HTML>\
" ;

/***********************************
 * RAM variables
 */

unsigned char   myMacAddr[6] = {0x00, 0x14, 0xA5, 0x76, 0x19, 0x3f} ;   // my MAC address
unsigned char   myIpAddr[4]  = {192, 168, 1, 50} ;                     // my IP address

unsigned char   getRequest[15] ;                                        // HTTP request buffer
unsigned char   dyna[30] ;                                              // buffer for dynamic response
unsigned long   httpCounter = 0 ;                                       // counter of HTTP requests



const double Rl      = 1000.0;               // Rl (Ohm) - Load resistance
const double Vadc_5  = 0.0048828125;         // ADC step 5V/1024 4,88mV (10bit ADC)
double Vrl;                                  // Output voltage
double Rs;                                   // Rs (Ohm) - Sensor resistance
double ppm;                                  // ppm
double ratio;                                // Rs/Rl ratio

unsigned int adc_rd;

char txt[6];
bit izci;

void calculatePPM() {
  double lgPPM;
  Vrl = (float)adc_rd * Vadc_5;             // For 5V Vcc use Vadc_5  and  for 3V Vcc use Vadc_33
  Rs = Rl * (5 - Vrl)/Vrl;                   // Calculate sensor resistance
  ratio = Rs/Rl;                             // Calculate ratio
  lgPPM = (log10(ratio) * -0.8)+ 0.9;        // Calculate ppm
  ppm = pow(10,lgPPM);                       // Calculate ppm
}

 const char degree[] = {14,10,14,0,0,0,0,0};
void degreeChar(char pos_row, char pos_char) {
  char i;
    Lcd_Cmd(64);
    for (i = 0; i<=7; i++) Lcd_Chr_CP(degree[i]);
    Lcd_Cmd(_LCD_RETURN_HOME);
    Lcd_Chr(pos_row, pos_char, 0);
}

#define putConstString  SPI_Ethernet_putConstString

#define putString  SPI_Ethernet_putString

/*
 * this function is called by the library
 * the user accesses to the HTTP request by successive calls to Spi_Ethernet_getByte()
 * the user puts data in the transmit buffer by successive calls to Spi_Ethernet_putByte()
 * the function must return the length in bytes of the HTTP reply, or 0 if nothing to transmit
 *
 * if you don't need to reply to HTTP requests,
 * just define this function with a return(0) as single statement
 *
 */
unsigned int  SPI_Ethernet_UserTCP(unsigned char *remoteHost, unsigned int remotePort, unsigned int localPort, unsigned int reqLength, TEthPktFlags *flags)
{
        unsigned int    len = 0 ;                   // my reply length
        unsigned int    i ;                         // general purpose integer

        // should we close tcp socket after response is sent?
        // library closes tcp socket by default if canClose flag is not reset here
        // flags->canClose = 0; // 0 - do not close socket
                          // otherwise - close socket

        if(localPort != 80)                         // I listen only to web request on port 80
                {
                return(0) ;
                }

        // get 10 first bytes only of the request, the rest does not matter here
        for(i = 0 ; i < 10 ; i++)
                {
                getRequest[i] = SPI_Ethernet_getByte() ;
                }
        getRequest[i] = 0 ;
        //Lcd_Out(2, 1, getRequest);

        if(memcmp(getRequest, httpMethod, 5))       // only GET method is supported here
                {
                return(0) ;
                }

        httpCounter++ ;                             // one more request done

        if(getRequest[5] == 's')                    // if request path name starts with s, store dynamic data in transmit buffer
                {
                // the text string replied by this request can be interpreted as javascript statements
                // by browsers

                len = putConstString(httpHeader) ;              // HTTP header
                len += putConstString(httpMimeTypeScript) ;     // with text MIME type

                if (AM2302_Read() == 0){


                    if(get_temperature_sign())
                            IntToStr(get_temperature_whole()*(-1),dyna);
                    else
                            IntToStr(get_temperature_whole(),dyna);
                    len += putConstString("var SCT=") ;
                    len += putString(dyna) ;
                    len += putConstString(";") ;

                    WordToStr(get_temperature_fraction(),dyna);
                    len += putConstString("var SCK=") ;
                    len += putString(dyna) ;
                    len += putConstString(";") ;

                    WordToStr(get_humidity_whole(), dyna) ;
                    len += putConstString("var NEMT=") ;
                    len += putString(dyna) ;
                    len += putConstString(";") ;

                    WordToStr(get_humidity_fraction(), dyna) ;
                    len += putConstString("var NEMK=") ;
                    len += putString(dyna) ;
                    len += putConstString(";") ;
                    
                }
                  adc_rd=ADC_Read(2);
                  calculatePPM();
                  FloatToStr(ppm, txt) ;
                  strncpy(dyna,txt,6);



                // add AN2 value to reply
                //IntToStr(ADC_Read(2), dyna) ;
                len += putConstString("var AN2=") ;
                len += putString(dyna) ;
                len += putConstString(";") ;

                // add AN1 value to reply
                len += putConstString("var AN1=") ;
                adc_rd=ADC_Read(1);
                if(adc_rd>=450)
                len += putConstString("\"AYDINLIK\"") ;
                else len += putConstString("\"KARANLIK\"") ;
                len += putConstString(";") ;

                    if(izci){
                    LCD_Cmd(_LCD_CLEAR);
                    ByteToStr(get_temperature_whole(), txt);
                    Lcd_Out(1,1,"SICAKLK:");
                    Lcd_Out(1,9,txt);
                    if(get_temperature_sign()) Lcd_Out(1,9,"-");
                    Lcd_Out(1,12,".");
                    Lcd_Chr(1,13,get_temperature_fraction()+48);
                    degreeChar(1, 14);
                    Lcd_Out(1,15,"C");

                    ByteToStr(get_humidity_whole(), txt);
                    Lcd_Out(2,1,"NEM    :");
                    Lcd_Out(2,9,txt);
                    Lcd_Out(2,12,".");
                    Lcd_Chr(2,13,get_humidity_fraction()+48);
                    Lcd_Out(2,14,"%RH");
                    }
                    else{
                     LCD_Cmd(_LCD_CLEAR);
                     Lcd_Out(1,1,"HAVA:");
                     Lcd_Out(1,7,dyna);
                     Lcd_Out(1,13," ppm");

                     if(adc_rd>=450)
                     Lcd_Out(2,1,"ORTAM AYDINLIK");
                     else
                     Lcd_Out(2,1,"ORTAM KARANLIK");
                    }

                    izci=~izci;



                // add PORTD value (LEDs) to reply
                len += putConstString("var PORTD=") ;
                IntToStr(PORTD, dyna) ;
                len += putString(dyna) ;
                len += putConstString(";") ;

                // add HTTP requests counter to reply
                IntToStr(httpCounter, dyna) ;
                len += putConstString("var REQ=") ;
                len += putString(dyna) ;
                len += putConstString(";") ;
                }
        else if(getRequest[5] == 't')                           // if request path name starts with t, toggle PORTD (LED) bit number that comes after
                {
                unsigned char   bitMask = 0 ;                   // for bit mask

                if(isdigit(getRequest[6]))                      // if 0 <= bit number <= 9, bits 8 & 9 does not exist but does not matter
                        {
                        bitMask = getRequest[6] - '0' ;         // convert ASCII to integer
                        bitMask = 1 << (bitMask + 4) ;                // create bit mask
                        PORTD ^=bitMask;                      // toggle PORTD with xor operator
                       }
                }

        if(len == 0)                                            // what do to by default
                {
                len =  putConstString(httpHeader) ;             // HTTP header
                len += putConstString(httpMimeTypeHTML) ;       // with HTML MIME type
                len += putConstString(indexPage) ;              // HTML page first part
                len += putConstString(indexPage2) ;             // HTML page second part
                }

        return(len) ;                                           // return to the library with the number of bytes to transmit
        }

/*
 * this function is called by the library
 * the user accesses to the UDP request by successive calls to Spi_Ethernet_getByte()
 * the user puts data in the transmit buffer by successive calls to Spi_Ethernet_putByte()
 * the function must return the length in bytes of the UDP reply, or 0 if nothing to transmit
 *
 * if you don't need to reply to UDP requests,
 * just define this function with a return(0) as single statement
 *
 */
unsigned int  SPI_Ethernet_UserUDP(unsigned char *remoteHost, unsigned int remotePort, unsigned int destPort, unsigned int reqLength, TEthPktFlags *flags)
        {
        unsigned int    len ;                           // my reply length

        // reply is made of the remote host IP address in human readable format
        ByteToStr(remoteHost[0], dyna) ;                // first IP address byte
        dyna[3] = '.' ;
        ByteToStr(remoteHost[1], dyna + 4) ;            // second
        dyna[7] = '.' ;
        ByteToStr(remoteHost[2], dyna + 8) ;            // third
        dyna[11] = '.' ;
        ByteToStr(remoteHost[3], dyna + 12) ;           // fourth

        dyna[15] = ':' ;                                // add separator

        // then remote host port number
        WordToStr(remotePort, dyna + 16) ;
        dyna[21] = '[' ;
        WordToStr(destPort, dyna + 22) ;
        dyna[27] = ']' ;
        dyna[28] = 0 ;

        // the total length of the request is the length of the dynamic string plus the text of the request
        len = 28 + reqLength;

        // puts the dynamic string into the transmit buffer
        SPI_Ethernet_putBytes(dyna, 28) ;

        // then puts the request string converted into upper char into the transmit buffer
        while(reqLength--)
                {
                SPI_Ethernet_putByte(toupper(SPI_Ethernet_getByte())) ;
                }

        return(len) ;           // back to the library with the length of the UDP reply
        }



void main() {
        ADCON0 = 0x09;
        ADCON1 |= 0x0C; // no analog inputs
        ADFM_bit=1;
        CMCON |= 0x07 ; // turn off comparators

        PORTD = 0xFF;
        TRISD = 0 ;             // set PORTD as output
        PORTA = 0 ;
        TRISA = 0xff ;          // set PORTA as input for ADC

        PORTB = 0 ;

        izci=1;

        Lcd_Init();                               // initialize Lcd.
        Lcd_Cmd(_LCD_CLEAR);                      // clear Lcd.
        Lcd_Cmd(_LCD_CURSOR_OFF);                 // turn off cursor.
        Lcd_Out(1, 1, "    MIKROHEX    ");
        Lcd_Out(2, 1, "   WEB SERVER   ");

        SPI1_Init();
        SPI_Ethernet_Init(myMacAddr, myIpAddr, Spi_Ethernet_FULLDUPLEX) ;

        while(1) {

        SPI_Ethernet_doPacket() ;

  }
}
   Programda kullanılan fonksiyonlar hakkında bilgi edinmek için MikroC PRO for PIC derleyicisinin Library Manager bölümünü inceleyebilirsiniz. Devremizi yukarıdaki şemada olduğu gibi kurduktan ve programı PIC mikrodenetleyiciye yükledikten sonra ENC28J60 modülümüzü ethernet kablosuyla modemimize bağlamamız gerekmektedir. Tüm bağlantıları yapıp devremizi besledikten sonra web tarayıcımızda 192.168.1.50 IP adresine giderek web server sayfamıza ulaşmış olacağız. Karşılaşacağımız sayfa aşağıdaki gibi olacaktır.




















Çalışmamızın fotoğrafı aşağıdadır.

   Projemizin çalışma videosu aşağıda yer almaktadır.

   Proje hakkında sormak istediklerinizi sayfanın altında yorum kutusuna yazabilirsiniz. Bir başka yazıda görüşmek üzere...
Başarılar...

4 yorum:

  1. Harika bir yazı olmuş .Eline sağlık

    YanıtlaSil
  2. Eser Bey çok güzel ve eğitici olmuş. Emeğinize sağlık...

    Yalnız bir ricam var... MicroC yi yeni öğreniyorum sizinle. Ben genellikle PicBasic kullanıyorum. Biraz zorlanacağım.
    Aynı projede sadece ilgisayarımdaki saat bilgisini pic'e nasıl gönderebilirim... 6 digit seven segment display kullanacağım...
    Yardımcı olursanız sevinirim...
    Saygılarımla...

    YanıtlaSil
  3. hocam wep server arayüzünü nasıl tasarladınız acaba bilgi verirmisiniz

    YanıtlaSil