Sistema de Controle de Irrigação Eletrônico

Participantes:

Daniel Hulshof Saint Martin

Resumo do projeto:

Sistema de irrigação inteligente de baixo custo para pequenos e médios produtores.

Descrição do projeto:

A proposta é criar um sistema eletrônico que automatiza a irrigação de pomares, hortas e jardins, de baixo custo e fácil instalação, agregando tecnologia para promover economia e benefício ambiental aos utilizadores.

O Sistema de Controle de Irrigação Eletrônico é composto por uma Unidade Controladora que atuará sobre sensores e atuadores (válvulas ou bombas) instalados na área verde. Cada controladora poderá atuar sobre até 4 canais (sensor + atuador), promovendo economia e versatilidade na aplicação.

O sensor utilizado é um Sensor de Umidade de Solo resistivo, de baixo custo (Aprox. R$ 10,00), que quando conectado a uma entrada analógica da Unidade Controladora permite aferir o nível de umidade presente no solo, possibilitando definir o melhor momento e ajustar a quantidade de água que deve ser utilizada na
rega.

O controle da água pode ser feito por uma variedade de equipamentos, entre válvulas solenoide e bombas de água, tanto AC como DC. O atuador selecionado para o projeto é uma Válvula Solenoide 12 Volts DC, de baixo custo (Aprox. R$ 30,00), que permite controlar o fluxo de água em uma linha de irrigação de baixa pressão. A válvula pode ser instalada em área externa e não requer cuidados especiais para funcionar.

A Unidade Controladora será inicialmente composta de uma placa de desenvolvimento, uma interface wireless para comunicação com outros dispositivos, uma placa de interface para entradas analógicas com amplificadores operacionais, uma placa de interface para saídas de Relê 12V DC. A controladora deve ser instalada em ambiente protegido, de preferência em caixa emética se ficar exposta a umidade.

O sistema pode ser gerenciado via interface web, através de app dedicado ou browser, permitindo criar regras de irrigação com base em dias ou horários específicos e dados do Sensor de Umidade de Solo. As regras de irrigação serão executadas dela Unidade Controladora de acordo com a programação do usuário, levando em conta as informações obtidas pelos sensores de umidade.

As vantagens da utilização do sensor de umidade são a economia de água, pois evita a irrigação em dias de chuva, e maior qualidade do controle de umidade do solo, o que beneficia o desenvolvimento dos vegetais.

Diagrama Funcional:

sistema

 

Maiores desafios:

Avaliando todo o desenvolvimento, a maior dificuldade foi selecionar as tecnologias e componentes que possuem o menor custo possível, mas ainda assim atendem os objetivos do projeto.

A utilização de Sensores de Umidade resistivos, por exemplo, que possuem um custo bastante reduzido, levou a uma mudança no funcionamento da solução. Para evitar corrosão excessiva dos eletrodos, a alimentação do sensor precisou ser limitada a uma janela de 10 milissegundos a cada um segundo. Feita a alteração, a vida útil do sensor aumentou consideravelmente, mostrando sua viabilidade.

Também a opção por Bluetooth LE como interface wireless de comunicação permitiu construir uma solução “turnkey”, ou seja, que já funciona assim que é instalada sem precisar de configurações ou qualquer outro tipo de equipamento ou serviço auxiliar (roteadores, internet, etc.). Isso torna a solução mais acessível e amigável, tanto na instalação quanto no uso, porém também traz algumas limitações. Entre as principais estão a necessidade de um smartphone compatível com Bluetooth LE 4.0 (já são mais de 80% dos Androids) e a limitação na capacidade de transmissão de dados, restritos a 20 bytes por pacote. Essa limitação na transferência de dados aumentou a complexidade do software embarcado e do aplicativo, obrigando que fossem desenvolvidas soluções especiais para divisão e união de dados antes e depois do envio. Ainda assim, mesmo na transmissão de blocos de dados maiores (histórico por exemplo), a aplicação ficou confiável e a velocidade de transferência bastante razoável.

 

Demonstração Atualizada:

Histórico do desenvolvimento:

28/08: Testes com sensor de umidade e válvula solenoide.

O sensor de umidade possui uma boa sensibilidade, variando de 1,2V totalmente imerso em água potável até 3,3V quanto totalmente isolado.

A válvula solenoide funcionou bem, mas faz bastante barulho e possui vazão pequena, que obriga o uso uma coluna de água com mais de 3 metros.

componentes

29/08: Leitura em software do sinal do sensor e atuação da válvula solenoide via mini-relê.

Realizada a leitura do sinal do sensor de umidade através de conversor AD e acionamento da válvula solenoide através de relê auxiliar 12V, com base nas informações obtidas pelo sensor. Se a umidade for menor que 50%, o relê aciona a válvula solenoide.

teste

16/09: Desenvolvimento do sistema de regras e agendamento de irrigação.

Sistema de regras e agendamento de irrigação concluído e testado. As regras podem ser criadas utilizando horário fixo, umidade do solo, ou os dois parâmetros ao mesmo tempo. Cada regra possui um tempo de irrigação e um tempo opcional de espera após irrigar, utilizado para que a água tenha tempo de alcançar o sensor de umidade. Exemplo 1:  Sempre que a umidade ficar abaixo de 30%, irrigar por 10 minutos e esperar 2 horas até a próxima medição. Exemplo 2: Todos os dias, às 7:00, irrigar por 10 minutos. Exemplo 3: Todos os dias, às 18:00 horas, se a umidade estiver abaixo de 30%, irrigar por 20 minutos. Atualmente o sistema é capaz de armazenar 8 regras, que podem atuar nos 4 canais de forma independente.

19/09: Montagem do Protótipo

Para acelerar o desenvolvimento e melhorar a mobilidade do protótipo, optei por utilizar como atuador uma bomba elétrica automotiva, que é utilizada nos esguichos de água do para-brisa. Utilizado um pote como reservatório de água e colocando a bomba internamente, foi possível criar um protótipo funcional móvel, que facilita o desenvolvimento e os testes. Na tampa do pote coloquei a central com as conexões para os periféricos (sensores, bomba, módulo RF).

dsc_0054 dsc_0057 dsc_0056 dsc_0058

22/09: Interface Wireless Bluetooth

Com objetivo de reduzir custos e simplificar a interface de comunicação com o dispositivo e sua configuração, optei por utilizar um módulo Bluetooth Low Energy 4.0, da empresa BlueGiga. O módulo é programado via script e comunica-se com o micro-controlador via porta serial UART. Acompanhando a interface bluetooth, desenvolvi um aplicativo na plataforma Android que fará a interface de configuração e comando das funções da central, além de receber as informações de status de cada canal de irrigação.

screenshot_20160922-171022  screenshot_20160922-162256  screenshot_20160922-162159  screenshot_20160922-162247

25/09: Ajustes na leitura de umidade

Após observar uma corrosão excessiva nos terminais do sensor de umidade, que é provocada por uma eletrolise causada pela passagem de corrente elétrica, decidi energizar o sensor somente no momento da leitura. Assim, o microcontrolador alimenta o sensor, aguarda 10 milissegundos, faz a leitura e então desliga novamente o sensor. Isso reduziu drasticamente a oxidação dos terminais e me levou a acreditar que a solução de sensor de umidade resistivo pode ser viável em termos práticos.

void LeUmidade()
{
	// Verifica flag de controle de leitura de umidade, setada via timer
	if (contTempoCtrlLeituraUmidade == 10 mSeg)
	{
		// Liga os sensores para realizar a leitura
		LigaSensores();
	}
	else if (contTempoCtrlLeituraUmidade == 0)
	{
		// Faz a leitura das entradas AD e armazena na memória
		LeBurstAD ();

		// Desliga os sensores para evitar corrosão
		DesligaSensores();

		// Converte os valores lidos em % de umidade do solo
#if VER_PLACA == 1
		UmidadeAtual[0] = ((1024 - ValorAD[3])*100)/1024;
		UmidadeAtual[1] = ((1024 - ValorAD[6])*100)/1024;
		UmidadeAtual[2] = ((1024 - ValorAD[0])*100)/1024;
		UmidadeAtual[3] = ((1024 - ValorAD[1])*100)/1024;
#else
		UmidadeAtual[0] = ((1024 - ValorAD[0])*100)/1024;
		UmidadeAtual[1] = 100;
		UmidadeAtual[2] = 100;
		UmidadeAtual[3] = 100;
#endif

		// Soma umidade atual a média
		UmidadeMedia[0] += UmidadeAtual[0];
		UmidadeMedia[1] += UmidadeAtual[1];
		UmidadeMedia[2] += UmidadeAtual[2];
		UmidadeMedia[3] += UmidadeAtual[3];

		// Incrementa contador de leituras da média
		contNumLeiturasUmidade++;

		// Seta contador para próxima leitura
		contTempoCtrlLeituraUmidade = 1 Seg;
	}
}

28/09: Histórico de umidade

Nos últimos dias me dediquei a implementar um log com histórico de umidade. Foi trabalhoso, tanto no lado embarcado, pela limitação de memória, quando no smartphone, pela limitação na transmissão de dados via Bluetooth LE. Finalmente funcionando, o sistema agora é capaz de armazenar 72 horas de leituras de umidade dos 4 canais. Isso representa um total de 288 bytes de dados que devem ser armazenado na flash do microcontrolador e transmitidos via Bluetooth, em pacotes de 16 bytes. Para não reduzir muito a vida útil do microcontrolador, a gravação na flash é feita a cada 4 horas. No smartphone, que controla todo o processo de envio dos pacotes, a exibição dos dados é feita no formato de um gráfico, de Umidade x Horas. Os dados apresentados nas imagens foram assim inicializados no firmware para testes, e serão substituídos por medidas de umidade nas próximas horas.

screenshot_20160928-171742  screenshot_20160928-171521  screenshot_20160928-165902  screenshot_20160928-165852

30/09: Melhoras nas Regras e nos Logs

Após 48 horas catalogando dados, percebi que seria mais adequado armazenar a média das leituras de umidade da última hora, e não somente a leitura instantânea, devido a variação na leitura imediata (até 3%). Feita essa alteração, o protótipo entrou em testes novamente, por mais alguns dias. Além dos logs, mudei também a forma de funcionamento das regras. Agora as Saídas e Sensores operam de forma independente, não mais amarradas como um canal. Isso significa que agora é possível atuar na saída 3 utilizando dados do sensor 1, por exemplo, o que flexibiliza a aplicação do sistema. Mais algumas alterações estéticas foram feitas, sem maior relevância.

screenshot_20160930-182035  screenshot_20160930-182054  screenshot_20160930-190713  screenshot_20160930-190722

03/10: Regras com Dia da Semana e Mais Testes

Finalizando a última funcionalidade proposta inicialmente, implementei a opção de definir o dia da semana na configuração das regras. Isso permitirá ao usuário criar regras com execução em intervalos maiores que 24 horas, até o limite de 7 dias. Após mais alguns dias em testes, pude analisar os resultados do armazenamento da média de umidade no histórico, que gerou dados mais estáveis. É possível notar que nas horas mais quentes do dia a medida de umidade varia para cima, e durante a noite, volta a diminuir. Foi possível também observar uma das regras sendo executada, quando a umidade ficou abaixo de 50%. Adicionei fotos da horta “cobaia” onde o sistema está instalado.

screenshot_20161003-161032  screenshot_20161003-151729  dsc_0071  dsc_0070

06/10: Mudanças na entrada de tempo de Irrigação e Espera e melhoramentos diversos

Agora a entrada dos campos de tempo de Irrigação e Espera nas definições de regras permite escolher Horas, Minutos e Segundos, possibilitando mais precisão (usando segundos) na irrigação de áreas pequenas, como vasos, e mais facilidade (usando horas) na irrigação de áreas grandes, como hortas. Fiz ainda mais algumas mudanças estéticas no aplicativo e pequenas correções de bugs no firmware. Adicionei também uma macro para teste dos logs, que altera o tempo de amostragem para 1 segundo e desabilita a gravação na flash.

screenshot_20161006-091100  screenshot_20161006-134459  screenshot_20161006-134830  screenshot_20161006-134906

09/10: Novo protótipo

Para liberar o primeiro protótipo para desenvolvimento e ainda continuar os testes de maior duração (dias a semanas), decidi construir um novo protótipo, desta vez miniaturizado. Para tanto limitei a solução a um único canal, ou seja, um sensor de umidade e uma válvula solenoide, encapsulando tudo em uma caixa plástica Patola PB-112/2 TE, onde ficaram: a fonte AC/DC 12V, a central, o módulo Bluetooth e a válvula solenoide. A placa do sensor de umidade deve ficar próxima à área úmida, então acabou ficando fora da caixa. Aproveitei para fazer um B.O.M. do protótipo Mini, que totalizou R$ 107,14 a preços de varejo.

dsc_0075  dsc_0077  dsc_0078  dsc_0080  dsc_0045  dsc_0047

12/10: Projeto de Hardware

Aproveitando a oportunidade, decidi aprender uma nova ferramenta para projetos eletrônicos, o KiCad. Fiz o projeto completo, com Esquemático, PCI e Modelo 3D. Utilizei as bibliotecas prontas do KiCad, algumas compartilhadas e algumas criadas por mim mesmo. Após 3 dias de trabalho, o resultado ficou bastante satisfatório, e eu gostei muito da ferramenta. O projeto da PCI foi feito para caber em uma caixa plástica Patola CP-018, com tampa para conector MiniFit, 14 Vias, 90º.

esquematico  esquematico2  pcb-kicad  pcb-kicad2  cad  cadw

14/10: Melhorias no Aplicativo e Firmware

Adicionei ao aplicativo uma aba de configuração onde é possível ocultar os canais não utilizados. Assim a visualização fica mais limpa na tela principal e mais fácil na tela de Histórico. Como estou testando duas versões diferentes de hardware, no software embarcado adicionei a diferenciação da versão da placa, que é enviada via Bluetooth ao aplicativo, que por sua vez oculta automaticamente os canais que não estão disponíveis no hardware. Modifiquei ainda o funcionamento do botão Atualizar que fica na ActionBar do aplicativo. Agora o botão funciona de acordo com o fragmento visível na tela no momento do toque, ou seja, se por exemplo o Histórico estiver aberto, ao apertar o botão Atualizar histórico é carregado novamente. Agora também é possível renomear os canais, de acordo com sua aplicação.

      screenshot_20161014-174124 screenshot_20161014-174131 screenshot_20161014-172252 screenshot_20161013-165527

15/10: Melhora na comunicação via Bluetooth

Com objetivo de agilizar a leitura dos Logs, que estava levando cerca de 5 segundos, fiz várias modificações no código embarcado e no smartphone, que resultaram em uma grande melhora. Agora a leitura dos Logs demora menos de 1 segundo. Entre as mudanças está a remoção do delay de 10 milissegundos na leitura da umidade, que era o único no loop principal e em todo o código, sendo que agora a leitura de umidade é controlada via timer. Além disso, mudei o envio das atualizações, que agora só acontece quando o smartphone envia o comando específico. Houveram também mudanças nos timeouts tanto do lado embarcado quanto do smartphone.

19/10: Android Wear

Última atualização! Em um projeto como esse, um aplicativo para Dispositivos Vestíveis (Wearables) não faz muito sentido. Mas como não custa nada, resolvi fazer um mesmo assim. Agora o app Android vem com o módulo para Android Wear, que foi desenvolvido para rodar na tela do Moto 360. Devido ao tempo escasso e a não possuir outros dispositivos, não fiz o layout para outros tipos de tela. O app para smartwatches apresenta as informações sobre a umidade do solo atual e permite controlar a saída manualmente. Nessa primeira versão apenas o Canal 1 está disponível. Posteriormente devo atualizar o app para comandar todos os canais. Coloquei no vídeo um pequeno trecho com demonstração do smartwatch rodando o app.

moto-360-menu  moto-360-app

22/10: Botão físico, Leds, Pareamento e Ajustes visuais

O adiamento do prazo de entrega me permitiu fazer mais alguns ajustes no projeto. Agora o protótipo possui um botão físico que permite ligar e desligar as saídas manualmente, sem a necessidade do smartphone. Quais saídas são acionadas e o tempo de acionamento podem ser configurados no aplicativo. O botão é utilizado também para permitir o pareamento (bonding) com o smartphone, que agora é obrigatório antes de qualquer comunicação com a central. Foram adicionados dois Leds, um Verde que indica alimentação, e outro Amarelo, de Status. O Led de Status fica aceso sempre que alguma saída está ligada, fica piscando lentamente quando há alguma conexão bluetooth ativa e pisca rapidamente quando a central está em modo de pareamento. Fiz também ajustes visuais que deixaram algumas teslas mais compatíveis com o Material Design do Android. Fiz também um novo vídeo explicando a solução.

dsc_0091  dsc_0088  screenshot_20161022-143548  screenshot_20161023-085356

 

Hardware:

SCIE –  Versão 1: Protótipo de Bancada

Como já trabalho com NXP há algum tempo, optei por usar uma placa desenvolvida em 2013 que utiliza um micro NXP LPC1111, um Cortex M0 de baixo custo. A placa utilizada no protótipo foi criada por mim para automatizar algumas luzes em casa, mas com pequenas adaptações ficará adequada ao projeto de irrigação. A ideia por trás disso é utilizar o pequeno micro LPC1111, de baixo custo e fácil obtenção no mercado nacional.

Além do microcontrolador, serão utilizados mini-relês 12V (20A DC / 7A AC), sensores de umidade com placa amplificadora, válvulas solenoide e um módulo de comunicação wireless.

Como interface wireless optei pelo módulo Bluetooth LE 4.o BlueGiga BLE112.

dsc_0056

SCIE –  Versão 2: Protótipo Miniaturizado

Com o objetivo de testar extensivamente a solução, montei uma versão miniatura do SCIE, com apenas 1 canal, que ficará funcionando indefinidamente. O hardware é basicamente o mesmo do primeiro protótipo, com a mudança apenas no módulo Bluetooth, que agora é a versão BLE113.

dsc_0088 dsc_0091

 

SCIE – Versão 3: Protótipo Comercial

Com o hardware já bem definido, passei para o desenvolvimento de um protótipo comercial para lote piloto. Projetado para ser fabricado com tecnologia SMD, sendo a maioria dos componentes de fácil acesso no mercado nacional. A caixa plástica é uma Patola CP-018, bastante comum, que atende às necessidades do projeto. O chicote possui 14 vias, com cabos de 1mm e 0,5mm. Os preços utilizados no BOM são baseados na minha experiência na indústria eletrônica de Curitiba.

Repositório do Hardware – SCIE

Esquema Elétrico da Versão 3 do projeto: SCIEv3 – Esquematico

BOM Completo da Versão 3 do projeto: SCIEv3 – BOM

BOM Simplificado da Versão 3:

bom-simplificado

Software/Firmware:

Firmware

Repositório do Firmware – SCIE

O software embarcado é estruturado como uma máquina de estados temporizada controlada por um timer com interrupções a cada 10 Milissegundos. O timer controla o tempo geral e a execução das regras.

O Loop Principal coloca o núcleo em economia e volta a executar sempre que há uma interrupção. A cada interrupção é verificado o recebimento de mensagens via Bluetooth, para otimizar o tempo de resposta. A cada 1 segundo são feitas as medições das entradas AD, que são convertidas em valores de umidade (0% a 100%) e é calculada a média das últimas leituras. A cada 1 hora é feita o armazenamento das médias de umidade nos Logs, e a cada 4 horas os logs são armazenado na flash do microcontrolador.

A interface Bluetooth recebe e envia mensagens de tamanho fixo de 18 bytes. Cada comando possui uma quantidade de dados específica, que é decodificada de acordo com sua definição. Os comandos são:

BT_CMD_LIGA_SAIDA: Liga saída manualmente, por tempo determinado.
BT_CMD_DESLIGA_SAIDA: Desliga saída manualmente.
BT_CMD_LE_REGRA: O smartphone envia o pedido e a central responde com a regra específica.
BT_CMD_GRAVA_REGRA: O smartphone envia a regra específica que é gravada na memória da central.
BT_CMD_ATUALIZA: O smartphone envia o pedido e a central responde com o estado atual da umidade e das saídas.
BT_CMD_AJUSTA_HORA: O smartphone envia a hora atual que é ajustada pela central.
BT_CMD_LE_LOG_PACKET: O smartphone envia o pedido e a central responde com o log packet específico.
BT_CMD_INICIA_BONDING: A central envia o comando para o módulo bluetooth que libera o pareamento por 30 segundos.
BT_CMD_CANCELA_BONDING: A central envia o comando para o módulo bluetooth que cancela o pareamento.
BT_CMD_LE_ACION_MANUAL: O smartphone envia o pedido e a central responde com os dados de acionamento manual da saída específica.
BT_CMD_GRAVA_ACION_MANUAL: O smartphone envia os dados de acionamento manual da saída específica, que são gravados na memória da central.

Para melhorar o consumo de energia e o desempenho da comunicação Bluetooth, o envio automático das medidas de umidade via indication foi removido. Agora o aplicativo é responsável por pedir a atualização, que é feita automaticamente a cada 1 segundo. A leitura dos logs é dividida em 19 packets, sendo 18 packets com dados dos logs e 1 packet com o índice atual. Durante a leitura dos logs, a atualização automática é desativada, para não haver concorrência.

A execução das regras é controlada pelo timer. A cada 1 segundo a rotina de verificação das regras é executada. Essa rotina, apesar de longa, é uma sequencia de testes lógicos e é executada rapidamente pois não faz grandes manipulações na memória. Os estados da execução das regras são:

REGRA_DESATIVADA: Regra desativada, nunca será executada.
REGRA_ATIVADA: Regra ativada, pode ser executada se as condições estiverem adequadas.
REGRA_EXECUTANDO: Regra em execução por tempo determinado.
REGRA_ESPERANDO: Regra executada, esperando o tempo determinado para que possa ser executada novamente.
REGRA_FINALIZADA: Regra finalizada, pode ser executada novamente, se as condições estiverem adequadas.

Diagrama do Firmware:

firmware

Abaixo trechos do código:

int main(void)
{
	SystemInit();

	AjustaCentral ();

	while (1)
	{
		// Dorme
		Dorme ();

		// Verifica recebimento de mensagens via BT
		VerificaBT ();

		// Grava os logs
		GravaLog();

		// Faz a leitura da umidade
		LeUmidade();

		// Reset watchdog counter
		FeedWatchdog ();
	}

	return 0;
}
typedef struct
{
	uint32_t Duracao;	// Duração da irrigação (0 ~ n)
	uint32_t Espera;	// Tempo de espera entre cada rega (0 ~ n)
	int8_t Minutos; 	// Minutos do horário agendado (0 ~ 59, -1: desativado)
	int8_t Horas; 		// Horas do horário agendado (0 ~ 23, -1: desativado)
	int8_t DiaSemana;	// Dia da semana (bitfield BIT0:Domingo ~ BIT6:Sabado)
	int8_t Umidade;		// Umidade abaixo da qual a regra deve ser executada (0 ~ 100, -1: desativado)
	uint8_t Sensor;		// Qual sensor a regra se refere (0 ~ 3)
	uint8_t Saida;		// Qual saida a regra se refere (0 ~ 3)
	uint8_t Estado;		// Estado atual da regra (ATIVADA, EXECUTANDO, ESPERANDO, DESATIVADA)
	uint8_t pad1;		// Para futura expansão (memoria alinhada em 32 bits)
} REGRA_IRRIG;
void VerificaRegras ()
{
	// Verifica as regras armazenadas
	for (int32_t i = 0; i < NUM_REGRAS; i++)
	{
		// Verifica se a regra atual está ativada e não está executando
		if (Config.Regra[i].Estado == REGRA_ATIVADA && ControleRegra[i] == REGRA_FINALIZADA)
		{
			// Verifica se foi definida regra de umidade
			if (Config.Regra[i].Umidade >= 0)
			{
				// Verifica se também foi definido horário
				if (Config.Regra[i].Minutos >= 0 && Config.Regra[i].Horas >= 0)
				{
					//  Verifica se a umidade está abaixo do que foi definido na regra e se o horário e dia da semana está correto
					if ((UmidadeAtual[Config.Regra[i].Sensor] < Config.Regra[i].Umidade) && (Config.Regra[i].Minutos == contTempoMinutos && Config.Regra[i].Horas == contTempoHoras && contTempoSegundos == 0) && (Config.Regra[i].DiaSemana & (1<<contTempoDiaSemana)))
					{
						// Liga a saida para executar a irrigação
						ControleRegra[i] = REGRA_EXECUTANDO;
						LigaSaida(Config.Regra[i].Saida, Config.Regra[i].Duracao);
						contTempoControleRegra[i] = Config.Regra[i].Duracao;
					}
				}
				else
				{
					// Verifica se a umidade está abaixo do que foi definido na regra
					if (UmidadeAtual[Config.Regra[i].Sensor] < Config.Regra[i].Umidade)
					{
						// Liga a saida para executar a irrigação
						ControleRegra[i] = REGRA_EXECUTANDO;
						LigaSaida(Config.Regra[i].Saida, Config.Regra[i].Duracao);
						contTempoControleRegra[i] = Config.Regra[i].Duracao;
					}
				}
			}
			else
			{
				// Sem regra de umidade, verifica regra de horário e dia da semana
				if ((Config.Regra[i].Minutos == contTempoMinutos && Config.Regra[i].Horas == contTempoHoras && contTempoSegundos == 0) && (Config.Regra[i].DiaSemana & (1<<contTempoDiaSemana)))
				{
					// Liga a saida para executar a irrigação
					ControleRegra[i] = REGRA_EXECUTANDO;
					LigaSaida(Config.Regra[i].Saida, Config.Regra[i].Duracao);
					contTempoControleRegra[i] = Config.Regra[i].Duracao;
				}
			}
		}
	}
}
void GravaLog()
{
	// Verifica flag de controle de gravação, setada via timer
	if (HoraGravarLog)
	{
		HoraGravarLog = 0;

		for (int i = 0; i < NUM_CANAIS; i++)
		{
			// Calcula a média dividindo os valores somados na última hora
			UmidadeMedia[i] = UmidadeMedia[i] / contNumLeiturasUmidade;

			// Salva a umidade dos canais no log atual
			Config.Log[Config.LogIndex].Sensor[i] = UmidadeMedia[i];

			// Reinicia o cálculo da umidade média
			UmidadeMedia[i] = 0;
		}

		// Reinicia contagem de leituras da umidade média
		contNumLeiturasUmidade = 0;

		// Incrementa o indice dos logs
		Config.LogIndex++;

		// Verifica se deve reiniciar o indice
		if (Config.LogIndex >= NUM_LOGS)
		{
			Config.LogIndex = 0;
		}

		// Grava na flash a cada 4 leituras
		if (!(Config.LogIndex%4))
		{
#ifndef TESTE_LOGS
			ProgramFlash(POS_MEM_CONFIG, (unsigned long *)&Config, sizeof (CONFIG_CENTRAL));
#endif
		}
	}
}

 

Aplicativo

Repositório do Aplicativo – SCIE

O aplicativo de controle foi desenvolvido para rodar em smartphones que utilizam a plataforma Android, Versão 4.4 em diante.  Quando iniciado, o aplicativo cria um serviço em background que gerencia a conexão Bluetooth com a central. É possível salvar o endereço MAC de um dispositivo favorito, permitindo assim a conexão automática quando o aplicativo é aberto.

Diagrama do Aplicativo:

aplicativo

Abaixo trechos do código:

public void UpdateRegrasFragment() {

    // Verifica se a view já foi inflada
    if (getView() == null)
        return;

    // Verifica o estado da conexão bluetooth
    if (mApplication.GetConnectionStatus() == IrrigadorApplication.BLUETOOTH_DISCONNECTED){

        mViewParent.setVisibility(View.GONE);
        mViewGravar.setVisibility(View.GONE);
        mTextViewDesconectado.setText("Desconectado");
        mTextViewDesconectado.setVisibility(View.VISIBLE);
    }
    else if (mApplication.GetConnectionStatus() == IrrigadorApplication.BLUETOOTH_CONNECTING){

        mTextViewDesconectado.setText("Conectando...");
        mTextViewDesconectado.setVisibility(View.VISIBLE);
    }
    else {
        mViewParent.setVisibility(View.VISIBLE);
        mViewGravar.setVisibility(View.VISIBLE);
        mTextViewDesconectado.setVisibility(View.GONE);

        // Obtem a regra atualmente selecionada
        int regra = mSpinnerNumRegra.getSelectedItemPosition();

        // Verifica o estado da regra
        if (mApplication.mRegra[regra].mEstado == Regra.REGRA_ATIVADA){
            // Regra ativada, exibe a view Estado
            mViewEstado.setVisibility(View.VISIBLE);

            mSwitchEstado.setText("Ativada");
            mSwitchEstado.setChecked(true);
        }
        else{
            // Regra deativada, oculta a view Estado
            mViewEstado.setVisibility(View.GONE);

            mSwitchEstado.setText("Desativada");
            mSwitchEstado.setChecked(false);
        }

        // Atualiza o spinner de canal com o canal lido da central
        mSpinnerNumSensor.setSelection(mApplication.mRegra[regra].mSensor);

        // Atualiza o spinner de canal com o canal lido da central
        mSpinnerNumSaida.setSelection(mApplication.mRegra[regra].mSaida);

        // Atualiza os editText com duração e espera
        mTextViewDuracao.setText(TimeStringFromInt(mApplication.mRegra[regra].mDuracao));
        mTextViewEspera.setText(TimeStringFromInt(mApplication.mRegra[regra].mEspera));

        // Bloqueia todas as view seguintes, para desbloquear abaixo de acordo com a regra
        setEnabledForEveryChildView(false, mViewUmidade);
        setEnabledForEveryChildView(false, mViewHorario);
        setEnabledForEveryChildView(false, mViewTempo);
        setEnabledForEveryChildView(false, mViewEspera);

        // Verifica se há regra de umidade
        if (mApplication.mRegra[regra].mUmidade >= 0 && mApplication.mRegra[regra].mUmidade <= 100) {
            // Desbloqueia e atualiza views de umidade
            setEnabledForEveryChildView(true, mViewUmidade);
            setEnabledForEveryChildView(true, mViewTempo);
            setEnabledForEveryChildView(true, mViewEspera);
            mSwichRegraUmidade.setChecked(true);
            mSeekBarUmidade.setProgress(mApplication.mRegra[regra].mUmidade);
            mTextViewUmidade.setText("Irrigar quando ficar abaixo de: " + mApplication.mRegra[regra].mUmidade + " %");
        }
        else{
            // Sem regra de umidade
            mSwichRegraUmidade.setChecked(false);
            mTextViewUmidade.setText("Qualquer Umidade");
        }

        // Verifica se há regra de horario
        if ((mApplication.mRegra[regra].mHoras >= 0 && mApplication.mRegra[regra].mHoras <= 23) && (mApplication.mRegra[regra].mMinutos >= 0 && mApplication.mRegra[regra].mMinutos <= 59)){
            setEnabledForEveryChildView(true, mViewHorario);
            setEnabledForEveryChildView(true, mViewTempo);
            setEnabledForEveryChildView(false, mViewEspera);
            mSwichRegraHorario.setChecked(true);
            mTextViewHorario.setText("Irrigar às:  " + String.format("%02d", mApplication.mRegra[regra].mHoras) + ":" + String.format("%02d", mApplication.mRegra[regra].mMinutos) + " horas.");
            mTextViewDiaSemana.setText("Nos dias da semana:");

            mCheckBoxDom.setChecked((mApplication.mRegra[regra].mDiaSemana & Regra.DOMINGO) == Regra.DOMINGO);
            mCheckBoxSeg.setChecked((mApplication.mRegra[regra].mDiaSemana & Regra.SEGUNDA) == Regra.SEGUNDA);
            mCheckBoxTer.setChecked((mApplication.mRegra[regra].mDiaSemana & Regra.TERCA) == Regra.TERCA);
            mCheckBoxQua.setChecked((mApplication.mRegra[regra].mDiaSemana & Regra.QUARTA) == Regra.QUARTA);
            mCheckBoxQui.setChecked((mApplication.mRegra[regra].mDiaSemana & Regra.QUINTA) == Regra.QUINTA);
            mCheckBoxSex.setChecked((mApplication.mRegra[regra].mDiaSemana & Regra.SEXTA) == Regra.SEXTA);
            mCheckBoxSab.setChecked((mApplication.mRegra[regra].mDiaSemana & Regra.SABADO) == Regra.SABADO);
        }
        else{
            mSwichRegraHorario.setChecked(false);
            mTextViewHorario.setText("Qualquer horário");
            mTextViewDiaSemana.setText("Todos os dias da semana");

            mCheckBoxDom.setChecked(true);
            mCheckBoxSeg.setChecked(true);
            mCheckBoxTer.setChecked(true);
            mCheckBoxQua.setChecked(true);
            mCheckBoxQui.setChecked(true);
            mCheckBoxSex.setChecked(true);
            mCheckBoxSab.setChecked(true);
        }
    }
}
private void LeLogs (){
    int i = 0;

    // Verifica se é o estado inicial
    if (mEstadoLeituraLogs == LEITURA_LOG_INICIA){

        // Seta flag de leitura de Logs que suspende outras comunicações via bluetooth
        mApplication.mLendoLogs = true;

        // Inicia controle de leitura dos packets
        for (i = 0; i < IrrigadorApplication.NUM_LOG_PACKETS + 1; i++) {
            mLogPacketLido[i] = false;
        }

        // Muda estado da leitura
        mEstadoLeituraLogs = LEITURA_LOG_LENDO;

        // Ajusta interface gráfica
        mViewLogs.setVisibility(View.GONE);
        mViewParent.setVisibility(View.GONE);
        mViewCancelar.setVisibility(View.VISIBLE);
        mTextViewDesconectado.setVisibility(View.VISIBLE);
        mTextViewDesconectado.setText("Lendo dados...");
    }

    // Lendo logs
    if (mEstadoLeituraLogs == LEITURA_LOG_LENDO){

        // Verifica todos os log packets
        for (i = 0; i <= IrrigadorApplication.NUM_LOG_PACKETS; i++) {

            // Verifica se já foi lido
            if (!mLogPacketLido[i]) {

                // Envia solicitação para a central
                mApplication.LeLogPacket(i);
                
                // Muda estado para aguardando resposta
                mEstadoLeituraLogs = LEITURA_LOG_AGUARDANDO;
                
                // Seta timeout de espera
                mTimerHandler.postDelayed(mTmerRunnable, UPDATE_PERIOD);

                // Atualiza interface gráfica 
                mTextViewDesconectado.setText("Lendo Pacote " + (i+1) + "/" + (IrrigadorApplication.NUM_LOG_PACKETS +1));

                Log.i("LogFragment", "Lendo Pacote " + i);
                
                // Interrompe o loop para ler apenas um log de cada vez
                break;
            }
        }

        // Verifica se leu todos os Logs
        if (i > IrrigadorApplication.NUM_LOG_PACKETS){

            // Terminou leitura
            mApplication.mLendoLogs = false;

            // Ajusta interface gráfica
            mViewLogs.setVisibility(View.VISIBLE);
            mViewParent.setVisibility(View.VISIBLE);
            mTextViewDesconectado.setVisibility(View.GONE);
            mViewCancelar.setVisibility(View.GONE);

            // Atualiza os dados dos LogsFragment
            UpdateLogsFragment();
            
            // Muda estado para finalizado
            mEstadoLeituraLogs = LEITURA_LOG_FINALIZADO;

            // Atualiza o gráfico
            UpdateChart ();
        }
    }
}