quinta-feira, 23 de agosto de 2012

Explorando o NeonMika.Webserver - Parte 3

   Nesta ultima parte sobre o NeonMika.Webserver mostraremos uma opção simples de configuração da placa reunindo tudo que já foi falado antes com relação ao Webserver e as opções de leitura e escrita no cartão microSD. Também será avaliado as opções de requisição XML que existem no servidor através de um pequeno programa simples que eu fiz.

Requisição de Login

        Tive fazer algumas alterações na parte de requisição de login. Se você teve a oportunidade de testar o código que disponibilizei na segunda etapa desses artigos sobre o NeonMika.Webserver certamente notou que uma vez o cliente conectado na placa a conexão nunca mais seria fechada; e mais ainda, todo e qualquer cliente poderia acessá-la sem necessitar de uma nova requisição de login.

        Naturalmente isso está errado, os clientes não podem se conectar indiscriminadamente e alguma espécie de Timeout tem que existir na requisição do cliente para que automaticamente seja removido por inatividade.
        Para compreender melhor esse processo fiz um pequeno esquema de como funciona a nova requisição de login:


        Uma vez requisitada uma página de HTML o código verifica se o cliente (o IP de onde partiu a requisição) já esta na lista interna de clientes, caso não esteja envia a página de login e armazena a página requisitada na memória para enviá-la depois de um login bem sucedido.

        Na página de login o usuário deve informar o nome de usuário e senha através de um formulário que será enviado pelo método de POST contendo ao menos estes dois objetos: "username" e "password". Uma vez logado corretamente esse cliente é incluído na lista de clientes com um tempo de vida da sua conexão e em seguida a página requisitada será aberta, se está página é nula ou vazia é carregada a página "index.html".

        O usuário pode navegar em todas as página disponíveis no servidor e pode também ter acesso aos outros tipos de resposta do servidor como XMLResponse e JSONResponse. Tova vez que um acesso desse tipo é feito o tempo de vida da conexão do cliente é regenerado ao valor pré-definido nas configurações.

        O tempo de vida da conexão é automaticamente decrementado numa função periódica independente. Ao atingir o valor zero o cliente é removido da lista e uma nova conexão é necessária. Dessa forma o servidor web já tem um login mais seguro.

Configurações da conexão via HTML

        A configuração do IP foi uma união da segunda parte dessa sequência de artigos sobre o NeonMika.Webserver com o artigo sobre escrita de configuração no cartão microSD. Fui necessária uma nova função de construção do servidor onde os dados seriam obtidos com base num arquivo gravado no cartão microSD.
        Infelizmente a universalidade do código se foi, ou seja, foi necessário uma página de html vinculada a uma função de código. Também utilizando o método POST a requisição de login foi adicionada como uma requisição XML (embora a resposta não seja bem XML). Dessa forma as requirições da página "http://ipnetduino/config" são enviadas para a a função "SetNewConfig" em Server.cs.

public Server(OutputPort ledPort, int portNumber = 80)
{

    Configuration config = new Configuration(Settings.CONFIG_FILE);
                       

    var interf = NetworkInterface.GetAllNetworkInterfaces()[0];
    if (config.GetConfigurationOf("dhcp", Configuration.ConfigMode.Append, "false") == "true")
    {
        interf.EnableDhcp();
        interf.RenewDhcpLease();
    }
    else
    {
        //New to fix 
        string ipAddress = config.GetConfigurationOf("ip", Configuration.ConfigMode.Append, "10.20.19.200");
        string subnetMask = config.GetConfigurationOf("mask", Configuration.ConfigMode.Append, "255.255.0.0");
        string gatewayAddress = config.GetConfigurationOf("gateway", Configuration.ConfigMode.Append, "10.20.19.1");
            
        interf.EnableStaticIP(ipAddress, subnetMask, gatewayAddress);
    }

            
    //New to set login webpage
    string loginName = config.GetConfigurationOf("loginName", Configuration.ConfigMode.Append, "admin");
    string loginPassword = config.GetConfigurationOf("loginPassword", Configuration.ConfigMode.Append, "admin");
    if (!((loginName == null) && (loginPassword == null)))
    {
        _UserName = loginName;
        _Password = loginPassword;
    }

    ClientsLogedTimeOut = new Timer(new TimerCallback(ClientsLogedEvent), null, 60000, 60000);

           
    this._PortNumber = portNumber;
    _OnboardLed = ledPort;
    ResponseListInitialize();

    _ListeningSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    _ListeningSocket.Bind(new IPEndPoint(IPAddress.Any, portNumber));
    _ListeningSocket.Listen(4);

    var webserverThread = new Thread(WaitingForRequest);
    webserverThread.Start();

    //To force file write in the card
    config.ForceToWrite();

    //limpa a memória
    Debug.GC(true);
}

         A função "SetNewConfig"é uma função que pode ser chamada através de dois métodos: POST e o GET. Quando solicitado o método GET trata-se de uma requisição da página do HTML enviando a pagina de HTML existente no cartão SD: "http://ipnetduino/config.html". Está página "config.html" pode ser configurada através da definição REQUEST_CONFIG_URL em Settings.cs. Uma vez carregada a página é possível enviar o formulário através de um POST com as informações da nova configuração. Quando o cliente envia o POST a requisição trata cada informação enviada manipulando o arquivo de configuração e ajustando as definições.

>private void SetNewConfig(RequestReceivedEventArgs e, Hashtable results)
{
    if (e.Request.Method == "POST")
    {
        //Esta enviando as informações de configuração
        Configuration config = new Configuration(Settings.CONFIG_FILE);

        foreach( DictionaryEntry ent in e.Request.PostArguments)
        {
            config.SetConfig(ent.Key.ToString(), ent.Value.ToString(),true);
        }
        //Envia a página de configuração
        e.Request.URL = Settings.REQUEST_CONFIG_OK_URL;
        Response response = null;
        response = (Response)_Responses["FileResponse"];
        if (response != null)
        {
            if (response.ConditionsCheckAndDataFill(e))
            {
                response.SendResponse(e);
            }
        }   

        //Force to Write
        config.ForceToWrite();
        //Apaga tudo!
        Debug.GC(true);
        //Desliga a placa
        PowerState.RebootDevice(false);
    }
    else
    {
        //envia a página de configuração
        e.Request.URL = Settings.REQUEST_CONFIG_URL;
        Response response = null;
        response = (Response)_Responses["FileResponse"];
        if (response != null)
        {        
            if (response.ConditionsCheckAndDataFill(e))
            {
                if (!response.SendResponse(e))
                {
                }
                ////Debug("Sending response failed");

                Thread ledThread = new Thread(new ThreadStart(delegate()
                {
                    for (int i = 0; i < 3; i++)
                    {
                        _OnboardLed.Write(true); Thread.Sleep(5);
                        _OnboardLed.Write(false); Thread.Sleep(20);
                    }
                }));
                        
                ledThread.Start();
            }
        }   
    }
}

         A final, uma página informado que as configurações foram aceitas é enviada de volta e a placa é reiniciada pelo código para que as configurações da conexão entrem em funcionamento.

//Force to Write
config.ForceToWrite();

//Apaga tudo!
Debug.GC(true);

//Desliga a placa
PowerState.RebootDevice(false);

         Observe que a página quando enviada não é pré-ajustada com os valores de configuração atual isso não fiz a implementação mas é necessário para tornar a solução bem robusta, além disso quando o método POST é recebido os campos do formulário são escritos no arquivo sem nenhum tipo de validação se está correta a informação enviada ou se o atributo faz sentido para a configuração. Uma validação nessa etapa é necessária.

         Outra simplificação feita foi que nem todos os atributos podem ser ajustados, o username e password não estão por definição no meu formulário, mas você pode simplesmente aumentar no formulário e incluir dois campos com label: username e password e tudo estará resolvido. Lembre-se de que como não existe validação deixar esses espaços em branco permite que o usuário seja "" (string vazio) e a senha a mesma coisa "" (vazio).

          Com isso é possível mostrar o quão longe pode-se ir com o Netduino Plus. Naturalmente ainda exite muito a se fazer para tornar essa solução realmente comercial, mas o primeiro passo já foi dado a fim de criar o Netduino Plus como um porto seguro em termos de hardware para webserver
           Esses dias procurando no forum do Netduino encontrei um post argumentando sobre um artigo de uma revista criticando o Netduino e a iniciativa como algo obsoleto e que vai cair em desuso, li diversosr comentários tanto do Chirs Walker como de outros membros da comunidade para saber a opinião de cada um a respeito da continuidade do projeto e posso dizer que a possibilidade que temos em função do Netduino é imensa. Destaco não o Netduino porque acho uma plataforma extremamente cara mais sim a solução do .NET Micro Framework que permite que sem um sistema operacional embarcado seja possível programar em alto nível sem um custo de memória externa. Tenho certeza montar um servidor web com u PIC24 ou um MSP430 não é uma tarefa tão fácil e universal como foi feito aqui.


Requisições XML

           Uma das funcionalidades implementadas pelo Marcus VV. foram as requisições por XML. Trata-se de enviar um GET para escrever/ler algo em hardware/software no Netduino; o que é muito útil se você pretende mandar através do servidor algum comando de automação como abrir e fechar um relé ou verificar o estado de iluminação (por exemplo).
           As respostas XML são na verdade a delegação de um tratamento de um GET (a princípio porque já provei nas configurações que é possível enviar um POST) enviado por um cliente. Como no meu código modificado requisita um login e senha, este login também é necessário para acesso dos métodos XML.
           Em Server.cs tem uma função chama de ResponseListInitialize(), esta função inicia todas as delegações de requisição e os métodos de cada delegação são funções dedicadas para a dada finalidade. Vide o exemplo do "switchDigitalPin", quando enviada a requisição: "http://ipnetduino/switchDigitalPin" essa requisição será interpretada pela função SwitchDigitalPin que necessita do atributo "pin" e "state" que são recebidos pelo método GET, assim a requisição é feita da seguinte maneira:

>http://10.20.19.41/setDigitalPin?pin=10&amp;state=false

           O retorno dessa função é um arquivo XML como pode ser visto nesta requisição:

     
            Além da reuisição de ajuste de pino ainda está disponível as seguintes funções de acesso ao hardware:
  • echo
    > [ipnetduino]/echo?value=[a-Z]
  • switchDigitalPin
    > [ipnetduino]/switchDigitalPin?pin=[0-13]
     
  • setDigitalPin
    > [ipnetduino]/setDigitalPin?pin=[0-13]&state=[true|false]
     
  • pwm
    > [ipnetduino]/pwm?pin=[5|6|9|10]&period=[int]&duration=[int]
     
  • xmlResponselist
    > [ipnetduino]/xmlResponselist
     
  • getAnalogPinValue
    > [ipnetduino]/getAnalogPinValue?pin=[0-5]
     
  • getDigitalPinState
    > [ipnetduino]/getDigitalPinState?pin=[0-13]
     
  • multipleXML
    >[ipnetduino]/multixml
            O que vem a cabeça nesse momento é necessidade expressiva da utilização do navegador para realizar o acesso a essas "funções" disponíveis para acesso a placa. Para provar que isso não é necessário eu fiz um pequeno programa animado para realizar o login no servidor do Netduino Plus e realizar os ajustes dos pinos e ler as entradas analógicas de maneira prática.

            A ideia do software é tão somente mostrar a praticidade do NeoMika.Webserver e o suporte ao XML. Iniciando o programa você pode ajustar o IP para o valor do IP que a sua placa do Netduino Plus está e realizar um teste de ping. Ping's recebidos retornam um true no log de eventos. Antes de acessar a placa você necessita realizar login, eu tentei de diversas formas realizar o envio do POST de login mas foi em vão, por isso, quando você clicar no botão de Login  você será enviado para a página de login que uma vez realizado corretamente dá acesso ao programa a placa.

            Do lado aparece a imagem do Netduino e ao redor dela os campos de acesso. Ao clicar em atualizar os valores dos pinos analógicos são atualizados e ao mudar o valor de percentagem dos PWM's estes mudam na placa bem como os botões manipulam os pinos IO. Tudo isso é realizado através de métodos de XML.
Function getAnvalue(pin As Integer) As String
        Dim webClient As New System.Net.WebClient
        Dim result As String = webClient.DownloadString("http://" + boardIP.Text.ToString() + "/getAnalogPinValue?pin=" + pin.ToString())
        EventsLog.Text += result.ToString() + vbNewLine
        Dim AdStrVal As String() = result.Split(">")

        Dim AdStrVal2 As String() = AdStrVal(3).Split("<")

        Dim advalue As Integer = CType(AdStrVal2(0), Integer)

        Return (3.3 * advalue / 1024).ToString()
    End Function

Considerações Finais

            Para encerrar essa serie de artigos sobre o NeoMika.Webserver e o trabalho do Marcus VV. gostaria de destacar a praticidade com que tudo isso foi implementado e que as novas aplicações podem ser encerradas no meio do código sem muitas alterações. Isso é importante porque mostra o quão rápido pode se desenvolver algo novo sendo ideal para pequenas aplicações profissionais.

            Muito embora ainda existam problemas e a solução não seja completamente lapidada ainda existem muitos pontos que podem ser aprimorados e meu desenvolvimento do servidor vai continuar. Espero que tenham gostado do que foi realizado, se tiverem dúvidas postem comentários que assim que possível responderei.


A versão atualizada do servidor web pode ser baixada aqui.
O software de manipulação do XML pode ser baixado aqui.

sexta-feira, 17 de agosto de 2012

Verificando o Hardware do Netduino

Como eu disse ontem, dei uma destruída no meu Netduino... Na verdade o que aconteceu é que eu queria fazer o teste com um controlador PWM no motor e na afobação teste o código que eu havia postado a um tempo atrás no blog para variar o PWM de 0 - 100%. Evidentemente não funcionou porque abaixo de uns X% o motor não parte e fica travado (não tem energia suficiente para dar a partida) assim tive que partir de 100% e ir reduzindo até chegar no tal X%.

Então trouxe a placa aqui para minha mesa para colocar um botão para reduzir o PWM e outro para aumentar. Alimentando pela USB o 5V da palaca fica com a tensão da porta USB e o "Vin" fica com uma tensão residual de poucos Volts assim resolvi tirar o fio que alimentava os botões e colocar no 5V. Os testes aqui no PC funcionaram bem e tratei de ir para bancada da firma testar com a bateria e alimentei a placa pela bateria de 12V através da conexão da protoboard, ou seja, alimentei com 12V na entrada de 5V...

O que me deixou com uma raiva mortal é que antes de testar pensei em colocar um fusível mas a afobação foi tão grande que deixei isso de lado e destruí a placa. A princípio o prejuízo não foi tão grande porque vai dar para recuperar.

Agora você ai deve se perguntar: "que otário fez um post babaca só para falar que queimou a placa...". Mas a minha intensão é diferente, fiz o post para mostrar uma maneira de testar todo o hardware da placa, a final depois que a placa sofre danos os novos testes que não funcionam vem sempre com a pergunta: "Será que foi alguma coisa que eu queimei?".

Primeiro de tudo é analisar partindo da origem do erro, no meu caso uma inversão da alimentação injetei 12V no barramento de 5V. Analisando pelo esquemático do Netduino, e também pelo Layout porque o Netduino não tem Label dos componentes impressos na placa, é possível notar que no barramento de 5V existe conectado o U1 LMV7271MF, os reguladores, os dois Led's da placa e o U7 SN74LVC1G3157DBVR (comutador analógico de referência do ADC).

O ideal é testar cada periférico separadamente iniciando pelos reguladores - não é necessário removê-lo da placa apenas aplicar uma tensão levemente mais alta na entrada e verificar se a saída está conforme através dos pinos 3.3V e 5V.

 Aqui eu tenho osciloscópio e a forma de onda do regulador com defeito é essa:
Não é necessário ter o um osciloscópio para e ver a forma de onda para verificar se está com defeito, basta ver o nível de tensão, que com o multímetro chegava a 6V (eu fiz a medida).

Naturalmente, a forma de onda te dá outro sinal, nesse caso de sobrecarga, ou seja, algo a frente no circuito esta consumindo mais que o regulador é capaz de fornecer no meu casso teve um componente que fumou mesmo e vou começar tirando ele e checando se resolveu o ripple (a tensão do regulador estabilizou).

 Retirei o CI que estava com defeito o U7, no próprio esquemático tem uma nota de como ele opera:
AREF_SOURCE_CONTROL
IF == 0, AREF_FILTERED = External AREF
IF == 1, AREF_FILTERED = Onboard +3.3V
Que faz referência a referência analógica do microcontrolador. A remoção surtiu um efeito, não o que eu espera, porque o circuito passou a esquentar mais o que é um péssimo sinal. 

Seria lindo se o mundo fosse perfeito e a placa terminasse funcionando bem, mas a vida real é diferente e a remoção do componente terminou por estragar de vez a placa... Agora é ter mais atenção na próxima vez...

quinta-feira, 16 de agosto de 2012

Fabricando meu primeiro robo explorador

A alguns meses atrás, conversando com alguns amigos que estão se formando na faculdade, veio a conversa sobre trabalho de conclusão de curso e em meio a isso surgiu a ideia de se fazer algo voltado para área de reconhecimento de objetos. O Interesse dos meus amigos era tão somente reconhecer objetos e controlar um pequeno carrinho equipado com uma câmera.

E porque fazer uma coisa simples se podemos complicar! Pensando assim nesses últimos dois dias estive trabalhando em uma coisa nova: um carrinho para ser movimentado pela paca.

Porque não utilizar um carrinho comercial: realmente podia-se ter utilizado esses kit's pré-fabricados mas montar um é melhor especialmente porque eu me propus a fazê-lo. A muitos anos eu venho desenvolvendo coisas estáticas sem muito dinamismo - uma porção de coisas sem graça - geralmente para automação doméstica quando muito realizando projetos para o trabalho... não que isso não envolva desafios mas quando se mexe sozinho é mais desafiador.

Pensando assim resolvi ajudá-los fazendo o carrinho propriamente dito com os motores e a parte de eletrônica de potência. Aqui estão as fotos do trabalho de hoje:


 
 Essa foi a sequência que eu estava fazendo das peças, por enquanto, como é só para testes ele vai ficar assim mesmo, depois pretendo fazer uma capa pintá-lo e de dar uma cara elegante para ele.


 Querendo eu fazer um teste rápido de um controlador PWM acabei por danificar o Netduino... é... deu uma fumada... que bom que vai dar para recuperar porque o processador não queimou! Segue os vídeos, são dois vídeos curtos mostrando o carrinho (desculpe a falta de profissionalismo nos vídeos... vou aprender a editar... )

terça-feira, 14 de agosto de 2012

Usando o LCD 16x4: μLiquidCrystal

Recentemente (ontem) descobri que o .NET MF inclui suporte avançado para interfaces gráficas. Existem algumas placas como a Tahoe II, ChipworkX, or FEZ Cobra que tem display gráfico e que podem ser manipulados pelas classes implementadas no .NETMF, um dia vou utilizar, até porque eu tenho alguns displays (LCD) prevenientes da sucata que eu pretendo utilizar para alguma finalidade.

Contudo, o mais comum entre os hobbistas brasileiros são os tradicionais display's alfanuméricos em linha, em especial o de 16 caracteres por duas linhas "16x2". Eu já fiz enumeras aplicações utilizando PIC e LCD's seja para mostrar um simples valor ou debugar uma aplicação.

 O meu objetivo com esse artigo é fazer uma apanhado de tudo que eu encontrei sobre LCD nesse mar de internet e mostrar uma solução viável e relativamente elegante que pode ser utilizada para aplicações futuras (minhas também). É importante notar que esse blog para mim é uma espécie de documentação dos meus códigos, assim eu não perco nada.

Os LCD de 16x2 são muito populares porque a maneira de comunicar é através de um barramento paralelo podendo ser usado com as velhas portas LPT1 (impressoras) o que popularizou bastante o seu uso além de permitir uma "emulação manual" da mensagem, ou seja, você pode prender uma porção de chaves e repetir exaustivamente o procedimento de entrada dos dados até mostrar a mensagem no LCD, a alguns anos atrás isso aqui no Brasil era vital porque na maior parte dos entusiastas não tinham osciloscópio e o diagnóstico do código era por Led mesmo (aceso e apagado).

Os tempos mudaram mas o preço ainda continua bem atrativo, comprei dois por R$ 17,00 a unidade na loja Solda Fria enquanto o equivalente gráfico custava a bagatela de R$ 60,00, quase o preço do Netduino.

A desvantagem da comunicação paralela é quantidade de IO's que serão "mortas", oito para ser mais exato e isso é um problema quando só se tem 14IO's disponíveis (lamentável o Netduino usar um Chip com 50 IO e só disponibilizar na placa 14 pinos). Outra desvantagem grande é quando é necessário colocar o LCD longe da placa de controle, vide o exemplo do controlador doméstico; controlando portas e janelas pelos pinos IO é conveniente que a unidade de controle fique num lugar perto do roteador para transferir os dados e ao mesmo tempo perto do centro da casa equidistante dos pontos de controle e o LCD nisso tudo não ficará perto da placa sendo necessário passar um cabo de 5 pares (10 fios)  para o LCD.

Naturalmente a aplicação ditará a forma mais fácil de se implementar o LCD. Contudo nesse artigo  procurei uma solução que atendesse a maioria das aplicações de maneira geral e com um custo em termos de software menor. A melhor solução foi a apresentada por Pavel Bánský em seu blog. Trata-se de dois artigos onde o primeiro artigo foi realizada uma análise da multiplexação do sinal do LCD para utilização de 3 fios e a segundo artigo relacionado foi construída uma aplicação envolvendo o hardware de I²C do microcontrolador para reduzir o custo de software.

Além do trabalho do Pavel ainda tem uma excelente referência na internet que é o projeto μLiquidCrystal que começou como um porjeto do Szymon Kobalczyk's  e depois foi ganhando corpo e agora é um pacote completo de classes para tratamento de dispositivos como LCD's e Display's.

Praticamente todos esses LCD's populares são feitos com o controlador HD44780que provê essa comunicação paralela. O conector padrão de 16 pinos em linha no canto inferior direito da placa do LCD também é bem típico podendo utilizar uma combinação de 8 bit's paralelos de informação ou 4 bits (pseudo serial), ou seja, dos  16 pinos 8 são para comunicação uma alimentação lógica, dois pinos para alimentação da luz de fundo, o pino de terra e a alimentação do LCD propriamente dito e mais dois pinos de comunicação serial totalizado os 16 pinos.


As instruções de escrita e leitura é um padrão que Pavel Bánský mostrou que é simples de se multiplexar com registrador de deslocamento e colocar o código para dentro do Netduino. Pavel usou o CMOS 4094 que é o mesmo que o 74HC595 só que é TTL que aceita como nível alto valores acima de 2,5V o que significa que você pode pendurar um LCD de 5V diretamente com as IO's do Netduino sem precisar de um circuito extra, apenas o registrador de deslocamento 74HC595. Mais ainda, se você quiser usar um LCD de 3,3V basta colocar um divisor restivo de feito com uma série de 1,68kOhms e 3,3kOhms que o nível de saída será de 3,3V.

Pavel implementou o tratamento do LCD em duas camas de funções uma por baixo da outra. A classe que realmente é incluída nos códigos é a LiquidCrystal e em um segundo nível a requerida  ILiquidCrystalTransferProvider que trata da interfaceamento de hardware assim você pode utilizar a mesma classe por cima com manipuladores de hardware diferentes, os dois implementados por Pavel são: GpioLiquidCrystalTransferProvider e HC4094 que na página do Szymon ele passou a chamar de Shifter74Hc595LiquidCrystalTransferProvider, isso porque Szymon estava usando o 74595 para interfacear o LCD.

Tem outras coisas implementas relacionas com algumas placas de expansão para LCD dotadas de chip para comunicação, como estamos no Brasil, o máximo que se encontra é uma comunicação serial feita com uma placa de expansão com PIC16F628 ou algo do tipo. As implementações feitas pelo Pavel e pelo  Szymon incluem suporte aos chips MCP23008 e PCF8574P numa classe chamada BaseShifterLiquidCrystalTransferProvicer.

O Szymon no seu blog mencionou a comunicação SPI para conversar como o LCD, uma alternativa caso o barramento I²C esteja carregado muito embora eu não tenha visto a forma com que ocorre o tratamento da comunicação SPI. A SPI no Netduino está disponível através dos pinos 11 (MOSI), 12 (MOSO) e 13 (SPCK) e outro pino qualquer para selecionar o escravo desejado. Essa implementação do Szymon faz parte do  μLiquidCrystal, que realmente não testei, mas vale a pena dar uma olhada.


Com relação ao circuito, vale algumas considerações. A primara diz respeito a um transistor NPN (eu usei o BC548 mas o 2N2222 é equivalente americano) que tipicamente se coloca para controle da luz de fundo do LCD através de uma saída do registrador de deslocamento assim é possível ligar e desligar o LCD pelo código caso deseje economizar energia.A outra é controle do brilho do LCD que pode ser feito colocando um potenciômetro de 47kOhms a 2kOhms, ou se não quiser ajustar apenas coloque um divisor resistivo e pronto.

O Pavel criou uma biblioteca (que como tava velha tive que recriá-la no .NETMF 4.2) chamada de Bansky.SPOT.LCD onde existem os métodos ITransferProvider e SendByte utilizados para interfacear uma camada com a outra.

O código de teste é esse:

>// Create instance of shift register 
// (Serve para o 74595!!)
HC4094 shifter = new HC4094(Pins.GPIO_PIN_D0 ,     
                                Pins.GPIO_PIN_D2, 
                                Pins.GPIO_PIN_D1,
                                false);          
// Create new LCD instance 
LCD4Bit lcd = new LCD4Bit(shifter);

// Turn display on, turn back light on,
//hide small cursor, show big blinking cursor
lcd.Display(true, true, false, true);

//Limpa a janela
lcd.Clear();  
//Escrever a mensagem na 1ª Linha
lcd.Write("Victor M. >>>>>>>>"); 
//Passa o cursor para 2ª Linha
lcd.SetPosition(40);          
//Escreve na 2ª Linha
lcd.Write(".NETMF Q2");

Thread.Sleep(Timeout.Infinite);

Note que o fluxo do processo é bem simples:
  1. Criar a classe da primeira camada informando os pinos do hardware que estão sendo usados para tal função;
  2. Criar a classe de manipulação do LCD partindo do hardware;
  3. Ajustar o que se deseja mostrar no LCD (no caso o quadro piscante);
  4. Limpar o LCD;
  5. Escrever a mensagem.

Não existe muito mais o que falar sobre o LCD, é bem simples mesmo, o que meu mais trabalho foi montar o circuito na protoboard do que entender o funcionamento. E ai na foto eu mostro resultado final:



Você pode fazer download do programa aqui para testá-lo.

segunda-feira, 13 de agosto de 2012

Salvando suas configurações no cartão microSD

Uma coisa muito importante que em qualquer aplicação hoje em dia é necessária é a configuração. Ninguém mais trabalha com soluções fixas sem salvar algum tipo de configuração. Seja um simples configuração de um IP ou mensagem personalizada, um arquivo de configuração sempre é necessário.

Inicialmente, quando eu comecei a programar utilizando microcontroladores PIC isso era uma tarefa nível "very hard" porque tratava-se de alocar uma parte da memória de código para salvar variáveis e como na época utilizava códigos de controle e variáveis tipo Double em processadores ponto fixo isso me custava horas de programação para desmontar com segurança um valor Double e tornar a montá-lo depois (O nome dessa técnica é EWR - EEPROM Read Write). Isso ainda é um bom método porque não necessita de uma memória externa como os tradicionais chips SPI  25LCxxxx onde xxxx é a quantidade de memória em míseros kb's (máximo 2Mb)!

Não estou dizendo que trata-se de uma solução ruim, não existe uma solução ruim. Se o interesse é tão somente gravar poucas variáveis essa é uma ideia excelente mesmo que o Chirs Walker não simpatize até porque as placas Netduino e Netduino Mini custam a metade do preço do Netduino Plus, ou seja sua aplicação cai pela metade do preço!. Esse tópico no fórum do Netduino mostra uma discussão a respeito do tema que um dia eu pretendo implementar para reduzir custo e tudo mais.

No caso do Netduino Plus, temos um cartão microSD de 4Gb de espaço para gravar tudo que se deseja e nada melhor que gravar um arquivo só para configurações no cartão, dessa forma o acesso a informação fica prático e além disso para modificar as entradas basta mudar o arquivo no cartão microSD sem necessitar recompilar o código.

O que os leitores devem estar de perguntando nesse momento é porque eu não terminei o atrigo sobre o NeonMika.Webserver e estou escrevendo sobre arquivos de configuração. A lógica está na etapa de salvar as informações da configuração da placa no cartão e para isso eu desenvolvi uma classe independente para tratar desse tipo de coisa.

using System;
using System.Text;
using System.Collections;
using System.Net.Sockets;
using System.Net;
using System.Diagnostics;
using System.IO;
using Microsoft.SPOT;

namespace SDCardClass
{
    /// <summary>
    /// Configuration File Class
    /// 
    /// Developed to: Netduino Plus REV.B Borad 
    /// .NETMF 4.2 RC5
    /// 
    /// By Victor M. Batista
    /// email: victor.mobatista@gmail.com
    /// 
    /// Date: 13-Ago-2012 
    /// </summary>
    class Configuration
    {
        //TODO: Insert List<Hashtable> to create a file with configuration sections
        private Hashtable _Configuration = new Hashtable();
        private string _FileName;

        public enum ConfigMode{Append,DoNothing};

        /// <summary>
        /// Open a configuration file if it exist and provide 
        /// information for request's.
        /// </summary>
        /// <param name="filename">Optinal file name</param>
        public Configuration(string filename = "ndpcfg.ini")
        {
            //Nome global
            _FileName = filename;

            //Clear configuration file;
            _Configuration.Clear();

            //Read configuration file
            if (File.Exists("\\SD\\" + _FileName))
            {
                FileStream CfgFileStream;
                StreamReader CfgStreamReader;

                try
                {

                    CfgFileStream = new FileStream("\\SD\\" + _FileName,
                                                               FileMode.Open,
                                                               FileAccess.ReadWrite,
                                                               FileShare.None);

                    CfgStreamReader = new StreamReader(CfgFileStream);

                    string val = CfgStreamReader.ReadLine();
                    if (val != null)
                    {
                        do
                        {
                            if (val[0] != ';') // If is not a comment line
                            {
                                string[] SplitVals = val.Split('=');
                                if (SplitVals.Length == 2)
                                {
                                    _Configuration.Add(SplitVals[0], SplitVals[1]);
                                }
                            }

                                val = CfgStreamReader.ReadLine();

                        } while (val != null);
                    }

                    CfgStreamReader.Close();
                    CfgFileStream.Close();


                }catch {}

            }

            
        }



        /// <summary>
        /// Apend new configuration in configuration file 
        /// </summary>
        /// <param name="key">Key as a configuration variable</param>
        /// <param name="value">Key value as string</param>
        /// <param name="SetIfExist">Set a this value to the key 
        /// if this current key already exist in context</param>
        public void AppendConfig(string key, string value, bool SetIfExist = false)
        {
            if (!_Configuration.Contains(key))
            {
                _Configuration.Add((object)key, (object)value);
            }
            else if(SetIfExist)
            {
                _Configuration[key] = value;
            }
        }

        /// <summary>
        /// Set new configuration in configuration file 
        /// </summary>
        /// <param name="key">Key as a configuration variable</param>
        /// <param name="value">Key value as string</param>
        public void SetConfig(string key, string value, bool CreateIfNotExist = false)
        {

            if (_Configuration.Contains(key))
            {
                _Configuration[key] = value;
                
            }
            else if (CreateIfNotExist)
            {
                _Configuration.Add((object)key, (object)value);
            }
        }

        /// <summary>
        /// Get a value of key. If current kay did not exist in the context
        /// you can put a default value to set or simples ignore and recive 
        /// a empty string as defult
        /// </summary>
        /// <param name="key">Key that you need the value</param>
        /// <param name="mode">Handling method if key don't exit in the file</param>
        /// <param name="val">Default value that to place in key if it don't exist.
        /// If *mode* is DoNothig this value was ignored</param>
        /// <returns></returns>
        public string GetConfigurationOf(string key, ConfigMode mode =
            ConfigMode.DoNothing, string val = "" ){

            //Verifay if this key exit
            if(_Configuration.Contains(key))
            {
                //If key exist return value.
                return _Configuration[key].ToString();
            }else{
                //else make the handling 
                if (mode != ConfigMode.DoNothing)
                {
                    //append new entry in configuration file.
                    AppendConfig(key, val);
                    return val;
                }
                else
                {
                    return "";
                }
            }
        }


        /// <summary>
        /// Function to force to write in the microSD card the current
        /// configuration.
        /// </summary>
        public void ForceToWrite()
        {
            try
            {
                FileStream CfgFileStream = new FileStream("\\SD\\" + _FileName,
                                                            FileMode.OpenOrCreate,
                                                            FileAccess.ReadWrite,
                                                            FileShare.ReadWrite);

                StreamWriter CfgStreamWriter = new StreamWriter(CfgFileStream);

                CfgStreamWriter.WriteLine(";Netduino Plus Configuration File");
                foreach (DictionaryEntry Entry in _Configuration)
                {
CfgStreamWriter.WriteLine(Entry.Key.ToString() + "=" +
                                                Entry.Value.ToString());
                }
                CfgStreamWriter.Close();
                CfgFileStream.Close();
            }
            catch { }
        }

        /// <summary>
        /// Desconstructor to write de file in card when the class is
        /// disposed
        /// </summary>
        ~Configuration()
        {
            ForceToWrite();
        }
    }
}


A classe é bem simples chama-se configuraition e serve para manipular eventos de escrita e leitura no cartão microSD isso facilita porque não é necessário acessar o arquivo diretamente, apenas criar a classe e pegar os valores de configuração desejados.


O formato escolhido para o arquivo é o INI - agradeço ao Cairo por ter me mostrado esse formato muito vantajoso de arquivo. Para quem não sabe o formato de arquivo INI é um padrão para elaboração de arquivos dedicados a carregar configurações de programas e dispositivos. Os arquivos INI são arquivos utilizam um texto simples com uma estrutura bem básica que muito difundidos na Microsoft e espalhados pelo Windows. Naturalmente como fica muito solto (um arquivo oculto) no Windows  foi substituído pelo registro do Windows mas o formato ficou.

Quando ao nome vem da extensão de nome de arquivo normalmente utilizada, ".INI", que vem de "inicialização". Como ".INI" é uma extensão do "Windows" muitos desenvolvedores começaram a bagunçar criando extensões diversas como como ".CFG", ".conf" ou ".TXT".


O formato INI encapsula a mensagem através de novas linhas onde uma  propriedade (ou parâmetro) é único e está numa linha qualquer do arquivo. A essa propriedade tem um nome e um valor, delimitado por um sinal de igual(=). O nome aparece à esquerda do sinal de igual.

propriedade = valor
propriedade2 = valor2
O ponto-e-vírgula (";") indica o início de uma linha que é comentário tratada como comentário Qualquer coisa entre o ponto e vírgula e o final da linha é ignorada.

 O código que manipula um arquivo escrito dessa forma no cartão microSD. permitindo a criação de comentários e a manipulação dos atributos através de uma tabela. Os princiapis métodos são o Construtor, o AppendConfig(), GetConfigurationOf() e o ForceToWrite().

O construtor da classe tem um valor padrão trata do nome padrão ndpcfg.ini que quer dizer Netduino Plus Configuration esse arquivo pode ser alterado de local ou nome criando a classe com um nome de arquivo diferente. Caso deseje colocar numa subpasta lembre-se da sintax:  "Pasta1\\Pasta2\\nomedoarquivo.extensão".

O método GetConfigurationOf( key ) trata ma requisição de uma propriedade listada no arquivo, o natural é que a propriedade me questão exista e o valor, como string, seja retornado caso contrário retorna um string sem caracteres (""). Mas é possível atribuir um valor padrão caso não existe que será gravado ao terminar a classe de manipulação.

O outro método é para atribuir uma chave nova pode utilizar as funções AppendConfig() ou SetConfig(), isso porque o arquivo INI tem uma sintaxe que não permite propriedades com o mesmo nome assim você pode tanto tentar criar e se já existir no contexto apenas mudar o valor ou então o vice-versa com o SetConfig()

Por fim, tem o método ForceToWrite() que serve para forçar a escrita de toda a configuração no cartão microSD mesmo antes da classe ser encerrada. Isso reduz a quantidade de vezes que o cartão é acessado, não é a melhor maneira de fazer isso mas essa classe ainda vai evoluir.

Para testar a classe baixe o projeto aqui.

Configuration config = new Configuration();
config.GetConfigurationOf("IP",
    Configuration.ConfigMode.Append, "192.200.6.254");
            
//Contando o numero de execuções do código
Int32 exec = Convert.ToInt32(
    config.GetConfigurationOf("ExecutionsCount", 
    Configuration.ConfigMode.Append, "1"));
exec++;
config.SetConfig("ExecutionsCount", exec.ToString());
            
//Gravando valores
config.AppendConfig("mask", "255.255.0.0");
config.SetConfig("gateway", "192.200.1.1");

//Forçando a escrita
config.ForceToWrite();

Para ilustrar o funcionamento eu criei uma varável no arquivo de configuração que é incrementada toda vez que o código é executado, um exemplo clássico de aplicação do arquivo de configuração.

Abra sua cabeça para novas ideias, no arquivo de configuração pode constar valores como mensagens de LCD para diferentes tipos de cultura, rotinas de tratamento de opções preferidas pelos usuários da sua solução e muito mais.

domingo, 12 de agosto de 2012

Explorando o NeonMika.Webserver - Parte 2

Agora acredito que devo mudar o nome dessa sequência de artigos para: "modificando o NeonMika.Webserver" porque o que eu já fiz de modificação está dando mais trabalho do que eu imaginava, já estou quase implementando tudo outra vez.

Demorei umas duas semanas para postar algo novo sobre o servidor porque precisei de tempo para aprender como um servidor web realmente funciona, tive que aprender HTML, Javascript (outra vez), protocolo de rede, métodos (POST e GET) e por ai vai. Depois muita luta consegui trazer um código novo, ainda sem a parte de configuração, mas com a página de login.

A parte de login realmente deu um trabalho do cão para fazer, isso porque tive que lutar com as página de web para fazer uma que me me fosse conveniente para embarcar no cartão microSD e que o Netduino Plus pudesse processar. Primeiro baixei o NVU para criar a página de web, como não sabia como utilizar, parti para um tutorial mas a minha anciedade não permitiu que eu o termina-se de ler e tive que abandonar aquela coisa chata e que no final de tudo não iria me ajudar para o que eu desejava então parti para "mão grande" e começei a fazer com o editor de texto mesmo e consultando tudo na www.w3schools.com que hoje é referência básica para HTML e afins.


Através dos meus novos conhecimentos consegui fazer a página de login e uma página de configuração, o que precisava seguida era animar isso tudo. Nesse ponto vem a parte do Javascript, tentei por várias horas ontem criar uma forma de logar independente do servidor, ou seja, todo o tratamento do login ficasse na página, mas isso foi em vão... até consegui ter algum sucesso colocando um código Javascript na parte no cabeçalho da página para que tova vez que uma página fosse aberta solicitasse uma senha, o grande problema é que toda página tinha que ser "logada" sempre ao acessar a página e o tratamento do login incorreto era abrir outra janela de login e mais ainda, toda a vez que a página fosse aberta poderia revelar a senha no código fonte da página. Com todos esses percalços tive mesmo é que abandonar essa ideia e passar para uma requisição de login dentro do servidor.

/// <summary>
/// microSD card webpage how are the login webpage
/// </summary>
public const string REQUEST_LOGIN_URL = "login.html";
 
Que faz referência a página onde se encontra o formulário de login em página de web. Essa página que no meu exemplo se chama "login.html" trata-se de uma página de web com um formulário que passa os seus parâmetro pelo método POST. Utilizei o método POST para enviar os dados através do corpo da mensagem o que dificulta levemente a maneira de descobrir a senha de segurança para acessar o servidor na placa.

O formulário com o método POST deve ser escrito da seguinte maneira:


<form method="post">
<span style="color: rgb(255, 255, 255);">Usuário : </span><input type="text" name="Username">
<br>
<span style="color: rgb(255, 255, 255);">Senha  : </span><input type="password" name="Password">
<br>
<br>
<input type="submit" value="Login" onClick="sendlogin1()">
</form>

O tratamento é feito de tal forma que quando uma requisição de uma página chega ao Netduino Plus, verifica-se se o IP de onde vem a requisição ainda não fez login, caso afirmativo, envia a página de login para que o usuário informe a senha.  Uma vez recebida as informações de usuário e senha o servidor torna-se aberto para o cliente acessar as páginas.

A função que manipula a requisição de páginas ficou assim:

/// <summary>
/// Checks an incoming request against the possible responses
/// </summary>
/// <param name="e"></param>
private void HandleRequest(RequestReceivedEventArgs e)
{
    ////Debug("Start checking requests");
    Response response = null;
    if (_Loged)
    {
        //normal file response
        if (_Responses.Contains(e.Request.URL))
        {
            //Trata as repostas personalizadas criadas na aplicação
            response = (Response)_Responses[e.Request.URL];
        }
        else
        {
           response = (Response)_Responses["FileResponse"];
        }
    }
    else
    {
        //Test if it's a Login post message
        if(e.Request.Method == "POST")
        {
            int validation = 2;
            foreach (DictionaryEntry entry in e.Request.PostArguments  )
            {
                if ((entry.Key.ToString() == "Password") && (entry.Value.ToString() == _Password))
                {
                    validation--;
                }
                if ((entry.Key.ToString() == "Username") && (entry.Value.ToString() == _UserName))
                {
                    validation--;
                }
                if (validation == 0) { 
                    // login and load the Webpage that user Requested
                    _Loged = true;
                    e.Request.LoadWebpageRequested();
                    response = (Response)_Responses["FileResponse"];
                }
            }
        }
        else
        {
            //Set to call Login webpage - for load Netduino icon e don't load other webpages
            if (e.Request.URL.Length > 5)
            {
                if (e.Request.URL.Substring(e.Request.URL.Length - 5) == "html")
                {
                    e.Request.RequestLogin();
                }
            }
            else
            {
                e.Request.RequestLogin();
            }
            //Go to fileResponse to send the webpage as default
            response = (Response)_Responses["FileResponse"];
        }
    }

    if (response != null)
    {
        if (response.ConditionsCheckAndDataFill(e))
        {
            Thread ledThread = new Thread(new ThreadStart(delegate()
            {
                for (int i = 0; i < 3; i++)
                {
                    _OnboardLed.Write(true); Thread.Sleep(5);
                    _OnboardLed.Write(false); Thread.Sleep(20);
                }
            }));
            ledThread.Start();
        }
    }
}

Link para download do código fonte.
Link para download dos arquivos para cartão micro SD (para rodar o exemplo basta colocar na raiz)

O problema das Imagens

Um problema que eu resolvi nessa segunda parte sobre o NeonMika.Webserver foi o problema das imagens que não carregavam, pois bem, o problema é que as imagens eram muito pesadas... míseros 500kB já é uma coisa pesada para o nosso pequeno webserver mas tive bons resultados com o logo do netduino, como podem ver pelos screenshot's do servidor e a imagem da placa, cerca de 70kB.


O jeito por enquanto é tratar de usar figuras pequenas ou coleção de imagens bem pequenas, mas estou vendo através do forum do Netduino alguma maneira para passar um ponteiro para o cartão microSD transferir o arquivo sem precisar colocá-lo em uma variável interna. A vantagem disso seria não gastar memória do processador para buffer de página de HTML e arquivos além de permitir que arquivos de qualquer tamanho sejam transferidos. O problema agora é alguém me dizer se existe a solução ou vou ter que garimpar muito...

 O problema de timeout das páginas

Isso é um problema chato que está acontecendo e está prejudicando o envido do POST do login. Muitas vezes a placa está muito lenta para tratar as requisições do navegador e com isso á página não é carregada necessitando atualizar a página para que numa segunda requisição a placa inicie e envie a página.

Esse problema não é só nosso, mas todos de um modo geral tem tido esse problema porque a camada de código está muito acima e acaba demorando muito apara responder, mas estão trabalhando para melhorar isso no .NETMF.

___


Na terceira parte vou falar sobre os métodos XML e JSON e sobre a configuração do IP utilizando o servidor NeoMika.Webserver.

sábado, 4 de agosto de 2012

Explorando o NeonMika.Webserver - Parte 1

Hoje teria que continuar trabalhando no projeto da empresa, o SimOp, mas por um erro meu não fiz o upload do arquivo certo e agora não estou com a versão do código mais atualizada então não posso trabalha nele, por isso resolvi continuar desbravando o NeonMika.WebServer.

O que me interessa a princípio é fazer uma aplicação simples com a comunicação pela internet. Simples não quer dizer que seja fácil! O que eu espero é ter uma estrutura de manipulação da placa (ler o ADC, ler e escrever as portas IO, etc) que é perfeito para quem está desenvolvendo. No nosso caso, caros amigos leitores desse blog, podemos parar por aqui, a final de contas se você quiser mudar qualquer configuração basta recompilar o código e embarcar que estará tudo novo!

O grande problema é que 99% dos robsistas param por aqui, mas para aplicações mais comerciais isso não basta, se não vai se tornar um produto que eu costumo chamar de "house made" como muita coisa nacional que se comercializa apenas no mercado brasileiro por rasões óbvias. Existem muitos exemplos disso - geralmente in nichos de mercado gerados por lei - como o caso do controle de acesso homologado pelo ministério do trabalho existem modelos importados excelentes, mas como não são homologados não servem para empresas e nós acabamos sendo vitimas disso.

Outro exemplo forte nessa área são os receptores via satélite, dezenas de modelos para mercado nacional (Tecsys, Amplimatic, Plasmatic, Limon etc etc etc... ) que estão ainda muito longe do padrão dos aparelhos Azbox, Az América, NewGen que são nomes genéricos de modelos fabricados obre encomendada para as TV por assinatura  ( Sky, NET e etc...).

Em função de toda essa fama, um dos meus objetivos antes de morrer será fabricar algo nacional que não seja mais um produto no mercado sem visibilidade mas sim algo que possa competir com os modelos que estão no mercado, seja qual segmento for...

Então, continuando a explanação, exite a necessidade de algo mais profissional para aplicação com Internet dizendo respeito a configuração do dispositivo e afins. Vendo as soluções do mercado pode-se tirar como base os pequenos dispositivos que estruturam a rede de internet que necessitam de configuração (Roteadores, Access Point's), eles utilizam um pequeno servidor interno para permitir configurar o dispositivo, o que é uma solução bem interessante uma vez que não necessita de mais portas além da porta de Ethernet que já está disponível.

No caso do Netduino Plus, existem 3 formas facilitadas (facilitadas porque não precisa de hardware externo) para realizar a configuração:
  • Através de um arquivo no cartão microSD (necessita da remoção do cartão);
  • Através da porta USB (necessita disponibilizar a porta e fazer um "miguer");
  • Através de um servidor web;
 Sem dúvida  o servidor web é mais interessante que as demais soluções por motivos não tão óbvios assim. Posso destacar o hábito como um forte aliado para escolha, quem configura dispositivos na rede já está acostumado com o processo pela página interna do dispositivo (um facilitador) e além disso não é necessário deixar disponível a porta USB para alguém tentar colocar um cabo nela e acabar por travar a execução do seu código e tão pouco deixar o cartão microSD disponível para ser removido (não precisa tratar a remoção do cartão acidental).

Por fim, destaco ainda, que através do servidor web na placa é possível fazer uma pequena depurção de código e apresentar todos os dados do cartão microSD, mais a frente pretendo detalhar uma maneira de fazer isso transparente, escrevendo um arquivo de log de eventos no cartão microSD e depois apresentando-o através da página do servidor html interno.

Outra vantagem que pode ser destacada é a facilidade de acesso e configuração remota da placa. Se a aplicação for colocada em um local de dificil alcance torna-se mais rápido configurá-la via Ethernet.

Por todos esse motivos não tem porque não utilizar o servidor web no Netduino Plus e agora podemos entrar diretamente no NeonMika.WebServer e como vamos utilizá-lo. Particularmente, não conheço muito de internet e protocolos de rede por isso vou dar uma explicação ao meu ver do NeonMika, até porque tive muita dificuldade em encontrar informação sobre o projeto.

Aqui vão algumas dicas para você que está começando nesse negócio de WebServer com o NeonMika, porque eu apanhei muito hoje para colocar esse código para rodar. Primeiro, faça download da versão mais atual do NeonMika.Webserver, se estiver usando o .NET MF 4.2, atualize o primeiro projeto da solução para o .NET MF 4.2, depois atualize os outros em sequência. Feito isso dê uma compilada no projeto, vãoa parecer alguns erros ligados a identificação do AnalogInput, altere para SecretLabs.NETMF.Hardware.AnalogInput todos.

Agora, quando você tentar debugar, vai ter um erro: "The project 'NeonMika.Webserver' cannot be referenced.". Para resolver abara o projeto do NeonMika.Webserver tente uma vez deugar usando o emulador depois retorne para USB > NetduinoPlus_NetduinoPlus.

Para achar o IP que foi lançada a aplaca na rede veja na janela Output, tem algo escrito como: "Webserver is running on 192.168.5.100 /// DHCP: False". Pronto está rorando, agora basta acessar a página pelo IP que você verá a página inicial, ou não, aqui em casa ele estava subindo numa subrede diferente aqui é 192.168.1.xxx não 192.168.xxx.xxx!

Bom, na minha rede isso acontece porque eu deixo o IP fixo e mando o DHCP gerar IP's fora da rede para confundir qualquer favelado que more na vizinhança se ele tentar derrubar a minha rede sem fios para acessá-la (acredite isso acontece...). De qualquer forma, demorei não mais que 1 hora procurando onde se colocava o IP que teoricamente seria fixo, então comecei a entender o código parindo do construtor Server em Server.cs. Descobri que não existe um lugar para colocar o IP fixo! Então fiz uma pequena modificação na função, e ficou assim:

///
/// Creates an instance running in a seperate thread
///
/// OutputPort to indentify
/// The port to listen
///
/// IP
/// Mask
/// GateWay
public Server(OutputPort ledPort, int portNumber = 80, bool DhcpEnable = false,
                string ipAddress = "", string subnetMask = "", string gatewayAddress = "")
{
    var interf = NetworkInterface.GetAllNetworkInterfaces()[0];

    if (DhcpEnable)
    {
        interf.EnableDhcp();
        interf.RenewDhcpLease();
    }
    else
    {
        //New to fix
        interf.EnableStaticIP(ipAddress, subnetMask , gatewayAddress );

    }

    Debug.Print("Webserver is running on " + interf.IPAddress.ToString() + " /// DHCP: " + interf.IsDhcpEnabled.ToString());

    this._PortNumber = portNumber;
    _OnboardLed = ledPort;
    ResponseListInitialize();

    _ListeningSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    _ListeningSocket.Bind(new IPEndPoint(IPAddress.Any, portNumber));
    _ListeningSocket.Listen(4);

    var webserverThread = new Thread(WaitingForRequest);
    webserverThread.Start();
}
E naturalmente, você terá a opção de declarar um IP fixo em Program.cs:

bServer = new Server(PinManagement.OnboardLED, 80, false, "192.168.1.41", "255.255.255.0", "192.168.1.1");
Continuando, não foi só isso que modifiquei, na verdade, como o meu interesse era usar a página no cartão microSD e nada mais justo que ver funcionando - e isso me tomou tempo - em especial porque não conseguia acertar de maneira alguma o local onde o arquivo fica alojado para ser chamado, muito embora os métodos estivessem todos corretos, o caminho tinha problemas com a barras "\" e "/".  Assim eu tive que modificar a fonção SendResponse para que ela faça a inversão das barras do endereço e busque dentro do cartão o local correto.

public override bool SendResponse(RequestReceivedEventArgs RequestArguments)
{
           
    //System.Text.StringBuilder.Replace(char, char)
    StringBuilder filePath = new StringBuilder();
                           
    filePath.Append(@"\SD\");
    filePath.Append(RequestArguments.Request.URL);
    filePath.Replace(@"/",@"\");


    //string filePath = @"\SD\shali2.jpg";

    //File found check
    try
    {
        if (!File.Exists(filePath.ToString()))
        {
            Send404_NotFound(RequestArguments.Client, "Não encontrado:" + filePath.ToString());
            return false;
        }
    }
    catch (Exception ex)
    {
        Debug.Print(ex.ToString());
        return false;
     ..... continua ....

Se você estiver modificando na versão atual do NeonMika.Webserver você vai notar que a função
Send404_NotFound também foi modificada. Tive que modificá-la porque simplesmente não estava funcionado e, por outro lado, ela não permitia a depuração na página dizendo o endereço ou uma mensagem personalizada. Então a função ficou assim:

///
/// Sends a 404 Not Found response
///
protected void Send404_NotFound(Socket Client, String Message = null)
{

    Debug.GC(true);
    if (Client != null)
    {
        string header = null;

        if (Message != null)
        {
            header = "\n\n\n

HTTP/1.1 404 Not Found

\n"
+ Message + "
\n\n\n";
        }
        else
        {
            header = "\n\n\n

HTTP/1.1 404 Not Found

\nNeonMika.Webserver is sorry...
\n\n\n"
;
        }
               
        Client.Send(Encoding.UTF8.GetBytes(header), header.Length, SocketFlags.None );
    }
}
}

Com isso já dava para utilizar o NeonMika.WebServer e colocar uma página no cartão SD para testar. Com o intuito de testar eu busquei na internet um template de site que fosse relativamente completo com imagens e tudo mais e subpáginas e consegui ter um bom grau de sucesso, muito embora as imagens não tenham sido carregadas ainda... mas assim que consertar isso vou postar a segunda parte dessa análise do servidor NeonMika.





Para fazer download do código NeonMika com as modificações clique aqui.
Para fazer download da página para testar no cartão MicroSD clique aqui.