Archive for the 'Usb' Category

Raspberry Pi & Arduino. Un nuevo ecosistema?

Viernes, mayo 18th, 2012


Afortunadamente (para mí) he sido uno de los que han recibido la Raspberry Pi en los primeros envíos de Farnell, creo que en la segunda tanda.

Como es de bien nacidos ser agradecido, quiero hacer lo propio con l@s chic@s de @FarnellES en twitter que me han tenido al tanto siempre de como iba la distribución de las Raspberry Pi ;) .

Bueno, pues a lo que iba, esto hoy va a ser más un artículo de opinión personal con mi experiencia (muy poca todavía) con la placa y su entorno.

Lo que me ha llevado a escribir esta entrada, es un post de Liz (Raspberry Pi Foundation), en el que mostraba su tristeza por la actitud (quizás demasiado apasionada si esa puede ser la descripción) de alguna gente de la comunidad de Arduino que se mostraba a la defensiva dando a entender que la Raspberry venía a ocupar el nicho que actualmente posee Arduino en el mundo geek.

Personalmente, pienso como ella y no creo que eso llegue a pasar, pues son dos cosas completamente diferentes y que a mi entender se complementan o pueden hacerlo muy bien.

No podemos comparar la capacidad de cálculo de la Raspberry Pi con la de Arduino, como tampoco podemos hacerlo en la Raspberry Pi con la versatilidad I/O y adaptabilidad que nos ofrece Arduino. Son cosas completamente diferentes e incluso conceptualmente tambien lo son.

He querido hacer una prueba que me demostrase una de las principales diferencias entre una placa y la otra, y nada mejor que un control de dos servos, que implica la generación de dos señales PWM.

Usé un Pan-tilt con dos servos de 7grs que responden a una señal de 3,3v como pulso PWM de control para poder conectarla a los GPIO de la Rasperry Pi directamente. Los servos van alimentados con 5v desde una FA.

La primera prueba la vamos a hacer conectando las señales de los dos servos a los GPIO de la Raspberry (ojo! son conexiones directas, sin buffers intermedios ni ningún tipo de protección. Un mal uso quemaría el GPIO de la Raspberry o hasta el SoC) en este caso a las patillas 11 y 16 de P1, que se corresponden con GPIO 21 y 22 de la librería para python RPi.GPIO-0.1.0.zip de Ben Croston.

Es necesario instalarla para poder ejecutar el programa.

#       servo_GPIOs.py
#
#       Copyright 2012 Raspberry Pi User pi@raspberrypi
#
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

import RPi.GPIO as GPIO
import time
import consola_io
#-- Servo positions
ESC = '\x1B'
servo1=90
servo2=90

# Move servos routine
# servos are attached at  GPIO22 and GPIO21
# on raspberry Pi
def mover_servo(grados,servo):
    if servo==1: GPIO_servo=22
    elif servo==2: GPIO_servo=21
    # create PWM pulse
    pos_servo=(0.0000122*grados)+0.0002
    GPIO.output(GPIO_servo, True)
    # Using time.sleep is a worst idea
    time.sleep(pos_servo)
    GPIO.output(GPIO_servo, False)
    # Another bad idea.
    time.sleep(0.0025-pos_servo)
# Asimple menu with some info
def menu():
    print """

        W - Arriba
        S - Abajo
        A - Izquierda
        D - Derecha

    ESC - Terminar
    @radikaldesig 2012 RaspberryPi
    Control de servos
    """

# set up the GPIO channels 21 & 22 as output
GPIO.setup(22, GPIO.OUT)
GPIO.setup(21, GPIO.OUT)

menu()
# main loop
while 1:
    c = consola_io.getkey()

    if c=="w" or c=="W":
        servo1=servo1+1
        if servo1>180 : servo1=180
        mover_servo(servo1,1)
    elif c=="s" or c=="S":
        servo1=servo1-1
        if servo1<0 : servo1=0
        mover_servo(servo1,1)
    elif c=="a" or c=="A":
        servo2=servo2-1
        if servo2<0 : servo2=0
        mover_servo(servo2,2)
    elif c=="d" or c=="D":
        servo2=servo2+1
        if servo2>180: servo2=180
        mover_servo(2,2)
    elif c==ESC:break

print "Goodbye"

Como se puede ver en el código, la generación del pulso PWM es “manual”, usando como generador de retardos el procedimiento sleep de la librería time de python.
Es una solución chabacana que nos va a dar problemas y va a hacer que el pulso sea irregular y que varíe o que se interrumpa, con lo que no podremos conseguir una señal estable que nos permita mover los servos en condiciones y luego veremos porqué pasa esto.
Este video muestra el movimiento de los servos y en él se aprecian los saltos producidos por las pérdidas de pulsos o la rotura de los mismos:

Si nos paramos a pensar detenidamente, no es una buena idea generar los pulsos PWM para los servos desde los GPIOs de la Raspberry:

  • Python es un lenguaje interpretado y por tanto, es lento de ejecución (aunque no lo notemos)
  • Las características propias de Linux y la forma de planificación de procesos por parte del sistema hace que perdamos tiempo en los cambios de contexto que se producen y que son inherentes al propio SO
  • Hagamos un ejemplo en C para ver como se producen retardos entre procesos por la planificación de éstos en el sistema:

    #include signal.h
    #include time.h
    main ()
    {
     int a,b;
     b=0;
     if ((a=fork())==0)
    {
     while (1)
    {
     printf("iter:%d\n",b);
     usleep(100);
     b++;
    }
    }
     usleep(100000);
     printf("Terminacion del proceso con pid= %d e iter:%d\n",a,b);
     kill(a,SIGTERM);
    }

    Tenemos que un servo se mueve aproximadamente con un pulso de entre 0,5ms y 2,5ms a una frecuencia de unos 50Hz (20ms) algo, como esto:

    Servo pulse

    Así que para una precisión de 1 grado necesitaríamos poder ajustar la longitud del pulso en intervalos de 11,1 uS.
    Con este pequeño programa de prueba, lo que vamos a hacer es crear un proceso hijo cada 100 uS durante un tiempo máx de 100mS.
    Ponemos una variable a contar los procesos hijos creados y comprobamos en varias ejecuciones que estos difieren en número en cada ocasión.
    Es muy simple, el núcleo del sistema atiende otros procesos a la vez además del nuestro, y esto hace que el nivel de prioridad del ejemplo descienda en la tabla de planificación y que nuestro pulso de 0,8ms sea realmente de 1,1ms o 0,9ms o 0,83ms provocando desviaciones de hasta 90 grados ( WoW! ), dependiendo de la carga de procesos del sistema.
    Y claro, se nos viene a la cabeza -Esto con Arduino no pasa, el uC ejecuta un solo proceso unicamente interrumpible por una IRQ y que yo tengo que habilitarle su permiso… -
    Pues sí, basta el ejemplo de cualquier dispositivo o máquina industrial, ninguna CPU o sistema embebido realiza la maniobra de un servomotor, actuador o electroválvula… de todo ello se encarga un sistema periférico, bien un inversor, un regulador de giro o un sistema controlador de servos comunicados por CAN, profibus, RS485, etc.
    Entonces, ¿porqué no complementar la capacidad de cálculo de la Raspberry Pi con las posibilidades I/O de Arduino estableciendo una comunicación a través del propio USB, que para eso disponemos de él?
    Además establecemos una barrera perfecta entre nuestros motores, servos, Triacs o lo que sea y nuestra Raspberry, que no ha sido diseñada para eso.
    Ese es el paso siguiente. Conectemos el Arduino al USB y busquemos a ver que tenemos:

    Ya tenemos localizado entre los dispositivos serie la id de nuestro Arduino. En Debian debería de crearnos un nuevo dispositivo del tipo /dev/ttyACM0.
    Podemos olvidarnos por ahora del Arduino IDE en nuestra Raspberry, ya que la máquina de java es muy pesada y tenemos la limitación de 256Mb de RAM de la placa, así que nos vamos a limitar a comunicarnos con Arduino a través de la conexión serie del USB.
    Cargamos nuestro Arduino con ServoControl.pde:

    /*
     * Servo control with Arduino
     * using an USB comunication
     */
    #No olvideis poner los angulos en el include
    #include Servo.h 
    
    // Create objects
    Servo servo1;
    Servo servo2;
    
    // Common servo setup values
    int minP = 600;   // minimum servo position, us (microseconds)
    int maxP = 2400;  // maximum servo position, us
    
    // User input for servo and position
    int inputData[3];    // raw input from serial buffer, 3 bytes
    int byteIni;        // start byte, begin reading input
    int servo;           // which servo to pulse?
    int pos;             // servo angle 0-180
    int i;               // iterator
    
    void setup()
    {
      // Attach each Servo object to a digital pin
      servo1.attach(2, minP, maxP);
      servo2.attach(3, minP, maxP);
    
      // Open the serial connection, 9600 baud
      Serial.begin(9600);
    } 
    
    void loop()
    {
      // Wait for serial input (min 3 bytes in buffer)
      if (Serial.available() > 2) {
        // Read the first byte
        byteIni = Serial.read();
        // If it's really the byteIni (255) ...
        if (byteIni == 255) {
          // ... then get the next two bytes
          for (i=0;i<2;i++) {
            inputData[i] = Serial.read();
          }
          // First byte = servo to move?
          servo = inputData[0];
          // Second byte = which position?
          pos = inputData[1];
          // Packet error checking and recovery
          if (pos == 255) { servo = 255; }
    
          // Assign new position to appropriate servo
          switch (servo) {
            case 1:
              servo1.write(pos);    // move servo1 to 'pos'
              break;
            case 2:
              servo2.write(pos);
              break;
          }
        }
      }
    }

    Conectamos las señales de control de los servos a Digital2 y Digital3 del Arduino y ya tenemos nuestro módulo periférico totalmente preciso al que vamos a enviarle las órdenes de movimiento de la forma:
    [start byte 0xFF] [servo a mover] [posicion en grados]
    Usamos la librería servo incluida en el entorno de Arduino y ahora ya podemos dejar los cálculos y el control de las posiciones para la Raspberry.
    Hagamos un ejemplo en Python:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    #
    #       servos.py
    #
    #       Copyright 2012 Raspberry Pi User pi@raspberrypi
    #
    #       This program is free software; you can redistribute it and/or modify
    #       it under the terms of the GNU General Public License as published by
    #       the Free Software Foundation; either version 2 of the License, or
    #       (at your option) any later version.
    #
    #       This program is distributed in the hope that it will be useful,
    #       but WITHOUT ANY WARRANTY; without even the implied warranty of
    #       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #       GNU General Public License for more details.
    #
    #       You should have received a copy of the GNU General Public License
    #       along with this program; if not, write to the Free Software
    #       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
    #       MA 02110-1301, USA.
    
    import time
    import serial
    import consola_io
    import random
    
    # Assign Arduino's serial port address
    #   Windows example
    #     usbport = 'COM3'
    #   Linux example
    #     usbport = '/dev/ttyACM0'
    #   MacOSX example
    #     usbport = '/dev/tty.usbmodem621'
    usbport = '/dev/ttyACM0'
    
    # Set up serial baud rate
    ser = serial.Serial(usbport, 9600, timeout=1)
    
    # servo's position
    servo1=90
    servo2=90
    
    # move servo routine that send data to Arduino
    def mover_servo(grados,servo):
        ser.write(chr(255))    # start transmission
        ser.write(chr(servo))  # Select servo
        ser.write(chr(grados)) # degrees to move
    
    # A little menu with info
    def menu():
        print """
    
            W - Arriba
            S - Abajo
            A - Izquierda
            D - Derecha
    
        ESC - Terminar
        @radikaldesig 2012 RaspberryPi
        Control de servos
        """
    
    menu()
    while 1:
        c = consola_io.getkey()
    
        # Move UP
        if c=="w" or c=="W":
            servo1=servo1+5
            if servo1>180 : servo1=180
            mover_servo(servo1,1)
        # Move down
        elif c=="s" or c=="S":
            servo1=servo1-5
            if servo1<0 : servo1=0
            mover_servo(servo1,1)
        # Move left
        elif c=="a" or c=="A":
            servo2=servo2-5
            if servo2<0 : servo2=0
            mover_servo(servo2,2)
        # Move right
        elif c=="d" or c=="D":
            servo2=servo2+5
            if servo2>180: servo2=180
            mover_servo(servo2,2)
        # move to 20 random pos
        elif c=="v" or c=="V":
    	for i in range(20):
     	   servo1=random.randint(0,180)
    	   servo2=random.randint(0,180)
    	   mover_servo(servo1,1)
    	   mover_servo(servo2,2)
    	   time.sleep(0.5)
        elif c==ESC:break
    
    print "Goodbye"

    Necesitaremos la librería consola_io.py en el mismo directorio donde lancemos el programa y además instalar las librerías de comunicación serie de python pyserial-2.5
    Tambien debemos configurar el dispositivo creado por el Arduino para dirigir nuestra comunicación, en mi caso es usbport = ‘/dev/ttyACM0′
    Nuestro porgrama es simple, dependiendo de la pulsación de las teclas WASD, mueve los servos a intervalos de 5 grados, desde la posición de 0 grados hasta la de 180 grados, enviando 3 bytes al Arduino:
    Startbyte, Nservo, Posicion
    Le he añadido una pequeña feature al pulsar V y es que mueve los 2 servos a 20 posiciones aleatorias cada 0,5 segundos.
    Ahora si que no hay saltos y los pulsos son perfectos.
    Verdad que el Arduino y la Raspberry se complementan bien?

    Ya resumiendo, es evidente que no necesitamos una Raspberry Pi para controlar un Arduino que mueva dos servos, pero sí podemos usarla para montarla sobre una base móvil (robot, orugas,…) y poder implementar un sistema de visión artificial con OpenCV corriendo en Python usando una cámara USB, enviando la señal de la imagen por wifi, y a su vez datos de telemetría, sensores de temperatura, giroscopios, acelerómetros y dotarla de una IA que entonces sí gestione un Arduino encargado de mover los motores, el pan-tilt de la cámara, leer los sensores y manejar infinidad de dispositivos I/O que para eso Arduino tiene mucho músculo y rodaje.

    Aún podemos pensar que esto no puede ser un ecosistema maravilloso???
    Os leo en el twitter…

    Droky
    @radikaldesig


    DESCARGAS

     

     

     

    Conversor USB<>TTL ( 5v y 3,3v )

    Lunes, junio 23rd, 2008

    Seguro que alguna vez habéis necesitado conectar vuesro circuito con PIC, Arduino, Atmel, un GPS, una placa entrenadora o cualquier cosa a un puerto serie de vuestro ordenador y os habéis encontrado que los portátiles modernos ya no llevan conector RS232 y necesitais un adaptador y luego un convertidor de niveles a TTL. Y si resulta que necesitáis que el circuito funcione con niveles de 3,3v pues el lío aún es mayor.

    Este circuito permite conseguir esa comunicación serie tan necesaria a veces y además podemos seleccionar el tipo de niveles de tensión con los que vamos a comunicarnos: 5v o 3,3v simplemente con un pequeño jumper de soldadura en la placa.
    Añadiría un plus al circuito, y es que puede alimentar un circuito conectado a él ( a 5v o 3.3v ) y hasta un máximo de 100mA de consumo. No esta mal…

    El circuito

    El circuito gira alrededor de un chip de FTDI, el FT232RL que es un chip con “USB to serial UART interface” integrado. No necesita ningún tipo de programación, solo conectarse con unos pocos componentes alrededor y listo.
    Observamos el esquema que es prácticamente uno de los App Note del fabricante pero modificado para el tipo de placa simple y pequeña que me apetecía desarrollar.
    Tenemos 5 condensadores de desacoplo de las diferentes tensiones de alimentación que existen en el circuito, que son :
    La tensión de salida del USB, la misma tensión después de pasar por el filtro de ferrita (Ferrite Bead), la tensión de salida de 5v, la tensión de salida de 3.3v y la tensión de alimentación del FT232RL. Estos valores se han tomado siguiendo el consejo del fabricante.
    JMP1 es el jumper que permite seleccionar la tensión de trabajo del circuito entre 3.3v y 5v, que será la misma que proporcionará a la salida por el pin 1 del conector SV1.
    Y R1 es una resistencia de 0 ohm usada como jumper para poder resolver la PCB a una sola cara uniendo dos planos de masa.

    La PCB

    He intentado que fuera lo más pequeña posible y a una sola cara, para no complicar el asunto de la PCB y desechando componentes tamaño 0805 porque no podia pasar pistas entre las patillas, me decidí por el tamaño 1206 que también es más cómodo de posicionar. Para reducir espacio ( mi prototipo mide 38×17 mm ) usé un conector miniUSB tipo B y el FT232RL tiene un encapsulado SSOP de 28 pines.
    El conector de salida es una tira de 5 pines con las siguientes conexiones:
    1-VCCOut, 2-TX, 3-RX, 4y5-GND
    Así me quedó la placa usando un nuevo proceso para crear el fotolito con impresora laser que aún estoy perfeccionando:

    Y este es el fotolito de la PCB:


    El resultado final con la simulacion en Eagle 3D:

    Los Drivers
    Una vez comprobado todo de posibles cortocircuitos y soldado todo ( impresiona ver como se va todo al sitio en el horno de soldadura ) llega el momento de la verdad, así que conectamos el circuito a nuestro puerto USB y parece que nos lo detecta sin problemas:

    Le damos el DRIVER DEL FABRICANTE y ya lo tenemos instalado como un nuevo puerto serie:


    La Prueba

    Pues con un programa de comunicaciones tipo terminal, envío una trama en hexadecimal por el nuevo puerto serie creado, tal que: 0A0F0A0 y esto es lo que obtengo en el pin TX del circuito:
    Para JMP1=5v:

    Y para JMP1=3.3v

    El resultado es que envía datos y como tenía que probar la recepción y la transmisión, que mejor que una fonera y conectada a niveles de 3.3v. Sueldo el Jumper JMP1 para modo 3.3v y preparo un adaptador ( sin quererlo casi se conecta directamente a la fonera ) para conectar con la fonera.

    Con el hiperterminal , me conecto a la fonera. Me sale la consola de RedBoot y pulso Control+C … ya está, envío y recibo. El circuito funciona perfectamente.

    Descargas:


    Copy Protected by Tech Tips's CopyProtect Popup Images .