¡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.

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

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

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.
- #!/usr/bin/env python
- #UDP SERVER CODE
- #To run this script at boot you have te add the following to the /etc/rc.local file in sudo mode
- #sudo python /path_to_this_script/script_name.py
- #before the exit 0 directive
- import os
- import sys
- import socket
- import serial
- import time
- UDP_PORT = 5000
- UDP_IP = '192.168.4.1' #IP address of the machine to whick this script will run
- #small delay to ensure that everithing started up
- time.sleep(10)
- port = serial.Serial("/dev/ttyGS0", baudrate=115200, timeout=0.1)
- sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- sock.bind((UDP_IP, UDP_PORT))
- while ((port.in_waiting) <= 0):
- data, addr = sock.recvfrom(1024) #store received data
- #print(data) #for debug
- port.write(data + '\n') #write received data from UDP to the emulated serial port
- #if the input buffer of the serial port is NOT empty that means that the shutdown command has been received from the PC
- os.system("shutdown now -h")
BLEtoUDP.ino
Este código se muestra en el NodeMCU.
- //SENDER
- #include
- #include
- //UDP handle
- WiFiUDP Udp;
- unsigned int UDP_Port = 5000;
- char* UDP_IP = "192.168.4.1"; //IP address of the RECEIVER
- char PacketBuffer[1024]; //Buffer used for UDP data
- //Wifi handle
- const char* ssid = "YOUR_SSID";
- const char* password = "YOUR_PASSWORD";
- IPAddress ip(192, 168, 4, 2); //set a static IP for this device
- IPAddress gateway(192, 168, 4, 1);
- IPAddress subnet(255, 255, 255, 0);
- bool flag = false; //to handle complete read of BLE data
- int count = 0; //counter for checking correct length of received buffer from BLE, starts from 0 to 19
- void setup() {
- memset(&PacketBuffer, (char)0, 1024); //set all buffer to 0
- pinMode(LED_BUILTIN, OUTPUT); //LOW = WiFi connected; HIGH = WiFi not connected
- digitalWrite(LED_BUILTIN, HIGH);
- Serial.begin(115200); //BLE serial
- WiFi.config(ip, gateway, subnet);
- WiFi.mode(WIFI_STA); //station mode
- WiFi.begin(ssid, password);
- Serial.println();
- Serial.print("Wait for WiFi");
- //wait for wireless connection
- while (WiFi.status() != WL_CONNECTED) {
- delay(500);
- Serial.print(".");
- }
- digitalWrite(LED_BUILTIN, LOW);
- Serial.println("");
- Serial.println("WiFi connected");
- Serial.println("IP address: " + WiFi.localIP().toString());
- Serial.println();
- Udp.begin(UDP_Port); //initialize UDP
- }
- void loop() {
- //if wireless connection drops, try to reconnect to it
- while (WiFi.status() != WL_CONNECTED) {
- digitalWrite(LED_BUILTIN, HIGH);
- delay(500);
- Serial.print(".");
- }
- digitalWrite(LED_BUILTIN, LOW);
- if (Serial.available()) {
- //read one char at the time and store it to che progressive 'count' position in the buffer array
- Serial.read(&PacketBuffer[count], 1);
- //checking for carriage return and line feed chars
- //replace carriage return (if for any reasons is present) with whitespace to avoid complications in the buffer processing by the receiver
- if (PacketBuffer[count] == '\r') PacketBuffer[count] = ' ';
- else if (PacketBuffer[count] == '\n') {
- //at this point the one buffer from BLE serial is completely processed
- PacketBuffer[count] = (char)0;
- count = 0; //reset counter
- flag = true; //complete data
- }
- else {
- //increment counter for next char read
- count++;
- }
- }
- if (flag) {
- //start send data from [1] and not [0] due to how data is sent by the T-skin.
- //the data in [0] is treated by a serial read as a terminator char (char)0.
- //if this data ends up in the buffer that we send the calid data after that char will be ignored
- //sending data from 2nd element is less time consuming that shifting all buffer
- //Serial.println(&PacketBuffer[1]); //for debug
- //here we send via UDP the data from BLE
- Udp.beginPacket(UDP_IP, UDP_Port);
- Udp.write(&PacketBuffer[1]);
- Udp.endPacket();
- flag = false; //reset flag for next buffer
- memset(PacketBuffer, (char)0, 1024); //set all buffer to 0
- }
- }