No dia 31 de março aconteceu em Cuiabá o Arduino Day Univag 2018. Centenas de pessoas estiveram no Centro Universitário Univag para compartilhar ideias e experiências sobre Arduino e o mundo maker.
O evento organizado pela Univag e pelos canais Arduino Brasil e Eletrônica Fácil, contou com a presença de makers de renome como: Rodolpho Oliveira, Nascimento Júnior, Waldyr Reis, Alvaro Viebrantz, Fábio Souza, Gedeane Kenshima e eu Marcelo Maximiano, que recebi com imensa alegria o convite de participar desse evento grandioso.
Eu apresentei uma palestra com o tema "Como transformar um protótipo com Arduino em um produto final" e uma oficina prática "IoT - Comunicação entre Máquinas", que apresento neste artigo.
IOT - Comunicação entre máquinas
O objetivo da oficina era mostrar uma implementação simples de IoT (Internet das Coisas), onde um Arduino comandasse remotamente um motor de passo.
O diagrama abaixo mostra a implementação onde um Arduino envia comandos (definidos por um protocolo simples) via comunicação serial para um ESP8266, esse se conecta a um broker MQTT e publica uma mensagem com esse comando no feed "motor".
O ESP8266 no módulo receptor, faz subscrição do mesmo tópico, e assim que recebe a mensagem, a envia via comunicação serial para o Arduino que comanda através do driver de motor ULN2003, um motor de passo modelo 28BYJ-48.
O ESP8266 no módulo receptor, faz subscrição do mesmo tópico, e assim que recebe a mensagem, a envia via comunicação serial para o Arduino que comanda através do driver de motor ULN2003, um motor de passo modelo 28BYJ-48.
Para simplificar o exercício, utilizamos placas MBZ Wifi, pela facilidade de ter o Arduino integrado com o ESP8266. Além disso, no módulo receptor, o ULN2003 foi integrado à placa, ficando assim uma montagem única.
Protocolo de Comando do Motor
A mensagem de comando do motor segue um protocolo simples, definido para esse exercício, composto por 5 comandos:
I - Id do usuário
S - velocidade de movimento do motor. Valores válidos: 10-60
R - girar para a direita X graus. Valores válidos: 1-360
L - girar para a esquerda X graus. Valores válidos: 1-360
P - pausa X milisegundos. Valores válidos: 1-2000
Desta forma uma sequencia complexa de movimentos pode ser enviada em uma única transmissão para o módulo receptor, que fará o parsing da mensagem e executará comando a comando a sequencia de movimentos.
Exemplo:
I:3 S:20 R:180 P:500 L:180 S:60 R:360
ID 3, velocidade 20, gira para a direita 180 graus, pausa por 500ms, gira para a esquerda 180 graus, aumenta a velocidade para 60 e gira para a direita 360 graus.
Criar Feed no serviço IoT
Para poder publicar os dados na nuvem, será necessário criar uma conta e configurar conforme detalhado abaixo:
1) crie uma conta no site iot.adafruit.com
2) crie um feed com o nome "motor"
Programas
Seguem abaixo os programas, numerados conforme o diagrama acima:
Lembre-se de instalar a biblioteca "Adafruit MQTT Library"
Módulo Emissor:
1) Arduino: envia o comando para o ESP8266
2) ESP8266: recebe o comando e envia para o MQTT broker
Módulo Receptor:
3) ESP8266: faz subscrição no MQTT broker e envia o comando para o Arduino
4) Arduino: recebe o comando e faz a movimentação do motor de passo
Programa 1 - para rodar no ATMega328P: send_atmega.ino
/******************************************************************* MBZ MQTT example (ATMEGA328P) ----------------------------- Este programa envia comandos para girar um motor de passo para o módulo ESP8266. O ESP8266 recebe os dados e os envia o serviço de IOT da Adafruit via protocolo MQTT (Message Queuing Telemetry Transport ********************************************************************/ #include <SoftwareSerial.h> #define rxPin 2 #define txPin 3 SoftwareSerial EspSerial(rxPin, txPin); // RX, TX #define PIN_ENABLE_ESP8266 4 void setup() { // seta a velocidade das portas seriais Serial.begin(19200); EspSerial.begin(19200); // inicializa o ESP8266 Serial.println("Inicializando o ESP8266"); pinMode(PIN_ENABLE_ESP8266, OUTPUT); digitalWrite(PIN_ENABLE_ESP8266, LOW); delay(200); digitalWrite(PIN_ENABLE_ESP8266, HIGH); delay(1000); String motorCommand = "I:3 S:20 R:180 P:500 L:180 S:60 R:360 "; EspSerial.print(motorCommand); } void loop() { }
/******************************************************************* MBZ MQTT example (ESP8266) ----------------------------- Este programa recebe os dados uma sequencia de comandos para para girar um motor de passo, e os envia para o serviço IOT da Adafruit via protocolo MQTT ********************************************************************/ #include <ESP8266WiFi.h> #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #define SSID "redewifi" // id do roteador WIFI #define PWD "senha" // senha do roteador WIFI #define SERVER "io.adafruit.com" // servidor MQTT #define PORT 1883 // porta do servidor #define USR "AIO_USER" // usuário do IO Adafruit #define KEY "AIO_KEY" // chave de acesso do IO Adafruit WiFiClient client; Adafruit_MQTT_Client mqtt(&client, SERVER, PORT, USR, KEY); Adafruit_MQTT_Publish MOTOR = Adafruit_MQTT_Publish(&mqtt, USR "/feeds/motor"); // feed 1: luz String serialData; void setup() { Serial.begin(19200); // Conecta no WIFI Serial.println(); Serial.print("Conectando no WIFI: "); Serial.println(SSID); WiFi.begin(SSID, PWD); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.println("WiFi conectado"); Serial.print("IP: "); Serial.println(WiFi.localIP()); Serial.println(); } void loop() { if (getSerialData()) { if (mqtt.connect() == 0) { char buff[100]; serialData.toCharArray(buff,100); // envia dados para o servidor MQTT MOTOR.publish(buff); } } serialData = ""; delay(100); } bool getSerialData() { bool ret = false; if (Serial.available()) { // lê porta serial serialData = Serial.readString(); } if (!serialData.equals("")) { ret = true; } return ret; } int getValue(String str, String tk) { String buff = ""; int p0, p1; p0 = str.indexOf(tk); if (p0 != -1) { p0 = str.indexOf(":", p0) + 1; p1 = str.indexOf(";", p0); buff = str.substring(p0, p1); } return buff.toInt(); } void MQTT_connect() { int8_t ret; // Stop if already connected. if (mqtt.connected()) { return; } Serial.print("Connecting to MQTT... "); uint8_t retries = 3; while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected Serial.println(mqtt.connectErrorString(ret)); Serial.println("Retrying MQTT connection in 5 seconds..."); mqtt.disconnect(); delay(5000); // wait 5 seconds retries--; if (retries == 0) { // basically die and wait for WDT to reset me while (1); } } Serial.println("MQTT Connected!"); }
Programa 3 - para rodar no ESP8266: rec_esp.ino
/******************************************************************** MBZ MQTT example (ESP8266) ----------------------------- Este programa faz a subscrição do feed motor e quando recebe a mensagem, a envia para o Arduino *********************************************************************/ #include <ESP8266WiFi.h> #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #define SSID "redewifi" // id do roteador WIFI #define PWD "senha" // senha do roteador WIFI #define SERVER "io.adafruit.com" // servidor MQTT #define PORT 1883 // porta do servidor #define USR "AIO_USER" // usuário do IO Adafruit #define KEY "AIO_KEY" // chave de acesso do IO Adafruit WiFiClient client; Adafruit_MQTT_Client mqtt(&client, SERVER, PORT, USR, KEY); Adafruit_MQTT_Subscribe motor = Adafruit_MQTT_Subscribe(&mqtt, USR "/feeds/motor"); void setup() { Serial.begin(19200); // Conecta no WIFI Serial.println(); Serial.print("Conectando no WIFI: "); Serial.println(SSID); WiFi.begin(SSID, PWD); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.println("WiFi conectado"); Serial.print("IP: "); Serial.println(WiFi.localIP()); Serial.println(); // subscreve o feed motor mqtt.subscribe(&motor); } void loop() { // conecta no servidor MQTT_connect(); // verifica se há dados Adafruit_MQTT_Subscribe *subscription; while ((subscription = mqtt.readSubscription(5000))) { if (subscription == &motor) { Serial.print((char *)motor.lastread); } } delay(100); } void MQTT_connect() { int8_t ret; // Stop if already connected. if (mqtt.connected()) { return; } Serial.print("Connecting to MQTT... "); uint8_t retries = 3; while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected Serial.println(mqtt.connectErrorString(ret)); Serial.println("Retrying MQTT connection in 5 seconds..."); mqtt.disconnect(); delay(5000); // wait 5 seconds retries--; if (retries == 0) { // basically die and wait for WDT to reset me while (1); } } Serial.println("MQTT Connected!"); }
Programa 4 - para rodar no ATMega328P: rec_atmega.ino
/******************************************************************* MBZ MQTT example (ATMega328P) ----------------------------- Este programa recebe a mensagem do ESP8266 e comanda o motor ********************************************************************/ #include <Stepper.h> #include <Softwareserial.h> #define PIN_ENABLE_ESP8266 4 #define rxPin 2 #define txPin 3 SoftwareSerial EspSerial(rxPin, txPin); // RX, TX const int stepsPerRevolution = 512; Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11); void setup() { // seta a velocidade das portas seriais Serial.begin(19200); EspSerial.begin(19200); // inicializa o ESP8266 Serial.println("Inicializando o ESP8266"); pinMode(PIN_ENABLE_ESP8266, OUTPUT); digitalWrite(PIN_ENABLE_ESP8266, LOW); delay(200); digitalWrite(PIN_ENABLE_ESP8266, HIGH); delay(5000); } void loop() { String st = ""; st = getSerialData(5000); if (st != "") { Serial.print("Motor Cmd = "); Serial.println(st); processCmd(st); } } void processCmd(String cmd) { int p = 0; int ln = 0; int id = 0; int p0 = 0; int p1 = 0; int value = 0; String ch = ""; String buff = ""; // exemplo // I:3 S:30 R:10 S:50 L:30 P:500 S:60 R:180 cmd.trim(); cmd.toUpperCase(); ln = cmd.length(); while (p < ln) { ch = cmd.substring(p, p + 1); if ((ch == "I") || (ch == "S") || (ch == "R") || (ch == "L") || (ch == "P")) { p0 = cmd.indexOf(":", p) + 1; p1 = cmd.indexOf(" ", p0); if (p1 == -1) { p1 = ln; } buff = cmd.substring(p0, p1); value = buff.toInt(); p = p1; if (ch == "I") { id = value; } if (ch == "S") { // limita a velocidade: 10 - 60 if (value < 10) { value = 10; } if (value > 60) { value = 60; } myStepper.setSpeed(value); } if (ch == "R") { turnMotor(value, 1); } if (ch == "L") { turnMotor(value, 0); } if (ch == "P") { // limita a pausa: 1 - 2000 if (value < 1) { value = 1; } if (value > 2000) { value = 2000; } delay(value); } } p++; } } void turnMotor(long degrees, int direction) { long steps; // pega o valor absoluto degrees = abs(degrees); // converte de graus para passos steps = (2048 * degrees) / 360; // se a direcao for esquerda, deixa steps negativo if (direction == 0) { // girar para a esquerda steps = steps * -1; } myStepper.step(steps); } int getValue(String str, String tk) { String buff = ""; int p0, p1; p0 = str.indexOf(tk); if (p0 != -1) { p0 = str.indexOf(":", p0) + 1; p1 = str.indexOf(";", p0); buff = str.substring(p0, p1); } return buff.toInt(); } String getSerialData(const int timeout) { String strBuffer = ""; long int time = millis(); while ( (time + timeout) > millis()) { if (EspSerial.available() > 0) { strBuffer = EspSerial.readString(); break; } } return strBuffer; }
0 comentários :
Postar um comentário