sexta-feira, 15 de abril de 2016

Expansão de Entradas digitais e Analógicas

Esta semana estive trabalhando no firmware de um arduino para expandir as entradas digitais e analógicas da beaglebone. Basicamente desenvolvi um firmware que através de um protocolo de mensagens, envia pela interface serial as informações das entradas digitais e analógicas mapeadas dentro dele.
Fiz duas versões:

1) Protocolo proprietário com mensagem no padrão ASCII: este programa ainda não foi devidamente testado, mas creio que funcionará. A mensagem a ser enviada para obtenção dos valores digitais e analógicos é: 
Requisição: >ID,NOME_DO_PINO# -> Ex: >0001,A0# (4 bytes para o ID e 2 bytes para o nome do pino)

Resposta: >ID,NOME_DO_PINO,VALOR# -> Ex: >0001,A0,1024# (4 bytes para o ID, 2 bytes para o nome do pino e ATÉ 4 bytes para o valor)

Obs: As mensagens sempre terão em seu conteúdo o carácter de início '>', o carácter de separação ',' e o carácter de fim '#'.

2) Protocolo padrão modbus RTU: este programa está testado e funcional. Nele mapeei 8 entradas analógicas e 8 entradas digitais. Usei como base para desenvolvimento deste firmware a library modbus para arduino desenvolvido por André Sarmento e que está publicado no GIT HUB (clique aqui para acessar).
As funções para dar acesso aos valores dos periféricos mapeados são:

Para ler o estado das entradas digitais -> Read Discrete Inputs (0x02).
Para ler as amostras das entradas analógicas -> Read Input Registers (0x04).

Para informações do funcionamento do protocolo modbus busque santo google.

Proximo passo é colocar a serial da Beaglebone conversando com o Arduino e alterar o Programa 2 para ter acesso a essas entradas digitais e analógicas do Arduino.


sexta-feira, 1 de abril de 2016

Programa 2 - Aplicação de leitura das entradas analógicas e entradas digitais do hardware associados a cada Sensor

Este programa tem por objetivo capturar as amostras analógicas e os estados digitais de todos os sensores cadastrados num intervalo de tempo pré determinado. A quantidade de sensores e o intervalo de tempo para capturas de amostras são definidos pelos parâmetros SensorsQuantity e SensorsWaitTimeSample do arquivo de configuração "sensoresconfig".
O valores das amostras e dos estados dos sensores são salva no arquivo correspondente a cada sensor "sensorX" nos parâmetros SensorSampleValue e SensorOperStatus.
A plataforma utilizada é a BeagleBone Black rodando uma distribuição Linux. As funções de manipulação dos GPIOs e das entradas analógicas foram utilizadas as funções desenvolvidas pelo site (com pequenas modificações): https://www.adafruit.com/

Posso ainda fazer alterações no código, mas a príncipio ele está funcional. Futuramente, farei um diagrama de blocos e colocarei o código final aqui.

Programa 1 - Alteração no arquivo entPhySensorTable_data_access.c e entPhySensorTable_data_access.h

Basicamente, eu escrevi algumas linhas de código para ler os dados dos arquivos de configuração (descritos no post anterior) e salvá-los na estrutura de dados da mib ENTITY-SENSOR-MIB. Os arquivos alterados foram o entPhySensorTable_data_access.c e o entPhySensorTable_data_access.h.

O parâmetro SensorsQuantity do arquivo "sensoresconfig" é utilizado neste programa pra definir o tamanho da tabela de dados da mib. (Depois explicarei com maior detalhes, pois ainda isso é meio obscuro pra mim!)

Os parâmetros SensorType, SensorScale, SensorPrecision, SensorOperStatus, SensorSampleValue, SensorUnitsDisplay, SensorValueTimeStamp, SensorValueUpdateRate do arquivo "sensorX" são passados para a estrutura de dados criada para cada sensor para serem consultados quando o usuário chama as entidades da mib.


entPhySensorType = SensorType
entPhySensorScale = SensorScale
entPhySensorPrecision = SensorPrecision
entPhySensorValue = SensorValue (após conversão com os valores de SensorSampleValue, SensorARef e SensorConversionValue)
entPhySensorOperStatus = SensorOperStatus  
entPhySensorValueTimeStamp = SensorValueTimeStamp
entPhySensorValueUpdateRate = SensorValueUpdateRate



Lembrando que o parâmetro SensorSampleValue, SensorARef e SensorConversionValue são convertidos dentro do programa pra gerar o parâmetro SensorValue.

Função alterada no arquivo entPhySensorTable_data_access.c:


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
int
entPhySensorTable_container_load(netsnmp_container *container)
{
    entPhySensorTable_rowreq_ctx *rowreq_ctx;
 long   entPhysicalIndex = 0;

 char conteudoBuf[MAX_BUFFER_CONTEUDO];
 char bufAuxiliar[MAX_BUFFER_CONTEUDO];
 unsigned int quantidadeSensores, i;

 struct sensorTable_data sensorTemp;

    DEBUGMSGTL(("verbose:entPhySensorTable:entPhySensorTable_container_load","called\n"));

 if(leDadosArquivo(LOCAL_ARQ_SENSORESCONFIG, -1, conteudoBuf) == OK)
 {
  for(i=0; i < sizeof bufAuxiliar; i++) bufAuxiliar[i] = 0;

  if(leConteudo(conteudoBuf, SensorsQuantityText, bufAuxiliar) == OK)
  {
   quantidadeSensores = atoi(bufAuxiliar);
   if((quantidadeSensores > 0) && (quantidadeSensores <= MAX_SENSORES)) ;//flagAmostraStatus = TRUE;
   else {
    printf("\t> ERRO: Quantidade de sensores configurado incorretamente (Valor deve ser maior entre 1 a %d).", MAX_SENSORES); 
    printf("\t        Refaca as configuracoes dos sensores para obter as leituras.\n");
   }
  }
  else {
   printf("\t> AVISO: Dados incorretos.\n");
   printf("\t> ERRO: Refaca as configuracoes dos sensores para obter as leituras.\n");
  }
 }
 else printf("\t> AVISO: Arquivo nao pode ser lido.\n");


    rowreq_ctx = malloc(sizeof(entPhySensorTable_rowreq_ctx)*quantidadeSensores);

    while( entPhysicalIndex < quantidadeSensores ) {
     for(i=0; i < sizeof conteudoBuf; i++) conteudoBuf[i] = 0;
  for(i=0; i < sizeof sensorTemp.sensorUnitsDisplay; i++) sensorTemp.sensorUnitsDisplay[i] = 0;
     
  if(leDadosArquivo(LOCAL_ARQ_SENSORCONFIG, entPhysicalIndex, conteudoBuf) == OK)
  {
   for(i=0; i < sizeof bufAuxiliar; i++) bufAuxiliar[i] = 0;
   if(leConteudo(conteudoBuf, SensorTypeText, bufAuxiliar) == OK)
   {
    sensorTemp.sensorType = atoi(bufAuxiliar);
   } else { }

   for(i=0; i < sizeof bufAuxiliar; i++) bufAuxiliar[i] = 0;
   if(leConteudo(conteudoBuf, SensorScaleText, bufAuxiliar) == OK)
   {
    sensorTemp.sensorScale = atoi(bufAuxiliar);
   } else { }
   
   for(i=0; i < sizeof bufAuxiliar; i++) bufAuxiliar[i] = 0;
   if(leConteudo(conteudoBuf, SensorPrecisionText, bufAuxiliar) == OK)
   {
    sensorTemp.sensorPrecision = atoi(bufAuxiliar);
   } else { }

   for(i=0; i < sizeof bufAuxiliar; i++) bufAuxiliar[i] = 0;
   if(leConteudo(conteudoBuf, SensorSampleValueText, bufAuxiliar) == OK)
   {
    sensorTemp.sensorValue = atoi(bufAuxiliar);
   } else { }

   for(i=0; i < sizeof bufAuxiliar; i++) bufAuxiliar[i] = 0;
   if(leConteudo(conteudoBuf, SensorOperStatusText, bufAuxiliar) == OK)
   {
    sensorTemp.sensorOperStatus = atoi(bufAuxiliar);
   } else { }

   for(i=0; i < sizeof bufAuxiliar; i++) bufAuxiliar[i] = 0;
   if(leConteudo(conteudoBuf, SensorValueTimeStampText, bufAuxiliar) == OK)
   {
    sensorTemp.sensorValueTimeStamp = atoi(bufAuxiliar);
   } else { }

   for(i=0; i < sizeof bufAuxiliar; i++) bufAuxiliar[i] = 0;
   if(leConteudo(conteudoBuf, SensorValueUpdateRateText, bufAuxiliar) == OK)
   {
    sensorTemp.sensorValueUpdateRate = atoi(bufAuxiliar);
   } else { }

   for(i=0; i < sizeof bufAuxiliar; i++) bufAuxiliar[i] = 0;
   if(leConteudo(conteudoBuf, SensorUnitsDisplayText, bufAuxiliar) == OK)
   {
    memcpy(sensorTemp.sensorUnitsDisplay, bufAuxiliar, ((strlen(bufAuxiliar)+1) < MAX_LINE_SIZE) ? (strlen(bufAuxiliar)+1) : MAX_LINE_SIZE);
    sensorTemp.sensorUnitsDisplay_len = strlen(sensorTemp.sensorUnitsDisplay) * sizeof(char);
   } else { }

  }
  else {
   printf("\t> AVISO: Arquivo %s%d nao pode ser lido.\n", LOCAL_ARQ_SENSORCONFIG, entPhysicalIndex);
   printf("\t>     Dados DEFAULT retornados!\n");
   
   sensorTemp.sensorType = SENSOR_TYPE_DEFAULT;
   sensorTemp.sensorScale = SENSOR_SCALE_DEFAULT;
   sensorTemp.sensorPrecision = SENSOR_PRECISION_DEFAULT;
   sensorTemp.sensorValue = SENSOR_SAMPLE_VALUE_DEFAULT;
   sensorTemp.sensorOperStatus = SENSOR_OPER_STATUS_DEFAULT;
   memcpy(sensorTemp.sensorUnitsDisplay, SENSOR_UNITS_DISPLAY_DEFAULT, ((strlen(SENSOR_UNITS_DISPLAY_DEFAULT)+1) < MAX_LINE_SIZE) ? (strlen(SENSOR_UNITS_DISPLAY_DEFAULT)+1) : MAX_LINE_SIZE);
   sensorTemp.sensorUnitsDisplay_len = strlen(sensorTemp.sensorUnitsDisplay) * sizeof(char);
   sensorTemp.sensorValueTimeStamp = SENSOR_VALUE_TIMESTAMP_DEFAULT;
   sensorTemp.sensorValueUpdateRate = SENSOR_VALUE_UPDATE_RATE_DEFAULT;
   
  }
 

    /*
    ***---------------------------------------------***
    ***              END  EXAMPLE CODE              ***
    ***************************************************/

        /*
         * TODO:352:M: |   |-> set indexes in new entPhySensorTable rowreq context.
         * data context will be set from the param (unless NULL,
         *      in which case a new data context will be allocated)
         */


        rowreq_ctx = entPhySensorTable_allocate_rowreq_ctx(NULL);

        if (NULL == rowreq_ctx) {
            printf("rowreq_ctx==NULL\n");
            snmp_log(LOG_ERR, "memory allocation failed\n");
            return MFD_RESOURCE_UNAVAILABLE;
        }
        if(MFD_SUCCESS != entPhySensorTable_indexes_set(rowreq_ctx
                               , entPhysicalIndex
               )) {

            printf("entPhySensorTable_indexes_set!=MFD_SUCCESS\n");

            snmp_log(LOG_ERR,"error setting index while loading "
                     "entPhySensorTable data.\n");
            entPhySensorTable_release_rowreq_ctx(rowreq_ctx);
            continue;
        }

        printf("\nrowreq_ctx.tbl_idx.entPhysicalIndex = %d \n", rowreq_ctx->tbl_idx.entPhysicalIndex);
       
        
        
    /*
     * TODO:352:r: |   |-> populate entPhySensorTable data context.
     * Populate data context here. (optionally, delay until row prep)
     */
 
     
  rowreq_ctx->data.entPhySensorType = sensorTemp.sensorType;
  rowreq_ctx->data.entPhySensorScale = sensorTemp.sensorScale;
  rowreq_ctx->data.entPhySensorPrecision = sensorTemp.sensorPrecision;
  rowreq_ctx->data.entPhySensorValue = sensorTemp.sensorValue;
  rowreq_ctx->data.entPhySensorOperStatus = sensorTemp.sensorOperStatus;    
  rowreq_ctx->data.entPhySensorUnitsDisplay_len = sensorTemp.sensorUnitsDisplay_len;       
  memcpy( rowreq_ctx->data.entPhySensorUnitsDisplay, sensorTemp.sensorUnitsDisplay, sensorTemp.sensorUnitsDisplay_len );
  rowreq_ctx->data.entPhySensorValueTimeStamp = sensorTemp.sensorValueTimeStamp;
  rowreq_ctx->data.entPhySensorValueUpdateRate = sensorTemp.sensorValueUpdateRate;

 
 //snmp_log(LOG_ERR,"rowreq_ctx->data.entPhySensorUnitsDisplay_len = %d\n", rowreq_ctx->data.entPhySensorUnitsDisplay_len);
    //snmp_log(LOG_ERR,"rowreq_ctx->data.entPhySensorUnitsDisplay = %s\n", rowreq_ctx->data.entPhySensorUnitsDisplay);
    

        /*
         * insert into table container
         */
  entPhysicalIndex++;
        CONTAINER_INSERT(container, rowreq_ctx);
  
    }

    printf("inserted %d records\n", entPhysicalIndex);
    
    return MFD_SUCCESS;
} /* entPhySensorTable_container_load */

Funções criadas para manipular arquivos (estão no arquivo entPhySensorTable_data_access.c):


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*******************************************************************************
* Nome da Função : int leDadosArquivo(char *, int, char *)
* Retorna : inteiro (0 ou -1) p/ verificação de erro
* Criado por : Everson Osvanir da Silva
* Data De Criação : 17/03/2016
* Descrição : Abre o arquivo e armazena todo seu conteudo no buffer apontado
*     pelo char *conteudoBuf
* Notas : 
*******************************************************************************/
int leDadosArquivo(char *caminho, int indice, char *conteudoBuf)
{
 FILE *fp;
 char arquivoLocal[MAX_BUFFER_ARQUIVO];
 unsigned int i = 0;
 //char conteudoBuffer[MAX_BUFFER_CONTEUDO];

 if(indice >= 0) sprintf(arquivoLocal, "%s%d", caminho, indice);
 else sprintf(arquivoLocal, "%s", caminho);
 
 fp = fopen(arquivoLocal, "r");
 if(fp == NULL)
 {
  //DBG(DBG_TRACE, "Arquivo modo leitura nao pode ser aberto.");
  return -1;
 }
 
 i=0;
 while(!feof(fp)){      /* Enquanto n�o chegar ao final do arquivo */
  conteudoBuf[i]=getc(fp);                 /* imprime o caracter lido */
  i++;
 }
 fclose(fp);
 
 i--;
 conteudoBuf[i] = 0; //Limpa "lixo" no final do arquivo

 //DBG(DBG_TRACE, "CONTEUDO ORIGINAL:\n%s\n", conteudoBuf); 
 
 return 0;
}

/*******************************************************************************
* Nome da Função : int leConteudo(char *, char *, char *)
* Retorna : inteiro (0 ou -1) p/ verificação de erro
* Criado por : Everson Osvanir da Silva
* Data De Criação : 17/03/2016
* Descrição : Le o valor do conteudo do arquivo referente a linha que contem o
*     char *partConteudo.
* Notas : 
*******************************************************************************/
int leConteudoArquivo(char *caminho, char *partConteudo, char *valor)
{
 char buf[MAX_BUFFER_CONTEUDO];
 char *pBuf;
 unsigned int x, y=0;

 if(leDadosArquivo(caminho, -1, buf) == ERROR)
 {
  //DBG(DBG_TRACE, "Arquivo modo leitura nao pode ser aberto.");
  return -1;
 }
 
 pBuf = strstr(buf, partConteudo);
 
 if(pBuf == NULL)
 {
  //DBG(DBG_TRACE, "Arquivo nao contem o dado solicitado: %s", partConteudo);
  return -1;
 }

 for(x=0; &buf[x] != pBuf; x++);
 for(;buf[x] != CARACTER_SEPARACAO; x++);
 x++;
 for(;buf[x] != CARACTER_FIMLINHA; x++) 
 {
  valor[y] = buf[x];
  y++;
 }
 return 0;
}

/*******************************************************************************
* Nome da Função : int leConteudo(char *, char *, char *)
* Retorna : inteiro (0 ou -1) p/ verificação de erro
* Criado por : Everson Osvanir da Silva
* Data De Criação : 17/03/2016
* Descrição : Le o valor do conteudo do buffer referente a linha que contem o
*     char *partConteudo.
* Notas : 
*******************************************************************************/
int leConteudo(char *conteudoBuf, char *partConteudo, char *valor)
{
 char *pBuf;
 unsigned int x, y=0;

 //i = strlen(conteudoBuf);
 
 pBuf = strstr(conteudoBuf, partConteudo);
 
 if(pBuf == NULL)
 {
  //DBG(DBG_TRACE, "Arquivo nao contem o dado solicitado: %s", partConteudo);
  return -1;
 }

 for(x=0; &conteudoBuf[x] != pBuf; x++);
 for(;conteudoBuf[x] != CARACTER_SEPARACAO; x++);
 x++;
 for(;conteudoBuf[x] != CARACTER_FIMLINHA; x++) 
 {
  valor[y] = conteudoBuf[x];
  y++;
 }
 return 0;
}

Alterações no arquivo entPhySensorTable_data_access.h:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    /*
    ***************************************************
    ***             START EXAMPLE CODE              ***
    ***---------------------------------------------***/
/* *********************************************************************
 * Since we have no idea how you really access your data, we'll go with
 * a worst case example: a flat text file.
 */
#define MAX_LINE_SIZE 255
#define MAX_BUFFER_CONTEUDO 1024
#define MAX_BUFFER_ARQUIVO 256

#define OK 0
#define ERROR -1

/* DADOS DEFAULT PARA entPhySensorTable */
#define SENSOR_TYPE_DEFAULT 2 //unknown(2): unknown measurement, or arbitrary, relative numbers
#define SENSOR_SCALE_DEFAULT 9 //units(9),   -- 10^0
#define SENSOR_PRECISION_DEFAULT 1
#define SENSOR_UNITS_DISPLAY_DEFAULT "desconhecido ou nao operacional"
#define SENSOR_VALUE_UPDATE_RATE_DEFAULT 0
#define SENSOR_SAMPLE_VALUE_DEFAULT 0
#define SENSOR_VALUE_TIMESTAMP_DEFAULT 0
#define SENSOR_OPER_STATUS_DEFAULT 3 //nonoperational(3)


/* SENSOR CONFIGURACAO */
#define SensorTypeText "SensorType"
#define SensorScaleText "SensorScale"
#define SensorPrecisionText "SensorPrecision"
#define SensorUnitsDisplayText "SensorUnitsDisplay"
#define SensorValueUpdateRateText "SensorValueUpdateRate"
#define SensorSampleValueText "SensorSampleValue"
#define SensorValueTimeStampText "SensorValueTimeStamp"
#define SensorOperStatusText "SensorOperStatus"

/* SENSORES CONFIGURACAO */
#define MAX_SENSORES 7
#define SensorsQuantityText "SensorsQuantity"
#define SensorsWaitTimeSampleText "SensorsWaitTimeSample"

#define CARACTER_SEPARACAO '\t'
#define CARACTER_FIMLINHA '\n'

#define LOCAL_ARQ_SENSORCONFIG "/home/debian/cebra/confs/sensor"
#define LOCAL_ARQ_SENSORESCONFIG "/home/debian/cebra/confs/sensoresconfig"

#define N_TENTATIVAS 3
#define TENTATIVAS_OVERFLOW N_TENTATIVAS+1

struct sensorTable_data {
 unsigned long int sensorType;
 unsigned long int sensorScale;
 long int sensorPrecision;
 long int sensorValue;
 unsigned long int sensorOperStatus;
 char sensorUnitsDisplay[MAX_LINE_SIZE];
 size_t sensorUnitsDisplay_len;
 unsigned long int   sensorValueTimeStamp;
 unsigned long int   sensorValueUpdateRate;
};

int leDadosArquivo(char *caminho, int indice, char *conteudoBuf);
int leConteudoArquivo(char *caminho, char *partConteudo, char *valor);
int leConteudo(char *conteudoBuf, char *partConteudo, char *valor);
    /*
    ***---------------------------------------------***
    ***              END  EXAMPLE CODE              ***
    ***************************************************/

Pode haver alterações futuras nesses arquivos.

Arquivos de configuração e armazenamento de dados dos Sensores

Nas últimas semanas comecei o desenvolvimento da estrutura de sensores (com as entradas analógicas do hardware que estou usando). Criei, primeiramente, uma estrutura de arquivos que armazenarão as configurações dos sensores para a ENTITY-SENSOR-MIB, bem como, para armazenar as amostras analógicas e o estado dos sensores (obtido por um GPIO associado a cada entrada analógica). Esses arquivos serão acessados pelo SNMP, pela mib ENTITY-SENSOR-MIB,  para monitoramento dos parâmetro e também pelo programa que estou desenvolvendo para capturar as amostras analógicas e os estados dos GPIOs. Além disso, uma terceira aplicação que será desenvolvida acessará esses arquivos para escrever a configuração inicial (feito pelo usuário) dos parâmetros das entidades da mib ENTITY-SENSOR-MIB.

Abaixo listo os programas em desenvolvimento:
  • Programa 1 - Alteração no arquivo entPhySensorTable_data_access.c para dar acesso aos parâmetros dos sensores cadastrados: Conforme apresntado no post anterior, a partir da mib ENTITY-SENSOR-MIB eu gerei os arquivos .c e .h para desenvolver conforme minhas necessidades o acesso aos sensores da minha plataforma de hardware. Basicamente, alterei o arquivo .h para ler os parâmetros armazenados nos arquivos de configuração dos sensores.
  • Programa 2 - Aplicação de leitura das entradas analógicas e entradas digitais do hardware associados a cada Sensor: os dados lidos por este programa são armazenados nos arquivos de configuração.
  • Programa 3 - Aplicação para configuração dos parâmetros dos sensores pelo usuário: esses parâmetros estão associados aos parâmetros das entidades da mib ENTITY-SENSOR-MIB.


Composição dos arquivos

Os arquivos de configuração contém os parâmetros de cada sensor. São compostos por um arquivo geral chamado "sensoresconfig" que possuem alguns parâmetros em comum com todos os sensores e outros arquivos (correspondente a cada sensor), com o índice do sensor indicado no final do nome do arquivo. Denominados, portanto, "sensor0" a "sensorX".
Cada linha dos arquivos contém o nome do parâmetro precedido de um TAB e o seu valor correspondente. No fim de cada linha é terminado pelo código ASCII correspondente ao Enter.

O arquivo "sensoresconfig" contém:

SensorsQuantity 7
SensorsWaitTimeSample 3000

Esses dois parâmetros são comuns a todos os sensores.
O SensorsQuantity define a quantidade de sensores utilizados. Por enquanto, o máximo é 7, pois a plataforma utilizada (agora não mais o PCduino, e sim a Beaglebone Black) possui só 7 entradas analógicas.
O SensorsWaitTimeSample é o tempo em milissegundos de intervalo de captura de amostras. Este parâmetro é usado no programa 2 (descrito acima) para fazer a medição de todos os sensores ativos definido pela quantidade (SensorsQuantity) no intervalo de tempo definido neste parâmetro.

Os arquivos "sensorX" contêm:

SensorARef 1.8
SensorConversionValue 3.14
SensorType 1
SensorScale 1
SensorPrecision 1
SensorSampleValue 4095
SensorOperStatus 1
SensorUnitsDisplay Sensor de tensao
SensorValueTimeStamp 12135445
SensorValueUpdateRate 1000

Os parâmetros SensorARef e SensorConversionValue ainda não estão bem definidos, mas servirão pra fazer a conversão do valor de amostragem definida no parâmetro SensorSampleValue. O programa 1 (descrito acima) já passará para os parâmetro das entidades da MIB o valor convertido.
Os outros parâmetros listados no arquivo são referentes aos parâmetros das entidades da mib ENTITY-SENSOR-MIB.

sexta-feira, 11 de março de 2016

Gerando os arquivos .c e .h da mib ENTITY-SENSOR-MIB

1) Primeiramente baixar a versão dos arquivos fontes do net-smnp no site: http://www.net-snmp.org/.
A versão utilizada foi: net-snmp-5.7.3

2) Baixar a mib no site da cisco: http://tools.cisco.com/Support/SNMP/do/BrowseMIB.do?local=en&step=2&mibName=ENTITY-SENSOR-MIB

  • ENTITY-MIB.my
  • ENTITY-SENSOR-MIB.my
3) Renomeá-las:
  • ENTITY-MIB.my para ENTITY-MIB.txt
  • ENTITY-SENSOR-MIB.my para ENTITY-SENSOR-MIB.txt

4) Descompacte o arquivo "net-snmp-5.7.3.tar.gz" baixado no site do NET-SMNP.

5)  Na raiz da pasta "net-snmp-5.7.3/" execute os comandos a seguir:
./configure --prefix=/<caminho absoluto>/net-snmp-5.7.3/saida --disable-embedded-perl
Em seguida: make
Depois: sudo make install

6) Copie as mibs ENTITY-MIB e ENTITY-SENSOR-MIB para a pasta "./net-snmp-5.7.3/saida/share/snmp/mibs/"

Nas próximas etapas eu segui as informações do tutorial "MIBs For Dummies" do link: http://net-snmp.sourceforge.net/tutorial/tutorial-5/toolkit/mfd/index.html

7)Criei a pasta, junto com a sub-pasta: mkdir ENTITY-SENSOR-MIB/entPhySensorTable (fiz tudo dentro da pasta "saida", mas podia ser em qualquer lugar)

8) Executei o comando: MIBS="+ENTITY-SENSOR-MIB" ./../../bin/mib2c -c mib2c.mfd.conf entPhySensorTable (pasta atual: ./net-snmp-5.7.3/saida/ENTITY-SENSOR-MIB/entPhySensorTable)

  • MIBS="+ENTITY-SENSOR-MIB": é uma variável de ambiente que serve para apontar para a MIB que deseja usar com o comando mib2c
  • ./../../bin/mib2c: Após compilar o net-snmp-5.7.3 e instalar na pasta "./net-snmp-5.7.3/saida", os arquivos de configuração e binários ficam todos nessa pasta, inclusive o binário mib2c. Se desejar instalar o comando mib2c direto no sistema, basta baixar o pacote libsnmp-dev executando no shell: sudo apt-get install libsnmp-dev
  •  mib2c.mfd.conf: é o arquivo de configuração do mib2c feito para gerar arquivos .c e .h para usuários leigos em MIBs e SNMP.
  • entPhySensorTable: é o mib node descrito no arquivo da mib ENTITY-SENSOR-MIB.TXT
9) Nas perguntas que aparecem após executar o comando anterior eu respondi tudo com resposta DEFAULT (pressionando ENTER em todas elas).

10) Executei o comando: MIBS="+ENTITY-SENSOR-MIB" ./../../bin/mib2c -c mfd-makefile.m2m entPhySensorTable para gerar o arquivo makefile.

11) MIBS="+ENTITY-SENSOR-MIB" ./../../bin/mib2c -c subagent.m2c entPhySensorTable para gerar o arquivo subagent.

Os arquivos gerados foram:
  • entPhySensorTable.c
  • entPhySensorTable.h
  • entPhySensorTable_data_get.c
  • entPhySensorTable_data_get.h
  • entPhySensorTable_data_set.c
  • entPhySensorTable_data_set.h
  • entPhySensorTable_data_access.c
  • entPhySensorTable_data_access.h
  • entPhySensorTable_enums.h
  • entPhySensorTable_interface.c
  • entPhySensorTable_interface.h
  • entPhySensorTable_Makefile
  • entPhySensorTable_oids.h
  • entPhySensorTable-README-entPhySensorTable.txt
  • entPhySensorTable-README-FIRST.txt
  • entPhySensorTable_subagent.c

segunda-feira, 19 de outubro de 2015

Portando o NET-SNMP do Tiaguinho para o PCDuino

Inicialmente busquei seguir os passos que o Tiago fez para um projeto anterior ao qual ele criou uma MIB específica para medição de grandezas elétricas e instalou o NET-SNMP na plataforma de microcontroladores da Digi International.
Para iniciar, o Thiago me passou a sua Máquina Virtual Linux Ubuntu que ele utilizou para fazer o projeto do SMNP com suporte a mib ENTITY-SENSOR-MIB na plataforma da Digi.

Primeiramente o Thiago baixou a versão os arquivos fontes do net-smnp no site: http://www.net-snmp.org/
A versão que ele utilizou na época foi: net-snmp-5.7.1

Depois disso, ele baixou a versão 2 da ENTITY-SENSOR-MIB no site: http://tools.cisco.com/Support/SNMP/do/BrowseMIB.do?local=en&step=2&mibName=ENTITY-SENSOR-MIB


Compilando o NET-SNMP com a ENTITY-SENSOR-MIB

O próximo passo foi executar a compilação da net-snmp-5.7.1 carregando a ENTITY-SENSOR-MIB através dos passos a seguir:

1) Descompacte o arquivo "net-snmp-5.7.1.tar.gz" baixado no site do NET-SMNP.

2) A partir da MIB que o Tiago baixou, ele gerou arquivos .c e .h. Esse processo ainda está meio obscuro na minha cabeça e o Tiago não soube explicar com clareza. Portanto, deixarei para detalhar esse processo depois, quando tiver bem compreendido. Por agora, o que precisarei modificar para nossa aplicação será apenas um arquivo desses gerados. Os arquivos estão na pasta "inserirMIB". O arquivo modificado pelo Tiago para o projeto passado (e que terei que alterar para o projeto atual...) foi o "entPhySensorTable_data_access.c". Em outros posts detalharei essas alterações.

3) Com esses arquivos gerados da ENTITY-SENSOR-MIB, os mesmos devem ser copiados para as subpastas do net-snmp-5.7.1 da seguinte forma:

  • Coloque os arquivos pasta "net-snmp-5.7.1/mibs/":
    • ENTITY-MIB.txt
    • ENTITY-SENSOR-MIB.txt
  • Coloque esses arquivos na pasta "net-snmp-5.7.1/agent/mibgroup/ENTITY-SENSOR-MIB/entPhySensorTable/": 
    • entPhySensorTable.c
    • entPhySensorTable_data_get.c
    • entPhySensorTable_data_set.h
    • entPhySensorTable_interface.c
    • entPhySensorTable_subagent.c
    • entPhySensorTable_data_access.c
    • entPhySensorTable_data_get.h
    • entPhySensorTable_enums.h
    • entPhySensorTable_interface.h
    • entPhySensorTable_data_access.h
    • entPhySensorTable_data_set.c
    • entPhySensorTable.h
    • entPhySensorTable_oids.h
  • [CORREÇÃO] Crie um arquivo chamado "entPhySensorTable.h" na pasta "net-snmp-5.7.1/agent/mibgroup/" com as linhas:
    • config_require(ENTITY-SENSOR-MIB/entPhySensorTable/entPhySensorTable)
    • config_add_mib(ENTITY-SENSOR-MIB)
  • [DESNECESSÁRIO] Coloque os arquivos abaixo na pasta "net-snmp-5.7.1/agent/mibgroup/": 
    • entPhySensorTable.h
    • entPhySensorTable_data_set.h
    • entPhySensorTable_data_get.h
    • entPhySensorTable_enums.h
    • entPhySensorTable_interface.h
    • entPhySensorTable_data_access.h
    • entPhySensorTable_oids.h

4) Na raiz da pasta "net-snmp-5.7.1/" crie uma subpasta (exemplo, "net-snmp-5.7.1/saida/");

5) Na raiz da pasta "net-snmp-5.7.1/" execute os comandos a seguir:
  • ./configure --prefix=/<caminho absoluto>/net-snmp-5.7.1/saida --disable-embedded-perl --with-mib-modules="entPhySensorTable" --with-mibs="ENTITY-MIB"
  • Em seguida: make
  • Depois: sudo make install
Obs1: O comando "--with-mib-modules="entPhySensorTable" da sequência acima é relacionado em chamar o arquivo criado na etapa 3, localizado na pasta "net-snmp-5.7.1/agent/mibgroup/".

Executando o NET-SNMP e testando o funcionamento da ENTITY-SENSOR-MIB

Acesse a pasta de saída: "net-snmp-5.7.1/saida/" e execute os seguintes comandos:
  • Execute o comando "./bin/snmpconf -i -g basic_setup" para criar o arquivo de configuração "snmpd.conf" na raiz da pasta "saída". Se preferir e não souber configurar manualmente, crie esse arquivo e edite com a seguinte informação:
      • rwcommunity public
    • Execute este comando para rodar o SNMP: 
      • sudo ./sbin/snmpd -f -M ./share/snmp/mibs -Lf snmpd.log -c snmpd.conf <numero de ip>:<porta> (ex., 127.0.0.1:161)
    • Em outra janela do terminal, execute o seguinte comando para testar a MIB já adaptada para o projeto anterior do Tiago (alterei algumas coisas no arquivo citado anteriormente para fazer os testes):
      • ./bin/snmpwalk -v1 -c public <numero de ip>:<porta> ENTITY-SENSOR-MIB::entPhySensorValue

    O próximo passo é instalar uma versão mais recente do NET-SNMP e fazer o mesmo porte da ENTITY-SENSOR-MIB. Aguardem os próximos posts!!!






    terça-feira, 13 de outubro de 2015

    Apresentação

    Boa Tarde pessoal,

    Este blog foi criado para relatar os avanços do projeto intitulado "SMART GRID CEBRA: Gerenciamento Remoto de Conversores Estáticos Empregando Protocolo SNMP e Serviços Web para Empresa CEBRA LTDA" e submetido a chamada "Chamada CNPq-SETEC/MEC N º 17/2014 - Apoio a Projetos Cooperativos de Pesquisa Aplicada e de Extensão Tecnológica", tendo como coordenador o professor Roberto Alexandre Dias. A instituição executora do projeto é o Instituto Federal de Educação Ciência e Tecnologia de SC - IFSC e a instituição parceira é a empresa Conversores Estáticos Brasileiros LTDA - CEBRA.

    O documento de submissão do projeto pode ser baixado clicando aqui.