基于esp32 s3 box lite的联网文本阅读器
基于esp32 s3 box lite的联网文本阅读器,使用platformIO 基于Arduino框架开发,实现访问HTTP网页并显示网页内容,通过本地HTTP服务器实现文本阅读
标签
Funpack活动
ESP32-S3-BOX-LITE
我卡壳了
更新2023-08-04
848

一、项目需求

  1. 实现ESP32连接WIFI,获取网页信息;
  2. 实现TFT屏幕的驱动,并添加中文显示功能;
  3. 能够将网页上的文本信息在屏幕上显示出来;
  4. 实现按键交互,完成子页面选择,切换和翻页功能。

二、实现方案

1.总体方案

软件上采用PlatformIO进行开发,基于Arduino的软件框架,有很多现成的功能库可以调用,能极大的方便开发工作的进行。
在本次项目中我使用了TFT_eSPI库驱动TFT屏幕只需要根据硬件连接图对setup文件进行配置更改就可以快速点亮esp32-s3-box上的LCD屏幕。
然后关于ADC按键和WIFI等功能的开发都可以使用Arduino的接口进行快速开发。

项目设计思路流程为:

HTTP服务器搭建 -> LCD屏幕驱动、按键驱动和wifi程序设计 -> 中文显示程序设计  -> 菜单逻辑设计 -> 整合功能,完成软件设计

系统软件流程图:

FsulfwN69cFvLHH8xTtRZD_otNJ8

2.服务器搭建

由于我对网络相关知识了解较少,所以本次项目中使用的是python中的http.server模块。
对其源文件进行了简单的修改,实现了通过.py文件直接将其同目录下的pyhttpserver文件夹作为一个局域网http服务器,可以通过本机浏览器访问localhost:8000或0.0.0.0:8000进入该目录下,局域网设备也可通过访问本机IP端口8000访问该路径

FjuYfcCxE4_2dQh6IrCZlq46lVlc

Fj__SqKtkwL2Cwj-TU19mv3TEgso

3.中文显示

中文显示是通过字库生成工具生成了一个完整的16*16单色深unioncode字库,以c源码const数组的形式存放在项目工程中,通过程序,根据UTF-8编码计算获得unioncode汉字索引

uint8_t get_utf8_font(uint8_t* utf8_text, uint8_t** font_data)
{
    int outputSize = 0; //记录转换后的Unicode字符串的字节数

    uint16_t font_format = 0;  // 字符unicode编码
    uint8_t tmp[2];

    if (*utf8_text > 0x00 && *utf8_text <= 0x7F) //处理单字节UTF8字符(英文字母、数字)
    {
        font_format = *utf8_text;
        outputSize = 1;
    }
    else if (((*utf8_text) & 0xE0) == 0xC0) //处理双字节UTF8字符
    {
        char high = *utf8_text;
        utf8_text++;
        char low = *utf8_text;

        if ((low & 0xC0) != 0x80)  //检查是否为合法的UTF8字符表示
        {
            return -1; //如果不是则报错
        }

        tmp[0] = (high << 6) + (low & 0x3F);
        tmp[1] = (high >> 2) & 0x07;
        font_format = tmp[0] + (tmp[1] << 8);
        outputSize = 2;
    }
    else if (((*utf8_text) & 0xF0) == 0xE0) //处理三字节UTF8字符
    {
        char high = *utf8_text;
        utf8_text++;
        char middle = *utf8_text;
        utf8_text++;
        char low = *utf8_text;

        if (((middle & 0xC0) != 0x80) || ((low & 0xC0) != 0x80))
        {
            return -1;
        }

        tmp[0] = (middle << 6) + (low & 0x7F);
        tmp[1] = (high << 4) + ((middle >> 2) & 0x0F); 
        font_format = tmp[0] + (tmp[1] << 8);
        outputSize = 3;
    }
    else //对于其他字节数的UTF8字符不进行处理
    {
        return -1;
    }

    *font_data = font_16VH[font_format];

    return outputSize;
}

再按照字库取模方式,逐列式画点,编写文本显示函数,实现全unioncode的中文显示。

uint16_t show_char(uint16_t x0, uint16_t y0, uint8_t *pdata, uint16_t len, uint32_t color)
{
    uint8_t *utf8_p = pdata;
    uint8_t *dis_ram;
    uint8_t data_size;
    uint16_t x = x0, y = y0;
    for (int ret = 0; ret < len && *utf8_p != 0; ret++)
    {
        if (*utf8_p > 0x00 && *utf8_p <= 0x7F)
            data_size = 8;
        else
            data_size = 16;
        uint8_t updata = get_utf8_font(utf8_p, &dis_ram);
        if (updata)
            utf8_p += updata;
        else
            utf8_p++;

        for (int i = 0; i < data_size; i++)
        {
            uint8_t dis_data = dis_ram[i * 2];
            for (uint8_t j = 0; j < 8; j++)
            {
                if (dis_data & 0x01)
                    tft.drawPixel(x + i, y + j, color);
                dis_data >>= 1;
            }
            dis_data = dis_ram[i * 2 + 1];
            for (uint8_t j = 0; j < 8; j++)
            {
                if (dis_data & 0x01)
                    tft.drawPixel(x + i, y + 8 + j, color);
                dis_data >>= 1;
            }
        }
        x += (data_size + 2);
    }
    return utf8_p - pdata;
}

4.网页子页面解析

根据http网页源码内容,对各个子路径的链接进行内容解析,获取各个文档的子路径,名字和大小,然后通过后续程序即可访问子路径并显示器中的文本内容

void http_get_list(void) // 获取首页html信息
{
    HTTPClient http; // 声明HTTPClient对象

    http.begin("http://192.168.31.169:8000/text/"); // 准备启用连接
    int httpCode = http.GET();                              // 发起GET请求

    if (httpCode > 0) // 如果状态码大于0说明请求过程无异常
    {
        if (httpCode == HTTP_CODE_OK) // 请求被服务器正常响应,等同于httpCode == 200
        {

            int len = http.getSize(); // 读取响应正文数据字节数,如果返回-1是因为响应头中没有Content-Length属性

            WiFiClient *stream = http.getStreamPtr(); // 获取响应正文数据流指针

            while (http.connected() && (len > 0 || len == -1)) // 当前已连接并且有数据可读
            {
                size_t size = stream->available(); // 获取数据流中可用字节数
                if (size)
                {
                    int c = stream->readBytes(txt_buff, ((size > 10*1024) ? 10*1024 : size)); // 读取数据到buff
                }

                memset(html_list, 0, sizeof(html_list));
				box_book_num = get_html_list(txt_buff, strlen((char *)txt_buff), html_list);
				
				Serial.printf("html list num : %d\r\n", box_book_num);
				for(uint8_t ret = 0; ret < box_book_num ; ret ++)
				{
					Serial.printf("%d, addr:%s, name:%s, size:%d\r\n", ret + 1, html_list[ret].name, html_list[ret].address, html_list[ret].size);
				}
            }
        }
    }
    else
    {
        Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }

    http.end(); // 结束当前连接
}

再将读到的网页信息根据网页格式,解析出各个链接和文件名

uint8_t get_html_list(uint8_t* text, uint32_t text_len, html_info* list) // 解析页面中的标题和子链接路径
{ 
	uint32_t ret = 0;
	uint8_t count = 0;
	for(ret = 0 ; ret < text_len ; ret ++)
	{
		if(text[ret] == 'a')
		{
			if(strncmp((char*)(&text[ret]), "a href=\"", 8) == 0)
			{
				ret += 8;
				uint8_t ret_s;
				for(ret_s = 0 ; ret_s < 100 ; ret_s ++)
				{
					if(text[ret] != '\"')
					{
						list[count].address[ret_s] = text[ret];
						ret ++;
					}
					else
						break;
				}
				ret += 2;
				for(ret_s = 0 ; ret_s < 100 ; ret_s ++)
				{
					if(strncmp((char*)(&text[ret - 4]), ".txt", 4) == 0)
					{
						list[count].size = str_2_dec(&text[ret + 9]);
						break;
					}
					list[count].name[ret_s] = text[ret];
					ret ++;
					if(text[ret] == '/')
					{
						list[count].size = 0;
						break;
					}
				}
				count ++;
			}
		}
	}
	return count;
}

FoEue92vI68CP36MJnc3j3W45W6I

得到这些信息就可以对各个文档的子链接进行访问

5.交互功能整合

在可以获取各个网页信息,显示文本内容以后,结合按键驱动,完善项目逻辑实现完整的文本阅读器功能。

void loop()
{

    tft.fillScreen(0xFFFF);
    http_get_list();
    while(box_func_page == 0)
    {
        for(uint8_t ret = 0; ret < box_book_num ; ret ++)
        {
            memset(LIST_DIS_BUF, 0, 100);
            sprintf((char *)LIST_DIS_BUF, "%d : %s\r\n", ret + 1, html_list[ret].name, html_list[ret].size);
            show_char(20, 5 + ret * 18, LIST_DIS_BUF, sizeof(LIST_DIS_BUF), 0);
        }

        show_char(5, 5 + box_select_cursor * 18, (uint8_t *)">", 1, 0);

        key = esp_box_adcbutton();

        if(key == 1)
        {
            if(box_select_cursor > 0) 
            {
                tft.fillRect(5, 5 + box_select_cursor * 18, 8, 16, 0xFFFF);
                box_select_cursor --;
            }
        }
        if(key == 2)
        {
            box_func_page = 1;
        }
        if(key == 3)
        {
            if(box_select_cursor < box_book_num) 
            {
                tft.fillRect(5, 5 + box_select_cursor * 18, 8, 16, 0xFFFF);
                box_select_cursor ++;
            }
        }
    }

    tft.fillScreen(0xFFFF);
    show_char(120, 100, (uint8_t *)"加载中。。。", strlen("加载中。。。"), 0);
    http_get_txt();
    tft.fillScreen(0xFFFF);
    box_page_num = 0;
    box_dis_cursor = 0;
    while(box_func_page == 1)
    {
        show_page(txt_buff + box_dis_cursor, box_all_data_len - box_dis_cursor, &n_data_len, &n_dis_len, 0);
        box_page_data_len[box_page_num] = n_data_len;

        key = esp_box_adcbutton();

        if(key == 1)
        {
            if(box_page_num > 0) 
            {
                box_page_num --;
                box_dis_cursor -= box_page_data_len[box_page_num];
                tft.fillScreen(0xFFFF);
            }
        }
        if(key == 2)
        {
            box_func_page = 0;
        }
        if(key == 3)
        {
            if(box_dis_cursor + box_page_data_len[box_page_num] < box_all_data_len && box_page_num < 5000 - 1) 
            {
                box_dis_cursor += box_page_data_len[box_page_num];
                box_page_num ++;
                tft.fillScreen(0xFFFF);
            }
        }
    }
}

三、项目成果

项目是实现了一个可以访问HTTP服务器内txt文本的文本阅读器,可通过案件选择要阅读的文本,以及通过案件进行翻页操作。

Fj0hOER4xnVdP4KxEzS3GifTb0pTFry5VBYtnr8-MUpQUKjAT_whGldnFkg034L5u3C2Ad5WVsJZLqJfLYo9

附件下载
esp32_box_reader.7z
py_http_server.zip
团队介绍
个人嵌入式工作者
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号