【M5Stack物联网开发】第八章 使用传感器(二)
1. 使用RTC
1.1 实时时钟模块
ESP32 Real-Time Clock (RTC) 不是一个独立的模块,而是ESP32 微控制器芯片内部的一部分。它能在多种低功耗模式下提供时间相关的功能,特别适合需要时间管理和调度任务的应用。
-
系统 RTC:
- ESP32 包含一个整合在芯片中的 RTC,通过配置能够实现许多低功耗功能。
- RTC 是低功耗 CPU(ULP)的一个重要组件,可以在深度睡眠模式下维持运行。
-
RTC 日历功能:
- ESP32 内部的 RTC 可以保持当前时间和日期。
- 在设备断电后,RTC 仍需要外部电源供电(例如纽扣电池)以继续保存时间。
1.2 网络时间协议
NTP(Network Time Protocol)和SNTP(Simple Network Time Protocol)都是用于网络时间同步的协议,它们帮助计算机系统和网络设备在互联网上精确同步其时钟。NTP是一个高度准确的网络时间协议,广泛用于同步计算机系统和网络设备的时钟。NTP可以通过多种层级的时间服务器提供时间同步服务,以保证高精度和可靠性。
主要特点
-
高精度:
- NTP可以将时钟同步到毫秒级别,甚至在理想条件下达到微秒级别的精度。
- 使用复杂的算法来校正网络延迟和传播时间,保证时间同步的准确性。
-
多层级结构:
- NTP采用层级架构(称为“stratum”),其中一级服务器直接连接到高精度参考时钟(例如GPS)。
- 二级服务器则从一级服务器获取时间,并为下一级服务器提供同步服务,依此类推,形成层级结构。
-
容错和冗余:
- NTP可以从多个时间源获取时间,并通过投票算法选择最可靠的时间源,增强系统的容错能力。
-
复杂性:
- NTP的实现和配置相对复杂,适用于需要高精度时间同步的大中型网络和关键任务系统。
SNTP是NTP的简化版本,旨在提供相对较简单但足够准确的时间同步服务。它适用于某些对时间精度要求不高、资源有限的嵌入式系统和小型网络。
主要特点
-
简化版本:
- SNTP简化了NTP的算法和功能,易于实现和配置。
- 实现相对较简单,适合资源有限的设备。
-
基本精度:
- 虽然没有NTP那么高精度,SNTP仍能提供足够准确的时间同步,通常精度可达几毫秒到几十毫秒。
-
无多层结构:
- SNTP不支持NTP的多层级结构,只能直接从一个或多个时间服务器获取时间。
- 这简化了配置,但也限制了容错能力。
-
低资源占用:
- SNTP占用的资源较少,适合嵌入式系统或处理能力有限的设备。
1.3 标准时间
GMT(Greenwich Mean Time)起源于格林尼治皇家天文台(Royal Observatory, Greenwich)所在地的平均太阳时,即基于太阳在格林尼治的天顶点来定义的时间。GMT作为世界标准时间已有数百年历史,长期以来被用作航海和铁路运输等领域的时间基准。GMT是一个固定时区,没有夏令时调整,是0时区的标准时间。随着时间科学和技术的发展,GMT在精确度和一致性方面有所不足,因此逐渐被UTC取代。UTC(Coordinated Universal Time)是通过国际原子时(TAI)与地球自转的UT1时间协调生成的全球时间标准。它是现代全球时间标准体系的基石。UTC由国际度量衡大会(CIPM)来协调,目前是国际上用于科学、技术与日常生活的时间标准。UTC是一个全球统一的时间标尺,没有特定地区的相关定义,它的用途广泛,包括国际航空、航海、通信、科学研究等领域。不同地区的本地时间可以通过相对于UTC的偏移量来表示,例如北京时间(CST)是UTC+8小时。
下面是每个大洲中使用最广泛且常见的UTC时区,每个时区代表一个主要区域:
亚洲
- 中国标准时间(CST)
- 时区:UTC+8
- 主要覆盖区域:中国大陆、香港、澳门、台湾。
- 说明:全年使用中国标准时间,无夏令时。
- 日本标准时间(JST)
- 时区:UTC+9
- 主要覆盖区域:日本
- 说明:日本标准时间,没有夏令时。
欧洲
- 中欧时间(CET)
- 时区:UTC+1
- 主要覆盖区域:包括法国、德国、意大利、西班牙等大部分西欧和中欧国家。
- 说明:中欧夏令时(CEST)在夏季使用,偏移量为UTC+2。
非洲
- 东非时间(EAT)
- 时区:UTC+3
- 主要覆盖区域:肯尼亚、坦桑尼亚、乌干达、埃塞俄比亚等东非国家。
- 说明:大部分东非国家全年使用东非时间,无夏令时。
北美洲
- 东部标准时间(EST)
- 时区:UTC-5
- 主要覆盖区域:美国东部、加拿大东部地区。
- 说明:东部夏令时(EDT)在夏季使用,偏移量为UTC-4。
南美洲
- 巴西时间(BRT)
- 时区:UTC-3
- 主要覆盖区域:巴西大部分地区。
- 说明:部分地区在夏季使用巴西夏令时(BRST),偏移量为UTC-2。
大洋洲
- 澳大利亚东部时间(AEST)
- 时区:UTC+10
- 主要覆盖区域:澳大利亚东部,包括新南威尔士、昆士兰、维多利亚等。
- 说明:澳大利亚东部夏令时(AEDT)在夏季使用,偏移量为UTC+11。
中东
- 海湾标准时间(GST)
- 时区:UTC+4
- 主要覆盖区域:阿拉伯联合酋长国、阿曼。
- 说明:全年使用,无夏令时。
1.4 C++中的时间处理API
time.h
是C语言标准库中的一个头文件,提供了一组用于日期和时间操作的函数。该文件中的功能涵盖了从获取当前时间和日期,到格式化和操作时间值等多种用途。
关键数据结构
-
time_t:
- 定义:
time_t
类型通常用于表示自1970年1月1日00:00:00 UTC 起经过的秒数(也称为epoch时间)。 - 作用:它被广泛用于表示时间点。
- 定义:
-
struct tm:
- 定义:
struct tm
是一个结构体,用于存储分解的时间值。 -
struct tm { int tm_sec; // 秒数:0 - 60(允许闰秒) int tm_min; // 分钟:0 - 59 int tm_hour; // 小时:0 - 23 int tm_mday; // 一个月中的第几天:1 - 31 int tm_mon; // 月份:0 - 11(1月表示为0) int tm_year; // 年份:从 1900 年起算的年数(例如,1990 表示为 90) int tm_wday; // 星期几:0 - 6(星期天表示为0) int tm_yday; // 一年中的第几天:0 - 365 int tm_isdst; // 夏令时标志:正值表示夏令时,零值表示标准时,负值表示信息不可用 };
- 定义:
常用函数
-
time
- 原型:
time_t time(time_t *tloc);
- 功能:获取当前时间,并以
time_t
格式返回。如果tloc
不为NULL
,那么当前时间也将存储到tloc
指向的对象中。
- 原型:
-
clock
- 原型:
clock_t clock(void);
- 功能:返回程序执行起至调用该函数时所使用的处理器时间(单位是
clock ticks
)。
- 原型:
-
difftime
- 原型:
double difftime(time_t time1, time_t time2);
- 功能:计算两个时间点
time1
和time2
之间的秒数差。
- 原型:
-
mktime
- 原型:
time_t mktime(struct tm *timeptr);
- 功能:将
struct tm
结构表示的时间转换为time_t
类型。
- 原型:
-
gmtime
- 原型:
struct tm *gmtime(const time_t *timer);
- 功能:将
time_t
类型转换为 UTC(协调世界时)的struct tm
结构。
- 原型:
-
localtime
- 原型:
struct tm *localtime(const time_t *timer);
- 功能:将
time_t
类型转换为本地时间的struct tm
结构。
- 原型:
-
ctime
- 原型:
char *ctime(const time_t *timer);
- 功能:将
time_t
类型的时间转换为字符串格式,表示当地的时间和日期。
- 原型:
-
asctime
- 原型:
char *asctime(const struct tm *timeptr);
- 功能:将
struct tm
类型的时间转换为字符串格式。
- 原型:
-
strftime
- 原型:
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
- 功能:根据格式字符串
format
格式化struct tm
类型的时间,并将结果存储在字符数组s
中,最多写入max
个字符。
- 原型:
输出格式
- %a:星期几的缩写(例如:Sun)。
- %A:星期几的全称(例如:Sunday)。
- %b:月份名的缩写(例如:Jan)。
- %B:月份名的全称(例如:January)。
- %c:日期和时间的完整格式(例如:Sun Oct 17 04:41:13 2021)。
- %C:世纪数(年数的前两位数字,00-99)。
- %d:一月中的第几天(01-31)。
- %D:日期格式(等同于
%m/%d/%y
)。 - %e:一月中的第几天,用空格填充( 1-31)。
- %F:ISO 8601 日期格式(等同于
%Y-%m-%d
)。 - %g:ISO 8601 标准下的两位数年份(00-99)。
- %G:ISO 8601 标准下的四位数年份。
- %h:月份名的缩写(等同于
%b
)。 - %H:24小时制的小时(00-23)。
- %I:12小时制的小时(01-12)。
- %j:一年中的第几天(001-366)。
- %k:24小时制的小时,用空格填充( 0-23)。
- %l:12小时制的小时,用空格填充( 1-12)。
- %m:月份(01-12)。
- %M:分钟(00-59)。
- %n:换行符。
- %p:AM 或 PM 标记。
- %P:小写的 am 或 pm 标记。
- %r:12小时制的时间格式(等同于
%I:%M:%S %p
)。 - %R:24小时制的时间格式(等同于
%H:%M
)。 - %S:秒(00-60),允许闰秒。
- %t:制表符。
- %T:时间格式(等同于
%H:%M:%S
)。 - %u:星期几(1-7,周一为1)。
- %U:一年中的第几周,以周日为第一天计算(00-53)。
- %V:一年中的第几周,按照 ISO 8601 标准计算(01-53)。
- %w:星期几(0-6,周日为0)。
- %W:一年中的第几周,以周一为第一天计算(00-53)。
- %x:日期的格式化字符串(国际化区域设定的格式)。
- %X:时间的格式化字符串(国际化区域设定的格式)。
- %y:两位数的年份(00-99)。
- %Y:四位数的年份。
- %z:ISO 8601 格式的时区偏移(例如:-0500)。
- %Z:时区名称或缩写。
#include <time.h> int main() { // 获取当前时间 time_t current_time = time(NULL); struct tm *local_time = localtime(¤t_time); char buffer[80]; // 完整日期和时间 strftime(buffer, sizeof(buffer), "%c", local_time); printf("Full date and time: %s\n", buffer); // YYYY-MM-DD 格式 strftime(buffer, sizeof(buffer), "%Y-%m-%d", local_time); printf("ISO date format: %s\n", buffer); // 12小时制时间 strftime(buffer, sizeof(buffer), "%I:%M:%S %p", local_time); printf("12-hour time: %s\n", buffer); // 24小时制时间 strftime(buffer, sizeof(buffer), "%H:%M:%S", local_time); printf("24-hour time: %s\n", buffer); // 星期几 strftime(buffer, sizeof(buffer), "%A", local_time); printf("Day of the week: %s\n", buffer); // ISO 8601 week number strftime(buffer, sizeof(buffer), "Week number: %V", local_time); printf("%s\n", buffer); return 0; }
2. 使用GPS模块
2.1 全球定位系统
全球定位系统(Global Positioning System, GPS)是一种利用卫星进行导航和位置计算的系统,它能够提供全球范围内的定位和时间信息。
除了美国的GPS,还有其他国家或地区开发的全球导航卫星系统,类似于GPS,如:
- GLONASS:俄罗斯的全球导航卫星系统。
- Galileo:欧洲的全球导航卫星系统。
- 北斗:中国的全球导航卫星系统。
这些系统共同构成了全球导航卫星系统(Global Navigation Satellite System, GNSS),提供更可靠和精确的定位服务。
2.2 经纬度
经纬度(Longitude and Latitude)是一种用于描述地球表面某个点的位置的坐标系统。通过经纬度可以精确确定地点在地球上的位置。
纬度是指一个点在地球上的南北位置。纬线为东西方向的平行线,并与赤道平行。主要特点和定义如下:
纬度(Latitude)
纬度是指一个点在地球上的南北位置。纬线为东西方向的平行线,并与赤道平行。主要特点和定义如下:
- 赤道:赤道是0°纬线,将地球分为北半球和南半球。
- 南北极:北极为90°N(北纬),南极为90°S(南纬)。
- 范围:纬度的范围从赤道的0°到北极的+90°(北纬),以及从赤道的0°到南极的-90°(南纬)。
- 表示方式:纬度一般用度(°)、分(′)和秒(″)来表示。例如,北纬37度14分6秒表示为37°14'6"N。
经度(Longitude)
经度是指一个点在地球上的东西位置。经线为南北方向的半圆线,所有经线在地极相交。主要特点和定义如下:
- 本初子午线:0°经线,又称格林尼治子午线(通过英国伦敦的格林尼治天文台),将地球分为东经和西经两部分。
- 国际日期变更线:大约在180°经线的位置,用于确定日期变化。
- 范围:经度的范围从本初子午线的0°到东经+180°和西经-180°。
- 表示方式:经度一般用度(°)、分(′)和秒(″)来表示。例如,东经121度29分14秒表示为121°29'14"E。从格林尼治向东称为东经,从格林尼治向西称为西经。
经纬度的表示方法
一个具体位置的经纬度表示方法结合了两个坐标:
- 格式:纬度在前,经度在后。例如,北京市天安门广场的经纬度为39°54'26"N, 116°23'17"E。
- 如果使用double格式可以表示为39.5426和116.2317
2.3 NMEA协议
NMEA(National Marine Electronics Association,国家海洋电子协会)是一个标准协议,用于传输来自GPS、声纳、测深仪和其他导航设备的数据。NMEA协议的主要目标是确保不同设备之间数据通信的兼容性,使得数据能在不同厂商的设备之间互换。这种标准协议被广泛应用于船舶和航空导航系统。
NMEA 0183协议
NMEA 0183是最常见的NMEA协议版本,也是目前使用最广泛的。NMEA 0183协议定义了数据在单个数据流中的传输方法,有几个关键特征:
- 数据格式:NMEA 0183消息通常是ASCII格式,以逗号分隔。
- 波特率:标准波特率为4800bps,但也有9600bps或更高的实现。
- 消息类型:每条NMEA消息的开头都有一个特定的消息标识符,用以区分不同类型的数据。
- 终止符:每条消息以回车换行符结束(
<CR><LF>
)。
NMEA消息结构
NMEA消息通用格式如下:
$<Talker ID><Message Type>,<Data1>,<Data2>,...,<Checksum>*<CR><LF>
- $:消息起始符。
- <Talker ID>:两个字符,表示发送消息的设备。例如,
GP
为标准GPS接收机,UB
为u-blox GPS模块。 - <Message Type>:三个字符,指示消息的类型。例如,
GGA
,RMC
,GLL
等。 - <Data>:以逗号分隔的数据字段。
- <Checksum>:校验和,用于验证消息是否传输完整和正确。
常见NMEA GPS消息类型
-
GGA - Global Positioning System Fix Data
- 提供固定时间的位置和相关数据。
- 示例消息:
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
- 字段解释:
$GPGGA
:消息标识符123519
:UTC时间(12:35:19)4807.038,N
:纬度48度07.038分北(N)01131.000,E
:经度11度31.000分东(E)1
:GPS定位质量指示(0 = 无定位,1 = GPS定位,2 = 差分GPS定位)08
:使用中的卫星数0.9
:水平精度因子545.4,M
:天线离海平面的高度,单位为米46.9,M
:大地水准面分离,从椭球到大地水准面的高度,,
:未使用*47
:校验和
-
RMC - Recommended Minimum Specific GPS/Transit Data
- 提供最低限度的GPS数据,包括时间、日期、位置、速度和航向等。
- 示例消息:
$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
- 字段解释:
$GPRMC
:消息标识符123519
:UTC时间(12:35:19)A
:数据状态(A = 有效,V = 无效)4807.038,N
:纬度48度07.038分北(N)01131.000,E
:经度11度31.000分东(E)022.4
:速度(单位:节)084.4
:航向(度)230394
:UTC日期(23 March 1994)003.1,W
:磁偏角(3.1度西(W))*6A
:校验和
-
GSA - GPS DOP and Active Satellites
- 提供与当前使用的卫星和位置精度有关的信息。
- 示例消息:
$GPGSA,A,3,04,05,,09,12,,24,,,,,2.5,1.3,2.1*39
- 字段解释:
$GPGSA
:消息标识符A
:自动选择2D或3D模式(M = 手动,A = 自动)3
:定位类型(1 = 无定位,2 = 2D,3 = 3D)04,05,...
:当前使用的卫星PRN号2.5
:位置精度因子(PDOP)1.3
:水平精度因子(HDOP)2.1
:垂直精度因子(VDOP)*39
:校验和
-
GSV - GPS Satellites in View
- 提供当前可见的所有卫星的详细信息。
- 示例消息:
$GPGSV,2,1,08,01,40,083,41,02,50,123,42,03,60,203,43,04,70,053,44*70
- 字段解释:
$GPGSV
:消息标识符2
:总消息数1
:当前消息编号08
:可见卫星总数01,40,083,41
:卫星1的PRN号,仰角,方位角,信号强度(重复出现,每条消息最多包含4颗卫星信息)*70
:校验和
-
GLL - Geographic Position - Latitude/Longitude
- 提供当前的纬度和经度。
- 示例消息:
$GPGLL,4916.45,N,12311.12,W,225444,A,*1D
- 字段解释:
$GPGLL
:消息标识符4916.45,N
:纬度49度16.45分北(N)12311.12,W
:经度123度11.12分西(W)225444
:UTC时间(22:54:44)A
:数据状态(A = 有效,V = 无效)*1D
:校验和
校验和(Checksum)
每条NMEA消息以星号(*
)和校验和结束。校验和是从$
开始到*
之前所有字符的异或(XOR)结果,用于验证数据的完整性。如果消息校验失败,意味着数据在传输过程中出现错误,接收端可以忽略这条消息。
3. 小程序:旅行位置记录
3.1 功能分析
-
初始化模块:
- 初始化ESP32、GPS模块、RTC模块和存储设备(如microSD卡)。
- 检查各模块的连接和状态,确保工作正常。
-
获取GPS数据:
- 通过UART接口与GPS模块(AT6558)通信,读取NMEA数据。
- 使用TinyGPS++库解析NMEA数据,获取经纬度、速度、方向、时间等信息。
-
获取RTC时间:
- 使用RTC库读取RTC时间,确保时间记录准确。
-
数据存储:
- 将获取的GPS数据和RTC时间格式化并写入存储设备。
- 数据格式可考虑TXT或JSON格式,方便后续数据处理和分析。
3.2 伪代码
3.3 功能实现
4. 小练习:利用GPS信息,当传感器到达特定位置时,播放声音
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具