“IOT FEITO FÁCIL”: CONECTANDO COISAS DESDE QUALQUER CANTO DO MUNDO!
Nosso objetivo neste tutorial será basicamente coletar informações de uma unidade local, enviando-as à internet.
Um usuário em qualquer parte do planeta, olhando estas informações, tomará decisões enviando comandos remotos à atuadores, os quais também estarão nesta unidade local. Qualquer sensor ou atuador poderia ser utilizado.
Este tutorial foi escrito a partir do que desenvolvi previamente envolvendo o ArduFarmBot e de meu projeto final apresentado para o curso: “Objetos inteligentes conectados”, promovido pelo CodeIOT, um projeto da Samsung em parceria com o Laboratório de Sistemas Integráveis Tecnológico.
A maioria de meu trabalho no campo do IoT utiliza o NodeMCU (ESP826612-E) e mais recentemente, o ESP32. Mas, acredito importante não esquecer de meus primeiros passos, onde começei a aprender IoT, usando-se de um simples Arduino UNO e do velho e bom ESP8266-01.
Decidi então, retornar a essa dupla, agora com um pouquinho mais de experiência e explorar novamente esses ótimos dispositivos, conectando-os à nuvem, usando-se do ThingSpeak.com como nosso “Web Service”.
Também exploraremos como controlar coisas remotamente desde qualquer lugar no mundo, utilizando-se de um aplicativo Android desenvolvido a partir do MIT AppInventor.
O “Centro do nosso projeto IoT” será o ThingSpeak.com. A unidade local (UNO / ESP-01) será a encarregada de capturar tanto os dados dos sensores quanto o status dos atuadores, enviando-os à Internet, ou seja, “escrevendo” em um canal específico do para o status da unidade local no ThingSpeak.com. A unidade local também receberá dados da internet, “lendo” canais específicos para os atuadores no ThingSpeak.com.
Um aplicativo Android também estará “lendo” esses dados de status guardados no ThingSpeak.com (Status Channel), exibindo-os para o usuário. Da mesma forma, o usuário, com base nesta informação de status, poderá enviar comandos para os atuadores, escrevendo commandos nos canais específicos para os atuadores no ThingSpeak.com (veja o diagrama de blocos acima para entender melhor o fluxo de dados).
O diagrama de blocos mostrado na próxima etapa nos dará uma visão geral do projeto final, onde como exemplo controlaremos a irrigação e o calor de uma plantação.
1: Introdução
Utilizando-se de sensores comuns, nosso projeto irá capturar vários dados, enviando-os para a nuvem, onde todos poderão vê-los através da internet. Para trabalhar esses dados, usaremos o serviço fornecido pela ThingSpeak.com, uma plataforma IoT aberta que nos permitirá coletar, analisar e atuar sobre esses dados.
Os dados a serem coletados pelos sensores serão:
- Temperatura e umidade relativa do a
- Temperatura e umidade do solo
- Luminosidade
O projeto terá 2 atuadores:
- Bomba elétrica
- Lâmpada elétrica
Os status desses atuadores (“ON” ou “OFF”), também deverão ser enviados para a nuvem.
A idéia será capturar esses dados dos sensores, por exemplo, uma plantação e enviá-los para a nuvem. Com base nesses dados, um usuário deverá tomar decisões com base nestes dados:
- Liguar a bomba de água se a umidade do solo estiver muito baixa
- Liguar a lâmpada elétrica (“calor”) se a temperatura do solo estiver muito baixa
Para controlar remotamente nossos atuadores, usaremos uma aplicação Android.
2: BoM – Lista de materiais
Os componentes mais importantes listados aqui, possuem um link e um preço indicativo em USD associado a eles. Esses links são apenas para referência.
- Arduino UNO (Microcontrolador) – $10.00
- ESP8266-01 (Módulo de comunicação) – $3.50
- DHT22 (Sensor digital para temperaura e umidade relativa do ar) – $9.00
- DS18B20 (Sensor Digital de temperatura para uso com o solo) – $6.00
- YL-69 + LM393 (Sensor analógico de umidade de solo) – $2.00
- LDR (Sensor analógico de luminosidade) – $0.20
- 2 x LEDs (vermelho e verde)
- 1 x 2 Channel DC 5V Relay Module with Optocoupler Low Level Trigger – $7.00
- 5V DC Pump – $3.00
- Lâmpada de 220V
- 2 resistores de 330 ohm (para serem utilizados com os LEDs)
- 2 resistores de 10K ohm (para serem utilizados com o DHT22 e o LDR)
- 1 resistor de 4K7 ohm (para ser utilizado com o DS18B20)
- Protoboard
- Jumpers
- Fonte externa de 5V DC para alimentação dos Relés
3: O Hardware
Montemos o HW do projeto. O ideal é instalar e testar nosso projeto por partes. Como uma sugestão, podemos seguir as etapas:
- Instale e teste localmente todos os sensores
- Instale e configure o ESP-01 (“BareMinimum”)
- Altere a instalação do ESP-01 para sua configuração final e teste-o
- Configure o canal de Status no ThingSpeak
- Instale o código para conversar com o ThingSpeak em seu Arduino e verifique o status dos Sensores na nuvem
- Desenvolva a primeira versão do aplicativo Android para exibir Status e mensagens
- Instale os atuadores (LEDs e relés)
- Configure os canais dos atuadores no ThingSpeak
- Instale e teste o Código Arduino para trabalhar com os atuadores
- Desenvolva a versão final da aplicação de Android
- Teste todo o projeto
4: Conectando os sensores
Para ler os sensores corretamente, deveremos possuir algumas bibliotecas instaladas no IDE do Arduino. Verifique se você possui todas as bibliotecas instaladas. Sua configuração inicial deve ser:
// DS18B20 #include <OneWire.h> #include <DallasTemperature.h> #define ONE_WIRE_BUS 5 // DS18B20 on pin D5 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature DS18B20(&oneWire); int soilTemp = 0; //DHT #include "DHT.h" #include <stdlib.h> int pinoDHT = 11; int tipoDHT = DHT22; DHT dht(pinoDHT, tipoDHT); int airTemp = 0; int airHum = 0; // LDR (Light) #define ldrPIN 1 int light = 0; // Soil humidity #define soilHumPIN 0 int soilHum = 0; Para o setup() e loop(): void setup() { Serial.begin(9600); DS18B20.begin(); dht.begin(); } void loop() { readSensors(); displaySensors(); delay (10000); }
E finalmente, escreveremos duas funções específicas, uma para ler nossos sensores e outra para exibir seus valores no Serial Monitor:
/********* Read Sensors value *************/ void readSensors(void) { airTemp = dht.readTemperature(); airHum = dht.readHumidity(); DS18B20.requestTemperatures(); soilTemp = DS18B20.getTempCByIndex(0); // Sensor 0 will capture Soil Temp in Celcius soilHum = map(analogRead(soilHumPIN), 1023, 0, 0, 100); light = map(analogRead(ldrPIN), 1023, 0, 0, 100); //LDRDark:0 ==> light 100% } /********* Display Sensors value *************/ void displaySensors(void) { Serial.print ("airTemp (oC): "); Serial.println (airTemp); Serial.print ("airHum (%): "); Serial.println (airHum); Serial.print ("soilTemp (oC): "); Serial.println (soilTemp); Serial.print ("soilHum (%): "); Serial.println (soilHum); Serial.print ("light (%): "); Serial.println (light); Serial.println (""); }
O printScreen do Serial Monitor nos mostra os valores dos sensores:
O código completo poderá ser baixado de meu GITHUB:
Sensors_Test.ino
5: ESP8266-01 – Configuração inicial
O ESP-01 será utilizado como “Serial Bridge”, o que significa que vamos programá-lo usando” AT commands“.
A primeira coisa é ter certeza de que seu ESP-01 está programado com velocidade de comunicação (Baud Rate) correta. No nosso caso: 9.600 bauds. Normalmente, o ESP-01 vem programado da fábrica com 115.200 baud e devemos mudá-lo para 9.600 baud.
Primeiramente, você deverá conectar o ESP-01 como mostrado acima.
Observe que o pino Tx do ESP-01 está conectado ao pino Tx do UNO, o mesmo para os pinos Rx. Isso será alterado mais tarde.
Em seguida, conecte o Arduino ao seu computador, abra o IDE e carregue o exemplo que está em Arquivo> Exemplos> 01.Basics> BareMinimum. Este é um código vazio, utilizado para garantir que não haverá conflito de comunicação entre Arduino e o ESP-01.
Transferiremos este código para o Arduino antes de conectá-lo ao ESP-01, para garantir que o Arduino não use a comunicação serial (Tx e Rx). Isso é importante para o ESP-01 poder comunicar-se adequadamente.
Abra o monitor serial do IDE e altere a velocidade para 115.200 baud. Comece a enviar um comando “AT” no seu Monitor IDE Serial. O ESP-01 deve retornar “OK”
Em seguida, mudemos sua velocidade. Para isso, você poderá utilizar o comando:
AT + CIOBAUD = 9600
Em seguida, mudemos sua velocidade. Para isso, você poderá utilizar o comando:
AT + CIOBAUD = 9600
Observe que o ESP-01 poderá eventualmente retornar à programação original de fábrica (não sei se isso é devido à versão FW). Pelo menos no meu caso, tive que usar um comando diferente para mudar o BaudRate definitivamente:
AT+ UART_DEF=<baudrate>,<databits>,<stopbits>,<parity>,<flow control>
Por exemplo, para configurar: 9600 baud / 8 data bits / 1 stop bits, none parity e flow control:
AT + UART_DEF = 9600,8,1,0,0
Na caixa de seleção na parte inferior do seu Serial Monitor, altere a velocidade para “9600 baud”
Teste a comunicação: na parte superior da janela, digite “AT” e veja se a resposta que chega é “OK”. Agora, você deverá configurar o módulo no Modo Estação para atuar como Cliente de sua rede Wi-Fi. Para isto use o comando:
AT + CWMODE = 1
Agora, deveremos conectar o módulo a rede Wi-Fi.
Para fazer isso, utilize o comando abaixo, substituindo “network_name” pelo nome da sua rede Wi-Fi e “password” com sua senha. Mantenha as aspas.
AT + CWJAP = “network_name”, “password”
Se você ter como resposta o mesmo que abaixo, sua conexão deverá ter sido estabelecida corretamente:
WIFI CONNECTED WIFI GOT IP
Para encontrar o IP, execute o comando:
AT + CIFSR
Tome nota deste IP, voce poderá necessitar do mesmo posteriormente.
6: Testando o ESP-01
Uma vez que temos o ESP-01 configurado, devemos instalá-lo em seu circuito final. Para isso, devemos MUDAR a fiação feita anteriormente e conectar o ESP-01 ao nosso UNO como abaixo:
- ESP-01 RX (Yellow) ao UNO Pino D7
- ESP-01 TX (Orange) ao UNO Pino D6
- ESP-01 Ch-Pd (Brown) ao Vcc (3.3V)
- ESP-01 Reset (Blue) ao UNO Pino D8
- ESP-01 Vcc (Red) ao 3.3V
- ESP-01 GND (Black) ao UNO GND
Note que usaremos a biblioteca do Arduino “Software Serial”, tendo o pino D7 do UNO como Tx, conectado ao pino Rx do ESP-01 e o pino D6 doUNO como Rx, conectado ao pino Tx do ESP-01.
Faremos um simples teste para verificar se o nosso ESP-01 esta corretamante instalado.
Digite o código abaixo:
#include <SoftwareSerial.h> SoftwareSerial esp8266(6,7); //Rx ==> Pin 6; TX ==> Pin7 #define speed8266 9600 void setup() { esp8266.begin (speed8266); Serial.begin(speed8266); Serial.println("ESP8266 Setup test - use AT coomands"); } void loop() { while(esp8266.available()) { Serial.write(esp8266.read()); } while(Serial.available()) { esp8266.write(Serial.read()); } }
Tente agora alguns comandos AT como estes abaixo:
* AT =====> ESP8266 returns OK
* AT+RST =====> ESP8266 restart and returns OK
* AT+GMR =====> ESP8266 returns AT Version; SDK version; id; OK
* AT+CWMODE? => ESP8266 returns mode type
* AT+CWLAP ===> ESP8266 returs close access points
* AT+CIFSR ===> ESP8266 returs designided IP
e observe o resultado em seu IDE Serial monitor:
O código acima poderá ser baixado de meu GITHUB:
Caso voce deseje se conectar a rede WiFi todas as vezes que um reset ocorrer (ou seu arduino seja desligado/ligado) e introduzindo suas credenciais, adicione uma chamada para a função connectWiFi() ao final da função setup():
setup() { ... connectWiFi(); }
A função connectWiFi() deverá estar ao final de seu código principal .ino:
/*************************************************** * Connect WiFi ****************************************************/ void connectWiFi(void) { sendData("AT+RST\r\n", 2000, 0); // reset sendData("AT+CWJAP=\"YOUR USERNAME\",\"YOUR PASSWORD\"\r\n", 2000, 0); //Connect network delay(3000); sendData("AT+CWMODE=1\r\n", 1000, 0); sendData("AT+CIFSR\r\n", 1000, 0); // Show IP Adress Serial.println("8266 Connected"); }
voce deverá entrar com as credencias: “YOUR USERNAME\” and “YOUR PASSWORD\” diretamente na função, substituindo os strings genéricos.
Observe que a função acima chama outra função sendData(data), a qual também deverá estar localizada em seu código:
/*************************************************** * Send AT commands to module ****************************************************/ String sendData(String command, const int timeout, boolean debug) { String response = ""; EspSerial.print(command); long int time = millis(); while ( (time + timeout) > millis()) { while (EspSerial.available()) { // The esp has data so display its output to the serial window char c = EspSerial.read(); // read the next character. response += c; } } if (debug) { Serial.print(response); } return response; }
Em meu GITHub ao final encontrará uma versão completa do projecto (v1.1), a qual contempla a conexão a rede WiFi com credenciais.
7: Conectando sensores e ESP-01 ao UNO
Depois de ter todos os sensores instalados e testados ademais de nosso ESP-01 funcionando corretamente, deixemos tudo junto e preparado para enviar dados para a internet.
8: O ThingSpeak
aberta que nos permitirá coletar, analisar e atuar em dados capturados. Se você ainda não tem, por favor vá para ThingSpeak sign up e siga os passos para a criação de uma conta. É gratis.
Em seguida, crie um novo Canal onde coletaremos o status de nossos 2 atuadores, os dados provenientes dos 5 sensores e um campo sobressalente para uso futuro (ou debug):
- Field 1: Actuator 1
- Field 2: Actuator 2
- Field 3: Air Temperature in oC
- Filed 4: Air Relative Humidity in %
- Field 5: Soil Temperature in oC
- Field 6: Soil Humidity in %
- Field 7: Luminosity in %
- Field 8: Spare
O campo 8 será deixado como sobressalente para ser usado para expansão futura ou para fins de depuração. Por exemplo, vou usá-lo como um “contador” para cada erro de comunicação que ocorre durante o handshake entre o Arduino / ESP-01 com o ThingSpeak.com.
Depois de criar o seu canal (neste caso será o nosso “Canal de Status”), será importante tomar nota de seu ID e de suas chaves, conforme mostrado abaixo:
9: Enviando dados para a nuvem
Neste ponto, já temos nosso Cloud Service disponível e nossos sensores capturando dados localmente. Vamos tomar esses valores e enviá-los para o ThingSpeak.com.
Deveremos ESCREVER no canal do ThingSpeak e para isso, precisaremos enviar uma string do tipo “GET”. Faremos isto em 3 partes:
Enviaremos um “start cmd”, que apontará para a URL do ThingSpeak.com:
AT+CIPSTART=”TCP”,”184.106.153.149″,80
Seguido do comprimento em bytes “length” do comando a ser enviado:
AT+CIPSEND=116
E finalmente o “GET string”, o qual escreverá os dados nos campos correspondentes do canal:
GET /update?api_key=YOUR_WRITE_KEY_HERE&field1=pump&fieldlamp=0&field3=airTemp&field4=airHum&field5=soilTemp&field6=soilHum&field7=light&field8=spare
Observe que não devemos escrever no canal do ThingSpeak em intervalos inferiores a 16 segundos.
O código abaixo fará o trabalho para nós e o PrintScreen acima mostra o resultado final que aparece no IDE Serial Monitor:
// Thingspeak String statusChWriteKey = "YOUR WRITE KEY"; // Status Channel id: 385184 #include <SoftwareSerial.h> SoftwareSerial EspSerial(6, 7); // Rx, Tx #define HARDWARE_RESET 8 // DS18B20 #include <OneWire.h> #include <DallasTemperature.h> #define ONE_WIRE_BUS 5 // DS18B20 on pin D5 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature DS18B20(&oneWire); int soilTemp = 0; //DHT #include "DHT.h" #include <stdlib.h> int pinoDHT = 11; int tipoDHT = DHT22; DHT dht(pinoDHT, tipoDHT); int airTemp = 0; int airHum = 0; // LDR (Light) #define ldrPIN 1 int light = 0; // Soil humidity #define soilHumPIN 0 int soilHum = 0; // Variables to be used with timers long writeTimingSeconds = 17; // ==> Define Sample time in seconds to send data long startWriteTiming = 0; long elapsedWriteTime = 0; // Variables to be used with Actuators boolean pump = 0; boolean lamp = 0; int spare = 0; boolean error; void setup() { Serial.begin(9600); pinMode(HARDWARE_RESET,OUTPUT); digitalWrite(HARDWARE_RESET, HIGH); DS18B20.begin(); dht.begin(); EspSerial.begin(9600); // Comunicacao com Modulo WiFi EspHardwareReset(); //Reset do Modulo WiFi startWriteTiming = millis(); // starting the "program clock" } void loop() { start: //label error=0; elapsedWriteTime = millis()-startWriteTiming; if (elapsedWriteTime > (writeTimingSeconds*1000)) { readSensors(); writeThingSpeak(); startWriteTiming = millis(); } if (error==1) //Resend if transmission is not completed { Serial.println(" <<<< ERROR >>>>"); delay (2000); goto start; //go to label "start" } } /********* Read Sensors value *************/ void readSensors(void) { airTemp = dht.readTemperature(); airHum = dht.readHumidity(); DS18B20.requestTemperatures(); soilTemp = DS18B20.getTempCByIndex(0); // Sensor 0 will capture Soil Temp in Celcius light = map(analogRead(ldrPIN), 1023, 0, 0, 100); //LDRDark:0 ==> light 100% soilHum = map(analogRead(soilHumPIN), 1023, 0, 0, 100); } /********* Conexao com TCP com Thingspeak *******/ void writeThingSpeak(void) { startThingSpeakCmd(); // preparacao da string GET String getStr = "GET /update?api_key="; getStr += statusChWriteKey; getStr +="&field1="; getStr += String(pump); getStr +="&field2="; getStr += String(lamp); getStr +="&field3="; getStr += String(airTemp); getStr +="&field4="; getStr += String(airHum); getStr +="&field5="; getStr += String(soilTemp); getStr +="&field6="; getStr += String(soilHum); getStr +="&field7="; getStr += String(light); getStr +="&field8="; getStr += String(spare); getStr += "\r\n\r\n"; sendThingSpeakGetCmd(getStr); } /********* Reset ESP *************/ void EspHardwareReset(void) { Serial.println("Reseting......."); digitalWrite(HARDWARE_RESET, LOW); delay(500); digitalWrite(HARDWARE_RESET, HIGH); delay(8000);//Tempo necessário para começar a ler Serial.println("RESET"); } /********* Start communication with ThingSpeak*************/ void startThingSpeakCmd(void) { EspSerial.flush();//limpa o buffer antes de começar a gravar String cmd = "AT+CIPSTART=\"TCP\",\""; cmd += "184.106.153.149"; // Endereco IP de api.thingspeak.com cmd += "\",80"; EspSerial.println(cmd); Serial.print("enviado ==> Start cmd: "); Serial.println(cmd); if(EspSerial.find("Error")) { Serial.println("AT+CIPSTART error"); return; } } /********* send a GET cmd to ThingSpeak *************/ String sendThingSpeakGetCmd(String getStr) { String cmd = "AT+CIPSEND="; cmd += String(getStr.length()); EspSerial.println(cmd); Serial.print("enviado ==> lenght cmd: "); Serial.println(cmd); if(EspSerial.find((char *)">")) { EspSerial.print(getStr); Serial.print("enviado ==> getStr: "); Serial.println(getStr); delay(500);//tempo para processar o GET, sem este delay apresenta busy no próximo comando String messageBody = ""; while (EspSerial.available()) { String line = EspSerial.readStringUntil('\n'); if (line.length() == 1) { //actual content starts after empty line (that has length 1) messageBody = EspSerial.readStringUntil('\n'); } GET /update?api_key=YOUR_WRITE_KEY_HERE&field1=pump&fieldlamp=0&field3=airTemp&field4=airHum&field5=soilTemp&field6=soilHum&field7=light&field8=spare } Serial.print("MessageBody received: "); Serial.println(messageBody); return messageBody; } else { EspSerial.println("AT+CIPCLOSE"); // alert user Serial.println("ESP8266 CIPSEND ERROR: RESENDING"); //Resend... spare = spare + 1; error=1; return "error"; } }
O código acima poderá ser baixado de meu GITHUB: SendingStatusTS_EXT.ino
10: O App Android (1a. parte) – Monitorando o status
Vamos criar nossa primeira parte da aplicação de Android.
Primeiro, criaremos a interface do usuário. Abaixo vemos os principais elementos visíveis e não visíveis:
Em seguida, programemos os blocos:
- As variáveis de status devem ser declaradas como globais (#1).
- A cada 2 segundos, definido pelo Clock1 (#2), chamaremos um procedimento chamado: “readArduino”.
- O retorno de tal procedimento será o valor para cada uma das variáveis Status que devem ser exibidas na tela.
- Note que “converteremos” os valores “0” e “1” do status dos atuadores para “OFF” e “ON” para uma melhor compreensão.
- Esses valores (“Status”) serão exibidos no correspondente “rótulos”
- O procedimento “readArduino”, na verdade, lerá o Status Channel no ThingSpeak. Então, devemos definir o URL a ser enviado para o Thingspeak.
- Para isso, 3 variáveis globais devem ser declaradas e juntas para criar o URL a ser enviado ao TS (#3) . Um GET deve ser enviado para o componente Web chamado “ArduFarmBotStatusCh”
- O texto obtido pela função anterior, chegará no formato JSon (#4). Este texto deverá então ser processado (cada campo lido e armazenado na variável global correspondente).
Em seguida, lidaremos com as mensagens de alarme:
- O procedimento “Alarm” (#5), analisará o status dos dois sensores relativos ao solo. Se a temperatura for muito baixa (no caso 10oC), uma mensagem deverá ser exibida. O mesmo para a umidade, se for inferior a 60%.
- Observe que definimos outro temporizador (Clock2), programado para ser disparado cada 1 segundo. Isto é apenas para “alternar” a cor do texto da mensagem (do branco ao vermelho). Isso fará com que a mensagem “pisque”.
foto abaixo mostra o App funcionando:
O codigo no formato .aia poderá ser baixado desde meu GITHUB:
ArduFarmBot_Status_App_EXT.aia
11: Instalando os atuadores (LEDs e Relés)
Vamos completar o nosso HW.
Para isso, devemos instalar os Atuadores. Como você se lembra, receberemos comandos remotamente para ligar e desligar uma bomba e uma lâmpada. A saída do Arduino ativará um Relé (e um LED) para obter essas ações.
Utilizaremos um módulo de relé que tenha acionamento por “nível baixo” (ou seja “0”) e isolamento óptico. Além disso, forneceremos os 5V de alimentação para este relé através de uma fonte separada do Arduino e portanto não precisaremos fornecer a corrente necessária para atuação do relé em seu pino de entrada. O módulo do relé fará isso para nós.
A figura acima mostra como os atuadores devem estar conectados. Observe que o pino GND do relé NÃO ESTÁ CONECTADO ao GND do Arduino. Isso ajudará a que o UNO não receba ruídos quando o relé funcionar.
Por simplicidade, omiti do diagrama os sensores. Mas você poderá adicionar os circuitos dos atuadores ao seu projeto sem remover o dos sensores, os quais você já instalou e testou.
12: Configurando os canais de atuadores no ThingSpeak
Da mesma forma que fizemos para o Status, criaremos agora 2 novos canais, um para cada atuador.
De cada canal, tome nota de seu ID e chaves de leitura e escrita.
Escreveremos apenas no Campo 1 de cada um desses canais. Em meu caso, por exemplo:
- Channel ID 375598 ==> LED Vermelho – Bomba (Pump)
Field1 = 0 ==> Pump OFF
Field1 = 1 ==> Pump ON - Channel ID 375599 ==> LED verde – Lâmpada (Lamp)
Field1 = 0 ==> Lamp OFF
Field1 = 1 ==> Lamp ON
13: Introduzindo os atuadores no código do Arduino
Quando enviamos dados para a Web, o que fizemos foi ESCREVER em um canal ThingSpeak (canal de Status ). O Arduino/ESP-01 deve “transmitir” (upload) estes dados. Agora, devemos LER de um canal ThingSpeak (Canal Atuador). O Arduino deverá “receber” (download) estes dados.
LEREMOS de um canal ThingSpeak e para isso, precisaremos enviar uma “string do tipo GET”. Faremos isto em 3 partes:
Enviaremos um “start cmd”, que apontará para a URL do ThingSpeak.com:
AT+CIPSTART=”TCP”,”184.106.153.149″,80
Seguido do comprimento em bytes “length” do comando a ser enviado:
AT+CIPSEND=36
E finalmente o “GET string”, o qual lerá o último valor armazenado no campo 1 correspondente a cada canal do atuador:
GET /channels/375598/fields/1/last
Nós estaremos lendo dos canais do ThingSpeak em intervalos de 10 segundos
Depois de enviar o comando GET acima, o qual estará solicitando o “ÚLTIMO VALOR ARMAZENADO NO CAMPO 1”, receberemos uma resposta do ThingSpeak que deve ser “1” ou “0” em uma posição específica da resposta. Se algo diferente disso chegar, devemos ignorá-lo.
A principal diferença entre esta parte do código e a anterior (para enviar os dados de status) é a função:
readThingSpeak(String channelID)
O código abaixo fará o trabalho para nós e o PrintScreen acima mostra o resultado final que aparece no IDE Serial Monitor:
// Thingspeak String canalID1 = "999999"; //Actuator1 String canalID2 = "999999"; //Actuator2 #include <SoftwareSerial.h> SoftwareSerial EspSerial(6, 7); // Rx, Tx #define HARDWARE_RESET 8 // Variables to be used with timers long readTimingSeconds = 10; // ==> Define Sample time in seconds to receive data long startReadTiming = 0; long elapsedReadTime = 0; //Relays #define ACTUATOR1 10 // RED LED ==> Pump #define ACTUATOR2 12 // GREEN LED ==> Lamp boolean pump = 0; boolean lamp = 0; int spare = 0; boolean error; void setup() { Serial.begin(9600); pinMode(ACTUATOR1,OUTPUT); pinMode(ACTUATOR2,OUTPUT); pinMode(HARDWARE_RESET,OUTPUT); digitalWrite(ACTUATOR1, HIGH); //o módulo relé é ativo em LOW digitalWrite(ACTUATOR2, HIGH); //o módulo relé é ativo em LOW digitalWrite(HARDWARE_RESET, HIGH); EspSerial.begin(9600); // Comunicacao com Modulo WiFi EspHardwareReset(); //Reset do Modulo WiFi startReadTiming = millis(); // starting the "program clock" } void loop() { start: //label error=0; elapsedReadTime = millis()-startReadTiming; if (elapsedReadTime > (readTimingSeconds*1000)) { int command = readThingSpeak(canalID1); if (command != 9) pump = command; delay (5000); command = readThingSpeak(canalID2); if (command != 9) lamp = command; takeActions(); startReadTiming = millis(); } if (error==1) //Resend if transmission is not completed { Serial.println(" <<<< ERROR >>>>"); delay (2000); goto start; //go to label "start" } } /********* Take actions based on ThingSpeak Commands *************/ void takeActions(void) { Serial.print("Pump: "); Serial.println(pump); Serial.print("Lamp: "); Serial.println(lamp); if (pump == 1) digitalWrite(ACTUATOR1, LOW); else digitalWrite(ACTUATOR1, HIGH); if (lamp == 1) digitalWrite(ACTUATOR2, LOW); else digitalWrite(ACTUATOR2, HIGH); } /********* Read Actuators command from ThingSpeak *************/ int readThingSpeak(String channelID) { startThingSpeakCmd(); int command; // preparacao da string GET String getStr = "GET /channels/"; getStr += channelID; getStr +="/fields/1/last"; getStr += "\r\n"; String messageDown = sendThingSpeakGetCmd(getStr); if (messageDown[5] == 49) { command = messageDown[7]-48; Serial.print("Command received: "); Serial.println(command); } else command = 9; return command; } /********* Reset ESP *************/ void EspHardwareReset(void) { Serial.println("Reseting......."); digitalWrite(HARDWARE_RESET, LOW); delay(500); digitalWrite(HARDWARE_RESET, HIGH); delay(8000);//Tempo necessário para começar a ler Serial.println("RESET"); } /********* Start communication with ThingSpeak*************/ void startThingSpeakCmd(void) { EspSerial.flush();//limpa o buffer antes de começar a gravar AT+CIPSTART="TCP","184.106.153.149",80 String cmd = "AT+CIPSTART=\"TCP\",\""; cmd += "184.106.153.149"; // Endereco IP de api.thingspeak.com cmd += "\",80"; EspSerial.println(cmd); Serial.print("enviado ==> Start cmd: "); Serial.println(cmd); if(EspSerial.find("Error")) { Serial.println("AT+CIPSTART error"); return; } } /********* send a GET cmd to ThingSpeak *************/ String sendThingSpeakGetCmd(String getStr) { String cmd = "AT+CIPSEND="; cmd += String(getStr.length()); EspSerial.println(cmd); Serial.print("enviado ==> lenght cmd: "); Serial.println(cmd); if(EspSerial.find((char *)">")) { EspSerial.print(getStr); Serial.print("enviado ==> getStr: "); Serial.println(getStr); delay(500);//tempo para processar o GET, sem este delay apresenta busy no próximo comando String messageBody = ""; while (EspSerial.available()) { String line = EspSerial.readStringUntil('\n'); if (line.length() == 1) { //actual content starts after empty line (that has length 1) messageBody = EspSerial.readStringUntil('\n'); } } Serial.print("MessageBody received: "); Serial.println(messageBody); return messageBody; } else { EspSerial.println("AT+CIPCLOSE"); // alert user Serial.println("ESP8266 CIPSEND ERROR: RESENDING"); //Resend... spare = spare + 1; error=1; // Thingspeak String canalID1 = "999999"; //Actuator1 String canalID2 = "999999"; //Actuator2 #include <SoftwareSerial.h> SoftwareSerial EspSerial(6, 7); // Rx, Tx #define HARDWARE_RESET 8 // Variables to be used with timers long readTimingSeconds = 10; // ==> Define Sample time in seconds to receive data long startReadTiming = 0; long elapsedReadTime = 0; //Relays #define ACTUATOR1 10 // RED LED ==> Pump #define ACTUATOR2 12 // GREEN LED ==> Lamp boolean pump = 0; boolean lamp = 0; int spare = 0; boolean error; void setup() { Serial.begin(9600); pinMode(ACTUATOR1,OUTPUT); pinMode(ACTUATOR2,OUTPUT); pinMode(HARDWARE_RESET,OUTPUT); digitalWrite(ACTUATOR1, HIGH); //o módulo relé é ativo em LOW digitalWrite(ACTUATOR2, HIGH); //o módulo relé é ativo em LOW digitalWrite(HARDWARE_RESET, HIGH); EspSerial.begin(9600); // Comunicacao com Modulo WiFi EspHardwareReset(); //Reset do Modulo WiFi startReadTiming = millis(); // starting the "program clock" } void loop() { start: //label error=0; elapsedReadTime = millis()-startReadTiming; if (elapsedReadTime > (readTimingSeconds*1000)) { int command = readThingSpeak(canalID1); if (command != 9) pump = command; delay (5000); command = readThingSpeak(canalID2); if (command != 9) lamp = command; takeActions(); startReadTiming = millis(); } if (error==1) //Resend if transmission is not completed { Serial.println(" <<<< ERROR >>>>"); delay (2000); goto start; //go to label "start" } } /********* Take actions based on ThingSpeak Commands *************/ void takeActions(void) { Serial.print("Pump: "); Serial.println(pump); Serial.print("Lamp: "); Serial.println(lamp); if (pump == 1) digitalWrite(ACTUATOR1, LOW); else digitalWrite(ACTUATOR1, HIGH); if (lamp == 1) digitalWrite(ACTUATOR2, LOW); else digitalWrite(ACTUATOR2, HIGH); } /********* Read Actuators command from ThingSpeak *************/ int readThingSpeak(String channelID) { startThingSpeakCmd(); int command; // preparacao da string GET String getStr = "GET /channels/"; getStr += channelID; getStr +="/fields/1/last"; getStr += "\r\n"; String messageDown = sendThingSpeakGetCmd(getStr); if (messageDown[5] == 49) { command = messageDown[7]-48; Serial.print("Command received: "); Serial.println(command); } else command = 9; return command; } /********* Reset ESP *************/ void EspHardwareReset(void) { Serial.println("Reseting......."); digitalWrite(HARDWARE_RESET, LOW); delay(500); digitalWrite(HARDWARE_RESET, HIGH); delay(8000);//Tempo necessário para começar a ler Serial.println("RESET"); } /********* Start communication with ThingSpeak*************/ void startThingSpeakCmd(void) { EspSerial.flush();//limpa o buffer antes de começar a gravar AT+CIPSTART="TCP","184.106.153.149",80 String cmd = "AT+CIPSTART=\"TCP\",\""; cmd += "184.106.153.149"; // Endereco IP de api.thingspeak.com cmd += "\",80"; EspSerial.println(cmd); Serial.print("enviado ==> Start cmd: "); Serial.println(cmd); if(EspSerial.find("Error")) { Serial.println("AT+CIPSTART error"); return; } return "error"; } }
O código completo apara a leitura dos comandos poderá ser baixado de meu GITHUB:
14: Enviando comandos aos atuadores
Neste ponto, temos os canais atuadores configurados no ThingSpeak e alterando o valor do Campo 1 em cada canal, deveremos ver os atuadores mudarem de acordo.
Em nosso projeto final, faremos essa tarefa utilizando-se do aplicativo Android, mas para efeito de testes, poderemos fazer isso utilizando-se de um navegador.
Os comandos são:
Turn ON Pump (RED LED):
https://api.thingspeak.com/update?api_key=ACT1_WRITE_KEY&field1=1
Turn OFF Pump (RED LED):
https://api.thingspeak.com/update?api_key=ACT1_WRITE_KEY&field1=0
Turn ON Lamp (GREEN LED):
https://api.thingspeak.com/update?api_key=ACT2_WRITE_KEY&field1=1
Turn OFF Lamp (GREEN LED):
https://api.thingspeak.com/update?api_key=ACT2_WRITE_KEY&field1=0
N parte superior da figura acima, você poderá observar o envio do comando para ligar a bomba em um navegador e na parte inferior da figura, como este comando aparecerá no IDE Serial Monitor. Obviamente, o LED Vermelho e o relé também serão ativados.
15: Completando o App Android
Completemos agora a APP. Anteriormente, desenvolvemos uma aplicação simples que obtém o status do ThingSpeak, LENDO o “Status Channel”. Agora, deveremos ESCREVER nos canais do Atuador para que esses comandos possam ser lidos pelo Arduino e atuem em Bomba e Lâmpada de acordo.
Para que um usuário passe os comandos para o aplicativo, usaremos “botões”. Um par de botões (ON e OFF) para cada um dos atuadores.Quando um botão é pressionado, a cor do texto deverá mudar.
- If ON ==> Azul
- if OFF ==> Vermelho
Acima, você pode ver o conjunto de blocos para cada um dos pares de botões.
O App final pode ser visto abaixo:
Teste o aplicativo, enviando comandos para ligar e desligar os atuadores. Verifique no monitor serial de seu IDE, as mensagens trocadas entre ESP-01 e ThingSpeak (deverão ser semelhantes as que vimos anteriormente).
O codigo completo para o APP poderá ser baixado de meu GITHUB:
16: Juntando tudo!
Perfeito! Neste ponto, você tem uma aplicação completa para ser utilizada em seu dispositivo Android, um HW completo, mas você ainda não possui um código que continuamente leia e escreva no ThingSpeak. Vamos combinar tudo o que desenvolvemos anteriormente.
No código final, você encontrará porções adicionais para verificar, por exemplo, se o ESP-01 não está congelando. Nós faremos isso, enviando um comando AT antes de que o ESP-01 execute uma função de ler ou escrever no ThinSpeak. Como vimos no início deste tutorial, o envio de um simples comando: “AT” ao ESP-01 deve receber como resposta um “OK”. Se isso não acontecer, procederemos com uma reinicialização de HW, comandada pelo SW, como fazemos durante a fase de setup().
O código final completo poderá ser baixado de meu GITHUB:
Uma versão adicional (v1.1) foi adicionada a meu depositário, a qual inclui conexão rede WiFi local usando credenciais: versão 1.1
17: Conclusão
Há muito a ser explorado na arena IoT com estes fantásticos dispositivos, o Arduino Uno e o ESP8266-01 juntamente com o ThingSpeak e o MIT App Inventor. Voltaremos com novos tutoriais! Continue seguindo os tutoriais do MJRoBot!
Como sempre, espero que este projeto possa ajudar os outros a encontrar seu caminho no excitante mundo da eletrônica, da robótica e do IoT!
Visite meu GitHub para arquivos atualizados: ArduFarmBot_Light
Saludos desde el sur del mundo!
Até meu próximo post!