Proyecto: añadir sensores a un robot submarino

Saludos a todos!

He estado trabajando por algun tiempo en añadir sensores a un robot submarino. Mi plan es conectar estos sensores a un arduino uno, que posteriormente conectare al arduino del robot via I2C como esclavo.
I had done this code to connect the data adquisition arduino to the computer by USB.
He realizado este codigo para conectar dicho arduino directamente al ordenador.

"CodigoSensoresOrdenador"

Como veis no es un codigo muy complicado, pero al ser la primera vez que trabajo con I2C (ya lo intente una vez pero al final me desespere y acabe utilizando un USB, pero no tengo esa posibilidad ahora), no se muy bien como hacerlo y me vendría bien toda ayuda o consejo que me podaís ofrecer. Investigando un poco he realizado este codigo, que seguramente este mal.

"CodigoSensoresEsclavoI2C"

Si alguién quiere ver como estan conectados puedo subir un diagrama diagrama de conexión rapidamente. De todas maneras lo explico un poco, cuatro de los sensores estan conectados al arduino por un serial port connector y el de temperatura esta conectado directamente a la placa arduino. y todo este arduino es el que iría conectado por I2C al arduino del robot, que a su vez va a una BeagleBone Black (una especie de Raspberry) que esta conectada al ordenador por un cable ethernet.

Muchas Gracias por todo!

Javier Garcia Martin.

P.D:Edito porque no habia adjuntado los códigos

CodigoSensoresEsclavoI2C.ino (5.32 KB)

CodigoSensoresOrdenador.ino (5.84 KB)

Porfa ayuda, que no hay forma de que me salga lo de conectarlo por I2C :blush: :sweat_smile:

Hola.
Tal vez deberías poner ese esquema que refieres, porque yo al menos no me termino de aclarar.
Si estos dos códigos se deben comunicar entre sí por i2c, entiendo que codeigosensoresordenador sería el master, ¿no?
Pero el caso es que ni has iniciado Wire en él (de hecho ni incluyes la librería) así que el otro ya puede esforzarse :P.
La verdad es que yo no he utilizado i2c, pero si miras en la referencia el ejemplo write y el ejemplo read, verás al menos cómo empezar con la parte de master.
Saludos.

Perdona, creo que me hice la picha un lio explicandome. Adjunto pongo un esquema de cual es mi idea para conectarlo al robot ("EverythingSensorPackageDiagram), la idea es conectar un arduino digamos de adquisición de datos, con el arduino que ya trae el robot, por I2C.
El arduino de adquisición de datos debe estar conectado por supuesto también a los sensores, y esta conexión se hace a traves de un serial port, para todos los sensores excepto para el de temperatura, que se hace directamente ("serial_port_connector_arduino_uno_1", "Wiringdiagram_ENV_TMP.pdf").
Para probar directamente el arduino de adquisición de datos, lo conecte via serie al ordenador y realice el código llamado CodigoSensoresOrdenador, mi problema es que al no haber usado nunca I2C, no se adaptar bien ese código para que las ordenes de tomar dato de "x" o pedir dato de "x" las mande por I2C en lugar de via serie.
Modificar el codigo del arduino del robot para que las pida, ya es otro problema posterior.
Un saludo!

Wiringdiagram_ENV_TMP.pdf (277 KB)

Bueno; entiendo que lo que quieres es sustituir la transmisión-recepción serial en codigosensoresesclavo por una i2c y en codigosensoresordenador aún no has implementado nada del master. Ya puedes poner dispositivos esclavos perfectamente programados, que como no haya un master que les dé la palabra, no van a decir ni pío.
Creo que podíamos empezar concretando más qué información deseas enviar del master al esclavo y viceversa, y centrarnos en un pequeño programa que muestre en el monitor del master lo que se recibe del esclavo. Una de las cuestiones a tener en cuenta es que el máximo de datos que se puede enviar en una trama i2c creo que es 32 bytes (aunque se puede modificar en la librería), así que lo ideal sería enviar los datos mínimos, con lo que además optimizamos tanto el tiempo de transmisión como la posterior extracción y conversión de los datos. No tiene sentido que algo que vamos a enviar para consumo del arduino se le envíe "humanizado" en plan cadenas tipo "temperatura1=27.56,temperatura2=27.30". Lo mismo se podría aplicar a las transmisiones serie entre dispositivos (otra cosa es una transmisión destinada al usuario, como el monitor serial).
De todas formas, si de momento sólo te interesa conseguir una transmisión i2c tal vez esto te ayude para empezar.
Ya irás contando.
Saludos.

CodigoSensoresOrdenador es un código que hice para ver si los sensores funcionaban. En dicho código no hay conexión I2C, solo vía serie al ordenador. Lo que hace es que cada vez que recibe una orden del ordenador, esta orden lleva asociada un canal (uno de los sensores) y un cmd. Así pués, en función de lo que se le dice desde el ordenador toma una medida u otra. El ordenador envia las ordenes en forma de bytes con readbytesuntil.

Ese código es el que quiero transformar para en lugar de recibir esos bytes que dan la orden desde el ordenador por puerto serie, se reciban por I2C desde otro arduino maestro. Esta claro que hasta que no programe el maestro me puedo comer los mocos, pero quiero creer que es más facil empezar adaptando este código. El arduino maestro lleva encima toda la programación del robot que no es poca, y para hacerlo master hay que hacer un analisis del código grande.

Bien. En el código del esclavo i2c debes, básicamente, construir las funciones callback para onReceive y onRequest

void receiveEvent(int computer_bytes_received)
{
      for (int i=0; i<computer_bytes_received; i++){
            computerdata[i]=wire.read();
      }
      // analizar los datos recibidos en computerdata, preparar una respuesta en sensordata
}


void requestEvent(void)
{
      wire.write(sensordata); // Si es una cadena, así valdría. si es una estructura sería wire.write(estructura, numbytes)     
}

La principal diferencia es que cuando recibes datos del maestro, no puedes contestar inmediatamente, sino que irás preparando una respuesta en un buffer a la espera de que el master solicite datos. En requestEvent enviaremos esos datos previamente preparados en ese buffer.

Vale, creo que lo voy entendiendo, puedes desarrollar un poco la idea del buffer.

Muchas gracias!

Con lo del buffer, me refiero a un espacio de memoria en el que irás preparando los datos a enviar. Puede ser una cadena, en cuyo caso la "construyes" cuando el master te envía sus mensaje, y será la cadena que permanecerá a la espera de ser enviada cuando el master te diga "habla".
Digamos que antes, cuando te llegaba la pregunta ibas contestando la respuesta; ahora sólo preparas la respuesta para cuando te den permiso para hablar.
Sería interesante saber en qué "dialecto" van a ir esas preguntas y respuestas y podría decirte códigos más concretos.

Y si no me importa demasiado perder datos, me vale con que el arduino envie al maestro el último valor del dato que tiene (el actual).
He desarrollado el siguiente código un poco con tus indicaciones, un poco con lo que he encontrado en reference. SIn embargo, no se si esta bien, compilarme no me compila porque da algún tipo de error raro en el onReceive de que no se puede hacer una conversion void a void o algo así.

DataAdquisitionCode.ino (6.31 KB)

Claro. Te da ese error, precisamente porque la función que asignes a onReceive debe ser void y recibir un parámetro int. Prueba el receiveEvent que te puse cuatro post arriba.

void receiveEvent(int computer_bytes_received)
{
      for (int i=0; i<computer_bytes_received; i++){
            computerdata[i]=wire.read();
      }
}

Y en cuanto a la función que asignas a requestEvent, prueba por este otro camino (escribiendo los datos previamente en un buffer de memoria), ya que creo que desde el esclavo se debe escribir, si no me equivoco, en un solo comando.

void requestEvent()
{
      char buffer_envio[20];
      sprintf(buffer_envio,"%s%f", sensordata, temp);
      wire.write(buffer_envio); // El envío desde esclavo i2c se debe hacer de una vez, creo
}

Vale lo pruebo. Pero no entiendo muy bien como funciona, y porque debe recibir un int :sweat_smile:

Bueno compilar compila, pero sigo sin entender bien tus modificaciones.
Me queda probarlo cuando consiga modificar el código del robot para ver si funciona. Lo que pasa es que el código del master va a tener tela, tengo que encontrar como insertarlo aquí https://github.com/OpenROV/openrov-software-arduino/tree/master/OpenROV

DataAdquisitionCodeV2.ino (5.95 KB)

Una de las características de C++ que para algunos será virtud y para otros defecto, es que es muy estricto en cuanto a los tipos de variable, lo que incluye las funciones. Si miras la referencia, verás que el parámetro que se pasa a wire.onRequest es una función de tipo void y sin parámetros. Si la función que le asignas no coincide, el compilador te dará error. De la misma forma, para wire.onReceive el parámetro es una función tipo void y que va a recibir un parámetro int, así que tu función debe ser así. Si lees más, verás que a través de ese parámetro vas a recibir la cantidad de bytes que se te envían (yo lo he llamado computer_bytes_received), así que lo mejor es leer ese parámetro para saber cuántos bytes leer (como ves lo he utilizado en el for).
El código del master no debería ser muy complejo, sabiendo qué hay que enviar y recibir.
En el vínculo que te pasé en otro post tienes un ejemplo de transmisión master, recepción master, recepción esclavo y transmisión esclavo (al recibir onrequest).

Muchas gracias, voy a mirarlo y te digo cuando vea algo.
El problema que tengo es que si ves el código del robot que te he puesto, es un .ino que corre todos los devices en el loop (los devices estan definidos en los .h y .cpp) y uno de estos es I2Cdevices, tengo que averiguar la forma de meter este arduino como un I2C device en el Robot :roll_eyes:

He estado echando un vistazo al código, pero me resulta bastante lioso intentar entenderlo por las bravas, por la cantidad de archivos. Supongo que habrá alguna documentación al respecto de cómo funciona todo. No obstante, intuyo que tienes que registrar un device que represente a tu arduino esclavo.
Lo primero que necesitas es saber cómo registrar tu device, olvidándote de la comunicación; qué comandos va a recibir y qué va a hacer con ellos y con los datos que se reciban del device. Luego, supongo que con incluir en tu device la cabecera i2c.h y utilizando sus comandos para comunicarte con el arduino en el loop y haciendo en el esclavo una programación que se entienda con lo que envías desde el master, lo deberías tener hecho.
¿Tienes en el robot ya algún otro dispositivo i2c funcionando? ¿Qué dispositivos va a llevar el arduino esclavo conectados?
Saludos.

Te explico:
Es imposible entender el codigo por las bravas, es demasiado largo como dices, aqui hay un esquema más menos de como furula:
https://camo.githubusercontent.com/114e1b46fbd5d869554646bdba6c9b3d0169407d/687474703a2f2f79756d6c2e6d652f3239316261666361
Basciamente el codigo "openrov.no" ejecuta en su loop un device_loop (es un método que esta en device.h y device.cpp, que es el otro código importante) y en los demás archivos lo que esta explicado es cada uno de los elementos. Si te fijas, estan los códigos:
https://github.com/OpenROV/openrov-software-arduino/blob/master/OpenROV/I2Cdev.cpp
https://github.com/OpenROV/openrov-software-arduino/blob/master/OpenROV/I2Cdev.h
que se suponen que tratan la comunicación I2C del robot.
Y hay un dispositivo, el sensor de profundidad, que esta conectado por I2C, si no me equivoco su código era este:
https://github.com/OpenROV/openrov-software-arduino/blob/master/OpenROV/MinIMU_I2C.cpp
https://github.com/OpenROV/openrov-software-arduino/blob/master/OpenROV/MinIMU_I2C.h
En el fichero https://github.com/OpenROV/openrov-software-arduino/blob/master/OpenROV/AConfig.h se debe especificar que devices estan activos.
Los dispositivos que va a llevar el arduino esclavo son una serie de sensores medioambientales desarrollados por http://atlas-scientific.com/ en concreto el ENV-SDS Kit.
Un saludo!

Bien. Entiendo que se trata de algo que no tiene que ver con nada de lo que trae implementado el robot, así que ¿Cómo se supone que van a funcionar maestro y esclavo respecto a los sensores?
Quiero decir ¿El maestro va a recibir datos de esos sensores? ¿Qué hará luego con ellos?

El maestro esta conectado a una BeagleBone Black (algo parecido a una raspberry) a la que manda datos cada cierto tiempo de refresco (un segundo creo), la beaglebone los manda a la superficie a traves de un cable (ethernet) y así llegan al ordenador, de forma que puedes ver casi en tiempo real estos datos, mi idea es agregar los datos obtenidos por los sensores a estos otros datos.
Si te fijas en https://github.com/OpenROV/openrov-software-arduino/blob/master/OpenROV/OpenROV.ino (código principal)
tras hacer el watchdog y el device_loop de cada device llama a los metodos outputshareddata() y outputnavdata() cada ciertos tiempos de refresco (son diferentes entre sí, es decir refresca unos datos más a menudo que otros, entiendo yo).

Bien.
Entonces deberíamos, creo yo, comenzar por modificar/ampliar la clase envdata en el archivo device.h para incluir todos los datos que vamos a medir. Actualmente contiene sólo temp y pres (supongo que temperatura y presión). El resto de datos se registraría igual:
static tipo nombredato. Por ejemplo, static float ACID.
En el archivo device.cpp deberemos, igualmente, inicializar esos nuevos datos. Para ello, agregaremos una línea similar a las que existen para las otras dos variables para cada nuevo dato incluido. Por ejemplo, float envdata::ACID=0.
El siguiente paso sería modificar (en device.cpp) la función OutputSharedData, para incluir la visualización de las nuevas variables. También nos podemos fijar en cómo se envían las anteriores. Por ejemplo:
Serial.print(F("acidez:"));
Serial.print(envdata::ACID);
Serial.print(';');
Con esto, deberían aparecer en el monitor los datos (de momento a cero) de los nuevos sensores.

Luego ya vendría la guinda: crear nuestro device para recibir datos del arduino esclavo. ¿Qué datos nos va a enviar? Creo que la forma más "económica" sería definir una estructura con esos datos y enviarla de un tirón. Si la única función del esclavo va a ser enviar esos datos, sólo vamos a necesitar de su método requestEvent, en el que enviaríamos esa estructura.