[Funpack2-5] ESP32 BOX Lite + CircuitPython 推荐书目信息-类电子书项目
ESP32 BOX Lite + CircuitPython 的图书阅读系统
标签
Funpack活动
CircuitPython
ESP32-S3-BOX-LITE
disk
更新2023-08-02
715

 

TL;DR

在ESP32-S3-BOX-lite上,使用CircuitPython 位图显示的方法,从互联网获取信息,浏览书名和展示图书简介的项目


项目介绍、设计思路、简单的硬件介绍

   Funpack-5 主要硬件是ESP32-S3-BOX-Lite,在我看来,和S3-BOX 只有触摸屏与按键的这一个区别比较大,静音键什么的都没太大所谓。

   软件平台上,当然电子森林也在推荐大家使用ESP-IDF的开放方式,从任务一和任务二来说,站在乐鑫巨人的肩膀上来二次开发,省时省力,而且图形界面,乐鑫已经集成好了LVGL 。事情总是不会很完美,因为对C/C++/C#的不熟悉,对我而言,还是吃力很多,所以不得不把视线投到Python阵营。

   MicroPython (以下简称MPY)和CircuitPython(以下简称CPY)。其中CPY,2017 年 1 月,Adafruit 推出了CircuitPython,这是MicroPython编程语言的一个分支,经过优化,可在选定的 Adafruit 产品上运行。2019 年,CircuitPython 的资源转移到了 circuitpython.org,此举表明使用 CircuitPython 的第三方电路板数量已经超过了仅由 Adafruit 制造的电路板数量。这包括用于微控制器的 CircuitPython 和使用名为“Blinka”的兼容层 Adafruit的单板计算机上的CircuitPython,以访问通用输入/输出功能以及与 160 多个传感器和驱动程序库的兼容性。

 


软件流程图及各功能对应的主要代码片段及说明

 

Fv8tQdu-kUwmkBx8IJ8O8jRY-zZK

 

  • 整个项目入口文件时code.py, 主要包含show_gird_screen() 入口函数;show_detail_screen() 显示页函数。
  • project_details.py 主要时辅助函数,用于抓取需要的内容。
  • project_btn_detect.py 主要是 用于正面三个按键的检测,此处感谢微信群里的小伙伴们的帮助,因为按键并不是分别连接不同的GPIO,而是都接入了GPIO1,根据不同电压的大小来判断。
  • project_constants.py 主要是项目使用的常量。

 

运行时和Wi-Fi 

         因为CPY提供了在线的固件刷入服务和Wi-Fi 连接配置,即便没有任何编程经验的人也可以,轻松过完成.

         https://circuitpython.org/downloads?q=S3+BOX

         固件刷入后,可以网页编辑Wi-Fi信息,也可以手动编辑 settings.toml

CIRCUITPY_WIFI_SSID = "WiFi_SSID"
CIRCUITPY_WIFI_PASSWORD = "12345678"
CIRCUITPY_WEB_API_PASSWORD = "PASSWORD"
CIRCUITPY_WEB_API_PORT = 80

      其中,前两行是Wi-Fi信息,后两行是网页编程器的密码和访问端口,等BOX-Lite成功连接网络后,屏幕会显示获取的IP,这时可以在同一网络下访问IP+端口,并使用API_PASSWORD 可以开启网页编辑器。

 

 

文本显示的主要函数

 

from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import label

   项目的显示文字部分主要使用这两个外部库,字体建议使用转化后的pcf格式字体,加载会更快。因为阅读器项目,中文字体的字符又非常多,所以,对MCU的性能也是一种挑战。在项目中,使用了得以黑作为一级菜单的字体,谷歌开源的字体作为二级详情页的字体。 

 

 

 

首页书籍字段获取部分代码解释

def get_homepage_books():
    grid_url = WEBSITE_URL + GRID_PATH 
    # make a GET request to the URL and store the response
    session = session_init()
    if session:
        response = session.get(grid_url)
        html_content = response.text
        response.close()
        pattern = r'<div class="col-sm-4 col-md-2">\s*<a href=".+?/([^/]+?)">\s*<div class="thumbnail">\s*<img src="(.+?)" alt="(.+?)" style=".+?">'
        matches = []
        match = re.search(pattern, html_content)
        while match:
            matches.append(match.groups())
            html_content = html_content[match.end():]
            match = re.search(pattern, html_content)
        return  matches if matches else ['获取列表失败,请尝试重启']
    else:
        return ['网络未连接,请检查settings.toml,\n或按左侧下键重启']

初始化完Session 后,请求页面,然后re.search 正则表达式获取需要的信息,返回一个类似

[('934', '/bp/e9be6e2e-daa7-4e54-b727-f11b743b1f88.jpg', '当代中国的精神力量'), ('932', '/bp/deb215f2-caae-40b8-82fb-3d54e6116441.jpg', '怒航'),...]

分别代表,细节页的URI ,图片URL,书名

对应的在code.py中负责显示的函数:

def show_gird_screen(page_id=0):
    gc.collect()
    global homepage_info_list

    if homepage_info_list:
        result_list = homepage_info_list
    else:
        result_list = get_homepage_books()
        homepage_info_list = result_list
  
    
    available_item_amount = len(result_list)

    
    if available_item_amount == 1:
        error_msg = result_list[0]
        display.show(text_area_display(error_msg,font_S_20,True))
        raise SystemExit

    # to-do 多页
    # show_pages_amount = math.ceil(available_item_amount / SCREEN_ONEPAGE_LINE_COUNT)
    
    current_index_id = page_id
 
    display.show(text_area_display(formate_grid(result_list[current_index_id][2]),font_S_20,True)

    while True:

前半段是,判断是否已经已经获取,尤其是从细节页面返回的时候,就不重复获取

display.show()默认开机显示第一条记录, while True: 后为按键判断逻辑。

显示效果为

Frm8S24_YlYK49Mzo3fk8Ql2PSsq

 

 

 

详情页显示部分代码解释

初始化部分和首页类似

def get_details_by_id(book_id):
    gc.collect()
    book_url = WEBSITE_URL + DETAIL_PATH + str(book_id)
    ‘’‘
    省略 Session初始化和Htm获取代码
   ’‘’
   if div_start != -1 and ul_start != -1 and ul_end != -1:
            ul_content = html_content[ul_start: ul_end]

            # 提取标题
            title_start = ul_content.find('<h3>')
            title_end = ul_content.find('</h3>', title_start)
            title = ul_content[title_start + 4: title_end]

            # 提取其他元素
            publish_time_start = ul_content.find('发布时间:', title_end) + 5
            publish_time_end = ul_content.find('</li>', publish_time_start)
            publish_time = ul_content[publish_time_start: publish_time_end]

            views_start = ul_content.find('浏览次数:', publish_time_end) + 5
            views_end = ul_content.find('</li>', views_start)
            views = ul_content[views_start: views_end]

            authors_start = ul_content.find('作者:', views_end) + 3
            authors_end = ul_content.find('</li>', authors_start)
            authors = ul_content[authors_start: authors_end]


            summary_start = ul_content.find('内容简介:', authors_end) + 5
            summary_end = ul_content.find('</li>', summary_start)
            summary = ul_content[summary_start: summary_end]
            summary = re.sub(r'(&#\d+;)|(<.*?>)', '', summary)
            summary = '内容简介:'+summary 
            summary = add_newline(summary)
            
         
            text_info =  '《'+title + '》\n作者:'+ authors + '\n发布时间:'+ publish_time +' 浏览次数:'+ views  +'\n'+ summary +'(完)'
            return split_string_by_lines(text_info)

 

返回的结果样例为:

(3, ['《时代广场的蟋蟀》\n作者:(美)乔治&amp;#8226;塞尔登\n发布时间:2015/12/9  浏览次数:24330\n内容简介:柴斯特,一只出生在康涅狄克州\n草原的蟋蟀,因为贪吃,被意外带到了纽约\n时代广场的地铁站。乡下来的小蟋蟀第一次', '出远门,就来到了全世界最繁华的地方。在\n这个全然陌生的世界,他幸运地被一个好心\n的男孩,报摊老板的儿子玛利欧收养,并与\n两个地铁站里的老住户——亨利猫和塔克鼠\n成为了好朋友。柴斯特有着惊人的音乐天赋\n,他“演奏”的美妙音乐,打动了听过的每', '个人,成为了纽约最受瞩目的明星,改变了\n玛利欧一家人贫穷的生活。令人意外的是,\n柴斯特收获了那么多的掌声和鲜花,依旧念\n念不忘家乡的自由生活,于是他告别了朋友\n们,踏上了回家之路。\n(完)'])

在内容介绍部分,增加了‘/n’换行符来适应屏幕宽度。

效果如下:

Fu1pwIdisXbZLygnj96h6oAKCsmP

上下键翻页,中间键返回首页

 

 

 

按键判断逻辑代码解析

以详情页为例

new_v = round((project_analog_in.value * 3.3) / 65536, 1)
        
        if check_button(new_v, 2.4):
            # print('left btn')
            if current_page_id - 1 >= 0:
                current_page_id -= 1
                print(current_page_id)
                display.show(text_area_display(book_content[0][current_page_id]))
                
        elif check_button(new_v, 2.0):
            print('detail mid btn')
            print('detail back pass_current_index_id',pass_current_index_id)
            time.sleep(0.18)
            show_gird_screen(pass_current_index_id)
            
            
            # use grid list
        elif check_button(new_v, 0.8):
            print('right btn')
            if current_page_id + 1 < book_pages:
                current_page_id += 1
                print(current_page_id)
                display.show(text_area_display(book_content[0][current_page_id]))

可以看到ADC检查电压的方法来判断三个键是否被按下,左右键相对简单直接改变显示字段的ID 来变化内容,中间键绑定不同的函数,实现返回或者进入。

 

 

其他部分

因为CPY 没有beautifulSoup类的解析html的库,只能使用正则表达式来解析内容,在项目的前后使用了两种解析方法,其中一种参考了ChatGPT的回答

pattern = r'<div class="col-sm-4 col-md-2">\s*<a href=".+?/([^/]+?)">\s*<div class="thumbnail">\s*<img src="(.+?)" alt="(.+?)" style=".+?">'
matches = []
match = re.search(pattern, html_content)

可以看到HTML解析部分,使用了另外一种方法,更为直观,比首页字段获取的一行式正则表达式。

此外,Session的构造也很重要

def session_init():
    try:
    # configure the WiFi connection
        wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
    except ConnectionError:
        print('ConnectionError')
        return False

    # configure the SSL context and socket pool
    context = ssl.create_default_context()
    pool = socketpool.SocketPool(wifi.radio)

    # create an instance of the Adafruit Requests session
    session = adafruit_requests.Session(pool, ssl_context=context)
    return session

 

 


功能展示及说明

 

Frm8S24_YlYK49Mzo3fk8Ql2PSsq

首页

Fu1pwIdisXbZLygnj96h6oAKCsmP

详情页

更多详见视频

 

 


值得分享的资料:

字体BDF转PCF 网站 https://adafruit.github.io/web-bdftopcf/

 

个人总结:

 第一次参加电子森林的活动,不管是项目和这篇报告,都还有很多值得提升的地方。这个项目给我最大的感受,是如何使用ESP32-S3这个MCU,不断的妥协和更换解决办法,来实现目标的这个过程,简单来说痛苦又快乐。因为在PC项目上有着大量知名的库,可以直接使用。也可能因为水平有限,在MCU上总是束手束脚,代码写的也很不优雅,还有很多需要改进的地方,时间的原因没办法完成,包括:

  • 将详情页保存到本地,不用每次都抓取
  • 加一个加载loading的界面,不用每次干等
  • 增加图书图片作为背景
  • ...

再次感谢电子森林和得捷电子,以为微信群里的各位小伙伴。谢谢和大家一起积极帮助和相互学习。

 

 

软硬件
元器件
ESP32-S3-WROOM-1-N16R8
ESP32-S3-WROOM-1 是通用型 Wi-Fi + 蓝牙 MCU 模组,具有丰富的外设接口,强大的神经网络运算能力和信号处理能力,是专为人工智能和 AIoT 市场打造的两款模组,适用于多种应用场景, 例如唤醒词检测和语音命令识别、人脸检测和识别、智能家居、智能家电、智能控制面板、智能扬声器等。
附件下载
code_attachment.zip
输入CircuitPython 后,记得修改settings.toml的Wi-Fi 信息
团队介绍
Python 小白,ESP32 系列爱好者,CircuitPython粉丝
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号