Projeto: Controlando robô com módulo Zigbee e acelerômetro

Este vídeo mostra um projeto controlando um robô com módulos Zigbee e acelerômetro. Neste projeto foi utilizado os seguintes componentes:

  • 2x Arduino
  • 2x Módulos Zigbee
  • 1x acelerômetro MMA8452
  • 1x kit de robótica com 2 servos 360º

Para conectar o acelerômetro MMA8452 veja o seguinte tutorial clicando aqui! Utilizando o mesmo exemplo utilizado no tutorial. Abra-o e passe a seguinte programação:

/* MMA8452Q Example Code
by: Jim Lindblom
SparkFun Electronics
date: November 17, 2011
license: Beerware - Use this code however you'd like. If you
find it useful you can buy me a beer some time.

This code should provide example usage for most features of
the MMA8452Q 3-axis, I2C accelerometer. In the loop function
the accelerometer interrupt outputs will be polled, and either
the x/y/z accel data will be output, or single/double-taps,
portrait/landscape changes will be announced to the serial port.
Feel free to comment/uncomment out some of the Serial.print
lines so you can see the information you're most intereseted in.

The skeleton is here, feel free to cut/paste what code you need.
Play around with the settings in initMMA8452Q. Try running the
code without printing the accel values, to really appreciate
the single/double-tap and portrait landscape functions. The
P/L stuff is really neat, something not many accelerometers have.

Hardware setup:
MMA8452 Breakout ------------ Arduino
3.3V --------------------- 3.3V
SDA ----------------------- A4
SCL ----------------------- A5
INT2 ---------------------- D3
INT1 ---------------------- D2
GND ---------------------- GND

SDA and SCL should have external pull-up resistors (to 3.3V).
10k resistors worked for me. They should be on the breakout
board.

Note: The MMA8452 is an I2C sensor, however this code does
not make use of the Arduino Wire library. Because the sensor
is not 5V tolerant, we can't use the internal pull-ups used
by the Wire library. Instead use the included i2c.h, defs.h and types.h files.
*/
#include "i2c.h" // not the wire library, can't use pull-ups

#include <SoftwareSerial.h>
// the SparkFun breakout board defaults to 1, set to 0 if SA0 jumper on the bottom of the board is set
#define SA0 1
#if SA0
#define MMA8452_ADDRESS 0x1D // SA0 is high, 0x1C if low
#else
#define MMA8452_ADDRESS 0x1C
#endif

/* Set the scale below either 2, 4 or 8*/
const byte SCALE = 2; // Sets full-scale range to +/-2, 4, or 8g. Used to calc real g values.
/* Set the output data rate below. Value should be between 0 and 7*/
const byte dataRate = 0; // 0=800Hz, 1=400, 2=200, 3=100, 4=50, 5=12.5, 6=6.25, 7=1.56

/* Pin definitions */
int int1Pin = 2; // These can be changed, 2 and 3 are the Arduinos ext int pins
int int2Pin = 3;
float number=0;
float number1=0;
int accelCount[3]; // Stores the 12-bit signed value
float accelG[3]; // Stores the real accel value in g's
SoftwareSerial ubee(4,5);

void setup()
{
byte c;

Serial.begin(19200);
ubee.begin(19200);
/* Set up the interrupt pins, they're set as active high, push-pull */
pinMode(int1Pin, INPUT);
digitalWrite(int1Pin, LOW);
pinMode(int2Pin, INPUT);
digitalWrite(int2Pin, LOW);

/* Read the WHO_AM_I register, this is a good test of communication */
c = readRegister(0x0D); // Read WHO_AM_I register
if (c == 0x2A) // WHO_AM_I should always be 0x2A
{
initMMA8452(SCALE, dataRate); // init the accelerometer if communication is OK
Serial.println("MMA8452Q is online...");
}
else
{
Serial.print("Could not connect to MMA8452Q: 0x");
Serial.println(c, HEX);
while (1) // Loop forever if communication doesn't happen
;
}
}

void loop()
{
static byte source;

/* If int1 goes high, all data registers have new data */
if (digitalRead(int1Pin)==1) // Interrupt pin, should probably attach to interrupt function
{
readAccelData(accelCount); // Read the x/y/z adc values

/* Below we'll print out the ADC values for acceleration
for (int i=0; i<3; i++)
{
Serial.print(accelCount[i]);
Serial.print("\t\t");
}
Serial.println();*/

/* Now we'll calculate the accleration value into actual g's */
for (int i=0; i<3; i++)
accelG[i] = (float) accelCount[i]/((112)/(2*SCALE)); // get actual g value, this depends on scale being set
/* print out values */
for (int i=0; i<3; i++)
{
Serial.print(accelG[i], 4); // Print g values

Serial.print("\t\t"); // tabs in between axes
}
if((accelG[0]>0.3) && ((accelG[1]<0.5) && (accelG[1]>-0.5))) //Frente
{
ubee.print('F');
Serial.println("Forward");

}
else if((accelG[0]<-0.3) && ((accelG[1]<0.5) && (accelG[1]>-0.5)) ) //Tras
{
ubee.print('B');
Serial.println("Back");

}
else if((accelG[1]<-0.3) && ((accelG[0]<0.5) && (accelG[0]>-0.5)))
{
ubee.print('R');
Serial.println("Right");
}
else if((accelG[1]>0.3) && ((accelG[0]<0.5) && (accelG[0]>-0.5)))
{
ubee.print('L');
Serial.println("Left");
}
else
{
ubee.print('S');
Serial.println("Stopped");
}
Serial.println();
}

/* If int2 goes high, either p/l has changed or there's been a single/double tap */

delay(1000); // Delay here for visibility
}

void readAccelData(int * destination)
{
byte rawData[6]; // x/y/z accel register data stored here

readRegisters(0x01, 6, &rawData[0]); // Read the six raw data registers into data array

/* loop to calculate 12-bit ADC and g value for each axis */
for (int i=0; i<6; i+=2)
{
destination[i/2] = ((rawData[i] 8) | rawData[i+1]) >> 4; // Turn the MSB and LSB into a 12-bit value
if (rawData[i] > 0x7F)
{ // If the number is negative, we have to make it so manually (no 12-bit data type)
destination[i/2] = ~destination[i/2] + 1;
destination[i/2] *= -1; // Transform into negative 2's complement #
}
}
}


/* This function will read the p/l source register and
print what direction the sensor is now facing */
void portraitLandscapeHandler()
{
byte pl = readRegister(0x10); // Reads the PL_STATUS register
switch((pl&0x06)>>1) // Check on the LAPO[1:0] bits
{
case 0:
Serial.print("Portrait up, ");
break;
case 1:
Serial.print("Portrait Down, ");
break;
case 2:
Serial.print("Landscape Right, ");
break;
case 3:
Serial.print("Landscape Left, ");
break;
}
if (pl&0x01) // Check the BAFRO bit
Serial.print("Back");
else
Serial.print("Front");
if (pl&0x40) // Check the LO bit
Serial.print(", Z-tilt!");
Serial.println();
}

/* Initialize the MMA8452 registers
See the many application notes for more info on setting
all of these registers:
http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MMA8...

Feel free to modify any values, these are settings that work well for me.
*/
void initMMA8452(byte fsr, byte dataRate)
{
MMA8452Standby(); // Must be in standby to change registers

/* Set up the full scale range to 2, 4, or 8g. */
if ((fsr==2)||(fsr==4)||(fsr==8))
writeRegister(0x0E, fsr >> 2);
else
writeRegister(0x0E, 0);

/* Setup the 3 data rate bits, from 0 to 7 */
writeRegister(0x2A, readRegister(0x2A) & ~(0x38));
if (dataRate <= 7)
writeRegister(0x2A, readRegister(0x2A) | (dataRate 3));

/* Set up portrait/landscap registers - 4 steps:
1. Enable P/L
2. Set the back/front angle trigger points (z-lock)
3. Set the threshold/hysteresis angle
4. Set the debouce rate
// For more info check out this app note: http://cache.freescale.com/files/sensors/doc/app_note/AN4068.pdf */
writeRegister(0x11, 0x40); // 1. Enable P/L
writeRegister(0x13, 0x44); // 2. 29deg z-lock (don't think this register is actually writable)
writeRegister(0x14, 0x84); // 3. 45deg thresh, 14deg hyst (don't think this register is writable either)
writeRegister(0x12, 0x50); // 4. debounce counter at 100ms (at 800 hz)

/* Set up single and double tap - 5 steps:
1. Set up single and/or double tap detection on each axis individually.
2. Set the threshold - minimum required acceleration to cause a tap.
3. Set the time limit - the maximum time that a tap can be above the threshold
4. Set the pulse latency - the minimum required time between one pulse and the next
5. Set the second pulse window - maximum allowed time between end of latency and start of second pulse
for more info check out this app note: http://cache.freescale.com/files/sensors/doc/app_note/AN4072.pdf */
writeRegister(0x21, 0x7F); // 1. enable single/double taps on all axes
// writeRegister(0x21, 0x55); // 1. single taps only on all axes
// writeRegister(0x21, 0x6A); // 1. double taps only on all axes
writeRegister(0x23, 0x20); // 2. x thresh at 2g, multiply the value by 0.0625g/LSB to get the threshold
writeRegister(0x24, 0x20); // 2. y thresh at 2g, multiply the value by 0.0625g/LSB to get the threshold
writeRegister(0x25, 0x08); // 2. z thresh at .5g, multiply the value by 0.0625g/LSB to get the threshold
writeRegister(0x26, 0x30); // 3. 30ms time limit at 800Hz odr, this is very dependent on data rate, see the app note
writeRegister(0x27, 0xA0); // 4. 200ms (at 800Hz odr) between taps min, this also depends on the data rate
writeRegister(0x28, 0xFF); // 5. 318ms (max value) between taps max

/* Set up interrupt 1 and 2 */
writeRegister(0x2C, 0x02); // Active high, push-pull interrupts
writeRegister(0x2D, 0x19); // DRDY, P/L and tap ints enabled
writeRegister(0x2E, 0x01); // DRDY on INT1, P/L and taps on INT2

MMA8452Active(); // Set to active to start reading
}

/* Sets the MMA8452 to standby mode.
It must be in standby to change most register settings */
void MMA8452Standby()
{
byte c = readRegister(0x2A);
writeRegister(0x2A, c & ~(0x01));
}

/* Sets the MMA8452 to active mode.
Needs to be in this mode to output data */
void MMA8452Active()
{
byte c = readRegister(0x2A);
writeRegister(0x2A, c | 0x01);
}

/* Read i registers sequentially, starting at address
into the dest byte arra */
void readRegisters(byte address, int i, byte * dest)
{
i2cSendStart();
i2cWaitForComplete();

i2cSendByte((MMA8452_ADDRESS1)); // write 0xB4
i2cWaitForComplete();

i2cSendByte(address); // write register address
i2cWaitForComplete();

i2cSendStart();
i2cSendByte((MMA8452_ADDRESS1)|0x01); // write 0xB5
i2cWaitForComplete();
for (int j=0; j<i; j++)
{
i2cReceiveByte(TRUE);
i2cWaitForComplete();
dest[j] = i2cGetReceivedByte(); // Get MSB result
}
i2cWaitForComplete();
i2cSendStop();

cbi(TWCR, TWEN); // Disable TWI
sbi(TWCR, TWEN); // Enable TWI
}

/* read a single byte from address and return it as a byte */
byte readRegister(uint8_t address)
{
byte data;

i2cSendStart();
i2cWaitForComplete();

i2cSendByte((MMA8452_ADDRESS1)); // write 0xB4
i2cWaitForComplete();

i2cSendByte(address); // write register address
i2cWaitForComplete();

i2cSendStart();

i2cSendByte((MMA8452_ADDRESS1)|0x01); // write 0xB5
i2cWaitForComplete();
i2cReceiveByte(TRUE);
i2cWaitForComplete();

data = i2cGetReceivedByte(); // Get MSB result
i2cWaitForComplete();
i2cSendStop();

cbi(TWCR, TWEN); // Disable TWI
sbi(TWCR, TWEN); // Enable TWI

return data;
}

/* Writes a single byte (data) into address */
void writeRegister(unsigned char address, unsigned char data)
{
i2cSendStart();
i2cWaitForComplete();

i2cSendByte((MMA8452_ADDRESS1));// write 0xB4
i2cWaitForComplete();

i2cSendByte(address); // write register address
i2cWaitForComplete();

i2cSendByte(data);
i2cWaitForComplete();

i2cSendStop();
}

A programação destacada em vermelho foi implementada para utilizar o módulo Zigbee e assim poder mandar os comandos para o robô. Caso um dos eixos x ou y for maior que um threshold, o Arduino com acelerômetro irá mandar o respectivo comando para o Arduino do robô pelo módulo Zigbee.

A programação abaixo, mostra a programação implementada para controlar os servos do robô:

#include <Servo.h>
#include <SoftwareSerial.h>

Servo servoleft,servoright;
SoftwareSerial ubee(4,5);

char c;

void setup()
{
ubee.begin(19200);
Serial.begin(19200);
servoleft.attach(6);
servoright.attach(7);
Serial.print("Start");
}

void loop()
{
if(ubee.available()>0)
{
c=ubee.read();
//delay(1);
if(c=='F')
{
servoright.write(0);
servoleft.write(180);
Serial.println("Forward");
}
else if(c=='B')
{
servoright.write(180);
servoleft.write(0);
Serial.println("Back");
}
else if(c=='R')
{
servoright.write(100);
servoleft.write(100);
Serial.println("Right");
}
else if(c=='L')
{
servoright.write(80);
servoleft.write(80);
Serial.println("Left");
}
else if(c=='S')
{
servoright.write(90);
servoleft.write(90);
Serial.println("Stopped");
}
}

}

O Arduino do robô recebe os comandos dados pelo módulo Zigbee e move os servos de acordo com o dado recebido.

Para ver como utilizar o servo motor com Arduino, clique aqui!

E é isso! Esperamos que tenha gostado! Caso tenha dúvidas sobre o projeto, poste aqui neste blog! Para sugestões de tutoriais, clique aqui! Para ver outros tutoriais e projetos desenvolvidos pela equipe LdG e por outros garagistas, clique aqui e aqui, respectivamente! Até a próxima!

Referências:

http://labdegaragem.com/profiles/blogs/tutorial-como-utilizar-o-ace...

http://labdegaragem.com/profiles/blogs/tutorial-arduino-com-servomotor

http://www.labdegaragem.org/loja/index.php/29-arduino.html

http://www.labdegaragem.org/loja/index.php/36-motores-afins/servo-m...

http://www.labdegaragem.org/loja/index.php/34-sensores/triple-axis-...

Exibições: 10646

Comentar

Você precisa ser um membro de Laboratorio de Garagem (arduino, eletrônica, robotica, hacking) para adicionar comentários!

Entrar em Laboratorio de Garagem (arduino, eletrônica, robotica, hacking)

Destaques

Registre-se no
Lab de Garagem
Clicando aqui

Convide um
amigo para fazer
parte

curso gratis de arduino

© 2018   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço