Mostrando entradas con la etiqueta python. Mostrar todas las entradas
Mostrando entradas con la etiqueta python. Mostrar todas las entradas

9/22/2020

Primeros pasos con Arduino nano 33 Sense BLE

 

 



 

 

Saludos, estamos vivos y en este tiempo es bueno poder decir esto. Bien ha sido un periodo muy extraño que por suerte nos ha permitido seguir trabajando desde casa en horarios no euclidianos.

Entre otras muchas cosas me llego un proyecto para trabajar con un Arduino nano 33 sense con Ble. La placa por menos de 35 €  ofrece una cantidad de sensores:

·        Sensor de inercia de 9 ejes.

·        Sensor de humedad y temperatura.

·        Sensor barométrico

·        Sensor de gestos, proximidad, color, luz

·        Micrófono

·        BLE: bluetooth de bajo consumo

Sin daros mucho la chapa, como comenzar a trabajar con el esta muy bien explicado en el tutorial de arduino.

Quizá tengáis algo de trabajo en instalar todas las librerías, porque no están agrupadas en una sola.

 

#include <ArduinoBLE.h>                //Bluetooth ble

#include <Arduino_LSM9DS1.h>      // IMU //

#include <PDM.h>                            // Digital microphone

#include <Arduino_APDS9960.h>    // Gesture sensor //

#include <Arduino_LPS22HB.h>     // Pressure //

#include <Arduino_HTS221.h>      // Relative humidity and temperature //

 

Casi todas fácilmente instalables desde el administrador de bibliotecas.

 

La gracia es tratar de extraer la información de los sensores y enviarlos por bluetooth. Pero para esto que con un bluetooth típico HC-06  el funcionamiento es muy similar al Serial. Pero con el ble es otro mundo y este post es ayudar a solucionar algunos problemas que me he topado.

Para explicarlo vamos a usar un simple código donde enviaremos el valor de la IMU y recibiremos un char que nos variara el estado del led (Pin 13). El Ble su uso esta muy extendido en un montón de periféricos y su uso no debería ser problemático… Ahora bien, intentar capturar los datos mencionados en el ejemplo anterior en un ordenador con Windows.  Sin la aplicación específica del fabricante es complicado.





Topología del modo connected

 

Insisto no os voy a dar la chapa, aquí tenéis una buena explicación de como funciona el bluetooth low energy BLE. Resumiendo, nosotros en el Arduino vamos a tener que crear una serie de servicios con unas propiedades, de lectura, escritura o notificación. Y desde la central (programa Python en Windows) nos conectaremos cuando necesitemos y obtendremos los datos.

 

 

Cabecera del programa

 

#include <ArduinoBLE.h>

#include <Arduino_LSM9DS1.h> //IMU

 float Ax, Ay, Az, Gx, Gy, Gz, Mx, My, Mz;

const int BUFFER_SIZE = 64;

char msgprint[BUFFER_SIZE];

 

 const char* uuid_service="00001101-0000-1000-8000-00805f9b34fb";

const char* uuid_string="00001143-0000-1000-8000-00805f9b34fb";

const char* uuid_char="00001150-0000-1000-8000-00805f9b34fb";

 

BLEService customService(uuid_service);

BLECharacteristic Send_string(uuid_string,BLERead | BLENotify ,BUFFER_SIZE,false);

BLECharCharacteristic led_control(uuid_char, BLERead | BLEWrite);// 0,1

 

 

Las direcciones uuid tiene un formato de 16 bits o 128, en principio pensé que podían ser inventadas, y pueden pero el primer valor 0x1101 o 0x1143, representa el GATT, que en función del valor que sean que se está enviando, aquí podéis ver una lista de perfiles existentes. Esto esta pensado para hacer el protocolo más estable entre fabricantes. Si ponéis un perfil  especifico, en las aplicaciones de Android o ios os aparecerá ese nombre en el servicio. Si no aparecerá toda la dirección.

 

Definimos tres, la principal es la que contendrá el resto de los servicios vendría siendo el “profile”.

La segunda es la dirección donde enviaremos el string, como características se puede hacer una orden de lectura o recibir notificaciones de cambios.

Y la tercera es donde leeremos y escribiremos en un char que emplearemos para activar el led.

 

 



Servicios y caracteristicas

 

 

Setup del programa

void setup() {

  Serial.begin(9600); //to debug

  if (!IMU.begin()) {    // init IMU

    Serial.println("Failed to initialize IMU!");

    while (1);

  }

  if (!BLE.begin()) {   //init  BLE

    Serial.println("starting BLE failed!");

    while (1);

  }

  BLE.setLocalName("IMU_test"); // name of Bluetooth

  BLE.setAdvertisedService(customService.uuid());

  customService.addCharacteristic(Send_string); // add characteristic

  customService.addCharacteristic(led_control); // add characteristic

  BLE.addService(customService);

 

 

  led_control.setValue(‘0’); // initial values 0x30 o 0 en Asccii

  

  // Events

  led_control.setEventHandler(BLEWritten,led_Update); // event to write

  BLE.setEventHandler(BLEConnected, onBLEConnected); // event to connect optional

  BLE.setEventHandler(BLEDisconnected, onBLEDisconnected); // event to disconned optional

   

  BLE.advertise();

  Serial.println(BLE.address());// Print the mac address

  pinMode(13,OUTPUT);

}

 

Inicializamos el Serial, el IMU y la conexión BLE.

Añadimos el nombre al bluetooth, activamos el profile y le añadimos las características que hemos defino anteriormente. Si uno de estos servicios necesita inicializarlo “led_control.setValue(‘0’)”.

El valor inicial va en función del tipo de característica definido, en nuestro caso un Char porque lo cambiaremos mediante la introducción de teclado y para no andar con conversiones de char a int lo dejamos como char.

Los eventos, “setEventHandler” definen funciones que se llaman cuando se da la condición propiamente descrita, en este caso cuando queramos escribir un char o cuando el bluetooth se conecte o se desconecte.

 

Funciones

void led_Update(BLEDevice central, BLECharacteristic characteristic) {

   char aux=led_control.value();

   if (aux=='1') digitalWrite(13,HIGH); // '1' o 0x31

   else  digitalWrite(13,LOW);

}

 

void onBLEConnected(BLEDevice central) {

  Serial.print("Connected event, central: ");

  Serial.println(central.address());

 }

 

void onBLEDisconnected(BLEDevice central) {

  Serial.print("Disconnected event, central: ");

  Serial.println(central.address()); // util to  know which mac address is your board

 }

  

 

 

Y por último tenemos el loop, donde gestionamos que hacer una vez nos conectemos a la central, en nuestro caso leeremos los valores de la IMU, los agruparemos en un string que enviaremos. Podríamos haber hecho un evento para la lectura de los valores, pero así podemos definir qué hacer.

Loop

void loop() {

  BLEDevice central = BLE.central();

  if (central) {

    while (central.connected()) {

      if (IMU.accelerationAvailable()&& IMU.gyroscopeAvailable() && IMU.magneticFieldAvailable()) {

        IMU.readAcceleration(Ax, Ay, Az);

        IMU.readGyroscope(Gx, Gy, Gz);

        IMU.readMagneticField(Mx, My, Mz);

        sprintf(msgprint, "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,", Ax, Ay, Az, Gx, Gy, Gz, Mx,             My, Mz);

        Serial.println(msgprint);

        Send_string.writeValue(msgprint, sizeof(msgprint));  // guardamos en el servicio Send_string

         }

  }

}

 

Mientras estemos conectados imprimirá por el serial el valor de la IMU, y enviará estos valores al servicio.

Código completo.

 

El Ide de Arduino tarda en compilar y luego esta placa tiene otra particularidad, tiene dos puertos un para monitorear y otro para programar, pulsando 2 veces seguidas el reset entra en modo programar, selecciona ese puerto para enviar el programa. Una vez hecho tendrás que cambiar al otro puerto para mirar el serial.

 

Vamos al ordenador.

Con Python 3.7 he empleado la librería Bleak (“pip install bleak”) para comunicación con el bluetooth low energy y aiconsole (“pip install aioconsole” ), para introducir comandos en la consola

La lecturas que se van a realizar son asíncronas, y el programa se detendrá para esperar la instrucción del control del led, si se le envía ‘x’ romperá el bucle, se desconectara y saldrá del programa.

 

Inicio

 

import logging

import asyncio

import datetime

 

from aioconsole import ainput

from bleak import BleakClient, BleakError

 

read_string_uuid = "00001143-0000-1000-8000-00805f9b34fb" #servicio de lectura de string

led_control_uuid = "00001150-0000-1000-8000-00805f9b34fb" #servicio control del led

 

 

#definimos variable globales

Ax=0.0;Ay=0.0;Az=0.0;Gx=0.0;Gy=0.0;Gz=0.0;Mx=0.0;My=0.0;Mz=0.0

 

Definimos los servicios con el mismo  valor que los servicios del Arduino

 

 

Funcion principal

 

async with BleakClient(address, loop=loop) as client:

        while(1):

            x = await client.is_connected() #  Connect to address

            log.info("Connected: {0}".format(x))

            log.info("Starting notifications/indications...")

           

# Start receiving notifications, which are sent to the `data_handler method`

            await client.start_notify(indication_characteristic_uuid, data_handler)# read the string

           

            # wait for instruction to crontrol the led

            keyboard_input = await ainput("Control Led [0,1]: ")

            if keyboard_input== 'x':

                break

            bytes_to_send = bytearray(map(ord, keyboard_input))

            await client.write_gatt_char(led_control_uuid, bytes_to_send, response=True) # Sino es True no lo envia

 

            # Send a request to the peripheral to stop sending notifications.

            await client.stop_notify(indication_characteristic_uuid)

            log.info("Stopping notifications/indications...")

 

 

Una vez conectado, la data contiene 64 bytes, que es como hemos definido el buffer y en data_handler procesamos esos datos. Después el sistema se detiene a la espera de una entrada de teclado. Con un 1 leído como un char se enciende el led del Arduino, x cierra la aplicación y cualquier otra cosa apaga el led.

 

def data_handler

def data_handler(sender, data):

        global Ax,Ay,Az,Gx,Gy,Gz,Mx,My,Mz

        #log.info("{0}: {1}".format(sender, data))

        data_storage.append((datetime.datetime.utcnow().timestamp(), data))

        aux = bytearray(data)

        datos = stringdata = aux.decode('utf-8')

        msg = datos.split(",")

        Ax=float(msg[0])

        Ay=float(msg[1])

        Az=float(msg[2])

        Gx=float(msg[3])

        Gy=float(msg[4])

        Gz=float(msg[5])

        Mx=float(msg[6])

        My=float(msg[7])

        Mz=float(msg[8])

        print("Accelerometro [xyz]:"+str(Ax)+", "+str(Ay)+", "+str(Az))

        print("Gyroscope [xyz]:"+str(Gx)+", "+str(Gy)+", "+str(Gz))

        print("Magnetometre [xyz]:"+str(Mx)+", "+str(My)+", "+str(Mz))

 

 

Recibimos 64 bytes en un bytearray, salvo en MAC que hay que convertirlo a bytearry de 64 bytes, mediante un Split separamos por las comas y el valor msg[9] es descartable son todo cero. El resto lo convertimos a valores float en este caso. Y visualizamos con el print

 

 

Código python



Anakleto...

 

5/08/2017

Eddibot con palanca y control python

Saludos, 


he estado trabajando ya nuestros Eddiebots les he añadido algunas curiosidades, además del sensor de distancia APDS 9960, le hemos puesto un seguidor de línea y una barra para que se levante del suelo el solo.  En ambos caso los sensores/actuadores que hemos instalado son I2c.




Esta entrada podría ser la continuación de la sensores i2c con pic o una segunda entrada del Intel Edison. Al modelo original de Ediboot le he añadido unos soportes en la parte posterior para que se quede en una posición semi-vertical.




En la parte delantera he instalado una palanca controlada por un motor micrometal de 298: 1 de reducción, más que de sobra para poder desplazar el peso del robot, para controlar el motor he empleado como en otras ocasiones el driver L272M controlado por el pic12lf1840 configurado como esclavo i2c. Podría haber empleado alguno de los pines libres de la placa de GPIO pero no son tantas las que están libres por el uso de la placa de motores y opte por este método que me da más libertad de diseño, reduciendo los problemas a otros más pequeños.


Controlar el motor al fin y al cabo no es mucho más complicado que el control del Led, que explique en el primer ejemplo de i2c, se le puede enviar tres instrucciones:



0x31: Bajar palanca

0x32: Recoger palanca

0x30: Parar Palanca


Al no tener encoder el motor o bien se hacía un control por tiempos, algo que suele descalabrarse, así que opte por colocar un pulsador en el pecho del robótico para que actuara de final de carrera cuando se recoge la palanca.  A la hora de bajarla el limite lo impongo controlando el ángulo del “kalmanfilter”, cuando llegue a cierto ángulo el robot se pondrá de pie solo.





Otro de los sensores que le instalado en el seguidor de línea, en varias versiones, esto quiere decir que he ido modificando como colocar el sensor en función de cuantas cosas llevara el robot.





Al igual que la “pata”, su nombre oficial es otro en la oficina no dejan de reírse con el robot con “pene”, el sensor de línea he empleado el picf1840 para hacer el sensor esclavo i2c. El sistema es cómodo y te permite realizar sensores que se pueden adaptar a cualquier plataforma, Arduino, raspberry pi, Intel edison … 

El aspecto final del sensor que he empleado es:




Placa “home Made” con picf1840, se instala entre los motores del Eddiebot







Otra cosa que debíamos hacer con los Eddibot es que colaborasen entre ellos transportando una plancha o algo similar, haciendo pruebas es algo “relativamente” sencillo de implementar, lo complicado era posicionarlos en una poción concreta para realizar el experimento. 






Para que esto fuera sencillo realice un control mediante Python para poder situar a los robots en la posición correspondiente. No necesita de mucha explicación simplemente cambiar las ip’s para que coincidan con la de vuestros robots y poco más.


Conectarse con los Robots Z
Robot 1 control W,S,A,D
Robot 2 Control I,K,J,L,
Control común T,G,B
Salir X

Anakleto

9/28/2015

Robots con arduino Yun

Saludos,

A principio del mes de agosto estuve enfrascado en un proyecto con el Arduino yun, para realizar un robot semejante a los que ya he construido pero con la diferencia que en este caso podría tratarse de un robot autónomo.



Me explico, a grandes rasgos el Arduino yun funciona como un Arduino normal, con la salvedad que tiene conexión wifi y un sistema operativo propio el linino.


AVR Arduino microcontroller
Linux Microprocessor
Microcontroller                ATmega32U4
Operating Voltage          5V
Input Voltage    5
Digital I/O Pins  20
PWM Channels                7
Analog Input Pins            12
DC Current per I/O Pin  40 mA
DC Current for 3.3V Pin                50 mA
Flash Memory  32 KB (of which 4 KB used by bootloader)
SRAM   2.5 KB
EEPROM              1 KB
Clock Speed       16 MHz
Processor           Atheros AR9331
Architecture      MIPS @400MHz
Operating Voltage          3.3V
Ethernet             IEEE 802.3 10/100Mbit/s
WiFi       IEEE 802.11b/g/n
USB Type-A       2.0 Host
Card Reader      Micro-SD only
RAM      64 MB DDR2
Flash Memory  16 MB
SRAM   2.5 KB
EEPROM              1 KB
Clock Speed       16 MHz
PoE compatible 802.3af card support     See Power

El proyecto era montar un robot, con gripper, cámara con pant tilt, seguidor de línea, sensor de distancia,  servomotores y un led. Como ya os he dicho sería muy semejante a robots anteriores pero la diferencia de que la comunicación se emplearía wifi y la gracia que en linino se podrían hacer funcionar algunos módulos en python. Su destino era ir a USA para realizar un workshop, la verdad es que mucho más no sé, pero tenía que hacer dos unidades, ya como la Nasa (jajaja), por si uno fallaba.







Para hacer el setup del yun podéis seguir los pasos de Getting started, en Yun puede crear un punto de conexión, pero recomiendo configurarlo a una red la manera de configurarlo es conectarte al Arduino mediante wifi y abrir una página con la dirección 192.168.240.1 ahí os pedirá un password de acceso que por defecto es “arduino” sin las comillas. En el caso del Arduino mini yun es “doghunter” que encontrareis una mini guía aqui.

Una vez configurado el arduino a la red ya no es necesario que esté conectado al ordenador para cargarle el programa. Para que os aparezca en el Arduino ide, tenéis que cerrarlo u os pasara como a mí que no había forma que me apareciese, pues la nueva ip del Arduino no la sabes. Con el Arduino mini yun, hay otro tema este producto es de “la otra  compañía de Arduino” , así que tendréis que bajaros su ide propia.


Lo primero que descubres del yun es que las librerías wifi de Arduino no funcionan, resumiendo un poco el Arduino no tiene wifi lo tiene el Linux, así que se ha de emplear la librería Bridge.h para poder comunicar el Arduino con el linino y es el Linux el que podrá recibir y enviar mediante wifi.
Para la comunicación íbamos a emplear el protocolo UDP para ello haríamos un programa en python que corriera en el linino  que nos abriera un puerto en 192.168.0.102:2390 desde el cual recibiría instrucciones para testear el robot. Del mismo modo en el ordenador ejecutaríamos otra aplicación en python que recibiría los comandos del teclado y los enviaría por el puerto.


Módulo python en linino enlace


Para instalar el módulo de python en el linino, hay que conectarse mediante ssh a la ip 192.168.0.102 que es la ip que tiene asignada el yun en nuestro router. Desde Windows yo empleo el programa putty desde Linux aquí tenéis una guía de instalación. Una vez conectados no pedirá el password que le hemos puesto al Yun para poder acceder en modo root. Ahí editamos un documento nuevo.py y ponemos el código.






Luego hacemos chmod +x lininomybot.py para cambiar los permisos y lo ejecutamos mediante:
./ lininomybot.py
o
python  lininomybot.py


Módulo python Ordenador enlace


El modulo del ordenador abre una  ventana del terminal donde podéis  escribir en mayúsculas:
W --> Forward
S   --> Backward
A  --> Left
D  --> Right
X  --> Stop
Z  --> Led
C  --> Gripper
Q --> Cámara
E  --> Sensores

Este módulo genera el puerto comunicación mediante la instrucción:

 sock = socket.socket(socket.AF_INET, #Internet
                socket.SOCK_DGRAM) #UDP

Y envía las instrucciones  mediante:

 sock.sendto(msg, (UDP_IP,UDP_PORT))

Por otro lado el mensaje es recibido por el modulo del linino,  mediante la instrucción:

 sock.bind((UDP_IP,UDP_PORT)) y msg = sock.recv(1024).

Una vez   recibido el mensaje empleamos el bridge.put(“orden”,”msg”), importante es emplear el mismo nombre de variable en este caso “orden” en el módulo de python y en programa del Arduino.

Código Arduino enlace

En el código de Arduino está hecho con trozos de otros programas y básicamente sirve para testear que todo funciona correctamente. Como el control pant tilt de la pixy, que funciona buscando el color o los colores que le hemos entrenado. Ahora no lo hace pero cuando encuentre el objeto del color nos puede dar las coordenadas (x,y) y emplearlo para una aproximación. 

El led y el gripper funcionan en modo “conmutador” , cada vez que le enviamos la instrucción cambia de estado, abierto/cerrado, encendido/apagado.

La dirección está controlada por dos servomotores de 360º, no obtienen mucha velocidad pero sí bastante control.

La parte de los sensores no está terminada, en el ejemplo empleamos Console.print(“   ”); que envía la información al linino, no es necesario emplear la librería console.h, pero no envía la información a ordenador, parte de la idea de esta publicación es refrescarme un poco el tema y continuar.

Algún enlace de ayuda con ejemplos