domingo, 17 de junho de 2012

Escrevedo no Cartão SD - Parte 1


No trabalho temos alguns registradores de qualidade de energia, são medidores bem avançados capazes de medir até o 31º harmônico além de medir a tensão, corrente, potência etc.. O único problema são os ridículos 2 MB de memória disponível para armazenar os dados das leituras efetuadas o que nos leva a um triste exercício de reflexão sobre quantas amostras temos que tomar para otimizar os 2MB de memória.

Pois bem, mês passado coloquei o medidor em um cliente para 7 dias de medição. Infelizmente cometi um erro confundindo-me 10 minutos com 10 segundos e com isso perdi uma semana de medição porque a memória do medidor acabou e só pude colher dados durante as 12horas inicias.
Esse episódio me fez pensar em como são engessadas essas tecnologias nacionais em termos de armazenamento e esse medidor custou o preço simbólico de R$ 5.700,00. Bom, lamentos a parte vamos ao que interessa de fato - o armazenamento no cartão SD do Netduino Plus.

Armazenar dados é uma coisa importante para qualquer aplicação que se tenha em vista, em especial quando se deseja monitorar alguma coisa. Meu foco aqui será ater-se a escrita no cartão SD por isso, pretendo implementar um projeto simples: monitorar um sensor de luz do sol  se está escuro ou claro aberta ou fechada. Muito embora seja simples apresentarei com isso um evento de escrita no cartão SD acionado por um sensor externo, o que é bem interessante.

Vamos então ao primeiro passo da brincadeira, atualizar o Netduino Plus. Eu atualizei o meu para o 4.2 RC5 que tem uma classe chama "StorageDevice.MountSD" que não tinha no 4.1, por isso tenha certeza que está rodando o .NET Micro Framework4.2.

Eu fiz um código exemplo, que a pesar de simplificado, ajuda a entender algumas coisas sobre o cartão SD. Lembre-se que você deve adicionar nas referências do projeto SecretLabs.NETMF.IO . Feito isso o restante é copiar o código.
__________________________________________________________________________________________
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.IO;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoPlus;

namespace SD_Card_Example_01
{
    public class Program
    {
        //Isso é para codificação UTF8, não sei como funciona ao certo.
        private static System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
        public static void Main()
        {
            //Inicializa o Led e o Botão da placa
            OutputPort bord_led = new OutputPort(Pins.ONBOARD_LED, false);
            InputPort bord_button = new InputPort(Pins.ONBOARD_SW1, false, Port.ResistorMode.Disabled);
           

            while (true)
            {
                //Sempre que o botão for precionado faça:
                if (bord_button.Read())
                {
                    //Acessa o Cartão SD e cria/acessa (cria se não existir ainda)                            // um arquivo
                    //chamdo "fileTest.txt" onde os dados serão salvos
                    FileStream log_file = new FileStream(@"\SD\fileTest.txt",
                                                                     FileMode.Append);

                    //Cria uma mensagem de teste que será escrita toda a vez
                    //precionado na placa do netduino
                    string msg = "Isso é um teste do cartão SD! ";

                    //Escreve o dado no arquivo
                    log_file.Write(encoding.GetBytes(msg), 0, msg.Length);

                    //Fecha o acesso ao cartão
                    log_file.Flush();
                    log_file.Close();
                    log_file.Dispose();

                    //Pisca o Led 10x para comprovar que o o dado foi escrito mesmo.
                    Int16 i = 0;
                    for (i = 0; i < 10; i++)
                    {
                        bord_led.Write(!bord_led.Read());
                        Thread.Sleep(100);
                    }

                    //Dorme um pouco só por garantia
                    Thread.Sleep(100);
                }
            }
            Thread.Sleep(Timeout.Infinite);
        }
__________________________________________________________________________________________

 Este é um código simplificado que eu reuni através do post's no fórum do Netduino. Debugando o código algumas centenas de vezes é possível ver o Led piscando cada vez que o botão da placa é pressionado.

Desligando a placa do Netduino e conectando o cartão SD no computador , pode-se abrir o arquivo com o bloco de notas e verá o resultado, as mensagens escritas em sequência.


 Nesse primeiro exemplo não me preocupei muito com tabulação e a forma com que os dados serão interpretados depois, o importante é constatar quão simples foi o código e o universo de possibilidades que o cartão pode trazer. Em um primeiro momento a escrita sequencial dos dados, com um tempo de amostragem fixo pode ser um primeiro passo para a aquisição de dados. Além dessa função é possível, também, ler um arquivo simples de configuração tal como os tradicionais *.ini do Windows que utilizam a sintaxe "variável=valor;". Muitas outras formas de usar o cartão SD podem ser elaboradas e utilizadas na prática como salvar uma página de HTML e usar o Netduino Plus como Host de página.


Aqui vale uma breve explicação quanto aos Cartões SD de grande capacidade e o chinas (cartões fake). Comprei um cartão de 2Gb  da Kingston por R$5,00 aqui no rio meses atrás para usar no Netduino, como na época estava focado em outros projetos um amigo meu iniciou os testes e constatou que o cartão não era reconhecido pela placa do Netduino Plus. Logo depois de alguns post trocados no forum do Netduino verificou-se que o cartão era um Kingston falso e não tinha comunicação SPI.  Os cartões SD possuem três padrões de comunicação SDIO1, SDIO3, e SPI - a SPI não é obrigatória nos cartões SD de grande capacidade (acima de 1Gb) assim muito cartões SD não tem essa comunicação por isso é importante ter atenção na hora de comprar o cartão SD compatível com o Netduino,a além da limitação de capacidade de 2Gb ainda tem a limitação da comunicação.

Teste com os cartões originais da Sony e SanDisk e da Kingston de 2Gb e tudo saiu bem.

Mudando o foco um pouco para a aplicação, o sensor de luz do tipo LDR (Light Dependent Resistors) tem uma resistência de 60,0 kΩ - Sem Luz e 1,0 kΩ - Com Luz do Dia  uma faixa bem ampla. Alimentando o sensor diretamente com 3,0V com um resistor de aterramento de  5k tem-se uma tensão de de 2,50V quando o LDR for excitado com luz e 0,23V no inverso (sem luz incidindo) o que é razoável analisando pelo datasheet esses níveis lógicos alto e baixo.


A foto mostra o LDR montado para o teste junto com Netduino Plus. O teste foi bem simples e código ficou um pouco mais elaborado para controlar o tempo através de uma interrupção periódica, fiz isso para deixar essa medição independente de outras tarefas que futuramente pretendo implementar. De modo geral o código e bem simples de entender - uma classe separada foi criada para manipular a checagem da luz (LightCheck) essa classe tem as variáveis internas: o tempo de interrupção e a data atual, ambas passadas do código principal através do construtor. Uma vez construída e  atribuido o evento ao LightCheckProcessEvents a função é executada independentemente.

__________________________________________________________________________________________ namespace SD_Card_Example_01
{
    public class Program
    {
        //Inicialização do código
        public static void Main()
        {
            //Inicializa o Led e o Botão da placa
            InputPort bord_button = new InputPort(Pins.ONBOARD_SW1, false,
                                     Port.ResistorMode.Disabled);
            //Cria a Data Atual
            DateTime CurrentDate = new DateTime(2012, 06, 17, 12, 02, 00);
            Int16  InterruptTime = 1000;

            //Criando o Evento periódico de tempo que chama a função LightCheckProcessEvents
            //para escrever no cartão a condição de luz e o tempo
            AutoResetEvent autoEvent = new AutoResetEvent(false);
            LightCheck LightCheckObject = new LightCheck(CurrentDate, InterruptTime);
            TimerCallback LightCheckCallBack= LightCheckObject.LightCheckProcessEvents;
            Timer LightCheckTimer = new System.Threading.Timer(LightCheckCallBack, autoEvent, InterruptTime, InterruptTime);

            Thread.Sleep(Timeout.Infinite);
        }
    }

    public class LightCheck
    {
        private static System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();

        // Global
        DateTime InternalDate;
        Int16 InternalInterruptTime;
        OutputPort bord_led = new OutputPort(Pins.ONBOARD_LED, false);
        InputPort  bord_ldr = new InputPort(Pins.GPIO_PIN_D0, false, Port.ResistorMode.Disabled);

        ///

        /// Contrutor, passa a data corrente paa inicio dos registros
        ///

        /// Data Atual
        /// Tempo da Interrupção em milisegundos
        public LightCheck(DateTime date, Int16 InterruptTime)
        {
            InternalDate = date;
            InternalInterruptTime = InterruptTime;
        }

        ///

        /// Processa os eventos basenados na interrupção periódica
        ///

        /// Objeto que Chama
        public void LightCheckProcessEvents( object o )
        {
            //quando chama aumenta o tempo
            InternalDate.AddMilliseconds(InternalInterruptTime);

            //Acessa o Cartão SD e cria/acessa (cria se não existir ainda) um arquivo
            //chamdo "fileTest.txt" onde os dados serão salvos
            FileStream log_file = new FileStream(@"\SD\light.txt", FileMode.Append);

            //Cria uma mensagem de teste que será escrita toda a vez que o botão for
            //precionado na placa do netduino
            string msg = InternalDate.ToString("G") + "\t" +
                                   bord_ldr.Read().ToString() + "\n";

            //Escreve o dado no arquivo
            log_file.Write(encoding.GetBytes(msg), 0, msg.Length);

            //Fecha o acesso ao cartão
            log_file.Flush();
            log_file.Close();
            log_file.Dispose();

            //Pisca o Led 10x para comprovar que o o dado foi escrito mesmo.
            Int16 i = 0;
            for(i = 0; i < 10; i++)
            {
                bord_led.Write(!bord_led.Read());
                Thread.Sleep(100);
            }
        }
    }
} __________________________________________________________________________________________

O resultado disso é uma execução continuada e periódica do código, onde a data é incrementada e a mensagem é escrita dizendo se o ambiente estava iluminado ou não. Nesse código o tempo de interrupção é de 10 segundos, baixo devido ao teste, acredito que a cada 60s seja uma amostragem boa.
 

A conclusão desse tópico é que podemo escrever no Cartão SD com muita facilidade utilizando o Netduino Plus o que faz dele uma plataforma muito mais poderosa e rápida de se programar se comparado com os tradicionais Arduíno e outras famílias de placas de desenvolvimento. No próximo tópico será discutida leitura do cartão SD onde será possível ler a data de partida do cartão SD.


2 comentários:

  1. Olá Victor,

    já agradeço de antemão pelo post, pois me ajudou muito em um projeto no qual estou envolvido, porém gostaria de saber se é possível fazer a extração dos dados outro formato de arquivo, como um .xls por exemplo. Já dei uma pesquisada rápida, mas não encontrei nada.

    parabéns pelo blog.

    att Rubens A Lopes.

    ResponderExcluir
  2. Olá Rubens,
    É possível sim, eu fiz um programa no MATLAB para fazer essa leitura do arquivo para conversão para excel e também para plotar como gráfico. A noite vou postar o código aqui no blog. Como não tenho MATLAB em casa vou converter para scilab.

    [abs],
    Victor

    ResponderExcluir