[RVB2601 Creative Application Development] 5. Using NTP Timer RTC Clock
[Copy link]
This post was last edited by anni_zzg on 2022-5-25 11:42
After figuring out the RTC clock, I started thinking about designing the time through the serial port. Later, I learned that I could use NTP for timing. I was suddenly inspired and decided to start working on it.
1. Download and install the NTP component first.
The previous tests were all performed in the ch2601_helloworld project, so the NTP function was also operated in this project.
First, right-click on any component in the open project and select View Details Web... to enter the component resource interface.
Enter the component you want to view, then search and install NTP
2. Code implementation.
(1) Network settings . You can refer to the ch2601_webplayer_demo project. At least its network function is not a big problem (UDP has some problems)
Add the following code to the init.c file
netmgr_hdl_t app_netmgr_hdl;
void w800_in(int linkid, void* data, size_t len, char remote_ip[16], uint16_t remote_ports)
{
uint8_t* d;
d = (uint8_t*)data;
printf("recv data:%s", d);
}
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, "CMCC-GEFm", 9, "24r37ph4", 8); //无线配置
// w800_packet_input_cb_register(&w800_in);//初始化输入回调函数
netmgr_start(app_netmgr_hdl); //使能网络设备
}
}
void board_yoc_init()
{
board_init(); // 板级初始化
event_service_init(NULL); // 发布订阅服务初始化
console_init(CONSOLE_UART_IDX, 115200, 512); // 串口初始化
ulog_init(); // 日志初始化
aos_set_log_level(AOS_LL_DEBUG); // 配置默认日志打印级别
int ret = partition_init(); // 分区初始化
if(ret <= 0) {
LOGE(TAG, "partition init failed");
} else {
LOGI(TAG, "find %d partitions", ret);
}
aos_kv_init("kv"); // kv文件系统初始化,可用于保存网络ssid&psk
network_init(); // 网络初始化
board_cli_init(); // 命令行初始化并注册默认的命令
}
Add the following code to main.c:
static void network_event(uint32_t event_id, const void* param, void* context)
{
switch(event_id) {
case EVENT_NETMGR_GOT_IP:
// LOGD(TAG, "net got ip");
ntp_sync_time(NULL);
break;
case EVENT_NETMGR_NET_DISCON:
LOGD(TAG, "net disconnect");
if((int)param == NET_DISCON_REASON_DHCP_ERROR) {
netmgr_reset(netmgr_get_handle("wifi"), 30); // 本次网络连接失败,30s后自动重连
}
break;
}
app_exception_event(event_id);
}
int main(void)
{
board_yoc_init();
LOGD(TAG, "%s\n", aos_get_app_version());
// uart0_init();
oled_init();
led_init();
key_init();
timer_init();
aita_InitTimer();
rtc_init();
IIC_Init();
SGP30_Init();
LED_RED_OFF;
LED_GREEN_OFF;
LED_BLUE_OFF;
aos_timer_start(&polling_timer);
event_subscribe(EVENT_NETMGR_GOT_IP, network_event, NULL); //订阅服务
event_subscribe(EVENT_NETMGR_NET_DISCON, network_event, NULL);
return 0;
}
(2) Adding RTC clock
aos_timer_t polling_timer; // timer for polling
void pollingTimerCb(void* arg1, void* arg2)
{
csi_rtc_time_t rtc;
csi_rtc_get_time(&aita_rtc, &rtc);
strftime(oled_buf, sizeof(oled_buf), "%Y-%m-%d", &rtc);
show_string(32, 0, oled_buf);
strftime(oled_buf, sizeof(oled_buf), "%T", &rtc);
show_string(40, 16, oled_buf);
SHT30_read_result();
SGP30_read_result();
strftime(oled_buf, sizeof(oled_buf), "%Y-%m-%d %T", &rtc);
LOGD(TAG, "%s\n", oled_buf);
}
// Init and startup periodic timer for polling job
void aita_InitTimer(void)
{
int ret = -1;
LOGI(TAG, "initialize timer for polling job!\n");
ret = aos_timer_new_ext(&polling_timer, pollingTimerCb, "args", 1000, 1, 0);
if(ret != 0) {
LOGE(TAG, "polling_timer create failed\n");
}
}
void rtc_init(void)
{
csi_error_t ret;
ret = csi_rtc_init(&aita_rtc, 0);
if(ret == CSI_OK)
printf("csi_rtc OK\n");
else
printf("csi_rtc wrong\n");
}
(3) Adding NTP function
/* ntpclient.c */
#include <aos/aos.h>
#include <sys/select.h>
#include <lwip/netdb.h>
#include <arpa/inet.h>
#include <drv/rtc.h>
static const char* TAG = "NTP";
#ifndef CONFIG_NTP_CTS_ZONE
#define CTS_ZONE 8
#else
#define CTS_ZONE CONFIG_NTP_CTS_ZONE
#endif
#define VERSION_3 3
#define VERSION_4 4
#define MODE_CLIENT 3
#define MODE_SERVER 4
// NTP protocol Content
#define NTP_LI 0
#define NTP_VN VERSION_3
#define NTP_MODE MODE_CLIENT
#define NTP_STRATUM 0
#define NTP_POLL 4
#define NTP_PRECISION -6
#define NTP_HLEN 48
#define NTP_PORT 123
#define TIMEOUT 3
#define BUFSIZE 128
#define JAN_1970 0x83aa7e80
#define NTP_CONV_FRAC32(x) (uint64_t)((x) * ((uint64_t)1 << 32))
#define NTP_REVE_FRAC32(x) ((double)((double)(x) / ((uint64_t)1 << 32)))
#define NTP_CONV_FRAC16(x) (uint32_t)((x) * ((uint32_t)1 << 16))
#define NTP_REVE_FRAC16(x) ((double)((double)(x) / ((uint32_t)1 << 16)))
#define USEC2FRAC(x) ((uint32_t)NTP_CONV_FRAC32((x) / 1000000.0))
#define FRAC2USEC(x) ((uint32_t)NTP_REVE_FRAC32((x)*1000000.0))
#define NTP_LFIXED2DOUBLE(x) \
((double)(ntohl(((struct l_fixedpt*)(x))->intpart) - JAN_1970 + \
FRAC2USEC(ntohl(((struct l_fixedpt*)(x))->fracpart)) / 1000000.0))
struct s_fixedpt {
uint16_t intpart;
uint16_t fracpart;
};
struct l_fixedpt {
uint32_t intpart;
uint32_t fracpart;
};
struct ntphdr {
unsigned int ntp_mode : 3;
unsigned int ntp_vn : 3;
unsigned int ntp_li : 2;
uint8_t ntp_stratum;
uint8_t ntp_poll;
int8_t ntp_precision;
struct s_fixedpt ntp_rtdelay;
struct s_fixedpt ntp_rtdispersion;
uint32_t ntp_refid;
struct l_fixedpt ntp_refts;
struct l_fixedpt ntp_orits;
struct l_fixedpt ntp_recvts;
struct l_fixedpt ntp_transts;
};
in_addr_t inet_host(const char* host)
{
in_addr_t saddr;
struct hostent* hostent;
if((saddr = inet_addr(host)) == INADDR_NONE) {
if((hostent = gethostbyname(host)) == NULL) {
return INADDR_NONE;
}
// memcpy(&saddr, hostent->h_addr_list[0], hostent->h_length);
saddr = *((unsigned long*)hostent->h_addr_list[0]);
}
return saddr;
}
int get_ntp_packet(void* buf, size_t* size)
{
struct ntphdr* ntp;
struct timeval tv;
if(!size || *size < NTP_HLEN) {
return -1;
}
memset(buf, 0, *size);
ntp = (struct ntphdr*)buf;
ntp->ntp_li = NTP_LI;
ntp->ntp_vn = NTP_VN;
ntp->ntp_mode = NTP_MODE;
ntp->ntp_stratum = NTP_STRATUM;
ntp->ntp_poll = NTP_POLL;
ntp->ntp_precision = NTP_PRECISION;
gettimeofday(&tv, NULL);
ntp->ntp_transts.intpart = htonl(tv.tv_sec + JAN_1970);
ntp->ntp_transts.fracpart = htonl(USEC2FRAC(tv.tv_usec));
*size = NTP_HLEN;
return 0;
}
double get_offset(const struct ntphdr* ntp, const struct timeval* recvtv)
{
double t1, t2, t3, t4;
t1 = NTP_LFIXED2DOUBLE(&ntp->ntp_orits);
t2 = NTP_LFIXED2DOUBLE(&ntp->ntp_recvts);
t3 = NTP_LFIXED2DOUBLE(&ntp->ntp_transts);
t4 = recvtv->tv_sec + recvtv->tv_usec / 1000000.0;
return ((t2 - t1) + (t3 - t4)) / 2;
}
struct tm tm_val;
void aita_print_ntp(struct ntphdr* ntp)
{
time_t time;
// by author. get ntp time, set into rtc then show datetime once
time = ntohl(ntp->ntp_recvts.intpart) - JAN_1970 + CTS_ZONE * 3600;
; // with timezone offset
char tbuf[64];
tm_val = *(localtime(&time));
extern csi_rtc_t aita_rtc; // rtc device descriptor
csi_rtc_set_time(&aita_rtc, &tm_val);
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %T", &tm_val);
printf("aita datetime: %s\n", tbuf);
}
static int _ntp_sync_time(char* server)
{
char buf[BUFSIZE];
size_t nbytes;
int sockfd, maxfd1;
struct sockaddr_in servaddr = {
0,
};
fd_set readfds;
struct timeval timeout, recvtv, tv, rcvtimeout = { 3, 0 };
double offset;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(NTP_PORT);
if(server == NULL) {
// 1.cn.pool.ntp.org is more reliable
servaddr.sin_addr.s_addr = inet_host("ntp1.aliyun.com");
LOGD(TAG, "ntp1.aliyun.com");
} else {
servaddr.sin_addr.s_addr = inet_host(server);
LOGD(TAG, "%s", server);
}
printf("--------------------->check server done! _ntp_sync_time()\n");
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
// LOGE(TAG, "socket error");
return -1;
}
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeout, sizeof(struct timeval));
if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr)) != 0) {
// LOGE(TAG, "connect error");
close(sockfd);
return -errno;
}
printf("--------------------->connect server done! _ntp_sync_time()\n");
nbytes = BUFSIZE;
if(get_ntp_packet(buf, &nbytes) != 0) {
// LOGE(TAG, "construct ntp request errorr");
close(sockfd);
return -1;
}
send(sockfd, buf, nbytes, 0); //发送一个UDP数据包
printf("--------------------->send request pack done! _ntp_sync_time()\n");
FD_ZERO(&readfds); //将集合readfds清零
FD_SET(sockfd, &readfds); //将sockfd加入集合
maxfd1 = sockfd + 1;
timeout.tv_sec = TIMEOUT;
timeout.tv_usec = 0;
if(select(maxfd1, &readfds, NULL, NULL, &timeout) > 0) {
if(FD_ISSET(sockfd, &readfds)) {
if((nbytes = recv(sockfd, buf, BUFSIZE, 0)) < 0) {
// LOGE(TAG, "recv error");
close(sockfd);
return -1;
}
// printf("nbytes = %d\n", nbytes);
// print_ntp((struct ntphdr *) buf);
aita_print_ntp((struct ntphdr*)buf);
gettimeofday(&recvtv, NULL);
offset = get_offset((struct ntphdr*)buf, &recvtv);
gettimeofday(&tv, NULL);
// TODO: ctime has some problem
// char tbuf[64];
// strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %T", localtime(&tv.tv_sec));
// memset(&tv, 0, sizeof(tv));
// tbuf = ctime((time_t *)&tv.tv_sec);
// LOGD(TAG, "system time1:%s", tbuf);
#if 1
tv.tv_sec += (int)offset;
tv.tv_usec += offset - (int)offset;
if(settimeofday(&tv, NULL) != 0) {
LOGE(TAG, "set time error");
close(sockfd);
return -1;
}
LOGD(TAG, "--------------------->settimeofday done! _ntp_sync_time()\n");
// TODO: ctime has some problem
// LOGD(TAG, "ntp time:\t%s", ctime((time_t *) &tv.tv_sec));
#endif
}
} else {
close(sockfd);
LOGD(TAG, "--------------------->select body error! _ntp_sync_time()\n");
return -1;
}
close(sockfd);
LOGD(TAG, "--------------------->whole done! _ntp_sync_time()\n");
return 0;
}
int ntp_sync_time(char* server)
{
int ret = -1;
for(int i = 0; i < 2; i++) {
ret = _ntp_sync_time(server);
if(ret == 0) {
LOGD(TAG, "sync success");
break;
}
}
if(ret < 0) {
LOGE(TAG, "sync error");
}
return ret;
}
( 4) Modify the UDP connection function . UDP communication requires not only the remote IP and remote port, but also the local port. The local port sets a port value greater than 1024.
int32_t udp_local_port = 1338;
int w800_connect_remote(int id, net_conn_e type, char *srvname, uint16_t port)
{
int ret = -1;
int ret_id;
if (g_net_status < NET_STATUS_GOTIP) {
LOGE(TAG, "net status error\n");
return -1;
}
aos_mutex_lock(&g_cmd_mutex, AOS_WAIT_FOREVER);
atparser_clr_buf(g_atparser_uservice_t);
switch (type) {
case NET_TYPE_TCP_SERVER:
/* TCP Server can NOT ignore lport */
break;
case NET_TYPE_UDP_UNICAST:
printf("UDP_UNICAST mode\n");
ret = atparser_send(g_atparser_uservice_t, "AT+CIPSTART=%d,%s,%s,%d,%d", id, "udp_unicast", srvname, port, udp_local_port);
break;
case NET_TYPE_TCP_CLIENT:
ret = atparser_send(g_atparser_uservice_t, "AT+CIPSTART=%d,%s,%s,%d", id, "tcp_client", srvname, port);
break;
default:
LOGE(TAG, "type=%d err!", type);
return -1;
}
if (ret == 0) {
ret = -1;
if ((atparser_recv(g_atparser_uservice_t, "OK\n") == 0) \
&& (atparser_recv(g_atparser_uservice_t, "+EVENT=CONNECT,%d\n", &ret_id) == 0)) {
if (ret_id == id) {
ret = 0;
}
}
}
atparser_cmd_exit(g_atparser_uservice_t);
aos_mutex_unlock(&g_cmd_mutex);
return ret;
}
(5) For the display part, please refer to my experience sharing three and four.
3. Summary.
The implementation of NTP has gone through many pitfalls. At first, the network was connected, but NTP was not connected. I didn't know how to start. Fortunately, the RVB2601 activity group shared the RTC application example of netizen sonicfirr (OCC ID: firr) [Rockhead RVB2601 Creative Application Development] User Experience 06 -- NTP Timing - Rockhead RISC-V RVB2601 Activity Zone - Electronic Engineering World Forum (eeworld.com.cn) . In this program, the two structures aita_rtc_t and aita_tm_u defined by sonicfirr were removed. It has been verified that csi_rtc_time_t and time function tm can be converted and used.
4. Experimental Results
(1) By setting netmgr_config_wifi(app_netmgr_hdl, "CMCC-GEFm", 9, "24r37ph4", 8 ); //Configure to your own WIFI, as shown below, it means you get your own IP address and the WIFI network connection is successful.
(2) Timing and reading RTC
(3) OLED results display
|