HTC論壇

標題: 彎曲傳感手套(二) - Bluetooth [打印本頁]

作者: 8a462c04-a634-45b6-9343-1b6aa667657a    時間: 2018-10-6 01:10
標題: 彎曲傳感手套(二) - Bluetooth
【前言】
    藍牙(英語:Bluetooth),是無線通訊技術的一種標準,主要功用是讓兩台裝置在短距離之間作資料交換,這個技術是在1994年由電信商愛利信(Ericsson)發展出的技術,發展至今已經到了第五代,我們這次要整合的HC-05模組是採用英國劍橋的CSR (Cambridge Silicon Radio) 公司的BC417143晶片,支援藍牙2.1+EDR規範,可以讓"彎曲傳感手套"達到無線傳輸的功用,至於如何做呢?請看以下教學。。。
【準備材料】
【使用軟體】【程式下載】【電路圖】
【在電腦安裝藍牙接收器】
【Arduino】
#include <SoftwareSerial.h>

#define PIN_THUMB   A0    // 拇指.
#define PIN_POINTER A1    // 食指.
#define PIN_MIDDLE  A2    // 中指.
#define PIN_RING    A3    // 無名指.
#define PIN_LITTLE  A6    // 小指.

// HC-05.
SoftwareSerial BT(10, 11);

// 拇指數值.
int thumbValue = 0;
// 食指數值.
int pointerValue = 0;
// 中指數值.
int middleValue = 0;
// 無名指數值.
int ringValue = 0;
// 小指數值.
int littleValue = 0;

// 手套數值.
String InfinityStr = "";

// 時脈相關變數.
unsigned long timer = 0;
float timeTick = 0;

//-------------------------------------------------------
// 初始.
//-------------------------------------------------------
void setup() {   
  BT.begin(9600);  
}

//-------------------------------------------------------
// 主迴圈.
//-------------------------------------------------------
void loop() {  
  // 紀錄時脈.      
  timer = millis();
  
  //-------------------------------------------------------
  // 手指彎曲數值.  
  //-------------------------------------------------------
  // 拇指數值.
  thumbValue = analogRead(PIN_THUMB);
  // 食指數值.
  pointerValue = analogRead(PIN_POINTER);
  // 中指數值.
  middleValue = analogRead(PIN_MIDDLE);
  // 無名指數值.
  ringValue = analogRead(PIN_RING);
  // 小指數值.
  littleValue = analogRead(PIN_LITTLE);

  // 判斷時脈傳送.
  if(timeTick > 25){
    // 組合手指數值指令.
    InfinityStr = "inf|" + (String)thumbValue + "," + (String)pointerValue + "," + (String)middleValue + "," + (String)ringValue + "," + (String)littleValue + "|~";   
    // 將組合字串透過HC-05傳送給PC.
    BT.println(InfinityStr);
    // 初始時脈.
    timeTick = 0;  
  }
  timeTick += millis()-timer;  
}


【Unity】
using UnityEngine;
using UnityEngine.UI;
using System;
using System.IO.Ports;
using LinkedList;
using System.Threading;

public class MainCamera : MonoBehaviour
{
    // 連線COM(請查看您電腦藍牙接收器使用的COM編號作修改).
    const string PORT_NAME = "COM7";

    public Image ImageHandR = null;
    public Sprite ImageHandOpenR = null;
    public Sprite ImageHandCloseR = null;

    public Text ThumbText = null;                   // 拇指數值.
    public Text PointerText = null;                 // 食指數值.
    public Text MiddleText = null;                  // 中指數值
    public Text RingText = null;                    // 無名指數值.
    public Text LittleText = null;                  // 小指數值.

    public Slider SliderThumb = null;               // 拇指拉霸.
    public Slider SliderPointer = null;             // 食指拉霸.
    public Slider SliderMiddle = null;              // 中指拉霸.
    public Slider SliderRing = null;                // 無名指拉霸.
    public Slider SliderLittle = null;              // 小指拉霸.

    public InputField ThumbInputMax = null;         // 拇指張手最大值.
    public InputField PointerInputMax = null;       // 食指張手最大值.
    public InputField MiddleInputMax = null;        // 中指張手最大值.
    public InputField RingInputMax = null;          // 無名指張手最大值.
    public InputField LittleInputMax = null;        // 小指張手最大值.

    public InputField ThumbInputNow = null;         // 拇指數值.
    public InputField PointerInputNow = null;       // 食指數值.
    public InputField MiddleInputNow = null;        // 中指數值.
    public InputField RingInputNow = null;          // 無名指數值.
    public InputField LittleInputNow = null;        // 小指張數值.

    public InputField ThumbInputMin = null;         // 拇指張手最小值.
    public InputField PointerInputMin = null;       // 食指張手最小值.
    public InputField MiddleInputMin = null;        // 中指張手最小值.
    public InputField RingInputMin = null;          // 無名指張手最小值.
    public InputField LittleInputMin = null;        // 小指張手最小值.

    //------------------------------------------------------------------------
    // 手套相關,
    //------------------------------------------------------------------------
    private float thumbMax = 0;                     // 拇指最大數值.   
    private float pointerMax = 0;                   // 食指最大數值.   
    private float middleMax = 0;                    // 中指最大數值   
    private float ringMax = 0;                      // 無名指最大數值.   
    private float littleMax = 0;                    // 小指最大數值.

    private float thumbMin = 0;                     // 拇指最小數值.   
    private float pointerMin = 0;                   // 食指最小數值.   
    private float middleMin = 0;                    // 中指最小數值   
    private float ringMin = 0;                      // 無名指最小數值.   
    private float littleMin = 0;                    // 小指最小數值.

    private float thumbTransform = 0;               // 拇指轉換數值.   
    private float pointerTransform = 0;             // 食指轉換數值.   
    private float middleTransform = 0;              // 中指轉換數值   
    private float ringTransform = 0;                // 無名指轉換數值.   
    private float littleTransform = 0;              // 小指轉換數值.

    private float thumbShock = 0;                   // 拇指防震動.
    private float pointerShock = 0;                 // 食指防震動.
    private float middleShock = 0;                  // 中指防震動.
    private float ringShock = 0;                    // 無名指防震動.
    private float littleShock = 0;                  // 小指防震動.

    // 手指數據平均值物件.
    private BalancedValue thumbBalancedValue;
    private BalancedValue pointerBalancedValue;
    private BalancedValue middleBalancedValue;
    private BalancedValue ringBalancedValue;
    private BalancedValue littleBalancedValue;

    // 不全指令備份.
    private string receivedDataTemp = "";
    // 指令串列.
    private SinglyLinkedList<string> commandLinkedList = null;

    // 多執行緒.
    private Thread thread;
    private bool isRun = true;

    // 藍牙.
    private SerialPort sp;

    //------------------------------------------------------------------------
    // 初始.
    //------------------------------------------------------------------------
    void Start()
    {
        // 建立指令串列.
        commandLinkedList = new SinglyLinkedList<string>();

        // 藍牙.
        try
        {
            sp = new SerialPort("\\\\.\\" + PORT_NAME, 9600);
            sp.Open();
            sp.ReadTimeout = 50;
        }
        catch (System.IO.IOException e) { }
        catch (System.InvalidOperationException e) { }

        // 建立多執行緒接收指令.
        thread = new Thread(getReceivedData);
        thread.IsBackground = true;
        thread.Start();

        // 建立手指數據平均值物件.
        thumbBalancedValue = new BalancedValue(8);
        pointerBalancedValue = new BalancedValue(8);
        middleBalancedValue = new BalancedValue(8);
        ringBalancedValue = new BalancedValue(8);
        littleBalancedValue = new BalancedValue(8);
    }

    //------------------------------------------------------------------------
    // 更新.
    //------------------------------------------------------------------------
    void Update()
    {
        // 未與藍芽連線.
        if (sp == null || !sp.IsOpen) { return; }

        try
        {
            // 最大值.
            thumbMax = float.Parse(ThumbInputMax.text);            // 拇指最大數值.
            pointerMax = float.Parse(PointerInputMax.text);        // 食指最大數值.
            middleMax = float.Parse(MiddleInputMax.text);          // 中指最大數值.
            ringMax = float.Parse(RingInputMax.text);              // 無名指最大數值.
            littleMax = float.Parse(LittleInputMax.text);          // 小指最大數值.

            // 現值.
            ThumbInputNow.text = thumbBalancedValue.Value.ToString();
            PointerInputNow.text = pointerBalancedValue.Value.ToString();
            MiddleInputNow.text = middleBalancedValue.Value.ToString();
            RingInputNow.text = ringBalancedValue.Value.ToString();
            LittleInputNow.text = littleBalancedValue.Value.ToString();

            // 最小值.
            thumbMin = float.Parse(ThumbInputMin.text);             // 拇指最小數值.
            pointerMin = float.Parse(PointerInputMin.text);         // 食指最小數值.   
            middleMin = float.Parse(MiddleInputMin.text);           // 中指最小.   
            ringMin = float.Parse(RingInputMin.text);               // 無名指最小.   
            littleMin = float.Parse(LittleInputMin.text);           // 小指最小.

            // 接收數值.            
            string value = getCommand();
            if (value != "")
            {
                // 解指令.
                string[] decoding = null;
                decoding = value.Split('|');

                // 判斷指令.
                switch (decoding[0])
                {
                    case "inf":      // 判斷手套指令.
                        // 解字串.
                        string[] infDecoding = null;
                        infDecoding = decoding[1].Split(',');

                        //----------------------------------------
                        // 拇指數.
                        //----------------------------------------
                        // 存入手套取的數值以計算平均值.
                        thumbBalancedValue.Add(float.Parse(infDecoding[0]));
                        // 避免手指抖動(將上一次與最新的平均值相減後取絕對值).
                        if (Math.Abs(thumbBalancedValue.Value - thumbShock) > 4)
                        {
                            // 取得平均值.
                            thumbShock = thumbBalancedValue.Value;
                            // 最大.
                            if (thumbShock > thumbMax)
                            {
                                thumbTransform = 100;
                            }
                            // 最小.
                            else if (thumbShock < thumbMin)
                            {
                                thumbTransform = 0;
                            }
                            // 中間.
                            else
                            {
                                // 換算顯示數值(0~10).
                                thumbTransform = Mathf.Round((100 / (thumbMax - thumbMin)) * (thumbShock - thumbMin));
                            }
                            // 顯示拉條位置.
                            SliderThumb.value = thumbTransform * 0.01f;
                            // 顯示數值.
                            ThumbText.text = thumbTransform.ToString();
                        }

                        //----------------------------------------
                        // 食指數值.
                        //----------------------------------------
                        // 存入手套取的數值以計算平均值.
                        pointerBalancedValue.Add(float.Parse(infDecoding[1]));
                        // 避免手指抖動(將上一次與最新的平均值相減後取絕對值).
                        if (Math.Abs(pointerBalancedValue.Value - pointerShock) > 4)
                        {
                            // 取得平均值.
                            pointerShock = pointerBalancedValue.Value;
                            // 最大.
                            if (pointerShock > pointerMax)
                            {
                                pointerTransform = 100;
                            }
                            // 最小.
                            else if (pointerShock < pointerMin)

                            {
                                pointerTransform = 0;
                            }
                            // 中間.
                            else
                            {
                                // 換算顯示數值(0~10).
                                pointerTransform = Mathf.Round((100 / (pointerMax - pointerMin)) * (pointerShock - pointerMin));
                            }
                            // 顯示拉條位置.
                            SliderPointer.value = pointerTransform * 0.01f;
                            // 顯示數值.
                            PointerText.text = pointerTransform.ToString();
                        }

                        //----------------------------------------
                        // 中指數值.
                        //----------------------------------------
                        // 存入手套取的數值以計算平均值.
                        middleBalancedValue.Add(float.Parse(infDecoding[2]));
                        // 避免手指抖動(將上一次與最新的平均值相減後取絕對值).
                        if (Math.Abs(middleBalancedValue.Value - middleShock) > 4)
                        {
                            // 取得平均值.
                            middleShock = middleBalancedValue.Value;
                            // 最大.
                            if (middleShock > middleMax)
                            {
                                middleTransform = 100;
                            }
                            // 最小.
                            else if (middleShock < middleMin)

                            {
                                middleTransform = 0;
                            }
                            // 中間.
                            else
                            {
                                // 換算顯示數值(0~10).
                                middleTransform = Mathf.Round((100 / (middleMax - middleMin)) * (middleShock - middleMin));
                            }
                            // 顯示拉條位置.
                            SliderMiddle.value = middleTransform * 0.01f;
                            // 顯示數值.
                            MiddleText.text = middleTransform.ToString();
                        }

                        //----------------------------------------
                        // 無名指數值.
                        //----------------------------------------
                        // 存入手套取的數值以計算平均值.
                        ringBalancedValue.Add(float.Parse(infDecoding[3]));
                        // 避免手指抖動(將上一次與最新的平均值相減後取絕對值).
                        if (Math.Abs(ringBalancedValue.Value - ringShock) > 4)
                        {
                            // 取得平均值.
                            ringShock = ringBalancedValue.Value;
                            // 最大.
                            if (ringShock > ringMax)
                            {
                                ringTransform = 100;
                            }
                            // 最小.
                            else if (ringShock < ringMin)

                            {
                                ringTransform = 0;
                            }
                            // 中間.
                            else
                            {
                                // 換算顯示數值(0~10).
                                ringTransform = Mathf.Round((100 / (ringMax - ringMin)) * (ringShock - ringMin));
                            }
                            // 顯示拉條位置.
                            SliderRing.value = ringTransform * 0.01f;
                            // 顯示數值.
                            RingText.text = ringTransform.ToString();
                        }

                        //----------------------------------------
                        // 小指數值.
                        //----------------------------------------
                        // 存入手套取的數值以計算平均值.
                        littleBalancedValue.Add(float.Parse(infDecoding[4]));
                        // 避免手指抖動(將上一次與最新的平均值相減後取絕對值).
                        if (Math.Abs(littleBalancedValue.Value - littleShock) > 4)
                        {
                            // 取得平均值.
                            littleShock = littleBalancedValue.Value;
                            // 最大.
                            if (littleShock > littleMax)
                            {
                                littleTransform = 100;
                            }
                            // 最小.
                            else if (littleShock < littleMin)

                            {
                                littleTransform = 0;
                            }
                            // 中間.
                            else
                            {
                                // 換算顯示數值(0~10).
                                littleTransform = Mathf.Round((100 / (littleMax - littleMin)) * (littleShock - littleMin));
                            }
                            // 顯示拉條位置.
                            SliderLittle.value = littleTransform * 0.01f;
                            LittleText.text = littleTransform.ToString();
                        }
                        break;
                }
            }
        }
        catch (TimeoutException)
        {}
    }

    //------------------------------------------------------------------------
    // 離開程式.
    //------------------------------------------------------------------------
    void OnApplicationQuit()
    {
        isRun = false;
        thread = null;
        if (sp != null && sp.IsOpen)
            sp.Close();
    }

    //------------------------------------------------------------------------
    // 取得HC-05傳送的指令.
    //------------------------------------------------------------------------
    public String getCommand()
    {
        string s = "";

        s = "";
        if (commandLinkedList.Count > 0)
        {
            s = commandLinkedList.First.Value;
            commandLinkedList.RemoveFirst();
            return s;
        }
        return "";
    }

    //------------------------------------------------------------------------
    // 接收指令.
    //------------------------------------------------------------------------
    private string receivedData = "";
    public void getReceivedData()
    {
        while (isRun)
        {
            try
            {
                string s = "";
                int len = 0;

                // 組合字串.
                receivedData += sp.ReadLine();
                // 分解字串.
                len = receivedData.Length;
                for (int i = 0; i < len; i++)
                {
                    // 組合指令.
                    if (receivedData != '~')
                    {
                        s += receivedData;
                    }
                    else
                    {
                        // 加入指令.
                        commandLinkedList.AddLast(s);
                        s = "";
                    }
                }
                receivedData = s;
            }
            catch (System.InvalidOperationException e) { }
            catch (System.TimeoutException e) { }
        }
    }
}

【關於外接電源】
【後記】
下一篇教學我們將繼續在Unity內整合3D手掌模型與新增硬體六軸傳感器並對這次很亂的程式碼作優化(其實善用陣列可以讓程式碼少一半),敬請期待。



作者: 7aa3d266-cced-4b0d-988c-dfb5c6f95bc1    時間: 2018-10-7 20:39
哇塞~神人啊~這東西也能搞出來,太強~~~期待作品!

作者: 84ffa6b3-2990-40ed-92a6-96dcf5e9f711    時間: 2018-10-23 10:39
我一直以為動作訊號要從~~定位器的usb傳進去~~想不到是另外一個藍芽輸出,哈~我真的想太多了

如果要另外藍芽傳輸動作~那軟體有支援才可以用~而不是任何遊戲都可以用~~嗯~~~~

這就是為何omni 需要 針對他開發的遊戲才可玩的原因~~

思考思考~~~~~樓主真強~~~


作者: 2bd19204-0082-412e-9f71-55f2345c212c    時間: 2021-3-18 12:13
請問版大,能不能推薦一下您所使用的藍牙接收器

我最近在找可以接收到Arduino藍牙模組的接受器,找不太到QQ



作者: ce213851-0261-44d4-a79c-203ceec7f668    時間: 2024-1-10 19:59
Nice to read your article! I am looking forward to sharing your adventures and experiences.seo оптимизация

作者: bc212577-5840-4f8f-b47f-e300183ec595    時間: 2024-1-11 17:48
You have a real ability for writing unique content. I like how you think and the way you represent your views in this article. I agree with your way of thinking. Thank you for sharing.UFABETแจกเครดิตฟรี

作者: ce213851-0261-44d4-a79c-203ceec7f668    時間: 2024-1-20 21:58
I did take pleasure in studying posts published on this web site. They may be remarkable and contains plenty of beneficial details.naughty panties adult humor

作者: ce213851-0261-44d4-a79c-203ceec7f668    時間: 2024-1-21 20:13
This particular papers fabulous, and My spouse and i enjoy each of the perform that you have placed into this. I’m sure that you will be making a really useful place. I has been additionally pleased. Good perform!Singles events in Barcelona

作者: ce213851-0261-44d4-a79c-203ceec7f668    時間: 2024-1-23 17:27
Thank you because you have been willing to share information with us. we will always appreciate all you have done here because I know you are very concerned with our.Dream Dictionary

作者: bc212577-5840-4f8f-b47f-e300183ec595    時間: 2024-1-23 20:25
Thank you for some other informative website. The place else may just I get that kind of information written in such a perfect method? I have a venture that I am simply now running on, and I've been at the glance out for such info .UFABETเข้าเว็บแทงบอลอย่างไร

作者: ce213851-0261-44d4-a79c-203ceec7f668    時間: 2024-4-14 21:34
Ready for a change? Breast augmentation can enhance your silhouette with customized options to suit your preferences and anatomy.Impianti al seno





歡迎光臨 HTC論壇 (https://community.htc.com/tw/) Powered by Discuz! X3.1