HS-S78P GPS定位模組

HS-S78P GPS定位模組

1、介紹

ATGM336H-5N 系列是小尺寸高性能 BDS/GNSS 全星座定位導航模組,基於中科微第四代低功耗 GNSS SOC 單芯片 AT6558,支援多衛星導航系統及衛星增强系統,有 32 個追蹤通道,可同時接收六系統 GNSS 信号並聯合定位導航授時,具備高靈敏度、低功耗、低成本優勢,適用於車載導航、手持定位、可穿戴設備,可直接替換 Ublox MAX 系列模組。

2、原理圖

GPS定位模組-HS-S78P 圖紙點擊查看

3、模組參數

引腳名稱

描述

G

GND(電源輸入負極)

V

VCC(電源輸入正極)

R

串口通信傳輸RX引腳

T

串口通信傳輸TX引腳

  • 供電電壓: 3.3V-5V

  • 連接方式:PH2.0 4P端子線

  • 安裝方式:螺絲固定

4、電路板尺寸

5、Arduino IDE示範程序

注意:程式上傳時如果提示庫文件報錯,請先導入庫文件!
Arduino IDE 庁件文件下載及導入教範:
點擊查看

示範程序(UNO開發板):

#include <TinyGPS++.h>
#include <SoftwareSerial.h>

TinyGPSPlus gps;
SoftwareSerial gps_ss(A5, A4);

void setup(){
  gps_ss.begin(9600);
  Serial.begin(9600);
}

void loop(){
  while (gps_ss.available()) {
    if (gps.encode(gps_ss.read())) {
      if (gps.location.isValid()) {
        Serial.println(gps.location.lat());
        Serial.println(gps.location.lng());

      }

    }
  }

}

6、ESP32 Python 示例(適用於Mixly IDE /米思齊)

開發板選擇 Python ESP32 【ESP32 Generic(4MB)】切換為代碼模式上傳

注意:程式上傳時如果提示庫文件報錯,請先導入庫文件!
米思齊(Mixly)IDE ESP32庫檔案下載及匯入教學:
點擊查看

示例程式(ESP32-Python):

from machine import UART, Pin
import time
import machine


# 示例程序

class GPSNMEA:
    def __init__(self):
        self._buf = bytearray()
        self._lat = None
        self._lon = None
        self._fix_ok = False
        self._sats = 0
        self._fix_quality = 0
        self._hdop = None
        self._rmc_valid = False
        self._utc_time = None
        self._date = None
    def has_fix(self):
        return bool(self._fix_ok)
    def latitude(self):
        return self._lat
    def longitude(self):
        return self._lon
    def satellites(self):
        return self._sats
    def hdop(self):
        return self._hdop
    def beijing_time(self):
        dt = self.beijing_datetime_tuple()
        if dt:
            y, mo, d, hh, mm, ss = dt
            return f"{y:04d}-{mo:02d}-{d:02d} {hh:02d}:{mm:02d}:{ss:02d}"
        return None
    def beijing_datetime_tuple(self):
        if not self._utc_time or not self._date:
            return None
        day, month, year_2 = self._date
        year_full = 2000 + year_2 if year_2 < 80 else 1900 + year_2
        h, m, s = self._utc_time
        try:
            epoch = time.mktime((year_full, month, day, h, m, s, 0, 0))
            epoch += 8 * 3600
            y, mo, d, hh, mm, ss, _, _ = time.localtime(epoch)
            return (y, mo, d, hh, mm, ss)
        except:
            return None
    def feed(self, data):
        if data is None:
            return
        if isinstance(data, int):
            data = bytes([data])
        elif isinstance(data, memoryview):
            data = bytes(data)
        elif isinstance(data, str):
            data = data.encode('ascii', 'ignore')
        self._buf.extend(data)
        while True:
            idx = self._buf.find(b'\n')
            if idx < 0:
                break
            line = self._buf[:idx + 1]
            self._buf = self._buf[idx + 1:]
            try:
                s = bytes(line).decode('ascii', 'ignore').strip()
            except:
                continue
            if s:
                self._handle_sentence(s)
    def _handle_sentence(self, s):
        if not s.startswith('$') or len(s) < 7:
            return
        if not self._checksum_ok(s):
            return
        fields = s.split('*', 1)[0].split(',')
        tag = fields[0][1:]
        if tag.endswith('RMC'):
            self._parse_rmc(fields)
        elif tag.endswith('GGA'):
            self._parse_gga(fields)
        self._fix_ok = self._rmc_valid or (self._fix_quality > 0)
    @staticmethod
    def _checksum_ok(sentence):
        if not sentence.startswith('$') or '*' not in sentence:
            return False
        try:
            data, cshex = sentence[1:].split('*', 1)
        except ValueError:
            return False
        calc = 0
        for ch in data:
            calc ^= ord(ch)
        try:
            given = int(cshex.strip()[:2], 16)
        except ValueError:
            return False
        return calc == given
    @staticmethod
    def _dm_to_deg(dm, neg):
        if not dm or '.' not in dm:
            return None
        i = dm.find('.')
        head, tail = dm[:i], dm[i:]
        if len(head) < 3:
            return None
        try:
            minutes = float(head[-2:] + tail)
            degrees = float(head[:-2]) if head[:-2] else 0.0
        except ValueError:
            return None
        dec = degrees + minutes / 60.0
        return -dec if neg else dec
    def _parse_rmc(self, f):
        if len(f) < 7:
            self._rmc_valid = False
            return
        if len(f) > 1 and f[1]:
            try:
                hh = int(f[1][0:2]); mm = int(f[1][2:4]); ss = int(f[1][4:6])
                self._utc_time = (hh, mm, ss)
            except:
                pass
        if len(f) > 9 and f[9]:
            try:
                dd = int(f[9][0:2]); mo = int(f[9][2:4]); yy = int(f[9][4:6])
                self._date = (dd, mo, yy)
            except:
                pass
        status = f[2] if len(f) > 2 else 'V'
        self._rmc_valid = (status == 'A')
        if not self._rmc_valid:
            return
        lat = self._dm_to_deg(f[3] if len(f) > 3 else ', (f[4] if len(f) > 4 else ') == 'S')
        lon = self._dm_to_deg(f[5] if len(f) > 5 else ', (f[6] if len(f) > 6 else ') == 'W')
        if (lat is not None) and (lon is not None):
            self._lat, self._lon = lat, lon
    def _parse_gga(self, f):
        if len(f) < 10:
            return
        try:
            self._fix_quality = int(f[6]) if f[6] else 0
        except ValueError:
            self._fix_quality = 0
        try:
            self._sats = int(f[7]) if f[7] else 0
        except ValueError:
            self._sats = 0
        try:
            self._hdop = float(f[8]) if f[8] else None
        except ValueError:
            self._hdop = None
        if self._fix_quality > 0:
            lat = self._dm_to_deg(f[2], f[3] == 'S') if len(f) > 4 else None
            lon = self._dm_to_deg(f[4], f[5] == 'W') if len(f) > 6 else None
            if (lat is not None) and (lon is not None):
                self._lat, self._lon = lat, lon

uart = UART(1, baudrate=9600, tx=Pin(17), rx=Pin(16), timeout=1000)
gps = GPSNMEA()

def get_latitude():
    return gps.latitude()
def get_longitude():
    return gps.longitude()
def get_beijing_time():
    return gps.beijing_time()
def get_beijing_datetime_tuple():
    return gps.beijing_datetime_tuple()
def has_fix():
    return gps.has_fix()

buf = bytearray(256)
# 这个变量必须有,获取时间块使用的这个变量
dt = (0,0,0,0,0,0)
while True:

    # -----------------------串口数据切片处理------------------------------------

    n = uart.any()  # 检查 UART 缓冲区中是否有可读取的字节数量
    if n:
        # 防止读取的数据超过缓冲区长度
        n = min(n, len(buf))
        # 从 UART 中读取 n 个字节到 buf 数组中
        read_bytes = uart.readinto(buf, n)
        if read_bytes:
            # 将实际读取的字节切片,并转换成 bytes 类型,传给 GPS 解析器
            gps.feed(bytes(buf[:read_bytes]))

    #-------------------------------------------------------------------------
    if has_fix():
        WeiDu = get_latitude()
        JingDu = get_longitude()
        dt = get_beijing_datetime_tuple()
        print(('纬度:' + str(WeiDu)))
        print(('经度:' + str(JingDu)))
        time.sleep(1)
        # 需先让GPS获取时间
        if dt:
            print('时间:',end ="")
            print((str(dt[3]) + ' : '),end ="")
            print((str(dt[4]) + ' : '),end ="")
            print(str(dt[5]))
    else:
        print('解析失败')

7、米思齊 Mixly 示例程式(圖形化語言)

示範程序(UNO開發板):點擊下載
注意:程式上傳時如果提示庫文件報錯,請先導入庫文件!
米思齊(Mixly)IDE Arduino 圖庫檔案下載及匯入教學:點擊查看

示範程序(ESP32開發板):點擊下載
注意:程式上傳時如果提示庫文件報錯,請先導入庫文件!
米思齊(Mixly)IDE ESP32庫檔案下載及匯入教學:
點擊查看

8、測試環境搭建

Arduino UNO 測試環境搭建

準備配件:

  • UNO-R3 開發板 *1

  • UNO-R3 EXP 開放板 *1

  • USB type-c 資料線 *1

  • HS-S78P GPS模組*1

  • PH2.0 4P雙頭端子線 *1

電路接線圖:

ESP32 測試環境搭建

準備配件:等更新...

電路接線圖:等更新...

9、視頻教學

Arduino UNO 觀看教程:點擊查看

ESP32 Python視頻教學:點擊查看


10、測試結果

Arduino UNO測試結果:

下入代碼,即可在電腦串口看到自己的經度和緯度。(注意:室內可能因為天線信號原因没法列印顯示不了經度和緯度,最好在室外開闊區域測試)

ESP32 測試結果:

等更新...