domingo, 25 de novembro de 2012

Hora Certa no Netduino - NIST Internet Time Service IP


Assim que comecei a fazer medições a primeira coisa que precisa era atribuir data e hora de cada medição ao salvar no cartão microSD. A boa notícia é que o .NETMF tem suporte ao formato "DateTime"  que é muito usado no .NET normal para pegar a data e hora do windows e usar na aplicação, o problema é que o serviço de hora no Netduino é iniciado quando a placa liga ou chama o serviço de hora a primeira vez, ou seja, a primeira chamada é sempre zero hora.....

Para fazer um teste inicial você pode executar um código simples:

while (true)
{
 Debug.Print("Horário Atual: " + DateTime.Now.ToString());
    Thread.Sleep(50000);
}

A saída desse código é só para o Debug e é mais ou menos assim:


Note que o horário é meio louco, agora são 17:25 de 2012. Não sei que hora é essa mas vamos lá se queremos um medidor que envie dados para a internet é conveniente que ele também tenha um ajuste de hora automático. Já pensou, toda vez que a placa desligar ter que ir até ela e corrigir a hora na "mão grande"...

A solução para o Netduino Plus é utilizar um servidor de tempo. O Time Server ou servidor de tempo (NIST) é um servidor que lê uma informação de tempo de um relógio de tempo real e distribui a informação pela internet através de um protocolo próprio chamado de Network Time Protocol (NTP), embora outros protocolos de tempo menos populares (ultrapassados) ainda estejam em uso.

O NTP é um protocolo baseado no UDP que tem uma finalidade muito especial de sincronização do relógio de um conjunto de computadores em redes de dados com latência variável. O NTP permite manter o relógio de um computador com a hora sempre certa e com grande exatidão. Originalmente idealizado por David L. Mills da Universidade do Delaware e ainda hoje mantido por si e por uma equipa de voluntários, o NTP foi utilizado pela primeira vez antes de 1985, sendo ainda hoje muito popular e um dos mais antigos protocolos da internet (veja mais detalhes na wikipédia). 

Exitem na internet uma série de servidores de tempo (NIST) na internet para capturar a hora certa e utilizar no sei código como referência, o site que eu peguei os IP's foi este: http://tf.nist.gov/tf-cgi/servers.cgi. Que são exatamente todos servidores no EUA, mas exitem outros pelo mundo a fora.


O código que eu fiz é bem simples. Trata-se de uma classe que quando inicializada atualiza o valor do tempo para a hora atual obtida através de um servidor da lista de servidores NIST da classe. O único parametro que deve ser passo para classe são os minutos de defasagem entre o valor da hora de referência em Greenwich para sua hora local, que no meu caso, Rio de Janeiro é -120 minutos ( -2 horas).

Esta é a classe:

using System.Net.Sockets;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using System.IO;
using System.Text;
using System;
using System.Net;
using Microsoft.SPOT.Net.NetworkInformation;


namespace RealTimeTest
{
           
    /// <summary>
    /// Autor: Victor Manuel
    /// Data: 11/02/2012
    /// 
    /// Descrição: Classe De serviços de sincronização de relógio
    /// para o NetduinoPlus
    /// 
    /// 
    /// Exemplo:
    /// 
    /// //Seta o IP da porta
    /// NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticIP("10.20.19.35", "255.255.255.0", "10.20.19.1");
    /// string[] dns = { "10.20.19.1", "200.222.122.133" };
    /// NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticDns(dns);
    /// System.Threading.Thread.Sleep(10000);
    /// 
    /// InternetTimeService nits = new InternetTimeService(-120);
    /// </summary>
    class InternetTimeService
    {
        private int TimeServerIndex = 0;
        private int OffSetMinutes = 0;


        /// <summary>
        /// System Synchronize Internal DateTime
        /// </summary>
        /// <param name="minutes">Time Zone Difference</param>
        public InternetTimeService(int minutes)
        {
            Debug.Print("InternetTimeService routine is running ...");
            OffSetMinutes = minutes;
            Utility.SetLocalTime(GetNetworkTime());
            Debug.GC(true);
        }

        /// <summary>
        /// NIST Internet Time Servers
        /// For server list access: http://tf.nist.gov/tf-cgi/servers.cgi
        /// </summary>
        public string[] InternetTimeServers = {"time.nist.gov", //  global address for all servers  Multiple locations 
                                               "nist1-ny.ustiming.org", //New York City, NY 
                                               "nist1.aol-va.symmetricom.com", //Reston, Virginia 
                                               "nist1-atl.ustiming.org ", //Atlanta, Georgia 
                                               "nist1-la.ustiming.org",  //Los Angeles, California 
                                               "time-nw.nist.gov", //Microsoft, Redmond, Washington 
                                               "utcnist.colorado.edu" //University of Colorado, Boulder 
                                               };
        


        /// <summary>
        /// Force to system synchronize internal DateTime
        /// </summary>
        public void ForceToUpdate()
        {
            Utility.SetLocalTime(GetNetworkTime());
            Debug.GC(true);
        }


        /// <summary>
        /// Method to query a NTP server and set the device date to the returned value
        /// </summary>
        /// <returns>Recived Server Time</returns>
        public DateTime GetNetworkTime()
        {
            while (true)
            {
                try
                {
                    Debug.Print("Conecting to:" + InternetTimeServers[TimeServerIndex].ToString());
                    IPEndPoint ep = new IPEndPoint(Dns.GetHostEntry(InternetTimeServers[TimeServerIndex].ToString()).AddressList[0], 123);

                    Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                    s.Connect(ep);

                    byte[] ntpData = new byte[48]; // RFC 2030
                    ntpData[0] = 0x1B;
                    for (int i = 1; i < 48; i++)
                        ntpData[i] = 0;

                    s.Send(ntpData);
                    s.ReceiveTimeout = 10000;
                    s.Receive(ntpData);

                    byte offsetTransmitTime = 40;
                    ulong intpart = 0;
                    ulong fractpart = 0;
                    for (int i = 0; i <= 3; i++)
                        intpart = 256 * intpart + ntpData[offsetTransmitTime + i];

                    for (int i = 4; i <= 7; i++)
                        fractpart = 256 * fractpart + ntpData[offsetTransmitTime + i];

                    ulong milliseconds = (intpart * 1000 + (fractpart * 1000) / 0x100000000L);

                    s.Close();

                    TimeSpan timeSpan = TimeSpan.FromTicks((long)milliseconds * TimeSpan.TicksPerMillisecond);

                    DateTime networkDateTime = (new DateTime(1900, 1, 1)) + timeSpan;
                    
                    Debug.Print("UTC: " + networkDateTime.ToString());

                    networkDateTime = (new DateTime(1900, 1, 1)) + timeSpan + TimeSpan.FromTicks((long)OffSetMinutes * TimeSpan.TicksPerMinute);

                    Debug.Print("LOCAL: " + networkDateTime.ToString());

                    return networkDateTime;
                }
                catch
                {
                    Debug.Print("Impossible to conect on " + InternetTimeServers[TimeServerIndex].ToString());
                    Debug.Print("try next time server..");
                    TimeServerIndex++;
                    if (TimeServerIndex == InternetTimeServers.Length)
                    {
                        Debug.Print("Impossible to access Internet Time Servers... ");
                        return DateTime.Now;
                    }

                }
            }
        }

    }
}
 
Este é o exemplo de utilização:

NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticIP("10.20.19.35", "255.255.255.0", "10.20.19.1");
string[] dns = { "10.20.19.1", "200.222.122.133" };
NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticDns(dns);
System.Threading.Thread.Sleep(10000);

InternetTimeService nits = new InternetTimeService(-120);
      
while (true)
{
    Debug.Print("Horário Atual: " + DateTime.Now.ToString());
    Thread.Sleep(50000);
}

Quando você mandar rodar aparecerá a seguinte mensagem na janela de depuração:

Espero que tenha ajudado mais com esse artigo sobre servidores NIST no Netduino.

domingo, 11 de novembro de 2012

Getting Started with the Internet of Things - Parte 2

 O Cosm é um serviço bem legal para ver suas medidas na internet. Um bom passo é ler a documentação disponível no próprio site que informa como funciona o envio de dados para a plataforma online.

O primeiro passo é criar uma conta no https://cosm.com/. O Cosm é um serviço gratuito para nós pequenos desenvolvedores assim você pode ter uma plataforma online gratuita para enviar a informação dos seus medidores a acessar de qualquer lugar.

Depois de cirada a conta você deverá fazer algumas configurações para enviar os dados. A primeira delas é criar uma chave. A chave (Key) é uma primeira camada de controle de permissão, parece que no Pachube antigo isso não existia. Deste novo modelo com chave de acesso você pode criar permissões distintas de acesso a conta no Cosm. Por exemplo, você pode ter uma chave que permite a criação de novas chaves mas só pode ser feita por um endereço de IP específico, e você pode ter outras chaves com permissões restritas apenas para envio de dados. Da mesma forma você pode ter um dispositivo cliente que mostra dados acessando o Cosm por uma chave de leitura apenas.

Para criar uma chave nova vá em Keys, logo abaixo do seu nome de login no canto superior direito. Você deve informar um Label de identificação da chave, você deve informar atmbém como ele vai atuar com os alimentadores, os privilégios da chave e se quiser outras opções avançadas como filtro de IP, numero de acessos e até data de expiração da chave. Recomendo para o primeiro teste colocar uma chave com acesso a todos os serviços, muito embora só vou usar nesse posto para alimentar dados.

A chave será um código enorme como este: woxZM8LeIHOE2-2r0XTcthj_8XqSAKxzMmoxUS8zL0I4UT0g.

Agora já temos uma chave de acesso e devemos incluir um dispositivo que vai enviar dados para o serviço, na verdade não é bem um dispositivo mas é uma espécie de "concertador" de dados que pode enviar diversas informações para o Cosm. Para isso vá em "Console", no canto superior direto logo abaixo do seu nome de login do Cosm.

A página aberta estará vazia, clique no botão azul chamado "+ Device/Feed" e aparcerá quatro opções na tela: Arduino, Current Cost, Twitter Stats e Somithing Else; Selecione "Something Else".

São quatro passos para configurar:
  1. Método de envio de dados: Os dados de alimentação podem ser enviados do dispositivo para o Cosm ou o Cosm pode tentar acessar os dados através de uma requisição. A segunda opção é muito complicada porque geralmente o Netduino vai ficar dentro de uma rede doméstica que é protegida por um firewall do roteador e da provedora de internet assim o melhor mesmo é fazer o envio dos dados da placa do Netduino Plus para o Cosm. Por isso selecione a opção "No, I will push data to Cosm";
  2. Título do Dispositivo: apenas dê um nome para o seu dispositivo que vai se conectar ao Cosm, não faz diferença para a programação;
  3. Tags: Os tag's são os identificadores dos dados que serão enviados: por exemplo: Temperatura, Humidade, Energia, Vazão etc... Coloque os separados por virgulas.
  4. A ultima opção é só para confirmar.
Assim que você confirmar a operação aparecerá um dispositivo na lista com um gráfico para cada tag de medição que você criou, isso mostra que está tudo certo, como não teve medida nenhuma estará tudo limpo. Clique sobre sobre o título do dispositivo, os dados vaõ ocupar a página toda e a URL da página será do tipo https://cosm.com/feeds/83197. Esse código final é o Feed ID (83197), guarde esse número.

Ainda se você clicar sobre o as configurações no cantor superior direito do medidor, em "Edit" você poderá ainda colocar o local onde está o medidor, adicionar novas tag's e mudar aquela configuração inicial também. Agora a parte do Cosm está pronta, vamos a programação.

Para iniciar a programação tenha em mãos a Key e o Feed ID. O Meu código está disponível aqui. Vão notar que deixei minha chave e os meus tag's, fiz isso porque quando eu estva começando não tinha nem ideia de como era a chave e o Feed ID e demorei um dia tentando entender como funcionava o sistema. De qualquer forma pode utilizá-los, eu deixo. O código é copiado de um post do Forum do Netduino com algumas modificações.

Na primeira parte eu fiz a configuração do IP estático e do DNS estático também porque o Oi Velox é um lixo tão grande que dá problema de DNS. Essa parte é a configuração da camada de rede que uma vez configura o acesso aos serviços de internet do .NETMF tornam-se disponíveis. Tem gente que configura isso pelo MFDeploy mas eu particularmente gosto de ver configurado no código.

Lembre-se de conectar o cabo de rede antes da alimentação da placa, o Netduino Plus ainda está com esse problema.

Outra coisa que merce atenção é que o NetworkInterface não está como pacote default do Netduino assim você deve ir no projeto em "References", com o botão direito clicar em "Add Reference", e adicionar o System.Http, depois adicionar a linha ao topo do seu código "using Microsoft.SPOT.Net.NetworkInformation;".

NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticIP("10.20.19.122", "255.255.255.0", "10.20.19.1");
string[] NetworkDns = { "10.20.19.1", "200.222.122.133" };
NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticDns(NetworkDns);

Uma vez conectada a interface de rede você pode iniciar o envio de dados. Para enviar os dados você deve se conectar ao serviço do Cosm através da função conect(). Essa função cria um socket que é nada mais do que uma "tomada" que designa uma usada para ligar o dispositivo (Netduino Plus) a um ponto final específico da rede designado por um endereço de rede, nesse caso a URI do Cosm: api.cosm.com.
 
A função conect() também tem um atributo de timeout, quando o tempo máximo é atingido ele retorna um socket vazio, isso é feito porque o Cosm pode estar indisponível por algum problema nas camadas de rede  ou até mesmo o serviço fora do ar e desse modo o código pode processar outra atitude como gravar no cartão microSD, por exemplo o dado lido.

static Socket Connect(string host, int timeout)
{
    // look up host's domain name, to find IP address(es)
    IPHostEntry hostEntry = Dns.GetHostEntry(host);
    // extract a returned address
    IPAddress hostAddress = hostEntry.AddressList[0];
    IPEndPoint remoteEndPoint = new IPEndPoint(hostAddress, 80);

    // connect!
    Debug.Print("connect...");
    var connection = new Socket(AddressFamily.InterNetwork,
        SocketType.Stream, ProtocolType.Tcp);
    connection.Connect(remoteEndPoint);
    connection.SetSocketOption(SocketOptionLevel.Tcp,
        SocketOptionName.NoDelay, true);
    connection.SendTimeout = timeout;
    return connection;
}

Se tudo ocorrer bem na rede de internet será possível criar um socket e barir a comunicação com o serviço do Cosm e enviar dados. O Envio de dados é feito pela função SendRequest(). esta função recebe a chave (key), o Feed ID o socket criado e a informação que será enviada no formato CSV (Comma-Separated Format), ou formato separado por virgulas onde será enviado no corpo da mensagem a informação no estilo: "tag,valor".

Toda mensagem enviada por internet tem uma URI de requisição (requestLine), um cabeçalho (header)e um conteúdo (content). a reqisição é o endereço de onde será enviada a mensagem com o método de envio que neste caso é o método "PUT" "/v2/feeds/seu feedid.csv HTTP/1.1" que se soma a URL do site e acessa o serviço específico de dados do tipo CSV utilizando o protocolo HTTP 1.1. O Header, ou cabeçalho da mensagem contem as informações necessárias ao direcionamento da mensagem para sua conta mediante a chave de acesso, o host name e o tamanho do conteúdo da mensagem. e por fim a mensagem em formato de texto csv.

static void SendRequest(Socket s, string apiKey, string feedId,
    string content)
{
    byte[] contentBuffer = Encoding.UTF8.GetBytes(content);
    const string CRLF = "\r\n";
    var requestLine =
        "PUT /v2/feeds/" + feedId + ".csv HTTP/1.1" + CRLF;
    byte[] requestLineBuffer = Encoding.UTF8.GetBytes(requestLine);
    var headers =
        "Host: api.cosm.com" + CRLF +
        "X-ApiKey: " + apiKey + CRLF +
        "Content-Type: text/csv" + CRLF +
        "Content-Length: " + contentBuffer.Length + CRLF +
        CRLF;
    byte[] headersBuffer = Encoding.UTF8.GetBytes(headers);
    s.Send(requestLineBuffer);
    s.Send(headersBuffer);
    s.Send(contentBuffer);
}

O serviço do Cosm também permite outros formatos como JASON e XML mas você pode implementar depois pois muda apenas a sintaxe da mensagem.

Depois de ter tudo construído em texto basta enviá-los através do socket que foi aberto. E o código funciona assim em Loop Infinito e manda um dado a cada 20 segundos, aproximadamente. No meu código eu pego um valor aleatório e envio para o servidor do Cosm.

public static void Main()
{
    NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticIP("10.20.19.122", "255.255.255.0", "10.20.19.1");
    string[] NetworkDns = { "10.20.19.1", "200.222.122.133" };
    NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticDns(NetworkDns);

    const string apiKey = "woxZM8LeIHOE2-2r0XTctHb_8XqSAKxzMmoxUS8zL0I4UT0g";
    const string feedId = "83197";
    Socket connection = null;
    while (true)   // main loop
    {
        Debug.Print("time: " + DateTime.Now);
        Debug.Print("memory available: " + Debug.GC(true));

        if (connection == null)   // create connection
        {
            try
            {
                connection = Connect("api.cosm.com",1000 );
            }
            catch
            {
                Debug.Print("connection error");
            }
        }

        if (connection != null)
        {
            try
            {
                Random rnd = new Random();
                double value = rnd.NextDouble();
                string sample = "voltage," + value.ToString("f");
                Debug.Print("new sample: " + sample);
                SendRequest(connection, apiKey, feedId, sample);
                sample = "number," + (rnd.NextDouble()).ToString("f");
                SendRequest(connection, apiKey, feedId, sample);
            }
            catch (SocketException)
            {
                connection.Close();
                connection = null;
            }
        }
System.Threading.Thread.Sleep(20000);
    }
}
Espero que tenha sido útil todas essas informações para quem pretende acessar o serviço do Cosm e mandar dados pela internet. Aproveito para dizer que gostaria que vocês comentassem os post's para que eu continue melhorando e colocando o assunto que for mais pertinente.


Se você quiser ver meus gráficos pode acessá-los através do site do Cosm

Para baixar o arquivo pronto do projeto com as funções clique aqui.

sexta-feira, 19 de outubro de 2012

Getting Started with the Internet of Things - Parte 1

    Muito embora eu tenha á publicado artigos relacionas aplicações do Netduino Plus envolvendo rede de internet uma das maiores referências nessa parte é o livro do Cuno Pfister - Getting Started with the Internet of Things. O livro trata do Netduino Plus e aplicações para internet de um jeito fácil e rápido. 
    Nesta série de artigos pretendo explorar o livro e mostrar um pouco do que o livro fala ampliando a discussão para aplicações em automação doméstica e monitoramento/medição. Para quem quiser comprar o Livro: O'REILLY  Amazon.com. Um apêndice disponibilizado pelo Chris Walker no site do Netduino: AQUI.

       O livro começa mesmo no capítulo II: Dispositivos funcionando como clientes HTTP. Trata-se de um capítulo inteiramente baseado  no "Pachube", um serviço na nuvem especialmente dedicado ao armazenamento de informações provenientes de dispositivos pendurados na rede em fase de desenvolvimento. Certamente você não encontrará mais esse nome pela rede, na verdade o serviço agora se chama Cosm, o livro é de 2011, ou seja, está velho...

      O .NET MF disponibiliza duas implementações para aplicações HTTP: (i) uma em alto nível para tratar requisições no espaço System.Net e (ii) outra em baixo nível para envio de Socket no espaço System.

Alguns conceitos

     Antes de iniciar propriamente no dito cujo, vale a oportunidade de revisar alguns conceitos sobre internet e protocolos. O livro começa nessa parte de uma maneira bem emblemática jogando as informações mínimas relevantes.
     Assim como ocorreu na comunicação serial diversos protocolos começaram a ganhar corpo com o advento da internet, iniciando-se no uso militar da década de 70 e 80 os protocolos passaram por uma longa evolução até a chegada de um "modelo ideal" que permitisse de modo bastante universal enviar quase todo o tipo de informação por ele: o protocolo TCP/IP.

   O TCP/IP é, na verdade, não apenas um protocolo mas sim um conjunto de protocolos de comunicação entre dispositivos em rede chamado muitas vezes de "pilha TCP/IP". TCP quer dizer Transmission Control Protocol, Protocolo de Controlo de Transmissão, e IP significa Internet Protocol,  Protocolo de Interconexão. Pode-se dizer que trata-se de camadas de tratamento de transporte de informação em alto nível (complicado!!???), onde cada camada é responsável por um grupo de tarefas, fornecendo um conjunto de serviços bem definidos para o protocolo da camada superior. As camadas mais altas estão logicamente mais perto do usuário (chamada camada de aplicação) e lidam com dados mais abstratos, confiando em protocolos de camadas mais baixas para tarefas de menor nível de abstração.


     O modelo OSI (modelo de topologia da internet) é um grupo de sete camadas que foi tomado como base para a criação do TCP/IP. O TCP/IP é convencionalmente dividido em 4 níveis básicos: Host/rede; Inter-rede; Transporte; e Aplicação, muito embora exista na internet o modo "novo" com 5 camadas, que retira o excesso do modelo OSI e melhora o modelo TCP/IP: Física; Enlace; Rede; Transporte; e Aplicação.

      Para não delongar muito a explicação sobre isso pode-se dizer que trata-se de uma solução prática para problemas de transmissão generalizada de dados.


      Há de se convir que as camadas mais "altas", como mostra o diagrama, são mais inteligíveis por parte de nós usuários, enquanto aquelas mais abaixo estão logicamente mais perto da transmissão física do dado (os bits's efetivamente transmitidos). Podemos entender isso como uma programação em camadas: cada camada serve de funções a camada mais acima.

      O que queremos de fato é uma abstração dos detalhes de transmissão bits através da ethernet. Por exemplo, quando o Firefox envia a página não a necessidade de se preocupar com a detecção de colisão de bits, a camada Física se preocupa com isso evitando assim ter de conhecer os detalhes de todas as aplicações e seus protocolos.

Essa topologia permite que camadas de cima forneçam serviços que as camadas de baixo não podem fornecer como exemplo temos o IP que é projetado para não ser confiável. Isso significa que toda a camada de transporte deve indicar se irá ou não fornecer confiabilidade e em qual nível.

O TCP (Transmission Control Protocol - Protocolo de Controle de Transmissão), é um protocolo orientado a conexões confiável que permite a entrega sem erros de um fluxo de bytes.

O UDP fornece integridade de dados (via um checksum) mas não fornece entrega garantida; já o TCP fornece tanto integridade dos dados quanto garantia de entrega (retransmitindo até que o destinatário receba o pacote).


Vamos agora fazer um breve apanhado das camadas de rede:

(i) Camada de aplicação:
        Trata-se da camada utilizada por 99% dos programas de rede para comunicação através de uma rede com outros programas. Códigos desenvolvidos nessa camada tendem a ser mais simples sendo que os dados são tratados no programa já obedecendo o formato da aplicação que quando necessários são codificados dentro do padrão de um protocolo.
        Serviços que suportam diretamente aplicações do usuário são tratados nessa camada são eles o HTTP (navegação na World Wide Web), FTP (transporte de arquivos), SMTP (envio de email), SSH (login remoto seguro), DNS (pesquisas nome <-> IP) e muitos outros.

     
(ii) Camada de Transporte

        Esta camada é responsável por resolver problemas como confiabilidade, integridade e a direção que o dado deve ser transmisito/recebido. O roteamento de dados está nessa camada também muito embora considerado parte da camada de rede.
     
(iii) Camada de Enlace
        A camada da pele enlace é parte do modelo TCP/IP  liga a ancoragem da aplicação/transporte com o hardware que fará a transmissão da informação. Podendo ser controlado tanto em software (device driver) para a placa de rede quanto em firmware. Responsável por atribuir aos dados um cabeçalho preparando os dados para transmissão. Da mesma forma, a camada de enlace recebe os dados, retira os header's adicionados e encaminhar os pacotes recebidos para a camada de rede.
        Veremos mais sobre essa camada quando implementamos dispositivos externos para comunicação como o mIP faz no Netduino que empacota as mensagens para o ENC28J60 e outros "drivers"  para a wifly shield.

(iv) Camada Física


        A camada física do Protocolo TCP/IP trata das características elétricas e mecânicas do meio, como tipos de conectores e cabos utilizado para estabelecer uma comunicação.

Com essa breve introdução já é possível continuar a entender o que está no .NET MF para aplicações pela internet.

Devaneando Aplicações

     Eu gosto sempre de firmar uma proposição inicial como objetivo de algum estudo, no nosso caso o livro já faz uma referência ao objetivo: enviar dados de temperatura de um sensor montado no Netduino Plus por internet.
     Como eu já vinha dizendo existem muitos protocolos por Ethernet naturalmente o livro trata apenas do TCP/IP - o que se alega em favor é a modularidade - como existem muitos dispositivos adaptados a esse tipo de protocolo torna-se mais abrangente uma solução em TCP/IP via HTTP por exemplo.
     O protocolo mais usado usando TCP/IP é o HTTP ( Hypertext Transfer Protocol ). Através do HTTP é possível enviar mensagens e receber pacotes em qualquer computador ou dispositivo conectado na rede, o que é uma vantagem, assim, focaremos nele.
   

 


domingo, 23 de setembro de 2012

Comunicação Serial no Netduino - Parte 2

    Nessa segunda parte sobre a comunicação serial no Netduino pretendo falar um pouco do impacto do controle de fluxo na comunicação RS-232, quando se recomenda utilizá-la e porque muitos entusiastas de eletrônica simplesmente resolvem não utilizar essa parte importante da comunicação.


   Muitos fatores podem influenciar na transmissão dos dados entre duas aplicações, aqui vamos testar a comunicação serial TTL diretamente utilizando os pinos de 3,3V do Netduino para verificar alguns problemas que na primeira parte eu tratei com software, ou simplesmente não foi necessário de se tratar pela forma com que a solução foi enjambrada. 

       O teste em si é tão simples quanto o da primeira parte sobre comunicação serial mas nesse caso utilizaremos 2 placas de Netduino para estabelecer a comunicação entre elas e realizar alguns teste no canal (a interligação elétrica entre as duas placas...).

1ª Etapa - Teste do sincronismo da conexão


   Para simplificar um pouco mais vamos colocar uma placa apenas transmitindo e outra recebendo. O código abaixo é da placa de transmissão que envia os dados de periodicamente a pouco mais de um segundo entre os pacotes de informação. A mensagem é a mesma "Netduino!\n"
using System;
using System.IO.Ports;
using System.Threading; using Microsoft.SPOT;
using Microsoft.SPOT.Hardware; using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoPlus;
using System.Text; namespace NetduinoPlusSerialExample
{
    public class Program
    {
        static SerialPort serial;
        static OutputPort Led;         
  
  public static void Main()
        {
            // initialize the serial port for COM1 (using D0 & D1)       
            serial = new SerialPort("COM1", 56000,
                                    Parity.None, 8, StopBits.One);
            // open the serial-port, so we can send & receive data       
            serial.Open();
            // add an event-handler for handling incoming data       
         
            Led = new OutputPort(Pins.ONBOARD_LED, false); 
            while (true)
            {
                Led.Write(true);
                Thread.Sleep(1000);
                Led.Write(false);
                byte[] msg = 
                 Encoding.UTF8.GetBytes("Netduino!\n");
                serial.Write(msg, 0, msg.Length);
            }
        } 
    }
}

       O código de recebimento é apenas uma interrupção de recebimento praticamente iguala ao apresentado na parte 1 dessa série sobre a comunicação serial.
using System;
using System.IO.Ports;
using System.Threading; using Microsoft.SPOT;
using Microsoft.SPOT.Hardware; using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoPlus;
using System.Text; namespace NetduinoPlusSerialExample
{
    public class Program
    {
        static SerialPort serial;
        static OutputPort Led;         
  
  public static void Main()
        {
        
   // initialize the serial port for COM1 (using D0 & D1)       
            serial = new SerialPort("COM1", 56000,
                                    Parity.None, 8, StopBits.One);
            // open the serial-port, so we can send & receive data       

            serial.Open();

            // add an event-handler for handling incoming data       
            serial.DataReceived +=
                new SerialDataReceivedEventHandler(serial_DataReceived);
    Led = new OutputPort(Pins.ONBOARD_LED, false);             
    Thread.Sleep(Timeout.Infinite);         
     } 
  
  
        private static void serial_DataReceived(Object sender,
            SerialDataReceivedEventArgs e)
        {
            Thread.Sleep(100);            
   
   int bytesToRead = serial.BytesToRead;
            
   //start reading the stream
            if (bytesToRead > 0)
            {
                // get the waiting data
                byte[] buffer = new byte[bytesToRead];
                serial.Read(buffer, 0, buffer.Length);
                string restoredText =
                  new string(Encoding.UTF8.GetChars(buffer));
                Debug.Print(restoredText);                 
    
    //Resend
                serial.Write(buffer, 0, buffer.Length);                 Led.Write(true);
            } 
            Led.Write(false); 
        }     
 }
}
    

     Lembre-se de debugar apenas a placa que recebe os dados, e ligar a placa que transmite os dados depois. Feito isso você vai receber as mensagens "Netduino!" no seu console de saída de debug. Eu utilizei a alimentação de 3,3V da placa de recebimento para alimentar a placa de transmissão, note pela foto que o led de PWR ficou meio apagado, isso porque ele está ligado no 5V que eu não estou usando, mas a placa está ótima, só precisamos alimentar o 3,3V porque é o nível do processador.

       Agora podemos fazer os três testes no canal: o primeiro é tentar mandar uma mensagem muito grande tipo: "O vasco é vice pelos séculos dos seculos !\n". Até aqui tudo bem, se você repetir o procedimento de reprogramar o código na placa que transmite, debugar a placa de recepção e ligar em seguida a placa que transmite. 
     
       Na sequência repita o procedimento retirando previamente a pausa de 100ms na da interrupção serial_DataReceived().

//Thread.Sleep(100);

 Ocorrerá um erro durante a deugação! No meu caso a placa recebeu algumas mensagens na janela de output do debug e em seguida ocorreu um erro: "An unhandled exception of type 'System.Exception' occurred in mscorlib.dll". A titulo de curiosidade a saída do meu debug ficou assim:

Diamonds é um daqueles perfumes noturnos e amadeirados que faz qualquer mulher p
erder o rumo. Não
 passa dos li
mites em term
os de amadeir
ado e não ab
usa do olfato
 alheio.

Diamo
nds é um d
aqueles perfu
mes noturnos
 e amadeirados
 que faz qual
quer mulher p
erder o rumo.
 Não passa d
os limites em
 termos de am
A first chance exception of type 'System.Exception' occurred in mscorlib.dll
An unhandled exception of type 'System.Exception' occurred in mscorlib.dll

        A pergunta agora é: porque isso acontece. Isso acontece porque a serial não tem um bom controle de fluxo de mensagens, diferente dos protocolos de rede e de Ethernet e USB a serial é apenas o meio de comunicação e cabe a você fazer uma camada de código para tratar possíveis problemas de comunicação. Na forma que o hardware (o processador) é feito quando você envia uma mensagem de 8bits (1 char) de cada vez, e o primeiro recebimento (o primeiro char) faz o trigger da função de recebimento mas como você mandou uma frase inteira outros dados (char's) estão sendo enviados  e o tempo que demora até executar a função:

serial.Read(buffer, 0, buffer.Length);

Outros dados são recebidos mas de forma descontrolada em torno de 12 caracteres como podemos ver pela mensagem acima. O atraso fixo faz com que a mensagem seja toda recebi antes da leitura assim quando executamos a leitura da serial uma mensagem completa pode ser interpretada. O erro ao final é causado por esse atropelo entre os envios de mensagens e a interpretação do código.

2ª Etapa - Teste do byte danificado
 
      Um teste que você pode fazer é ligar a placa de transmissão antes de debugar o código de recebimento de mensagens na outra placa. Ocorrerá um erro também, esse erro é causado pela entrada de um pacote com um caractere que não pode ser interpretado pelo código ASCII  para um string e provoca um erro na linha:

 string restoredText =
                    new string(Encoding.UTF8.GetChars(buffer));

      Essa caractere é fruto ou de uma oscilação provocada no momento em que a energia foi conectada a placa ou por uma condição de imeout que leu o restante do byte como nivel lógico alto, é comum quando a leitura receber algo próximo de 0xFF nesses casos. 

      O que é de praxe entre os programadores é descartar esses erros e olhar a mensagem a frente, assim você pode resolver esses dois problemas com uma temporização de recebimento e um try{}cach{} para ignorar qualquer erro que venha prejudicar seu código.


3ª Etapa - Teste do comprimento da conexão

    O terceiro teste, é mais uma continuação do anterior, é o teste do comprimento de cabo, aqui eu fiz o teste usando um cabo de 60 metros para ver a perturbação no sinal e não ficou nada bom  como podem ver pelas imagens obtidas com o osciloscópio. Repare que o sinal de saída (sinal em amarelo obtido na extremidade do pino do receptor) oscila muito e o sinal na placa que transmite recebe uns spikes de comutação que podem até danificar a placa. 

    Logicamente o sinal nem era interpretado pelo receptor, as oscilações em níveis exagerados provocavam ruído de comutação causando erro na leitura do byte. 
    Esse teste fiz para que se note que a comunicação serial (TTL) é ruim para realização de transmissões mais longas.

As Soluções

     Falei muito dos problemas, mas vamos ver as soluções para os problemas que identificamos acima. O 3º item é o mais complexo de se resolver, diz respeito ao canal de comunicação, o que se faz é a compensação capacitiva mais conhecida pelos pessoal de rádio e TV como o "casamento de impedâncias". Uma proteção legal é colocar nos extremos uns diodos zener de 3,3V para limitar uma possível tensão elevada sustentada.

    Para resolver o 2º problema da entrada errada de sinal é utilizar o controle de fluxo, isso é, usar os pinos CTS e RTS que eu comentei anteriormente. Utilizá-los é bom mas não soluciona o problema por completo, em especial no caso de teste (contraditório eu sei), geralmente o controle de fluxo previne em especial problemas relacionados com baud rate

     Para entender como isso acontece primeiro necessita-se de uma breve explanasão sobre o sinal de disparo de envio; o sinal que dispara o envio é um clock interno do dispositivo transmissor que geralmente é compartilhado com outros periféricos e através de um ganho lógico se ajusta ao valor desejado. O casamento imperfeito entre o valor exato de de baud rate e o clock utilizado pode causar perda do sincronismo em pacotes muito grandes. Quando a mensagem a se enviar tem muitos bytes esse controle por CTS e RTS faz com que o clock de envio seja "sincronizado" com o clock do receptor a cada 9 bytes transmitidos fazendo com que um pequeno erro em cada transmissão não se acumule causando problemas de distorção de pacotes.

     Voltando ao problema 2, o pino RTS atua como uma prevenção de disparo de transmissão, ou seja se o RTS não tiver sido ajustado pelo receptor como "estou disponível para receber um dado" o dado não é enviá-lo assim a placa transmissora só armazena o dado no seu buffer interno para enviar quando disponível. No caso de desconexão entre os aparelhos é necessário um resistor de pull-up mantendo o RTS para que no momento que ele esteja desconectado o pino seja mantido em nível lógico alto.

    A mais complicada de resolver mesmo é a primeira porque necessita de um tratamento em termos de protocolo. Todos os testes que eu fiz pela serial envolvem envio de informações através de strings de texto mas de fato qualquer tipo de informação empacotada pode ser enviada. Veja o exemplo do projeto de controle eletrônico do RogerCom, ele utilizou um protocolo único em que cada mensagem de 8bits não significa uma letra mais sim uma mensagem em si.

    O que tem que ser feito é uma camada de tratamento que será implementada nos dois dispositivos que estão se comunicando, geralmente o que eu faço é  empacotar as mensagens com um terminador e um inciador, ex.: . Quando isso é lido pelo dispositivo receptor é possivel reconhecer através do sóftware que a mensagem está completa, tem um caractere inciador "<" e um terminador ">" e uma mensagem do tipo variável = valor numérico. Muito além o transmissor não precisa deliberadamente enviar suas mensagens, ele pode aguardar o receptor informar algo para ele transmitir a sua mensagem (já vendo o ponto de vista bidirecional) como por exemplo: ".


     Dessa forma, com um código próprio é possível realizar uma comunicação mais estável e segura, o receptor pergunta algo para o transmissor que detêm o dado, e fica aguardando, se responder ele verifica se está corretor caso contrário, não tenha respondido ou a mensagem esteja danificada ele realiza uma nova requisição ao transmissor.


      Na próxima parte sobre a comunicação serial vou apresentar a plaquinha serial que converte um sinal de TTL para diferencial e poderemos analisar o efeito no par trançado de cabos transmitindo a informação. Além disso vou fazer um driver para encapsular a mensagem como eu disse acima mandando também dados contidos em um arquivo de configuração.

sábado, 22 de setembro de 2012

Rio Oil & Gas 2012

   Galera esse post é mais uma explicação devido a essas 3 semanas sem postar nada no blog sobre o Netduino, essa plataforma genial e prática.
Rodrigo Martins, Sócio/Diretor da ENELTEC

   Como vocês já sabem eu tenho objetivos mais comerciais com a plataforma do Netduino, e foi buscando uma boa plataforma comercial para minhas aplicações que eu cheguei nele. Modularidade é a plavra de ordem hoje em dia e nada mais modular que o sokets o formato de Arduino que compatibiliza um grande numero de outras placas permitindo um desenvolvimento rápido mesmo quando não se tem um rumo bem definido ou seja lá uma estratégia...

   Contudo, a ENELTEC foi convidada a participar da Feira Rio Oil & Gas 2012 que aconteceu aqui no Rio de Janeiro mesmo reunindo diversas empresas do setor de petróleo e gás.

   Muito embora as aplicações ainda estejam em fase embrionária na empresa, meu chefe (que aparece na foto - achei melhor colocar a foto dele afinal ele é o chefe...) julgou de extrema relevância apresentar no evento os medidores de água e energia que estamos desenvolvendo. E assim foi feito, nessas duas semanas eu estive envolvido na construção de dois medidores: um de fluxo de água; e outro de energia - ambos utilizando o Netduino+.

   Aqui estão as fotos parciais do medidor, infelizmente serão só fotos que vou mostrar mesmo...

Medidor de Energia tipo tomada

Medidor de Vazão D'Água
   Os Medidores foram desenvolvidos para enviar os dados diretamente para o Google mostrando graficamente as medidas efetuada s através de um site. Aqui está o Link para o site de demonstração que eu usei no evento. Você vai notar que a medida está meio parada no tempo, isso ocorre porque eu removi os medidores, mas a ideia é que quando instalados de fato o site seja atualizado de minuto a minuto.

  O site em si dispensa muitas apresentações, tem uma visão 2D de um escritório fictício onde estão instalados dois medidores de energia (SALA 1 e SALA 2). Logo abaixo são apresentados os gráficos de potência consumida e o calculo da energia e do faturamento deveria estar aparecendo logo ao lado, mas não tive tempo de implementar mesmo sendo uma coisa bem simples de se fazer.

   Outra observação é que a foto do escritório é animada e clicando sobre o medidor 2 você poderá ver também dados de fator de potência e tensão na barra, mais a frente será implementado também THD e Nível der Harmônicas.

   Bom foi isso, eu realmente trabalhei bastante nisso nesses dias em especial em uma shield própria para medição de corrente e tensão, não posso dar muitos detalhes ainda sobre o projeto mas já está chegando em uma fase de lote piloto.


domingo, 2 de setembro de 2012

Comunicação Serial no Netduino - Parte 1

   A muito tempo tenho interesse em publicar algo sobre comunicação serial com o Netduino, como algumas pessoas me perguntaram sobre isso resolvi escrever um post dedicado no blog a comunicação serial.
    E como há de ser, fã da MICROCHIP não vivo sem uma comunicação serial. Mesmo fazendo tudo pela USB eu precisava de um serial para comunicar minhas velhas aplicações com o Netduino.

        A princípio pode parecer uma grande regressão mas a comunicação serial ainda estará em voga e continuará ainda pelos próximos anos devido aos velhos aplicativos de baixa velocidade. Convenhamos, para aplicações de baixa velocidade torna-se muito trabalhoso e difícil implementar a comunicação USB que acaba sendo por vezes um preciosíssimo. Uma certa vez me deparei com um projeto de iluminação, se tratava de uma solução específica onde desejava-se comunicar com computador para realizar uma apresentação de como seria uma iluminação de rua controlável por sensores, naquela época ainda tinha pouca experiência com PIC e foi muito prático, utilizando o osciloscópio, ver se os sinais estavam corretos e implementar essa comunicação no tempo que tinha disponível, poucas horas...


A Comunicação Serial:

       A comunicação serial, na essência, é um processo de enviar dados um bit de cada vez, sequencialmente, através de um único canal de comunicação. Para comprar, na comunicação paralela os bits de cada informação são enviados ao mesmo tempo através de canais de comunicação diferentes porem associados um ao lado do outro.

        O conceito é bem amplo a tal ponto que é possível dizer que a comunicação serial é usada em toda comunicação de longo alcance e na maioria das aplacações comerciais, onde o custo de cabos e as dificuldades de sincronização tornam a comunicação paralela impraticável. Mais ainda, em curtas distâncias, barramentos seriais estão se tornando cada vez mais comuns devido ao ponto em que as desvantagens dos barramentos paralelos (densidade de interconexão) somados a redução do custo de implementação da serialização da informação estão tornando a comunicação serial um caminho sem volta para os novos dispositivos embarcados.

        Além disso mesmo que seja entre periféricos na mesma placa a quantidade de pinos utilizada se reduz drasticamente, por exemplo, para enviar um byte de 8 bits seria necessário 8 pinos enquanto na serial apenas 1 seria o suficiente.

       Através desse conceito a Ethernet, CAN, I2C, SPI são todas comunicações seriais no entanto quando falamos de comunicação serial com um PC ou outras placas lembramos do conector DB9 dos PC's antigos que usa o protocolo RS-232/RS-485. Na verdade é isso mesmo, quando comecei a estudar tinha muitas dúvidas a respeito disso mas a bem da verdade é que 90% das vezes que ouvimos falar em comunicação serial estamos falando do padrão RS-232.
  Só para não deixar passar em branco, abaixo temos uma lista das principais comunicações seriais que peguei na wikipédia:

       Dessa lista podemos ver alguns protocolos muito utilizados como o SATA, que substitui o tradicional cabo paralelo que causava diversos problemas nos PC antigos para comunicação com discos rígidos, e o protocolo de Ethernet que ninguém importa pela forma de envio dos dados devido a quantidade de camadas mas é importante destacar que trata-se de uma comunicação serial também.


       Assim, o que nos interessa mesmo é a "interface serial" ou "porta serial", conhecida como RS-232. Se você tem 25 anos ou mias certamente lembra-se da época em que a USB não era tão conhecida e os dispositivos se conectavam nos PC's fabricados pela IBM pela porta serial, os conectores DB-9. Os equipamentos mais velhos utilizavam a porta de comunicação serial para comunicar-se com o PC, tais como modems, mouses, teclados, algumas impressoras, scanners e outros equipamentos de hardware. O padrão RS-232 foi originalmente definido para uma comunicação por meio de 25 fios diferentes, portas paralelas (chamavam de porta de impressora LPT1 com um conector grande DB-25). A IBM ao utilizar o padrão para o seu projeto do IBM-PC, definiu que apenas 9 pinos seriam necessários e esse se tornou o padrão de porta serial. O padrão de 25 pinos (DB-25) ficou conhecido como paralelo porque tratava-se de duas comunicações seriais que poderiam funcionar em paralelo duplicando a velocidade, não vou entrar nesses detalhes aqui.

       A pergunta que vem agora a mente é que vantagem tem em utilizar esse padrão de comunicação antigo e obsoleto. A grande vantagem é que não existe nenhuma camada entre os dados que você recebe através da porta serial e o seu código, assim você pode "ver"  os dados sendo transmitidos através do barramento sendo ideal para comunicação com dispositivos de baixa velocidade. A titulo de comparação podemos comparar com a comunicação USB: No padrão USB exitem camadas de código intermediárias por baixo da sua camada de comunicação sendo necessário um driver para que um computador compreenda qual o dispositivo e como deve tratar as informações trocadas tornando mais complicado a implementação de coisas mais simples além disso a velocidade da porta USB é por vezes tão alta que interferências da protoboard podem derrubar a comunicação.

       Se vocês já fizeram a atualização de firmeware do Netduino lembram-se do "GPS Camera Driver" que era utilizado pelo SAM7X para comunicar com o PC pela porta USB. Esse driver foi feito para o SAM7X emular uma comunicação serial pela USB, isso é tanto verdade que quando você inicia o SAM-BA você selecionava uma USB emulada para embarque do software. Essa emulação da serial pela USB tem sido uma solução implementada pela maior parte dos fabricantes para aplicações mas simples como embarque do software e debug de código (veja a launchpad da Texas e o Super-Arduino da MICROCHIP).

       Essa comunicação foi feita pela Eletronics Industries Association (EIA), o padrão RS-232 (versão mais conhecida é o RS-232-C mas isso não importa muito) em 1969!! Para maiores detalhes leia o artigo da wikipédia.

A conexão

       Há de pensar porque são necessários 9 pinos no conector embora a conexão serial por definição necessite apenas de uma via para enviar os dados de um dispositivo para outro. Na verdade ocorre que existem algumas ponderações a se fazer sobre isso. Destaco primeiramente o sinal de referência; é necessário que exista uma via para interconectar as referências entre os dois dispositivos granindo que o sinal de tensão entre o GND (referência) e a via que transporta o sinal sejam iguais. Em segundo lugar tem-se a questão da direção, é necessário que os dois dispositivos possa enviar e receber informações o que através de um único barramento poderia ocasionar colisão então é interessante usar duas vias uma para envio outra para recepção de bits.

       Com isso podemos montar a conexão serial mais elementar a três fios que correspondem a mais 70% das aplicações de desenvolvimento e hobby onde se utiliza os pinos 2, 3, e 5 do conector DB9. É importante ressaltar que os pinos de envio (chamemos de TX) e de recepção (chamemos de RX) entre os dois dispositivos que desejamos conectar devem ser cruzados de tal forma que o que é enviado pelo primeiro através do seu pino TX é recebido na porta RX do segundo.


       Os demais pinos são dedicados ao controle do fluxo de dados aqui em baixo tem os diagramas de pinos da RS-232 e a tabela informando uma breve descrição da funcionalidade:
PINO
 DB-25
DB-9
Abreviação
Descrição
Transmit Data
2
3
TX
Transmissão de dados.
Receive Data
3
2
RX
Recepção de dados.
Request to Send
4
7
RTS
Requisição de envio. Ativo  antes de cada transmissão.
Clear to  Send
5
8
CTS
Limpa para enviar.
Data Set Ready
6
6
DSR
Sinaliza que o modem ou periférico está pronto
GND
7
5
SG
Terra comum do sinal.
Carrier Detect
8
1
CD
Detecção de transporte
Data Terminal Ready
20
4
DTR
Indica que o terminal DTR está pronto para iniciar troca de informações
Ring Indicator
22
9
RI
Indica “toque de chamada”


       Agora que entendido os pinos é importante ainda ressaltar que o padrão de comunicação  utilizado  pelo  RS232 é diferente do utilizado pelo TTL (ou CMOS 3,3V disponível no Netduino),  onde  o nível 1 está associado a 5V e o nível 0 ao 0V. No padrão RS232, o nível "1" está associado  a  uma  tensão  de  –3V  a  –18V  enquanto o  "0"  está  associado  a  uma tensão de 3V a 18V. Qualquer tensão dentro desta faixa será entendido como "1" ou "0". Essa é uma confusão que eu fiz durante muito tempo e não compreendia a necessidade do chip MAX232 para realizar a comunicação pela serial e com uma porção de informação desencontrada na internet ficou dificil de entender isso a primeira vista.

       Então, o Netdiuno disponibiliza essa versão TTL (muito embora no nível de 3,3V) nos pinos D0 e D1 (COM1) e o controle de fluxo fica nas nos pinos 8 e 7 como pode ser visto na figura abaixo:


       Agora que sabemos onde está localizado o canal de RX e TX no Netduino continuemos a explanação sobre a comunicação porque ainda não comentei nada ainda sobre a mensagem. A mensagem é representada por bits de dados individuais, que podem ser encapsulados  em  mensagens de  vários bits.  Um byte  (conjunto de  8  bits)  é  um  exemplo de  uma unidade de mensagem que pode trafegar através de um canal digital de comunicações e essa é a implementação de hardware da maior parte das portas seriais: mensagens encapsuladas em bytes de 8 bits assim a cada 8 bits recebidos um "buffer" em hardware acumula em um byte se você enviar vários isso cria um chamado “frame”, ou seja, unidade de mensagem de maior nível. Esses múltiplos níveis de encapsulamento facilitam o reconhecimento de mensagens e interconexões de dados complexos em especial mensagens de texto.

        A maioria das mensagens digitais são mais longas que alguns poucos bits, no nosso caso de pequenos textos (é o que eu pretendo enviar são strings). Por não ser prático nem econômico transferir todos os bits de uma mensagem simultaneamente, a mensagem é quebrada em partes menores e transmitida sequencialmente. A transmissão bit-serial converte a mensagem em um bit por vez através de um canal. Cada bit representa uma parte da mensagem. Os bits individuais são então  rearranjados  no  destino  para  compor  a  mensagem  original.  Em  geral,  um  canal  irá  passar apenas um bit por vez.


Características da comunicação

        A taxa de transferência refere-se a velocidade com que os dados são enviados através de um canal e é medido em transições elétricas por segundo. Na norma EIA, ocorre uma transição de sinal por bit, por exemplo uma taxa de 9600 bauds  corresponde  a  uma  transferência  de  9600  dados  por  segundo,  ou  um  período  de aproximadamente, 104 µs (1/9600 s). Os valores típicos de baude são:


Baud Rate
2400 4800 9600 19200 38400 57600 115200


        Importante notar que a mensagem trafega livremente no canal no entanto nem todo o canal pode ser utilizado, a isso chamamos de "eficiência do canal de comunicação" que tem por definição o número de bits de informação utilizável (dados) enviados através do canal por segundo. Ele não inclui bits de sincronismo, formatação, e detecção de erro que podem ser adicionados a informação antes da mensagem ser transmitida, e sempre será no máximo igual a um.

        Podemos endagar o seguinte questionamento: quando identificar o inicio e o fim de um frame de mensagens? Os dados serializados não são enviados de maneira uniforme através de um canal de maneira ininterrupta. Os pacotes com informação regulares são enviados seguidos de uma pausa. Os pacotes de dados binários são enviados dessa maneira, e cabe ao usuário (você programador) criar o seu protocolo ajustando pela pausa ou por um caractere validador de inicio de frame. Dessa maneira o circuito receptor dos dados deve saber o momento apropriado para ler os bits individuais desse canal e mais ainda deve saber identificar quando começa a ser enviada a informação e qual o período entre os bits (Baude Rate). Caso o conhecimento desse período esteja comprometido perdas podem ser ocasionadas na mensagem.

         Esse tipo de comunicação é dita assíncrona, isso porque não exite nada que informe o envio da mensagem como um sinal de relógio, a informação trafega por um canal único e o receptor deve saber reconhecer o protocolo, ou seja, tanto o transmissor quanto o receptor devem ser configurados antecipadamente para que a comunicação se estabeleça. através de um contador o receptor irá gerar um sinal interno a partir do envio da primeira mensagem segundo o tempo configurado no baud rate para receber as mensagens subsequentes do mesmo buffer.  No protocolo EIA os dados são enviados em pequenos pacotes de 10 ou 11 bits, dos quais 8 constituem a mensagem. Quando o canal está em repouso, o sinal correspondente no canal tem um nível alto. Um pacote de dados sempre começa com um nível lógico baixo é atingido sinalizando o receptor que um transmissão foi iniciada. Esse bit iniciador é conhecido na literatura como sendo o “start bit” sendo o responsável também pela inicialização do temporizador interno no receptor avisando que a transmissão começou e que serão necessário verificar o canal em intervalos fixos para ler a mensagem. Logo após o start bit, 8 bits de dados de mensagem são enviados segundo o baud rate do transmissor. O processo é encerrado com o “stop bit” que é exatamente um bit de paridade, a única proteção para uma perda na mensagem .

         O bit de paridade pode ser entendido como o número de bits '1' que estão sendo enviados no byte da mensagem. Para validar a mensagem, é adicionado, no final de cada transmissão RS-232 um dígito binário de paridade. Assim o próprio hardware já faz uma validação da detectar erros nas transmissões, já que o seu cálculo é extremamente simples (para colocação em um chip dedicado).
Existem dois tipos de código de paridade que são configuráveis na maioria dos processadores: a paridade par e a paridade ímpar. A paridade será par quando o número de bits de valor '1' for par; caso contrário, será ímpar.


 Primeiros passos no Netduino

         Como eu fumei o meu Netduino, vou ter que fazer tudo no Netduino Plus. Mas podemos começar pelo mais simples: fazendo o Netduino transmitir um sinal pelo TX e recebê-lo pelo RX. E faremos isso colocando um jumper entres os pinos D0 e D1, como na figura.


         Feito isso, o passo seguinte é iniciar o Visual Studio 2010 e criar o projeto com esse código:

using System;
using System.IO.Ports;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoPlus;
using System.Text;

namespace NetduinoPlusSerialExample
{
    public class Program
    {
        static SerialPort serial;
        static OutputPort Led;

        public static void Main()
        {
            // initialize the serial port for COM1 (using D0 & D1)        
            serial = new SerialPort("COM1", 56000,
                                    Parity.None, 8, StopBits.One);
            // open the serial-port, so we can send & receive data        
            serial.Open();
            // add an event-handler for handling incoming data        
            serial.DataReceived += 
                new SerialDataReceivedEventHandler(serial_DataReceived);
                        
            Led = new OutputPort(Pins.ONBOARD_LED, false);

            //Send the first data!
            byte[] msg = Encoding.UTF8.GetBytes("Netduino!\n");
            serial.Write(msg, 0, msg.Length);
        }
        

        private static void serial_DataReceived(Object sender, 
            SerialDataReceivedEventArgs e)
        {
            Thread.Sleep(100);

            int bytesToRead = serial.BytesToRead;
            //start reading the stream 
            if (bytesToRead > 0)
            {
                // get the waiting data 
                byte[] buffer = new byte[bytesToRead];
                serial.Read(buffer, 0, buffer.Length);
                string restoredText = 
                    new string(Encoding.UTF8.GetChars(buffer));
                Debug.Print(restoredText);

                //Resend
                serial.Write(buffer, 0, buffer.Length);

                Led.Write(true);
            }

            
            Led.Write(false);


        }

    }
}


         Se você tiver um osciloscópio você poderá facilmente ver o sinal trafegando pelo jumper:


        O resultado dessa brincadeira é que o código envia um dado inicial que é recebido pelo próprio Netduino que volta a enviar o dado novamente pelo mesmo barramento COM1. Exite uma série de coisas a serem comentadas sobre esse código, começando pela declaração da SerialPort que é declarada como static.
        Quando se declara um variável como static significa que a variável pertence a classe em si não dando acesso a modificadores externos e preservando o valor para acesso que nesse caso também é feito, além do main(void), pela função estática de interrupção de recebimento.
        A construção da porta serial você pode utilizar os construtores da SecretLabs como no exemplo abaixo, ou usar como eu fiz no código anterior com string e valores numéricos conhecidos.

serial = new SerialPort(SerialPorts.COM1, 56000, Parity.None, 8, StopBits.One);

        Depois de configura a porta serial é aberta para comunicação e é atribuído um evento a porta para cada sinalização que dados estão disponíveis para ler entrar em uma função para tratar o recebimento; é possível também checar se a porta está com dados para serem lidos através de um loop infinito mas isso é pouco eficiente logo tratei de trazer para o blog algo melhor e mais prático de ser utilizado.
         Por fim, é feito o envio de uma informação de texto. Note que a função SerialPort.Write(byte[]) recebe um vetor de bytes para serem enviados, isso significa que o texto não pode ser enviado diretamente e uma etapa de serialização é necessária. Essa disposição do texto em um array de bytes é feita Encoding.UTF8.GetBytes() que pertence ao System.Text.

  
          Uma vez enviado o dado, caso não exita nenhum mau contato no fio que você utilizou como jumper,  a mensagem será recebida e a função serial_DataReceived() será chamada, note que no inicio eu faço o programa esperar 100ms isso ajuda para que a mensagem não fique "picotada". Lembre-se que ao enviar "Netduino!\n" estamos enviando um frame de 10 bytes e essa interrupção será chamada após a recepção do 1º byte, caso não espere terá a seguinte visão ao debugar o código:


      Você pode testar até com 1ms que a resposta é perfeita ainda. Por fim, fazemos o processo inverso descobrimos quantos bytes estão disponíveis para leitura, após 1ms todos os bytes já estão no buffer de recepção e a mensagem é transferida para a variável do tipo byte[] e mais uma vez é necessária uma conversão para torná-la um string que é feita logo em seguida através de um construtor de string.




        Notadamente, meus propósitos aqui são um pouco mais amplos que isso. O Netduino, inicialmente não permitia suporte a comunicação livre USB,  ou seja, a USB servia tão somente para fazer embarque e desembarque do software e não para aplicações um engano terrível que eu mesmo cometi quando comprei a placa, pensava que sua conectividade básica era pela USB e com isso seria possível fazer os mais diversas aplicações. Isso a primeira vista uma decepção com o Netduino e o pessoal que trabalhava comigo também teve a mesma impressão.

        Mas tudo tem uma solução, a final, não se tratava apenas de um interessado em utilizar aquela USB. No fórum do Netduino tem um post do Chris Walker falando sobre isso, como usar a USB com um exemplo de um dispositivo USB HID. O problema é que para isso ser feito a parte de embarque do software e debug tem que migrar para a serial UART.

        O Netduino permite que o programa seja embarcado pela UART nos pinos I/O 1 e 2. Naturalmente é interessante utilizar o MAX232 para realizar a conversão com uma placa própria , por isso tratei de fazer a minha própria placa (importar aqui no Brasil é um grande problema).

        Nos próximos artigos dessa nova série sobre a comunicação serial farei mais alguns testes e no final pretendo apresentar uma shield desenhada por mim para programação do Netduino pela porta serial e em seguida pretendo iniciar a comunicação com a USB.