วันศุกร์ที่ 11 พฤษภาคม พ.ศ. 2561

ทดลองใช้งานโมดูล GPS


ครับ.. ก่อนอื่นเลยต้องเริ่มจากการเตรียมอุปกรณ์

เตรียมอุปกรณ์

    1. GPS Module





                จริงๆมีหลากหลายรูปแบบให้เลือกใช้ และส่วนใหญ่ก็จะใช้โปรโตคอลเดียวกันคือ NMEA ผ่าน Serial ที่ Buadrate 9600  ส่วนที่ผมเลือกตัวนี้เพราะความกระทัดรัดของมัน มีขนาดแค่เพียงเหรียญ 5 บาทเท่านั้น และมี Pin Out ดังนี้
ดำ -> GPS_GND
ขาว -> GPS_TX
เขียว -> GPS_RX
                   แดง -> GPS_VCC (3.3v / 5V)

                หลังจากที่เราต่อไฟเลี้ยงแล้ว ผมต่อวงจรตามนี้ GPS_TX  -> D4 , GPS_RX -> D5

    2. Arduino 


                 สามารถเลือกใช้ได้ตามสะดวก ถ้ามี USB Connector อยู่แล้วก็ไม่ต้องต่อ USB to Serial อีก และอีกเช่นเคย ผมเลือก Promini เพราะความกระทัดรัด และสเป็คเท่ากันกับ UNO และ NANO และมีราคาถูกกว่า

           

Coding




               เราจะเริ่มจากการเขียนโค๊ด Software Serial ก่อน เนื่องจาก Arduino Prominiของเรา มีฮาร์ดแวร์ซีเรียลแค่พอร์ตเดียว (1 TX , 1 RX) ซึ่งเราจะใช้มันสำหรับการแสดงผลบนคอมพิวเตอร์ และอัพโหลดโค๊ดโปรแกรม



**** Hardware Serial คือ Register หนึ่งในไมโครคอนโทรลเลอร์ ใช้ในการอ่านค่าข้อมูล และส่งข้อมูลผ่าน Serial Comunication โดยตัว Register จะทำหน้าที่อ่านเฟรมข้อมูลที่เข้ามาตามความเร็ว (Buadrate) ที่กำหนดไว้และเก็บไว้ในรีจิสเตอร์รอให้คอนโทรลเลอร์มาอ่าน และนำข้อมูลที่เตรียมส่งที่เก็บไว้ในรีจิสเตอร์ ส่งไปตามความเร็วที่กำหนดไว้  ต่างจากซอฟแวร์ซีเรียลที่เปรียบเหมือนการอ่านค่า - เขียนค่า Digital Pin ด้วยโปรแกรมเพื่อให้สามารถรับ หรือส่งสัญญาณตามเงื่อนไขที่กำหนด ดังนั้นการใช้ซอฟแวร์ซีเรียล จะเสียเวลาในการประมวลผลมากกว่า และมีเสถียรภาพต่ำกว่าแบบฮาร์ดแวร์****


#include <SoftwareSerial.h>

SoftwareSerial mySerial(4, 5); // RX, TX
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  mySerial.begin(9600);
 
}

void loop() {
  // put your main code here, to run repeatedly:

  if (mySerial.available()) {
    char ch_in = mySerial.read();
    if (ch_in >= ' ' && ch_in <= '~') { // display if ch_in is a character
      Serial.write(ch_in);
    }
    else   if (ch_in == '\n')  Serial.print("[\\n]");
    else   if (ch_in == '\r')  Serial.print("[\\r]");
    else  Serial.print("[?]");
    if (ch_in == '\n')  Serial.write('\n');


  }

} 
 
เมื่อรันโค๊ดนี้ควรจะได้ผลลัพธ์ดังนี้
 
                 อันนี้เกิดจากผมทดสอบในห้อง ทำให้ดูเหมือนมันจะรับสัญญาณไม่ได้ ต่อไปผมจะจำแนกชนิดข้อมูล ตามตัวอักษร
ตั้งต้นของชุดข้อมูล
จากข้อมูลหลายอย่างที่ส่งมา ผมจะใช้แค่ 2 ชนิด คือ GGA และ RMC โดยมีโปรโตคอลตามนี้ (อ้างอิง)

               ผมจะลองเจียนของ GGA ให้ดูเป็นแนวทางก่อนนะครับ อันดับแรกเราต้องแบ่ง String ในช่วงข้อมูลต่างๆให้ได้ แต่ผมไม่อยาก
ประกาศตัวแปร String ให้มากเกินไป ผมจึงใช้วิธีระบุตำแหน่งของเครื่องหมายจุลภาค แล้วใช้ sub string เพื่อดึงข้อความ
ตรงกลางออกมา ตามโค๊ดนี้
 

#include <SoftwareSerial.h>

SoftwareSerial mySerial(4, 5); // RX, TX
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  mySerial.begin(9600);


}

void loop() {
  // put your main code here, to run repeatedly:
  GPS_loop();


}
void GPS_loop() {
  static bool receiving = false;
  static String msg = "";
  if (mySerial.available()) {
    char ch_in = mySerial.read();
    if ( ch_in == '$') {
      msg = "";
      receiving = true;
    }
    else if  ( ch_in == '\r') {

      GPS_read(msg);
      receiving = false;
    }
    else if (receiving) {
      msg += ch_in;
    }
  }
}

void GPS_read(String &msg) {

  if (msg.substring(2, 5).equals("GGA")) {

    GPS_GGA_read(msg);

  }
  else if (msg.substring(2, 5).equals("RMC")) {

    GPS_RMC_read(msg);
  }
}

void GPS_GGA_read(String &msg) {
  Serial.println("GGA");
  unsigned char comma_arr[14];
  unsigned char comma_i = 0;
  unsigned char packet_checksum = 0;
  unsigned char CRC = msg.charAt(0);

  for (unsigned char i = 0 ; i < msg.length() ; i++) {
    if (comma_i > 14)return;
    if (msg.charAt(i) == ',') {
      comma_arr[comma_i] = i;
      comma_i++;

    }
    if (i > 0 && i < msg.length() - 3) {
      CRC = CRC ^ msg.charAt(i);
    }
    Serial.print(msg.charAt(i));

  }

  Serial.println();
  unsigned char CRC_rx = (hex_char_to_byte (msg.charAt(msg.length() - 2)) << 4) | hex_char_to_byte (msg.charAt(msg.length() - 1));
  if (CRC != CRC_rx  ) {

    Serial.println("CRC invalid!");
    Serial.println(CRC, HEX);
    Serial.println((hex_char_to_byte (msg.charAt(msg.length() - 2)) << 4) | hex_char_to_byte (msg.charAt(msg.length() - 1))  , HEX);
    return;
  }

  if (msg.charAt(comma_arr[5] + 1) == '0') {
    Serial.println("GGA unavailable");
    return;
  }
  Serial.print("UTC\t:");

  Serial.println(msg.substring(comma_arr[0] + 1, comma_arr[1]));
  Serial.println("GGA available");
  Serial.print("LAT\t:");
  Serial.print(msg.substring(comma_arr[1] + 1, comma_arr[2]));
  Serial.println(msg.substring(comma_arr[2] + 1, comma_arr[3]));
  Serial.print("LONG\t:");
  Serial.print(msg.substring(comma_arr[3] + 1, comma_arr[4]));
  Serial.println(msg.substring(comma_arr[4] + 1, comma_arr[5]));

  Serial.print("QUAL\t:");
  Serial.println(msg.substring(comma_arr[5] + 1, comma_arr[6]));
  Serial.print("SAT\t:");
  Serial.println(msg.substring(comma_arr[6] + 1, comma_arr[7]));

  Serial.print("ALT\t:");
  Serial.println(msg.substring(comma_arr[8] + 1, comma_arr[9]));



}

void GPS_RMC_read(String &msg) {

}

unsigned char hex_char_to_byte (char hex_char) {
  if (hex_char >= '0' && hex_char <= '9')return hex_char - '0';
  return (char_upper(hex_char) - 'A') + 10;
}

char char_upper(char ch) {
  if (ch >= 'a' && ch <= 'z') ch = ch - ('a' - 'A');
  return ch;
} 
 
 
                หลังจากเอาเครื่องไปวางไว้กลางแจ้ง ผมก็ได้ข้อมูลกลับมาตามนี้ 
 
ผมจะเขียนโปรแกรมในทำนองเดียวกัน สำหรับ RMC 
 
 
#include <SoftwareSerial.h>

SoftwareSerial mySerial(4, 5); // RX, TX
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  mySerial.begin(9600);


}

void loop() {
  // put your main code here, to run repeatedly:
  GPS_loop();


}
void GPS_loop() {
  static bool receiving = false;
  static String msg = "";
  if (mySerial.available()) {
    char ch_in = mySerial.read();
    if ( ch_in == '$') {
      msg = "";
      receiving = true;
    }
    else if  ( ch_in == '\r') {

      GPS_read(msg);
      receiving = false;
    }
    else if (receiving) {
      msg += ch_in;
    }
  }
}

void GPS_read(String &msg) {

  if (msg.substring(2, 5).equals("GGA")) {

    GPS_GGA_read(msg);

  }
  else if (msg.substring(2, 5).equals("RMC")) {

    GPS_RMC_read(msg);
  }
}

void GPS_GGA_read(String &msg) {

  unsigned char comma_arr[14];
  unsigned char comma_i = 0;
  unsigned char packet_checksum = 0;
  unsigned char CRC = msg.charAt(0);

  for (unsigned char i = 0 ; i < msg.length() ; i++) {
    if (comma_i > 14) {
      Serial.println("ERR");
      Serial.println(comma_i);
      return;
    }
    if (msg.charAt(i) == ',') {
      comma_arr[comma_i] = i;
      comma_i++;

    }
    if (i > 0 && i < msg.length() - 3) {
      CRC = CRC ^ msg.charAt(i);
    }
    Serial.print(msg.charAt(i));

  }

  Serial.println();
  unsigned char CRC_rx = (hex_char_to_byte (msg.charAt(msg.length() - 2)) << 4) | hex_char_to_byte (msg.charAt(msg.length() - 1));
  if (CRC != CRC_rx  ) {

    Serial.println("CRC invalid!");
    Serial.println(CRC, HEX);
    Serial.println((hex_char_to_byte (msg.charAt(msg.length() - 2)) << 4) | hex_char_to_byte (msg.charAt(msg.length() - 1))  , HEX);
    return;
  }

  if (msg.charAt(comma_arr[5] + 1) == '0') {
    Serial.println("GGA unavailable");
    return;
  }
  Serial.print("UTC\t:");

  Serial.println(msg.substring(comma_arr[0] + 1, comma_arr[1]));
  Serial.println("GGA available");
  Serial.print("LAT\t:");
  Serial.print(msg.substring(comma_arr[1] + 1, comma_arr[2]));
  Serial.println(msg.substring(comma_arr[2] + 1, comma_arr[3]));
  Serial.print("LONG\t:");
  Serial.print(msg.substring(comma_arr[3] + 1, comma_arr[4]));
  Serial.println(msg.substring(comma_arr[4] + 1, comma_arr[5]));

  Serial.print("QUAL\t:");
  Serial.println(msg.substring(comma_arr[5] + 1, comma_arr[6]));
  Serial.print("SAT\t:");
  Serial.println(msg.substring(comma_arr[6] + 1, comma_arr[7]));

  Serial.print("ALT\t:");
  Serial.println(msg.substring(comma_arr[8] + 1, comma_arr[9]));



}

void GPS_RMC_read(String &msg) {
  unsigned char comma_arr[14];
  unsigned char comma_i = 0;
  unsigned char packet_checksum = 0;
  unsigned char CRC = msg.charAt(0);

  for (unsigned char i = 0 ; i < msg.length() ; i++) {
    if (comma_i > 12)return;
    if (msg.charAt(i) == ',') {
      comma_arr[comma_i] = i;
      comma_i++;

    }
    if (i > 0 && i < msg.length() - 3) {
      CRC = CRC ^ msg.charAt(i);
    }
    Serial.print(msg.charAt(i));

  }


  Serial.println();
  unsigned char CRC_rx = (hex_char_to_byte (msg.charAt(msg.length() - 2)) << 4) | hex_char_to_byte (msg.charAt(msg.length() - 1));
  if (CRC != CRC_rx  ) {

    Serial.println("CRC invalid!");
    Serial.println(CRC, HEX);
    Serial.println((hex_char_to_byte (msg.charAt(msg.length() - 2)) << 4) | hex_char_to_byte (msg.charAt(msg.length() - 1))  , HEX);
    return;
  }

  if (msg.charAt(comma_arr[11] + 1) == 'N') {
    Serial.println("RMC unavailable");
    return;
  }
   Serial.print("SPEED\t:");
  Serial.println(msg.substring(comma_arr[6] + 1, comma_arr[7]));
}

unsigned char hex_char_to_byte (char hex_char) {
  if (hex_char >= '0' && hex_char <= '9')return hex_char - '0';
  return (char_upper(hex_char) - 'A') + 10;
}

char char_upper(char ch) {
  if (ch >= 'a' && ch <= 'z') ch = ch - ('a' - 'A');
  return ch;
} 
 
            สำหรับผลลัพธ์ ผมสามารถอ่านค่าเวลา ตำแหน่ง และความเร็วได้แล้ว ในรูปแบบสตริง ส่วนจะเอาไปทำอะไรต่อเราก็สามารถ
นำสตริงไป cast เป็นตัวแปรชนิดอื่นๆต่อไปได้
 
 สำหรับบทความนี้ เราจะจบลงตรงนี้ก่อน เพราะหลังจากนี้จะเะป็นการประยุกต์ใช้ ซึ่งอยู่นอกเหนือของเขตเนื้อหานี้
ส่วนจะเอาไปทำอะไรนั้น ก็คงต้องติดตามกันในบทความถัดๆไปครับ ^^

ไม่มีความคิดเห็น:

แสดงความคิดเห็น