926|3

27

帖子

2

TA的资源

一粒金砂(中级)

楼主
 

【得捷电子Follow me第1期】+交作业 [复制链接]

 

96d3c6f5eb9b7687c94155214004beb1

一、熟悉micropython的基本语法

任务要求:通过Raspberry Pi Pico w开发板,熟悉固件下载、Mu Editor软件的基本使用方法、micropython的基本语法。

此任务需要搭配Raspberry Pi Pico w开发板来操作。

固件下载首先需要通过USB口进入BOOTSEL模式来下载MicroPython固件。按住板上BOOTSEL按键,将Raspberry Pi Pico w开发板通过USB口上电,此时电脑上会弹出一个叫RPI-RP2的盘符,将从树莓派官网下载的MicroPython固件firmware.uf2文件拖入这个盘符内,此时就将MicroPython固件下载到板上Flash存储器中。之后,Raspberry Pi Pico w开发板将自动重启,不再弹出RPI-RP2的盘符,进入MicroPython模式。

本次活动涉及到的模块有OLED、蜂鸣器、LED、WIFI、GPS,对应需要用到的外设包括machine库下的Pin、ADC、PWM、UART、I2C、RTC等类,network库、framebuffer库等。

对于machine下各种类的用法,可以在MicroPython官方说明文档中获得。

https://docs.micropython.org/en/latest/library/machine.html

按照官方文档,体验下Raspberry Pi Pico w开发板连上wifi后的跑流能力。

import network

import time

wlan = network.WLAN(network.STA_IF)

wlan.active(True)

wlan.connect('Xiaomi_F330', '1029384756')

# Wait for connect or fail

max_wait = 10

while max_wait > 0:

if wlan.status() <0 or wlan.status() >= 3:

break

max_wait -= 1

print('waiting for connection...')

time.sleep(1)

# Handle connection error

if wlan.status() != 3:

raise RuntimeError('network connection failed')

else:

print('connected')

status = wlan.ifconfig()

print('IP = '+status[0])

import upip

upip.install("uiperf3")

 

通过上述代码通过upip安装iperf

>>> import uiperf3

>>> uiperf3.client('192.168.31.53')

 

通过上述代码将Raspberry Pi Pico w开发板构建为客户端,在ubuntu下通过iperf3- s构建服务器,之后即可进行wifi网络连接速度测试。

测试收发速度在6Mbits/sec左右。

>>> import machine

>>> machine.freq()

125000000

 

此外,也可以通过machine.freq()获取或设置当前主频。

>>> import sys

>>> sys.implementation

(name='micropython', version=(1, 19, 1), _machine='Raspberry Pi Pico W with RP2040', _mpy=4102)

通过sys.implementation获取当前MicroPython固件版本号,方便及时更新。

 

二、驱动外设

任务要求:驱动LED、OLED显示屏、蜂鸣器等外设。

此任务需要搭配Raspberry Pi Pico w开发板、GROVE SHIELD扩展板、GROVE OLED扩展板、GROVE BUZZER扩展板以及LED模块来完成。

驱动LED

from machine import Pin

import time

Red_led = Pin(4,Pin.OUT)

Green_led = Pin(3,Pin.OUT)

Blue_led = Pin(2,Pin.OUT)

while True:

"""

Red_led.on()

Green_led.on()

Blue_led.on()

time.sleep(5000)

"""

Red_led.off()

Blue_led.on()

time.sleep(5000)

Green_led.off()

Red_led.on()

time.sleep(5000)

通过上述代码,将三色RGB灯分别接到GP4、GP3、GP2上,便可控制LED闪烁。

驱动蜂鸣器

GROVE BUZZER扩展板由一个蜂鸣器+Grove 4Pin座子组成,分别为GND、VCC、NC、SIG,当SIG引脚为高电平时,蜂鸣器发声。

import time

from machine import Pin

buzzer=Pin(16,Pin.OUT)

while True:

try:

# Buzz for 1 second

buzzer.value(1)

print ('start')

time.sleep(1)

# Stop buzzing for 1 second and repeat

buzzer.value(0)

print ('stop')

time.sleep(1)

except KeyboardInterrupt:

buzzer.value(0)

break

except IOError:

print ("Error")

通过上面代码,将GROVE BUZZER扩展板插到GROVE SHIELD扩展板的D16接口上,即可控制蜂鸣器发声。

驱动OLED显示屏

点亮OLED屏,首先得需要OLED屏幕的驱动程序,好在micropython官方已经将驱动程序封装好了,直接安装就好

安装完之后,通过下面的代码就可以实现点亮OLED

# Display Image & text on I2C driven ssd1306 OLED display

from machine import Pin, I2C

from ssd1306 import SSD1306_I2C

import framebuf

import time

#import rp2

WIDTH = 128 # oled display width

HEIGHT = 64 # oled display height

i2c = I2C(0) # Init I2C using I2C0 defaults, SCL=Pin(GP9), SDA=Pin(GP8), freq=400000

print("I2C Address : "+hex(i2c.scan()[0]).upper()) # Display device address

print("I2C Configuration: "+str(i2c)) # Display I2C config

oled = SSD1306_I2C(WIDTH, HEIGHT, i2c) # Init oled display

# Raspberry Pi logo as 32x32 bytearray

buffer = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|?\x00\x01\x86@\x80\x01\x01\x80\x80\x01\x11\x88\x80\x01\x05\xa0\x80\x00\x83\xc1\x00\x00C\xe3\x00\x00~\xfc\x00\x00L'\x00\x00\x9c\x11\x00\x00\xbf\xfd\x00\x00\xe1\x87\x00\x01\xc1\x83\x80\x02A\x82@\x02A\x82@\x02\xc1\xc2@\x02\xf6>\xc0\x01\xfc=\x80\x01\x18\x18\x80\x01\x88\x10\x80\x00\x8c!\x00\x00\x87\xf1\x00\x00\x7f\xf6\x00\x008\x1c\x00\x00\x0c \x00\x00\x03\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")

# Load the raspberry pi logo into the framebuffer (the image is 32x32)

fb = framebuf.FrameBuffer(buffer, 32, 32, framebuf.MONO_HLSB)

# Clear the oled display in case it has junk on it.

oled.fill(0)

# Blit the image from the framebuffer to the oled display

oled.blit(fb, 96, 0)

# Add some text

oled.text("Raspberry Pi",5,5)

oled.text("Pico w",5,15)

oled.text("hello micropython",5,40)

# Finally update the oled display so the image & text is displayed

oled.show()

将GROVE OLED扩展板插到GROVE SHIELD扩展板的I2C0接口上,即可点亮OLED屏幕。

 

三、同步网络时间

任务要求:学习network模块用法,掌握连接网络、查看网络参数等用法,实现通过网络同步系统时间

此任务需要搭配Raspberry Pi Pico w开发板及其板载WIFI模块实现

实现同步网络时间功能,首先需要联网,将Raspberry Pi Pico w设置为一个NTP客户端,来访问公共的NTP服务器域名或IP。

非常多,随便选一个就好。但是NTP获取到的为原始数据,需要对原始数据进行解析才能获取到平时看到的时间,好在micropython官网提供了一个ntptime.py文件,里面已经将解析的工作做好了,直接拿来用就好。

从micropython官方下载ntptime.py源文件,保存到Raspberry Pi Pico w开发板。

import utime

try:

import usocket as socket

except:

import socket

try:

import ustruct as struct

except:

import struct

# The NTP host can be configured at runtime by doing: ntptime.host = 'myhost.org'

host = "pool.ntp.org"

# The NTP socket timeout can be configured at runtime by doing: ntptime.timeout = 2

timeout = 1

def time():

NTP_QUERY = bytearray(48)

NTP_QUERY[0] = 0x1B

addr = socket.getaddrinfo(host, 123)[0][-1]

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

try:

s.settimeout(timeout)

res = s.sendto(NTP_QUERY, addr)

msg = s.recv(48)

finally:

s.close()

val = struct.unpack("!I", msg[40:44])[0]

EPOCH_YEAR = utime.gmtime(0)[0]

if EPOCH_YEAR == 2000:

# (date(2000, 1, 1) - date(1900, 1, 1)).days * 24*60*60

NTP_DELTA = 3155673600

elif EPOCH_YEAR == 1970:

# (date(1970, 1, 1) - date(1900, 1, 1)).days * 24*60*60

NTP_DELTA = 2208988800

else:

raise Exception("Unsupported epoch: {}".format(EPOCH_YEAR))

return val - NTP_DELTA

# There's currently no timezone support in MicroPython, and the RTC is set in UTC time.

def settime():

t = time()

import machine

tm = utime.gmtime(t)

machine.RTC().datetime((tm[0], tm[1], tm[2], tm[6] + 1, tm[3], tm[4], tm[5], 0))

接着,可以根据这个文件实现同步网络时间功能,通过下面代码可以获得。

import network

wlan = network.WLAN(network.STA_IF)

wlan.active(True)

wlan.connect('Xiaomi_F330', '1029384756')

wlan.status() #如果打印3说明连接成功

import ntptime

def sync_ntp():

ntptime.NTP_DELTA = 3155644800 # 可选 UTC+8偏移时间(秒),不设置就是UTC0

ntptime.host = 'edu.ntp.org.cn' # 可选,ntp服务器,默认是"pool.ntp.org"

ntptime.settime() # 修改设备时间,到这就已经设置好了

sync_ntp()

# 已经设置好了,随便用

from machine import RTC

rtc = RTC()

print(rtc.datetime())

 

四、实现定位功能

任务要求:掌握GNSS模块用法,实现定位功能

此任务需要搭配Pico w开发板和GROVE-GPS模块共同完成,其中GROVE-GPS模块基于合宙AIR530模组设计而来,AIR530是一个高度集成的多模卫星导航和定位模组,支持GPS、北斗、Glonass、Galileo、QZSS、SBAS卫星定位系统,非常适合用在汽车导航、智能穿戴等GNSS导航定位应用中。

GROVE-GPS模块支持3.3V或5V供电,工作电流达60mA左右,通过串口可对其进行操作,获取到对应的位置信息原始数据,根据NMEA0183协议解析出来即可获得对应的经纬度、时间、速度等信息。

模块原理图如下,通过J1、J2、J3接口与外部控制器传输数据,功耗低、体积小,可支持纽扣电池供电

网上大家都说有一个现成的micropyGPS的文件,已经把解析GPS原始数据的功能做好了,这里直接拿来用。将micropyGPS源文件保存到Pico w开发板上。

"""
# MicropyGPS - a GPS NMEA sentence parser for Micropython/Python 3.X
# Copyright (c) 2017 Michael Calvin McCoy (calvin.mccoy@protonmail.com)
# The MIT License (MIT) - see LICENSE file
"""

# TODO:
# Time Since First Fix
# Distance/Time to Target
# More Helper Functions
# Dynamically limit sentences types to parse

from math import floor, modf

# Import utime or time for fix time handling
try:
    # Assume running on MicroPython
    import utime
except ImportError:
    # Otherwise default to time module for non-embedded implementations
    # Should still support millisecond resolution.
    import time


class MicropyGPS(object):
    """GPS NMEA Sentence Parser. Creates object that stores all relevant GPS data and statistics.
    Parses sentences one character at a time using update(). """

    # Max Number of Characters a valid sentence can be (based on GGA sentence)
    SENTENCE_LIMIT = 90
    __HEMISPHERES = ('N', 'S', 'E', 'W')
    __NO_FIX = 1
    __FIX_2D = 2
    __FIX_3D = 3
    __DIRECTIONS = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W',
                    'WNW', 'NW', 'NNW')
    __MONTHS = ('January', 'February', 'March', 'April', 'May',
                'June', 'July', 'August', 'September', 'October',
                'November', 'December')

    def __init__(self, local_offset=0, location_formatting='ddm'):
        """
        Setup GPS Object Status Flags, Internal Data Registers, etc
            local_offset (int): Timzone Difference to UTC
            location_formatting (str): Style For Presenting Longitude/Latitude:
                                       Decimal Degree Minute (ddm) - 40° 26.767′ N
                                       Degrees Minutes Seconds (dms) - 40° 26′ 46″ N
                                       Decimal Degrees (dd) - 40.446° N
        """

        #####################
        # Object Status Flags
        self.sentence_active = False
        self.active_segment = 0
        self.process_crc = False
        self.gps_segments = []
        self.crc_xor = 0
        self.char_count = 0
        self.fix_time = 0

        #####################
        # Sentence Statistics
        self.crc_fails = 0
        self.clean_sentences = 0
        self.parsed_sentences = 0

        #####################
        # Logging Related
        self.log_handle = None
        self.log_en = False

        #####################
        # Data From Sentences
        # Time
        self.timestamp = [0, 0, 0.0]
        self.date = [0, 0, 0]
        self.local_offset = local_offset

        # Position/Motion
        self._latitude = [0, 0.0, 'N']
        self._longitude = [0, 0.0, 'W']
        self.coord_format = location_formatting
        self.speed = [0.0, 0.0, 0.0]
        self.course = 0.0
        self.altitude = 0.0
        self.geoid_height = 0.0

        # GPS Info
        self.satellites_in_view = 0
        self.satellites_in_use = 0
        self.satellites_used = []
        self.last_sv_sentence = 0
        self.total_sv_sentences = 0
        self.satellite_data = dict()
        self.hdop = 0.0
        self.pdop = 0.0
        self.vdop = 0.0
        self.valid = False
        self.fix_stat = 0
        self.fix_type = 1

    ########################################
    # Coordinates Translation Functions
    ########################################
    @property
    def latitude(self):
        """Format Latitude Data Correctly"""
        if self.coord_format == 'dd':
            decimal_degrees = self._latitude[0] + (self._latitude[1] / 60)
            return [decimal_degrees, self._latitude[2]]
        elif self.coord_format == 'dms':
            minute_parts = modf(self._latitude[1])
            seconds = round(minute_parts[0] * 60)
            return [self._latitude[0], int(minute_parts[1]), seconds, self._latitude[2]]
        else:
            return self._latitude

    @property
    def longitude(self):
        """Format Longitude Data Correctly"""
        if self.coord_format == 'dd':
            decimal_degrees = self._longitude[0] + (self._longitude[1] / 60)
            return [decimal_degrees, self._longitude[2]]
        elif self.coord_format == 'dms':
            minute_parts = modf(self._longitude[1])
            seconds = round(minute_parts[0] * 60)
            return [self._longitude[0], int(minute_parts[1]), seconds, self._longitude[2]]
        else:
            return self._longitude

    ########################################
    # Logging Related Functions
    ########################################
    def start_logging(self, target_file, mode="append"):
        """
        Create GPS data log object
        """
        # Set Write Mode Overwrite or Append
        mode_code = 'w' if mode == 'new' else 'a'

        try:
            self.log_handle = open(target_file, mode_code)
        except AttributeError:
            print("Invalid FileName")
            return False

        self.log_en = True
        return True

    def stop_logging(self):
        """
        Closes the log file handler and disables further logging
        """
        try:
            self.log_handle.close()
        except AttributeError:
            print("Invalid Handle")
            return False

        self.log_en = False
        return True

    def write_log(self, log_string):
        """Attempts to write the last valid NMEA sentence character to the active file handler
        """
        try:
            self.log_handle.write(log_string)
        except TypeError:
            return False
        return True

    ########################################
    # Sentence Parsers
    ########################################
    def gprmc(self):
        """Parse Recommended Minimum Specific GPS/Transit data (RMC)Sentence.
        Updates UTC timestamp, latitude, longitude, Course, Speed, Date, and fix status
        """

        # UTC Timestamp
        try:
            utc_string = self.gps_segments[1]

            if utc_string:  # Possible timestamp found
                hours = (int(utc_string[0:2]) + self.local_offset) % 24
                minutes = int(utc_string[2:4])
                seconds = float(utc_string[4:])
                self.timestamp = [hours, minutes, seconds]
            else:  # No Time stamp yet
                self.timestamp = [0, 0, 0.0]

        except ValueError:  # Bad Timestamp value present
            return False

        # Date stamp
        try:
            date_string = self.gps_segments[9]

            # Date string printer function assumes to be year >=2000,
            # date_string() must be supplied with the correct century argument to display correctly
            if date_string:  # Possible date stamp found
                day = int(date_string[0:2])
                month = int(date_string[2:4])
                year = int(date_string[4:6])
                self.date = (day, month, year)
            else:  # No Date stamp yet
                self.date = (0, 0, 0)

        except ValueError:  # Bad Date stamp value present
            return False

        # Check Receiver Data Valid Flag
        if self.gps_segments[2] == 'A':  # Data from Receiver is Valid/Has Fix

            # Longitude / Latitude
            try:
                # Latitude
                l_string = self.gps_segments[3]
                lat_degs = int(l_string[0:2])
                lat_mins = float(l_string[2:])
                lat_hemi = self.gps_segments[4]

                # Longitude
                l_string = self.gps_segments[5]
                lon_degs = int(l_string[0:3])
                lon_mins = float(l_string[3:])
                lon_hemi = self.gps_segments[6]
            except ValueError:
                return False

            if lat_hemi not in self.__HEMISPHERES:
                return False

            if lon_hemi not in self.__HEMISPHERES:
                return False

            # Speed
            try:
                spd_knt = float(self.gps_segments[7])
            except ValueError:
                return False

            # Course
            try:
                if self.gps_segments[8]:
                    course = float(self.gps_segments[8])
                else:
                    course = 0.0
            except ValueError:
                return False

            # TODO - Add Magnetic Variation

            # Update Object Data
            self._latitude = [lat_degs, lat_mins, lat_hemi]
            self._longitude = [lon_degs, lon_mins, lon_hemi]
            # Include mph and hm/h
            self.speed = [spd_knt, spd_knt * 1.151, spd_knt * 1.852]
            self.course = course
            self.valid = True

            # Update Last Fix Time
            self.new_fix_time()

        else:  # Clear Position Data if Sentence is 'Invalid'
            self._latitude = [0, 0.0, 'N']
            self._longitude = [0, 0.0, 'W']
            self.speed = [0.0, 0.0, 0.0]
            self.course = 0.0
            self.valid = False

        return True

    def gpgll(self):
        """Parse Geographic Latitude and Longitude (GLL)Sentence. Updates UTC timestamp, latitude,
        longitude, and fix status"""

        # UTC Timestamp
        try:
            utc_string = self.gps_segments[5]

            if utc_string:  # Possible timestamp found
                hours = (int(utc_string[0:2]) + self.local_offset) % 24
                minutes = int(utc_string[2:4])
                seconds = float(utc_string[4:])
                self.timestamp = [hours, minutes, seconds]
            else:  # No Time stamp yet
                self.timestamp = [0, 0, 0.0]

        except ValueError:  # Bad Timestamp value present
            return False

        # Check Receiver Data Valid Flag
        if self.gps_segments[6] == 'A':  # Data from Receiver is Valid/Has Fix

            # Longitude / Latitude
            try:
                # Latitude
                l_string = self.gps_segments[1]
                lat_degs = int(l_string[0:2])
                lat_mins = float(l_string[2:])
                lat_hemi = self.gps_segments[2]

                # Longitude
                l_string = self.gps_segments[3]
                lon_degs = int(l_string[0:3])
                lon_mins = float(l_string[3:])
                lon_hemi = self.gps_segments[4]
            except ValueError:
                return False

            if lat_hemi not in self.__HEMISPHERES:
                return False

            if lon_hemi not in self.__HEMISPHERES:
                return False

            # Update Object Data
            self._latitude = [lat_degs, lat_mins, lat_hemi]
            self._longitude = [lon_degs, lon_mins, lon_hemi]
            self.valid = True

            # Update Last Fix Time
            self.new_fix_time()

        else:  # Clear Position Data if Sentence is 'Invalid'
            self._latitude = [0, 0.0, 'N']
            self._longitude = [0, 0.0, 'W']
            self.valid = False

        return True

    def gpvtg(self):
        """Parse Track Made Good and Ground Speed (VTG) Sentence. Updates speed and course"""
        try:
            course = float(self.gps_segments[1]) if self.gps_segments[1] else 0.0
            spd_knt = float(self.gps_segments[5]) if self.gps_segments[5] else 0.0
        except ValueError:
            return False

        # Include mph and km/h
        self.speed = (spd_knt, spd_knt * 1.151, spd_knt * 1.852)
        self.course = course
        return True

    def gpgga(self):
        """Parse Global Positioning System Fix Data (GGA) Sentence. Updates UTC timestamp, latitude, longitude,
        fix status, satellites in use, Horizontal Dilution of Precision (HDOP), altitude, geoid height and fix status"""

        try:
            # UTC Timestamp
            utc_string = self.gps_segments[1]

            # Skip timestamp if receiver doesn't have on yet
            if utc_string:
                hours = (int(utc_string[0:2]) + self.local_offset) % 24
                minutes = int(utc_string[2:4])
                seconds = float(utc_string[4:])
            else:
                hours = 0
                minutes = 0
                seconds = 0.0

            # Number of Satellites in Use
            satellites_in_use = int(self.gps_segments[7])

            # Get Fix Status
            fix_stat = int(self.gps_segments[6])

        except (ValueError, IndexError):
            return False

        try:
            # Horizontal Dilution of Precision
            hdop = float(self.gps_segments[8])
        except (ValueError, IndexError):
            hdop = 0.0

        # Process Location and Speed Data if Fix is GOOD
        if fix_stat:

            # Longitude / Latitude
            try:
                # Latitude
                l_string = self.gps_segments[2]
                lat_degs = int(l_string[0:2])
                lat_mins = float(l_string[2:])
                lat_hemi = self.gps_segments[3]

                # Longitude
                l_string = self.gps_segments[4]
                lon_degs = int(l_string[0:3])
                lon_mins = float(l_string[3:])
                lon_hemi = self.gps_segments[5]
            except ValueError:
                return False

            if lat_hemi not in self.__HEMISPHERES:
                return False

            if lon_hemi not in self.__HEMISPHERES:
                return False

            # Altitude / Height Above Geoid
            try:
                altitude = float(self.gps_segments[9])
                geoid_height = float(self.gps_segments[11])
            except ValueError:
                altitude = 0
                geoid_height = 0

            # Update Object Data
            self._latitude = [lat_degs, lat_mins, lat_hemi]
            self._longitude = [lon_degs, lon_mins, lon_hemi]
            self.altitude = altitude
            self.geoid_height = geoid_height

        # Update Object Data
        self.timestamp = [hours, minutes, seconds]
        self.satellites_in_use = satellites_in_use
        self.hdop = hdop
        self.fix_stat = fix_stat

        # If Fix is GOOD, update fix timestamp
        if fix_stat:
            self.new_fix_time()

        return True

    def gpgsa(self):
        """Parse GNSS DOP and Active Satellites (GSA) sentence. Updates GPS fix type, list of satellites used in
        fix calculation, Position Dilution of Precision (PDOP), Horizontal Dilution of Precision (HDOP), Vertical
        Dilution of Precision, and fix status"""

        # Fix Type (None,2D or 3D)
        try:
            fix_type = int(self.gps_segments[2])
        except ValueError:
            return False

        # Read All (up to 12) Available PRN Satellite Numbers
        sats_used = []
        for sats in range(12):
            sat_number_str = self.gps_segments[3 + sats]
            if sat_number_str:
                try:
                    sat_number = int(sat_number_str)
                    sats_used.append(sat_number)
                except ValueError:
                    return False
            else:
                break

        # PDOP,HDOP,VDOP
        try:
            pdop = float(self.gps_segments[15])
            hdop = float(self.gps_segments[16])
            vdop = float(self.gps_segments[17])
        except ValueError:
            return False

        # Update Object Data
        self.fix_type = fix_type

        # If Fix is GOOD, update fix timestamp
        if fix_type > self.__NO_FIX:
            self.new_fix_time()

        self.satellites_used = sats_used
        self.hdop = hdop
        self.vdop = vdop
        self.pdop = pdop

        return True

    def gpgsv(self):
        """Parse Satellites in View (GSV) sentence. Updates number of SV Sentences,the number of the last SV sentence
        parsed, and data on each satellite present in the sentence"""
        try:
            num_sv_sentences = int(self.gps_segments[1])
            current_sv_sentence = int(self.gps_segments[2])
            sats_in_view = int(self.gps_segments[3])
        except ValueError:
            return False

        # Create a blank dict to store all the satellite data from this sentence in:
        # satellite PRN is key, tuple containing telemetry is value
        satellite_dict = dict()

        # Calculate  Number of Satelites to pull data for and thus how many segment positions to read
        if num_sv_sentences == current_sv_sentence:
            # Last sentence may have 1-4 satellites; 5 - 20 positions
            sat_segment_limit = (sats_in_view - ((num_sv_sentences - 1) * 4)) * 5
        else:
            sat_segment_limit = 20  # Non-last sentences have 4 satellites and thus read up to position 20

        # Try to recover data for up to 4 satellites in sentence
        for sats in range(4, sat_segment_limit, 4):

            # If a PRN is present, grab satellite data
            if self.gps_segments[sats]:
                try:
                    sat_id = int(self.gps_segments[sats])
                except (ValueError,IndexError):
                    return False

                try:  # elevation can be null (no value) when not tracking
                    elevation = int(self.gps_segments[sats+1])
                except (ValueError,IndexError):
                    elevation = None

                try:  # azimuth can be null (no value) when not tracking
                    azimuth = int(self.gps_segments[sats+2])
                except (ValueError,IndexError):
                    azimuth = None

                try:  # SNR can be null (no value) when not tracking
                    snr = int(self.gps_segments[sats+3])
                except (ValueError,IndexError):
                    snr = None
            # If no PRN is found, then the sentence has no more satellites to read
            else:
                break

            # Add Satellite Data to Sentence Dict
            satellite_dict[sat_id] = (elevation, azimuth, snr)

        # Update Object Data
        self.total_sv_sentences = num_sv_sentences
        self.last_sv_sentence = current_sv_sentence
        self.satellites_in_view = sats_in_view

        # For a new set of sentences, we either clear out the existing sat data or
        # update it as additional SV sentences are parsed
        if current_sv_sentence == 1:
            self.satellite_data = satellite_dict
        else:
            self.satellite_data.update(satellite_dict)

        return True

    ##########################################
    # Data Stream Handler Functions
    ##########################################

    def new_sentence(self):
        """Adjust Object Flags in Preparation for a New Sentence"""
        self.gps_segments = ['']
        self.active_segment = 0
        self.crc_xor = 0
        self.sentence_active = True
        self.process_crc = True
        self.char_count = 0

    def update(self, new_char):
        """Process a new input char and updates GPS object if necessary based on special characters ('$', ',', '*')
        Function builds a list of received string that are validate by CRC prior to parsing by the  appropriate
        sentence function. Returns sentence type on successful parse, None otherwise"""

        valid_sentence = False

        # Validate new_char is a printable char
        ascii_char = ord(new_char)

        if 10 <= ascii_char <= 126:
            self.char_count += 1

            # Write Character to log file if enabled
            if self.log_en:
                self.write_log(new_char)

            # Check if a new string is starting ($)
            if new_char == '$':
                self.new_sentence()
                return None

            elif self.sentence_active:

                # Check if sentence is ending (*)
                if new_char == '*':
                    self.process_crc = False
                    self.active_segment += 1
                    self.gps_segments.append('')
                    return None

                # Check if a section is ended (,), Create a new substring to feed
                # characters to
                elif new_char == ',':
                    self.active_segment += 1
                    self.gps_segments.append('')

                # Store All Other printable character and check CRC when ready
                else:
                    self.gps_segments[self.active_segment] += new_char

                    # When CRC input is disabled, sentence is nearly complete
                    if not self.process_crc:

                        if len(self.gps_segments[self.active_segment]) == 2:
                            try:
                                final_crc = int(self.gps_segments[self.active_segment], 16)
                                if self.crc_xor == final_crc:
                                    valid_sentence = True
                                else:
                                    self.crc_fails += 1
                            except ValueError:
                                pass  # CRC Value was deformed and could not have been correct

                # Update CRC
                if self.process_crc:
                    self.crc_xor ^= ascii_char

                # If a Valid Sentence Was received and it's a supported sentence, then parse it!!
                if valid_sentence:
                    self.clean_sentences += 1  # Increment clean sentences received
                    self.sentence_active = False  # Clear Active Processing Flag

                    if self.gps_segments[0] in self.supported_sentences:

                        # parse the Sentence Based on the message type, return True if parse is clean
                        if self.supported_sentences[self.gps_segments[0]](self):

                            # Let host know that the GPS object was updated by returning parsed sentence type
                            self.parsed_sentences += 1
                            return self.gps_segments[0]

                # Check that the sentence buffer isn't filling up with Garage waiting for the sentence to complete
                if self.char_count > self.SENTENCE_LIMIT:
                    self.sentence_active = False

        # Tell Host no new sentence was parsed
        return None

    def new_fix_time(self):
        """Updates a high resolution counter with current time when fix is updated. Currently only triggered from
        GGA, GSA and RMC sentences"""
        try:
            self.fix_time = utime.ticks_ms()
        except NameError:
            self.fix_time = time.time()

    #########################################
    # User Helper Functions
    # These functions make working with the GPS object data easier
    #########################################

    def satellite_data_updated(self):
        """
        Checks if the all the GSV sentences in a group have been read, making satellite data complete
        :return: boolean
        """
        if self.total_sv_sentences > 0 and self.total_sv_sentences == self.last_sv_sentence:
            return True
        else:
            return False

    def unset_satellite_data_updated(self):
        """
        Mark GSV sentences as read indicating the data has been used and future updates are fresh
        """
        self.last_sv_sentence = 0

    def satellites_visible(self):
        """
        Returns a list of of the satellite PRNs currently visible to the receiver
        :return: list
        """
        return list(self.satellite_data.keys())

    def time_since_fix(self):
        """Returns number of millisecond since the last sentence with a valid fix was parsed. Returns 0 if
        no fix has been found"""

        # Test if a Fix has been found
        if self.fix_time == 0:
            return -1

        # Try calculating fix time using utime; if not running MicroPython
        # time.time() returns a floating point value in secs
        try:
            current = utime.ticks_diff(utime.ticks_ms(), self.fix_time)
        except NameError:
            current = (time.time() - self.fix_time) * 1000  # ms

        return current

    def compass_direction(self):
        """
        Determine a cardinal or inter-cardinal direction based on current course.
        :return: string
        """
        # Calculate the offset for a rotated compass
        if self.course >= 348.75:
            offset_course = 360 - self.course
        else:
            offset_course = self.course + 11.25

        # Each compass point is separated by 22.5 degrees, divide to find lookup value
        dir_index = floor(offset_course / 22.5)

        final_dir = self.__DIRECTIONS[dir_index]

        return final_dir

    def latitude_string(self):
        """
        Create a readable string of the current latitude data
        :return: string
        """
        if self.coord_format == 'dd':
            formatted_latitude = self.latitude
            lat_string = str(formatted_latitude[0]) + '° ' + str(self._latitude[2])
        elif self.coord_format == 'dms':
            formatted_latitude = self.latitude
            lat_string = str(formatted_latitude[0]) + '° ' + str(formatted_latitude[1]) + "' " + str(formatted_latitude[2]) + '" ' + str(formatted_latitude[3])
        else:
            lat_string = str(self._latitude[0]) + '° ' + str(self._latitude[1]) + "' " + str(self._latitude[2])
        return lat_string

    def longitude_string(self):
        """
        Create a readable string of the current longitude data
        :return: string
        """
        if self.coord_format == 'dd':
            formatted_longitude = self.longitude
            lon_string = str(formatted_longitude[0]) + '° ' + str(self._longitude[2])
        elif self.coord_format == 'dms':
            formatted_longitude = self.longitude
            lon_string = str(formatted_longitude[0]) + '° ' + str(formatted_longitude[1]) + "' " + str(formatted_longitude[2]) + '" ' + str(formatted_longitude[3])
        else:
            lon_string = str(self._longitude[0]) + '° ' + str(self._longitude[1]) + "' " + str(self._longitude[2])
        return lon_string

    def speed_string(self, unit='kph'):
        """
        Creates a readable string of the current speed data in one of three units
        :param unit: string of 'kph','mph, or 'knot'
        :return:
        """
        if unit == 'mph':
            speed_string = str(self.speed[1]) + ' mph'

        elif unit == 'knot':
            if self.speed[0] == 1:
                unit_str = ' knot'
            else:
                unit_str = ' knots'
            speed_string = str(self.speed[0]) + unit_str

        else:
            speed_string = str(self.speed[2]) + ' km/h'

        return speed_string

    def date_string(self, formatting='s_mdy', century='20'):
        """
        Creates a readable string of the current date.
        Can select between long format: Januray 1st, 2014
        or two short formats:
        11/01/2014 (MM/DD/YYYY)
        01/11/2014 (DD/MM/YYYY)
        :param formatting: string 's_mdy', 's_dmy', or 'long'
        :param century: int delineating the century the GPS data is from (19 for 19XX, 20 for 20XX)
        :return: date_string  string with long or short format date
        """

        # Long Format Januray 1st, 2014
        if formatting == 'long':
            # Retrieve Month string from private set
            month = self.__MONTHS[self.date[1] - 1]

            # Determine Date Suffix
            if self.date[0] in (1, 21, 31):
                suffix = 'st'
            elif self.date[0] in (2, 22):
                suffix = 'nd'
            elif self.date[0] == (3, 23):
                suffix = 'rd'
            else:
                suffix = 'th'

            day = str(self.date[0]) + suffix  # Create Day String

            year = century + str(self.date[2])  # Create Year String

            date_string = month + ' ' + day + ', ' + year  # Put it all together

        else:
            # Add leading zeros to day string if necessary
            if self.date[0] < 10:
                day = '0' + str(self.date[0])
            else:
                day = str(self.date[0])

            # Add leading zeros to month string if necessary
            if self.date[1] < 10:
                month = '0' + str(self.date[1])
            else:
                month = str(self.date[1])

            # Add leading zeros to year string if necessary
            if self.date[2] < 10:
                year = '0' + str(self.date[2])
            else:
                year = str(self.date[2])

            # Build final string based on desired formatting
            if formatting == 's_dmy':
                date_string = day + '/' + month + '/' + year

            else:  # Default date format
                date_string = month + '/' + day + '/' + year

        return date_string

    # All the currently supported NMEA sentences
    supported_sentences = {'GPRMC': gprmc, 'GLRMC': gprmc,
                           'GPGGA': gpgga, 'GLGGA': gpgga,
                           'GPVTG': gpvtg, 'GLVTG': gpvtg,
                           'GPGSA': gpgsa, 'GLGSA': gpgsa,
                           'GPGSV': gpgsv, 'GLGSV': gpgsv,
                           'GPGLL': gpgll, 'GLGLL': gpgll,
                           'GNGGA': gpgga, 'GNRMC': gprmc,
                           'GNVTG': gpvtg, 'GNGLL': gpgll,
                           'GNGSA': gpgsa,
                          }

if __name__ == "__main__":
    pass


之后根据此文件,构建自己的GPS定位程序,代码如下。

from machine import UART, Pin
import time
from micropyGPS import MicropyGPS


uart0 = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))

time.sleep_ms(100)
#rxData = bytes()
my_gps = MicropyGPS()
while True:
    if uart0.any():
        gps_stat = my_gps.update(uart0.read(1).decode("ascii"))  
        # Note the conversion to to chr, UART outputs ints normally
        if gps_stat:
            print("Latitude:", my_gps.latitude_string())					#读取经度、纬度信息
            print("Longitude:", my_gps.longitude_string())
            print("Speed:",my_gps.speed_string("kph"),"or",my_gps.speed_string("mph"),"or", my_gps.speed_string("knot"),)  #读取速度信息
            print("Date (Long Format):", my_gps.date_string("long"))										#读取时间信息
            print("Date (Short D/M/Y Format):", my_gps.date_string("s_dmy"))
            print("timestamp (Short [H,M,S] Format):", my_gps.timestamp)
            stat = None

解析出来的数据在shell窗口可以看到。

 

五、心得体会

非常感谢EEWORLD和得捷一起举办的这次比赛,这是我第一次接触micropython语言,整体使用下来确实感觉比c语言用起来简单了不少,用每个IO和外设前都不用初始化时钟先走个四五行了,总之学到了不少东西,关于wifi和GPS定位的知识也学习到了很多,发挥部分是想根据任务三和任务四,反过来,根据pico w做一个NTP服务器,使用GPS上的PPS作为时钟源,为NTP服务器设置一个基准时间,之后根据PPS信号进行跳秒,可以为其他设备进行网络授时。但是,种种原因目前还未实现,后续成功做出来,也会在论坛进行分享。最后,希望EEWORLD和得捷为坛友们带来更多更好的活动,期待接下来的Follow me第2期。

iperf3_test_00.py (576 Bytes, 下载次数: 1)
led_test_01.py (352 Bytes, 下载次数: 0)
buzzer_test_02.py (436 Bytes, 下载次数: 0)
oled_test_03.py (1.68 KB, 下载次数: 0)
ntptime.py (1.34 KB, 下载次数: 0)
ntp_test_04.py (569 Bytes, 下载次数: 0)
micropyGPS.py (29.64 KB, 下载次数: 0)
GPS_Test_05.py (945 Bytes, 下载次数: 0)

最新回复

gps还是很好玩的,有试过在地库吗  详情 回复 发表于 2023-7-3 06:49
点赞 关注
 
 

回复
举报

7091

帖子

11

TA的资源

版主

沙发
 

非常感谢EEWORLD和得捷一起举办的这次比赛.

恭喜完成作业。

 
 
 

回复

4947

帖子

12

TA的资源

版主

板凳
 
gps还是很好玩的,有试过在地库吗
 
 
 

回复

27

帖子

2

TA的资源

一粒金砂(中级)

4
 

补录视频https://training.eeworld.com.cn/course/68026

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表