2017年5月21日 星期日

第五章:MCU + Lora 與 Android室內平面顯示

前面說過,我們的架構是這樣


代表著MCU掌握了所有溝通技能,一步錯就會導致系統崩潰

PM腦說:哦哦哦! 那不就跟我的角色一樣嗎?

RD腦皺著眉回覆:這有難回答,我先去抽根菸

PM腦接著說:醒醒吧! 你根本不會抽菸,還想多出這一句拖稿! 你是不是不把我奔雷手文泰放在眼裡!?

RD腦指著PM腦說:你
查了幾家做Lora的廠商,台灣有正文、群登和iFROG LAB,國外有Pycom出的LoPy

而群登是Cortex M4 + Lora,這我從來沒試過這麼清新脫俗的感覺,就猶如強大MCU的鮮、與Lora遠距離的甜,混在一起的味道竟然比AlphaGo橫空出世有過之而無不及

又這麼剛好有2UART可以搭上WiFiBLE
也難怪PM腦會說:爭什麼呀! 摻在一起做不只是智能燈座功能的智能燈座啊!笨蛋!

PM腦平常都在看市場,很注重客戶的使用狀態回覆,也難怪他程式學不好,我錯怪他了 RD腦心想著

我們使用AcsipAI127x來做主系統,SDK可以看github上面的
https://github.com/AcSiP/AI127x_SDK

這裡就不寫貼整程式碼了,只寫概念,設計流程如下
1、UART1WiFi使用,UART2BLE使用
2、UART1UART2要在最後一個byte]才會進判斷式
3、進入判斷式後,用\做分割,所以進來的數據會長這樣
UART1 RXà\Smart phoneMQTT傳來的訊息判斷\]
UART1 TXà\上傳BLE的訊號到Cloud\]

UART2 RXà\自身的UD\掃瞄到的BLE ID\BLE IDRSSI\RSSI轉為距離\]
UART2 TXà\GO\]  - 這是告訴BLE可以開始掃瞄
4、main()內寫UART1UART2的流程

所以先著手UART設計
void USART2_IRQHandler(void) {
       
        if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) {
               
                /* Read one byte from the receive data register */
                uartReceBuffer[uartReceIndex] = USART_ReceiveData(USART2);
                /* Clear the USART2 Receive interrupt */
                USART_ClearITPendingBit(USART2, USART_IT_RXNE);
               
    
     uartReceIndex++;
     if(uartReceIndex >= 50 )  //buffer只有50
       uartReceIndex =0;

       uartReceCount++;  //count+1
  }
       
}

再新增一個readRxBuffer
void readRxBuffer(void)
{

   if(uartReceCount == 0)
    return;
   uartReceCount--; //???ring????byte
             
         myData[myDataCount] = uartReceBuffer[uartReadIndex];
   uartReadIndex++;
   if(uartReadIndex >= 50)
      uartReadIndex = 0;

  

         if(myData[myDataCount] ==  0x5D) //只要最後一個為]就進判斷式
    {
                       
                        myData[myDataCount] = NULL; //]設為空值
                        myDataCount = 0;
     uartReceComplete = 1;
j=0;
     return;
    }
               
if(myData[myDataCount]== 0x5c) //只要是反斜線就分割
         {
          split[j] = main_count;
                 
                 j++;
                 return;
         }
                myDataCount++;
  main_count++;
}

Uart架構完成後,回到main()內做設定

    while(1)
        {
                readRxBuffer_uart1(); //wifi的輸入
if(uartReceComplete_uart1 == 1)
 {
  uartReceComplete_uart1 = 0;


         if(myData_uart1[0] == 'G' && myData_uart1[1] ==  'O') //如果傳來的是GO,就叫BLE開始Scan
         {
       
                 WiFi_RX_Flag = 1;
                 wifi_coming_flag=1;

                main_count_uart1=0;
USART2_UartWrite((uint8_t *)"AT+GO", strlen("AT+GO")); //AT+GOBLE

         USB_OTG_BSP_mDelay (3000);
 }
         
 if(wifi_coming_flag==1)//WIFI的回傳,也就是ESP8266subscribe
 {
//還沒開始寫 但預計這裡是手機控制燈座的狀態
//例如開關燈、保全功能的設徹防以及配件如人體感應器或門磁的狀態
//MQTT的架構應該是這樣 à UID/Bulb/Status UID/Accessory/Status
}


}
                readRxBuffer(); //BLE的輸入

                 if(uartReceComplete == 1)
 {
  uartReceComplete = 0;

         if(CentralID_flag==1)//取得BLE的唯一碼當燈座的唯一碼
         {
                 CentralID_flag=0;
                 USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                 for(i=split[0];i<=split[1]-1;i++)
                 {
            CentralID[i] = myData[i];
                sprintf(str1, "%c", CentralID[i]);
          USART1_UartWrite((char *)str1, strlen((char *)str1));
           }
                 USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                 main_count=0;
         }
         
         else{
                 
                if( WiFi_RX_Flag == 1)//BLE掃瞄到的資料全往上傳
                {
                        USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                        for(i=split[0];i<=split[1]-1;i++)
                        {
                         sprintf(str1, "%c", myData[i]);
                USART1_UartWrite((char *)str1, strlen((char *)str1));
                        }
                        USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                        for(i=split[1];i<=split[2]-1;i++)
                        {
                         sprintf(str1, "%c", myData[i]);
                USART1_UartWrite((char *)str1, strlen((char *)str1));
                        }
                        USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                        for(i=split[2];i<=split[3]-1;i++)
                        {
                         sprintf(str1, "%c", myData[i]);
                USART1_UartWrite((char *)str1, strlen((char *)str1));
                        }

                //由於RSSIUART收進來是ASCII,要轉為數字
                        char string1[5];
                        int l=0;
                       
                        for(i=split[2];i<=split[3]-1;i++)
                        {
                                string1[l] = myData[i];// 把收到的放到string1
                                l++;
                        }
                       
                        float bb = atoi ( string1 ); // string1轉為數字
               
                        float cc = calculateAccuracy( -54.0, bb ); //計算有多少米距離
                        USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                        sprintf(str1, "%.5f", cc); //取到小數點第五位
                USART1_UartWrite((float *)str1, strlen((float *)str1));
                       
                        USART1_UartWrite((uint8_t *)"\r\n", strlen("\r\n"));

                                USB_OTG_BSP_mDelay (50);
                        main_count_uart1=0;
                        main_count=0;
                }
        }
 }
}

再加下計算距離的公式
float calculateAccuracy( float txPower, float rssi )
{
       if (rssi == 0) {
           return -2.0; // if we cannot determine accuracy, return -1.
       }
       float ratio = rssi * 1.0 / txPower;
       if (ratio < 1.0) {
           return pow(ratio, 10);
       } else {
           float accuracy = (0.42093) * pow(ratio, 6.9476) + 0.54992;
           return accuracy;
       }
}

如此就完成MCU的寫法了

簡單說明一下,calculateAccuracy裡頭有個txPower
這裡我們在mian()是寫-54Beacon為了計算距離,會有個txPower
指的是一公尺的RSSI值,而Beacon會帶這個值
但我們用的是小米及Fitbit這種常見的產品,它不是Beacon

經測試後,通常會在-54-64之間,而在這個EVT Stage,我們選擇的是-54
但將來做成產品會讓使用者去校正
既然得到了手環與燈座的距離,就由Android去顯示三角定位的圖示
所以雛形大約長這樣,藍點代表定位的那一位,燈泡圖形就代表智能燈座

PM腦說:零分!!!

RD腦回:為什麼??? 我的禾花雀平面圖,設定可說是百中挑一的三角定位演算法,無論速度、解析度,都是一等一的

PM腦說:我指得不是演算法,我指的是禾花雀平面圖設計的太醜了!!!

RD腦回:設計醜?喂! 醜不醜關專案什麼事啊?

PM腦說:有事!挑男女朋友都挑長的帥或漂亮順眼的,使用者在下載APP也選美觀大方的,誰還有胃口來選你這個醜不拉嘰的APP呢?所以我看了一眼就把早餐吐出來了!

RD腦只好先把Android交代完畢,之後再想要怎麼修改這平面圖的設計

RD腦說:ㄜ其實我看了幾篇文章才寫出來的Code,但時間有點久,貼這2篇文章也許能代表我想說什麼

https://www.ece.lsu.edu/scalzo/FDR_Final.pdf page 11頁的Trilateration in 2D

Android的寫法如下:
import java.math.BigDecimal;
import java.text.DecimalFormat;

import android.app.Activity;

import android.graphics.Point;
import android.os.Bundle;

import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class IndoorLocation extends Activity{

         
          private SurfaceView sfv;
          private SurfaceHolder sfh;
         
          private Point greenXY = new Point(0, -13);                                               
          private Point blueXY = new Point(-8, 13);
          private Point purpleXY = new Point(8, 13);
          public static Point[] beacons;
          private int beaconsNum =3 ;                                                                     
          static double locationX = 0;
          static double locationY = 0;
         
          private void init(){
                  beacons = new Point[beaconsNum];
                  beacons[0] = greenXY;
                  beacons[1] = blueXY;
                  beacons[2] = purpleXY;
                 

          }
         
          @Override
          protected void onCreate(Bundle savedInstanceState) {
                  init();
                  super.onCreate(savedInstanceState);
                  setContentView(R.layout.indoorsurfaceview);

                  sfv = (SurfaceView) findViewById(R.id.surfaceView1);
                  sfh = sfv.getHolder();
                  sfh.addCallback(new MySfvCallback(IndoorLocation.this));


                 
                  double xypoint[] = getMeetingPoints(4.0, 4.0, 9.0, 7.0, 9.0, 1.0, 0.0025, 1.42, 6.96);
                         
                  locationX = xypoint[0];
                  locationY = -xypoint[1];//因為不知道為什麼y的相限剛好是相反的

          }


          double norm (double x,double y) // get the norm of a vector
          {
              return (float) Math.pow(Math.pow(x,2)+Math.pow(y,2),0.5);
          }
         
          private double[] getMeetingPoints(double x1, double y1, double x2, double y2, double x3, double y3, double r1, double r2, double r3)
          {

                  double p2p1Distance = (Math.pow(Math.pow(x2-x1,2) + Math.pow(y2-y1,2),0.5));

                 
                  double ex[] = {(x2-x1)/p2p1Distance, (y2-y1)/p2p1Distance};

                  double aux[] = {x3-x1,y3-y1}; //correct
             
              //signed magnitude of the x component
                  double i = ex[0] * aux[0] + ex[1] * aux[1]; //correct

               
                  double aux2[] = { x3-x1-i*ex[0], y3-y1-i*ex[1]};//跑了一點
                 
                    DecimalFormat df = new DecimalFormat("##.000");//四捨五入小數點第三位
                    double d1 = Double.parseDouble(df.format(aux2[0]));
                    double d2 = Double.parseDouble(df.format(aux2[1]));
                   
                        double ey[] = { d1 / norm (d1,d2), d2 / norm (d1,d2) };
                    DecimalFormat df1 = new DecimalFormat("##.000");//四捨五入小數點第三位
                    double d11 = Double.parseDouble(df1.format(ey[0]));
                    double d21 = Double.parseDouble(df1.format(ey[1]));
                         
                    double j = d11 * d1 + d21 * d2;
                    DecimalFormat jj = new DecimalFormat("##.000");//四捨五入小數點第三位
                    double jj1 = Double.parseDouble(jj.format(j));

              //coordinates
                  //double x = ((Math.pow(r1,2) - Math.pow(r2,2) + Math.pow(p2p1Distance,2))/ (2 * p2p1Distance));
              //double y = ((Math.pow(r1,2) - Math.pow(r3,2) + Math.pow(i,2) + Math.pow(j,2))/(2*j) - i*x/j);
                  double x = ((Math.pow(r1,2) - Math.pow(r2,2) + Math.pow(p2p1Distance,2))/ (2 * p2p1Distance));
                  DecimalFormat xx = new DecimalFormat("##.000");//四捨五入小數點第三位
                  double xx1 = Double.parseDouble(xx.format(x));
                 
                 
                  double y = ((Math.pow(r1,2) - Math.pow(r3,2) + Math.pow(i,2) + Math.pow(jj1,2))/((2*jj1) - (i*x/jj1)));
                  DecimalFormat yy = new DecimalFormat("##.000");//四捨五入小數點第三位
                  double yy1 = Double.parseDouble(yy.format(y));
                 


                  //result coordinates
                  double finalXY[] = {x1+ x*ex[0] + y*d11, y1+ x*ex[1] + y*d21};
                 
                  DecimalFormat xy = new DecimalFormat("##.000");//四捨五入小數點第三位
                  double xy1 = Double.parseDouble(xy.format(finalXY[0]));
                  double xy2 = Double.parseDouble(xy.format(finalXY[1]));
                 
                  double finalXY_return[]={xy1,xy2};
                 
              return finalXY_return;

          }
}

layout的部份如下
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.csform.android.uiapptemplate"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:gravity="center" >
   
    <SurfaceView
        android:id="@+id/surfaceView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" />

</RelativeLayout>

本體:有沒有高手可以跳出來解釋一下2D3D室內定位的方法?
忘了之前是在哪裡看到的方法,接下來DVT Stage還要修正,才能讓室內定位更精確

沒有留言:

張貼留言

FB設定搶先看的方式

設定搶先看的兩種方式 A1. 先到我家的日常粉絲團按下 …( 紅框處 ) A2. 按下追蹤中 ( 紅框處 ) A3. 按下搶先看 ( 紅框處 ) A4. 完成!!! 另一種方式 ...