前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AIoT应用创新大赛-基于TencentOS Tiny 电量统计与显示软件

AIoT应用创新大赛-基于TencentOS Tiny 电量统计与显示软件

原创
作者头像
用户9551377
修改2022-03-14 11:57:06
4830
修改2022-03-14 11:57:06
举报
文章被收录于专栏:用户9551377
电量统计显示.pptx
视频内容

实现功能

1. 接收电量探测设备发送的电量信息

2. 将接收到的电量信息通过图标进行显示

3. 能够查看历史电量

实现思路

1. 电量探测设备通过Lora模块将电量数据发送到EVB-AIoT连接的Lora模块

2. 本程序通过TencentOS的AT和UART模块接收电量数据,并且进行保存

3. 本程序的另一个线程根据当前时间从保存的数据中读取并进行图标的绘制

4. 通过板子的F1、F2 2个按钮对本程序进行控制,单个按钮短按或者长按来显示不同时间的数据,2个按钮同时按来切换显示的模式

引脚

引脚
引脚

关键代码

1. AT指令收发

代码语言:javascript
复制
// Send
int atk_lora_send(const char *buf, size_t len, char * expected, char * recv_buf, size_t recv_buf_size)
{
    int try = 0;
	at_echo_t echo;

	char cmd[128] = {0};

    if (len > sizeof(cmd) - 2) {
        return -1;
    }

    memcpy(cmd, buf, sizeof(cmd) > len ? len : sizeof(cmd));
    memcpy(cmd + len, "\r\n", 2);

	tos_at_echo_create(&echo, NULL, 0, expected);
	while (try++ < 10) {
		tos_at_cmd_exec(&echo, 3000, cmd);
		if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
            PRINTF("%s %d\r\n", __FUNCTION__, __LINE__);
			return 0;
		}
	}
	return -1;
}

// Recv
static void atk_lora_at_event_callback(atk_lora_recv_event_t * event_handler,
    char * incoming_data_buffer, uint32_t size)
{
    int offset = 0;
    int err = 0;
    int is_recv_finish = 0;
    uint8_t data;

    memset(incoming_data_buffer, 0x00, size);

    k_tick_t begin = tos_systick_get();

    while ((tos_systick_get() - begin) < event_handler->timeout
    		&& 0 == is_recv_finish
			&& 0 == err)
    {
        tos_at_uart_read(&data, 1);
        switch (data)
        {
        case '\r':
        	continue;
        case '\n':
        	is_recv_finish = 1;
        	break;
        default:
			if (offset < size)
			{
				incoming_data_buffer[offset++] = data;
				continue;
			}
			printf(" ERR:incomming data is big ,please give more space for incoming_data_buffer\n");
			err = -1;
			break;
        }
    }

    // printf("[%p] Received err: %d finished: %d prefix: %s data: %s\n",
    // 		event_handler, err, is_recv_finish, event_handler->event, incoming_data_buffer);

    if (0 != is_recv_finish && event_handler->handler)
    {
    	event_handler->handler(event_handler->event, incoming_data_buffer, offset);
    }
}

2. 屏幕绘制

代码语言:javascript
复制
void draw_axis(uint32_t frameBuffer[APP_IMG_HEIGHT][APP_IMG_WIDTH], uint32_t fg_color, uint32_t bg_color)
{
	int y_max = g_y_max;
	int y_min = g_y_min;

    // Draw axises
	{
		draw_line(CONF_Y_AXIS_LABEL_WIDTH, CONF_X_AXIS_LABEL_HEIGHT,
            CONF_Y_AXIS_LABEL_WIDTH, APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT,
            frameBuffer, fg_color);
		draw_line(CONF_Y_AXIS_LABEL_WIDTH, APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT,
            CONF_Y_AXIS_LABEL_WIDTH - CONF_AXIS_ARROW_HALF_WIDTH,
            APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT - CONF_AXIS_ARROW_HEIGHT,
            frameBuffer, fg_color);
		draw_line(CONF_Y_AXIS_LABEL_WIDTH, APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT,
            CONF_Y_AXIS_LABEL_WIDTH + CONF_AXIS_ARROW_HALF_WIDTH,
            APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT - CONF_AXIS_ARROW_HEIGHT,
            frameBuffer, fg_color);
		draw_string(CONF_Y_AXIS_LABEL_WIDTH,
            APP_IMG_HEIGHT - CONF_Y_AXIS_NAME_HEIGHT, g_y_label, 24,
            frameBuffer, fg_color, bg_color);
		draw_line(CONF_Y_AXIS_LABEL_WIDTH, CONF_X_AXIS_LABEL_HEIGHT,
            APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH, CONF_X_AXIS_LABEL_HEIGHT,
            frameBuffer, fg_color);
		draw_line(APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH, CONF_X_AXIS_LABEL_HEIGHT,
            APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH - CONF_AXIS_ARROW_HEIGHT,
            CONF_X_AXIS_LABEL_HEIGHT + CONF_AXIS_ARROW_HALF_WIDTH,
            frameBuffer, fg_color);
		draw_line(APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH, CONF_X_AXIS_LABEL_HEIGHT,
            APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH - CONF_AXIS_ARROW_HEIGHT,
            CONF_X_AXIS_LABEL_HEIGHT - CONF_AXIS_ARROW_HALF_WIDTH,
            frameBuffer, fg_color);
		draw_string_v_center_align(APP_IMG_WIDTH - CONF_X_AXIS_NAME_WIDTH,
            CONF_X_AXIS_LABEL_HEIGHT, CONF_X_AXIS_NAME, 24,
            frameBuffer, fg_color, bg_color);
	}

	// Draw scales
	{
		// Draw Y scales
		int y_scale_step = (y_max - y_min) / CONF_Y_AXIS_N_SCALES;
		int y_step = (APP_IMG_HEIGHT - CONF_X_AXIS_LABEL_HEIGHT -
            CONF_Y_AXIS_NAME_HEIGHT - CONF_AXIS_ARROW_HEIGHT -
			CONF_X_AXIS_0_POINT_OFFSET) /
            CONF_Y_AXIS_N_SCALES;
		int y_offset = 0;
		int y_scale = y_min;
		for (int i = 0; i < CONF_Y_AXIS_N_SCALES + 1; ++i)
		{
			draw_line(CONF_Y_AXIS_LABEL_WIDTH,
				CONF_X_AXIS_LABEL_HEIGHT + y_offset + CONF_Y_AXIS_0_POINT_OFFSET,
                CONF_Y_AXIS_LABEL_WIDTH - 10,
				CONF_X_AXIS_LABEL_HEIGHT + y_offset + CONF_Y_AXIS_0_POINT_OFFSET,
                frameBuffer, fg_color);
			char scale[16] = {0};
			snprintf(scale, sizeof(scale), "%d", y_scale);
			draw_string_v_center_right_align(CONF_Y_AXIS_LABEL_WIDTH - 10,
                CONF_X_AXIS_LABEL_HEIGHT + y_offset + CONF_Y_AXIS_0_POINT_OFFSET,
				scale, 24, frameBuffer, fg_color, bg_color);
			y_offset += y_step;
			y_scale += y_scale_step;
		}

		// Draw X scales
		int x_step = CONF_X_AXIS_STEP;
		int x_scale_step = CONF_X_AXIS_SCALE_STEP;
		int x_offset = 0;
		for (int i = 0; i < CONF_X_AXIS_N_SCALES + 1; ++i)
		{
			draw_line(
				CONF_Y_AXIS_LABEL_WIDTH + x_offset + CONF_X_AXIS_0_POINT_OFFSET,
				CONF_X_AXIS_LABEL_HEIGHT,
                CONF_Y_AXIS_LABEL_WIDTH + x_offset + CONF_X_AXIS_0_POINT_OFFSET,
				CONF_X_AXIS_LABEL_HEIGHT - 10,
                frameBuffer, fg_color);
			char scale[32] = {0};
			if (K_NULL != g_k_tick_t_2_x_scale_label)
			{
				g_k_tick_t_2_x_scale_label(i, scale, sizeof(scale));
			} else
			{
				k_tick_t_2_format_time_of_day(i, scale, sizeof(scale));
			}
			draw_string_h_center_align(
				CONF_Y_AXIS_LABEL_WIDTH + x_offset + CONF_X_AXIS_0_POINT_OFFSET,
				10, scale, 16, frameBuffer, fg_color, bg_color);
			x_offset += x_step;
		}
	}

	g_fg_color = fg_color;
	g_bg_color = bg_color;
}

void axis_redraw(uint32_t frameBuffer[APP_IMG_HEIGHT][APP_IMG_WIDTH])
{
	int x_begin = CONF_Y_AXIS_LABEL_WIDTH + CONF_X_AXIS_0_POINT_OFFSET;
	int x_offset_point = CONF_X_AXIS_STEP / CONF_X_AXIS_SCALE_SECTION_N_POINTS;
	int y_max = g_y_max;
	int y_min = g_y_min;
	axis_clear_data(frameBuffer);
	draw_axis(frameBuffer, g_fg_color, g_bg_color);
	for (int i = 0; i < g_index - 1; ++i)
	{
		draw_line(x_begin + i * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
			x_begin + (i + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
			frameBuffer, g_data_color);
	}

	if (g_index_loop)
	{
		int index_begin = g_index + 1;
		if (index_begin >= N_G_VALUES)
		{
			for (int i = 0; i < N_G_VALUES - 1; ++i)
			{
				draw_line(x_begin + i * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
					x_begin + (i + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
					frameBuffer, g_data_color);
			}
		} else
		{
			int offset = 0;
			for (int i = index_begin; i < N_G_VALUES - 1; ++i, ++offset)
			{
				draw_line(x_begin + offset * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
					x_begin + (offset + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
					frameBuffer, g_data_color);
			}
			draw_line(x_begin + offset * x_offset_point, get_y_value(y_max, y_min, g_values[N_G_VALUES - 1]),
				x_begin + (offset + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[0]),
				frameBuffer, g_data_color);
			++offset;
			for (int i = 0; i < index_begin - 1; ++i, ++offset)
			{
				draw_line(x_begin + offset * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
					x_begin + (offset + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
					frameBuffer, g_data_color);
			}
		}
	} else
	{
		for (int i = 0; i < g_index - 1; ++i)
		{
			draw_line(x_begin + i * x_offset_point, get_y_value(y_max, y_min, g_values[i]),
				x_begin + (i + 1) * x_offset_point, get_y_value(y_max, y_min, g_values[i + 1]),
				frameBuffer, g_data_color);
		}
	}
}

3. 按钮事件

代码语言:javascript
复制
static void buttons_main(void * arg)
{
    while (g_run)
    {
        k_tick_t cur = get_sys_time();
        for (buttons_id_t id = BTN_ID_1; id < BTN_ID_COUNT; ++id)
        {
            int event = 0;
            tos_mutex_pend(&g_button_mutex);
            if (has_status(g_btn_contexts[id].comming_pressed_status))
            {
                if (g_btn_contexts[id].comming_pressed_status != g_btn_contexts[id].pressed_status)
                {
                    if (BTN_STATUS_PRESSED == g_btn_contexts[id].comming_pressed_status)
                    {
                        g_btn_contexts[id].pressed_time = get_sys_time();
                        event |= BTN_EVENT_PRESSED;
                    } else
                    {
                        if ((cur - g_btn_contexts[id].pressed_time) < BTN_LONG_PRESSED_TIME)
                        {
                            event |= BTN_EVENT_CLICK;
                        }
                        event |= BTN_EVENT_RELEASED;
                        g_btn_contexts[id].in_long_pressed = 0;
                    }
                    g_btn_contexts[id].pressed_status = g_btn_contexts[id].comming_pressed_status;
                } else
                {
                    if (BTN_STATUS_PRESSED == g_btn_contexts[id].pressed_status)
                    {
                        if ((cur - g_btn_contexts[id].pressed_time) >= BTN_LONG_PRESSED_TIME
                            && !g_btn_contexts[id].in_long_pressed)
                        {
                            event |= BTN_EVENT_LONG_PRESS;
                            g_btn_contexts[id].in_long_pressed = 1;
                        }
                    }
                }
                g_btn_contexts[id].comming_pressed_status = BTN_STATUS_NON_INIT;
            } else  // No comming status
            {
                if (BTN_STATUS_PRESSED == g_btn_contexts[id].pressed_status)
                {
                    if ((cur - g_btn_contexts[id].pressed_time) >= BTN_LONG_PRESSED_TIME
                        && !g_btn_contexts[id].in_long_pressed)
                    {
                        event |= BTN_EVENT_LONG_PRESS;
                        g_btn_contexts[id].in_long_pressed = 1;
                    }
                }
            }
            tos_mutex_post(&g_button_mutex);
            if (event > 0)
            {
                g_btn_contexts[id].callback(event);
            }
        }
        tos_task_delay(10);
    }
}

static void btn_ctrl_main(void * arg)
{
    while (g_run)
    {
        btn_ctrl_do_what_t do_what = BTN_CTRL_NOTHING;
        tos_mutex_pend(&g_btn_ctrl_mutex);
        if (BTN_CTRL_F1_F2_PRESSED != g_btn_ctrl_status.do_what)
        {
            if ((g_btn_ctrl_status.btn_status[BTN_ID_1] & BTN_EVENT_PRESSED) 
                && (g_btn_ctrl_status.btn_status[BTN_ID_2] & BTN_EVENT_PRESSED))
            {
                g_btn_ctrl_status.do_what = do_what = BTN_CTRL_F1_F2_PRESSED;
                g_btn_ctrl_status.btn_status[BTN_ID_1] = BTN_EVENT_PRESSED;
                g_btn_ctrl_status.btn_status[BTN_ID_2] = BTN_EVENT_PRESSED;
            } else if (BTN_EVENT_LONG_PRESS & g_btn_ctrl_status.btn_status[BTN_ID_1])
            {
                g_btn_ctrl_status.do_what = BTN_CTRL_NOTHING;
                do_what = BTN_CTRL_F1_LONG_PRESSED;
                g_btn_ctrl_status.btn_status[BTN_ID_1] = BTN_EVENT_LONG_PRESS;
            } else if (BTN_EVENT_LONG_PRESS & g_btn_ctrl_status.btn_status[BTN_ID_2])
            {
                g_btn_ctrl_status.do_what = BTN_CTRL_NOTHING;
                do_what = BTN_CTRL_F2_LONG_PRESSED;
                g_btn_ctrl_status.btn_status[BTN_ID_2] = BTN_EVENT_LONG_PRESS;
            } else if (BTN_EVENT_CLICK & g_btn_ctrl_status.btn_status[BTN_ID_1])
            {
                g_btn_ctrl_status.do_what = BTN_CTRL_NOTHING;
                do_what = BTN_CTRL_F1_CLICK;
                g_btn_ctrl_status.btn_status[BTN_ID_1] = BTN_EVENT_RELEASED;
            } else if (BTN_EVENT_CLICK & g_btn_ctrl_status.btn_status[BTN_ID_2])
            {
                g_btn_ctrl_status.do_what = BTN_CTRL_NOTHING;
                do_what = BTN_CTRL_F2_CLICK;
                g_btn_ctrl_status.btn_status[BTN_ID_2] = BTN_EVENT_RELEASED;
            }
        } else 
        {
            if ((g_btn_ctrl_status.btn_status[BTN_ID_1] & BTN_EVENT_RELEASED) 
                && (g_btn_ctrl_status.btn_status[BTN_ID_2] & BTN_EVENT_RELEASED))
            {
                g_btn_ctrl_status.do_what = BTN_CTRL_NOTHING;
                do_what = BTN_CTRL_F1_F2_RELEASED;
                g_btn_ctrl_status.btn_status[BTN_ID_1] = BTN_EVENT_RELEASED;
                g_btn_ctrl_status.btn_status[BTN_ID_2] = BTN_EVENT_RELEASED;
            }
        }
        tos_mutex_post(&g_btn_ctrl_mutex);

        btn_ctrl_do_what(do_what);

        tos_task_delay(10);
    }
}

代码地址

EVB_AIoT软件

https://gitee.com/delightedok/tos-electricity-consumption.git

模拟软件

https://gitee.com/delightedok/electricity-consumption-terminal.git

遇到的困难

1. 报名的时候打算用KV来对数据进行保存和读取,但是在实践中的时候发现GPIO_AD_B0_02被ELCDIF使用了;而如果使用NOR Flash驱动的时候,如果把framebuffer定义在NCACHE_REGION的时候,初始化BSS会出现Hard fault错误;最后尝试了SD卡,但是在发送CMD8数据的时候收不到SD卡的应答,所以最后选择了仅把数据放在内存中

2. 使用AT框架进行数据的收发的时候发现如果想使用同步的方式对数据进行读写,会出现读到的数据不完整的问题,看AT的代码认为是框架读到了跟expected相同的数据后就做出了返回,没有等待\r\n的到来,最后选择了使用异步的方式来处理返回的数据

3. 由于Lora模块占用了Wifi模块ESP8266的座子,因此时间同步选择了通过Lora模块来进行

后续

1. 移植到开源UI库中,例如LVGL

2. 数据保存到SD卡中

总结

感谢这次的比赛让我熟悉单片机层面的知识,对于嵌入式的初学者,这块板子个人感觉确实不错,有很多SDK的demo帮助我们调试和理解。感谢大佬们的分享和帮助。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实现功能
  • 实现思路
  • 引脚
  • 关键代码
    • 1. AT指令收发
      • 2. 屏幕绘制
        • 3. 按钮事件
        • 代码地址
          • EVB_AIoT软件
            • 模拟软件
            • 遇到的困难
            • 后续
            • 总结
            相关产品与服务
            TencentOS Server
            TencentOS Server 是腾讯云推出的 Linux 操作系统,它旨在为云上运行的应用程序提供稳定、安全和高性能的执行环境。它可以运行在腾讯云 CVM 全规格实例上,包括黑石物理服务器2.0。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档