Conectar dos Arduino UNO. GPS, BT, IMU. con I2C

Hola,

Llevo un tiempo haciendo un drone (que está de moda) de tres rotores y he logrado hacerlo volar de manera decente con varios sensores y de forma muy sencilla.

  • GY-85: ADXL345, ITG3205, HMC5883L -> I2C, Pines A4 y A5
  • Receptor RX de 6 canales (PPM) -> Pin 2
  • ESC + Motores sin escobillas (PWM) -> Pines 3, 5 y 6

Bien, hasta aquí no he tenido demasiados problemas.
Ahora lo que quiero es ampliar su funcionamiento añadiéndole un GPS y un módulo BT para poder enviarle a través del móvil varios comandos para hacer diferentes tareas, es decir, básicamente convertir el BT en la consola de comandos y de debug (obviemos el hecho de que el rango del BT es muy pequeño)

Receptor GPS: UBlox 6M. Se conecta a través de puerto serie. Configurado a 38400 baudios y una frecuencia de 5hz (200ms)
Módulo BT: HC-05. Se conecta a través´ de puerto serie. Lo puse a 57600.

Intenté conectar ambos usando la librería SoftwareSerial que en principio parecía muy prometedora, pero tuve problemas y esq, por lo visto, la librería no puede trabajar con esos baudrate y la información del GPS llega cortada y/o nisiquiera recibe los comandos que envío a través del BT.
Podéis echar un vistazo de como llegamos a esta conclusión en este post: GPS (uBlox) a través de BLUETOOTH HC-05 - Software - Arduino Forum

GPS pines 8 y 9 (en verdad 8 ya que no necesito enviar datos)
BT pines 10 y 11 (aquí sí que necesito ambos pines)

He probado con AltSoftSerial, la verdad es que ha mejorado mucho la recepción del GPS pero aún así no me vale para el BT.

Aquí viene una idea que he tenido q lo mismo no se puede hacer, y es donde vienen mis dudas.

Pretendo conectar dos Arduinos. Uno de ellos tendría el GY-85 y el GPS (es decir, una IMU como tal) que enviaría la información cada X ms. Esta información sería el YPR y la información del GPS que me pueda interesar (LONG, LAT, TIME, etc).

Con la IMU no tendría problemas ya que el GY85 iría por I2C (SCL/SDA) y podría usar la AltSoftSerial para la conexión del GPS.

Esta información la encapsularía y la enviaría a través del puerto serie con un Serial.write o Serial.print según me interesase.

Bien, entiendo que ambos arduinos los tendría que conectar por el puerto serie ¿no? No existe otra manera de mandarse información entre ellos ¿no?.

Si fuese así, entiendo que en principio perdería el poder programarlos como he hecho hasta ahora, por el puerto serie. ¿Qué otras formas hay de cargar un sketch al arduino?

En el segundo arduino (el que NO es el IMU), entiendo que el "Serial.read()" me daría la info del IMU ¿correcto? Por lo que a través de la libería de SoftwareSerial o AltSoftSerial conectaría el BT y podría ver la información que envía y enviarle comandos a través del BT ¿es correcto?

He llegado a este punto porque creo que NO es posible conectar "todo a la vez" en un único Arduino.

Sé que existe el Arduino Mega que lleva varios puertos serie y sería mejor y mucho más simple para esto, pero tengo sobrepoblación de Arduinos Unos y me gustaría exprimir esta opción :stuck_out_tongue:

Os dejo el código del sketch principal, el resto son librerías.

#include <Arduino.h>
#include <Wire\Wire.h>
#include <ServoTimer2\ServoTimer2.h>
#include <AltSoftSerial\AltSoftSerial.h>
//#include <SoftwareSerial\SoftwareSerial.h>
#include <TinyGPS\TinyGPS.h>
#include "ADXL345.h"
#include "ITG3205.h"
#include "HMC5883L.h"
#include "AHRS.h"
#include "ESC.h"

#define ANGLE(x,y) (degrees(atan2(x,y))
#define RC_PIN 2
#define RC_CHANNELS 6

int rcData[6] = { 1500, 1500, 1500, 1500, 1500, 1500 }; // interval [1000;2000]
bool failSafe = false;

void readSerial();
void processCommand(char* command);
void readPPM();
void getSensorData();
void getYawPitchRoll();
void getComplementaryFilter();
void getAHRS();
void printYPR();

ADXL345 accel;
ITG3205 gyro;
HMC5883L magn;
AHRS ahrs;
ESC motors;

AltSoftSerial gps;
TinyGPS gpsParser;
bool newGPSData = false;
unsigned long gpsChars;
unsigned short gpsSentences, gpsFailed;
long gpsLastRead = 0;

//FAILSAFE COUNTER
long lastFailSafe = 0;
long failSafeTh = 900;

//SERIAL PRINTDATA
float ypr[3] = { 0.0f, 0.0f, 0.0f };
long samplePeriod = 20;
long lastSamplePeriod = 0;
long lastSampleTime = 0;

long lastSamplePrint = 0;

// Complimentary filter
float ALPHA = 0.2;
float GYRO_GAIN = 1;
float ACCEL_GAIN = 1;
int step = 0;

//Commands
char command[100];
char commandId[10];
byte commandIndex;


int now, diff;

int main()
{
 init();

 Wire.begin();

 Serial.begin(38400);
 Serial.println("Ready");

 gps.begin(38400);

 accel.initAccel();
 accel.calibration();
 accel.printOffsets();

 gyro.init();
 gyro.calibration();
 gyro.printOffsets();

 magn.init();
 //magn.calibration();
 //magn.printOffsets();
 
 //RC
 pinMode(RC_PIN, INPUT);
 attachInterrupt(0, readPPM, RISING);

 ////ESC
 motors.initESC();
 motors.setupESC();

 

 for (;;)
 {
 lastSampleTime = micros();

 //YPR
 getYawPitchRoll();

 //COMMANDS
 readSerial();

 //MOTOS
 motors.setMotorSpeed(1, rcData[0]);
 motors.setMotorSpeed(2, rcData[1]);
 motors.setMotorSpeed(3, rcData[2]);

 ////GPS
 //if (gps.available()) 
 //{
 // char chr = gps.read();
 // gpsParser.encode(chr);
 //}


 //printYPR();
 }

 return 0;
}

void readPPM()
{
 
 static int last = 0;
 static int chann = 0;

 now = micros();

 sei(); //Enable Interrupts

 diff = now - last;
 last = now;

 if (diff > 3000)
 {
 chann = 0;
 }
 else if (900 < diff && diff < 2200 && chann < RC_CHANNELS)
 {
 //Only if the signal is between these values it is valid, otherwise the failsafe counter should move up
 rcData[chann] = diff;
 chann++;

 failSafe = false;
 }
 else if (diff < failSafeTh)
 {
 //TODO: Si los ultimos 10 son < failSafeTh, failSafe = true
 failSafe = true;
 lastFailSafe = now;
 }
 
}

void readSerial()
{
 char charRecv;
 while (Serial.available())
 {
 charRecv = Serial.read();

 if (charRecv == '\n')
 {
 // Termino el string
 command[commandIndex] = '\0';
 processCommand(command);

 //Inicializo el string
 commandIndex = 0;
 command[0] = '\0';
 }
 else
 {
 command[commandIndex] = charRecv;
 commandIndex++;
 }
 }
}

void getYawPitchRoll()
{
 //YPR
 lastSamplePeriod = micros() - lastSampleTime;
 

 getSensorData();

 getComplementaryFilter();
 

 lastSampleTime = micros();
}

void getSensorData()
{
 switch (step)
 {
 case 0:
 //ACCEL
 accel.getAngleData();
 step++;
 break;
 case 1:
 //GYRO
 gyro.getDSData();
 step++;
 break;
 case 2:
 //MAGN
 magn.getClearRawData();
 step = 0;
 break;
 default:
 break;
 }
}

void getComplementaryFilter()
{ 
 //if (lastSamplePeriod > samplePeriod * 1000)
 //{
 ypr[0] = ALPHA * (ypr[0] + (gyro.dsData[0] * GYRO_GAIN) * ((float)(micros() - lastSampleTime) / 1000000)) + (1.0f - ALPHA) * (accel.angleData[0] * ACCEL_GAIN);
 ypr[1] = ALPHA * (ypr[1] + (gyro.dsData[1] * GYRO_GAIN) * ((float)(micros() - lastSampleTime) / 1000000)) + (1.0f - ALPHA) * (accel.angleData[1] * ACCEL_GAIN);
 //}
}

void printYPR()
{
 long now = micros();

 if (now - lastSamplePrint > 1000000)
 {
 lastSamplePrint = now;

 //float flat, flon;
 //unsigned long age;

 //gpsParser.f_get_position(&flat, &flon, &age);
 //gpsParser.stats(&gpsChars, &gpsSentences, &gpsFailed);

 Serial.print((int)ypr[0]);
 Serial.print("\t");
 Serial.print((int)ypr[1]);
 Serial.print("\t");
 Serial.print((int)ypr[2]);
 Serial.print("\t");


 //Serial.print("CHARS=");
 //Serial.print(gpsChars);
 //Serial.print("\t");

 //Serial.print("SENTENCES=");
 //Serial.print(gpsSentences);
 //Serial.print("\t");

 //Serial.print("CSUM ERR=");
 //Serial.print(gpsFailed);
 //Serial.print("\t");

 //Serial.print("LAT=");
 //Serial.print(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flat, 6);
 //Serial.print(" LON=");
 //Serial.print(flon == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flon, 6);
 //Serial.print(" SAT=");
 //Serial.print(gpsParser.satellites() == TinyGPS::GPS_INVALID_SATELLITES ? 0 : gpsParser.satellites());
 //Serial.print(" PREC=");
 //Serial.print(gpsParser.hdop() == TinyGPS::GPS_INVALID_HDOP ? 0 : gpsParser.hdop());

 Serial.print(diff);
 Serial.print("\t");

 Serial.print(lastSamplePeriod);
 Serial.print("\t");

 Serial.println("");
 }
}


}

Puedes conectarlos por i2C A4 y A5 son los pines dedicados a eso.
una conexion master/slave.
Tambien puedes usar softwareSerial es una librería que te agrega un puerto serie en un par de pines que tu eliges.

Hola,

El I2C no puedo usarlo ya que es usado por el GY-85.

Estoy mirando de usar el SPI, que lo tengo libre y por lo visto se puede usar para enviar información.

En cuanto a lo del SoftwareSerial, releete el post que puse y mira el enlace en el que también me contestaste :stuck_out_tongue:

Gracias.

Si puedes usarlo quien te dijo que no puedes usarlo. I2C no esta pensado para un solo dispositivo.
Solo le dices al segundo arduino que dirección tendrá como esclavo y responderá cuando sea consultado.
Asi que no descartes esa idea.
Por otro lado ahora reparo un error, aunque creo que debes probarlo.
SoftwareSerial sea el que sea, no debes usarlo a semejante velocidad. Prueba con 9600 y no subas demasiado.

Que enlace.. no veo ningun enlace. Si no hay tags para mi no existen. Sino lee las normas sobre como usar tags.

Si no hay tag no existe? Vaya, pues no sabes la de cosas que te pierdes! :slight_smile:

Intenté conectar ambos usando la librería SoftwareSerial que en principio parecía muy prometedora, pero tuve problemas y esq, por lo visto, la librería no puede trabajar con esos baudrate y la información del GPS llega cortada y/o nisiquiera recibe los comandos que envío a través del BT.
Podéis echar un vistazo de como llegamos a esta conclusión en este post: GPS (uBlox) a través de BLUETOOTH HC-05 - Software - Arduino Forum

Seguro que te resulta familiar y el pq no puedo usar 9600 :slight_smile:

Mirando lo del I2C entre Arduinos...

Si. En que quedó eso como conclusión del SoftwareSerial.
Anduvo con alguna velocidad o no?
Consideraste usar un MEGA?

EDITADO: Digo, vas a usar dos Arduinos por no usar un MEGA, te parece lógico?
Dos UNOs o dos NANOs (aca no se si da la cuenta) son mas caros que un MEGA que tiene 4 serial.
Vale la pena complicarse?

Saludos
Estoy jugando con el ESP8266, con respecto al alcance dice que hasta 400 m, no se cuanto en realidad puede darte de autonomía con tu proyecto. Pero también te puede funcionar para cargarle el firmware algo como esto.

No lo he probado pero espero que te sirva de inicio.

Hola,

surbyte únicamente con un dispositivo sí que va, sino empieza a dar errores. En la documentación del AltSoftSerial pone que puedes usar diferentes baudrates pero que aún así no va muy fino...

En cuanto a lo de Mega, ya se que puedo usar un Mega, pero estoy más bien experimentando.

He tirado por la opción de la comunicación I2C como me dijiste. Lo que he hecho ha sido montar un Arduino Pro Mini con el GY-85 y el GPS usando el AltSoftSerial. Es muy pequeñito. Y otro que será el Master.

El Master hará una petición al Slave(2) y éste le devolverá los datos del IMU y el GPS.

Una vez tenía todo montado y programado he visto que tenía lagunas en la recepción, pongo un ejemplo de un sketch sencillo que hice.

Slave:

Wire.begin(0x02);
Wire.onRequest(requestData);

void requestData()
{
 digitalWrite(13, HIGH);
 delay(100);
 digitalWrite(13, LOW);

 static bool change = false;

 if (change)
 {
 Wire.write("12345", 5);
 }
 else
 {
 Wire.write("54321", 5);
 }
 change = !change; 
}

Maestro:

#include <Wire.h>  

void setup () 
{
  Serial.begin(115200); 
  Wire.begin();   
} 


void loop()
{
  if(getData(0x02) == 0)
  {
    Serial.println("\tOk");
  }
  else
  {
    Serial.println("\tWrong!");
  }
}  

int getData(int sensorID)
{
  int i = 0;

  Wire.requestFrom(sensorID, 5);
 
  while(Wire.available())
  {
    char c = Wire.read();
    Serial.print(c);
    i++;
  }

  if(i == 5) // he leido 5, salgo con Ok
  {
    return 0;
  }
  return 1;
}

El output es:

12345 Ok
 ÿÿÿÿ Ok
 ÿÿÿÿ Ok
54321 Ok
12345 Ok
 ÿÿÿÿ Ok
12345 Ok
 ÿÿÿÿ Ok
12345 Ok
54321 Ok
12345 Ok
 ÿÿÿÿ Ok
12345 Ok

Alguna idea que son esos datos raros que dice ÿÿÿÿ? Incluso me lo devuelve como Ok...

PD: max_saeta he probado el ESP8266, prefiero el BT ya que es solo para enviar comandos pre-vuelo y tengo el móvil a mano con el BT es más sencillo que montarme un HotSpot y conectarlo... No busco "alcance" en este caso.

Gracias.

Edición: Tanto SCL como SDA los tengo conectados con una resistencia de 4K7 a los 3.3V del Arduino tal y como he visto por los foros.

Edición: He probado a mostrar la info en DEC para ver el ascii (Serial.print(c, DEC):wink:

0-1-1-1-1	Ok
4950515253	Ok
5352515049	Ok
4950515253	Ok
5352515049	Ok
4950515253	Ok
5352515049	Ok
4950515253	Ok
0-1-1-1-1	Ok
4950515253	Ok
5352515049	Ok
4950515253	Ok
5352515049	Ok
4950515253	Ok
0-1-1-1-1	Ok
4950515253	Ok
5352515049	Ok
4950515253	Ok
5352515049	Ok
4950515253	Ok
0-1-1-1-1	Ok

Como curiosidad, parece que va en bloques de 5... aunque cuando no es en DEC no sigue ningún patrón...

Editado: No sigue patrón, fue pura chorra.