Categories

Enterprise technology |
Software and apps |
Tech, other |

Cómo crear un extensor de rango Ble-wifi para T-skin

Introducción
¡Hola! Este proyecto está diseñado para ilustrar la ampliación de las capacidades de comunicación de Tactigon Skin (T-Skin). Queremos poder usarlo en distancias más largas que las que ofrece Bluetooth Low Energy. Planeamos transmitir datos desde el T-Skin a una computadora personal a través de WiFi con una Raspberry Pi Zero W que emplea el protocolo UDP. Se utilizó un NodeMCU con un módulo BLE para proporcionar al Tactigon Skin la capacidad de transmitir mediante WiFi.

Arquitectura de hardware
Utilizamos mucho hardware en este proyecto, por lo que pensamos que una imagen puede ayudar a mantener las cosas en su mente.
9YLmDntZaATRNmCvPeXv9TbRYlItNlSdcRngLpYa.jpegEl Tactigon Skin no admite de forma nativa la comunicación WiFi. Superamos esta limitación transmitiendo datos a la Raspberry Pi (RPi) con un NodeMCU con un módulo BLE. Utilizamos una red privada alojada por Raspberry. Esto elimina la necesidad de acceder a las redes públicas, ya que el RPi se utiliza como punto de acceso a la red.
Para aprender a configurar el RPi como punto de acceso, siga este enlace: Configure Access Point Mode

Arquitectura de software
Ahora que la infraestructura de nuestro proyecto está definida, veamos cómo funcionan los componentes.
BLE al nodo UDP
wNiI2o5ivNeQSJA45aqEXFuhOfrmZpEWI6hGzOkz.jpegBLE al nodo UDPEl T-Skin recopila datos de sus sensores, como sus acelerómetros y giroscopios. La información se utiliza para crear un paquete para su transmisión a NodeMCU a través de BLE.

T-Skin recopila los datos usando este código:
T_QUAT qMeter; T_QData qData; qData = qMeter.getQs(); roll = radToDegree(qData.roll - rollZero); pitch = radToDegree(qData.pitch - pitchZero); yaw = radToDegree(qData.yaw - yawZero);Primero, se definen las variables qMeter y qData. Estos objetos se crean utilizando las clases T_QUAT y T_QData. Estas variables contendrán los datos de los cuaterniones del giroscopio. La lectura de datos se realiza con la instrucción qMeter.getQs (). Luego usamos variables separadas para almacenar los valores de balanceo, cabeceo y guiñada.
Luego, el protocolo de comunicación UDP se usa para enviar los datos al Raspberry Pi Zero W.
WiFiUDP Udp; unsigned int UDP_Port = 5000; char* UDP_IP = "192.168.4.1"; char PacketBuffer[1024]; Udp.beginPacket(UDP_IP, UDP_Port); Udp.write(PacketBuffer); Udp.endPacket();Cuando se recibe un paquete de datos completo, se usa para crear un paquete UDP para la transmisión. Después de definir el puerto y la dirección IP del receptor, los datos se escriben en la línea y el procedimiento de envío de paquetes finaliza.
Hemos configurado NodeMCU para que intente volver a conectarse lo antes posible en caso de que falle la conexión.
UDP a VCOM
EJVxklhfyqpASFsIH7ltPDFkCtg63ka7PHhggKRI.jpegEn nuestro escenario, tenemos la Raspberry Pi configurada para ser un dispositivo Gadget. Esto permite que una PC lo identifique como un dispositivo de puerto serie para activar la comunicación. Puede aprender cómo configurar el dispositivo de la misma manera en este enlace: Serial Gadget Mode Configuration.
Una secuencia de comandos de Python que se ejecuta continuamente toma los paquetes UDP y los envía al puerto serie virtual de Raspberry Pi. Si ejecuta un programa de monitor de puerto serie para interrogar el puerto COM en la PC, puede ver la secuencia de datos recopilada por el Tactigon Skin.
Aquí hay un ejemplo de este sistema en acción:

Conclusiones
Consideramos que este proyecto fue un éxito ya que logramos nuestro objetivo y pudimos usar el Tactigon Skin en distancias considerablemente más largas. Mediante la transmisión WiFi, pudimos aumentar el rango de comunicación de tres o cuatro metros a más de 40 metros. Toda una diferencia!
Tendrá dos limitaciones principales cuando implemente la comunicación con este método. Primero está la interferencia potencial de otras señales que usan la banda de 2.4 GHz. El alcance máximo de la antena Wi-Fi Raspberry Pi Zero W es el segundo factor limitante. Se pueden usar protocolos LPWAN alternativos como LoRa y SigFox para minimizar los problemas de señal que pueden surgir.
Una vez que se han superado las limitaciones de distancia del T-Skin, el dispositivo se vuelve capaz de un nivel de funcionalidad mucho mayor. Se puede usar para controlar dispositivos de robot de forma remota para su uso en misiones de búsqueda y rescate posteriores al desastre o para ayudar a equipos de demolición sin arriesgar vidas humanas.

Código
RPi_Tactigon_Extender.py
Este código se ejecutará automáticamente al arrancar el Raspberry Pi Zero W.
  1.  #!/usr/bin/env python
  2. #UDP SERVER CODE
  3. #To run this script at boot you have te add the following to the /etc/rc.local file in sudo mode
  4. #sudo python /path_to_this_script/script_name.py
  5. #before the exit 0 directive
  6. import os
  7. import sys
  8. import socket
  9. import serial
  10. import time

  11. UDP_PORT = 5000
  12. UDP_IP = '192.168.4.1' #IP address of the machine to whick this script will run
  13. #small delay to ensure that everithing started up
  14. time.sleep(10)

  15. port = serial.Serial("/dev/ttyGS0", baudrate=115200, timeout=0.1)

  16. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  17. sock.bind((UDP_IP, UDP_PORT))

  18. while ((port.in_waiting) <= 0):
  19. data, addr = sock.recvfrom(1024) #store received data
  20. #print(data) #for debug
  21. port.write(data + '\n') #write received data from UDP to the emulated serial port
  22. #if the input buffer of the serial port is NOT empty that means that the shutdown command has been received from the PC
  23. os.system("shutdown now -h")

BLEtoUDP.ino
Este código se muestra en el NodeMCU.
  1.  //SENDER
  2. #include
  3. #include

  4. //UDP handle
  5. WiFiUDP Udp;
  6. unsigned int UDP_Port = 5000;
  7. char* UDP_IP = "192.168.4.1"; //IP address of the RECEIVER
  8. char PacketBuffer[1024]; //Buffer used for UDP data

  9. //Wifi handle
  10. const char* ssid = "YOUR_SSID";
  11. const char* password = "YOUR_PASSWORD";
  12. IPAddress ip(192, 168, 4, 2); //set a static IP for this device
  13. IPAddress gateway(192, 168, 4, 1);
  14. IPAddress subnet(255, 255, 255, 0);

  15. bool flag = false; //to handle complete read of BLE data
  16. int count = 0; //counter for checking correct length of received buffer from BLE, starts from 0 to 19

  17. void setup() {
  18.     memset(&PacketBuffer, (char)0, 1024); //set all buffer to 0
  19.     pinMode(LED_BUILTIN, OUTPUT); //LOW = WiFi connected; HIGH = WiFi not connected
  20.     digitalWrite(LED_BUILTIN, HIGH);
  21.     Serial.begin(115200); //BLE serial
  22.     WiFi.config(ip, gateway, subnet);
  23.     WiFi.mode(WIFI_STA); //station mode
  24.     WiFi.begin(ssid, password);
  25.     Serial.println();
  26.     Serial.print("Wait for WiFi");
  27.     //wait for wireless connection
  28.     while (WiFi.status() != WL_CONNECTED) {
  29.         delay(500);
  30.         Serial.print(".");
  31.     }
  32.     digitalWrite(LED_BUILTIN, LOW);
  33.     Serial.println("");
  34.     Serial.println("WiFi connected");
  35.     Serial.println("IP address: " + WiFi.localIP().toString());
  36.     Serial.println();
  37.     Udp.begin(UDP_Port); //initialize UDP
  38. }

  39. void loop() {
  40.     //if wireless connection drops, try to reconnect to it
  41.     while (WiFi.status() != WL_CONNECTED) {
  42.         digitalWrite(LED_BUILTIN, HIGH);
  43.         delay(500);
  44.         Serial.print(".");
  45.     }

  46.     digitalWrite(LED_BUILTIN, LOW);
  47.     if (Serial.available()) {
  48.         //read one char at the time and store it to che progressive 'count' position in the buffer array
  49.         Serial.read(&PacketBuffer[count], 1);
  50.         //checking for carriage return and line feed chars
  51.         //replace carriage return (if for any reasons is present) with whitespace to avoid complications in the buffer processing by the receiver
  52.         if (PacketBuffer[count] == '\r') PacketBuffer[count] = ' ';
  53.         else if (PacketBuffer[count] == '\n') {
  54.             //at this point the one buffer from BLE serial is completely processed
  55.             PacketBuffer[count] = (char)0;
  56.             count = 0; //reset counter
  57.             flag = true; //complete data
  58.         }
  59.         else {
  60.             //increment counter for next char read
  61.             count++;
  62.         }
  63.     }

  64.     if (flag) {
  65.         //start send data from [1] and not [0] due to how data is sent by the T-skin.
  66.         //the data in [0] is treated by a serial read as a terminator char (char)0.
  67.         //if this data ends up in the buffer that we send the calid data after that char will be ignored
  68.         //sending data from 2nd element is less time consuming that shifting all buffer
  69.         //Serial.println(&PacketBuffer[1]); //for debug
  70.         //here we send via UDP the data from BLE
  71.         Udp.beginPacket(UDP_IP, UDP_Port);
  72.         Udp.write(&PacketBuffer[1]);
  73.         Udp.endPacket();
  74.         flag = false; //reset flag for next buffer
  75.         memset(PacketBuffer, (char)0, 1024); //set all buffer to 0
  76.     }
  77. }

Oliver Rieder - oliverrieder8891@gmail.com