Mostrando postagens com marcador PIC. Mostrar todas as postagens
Mostrando postagens com marcador PIC. Mostrar todas as postagens

segunda-feira, 1 de agosto de 2011

BodyCom: nova tecnologia de Personal Area Network

Se alguém já jogou alguma vez Metal Gear Solid no Playstation, vai se lembrar dos PAN cards, que Snake tinha que conseguir para abrir as várias portas do complexo de Shadow Moses. Talvez muitos não saibam, mas as tecnologias de rede utilizando o corpo humano estão se desenvolvendo, e uma adição a este campo foi feita estes dias pela Microchip, com a publicação em seu site de uma introdução à tecnologia batizada por eles de BodyCom.
A tecnologia foi criada para ser uma rede de curto alcance, onde o principal meio de comunicação é o corpo humano. A comunicação é iniciada quando os dispositivos estão há poucos centímetros do corpo humano, ou quando o sensor de toque for acionado.
Qual a vantagem de uma rede que só se comunica quando há uma grande proximidade? É fácil entender como uma rede wireless pode ser útil. As vantagens do BodyCom ficam por conta da segurança, em primeiro lugar. A aplicação não precisará se preocupar bastante com segurança, pois não há tanta facilidade em se "sniffar" os pacotes de uma pessoa. Por outro lado, como a comunicação é iniciada quando o usuário tocar o dispositivo específico, ele não precisa de usar as mãos para ativar um controle ou algo do gênero (bom quando você chegar em casa debaixo de chuva e com as mãos cheias de compras, não?).
Por causa da alta permissividade do corpo humano a baixas frequências, a comunicação é realizada entre 60KHz e 30MHz. O sinal é acoplado capacitivamente pelo corpo humano usando um Amplitude Shift Key (ASK) de baixa frequência.
Segundo a Microchip, o BodyCom foi desenvolvido visando os seguintes objetivos:
  1. Consumo muito baixo, principalmente do dispositivo móvel.
  2. Resposta rápida do sistema.
  3. Comunicação estável e robusta, com detecção de falha.
  4. Campo de ação limitada (para permitir a comunicação apenas quando houver o toque da pessoa no dispositivo).
  5. Custo e complexidade baixos.
Por usar acoplamento capacitivo, sinais de frequência mais baixa possuem amplitude menor, requerendo que sinais de frequência baixa possuam amplitude maior. Além disto, o dispositivo móvel, operando a bateria, deve ter o consumo bem reduzido. Isto influenciou nas escolhas de frequências de comunicação. Assim, o dispositivo móvel recebe a uma frequência de 128KHz, enquanto transmite a 8MHz.
A Microchip detalha então como podemos implementar seu sistema de comunicações em seu datasheet de 26 páginas. É um arquivo bem completo, com todos os esquemáticos e cálculos necessários. O sistema obviamente é feito para ser usado com os microcontroladores PIC.

quinta-feira, 14 de julho de 2011

MiWi: Criando redes MiWi com PIC18F4550

Depois de nossa série sobre o protocolo MiWi, chega a hora de colocar tudo em prática. Assim, vamos criar aqui um projeto de redes MiWi com o PIC. Vou selecionar o PIC18F4550, pois depois poderemos atualizar o projeto, adicionando o controle através da porta USB.
Se vocês perderam algum dos posts anteriores, podem acessá-los pela lista abaixo:
1- MiWi: Dispositivos e topologias de rede;
2- MiWi: Endereçamento;
3- MiWi: Relatórios de protocolo;
4- MiWi: Mensagens de Stack e Serviços
5- MiWi: Segurança
Como a biblioteca da Microchip para o MiWi foi desenvolvida recentemente, há ainda alguns bugs e também muitas atualizações. Ela é fornecida com o Microchip Application Libraries, e a última versão disponível agora, enquanto escrevo este artigo, é a versão de junho de 2011 (Stack MiWi 4.2). Quem estiver com sua versão desatualizada, é bom atualizar.

Hardware do projeto

Desenhamos o circuito do projeto no KiCad, que pode ser visto abaixo:


Criamos o componente MRF24J40MA, já que o KiCad não o possui. Você poderá obter todos os datasheets relacionados a este módulo no site da Microchip.
O módulo MRF24J40MA é o módulo que fará todo o trabalho de transmissão e recepção de pacotes. Você o controla via barramento SPI. Ele na verdade é um transceiver feito pela Microchip para os protocolos ZigBee ou MiWi... Mas como um transceiver, você poderá criar qualquer protocolo de comunicação com ele. O Stack MiWi não está no módulo, mas é implementado pelas bibliotecas da Microchip.
Para você ter uma visão geral dos registradores disponíveis deste módulo, procure pelo datasheet do circuito integrado principal dele, o MRF24J40. Há ainda uma versão deste módulo de maior alcance, chamado de MRF24J40MB.
A alimentação do módulo é de 3.3V. Para simplificar o desenho acima, não incluímos a fonte. Portanto, quando você for implementar este projeto, lembre-se de incluí-la.

Incluindo os arquivos do projeto

Vamos agora ao PCAD, e vamos criar um novo projeto com PICF4550. Os arquivos que devemos incluir são os arquivos abaixo:

ArquivoPasta
Console.c\Microchip Solutions\Microchip\WirelessProtocols\
MSPI.c\Microchip Solutions\Microchip\WirelessProtocols\
SymbolTime.c\Microchip Solutions\Microchip\WirelessProtocols\
MiWi.c\Microchip Solutions\Microchip\WirelessProtocols\MiWi\
MRF24J40.c\Microchip Solutions\Microchip\Transceivers\MRF24J40\
MiWi.c\Microchip Solutions\Microchip\WirelessProtocols\MiWi\

Os cabeçalhos relacionados com estes arquivos podem ser adicionados para facilitar a visualização. Por fim, devemos copiar três arquivos que serão necessários para o projeto: ConfigApp.h, HardwareProfile.h, SystemProfile.h. Para o projeto de um dispositivo End Device, nós vamos copiar estes arquivos do diretório Microchip Solutions\MiWi DE Demos\Node 2. Para Coordinators, devemos copiar estes arquivos do diretório Microchip Solutions\MiWi DE Demos\Node 1. São eles que conterão as configurações de rede do projeto, bem como o esquema de ligação deste projeto.
Assim que copiar estes arquivos para a pasta do projeto, inclua-os. Fazendo isto, vamos editar o arquivo HardwareProfile.h, para que ele reflita o nosso esquemático:
#ifndef _HARDWARE_PROFILE_H
    #define _HARDWARE_PROFILE_H
    
    #include "GenericTypeDefs.h"
    #include "ConfigApp.h"
    #include <p18cxxx.h>
    
    #define CLOCK_FREQ          48000000
    
    #define RFIF                INTCON3bits.INT2IF
    #define RFIE                INTCON3bits.INT2IE
    
    #define PHY_CS              LATCbits.LATC2
    #define PHY_CS_TRIS         TRISCbits.TRISC2
    #define RF_INT_PIN          PORTBbits.RB2
    #define RF_INT_TRIS         TRISBbits.TRISB2
    
    #define SPI_SDI             PORTBbits.RB0               
    #define SDI_TRIS            TRISBbits.TRISB0
    #define SPI_SDO             LATCbits.LATC7
    #define SDO_TRIS            TRISCbits.TRISC7
    #define SPI_SCK             LATBbits.LATB1
    #define SCK_TRIS            TRICBbits.TRISB1

    #define PHY_RESETn          LATCbits.LATC0
    #define PHY_RESETn_TRIS     TRISCbits.TRISC0
    #define PHY_WAKE            LATCbits.LATC1
    #define PHY_WAKE_TRIS       TRISCbits.TRISC1
    
    #define LED_1               PORTEbits.RE0
    #define LED_1_TRIS          TRISEbits.TRISE0
    #define LED_2               PORTEbits.RE1
    #define LED_2_TRIS          TRISEbits.TRISE1
    #define LED_3               PORTEbits.RE2
    #define LED_3_TRIS          TRISEbits.TRISE2

    #define TMRL                TMR0L
    
    #define TOOGLE(c)           c = ~c

#endif
Aqui nós definimos todas as ligações feitas com o módulo MRF24J40MA. Se alguma destas definições faltar, o compilador gerará erros. O clock e o registrador do timer usado também devem ser definidos aqui, para que o Stack MiWi possa fazer as temporizações corretamente. Portanto, você não poderá usar este timer.

Configurando o projeto

A configuração do projeto é feita no arquivo ConfigApp.h. Veja quais opções você poderá selecionar neste arquivo:

ConfiguraçãoDescrição
ENABLE_CONSOLEHabilita a impressão de mensagens pela serial do microcontrolador. Comente esta definição, para desabilitá-la.
SIMPLE_EXAMPLEDesabilita todas as funções mais avançadas do Stack. Insira esta definição no projeto, para habilitá-la.
ENABLE_NETWORK_FREEZERGrava todos os dados mais importantes de uma conexão em memória não volátil, para rápido restabelecimento da rede. É necessário ter esta memória no projeto.
HARDWARE_SPIUtiliza o módulo SPI nativo do microcontrolador para comunicação. Comentando-se esta definição, o Stack tentará emular uma SPI. Recomendável usar o SPI nativo.
PROTOCOL_P2PHabilita a rede P2P. Não pode ser definido juntamente com PROTOCOL_MIWI
PROTOCOL_MIWIHabilita a rede MiWi Mesh. Não pode ser definido juntamente com PROTOCOL_P2P
NWK_ROLE_COORDINATORDefine o dispositivo como Coordinator. Não pode ser definido juntamente com NWK_ROLE_END_DEVICE.
NWK_ROLE_END_DEVICEDefine o dispositivo como End Device. Não pode ser definido juntamente com NWK_ROLE_COORDINATOR.
MRF24J40Define que o módulo MRF24J40MA de 2.4GHz será usado. Não pode ser definido junto com outros transceivers.
MRF49XADefine que o módulo subGHz MRF49XA da Microchip será usado. Não pode ser definido junto com outros transceivers.
MRF89XADefine que o módulo subGHz MRF89XA da Microchip será usado. Não pode ser definido junto com outros transceivers.
MY_ADDRESS_LENGTHDefine o tamanho do endereço permanente do nó, em bytes.
EUI_0 - EUI_7Esta é a definição do endereço EUI do módulo. Cada dígito é um byte do endereço.
TX_BUFFER_SIZEDefine o tamanho do buffer de transmissão.
RX_BUFFER_SIZEDefine o tamanho do buffer de recepção.
MY_PAN_IDDefine o PANID do dispositivo.
ADDITIONAL_NODE_ID_SIZEDefine o tamanho dos dados adicionais que serão anexados à requisição P2P. Estas são informações que os dispositivos querem compartilhar com outros pontos na conexão. Estes dados serão definidos pela aplicação.
CONNECTION_SIZEDefine o máximo de conexões P2P que este dispositivo permite ao mesmo tempo.
TARGET_SMALLRemove algumas funcionalidades para diminuir o código gerado.
ENABLE_PA_LNAHabilita o amplificador de potência em transceivers que o possuem.
ENABLE_HAND_SHAKEHabilita o hand-shake antes da comunicação. Sem isto, os transceivers RF só poderão fazer broadcast, ou possuir o endereço de destino programado no firmware para comunicação com um dispositivo.
ENABLE_SLEEPPermite que o dispositivo vá para o modo sleep e volte dele.
ENABLE_ED_SCANHabilita a detecção de energia, para descobrir qual o canal que possui menos ruído.
ENABLE_ACTIVE_SCANHabilita o Active Scan, para detectar as conexões atualmente existentes.
ENABLE_SECURITYHabilita a encriptação de dados na transmissão.
ENABLE_INDIRECT_MESSAGEFará com que o dispositivo guarde mensagens para dispositivos que estão em modo sleep, até que eles retornem ao funcionamento normal.
ENABLE_BROADCASTFará com que o dispositivo envie broadcasts aos dispositivos em modo sleep, até que eles retornem ao funcionamento normal.
RFD_WAKEUP_INTERVALDefine o intervalo de wakeup para dispositivos RFD em segundos. Esta definição no entanto não é utilizada pelos dispositivos RFD, mas pelos dispositivos FFD, para calcular seus vários timeouts.
ENABLE_FREQUENCY_AGILITYFaz com que o dispositivo possa mudar de canal, quando houver uma súbita mudança no ruído.
Alterando o valor destas definições, você poderá configurar seu projeto de acordo com suas necessidades. É sempre bom dar uma olhada nestas configurações depois de copiar os arquivos.

Arquivo principal do projeto

No arquivo principal, você precisará definir algumas coisas também. Vamos ao esqueleto básico deste arquivo:
#include <stdio.h>
#include "GenericTypeDefs.h"
#include "Compiler.h"
#include "WirelessProtocols/MSPI.h"
#include "WirelessProtocols/MCHP_API.h"

#include "HardwareProfile.h"

#if ADDITIONAL_NODE_ID_SIZE > 0
 BYTE AdditionalNodeID[ADDITIONAL_NODE_ID_SIZE] = {0x01};
#endif

BYTE myChannel = 11;

#pragma config FOSC=HSPLL_HS
#pragma config CPUDIV=OSC1_PLL2
#pragma config PLLDIV=5
#pragma config USBDIV=2
#pragma config CCP2MX=ON
#pragma config WDT=OFF
#pragma config WDTPS=32768
#pragma config MCLRE=ON
#pragma config LVP=OFF
#pragma config VREGEN=ON
#pragma config IESO=OFF
#pragma config PWRT=ON
#pragma config BOR=OFF
#pragma config CP0=OFF
#pragma config CP1=OFF
#pragma config CP2=OFF
#pragma config CP3=OFF
#pragma config CPB=OFF
#pragma config CPD=OFF
#pragma config WRT0=OFF
#pragma config WRT1=OFF
#pragma config WRT2=OFF
#pragma config WRT3=OFF
#pragma config WRTB=OFF
#pragma config WRTC=OFF
#pragma config WRTD=OFF
#pragma config EBTR0=OFF
#pragma config EBTR1=OFF
#pragma config EBTR2=OFF
#pragma config EBTR3=OFF
#pragma config EBTRB=OFF
#pragma config DEBUG=ON
#pragma config XINST=ON

void UserInterruptHandler(void)
{
}              

void main(void)
{
 BYTE i=0xFF;
 
 RCON = 0x80;           // Habilita prioridades para interrupções
 ADCON0 = 0x00;
 ADCON1 = 0x0F;       // PORTA só como entradas e saídas digitais.
 CMCON = 0x07;        // Comparadores desligados.
 TRISA = 0xFF;
 TRISB = 0x05;
 TRISC = 0x00;
 PORTC = 0x06;
 TRISD = 0x00;
 PORTD = 0x00;
 TRISE = 0x07;
 PORTE = 0xFF;
 INTCON = 0x00;
 INTCON2 = 0x00;
 INTCON3 = 0x00;
 // Configura o módulo SPI
 SSPSTAT = 0xC0;
 SSPCON1 = 0x20;
 
 INTCONbits.GIEH = 1;
 RFIF = 0;               // Limpa o interrupt flag do chip.
 RFIE = 1;
 INTCON2bits.INTEDG2 = 0;

 MiApp_ProtocolInit(FALSE);
 if(MiApp_SetChannel(myChannel) == FALSE)
 {
  return;
 }
 MiApp_ConnectionMode(ENABLE_ALL_CONN);
 while((i = MiApp_EstablishConnection(0xFF, CONN_MODE_DIRECT)) == 0xFF);
 #ifdef ENABLE_DUMP
        DumpConnection(0xFF);
 #endif
 LED_2 = 1;
 for(;;)
 {
  if(MiApp_MessageAvailable())
  {
   // Aqui tratamos a mensagem recebida.
   MiApp_DiscardMessage();
  }
 }
}
O código acima é de um arquivo prinpipal para o projeto de dispositivo End Device. A diferença entre o End Device e o Coordinator é apenas a função main. Duas definições devem ser feitas no arquivo principal. A primeira é a definição da variável AdditionalNodeID, conforme pode ser visto acima. Os arquivos do Stack MiWi usam esta variável, portanto é necessário pelo menos definí-la. A segunda definição é a da função UserInterruptHandler. Esta função é chamada depois que a interrupção do MiWi for executada, e precisa também ser declarada, mesmo que nada seja feito ali.
Além destas declarações, você deverá ainda configurar o módulo SPI e habilitar a interrupção que você definiu para o módulo MRF24J40MA (o Stack não faz isto como acontece em outras bibliotecas Microchip). Feito isto, seu projeto está pronto para funcionar.
As diferenças existentes neste arquivo acima, para o projeto do Coordinator são poucas. Compare com o código abaixo (do Coordinator):
MiApp_ProtocolInit(FALSE);
 if(MiApp_SetChannel(myChannel) == FALSE)
 {
  return;
 }
 MiApp_ConnectionMode(ENABLE_ALL_CONN);
 MiApp_StartConnection(START_CONN_DIRECT, 10, 0);
 #ifdef ENABLE_DUMP
        DumpConnection(0xFF);
 #endif
 LED_2 = 1;
 for(;;)
 {
  if(MiApp_MessageAvailable())
  {
   MiApp_DiscardMessage();
  }
 }
Pode-se observar que o End Device usa a função MiApp_EstablishConnection, enquanto o Coordinator usa a função MiApp_StartConnection. Esta função inicializa a rede MiWi, e somente com uma rede estabelecida, é que os dispositivos End Device podem estabelecer conexões.

Conclusão

Terminamos aqui com a criação de um projeto MiWi. No nosso próximo post, iremos desvendar um pouco mais as rotinas usadas pelo Stack, para implementar a comunicação via protocolo MiWi, comparando com o que já foi escrito sobre o protocolo, aqui.

segunda-feira, 21 de março de 2011

Páginas dinâmicas e AJAX no PIC.

Como eu havia dito em outro post, vou continuar nossa matéria sobre servidores Web no PIC. Nesta última parte, estarei descrevendo como você poderá inserir variáveis dinâmicas em suas páginas, bem como usar AJAX para atualizá-las.
Uma coisa que critico muito na plataforma da Microchip é a falta de documentação sobre este assunto. No entanto, se formos observar, há uma falta de documentação em toda nossa área. Espero assim preencher uma lacuna existente em nossa área.
Bem, se vocês acompanharam nossa última postagem, estarão com um servidor Web pronto para usar. Devemos agora criar nossas variáveis dinâmicas.

Criando variáveis dinâmicas na página Web

A Microchip definiu uma forma para a criação de variáveis dinâmicas em páginas Web. O padrão adotado é colocar o nome da variável entre dois '~'. Assim, poderíamos criar uma página web com o seguinte código HTML:
<html>
<head>
<title>Contagem de itens</title>
</head>
<body>
<p>Até o momento foram produzidos ~varItens~ itens.</p>
</body>
</html>
Criamos então aqui, uma página que irá mostrar o valor de uma variável chamada varItens. Esta página faz parte de um projeto que irá contar a quantidade de certos itens produzidos em uma indústria. Você deverá salvar esta página em seu diretório de páginas HTML, e então através do programa MPFS.exe (localizado na pasta [Raiz]\Microchip Solutions\Microchip\TCPIP Stack\Utilities), você irá gerar o código para ser incluído em seu programa. Se tudo ocorrer bem, o programa gerará o arquivo HTTPPrint.h com um novo protótipo de função:
void HTTPPrint_varItens(void);
Esta é a função que você deverá implementar para que sua variável dinâmica seja corretamente impressa no browser.
 
Implementando a função de impressão de variáveis dinâmicas
 
Vamos então criar uma função que imprime o valor de uma variável inteira chamada de quantItens. O nome da variável que escolhemos no código HTML não nos obriga a adotá-lo em parte alguma do código,pois escolhemos ali o nome da nossa função de impressão.
Assim, nossa função se chamará HTTPPrint_varItens. Eis o código-fonte desta função:
void HTTPPrint_varItens(void) 
{
   char tmpBuff[6];
   sprintf(tmpBuff, "%d", quantItens);
   TCPPutString(sktHTTP, (BYTE*)tmpBuff);
}
O que fiz foi basicamente converter o valor inteiro contido na variável quantItens para uma string, e usando a função TCPPutString, enviamos os dados para o browser que estiver visualizando a página. Tudo que for enviado através da função TCPPutString será exibido na posição que definimos no código HTML pela variável ~varItens~. Assim, não imprimimos apenas valores inteiros, mas também strings com mensagens para nossos usuários, códigos HTML para formatação de mensagens, etc...
 
Usando AJAX
 
Como implementar o AJAX em um servidor com PIC? A tarefa não é tão complexa assim. Imaginemos então que nosso projeto tenha que monitorar em tempo real a quantidade de itens na linha de produção, e que o browser deverá atualizar esta leitura sempre. Como implementar isto?
O primeiro passo será criar um arquivo que a página Web que criamos deverá buscar sempre com novas leituras. Este arquivo deverá ser um arquivo XML, e seu código pode ser visto abaixo:
<leitura> 
<contagem>~varItens~</contagem>
</leitura>
Observem que o elemento raiz é chamado de leitura, e os itens estão dentro de uma tag que chamamos de contagem.
Aqui neste arquivo, que vamos chamar de leitura.xml, nós colocamos novamente a impressão da variável varItens, que irá chamar a função que definimos acima. Novamente usamos o arquivo MPSF.exe para converter estes arquivos para um formato que possa ser utilizado pelo compilador.
Precisamos agora de algum mecanismo para buscar este arquivo no servidor e atualizar nossa página. Quem trabalha com Web poderia muito bem criar algumas funções em Javascript para usar o AJAX. No entanto, a Microchip já disponibiliza um arquivo Javascript que implementa o AJAX, e já faz o trabalho de atualizar periodicamente uma área de sua página Web.
O grande detalhe aqui é que a Microchip não divulgou muito isto. Não é um arquivo que está separado em uma pasta, somente esperando para ser usado. Este arquivo na verdade faz parte de um de seus exemplos, e pode ser encontrado na pasta [Raiz]\Microchip Solutions\TCPIP Demo App\WebPages2. O nome deste arquivo é mchp.js. Você deverá copiar este arquivo para sua pasta de páginas Web, e fazer algumas modificações no arquivo HTML que apresentamos acima:
<html>
<head>
<title>Contagem de itens</title>
<script type="text/javascript" src="mchp.js"></script>
<script type="text/javascript">
/* <![CDATA[ */
function displayItens(r)
{
  document.getElementById('leitura').innerHTML = getXMLValue(r, 'contagem');
}
function init()
{
  newAJAXCommand('leitura.xml', displayItens, true);
}
/* ]]> */
</script>
</head>
<body onload="init();">
<p>Até o momento foram produzidos <span id="leitura">~varItens~</span> itens.</p>
</body>
</html>
Em primeiro lugar, usamos um elemento SPAN para conter o valor a ser atualizado. Desta forma, limitamos a área onde será atualizada. É importante definir um ID para este SPAN, pois através deste localizaremos este elemento.
Definimos também a propriedade onload do elemento BODY. Quando a página carregar, ela irá invocar a função nesta propriedade. Vemos também que o código agora inclui dois scripts javascript: um é nosso arquivo mhcp.js, o outro é o nosso código.
Este nosso código simplesmente invoca a função newAJAXCommand, que faz todo o trabalho. O primeiro parâmetro desta função é o nome do arquivo que ela irá buscar no servidor. No nosso caso, é o arquivo leitura.xml que criamos anteriormente. O segundo parâmetro é o nome da função que será chamada, quando o arquivo for recebido. Criamos a função displayItens para este propósito, e por isto fornecemos este nome para a função. O terceiro parâmetro indica se você deseja repetir este chamado periodicamente, ou se este será um chamado apenas. No nosso caso, como desejamos uma leitura em tempo real, deixamos o valor como true. Esta função permite ainda um quarto parâmetro, que não vamos usar. Este parâmetro te permite enviar outras variáveis via POST para seu servidor.
Enviando o comando, o seu servidor responderá, enviando o arquivo leitura.xml. Quando recebermos o arquivo, ele será processado pela função que você passou como parâmetro para a função newAJAXCommand. Ela deve ter um parâmetro, que é o objeto XML que recebemos. Assim, para obtermos um item deste objeto, usamos a função getXMLValue. Ela recebe dois parâmetros. O primeiro, é o objeto XML que desejamos processar. Passe o objeto que recebemos como resposta. O segundo parâmetro é a tag no arquivo XML, que desejamos obter do arquivo. Como vocês podem se lembrar, colocamos a leitura dentro de uma tag contagem. Assim, passamos isto como parâmetro. Atribuímos então este valor à propriedade innerHTML do nosso SPAN, e pronto! Nosso valor estará sendo atualizado o tempo inteiro.
 
Conclusão
 
Temos então nosso servidor Web no PIC, utilizando variáveis dinâmicas e AJAX. A partir deste exemplo muitas outras adaptações poderão ser feitas, para atender aos requerimentos de cada projeto.

terça-feira, 31 de agosto de 2010

Criando um servidor Web no PIC

Olá, pessoal. Muito tempo sem postar por aqui...
Bem, estive ocupado em meu serviço tentando descobrir como criar um servidor Web usando os microcontroladores da família PIC. O grande lance destes microcontroladores é que a Microchip já fornece gratuitamente as bibliotecas necessárias para fazer isto. A questão é que não é muito simples de se configurar, sem muita documentação sobre isto. Por isto resolvi fazer uma descrição sobre este tema aqui.

Preparando terreno

Para que todos possam entender como se configura este projeto a partir do zero, resolvi detalhar todo o processo a partir do download dos arquivos necessários, no site da Microchip.
Assim, o primeiro passo é fazer o download, para quem ainda não fez, do MPLAB, que é a IDE para desenvolvimento de projetos no PIC. Para mim, este é um passo fundamental, já que através do MPLAB, você configura e depura projetos com o PIC muito facilmente. O link para o download do MPLAB (última versão), está aqui:
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en019469&part=SW007002

Mas este não será o único download no site da Microchip. É importante também fazer o download de algum compilador C deste fabricante. Como este compilador dependerá do microcontrolador que você irá usar, então haverá escolhas diferentes aqui. Como exemplo, irei usar o compilador C18. A página abaixo possui um link para os vários compiladores disponíveis:
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en534868&page=wwwCompilers

Pode-se fazer o download da versão lite destes compiladores, que são versões que não usam todas as otimizações do compilador versão full. Para mim está de bom tamanho para fazer este tipo de desenvolvimento, já que estes compiladores costumam ser muito caros para hobistas.
Tendo feito o download do compilador, resta agora fazer o download das bibliotecas da Microchip para TCP/IP e outros. Isto pode ser feito no link abaixo:
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2680&dDocName=en547784

Muito bem, terminamos com as etapas de downloads... Agora vamos para a parte de instalação, que não é muito complicada. Só para garantir que tudo dará certo, vamos instalar o MPLAB primeiro, o C18 depois e por fim as bibliotecas da Microchip. Não tenho nenhuma observação a fazer aqui, embora eu recomende que deixe que as instalações sejam feitas na raiz da unidade C, para diminuir o caminho a ser incluido nos projetos. Agora estamos prontos para iniciar nosso projeto.

Iniciando o projeto

Quando procurei saber como iniciar um projeto de servidor HTTP no PIC, encontrei várias sugestões me indicando o projeto TCPIP Demo App, que é instalado junto com as bibliotecas da Microchip. Pessoalmente não gostei muito disto, pois o projeto por se tratar de uma demonstração, foi feito para incluir vários tipos de demonstração. Acho que não é necessário copiar todos os arquivos desta pasta. Os arquivos que são necessários para o projeto são os arquivos TCPIPConfig.h e HardwareProfile.h. Através destes arquivos, você poderá configurar seu projeto. Você precisará mudar manualmente o arquivo HardwareProfile.h.
Eu por exemplo tenho aqui uma placa Explorer16BR, da Mosaico. Todo mundo poderia achar que pelo fato de haver uma definição EXPLORER_16 neste arquivo, bastaria habilitá-la para que tudo funcione perfeitamente, certo? Bem, a verdade é que a Mosaico andou alterando alguns pinos nesta placa, e entre eles está justamente o pino de habilitação do Driver Ethernet. Portanto, se alguém estiver usando esta placa, procure pela definição
#else // SPI1 for all other processors
 #define ENC_CS_TRIS (TRISDbits.TRISD14)
 #define ENC_CS_IO (LATDbits.LATD14)
E altere para:
#else // SPI1 for all other processors
 #define ENC_CS_TRIS (TRISCbits.TRISC4)
 #define ENC_CS_IO (LATCbits.LATC4)
Há também a opção de se criar o arquivo do zero. Neste caso, a pessoa deve criar algumas definições obrigatórias neste arquivo, como está abaixo:
#ifndef __HARDWARE_PROFILE_H
#define __HARDWARE_PROFILE_H

#include "GenericTypeDefs.h"
#include "Compiler.h"

// Define os fusíveis de configuração (apenas uma vez)
#if defined(THIS_IS_STACK_APPLICATION)
 #if defined(__18CXX)
  #if defined(__EXTENDED18__)
   #pragma config XINST=ON
  #elif !defined(HI_TECH_C)
   #pragma config XINST=OFF
  #endif
  
  #pragma config WDT=OFF, FOSC2=ON, FOSC=HSPLL, ETHLED=ON

 #endif
#endif // Previne a definição de mais de uma vez dos mesmos fusíveis

// Valor da frequência de clock.
// Este valor é usado para calcular o valor de Tick Counter
#define GetSystemClock()      (41666667ul)      // Hz
#define GetInstructionClock() (GetSystemClock()/4)
#define GetPeripheralClock()  GetInstructionClock()
No caso, as definições que devem ser feitas são as definições de clock do sistema. Você precisa verificar qual as configurações de frequência você está usando, para criar estas definições.
Feito isto, podemos agora configurar a parte de TCP/IP. Para isto, a Microchip desenvolveu um executável que faz toda a configuração para nós. Este executável se encontra na pasta:
[Diretório instalado]\Microchip Solutions\Microchip\TCPIP Stack\Utilities

E o executável se chama TCPIPConfig.exe. Execute ele e forneça o caminho para seu projeto. Não se esqueça de marcar a opção Show Advanced Settings, para podermos configurar nosso projeto nos mínimos detalhes. Na próxima tela teremos várias opções de projeto:



Selecionaremos apenas Web Server. Selecionando Next, teremos várias opções de códigos de exemplo. Não iremos selecionar nenhuma destas opções aqui. Clicando-se novamente em Next, teremos uma seleção de módulos:


Aqui selecionamos ICMP Client, ICMP Server e o NetBIOS Name Service. Os dois ICMP são necessários para que a aplicação possa fazer PING em computadores e para que ela possa também responder a este comando, muito útil para verificar a conectividade de dispositivos. O NetBIOS Name Service será importante para que você possa acessar seu dispositivo pelo Browser digitando o nome deste dispositivo na barra de endereços.
É na próxima tela que você configurará o endereço IP de seu projeto, nome do dispositivo, máscara de sub-rede, endereço MAC, etc...


Configure de forma que o dispositivo seja reconhecido em sua rede. Assim que isto for feito, clique em Next, e você será perguntado sobre qual biblioteca HTTP você pretende usar em seu projeto.


Vamos usar o HTTP2, pois ele traz inúmeras vantagens, como a possibilidade de usar métodos POST em formulários e o uso de variáveis dinâmicas. Feito isto, na próxima tela temos a seleção de configurações da biblioteca:


Como é um teste simples, vamos habilitar apenas os métodos POST. A página inicial será a página index.htm, e permitiremos apenas duas conecções por vez. Na próxima tela temos a seleção do sistema de arquivos, mas o HTTP2 da Microchip possibilita apenas o uso do sistema de arquivos MPFS2. Depois desta seleção, podemos escolher onde os arquivos serão gravados. No meu projeto, preferi salvar os arquivos dentro da Flash de programa do microcontrolador, que tinha espaço suficiente para todos os arquivos. Outras opções são:


O próximo passo é o mais complicado. Neste passo, você determinará a quantidade de memória usada para cada conecção.


Como não usamos nenhum outro tipo de conecção, determinaremos memória apenas para o HTTP_SERVER. Verifique se os outros tipos de Sockets estão com seu Count diferente de 0. O Count determina quantas conecções daquele tipo existirão. Como só temos duas conecções HTTP, só neste tipo de Socket é que deveremos ter um valor diferente de 0. A próxima tela permite você escolher quantas conecções UDP sua aplicação terá, e finalmente você poderá concluir esta configuração. O programa vai sobreescrever os valores disponíveis no seu arquivo TCPIPConfig.h. Estamos prontos para dar início a nosso projeto.

Arquivo principal do projeto

O arquivo principal do projeto deverá seguir alguns procedimentos básicos. O primeiro é que ele deve trazer a seguinte definição:
#define THIS_IS_STACK_APPLICATION
Isto indicará ao compilador qual é o arquivo principal. O segundo passo é que sua aplicação deverá definir uma instância da estrutura APP_CONFIG chamada de AppConfig. Esta estrutura contém as configurações de TCP/IP do projeto. Adicione a linha:
APP_CONFIG AppConfig;
Esta será uma variável global.
Nós temos também que definir o tratamento de interrupções de tempo, pois o Stack TCP/IP da Microchip usa os timers para o gerador de ticks. O tratamento de interrupção pode ser feito através do seguinte código:
#pragma interruptlow LowISR
void LowISR(void)
{
 TickUpdate();
}
 
#pragma interruptlow HighISR
void HighISR(void)
{
}
 
#pragma code lowVector=0x18
void LowVector(void){_asm goto LowISR _endasm}
#pragma code highVector=0x8
void HighVector(void){_asm goto HighISR _endasm}
#pragma code // Volta para a seção padrão de código
Para um servidor HTTP, as funções de tratamento de mensagens GET e POST (quando habilitadas) serão chamadas. Mas as bibliotecas da Microchip deixam a definição destas funções a cargo da aplicação, para que o programador trate destas mensagens da forma que ele deseja. Como estamos no início do projeto e a intenção é apenas gerar um servidor sem formulários no começo, vamos definir funções que não fazem nenhum tratamento:
HTTP_IO_RESULT HTTPExecuteGet(void)
{
  return HTTP_IO_DONE;
}

HTTP_IO_RESULT HTTPExecutePost(void) 
{
  return HTTP_IO_DONE;
}
Retornando HTTP_IO_DONE em ambas funções, dizemos às bibliotecas TCP/IP da microchip que o tratamento destas funções foi bem-sucedido, o que não deixa de ser verdade.
Como antes criamos uma instância da estrutura APP_CONFIG, precisamos agora preenchê-la com os dados necessários à aplicação. Os detalhes desta função podem ser vistos no programa de demonstração da Microchip, mas para facilitar esta tarefa, estarei colocando a função que criei aqui:
static ROM BYTE SerializedMACAddress[6] = {MY_DEFAULT_MAC_BYTE1, MY_DEFAULT_MAC_BYTE2, MY_DEFAULT_MAC_BYTE3, MY_DEFAULT_MAC_BYTE4, MY_DEFAULT_MAC_BYTE5, MY_DEFAULT_MAC_BYTE6};

static void InitAppConfig(void)
{
 AppConfig.Flags.bIsDHCPEnabled = TRUE;
 AppConfig.Flags.bInConfigMode = TRUE;
 memcpypgm2ram((void*)&AppConfig.MyMACAddr, (ROM void*)SerializedMACAddress, sizeof(AppConfig.MyMACAddr));
 AppConfig.MyIPAddr.Val = MY_DEFAULT_IP_ADDR_BYTE1 |  MY_DEFAULT_IP_ADDR_BYTE2<<8ul | MY_DEFAULT_IP_ADDR_BYTE3<<16ul | MY_DEFAULT_IP_ADDR_BYTE4<<24ul; AppConfig.DefaultIPAddr.Val = AppConfig.MyIPAddr.Val; AppConfig.MyMask.Val = MY_DEFAULT_MASK_BYTE1 | MY_DEFAULT_MASK_BYTE2<<8ul | MY_DEFAULT_MASK_BYTE3<<16ul | MY_DEFAULT_MASK_BYTE4<<24ul; AppConfig.DefaultMask.Val = AppConfig.MyMask.Val; AppConfig.MyGateway.Val = MY_DEFAULT_GATE_BYTE1 | MY_DEFAULT_GATE_BYTE2<<8ul | MY_DEFAULT_GATE_BYTE3<<16ul | MY_DEFAULT_GATE_BYTE4<<24ul; AppConfig.PrimaryDNSServer.Val = MY_DEFAULT_PRIMARY_DNS_BYTE1 | MY_DEFAULT_PRIMARY_DNS_BYTE2<<8ul | MY_DEFAULT_PRIMARY_DNS_BYTE3<<16ul | MY_DEFAULT_PRIMARY_DNS_BYTE4<<24ul; AppConfig.SecondaryDNSServer.Val = MY_DEFAULT_SECONDARY_DNS_BYTE1 | MY_DEFAULT_SECONDARY_DNS_BYTE2<<8ul | MY_DEFAULT_SECONDARY_DNS_BYTE3<<16ul | MY_DEFAULT_SECONDARY_DNS_BYTE4<<24ul; memcpypgm2ram(AppConfig.NetBIOSName, (ROM void*)MY_DEFAULT_HOST_NAME, 16); FormatNetBIOSName(AppConfig.NetBIOSName);
}
Por fim, nossa função main. Aqui há algumas observações a se fazer... As funções de inicialização do protocolo devem seguir uma ordem para serem executadas, pois do contrário o nosso servidor não funcionará. A ordem é a seguinte:
InitAppConfig();
TickInit();
MPFSInit();
StackInit();
O loop principal de seu programa deve executar estas duas funções:
StackTask();
StackApplications();
São estas as funções que devem ser usadas no seu projeto.

Configurando o projeto

Devemos incluir os arquivos para o projeto. Antes disto, devemos configurar o projeto para que inclua os cabeçalhos da Microchip na hora de compilar. Vamos ao menu Project, Build Options... Em Directories, clicando no menu Drop Down, selecionamos a opção Include Search Path. Vamos incluir aqui o diretório do projeto ".", o diretório de includes da Microchip Solutions, e o diretório de includes do C18. Em Library Search Path incluiremos também o diretório de livrarias do C18. Feito isto, estamos prontos para incluir os arquivos do projeto.
Para compilar o projeto, precisamos incluir os seguintes arquivos: ARP.c, Delay.c, DNS.c, ENC28J60.c, Helpers.c, HTTP2.c, ICMP.c, IP.c, MPFS2.c, NBNS.c, StackTsk.c, TCP.c, Tick.c, UDP.c. Agora nos resta criar os arquivos html para o servidor web.

Arquivos do servidor

Na sua pasta de projeto, crie uma pasta para guardar os arquivos html do servidor. É importante criar o arquivo index.htm que nós colocamos antes como o arquivo inicial do servidor. Neste primeiro passo, vamos criar apenas arquivos estáticos.
Assim que os arquivos forem criados, poderemos voltar na pasta Utilities onde encontramos o executável de configuração de TCP/IP. Agora vamos executar o programa MPFS2.exe.


Este programa irá converter os arquivos da pasta que você indicar para o sistema de arquivos MPFS. Como desejamos gravar na memória de programa do PIC, iremos converter nossos arquivos em um arquivo .c, que deverá ser incluído também no projeto. Assim que você clicar Generate, dois arquivos serão criados na pasta de projeto que você especificou. Depois disto, inclua o arquivo HTTPPrint.h que o programa gerar.
Pronto! Nosso projeto está completo e poderá ser compilado.

Voltaremos a falar sobre este tema posteriormente, explicando como usar formulários e variáveis dinâmicas. Por fim, ensinaremos a usar AJAX com nosso servidor web.

Você também poderá gostar de