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