基于esp32 的时间系统

一,简介

该系统拥有一个全局时间变量global_clk,该变量记录了当前的时间(年,月,日,小时,分钟,秒),并维持一个定时任务列表。用户可以添加定时任务,删除定时任务等。定时任务执行完成后会被移除列表,但不会被释放。

二,结构体

//日历结构体
struct calendar
{
	unsigned int second : 6 ;	//低位
	unsigned int minute : 6 ;
	unsigned int hour : 5 ;
	unsigned int date : 5 ;
	unsigned int month : 4 ;
	unsigned int year : 6 ;	//高位
};


//时间结构体,cal的值决定了value
union clk
{
	struct calendar cal;
	unsigned int value;
};

typedef union clk clk_t;
struct timer;
typedef unsigned char (*timer_cb)(struct timer *tmr ,void *arg);	//回调函数

//定时任务结构体
struct timer
{
	char *name;	//名称
    struct timer *next;	//指向下一个定时
    clk_t timeout;	//定时的时间
    timer_cb cb;	//回调函数
    void *arg;	//回调参数
};
extern clk_t global_clk;	//全局时间
extern struct timer *timer_list;	//定时任务列表

三,函数接口

void printf_tmrlist();	//打印定时列表
struct timer *tmr_new(clk_t *conf,timer_cb cb,void *arg,char *name); //新建闹钟
int update_clk(clk_t *clk);	//更新时间
int tmr_add(struct timer *tmr);	//添加定时任务
int tmr_remove(struct timer *tmr);	//移除定时任务
int tmr_delete(struct timer *tmr);	//删除定时任务
void tmr_set_global(clk_t conf);	//设置全局时间
clk_t get_current_nettime();	//获取当前时间
int global_clk_init();	//全局时间初始化
void tmr_process(void *arg);	//定时任务处理

void clock_task(void *arg);	//时间系统线程

四,使用示例

首先需要对全局时间来一个初始化,确定当前的时间,这个部分是通过网络获取北京时间的。同时,该示例设置一个定时任务,每24小时就通过网络更新时间,当24小时也就是一天的定时到了,update_global_cb()就会被执行。

/*
 * 定时器回调函数:更新全局时间
 */
timer_cb update_global_cb(struct timer *tmr, void *agr)
{
	clk_t t = get_current_nettime();
	tmr_set_global(t); //更新全局时间
	tmr->timeout.value = global_clk.value;
	tmr->timeout.cal.date += 1;

	tmr_add(tmr); //将更新任务再次添加进
	return NULL;
}
/*
 * 全局时间初始化
 * 获取网络事件,设置定时器,自动更新全局时间
 */
int global_clk_init()
{
	clk_t t = get_current_nettime();	//获取网络时间
	tmr_set_global(t); //更新全局时间
	t.cal.date += 1;	//日期+1
	tmr_new(&t, update_global_cb, NULL, "UPDATE"); //创建定时器,每24小时更新网络时间,并添加到列表
	return 1;
}

接下来,在线程中,每秒调用tmr_process();保证时间系统的正常运行。

/*
 * 时钟任务,每秒执行,处理定时任务及时间更新
 */
void clock_task(void *arg)
{
	esp_log_level_set(TAG, ESP_LOG_INFO);
	while (1)
	{

		vTaskDelay(1000 / portTICK_RATE_MS);
		tmr_process(NULL);
	}
}

五,代码详解

1,clk_t get_current_nettime();

该函数先调用获取网络时间的代码。esp32获取网络时间;将时间字符串转换成数字格式,复制到结构体并返回;

/*
 * 获取当前网络时间
 * 返回:时间结构体
 */
clk_t get_current_nettime()
{
	char *origin = get_Time_String(); //调用myhttp.c,获取时间字符串:"2021-03-24 04:49:53"

	char str[6][10] = {'\0'}; //year,month,date,hour,minute,second
	//printf("origin = %s\n", origin);

	//分别提取出时间
	for (int i = 0; i <= 5; i++)
	{
		char *c = str[i];
		while (!((*origin == '-') || (*origin == ' ') || (*origin == ':')))
		{
			if (*origin == '\0')
			{
				break;
			}
			*c = *origin;
			origin++;
			c++;
		}
		origin++;
	}
	unsigned int x[6] = {0};
	//将字符串转换成数字
	for (int i = 0; i < 6; i++)
	{
		x[i] = (unsigned int)atoi(str[i]);
		printf("x = %d\n", x[i]);
	}
	//赋值给时间结构体
	clk_t conf;
	conf.cal.year = x[0] - 2000;
	conf.cal.month = x[1];
	conf.cal.date = x[2];
	conf.cal.hour = x[3] + 8;
	conf.cal.minute = x[4];
	conf.cal.second = x[5];
	return conf;
}
2,struct timer *tmr_new(clk_t *conf, timer_cb cb, void *arg, char *name);
/*
 * 创建一个定时器 并添加进定时队列
 * conf:定时时间
 * cb:定时回调函数
 * arg:回调函数参数
 * name:定时器名
 * 返回:定时器结构体指针
 */
struct timer *tmr_new(clk_t *conf, timer_cb cb, void *arg, char *name)
{
	struct timer *tmr;
	clk_t clk;

	//tmr must > global_clk
	if (conf == NULL || (conf->value <= global_clk.value))
	{
		ESP_LOGI(TAG,"new tmr error\n");
		return 0;
	}
	tmr = (struct timer *)malloc(sizeof(struct timer));
	//chcek the conf

	clk.value = conf->value;
	update_clk(&clk); //规范化clk
	ESP_LOGI(TAG,"new timer %s: 20%d-%d-%d %d:%d:%d values =%u\r\n", name, clk.cal.year, clk.cal.month, clk.cal.date, clk.cal.hour, clk.cal.minute, clk.cal.second, clk.value);
	//printf("new timer %s: 20%d-%d-%d %d:%d:%d values =%u\r\n", name, clk.cal.year, clk.cal.month, clk.cal.date, clk.cal.hour, clk.cal.minute, clk.cal.second, clk.value);
	tmr->timeout.value = clk.value; //将clk赋值给定时器

	if (cb != NULL)
	{
		tmr->cb = cb;
	}
	if (arg != NULL)
	{
		tmr->arg = arg;
	}
	if (name != NULL)
	{
		char *dest = (char *)malloc(strlen(name));
		strncpy(dest, name, strlen(name) + 1);
		tmr->name = dest;
	}
	tmr->next = NULL;

	tmr_add(tmr);
	printf_tmrlist();
	return tmr;
}
/*
 * 更新时间结构体,使其成员符合时间规则
 * clk:要更新的时间
 * 返回:0 年份更新;1 年份未更新
 */
int update_clk(clk_t *clk)
{

	if (clk->cal.second >= 60)
	{
		//one minute
		clk->cal.second = clk->cal.second - 60;
		clk->cal.minute++;
		if (clk->cal.minute >= 60)
		{
			//one hour
			clk->cal.minute = clk->cal.minute - 60;
			clk->cal.hour++;
			if (clk->cal.hour >= 24)
			{
				//date +1
				clk->cal.hour = clk->cal.hour - 24;
				clk->cal.date = clk->cal.date + 1;
				switch (clk->cal.month)
				{
				case 1:
				case 3:
				case 5:
				case 7:
				case 8:
				case 10:
				case 12:
					if (clk->cal.date == 32)
					{

						goto exit;
					}
					break;
				case 2:
					//29 or 28 in february
					if (((clk->cal.year % 4) == 0 && clk->cal.date == 30) || ((clk->cal.year % 4 != 0) && (clk->cal.date == 29)))
					{

						goto exit;
					}
					break;
				default:
					if (clk->cal.date == 31)
					{
						goto exit;
					}
					break;
				}
			}
		}
	}
	return 1;
//month +1
exit:
	clk->cal.date = 1;
	clk->cal.month++;
	if (clk->cal.month == 13)
	{
		clk->cal.month = 1;
		clk->cal.year++; //max to 2064
	}
	return 0;
}
3, int tmr_add(struct timer *tmr);
/*
 * 将定时器插入链表,按时间先后顺序
 * tmr:要插入的定时器
 * 返回:0失败;1成功
 */
int tmr_add(struct timer *tmr)
{
	struct timer *t, *prev = NULL;
	ESP_LOGI(TAG,"adding timer to timerlist:");
	if (tmr == NULL)
	{
		ESP_LOGI(TAG, "new timer is null");
		return 0;
	}
	//若当前无定时器则,tmr将作为第一个定时器
	if (timer_list == NULL)
	{
		ESP_LOGI(TAG, "timer %s is the only timer",tmr->name);
		//first tmr
		tmr->next = NULL;
		timer_list = tmr;
	}
	else //按时间先后顺序将tmr插入链表
	{
		t = timer_list;
		//时间小的在前面
		while (((t->timeout.value) < (tmr->timeout.value)))
		{
			ESP_LOGI(TAG, "step for next timer");
			prev = t; //保存t的上一个
			if (t->next == NULL)
			{
				//若t是最后一个定时器
				t->next = tmr; //将tmr插入t后
				tmr->next = NULL;
				ESP_LOGI(TAG, "add timer %s to the tail of the list ",tmr->name);
				return 1;
			}
			t = t->next; //检查下一个定时器
		}
		//上一个为空,说明tmr是最早的定时器
		if (prev == NULL)
		{
			
			//tmr less than the first one
			tmr->next = timer_list; //tmr插入timer_list的前面
			timer_list = tmr;		//再更新timer_list
			ESP_LOGI(TAG, "add timer %s to the head of list",timer_list->name);
		}
		else
		{
			
			tmr->next = t; //tmr插入t之前

			prev->next = tmr; //tmr插入prev之后
			ESP_LOGI(TAG, "add timer %s behind %s",tmr->name,prev->name);
		}
	}
	return 1;
}
4,int tmr_remove(struct timer *tmr); and int tmr_delete(struct timer *tmr);
/*
 * 将定时器从链表移除
 * tmr:要移除的定时器
 * 返回 0失败;1成功
 */
int tmr_remove(struct timer *tmr)
{
	struct timer *t, *prev = NULL;
	if (tmr == NULL)
	{
		return 0;
	}

	//check if the tmr is in the list
	if (timer_list == NULL)
	{
		return 0;
	}
	t = timer_list;

	//find the tmr
	while (t != tmr)
	{
		if (t->next == NULL)
		{
			return 0; //no match
		}
		prev = t;
		t = t->next;
	}

	//t==tmr
	if (prev == NULL)
	{
		//t is the first
		timer_list = t->next;
	}
	else
	{
		if (t->next == NULL)
		{
			//t is the last
			prev->next = NULL;
		}
		else
		{
			//t in the middle
			prev->next = t->next;
		}
	}
	t->next = NULL;

	return 1;
}
/*
 * 删除定时器,释放定时器占用的内存
 * tmr:要删除的定时器
 * 返回:0失败,1成功
 */
int tmr_delete(struct timer *tmr)
{
	if (tmr == NULL)
	{
		return 0;
	}

	tmr_remove(tmr);

	free(tmr->name);

	free(tmr);

	return 1;
}
5,void tmr_process(void *arg);
clk_t global_clk; //全局时间
/*
 * 时间任务进程 负责更新全局时间以及触发定时器
 * 在定时器中断中,每秒执行
 */
void tmr_process(void *arg)
{

	struct timer *t;

	//update the global_clk
	global_clk.cal.second++;

	update_clk(&global_clk);

	//check if timer_list is null
	if (timer_list == NULL)
	{
		return;
	}
	//check the first timer in list
	t = timer_list;
	//printf("timer_list value = %d\n",t->timeout.value);
	if (global_clk.value == (t->timeout.value))
	{
		tmr_remove(t); //若要重复定时,只需要在回调函数中调用tmr_add()

		printf("timer %s\n", t->name);
		t->cb(t, t->arg); //callback func
	}
}
/*
 * 设置全局事件
 * conf:新的时间
 */
void tmr_set_global(clk_t conf)
{
	global_clk.value = conf.value;
}

在这里插入图片描述

  • 12
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
基于ESP32的自动浇花系统是一种利用物联网技术实现的智能设备。该系统通过ESP32微控制器和相应的传感器、执行器等组件,实现对植物的自动浇水控制。其工作原理如下: 首先,通过土壤湿度传感器感知植物根部的湿度情况,当土壤湿度低于设定阈值时,系统开始自动浇水。ESP32通过WiFi或蓝牙模块连接到互联网,可以远程监控和控制系统。 当系统检测到土壤湿度低于设定阈值时,ESP32会向水泵模块发出信号,打开水泵。水泵会将水源泵送到植物的根部,直到土壤湿度达到设定的最佳湿度。同时,系统会通过LED灯或触摸屏等输出装置在显示屏上显示当前的湿度情况。 此外,该系统具备一定的智能化功能。可以根据用户设置的浇水时间和周期,定时进行浇水。用户可以通过手机APP或者网页进行设置。 基于ESP32的自动浇花系统具有以下优点:一是方便易用,通过手机APP或网页就可以进行远程控制,大大方便了用户的操作;二是节水高效,系统可以根据植物实际需要进行浇水,避免了过度浇水导致的水资源浪费;三是智能化程度高,可以根据不同植物的需求,进行个性化的浇水设置,提高植物的生长质量。 总之,基于ESP32的自动浇花系统结合了物联网技术和植物生长的需求,能够实现对植物的精准浇水,方便实用,提高了植物的生长质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值