【NodeMCU实时天气时钟温湿度项目 4】通过NTPClient库获取实时网络时间并显示在TFT屏幕上

news2024/11/26 22:43:36

        今天是【实时天气时钟温湿度项目】第四专题,主要内容是:学习导入NTPClient库,通过这个库获取实时网络时间,显示在1.3寸TFT液晶屏幕上。此前三个专题,请选择查看以下链接。

        第一专题内容,请参考
        【NodeMCU实时天气时钟温湿度项目 1】连接点亮SPI-TFT屏幕和UI布局设计-CSDN博客
        第二专题内容,请参考
        【NodeMCU实时天气时钟温湿度项目 2】WIFI模式设置及连接-CSDN博客
        第三专题内容,请参考       
【NodeMCU实时天气时钟温湿度项目 3】连接SHT30传感器,获取并显示当前环境温湿度数据(I2C)-CSDN博客
        NTPClient功能库有关内容,请参考
        【Arduino】NTPClient:连接NTP服务器获取实时网络时间_ntpclient.h-CSDN博客

一、添加NTPClient库

        获取实时网络时间,一般通过 NTP (网络时间协议)服务器来实现。在Arduino框架下,我们通过NTPClient库提供的函数功能,连接到NTP服务器,从服务器获取时间,并保持同步。
        添加库的方法:打开 PlatformIO 界面,选择 Libraries 图标,在搜索栏内输入 NTPClient,在查询结果中选择NTPClient库,,添加到本项目中。

二、NTPClient官方示例代码及主要函数

        下面是NTPClient库官方示例 Advanced.info 的代码内容,将其全文复制到主文件 main.cpp 中,添加库,编译上传到NodeMCU开发板,就可以正常运行了。
        提醒:请务必将 ssid 和 password ,变更为您所在环境的 AP访问点名称和密码。       

#include <NTPClient.h>
// change next line to use with another board/shield
#include <ESP8266WiFi.h>
//#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>

const char *ssid     = "xcb940";
const char *password = "87589940abc";

WiFiUDP ntpUDP;

// You can specify the time server pool and the offset (in seconds, can be
// changed later with setTimeOffset() ). Additionally you can specify the
// update interval (in milliseconds, can be changed using setUpdateInterval() ).
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);

void setup(){
  Serial.begin(115200);

  WiFi.begin(ssid, password);

  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 500 );
    Serial.print ( "." );
  }

  timeClient.begin();
}

void loop() {
  timeClient.update();

  Serial.println(timeClient.getFormattedTime());

  delay(1000);
}

下图是程序正常运行后的模样。

        特别说明:关于NTPClient库的介绍和应用,我在前期曾发布过一个博文,对这个库作了详细介绍。官方示例中涉及到的功能函数,在博文中都有详细介绍,具体内容请点击下面的链接查看:【Arduino】NTPClient:连接NTP服务器获取实时网络时间_ntpclient.h-CSDN博客

三、本项目关于获取网络时间函数说明       

        通过NTPClient功能库获取网络时间的代码,封装在项目的 ntptime.h 文件中,主要有以下4个功能函数。
        (1)void initNtp(),启动NTPClient,开始客户端与NTP服务器同步、获取UTC时间,填充时日期时间数据结构 struct dt_data;设置时区偏移量(28800 = 60 * 60 * 8 秒)。

void initNtp() {
  timeClient.begin();
  //28800 = +8时区(我们的北京时间)
  timeClient.setTimeOffset(28800);
  loopNtp();
}
struct dt_data {
  String localDate = "";
  String localTime = "";
  String y;
  String m;
  String d;
  String h;
  String i;
  String s;
  uint16_t year;
  uint8_t month;
  uint8_t day;
  uint8_t hours;
  uint8_t minutes;
  uint8_t seconds;
} dt;

        (2)void loopNtp(),获取UTC时间,进行格式转换,填充时日期时间数据结构 struct dt_data;每调用一次,获取一次时间。

void loopNtp() {
  timeClient.update();
  Serial.println(timeClient.getFormattedTime());
  //获取unix时间戳(1970年至今的总秒数)
  unsigned long epochTime = timeClient.getEpochTime();
  Serial.print("epochTime: ");
  Serial.println(epochTime);

  //格式化得到 时:分:秒
  dt.localTime = timeClient.getFormattedTime();

  //重新计算得到 年-月-日
  time_t rawtime = epochTime;
  struct tm * ti;
  ti = localtime (&rawtime);

  dt.year = ti->tm_year + 1900;
  dt.y = String(dt.year);

  dt.month = ti->tm_mon + 1;
  dt.m = dt.month < 10 ? "0" + String(dt.month) : String(dt.month);

  dt.day = ti->tm_mday;
  dt.d = dt.day < 10 ? "0" + String(dt.day) : String(dt.day);

  dt.hours = ti->tm_hour;
  dt.h = dt.hours < 10 ? "0" + String(dt.hours) : String(dt.hours);

  dt.minutes = ti->tm_min;
  dt.i = dt.minutes < 10 ? "0" + String(dt.minutes) : String(dt.minutes);

  dt.seconds = ti->tm_sec;
  dt.s = dt.seconds < 10 ? "0" + String(dt.seconds) : String(dt.seconds);

  //将得到的年月日写入weather_data 结构体
  dt.localDate = dt.y + "-" + dt.m + "-" + dt.d;
  //Serial.println(dt.h + "-" + dt.i + "-" + dt.s);
}

        (3)LunarDate LunarCalendar(int year, int month, int day),将阳历的年月日转换成阴历的年月日,存放到结构体 struct LunarDate 中。

LunarDate LunarCalendar(int year, int month, int day)
{
  int Spring_NY, Sun_NY, StaticDayCount;
  int index, flag;
  //Spring_NY 记录春节离当年元旦的天数。
  //Sun_NY 记录阳历日离当年元旦的天数。
  if ( ((LunarCalendarTable[year - 1901] & 0x0060) >> 5) == 1)
    Spring_NY = (LunarCalendarTable[year - 1901] & 0x001F) - 1;
  else
    Spring_NY = (LunarCalendarTable[year - 1901] & 0x001F) - 1 + 31;
  Sun_NY = MonthAdd[month - 1] + day - 1;
  if ( (!(year % 4)) && (month > 2))
    Sun_NY++;
  //StaticDayCount记录大小月的天数 29 或30
  //index 记录从哪个月开始来计算。
  //flag 是用来对闰月的特殊处理。
  //判断阳历日在春节前还是春节后
  if (Sun_NY >= Spring_NY)//阳历日在春节后(含春节那天)
  {
    Sun_NY -= Spring_NY;
    month = 1;
    index = 1;
    flag = 0;
    if ( ( LunarCalendarTable[year - 1901] & (0x80000 >> (index - 1)) ) == 0)
      StaticDayCount = 29;
    else
      StaticDayCount = 30;
    while (Sun_NY >= StaticDayCount)
    {
      Sun_NY -= StaticDayCount;
      index++;
      if (month == ((LunarCalendarTable[year - 1901] & 0xF00000) >> 20) )
      {
        flag = ~flag;
        if (flag == 0)
          month++;
      }
      else
        month++;
      if ( ( LunarCalendarTable[year - 1901] & (0x80000 >> (index - 1)) ) == 0)
        StaticDayCount = 29;
      else
        StaticDayCount = 30;
    }
    day = Sun_NY + 1;
  }
  else //阳历日在春节前
  {
    Spring_NY -= Sun_NY;
    year--;
    month = 12;
    if ( ((LunarCalendarTable[year - 1901] & 0xF00000) >> 20) == 0)
      index = 12;
    else
      index = 13;
    flag = 0;
    if ( ( LunarCalendarTable[year - 1901] & (0x80000 >> (index - 1)) ) == 0)
      StaticDayCount = 29;
    else
      StaticDayCount = 30;
    while (Spring_NY > StaticDayCount)
    {
      Spring_NY -= StaticDayCount;
      index--;
      if (flag == 0)
        month--;
      if (month == ((LunarCalendarTable[year - 1901] & 0xF00000) >> 20))
        flag = ~flag;
      if ( ( LunarCalendarTable[year - 1901] & (0x80000 >> (index - 1)) ) == 0)
        StaticDayCount = 29;
      else
        StaticDayCount = 30;
    }
    day = StaticDayCount - Spring_NY + 1;
  }
  LunarCalendarDay |= day;
  LunarCalendarDay |= (month << 6);

  LunarDate d;
  d.year = year;
  d.month = month;
  d.day = day;
  if (month == ((LunarCalendarTable[year - 1901] & 0xF00000) >> 20))
    d.leap =  1;
  else
    d.leap =  0;
  return d;
}

String outputLunarDate(int year, int month, int day) {
  LunarDate ld = LunarCalendar(year, month, day);
  String str = "";
  if (ld.leap) {
    str += "闰";
  } else {
    str += " ";
  }
  str += ChMonth[ld.month] + ChDay[ld.day];
  return str;
}

        (4)String weekOfDate1(int year, int month, int day),将阳历的年月日转换为星期几。

String weekOfDate1(int year, int month, int day)
{
  int adjustment, mm, yy;
  if (year < 2000) year += 2000;
  adjustment = (14 - month) / 12;
  mm = month + 12 * adjustment - 2;
  yy = year - adjustment;
  int week = (day + (13 * mm - 1) / 5 +
              yy + yy / 4 - yy / 100 + yy / 400) % 7;
  return weekly[week];
}

        说明:本程序主要内容,来源于网络大神,如有异议,请及时联系作者。

四、项目运行结果视频展示

        本阶段TFT屏幕上部,第一行,可以显示阳历日期、星期几、农历日期;第二行显示实时网络时间(东八东,偏移量28800秒),每秒更新一次;
        中部,实时天气和明天天气预报部分,目前仍显示静态画展,没有加入实时获取天气信息的代码。
        下部,显示当前所在环境实时的温度和湿度数据,每3秒更新一次。        

WeatherClock_exalmpl4

五、项目第四专题代码下载

        百度网盘下载链接:
        https://pan.baidu.com/s/1tBrHF5KNcwBR-2-HXvNQdw?pwd=bw9e,提取码:bw9e

        参考文档
        1. NTPClient 部分源代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1650802.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Docker安装MySQL5.7.36

拉取镜像并查看 docker pull mysql:5.7.36拉取成功后查看&#xff08;非必须&#xff09; docker images创建并设置宿主机 mysql 配置文件目录和数据文件目录 创建相关文件夹将容器中的mysql数据保存到本地&#xff0c;这样即使容器被删除&#xff0c;数据也不会丢失。 mkd…

牛客NC142 最长重复子串【中等 字符串 Java/Go】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/4fe306a84f084c249e4afad5edf889cc 思路 注意&#xff1a;题目给的时间复杂度是O(N^2)那么直接套用双重循环&#xff1a;外层循环i为假定起始重复子串的初始位置&#xff0c;内层循环的j为假定重复子串的结束位置…

每日一题5:Pandas-修改列

一、每日一题 一家公司决定增加员工的薪水。 编写一个解决方案&#xff0c;将每个员工的薪水乘以2来 修改 salary 列。 返回结果格式如下示例所示。 解答&#xff1a; import pandas as pddef modifySalaryColumn(employees: pd.DataFrame) -> pd.DataFrame:employees.loc[…

人工神经网络初步

大家好&#xff0c;这里是七七&#xff0c;由于各种比赛的缘故&#xff0c;使用了很多人工神经网络模型。但是很多的原理都不是很明白&#xff0c;就导致了不能灵活地运用┭┮﹏┭┮。为此&#xff0c;去看了些人工神经网络原理书&#xff0c;写下此专题。 在进入正文之前要先…

Delta lake with Java--数据增删改查

之前写的关于spark sql 操作delta lake表的&#xff0c;总觉得有点混乱&#xff0c;今天用Java结合真实的数据来进行一次数据的CRUD操作&#xff0c;所涉及的数据来源于Delta lake up and running配套的 GitGitHub - benniehaelen/delta-lake-up-and-running: Companion reposi…

Mac idea gradle解决异常: SSL peer shut down incorrectly

系统&#xff1a;mac 软件&#xff1a;idea 解决异常: SSL peer shut down incorrectly 查看有没有安装 gradle -v安装 根据项目gradle提示安装版本 brew install gradle7idea的配置 在settings搜索gradle&#xff0c;配置Local installation&#xff0c;选择自己的安装目录…

Unity Shader中获取像素点深度信息

1.顶点着色器中对深度进行计算 v2f vert(appdata v) {v2f o;o.pos UnityObjectToClipPos(v.vertex);o.uv TRANSFORM_TEX(v.uv, _MainTex);o.depth (o.pos.z / o.pos.w 1.0) * 0.5; // Normalize depth to [0, 1]return o; }但是达不到预期&#xff0c;最后返回的值一直大于…

连通“数据”,让制造变“聪明”

说起数据智能&#xff0c;你第一时间想到的是什么呢&#xff1f;是科技感十足的智慧城市&#xff1f;还是炫酷的人工智能景象&#xff1f; 数据作为企业的战略资产越来越受到重视&#xff0c;从最初的数据协助业务协同&#xff0c;转化为数据驱动业务&#xff0c;数据驱动运营…

Hive两代命令行客户端(Hive、Beeline)

Hive命令行客户端 Hive有两个主要的客户端工具&#xff0c;分别是旧版的Hive CLI&#xff08;Command Line Interface&#xff09;和新版的Beeline。 Hive CLI&#xff1a; Hive CLI 是 Hive 最早期的命令行客户端工具&#xff0c;它使用 JDBC 连接到 Hive 服务器&#xff0c;…

栈的2道面试题【有效的括号】【用栈实现队列】

栈的面试题&#xff1a; 1.有效的括号 题目&#xff1a; 有效的括号 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合…

深入剖析Tomcat(七) 日志记录器

在看原书第六章之前&#xff0c;一直觉得Tomcat记日志的架构可能是个“有点东西”的东西。在看了第六章之后呢&#xff0c;额… 就这&#xff1f;不甘心的我又翻了翻logback与新版tomcat的源码&#xff0c;额…&#xff0c;日志架构原来也没那么神秘。本篇文章先过一遍原书内容…

为什么会查询不到DNS信息?怎么排查?

DNS&#xff08;域名系统&#xff09;是将域名转换为相应 IP 地址的关键系统。查询 DNS 信息具有重要作用&#xff0c;通过查询 DNS 信息&#xff0c;我们可以知道域名对应的 IP 地址&#xff0c;这是最主要的信息&#xff0c;使设备能与目标服务器进行通信&#xff1b;其次是域…

网络 IO 模式

同步 IO 与异步 IO 同步 IO 和异步 IO 是关于数据读写方式的两种不同模式。 同步 IO 是指在程序读写数据时&#xff0c;需要等待操作完成后才能继续执行后面的程序。这种模式下&#xff0c;当程序使用阻塞式 IO 时&#xff0c;会一直等待IO操作完成&#xff0c;程序会暂停执行…

Python中设计注册登录代码

import hashlib import json import os import sys # user interface 用户是界面 UI """ 用户登录系统 1.注册 2.登陆 0.退出 """ # 读取users.bin def load(path): return json.load(open(path, "rt")) # 保存user.bin def save(dic…

Autosar NvM配置-手动配置Nvblock及使用-基于ETAS软件

文章目录 前言NvDataInterfaceNvBlockNvM配置SWC配置RTE Mapping使用生成的接口操作NVM总结前言 NVM作为存储协议栈中最顶层的模块,是必须要掌握的。目前项目基本使用MCU带的Dflash模块,使用Fee模拟eeprom。在项目前期阶段,应该充分讨论需要存储的内容,包括应用数据,诊断…

ETLCloud工具怎么实现多流SQL实时运算?

多流SQL实时运算的特点和应用场景 多流SQL实时运算是一种先进的数据处理技术&#xff0c;它在大数据处理领域中扮演着至关重要的角色&#xff0c;尤其是在需要对多个数据流进行实时分析和处理的应用场景中。该技术结合了SQL&#xff08;结构化查询语言&#xff09;的易用性和流…

15.计算机网络

1.物理层的互联设备 中继器 和 集线器 2.集线器可以看做特殊的多路中继器 集线器 不可以做到自动寻址的功能 3.数据链路层 网桥 和 交换机 4.交换机是多端口网桥 5.网络层 路由器 6.应用层 网关 7.广播域 网络层 可以形成多个广播域 冲突域 网络层数据链路层 可以形成多个冲突域…

matlab 基于拉依达检验法(3σ准则) 实现多类别多参数的批量异常样本检验 V2.0

简介 拉依达检验法&#xff08;3σ准则&#xff09;是一种统计学方法&#xff0c;用于检测数据中的异常值。这种方法基于正态分布的特性来确定数据点是否可能是异常值。以下是关于拉依达检验法&#xff08;3σ准则&#xff09;的详细介绍&#xff1a; 基本原理&#xff1a; 拉…

分布式锁概述

什么是分布式锁 分布式锁是一种在分布式计算环境中用于同步访问共享资源的机制。它的主要目的是在一个分布式系统中&#xff0c;当多个进程或服务需要同时访问同一个资源时&#xff0c;确保任一时刻只有一个进程或服务能够执行涉及该资源的关键操作。这类似于传统单体应用中的…

C语言判断字符旋转

前言 今天我们使用c语言来写代码来实现字符串选择的判断&#xff0c;我们来看题目 题目描述 写一个函数&#xff0c;判断一个字符串是否为另外一个字符串旋转之后的字符串。 例如&#xff1a;给定s1 AABCD和s2 BCDAA&#xff0c;返回1 给定s1abcd和s2ACBD&#xff0c;返回0. A…