HOME      PROJETOS      PROGRAMAÇÃO     

WEB SERVER NO PIC18F4620




INTRODUÇÃO

Este projeto explica como montar um servidor de web (HTTP) num microcontrolador PIC18F4620 usando o Stack TCP/IP que a Microchip fornece gratuitamente para ser embarcado em seus microcontroladores. Isso deveria facilitar a tarefa de programação pois o Stack TCP/IP contem todos os protocolos necessários para comunicação em rede mas, para muitos hobistas e curiosos da eletrônica digital, não é tão simples como parece. Além da literatura estar em inglês, muitas coisas não são explicadas adequadamente ficando um abismo profundo entre a vontade de fazer a coisa funcionar e a possibilidade prática de executar o circuito e a programação. Como se isso não bastasse, ao abrir a pasta de exemplos e demos, que acompanha o Stack TCP/IP da Microchip, muitos vão se surpreender ao descobrir que todos os software foram escritos para suportar somente as placas de desenvolvimento homologadas pela Microchip, sem considerar que todos os programas são escritos em linguagem C, mais especificamente o C18.




OBJETIVOS

O objetivo principal deste projeto era embarcar o TCP/IP num microcontrolador simplesmente espetado num protoboard, e que pudesse responder a alguns comandos vindos do browser (Firefox, Opera, GoogleChrome, etc). Depois de vários dias pesquisando, encontramos o site do Jorge Amodio onde havia um projeto que embarcava um Stack TCP/IP e um servidor de web num microcontrolador PIC18F4620. Analisando o projeto, verificamos que haviam várias características indesejáveis que tornavam o projeto um pouco complexo para nossos objetivos. No projeto analisado havia um controle para display de mensagens num LCD, uma memória EEPROM externa onde seria gravado o sistema de arquivos do servidor de web, comunicação com o PC através da USART com um chip MAX232.

Além disso, para fazer a conexão com a rede, ele usava um chip ENC28J60 e um conector MagJack J45 acoplados com uma porção de outros componentes passivos (resistores, capacitores e bobinas) que complicavam a execução do circuito.

Após uma revisão, os periféricos LCD, EEPROM externa e o chip MAX232 foram eliminados. O chip ENC28J60 e o conector MagJack J45 configurados no esquema foram substituidos por uma placa da HanRun com um ENC28J60 integrado com um soquete MagJack J45 que já fornece a conexão com a rede Ethernet, facilitando em muito a vida do pobre curioso ou hobista que pretenda montar esse circuito. O único trabalho vai ser montar um cabinho para conectar a placa ao protoboard ou ao PCB.

A foto a esquerda mostra a placa da HanRun que, no Brasil, pode ser adquirida em lojas de eletrônica ou pela Internet pelo site Mercado Livre. Para quem quiser montar o projeto original, espetando chip ENC28J60 e o conector MagJack J45 no protoboard, aqui está o esquema elétrico.




O CIRCUITO

Este projeto já foi testado no protoboard com sucesso, mas ainda não foi montado em PCB, por isso, para quem quiser fazer o PCB, o arquivo de Projeto Eagle com o esquema elétrico está disponível aqui.

A imagem abaixo mostra o esquema elétrico do nosso projeto simplificado com os seguintes componentes:

  • Um microcontrolador PIC18F4620
  • Uma placa da HanRun adquirida no mercado
  • Um integrado CMOS 74ACT125N (three-state buffer não-inversor) ENC28J45 ao PIC
  • Tres chaves tácteis
  • Cinco LEDs
  • Um cristal de 10MHZ
  • Um regulador de tensão 7805
  • Alguns capacitores, diodos e resistores.






A PLACA HanRun

A placa da HanRun contem um microcontrolador ENC28J60 com o protocolo Ethernet embarcado para facilitar a tarefa de comunicação com esse tipo de rede. Essa placa executa a mesma função da placa de rede Ethernet do seu PC.

O microcontrolador PIC18F4620 é alimentado com 5,0V porém, a placa ENC28J60 deve ser alimentada no máximo com 3,3V, obrigando o uso de um regulador nessa faixa de tensão. No nosso projeto foram usado dois diodos 1N4148 que provocam uma queda de tensão de aproximadamente 0,7V cada um, fazendo com que a tensão caia de 5,0V para 3,6V. Isso pode ser feito com segurança pois o regulador de tensão de entrada 7805, fornece uma tensão nominal de 5,0V que na verdade fica entre 4,7V e 5,0V. Assim temos...

4,7v - (2 x 0,7v) = 3,3V no mínimo e
5,0v - (2 x 0,7v) = 3,6V no máximo

...que é perfeitamente seguro e mais que suficiente para alimentar a placa.

Aqui não vamos discutir em profundidade o funcionamento placa da HanRun mas apenas prestar atenção na sua pinagem. A placa possui 10 pinos emparelhados 5 a 5. Observando a foto do lado esquerdo, que não está muito nítida, podemos notar duas colunas de pinos verticais.

A coluna de pinos da esquerda, de cima para baixo, apresenta os sinais CLKOUT, WOL, SI, CS e VCC.

A coluna de pinos da direita, de cima para baixo, apresenta os sinais INT, SO, SCK, RESET e GND.

Esses pinos devem ser conectados aos pinos correspondentes do esquema elétrico. Notar que os pinos CLKOUT, WOL não são usados neste projeto e ficam desconectados.

Para conectar essa placa ao protoboard foi montado um cabinho com conectores Modu que organizam a interface e simplifica a conexão.
No protoboard foi inserida uma barra de pinos verticais onde o cabinho foi conectado. A foto do lado direito mostra como ficou a ligação da plaquinha no protoboard.




POR QUE USAR UM PIC18F4620 NESTE PROJETO?

O microcontrolador PIC18F4620 foi escolhido para este projeto por ter uma capacidade de memória de programa suficiente para embarcar o Stack TCP/IP com um servidor HTTP (Web Server) e ainda poder alojar, na mesma memória, o sistema de arquivos que contem as páginas HTML e arquivos de imagens que serão usados pelo Web Server.




O OSCILADOR DE 40MHZ

Neste projeto o PIC18F4620 deve operar com um cristal de 10MHZ. Essa frequência deverá ser quadruplicada usado a opção HSPLL que habilitará o PLL interno, fazendo com que o microcontrolador atinja uma frequência de operação de 40MHZ. Isso é necessário pois o PIC vai ter que suar um bocado para atender a requisição de páginas pelo Web Server. O hobista não precisa se preocupar pois todos esses detalhes já estão definidos no programa. Basta apenas usar um cristal de 10MHZ ou menos na montagem do circuito. Pode-se usar cristais com frequências menores porém a performance vai cair. Nunca use um cristal de frequência superior a 10MHZ a menos que o programa seja recompilado com a configuração HS que desabilita o PLL.

Após montar corretamente o circuito (confira N vezes, sendo N > 9), faça o download do Projeto WebServer para o MPLAB e descomprima o arquivo.

Para poder recompilar o programa deste projeto é necessário que o MPLAB possa acessar o compilador C18.




O 3-STATE BUFFER 74ACT125N

Os pinos INT e SO da placa HanRun fornecem no máximo 3,3V de tensão para o nível lógico alto (1). O PIC deve receber uma tensão nominal de 5,0V para entender que se trata de um sinal no nível alto. Para ajustar o nível da tensão enviada ao PIC pela placa HanRun, é empregado um 3-STATE BUFFERR 74ACT125N que tem somente essa função.




MONTAGEM NO PROTOBOARD
FUNCIONA!!!




O STACK TCP/IP E O MODELO MULTITAREFA COOPERATIVA

O Stack TCP/IP da Microchip é um conjunto de protocolos semelhantes ao verdadeiro TCP/IP usado nos computadores reais e seu objetivo é permitir que um microcontrolador seja conectado à rede e responda a requisições de modo transparente, isto é, como se fosse um computador real. Nem todas as funções estão presentes no Stack da Microchip mas podem ser programadas pelo usuário.

O TCP/IP tradicional roda sob um sistema multitarefa que é gerenciado pelo sistema operacional do host (Linux, Windows).
Um modelo multitarefa é exigido quando muitos serviços devem ser executados simultaneamente.
Como o Stack TCP/IP vai rodar num microcontrolador e não sob um sistema operacional, ele próprio é construido para proporcionar um modelo de multitarefa chamado Multitarefa Cooperativa. Em poucas palavras, o modelo cooperativo implica em dividir a aplicação em pequenas partes (módulos) e colocá-las sob controle de um laço (loop) no programa principal que despachará cada uma. A tarefa despachada é então executada e retorna o controle ao programa principal que despachará a próxima tarefa.

O trecho de código abaixo exemplifica como isso acontece no módulo main.c que se encontra em /webserver_v04/src/main.c. As tarefas "StackTask()", "HTTPServer()" e "DiscoveryTask()" são despachadas nessa ordem pelo laço infinito "while(1)" fazendo nosso web server funcionar.

     while(1)
     {
         if ( TickGetDiff(TickGet(), t) >= TICK_SECOND/2 )
         {
           t = TickGet();
           LED0_IO ^= 1;        // pisca o LED1
         }

         StackTask();           // Executa serviços do TCP/IP
         HTTPServer();          // Executa o web server
         DiscoveryTask();       // Executa a função announce
     }

O Stack TCP/IP deverá ser carregados no PIC juntamente com um programa aplicativo que executará as tarefas do Web Server. Como o Stack possui muitos módulos e muitos deles eram desnecessários para nosso objetivo, somente alguns deles foram carregados para compilar nosso Web Server.




O ENDEREÇO IP

O endereço IP configurado no programa deste projeto é 192.168.0.60.

Se sua rede possui endereço 192.168.0.0 e seu Gateway tem o endereço 192.168.0.1, então pode se considerar com sorte porque nenhuma alteração será necessária para rodar o programa. Continue lendo a partir do ítem GRAVANDO O PROGRAMA.

Se sua rede tem endereço diferente de 192.168.0.0, então será necessário configurar o programa para refletir o endereço de sua rede. Para isso, abra no MPLAB o projeto PIC10T.mcp, que se encontra na pasta webserver_v04, edite o arquivo config.h, que se encontra na aba Headers da janela Projetos, altere as seguintes linhas desse arquivo para configurá-lo com as características de sua rede:

LINHA 25 ..... #define DEFAULT_IP_ADDRESS { 192, 168, 0, 60 }   endereco IP da sua rede
LINHA 26 ..... #define DEFAULT_NETMASK { 255, 255, 255, 0 }   mascara
LINHA 27 ..... #define DEFAULT_GATEWAY { 192, 168, 0, 1 }   endereco do seu Gateway
LINHA 28 ..... #define DEFAULT_NS1 { 192, 168, 1, 1 }   endereco do NS

Atenção, nas linhas acima os números que formam os endereços devem ser separados por vírgula.

Salve o arquivo e recompile o programa.




GRAVANDO O PROGRAMA

A gravação do PIC18F4620 deverá ser feita com um programador ICSP ou algum outro programador que possa ser reconhecido pelo MPLAB e que suporte o microcontrolador PIC18F4620.

Se optar por um programador tipo JDM/ICSP use o programa WinPic800 pois ele possui suporte para gravação do PIC18F4620.
Na configuração do WinPic800, deve-se selecionar o programador JDM.

O programa ICPROG ainda não possui suporte para esse microcontrolador.

Após selecionar o programador, carregue o arquivo /webserver_v04/bin/PIC10T.hex e grave no PIC18F4620.




TESTANDO O PROGRAMA

Ligue a alimentação e, se tudo correu bem, o LED 1 começará a piscar.

Abra seu browser preferido e, na linha de comandos, digite o endereço IP configurado para o circuito, neste caso http://192.168.0.60 e a seguinte página deverá aparecer:

Clique no botão Iniciar e a página da aplicação vai aparecer.

Nesta tela pode-se interagir com o circuito.

A cor azul representa o LED1 que está piscando no circuito.

Os botões L1, L2,L3 e L4 podem ser clicado para acender e apagar os respectivos LEDs. Quando o servidor recebe a solicitação, o estado do LED clicado muda e a cor que representa seu estado na página é alterada.

O botão Blink faz com que os LEDs pisquem durante alguns segundos.

O botão Toggle inverte o estado dos LEDs, isto é, os que estão apagados se acenderão e vice-versa.

O botão Reset apagará todos os LEDs.

As cores verdes do lado direito B1 e B2 representam as chaves tácteis nomeadas BOTAO 1 e BOTAO 2 no esquema elétrico. Quando uma ou ambas as chaves forem pressionadas no circuito, as cores mudarão de verde claro para verde escuro na página.

A página é refrescada a cada segundo por um Java Script fazendo com que o servidor envie o estado em que as variáveis apresentam no momento.

Aqui concluimos a parte braçal deste projeto. Agora vamos tentar entender um pouco como tudo funciona para que um dia possamos fazer algo útil com isso.





CONFIGURANDO O HARDWARE

O hardware do projeto é configurado no arquivo /webserver_v04/src/include/spin2.h. Veja a listagem abaixo:

...1 // ****************************************************************************
...2 // CONFIGURACAO DE HARDWARE
...3 // ****************************************************************************
...4 #include <p18f4620.h>
...5 
...6 //*****************************************************************************
...7 // Define Frequencia em Hertz
...8 #define CPU_CLOCK (40000000)
...9 #define TCY_CLOCK (CPU_CLOCK/4)
..10 
..11 //*****************************************************************************
..12 //
..13 // PORTA Direction and initial status
..14 // +-------------- n/a OSC1
..15 // |+------------- n/a OSC2
..16 // ||+------------ RA5 = LCD E (if LCD preent)
..17 // |||+----------- RA4 = LED3, mapped to software LED2_IO
..18 // ||||+---------- RA3 = LED2, mapped to software LED1_IO
..19 // |||||+--------- RA2 = LED1, mapped to software LED0_IO
..20 // ||||||+-------- RA1 = n/c
..21 // |||||||+------- RA0 = n/c
..22 #define INIT_TRISA (0b00000000)
..23 #define INIT_PORTA (0b00000000)
..24 
..25 // PORTB Direction and initial status
..26 // +-------------- RB7 = Used as PGD for ICSP
..27 // |+------------- RB6 = Used as PGC for ICSP
..28 // ||+------------ RB5 = Optional ENC28J60 RESET
..29 // |||+----------- RB4 = 25LC256 Serial EEPROM CS
..30 // ||||+---------- RB3 = ENC28J60 CS
..31 // |||||+--------- RB2 = Optional Input for ENC28J60 INT
..32 // ||||||+-------- RB1 = S3 Push Button, mapped to BUTTON1_IO
..33 // |||||||+------- RB0 = S2 Push Button, mapped to BUTTON0_IO
..34 #define INIT_TRISB (0b00000011)
..35 #define INIT_PORTB (0b00011000)
..36 
..37 // PORTC Direction and initial status
..38 // +-------------- RC7 = Used as RX by USART
..39 // |+------------- RC6 = Used as TX by USART
..40 // ||+------------ RC5 = Used as SDO for SPI interface
..41 // |||+----------- RC4 = Used as SDI for SPI interface
..42 // ||||+---------- RC3 = Used as SCK for SPI interface
..43 // |||||+--------- RC2 = n/c
..44 // ||||||+-------- RC1 = n/c
..45 // |||||||+------- RC0 = n/c
..46 #define INIT_TRISC (0b10010000)
..47 #define INIT_PORTC (0b11000000)
..48 
..49 // PORTD Direction and initial status
..50 // +-------------- RD7 = LED5, mapped to software LED4_IO
..51 // |+------------- RD6 = LED4, mapped to software LED3_IO
..52 // ||+------------ RD5 = LCD R/W
..53 // |||+----------- RD4 = LCD RS
..54 // ||||+---------- RD3 = LCD DB7
..55 // |||||+--------- RD2 = LCD DB6
..56 // ||||||+-------- RD1 = LCD DB5
..57 // |||||||+------- RD0 = LCD DB4
..58 #define INIT_TRISD (0b00000000)
..59 #define INIT_PORTD (0b00000000)
..60 
..61 // PORTE Direction and initial status
..62 // +--------- RE2 = n/c
..63 // |+-------- RE1 = n/c
..64 // ||+------- RE0 = n/c
..65 #define INIT_TRISE (0b00000000)
..66 #define INIT_PORTE (0b00000000)
..67 
..68 //*****************************************************************************
..69 // Initialization values for various registers
..70 #define INIT_ADCON0 (0b00000000) // ADON=0, Channel 0
..71 #define INIT_ADCON1 (0b00001111) // No analog inputs
..72 #define INIT_ADCON2 (0b10111110) // Right justify, 20TAD ACQ time, Fosc/64 (~21.0kHz)
..73 
..74 //*****************************************************************************
..75 // Available LEDs and switches macro name definitions for application use
..76 #define LED0_IO (LATAbits.LATA2)
..77 #define LED1_IO (LATAbits.LATA3)
..78 #define LED2_IO (LATAbits.LATA4)
..79 #define LED3_IO (LATDbits.LATD6)
..80 #define LED4_IO (LATDbits.LATD7)
..81 #define LED5_IO (LATDbits.LATD7) // No LED5 map to LED4
..82 #define LED6_IO (LATDbits.LATD7) // No LED6 map to LED4
..83 #define LED7_IO (LATDbits.LATD7) // No LED7 map to LED4
..84 
..85 #define BUTTON0_IO (PORTBbits.RB0)
..86 #define BUTTON1_IO (PORTBbits.RB1)
..87 #define BUTTON2_IO (PORTBbits.RB1) // No BUTTON2 map to BUTTON1
..88 #define BUTTON3_IO (PORTBbits.RB1) // No BUTTON3 map to BUTTON1
..89 
..90 //*****************************************************************************
..91 // Definitions for ENC28J60 Ethernet Controller interface
..92 #define USE_ENC28J60
..93 #define ENC_CS_IO (LATBbits.LATB3)
..94 #define ENC_SPI_IF (PIR1bits.SSPIF)
..95 #define ENC_SSPBUF (SSPBUF)
..96 #define ENC_SPISTAT (SSPSTAT)
..97 #define ENC_SPISTATbits (SSPSTATbits)
..98 #define ENC_SPICON1 (SSPCON1)
..99 #define ENC_SPICON1bits (SSPCON1bits)
.100 #define ENC_SPIEN (SSPCON1bits.SSPEN)
.101 #define ENC_SPICON1_CFG (0x20)
.102 #define ENC_SPISTAT_CFG (0x40)
.103 

Algumas coisas importantes devem ser observadas na listagem acima:

  1. A linha 4 inclui o arquivo referente ao microcontrolador em uso, o PIC18F4620.

  2. A linha 8 define a frequência de clock na qual o microcontrolador vai operar. A configuração de clock já foi discutida anteriormente, assim, se desejar modificar a frequência de clock, deve considerar a modificação do valor do parâmetro CPU_CLOCK nesta linha.

  3. Da linha 12 até a linha 89 são mapeadas as portas do PIC18F4620. Qualquer referência a uma porta ou a um bit de uma porta deve ser feito usando-se as definições estabelecidas nessas linhas. Assim, se em algum lugar do programa você desejar acender o LED1, deverá usar a referência LED1_IO conforme definido na linha 77.

  4. Da linha 92 até a linha 102, estão as definições dos registradores do ENC28J60. Jamais altere essas definições, senão...





CONFIGURAÇÃO DO TCP/IP

A configuração do TCP/IP é feita no arquivo webserver_v04/src/include/config.h>. Veja a listagem abaixo.

...1 #ifndef _CONFIG_H
...2 #define _CONFIG_H
...3 
...4 #include <stdio.h>
...5 #include <stdlib.h>
...6 #include <string.h>
...7 
...8 #if defined(__C30__)
...9 #include <libpic30.h>
..10 #endif
..11 
..12 #define VERSION   "HPSpin 4.17" 
..13 
..14 #include "include/spin2.h"              // configuracao de hardware
..15 
..16 
..17 //*****************************************************************************
..18 // TCP/IP Config
..19 //
..20 // Define the default values for your MAC and IP configuration.
..21 // Notice that the IP addresses are separated by commas and not by the
..22 // traditional dot in the decimal dotted notation.
..23 //
..24 #define DEFAULT_MAC_ADDRESS { 0x00, 0x04, 0xa3, 0x00, 0x02, 0x00 }
..25 #define DEFAULT_IP_ADDRESS  { 192, 168, 0, 60 }
..26 #define DEFAULT_NETMASK     { 255, 255, 255, 0 }
..27 #define DEFAULT_GATEWAY     { 192, 168, 0, 1 }
..28 #define DEFAULT_NS1         { 192, 168, 1, 1 }
..29 //#define SNTP_SERVER_IP      { 192, 43, 244, 18 }   // time.nist.gov
..30 
..31 #define DEFAULT_NETBIOS_NAME  "SPIN2"
..32 
..33 
..34 //*****************************************************************************
..35 // Stack Modules
..36 //
..37 #define STACK_USE_ICMP               // ICMP reply (ping) module
..38 #define STACK_USE_HTTP_SERVER        // HTTP server
..39 #define STACK_USE_ANNOUNCE           // Ethernet Device Discoverer server/client
..40 
..41 
..42 //*****************************************************************************
..43 // Some time related definitions
..44 //
..45 // Define Ticks per second for the tick manager
..46 //
..47 #define TICKS_PER_SECOND     (100)   // 10ms
..48 
..49 
..50 //*****************************************************************************
..51 // Miscellaneous Stack Options
..52 //
..53 
..54 // Define the Baud Rate for the RS-232 Serial Interface
..55 #define BAUD_RATE (19200)            // bps
..56 
..57 // Include User process code in main.c
..58 //
..59 //#define ENABLE_USER_PROCESS
..60 
..61 
..62 // Define default TCP port for HTTP server
..63 //
..64 #define HTTP_PORT (80)
..65 
..66 
..67 // Maximum number of TCP sockets allowed
..68 // Note that each TCP socket consumes 38 bytes of RAM.
..69 //
..70 #define MAX_SOCKETS          (6u)
..71 
..72 // Maximum number of TCP sockets allowed
..73 //
..74 #define MAX_UDP_SOCKETS      (4u)
..75 
..76 // Maximum numbers of simultaneous HTTP connections allowed.
..77 // Each connection consumes 10 bytes of RAM.
..78 //
..79 #define MAX_HTTP_CONNECTIONS (3u)
..80 
..81 
..82 //*****************************************************************************
..83 // Storage options
..84 #define MPFS_USE_PGRM            // Use Program Memory
..85 
..86 
..87 //*****************************************************************************
..88 // Set Baud Rate Generator Register value based on defined baud rate
..89 // and map registers based on UART.
..90 #if ((TCY_CLOCK+2*BAUD_RATE)/BAUD_RATE/4 - 1) <= 255
..91         #define SPBRG_VAL ((TCY_CLOCK+2*BAUD_RATE)/BAUD_RATE/4 - 1)
..92         #define USART_USE_BRGH_HIGH
..93 #else
..94         #define SPBRG_VAL ((TCY_CLOCK+8*BAUD_RATE)/BAUD_RATE/16 - 1)
..95 #endif
..96 
..97 #define UART_TXSTAbits TXSTAbits
..98 #define UART_RCSTAbits RCSTAbits
..99 #define UART_TXREG     TXREG
.100 #define UART_TXSTA     TXSTA
.101 #define UART_RCSTA     RCSTA
.102 #define UART_SPBRG     SPBRG
.103 #define UART_RCREG     RCREG
.104 
.105 #define STACK_USE_UDP
.106 #define STACK_USE_TCP
.107 #define STACK_USE_SW_CKSUM
.108 #define MAC_TX_BUFFER_SIZE   (576)
.109 #define MAC_TX_BUFFER_COUNT  (MAX_SOCKETS+1)
.110 #define MAC_RX_BUFFER_SIZE       (MAC_TX_BUFFER_SIZE)
.111 
.112 //*****************************************************************************
.113 // Based on the protocol/applications modules enabled calculate the number
.114 // of available sockets
.115 #define AVAILABLE_SOCKETS        (MAX_SOCKETS)
.116 #define AVAILABLE_UDP_SOCKETS    (MAX_UDP_SOCKETS)
.117 
.118 #define AVAILABLE_SOCKETS2   (AVAILABLE_SOCKETS - MAX_HTTP_CONNECTIONS)
.119 
.120 #define MPFS_RESERVE_BLOCK       (64)
.121 
.122 
.123 //*****************************************************************************
.124 // Verify hardware configuration
.125 
.126 // Verify that CPU clock frequency is defined
.127 #if !defined(CPU_CLOCK)
.128     #error CFG002: CPU_CLOCK not defined in hardware configuration file
.129 #endif
.130 
.131 #if !defined(TCY_CLOCK)
.132     #error CFG003: TCY_CLOCK not defined in hardware configuration file
.133 #endif
.134 
.135 #if (SPBRG_VAL > 255) && !defined(__C30__)
.136     #error CFG004: SPBRG value is out of range for defined CPU_CLOCK
.137 #endif
.138 
.139 #if !defined(TICKS_PER_SECOND)
.140     #error CFG005: TICKS_PER_SECOND must be defined
.141 #endif
.142 
.143 // Verify Ethernet controller interface configuration
.144 #if defined(USE_ENC28J60)
.145     #if !defined(ENC_CS_IO)
.146         #error CFG011: ENC_CS_IO not defined in hardware configuration file
.147     #endif
.148     #if !defined(ENC_SPI_IF) || !defined(ENC_SSPBUF) || !defined(ENC_SPISTAT) || \
.149         !defined(ENC_SPISTATbits) || !defined(ENC_SPICON1) || \
.150         !defined(ENC_SPICON1bits) 
.151         #error CFG012: Missing SPI configuration registers for ENC28J60
.152     #endif
.153 #else
.154     #error CFG010: No ethernet controller defined
.155 #endif
.156 
.157 
.158 
.159 //*****************************************************************************
.160 // Verify TCP/IP stack options and configuration
.161 //
.162 #if !defined(DEFAULT_MAC_ADDRESS)
.163     #error CFG050: No MAC Address defined
.164 #endif
.165 
.166 #if !defined(DEFAULT_IP_ADDRESS) 
.167     #error CFG051: No IP Address defined
.168 #endif
.169 
.170 #if !defined(DEFAULT_NETMASK)
.171     #error CFG052: No network mask defined
.172 #endif
.173 
.174 #if !defined(DEFAULT_GATEWAY)
.175     #error CFG053: No default gateway defined
.176 #endif
.177 
.178 #if defined(STACK_USE_DNS) && !defined(DEFAULT_NS1)
.179     #error CFG054: DNS client enabled but no default name server defined
.180 #endif
.181 
.182 #if defined(STACK_USE_FTP_SERVER) && (!defined(FTP_USERNAME) || \
.183     !defined(FTP_PASSWORD))
.184     #error CFG055: FTP server enabled but username or password not defined
.185 #endif
.186 
.187 #if defined(STACK_USE_NBNS) && !defined(DEFAULT_NETBIOS_NAME)
.188     #error CFG056: NetBIOS Name Service enabled but hostname not defined
.189 #endif
.190 
.191 #if (MAX_SOCKETS <= 0 || MAX_SOCKETS > 255)
.192     #error CFG058: Invalid MAX_SOCKETS value defined
.193 #endif
.194 
.195 #if (MAX_UDP_SOCKETS <= 0 || MAX_UDP_SOCKETS > 255 )
.196     #error CFG059: Invalid MAX_UDP_SOCKETS value defined
.197 #endif
.198 
.199 #if (MAC_TX_BUFFER_SIZE <= 0 || MAC_TX_BUFFER_SIZE > 1500 )
.200     #error CFG060: Invalid MAC_TX_BUFFER_SIZE value defined
.201 #endif
.202 
.203 #if ( (MAC_TX_BUFFER_SIZE * MAC_TX_BUFFER_COUNT) > (5 * 1024) )
.204     #error CFG061: Warning, receive buffer is small.  Excessive packet loss may occur.
.205 #endif
.206 
.207 #if defined(STACK_USE_HTTP_SERVER)
.208     #if (MAX_HTTP_CONNECTIONS <= 0 || MAX_HTTP_CONNECTIONS > 255 )
.209         #error CFG062: Invalid MAX_HTTP_CONNECTIONS value defined
.210     #endif
.211 #endif
.212 
.213 #if AVAILABLE_SOCKETS3 < 0 || AVAILABLE_SOCKETS3 > MAX_SOCKETS
.214     #error CFG063: The defined MAX_SOCKETS value is too low
.215 #endif
.216 
.217 #if AVAILABLE_UDP_SOCKETS4 < 0 || AVAILABLE_UDP_SOCKETS4 > MAX_UDP_SOCKETS
.218     #error CFG064: The defined MAX_UDP_SOCKETS value is too low
.219 #endif
.220 
.221 #if defined(MPFS_USE_EEPROM) && defined(MPFS_USE_PGRM)
.222     #error CFG080: You can not define both clases of storage defined
.223 #endif
.224 
.225 #if defined(STACK_USE_HTTP_SERVER) && !(defined(MPFS_USE_EEPROM) || \
.226     defined(MPFS_USE_PGRM))
.227     #error CFG081: No storage option defined for HTTP Server pages
.228 #endif
.229 
.230 #if defined(STACK_USE_FTP_SERVER) && defined(MPFS_USE_PGRM)
.231     #error CFG082: FTP Server not supported for program memory storage option
.232 #endif
.233 
.234 #if defined(STACK_USE_HTTP_SERVER)
.235     #if (MAC_TX_BUFFER_SIZE <= 130 || MAC_TX_BUFFER_SIZE > 1500 )
.236         #error HTTP001: Invalid MAC_TX_BUFFER_SIZE value specified
.237     #endif
.238 #endif
.239 
.240 
.241 //*****************************************************************************
.242 // Compiler specific replacement macros
.243 //*****************************************************************************
.244 #if defined(DEBUG)
.245     #define DebugPrint(a)       {putrsUART(a);}
.246 #else
.247     #define DebugPrint(a)       {}
.248 #endif
.249 
.250 #if defined(__18CXX)
.251     #define ROM                  rom
.252     #define ROMPC                rom far char
.253     #define strcpypgm2ram(a, b)  strcpypgm2ram(a,(rom far char*)b)
.254     #define memcpypgm2ram(a,b,c) memcpypgm2ram((void *)a,(const far rom void *)b,c)
.255     #define memcmppgm2ram(a,b,c) memcmppgm2ram((void *)a,(const far rom void *)b,c)
.256     #define __attribute__(a)
.257     #define BusyUART()           BusyUSART()
.258     #define CloseUART()          CloseUSART()
.259     #define ConfigIntUART(a)     ConfigIntUSART(a)
.260     #define DataRdyUART()        DataRdyUSART()
.261     #define OpenUART(a,b,c)      OpenUSART(a,b,c)
.262     #define ReadUART()           ReadUSART()
.263     #define WriteUART(a)         WriteUSART(a)
.264     #define getsUART(a,b,c)      getsUSART(b,a)
.265     #define putsUART(a)          putsUSART(a)
.266     #define getcUART()           ReadUSART()
.267     #define putcUART(a)          WriteUSART(a)
.268     #define putrsUART(a)         putrsUSART((rom far char*)a)
.269     #define printf(a)            printf((rom far char *)a) 
.270 #endif
.271 
.272 #endif // _CONFIG_H
.273 
.274 

Algumas coisas importantes devem ser observadas na listagem acima:

  1. As linhas de 24 a 31 refletem as características de sua rede. Como já foi dito anteriormente, se sua rede tem o endereço 192.168.0.0, nada deve ser modificado neste arquivo mas, se sua rede não possui esse endereço, as modificações devem ser feitas nessas linhas.

  2. A linha 31 define o nome Netbios do nosso circuito na rede. Qualquer nome pode ser atribuido aqui.

  3. Outra coisa importante a observar são as linhas de 37 a 39. Essas linhas definem os protocolos do Stack TCP/IP que devem ser usados no projeto.





HTTP - O SERVIDOR

Talvez seja desnecessário definir aqui o que é um web server mas, vamos relembrar:

Um web server é um programa que atende requisições HTTP que é uma sigla que significa "HyperText Transfer Protocol".
Esse protocolo pertence à camada de aplicação do modelo OSI.

Uma requisição para um web server geralmente é feita por um browser com o comando GET, mais precisamente:

GET nome_do_arquivo HTTP/1.1

Quando essa requisição chega ao web server, ele procura em seu diretório pelo arquivo requisitado em "nome_do_arquivo", lê esse arquivo e o devolve ao browser solicitante. O arquivo devolvido ao cliente é, na maioria das vezes, um arquivo texto escrito em linguagem HTML. Assim, o web server nada mais é do que um servidor de arquivos.

Além disso, um bom web server deve responder certas requisições e produzir respostas dinâmicas, como por exemplo, enviar uma página ao browser com o resultado de uma pesquisa. Isso também pode ser feito com o comando GET, acrescentando alguns parâmetros na requisição como:

GET nome_do_arquivo?campo1=valor1&campo2=valor2&campo3=valor3 HTTP/1.1

Na requisição acima, na frente do "nome_do_arquivo", temos o caracter "?" informando ao web server que o texto que vem a seguir contem uma série de pares "nome=valor" separados por um caracter "&". Ao identificar esse tipo de requisição, o web server verifica se o arquivo informado em "nome_do_arquivo" é um executável e, em caso positivo, passa o controle para esse arquivo processar a tarefa usando os pares "nome=valor" como parâmetros. O arquivo informado em "nome_do_arquivo" ganha controle, processa as informações e devolve uma resposta dinamicamente, enviando linhas de texto HTML de volta ao browser.

Além de GET há outro método chamado POST mas não vamos discutí-lo aqui para não alongar muito o assunto.

Os métodos "GET" e "POST" são usados em formulários HTML que facilitam a aquisição de dados através do browser como no exemplo abaixo:

<form method="GET" action="nome_do_arquivo">
<input name="campo1" value="valor1">
<input name="campo2" value="valor2">
<input name="campo3" value="valor3">
<input type="submit" value="ok">
</form>

Ao acionar o botão OK, o formulário acima envia ao servidor uma requisição do tipo:
GET nome_do_arquivo?campo1=valor1&campo2=valor2&campo3=valor3 HTTP/1.1

Num web server como o Apache, rodando num computador, o processo dinâmico de geração de páginas é conseguido através da execução de programas chamados CGIs que podem ser escritos em linguagem C ou Perl e também por módulos especializados como o PHP. Concluindo, os processos de geração de páginas dinâmicas são os responsáveis pela força de todos os produtos que hoje rolam pela Internet.





HTTP - O SISTEMA DE ARQUIVOS

Como dissemos anteriormente...

"Quando uma requisição chega ao web server, ele procura em seu diretório pelo arquivo requisitado em "nome_do_arquivo", lê esse arquivo e o devolve ao browser solicitante..."

No caso de um web server, como o Apache, que roda num servidor e atende muitas requisições, as páginas escritas em HTML devem ficar gravadas em algum diretório do sistema de arquivos do computador.

Nem é preciso dizer que: um web server rodando num microcontrolador como o PIC18F4620 não pode ser comparado a um web server Apache rodando num computador, mesmo que seja num pequeno desktop.

Assim, nosso sistema de arquivos não poderá ser tão grande que não caiba dentro do microcontrolador e nem muito pequeno que não possa armazenar algumas páginas e figuras.

Por simplificação, nosso projeto propõe usar a memória de programa do PIC18F4620 para armazenar o sistema de arquivos. Isso vai limitar a quantidade de espaço de armazenamento necessário porém, vamos ganhar em velocidade de acesso, facilidade de programação e praticidade na manutenção das páginas.

As páginas necessárias para nosso projeto estão na pasta /webserver_v04/web. Nessa pasta encontram-se os arquivos "index.htm", "index.cgi" e "mchp.gif". Esses arquivos são suficientes para demonstrar o funcionamento de um web server que responde a requisições e gera páginas HTML dinamicamente, como veremos mais para frente em Execução de Comandos e Substituição de Variáveis.

Como o sistema de arquivos deve residir na memória de programa, teremos que, de alguma maneira, fazer com que a pasta /webserver_v04/web seja gravada nessa memória. Para fazer isso, há um programa utilitário fornecido pela Microchip chamado "MPFS.EXE" que se encontra na pasta "/webserver_v04/tools/MPFS.EXE".

Para formatar o sistema de arquivos siga os passos:

  1. No menu Iniciar do Windows, abra a opção Executar e digite cmd.

  2. Na tela preta do Prompt de Comandos digite cd /webserver_v04/tools e tecle Enter.

  3. Execute o programa digitando o comando:
    MPFS   /webserver_v04/web  mpfsimg.c  /c
    Isso vai gerar o arquivo mpfsimg.c na pasta atual cd /webserver_v04/tools.

  4. Edite o arquivo mpfsimg.c e, na linha 11 ou próximo dela, substitua no comando #include o nome de arquivo Compiler.h por config.h e salve em seguida.

  5. Copie o arquivo mpfsimg.c para a pasta /webserver_v04/src/.

  6. Recompile o programa e regrave o microcontrolador.

Lembre-se, o arquivo "PIC10T.hex" que se encontra na pasta "/webserver_v04/bin/PIC10T.hex" já tem o sistema de arquivos embutido e esta pronto para ser usado. A recompilação do programa só deve ser feita se você quiser modificar o conteúdo das páginas ou reconfigurar o endereço da rede.





HTTP - EXECUÇÃO DE COMANDOS

Como vimos anteriormente, o web server pode responder a uma requisição gerando páginas dinamicamente.
Uma requisição do tipo GET nome_do_arquivo?campo1=valor1&campo2=valor2&campo3=valor3 HTTP/1.1 pede ao web server para executar o programa especificado em "nome_do_arquivo" e pede ao web server que passe para esse programa os parâmetros "campo1=valor1&campo2=valor2&campo3=valor3".

Pelo fato do web server presente no Stack TCP/IP da Microchip precisar ser muito simples, algumas modificações foram feitas para adaptá-lo à realidade de um microcontrolador. Convencionou-se, então, que a requisição passada para o web server pelo GET será: "GET CMD?c1=v1&c2=v2&....cn=vn" onde:

  • CMD é um número de comando.

  • c1 a cn são números de argumentos

  • v1 a vn são os respectivos valores dos argumentos.

Além disso, o web server do Stack TCP/IP da Microchip deixa a cargo do desenvolvedor escrever uma função para detetar os parâmetros e executar a ação requerida pelos comandos CMD. Toda vez que uma requisição com mais de um argumento for passada ao web server, ele chamará a função "HTTPExecCmd()" para realizar essa tarefa.

Supondo que uma requisição     GET 0?3=12&4=x     tenha sido feita ao web server, este deve interpretar os argumentos e armazená-los na variável argv da seguinte maneira:

  • argv[0] => 0 ........... número do comando; geralmente o valor de um ACTION de um formulário
  • argv[1] => 3 ........... número do argumento; geralmente o nome de um campo de formulário
  • argv[2] => 12 .......... valor do argumento
  • argv[3] => 4 ........... número do argumento; geralmente o nome de um campo de formulário
  • argv[4] => x ........... valor do argumento

A função "HTTPExecCmd()" esta definida na linha 358 do módulo /webserver_v04/src/main.c.

Os números de comandos (CMD) devem ser definidos através macros como, por exemplo, o comando 0 (zero) esta definido com a macro #define CGI_CMD_DIGOUT (0) (ver linha 270 do módulo main.c).

Abra o arquivo main.c e estude como a função "HTTPExecCmd()" trabalha pois um exemplo vale por mil palavras.





HTTP - SUBSTITUIÇÃO DE VARIÁVEIS

Como não poderia deixar de ser, além de executar os comandos, o web server também deixa a cargo do desenvolvedor a substituição de variáveis no texto a ser devolvido ao browser quando a resposta é dinâmica. Isso é feito pela função "HTTPGetVar()" que esta definida na linha 358 do módulo /webserver_v04/src/main.c.

Toda variável deve ser definida com o uso de macros, tal como na linha 277 do do módulo main.c e podem haver até 255 variáveis diferentes.

Uma variável que deve ser substituida por um valor, deve ser referenciada pelo caracter "%".
Veja, por exemplo, o arquivo /webserver_04/web/index.cgi listado abaixo:

...1 <html>
...2 <head>
...3 <title>PIC Web Server</title>
...4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
...5 <script type="text/JavaScript">
...6 function timedRefresh(timeoutPeriod) {
...7  setTimeout("location.reload(true);",timeoutPeriod);
...8 }
...9 </script>
..10 </head>
..11 <body bgcolor="#aabbaa" onload="JavaScript:timedRefresh(3000);">
..12 <center>
..13 <h2>*** PIC Web Server V04***</h2>
..14 <h4>*** %24 ***</h4>
..15 <h4>*** Total Hits %25 ***</h4>
..16 <br><hr />
..17 <form name="f1" method="get" action="0">
..18 <table border="1" cellpadding="0" cellspacing="0" bgcolor="#779977">
..19 <tr>
..20     <td align="center"> STAT </td>
..21     <td align="center"><input type="submit" name="0" value="L1"></input></td>
..22     <td align="center"><input type="submit" name="1" value="L2"></input></td>
..23     <td align="center"><input type="submit" name="5" value="L3"></input></td>
..24     <td align="center"><input type="submit" name="6" value="L4"></input></td>
..25     <td align="center"> B1 </td>
..26     <td align="center"> B2 </td>
..27 </tr>
..28 <tr>
..29     <td bgcolor="%00">     </td>
..30     <td bgcolor="%01">     </td>
..31     <td bgcolor="%10">     </td>
..32     <td bgcolor="%11">     </td>
..33     <td bgcolor="%12">     </td>
..34     <td bgcolor="%04">     </td>
..35     <td bgcolor="%0D">     </td>
..36 </tr>
..37 </table>
..38 <br /><br /><br />
..39 <input type="submit" name="2" value="Pisca"></input>
..40 <input type="submit" name="3" value="Alterna"></input>
..41 <input type="submit" name="4" value="Limpa"></input>
..42 </form>
..43 <br /><hr />
..44 <h4>VAR_STACK_VERSION: %16</h4>
..45 <h4>VAR_STACK_DATE: %17</h4>
..46 <hr />
..47 </center>
..48 </body>
..49 </html>
..50 

Na linha 14 temos a referência %24 e a linha 15 temos %25, que correspondem respectivamente as variáveis VAR_MSG1 (0x24) e VAR_HITSCNT (0x25) definidas nas linhas 306 e 307 do módulo main.c.

Nas linhas de 29 a 35 temos as referências %00, %01, %10, %11, %12, %04 e %0D que correspondem respectivamente as variáveis VAR_LED1, VAR_LED2, VAR_LED3, VAR_LED4, VAR_DIGIN0 e VAR_DIGIN1 definidas a partir da linha 277 de main.c.

Além disso, o web server substitui um caracter por vez de cada variável, chamando a função "HTTPGetVar()" para cada caracter a ser substituido. A função "HTTPGetVar()" deve então verificar se é o início ou fim da variável entes de continuar. O trecho de código abaixo, retirado das linhas 535 a 547 do módulo main.c, exemplifica o que tentamos dizer:

   case VAR_MSG1:
     if( ref == HTTP_START_OF_VAR )
     {
        strcpy(VarString, msg001);
     }
     *val = VarString[(BYTE)ref];
     if(VarString[(BYTE)ref] == '\0')
        return HTTP_END_OF_VAR;
     else if(VarString[(BYTE)++ref] == '\0' )
        return HTTP_END_OF_VAR;
     return ref;




CONCLUSÃO

Para saber mais, é necessário abrir os arquivos que compõem o Stack TCP/IP e analisá-los com cuidado. Divirta-se...

Longe de mim esperar que essa página tenha servido para sanar totalmente suas dúvidas. Espero apenas ter acendido uma vela para iluminar a escuridão que envolvia o assunto.



Índice dos circuitos




H P S P I N

Desde 04 de Março de 2010

Atualização: 19 de Mar de 2024