Przeglądaj źródła

!97 add: turnkey-EinkBook
Merge pull request !97 from Jeremy/master

Dozingfiretruck 3 lat temu
rodzic
commit
313e1c2852

+ 15 - 0
script/turnkey/EinkBook/.gitignore

@@ -0,0 +1,15 @@
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+*.exe
+
+*.soc
+
+.idea

+ 21 - 0
script/turnkey/EinkBook/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Jeremy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 39 - 6
script/turnkey/EinkBook/README.md

@@ -1,19 +1,52 @@
 # EinkBook-LuatOS
 # EinkBook-LuatOS
 
 
-### 仓库地址
-
-https://gitee.com/HashJeremy/EinkBook-LuatOS
-
 ### 介绍
 ### 介绍
+
 使用LuatOS-ESP32制作一个电纸书
 使用LuatOS-ESP32制作一个电纸书
 
 
 #### 效果展示
 #### 效果展示
+
 ![](https://cdn.openluat-luatcommunity.openluat.com/images/20220313202435046_IMG_20220310_154336.jpg)
 ![](https://cdn.openluat-luatcommunity.openluat.com/images/20220313202435046_IMG_20220310_154336.jpg)
 
 
 ### 硬件
 ### 硬件
+
 + 合宙ESP32-C3开发板
 + 合宙ESP32-C3开发板
 + MODEL_1in54 墨水屏
 + MODEL_1in54 墨水屏
 
 
 ### 软件
 ### 软件
-+ LuatOS-ESP32
-+ GoFrame
+
++ [LuatOS-ESP32](https://gitee.com/dreamcmi/LuatOS-ESP32/tree/master)
++ [GoFrame](https://goframe.org/display/gf)
+
+### 部署方法
+
+#### 服务端
+
+> 请先自行配置Golang工具链
+
++ 将想要阅读的小说放到`Server\books`目录下(目前仅支持txt格式)
++ 开启小说服务端程序
+
+```bat
+cd Server
+windows:
+    ./run.bat
+linux or macos:
+    ./run.sh
+```
+
+#### 电纸书
+
+> 需要解锁ESP32C3的GPIO11,详情参考这里[ESP32C3解锁使用IO11](https://gitee.com/dreamcmi/LuatOS-ESP32/blob/master/doc/VDD_SPI_AS_GPIO.md)
+
+> 注意: 需要使用带有`sarasa_regular_12`字体的固件,本地没有LuatOS-ESP32编译环境可以参考[云编译](https://wiki.luatos.com/develop/compile/Cloud_compilation.html)
+
+使用LuaTools将Scripts目录下所有文件烧录到ESP32-C3模块中
+
+### 电纸书使用方法
+
+使用BOOT键(GPIO 9)作为功能按键
+
++ 单击:下一个
++ 双击:上一个
++ 长按:进入/退出

+ 71 - 0
script/turnkey/EinkBook/Scripts/httpLib.lua

@@ -0,0 +1,71 @@
+local httpLib = {}
+-- printTable(esphttp)
+-- {
+-- 	"init" = function: 42008586,
+-- 	"post_field" = function: 42008A58,
+-- 	"perform" = function: 42008BA0,
+-- 	"status_code" = function: 42008A0E,
+-- 	"content_length" = function: 42008C28,
+-- 	"read_response" = function: 42008992,
+-- 	"set_header" = function: 42008910,
+-- 	"get_header" = function: 420088A0,
+-- 	"cleanup" = function: 42008848,
+-- 	"is_done" = function: 420087EE,
+-- 	"go" = function: 42008B1E,
+-- 	"GET" = 0,
+-- 	"POST" = 1,
+-- 	"PUT" = 2,
+-- 	"PATCH" = 3,
+-- 	"DELETE" = 4,
+-- 	"EVENT_ON_FINISH" = 5,
+-- 	"EVENT_ERROR" = 0,
+-- 	"EVENT_DISCONNECTED" = 6,
+-- 	"EVENT_ON_DATA" = 4,
+-- }
+
+local methodTable = {
+    GET = esphttp.GET,
+    POST = esphttp.POST,
+    PUT = esphttp.PUT,
+    DELETE = esphttp.DELETE
+}
+
+function httpLib.request(method, url, head)
+    local responseCode = 0
+    local httpc = esphttp.init(methodTable[method], url)
+    if httpc == nil then
+        esphttp.cleanup(httpc)
+        return false, responseCode, "create httpClient error"
+    end
+    if head ~= nil then
+        for k, v in pairs(head) do
+            esphttp.set_header(httpc, k, v)
+        end
+    end
+    local ok, err = esphttp.perform(httpc, true)
+    if ok then
+        local response = ""
+        while 1 do
+            local result, c, ret, data = sys.waitUntil("ESPHTTP_EVT", 20000)
+            -- log.info("ESPHTTP_EVT", result, c, ret, data)
+            if result == false then
+                esphttp.cleanup(httpc)
+                return false, responseCode, "wait for http response timeout"
+            end
+            if c == httpc then
+                if esphttp.is_done(httpc, ret) then
+                    esphttp.cleanup(httpc)
+                    return true, esphttp.status_code(httpc), response
+                end
+                if ret == esphttp.EVENT_ON_DATA then
+                    response = response .. data
+                end
+            end
+        end
+    else
+        esphttp.cleanup(httpc)
+        return false, responseCode, "perform httpClient error " .. err
+    end
+end
+
+return httpLib

+ 340 - 0
script/turnkey/EinkBook/Scripts/main.lua

@@ -0,0 +1,340 @@
+PROJECT = "EinkBook-LuatOS"
+VERSION = "1.0.0"
+MOD_TYPE = rtos.bsp()
+log.info("MOD_TYPE", MOD_TYPE)
+sys = require("sys")
+wifiLib = require("wifiLib")
+httpLib = require("httpLib")
+
+tag = "EINKBOOK"
+-- 是否启用配网功能(配合esptouch使用)
+USE_SMARTCONFIG = false
+-- 这是已有的电纸书服务端地址,如果自己搭建或下面这个服务失效了,请修改
+serverAdress = "http://47.96.229.157:2333/"
+-- 改为需要连接的WiFi名称和密码(使用2.4Ghz频段,ESP32C3无法使用5GHz WiFi)
+SSID, PASSWD = "Xiaomi_AX6000", "Air123456"
+
+waitHttpTask, waitDoubleClick = false, false
+einkPrintTime = 0
+PAGE, gpage = "LIST", 1
+onlineBooksTable, onlineBooksShowTable, onlineBooksShowTableTmp, einkBooksIndex, onlineBooksTableLen = {}, {}, {}, 1, 0
+gBTN, gPressTime, gShortCb, gLongCb, gDoubleCb, gBtnStatus = 0, 1000, nil, nil, nil, "IDLE"
+
+function printTable(tbl, lv)
+    lv = lv and lv .. "\t" or ""
+    print(lv .. "{")
+    for k, v in pairs(tbl) do
+        if type(k) == "string" then
+            k = "\"" .. k .. "\""
+        end
+        if "string" == type(v) then
+            local qv = string.match(string.format("%q", v), ".(.*).")
+            v = qv == v and '"' .. v .. '"' or "'" .. v:toHex() .. "'"
+        end
+        if type(v) == "table" then
+            print(lv .. "\t" .. tostring(k) .. " = ")
+            printTable(v, lv)
+        else
+
+            print(lv .. "\t" .. tostring(k) .. " = " .. tostring(v) .. ",")
+        end
+    end
+    print(lv .. "},")
+end
+
+function getTableSlice(intable, startIndex, endIndex)
+    local outTable = {}
+    for i = startIndex, endIndex do
+        table.insert(outTable, intable[i])
+    end
+    return outTable
+end
+
+function getTableLen(t)
+    local count = 0
+    for _, _ in pairs(t) do
+        count = count + 1
+    end
+    return count
+end
+
+function formatOnlineBooksTable(inTable)
+    local outTable = {}
+    local i = 1
+    for k, v in pairs(inTable) do
+        v["index"] = i
+        table.insert(outTable, {
+            [k] = v
+        })
+        i = i + 1
+    end
+    return outTable
+end
+
+function longTimerCb()
+    gBtnStatus = "LONGPRESSED"
+    gLongCb()
+end
+
+function btnHandle(val)
+    if val == 0 then
+        if waitDoubleClick == true then
+            sys.timerStop(gShortCb)
+            gDoubleCb()
+            waitDoubleClick = false
+            return
+        end
+        sys.timerStart(longTimerCb, gPressTime)
+        gBtnStatus = "PRESSED"
+    else
+        sys.timerStop(longTimerCb)
+        if gBtnStatus == "PRESSED" then
+            sys.timerStart(gShortCb, 500)
+            waitDoubleClick = true
+            gBtnStatus = "IDLE"
+        elseif gBtnStatus == "LONGPRESSED" then
+            gBtnStatus = "IDLE"
+        end
+    end
+end
+
+function btnSetup(gpioNumber, pressTime, shortCb, longCb, doubleCb)
+    gpio.setup(gpioNumber, btnHandle, gpio.PULLUP)
+    gPressTime = pressTime
+    gShortCb = shortCb
+    gLongCb = longCb
+    gDoubleCb = doubleCb
+end
+
+function showBookList(index)
+    local firstIndex
+    for k, v in pairs(onlineBooksShowTableTmp[1]) do
+        firstIndex = v["index"]
+    end
+    if index > firstIndex + 10 then
+        onlineBooksShowTableTmp = getTableSlice(onlineBooksShowTable, index - 10, index)
+    end
+    if index < firstIndex then
+        onlineBooksShowTableTmp = getTableSlice(onlineBooksShowTable, index, index + 10)
+    end
+    einkShowStr(0, 16, "图书列表", 0, true)
+    local ifShow = false
+    local len = getTableLen(onlineBooksTable)
+    local showLen = getTableLen(onlineBooksShowTableTmp)
+    if len == 0 then
+        einkShowStr(0, 32, "暂无在线图书", 0, false, true)
+        return
+    end
+    local i = 1
+    for k, v in pairs(onlineBooksShowTableTmp) do
+        for name, info in pairs(v) do
+            local bookName = string.split(name, ".")[1]
+            local bookSize = tonumber(info["size"]) / 1024 / 1024
+            if i == showLen then
+                ifShow = true
+            end
+            if info["index"] == index then
+                eink.rect(0, 16 * i, 200, 16 * (i + 1), 0, 1, nil, ifShow)
+                einkShowStr(0, 16 * (i + 1), bookName .. "       " .. string.format("%.2f", bookSize) .. "MB", 1, nil,
+                    ifShow)
+            else
+                einkShowStr(0, 16 * (i + 1), bookName .. "       " .. string.format("%.2f", bookSize) .. "MB", 0, nil,
+                    ifShow)
+            end
+            i = i + 1
+        end
+    end
+end
+
+function showBook(bookName, bookUrl, page)
+    sys.taskInit(function()
+        waitHttpTask = true
+        for i = 1, 3 do
+            local result, code, data = httpLib.request("GET", bookUrl .. "/" .. page)
+            log.info("SHOWBOOK", result, code)
+            if result == false or code == -1 or code == 0 then
+                log.error("SHOWBOOK", "获取图书内容失败 ", data)
+            else
+                local bookLines = json.decode(data)
+                for k, v in pairs(bookLines) do
+                    if k == 1 then
+                        einkShowStr(0, 16 * k, v, 0, true, false)
+                    elseif k == #bookLines then
+                        einkShowStr(0, 16 * k, v, 0, false, false)
+                    else
+                        einkShowStr(0, 16 * k, v, 0, false, false)
+                    end
+                end
+                einkShowStr(60, 16 * 12 + 2, page .. "/" .. onlineBooksTable[bookName]["pages"], 0, false, true)
+                break
+            end
+        end
+        waitHttpTask = false
+    end)
+end
+
+function btnShortHandle()
+    if waitHttpTask == true then
+        waitDoubleClick = false
+        return
+    end
+    if PAGE == "LIST" then
+        if einkBooksIndex == onlineBooksTableLen then
+            einkBooksIndex = 1
+        else
+            einkBooksIndex = einkBooksIndex + 1
+        end
+        showBookList(einkBooksIndex)
+    else
+        local i = 1
+        local bookName = nil
+        for k, v in pairs(onlineBooksTable) do
+            if i == einkBooksIndex then
+                bookName = k
+            end
+            i = i + 1
+        end
+        local thisBookPages = tonumber(onlineBooksTable[bookName]["pages"])
+        if thisBookPages == gpage then
+            waitDoubleClick = false
+            return
+        end
+        gpage = gpage + 1
+        showBook(bookName, serverAdress .. string.urlEncode(bookName), gpage)
+        log.info(bookName, gpage)
+        fdb.kv_set(bookName, gpage)
+    end
+    waitDoubleClick = false
+end
+
+function btnLongHandle()
+    if waitHttpTask == true then
+        return
+    end
+    if PAGE == "LIST" then
+        PAGE = "BOOK"
+        local i = 1
+        local bookName = nil
+        for k, v in pairs(onlineBooksTable) do
+            if i == einkBooksIndex then
+                bookName = k
+            end
+            i = i + 1
+        end
+        local pageCache = fdb.kv_get(bookName)
+        log.info(bookName, pageCache)
+        if pageCache == nil then
+            gpage = 1
+            showBook(bookName, serverAdress .. string.urlEncode(bookName), gpage)
+        else
+            gpage = pageCache
+            showBook(bookName, serverAdress .. string.urlEncode(bookName), pageCache)
+        end
+
+    elseif PAGE == "BOOK" then
+        PAGE = "LIST"
+        showBookList(einkBooksIndex)
+    end
+end
+
+function btnDoublehandle()
+    if waitHttpTask == true then
+        return
+    end
+    if PAGE == "LIST" then
+        if einkBooksIndex == 1 then
+            einkBooksIndex = onlineBooksTableLen
+        else
+            einkBooksIndex = einkBooksIndex - 1
+        end
+        showBookList(einkBooksIndex)
+    else
+        if gpage == 1 then
+            return
+        end
+        gpage = gpage - 1
+        local i = 1
+        local bookName = nil
+        for k, v in pairs(onlineBooksTable) do
+            if i == einkBooksIndex then
+                bookName = k
+            end
+            i = i + 1
+        end
+        log.info(bookName, gpage)
+        fdb.kv_set(bookName, gpage)
+        showBook(bookName, serverAdress .. string.urlEncode(bookName), gpage)
+    end
+end
+
+function einkShowStr(x, y, str, colored, clear, show)
+    if einkPrintTime > 20 then
+        einkPrintTime = 0
+        eink.rect(0, 0, 200, 200, 0, 1)
+        eink.show(0, 0, true)
+        eink.rect(0, 0, 200, 200, 1, 1)
+        eink.show(0, 0, true)
+    end
+    if clear == true then
+        eink.clear()
+    end
+    eink.print(x, y, str, colored)
+    if show == true then
+        einkPrintTime = einkPrintTime + 1
+        eink.show(0, 0, true)
+    end
+end
+
+sys.taskInit(function()
+    assert(fdb.kvdb_init("env", "onchip_flash") == true, tag .. ".kvdb_init ERROR")
+    eink.model(eink.MODEL_1in54)
+    if MOD_TYPE == "AIR101" then
+        eink.setup(1, 0, 16, 19, 17, 20)
+    elseif MOD_TYPE == "ESP32C3" then
+        eink.setup(1, 2, 11, 10, 6, 7)
+    end
+    eink.setWin(200, 200, 0)
+    eink.clear(0, true)
+    eink.show(0, 0)
+    eink.clear(1, true)
+    eink.show(0, 0)
+    eink.setFont(fonts.get("sarasa_regular_12"))
+
+    if USE_SMARTCONFIG == true then
+        einkShowStr(0, 16, "开机中 等待配网...", 0, false, true)
+        local connectRes = wifiLib.connect()
+        if connectRes == false then
+            einkShowStr(0, 16, "配网失败 重启中...", 0, true, true)
+            rtos.reboot()
+        end
+    else
+        einkShowStr(0, 16, "开机中...", 0, false, true)
+        local connectRes = wifiLib.connect(SSID, PASSWD)
+        if connectRes == false then
+            einkShowStr(0, 16, "联网失败 重启中...", 0, true, true)
+            rtos.reboot()
+        end
+    end
+    for i = 1, 5 do
+        local result, code, data = httpLib.request("GET", serverAdress .. "getBooks")
+        if result == false or code == -1 or code == 0 then
+            log.error(tag, "获取图书列表失败 ", data)
+            if i == 5 then
+                einkShowStr(0, 16, "连接图书服务器失败 正在重启", 0, true, true)
+                rtos.reboot()
+            end
+        else
+            onlineBooksTable = json.decode(data)
+            printTable(onlineBooksTable)
+            onlineBooksTableLen = getTableLen(onlineBooksTable)
+            onlineBooksShowTable = formatOnlineBooksTable(onlineBooksTable)
+            onlineBooksShowTableTmp = getTableSlice(onlineBooksShowTable, 1, 11)
+            showBookList(1)
+            btnSetup(9, 1000, btnShortHandle, btnLongHandle, btnDoublehandle)
+            break
+        end
+        sys.wait(1000)
+    end
+end)
+
+sys.run()

+ 53 - 0
script/turnkey/EinkBook/Scripts/wifiLib.lua

@@ -0,0 +1,53 @@
+local wifiConnect = {}
+
+function wifiConnect.connect(ssid, passwd)
+    local waitRes, data
+    if wlan.init() ~= 0 then
+        log.error(tag .. ".init", "ERROR")
+        return false
+    end
+    if wlan.setMode(wlan.STATION) ~= 0 then
+        log.error(tag .. ".setMode", "ERROR")
+        return false
+    end
+
+    if USE_SMARTCONFIG == true then
+        if wlan.smartconfig() ~= 0 then
+            log.error(tag .. ".connect", "ERROR")
+            return false
+        end
+        waitRes, data = sys.waitUntil("WLAN_STA_CONNECTED", 180 * 10000)
+        log.info("WLAN_STA_CONNECTED", waitRes, data)
+        if waitRes ~= true then
+            log.error(tag .. ".wlan ERROR")
+            return false
+        end
+        waitRes, data = sys.waitUntil("IP_READY", 10000)
+        if waitRes ~= true then
+            log.error(tag .. ".wlan ERROR")
+            return false
+        end
+        log.info("IP_READY", waitRes, data)
+        return true
+    end
+
+    if wlan.connect(ssid, passwd) ~= 0 then
+        log.error(tag .. ".connect", "ERROR")
+        return false
+    end
+    waitRes, data = sys.waitUntil("WLAN_STA_CONNECTED", 10000)
+    if waitRes ~= true then
+        log.error(tag .. ".wlan ERROR")
+        return false
+    end
+    log.info("WLAN_STA_CONNECTED", waitRes, data)
+    waitRes, data = sys.waitUntil("IP_READY", 10000)
+    if waitRes ~= true then
+        log.error(tag .. ".wlan ERROR")
+        return false
+    end
+    log.info("IP_READY", waitRes, data)
+    return true
+end
+
+return wifiConnect

+ 3 - 0
script/turnkey/EinkBook/Server/README.md

@@ -0,0 +1,3 @@
+# Server
+
+Server 代码 参考 https://gitee.com/HashJeremy/EinkBook-LuatOS/tree/master/Server