El objetivo de este tutorial es aprender a controlar servos analógicos RC –como los usados en modelismo- mediante señales PWM de Pi4j en un Raspberry PI.

Como conocimiento colateral adquiriremos la noción del protocolo de comunicación interfaz I2C con un Servo Driver – PCA9685 de Adafruit.

Partimos del tutorial anterior de PI4J
https://unpocodejava.wordpress.com/2013/08/08/pi4j-control-del-gpio-de-raspberry-pi-con-java/

y el tutorial de control de motores
https://unpocodejava.wordpress.com/2013/08/15/control-de-motores-con-java-pi4j-en-raspberry-pi/

 

Pulsos PWM en servos RC


Ya vimos en el tutorial de control de motores lo que es la modulación de amplitud de pulsos. Podemos ver como ejemplo en el diagrama de abajo muestra señales típicas de un servo RC.

El control de los pulsos RC debe ser mucho más preciso que el de un motor DC. En tan solo 1ms de margen se define todo el rango de señales y esto plantea una problemática en Java y Pi4J: El control mediante el jvm –debido al indeterminismo en la prioridad de ejecución de los hilos- no es lo suficientemente preciso para modular pulsos de 1ms.

Este cuadro podría ser interesante para quien haya intentado controlar un servo desde el Pi:

  • La distribución occidentalis posibilita pulsos PWM de kernel a través del pin 18. Aparte de vernos forzados a una distribución, solo podríamos controlar un servo.
  • El control de servos mediante Java se ve alterado por la prioridad de hilos y gc en Pi4J. Los movimientos resultan temblorosos, erráticos y con errores impredecibles.
  • El autor de wiringpi también comenta que el control mediante sus librerías se puede ver alterado por procesos en segundo plano del sistema.

 

Control Mediante Servo Driver


La mejor solución para controlar servos es mediante hardware, un Servo Driver. Entre diversas alternativas optamos por el I2C interface – PCA9685 de Adafruit http://www.adafruit.com/products/815

  • Controlado por el protocolo I2C; múltiples dispositivos ocupan solo 2 pines del raspberry pi.
  • Incluye reloj interno como generador de onda, mucho más preciso que el generado por software.
  • Entrada de 5V (chip alimentado por 3V).
  • El circuito otorga el control de 16 dispositivos
  • Posibilidad de encadenar en serie drivers adicionales (¡el potencial de control según la web de adafruit seria de 992 servos con solo 2 pines!)

Ahora el objetivo está claro. Generaremos señales software al bus I2C para que el servo responda a través del controlador.

 

Ingredientes


Vamos a hacer un ejemplo sencillo. Para hacer la prueba, aparte del pi y el servo driver PCA9685 de adafruit, necesitamos:

  • Faja GPIO
  • Placa de Pruebas
  • Porta baterías 6V (encontré uno como éste en la parte de modelismo de la juguetería poli)
  • Cables
  • Micro-Servo RC 5V (menos de 10 euros, en cualquier tienda de modelismo)

 

Configuración


Montamos el equipo de esta manera:

Con las siguientes peculiaridades:

  • El pin GND del Servo Driver (y del GPIO) va a tierra.
  • Atar pines SCL y SCA del GPIO a los respectivos pines SCL y SCA del Servo Driver.
  • El pin VCC del Servo Driver alimenta el chip PCA9685 con 3 Voltios. Aprovechamos para la salida 3.3 de GPIO para ello.
  • La entrada de alimentación del Servo Driver(V+/GND) recibe la corriente del portabaterias.

Los servos necesitan 5V. Podríamos tener la idea de usar el pin 5V del Pi para alimentar los servos o el driver –yo si la tuve-. No lo hagáis. Los picos de potencia harán que el Pi se reinicie o se quede en estado inconsistente.

El servo 0 se conectará a canal 0 del Servo Driver, el primero empezando por la izquierda.
El servo 1 se conectaría a canal 1 del Servo Driver, el segundo empezando por la izquierda.

 

Configurar el Pi para I2C


Como usamos Raspbian, necesitamos editar el fichero:
>sudo nano /etc/modules

y añadir estas dos líneas:
i2c-bcm2708
i2c-dev

después reiniciamos
>sudo reboot

Si Raspbian da problemas, probar a hacer update a la última versión. En este tutorial he usado en la versión del 2013-07-26.

El bus I2C permite encadenar en serie distintos dispositivos. Para conocer las direcciones “address” de cada uno podemos instalar la utilidad i2c-tools:
> apt-get install python-smbus
> sudo apt-get install i2c-tools

Una vez instalada, comprobamos las direcciones de los dispositivos instalados. Ejecutamos
>sudo i2cdetect -y 1
(probar con un argumento 0 si el Pi es anterior a Octubre de 2012)

Se debería mostrar la dirección del dispositivo, 0x40 (binario 1000000), de esta manera:

Dependiendo de la distribución, podríamos encontrar un fichero en
>sudo nano /etc/modprobe.d/raspi-blacklist.conf

Si lo encontramos, debemos editarlo y comentar (poner un # por delante) las dos líneas siguientes:
#blacklist spi-bmc2708
#blacklist i2c-bcm2708

 

Control del Bus I2C con Java


Una vez instalados los paquetes, tenemos lo necesario para acceder a dispositivo conectados al bus I2C mediante python, incluido un ejemplo de control de servos.

El problema es que queremos integrarnos con Java. De momento y tras muchos foros, el asunto no parece resuelto.

Si alguien lo ha intentado, las experiencias del cuadro pueden resultar de interés:

  • Aunque Pi4J incluye algún ejemplo de uso del I2C, el chip PCA9685 requiere de un protocolo específico.
  • Los ejemplos de Python funcionan. Al intentar utilizar el interprete Jython para invocar los scripts desde Java, una librería binaria (smbus.so) termina dando un error. No es interpretable por Jython
  • Quizá se me escapa algo, pero el puente JNI Java-Python “Jepp” tiene problemas para instalarse en el Pi ¿Sera la versión Java8?
  • Invocar scrips del sistema operativo es lento y nada optimo.

Después de frustrarme intentando invocar Python desde Java, decido cambiar de estrategia. Empiezo a bucear en los scripts del driver en python, con el propósito de traducirlos a Java. Al final lo más oscuro es la librería nativa msbus.so. Mirando los ejemplos I2C de Pi4J, encuentro un I2CBusImpl.java ¿Hablamos del mismo bus? Afortunadamente, SI.

Así que no me queda otra que escribir un driver para el PCA9685 con Pi4J.

Adjunto el jar. Incluye los fuentes y el pom de maven.

 

Ejemplo de uso del Driver


Dentro del jar encontramos un ejemplo de uso.

public class Test_Java_Servo_Driver {
   public Test_Java_Servo_Driver(){
   }

   public void testJavaServoDriver() throws Exception {
      // Cargar Driver PWM en la direccion 0x40 (primer ServoDriver)
      JavaPWM pwm = new JavaPWM( 0x40, true );
      // Señal cada 20 microsegundos (frecuencia 50Hz)
      pwm.setPWMFreq( 50 );

      // Mínimo y máximo pulso (valor dependiente de cada servo, sobre un total de 4096)
      int servoMin = 170; // # Min pulse length out of 4096
      int servoMax = 400; // # Max pulse length out of 4096
      int i = 10;

      while ( i-- > 0 ) {
         // enviar señal pwm al servo del canal 0, comienzo y fin de la señal 5V
         pwm.setPWM( 0, 0, servoMin );
         Thread.sleep( 500 );

         // enviar señal pwm al servo del canal 0, comienzo y fin de la señal 5V
         pwm.setPWM( 0, 0, servoMax );
         Thread.sleep( 500 );

         // enviar señal pwm al servo camal 0, comienzo y fin de la señal 5V
         pwm.setPWM( 0, 0, ( servoMin + servoMax ) / 2 );
         Thread.sleep( 500 );
      }
   }
   public static void main( String[] args ) throws Exception {
      new Test_Java_Servo_Driver().testJavaServoDriver();
   }
}

Dentro del Pi, copiamos en la carpeta de trabajo el driver
Java_Servo_Driver.jar

e incluimos en la carpeta ./Java_Servo_Driver_lib/. las librerías:

Podemos invocar el ejemplo con
>sudo java –classpath .:classpath:/home/pi/pro/Java_Servo_Driver_lib/’*’:/home/pi/pro/Java_Servo_Driver.jar com.metaclura.i2c.Test_ Java_Servo_Driver

 

Referencias


Configurar el i2c
http://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-i2c

Control del ServoDriver PCA9685 mediante Python
http://learn.adafruit.com/adafruit-16-channel-servo-driver-with-raspberry-pi?view=all