2017年5月27日 星期六

LinkIt ONE: GPS 訊號 + MediaTek Cloud Sandbox 服務整合

透過聯發科 MediaTek Cloud Sandbox 服務,可以很輕鬆地建立物聯網資料服務,以下筆記紀錄取得 GPS 訊號後,搭配聯發科平台處理座標資訊。

設置 GPS + Wifi 模組


圖出處: (連結)
請依照圖示安裝好 Wifi, GPS 天線就好了。 (安裝天線請記得拔除電源)

設置 Cloud Sandbox 平台

我們要讓資料呈現在雲端,可以利用 Cloud Sandbox 平台處理。

網址: https://mcs.mediatek.com/

請註冊後進入 Cloud Sandbox (https://mcs.mediatek.com/v2console/) 的後台。

1. 選擇 Develop with LinkIt ONE
2. 請在左上角選擇 Prototype

3. 新建一個 Prototype
4. 填寫 Prototype 資料:
5. 選擇剛剛建立的 Prototype: (按下 Detail)
6. 新增一個 Channel 來顯示 GPS 資訊 (選擇 Controller -> Add):
7. 接著設定 Data type 為 GPS (下拉選單):
8. 建立好 channel 之後,現在要新增裝置:
9. 接著可以到 Test Device 找到剛剛新增 "裝置" 的 DeviceId 和 DeviceKey。

Arduino 取得 GPS 訊號

Examples -> LGPS -> GPS 範例如下:
#include <LGPS.h>

gpsSentenceInfoStruct info;
char buff[256];

static unsigned char getComma(unsigned char num,const char *str)
{
  unsigned char i,j = 0;
  int len=strlen(str);
  for(i = 0;i < len;i ++)
  {
     if(str[i] == ',')
      j++;
     if(j == num)
      return i + 1; 
  }
  return 0; 
}

static double getDoubleNumber(const char *s)
{
  char buf[10];
  unsigned char i;
  double rev;
  
  i=getComma(1, s);
  i = i - 1;
  strncpy(buf, s, i);
  buf[i] = 0;
  rev=atof(buf);
  return rev; 
}

static double getIntNumber(const char *s)
{
  char buf[10];
  unsigned char i;
  double rev;
  
  i=getComma(1, s);
  i = i - 1;
  strncpy(buf, s, i);
  buf[i] = 0;
  rev=atoi(buf);
  return rev; 
}

void parseGPGGA(const char* GPGGAstr)
{
  /* Refer to http://www.gpsinformation.org/dale/nmea.htm#GGA
   * Sample data: $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
   * Where:
   *  GGA          Global Positioning System Fix Data
   *  123519       Fix taken at 12:35:19 UTC
   *  4807.038,N   Latitude 48 deg 07.038' N
   *  01131.000,E  Longitude 11 deg 31.000' E
   *  1            Fix quality: 0 = invalid
   *                            1 = GPS fix (SPS)
   *                            2 = DGPS fix
   *                            3 = PPS fix
   *                            4 = Real Time Kinematic
   *                            5 = Float RTK
   *                            6 = estimated (dead reckoning) (2.3 feature)
   *                            7 = Manual input mode
   *                            8 = Simulation mode
   *  08           Number of satellites being tracked
   *  0.9          Horizontal dilution of position
   *  545.4,M      Altitude, Meters, above mean sea level
   *  46.9,M       Height of geoid (mean sea level) above WGS84
   *                   ellipsoid
   *  (empty field) time in seconds since last DGPS update
   *  (empty field) DGPS station ID number
   *  *47          the checksum data, always begins with *
   */
  double latitude;
  double longitude;
  int tmp, hour, minute, second, num ;
  if(GPGGAstr[0] == '$')
  {
    tmp = getComma(1, GPGGAstr);
    hour     = (GPGGAstr[tmp + 0] - '0') * 10 + (GPGGAstr[tmp + 1] - '0');
    minute   = (GPGGAstr[tmp + 2] - '0') * 10 + (GPGGAstr[tmp + 3] - '0');
    second    = (GPGGAstr[tmp + 4] - '0') * 10 + (GPGGAstr[tmp + 5] - '0');
    
    sprintf(buff, "UTC timer %2d-%2d-%2d", hour, minute, second);
    Serial.println(buff);
    
    tmp = getComma(2, GPGGAstr);
    latitude = getDoubleNumber(&GPGGAstr[tmp]);
    tmp = getComma(4, GPGGAstr);
    longitude = getDoubleNumber(&GPGGAstr[tmp]);
    sprintf(buff, "latitude = %10.4f, longitude = %10.4f", latitude, longitude);
    Serial.println(buff); 
    
    tmp = getComma(7, GPGGAstr);
    num = getIntNumber(&GPGGAstr[tmp]);    
    sprintf(buff, "satellites number = %d", num);
    Serial.println(buff); 
  }
  else
  {
    Serial.println("Not get data"); 
  }
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  LGPS.powerOn();
  Serial.println("LGPS Power on, and waiting ..."); 
  delay(3000);
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println("LGPS loop"); 
  LGPS.getData(&info);
  Serial.println((char*)info.GPGGA);  //可以更換為 GPGGA,GPGSA,GPGSV,GPRMC 格式
  parseGPGGA((const char*)info.GPGGA);
  delay(2000);
}

利用 Wifi 傳送 GPS 訊號到 Cloud Platform

(原出處: http://www.instructables.com/id/LinkIt-ONE-GPS-Tracker-MediaTek-Cloud-Sandbox-tuto/)

請先安裝 HttpClient Library: https://github.com/amcewen/HttpClient

connect.ino
void AP_connect(){
  Serial.print("Connecting to AP...");
  while (0 == LWiFi.connect(WIFI_AP, LWiFiLoginInfo(WIFI_AUTH, WIFI_PASSWORD)))
  {
    Serial.print(".");
    delay(500);
  }
  Serial.println("Success!");
  
  Serial.print("Connecting site...");

  while (!c2.connect(SITE_URL, 80))
  {
    Serial.print(".");
    delay(500);
  }
  Serial.println("Success!");
  delay(100);
}


void getconnectInfo(){
  //calling RESTful API to get TCP socket connection
  c2.print("GET /mcs/v2/devices/");
  c2.print(DEVICEID);
  c2.println("/connections.csv HTTP/1.1");
  c2.print("Host: ");
  c2.println(SITE_URL);
  c2.print("deviceKey: ");
  c2.println(DEVICEKEY);
  c2.println("Connection: close");
  c2.println();
  
  delay(500);

  int errorcount = 0;
  Serial.print("waiting for HTTP response...");
  while (!c2.available())
  {
    Serial.print(".");
    errorcount += 1;
    delay(150);
  }
  Serial.println();
  int err = http.skipResponseHeaders();

  int bodyLen = http.contentLength();
  char c;
  int ipcount = 0;
  int count = 0;
  int separater = 0;
  while (c2)
  {
    int v = (int)c2.read();
    if (v != -1)
    {
      c = v;
      //Serial.print(c);
      connection_info[ipcount]=c;
      if(c==',')
      separater=ipcount;
      ipcount++;    
    }
    else
    {
      Serial.println("no more content, disconnect");
      c2.stop();

    }
    
  }

  //connection_info[ipcount]=NULL;
  int i;
  for(i=0;i<separater;i++)
  {  ip[i]=connection_info[i];
  }
  int j=0;
  separater++;
  
  for(i=separater;i<21 && j<5 && i < ipcount;i++)
  {  port[j]=connection_info[i];
     j++;
  }
  //port[j] = NULL;
  
  portnum = atoi (port);

} //getconnectInfo

void connectTCP(){
  //establish TCP connection with TCP Server with designate IP and Port
  c.stop();
  Serial.print("Connecting to TCP...");
  while (0 == c.connect(ip, portnum))
  {
    Serial.println("Re-Connecting to TCP");    
    delay(1000);
  }
  c.println(tcpdata);
  c.println();
  Serial.println("Success!");
} //connectTCP


void heartBeat(){
  Serial.println("send TCP heartBeat");
  c.println(tcpdata);
  c.println();
    
} //heartBeat

GPS.ino
static unsigned char getComma(unsigned char num,const char *str)
{
  unsigned char i,j = 0;
  int len=strlen(str);
  for(i = 0;i < len;i ++)
  {
     if(str[i] == ',')
      j++;
     if(j == num)
      return i + 1; 
  }
  return 0; 
}

static double getDoubleNumber(const char *s)
{
  char buf[10];
  unsigned char i;
  double rev;
  
  i=getComma(1, s);
  i = i - 1;
  strncpy(buf, s, i);
  buf[i] = 0;
  rev=atof(buf);
  return rev; 
}

static double getIntNumber(const char *s)
{
  char buf[10];
  unsigned char i;
  double rev;
  
  i=getComma(1, s);
  i = i - 1;
  strncpy(buf, s, i);
  buf[i] = 0;
  rev=atoi(buf);
  return rev; 
}


void parseGPGGA(const char* GPGGAstr)
{
  /* Refer to http://www.gpsinformation.org/dale/nmea.htm#GGA
   * Sample data: $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
   * Where:
   *  GGA          Global Positioning System Fix Data
   *  123519       Fix taken at 12:35:19 UTC
   *  4807.038,N   Latitude 48 deg 07.038' N
   *  01131.000,E  Longitude 11 deg 31.000' E
   *  1            Fix quality: 0 = invalid
   *                            1 = GPS fix (SPS)
   *                            2 = DGPS fix
   *                            3 = PPS fix
   *                            4 = Real Time Kinematic
   *                            5 = Float RTK
   *                            6 = estimated (dead reckoning) (2.3 feature)
   *                            7 = Manual input mode
   *                            8 = Simulation mode
   *  08           Number of satellites being tracked
   *  0.9          Horizontal dilution of position
   *  545.4,M      Altitude, Meters, above mean sea level
   *  46.9,M       Height of geoid (mean sea level) above WGS84
   *                   ellipsoid
   *  (empty field) time in seconds since last DGPS update
   *  (empty field) DGPS station ID number
   *  *47          the checksum data, always begins with *
   */
  
  int tmp, hour, minute, second, num ;
  if(GPGGAstr[0] == '$')
  {
    tmp = getComma(1, GPGGAstr);
    hour     = (GPGGAstr[tmp + 0] - '0') * 10 + (GPGGAstr[tmp + 1] - '0');
    minute   = (GPGGAstr[tmp + 2] - '0') * 10 + (GPGGAstr[tmp + 3] - '0');
    second    = (GPGGAstr[tmp + 4] - '0') * 10 + (GPGGAstr[tmp + 5] - '0');
    
    sprintf(buff, "UTC timer %2d-%2d-%2d", hour, minute, second);
    //Serial.println(buff);
    
    tmp = getComma(2, GPGGAstr);
    latitude = getDoubleNumber(&GPGGAstr[tmp])/100.0;
    int latitude_int=floor(latitude);
    double latitude_decimal=(latitude-latitude_int)*100.0/60.0;
    latitude=latitude_int+latitude_decimal;
    tmp = getComma(4, GPGGAstr);
    longitude = getDoubleNumber(&GPGGAstr[tmp])/100.0;
    int longitude_int=floor(longitude);
    double longitude_decimal=(longitude-longitude_int)*100.0/60.0;
    longitude=longitude_int+longitude_decimal;
    
    sprintf(buff, "latitude = %10.4f, longitude = %10.4f", latitude, longitude);
    //Serial.println(buff); 
    
    tmp = getComma(7, GPGGAstr);
    num = getIntNumber(&GPGGAstr[tmp]);    
    sprintf(buff, "satellites number = %d", num);
    //Serial.println(buff); 
  }
  else
  {
    Serial.println("Not get data"); 
  }
}

void GPS_receive() {
  LGPS.getData(&info);
  //Serial.println((char*)info.GPGGA); 
  parseGPGGA((const char*)info.GPGGA);
}

upload.ino
void uploadstatus(){//calling RESTful API to upload datapoint to MCS to report LED status

  while (!c2.connect(SITE_URL, 80))
  {
    Serial.print(".");
    delay(500);
  }
  
  delay(100);
  
  if(digitalRead(13)==1)
    upload_led = "LED_DISPLAY,,1";
  else
    upload_led = "LED_DISPLAY,,0";
  
  int thislength = upload_led.length();
  HttpClient http(c2);
  c2.print("POST /mcs/v2/devices/");
  c2.print(DEVICEID);
  c2.println("/datapoints.csv HTTP/1.1");
  c2.print("Host: ");
  c2.println(SITE_URL);
  c2.print("deviceKey: ");
  c2.println(DEVICEKEY);
  c2.print("Content-Length: ");
  c2.println(thislength);
  c2.println("Content-Type: text/csv");
  c2.println("Connection: close");
  c2.println();
  c2.println(upload_led);
  delay(500);

  int errorcount = 0;
  while (!c2.available())
  {
    Serial.print(".");
    delay(100);
  }
  int err = http.skipResponseHeaders();
  int bodyLen = http.contentLength();
  
  while (c2)
  {
    int v = c2.read();
    if (v != -1)
    {
      Serial.print(char(v));
    }
    else
    {
      Serial.println("no more content, disconnect");
      c2.stop();
    }
    
  }
  Serial.println();
}

void uploadGPS(){

  while (!c2.connect(SITE_URL, 80))
  {
    Serial.print(".");
    delay(500);
  }
  
  delay(100);

  float latitude_post=latitude;
  float longitude_post=longitude;
  Serial.printf("latitude=%.4f\tlongitude=%.4f\n",latitude,longitude);
  if(latitude>-90 && latitude<=90 && longitude>=0 && longitude<360){
    sprintf(buffer_latitude, "%.4f", latitude);
    sprintf(buffer_longitude, "%.4f", longitude);
  }
  String upload_GPS = "GPS,,"+String(buffer_latitude)+","+String(buffer_longitude)+","+"0"+"\n"+"LATITUDE,,"+buffer_latitude+"\n"+"LONGITUDE,,"+buffer_longitude;//null altitude
  int GPS_length = upload_GPS.length();
  HttpClient http(c2);
  c2.print("POST /mcs/v2/devices/");
  c2.print(DEVICEID);
  c2.println("/datapoints.csv HTTP/1.1");
  c2.print("Host: ");
  c2.println(SITE_URL);
  c2.print("deviceKey: ");
  c2.println(DEVICEKEY);
  c2.print("Content-Length: ");
  c2.println(GPS_length);
  c2.println("Content-Type: text/csv");
  c2.println("Connection: close");
  c2.println();
  c2.println(upload_GPS);
  delay(500);

  int errorcount = 0;
  
  while (!c2.available())
  {
    Serial.print(".");
    delay(100);
  }
  int err = http.skipResponseHeaders();
  int bodyLen = http.contentLength();
  
  while (c2)
  {
    int v = c2.read();
    if (v != -1)
    {
      Serial.print(char(v));
    }
    else
    {
      Serial.println("no more content, disconnect");
      c2.stop();
    }
    
  }
  Serial.println();
}

main.ino
#include <LGPRS.h>
#include <LGPRSClient.h>
#include <LGPRSServer.h>

#include <LGPS.h>
#include <HttpClient.h>
#include <LTask.h>
#include <LWiFi.h>
#include <LWiFiClient.h>
#include <LDateTime.h>
#define WIFI_AP "WIFI 名稱"
#define WIFI_PASSWORD "WIFI 密碼"
#define WIFI_AUTH LWIFI_WPA  // choose from LWIFI_OPEN, LWIFI_WPA, or LWIFI_WEP.
#define per 50
#define per1 3
#define DEVICEID "裝置 ID"
#define DEVICEKEY "裝置 key"
#define SITE_URL "api.mediatek.com"

gpsSentenceInfoStruct info;
char buff[256];
double latitude;
double longitude;

char buffer_latitude[8];
char buffer_longitude[8];
LGPRSClient c; //用註解掉方式選擇用 gprs 或 wifi 連接
//LWiFiClient c;
unsigned int rtc;
unsigned int lrtc;
unsigned int rtc1;
unsigned int lrtc1;
char port[4]={0};
char connection_info[21]={0};
char ip[15]={0};             
int portnum;
int val = 0;
String tcpdata = String(DEVICEID) + "," + String(DEVICEKEY) + ",0";
String tcpcmd_led_on = "LED_CONTROL,1";
String tcpcmd_led_off = "LED_CONTROL,0";
String upload_led;

//LGPRSClient c2; //用註解方式選擇採用 gprs 或 wifi 連接
LWiFiClient c2; //選擇採用 wifi 連接
HttpClient http(c2);

void setup()
{
  pinMode(13, OUTPUT);
  LTask.begin();
  LWiFi.begin();
  Serial.begin(115200);
  LGPS.powerOn();
  //while(!Serial);
  
  AP_connect();
  getconnectInfo();
  connectTCP();
  Serial.println("==============================");
}

void loop()
{
  String tcpcmd="";
  while (c.available())
   {
      int v = c.read();
      if (v != -1)
      {
        Serial.print((char)v);
        tcpcmd += (char)v;
        if (tcpcmd.substring(40).equals(tcpcmd_led_on)){
          digitalWrite(13, HIGH);
          Serial.print("Switch LED ON ");
          tcpcmd="";
        }else if(tcpcmd.substring(40).equals(tcpcmd_led_off)){  
          digitalWrite(13, LOW);
          Serial.print("Switch LED OFF");
          tcpcmd="";
        }
      }
   }

  LDateTime.getRtc(&rtc);
  if ((rtc - lrtc) >= per) {
    heartBeat();
    lrtc = rtc;
  }
  //Check for report datapoint status interval
  LDateTime.getRtc(&rtc1);
  if ((rtc1 - lrtc1) >= per1) {
    
    uploadstatus();
    GPS_receive();
    uploadGPS();
    lrtc1 = rtc1;
  }
}

設定後,上傳到 LinkIt ONE,並到 Cloud Sandbox: Development -> Test Devices,對剛剛新增的 gpsdevice 按下 detail:
進入詳細後,可以在 data channel 選擇 history:

拖曳拉霸後可以找到詳細時間點:



Reference:
https://docs.labs.mediatek.com/resource/linkit-one/en/tutorials/car-tracker
https://mcs.mediatek.com/v2console/
https://docs.labs.mediatek.com/resource/linkit-one/en/tutorials
http://labs.mediatek.com/api/linkit-one/frames.html?frmname=topic&amp;amp;frmfile=index.html
MediaTek_LinkIt_ONE_Developers_Guide_v1_3.pdf
https://mcs.mediatek.com/
http://makerpro.cc/2015/06/six-steps-to-run-linkit-one%CA%BBs-gps-function/
http://www.instructables.com/id/LinkIt-ONE-GPS-Tracker-MediaTek-Cloud-Sandbox-tuto/
http://blog.cavedu.com/%E7%89%A9%E8%81%AF%E7%B6%B2/linkit/linkit-one-%E6%95%99%E5%AD%B8-mediatek-cloud-sandbox-%E6%8E%A7%E5%88%B6-led-%E4%B8%A6%E6%AA%A2%E8%A6%96%E7%8B%80%E6%85%8B-%E5%8F%AF%E5%90%8C%E6%99%82%E7%94%A8%E6%89%8B%E6%A9%9F%E8%88%87%E7%B6%B2/
https://github.com/amcewen/HttpClient
https://www.alliotcloud.com/media/singl.php?aid=120

Resource about GPS Meta Format, please reference follow this line:
https://rl.se/gprmc
http://b8807053.pixnet.net/blog/post/3610870-gps%E8%B3%87%E6%96%99%E6%A0%BC%E5%BC%8F
http://aprs.gids.nl/nmea/
http://www.gpsinformation.org/dale/nmea.htm
http://www.findlatitudeandlongitude.com/
http://yehnan.blogspot.tw/2015/03/linkit-onegps.html

Resource about Cloud Sandbox, please click follow this picture:

沒有留言:

張貼留言

© ERIC RILEY , 自由無須告知轉貼
Background Japanese Sayagata by Olga Libby