Monitor de Saúde de Rios: Sistema de Qualidade da Água Baseado em Arduino
Componentes e Materiais
| Componente | Quantidade |
|---|---|
| Arduino MKR1000 (MKR FOX 1200) | 1 |
| Sensor de Nível de Água | 1 |
| Sensor de pH | 1 |
| LED (branco, preferencialmente) | 1 |
| Resistor 220 ohm | 2 |
| Fotorresistor (LDR) | 1 |
| Jumpers | 1 kit |
Ferramentas Necessárias
- Ferro de solda
- Serra de arco (hacksaw)
Aplicativos e Serviços Online
- Arduino Web Editor
- Sigfox
- Wia (plataforma IoT)
Sobre o Projeto
Visão Geral
Um rio ou lago pode ser ótima fonte de lazer e relaxamento. Mas se a água fica suja e contaminada, a diversão acaba. Muitas pessoas dependem da água do rio para seus animais e não podem dar água suja a eles.
Imagine morar à beira de um rio: não quer acordar de madrugada com a casa alagada por enchente. Ou se um vizinho jogar gasolina no rio, não quer seus animais adoecendo.
O MyRiver resolve esses problemas. Opera em áreas isoladas graças à rede Sigfox, dando acesso a todos. O dispositivo lê o pH da água, nível de poluição (método inovador) e nível da água. Você monitora o rio em tempo real no celular ou computador via plataforma Wia. Assim, sabe se há problema no curso d'água e pode agir antes que seja tarde. Notifica imediatamente se houver problema.
Funcionalidade
Baseado no Arduino MKR FOX, projetado para uso remoto. Coloque o dispositivo no rio/lago e monitore de qualquer lugar. Graças ao modo de baixa energia do SAM D, funciona por longo período com 2 pilhas AA.
Faz leituras a cada 30 minutos, entrando em sleep depois (mínimo 20 minutos de sleep). O MKR FOX lê:
- pH do rio via sensor de pH;
- Nível da água via sensor de nível;
- Poluição por substâncias insolúveis: um LED brilha sobre um fotorresistor – se a leitura for baixa, substância sólida (óleo, gasolina, mercúrio, etc.) está presente na água.
Dados enviados ao Sigfox, repassados ao Wia, processados e visualizados. Usuário notificado sobre o estado do rio.
O buffer é enviado ao Sigfox codificado em HEX. Sigfox repassa ao Wia, onde passa por um flow: convertido de volta para string, depois para valores dos sensores, processados e usuário notificado se valores fora dos limites.
Partes Principais do Código
Read Sensors: lê pH, nível da água e fotorresistor, armazena em variáveis;Format Buffer: mescla os 3 valores em buffer de 12 bytes para envio ao Sigfox;Parse Data: envia o buffer ao Sigfox.
O MKR FOX envia apenas 12 bytes via Sigfox. Todos os valores dos sensores (máximo 1024, analógicos) devem caber no buffer: cada sensor ocupa 4 bytes (1/3 do buffer). O Arduino converte inteiros em strings, preenche com zeros à esquerda até 4 bytes, e mescla.
Exemplo: fotorresistor = 620 → 0620 (um zero). Nível da água = 24 → 0024 (dois zeros). Valores mesclados em buffer de 12 bytes e enviados ao SigFox.
O Dispositivo em Ação
Benefícios
O usuário beneficia-se em:
- Monitorar facilmente nível da água, pH e poluição de qualquer lugar, a qualquer hora;
- Receber notificação se rio poluído (substâncias solúveis/insolúveis) ou risco de enchente;
- Facilidade de uso e instalação;
- Funciona em qualquer país com cobertura Sigfox.
Construindo o Projeto
Passo 1: Material Necessário
Todos os componentes devem ser soldados (não usa protoboard). Lista:
- 1x Arduino MKR FOX 1200
- 1x Sensor de pH
- 1x Sensor de Nível de Água
- 1x Fotorresistor
- 1x LED Branco
- 2x Resistores 220Ω
- Jumpers
Passo 2: Conectando o Circuito
O diagrama pode ser confuso pois componentes não estão em protoboard. Se tiver dificuldade, baixe o arquivo Fritzing e reorganize os fios no computador.
Preparando o MKR FOX
Conecte a caixa de bateria e antena ao MKR FOX. A caixa de bateria deve ser conectada após os sensores. Outras ligações em Constructing the Project / Final.
Passo 3: Entendendo o Código
Três partes principais:
Read Sensors
struct GetValue {
int riverLevel() {
const int val = analogRead(A2);
if(proDebug) {
Serial.print(" River Level ");
Serial.println(val);
}
return val;
}
int riverPh() {
const int val = (analogRead(A1) / 5 * 3.3);
if(proDebug) {
Serial.print(" River Ph ");
Serial.println(val);
}
return val;
}
int riverPol() {
const int val = analogRead(A3);
if(proDebug) {
Serial.print(" River Pollution ");
Serial.println(val);
}
return val;
}
};
Estrutura com loops para ler cada sensor separadamente.
Format Buffer
String data(int level, int ph, int pol) {
int values[3] = {level, ph, pol};
String newValues;
for(int i = 0; i < 3; i++) {
if(values[i] < 10) {
newValues += "000";
} else if(values[i] < 100) {
newValues += "00";
} else if(values[i] < 1000) {
newValues += "0";
}
newValues += values[i];
}
return newValues;
}
Recebe 3 valores, coloca em array, percorre adicionando zeros à esquerda até 4 bytes cada, concatena em string retornada.
Parse Data
void parseData(String data) {
SigFox.beginPacket();
SigFox.print(data);
int ret = SigFox.endPacket();
}
Envia string formatada ao Sigfox.
Passo 4: Configurando Integração Wia + Sigfox
Wia é plataforma IoT profissional para visualizar dados via widgets (web/app) e criar flows (código em blocos) para processar dados. Dados representados por eventos.
Pré-requisitos: conta Sigfox, conta Wia, app Wia no iOS/Android para notificações.
Configuração do webhook no Sigfox para Wia:
- Type:
Data Uplink - Channel:
URL - Custom Payload Configuration: deixar vazio
- URL Pattern:
https://api.wia.io/v1/events - HTTP Method:
POST - Header:
Authorisation=Bearer <seu_token> - ContentType:
application/json - Body: copiar código do link fornecido
- Clique OK
Passo 5: Configurando Flow no Wia
O flow converte dados de HEX para String, parseia em 3 valores dos sensores, converte para inteiros, transforma poluição e nível em "High/OK" e pH real, notifica se fora dos limites.
Funções Wia (copie e cole)
processPayload
if(input.body) {
output.body.name = "value";
output.body.data = toByteArrayStr(input.body.data.sigfoxData);
}
function toByteArrayStr(sigfoxData) {
let result = Buffer(sigfoxData, 'hex');
return result.toString('utf8');
}
processPollution
if(input.body.data) {
let data = input.body.data;
if(data == "POLLUTED") {
output.body.name = "waterLevel";
output.body.data = data;
} else {
while(1);
}
}
processPh
let minVal = 6.5;
let maxVal = 8.5;
if(input.body.data) {
let data = input.body.data;
if(data > maxVal || data < minVal) {
output.body.name = "waterPh";
output.body.data = data;
} else {
while(1);
}
}
processLevel
if(input.body.data) {
let data = input.body.data;
if(data == "HIGH") {
output.body.name = "waterLevel";
output.body.data = data;
} else {
while(1);
}
}
riverPollution
let minVal = 200;
let maxVal = 0;
if(input.body.data.sigfoxData) {
let data = input.body.data.sigfoxData;
let processedData = toByteArrayStr(data);
let newData = cutString(processedData);
output.body.name = "waterPollution";
output.body.data = processData(newData);
}
function toByteArrayStr(sigfoxData) {
let result = Buffer(sigfoxData, 'hex');
return result.toString('utf8');
}
function cutString(data) {
let newVal = data[8] + data[9] + data[10] + data[11];
return parseInt(newVal);
}
function processData(data) {
return (data < minVal) ? "POLLUTED" : "OK";
}
riverPh
let minVal = 300;
let maxVal = 600;
if(input.body.data.sigfoxData) {
let data = input.body.data.sigfoxData;
let processedData = toByteArrayStr(data);
let newData = cutString(processedData);
let finalVal = (newData / 73.1428571);
let valueToSend = finalVal.toFixed(2);
output.body.name = "waterPh";
output.body.data = valueToSend;
}
function toByteArrayStr(sigfoxData) {
let result = Buffer(sigfoxData, 'hex');
return result.toString('utf8');
}
function cutString(data) {
let newVal = data[4] + data[5] + data[6] + data[7];
return parseInt(newVal);
}
riverLevel
let minVal = 0;
let maxVal = 200;
if(input.body.data.sigfoxData) {
let data = input.body.data.sigfoxData;
let processedData = toByteArrayStr(data);
let newData = cutString(processedData);
output.body.name = "waterLevel";
output.body.data = processData(newData);
}
function cutString(data) {
let newVal = data[0] + data[1] + data[2] + data[3];
return parseInt(newVal);
}
function toByteArrayStr(sigfoxData) {
let result = Buffer(sigfoxData, 'hex');
return result.toString('utf8');
}
function processData(data) {
return (data > maxVal) ? "HIGH" : "OK";
}
E-mails de Alerta Wia
riverPollutionEmail
Sistema Automático de Mensagens - Alerta de Dados
Dispositivo myWater ${trigger.source.device.id} detectou problema no rio.
Alerta - Água Poluída por Substâncias Insolúveis.
Info: Dispositivo ${trigger.source.device.id} | Dados Brutos ${input.body.data}
riverPhEmail
Sistema Automático de Mensagens - Alerta de Dados
Dispositivo myWater ${trigger.source.device.id} detectou problema no rio.
Alerta - pH da Água Inadequado. Água Contaminada.
Info: Dispositivo ${trigger.source.device.id} | Dados Brutos ${input.body.data}
riverLevelEmail
Sistema Automático de Mensagens - Alerta de Dados
Dispositivo myWater ${trigger.source.device.id} detectou problema no rio.
Alerta - Nível da Água Muito Alto, Rio Pode Estar Transbordando.
Info: Dispositivo ${trigger.source.device.id} | Dados Brutos ${input.body.data}
Configurando Variáveis
Quando dados chegam ao Wia, são processados. Se entre min/max, valor é seguro. Limites de pH e nível já definidos, mas usuário deve definir limite do fotorresistor manualmente. Teste para determinar:
void setup() {
pinMode(1, OUTPUT);
Serial.begin(9600);
while(!Serial) {};
digitalWrite(1, HIGH);
}
void loop() {
const int val = analogRead(A3);
Serial.println(val);
delay(5000);
}
Outra variável: proDebug. Se habilitado, Arduino precisa de USB + Serial Monitor aberto (imprime para debug). Padrão: false para operação em campo.
Bibliotecas
- Sigfox – copyright (c) 2016 Arduino LLC, GNU LGPL, domínio público
- ArduinoLowPower – (c) 2016 Arduino LLC, GNU LGPL, domínio público
Passo Final: Gabinete
Conecte Arduino ao PC/Mac, faça upload do sketch, certifique-se que caixa de bateria está conectada e circuito OK. Próximos passos: fazer gabinete.
Usei garrafa PET – forma ecológica e criativa. Dispositivo fica como na foto:
Imagens do processo de fabricação do gabinete:
Instalação no Rio
Vá ao rio durante Maré Alta – essencial para não alertar enchente falsamente. Coloque fita na tampa da garrafa para vedar. Fixe o projeto à margem com fita ou parafusos. A sonda deve ter água na base do sensor de nível, cobrindo totalmente o fotorresistor.
Testamos em rio poluído local – todos alarmes dispararam corretamente.
Contexto
Relaxando em casa, olhando a janela para o rio que era belo... mas agora verde e viscoso. Você pensaria em se mudar. Se o rio invadir sua casa à noite enquanto dorme, certamente se mudaria.
Pensei nesses problemas e como resolvê-los num só dispositivo: nasceu o MyRiver.
Na cidade, avisa se rio vai estragar seu dia ou vizinho jogando óleo. Na fazenda, avisa se água suja antes de dar aos animais, e dá tempo de recolhê-los antes de enchente.
Só ler pH não basta – substâncias insolúveis (mercúrio, metais) não alteram pH. Criamos método único: fotorresistor lendo luz de LED atravessando a água. Qualquer impureza é detectada.
Código Completo
Código completo disponível na seção de código do projeto original.
Esquemáticos
Arquivo Fritzing: schematics_lXO5SiusZO.fzz
Processo de manufatura
- Entendendo o Sensor de Oxigênio (O2): Função, Localização e Sinais de Falha
- Construa Monitor de Temperatura Doméstico com Raspberry Pi, MCP9808, InfluxDB e Grafana
- Sensor de Emoção & EEG: Hardware de Baixo Custo para Detecção de Emoções em Tempo Real
- Dispositivo de Precisão para Medição de Pressão e Fluxo de Ar com Sensor Analógico
- Projeto DHT11: Sensor Temperatura/Umidade com LEDs Indicadores e Buzzer
- UnifiedWater v1 – Kit Completo de Sensor de Qualidade da Água com Arduino
- Relógio 3D RGB Arduclock – Bluetooth, LEDs Endereçáveis e RTC
- Construa um Sensor de Obstáculos Simples com Arduino – Tutorial Fácil com LED IR e Fotodiodo
- Jogo Interativo com Joystick e Arduino – Feedback com LEDs
- Find Me: Localizador Inteligente de Objetos com Arduino e Bluetooth