Panama Hitek Logo
Panama Hitek Logo

Comunicación inalámbrica entre Arduino y Python usando BLE

En este post, exploraremos el proceso de establecer una comunicación inalámbrica entre un sensor IMU basado en Arduino y una aplicación Python utilizando Bluetooth Low Energy (BLE). El uso de BLE nos permite transferir datos inalámbricamente entre el sensor y la computadora, facilitando la recopilación, almacenamiento y análisis de los datos del sensor IMU.

Comenzaremos configurando el sensor IMU con Arduino, seguido de una guía paso a paso para conectarlo a una aplicación Python utilizando la biblioteca Bleak. El código de Arduino es la base de la comunicación inalámbrica, y establece el sensor IMU, declara el servicio BLE y las características, y envía continuamente los datos de acelerómetro y giroscopio al dispositivo central BLE conectado.

Para hacer el proceso lo más sencillo posible, utilizaremos el Arduino Nano BLE 33 Sense para esta demostración. Esta placa combina la funcionalidad del Arduino Nano con un módulo BLE, haciéndolo una opción ideal para proyectos que requieren tanto un microcontrolador como conectividad inalámbrica.

Al final de este post, tendrás una comprensión mejorada de cómo interfacear los sensores IMU con Python y usar BLE para la transferencia de datos inalámbricos. Además, tendrás un ejemplo funcionante que podrás usar como punto de partida para tus propios proyectos. Dicho esto, empecemos.

Arduino code

En esta sección, demostraré cómo configurar un sistema donde los datos de aceleración y giroscopio leídos por el Arduino se transmitan rápidamente a un script en Python.

En el código de Arduino, el primer paso es incluir las librerías necesarias, que son las bibliotecas ArduinoBLE.h y Arduino_LSM9DS1.h. La librería ArduinoBLE se utiliza para manejar la comunicación BLE, mientras que la librería Arduino_LSM9DS1 se utiliza para interactuar con el sensor IMU.

A continuación, el código declara un servicio BLE con un UUID único de «1001». Este servicio actúa como un contenedor para las dos características BLE, que son los datos de aceleración y giroscopio. Las características se declaran utilizando la clase BLECharacteristic y se asignan UUIDs de «2001» y «2011» respectivamente. Estos UUID se utilizan para identificar las características en el periférico BLE.

En la función setup(), el código inicializa el módulo BLE y el sensor IMU. Si el módulo BLE o el sensor IMU no se inicializan correctamente, se entra en un bucle infinito. El código luego establece el nombre de dispositivo y el nombre local como «IMU», agrega el servicio al módulo BLE y establece el intervalo de conexión y el estado conectable del módulo BLE. Finalmente, el módulo BLE comienza a anunciar la conexión BLE.

En la función loop(), el código envía continuamente los datos de aceleración y giroscopio al dispositivo central BLE conectado. El código obtiene primero el dispositivo central BLE conectado, y si lo hay, entra en un bucle infinito. Dentro del bucle, el código lee los datos de aceleración y giroscopio del sensor IMU, crea cadenas con los datos y escribe las cadenas en las características BLE correspondientes. El bucle luego espera 7 milisegundos antes de enviar los siguientes datos.

Aquí está el código para el Arduino Nano BLE 33 Sense:

Este código está disponible en nuestro repositorio de Github. La tasa de muestreo máxima del acelerómetro y el giroscopio en el Arduino Nano BLE 33 Sense es de 104 Hz. Esto significa que el dispositivo puede enviar datos de acelerómetro y giroscopio hasta 104 veces por segundo al dispositivo BLE conectado.

Al enviar los datos lo más rápido posible, la aplicación de Python puede recibir datos de sensores en tiempo real, lo que facilita la analítica y el procesamiento de la información en tiempo real. Esta alta tasa de muestreo hace que el Arduino Nano BLE 33 Sense sea un dispositivo ideal para una amplia gama de aplicaciones que requieren datos rápidos y confiables del IMU.

Scanner BLE en Python

En esta sección, nos adentraremos en el código de Python y veremos cómo se conecta al sensor IMU utilizando el protocolo BLE. Utilizaremos la librería Bleak para manejar la comunicación BLE, y el código se escribirá en Python 3.

Para comenzar, escaneará los dispositivos BLE disponibles y asegurará que el sensor IMU esté disponible. Una vez que se haya detectado el sensor IMU, usaremos su dirección para establecer una conexión. Aquí hay un código para eso (también disponible en Github):

Este código utiliza la función discover de la librería Bleak para buscar dispositivos BLE. La función de discover devuelve una lista de objetos de dispositivos, que contienen información sobre cada dispositivo, incluyendo el nombre, la dirección y el RSSI (indicador de fuerza de señal recibida) del dispositivo.

En la función de scan, el código recorre la lista de dispositivos descubiertos e imprime la información de cada dispositivo usando la función print. Finalmente, el código obtiene el bucle de eventos y ejecuta la función de scan usando loop.run_until_complete.

Al usar este código, puedes verificar fácilmente si tu dispositivo Arduino está disponible y obtener su dirección, lo que es esencial para establecer una conexión BLE entre la aplicación Python y el Arduino. Cuando ejecuto este script, obtengo una salida como esta:

IMU es el nombre del dispositivo que especificamos en el código de Arduino (BLE.setDeviceName(«IMU»)). Necesitaremos la dirección de nuestro dispositivo para crear un canal inalámbrico entre el Arduino y Python.

BLE es una tecnología ampliamente utilizada, y como resultado, muchos dispositivos BLE pueden ser detectados en cualquier ubicación dada. Dependiendo del entorno, no es raro detectar unos pocos o incluso docenas de dispositivos BLE cercanos.

Comunicación inalámbrica en tiempo real entre Arduino y Python con BLE

Ahora que tenemos la dirección BLE del Arduino Nano BLE 33 Sense, podemos iniciar el proceso de comunicación entre el sensor IMU y la aplicación de Python.

En esta sección, revisaremos el código de Python que utiliza la biblioteca Bleak para conectarse al periférico BLE y recibir datos en tiempo real del acelerómetro y giroscopio del sensor IMU.

El código utiliza dos funciones, handle_accel_notification y handle_gyro_notification, para manejar los datos del acelerómetro y giroscopio recibidos como notificaciones. Luego, los datos se procesan y se almacenan en un archivo JSON para su posterior análisis.

También usaremos una función asíncrona, run, para conectarse al periférico BLE y comenzar a recibir las notificaciones de los datos del acelerómetro y giroscopio. La función run continuará ejecutando el bucle para recibir datos en tiempo real del sensor IMU.

Aquí tenemos el código en Python (también disponible en Github):

En este texto se describe cómo el código de Python utiliza la librería Bleak para conectarse al periférico BLE y recibir datos en tiempo real del sensor IMU. El código comienza definiendo dos funciones para manejar los datos de acelerómetro y giroscopio recibidos como notificaciones.

La función handle_accel_notification toma como argumentos el remitente y los datos y separa los datos en valores x, y, z separados. Luego obtiene la hora actual, crea un diccionario con los datos de aceleración y escribe los datos en un archivo JSON.

La función handle_gyro_notification es similar a la función handle_accel_notification, pero maneja los datos del giroscopio en lugar de los datos del acelerómetro.

La función run es una función asíncrona que se conecta al periférico BLE y comienza a recibir notificaciones de los datos de acelerómetro y giroscopio. La función utiliza el método start_notify de la clase BleakClient para comenzar a recibir notificaciones de las características BLE con UUIDs «2001» y «2011».

Luego, el código entra en un bucle infinito para recibir continuamente los datos del periférico BLE. El bucle se ejecuta utilizando el método run_until_complete del bucle de eventos. Finalmente, el código establece la dirección del periférico BLE al que se debe conectar y obtiene el bucle de eventos. La función asíncrona se ejecuta entonces utilizando el método run_until_complete del bucle de eventos.

De esta manera, el código de Python se conecta al periférico BLE y recibe datos en tiempo real de aceleración y giroscopio del sensor IMU, facilitando la recopilación, almacenamiento y análisis de los datos del sensor IMU.

Resultados

El código anterior guardará los datos del Arduino y los almacenará en un archivo JSON. Ese archivo se verá así:

También puedes imprimir las variables accel_data y gyro_data si quieres ver el flujo de datos en tiempo real. En mi experiencia con este algoritmo, puedes obtener de 180 a 200 muestras por segundo, con una distribución equitativa entre los datos de aceleración y giroscopio.

Conclusión

En conclusión, hemos demostrado con éxito cómo establecer una comunicación inalámbrica entre un sensor IMU basado en Arduino y una aplicación de Python utilizando Bluetooth de Baja Energía (BLE). Al usar el Arduino Nano BLE 33 Sense, fuimos capaces de enviar datos de acelerómetro y giroscopio del sensor IMU a la aplicación de Python en tiempo real, con una tasa de muestreo máxima de 104 Hz.

Cubrimos el proceso de configuración del sensor IMU con Arduino y su conexión a la aplicación de Python utilizando la librería Bleak. El código de Python demostró cómo escanear dispositivos BLE, conectarse al periférico BLE y recibir datos en tiempo real del sensor IMU como notificaciones.

Este post sirve como punto de partida para una amplia gama de proyectos que requieren datos IMU rápidos y confiables. Al utilizar BLE, es posible transferir datos inalámbricamente entre el sensor y el computador, lo que facilita la recopilación, almacenamiento y análisis de los datos del sensor IMU.

Gracias por tomarse el tiempo de leer este post. Si tienes alguna pregunta, comentario o retroalimentación, no dudes en comunicarte.

Antony García González
Antony García González
Ingeniero Electromecánico, egresado de la Universidad Tecnológica de Panamá. Miembro fundador de Panama Hitek. Entusiasta de la electrónica y la programación.

Posts relacionados

12 COMENTARIOS

  1. Hola, muy buen post, tengo una duda eso si, me tira este error al correr el programa:
    in start_notify raise BleakError(f»Characteristic {char_specifier} not found!»)
    bleak.exc.BleakError: Characteristic 00002001-0000-1000-8000-00805f9b34fb not found!
    Entiendo que no reconoce el UUID para realizar la comunicación, pero en el programa de arduino está definido como:
    BLECharacteristic accelData(«2001», BLERead | BLENotify, 20);
    BLECharacteristic gyroData(«2011», BLERead | BLENotify, 20);
    Usted sabe que podría estar causando dicho error? probé cambiando los UUID y me sigue diciendo que no lo reconoce…

    • Tienes que identificar el ID de la característica, la cual puede cambiar con cada dispositivo. Usa algún BLE Scanner para conectarte al Arduino e identificar los numeros correctos. Me falta agregar eso al post, ya que para cada dispositivo será distinto.

      Saludos.

      • Pero si estoy usando un Arduino nano 33 BLE también va a cambiar ese identificador? Porque use 2 aplicaciones para escanear el sensor (nRF Connect y BLE Scanner), y me arrojan lo mismo que está escrito en el script, lo siguiente:
        CUSTOM SERVICE
        UUID: 0001001-0000-1000-8000-00805F9B34FB
        CUSTOM CHARACTERISTIC
        UUID: 00002001-0000-1000-8000-00805F9B34FB
        CUSTOM CHARACTERISTIC
        UUID: 00002011-0000-1000-8000-00805F9B34FB
        debajo de cada una sale un mensaje que dice:
        CLIENT CHARACTERISTIC CONFIGURATION
        UUID:0X2902
        No influye que en el script de arduino esté escrito como «2001» y en el programa de python como «00002001-0000-1000-8000-00805F9B34FB»? Leí que eran distintas formas, una en 16 byte y la otra en 128, o algo así…
        Agradecería cualquier ayuda…

        • Verificalo de nuevo. Algún número debe estar mal. Eso cambia de un Arduino Nano a otro. Hace unos días me pasó y no era igual al que usé en el post

          • Gracias por responder, existe posibilidad de enviarle una foto de los UUID que me arrojan las aplicaciones a algún correo? quizá estoy ingresando el equivocado.

          • 1) En nRF Connect:

            Generic Access
            UUID: 0x1800
            Primary Service

            Device Name
            UUID:0x2A00
            Properties: READ

            Appearance
            UUID:0x2A00
            Properties: READ

            Generic Attribute:
            UUID: 0x1801
            Primary Service

            Service Changed
            UUID:0x2A05
            Properties: INDICATE
            Descriptors:
            Client Characteristic Configuration
            UUID:0x2902

            Unknown Service
            UUID: 0x1001
            Primary Service

            Unknown CharacteristicUUID: 0x2001Properties: NOTIFY,READ
            Descriptors:
            Client Characteristic Configuration
            UUID:0x2902

            Unknown Characteristic
            UUID: 0x2011
            Properties: NOTIFY,READ
            Descriptors:
            Client Characteristic Configuration
            UUID:0x2902

            2) En BLE Scanner:

            Generic Access
            0x1800
            Primary Service
            Device Name
            UUID:00002A00-0000-1000-8000-00805F9B34FBProperties: READ
            Appearance
            UUID:00002A01-0000-1000-8000-00805F9B34FB
            Properties: READ

            Generic Attribute
            0x1801
            Primary Service
            Service Changed
            UUID:00002A00-0000-1000-8000-00805F9B34FB
            Properties: INDICATE
            Descriptors:
            Client Characteristic Configuration
            UUID:0x2902

            Custom Service00001001-0000-1000-8000-00805F9B34FBPrimary Service

            Custom Characteristic
            UUID:00002001-0000-1000-8000-00805F9B34FB
            Properties: READ,NOTIFY
            Descriptors:
            Client Characteristic Configuration
            UUID:0x2902

            Custom Characteristic
            UUID:00002011-0000-1000-8000-00805F9B34FB
            Properties: READ,NOTIFY
            Descriptors:
            Client Characteristic Configuration
            UUID:0x2902

            Eso sería lo que veo en pantalla para cada aplicación…

          • Se ven igual.

            Te aseguraste de cambiar el address? Talvez puede ser algún problema con la instalación de la librería en Python

          • Si, el address está cambiado, yo también sospecho que es algo de la librería, quizá para la última versión de Bleak no funciona, o me falta actualizar algo más…

          • Corrí el código service_explorer en python y obtuve lo siguiente:

            C:\Users\Tomás Herrera Muño\Dropbox\PhD PUC\Tesis\Proyecto Towing Tank\Arduino\Códigos\Nano 33 BLE>python service_explorer.py –address 7E:36:B3:47:6F:DA

            2023-07-31 09:28:30,443 __main__ INFO: starting scan…
            2023-07-31 09:28:30,751 __main__ INFO: connecting to device…
            2023-07-31 09:28:31,229 __main__ INFO: connected
            2023-07-31 09:28:31,229 __main__ INFO: [Service] 00001800-0000-1000-8000-00805f9b34fb (Handle: 1): Generic Access Profile
            2023-07-31 09:28:31,245 __main__ ERROR:  [Characteristic] 00002a00-0000-1000-8000-00805f9b34fb (Handle: 2): Device Name (read), Error: Could not read characteristic handle 2: Unreachable
            2023-07-31 09:28:31,246 __main__ ERROR:  [Characteristic] 00002a01-0000-1000-8000-00805f9b34fb (Handle: 4): Appearance (read), Error: Not connected
            2023-07-31 09:28:31,247 __main__ INFO: [Service] 00001801-0000-1000-8000-00805f9b34fb (Handle: 6): Generic Attribute Profile
            2023-07-31 09:28:31,247 __main__ INFO:  [Characteristic] 00002a05-0000-1000-8000-00805f9b34fb (Handle: 7): Service Changed (indicate)
            2023-07-31 09:28:31,248 __main__ ERROR:   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 9): Client Characteristic Configuration, Error: Not connected
            2023-07-31 09:28:31,248 __main__ INFO: [Service] 00001101-0000-1000-8000-00805f9b34fb (Handle: 10): Serial Port
            2023-07-31 09:28:31,249 __main__ ERROR:  [Characteristic] 00002101-0000-1000-8000-00805f9b34fb (Handle: 11): Vendor specific (read,notify), Error: Not connected
            2023-07-31 09:28:31,250 __main__ ERROR:   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 13): Client Characteristic Configuration, Error: Not connected
            2023-07-31 09:28:31,251 __main__ ERROR:  [Characteristic] 00002102-0000-1000-8000-00805f9b34fb (Handle: 14): Vendor specific (read,notify), Error: Not connected
            2023-07-31 09:28:31,251 __main__ ERROR:   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 16): Client Characteristic Configuration, Error: Not connected
            2023-07-31 09:28:31,252 __main__ ERROR:  [Characteristic] 00002103-0000-1000-8000-00805f9b34fb (Handle: 17): Vendor specific (read,notify), Error: Not connected
            2023-07-31 09:28:31,252 __main__ ERROR:   [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 19): Client Characteristic Configuration, Error: Not connected
            2023-07-31 09:28:31,253 __main__ INFO: disconnecting…
            2023-07-31 09:28:31,261 __main__ INFO: disconnected
            Al parecer no está leyendo la característica y por eso salen errores después en el proceso….

DEJA UNA RESPUESTA

Por favor ingrese su comentario!
Por favor ingrese su nombre aquí

Post relacionados