DevelopOverview.rst 24.1 KB
Newer Older
Nelso Jost's avatar
Nelso Jost committed
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
###########
Visão geral
###########

Este documento procura oferecer uma visão geral sobre o funcionamento do código deste projeto, útil para aqueles que desejam modificá-lo ou simplesmente endendê-lo. 

Estrutura de arquivos
*********************

Segue uma breve descrição dos arquivos/diretórios presentes na pasta raiz do projeto:

.. code-block:: shell

    arduino-meteorolog/
    ├── data/                # contém dados gerados pelo logger
    ├── docs/                # arquivos de documentação
    ├── logger/              # software que faz coleta de dados e envio para o servidor
    ├── meteorolog/          # projeto ".ino" do firmware (compilável pela Arduino Toolchain)
    ├── scripts/             # scripts utilizados pelo Makefile
    ├── settings.ini         # configurações do logger
    └── Makefile             # proporciona diversos comandos para fácil utilização

O arquivo ``Makefile`` contém diversos comandos [1]_ curtos para facilitar a realização de diversas operações de instalação e manutenção de todos os softwares do projeto. Assim, basta estar presente nesta pasta raiz e executar:

.. code-block:: shell

    $ make <comando>

para realizar alguma tarefa. Experimente ``make help`` para listar todos os comandos possíveis.

Funcionamento
*************

Essa estrutura existe para comportar os dois seguintes softwares:

* **Firmware**: Executado no processador do Arduino, é responsável por ler os sensores conectados de acordo com solicitações enviadas à porta serial.

* **Logger**: Executado em uma máquina Linux (PC, Raspberry, etc), é responsável por coletar dados da placa através de uma leitura serial, fazer armazenamento local e também remoto (envio de dados para [dados.cta.if.ufrgs.br/emm](dados.cta.if.ufrgs.br/emm)).

Ambos encontram-se no mesmo repositório pois o logger está preparado para enviar comandos pela serial cujo formato o firmware está preparado para receber. Por exemplo, considere a seguinte string enviada pelo logger à porta serial onde está a placa Arduino::

    readSensors,LDR,DHT22_TEMP

Ao receber esses caracteres, o firmware determinará que trata-se de um comando para leitura de sensores e que os sensores a serem lidos são, nessa ordem: o ``LDR`` e o ``DHT22_TEMP`` (luminosidade e temperatura, respectivamente). O firmware retorna pela serial uma resposta com números separados por vírgula, algo como::

    84.1,24.5

indicando 84,1 % de luminosidade e 24,5 ºC de temperatura. De acordo com o comando enviado, o logger estará preparado para receber uma resposta com dois valores **CSV** (*comma-separated-values*), que serão guardados juntamente com a hora do sistema em um arquivo de log local. Adicionamente, o logger também fará uma tentativa de envio ao servidor remoto. Caso falhe, será guardado em um arquivo ``outgoing.json`` para tentativas futuras.

########
Firmware
########

Escrito na linguagem C++ suportada pela Arduino Toolchain, pode ser compilado utilizando a IDE do Arduino ou pelo terminal através de ``$ make firmware``.

Estrutura de arquivos
*********************

.. code-block:: shell

    meteorolog/
    ├── libs/                         # bibliotecas de terceiros
    │   ├── Adafruit_BMP085.cpp       
    │   ├── Adafruit_BMP085.h         # sensor BMP085 (licença BSD)
    │   ├── DHT.cpp 
    │   ├── DHT.h                     # sensor DHT11 e 22 (licença MIT)
    │   ├── RTClib.cpp
    │   └── RTClib.h                  # relógio RTC DS1307 (domínio público)
    ├── meteorolog.ino                # setup() e loop() da Arduino Toolchain
    ├── mysensors.cpp
    ├── mysensors.h                   # leitura dos sensores disponíveis
    ├── boardcommands.cpp
    ├── boardcommands.h               # execução de comandos para a placa
    ├── utils.cpp 
    └── utils.h                       # utilidades suplentes da Arduino Toolchain

.. note:: Os arquivos ``.h`` (cabeçalhos) contém os protótipos juntamente com a documentação do código implementado nos ``.cpp``.

O ponto de entrada é o arquivo ``meteorolog.ino``, pois ele define as duas seguintes funções padrões de um Sketch Arduino:

* ``setup()``

    Executada uma vez quando a placa é ligada, inicializa a comunicação serial e chama ``mysensors_setup()``, que fará inicialização dos sensores.

* ``loop()``

    Executada enquanto a placa estiver ligada, verifica constantemente se há caracteres disponíveis na porta serial. Caso afirmativo, lê a string ali presente e encaminha ela para ``execute_board_command()``. Essa função, por sua vez, interpreta o comando presente na string recebida e retorna uma string como resposta, que é então devolvida para a porta serial e o loop recomeça.

boardcommands.h
***************

Os comandos esperados pelo firmware constituem strings no seguinte formato CSV::

    nomeDoComando,arg1,arg2,...,argN

Essa string contendo o comando e seus argumentos é enviada para ``execute_board_command()``, que interpretará a parte inicial ``nomeDoComando`` para delegar uma ação apropriada. O retorno de ``execute_board_command()`` é uma string contendo a resposta do comando ou, em caso de erros (comando inexistente, argumentos insuficientes, etc)::

    <invalid_commmand:nomeDoComando,...>

readSensors
===========

A leitura dos sensores é feita pelo seguinte comando da placa::

    readSensors,nome1,nome2,...,nomeN

onde os argumentos ``nome1,nome2,...,nomeN`` são transmitidos para ``read_sensors()``, que fará as solicitações de leitura. Essa função itera sobre cada nome/apelido, passando o mesmo para ``call_read_sensor()`` de modo que a função correta de leitura seja invocada.

Por exemplo, sejam os dois seguintes sensores passados como argumento::

    LDR,p

O primeiro deve levar à execução da função ``read_LDR()`` e o segundo, à execução de ``read_BMP085_PRESSURE()`` (pois ``"p"`` é um apelido para ``BMP085_PRESSURE``). Ambas funções não recebem nenhum argumento e retornam uma string contendo, presumivelmente, o número medido ou um indicador de erro conforme programado em `my_sensors.h <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/meteorolog/mysensors.h>`_.

A operação de ``call_read_sensor()`` depende então de mapear-se uma string como ``"LDR"`` para um ponteiro da função ``read_LDR()``. Isso é alcançado em `boardcommands.cpp <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/meteorolog/boardcommands.cpp>`_ através dos três seguintes vetores globais:

* ``_sensor_names[]``: Contém o nome de todos os sensores disponíveis.
* ``_sensor_nicknames[]``: Contém todos os respectivos apelidos.
* ``_fp_array_read_sensor[]``: Contém os ponteiros de função das ``read_X()``, onde ``X`` é o nome de um sensor -- por exemplo, ``read_LDR()``.

Percorrendo-se os dois primeiros, ``call_read_sensor()`` busca por um nome/apelido válido. Caso encontre, o índice é utilizado para acessar ``_fp_array_read_sensor[]``, obter o ponteiro da função e finalmente executá-la.

Os vetores são incializados com as respectivas constantes declaradas em `my_sensors.h <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/meteorolog/mysensors.h>`_. 

setRTC
======

A configuração do relógio (se presente) na placa é feita com o comando::

    setRTC,ano,mes,dia,hora,minuto,segundo

onde os argumentos ``ano,mes,dia,hora,minuto,segundo`` são repassados para ``set_time_from_csv()`` (`my_sensors.h <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/meteorolog/mysensors.h>`_), cujo funcionamento depende do RTC em questão (ver seção sobre o ``RTC_DS1307`` e suas funções).

Exemplo::

    setRTC,2015,8,17,14,43,10

caso bem sucedido deverá retornar a string::

    done: 2015-08-17 14:43:10

mysensors.h
***********

Esse módulo contém:

* Funções ``read_X()`` onde ``X`` é o nome de um sensor disponível;
* Função ``mysensors_setup()`` para inicialização programada de todos os sensores ao ligar a placa;
* Constantes a serem usadas por `boardcommands.cpp <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/meteorolog/boardcommands.cpp>`_ nos vetores de lookup das funções ``read_X()`` :

  * ``__SENSOR_COUNT``: total de sensores;
  * ``__SENSOR_NAMES``: vetor de strings de nomes de todos os sensores;
  * ``__SENSOR_NICKNAMES``: vetor de strings de apelidos de todos os sensores;
  * ``__FP_ARRAY_READ_SENSOR``: vetor de ponteiros de função das ``read_X()``.

156
.. note:: Entende-se aqui **sensor** por um elemento de software capaz de proporcionar um valor medido. Ou seja, ainda que um único componente eletrônico possa oferecer diversas medições (como temperatura e umidade do ar pelo DHT22), em termos do software cada medição é devida a um sensor, conforme cadastrado em http://dados.cta.if.ufrgs.br/emm. 
Nelso Jost's avatar
Nelso Jost committed
157

158
De um modo geral as funções ``read_X()`` são bem simples, pois apenas invocam funções externas para obteção da medição numérica que é então convertida para string -- o tipo de retorno esperado.
Nelso Jost's avatar
Nelso Jost committed
159

160
Por exemplo, sensores que atuam diretamente em pinos analógicos podem fazer uso de ``analogRead()`` (biblioteca do Arduino) e alguma matemática para calibração diretamente nas ``read_X()`` (a exemplo de ``read_LDR()``). Já sensores que possuem controladoras Wire ou I2C em gearl acabam fazendo uso de bibliotecas separadas para melhor organização do código. 
Nelso Jost's avatar
Nelso Jost committed
161

162
.. note:: Bibliotecas de terceiros são mantidas no subdiretório ``libs/``.
Nelso Jost's avatar
Nelso Jost committed
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215

Inserindo novos sensores
========================

O software do repositório contém o código básico para os sensores suportados oficialmente, mas nada impede que novos sensores sejam adicionados. Para isso, siga os seguintes passos:

**1. (opcional) Disponibilize uma biblioteca dentro de libs/**

Caso o código de leitura seja complexo demais, considere criar uma nova biblioteca::
    
    libs/novo_sensor.h
    libs/novo_sensor.cpp

Dica: utilize orientação a objetos para melhor organização.

**2. Registre o protótipo da nova função read_X() em mysensors.h**

.. code-block:: cpp

    #include "libs/novo_sensor.h"
    String read_NOVO_NOME();

onde ``NOVO_NOME`` será o nome do novo sensor. 

**3. Registre novo nome e apelido**

* Incremente ``__SENSOR_COUNT``;
* Inclua ``NOVO_NOME`` no vetor ``__SENSOR_NAMES``;
* Inclua um apelido curto qualquer no vetor ``__SENSOR_NICKNAMES``, na mesma posição utilizada por ``NOVO_NOME`` anteriormente;
* Inclua o ponteiro de função ``&read_NOVO_NOME`` no vetor ``__FP_ARRAY_READ_SENSOR``, na mesma posição utilizada por ``NOVO_NOME`` anteriormente.

**4. Implemente o código em mysensors.cpp**

Exemplo :

.. code-block:: cpp

    // === NOVO_NOME SETUP =======================================

    #define NOVO_NOME_PIN        8   // digital

    String read_NOVO_NOME()
    {
        return FloatToString(...);
    } 

    // ===========================================================


######
Logger
######

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
Software responsável por solicitar leitura dos sensores pela placa, guardar os dados localmente e também enviá-los ao servidor. Foi pensado para execução initerrupta em background através de um **daemon** registrado no gerenciador `supervisor <https://supervisor.readthedocs.org/en/latest/>`_.

Seguem abaixo algumas das filosofias do software:

* Configuração amigável ao usuário leigo;

* Sistema completo de logging (dados e execução);

* Sincronismo de dados locais e remotos;


Estrutura de arquivos
*********************

Este software está escrito na linguagem Python 3 e apresenta a seguinte estrutura de arquivos:

.. code-block:: shell

    logger/
    ├── app/                  # Python package contendo a aplicação
    │   ├── __init__.py       # torna essa pasta um package e faz inicializações
    │   ├── config.py         # gerencia configurações do programa
    │   └── main.py           # implementa a classe Meterologger
    ├── logs/                 # guarda logs de execução
    ├── deploy.py             # script para registrar o daemon do logger
    ├── init_serial.py        # script para obter uma conexão serial
    ├── requirements.pip      # bibliotecas Python de terceiros
    └── run.py                # ponto de entrada para excução do logger

.. note:: Normalmente em projetos Python, o arquivo de configuração fica presente no nível superior da pasta package ao lado de ``run.py``. No caso deste projeto, optamos por mantê-lo na raíz do repositório, na posição de destaque ao lado do ``Makefile``.

Projetos Python multi-arquivos fazem uso do conceito de **package**: pasta que contém um arquivo `__init__.py <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/logger/app/__init__.py>`_ tornando-se acessível exteriormente como um módulo. Assim, tanto um interpretador em `logger/ <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/logger>`_ como o arquivo `run.py <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/logger/run.py>`_ fora do package podem fazer::

    from app.main import Meteorologger

Módulos internos do package podem acessar uns aos outros por importação relativa, como acontece em `app/main.py <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/logger/app/main.py>`_::

    from .config import Config

onde o operador ``.`` refere-se ao nível atual (`main.py <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/logger/app/main.py>`_ e `config.py <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/logger/app/config.py>`_ estão na mesma pasta), ``..`` indica nível superior e assim por diante.

Em resumo, o código do aplicativo logger está todo na pasta ``app/``, onde ``Meteorologger`` é a classe principal, e sua execução se dá pelo arquivo ``run.py`` com o seguinte ponto de entrada::

    Meteorologger().run()

.. note:: A instanciação ``Meteorologger()`` é responsável principalmente pelo carregamento do arquivo de configurações e sua validação. Já o método ``run()`` contém o loop infinito que consiste na execução do logger.

Dependências
************

Além da linguagem Python 3 o logger depende das seguintes bibliotecas de terceiros:

* ``pyserial``

    Possibilita comunicação entre Python e portas seriais. Aqui é utilizada para enviar comandos à placa Arduino e ler as respostas obtidas.

* ``requests``

    Alternativa à biblioteca padrão ``urllib``, usada para comunicação HTTP. Aqui é utilizada para enviar requests para a API do servidor em http://dados.cta.if.ufrgs.br/emm.

.. note:: As bibliotecas e suas versões estão listadas no arquivo  `requirements.pip <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/logger/requirements.pip>`_ para instalação automatizada através do gerenciador de pacotes **pip3** (vem por padrão com Python 3.4+).

Afim de evitar comprometer a instalação global do Python do usuário, optamos aqui pelo uso da ferramenta `virtualenv <https://virtualenv.pypa.io/en/latest/>`_ para a criação de um ambiente virtual contendo uma cópia isolada do interpretador Python. Todo o processo é automatizado pelo ``Makefile`` principal do projeto através do comando:

.. code-block:: shell

    $ make setup

O resultado é a criação de uma pasta ``.venv`` contendo uma instalação isolada de Python 3 e as bibliotecas mencionadas acima. A execução correta desse comando depende dos seguintes programas no sistema:

* **python3** : interpretador da linguagem Python 3.x (recomenda-se versão 3.4);

    * Pacote Debian: ``python3``

* **pip3** : gerenciador de pacotes do Python 3;

    * Pacote Debian: ``python3-pip``

* **virtualenv** : criação de ambientes virtuais de Python;

    * Instale via **pip3**: ``$ sudo pip3 install virtualenv``

Adicionamente, para que o logger possa ser executado em background (ver seção `deploy.py`) esse projeto também requer a seguinte ferramenta:

* **supervisor** : gerenciador de daemons (processos background);

    * Pacote Debian: ``supervisor``

.. note:: Algumas distribuições podem possuir as versões 3.x do interpretador Python registradas em comandos diferentes de ``python3``, como por exemplo, ``python-3.x``, o mesmo valendo para o **pip** (ex: ``pip-3.x``). Nesse caso, você precisa fornecer o nome correto através da variável ``PYBIN``::
    
    $ make setup PYBIN=python-3.x

.. note:: Os pacotes Debian podem ser instalados com ``$ sudo apt-get install <pacote>``. Usuários de outras distribuições deverão procurar os equivalentes para o seu gerenciador de pacotes.
 

run.py
******

Este arquivo consiste no ponto de entrada da aplicação, permitindo a execução do logger por um interpretador Python: ``$ python3 run.py [options]``. Entretanto, conforme descrito na seção anterior sobre dependências, deve ser utilizado o interpretador do ambiente virtual. Isso é alcançado pelo seguinte comando do ``Makefile``:

.. code-block:: shell

    $ make logger-run

que deve ser executado após a criação do ambiente virtual com ``make setup``.

Parâmetros
==========

* ``--verbose``

    Se presente, resulta em ``Meteorologger.verbose = True``, o que coloca o log de execução em nível debug.

* ``--sync-rtc``

    Se presente, ao invés de executar o logger, simplesmente executa o método ``Meterologger().sync_rtc()`` para realizar sincronização do relógio da placa (``RTC_DS1307``) com o da máquina. Essa operação está disponibilizada no seguinte comando do ``Makefile``::

    $ make sync-rtc

deploy.py
*********

Conforme mencionado na introdução, o logger foi pensado como um programa para ser executado em background. Por exemplo, as menssagens do log de execução são, por padrão, escritas em um arquivo dentro de ``logger/logs`` através da biblioteca padrão ``logging`` de Python. O script `deploy.py <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/logger/deploy.py>`_ é responsável por registrar um novo processo **daemon** no `supervisor <https://supervisor.readthedocs.org/en/latest/>`_ atendendo pelo nome ``meteorologger``.

A execução se dá pelo seguinte comando do ``Makefile`` (permissões de administrador serão solicitadas):

.. code-block:: shell

    $ make deploy

O registro de um *daemon* no supervisor consiste na criação de um arquivo de configuração em ``/etc/supervisor/conf.d/`` e a subsquente execução de ``supervisorctl update``. É exatamente isso que faz a função ``deploy_supervisor()`` do script `deploy.py <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/logger/deploy.py>`_. O arquivo de configuração utiliza o seguinte template (string ``TEMPLATE_SUPERVISOR_CONF``)::

    [program:{PROCESS_NAME}]
    command={BASE_DIR}/.venv/bin/python {BASE_DIR}/logger/run.py
    directory={BASE_DIR}
    user=root
    autostart=true
    autorestart=true
    redirect_stderr=true
    stdout_logfile={BASE_DIR}/logger/logs/stdout.log
    logfile={BASE_DIR}/logger/logs/supervisor-{PROCESS_NAME}.log

Os valores substituídos nesse template estão declarados nas constantes globais:

* ``PROCESS_NAME``: apelido para o *daemon* dentro do supervisor. No caso, ``meteorologger``.
* ``BASE_DIR``: diretório raiz do projeto, que contém o ``Makefile``. Obtido pelo cálculo relativo da posição do arquivo ``deploy.py``.

Sobre as configurações, vale destacar:

* ``redirect_stderr``: menssagens de erro serão escritas na saída padrão.
* ``stdout_logfile``: além das menssagens da saída padrão, o traceback aparecerá nesse arquivo caso o programa falhe.

Por fim, o mesmo script `deploy.py <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/logger/deploy.py>`_ é utilizado também para *undeployment*, isto é, remoção do *daemon* no supervisor. Isso é feito passando-se o argumento ``-u`` para o script, operação disponível pelo seguinte comando do ``Makefile``:

.. code-block:: shell

    $ make undeploy

app/config.py
*************

O arquivo de configuração utilizado pelo logger chama-se `settings.ini <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/settings.ini>`_ e encontra-se na pasta raiz do projeto, ao lado do `Makefile <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/Makefile>`_ por ser uma posição de destaque. Foi concebido para ser configurado por um usuário leigo.

Existem várias opções de sintaxe para arquivos de configuração no universo Python: *XML*, *JSON*, *YAML*, *INI*, etc. Apesar de que sintaticamente o *YAML* seja mais interessante para projetos Python por levar em conta a identação, esse mesmo motivo dificultaria a configuração por usuários leigos em computação. Assim, a flexibilidade do formato *INI* tratado pela biblioteca padrão ``configparser`` determinou sua escolha para esse projeto.

O módulo `app/config.py <https://git.cta.if.ufrgs.br/meteorolog/arduino-meteorolog/blob/master/app/config.py>`_ é responsável pela leitura e validação do arquivo de configuração através da classe ``Config``, que deverá se comportar como um dicionário para obteção das seções e chaves. Como todos valores lidos e armazenados pelo objeto ``configparser.ConfigParser`` são strings, optamos aqui por utilizar e manipular uma cópia em dicionário das configurações através do atributo ``_sections`` deste objeto. Assim, quando uma seção de configuração é acessada dentro de ``Config`` com o operador ``[]``, o método mágico ``__getitem__()`` retorna um dicionário dentro de ``_sections`` podendo conter qualquer tipo de dados como chaves e valores.

Além disso, a classe ``Config`` implementa diversos métodos iniciando por ``validate_``, dedicados a validar seções e chaves específicas do arquivo de configuração, por vezes introduzindo novas chaves úteis à classe ``Meteorologger``. A organização foi feita assim para evitar um único método com mais de 20 linhas de código. Segue um resumo das validações realizadas:

Validações
==========

* ``validate_server_url()``

    Utiliza valores da seção ``[server]`` para compor a URL utilizada para postagem de dados. O valor ``URL`` consiste na base do endereço do servidor -- opção disponibilizada para o caso de o usuário desejar utilizar outro servidor que não o nosso (por exemplo, um servidor local como ``http://localhost``). O valor ``BOARD_ID`` é utilizado pela URL e também pela API do site ao validar o usuário no momento da postagem.

* ``validate_reading_sensors()``

    Utiliza os valores da seção ``[reading]`` para determinar quais sensores terão a leitura solicitada pelo logger e também se deverá ser lido o relógio da placa (visto como um sensor de nome ``RTC_DS1307``). A ordem dos sensores na chave ``SENSORS`` determinará as colunas do arquivo *datalog.csv* (guarda dados localmente).

    Também introduz a nova chave ``reading/command`` contendo a linha de comando a ser enviada para a porta serial. Essa linha vai conter todos os sensores da chave ``SENSORS``, e também o ``RTC_DS1307`` caso a chave ``RTC_DS1304`` seja ``true``.

* ``validate_reading_interval()``

    Valida a chave ``INTERVAL`` da seção ``[reading]``, transformando a string ``h:m:s`` em um dicionário ``{'H': hh, 'M': mm, 'S': ss}`` para fácil acesso posterior a esses valores. 
    
    Também introduz a nova chave ``reading/interval_seconds`` contendo o valor do intervalo de leitura já convertido para segundos.

* ``validate_datalog_csv_sep()``

    Valida o caracetere utilizado como separador CSV do arquivo *datalog.csv*, configurado na chave ``CSV_SEP`` da seção ``[datalog]``. Além de eliminar opções inválidas, decodifica o caractere para uso ASCII correto posteriormente.  

* ``validate_arduino_serial_port()``

    Valida a chave ``SERIAL_PORT`` da seção ``[arduino]``. O usuário pode especificar uma ou mais portas separadas por vírgula para que o logger tente conexão caso uma delas falhe. Adicionalmente, essa chave pode ser deixada em branco, caso em que será gerada a seguinte lista de portas:

    ``['/dev/ttyACM0', '/dev/ttyUSB0', ..., '/dev/tty/ACM4', '/dev/ttyUSB4']``

    para que o logger tente buscar sozinho a porta onde está a placa.

app/main.py
***********

Nelso Jost's avatar
Nelso Jost committed
419
420

.. [1] Requer que o programa ``make`` esteja instalado no sistema Linux. Felizmente ele vem por padrão nas principais distribuições.