这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【M5STACKTAB5W/OBATTERY】【三】简易电子书阅读器

共1条 1/1 1 跳转至

【M5STACKTAB5W/OBATTERY】【三】简易电子书阅读器

工程师
2026-01-22 00:41:30     打赏

简介

在完成了一个基于M5 Stack Tab5的SD卡文件浏览项目后,我想到一个有趣的需求:如果能在这块屏幕上舒适地阅读电子书那该多好。相比其他电子阅读设备,M5 Stack拥有充足的RAM和大容量的Flash,用它来实现一个简单但功能完整的文本阅读器是完全可行的。

于是我决定在Arduino环境下,利用M5UNIFIED和M5GFX这两个官方BSP库,开发一个具有以下功能的电子书阅读器

主要支持功能如下

  • 本地TXT文件阅读:直接从SD卡读取文本文件

  • 进度自动保存:记住每个文件的阅读位置,下次打开自动恢复

  • 简易UI设计:清晰的导航栏、翻页操作和进度显示

  • 翻页:触摸屏左右区域实现前后翻页

视频效果如下

核心设计思路1. 进度保存机制

与简单的文件浏览不同,阅读器需要记住用户的阅读位置。我采用了一个简洁的方案:

/* ===== 保存读取进度 ===== */
void saveProgress() {
    if (!reading || currentFile.isEmpty())
        return;
    if (filePos == lastSavePos)
        return; // 避免频繁写入

    String progFile = String(PROGRESS_DIR) + "/" + currentFile + ".pos";
    File f = SD.open(progFile, FILE_WRITE);
    if (f) {
        f.seek(0);
        f.write((uint8_t *)&filePos, sizeof(filePos));
        f.close();
        lastSavePos = filePos;
    }
}

uint32_t loadProgress(String filename) {
    String progFile = String(PROGRESS_DIR) + "/" + filename + ".pos";
    if (SD.exists(progFile)) {
        File f = SD.open(progFile, FILE_READ);
        if (f && f.size() >= sizeof(uint32_t)) {
            uint32_t pos = 0;
            f.read((uint8_t *)&pos, sizeof(pos));
            f.close();
            return pos;
        }
        if (f) f.close();
    }
    return 0;
}

每个TXT文件都对应一个.pos文件,记录当前的字节位置。这样做的优点是

  • 轻量级,不占用大量空间

  • 快速查找和保存

  • 支持多个文件的独立进度记录


2. 分页显示逻辑

为了充分利用屏幕空间,我设定每次读取256字节并逐行显示:

#define PAGE_BYTES 256      // 每次读取256字节
#define TOP_MARGIN 65       // 顶部导航栏高度

在绘制界面时,先绘制顶部导航栏,然后从TOP_MARGIN位置开始显示文本内容:

void drawPage() {
    if (!bookFile) return;

    M5.Display.clear(TFT_BLACK);

    // 绘制顶部栏
    M5.Display.fillRect(0, 0, M5.Display.width(), 45, TFT_DARKGREY);
    M5.Display.drawLine(0, 45, M5.Display.width(), 45, TFT_WHITE);

    // 返回按钮
    M5.Display.fillRect(5, 2, 50, 35, TFT_BLUE);
    M5.Display.drawRect(5, 2, 50, 35, TFT_WHITE);
    M5.Display.setFont(&fonts::FreeMonoBold9pt7b);
    M5.Display.setTextColor(TFT_WHITE);
    M5.Display.setCursor(14, 25);
    M5.Display.println("Back");

    // 文件名和进度
    M5.Display.setFont(&fonts::FreeMonoBold9pt7b);
    M5.Display.setCursor(65, 15);
    M5.Display.printf("%s", currentFile.c_str());

    // 进度显示在右上角
    float progress = (float)filePos / maxFileSize * 100.0;
    M5.Display.setFont(&fonts::FreeMonoBold9pt7b);
    M5.Display.setCursor(M5.Display.width() - 100, 20);
    M5.Display.printf("%.0f%%", progress);

    // 内容显示
    M5.Display.setFont(&fonts::FreeMono18pt7b);
    M5.Display.setTextColor(TFT_WHITE);
    M5.Display.setCursor(10, TOP_MARGIN + 5);

    bookFile.seek(filePos);
    char buf[PAGE_BYTES + 1];
    int n = bookFile.readBytes(buf, PAGE_BYTES);
    buf[n] = 0;
    M5.Display.print(buf);
}


3. 触摸交互

文件列表界面和阅读界面分别处理触摸事件

void loop() {
    M5.update();

    // 定期保存进度(每5秒)
    if (reading && millis() - lastSaveTime > 5000) {
        saveProgress();
        lastSaveTime = millis();
    }

    int touchCount = M5.Touch.getCount();
    if (touchCount > 0) {
        auto t = M5.Touch.getDetail();

        if (!touchActive) {
            touchActive = true;

            if (!reading) {
                checkFileClick(t.x, t.y);  // 列表交互
            } else {
                int w = M5.Display.width();
                if (t.y <= 40 && t.x <= 55) {
                    saveProgress();
                    if (bookFile) bookFile.close();
                    listDir(SD, "/");  // 返回列表
                } else if (t.x < w / 2) {
                    prevPage();  // 左侧 - 上一页
                } else {
                    nextPage();  // 右侧 - 下一页
                }
            }
        }
    } else {
        touchActive = false;
    }
}


效果如下所示

daf9a7cf0382fb17960d42d743de5cf9.jpg

文件列表


e9c801227e14538fc1b2539fe7bcc85c.jpg

阅读界面



附件代码

txt_reader.zip




关键词: M5STACKTAB5W     电子书阅读器    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]