开发者学堂课程【HaaS 物联网应用开发课程:5_1_3_HaaS 首页信息屏_网络状态_网络对时】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/801/detail/13844
5_1_3_HaaS 首页信息屏_网络状态_网络对时
内容介绍:
一、系统网络状态获取
二、 网络对时
一、系统网络状态获取
1.网络管理模块 netmgr
系统网络状态主要包含: Wi-Fi 连线状态和 Wi-Fi IP 地址
经过之前的学习,AliOS Things 进 行网络管理功能的是 Netmgr,Netmgr 的架构如下图:
Wi-Fi 具体的操作通过 Netmgr 进行统一封装,对应用层提供接口,方便的对 Wi-Fi 网络进行管理。
netmgr 主要对上 APP 提供API,对下的Wi-Fi对接提供 HAL 抽象层。
netmgr 内部由多个子模块组成,各个子模块主要是负责 Wi-Fi 初始化,Wi-Fi 动态管理,DHCP 获取 IP 地址,SNTP获取网络时间等。
Netmgr 对应用层提供的最常用的 API 如下:
① 初始化 Wi-Fi
int netmgr_init(void);
② 启动 netmgr
定义:
int netmgr_start(bool autoconfig);
其中 autoconfig 参数代表是否需要回连路由器,如果之前成功连接过路由器,并且路由器的s sid、password 的配置信息已经记录在系统上,autoconfig 这个变量设置成 TRUE 之后,netmgr 就会自动尝试连接路由器;在 autoconfig设置成 False 之后,,netmgr 不会自动尝试连接路由器。
参数说明:
autoconfig: true 自动连接网络;false 不自动连接网络
③ 连接 Wi-Fi
定义:
int32_t netmgr_connect(const char *ssid,const uint8_t *password,uint32_t timeout);
参数说明:
ssid:连接 wi-Fi 的名字
password:连接 wi-Fi 的密码,没有密码为空,指定为 NULL
timeout:连接超时时间,单位 ms,建议设置成10左右
用例:
netmgr_connect("ssid",“passwd",10 * 1000 ) ;
从这些 API 我们可以看出,通过 netmgr 提供的 API,使用 Wi-Fi 的功能是非常方便的。
2.软件流程及代码
主要分为三部分:第一,网络初始化
在系统启动之后,首先会呼叫 netmgr init 对 Wi-Fi 进行初始化,接下来会呼叫 AOS 提供的 API 注册 Wi-Fi 事件通知的回调函数,注册这个回调函数之后,在 Wi-Fi 发生连线成功断线或者是成功获取 IP 地址等事件之后,netmgr 会主动呼叫这个回调函数,通知有这些事件发生,之后就可以根据这些事件来进行合适的处理。最后是呼叫netmgr starter 来启动 netmgr 模块。
第二,Wi-Fi 事件监听
在 edu demoAPP 中,需要使用两个变量分别记录 Wi-Fi 的连线状态和 Wi-Fi 是否获取到 IP 地址的状态。中间是 Wi-Fi 事件通知回调函数中的处理逻辑,这个函数会根据收到的 Wi-Fi 事件对这两个变量的状态进行更新,如果收到的是断线状态,则会把这两个变量都清成零。如果收到的是连线状态,则会把 wifi_connected 变量设置成1,如果收到的是获取到 IP 地址事件,那会把 get_ipaddr、wifi connected 都设置成1。
第三,Wi-Fi 事件显示
两个变量是在 on page task 的主线程中使用的。如果判断 Wi-Fi 连接成功,也就是 wifi connected 变量是1,会在OLED 屏幕上绘制 Wi-Fi 的图标。如果 get ipaddr=1就代表获取 IP 地址成功,然后会把 IP 地址的信息显示在 OLED显 示屏上。
实际代码如下:
网络初始化- application/example/edu_ demo/app_ entry.c
#i fndef CONFIG ENABLE L INKKIT
netmgr_init();
aos regi ster event filter(EV_ WIFI, wifi. service_ event, NULL);
netmgr- start(true);
#else
linkit_ app_ start();
#endif
CONFIG ENABLE L INKKIT 代表会使用到 linkit_ app 的功能,在做 linkit_ app_ start();它里面会做 netmgr_init()等API
网络状态监控- application/example/edu_ demo/app_ entry.c
wifi_ connected: Wi-Fi 是否连接成功
get_ ipaddr: 是否获取到 IP 地址
static void wifi_ service event(input_ event t *event, void *priv_ data)
{
if (event->type != EV WIFI) {
retu
r
n;
}
swi tch event ->code)
{
case CODE WIFI ON_ DISCONNECT:
wifi_connected = 0;
get_ipaddr =0;
break;
case CODE WIFI. ON CONNECTED:
wifi_connected =1;
break;
case CODE WIFI. ON GOT_IP:
wifi_Connected=1;
get _ipaddr = 1;
break;
default:
break;
}
网络状态显示- application/example/edu_ _demo/homepage
/homepage.c
if (wifi_ connected) 如果 Wi-Fi 连接成功,则显示 Wi-Fi 图标
{
OLED_ Icon_Draw(86, 0,&icon_ wifi._on_ 12_12,0);
}
if (get_ipaddr)
{
netmgr_stats(0, &stats); 如果获取IP地址成功 ,则打印IP地址信息
if (stats. ip_ available)
{
snprintf(image_ version, 20, "IP: %s" ,stats.ip);
OLED_Show _String(20, (12 + 4) * 3,image version, 12,1);
}
}
通过上述代码可以看出:如果 Wi-Fi 连接不成功、未获取 IP 地址时,Wi-Fi 图标以及 IP 地址信息时不会显示在屏幕上的。
二、网络对时
1.系统时间显示效果
首页信息屏的左上角显示的是系统时间,在连接 Wi-Fi 之前显示的是未经校准的时间,以系统开机时间为基准;连网成功之后显示的是所在东八区的时间。
2.网络对时原理
没 有RTC , HaaS EDU K1是如何获取到正确的时间并显示在屏幕上的:
简单网络时间协议( Simple Network Time Protocol) , 由 NTP 改编而来,主要用来同步 Internet 中的计算机时钟,在RFC2030标准中定义。
SNTP 协议采用客户端/服务器的工作方式,可以采用单播(点对点)或者广播(一点对多点)模式操作。SNTP 服务器通过接收 GPS 信号或自带的原子钟作为系统的时间基准。
➢单播模式下, SNTP 客户端能够通过定期访问 SNTP 服务器获得准确的时间信息,用于调整客户端自身所在系统的时间,达到同步时间的目的。
➢广播模式下, SNTP 服务器周期性地发送消息给指定的 IP 广播地址或者 IP 多播地址。SNTP 客户端通过监听这些地址来获得时间信息。
网络中一般存在很多台 SNTP 服务器,客户端会通过一定的算法选择最好的几台服务器使用。
3. SNTP 协议实现模块
SNTP 协议实现: components/network/sntp/sntp.c
主要 API :
①
@brief Set the remote ntp server address by name .
@note
@param idx: numdns the index of the NTP server to set must be < SNTP MAX. SERVERS
arg: servers name that want to set. such as:” cn.ntp. org. cn" 118.24. 4.66"
@
retval 0:success -1:fail
/
int sntp_set_server(uint8_t idx, char *server)
②
@brief Get time from the remote server.
@note This a blocking interface .
@param ntp_time: Pointer to the struct timeval.
arg: The pointer of sntp module parameter
@retval 0:success -1:fail
/
int sntp_get_time(sntp_arg *arg, struct timeval *ntp._time )
sntp_ get _time 这个 API 是- -个阻塞性的 API ,在获取到 SNTP 时间之后才会返回,所以在实际使用的
时候为了避免 block 主线程的流程,一般呼叫这个 API 的操作是在单独的一个 task 中执行的。
4.SNTP 对时操作
使用 SNTP 协议实现层提供的 API 进行网络对时操作需要2个步骤:
(1)设置 NTP 服务器地址
static char *m sntp- servaddr[SNTP MAX_ SERVERS] = {
”
cn. pool.ntp.org",
”0
.cn. pool.ntp.org",
"1. cn. pool.ntp .org",
};
(2)开始对时操作(对时成功, SNTP 协议实现模块中会将时间设置到系统中)
AliOS Things SNTP 服务入口: components/network/netmgr
/src/wifi_service.c
static void wifi_sntp_task(void)
{
aos_task_t sntp task;
if(g_is_ sntp_task_running == false) {
WIFI_SERVICE_LOGI("start sntp task . \n" );
aos .task new_ext(&sntp_task, "sntp_ task", wifi_sntp_handle, NULL, 4*1024, WIFI_SERVICE_SNTP PRI);
} else {
WIFI_SERVICE_LOGEC"sntp task is already running.\n" );
}
}
上面代码中会启动一个名为 sntp_task 的任务进行 SNTP 对时操作,接下来看这个 task 中的逻辑是如何实现的:
components/network/netmgr/src/wifi_ service.c
主要逻辑是在 wifi_sntp-handle API 里实现的,进来之后会设置 SNTP task running 标志为 TRUE。
在 wifi_sntp-handle API 里,首先会呼叫
hal_wifi_get_default_module()以获取默认 Wi-Fi 模块的 instance;然后会呼叫 sntp_start ()开始 sntp 对时的过程,如果 sntp 返回成功代表对时成功,这时 netmg_stat_chg_event 会广播 NOTIFY_SNTP_SUCCESS;然后会启动延时任务进行时间同步。
如果失败,会广播 NOTIFY_SNTP_FAILED,同时也会设置延时任务进行时间同步。
static V
oid
pertodic
_
sntp_handco
{
Hal_wifi_module_t* m;
WIFI_SERVICE_LOGDC"%s:%d\n", _ _funC_ _, _ _LINE_ _);
m – hal_wifi_get_default_module();
if(m==NULL){
WIFI_SERVICE_LOGEC"%s:%d get module failed\n", _ _func_ _ LINE_ _);
return ;
}
if(sntp_start
()=
=true) {
netmg_stat_chg_event(m, NOTIFY_SNTP_SUCCESS, NULL);
aos_ post_delayed_action(SNTP_PERIODIC_SYNC_ INTERVAL_MS, periodic_sntp_handle, NULL);
WIFI SERVICE LOGIC "%s:%d cancel sntp action\n",_
_
func_ _ LINE_ _ );
}
Else {
Netmgr_stat_chg_ event(m, NOTIFY_SNTP_FAILED, NULL);
WIFI_SERVICE_LOGIC "%s:%d cancel_sntp_action\n",_ _func_ _,. _ _LINE_ _ );
Aos
_
cancel_delayed_action(SNTP_PERIODIC_SYNC_INTERVAL MS, periodic_sntp_handle, NULD);
static void wifi_sntp_handLe(void arg)
{
(void) arg
g_is_ sntp_task_ running - true;
WIFI_SERVICE_LOGI("%s:%d start sntp action\n",_ _func_ _
,
_ _LINE
_
_ );
periodic_ sntp_handle();
g_is_sntp_ task running=false;
}
Sntp_start 也是在 components/network/netmgr/src/wifi_ service.c 里面
第一个代码:
W
hile(1){
WIFI
_SERVICE_LOGI(“sntp_gotting time”
)
sntp
_config_servaddr()
;
if
(0==sntp_get_time
(&m
_sntp_arg,&ntp_time)
){
WIFI_SERVICE_LOGI(“[sntp] OK,see%1d usec %1d\n,”ntp_time.tv_sec,ntp_time.tv_usec);
return true ;
} else if((g_wifi_conn_info->state != CONN_ STATE_CONNECTED)
&& (g_wifi_conn_info->state 口CONN STATE OBTAINING IP)
&&
(
g_wifi_conn_info->state != CONN STATE NE TWORK CONNECTED)) {
WIFI_ SERVICE_LOGEC"sntp task quit round for network is di sconnected\n" );
return false;
}
else {
int retry_time . retry_backof f_time [round];
WIFI_ SERVICE_LOGI(" [sntp] wait for sntp done.. .e\n");
if(round < WIFI SERVICE_RETRY_MAX_NUM-1) {
round ++ ;
}
aos_ msleep(retry- time . 1000);
}
第二个代码:
static char *m sntp_ servaddrCSNTP MAX_ SERVERS] 一{
"cn. pool .ntp
.
org"
“0. cn. pool .ntp.org”
"1. N. pool .ntp .org",
};
static void sntp_config_servaddr(void)
{
Int i=0
WIFI_SERVICE_LOGIC"sntp config servadd start. ");
for Ci = 0; i < SNTP MAX SERVERS; i+t)
if (0 !=sntp_set_server(i, m_sntp_servaddr[
i
])) {
WIFI_SERVICE_LOGE(set_sntp_server:%s fatLea\n,m_sntp_ servaddr[i]);
}
else {
WIFI_SERVICE_LOGIC"set sntp server:%s successfully\n", m_ sntp_ servaddr[i]);
}
}
WIFI_ SERVICE
_
LOGIC"sntp config servadd end.");
进入第一个代码之后,第一步是呼叫 sntp_config_servaddr(),第二个代码就会呼叫 sntp_set_server,然后将"cn. pool .ntp.org"
“0. cn. pool .ntp . org”、"1. N. pool .ntp . org"三个 ntp server 的域名设置到 sntp 协议里面。
第二步,呼叫 sntp_get_time 进行 sntp 对时操作,如果 sntp_get_time 返回成功,则代表此次对时成功;如果失败要返回判断当前 Wi-Fi 是否是连线状态,如果 Wi-Fi 断线,直接返回 FALSE,结束本次对时操作;如果 Wi-Fi 是连接状态,等待一个 backoff 的时间之后尝试重新对时.
5.获取系统时间
以下是显示系统时间的逻辑:
void homepage_ task( void)
{
unsigned char c = 0 ;
struct tm *info;
struct timespec tv;
uint8_t image_ version[22];
uint8_t tmp[22];
netmgr_stats_t stats;
while (1)
{
OLED_ Clear();
/*
获取
GMT时间*/
clock_ gettime(CLOCK_ REALTIME, &tv);
//printf(”r\n=====timespec LS:%d===\r\n",tv);
info = gmtime(&tv);
snprintf( tmp, 21,"%2d:%02d", (info->tm_ hour) % 24,info->tm_ min);
OLED_ Show_ String(0, 12 * 0, tmp, 12, 1);
在 homepage_ task的while(1)中,先清空屏幕,后通过呼叫 clock_ gettime 获取系统时间,获取之后将时间转化为时分秒的格式,最后呼叫O LED_ Show_ String 将系统时间打印到屏幕。
注意:clock_ gettime 里的第一个参数必须是 CLOCK_ REALTIME,这样获取到的是 jmt 的时间,否则获取的是系统启动的时间。