作者:czxin
NTP全称Network time Protocol,即网络时间协议,顾名思义就是通过与网络服务器之间通信来达到时间校准的协议,它是基于UDP的协议,记住这里的UDP,和后面遇到的WIFi驱动Bug密切相关。有了这个协议加上RTC外设的配合,便可以使运行的设备精准报时。
要实现这个功能,首先必须连接网络。RVB2601板载一颗WiFi芯片W800,提供了基于AT命令的驱动接口:W800模组AT指令集。
在YoC架构下,所有的网络由网络管理器netmgr负责,这里也不例外。我们需要将W800挂载到netmgr下。
static void network_init() { w800_wifi_param_t w800_param; /* init wifi driver and network */ w800_param.reset_pin = PA21; w800_param.baud = 1*1000000; w800_param.cs_pin = PA15; w800_param.wakeup_pin = PA25; w800_param.int_pin = PA22; w800_param.channel_id = 0; w800_param.buffer_size = 4*1024; wifi_w800_register(NULL, &w800_param); app_netmgr_hdl = netmgr_dev_wifi_init(); if (app_netmgr_hdl) { utask_t *task = utask_new("netmgr", 2 * 1024, QUEUE_MSG_COUNT, AOS_DEFAULT_APP_PRI); netmgr_service_init(task); netmgr_config_wifi(app_netmgr_hdl, "xxxxxxxx", 8, "xxxxxxxxxx", 10); netmgr_start(app_netmgr_hdl); } }
这段代码来自例程webplayer,已经帮我们搭好了WiFi驱动和网络管理器之间的连接,只需要将“netmgr_config_wifi ”的第二个参数设为自己WiFi的SSID,第三个参数为SSID长度,然后第四个和第五个分别是密码和密码长度。
不过光有netmgr,我们的联网体验还不能达到最好。可以加入微服务组件uService,让系统为我们提供消息的发布服务。通过下面代码中的订阅方式,用户可以轻松掌握网络连接状况,通过回调函数network_event根据当前网络状况及时做出反应。
event_subscribe(EVENT_NETMGR_GOT_IP, network_event, NULL); event_subscribe(EVENT_NETMGR_NET_DISCON, network_event, NULL);
为了达到每次开机不用手动输入SSID和密码的繁琐,可以利用YoC的partition(分区管理)和KV(Key-Value存储系统),将SSID和密码以键值对的方式保存在Flash中。
做好上述工作,便可以使我们的板子轻松连接到互联网中,连接后最习惯的操作就是ping一下。
确保ping通之后开始加入NTP功能。
YoC提供了两个组件ntp和sntp。看全称sntp的s代表的是simple,那就选择简单的入门,原想着快速达成功能,可这才是一波三折的开始……
第一次编译,系统提示我没有找到“sntp_setservername”函数。感觉被泼冷水一般,心想着官方的例子也会出错?抱着肯定是自己使用不当的态度,开始追源代码。发现这个sntp的simple和协议没多大关系,是直接调用了lwip的ntp的app。
好吧,看来是我没有加入lwip才导致的这个错误。手动添加lwip之后再次编译,噢吼,又有error。CDK告诉我,lwip的sys_init()这个函数被重复定义了。怎么可能?拿出八倍镜细细看了一下错误信息,原来是我手动添加的lwip之前,系统就有lwip这个组件了。
那么这个“罪魁祸首”是谁呢?SAL,Socket Adapter Layer。
在AliOS Things移植过程中,如果需要支持外接Wifi/BLE等模,且TCP/IP协议栈运行模组侧; 则需要SAL和底层模组控制模块进行对接。 SAL功能对上层提供标准socket接口,使上层应用不感知TCP/IP协议栈运行在MCU侧还是通讯模组侧。
那么对于RVB2601来说外挂的W800刚好属于运行了TCP/IP协议栈的通讯模组。可是SAL中并没有加入完整的lwip,那么现实就是对于RVB2601来说有SAL就没有SNTP,为了保住联网功能,只能放弃SNTP了。后来也咨询了OCC的技术小二,验证了这个想法。
幸好还有一条路,使用NTP组件。这次编译的过程倒是很顺,但是我依然不敢放松警惕。果不其然,还是出“幺蛾子”了:系统显示WiFi连接成功了,ping也通了,而且W800也完成了“doamin to IP”的解析,获取了服务器的实际IP,但是最后却告诉我时间同步失败了,见下图最后一行“NTP sync error”。
分析这段log发现,有一行“w800_dev conn_start fail”。就很纳闷,w800不是连接正常的吗。继续思考,对照W800的AT指令逐条分析,回想着基于UDP的NTP协议,发现驱动的一个Bug。
先说结论,这个Bug会导致所有基于UDP的网络请求连接失败。那么是什么导致这个Bug呢?w800_api.c这个文件的“w800_connect_remote”这个函数在发起UDP请求时的AT命令格式错误,正确的UDP AT命令应该是
id,udp_unicast,ip,remote_port,local_port
而驱动竟然是
id,udp_client,ip,remote_port
不仅把udp_unicast写成udp_client(只有tcp才有tcp_client),还漏写了local_port这个参数。
代码追到这里已经快到凌晨24点了,如果还是连接不上那我也只能放弃了。好在并没有其他的Bug出现,成功完成了NTP网络时间校准功能。
因为我们所属东八区,所以返回的时间需要加上8小时,才是正确时间。
总结,通过NTP协议完成了时间校准功能,并修复W800的AT命令Bug,使其能够建立正确的UDP连接。
本文源自:平头哥芯片开放社区
欢迎关注公众号:芯片开放社区(ID:OCC_THEAD),查看更多应用实战文章。