TスキンのBle-wifiレンジエクステンダーの作成方法

前書き
こんにちは!このプロジェクトは、Tactigon Skin(T-Skin)の通信機能の拡張を説明するために設計されています。 Bluetooth Low Energyが提供する距離よりも長い距離で使用できるようにしたいと考えています。 UDPプロトコルを使用するRaspberry Pi Zero Wを使用して、WiFi経由でT-Skinからパーソナルコンピューターにデータを送信する予定です。 BLEモジュールを備えたNodeMCUを使用して、TactigonスキンにWiFiを使用して送信する能力を提供しました。
 
ハードウェアアーキテクチャ
私たちはこのプロジェクトで多くのハードウェアを使用しているため、写真が物事をまっすぐに保つのに役立つと考えました。

f:id:OliverSpace:20190807223553j:plain

Tactigonスキンは、Wi-Fi通信をネイティブでサポートしていません。 BLEモジュールを備えたNodeMCUを使用してRaspberry Pi(RPi)にデータを送信することで、この制限を回避しました。 Raspberryがホストするプライベートネットワークを使用します。これにより、RPiがネットワークアクセスポイントとして使用されるため、パブリックネットワークにアクセスする必要がなくなります。
 
アクセスポイントとしてRPiを構成する方法については、次のリンクを参照してください。Configure Access Point Mode

ソフトウェアアーキテクチャ
プロジェクトのインフラストラクチャが定義されたので、コンポーネントの動作を見てみましょう。
BLEからUDPノード

f:id:OliverSpace:20190807223526j:plain

BLEからUDPノード
T-Skinは、加速度計やジャイロスコープなどのセンサーからデータを収集します。この情報は、BLEを介してNodeMCUに送信するためのパケットを作成するために使用されます。
 
T-Skinは次のコードを使用してデータを収集します。

T_QUAT qMeter;
T_QData qData;
  
qData = qMeter.getQs();
roll = radToDegree(qData.roll - rollZero);
pitch = radToDegree(qData.pitch - pitchZero);
yaw = radToDegree(qData.yaw - yawZero);

 
最初に、qMeterおよびqData変数が定義されます。これらのオブジェクトは、T_QUATおよびT_QDataクラスを使用して構築されます。これらの変数は、ジャイロスコープからのクォータニオンデータを保持します。読み取られたデータは、qMeter.getQs()ステートメントで実行されます。次に、個別の変数を使用して、ロール、ピッチ、ヨーの値を保存します。
次に、UDP通信プロトコルを使用して、データを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();

 
完全なデータパケットを受信すると、送信用のUDPパケットの作成に使用されます。受信者のポートとIPアドレスを定義すると、データが回線に書き込まれ、パケット送信手順が終了します。
接続が失敗した場合にできるだけ早く再接続を試みるようにNodeMCUを構成しました。
UDPからVCOM

f:id:OliverSpace:20190807223544j:plain

このシナリオでは、Raspberry Piをガジェットデバイスとして構成しました。これにより、PCはそれをシリアルポートデバイスとして識別し、通信を確立できます。このリンク:Serial Gadget Mode Configurationで同じ方法でデバイスを構成する方法を学ぶことができます。
継続的に実行されているPythonスクリプトは、UDPパケットを取得し、Raspberry PiのVirtual Serial Portに送信します。シリアルポートモニタープログラムを実行してPCのCOMポートに問い合わせると、Tactigon Skinによって収集されたデータストリームを確認できます。
このシステムの実際の例を次に示します。
結論
目標を達成し、かなり長い距離にわたってTactigonスキンを使用することができたため、このプロジェクトは成功したと考えています。 WiFi伝送を使用して、通信範囲を3メートルまたは4メートルから40メートル以上に増やすことができました。かなり違います!
この方法で通信を実装する場合、2つの主要な制限に直面します。 1つ目は、2.4 GHz帯域を使用した他の信号からの潜在的な干渉です。 Raspberry Pi Zero W Wi-Fiアンテナの最大範囲は、2番目の制限要因です。 LoRaやSigFoxなどの代替LPWANプロトコルを使用して、発生する可能性のある信号の問題を最小限に抑えることができます。
T-Skinの距離制限が解消されると、デバイスの機能レベルが大幅に向上します。ロボットデバイスをリモートで制御して、災害後の捜索および救助ミッションで使用したり、人命を危険にさらすことなく解体チームを支援したりできます。


コード
 
RPi_Tactigon_Extender.py
このコードは、起動時に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*1

while *2; 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

}

}

 

 

*1:UDP_IP, UDP_PORT

*2: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
このコードはNodeMCUにフラッシュされます。

//SENDER

#include <ESP8266WiFi.h>

#include <WiFiUdp.h>
//UDP handle

WiFiUDP Udp;

unsigned int UDP_Port = 5000;

char* UDP_IP = "192.168.4.1"; //IP address of the RECEIVERchar 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 deviceIPAddress gateway(192, 168, 4, 1);

IPAddress subnet(255, 255, 255, 0);
bool flag = false; //to handle complete read of BLE dataint 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(